diff options
author | Tony Lindgren <tony@atomide.com> | 2012-07-10 05:05:46 -0400 |
---|---|---|
committer | Linus Walleij <linus.walleij@linaro.org> | 2012-07-14 16:33:08 -0400 |
commit | 8b8b091bf07fa7ef7f13c1ac40b30bcf74050b60 (patch) | |
tree | 4cfa94d44a78fd13f0b9a1700222f67781256170 | |
parent | 3923040bd1ba2ee5357f6ac01c82d8c24d73ce26 (diff) |
pinctrl: Add one-register-per-pin type device tree based pinctrl driver
Add one-register-per-pin type device tree based pinctrl driver.
This driver has been tested on omap2+ series of processors,
where there is either an 8 or 16-bit padconf register for each pin.
Support for other similar pinmux controllers can be added.
Signed-off-by: Tony Lindgren <tony@atomide.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
-rw-r--r-- | Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt | 93 | ||||
-rw-r--r-- | drivers/pinctrl/Kconfig | 8 | ||||
-rw-r--r-- | drivers/pinctrl/Makefile | 1 | ||||
-rw-r--r-- | drivers/pinctrl/pinctrl-single.c | 987 |
4 files changed, 1089 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt new file mode 100644 index 000000000000..5187f0dd8b28 --- /dev/null +++ b/Documentation/devicetree/bindings/pinctrl/pinctrl-single.txt | |||
@@ -0,0 +1,93 @@ | |||
1 | One-register-per-pin type device tree based pinctrl driver | ||
2 | |||
3 | Required properties: | ||
4 | - compatible : "pinctrl-single" | ||
5 | |||
6 | - reg : offset and length of the register set for the mux registers | ||
7 | |||
8 | - pinctrl-single,register-width : pinmux register access width in bits | ||
9 | |||
10 | - pinctrl-single,function-mask : mask of allowed pinmux function bits | ||
11 | in the pinmux register | ||
12 | |||
13 | Optional properties: | ||
14 | - pinctrl-single,function-off : function off mode for disabled state if | ||
15 | available and same for all registers; if not specified, disabling of | ||
16 | pin functions is ignored | ||
17 | |||
18 | This driver assumes that there is only one register for each pin, | ||
19 | and uses the common pinctrl bindings as specified in the pinctrl-bindings.txt | ||
20 | document in this directory. | ||
21 | |||
22 | The pin configuration nodes for pinctrl-single are specified as pinctrl | ||
23 | register offset and value pairs using pinctrl-single,pins. Only the bits | ||
24 | specified in pinctrl-single,function-mask are updated. For example, setting | ||
25 | a pin for a device could be done with: | ||
26 | |||
27 | pinctrl-single,pins = <0xdc 0x118>; | ||
28 | |||
29 | Where 0xdc is the offset from the pinctrl register base address for the | ||
30 | device pinctrl register, and 0x118 contains the desired value of the | ||
31 | pinctrl register. See the device example and static board pins example | ||
32 | below for more information. | ||
33 | |||
34 | Example: | ||
35 | |||
36 | /* SoC common file */ | ||
37 | |||
38 | /* first controller instance for pins in core domain */ | ||
39 | pmx_core: pinmux@4a100040 { | ||
40 | compatible = "pinctrl-single"; | ||
41 | reg = <0x4a100040 0x0196>; | ||
42 | #address-cells = <1>; | ||
43 | #size-cells = <0>; | ||
44 | pinctrl-single,register-width = <16>; | ||
45 | pinctrl-single,function-mask = <0xffff>; | ||
46 | }; | ||
47 | |||
48 | /* second controller instance for pins in wkup domain */ | ||
49 | pmx_wkup: pinmux@4a31e040 { | ||
50 | compatible = "pinctrl-single; | ||
51 | reg = <0x4a31e040 0x0038>; | ||
52 | #address-cells = <1>; | ||
53 | #size-cells = <0>; | ||
54 | pinctrl-single,register-width = <16>; | ||
55 | pinctrl-single,function-mask = <0xffff>; | ||
56 | }; | ||
57 | |||
58 | |||
59 | /* board specific .dts file */ | ||
60 | |||
61 | &pmx_core { | ||
62 | |||
63 | /* | ||
64 | * map all board specific static pins enabled by the pinctrl driver | ||
65 | * itself during the boot (or just set them up in the bootloader) | ||
66 | */ | ||
67 | pinctrl-names = "default"; | ||
68 | pinctrl-0 = <&board_pins>; | ||
69 | |||
70 | board_pins: pinmux_board_pins { | ||
71 | pinctrl-single,pins = < | ||
72 | 0x6c 0xf | ||
73 | 0x6e 0xf | ||
74 | 0x70 0xf | ||
75 | 0x72 0xf | ||
76 | >; | ||
77 | }; | ||
78 | |||
79 | /* map uart2 pins */ | ||
80 | uart2_pins: pinmux_uart2_pins { | ||
81 | pinctrl-single,pins = < | ||
82 | 0xd8 0x118 | ||
83 | 0xda 0 | ||
84 | 0xdc 0x118 | ||
85 | 0xde 0 | ||
86 | >; | ||
87 | }; | ||
88 | }; | ||
89 | |||
90 | &uart2 { | ||
91 | pinctrl-names = "default"; | ||
92 | pinctrl-0 = <&uart2_pins>; | ||
93 | }; | ||
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index 4c05fe8ced59..54e3588bef62 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig | |||
@@ -102,6 +102,14 @@ config PINCTRL_PXA910 | |||
102 | select PINCTRL_PXA3xx | 102 | select PINCTRL_PXA3xx |
103 | select PINCONF | 103 | select PINCONF |
104 | 104 | ||
105 | config PINCTRL_SINGLE | ||
106 | tristate "One-register-per-pin type device tree based pinctrl driver" | ||
107 | depends on OF | ||
108 | select PINMUX | ||
109 | select PINCONF | ||
110 | help | ||
111 | This selects the device tree based generic pinctrl driver. | ||
112 | |||
105 | config PINCTRL_SIRF | 113 | config PINCTRL_SIRF |
106 | bool "CSR SiRFprimaII pin controller driver" | 114 | bool "CSR SiRFprimaII pin controller driver" |
107 | depends on ARCH_PRIMA2 | 115 | depends on ARCH_PRIMA2 |
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index 8c074376cdea..f40b1f81ff2c 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile | |||
@@ -22,6 +22,7 @@ obj-$(CONFIG_PINCTRL_NOMADIK) += pinctrl-nomadik.o | |||
22 | obj-$(CONFIG_PINCTRL_DB8500) += pinctrl-nomadik-db8500.o | 22 | obj-$(CONFIG_PINCTRL_DB8500) += pinctrl-nomadik-db8500.o |
23 | obj-$(CONFIG_PINCTRL_PXA168) += pinctrl-pxa168.o | 23 | obj-$(CONFIG_PINCTRL_PXA168) += pinctrl-pxa168.o |
24 | obj-$(CONFIG_PINCTRL_PXA910) += pinctrl-pxa910.o | 24 | obj-$(CONFIG_PINCTRL_PXA910) += pinctrl-pxa910.o |
25 | obj-$(CONFIG_PINCTRL_SINGLE) += pinctrl-single.o | ||
25 | obj-$(CONFIG_PINCTRL_SIRF) += pinctrl-sirf.o | 26 | obj-$(CONFIG_PINCTRL_SIRF) += pinctrl-sirf.o |
26 | obj-$(CONFIG_PINCTRL_TEGRA) += pinctrl-tegra.o | 27 | obj-$(CONFIG_PINCTRL_TEGRA) += pinctrl-tegra.o |
27 | obj-$(CONFIG_PINCTRL_TEGRA20) += pinctrl-tegra20.o | 28 | obj-$(CONFIG_PINCTRL_TEGRA20) += pinctrl-tegra20.o |
diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c new file mode 100644 index 000000000000..76a4260f20f3 --- /dev/null +++ b/drivers/pinctrl/pinctrl-single.c | |||
@@ -0,0 +1,987 @@ | |||
1 | /* | ||
2 | * Generic device tree based pinctrl driver for one register per pin | ||
3 | * type pinmux controllers | ||
4 | * | ||
5 | * Copyright (C) 2012 Texas Instruments, Inc. | ||
6 | * | ||
7 | * This file is licensed under the terms of the GNU General Public | ||
8 | * License version 2. This program is licensed "as is" without any | ||
9 | * warranty of any kind, whether express or implied. | ||
10 | */ | ||
11 | |||
12 | #include <linux/init.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/io.h> | ||
15 | #include <linux/slab.h> | ||
16 | #include <linux/err.h> | ||
17 | #include <linux/list.h> | ||
18 | |||
19 | #include <linux/of.h> | ||
20 | #include <linux/of_device.h> | ||
21 | #include <linux/of_address.h> | ||
22 | |||
23 | #include <linux/pinctrl/pinctrl.h> | ||
24 | #include <linux/pinctrl/pinmux.h> | ||
25 | |||
26 | #include "core.h" | ||
27 | |||
28 | #define DRIVER_NAME "pinctrl-single" | ||
29 | #define PCS_MUX_NAME "pinctrl-single,pins" | ||
30 | #define PCS_REG_NAME_LEN ((sizeof(unsigned long) * 2) + 1) | ||
31 | #define PCS_OFF_DISABLED ~0U | ||
32 | |||
33 | /** | ||
34 | * struct pcs_pingroup - pingroups for a function | ||
35 | * @np: pingroup device node pointer | ||
36 | * @name: pingroup name | ||
37 | * @gpins: array of the pins in the group | ||
38 | * @ngpins: number of pins in the group | ||
39 | * @node: list node | ||
40 | */ | ||
41 | struct pcs_pingroup { | ||
42 | struct device_node *np; | ||
43 | const char *name; | ||
44 | int *gpins; | ||
45 | int ngpins; | ||
46 | struct list_head node; | ||
47 | }; | ||
48 | |||
49 | /** | ||
50 | * struct pcs_func_vals - mux function register offset and value pair | ||
51 | * @reg: register virtual address | ||
52 | * @val: register value | ||
53 | */ | ||
54 | struct pcs_func_vals { | ||
55 | void __iomem *reg; | ||
56 | unsigned val; | ||
57 | }; | ||
58 | |||
59 | /** | ||
60 | * struct pcs_function - pinctrl function | ||
61 | * @name: pinctrl function name | ||
62 | * @vals: register and vals array | ||
63 | * @nvals: number of entries in vals array | ||
64 | * @pgnames: array of pingroup names the function uses | ||
65 | * @npgnames: number of pingroup names the function uses | ||
66 | * @node: list node | ||
67 | */ | ||
68 | struct pcs_function { | ||
69 | const char *name; | ||
70 | struct pcs_func_vals *vals; | ||
71 | unsigned nvals; | ||
72 | const char **pgnames; | ||
73 | int npgnames; | ||
74 | struct list_head node; | ||
75 | }; | ||
76 | |||
77 | /** | ||
78 | * struct pcs_data - wrapper for data needed by pinctrl framework | ||
79 | * @pa: pindesc array | ||
80 | * @cur: index to current element | ||
81 | * | ||
82 | * REVISIT: We should be able to drop this eventually by adding | ||
83 | * support for registering pins individually in the pinctrl | ||
84 | * framework for those drivers that don't need a static array. | ||
85 | */ | ||
86 | struct pcs_data { | ||
87 | struct pinctrl_pin_desc *pa; | ||
88 | int cur; | ||
89 | }; | ||
90 | |||
91 | /** | ||
92 | * struct pcs_name - register name for a pin | ||
93 | * @name: name of the pinctrl register | ||
94 | * | ||
95 | * REVISIT: We may want to make names optional in the pinctrl | ||
96 | * framework as some drivers may not care about pin names to | ||
97 | * avoid kernel bloat. The pin names can be deciphered by user | ||
98 | * space tools using debugfs based on the register address and | ||
99 | * SoC packaging information. | ||
100 | */ | ||
101 | struct pcs_name { | ||
102 | char name[PCS_REG_NAME_LEN]; | ||
103 | }; | ||
104 | |||
105 | /** | ||
106 | * struct pcs_device - pinctrl device instance | ||
107 | * @res: resources | ||
108 | * @base: virtual address of the controller | ||
109 | * @size: size of the ioremapped area | ||
110 | * @dev: device entry | ||
111 | * @pctl: pin controller device | ||
112 | * @mutex: mutex protecting the lists | ||
113 | * @width: bits per mux register | ||
114 | * @fmask: function register mask | ||
115 | * @fshift: function register shift | ||
116 | * @foff: value to turn mux off | ||
117 | * @fmax: max number of functions in fmask | ||
118 | * @names: array of register names for pins | ||
119 | * @pins: physical pins on the SoC | ||
120 | * @pgtree: pingroup index radix tree | ||
121 | * @ftree: function index radix tree | ||
122 | * @pingroups: list of pingroups | ||
123 | * @functions: list of functions | ||
124 | * @ngroups: number of pingroups | ||
125 | * @nfuncs: number of functions | ||
126 | * @desc: pin controller descriptor | ||
127 | * @read: register read function to use | ||
128 | * @write: register write function to use | ||
129 | */ | ||
130 | struct pcs_device { | ||
131 | struct resource *res; | ||
132 | void __iomem *base; | ||
133 | unsigned size; | ||
134 | struct device *dev; | ||
135 | struct pinctrl_dev *pctl; | ||
136 | struct mutex mutex; | ||
137 | unsigned width; | ||
138 | unsigned fmask; | ||
139 | unsigned fshift; | ||
140 | unsigned foff; | ||
141 | unsigned fmax; | ||
142 | struct pcs_name *names; | ||
143 | struct pcs_data pins; | ||
144 | struct radix_tree_root pgtree; | ||
145 | struct radix_tree_root ftree; | ||
146 | struct list_head pingroups; | ||
147 | struct list_head functions; | ||
148 | unsigned ngroups; | ||
149 | unsigned nfuncs; | ||
150 | struct pinctrl_desc desc; | ||
151 | unsigned (*read)(void __iomem *reg); | ||
152 | void (*write)(unsigned val, void __iomem *reg); | ||
153 | }; | ||
154 | |||
155 | /* | ||
156 | * REVISIT: Reads and writes could eventually use regmap or something | ||
157 | * generic. But at least on omaps, some mux registers are performance | ||
158 | * critical as they may need to be remuxed every time before and after | ||
159 | * idle. Adding tests for register access width for every read and | ||
160 | * write like regmap is doing is not desired, and caching the registers | ||
161 | * does not help in this case. | ||
162 | */ | ||
163 | |||
164 | static unsigned __maybe_unused pcs_readb(void __iomem *reg) | ||
165 | { | ||
166 | return readb(reg); | ||
167 | } | ||
168 | |||
169 | static unsigned __maybe_unused pcs_readw(void __iomem *reg) | ||
170 | { | ||
171 | return readw(reg); | ||
172 | } | ||
173 | |||
174 | static unsigned __maybe_unused pcs_readl(void __iomem *reg) | ||
175 | { | ||
176 | return readl(reg); | ||
177 | } | ||
178 | |||
179 | static void __maybe_unused pcs_writeb(unsigned val, void __iomem *reg) | ||
180 | { | ||
181 | writeb(val, reg); | ||
182 | } | ||
183 | |||
184 | static void __maybe_unused pcs_writew(unsigned val, void __iomem *reg) | ||
185 | { | ||
186 | writew(val, reg); | ||
187 | } | ||
188 | |||
189 | static void __maybe_unused pcs_writel(unsigned val, void __iomem *reg) | ||
190 | { | ||
191 | writel(val, reg); | ||
192 | } | ||
193 | |||
194 | static int pcs_get_groups_count(struct pinctrl_dev *pctldev) | ||
195 | { | ||
196 | struct pcs_device *pcs; | ||
197 | |||
198 | pcs = pinctrl_dev_get_drvdata(pctldev); | ||
199 | |||
200 | return pcs->ngroups; | ||
201 | } | ||
202 | |||
203 | static const char *pcs_get_group_name(struct pinctrl_dev *pctldev, | ||
204 | unsigned gselector) | ||
205 | { | ||
206 | struct pcs_device *pcs; | ||
207 | struct pcs_pingroup *group; | ||
208 | |||
209 | pcs = pinctrl_dev_get_drvdata(pctldev); | ||
210 | group = radix_tree_lookup(&pcs->pgtree, gselector); | ||
211 | if (!group) { | ||
212 | dev_err(pcs->dev, "%s could not find pingroup%i\n", | ||
213 | __func__, gselector); | ||
214 | return NULL; | ||
215 | } | ||
216 | |||
217 | return group->name; | ||
218 | } | ||
219 | |||
220 | static int pcs_get_group_pins(struct pinctrl_dev *pctldev, | ||
221 | unsigned gselector, | ||
222 | const unsigned **pins, | ||
223 | unsigned *npins) | ||
224 | { | ||
225 | struct pcs_device *pcs; | ||
226 | struct pcs_pingroup *group; | ||
227 | |||
228 | pcs = pinctrl_dev_get_drvdata(pctldev); | ||
229 | group = radix_tree_lookup(&pcs->pgtree, gselector); | ||
230 | if (!group) { | ||
231 | dev_err(pcs->dev, "%s could not find pingroup%i\n", | ||
232 | __func__, gselector); | ||
233 | return -EINVAL; | ||
234 | } | ||
235 | |||
236 | *pins = group->gpins; | ||
237 | *npins = group->ngpins; | ||
238 | |||
239 | return 0; | ||
240 | } | ||
241 | |||
242 | static void pcs_pin_dbg_show(struct pinctrl_dev *pctldev, | ||
243 | struct seq_file *s, | ||
244 | unsigned offset) | ||
245 | { | ||
246 | seq_printf(s, " " DRIVER_NAME); | ||
247 | } | ||
248 | |||
249 | static void pcs_dt_free_map(struct pinctrl_dev *pctldev, | ||
250 | struct pinctrl_map *map, unsigned num_maps) | ||
251 | { | ||
252 | struct pcs_device *pcs; | ||
253 | |||
254 | pcs = pinctrl_dev_get_drvdata(pctldev); | ||
255 | devm_kfree(pcs->dev, map); | ||
256 | } | ||
257 | |||
258 | static int pcs_dt_node_to_map(struct pinctrl_dev *pctldev, | ||
259 | struct device_node *np_config, | ||
260 | struct pinctrl_map **map, unsigned *num_maps); | ||
261 | |||
262 | static struct pinctrl_ops pcs_pinctrl_ops = { | ||
263 | .get_groups_count = pcs_get_groups_count, | ||
264 | .get_group_name = pcs_get_group_name, | ||
265 | .get_group_pins = pcs_get_group_pins, | ||
266 | .pin_dbg_show = pcs_pin_dbg_show, | ||
267 | .dt_node_to_map = pcs_dt_node_to_map, | ||
268 | .dt_free_map = pcs_dt_free_map, | ||
269 | }; | ||
270 | |||
271 | static int pcs_get_functions_count(struct pinctrl_dev *pctldev) | ||
272 | { | ||
273 | struct pcs_device *pcs; | ||
274 | |||
275 | pcs = pinctrl_dev_get_drvdata(pctldev); | ||
276 | |||
277 | return pcs->nfuncs; | ||
278 | } | ||
279 | |||
280 | static const char *pcs_get_function_name(struct pinctrl_dev *pctldev, | ||
281 | unsigned fselector) | ||
282 | { | ||
283 | struct pcs_device *pcs; | ||
284 | struct pcs_function *func; | ||
285 | |||
286 | pcs = pinctrl_dev_get_drvdata(pctldev); | ||
287 | func = radix_tree_lookup(&pcs->ftree, fselector); | ||
288 | if (!func) { | ||
289 | dev_err(pcs->dev, "%s could not find function%i\n", | ||
290 | __func__, fselector); | ||
291 | return NULL; | ||
292 | } | ||
293 | |||
294 | return func->name; | ||
295 | } | ||
296 | |||
297 | static int pcs_get_function_groups(struct pinctrl_dev *pctldev, | ||
298 | unsigned fselector, | ||
299 | const char * const **groups, | ||
300 | unsigned * const ngroups) | ||
301 | { | ||
302 | struct pcs_device *pcs; | ||
303 | struct pcs_function *func; | ||
304 | |||
305 | pcs = pinctrl_dev_get_drvdata(pctldev); | ||
306 | func = radix_tree_lookup(&pcs->ftree, fselector); | ||
307 | if (!func) { | ||
308 | dev_err(pcs->dev, "%s could not find function%i\n", | ||
309 | __func__, fselector); | ||
310 | return -EINVAL; | ||
311 | } | ||
312 | *groups = func->pgnames; | ||
313 | *ngroups = func->npgnames; | ||
314 | |||
315 | return 0; | ||
316 | } | ||
317 | |||
318 | static int pcs_enable(struct pinctrl_dev *pctldev, unsigned fselector, | ||
319 | unsigned group) | ||
320 | { | ||
321 | struct pcs_device *pcs; | ||
322 | struct pcs_function *func; | ||
323 | int i; | ||
324 | |||
325 | pcs = pinctrl_dev_get_drvdata(pctldev); | ||
326 | func = radix_tree_lookup(&pcs->ftree, fselector); | ||
327 | if (!func) | ||
328 | return -EINVAL; | ||
329 | |||
330 | dev_dbg(pcs->dev, "enabling %s function%i\n", | ||
331 | func->name, fselector); | ||
332 | |||
333 | for (i = 0; i < func->nvals; i++) { | ||
334 | struct pcs_func_vals *vals; | ||
335 | unsigned val; | ||
336 | |||
337 | vals = &func->vals[i]; | ||
338 | val = pcs->read(vals->reg); | ||
339 | val &= ~pcs->fmask; | ||
340 | val |= vals->val; | ||
341 | pcs->write(val, vals->reg); | ||
342 | } | ||
343 | |||
344 | return 0; | ||
345 | } | ||
346 | |||
347 | static void pcs_disable(struct pinctrl_dev *pctldev, unsigned fselector, | ||
348 | unsigned group) | ||
349 | { | ||
350 | struct pcs_device *pcs; | ||
351 | struct pcs_function *func; | ||
352 | int i; | ||
353 | |||
354 | pcs = pinctrl_dev_get_drvdata(pctldev); | ||
355 | func = radix_tree_lookup(&pcs->ftree, fselector); | ||
356 | if (!func) { | ||
357 | dev_err(pcs->dev, "%s could not find function%i\n", | ||
358 | __func__, fselector); | ||
359 | return; | ||
360 | } | ||
361 | |||
362 | /* | ||
363 | * Ignore disable if function-off is not specified. Some hardware | ||
364 | * does not have clearly defined disable function. For pin specific | ||
365 | * off modes, you can use alternate named states as described in | ||
366 | * pinctrl-bindings.txt. | ||
367 | */ | ||
368 | if (pcs->foff == PCS_OFF_DISABLED) { | ||
369 | dev_dbg(pcs->dev, "ignoring disable for %s function%i\n", | ||
370 | func->name, fselector); | ||
371 | return; | ||
372 | } | ||
373 | |||
374 | dev_dbg(pcs->dev, "disabling function%i %s\n", | ||
375 | fselector, func->name); | ||
376 | |||
377 | for (i = 0; i < func->nvals; i++) { | ||
378 | struct pcs_func_vals *vals; | ||
379 | unsigned val; | ||
380 | |||
381 | vals = &func->vals[i]; | ||
382 | val = pcs->read(vals->reg); | ||
383 | val &= ~pcs->fmask; | ||
384 | val |= pcs->foff << pcs->fshift; | ||
385 | pcs->write(val, vals->reg); | ||
386 | } | ||
387 | } | ||
388 | |||
389 | static int pcs_request_gpio(struct pinctrl_dev *pctldev, | ||
390 | struct pinctrl_gpio_range *range, unsigned offset) | ||
391 | { | ||
392 | return -ENOTSUPP; | ||
393 | } | ||
394 | |||
395 | static struct pinmux_ops pcs_pinmux_ops = { | ||
396 | .get_functions_count = pcs_get_functions_count, | ||
397 | .get_function_name = pcs_get_function_name, | ||
398 | .get_function_groups = pcs_get_function_groups, | ||
399 | .enable = pcs_enable, | ||
400 | .disable = pcs_disable, | ||
401 | .gpio_request_enable = pcs_request_gpio, | ||
402 | }; | ||
403 | |||
404 | static int pcs_pinconf_get(struct pinctrl_dev *pctldev, | ||
405 | unsigned pin, unsigned long *config) | ||
406 | { | ||
407 | return -ENOTSUPP; | ||
408 | } | ||
409 | |||
410 | static int pcs_pinconf_set(struct pinctrl_dev *pctldev, | ||
411 | unsigned pin, unsigned long config) | ||
412 | { | ||
413 | return -ENOTSUPP; | ||
414 | } | ||
415 | |||
416 | static int pcs_pinconf_group_get(struct pinctrl_dev *pctldev, | ||
417 | unsigned group, unsigned long *config) | ||
418 | { | ||
419 | return -ENOTSUPP; | ||
420 | } | ||
421 | |||
422 | static int pcs_pinconf_group_set(struct pinctrl_dev *pctldev, | ||
423 | unsigned group, unsigned long config) | ||
424 | { | ||
425 | return -ENOTSUPP; | ||
426 | } | ||
427 | |||
428 | static void pcs_pinconf_dbg_show(struct pinctrl_dev *pctldev, | ||
429 | struct seq_file *s, unsigned offset) | ||
430 | { | ||
431 | } | ||
432 | |||
433 | static void pcs_pinconf_group_dbg_show(struct pinctrl_dev *pctldev, | ||
434 | struct seq_file *s, unsigned selector) | ||
435 | { | ||
436 | } | ||
437 | |||
438 | static struct pinconf_ops pcs_pinconf_ops = { | ||
439 | .pin_config_get = pcs_pinconf_get, | ||
440 | .pin_config_set = pcs_pinconf_set, | ||
441 | .pin_config_group_get = pcs_pinconf_group_get, | ||
442 | .pin_config_group_set = pcs_pinconf_group_set, | ||
443 | .pin_config_dbg_show = pcs_pinconf_dbg_show, | ||
444 | .pin_config_group_dbg_show = pcs_pinconf_group_dbg_show, | ||
445 | }; | ||
446 | |||
447 | /** | ||
448 | * pcs_add_pin() - add a pin to the static per controller pin array | ||
449 | * @pcs: pcs driver instance | ||
450 | * @offset: register offset from base | ||
451 | */ | ||
452 | static int __devinit pcs_add_pin(struct pcs_device *pcs, unsigned offset) | ||
453 | { | ||
454 | struct pinctrl_pin_desc *pin; | ||
455 | struct pcs_name *pn; | ||
456 | int i; | ||
457 | |||
458 | i = pcs->pins.cur; | ||
459 | if (i >= pcs->desc.npins) { | ||
460 | dev_err(pcs->dev, "too many pins, max %i\n", | ||
461 | pcs->desc.npins); | ||
462 | return -ENOMEM; | ||
463 | } | ||
464 | |||
465 | pin = &pcs->pins.pa[i]; | ||
466 | pn = &pcs->names[i]; | ||
467 | sprintf(pn->name, "%lx", | ||
468 | (unsigned long)pcs->res->start + offset); | ||
469 | pin->name = pn->name; | ||
470 | pin->number = i; | ||
471 | pcs->pins.cur++; | ||
472 | |||
473 | return i; | ||
474 | } | ||
475 | |||
476 | /** | ||
477 | * pcs_allocate_pin_table() - adds all the pins for the pinctrl driver | ||
478 | * @pcs: pcs driver instance | ||
479 | * | ||
480 | * In case of errors, resources are freed in pcs_free_resources. | ||
481 | * | ||
482 | * If your hardware needs holes in the address space, then just set | ||
483 | * up multiple driver instances. | ||
484 | */ | ||
485 | static int __devinit pcs_allocate_pin_table(struct pcs_device *pcs) | ||
486 | { | ||
487 | int mux_bytes, nr_pins, i; | ||
488 | |||
489 | mux_bytes = pcs->width / BITS_PER_BYTE; | ||
490 | nr_pins = pcs->size / mux_bytes; | ||
491 | |||
492 | dev_dbg(pcs->dev, "allocating %i pins\n", nr_pins); | ||
493 | pcs->pins.pa = devm_kzalloc(pcs->dev, | ||
494 | sizeof(*pcs->pins.pa) * nr_pins, | ||
495 | GFP_KERNEL); | ||
496 | if (!pcs->pins.pa) | ||
497 | return -ENOMEM; | ||
498 | |||
499 | pcs->names = devm_kzalloc(pcs->dev, | ||
500 | sizeof(struct pcs_name) * nr_pins, | ||
501 | GFP_KERNEL); | ||
502 | if (!pcs->names) | ||
503 | return -ENOMEM; | ||
504 | |||
505 | pcs->desc.pins = pcs->pins.pa; | ||
506 | pcs->desc.npins = nr_pins; | ||
507 | |||
508 | for (i = 0; i < pcs->desc.npins; i++) { | ||
509 | unsigned offset; | ||
510 | int res; | ||
511 | |||
512 | offset = i * mux_bytes; | ||
513 | res = pcs_add_pin(pcs, offset); | ||
514 | if (res < 0) { | ||
515 | dev_err(pcs->dev, "error adding pins: %i\n", res); | ||
516 | return res; | ||
517 | } | ||
518 | } | ||
519 | |||
520 | return 0; | ||
521 | } | ||
522 | |||
523 | /** | ||
524 | * pcs_add_function() - adds a new function to the function list | ||
525 | * @pcs: pcs driver instance | ||
526 | * @np: device node of the mux entry | ||
527 | * @name: name of the function | ||
528 | * @vals: array of mux register value pairs used by the function | ||
529 | * @nvals: number of mux register value pairs | ||
530 | * @pgnames: array of pingroup names for the function | ||
531 | * @npgnames: number of pingroup names | ||
532 | */ | ||
533 | static struct pcs_function *pcs_add_function(struct pcs_device *pcs, | ||
534 | struct device_node *np, | ||
535 | const char *name, | ||
536 | struct pcs_func_vals *vals, | ||
537 | unsigned nvals, | ||
538 | const char **pgnames, | ||
539 | unsigned npgnames) | ||
540 | { | ||
541 | struct pcs_function *function; | ||
542 | |||
543 | function = devm_kzalloc(pcs->dev, sizeof(*function), GFP_KERNEL); | ||
544 | if (!function) | ||
545 | return NULL; | ||
546 | |||
547 | function->name = name; | ||
548 | function->vals = vals; | ||
549 | function->nvals = nvals; | ||
550 | function->pgnames = pgnames; | ||
551 | function->npgnames = npgnames; | ||
552 | |||
553 | mutex_lock(&pcs->mutex); | ||
554 | list_add_tail(&function->node, &pcs->functions); | ||
555 | radix_tree_insert(&pcs->ftree, pcs->nfuncs, function); | ||
556 | pcs->nfuncs++; | ||
557 | mutex_unlock(&pcs->mutex); | ||
558 | |||
559 | return function; | ||
560 | } | ||
561 | |||
562 | static void pcs_remove_function(struct pcs_device *pcs, | ||
563 | struct pcs_function *function) | ||
564 | { | ||
565 | int i; | ||
566 | |||
567 | mutex_lock(&pcs->mutex); | ||
568 | for (i = 0; i < pcs->nfuncs; i++) { | ||
569 | struct pcs_function *found; | ||
570 | |||
571 | found = radix_tree_lookup(&pcs->ftree, i); | ||
572 | if (found == function) | ||
573 | radix_tree_delete(&pcs->ftree, i); | ||
574 | } | ||
575 | list_del(&function->node); | ||
576 | mutex_unlock(&pcs->mutex); | ||
577 | } | ||
578 | |||
579 | /** | ||
580 | * pcs_add_pingroup() - add a pingroup to the pingroup list | ||
581 | * @pcs: pcs driver instance | ||
582 | * @np: device node of the mux entry | ||
583 | * @name: name of the pingroup | ||
584 | * @gpins: array of the pins that belong to the group | ||
585 | * @ngpins: number of pins in the group | ||
586 | */ | ||
587 | static int pcs_add_pingroup(struct pcs_device *pcs, | ||
588 | struct device_node *np, | ||
589 | const char *name, | ||
590 | int *gpins, | ||
591 | int ngpins) | ||
592 | { | ||
593 | struct pcs_pingroup *pingroup; | ||
594 | |||
595 | pingroup = devm_kzalloc(pcs->dev, sizeof(*pingroup), GFP_KERNEL); | ||
596 | if (!pingroup) | ||
597 | return -ENOMEM; | ||
598 | |||
599 | pingroup->name = name; | ||
600 | pingroup->np = np; | ||
601 | pingroup->gpins = gpins; | ||
602 | pingroup->ngpins = ngpins; | ||
603 | |||
604 | mutex_lock(&pcs->mutex); | ||
605 | list_add_tail(&pingroup->node, &pcs->pingroups); | ||
606 | radix_tree_insert(&pcs->pgtree, pcs->ngroups, pingroup); | ||
607 | pcs->ngroups++; | ||
608 | mutex_unlock(&pcs->mutex); | ||
609 | |||
610 | return 0; | ||
611 | } | ||
612 | |||
613 | /** | ||
614 | * pcs_get_pin_by_offset() - get a pin index based on the register offset | ||
615 | * @pcs: pcs driver instance | ||
616 | * @offset: register offset from the base | ||
617 | * | ||
618 | * Note that this is OK as long as the pins are in a static array. | ||
619 | */ | ||
620 | static int pcs_get_pin_by_offset(struct pcs_device *pcs, unsigned offset) | ||
621 | { | ||
622 | unsigned index; | ||
623 | |||
624 | if (offset >= pcs->size) { | ||
625 | dev_err(pcs->dev, "mux offset out of range: 0x%x (0x%x)\n", | ||
626 | offset, pcs->size); | ||
627 | return -EINVAL; | ||
628 | } | ||
629 | |||
630 | index = offset / (pcs->width / BITS_PER_BYTE); | ||
631 | |||
632 | return index; | ||
633 | } | ||
634 | |||
635 | /** | ||
636 | * smux_parse_one_pinctrl_entry() - parses a device tree mux entry | ||
637 | * @pcs: pinctrl driver instance | ||
638 | * @np: device node of the mux entry | ||
639 | * @map: map entry | ||
640 | * @pgnames: pingroup names | ||
641 | * | ||
642 | * Note that this binding currently supports only sets of one register + value. | ||
643 | * | ||
644 | * Also note that this driver tries to avoid understanding pin and function | ||
645 | * names because of the extra bloat they would cause especially in the case of | ||
646 | * a large number of pins. This driver just sets what is specified for the board | ||
647 | * in the .dts file. Further user space debugging tools can be developed to | ||
648 | * decipher the pin and function names using debugfs. | ||
649 | * | ||
650 | * If you are concerned about the boot time, set up the static pins in | ||
651 | * the bootloader, and only set up selected pins as device tree entries. | ||
652 | */ | ||
653 | static int pcs_parse_one_pinctrl_entry(struct pcs_device *pcs, | ||
654 | struct device_node *np, | ||
655 | struct pinctrl_map **map, | ||
656 | const char **pgnames) | ||
657 | { | ||
658 | struct pcs_func_vals *vals; | ||
659 | const __be32 *mux; | ||
660 | int size, rows, *pins, index = 0, found = 0, res = -ENOMEM; | ||
661 | struct pcs_function *function; | ||
662 | |||
663 | mux = of_get_property(np, PCS_MUX_NAME, &size); | ||
664 | if ((!mux) || (size < sizeof(*mux) * 2)) { | ||
665 | dev_err(pcs->dev, "bad data for mux %s\n", | ||
666 | np->name); | ||
667 | return -EINVAL; | ||
668 | } | ||
669 | |||
670 | size /= sizeof(*mux); /* Number of elements in array */ | ||
671 | rows = size / 2; /* Each row is a key value pair */ | ||
672 | |||
673 | vals = devm_kzalloc(pcs->dev, sizeof(*vals) * rows, GFP_KERNEL); | ||
674 | if (!vals) | ||
675 | return -ENOMEM; | ||
676 | |||
677 | pins = devm_kzalloc(pcs->dev, sizeof(*pins) * rows, GFP_KERNEL); | ||
678 | if (!pins) | ||
679 | goto free_vals; | ||
680 | |||
681 | while (index < size) { | ||
682 | unsigned offset, val; | ||
683 | int pin; | ||
684 | |||
685 | offset = be32_to_cpup(mux + index++); | ||
686 | val = be32_to_cpup(mux + index++); | ||
687 | vals[found].reg = pcs->base + offset; | ||
688 | vals[found].val = val; | ||
689 | |||
690 | pin = pcs_get_pin_by_offset(pcs, offset); | ||
691 | if (pin < 0) { | ||
692 | dev_err(pcs->dev, | ||
693 | "could not add functions for %s %ux\n", | ||
694 | np->name, offset); | ||
695 | break; | ||
696 | } | ||
697 | pins[found++] = pin; | ||
698 | } | ||
699 | |||
700 | pgnames[0] = np->name; | ||
701 | function = pcs_add_function(pcs, np, np->name, vals, found, pgnames, 1); | ||
702 | if (!function) | ||
703 | goto free_pins; | ||
704 | |||
705 | res = pcs_add_pingroup(pcs, np, np->name, pins, found); | ||
706 | if (res < 0) | ||
707 | goto free_function; | ||
708 | |||
709 | (*map)->type = PIN_MAP_TYPE_MUX_GROUP; | ||
710 | (*map)->data.mux.group = np->name; | ||
711 | (*map)->data.mux.function = np->name; | ||
712 | |||
713 | return 0; | ||
714 | |||
715 | free_function: | ||
716 | pcs_remove_function(pcs, function); | ||
717 | |||
718 | free_pins: | ||
719 | devm_kfree(pcs->dev, pins); | ||
720 | |||
721 | free_vals: | ||
722 | devm_kfree(pcs->dev, vals); | ||
723 | |||
724 | return res; | ||
725 | } | ||
726 | /** | ||
727 | * pcs_dt_node_to_map() - allocates and parses pinctrl maps | ||
728 | * @pctldev: pinctrl instance | ||
729 | * @np_config: device tree pinmux entry | ||
730 | * @map: array of map entries | ||
731 | * @num_maps: number of maps | ||
732 | */ | ||
733 | static int pcs_dt_node_to_map(struct pinctrl_dev *pctldev, | ||
734 | struct device_node *np_config, | ||
735 | struct pinctrl_map **map, unsigned *num_maps) | ||
736 | { | ||
737 | struct pcs_device *pcs; | ||
738 | const char **pgnames; | ||
739 | int ret; | ||
740 | |||
741 | pcs = pinctrl_dev_get_drvdata(pctldev); | ||
742 | |||
743 | *map = devm_kzalloc(pcs->dev, sizeof(**map), GFP_KERNEL); | ||
744 | if (!map) | ||
745 | return -ENOMEM; | ||
746 | |||
747 | *num_maps = 0; | ||
748 | |||
749 | pgnames = devm_kzalloc(pcs->dev, sizeof(*pgnames), GFP_KERNEL); | ||
750 | if (!pgnames) { | ||
751 | ret = -ENOMEM; | ||
752 | goto free_map; | ||
753 | } | ||
754 | |||
755 | ret = pcs_parse_one_pinctrl_entry(pcs, np_config, map, pgnames); | ||
756 | if (ret < 0) { | ||
757 | dev_err(pcs->dev, "no pins entries for %s\n", | ||
758 | np_config->name); | ||
759 | goto free_pgnames; | ||
760 | } | ||
761 | *num_maps = 1; | ||
762 | |||
763 | return 0; | ||
764 | |||
765 | free_pgnames: | ||
766 | devm_kfree(pcs->dev, pgnames); | ||
767 | free_map: | ||
768 | devm_kfree(pcs->dev, *map); | ||
769 | |||
770 | return ret; | ||
771 | } | ||
772 | |||
773 | /** | ||
774 | * pcs_free_funcs() - free memory used by functions | ||
775 | * @pcs: pcs driver instance | ||
776 | */ | ||
777 | static void pcs_free_funcs(struct pcs_device *pcs) | ||
778 | { | ||
779 | struct list_head *pos, *tmp; | ||
780 | int i; | ||
781 | |||
782 | mutex_lock(&pcs->mutex); | ||
783 | for (i = 0; i < pcs->nfuncs; i++) { | ||
784 | struct pcs_function *func; | ||
785 | |||
786 | func = radix_tree_lookup(&pcs->ftree, i); | ||
787 | if (!func) | ||
788 | continue; | ||
789 | radix_tree_delete(&pcs->ftree, i); | ||
790 | } | ||
791 | list_for_each_safe(pos, tmp, &pcs->functions) { | ||
792 | struct pcs_function *function; | ||
793 | |||
794 | function = list_entry(pos, struct pcs_function, node); | ||
795 | list_del(&function->node); | ||
796 | } | ||
797 | mutex_unlock(&pcs->mutex); | ||
798 | } | ||
799 | |||
800 | /** | ||
801 | * pcs_free_pingroups() - free memory used by pingroups | ||
802 | * @pcs: pcs driver instance | ||
803 | */ | ||
804 | static void pcs_free_pingroups(struct pcs_device *pcs) | ||
805 | { | ||
806 | struct list_head *pos, *tmp; | ||
807 | int i; | ||
808 | |||
809 | mutex_lock(&pcs->mutex); | ||
810 | for (i = 0; i < pcs->ngroups; i++) { | ||
811 | struct pcs_pingroup *pingroup; | ||
812 | |||
813 | pingroup = radix_tree_lookup(&pcs->pgtree, i); | ||
814 | if (!pingroup) | ||
815 | continue; | ||
816 | radix_tree_delete(&pcs->pgtree, i); | ||
817 | } | ||
818 | list_for_each_safe(pos, tmp, &pcs->pingroups) { | ||
819 | struct pcs_pingroup *pingroup; | ||
820 | |||
821 | pingroup = list_entry(pos, struct pcs_pingroup, node); | ||
822 | list_del(&pingroup->node); | ||
823 | } | ||
824 | mutex_unlock(&pcs->mutex); | ||
825 | } | ||
826 | |||
827 | /** | ||
828 | * pcs_free_resources() - free memory used by this driver | ||
829 | * @pcs: pcs driver instance | ||
830 | */ | ||
831 | static void pcs_free_resources(struct pcs_device *pcs) | ||
832 | { | ||
833 | if (pcs->pctl) | ||
834 | pinctrl_unregister(pcs->pctl); | ||
835 | |||
836 | pcs_free_funcs(pcs); | ||
837 | pcs_free_pingroups(pcs); | ||
838 | } | ||
839 | |||
840 | #define PCS_GET_PROP_U32(name, reg, err) \ | ||
841 | do { \ | ||
842 | ret = of_property_read_u32(np, name, reg); \ | ||
843 | if (ret) { \ | ||
844 | dev_err(pcs->dev, err); \ | ||
845 | return ret; \ | ||
846 | } \ | ||
847 | } while (0); | ||
848 | |||
849 | static struct of_device_id pcs_of_match[]; | ||
850 | |||
851 | static int __devinit pcs_probe(struct platform_device *pdev) | ||
852 | { | ||
853 | struct device_node *np = pdev->dev.of_node; | ||
854 | const struct of_device_id *match; | ||
855 | struct resource *res; | ||
856 | struct pcs_device *pcs; | ||
857 | int ret; | ||
858 | |||
859 | match = of_match_device(pcs_of_match, &pdev->dev); | ||
860 | if (!match) | ||
861 | return -EINVAL; | ||
862 | |||
863 | pcs = devm_kzalloc(&pdev->dev, sizeof(*pcs), GFP_KERNEL); | ||
864 | if (!pcs) { | ||
865 | dev_err(&pdev->dev, "could not allocate\n"); | ||
866 | return -ENOMEM; | ||
867 | } | ||
868 | pcs->dev = &pdev->dev; | ||
869 | mutex_init(&pcs->mutex); | ||
870 | INIT_LIST_HEAD(&pcs->pingroups); | ||
871 | INIT_LIST_HEAD(&pcs->functions); | ||
872 | |||
873 | PCS_GET_PROP_U32("pinctrl-single,register-width", &pcs->width, | ||
874 | "register width not specified\n"); | ||
875 | |||
876 | PCS_GET_PROP_U32("pinctrl-single,function-mask", &pcs->fmask, | ||
877 | "function register mask not specified\n"); | ||
878 | pcs->fshift = ffs(pcs->fmask) - 1; | ||
879 | pcs->fmax = pcs->fmask >> pcs->fshift; | ||
880 | |||
881 | ret = of_property_read_u32(np, "pinctrl-single,function-off", | ||
882 | &pcs->foff); | ||
883 | if (ret) | ||
884 | pcs->foff = PCS_OFF_DISABLED; | ||
885 | |||
886 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
887 | if (!res) { | ||
888 | dev_err(pcs->dev, "could not get resource\n"); | ||
889 | return -ENODEV; | ||
890 | } | ||
891 | |||
892 | pcs->res = devm_request_mem_region(pcs->dev, res->start, | ||
893 | resource_size(res), DRIVER_NAME); | ||
894 | if (!pcs->res) { | ||
895 | dev_err(pcs->dev, "could not get mem_region\n"); | ||
896 | return -EBUSY; | ||
897 | } | ||
898 | |||
899 | pcs->size = resource_size(pcs->res); | ||
900 | pcs->base = devm_ioremap(pcs->dev, pcs->res->start, pcs->size); | ||
901 | if (!pcs->base) { | ||
902 | dev_err(pcs->dev, "could not ioremap\n"); | ||
903 | return -ENODEV; | ||
904 | } | ||
905 | |||
906 | INIT_RADIX_TREE(&pcs->pgtree, GFP_KERNEL); | ||
907 | INIT_RADIX_TREE(&pcs->ftree, GFP_KERNEL); | ||
908 | platform_set_drvdata(pdev, pcs); | ||
909 | |||
910 | switch (pcs->width) { | ||
911 | case 8: | ||
912 | pcs->read = pcs_readb; | ||
913 | pcs->write = pcs_writeb; | ||
914 | break; | ||
915 | case 16: | ||
916 | pcs->read = pcs_readw; | ||
917 | pcs->write = pcs_writew; | ||
918 | break; | ||
919 | case 32: | ||
920 | pcs->read = pcs_readl; | ||
921 | pcs->write = pcs_writel; | ||
922 | break; | ||
923 | default: | ||
924 | break; | ||
925 | } | ||
926 | |||
927 | pcs->desc.name = DRIVER_NAME; | ||
928 | pcs->desc.pctlops = &pcs_pinctrl_ops; | ||
929 | pcs->desc.pmxops = &pcs_pinmux_ops; | ||
930 | pcs->desc.confops = &pcs_pinconf_ops; | ||
931 | pcs->desc.owner = THIS_MODULE; | ||
932 | |||
933 | ret = pcs_allocate_pin_table(pcs); | ||
934 | if (ret < 0) | ||
935 | goto free; | ||
936 | |||
937 | pcs->pctl = pinctrl_register(&pcs->desc, pcs->dev, pcs); | ||
938 | if (!pcs->pctl) { | ||
939 | dev_err(pcs->dev, "could not register single pinctrl driver\n"); | ||
940 | ret = -EINVAL; | ||
941 | goto free; | ||
942 | } | ||
943 | |||
944 | dev_info(pcs->dev, "%i pins at pa %p size %u\n", | ||
945 | pcs->desc.npins, pcs->base, pcs->size); | ||
946 | |||
947 | return 0; | ||
948 | |||
949 | free: | ||
950 | pcs_free_resources(pcs); | ||
951 | |||
952 | return ret; | ||
953 | } | ||
954 | |||
955 | static int __devexit pcs_remove(struct platform_device *pdev) | ||
956 | { | ||
957 | struct pcs_device *pcs = platform_get_drvdata(pdev); | ||
958 | |||
959 | if (!pcs) | ||
960 | return 0; | ||
961 | |||
962 | pcs_free_resources(pcs); | ||
963 | |||
964 | return 0; | ||
965 | } | ||
966 | |||
967 | static struct of_device_id pcs_of_match[] __devinitdata = { | ||
968 | { .compatible = DRIVER_NAME, }, | ||
969 | { }, | ||
970 | }; | ||
971 | MODULE_DEVICE_TABLE(of, pcs_of_match); | ||
972 | |||
973 | static struct platform_driver pcs_driver = { | ||
974 | .probe = pcs_probe, | ||
975 | .remove = __devexit_p(pcs_remove), | ||
976 | .driver = { | ||
977 | .owner = THIS_MODULE, | ||
978 | .name = DRIVER_NAME, | ||
979 | .of_match_table = pcs_of_match, | ||
980 | }, | ||
981 | }; | ||
982 | |||
983 | module_platform_driver(pcs_driver); | ||
984 | |||
985 | MODULE_AUTHOR("Tony Lindgren <tony@atomide.com>"); | ||
986 | MODULE_DESCRIPTION("One-register-per-pin type device tree based pinctrl driver"); | ||
987 | MODULE_LICENSE("GPL v2"); | ||