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> 165a0e3ad6STejun Heo #include <linux/slab.h> 17bbd51b1fSHaojian Zhuang 1853dbab7aSHaojian Zhuang static inline int pm860x_read_device(struct i2c_client *i2c, 19bbd51b1fSHaojian Zhuang int reg, int bytes, void *dest) 20bbd51b1fSHaojian Zhuang { 21bbd51b1fSHaojian Zhuang unsigned char data; 22bbd51b1fSHaojian Zhuang int ret; 23bbd51b1fSHaojian Zhuang 24bbd51b1fSHaojian Zhuang data = (unsigned char)reg; 25bbd51b1fSHaojian Zhuang ret = i2c_master_send(i2c, &data, 1); 26bbd51b1fSHaojian Zhuang if (ret < 0) 27bbd51b1fSHaojian Zhuang return ret; 28bbd51b1fSHaojian Zhuang 29bbd51b1fSHaojian Zhuang ret = i2c_master_recv(i2c, dest, bytes); 30bbd51b1fSHaojian Zhuang if (ret < 0) 31bbd51b1fSHaojian Zhuang return ret; 32bbd51b1fSHaojian Zhuang return 0; 33bbd51b1fSHaojian Zhuang } 34bbd51b1fSHaojian Zhuang 3553dbab7aSHaojian Zhuang static inline int pm860x_write_device(struct i2c_client *i2c, 36bbd51b1fSHaojian Zhuang int reg, int bytes, void *src) 37bbd51b1fSHaojian Zhuang { 38bbd51b1fSHaojian Zhuang unsigned char buf[bytes + 1]; 39bbd51b1fSHaojian Zhuang int ret; 40bbd51b1fSHaojian Zhuang 41bbd51b1fSHaojian Zhuang buf[0] = (unsigned char)reg; 42bbd51b1fSHaojian Zhuang memcpy(&buf[1], src, bytes); 43bbd51b1fSHaojian Zhuang 44bbd51b1fSHaojian Zhuang ret = i2c_master_send(i2c, buf, bytes + 1); 45bbd51b1fSHaojian Zhuang if (ret < 0) 46bbd51b1fSHaojian Zhuang return ret; 47bbd51b1fSHaojian Zhuang return 0; 48bbd51b1fSHaojian Zhuang } 49bbd51b1fSHaojian Zhuang 5053dbab7aSHaojian Zhuang int pm860x_reg_read(struct i2c_client *i2c, int reg) 51bbd51b1fSHaojian Zhuang { 5253dbab7aSHaojian Zhuang struct pm860x_chip *chip = i2c_get_clientdata(i2c); 53bbd51b1fSHaojian Zhuang unsigned char data; 54bbd51b1fSHaojian Zhuang int ret; 55bbd51b1fSHaojian Zhuang 56bbd51b1fSHaojian Zhuang mutex_lock(&chip->io_lock); 5753dbab7aSHaojian Zhuang ret = pm860x_read_device(i2c, reg, 1, &data); 58bbd51b1fSHaojian Zhuang mutex_unlock(&chip->io_lock); 59bbd51b1fSHaojian Zhuang 60bbd51b1fSHaojian Zhuang if (ret < 0) 61bbd51b1fSHaojian Zhuang return ret; 62bbd51b1fSHaojian Zhuang else 63bbd51b1fSHaojian Zhuang return (int)data; 64bbd51b1fSHaojian Zhuang } 6553dbab7aSHaojian Zhuang EXPORT_SYMBOL(pm860x_reg_read); 66bbd51b1fSHaojian Zhuang 6753dbab7aSHaojian Zhuang int pm860x_reg_write(struct i2c_client *i2c, int reg, 68bbd51b1fSHaojian Zhuang unsigned char data) 69bbd51b1fSHaojian Zhuang { 7053dbab7aSHaojian Zhuang struct pm860x_chip *chip = i2c_get_clientdata(i2c); 71bbd51b1fSHaojian Zhuang int ret; 72bbd51b1fSHaojian Zhuang 73bbd51b1fSHaojian Zhuang mutex_lock(&chip->io_lock); 7453dbab7aSHaojian Zhuang ret = pm860x_write_device(i2c, reg, 1, &data); 75bbd51b1fSHaojian Zhuang mutex_unlock(&chip->io_lock); 76bbd51b1fSHaojian Zhuang 77bbd51b1fSHaojian Zhuang return ret; 78bbd51b1fSHaojian Zhuang } 7953dbab7aSHaojian Zhuang EXPORT_SYMBOL(pm860x_reg_write); 80bbd51b1fSHaojian Zhuang 8153dbab7aSHaojian Zhuang int pm860x_bulk_read(struct i2c_client *i2c, int reg, 82bbd51b1fSHaojian Zhuang int count, unsigned char *buf) 83bbd51b1fSHaojian Zhuang { 8453dbab7aSHaojian Zhuang struct pm860x_chip *chip = i2c_get_clientdata(i2c); 85bbd51b1fSHaojian Zhuang int ret; 86bbd51b1fSHaojian Zhuang 87bbd51b1fSHaojian Zhuang mutex_lock(&chip->io_lock); 8853dbab7aSHaojian Zhuang ret = pm860x_read_device(i2c, reg, count, buf); 89bbd51b1fSHaojian Zhuang mutex_unlock(&chip->io_lock); 90bbd51b1fSHaojian Zhuang 91bbd51b1fSHaojian Zhuang return ret; 92bbd51b1fSHaojian Zhuang } 9353dbab7aSHaojian Zhuang EXPORT_SYMBOL(pm860x_bulk_read); 94bbd51b1fSHaojian Zhuang 9553dbab7aSHaojian Zhuang int pm860x_bulk_write(struct i2c_client *i2c, int reg, 96bbd51b1fSHaojian Zhuang int count, unsigned char *buf) 97bbd51b1fSHaojian Zhuang { 9853dbab7aSHaojian Zhuang struct pm860x_chip *chip = i2c_get_clientdata(i2c); 99bbd51b1fSHaojian Zhuang int ret; 100bbd51b1fSHaojian Zhuang 101bbd51b1fSHaojian Zhuang mutex_lock(&chip->io_lock); 10253dbab7aSHaojian Zhuang ret = pm860x_write_device(i2c, reg, count, buf); 103bbd51b1fSHaojian Zhuang mutex_unlock(&chip->io_lock); 104bbd51b1fSHaojian Zhuang 105bbd51b1fSHaojian Zhuang return ret; 106bbd51b1fSHaojian Zhuang } 10753dbab7aSHaojian Zhuang EXPORT_SYMBOL(pm860x_bulk_write); 108bbd51b1fSHaojian Zhuang 10953dbab7aSHaojian Zhuang int pm860x_set_bits(struct i2c_client *i2c, int reg, 110bbd51b1fSHaojian Zhuang unsigned char mask, unsigned char data) 111bbd51b1fSHaojian Zhuang { 11253dbab7aSHaojian Zhuang struct pm860x_chip *chip = i2c_get_clientdata(i2c); 113bbd51b1fSHaojian Zhuang unsigned char value; 114bbd51b1fSHaojian Zhuang int ret; 115bbd51b1fSHaojian Zhuang 116bbd51b1fSHaojian Zhuang mutex_lock(&chip->io_lock); 11753dbab7aSHaojian Zhuang ret = pm860x_read_device(i2c, reg, 1, &value); 118bbd51b1fSHaojian Zhuang if (ret < 0) 119bbd51b1fSHaojian Zhuang goto out; 120bbd51b1fSHaojian Zhuang value &= ~mask; 121bbd51b1fSHaojian Zhuang value |= data; 12253dbab7aSHaojian Zhuang ret = pm860x_write_device(i2c, reg, 1, &value); 123bbd51b1fSHaojian Zhuang out: 124bbd51b1fSHaojian Zhuang mutex_unlock(&chip->io_lock); 125bbd51b1fSHaojian Zhuang return ret; 126bbd51b1fSHaojian Zhuang } 12753dbab7aSHaojian Zhuang EXPORT_SYMBOL(pm860x_set_bits); 128bbd51b1fSHaojian Zhuang 12909b03419SHaojian Zhuang int pm860x_page_reg_read(struct i2c_client *i2c, int reg) 13009b03419SHaojian Zhuang { 13109b03419SHaojian Zhuang struct pm860x_chip *chip = i2c_get_clientdata(i2c); 13209b03419SHaojian Zhuang unsigned char zero = 0; 13309b03419SHaojian Zhuang unsigned char data; 13409b03419SHaojian Zhuang int ret; 13509b03419SHaojian Zhuang 13609b03419SHaojian Zhuang mutex_lock(&chip->io_lock); 13709b03419SHaojian Zhuang pm860x_write_device(i2c, 0xFA, 0, &zero); 13809b03419SHaojian Zhuang pm860x_write_device(i2c, 0xFB, 0, &zero); 13909b03419SHaojian Zhuang pm860x_write_device(i2c, 0xFF, 0, &zero); 14009b03419SHaojian Zhuang ret = pm860x_read_device(i2c, reg, 1, &data); 14109b03419SHaojian Zhuang if (ret >= 0) 14209b03419SHaojian Zhuang ret = (int)data; 14309b03419SHaojian Zhuang pm860x_write_device(i2c, 0xFE, 0, &zero); 14409b03419SHaojian Zhuang pm860x_write_device(i2c, 0xFC, 0, &zero); 14509b03419SHaojian Zhuang mutex_unlock(&chip->io_lock); 14609b03419SHaojian Zhuang return ret; 14709b03419SHaojian Zhuang } 14809b03419SHaojian Zhuang EXPORT_SYMBOL(pm860x_page_reg_read); 14909b03419SHaojian Zhuang 15009b03419SHaojian Zhuang int pm860x_page_reg_write(struct i2c_client *i2c, int reg, 15109b03419SHaojian Zhuang unsigned char data) 15209b03419SHaojian Zhuang { 15309b03419SHaojian Zhuang struct pm860x_chip *chip = i2c_get_clientdata(i2c); 15409b03419SHaojian Zhuang unsigned char zero; 15509b03419SHaojian Zhuang int ret; 15609b03419SHaojian Zhuang 15709b03419SHaojian Zhuang mutex_lock(&chip->io_lock); 15809b03419SHaojian Zhuang pm860x_write_device(i2c, 0xFA, 0, &zero); 15909b03419SHaojian Zhuang pm860x_write_device(i2c, 0xFB, 0, &zero); 16009b03419SHaojian Zhuang pm860x_write_device(i2c, 0xFF, 0, &zero); 16109b03419SHaojian Zhuang ret = pm860x_write_device(i2c, reg, 1, &data); 16209b03419SHaojian Zhuang pm860x_write_device(i2c, 0xFE, 0, &zero); 16309b03419SHaojian Zhuang pm860x_write_device(i2c, 0xFC, 0, &zero); 16409b03419SHaojian Zhuang mutex_unlock(&chip->io_lock); 16509b03419SHaojian Zhuang return ret; 16609b03419SHaojian Zhuang } 16709b03419SHaojian Zhuang EXPORT_SYMBOL(pm860x_page_reg_write); 16809b03419SHaojian Zhuang 16909b03419SHaojian Zhuang int pm860x_page_bulk_read(struct i2c_client *i2c, int reg, 17009b03419SHaojian Zhuang int count, unsigned char *buf) 17109b03419SHaojian Zhuang { 17209b03419SHaojian Zhuang struct pm860x_chip *chip = i2c_get_clientdata(i2c); 17309b03419SHaojian Zhuang unsigned char zero = 0; 17409b03419SHaojian Zhuang int ret; 17509b03419SHaojian Zhuang 17609b03419SHaojian Zhuang mutex_lock(&chip->io_lock); 17709b03419SHaojian Zhuang pm860x_write_device(i2c, 0xFA, 0, &zero); 17809b03419SHaojian Zhuang pm860x_write_device(i2c, 0xFB, 0, &zero); 17909b03419SHaojian Zhuang pm860x_write_device(i2c, 0xFF, 0, &zero); 18009b03419SHaojian Zhuang ret = pm860x_read_device(i2c, reg, count, buf); 18109b03419SHaojian Zhuang pm860x_write_device(i2c, 0xFE, 0, &zero); 18209b03419SHaojian Zhuang pm860x_write_device(i2c, 0xFC, 0, &zero); 18309b03419SHaojian Zhuang mutex_unlock(&chip->io_lock); 18409b03419SHaojian Zhuang return ret; 18509b03419SHaojian Zhuang } 18609b03419SHaojian Zhuang EXPORT_SYMBOL(pm860x_page_bulk_read); 18709b03419SHaojian Zhuang 18809b03419SHaojian Zhuang int pm860x_page_bulk_write(struct i2c_client *i2c, int reg, 18909b03419SHaojian Zhuang int count, unsigned char *buf) 19009b03419SHaojian Zhuang { 19109b03419SHaojian Zhuang struct pm860x_chip *chip = i2c_get_clientdata(i2c); 19209b03419SHaojian Zhuang unsigned char zero = 0; 19309b03419SHaojian Zhuang int ret; 19409b03419SHaojian Zhuang 19509b03419SHaojian Zhuang mutex_lock(&chip->io_lock); 19609b03419SHaojian Zhuang pm860x_write_device(i2c, 0xFA, 0, &zero); 19709b03419SHaojian Zhuang pm860x_write_device(i2c, 0xFB, 0, &zero); 19809b03419SHaojian Zhuang pm860x_write_device(i2c, 0xFF, 0, &zero); 19909b03419SHaojian Zhuang ret = pm860x_write_device(i2c, reg, count, buf); 20009b03419SHaojian Zhuang pm860x_write_device(i2c, 0xFE, 0, &zero); 20109b03419SHaojian Zhuang pm860x_write_device(i2c, 0xFC, 0, &zero); 20209b03419SHaojian Zhuang mutex_unlock(&chip->io_lock); 20309b03419SHaojian Zhuang return ret; 20409b03419SHaojian Zhuang } 20509b03419SHaojian Zhuang EXPORT_SYMBOL(pm860x_page_bulk_write); 20609b03419SHaojian Zhuang 20709b03419SHaojian Zhuang int pm860x_page_set_bits(struct i2c_client *i2c, int reg, 20809b03419SHaojian Zhuang unsigned char mask, unsigned char data) 20909b03419SHaojian Zhuang { 21009b03419SHaojian Zhuang struct pm860x_chip *chip = i2c_get_clientdata(i2c); 21109b03419SHaojian Zhuang unsigned char zero; 21209b03419SHaojian Zhuang unsigned char value; 21309b03419SHaojian Zhuang int ret; 21409b03419SHaojian Zhuang 21509b03419SHaojian Zhuang mutex_lock(&chip->io_lock); 21609b03419SHaojian Zhuang pm860x_write_device(i2c, 0xFA, 0, &zero); 21709b03419SHaojian Zhuang pm860x_write_device(i2c, 0xFB, 0, &zero); 21809b03419SHaojian Zhuang pm860x_write_device(i2c, 0xFF, 0, &zero); 21909b03419SHaojian Zhuang ret = pm860x_read_device(i2c, reg, 1, &value); 22009b03419SHaojian Zhuang if (ret < 0) 22109b03419SHaojian Zhuang goto out; 22209b03419SHaojian Zhuang value &= ~mask; 22309b03419SHaojian Zhuang value |= data; 22409b03419SHaojian Zhuang ret = pm860x_write_device(i2c, reg, 1, &value); 22509b03419SHaojian Zhuang out: 22609b03419SHaojian Zhuang pm860x_write_device(i2c, 0xFE, 0, &zero); 22709b03419SHaojian Zhuang pm860x_write_device(i2c, 0xFC, 0, &zero); 22809b03419SHaojian Zhuang mutex_unlock(&chip->io_lock); 22909b03419SHaojian Zhuang return ret; 23009b03419SHaojian Zhuang } 23109b03419SHaojian Zhuang EXPORT_SYMBOL(pm860x_page_set_bits); 232bbd51b1fSHaojian Zhuang 233bbd51b1fSHaojian Zhuang static const struct i2c_device_id pm860x_id_table[] = { 23453dbab7aSHaojian Zhuang { "88PM860x", 0 }, 235bbd51b1fSHaojian Zhuang {} 236bbd51b1fSHaojian Zhuang }; 237bbd51b1fSHaojian Zhuang MODULE_DEVICE_TABLE(i2c, pm860x_id_table); 238bbd51b1fSHaojian Zhuang 23953dbab7aSHaojian Zhuang static int verify_addr(struct i2c_client *i2c) 24053dbab7aSHaojian Zhuang { 24153dbab7aSHaojian Zhuang unsigned short addr_8607[] = {0x30, 0x34}; 24253dbab7aSHaojian Zhuang unsigned short addr_8606[] = {0x10, 0x11}; 24353dbab7aSHaojian Zhuang int size, i; 24453dbab7aSHaojian Zhuang 24553dbab7aSHaojian Zhuang if (i2c == NULL) 24653dbab7aSHaojian Zhuang return 0; 24753dbab7aSHaojian Zhuang size = ARRAY_SIZE(addr_8606); 24853dbab7aSHaojian Zhuang for (i = 0; i < size; i++) { 24953dbab7aSHaojian Zhuang if (i2c->addr == *(addr_8606 + i)) 25053dbab7aSHaojian Zhuang return CHIP_PM8606; 25153dbab7aSHaojian Zhuang } 25253dbab7aSHaojian Zhuang size = ARRAY_SIZE(addr_8607); 25353dbab7aSHaojian Zhuang for (i = 0; i < size; i++) { 25453dbab7aSHaojian Zhuang if (i2c->addr == *(addr_8607 + i)) 25553dbab7aSHaojian Zhuang return CHIP_PM8607; 25653dbab7aSHaojian Zhuang } 25753dbab7aSHaojian Zhuang return 0; 25853dbab7aSHaojian Zhuang } 25953dbab7aSHaojian Zhuang 260bbd51b1fSHaojian Zhuang static int __devinit pm860x_probe(struct i2c_client *client, 261bbd51b1fSHaojian Zhuang const struct i2c_device_id *id) 262bbd51b1fSHaojian Zhuang { 26353dbab7aSHaojian Zhuang struct pm860x_platform_data *pdata = client->dev.platform_data; 264e8343ddaSHaojian Zhuang struct pm860x_chip *chip; 265bbd51b1fSHaojian Zhuang 266e8343ddaSHaojian Zhuang if (!pdata) { 26753dbab7aSHaojian Zhuang pr_info("No platform data in %s!\n", __func__); 26853dbab7aSHaojian Zhuang return -EINVAL; 26953dbab7aSHaojian Zhuang } 27053dbab7aSHaojian Zhuang 271e8343ddaSHaojian Zhuang chip = kzalloc(sizeof(struct pm860x_chip), GFP_KERNEL); 272e8343ddaSHaojian Zhuang if (chip == NULL) 273e8343ddaSHaojian Zhuang return -ENOMEM; 274e8343ddaSHaojian Zhuang 275e8343ddaSHaojian Zhuang chip->id = verify_addr(client); 276e8343ddaSHaojian Zhuang chip->client = client; 277e8343ddaSHaojian Zhuang i2c_set_clientdata(client, chip); 278e8343ddaSHaojian Zhuang chip->dev = &client->dev; 279e8343ddaSHaojian Zhuang mutex_init(&chip->io_lock); 280e8343ddaSHaojian Zhuang dev_set_drvdata(chip->dev, chip); 281e8343ddaSHaojian Zhuang 28253dbab7aSHaojian Zhuang /* 28353dbab7aSHaojian Zhuang * Both client and companion client shares same platform driver. 28453dbab7aSHaojian Zhuang * Driver distinguishes them by pdata->companion_addr. 28553dbab7aSHaojian Zhuang * pdata->companion_addr is only assigned if companion chip exists. 28653dbab7aSHaojian Zhuang * At the same time, the companion_addr shouldn't equal to client 28753dbab7aSHaojian Zhuang * address. 28853dbab7aSHaojian Zhuang */ 289e8343ddaSHaojian Zhuang if (pdata->companion_addr && (pdata->companion_addr != client->addr)) { 290e8343ddaSHaojian Zhuang chip->companion_addr = pdata->companion_addr; 291e8343ddaSHaojian Zhuang chip->companion = i2c_new_dummy(chip->client->adapter, 292e8343ddaSHaojian Zhuang chip->companion_addr); 29353dbab7aSHaojian Zhuang i2c_set_clientdata(chip->companion, chip); 29453dbab7aSHaojian Zhuang } 295e8343ddaSHaojian Zhuang 296e8343ddaSHaojian Zhuang pm860x_device_init(chip, pdata); 297bbd51b1fSHaojian Zhuang return 0; 298bbd51b1fSHaojian Zhuang } 299bbd51b1fSHaojian Zhuang 300bbd51b1fSHaojian Zhuang static int __devexit pm860x_remove(struct i2c_client *client) 301bbd51b1fSHaojian Zhuang { 30253dbab7aSHaojian Zhuang struct pm860x_chip *chip = i2c_get_clientdata(client); 303bbd51b1fSHaojian Zhuang 30453dbab7aSHaojian Zhuang pm860x_device_exit(chip); 30553dbab7aSHaojian Zhuang i2c_unregister_device(chip->companion); 306bbd51b1fSHaojian Zhuang kfree(chip); 307bbd51b1fSHaojian Zhuang return 0; 308bbd51b1fSHaojian Zhuang } 309bbd51b1fSHaojian Zhuang 310bbd51b1fSHaojian Zhuang static struct i2c_driver pm860x_driver = { 311bbd51b1fSHaojian Zhuang .driver = { 312bbd51b1fSHaojian Zhuang .name = "88PM860x", 313bbd51b1fSHaojian Zhuang .owner = THIS_MODULE, 314bbd51b1fSHaojian Zhuang }, 315bbd51b1fSHaojian Zhuang .probe = pm860x_probe, 316bbd51b1fSHaojian Zhuang .remove = __devexit_p(pm860x_remove), 317bbd51b1fSHaojian Zhuang .id_table = pm860x_id_table, 318bbd51b1fSHaojian Zhuang }; 319bbd51b1fSHaojian Zhuang 320bbd51b1fSHaojian Zhuang static int __init pm860x_i2c_init(void) 321bbd51b1fSHaojian Zhuang { 322bbd51b1fSHaojian Zhuang int ret; 323bbd51b1fSHaojian Zhuang ret = i2c_add_driver(&pm860x_driver); 324bbd51b1fSHaojian Zhuang if (ret != 0) 325bbd51b1fSHaojian Zhuang pr_err("Failed to register 88PM860x I2C driver: %d\n", ret); 326bbd51b1fSHaojian Zhuang return ret; 327bbd51b1fSHaojian Zhuang } 328bbd51b1fSHaojian Zhuang subsys_initcall(pm860x_i2c_init); 329bbd51b1fSHaojian Zhuang 330bbd51b1fSHaojian Zhuang static void __exit pm860x_i2c_exit(void) 331bbd51b1fSHaojian Zhuang { 332bbd51b1fSHaojian Zhuang i2c_del_driver(&pm860x_driver); 333bbd51b1fSHaojian Zhuang } 334bbd51b1fSHaojian Zhuang module_exit(pm860x_i2c_exit); 335bbd51b1fSHaojian Zhuang 336bbd51b1fSHaojian Zhuang MODULE_DESCRIPTION("I2C Driver for Marvell 88PM860x"); 337bbd51b1fSHaojian Zhuang MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>"); 338bbd51b1fSHaojian Zhuang MODULE_LICENSE("GPL"); 339