From adff73d618bde2c83e9e6566ff199e27a79b7aad Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Wed, 7 Sep 2022 15:30:23 +0300 Subject: [PATCH] smbus: Introduce SMBus subsystem driver API Introduces SMBus driver API for SMBus controllers. Signed-off-by: Andrei Emeltchenko --- drivers/CMakeLists.txt | 1 + drivers/Kconfig | 1 + drivers/smbus/CMakeLists.txt | 6 + drivers/smbus/Kconfig | 30 ++ drivers/smbus/smbus_handlers.c | 130 +++++ include/zephyr/drivers/smbus.h | 937 +++++++++++++++++++++++++++++++++ 6 files changed, 1105 insertions(+) create mode 100644 drivers/smbus/CMakeLists.txt create mode 100644 drivers/smbus/Kconfig create mode 100644 drivers/smbus/smbus_handlers.c create mode 100644 include/zephyr/drivers/smbus.h diff --git a/drivers/CMakeLists.txt b/drivers/CMakeLists.txt index eb626f2868a..98d5b55a2b9 100644 --- a/drivers/CMakeLists.txt +++ b/drivers/CMakeLists.txt @@ -35,6 +35,7 @@ add_subdirectory_ifdef(CONFIG_FUEL_GAUGE fuel_gauge) add_subdirectory_ifdef(CONFIG_GPIO gpio) add_subdirectory_ifdef(CONFIG_HWINFO hwinfo) add_subdirectory_ifdef(CONFIG_I2C i2c) +add_subdirectory_ifdef(CONFIG_SMBUS smbus) add_subdirectory_ifdef(CONFIG_I2S i2s) add_subdirectory_ifdef(CONFIG_I3C i3c) add_subdirectory_ifdef(CONFIG_IEEE802154 ieee802154) diff --git a/drivers/Kconfig b/drivers/Kconfig index e462c28a440..1439d533071 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -35,6 +35,7 @@ source "drivers/hwinfo/Kconfig" source "drivers/i2c/Kconfig" source "drivers/i2s/Kconfig" source "drivers/i3c/Kconfig" +source "drivers/smbus/Kconfig" source "drivers/ieee802154/Kconfig" source "drivers/input/Kconfig" source "drivers/interrupt_controller/Kconfig" diff --git a/drivers/smbus/CMakeLists.txt b/drivers/smbus/CMakeLists.txt new file mode 100644 index 00000000000..a2d2e75e489 --- /dev/null +++ b/drivers/smbus/CMakeLists.txt @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() + + +zephyr_library_sources_ifdef(CONFIG_USERSPACE smbus_handlers.c) diff --git a/drivers/smbus/Kconfig b/drivers/smbus/Kconfig new file mode 100644 index 00000000000..808aa74845f --- /dev/null +++ b/drivers/smbus/Kconfig @@ -0,0 +1,30 @@ +# SMBus configuration options + +# Copyright (c) 2022 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +menuconfig SMBUS + bool "SMBus Drivers" + help + Enable SMBus Driver Configuration + +if SMBUS + +config SMBUS_STATS + bool "SMBus device Stats" + depends on STATS + help + Enable SMBus Stats. + +config SMBUS_INIT_PRIORITY + int "Init priority" + default KERNEL_INIT_PRIORITY_DEVICE + help + SMBus device driver initialization priority. + + +module = SMBUS +module-str = smbus +source "subsys/logging/Kconfig.template.log_config" + +endif # SMBUS diff --git a/drivers/smbus/smbus_handlers.c b/drivers/smbus/smbus_handlers.c new file mode 100644 index 00000000000..93ae3eb9a60 --- /dev/null +++ b/drivers/smbus/smbus_handlers.c @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2022 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +static inline int z_vrfy_smbus_configure(const struct device *dev, + uint32_t dev_config) +{ + Z_OOPS(Z_SYSCALL_DRIVER_SMBUS(dev, configure)); + + return z_impl_smbus_configure(dev, dev_config); +} +#include + +static inline int z_vrfy_smbus_get_config(const struct device *dev, + uint32_t *dev_config) +{ + Z_OOPS(Z_SYSCALL_DRIVER_SMBUS(dev, get_config)); + Z_OOPS(Z_SYSCALL_MEMORY_WRITE(dev_config, sizeof(uint32_t))); + + return z_impl_smbus_get_config(dev, dev_config); +} +#include + +static inline int z_vrfy_smbus_quick(const struct device *dev, uint16_t addr, + enum smbus_direction rw) +{ + Z_OOPS(Z_SYSCALL_OBJ(dev, K_OBJ_DRIVER_SMBUS)); + + return z_impl_smbus_quick(dev, addr, rw); +} +#include + +static inline int z_vrfy_smbus_byte_write(const struct device *dev, + uint16_t addr, uint8_t byte) +{ + Z_OOPS(Z_SYSCALL_OBJ(dev, K_OBJ_DRIVER_SMBUS)); + + return z_impl_smbus_byte_write(dev, addr, byte); +} +#include + +static inline int z_vrfy_smbus_byte_read(const struct device *dev, + uint16_t addr, uint8_t *byte) +{ + Z_OOPS(Z_SYSCALL_OBJ(dev, K_OBJ_DRIVER_SMBUS)); + Z_OOPS(Z_SYSCALL_MEMORY_WRITE(byte, sizeof(uint8_t))); + + return z_impl_smbus_byte_read(dev, addr, byte); +} +#include + +static inline int z_vrfy_smbus_byte_data_write(const struct device *dev, + uint16_t addr, uint8_t cmd, + uint8_t byte) +{ + Z_OOPS(Z_SYSCALL_OBJ(dev, K_OBJ_DRIVER_SMBUS)); + + return z_impl_smbus_byte_data_write(dev, addr, cmd, byte); +} +#include + +static inline int z_vrfy_smbus_byte_data_read(const struct device *dev, + uint16_t addr, uint8_t cmd, + uint8_t *byte) +{ + Z_OOPS(Z_SYSCALL_OBJ(dev, K_OBJ_DRIVER_SMBUS)); + Z_OOPS(Z_SYSCALL_MEMORY_WRITE(byte, sizeof(uint8_t))); + + return z_impl_smbus_byte_data_read(dev, addr, cmd, byte); +} +#include + +static inline int z_vrfy_smbus_word_data_write(const struct device *dev, + uint16_t addr, uint8_t cmd, + uint16_t word) +{ + Z_OOPS(Z_SYSCALL_OBJ(dev, K_OBJ_DRIVER_SMBUS)); + + return z_impl_smbus_word_data_write(dev, addr, cmd, word); +} +#include + +static inline int z_vrfy_smbus_word_data_read(const struct device *dev, + uint16_t addr, uint8_t cmd, + uint16_t *word) +{ + Z_OOPS(Z_SYSCALL_OBJ(dev, K_OBJ_DRIVER_SMBUS)); + Z_OOPS(Z_SYSCALL_MEMORY_WRITE(word, sizeof(uint16_t))); + + return z_impl_smbus_word_data_read(dev, addr, cmd, word); +} +#include + +static inline int z_vrfy_smbus_pcall(const struct device *dev, + uint16_t addr, uint8_t cmd, + uint16_t send_word, uint16_t *recv_word) +{ + Z_OOPS(Z_SYSCALL_OBJ(dev, K_OBJ_DRIVER_SMBUS)); + Z_OOPS(Z_SYSCALL_MEMORY_WRITE(recv_word, sizeof(uint16_t))); + + return z_impl_smbus_pcall(dev, addr, cmd, send_word, recv_word); +} +#include + +static inline int z_vrfy_smbus_block_write(const struct device *dev, + uint16_t addr, uint8_t cmd, + uint8_t count, uint8_t *buf) +{ + Z_OOPS(Z_SYSCALL_OBJ(dev, K_OBJ_DRIVER_SMBUS)); + Z_OOPS(Z_SYSCALL_MEMORY_READ(buf, count)); + + return z_impl_smbus_block_write(dev, addr, cmd, count, buf); +} +#include + +static inline int z_vrfy_smbus_block_read(const struct device *dev, + uint16_t addr, uint8_t cmd, + uint8_t *count, uint8_t *buf) +{ + Z_OOPS(Z_SYSCALL_OBJ(dev, K_OBJ_DRIVER_SMBUS)); + Z_OOPS(Z_SYSCALL_MEMORY_WRITE(count, sizeof(uint8_t))); + + return z_impl_smbus_block_read(dev, addr, cmd, count, buf); +} +#include diff --git a/include/zephyr/drivers/smbus.h b/include/zephyr/drivers/smbus.h new file mode 100644 index 00000000000..872f0481f29 --- /dev/null +++ b/include/zephyr/drivers/smbus.h @@ -0,0 +1,937 @@ +/* + * Copyright (c) 2022 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DRIVERS_SMBUS_H_ +#define ZEPHYR_INCLUDE_DRIVERS_SMBUS_H_ + +/** + * @brief SMBus Interface + * @defgroup smbus_interface SMBus Interface + * @ingroup io_interfaces + * @{ + */ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @name SMBus Protocol commands + * @{ + * + * SMBus Specification defines following SMBus protocols operations + */ + +/** + * SMBus Quick protocol is very simple command with no data sent or + * received. Peripheral may denote only R/W bit, which can still be + * used for the peripheral management, for example to switch peripheral + * On/Off. Quick protocol can also be used for peripheral devices + * scanning. + * + * @code + * 0 1 + * 0 1 2 3 4 5 6 7 8 9 0 + * +-+-+-+-+-+-+-+-+-+-+-+ + * |S| Periph Addr |D|A|P| + * +-+-+-+-+-+-+-+-+-+-+-+ + * @endcode + */ +#define SMBUS_CMD_QUICK 0b000 + +/** + * SMBus Byte protocol can send or receive one byte of data. + * + * @code + * Byte Write + * + * 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| Periph Addr |W|A| Command code |A|P| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * Byte Read + * + * 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| Periph Addr |R|A| Byte received |N|P| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * @endcode + */ +#define SMBUS_CMD_BYTE 0b001 + +/** + * SMBus Byte Data protocol send first byte (command) followed + * by read or write one byte. + * + * @code + * Byte Data Write + * + * 0 1 2 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |S| Periph Addr |W|A| Command code |A| Data Write |A|P| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * Byte Data Read + * + * 0 1 2 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |S| Periph Addr |W|A| Command code |A|S| Periph Addr |R|A| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Data Read |N|P| + * +-+-+-+-+-+-+-+-+-+-+ + * @endcode + */ +#define SMBUS_CMD_BYTE_DATA 0b010 + +/** + * SMBus Word Data protocol send first byte (command) followed + * by read or write two bytes. + * + * @code + * Word Data Write + * + * 0 1 2 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |S| Periph Addr |W|A| Command code |A| Data Write Low|A| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Data Write Hi |A|P| + * +-+-+-+-+-+-+-+-+-+-+ + * + * Word Data Read + * + * 0 1 2 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |S| Periph Addr |W|A| Command code |A|S| Periph Addr |R| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |A| Data Read Low |A| Data Read Hi |N|P| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * @endcode + */ +#define SMBUS_CMD_WORD_DATA 0b011 + +/** + * SMBus Process Call protocol is basically Write Word followed by + * Wead Word. It is named so because command sends data and waits + * for the perihperal to return reply. + * + * @code + * 0 1 2 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |S| Periph Addr |W|A| Command code |A| Data Write Low|A| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Data Write Hi |A|S| Periph Addr |R|A| Data Read Low |A| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Data Read Hi |N|P| + * +-+-+-+-+-+-+-+-+-+-+ + * @endcode + */ +#define SMBUS_CMD_PROC_CALL 0b100 + +/** + * SMBus Block protocol reads or writes block of data up to 32 bytes. + * Count byte specifies the amount of data. + * + * @code + * + * SMBus Block Write + * + * 0 1 2 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |S| Periph Addr |W|A| Command code |A| Send Count=N |A| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Data Write 1 |A| ... |A| Data Write N |A|P| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * SMBus Block Read + * + * 0 1 2 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |S| Periph Addr |W|A| Command code |A|S| Periph Addr |R| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |A| Recv Count=N |A| Data Read 1 |A| ... |A| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Data Read N |N|P| + * +-+-+-+-+-+-+-+-+-+-+ + * @endcode + */ +#define SMBUS_CMD_BLOCK 0b101 + +/** + * SMBus Block Write - Block Read Process Call protocol is basically + * Block Write followed by Block Read + * + * @code + * 0 1 2 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |S| Periph Addr |W|A| Command code |A| Count = N |A| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Data Write 1 |A| ... |A| Data Write N |A|S| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Periph Addr |R|A| Recv Count=N |A| Data Read 1 |A| | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | ... |A| Data Read N |N|P| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * @endcode + */ +#define SMBUS_CMD_BLOCK_PROC 0b111 +/** @} */ + +/** Maximum number of bytes in SMBus Block protocol */ +#define SMBUS_BLOCK_BYTES_MAX 32 + +/** + * @name SMBus device functionality + * @{ + * + * Following parameters describes functionality of SMBus device + */ + +/** Peripheral to act as Controller. */ +#define SMBUS_MODE_CONTROLLER BIT(0) + +/** Support Packet Error Code (PEC) checking */ +#define SMBUS_MODE_PEC BIT(1) + +/** @} */ + +/** + * @name SMBus special reserved addresses + * @{ + * + * Following addresses are reserved by SMBus specification + */ + +/** + * @brief Alert Response Address (ARA) + * + * A broadcast address used by the system host as part of the + * Alert Response Protocol. + */ +#define SMBUS_ADDRESS_ARA 0x0c + +/** @} */ + +/** + * @name SMBus read / write direction + * @{ + */ + +/** @brief SMBus read / write direction */ +enum smbus_direction { + /** Write message to SMBus */ + SMBUS_MSG_WRITE = 0, + /** Read message from SMBus */ + SMBUS_MSG_READ = 1, +}; + +/** @} */ + +/** @cond INTERNAL_HIDDEN */ +#define SMBUS_MSG_RW_MASK BIT(0) +/** @endcond */ + +/** + * @brief Complete SMBus DT information + * + * @param bus SMBus bus + * @param addr peripheral address + */ +struct smbus_dt_spec { + const struct device *bus; + uint16_t addr; +}; + +/** + * @brief Structure initializer for smbus_dt_spec from devicetree + * + * This helper macro expands to a static initializer for a struct + * smbus_dt_spec by reading the relevant bus and address data from + * the devicetree. + * + * @param node_id Devicetree node identifier for the SMBus device whose + * struct smbus_dt_spec to create an initializer for + */ +#define SMBUS_DT_SPEC_GET(node_id) \ + { \ + .bus = DEVICE_DT_GET(DT_BUS(node_id)), \ + .addr = DT_REG_ADDR(node_id) \ + } + +/** + * @brief Structure initializer for smbus_dt_spec from devicetree instance + * + * This is equivalent to + * SMBUS_DT_SPEC_GET(DT_DRV_INST(inst)). + * + * @param inst Devicetree instance number + */ +#define SMBUS_DT_SPEC_INST_GET(inst) SMBUS_DT_SPEC_GET(DT_DRV_INST(inst)) + +/** + * @cond INTERNAL_HIDDEN + * + * These are for internal use only, so skip these in + * public documentation. + */ + +typedef int (*smbus_api_configure_t)(const struct device *dev, + uint32_t dev_config); +typedef int (*smbus_api_get_config_t)(const struct device *dev, + uint32_t *dev_config); +typedef int (*smbus_api_quick_t)(const struct device *dev, + uint16_t addr, enum smbus_direction); +typedef int (*smbus_api_byte_write_t)(const struct device *dev, + uint16_t addr, uint8_t byte); +typedef int (*smbus_api_byte_read_t)(const struct device *dev, + uint16_t addr, uint8_t *byte); +typedef int (*smbus_api_byte_data_write_t)(const struct device *dev, + uint16_t addr, uint8_t cmd, + uint8_t byte); +typedef int (*smbus_api_byte_data_read_t)(const struct device *dev, + uint16_t addr, uint8_t cmd, + uint8_t *byte); +typedef int (*smbus_api_word_data_write_t)(const struct device *dev, + uint16_t addr, uint8_t cmd, + uint16_t word); +typedef int (*smbus_api_word_data_read_t)(const struct device *dev, + uint16_t addr, uint8_t cmd, + uint16_t *word); +typedef int (*smbus_api_pcall_t)(const struct device *dev, + uint16_t addr, uint8_t cmd, + uint16_t send_word, uint16_t *recv_word); +typedef int (*smbus_api_block_write_t)(const struct device *dev, + uint16_t addr, uint8_t cmd, + uint8_t count, uint8_t *buf); +typedef int (*smbus_api_block_read_t)(const struct device *dev, + uint16_t addr, uint8_t cmd, + uint8_t *count, uint8_t *buf); +typedef int (*smbus_api_block_pcall_t)(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); + +__subsystem struct smbus_driver_api { + smbus_api_configure_t configure; + smbus_api_get_config_t get_config; + smbus_api_quick_t smbus_quick; + smbus_api_byte_write_t smbus_byte_write; + smbus_api_byte_read_t smbus_byte_read; + smbus_api_byte_data_write_t smbus_byte_data_write; + smbus_api_byte_data_read_t smbus_byte_data_read; + smbus_api_word_data_write_t smbus_word_data_write; + smbus_api_word_data_read_t smbus_word_data_read; + smbus_api_pcall_t smbus_pcall; + smbus_api_block_write_t smbus_block_write; + smbus_api_block_read_t smbus_block_read; + smbus_api_block_pcall_t smbus_block_pcall; +}; + +/** + * @endcond + */ + +#if defined(CONFIG_SMBUS_STATS) || defined(__DOXYGEN__) + +#include + +/** @cond INTERNAL_HIDDEN */ + +STATS_SECT_START(smbus) +STATS_SECT_ENTRY32(bytes_read) +STATS_SECT_ENTRY32(bytes_written) +STATS_SECT_ENTRY32(command_count) +STATS_SECT_END; + +STATS_NAME_START(smbus) +STATS_NAME(smbus, bytes_read) +STATS_NAME(smbus, bytes_written) +STATS_NAME(smbus, command_count) +STATS_NAME_END(smbus); + +struct smbus_device_state { + struct device_state devstate; + struct stats_smbus stats; +}; + +/** + * @brief Define a statically allocated and section assigned smbus device state + */ +#define Z_SMBUS_DEVICE_STATE_DEFINE(node_id, dev_name) \ + static struct smbus_device_state Z_DEVICE_STATE_NAME(dev_name) \ + __attribute__((__section__(".z_devstate"))); + +/** + * @brief Define an smbus device init wrapper function + * + * This does device instance specific initialization of common data + * (such as stats) and calls the given init_fn + */ +#define Z_SMBUS_INIT_FN(dev_name, init_fn) \ + static inline int \ + UTIL_CAT(dev_name, _init)(const struct device *dev) \ + { \ + struct smbus_device_state *state = \ + CONTAINER_OF(dev->state, \ + struct smbus_device_state, \ + devstate); \ + stats_init(&state->stats.s_hdr, STATS_SIZE_32, 4, \ + STATS_NAME_INIT_PARMS(smbus)); \ + stats_register(dev->name, &(state->stats.s_hdr)); \ + return init_fn(dev); \ + } + +/** @endcond */ + +/** + * @brief Updates the SMBus stats + * + * @param dev SMBus device to update stats for + * @param sent Number of bytes sent + * @param recv Number of bytes received + */ +static inline void smbus_xfer_stats(const struct device *dev, uint8_t sent, + uint8_t recv) +{ + struct smbus_device_state *state = + CONTAINER_OF(dev->state, struct smbus_device_state, devstate); + + STATS_INC(state->stats, command_count); + STATS_INCN(state->stats, bytes_read, recv); + STATS_INCN(state->stats, bytes_written, sent); +} + +/** + * @brief Like DEVICE_DT_DEFINE() with SMBus specifics. + * + * @details Defines a device which implements the SMBus API. May + * generate a custom device_state container struct and init_fn + * wrapper when needed depending on SMBus @kconfig{CONFIG_SMBUS_STATS}. + * + * @param node_id The devicetree node identifier. + * + * @param init_fn Name of the init function of the driver. + * + * @param pm_device PM device resources reference + * (NULL if device does not use PM). + * + * @param data_ptr Pointer to the device's private data. + * + * @param cfg_ptr The address to the structure containing the + * configuration information for this instance of the driver. + * + * @param level The initialization level. See SYS_INIT() for + * details. + * + * @param prio Priority within the selected initialization level. See + * SYS_INIT() for details. + * + * @param api_ptr Provides an initial pointer to the API function struct + * used by the driver. Can be NULL. + */ +#define SMBUS_DEVICE_DT_DEFINE(node_id, init_fn, pm_device, \ + data_ptr, cfg_ptr, level, prio, \ + api_ptr, ...) \ + Z_SMBUS_DEVICE_STATE_DEFINE(node_id, \ + Z_DEVICE_DT_DEV_NAME(node_id)); \ + Z_SMBUS_INIT_FN(Z_DEVICE_DT_DEV_NAME(node_id), init_fn) \ + Z_DEVICE_DEFINE(node_id, Z_DEVICE_DT_DEV_NAME(node_id), \ + DEVICE_DT_NAME(node_id), \ + &UTIL_CAT(Z_DEVICE_DT_DEV_NAME(node_id), _init),\ + pm_device, \ + data_ptr, cfg_ptr, level, prio, \ + api_ptr, \ + &(Z_DEVICE_STATE_NAME(Z_DEVICE_DT_DEV_NAME \ + (node_id)).devstate), \ + __VA_ARGS__) + +#else /* CONFIG_SMBUS_STATS */ + +static inline void smbus_xfer_stats(const struct device *dev, uint8_t sent, + uint8_t recv) +{ + ARG_UNUSED(dev); + ARG_UNUSED(sent); + ARG_UNUSED(recv); +} + +#define SMBUS_DEVICE_DT_DEFINE(node_id, init_fn, pm_device, \ + data_ptr, cfg_ptr, level, prio, \ + api_ptr, ...) \ + DEVICE_DT_DEFINE(node_id, &init_fn, pm_device, \ + data_ptr, cfg_ptr, level, prio, \ + api_ptr, __VA_ARGS__) + +#endif /* CONFIG_SMBUS_STATS */ + +/** + * @brief Like SMBUS_DEVICE_DT_DEFINE() for an instance of a DT_DRV_COMPAT + * compatible + * + * @param inst instance number. This is replaced by + * DT_DRV_COMPAT(inst) in the call to SMBUS_DEVICE_DT_DEFINE(). + * + * @param ... other parameters as expected by SMBUS_DEVICE_DT_DEFINE(). + */ +#define SMBUS_DEVICE_DT_INST_DEFINE(inst, ...) \ + SMBUS_DEVICE_DT_DEFINE(DT_DRV_INST(inst), __VA_ARGS__) + +/** + * @brief Configure operation of a SMBus host controller. + * + * @param dev Pointer to the device structure for the driver instance. + * @param dev_config Bit-packed 32-bit value to the device runtime configuration + * for the SMBus controller. + * + * @retval 0 If successful. + * @retval -EIO General input / output error, failed to configure device. + */ +__syscall int smbus_configure(const struct device *dev, uint32_t dev_config); + +static inline int z_impl_smbus_configure(const struct device *dev, + uint32_t dev_config) +{ + const struct smbus_driver_api *api = + (const struct smbus_driver_api *)dev->api; + + return api->configure(dev, dev_config); +} + +/** + * @brief Get configuration of a SMBus host controller. + * + * This routine provides a way to get current configuration. It is allowed to + * call the function before smbus_configure, because some SMBus ports can be + * configured during init process. However, if the SMBus port is not configured, + * smbus_get_config returns an error. + * + * smbus_get_config can return cached config or probe hardware, but it has to be + * up to date with current configuration. + * + * @param dev Pointer to the device structure for the driver instance. + * @param dev_config Pointer to return bit-packed 32-bit value of + * the SMBus controller configuration. + * + * @retval 0 If successful. + * @retval -EIO General input / output error. + * @retval -ENOSYS If get config is not implemented + */ +__syscall int smbus_get_config(const struct device *dev, uint32_t *dev_config); + +static inline int z_impl_smbus_get_config(const struct device *dev, + uint32_t *dev_config) +{ + const struct smbus_driver_api *api = + (const struct smbus_driver_api *)dev->api; + + if (api->get_config == NULL) { + return -ENOSYS; + } + + return api->get_config(dev, dev_config); +} + +/** + * @brief Perform SMBus Quick operation + * + * This routine provides a generic interface to perform SMBus Quick + * operation. + * + * @param dev Pointer to the device structure for an SMBus + * driver configured in controller mode. + * @param addr Address of the SMBus peripheral device. + * @param direction Direction Read or Write. + * + * @retval 0 If successful. + * @retval -ENOSYS If not implemented by the driver. + * @retval -EIO General input / output error. + */ +__syscall int smbus_quick(const struct device *dev, uint16_t addr, + enum smbus_direction direction); + +static inline int z_impl_smbus_quick(const struct device *dev, uint16_t addr, + enum smbus_direction direction) +{ + const struct smbus_driver_api *api = + (const struct smbus_driver_api *)dev->api; + + if (api->smbus_quick == NULL) { + return -ENOSYS; + } + + if (direction != SMBUS_MSG_READ && direction != SMBUS_MSG_WRITE) { + return -EINVAL; + } + + return api->smbus_quick(dev, addr, direction); +} + +/** + * @brief Perform SMBus Byte Write operation + * + * This routine provides a generic interface to perform SMBus + * Byte Write operation. + * + * @param dev Pointer to the device structure for an SMBus + * driver configured in controller mode. + * @param addr Address of the SMBus peripheral device. + * @param byte Byte to be sent to the peripheral device. + * + * @retval 0 If successful. + * @retval -ENOSYS If not implemented by the driver. + * @retval -EIO General input / output error. + */ +__syscall int smbus_byte_write(const struct device *dev, uint16_t addr, + uint8_t byte); + +static inline int z_impl_smbus_byte_write(const struct device *dev, + uint16_t addr, uint8_t byte) +{ + const struct smbus_driver_api *api = + (const struct smbus_driver_api *)dev->api; + + if (api->smbus_byte_write == NULL) { + return -ENOSYS; + } + + return api->smbus_byte_write(dev, addr, byte); +} + +/** + * @brief Perform SMBus Byte Read operation + * + * This routine provides a generic interface to perform SMBus + * Byte Read operation. + * + * @param dev Pointer to the device structure for an SMBus + * driver configured in controller mode. + * @param addr Address of the SMBus peripheral device. + * @param byte Byte received from the peripheral device. + * + * @retval 0 If successful. + * @retval -ENOSYS If not implemented by the driver. + * @retval -EIO General input / output error. + */ +__syscall int smbus_byte_read(const struct device *dev, uint16_t addr, + uint8_t *byte); + +static inline int z_impl_smbus_byte_read(const struct device *dev, + uint16_t addr, uint8_t *byte) +{ + const struct smbus_driver_api *api = + (const struct smbus_driver_api *)dev->api; + + if (api->smbus_byte_read == NULL) { + return -ENOSYS; + } + + return api->smbus_byte_read(dev, addr, byte); +} + +/** + * @brief Perform SMBus Byte Data Write operation + * + * This routine provides a generic interface to perform SMBus + * Byte Data Write operation. + * + * @param dev Pointer to the device structure for an SMBus + * driver configured in controller mode. + * @param addr Address of the SMBus peripheral device. + * @param cmd Command byte which is sent to peripheral device first. + * @param byte Byte to be sent to the peripheral device. + * + * @retval 0 If successful. + * @retval -ENOSYS If not implemented by the driver. + * @retval -EIO General input / output error. + */ +__syscall int smbus_byte_data_write(const struct device *dev, uint16_t addr, + uint8_t cmd, uint8_t byte); + +static inline int z_impl_smbus_byte_data_write(const struct device *dev, + uint16_t addr, uint8_t cmd, + uint8_t byte) +{ + const struct smbus_driver_api *api = + (const struct smbus_driver_api *)dev->api; + + if (api->smbus_byte_data_write == NULL) { + return -ENOSYS; + } + + return api->smbus_byte_data_write(dev, addr, cmd, byte); +} + +__syscall int smbus_byte_data_read(const struct device *dev, uint16_t addr, + uint8_t cmd, uint8_t *byte); + +/** + * @brief Perform SMBus Byte Data Read operation + * + * This routine provides a generic interface to perform SMBus + * Byte Data Read operation. + * + * @param dev Pointer to the device structure for an SMBus + * driver configured in controller mode. + * @param addr Address of the SMBus peripheral device. + * @param cmd Command byte which is sent to peripheral device first. + * @param byte Byte received from the peripheral device. + * + * @retval 0 If successful. + * @retval -ENOSYS If not implemented by the driver. + * @retval -EIO General input / output error. + */ +static inline int z_impl_smbus_byte_data_read(const struct device *dev, + uint16_t addr, uint8_t cmd, + uint8_t *byte) +{ + const struct smbus_driver_api *api = + (const struct smbus_driver_api *)dev->api; + + if (api->smbus_byte_data_read == NULL) { + return -ENOSYS; + } + + return api->smbus_byte_data_read(dev, addr, cmd, byte); +} + +/** + * @brief Perform SMBus Word Data Write operation + * + * This routine provides a generic interface to perform SMBus + * Word Data Write operation. + * + * @param dev Pointer to the device structure for an SMBus + * driver configured in controller mode. + * @param addr Address of the SMBus peripheral device. + * @param cmd Command byte which is sent to peripheral device first. + * @param word Word (16-bit) to be sent to the peripheral device. + * + * @retval 0 If successful. + * @retval -ENOSYS If not implemented by the driver. + * @retval -EIO General input / output error. + */ +__syscall int smbus_word_data_write(const struct device *dev, uint16_t addr, + uint8_t cmd, uint16_t word); + +static inline int z_impl_smbus_word_data_write(const struct device *dev, + uint16_t addr, uint8_t cmd, + uint16_t word) +{ + const struct smbus_driver_api *api = + (const struct smbus_driver_api *)dev->api; + + if (api->smbus_word_data_write == NULL) { + return -ENOSYS; + } + + return api->smbus_word_data_write(dev, addr, cmd, word); +} + +/** + * @brief Perform SMBus Word Data Read operation + * + * This routine provides a generic interface to perform SMBus + * Word Data Read operation. + * + * @param dev Pointer to the device structure for an SMBus + * driver configured in controller mode. + * @param addr Address of the SMBus peripheral device. + * @param cmd Command byte which is sent to peripheral device first. + * @param word Word (16-bit) received from the peripheral device. + * + * @retval 0 If successful. + * @retval -ENOSYS If not implemented by the driver. + * @retval -EIO General input / output error. + */ +__syscall int smbus_word_data_read(const struct device *dev, uint16_t addr, + uint8_t cmd, uint16_t *word); + +static inline int z_impl_smbus_word_data_read(const struct device *dev, + uint16_t addr, uint8_t cmd, + uint16_t *word) +{ + const struct smbus_driver_api *api = + (const struct smbus_driver_api *)dev->api; + + if (api->smbus_word_data_read == NULL) { + return -ENOSYS; + } + + return api->smbus_word_data_read(dev, addr, cmd, word); +} + +/** + * @brief Perform SMBus Process Call operation + * + * This routine provides a generic interface to perform SMBus + * Process Call operation, which means Write 2 bytes following by + * Read 2 bytes. + * + * @param dev Pointer to the device structure for an SMBus + * driver configured in controller mode. + * @param addr Address of the SMBus peripheral device. + * @param cmd Command byte which is sent to peripheral device first. + * @param send_word Word (16-bit) to be sent to the peripheral device. + * @param recv_word Word (16-bit) received from the peripheral device. + * + * @retval 0 If successful. + * @retval -ENOSYS If not implemented by the driver. + * @retval -EIO General input / output error. + */ +__syscall int smbus_pcall(const struct device *dev, uint16_t addr, + uint8_t cmd, uint16_t send_word, uint16_t *recv_word); + +static inline int z_impl_smbus_pcall(const struct device *dev, + uint16_t addr, uint8_t cmd, + uint16_t send_word, uint16_t *recv_word) +{ + const struct smbus_driver_api *api = + (const struct smbus_driver_api *)dev->api; + + if (api->smbus_pcall == NULL) { + return -ENOSYS; + } + + return api->smbus_pcall(dev, addr, cmd, send_word, recv_word); +} + +/** + * @brief Perform SMBus Block Write operation + * + * This routine provides a generic interface to perform SMBus + * Block Write operation. + * + * @param dev Pointer to the device structure for an SMBus + * driver configured in controller mode. + * @param addr Address of the SMBus peripheral device. + * @param cmd Command byte which is sent to peripheral device first. + * @param count Size of the data block buffer. Maximum 32 bytes. + * @param buf Data block buffer to be sent to the peripheral device. + * + * @retval 0 If successful. + * @retval -ENOSYS If not implemented by the driver. + * @retval -EIO General input / output error. + */ +__syscall int smbus_block_write(const struct device *dev, uint16_t addr, + uint8_t cmd, uint8_t count, uint8_t *buf); + +static inline int z_impl_smbus_block_write(const struct device *dev, + uint16_t addr, uint8_t cmd, + uint8_t count, uint8_t *buf) +{ + const struct smbus_driver_api *api = + (const struct smbus_driver_api *)dev->api; + + if (api->smbus_block_write == NULL) { + return -ENOSYS; + } + + if (count < 1 || count > SMBUS_BLOCK_BYTES_MAX) { + return -EINVAL; + } + + return api->smbus_block_write(dev, addr, cmd, count, buf); +} + +/** + * @brief Perform SMBus Block Read operation + * + * This routine provides a generic interface to perform SMBus + * Block Read operation. + * + * @param dev Pointer to the device structure for an SMBus + * driver configured in controller mode. + * @param addr Address of the SMBus peripheral device. + * @param cmd Command byte which is sent to peripheral device first. + * @param count Size of the data peripheral sent. Maximum 32 bytes. + * @param buf Data block buffer received from the peripheral device. + * + * @retval 0 If successful. + * @retval -ENOSYS If not implemented by the driver. + * @retval -EIO General input / output error. + */ +__syscall int smbus_block_read(const struct device *dev, uint16_t addr, + uint8_t cmd, uint8_t *count, uint8_t *buf); + +static inline int z_impl_smbus_block_read(const struct device *dev, + uint16_t addr, uint8_t cmd, + uint8_t *count, uint8_t *buf) +{ + const struct smbus_driver_api *api = + (const struct smbus_driver_api *)dev->api; + + if (api->smbus_block_read == NULL) { + return -ENOSYS; + } + + return api->smbus_block_read(dev, addr, cmd, count, buf); +} + +/** + * @brief Perform SMBus Block Process Call operation + * + * This routine provides a generic interface to perform SMBus + * Block Process Call operation. This operation is basically + * Block Write followed by Block Read. + * + * @param dev Pointer to the device structure for an SMBus + * driver configured in controller mode. + * @param addr Address of the SMBus peripheral device. + * @param cmd Command byte which is sent to peripheral device first. + * @param snd_count Size of the data block buffer to send. + * @param snd_buf Data block buffer send to the peripheral device. + * @param rcv_count Size of the data peripheral sent. + * @param rcv_buf Data block buffer received from the peripheral device. + * + * @retval 0 If successful. + * @retval -ENOSYS If not implemented by the driver. + * @retval -EIO General input / output error. + */ +__syscall int smbus_block_pcall(const struct device *dev, + uint16_t addr, uint8_t cmd, + uint8_t snd_count, uint8_t *snd_buf, + uint8_t *rcv_count, uint8_t *rcv_buf); + +static inline int z_impl_smbus_block_pcall(const struct device *dev, + uint16_t addr, uint8_t cmd, + uint8_t snd_count, uint8_t *snd_buf, + uint8_t *rcv_count, uint8_t *rcv_buf) +{ + const struct smbus_driver_api *api = + (const struct smbus_driver_api *)dev->api; + + if (api->smbus_block_pcall == NULL) { + return -ENOSYS; + } + + return api->smbus_block_pcall(dev, addr, cmd, snd_count, snd_buf, + rcv_count, rcv_buf); +} + +#ifdef __cplusplus +} +#endif + +/** + * @} + */ + +#include + +#endif /* ZEPHYR_INCLUDE_DRIVERS_SMBUS_H_ */