Commit 6af07719 authored by Alain Volmat's avatar Alain Volmat Committed by Wolfram Sang

i2c: stm32f7: Add SMBus Host-Notify protocol support

Rely on the core functions to implement the host-notify
protocol via the a I2C slave device.
Signed-off-by: default avatarAlain Volmat <alain.volmat@st.com>
Reviewed-by: default avatarPierre-Yves MORDRET <pierre-yves.mordret@st.com>
Signed-off-by: default avatarWolfram Sang <wsa@kernel.org>
parent 2a71593d
...@@ -1025,6 +1025,7 @@ config I2C_STM32F7 ...@@ -1025,6 +1025,7 @@ config I2C_STM32F7
tristate "STMicroelectronics STM32F7 I2C support" tristate "STMicroelectronics STM32F7 I2C support"
depends on ARCH_STM32 || COMPILE_TEST depends on ARCH_STM32 || COMPILE_TEST
select I2C_SLAVE select I2C_SLAVE
select I2C_SMBUS
help help
Enable this option to add support for STM32 I2C controller embedded Enable this option to add support for STM32 I2C controller embedded
in STM32F7 SoCs. in STM32F7 SoCs.
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/i2c-smbus.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/iopoll.h> #include <linux/iopoll.h>
...@@ -50,6 +51,7 @@ ...@@ -50,6 +51,7 @@
/* STM32F7 I2C control 1 */ /* STM32F7 I2C control 1 */
#define STM32F7_I2C_CR1_PECEN BIT(23) #define STM32F7_I2C_CR1_PECEN BIT(23)
#define STM32F7_I2C_CR1_SMBHEN BIT(20)
#define STM32F7_I2C_CR1_WUPEN BIT(18) #define STM32F7_I2C_CR1_WUPEN BIT(18)
#define STM32F7_I2C_CR1_SBC BIT(16) #define STM32F7_I2C_CR1_SBC BIT(16)
#define STM32F7_I2C_CR1_RXDMAEN BIT(15) #define STM32F7_I2C_CR1_RXDMAEN BIT(15)
...@@ -150,7 +152,7 @@ ...@@ -150,7 +152,7 @@
#define STM32F7_I2C_MAX_LEN 0xff #define STM32F7_I2C_MAX_LEN 0xff
#define STM32F7_I2C_DMA_LEN_MIN 0x16 #define STM32F7_I2C_DMA_LEN_MIN 0x16
#define STM32F7_I2C_MAX_SLAVE 0x2 #define STM32F7_I2C_MAX_SLAVE 0x3
#define STM32F7_I2C_DNF_DEFAULT 0 #define STM32F7_I2C_DNF_DEFAULT 0
#define STM32F7_I2C_DNF_MAX 16 #define STM32F7_I2C_DNF_MAX 16
...@@ -301,6 +303,8 @@ struct stm32f7_i2c_msg { ...@@ -301,6 +303,8 @@ struct stm32f7_i2c_msg {
* @fmp_creg: register address for clearing Fast Mode Plus bits * @fmp_creg: register address for clearing Fast Mode Plus bits
* @fmp_mask: mask for Fast Mode Plus bits in set register * @fmp_mask: mask for Fast Mode Plus bits in set register
* @wakeup_src: boolean to know if the device is a wakeup source * @wakeup_src: boolean to know if the device is a wakeup source
* @smbus_mode: states that the controller is configured in SMBus mode
* @host_notify_client: SMBus host-notify client
*/ */
struct stm32f7_i2c_dev { struct stm32f7_i2c_dev {
struct i2c_adapter adap; struct i2c_adapter adap;
...@@ -327,6 +331,8 @@ struct stm32f7_i2c_dev { ...@@ -327,6 +331,8 @@ struct stm32f7_i2c_dev {
u32 fmp_creg; u32 fmp_creg;
u32 fmp_mask; u32 fmp_mask;
bool wakeup_src; bool wakeup_src;
bool smbus_mode;
struct i2c_client *host_notify_client;
}; };
/* /*
...@@ -1321,11 +1327,19 @@ static int stm32f7_i2c_get_free_slave_id(struct stm32f7_i2c_dev *i2c_dev, ...@@ -1321,11 +1327,19 @@ static int stm32f7_i2c_get_free_slave_id(struct stm32f7_i2c_dev *i2c_dev,
int i; int i;
/* /*
* slave[0] supports 7-bit and 10-bit slave address * slave[0] support only SMBus Host address (0x8)
* slave[1] supports 7-bit slave address only * slave[1] supports 7-bit and 10-bit slave address
* slave[2] supports 7-bit slave address only
*/ */
for (i = STM32F7_I2C_MAX_SLAVE - 1; i >= 0; i--) { if (i2c_dev->smbus_mode && (slave->addr == 0x08)) {
if (i == 1 && (slave->flags & I2C_CLIENT_TEN)) if (i2c_dev->slave[0])
goto fail;
*id = 0;
return 0;
}
for (i = STM32F7_I2C_MAX_SLAVE - 1; i > 0; i--) {
if (i == 2 && (slave->flags & I2C_CLIENT_TEN))
continue; continue;
if (!i2c_dev->slave[i]) { if (!i2c_dev->slave[i]) {
*id = i; *id = i;
...@@ -1333,6 +1347,7 @@ static int stm32f7_i2c_get_free_slave_id(struct stm32f7_i2c_dev *i2c_dev, ...@@ -1333,6 +1347,7 @@ static int stm32f7_i2c_get_free_slave_id(struct stm32f7_i2c_dev *i2c_dev,
} }
} }
fail:
dev_err(dev, "Slave 0x%x could not be registered\n", slave->addr); dev_err(dev, "Slave 0x%x could not be registered\n", slave->addr);
return -EINVAL; return -EINVAL;
...@@ -1776,7 +1791,13 @@ static int stm32f7_i2c_reg_slave(struct i2c_client *slave) ...@@ -1776,7 +1791,13 @@ static int stm32f7_i2c_reg_slave(struct i2c_client *slave)
if (!stm32f7_i2c_is_slave_registered(i2c_dev)) if (!stm32f7_i2c_is_slave_registered(i2c_dev))
stm32f7_i2c_enable_wakeup(i2c_dev, true); stm32f7_i2c_enable_wakeup(i2c_dev, true);
if (id == 0) { switch (id) {
case 0:
/* Slave SMBus Host */
i2c_dev->slave[id] = slave;
break;
case 1:
/* Configure Own Address 1 */ /* Configure Own Address 1 */
oar1 = readl_relaxed(i2c_dev->base + STM32F7_I2C_OAR1); oar1 = readl_relaxed(i2c_dev->base + STM32F7_I2C_OAR1);
oar1 &= ~STM32F7_I2C_OAR1_MASK; oar1 &= ~STM32F7_I2C_OAR1_MASK;
...@@ -1789,7 +1810,9 @@ static int stm32f7_i2c_reg_slave(struct i2c_client *slave) ...@@ -1789,7 +1810,9 @@ static int stm32f7_i2c_reg_slave(struct i2c_client *slave)
oar1 |= STM32F7_I2C_OAR1_OA1EN; oar1 |= STM32F7_I2C_OAR1_OA1EN;
i2c_dev->slave[id] = slave; i2c_dev->slave[id] = slave;
writel_relaxed(oar1, i2c_dev->base + STM32F7_I2C_OAR1); writel_relaxed(oar1, i2c_dev->base + STM32F7_I2C_OAR1);
} else if (id == 1) { break;
case 2:
/* Configure Own Address 2 */ /* Configure Own Address 2 */
oar2 = readl_relaxed(i2c_dev->base + STM32F7_I2C_OAR2); oar2 = readl_relaxed(i2c_dev->base + STM32F7_I2C_OAR2);
oar2 &= ~STM32F7_I2C_OAR2_MASK; oar2 &= ~STM32F7_I2C_OAR2_MASK;
...@@ -1802,7 +1825,10 @@ static int stm32f7_i2c_reg_slave(struct i2c_client *slave) ...@@ -1802,7 +1825,10 @@ static int stm32f7_i2c_reg_slave(struct i2c_client *slave)
oar2 |= STM32F7_I2C_OAR2_OA2EN; oar2 |= STM32F7_I2C_OAR2_OA2EN;
i2c_dev->slave[id] = slave; i2c_dev->slave[id] = slave;
writel_relaxed(oar2, i2c_dev->base + STM32F7_I2C_OAR2); writel_relaxed(oar2, i2c_dev->base + STM32F7_I2C_OAR2);
} else { break;
default:
dev_err(dev, "I2C slave id not supported\n");
ret = -ENODEV; ret = -ENODEV;
goto pm_free; goto pm_free;
} }
...@@ -1843,10 +1869,10 @@ static int stm32f7_i2c_unreg_slave(struct i2c_client *slave) ...@@ -1843,10 +1869,10 @@ static int stm32f7_i2c_unreg_slave(struct i2c_client *slave)
if (ret < 0) if (ret < 0)
return ret; return ret;
if (id == 0) { if (id == 1) {
mask = STM32F7_I2C_OAR1_OA1EN; mask = STM32F7_I2C_OAR1_OA1EN;
stm32f7_i2c_clr_bits(base + STM32F7_I2C_OAR1, mask); stm32f7_i2c_clr_bits(base + STM32F7_I2C_OAR1, mask);
} else { } else if (id == 2) {
mask = STM32F7_I2C_OAR2_OA2EN; mask = STM32F7_I2C_OAR2_OA2EN;
stm32f7_i2c_clr_bits(base + STM32F7_I2C_OAR2, mask); stm32f7_i2c_clr_bits(base + STM32F7_I2C_OAR2, mask);
} }
...@@ -1911,14 +1937,51 @@ static int stm32f7_i2c_setup_fm_plus_bits(struct platform_device *pdev, ...@@ -1911,14 +1937,51 @@ static int stm32f7_i2c_setup_fm_plus_bits(struct platform_device *pdev,
&i2c_dev->fmp_mask); &i2c_dev->fmp_mask);
} }
static int stm32f7_i2c_enable_smbus_host(struct stm32f7_i2c_dev *i2c_dev)
{
struct i2c_adapter *adap = &i2c_dev->adap;
void __iomem *base = i2c_dev->base;
struct i2c_client *client;
client = i2c_new_slave_host_notify_device(adap);
if (IS_ERR(client))
return PTR_ERR(client);
i2c_dev->host_notify_client = client;
/* Enable SMBus Host address */
stm32f7_i2c_set_bits(base + STM32F7_I2C_CR1, STM32F7_I2C_CR1_SMBHEN);
return 0;
}
static void stm32f7_i2c_disable_smbus_host(struct stm32f7_i2c_dev *i2c_dev)
{
void __iomem *base = i2c_dev->base;
if (i2c_dev->host_notify_client) {
/* Disable SMBus Host address */
stm32f7_i2c_clr_bits(base + STM32F7_I2C_CR1,
STM32F7_I2C_CR1_SMBHEN);
i2c_free_slave_host_notify_device(i2c_dev->host_notify_client);
}
}
static u32 stm32f7_i2c_func(struct i2c_adapter *adap) static u32 stm32f7_i2c_func(struct i2c_adapter *adap)
{ {
return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR | I2C_FUNC_SLAVE | struct stm32f7_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | u32 func = I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR | I2C_FUNC_SLAVE |
I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_BLOCK_PROC_CALL | I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
I2C_FUNC_SMBUS_PROC_CALL | I2C_FUNC_SMBUS_PEC | I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
I2C_FUNC_SMBUS_I2C_BLOCK; I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_BLOCK_PROC_CALL |
I2C_FUNC_SMBUS_PROC_CALL | I2C_FUNC_SMBUS_PEC |
I2C_FUNC_SMBUS_I2C_BLOCK;
if (i2c_dev->smbus_mode)
func |= I2C_FUNC_SMBUS_HOST_NOTIFY;
return func;
} }
static const struct i2c_algorithm stm32f7_i2c_algo = { static const struct i2c_algorithm stm32f7_i2c_algo = {
...@@ -2084,10 +2147,22 @@ static int stm32f7_i2c_probe(struct platform_device *pdev) ...@@ -2084,10 +2147,22 @@ static int stm32f7_i2c_probe(struct platform_device *pdev)
stm32f7_i2c_hw_config(i2c_dev); stm32f7_i2c_hw_config(i2c_dev);
i2c_dev->smbus_mode = of_property_read_bool(pdev->dev.of_node, "smbus");
ret = i2c_add_adapter(adap); ret = i2c_add_adapter(adap);
if (ret) if (ret)
goto pm_disable; goto pm_disable;
if (i2c_dev->smbus_mode) {
ret = stm32f7_i2c_enable_smbus_host(i2c_dev);
if (ret) {
dev_err(i2c_dev->dev,
"failed to enable SMBus Host-Notify protocol (%d)\n",
ret);
goto i2c_adapter_remove;
}
}
dev_info(i2c_dev->dev, "STM32F7 I2C-%d bus adapter\n", adap->nr); dev_info(i2c_dev->dev, "STM32F7 I2C-%d bus adapter\n", adap->nr);
pm_runtime_mark_last_busy(i2c_dev->dev); pm_runtime_mark_last_busy(i2c_dev->dev);
...@@ -2095,6 +2170,9 @@ static int stm32f7_i2c_probe(struct platform_device *pdev) ...@@ -2095,6 +2170,9 @@ static int stm32f7_i2c_probe(struct platform_device *pdev)
return 0; return 0;
i2c_adapter_remove:
i2c_del_adapter(adap);
pm_disable: pm_disable:
pm_runtime_put_noidle(i2c_dev->dev); pm_runtime_put_noidle(i2c_dev->dev);
pm_runtime_disable(i2c_dev->dev); pm_runtime_disable(i2c_dev->dev);
...@@ -2126,6 +2204,8 @@ static int stm32f7_i2c_remove(struct platform_device *pdev) ...@@ -2126,6 +2204,8 @@ static int stm32f7_i2c_remove(struct platform_device *pdev)
{ {
struct stm32f7_i2c_dev *i2c_dev = platform_get_drvdata(pdev); struct stm32f7_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
stm32f7_i2c_disable_smbus_host(i2c_dev);
i2c_del_adapter(&i2c_dev->adap); i2c_del_adapter(&i2c_dev->adap);
pm_runtime_get_sync(i2c_dev->dev); pm_runtime_get_sync(i2c_dev->dev);
......
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