Commit c4651f11 authored by Wolfram Sang's avatar Wolfram Sang Committed by Wolfram Sang

i2c: rcar: add HostNotify support

The I2C core can now utilize a slave interface to handle SMBus
HostNotify events. Enable it in this driver.
Signed-off-by: default avatarWolfram Sang <wsa+renesas@sang-engineering.com>
Signed-off-by: default avatarWolfram Sang <wsa@kernel.org>
parent a8335c64
...@@ -1181,6 +1181,7 @@ config I2C_RCAR ...@@ -1181,6 +1181,7 @@ config I2C_RCAR
tristate "Renesas R-Car I2C Controller" tristate "Renesas R-Car I2C Controller"
depends on ARCH_RENESAS || COMPILE_TEST depends on ARCH_RENESAS || COMPILE_TEST
select I2C_SLAVE select I2C_SLAVE
select I2C_SMBUS
select RESET_CONTROLLER if ARCH_RCAR_GEN3 select RESET_CONTROLLER if ARCH_RCAR_GEN3
help help
If you say yes to this option, support will be included for the If you say yes to this option, support will be included for the
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include <linux/io.h> #include <linux/io.h>
#include <linux/iopoll.h> #include <linux/iopoll.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/i2c-smbus.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of_device.h> #include <linux/of_device.h>
...@@ -106,10 +107,11 @@ ...@@ -106,10 +107,11 @@
#define ID_ARBLOST (1 << 3) #define ID_ARBLOST (1 << 3)
#define ID_NACK (1 << 4) #define ID_NACK (1 << 4)
/* persistent flags */ /* persistent flags */
#define ID_P_HOST_NOTIFY BIT(28)
#define ID_P_REP_AFTER_RD BIT(29) #define ID_P_REP_AFTER_RD BIT(29)
#define ID_P_NO_RXDMA BIT(30) /* HW forbids RXDMA sometimes */ #define ID_P_NO_RXDMA BIT(30) /* HW forbids RXDMA sometimes */
#define ID_P_PM_BLOCKED BIT(31) #define ID_P_PM_BLOCKED BIT(31)
#define ID_P_MASK GENMASK(31, 29) #define ID_P_MASK GENMASK(31, 28)
enum rcar_i2c_type { enum rcar_i2c_type {
I2C_RCAR_GEN1, I2C_RCAR_GEN1,
...@@ -141,6 +143,8 @@ struct rcar_i2c_priv { ...@@ -141,6 +143,8 @@ struct rcar_i2c_priv {
struct reset_control *rstc; struct reset_control *rstc;
int irq; int irq;
struct i2c_client *host_notify_client;
}; };
#define rcar_i2c_priv_to_dev(p) ((p)->adap.dev.parent) #define rcar_i2c_priv_to_dev(p) ((p)->adap.dev.parent)
...@@ -875,14 +879,21 @@ static int rcar_unreg_slave(struct i2c_client *slave) ...@@ -875,14 +879,21 @@ static int rcar_unreg_slave(struct i2c_client *slave)
static u32 rcar_i2c_func(struct i2c_adapter *adap) static u32 rcar_i2c_func(struct i2c_adapter *adap)
{ {
struct rcar_i2c_priv *priv = i2c_get_adapdata(adap);
/* /*
* This HW can't do: * This HW can't do:
* I2C_SMBUS_QUICK (setting FSB during START didn't work) * I2C_SMBUS_QUICK (setting FSB during START didn't work)
* I2C_M_NOSTART (automatically sends address after START) * I2C_M_NOSTART (automatically sends address after START)
* I2C_M_IGNORE_NAK (automatically sends STOP after NAK) * I2C_M_IGNORE_NAK (automatically sends STOP after NAK)
*/ */
return I2C_FUNC_I2C | I2C_FUNC_SLAVE | u32 func = I2C_FUNC_I2C | I2C_FUNC_SLAVE |
(I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK); (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
if (priv->flags & ID_P_HOST_NOTIFY)
func |= I2C_FUNC_SMBUS_HOST_NOTIFY;
return func;
} }
static const struct i2c_algorithm rcar_i2c_algo = { static const struct i2c_algorithm rcar_i2c_algo = {
...@@ -982,6 +993,8 @@ static int rcar_i2c_probe(struct platform_device *pdev) ...@@ -982,6 +993,8 @@ static int rcar_i2c_probe(struct platform_device *pdev)
else else
pm_runtime_put(dev); pm_runtime_put(dev);
if (of_property_read_bool(dev->of_node, "smbus"))
priv->flags |= ID_P_HOST_NOTIFY;
priv->irq = platform_get_irq(pdev, 0); priv->irq = platform_get_irq(pdev, 0);
ret = devm_request_irq(dev, priv->irq, rcar_i2c_irq, 0, dev_name(dev), priv); ret = devm_request_irq(dev, priv->irq, rcar_i2c_irq, 0, dev_name(dev), priv);
...@@ -996,10 +1009,20 @@ static int rcar_i2c_probe(struct platform_device *pdev) ...@@ -996,10 +1009,20 @@ static int rcar_i2c_probe(struct platform_device *pdev)
if (ret < 0) if (ret < 0)
goto out_pm_disable; goto out_pm_disable;
if (priv->flags & ID_P_HOST_NOTIFY) {
priv->host_notify_client = i2c_new_slave_host_notify_device(adap);
if (IS_ERR(priv->host_notify_client)) {
ret = PTR_ERR(priv->host_notify_client);
goto out_del_device;
}
}
dev_info(dev, "probed\n"); dev_info(dev, "probed\n");
return 0; return 0;
out_del_device:
i2c_del_adapter(&priv->adap);
out_pm_put: out_pm_put:
pm_runtime_put(dev); pm_runtime_put(dev);
out_pm_disable: out_pm_disable:
...@@ -1012,6 +1035,8 @@ static int rcar_i2c_remove(struct platform_device *pdev) ...@@ -1012,6 +1035,8 @@ static int rcar_i2c_remove(struct platform_device *pdev)
struct rcar_i2c_priv *priv = platform_get_drvdata(pdev); struct rcar_i2c_priv *priv = platform_get_drvdata(pdev);
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
if (priv->host_notify_client)
i2c_free_slave_host_notify_device(priv->host_notify_client);
i2c_del_adapter(&priv->adap); i2c_del_adapter(&priv->adap);
rcar_i2c_release_dma(priv); rcar_i2c_release_dma(priv);
if (priv->flags & ID_P_PM_BLOCKED) if (priv->flags & ID_P_PM_BLOCKED)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment