aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpio
diff options
context:
space:
mode:
authorSimon Guinot <simon.guinot@sequanux.org>2013-08-29 16:56:56 -0400
committerLinus Walleij <linus.walleij@linaro.org>2013-08-30 03:25:52 -0400
commit6c17aa0138a6c55364936bbaa35846e09a4db53b (patch)
treeddbe02fbee48e5cc0f058e9cda30a1c659e1df59 /drivers/gpio
parentc6641da12e0f5516b75386dccedf163bbfeaabe0 (diff)
gpio: add GPIO support for F71882FG and F71889F
This patch adds support for the GPIOs found on the Fintek super-I/O chips F71882FG and F71889F. A super-I/O is a legacy I/O controller embedded on x86 motherboards. It is used to connect the low-bandwidth devices. Among others functions the F71882FG/F71889F provides: a parallel port, two serial ports, a keyboard controller, an hardware monitoring controller and some GPIO pins. Note that this super-I/Os are embedded on some Atom-based LaCie NASes. The GPIOs are used to control the LEDs and the hard drive power. Changes since v3: - Use request_muxed_region to protect the I/O ports against concurrent accesses. Changes since v2: - Remove useless NULL setters for driver data. Changes since v1: - Enhance the commit message by describing what is a Super-I/O. - Use self-explanatory names for the GPIO register macros. - Add a comment to explain the platform device and driver registration. - Fix gpio_get when GPIO is configured in input mode. I only had the hardware to check this mode recently... Signed-off-by: Simon Guinot <simon.guinot@sequanux.org> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Diffstat (limited to 'drivers/gpio')
-rw-r--r--drivers/gpio/Kconfig10
-rw-r--r--drivers/gpio/Makefile1
-rw-r--r--drivers/gpio/gpio-f7188x.c469
3 files changed, 480 insertions, 0 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 4b7ba53e96db..349b16160ac9 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -146,6 +146,16 @@ config GPIO_MM_LANTIQ
146 (EBU) found on Lantiq SoCs. The gpios are output only as they are 146 (EBU) found on Lantiq SoCs. The gpios are output only as they are
147 created by attaching a 16bit latch to the bus. 147 created by attaching a 16bit latch to the bus.
148 148
149config GPIO_F7188X
150 tristate "F71882FG and F71889F GPIO support"
151 depends on X86
152 help
153 This option enables support for GPIOs found on Fintek Super-I/O
154 chips F71882FG and F71889F.
155
156 To compile this driver as a module, choose M here: the module will
157 be called f7188x-gpio.
158
149config GPIO_MPC5200 159config GPIO_MPC5200
150 def_bool y 160 def_bool y
151 depends on PPC_MPC52xx 161 depends on PPC_MPC52xx
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 156fd283945c..97438bf8434a 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -24,6 +24,7 @@ obj-$(CONFIG_GPIO_DA9055) += gpio-da9055.o
24obj-$(CONFIG_ARCH_DAVINCI) += gpio-davinci.o 24obj-$(CONFIG_ARCH_DAVINCI) += gpio-davinci.o
25obj-$(CONFIG_GPIO_EM) += gpio-em.o 25obj-$(CONFIG_GPIO_EM) += gpio-em.o
26obj-$(CONFIG_GPIO_EP93XX) += gpio-ep93xx.o 26obj-$(CONFIG_GPIO_EP93XX) += gpio-ep93xx.o
27obj-$(CONFIG_GPIO_F7188X) += gpio-f7188x.o
27obj-$(CONFIG_GPIO_GE_FPGA) += gpio-ge.o 28obj-$(CONFIG_GPIO_GE_FPGA) += gpio-ge.o
28obj-$(CONFIG_GPIO_GRGPIO) += gpio-grgpio.o 29obj-$(CONFIG_GPIO_GRGPIO) += gpio-grgpio.o
29obj-$(CONFIG_GPIO_ICH) += gpio-ich.o 30obj-$(CONFIG_GPIO_ICH) += gpio-ich.o
diff --git a/drivers/gpio/gpio-f7188x.c b/drivers/gpio/gpio-f7188x.c
new file mode 100644
index 000000000000..9cb8320e1181
--- /dev/null
+++ b/drivers/gpio/gpio-f7188x.c
@@ -0,0 +1,469 @@
1/*
2 * GPIO driver for Fintek Super-I/O F71882 and F71889
3 *
4 * Copyright (C) 2010-2013 LaCie
5 *
6 * Author: Simon Guinot <simon.guinot@sequanux.org>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 */
13
14#include <linux/module.h>
15#include <linux/init.h>
16#include <linux/platform_device.h>
17#include <linux/io.h>
18#include <linux/gpio.h>
19
20#define DRVNAME "gpio-f7188x"
21
22/*
23 * Super-I/O registers
24 */
25#define SIO_LDSEL 0x07 /* Logical device select */
26#define SIO_DEVID 0x20 /* Device ID (2 bytes) */
27#define SIO_DEVREV 0x22 /* Device revision */
28#define SIO_MANID 0x23 /* Fintek ID (2 bytes) */
29
30#define SIO_LD_GPIO 0x06 /* GPIO logical device */
31#define SIO_UNLOCK_KEY 0x87 /* Key to enable Super-I/O */
32#define SIO_LOCK_KEY 0xAA /* Key to disable Super-I/O */
33
34#define SIO_FINTEK_ID 0x1934 /* Manufacturer ID */
35#define SIO_F71882_ID 0x0541 /* F71882 chipset ID */
36#define SIO_F71889_ID 0x0909 /* F71889 chipset ID */
37
38enum chips { f71882fg, f71889f };
39
40static const char * const f7188x_names[] = {
41 "f71882fg",
42 "f71889f",
43};
44
45struct f7188x_sio {
46 int addr;
47 enum chips type;
48};
49
50struct f7188x_gpio_bank {
51 struct gpio_chip chip;
52 unsigned int regbase;
53 struct f7188x_gpio_data *data;
54};
55
56struct f7188x_gpio_data {
57 struct f7188x_sio *sio;
58 int nr_bank;
59 struct f7188x_gpio_bank *bank;
60};
61
62/*
63 * Super-I/O functions.
64 */
65
66static inline int superio_inb(int base, int reg)
67{
68 outb(reg, base);
69 return inb(base + 1);
70}
71
72static int superio_inw(int base, int reg)
73{
74 int val;
75
76 outb(reg++, base);
77 val = inb(base + 1) << 8;
78 outb(reg, base);
79 val |= inb(base + 1);
80
81 return val;
82}
83
84static inline void superio_outb(int base, int reg, int val)
85{
86 outb(reg, base);
87 outb(val, base + 1);
88}
89
90static inline int superio_enter(int base)
91{
92 /* Don't step on other drivers' I/O space by accident. */
93 if (!request_muxed_region(base, 2, DRVNAME)) {
94 pr_err(DRVNAME "I/O address 0x%04x already in use\n", base);
95 return -EBUSY;
96 }
97
98 /* According to the datasheet the key must be send twice. */
99 outb(SIO_UNLOCK_KEY, base);
100 outb(SIO_UNLOCK_KEY, base);
101
102 return 0;
103}
104
105static inline void superio_select(int base, int ld)
106{
107 outb(SIO_LDSEL, base);
108 outb(ld, base + 1);
109}
110
111static inline void superio_exit(int base)
112{
113 outb(SIO_LOCK_KEY, base);
114 release_region(base, 2);
115}
116
117/*
118 * GPIO chip.
119 */
120
121static int f7188x_gpio_direction_in(struct gpio_chip *chip, unsigned offset);
122static int f7188x_gpio_get(struct gpio_chip *chip, unsigned offset);
123static int f7188x_gpio_direction_out(struct gpio_chip *chip,
124 unsigned offset, int value);
125static void f7188x_gpio_set(struct gpio_chip *chip, unsigned offset, int value);
126
127#define F7188X_GPIO_BANK(_base, _ngpio, _regbase) \
128 { \
129 .chip = { \
130 .label = DRVNAME, \
131 .owner = THIS_MODULE, \
132 .direction_input = f7188x_gpio_direction_in, \
133 .get = f7188x_gpio_get, \
134 .direction_output = f7188x_gpio_direction_out, \
135 .set = f7188x_gpio_set, \
136 .base = _base, \
137 .ngpio = _ngpio, \
138 }, \
139 .regbase = _regbase, \
140 }
141
142#define gpio_dir(base) (base + 0)
143#define gpio_data_out(base) (base + 1)
144#define gpio_data_in(base) (base + 2)
145/* Output mode register (0:open drain 1:push-pull). */
146#define gpio_out_mode(base) (base + 3)
147
148static struct f7188x_gpio_bank f71882_gpio_bank[] = {
149 F7188X_GPIO_BANK(0 , 8, 0xF0),
150 F7188X_GPIO_BANK(10, 8, 0xE0),
151 F7188X_GPIO_BANK(20, 8, 0xD0),
152 F7188X_GPIO_BANK(30, 4, 0xC0),
153 F7188X_GPIO_BANK(40, 4, 0xB0),
154};
155
156static struct f7188x_gpio_bank f71889_gpio_bank[] = {
157 F7188X_GPIO_BANK(0 , 7, 0xF0),
158 F7188X_GPIO_BANK(10, 7, 0xE0),
159 F7188X_GPIO_BANK(20, 8, 0xD0),
160 F7188X_GPIO_BANK(30, 8, 0xC0),
161 F7188X_GPIO_BANK(40, 8, 0xB0),
162 F7188X_GPIO_BANK(50, 5, 0xA0),
163 F7188X_GPIO_BANK(60, 8, 0x90),
164 F7188X_GPIO_BANK(70, 8, 0x80),
165};
166
167static int f7188x_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
168{
169 int err;
170 struct f7188x_gpio_bank *bank =
171 container_of(chip, struct f7188x_gpio_bank, chip);
172 struct f7188x_sio *sio = bank->data->sio;
173 u8 dir;
174
175 err = superio_enter(sio->addr);
176 if (err)
177 return err;
178 superio_select(sio->addr, SIO_LD_GPIO);
179
180 dir = superio_inb(sio->addr, gpio_dir(bank->regbase));
181 dir &= ~(1 << offset);
182 superio_outb(sio->addr, gpio_dir(bank->regbase), dir);
183
184 superio_exit(sio->addr);
185
186 return 0;
187}
188
189static int f7188x_gpio_get(struct gpio_chip *chip, unsigned offset)
190{
191 int err;
192 struct f7188x_gpio_bank *bank =
193 container_of(chip, struct f7188x_gpio_bank, chip);
194 struct f7188x_sio *sio = bank->data->sio;
195 u8 dir, data;
196
197 err = superio_enter(sio->addr);
198 if (err)
199 return err;
200 superio_select(sio->addr, SIO_LD_GPIO);
201
202 dir = superio_inb(sio->addr, gpio_dir(bank->regbase));
203 dir = !!(dir & (1 << offset));
204 if (dir)
205 data = superio_inb(sio->addr, gpio_data_out(bank->regbase));
206 else
207 data = superio_inb(sio->addr, gpio_data_in(bank->regbase));
208
209 superio_exit(sio->addr);
210
211 return !!(data & 1 << offset);
212}
213
214static int f7188x_gpio_direction_out(struct gpio_chip *chip,
215 unsigned offset, int value)
216{
217 int err;
218 struct f7188x_gpio_bank *bank =
219 container_of(chip, struct f7188x_gpio_bank, chip);
220 struct f7188x_sio *sio = bank->data->sio;
221 u8 dir, data_out;
222
223 err = superio_enter(sio->addr);
224 if (err)
225 return err;
226 superio_select(sio->addr, SIO_LD_GPIO);
227
228 data_out = superio_inb(sio->addr, gpio_data_out(bank->regbase));
229 if (value)
230 data_out |= (1 << offset);
231 else
232 data_out &= ~(1 << offset);
233 superio_outb(sio->addr, gpio_data_out(bank->regbase), data_out);
234
235 dir = superio_inb(sio->addr, gpio_dir(bank->regbase));
236 dir |= (1 << offset);
237 superio_outb(sio->addr, gpio_dir(bank->regbase), dir);
238
239 superio_exit(sio->addr);
240
241 return 0;
242}
243
244static void f7188x_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
245{
246 int err;
247 struct f7188x_gpio_bank *bank =
248 container_of(chip, struct f7188x_gpio_bank, chip);
249 struct f7188x_sio *sio = bank->data->sio;
250 u8 data_out;
251
252 err = superio_enter(sio->addr);
253 if (err)
254 return;
255 superio_select(sio->addr, SIO_LD_GPIO);
256
257 data_out = superio_inb(sio->addr, gpio_data_out(bank->regbase));
258 if (value)
259 data_out |= (1 << offset);
260 else
261 data_out &= ~(1 << offset);
262 superio_outb(sio->addr, gpio_data_out(bank->regbase), data_out);
263
264 superio_exit(sio->addr);
265}
266
267/*
268 * Platform device and driver.
269 */
270
271static int f7188x_gpio_probe(struct platform_device *pdev)
272{
273 int err;
274 int i;
275 struct f7188x_sio *sio = pdev->dev.platform_data;
276 struct f7188x_gpio_data *data;
277
278 data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
279 if (!data)
280 return -ENOMEM;
281
282 switch (sio->type) {
283 case f71882fg:
284 data->nr_bank = ARRAY_SIZE(f71882_gpio_bank);
285 data->bank = f71882_gpio_bank;
286 break;
287 case f71889f:
288 data->nr_bank = ARRAY_SIZE(f71889_gpio_bank);
289 data->bank = f71889_gpio_bank;
290 break;
291 default:
292 return -ENODEV;
293 }
294 data->sio = sio;
295
296 platform_set_drvdata(pdev, data);
297
298 /* For each GPIO bank, register a GPIO chip. */
299 for (i = 0; i < data->nr_bank; i++) {
300 struct f7188x_gpio_bank *bank = &data->bank[i];
301
302 bank->chip.dev = &pdev->dev;
303 bank->data = data;
304
305 err = gpiochip_add(&bank->chip);
306 if (err) {
307 dev_err(&pdev->dev,
308 "Failed to register gpiochip %d: %d\n",
309 i, err);
310 goto err_gpiochip;
311 }
312 }
313
314 return 0;
315
316err_gpiochip:
317 for (i = i - 1; i >= 0; i--) {
318 struct f7188x_gpio_bank *bank = &data->bank[i];
319 int tmp;
320
321 tmp = gpiochip_remove(&bank->chip);
322 if (tmp < 0)
323 dev_err(&pdev->dev,
324 "Failed to remove gpiochip %d: %d\n",
325 i, tmp);
326 }
327
328 return err;
329}
330
331static int f7188x_gpio_remove(struct platform_device *pdev)
332{
333 int err;
334 int i;
335 struct f7188x_gpio_data *data = platform_get_drvdata(pdev);
336
337 for (i = 0; i < data->nr_bank; i++) {
338 struct f7188x_gpio_bank *bank = &data->bank[i];
339
340 err = gpiochip_remove(&bank->chip);
341 if (err) {
342 dev_err(&pdev->dev,
343 "Failed to remove GPIO gpiochip %d: %d\n",
344 i, err);
345 return err;
346 }
347 }
348
349 return 0;
350}
351
352static int __init f7188x_find(int addr, struct f7188x_sio *sio)
353{
354 int err;
355 u16 devid;
356
357 err = superio_enter(addr);
358 if (err)
359 return err;
360
361 err = -ENODEV;
362 devid = superio_inw(addr, SIO_MANID);
363 if (devid != SIO_FINTEK_ID) {
364 pr_debug(DRVNAME ": Not a Fintek device at 0x%08x\n", addr);
365 goto err;
366 }
367
368 devid = superio_inw(addr, SIO_DEVID);
369 switch (devid) {
370 case SIO_F71882_ID:
371 sio->type = f71882fg;
372 break;
373 case SIO_F71889_ID:
374 sio->type = f71889f;
375 break;
376 default:
377 pr_info(DRVNAME ": Unsupported Fintek device 0x%04x\n", devid);
378 goto err;
379 }
380 sio->addr = addr;
381 err = 0;
382
383 pr_info(DRVNAME ": Found %s at %#x, revision %d\n",
384 f7188x_names[sio->type],
385 (unsigned int) addr,
386 (int) superio_inb(addr, SIO_DEVREV));
387
388err:
389 superio_exit(addr);
390 return err;
391}
392
393static struct platform_device *f7188x_gpio_pdev;
394
395static int __init
396f7188x_gpio_device_add(const struct f7188x_sio *sio)
397{
398 int err;
399
400 f7188x_gpio_pdev = platform_device_alloc(DRVNAME, -1);
401 if (!f7188x_gpio_pdev)
402 return -ENOMEM;
403
404 err = platform_device_add_data(f7188x_gpio_pdev,
405 sio, sizeof(*sio));
406 if (err) {
407 pr_err(DRVNAME "Platform data allocation failed\n");
408 goto err;
409 }
410
411 err = platform_device_add(f7188x_gpio_pdev);
412 if (err) {
413 pr_err(DRVNAME "Device addition failed\n");
414 goto err;
415 }
416
417 return 0;
418
419err:
420 platform_device_put(f7188x_gpio_pdev);
421
422 return err;
423}
424
425/*
426 * Try to match a supported Fintech device by reading the (hard-wired)
427 * configuration I/O ports. If available, then register both the platform
428 * device and driver to support the GPIOs.
429 */
430
431static struct platform_driver f7188x_gpio_driver = {
432 .driver = {
433 .owner = THIS_MODULE,
434 .name = DRVNAME,
435 },
436 .probe = f7188x_gpio_probe,
437 .remove = f7188x_gpio_remove,
438};
439
440static int __init f7188x_gpio_init(void)
441{
442 int err;
443 struct f7188x_sio sio;
444
445 if (f7188x_find(0x2e, &sio) &&
446 f7188x_find(0x4e, &sio))
447 return -ENODEV;
448
449 err = platform_driver_register(&f7188x_gpio_driver);
450 if (!err) {
451 err = f7188x_gpio_device_add(&sio);
452 if (err)
453 platform_driver_unregister(&f7188x_gpio_driver);
454 }
455
456 return err;
457}
458subsys_initcall(f7188x_gpio_init);
459
460static void __exit f7188x_gpio_exit(void)
461{
462 platform_device_unregister(f7188x_gpio_pdev);
463 platform_driver_unregister(&f7188x_gpio_driver);
464}
465module_exit(f7188x_gpio_exit);
466
467MODULE_DESCRIPTION("GPIO driver for Super-I/O chips F71882FG and F71889F");
468MODULE_AUTHOR("Simon Guinot <simon.guinot@sequanux.org>");
469MODULE_LICENSE("GPL");