Blame view

drivers/mfd/88pm860x-i2c.c 8.93 KB
bbd51b1ff   Haojian Zhuang   mfd: Split 88pm86...
1
  /*
53dbab7af   Haojian Zhuang   mfd: Support 88pm...
2
   * I2C driver for Marvell 88PM860x
bbd51b1ff   Haojian Zhuang   mfd: Split 88pm86...
3
4
5
6
7
8
9
10
11
12
13
14
   *
   * Copyright (C) 2009 Marvell International Ltd.
   * 	Haojian Zhuang <haojian.zhuang@marvell.com>
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License version 2 as
   * published by the Free Software Foundation.
   */
  #include <linux/kernel.h>
  #include <linux/module.h>
  #include <linux/platform_device.h>
  #include <linux/i2c.h>
b46a36c0e   Jett.Zhou   mfd: Convert 88pm...
15
16
  #include <linux/err.h>
  #include <linux/regmap.h>
53dbab7af   Haojian Zhuang   mfd: Support 88pm...
17
  #include <linux/mfd/88pm860x.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
18
  #include <linux/slab.h>
bbd51b1ff   Haojian Zhuang   mfd: Split 88pm86...
19

53dbab7af   Haojian Zhuang   mfd: Support 88pm...
20
  int pm860x_reg_read(struct i2c_client *i2c, int reg)
bbd51b1ff   Haojian Zhuang   mfd: Split 88pm86...
21
  {
53dbab7af   Haojian Zhuang   mfd: Support 88pm...
22
  	struct pm860x_chip *chip = i2c_get_clientdata(i2c);
b46a36c0e   Jett.Zhou   mfd: Convert 88pm...
23
24
25
  	struct regmap *map = (i2c == chip->client) ? chip->regmap
  				: chip->regmap_companion;
  	unsigned int data;
bbd51b1ff   Haojian Zhuang   mfd: Split 88pm86...
26
  	int ret;
b46a36c0e   Jett.Zhou   mfd: Convert 88pm...
27
  	ret = regmap_read(map, reg, &data);
bbd51b1ff   Haojian Zhuang   mfd: Split 88pm86...
28
29
30
31
32
  	if (ret < 0)
  		return ret;
  	else
  		return (int)data;
  }
53dbab7af   Haojian Zhuang   mfd: Support 88pm...
33
  EXPORT_SYMBOL(pm860x_reg_read);
bbd51b1ff   Haojian Zhuang   mfd: Split 88pm86...
34

53dbab7af   Haojian Zhuang   mfd: Support 88pm...
35
  int pm860x_reg_write(struct i2c_client *i2c, int reg,
bbd51b1ff   Haojian Zhuang   mfd: Split 88pm86...
36
37
  		     unsigned char data)
  {
53dbab7af   Haojian Zhuang   mfd: Support 88pm...
38
  	struct pm860x_chip *chip = i2c_get_clientdata(i2c);
b46a36c0e   Jett.Zhou   mfd: Convert 88pm...
39
40
  	struct regmap *map = (i2c == chip->client) ? chip->regmap
  				: chip->regmap_companion;
bbd51b1ff   Haojian Zhuang   mfd: Split 88pm86...
41
  	int ret;
b46a36c0e   Jett.Zhou   mfd: Convert 88pm...
42
  	ret = regmap_write(map, reg, data);
bbd51b1ff   Haojian Zhuang   mfd: Split 88pm86...
43
44
  	return ret;
  }
53dbab7af   Haojian Zhuang   mfd: Support 88pm...
45
  EXPORT_SYMBOL(pm860x_reg_write);
bbd51b1ff   Haojian Zhuang   mfd: Split 88pm86...
46

53dbab7af   Haojian Zhuang   mfd: Support 88pm...
47
  int pm860x_bulk_read(struct i2c_client *i2c, int reg,
bbd51b1ff   Haojian Zhuang   mfd: Split 88pm86...
48
49
  		     int count, unsigned char *buf)
  {
53dbab7af   Haojian Zhuang   mfd: Support 88pm...
50
  	struct pm860x_chip *chip = i2c_get_clientdata(i2c);
b46a36c0e   Jett.Zhou   mfd: Convert 88pm...
51
52
  	struct regmap *map = (i2c == chip->client) ? chip->regmap
  				: chip->regmap_companion;
bbd51b1ff   Haojian Zhuang   mfd: Split 88pm86...
53
  	int ret;
b46a36c0e   Jett.Zhou   mfd: Convert 88pm...
54
  	ret = regmap_raw_read(map, reg, buf, count);
bbd51b1ff   Haojian Zhuang   mfd: Split 88pm86...
55
56
  	return ret;
  }
