summaryrefslogtreecommitdiff
path: root/drivers/i2c/busses/i2c-cadence.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2021-07-04 11:47:18 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2021-07-04 11:47:18 -0700
commit855ff900b8605df2b9ea309534cd2f02dc0c4cb8 (patch)
treec6f38bbb17d8702c7a692b383be94a59d451922b /drivers/i2c/busses/i2c-cadence.c
parentd2500a0c0e73d4387cde9185edcdf397f52e428b (diff)
parent9d6336831bdc78e5207eaf147cc17228b5e984c3 (diff)
Merge branch 'i2c/for-mergewindow' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux
Pull i2c updates from Wolfram Sang: - core supports now bus regulators controlling power for SCL/SDA - quite some DT binding conversions to YAML - added a seperate DT binding for the optional SMBus Alert feature - documentation with examples how to deal with I2C sysfs files - some bigger rework for the i801 driver - and a few usual driver updates * 'i2c/for-mergewindow' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux: (42 commits) i2c: ali1535: mention that the device should not be disabled i2c: mpc: Restore reread of I2C status register i2c: core-smbus: Expose PEC calculate function for generic use Documentation: i2c: Add doc for I2C sysfs i2c: core: Disable client irq on reboot/shutdown dt-bindings: i2c: update bindings for MT8195 SoC i2c: imx: Fix some checkpatch warnings i2c: davinci: Simplify with dev_err_probe() i2c: cadence: Simplify with dev_err_probe() i2c: xiic: Simplify with dev_err_probe() i2c: cadence: Clear HOLD bit before xfer_size register rolls over dt-bindings: i2c: ce4100: Replace "ti,pcf8575" by "nxp,pcf8575" i2c: i801: Improve i801_setup_hstcfg i2c: i801: Use driver name constant instead of function dev_driver_string i2c: i801: Simplify initialization of i2c_board_info in i801_probe_optional_slaves i2c: i801: Improve status polling i2c: cht-wc: Replace of_node by NULL i2c: riic: Add RZ/G2L support dt-bindings: i2c: renesas,riic: Document RZ/G2L I2C controller dt-bindings: i2c: renesas,iic: Convert to json-schema ...
Diffstat (limited to 'drivers/i2c/busses/i2c-cadence.c')
-rw-r--r--drivers/i2c/busses/i2c-cadence.c57
1 files changed, 45 insertions, 12 deletions
diff --git a/drivers/i2c/busses/i2c-cadence.c b/drivers/i2c/busses/i2c-cadence.c
index 66aafa7d1123..20aa3398e642 100644
--- a/drivers/i2c/busses/i2c-cadence.c
+++ b/drivers/i2c/busses/i2c-cadence.c
@@ -578,6 +578,11 @@ static void cdns_i2c_mrecv(struct cdns_i2c *id)
{
unsigned int ctrl_reg;
unsigned int isr_status;
+ unsigned long flags;
+ bool hold_clear = false;
+ bool irq_save = false;
+
+ u32 addr;
id->p_recv_buf = id->p_msg->buf;
id->recv_count = id->p_msg->len;
@@ -618,14 +623,43 @@ static void cdns_i2c_mrecv(struct cdns_i2c *id)
cdns_i2c_writereg(id->recv_count, CDNS_I2C_XFER_SIZE_OFFSET);
}
- /* Set the slave address in address register - triggers operation */
- cdns_i2c_writereg(id->p_msg->addr & CDNS_I2C_ADDR_MASK,
- CDNS_I2C_ADDR_OFFSET);
- /* Clear the bus hold flag if bytes to receive is less than FIFO size */
+ /* Determine hold_clear based on number of bytes to receive and hold flag */
if (!id->bus_hold_flag &&
- ((id->p_msg->flags & I2C_M_RECV_LEN) != I2C_M_RECV_LEN) &&
- (id->recv_count <= CDNS_I2C_FIFO_DEPTH))
- cdns_i2c_clear_bus_hold(id);
+ ((id->p_msg->flags & I2C_M_RECV_LEN) != I2C_M_RECV_LEN) &&
+ (id->recv_count <= CDNS_I2C_FIFO_DEPTH)) {
+ if (cdns_i2c_readreg(CDNS_I2C_CR_OFFSET) & CDNS_I2C_CR_HOLD) {
+ hold_clear = true;
+ if (id->quirks & CDNS_I2C_BROKEN_HOLD_BIT)
+ irq_save = true;
+ }
+ }
+
+ addr = id->p_msg->addr;
+ addr &= CDNS_I2C_ADDR_MASK;
+
+ if (hold_clear) {
+ ctrl_reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET) & ~CDNS_I2C_CR_HOLD;
+ /*
+ * In case of Xilinx Zynq SOC, clear the HOLD bit before transfer size
+ * register reaches '0'. This is an IP bug which causes transfer size
+ * register overflow to 0xFF. To satisfy this timing requirement,
+ * disable the interrupts on current processor core between register
+ * writes to slave address register and control register.
+ */
+ if (irq_save)
+ local_irq_save(flags);
+
+ cdns_i2c_writereg(addr, CDNS_I2C_ADDR_OFFSET);
+ cdns_i2c_writereg(ctrl_reg, CDNS_I2C_CR_OFFSET);
+ /* Read it back to avoid bufferring and make sure write happens */
+ cdns_i2c_readreg(CDNS_I2C_CR_OFFSET);
+
+ if (irq_save)
+ local_irq_restore(flags);
+ } else {
+ cdns_i2c_writereg(addr, CDNS_I2C_ADDR_OFFSET);
+ }
+
cdns_i2c_writereg(CDNS_I2C_ENABLED_INTR_MASK, CDNS_I2C_IER_OFFSET);
}
@@ -1217,11 +1251,10 @@ static int cdns_i2c_probe(struct platform_device *pdev)
"Cadence I2C at %08lx", (unsigned long)r_mem->start);
id->clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(id->clk)) {
- if (PTR_ERR(id->clk) != -EPROBE_DEFER)
- dev_err(&pdev->dev, "input clock not found.\n");
- return PTR_ERR(id->clk);
- }
+ if (IS_ERR(id->clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(id->clk),
+ "input clock not found.\n");
+
ret = clk_prepare_enable(id->clk);
if (ret)
dev_err(&pdev->dev, "Unable to enable clock.\n");