diff options
author | Anton Vorontsov <cbouatmailru@gmail.com> | 2010-10-27 18:33:15 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-10-27 21:03:06 -0400 |
commit | aeec56e331c6d2750de02ef34b305338305ca690 (patch) | |
tree | 1a9df159e4408cf1f4d9ab8451ae803de6007126 /drivers/gpio | |
parent | d0f744c8cbd19a8d07eccb15bb08e6a29c4d5192 (diff) |
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>
Diffstat (limited to 'drivers/gpio')
-rw-r--r-- | drivers/gpio/Kconfig | 5 | ||||
-rw-r--r-- | drivers/gpio/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpio/basic_mmio_gpio.c | 297 |
3 files changed, 303 insertions, 0 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 510aa2054544..e47ef94be379 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig | |||
@@ -70,6 +70,11 @@ config GPIO_MAX730X | |||
70 | 70 | ||
71 | comment "Memory mapped GPIO expanders:" | 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 | config GPIO_IT8761E | 78 | config GPIO_IT8761E |
74 | tristate "IT8761E GPIO support" | 79 | tristate "IT8761E GPIO support" |
75 | depends on GPIOLIB | 80 | depends on GPIOLIB |
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index fc6019d93720..3ff0651fd173 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile | |||
@@ -10,6 +10,7 @@ obj-$(CONFIG_GPIOLIB) += gpiolib.o | |||
10 | 10 | ||
11 | obj-$(CONFIG_GPIO_ADP5520) += adp5520-gpio.o | 11 | obj-$(CONFIG_GPIO_ADP5520) += adp5520-gpio.o |
12 | obj-$(CONFIG_GPIO_ADP5588) += adp5588-gpio.o | 12 | obj-$(CONFIG_GPIO_ADP5588) += adp5588-gpio.o |
13 | obj-$(CONFIG_GPIO_BASIC_MMIO) += basic_mmio_gpio.o | ||
13 | obj-$(CONFIG_GPIO_LANGWELL) += langwell_gpio.o | 14 | obj-$(CONFIG_GPIO_LANGWELL) += langwell_gpio.o |
14 | obj-$(CONFIG_GPIO_MAX730X) += max730x.o | 15 | obj-$(CONFIG_GPIO_MAX730X) += max730x.o |
15 | obj-$(CONFIG_GPIO_MAX7300) += max7300.o | 16 | obj-$(CONFIG_GPIO_MAX7300) += max7300.o |
diff --git a/drivers/gpio/basic_mmio_gpio.c b/drivers/gpio/basic_mmio_gpio.c new file mode 100644 index 000000000000..3addea65894e --- /dev/null +++ b/drivers/gpio/basic_mmio_gpio.c | |||
@@ -0,0 +1,297 @@ | |||
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"); | ||