blob: f93dd9571c3c81e8b44f26dc51d54b0901039cd6 [file] [log] [blame]
Haojian Zhuangbbd51b12010-01-06 17:04:18 -05001/*
Haojian Zhuang53dbab72010-01-08 06:01:24 -05002 * I2C driver for Marvell 88PM860x
Haojian Zhuangbbd51b12010-01-06 17:04:18 -05003 *
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>
Jett.Zhoub46a36c2011-11-11 15:38:27 +080015#include <linux/err.h>
16#include <linux/regmap.h>
Haojian Zhuang53dbab72010-01-08 06:01:24 -050017#include <linux/mfd/88pm860x.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090018#include <linux/slab.h>
Haojian Zhuangbbd51b12010-01-06 17:04:18 -050019
Haojian Zhuang53dbab72010-01-08 06:01:24 -050020int pm860x_reg_read(struct i2c_client *i2c, int reg)
Haojian Zhuangbbd51b12010-01-06 17:04:18 -050021{
Haojian Zhuang53dbab72010-01-08 06:01:24 -050022 struct pm860x_chip *chip = i2c_get_clientdata(i2c);
Jett.Zhoub46a36c2011-11-11 15:38:27 +080023 struct regmap *map = (i2c == chip->client) ? chip->regmap
24 : chip->regmap_companion;
25 unsigned int data;
Haojian Zhuangbbd51b12010-01-06 17:04:18 -050026 int ret;
27
Jett.Zhoub46a36c2011-11-11 15:38:27 +080028 ret = regmap_read(map, reg, &data);
Haojian Zhuangbbd51b12010-01-06 17:04:18 -050029 if (ret < 0)
30 return ret;
31 else
32 return (int)data;
33}
Haojian Zhuang53dbab72010-01-08 06:01:24 -050034EXPORT_SYMBOL(pm860x_reg_read);
Haojian Zhuangbbd51b12010-01-06 17:04:18 -050035
Haojian Zhuang53dbab72010-01-08 06:01:24 -050036int pm860x_reg_write(struct i2c_client *i2c, int reg,
Haojian Zhuangbbd51b12010-01-06 17:04:18 -050037 unsigned char data)
38{
Haojian Zhuang53dbab72010-01-08 06:01:24 -050039 struct pm860x_chip *chip = i2c_get_clientdata(i2c);
Jett.Zhoub46a36c2011-11-11 15:38:27 +080040 struct regmap *map = (i2c == chip->client) ? chip->regmap
41 : chip->regmap_companion;
Haojian Zhuangbbd51b12010-01-06 17:04:18 -050042 int ret;
43
Jett.Zhoub46a36c2011-11-11 15:38:27 +080044 ret = regmap_write(map, reg, data);
Haojian Zhuangbbd51b12010-01-06 17:04:18 -050045 return ret;
46}
Haojian Zhuang53dbab72010-01-08 06:01:24 -050047EXPORT_SYMBOL(pm860x_reg_write);
Haojian Zhuangbbd51b12010-01-06 17:04:18 -050048
Haojian Zhuang53dbab72010-01-08 06:01:24 -050049int pm860x_bulk_read(struct i2c_client *i2c, int reg,
Haojian Zhuangbbd51b12010-01-06 17:04:18 -050050 int count, unsigned char *buf)
51{
Haojian Zhuang53dbab72010-01-08 06:01:24 -050052 struct pm860x_chip *chip = i2c_get_clientdata(i2c);
Jett.Zhoub46a36c2011-11-11 15:38:27 +080053 struct regmap *map = (i2c == chip->client) ? chip->regmap
54 : chip->regmap_companion;
Haojian Zhuangbbd51b12010-01-06 17:04:18 -050055 int ret;
56
Jett.Zhoub46a36c2011-11-11 15:38:27 +080057 ret = regmap_raw_read(map, reg, buf, count);
Haojian Zhuangbbd51b12010-01-06 17:04:18 -050058 return ret;
59}
Haojian Zhuang53dbab72010-01-08 06:01:24 -050060EXPORT_SYMBOL(pm860x_bulk_read);
Haojian Zhuangbbd51b12010-01-06 17:04:18 -050061
Haojian Zhuang53dbab72010-01-08 06:01:24 -050062int pm860x_bulk_write(struct i2c_client *i2c, int reg,
Haojian Zhuangbbd51b12010-01-06 17:04:18 -050063 int count, unsigned char *buf)
64{
Haojian Zhuang53dbab72010-01-08 06:01:24 -050065 struct pm860x_chip *chip = i2c_get_clientdata(i2c);
Jett.Zhoub46a36c2011-11-11 15:38:27 +080066 struct regmap *map = (i2c == chip->client) ? chip->regmap
67 : chip->regmap_companion;
Haojian Zhuangbbd51b12010-01-06 17:04:18 -050068 int ret;
69
Jett.Zhoub46a36c2011-11-11 15:38:27 +080070 ret = regmap_raw_write(map, reg, buf, count);
Haojian Zhuangbbd51b12010-01-06 17:04:18 -050071 return ret;
72}
Haojian Zhuang53dbab72010-01-08 06:01:24 -050073EXPORT_SYMBOL(pm860x_bulk_write);
Haojian Zhuangbbd51b12010-01-06 17:04:18 -050074
Haojian Zhuang53dbab72010-01-08 06:01:24 -050075int pm860x_set_bits(struct i2c_client *i2c, int reg,
Haojian Zhuangbbd51b12010-01-06 17:04:18 -050076 unsigned char mask, unsigned char data)
77{
Haojian Zhuang53dbab72010-01-08 06:01:24 -050078 struct pm860x_chip *chip = i2c_get_clientdata(i2c);
Jett.Zhoub46a36c2011-11-11 15:38:27 +080079 struct regmap *map = (i2c == chip->client) ? chip->regmap
80 : chip->regmap_companion;
Haojian Zhuangbbd51b12010-01-06 17:04:18 -050081 int ret;
82
Jett.Zhoub46a36c2011-11-11 15:38:27 +080083 ret = regmap_update_bits(map, reg, mask, data);
Haojian Zhuangbbd51b12010-01-06 17:04:18 -050084 return ret;
85}
Haojian Zhuang53dbab72010-01-08 06:01:24 -050086EXPORT_SYMBOL(pm860x_set_bits);
Haojian Zhuangbbd51b12010-01-06 17:04:18 -050087
Jett.Zhou5bdf7412011-11-11 15:38:26 +080088static 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
114static 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
Haojian Zhuang09b03412011-03-07 23:43:16 +0800135int pm860x_page_reg_read(struct i2c_client *i2c, int reg)
136{
Haojian Zhuang09b03412011-03-07 23:43:16 +0800137 unsigned char zero = 0;
138 unsigned char data;
139 int ret;
140
Jett.Zhou5bdf7412011-11-11 15:38:26 +0800141 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);
Haojian Zhuang09b03412011-03-07 23:43:16 +0800146 if (ret >= 0)
147 ret = (int)data;
Jett.Zhou5bdf7412011-11-11 15:38:26 +0800148 read_device(i2c, 0xFE, 0, &zero);
149 read_device(i2c, 0xFC, 0, &zero);
150 i2c_unlock_adapter(i2c->adapter);
Haojian Zhuang09b03412011-03-07 23:43:16 +0800151 return ret;
152}
153EXPORT_SYMBOL(pm860x_page_reg_read);
154
155int pm860x_page_reg_write(struct i2c_client *i2c, int reg,
156 unsigned char data)
157{
Haojian Zhuang09b03412011-03-07 23:43:16 +0800158 unsigned char zero;
159 int ret;
160
Jett.Zhou5bdf7412011-11-11 15:38:26 +0800161 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);
Haojian Zhuang09b03412011-03-07 23:43:16 +0800169 return ret;
170}
171EXPORT_SYMBOL(pm860x_page_reg_write);
172
173int pm860x_page_bulk_read(struct i2c_client *i2c, int reg,
174 int count, unsigned char *buf)
175{
Haojian Zhuang09b03412011-03-07 23:43:16 +0800176 unsigned char zero = 0;
177 int ret;
178
Jett.Zhou5bdf7412011-11-11 15:38:26 +0800179 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);
Haojian Zhuang09b03412011-03-07 23:43:16 +0800187 return ret;
188}
189EXPORT_SYMBOL(pm860x_page_bulk_read);
190
191int pm860x_page_bulk_write(struct i2c_client *i2c, int reg,
192 int count, unsigned char *buf)
193{
Haojian Zhuang09b03412011-03-07 23:43:16 +0800194 unsigned char zero = 0;
195 int ret;
196
Jett.Zhou5bdf7412011-11-11 15:38:26 +0800197 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);
Haojian Zhuang09b03412011-03-07 23:43:16 +0800206 return ret;
207}
208EXPORT_SYMBOL(pm860x_page_bulk_write);
209
210int pm860x_page_set_bits(struct i2c_client *i2c, int reg,
211 unsigned char mask, unsigned char data)
212{
Haojian Zhuang09b03412011-03-07 23:43:16 +0800213 unsigned char zero;
214 unsigned char value;
215 int ret;
216
Jett.Zhou5bdf7412011-11-11 15:38:26 +0800217 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);
Haojian Zhuang09b03412011-03-07 23:43:16 +0800222 if (ret < 0)
223 goto out;
224 value &= ~mask;
225 value |= data;
Jett.Zhou5bdf7412011-11-11 15:38:26 +0800226 ret = write_device(i2c, reg, 1, &value);
Haojian Zhuang09b03412011-03-07 23:43:16 +0800227out:
Jett.Zhou5bdf7412011-11-11 15:38:26 +0800228 read_device(i2c, 0xFE, 0, &zero);
229 read_device(i2c, 0xFC, 0, &zero);
230 i2c_unlock_adapter(i2c->adapter);
Haojian Zhuang09b03412011-03-07 23:43:16 +0800231 return ret;
232}
233EXPORT_SYMBOL(pm860x_page_set_bits);
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500234
235static const struct i2c_device_id pm860x_id_table[] = {
Haojian Zhuang53dbab72010-01-08 06:01:24 -0500236 { "88PM860x", 0 },
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500237 {}
238};
239MODULE_DEVICE_TABLE(i2c, pm860x_id_table);
240
Haojian Zhuang53dbab72010-01-08 06:01:24 -0500241static 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
Jett.Zhoub46a36c2011-11-11 15:38:27 +0800262static struct regmap_config pm860x_regmap_config = {
263 .reg_bits = 8,
264 .val_bits = 8,
265};
266
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500267static int __devinit pm860x_probe(struct i2c_client *client,
268 const struct i2c_device_id *id)
269{
Haojian Zhuang53dbab72010-01-08 06:01:24 -0500270 struct pm860x_platform_data *pdata = client->dev.platform_data;
Haojian Zhuange8343dd2010-02-03 15:38:12 -0500271 struct pm860x_chip *chip;
Jett.Zhoub46a36c2011-11-11 15:38:27 +0800272 int ret;
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500273
Haojian Zhuange8343dd2010-02-03 15:38:12 -0500274 if (!pdata) {
Haojian Zhuang53dbab72010-01-08 06:01:24 -0500275 pr_info("No platform data in %s!\n", __func__);
276 return -EINVAL;
277 }
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500278
Haojian Zhuange8343dd2010-02-03 15:38:12 -0500279 chip = kzalloc(sizeof(struct pm860x_chip), GFP_KERNEL);
280 if (chip == NULL)
281 return -ENOMEM;
282
283 chip->id = verify_addr(client);
Jett.Zhoub46a36c2011-11-11 15:38:27 +0800284 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);
Julia Lawalle3380332011-12-23 18:39:26 +0100289 kfree(chip);
Jett.Zhoub46a36c2011-11-11 15:38:27 +0800290 return ret;
291 }
Haojian Zhuange8343dd2010-02-03 15:38:12 -0500292 chip->client = client;
293 i2c_set_clientdata(client, chip);
294 chip->dev = &client->dev;
Haojian Zhuange8343dd2010-02-03 15:38:12 -0500295 dev_set_drvdata(chip->dev, chip);
296
Haojian Zhuang53dbab72010-01-08 06:01:24 -0500297 /*
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 */
Haojian Zhuange8343dd2010-02-03 15:38:12 -0500304 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);
Jett.Zhoub46a36c2011-11-11 15:38:27 +0800308 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 }
Haojian Zhuang53dbab72010-01-08 06:01:24 -0500316 i2c_set_clientdata(chip->companion, chip);
Haojian Zhuang53dbab72010-01-08 06:01:24 -0500317 }
Haojian Zhuange8343dd2010-02-03 15:38:12 -0500318
319 pm860x_device_init(chip, pdata);
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500320 return 0;
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500321}
322
323static int __devexit pm860x_remove(struct i2c_client *client)
324{
Haojian Zhuang53dbab72010-01-08 06:01:24 -0500325 struct pm860x_chip *chip = i2c_get_clientdata(client);
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500326
Haojian Zhuang53dbab72010-01-08 06:01:24 -0500327 pm860x_device_exit(chip);
Jett.Zhoub46a36c2011-11-11 15:38:27 +0800328 if (chip->companion) {
329 regmap_exit(chip->regmap_companion);
330 i2c_unregister_device(chip->companion);
331 }
332 regmap_exit(chip->regmap);
Haojian Zhuangbbd51b12010-01-06 17:04:18 -0500333 kfree(chip);
334 return 0;
335}
336
337static struct i2c_driver pm860x_driver = {
338 .driver = {
339 .name = "88PM860x",
340 .owner = THIS_MODULE,
341 },
342 .probe = pm860x_probe,
343 .remove = __devexit_p(pm860x_remove),
344 .id_table = pm860x_id_table,
345};
346
347static int __init pm860x_i2c_init(void)
348{
349 int ret;
350 ret = i2c_add_driver(&pm860x_driver);
351 if (ret != 0)
352 pr_err("Failed to register 88PM860x I2C driver: %d\n", ret);
353 return ret;
354}
355subsys_initcall(pm860x_i2c_init);
356
357static void __exit pm860x_i2c_exit(void)
358{
359 i2c_del_driver(&pm860x_driver);
360}
361module_exit(pm860x_i2c_exit);
362
363MODULE_DESCRIPTION("I2C Driver for Marvell 88PM860x");
364MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
365MODULE_LICENSE("GPL");