diff options
author | Michael Hennerich <michael.hennerich@analog.com> | 2009-09-22 19:46:39 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-09-23 10:39:48 -0400 |
commit | ef72af408259f0ff26a733dfa2088d570a111550 (patch) | |
tree | 6e05ddf6ab5eafc03e52225499fbb292e3f61649 | |
parent | ff77c352ae17768c61cfc36357f0a3904552f11c (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>
-rw-r--r-- | drivers/gpio/Kconfig | 10 | ||||
-rw-r--r-- | drivers/gpio/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpio/adp5520-gpio.c | 206 |
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 | ||
165 | config 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 | |||
165 | comment "PCI GPIO expanders:" | 175 | comment "PCI GPIO expanders:" |
166 | 176 | ||
167 | config GPIO_BT8XX | 177 | config 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 | ||
5 | obj-$(CONFIG_GPIOLIB) += gpiolib.o | 5 | obj-$(CONFIG_GPIOLIB) += gpiolib.o |
6 | 6 | ||
7 | obj-$(CONFIG_GPIO_ADP5520) += adp5520-gpio.o | ||
7 | obj-$(CONFIG_GPIO_LANGWELL) += langwell_gpio.o | 8 | obj-$(CONFIG_GPIO_LANGWELL) += langwell_gpio.o |
8 | obj-$(CONFIG_GPIO_MAX7301) += max7301.o | 9 | obj-$(CONFIG_GPIO_MAX7301) += max7301.o |
9 | obj-$(CONFIG_GPIO_MAX732X) += max732x.o | 10 | obj-$(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 | |||
17 | struct adp5520_gpio { | ||
18 | struct device *master; | ||
19 | struct gpio_chip gpio_chip; | ||
20 | unsigned char lut[ADP5520_MAXGPIOS]; | ||
21 | unsigned long output; | ||
22 | }; | ||
23 | |||
24 | static 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, ®_val); | ||
38 | else | ||
39 | adp5520_read(dev->master, GPIO_IN, ®_val); | ||
40 | |||
41 | return !!(reg_val & dev->lut[off]); | ||
42 | } | ||
43 | |||
44 | static 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 | |||
56 | static 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 | |||
66 | static 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 | |||
85 | static 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 | |||
160 | err: | ||
161 | kfree(dev); | ||
162 | return ret; | ||
163 | } | ||
164 | |||
165 | static 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 | |||
182 | static 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 | |||
191 | static int __init adp5520_gpio_init(void) | ||
192 | { | ||
193 | return platform_driver_register(&adp5520_gpio_driver); | ||
194 | } | ||
195 | module_init(adp5520_gpio_init); | ||
196 | |||
197 | static void __exit adp5520_gpio_exit(void) | ||
198 | { | ||
199 | platform_driver_unregister(&adp5520_gpio_driver); | ||
200 | } | ||
201 | module_exit(adp5520_gpio_exit); | ||
202 | |||
203 | MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); | ||
204 | MODULE_DESCRIPTION("GPIO ADP5520 Driver"); | ||
205 | MODULE_LICENSE("GPL"); | ||
206 | MODULE_ALIAS("platform:adp5520-gpio"); | ||