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 
pm860x_reg_read(struct i2c_client * i2c,int reg)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 
pm860x_reg_write(struct i2c_client * i2c,int reg,unsigned char data)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 
pm860x_bulk_read(struct i2c_client * i2c,int reg,int count,unsigned char * buf)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 
pm860x_bulk_write(struct i2c_client * i2c,int reg,int count,unsigned char * buf)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 
pm860x_set_bits(struct i2c_client * i2c,int reg,unsigned char mask,unsigned char data)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 
read_device(struct i2c_client * i2c,int reg,int bytes,void * dest)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 
write_device(struct i2c_client * i2c,int reg,int bytes,void * src)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 
pm860x_page_reg_read(struct i2c_client * i2c,int reg)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 
pm860x_page_reg_write(struct i2c_client * i2c,int reg,unsigned char data)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 
pm860x_page_bulk_read(struct i2c_client * i2c,int reg,int count,unsigned char * buf)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 
pm860x_page_bulk_write(struct i2c_client * i2c,int reg,int count,unsigned char * buf)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 
pm860x_page_set_bits(struct i2c_client * i2c,int reg,unsigned char mask,unsigned char data)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 
verify_addr(struct i2c_client * i2c)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 
pm860x_probe(struct i2c_client * client,const struct i2c_device_id * id)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 
pm860x_remove(struct i2c_client * client)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
pm860x_suspend(struct device * dev)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 
pm860x_resume(struct device * dev)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 
pm860x_i2c_init(void)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 
pm860x_i2c_exit(void)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