xref: /openbmc/linux/drivers/mfd/88pm860x-i2c.c (revision 53dbab7a)
1bbd51b1fSHaojian Zhuang /*
253dbab7aSHaojian Zhuang  * I2C driver for Marvell 88PM860x
3bbd51b1fSHaojian Zhuang  *
4bbd51b1fSHaojian Zhuang  * Copyright (C) 2009 Marvell International Ltd.
5bbd51b1fSHaojian Zhuang  * 	Haojian Zhuang <haojian.zhuang@marvell.com>
6bbd51b1fSHaojian Zhuang  *
7bbd51b1fSHaojian Zhuang  * This program is free software; you can redistribute it and/or modify
8bbd51b1fSHaojian Zhuang  * it under the terms of the GNU General Public License version 2 as
9bbd51b1fSHaojian Zhuang  * published by the Free Software Foundation.
10bbd51b1fSHaojian Zhuang  */
11bbd51b1fSHaojian Zhuang #include <linux/kernel.h>
12bbd51b1fSHaojian Zhuang #include <linux/module.h>
13bbd51b1fSHaojian Zhuang #include <linux/platform_device.h>
14bbd51b1fSHaojian Zhuang #include <linux/i2c.h>
1553dbab7aSHaojian Zhuang #include <linux/mfd/88pm860x.h>
16bbd51b1fSHaojian Zhuang 
1753dbab7aSHaojian Zhuang static inline int pm860x_read_device(struct i2c_client *i2c,
18bbd51b1fSHaojian Zhuang 				     int reg, int bytes, void *dest)
19bbd51b1fSHaojian Zhuang {
20bbd51b1fSHaojian Zhuang 	unsigned char data;
21bbd51b1fSHaojian Zhuang 	int ret;
22bbd51b1fSHaojian Zhuang 
23bbd51b1fSHaojian Zhuang 	data = (unsigned char)reg;
24bbd51b1fSHaojian Zhuang 	ret = i2c_master_send(i2c, &data, 1);
25bbd51b1fSHaojian Zhuang 	if (ret < 0)
26bbd51b1fSHaojian Zhuang 		return ret;
27bbd51b1fSHaojian Zhuang 
28bbd51b1fSHaojian Zhuang 	ret = i2c_master_recv(i2c, dest, bytes);
29bbd51b1fSHaojian Zhuang 	if (ret < 0)
30bbd51b1fSHaojian Zhuang 		return ret;
31bbd51b1fSHaojian Zhuang 	return 0;
32bbd51b1fSHaojian Zhuang }
33bbd51b1fSHaojian Zhuang 
3453dbab7aSHaojian Zhuang static inline int pm860x_write_device(struct i2c_client *i2c,
35bbd51b1fSHaojian Zhuang 				      int reg, int bytes, void *src)
36bbd51b1fSHaojian Zhuang {
37bbd51b1fSHaojian Zhuang 	unsigned char buf[bytes + 1];
38bbd51b1fSHaojian Zhuang 	int ret;
39bbd51b1fSHaojian Zhuang 
40bbd51b1fSHaojian Zhuang 	buf[0] = (unsigned char)reg;
41bbd51b1fSHaojian Zhuang 	memcpy(&buf[1], src, bytes);
42bbd51b1fSHaojian Zhuang 
43bbd51b1fSHaojian Zhuang 	ret = i2c_master_send(i2c, buf, bytes + 1);
44bbd51b1fSHaojian Zhuang 	if (ret < 0)
45bbd51b1fSHaojian Zhuang 		return ret;
46bbd51b1fSHaojian Zhuang 	return 0;
47bbd51b1fSHaojian Zhuang }
48bbd51b1fSHaojian Zhuang 
4953dbab7aSHaojian Zhuang int pm860x_reg_read(struct i2c_client *i2c, int reg)
50bbd51b1fSHaojian Zhuang {
5153dbab7aSHaojian Zhuang 	struct pm860x_chip *chip = i2c_get_clientdata(i2c);
52bbd51b1fSHaojian Zhuang 	unsigned char data;
53bbd51b1fSHaojian Zhuang 	int ret;
54bbd51b1fSHaojian Zhuang 
55bbd51b1fSHaojian Zhuang 	mutex_lock(&chip->io_lock);
5653dbab7aSHaojian Zhuang 	ret = pm860x_read_device(i2c, reg, 1, &data);
57bbd51b1fSHaojian Zhuang 	mutex_unlock(&chip->io_lock);
58bbd51b1fSHaojian Zhuang 
59bbd51b1fSHaojian Zhuang 	if (ret < 0)
60bbd51b1fSHaojian Zhuang 		return ret;
61bbd51b1fSHaojian Zhuang 	else
62bbd51b1fSHaojian Zhuang 		return (int)data;
63bbd51b1fSHaojian Zhuang }
6453dbab7aSHaojian Zhuang EXPORT_SYMBOL(pm860x_reg_read);
65bbd51b1fSHaojian Zhuang 
6653dbab7aSHaojian Zhuang int pm860x_reg_write(struct i2c_client *i2c, int reg,
67bbd51b1fSHaojian Zhuang 		     unsigned char data)
68bbd51b1fSHaojian Zhuang {
6953dbab7aSHaojian Zhuang 	struct pm860x_chip *chip = i2c_get_clientdata(i2c);
70bbd51b1fSHaojian Zhuang 	int ret;
71bbd51b1fSHaojian Zhuang 
72bbd51b1fSHaojian Zhuang 	mutex_lock(&chip->io_lock);
7353dbab7aSHaojian Zhuang 	ret = pm860x_write_device(i2c, reg, 1, &data);
74bbd51b1fSHaojian Zhuang 	mutex_unlock(&chip->io_lock);
75bbd51b1fSHaojian Zhuang 
76bbd51b1fSHaojian Zhuang 	return ret;
77bbd51b1fSHaojian Zhuang }
7853dbab7aSHaojian Zhuang EXPORT_SYMBOL(pm860x_reg_write);
79bbd51b1fSHaojian Zhuang 
8053dbab7aSHaojian Zhuang int pm860x_bulk_read(struct i2c_client *i2c, int reg,
81bbd51b1fSHaojian Zhuang 		     int count, unsigned char *buf)
82bbd51b1fSHaojian Zhuang {
8353dbab7aSHaojian Zhuang 	struct pm860x_chip *chip = i2c_get_clientdata(i2c);
84bbd51b1fSHaojian Zhuang 	int ret;
85bbd51b1fSHaojian Zhuang 
86bbd51b1fSHaojian Zhuang 	mutex_lock(&chip->io_lock);
8753dbab7aSHaojian Zhuang 	ret = pm860x_read_device(i2c, reg, count, buf);
88bbd51b1fSHaojian Zhuang 	mutex_unlock(&chip->io_lock);
89bbd51b1fSHaojian Zhuang 
90bbd51b1fSHaojian Zhuang 	return ret;
91bbd51b1fSHaojian Zhuang }
9253dbab7aSHaojian Zhuang EXPORT_SYMBOL(pm860x_bulk_read);
93bbd51b1fSHaojian Zhuang 
9453dbab7aSHaojian Zhuang int pm860x_bulk_write(struct i2c_client *i2c, int reg,
95bbd51b1fSHaojian Zhuang 		      int count, unsigned char *buf)
96bbd51b1fSHaojian Zhuang {
9753dbab7aSHaojian Zhuang 	struct pm860x_chip *chip = i2c_get_clientdata(i2c);
98bbd51b1fSHaojian Zhuang 	int ret;
99bbd51b1fSHaojian Zhuang 
100bbd51b1fSHaojian Zhuang 	mutex_lock(&chip->io_lock);
10153dbab7aSHaojian Zhuang 	ret = pm860x_write_device(i2c, reg, count, buf);
102bbd51b1fSHaojian Zhuang 	mutex_unlock(&chip->io_lock);
103bbd51b1fSHaojian Zhuang 
104bbd51b1fSHaojian Zhuang 	return ret;
105bbd51b1fSHaojian Zhuang }
10653dbab7aSHaojian Zhuang EXPORT_SYMBOL(pm860x_bulk_write);
107bbd51b1fSHaojian Zhuang 
10853dbab7aSHaojian Zhuang int pm860x_set_bits(struct i2c_client *i2c, int reg,
109bbd51b1fSHaojian Zhuang 		    unsigned char mask, unsigned char data)
110bbd51b1fSHaojian Zhuang {
11153dbab7aSHaojian Zhuang 	struct pm860x_chip *chip = i2c_get_clientdata(i2c);
112bbd51b1fSHaojian Zhuang 	unsigned char value;
113bbd51b1fSHaojian Zhuang 	int ret;
114bbd51b1fSHaojian Zhuang 
115bbd51b1fSHaojian Zhuang 	mutex_lock(&chip->io_lock);
11653dbab7aSHaojian Zhuang 	ret = pm860x_read_device(i2c, reg, 1, &value);
117bbd51b1fSHaojian Zhuang 	if (ret < 0)
118bbd51b1fSHaojian Zhuang 		goto out;
119bbd51b1fSHaojian Zhuang 	value &= ~mask;
120bbd51b1fSHaojian Zhuang 	value |= data;
12153dbab7aSHaojian Zhuang 	ret = pm860x_write_device(i2c, reg, 1, &value);
122bbd51b1fSHaojian Zhuang out:
123bbd51b1fSHaojian Zhuang 	mutex_unlock(&chip->io_lock);
124bbd51b1fSHaojian Zhuang 	return ret;
125bbd51b1fSHaojian Zhuang }
12653dbab7aSHaojian Zhuang EXPORT_SYMBOL(pm860x_set_bits);
127bbd51b1fSHaojian Zhuang 
128bbd51b1fSHaojian Zhuang 
129bbd51b1fSHaojian Zhuang static const struct i2c_device_id pm860x_id_table[] = {
13053dbab7aSHaojian Zhuang 	{ "88PM860x", 0 },
131bbd51b1fSHaojian Zhuang 	{}
132bbd51b1fSHaojian Zhuang };
133bbd51b1fSHaojian Zhuang MODULE_DEVICE_TABLE(i2c, pm860x_id_table);
134bbd51b1fSHaojian Zhuang 
13553dbab7aSHaojian Zhuang static int verify_addr(struct i2c_client *i2c)
13653dbab7aSHaojian Zhuang {
13753dbab7aSHaojian Zhuang 	unsigned short addr_8607[] = {0x30, 0x34};
13853dbab7aSHaojian Zhuang 	unsigned short addr_8606[] = {0x10, 0x11};
13953dbab7aSHaojian Zhuang 	int size, i;
14053dbab7aSHaojian Zhuang 
14153dbab7aSHaojian Zhuang 	if (i2c == NULL)
14253dbab7aSHaojian Zhuang 		return 0;
14353dbab7aSHaojian Zhuang 	size = ARRAY_SIZE(addr_8606);
14453dbab7aSHaojian Zhuang 	for (i = 0; i < size; i++) {
14553dbab7aSHaojian Zhuang 		if (i2c->addr == *(addr_8606 + i))
14653dbab7aSHaojian Zhuang 			return CHIP_PM8606;
14753dbab7aSHaojian Zhuang 	}
14853dbab7aSHaojian Zhuang 	size = ARRAY_SIZE(addr_8607);
14953dbab7aSHaojian Zhuang 	for (i = 0; i < size; i++) {
15053dbab7aSHaojian Zhuang 		if (i2c->addr == *(addr_8607 + i))
15153dbab7aSHaojian Zhuang 			return CHIP_PM8607;
15253dbab7aSHaojian Zhuang 	}
15353dbab7aSHaojian Zhuang 	return 0;
15453dbab7aSHaojian Zhuang }
15553dbab7aSHaojian Zhuang 
156bbd51b1fSHaojian Zhuang static int __devinit pm860x_probe(struct i2c_client *client,
157bbd51b1fSHaojian Zhuang 				  const struct i2c_device_id *id)
158bbd51b1fSHaojian Zhuang {
15953dbab7aSHaojian Zhuang 	struct pm860x_platform_data *pdata = client->dev.platform_data;
16053dbab7aSHaojian Zhuang 	static struct pm860x_chip *chip;
16153dbab7aSHaojian Zhuang 	struct i2c_board_info i2c_info = {
16253dbab7aSHaojian Zhuang 		.type		= "88PM860x",
16353dbab7aSHaojian Zhuang 		.platform_data	= client->dev.platform_data,
16453dbab7aSHaojian Zhuang 	};
16553dbab7aSHaojian Zhuang 	int addr_c, found_companion = 0;
166bbd51b1fSHaojian Zhuang 
16753dbab7aSHaojian Zhuang 	if (pdata == NULL) {
16853dbab7aSHaojian Zhuang 		pr_info("No platform data in %s!\n", __func__);
16953dbab7aSHaojian Zhuang 		return -EINVAL;
17053dbab7aSHaojian Zhuang 	}
17153dbab7aSHaojian Zhuang 
17253dbab7aSHaojian Zhuang 	/*
17353dbab7aSHaojian Zhuang 	 * Both client and companion client shares same platform driver.
17453dbab7aSHaojian Zhuang 	 * Driver distinguishes them by pdata->companion_addr.
17553dbab7aSHaojian Zhuang 	 * pdata->companion_addr is only assigned if companion chip exists.
17653dbab7aSHaojian Zhuang 	 * At the same time, the companion_addr shouldn't equal to client
17753dbab7aSHaojian Zhuang 	 * address.
17853dbab7aSHaojian Zhuang 	 */
17953dbab7aSHaojian Zhuang 	addr_c = pdata->companion_addr;
18053dbab7aSHaojian Zhuang 	if (addr_c && (addr_c != client->addr)) {
18153dbab7aSHaojian Zhuang 		i2c_info.addr = addr_c;
18253dbab7aSHaojian Zhuang 		found_companion = 1;
18353dbab7aSHaojian Zhuang 	}
18453dbab7aSHaojian Zhuang 
18553dbab7aSHaojian Zhuang 	if (found_companion || (addr_c == 0)) {
18653dbab7aSHaojian Zhuang 		chip = kzalloc(sizeof(struct pm860x_chip), GFP_KERNEL);
187bbd51b1fSHaojian Zhuang 		if (chip == NULL)
188bbd51b1fSHaojian Zhuang 			return -ENOMEM;
189bbd51b1fSHaojian Zhuang 
19053dbab7aSHaojian Zhuang 		chip->id = verify_addr(client);
19153dbab7aSHaojian Zhuang 		chip->companion_addr = addr_c;
192bbd51b1fSHaojian Zhuang 		chip->client = client;
193bbd51b1fSHaojian Zhuang 		i2c_set_clientdata(client, chip);
19453dbab7aSHaojian Zhuang 		chip->dev = &client->dev;
195bbd51b1fSHaojian Zhuang 		mutex_init(&chip->io_lock);
196bbd51b1fSHaojian Zhuang 		dev_set_drvdata(chip->dev, chip);
197bbd51b1fSHaojian Zhuang 
19853dbab7aSHaojian Zhuang 		if (found_companion) {
19953dbab7aSHaojian Zhuang 			/*
20053dbab7aSHaojian Zhuang 			 * If this driver is built in, probe function is
20153dbab7aSHaojian Zhuang 			 * recursive.
20253dbab7aSHaojian Zhuang 			 * If this driver is built as module, the next probe
20353dbab7aSHaojian Zhuang 			 * function is called after the first one finished.
20453dbab7aSHaojian Zhuang 			 */
20553dbab7aSHaojian Zhuang 			chip->companion = i2c_new_device(client->adapter,
20653dbab7aSHaojian Zhuang 							 &i2c_info);
20753dbab7aSHaojian Zhuang 		}
20853dbab7aSHaojian Zhuang 	}
209bbd51b1fSHaojian Zhuang 
21053dbab7aSHaojian Zhuang 	/*
21153dbab7aSHaojian Zhuang 	 * If companion chip existes, it's called by companion probe.
21253dbab7aSHaojian Zhuang 	 * If there's no companion chip, it's called by client probe.
21353dbab7aSHaojian Zhuang 	 */
21453dbab7aSHaojian Zhuang 	if ((addr_c == 0) || (addr_c == client->addr)) {
21553dbab7aSHaojian Zhuang 		chip->companion = client;
21653dbab7aSHaojian Zhuang 		i2c_set_clientdata(chip->companion, chip);
21753dbab7aSHaojian Zhuang 		pm860x_device_init(chip, pdata);
21853dbab7aSHaojian Zhuang 	}
219bbd51b1fSHaojian Zhuang 	return 0;
220bbd51b1fSHaojian Zhuang }
221bbd51b1fSHaojian Zhuang 
222bbd51b1fSHaojian Zhuang static int __devexit pm860x_remove(struct i2c_client *client)
223bbd51b1fSHaojian Zhuang {
22453dbab7aSHaojian Zhuang 	struct pm860x_chip *chip = i2c_get_clientdata(client);
225bbd51b1fSHaojian Zhuang 
22653dbab7aSHaojian Zhuang 	/*
22753dbab7aSHaojian Zhuang 	 * If companion existes, companion client is removed first.
22853dbab7aSHaojian Zhuang 	 * Because companion client is registered last and removed first.
22953dbab7aSHaojian Zhuang 	 */
23053dbab7aSHaojian Zhuang 	if (chip->companion_addr == client->addr)
23153dbab7aSHaojian Zhuang 		return 0;
23253dbab7aSHaojian Zhuang 	pm860x_device_exit(chip);
23353dbab7aSHaojian Zhuang 	i2c_unregister_device(chip->companion);
23453dbab7aSHaojian Zhuang 	i2c_set_clientdata(chip->companion, NULL);
23553dbab7aSHaojian Zhuang 	i2c_set_clientdata(chip->client, NULL);
236bbd51b1fSHaojian Zhuang 	kfree(chip);
237bbd51b1fSHaojian Zhuang 	return 0;
238bbd51b1fSHaojian Zhuang }
239bbd51b1fSHaojian Zhuang 
240bbd51b1fSHaojian Zhuang static struct i2c_driver pm860x_driver = {
241bbd51b1fSHaojian Zhuang 	.driver	= {
242bbd51b1fSHaojian Zhuang 		.name	= "88PM860x",
243bbd51b1fSHaojian Zhuang 		.owner	= THIS_MODULE,
244bbd51b1fSHaojian Zhuang 	},
245bbd51b1fSHaojian Zhuang 	.probe		= pm860x_probe,
246bbd51b1fSHaojian Zhuang 	.remove		= __devexit_p(pm860x_remove),
247bbd51b1fSHaojian Zhuang 	.id_table	= pm860x_id_table,
248bbd51b1fSHaojian Zhuang };
249bbd51b1fSHaojian Zhuang 
250bbd51b1fSHaojian Zhuang static int __init pm860x_i2c_init(void)
251bbd51b1fSHaojian Zhuang {
252bbd51b1fSHaojian Zhuang 	int ret;
253bbd51b1fSHaojian Zhuang 	ret = i2c_add_driver(&pm860x_driver);
254bbd51b1fSHaojian Zhuang 	if (ret != 0)
255bbd51b1fSHaojian Zhuang 		pr_err("Failed to register 88PM860x I2C driver: %d\n", ret);
256bbd51b1fSHaojian Zhuang 	return ret;
257bbd51b1fSHaojian Zhuang }
258bbd51b1fSHaojian Zhuang subsys_initcall(pm860x_i2c_init);
259bbd51b1fSHaojian Zhuang 
260bbd51b1fSHaojian Zhuang static void __exit pm860x_i2c_exit(void)
261bbd51b1fSHaojian Zhuang {
262bbd51b1fSHaojian Zhuang 	i2c_del_driver(&pm860x_driver);
263bbd51b1fSHaojian Zhuang }
264bbd51b1fSHaojian Zhuang module_exit(pm860x_i2c_exit);
265bbd51b1fSHaojian Zhuang 
266bbd51b1fSHaojian Zhuang MODULE_DESCRIPTION("I2C Driver for Marvell 88PM860x");
267bbd51b1fSHaojian Zhuang MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
268bbd51b1fSHaojian Zhuang MODULE_LICENSE("GPL");
269