基於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。

 

未完待續... ...

posted @ 2016-11-20 23:09  摩斯电码  阅读(1882)  评论(0编辑  收藏  举报