linux/drivers/mfd/88pm860x-i2c.c
<<
>>
Prefs
   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/mfd/88pm860x.h>
  16#include <linux/slab.h>
  17
  18static inline int pm860x_read_device(struct i2c_client *i2c,
  19                                     int reg, int bytes, void *dest)
  20{
  21        unsigned char data;
  22        int ret;
  23
  24        data = (unsigned char)reg;
  25        ret = i2c_master_send(i2c, &data, 1);
  26        if (ret < 0)
  27                return ret;
  28
  29        ret = i2c_master_recv(i2c, dest, bytes);
  30        if (ret < 0)
  31                return ret;
  32        return 0;
  33}
  34
  35static inline int pm860x_write_device(struct i2c_client *i2c,
  36                                      int reg, int bytes, void *src)
  37{
  38        unsigned char buf[bytes + 1];
  39        int ret;
  40
  41        buf[0] = (unsigned char)reg;
  42        memcpy(&buf[1], src, bytes);
  43
  44        ret = i2c_master_send(i2c, buf, bytes + 1);
  45        if (ret < 0)
  46                return ret;
  47        return 0;
  48}
  49
  50int pm860x_reg_read(struct i2c_client *i2c, int reg)
  51{
  52        struct pm860x_chip *chip = i2c_get_clientdata(i2c);
  53        unsigned char data;
  54        int ret;
  55
  56        mutex_lock(&chip->io_lock);
  57        ret = pm860x_read_device(i2c, reg, 1, &data);
  58        mutex_unlock(&chip->io_lock);
  59
  60        if (ret < 0)
  61                return ret;
  62        else
  63                return (int)data;
  64}
  65EXPORT_SYMBOL(pm860x_reg_read);
  66
  67int pm860x_reg_write(struct i2c_client *i2c, int reg,
  68                     unsigned char data)
  69{
  70        struct pm860x_chip *chip = i2c_get_clientdata(i2c);
  71        int ret;
  72
  73        mutex_lock(&chip->io_lock);
  74        ret = pm860x_write_device(i2c, reg, 1, &data);
  75        mutex_unlock(&chip->io_lock);
  76
  77        return ret;
  78}
  79EXPORT_SYMBOL(pm860x_reg_write);
  80
  81int pm860x_bulk_read(struct i2c_client *i2c, int reg,
  82                     int count, unsigned char *buf)
  83{
  84        struct pm860x_chip *chip = i2c_get_clientdata(i2c);
  85        int ret;
  86
  87        mutex_lock(&chip->io_lock);
  88        ret = pm860x_read_device(i2c, reg, count, buf);
  89        mutex_unlock(&chip->io_lock);
  90
  91        return ret;
  92}
  93EXPORT_SYMBOL(pm860x_bulk_read);
  94
  95int pm860x_bulk_write(struct i2c_client *i2c, int reg,
  96                      int count, unsigned char *buf)
  97{
  98        struct pm860x_chip *chip = i2c_get_clientdata(i2c);
  99        int ret;
 100
 101        mutex_lock(&chip->io_lock);
 102        ret = pm860x_write_device(i2c, reg, count, buf);
 103        mutex_unlock(&chip->io_lock);
 104
 105        return ret;
 106}
 107EXPORT_SYMBOL(pm860x_bulk_write);
 108
 109int pm860x_set_bits(struct i2c_client *i2c, int reg,
 110                    unsigned char mask, unsigned char data)
 111{
 112        struct pm860x_chip *chip = i2c_get_clientdata(i2c);
 113        unsigned char value;
 114        int ret;
 115
 116        mutex_lock(&chip->io_lock);
 117        ret = pm860x_read_device(i2c, reg, 1, &value);
 118        if (ret < 0)
 119                goto out;
 120        value &= ~mask;
 121        value |= data;
 122        ret = pm860x_write_device(i2c, reg, 1, &value);
 123out:
 124        mutex_unlock(&chip->io_lock);
 125        return ret;
 126}
 127EXPORT_SYMBOL(pm860x_set_bits);
 128
 129int pm860x_page_reg_read(struct i2c_client *i2c, int reg)
 130{
 131        struct pm860x_chip *chip = i2c_get_clientdata(i2c);
 132        unsigned char zero = 0;
 133        unsigned char data;
 134        int ret;
 135
 136        mutex_lock(&chip->io_lock);
 137        pm860x_write_device(i2c, 0xFA, 0, &zero);
 138        pm860x_write_device(i2c, 0xFB, 0, &zero);
 139        pm860x_write_device(i2c, 0xFF, 0, &zero);
 140        ret = pm860x_read_device(i2c, reg, 1, &data);
 141        if (ret >= 0)
 142                ret = (int)data;
 143        pm860x_write_device(i2c, 0xFE, 0, &zero);
 144        pm860x_write_device(i2c, 0xFC, 0, &zero);
 145        mutex_unlock(&chip->io_lock);
 146        return ret;
 147}
 148EXPORT_SYMBOL(pm860x_page_reg_read);
 149
 150int pm860x_page_reg_write(struct i2c_client *i2c, int reg,
 151                          unsigned char data)
 152{
 153        struct pm860x_chip *chip = i2c_get_clientdata(i2c);
 154        unsigned char zero;
 155        int ret;
 156
 157        mutex_lock(&chip->io_lock);
 158        pm860x_write_device(i2c, 0xFA, 0, &zero);
 159        pm860x_write_device(i2c, 0xFB, 0, &zero);
 160        pm860x_write_device(i2c, 0xFF, 0, &zero);
 161        ret = pm860x_write_device(i2c, reg, 1, &data);
 162        pm860x_write_device(i2c, 0xFE, 0, &zero);
 163        pm860x_write_device(i2c, 0xFC, 0, &zero);
 164        mutex_unlock(&chip->io_lock);
 165        return ret;
 166}
 167EXPORT_SYMBOL(pm860x_page_reg_write);
 168
 169int pm860x_page_bulk_read(struct i2c_client *i2c, int reg,
 170                          int count, unsigned char *buf)
 171{
 172        struct pm860x_chip *chip = i2c_get_clientdata(i2c);
 173        unsigned char zero = 0;
 174        int ret;
 175
 176        mutex_lock(&chip->io_lock);
 177        pm860x_write_device(i2c, 0xFA, 0, &zero);
 178        pm860x_write_device(i2c, 0xFB, 0, &zero);
 179        pm860x_write_device(i2c, 0xFF, 0, &zero);
 180        ret = pm860x_read_device(i2c, reg, count, buf);
 181        pm860x_write_device(i2c, 0xFE, 0, &zero);
 182        pm860x_write_device(i2c, 0xFC, 0, &zero);
 183        mutex_unlock(&chip->io_lock);
 184        return ret;
 185}
 186EXPORT_SYMBOL(pm860x_page_bulk_read);
 187
 188int pm860x_page_bulk_write(struct i2c_client *i2c, int reg,
 189                           int count, unsigned char *buf)
 190{
 191        struct pm860x_chip *chip = i2c_get_clientdata(i2c);
 192        unsigned char zero = 0;
 193        int ret;
 194
 195        mutex_lock(&chip->io_lock);
 196        pm860x_write_device(i2c, 0xFA, 0, &zero);
 197        pm860x_write_device(i2c, 0xFB, 0, &zero);
 198        pm860x_write_device(i2c, 0xFF, 0, &zero);
 199        ret = pm860x_write_device(i2c, reg, count, buf);
 200        pm860x_write_device(i2c, 0xFE, 0, &zero);
 201        pm860x_write_device(i2c, 0xFC, 0, &zero);
 202        mutex_unlock(&chip->io_lock);
 203        return ret;
 204}
 205EXPORT_SYMBOL(pm860x_page_bulk_write);
 206
 207int pm860x_page_set_bits(struct i2c_client *i2c, int reg,
 208                         unsigned char mask, unsigned char data)
 209{
 210        struct pm860x_chip *chip = i2c_get_clientdata(i2c);
 211        unsigned char zero;
 212        unsigned char value;
 213        int ret;
 214
 215        mutex_lock(&chip->io_lock);
 216        pm860x_write_device(i2c, 0xFA, 0, &zero);
 217        pm860x_write_device(i2c, 0xFB, 0, &zero);
 218        pm860x_write_device(i2c, 0xFF, 0, &zero);
 219        ret = pm860x_read_device(i2c, reg, 1, &value);
 220        if (ret < 0)
 221                goto out;
 222        value &= ~mask;
 223        value |= data;
 224        ret = pm860x_write_device(i2c, reg, 1, &value);
 225out:
 226        pm860x_write_device(i2c, 0xFE, 0, &zero);
 227        pm860x_write_device(i2c, 0xFC, 0, &zero);
 228        mutex_unlock(&chip->io_lock);
 229        return ret;
 230}
 231EXPORT_SYMBOL(pm860x_page_set_bits);
 232
 233static const struct i2c_device_id pm860x_id_table[] = {
 234        { "88PM860x", 0 },
 235        {}
 236};
 237MODULE_DEVICE_TABLE(i2c, pm860x_id_table);
 238
 239static int verify_addr(struct i2c_client *i2c)
 240{
 241        unsigned short addr_8607[] = {0x30, 0x34};
 242        unsigned short addr_8606[] = {0x10, 0x11};
 243        int size, i;
 244
 245        if (i2c == NULL)
 246                return 0;
 247        size = ARRAY_SIZE(addr_8606);
 248        for (i = 0; i < size; i++) {
 249                if (i2c->addr == *(addr_8606 + i))
 250                        return CHIP_PM8606;
 251        }
 252        size = ARRAY_SIZE(addr_8607);
 253        for (i = 0; i < size; i++) {
 254                if (i2c->addr == *(addr_8607 + i))
 255                        return CHIP_PM8607;
 256        }
 257        return 0;
 258}
 259
 260static int __devinit pm860x_probe(struct i2c_client *client,
 261                                  const struct i2c_device_id *id)
 262{
 263        struct pm860x_platform_data *pdata = client->dev.platform_data;
 264        struct pm860x_chip *chip;
 265
 266        if (!pdata) {
 267                pr_info("No platform data in %s!\n", __func__);
 268                return -EINVAL;
 269        }
 270
 271        chip = kzalloc(sizeof(struct pm860x_chip), GFP_KERNEL);
 272        if (chip == NULL)
 273                return -ENOMEM;
 274
 275        chip->id = verify_addr(client);
 276        chip->client = client;
 277        i2c_set_clientdata(client, chip);
 278        chip->dev = &client->dev;
 279        mutex_init(&chip->io_lock);
 280        dev_set_drvdata(chip->dev, chip);
 281
 282        /*
 283         * Both client and companion client shares same platform driver.
 284         * Driver distinguishes them by pdata->companion_addr.
 285         * pdata->companion_addr is only assigned if companion chip exists.
 286         * At the same time, the companion_addr shouldn't equal to client
 287         * address.
 288         */
 289        if (pdata->companion_addr && (pdata->companion_addr != client->addr)) {
 290                chip->companion_addr = pdata->companion_addr;
 291                chip->companion = i2c_new_dummy(chip->client->adapter,
 292                                                chip->companion_addr);
 293                i2c_set_clientdata(chip->companion, chip);
 294        }
 295
 296        pm860x_device_init(chip, pdata);
 297        return 0;
 298}
 299
 300static int __devexit pm860x_remove(struct i2c_client *client)
 301{
 302        struct pm860x_chip *chip = i2c_get_clientdata(client);
 303
 304        pm860x_device_exit(chip);
 305        i2c_unregister_device(chip->companion);
 306        kfree(chip);
 307        return 0;
 308}
 309
 310static struct i2c_driver pm860x_driver = {
 311        .driver = {
 312                .name   = "88PM860x",
 313                .owner  = THIS_MODULE,
 314        },
 315        .probe          = pm860x_probe,
 316        .remove         = __devexit_p(pm860x_remove),
 317        .id_table       = pm860x_id_table,
 318};
 319
 320static int __init pm860x_i2c_init(void)
 321{
 322        int ret;
 323        ret = i2c_add_driver(&pm860x_driver);
 324        if (ret != 0)
 325                pr_err("Failed to register 88PM860x I2C driver: %d\n", ret);
 326        return ret;
 327}
 328subsys_initcall(pm860x_i2c_init);
 329
 330static void __exit pm860x_i2c_exit(void)
 331{
 332        i2c_del_driver(&pm860x_driver);
 333}
 334module_exit(pm860x_i2c_exit);
 335
 336MODULE_DESCRIPTION("I2C Driver for Marvell 88PM860x");
 337MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
 338MODULE_LICENSE("GPL");
 339