Skip to content
Open
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
111 changes: 101 additions & 10 deletions drivers/smbus/smbus_stm32.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
#include <zephyr/drivers/pinctrl.h>
#include <zephyr/drivers/smbus.h>
#include <zephyr/logging/log.h>
#include <zephyr/sys/byteorder.h>
#include <soc.h>

#include "smbus_utils.h"
Expand Down Expand Up @@ -346,18 +345,46 @@ static int smbus_stm32_word_data_read(const struct device *dev, uint16_t periph_
static int smbus_stm32_pcall(const struct device *dev, uint16_t periph_addr, uint8_t command,
uint16_t send_word, uint16_t *recv_word)
{
int ret;
uint8_t pec;
uint8_t num_msgs;
struct i2c_msg messages[] = {
{
.buf = &command,
.len = sizeof(command),
.flags = I2C_MSG_WRITE,
},
{
.buf = &send_word,
.len = sizeof(send_word),
.flags = I2C_MSG_WRITE,
},
{
.buf = recv_word,
.len = sizeof(*recv_word),
.flags = I2C_MSG_READ | I2C_MSG_RESTART,
},
{
.buf = &pec,
.len = 1,
.flags = I2C_MSG_READ,
},
};
struct smbus_stm32_data *data = dev->data;
const struct smbus_stm32_config *config = dev->config;
uint8_t buffer[sizeof(command) + sizeof(send_word)];
int result;

buffer[0] = command;
sys_put_le16(send_word, buffer + 1);
num_msgs = smbus_pec_num_msgs(data->config, ARRAY_SIZE(messages));
ret = i2c_transfer(config->i2c_dev, messages, num_msgs, periph_addr);
if (ret < 0) {
return ret;
}

result = i2c_write_read(config->i2c_dev, periph_addr, buffer, ARRAY_SIZE(buffer), recv_word,
sizeof(*recv_word));
*recv_word = sys_le16_to_cpu(*recv_word);
ret = smbus_read_check_pec(data->config, periph_addr, messages, num_msgs);
if (ret < 0) {
return ret;
}

return result;
return 0;
}

static int smbus_stm32_block_write(const struct device *dev, uint16_t periph_addr, uint8_t command,
Expand Down Expand Up @@ -447,6 +474,70 @@ static int smbus_stm32_block_read(const struct device *dev, uint16_t periph_addr
return 0;
}

int smbus_stm32_block_pcall(const struct device *dev, uint16_t addr, uint8_t cmd,
uint8_t send_count, uint8_t *send_buf, uint8_t *recv_count,
uint8_t *recv_buf)
{
int ret;
uint8_t num_msgs;
uint8_t received_pec;
struct i2c_msg msgs[] = {
{
.buf = &cmd,
.len = sizeof(cmd),
.flags = I2C_MSG_WRITE,
},
{
.buf = &send_count,
.len = sizeof(send_count),
.flags = I2C_MSG_WRITE,
},
{
.buf = send_buf,
.len = send_count,
.flags = I2C_MSG_WRITE,
},
{
.buf = NULL, /* will point to next message's len field */
.len = 1,
.flags = I2C_MSG_READ | I2C_MSG_RESTART,
},
{
.buf = recv_buf,
.len = 0, /* written by previous message! */
.flags = I2C_MSG_READ,
},
{
.buf = &received_pec,
.len = 1,
.flags = I2C_MSG_READ,
},
};
struct smbus_stm32_data *data = dev->data;
const struct smbus_stm32_config *config = dev->config;

/* Count is read in msg 3 and stored in the len of msg 4.
* This works because the STM I2C driver processes each message serially.
* The addressing math assumes little-endian.
*/
msgs[3].buf = (uint8_t *)&msgs[4].len;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

neat trick

Copy link
Member Author

@cfriedt cfriedt Oct 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that came from @jgrowdenTT 😀

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, but I can't claim credit :) it's a repeat of the idea in smbus_stm32_block_read

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably @alewycky-tenstorrent in that case 😀

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems a little unsafe to blindly trust a size from the wire 🙂

But since the driver already does it...

Copy link
Contributor

@etienne-lms etienne-lms Oct 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed. If caller is expected to set the input value to the size available in the output buffer, it would be worth checking that the received data won't overflow memory. As you said @mathieuchopstm, it's seems something to address driver wide (possibly outside of this P-R).


num_msgs = smbus_pec_num_msgs(data->config, ARRAY_SIZE(msgs));
ret = i2c_transfer(config->i2c_dev, msgs, num_msgs, addr);
if (ret < 0) {
return ret;
}

ret = smbus_read_check_pec(data->config, addr, msgs, num_msgs);
if (ret < 0) {
return ret;
}

*recv_count = msgs[4].len;

return 0;
}

static DEVICE_API(smbus, smbus_stm32_api) = {
.configure = smbus_stm32_configure,
.get_config = smbus_stm32_get_config,
Expand All @@ -467,7 +558,7 @@ static DEVICE_API(smbus, smbus_stm32_api) = {
.smbus_smbalert_set_cb = NULL,
.smbus_smbalert_remove_cb = NULL,
#endif /* CONFIG_SMBUS_STM32_SMBALERT */
.smbus_block_pcall = NULL,
.smbus_block_pcall = smbus_stm32_block_pcall,
.smbus_host_notify_set_cb = NULL,
.smbus_host_notify_remove_cb = NULL,
};
Expand Down