基於tiny4412的Linux內核移植--- 中斷和GPIO學習(2)
作者
彭東林
pengdonglin137@163.com
平臺
tiny4412 ADK
Linux-4.4.4
u-boot使用的U-Boot 2010.12,是友善自帶的,爲支持設備樹和uImage做了稍許改動
概述
這篇博客以一個簡單的led燈實驗演示一下在含有設備樹、pinctrl時的gpio控制方法。
正文
我們以控制tiny4412上的LED1和LED2爲例,使用的GPIO是GPM4_0和GPM4_1對應的原理圖如下:
datasheet如下:
在samsung的pinctrl驅動中加一些調試用的log:
1 diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c 2 index 4e4c308..53fc268 100644 3 --- a/drivers/gpio/gpiolib.c 4 +++ b/drivers/gpio/gpiolib.c 5 @@ -753,6 +753,7 @@ static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip) {} 6 */ 7 int gpiochip_generic_request(struct gpio_chip *chip, unsigned offset) 8 { 9 + printk("%s enter\n", __func__); 10 return pinctrl_request_gpio(chip->base + offset); 11 } 12 EXPORT_SYMBOL_GPL(gpiochip_generic_request); 13 diff --git a/drivers/pinctrl/samsung/pinctrl-samsung.c b/drivers/pinctrl/samsung/pinctrl-samsung.c 14 index 3f622cc..4f3bbe2 100644 15 --- a/drivers/pinctrl/samsung/pinctrl-samsung.c 16 +++ b/drivers/pinctrl/samsung/pinctrl-samsung.c 17 @@ -391,6 +391,7 @@ static void samsung_pinmux_setup(struct pinctrl_dev *pctldev, unsigned selector, 18 if (enable) 19 data |= func->val << shift; 20 writel(data, reg + type->reg_offset[PINCFG_TYPE_FUNC]); 21 + printk("%s: reg: 0x%x, value: 0x%x\n", __func__, reg + type->reg_offset[PINCFG_TYPE_FUNC], data); 22 23 spin_unlock_irqrestore(&bank->slock, flags); 24 } 25 @@ -447,6 +448,7 @@ static int samsung_pinconf_rw(struct pinctrl_dev *pctldev, unsigned int pin, 26 data &= ~(mask << shift); 27 data |= (cfg_value << shift); 28 writel(data, reg_base + cfg_reg); 29 + printk("%s: reg: 0x%x, value: 0x%x\n", __func__, reg_base + cfg_reg, data); 30 } else { 31 data >>= shift; 32 data &= mask; 33 @@ -537,6 +539,7 @@ static void samsung_gpio_set(struct gpio_chip *gc, unsigned offset, int value) 34 if (value) 35 data |= 1 << offset; 36 writel(data, reg + type->reg_offset[PINCFG_TYPE_DAT]); 37 + printk("%s: reg: 0x%x, value: 0x%x\n", __func__, reg + type->reg_offset[PINCFG_TYPE_DAT], data); 38 39 spin_unlock_irqrestore(&bank->slock, flags); 40 } 41 @@ -593,6 +596,7 @@ static int samsung_gpio_set_direction(struct gpio_chip *gc, 42 if (!input) 43 data |= FUNC_OUTPUT << shift; 44 writel(data, reg); 45 + printk("%s: reg: 0x%x, value: 0x%x\n", __func__, reg, data); 46 47 spin_unlock_irqrestore(&bank->slock, flags); 48 49 @@ -622,6 +626,8 @@ static int samsung_gpio_to_irq(struct gpio_chip *gc, unsigned offset) 50 struct samsung_pin_bank *bank = gc_to_pin_bank(gc); 51 unsigned int virq; 52 53 + printk("%s enter.\n", __func__); 54 + 55 if (!bank->irq_domain) 56 return -ENXIO;
將來在操作gpio的時候會回調到上面的這些函數。
在設備樹中添加相應的節點:
1. 首先把"gpio-leds"節點disable掉,否則會有衝突
2. 添加"tiny4412,gpio_demo"節點
3. 給pinctrl添加相關的pinmux節點
1 diff --git a/arch/arm/boot/dts/exynos4412-tiny4412.dts b/arch/arm/boot/dts/exynos4412-tiny4412.dts 2 index 610202a..0280935 100644 3 --- a/arch/arm/boot/dts/exynos4412-tiny4412.dts 4 +++ b/arch/arm/boot/dts/exynos4412-tiny4412.dts 5 @@ -31,7 +31,7 @@ 6 #ifdef CONFIG_MMA7660_USE_I2C_GPIO 7 i2c9 = &i2c_mma7660; 8 #endif 9 - }; 10 + }; 11 12 memory { 13 reg = <0x40000000 0x40000000>; 14 @@ -39,7 +39,7 @@ 15 16 leds { 17 compatible = "gpio-leds"; 18 - 19 + status = "disabled"; 20 led1 { 21 label = "led1"; 22 gpios = <&gpm4 0 GPIO_ACTIVE_LOW>; 23 @@ -109,7 +109,7 @@ 24 pinctrl-0 = <&pwm0_out>; 25 pinctrl-names = "default"; 26 #endif 27 - status = "disabled"; 28 + status = "disabled"; 29 }; 30 31 #ifdef CONFIG_MMA7660_USE_I2C_GPIO 32 @@ -136,6 +136,48 @@ 33 }; 34 }; 35 #endif 36 + ... ...45 + 46 + gpio_demo: gpio_demo { 47 + compatible = "tiny4412,gpio_demo"; 48 + reg = <0x110002e0 0x10>; 49 + tiny4412,gpio = <&gpm4 0 GPIO_ACTIVE_HIGH &gpm4 1 GPIO_ACTIVE_HIGH>; 50 + pinctrl-names = "default", "gpio_sleep", "gpio_active"; 51 + pinctrl-0 = <&led_gpio_default>; 52 + pinctrl-1 = <&led_gpio_sleep>; 53 + pinctrl-2 = <&led_gpio_active>; 54 + }; 55 +}; 56 + 57 +&pinctrl_1 { 58 + led_gpio_default: led_gpio_default { 59 + samsung,pins = "gpm4-0", "gpm4-1"; 60 + samsung,pin-function = <1>; 61 + samsung,pin-pud = <1>; 62 + samsung,pin-drv = <2>; 63 + }; 64 + 65 + led_gpio_sleep: led_gpio_sleep { 66 + samsung,pins = "gpm4-0", "gpm4-1"; 67 + samsung,pin-function = <1>; 68 + samsung,pin-pud = <0>; 69 + samsung,pin-drv = <0>; 70 + }; 71 + 72 + led_gpio_active: led_gpio_active { 73 + samsung,pins = "gpm4-0", "gpm4-1"; 74 + samsung,pin-function = <1>; 75 + samsung,pin-pud = <3>; 76 + samsung,pin-drv = <3>; 77 + }; 78 }; 79 80 &rtc {
編寫對應的測試驅動程序:
1 #include <linux/init.h> 2 #include <linux/module.h> 3 #include <linux/platform_device.h> 4 #include <linux/gpio.h> 5 #include <linux/of.h> 6 #include <linux/of_gpio.h> 7 8 typedef struct 9 { 10 int gpio; 11 int state; 12 struct pinctrl *pctrl; 13 struct pinctrl_state *sleep_pstate; 14 struct pinctrl_state *active_pstate; 15 char name[20]; 16 } gpio_demo_data_t; 17 18 static u32 __iomem *base; 19 static u32 __iomem *cfg_reg; 20 static u32 __iomem *pud_reg; 21 static u32 __iomem *drv_reg; 22 23 static ssize_t gpio_demo_show(struct device *dev, 24 struct device_attribute *attr, char *buf) 25 { 26 gpio_demo_data_t *data = dev_get_drvdata(dev); 27 u32 config, pud, drv; 28 29 config = readl(cfg_reg); 30 pud = readl(pud_reg); 31 drv = readl(drv_reg); 32 33 return snprintf(buf, PAGE_SIZE, "gpio0: %s, gpio1: %s, cfg: 0x%x, pud: 0x%x, drv: 0x%x\n", data[0].state?"active":"sleep", 34 data[1].state?"active":"sleep", config, pud, drv); 35 } 36 37 static ssize_t gpio_demo_store(struct device *dev, 38 struct device_attribute *attr, const char *buf, size_t count) 39 { 40 gpio_demo_data_t *data = dev_get_drvdata(dev); 41 char state[10]; 42 int value = 0; 43 44 if (sscanf(buf, "%s", state) != 1) 45 return -EINVAL; 46 if (strncmp(state, "active", strlen("active")) == 0) { // 向gpio_demo中寫入active字符串 47 pinctrl_select_state(data[0].pctrl, data[0].active_pstate); 48 pinctrl_select_state(data[1].pctrl, data[1].active_pstate); 49 data[0].state = 1; 50 data[1].state = 1; 51 } else if (strncmp(state, "sleep", strlen("sleep")) == 0) { // 向gpio_demo中寫入sleep字符串 52 pinctrl_select_state(data[0].pctrl, data[0].sleep_pstate); 53 pinctrl_select_state(data[1].pctrl, data[1].sleep_pstate); 54 data[0].state = 0; 55 data[1].state = 0; 56 } else if (sscanf(buf, "%d", &value)) { // 向gpio_demo中寫入數字可以控制GPM4_0(第0位)和GPM4_1(第1位)的高低電平 57 if (value & (1 << 0)) 58 gpio_set_value(data[0].gpio, 1); 59 else 60 gpio_set_value(data[0].gpio, 0); 61 62 if (value & (1 << 1)) 63 gpio_set_value(data[1].gpio, 1); 64 else 65 gpio_set_value(data[1].gpio, 0); 66 } 67 68 return count; 69 } 70 71 static DEVICE_ATTR(gpio_demo, 0644, gpio_demo_show, 72 gpio_demo_store); 73 74 static int gpio_demo_probe(struct platform_device *pdev) { 75 struct device *dev = &pdev->dev; 76 int ret = 0; 77 int i = 0; 78 int gpio = -1; 79 gpio_demo_data_t *data = NULL; 80 struct resource *res = NULL; 81 u32 config, pud, drv; 82 83 printk("%s enter.\n", __func__); 84 85 if (!dev->of_node) { 86 dev_err(dev, "no platform data.\n"); 87 goto err1; 88 } 89 90 data = devm_kzalloc(dev, sizeof(*data)*2, GFP_KERNEL); 91 if (!data) { 92 dev_err(dev, "no memory.\n"); 93 goto err0; 94 } 95 96 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 97 // 由於這部分內存空間在drivers/pinctrl/samsung/pinctrl-samsung.c中已經被申請,這裏再去申請會報錯,但是我們還是可以調用ioremap直接映射 98 #if 0 99 base = devm_ioremap_resource(dev, res); 100 #else 101 base = devm_ioremap(dev, res->start, res->end - res->start); 102 printk("%s: remap: 0x%x ---> 0x%x to %p\n", __func__, res->start, res->end, base); 103 #endif 104 if (IS_ERR(base)) { 105 dev_err(dev, "remap failed.\n"); 106 devm_kfree(dev, data); 107 return PTR_ERR(base); 108 } 109 110 cfg_reg = base; 111 pud_reg = (u32 *)((u32)cfg_reg + 8); 112 drv_reg = (u32 *)((u32)pud_reg + 4); 113 114 for (i = 0; i < 2; i++) { 115 gpio = of_get_named_gpio(dev->of_node, 116 "tiny4412,gpio", i); // 從設備樹中獲得gpio號 117 if (gpio < 0) { 118 dev_err(dev, "Looking up %s (%d) property in node %s failed %d\n", 119 "tiny4412,gpio", i, dev->of_node->full_name, gpio); 120 goto err2; 121 } 122 123 data[i].gpio = gpio; 124 if (gpio_is_valid(gpio)) { 125 sprintf(data[i].name, "tiny4412.led%d", i); 126 ret = devm_gpio_request_one(dev, gpio, GPIOF_INIT_HIGH, data[i].name); // 申請gpio資源 127 if (ret < 0) { 128 dev_err(dev, "request gpio %d failed.\n", gpio); 129 goto err2; 130 } 131 } 132 133 data[i].pctrl = devm_pinctrl_get(dev); // 獲得pinctrl 134 if (IS_ERR(data[i].pctrl)) { 135 dev_err(dev, "pinctrl get failed.\n"); 136 devm_gpio_free(dev, gpio); 137 goto err2; 138 } 139 140 data[i].sleep_pstate = pinctrl_lookup_state(data[i].pctrl, "gpio_sleep"); // 獲得'gpio_sleep'對應的配置 141 if (IS_ERR(data[i].sleep_pstate)) { 142 dev_err(dev, "look up sleep state failed.\n"); 143 devm_pinctrl_put(data[i].pctrl); 144 devm_gpio_free(dev, gpio); 145 goto err2; 146 } 147 148 data[i].active_pstate = pinctrl_lookup_state(data[i].pctrl, "gpio_active"); // 獲得'gpio_active'對應的配置 149 if (IS_ERR(data[i].active_pstate)) { 150 dev_err(dev, "look up sleep state failed.\n"); 151 devm_pinctrl_put(data[i].pctrl); 152 devm_gpio_free(dev, gpio); 153 goto err2; 154 } 155 } 156 157 dev_set_drvdata(dev, data); 158 // 這樣會在/sys/bus/platform/drivers/gpio_demo/110002e0.gpio_demo/下生成一個名爲gpio_demo的節點 159 device_create_file(dev, &dev_attr_gpio_demo); 160 161 config = readl(cfg_reg); 162 pud = readl(pud_reg); 163 drv = readl(drv_reg); 164 printk("%s, default state: cfg: 0x%x, pud: 0x%x, drv: 0x%x\n", __func__, config, pud, drv); 165 166 pinctrl_select_state(data[0].pctrl, data[0].active_pstate); // 講GPM4_0設置爲active對應的配置 167 pinctrl_select_state(data[1].pctrl, data[1].active_pstate); // 講GPM4_1設置爲active對應的配置 168 data[0].state = 1; 169 data[1].state = 1; 170 171 config = readl(cfg_reg); 172 pud = readl(pud_reg); 173 drv = readl(drv_reg); 174 printk("%s, active state: cfg: 0x%x, pud: 0x%x, drv: 0x%x\n", __func__, config, pud, drv); 175 176 return 0; 177 178 err2: 179 devm_iounmap(dev, base); 180 err1: 181 devm_kfree(dev, data); 182 err0: 183 return -EINVAL; 184 } 185 186 static int gpio_demo_remove(struct platform_device *pdev) 187 { 188 printk("%s enter.\n", __func__); 189 device_remove_file(&pdev->dev, &dev_attr_gpio_demo); 190 191 return 0; 192 } 193 194 static const struct of_device_id gpio_demo_dt_ids[] = { 195 { .compatible = "tiny4412,gpio_demo", }, 196 {}, 197 }; 198 199 MODULE_DEVICE_TABLE(of, gpio_demo_dt_ids); 200 201 static struct platform_driver gpio_demo_driver = { 202 .driver = { 203 .name = "gpio_demo", 204 .of_match_table = of_match_ptr(gpio_demo_dt_ids), 205 }, 206 .probe = gpio_demo_probe, 207 .remove = gpio_demo_remove, 208 }; 209 210 static int __init gpio_demo_init(void) 211 { 212 int ret; 213 214 ret = platform_driver_register(&gpio_demo_driver); 215 if (ret) 216 printk(KERN_ERR "int demo: probe failed: %d\n", ret); 217 218 return ret; 219 } 220 module_init(gpio_demo_init); 221 222 static void __exit gpio_demo_exit(void) 223 { 224 platform_driver_unregister(&gpio_demo_driver); 225 } 226 module_exit(gpio_demo_exit); 227 228 MODULE_LICENSE("GPL");
使用新的設備樹鏡像啓動系統,加載上面的驅動:
1 [root@tiny4412 root]# cd /mnt/ 2 [root@tiny4412 mnt]# ls 3 fdt gpio_demo.ko interrupt_demo.ko 4 [root@tiny4412 mnt]# insmod gpio_demo.ko 5 [ 2353.713293] samsung_pinmux_setup: reg: 0xf08a02e0, value: 0x1111 6 [ 2353.713451] samsung_pinconf_rw: reg: 0xf08a02e8, value: 0x555d 7 [ 2353.713633] samsung_pinconf_rw: reg: 0xf08a02ec, value: 0xe 8 [ 2353.713799] samsung_pinmux_setup: reg: 0xf08a02e0, value: 0x1111 9 [ 2353.714409] samsung_pinconf_rw: reg: 0xf08a02e8, value: 0x5555 10 [ 2353.720404] samsung_pinconf_rw: reg: 0xf08a02ec, value: 0xa // 在probe之前,會將這個gpio配置爲default指向的狀態 11 [ 2353.726129] gpio_demo_probe enter. 12 [ 2353.729197] gpio_demo_probe: remap: 0x110002e0 ---> 0x110002ef to f0a202e0 13 [ 2353.736304] gpiochip_generic_request enter 14 [ 2353.740113] samsung_gpio_set: reg: 0xf08a02e4, value: 0xf 15 [ 2353.745615] samsung_gpio_set_direction: reg: 0xf08a02e0, value: 0x1111 // 申請GPIO,並設置初始狀態 16 [ 2353.752264] gpiochip_generic_request enter 17 [ 2353.756144] samsung_gpio_set: reg: 0xf08a02e4, value: 0xf 18 [ 2353.761489] samsung_gpio_set_direction: reg: 0xf08a02e0, value: 0x1111 19 [ 2353.768047] gpio_demo_probe, default state: cfg: 0x1111, pud: 0x5555, drv: 0xa 20 [ 2353.775238] samsung_pinmux_setup: reg: 0xf08a02e0, value: 0x1111 21 [ 2353.781199] samsung_pinconf_rw: reg: 0xf08a02e8, value: 0x5557 22 [ 2353.787003] samsung_pinconf_rw: reg: 0xf08a02ec, value: 0xb 23 [ 2353.792561] samsung_pinmux_setup: reg: 0xf08a02e0, value: 0x1111 24 [ 2353.798549] samsung_pinconf_rw: reg: 0xf08a02e8, value: 0x555f 25 [ 2353.804707] samsung_pinconf_rw: reg: 0xf08a02ec, value: 0xf // 將gpio設置爲active狀態 26 [ 2353.809868] gpio_demo_probe, active state: cfg: 0x1111, pud: 0x555f, drv: 0xf 27 [root@tiny4412 mnt]# 28 [root@tiny4412 mnt]# cd /sys/bus/platform/drivers/gpio_demo/110002e0.gpio_demo/ 29 [root@tiny4412 110002e0.gpio_demo]# 30 [root@tiny4412 110002e0.gpio_demo]# ls 31 driver gpio_demo of_node subsystem 32 driver_override modalias power uevent 33 [root@tiny4412 110002e0.gpio_demo]# echo 0 > ./gpio_demo // 可以看到兩個LED燈都亮了 34 [ 2375.680666] samsung_gpio_set: reg: 0xf08a02e4, value: 0xe 35 [ 2375.680791] samsung_gpio_set: reg: 0xf08a02e4, value: 0xc 36 [root@tiny4412 110002e0.gpio_demo]# 37 [root@tiny4412 110002e0.gpio_demo]# echo 3 > ./gpio_demo // 可以看到兩個LED燈都滅了 38 [ 2381.337016] samsung_gpio_set: reg: 0xf08a02e4, value: 0xd 39 [ 2381.337135] samsung_gpio_set: reg: 0xf08a02e4, value: 0xf 40 [root@tiny4412 110002e0.gpio_demo]# 41 [root@tiny4412 110002e0.gpio_demo]# echo 1 > ./gpio_demo // 可以看到兩個LED2亮了 42 [ 2392.969679] samsung_gpio_set: reg: 0xf08a02e4, value: 0xf 43 [ 2392.969798] samsung_gpio_set: reg: 0xf08a02e4, value: 0xd 44 [root@tiny4412 110002e0.gpio_demo]# 45 [root@tiny4412 110002e0.gpio_demo]# 46 [root@tiny4412 110002e0.gpio_demo]# echo 1 > ./gpio_demo // 可以看大,不會緩存上次的狀態 47 [ 2459.084622] samsung_gpio_set: reg: 0xf08a02e4, value: 0xd 48 [ 2459.084744] samsung_gpio_set: reg: 0xf08a02e4, value: 0xd 49 [root@tiny4412 110002e0.gpio_demo]# 50 [root@tiny4412 110002e0.gpio_demo]# echo 1 > ./gpio_demo 51 [ 2478.405474] samsung_gpio_set: reg: 0xf08a02e4, value: 0xd 52 [ 2478.405592] samsung_gpio_set: reg: 0xf08a02e4, value: 0xd 53 [root@tiny4412 110002e0.gpio_demo]# 54 [root@tiny4412 110002e0.gpio_demo]# echo sleep > ./gpio_demo // 設置爲sleep狀態,會緩存當前狀態,當前狀態已經是sleep了,再次設置sleep的時候,不會看到下面的log 55 [ 2939.212916] samsung_pinmux_setup: reg: 0xf08a02e0, value: 0x1111 56 [ 2939.213045] samsung_pinconf_rw: reg: 0xf08a02e8, value: 0x555c 57 [ 2939.213160] samsung_pinconf_rw: reg: 0xf08a02ec, value: 0xc 58 [ 2939.213272] samsung_pinmux_setup: reg: 0xf08a02e0, value: 0x1111 59 [ 2939.214008] samsung_pinconf_rw: reg: 0xf08a02e8, value: 0x5550 60 [ 2939.219955] samsung_pinconf_rw: reg: 0xf08a02ec, value: 0x0 61 [root@tiny4412 110002e0.gpio_demo]# 62 [root@tiny4412 110002e0.gpio_demo]# cat gpio_demo 63 gpio0: sleep, gpio1: sleep, cfg: 0x1111, pud: 0x5550, drv: 0x0 64 [root@tiny4412 110002e0.gpio_demo]# 65 [root@tiny4412 110002e0.gpio_demo]# echo active > ./gpio_demo // 設置爲active狀態 66 [ 2950.581726] samsung_pinmux_setup: reg: 0xf08a02e0, value: 0x1111 67 [ 2950.581855] samsung_pinconf_rw: reg: 0xf08a02e8, value: 0x5553 68 [ 2950.581968] samsung_pinconf_rw: reg: 0xf08a02ec, value: 0x3 69 [ 2950.582078] samsung_pinmux_setup: reg: 0xf08a02e0, value: 0x1111 70 [ 2950.582821] samsung_pinconf_rw: reg: 0xf08a02e8, value: 0x555f 71 [ 2950.588734] samsung_pinconf_rw: reg: 0xf08a02ec, value: 0xf 72 [root@tiny4412 110002e0.gpio_demo]# 73 [root@tiny4412 110002e0.gpio_demo]# cat gpio_demo 74 gpio0: active, gpio1: active, cfg: 0x1111, pud: 0x555f, drv: 0xf 75 [root@tiny4412 110002e0.gpio_demo]#
對應寄存器手冊和設備樹的屬性,可以理解上面的log。
未完待續... ...
本文来自博客园,作者:摩斯电码,未经同意,禁止转载