diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2015-11-10 22:58:25 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-11-10 22:58:25 +0300 |
commit | d55fc37856244c929965c190c8e9dcb49e2c07aa (patch) | |
tree | ccb348340a6f0aac546b06487ea6a1d1d35490f6 /drivers/i2c/busses/i2c-img-scb.c | |
parent | 42d4ebb42a17754d2e8344dc1aa486119671d0eb (diff) | |
parent | 75ecc64ef5a1f310fc80f732ad8cfb7e1bdc59d5 (diff) | |
download | linux-d55fc37856244c929965c190c8e9dcb49e2c07aa.tar.xz |
Merge branch 'i2c/for-4.4' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux
Pull i2c updates from Wolfram Sang:
- New drivers: UniPhier (with and without FIFO)
- some drivers got some bigger rework: ismt, designware, img-scb (rcar
had to be reverted because issues were showing up just lately)
- ACPI: reworked the device scanning and added support for muxes
... and quite a lot of driver bugfixes and cleanups this time. All
files touched outside of the i2c realm have proper acks.
* 'i2c/for-4.4' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux: (70 commits)
i2c: rcar: Revert the latest refactoring series
i2c: pnx: remove superfluous assignment
MAINTAINERS: i2c: drop i2c-pnx maintainer
MAINTAINERS: i2c: mark also subdirectories as maintained
i2c: cadence: enable driver for ARM64
i2c: i801: Document Intel DNV and Broxton
i2c: at91: manage unexpected RXRDY flag when starting a transfer
i2c: pnx: Use setup_timer instead of open coding it
i2c: add ACPI support for I2C mux ports
acpi: add acpi_preset_companion() stub
i2c: pxa: Add support for pxa910/988 & new configuration features
i2c: au1550: Convert to devm_kzalloc and devm_ioremap_resource
i2c-dev: Fix I2C_SLAVE ioctl comment
i2c-dev: Fix typo in ioctl name reference
i2c: sirf: tune the divider to make i2c bus freq more accurate
i2c: imx: Use -ENXIO as error in the NACK case
i2c: i801: Add support for Intel Broxton
i2c: i801: Add support for Intel DNV
i2c: mediatek: add i2c resume support
i2c: imx: implement bus recovery
...
Diffstat (limited to 'drivers/i2c/busses/i2c-img-scb.c')
-rw-r--r-- | drivers/i2c/busses/i2c-img-scb.c | 73 |
1 files changed, 43 insertions, 30 deletions
diff --git a/drivers/i2c/busses/i2c-img-scb.c b/drivers/i2c/busses/i2c-img-scb.c index 00ffd6613680..3795fe130ef2 100644 --- a/drivers/i2c/busses/i2c-img-scb.c +++ b/drivers/i2c/busses/i2c-img-scb.c @@ -278,8 +278,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, @@ -536,6 +534,7 @@ static void img_i2c_read_fifo(struct img_i2c *i2c) u32 fifo_status; u8 data; + img_i2c_wr_rd_fence(i2c); fifo_status = img_i2c_readl(i2c, SCB_FIFO_STATUS_REG); if (fifo_status & FIFO_READ_EMPTY) break; @@ -544,7 +543,6 @@ static void img_i2c_read_fifo(struct img_i2c *i2c) *i2c->msg.buf = data; img_i2c_writel(i2c, SCB_READ_FIFO_REG, 0xff); - img_i2c_wr_rd_fence(i2c); i2c->msg.len--; i2c->msg.buf++; } @@ -556,12 +554,12 @@ static void img_i2c_write_fifo(struct img_i2c *i2c) while (i2c->msg.len) { u32 fifo_status; + img_i2c_wr_rd_fence(i2c); fifo_status = img_i2c_readl(i2c, SCB_FIFO_STATUS_REG); if (fifo_status & FIFO_WRITE_FULL) break; img_i2c_writel(i2c, SCB_WRITE_DATA_REG, *i2c->msg.buf); - img_i2c_wr_rd_fence(i2c); i2c->msg.len--; i2c->msg.buf++; } @@ -859,7 +857,7 @@ static unsigned int img_i2c_auto(struct img_i2c *i2c, } /* Enable transaction halt on start bit */ - if (!i2c->last_msg && i2c->line_status & LINESTAT_START_BIT_DET) { + if (!i2c->last_msg && line_status & LINESTAT_START_BIT_DET) { img_i2c_transaction_halt(i2c, true); /* we're no longer interested in the slave event */ i2c->int_enable &= ~INT_SLAVE_EVENT; @@ -1062,6 +1060,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 if (msg->flags & I2C_M_RD) @@ -1120,13 +1127,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]; @@ -1136,6 +1138,17 @@ static int img_i2c_init(struct img_i2c *i2c) break; } } + 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); @@ -1182,32 +1195,32 @@ static int img_i2c_init(struct img_i2c *i2c) ((bitrate_khz * clk_period) / 2)) int_bitrate++; - /* Setup TCKH value */ - tckh = timing.tckh / clk_period; - if (timing.tckh % clk_period) - tckh++; + /* + * Setup clock duty cycle, start with 50% and adjust TCKH and TCKL + * values from there if they don't meet minimum timing requirements + */ + tckh = int_bitrate / 2; + tckl = int_bitrate - tckh; - if (tckh > 0) - data = tckh - 1; - else - data = 0; + /* Adjust TCKH and TCKL values */ + data = DIV_ROUND_UP(timing.tckl, clk_period); - img_i2c_writel(i2c, SCB_TIME_TCKH_REG, data); + if (tckl < data) { + tckl = data; + tckh = int_bitrate - tckl; + } - /* Setup TCKL value */ - tckl = int_bitrate - tckh; + if (tckh > 0) + --tckh; if (tckl > 0) - data = tckl - 1; - else - data = 0; + --tckl; - img_i2c_writel(i2c, SCB_TIME_TCKL_REG, data); + 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; |