File saa716x_further_improve_i2c_handling.diff of Package v4l-dvb-saa716x
diff --git a/drivers/media/pci/saa716x/saa716x_i2c.c b/drivers/media/pci/saa716x/saa716x_i2c.c
index 4db168913a7e65..2a9124f25d65ed 100644
--- a/drivers/media/pci/saa716x/saa716x_i2c.c
+++ b/drivers/media/pci/saa716x/saa716x_i2c.c
@@ -17,38 +17,42 @@
#include "saa716x_i2c.h"
#include "saa716x_priv.h"
-static int saa716x_i2c_hwinit(struct saa716x_i2c *i2c, u32 I2C_DEV)
+static void saa716x_i2c_recover(struct saa716x_i2c *i2c)
{
struct saa716x_dev *saa716x = i2c->saa716x;
- struct i2c_adapter *adapter = &i2c->i2c_adapter;
+ u32 I2C_DEV = SAA716x_I2C_BUS(i2c->i2c_dev);
+ int i;
+
+ u8 i2c_recover[23] = { /* SCL/SDA high */
+ 0xc0,
+ /* send 9 SCL pulses w/o ACK (SDA high) */
+ 0x40, 0xc0, 0x40, 0xc0, 0x40, 0xc0,
+ 0x40, 0xc0, 0x40, 0xc0, 0x40, 0xc0,
+ 0x40, 0xc0, 0x40, 0xc0, 0x40, 0xc0,
+ /* create STOP condition */
+ 0x40, 0x00, 0x80, 0xc0 };
+
+ /* Some slave may be in read state, force bus release */
+ for (i = 0; i < sizeof(i2c_recover)/sizeof(*i2c_recover); i++) {
+ SAA716x_EPWR(I2C_DEV, I2C_CONTROL, i2c_recover[i]);
+ /* Wait long enough for possible clock stretching */
+ usleep_range(100, 120);
+ }
+}
- int i, max;
- u32 reg;
+static void saa716x_i2c_hwinit(struct saa716x_i2c *i2c)
+{
+ struct saa716x_dev *saa716x = i2c->saa716x;
+ struct i2c_adapter *adapter = &i2c->i2c_adapter;
+ u32 I2C_DEV = SAA716x_I2C_BUS(i2c->i2c_dev);
/* Reset I2C Core */
SAA716x_EPWR(I2C_DEV, I2C_CONTROL, 0xc1);
- max = 100;
- for (i = 0; i < max; i++) {
- reg = SAA716x_EPRD(I2C_DEV, I2C_CONTROL);
- if (reg == 0xc0) {
- pci_dbg(saa716x->pdev, "Adapter (%02x) %s RESET",
- I2C_DEV, adapter->name);
- break;
- }
- usleep_range(1000, 2000);
- }
- if (i == max) {
- pci_err(saa716x->pdev, "Adapter (%02x) %s RESET failed",
- I2C_DEV, adapter->name);
- return -EIO;
- }
-
/* I2C Rate Setup, set clock divisor to 0.5 * 27MHz/i2c_rate */
switch (i2c->i2c_rate) {
case SAA716x_I2C_RATE_400:
-
- pci_dbg(saa716x->pdev, "Initializing Adapter %s @ 400k",
+ pci_info(saa716x->pdev, "Initializing %s @ 400kHz",
adapter->name);
SAA716x_EPWR(I2C_DEV, I2C_CLOCK_DIVISOR_HIGH, 34);
SAA716x_EPWR(I2C_DEV, I2C_CLOCK_DIVISOR_LOW, 34);
@@ -56,8 +60,7 @@ static int saa716x_i2c_hwinit(struct saa716x_i2c *i2c, u32 I2C_DEV)
break;
case SAA716x_I2C_RATE_100:
-
- pci_dbg(saa716x->pdev, "Initializing Adapter %s @ 100k",
+ pci_info(saa716x->pdev, "Initializing %s @ 100kHz",
adapter->name);
SAA716x_EPWR(I2C_DEV, I2C_CLOCK_DIVISOR_HIGH, 135);
SAA716x_EPWR(I2C_DEV, I2C_CLOCK_DIVISOR_LOW, 135);
@@ -65,173 +68,180 @@ static int saa716x_i2c_hwinit(struct saa716x_i2c *i2c, u32 I2C_DEV)
break;
default:
-
- pci_err(saa716x->pdev, "Adapter %s Unknown Rate (Rate=0x%02x)",
+ pci_err(saa716x->pdev, "%s Unknown Rate (0x%02x)",
adapter->name,
i2c->i2c_rate);
-
break;
}
- /* Some slave may be in read state, force bus release */
- SAA716x_EPWR(I2C_DEV, I2C_CONTROL, 0xc0); /* SCL/SDA high */
- usleep_range(50, 200);
- for (i = 0; i < 9; i++) { /* Issue 9 SCL pulses w/o ACK */
- SAA716x_EPWR(I2C_DEV, I2C_CONTROL, 0x40);
- usleep_range(50, 200);
- SAA716x_EPWR(I2C_DEV, I2C_CONTROL, 0xc0);
- usleep_range(50, 200);
- }
- /* Create STOP condition */
- SAA716x_EPWR(I2C_DEV, I2C_CONTROL, 0x40); /* SCL low */
- usleep_range(50, 200);
- SAA716x_EPWR(I2C_DEV, I2C_CONTROL, 0x00); /* SCL/SDA low */
- usleep_range(50, 200);
- SAA716x_EPWR(I2C_DEV, I2C_CONTROL, 0x80); /* SDA low */
- usleep_range(50, 200);
- SAA716x_EPWR(I2C_DEV, I2C_CONTROL, 0xc0); /* SCL/SDA high */
- usleep_range(50, 200);
-
- return 0;
+ saa716x_i2c_recover(i2c);
}
-static int saa716x_i2c_send(struct saa716x_i2c *i2c, u32 I2C_DEV, u32 data)
+static int saa716x_i2c_bytetime_us(struct saa716x_i2c *i2c)
{
- struct saa716x_dev *saa716x = i2c->saa716x;
- u32 reg;
-
- /* Check FIFO status before TX */
- reg = SAA716x_EPRD(I2C_DEV, I2C_STATUS);
- if (reg & I2C_TRANSMIT_PROG)
- return -EIO; /* fifo full */
-
- /* Write to FIFO */
- SAA716x_EPWR(I2C_DEV, TX_FIFO, data);
-
- return 0;
+ return (i2c->i2c_rate == SAA716x_I2C_RATE_400) ? 23 : 90;
}
-static int saa716x_i2c_recv(struct saa716x_i2c *i2c, u32 I2C_DEV, u32 *data)
+static int saa716x_i2c_send(struct saa716x_i2c *i2c, u32 data)
{
struct saa716x_dev *saa716x = i2c->saa716x;
- u32 reg;
+ u32 I2C_DEV = SAA716x_I2C_BUS(i2c->i2c_dev);
+ int bytetime_us = saa716x_i2c_bytetime_us(i2c);
+ u32 i2c_status;
int i;
- for (i = 0; i < 50; i++) {
- reg = SAA716x_EPRD(I2C_DEV, I2C_STATUS);
- if (!(reg & I2C_RECEIVE_CLEAR)) {
- /* Read from FIFO */
- *data = SAA716x_EPRD(I2C_DEV, RX_FIFO);
+ for (i = 0; i < 20; i++) {
+ i2c_status = SAA716x_EPRD(I2C_DEV, I2C_STATUS);
+ if (!(i2c_status & I2C_TRANSMIT_PROG)) {
+ /* Write to FIFO */
+ SAA716x_EPWR(I2C_DEV, TX_FIFO, data);
return 0;
}
- usleep_range(10, 20);
+ usleep_range(4 * bytetime_us, 6 * bytetime_us);
}
return -ETIMEDOUT;
}
-static int saa716x_i2c_wait(struct saa716x_i2c *i2c, u32 I2C_DEV)
+static int saa716x_i2c_wait(struct saa716x_i2c *i2c)
{
struct saa716x_dev *saa716x = i2c->saa716x;
+ u32 I2C_DEV = SAA716x_I2C_BUS(i2c->i2c_dev);
+ int bytetime_us = saa716x_i2c_bytetime_us(i2c);
+ u32 int_status, tx_bytes;
int i;
- u32 i2c_status, int_status;
/* Wait for transfer done */
- for (i = 0; i < 50; i++) {
- i2c_status = SAA716x_EPRD(I2C_DEV, I2C_STATUS);
- int_status = SAA716x_EPRD(I2C_DEV, INT_STATUS);
- if (int_status & I2C_ERROR_IBE)
- return -EIO;
- if (i2c_status & I2C_TRANSMIT_CLEAR) {
+ for (i = 0; i < 20; i++) {
+ tx_bytes = SAA716x_EPRD(I2C_DEV, I2C_TX_LEVEL);
+ tx_bytes &= I2C_TRANSMIT_RANGE;
+ if (tx_bytes != 0) {
+ usleep_range(tx_bytes * bytetime_us,
+ (tx_bytes + 1) * bytetime_us);
+ } else {
+ int_status = SAA716x_EPRD(I2C_DEV, INT_STATUS);
+ if (int_status & I2C_ERROR_IBE)
+ return -EIO;
if (int_status & I2C_ACK_INTER_MTNA)
return -ENXIO; /* NACK */
- return 0;
+ if (int_status & I2C_INTERRUPT_MTD)
+ return 0;
+ usleep_range(bytetime_us/2, bytetime_us);
}
- usleep_range(100, 200);
}
return -ETIMEDOUT;
}
-static int saa716x_i2c_xfer_msg(struct saa716x_i2c *i2c, u32 I2C_DEV,
- u16 addr, u8 *buf, u16 len, u8 read, u8 add_stop)
+static int saa716x_i2c_recv(struct saa716x_i2c *i2c, u32 *data)
{
struct saa716x_dev *saa716x = i2c->saa716x;
- u32 data;
- int err;
+ u32 I2C_DEV = SAA716x_I2C_BUS(i2c->i2c_dev);
+ int bytetime_us = saa716x_i2c_bytetime_us(i2c);
+ u32 i2c_status, int_status;
int i;
- SAA716x_EPWR(I2C_DEV, INT_CLR_STATUS, 0x1fff);
-
- /* first write START with I2C address */
- data = I2C_START_BIT | (addr << 1) | ((read) ? 1 : 0);
- pci_dbg(saa716x->pdev, "length=%d Addr:0x%02x", len, data);
- err = saa716x_i2c_send(i2c, I2C_DEV, data);
- if (err < 0) {
- pci_err(saa716x->pdev, "Address write failed");
- return err;
+ for (i = 0; i < 50; i++) {
+ i2c_status = SAA716x_EPRD(I2C_DEV, I2C_STATUS);
+ if (!(i2c_status & I2C_RECEIVE_CLEAR)) {
+ /* Read from FIFO */
+ *data = SAA716x_EPRD(I2C_DEV, RX_FIFO);
+ return 0;
+ }
+ int_status = SAA716x_EPRD(I2C_DEV, INT_STATUS);
+ if (int_status & I2C_ERROR_IBE)
+ return -EIO;
+ if (int_status & I2C_ACK_INTER_MTNA) {
+ /* Clear TX FIFO */
+ SAA716x_EPWR(I2C_DEV, I2C_CONTROL, 0xc1);
+ return -ENXIO; /* NACK */
+ }
+ usleep_range(bytetime_us/2, bytetime_us);
}
- err = saa716x_i2c_wait(i2c, I2C_DEV);
- if (err < 0)
- return err;
-
- SAA716x_EPWR(I2C_DEV, INT_CLR_STATUS, 0x1fff);
+ return -ETIMEDOUT;
+}
- /* now write the data */
- for (i = 0; i < len; i++) {
- data = buf[i]; /* dummy data for read */
- if (read == 0)
+static int saa716x_i2c_xfer_msg(struct saa716x_i2c *i2c, u16 addr,
+ u8 *buf, u16 len, bool read, bool add_stop)
+{
+ struct saa716x_dev *saa716x = i2c->saa716x;
+ u32 I2C_DEV = SAA716x_I2C_BUS(i2c->i2c_dev);
+ int bytetime_us = saa716x_i2c_bytetime_us(i2c);
+ u32 data, tx_bytes;
+ int err, i;
+
+ if (read && len > 8)
+ return -ENOTSUPP; /* Do we need longer reads? */
+
+ pci_dbg(saa716x->pdev, "I2C %d %s transfer, addr=0x%02x length=%d",
+ i2c->i2c_dev, (read) ? "read" : "write", addr, len);
+
+ /* Write address and data */
+ for (i = 0; i <= len; i++) {
+ data = (i == 0) ?
+ /* First write START with I2C address, */
+ I2C_START_BIT | (addr << 1) | ((read) ? 1 : 0) :
+ /* then write the data (dummy data for read) */
+ buf[i-1];
+ if (i > 0 && !read)
pci_dbg(saa716x->pdev, " <W %04x> 0x%02x", i, data);
- if (add_stop && i == (len - 1))
+ if (add_stop && i == len)
data |= I2C_STOP_BIT;
- err = saa716x_i2c_send(i2c, I2C_DEV, data);
+ err = saa716x_i2c_send(i2c, data);
if (err < 0) {
- pci_err(saa716x->pdev, "Data send failed");
+ pci_err(saa716x->pdev, "I2C data %d send failed", i);
return err;
}
}
- err = saa716x_i2c_wait(i2c, I2C_DEV);
- if (err < 0 || !read)
- return err;
+ if (!read || len == 0) {
+ if (add_stop)
+ return saa716x_i2c_wait(i2c);
+ return 0;
+ }
- /* now read the data */
+ /* Now read the data */
for (i = 0; i < len; i++) {
- err = saa716x_i2c_recv(i2c, I2C_DEV, &data);
- if (err < 0) {
- pci_err(saa716x->pdev, "Data receive failed");
+ err = saa716x_i2c_recv(i2c, &data);
+ if (err < 0)
return err;
- }
pci_dbg(saa716x->pdev, " <R %04x> 0x%02x", i, data);
buf[i] = data;
}
+ SAA716x_EPWR(I2C_DEV, INT_CLR_STATUS, 0x1fff);
return 0;
}
static int saa716x_i2c_xfer(struct i2c_adapter *adapter,
struct i2c_msg *msgs, int num)
{
- struct saa716x_i2c *i2c = i2c_get_adapdata(adapter);
- struct saa716x_dev *saa716x = i2c->saa716x;
-
- u32 DEV = SAA716x_I2C_BUS(i2c->i2c_dev);
+ struct saa716x_i2c *i2c = i2c_get_adapdata(adapter);
+ struct saa716x_dev *saa716x = i2c->saa716x;
+ u32 I2C_DEV = SAA716x_I2C_BUS(i2c->i2c_dev);
+ bool read;
int i, err;
- pci_dbg(saa716x->pdev, "Bus(%02x) I2C transfer", DEV);
mutex_lock(&i2c->i2c_lock);
+ SAA716x_EPWR(I2C_DEV, INT_CLR_STATUS, 0x1fff);
+
for (i = 0; i < num; i++) {
- err = saa716x_i2c_xfer_msg(i2c, DEV,
- msgs[i].addr, msgs[i].buf, msgs[i].len,
- msgs[i].flags & I2C_M_RD, i == (num - 1));
- if (err < 0) {
- if (err != -ENXIO ||
- (msgs[i].flags == 0 && msgs[i].len != 0)) {
- pci_err(saa716x->pdev,
- "I2C transfer error, msg %d, "
- "addr = 0x%02x, len=%d, "
- "flags=0x%x, %pe",
- i, msgs[i].addr, msgs[i].len,
- msgs[i].flags, ERR_PTR(err));
- saa716x_i2c_hwinit(i2c, DEV);
- }
+ read = msgs[i].flags & I2C_M_RD;
+ err = saa716x_i2c_xfer_msg(i2c, msgs[i].addr, msgs[i].buf,
+ msgs[i].len, read, i == (num - 1));
+ if (err == -ENXIO) {
+ pci_dbg(saa716x->pdev,
+ "I2C %d %s NACK, msg %d, addr = 0x%02x, len=%d",
+ i2c->i2c_dev, (read) ? "read" : "write", i,
+ msgs[i].addr, msgs[i].len);
+ break;
+ } else if (err < 0) {
+ pci_err(saa716x->pdev,
+ "I2C %d %s transfer error, msg %d, addr = 0x%02x, len=%d, %pe",
+ i2c->i2c_dev, (read) ? "read" : "write", i,
+ msgs[i].addr, msgs[i].len, ERR_PTR(err));
+ /* Reset I2C Core */
+ SAA716x_EPWR(I2C_DEV, I2C_CONTROL, 0xc1);
+ /* Finish running transfers */
+ usleep_range(300, 400);
+ saa716x_i2c_recover(i2c);
break;
}
}
@@ -255,9 +265,9 @@ static const struct i2c_algorithm saa716x_algo = {
int saa716x_i2c_init(struct saa716x_dev *saa716x)
{
- struct pci_dev *pdev = saa716x->pdev;
- struct saa716x_i2c *i2c = saa716x->i2c;
- struct i2c_adapter *adapter = NULL;
+ struct pci_dev *pdev = saa716x->pdev;
+ struct saa716x_i2c *i2c = saa716x->i2c;
+ struct i2c_adapter *adapter = NULL;
int i, err = 0;
@@ -268,9 +278,9 @@ int saa716x_i2c_init(struct saa716x_dev *saa716x)
mutex_init(&i2c->i2c_lock);
- i2c->i2c_dev = i;
- i2c->i2c_rate = saa716x->config->i2c_rate;
- adapter = &i2c->i2c_adapter;
+ i2c->i2c_dev = i;
+ i2c->i2c_rate = saa716x->config->i2c_rate;
+ adapter = &i2c->i2c_adapter;
if (adapter != NULL) {
@@ -278,21 +288,23 @@ int saa716x_i2c_init(struct saa716x_dev *saa716x)
strcpy(adapter->name, SAA716x_I2C_ADAPTER(i));
- adapter->owner = saa716x->module;
- adapter->algo = &saa716x_algo;
- adapter->algo_data = NULL;
- adapter->dev.parent = &pdev->dev;
+ adapter->owner = saa716x->module;
+ adapter->algo = &saa716x_algo;
+ adapter->algo_data = NULL;
+ adapter->dev.parent = &pdev->dev;
- pci_dbg(saa716x->pdev, "Initializing adapter (%d) %s",
- i,
+ pci_dbg(saa716x->pdev, "Initializing %s",
adapter->name);
err = i2c_add_adapter(adapter);
- if (err < 0)
- goto exit;
+ if (err < 0) {
+ pci_err(saa716x->pdev, "%s init failed",
+ adapter->name);
+ return err;
+ }
i2c->saa716x = saa716x;
- saa716x_i2c_hwinit(i2c, SAA716x_I2C_BUS(i));
+ saa716x_i2c_hwinit(i2c);
}
i2c++;
}
@@ -301,16 +313,13 @@ int saa716x_i2c_init(struct saa716x_dev *saa716x)
saa716x->pdev->device);
return 0;
-exit:
- pci_err(saa716x->pdev, "Adapter (%d) %s init failed", i, adapter->name);
- return err;
}
EXPORT_SYMBOL_GPL(saa716x_i2c_init);
void saa716x_i2c_exit(struct saa716x_dev *saa716x)
{
- struct saa716x_i2c *i2c = saa716x->i2c;
- struct i2c_adapter *adapter = NULL;
+ struct saa716x_i2c *i2c = saa716x->i2c;
+ struct i2c_adapter *adapter = NULL;
int i;
pci_dbg(saa716x->pdev, "Removing SAA%02x I2C Core",
@@ -319,8 +328,7 @@ void saa716x_i2c_exit(struct saa716x_dev *saa716x)
for (i = 0; i < SAA716x_I2C_ADAPTERS; i++) {
adapter = &i2c->i2c_adapter;
- pci_dbg(saa716x->pdev, "Removing adapter (%d) %s", i,
- adapter->name);
+ pci_dbg(saa716x->pdev, "Removing %s", adapter->name);
i2c_del_adapter(adapter);
i2c++;