53dbab7af   Haojian Zhuang   mfd: Support 88pm...
57
  EXPORT_SYMBOL(pm860x_bulk_read);
bbd51b1ff   Haojian Zhuang   mfd: Split 88pm86...
58

53dbab7af   Haojian Zhuang   mfd: Support 88pm...
59
  int pm860x_bulk_write(struct i2c_client *i2c, int reg,
bbd51b1ff   Haojian Zhuang   mfd: Split 88pm86...
60
61
  		      int count, unsigned char *buf)
  {
53dbab7af   Haojian Zhuang   mfd: Support 88pm...
62
  	struct pm860x_chip *chip = i2c_get_clientdata(i2c);
b46a36c0e   Jett.Zhou   mfd: Convert 88pm...
63
64
  	struct regmap *map = (i2c == chip->client) ? chip->regmap
  				: chip->regmap_companion;
bbd51b1ff   Haojian Zhuang   mfd: Split 88pm86...
65
  	int ret;
b46a36c0e   Jett.Zhou   mfd: Convert 88pm...
66
  	ret = regmap_raw_write(map, reg, buf, count);
bbd51b1ff   Haojian Zhuang   mfd: Split 88pm86...
67
68
  	return ret;
  }
53dbab7af   Haojian Zhuang   mfd: Support 88pm...
69
  EXPORT_SYMBOL(pm860x_bulk_write);
bbd51b1ff   Haojian Zhuang   mfd: Split 88pm86...
70

53dbab7af   Haojian Zhuang   mfd: Support 88pm...
71
  int pm860x_set_bits(struct i2c_client *i2c, int reg,
bbd51b1ff   Haojian Zhuang   mfd: Split 88pm86...
72
73
  		    unsigned char mask, unsigned char data)
  {
53dbab7af   Haojian Zhuang   mfd: Support 88pm...
74
  	struct pm860x_chip *chip = i2c_get_clientdata(i2c);
b46a36c0e   Jett.Zhou   mfd: Convert 88pm...
75
76
  	struct regmap *map = (i2c == chip->client) ? chip->regmap
  				: chip->regmap_companion;
bbd51b1ff   Haojian Zhuang   mfd: Split 88pm86...
77
  	int ret;
b46a36c0e   Jett.Zhou   mfd: Convert 88pm...
78
  	ret = regmap_update_bits(map, reg, mask, data);
bbd51b1ff   Haojian Zhuang   mfd: Split 88pm86...
79
80
  	return ret;
  }
53dbab7af   Haojian Zhuang   mfd: Support 88pm...
81
  EXPORT_SYMBOL(pm860x_set_bits);
bbd51b1ff   Haojian Zhuang   mfd: Split 88pm86...
82

5bdf7411b   Jett.Zhou   mfd: Fix 88pm860x...
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
  static int read_device(struct i2c_client *i2c, int reg,
  		       int bytes, void *dest)
  {
  	unsigned char msgbuf0[I2C_SMBUS_BLOCK_MAX + 3];
  	unsigned char msgbuf1[I2C_SMBUS_BLOCK_MAX + 2];
  	struct i2c_adapter *adap = i2c->adapter;
  	struct i2c_msg msg[2] = {{i2c->addr, 0, 1, msgbuf0},
  				 {i2c->addr, I2C_M_RD, 0, msgbuf1},
  				};
  	int num = 1, ret = 0;
  
  	if (dest == NULL)
  		return -EINVAL;
  	msgbuf0[0] = (unsigned char)reg;	/* command */
  	msg[1].len = bytes;
  
  	/* if data needs to read back, num should be 2 */
  	if (bytes > 0)
  		num = 2;
  	ret = adap->algo->master_xfer(adap, msg, num);
  	memcpy(dest, msgbuf1, bytes);
  	if (ret < 0)
  		return ret;
  	return 0;
  }
  
  static int write_device(struct i2c_client *i2c, int reg,
  			int bytes, void *src)
  {
  	unsigned char buf[bytes + 1];
  	struct i2c_adapter *adap = i2c->adapter;
  	struct i2c_msg msg;
  	int ret;
  
  	buf[0] = (unsigned char)reg;
  	memcpy(&buf[1], src, bytes);
  	msg.addr = i2c->addr;
  	msg.flags = 0;
  	msg.len = bytes + 1;
  	msg.buf = buf;
  
  	ret = adap->algo->master_xfer(adap, &msg, 1);
  	if (ret < 0)
  		return ret;
  	return 0;
  }
