summaryrefslogtreecommitdiff
path: root/drivers/i2c/busses/i2c-rtl9300.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/i2c/busses/i2c-rtl9300.c')
-rw-r--r--drivers/i2c/busses/i2c-rtl9300.c234
1 files changed, 114 insertions, 120 deletions
diff --git a/drivers/i2c/busses/i2c-rtl9300.c b/drivers/i2c/busses/i2c-rtl9300.c
index 9e3517b09b3d..fb3ebbd46a18 100644
--- a/drivers/i2c/busses/i2c-rtl9300.c
+++ b/drivers/i2c/busses/i2c-rtl9300.c
@@ -8,6 +8,7 @@
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
+#include <linux/unaligned.h>
enum rtl9300_bus_freq {
RTL9300_I2C_STD_FREQ,
@@ -71,6 +72,22 @@ struct rtl9300_i2c {
struct mutex lock;
};
+enum rtl9300_i2c_xfer_type {
+ RTL9300_I2C_XFER_BYTE,
+ RTL9300_I2C_XFER_WORD,
+ RTL9300_I2C_XFER_BLOCK,
+};
+
+struct rtl9300_i2c_xfer {
+ enum rtl9300_i2c_xfer_type type;
+ u16 dev_addr;
+ u8 reg_addr;
+ u8 reg_addr_len;
+ u8 *data;
+ u8 data_len;
+ bool write;
+};
+
#define RTL9300_I2C_MST_CTRL1 0x0
#define RTL9300_I2C_MST_CTRL2 0x4
#define RTL9300_I2C_MST_DATA_WORD0 0x8
@@ -95,45 +112,37 @@ static int rtl9300_i2c_select_scl(struct rtl9300_i2c *i2c, u8 scl)
return regmap_field_write(i2c->fields[F_SCL_SEL], 1);
}
-static int rtl9300_i2c_config_io(struct rtl9300_i2c *i2c, struct rtl9300_i2c_chan *chan)
+static int rtl9300_i2c_config_chan(struct rtl9300_i2c *i2c, struct rtl9300_i2c_chan *chan)
{
struct rtl9300_i2c_drv_data *drv_data;
int ret;
- drv_data = (struct rtl9300_i2c_drv_data *)device_get_match_data(i2c->dev);
+ if (i2c->sda_num == chan->sda_num)
+ return 0;
- ret = regmap_field_update_bits(i2c->fields[F_SDA_SEL], BIT(chan->sda_num),
- BIT(chan->sda_num));
+ ret = regmap_field_write(i2c->fields[F_SCL_FREQ], chan->bus_freq);
if (ret)
return ret;
- ret = regmap_field_write(i2c->fields[F_SDA_OUT_SEL], chan->sda_num);
+ drv_data = (struct rtl9300_i2c_drv_data *)device_get_match_data(i2c->dev);
+ ret = drv_data->select_scl(i2c, 0);
if (ret)
return ret;
- ret = regmap_field_write(i2c->fields[F_SCL_FREQ], chan->bus_freq);
+ ret = regmap_field_update_bits(i2c->fields[F_SDA_SEL], BIT(chan->sda_num),
+ BIT(chan->sda_num));
if (ret)
return ret;
- return drv_data->select_scl(i2c, 0);
-}
-
-static int rtl9300_i2c_config_xfer(struct rtl9300_i2c *i2c, struct rtl9300_i2c_chan *chan,
- u16 addr, u16 len)
-{
- int ret;
-
- if (len < 1 || len > 16)
- return -EINVAL;
-
- ret = regmap_field_write(i2c->fields[F_DEV_ADDR], addr);
+ ret = regmap_field_write(i2c->fields[F_SDA_OUT_SEL], chan->sda_num);
if (ret)
return ret;
- return regmap_field_write(i2c->fields[F_DATA_WIDTH], (len - 1) & 0xf);
+ i2c->sda_num = chan->sda_num;
+ return 0;
}
-static int rtl9300_i2c_read(struct rtl9300_i2c *i2c, u8 *buf, int len)
+static int rtl9300_i2c_read(struct rtl9300_i2c *i2c, u8 *buf, u8 len)
{
u32 vals[4] = {};
int i, ret;
@@ -153,7 +162,7 @@ static int rtl9300_i2c_read(struct rtl9300_i2c *i2c, u8 *buf, int len)
return 0;
}
-static int rtl9300_i2c_write(struct rtl9300_i2c *i2c, u8 *buf, int len)
+static int rtl9300_i2c_write(struct rtl9300_i2c *i2c, u8 *buf, u8 len)
{
u32 vals[4] = {};
int i;
@@ -176,16 +185,51 @@ static int rtl9300_i2c_writel(struct rtl9300_i2c *i2c, u32 data)
return regmap_write(i2c->regmap, i2c->data_reg, data);
}
-static int rtl9300_i2c_execute_xfer(struct rtl9300_i2c *i2c, char read_write,
- int size, union i2c_smbus_data *data, int len)
+static int rtl9300_i2c_prepare_xfer(struct rtl9300_i2c *i2c, struct rtl9300_i2c_xfer *xfer)
{
- u32 val;
int ret;
- ret = regmap_field_write(i2c->fields[F_RWOP], read_write == I2C_SMBUS_WRITE);
+ if (xfer->data_len < 1 || xfer->data_len > 16)
+ return -EINVAL;
+
+ ret = regmap_field_write(i2c->fields[F_DEV_ADDR], xfer->dev_addr);
+ if (ret)
+ return ret;
+
+ ret = rtl9300_i2c_reg_addr_set(i2c, xfer->reg_addr, xfer->reg_addr_len);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(i2c->fields[F_RWOP], xfer->write);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(i2c->fields[F_DATA_WIDTH], (xfer->data_len - 1) & 0xf);
if (ret)
return ret;
+ if (xfer->write) {
+ switch (xfer->type) {
+ case RTL9300_I2C_XFER_BYTE:
+ ret = rtl9300_i2c_writel(i2c, *xfer->data);
+ break;
+ case RTL9300_I2C_XFER_WORD:
+ ret = rtl9300_i2c_writel(i2c, get_unaligned((const u16 *)xfer->data));
+ break;
+ default:
+ ret = rtl9300_i2c_write(i2c, xfer->data, xfer->data_len);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static int rtl9300_i2c_do_xfer(struct rtl9300_i2c *i2c, struct rtl9300_i2c_xfer *xfer)
+{
+ u32 val;
+ int ret;
+
ret = regmap_field_write(i2c->fields[F_I2C_TRIG], 1);
if (ret)
return ret;
@@ -200,28 +244,24 @@ static int rtl9300_i2c_execute_xfer(struct rtl9300_i2c *i2c, char read_write,
if (val)
return -EIO;
- if (read_write == I2C_SMBUS_READ) {
- switch (size) {
- case I2C_SMBUS_BYTE:
- case I2C_SMBUS_BYTE_DATA:
+ if (!xfer->write) {
+ switch (xfer->type) {
+ case RTL9300_I2C_XFER_BYTE:
ret = regmap_read(i2c->regmap, i2c->data_reg, &val);
if (ret)
return ret;
- data->byte = val & 0xff;
+
+ *xfer->data = val & 0xff;
break;
- case I2C_SMBUS_WORD_DATA:
+ case RTL9300_I2C_XFER_WORD:
ret = regmap_read(i2c->regmap, i2c->data_reg, &val);
if (ret)
return ret;
- data->word = val & 0xffff;
- break;
- case I2C_SMBUS_I2C_BLOCK_DATA:
- ret = rtl9300_i2c_read(i2c, &data->block[1], len);
- if (ret)
- return ret;
+
+ put_unaligned(val & 0xffff, (u16*)xfer->data);
break;
default:
- ret = rtl9300_i2c_read(i2c, &data->block[0], len);
+ ret = rtl9300_i2c_read(i2c, xfer->data, xfer->data_len);
if (ret)
return ret;
break;
@@ -237,108 +277,62 @@ static int rtl9300_i2c_smbus_xfer(struct i2c_adapter *adap, u16 addr, unsigned s
{
struct rtl9300_i2c_chan *chan = i2c_get_adapdata(adap);
struct rtl9300_i2c *i2c = chan->i2c;
- int len = 0, ret;
+ struct rtl9300_i2c_xfer xfer = {0};
+ int ret;
+
+ if (addr > 0x7f)
+ return -EINVAL;
mutex_lock(&i2c->lock);
- if (chan->sda_num != i2c->sda_num) {
- ret = rtl9300_i2c_config_io(i2c, chan);
- if (ret)
- goto out_unlock;
- i2c->sda_num = chan->sda_num;
- }
+
+ ret = rtl9300_i2c_config_chan(i2c, chan);
+ if (ret)
+ goto out_unlock;
+
+ xfer.dev_addr = addr & 0x7f;
+ xfer.write = (read_write == I2C_SMBUS_WRITE);
+ xfer.reg_addr = command;
+ xfer.reg_addr_len = 1;
switch (size) {
case I2C_SMBUS_BYTE:
- if (read_write == I2C_SMBUS_WRITE) {
- ret = rtl9300_i2c_config_xfer(i2c, chan, addr, 0);
- if (ret)
- goto out_unlock;
- ret = rtl9300_i2c_reg_addr_set(i2c, command, 1);
- if (ret)
- goto out_unlock;
- } else {
- ret = rtl9300_i2c_config_xfer(i2c, chan, addr, 1);
- if (ret)
- goto out_unlock;
- ret = rtl9300_i2c_reg_addr_set(i2c, 0, 0);
- if (ret)
- goto out_unlock;
- }
+ xfer.data = (read_write == I2C_SMBUS_READ) ? &data->byte : &command;
+ xfer.data_len = 1;
+ xfer.reg_addr = 0;
+ xfer.reg_addr_len = 0;
+ xfer.type = RTL9300_I2C_XFER_BYTE;
break;
-
case I2C_SMBUS_BYTE_DATA:
- ret = rtl9300_i2c_reg_addr_set(i2c, command, 1);
- if (ret)
- goto out_unlock;
- ret = rtl9300_i2c_config_xfer(i2c, chan, addr, 1);
- if (ret)
- goto out_unlock;
- if (read_write == I2C_SMBUS_WRITE) {
- ret = rtl9300_i2c_writel(i2c, data->byte);
- if (ret)
- goto out_unlock;
- }
+ xfer.data = &data->byte;
+ xfer.data_len = 1;
+ xfer.type = RTL9300_I2C_XFER_BYTE;
break;
-
case I2C_SMBUS_WORD_DATA:
- ret = rtl9300_i2c_reg_addr_set(i2c, command, 1);
- if (ret)
- goto out_unlock;
- ret = rtl9300_i2c_config_xfer(i2c, chan, addr, 2);
- if (ret)
- goto out_unlock;
- if (read_write == I2C_SMBUS_WRITE) {
- ret = rtl9300_i2c_writel(i2c, data->word);
- if (ret)
- goto out_unlock;
- }
+ xfer.data = (u8 *)&data->word;
+ xfer.data_len = 2;
+ xfer.type = RTL9300_I2C_XFER_WORD;
break;
-
case I2C_SMBUS_BLOCK_DATA:
- ret = rtl9300_i2c_reg_addr_set(i2c, command, 1);
- if (ret)
- goto out_unlock;
- if (data->block[0] < 1 || data->block[0] > I2C_SMBUS_BLOCK_MAX) {
- ret = -EINVAL;
- goto out_unlock;
- }
- ret = rtl9300_i2c_config_xfer(i2c, chan, addr, data->block[0] + 1);
- if (ret)
- goto out_unlock;
- if (read_write == I2C_SMBUS_WRITE) {
- ret = rtl9300_i2c_write(i2c, &data->block[0], data->block[0] + 1);
- if (ret)
- goto out_unlock;
- }
- len = data->block[0] + 1;
+ xfer.data = &data->block[0];
+ xfer.data_len = data->block[0] + 1;
+ xfer.type = RTL9300_I2C_XFER_BLOCK;
break;
-
case I2C_SMBUS_I2C_BLOCK_DATA:
- ret = rtl9300_i2c_reg_addr_set(i2c, command, 1);
- if (ret)
- goto out_unlock;
- if (data->block[0] < 1 || data->block[0] > I2C_SMBUS_BLOCK_MAX) {
- ret = -EINVAL;
- goto out_unlock;
- }
- ret = rtl9300_i2c_config_xfer(i2c, chan, addr, data->block[0]);
- if (ret)
- goto out_unlock;
- if (read_write == I2C_SMBUS_WRITE) {
- ret = rtl9300_i2c_write(i2c, &data->block[1], data->block[0]);
- if (ret)
- goto out_unlock;
- }
- len = data->block[0];
+ xfer.data = &data->block[1];
+ xfer.data_len = data->block[0];
+ xfer.type = RTL9300_I2C_XFER_BLOCK;
break;
-
default:
dev_err(&adap->dev, "Unsupported transaction %d\n", size);
ret = -EOPNOTSUPP;
goto out_unlock;
}
- ret = rtl9300_i2c_execute_xfer(i2c, read_write, size, data, len);
+ ret = rtl9300_i2c_prepare_xfer(i2c, &xfer);
+ if (ret)
+ goto out_unlock;
+
+ ret = rtl9300_i2c_do_xfer(i2c, &xfer);
out_unlock:
mutex_unlock(&i2c->lock);