在 Linux 系統中,裝置驅動程式需要有效地處理硬體中斷,才能確保系統的穩定性和效能。本文以 CY8C9520A I2C GPIO 擴充套件器驅動程式為例,探討中斷處理機制,並解析程式碼中關鍵部分的邏輯與功能。同時,我們也將介紹如何使用裝置樹疊加層(Device Tree Overlay)技術,動態地修改硬體組態,無需重新編譯核心,即可新增或修改裝置的設定。此方法提升了驅動程式開發的彈性,並簡化了硬體組態的管理。

裝置驅動程式中的中斷處理

在 Linux 裝置驅動程式開發中,中斷處理是一項至關重要的技術。本章節將探討如何在裝置驅動程式中處理中斷,並透過實際範例進行演示。

CY8C9520A 驅動程式分析

首先,我們來分析 CY8C9520A 驅動程式的關鍵部分。該驅動程式是一個 I2C GPIO 擴充套件器驅動,支援中斷處理和 PWM 控制。

初始化與設定

cy8c9520a_probe 函式中,驅動程式進行了初始化和設定:

static int cy8c9520a_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    // ...
    mutex_init(&cygpio->lock);
    // ...
    cy8c9520a_irq_setup(cygpio);
    // ...
}

中斷設定

cy8c9520a_irq_setup 函式中,驅動程式進行了中斷設定:

static int cy8c9520a_irq_setup(struct cy8c9520a *cygpio)
{
    // ...
    cy8c9520a_irq_bus_lock(cygpio);
    // ...
    cy8c9520a_irq_set_type(cygpio, irq, flow_type);
    // ...
    cy8c9520a_irq_unmask(cygpio, irq);
    // ...
}

中斷處理

當中斷發生時,驅動程式會呼叫 cy8c9520a_irq_handler 函式進行處理:

static irqreturn_t cy8c9520a_irq_handler(int irq, void *dev_id)
{
    // ...
    printk(KERN_INFO "the interrupt ISR has been entered\n");
    // ...
}

實驗演示

接下來,我們透過實驗演示來驗證驅動程式的功能。

載入驅動程式

首先,載入 CY8C9520A 驅動程式:

root@raspberrypi:/home/pi# insmod CY8C9520A_pwm_pinctrl.ko

輸出結果顯示驅動程式已成功載入並初始化。

載入 GPIO 中斷模組

接著,載入 GPIO 中斷模組:

root@raspberrypi:/home/pi# insmod int_rpi3_gpio.ko

輸出結果顯示 GPIO 中斷模組已成功載入並初始化。

測試中斷處理

當將 P0 的第 0 線連線到 GND 然後斷開時,會觸發兩次中斷:

root@raspberrypi:/home/pi#
the interrupt ISR has been entered
int_gpio_expand int_gpio: interrupt received. key: P0_line0_INT
the interrupt ISR has been entered
int_gpio_expand int_gpio: interrupt received. key: P0_line0_INT

PWM 控制

最後,我們測試 PWM 控制功能。匯出 PWM 通道 1:

root@raspberrypi:/sys/class/pwm/pwmchip0# echo 1 > export

設定 PWM 訊號的週期和佔空比:

root@raspberrypi:/sys/class/pwm/pwmchip0# echo 100000 > pwm1/period
root@raspberrypi:/sys/class/pwm/pwmchip0# echo 50000 > pwm1/duty_cycle

啟用 PWM 訊號:

root@raspberrypi:/sys/class/pwm/pwmchip0# echo 1 > pwm1/enable

當將 P0 的第 0 線連線到 P0 的第 3 線時,會看到中斷被觸發:

int_gpio_expand int_gpio: interrupt received. key: P0_line0_INT
the interrupt ISR has been entered
int_gpio_expand int_gpio: interrupt received. key: P0_line0_INT
the interrupt ISR has been entered
...
詳細內容解密:
  1. CY8C9520A 驅動程式初始化

    • cy8c9520a_probe 中進行初始化,包括互斥鎖的初始化和中斷設定的呼叫。
    • 使用 mutex_init(&cygpio->lock); 初始化互斥鎖,以確保執行緒安全。
  2. 中斷設定的流程

    • cy8c9520a_irq_setup 中進行中斷設定,包括鎖定 bus、設定中斷型別和解除中斷遮蔽。
    • 使用 cy8c9520a_irq_bus_lock(cygpio); 鎖定 bus,以防止平行存取衝突。
  3. 實驗步驟與驗證

    • 載入 CY8C9520A 驅動程式和 GPIO 中斷模組,並透過連線和斷開 P0 第 0 線的 GND 來測試中斷處理。
    • 使用 insmod 命令載入模組,並觀察系統輸出以確認初始化成功。
  4. PWM 控制的實作

    • 透過匯出 PWM 通道、設定週期和佔空比以及啟用 PWM 訊號來測試 PWM 控制功能。
    • 使用 echo 命令向 sysfs 中的對應檔案寫入值,以控制 PWM 訊號。
  5. 技術細節與邏輯

    • 中斷處理函式 cy8c9520a_irq_handler 在每次中斷發生時被呼叫,並輸出日誌資訊。
    • PWM 控制涉及多個 sysfs 操作,包括匯出通道、設定週期和佔空比以及啟用訊號。

