aboutsummaryrefslogtreecommitdiff
path: root/drivers/i2c/busses/i2c-img-scb.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/i2c/busses/i2c-img-scb.c')
-rw-r--r--drivers/i2c/busses/i2c-img-scb.c185
1 files changed, 68 insertions, 117 deletions
diff --git a/drivers/i2c/busses/i2c-img-scb.c b/drivers/i2c/busses/i2c-img-scb.c
index bb98088402e..379ef9c3166 100644
--- a/drivers/i2c/busses/i2c-img-scb.c
+++ b/drivers/i2c/busses/i2c-img-scb.c
@@ -280,8 +280,6 @@
#define ISR_COMPLETE(err) (ISR_COMPLETE_M | (ISR_STATUS_M & (err)))
#define ISR_FATAL(err) (ISR_COMPLETE(err) | ISR_FATAL_M)
-#define REL_SOC_IP_SCB_2_2_1 0x00020201
-
enum img_i2c_mode {
MODE_INACTIVE,
MODE_RAW,
@@ -511,22 +509,8 @@ static void img_i2c_soft_reset(struct img_i2c *i2c)
{
i2c->t_halt = false;
img_i2c_writel(i2c, SCB_CONTROL_REG, 0);
-
- /* Disable all interrupts */
- img_i2c_writel(i2c, SCB_INT_MASK_REG, 0);
-
- /* Clear all interrupts */
- img_i2c_writel(i2c, SCB_INT_CLEAR_REG, ~0);
-
- /* Clear the scb_line_status events */
- img_i2c_writel(i2c, SCB_CLEAR_REG, ~0);
-
img_i2c_writel(i2c, SCB_CONTROL_REG,
SCB_CONTROL_CLK_ENABLE | SCB_CONTROL_SOFT_RESET);
-
- /* Enable interrupts */
- img_i2c_switch_mode(i2c, MODE_INACTIVE);
- img_i2c_writel(i2c, SCB_INT_MASK_REG, i2c->int_enable);
}
/*
@@ -638,10 +622,7 @@ static void img_i2c_complete_transaction(struct img_i2c *i2c, int status)
img_i2c_switch_mode(i2c, MODE_INACTIVE);
if (status) {
i2c->msg_status = status;
- img_i2c_soft_reset(i2c);
- } else {
- img_i2c_writel(i2c, SCB_INT_CLEAR_REG, ~0);
- img_i2c_writel(i2c, SCB_CLEAR_REG, ~0);
+ img_i2c_transaction_halt(i2c, false);
}
complete(&i2c->msg_complete);
}
@@ -780,8 +761,8 @@ static unsigned int img_i2c_atomic(struct img_i2c *i2c,
break;
case CMD_RET_ACK:
if (i2c->line_status & LINESTAT_ACK_DET ||
- (i2c->line_status & LINESTAT_NACK_DET
- && i2c->msg.flags & I2C_M_IGNORE_NAK)) {
+ (i2c->line_status & LINESTAT_NACK_DET &&
+ i2c->msg.flags & I2C_M_IGNORE_NAK)) {
if (i2c->msg.len == 0) {
next_cmd = CMD_GEN_STOP;
} else if (i2c->msg.flags & I2C_M_RD) {
@@ -888,87 +869,42 @@ static unsigned int img_i2c_auto(struct img_i2c *i2c,
}
/* Enable transaction halt on start bit */
- if (i2c->line_status & LINESTAT_START_BIT_DET) {
- if (!i2c->last_msg) {
- img_i2c_transaction_halt(i2c, true);
- /* we're no longer interested in the slave event */
- i2c->int_enable &= ~INT_SLAVE_EVENT;
- }
- /*
- * Remove start bit detected status after it is handled,
- * doing so will prevent this condition being hit for
- * every interrupt on a particular transfer.
- */
- i2c->line_status &= ~LINESTAT_START_BIT_DET;
+ if (!i2c->last_msg && line_status & LINESTAT_START_BIT_DET) {
+ img_i2c_transaction_halt(i2c, !i2c->last_msg);
+ /* we're no longer interested in the slave event */
+ i2c->int_enable &= ~INT_SLAVE_EVENT;
}
mod_timer(&i2c->check_timer, jiffies + msecs_to_jiffies(1));
+ if (int_status & INT_STOP_DETECTED) {
+ /* Drain remaining data in FIFO and complete transaction */
+ if (i2c->msg.flags & I2C_M_RD)
+ img_i2c_read_fifo(i2c);
+ return ISR_COMPLETE(0);
+ }
+
if (i2c->msg.flags & I2C_M_RD) {
- if (int_status & INT_MASTER_HALTED) {
+ if (int_status & (INT_FIFO_FULL_FILLING | INT_MASTER_HALTED)) {
img_i2c_read_fifo(i2c);
if (i2c->msg.len == 0)
- return ISR_COMPLETE(0);
- /*
- * By releasing and then enabling transaction halt,
- * trying to allow only a single byte to proceed.
- */
- img_i2c_transaction_halt(i2c, false);
- img_i2c_transaction_halt(i2c, !i2c->last_msg);
- }
- if (int_status & INT_FIFO_FULL_FILLING) {
- img_i2c_read_fifo(i2c);
- if (i2c->msg.len == 0) {
- if (i2c->last_msg)
- return ISR_WAITSTOP;
- return ISR_COMPLETE(0);
- }
- }
- if (int_status & INT_STOP_DETECTED) {
- int ret;
- /*
- * Stop bit indicates the end of the transfer, it means
- * we should read all the data (or drain the FIFO). We
- * must signal completion for this transaction.
- */
- img_i2c_transaction_halt(i2c, false);
- img_i2c_read_fifo(i2c);
- ret = (i2c->msg.len == 0) ? 0 : EIO;
- return ISR_COMPLETE(ret);
+ return ISR_WAITSTOP;
}
} else {
- if (int_status & INT_MASTER_HALTED) {
+ if (int_status & (INT_FIFO_EMPTY | INT_MASTER_HALTED)) {
if ((int_status & INT_FIFO_EMPTY) &&
- i2c->msg.len == 0)
- return ISR_COMPLETE(0);
- img_i2c_write_fifo(i2c);
- /*
- * By releasing and then enabling transaction halt,
- * trying to allow only a single byte to proceed.
- */
- img_i2c_transaction_halt(i2c, false);
- img_i2c_transaction_halt(i2c, !i2c->last_msg);
- }
- if (int_status & INT_FIFO_EMPTY) {
- if (i2c->msg.len == 0) {
- if (i2c->last_msg)
- return ISR_WAITSTOP;
- return ISR_COMPLETE(0);
- }
+ i2c->msg.len == 0)
+ return ISR_WAITSTOP;
img_i2c_write_fifo(i2c);
}
- if (int_status & INT_STOP_DETECTED) {
- int ret;
-
- img_i2c_transaction_halt(i2c, false);
- /*
- * Stop bit indicates the end of a transfer and if the
- * transfer has finished before all data is written to
- * the fifo return error with transfer complete signal.
- */
- ret = (i2c->msg.len == 0) ? 0 : EIO;
- return ISR_COMPLETE(ret);
- }
+ }
+ if (int_status & INT_MASTER_HALTED) {
+ /*
+ * Release and then enable transaction halt, to
+ * allow only a single byte to proceed.
+ */
+ img_i2c_transaction_halt(i2c, false);
+ img_i2c_transaction_halt(i2c, !i2c->last_msg);
}
return 0;
@@ -1147,6 +1083,15 @@ static int img_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
i2c->last_msg = (i == num - 1);
reinit_completion(&i2c->msg_complete);
+ /*
+ * Clear line status and all interrupts before starting a
+ * transfer, as we may have unserviced interrupts from
+ * previous transfers that might be handled in the context
+ * of the new transfer.
+ */
+ img_i2c_writel(i2c, SCB_INT_CLEAR_REG, ~0);
+ img_i2c_writel(i2c, SCB_CLEAR_REG, ~0);
+
if (atomic) {
img_i2c_atomic_start(i2c);
} else {
@@ -1162,8 +1107,8 @@ static int img_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
img_i2c_write(i2c);
/*
- * By releasing and then enabling transaction halt,
- * trying to allow only a single byte to proceed.
+ * Release and then enable transaction halt, to
+ * allow only a single byte to proceed.
* This doesn't have an effect on the initial transfer
* but will allow the following transfers to start
* processing if the previous transfer was marked as
@@ -1181,7 +1126,6 @@ static int img_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
if (time_left == 0) {
dev_err(adap->dev.parent, "i2c transfer timed out\n");
i2c->msg_status = -ETIMEDOUT;
- img_i2c_soft_reset(i2c);
break;
}
@@ -1225,13 +1169,8 @@ static int img_i2c_init(struct img_i2c *i2c)
return -EINVAL;
}
- if (rev >= REL_SOC_IP_SCB_2_2_1) {
- i2c->need_wr_rd_fence = true;
- dev_info(i2c->adap.dev.parent, "fence quirk enabled");
- }
-
- bitrate_khz = i2c->bitrate / 1000;
- clk_khz = clk_get_rate(i2c->scb_clk) / 1000;
+ /* Fencing enabled by default. */
+ i2c->need_wr_rd_fence = true;
/* Determine what mode we're in from the bitrate */
timing = timings[0];
@@ -1241,13 +1180,18 @@ static int img_i2c_init(struct img_i2c *i2c)
break;
}
}
- if (i2c->bitrate > timing.max_bitrate) {
- dev_err(i2c->adap.dev.parent,
- "requested bitrate (%d) not supported\n",
- i2c->bitrate);
- return -EINVAL;
+ if (i2c->bitrate > timings[ARRAY_SIZE(timings) - 1].max_bitrate) {
+ dev_warn(i2c->adap.dev.parent,
+ "requested bitrate (%u) is higher than the max bitrate supported (%u)\n",
+ i2c->bitrate,
+ timings[ARRAY_SIZE(timings) - 1].max_bitrate);
+ timing = timings[ARRAY_SIZE(timings) - 1];
+ i2c->bitrate = timing.max_bitrate;
}
+ bitrate_khz = i2c->bitrate / 1000;
+ clk_khz = clk_get_rate(i2c->scb_clk) / 1000;
+
/* Find the prescale that would give us that inc (approx delay = 0) */
prescale = SCB_OPT_INC * clk_khz / (256 * 16 * bitrate_khz);
prescale = clamp_t(unsigned int, prescale, 1, 8);
@@ -1297,14 +1241,11 @@ static int img_i2c_init(struct img_i2c *i2c)
* Setup clock duty cycle, start with 50% and adjust TCKH and TCKL
* values from there if they don't meet minimum timing requirements
*/
- tckh = tckl = int_bitrate / 2;
- if (int_bitrate % 2)
- tckl++;
+ tckh = int_bitrate / 2;
+ tckl = int_bitrate - tckh;
/* Adjust TCKH and TCKL values */
- data = timing.tckl / clk_period;
- if (timing.tckl % clk_period)
- data++;
+ data = DIV_ROUND_UP(timing.tckl, clk_period);
if (tckl < data) {
tckl = data;
@@ -1312,18 +1253,16 @@ static int img_i2c_init(struct img_i2c *i2c)
}
if (tckh > 0)
- tckh -= 1;
+ --tckh;
if (tckl > 0)
- tckl -= 1;
+ --tckl;
img_i2c_writel(i2c, SCB_TIME_TCKH_REG, tckh);
img_i2c_writel(i2c, SCB_TIME_TCKL_REG, tckl);
/* Setup TSDH value */
- tsdh = timing.tsdh / clk_period;
- if (timing.tsdh % clk_period)
- tsdh++;
+ tsdh = DIV_ROUND_UP(timing.tsdh, clk_period);
if (tsdh > 1)
data = tsdh - 1;
@@ -1362,6 +1301,18 @@ static int img_i2c_init(struct img_i2c *i2c)
/* Take module out of soft reset and enable clocks */
img_i2c_soft_reset(i2c);
+ /* Disable all interrupts */
+ img_i2c_writel(i2c, SCB_INT_MASK_REG, 0);
+
+ /* Clear all interrupts */
+ img_i2c_writel(i2c, SCB_INT_CLEAR_REG, ~0);
+
+ /* Clear the scb_line_status events */
+ img_i2c_writel(i2c, SCB_CLEAR_REG, ~0);
+
+ /* Enable interrupts */
+ img_i2c_writel(i2c, SCB_INT_MASK_REG, i2c->int_enable);
+
/* Perform a synchronous sequence to reset the bus */
ret = img_i2c_reset_bus(i2c);