09b034191   Haojian Zhuang   mfd: Append addit...
129
130
  int pm860x_page_reg_read(struct i2c_client *i2c, int reg)
  {
09b034191   Haojian Zhuang   mfd: Append addit...
131
132
133
  	unsigned char zero = 0;
  	unsigned char data;
  	int ret;
5bdf7411b   Jett.Zhou   mfd: Fix 88pm860x...
134
135
136
137
138
  	i2c_lock_adapter(i2c->adapter);
  	read_device(i2c, 0xFA, 0, &zero);
  	read_device(i2c, 0xFB, 0, &zero);
  	read_device(i2c, 0xFF, 0, &zero);
  	ret = read_device(i2c, reg, 1, &data);
09b034191   Haojian Zhuang   mfd: Append addit...
139
140
  	if (ret >= 0)
  		ret = (int)data;
5bdf7411b   Jett.Zhou   mfd: Fix 88pm860x...
141
142
143
  	read_device(i2c, 0xFE, 0, &zero);
  	read_device(i2c, 0xFC, 0, &zero);
  	i2c_unlock_adapter(i2c->adapter);
09b034191   Haojian Zhuang   mfd: Append addit...
144
145
146
147
148
149
150
  	return ret;
  }
  EXPORT_SYMBOL(pm860x_page_reg_read);
  
  int pm860x_page_reg_write(struct i2c_client *i2c, int reg,
  			  unsigned char data)
  {
09b034191   Haojian Zhuang   mfd: Append addit...
151
152
  	unsigned char zero;
  	int ret;
5bdf7411b   Jett.Zhou   mfd: Fix 88pm860x...
153
154
155
156
157
158
159
160
  	i2c_lock_adapter(i2c->adapter);
  	read_device(i2c, 0xFA, 0, &zero);
  	read_device(i2c, 0xFB, 0, &zero);
  	read_device(i2c, 0xFF, 0, &zero);
  	ret = write_device(i2c, reg, 1, &data);
  	read_device(i2c, 0xFE, 0, &zero);
  	read_device(i2c, 0xFC, 0, &zero);
  	i2c_unlock_adapter(i2c->adapter);
09b034191   Haojian Zhuang   mfd: Append addit...
161
162
163
164
165
166
167
  	return ret;
  }
  EXPORT_SYMBOL(pm860x_page_reg_write);
  
  int pm860x_page_bulk_read(struct i2c_client *i2c, int reg,
  			  int count, unsigned char *buf)
  {
09b034191   Haojian Zhuang   mfd: Append addit...
168
169
  	unsigned char zero = 0;
  	int ret;
5bdf7411b   Jett.Zhou   mfd: Fix 88pm860x...
170
171
172
173
174
175
176
177
  	i2c_lock_adapter(i2c->adapter);
  	read_device(i2c, 0xfa, 0, &zero);
  	read_device(i2c, 0xfb, 0, &zero);
  	read_device(i2c, 0xff, 0, &zero);
  	ret = read_device(i2c, reg, count, buf);
  	read_device(i2c, 0xFE, 0, &zero);
  	read_device(i2c, 0xFC, 0, &zero);
  	i2c_unlock_adapter(i2c->adapter);
09b034191   Haojian Zhuang   mfd: Append addit...
178
179
180
181
182
183
184
  	return ret;
  }
  EXPORT_SYMBOL(pm860x_page_bulk_read);
  
  int pm860x_page_bulk_write(struct i2c_client *i2c, int reg,
  			   int count, unsigned char *buf)
  {
09b034191   Haojian Zhuang   mfd: Append addit...
185
186
  	unsigned char zero = 0;
  	int ret;
5bdf7411b   Jett.Zhou   mfd: Fix 88pm860x...
187
188
189
190
191
192
193
194
195
  	i2c_lock_adapter(i2c->adapter);
  	read_device(i2c, 0xFA, 0, &zero);
  	read_device(i2c, 0xFB, 0, &zero);
  	read_device(i2c, 0xFF, 0, &zero);
  	ret = write_device(i2c, reg, count, buf);
  	read_device(i2c, 0xFE, 0, &zero);
  	read_device(i2c, 0xFC, 0, &zero);
  	i2c_unlock_adapter(i2c->adapter);
  	i2c_unlock_adapter(i2c->adapter);
09b034191   Haojian Zhuang   mfd: Append addit...
196
197
198
199
200
201
202
  	return ret;
  }
  EXPORT_SYMBOL(pm860x_page_bulk_write);
  
  int pm860x_page_set_bits(struct i2c_client *i2c, int reg,
  			 unsigned char mask, unsigned char data)
  {
09b034191   Haojian Zhuang   mfd: Append addit...
203
204
205
  	unsigned char zero;
  	unsigned char value;
  	int ret;
5bdf7411b   Jett.Zhou   mfd: Fix 88pm860x...
206
207
208
209
210
  	i2c_lock_adapter(i2c->adapter);
  	read_device(i2c, 0xFA, 0, &zero);
  	read_device(i2c, 0xFB, 0, &zero);
  	read_device(i2c, 0xFF, 0, &zero);
  	ret = read_device(i2c, reg, 1, &value);
09b034191   Haojian Zhuang   mfd: Append addit...
211
212
213
214
  	if (ret < 0)
  		goto out;
  	value &= ~mask;
  	value |= data;
5bdf7411b   Jett.Zhou   mfd: Fix 88pm860x...
215
  	ret = write_device(i2c, reg, 1, &value);
09b034191   Haojian Zhuang   mfd: Append addit...
216
  out:
5bdf7411b   Jett.Zhou   mfd: Fix 88pm860x...
217
218
219
  	read_device(i2c, 0xFE, 0, &zero);
  	read_device(i2c, 0xFC, 0, &zero);
  	i2c_unlock_adapter(i2c->adapter);
09b034191   Haojian Zhuang   mfd: Append addit...
220
221
222
  	return ret;
  }
  EXPORT_SYMBOL(pm860x_page_set_bits);