透過上述詳細的內容解說,可以清楚理解 CY8C9520A 驅動程式的工作原理和實驗步驟,從而更深入地掌握 Linux 裝置驅動程式中的中斷處理和 PWM 控制技術。

在裝置驅動程式中處理中斷(Handling Interrupts in Device Drivers)

實驗7.6:CY8C9520A裝置樹疊加層(Device Tree overlay)

在這個實驗中,我們將探討如何在Raspberry Pi上使用裝置樹疊加層機制來引入新的硬體支援。在之前的實驗中,我們透過在bcm2710-rpi-3-b.dts檔案中編寫CY8C9520A裝置的裝置樹屬性來描述該裝置。裝置樹疊加層機制允許我們在保持原始裝置樹原始檔案不變的情況下,動態地新增描述新硬體的片段。

步驟1:檢視原始裝置樹設定

首先,開啟bcm2710-rpi-3-b.dts檔案(位於核心原始碼樹中的/arch/arm/boot/dts/),並找到i2c1節點。您將看到以下節點:

&i2c1 {
    pinctrl-names = "default";
    pinctrl-0 = <&i2c1_pins>;
    clock-frequency = <100000>;
};
i2c1_pins: i2c1 {
    brcm,pins = <2 3>;
    brcm,function = <4>;
};

第一個節點為i2c1控制器主節點新增了幾個屬性。在i2c1節點中,您可以看到pinctrl-0屬性指向了i2c1_pins引腳組態節點(第二個節點),該節點將i2c1控制器的引腳組態為I2C模式。

步驟2:瞭解i2c1控制器節點

i2c1控制器節點在bcm283x.dtsi檔案中描述(包含在核心原始碼樹中的/arch/arm/boot/dts/):

i2c1: i2c@7e804000 {
    compatible = "brcm,bcm2835-i2c";
    reg = <0x7e804000 0x1000>;
    interrupts = <2 21>;
    clocks = <&clocks BCM2835_CLOCK_VPU>;
    #address-cells = <1>;
    #size-cells = <0>;
    status = "disabled";
};

步驟3:新增CY8C9520A裝置節點

在實驗7.5中,您新增了以下子節點和屬性(以粗體顯示)到i2c1gpio節點(包含在bcm2710-rpi-3-b.dts檔案中):

&i2c1 {
    pinctrl-names = "default";
    pinctrl-0 = <&i2c1_pins>;
    clock-frequency = <100000>;
    status = "okay";
    cy8c9520a: cy8c9520a@20 {
        compatible = "cy8c9520a";
        reg = <0x20>;
        interrupt-controller;
        #interrupt-cells = <2>;
        gpio-controller;
        #gpio-cells = <2>;
        interrupts = <23 1>;
        interrupt-parent = <&gpio>;
        #pwm-cells = <2>;
        pwm0 = <20>; // pwm not supported
        pwm1 = <3>;
        pwm2 = <20>; // pwm not supported
        pwm3 = <2>;
        pinctrl-names = "default";
        pinctrl-0 = <&accel_int_pin &cy8c9520apullups &cy8c9520apulldowns &cy8c9520adrivestrength>;
        // ...
    };
};

步驟4:建立裝置樹疊加層檔案

現在,您將建立一個名為cy8c9520a-overlay.dts的檔案,並新增以下程式碼:

/ {
    compatible = "brcm,bcm2835";
    fragment@0 {
        target = <&gpio>;
        __overlay__ {
            accel_int_pin: accel_int_pin {
                brcm,pins = <23>;
                brcm,function = <0>; /* Input */
                brcm,pull = <0>; /* none */
            };
        };
    };
    fragment@1 {
        target = <&i2c1>;
        __overlay__ {
            #address-cells = <1>;
            #size-cells = <0>;
            status = "okay";
            cy8c9520a: cy8c9520a@20 {
                compatible = "cy8c9520a";
                reg = <0x20>;
                interrupt-controller;
                #interrupt-cells = <2>;
                gpio-controller;
                #gpio-cells = <2>;
                interrupts = <23 1>;
                interrupt-parent = <&gpio>;
                #pwm-cells = <2>;
                pwm0 = <20>; // pwm not supported
                pwm1 = <3>;
                pwm2 = <20>; // pwm not supported
                pwm3 = <2>;
                pinctrl-names = "default";
                pinctrl-0 = <&accel_int_pin &cy8c9520apullups &cy8c9520apulldowns &cy8c9520adrivestrength>;
                // ...
            };
        };
    };
};

程式碼解析

fragment@0 {
    target = <&gpio>;
    __overlay__ {
        accel_int_pin: accel_int_pin {
            brcm,pins = <23>;
            brcm,function = <0>; /* Input */
            brcm,pull = <0>; /* none */
        };
    };
};

