Linux晶片級移植與底層驅動(基於3.7.4核心)(GPIO&&pinctrl&&clk)
6. GPIO驅動
在drivers/gpio下實現了通用的基於gpiolib的GPIO驅動,其中定義了一個通用的用於描述底層GPIO控制器的gpio_chip結構體,並要求具體的SoC實現gpio_chip結構體的成員函式,最後透過gpiochip_add()註冊gpio_chip。
gpio_chip結構體封裝了底層的硬體的GPIO enable/disable等操作,它定義為:
94struct gpio_chip {
95 const char *label;
96 struct device *dev;
97 struct module *owner;
98
99 int (*request)(struct gpio_chip *chip,
100 unsigned offset);
101 void (*free)(struct gpio_chip *chip,
102 unsigned offset);
103
104 int (*direction_input)(struct gpio_chip *chip,
105 unsigned offset);
106 int (*get)(struct gpio_chip *chip,
107 unsigned offset);
108 int (*direction_output)(structgpio_chip *chip,
109 unsigned offset, int value);
110 int (*set_debounce)(struct gpio_chip *chip,
111 unsigned offset, unsigned debounce);
112
113 void (*set)(struct gpio_chip *chip,
114 unsigned offset, int value);
115
116 int (*to_irq)(struct gpio_chip *chip,
117 unsigned offset);
118
119 void (*dbg_show)(struct seq_file *s,
120 struct gpio_chip *chip);
121 int base;
122 u16 ngpio;
123 const char *const*names;
124 unsigned can_sleep:1;
125 unsigned exported:1;
126
127#if defined(CONFIG_OF_GPIO)
128 /*
129 * If CONFIG_OF is enabled, then all GPIOcontrollers described in the
130 * device tree automatically may have an OF translation
131 */
132 struct device_node *of_node;
133 int of_gpio_n_cells;
134 int (*of_xlate)(struct gpio_chip *gc,
135 const structof_phandle_args *gpiospec, u32 *flags);
136#endif
137};
透過這層封裝,每個具體的要用到GPIO的裝置驅動都使用通用的GPIO API來操作GPIO,這些API主要用於GPIO的申請、釋放和設定:
intgpio_request(unsigned gpio, const char *label);
voidgpio_free(unsigned gpio);
intgpio_direction_input(unsigned gpio);
intgpio_direction_output(unsigned gpio, int value);
intgpio_set_debounce(unsigned gpio, unsigned debounce);
intgpio_get_value_cansleep(unsigned gpio);
voidgpio_set_value_cansleep(unsigned gpio, int value);
intgpio_request_one(unsigned gpio, unsigned long flags, const char *label);
intgpio_request_array(const struct gpio *array, size_t num);
voidgpio_free_array(const struct gpio *array, size_t num);
intdevm_gpio_request(struct device *dev, unsigned gpio, const char *label);
intdevm_gpio_request_one(struct device *dev, unsigned gpio,
unsigned long flags,const char *label);
voiddevm_gpio_free(struct device *dev, unsigned int gpio);
注意,核心中針對記憶體、IRQ、時鐘、GPIO、pinctrl都有devm_開頭的API,使用這部分API的時候,核心會有類似於Java資源自動回收機制,因此在程式碼中做出錯處理時,無需釋放相關的資源。
對於GPIO而言,特別值得一提的是,核心會建立/sys結點 /sys/class/gpio/gpioN/,透過它我們可以echo值從而改變GPIO的方向、設定和獲取GPIO的值。
在擁有Device Tree支援的情況之下,我們可以透過Device Tree來描述某GPIO控制器提供的GPIO引腳被具體裝置使用的情況。在GPIO控制器對應的結點中,需定義#gpio-cells 和gpio-controller屬性,具體的裝置結點則透過xxx-gpios屬性來引用GPIO控制器結點及GPIO引腳。
如VEXPRESS電路板 DT檔案arch/arm/boot/dts/vexpress-v2m.dtsi中擁有如下GPIO控制器結點:
73 v2m_sysreg:[email protected] {
74 compatible ="arm,vexpress-sysreg";
75 reg = <0x000000x1000>;
76 gpio-controller;
77 #gpio-cells =<2>;
78 };
VEXPRESS電路板上的MMC控制器會使用該結點GPIO控制器提供的GPIO引腳,則具體的[email protected]裝置結點的會通過-gpios屬性引用GPIO:
111 [email protected] {
112 compatible ="arm,pl180", "arm,primecell";
113 reg =<0x05000 0x1000>;
114 interrupts =<9 10>;
115 cd-gpios = <&v2m_sysreg 0 0>;
116 wp-gpios =<&v2m_sysreg 1 0>;
117 …
121 };
其中的cd-gpios用於SD/MMC卡的detection,而wp-gpios用於防寫,MMC主機控制器驅動會透過如下方法獲取這2個GPIO,詳見於drivers/mmc/host/mmci.c:
1220static void mmci_dt_populate_generic_pdata(struct device_node *np,
1221 structmmci_platform_data *pdata)
1222{
1223 int bus_width = 0;
1224
1225 pdata->gpio_wp =of_get_named_gpio(np, "wp-gpios", 0);
1226 pdata->gpio_cd =of_get_named_gpio(np, "cd-gpios", 0);
…
}
7. pinctrl驅動
許多SoC內部都包含pin控制器,通過pin控制器的暫存器,我們可以配置一個或者一組引腳的功能和特性。在軟體上,Linux核心的pinctrl驅動可以操作pin控制器為我們完成如下工作:
§ 列舉並且命名pin控制器可控制的所有引腳;
§ 提供引腳複用的能力;
§ 提供配置引腳的能力,如驅動能力、上拉下拉、開漏(open drain)等。
pinctrl和引腳
在特定SoC的pinctrl驅動中,我們需要定義引腳。假設有一個PGA封裝的晶片的引腳排布如下:
A B C D E F G H
8 o o o o o o o o
7 o o o o o o o o
6 o o o o o o o o
5 o o o o o o o o
4 o o o o o o o o
3 o o o o o o o o
2 o o o o o o o o
1 o o o o o o o o
在pinctrl驅動初始化的時候,需要向pinctrl子系統註冊一個pinctrl_desc描述符,在該描述符中包含所有引腳的列表。可以通過如下程式碼來註冊這個pin控制器並命名其所有引腳:
59#include <linux/pinctrl/pinctrl.h>
60
61const struct pinctrl_pin_descfoo_pins[] = {
62 PINCTRL_PIN(0, "A8"),
63 PINCTRL_PIN(1, "B8"),
64 PINCTRL_PIN(2, "C8"),
65 ...
66 PINCTRL_PIN(61, "F1"),
67 PINCTRL_PIN(62, "G1"),
68 PINCTRL_PIN(63, "H1"),
69};
70
71static struct pinctrl_descfoo_desc = {
72 .name = "foo",
73 .pins = foo_pins,
74 .npins = ARRAY_SIZE(foo_pins),
75 .maxpin = 63,
76 .owner = THIS_MODULE,
77};
78
79int __init foo_probe(void)
80{
81 struct pinctrl_dev *pctl;
82
83 pctl = pinctrl_register(&foo_desc,<PARENT>, NULL);
84 if (IS_ERR(pctl))
85 pr_err("could not registerfoo pin driver\n");
86}
引腳組(pin group)
在pinctrl子系統中,支援將一組引腳繫結為同一功能。假設{ 0, 8, 16, 24 }這一組引腳承擔SPI的功能,而{ 24, 25 }這一組引腳承擔I2C介面功能。在驅動的程式碼中,需要體現這個分組關係,並且為這些分組實現pinctrl_ops的成員函式get_groups_count、get_groups_count和get_groups_count,將pinctrl_ops填充到前文pinctrl_desc的例項foo_desc中。
130#include <linux/pinctrl/pinctrl.h>
131
132struct foo_group {
133 const char *name;
134 const unsigned int *pins;
135 const unsigned num_pins;
136};
137
138static const unsigned int spi0_pins[] = { 0, 8, 16, 24 };
139static const unsigned int i2c0_pins[] = { 24, 25 };
140
141static const struct foo_group foo_groups[] = {
142 {
143 .name = "spi0_grp",
144 .pins = spi0_pins,
145 .num_pins =ARRAY_SIZE(spi0_pins),
146 },
147 {
148 .name = "i2c0_grp",
149 .pins = i2c0_pins,
150 .num_pins =ARRAY_SIZE(i2c0_pins),
151 },
152};
153
154
155static int foo_get_groups_count(struct pinctrl_dev *pctldev)
156{
157 return ARRAY_SIZE(foo_groups);
158}
159
160static const char *foo_get_group_name(struct pinctrl_dev *pctldev,
161 unsigned selector)
162{
163 return foo_groups[selector].name;
164}
165
166static int foo_get_group_pins(struct pinctrl_dev *pctldev, unsigned selector,
167 unsigned **const pins,
168 unsigned *const num_pins)
169{
170 *pins = (unsigned *) foo_groups[selector].pins;
171 *num_pins =foo_groups[selector].num_pins;
172 return 0;
173}
174
175static struct pinctrl_opsfoo_pctrl_ops = {
176 .get_groups_count =foo_get_groups_count,
177 .get_group_name = foo_get_group_name,
178 .get_group_pins = foo_get_group_pins,
179};
180
181
182static struct pinctrl_descfoo_desc = {
183 ...
184 .pctlops = &foo_pctrl_ops,
185};
get_groups_count()成員函式用於告知pinctrl子系統該SoC中合法的被選引腳組有多少個,而get_group_name()則提供引腳組的名字,get_group_pins()提供引腳組的引腳表。在裝置驅動呼叫pinctrl通用API使能某一組引腳的對應功能時,pinctrl子系統的核心層會呼叫上述callback函式。
引腳配置
裝置驅動有時候需要配置引腳,譬如可能把引腳設定為高阻或者三態(達到類似斷連引腳的效果),或通過某阻值將引腳上拉/下拉以確保預設狀態下引腳的電平狀態。驅動中可以自定義相應板級引腳配置API的細節,譬如某裝置驅動可能通過如下程式碼將某引腳上拉:
#include<linux/pinctrl/consumer.h>
ret= pin_config_set("foo-dev", "FOO_GPIO_PIN",PLATFORM_X_PULL_UP);
其中的PLATFORM_X_PULL_UP由特定的pinctrl驅動定義。在特定的pinctrl驅動中,需要實現完成這些配置所需要的callback函式(pinctrl_desc的confops成員函式):
222#include <linux/pinctrl/pinctrl.h>
223#include <linux/pinctrl/pinconf.h>
224#include "platform_x_pindefs.h"
225
226static int foo_pin_config_get(struct pinctrl_dev *pctldev,
227 unsigned offset,
228 unsigned long *config)
229{
230 struct my_conftype conf;
231
232 ... Find setting for pin @ offset ...
233
234 *config = (unsigned long) conf;
235}
236
237static int foo_pin_config_set(struct pinctrl_dev *pctldev,
238 unsigned offset,
239 unsigned long config)
240{
241 struct my_conftype *conf = (structmy_conftype *) config;
242
243 switch (conf) {
244 case PLATFORM_X_PULL_UP:
245 ...
246 }
247 }
248}
249
250static int foo_pin_config_group_get (struct pinctrl_dev *pctldev,
251 unsigned selector,
252 unsigned long *config)
253{
254 ...
255}
256
257static int foo_pin_config_group_set (struct pinctrl_dev *pctldev,
258 unsigned selector,
259 unsigned long config)
260{
261 ...
262}
263
264static struct pinconf_opsfoo_pconf_ops = {
265 .pin_config_get = foo_pin_config_get,
266 .pin_config_set = foo_pin_config_set,
267 .pin_config_group_get =foo_pin_config_group_get,
268 .pin_config_group_set =foo_pin_config_group_set,
269};
270
271/* Pin config operations are handled by some pin controller */
272static struct pinctrl_descfoo_desc = {
273 ...
274 .confops = &foo_pconf_ops,
275};
其中的pin_config_group_get()、pin_config_group_set()針對的是可同時配置一個引腳組的狀態情況,而pin_config_get()、pin_config_set()針對的則是單個引腳的配置。
與GPIO子系統的互動
pinctrl驅動中所覆蓋的引腳可能同時可作為GPIO用,核心的GPIO子系統和pinctrl子系統本來是並行工作的,但是有時候需要交叉對映,這種情況下,需要在pinctrl驅動中告知pinctrl子系統核心層GPIO與底層pinctrl驅動所管理的引腳之間的對映關係。假設pinctrl驅動中定義的引腳32~47與gpio_chip例項chip_a的GPIO對應,引腳64~71與gpio_chip例項chip_b的GPIO對應,即對映關係為:
chip a:
- GPIO range :[32 .. 47]
- pinrange : [32 .. 47]
chip b:
- GPIO range :[48 .. 55]
- pinrange : [64 .. 71]
則在特定pinctrl驅動中可以透過如下程式碼註冊2個GPIO範圍:
305struct gpio_chip chip_a;
306struct gpio_chip chip_b;
307
308static struct pinctrl_gpio_range gpio_range_a = {
309 .name = "chip a",
310 .id = 0,
311 .base = 32,
312 .pin_base = 32,