aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorMichael Hennerich <michael.hennerich@analog.com>2009-09-22 19:46:39 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2009-09-23 10:39:48 -0400
commitef72af408259f0ff26a733dfa2088d570a111550 (patch)
tree6e05ddf6ab5eafc03e52225499fbb292e3f61649 /drivers
parentff77c352ae17768c61cfc36357f0a3904552f11c (diff)
gpio: gpio support for ADP5520/ADP5501 MFD PMICs
Signed-off-by: Michael Hennerich <michael.hennerich@analog.com> Signed-off-by: Bryan Wu <cooloney@kernel.org> Signed-off-by: Mike Frysinger <vapier@gentoo.org> Cc: David Brownell <david-b@pacbell.net> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/gpio/Kconfig10
-rw-r--r--drivers/gpio/Makefile1
-rw-r--r--drivers/gpio/adp5520-gpio.c206
3 files changed, 217 insertions, 0 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 9ad20ffdbf8c..2ad0128c63c6 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -162,6 +162,16 @@ config GPIO_WM831X
162 Say yes here to access the GPIO signals of WM831x power management 162 Say yes here to access the GPIO signals of WM831x power management
163 chips from Wolfson Microelectronics. 163 chips from Wolfson Microelectronics.
164 164
165config GPIO_ADP5520
166 tristate "GPIO Support for ADP5520 PMIC"
167 depends on PMIC_ADP5520
168 help
169 This option enables support for on-chip GPIO found
170 on Analog Devices ADP5520 PMICs.
171
172 To compile this driver as a module, choose M here: the module will
173 be called adp5520-gpio.
174
165comment "PCI GPIO expanders:" 175comment "PCI GPIO expanders:"
166 176
167config GPIO_BT8XX 177config GPIO_BT8XX
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index b8eef768387f..00a532c9a1e2 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -4,6 +4,7 @@ ccflags-$(CONFIG_DEBUG_GPIO) += -DDEBUG
4 4
5obj-$(CONFIG_GPIOLIB) += gpiolib.o 5obj-$(CONFIG_GPIOLIB) += gpiolib.o
6 6
7obj-$(CONFIG_GPIO_ADP5520) += adp5520-gpio.o
7obj-$(CONFIG_GPIO_LANGWELL) += langwell_gpio.o 8obj-$(CONFIG_GPIO_LANGWELL) += langwell_gpio.o
8obj-$(CONFIG_GPIO_MAX7301) += max7301.o 9obj-$(CONFIG_GPIO_MAX7301) += max7301.o
9obj-$(CONFIG_GPIO_MAX732X) += max732x.o 10obj-$(CONFIG_GPIO_MAX732X) += max732x.o
diff --git a/drivers/gpio/adp5520-gpio.c b/drivers/gpio/adp5520-gpio.c
new file mode 100644
index 000000000000..ad05bbc7ffd5
--- /dev/null
+++ b/drivers/gpio/adp5520-gpio.c
@@ -0,0 +1,206 @@
1/*
2 * GPIO driver for Analog Devices ADP5520 MFD PMICs
3 *
4 * Copyright 2009 Analog Devices Inc.
5 *
6 * Licensed under the GPL-2 or later.
7 */
8
9#include <linux/module.h>
10#include <linux/kernel.h>
11#include <linux/init.h>
12#include <linux/platform_device.h>
13#include <linux/mfd/adp5520.h>
14
15#include <linux/gpio.h>
16
17struct adp5520_gpio {
18 struct device *master;
19 struct gpio_chip gpio_chip;
20 unsigned char lut[ADP5520_MAXGPIOS];
21 unsigned long output;
22};
23
24static int adp5520_gpio_get_value(struct gpio_chip *chip, unsigned off)
25{
26 struct adp5520_gpio *dev;
27 uint8_t reg_val;
28
29 dev = container_of(chip, struct adp5520_gpio, gpio_chip);
30
31 /*
32 * There are dedicated registers for GPIO IN/OUT.
33 * Make sure we return the right value, even when configured as output
34 */
35
36 if (test_bit(off, &dev->output))
37 adp5520_read(dev->master, GPIO_OUT, &reg_val);
38 else
39 adp5520_read(dev->master, GPIO_IN, &reg_val);
40
41 return !!(reg_val & dev->lut[off]);
42}
43
44static void adp5520_gpio_set_value(struct gpio_chip *chip,
45 unsigned off, int val)
46{
47 struct adp5520_gpio *dev;
48 dev = container_of(chip, struct adp5520_gpio, gpio_chip);
49
50 if (val)
51 adp5520_set_bits(dev->master, GPIO_OUT, dev->lut[off]);
52 else
53 adp5520_clr_bits(dev->master, GPIO_OUT, dev->lut[off]);
54}
55
56static int adp5520_gpio_direction_input(struct gpio_chip *chip, unsigned off)
57{
58 struct adp5520_gpio *dev;
59 dev = container_of(chip, struct adp5520_gpio, gpio_chip);
60
61 clear_bit(off, &dev->output);
62
63 return adp5520_clr_bits(dev->master, GPIO_CFG_2, dev->lut[off]);
64}
65
66static int adp5520_gpio_direction_output(struct gpio_chip *chip,
67 unsigned off, int val)
68{
69 struct adp5520_gpio *dev;
70 int ret = 0;
71 dev = container_of(chip, struct adp5520_gpio, gpio_chip);
72
73 set_bit(off, &dev->output);
74
75 if (val)
76 ret |= adp5520_set_bits(dev->master, GPIO_OUT, dev->lut[off]);
77 else
78 ret |= adp5520_clr_bits(dev->master, GPIO_OUT, dev->lut[off]);
79
80 ret |= adp5520_set_bits(dev->master, GPIO_CFG_2, dev->lut[off]);
81
82 return ret;
83}
84
85static int __devinit adp5520_gpio_probe(struct platform_device *pdev)
86{
87 struct adp5520_gpio_platfrom_data *pdata = pdev->dev.platform_data;
88 struct adp5520_gpio *dev;
89 struct gpio_chip *gc;
90 int ret, i, gpios;
91 unsigned char ctl_mask = 0;
92
93 if (pdata == NULL) {
94 dev_err(&pdev->dev, "missing platform data\n");
95 return -ENODEV;
96 }
97
98 if (pdev->id != ID_ADP5520) {
99 dev_err(&pdev->dev, "only ADP5520 supports GPIO\n");
100 return -ENODEV;
101 }
102
103 dev = kzalloc(sizeof(*dev), GFP_KERNEL);
104 if (dev == NULL) {
105 dev_err(&pdev->dev, "failed to alloc memory\n");
106 return -ENOMEM;
107 }
108
109 dev->master = pdev->dev.parent;
110
111 for (gpios = 0, i = 0; i < ADP5520_MAXGPIOS; i++)
112 if (pdata->gpio_en_mask & (1 << i))
113 dev->lut[gpios++] = 1 << i;
114
115 if (gpios < 1) {
116 ret = -EINVAL;
117 goto err;
118 }
119
120 gc = &dev->gpio_chip;
121 gc->direction_input = adp5520_gpio_direction_input;
122 gc->direction_output = adp5520_gpio_direction_output;
123 gc->get = adp5520_gpio_get_value;
124 gc->set = adp5520_gpio_set_value;
125 gc->can_sleep = 1;
126
127 gc->base = pdata->gpio_start;
128 gc->ngpio = gpios;
129 gc->label = pdev->name;
130 gc->owner = THIS_MODULE;
131
132 ret = adp5520_clr_bits(dev->master, GPIO_CFG_1,
133 pdata->gpio_en_mask);
134
135 if (pdata->gpio_en_mask & GPIO_C3)
136 ctl_mask |= C3_MODE;
137
138 if (pdata->gpio_en_mask & GPIO_R3)
139 ctl_mask |= R3_MODE;
140
141 if (ctl_mask)
142 ret = adp5520_set_bits(dev->master, LED_CONTROL,
143 ctl_mask);
144
145 ret |= adp5520_set_bits(dev->master, GPIO_PULLUP,
146 pdata->gpio_pullup_mask);
147
148 if (ret) {
149 dev_err(&pdev->dev, "failed to write\n");
150 goto err;
151 }
152
153 ret = gpiochip_add(&dev->gpio_chip);
154 if (ret)
155 goto err;
156
157 platform_set_drvdata(pdev, dev);
158 return 0;
159
160err:
161 kfree(dev);
162 return ret;
163}
164
165static int __devexit adp5520_gpio_remove(struct platform_device *pdev)
166{
167 struct adp5520_gpio *dev;
168 int ret;
169
170 dev = platform_get_drvdata(pdev);
171 ret = gpiochip_remove(&dev->gpio_chip);
172 if (ret) {
173 dev_err(&pdev->dev, "%s failed, %d\n",
174 "gpiochip_remove()", ret);
175 return ret;
176 }
177
178 kfree(dev);
179 return 0;
180}
181
182static struct platform_driver adp5520_gpio_driver = {
183 .driver = {
184 .name = "adp5520-gpio",
185 .owner = THIS_MODULE,
186 },
187 .probe = adp5520_gpio_probe,
188 .remove = __devexit_p(adp5520_gpio_remove),
189};
190
191static int __init adp5520_gpio_init(void)
192{
193 return platform_driver_register(&adp5520_gpio_driver);
194}
195module_init(adp5520_gpio_init);
196
197static void __exit adp5520_gpio_exit(void)
198{
199 platform_driver_unregister(&adp5520_gpio_driver);
200}
201module_exit(adp5520_gpio_exit);
202
203MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
204MODULE_DESCRIPTION("GPIO ADP5520 Driver");
205MODULE_LICENSE("GPL");
206MODULE_ALIAS("platform:adp5520-gpio");