diff options
author | Jin Park <jinyoungp@nvidia.com> | 2011-07-04 13:48:12 -0400 |
---|---|---|
committer | Samuel Ortiz <sameo@linux.intel.com> | 2011-07-31 17:28:26 -0400 |
commit | 09d6292befba8c6319d9471803149573ea6ed170 (patch) | |
tree | 1de1bda81b4ca3844e80a6041e0ccefbc5db1628 /drivers/mfd | |
parent | 7785bf11f312624101462d7dc840e27344899873 (diff) |
mfd: Add AAT2870 mfd driver
Add mfd core driver for AnalogicTech AAT2870.
The AAT2870 is communication through I2C and contains backlight and
regulator components.
Signed-off-by: Jin Park <jinyoungp@nvidia.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Diffstat (limited to 'drivers/mfd')
-rw-r--r-- | drivers/mfd/Kconfig | 10 | ||||
-rw-r--r-- | drivers/mfd/Makefile | 1 | ||||
-rw-r--r-- | drivers/mfd/aat2870-core.c | 535 |
3 files changed, 546 insertions, 0 deletions
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index c26136443c05..21574bdf485f 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig | |||
@@ -760,6 +760,16 @@ config MFD_PM8XXX_IRQ | |||
760 | config TPS65911_COMPARATOR | 760 | config TPS65911_COMPARATOR |
761 | tristate | 761 | tristate |
762 | 762 | ||
763 | config MFD_AAT2870_CORE | ||
764 | bool "Support for the AnalogicTech AAT2870" | ||
765 | select MFD_CORE | ||
766 | depends on I2C=y && GPIOLIB | ||
767 | help | ||
768 | If you say yes here you get support for the AAT2870. | ||
769 | This driver provides common support for accessing the device, | ||
770 | additional drivers must be enabled in order to use the | ||
771 | functionality of the device. | ||
772 | |||
763 | endif # MFD_SUPPORT | 773 | endif # MFD_SUPPORT |
764 | 774 | ||
765 | menu "Multimedia Capabilities Port drivers" | 775 | menu "Multimedia Capabilities Port drivers" |
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index ca1867db3db8..c58020303d18 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile | |||
@@ -101,3 +101,4 @@ obj-$(CONFIG_MFD_OMAP_USB_HOST) += omap-usb-host.o | |||
101 | obj-$(CONFIG_MFD_PM8921_CORE) += pm8921-core.o | 101 | obj-$(CONFIG_MFD_PM8921_CORE) += pm8921-core.o |
102 | obj-$(CONFIG_MFD_PM8XXX_IRQ) += pm8xxx-irq.o | 102 | obj-$(CONFIG_MFD_PM8XXX_IRQ) += pm8xxx-irq.o |
103 | obj-$(CONFIG_TPS65911_COMPARATOR) += tps65911-comparator.o | 103 | obj-$(CONFIG_TPS65911_COMPARATOR) += tps65911-comparator.o |
104 | obj-$(CONFIG_MFD_AAT2870_CORE) += aat2870-core.o | ||
diff --git a/drivers/mfd/aat2870-core.c b/drivers/mfd/aat2870-core.c new file mode 100644 index 000000000000..345dc658ef06 --- /dev/null +++ b/drivers/mfd/aat2870-core.c | |||
@@ -0,0 +1,535 @@ | |||
1 | /* | ||
2 | * linux/drivers/mfd/aat2870-core.c | ||
3 | * | ||
4 | * Copyright (c) 2011, NVIDIA Corporation. | ||
5 | * Author: Jin Park <jinyoungp@nvidia.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU General Public License | ||
9 | * version 2 as published by the Free Software Foundation. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, but | ||
12 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
14 | * General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
19 | * 02110-1301 USA | ||
20 | */ | ||
21 | |||
22 | #include <linux/kernel.h> | ||
23 | #include <linux/module.h> | ||
24 | #include <linux/init.h> | ||
25 | #include <linux/debugfs.h> | ||
26 | #include <linux/slab.h> | ||
27 | #include <linux/uaccess.h> | ||
28 | #include <linux/i2c.h> | ||
29 | #include <linux/delay.h> | ||
30 | #include <linux/gpio.h> | ||
31 | #include <linux/mfd/core.h> | ||
32 | #include <linux/mfd/aat2870.h> | ||
33 | #include <linux/regulator/machine.h> | ||
34 | |||
35 | static struct aat2870_register aat2870_regs[AAT2870_REG_NUM] = { | ||
36 | /* readable, writeable, value */ | ||
37 | { 0, 1, 0x00 }, /* 0x00 AAT2870_BL_CH_EN */ | ||
38 | { 0, 1, 0x16 }, /* 0x01 AAT2870_BLM */ | ||
39 | { 0, 1, 0x16 }, /* 0x02 AAT2870_BLS */ | ||
40 | { 0, 1, 0x56 }, /* 0x03 AAT2870_BL1 */ | ||
41 | { 0, 1, 0x56 }, /* 0x04 AAT2870_BL2 */ | ||
42 | { 0, 1, 0x56 }, /* 0x05 AAT2870_BL3 */ | ||
43 | { 0, 1, 0x56 }, /* 0x06 AAT2870_BL4 */ | ||
44 | { 0, 1, 0x56 }, /* 0x07 AAT2870_BL5 */ | ||
45 | { 0, 1, 0x56 }, /* 0x08 AAT2870_BL6 */ | ||
46 | { 0, 1, 0x56 }, /* 0x09 AAT2870_BL7 */ | ||
47 | { 0, 1, 0x56 }, /* 0x0A AAT2870_BL8 */ | ||
48 | { 0, 1, 0x00 }, /* 0x0B AAT2870_FLR */ | ||
49 | { 0, 1, 0x03 }, /* 0x0C AAT2870_FM */ | ||
50 | { 0, 1, 0x03 }, /* 0x0D AAT2870_FS */ | ||
51 | { 0, 1, 0x10 }, /* 0x0E AAT2870_ALS_CFG0 */ | ||
52 | { 0, 1, 0x06 }, /* 0x0F AAT2870_ALS_CFG1 */ | ||
53 | { 0, 1, 0x00 }, /* 0x10 AAT2870_ALS_CFG2 */ | ||
54 | { 1, 0, 0x00 }, /* 0x11 AAT2870_AMB */ | ||
55 | { 0, 1, 0x00 }, /* 0x12 AAT2870_ALS0 */ | ||
56 | { 0, 1, 0x00 }, /* 0x13 AAT2870_ALS1 */ | ||
57 | { 0, 1, 0x00 }, /* 0x14 AAT2870_ALS2 */ | ||
58 | { 0, 1, 0x00 }, /* 0x15 AAT2870_ALS3 */ | ||
59 | { 0, 1, 0x00 }, /* 0x16 AAT2870_ALS4 */ | ||
60 | { 0, 1, 0x00 }, /* 0x17 AAT2870_ALS5 */ | ||
61 | { 0, 1, 0x00 }, /* 0x18 AAT2870_ALS6 */ | ||
62 | { 0, 1, 0x00 }, /* 0x19 AAT2870_ALS7 */ | ||
63 | { 0, 1, 0x00 }, /* 0x1A AAT2870_ALS8 */ | ||
64 | { 0, 1, 0x00 }, /* 0x1B AAT2870_ALS9 */ | ||
65 | { 0, 1, 0x00 }, /* 0x1C AAT2870_ALSA */ | ||
66 | { 0, 1, 0x00 }, /* 0x1D AAT2870_ALSB */ | ||
67 | { 0, 1, 0x00 }, /* 0x1E AAT2870_ALSC */ | ||
68 | { 0, 1, 0x00 }, /* 0x1F AAT2870_ALSD */ | ||
69 | { 0, 1, 0x00 }, /* 0x20 AAT2870_ALSE */ | ||
70 | { 0, 1, 0x00 }, /* 0x21 AAT2870_ALSF */ | ||
71 | { 0, 1, 0x00 }, /* 0x22 AAT2870_SUB_SET */ | ||
72 | { 0, 1, 0x00 }, /* 0x23 AAT2870_SUB_CTRL */ | ||
73 | { 0, 1, 0x00 }, /* 0x24 AAT2870_LDO_AB */ | ||
74 | { 0, 1, 0x00 }, /* 0x25 AAT2870_LDO_CD */ | ||
75 | { 0, 1, 0x00 }, /* 0x26 AAT2870_LDO_EN */ | ||
76 | }; | ||
77 | |||
78 | static struct mfd_cell aat2870_devs[] = { | ||
79 | { | ||
80 | .name = "aat2870-backlight", | ||
81 | .id = AAT2870_ID_BL, | ||
82 | .pdata_size = sizeof(struct aat2870_bl_platform_data), | ||
83 | }, | ||
84 | { | ||
85 | .name = "aat2870-regulator", | ||
86 | .id = AAT2870_ID_LDOA, | ||
87 | .pdata_size = sizeof(struct regulator_init_data), | ||
88 | }, | ||
89 | { | ||
90 | .name = "aat2870-regulator", | ||
91 | .id = AAT2870_ID_LDOB, | ||
92 | .pdata_size = sizeof(struct regulator_init_data), | ||
93 | }, | ||
94 | { | ||
95 | .name = "aat2870-regulator", | ||
96 | .id = AAT2870_ID_LDOC, | ||
97 | .pdata_size = sizeof(struct regulator_init_data), | ||
98 | }, | ||
99 | { | ||
100 | .name = "aat2870-regulator", | ||
101 | .id = AAT2870_ID_LDOD, | ||
102 | .pdata_size = sizeof(struct regulator_init_data), | ||
103 | }, | ||
104 | }; | ||
105 | |||
106 | static int __aat2870_read(struct aat2870_data *aat2870, u8 addr, u8 *val) | ||
107 | { | ||
108 | int ret; | ||
109 | |||
110 | if (addr >= AAT2870_REG_NUM) { | ||
111 | dev_err(aat2870->dev, "Invalid address, 0x%02x\n", addr); | ||
112 | return -EINVAL; | ||
113 | } | ||
114 | |||
115 | if (!aat2870->reg_cache[addr].readable) { | ||
116 | *val = aat2870->reg_cache[addr].value; | ||
117 | goto out; | ||
118 | } | ||
119 | |||
120 | ret = i2c_master_send(aat2870->client, &addr, 1); | ||
121 | if (ret < 0) | ||
122 | return ret; | ||
123 | if (ret != 1) | ||
124 | return -EIO; | ||
125 | |||
126 | ret = i2c_master_recv(aat2870->client, val, 1); | ||
127 | if (ret < 0) | ||
128 | return ret; | ||
129 | if (ret != 1) | ||
130 | return -EIO; | ||
131 | |||
132 | out: | ||
133 | dev_dbg(aat2870->dev, "read: addr=0x%02x, val=0x%02x\n", addr, *val); | ||
134 | return 0; | ||
135 | } | ||
136 | |||
137 | static int __aat2870_write(struct aat2870_data *aat2870, u8 addr, u8 val) | ||
138 | { | ||
139 | u8 msg[2]; | ||
140 | int ret; | ||
141 | |||
142 | if (addr >= AAT2870_REG_NUM) { | ||
143 | dev_err(aat2870->dev, "Invalid address, 0x%02x\n", addr); | ||
144 | return -EINVAL; | ||
145 | } | ||
146 | |||
147 | if (!aat2870->reg_cache[addr].writeable) { | ||
148 | dev_err(aat2870->dev, "Address 0x%02x is not writeable\n", | ||
149 | addr); | ||
150 | return -EINVAL; | ||
151 | } | ||
152 | |||
153 | msg[0] = addr; | ||
154 | msg[1] = val; | ||
155 | ret = i2c_master_send(aat2870->client, msg, 2); | ||
156 | if (ret < 0) | ||
157 | return ret; | ||
158 | if (ret != 2) | ||
159 | return -EIO; | ||
160 | |||
161 | aat2870->reg_cache[addr].value = val; | ||
162 | |||
163 | dev_dbg(aat2870->dev, "write: addr=0x%02x, val=0x%02x\n", addr, val); | ||
164 | return 0; | ||
165 | } | ||
166 | |||
167 | static int aat2870_read(struct aat2870_data *aat2870, u8 addr, u8 *val) | ||
168 | { | ||
169 | int ret; | ||
170 | |||
171 | mutex_lock(&aat2870->io_lock); | ||
172 | ret = __aat2870_read(aat2870, addr, val); | ||
173 | mutex_unlock(&aat2870->io_lock); | ||
174 | |||
175 | return ret; | ||
176 | } | ||
177 | |||
178 | static int aat2870_write(struct aat2870_data *aat2870, u8 addr, u8 val) | ||
179 | { | ||
180 | int ret; | ||
181 | |||
182 | mutex_lock(&aat2870->io_lock); | ||
183 | ret = __aat2870_write(aat2870, addr, val); | ||
184 | mutex_unlock(&aat2870->io_lock); | ||
185 | |||
186 | return ret; | ||
187 | } | ||
188 | |||
189 | static int aat2870_update(struct aat2870_data *aat2870, u8 addr, u8 mask, | ||
190 | u8 val) | ||
191 | { | ||
192 | int change; | ||
193 | u8 old_val, new_val; | ||
194 | int ret; | ||
195 | |||
196 | mutex_lock(&aat2870->io_lock); | ||
197 | |||
198 | ret = __aat2870_read(aat2870, addr, &old_val); | ||
199 | if (ret) | ||
200 | goto out_unlock; | ||
201 | |||
202 | new_val = (old_val & ~mask) | (val & mask); | ||
203 | change = old_val != new_val; | ||
204 | if (change) | ||
205 | ret = __aat2870_write(aat2870, addr, new_val); | ||
206 | |||
207 | out_unlock: | ||
208 | mutex_unlock(&aat2870->io_lock); | ||
209 | |||
210 | return ret; | ||
211 | } | ||
212 | |||
213 | static inline void aat2870_enable(struct aat2870_data *aat2870) | ||
214 | { | ||
215 | if (aat2870->en_pin >= 0) | ||
216 | gpio_set_value(aat2870->en_pin, 1); | ||
217 | |||
218 | aat2870->is_enable = 1; | ||
219 | } | ||
220 | |||
221 | static inline void aat2870_disable(struct aat2870_data *aat2870) | ||
222 | { | ||
223 | if (aat2870->en_pin >= 0) | ||
224 | gpio_set_value(aat2870->en_pin, 0); | ||
225 | |||
226 | aat2870->is_enable = 0; | ||
227 | } | ||
228 | |||
229 | #ifdef CONFIG_DEBUG_FS | ||
230 | static ssize_t aat2870_dump_reg(struct aat2870_data *aat2870, char *buf) | ||
231 | { | ||
232 | u8 addr, val; | ||
233 | ssize_t count = 0; | ||
234 | int ret; | ||
235 | |||
236 | count += sprintf(buf, "aat2870 registers\n"); | ||
237 | for (addr = 0; addr < AAT2870_REG_NUM; addr++) { | ||
238 | count += sprintf(buf + count, "0x%02x: ", addr); | ||
239 | if (count >= PAGE_SIZE - 1) | ||
240 | break; | ||
241 | |||
242 | ret = aat2870->read(aat2870, addr, &val); | ||
243 | if (ret == 0) | ||
244 | count += snprintf(buf + count, PAGE_SIZE - count, | ||
245 | "0x%02x", val); | ||
246 | else | ||
247 | count += snprintf(buf + count, PAGE_SIZE - count, | ||
248 | "<read fail: %d>", ret); | ||
249 | |||
250 | if (count >= PAGE_SIZE - 1) | ||
251 | break; | ||
252 | |||
253 | count += snprintf(buf + count, PAGE_SIZE - count, "\n"); | ||
254 | if (count >= PAGE_SIZE - 1) | ||
255 | break; | ||
256 | } | ||
257 | |||
258 | /* Truncate count; min() would cause a warning */ | ||
259 | if (count >= PAGE_SIZE) | ||
260 | count = PAGE_SIZE - 1; | ||
261 | |||
262 | return count; | ||
263 | } | ||
264 | |||
265 | static int aat2870_reg_open_file(struct inode *inode, struct file *file) | ||
266 | { | ||
267 | file->private_data = inode->i_private; | ||
268 | |||
269 | return 0; | ||
270 | } | ||
271 | |||
272 | static ssize_t aat2870_reg_read_file(struct file *file, char __user *user_buf, | ||
273 | size_t count, loff_t *ppos) | ||
274 | { | ||
275 | struct aat2870_data *aat2870 = file->private_data; | ||
276 | char *buf; | ||
277 | ssize_t ret; | ||
278 | |||
279 | buf = kmalloc(PAGE_SIZE, GFP_KERNEL); | ||
280 | if (!buf) | ||
281 | return -ENOMEM; | ||
282 | |||
283 | ret = aat2870_dump_reg(aat2870, buf); | ||
284 | if (ret >= 0) | ||
285 | ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret); | ||
286 | |||
287 | kfree(buf); | ||
288 | |||
289 | return ret; | ||
290 | } | ||
291 | |||
292 | static ssize_t aat2870_reg_write_file(struct file *file, | ||
293 | const char __user *user_buf, size_t count, | ||
294 | loff_t *ppos) | ||
295 | { | ||
296 | struct aat2870_data *aat2870 = file->private_data; | ||
297 | char buf[32]; | ||
298 | int buf_size; | ||
299 | char *start = buf; | ||
300 | unsigned long addr, val; | ||
301 | int ret; | ||
302 | |||
303 | buf_size = min(count, (sizeof(buf)-1)); | ||
304 | if (copy_from_user(buf, user_buf, buf_size)) { | ||
305 | dev_err(aat2870->dev, "Failed to copy from user\n"); | ||
306 | return -EFAULT; | ||
307 | } | ||
308 | buf[buf_size] = 0; | ||
309 | |||
310 | while (*start == ' ') | ||
311 | start++; | ||
312 | |||
313 | addr = simple_strtoul(start, &start, 16); | ||
314 | if (addr >= AAT2870_REG_NUM) { | ||
315 | dev_err(aat2870->dev, "Invalid address, 0x%lx\n", addr); | ||
316 | return -EINVAL; | ||
317 | } | ||
318 | |||
319 | while (*start == ' ') | ||
320 | start++; | ||
321 | |||
322 | if (strict_strtoul(start, 16, &val)) | ||
323 | return -EINVAL; | ||
324 | |||
325 | ret = aat2870->write(aat2870, (u8)addr, (u8)val); | ||
326 | if (ret) | ||
327 | return ret; | ||
328 | |||
329 | return buf_size; | ||
330 | } | ||
331 | |||
332 | static const struct file_operations aat2870_reg_fops = { | ||
333 | .open = aat2870_reg_open_file, | ||
334 | .read = aat2870_reg_read_file, | ||
335 | .write = aat2870_reg_write_file, | ||
336 | }; | ||
337 | |||
338 | static void aat2870_init_debugfs(struct aat2870_data *aat2870) | ||
339 | { | ||
340 | aat2870->dentry_root = debugfs_create_dir("aat2870", NULL); | ||
341 | if (!aat2870->dentry_root) { | ||
342 | dev_warn(aat2870->dev, | ||
343 | "Failed to create debugfs root directory\n"); | ||
344 | return; | ||
345 | } | ||
346 | |||
347 | aat2870->dentry_reg = debugfs_create_file("regs", 0644, | ||
348 | aat2870->dentry_root, | ||
349 | aat2870, &aat2870_reg_fops); | ||
350 | if (!aat2870->dentry_reg) | ||
351 | dev_warn(aat2870->dev, | ||
352 | "Failed to create debugfs register file\n"); | ||
353 | } | ||
354 | |||
355 | static void aat2870_uninit_debugfs(struct aat2870_data *aat2870) | ||
356 | { | ||
357 | debugfs_remove_recursive(aat2870->dentry_root); | ||
358 | } | ||
359 | #else | ||
360 | static inline void aat2870_init_debugfs(struct aat2870_data *aat2870) | ||
361 | { | ||
362 | } | ||
363 | |||
364 | static inline void aat2870_uninit_debugfs(struct aat2870_data *aat2870) | ||
365 | { | ||
366 | } | ||
367 | #endif /* CONFIG_DEBUG_FS */ | ||
368 | |||
369 | static int aat2870_i2c_probe(struct i2c_client *client, | ||
370 | const struct i2c_device_id *id) | ||
371 | { | ||
372 | struct aat2870_platform_data *pdata = client->dev.platform_data; | ||
373 | struct aat2870_data *aat2870; | ||
374 | int i, j; | ||
375 | int ret = 0; | ||
376 | |||
377 | aat2870 = kzalloc(sizeof(struct aat2870_data), GFP_KERNEL); | ||
378 | if (!aat2870) { | ||
379 | dev_err(&client->dev, | ||
380 | "Failed to allocate memory for aat2870\n"); | ||
381 | ret = -ENOMEM; | ||
382 | goto out; | ||
383 | } | ||
384 | |||
385 | aat2870->dev = &client->dev; | ||
386 | dev_set_drvdata(aat2870->dev, aat2870); | ||
387 | |||
388 | aat2870->client = client; | ||
389 | i2c_set_clientdata(client, aat2870); | ||
390 | |||
391 | aat2870->reg_cache = aat2870_regs; | ||
392 | |||
393 | if (pdata->en_pin < 0) | ||
394 | aat2870->en_pin = -1; | ||
395 | else | ||
396 | aat2870->en_pin = pdata->en_pin; | ||
397 | |||
398 | aat2870->init = pdata->init; | ||
399 | aat2870->uninit = pdata->uninit; | ||
400 | aat2870->read = aat2870_read; | ||
401 | aat2870->write = aat2870_write; | ||
402 | aat2870->update = aat2870_update; | ||
403 | |||
404 | mutex_init(&aat2870->io_lock); | ||
405 | |||
406 | if (aat2870->init) | ||
407 | aat2870->init(aat2870); | ||
408 | |||
409 | if (aat2870->en_pin >= 0) { | ||
410 | ret = gpio_request(aat2870->en_pin, "aat2870-en"); | ||
411 | if (ret < 0) { | ||
412 | dev_err(&client->dev, | ||
413 | "Failed to request GPIO %d\n", aat2870->en_pin); | ||
414 | goto out_kfree; | ||
415 | } | ||
416 | gpio_direction_output(aat2870->en_pin, 1); | ||
417 | } | ||
418 | |||
419 | aat2870_enable(aat2870); | ||
420 | |||
421 | for (i = 0; i < pdata->num_subdevs; i++) { | ||
422 | for (j = 0; j < ARRAY_SIZE(aat2870_devs); j++) { | ||
423 | if ((pdata->subdevs[i].id == aat2870_devs[j].id) && | ||
424 | !strcmp(pdata->subdevs[i].name, | ||
425 | aat2870_devs[j].name)) { | ||
426 | aat2870_devs[j].platform_data = | ||
427 | pdata->subdevs[i].platform_data; | ||
428 | break; | ||
429 | } | ||
430 | } | ||
431 | } | ||
432 | |||
433 | ret = mfd_add_devices(aat2870->dev, 0, aat2870_devs, | ||
434 | ARRAY_SIZE(aat2870_devs), NULL, 0); | ||
435 | if (ret != 0) { | ||
436 | dev_err(aat2870->dev, "Failed to add subdev: %d\n", ret); | ||
437 | goto out_disable; | ||
438 | } | ||
439 | |||
440 | aat2870_init_debugfs(aat2870); | ||
441 | |||
442 | return 0; | ||
443 | |||
444 | out_disable: | ||
445 | aat2870_disable(aat2870); | ||
446 | if (aat2870->en_pin >= 0) | ||
447 | gpio_free(aat2870->en_pin); | ||
448 | out_kfree: | ||
449 | kfree(aat2870); | ||
450 | out: | ||
451 | return ret; | ||
452 | } | ||
453 | |||
454 | static int aat2870_i2c_remove(struct i2c_client *client) | ||
455 | { | ||
456 | struct aat2870_data *aat2870 = i2c_get_clientdata(client); | ||
457 | |||
458 | aat2870_uninit_debugfs(aat2870); | ||
459 | |||
460 | mfd_remove_devices(aat2870->dev); | ||
461 | aat2870_disable(aat2870); | ||
462 | if (aat2870->en_pin >= 0) | ||
463 | gpio_free(aat2870->en_pin); | ||
464 | if (aat2870->uninit) | ||
465 | aat2870->uninit(aat2870); | ||
466 | kfree(aat2870); | ||
467 | |||
468 | return 0; | ||
469 | } | ||
470 | |||
471 | #ifdef CONFIG_PM | ||
472 | static int aat2870_i2c_suspend(struct i2c_client *client, pm_message_t state) | ||
473 | { | ||
474 | struct aat2870_data *aat2870 = i2c_get_clientdata(client); | ||
475 | |||
476 | aat2870_disable(aat2870); | ||
477 | |||
478 | return 0; | ||
479 | } | ||
480 | |||
481 | static int aat2870_i2c_resume(struct i2c_client *client) | ||
482 | { | ||
483 | struct aat2870_data *aat2870 = i2c_get_clientdata(client); | ||
484 | struct aat2870_register *reg = NULL; | ||
485 | int i; | ||
486 | |||
487 | aat2870_enable(aat2870); | ||
488 | |||
489 | /* restore registers */ | ||
490 | for (i = 0; i < AAT2870_REG_NUM; i++) { | ||
491 | reg = &aat2870->reg_cache[i]; | ||
492 | if (reg->writeable) | ||
493 | aat2870->write(aat2870, i, reg->value); | ||
494 | } | ||
495 | |||
496 | return 0; | ||
497 | } | ||
498 | #else | ||
499 | #define aat2870_i2c_suspend NULL | ||
500 | #define aat2870_i2c_resume NULL | ||
501 | #endif /* CONFIG_PM */ | ||
502 | |||
503 | static struct i2c_device_id aat2870_i2c_id_table[] = { | ||
504 | { "aat2870", 0 }, | ||
505 | { } | ||
506 | }; | ||
507 | MODULE_DEVICE_TABLE(i2c, aat2870_i2c_id_table); | ||
508 | |||
509 | static struct i2c_driver aat2870_i2c_driver = { | ||
510 | .driver = { | ||
511 | .name = "aat2870", | ||
512 | .owner = THIS_MODULE, | ||
513 | }, | ||
514 | .probe = aat2870_i2c_probe, | ||
515 | .remove = aat2870_i2c_remove, | ||
516 | .suspend = aat2870_i2c_suspend, | ||
517 | .resume = aat2870_i2c_resume, | ||
518 | .id_table = aat2870_i2c_id_table, | ||
519 | }; | ||
520 | |||
521 | static int __init aat2870_init(void) | ||
522 | { | ||
523 | return i2c_add_driver(&aat2870_i2c_driver); | ||
524 | } | ||
525 | subsys_initcall(aat2870_init); | ||
526 | |||
527 | static void __exit aat2870_exit(void) | ||
528 | { | ||
529 | i2c_del_driver(&aat2870_i2c_driver); | ||
530 | } | ||
531 | module_exit(aat2870_exit); | ||
532 | |||
533 | MODULE_DESCRIPTION("Core support for the AnalogicTech AAT2870"); | ||
534 | MODULE_LICENSE("GPL"); | ||
535 | MODULE_AUTHOR("Jin Park <jinyoungp@nvidia.com>"); | ||