|
@@ -43,6 +43,7 @@
|
|
#include <linux/slab.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/i2c-omap.h>
|
|
#include <linux/i2c-omap.h>
|
|
#include <linux/pm_runtime.h>
|
|
#include <linux/pm_runtime.h>
|
|
|
|
+#include <linux/pm_qos.h>
|
|
|
|
|
|
/* I2C controller revisions */
|
|
/* I2C controller revisions */
|
|
#define OMAP_I2C_OMAP1_REV_2 0x20
|
|
#define OMAP_I2C_OMAP1_REV_2 0x20
|
|
@@ -55,6 +56,9 @@
|
|
/* timeout waiting for the controller to respond */
|
|
/* timeout waiting for the controller to respond */
|
|
#define OMAP_I2C_TIMEOUT (msecs_to_jiffies(1000))
|
|
#define OMAP_I2C_TIMEOUT (msecs_to_jiffies(1000))
|
|
|
|
|
|
|
|
+/* timeout for pm runtime autosuspend */
|
|
|
|
+#define OMAP_I2C_PM_TIMEOUT 1000 /* ms */
|
|
|
|
+
|
|
/* For OMAP3 I2C_IV has changed to I2C_WE (wakeup enable) */
|
|
/* For OMAP3 I2C_IV has changed to I2C_WE (wakeup enable) */
|
|
enum {
|
|
enum {
|
|
OMAP_I2C_REV_REG = 0,
|
|
OMAP_I2C_REV_REG = 0,
|
|
@@ -176,15 +180,15 @@ enum {
|
|
#define I2C_OMAP_ERRATA_I462 (1 << 1)
|
|
#define I2C_OMAP_ERRATA_I462 (1 << 1)
|
|
|
|
|
|
struct omap_i2c_dev {
|
|
struct omap_i2c_dev {
|
|
|
|
+ spinlock_t lock; /* IRQ synchronization */
|
|
struct device *dev;
|
|
struct device *dev;
|
|
void __iomem *base; /* virtual */
|
|
void __iomem *base; /* virtual */
|
|
int irq;
|
|
int irq;
|
|
int reg_shift; /* bit shift for I2C register addresses */
|
|
int reg_shift; /* bit shift for I2C register addresses */
|
|
struct completion cmd_complete;
|
|
struct completion cmd_complete;
|
|
struct resource *ioarea;
|
|
struct resource *ioarea;
|
|
- u32 latency; /* maximum mpu wkup latency */
|
|
|
|
- void (*set_mpu_wkup_lat)(struct device *dev,
|
|
|
|
- long latency);
|
|
|
|
|
|
+ u32 latency; /* maximum MPU wkup latency */
|
|
|
|
+ struct pm_qos_request pm_qos_request;
|
|
u32 speed; /* Speed of bus in kHz */
|
|
u32 speed; /* Speed of bus in kHz */
|
|
u32 dtrev; /* extra revision from DT */
|
|
u32 dtrev; /* extra revision from DT */
|
|
u32 flags;
|
|
u32 flags;
|
|
@@ -193,12 +197,14 @@ struct omap_i2c_dev {
|
|
u8 *regs;
|
|
u8 *regs;
|
|
size_t buf_len;
|
|
size_t buf_len;
|
|
struct i2c_adapter adapter;
|
|
struct i2c_adapter adapter;
|
|
|
|
+ u8 threshold;
|
|
u8 fifo_size; /* use as flag and value
|
|
u8 fifo_size; /* use as flag and value
|
|
* fifo_size==0 implies no fifo
|
|
* fifo_size==0 implies no fifo
|
|
* if set, should be trsh+1
|
|
* if set, should be trsh+1
|
|
*/
|
|
*/
|
|
u8 rev;
|
|
u8 rev;
|
|
unsigned b_hw:1; /* bad h/w fixes */
|
|
unsigned b_hw:1; /* bad h/w fixes */
|
|
|
|
+ unsigned receiver:1; /* true when we're in receiver mode */
|
|
u16 iestate; /* Saved interrupt register */
|
|
u16 iestate; /* Saved interrupt register */
|
|
u16 pscstate;
|
|
u16 pscstate;
|
|
u16 scllstate;
|
|
u16 scllstate;
|
|
@@ -417,13 +423,6 @@ static int omap_i2c_init(struct omap_i2c_dev *dev)
|
|
omap_i2c_write_reg(dev, OMAP_I2C_SCLL_REG, scll);
|
|
omap_i2c_write_reg(dev, OMAP_I2C_SCLL_REG, scll);
|
|
omap_i2c_write_reg(dev, OMAP_I2C_SCLH_REG, sclh);
|
|
omap_i2c_write_reg(dev, OMAP_I2C_SCLH_REG, sclh);
|
|
|
|
|
|
- if (dev->fifo_size) {
|
|
|
|
- /* Note: setup required fifo size - 1. RTRSH and XTRSH */
|
|
|
|
- buf = (dev->fifo_size - 1) << 8 | OMAP_I2C_BUF_RXFIF_CLR |
|
|
|
|
- (dev->fifo_size - 1) | OMAP_I2C_BUF_TXFIF_CLR;
|
|
|
|
- omap_i2c_write_reg(dev, OMAP_I2C_BUF_REG, buf);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
/* Take the I2C module out of reset: */
|
|
/* Take the I2C module out of reset: */
|
|
omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, OMAP_I2C_CON_EN);
|
|
omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, OMAP_I2C_CON_EN);
|
|
|
|
|
|
@@ -461,6 +460,43 @@ static int omap_i2c_wait_for_bb(struct omap_i2c_dev *dev)
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static void omap_i2c_resize_fifo(struct omap_i2c_dev *dev, u8 size, bool is_rx)
|
|
|
|
+{
|
|
|
|
+ u16 buf;
|
|
|
|
+
|
|
|
|
+ if (dev->flags & OMAP_I2C_FLAG_NO_FIFO)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Set up notification threshold based on message size. We're doing
|
|
|
|
+ * this to try and avoid draining feature as much as possible. Whenever
|
|
|
|
+ * we have big messages to transfer (bigger than our total fifo size)
|
|
|
|
+ * then we might use draining feature to transfer the remaining bytes.
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+ dev->threshold = clamp(size, (u8) 1, dev->fifo_size);
|
|
|
|
+
|
|
|
|
+ buf = omap_i2c_read_reg(dev, OMAP_I2C_BUF_REG);
|
|
|
|
+
|
|
|
|
+ if (is_rx) {
|
|
|
|
+ /* Clear RX Threshold */
|
|
|
|
+ buf &= ~(0x3f << 8);
|
|
|
|
+ buf |= ((dev->threshold - 1) << 8) | OMAP_I2C_BUF_RXFIF_CLR;
|
|
|
|
+ } else {
|
|
|
|
+ /* Clear TX Threshold */
|
|
|
|
+ buf &= ~0x3f;
|
|
|
|
+ buf |= (dev->threshold - 1) | OMAP_I2C_BUF_TXFIF_CLR;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ omap_i2c_write_reg(dev, OMAP_I2C_BUF_REG, buf);
|
|
|
|
+
|
|
|
|
+ if (dev->rev < OMAP_I2C_REV_ON_3630_4430)
|
|
|
|
+ dev->b_hw = 1; /* Enable hardware fixes */
|
|
|
|
+
|
|
|
|
+ /* calculate wakeup latency constraint for MPU */
|
|
|
|
+ dev->latency = (1000000 * dev->threshold) / (1000 * dev->speed / 8);
|
|
|
|
+}
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* Low level master read/write transaction.
|
|
* Low level master read/write transaction.
|
|
*/
|
|
*/
|
|
@@ -477,6 +513,9 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
|
|
if (msg->len == 0)
|
|
if (msg->len == 0)
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
+ dev->receiver = !!(msg->flags & I2C_M_RD);
|
|
|
|
+ omap_i2c_resize_fifo(dev, msg->len, dev->receiver);
|
|
|
|
+
|
|
omap_i2c_write_reg(dev, OMAP_I2C_SA_REG, msg->addr);
|
|
omap_i2c_write_reg(dev, OMAP_I2C_SA_REG, msg->addr);
|
|
|
|
|
|
/* REVISIT: Could the STB bit of I2C_CON be used with probing? */
|
|
/* REVISIT: Could the STB bit of I2C_CON be used with probing? */
|
|
@@ -590,8 +629,16 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
|
|
if (r < 0)
|
|
if (r < 0)
|
|
goto out;
|
|
goto out;
|
|
|
|
|
|
- if (dev->set_mpu_wkup_lat != NULL)
|
|
|
|
- dev->set_mpu_wkup_lat(dev->dev, dev->latency);
|
|
|
|
|
|
+ /*
|
|
|
|
+ * When waiting for completion of a i2c transfer, we need to
|
|
|
|
+ * set a wake up latency constraint for the MPU. This is to
|
|
|
|
+ * ensure quick enough wakeup from idle, when transfer
|
|
|
|
+ * completes.
|
|
|
|
+ */
|
|
|
|
+ if (dev->latency)
|
|
|
|
+ pm_qos_add_request(&dev->pm_qos_request,
|
|
|
|
+ PM_QOS_CPU_DMA_LATENCY,
|
|
|
|
+ dev->latency);
|
|
|
|
|
|
for (i = 0; i < num; i++) {
|
|
for (i = 0; i < num; i++) {
|
|
r = omap_i2c_xfer_msg(adap, &msgs[i], (i == (num - 1)));
|
|
r = omap_i2c_xfer_msg(adap, &msgs[i], (i == (num - 1)));
|
|
@@ -599,15 +646,16 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
- if (dev->set_mpu_wkup_lat != NULL)
|
|
|
|
- dev->set_mpu_wkup_lat(dev->dev, -1);
|
|
|
|
|
|
+ if (dev->latency)
|
|
|
|
+ pm_qos_remove_request(&dev->pm_qos_request);
|
|
|
|
|
|
if (r == 0)
|
|
if (r == 0)
|
|
r = num;
|
|
r = num;
|
|
|
|
|
|
omap_i2c_wait_for_bb(dev);
|
|
omap_i2c_wait_for_bb(dev);
|
|
out:
|
|
out:
|
|
- pm_runtime_put(dev->dev);
|
|
|
|
|
|
+ pm_runtime_mark_last_busy(dev->dev);
|
|
|
|
+ pm_runtime_put_autosuspend(dev->dev);
|
|
return r;
|
|
return r;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -725,186 +773,252 @@ omap_i2c_omap1_isr(int this_irq, void *dev_id)
|
|
* data to DATA_REG. Otherwise some data bytes can be lost while transferring
|
|
* data to DATA_REG. Otherwise some data bytes can be lost while transferring
|
|
* them from the memory to the I2C interface.
|
|
* them from the memory to the I2C interface.
|
|
*/
|
|
*/
|
|
-static int errata_omap3_i462(struct omap_i2c_dev *dev, u16 *stat, int *err)
|
|
|
|
|
|
+static int errata_omap3_i462(struct omap_i2c_dev *dev)
|
|
{
|
|
{
|
|
unsigned long timeout = 10000;
|
|
unsigned long timeout = 10000;
|
|
|
|
+ u16 stat;
|
|
|
|
|
|
- while (--timeout && !(*stat & OMAP_I2C_STAT_XUDF)) {
|
|
|
|
- if (*stat & (OMAP_I2C_STAT_NACK | OMAP_I2C_STAT_AL)) {
|
|
|
|
- omap_i2c_ack_stat(dev, *stat & (OMAP_I2C_STAT_XRDY |
|
|
|
|
|
|
+ do {
|
|
|
|
+ stat = omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG);
|
|
|
|
+ if (stat & OMAP_I2C_STAT_XUDF)
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ if (stat & (OMAP_I2C_STAT_NACK | OMAP_I2C_STAT_AL)) {
|
|
|
|
+ omap_i2c_ack_stat(dev, (OMAP_I2C_STAT_XRDY |
|
|
OMAP_I2C_STAT_XDR));
|
|
OMAP_I2C_STAT_XDR));
|
|
- return -ETIMEDOUT;
|
|
|
|
|
|
+ if (stat & OMAP_I2C_STAT_NACK) {
|
|
|
|
+ dev->cmd_err |= OMAP_I2C_STAT_NACK;
|
|
|
|
+ omap_i2c_ack_stat(dev, OMAP_I2C_STAT_NACK);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (stat & OMAP_I2C_STAT_AL) {
|
|
|
|
+ dev_err(dev->dev, "Arbitration lost\n");
|
|
|
|
+ dev->cmd_err |= OMAP_I2C_STAT_AL;
|
|
|
|
+ omap_i2c_ack_stat(dev, OMAP_I2C_STAT_NACK);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return -EIO;
|
|
}
|
|
}
|
|
|
|
|
|
cpu_relax();
|
|
cpu_relax();
|
|
- *stat = omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG);
|
|
|
|
- }
|
|
|
|
|
|
+ } while (--timeout);
|
|
|
|
|
|
if (!timeout) {
|
|
if (!timeout) {
|
|
dev_err(dev->dev, "timeout waiting on XUDF bit\n");
|
|
dev_err(dev->dev, "timeout waiting on XUDF bit\n");
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
- *err |= OMAP_I2C_STAT_XUDF;
|
|
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static void omap_i2c_receive_data(struct omap_i2c_dev *dev, u8 num_bytes,
|
|
|
|
+ bool is_rdr)
|
|
|
|
+{
|
|
|
|
+ u16 w;
|
|
|
|
+
|
|
|
|
+ while (num_bytes--) {
|
|
|
|
+ w = omap_i2c_read_reg(dev, OMAP_I2C_DATA_REG);
|
|
|
|
+ *dev->buf++ = w;
|
|
|
|
+ dev->buf_len--;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Data reg in 2430, omap3 and
|
|
|
|
+ * omap4 is 8 bit wide
|
|
|
|
+ */
|
|
|
|
+ if (dev->flags & OMAP_I2C_FLAG_16BIT_DATA_REG) {
|
|
|
|
+ *dev->buf++ = w >> 8;
|
|
|
|
+ dev->buf_len--;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int omap_i2c_transmit_data(struct omap_i2c_dev *dev, u8 num_bytes,
|
|
|
|
+ bool is_xdr)
|
|
|
|
+{
|
|
|
|
+ u16 w;
|
|
|
|
+
|
|
|
|
+ while (num_bytes--) {
|
|
|
|
+ w = *dev->buf++;
|
|
|
|
+ dev->buf_len--;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Data reg in 2430, omap3 and
|
|
|
|
+ * omap4 is 8 bit wide
|
|
|
|
+ */
|
|
|
|
+ if (dev->flags & OMAP_I2C_FLAG_16BIT_DATA_REG) {
|
|
|
|
+ w |= *dev->buf++ << 8;
|
|
|
|
+ dev->buf_len--;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (dev->errata & I2C_OMAP_ERRATA_I462) {
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ ret = errata_omap3_i462(dev);
|
|
|
|
+ if (ret < 0)
|
|
|
|
+ return ret;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ omap_i2c_write_reg(dev, OMAP_I2C_DATA_REG, w);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static irqreturn_t
|
|
|
|
+omap_i2c_isr(int irq, void *dev_id)
|
|
|
|
+{
|
|
|
|
+ struct omap_i2c_dev *dev = dev_id;
|
|
|
|
+ irqreturn_t ret = IRQ_HANDLED;
|
|
|
|
+ u16 mask;
|
|
|
|
+ u16 stat;
|
|
|
|
+
|
|
|
|
+ spin_lock(&dev->lock);
|
|
|
|
+ mask = omap_i2c_read_reg(dev, OMAP_I2C_IE_REG);
|
|
|
|
+ stat = omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG);
|
|
|
|
+
|
|
|
|
+ if (stat & mask)
|
|
|
|
+ ret = IRQ_WAKE_THREAD;
|
|
|
|
+
|
|
|
|
+ spin_unlock(&dev->lock);
|
|
|
|
+
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
static irqreturn_t
|
|
static irqreturn_t
|
|
-omap_i2c_isr(int this_irq, void *dev_id)
|
|
|
|
|
|
+omap_i2c_isr_thread(int this_irq, void *dev_id)
|
|
{
|
|
{
|
|
struct omap_i2c_dev *dev = dev_id;
|
|
struct omap_i2c_dev *dev = dev_id;
|
|
|
|
+ unsigned long flags;
|
|
u16 bits;
|
|
u16 bits;
|
|
- u16 stat, w;
|
|
|
|
- int err, count = 0;
|
|
|
|
|
|
+ u16 stat;
|
|
|
|
+ int err = 0, count = 0;
|
|
|
|
+
|
|
|
|
+ spin_lock_irqsave(&dev->lock, flags);
|
|
|
|
+ do {
|
|
|
|
+ bits = omap_i2c_read_reg(dev, OMAP_I2C_IE_REG);
|
|
|
|
+ stat = omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG);
|
|
|
|
+ stat &= bits;
|
|
|
|
+
|
|
|
|
+ /* If we're in receiver mode, ignore XDR/XRDY */
|
|
|
|
+ if (dev->receiver)
|
|
|
|
+ stat &= ~(OMAP_I2C_STAT_XDR | OMAP_I2C_STAT_XRDY);
|
|
|
|
+ else
|
|
|
|
+ stat &= ~(OMAP_I2C_STAT_RDR | OMAP_I2C_STAT_RRDY);
|
|
|
|
|
|
- if (pm_runtime_suspended(dev->dev))
|
|
|
|
- return IRQ_NONE;
|
|
|
|
|
|
+ if (!stat) {
|
|
|
|
+ /* my work here is done */
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
|
|
- bits = omap_i2c_read_reg(dev, OMAP_I2C_IE_REG);
|
|
|
|
- while ((stat = (omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG))) & bits) {
|
|
|
|
dev_dbg(dev->dev, "IRQ (ISR = 0x%04x)\n", stat);
|
|
dev_dbg(dev->dev, "IRQ (ISR = 0x%04x)\n", stat);
|
|
if (count++ == 100) {
|
|
if (count++ == 100) {
|
|
dev_warn(dev->dev, "Too much work in one IRQ\n");
|
|
dev_warn(dev->dev, "Too much work in one IRQ\n");
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
- err = 0;
|
|
|
|
-complete:
|
|
|
|
- /*
|
|
|
|
- * Ack the stat in one go, but [R/X]DR and [R/X]RDY should be
|
|
|
|
- * acked after the data operation is complete.
|
|
|
|
- * Ref: TRM SWPU114Q Figure 18-31
|
|
|
|
- */
|
|
|
|
- omap_i2c_write_reg(dev, OMAP_I2C_STAT_REG, stat &
|
|
|
|
- ~(OMAP_I2C_STAT_RRDY | OMAP_I2C_STAT_RDR |
|
|
|
|
- OMAP_I2C_STAT_XRDY | OMAP_I2C_STAT_XDR));
|
|
|
|
-
|
|
|
|
- if (stat & OMAP_I2C_STAT_NACK)
|
|
|
|
|
|
+ if (stat & OMAP_I2C_STAT_NACK) {
|
|
err |= OMAP_I2C_STAT_NACK;
|
|
err |= OMAP_I2C_STAT_NACK;
|
|
|
|
+ omap_i2c_ack_stat(dev, OMAP_I2C_STAT_NACK);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
|
|
if (stat & OMAP_I2C_STAT_AL) {
|
|
if (stat & OMAP_I2C_STAT_AL) {
|
|
dev_err(dev->dev, "Arbitration lost\n");
|
|
dev_err(dev->dev, "Arbitration lost\n");
|
|
err |= OMAP_I2C_STAT_AL;
|
|
err |= OMAP_I2C_STAT_AL;
|
|
|
|
+ omap_i2c_ack_stat(dev, OMAP_I2C_STAT_AL);
|
|
|
|
+ break;
|
|
}
|
|
}
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* ProDB0017052: Clear ARDY bit twice
|
|
* ProDB0017052: Clear ARDY bit twice
|
|
*/
|
|
*/
|
|
if (stat & (OMAP_I2C_STAT_ARDY | OMAP_I2C_STAT_NACK |
|
|
if (stat & (OMAP_I2C_STAT_ARDY | OMAP_I2C_STAT_NACK |
|
|
OMAP_I2C_STAT_AL)) {
|
|
OMAP_I2C_STAT_AL)) {
|
|
- omap_i2c_ack_stat(dev, stat &
|
|
|
|
- (OMAP_I2C_STAT_RRDY | OMAP_I2C_STAT_RDR |
|
|
|
|
- OMAP_I2C_STAT_XRDY | OMAP_I2C_STAT_XDR |
|
|
|
|
- OMAP_I2C_STAT_ARDY));
|
|
|
|
- omap_i2c_complete_cmd(dev, err);
|
|
|
|
- return IRQ_HANDLED;
|
|
|
|
|
|
+ omap_i2c_ack_stat(dev, (OMAP_I2C_STAT_RRDY |
|
|
|
|
+ OMAP_I2C_STAT_RDR |
|
|
|
|
+ OMAP_I2C_STAT_XRDY |
|
|
|
|
+ OMAP_I2C_STAT_XDR |
|
|
|
|
+ OMAP_I2C_STAT_ARDY));
|
|
|
|
+ break;
|
|
}
|
|
}
|
|
- if (stat & (OMAP_I2C_STAT_RRDY | OMAP_I2C_STAT_RDR)) {
|
|
|
|
|
|
+
|
|
|
|
+ if (stat & OMAP_I2C_STAT_RDR) {
|
|
u8 num_bytes = 1;
|
|
u8 num_bytes = 1;
|
|
|
|
|
|
|
|
+ if (dev->fifo_size)
|
|
|
|
+ num_bytes = dev->buf_len;
|
|
|
|
+
|
|
|
|
+ omap_i2c_receive_data(dev, num_bytes, true);
|
|
|
|
+
|
|
if (dev->errata & I2C_OMAP_ERRATA_I207)
|
|
if (dev->errata & I2C_OMAP_ERRATA_I207)
|
|
i2c_omap_errata_i207(dev, stat);
|
|
i2c_omap_errata_i207(dev, stat);
|
|
|
|
|
|
- if (dev->fifo_size) {
|
|
|
|
- if (stat & OMAP_I2C_STAT_RRDY)
|
|
|
|
- num_bytes = dev->fifo_size;
|
|
|
|
- else /* read RXSTAT on RDR interrupt */
|
|
|
|
- num_bytes = (omap_i2c_read_reg(dev,
|
|
|
|
- OMAP_I2C_BUFSTAT_REG)
|
|
|
|
- >> 8) & 0x3F;
|
|
|
|
- }
|
|
|
|
- while (num_bytes) {
|
|
|
|
- num_bytes--;
|
|
|
|
- w = omap_i2c_read_reg(dev, OMAP_I2C_DATA_REG);
|
|
|
|
- if (dev->buf_len) {
|
|
|
|
- *dev->buf++ = w;
|
|
|
|
- dev->buf_len--;
|
|
|
|
- /*
|
|
|
|
- * Data reg in 2430, omap3 and
|
|
|
|
- * omap4 is 8 bit wide
|
|
|
|
- */
|
|
|
|
- if (dev->flags &
|
|
|
|
- OMAP_I2C_FLAG_16BIT_DATA_REG) {
|
|
|
|
- if (dev->buf_len) {
|
|
|
|
- *dev->buf++ = w >> 8;
|
|
|
|
- dev->buf_len--;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- } else {
|
|
|
|
- if (stat & OMAP_I2C_STAT_RRDY)
|
|
|
|
- dev_err(dev->dev,
|
|
|
|
- "RRDY IRQ while no data"
|
|
|
|
- " requested\n");
|
|
|
|
- if (stat & OMAP_I2C_STAT_RDR)
|
|
|
|
- dev_err(dev->dev,
|
|
|
|
- "RDR IRQ while no data"
|
|
|
|
- " requested\n");
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- omap_i2c_ack_stat(dev,
|
|
|
|
- stat & (OMAP_I2C_STAT_RRDY | OMAP_I2C_STAT_RDR));
|
|
|
|
|
|
+ omap_i2c_ack_stat(dev, OMAP_I2C_STAT_RDR);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (stat & OMAP_I2C_STAT_RRDY) {
|
|
|
|
+ u8 num_bytes = 1;
|
|
|
|
+
|
|
|
|
+ if (dev->threshold)
|
|
|
|
+ num_bytes = dev->threshold;
|
|
|
|
+
|
|
|
|
+ omap_i2c_receive_data(dev, num_bytes, false);
|
|
|
|
+ omap_i2c_ack_stat(dev, OMAP_I2C_STAT_RRDY);
|
|
continue;
|
|
continue;
|
|
}
|
|
}
|
|
- if (stat & (OMAP_I2C_STAT_XRDY | OMAP_I2C_STAT_XDR)) {
|
|
|
|
|
|
+
|
|
|
|
+ if (stat & OMAP_I2C_STAT_XDR) {
|
|
u8 num_bytes = 1;
|
|
u8 num_bytes = 1;
|
|
- if (dev->fifo_size) {
|
|
|
|
- if (stat & OMAP_I2C_STAT_XRDY)
|
|
|
|
- num_bytes = dev->fifo_size;
|
|
|
|
- else /* read TXSTAT on XDR interrupt */
|
|
|
|
- num_bytes = omap_i2c_read_reg(dev,
|
|
|
|
- OMAP_I2C_BUFSTAT_REG)
|
|
|
|
- & 0x3F;
|
|
|
|
- }
|
|
|
|
- while (num_bytes) {
|
|
|
|
- num_bytes--;
|
|
|
|
- w = 0;
|
|
|
|
- if (dev->buf_len) {
|
|
|
|
- w = *dev->buf++;
|
|
|
|
- dev->buf_len--;
|
|
|
|
- /*
|
|
|
|
- * Data reg in 2430, omap3 and
|
|
|
|
- * omap4 is 8 bit wide
|
|
|
|
- */
|
|
|
|
- if (dev->flags &
|
|
|
|
- OMAP_I2C_FLAG_16BIT_DATA_REG) {
|
|
|
|
- if (dev->buf_len) {
|
|
|
|
- w |= *dev->buf++ << 8;
|
|
|
|
- dev->buf_len--;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- } else {
|
|
|
|
- if (stat & OMAP_I2C_STAT_XRDY)
|
|
|
|
- dev_err(dev->dev,
|
|
|
|
- "XRDY IRQ while no "
|
|
|
|
- "data to send\n");
|
|
|
|
- if (stat & OMAP_I2C_STAT_XDR)
|
|
|
|
- dev_err(dev->dev,
|
|
|
|
- "XDR IRQ while no "
|
|
|
|
- "data to send\n");
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if ((dev->errata & I2C_OMAP_ERRATA_I462) &&
|
|
|
|
- errata_omap3_i462(dev, &stat, &err))
|
|
|
|
- goto complete;
|
|
|
|
-
|
|
|
|
- omap_i2c_write_reg(dev, OMAP_I2C_DATA_REG, w);
|
|
|
|
- }
|
|
|
|
- omap_i2c_ack_stat(dev,
|
|
|
|
- stat & (OMAP_I2C_STAT_XRDY | OMAP_I2C_STAT_XDR));
|
|
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ if (dev->fifo_size)
|
|
|
|
+ num_bytes = dev->buf_len;
|
|
|
|
+
|
|
|
|
+ ret = omap_i2c_transmit_data(dev, num_bytes, true);
|
|
|
|
+ if (ret < 0)
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ omap_i2c_ack_stat(dev, OMAP_I2C_STAT_XDR);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (stat & OMAP_I2C_STAT_XRDY) {
|
|
|
|
+ u8 num_bytes = 1;
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ if (dev->threshold)
|
|
|
|
+ num_bytes = dev->threshold;
|
|
|
|
+
|
|
|
|
+ ret = omap_i2c_transmit_data(dev, num_bytes, false);
|
|
|
|
+ if (ret < 0)
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ omap_i2c_ack_stat(dev, OMAP_I2C_STAT_XRDY);
|
|
continue;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
+
|
|
if (stat & OMAP_I2C_STAT_ROVR) {
|
|
if (stat & OMAP_I2C_STAT_ROVR) {
|
|
dev_err(dev->dev, "Receive overrun\n");
|
|
dev_err(dev->dev, "Receive overrun\n");
|
|
- dev->cmd_err |= OMAP_I2C_STAT_ROVR;
|
|
|
|
|
|
+ err |= OMAP_I2C_STAT_ROVR;
|
|
|
|
+ omap_i2c_ack_stat(dev, OMAP_I2C_STAT_ROVR);
|
|
|
|
+ break;
|
|
}
|
|
}
|
|
|
|
+
|
|
if (stat & OMAP_I2C_STAT_XUDF) {
|
|
if (stat & OMAP_I2C_STAT_XUDF) {
|
|
dev_err(dev->dev, "Transmit underflow\n");
|
|
dev_err(dev->dev, "Transmit underflow\n");
|
|
- dev->cmd_err |= OMAP_I2C_STAT_XUDF;
|
|
|
|
|
|
+ err |= OMAP_I2C_STAT_XUDF;
|
|
|
|
+ omap_i2c_ack_stat(dev, OMAP_I2C_STAT_XUDF);
|
|
|
|
+ break;
|
|
}
|
|
}
|
|
- }
|
|
|
|
|
|
+ } while (stat);
|
|
|
|
+
|
|
|
|
+ omap_i2c_complete_cmd(dev, err);
|
|
|
|
+
|
|
|
|
+out:
|
|
|
|
+ spin_unlock_irqrestore(&dev->lock, flags);
|
|
|
|
|
|
- return count ? IRQ_HANDLED : IRQ_NONE;
|
|
|
|
|
|
+ return IRQ_HANDLED;
|
|
}
|
|
}
|
|
|
|
|
|
static const struct i2c_algorithm omap_i2c_algo = {
|
|
static const struct i2c_algorithm omap_i2c_algo = {
|
|
@@ -943,12 +1057,12 @@ omap_i2c_probe(struct platform_device *pdev)
|
|
{
|
|
{
|
|
struct omap_i2c_dev *dev;
|
|
struct omap_i2c_dev *dev;
|
|
struct i2c_adapter *adap;
|
|
struct i2c_adapter *adap;
|
|
- struct resource *mem, *irq, *ioarea;
|
|
|
|
|
|
+ struct resource *mem;
|
|
const struct omap_i2c_bus_platform_data *pdata =
|
|
const struct omap_i2c_bus_platform_data *pdata =
|
|
pdev->dev.platform_data;
|
|
pdev->dev.platform_data;
|
|
struct device_node *node = pdev->dev.of_node;
|
|
struct device_node *node = pdev->dev.of_node;
|
|
const struct of_device_id *match;
|
|
const struct of_device_id *match;
|
|
- irq_handler_t isr;
|
|
|
|
|
|
+ int irq;
|
|
int r;
|
|
int r;
|
|
|
|
|
|
/* NOTE: driver uses the static register mapping */
|
|
/* NOTE: driver uses the static register mapping */
|
|
@@ -957,23 +1071,23 @@ omap_i2c_probe(struct platform_device *pdev)
|
|
dev_err(&pdev->dev, "no mem resource?\n");
|
|
dev_err(&pdev->dev, "no mem resource?\n");
|
|
return -ENODEV;
|
|
return -ENODEV;
|
|
}
|
|
}
|
|
- irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
|
|
|
- if (!irq) {
|
|
|
|
|
|
+
|
|
|
|
+ irq = platform_get_irq(pdev, 0);
|
|
|
|
+ if (irq < 0) {
|
|
dev_err(&pdev->dev, "no irq resource?\n");
|
|
dev_err(&pdev->dev, "no irq resource?\n");
|
|
- return -ENODEV;
|
|
|
|
|
|
+ return irq;
|
|
}
|
|
}
|
|
|
|
|
|
- ioarea = request_mem_region(mem->start, resource_size(mem),
|
|
|
|
- pdev->name);
|
|
|
|
- if (!ioarea) {
|
|
|
|
- dev_err(&pdev->dev, "I2C region already claimed\n");
|
|
|
|
- return -EBUSY;
|
|
|
|
|
|
+ dev = devm_kzalloc(&pdev->dev, sizeof(struct omap_i2c_dev), GFP_KERNEL);
|
|
|
|
+ if (!dev) {
|
|
|
|
+ dev_err(&pdev->dev, "Menory allocation failed\n");
|
|
|
|
+ return -ENOMEM;
|
|
}
|
|
}
|
|
|
|
|
|
- dev = kzalloc(sizeof(struct omap_i2c_dev), GFP_KERNEL);
|
|
|
|
- if (!dev) {
|
|
|
|
- r = -ENOMEM;
|
|
|
|
- goto err_release_region;
|
|
|
|
|
|
+ dev->base = devm_request_and_ioremap(&pdev->dev, mem);
|
|
|
|
+ if (!dev->base) {
|
|
|
|
+ dev_err(&pdev->dev, "I2C region already claimed\n");
|
|
|
|
+ return -ENOMEM;
|
|
}
|
|
}
|
|
|
|
|
|
match = of_match_device(of_match_ptr(omap_i2c_of_match), &pdev->dev);
|
|
match = of_match_device(of_match_ptr(omap_i2c_of_match), &pdev->dev);
|
|
@@ -990,17 +1104,13 @@ omap_i2c_probe(struct platform_device *pdev)
|
|
} else if (pdata != NULL) {
|
|
} else if (pdata != NULL) {
|
|
dev->speed = pdata->clkrate;
|
|
dev->speed = pdata->clkrate;
|
|
dev->flags = pdata->flags;
|
|
dev->flags = pdata->flags;
|
|
- dev->set_mpu_wkup_lat = pdata->set_mpu_wkup_lat;
|
|
|
|
dev->dtrev = pdata->rev;
|
|
dev->dtrev = pdata->rev;
|
|
}
|
|
}
|
|
|
|
|
|
dev->dev = &pdev->dev;
|
|
dev->dev = &pdev->dev;
|
|
- dev->irq = irq->start;
|
|
|
|
- dev->base = ioremap(mem->start, resource_size(mem));
|
|
|
|
- if (!dev->base) {
|
|
|
|
- r = -ENOMEM;
|
|
|
|
- goto err_free_mem;
|
|
|
|
- }
|
|
|
|
|
|
+ dev->irq = irq;
|
|
|
|
+
|
|
|
|
+ spin_lock_init(&dev->lock);
|
|
|
|
|
|
platform_set_drvdata(pdev, dev);
|
|
platform_set_drvdata(pdev, dev);
|
|
init_completion(&dev->cmd_complete);
|
|
init_completion(&dev->cmd_complete);
|
|
@@ -1013,6 +1123,9 @@ omap_i2c_probe(struct platform_device *pdev)
|
|
dev->regs = (u8 *)reg_map_ip_v1;
|
|
dev->regs = (u8 *)reg_map_ip_v1;
|
|
|
|
|
|
pm_runtime_enable(dev->dev);
|
|
pm_runtime_enable(dev->dev);
|
|
|
|
+ pm_runtime_set_autosuspend_delay(dev->dev, OMAP_I2C_PM_TIMEOUT);
|
|
|
|
+ pm_runtime_use_autosuspend(dev->dev);
|
|
|
|
+
|
|
r = pm_runtime_get_sync(dev->dev);
|
|
r = pm_runtime_get_sync(dev->dev);
|
|
if (IS_ERR_VALUE(r))
|
|
if (IS_ERR_VALUE(r))
|
|
goto err_free_mem;
|
|
goto err_free_mem;
|
|
@@ -1042,32 +1155,31 @@ omap_i2c_probe(struct platform_device *pdev)
|
|
|
|
|
|
dev->fifo_size = (dev->fifo_size / 2);
|
|
dev->fifo_size = (dev->fifo_size / 2);
|
|
|
|
|
|
- if (dev->rev >= OMAP_I2C_REV_ON_3630_4430)
|
|
|
|
- dev->b_hw = 0; /* Disable hardware fixes */
|
|
|
|
- else
|
|
|
|
|
|
+ if (dev->rev < OMAP_I2C_REV_ON_3630_4430)
|
|
dev->b_hw = 1; /* Enable hardware fixes */
|
|
dev->b_hw = 1; /* Enable hardware fixes */
|
|
|
|
|
|
/* calculate wakeup latency constraint for MPU */
|
|
/* calculate wakeup latency constraint for MPU */
|
|
- if (dev->set_mpu_wkup_lat != NULL)
|
|
|
|
- dev->latency = (1000000 * dev->fifo_size) /
|
|
|
|
- (1000 * dev->speed / 8);
|
|
|
|
|
|
+ dev->latency = (1000000 * dev->fifo_size) /
|
|
|
|
+ (1000 * dev->speed / 8);
|
|
}
|
|
}
|
|
|
|
|
|
/* reset ASAP, clearing any IRQs */
|
|
/* reset ASAP, clearing any IRQs */
|
|
omap_i2c_init(dev);
|
|
omap_i2c_init(dev);
|
|
|
|
|
|
- isr = (dev->rev < OMAP_I2C_OMAP1_REV_2) ? omap_i2c_omap1_isr :
|
|
|
|
- omap_i2c_isr;
|
|
|
|
- r = request_irq(dev->irq, isr, IRQF_NO_SUSPEND, pdev->name, dev);
|
|
|
|
|
|
+ if (dev->rev < OMAP_I2C_OMAP1_REV_2)
|
|
|
|
+ r = devm_request_irq(&pdev->dev, dev->irq, omap_i2c_omap1_isr,
|
|
|
|
+ IRQF_NO_SUSPEND, pdev->name, dev);
|
|
|
|
+ else
|
|
|
|
+ r = devm_request_threaded_irq(&pdev->dev, dev->irq,
|
|
|
|
+ omap_i2c_isr, omap_i2c_isr_thread,
|
|
|
|
+ IRQF_NO_SUSPEND | IRQF_ONESHOT,
|
|
|
|
+ pdev->name, dev);
|
|
|
|
|
|
if (r) {
|
|
if (r) {
|
|
dev_err(dev->dev, "failure requesting irq %i\n", dev->irq);
|
|
dev_err(dev->dev, "failure requesting irq %i\n", dev->irq);
|
|
goto err_unuse_clocks;
|
|
goto err_unuse_clocks;
|
|
}
|
|
}
|
|
|
|
|
|
- dev_info(dev->dev, "bus %d rev%d.%d.%d at %d kHz\n", pdev->id,
|
|
|
|
- dev->dtrev, dev->rev >> 4, dev->rev & 0xf, dev->speed);
|
|
|
|
-
|
|
|
|
adap = &dev->adapter;
|
|
adap = &dev->adapter;
|
|
i2c_set_adapdata(adap, dev);
|
|
i2c_set_adapdata(adap, dev);
|
|
adap->owner = THIS_MODULE;
|
|
adap->owner = THIS_MODULE;
|
|
@@ -1082,27 +1194,25 @@ omap_i2c_probe(struct platform_device *pdev)
|
|
r = i2c_add_numbered_adapter(adap);
|
|
r = i2c_add_numbered_adapter(adap);
|
|
if (r) {
|
|
if (r) {
|
|
dev_err(dev->dev, "failure adding adapter\n");
|
|
dev_err(dev->dev, "failure adding adapter\n");
|
|
- goto err_free_irq;
|
|
|
|
|
|
+ goto err_unuse_clocks;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ dev_info(dev->dev, "bus %d rev%d.%d.%d at %d kHz\n", adap->nr,
|
|
|
|
+ dev->dtrev, dev->rev >> 4, dev->rev & 0xf, dev->speed);
|
|
|
|
+
|
|
of_i2c_register_devices(adap);
|
|
of_i2c_register_devices(adap);
|
|
|
|
|
|
- pm_runtime_put(dev->dev);
|
|
|
|
|
|
+ pm_runtime_mark_last_busy(dev->dev);
|
|
|
|
+ pm_runtime_put_autosuspend(dev->dev);
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
-err_free_irq:
|
|
|
|
- free_irq(dev->irq, dev);
|
|
|
|
err_unuse_clocks:
|
|
err_unuse_clocks:
|
|
omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0);
|
|
omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0);
|
|
pm_runtime_put(dev->dev);
|
|
pm_runtime_put(dev->dev);
|
|
- iounmap(dev->base);
|
|
|
|
pm_runtime_disable(&pdev->dev);
|
|
pm_runtime_disable(&pdev->dev);
|
|
err_free_mem:
|
|
err_free_mem:
|
|
platform_set_drvdata(pdev, NULL);
|
|
platform_set_drvdata(pdev, NULL);
|
|
- kfree(dev);
|
|
|
|
-err_release_region:
|
|
|
|
- release_mem_region(mem->start, resource_size(mem));
|
|
|
|
|
|
|
|
return r;
|
|
return r;
|
|
}
|
|
}
|
|
@@ -1110,12 +1220,10 @@ err_release_region:
|
|
static int __devexit omap_i2c_remove(struct platform_device *pdev)
|
|
static int __devexit omap_i2c_remove(struct platform_device *pdev)
|
|
{
|
|
{
|
|
struct omap_i2c_dev *dev = platform_get_drvdata(pdev);
|
|
struct omap_i2c_dev *dev = platform_get_drvdata(pdev);
|
|
- struct resource *mem;
|
|
|
|
int ret;
|
|
int ret;
|
|
|
|
|
|
platform_set_drvdata(pdev, NULL);
|
|
platform_set_drvdata(pdev, NULL);
|
|
|
|
|
|
- free_irq(dev->irq, dev);
|
|
|
|
i2c_del_adapter(&dev->adapter);
|
|
i2c_del_adapter(&dev->adapter);
|
|
ret = pm_runtime_get_sync(&pdev->dev);
|
|
ret = pm_runtime_get_sync(&pdev->dev);
|
|
if (IS_ERR_VALUE(ret))
|
|
if (IS_ERR_VALUE(ret))
|
|
@@ -1124,10 +1232,6 @@ static int __devexit omap_i2c_remove(struct platform_device *pdev)
|
|
omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0);
|
|
omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0);
|
|
pm_runtime_put(&pdev->dev);
|
|
pm_runtime_put(&pdev->dev);
|
|
pm_runtime_disable(&pdev->dev);
|
|
pm_runtime_disable(&pdev->dev);
|
|
- iounmap(dev->base);
|
|
|
|
- kfree(dev);
|
|
|
|
- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
|
|
- release_mem_region(mem->start, resource_size(mem));
|
|
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|