Root/drivers/mfd/88pm860x-i2c.c

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
20int 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}
34EXPORT_SYMBOL(pm860x_reg_read);
35
36int 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}
47EXPORT_SYMBOL(pm860x_reg_write);
48
49int 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}
60EXPORT_SYMBOL(pm860x_bulk_read);
61
62int 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}
73EXPORT_SYMBOL(pm860x_bulk_write);
74
75int 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}
86EXPORT_SYMBOL(pm860x_set_bits);
87
88static 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
135int 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}
153EXPORT_SYMBOL(pm860x_page_reg_read);
154
155int 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}
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{
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}
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{
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}
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{
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);
227out:
228    read_device(i2c, 0xFE, 0, &zero);
229    read_device(i2c, 0xFC, 0, &zero);
230    i2c_unlock_adapter(i2c->adapter);
231    return ret;
232}
233EXPORT_SYMBOL(pm860x_page_set_bits);
234
235static const struct i2c_device_id pm860x_id_table[] = {
236    { "88PM860x", 0 },
237    {}
238};
239MODULE_DEVICE_TABLE(i2c, pm860x_id_table);
240
241static 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
262static struct regmap_config pm860x_regmap_config = {
263    .reg_bits = 8,
264    .val_bits = 8,
265};
266
267static 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
323static 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
338static 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
348static 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
359static SIMPLE_DEV_PM_OPS(pm860x_pm_ops, pm860x_suspend, pm860x_resume);
360
361static 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
372static 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}
380subsys_initcall(pm860x_i2c_init);
381
382static void __exit pm860x_i2c_exit(void)
383{
384    i2c_del_driver(&pm860x_driver);
385}
386module_exit(pm860x_i2c_exit);
387
388MODULE_DESCRIPTION("I2C Driver for Marvell 88PM860x");
389MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
390MODULE_LICENSE("GPL");
391

Archive Download this file



interactive