diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-06-14 10:21:46 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-06-14 10:21:46 +0300 |
commit | 9bca19a01d50143b736f0f59eb3ccc05b1106172 (patch) | |
tree | 3321118b4a6bd4c949634bbdbb3d2c03454ae33b /drivers/i2c/busses/i2c-xlp9xx.c | |
parent | 463f202172c31b9c36278001cabfbad4e12da42e (diff) | |
parent | 53e39628ac228fada53cc0106be62c6f65f67501 (diff) | |
download | linux-9bca19a01d50143b736f0f59eb3ccc05b1106172.tar.xz |
Merge branch 'i2c/for-4.18' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux
Pull i2c updates from Wolfram Sang:
- mainly feature additions to drivers (stm32f7, qup, xlp9xx, mlxcpld, ...)
- conversion to use the i2c_8bit_addr_from_msg macro consistently
- move includes to platform_data
- core updates to allow the (still in review) I3C subsystem to connect
- and the regular share of smaller driver updates
* 'i2c/for-4.18' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux: (68 commits)
i2c: qup: fix building without CONFIG_ACPI
i2c: tegra: Remove suspend-resume
i2c: imx-lpi2c: Switch to SPDX identifier
i2c: mxs: Switch to SPDX identifier
i2c: busses: make use of i2c_8bit_addr_from_msg
i2c: algos: make use of i2c_8bit_addr_from_msg
i2c: rcar: document R8A77980 bindings
i2c: qup: Add command-line parameter to override SCL frequency
i2c: qup: Correct duty cycle for FM and FM+
i2c: qup: Add support for Fast Mode Plus
i2c: qup: add probe path for Centriq ACPI devices
i2c: robotfuzz-osif: drop pointless test
i2c: robotfuzz-osif: remove pointless local variable
i2c: rk3x: Don't print visible virtual mapping MMIO address
i2c: opal: don't check number of messages in the driver
i2c: ibm_iic: don't check number of messages in the driver
i2c: imx: Switch to SPDX identifier
i2c: mux: pca954x: merge calls to of_match_device and of_device_get_match_data
i2c: mux: demux-pinctrl: use proper parent device for demux adapter
i2c: mux: improve error message for failed symlink
...
Diffstat (limited to 'drivers/i2c/busses/i2c-xlp9xx.c')
-rw-r--r-- | drivers/i2c/busses/i2c-xlp9xx.c | 89 |
1 files changed, 68 insertions, 21 deletions
diff --git a/drivers/i2c/busses/i2c-xlp9xx.c b/drivers/i2c/busses/i2c-xlp9xx.c index eb8913eba0c5..1f41a4f89c08 100644 --- a/drivers/i2c/busses/i2c-xlp9xx.c +++ b/drivers/i2c/busses/i2c-xlp9xx.c @@ -10,6 +10,7 @@ #include <linux/clk.h> #include <linux/completion.h> #include <linux/i2c.h> +#include <linux/i2c-smbus.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/io.h> @@ -84,6 +85,8 @@ struct xlp9xx_i2c_dev { struct device *dev; struct i2c_adapter adapter; struct completion msg_complete; + struct i2c_smbus_alert_setup alert_data; + struct i2c_client *ara; int irq; bool msg_read; bool len_recv; @@ -155,9 +158,30 @@ static void xlp9xx_i2c_fill_tx_fifo(struct xlp9xx_i2c_dev *priv) priv->msg_buf += len; } +static void xlp9xx_i2c_update_rlen(struct xlp9xx_i2c_dev *priv) +{ + u32 val, len; + + /* + * Update receive length. Re-read len to get the latest value, + * and then add 4 to have a minimum value that can be safely + * written. This is to account for the byte read above, the + * transfer in progress and any delays in the register I/O + */ + val = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_CTRL); + len = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_FIFOWCNT) & + XLP9XX_I2C_FIFO_WCNT_MASK; + len = max_t(u32, priv->msg_len, len + 4); + if (len >= I2C_SMBUS_BLOCK_MAX + 2) + return; + val = (val & ~XLP9XX_I2C_CTRL_MCTLEN_MASK) | + (len << XLP9XX_I2C_CTRL_MCTLEN_SHIFT); + xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_CTRL, val); +} + static void xlp9xx_i2c_drain_rx_fifo(struct xlp9xx_i2c_dev *priv) { - u32 len, i, val; + u32 len, i; u8 rlen, *buf = priv->msg_buf; len = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_FIFOWCNT) & @@ -167,21 +191,20 @@ static void xlp9xx_i2c_drain_rx_fifo(struct xlp9xx_i2c_dev *priv) if (priv->len_recv) { /* read length byte */ rlen = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_MRXFIFO); - *buf++ = rlen; - len--; - - if (priv->client_pec) - ++rlen; - /* update remaining bytes and message length */ - priv->msg_buf_remaining = rlen; - priv->msg_len = rlen + 1; + if (rlen > I2C_SMBUS_BLOCK_MAX || rlen == 0) { + rlen = 0; /*abort transfer */ + priv->msg_buf_remaining = 0; + priv->msg_len = 0; + } else { + *buf++ = rlen; + if (priv->client_pec) + ++rlen; /* account for error check byte */ + /* update remaining bytes and message length */ + priv->msg_buf_remaining = rlen; + priv->msg_len = rlen + 1; + } + xlp9xx_i2c_update_rlen(priv); priv->len_recv = false; - - /* Update transfer length to read only actual data */ - val = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_CTRL); - val = (val & ~XLP9XX_I2C_CTRL_MCTLEN_MASK) | - ((rlen + 1) << XLP9XX_I2C_CTRL_MCTLEN_SHIFT); - xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_CTRL, val); } else { len = min(priv->msg_buf_remaining, len); for (i = 0; i < len; i++, buf++) @@ -300,10 +323,6 @@ static int xlp9xx_i2c_xfer_msg(struct xlp9xx_i2c_dev *priv, struct i2c_msg *msg, xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_MFIFOCTRL, XLP9XX_I2C_MFIFOCTRL_RST); - /* set FIFO threshold if reading */ - if (priv->msg_read) - xlp9xx_i2c_update_rx_fifo_thres(priv); - /* set slave addr */ xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_SLAVEADDR, (msg->addr << XLP9XX_I2C_SLAVEADDR_ADDR_SHIFT) | @@ -322,9 +341,13 @@ static int xlp9xx_i2c_xfer_msg(struct xlp9xx_i2c_dev *priv, struct i2c_msg *msg, val &= ~XLP9XX_I2C_CTRL_ADDMODE; priv->len_recv = msg->flags & I2C_M_RECV_LEN; - len = priv->len_recv ? XLP9XX_I2C_FIFO_SIZE : msg->len; + len = priv->len_recv ? I2C_SMBUS_BLOCK_MAX + 2 : msg->len; priv->client_pec = msg->flags & I2C_CLIENT_PEC; + /* set FIFO threshold if reading */ + if (priv->msg_read) + xlp9xx_i2c_update_rx_fifo_thres(priv); + /* set data length to be transferred */ val = (val & ~XLP9XX_I2C_CTRL_MCTLEN_MASK) | (len << XLP9XX_I2C_CTRL_MCTLEN_SHIFT); @@ -378,8 +401,11 @@ static int xlp9xx_i2c_xfer_msg(struct xlp9xx_i2c_dev *priv, struct i2c_msg *msg, } /* update msg->len with actual received length */ - if (msg->flags & I2C_M_RECV_LEN) + if (msg->flags & I2C_M_RECV_LEN) { + if (!priv->msg_len) + return -EPROTO; msg->len = priv->msg_len; + } return 0; } @@ -447,6 +473,19 @@ static int xlp9xx_i2c_get_frequency(struct platform_device *pdev, return 0; } +static int xlp9xx_i2c_smbus_setup(struct xlp9xx_i2c_dev *priv, + struct platform_device *pdev) +{ + if (!priv->alert_data.irq) + return -EINVAL; + + priv->ara = i2c_setup_smbus_alert(&priv->adapter, &priv->alert_data); + if (!priv->ara) + return -ENODEV; + + return 0; +} + static int xlp9xx_i2c_probe(struct platform_device *pdev) { struct xlp9xx_i2c_dev *priv; @@ -467,6 +506,10 @@ static int xlp9xx_i2c_probe(struct platform_device *pdev) dev_err(&pdev->dev, "invalid irq!\n"); return priv->irq; } + /* SMBAlert irq */ + priv->alert_data.irq = platform_get_irq(pdev, 1); + if (priv->alert_data.irq <= 0) + priv->alert_data.irq = 0; xlp9xx_i2c_get_frequency(pdev, priv); xlp9xx_i2c_init(priv); @@ -493,6 +536,10 @@ static int xlp9xx_i2c_probe(struct platform_device *pdev) if (err) return err; + err = xlp9xx_i2c_smbus_setup(priv, pdev); + if (err) + dev_dbg(&pdev->dev, "No active SMBus alert %d\n", err); + platform_set_drvdata(pdev, priv); dev_dbg(&pdev->dev, "I2C bus:%d added\n", priv->adapter.nr); |