From 98b70352ffcf6e674a88be871fdc70e2a2ccf870 Mon Sep 17 00:00:00 2001 From: Ramiro de Rojas Perez Date: Fri, 4 Nov 2022 16:08:07 -0700 Subject: [PATCH] i3c: imx3112: Add driver for Renesas IMX3112 mux This change adds the driver for the Renesas IMX3112 2:1 bus multiplexer. It also adds dt-bindings for generic i3c-muxes, and Kconfig priorities to make sure the init order is: 1. bus controller 2. mux 3. channel Signed-off-by: Ramiro de Rojas Perez --- drivers/i3c/CMakeLists.txt | 5 + drivers/i3c/Kconfig | 1 + drivers/i3c/Kconfig.renesas | 28 ++ drivers/i3c/i3c_renesas_imx3112.c | 414 ++++++++++++++++++++++ drivers/i3c/i3c_renesas_imx3112_regs.h | 121 +++++++ dts/bindings/i3c/i3c-mux-device.yaml | 49 +++ dts/bindings/i3c/renesas,imx3112-i3c.yaml | 56 +++ 7 files changed, 674 insertions(+) create mode 100644 drivers/i3c/Kconfig.renesas create mode 100644 drivers/i3c/i3c_renesas_imx3112.c create mode 100644 drivers/i3c/i3c_renesas_imx3112_regs.h create mode 100644 dts/bindings/i3c/i3c-mux-device.yaml create mode 100644 dts/bindings/i3c/renesas,imx3112-i3c.yaml diff --git a/drivers/i3c/CMakeLists.txt b/drivers/i3c/CMakeLists.txt index 4371cd385000633..8a299f283242bc0 100644 --- a/drivers/i3c/CMakeLists.txt +++ b/drivers/i3c/CMakeLists.txt @@ -28,3 +28,8 @@ zephyr_library_sources_ifdef( CONFIG_I3C_CADENCE i3c_cdns.c ) + +zephyr_library_sources_ifdef( + CONFIG_I3C_RENESAS_IMX3112 + i3c_renesas_imx3112.c +) diff --git a/drivers/i3c/Kconfig b/drivers/i3c/Kconfig index f4a343632abf4bb..0ea4ca3ff1b3f79 100644 --- a/drivers/i3c/Kconfig +++ b/drivers/i3c/Kconfig @@ -100,5 +100,6 @@ comment "Device Drivers" rsource "Kconfig.nxp" rsource "Kconfig.cdns" +rsource "Kconfig.renesas" endif # I3C diff --git a/drivers/i3c/Kconfig.renesas b/drivers/i3c/Kconfig.renesas new file mode 100644 index 000000000000000..80c5f844fb36167 --- /dev/null +++ b/drivers/i3c/Kconfig.renesas @@ -0,0 +1,28 @@ +# Copyright (c) 2022 Google LLC +# SPDX-License-Identifier: Apache-2.0 + +config I3C_RENESAS_MUX_PRIORITY + int "I3C Renesas Mux Init Priority" + # Default is just after I3C_CONTROLLER_INIT_PRIORITY + default 51 + help + This is used for setting up an I3C renesas mux, by configuring + the electrical parameters and doing device discovery. + + Note that this needs to be done before the device driver + instances of the connected I3C devices start + initializing those devices, but after the bus controller finishes + initialization + +config I3C_RENESAS_CHANNEL_PRIORITY + int "I3C Renesas Mux Init Priority" + # Default is just after I3C_RENESAS_MUX_PRIORITY + default 52 + help + This is used for setting up an I3C renesas mux channels by + doing device discovery in each channel + +config I3C_RENESAS_IMX3112 + bool "Renesas I3C 2:1 bus multiplexer imx3112" + help + Enable the renesas imx3112 mux diff --git a/drivers/i3c/i3c_renesas_imx3112.c b/drivers/i3c/i3c_renesas_imx3112.c new file mode 100644 index 000000000000000..450aa68e945a0d6 --- /dev/null +++ b/drivers/i3c/i3c_renesas_imx3112.c @@ -0,0 +1,414 @@ +/* renesas_imx3112_i3c.c - I3C file for Renesas I3C 1:2 bus multiplexer */ + +/* + * Copyright (c) 2022 Google LLC. + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "i3c_renesas_imx3112_regs.h" + +#include +#include +#include +#include +#include +#include + +#define LOG_LEVEL CONFIG_I3C_LOG_LEVEL +#include +LOG_MODULE_REGISTER(i3c_renesas_imx3112); + +/* Number of ports that can be accessed by device */ +#define NUM_CHANNELS 2 +/* Timeout for locking in a channel - 5 seconds is more than sufficient */ +#define CHAN_LOCK_TIMEOUT K_MSEC(5000) + +/* + * Resistor values for SCL/SDA lines. + * Not used if has_external_resistor is true + */ +struct channel_resistance { + uint8_t scl; + uint8_t sda; +}; + +/* + * Devicetree values used to configure the mux during initalization. + * Determine the electrical characteristics of the mux channels + */ +struct mux_hw_configuration { + /* + * Indicates if mux has external pullup resistors or + * internal programmable resistors for SCL/SDA lines + */ + bool has_external_resistor; + /* IO voltage level - at which SCL/SDA lines are driven */ + uint8_t io_voltage; + /* Channel resistances */ + struct channel_resistance ch_resistance[NUM_CHANNELS]; +}; + +/* + * I3C device configuration at initialization phase + * + * These are mostly configured with device tree information. + */ +struct i3c_renesas_imx3112_mux_config { + /* I3C bus controller */ + const struct device *bus; + + /* Device id of the mux in the bus */ + const struct i3c_device_id dev_id; + + /* Hw config for the mux */ + struct mux_hw_configuration hw_config; +}; + +/* + * I3C device data updated at runtime + */ +struct i3c_renesas_imx3112_mux_data { + struct { + /* Is the mux enabled - is at least one of the channels active */ + bool enabled; + /* Currently enabled channel mask - not relevant if enabled is false */ + uint8_t channel_mask; + } mux_state; + + /* + * I3C device description, updated after controller init. Runtime equivalent of + * i2c_dt_spec + */ + struct i3c_device_desc *mux_desc; + + /* mutex for imx3112 accesses */ + struct k_mutex lock; +}; + +struct i3c_renesas_imx3112_channel_config { + /* Pointer to parent driver */ + const struct device *mux; + /* Will be written directly to MR64/MR65 */ + uint8_t channel_mask; + /* List of devices attached to this channel */ + struct i3c_dev_list device_list; +}; + +static inline struct i3c_renesas_imx3112_mux_data * +get_mux_data_from_channel(const struct device *dev) +{ + const struct i3c_renesas_imx3112_channel_config *channel_config = dev->config; + + return channel_config->mux->data; +} + +static inline const struct i3c_renesas_imx3112_mux_config * +get_mux_config_from_channel(const struct device *dev) +{ + const struct i3c_renesas_imx3112_channel_config *channel_config = dev->config; + + return channel_config->mux->config; +} + +static inline int i3c_renesas_imx3112_read_reg(const struct device *mux_dev, uint8_t *val, + uint8_t addr) +{ + LOG_DBG("Reading from %s : address 0x%X", mux_dev->name, addr); + struct i3c_renesas_imx3112_mux_data *data = mux_dev->data; + struct i3c_device_desc *mux_desc = data->mux_desc; + uint8_t write_buf[2]; + int err = 0; + + /* Prepare message - address is split into two bytes */ + write_buf[0] = FIELD_GET(GENMASK(6, 0), addr); + write_buf[1] = FIELD_GET(BIT(7), addr); + + err = i3c_write_read(mux_desc, write_buf, sizeof(write_buf), val, sizeof(uint8_t)); + + if (err) { + LOG_ERR("Failed to read from %s : address 0x%X", mux_dev->name, addr); + return err; + } + return 0; +} + +static inline int i3c_renesas_imx3112_write_reg(const struct device *mux_dev, uint8_t addr, + uint8_t val) +{ + LOG_DBG("Writing 0x%X to %s : address 0x%X", val, mux_dev->name, addr); + struct i3c_renesas_imx3112_mux_data *data = mux_dev->data; + struct i3c_device_desc *mux_desc = data->mux_desc; + uint8_t write_buf[3]; + uint8_t actual_val; + int err = 0; + + /* Prepare message - address is split into two bytes */ + write_buf[0] = FIELD_GET(GENMASK(6, 0), addr); + write_buf[1] = FIELD_GET(BIT(7), addr); + write_buf[2] = val; + + err = i3c_write(mux_desc, write_buf, sizeof(write_buf)); + if (err) { + LOG_ERR("Failed to write to %s : address 0x%X", mux_dev->name, addr); + return err; + } + if (IS_ENABLED(CONFIG_I3C_LOG_LEVEL_DBG)) { + /* Check that the correct value was written to the correct address */ + err = i3c_renesas_imx3112_read_reg(mux_dev, &actual_val, addr); + if (err) { + return err; + } + if (val != actual_val) { + LOG_ERR("Read value from %s : address 0x%X was 0x%X, expected 0x%X", + mux_dev->name, addr, actual_val, val); + return -EIO; + } + } + + return 0; +} + +static inline int i3c_renesas_imx3112_set_channel(const struct device *mux_dev, uint8_t select_mask) +{ + struct i3c_renesas_imx3112_mux_data *data = mux_dev->data; + uint8_t select_mask_reg; + int err = 0; + + /* + * Only select the channel if its different from the last channel or if + * the mux hasn't been enabled yet + */ + LOG_DBG("Mux dev %s attempting mask change. Current channel mask is: 0x%X, " + "new channel mask is 0x%X", + mux_dev->name, data->mux_state.channel_mask, select_mask); + if (!data->mux_state.enabled || (data->mux_state.channel_mask != select_mask)) { + /* Offset the select_mask to the correct field */ + select_mask_reg = FIELD_PREP(RF_SCL_SDA_0_EN | RF_SCL_SDA_1_EN, select_mask); + /* Deactivate the mux temporarily while we select a new channel mask */ + err = i3c_renesas_imx3112_write_reg( + mux_dev, offsetof(struct i3c_renesas_imx3112_registers, mux_config), 0); + if (err) { + return err; + } + + /* Select new channel mask */ + err = i3c_renesas_imx3112_write_reg( + mux_dev, offsetof(struct i3c_renesas_imx3112_registers, mux_select), + select_mask_reg); + if (err) { + return err; + } + + /* Re-enable mux */ + err = i3c_renesas_imx3112_write_reg( + mux_dev, offsetof(struct i3c_renesas_imx3112_registers, mux_config), + select_mask_reg); + if (err) { + return err; + } + + /* Update runtime data */ + data->mux_state.enabled = true; + data->mux_state.channel_mask = select_mask; + } + return 0; +} + +static int i3c_renesas_imx3112_transfer(const struct device *channel_dev, + struct i3c_device_desc *target, struct i3c_msg *msgs, + uint8_t num_msgs) +{ + struct i3c_renesas_imx3112_mux_data *data = get_mux_data_from_channel(channel_dev); + const struct i3c_renesas_imx3112_mux_config *config = + get_mux_config_from_channel(channel_dev); + const struct i3c_renesas_imx3112_channel_config *channel_config = channel_dev->config; + const struct device *bus_dev = config->bus; + struct i3c_device_id pid = {.pid = target->pid}; + int err; + + err = k_mutex_lock(&data->lock, CHAN_LOCK_TIMEOUT); + if (err) { + return err; + } + + err = i3c_renesas_imx3112_set_channel(channel_config->mux, channel_config->channel_mask); + if (err) { + goto end_trans; + } + /* Change the device bus from the mux to the controller to redirect the transaction */ + target = i3c_device_find(bus_dev, &pid); + if (target == NULL) { + LOG_ERR("Mux dev %s failed to find target: 0x%llX", channel_dev->name, + (uint64_t)pid.pid); + return -ENXIO; + } + err = i3c_transfer(target, msgs, num_msgs); + +end_trans: + k_mutex_unlock(&data->lock); + if (err) { + LOG_ERR("Mux dev %s transfer failed with error %d", channel_dev->name, err); + } + return err; +} + +static int i3c_renesas_imx3112_configure(const struct device *dev, enum i3c_config_type type, + void *config) +{ + return -ENOSYS; +} + +static struct i3c_device_desc *i3c_renesas_imx3112_device_find(const struct device *channel_dev, + const struct i3c_device_id *id) +{ + const struct i3c_renesas_imx3112_channel_config *channel_config = channel_dev->config; + + return i3c_dev_list_find(&channel_config->device_list, id); +} + +static int i3c_renesas_imx3112_channel_initialize(const struct device *channel_dev) +{ + LOG_DBG("Initalizing mux channel %s", channel_dev->name); + const struct i3c_renesas_imx3112_channel_config *channel_cfg = channel_dev->config; + const struct i3c_renesas_imx3112_mux_config *mux_cfg = + get_mux_config_from_channel(channel_dev); + int err; + + if (!device_is_ready(channel_cfg->mux)) { + LOG_ERR("I3C mux root %s not ready", channel_cfg->mux->name); + return -ENODEV; + } + + /* Run a SETAASA on the channel to switch all children to I3C mode */ + err = i3c_renesas_imx3112_set_channel(channel_cfg->mux, channel_cfg->channel_mask); + if (err) { + return err; + } + err = i3c_ccc_do_setaasa(mux_cfg->bus); + if (err) { + return err; + } + LOG_DBG("Mux channel %s initalization complete", channel_dev->name); + return 0; +} + +static int i3c_renesas_imx3112_mux_initialize(const struct device *dev) +{ + LOG_DBG("Initalizing mux %s", dev->name); + const struct i3c_renesas_imx3112_mux_config *cfg = dev->config; + struct i3c_renesas_imx3112_mux_data *data = dev->data; + uint8_t intf_cfg_val = 0; + uint8_t res_cfg_val = 0; + int err; + + if (cfg->bus == NULL || !device_is_ready(cfg->bus)) { + LOG_ERR("Cannot find I3C controller"); + return -ENODEV; + } + + /* + * Need to grab the pointer to the I3C device descriptor + * before we can configure the mux + */ + data->mux_desc = i3c_device_find(cfg->bus, &cfg->dev_id); + if (data->mux_desc == NULL) { + LOG_ERR("Cannot find I3C device descriptor"); + return -ENODEV; + } + + /* Perform HW configuration for the mux based on devicetree defaults */ + intf_cfg_val |= + FIELD_PREP(RF_LOCAL_INF_PULLUP_CONF, cfg->hw_config.has_external_resistor ? 1 : 0); + intf_cfg_val |= FIELD_PREP(RF_LOCAL_INF_IO_LEVEL, cfg->hw_config.io_voltage); + err = i3c_renesas_imx3112_write_reg( + dev, offsetof(struct i3c_renesas_imx3112_registers, local_interface_cfg), + intf_cfg_val); + if (err) { + LOG_ERR("Mux %s HW configuration failed", dev->name); + return err; + } + + res_cfg_val |= FIELD_PREP(RF_LSCL_0_PU_RES, cfg->hw_config.ch_resistance[0].scl); + res_cfg_val |= FIELD_PREP(RF_LSDA_0_PU_RES, cfg->hw_config.ch_resistance[0].sda); + res_cfg_val |= FIELD_PREP(RF_LSCL_1_PU_RES, cfg->hw_config.ch_resistance[1].scl); + res_cfg_val |= FIELD_PREP(RF_LSDA_1_PU_RES, cfg->hw_config.ch_resistance[1].sda); + err = i3c_renesas_imx3112_write_reg( + dev, offsetof(struct i3c_renesas_imx3112_registers, pullup_resistor_config), + res_cfg_val); + + if (err) { + LOG_ERR("Mux %s HW configuration failed", dev->name); + return 0; + } + + LOG_DBG("Mux %s initalization complete", dev->name); + return 0; +} + +static const struct i3c_driver_api imx3112_api_funcs = { + .i3c_xfers = i3c_renesas_imx3112_transfer, + .i3c_device_find = i3c_renesas_imx3112_device_find, + .configure = i3c_renesas_imx3112_configure, +}; + +BUILD_ASSERT(CONFIG_I3C_RENESAS_CHANNEL_PRIORITY > CONFIG_I3C_RENESAS_MUX_PRIORITY, + "I3C multiplexer channels must be initialized after their root"); + +#define CHECK_MUX_CONFIG(n) \ + COND_CODE_1(DT_INST_PROP(n, has_external_resistor), \ + (BUILD_ASSERT(DT_INST_ENUM_IDX_OR(n, io_voltage, 0) < 3)), ()) + +/* Select hardware analog parameters based on devicetree config */ +#define MUX_HW_CONFIG(n) \ + { \ + .has_external_resistor = DT_INST_PROP(n, has_external_resistor), \ + .io_voltage = DT_INST_ENUM_IDX_OR(n, io_voltage, 0), \ + .ch_resistance[0].scl = \ + DT_ENUM_IDX_OR(DT_INST_CHILD(n, mux_i2c_0), scl_resistance, 0), \ + .ch_resistance[0].sda = \ + DT_ENUM_IDX_OR(DT_INST_CHILD(n, mux_i2c_0), sda_resistance, 0), \ + .ch_resistance[1].scl = \ + DT_ENUM_IDX_OR(DT_INST_CHILD(n, mux_i2c_1), scl_resistance, 0), \ + .ch_resistance[1].sda = \ + DT_ENUM_IDX_OR(DT_INST_CHILD(n, mux_i2c_1), sda_resistance, 0), \ + } + +#define DT_DRV_COMPAT renesas_imx3112_i3c +#define IMX3112_CHANNEL_INIT(parent_inst, node_id, ch_num) \ + BUILD_ASSERT(DT_REG_ADDR(node_id) < NUM_CHANNELS); \ + static struct i3c_device_desc i3c_renesas_imx3112_dev_arr_##parent_inst##_##ch_num[] = \ + I3C_DEVICE_ARRAY_DT(node_id); \ + static const struct i3c_renesas_imx3112_channel_config \ + imx3112_channel_##parent_inst##_##ch_num##_config = { \ + .channel_mask = BIT(DT_REG_ADDR(node_id)), \ + .mux = DEVICE_DT_GET(DT_PARENT(node_id)), \ + .device_list.i3c = i3c_renesas_imx3112_dev_arr_##parent_inst##_##ch_num, \ + .device_list.num_i3c = \ + ARRAY_SIZE(i3c_renesas_imx3112_dev_arr_##parent_inst##_##ch_num), \ + }; \ + DEVICE_DT_DEFINE(node_id, i3c_renesas_imx3112_channel_initialize, NULL, NULL, \ + &imx3112_channel_##parent_inst##_##ch_num##_config, POST_KERNEL, \ + CONFIG_I3C_RENESAS_CHANNEL_PRIORITY, &imx3112_api_funcs); + +#define I3C_DEVICE_INIT_RENESAS_IMX3112(n) \ + /* Internal resistors only regulate up to the VDD supply */ \ + CHECK_MUX_CONFIG(n) \ + static const struct i3c_renesas_imx3112_mux_config i3c_renesas_imx3112_mux_config_##n = { \ + .bus = DEVICE_DT_GET(DT_INST_BUS(n)), \ + .dev_id = I3C_DEVICE_ID_DT_INST(n), \ + .hw_config = MUX_HW_CONFIG(n), \ + }; \ + static struct i3c_renesas_imx3112_mux_data i3c_renesas_imx3112_mux_data_##n = { \ + .lock = Z_MUTEX_INITIALIZER(i3c_renesas_imx3112_mux_data_##n.lock), \ + .mux_state = {.enabled = false, .channel_mask = 0}, \ + }; \ + DEVICE_DT_INST_DEFINE(n, i3c_renesas_imx3112_mux_initialize, NULL, \ + &i3c_renesas_imx3112_mux_data_##n, \ + &i3c_renesas_imx3112_mux_config_##n, POST_KERNEL, \ + CONFIG_I3C_RENESAS_MUX_PRIORITY, NULL); \ + COND_CODE_1(DT_NODE_HAS_STATUS(DT_INST_CHILD(n, mux_i3c_0), okay), \ + (IMX3112_CHANNEL_INIT(n, DT_INST_CHILD(n, mux_i3c_0), 0)), ()) \ + COND_CODE_1(DT_NODE_HAS_STATUS(DT_INST_CHILD(n, mux_i3c_1), okay), \ + (IMX3112_CHANNEL_INIT(n, DT_INST_CHILD(n, mux_i3c_1), 1)), ()) + +DT_INST_FOREACH_STATUS_OKAY(I3C_DEVICE_INIT_RENESAS_IMX3112) diff --git a/drivers/i3c/i3c_renesas_imx3112_regs.h b/drivers/i3c/i3c_renesas_imx3112_regs.h new file mode 100644 index 000000000000000..31d2b038c7034a5 --- /dev/null +++ b/drivers/i3c/i3c_renesas_imx3112_regs.h @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2022 Google LLC. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef I3C_RENESAS_IMX3112_REGS_H_ +#define I3C_RENESAS_IMX3112_REGS_H_ + +#include +#include + +/* ---------------- */ +/* Register Fields. */ +/* ---------------- */ + +#define RF_DEVICE_TYPE GENMASK(7, 0) + +#define RF_VENDOR_ID GENMASK(7, 0) + +/* Local Interface - IO voltage */ +#define RF_LOCAL_INF_IO_LEVEL GENMASK(4, 2) +/* Local Interface - Pull Up Resistor Configuration */ +#define RF_LOCAL_INF_PULLUP_CONF BIT(5) + +/* LSCL_0 Pull-up Resistor Select */ +#define RF_LSCL_0_PU_RES GENMASK(1, 0) +/* LSDA_0 Pull-up Resistor Select */ +#define RF_LSDA_0_PU_RES GENMASK(3, 2) +/* LSCL_1 Pull-up Resistor Select */ +#define RF_LSCL_1_PU_RES GENMASK(5, 4) +/* LSDA_1 Pull-up Resistor Select */ +#define RF_LSDA_1_PU_RES GENMASK(7, 6) + +/* Burst length for read pointer address for PEC calculations */ +#define RF_DEF_READ_ADDRESS_POINT_BL BIT(1) +/* Default read pointer starting address */ +#define RF_DEF_RD_ADDR_POINT_START GENMASK(3, 2) +/* Default read address pointer enable */ +#define RF_DEF_RD_ADDR_POINT_EN BIT(4) +/* Interface Selection */ +#define RF_INF_SEL BIT(5) +/* Parity (T bit) Disable */ +#define RF_PAR_DIS BIT(6) +/* PEC enable */ +#define RF_PEC_EN BIT(7) + +/* LSDA_0 IO Port 0 Enable */ +#define RF_SCL_SDA_0_EN BIT(6) +/* LSDA_1 IO Port 1 Enable */ +#define RF_SCL_SDA_1_EN BIT(7) + +/* Write/Read Operation Port 0 Selection */ +#define RF_SCL_SDA_0_SEL BIT(6) +/* Write/Read Operation Port 1 Selection */ +#define RF_SCL_SDA_1_SEL BIT(7) + +/* ------------------------------ */ +/* Enums used in register fields. */ +/* ------------------------------ */ + +#define RE_INTERNAL_PULLUP_RESISTOR 0 +#define RE_EXTERNAL_PULLUP_RESISTOR 1 + +#define RE_INF_SEL_I2C 0 +#define RE_INF_SEL_I3C 1 + +/* ----------------- */ +/* Register structs. */ +/* ----------------- */ + +struct i3c_renesas_imx3112_registers { + /* 0x0: (MR0/1) The device type harcoded by renesas */ + uint8_t device_type[2]; + /* 0x2: (MR2) Split between major and minor */ + uint8_t device_revision; + /* 0x3: (MR3/4) Jedec standard renesas code */ + uint8_t vendor_id[2]; + uint8_t __reserved0[9]; + /* 0xe: (MR14) Device configuration for Resistance, Voltage */ + uint8_t local_interface_cfg; + /* 0xf: (MR15) */ + uint8_t pullup_resistor_config; + uint8_t __reserved1[2]; + /* 0x12: (MR18) PEC/parity/protocol selection */ + uint8_t device_cfg; + /* 0x13: (MR19) */ + uint8_t clear_temp_sensor_alarm; + /* 0x14: (MR20) Clear PEC/parity errors */ + uint8_t clear_ecc_error; + uint8_t __reserved2[5]; + /* 0x1a: (MR26) Enable/Disable temp sensor */ + uint8_t temp_sensor_cfg; + /* 0x1b: (MR27) IBI configuration */ + uint8_t interrupt_cfg; + /* 0x1c: (MR28/29) */ + uint8_t temp_hi_limit_cfg[2]; + /* 0x1e: (MR30/31) */ + uint8_t temp_lo_limit_cfg[2]; + /* 0x20: (MR32/33) Must be > temp_hi_limit_cfg */ + uint8_t temp_crit_hi_limit_cfg[2]; + /* 0x22: (MR34/35) Must be < temp_lo_limit_cfg */ + uint8_t temp_crit_lo_limit_cfg[2]; + uint8_t __reserved3[12]; + /* 0x30: (MR48) IBI status */ + uint8_t device_status; + /* 0x31: (MR49/50) (oC) */ + uint8_t current_temperature[2]; + /* 0x33: (MR51) Alarms for temperature thresholds */ + uint8_t temperature_status; + /* 0x34: (MR52) PEC/Parity errors */ + uint8_t error_status; + uint8_t __reserved4[11]; + /* 0x40: (MR64) Enable/Disable SCL/SDA lines for each port */ + uint8_t mux_config; + /* 0x41: (MR65) Select a port for passthrough */ + uint8_t mux_select; + uint8_t __reserved5[62]; +}; +BUILD_ASSERT(sizeof(struct i3c_renesas_imx3112_registers) == 128); + +#endif /* I3C_RENESAS_IMX3112_REGS_H_ */ diff --git a/dts/bindings/i3c/i3c-mux-device.yaml b/dts/bindings/i3c/i3c-mux-device.yaml new file mode 100644 index 000000000000000..1d6fc8a00c3aab1 --- /dev/null +++ b/dts/bindings/i3c/i3c-mux-device.yaml @@ -0,0 +1,49 @@ +# Copyright (c) 2022 Google LLC. +# SPDX-License-Identifier: Apache-2.0 + +description: | + Generic I3C mux + + Each channel is represented by a separate devicetree child node. + The channel node can then be used as a standard i3c bus controller + like in the following simplified example: + + /* The imx3112 node must be a child of an i3c controller */ + mux: imx3112@77 { + compatible = "renesas,imx3112-i3c"; + reg = <0x77 0xabcd 0x02345678>; + status = "okay"; + #address-cells = <1>; + #size-cells = <0>; + + mux_i3c@0 { + compatible: "renesas,imx3112-channel" + reg = <0>; + #address-cells = <3>; + #size-cells = <0>; + lps22hh0: lps22hh@10 { + compatible = "st,lps22hh"; + reg = <0x10 0xabcd 0x12345678>; + }; + }; + + mux_i3c@1 { + compatible: "renesas,imx3112-channel" + reg = <1>; + #address-cells = <3>; + #size-cells = <0>; + lps22hh1: lps22hh@10 { + compatible = "st,lps22hh"; + reg = <0x10 0xabcd 0x12345678>; + }; + }; + }; + +on-bus: i3c + +include: [i3c-device.yaml] + +child-binding: + description: Generic switch channel node + include: [i3c-controller.yaml] + on-bus: i3c diff --git a/dts/bindings/i3c/renesas,imx3112-i3c.yaml b/dts/bindings/i3c/renesas,imx3112-i3c.yaml new file mode 100644 index 000000000000000..17a46ae7579beab --- /dev/null +++ b/dts/bindings/i3c/renesas,imx3112-i3c.yaml @@ -0,0 +1,56 @@ +# Copyright (c) 2022 Google LLC. +# SPDX-License-Identifier: Apache-2.0 + +description: Renesas IMX3112 2:1 Bus multiplexer for I3C/I2C devices + +compatible: "renesas,imx3112-i3c" + +include: [i3c-mux-device.yaml] + +properties: + has-external-resistor: + type: boolean + description: | + Configures LOCAL_INF_PULLUP_CONF in MR14 of IMX3112 spec. + Indicates if the mux has an external pullup resistor (with pre-built resistance specs) + or an internal, configurable, pullup resistor. If has-external-resistor is true, + resistor-values will be ignored. + io-voltage: + type: string + description: | + Configures LOCAL_INF_IO_LEVEL in MR15 of IMX3112 spec. + Indicates the IO voltage level in volts. + If not present in device tree, will use the spec default + enum: + - "1.0" + - "1.1" + - "1.2" + - "1.8" + - "2.5" + - "3.3" + +child-binding: + compatible: "renesas,imx3112-channel" + properties: + scl-resistance: + type: string + description: | + Configures internal pullup resistance values for the SCL line. + If has-external-resistor is true, the value will be ignored. + Represents value in kilo-ohms. + enum: + - "0.5" + - "1.0" + - "2.0" + - "4.0" + sda-resistance: + type: string + description: | + Configures internal pullup resistance values for the SCL line. + If has-external-resistor is true, the value will be ignored. + Represents value in kilo-ohms. + enum: + - "0.5" + - "1.0" + - "2.0" + - "4.0"