Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add SMBus for STM32 #66260

Merged
merged 6 commits into from
Dec 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 70 additions & 1 deletion drivers/i2c/i2c_ll_stm32.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -441,6 +446,70 @@ 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 */

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;
#ifdef CONFIG_SMBUS_STM32
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;
#endif
default:
LOG_ERR("%s: invalid mode %i", dev->name, mode);
return;
}
}

#ifdef CONFIG_SMBUS_STM32
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
Expand Down
8 changes: 8 additions & 0 deletions drivers/i2c/i2c_ll_stm32.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
#ifndef ZEPHYR_DRIVERS_I2C_I2C_LL_STM32_H_
#define ZEPHYR_DRIVERS_I2C_I2C_LL_STM32_H_

#include <zephyr/drivers/i2c/stm32.h>

#ifdef CONFIG_I2C_STM32_BUS_RECOVERY
#include <zephyr/drivers/gpio.h>
#endif /* CONFIG_I2C_STM32_BUS_RECOVERY */
Expand Down Expand Up @@ -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,
Expand Down
28 changes: 23 additions & 5 deletions drivers/i2c/i2c_ll_stm32_v1.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -118,23 +122,25 @@ 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
stm32_i2c_disable_transfer_interrupts(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
}

Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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);
Expand Down
26 changes: 22 additions & 4 deletions drivers/i2c/i2c_ll_stm32_v2.c
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,18 @@ 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);
LL_I2C_DisableIT_RX(i2c);
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)
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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)) {
Expand Down Expand Up @@ -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);
Expand Down
3 changes: 3 additions & 0 deletions drivers/smbus/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ zephyr_syscall_header(${ZEPHYR_BASE}/include/zephyr/drivers/smbus.h)

zephyr_library()

zephyr_library_sources(smbus_utils.c)

zephyr_library_sources_ifdef(CONFIG_SMBUS_SHELL smbus_shell.c)
zephyr_library_sources_ifdef(CONFIG_SMBUS_INTEL_PCH intel_pch_smbus.c)
zephyr_library_sources_ifdef(CONFIG_SMBUS_STM32 smbus_stm32.c)

zephyr_library_sources_ifdef(CONFIG_USERSPACE smbus_handlers.c)
18 changes: 18 additions & 0 deletions drivers/smbus/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
28 changes: 1 addition & 27 deletions drivers/smbus/intel_pch_smbus.c
Original file line number Diff line number Diff line change
Expand Up @@ -143,33 +143,7 @@ static void smbalert_work(struct k_work *work)
smb_alert_work);
const struct device *dev = data->dev;

/**
* There might be several peripheral devices and the he highest
* priority (lowest address) device wins arbitration, we need to
* read them all.
*
* The format of the transaction is:
*
* 0 1 2
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* |S| Alert Addr |R|A| Address |X|N|P|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
do {
uint8_t addr;
int ret;

ret = smbus_byte_read(dev, SMBUS_ADDRESS_ARA, &addr);
if (ret < 0) {
LOG_DBG("Cannot read peripheral address (anymore)");
return;
}

LOG_DBG("Read addr 0x%02x, ret %d", addr, ret);

smbus_fire_callbacks(&data->smbalert_cbs, dev, addr);
} while (true);
smbus_loop_alert_devices(dev, &data->smbalert_cbs);
}

static int pch_smbus_smbalert_set_sb(const struct device *dev,
Expand Down
Loading
Loading