bbd51b1ff   Haojian Zhuang   mfd: Split 88pm86...
223
224
  
  static const struct i2c_device_id pm860x_id_table[] = {
53dbab7af   Haojian Zhuang   mfd: Support 88pm...
225
  	{ "88PM860x", 0 },
bbd51b1ff   Haojian Zhuang   mfd: Split 88pm86...
226
227
228
  	{}
  };
  MODULE_DEVICE_TABLE(i2c, pm860x_id_table);
53dbab7af   Haojian Zhuang   mfd: Support 88pm...
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
  static int verify_addr(struct i2c_client *i2c)
  {
  	unsigned short addr_8607[] = {0x30, 0x34};
  	unsigned short addr_8606[] = {0x10, 0x11};
  	int size, i;
  
  	if (i2c == NULL)
  		return 0;
  	size = ARRAY_SIZE(addr_8606);
  	for (i = 0; i < size; i++) {
  		if (i2c->addr == *(addr_8606 + i))
  			return CHIP_PM8606;
  	}
  	size = ARRAY_SIZE(addr_8607);
  	for (i = 0; i < size; i++) {
  		if (i2c->addr == *(addr_8607 + i))
  			return CHIP_PM8607;
  	}
  	return 0;
  }
b46a36c0e   Jett.Zhou   mfd: Convert 88pm...
249
250
251
252
  static struct regmap_config pm860x_regmap_config = {
  	.reg_bits = 8,
  	.val_bits = 8,
  };
