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

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