diff --git a/IMX6ULL/doc_pic/07_GPIO/09_GPIO子系统与Pinctrl子系统的交互.md b/IMX6ULL/doc_pic/07_GPIO/09_GPIO子系统与Pinctrl子系统的交互.md new file mode 100644 index 0000000..34e0c19 --- /dev/null +++ b/IMX6ULL/doc_pic/07_GPIO/09_GPIO子系统与Pinctrl子系统的交互.md @@ -0,0 +1,227 @@ +## GPIO子系统与Pinctrl子系统的交互 + +参考资料: + +* Linux 5.x内核文档 + * Linux-5.4\Documentation\driver-api + * Linux-5.4\Documentation\devicetree\bindings\gpio\gpio.txt + * Linux-5.4\drivers\gpio\gpio-74x164.c + +* Linux 4.x内核文档 + * Linux-4.9.88\Documentation\gpio + * Linux-4.9.88\Documentation\devicetree\bindings\gpio\gpio.txt + * Linux-4.9.88\drivers\gpio\gpio-74x164.c + + +### 1. 使用GPIO前应该设置Pinctrl + +假设使用这个虚拟的GPIO Controller的pinA来控制LED: + +![image-20210528163702792](pic/07_GPIO/11_virtual_gpio_led.png) + + + +要使用pinA来控制LED,首先要通过Pinctrl子系统把它设置为GPIO功能,然后才能设置它为输出引脚、设置它的输出值。 + +所以在设备树文件里,应该添加Pinctrl的内容: + +```shell +virtual_pincontroller { + compatible = "100ask,virtual_pinctrl"; + myled_pin: myled_pin { + functions = "gpio"; + groups = "pin0"; + configs = <0x11223344>; + }; +}; + +gpio_virt: virtual_gpiocontroller { + compatible = "100ask,virtual_gpio"; + gpio-controller; + #gpio-cells = <2>; + ngpios = <4>; +}; + +myled { + compatible = "100ask,leddrv"; + led-gpios = <&gpio_virt 0 GPIO_ACTIVE_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&myled_pin>; +}; +``` + + + +但是很多芯片,并不要求在设备树中把把引脚复用为GPIO功能。 + +比如STM32MP157,在它的设备树工具`STM32CubeMX`即使把引脚配置为GPIO功能,它也不会在设备树中出现。 + +原因在于:GPIO走了后门。 + +现实的芯片中,并没有Pinctrl这样的硬件,它的功能大部分是在GPIO模块中实现的。 + +Pinctrl是一个软件虚拟处理的概念,它的实现本来就跟GPIO密切相关。 + +甚至一些引脚默认就是GPIO功能。 + +按理说: + +一个引脚可能被用作GPIO,也可能被用作I2C,GPIO和I2C这些功能时相同低位的。 + +要用作GPIO,需要先通过Pinctrl把引脚复用为GPIO功能。 + +但是Pinctrl和GPIO关系密切,当你使用gpiod_get获得GPIO引脚时,它就`偷偷地`通过Pinctrl把引脚复用为GPIO功能了。 + + + +### 2. GPIO和Pinctrl的映射关系 + +#### 2.1 示例 + +![image-20210529094937479](pic/07_GPIO/12_gpio_and_pinctrl_range.png) + +从上图可知: + +* 左边的Pinctrl支持8个引脚,在Pinctrl的内部编号为0~7 +* 图中有2个GPIO控制器 + * GPIO0内部引脚编号为0~3,假设在GPIO子系统中全局编号为100~103 + * GPIO1内部引脚编号为0~3,假设在GPIO子系统中全局编号为104~107 + +* 假设我们要使用pin1_1,应该这样做: + * 根据GPIO1的内部编号1,可以换算为Pinctrl子系统中的编号5 + * 使用Pinctrl的函数,把第5个引脚配置为GPIO功能 + + + +#### 2.2 数据结构 + +![image-20210529101049897](pic/07_GPIO/13_gpio_pin_range.png) + + + +### 3. GPIO调用Pinctrl的过程 + +GPIO子系统中的request函数,用来申请某个GPIO引脚, + +它会导致Pinctrl子系统中的这2个函数之一被调用:`pmxops->gpio_request_enable`或`pmxops->request` + +![image-20210529101834908](pic/07_GPIO/14_gpio_request.png) + +调用关系如下: + +```c +gpiod_get + gpiod_get_index + desc = of_find_gpio(dev, con_id, idx, &lookupflags); + ret = gpiod_request(desc, con_id ? con_id : devname); + ret = gpiod_request_commit(desc, label); + if (chip->request) { + ret = chip->request(chip, offset); + } +``` + + + +我们编写GPIO驱动程序时,所设置`chip->request`函数,一般直接调用`gpiochip_generic_request`,它导致Pinctrl把引脚复用为GPIO功能。 + +```c +gpiochip_generic_request(struct gpio_chip *chip, unsigned offset) + pinctrl_request_gpio(chip->gpiodev->base + offset) + ret = pinctrl_get_device_gpio_range(gpio, &pctldev, &range); // gpio是引脚的全局编号 + + /* Convert to the pin controllers number space */ + pin = gpio_to_pin(range, gpio); + + ret = pinmux_request_gpio(pctldev, range, pin, gpio); + ret = pin_request(pctldev, pin, owner, range); +``` + + + +Pinctrl子系统中的pin_request函数就会把引脚配置为GPIO功能: + +```c +static int pin_request(struct pinctrl_dev *pctldev, + int pin, const char *owner, + struct pinctrl_gpio_range *gpio_range) +{ + const struct pinmux_ops *ops = pctldev->desc->pmxops; + + /* + * If there is no kind of request function for the pin we just assume + * we got it by default and proceed. + */ + if (gpio_range && ops->gpio_request_enable) + /* This requests and enables a single GPIO pin */ + status = ops->gpio_request_enable(pctldev, gpio_range, pin); + else if (ops->request) + status = ops->request(pctldev, pin); + else + status = 0; +} +``` + + + +### 3. 我们要做什么 + +如果不想在使用GPIO引脚时,在设备树中设置Pinctrl信息, + +如果想让GPIO和Pinctrl之间建立联系, + +我们需要做这些事情: + +#### 3.1 表明GPIO和Pinctrl间的联系 + +在GPIO设备树中使用`gpio-ranges`来描述它们之间的联系: + + * GPIO系统中有引脚号 + + * Pinctrl子系统中也有自己的引脚号 + + * 2个号码要建立映射关系 + + * 在GPIO设备树中使用如下代码建立映射关系 + + ```shell + // 当前GPIO控制器的0号引脚, 对应pinctrlA中的128号引脚, 数量为12 + gpio-ranges = <&pinctrlA 0 128 12>; + ``` + +#### 3.2 解析这些联系 + +在GPIO驱动程序中,解析跟Pinctrl之间的联系:处理`gpio-ranges`: + + * 这不需要我们自己写代码 + + * 注册gpio_chip时会自动调用 + + ```c + int gpiochip_add_data(struct gpio_chip *chip, void *data) + status = of_gpiochip_add(chip); + status = of_gpiochip_add_pin_range(chip); + + of_gpiochip_add_pin_range + for (;; index++) { + ret = of_parse_phandle_with_fixed_args(np, "gpio-ranges", 3, + index, &pinspec); + + pctldev = of_pinctrl_get(pinspec.np); // 根据gpio-ranges的第1个参数找到pctldev + + // 增加映射关系 + /* npins != 0: linear range */ + ret = gpiochip_add_pin_range(chip, + pinctrl_dev_get_devname(pctldev), + pinspec.args[0], + pinspec.args[1], + pinspec.args[2]); + ``` + + + +#### 3.3 编程 +* 在GPIO驱动程序中,提供`gpio_chip->request` + +* 在Pinctrl驱动程序中,提供`pmxops->gpio_request_enable`或`pmxops->request` + + diff --git a/IMX6ULL/doc_pic/07_GPIO/09_GPIO子系统与Pinctrl子系统的交互.tif b/IMX6ULL/doc_pic/07_GPIO/09_GPIO子系统与Pinctrl子系统的交互.tif new file mode 100644 index 0000000..b432dad Binary files /dev/null and b/IMX6ULL/doc_pic/07_GPIO/09_GPIO子系统与Pinctrl子系统的交互.tif differ diff --git a/IMX6ULL/doc_pic/07_GPIO/10_编程_GPIO使用Pinctrl.md b/IMX6ULL/doc_pic/07_GPIO/10_编程_GPIO使用Pinctrl.md new file mode 100644 index 0000000..9283c07 --- /dev/null +++ b/IMX6ULL/doc_pic/07_GPIO/10_编程_GPIO使用Pinctrl.md @@ -0,0 +1,396 @@ +## 编程_GPIO使用Pinctrl + +参考资料: + +* Linux 5.x内核文档 + * Linux-5.4\Documentation\driver-api + * Linux-5.4\Documentation\devicetree\bindings\gpio\gpio.txt + * Linux-5.4\drivers\gpio\gpio-74x164.c + +* Linux 4.x内核文档 + * Linux-4.9.88\Documentation\gpio + * Linux-4.9.88\Documentation\devicetree\bindings\gpio\gpio.txt + * Linux-4.9.88\drivers\gpio\gpio-74x164.c + +* 本节视频代码在GIT仓库中 + + ```shell + doc_and_source_for_drivers\IMX6ULL\source\07_GPIO\04_gpio_use_pinctrl_ok + doc_and_source_for_drivers\STM32MP157\source\A7\07_GPIO\04_gpio_use_pinctrl_ok + ``` + + + +### 1. 我们要做什么 + +假设使用这个虚拟的GPIO Controller的pinA来控制LED: + +![image-20210528163702792](pic/07_GPIO/11_virtual_gpio_led.png) + +如果不想在使用GPIO引脚时,在设备树中设置Pinctrl信息, + +如果想让GPIO和Pinctrl之间建立联系, + +我们需要做这些事情: + +#### 1.1 表明GPIO和Pinctrl间的联系 + +在GPIO设备树中使用`gpio-ranges`来描述它们之间的联系: + + * GPIO系统中有引脚号 + + * Pinctrl子系统中也有自己的引脚号 + + * 2个号码要建立映射关系 + + * 在GPIO设备树中使用如下代码建立映射关系 + + ```shell + // 当前GPIO控制器的0号引脚, 对应pinctrlA中的128号引脚, 数量为12 + gpio-ranges = <&pinctrlA 0 128 12>; + ``` + +#### 1.2 解析这些联系 + +在GPIO驱动程序中,解析跟Pinctrl之间的联系:处理`gpio-ranges`: + + * 这不需要我们自己写代码 + + * 注册gpio_chip时会自动调用 + + ```c + int gpiochip_add_data(struct gpio_chip *chip, void *data) + status = of_gpiochip_add(chip); + status = of_gpiochip_add_pin_range(chip); + + of_gpiochip_add_pin_range + for (;; index++) { + ret = of_parse_phandle_with_fixed_args(np, "gpio-ranges", 3, + index, &pinspec); + + pctldev = of_pinctrl_get(pinspec.np); // 根据gpio-ranges的第1个参数找到pctldev + + // 增加映射关系 + /* npins != 0: linear range */ + ret = gpiochip_add_pin_range(chip, + pinctrl_dev_get_devname(pctldev), + pinspec.args[0], + pinspec.args[1], + pinspec.args[2]); + ``` + + +#### 1.3 编程 + +* 在GPIO驱动程序中,提供`gpio_chip->request` + +* 在Pinctrl驱动程序中,提供`pmxops->gpio_request_enable`或`pmxops->request` + + + +### 2. 编写设备树 + +```shell +pinctrl_virt: virtual_pincontroller { + compatible = "100ask,virtual_pinctrl"; +}; + +gpio_virt: virtual_gpiocontroller { + compatible = "100ask,virtual_gpio"; + gpio-controller; + #gpio-cells = <2>; + ngpios = <4>; + gpio-ranges = <&pinctrl_virt 0 0 4>; +}; + +myled { + compatible = "100ask,leddrv"; + led-gpios = <&gpio_virt 0 GPIO_ACTIVE_LOW>; +}; +``` + + + +### 3. 编程 + +#### 3.1 GPIO控制器编程 + +gpio_chip中提供request函数: + +```c +chip->request = gpiochip_generic_request; +``` + + + +#### 3.2 Pinctrl编程 + +```c +static const struct pinmux_ops virtual_pmx_ops = { + .get_functions_count = virtual_pmx_get_funcs_count, + .get_function_name = virtual_pmx_get_func_name, + .get_function_groups = virtual_pmx_get_groups, + .set_mux = virtual_pmx_set, + .gpio_request_enable = virtual_pmx_gpio_request_enable, +}; +``` + + + +### 4. 上机实验 + +#### 4.1 设置工具链 + +##### 1. STM32MP157 + + **注意**:对于STM32MP157,以前说编译内核/驱动、编译APP的工具链不一样,其实编译APP用的工具链也能用来编译内核。 + + ```shell + export ARCH=arm + export CROSS_COMPILE=arm-buildroot-linux-gnueabihf- + export PATH=$PATH:/home/book/100ask_stm32mp157_pro-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin + ``` + +##### 2. IMX6ULL + + ```shell + export ARCH=arm + export CROSS_COMPILE=arm-linux-gnueabihf- + export PATH=$PATH:/home/book/100ask_imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin + ``` + + + +#### 4.2 编译、替换设备树 + +##### 1. STM32MP157 + + * 修改`arch/arm/boot/dts/stm32mp157c-100ask-512d-lcd-v1.dts`,添加如下代码: + + ```shell + / { + pinctrl_virt: virtual_pincontroller { + compatible = "100ask,virtual_pinctrl"; + myled_pin: myled_pin { + functions = "gpio"; + groups = "pin0"; + configs = <0x11223344>; + }; + i2cgrp: i2cgrp { + functions = "i2c", "i2c"; + groups = "pin0", "pin1"; + configs = <0x11223344 0x55667788>; + }; + }; + + gpio_virt: virtual_gpiocontroller { + compatible = "100ask,virtual_gpio"; + gpio-controller; + #gpio-cells = <2>; + ngpios = <4>; + gpio-ranges = <&pinctrl_virt 0 0 4>; + }; + + myled { + compatible = "100ask,leddrv"; + led-gpios = <&gpio_virt 2 GPIO_ACTIVE_LOW>; + }; + }; + ``` + + + + * 编译设备树: + 在Ubuntu的STM32MP157内核目录下执行如下命令, + 得到设备树文件:`arch/arm/boot/dts/stm32mp157c-100ask-512d-lcd-v1.dtb` + + ```shell + make dtbs + ``` + + * 复制到NFS目录: + + ```shell + $ cp arch/arm/boot/dts/stm32mp157c-100ask-512d-lcd-v1.dtb ~/nfs_rootfs/ + ``` + + * 开发板上挂载NFS文件系统 + + * vmware使用NAT(假设windowsIP为192.168.1.100) + + ```shell + [root@100ask:~]# mount -t nfs -o nolock,vers=3,port=2049,mountport=9999 + 192.168.1.100:/home/book/nfs_rootfs /mnt + ``` + + * vmware使用桥接,或者不使用vmware而是直接使用服务器:假设Ubuntu IP为192.168.1.137 + + ```shell + [root@100ask:~]# mount -t nfs -o nolock,vers=3 192.168.1.137:/home/book/nfs_rootfs /mnt + ``` + +* 确定设备树分区挂载在哪里 + + 由于版本变化,STM32MP157单板上烧录的系统可能有细微差别。 + 在开发板上执行`cat /proc/mounts`后,可以得到两种结果(见下图): + + * mmcblk2p2分区挂载在/boot目录下(下图左边):无需特殊操作,下面把文件复制到/boot目录即可 + + * mmcblk2p2挂载在/mnt目录下(下图右边) + + * 在视频里、后面文档里,都是更新/boot目录下的文件,所以要先执行以下命令重新挂载: + * `mount /dev/mmcblk2p2 /boot` + + ![](../04_I2C/pic/04_I2C/057_boot_mount.png) + + * 更新设备树 + + ```shell + [root@100ask:~]# cp /mnt/stm32mp157c-100ask-512d-lcd-v1.dtb /boot + [root@100ask:~]# sync + ``` + +* 重启开发板 + + + + + +##### 2. IMX6ULL + + * 修改`arch/arm/boot/dts/100ask_imx6ull-14x14.dts`,添加如下代码: + + ```shell + / { + pinctrl_virt: virtual_pincontroller { + compatible = "100ask,virtual_pinctrl"; + myled_pin: myled_pin { + functions = "gpio"; + groups = "pin0"; + configs = <0x11223344>; + }; + i2cgrp: i2cgrp { + functions = "i2c", "i2c"; + groups = "pin0", "pin1"; + configs = <0x11223344 0x55667788>; + }; + }; + + gpio_virt: virtual_gpiocontroller { + compatible = "100ask,virtual_gpio"; + gpio-controller; + #gpio-cells = <2>; + ngpios = <4>; + gpio-ranges = <&pinctrl_virt 0 0 4>; + }; + + myled { + compatible = "100ask,leddrv"; + led-gpios = <&gpio_virt 2 GPIO_ACTIVE_LOW>; + }; + }; + ``` + + + + * 编译设备树: + 在Ubuntu的IMX6ULL内核目录下执行如下命令, + 得到设备树文件:`arch/arm/boot/dts/100ask_imx6ull-14x14.dtb` + + ```shell + make dtbs + ``` + + * 复制到NFS目录: + + ```shell + $ cp arch/arm/boot/dts/100ask_imx6ull-14x14.dtb ~/nfs_rootfs/ + ``` + +* 开发板上挂载NFS文件系统 + + * vmware使用NAT(假设windowsIP为192.168.1.100) + + ```shell + [root@100ask:~]# mount -t nfs -o nolock,vers=3,port=2049,mountport=9999 + 192.168.1.100:/home/book/nfs_rootfs /mnt + ``` + + * vmware使用桥接,或者不使用vmware而是直接使用服务器:假设Ubuntu IP为192.168.1.137 + + ```shell + [root@100ask:~]# mount -t nfs -o nolock,vers=3 192.168.1.137:/home/book/nfs_rootfs /mnt + ``` + + * 更新设备树 + + ```shell + [root@100ask:~]# cp /mnt/100ask_imx6ull-14x14.dtb /boot + [root@100ask:~]# sync + ``` + +* 重启开发板 + + + +#### 4.3 编译、安装驱动程序 + +* 编译: + + * 在Ubuntu上 + * 进入`04_gpio_use_pinctrl_ok`下的3个驱动目录,都执行make命令 + +* 安装: + + * 在开发板上 + + * 挂载NFS,复制文件,insmod,类似如下命令: + + ```shell + mount -t nfs -o nolock,vers=3 192.168.1.137:/home/book/nfs_rootfs /mnt + // 对于IMX6ULL,想看到驱动打印信息,需要先执行 + echo "7 4 1 7" > /proc/sys/kernel/printk + + insmod -f /mnt/virtual_pinctrl_driver.ko + insmod -f /mnt/virtual_gpio_driver.ko + insmod -f /mnt/leddrv.ko + + ls /dev/100ask_led0 + /mnt/ledtest /dev/100ask_led0 on + /mnt/ledtest /dev/100ask_led0 off + ``` + +* 观察内核打印的信息 + + + +### 5. 再次开后门 + +在STM32MP157的内核中, + +Pinctrl驱动中并没有提供`pmxops->gpio_request_enable`或`pmxops->request`, + +为什么也可一直接使用GPIO功能? + +它的gpio_chip结构体中,有`direction_input`、`direction_output`,这2个函数的调用关系如下: + +```c +direction_output/direction_input + pinctrl_gpio_direction + ret = pinmux_gpio_direction(pctldev, range, pin, input); + ret = ops->gpio_set_direction(pctldev, range, pin, input); + stm32_pmx_gpio_set_direction + stm32_pmx_set_mode // 它会设置引脚为GPIO功能 +``` + + + +### 6. IMX6ULL的特殊情况 + +IMX6ULL使用GPIO时必须设置Pinctrl,如果不设置,只有那些默认就是GPIO功能的引脚可以正常使用。 + +原因: + +* GPIO控制器的设备树中,没有`gpio-ranges` +* Pinctrl驱动中并没有提供`pmxops->gpio_request_enable`或`pmxops->request` +* gpio_chip结构体中`direction_input`、`direction_output`,并没有配置引脚为GPIO功能 \ No newline at end of file diff --git a/IMX6ULL/doc_pic/07_GPIO/11_GPIO子系统的sysfs接口.md b/IMX6ULL/doc_pic/07_GPIO/11_GPIO子系统的sysfs接口.md new file mode 100644 index 0000000..4438cb0 --- /dev/null +++ b/IMX6ULL/doc_pic/07_GPIO/11_GPIO子系统的sysfs接口.md @@ -0,0 +1,110 @@ +## GPIO子系统的sysfs接口 + +参考资料: + +* Linux 5.x内核文档 + * Linux-5.4\Documentation\driver-api + * Linux-5.4\Documentation\devicetree\bindings\gpio\gpio.txt + * Linux-5.4\drivers\gpio\gpiolib-sysfs.c + +* Linux 4.x内核文档 + * Linux-4.9.88\Documentation\gpio + * Linux-4.9.88\Documentation\devicetree\bindings\gpio\gpio.txt + * Linux-4.9.88\drivers\gpio\gpiolib-sysfs.c + +* 本节视频代码在GIT仓库中 + + ```shell + doc_and_source_for_drivers\IMX6ULL\source\07_GPIO\04_gpio_use_pinctrl_ok + doc_and_source_for_drivers\STM32MP157\source\A7\07_GPIO\04_gpio_use_pinctrl_ok + ``` + + + +### 1. 驱动程序 + +驱动程序为`drivers\gpio\gpiolib-sysfs.c`,这里不打算分析它。 + + + +### 2. 常用的SYSFS文件 + +#### 2.1 有哪些GPIO控制器 + +`/sys/bus/gpio/devices`目录下,列出了所有的GPIO控制器,如下表示有11个GPIO控制器: + +```c +/sys/bus/gpio/devices/gpiochip0 +/sys/bus/gpio/devices/gpiochip1 +/sys/bus/gpio/devices/gpiochip2 +/sys/bus/gpio/devices/gpiochip3 +/sys/bus/gpio/devices/gpiochip4 +/sys/bus/gpio/devices/gpiochip5 +/sys/bus/gpio/devices/gpiochip6 +/sys/bus/gpio/devices/gpiochip7 +/sys/bus/gpio/devices/gpiochip8 +/sys/bus/gpio/devices/gpiochip9 +/sys/bus/gpio/devices/gpiochip10 +``` + + + +#### 2.2 每个GPIO控制器的详细信息 + +`/sys/class/gpio/gpiochipXXX`下,有这些信息: + +```shell +/sys/class/gpio/gpiochip508]# ls -1 +base // 这个GPIO控制器的GPIO编号 +device +label // 名字 +ngpio // 引脚个数 +power +subsystem +uevent +``` + + + +#### 2.3 查看GPIO使用情况 + +```shell +cat /sys/kernel/debug/gpio +``` + + + +#### 2.4 通过SYSFS使用GPIO + +如果只是简单的引脚控制(比如输出、查询输入值),可以不编写驱动程序。 + +但是涉及中断的话,就需要编写驱动程序了。 + +##### 1. 确定GPIO编号 + +查看每个`/sys/bus/gpio/devices/gpiochipXXX`目录下的label,确定是你要用的GPIO控制器,也称为GPIO Bank。 + +根据它名字gpiochipXXX,就可以知道基值是XXX。 + +基值加上引脚offset,就是这个引脚的编号。 + + + +##### 2. 导出/设置方向/读写值 + +举例: + +```shell +echo 509 > /sys/class/gpio/export +echo out > /sys/class/gpio/gpio509/direction +echo 1 > /sys/class/gpio/gpio509/value +echo 509 > /sys/class/gpio/unexport + +echo 509 > /sys/class/gpio/export +echo in > /sys/class/gpio/gpio509/direction +cat /sys/class/gpio/gpio509/value +echo 509 > /sys/class/gpio/unexport +``` + + + diff --git a/IMX6ULL/doc_pic/07_GPIO/pic/07_GPIO/12_gpio_and_pinctrl_range.png b/IMX6ULL/doc_pic/07_GPIO/pic/07_GPIO/12_gpio_and_pinctrl_range.png new file mode 100644 index 0000000..9bb71ec Binary files /dev/null and b/IMX6ULL/doc_pic/07_GPIO/pic/07_GPIO/12_gpio_and_pinctrl_range.png differ diff --git a/IMX6ULL/doc_pic/07_GPIO/pic/07_GPIO/13_gpio_pin_range.png b/IMX6ULL/doc_pic/07_GPIO/pic/07_GPIO/13_gpio_pin_range.png new file mode 100644 index 0000000..3c18b21 Binary files /dev/null and b/IMX6ULL/doc_pic/07_GPIO/pic/07_GPIO/13_gpio_pin_range.png differ diff --git a/IMX6ULL/doc_pic/07_GPIO/pic/07_GPIO/14_gpio_request.png b/IMX6ULL/doc_pic/07_GPIO/pic/07_GPIO/14_gpio_request.png new file mode 100644 index 0000000..efed222 Binary files /dev/null and b/IMX6ULL/doc_pic/07_GPIO/pic/07_GPIO/14_gpio_request.png differ diff --git a/IMX6ULL/source/07_GPIO/04_gpio_use_pinctrl_ok/01_led/Makefile b/IMX6ULL/source/07_GPIO/04_gpio_use_pinctrl_ok/01_led/Makefile new file mode 100644 index 0000000..ce80d85 --- /dev/null +++ b/IMX6ULL/source/07_GPIO/04_gpio_use_pinctrl_ok/01_led/Makefile @@ -0,0 +1,29 @@ + +# 1. 使用不同的开发板内核时, 一定要修改KERN_DIR +# 2. KERN_DIR中的内核要事先配置、编译, 为了能编译内核, 要先设置下列环境变量: +# 2.1 ARCH, 比如: export ARCH=arm64 +# 2.2 CROSS_COMPILE, 比如: export CROSS_COMPILE=aarch64-linux-gnu- +# 2.3 PATH, 比如: export PATH=$PATH:/home/book/100ask_roc-rk3399-pc/ToolChain-6.3.1/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin +# 注意: 不同的开发板不同的编译器上述3个环境变量不一定相同, +# 请参考各开发板的高级用户使用手册 + +KERN_DIR = /home/book/100ask_imx6ull-sdk/Linux-4.9.88 # 板子所用内核源码的目录 + +all: + make -C $(KERN_DIR) M=`pwd` modules + $(CROSS_COMPILE)gcc -o ledtest ledtest.c + +clean: + make -C $(KERN_DIR) M=`pwd` modules clean + rm -rf modules.order + rm -f ledtest + +# 参考内核源码drivers/char/ipmi/Makefile +# 要想把a.c, b.c编译成ab.ko, 可以这样指定: +# ab-y := a.o b.o +# obj-m += ab.o + + + +obj-m += leddrv.o + diff --git a/IMX6ULL/source/07_GPIO/04_gpio_use_pinctrl_ok/01_led/leddrv.c b/IMX6ULL/source/07_GPIO/04_gpio_use_pinctrl_ok/01_led/leddrv.c new file mode 100644 index 0000000..521c65d --- /dev/null +++ b/IMX6ULL/source/07_GPIO/04_gpio_use_pinctrl_ok/01_led/leddrv.c @@ -0,0 +1,161 @@ +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* 1. 确定主设备号 */ +static int major = 0; +static struct class *led_class; +static struct gpio_desc *led_gpio; + + +/* 3. 实现对应的open/read/write等函数,填入file_operations结构体 */ +static ssize_t led_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset) +{ + printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); + return 0; +} + +/* write(fd, &val, 1); */ +static ssize_t led_drv_write (struct file *file, const char __user *buf, size_t size, loff_t *offset) +{ + int err; + char status; + + printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); + err = copy_from_user(&status, buf, 1); + + /* 根据次设备号和status控制LED */ + gpiod_set_value(led_gpio, status); + + return 1; +} + +static int led_drv_open (struct inode *node, struct file *file) +{ + printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); + /* 根据次设备号初始化LED */ + gpiod_direction_output(led_gpio, 0); + + return 0; +} + +static int led_drv_close (struct inode *node, struct file *file) +{ + printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); + return 0; +} + +/* 定义自己的file_operations结构体 */ +static struct file_operations led_drv = { + .owner = THIS_MODULE, + .open = led_drv_open, + .read = led_drv_read, + .write = led_drv_write, + .release = led_drv_close, +}; + +/* 4. 从platform_device获得GPIO + * 把file_operations结构体告诉内核:注册驱动程序 + */ +static int chip_demo_gpio_probe(struct platform_device *pdev) +{ + printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); + + /* 4.1 设备树中定义有: led-gpios=<...>; */ + led_gpio = gpiod_get(&pdev->dev, "led", 0); + if (IS_ERR(led_gpio)) { + dev_err(&pdev->dev, "Failed to get GPIO for led\n"); + return PTR_ERR(led_gpio); + } + + /* 4.2 注册file_operations */ + major = register_chrdev(0, "100ask_led", &led_drv); /* /dev/led */ + + led_class = class_create(THIS_MODULE, "100ask_led_class"); + if (IS_ERR(led_class)) { + printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); + unregister_chrdev(major, "led"); + gpiod_put(led_gpio); + return PTR_ERR(led_class); + } + + device_create(led_class, NULL, MKDEV(major, 0), NULL, "100ask_led%d", 0); /* /dev/100ask_led0 */ + + return 0; + +} + +static int chip_demo_gpio_remove(struct platform_device *pdev) +{ + device_destroy(led_class, MKDEV(major, 0)); + class_destroy(led_class); + unregister_chrdev(major, "100ask_led"); + gpiod_put(led_gpio); + + return 0; +} + + +static const struct of_device_id ask100_leds[] = { + { .compatible = "100ask,leddrv" }, + { }, +}; + +/* 1. 定义platform_driver */ +static struct platform_driver chip_demo_gpio_driver = { + .probe = chip_demo_gpio_probe, + .remove = chip_demo_gpio_remove, + .driver = { + .name = "100ask_led", + .of_match_table = ask100_leds, + }, +}; + +/* 2. 在入口函数注册platform_driver */ +static int __init led_init(void) +{ + int err; + + printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); + + err = platform_driver_register(&chip_demo_gpio_driver); + + return err; +} + +/* 3. 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数 + * 卸载platform_driver + */ +static void __exit led_exit(void) +{ + printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); + + platform_driver_unregister(&chip_demo_gpio_driver); +} + + +/* 7. 其他完善:提供设备信息,自动创建设备节点 */ + +module_init(led_init); +module_exit(led_exit); + +MODULE_LICENSE("GPL"); + + diff --git a/IMX6ULL/source/07_GPIO/04_gpio_use_pinctrl_ok/01_led/leddrv_未测试的原始版本.c b/IMX6ULL/source/07_GPIO/04_gpio_use_pinctrl_ok/01_led/leddrv_未测试的原始版本.c new file mode 100644 index 0000000..6ef1a7c --- /dev/null +++ b/IMX6ULL/source/07_GPIO/04_gpio_use_pinctrl_ok/01_led/leddrv_未测试的原始版本.c @@ -0,0 +1,165 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* 1. 确定主设备号 */ +static int major = 0; +static struct class *led_class; +static struct gpio_desc *led_gpio; + + +/* 3. 实现对应的open/read/write等函数,填入file_operations结构体 */ +static ssize_t led_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset) +{ + printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); + return 0; +} + +/* write(fd, &val, 1); */ +static ssize_t led_drv_write (struct file *file, const char __user *buf, size_t size, loff_t *offset) +{ + int err; + char status; + struct inode *inode = file_inode(file); + int minor = iminor(inode); + + printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); + err = copy_from_user(&status, buf, 1); + + /* 根据次设备号和status控制LED */ + gpiod_set_value(led_gpio, status); + + return 1; +} + +static int led_drv_open (struct inode *node, struct file *file) +{ + int minor = iminor(node); + + printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); + /* 根据次设备号初始化LED */ + gpiod_direction_output(led_gpio, 0); + + return 0; +} + +static int led_drv_close (struct inode *node, struct file *file) +{ + printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); + return 0; +} + +/* 定义自己的file_operations结构体 */ +static struct file_operations led_drv = { + .owner = THIS_MODULE, + .open = led_drv_open, + .read = led_drv_read, + .write = led_drv_write, + .release = led_drv_close, +}; + +/* 4. 从platform_device获得GPIO + * 把file_operations结构体告诉内核:注册驱动程序 + */ +static int chip_demo_gpio_probe(struct platform_device *pdev) +{ + int err; + + printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); + + /* 4.1 设备树中定义有: led-gpios=<...>; */ + led_gpio = gpiod_get(&pdev->dev, "led", 0); + if (IS_ERR(led_gpio)) { + dev_err(&pdev->dev, "Failed to get GPIO for led\n"); + return PTR_ERR(led_gpio); + } + + /* 4.2 注册file_operations */ + major = register_chrdev(0, "100ask_led", &led_drv); /* /dev/led */ + + led_class = class_create(THIS_MODULE, "100ask_led_class"); + if (IS_ERR(led_class)) { + printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); + unregister_chrdev(major, "led"); + gpiod_free(led_gpio); + return PTR_ERR(led_class); + } + + device_create(led_class, NULL, MKDEV(major, 0), NULL, "100ask_led%d", 0); /* /dev/100ask_led0 */ + + return 0; + +} + +static int chip_demo_gpio_remove(struct platform_device *pdev) +{ + device_destroy(led_class, MKDEV(major, 0)); + class_destroy(led_class); + unregister_chrdev(major, "100ask_led"); + gpiod_free(led_gpio); + + return 0; +} + + +static const struct of_device_id ask100_leds[] = { + { .compatible = "100ask,leddrv" }, + { }, +}; + +/* 1. 定义platform_driver */ +static struct platform_driver chip_demo_gpio_driver = { + .probe = chip_demo_gpio_probe, + .remove = chip_demo_gpio_remove, + .driver = { + .name = "100ask_led", + .of_match_table = ask100_leds, + }, +}; + +/* 2. 在入口函数注册platform_driver */ +static int __init led_init(void) +{ + int err; + + printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); + + err = platform_driver_register(&chip_demo_gpio_driver); + + return err; +} + +/* 3. 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数 + * 卸载platform_driver + */ +static void __exit led_exit(void) +{ + printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); + + platform_driver_unregister(&chip_demo_gpio_driver); +} + + +/* 7. 其他完善:提供设备信息,自动创建设备节点 */ + +module_init(led_init); +module_exit(led_exit); + +MODULE_LICENSE("GPL"); + + diff --git a/IMX6ULL/source/07_GPIO/04_gpio_use_pinctrl_ok/01_led/ledtest.c b/IMX6ULL/source/07_GPIO/04_gpio_use_pinctrl_ok/01_led/ledtest.c new file mode 100644 index 0000000..099eb11 --- /dev/null +++ b/IMX6ULL/source/07_GPIO/04_gpio_use_pinctrl_ok/01_led/ledtest.c @@ -0,0 +1,50 @@ + +#include +#include +#include +#include +#include +#include + +/* + * ./ledtest /dev/100ask_led0 on + * ./ledtest /dev/100ask_led0 off + */ +int main(int argc, char **argv) +{ + int fd; + char status; + + /* 1. 判断参数 */ + if (argc != 3) + { + printf("Usage: %s \n", argv[0]); + return -1; + } + + /* 2. 打开文件 */ + fd = open(argv[1], O_RDWR); + if (fd == -1) + { + printf("can not open file %s\n", argv[1]); + return -1; + } + + /* 3. 写文件 */ + if (0 == strcmp(argv[2], "on")) + { + status = 1; + write(fd, &status, 1); + } + else + { + status = 0; + write(fd, &status, 1); + } + + close(fd); + + return 0; +} + + diff --git a/IMX6ULL/source/07_GPIO/04_gpio_use_pinctrl_ok/02_virtual_pinctrl_ok/Makefile b/IMX6ULL/source/07_GPIO/04_gpio_use_pinctrl_ok/02_virtual_pinctrl_ok/Makefile new file mode 100644 index 0000000..dbd4e41 --- /dev/null +++ b/IMX6ULL/source/07_GPIO/04_gpio_use_pinctrl_ok/02_virtual_pinctrl_ok/Makefile @@ -0,0 +1,22 @@ + +# 1. 使用不同的开发板内核时, 一定要修改KERN_DIR +# 2. KERN_DIR中的内核要事先配置、编译, 为了能编译内核, 要先设置下列环境变量: +# 2.1 ARCH, 比如: export ARCH=arm64 +# 2.2 CROSS_COMPILE, 比如: export CROSS_COMPILE=aarch64-linux-gnu- +# 2.3 PATH, 比如: export PATH=$PATH:/home/book/100ask_roc-rk3399-pc/ToolChain-6.3.1/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin +# 注意: 不同的开发板不同的编译器上述3个环境变量不一定相同, +# 请参考各开发板的高级用户使用手册 + +KERN_DIR = /home/book/100ask_imx6ull-sdk/Linux-4.9.88 + +all: + make -C $(KERN_DIR) M=`pwd` modules + +clean: + make -C $(KERN_DIR) M=`pwd` modules clean + rm -rf modules.order + +obj-m += virtual_pinctrl_driver.o +obj-m += virtual_pinctrl_client.o + + diff --git a/IMX6ULL/source/07_GPIO/04_gpio_use_pinctrl_ok/02_virtual_pinctrl_ok/core.h b/IMX6ULL/source/07_GPIO/04_gpio_use_pinctrl_ok/02_virtual_pinctrl_ok/core.h new file mode 100644 index 0000000..747c423 --- /dev/null +++ b/IMX6ULL/source/07_GPIO/04_gpio_use_pinctrl_ok/02_virtual_pinctrl_ok/core.h @@ -0,0 +1,205 @@ +/* + * Core private header for the pin control subsystem + * + * Copyright (C) 2011 ST-Ericsson SA + * Written on behalf of Linaro for ST-Ericsson + * + * Author: Linus Walleij + * + * License terms: GNU General Public License (GPL) version 2 + */ + +#include +#include +#include +#include +#include + +struct pinctrl_gpio_range; + +/** + * struct pinctrl_dev - pin control class device + * @node: node to include this pin controller in the global pin controller list + * @desc: the pin controller descriptor supplied when initializing this pin + * controller + * @pin_desc_tree: each pin descriptor for this pin controller is stored in + * this radix tree + * @gpio_ranges: a list of GPIO ranges that is handled by this pin controller, + * ranges are added to this list at runtime + * @dev: the device entry for this pin controller + * @owner: module providing the pin controller, used for refcounting + * @driver_data: driver data for drivers registering to the pin controller + * subsystem + * @p: result of pinctrl_get() for this device + * @hog_default: default state for pins hogged by this device + * @hog_sleep: sleep state for pins hogged by this device + * @mutex: mutex taken on each pin controller specific action + * @device_root: debugfs root for this device + */ +struct pinctrl_dev { + struct list_head node; + struct pinctrl_desc *desc; + struct radix_tree_root pin_desc_tree; + struct list_head gpio_ranges; + struct device *dev; + struct module *owner; + void *driver_data; + struct pinctrl *p; + struct pinctrl_state *hog_default; + struct pinctrl_state *hog_sleep; + struct mutex mutex; +#ifdef CONFIG_DEBUG_FS + struct dentry *device_root; +#endif +}; + +/** + * struct pinctrl - per-device pin control state holder + * @node: global list node + * @dev: the device using this pin control handle + * @states: a list of states for this device + * @state: the current state + * @dt_maps: the mapping table chunks dynamically parsed from device tree for + * this device, if any + * @users: reference count + */ +struct pinctrl { + struct list_head node; + struct device *dev; + struct list_head states; + struct pinctrl_state *state; + struct list_head dt_maps; + struct kref users; +}; + +/** + * struct pinctrl_state - a pinctrl state for a device + * @node: list node for struct pinctrl's @states field + * @name: the name of this state + * @settings: a list of settings for this state + */ +struct pinctrl_state { + struct list_head node; + const char *name; + struct list_head settings; +}; + +/** + * struct pinctrl_setting_mux - setting data for MAP_TYPE_MUX_GROUP + * @group: the group selector to program + * @func: the function selector to program + */ +struct pinctrl_setting_mux { + unsigned group; + unsigned func; +}; + +/** + * struct pinctrl_setting_configs - setting data for MAP_TYPE_CONFIGS_* + * @group_or_pin: the group selector or pin ID to program + * @configs: a pointer to an array of config parameters/values to program into + * hardware. Each individual pin controller defines the format and meaning + * of config parameters. + * @num_configs: the number of entries in array @configs + */ +struct pinctrl_setting_configs { + unsigned group_or_pin; + unsigned long *configs; + unsigned num_configs; +}; + +/** + * struct pinctrl_setting - an individual mux or config setting + * @node: list node for struct pinctrl_settings's @settings field + * @type: the type of setting + * @pctldev: pin control device handling to be programmed. Not used for + * PIN_MAP_TYPE_DUMMY_STATE. + * @dev_name: the name of the device using this state + * @data: Data specific to the setting type + */ +struct pinctrl_setting { + struct list_head node; + enum pinctrl_map_type type; + struct pinctrl_dev *pctldev; + const char *dev_name; + union { + struct pinctrl_setting_mux mux; + struct pinctrl_setting_configs configs; + } data; +}; + +/** + * struct pin_desc - pin descriptor for each physical pin in the arch + * @pctldev: corresponding pin control device + * @name: a name for the pin, e.g. the name of the pin/pad/finger on a + * datasheet or such + * @dynamic_name: if the name of this pin was dynamically allocated + * @drv_data: driver-defined per-pin data. pinctrl core does not touch this + * @mux_usecount: If zero, the pin is not claimed, and @owner should be NULL. + * If non-zero, this pin is claimed by @owner. This field is an integer + * rather than a boolean, since pinctrl_get() might process multiple + * mapping table entries that refer to, and hence claim, the same group + * or pin, and each of these will increment the @usecount. + * @mux_owner: The name of device that called pinctrl_get(). + * @mux_setting: The most recent selected mux setting for this pin, if any. + * @gpio_owner: If pinctrl_request_gpio() was called for this pin, this is + * the name of the GPIO that "owns" this pin. + */ +struct pin_desc { + struct pinctrl_dev *pctldev; + const char *name; + bool dynamic_name; + void *drv_data; + /* These fields only added when supporting pinmux drivers */ +#ifdef CONFIG_PINMUX + unsigned mux_usecount; + const char *mux_owner; + const struct pinctrl_setting_mux *mux_setting; + const char *gpio_owner; +#endif +}; + +/** + * struct pinctrl_maps - a list item containing part of the mapping table + * @node: mapping table list node + * @maps: array of mapping table entries + * @num_maps: the number of entries in @maps + */ +struct pinctrl_maps { + struct list_head node; + struct pinctrl_map const *maps; + unsigned num_maps; +}; + +struct pinctrl_dev *get_pinctrl_dev_from_devname(const char *dev_name); +struct pinctrl_dev *get_pinctrl_dev_from_of_node(struct device_node *np); +int pin_get_from_name(struct pinctrl_dev *pctldev, const char *name); +const char *pin_get_name(struct pinctrl_dev *pctldev, const unsigned pin); +int pinctrl_get_group_selector(struct pinctrl_dev *pctldev, + const char *pin_group); + +static inline struct pin_desc *pin_desc_get(struct pinctrl_dev *pctldev, + unsigned int pin) +{ + return radix_tree_lookup(&pctldev->pin_desc_tree, pin); +} + +extern struct pinctrl_gpio_range * +pinctrl_find_gpio_range_from_pin_nolock(struct pinctrl_dev *pctldev, + unsigned int pin); + +int pinctrl_register_map(struct pinctrl_map const *maps, unsigned num_maps, + bool dup); +void pinctrl_unregister_map(struct pinctrl_map const *map); + +extern int pinctrl_force_sleep(struct pinctrl_dev *pctldev); +extern int pinctrl_force_default(struct pinctrl_dev *pctldev); + +extern struct mutex pinctrl_maps_mutex; +extern struct list_head pinctrl_maps; + +#define for_each_maps(_maps_node_, _i_, _map_) \ + list_for_each_entry(_maps_node_, &pinctrl_maps, node) \ + for (_i_ = 0, _map_ = &_maps_node_->maps[_i_]; \ + _i_ < _maps_node_->num_maps; \ + _i_++, _map_ = &_maps_node_->maps[_i_]) diff --git a/IMX6ULL/source/07_GPIO/04_gpio_use_pinctrl_ok/02_virtual_pinctrl_ok/virtual_pinctrl_client.c b/IMX6ULL/source/07_GPIO/04_gpio_use_pinctrl_ok/02_virtual_pinctrl_ok/virtual_pinctrl_client.c new file mode 100644 index 0000000..bced33e --- /dev/null +++ b/IMX6ULL/source/07_GPIO/04_gpio_use_pinctrl_ok/02_virtual_pinctrl_ok/virtual_pinctrl_client.c @@ -0,0 +1,67 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const struct of_device_id virtual_client_of_match[] = { + { .compatible = "100ask,virtual_i2c", }, + { }, +}; + +static int virtual_client_probe(struct platform_device *pdev) +{ + printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); + return 0; +} +static int virtual_client_remove(struct platform_device *pdev) +{ + + printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); + return 0; +} + + +static struct platform_driver virtual_client_driver = { + .probe = virtual_client_probe, + .remove = virtual_client_remove, + .driver = { + .name = "100ask_virtual_client", + .of_match_table = of_match_ptr(virtual_client_of_match), + } +}; + + +/* 1. 入口函数 */ +static int __init virtual_client_init(void) +{ + printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); + /* 1.1 注册一个platform_driver */ + return platform_driver_register(&virtual_client_driver); +} + + +/* 2. 出口函数 */ +static void __exit virtual_client_exit(void) +{ + printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); + /* 2.1 反注册platform_driver */ + platform_driver_unregister(&virtual_client_driver); +} + +module_init(virtual_client_init); +module_exit(virtual_client_exit); + +MODULE_LICENSE("GPL"); + + + diff --git a/IMX6ULL/source/07_GPIO/04_gpio_use_pinctrl_ok/02_virtual_pinctrl_ok/virtual_pinctrl_driver.c b/IMX6ULL/source/07_GPIO/04_gpio_use_pinctrl_ok/02_virtual_pinctrl_ok/virtual_pinctrl_driver.c new file mode 100644 index 0000000..80c60e8 --- /dev/null +++ b/IMX6ULL/source/07_GPIO/04_gpio_use_pinctrl_ok/02_virtual_pinctrl_ok/virtual_pinctrl_driver.c @@ -0,0 +1,329 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core.h" + + +static struct pinctrl_dev *g_pinctrl_dev; + +static const struct pinctrl_pin_desc pins[] = { + {0, "pin0", NULL}, + {1, "pin1", NULL}, + {2, "pin2", NULL}, + {3, "pin3", NULL}, +}; + +static unsigned long g_configs[4]; + +struct virtual_functions_desc { + const char *func_name; + const char **groups; + int num_groups; +}; + + +static const char *func0_grps[] = {"pin0", "pin1", "pin2", "pin3"}; +static const char *func1_grps[] = {"pin0", "pin1"}; +static const char *func2_grps[] = {"pin2", "pin3"}; + +static struct virtual_functions_desc g_funcs_des[] = { + {"gpio", func0_grps, 4}, + {"i2c", func1_grps, 2}, + {"uart", func2_grps, 2}, +}; + + +static const struct of_device_id virtual_pinctrl_of_match[] = { + { .compatible = "100ask,virtual_pinctrl", }, + { }, +}; + +static int virtual_get_groups_count(struct pinctrl_dev *pctldev) +{ + return pctldev->desc->npins; +} + +static const char *virtual_get_group_name(struct pinctrl_dev *pctldev, + unsigned selector) +{ + return pctldev->desc->pins[selector].name; +} + +static int virtual_get_group_pins(struct pinctrl_dev *pctldev, unsigned selector, + const unsigned **pins, + unsigned *npins) +{ + if (selector >= pctldev->desc->npins) + return -EINVAL; + + *pins = &pctldev->desc->pins[selector].number; + *npins = 1; + + return 0; +} + +static void virtual_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s, + unsigned offset) +{ + seq_printf(s, "%s", dev_name(pctldev->dev)); +} + +/* + i2cgrp { + functions = "i2c", "i2c"; + groups = "pin0", "pin1"; + configs = <0x11223344 0x55667788>; + }; + + one pin ==> two pinctrl_map (one for mux, one for config) + + */ +static int virtual_dt_node_to_map(struct pinctrl_dev *pctldev, + struct device_node *np, + struct pinctrl_map **map, unsigned *num_maps) +{ + int i; + int num_pins = 0; + const char *pin; + const char *function; + unsigned int config; + struct pinctrl_map *new_map; + unsigned long *configs; + + /* 1. 确定pin个数/分配pinctrl_map */ + while (1) + { + if (of_property_read_string_index(np, "groups", num_pins, &pin) == 0) + num_pins++; + else + break; + } + + new_map = kmalloc(sizeof(struct pinctrl_map) * num_pins * 2, GFP_KERNEL); + + + for (i = 0; i < num_pins; i++) + { + /* 2. get pin/function/config */ + of_property_read_string_index(np, "groups", i, &pin); + of_property_read_string_index(np, "functions", i, &function); + of_property_read_u32_index(np, "configs", i, &config); + + + /* 3. 存入pinctrl_map */ + configs = kmalloc(sizeof(*configs), GFP_KERNEL); + + new_map[i*2].type = PIN_MAP_TYPE_MUX_GROUP; + new_map[i*2].data.mux.function = function; + new_map[i*2].data.mux.group = pin; + + new_map[i*2+1].type = PIN_MAP_TYPE_CONFIGS_PIN; + new_map[i*2+1].data.configs.group_or_pin = pin; + new_map[i*2+1].data.configs.configs = configs; + configs[0] = config; + new_map[i*2+1].data.configs.num_configs = 1; + + } + + *map = new_map; + *num_maps = num_pins * 2; + + return 0; +} +static void virtual_dt_free_map(struct pinctrl_dev *pctldev, + struct pinctrl_map *map, unsigned num_maps) +{ + while (num_maps--) + { + if (map->type == PIN_MAP_TYPE_CONFIGS_PIN) + kfree(map->data.configs.configs); + + kfree(map); + map++; + } +} + +static const struct pinctrl_ops virtual_pctrl_ops = { + .get_groups_count = virtual_get_groups_count, + .get_group_name = virtual_get_group_name, + .get_group_pins = virtual_get_group_pins, + .pin_dbg_show = virtual_pin_dbg_show, + .dt_node_to_map = virtual_dt_node_to_map, + .dt_free_map = virtual_dt_free_map, + +}; + +static int virtual_pmx_get_funcs_count(struct pinctrl_dev *pctldev) +{ + return ARRAY_SIZE(g_funcs_des); +} + +static const char *virtual_pmx_get_func_name(struct pinctrl_dev *pctldev, + unsigned selector) +{ + return g_funcs_des[selector].func_name; +} + +static int virtual_pmx_get_groups(struct pinctrl_dev *pctldev, unsigned selector, + const char * const **groups, + unsigned * const num_groups) +{ + *groups = g_funcs_des[selector].groups; + *num_groups = g_funcs_des[selector].num_groups; + + return 0; +} + +static int virtual_pmx_set(struct pinctrl_dev *pctldev, unsigned selector, + unsigned group) +{ + printk("set %s as %s\n", pctldev->desc->pins[group].name, g_funcs_des[selector].func_name); + return 0; +} + +static int virtual_pmx_gpio_request_enable (struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned offset) +{ + printk("set and enable pin %s as GPIO\n", pctldev->desc->pins[offset].name); + return 0; +} + + +static const struct pinmux_ops virtual_pmx_ops = { + .get_functions_count = virtual_pmx_get_funcs_count, + .get_function_name = virtual_pmx_get_func_name, + .get_function_groups = virtual_pmx_get_groups, + .set_mux = virtual_pmx_set, + .gpio_request_enable = virtual_pmx_gpio_request_enable, +}; + + +static int virtual_pinconf_get(struct pinctrl_dev *pctldev, + unsigned pin_id, unsigned long *config) +{ + *config = g_configs[pin_id]; + return 0; +} + + +static int virtual_pinconf_set(struct pinctrl_dev *pctldev, + unsigned pin_id, unsigned long *configs, + unsigned num_configs) +{ + if (num_configs != 1) + return -EINVAL; + + g_configs[pin_id] = *configs; + + printk("config %s as 0x%lx\n", pctldev->desc->pins[pin_id].name, *configs); + + return 0; +} + +static void virtual_pinconf_dbg_show(struct pinctrl_dev *pctldev, + struct seq_file *s, unsigned pin_id) +{ + seq_printf(s, "0x%lx", g_configs[pin_id]); +} + +static void virtual_pinconf_group_dbg_show(struct pinctrl_dev *pctldev, + struct seq_file *s, unsigned pin_id) +{ + seq_printf(s, "0x%lx", g_configs[pin_id]); +} + + +static const struct pinconf_ops virtual_pinconf_ops = { + .pin_config_get = virtual_pinconf_get, + .pin_config_set = virtual_pinconf_set, + .pin_config_dbg_show = virtual_pinconf_dbg_show, + .pin_config_group_dbg_show = virtual_pinconf_group_dbg_show, +}; + + + +static int virtual_pinctrl_probe(struct platform_device *pdev) +{ + struct pinctrl_desc *pictrl; + + printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); + + /* a. 分配pinctrl_desc */ + pictrl = devm_kzalloc(&pdev->dev, sizeof(*pictrl), GFP_KERNEL); + + /* b. 设置pinctrl_desc */ + pictrl->name = dev_name(&pdev->dev); + pictrl->owner = THIS_MODULE; + + /* b.1 pins and group */ + pictrl->pins = pins; + pictrl->npins = ARRAY_SIZE(pins); + + pictrl->pctlops = &virtual_pctrl_ops; + + /* b.2 pin mux */ + pictrl->pmxops = &virtual_pmx_ops; + + /* b.3 pin config */ + pictrl->confops = &virtual_pinconf_ops; + + /* c. 注册pinctrl_desc */ + g_pinctrl_dev = devm_pinctrl_register(&pdev->dev, pictrl, NULL); + + return 0; +} +static int virtual_pinctrl_remove(struct platform_device *pdev) +{ + + printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); + return 0; +} + + +static struct platform_driver virtual_pinctrl_driver = { + .probe = virtual_pinctrl_probe, + .remove = virtual_pinctrl_remove, + .driver = { + .name = "100ask_virtual_pinctrl", + .of_match_table = of_match_ptr(virtual_pinctrl_of_match), + } +}; + + +/* 1. 入口函数 */ +static int __init virtual_pinctrl_init(void) +{ + printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); + /* 1.1 注册一个platform_driver */ + return platform_driver_register(&virtual_pinctrl_driver); +} + + +/* 2. 出口函数 */ +static void __exit virtual_pinctrl_exit(void) +{ + printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); + /* 2.1 反注册platform_driver */ + platform_driver_unregister(&virtual_pinctrl_driver); +} + +module_init(virtual_pinctrl_init); +module_exit(virtual_pinctrl_exit); + +MODULE_LICENSE("GPL"); + + diff --git a/IMX6ULL/source/07_GPIO/04_gpio_use_pinctrl_ok/03_virtual_gpio_ok/Makefile b/IMX6ULL/source/07_GPIO/04_gpio_use_pinctrl_ok/03_virtual_gpio_ok/Makefile new file mode 100644 index 0000000..95163c6 --- /dev/null +++ b/IMX6ULL/source/07_GPIO/04_gpio_use_pinctrl_ok/03_virtual_gpio_ok/Makefile @@ -0,0 +1,21 @@ + +# 1. 使用不同的开发板内核时, 一定要修改KERN_DIR +# 2. KERN_DIR中的内核要事先配置、编译, 为了能编译内核, 要先设置下列环境变量: +# 2.1 ARCH, 比如: export ARCH=arm64 +# 2.2 CROSS_COMPILE, 比如: export CROSS_COMPILE=aarch64-linux-gnu- +# 2.3 PATH, 比如: export PATH=$PATH:/home/book/100ask_roc-rk3399-pc/ToolChain-6.3.1/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin +# 注意: 不同的开发板不同的编译器上述3个环境变量不一定相同, +# 请参考各开发板的高级用户使用手册 + +KERN_DIR = /home/book/100ask_imx6ull-sdk/Linux-4.9.88 + +all: + make -C $(KERN_DIR) M=`pwd` modules + +clean: + make -C $(KERN_DIR) M=`pwd` modules clean + rm -rf modules.order + +obj-m += virtual_gpio_driver.o + + diff --git a/IMX6ULL/source/07_GPIO/04_gpio_use_pinctrl_ok/03_virtual_gpio_ok/virtual_gpio_driver.c b/IMX6ULL/source/07_GPIO/04_gpio_use_pinctrl_ok/03_virtual_gpio_ok/virtual_gpio_driver.c new file mode 100644 index 0000000..4585219 --- /dev/null +++ b/IMX6ULL/source/07_GPIO/04_gpio_use_pinctrl_ok/03_virtual_gpio_ok/virtual_gpio_driver.c @@ -0,0 +1,128 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct gpio_chip * g_virt_gpio; +static int g_gpio_val = 0; + +static const struct of_device_id virtual_gpio_of_match[] = { + { .compatible = "100ask,virtual_gpio", }, + { }, +}; + +static int virt_gpio_direction_output(struct gpio_chip *gc, + unsigned offset, int val) +{ + printk("set pin %d as output %s\n", offset, val ? "high" : "low"); + return 0; +} + +static int virt_gpio_direction_input(struct gpio_chip *chip, + unsigned offset) +{ + printk("set pin %d as input\n", offset); + return 0; +} + + +static int virt_gpio_get_value(struct gpio_chip *gc, unsigned offset) +{ + int val; + val = (g_gpio_val & (1<dev, sizeof(*g_virt_gpio), GFP_KERNEL); + + /* 2. 设置gpio_chip */ + + /* 2.1 设置函数 */ + g_virt_gpio->label = pdev->name; + g_virt_gpio->direction_output = virt_gpio_direction_output; + g_virt_gpio->direction_input = virt_gpio_direction_input; + g_virt_gpio->get = virt_gpio_get_value; + g_virt_gpio->set = virt_gpio_set_value; + g_virt_gpio->request = gpiochip_generic_request; + + g_virt_gpio->parent = &pdev->dev; + g_virt_gpio->owner = THIS_MODULE; + + /* 2.2 设置base、ngpio值 */ + g_virt_gpio->base = -1; + ret = of_property_read_u32(pdev->dev.of_node, "ngpios", &val); + g_virt_gpio->ngpio = val; + + /* 3. 注册gpio_chip */ + ret = devm_gpiochip_add_data(&pdev->dev, g_virt_gpio, NULL); + + return 0; +} +static int virtual_gpio_remove(struct platform_device *pdev) +{ + printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); + return 0; +} + + +static struct platform_driver virtual_gpio_driver = { + .probe = virtual_gpio_probe, + .remove = virtual_gpio_remove, + .driver = { + .name = "100ask_virtual_gpio", + .of_match_table = of_match_ptr(virtual_gpio_of_match), + } +}; + + +/* 1. 入口函数 */ +static int __init virtual_gpio_init(void) +{ + printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); + /* 1.1 注册一个platform_driver */ + return platform_driver_register(&virtual_gpio_driver); +} + + +/* 2. 出口函数 */ +static void __exit virtual_gpio_exit(void) +{ + printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); + /* 2.1 反注册platform_driver */ + platform_driver_unregister(&virtual_gpio_driver); +} + +module_init(virtual_gpio_init); +module_exit(virtual_gpio_exit); + +MODULE_LICENSE("GPL"); + + diff --git a/README.md b/README.md index 002eec6..22bb448 100644 --- a/README.md +++ b/README.md @@ -243,6 +243,17 @@ git clone https://e.coding.net/weidongshan/linux/doc_and_source_for_drivers.git 07_编写一个虚拟GPIO控制器的驱动程序 08_调试与使用虚拟的GPIO控制器 ``` + +* 2021.05.29 完结"GPIO子系统": + + ```shell + 09_GPIO子系统与Pinctrl子系统的交互 + 10_编程_GPIO使用Pinctrl + 11_GPIO子系统的sysfs接口 + ``` + + + ## 6. 联系方式 diff --git a/STM32MP157/doc_pic/07_GPIO/09_GPIO子系统与Pinctrl子系统的交互.md b/STM32MP157/doc_pic/07_GPIO/09_GPIO子系统与Pinctrl子系统的交互.md new file mode 100644 index 0000000..34e0c19 --- /dev/null +++ b/STM32MP157/doc_pic/07_GPIO/09_GPIO子系统与Pinctrl子系统的交互.md @@ -0,0 +1,227 @@ +## GPIO子系统与Pinctrl子系统的交互 + +参考资料: + +* Linux 5.x内核文档 + * Linux-5.4\Documentation\driver-api + * Linux-5.4\Documentation\devicetree\bindings\gpio\gpio.txt + * Linux-5.4\drivers\gpio\gpio-74x164.c + +* Linux 4.x内核文档 + * Linux-4.9.88\Documentation\gpio + * Linux-4.9.88\Documentation\devicetree\bindings\gpio\gpio.txt + * Linux-4.9.88\drivers\gpio\gpio-74x164.c + + +### 1. 使用GPIO前应该设置Pinctrl + +假设使用这个虚拟的GPIO Controller的pinA来控制LED: + +![image-20210528163702792](pic/07_GPIO/11_virtual_gpio_led.png) + + + +要使用pinA来控制LED,首先要通过Pinctrl子系统把它设置为GPIO功能,然后才能设置它为输出引脚、设置它的输出值。 + +所以在设备树文件里,应该添加Pinctrl的内容: + +```shell +virtual_pincontroller { + compatible = "100ask,virtual_pinctrl"; + myled_pin: myled_pin { + functions = "gpio"; + groups = "pin0"; + configs = <0x11223344>; + }; +}; + +gpio_virt: virtual_gpiocontroller { + compatible = "100ask,virtual_gpio"; + gpio-controller; + #gpio-cells = <2>; + ngpios = <4>; +}; + +myled { + compatible = "100ask,leddrv"; + led-gpios = <&gpio_virt 0 GPIO_ACTIVE_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&myled_pin>; +}; +``` + + + +但是很多芯片,并不要求在设备树中把把引脚复用为GPIO功能。 + +比如STM32MP157,在它的设备树工具`STM32CubeMX`即使把引脚配置为GPIO功能,它也不会在设备树中出现。 + +原因在于:GPIO走了后门。 + +现实的芯片中,并没有Pinctrl这样的硬件,它的功能大部分是在GPIO模块中实现的。 + +Pinctrl是一个软件虚拟处理的概念,它的实现本来就跟GPIO密切相关。 + +甚至一些引脚默认就是GPIO功能。 + +按理说: + +一个引脚可能被用作GPIO,也可能被用作I2C,GPIO和I2C这些功能时相同低位的。 + +要用作GPIO,需要先通过Pinctrl把引脚复用为GPIO功能。 + +但是Pinctrl和GPIO关系密切,当你使用gpiod_get获得GPIO引脚时,它就`偷偷地`通过Pinctrl把引脚复用为GPIO功能了。 + + + +### 2. GPIO和Pinctrl的映射关系 + +#### 2.1 示例 + +![image-20210529094937479](pic/07_GPIO/12_gpio_and_pinctrl_range.png) + +从上图可知: + +* 左边的Pinctrl支持8个引脚,在Pinctrl的内部编号为0~7 +* 图中有2个GPIO控制器 + * GPIO0内部引脚编号为0~3,假设在GPIO子系统中全局编号为100~103 + * GPIO1内部引脚编号为0~3,假设在GPIO子系统中全局编号为104~107 + +* 假设我们要使用pin1_1,应该这样做: + * 根据GPIO1的内部编号1,可以换算为Pinctrl子系统中的编号5 + * 使用Pinctrl的函数,把第5个引脚配置为GPIO功能 + + + +#### 2.2 数据结构 + +![image-20210529101049897](pic/07_GPIO/13_gpio_pin_range.png) + + + +### 3. GPIO调用Pinctrl的过程 + +GPIO子系统中的request函数,用来申请某个GPIO引脚, + +它会导致Pinctrl子系统中的这2个函数之一被调用:`pmxops->gpio_request_enable`或`pmxops->request` + +![image-20210529101834908](pic/07_GPIO/14_gpio_request.png) + +调用关系如下: + +```c +gpiod_get + gpiod_get_index + desc = of_find_gpio(dev, con_id, idx, &lookupflags); + ret = gpiod_request(desc, con_id ? con_id : devname); + ret = gpiod_request_commit(desc, label); + if (chip->request) { + ret = chip->request(chip, offset); + } +``` + + + +我们编写GPIO驱动程序时,所设置`chip->request`函数,一般直接调用`gpiochip_generic_request`,它导致Pinctrl把引脚复用为GPIO功能。 + +```c +gpiochip_generic_request(struct gpio_chip *chip, unsigned offset) + pinctrl_request_gpio(chip->gpiodev->base + offset) + ret = pinctrl_get_device_gpio_range(gpio, &pctldev, &range); // gpio是引脚的全局编号 + + /* Convert to the pin controllers number space */ + pin = gpio_to_pin(range, gpio); + + ret = pinmux_request_gpio(pctldev, range, pin, gpio); + ret = pin_request(pctldev, pin, owner, range); +``` + + + +Pinctrl子系统中的pin_request函数就会把引脚配置为GPIO功能: + +```c +static int pin_request(struct pinctrl_dev *pctldev, + int pin, const char *owner, + struct pinctrl_gpio_range *gpio_range) +{ + const struct pinmux_ops *ops = pctldev->desc->pmxops; + + /* + * If there is no kind of request function for the pin we just assume + * we got it by default and proceed. + */ + if (gpio_range && ops->gpio_request_enable) + /* This requests and enables a single GPIO pin */ + status = ops->gpio_request_enable(pctldev, gpio_range, pin); + else if (ops->request) + status = ops->request(pctldev, pin); + else + status = 0; +} +``` + + + +### 3. 我们要做什么 + +如果不想在使用GPIO引脚时,在设备树中设置Pinctrl信息, + +如果想让GPIO和Pinctrl之间建立联系, + +我们需要做这些事情: + +#### 3.1 表明GPIO和Pinctrl间的联系 + +在GPIO设备树中使用`gpio-ranges`来描述它们之间的联系: + + * GPIO系统中有引脚号 + + * Pinctrl子系统中也有自己的引脚号 + + * 2个号码要建立映射关系 + + * 在GPIO设备树中使用如下代码建立映射关系 + + ```shell + // 当前GPIO控制器的0号引脚, 对应pinctrlA中的128号引脚, 数量为12 + gpio-ranges = <&pinctrlA 0 128 12>; + ``` + +#### 3.2 解析这些联系 + +在GPIO驱动程序中,解析跟Pinctrl之间的联系:处理`gpio-ranges`: + + * 这不需要我们自己写代码 + + * 注册gpio_chip时会自动调用 + + ```c + int gpiochip_add_data(struct gpio_chip *chip, void *data) + status = of_gpiochip_add(chip); + status = of_gpiochip_add_pin_range(chip); + + of_gpiochip_add_pin_range + for (;; index++) { + ret = of_parse_phandle_with_fixed_args(np, "gpio-ranges", 3, + index, &pinspec); + + pctldev = of_pinctrl_get(pinspec.np); // 根据gpio-ranges的第1个参数找到pctldev + + // 增加映射关系 + /* npins != 0: linear range */ + ret = gpiochip_add_pin_range(chip, + pinctrl_dev_get_devname(pctldev), + pinspec.args[0], + pinspec.args[1], + pinspec.args[2]); + ``` + + + +#### 3.3 编程 +* 在GPIO驱动程序中,提供`gpio_chip->request` + +* 在Pinctrl驱动程序中,提供`pmxops->gpio_request_enable`或`pmxops->request` + + diff --git a/STM32MP157/doc_pic/07_GPIO/09_GPIO子系统与Pinctrl子系统的交互.tif b/STM32MP157/doc_pic/07_GPIO/09_GPIO子系统与Pinctrl子系统的交互.tif new file mode 100644 index 0000000..b432dad Binary files /dev/null and b/STM32MP157/doc_pic/07_GPIO/09_GPIO子系统与Pinctrl子系统的交互.tif differ diff --git a/STM32MP157/doc_pic/07_GPIO/10_编程_GPIO使用Pinctrl.md b/STM32MP157/doc_pic/07_GPIO/10_编程_GPIO使用Pinctrl.md new file mode 100644 index 0000000..9283c07 --- /dev/null +++ b/STM32MP157/doc_pic/07_GPIO/10_编程_GPIO使用Pinctrl.md @@ -0,0 +1,396 @@ +## 编程_GPIO使用Pinctrl + +参考资料: + +* Linux 5.x内核文档 + * Linux-5.4\Documentation\driver-api + * Linux-5.4\Documentation\devicetree\bindings\gpio\gpio.txt + * Linux-5.4\drivers\gpio\gpio-74x164.c + +* Linux 4.x内核文档 + * Linux-4.9.88\Documentation\gpio + * Linux-4.9.88\Documentation\devicetree\bindings\gpio\gpio.txt + * Linux-4.9.88\drivers\gpio\gpio-74x164.c + +* 本节视频代码在GIT仓库中 + + ```shell + doc_and_source_for_drivers\IMX6ULL\source\07_GPIO\04_gpio_use_pinctrl_ok + doc_and_source_for_drivers\STM32MP157\source\A7\07_GPIO\04_gpio_use_pinctrl_ok + ``` + + + +### 1. 我们要做什么 + +假设使用这个虚拟的GPIO Controller的pinA来控制LED: + +![image-20210528163702792](pic/07_GPIO/11_virtual_gpio_led.png) + +如果不想在使用GPIO引脚时,在设备树中设置Pinctrl信息, + +如果想让GPIO和Pinctrl之间建立联系, + +我们需要做这些事情: + +#### 1.1 表明GPIO和Pinctrl间的联系 + +在GPIO设备树中使用`gpio-ranges`来描述它们之间的联系: + + * GPIO系统中有引脚号 + + * Pinctrl子系统中也有自己的引脚号 + + * 2个号码要建立映射关系 + + * 在GPIO设备树中使用如下代码建立映射关系 + + ```shell + // 当前GPIO控制器的0号引脚, 对应pinctrlA中的128号引脚, 数量为12 + gpio-ranges = <&pinctrlA 0 128 12>; + ``` + +#### 1.2 解析这些联系 + +在GPIO驱动程序中,解析跟Pinctrl之间的联系:处理`gpio-ranges`: + + * 这不需要我们自己写代码 + + * 注册gpio_chip时会自动调用 + + ```c + int gpiochip_add_data(struct gpio_chip *chip, void *data) + status = of_gpiochip_add(chip); + status = of_gpiochip_add_pin_range(chip); + + of_gpiochip_add_pin_range + for (;; index++) { + ret = of_parse_phandle_with_fixed_args(np, "gpio-ranges", 3, + index, &pinspec); + + pctldev = of_pinctrl_get(pinspec.np); // 根据gpio-ranges的第1个参数找到pctldev + + // 增加映射关系 + /* npins != 0: linear range */ + ret = gpiochip_add_pin_range(chip, + pinctrl_dev_get_devname(pctldev), + pinspec.args[0], + pinspec.args[1], + pinspec.args[2]); + ``` + + +#### 1.3 编程 + +* 在GPIO驱动程序中,提供`gpio_chip->request` + +* 在Pinctrl驱动程序中,提供`pmxops->gpio_request_enable`或`pmxops->request` + + + +### 2. 编写设备树 + +```shell +pinctrl_virt: virtual_pincontroller { + compatible = "100ask,virtual_pinctrl"; +}; + +gpio_virt: virtual_gpiocontroller { + compatible = "100ask,virtual_gpio"; + gpio-controller; + #gpio-cells = <2>; + ngpios = <4>; + gpio-ranges = <&pinctrl_virt 0 0 4>; +}; + +myled { + compatible = "100ask,leddrv"; + led-gpios = <&gpio_virt 0 GPIO_ACTIVE_LOW>; +}; +``` + + + +### 3. 编程 + +#### 3.1 GPIO控制器编程 + +gpio_chip中提供request函数: + +```c +chip->request = gpiochip_generic_request; +``` + + + +#### 3.2 Pinctrl编程 + +```c +static const struct pinmux_ops virtual_pmx_ops = { + .get_functions_count = virtual_pmx_get_funcs_count, + .get_function_name = virtual_pmx_get_func_name, + .get_function_groups = virtual_pmx_get_groups, + .set_mux = virtual_pmx_set, + .gpio_request_enable = virtual_pmx_gpio_request_enable, +}; +``` + + + +### 4. 上机实验 + +#### 4.1 设置工具链 + +##### 1. STM32MP157 + + **注意**:对于STM32MP157,以前说编译内核/驱动、编译APP的工具链不一样,其实编译APP用的工具链也能用来编译内核。 + + ```shell + export ARCH=arm + export CROSS_COMPILE=arm-buildroot-linux-gnueabihf- + export PATH=$PATH:/home/book/100ask_stm32mp157_pro-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin + ``` + +##### 2. IMX6ULL + + ```shell + export ARCH=arm + export CROSS_COMPILE=arm-linux-gnueabihf- + export PATH=$PATH:/home/book/100ask_imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin + ``` + + + +#### 4.2 编译、替换设备树 + +##### 1. STM32MP157 + + * 修改`arch/arm/boot/dts/stm32mp157c-100ask-512d-lcd-v1.dts`,添加如下代码: + + ```shell + / { + pinctrl_virt: virtual_pincontroller { + compatible = "100ask,virtual_pinctrl"; + myled_pin: myled_pin { + functions = "gpio"; + groups = "pin0"; + configs = <0x11223344>; + }; + i2cgrp: i2cgrp { + functions = "i2c", "i2c"; + groups = "pin0", "pin1"; + configs = <0x11223344 0x55667788>; + }; + }; + + gpio_virt: virtual_gpiocontroller { + compatible = "100ask,virtual_gpio"; + gpio-controller; + #gpio-cells = <2>; + ngpios = <4>; + gpio-ranges = <&pinctrl_virt 0 0 4>; + }; + + myled { + compatible = "100ask,leddrv"; + led-gpios = <&gpio_virt 2 GPIO_ACTIVE_LOW>; + }; + }; + ``` + + + + * 编译设备树: + 在Ubuntu的STM32MP157内核目录下执行如下命令, + 得到设备树文件:`arch/arm/boot/dts/stm32mp157c-100ask-512d-lcd-v1.dtb` + + ```shell + make dtbs + ``` + + * 复制到NFS目录: + + ```shell + $ cp arch/arm/boot/dts/stm32mp157c-100ask-512d-lcd-v1.dtb ~/nfs_rootfs/ + ``` + + * 开发板上挂载NFS文件系统 + + * vmware使用NAT(假设windowsIP为192.168.1.100) + + ```shell + [root@100ask:~]# mount -t nfs -o nolock,vers=3,port=2049,mountport=9999 + 192.168.1.100:/home/book/nfs_rootfs /mnt + ``` + + * vmware使用桥接,或者不使用vmware而是直接使用服务器:假设Ubuntu IP为192.168.1.137 + + ```shell + [root@100ask:~]# mount -t nfs -o nolock,vers=3 192.168.1.137:/home/book/nfs_rootfs /mnt + ``` + +* 确定设备树分区挂载在哪里 + + 由于版本变化,STM32MP157单板上烧录的系统可能有细微差别。 + 在开发板上执行`cat /proc/mounts`后,可以得到两种结果(见下图): + + * mmcblk2p2分区挂载在/boot目录下(下图左边):无需特殊操作,下面把文件复制到/boot目录即可 + + * mmcblk2p2挂载在/mnt目录下(下图右边) + + * 在视频里、后面文档里,都是更新/boot目录下的文件,所以要先执行以下命令重新挂载: + * `mount /dev/mmcblk2p2 /boot` + + ![](../04_I2C/pic/04_I2C/057_boot_mount.png) + + * 更新设备树 + + ```shell + [root@100ask:~]# cp /mnt/stm32mp157c-100ask-512d-lcd-v1.dtb /boot + [root@100ask:~]# sync + ``` + +* 重启开发板 + + + + + +##### 2. IMX6ULL + + * 修改`arch/arm/boot/dts/100ask_imx6ull-14x14.dts`,添加如下代码: + + ```shell + / { + pinctrl_virt: virtual_pincontroller { + compatible = "100ask,virtual_pinctrl"; + myled_pin: myled_pin { + functions = "gpio"; + groups = "pin0"; + configs = <0x11223344>; + }; + i2cgrp: i2cgrp { + functions = "i2c", "i2c"; + groups = "pin0", "pin1"; + configs = <0x11223344 0x55667788>; + }; + }; + + gpio_virt: virtual_gpiocontroller { + compatible = "100ask,virtual_gpio"; + gpio-controller; + #gpio-cells = <2>; + ngpios = <4>; + gpio-ranges = <&pinctrl_virt 0 0 4>; + }; + + myled { + compatible = "100ask,leddrv"; + led-gpios = <&gpio_virt 2 GPIO_ACTIVE_LOW>; + }; + }; + ``` + + + + * 编译设备树: + 在Ubuntu的IMX6ULL内核目录下执行如下命令, + 得到设备树文件:`arch/arm/boot/dts/100ask_imx6ull-14x14.dtb` + + ```shell + make dtbs + ``` + + * 复制到NFS目录: + + ```shell + $ cp arch/arm/boot/dts/100ask_imx6ull-14x14.dtb ~/nfs_rootfs/ + ``` + +* 开发板上挂载NFS文件系统 + + * vmware使用NAT(假设windowsIP为192.168.1.100) + + ```shell + [root@100ask:~]# mount -t nfs -o nolock,vers=3,port=2049,mountport=9999 + 192.168.1.100:/home/book/nfs_rootfs /mnt + ``` + + * vmware使用桥接,或者不使用vmware而是直接使用服务器:假设Ubuntu IP为192.168.1.137 + + ```shell + [root@100ask:~]# mount -t nfs -o nolock,vers=3 192.168.1.137:/home/book/nfs_rootfs /mnt + ``` + + * 更新设备树 + + ```shell + [root@100ask:~]# cp /mnt/100ask_imx6ull-14x14.dtb /boot + [root@100ask:~]# sync + ``` + +* 重启开发板 + + + +#### 4.3 编译、安装驱动程序 + +* 编译: + + * 在Ubuntu上 + * 进入`04_gpio_use_pinctrl_ok`下的3个驱动目录,都执行make命令 + +* 安装: + + * 在开发板上 + + * 挂载NFS,复制文件,insmod,类似如下命令: + + ```shell + mount -t nfs -o nolock,vers=3 192.168.1.137:/home/book/nfs_rootfs /mnt + // 对于IMX6ULL,想看到驱动打印信息,需要先执行 + echo "7 4 1 7" > /proc/sys/kernel/printk + + insmod -f /mnt/virtual_pinctrl_driver.ko + insmod -f /mnt/virtual_gpio_driver.ko + insmod -f /mnt/leddrv.ko + + ls /dev/100ask_led0 + /mnt/ledtest /dev/100ask_led0 on + /mnt/ledtest /dev/100ask_led0 off + ``` + +* 观察内核打印的信息 + + + +### 5. 再次开后门 + +在STM32MP157的内核中, + +Pinctrl驱动中并没有提供`pmxops->gpio_request_enable`或`pmxops->request`, + +为什么也可一直接使用GPIO功能? + +它的gpio_chip结构体中,有`direction_input`、`direction_output`,这2个函数的调用关系如下: + +```c +direction_output/direction_input + pinctrl_gpio_direction + ret = pinmux_gpio_direction(pctldev, range, pin, input); + ret = ops->gpio_set_direction(pctldev, range, pin, input); + stm32_pmx_gpio_set_direction + stm32_pmx_set_mode // 它会设置引脚为GPIO功能 +``` + + + +### 6. IMX6ULL的特殊情况 + +IMX6ULL使用GPIO时必须设置Pinctrl,如果不设置,只有那些默认就是GPIO功能的引脚可以正常使用。 + +原因: + +* GPIO控制器的设备树中,没有`gpio-ranges` +* Pinctrl驱动中并没有提供`pmxops->gpio_request_enable`或`pmxops->request` +* gpio_chip结构体中`direction_input`、`direction_output`,并没有配置引脚为GPIO功能 \ No newline at end of file diff --git a/STM32MP157/doc_pic/07_GPIO/11_GPIO子系统的sysfs接口.md b/STM32MP157/doc_pic/07_GPIO/11_GPIO子系统的sysfs接口.md new file mode 100644 index 0000000..4438cb0 --- /dev/null +++ b/STM32MP157/doc_pic/07_GPIO/11_GPIO子系统的sysfs接口.md @@ -0,0 +1,110 @@ +## GPIO子系统的sysfs接口 + +参考资料: + +* Linux 5.x内核文档 + * Linux-5.4\Documentation\driver-api + * Linux-5.4\Documentation\devicetree\bindings\gpio\gpio.txt + * Linux-5.4\drivers\gpio\gpiolib-sysfs.c + +* Linux 4.x内核文档 + * Linux-4.9.88\Documentation\gpio + * Linux-4.9.88\Documentation\devicetree\bindings\gpio\gpio.txt + * Linux-4.9.88\drivers\gpio\gpiolib-sysfs.c + +* 本节视频代码在GIT仓库中 + + ```shell + doc_and_source_for_drivers\IMX6ULL\source\07_GPIO\04_gpio_use_pinctrl_ok + doc_and_source_for_drivers\STM32MP157\source\A7\07_GPIO\04_gpio_use_pinctrl_ok + ``` + + + +### 1. 驱动程序 + +驱动程序为`drivers\gpio\gpiolib-sysfs.c`,这里不打算分析它。 + + + +### 2. 常用的SYSFS文件 + +#### 2.1 有哪些GPIO控制器 + +`/sys/bus/gpio/devices`目录下,列出了所有的GPIO控制器,如下表示有11个GPIO控制器: + +```c +/sys/bus/gpio/devices/gpiochip0 +/sys/bus/gpio/devices/gpiochip1 +/sys/bus/gpio/devices/gpiochip2 +/sys/bus/gpio/devices/gpiochip3 +/sys/bus/gpio/devices/gpiochip4 +/sys/bus/gpio/devices/gpiochip5 +/sys/bus/gpio/devices/gpiochip6 +/sys/bus/gpio/devices/gpiochip7 +/sys/bus/gpio/devices/gpiochip8 +/sys/bus/gpio/devices/gpiochip9 +/sys/bus/gpio/devices/gpiochip10 +``` + + + +#### 2.2 每个GPIO控制器的详细信息 + +`/sys/class/gpio/gpiochipXXX`下,有这些信息: + +```shell +/sys/class/gpio/gpiochip508]# ls -1 +base // 这个GPIO控制器的GPIO编号 +device +label // 名字 +ngpio // 引脚个数 +power +subsystem +uevent +``` + + + +#### 2.3 查看GPIO使用情况 + +```shell +cat /sys/kernel/debug/gpio +``` + + + +#### 2.4 通过SYSFS使用GPIO + +如果只是简单的引脚控制(比如输出、查询输入值),可以不编写驱动程序。 + +但是涉及中断的话,就需要编写驱动程序了。 + +##### 1. 确定GPIO编号 + +查看每个`/sys/bus/gpio/devices/gpiochipXXX`目录下的label,确定是你要用的GPIO控制器,也称为GPIO Bank。 + +根据它名字gpiochipXXX,就可以知道基值是XXX。 + +基值加上引脚offset,就是这个引脚的编号。 + + + +##### 2. 导出/设置方向/读写值 + +举例: + +```shell +echo 509 > /sys/class/gpio/export +echo out > /sys/class/gpio/gpio509/direction +echo 1 > /sys/class/gpio/gpio509/value +echo 509 > /sys/class/gpio/unexport + +echo 509 > /sys/class/gpio/export +echo in > /sys/class/gpio/gpio509/direction +cat /sys/class/gpio/gpio509/value +echo 509 > /sys/class/gpio/unexport +``` + + + diff --git a/STM32MP157/doc_pic/07_GPIO/pic/07_GPIO/09_imx6ull_gpio_chip_set.png b/STM32MP157/doc_pic/07_GPIO/pic/07_GPIO/09_imx6ull_gpio_chip_set.png new file mode 100644 index 0000000..13f431e Binary files /dev/null and b/STM32MP157/doc_pic/07_GPIO/pic/07_GPIO/09_imx6ull_gpio_chip_set.png differ diff --git a/STM32MP157/doc_pic/07_GPIO/pic/07_GPIO/12_gpio_and_pinctrl_range.png b/STM32MP157/doc_pic/07_GPIO/pic/07_GPIO/12_gpio_and_pinctrl_range.png new file mode 100644 index 0000000..9bb71ec Binary files /dev/null and b/STM32MP157/doc_pic/07_GPIO/pic/07_GPIO/12_gpio_and_pinctrl_range.png differ diff --git a/STM32MP157/doc_pic/07_GPIO/pic/07_GPIO/13_gpio_pin_range.png b/STM32MP157/doc_pic/07_GPIO/pic/07_GPIO/13_gpio_pin_range.png new file mode 100644 index 0000000..3c18b21 Binary files /dev/null and b/STM32MP157/doc_pic/07_GPIO/pic/07_GPIO/13_gpio_pin_range.png differ diff --git a/STM32MP157/doc_pic/07_GPIO/pic/07_GPIO/14_gpio_request.png b/STM32MP157/doc_pic/07_GPIO/pic/07_GPIO/14_gpio_request.png new file mode 100644 index 0000000..efed222 Binary files /dev/null and b/STM32MP157/doc_pic/07_GPIO/pic/07_GPIO/14_gpio_request.png differ diff --git a/STM32MP157/source/A7/07_GPIO/04_gpio_use_pinctrl_ok/01_led/Makefile b/STM32MP157/source/A7/07_GPIO/04_gpio_use_pinctrl_ok/01_led/Makefile new file mode 100644 index 0000000..b519dab --- /dev/null +++ b/STM32MP157/source/A7/07_GPIO/04_gpio_use_pinctrl_ok/01_led/Makefile @@ -0,0 +1,29 @@ + +# 1. 使用不同的开发板内核时, 一定要修改KERN_DIR +# 2. KERN_DIR中的内核要事先配置、编译, 为了能编译内核, 要先设置下列环境变量: +# 2.1 ARCH, 比如: export ARCH=arm64 +# 2.2 CROSS_COMPILE, 比如: export CROSS_COMPILE=aarch64-linux-gnu- +# 2.3 PATH, 比如: export PATH=$PATH:/home/book/100ask_roc-rk3399-pc/ToolChain-6.3.1/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin +# 注意: 不同的开发板不同的编译器上述3个环境变量不一定相同, +# 请参考各开发板的高级用户使用手册 + +KERN_DIR = /home/book/100ask_stm32mp157_pro-sdk/Linux-5.4 # 板子所用内核源码的目录 + +all: + make -C $(KERN_DIR) M=`pwd` modules + $(CROSS_COMPILE)gcc -o ledtest ledtest.c + +clean: + make -C $(KERN_DIR) M=`pwd` modules clean + rm -rf modules.order + rm -f ledtest + +# 参考内核源码drivers/char/ipmi/Makefile +# 要想把a.c, b.c编译成ab.ko, 可以这样指定: +# ab-y := a.o b.o +# obj-m += ab.o + + + +obj-m += leddrv.o + diff --git a/STM32MP157/source/A7/07_GPIO/04_gpio_use_pinctrl_ok/01_led/leddrv.c b/STM32MP157/source/A7/07_GPIO/04_gpio_use_pinctrl_ok/01_led/leddrv.c new file mode 100644 index 0000000..521c65d --- /dev/null +++ b/STM32MP157/source/A7/07_GPIO/04_gpio_use_pinctrl_ok/01_led/leddrv.c @@ -0,0 +1,161 @@ +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* 1. 确定主设备号 */ +static int major = 0; +static struct class *led_class; +static struct gpio_desc *led_gpio; + + +/* 3. 实现对应的open/read/write等函数,填入file_operations结构体 */ +static ssize_t led_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset) +{ + printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); + return 0; +} + +/* write(fd, &val, 1); */ +static ssize_t led_drv_write (struct file *file, const char __user *buf, size_t size, loff_t *offset) +{ + int err; + char status; + + printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); + err = copy_from_user(&status, buf, 1); + + /* 根据次设备号和status控制LED */ + gpiod_set_value(led_gpio, status); + + return 1; +} + +static int led_drv_open (struct inode *node, struct file *file) +{ + printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); + /* 根据次设备号初始化LED */ + gpiod_direction_output(led_gpio, 0); + + return 0; +} + +static int led_drv_close (struct inode *node, struct file *file) +{ + printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); + return 0; +} + +/* 定义自己的file_operations结构体 */ +static struct file_operations led_drv = { + .owner = THIS_MODULE, + .open = led_drv_open, + .read = led_drv_read, + .write = led_drv_write, + .release = led_drv_close, +}; + +/* 4. 从platform_device获得GPIO + * 把file_operations结构体告诉内核:注册驱动程序 + */ +static int chip_demo_gpio_probe(struct platform_device *pdev) +{ + printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); + + /* 4.1 设备树中定义有: led-gpios=<...>; */ + led_gpio = gpiod_get(&pdev->dev, "led", 0); + if (IS_ERR(led_gpio)) { + dev_err(&pdev->dev, "Failed to get GPIO for led\n"); + return PTR_ERR(led_gpio); + } + + /* 4.2 注册file_operations */ + major = register_chrdev(0, "100ask_led", &led_drv); /* /dev/led */ + + led_class = class_create(THIS_MODULE, "100ask_led_class"); + if (IS_ERR(led_class)) { + printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); + unregister_chrdev(major, "led"); + gpiod_put(led_gpio); + return PTR_ERR(led_class); + } + + device_create(led_class, NULL, MKDEV(major, 0), NULL, "100ask_led%d", 0); /* /dev/100ask_led0 */ + + return 0; + +} + +static int chip_demo_gpio_remove(struct platform_device *pdev) +{ + device_destroy(led_class, MKDEV(major, 0)); + class_destroy(led_class); + unregister_chrdev(major, "100ask_led"); + gpiod_put(led_gpio); + + return 0; +} + + +static const struct of_device_id ask100_leds[] = { + { .compatible = "100ask,leddrv" }, + { }, +}; + +/* 1. 定义platform_driver */ +static struct platform_driver chip_demo_gpio_driver = { + .probe = chip_demo_gpio_probe, + .remove = chip_demo_gpio_remove, + .driver = { + .name = "100ask_led", + .of_match_table = ask100_leds, + }, +}; + +/* 2. 在入口函数注册platform_driver */ +static int __init led_init(void) +{ + int err; + + printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); + + err = platform_driver_register(&chip_demo_gpio_driver); + + return err; +} + +/* 3. 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数 + * 卸载platform_driver + */ +static void __exit led_exit(void) +{ + printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); + + platform_driver_unregister(&chip_demo_gpio_driver); +} + + +/* 7. 其他完善:提供设备信息,自动创建设备节点 */ + +module_init(led_init); +module_exit(led_exit); + +MODULE_LICENSE("GPL"); + + diff --git a/STM32MP157/source/A7/07_GPIO/04_gpio_use_pinctrl_ok/01_led/leddrv_未测试的原始版本.c b/STM32MP157/source/A7/07_GPIO/04_gpio_use_pinctrl_ok/01_led/leddrv_未测试的原始版本.c new file mode 100644 index 0000000..6ef1a7c --- /dev/null +++ b/STM32MP157/source/A7/07_GPIO/04_gpio_use_pinctrl_ok/01_led/leddrv_未测试的原始版本.c @@ -0,0 +1,165 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* 1. 确定主设备号 */ +static int major = 0; +static struct class *led_class; +static struct gpio_desc *led_gpio; + + +/* 3. 实现对应的open/read/write等函数,填入file_operations结构体 */ +static ssize_t led_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset) +{ + printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); + return 0; +} + +/* write(fd, &val, 1); */ +static ssize_t led_drv_write (struct file *file, const char __user *buf, size_t size, loff_t *offset) +{ + int err; + char status; + struct inode *inode = file_inode(file); + int minor = iminor(inode); + + printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); + err = copy_from_user(&status, buf, 1); + + /* 根据次设备号和status控制LED */ + gpiod_set_value(led_gpio, status); + + return 1; +} + +static int led_drv_open (struct inode *node, struct file *file) +{ + int minor = iminor(node); + + printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); + /* 根据次设备号初始化LED */ + gpiod_direction_output(led_gpio, 0); + + return 0; +} + +static int led_drv_close (struct inode *node, struct file *file) +{ + printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); + return 0; +} + +/* 定义自己的file_operations结构体 */ +static struct file_operations led_drv = { + .owner = THIS_MODULE, + .open = led_drv_open, + .read = led_drv_read, + .write = led_drv_write, + .release = led_drv_close, +}; + +/* 4. 从platform_device获得GPIO + * 把file_operations结构体告诉内核:注册驱动程序 + */ +static int chip_demo_gpio_probe(struct platform_device *pdev) +{ + int err; + + printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); + + /* 4.1 设备树中定义有: led-gpios=<...>; */ + led_gpio = gpiod_get(&pdev->dev, "led", 0); + if (IS_ERR(led_gpio)) { + dev_err(&pdev->dev, "Failed to get GPIO for led\n"); + return PTR_ERR(led_gpio); + } + + /* 4.2 注册file_operations */ + major = register_chrdev(0, "100ask_led", &led_drv); /* /dev/led */ + + led_class = class_create(THIS_MODULE, "100ask_led_class"); + if (IS_ERR(led_class)) { + printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); + unregister_chrdev(major, "led"); + gpiod_free(led_gpio); + return PTR_ERR(led_class); + } + + device_create(led_class, NULL, MKDEV(major, 0), NULL, "100ask_led%d", 0); /* /dev/100ask_led0 */ + + return 0; + +} + +static int chip_demo_gpio_remove(struct platform_device *pdev) +{ + device_destroy(led_class, MKDEV(major, 0)); + class_destroy(led_class); + unregister_chrdev(major, "100ask_led"); + gpiod_free(led_gpio); + + return 0; +} + + +static const struct of_device_id ask100_leds[] = { + { .compatible = "100ask,leddrv" }, + { }, +}; + +/* 1. 定义platform_driver */ +static struct platform_driver chip_demo_gpio_driver = { + .probe = chip_demo_gpio_probe, + .remove = chip_demo_gpio_remove, + .driver = { + .name = "100ask_led", + .of_match_table = ask100_leds, + }, +}; + +/* 2. 在入口函数注册platform_driver */ +static int __init led_init(void) +{ + int err; + + printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); + + err = platform_driver_register(&chip_demo_gpio_driver); + + return err; +} + +/* 3. 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数 + * 卸载platform_driver + */ +static void __exit led_exit(void) +{ + printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__); + + platform_driver_unregister(&chip_demo_gpio_driver); +} + + +/* 7. 其他完善:提供设备信息,自动创建设备节点 */ + +module_init(led_init); +module_exit(led_exit); + +MODULE_LICENSE("GPL"); + + diff --git a/STM32MP157/source/A7/07_GPIO/04_gpio_use_pinctrl_ok/01_led/ledtest.c b/STM32MP157/source/A7/07_GPIO/04_gpio_use_pinctrl_ok/01_led/ledtest.c new file mode 100644 index 0000000..099eb11 --- /dev/null +++ b/STM32MP157/source/A7/07_GPIO/04_gpio_use_pinctrl_ok/01_led/ledtest.c @@ -0,0 +1,50 @@ + +#include +#include +#include +#include +#include +#include + +/* + * ./ledtest /dev/100ask_led0 on + * ./ledtest /dev/100ask_led0 off + */ +int main(int argc, char **argv) +{ + int fd; + char status; + + /* 1. 判断参数 */ + if (argc != 3) + { + printf("Usage: %s \n", argv[0]); + return -1; + } + + /* 2. 打开文件 */ + fd = open(argv[1], O_RDWR); + if (fd == -1) + { + printf("can not open file %s\n", argv[1]); + return -1; + } + + /* 3. 写文件 */ + if (0 == strcmp(argv[2], "on")) + { + status = 1; + write(fd, &status, 1); + } + else + { + status = 0; + write(fd, &status, 1); + } + + close(fd); + + return 0; +} + + diff --git a/STM32MP157/source/A7/07_GPIO/04_gpio_use_pinctrl_ok/02_virtual_pinctrl_ok/Makefile b/STM32MP157/source/A7/07_GPIO/04_gpio_use_pinctrl_ok/02_virtual_pinctrl_ok/Makefile new file mode 100644 index 0000000..bbbccf0 --- /dev/null +++ b/STM32MP157/source/A7/07_GPIO/04_gpio_use_pinctrl_ok/02_virtual_pinctrl_ok/Makefile @@ -0,0 +1,22 @@ + +# 1. 使用不同的开发板内核时, 一定要修改KERN_DIR +# 2. KERN_DIR中的内核要事先配置、编译, 为了能编译内核, 要先设置下列环境变量: +# 2.1 ARCH, 比如: export ARCH=arm64 +# 2.2 CROSS_COMPILE, 比如: export CROSS_COMPILE=aarch64-linux-gnu- +# 2.3 PATH, 比如: export PATH=$PATH:/home/book/100ask_roc-rk3399-pc/ToolChain-6.3.1/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin +# 注意: 不同的开发板不同的编译器上述3个环境变量不一定相同, +# 请参考各开发板的高级用户使用手册 + +KERN_DIR = /home/book/100ask_stm32mp157_pro-sdk/Linux-5.4 + +all: + make -C $(KERN_DIR) M=`pwd` modules + +clean: + make -C $(KERN_DIR) M=`pwd` modules clean + rm -rf modules.order + +obj-m += virtual_pinctrl_driver.o +obj-m += virtual_pinctrl_client.o + + diff --git a/STM32MP157/source/A7/07_GPIO/04_gpio_use_pinctrl_ok/02_virtual_pinctrl_ok/core.h b/STM32MP157/source/A7/07_GPIO/04_gpio_use_pinctrl_ok/02_virtual_pinctrl_ok/core.h new file mode 100644 index 0000000..7f34167 --- /dev/null +++ b/STM32MP157/source/A7/07_GPIO/04_gpio_use_pinctrl_ok/02_virtual_pinctrl_ok/core.h @@ -0,0 +1,253 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Core private header for the pin control subsystem + * + * Copyright (C) 2011 ST-Ericsson SA + * Written on behalf of Linaro for ST-Ericsson + * + * Author: Linus Walleij + */ + +#include +#include +#include +#include +#include + +struct pinctrl_gpio_range; + +/** + * struct pinctrl_dev - pin control class device + * @node: node to include this pin controller in the global pin controller list + * @desc: the pin controller descriptor supplied when initializing this pin + * controller + * @pin_desc_tree: each pin descriptor for this pin controller is stored in + * this radix tree + * @pin_group_tree: optionally each pin group can be stored in this radix tree + * @num_groups: optionally number of groups can be kept here + * @pin_function_tree: optionally each function can be stored in this radix tree + * @num_functions: optionally number of functions can be kept here + * @gpio_ranges: a list of GPIO ranges that is handled by this pin controller, + * ranges are added to this list at runtime + * @dev: the device entry for this pin controller + * @owner: module providing the pin controller, used for refcounting + * @driver_data: driver data for drivers registering to the pin controller + * subsystem + * @p: result of pinctrl_get() for this device + * @hog_default: default state for pins hogged by this device + * @hog_sleep: sleep state for pins hogged by this device + * @mutex: mutex taken on each pin controller specific action + * @device_root: debugfs root for this device + */ +struct pinctrl_dev { + struct list_head node; + struct pinctrl_desc *desc; + struct radix_tree_root pin_desc_tree; +#ifdef CONFIG_GENERIC_PINCTRL_GROUPS + struct radix_tree_root pin_group_tree; + unsigned int num_groups; +#endif +#ifdef CONFIG_GENERIC_PINMUX_FUNCTIONS + struct radix_tree_root pin_function_tree; + unsigned int num_functions; +#endif + struct list_head gpio_ranges; + struct device *dev; + struct module *owner; + void *driver_data; + struct pinctrl *p; + struct pinctrl_state *hog_default; + struct pinctrl_state *hog_sleep; + struct mutex mutex; +#ifdef CONFIG_DEBUG_FS + struct dentry *device_root; +#endif +}; + +/** + * struct pinctrl - per-device pin control state holder + * @node: global list node + * @dev: the device using this pin control handle + * @states: a list of states for this device + * @state: the current state + * @dt_maps: the mapping table chunks dynamically parsed from device tree for + * this device, if any + * @users: reference count + */ +struct pinctrl { + struct list_head node; + struct device *dev; + struct list_head states; + struct pinctrl_state *state; + struct list_head dt_maps; + struct kref users; +}; + +/** + * struct pinctrl_state - a pinctrl state for a device + * @node: list node for struct pinctrl's @states field + * @name: the name of this state + * @settings: a list of settings for this state + */ +struct pinctrl_state { + struct list_head node; + const char *name; + struct list_head settings; +}; + +/** + * struct pinctrl_setting_mux - setting data for MAP_TYPE_MUX_GROUP + * @group: the group selector to program + * @func: the function selector to program + */ +struct pinctrl_setting_mux { + unsigned group; + unsigned func; +}; + +/** + * struct pinctrl_setting_configs - setting data for MAP_TYPE_CONFIGS_* + * @group_or_pin: the group selector or pin ID to program + * @configs: a pointer to an array of config parameters/values to program into + * hardware. Each individual pin controller defines the format and meaning + * of config parameters. + * @num_configs: the number of entries in array @configs + */ +struct pinctrl_setting_configs { + unsigned group_or_pin; + unsigned long *configs; + unsigned num_configs; +}; + +/** + * struct pinctrl_setting - an individual mux or config setting + * @node: list node for struct pinctrl_settings's @settings field + * @type: the type of setting + * @pctldev: pin control device handling to be programmed. Not used for + * PIN_MAP_TYPE_DUMMY_STATE. + * @dev_name: the name of the device using this state + * @data: Data specific to the setting type + */ +struct pinctrl_setting { + struct list_head node; + enum pinctrl_map_type type; + struct pinctrl_dev *pctldev; + const char *dev_name; + union { + struct pinctrl_setting_mux mux; + struct pinctrl_setting_configs configs; + } data; +}; + +/** + * struct pin_desc - pin descriptor for each physical pin in the arch + * @pctldev: corresponding pin control device + * @name: a name for the pin, e.g. the name of the pin/pad/finger on a + * datasheet or such + * @dynamic_name: if the name of this pin was dynamically allocated + * @drv_data: driver-defined per-pin data. pinctrl core does not touch this + * @mux_usecount: If zero, the pin is not claimed, and @owner should be NULL. + * If non-zero, this pin is claimed by @owner. This field is an integer + * rather than a boolean, since pinctrl_get() might process multiple + * mapping table entries that refer to, and hence claim, the same group + * or pin, and each of these will increment the @usecount. + * @mux_owner: The name of device that called pinctrl_get(). + * @mux_setting: The most recent selected mux setting for this pin, if any. + * @gpio_owner: If pinctrl_gpio_request() was called for this pin, this is + * the name of the GPIO that "owns" this pin. + */ +struct pin_desc { + struct pinctrl_dev *pctldev; + const char *name; + bool dynamic_name; + void *drv_data; + /* These fields only added when supporting pinmux drivers */ +#ifdef CONFIG_PINMUX + unsigned mux_usecount; + const char *mux_owner; + const struct pinctrl_setting_mux *mux_setting; + const char *gpio_owner; +#endif +}; + +/** + * struct pinctrl_maps - a list item containing part of the mapping table + * @node: mapping table list node + * @maps: array of mapping table entries + * @num_maps: the number of entries in @maps + */ +struct pinctrl_maps { + struct list_head node; + const struct pinctrl_map *maps; + unsigned num_maps; +}; + +#ifdef CONFIG_GENERIC_PINCTRL_GROUPS + +/** + * struct group_desc - generic pin group descriptor + * @name: name of the pin group + * @pins: array of pins that belong to the group + * @num_pins: number of pins in the group + * @data: pin controller driver specific data + */ +struct group_desc { + const char *name; + int *pins; + int num_pins; + void *data; +}; + +int pinctrl_generic_get_group_count(struct pinctrl_dev *pctldev); + +const char *pinctrl_generic_get_group_name(struct pinctrl_dev *pctldev, + unsigned int group_selector); + +int pinctrl_generic_get_group_pins(struct pinctrl_dev *pctldev, + unsigned int group_selector, + const unsigned int **pins, + unsigned int *npins); + +struct group_desc *pinctrl_generic_get_group(struct pinctrl_dev *pctldev, + unsigned int group_selector); + +int pinctrl_generic_add_group(struct pinctrl_dev *pctldev, const char *name, + int *gpins, int ngpins, void *data); + +int pinctrl_generic_remove_group(struct pinctrl_dev *pctldev, + unsigned int group_selector); + +#endif /* CONFIG_GENERIC_PINCTRL_GROUPS */ + +struct pinctrl_dev *get_pinctrl_dev_from_devname(const char *dev_name); +struct pinctrl_dev *get_pinctrl_dev_from_of_node(struct device_node *np); +int pin_get_from_name(struct pinctrl_dev *pctldev, const char *name); +const char *pin_get_name(struct pinctrl_dev *pctldev, const unsigned pin); +int pinctrl_get_group_selector(struct pinctrl_dev *pctldev, + const char *pin_group); + +static inline struct pin_desc *pin_desc_get(struct pinctrl_dev *pctldev, + unsigned int pin) +{ + return radix_tree_lookup(&pctldev->pin_desc_tree, pin); +} + +extern struct pinctrl_gpio_range * +pinctrl_find_gpio_range_from_pin_nolock(struct pinctrl_dev *pctldev, + unsigned int pin); + +int pinctrl_register_map(const struct pinctrl_map *maps, unsigned num_maps, + bool dup); +void pinctrl_unregister_map(const struct pinctrl_map *map); + +extern int pinctrl_force_sleep(struct pinctrl_dev *pctldev); +extern int pinctrl_force_default(struct pinctrl_dev *pctldev); + +extern struct mutex pinctrl_maps_mutex; +extern struct list_head pinctrl_maps; + +#define for_each_maps(_maps_node_, _i_, _map_) \ + list_for_each_entry(_maps_node_, &pinctrl_maps, node) \ + for (_i_ = 0, _map_ = &_maps_node_->maps[_i_]; \ + _i_ < _maps_node_->num_maps; \ + _i_++, _map_ = &_maps_node_->maps[_i_]) diff --git a/STM32MP157/source/A7/07_GPIO/04_gpio_use_pinctrl_ok/02_virtual_pinctrl_ok/virtual_pinctrl_client.c b/STM32MP157/source/A7/07_GPIO/04_gpio_use_pinctrl_ok/02_virtual_pinctrl_ok/virtual_pinctrl_client.c new file mode 100644 index 0000000..bced33e --- /dev/null +++ b/STM32MP157/source/A7/07_GPIO/04_gpio_use_pinctrl_ok/02_virtual_pinctrl_ok/virtual_pinctrl_client.c @@ -0,0 +1,67 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const struct of_device_id virtual_client_of_match[] = { + { .compatible = "100ask,virtual_i2c", }, + { }, +}; + +static int virtual_client_probe(struct platform_device *pdev) +{ + printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); + return 0; +} +static int virtual_client_remove(struct platform_device *pdev) +{ + + printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); + return 0; +} + + +static struct platform_driver virtual_client_driver = { + .probe = virtual_client_probe, + .remove = virtual_client_remove, + .driver = { + .name = "100ask_virtual_client", + .of_match_table = of_match_ptr(virtual_client_of_match), + } +}; + + +/* 1. 入口函数 */ +static int __init virtual_client_init(void) +{ + printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); + /* 1.1 注册一个platform_driver */ + return platform_driver_register(&virtual_client_driver); +} + + +/* 2. 出口函数 */ +static void __exit virtual_client_exit(void) +{ + printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); + /* 2.1 反注册platform_driver */ + platform_driver_unregister(&virtual_client_driver); +} + +module_init(virtual_client_init); +module_exit(virtual_client_exit); + +MODULE_LICENSE("GPL"); + + + diff --git a/STM32MP157/source/A7/07_GPIO/04_gpio_use_pinctrl_ok/02_virtual_pinctrl_ok/virtual_pinctrl_driver.c b/STM32MP157/source/A7/07_GPIO/04_gpio_use_pinctrl_ok/02_virtual_pinctrl_ok/virtual_pinctrl_driver.c new file mode 100644 index 0000000..80c60e8 --- /dev/null +++ b/STM32MP157/source/A7/07_GPIO/04_gpio_use_pinctrl_ok/02_virtual_pinctrl_ok/virtual_pinctrl_driver.c @@ -0,0 +1,329 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core.h" + + +static struct pinctrl_dev *g_pinctrl_dev; + +static const struct pinctrl_pin_desc pins[] = { + {0, "pin0", NULL}, + {1, "pin1", NULL}, + {2, "pin2", NULL}, + {3, "pin3", NULL}, +}; + +static unsigned long g_configs[4]; + +struct virtual_functions_desc { + const char *func_name; + const char **groups; + int num_groups; +}; + + +static const char *func0_grps[] = {"pin0", "pin1", "pin2", "pin3"}; +static const char *func1_grps[] = {"pin0", "pin1"}; +static const char *func2_grps[] = {"pin2", "pin3"}; + +static struct virtual_functions_desc g_funcs_des[] = { + {"gpio", func0_grps, 4}, + {"i2c", func1_grps, 2}, + {"uart", func2_grps, 2}, +}; + + +static const struct of_device_id virtual_pinctrl_of_match[] = { + { .compatible = "100ask,virtual_pinctrl", }, + { }, +}; + +static int virtual_get_groups_count(struct pinctrl_dev *pctldev) +{ + return pctldev->desc->npins; +} + +static const char *virtual_get_group_name(struct pinctrl_dev *pctldev, + unsigned selector) +{ + return pctldev->desc->pins[selector].name; +} + +static int virtual_get_group_pins(struct pinctrl_dev *pctldev, unsigned selector, + const unsigned **pins, + unsigned *npins) +{ + if (selector >= pctldev->desc->npins) + return -EINVAL; + + *pins = &pctldev->desc->pins[selector].number; + *npins = 1; + + return 0; +} + +static void virtual_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s, + unsigned offset) +{ + seq_printf(s, "%s", dev_name(pctldev->dev)); +} + +/* + i2cgrp { + functions = "i2c", "i2c"; + groups = "pin0", "pin1"; + configs = <0x11223344 0x55667788>; + }; + + one pin ==> two pinctrl_map (one for mux, one for config) + + */ +static int virtual_dt_node_to_map(struct pinctrl_dev *pctldev, + struct device_node *np, + struct pinctrl_map **map, unsigned *num_maps) +{ + int i; + int num_pins = 0; + const char *pin; + const char *function; + unsigned int config; + struct pinctrl_map *new_map; + unsigned long *configs; + + /* 1. 确定pin个数/分配pinctrl_map */ + while (1) + { + if (of_property_read_string_index(np, "groups", num_pins, &pin) == 0) + num_pins++; + else + break; + } + + new_map = kmalloc(sizeof(struct pinctrl_map) * num_pins * 2, GFP_KERNEL); + + + for (i = 0; i < num_pins; i++) + { + /* 2. get pin/function/config */ + of_property_read_string_index(np, "groups", i, &pin); + of_property_read_string_index(np, "functions", i, &function); + of_property_read_u32_index(np, "configs", i, &config); + + + /* 3. 存入pinctrl_map */ + configs = kmalloc(sizeof(*configs), GFP_KERNEL); + + new_map[i*2].type = PIN_MAP_TYPE_MUX_GROUP; + new_map[i*2].data.mux.function = function; + new_map[i*2].data.mux.group = pin; + + new_map[i*2+1].type = PIN_MAP_TYPE_CONFIGS_PIN; + new_map[i*2+1].data.configs.group_or_pin = pin; + new_map[i*2+1].data.configs.configs = configs; + configs[0] = config; + new_map[i*2+1].data.configs.num_configs = 1; + + } + + *map = new_map; + *num_maps = num_pins * 2; + + return 0; +} +static void virtual_dt_free_map(struct pinctrl_dev *pctldev, + struct pinctrl_map *map, unsigned num_maps) +{ + while (num_maps--) + { + if (map->type == PIN_MAP_TYPE_CONFIGS_PIN) + kfree(map->data.configs.configs); + + kfree(map); + map++; + } +} + +static const struct pinctrl_ops virtual_pctrl_ops = { + .get_groups_count = virtual_get_groups_count, + .get_group_name = virtual_get_group_name, + .get_group_pins = virtual_get_group_pins, + .pin_dbg_show = virtual_pin_dbg_show, + .dt_node_to_map = virtual_dt_node_to_map, + .dt_free_map = virtual_dt_free_map, + +}; + +static int virtual_pmx_get_funcs_count(struct pinctrl_dev *pctldev) +{ + return ARRAY_SIZE(g_funcs_des); +} + +static const char *virtual_pmx_get_func_name(struct pinctrl_dev *pctldev, + unsigned selector) +{ + return g_funcs_des[selector].func_name; +} + +static int virtual_pmx_get_groups(struct pinctrl_dev *pctldev, unsigned selector, + const char * const **groups, + unsigned * const num_groups) +{ + *groups = g_funcs_des[selector].groups; + *num_groups = g_funcs_des[selector].num_groups; + + return 0; +} + +static int virtual_pmx_set(struct pinctrl_dev *pctldev, unsigned selector, + unsigned group) +{ + printk("set %s as %s\n", pctldev->desc->pins[group].name, g_funcs_des[selector].func_name); + return 0; +} + +static int virtual_pmx_gpio_request_enable (struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned offset) +{ + printk("set and enable pin %s as GPIO\n", pctldev->desc->pins[offset].name); + return 0; +} + + +static const struct pinmux_ops virtual_pmx_ops = { + .get_functions_count = virtual_pmx_get_funcs_count, + .get_function_name = virtual_pmx_get_func_name, + .get_function_groups = virtual_pmx_get_groups, + .set_mux = virtual_pmx_set, + .gpio_request_enable = virtual_pmx_gpio_request_enable, +}; + + +static int virtual_pinconf_get(struct pinctrl_dev *pctldev, + unsigned pin_id, unsigned long *config) +{ + *config = g_configs[pin_id]; + return 0; +} + + +static int virtual_pinconf_set(struct pinctrl_dev *pctldev, + unsigned pin_id, unsigned long *configs, + unsigned num_configs) +{ + if (num_configs != 1) + return -EINVAL; + + g_configs[pin_id] = *configs; + + printk("config %s as 0x%lx\n", pctldev->desc->pins[pin_id].name, *configs); + + return 0; +} + +static void virtual_pinconf_dbg_show(struct pinctrl_dev *pctldev, + struct seq_file *s, unsigned pin_id) +{ + seq_printf(s, "0x%lx", g_configs[pin_id]); +} + +static void virtual_pinconf_group_dbg_show(struct pinctrl_dev *pctldev, + struct seq_file *s, unsigned pin_id) +{ + seq_printf(s, "0x%lx", g_configs[pin_id]); +} + + +static const struct pinconf_ops virtual_pinconf_ops = { + .pin_config_get = virtual_pinconf_get, + .pin_config_set = virtual_pinconf_set, + .pin_config_dbg_show = virtual_pinconf_dbg_show, + .pin_config_group_dbg_show = virtual_pinconf_group_dbg_show, +}; + + + +static int virtual_pinctrl_probe(struct platform_device *pdev) +{ + struct pinctrl_desc *pictrl; + + printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); + + /* a. 分配pinctrl_desc */ + pictrl = devm_kzalloc(&pdev->dev, sizeof(*pictrl), GFP_KERNEL); + + /* b. 设置pinctrl_desc */ + pictrl->name = dev_name(&pdev->dev); + pictrl->owner = THIS_MODULE; + + /* b.1 pins and group */ + pictrl->pins = pins; + pictrl->npins = ARRAY_SIZE(pins); + + pictrl->pctlops = &virtual_pctrl_ops; + + /* b.2 pin mux */ + pictrl->pmxops = &virtual_pmx_ops; + + /* b.3 pin config */ + pictrl->confops = &virtual_pinconf_ops; + + /* c. 注册pinctrl_desc */ + g_pinctrl_dev = devm_pinctrl_register(&pdev->dev, pictrl, NULL); + + return 0; +} +static int virtual_pinctrl_remove(struct platform_device *pdev) +{ + + printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); + return 0; +} + + +static struct platform_driver virtual_pinctrl_driver = { + .probe = virtual_pinctrl_probe, + .remove = virtual_pinctrl_remove, + .driver = { + .name = "100ask_virtual_pinctrl", + .of_match_table = of_match_ptr(virtual_pinctrl_of_match), + } +}; + + +/* 1. 入口函数 */ +static int __init virtual_pinctrl_init(void) +{ + printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); + /* 1.1 注册一个platform_driver */ + return platform_driver_register(&virtual_pinctrl_driver); +} + + +/* 2. 出口函数 */ +static void __exit virtual_pinctrl_exit(void) +{ + printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); + /* 2.1 反注册platform_driver */ + platform_driver_unregister(&virtual_pinctrl_driver); +} + +module_init(virtual_pinctrl_init); +module_exit(virtual_pinctrl_exit); + +MODULE_LICENSE("GPL"); + + diff --git a/STM32MP157/source/A7/07_GPIO/04_gpio_use_pinctrl_ok/03_virtual_gpio_ok/Makefile b/STM32MP157/source/A7/07_GPIO/04_gpio_use_pinctrl_ok/03_virtual_gpio_ok/Makefile new file mode 100644 index 0000000..7581388 --- /dev/null +++ b/STM32MP157/source/A7/07_GPIO/04_gpio_use_pinctrl_ok/03_virtual_gpio_ok/Makefile @@ -0,0 +1,21 @@ + +# 1. 使用不同的开发板内核时, 一定要修改KERN_DIR +# 2. KERN_DIR中的内核要事先配置、编译, 为了能编译内核, 要先设置下列环境变量: +# 2.1 ARCH, 比如: export ARCH=arm64 +# 2.2 CROSS_COMPILE, 比如: export CROSS_COMPILE=aarch64-linux-gnu- +# 2.3 PATH, 比如: export PATH=$PATH:/home/book/100ask_roc-rk3399-pc/ToolChain-6.3.1/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin +# 注意: 不同的开发板不同的编译器上述3个环境变量不一定相同, +# 请参考各开发板的高级用户使用手册 + +KERN_DIR = /home/book/100ask_stm32mp157_pro-sdk/Linux-5.4 + +all: + make -C $(KERN_DIR) M=`pwd` modules + +clean: + make -C $(KERN_DIR) M=`pwd` modules clean + rm -rf modules.order + +obj-m += virtual_gpio_driver.o + + diff --git a/STM32MP157/source/A7/07_GPIO/04_gpio_use_pinctrl_ok/03_virtual_gpio_ok/virtual_gpio_driver.c b/STM32MP157/source/A7/07_GPIO/04_gpio_use_pinctrl_ok/03_virtual_gpio_ok/virtual_gpio_driver.c new file mode 100644 index 0000000..4585219 --- /dev/null +++ b/STM32MP157/source/A7/07_GPIO/04_gpio_use_pinctrl_ok/03_virtual_gpio_ok/virtual_gpio_driver.c @@ -0,0 +1,128 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct gpio_chip * g_virt_gpio; +static int g_gpio_val = 0; + +static const struct of_device_id virtual_gpio_of_match[] = { + { .compatible = "100ask,virtual_gpio", }, + { }, +}; + +static int virt_gpio_direction_output(struct gpio_chip *gc, + unsigned offset, int val) +{ + printk("set pin %d as output %s\n", offset, val ? "high" : "low"); + return 0; +} + +static int virt_gpio_direction_input(struct gpio_chip *chip, + unsigned offset) +{ + printk("set pin %d as input\n", offset); + return 0; +} + + +static int virt_gpio_get_value(struct gpio_chip *gc, unsigned offset) +{ + int val; + val = (g_gpio_val & (1<dev, sizeof(*g_virt_gpio), GFP_KERNEL); + + /* 2. 设置gpio_chip */ + + /* 2.1 设置函数 */ + g_virt_gpio->label = pdev->name; + g_virt_gpio->direction_output = virt_gpio_direction_output; + g_virt_gpio->direction_input = virt_gpio_direction_input; + g_virt_gpio->get = virt_gpio_get_value; + g_virt_gpio->set = virt_gpio_set_value; + g_virt_gpio->request = gpiochip_generic_request; + + g_virt_gpio->parent = &pdev->dev; + g_virt_gpio->owner = THIS_MODULE; + + /* 2.2 设置base、ngpio值 */ + g_virt_gpio->base = -1; + ret = of_property_read_u32(pdev->dev.of_node, "ngpios", &val); + g_virt_gpio->ngpio = val; + + /* 3. 注册gpio_chip */ + ret = devm_gpiochip_add_data(&pdev->dev, g_virt_gpio, NULL); + + return 0; +} +static int virtual_gpio_remove(struct platform_device *pdev) +{ + printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); + return 0; +} + + +static struct platform_driver virtual_gpio_driver = { + .probe = virtual_gpio_probe, + .remove = virtual_gpio_remove, + .driver = { + .name = "100ask_virtual_gpio", + .of_match_table = of_match_ptr(virtual_gpio_of_match), + } +}; + + +/* 1. 入口函数 */ +static int __init virtual_gpio_init(void) +{ + printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); + /* 1.1 注册一个platform_driver */ + return platform_driver_register(&virtual_gpio_driver); +} + + +/* 2. 出口函数 */ +static void __exit virtual_gpio_exit(void) +{ + printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__); + /* 2.1 反注册platform_driver */ + platform_driver_unregister(&virtual_gpio_driver); +} + +module_init(virtual_gpio_init); +module_exit(virtual_gpio_exit); + +MODULE_LICENSE("GPL"); + +