Commit aeec56e331c6d2750de02ef34b305338305ca690
Committed by
Linus Torvalds
1 parent
d0f744c8cb
Exists in
master
and in
39 other branches
gpio: add driver for basic memory-mapped GPIO controllers
The basic GPIO controllers may be found in various on-board FPGA and ASIC solutions that are used to control board's switches, LEDs, chip-selects, Ethernet/USB PHY power, etc. These controllers may not provide any means of pin setup (in/out/open drain). The driver supports: - 8/16/32/64 bits registers; - GPIO controllers with clear/set registers; - GPIO controllers with a single "data" register; - Big endian bits/GPIOs ordering (mostly used on PowerPC). Signed-off-by: Anton Vorontsov <cbouatmailru@gmail.com> Reviewed-by: Mark Brown <broonie@opensource.wolfsonmicro.com> Cc: David Brownell <david-b@pacbell.net> Cc: Samuel Ortiz <sameo@linux.intel.com>, Cc: Alan Cox <alan@lxorguk.ukuu.org.uk> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Showing 4 changed files with 323 additions and 0 deletions Side-by-side Diff
drivers/gpio/Kconfig
... | ... | @@ -70,6 +70,11 @@ |
70 | 70 | |
71 | 71 | comment "Memory mapped GPIO expanders:" |
72 | 72 | |
73 | +config GPIO_BASIC_MMIO | |
74 | + tristate "Basic memory-mapped GPIO controllers support" | |
75 | + help | |
76 | + Say yes here to support basic memory-mapped GPIO controllers. | |
77 | + | |
73 | 78 | config GPIO_IT8761E |
74 | 79 | tristate "IT8761E GPIO support" |
75 | 80 | depends on GPIOLIB |
drivers/gpio/Makefile
... | ... | @@ -10,6 +10,7 @@ |
10 | 10 | |
11 | 11 | obj-$(CONFIG_GPIO_ADP5520) += adp5520-gpio.o |
12 | 12 | obj-$(CONFIG_GPIO_ADP5588) += adp5588-gpio.o |
13 | +obj-$(CONFIG_GPIO_BASIC_MMIO) += basic_mmio_gpio.o | |
13 | 14 | obj-$(CONFIG_GPIO_LANGWELL) += langwell_gpio.o |
14 | 15 | obj-$(CONFIG_GPIO_MAX730X) += max730x.o |
15 | 16 | obj-$(CONFIG_GPIO_MAX7300) += max7300.o |
drivers/gpio/basic_mmio_gpio.c
1 | +/* | |
2 | + * Driver for basic memory-mapped GPIO controllers. | |
3 | + * | |
4 | + * Copyright 2008 MontaVista Software, Inc. | |
5 | + * Copyright 2008,2010 Anton Vorontsov <cbouatmailru@gmail.com> | |
6 | + * | |
7 | + * This program is free software; you can redistribute it and/or modify it | |
8 | + * under the terms of the GNU General Public License as published by the | |
9 | + * Free Software Foundation; either version 2 of the License, or (at your | |
10 | + * option) any later version. | |
11 | + * | |
12 | + * ....``.```~~~~````.`.`.`.`.```````'',,,.........`````......`....... | |
13 | + * ...`` ```````.. | |
14 | + * ..The simplest form of a GPIO controller that the driver supports is`` | |
15 | + * `.just a single "data" register, where GPIO state can be read and/or ` | |
16 | + * `,..written. ,,..``~~~~ .....``.`.`.~~.```.`.........``````.``````` | |
17 | + * ````````` | |
18 | + ___ | |
19 | +_/~~|___/~| . ```~~~~~~ ___/___\___ ,~.`.`.`.`````.~~...,,,,... | |
20 | +__________|~$@~~~ %~ /o*o*o*o*o*o\ .. Implementing such a GPIO . | |
21 | +o ` ~~~~\___/~~~~ ` controller in FPGA is ,.` | |
22 | + `....trivial..'~`.```.``` | |
23 | + * ``````` | |
24 | + * .```````~~~~`..`.``.``. | |
25 | + * . The driver supports `... ,..```.`~~~```````````````....````.``,, | |
26 | + * . big-endian notation, just`. .. A bit more sophisticated controllers , | |
27 | + * . register the device with -be`. .with a pair of set/clear-bit registers , | |
28 | + * `.. suffix. ```~~`````....`.` . affecting the data register and the .` | |
29 | + * ``.`.``...``` ```.. output pins are also supported.` | |
30 | + * ^^ `````.`````````.,``~``~``~~`````` | |
31 | + * . ^^ | |
32 | + * ,..`.`.`...````````````......`.`.`.`.`.`..`.`.`.. | |
33 | + * .. The expectation is that in at least some cases . ,-~~~-, | |
34 | + * .this will be used with roll-your-own ASIC/FPGA .` \ / | |
35 | + * .logic in Verilog or VHDL. ~~~`````````..`````~~` \ / | |
36 | + * ..````````......``````````` \o_ | |
37 | + * | | |
38 | + * ^^ / \ | |
39 | + * | |
40 | + * ...`````~~`.....``.`..........``````.`.``.```........``. | |
41 | + * ` 8, 16, 32 and 64 bits registers are supported, and``. | |
42 | + * . the number of GPIOs is determined by the width of ~ | |
43 | + * .. the registers. ,............```.`.`..`.`.~~~.`.`.`~ | |
44 | + * `.......````.``` | |
45 | + */ | |
46 | + | |
47 | +#include <linux/init.h> | |
48 | +#include <linux/bug.h> | |
49 | +#include <linux/kernel.h> | |
50 | +#include <linux/module.h> | |
51 | +#include <linux/spinlock.h> | |
52 | +#include <linux/compiler.h> | |
53 | +#include <linux/types.h> | |
54 | +#include <linux/errno.h> | |
55 | +#include <linux/log2.h> | |
56 | +#include <linux/ioport.h> | |
57 | +#include <linux/io.h> | |
58 | +#include <linux/gpio.h> | |
59 | +#include <linux/slab.h> | |
60 | +#include <linux/platform_device.h> | |
61 | +#include <linux/mod_devicetable.h> | |
62 | +#include <linux/basic_mmio_gpio.h> | |
63 | + | |
64 | +struct bgpio_chip { | |
65 | + struct gpio_chip gc; | |
66 | + void __iomem *reg_dat; | |
67 | + void __iomem *reg_set; | |
68 | + void __iomem *reg_clr; | |
69 | + | |
70 | + /* Number of bits (GPIOs): <register width> * 8. */ | |
71 | + int bits; | |
72 | + | |
73 | + /* | |
74 | + * Some GPIO controllers work with the big-endian bits notation, | |
75 | + * e.g. in a 8-bits register, GPIO7 is the least significant bit. | |
76 | + */ | |
77 | + int big_endian_bits; | |
78 | + | |
79 | + /* | |
80 | + * Used to lock bgpio_chip->data. Also, this is needed to keep | |
81 | + * shadowed and real data registers writes together. | |
82 | + */ | |
83 | + spinlock_t lock; | |
84 | + | |
85 | + /* Shadowed data register to clear/set bits safely. */ | |
86 | + unsigned long data; | |
87 | +}; | |
88 | + | |
89 | +static struct bgpio_chip *to_bgpio_chip(struct gpio_chip *gc) | |
90 | +{ | |
91 | + return container_of(gc, struct bgpio_chip, gc); | |
92 | +} | |
93 | + | |
94 | +static unsigned long bgpio_in(struct bgpio_chip *bgc) | |
95 | +{ | |
96 | + switch (bgc->bits) { | |
97 | + case 8: | |
98 | + return __raw_readb(bgc->reg_dat); | |
99 | + case 16: | |
100 | + return __raw_readw(bgc->reg_dat); | |
101 | + case 32: | |
102 | + return __raw_readl(bgc->reg_dat); | |
103 | +#if BITS_PER_LONG >= 64 | |
104 | + case 64: | |
105 | + return __raw_readq(bgc->reg_dat); | |
106 | +#endif | |
107 | + } | |
108 | + return -EINVAL; | |
109 | +} | |
110 | + | |
111 | +static void bgpio_out(struct bgpio_chip *bgc, void __iomem *reg, | |
112 | + unsigned long data) | |
113 | +{ | |
114 | + switch (bgc->bits) { | |
115 | + case 8: | |
116 | + __raw_writeb(data, reg); | |
117 | + return; | |
118 | + case 16: | |
119 | + __raw_writew(data, reg); | |
120 | + return; | |
121 | + case 32: | |
122 | + __raw_writel(data, reg); | |
123 | + return; | |
124 | +#if BITS_PER_LONG >= 64 | |
125 | + case 64: | |
126 | + __raw_writeq(data, reg); | |
127 | + return; | |
128 | +#endif | |
129 | + } | |
130 | +} | |
131 | + | |
132 | +static unsigned long bgpio_pin2mask(struct bgpio_chip *bgc, unsigned int pin) | |
133 | +{ | |
134 | + if (bgc->big_endian_bits) | |
135 | + return 1 << (bgc->bits - 1 - pin); | |
136 | + else | |
137 | + return 1 << pin; | |
138 | +} | |
139 | + | |
140 | +static int bgpio_get(struct gpio_chip *gc, unsigned int gpio) | |
141 | +{ | |
142 | + struct bgpio_chip *bgc = to_bgpio_chip(gc); | |
143 | + | |
144 | + return bgpio_in(bgc) & bgpio_pin2mask(bgc, gpio); | |
145 | +} | |
146 | + | |
147 | +static void bgpio_set(struct gpio_chip *gc, unsigned int gpio, int val) | |
148 | +{ | |
149 | + struct bgpio_chip *bgc = to_bgpio_chip(gc); | |
150 | + unsigned long mask = bgpio_pin2mask(bgc, gpio); | |
151 | + unsigned long flags; | |
152 | + | |
153 | + if (bgc->reg_set) { | |
154 | + if (val) | |
155 | + bgpio_out(bgc, bgc->reg_set, mask); | |
156 | + else | |
157 | + bgpio_out(bgc, bgc->reg_clr, mask); | |
158 | + return; | |
159 | + } | |
160 | + | |
161 | + spin_lock_irqsave(&bgc->lock, flags); | |
162 | + | |
163 | + if (val) | |
164 | + bgc->data |= mask; | |
165 | + else | |
166 | + bgc->data &= ~mask; | |
167 | + | |
168 | + bgpio_out(bgc, bgc->reg_dat, bgc->data); | |
169 | + | |
170 | + spin_unlock_irqrestore(&bgc->lock, flags); | |
171 | +} | |
172 | + | |
173 | +static int bgpio_dir_in(struct gpio_chip *gc, unsigned int gpio) | |
174 | +{ | |
175 | + return 0; | |
176 | +} | |
177 | + | |
178 | +static int bgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) | |
179 | +{ | |
180 | + bgpio_set(gc, gpio, val); | |
181 | + return 0; | |
182 | +} | |
183 | + | |
184 | +static int __devinit bgpio_probe(struct platform_device *pdev) | |
185 | +{ | |
186 | + const struct platform_device_id *platid = platform_get_device_id(pdev); | |
187 | + struct device *dev = &pdev->dev; | |
188 | + struct bgpio_pdata *pdata = dev_get_platdata(dev); | |
189 | + struct bgpio_chip *bgc; | |
190 | + struct resource *res_dat; | |
191 | + struct resource *res_set; | |
192 | + struct resource *res_clr; | |
193 | + resource_size_t dat_sz; | |
194 | + int bits; | |
195 | + int ret; | |
196 | + | |
197 | + res_dat = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dat"); | |
198 | + if (!res_dat) | |
199 | + return -EINVAL; | |
200 | + | |
201 | + dat_sz = resource_size(res_dat); | |
202 | + if (!is_power_of_2(dat_sz)) | |
203 | + return -EINVAL; | |
204 | + | |
205 | + bits = dat_sz * 8; | |
206 | + if (bits > BITS_PER_LONG) | |
207 | + return -EINVAL; | |
208 | + | |
209 | + bgc = devm_kzalloc(dev, sizeof(*bgc), GFP_KERNEL); | |
210 | + if (!bgc) | |
211 | + return -ENOMEM; | |
212 | + | |
213 | + bgc->reg_dat = devm_ioremap(dev, res_dat->start, dat_sz); | |
214 | + if (!bgc->reg_dat) | |
215 | + return -ENOMEM; | |
216 | + | |
217 | + res_set = platform_get_resource_byname(pdev, IORESOURCE_MEM, "set"); | |
218 | + res_clr = platform_get_resource_byname(pdev, IORESOURCE_MEM, "clr"); | |
219 | + if (res_set && res_clr) { | |
220 | + if (resource_size(res_set) != resource_size(res_clr) || | |
221 | + resource_size(res_set) != dat_sz) | |
222 | + return -EINVAL; | |
223 | + | |
224 | + bgc->reg_set = devm_ioremap(dev, res_set->start, dat_sz); | |
225 | + bgc->reg_clr = devm_ioremap(dev, res_clr->start, dat_sz); | |
226 | + if (!bgc->reg_set || !bgc->reg_clr) | |
227 | + return -ENOMEM; | |
228 | + } else if (res_set || res_clr) { | |
229 | + return -EINVAL; | |
230 | + } | |
231 | + | |
232 | + spin_lock_init(&bgc->lock); | |
233 | + | |
234 | + bgc->bits = bits; | |
235 | + bgc->big_endian_bits = !strcmp(platid->name, "basic-mmio-gpio-be"); | |
236 | + bgc->data = bgpio_in(bgc); | |
237 | + | |
238 | + bgc->gc.ngpio = bits; | |
239 | + bgc->gc.direction_input = bgpio_dir_in; | |
240 | + bgc->gc.direction_output = bgpio_dir_out; | |
241 | + bgc->gc.get = bgpio_get; | |
242 | + bgc->gc.set = bgpio_set; | |
243 | + bgc->gc.dev = dev; | |
244 | + bgc->gc.label = dev_name(dev); | |
245 | + | |
246 | + if (pdata) | |
247 | + bgc->gc.base = pdata->base; | |
248 | + else | |
249 | + bgc->gc.base = -1; | |
250 | + | |
251 | + dev_set_drvdata(dev, bgc); | |
252 | + | |
253 | + ret = gpiochip_add(&bgc->gc); | |
254 | + if (ret) | |
255 | + dev_err(dev, "gpiochip_add() failed: %d\n", ret); | |
256 | + | |
257 | + return ret; | |
258 | +} | |
259 | + | |
260 | +static int __devexit bgpio_remove(struct platform_device *pdev) | |
261 | +{ | |
262 | + struct bgpio_chip *bgc = dev_get_drvdata(&pdev->dev); | |
263 | + | |
264 | + return gpiochip_remove(&bgc->gc); | |
265 | +} | |
266 | + | |
267 | +static const struct platform_device_id bgpio_id_table[] = { | |
268 | + { "basic-mmio-gpio", }, | |
269 | + { "basic-mmio-gpio-be", }, | |
270 | + {}, | |
271 | +}; | |
272 | +MODULE_DEVICE_TABLE(platform, bgpio_id_table); | |
273 | + | |
274 | +static struct platform_driver bgpio_driver = { | |
275 | + .driver = { | |
276 | + .name = "basic-mmio-gpio", | |
277 | + }, | |
278 | + .id_table = bgpio_id_table, | |
279 | + .probe = bgpio_probe, | |
280 | + .remove = __devexit_p(bgpio_remove), | |
281 | +}; | |
282 | + | |
283 | +static int __init bgpio_init(void) | |
284 | +{ | |
285 | + return platform_driver_register(&bgpio_driver); | |
286 | +} | |
287 | +module_init(bgpio_init); | |
288 | + | |
289 | +static void __exit bgpio_exit(void) | |
290 | +{ | |
291 | + platform_driver_unregister(&bgpio_driver); | |
292 | +} | |
293 | +module_exit(bgpio_exit); | |
294 | + | |
295 | +MODULE_DESCRIPTION("Driver for basic memory-mapped GPIO controllers"); | |
296 | +MODULE_AUTHOR("Anton Vorontsov <cbouatmailru@gmail.com>"); | |
297 | +MODULE_LICENSE("GPL"); |
include/linux/basic_mmio_gpio.h
1 | +/* | |
2 | + * Basic memory-mapped GPIO controllers. | |
3 | + * | |
4 | + * Copyright 2008 MontaVista Software, Inc. | |
5 | + * Copyright 2008,2010 Anton Vorontsov <cbouatmailru@gmail.com> | |
6 | + * | |
7 | + * This program is free software; you can redistribute it and/or modify it | |
8 | + * under the terms of the GNU General Public License as published by the | |
9 | + * Free Software Foundation; either version 2 of the License, or (at your | |
10 | + * option) any later version. | |
11 | + */ | |
12 | + | |
13 | +#ifndef __BASIC_MMIO_GPIO_H | |
14 | +#define __BASIC_MMIO_GPIO_H | |
15 | + | |
16 | +struct bgpio_pdata { | |
17 | + int base; | |
18 | +}; | |
19 | + | |
20 | +#endif /* __BASIC_MMIO_GPIO_H */ |