zephyr/drivers/i3c/i3c_ccc.c
Ryan McClelland 9640905a71 drivers: i3c: add ccc getcaps helper
This adds the defines for getcaps format 1 and format 2.

Signed-off-by: Ryan McClelland <ryanmcclelland@meta.com>
2024-05-16 16:23:31 +02:00

593 lines
15 KiB
C

/*
* Copyright (c) 2022 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <zephyr/toolchain.h>
#include <zephyr/sys/byteorder.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_driver_data *bus_data = (struct i3c_driver_data *)target->bus->data;
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->init_dynamic_addr ?
target->init_dynamic_addr : target->static_addr) << 1;
/* check that initial dynamic address is free before setting it */
if ((target->init_dynamic_addr != 0) &&
(target->init_dynamic_addr != target->static_addr)) {
if (!i3c_addr_slots_is_free(&bus_data->attached_dev.addr_slots, dyn_addr >> 1)) {
return -EINVAL;
}
}
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_setnewda(const struct i3c_device_desc *target, struct i3c_ccc_address new_da)
{
struct i3c_driver_data *bus_data = (struct i3c_driver_data *)target->bus->data;
struct i3c_ccc_payload ccc_payload;
struct i3c_ccc_target_payload ccc_tgt_payload;
uint8_t new_dyn_addr;
__ASSERT_NO_MSG(target != NULL);
__ASSERT_NO_MSG(target->bus != NULL);
if (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;
*/
new_dyn_addr = new_da.addr << 1;
/* check that initial dynamic address is free before setting it */
if (target->dynamic_addr != new_da.addr) {
if (!i3c_addr_slots_is_free(&bus_data->attached_dev.addr_slots, new_da.addr)) {
return -EINVAL;
}
}
ccc_tgt_payload.addr = target->dynamic_addr;
ccc_tgt_payload.rnw = 0;
ccc_tgt_payload.data = &new_dyn_addr;
ccc_tgt_payload.data_len = 1;
memset(&ccc_payload, 0, sizeof(ccc_payload));
ccc_payload.ccc.id = I3C_CCC_SETNEWDA;
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. */
sys_put_be16(mwl->len, data);
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->dynamic_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. */
sys_put_be16(mwl->len, data);
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 = sys_get_be16(data);
}
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. */
sys_put_be16(mrl->len, data);
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->dynamic_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. */
sys_put_be16(mrl->len, data);
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 = sys_get_be16(data);
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 = sys_get_be16(data);
} else if (fmt == GETSTATUS_FORMAT_2) {
switch (defbyte) {
case GETSTATUS_FORMAT_2_TGTSTAT:
__fallthrough;
case GETSTATUS_FORMAT_2_PRECR:
status->fmt2.raw_u16 = sys_get_be16(data);
break;
default:
break;
}
}
}
out:
return ret;
}
int i3c_ccc_do_getcaps(const struct i3c_device_desc *target,
union i3c_ccc_getcaps *caps,
enum i3c_ccc_getcaps_fmt fmt,
enum i3c_ccc_getcaps_defbyte defbyte)
{
struct i3c_ccc_payload ccc_payload;
struct i3c_ccc_target_payload ccc_tgt_payload;
uint8_t defining_byte;
uint8_t data[4];
uint8_t len;
int ret;
__ASSERT_NO_MSG(target != NULL);
__ASSERT_NO_MSG(target->bus != NULL);
__ASSERT_NO_MSG(caps != NULL);
ccc_tgt_payload.addr = target->dynamic_addr;
ccc_tgt_payload.rnw = 1;
ccc_tgt_payload.data = &data[0];
if (fmt == GETCAPS_FORMAT_1) {
/* Could be 1-4 Data Bytes Returned */
ccc_tgt_payload.data_len = 4;
} else if (fmt == GETCAPS_FORMAT_2) {
switch (defbyte) {
case GETCAPS_FORMAT_2_CRCAPS:
__fallthrough;
case GETCAPS_FORMAT_2_VTCAPS:
/* Could be 1-2 Data Bytes Returned*/
ccc_tgt_payload.data_len = 2;
break;
case GETCAPS_FORMAT_2_TGTCAPS:
__fallthrough;
case GETCAPS_FORMAT_2_TESTPAT:
/* Could be 1-4 Data Bytes Returned */
ccc_tgt_payload.data_len = 4;
break;
default:
ret = -EINVAL;
goto out;
}
} else {
ret = -EINVAL;
goto out;
}
memset(&ccc_payload, 0, sizeof(ccc_payload));
ccc_payload.ccc.id = I3C_CCC_GETCAPS;
ccc_payload.targets.payloads = &ccc_tgt_payload;
ccc_payload.targets.num_targets = 1;
if (fmt == GETCAPS_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) {
/* GETCAPS will return a variable length */
len = ccc_tgt_payload.num_xfer;
if (fmt == GETCAPS_FORMAT_1) {
memcpy(caps->fmt1.getcaps, data, len);
/* for values not received, assume default (1'b0) */
memset(&caps->fmt1.getcaps[len], 0, sizeof(caps->fmt1.getcaps) - len);
} else if (fmt == GETCAPS_FORMAT_2) {
switch (defbyte) {
case GETCAPS_FORMAT_2_CRCAPS:
memcpy(caps->fmt2.crcaps, data, len);
/* for values not received, assume default (1'b0) */
memset(&caps->fmt2.crcaps[len], 0, sizeof(caps->fmt2.crcaps) - len);
case GETCAPS_FORMAT_2_VTCAPS:
memcpy(caps->fmt2.vtcaps, data, len);
/* for values not received, assume default (1'b0) */
memset(&caps->fmt2.vtcaps[len], 0, sizeof(caps->fmt2.vtcaps) - len);
break;
case GETCAPS_FORMAT_2_TGTCAPS:
memcpy(caps->fmt2.tgtcaps, data, len);
/* for values not received, assume default (1'b0) */
memset(&caps->fmt2.tgtcaps[len], 0,
sizeof(caps->fmt2.tgtcaps) - len);
break;
case GETCAPS_FORMAT_2_TESTPAT:
/* should always be 4 data bytes */
caps->fmt2.testpat = sys_get_be32(data);
break;
default:
break;
}
}
}
out:
return ret;
}