此段程式碼建立了一個名為fragment@0的片段,將accel_int_pin節點新增到gpio節點中。

fragment@1 {
    target = <&i2c1>;
    __overlay__ {
        // ...
    };
};

此段程式碼建立了一個名為fragment@1的片段,將cy8c9520a節點新增到i2c1節點中,並修改了i2c1節點的一些屬性。

內容解密:

此實驗展示瞭如何使用裝置樹疊加層來新增CY8C9520A裝置的支援。首先,我們檢視了原始裝置樹設定,然後瞭解了i2c1控制器節點。接著,我們新增了CY8C9520A裝置節點,並建立了裝置樹疊加層檔案。最後,我們解析了程式碼,瞭解瞭如何使用片段來新增和修改裝置樹節點。

本實驗展示了裝置樹疊加層的強大功能,使得我們可以更加靈活地管理和組態裝置樹。未來,我們可以將此技術應用於更多的裝置和平台上。

隨著物聯網和嵌入式系統的不斷發展,裝置樹疊加層技術將會發揮越來越重要的作用。未來,我們可以預見更多的裝置和平台將會支援裝置樹疊加層技術,從而使得硬體組態和管理變得更加靈活和高效。

參考資料

在裝置驅動程式中處理中斷

實驗目的

本實驗旨在展示如何在 Raspberry Pi 上使用 CY8C9520A 擴充套件晶片處理 GPIO 中斷,並透過裝置樹疊加層(Device Tree Overlay)組態硬體。

設定裝置樹疊加層

首先,需要建立一個裝置樹疊加層檔案(.dts)來描述 CY8C9520A 晶片的組態。以下是一個範例疊加層檔案的內容,用於設定 CY8C9520A 的 GPIO 腳位:

/dts-v1/;
/plugin/;

{
    compatible = "brcm,bcm2835";
    fragment@0 {
        target = <&i2c1>;
        __overlay__ {
            status = "okay";
            pinctrl-names = "default";
            pinctrl-0 = <&i2c1_pins>;
        };
    };

    fragment@1 {
        target = <&i2c1_pins>;
        pins1: __overlay__ {
            brcm,pins = <2 3>;
            brcm,function = <4>; /* alt 0 */
        };
    };

    fragment@2 {
        target = <&i2c1_pins>;
        pins2: __dormant__ {
            brcm,pins = <44 45>;
            brcm,function = <6>; /* alt 2 */
        };
    };

    __overrides__ {
        pins_2_3 = <0>,"=1!2";
        pins_44_45 = <0>,"!1=2";
    };
};

內容解密:

  • /dts-v1/;/plugin/; 定義了裝置樹疊加層的版本和型別。
  • compatible = "brcm,bcm2835"; 指定了相容的硬體平台。
  • fragment@0fragment@2 定義了不同的硬體組態片段,例如 I2C1 的啟用和腳位設定。
  • __overrides__ 部分允許在載入疊加層時動態修改組態,例如選擇不同的 I2C1 腳位。

編譯與載入裝置樹疊加層

編譯裝置樹疊加層並將其複製到 Raspberry Pi 上:

~/linux_rpi3/linux$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- dtbs
~/linux_rpi3/linux$ scp arch/arm/boot/dts/bcm2710-rpi-3-b.dtb root@10.0.0.10:/boot/
~/linux_rpi3/linux$ scp arch/arm/boot/dts/overlays/cy8c9520a.dtbo root@10.0.0.10:/boot/overlays

載入 CY8C9520A 驅動模組與測試應用程式

編譯並載入 CY8C9520A_pwm_pinctrl.ko 驅動模組,以及編譯 gpio_int 測試應用程式:

~/linux_5.4_rpi3_drivers/device_tree_overlays/linux_5.4_CY8C9520A_pwm_pinctrl$ make
~/linux_5.4_rpi3_drivers/device_tree_overlays/linux_5.4_CY8C9520A_pwm_pinctrl$ make deploy
~/linux_5.4_rpi3_drivers/device_tree_overlays/linux_5.4_CY8C9520A_pwm_pinctrl/app$ scp gpio_int.c root@10.0.0.10:/home

在 Raspberry Pi 上編譯 gpio_int.c:

root@raspberrypi:/home/pi# gcc -o gpio_int gpio_int.c

實驗步驟與結果

  1. 載入 CY8C9520A_pwm_pinctrl.ko 模組:
root@raspberrypi:/home/pi# insmod CY8C9520A_pwm_pinctrl.ko
  1. 執行 gpio_int 應用程式並測試中斷:
root@raspberrypi:/home/pi# ./gpio_int

當將 P0 的第 0 腳位連線到 GND 然後斷開時,會觸發中斷。gpio_int 應用程式會顯示中斷的相關資訊。

內容解密:

  • cy8c9520a_gpio_direction input is called 表示設定 GPIO 為輸入模式。
  • the interrupt ISR has been entered 表示進入中斷服務例程(ISR)。
  • irq received 表示接收到中斷請求。