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:
Daniel Leung 2022-07-18 13:21:12 -07:00 committed by Anas Nashif
commit ce7058d2f5
15 changed files with 4350 additions and 4 deletions

View file

@ -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

View file

@ -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:

View file

@ -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)

View file

@ -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"

View 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
View 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
View 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
View 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;
}

View 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>

View file

@ -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

File diff suppressed because it is too large Load diff

View 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_ */

File diff suppressed because it is too large Load diff

View 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_ */

View 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_ */