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/i2c.h> 14b46a36c0SJett.Zhou #include <linux/regmap.h> 1553dbab7aSHaojian Zhuang #include <linux/mfd/88pm860x.h> 16bbd51b1fSHaojian Zhuang 1753dbab7aSHaojian Zhuang int pm860x_reg_read(struct i2c_client *i2c, int reg) 18bbd51b1fSHaojian Zhuang { 1953dbab7aSHaojian Zhuang struct pm860x_chip *chip = i2c_get_clientdata(i2c); 20b46a36c0SJett.Zhou struct regmap *map = (i2c == chip->client) ? chip->regmap 21b46a36c0SJett.Zhou : chip->regmap_companion; 22b46a36c0SJett.Zhou unsigned int data; 23bbd51b1fSHaojian Zhuang int ret; 24bbd51b1fSHaojian Zhuang 25b46a36c0SJett.Zhou ret = regmap_read(map, reg, &data); 26bbd51b1fSHaojian Zhuang if (ret < 0) 27bbd51b1fSHaojian Zhuang return ret; 28bbd51b1fSHaojian Zhuang else 29bbd51b1fSHaojian Zhuang return (int)data; 30bbd51b1fSHaojian Zhuang } 3153dbab7aSHaojian Zhuang EXPORT_SYMBOL(pm860x_reg_read); 32bbd51b1fSHaojian Zhuang 3353dbab7aSHaojian Zhuang int pm860x_reg_write(struct i2c_client *i2c, int reg, 34bbd51b1fSHaojian Zhuang unsigned char data) 35bbd51b1fSHaojian Zhuang { 3653dbab7aSHaojian Zhuang struct pm860x_chip *chip = i2c_get_clientdata(i2c); 37b46a36c0SJett.Zhou struct regmap *map = (i2c == chip->client) ? chip->regmap 38b46a36c0SJett.Zhou : chip->regmap_companion; 39bbd51b1fSHaojian Zhuang int ret; 40bbd51b1fSHaojian Zhuang 41b46a36c0SJett.Zhou ret = regmap_write(map, reg, data); 42bbd51b1fSHaojian Zhuang return ret; 43bbd51b1fSHaojian Zhuang } 4453dbab7aSHaojian Zhuang EXPORT_SYMBOL(pm860x_reg_write); 45bbd51b1fSHaojian Zhuang 4653dbab7aSHaojian Zhuang int pm860x_bulk_read(struct i2c_client *i2c, int reg, 47bbd51b1fSHaojian Zhuang int count, unsigned char *buf) 48bbd51b1fSHaojian Zhuang { 4953dbab7aSHaojian Zhuang struct pm860x_chip *chip = i2c_get_clientdata(i2c); 50b46a36c0SJett.Zhou struct regmap *map = (i2c == chip->client) ? chip->regmap 51b46a36c0SJett.Zhou : chip->regmap_companion; 52bbd51b1fSHaojian Zhuang int ret; 53bbd51b1fSHaojian Zhuang 54b46a36c0SJett.Zhou ret = regmap_raw_read(map, reg, buf, count); 55bbd51b1fSHaojian Zhuang return ret; 56bbd51b1fSHaojian Zhuang } 5753dbab7aSHaojian Zhuang EXPORT_SYMBOL(pm860x_bulk_read); 58bbd51b1fSHaojian Zhuang 5953dbab7aSHaojian Zhuang int pm860x_bulk_write(struct i2c_client *i2c, int reg, 60bbd51b1fSHaojian Zhuang int count, unsigned char *buf) 61bbd51b1fSHaojian Zhuang { 6253dbab7aSHaojian Zhuang struct pm860x_chip *chip = i2c_get_clientdata(i2c); 63b46a36c0SJett.Zhou struct regmap *map = (i2c == chip->client) ? chip->regmap 64b46a36c0SJett.Zhou : chip->regmap_companion; 65bbd51b1fSHaojian Zhuang int ret; 66bbd51b1fSHaojian Zhuang 67b46a36c0SJett.Zhou ret = regmap_raw_write(map, reg, buf, count); 68bbd51b1fSHaojian Zhuang return ret; 69bbd51b1fSHaojian Zhuang } 7053dbab7aSHaojian Zhuang EXPORT_SYMBOL(pm860x_bulk_write); 71bbd51b1fSHaojian Zhuang 7253dbab7aSHaojian Zhuang int pm860x_set_bits(struct i2c_client *i2c, int reg, 73bbd51b1fSHaojian Zhuang unsigned char mask, unsigned char data) 74bbd51b1fSHaojian Zhuang { 7553dbab7aSHaojian Zhuang struct pm860x_chip *chip = i2c_get_clientdata(i2c); 76b46a36c0SJett.Zhou struct regmap *map = (i2c == chip->client) ? chip->regmap 77b46a36c0SJett.Zhou : chip->regmap_companion; 78bbd51b1fSHaojian Zhuang int ret; 79bbd51b1fSHaojian Zhuang 80b46a36c0SJett.Zhou ret = regmap_update_bits(map, reg, mask, data); 81bbd51b1fSHaojian Zhuang return ret; 82bbd51b1fSHaojian Zhuang } 8353dbab7aSHaojian Zhuang EXPORT_SYMBOL(pm860x_set_bits); 84bbd51b1fSHaojian Zhuang 855bdf7411SJett.Zhou static int read_device(struct i2c_client *i2c, int reg, 865bdf7411SJett.Zhou int bytes, void *dest) 875bdf7411SJett.Zhou { 885bdf7411SJett.Zhou unsigned char msgbuf0[I2C_SMBUS_BLOCK_MAX + 3]; 895bdf7411SJett.Zhou unsigned char msgbuf1[I2C_SMBUS_BLOCK_MAX + 2]; 905bdf7411SJett.Zhou struct i2c_adapter *adap = i2c->adapter; 9148942222SShubhrajyoti D struct i2c_msg msg[2] = { 9248942222SShubhrajyoti D { 9348942222SShubhrajyoti D .addr = i2c->addr, 9448942222SShubhrajyoti D .flags = 0, 9548942222SShubhrajyoti D .len = 1, 9648942222SShubhrajyoti D .buf = msgbuf0 9748942222SShubhrajyoti D }, 9848942222SShubhrajyoti D { .addr = i2c->addr, 9948942222SShubhrajyoti D .flags = I2C_M_RD, 10048942222SShubhrajyoti D .len = 0, 10148942222SShubhrajyoti D .buf = msgbuf1 10248942222SShubhrajyoti D }, 1035bdf7411SJett.Zhou }; 1045bdf7411SJett.Zhou int num = 1, ret = 0; 1055bdf7411SJett.Zhou 1065bdf7411SJett.Zhou if (dest == NULL) 1075bdf7411SJett.Zhou return -EINVAL; 1085bdf7411SJett.Zhou msgbuf0[0] = (unsigned char)reg; /* command */ 1095bdf7411SJett.Zhou msg[1].len = bytes; 1105bdf7411SJett.Zhou 1115bdf7411SJett.Zhou /* if data needs to read back, num should be 2 */ 1125bdf7411SJett.Zhou if (bytes > 0) 1135bdf7411SJett.Zhou num = 2; 1145bdf7411SJett.Zhou ret = adap->algo->master_xfer(adap, msg, num); 1155bdf7411SJett.Zhou memcpy(dest, msgbuf1, bytes); 1165bdf7411SJett.Zhou if (ret < 0) 1175bdf7411SJett.Zhou return ret; 1185bdf7411SJett.Zhou return 0; 1195bdf7411SJett.Zhou } 1205bdf7411SJett.Zhou 1215bdf7411SJett.Zhou static int write_device(struct i2c_client *i2c, int reg, 1225bdf7411SJett.Zhou int bytes, void *src) 1235bdf7411SJett.Zhou { 1245bdf7411SJett.Zhou unsigned char buf[bytes + 1]; 1255bdf7411SJett.Zhou struct i2c_adapter *adap = i2c->adapter; 1265bdf7411SJett.Zhou struct i2c_msg msg; 1275bdf7411SJett.Zhou int ret; 1285bdf7411SJett.Zhou 1295bdf7411SJett.Zhou buf[0] = (unsigned char)reg; 1305bdf7411SJett.Zhou memcpy(&buf[1], src, bytes); 1315bdf7411SJett.Zhou msg.addr = i2c->addr; 1325bdf7411SJett.Zhou msg.flags = 0; 1335bdf7411SJett.Zhou msg.len = bytes + 1; 1345bdf7411SJett.Zhou msg.buf = buf; 1355bdf7411SJett.Zhou 1365bdf7411SJett.Zhou ret = adap->algo->master_xfer(adap, &msg, 1); 1375bdf7411SJett.Zhou if (ret < 0) 1385bdf7411SJett.Zhou return ret; 1395bdf7411SJett.Zhou return 0; 1405bdf7411SJett.Zhou } 1415bdf7411SJett.Zhou 14209b03419SHaojian Zhuang int pm860x_page_reg_read(struct i2c_client *i2c, int reg) 14309b03419SHaojian Zhuang { 14409b03419SHaojian Zhuang unsigned char zero = 0; 14509b03419SHaojian Zhuang unsigned char data; 14609b03419SHaojian Zhuang int ret; 14709b03419SHaojian Zhuang 1485bdf7411SJett.Zhou i2c_lock_adapter(i2c->adapter); 1495bdf7411SJett.Zhou read_device(i2c, 0xFA, 0, &zero); 1505bdf7411SJett.Zhou read_device(i2c, 0xFB, 0, &zero); 1515bdf7411SJett.Zhou read_device(i2c, 0xFF, 0, &zero); 1525bdf7411SJett.Zhou ret = read_device(i2c, reg, 1, &data); 15309b03419SHaojian Zhuang if (ret >= 0) 15409b03419SHaojian Zhuang ret = (int)data; 1555bdf7411SJett.Zhou read_device(i2c, 0xFE, 0, &zero); 1565bdf7411SJett.Zhou read_device(i2c, 0xFC, 0, &zero); 1575bdf7411SJett.Zhou i2c_unlock_adapter(i2c->adapter); 15809b03419SHaojian Zhuang return ret; 15909b03419SHaojian Zhuang } 16009b03419SHaojian Zhuang EXPORT_SYMBOL(pm860x_page_reg_read); 16109b03419SHaojian Zhuang 16209b03419SHaojian Zhuang int pm860x_page_reg_write(struct i2c_client *i2c, int reg, 16309b03419SHaojian Zhuang unsigned char data) 16409b03419SHaojian Zhuang { 16509b03419SHaojian Zhuang unsigned char zero; 16609b03419SHaojian Zhuang int ret; 16709b03419SHaojian Zhuang 1685bdf7411SJett.Zhou i2c_lock_adapter(i2c->adapter); 1695bdf7411SJett.Zhou read_device(i2c, 0xFA, 0, &zero); 1705bdf7411SJett.Zhou read_device(i2c, 0xFB, 0, &zero); 1715bdf7411SJett.Zhou read_device(i2c, 0xFF, 0, &zero); 1725bdf7411SJett.Zhou ret = write_device(i2c, reg, 1, &data); 1735bdf7411SJett.Zhou read_device(i2c, 0xFE, 0, &zero); 1745bdf7411SJett.Zhou read_device(i2c, 0xFC, 0, &zero); 1755bdf7411SJett.Zhou i2c_unlock_adapter(i2c->adapter); 17609b03419SHaojian Zhuang return ret; 17709b03419SHaojian Zhuang } 17809b03419SHaojian Zhuang EXPORT_SYMBOL(pm860x_page_reg_write); 17909b03419SHaojian Zhuang 18009b03419SHaojian Zhuang int pm860x_page_bulk_read(struct i2c_client *i2c, int reg, 18109b03419SHaojian Zhuang int count, unsigned char *buf) 18209b03419SHaojian Zhuang { 18309b03419SHaojian Zhuang unsigned char zero = 0; 18409b03419SHaojian Zhuang int ret; 18509b03419SHaojian Zhuang 1865bdf7411SJett.Zhou i2c_lock_adapter(i2c->adapter); 1875bdf7411SJett.Zhou read_device(i2c, 0xfa, 0, &zero); 1885bdf7411SJett.Zhou read_device(i2c, 0xfb, 0, &zero); 1895bdf7411SJett.Zhou read_device(i2c, 0xff, 0, &zero); 1905bdf7411SJett.Zhou ret = read_device(i2c, reg, count, buf); 1915bdf7411SJett.Zhou read_device(i2c, 0xFE, 0, &zero); 1925bdf7411SJett.Zhou read_device(i2c, 0xFC, 0, &zero); 1935bdf7411SJett.Zhou i2c_unlock_adapter(i2c->adapter); 19409b03419SHaojian Zhuang return ret; 19509b03419SHaojian Zhuang } 19609b03419SHaojian Zhuang EXPORT_SYMBOL(pm860x_page_bulk_read); 19709b03419SHaojian Zhuang 19809b03419SHaojian Zhuang int pm860x_page_bulk_write(struct i2c_client *i2c, int reg, 19909b03419SHaojian Zhuang int count, unsigned char *buf) 20009b03419SHaojian Zhuang { 20109b03419SHaojian Zhuang unsigned char zero = 0; 20209b03419SHaojian Zhuang int ret; 20309b03419SHaojian Zhuang 2045bdf7411SJett.Zhou i2c_lock_adapter(i2c->adapter); 2055bdf7411SJett.Zhou read_device(i2c, 0xFA, 0, &zero); 2065bdf7411SJett.Zhou read_device(i2c, 0xFB, 0, &zero); 2075bdf7411SJett.Zhou read_device(i2c, 0xFF, 0, &zero); 2085bdf7411SJett.Zhou ret = write_device(i2c, reg, count, buf); 2095bdf7411SJett.Zhou read_device(i2c, 0xFE, 0, &zero); 2105bdf7411SJett.Zhou read_device(i2c, 0xFC, 0, &zero); 2115bdf7411SJett.Zhou i2c_unlock_adapter(i2c->adapter); 2125bdf7411SJett.Zhou i2c_unlock_adapter(i2c->adapter); 21309b03419SHaojian Zhuang return ret; 21409b03419SHaojian Zhuang } 21509b03419SHaojian Zhuang EXPORT_SYMBOL(pm860x_page_bulk_write); 21609b03419SHaojian Zhuang 21709b03419SHaojian Zhuang int pm860x_page_set_bits(struct i2c_client *i2c, int reg, 21809b03419SHaojian Zhuang unsigned char mask, unsigned char data) 21909b03419SHaojian Zhuang { 22009b03419SHaojian Zhuang unsigned char zero; 22109b03419SHaojian Zhuang unsigned char value; 22209b03419SHaojian Zhuang int ret; 22309b03419SHaojian Zhuang 2245bdf7411SJett.Zhou i2c_lock_adapter(i2c->adapter); 2255bdf7411SJett.Zhou read_device(i2c, 0xFA, 0, &zero); 2265bdf7411SJett.Zhou read_device(i2c, 0xFB, 0, &zero); 2275bdf7411SJett.Zhou read_device(i2c, 0xFF, 0, &zero); 2285bdf7411SJett.Zhou ret = read_device(i2c, reg, 1, &value); 22909b03419SHaojian Zhuang if (ret < 0) 23009b03419SHaojian Zhuang goto out; 23109b03419SHaojian Zhuang value &= ~mask; 23209b03419SHaojian Zhuang value |= data; 2335bdf7411SJett.Zhou ret = write_device(i2c, reg, 1, &value); 23409b03419SHaojian Zhuang out: 2355bdf7411SJett.Zhou read_device(i2c, 0xFE, 0, &zero); 2365bdf7411SJett.Zhou read_device(i2c, 0xFC, 0, &zero); 2375bdf7411SJett.Zhou i2c_unlock_adapter(i2c->adapter); 23809b03419SHaojian Zhuang return ret; 23909b03419SHaojian Zhuang } 24009b03419SHaojian Zhuang EXPORT_SYMBOL(pm860x_page_set_bits); 241