Root/
1 | /* |
2 | * I2C driver for Marvell 88PM860x |
3 | * |
4 | * Copyright (C) 2009 Marvell International Ltd. |
5 | * Haojian Zhuang <haojian.zhuang@marvell.com> |
6 | * |
7 | * This program is free software; you can redistribute it and/or modify |
8 | * it under the terms of the GNU General Public License version 2 as |
9 | * published by the Free Software Foundation. |
10 | */ |
11 | #include <linux/kernel.h> |
12 | #include <linux/module.h> |
13 | #include <linux/platform_device.h> |
14 | #include <linux/i2c.h> |
15 | #include <linux/err.h> |
16 | #include <linux/regmap.h> |
17 | #include <linux/mfd/88pm860x.h> |
18 | #include <linux/slab.h> |
19 | |
20 | int pm860x_reg_read(struct i2c_client *i2c, int reg) |
21 | { |
22 | struct pm860x_chip *chip = i2c_get_clientdata(i2c); |
23 | struct regmap *map = (i2c == chip->client) ? chip->regmap |
24 | : chip->regmap_companion; |
25 | unsigned int data; |
26 | int ret; |
27 | |
28 | ret = regmap_read(map, reg, &data); |
29 | if (ret < 0) |
30 | return ret; |
31 | else |
32 | return (int)data; |
33 | } |
34 | EXPORT_SYMBOL(pm860x_reg_read); |
35 | |
36 | int pm860x_reg_write(struct i2c_client *i2c, int reg, |
37 | unsigned char data) |
38 | { |
39 | struct pm860x_chip *chip = i2c_get_clientdata(i2c); |
40 | struct regmap *map = (i2c == chip->client) ? chip->regmap |
41 | : chip->regmap_companion; |
42 | int ret; |
43 | |
44 | ret = regmap_write(map, reg, data); |
45 | return ret; |
46 | } |
47 | EXPORT_SYMBOL(pm860x_reg_write); |
48 | |
49 | int pm860x_bulk_read(struct i2c_client *i2c, int reg, |
50 | int count, unsigned char *buf) |
51 | { |
52 | struct pm860x_chip *chip = i2c_get_clientdata(i2c); |
53 | struct regmap *map = (i2c == chip->client) ? chip->regmap |
54 | : chip->regmap_companion; |
55 | int ret; |
56 | |
57 | ret = regmap_raw_read(map, reg, buf, count); |
58 | return ret; |
59 | } |
60 | EXPORT_SYMBOL(pm860x_bulk_read); |
61 | |
62 | int pm860x_bulk_write(struct i2c_client *i2c, int reg, |
63 | int count, unsigned char *buf) |
64 | { |
65 | struct pm860x_chip *chip = i2c_get_clientdata(i2c); |
66 | struct regmap *map = (i2c == chip->client) ? chip->regmap |
67 | : chip->regmap_companion; |
68 | int ret; |
69 | |
70 | ret = regmap_raw_write(map, reg, buf, count); |
71 | return ret; |
72 | } |
73 | EXPORT_SYMBOL(pm860x_bulk_write); |
74 | |
75 | int pm860x_set_bits(struct i2c_client *i2c, int reg, |
76 | unsigned char mask, unsigned char data) |
77 | { |
78 | struct pm860x_chip *chip = i2c_get_clientdata(i2c); |
79 | struct regmap *map = (i2c == chip->client) ? chip->regmap |
80 | : chip->regmap_companion; |
81 | int ret; |
82 | |
83 | ret = regmap_update_bits(map, reg, mask, data); |
84 | return ret; |
85 | } |
86 | EXPORT_SYMBOL(pm860x_set_bits); |
87 | |
88 | static int read_device(struct i2c_client *i2c, int reg, |
89 | int bytes, void *dest) |
90 | { |
91 | unsigned char msgbuf0[I2C_SMBUS_BLOCK_MAX + 3]; |
92 | unsigned char msgbuf1[I2C_SMBUS_BLOCK_MAX + 2]; |
93 | struct i2c_adapter *adap = i2c->adapter; |
94 | struct i2c_msg msg[2] = {{i2c->addr, 0, 1, msgbuf0}, |
95 | {i2c->addr, I2C_M_RD, 0, msgbuf1}, |
96 | }; |
97 | int num = 1, ret = 0; |
98 | |
99 | if (dest == NULL) |
100 | return -EINVAL; |
101 | msgbuf0[0] = (unsigned char)reg; /* command */ |
102 | msg[1].len = bytes; |
103 | |
104 | /* if data needs to read back, num should be 2 */ |
105 | if (bytes > 0) |
106 | num = 2; |
107 | ret = adap->algo->master_xfer(adap, msg, num); |
108 | memcpy(dest, msgbuf1, bytes); |
109 | if (ret < 0) |
110 | return ret; |
111 | return 0; |
112 | } |
113 | |
114 | static int write_device(struct i2c_client *i2c, int reg, |
115 | int bytes, void *src) |
116 | { |
117 | unsigned char buf[bytes + 1]; |
118 | struct i2c_adapter *adap = i2c->adapter; |
119 | struct i2c_msg msg; |
120 | int ret; |
121 | |
122 | buf[0] = (unsigned char)reg; |
123 | memcpy(&buf[1], src, bytes); |
124 | msg.addr = i2c->addr; |
125 | msg.flags = 0; |
126 | msg.len = bytes + 1; |
127 | msg.buf = buf; |
128 | |
129 | ret = adap->algo->master_xfer(adap, &msg, 1); |
130 | if (ret < 0) |
131 | return ret; |
132 | return 0; |
133 | } |
134 | |
135 | int pm860x_page_reg_read(struct i2c_client *i2c, int reg) |
136 | { |
137 | unsigned char zero = 0; |
138 | unsigned char data; |
139 | int ret; |
140 | |
141 | i2c_lock_adapter(i2c->adapter); |
142 | read_device(i2c, 0xFA, 0, &zero); |
143 | read_device(i2c, 0xFB, 0, &zero); |
144 | read_device(i2c, 0xFF, 0, &zero); |
145 | ret = read_device(i2c, reg, 1, &data); |
146 | if (ret >= 0) |
147 | ret = (int)data; |
148 | read_device(i2c, 0xFE, 0, &zero); |
149 | read_device(i2c, 0xFC, 0, &zero); |
150 | i2c_unlock_adapter(i2c->adapter); |
151 | return ret; |
152 | } |
153 | EXPORT_SYMBOL(pm860x_page_reg_read); |
154 | |
155 | int pm860x_page_reg_write(struct i2c_client *i2c, int reg, |
156 | unsigned char data) |
157 | { |
158 | unsigned char zero; |
159 | int ret; |
160 | |
161 | i2c_lock_adapter(i2c->adapter); |
162 | read_device(i2c, 0xFA, 0, &zero); |
163 | read_device(i2c, 0xFB, 0, &zero); |
164 | read_device(i2c, 0xFF, 0, &zero); |
165 | ret = write_device(i2c, reg, 1, &data); |
166 | read_device(i2c, 0xFE, 0, &zero); |
167 | read_device(i2c, 0xFC, 0, &zero); |
168 | i2c_unlock_adapter(i2c->adapter); |
169 | return ret; |
170 | } |
171 | EXPORT_SYMBOL(pm860x_page_reg_write); |
172 | |
173 | int pm860x_page_bulk_read(struct i2c_client *i2c, int reg, |
174 | int count, unsigned char *buf) |
175 | { |
176 | unsigned char zero = 0; |
177 | int ret; |
178 | |
179 | i2c_lock_adapter(i2c->adapter); |
180 | read_device(i2c, 0xfa, 0, &zero); |
181 | read_device(i2c, 0xfb, 0, &zero); |
182 | read_device(i2c, 0xff, 0, &zero); |
183 | ret = read_device(i2c, reg, count, buf); |
184 | read_device(i2c, 0xFE, 0, &zero); |
185 | read_device(i2c, 0xFC, 0, &zero); |
186 | i2c_unlock_adapter(i2c->adapter); |
187 | return ret; |
188 | } |
189 | EXPORT_SYMBOL(pm860x_page_bulk_read); |
190 | |
191 | int pm860x_page_bulk_write(struct i2c_client *i2c, int reg, |
192 | int count, unsigned char *buf) |
193 | { |
194 | unsigned char zero = 0; |
195 | int ret; |
196 | |
197 | i2c_lock_adapter(i2c->adapter); |
198 | read_device(i2c, 0xFA, 0, &zero); |
199 | read_device(i2c, 0xFB, 0, &zero); |
200 | read_device(i2c, 0xFF, 0, &zero); |
201 | ret = write_device(i2c, reg, count, buf); |
202 | read_device(i2c, 0xFE, 0, &zero); |
203 | read_device(i2c, 0xFC, 0, &zero); |
204 | i2c_unlock_adapter(i2c->adapter); |
205 | i2c_unlock_adapter(i2c->adapter); |
206 | return ret; |
207 | } |
208 | EXPORT_SYMBOL(pm860x_page_bulk_write); |
209 | |
210 | int pm860x_page_set_bits(struct i2c_client *i2c, int reg, |
211 | unsigned char mask, unsigned char data) |
212 | { |
213 | unsigned char zero; |
214 | unsigned char value; |
215 | int ret; |
216 | |
217 | i2c_lock_adapter(i2c->adapter); |
218 | read_device(i2c, 0xFA, 0, &zero); |
219 | read_device(i2c, 0xFB, 0, &zero); |
220 | read_device(i2c, 0xFF, 0, &zero); |
221 | ret = read_device(i2c, reg, 1, &value); |
222 | if (ret < 0) |
223 | goto out; |
224 | value &= ~mask; |
225 | value |= data; |
226 | ret = write_device(i2c, reg, 1, &value); |
227 | out: |
228 | read_device(i2c, 0xFE, 0, &zero); |
229 | read_device(i2c, 0xFC, 0, &zero); |
230 | i2c_unlock_adapter(i2c->adapter); |
231 | return ret; |
232 | } |
233 | EXPORT_SYMBOL(pm860x_page_set_bits); |
234 | |
235 | static const struct i2c_device_id pm860x_id_table[] = { |
236 | { "88PM860x", 0 }, |
237 | {} |
238 | }; |
239 | MODULE_DEVICE_TABLE(i2c, pm860x_id_table); |
240 | |
241 | static int verify_addr(struct i2c_client *i2c) |
242 | { |
243 | unsigned short addr_8607[] = {0x30, 0x34}; |
244 | unsigned short addr_8606[] = {0x10, 0x11}; |
245 | int size, i; |
246 | |
247 | if (i2c == NULL) |
248 | return 0; |
249 | size = ARRAY_SIZE(addr_8606); |
250 | for (i = 0; i < size; i++) { |
251 | if (i2c->addr == *(addr_8606 + i)) |
252 | return CHIP_PM8606; |
253 | } |
254 | size = ARRAY_SIZE(addr_8607); |
255 | for (i = 0; i < size; i++) { |
256 | if (i2c->addr == *(addr_8607 + i)) |
257 | return CHIP_PM8607; |
258 | } |
259 | return 0; |
260 | } |
261 | |
262 | static struct regmap_config pm860x_regmap_config = { |
263 | .reg_bits = 8, |
264 | .val_bits = 8, |
265 | }; |
266 | |
267 | static int __devinit pm860x_probe(struct i2c_client *client, |
268 | const struct i2c_device_id *id) |
269 | { |
270 | struct pm860x_platform_data *pdata = client->dev.platform_data; |
271 | struct pm860x_chip *chip; |
272 | int ret; |
273 | |
274 | if (!pdata) { |
275 | pr_info("No platform data in %s!\n", __func__); |
276 | return -EINVAL; |
277 | } |
278 | |
279 | chip = kzalloc(sizeof(struct pm860x_chip), GFP_KERNEL); |
280 | if (chip == NULL) |
281 | return -ENOMEM; |
282 | |
283 | chip->id = verify_addr(client); |
284 | chip->regmap = regmap_init_i2c(client, &pm860x_regmap_config); |
285 | if (IS_ERR(chip->regmap)) { |
286 | ret = PTR_ERR(chip->regmap); |
287 | dev_err(&client->dev, "Failed to allocate register map: %d\n", |
288 | ret); |
289 | kfree(chip); |
290 | return ret; |
291 | } |
292 | chip->client = client; |
293 | i2c_set_clientdata(client, chip); |
294 | chip->dev = &client->dev; |
295 | dev_set_drvdata(chip->dev, chip); |
296 | |
297 | /* |
298 | * Both client and companion client shares same platform driver. |
299 | * Driver distinguishes them by pdata->companion_addr. |
300 | * pdata->companion_addr is only assigned if companion chip exists. |
301 | * At the same time, the companion_addr shouldn't equal to client |
302 | * address. |
303 | */ |
304 | if (pdata->companion_addr && (pdata->companion_addr != client->addr)) { |
305 | chip->companion_addr = pdata->companion_addr; |
306 | chip->companion = i2c_new_dummy(chip->client->adapter, |
307 | chip->companion_addr); |
308 | chip->regmap_companion = regmap_init_i2c(chip->companion, |
309 | &pm860x_regmap_config); |
310 | if (IS_ERR(chip->regmap_companion)) { |
311 | ret = PTR_ERR(chip->regmap_companion); |
312 | dev_err(&chip->companion->dev, |
313 | "Failed to allocate register map: %d\n", ret); |
314 | return ret; |
315 | } |
316 | i2c_set_clientdata(chip->companion, chip); |
317 | } |
318 | |
319 | pm860x_device_init(chip, pdata); |
320 | return 0; |
321 | } |
322 | |
323 | static int __devexit pm860x_remove(struct i2c_client *client) |
324 | { |
325 | struct pm860x_chip *chip = i2c_get_clientdata(client); |
326 | |
327 | pm860x_device_exit(chip); |
328 | if (chip->companion) { |
329 | regmap_exit(chip->regmap_companion); |
330 | i2c_unregister_device(chip->companion); |
331 | } |
332 | regmap_exit(chip->regmap); |
333 | kfree(chip); |
334 | return 0; |
335 | } |
336 | |
337 | #ifdef CONFIG_PM_SLEEP |
338 | static int pm860x_suspend(struct device *dev) |
339 | { |
340 | struct i2c_client *client = container_of(dev, struct i2c_client, dev); |
341 | struct pm860x_chip *chip = i2c_get_clientdata(client); |
342 | |
343 | if (device_may_wakeup(dev) && chip->wakeup_flag) |
344 | enable_irq_wake(chip->core_irq); |
345 | return 0; |
346 | } |
347 | |
348 | static int pm860x_resume(struct device *dev) |
349 | { |
350 | struct i2c_client *client = container_of(dev, struct i2c_client, dev); |
351 | struct pm860x_chip *chip = i2c_get_clientdata(client); |
352 | |
353 | if (device_may_wakeup(dev) && chip->wakeup_flag) |
354 | disable_irq_wake(chip->core_irq); |
355 | return 0; |
356 | } |
357 | #endif |
358 | |
359 | static SIMPLE_DEV_PM_OPS(pm860x_pm_ops, pm860x_suspend, pm860x_resume); |
360 | |
361 | static struct i2c_driver pm860x_driver = { |
362 | .driver = { |
363 | .name = "88PM860x", |
364 | .owner = THIS_MODULE, |
365 | .pm = &pm860x_pm_ops, |
366 | }, |
367 | .probe = pm860x_probe, |
368 | .remove = __devexit_p(pm860x_remove), |
369 | .id_table = pm860x_id_table, |
370 | }; |
371 | |
372 | static int __init pm860x_i2c_init(void) |
373 | { |
374 | int ret; |
375 | ret = i2c_add_driver(&pm860x_driver); |
376 | if (ret != 0) |
377 | pr_err("Failed to register 88PM860x I2C driver: %d\n", ret); |
378 | return ret; |
379 | } |
380 | subsys_initcall(pm860x_i2c_init); |
381 | |
382 | static void __exit pm860x_i2c_exit(void) |
383 | { |
384 | i2c_del_driver(&pm860x_driver); |
385 | } |
386 | module_exit(pm860x_i2c_exit); |
387 | |
388 | MODULE_DESCRIPTION("I2C Driver for Marvell 88PM860x"); |
389 | MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>"); |
390 | MODULE_LICENSE("GPL"); |
391 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9