Commit | Line | Data |
---|---|---|
80884094 MH |
1 | /* |
2 | * GPIO Chip driver for Analog Devices | |
3 | * ADP5588 I/O Expander and QWERTY Keypad Controller | |
4 | * | |
5 | * Copyright 2009 Analog Devices Inc. | |
6 | * | |
7 | * Licensed under the GPL-2 or later. | |
8 | */ | |
9 | ||
10 | #include <linux/module.h> | |
11 | #include <linux/kernel.h> | |
5a0e3ad6 | 12 | #include <linux/slab.h> |
80884094 MH |
13 | #include <linux/init.h> |
14 | #include <linux/i2c.h> | |
15 | #include <linux/gpio.h> | |
16 | ||
17 | #include <linux/i2c/adp5588.h> | |
18 | ||
19 | #define DRV_NAME "adp5588-gpio" | |
20 | #define MAXGPIO 18 | |
21 | #define ADP_BANK(offs) ((offs) >> 3) | |
22 | #define ADP_BIT(offs) (1u << ((offs) & 0x7)) | |
23 | ||
24 | struct adp5588_gpio { | |
25 | struct i2c_client *client; | |
26 | struct gpio_chip gpio_chip; | |
27 | struct mutex lock; /* protect cached dir, dat_out */ | |
28 | unsigned gpio_start; | |
29 | uint8_t dat_out[3]; | |
30 | uint8_t dir[3]; | |
31 | }; | |
32 | ||
33 | static int adp5588_gpio_read(struct i2c_client *client, u8 reg) | |
34 | { | |
35 | int ret = i2c_smbus_read_byte_data(client, reg); | |
36 | ||
37 | if (ret < 0) | |
38 | dev_err(&client->dev, "Read Error\n"); | |
39 | ||
40 | return ret; | |
41 | } | |
42 | ||
43 | static int adp5588_gpio_write(struct i2c_client *client, u8 reg, u8 val) | |
44 | { | |
45 | int ret = i2c_smbus_write_byte_data(client, reg, val); | |
46 | ||
47 | if (ret < 0) | |
48 | dev_err(&client->dev, "Write Error\n"); | |
49 | ||
50 | return ret; | |
51 | } | |
52 | ||
53 | static int adp5588_gpio_get_value(struct gpio_chip *chip, unsigned off) | |
54 | { | |
55 | struct adp5588_gpio *dev = | |
56 | container_of(chip, struct adp5588_gpio, gpio_chip); | |
57 | ||
58 | return !!(adp5588_gpio_read(dev->client, GPIO_DAT_STAT1 + ADP_BANK(off)) | |
59 | & ADP_BIT(off)); | |
60 | } | |
61 | ||
62 | static void adp5588_gpio_set_value(struct gpio_chip *chip, | |
63 | unsigned off, int val) | |
64 | { | |
65 | unsigned bank, bit; | |
66 | struct adp5588_gpio *dev = | |
67 | container_of(chip, struct adp5588_gpio, gpio_chip); | |
68 | ||
69 | bank = ADP_BANK(off); | |
70 | bit = ADP_BIT(off); | |
71 | ||
72 | mutex_lock(&dev->lock); | |
73 | if (val) | |
74 | dev->dat_out[bank] |= bit; | |
75 | else | |
76 | dev->dat_out[bank] &= ~bit; | |
77 | ||
78 | adp5588_gpio_write(dev->client, GPIO_DAT_OUT1 + bank, | |
79 | dev->dat_out[bank]); | |
80 | mutex_unlock(&dev->lock); | |
81 | } | |
82 | ||
83 | static int adp5588_gpio_direction_input(struct gpio_chip *chip, unsigned off) | |
84 | { | |
85 | int ret; | |
86 | unsigned bank; | |
87 | struct adp5588_gpio *dev = | |
88 | container_of(chip, struct adp5588_gpio, gpio_chip); | |
89 | ||
90 | bank = ADP_BANK(off); | |
91 | ||
92 | mutex_lock(&dev->lock); | |
93 | dev->dir[bank] &= ~ADP_BIT(off); | |
94 | ret = adp5588_gpio_write(dev->client, GPIO_DIR1 + bank, dev->dir[bank]); | |
95 | mutex_unlock(&dev->lock); | |
96 | ||
97 | return ret; | |
98 | } | |
99 | ||
100 | static int adp5588_gpio_direction_output(struct gpio_chip *chip, | |
101 | unsigned off, int val) | |
102 | { | |
103 | int ret; | |
104 | unsigned bank, bit; | |
105 | struct adp5588_gpio *dev = | |
106 | container_of(chip, struct adp5588_gpio, gpio_chip); | |
107 | ||
108 | bank = ADP_BANK(off); | |
109 | bit = ADP_BIT(off); | |
110 | ||
111 | mutex_lock(&dev->lock); | |
112 | dev->dir[bank] |= bit; | |
113 | ||
114 | if (val) | |
115 | dev->dat_out[bank] |= bit; | |
116 | else | |
117 | dev->dat_out[bank] &= ~bit; | |
118 | ||
119 | ret = adp5588_gpio_write(dev->client, GPIO_DAT_OUT1 + bank, | |
120 | dev->dat_out[bank]); | |
121 | ret |= adp5588_gpio_write(dev->client, GPIO_DIR1 + bank, | |
122 | dev->dir[bank]); | |
123 | mutex_unlock(&dev->lock); | |
124 | ||
125 | return ret; | |
126 | } | |
127 | ||
128 | static int __devinit adp5588_gpio_probe(struct i2c_client *client, | |
129 | const struct i2c_device_id *id) | |
130 | { | |
131 | struct adp5588_gpio_platform_data *pdata = client->dev.platform_data; | |
132 | struct adp5588_gpio *dev; | |
133 | struct gpio_chip *gc; | |
134 | int ret, i, revid; | |
135 | ||
136 | if (pdata == NULL) { | |
137 | dev_err(&client->dev, "missing platform data\n"); | |
138 | return -ENODEV; | |
139 | } | |
140 | ||
141 | if (!i2c_check_functionality(client->adapter, | |
142 | I2C_FUNC_SMBUS_BYTE_DATA)) { | |
143 | dev_err(&client->dev, "SMBUS Byte Data not Supported\n"); | |
144 | return -EIO; | |
145 | } | |
146 | ||
147 | dev = kzalloc(sizeof(*dev), GFP_KERNEL); | |
148 | if (dev == NULL) { | |
149 | dev_err(&client->dev, "failed to alloc memory\n"); | |
150 | return -ENOMEM; | |
151 | } | |
152 | ||
153 | dev->client = client; | |
154 | ||
155 | gc = &dev->gpio_chip; | |
156 | gc->direction_input = adp5588_gpio_direction_input; | |
157 | gc->direction_output = adp5588_gpio_direction_output; | |
158 | gc->get = adp5588_gpio_get_value; | |
159 | gc->set = adp5588_gpio_set_value; | |
160 | gc->can_sleep = 1; | |
161 | ||
162 | gc->base = pdata->gpio_start; | |
163 | gc->ngpio = MAXGPIO; | |
164 | gc->label = client->name; | |
165 | gc->owner = THIS_MODULE; | |
166 | ||
167 | mutex_init(&dev->lock); | |
168 | ||
169 | ||
170 | ret = adp5588_gpio_read(dev->client, DEV_ID); | |
171 | if (ret < 0) | |
172 | goto err; | |
173 | ||
174 | revid = ret & ADP5588_DEVICE_ID_MASK; | |
175 | ||
176 | for (i = 0, ret = 0; i <= ADP_BANK(MAXGPIO); i++) { | |
177 | dev->dat_out[i] = adp5588_gpio_read(client, GPIO_DAT_OUT1 + i); | |
178 | dev->dir[i] = adp5588_gpio_read(client, GPIO_DIR1 + i); | |
179 | ret |= adp5588_gpio_write(client, KP_GPIO1 + i, 0); | |
180 | ret |= adp5588_gpio_write(client, GPIO_PULL1 + i, | |
181 | (pdata->pullup_dis_mask >> (8 * i)) & 0xFF); | |
182 | ||
183 | if (ret) | |
184 | goto err; | |
185 | } | |
186 | ||
187 | ret = gpiochip_add(&dev->gpio_chip); | |
188 | if (ret) | |
189 | goto err; | |
190 | ||
191 | dev_info(&client->dev, "gpios %d..%d on a %s Rev. %d\n", | |
192 | gc->base, gc->base + gc->ngpio - 1, | |
193 | client->name, revid); | |
194 | ||
195 | if (pdata->setup) { | |
196 | ret = pdata->setup(client, gc->base, gc->ngpio, pdata->context); | |
197 | if (ret < 0) | |
198 | dev_warn(&client->dev, "setup failed, %d\n", ret); | |
199 | } | |
200 | ||
201 | i2c_set_clientdata(client, dev); | |
202 | return 0; | |
203 | ||
204 | err: | |
205 | kfree(dev); | |
206 | return ret; | |
207 | } | |
208 | ||
209 | static int __devexit adp5588_gpio_remove(struct i2c_client *client) | |
210 | { | |
211 | struct adp5588_gpio_platform_data *pdata = client->dev.platform_data; | |
212 | struct adp5588_gpio *dev = i2c_get_clientdata(client); | |
213 | int ret; | |
214 | ||
215 | if (pdata->teardown) { | |
216 | ret = pdata->teardown(client, | |
217 | dev->gpio_chip.base, dev->gpio_chip.ngpio, | |
218 | pdata->context); | |
219 | if (ret < 0) { | |
220 | dev_err(&client->dev, "teardown failed %d\n", ret); | |
221 | return ret; | |
222 | } | |
223 | } | |
224 | ||
225 | ret = gpiochip_remove(&dev->gpio_chip); | |
226 | if (ret) { | |
227 | dev_err(&client->dev, "gpiochip_remove failed %d\n", ret); | |
228 | return ret; | |
229 | } | |
230 | ||
231 | kfree(dev); | |
232 | return 0; | |
233 | } | |
234 | ||
235 | static const struct i2c_device_id adp5588_gpio_id[] = { | |
236 | {DRV_NAME, 0}, | |
237 | {} | |
238 | }; | |
239 | ||
240 | MODULE_DEVICE_TABLE(i2c, adp5588_gpio_id); | |
241 | ||
242 | static struct i2c_driver adp5588_gpio_driver = { | |
243 | .driver = { | |
244 | .name = DRV_NAME, | |
245 | }, | |
246 | .probe = adp5588_gpio_probe, | |
247 | .remove = __devexit_p(adp5588_gpio_remove), | |
248 | .id_table = adp5588_gpio_id, | |
249 | }; | |
250 | ||
251 | static int __init adp5588_gpio_init(void) | |
252 | { | |
253 | return i2c_add_driver(&adp5588_gpio_driver); | |
254 | } | |
255 | ||
256 | module_init(adp5588_gpio_init); | |
257 | ||
258 | static void __exit adp5588_gpio_exit(void) | |
259 | { | |
260 | i2c_del_driver(&adp5588_gpio_driver); | |
261 | } | |
262 | ||
263 | module_exit(adp5588_gpio_exit); | |
264 | ||
265 | MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); | |
266 | MODULE_DESCRIPTION("GPIO ADP5588 Driver"); | |
267 | MODULE_LICENSE("GPL"); |