diff options
Diffstat (limited to 'drivers/gpio/gpio-adp5520.c')
-rw-r--r-- | drivers/gpio/gpio-adp5520.c | 211 |
1 files changed, 211 insertions, 0 deletions
diff --git a/drivers/gpio/gpio-adp5520.c b/drivers/gpio/gpio-adp5520.c new file mode 100644 index 000000000000..9f2781537001 --- /dev/null +++ b/drivers/gpio/gpio-adp5520.c | |||
@@ -0,0 +1,211 @@ | |||
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/slab.h> | ||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/init.h> | ||
13 | #include <linux/platform_device.h> | ||
14 | #include <linux/mfd/adp5520.h> | ||
15 | |||
16 | #include <linux/gpio.h> | ||
17 | |||
18 | struct adp5520_gpio { | ||
19 | struct device *master; | ||
20 | struct gpio_chip gpio_chip; | ||
21 | unsigned char lut[ADP5520_MAXGPIOS]; | ||
22 | unsigned long output; | ||
23 | }; | ||
24 | |||
25 | static int adp5520_gpio_get_value(struct gpio_chip *chip, unsigned off) | ||
26 | { | ||
27 | struct adp5520_gpio *dev; | ||
28 | uint8_t reg_val; | ||
29 | |||
30 | dev = container_of(chip, struct adp5520_gpio, gpio_chip); | ||
31 | |||
32 | /* | ||
33 | * There are dedicated registers for GPIO IN/OUT. | ||
34 | * Make sure we return the right value, even when configured as output | ||
35 | */ | ||
36 | |||
37 | if (test_bit(off, &dev->output)) | ||
38 | adp5520_read(dev->master, ADP5520_GPIO_OUT, ®_val); | ||
39 | else | ||
40 | adp5520_read(dev->master, ADP5520_GPIO_IN, ®_val); | ||
41 | |||
42 | return !!(reg_val & dev->lut[off]); | ||
43 | } | ||
44 | |||
45 | static void adp5520_gpio_set_value(struct gpio_chip *chip, | ||
46 | unsigned off, int val) | ||
47 | { | ||
48 | struct adp5520_gpio *dev; | ||
49 | dev = container_of(chip, struct adp5520_gpio, gpio_chip); | ||
50 | |||
51 | if (val) | ||
52 | adp5520_set_bits(dev->master, ADP5520_GPIO_OUT, dev->lut[off]); | ||
53 | else | ||
54 | adp5520_clr_bits(dev->master, ADP5520_GPIO_OUT, dev->lut[off]); | ||
55 | } | ||
56 | |||
57 | static int adp5520_gpio_direction_input(struct gpio_chip *chip, unsigned off) | ||
58 | { | ||
59 | struct adp5520_gpio *dev; | ||
60 | dev = container_of(chip, struct adp5520_gpio, gpio_chip); | ||
61 | |||
62 | clear_bit(off, &dev->output); | ||
63 | |||
64 | return adp5520_clr_bits(dev->master, ADP5520_GPIO_CFG_2, | ||
65 | dev->lut[off]); | ||
66 | } | ||
67 | |||
68 | static int adp5520_gpio_direction_output(struct gpio_chip *chip, | ||
69 | unsigned off, int val) | ||
70 | { | ||
71 | struct adp5520_gpio *dev; | ||
72 | int ret = 0; | ||
73 | dev = container_of(chip, struct adp5520_gpio, gpio_chip); | ||
74 | |||
75 | set_bit(off, &dev->output); | ||
76 | |||
77 | if (val) | ||
78 | ret |= adp5520_set_bits(dev->master, ADP5520_GPIO_OUT, | ||
79 | dev->lut[off]); | ||
80 | else | ||
81 | ret |= adp5520_clr_bits(dev->master, ADP5520_GPIO_OUT, | ||
82 | dev->lut[off]); | ||
83 | |||
84 | ret |= adp5520_set_bits(dev->master, ADP5520_GPIO_CFG_2, | ||
85 | dev->lut[off]); | ||
86 | |||
87 | return ret; | ||
88 | } | ||
89 | |||
90 | static int __devinit adp5520_gpio_probe(struct platform_device *pdev) | ||
91 | { | ||
92 | struct adp5520_gpio_platform_data *pdata = pdev->dev.platform_data; | ||
93 | struct adp5520_gpio *dev; | ||
94 | struct gpio_chip *gc; | ||
95 | int ret, i, gpios; | ||
96 | unsigned char ctl_mask = 0; | ||
97 | |||
98 | if (pdata == NULL) { | ||
99 | dev_err(&pdev->dev, "missing platform data\n"); | ||
100 | return -ENODEV; | ||
101 | } | ||
102 | |||
103 | if (pdev->id != ID_ADP5520) { | ||
104 | dev_err(&pdev->dev, "only ADP5520 supports GPIO\n"); | ||
105 | return -ENODEV; | ||
106 | } | ||
107 | |||
108 | dev = kzalloc(sizeof(*dev), GFP_KERNEL); | ||
109 | if (dev == NULL) { | ||
110 | dev_err(&pdev->dev, "failed to alloc memory\n"); | ||
111 | return -ENOMEM; | ||
112 | } | ||
113 | |||
114 | dev->master = pdev->dev.parent; | ||
115 | |||
116 | for (gpios = 0, i = 0; i < ADP5520_MAXGPIOS; i++) | ||
117 | if (pdata->gpio_en_mask & (1 << i)) | ||
118 | dev->lut[gpios++] = 1 << i; | ||
119 | |||
120 | if (gpios < 1) { | ||
121 | ret = -EINVAL; | ||
122 | goto err; | ||
123 | } | ||
124 | |||
125 | gc = &dev->gpio_chip; | ||
126 | gc->direction_input = adp5520_gpio_direction_input; | ||
127 | gc->direction_output = adp5520_gpio_direction_output; | ||
128 | gc->get = adp5520_gpio_get_value; | ||
129 | gc->set = adp5520_gpio_set_value; | ||
130 | gc->can_sleep = 1; | ||
131 | |||
132 | gc->base = pdata->gpio_start; | ||
133 | gc->ngpio = gpios; | ||
134 | gc->label = pdev->name; | ||
135 | gc->owner = THIS_MODULE; | ||
136 | |||
137 | ret = adp5520_clr_bits(dev->master, ADP5520_GPIO_CFG_1, | ||
138 | pdata->gpio_en_mask); | ||
139 | |||
140 | if (pdata->gpio_en_mask & ADP5520_GPIO_C3) | ||
141 | ctl_mask |= ADP5520_C3_MODE; | ||
142 | |||
143 | if (pdata->gpio_en_mask & ADP5520_GPIO_R3) | ||
144 | ctl_mask |= ADP5520_R3_MODE; | ||
145 | |||
146 | if (ctl_mask) | ||
147 | ret = adp5520_set_bits(dev->master, ADP5520_LED_CONTROL, | ||
148 | ctl_mask); | ||
149 | |||
150 | ret |= adp5520_set_bits(dev->master, ADP5520_GPIO_PULLUP, | ||
151 | pdata->gpio_pullup_mask); | ||
152 | |||
153 | if (ret) { | ||
154 | dev_err(&pdev->dev, "failed to write\n"); | ||
155 | goto err; | ||
156 | } | ||
157 | |||
158 | ret = gpiochip_add(&dev->gpio_chip); | ||
159 | if (ret) | ||
160 | goto err; | ||
161 | |||
162 | platform_set_drvdata(pdev, dev); | ||
163 | return 0; | ||
164 | |||
165 | err: | ||
166 | kfree(dev); | ||
167 | return ret; | ||
168 | } | ||
169 | |||
170 | static int __devexit adp5520_gpio_remove(struct platform_device *pdev) | ||
171 | { | ||
172 | struct adp5520_gpio *dev; | ||
173 | int ret; | ||
174 | |||
175 | dev = platform_get_drvdata(pdev); | ||
176 | ret = gpiochip_remove(&dev->gpio_chip); | ||
177 | if (ret) { | ||
178 | dev_err(&pdev->dev, "%s failed, %d\n", | ||
179 | "gpiochip_remove()", ret); | ||
180 | return ret; | ||
181 | } | ||
182 | |||
183 | kfree(dev); | ||
184 | return 0; | ||
185 | } | ||
186 | |||
187 | static struct platform_driver adp5520_gpio_driver = { | ||
188 | .driver = { | ||
189 | .name = "adp5520-gpio", | ||
190 | .owner = THIS_MODULE, | ||
191 | }, | ||
192 | .probe = adp5520_gpio_probe, | ||
193 | .remove = __devexit_p(adp5520_gpio_remove), | ||
194 | }; | ||
195 | |||
196 | static int __init adp5520_gpio_init(void) | ||
197 | { | ||
198 | return platform_driver_register(&adp5520_gpio_driver); | ||
199 | } | ||
200 | module_init(adp5520_gpio_init); | ||
201 | |||
202 | static void __exit adp5520_gpio_exit(void) | ||
203 | { | ||
204 | platform_driver_unregister(&adp5520_gpio_driver); | ||
205 | } | ||
206 | module_exit(adp5520_gpio_exit); | ||
207 | |||
208 | MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); | ||
209 | MODULE_DESCRIPTION("GPIO ADP5520 Driver"); | ||
210 | MODULE_LICENSE("GPL"); | ||
211 | MODULE_ALIAS("platform:adp5520-gpio"); | ||