bbd51b1ff   Haojian Zhuang   mfd: Split 88pm86...
253
254
255
  static int __devinit pm860x_probe(struct i2c_client *client,
  				  const struct i2c_device_id *id)
  {
53dbab7af   Haojian Zhuang   mfd: Support 88pm...
256
  	struct pm860x_platform_data *pdata = client->dev.platform_data;
e8343ddac   Haojian Zhuang   mfd: Use i2c_dumm...
257
  	struct pm860x_chip *chip;
b46a36c0e   Jett.Zhou   mfd: Convert 88pm...
258
  	int ret;
e8343ddac   Haojian Zhuang   mfd: Use i2c_dumm...
259
260
  
  	if (!pdata) {
53dbab7af   Haojian Zhuang   mfd: Support 88pm...
261
262
263
264
  		pr_info("No platform data in %s!
  ", __func__);
  		return -EINVAL;
  	}
e8343ddac   Haojian Zhuang   mfd: Use i2c_dumm...
265
266
267
268
269
  	chip = kzalloc(sizeof(struct pm860x_chip), GFP_KERNEL);
  	if (chip == NULL)
  		return -ENOMEM;
  
  	chip->id = verify_addr(client);
b46a36c0e   Jett.Zhou   mfd: Convert 88pm...
270
271
272
273
274
275
  	chip->regmap = regmap_init_i2c(client, &pm860x_regmap_config);
  	if (IS_ERR(chip->regmap)) {
  		ret = PTR_ERR(chip->regmap);
  		dev_err(&client->dev, "Failed to allocate register map: %d
  ",
  				ret);
e3380333b   Julia Lawall   mfd: Introduce mi...
276
  		kfree(chip);
b46a36c0e   Jett.Zhou   mfd: Convert 88pm...
277
278
  		return ret;
  	}
e8343ddac   Haojian Zhuang   mfd: Use i2c_dumm...
279
280
281
  	chip->client = client;
  	i2c_set_clientdata(client, chip);
  	chip->dev = &client->dev;
e8343ddac   Haojian Zhuang   mfd: Use i2c_dumm...
282
  	dev_set_drvdata(chip->dev, chip);
53dbab7af   Haojian Zhuang   mfd: Support 88pm...
283
284
285
286
287
288
289
  	/*
  	 * Both client and companion client shares same platform driver.
  	 * Driver distinguishes them by pdata->companion_addr.
  	 * pdata->companion_addr is only assigned if companion chip exists.
  	 * At the same time, the companion_addr shouldn't equal to client
  	 * address.
  	 */
e8343ddac   Haojian Zhuang   mfd: Use i2c_dumm...
290
291
292
293
  	if (pdata->companion_addr && (pdata->companion_addr != client->addr)) {
  		chip->companion_addr = pdata->companion_addr;
  		chip->companion = i2c_new_dummy(chip->client->adapter,
  						chip->companion_addr);
b46a36c0e   Jett.Zhou   mfd: Convert 88pm...
294
295
296
297
298
299
300
301
302
  		chip->regmap_companion = regmap_init_i2c(chip->companion,
  							&pm860x_regmap_config);
  		if (IS_ERR(chip->regmap_companion)) {
  			ret = PTR_ERR(chip->regmap_companion);
  			dev_err(&chip->companion->dev,
  				"Failed to allocate register map: %d
  ", ret);
  			return ret;
  		}
53dbab7af   Haojian Zhuang   mfd: Support 88pm...
303
  		i2c_set_clientdata(chip->companion, chip);
53dbab7af   Haojian Zhuang   mfd: Support 88pm...
304
  	}
e8343ddac   Haojian Zhuang   mfd: Use i2c_dumm...
305
306
  
  	pm860x_device_init(chip, pdata);
bbd51b1ff   Haojian Zhuang   mfd: Split 88pm86...
307
  	return 0;
bbd51b1ff   Haojian Zhuang   mfd: Split 88pm86...
308
309
310
311
  }
  
  static int __devexit pm860x_remove(struct i2c_client *client)
  {
53dbab7af   Haojian Zhuang   mfd: Support 88pm...
312
  	struct pm860x_chip *chip = i2c_get_clientdata(client);
53dbab7af   Haojian Zhuang   mfd: Support 88pm...
313
  	pm860x_device_exit(chip);
b46a36c0e   Jett.Zhou   mfd: Convert 88pm...
314
315
316
317
318
  	if (chip->companion) {
  		regmap_exit(chip->regmap_companion);
  		i2c_unregister_device(chip->companion);
  	}
  	regmap_exit(chip->regmap);
bbd51b1ff   Haojian Zhuang   mfd: Split 88pm86...
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
  	kfree(chip);
  	return 0;
  }
  
  static struct i2c_driver pm860x_driver = {
  	.driver	= {
  		.name	= "88PM860x",
  		.owner	= THIS_MODULE,
  	},
  	.probe		= pm860x_probe,
  	.remove		= __devexit_p(pm860x_remove),
  	.id_table	= pm860x_id_table,
  };
  
  static int __init pm860x_i2c_init(void)
  {
  	int ret;
  	ret = i2c_add_driver(&pm860x_driver);
  	if (ret != 0)
  		pr_err("Failed to register 88PM860x I2C driver: %d
  ", ret);
  	return ret;
  }
  subsys_initcall(pm860x_i2c_init);
  
  static void __exit pm860x_i2c_exit(void)
  {
  	i2c_del_driver(&pm860x_driver);
  }
  module_exit(pm860x_i2c_exit);
  
  MODULE_DESCRIPTION("I2C Driver for Marvell 88PM860x");
  MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
  MODULE_LICENSE("GPL");