drivers: i3c: introduce I3C API for controllers
This introduces the I3C API for I3C controllers. Currently, this supports one controller per bus under Zephyr. Signed-off-by: Daniel Leung <daniel.leung@intel.com>
This commit is contained in:
parent
9cdd49c6e6
commit
ce7058d2f5
15 changed files with 4350 additions and 4 deletions
|
@ -304,6 +304,7 @@
|
||||||
/drivers/i2s/*litex* @mateusz-holenko @kgugala @pgielda
|
/drivers/i2s/*litex* @mateusz-holenko @kgugala @pgielda
|
||||||
/drivers/i2s/i2s_ll_stm32* @avisconti
|
/drivers/i2s/i2s_ll_stm32* @avisconti
|
||||||
/drivers/i2s/*nrfx* @anangl
|
/drivers/i2s/*nrfx* @anangl
|
||||||
|
/drivers/i3c/ @dcpleung
|
||||||
/drivers/ieee802154/ @rlubos @tbursztyka
|
/drivers/ieee802154/ @rlubos @tbursztyka
|
||||||
/drivers/ieee802154/*b91* @andy-liu-telink
|
/drivers/ieee802154/*b91* @andy-liu-telink
|
||||||
/drivers/ieee802154/ieee802154_nrf5* @jciupis
|
/drivers/ieee802154/ieee802154_nrf5* @jciupis
|
||||||
|
@ -535,6 +536,7 @@
|
||||||
/dts/bindings/*/vexriscv* @mateusz-holenko @kgugala @pgielda
|
/dts/bindings/*/vexriscv* @mateusz-holenko @kgugala @pgielda
|
||||||
/dts/bindings/*/andes* @cwshu @kevinwang821020 @jimmyzhe
|
/dts/bindings/*/andes* @cwshu @kevinwang821020 @jimmyzhe
|
||||||
/dts/bindings/*/neorv32* @henrikbrixandersen
|
/dts/bindings/*/neorv32* @henrikbrixandersen
|
||||||
|
/dts/bindings/i3c/ @dcpleung
|
||||||
/dts/bindings/pm_cpu_ops/* @carlocaione
|
/dts/bindings/pm_cpu_ops/* @carlocaione
|
||||||
/dts/bindings/ethernet/*gem.yaml @ibirnbaum
|
/dts/bindings/ethernet/*gem.yaml @ibirnbaum
|
||||||
/dts/posix/ @aescolar @daor-oti
|
/dts/posix/ @aescolar @daor-oti
|
||||||
|
@ -553,6 +555,8 @@
|
||||||
/include/zephyr/drivers/bluetooth/ @alwa-nordic @jhedberg @Vudentz
|
/include/zephyr/drivers/bluetooth/ @alwa-nordic @jhedberg @Vudentz
|
||||||
/include/zephyr/drivers/flash.h @nashif @carlescufi @galak @MaureenHelm @nvlsianpu
|
/include/zephyr/drivers/flash.h @nashif @carlescufi @galak @MaureenHelm @nvlsianpu
|
||||||
/include/zephyr/drivers/i2c_emul.h @sjg20
|
/include/zephyr/drivers/i2c_emul.h @sjg20
|
||||||
|
/include/zephyr/drivers/i3c.h @dcpleung
|
||||||
|
/include/zephyr/drivers/i3c/ @dcpleung
|
||||||
/include/zephyr/drivers/led/ht16k33.h @henrikbrixandersen
|
/include/zephyr/drivers/led/ht16k33.h @henrikbrixandersen
|
||||||
/include/zephyr/drivers/interrupt_controller/ @dcpleung @nashif
|
/include/zephyr/drivers/interrupt_controller/ @dcpleung @nashif
|
||||||
/include/zephyr/drivers/interrupt_controller/gic.h @stephanosio
|
/include/zephyr/drivers/interrupt_controller/gic.h @stephanosio
|
||||||
|
|
|
@ -768,6 +768,18 @@ Documentation:
|
||||||
labels:
|
labels:
|
||||||
- "area: I2S"
|
- "area: I2S"
|
||||||
|
|
||||||
|
"Drivers: I3C":
|
||||||
|
status: maintained
|
||||||
|
maintainers:
|
||||||
|
- dcpleung
|
||||||
|
files:
|
||||||
|
- drivers/i3c/
|
||||||
|
- dts/bindings/i3c/
|
||||||
|
- include/zephyr/drivers/i3c.h
|
||||||
|
- include/zephyr/drivers/i3c/
|
||||||
|
labels:
|
||||||
|
- "area: I3C"
|
||||||
|
|
||||||
"Drivers: IEEE 802.15.4":
|
"Drivers: IEEE 802.15.4":
|
||||||
status: maintained
|
status: maintained
|
||||||
maintainers:
|
maintainers:
|
||||||
|
|
|
@ -20,6 +20,7 @@ add_subdirectory_ifdef(CONFIG_DMA dma)
|
||||||
add_subdirectory_ifdef(CONFIG_GPIO gpio)
|
add_subdirectory_ifdef(CONFIG_GPIO gpio)
|
||||||
add_subdirectory_ifdef(CONFIG_EC_HOST_CMD_PERIPH ec_host_cmd_periph)
|
add_subdirectory_ifdef(CONFIG_EC_HOST_CMD_PERIPH ec_host_cmd_periph)
|
||||||
add_subdirectory_ifdef(CONFIG_I2C i2c)
|
add_subdirectory_ifdef(CONFIG_I2C i2c)
|
||||||
|
add_subdirectory_ifdef(CONFIG_I3C i3c)
|
||||||
add_subdirectory_ifdef(CONFIG_I2S i2s)
|
add_subdirectory_ifdef(CONFIG_I2S i2s)
|
||||||
add_subdirectory_ifdef(CONFIG_MDIO mdio)
|
add_subdirectory_ifdef(CONFIG_MDIO mdio)
|
||||||
add_subdirectory_ifdef(CONFIG_IEEE802154 ieee802154)
|
add_subdirectory_ifdef(CONFIG_IEEE802154 ieee802154)
|
||||||
|
|
|
@ -43,6 +43,8 @@ source "drivers/sdhc/Kconfig"
|
||||||
|
|
||||||
source "drivers/i2c/Kconfig"
|
source "drivers/i2c/Kconfig"
|
||||||
|
|
||||||
|
source "drivers/i3c/Kconfig"
|
||||||
|
|
||||||
source "drivers/i2s/Kconfig"
|
source "drivers/i2s/Kconfig"
|
||||||
|
|
||||||
source "drivers/dai/Kconfig"
|
source "drivers/dai/Kconfig"
|
||||||
|
|
15
drivers/i3c/CMakeLists.txt
Normal file
15
drivers/i3c/CMakeLists.txt
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
# Copyright (c) 2022 Intel Corporation
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
zephyr_library()
|
||||||
|
|
||||||
|
zephyr_library_sources(
|
||||||
|
i3c_ccc.c
|
||||||
|
i3c_common.c
|
||||||
|
)
|
||||||
|
|
||||||
|
zephyr_library_sources_ifdef(
|
||||||
|
CONFIG_USERSPACE
|
||||||
|
i3c_handlers.c
|
||||||
|
)
|
64
drivers/i3c/Kconfig
Normal file
64
drivers/i3c/Kconfig
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
# I3C configuration options
|
||||||
|
#
|
||||||
|
# Copyright (c) 2022 Intel Corporation
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
menuconfig I3C
|
||||||
|
bool "I3C Drivers"
|
||||||
|
help
|
||||||
|
Enable I3C Driver Configuration
|
||||||
|
|
||||||
|
if I3C
|
||||||
|
|
||||||
|
module = I3C
|
||||||
|
module-str = i3c
|
||||||
|
source "subsys/logging/Kconfig.template.log_config"
|
||||||
|
|
||||||
|
config I3C_USE_GROUP_ADDR
|
||||||
|
bool "Use Group Addresses"
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
Enable this to use group addresses if supported
|
||||||
|
by the controllers and target devices.
|
||||||
|
|
||||||
|
Says Y if unsure.
|
||||||
|
|
||||||
|
menuconfig I3C_USE_IBI
|
||||||
|
bool "Use In-Band Interrupt (IBI)"
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
Enable this to use In-Band Interrupt (IBI).
|
||||||
|
|
||||||
|
Says Y if unsure.
|
||||||
|
|
||||||
|
if I3C_USE_IBI
|
||||||
|
|
||||||
|
config I3C_IBI_MAX_PAYLOAD_SIZE
|
||||||
|
int "Maximum IBI Payload Size"
|
||||||
|
default 16
|
||||||
|
help
|
||||||
|
Maxmium IBI payload size.
|
||||||
|
|
||||||
|
endif # I3C_USE_IBI
|
||||||
|
|
||||||
|
comment "Initialization Priority"
|
||||||
|
|
||||||
|
config I3C_CONTROLLER_INIT_PRIORITY
|
||||||
|
int "I3C Controller Init Priority"
|
||||||
|
# Default is just after CONFIG_KERNEL_INIT_PRIORITY_DEVICE
|
||||||
|
default 50
|
||||||
|
help
|
||||||
|
This is for setting up I3C controller device driver instance
|
||||||
|
and also to perform bus initialization (e.g. dynamic address
|
||||||
|
assignment).
|
||||||
|
|
||||||
|
Note that this needs to be done before the device driver
|
||||||
|
instances of the connected I2C and I3C devices start
|
||||||
|
initializing those devices. This is because some devices
|
||||||
|
may not be addressable until addresses are assigned by
|
||||||
|
the controller.
|
||||||
|
|
||||||
|
comment "Device Drivers"
|
||||||
|
|
||||||
|
endif # I3C
|
446
drivers/i3c/i3c_ccc.c
Normal file
446
drivers/i3c/i3c_ccc.c
Normal file
|
@ -0,0 +1,446 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 Intel Corporation
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <zephyr/toolchain.h>
|
||||||
|
#include <zephyr/sys/__assert.h>
|
||||||
|
|
||||||
|
#include <zephyr/drivers/i3c.h>
|
||||||
|
|
||||||
|
#include <zephyr/logging/log.h>
|
||||||
|
LOG_MODULE_DECLARE(i3c, CONFIG_I3C_LOG_LEVEL);
|
||||||
|
|
||||||
|
int i3c_ccc_do_getbcr(struct i3c_device_desc *target,
|
||||||
|
struct i3c_ccc_getbcr *bcr)
|
||||||
|
{
|
||||||
|
struct i3c_ccc_payload ccc_payload;
|
||||||
|
struct i3c_ccc_target_payload ccc_tgt_payload;
|
||||||
|
|
||||||
|
__ASSERT_NO_MSG(target != NULL);
|
||||||
|
__ASSERT_NO_MSG(target->bus != NULL);
|
||||||
|
__ASSERT_NO_MSG(bcr != NULL);
|
||||||
|
|
||||||
|
ccc_tgt_payload.addr = target->dynamic_addr;
|
||||||
|
ccc_tgt_payload.rnw = 1;
|
||||||
|
ccc_tgt_payload.data = &bcr->bcr;
|
||||||
|
ccc_tgt_payload.data_len = sizeof(bcr->bcr);
|
||||||
|
|
||||||
|
memset(&ccc_payload, 0, sizeof(ccc_payload));
|
||||||
|
ccc_payload.ccc.id = I3C_CCC_GETBCR;
|
||||||
|
ccc_payload.targets.payloads = &ccc_tgt_payload;
|
||||||
|
ccc_payload.targets.num_targets = 1;
|
||||||
|
|
||||||
|
return i3c_do_ccc(target->bus, &ccc_payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
int i3c_ccc_do_getdcr(struct i3c_device_desc *target,
|
||||||
|
struct i3c_ccc_getdcr *dcr)
|
||||||
|
{
|
||||||
|
struct i3c_ccc_payload ccc_payload;
|
||||||
|
struct i3c_ccc_target_payload ccc_tgt_payload;
|
||||||
|
|
||||||
|
__ASSERT_NO_MSG(target != NULL);
|
||||||
|
__ASSERT_NO_MSG(target->bus != NULL);
|
||||||
|
__ASSERT_NO_MSG(dcr != NULL);
|
||||||
|
|
||||||
|
ccc_tgt_payload.addr = target->dynamic_addr;
|
||||||
|
ccc_tgt_payload.rnw = 1;
|
||||||
|
ccc_tgt_payload.data = &dcr->dcr;
|
||||||
|
ccc_tgt_payload.data_len = sizeof(dcr->dcr);
|
||||||
|
|
||||||
|
memset(&ccc_payload, 0, sizeof(ccc_payload));
|
||||||
|
ccc_payload.ccc.id = I3C_CCC_GETDCR;
|
||||||
|
ccc_payload.targets.payloads = &ccc_tgt_payload;
|
||||||
|
ccc_payload.targets.num_targets = 1;
|
||||||
|
|
||||||
|
return i3c_do_ccc(target->bus, &ccc_payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
int i3c_ccc_do_getpid(struct i3c_device_desc *target,
|
||||||
|
struct i3c_ccc_getpid *pid)
|
||||||
|
{
|
||||||
|
struct i3c_ccc_payload ccc_payload;
|
||||||
|
struct i3c_ccc_target_payload ccc_tgt_payload;
|
||||||
|
|
||||||
|
__ASSERT_NO_MSG(target != NULL);
|
||||||
|
__ASSERT_NO_MSG(target->bus != NULL);
|
||||||
|
__ASSERT_NO_MSG(pid != NULL);
|
||||||
|
|
||||||
|
ccc_tgt_payload.addr = target->dynamic_addr;
|
||||||
|
ccc_tgt_payload.rnw = 1;
|
||||||
|
ccc_tgt_payload.data = &pid->pid[0];
|
||||||
|
ccc_tgt_payload.data_len = sizeof(pid->pid);
|
||||||
|
|
||||||
|
memset(&ccc_payload, 0, sizeof(ccc_payload));
|
||||||
|
ccc_payload.ccc.id = I3C_CCC_GETPID;
|
||||||
|
ccc_payload.targets.payloads = &ccc_tgt_payload;
|
||||||
|
ccc_payload.targets.num_targets = 1;
|
||||||
|
|
||||||
|
return i3c_do_ccc(target->bus, &ccc_payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
int i3c_ccc_do_rstact_all(const struct device *controller,
|
||||||
|
enum i3c_ccc_rstact_defining_byte action)
|
||||||
|
{
|
||||||
|
struct i3c_ccc_payload ccc_payload;
|
||||||
|
uint8_t def_byte;
|
||||||
|
|
||||||
|
__ASSERT_NO_MSG(controller != NULL);
|
||||||
|
|
||||||
|
memset(&ccc_payload, 0, sizeof(ccc_payload));
|
||||||
|
ccc_payload.ccc.id = I3C_CCC_RSTACT(true);
|
||||||
|
|
||||||
|
def_byte = (uint8_t)action;
|
||||||
|
ccc_payload.ccc.data = &def_byte;
|
||||||
|
ccc_payload.ccc.data_len = 1U;
|
||||||
|
|
||||||
|
return i3c_do_ccc(controller, &ccc_payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
int i3c_ccc_do_rstdaa_all(const struct device *controller)
|
||||||
|
{
|
||||||
|
struct i3c_ccc_payload ccc_payload;
|
||||||
|
|
||||||
|
__ASSERT_NO_MSG(controller != NULL);
|
||||||
|
|
||||||
|
memset(&ccc_payload, 0, sizeof(ccc_payload));
|
||||||
|
ccc_payload.ccc.id = I3C_CCC_RSTDAA;
|
||||||
|
|
||||||
|
return i3c_do_ccc(controller, &ccc_payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
int i3c_ccc_do_setdasa(const struct i3c_device_desc *target)
|
||||||
|
{
|
||||||
|
struct i3c_ccc_payload ccc_payload;
|
||||||
|
struct i3c_ccc_target_payload ccc_tgt_payload;
|
||||||
|
uint8_t dyn_addr;
|
||||||
|
|
||||||
|
__ASSERT_NO_MSG(target != NULL);
|
||||||
|
__ASSERT_NO_MSG(target->bus != NULL);
|
||||||
|
|
||||||
|
if ((target->static_addr == 0U) || (target->dynamic_addr != 0U)) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Note that the 7-bit address needs to start at bit 1
|
||||||
|
* (aka left-justified). So shift left by 1;
|
||||||
|
*/
|
||||||
|
dyn_addr = target->static_addr << 1;
|
||||||
|
|
||||||
|
ccc_tgt_payload.addr = target->static_addr;
|
||||||
|
ccc_tgt_payload.rnw = 0;
|
||||||
|
ccc_tgt_payload.data = &dyn_addr;
|
||||||
|
ccc_tgt_payload.data_len = 1;
|
||||||
|
|
||||||
|
memset(&ccc_payload, 0, sizeof(ccc_payload));
|
||||||
|
ccc_payload.ccc.id = I3C_CCC_SETDASA;
|
||||||
|
ccc_payload.targets.payloads = &ccc_tgt_payload;
|
||||||
|
ccc_payload.targets.num_targets = 1;
|
||||||
|
|
||||||
|
return i3c_do_ccc(target->bus, &ccc_payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
int i3c_ccc_do_events_all_set(const struct device *controller,
|
||||||
|
bool enable, struct i3c_ccc_events *events)
|
||||||
|
{
|
||||||
|
struct i3c_ccc_payload ccc_payload;
|
||||||
|
|
||||||
|
__ASSERT_NO_MSG(controller != NULL);
|
||||||
|
|
||||||
|
memset(&ccc_payload, 0, sizeof(ccc_payload));
|
||||||
|
|
||||||
|
ccc_payload.ccc.id = enable ? I3C_CCC_ENEC(true) : I3C_CCC_DISEC(true);
|
||||||
|
|
||||||
|
ccc_payload.ccc.data = &events->events;
|
||||||
|
ccc_payload.ccc.data_len = sizeof(events->events);
|
||||||
|
|
||||||
|
return i3c_do_ccc(controller, &ccc_payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
int i3c_ccc_do_events_set(struct i3c_device_desc *target,
|
||||||
|
bool enable, struct i3c_ccc_events *events)
|
||||||
|
{
|
||||||
|
struct i3c_ccc_payload ccc_payload;
|
||||||
|
struct i3c_ccc_target_payload ccc_tgt_payload;
|
||||||
|
|
||||||
|
__ASSERT_NO_MSG(target != NULL);
|
||||||
|
__ASSERT_NO_MSG(target->bus != NULL);
|
||||||
|
|
||||||
|
if (target->dynamic_addr == 0U) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ccc_tgt_payload.addr = target->dynamic_addr;
|
||||||
|
ccc_tgt_payload.rnw = 0;
|
||||||
|
ccc_tgt_payload.data = &events->events;
|
||||||
|
ccc_tgt_payload.data_len = sizeof(events->events);
|
||||||
|
|
||||||
|
memset(&ccc_payload, 0, sizeof(ccc_payload));
|
||||||
|
ccc_payload.ccc.id = enable ? I3C_CCC_ENEC(false) : I3C_CCC_DISEC(false);
|
||||||
|
ccc_payload.targets.payloads = &ccc_tgt_payload;
|
||||||
|
ccc_payload.targets.num_targets = 1;
|
||||||
|
|
||||||
|
return i3c_do_ccc(target->bus, &ccc_payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
int i3c_ccc_do_setmwl_all(const struct device *controller,
|
||||||
|
const struct i3c_ccc_mwl *mwl)
|
||||||
|
{
|
||||||
|
struct i3c_ccc_payload ccc_payload;
|
||||||
|
uint8_t data[2];
|
||||||
|
|
||||||
|
__ASSERT_NO_MSG(controller != NULL);
|
||||||
|
|
||||||
|
memset(&ccc_payload, 0, sizeof(ccc_payload));
|
||||||
|
|
||||||
|
ccc_payload.ccc.id = I3C_CCC_SETMWL(true);
|
||||||
|
|
||||||
|
ccc_payload.ccc.data = &data[0];
|
||||||
|
ccc_payload.ccc.data_len = sizeof(data);
|
||||||
|
|
||||||
|
/* The actual data is MSB first. So order the data. */
|
||||||
|
data[0] = (uint8_t)((mwl->len & 0xFF00U) >> 8);
|
||||||
|
data[1] = (uint8_t)(mwl->len & 0xFFU);
|
||||||
|
|
||||||
|
return i3c_do_ccc(controller, &ccc_payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
int i3c_ccc_do_setmwl(const struct i3c_device_desc *target,
|
||||||
|
const struct i3c_ccc_mwl *mwl)
|
||||||
|
{
|
||||||
|
struct i3c_ccc_payload ccc_payload;
|
||||||
|
struct i3c_ccc_target_payload ccc_tgt_payload;
|
||||||
|
uint8_t data[2];
|
||||||
|
|
||||||
|
__ASSERT_NO_MSG(target != NULL);
|
||||||
|
__ASSERT_NO_MSG(target->bus != NULL);
|
||||||
|
|
||||||
|
memset(&ccc_payload, 0, sizeof(ccc_payload));
|
||||||
|
|
||||||
|
ccc_tgt_payload.addr = target->static_addr;
|
||||||
|
ccc_tgt_payload.rnw = 0;
|
||||||
|
ccc_tgt_payload.data = &data[0];
|
||||||
|
ccc_tgt_payload.data_len = sizeof(data);
|
||||||
|
|
||||||
|
ccc_payload.ccc.id = I3C_CCC_SETMWL(false);
|
||||||
|
ccc_payload.targets.payloads = &ccc_tgt_payload;
|
||||||
|
ccc_payload.targets.num_targets = 1;
|
||||||
|
|
||||||
|
/* The actual length is MSB first. So order the data. */
|
||||||
|
data[0] = (uint8_t)((mwl->len & 0xFF00U) >> 8);
|
||||||
|
data[1] = (uint8_t)(mwl->len & 0xFFU);
|
||||||
|
|
||||||
|
return i3c_do_ccc(target->bus, &ccc_payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
int i3c_ccc_do_getmwl(const struct i3c_device_desc *target,
|
||||||
|
struct i3c_ccc_mwl *mwl)
|
||||||
|
{
|
||||||
|
struct i3c_ccc_payload ccc_payload;
|
||||||
|
struct i3c_ccc_target_payload ccc_tgt_payload;
|
||||||
|
uint8_t data[2];
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
__ASSERT_NO_MSG(target != NULL);
|
||||||
|
__ASSERT_NO_MSG(target->bus != NULL);
|
||||||
|
__ASSERT_NO_MSG(mwl != NULL);
|
||||||
|
|
||||||
|
ccc_tgt_payload.addr = target->dynamic_addr;
|
||||||
|
ccc_tgt_payload.rnw = 1;
|
||||||
|
ccc_tgt_payload.data = &data[0];
|
||||||
|
ccc_tgt_payload.data_len = sizeof(data);
|
||||||
|
|
||||||
|
memset(&ccc_payload, 0, sizeof(ccc_payload));
|
||||||
|
ccc_payload.ccc.id = I3C_CCC_GETMWL;
|
||||||
|
ccc_payload.targets.payloads = &ccc_tgt_payload;
|
||||||
|
ccc_payload.targets.num_targets = 1;
|
||||||
|
|
||||||
|
ret = i3c_do_ccc(target->bus, &ccc_payload);
|
||||||
|
|
||||||
|
if (ret == 0) {
|
||||||
|
/* The actual length is MSB first. So order the data. */
|
||||||
|
mwl->len = (data[0] << 8) | data[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int i3c_ccc_do_setmrl_all(const struct device *controller,
|
||||||
|
const struct i3c_ccc_mrl *mrl,
|
||||||
|
bool has_ibi_size)
|
||||||
|
{
|
||||||
|
struct i3c_ccc_payload ccc_payload;
|
||||||
|
uint8_t data[3];
|
||||||
|
|
||||||
|
__ASSERT_NO_MSG(controller != NULL);
|
||||||
|
|
||||||
|
memset(&ccc_payload, 0, sizeof(ccc_payload));
|
||||||
|
|
||||||
|
ccc_payload.ccc.id = I3C_CCC_SETMRL(true);
|
||||||
|
|
||||||
|
ccc_payload.ccc.data = &data[0];
|
||||||
|
ccc_payload.ccc.data_len = has_ibi_size ? 3 : 2;
|
||||||
|
|
||||||
|
/* The actual length is MSB first. So order the data. */
|
||||||
|
data[0] = (uint8_t)((mrl->len & 0xFF00U) >> 8);
|
||||||
|
data[1] = (uint8_t)(mrl->len & 0xFFU);
|
||||||
|
|
||||||
|
if (has_ibi_size) {
|
||||||
|
data[2] = mrl->ibi_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
return i3c_do_ccc(controller, &ccc_payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
int i3c_ccc_do_setmrl(const struct i3c_device_desc *target,
|
||||||
|
const struct i3c_ccc_mrl *mrl)
|
||||||
|
{
|
||||||
|
struct i3c_ccc_payload ccc_payload;
|
||||||
|
struct i3c_ccc_target_payload ccc_tgt_payload;
|
||||||
|
uint8_t data[3];
|
||||||
|
|
||||||
|
__ASSERT_NO_MSG(target != NULL);
|
||||||
|
__ASSERT_NO_MSG(target->bus != NULL);
|
||||||
|
|
||||||
|
memset(&ccc_payload, 0, sizeof(ccc_payload));
|
||||||
|
|
||||||
|
ccc_tgt_payload.addr = target->static_addr;
|
||||||
|
ccc_tgt_payload.rnw = 0;
|
||||||
|
ccc_tgt_payload.data = &data[0];
|
||||||
|
|
||||||
|
ccc_payload.ccc.id = I3C_CCC_SETMRL(false);
|
||||||
|
ccc_payload.targets.payloads = &ccc_tgt_payload;
|
||||||
|
ccc_payload.targets.num_targets = 1;
|
||||||
|
|
||||||
|
/* The actual length is MSB first. So order the data. */
|
||||||
|
data[0] = (uint8_t)((mrl->len & 0xFF00U) >> 8);
|
||||||
|
data[1] = (uint8_t)(mrl->len & 0xFFU);
|
||||||
|
|
||||||
|
if ((target->bcr & I3C_BCR_IBI_PAYLOAD_HAS_DATA_BYTE)
|
||||||
|
== I3C_BCR_IBI_PAYLOAD_HAS_DATA_BYTE) {
|
||||||
|
ccc_tgt_payload.data_len = 3;
|
||||||
|
|
||||||
|
data[2] = mrl->ibi_len;
|
||||||
|
} else {
|
||||||
|
ccc_tgt_payload.data_len = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return i3c_do_ccc(target->bus, &ccc_payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
int i3c_ccc_do_getmrl(const struct i3c_device_desc *target,
|
||||||
|
struct i3c_ccc_mrl *mrl)
|
||||||
|
{
|
||||||
|
struct i3c_ccc_payload ccc_payload;
|
||||||
|
struct i3c_ccc_target_payload ccc_tgt_payload;
|
||||||
|
uint8_t data[3];
|
||||||
|
bool has_ibi_sz;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
__ASSERT_NO_MSG(target != NULL);
|
||||||
|
__ASSERT_NO_MSG(target->bus != NULL);
|
||||||
|
__ASSERT_NO_MSG(mrl != NULL);
|
||||||
|
|
||||||
|
has_ibi_sz = (target->bcr & I3C_BCR_IBI_PAYLOAD_HAS_DATA_BYTE)
|
||||||
|
== I3C_BCR_IBI_PAYLOAD_HAS_DATA_BYTE;
|
||||||
|
|
||||||
|
ccc_tgt_payload.addr = target->dynamic_addr;
|
||||||
|
ccc_tgt_payload.rnw = 1;
|
||||||
|
ccc_tgt_payload.data = &data[0];
|
||||||
|
ccc_tgt_payload.data_len = has_ibi_sz ? 3 : 2;
|
||||||
|
|
||||||
|
memset(&ccc_payload, 0, sizeof(ccc_payload));
|
||||||
|
ccc_payload.ccc.id = I3C_CCC_GETMRL;
|
||||||
|
ccc_payload.targets.payloads = &ccc_tgt_payload;
|
||||||
|
ccc_payload.targets.num_targets = 1;
|
||||||
|
|
||||||
|
ret = i3c_do_ccc(target->bus, &ccc_payload);
|
||||||
|
|
||||||
|
if (ret == 0) {
|
||||||
|
/* The actual length is MSB first. So order the data. */
|
||||||
|
mrl->len = (data[0] << 8) | data[1];
|
||||||
|
|
||||||
|
if (has_ibi_sz) {
|
||||||
|
mrl->ibi_len = data[2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int i3c_ccc_do_getstatus(const struct i3c_device_desc *target,
|
||||||
|
union i3c_ccc_getstatus *status,
|
||||||
|
enum i3c_ccc_getstatus_fmt fmt,
|
||||||
|
enum i3c_ccc_getstatus_defbyte defbyte)
|
||||||
|
{
|
||||||
|
struct i3c_ccc_payload ccc_payload;
|
||||||
|
struct i3c_ccc_target_payload ccc_tgt_payload;
|
||||||
|
uint8_t defining_byte;
|
||||||
|
uint8_t data[2];
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
__ASSERT_NO_MSG(target != NULL);
|
||||||
|
__ASSERT_NO_MSG(target->bus != NULL);
|
||||||
|
__ASSERT_NO_MSG(status != NULL);
|
||||||
|
|
||||||
|
ccc_tgt_payload.addr = target->dynamic_addr;
|
||||||
|
ccc_tgt_payload.rnw = 1;
|
||||||
|
ccc_tgt_payload.data = &data[0];
|
||||||
|
|
||||||
|
if (fmt == GETSTATUS_FORMAT_1) {
|
||||||
|
ccc_tgt_payload.data_len = 2;
|
||||||
|
} else if (fmt == GETSTATUS_FORMAT_2) {
|
||||||
|
switch (defbyte) {
|
||||||
|
case GETSTATUS_FORMAT_2_TGTSTAT:
|
||||||
|
__fallthrough;
|
||||||
|
case GETSTATUS_FORMAT_2_PRECR:
|
||||||
|
ccc_tgt_payload.data_len = 2;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&ccc_payload, 0, sizeof(ccc_payload));
|
||||||
|
ccc_payload.ccc.id = I3C_CCC_GETSTATUS;
|
||||||
|
ccc_payload.targets.payloads = &ccc_tgt_payload;
|
||||||
|
ccc_payload.targets.num_targets = 1;
|
||||||
|
|
||||||
|
if (fmt == GETSTATUS_FORMAT_2) {
|
||||||
|
defining_byte = (uint8_t)defbyte;
|
||||||
|
|
||||||
|
ccc_payload.ccc.data = &defining_byte;
|
||||||
|
ccc_payload.ccc.data_len = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = i3c_do_ccc(target->bus, &ccc_payload);
|
||||||
|
|
||||||
|
if (ret == 0) {
|
||||||
|
/* Received data is MSB first. So order the data. */
|
||||||
|
if (fmt == GETSTATUS_FORMAT_1) {
|
||||||
|
status->fmt1.status = (data[0] << 8) | data[1];
|
||||||
|
} else if (fmt == GETSTATUS_FORMAT_2) {
|
||||||
|
switch (defbyte) {
|
||||||
|
case GETSTATUS_FORMAT_2_TGTSTAT:
|
||||||
|
__fallthrough;
|
||||||
|
case GETSTATUS_FORMAT_2_PRECR:
|
||||||
|
status->fmt2.raw_u16 = (data[0] << 8) | data[1];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
return ret;
|
||||||
|
}
|
544
drivers/i3c/i3c_common.c
Normal file
544
drivers/i3c/i3c_common.c
Normal file
|
@ -0,0 +1,544 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 Intel Corporation
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <zephyr/toolchain.h>
|
||||||
|
#include <zephyr/sys/__assert.h>
|
||||||
|
#include <zephyr/sys/slist.h>
|
||||||
|
|
||||||
|
#include <zephyr/drivers/i3c.h>
|
||||||
|
|
||||||
|
#include <zephyr/logging/log.h>
|
||||||
|
LOG_MODULE_REGISTER(i3c, CONFIG_I3C_LOG_LEVEL);
|
||||||
|
|
||||||
|
void i3c_dump_msgs(const char *name, const struct i3c_msg *msgs,
|
||||||
|
uint8_t num_msgs, struct i3c_device_desc *target)
|
||||||
|
{
|
||||||
|
LOG_DBG("I3C msg: %s, addr=%x", name, target->dynamic_addr);
|
||||||
|
for (unsigned int i = 0; i < num_msgs; i++) {
|
||||||
|
const struct i3c_msg *msg = &msgs[i];
|
||||||
|
|
||||||
|
LOG_DBG(" %c len=%02x: ",
|
||||||
|
msg->flags & I3C_MSG_READ ? 'R' : 'W', msg->len);
|
||||||
|
if (!(msg->flags & I3C_MSG_READ)) {
|
||||||
|
LOG_HEXDUMP_DBG(msg->buf, msg->len, "contents:");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void i3c_addr_slots_set(struct i3c_addr_slots *slots,
|
||||||
|
uint8_t dev_addr,
|
||||||
|
enum i3c_addr_slot_status status)
|
||||||
|
{
|
||||||
|
int bitpos;
|
||||||
|
int idx;
|
||||||
|
|
||||||
|
__ASSERT_NO_MSG(slots != NULL);
|
||||||
|
|
||||||
|
if (dev_addr > I3C_MAX_ADDR) {
|
||||||
|
/* Invalid address. Do nothing. */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bitpos = dev_addr * 2;
|
||||||
|
idx = bitpos / BITS_PER_LONG;
|
||||||
|
|
||||||
|
slots->slots[idx] &= ~((unsigned long)I3C_ADDR_SLOT_STATUS_MASK <<
|
||||||
|
(bitpos % BITS_PER_LONG));
|
||||||
|
slots->slots[idx] |= status << (bitpos % BITS_PER_LONG);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum i3c_addr_slot_status
|
||||||
|
i3c_addr_slots_status(struct i3c_addr_slots *slots,
|
||||||
|
uint8_t dev_addr)
|
||||||
|
{
|
||||||
|
unsigned long status;
|
||||||
|
int bitpos;
|
||||||
|
int idx;
|
||||||
|
|
||||||
|
__ASSERT_NO_MSG(slots != NULL);
|
||||||
|
|
||||||
|
if (dev_addr > I3C_MAX_ADDR) {
|
||||||
|
/* Invalid address.
|
||||||
|
* Simply says it's reserved so it will not be
|
||||||
|
* used for anything.
|
||||||
|
*/
|
||||||
|
return I3C_ADDR_SLOT_STATUS_RSVD;
|
||||||
|
}
|
||||||
|
|
||||||
|
bitpos = dev_addr * 2;
|
||||||
|
idx = bitpos / BITS_PER_LONG;
|
||||||
|
|
||||||
|
status = slots->slots[idx] >> (bitpos % BITS_PER_LONG);
|
||||||
|
status &= I3C_ADDR_SLOT_STATUS_MASK;
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int i3c_addr_slots_init(struct i3c_addr_slots *slots,
|
||||||
|
const struct i3c_dev_list *dev_list)
|
||||||
|
{
|
||||||
|
int i, ret = 0;
|
||||||
|
struct i3c_device_desc *i3c_dev;
|
||||||
|
struct i3c_i2c_device_desc *i2c_dev;
|
||||||
|
|
||||||
|
__ASSERT_NO_MSG(slots != NULL);
|
||||||
|
|
||||||
|
(void)memset(slots, 0, sizeof(*slots));
|
||||||
|
|
||||||
|
for (i = 0; i <= 7; i++) {
|
||||||
|
/* Addresses 0 to 7 are reserved */
|
||||||
|
i3c_addr_slots_set(slots, i, I3C_ADDR_SLOT_STATUS_RSVD);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Addresses within a single bit error of broadcast address
|
||||||
|
* are also reserved.
|
||||||
|
*/
|
||||||
|
i3c_addr_slots_set(slots, I3C_BROADCAST_ADDR ^ BIT(i),
|
||||||
|
I3C_ADDR_SLOT_STATUS_RSVD);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The broadcast address is reserved */
|
||||||
|
i3c_addr_slots_set(slots, I3C_BROADCAST_ADDR,
|
||||||
|
I3C_ADDR_SLOT_STATUS_RSVD);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If there is a static address for the I3C devices, check
|
||||||
|
* if this address is free, and there is no other devices of
|
||||||
|
* the same (pre-assigned) address on the bus.
|
||||||
|
*/
|
||||||
|
for (i = 0; i < dev_list->num_i3c; i++) {
|
||||||
|
i3c_dev = &dev_list->i3c[i];
|
||||||
|
if (i3c_dev->static_addr != 0U) {
|
||||||
|
if (i3c_addr_slots_is_free(slots, i3c_dev->static_addr)) {
|
||||||
|
/*
|
||||||
|
* Mark address slot as I3C device for now to
|
||||||
|
* detect address collisons. This marking may be
|
||||||
|
* released during address assignment.
|
||||||
|
*/
|
||||||
|
i3c_addr_slots_mark_i3c(slots, i3c_dev->static_addr);
|
||||||
|
} else {
|
||||||
|
/* Address slot is not free */
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Mark all I2C addresses.
|
||||||
|
*/
|
||||||
|
for (i = 0; i < dev_list->num_i2c; i++) {
|
||||||
|
i2c_dev = &dev_list->i2c[i];
|
||||||
|
if (i3c_addr_slots_is_free(slots, i2c_dev->addr)) {
|
||||||
|
i3c_addr_slots_mark_i2c(slots, i2c_dev->addr);
|
||||||
|
} else {
|
||||||
|
/* Address slot is not free */
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool i3c_addr_slots_is_free(struct i3c_addr_slots *slots,
|
||||||
|
uint8_t dev_addr)
|
||||||
|
{
|
||||||
|
enum i3c_addr_slot_status status;
|
||||||
|
|
||||||
|
__ASSERT_NO_MSG(slots != NULL);
|
||||||
|
|
||||||
|
status = i3c_addr_slots_status(slots, dev_addr);
|
||||||
|
|
||||||
|
return (status == I3C_ADDR_SLOT_STATUS_FREE);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t i3c_addr_slots_next_free_find(struct i3c_addr_slots *slots)
|
||||||
|
{
|
||||||
|
uint8_t addr;
|
||||||
|
enum i3c_addr_slot_status status;
|
||||||
|
|
||||||
|
/* Addresses 0 to 7 are reserved. So start at 8. */
|
||||||
|
for (addr = 8; addr < I3C_MAX_ADDR; addr++) {
|
||||||
|
status = i3c_addr_slots_status(slots, addr);
|
||||||
|
if (status == I3C_ADDR_SLOT_STATUS_FREE) {
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct i3c_device_desc *i3c_dev_list_find(const struct i3c_dev_list *dev_list,
|
||||||
|
const struct i3c_device_id *id)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
struct i3c_device_desc *ret = NULL;
|
||||||
|
|
||||||
|
__ASSERT_NO_MSG(dev_list != NULL);
|
||||||
|
|
||||||
|
for (i = 0; i < dev_list->num_i3c; i++) {
|
||||||
|
struct i3c_device_desc *desc = &dev_list->i3c[i];
|
||||||
|
|
||||||
|
if (desc->pid == id->pid) {
|
||||||
|
ret = desc;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct i3c_device_desc *i3c_dev_list_i3c_addr_find(const struct i3c_dev_list *dev_list,
|
||||||
|
uint8_t addr)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
struct i3c_device_desc *ret = NULL;
|
||||||
|
|
||||||
|
__ASSERT_NO_MSG(dev_list != NULL);
|
||||||
|
|
||||||
|
for (i = 0; i < dev_list->num_i3c; i++) {
|
||||||
|
struct i3c_device_desc *desc = &dev_list->i3c[i];
|
||||||
|
|
||||||
|
if (desc->dynamic_addr == addr) {
|
||||||
|
ret = desc;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct i3c_i2c_device_desc *i3c_dev_list_i2c_addr_find(const struct i3c_dev_list *dev_list,
|
||||||
|
uint16_t addr)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
struct i3c_i2c_device_desc *ret = NULL;
|
||||||
|
|
||||||
|
__ASSERT_NO_MSG(dev_list != NULL);
|
||||||
|
|
||||||
|
for (i = 0; i < dev_list->num_i2c; i++) {
|
||||||
|
struct i3c_i2c_device_desc *desc = &dev_list->i2c[i];
|
||||||
|
|
||||||
|
if (desc->addr == addr) {
|
||||||
|
ret = desc;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int i3c_dev_list_daa_addr_helper(struct i3c_addr_slots *addr_slots,
|
||||||
|
const struct i3c_dev_list *dev_list,
|
||||||
|
uint64_t pid, bool must_match,
|
||||||
|
bool assigned_okay,
|
||||||
|
struct i3c_device_desc **target,
|
||||||
|
uint8_t *addr)
|
||||||
|
{
|
||||||
|
struct i3c_device_desc *desc;
|
||||||
|
const uint16_t vendor_id = (uint16_t)(pid >> 32);
|
||||||
|
const uint32_t part_no = (uint32_t)(pid & 0xFFFFFFFFU);
|
||||||
|
uint8_t dyn_addr = 0;
|
||||||
|
int ret = 0;
|
||||||
|
const struct i3c_device_id i3c_id = I3C_DEVICE_ID(pid);
|
||||||
|
|
||||||
|
desc = i3c_dev_list_find(dev_list, &i3c_id);
|
||||||
|
if (must_match && (desc == NULL)) {
|
||||||
|
/*
|
||||||
|
* No device descriptor matching incoming PID and
|
||||||
|
* that we want an exact match.
|
||||||
|
*/
|
||||||
|
ret = -ENODEV;
|
||||||
|
|
||||||
|
LOG_DBG("PID 0x%04x%08x is not in registered device list",
|
||||||
|
vendor_id, part_no);
|
||||||
|
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (desc->dynamic_addr != 0U) {
|
||||||
|
if (assigned_okay) {
|
||||||
|
/* Return the already assigned address if desired so. */
|
||||||
|
dyn_addr = desc->dynamic_addr;
|
||||||
|
goto out;
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* Bail If target already has an assigned address.
|
||||||
|
* This is probably due to having the same PIDs for multiple targets
|
||||||
|
* in the device tree.
|
||||||
|
*/
|
||||||
|
LOG_ERR("PID 0x%04x%08x already has "
|
||||||
|
"dynamic address (0x%02x) assigned",
|
||||||
|
vendor_id, part_no, desc->dynamic_addr);
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Use the desired dynamic address as the new dynamic address
|
||||||
|
* if the slot is free.
|
||||||
|
*/
|
||||||
|
if (desc->init_dynamic_addr != 0U) {
|
||||||
|
if (i3c_addr_slots_is_free(addr_slots,
|
||||||
|
desc->init_dynamic_addr)) {
|
||||||
|
dyn_addr = desc->init_dynamic_addr;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Find the next available address.
|
||||||
|
*/
|
||||||
|
dyn_addr = i3c_addr_slots_next_free_find(addr_slots);
|
||||||
|
|
||||||
|
if (dyn_addr == 0U) {
|
||||||
|
/* No free addresses available */
|
||||||
|
LOG_DBG("No more free addresses available.");
|
||||||
|
ret = -ENOSPC;
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
*addr = dyn_addr;
|
||||||
|
*target = desc;
|
||||||
|
|
||||||
|
err:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int i3c_device_basic_info_get(struct i3c_device_desc *target)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
uint8_t tmp_bcr;
|
||||||
|
|
||||||
|
struct i3c_ccc_getbcr bcr = {0};
|
||||||
|
struct i3c_ccc_getdcr dcr = {0};
|
||||||
|
struct i3c_ccc_mrl mrl = {0};
|
||||||
|
struct i3c_ccc_mwl mwl = {0};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Since some CCC functions requires BCR to function
|
||||||
|
* correctly, we save the BCR here and update the BCR
|
||||||
|
* in the descriptor. If any following operations fails,
|
||||||
|
* we can restore the BCR.
|
||||||
|
*/
|
||||||
|
tmp_bcr = target->bcr;
|
||||||
|
|
||||||
|
/* GETBCR */
|
||||||
|
ret = i3c_ccc_do_getbcr(target, &bcr);
|
||||||
|
if (ret != 0) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
target->bcr = bcr.bcr;
|
||||||
|
|
||||||
|
/* GETDCR */
|
||||||
|
ret = i3c_ccc_do_getdcr(target, &dcr);
|
||||||
|
if (ret != 0) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* GETMRL */
|
||||||
|
ret = i3c_ccc_do_getmrl(target, &mrl);
|
||||||
|
if (ret != 0) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* GETMWL */
|
||||||
|
ret = i3c_ccc_do_getmwl(target, &mwl);
|
||||||
|
if (ret != 0) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
target->dcr = dcr.dcr;
|
||||||
|
target->data_length.mrl = mrl.len;
|
||||||
|
target->data_length.mwl = mwl.len;
|
||||||
|
target->data_length.max_ibi = mrl.ibi_len;
|
||||||
|
|
||||||
|
out:
|
||||||
|
if (ret != 0) {
|
||||||
|
/* Restore BCR is any CCC fails. */
|
||||||
|
target->bcr = tmp_bcr;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Do SETDASA to set static address as dynamic address.
|
||||||
|
*
|
||||||
|
* @param dev Pointer to the device driver instance.
|
||||||
|
* @param[out] True if DAA is still needed. False if all registered
|
||||||
|
* devices have static addresses.
|
||||||
|
*
|
||||||
|
* @retval 0 if successful.
|
||||||
|
*/
|
||||||
|
static int i3c_bus_setdasa(const struct device *dev,
|
||||||
|
const struct i3c_dev_list *dev_list,
|
||||||
|
bool *need_daa)
|
||||||
|
{
|
||||||
|
int i, ret;
|
||||||
|
|
||||||
|
*need_daa = false;
|
||||||
|
|
||||||
|
/* Loop through the registered I3C devices */
|
||||||
|
for (i = 0; i < dev_list->num_i3c; i++) {
|
||||||
|
struct i3c_device_desc *desc = &dev_list->i3c[i];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A device without static address => need to do
|
||||||
|
* dynamic address assignment.
|
||||||
|
*/
|
||||||
|
if (desc->static_addr == 0U) {
|
||||||
|
*need_daa = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If there is a desired dynamic address and it is
|
||||||
|
* not the same as the static address, wait till
|
||||||
|
* ENTDAA to do address assignment as this is
|
||||||
|
* no longer SETDASA.
|
||||||
|
*/
|
||||||
|
if ((desc->init_dynamic_addr != 0U) &&
|
||||||
|
(desc->init_dynamic_addr != desc->static_addr)) {
|
||||||
|
*need_daa = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DBG("SETDASA for 0x%x", desc->static_addr);
|
||||||
|
|
||||||
|
ret = i3c_ccc_do_setdasa(desc);
|
||||||
|
if (ret == 0) {
|
||||||
|
desc->dynamic_addr = desc->static_addr;
|
||||||
|
} else {
|
||||||
|
LOG_ERR("SETDASA error on address 0x%x (%d)",
|
||||||
|
desc->static_addr, ret);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int i3c_bus_init(const struct device *dev, const struct i3c_dev_list *dev_list)
|
||||||
|
{
|
||||||
|
int i, ret = 0;
|
||||||
|
bool need_daa = true;
|
||||||
|
struct i3c_ccc_events i3c_events;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reset all connected targets. Also reset dynamic
|
||||||
|
* addresses for all devices as we have no idea what
|
||||||
|
* dynamic addresses the connected devices have
|
||||||
|
* (e.g. assigned during previous power cycle).
|
||||||
|
*
|
||||||
|
* Note that we ignore error for both RSTACT and RSTDAA
|
||||||
|
* as there may not be any connected devices responding
|
||||||
|
* to these CCCs.
|
||||||
|
*/
|
||||||
|
if (i3c_ccc_do_rstact_all(dev, I3C_CCC_RSTACT_RESET_WHOLE_TARGET) != 0) {
|
||||||
|
/*
|
||||||
|
* Reset Whole Target support is not required so
|
||||||
|
* if there is any NACK, we want to at least reset
|
||||||
|
* the I3C peripheral of targets.
|
||||||
|
*/
|
||||||
|
LOG_DBG("Broadcast RSTACT (whole target) was NACK.");
|
||||||
|
|
||||||
|
if (i3c_ccc_do_rstact_all(dev, I3C_CCC_RSTACT_PERIPHERAL_ONLY) != 0) {
|
||||||
|
LOG_DBG("Broadcast RSTACT (peripehral) was NACK.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i3c_ccc_do_rstdaa_all(dev) != 0) {
|
||||||
|
LOG_DBG("Broadcast RSTDAA was NACK.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Disable all events from targets to avoid them
|
||||||
|
* interfering with bus initialization,
|
||||||
|
* especially during DAA.
|
||||||
|
*/
|
||||||
|
i3c_events.events = I3C_CCC_EVT_ALL;
|
||||||
|
ret = i3c_ccc_do_events_all_set(dev, false, &i3c_events);
|
||||||
|
if (ret != 0) {
|
||||||
|
LOG_DBG("Broadcast DISEC was NACK.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set static addresses as dynamic addresses.
|
||||||
|
*/
|
||||||
|
ret = i3c_bus_setdasa(dev, dev_list, &need_daa);
|
||||||
|
if (ret != 0) {
|
||||||
|
goto err_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Perform Dynamic Address Assignment if needed.
|
||||||
|
*/
|
||||||
|
if (need_daa) {
|
||||||
|
ret = i3c_do_daa(dev);
|
||||||
|
if (ret != 0) {
|
||||||
|
/*
|
||||||
|
* Spec says to try once more
|
||||||
|
* if DAA fails the first time.
|
||||||
|
*/
|
||||||
|
ret = i3c_do_daa(dev);
|
||||||
|
if (ret != 0) {
|
||||||
|
/*
|
||||||
|
* Failure to finish dynamic address assignment
|
||||||
|
* is not the end of world... hopefully.
|
||||||
|
* Continue on so the devices already have
|
||||||
|
* addresses can still function.
|
||||||
|
*/
|
||||||
|
LOG_ERR("DAA was not successful.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Loop through the registered I3C devices to retrieve
|
||||||
|
* basic target information.
|
||||||
|
*/
|
||||||
|
for (i = 0; i < dev_list->num_i3c; i++) {
|
||||||
|
struct i3c_device_desc *desc = &dev_list->i3c[i];
|
||||||
|
|
||||||
|
if (desc->dynamic_addr == 0U) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = i3c_device_basic_info_get(desc);
|
||||||
|
if (ret != 0) {
|
||||||
|
LOG_ERR("Error getting basic device info for 0x%02x",
|
||||||
|
desc->static_addr);
|
||||||
|
} else {
|
||||||
|
LOG_DBG("Target 0x%02x, BCR 0x%02x, DCR 0x%02x, MRL %d, MWL %d, IBI %d",
|
||||||
|
desc->dynamic_addr, desc->bcr, desc->dcr,
|
||||||
|
desc->data_length.mrl, desc->data_length.mwl,
|
||||||
|
desc->data_length.max_ibi);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Only re-enable Hot-Join from targets.
|
||||||
|
* Target interrupts will be enabled when IBI is enabled.
|
||||||
|
* And transferring controller role is not supported so not need to
|
||||||
|
* enable the event.
|
||||||
|
*/
|
||||||
|
i3c_events.events = I3C_CCC_EVT_HJ;
|
||||||
|
ret = i3c_ccc_do_events_all_set(dev, true, &i3c_events);
|
||||||
|
if (ret != 0) {
|
||||||
|
LOG_DBG("Broadcast ENEC was NACK.");
|
||||||
|
}
|
||||||
|
|
||||||
|
err_out:
|
||||||
|
return ret;
|
||||||
|
}
|
82
drivers/i3c/i3c_handlers.c
Normal file
82
drivers/i3c/i3c_handlers.c
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 Intel Corporation
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <zephyr/drivers/i3c.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <zephyr/syscall_handler.h>
|
||||||
|
|
||||||
|
static inline int z_vrfy_i3c_do_ccc(const struct device *dev,
|
||||||
|
struct i3c_ccc_payload *payload)
|
||||||
|
{
|
||||||
|
Z_OOPS(Z_SYSCALL_DRIVER_I3C(dev, do_ccc));
|
||||||
|
Z_OOPS(Z_SYSCALL_MEMORY_READ(payload, sizeof(*payload)));
|
||||||
|
Z_OOPS(Z_SYSCALL_MEMORY_WRITE(payload, sizeof(*payload)));
|
||||||
|
|
||||||
|
if (payload->ccc.data != NULL) {
|
||||||
|
Z_OOPS(Z_SYSCALL_MEMORY_ARRAY_READ(payload->ccc.data,
|
||||||
|
payload->ccc.data_len,
|
||||||
|
sizeof(*payload->ccc.data)));
|
||||||
|
Z_OOPS(Z_SYSCALL_MEMORY_ARRAY_WRITE(payload->ccc.data,
|
||||||
|
payload->ccc.data_len,
|
||||||
|
sizeof(*payload->ccc.data)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (payload->targets.payloads != NULL) {
|
||||||
|
Z_OOPS(Z_SYSCALL_MEMORY_ARRAY_READ(payload->targets.payloads,
|
||||||
|
payload->targets.num_targets,
|
||||||
|
sizeof(*payload->targets.payloads)));
|
||||||
|
Z_OOPS(Z_SYSCALL_MEMORY_ARRAY_WRITE(payload->targets.payloads,
|
||||||
|
payload->targets.num_targets,
|
||||||
|
sizeof(*payload->targets.payloads)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return z_impl_i3c_do_ccc(dev, payload);
|
||||||
|
}
|
||||||
|
#include <syscalls/i3c_do_ccc_mrsh.c>
|
||||||
|
|
||||||
|
static uint32_t copy_i3c_msgs_and_transfer(struct i3c_device_desc *target,
|
||||||
|
const struct i3c_msg *msgs,
|
||||||
|
uint8_t num_msgs)
|
||||||
|
{
|
||||||
|
struct i3c_msg copy[num_msgs];
|
||||||
|
uint8_t i;
|
||||||
|
|
||||||
|
/* Use a local copy to avoid switcheroo attacks. */
|
||||||
|
memcpy(copy, msgs, num_msgs * sizeof(*msgs));
|
||||||
|
|
||||||
|
/* Validate the buffers in each message struct. Read options require
|
||||||
|
* that the target buffer be writable
|
||||||
|
*/
|
||||||
|
for (i = 0U; i < num_msgs; i++) {
|
||||||
|
Z_OOPS(Z_SYSCALL_MEMORY(copy[i].buf, copy[i].len,
|
||||||
|
copy[i].flags & I3C_MSG_READ));
|
||||||
|
}
|
||||||
|
|
||||||
|
return z_impl_i3c_transfer(target, copy, num_msgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int z_vrfy_i3c_transfer(struct i3c_device_desc *target,
|
||||||
|
struct i3c_msg *msgs, uint8_t num_msgs)
|
||||||
|
{
|
||||||
|
Z_OOPS(Z_SYSCALL_MEMORY_READ(target, sizeof(*target)));
|
||||||
|
Z_OOPS(Z_SYSCALL_OBJ(target->bus, K_OBJ_DRIVER_I3C));
|
||||||
|
|
||||||
|
/* copy_msgs_and_transfer() will allocate a copy on the stack using
|
||||||
|
* VLA, so ensure this won't blow the stack. Most functions defined
|
||||||
|
* in i2c.h use only a handful of messages, so up to 32 messages
|
||||||
|
* should be more than sufficient.
|
||||||
|
*/
|
||||||
|
Z_OOPS(Z_SYSCALL_VERIFY(num_msgs >= 1 && num_msgs < 32));
|
||||||
|
|
||||||
|
/* We need to be able to read the overall array of messages */
|
||||||
|
Z_OOPS(Z_SYSCALL_MEMORY_ARRAY_READ(msgs, num_msgs,
|
||||||
|
sizeof(struct i3c_msg)));
|
||||||
|
|
||||||
|
return copy_i3c_msgs_and_transfer((struct i3c_device_desc *)target,
|
||||||
|
(struct i3c_msg *)msgs,
|
||||||
|
(uint8_t)num_msgs);
|
||||||
|
}
|
||||||
|
#include <syscalls/i3c_transfer_mrsh.c>
|
|
@ -75,6 +75,34 @@ struct i2c_dt_spec {
|
||||||
uint16_t addr;
|
uint16_t addr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Structure initializer for i2c_dt_spec from devicetree (on I3C bus)
|
||||||
|
*
|
||||||
|
* This helper macro expands to a static initializer for a <tt>struct
|
||||||
|
* i2c_dt_spec</tt> by reading the relevant bus and address data from
|
||||||
|
* the devicetree.
|
||||||
|
*
|
||||||
|
* @param node_id Devicetree node identifier for the I2C device whose
|
||||||
|
* struct i2c_dt_spec to create an initializer for
|
||||||
|
*/
|
||||||
|
#define I2C_DT_SPEC_GET_ON_I3C(node_id) \
|
||||||
|
.bus = DEVICE_DT_GET(DT_BUS(node_id)), \
|
||||||
|
.addr = DT_PROP_BY_IDX(node_id, reg, 0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Structure initializer for i2c_dt_spec from devicetree (on I2C bus)
|
||||||
|
*
|
||||||
|
* This helper macro expands to a static initializer for a <tt>struct
|
||||||
|
* i2c_dt_spec</tt> by reading the relevant bus and address data from
|
||||||
|
* the devicetree.
|
||||||
|
*
|
||||||
|
* @param node_id Devicetree node identifier for the I2C device whose
|
||||||
|
* struct i2c_dt_spec to create an initializer for
|
||||||
|
*/
|
||||||
|
#define I2C_DT_SPEC_GET_ON_I2C(node_id) \
|
||||||
|
.bus = DEVICE_DT_GET(DT_BUS(node_id)), \
|
||||||
|
.addr = DT_REG_ADDR(node_id)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Structure initializer for i2c_dt_spec from devicetree
|
* @brief Structure initializer for i2c_dt_spec from devicetree
|
||||||
*
|
*
|
||||||
|
@ -85,10 +113,11 @@ struct i2c_dt_spec {
|
||||||
* @param node_id Devicetree node identifier for the I2C device whose
|
* @param node_id Devicetree node identifier for the I2C device whose
|
||||||
* struct i2c_dt_spec to create an initializer for
|
* struct i2c_dt_spec to create an initializer for
|
||||||
*/
|
*/
|
||||||
#define I2C_DT_SPEC_GET(node_id) \
|
#define I2C_DT_SPEC_GET(node_id) \
|
||||||
{ \
|
{ \
|
||||||
.bus = DEVICE_DT_GET(DT_BUS(node_id)), \
|
COND_CODE_1(DT_ON_BUS(node_id, i3c), \
|
||||||
.addr = DT_REG_ADDR(node_id) \
|
(I2C_DT_SPEC_GET_ON_I3C(node_id)), \
|
||||||
|
(I2C_DT_SPEC_GET_ON_I2C(node_id))) \
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
1573
include/zephyr/drivers/i3c.h
Normal file
1573
include/zephyr/drivers/i3c.h
Normal file
File diff suppressed because it is too large
Load diff
167
include/zephyr/drivers/i3c/addresses.h
Normal file
167
include/zephyr/drivers/i3c/addresses.h
Normal file
|
@ -0,0 +1,167 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2022 Intel Corporation
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ZEPHYR_INCLUDE_DRIVERS_I3C_ADDRESSES_H_
|
||||||
|
#define ZEPHYR_INCLUDE_DRIVERS_I3C_ADDRESSES_H_
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief I3C Address-related Helper Code
|
||||||
|
* @defgroup i3c_addresses I3C Address-related Helper Code
|
||||||
|
* @ingroup i3c_interface
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <zephyr/types.h>
|
||||||
|
#include <zephyr/sys/sys_io.h>
|
||||||
|
#include <zephyr/sys/util.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define I3C_BROADCAST_ADDR 0x7E
|
||||||
|
#define I3C_MAX_ADDR 0x7F
|
||||||
|
|
||||||
|
struct i3c_dev_list;
|
||||||
|
|
||||||
|
enum i3c_addr_slot_status {
|
||||||
|
I3C_ADDR_SLOT_STATUS_FREE = 0U,
|
||||||
|
I3C_ADDR_SLOT_STATUS_RSVD,
|
||||||
|
I3C_ADDR_SLOT_STATUS_I3C_DEV,
|
||||||
|
I3C_ADDR_SLOT_STATUS_I2C_DEV,
|
||||||
|
I3C_ADDR_SLOT_STATUS_MASK = 0x03U,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Structure to keep track of addresses on I3C bus.
|
||||||
|
*/
|
||||||
|
struct i3c_addr_slots {
|
||||||
|
/* 2 bits per slot */
|
||||||
|
unsigned long slots[((I3C_MAX_ADDR + 1) * 2) / BITS_PER_LONG];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize the I3C address slots struct.
|
||||||
|
*
|
||||||
|
* This clears out the assigned address bits, and set the reserved
|
||||||
|
* address bits according to the I3C specification.
|
||||||
|
*
|
||||||
|
* @param slots Pointer to address slots struct.
|
||||||
|
* @param dev_list Pointer to device list struct.
|
||||||
|
*
|
||||||
|
* @retval 0 if successful.
|
||||||
|
* @retval -EINVAL if duplicate addresses.
|
||||||
|
*/
|
||||||
|
int i3c_addr_slots_init(struct i3c_addr_slots *slots,
|
||||||
|
const struct i3c_dev_list *dev_list);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the address status of a device.
|
||||||
|
*
|
||||||
|
* @param slots Pointer to the address slots structure.
|
||||||
|
* @param dev_addr Device address.
|
||||||
|
* @param status New status for the address @p dev_addr.
|
||||||
|
*/
|
||||||
|
void i3c_addr_slots_set(struct i3c_addr_slots *slots,
|
||||||
|
uint8_t dev_addr,
|
||||||
|
enum i3c_addr_slot_status status);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the address status of a device.
|
||||||
|
*
|
||||||
|
* @param slots Pointer to the address slots structure.
|
||||||
|
* @param dev_addr Device address.
|
||||||
|
*
|
||||||
|
* @return Address status for the address @p dev_addr.
|
||||||
|
*/
|
||||||
|
enum i3c_addr_slot_status i3c_addr_slots_status(struct i3c_addr_slots *slots,
|
||||||
|
uint8_t dev_addr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check if the address is free.
|
||||||
|
*
|
||||||
|
* @param slots Pointer to the address slots structure.
|
||||||
|
* @param dev_addr Device address.
|
||||||
|
*
|
||||||
|
* @retval true if address is free.
|
||||||
|
* @retval false if address is not free.
|
||||||
|
*/
|
||||||
|
bool i3c_addr_slots_is_free(struct i3c_addr_slots *slots,
|
||||||
|
uint8_t dev_addr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Find the next free address.
|
||||||
|
*
|
||||||
|
* This can be used to find the next free address that can be
|
||||||
|
* assigned to a new device.
|
||||||
|
*
|
||||||
|
* @param slots Pointer to the address slots structure.
|
||||||
|
*
|
||||||
|
* @return The next free address, or 0 if none found.
|
||||||
|
*/
|
||||||
|
uint8_t i3c_addr_slots_next_free_find(struct i3c_addr_slots *slots);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Mark the address as free (not used) in device list.
|
||||||
|
*
|
||||||
|
* @param addr_slots Pointer to the address slots struct.
|
||||||
|
* @param addr Device address.
|
||||||
|
*/
|
||||||
|
static inline void i3c_addr_slots_mark_free(struct i3c_addr_slots *addr_slots,
|
||||||
|
uint8_t addr)
|
||||||
|
{
|
||||||
|
i3c_addr_slots_set(addr_slots, addr,
|
||||||
|
I3C_ADDR_SLOT_STATUS_FREE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Mark the address as reserved in device list.
|
||||||
|
*
|
||||||
|
* @param addr_slots Pointer to the address slots struct.
|
||||||
|
* @param addr Device address.
|
||||||
|
*/
|
||||||
|
static inline void i3c_addr_slots_mark_rsvd(struct i3c_addr_slots *addr_slots,
|
||||||
|
uint8_t addr)
|
||||||
|
{
|
||||||
|
i3c_addr_slots_set(addr_slots, addr,
|
||||||
|
I3C_ADDR_SLOT_STATUS_RSVD);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Mark the address as I3C device in device list.
|
||||||
|
*
|
||||||
|
* @param addr_slots Pointer to the address slots struct.
|
||||||
|
* @param addr Device address.
|
||||||
|
*/
|
||||||
|
static inline void i3c_addr_slots_mark_i3c(struct i3c_addr_slots *addr_slots,
|
||||||
|
uint8_t addr)
|
||||||
|
{
|
||||||
|
i3c_addr_slots_set(addr_slots, addr,
|
||||||
|
I3C_ADDR_SLOT_STATUS_I3C_DEV);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Mark the address as I2C device in device list.
|
||||||
|
*
|
||||||
|
* @param addr_slots Pointer to the address slots struct.
|
||||||
|
* @param addr Device address.
|
||||||
|
*/
|
||||||
|
static inline void i3c_addr_slots_mark_i2c(struct i3c_addr_slots *addr_slots,
|
||||||
|
uint8_t addr)
|
||||||
|
{
|
||||||
|
i3c_addr_slots_set(addr_slots, addr,
|
||||||
|
I3C_ADDR_SLOT_STATUS_I2C_DEV);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @}
|
||||||
|
*/
|
||||||
|
|
||||||
|
#endif /* ZEPHYR_INCLUDE_DRIVERS_I3C_ADDRESSES_H_ */
|
1064
include/zephyr/drivers/i3c/ccc.h
Normal file
1064
include/zephyr/drivers/i3c/ccc.h
Normal file
File diff suppressed because it is too large
Load diff
236
include/zephyr/drivers/i3c/devicetree.h
Normal file
236
include/zephyr/drivers/i3c/devicetree.h
Normal file
|
@ -0,0 +1,236 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2022 Intel Corporation
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ZEPHYR_INCLUDE_DRIVERS_I3C_DEVICETREE_H_
|
||||||
|
#define ZEPHYR_INCLUDE_DRIVERS_I3C_DEVICETREE_H_
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief I3C Devicetree related bits
|
||||||
|
* @defgroup i3c_devicetree I3C Devicetree related bits
|
||||||
|
* @ingroup i3c_interface
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <zephyr/device.h>
|
||||||
|
#include <zephyr/kernel.h>
|
||||||
|
#include <zephyr/types.h>
|
||||||
|
#include <zephyr/sys/util.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Structure initializer for i3c_device_id from devicetree
|
||||||
|
*
|
||||||
|
* This helper macro expands to a static initializer for a <tt>struct
|
||||||
|
* i3c_device_id</tt> by reading the relevant device data from devicetree.
|
||||||
|
*
|
||||||
|
* @param node_id Devicetree node identifier for the I3C device whose
|
||||||
|
* struct i3c_device_id to create an initializer for
|
||||||
|
*/
|
||||||
|
#define I3C_DEVICE_ID_DT(node_id) \
|
||||||
|
{ \
|
||||||
|
.pid = ((uint64_t)DT_PROP_BY_IDX(node_id, reg, 1) << 32)\
|
||||||
|
| DT_PROP_BY_IDX(node_id, reg, 2), \
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Structure initializer for i3c_device_id from devicetree instance
|
||||||
|
*
|
||||||
|
* This is equivalent to
|
||||||
|
* <tt>I3C_DEVICE_ID_DT(DT_DRV_INST(inst))</tt>.
|
||||||
|
*
|
||||||
|
* @param inst Devicetree instance number
|
||||||
|
*/
|
||||||
|
#define I3C_DEVICE_ID_DT_INST(inst) \
|
||||||
|
I3C_DEVICE_ID_DT(DT_DRV_INST(inst))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Structure initializer for i3c_device_desc from devicetree
|
||||||
|
*
|
||||||
|
* This helper macro expands to a static initializer for a <tt>struct
|
||||||
|
* i3c_device_desc</tt> by reading the relevant bus and device data
|
||||||
|
* from the devicetree.
|
||||||
|
*
|
||||||
|
* @param node_id Devicetree node identifier for the I3C device whose
|
||||||
|
* struct i3c_device_desc to create an initializer for
|
||||||
|
*/
|
||||||
|
#define I3C_DEVICE_DESC_DT(node_id) \
|
||||||
|
{ \
|
||||||
|
.bus = DEVICE_DT_GET(DT_BUS(node_id)), \
|
||||||
|
.dev = DEVICE_DT_GET(node_id), \
|
||||||
|
.static_addr = DT_PROP_BY_IDX(node_id, reg, 0), \
|
||||||
|
.pid = ((uint64_t)DT_PROP_BY_IDX(node_id, reg, 1) << 32)\
|
||||||
|
| DT_PROP_BY_IDX(node_id, reg, 2), \
|
||||||
|
.init_dynamic_addr = \
|
||||||
|
DT_PROP_OR(node_id, assigned_address, 0), \
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Structure initializer for i3c_device_desc from devicetree instance
|
||||||
|
*
|
||||||
|
* This is equivalent to
|
||||||
|
* <tt>I3C_DEVICE_DESC_DT(DT_DRV_INST(inst))</tt>.
|
||||||
|
*
|
||||||
|
* @param inst Devicetree instance number
|
||||||
|
*/
|
||||||
|
#define I3C_DEVICE_DESC_DT_INST(inst) \
|
||||||
|
I3C_DEVICE_DESC_DT(DT_DRV_INST(inst))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Structure initializer for i3c_device_desc from devicetree
|
||||||
|
*
|
||||||
|
* This is mainly used by <tt>I3C_DEVICE_ARRAY_DT()</tt> to only
|
||||||
|
* create a struct if and only if it is an I3C device.
|
||||||
|
*/
|
||||||
|
#define I3C_DEVICE_DESC_DT_FILTERED(node_id) \
|
||||||
|
COND_CODE_0(DT_PROP_BY_IDX(node_id, reg, 1), \
|
||||||
|
(), (I3C_DEVICE_DESC_DT(node_id)))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Array initializer for a list of i3c_device_desc from devicetree
|
||||||
|
*
|
||||||
|
* This is a helper macro to generate an array for a list of i3c_device_desc
|
||||||
|
* from device tree.
|
||||||
|
*
|
||||||
|
* @param node_id Devicetree node identifier of the I3C controller
|
||||||
|
*/
|
||||||
|
#define I3C_DEVICE_ARRAY_DT(node_id) \
|
||||||
|
{ \
|
||||||
|
DT_FOREACH_CHILD_STATUS_OKAY( \
|
||||||
|
node_id, \
|
||||||
|
I3C_DEVICE_DESC_DT_FILTERED) \
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Array initializer for a list of i3c_device_desc from devicetree instance
|
||||||
|
*
|
||||||
|
* This is equivalent to
|
||||||
|
* <tt>I3C_DEVICE_ARRAY_DT(DT_DRV_INST(inst))</tt>.
|
||||||
|
*
|
||||||
|
* @param inst Devicetree instance number of the I3C controller
|
||||||
|
*/
|
||||||
|
#define I3C_DEVICE_ARRAY_DT_INST(inst) \
|
||||||
|
I3C_DEVICE_ARRAY_DT(DT_DRV_INST(inst))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Like DEVICE_DT_DEFINE() with I3C target device specifics.
|
||||||
|
*
|
||||||
|
* Defines a I3C target device which implements the I3C target device API.
|
||||||
|
*
|
||||||
|
* @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 I3C_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__)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Like I3C_TARGET_DT_DEFINE() for an instance of a DT_DRV_COMPAT compatible
|
||||||
|
*
|
||||||
|
* @param inst instance number. This is replaced by
|
||||||
|
* <tt>DT_DRV_COMPAT(inst)</tt> in the call to I3C_TARGET_DT_DEFINE().
|
||||||
|
*
|
||||||
|
* @param ... other parameters as expected by I3C_TARGET_DT_DEFINE().
|
||||||
|
*/
|
||||||
|
#define I3C_DEVICE_DT_INST_DEFINE(inst, ...) \
|
||||||
|
I3C_DEVICE_DT_DEFINE(DT_DRV_INST(inst), __VA_ARGS__)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Structure initializer for i3c_i2c_device_desc from devicetree
|
||||||
|
*
|
||||||
|
* This helper macro expands to a static initializer for a <tt>struct
|
||||||
|
* i3c_i2c_device_desc</tt> by reading the relevant bus and device data
|
||||||
|
* from the devicetree.
|
||||||
|
*
|
||||||
|
* @param node_id Devicetree node identifier for the I3C device whose
|
||||||
|
* struct i3c_i2c_device_desc to create an initializer for
|
||||||
|
*/
|
||||||
|
#define I3C_I2C_DEVICE_DESC_DT(node_id) \
|
||||||
|
{ \
|
||||||
|
.bus = DEVICE_DT_GET(DT_BUS(node_id)), \
|
||||||
|
.addr = DT_PROP_BY_IDX(node_id, reg, 0), \
|
||||||
|
.lvr = DT_PROP_BY_IDX(node_id, reg, 2), \
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Structure initializer for i3c_i2c_device_desc from devicetree instance
|
||||||
|
*
|
||||||
|
* This is equivalent to
|
||||||
|
* <tt>I3C_I2C_DEVICE_DESC_DT(DT_DRV_INST(inst))</tt>.
|
||||||
|
*
|
||||||
|
* @param inst Devicetree instance number
|
||||||
|
*/
|
||||||
|
#define I3C_I2C_DEVICE_DESC_DT_INST(inst) \
|
||||||
|
I3C_I2C_DEVICE_DESC_DT(DT_DRV_INST(inst))
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Structure initializer for i3c_i2c_device_desc from devicetree
|
||||||
|
*
|
||||||
|
* This is mainly used by <tt>I3C_I2C_DEVICE_ARRAY_DT()</tt> to only
|
||||||
|
* create a struct if and only if it is an I2C device.
|
||||||
|
*/
|
||||||
|
#define I3C_I2C_DEVICE_DESC_DT_FILTERED(node_id) \
|
||||||
|
COND_CODE_0(DT_PROP_BY_IDX(node_id, reg, 1), \
|
||||||
|
(I3C_I2C_DEVICE_DESC_DT(node_id)), ())
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Array initializer for a list of i3c_i2c_device_desc from devicetree
|
||||||
|
*
|
||||||
|
* This is a helper macro to generate an array for a list of
|
||||||
|
* i3c_i2c_device_desc from device tree.
|
||||||
|
*
|
||||||
|
* @param node_id Devicetree node identifier of the I3C controller
|
||||||
|
*/
|
||||||
|
#define I3C_I2C_DEVICE_ARRAY_DT(node_id) \
|
||||||
|
{ \
|
||||||
|
DT_FOREACH_CHILD_STATUS_OKAY( \
|
||||||
|
node_id, \
|
||||||
|
I3C_I2C_DEVICE_DESC_DT_FILTERED) \
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Array initializer for a list of i3c_i2c_device_desc from devicetree instance
|
||||||
|
*
|
||||||
|
* This is equivalent to
|
||||||
|
* <tt>I3C_I2C_DEVICE_ARRAY_DT(DT_DRV_INST(inst))</tt>.
|
||||||
|
*
|
||||||
|
* @param inst Devicetree instance number of the I3C controller
|
||||||
|
*/
|
||||||
|
#define I3C_I2C_DEVICE_ARRAY_DT_INST(inst) \
|
||||||
|
I3C_I2C_DEVICE_ARRAY_DT(DT_DRV_INST(inst))
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @}
|
||||||
|
*/
|
||||||
|
|
||||||
|
#endif /* ZEPHYR_INCLUDE_DRIVERS_I3C_DEVICETREE_H_ */
|
107
include/zephyr/drivers/i3c/ibi.h
Normal file
107
include/zephyr/drivers/i3c/ibi.h
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2022 Intel Corporation
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ZEPHYR_INCLUDE_DRIVERS_I3C_IBI_H_
|
||||||
|
#define ZEPHYR_INCLUDE_DRIVERS_I3C_IBI_H_
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief I3C In-Band Interrupts
|
||||||
|
* @defgroup i3c_ibi I3C In-Band Interrupts
|
||||||
|
* @ingroup i3c_interface
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <zephyr/device.h>
|
||||||
|
#include <zephyr/kernel.h>
|
||||||
|
#include <zephyr/types.h>
|
||||||
|
#include <zephyr/sys/util.h>
|
||||||
|
|
||||||
|
#ifndef CONFIG_I3C_IBI_MAX_PAYLOAD_SIZE
|
||||||
|
#define CONFIG_I3C_IBI_MAX_PAYLOAD_SIZE 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct i3c_device_desc;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief IBI Types.
|
||||||
|
*/
|
||||||
|
enum i3c_ibi_type {
|
||||||
|
/** Target interrupt */
|
||||||
|
I3C_IBI_TARGET_INTR,
|
||||||
|
|
||||||
|
/** Controller Role Request */
|
||||||
|
I3C_IBI_CONTROLLER_ROLE_REQUEST,
|
||||||
|
|
||||||
|
/** Hot Join Request */
|
||||||
|
I3C_IBI_HOTJOIN,
|
||||||
|
|
||||||
|
I3C_IBI_TYPE_MAX = I3C_IBI_HOTJOIN,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Struct for IBI request.
|
||||||
|
*/
|
||||||
|
struct i3c_ibi {
|
||||||
|
/** Type of IBI. */
|
||||||
|
enum i3c_ibi_type ibi_type;
|
||||||
|
|
||||||
|
/** Pointer to payload of IBI. */
|
||||||
|
uint8_t *payload;
|
||||||
|
|
||||||
|
/** Length in bytes of the IBI payload. */
|
||||||
|
uint8_t payload_len;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Structure of payload buffer for IBI.
|
||||||
|
*
|
||||||
|
* This is used for the IBI callback.
|
||||||
|
*/
|
||||||
|
struct i3c_ibi_payload {
|
||||||
|
/**
|
||||||
|
* Length of available data in the payload buffer.
|
||||||
|
*/
|
||||||
|
uint8_t payload_len;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pointer to byte array as payload buffer.
|
||||||
|
*/
|
||||||
|
uint8_t payload[CONFIG_I3C_IBI_MAX_PAYLOAD_SIZE];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Function called when In-Band Interrupt received from target device.
|
||||||
|
*
|
||||||
|
* This function is invoked by the controller when the controller
|
||||||
|
* receives an In-Band Interrupt from the target device.
|
||||||
|
*
|
||||||
|
* A success return shall cause the controller to ACK the next byte
|
||||||
|
* received. An error return shall cause the controller to NACK the
|
||||||
|
* next byte received.
|
||||||
|
*
|
||||||
|
* @param target the device description structure associated with the
|
||||||
|
* device to which the operation is addressed.
|
||||||
|
* @param payload Payload associated with the IBI. NULL if there is
|
||||||
|
* no payload.
|
||||||
|
*
|
||||||
|
* @return 0 if the IBI is accepted, or a negative error code.
|
||||||
|
*/
|
||||||
|
typedef int (*i3c_target_ibi_cb_t)(struct i3c_device_desc *target,
|
||||||
|
struct i3c_ibi_payload *payload);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @}
|
||||||
|
*/
|
||||||
|
|
||||||
|
#endif /* ZEPHYR_INCLUDE_DRIVERS_I3C_IBI_H_ */
|
Loading…
Add table
Add a link
Reference in a new issue