diff --git a/drivers/i2c/i2c_ll_stm32.c b/drivers/i2c/i2c_ll_stm32.c index 552acfb6ab2ec37..0f1eedc8681f702 100644 --- a/drivers/i2c/i2c_ll_stm32.c +++ b/drivers/i2c/i2c_ll_stm32.c @@ -94,9 +94,13 @@ int i2c_stm32_runtime_configure(const struct device *dev, uint32_t config) #endif LL_I2C_Disable(i2c); - LL_I2C_SetMode(i2c, LL_I2C_MODE_I2C); + i2c_stm32_set_smbus_mode(dev, data->mode); ret = stm32_i2c_configure_timing(dev, i2c_clock); + if (data->smbalert_active) { + LL_I2C_Enable(i2c); + } + #ifdef CONFIG_PM_DEVICE_RUNTIME ret = clock_control_off(clk, (clock_control_subsys_t)&cfg->pclken[0]); if (ret < 0) { @@ -365,6 +369,7 @@ static int i2c_stm32_init(const struct device *dev) #endif data->is_configured = false; + data->mode = I2CSTM32MODE_I2C; /* * initialize mutex used when multiple transfers @@ -441,6 +446,65 @@ static int i2c_stm32_pm_action(const struct device *dev, enum pm_device_action a #endif +#ifdef CONFIG_SMBUS_STM32_SMBALERT +void i2c_stm32_smbalert_set_callback(const struct device *dev, i2c_stm32_smbalert_cb_func_t func, + const struct device *cb_dev) +{ + struct i2c_stm32_data *data = dev->data; + + data->smbalert_cb_func = func; + data->smbalert_cb_dev = cb_dev; +} +#endif /* CONFIG_SMBUS_STM32_SMBALERT */ + +#ifdef CONFIG_SMBUS_STM32 +void i2c_stm32_set_smbus_mode(const struct device *dev, enum i2c_stm32_mode mode) +{ + const struct i2c_stm32_config *cfg = dev->config; + struct i2c_stm32_data *data = dev->data; + I2C_TypeDef *i2c = cfg->i2c; + + data->mode = mode; + + switch (mode) { + case I2CSTM32MODE_I2C: + LL_I2C_SetMode(i2c, LL_I2C_MODE_I2C); + return; + case I2CSTM32MODE_SMBUSHOST: + LL_I2C_SetMode(i2c, LL_I2C_MODE_SMBUS_HOST); + return; + case I2CSTM32MODE_SMBUSDEVICE: + LL_I2C_SetMode(i2c, LL_I2C_MODE_SMBUS_DEVICE); + return; + case I2CSTM32MODE_SMBUSDEVICEARP: + LL_I2C_SetMode(i2c, LL_I2C_MODE_SMBUS_DEVICE_ARP); + return; + } +} + +void i2c_stm32_smbalert_enable(const struct device *dev) +{ + struct i2c_stm32_data *data = dev->data; + const struct i2c_stm32_config *cfg = dev->config; + + data->smbalert_active = true; + LL_I2C_EnableSMBusAlert(cfg->i2c); + LL_I2C_EnableIT_ERR(cfg->i2c); + LL_I2C_Enable(cfg->i2c); +} + +void i2c_stm32_smbalert_disable(const struct device *dev) +{ + struct i2c_stm32_data *data = dev->data; + const struct i2c_stm32_config *cfg = dev->config; + + data->smbalert_active = false; + LL_I2C_DisableSMBusAlert(cfg->i2c); + LL_I2C_DisableIT_ERR(cfg->i2c); + LL_I2C_Disable(cfg->i2c); +} +#endif /* CONFIG_SMBUS_STM32 */ + /* Macros for I2C instance declaration */ #ifdef CONFIG_I2C_STM32_INTERRUPT diff --git a/drivers/i2c/i2c_ll_stm32.h b/drivers/i2c/i2c_ll_stm32.h index c77b687038d75f8..0a9dc1be350a6a9 100644 --- a/drivers/i2c/i2c_ll_stm32.h +++ b/drivers/i2c/i2c_ll_stm32.h @@ -9,6 +9,8 @@ #ifndef ZEPHYR_DRIVERS_I2C_I2C_LL_STM32_H_ #define ZEPHYR_DRIVERS_I2C_I2C_LL_STM32_H_ +#include + #ifdef CONFIG_I2C_STM32_BUS_RECOVERY #include #endif /* CONFIG_I2C_STM32_BUS_RECOVERY */ @@ -79,6 +81,12 @@ struct i2c_stm32_data { bool slave_attached; #endif bool is_configured; + bool smbalert_active; + enum i2c_stm32_mode mode; +#ifdef CONFIG_SMBUS_STM32_SMBALERT + i2c_stm32_smbalert_cb_func_t smbalert_cb_func; + const struct device *smbalert_cb_dev; +#endif }; int32_t stm32_i2c_transaction(const struct device *dev, diff --git a/drivers/i2c/i2c_ll_stm32_v1.c b/drivers/i2c/i2c_ll_stm32_v1.c index d0a0131858fec42..4730cc155985d01 100644 --- a/drivers/i2c/i2c_ll_stm32_v1.c +++ b/drivers/i2c/i2c_ll_stm32_v1.c @@ -48,13 +48,17 @@ static void stm32_i2c_generate_start_condition(I2C_TypeDef *i2c) static void stm32_i2c_disable_transfer_interrupts(const struct device *dev) { const struct i2c_stm32_config *cfg = dev->config; + struct i2c_stm32_data *data = dev->data; I2C_TypeDef *i2c = cfg->i2c; LL_I2C_DisableIT_TX(i2c); LL_I2C_DisableIT_RX(i2c); LL_I2C_DisableIT_EVT(i2c); LL_I2C_DisableIT_BUF(i2c); - LL_I2C_DisableIT_ERR(i2c); + + if (!data->smbalert_active) { + LL_I2C_DisableIT_ERR(i2c); + } } static void stm32_i2c_enable_transfer_interrupts(const struct device *dev) @@ -118,6 +122,7 @@ static void stm32_i2c_reset(const struct device *dev) static void stm32_i2c_master_finish(const struct device *dev) { const struct i2c_stm32_config *cfg = dev->config; + struct i2c_stm32_data *data = dev->data; I2C_TypeDef *i2c = cfg->i2c; #ifdef CONFIG_I2C_STM32_INTERRUPT @@ -125,16 +130,17 @@ static void stm32_i2c_master_finish(const struct device *dev) #endif #if defined(CONFIG_I2C_TARGET) - struct i2c_stm32_data *data = dev->data; data->master_active = false; - if (!data->slave_attached) { + if (!data->slave_attached && !data->smbalert_active) { LL_I2C_Disable(i2c); } else { stm32_i2c_enable_transfer_interrupts(dev); LL_I2C_AcknowledgeNextData(i2c, LL_I2C_ACK); } #else - LL_I2C_Disable(i2c); + if (!data->smbalert_active) { + LL_I2C_Disable(i2c); + } #endif } @@ -538,7 +544,9 @@ int i2c_stm32_target_unregister(const struct device *dev, struct i2c_target_conf LL_I2C_ClearFlag_STOP(i2c); LL_I2C_ClearFlag_ADDR(i2c); - LL_I2C_Disable(i2c); + if (!data->smbalert_active) { + LL_I2C_Disable(i2c); + } data->slave_attached = false; @@ -608,6 +616,16 @@ void stm32_i2c_error_isr(void *arg) data->current.is_err = 1U; goto end; } + +#if defined(CONFIG_SMBUS_STM32_SMBALERT) + if (LL_I2C_IsActiveSMBusFlag_ALERT(i2c)) { + LL_I2C_ClearSMBusFlag_ALERT(i2c); + if (data->smbalert_cb_func != NULL) { + data->smbalert_cb_func(data->smbalert_cb_dev); + } + goto end; + } +#endif return; end: stm32_i2c_master_mode_end(dev); diff --git a/drivers/i2c/i2c_ll_stm32_v2.c b/drivers/i2c/i2c_ll_stm32_v2.c index 8292b2f6738f6f7..a685ebebeb78677 100644 --- a/drivers/i2c/i2c_ll_stm32_v2.c +++ b/drivers/i2c/i2c_ll_stm32_v2.c @@ -74,6 +74,7 @@ static inline void msg_init(const struct device *dev, struct i2c_msg *msg, static void stm32_i2c_disable_transfer_interrupts(const struct device *dev) { const struct i2c_stm32_config *cfg = dev->config; + struct i2c_stm32_data *data = dev->data; I2C_TypeDef *i2c = cfg->i2c; LL_I2C_DisableIT_TX(i2c); @@ -81,7 +82,10 @@ static void stm32_i2c_disable_transfer_interrupts(const struct device *dev) LL_I2C_DisableIT_STOP(i2c); LL_I2C_DisableIT_NACK(i2c); LL_I2C_DisableIT_TC(i2c); - LL_I2C_DisableIT_ERR(i2c); + + if (!data->smbalert_active) { + LL_I2C_DisableIT_ERR(i2c); + } } static void stm32_i2c_enable_transfer_interrupts(const struct device *dev) @@ -109,11 +113,13 @@ static void stm32_i2c_master_mode_end(const struct device *dev) #if defined(CONFIG_I2C_TARGET) data->master_active = false; - if (!data->slave_attached) { + if (!data->slave_attached && !data->smbalert_active) { LL_I2C_Disable(i2c); } #else - LL_I2C_Disable(i2c); + if (!data->smbalert_active) { + LL_I2C_Disable(i2c); + } #endif k_sem_give(&data->device_sync_sem); } @@ -330,7 +336,9 @@ int i2c_stm32_target_unregister(const struct device *dev, LL_I2C_ClearFlag_STOP(i2c); LL_I2C_ClearFlag_ADDR(i2c); - LL_I2C_Disable(i2c); + if (!data->smbalert_active) { + LL_I2C_Disable(i2c); + } #if defined(CONFIG_PM_DEVICE_RUNTIME) if (pm_device_wakeup_is_capable(dev)) { @@ -437,6 +445,16 @@ static int stm32_i2c_error(const struct device *dev) goto end; } +#if defined(CONFIG_SMBUS_STM32_SMBALERT) + if (LL_I2C_IsActiveSMBusFlag_ALERT(i2c)) { + LL_I2C_ClearSMBusFlag_ALERT(i2c); + if (data->smbalert_cb_func != NULL) { + data->smbalert_cb_func(data->smbalert_cb_dev); + } + goto end; + } +#endif + return 0; end: stm32_i2c_master_mode_end(dev); diff --git a/drivers/smbus/Kconfig b/drivers/smbus/Kconfig index 88eff8b0030b8e5..74efaae29f17646 100644 --- a/drivers/smbus/Kconfig +++ b/drivers/smbus/Kconfig @@ -77,4 +77,22 @@ config SMBUS_INTEL_PCH_SMBALERT endif # SMBUS_INTEL_PCH +config SMBUS_STM32 + bool "STM32 SMBus driver" + default y + depends on DT_HAS_ST_STM32_SMBUS_ENABLED + depends on I2C_STM32 + help + Enable STM32 SMBus driver. + +if SMBUS_STM32 + +config SMBUS_STM32_SMBALERT + bool "SMBus STM32 SMBALERT signal support" + default y + help + Support SMBALERT signal from peripheral devices. + +endif # SMBUS_STM32 + endif # SMBUS diff --git a/drivers/smbus/smbus_stm32.c b/drivers/smbus/smbus_stm32.c new file mode 100644 index 000000000000000..6cbac1806c1a8d1 --- /dev/null +++ b/drivers/smbus/smbus_stm32.c @@ -0,0 +1,310 @@ +/* + * Copyright (c) 2023 SILA Embedded Solutions GmbH + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "smbus_utils.h" + +LOG_MODULE_REGISTER(stm32_smbus, CONFIG_SMBUS_LOG_LEVEL); + +struct smbus_stm32_config { + const struct pinctrl_dev_config *pcfg; + const struct device *i2c_dev; +}; + +struct smbus_stm32_data { + uint32_t config; + const struct device *dev; +#ifdef CONFIG_SMBUS_STM32_SMBALERT + sys_slist_t smbalert_callbacks; + struct k_work smbalert_work; +#endif /* CONFIG_SMBUS_STM32_SMBALERT */ +}; + +#ifdef CONFIG_SMBUS_STM32_SMBALERT +static void smbus_stm32_smbalert_isr(const struct device *dev) +{ + struct smbus_stm32_data *data = dev->data; + + k_work_submit(&data->smbalert_work); +} + +static void smbus_stm32_smbalert_work(struct k_work *work) +{ + struct smbus_stm32_data *data = CONTAINER_OF(work, struct smbus_stm32_data, smbalert_work); + const struct device *dev = data->dev; + + LOG_DBG("%s: got SMB alert", dev->name); + + smbus_loop_alert_devices(dev, &data->smbalert_callbacks); +} + +static int smbus_stm32_smbalert_set_cb(const struct device *dev, struct smbus_callback *cb) +{ + struct smbus_stm32_data *data = dev->data; + + return smbus_callback_set(&data->smbalert_callbacks, cb); +} + +static int smbus_stm32_smbalert_remove_cb(const struct device *dev, struct smbus_callback *cb) +{ + struct smbus_stm32_data *data = dev->data; + + return smbus_callback_remove(&data->smbalert_callbacks, cb); +} +#else +static int smbus_stm32_smbalert_set_cb(const struct device *dev, struct smbus_callback *cb) +{ + return -ENOTSUP; +} + +static int smbus_stm32_smbalert_remove_cb(const struct device *dev, struct smbus_callback *cb) +{ + return -ENOTSUP; +} +#endif /* CONFIG_SMBUS_STM32_SMBALERT */ + +static int smbus_stm32_init(const struct device *dev) +{ + const struct smbus_stm32_config *config = dev->config; + struct smbus_stm32_data *data = dev->data; + int result; + + data->dev = dev; + + if (!device_is_ready(config->i2c_dev)) { + LOG_ERR("%s: I2C device is not ready", dev->name); + return -ENODEV; + } + + result = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT); + if (result < 0) { + LOG_ERR("%s: pinctrl setup failed (%d)", dev->name, result); + return result; + } + +#ifdef CONFIG_SMBUS_STM32_SMBALERT + k_work_init(&data->smbalert_work, smbus_stm32_smbalert_work); + + i2c_stm32_smbalert_set_callback(config->i2c_dev, smbus_stm32_smbalert_isr, dev); +#endif /* CONFIG_SMBUS_STM32_SMBALERT */ + + return 0; +} + +static int smbus_stm32_configure(const struct device *dev, uint32_t config_value) +{ + const struct smbus_stm32_config *config = dev->config; + struct smbus_stm32_data *data = dev->data; + + if (config_value & SMBUS_MODE_PEC) { + LOG_ERR("%s: not implemented", dev->name); + return -EINVAL; + } + + if (config_value & SMBUS_MODE_HOST_NOTIFY) { + LOG_ERR("%s: not available", dev->name); + return -EINVAL; + } + + if (config_value & SMBUS_MODE_CONTROLLER) { + LOG_DBG("%s: configuring SMB in host mode", dev->name); + i2c_stm32_set_smbus_mode(config->i2c_dev, I2CSTM32MODE_SMBUSHOST); + } else { + LOG_DBG("%s: configuring SMB in device mode", dev->name); + i2c_stm32_set_smbus_mode(config->i2c_dev, I2CSTM32MODE_SMBUSDEVICE); + } + + if (config_value & SMBUS_MODE_SMBALERT) { + LOG_DBG("%s: activating SMB alert", dev->name); + i2c_stm32_smbalert_enable(config->i2c_dev); + } else { + LOG_DBG("%s: deactivating SMB alert", dev->name); + i2c_stm32_smbalert_disable(config->i2c_dev); + } + + data->config = config_value; + return 0; +} + +static int smbus_stm32_get_config(const struct device *dev, uint32_t *config) +{ + struct smbus_stm32_data *data = dev->data; + *config = data->config; + return 0; +} + +static int smbus_stm32_quick(const struct device *dev, uint16_t periph_addr, + enum smbus_direction rw) +{ + const struct smbus_stm32_config *config = dev->config; + + switch (rw) { + case SMBUS_MSG_WRITE: + return i2c_write(config->i2c_dev, NULL, 0, periph_addr); + case SMBUS_MSG_READ: + return i2c_read(config->i2c_dev, NULL, 0, periph_addr); + default: + LOG_ERR("%s: invalid smbus direction %i", dev->name, rw); + return -EINVAL; + } +} + +static int smbus_stm32_byte_write(const struct device *dev, uint16_t periph_addr, uint8_t command) +{ + const struct smbus_stm32_config *config = dev->config; + + return i2c_write(config->i2c_dev, &command, sizeof(command), periph_addr); +} + +static int smbus_stm32_byte_read(const struct device *dev, uint16_t periph_addr, uint8_t *byte) +{ + const struct smbus_stm32_config *config = dev->config; + + return i2c_read(config->i2c_dev, byte, sizeof(*byte), periph_addr); +} + +static int smbus_stm32_byte_data_write(const struct device *dev, uint16_t periph_addr, + uint8_t command, uint8_t byte) +{ + const struct smbus_stm32_config *config = dev->config; + uint8_t buffer[] = { + command, + byte, + }; + + return i2c_write(config->i2c_dev, buffer, ARRAY_SIZE(buffer), periph_addr); +} + +static int smbus_stm32_byte_data_read(const struct device *dev, uint16_t periph_addr, + uint8_t command, uint8_t *byte) +{ + const struct smbus_stm32_config *config = dev->config; + + return i2c_write_read(config->i2c_dev, periph_addr, &command, sizeof(command), byte, + sizeof(*byte)); +} + +static int smbus_stm32_word_data_write(const struct device *dev, uint16_t periph_addr, + uint8_t command, uint16_t word) +{ + const struct smbus_stm32_config *config = dev->config; + uint8_t buffer[3]; + + buffer[0] = command; + sys_put_le16(word, buffer + 1); + + return i2c_write(config->i2c_dev, buffer, ARRAY_SIZE(buffer), periph_addr); +} + +static int smbus_stm32_word_data_read(const struct device *dev, uint16_t periph_addr, + uint8_t command, uint16_t *word) +{ + const struct smbus_stm32_config *config = dev->config; + + return i2c_write_read(config->i2c_dev, periph_addr, &command, sizeof(command), word, + sizeof(*word)); +} + +static int smbus_stm32_pcall(const struct device *dev, uint16_t periph_addr, uint8_t command, + uint16_t send_word, uint16_t *recv_word) +{ + const struct smbus_stm32_config *config = dev->config; + uint8_t buffer[3]; + + buffer[0] = command; + sys_put_le16(send_word, buffer + 1); + + return i2c_write_read(config->i2c_dev, periph_addr, buffer, ARRAY_SIZE(buffer), recv_word, + sizeof(*recv_word)); +} + +static int smbus_stm32_block_write(const struct device *dev, uint16_t periph_addr, uint8_t command, + uint8_t count, uint8_t *buf) +{ + const struct smbus_stm32_config *config = dev->config; + struct i2c_msg messages[] = { + { + .buf = &command, + .len = sizeof(command), + .flags = 0, + }, + { + .buf = buf, + .len = count, + .flags = 0, + }, + }; + + return i2c_transfer(config->i2c_dev, messages, ARRAY_SIZE(messages), periph_addr); +} + +static int smbus_stm32_block_read(const struct device *dev, uint16_t periph_addr, uint8_t command, + uint8_t *count, uint8_t *buf) +{ + return -ENOTSUP; +} + +static int smbus_stm32_block_pcall(const struct device *dev, uint16_t periph_addr, uint8_t command, + uint8_t send_count, uint8_t *send_buf, uint8_t *recv_count, + uint8_t *recv_buf) +{ + return -ENOTSUP; +} + +static int smbus_stm32_host_notify_set_cb(const struct device *dev, struct smbus_callback *cb) +{ + return -ENOTSUP; +} + +static int smbus_stm32_host_notify_remove_cb(const struct device *dev, struct smbus_callback *cb) +{ + return -ENOTSUP; +} + +static const struct smbus_driver_api smbus_stm32_api = { + .configure = smbus_stm32_configure, + .get_config = smbus_stm32_get_config, + .smbus_quick = smbus_stm32_quick, + .smbus_byte_write = smbus_stm32_byte_write, + .smbus_byte_read = smbus_stm32_byte_read, + .smbus_byte_data_write = smbus_stm32_byte_data_write, + .smbus_byte_data_read = smbus_stm32_byte_data_read, + .smbus_word_data_write = smbus_stm32_word_data_write, + .smbus_word_data_read = smbus_stm32_word_data_read, + .smbus_pcall = smbus_stm32_pcall, + .smbus_block_write = smbus_stm32_block_write, + .smbus_block_read = smbus_stm32_block_read, + .smbus_block_pcall = smbus_stm32_block_pcall, + .smbus_smbalert_set_cb = smbus_stm32_smbalert_set_cb, + .smbus_smbalert_remove_cb = smbus_stm32_smbalert_remove_cb, + .smbus_host_notify_set_cb = smbus_stm32_host_notify_set_cb, + .smbus_host_notify_remove_cb = smbus_stm32_host_notify_remove_cb, +}; + +#define DT_DRV_COMPAT st_stm32_smbus + +#define SMBUS_STM32_DEVICE_INIT(n) \ + PINCTRL_DT_INST_DEFINE(n); \ + static struct smbus_stm32_config smbus_stm32_config_##n = { \ + .i2c_dev = DEVICE_DT_GET(DT_INST_PROP(n, i2c)), \ + .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ + }; \ + \ + static struct smbus_stm32_data smbus_stm32_data_##n; \ + \ + SMBUS_DEVICE_DT_INST_DEFINE(n, smbus_stm32_init, NULL, &smbus_stm32_data_##n, \ + &smbus_stm32_config_##n, POST_KERNEL, \ + CONFIG_SMBUS_INIT_PRIORITY, &smbus_stm32_api); + +DT_INST_FOREACH_STATUS_OKAY(SMBUS_STM32_DEVICE_INIT) diff --git a/include/zephyr/drivers/i2c/stm32.h b/include/zephyr/drivers/i2c/stm32.h new file mode 100644 index 000000000000000..92fb3033d5a5867 --- /dev/null +++ b/include/zephyr/drivers/i2c/stm32.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2023 SILA Embedded Solutions GmbH + * + * SPDX-License-Identifier: Apache-2.0 + * + */ + +#ifndef ZEPHYR_INCLUDE_DRIVERS_I2C_STM32_H_ +#define ZEPHYR_INCLUDE_DRIVERS_I2C_STM32_H_ + +#include + +enum i2c_stm32_mode { + I2CSTM32MODE_I2C, + I2CSTM32MODE_SMBUSHOST, + I2CSTM32MODE_SMBUSDEVICE, + I2CSTM32MODE_SMBUSDEVICEARP, +}; + +#ifdef CONFIG_SMBUS_STM32 +void i2c_stm32_set_smbus_mode(const struct device *dev, enum i2c_stm32_mode mode); +#endif /* CONFIG_SMBUS_STM32 */ + +#ifdef CONFIG_SMBUS_STM32_SMBALERT +typedef void (*i2c_stm32_smbalert_cb_func_t)(const struct device *dev); + +void i2c_stm32_smbalert_set_callback(const struct device *dev, i2c_stm32_smbalert_cb_func_t func, + const struct device *cb_dev); +void i2c_stm32_smbalert_enable(const struct device *dev); +void i2c_stm32_smbalert_disable(const struct device *dev); +#endif /* CONFIG_SMBUS_STM32_SMBALERT */ + +#endif /* ZEPHYR_INCLUDE_DRIVERS_I2C_STM32_H_ */