zephyr/drivers/i3c/i3c_shell.c

2524 lines
72 KiB
C

/*
* Copyright (c) 2024 Meta Platforms
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/drivers/i3c.h>
#include <zephyr/shell/shell.h>
#include <stdlib.h>
#include <string.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/sys/util.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(i3c_shell, CONFIG_LOG_DEFAULT_LEVEL);
#define MAX_BYTES_FOR_REGISTER_INDEX 4
#define ARGV_DEV 1
#define ARGV_TDEV 2
#define ARGV_REG 3
/* Maximum bytes we can write or read at once */
#define MAX_I3C_BYTES 16
struct i3c_ctrl {
const struct device *dev;
const union shell_cmd_entry *i3c_attached_dev_subcmd;
const union shell_cmd_entry *i3c_list_dev_subcmd;
};
#define I3C_ATTACHED_DEV_GET_FN(node_id) \
static void node_id##cmd_i3c_attached_get(size_t idx, struct shell_static_entry *entry); \
\
SHELL_DYNAMIC_CMD_CREATE(node_id##sub_i3c_attached, node_id##cmd_i3c_attached_get); \
\
static void node_id##cmd_i3c_attached_get(size_t idx, struct shell_static_entry *entry) \
{ \
const struct device *dev = DEVICE_DT_GET(node_id); \
struct i3c_driver_data *data; \
sys_snode_t *node; \
size_t cnt = 0; \
\
entry->syntax = NULL; \
entry->handler = NULL; \
entry->subcmd = NULL; \
entry->help = NULL; \
\
data = (struct i3c_driver_data *)dev->data; \
if (!sys_slist_is_empty(&data->attached_dev.devices.i3c)) { \
SYS_SLIST_FOR_EACH_NODE(&data->attached_dev.devices.i3c, node) { \
if (cnt == idx) { \
struct i3c_device_desc *desc = \
CONTAINER_OF(node, struct i3c_device_desc, node); \
entry->syntax = desc->dev->name; \
return; \
} \
cnt++; \
} \
} \
}
#define I3C_LIST_DEV_GET_FN(node_id) \
static void node_id##cmd_i3c_list_get(size_t idx, struct shell_static_entry *entry); \
\
SHELL_DYNAMIC_CMD_CREATE(node_id##sub_i3c_list, node_id##cmd_i3c_list_get); \
\
static void node_id##cmd_i3c_list_get(size_t idx, struct shell_static_entry *entry) \
{ \
const struct device *dev = DEVICE_DT_GET(node_id); \
struct i3c_driver_config *config; \
\
entry->syntax = NULL; \
entry->handler = NULL; \
entry->subcmd = NULL; \
entry->help = NULL; \
\
config = (struct i3c_driver_config *)dev->config; \
if (idx < config->dev_list.num_i3c) { \
entry->syntax = config->dev_list.i3c[idx].dev->name; \
} \
}
#define I3C_CTRL_FN(node_id) \
I3C_ATTACHED_DEV_GET_FN(node_id) \
I3C_LIST_DEV_GET_FN(node_id)
/* zephyr-keep-sorted-start */
DT_FOREACH_STATUS_OKAY(cdns_i3c, I3C_CTRL_FN)
DT_FOREACH_STATUS_OKAY(nuvoton_npcx_i3c, I3C_CTRL_FN)
DT_FOREACH_STATUS_OKAY(nxp_mcux_i3c, I3C_CTRL_FN)
/* zephyr-keep-sorted-stop */
#define I3C_CTRL_LIST_ENTRY(node_id) \
{ \
.dev = DEVICE_DT_GET(node_id), \
.i3c_attached_dev_subcmd = &node_id##sub_i3c_attached, \
.i3c_list_dev_subcmd = &node_id##sub_i3c_list, \
},
const struct i3c_ctrl i3c_list[] = {
/* zephyr-keep-sorted-start */
DT_FOREACH_STATUS_OKAY(cdns_i3c, I3C_CTRL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(nuvoton_npcx_i3c, I3C_CTRL_LIST_ENTRY)
DT_FOREACH_STATUS_OKAY(nxp_mcux_i3c, I3C_CTRL_LIST_ENTRY)
/* zephyr-keep-sorted-stop */
};
static int get_bytes_count_for_hex(char *arg)
{
int length = (strlen(arg) + 1) / 2;
if (length > 1 && arg[0] == '0' && (arg[1] == 'x' || arg[1] == 'X')) {
length -= 1;
}
return MIN(MAX_BYTES_FOR_REGISTER_INDEX, length);
}
static struct i3c_i2c_device_desc *get_i3c_i2c_list_desc_from_addr(const struct device *dev,
uint16_t addr)
{
struct i3c_driver_config *config;
uint8_t i;
config = (struct i3c_driver_config *)dev->config;
for (i = 0; i < config->dev_list.num_i2c; i++) {
if (config->dev_list.i2c[i].addr == addr) {
/* only look for a device with the addr */
return &config->dev_list.i2c[i];
}
}
return NULL;
}
static struct i3c_device_desc *get_i3c_list_desc_from_dev_name(const struct device *dev,
const char *tdev_name)
{
struct i3c_driver_config *config;
uint8_t i;
config = (struct i3c_driver_config *)dev->config;
for (i = 0; i < config->dev_list.num_i3c; i++) {
if (strcmp(config->dev_list.i3c[i].dev->name, tdev_name) == 0) {
/* only look for a device with the same name */
return &config->dev_list.i3c[i];
}
}
return NULL;
}
static struct i3c_device_desc *get_i3c_attached_desc_from_dev_name(const struct device *dev,
const char *tdev_name)
{
struct i3c_driver_data *data;
sys_snode_t *node;
data = (struct i3c_driver_data *)dev->data;
if (!sys_slist_is_empty(&data->attached_dev.devices.i3c)) {
SYS_SLIST_FOR_EACH_NODE(&data->attached_dev.devices.i3c, node) {
struct i3c_device_desc *desc =
CONTAINER_OF(node, struct i3c_device_desc, node);
/* only look for a device with the same name */
if (strcmp(desc->dev->name, tdev_name) == 0) {
return desc;
}
}
}
return NULL;
}
/* i3c info <device> [<target>] */
static int cmd_i3c_info(const struct shell *sh, size_t argc, char **argv)
{
const struct device *dev, *tdev;
struct i3c_driver_data *data;
sys_snode_t *node;
bool found = false;
dev = device_get_binding(argv[ARGV_DEV]);
if (!dev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_DEV]);
return -ENODEV;
}
data = (struct i3c_driver_data *)dev->data;
if (argc == 3) {
/* TODO: is this needed? */
tdev = device_get_binding(argv[ARGV_TDEV]);
if (!tdev) {
shell_error(sh, "I3C: Target Device driver %s not found.",
argv[ARGV_DEV]);
return -ENODEV;
}
if (!sys_slist_is_empty(&data->attached_dev.devices.i3c)) {
SYS_SLIST_FOR_EACH_NODE(&data->attached_dev.devices.i3c, node) {
struct i3c_device_desc *desc =
CONTAINER_OF(node, struct i3c_device_desc, node);
/* only look for a device with the same name */
if (strcmp(desc->dev->name, tdev->name) == 0) {
shell_print(sh,
"name: %s\n"
"\tpid: 0x%012llx\n"
"\tstatic_addr: 0x%02x\n"
"\tdynamic_addr: 0x%02x\n"
#if defined(CONFIG_I3C_USE_GROUP_ADDR)
"\tgroup_addr: 0x%02x\n"
#endif
"\tbcr: 0x%02x\n"
"\tdcr: 0x%02x\n"
"\tmaxrd: 0x%02x\n"
"\tmaxwr: 0x%02x\n"
"\tmax_read_turnaround: 0x%06x\n"
"\tmrl: 0x%04x\n"
"\tmwl: 0x%04x\n"
"\tmax_ibi: 0x%02x\n"
"\tgetcaps: 0x%02x; 0x%02x; 0x%02x; 0x%02x",
desc->dev->name, (uint64_t)desc->pid,
desc->static_addr, desc->dynamic_addr,
#if defined(CONFIG_I3C_USE_GROUP_ADDR)
desc->group_addr,
#endif
desc->bcr, desc->dcr, desc->data_speed.maxrd,
desc->data_speed.maxwr,
desc->data_speed.max_read_turnaround,
desc->data_length.mrl, desc->data_length.mwl,
desc->data_length.max_ibi,
desc->getcaps.getcap1, desc->getcaps.getcap2,
desc->getcaps.getcap3, desc->getcaps.getcap4);
found = true;
break;
}
}
} else {
shell_print(sh, "I3C: No devices found.");
return -ENODEV;
}
if (found == false) {
shell_error(sh, "I3C: Target device not found.");
return -ENODEV;
}
} else if (argc == 2) {
/* This gets all "currently attached" I3C and I2C devices */
if (!sys_slist_is_empty(&data->attached_dev.devices.i3c)) {
shell_print(sh, "I3C: Devices found:");
SYS_SLIST_FOR_EACH_NODE(&data->attached_dev.devices.i3c, node) {
struct i3c_device_desc *desc =
CONTAINER_OF(node, struct i3c_device_desc, node);
shell_print(sh,
"name: %s\n"
"\tpid: 0x%012llx\n"
"\tstatic_addr: 0x%02x\n"
"\tdynamic_addr: 0x%02x\n"
#if defined(CONFIG_I3C_USE_GROUP_ADDR)
"\tgroup_addr: 0x%02x\n"
#endif
"\tbcr: 0x%02x\n"
"\tdcr: 0x%02x\n"
"\tmaxrd: 0x%02x\n"
"\tmaxwr: 0x%02x\n"
"\tmax_read_turnaround: 0x%06x\n"
"\tmrl: 0x%04x\n"
"\tmwl: 0x%04x\n"
"\tmax_ibi: 0x%02x\n"
"\tgetcaps: 0x%02x; 0x%02x; 0x%02x; 0x%02x",
desc->dev->name, (uint64_t)desc->pid, desc->static_addr,
desc->dynamic_addr,
#if defined(CONFIG_I3C_USE_GROUP_ADDR)
desc->group_addr,
#endif
desc->bcr, desc->dcr, desc->data_speed.maxrd,
desc->data_speed.maxwr,
desc->data_speed.max_read_turnaround,
desc->data_length.mrl, desc->data_length.mwl,
desc->data_length.max_ibi, desc->getcaps.getcap1,
desc->getcaps.getcap2, desc->getcaps.getcap3,
desc->getcaps.getcap4);
}
} else {
shell_print(sh, "I3C: No devices found.");
}
if (!sys_slist_is_empty(&data->attached_dev.devices.i2c)) {
shell_print(sh, "I2C: Devices found:");
SYS_SLIST_FOR_EACH_NODE(&data->attached_dev.devices.i2c, node) {
struct i3c_i2c_device_desc *desc =
CONTAINER_OF(node, struct i3c_i2c_device_desc, node);
shell_print(sh,
"addr: 0x%02x\n"
"\tlvr: 0x%02x",
desc->addr, desc->lvr);
}
} else {
shell_print(sh, "I2C: No devices found.");
}
} else {
shell_error(sh, "Invalid number of arguments.");
}
return 0;
}
/* i3c speed <device> <speed> */
static int cmd_i3c_speed(const struct shell *sh, size_t argc, char **argv)
{
const struct device *dev;
struct i3c_config_controller config;
uint32_t speed;
int ret;
dev = device_get_binding(argv[ARGV_DEV]);
if (!dev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[1]);
return -ENODEV;
}
speed = strtol(argv[ARGV_DEV + 1], NULL, 10);
ret = i3c_config_get(dev, I3C_CONFIG_CONTROLLER, &config);
if (ret != 0) {
shell_error(sh, "I3C: Failed to retrieve configuration");
return ret;
}
config.scl.i3c = speed;
ret = i3c_configure(dev, I3C_CONFIG_CONTROLLER, &config);
if (ret != 0) {
shell_error(sh, "I3C: Failed to configure device");
return ret;
}
return ret;
}
/* i3c recover <device> */
static int cmd_i3c_recover(const struct shell *sh, size_t argc, char **argv)
{
const struct device *dev;
int err;
dev = device_get_binding(argv[ARGV_DEV]);
if (!dev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[1]);
return -ENODEV;
}
err = i3c_recover_bus(dev);
if (err) {
shell_error(sh, "I3C: Bus recovery failed (err %d)", err);
return err;
}
return 0;
}
static int i3c_write_from_buffer(const struct shell *sh, char *s_dev_name, char *s_tdev_name,
char *s_reg_addr, char **data, uint8_t data_length)
{
/* This buffer must preserve 4 bytes for register address, as it is
* filled using put_be32 function and we don't want to lower available
* space when using 1 byte address.
*/
uint8_t buf[MAX_I3C_BYTES + MAX_BYTES_FOR_REGISTER_INDEX - 1];
const struct device *dev, *tdev;
struct i3c_device_desc *desc;
int reg_addr_bytes;
int reg_addr;
int ret;
int i;
dev = device_get_binding(s_dev_name);
if (!dev) {
shell_error(sh, "I3C: Device driver %s not found.", s_dev_name);
return -ENODEV;
}
tdev = device_get_binding(s_tdev_name);
if (!tdev) {
shell_error(sh, "I3C: Device driver %s not found.", s_tdev_name);
return -ENODEV;
}
desc = get_i3c_attached_desc_from_dev_name(dev, tdev->name);
if (!desc) {
shell_error(sh, "I3C: Device %s not attached to bus.", tdev->name);
return -ENODEV;
}
reg_addr = strtol(s_reg_addr, NULL, 16);
reg_addr_bytes = get_bytes_count_for_hex(s_reg_addr);
sys_put_be32(reg_addr, buf);
if (data_length + reg_addr_bytes > MAX_I3C_BYTES) {
data_length = MAX_I3C_BYTES - reg_addr_bytes;
shell_info(sh, "Too many bytes provided, limit is %d",
MAX_I3C_BYTES - reg_addr_bytes);
}
for (i = 0; i < data_length; i++) {
buf[MAX_BYTES_FOR_REGISTER_INDEX + i] = (uint8_t)strtol(data[i], NULL, 16);
}
ret = i3c_write(desc, buf + MAX_BYTES_FOR_REGISTER_INDEX - reg_addr_bytes,
reg_addr_bytes + data_length);
if (ret < 0) {
shell_error(sh, "Failed to write to device: %s", tdev->name);
return -EIO;
}
return 0;
}
/* i3c write <device> <dev_addr> <reg_addr> [<byte1>, ...] */
static int cmd_i3c_write(const struct shell *sh, size_t argc, char **argv)
{
return i3c_write_from_buffer(sh, argv[ARGV_DEV], argv[ARGV_TDEV], argv[ARGV_REG],
&argv[4], argc - 4);
}
/* i3c write_byte <device> <dev_addr> <reg_addr> <value> */
static int cmd_i3c_write_byte(const struct shell *sh, size_t argc, char **argv)
{
return i3c_write_from_buffer(sh, argv[ARGV_DEV], argv[ARGV_TDEV], argv[ARGV_REG],
&argv[4], 1);
}
static int i3c_read_to_buffer(const struct shell *sh, char *s_dev_name, char *s_tdev_name,
char *s_reg_addr, uint8_t *buf, uint8_t buf_length)
{
const struct device *dev, *tdev;
struct i3c_device_desc *desc;
uint8_t reg_addr_buf[MAX_BYTES_FOR_REGISTER_INDEX];
int reg_addr_bytes;
int reg_addr;
int ret;
dev = device_get_binding(s_dev_name);
if (!dev) {
shell_error(sh, "I3C: Device driver %s not found.", s_dev_name);
return -ENODEV;
}
tdev = device_get_binding(s_tdev_name);
if (!tdev) {
shell_error(sh, "I3C: Device driver %s not found.", s_dev_name);
return -ENODEV;
}
desc = get_i3c_attached_desc_from_dev_name(dev, tdev->name);
if (!desc) {
shell_error(sh, "I3C: Device %s not attached to bus.", tdev->name);
return -ENODEV;
}
reg_addr = strtol(s_reg_addr, NULL, 16);
reg_addr_bytes = get_bytes_count_for_hex(s_reg_addr);
sys_put_be32(reg_addr, reg_addr_buf);
ret = i3c_write_read(desc, reg_addr_buf + MAX_BYTES_FOR_REGISTER_INDEX - reg_addr_bytes,
reg_addr_bytes, buf, buf_length);
if (ret < 0) {
shell_error(sh, "Failed to read from device: %s", tdev->name);
return -EIO;
}
return 0;
}
/* i3c read_byte <device> <target> <reg_addr> */
static int cmd_i3c_read_byte(const struct shell *sh, size_t argc, char **argv)
{
uint8_t out;
int ret;
ret = i3c_read_to_buffer(sh, argv[ARGV_DEV], argv[ARGV_TDEV], argv[ARGV_REG], &out,
1);
if (ret == 0) {
shell_print(sh, "Output: 0x%x", out);
}
return ret;
}
/* i3c read <device> <target> <reg_addr> [<numbytes>] */
static int cmd_i3c_read(const struct shell *sh, size_t argc, char **argv)
{
uint8_t buf[MAX_I3C_BYTES];
int num_bytes;
int ret;
if (argc > 4) {
num_bytes = strtol(argv[4], NULL, 16);
if (num_bytes > MAX_I3C_BYTES) {
num_bytes = MAX_I3C_BYTES;
}
} else {
num_bytes = MAX_I3C_BYTES;
}
ret = i3c_read_to_buffer(sh, argv[ARGV_DEV], argv[ARGV_TDEV], argv[ARGV_REG], buf,
num_bytes);
if (ret == 0) {
shell_hexdump(sh, buf, num_bytes);
}
return ret;
}
/* i3c hdr ddr read <device> <target> <7b cmd> [<byte1>, ...] */
static int cmd_i3c_hdr_ddr_write(const struct shell *sh, size_t argc, char **argv)
{
const struct device *dev, *tdev;
struct i3c_device_desc *desc;
uint8_t buf[MAX_I3C_BYTES];
uint8_t cmd;
uint8_t data_length;
uint8_t i;
int ret;
dev = device_get_binding(argv[ARGV_DEV]);
if (!dev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_DEV]);
return -ENODEV;
}
tdev = device_get_binding(argv[ARGV_TDEV]);
if (!tdev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_TDEV]);
return -ENODEV;
}
desc = get_i3c_attached_desc_from_dev_name(dev, tdev->name);
if (!desc) {
shell_error(sh, "I3C: Device %s not attached to bus.", tdev->name);
return -ENODEV;
}
cmd = strtol(argv[3], NULL, 16);
data_length = argc - 4;
if (data_length > MAX_I3C_BYTES) {
shell_info(sh, "Too many bytes provided, limit is %d", MAX_I3C_BYTES);
}
for (i = 0; i < data_length; i++) {
buf[i] = (uint8_t)strtol(argv[4 + i], NULL, 16);
}
ret = i3c_hdr_ddr_write(desc, cmd, buf, data_length);
if (ret != 0) {
shell_error(sh, "I3C: unable to perform HDR DDR write.");
return ret;
}
return ret;
}
/* i3c hdr ddr read <device> <target> <7b cmd> [<numbytes>] */
static int cmd_i3c_hdr_ddr_read(const struct shell *sh, size_t argc, char **argv)
{
const struct device *dev, *tdev;
struct i3c_device_desc *desc;
uint8_t buf[MAX_I3C_BYTES];
int num_bytes;
uint8_t cmd;
int ret;
dev = device_get_binding(argv[ARGV_DEV]);
if (!dev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_DEV]);
return -ENODEV;
}
tdev = device_get_binding(argv[ARGV_TDEV]);
if (!tdev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_TDEV]);
return -ENODEV;
}
desc = get_i3c_attached_desc_from_dev_name(dev, tdev->name);
if (!desc) {
shell_error(sh, "I3C: Device %s not attached to bus.", tdev->name);
return -ENODEV;
}
cmd = strtol(argv[3], NULL, 16);
if (argc > 4) {
num_bytes = strtol(argv[4], NULL, 16);
if (num_bytes > MAX_I3C_BYTES) {
num_bytes = MAX_I3C_BYTES;
}
} else {
num_bytes = MAX_I3C_BYTES;
}
ret = i3c_hdr_ddr_read(desc, cmd, buf, num_bytes);
if (ret != 0) {
shell_error(sh, "I3C: unable to perform HDR DDR read.");
return ret;
}
shell_hexdump(sh, buf, num_bytes);
return ret;
}
/* i3c ccc rstdaa <device> */
static int cmd_i3c_ccc_rstdaa(const struct shell *sh, size_t argc, char **argv)
{
const struct device *dev;
struct i3c_driver_data *data;
sys_snode_t *node;
int ret;
dev = device_get_binding(argv[ARGV_DEV]);
if (!dev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_DEV]);
return -ENODEV;
}
data = (struct i3c_driver_data *)dev->data;
ret = i3c_ccc_do_rstdaa_all(dev);
if (ret < 0) {
shell_error(sh, "I3C: unable to send CCC RSTDAA.");
return ret;
}
/* reset all devices DA */
if (!sys_slist_is_empty(&data->attached_dev.devices.i3c)) {
SYS_SLIST_FOR_EACH_NODE(&data->attached_dev.devices.i3c, node) {
struct i3c_device_desc *desc =
CONTAINER_OF(node, struct i3c_device_desc, node);
desc->dynamic_addr = 0;
shell_print(sh, "Reset dynamic address for device %s",
desc->dev->name);
}
}
return ret;
}
/* i3c ccc entdaa <device> */
static int cmd_i3c_ccc_entdaa(const struct shell *sh, size_t argc, char **argv)
{
const struct device *dev;
dev = device_get_binding(argv[ARGV_DEV]);
if (!dev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_DEV]);
return -ENODEV;
}
return i3c_do_daa(dev);
}
/* i3c ccc setaasa <device> */
static int cmd_i3c_ccc_setaasa(const struct shell *sh, size_t argc, char **argv)
{
const struct device *dev;
struct i3c_driver_data *data;
sys_snode_t *node;
int ret;
dev = device_get_binding(argv[ARGV_DEV]);
if (!dev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_DEV]);
return -ENODEV;
}
data = (struct i3c_driver_data *)dev->data;
ret = i3c_ccc_do_setaasa_all(dev);
if (ret < 0) {
shell_error(sh, "I3C: unable to send CCC SETAASA.");
return ret;
}
/* set all devices DA to SA */
if (!sys_slist_is_empty(&data->attached_dev.devices.i3c)) {
SYS_SLIST_FOR_EACH_NODE(&data->attached_dev.devices.i3c, node) {
struct i3c_device_desc *desc =
CONTAINER_OF(node, struct i3c_device_desc, node);
if ((desc->supports_setaasa) && (desc->dynamic_addr == 0) &&
(desc->static_addr != 0)) {
desc->dynamic_addr = desc->static_addr;
}
}
}
return ret;
}
/* i3c ccc setdasa <device> <target> */
static int cmd_i3c_ccc_setdasa(const struct shell *sh, size_t argc, char **argv)
{
const struct device *dev, *tdev;
struct i3c_device_desc *desc;
int ret;
dev = device_get_binding(argv[ARGV_DEV]);
if (!dev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_DEV]);
return -ENODEV;
}
tdev = device_get_binding(argv[ARGV_TDEV]);
if (!tdev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_TDEV]);
return -ENODEV;
}
desc = get_i3c_attached_desc_from_dev_name(dev, tdev->name);
if (!desc) {
shell_error(sh, "I3C: Device %s not attached to bus.", tdev->name);
return -ENODEV;
}
ret = i3c_ccc_do_setdasa(desc);
if (ret < 0) {
shell_error(sh, "I3C: unable to send CCC SETDASA.");
return ret;
}
/* update the target's dynamic address */
desc->dynamic_addr = desc->init_dynamic_addr ? desc->init_dynamic_addr : desc->static_addr;
if (desc->dynamic_addr != desc->static_addr) {
ret = i3c_reattach_i3c_device(desc, desc->static_addr);
if (ret < 0) {
shell_error(sh, "I3C: unable to reattach device");
return ret;
}
}
return ret;
}
/* i3c ccc setnewda <device> <target> <dynamic address>*/
static int cmd_i3c_ccc_setnewda(const struct shell *sh, size_t argc, char **argv)
{
const struct device *dev, *tdev;
struct i3c_device_desc *desc;
struct i3c_driver_data *data;
struct i3c_ccc_address new_da;
uint8_t old_da;
int ret;
dev = device_get_binding(argv[ARGV_DEV]);
if (!dev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_DEV]);
return -ENODEV;
}
tdev = device_get_binding(argv[ARGV_TDEV]);
if (!tdev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_TDEV]);
return -ENODEV;
}
desc = get_i3c_attached_desc_from_dev_name(dev, tdev->name);
if (!desc) {
shell_error(sh, "I3C: Device %s not attached to bus.", tdev->name);
return -ENODEV;
}
data = (struct i3c_driver_data *)dev->data;
new_da.addr = strtol(argv[3], NULL, 16);
/* check if the addressed is free */
if (!i3c_addr_slots_is_free(&data->attached_dev.addr_slots, new_da.addr)) {
shell_error(sh, "I3C: Address 0x%02x is already in use.", new_da.addr);
return -EINVAL;
}
ret = i3c_ccc_do_setnewda(desc, new_da);
if (ret < 0) {
shell_error(sh, "I3C: unable to send CCC SETDASA.");
return ret;
}
/* reattach device address */
old_da = desc->dynamic_addr;
desc->dynamic_addr = new_da.addr;
ret = i3c_reattach_i3c_device(desc, old_da);
if (ret < 0) {
shell_error(sh, "I3C: unable to reattach device");
return ret;
}
return ret;
}
/* i3c ccc getbcr <device> <target> */
static int cmd_i3c_ccc_getbcr(const struct shell *sh, size_t argc, char **argv)
{
const struct device *dev, *tdev;
struct i3c_device_desc *desc;
struct i3c_ccc_getbcr bcr;
int ret;
dev = device_get_binding(argv[ARGV_DEV]);
if (!dev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_DEV]);
return -ENODEV;
}
tdev = device_get_binding(argv[ARGV_TDEV]);
if (!tdev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_TDEV]);
return -ENODEV;
}
desc = get_i3c_attached_desc_from_dev_name(dev, tdev->name);
if (!desc) {
shell_error(sh, "I3C: Device %s not attached to bus.", tdev->name);
return -ENODEV;
}
ret = i3c_ccc_do_getbcr(desc, &bcr);
if (ret < 0) {
shell_error(sh, "I3C: unable to send CCC GETBCR.");
return ret;
}
shell_print(sh, "BCR: 0x%02x", bcr.bcr);
desc->bcr = bcr.bcr;
return ret;
}
/* i3c ccc getdcr <device> <target> */
static int cmd_i3c_ccc_getdcr(const struct shell *sh, size_t argc, char **argv)
{
const struct device *dev, *tdev;
struct i3c_device_desc *desc;
struct i3c_ccc_getdcr dcr;
int ret;
dev = device_get_binding(argv[ARGV_DEV]);
if (!dev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_DEV]);
return -ENODEV;
}
tdev = device_get_binding(argv[ARGV_TDEV]);
if (!tdev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_TDEV]);
return -ENODEV;
}
desc = get_i3c_attached_desc_from_dev_name(dev, tdev->name);
if (!desc) {
shell_error(sh, "I3C: Device %s not attached to bus.", tdev->name);
return -ENODEV;
}
ret = i3c_ccc_do_getdcr(desc, &dcr);
if (ret < 0) {
shell_error(sh, "I3C: unable to send CCC GETDCR.");
return ret;
}
shell_print(sh, "DCR: 0x%02x", dcr.dcr);
desc->dcr = dcr.dcr;
return ret;
}
/* i3c ccc getpid <device> <target> */
static int cmd_i3c_ccc_getpid(const struct shell *sh, size_t argc, char **argv)
{
const struct device *dev, *tdev;
struct i3c_device_desc *desc;
struct i3c_ccc_getpid pid;
int ret;
dev = device_get_binding(argv[ARGV_DEV]);
if (!dev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_DEV]);
return -ENODEV;
}
tdev = device_get_binding(argv[ARGV_TDEV]);
if (!tdev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_TDEV]);
return -ENODEV;
}
desc = get_i3c_attached_desc_from_dev_name(dev, tdev->name);
if (!desc) {
shell_error(sh, "I3C: Device %s not attached to bus.", tdev->name);
return -ENODEV;
}
ret = i3c_ccc_do_getpid(desc, &pid);
if (ret < 0) {
shell_error(sh, "I3C: unable to send CCC GETPID.");
return ret;
}
shell_print(sh, "PID: 0x%012llx", sys_get_be48(pid.pid));
return ret;
}
/* i3c ccc getmrl <device> <target> */
static int cmd_i3c_ccc_getmrl(const struct shell *sh, size_t argc, char **argv)
{
const struct device *dev, *tdev;
struct i3c_device_desc *desc;
struct i3c_ccc_mrl mrl;
int ret;
dev = device_get_binding(argv[ARGV_DEV]);
if (!dev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_DEV]);
return -ENODEV;
}
tdev = device_get_binding(argv[ARGV_TDEV]);
if (!tdev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_TDEV]);
return -ENODEV;
}
desc = get_i3c_attached_desc_from_dev_name(dev, tdev->name);
if (!desc) {
shell_error(sh, "I3C: Device %s not attached to bus.", tdev->name);
return -ENODEV;
}
ret = i3c_ccc_do_getmrl(desc, &mrl);
if (ret < 0) {
shell_error(sh, "I3C: unable to send CCC GETMRL.");
return ret;
}
desc->data_length.mrl = mrl.len;
if (desc->bcr & I3C_BCR_IBI_PAYLOAD_HAS_DATA_BYTE) {
shell_print(sh, "MRL: 0x%04x; IBI Length:0x%02x", mrl.len, mrl.ibi_len);
desc->data_length.max_ibi = mrl.ibi_len;
} else {
shell_print(sh, "MRL: 0x%04x", mrl.len);
desc->data_length.max_ibi = 0;
}
return ret;
}
/* i3c ccc getmwl <device> <target> */
static int cmd_i3c_ccc_getmwl(const struct shell *sh, size_t argc, char **argv)
{
const struct device *dev, *tdev;
struct i3c_device_desc *desc;
struct i3c_ccc_mwl mwl;
int ret;
dev = device_get_binding(argv[ARGV_DEV]);
if (!dev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_DEV]);
return -ENODEV;
}
tdev = device_get_binding(argv[ARGV_TDEV]);
if (!tdev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_TDEV]);
return -ENODEV;
}
desc = get_i3c_attached_desc_from_dev_name(dev, tdev->name);
if (!desc) {
shell_error(sh, "I3C: Device %s not attached to bus.", tdev->name);
return -ENODEV;
}
ret = i3c_ccc_do_getmwl(desc, &mwl);
if (ret < 0) {
shell_error(sh, "I3C: unable to send CCC GETMWL.");
return ret;
}
shell_print(sh, "MWL: 0x%04x", mwl.len);
desc->data_length.mwl = mwl.len;
return ret;
}
/* i3c ccc setmrl <device> <target> <max read length> [<max ibi length>] */
static int cmd_i3c_ccc_setmrl(const struct shell *sh, size_t argc, char **argv)
{
const struct device *dev, *tdev;
struct i3c_device_desc *desc;
struct i3c_ccc_mrl mrl;
int ret;
dev = device_get_binding(argv[ARGV_DEV]);
if (!dev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_DEV]);
return -ENODEV;
}
tdev = device_get_binding(argv[ARGV_TDEV]);
if (!tdev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_TDEV]);
return -ENODEV;
}
desc = get_i3c_attached_desc_from_dev_name(dev, tdev->name);
if (!desc) {
shell_error(sh, "I3C: Device %s not attached to bus.", tdev->name);
return -ENODEV;
}
/* IBI length is required if the ibi payload bit is set */
if ((desc->bcr & I3C_BCR_IBI_PAYLOAD_HAS_DATA_BYTE) && (argc < 4)) {
shell_error(sh, "I3C: Missing IBI length.");
return -EINVAL;
}
mrl.len = strtol(argv[3], NULL, 16);
if (argc > 3) {
mrl.ibi_len = strtol(argv[4], NULL, 16);
}
ret = i3c_ccc_do_setmrl(desc, &mrl);
if (ret < 0) {
shell_error(sh, "I3C: unable to send CCC SETMRL.");
return ret;
}
desc->data_length.mrl = mrl.len;
if (argc > 3) {
desc->data_length.max_ibi = mrl.ibi_len;
}
return ret;
}
/* i3c ccc setmwl <device> <target> <max write length> */
static int cmd_i3c_ccc_setmwl(const struct shell *sh, size_t argc, char **argv)
{
const struct device *dev, *tdev;
struct i3c_device_desc *desc;
struct i3c_ccc_mwl mwl;
int ret;
dev = device_get_binding(argv[ARGV_DEV]);
if (!dev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_DEV]);
return -ENODEV;
}
tdev = device_get_binding(argv[ARGV_TDEV]);
if (!tdev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_TDEV]);
return -ENODEV;
}
desc = get_i3c_attached_desc_from_dev_name(dev, tdev->name);
if (!desc) {
shell_error(sh, "I3C: Device %s not attached to bus.", tdev->name);
return -ENODEV;
}
mwl.len = strtol(argv[3], NULL, 16);
ret = i3c_ccc_do_setmwl(desc, &mwl);
if (ret < 0) {
shell_error(sh, "I3C: unable to send CCC SETMWL.");
return ret;
}
desc->data_length.mwl = mwl.len;
return ret;
}
/* i3c ccc setmrl_bc <device> <max read length> [<max ibi length>] */
static int cmd_i3c_ccc_setmrl_bc(const struct shell *sh, size_t argc, char **argv)
{
const struct device *dev;
struct i3c_driver_data *data;
sys_snode_t *node;
struct i3c_ccc_mrl mrl;
int ret;
dev = device_get_binding(argv[ARGV_DEV]);
if (!dev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_DEV]);
return -ENODEV;
}
data = (struct i3c_driver_data *)dev->data;
mrl.len = strtol(argv[2], NULL, 16);
if (argc > 3) {
mrl.ibi_len = strtol(argv[3], NULL, 16);
}
ret = i3c_ccc_do_setmrl_all(dev, &mrl, argc > 3);
if (ret < 0) {
shell_error(sh, "I3C: unable to send CCC SETMRL BC.");
return ret;
}
if (!sys_slist_is_empty(&data->attached_dev.devices.i3c)) {
SYS_SLIST_FOR_EACH_NODE(&data->attached_dev.devices.i3c, node) {
struct i3c_device_desc *desc =
CONTAINER_OF(node, struct i3c_device_desc, node);
desc->data_length.mrl = mrl.len;
if ((argc > 3) && (desc->bcr & I3C_BCR_IBI_PAYLOAD_HAS_DATA_BYTE)) {
desc->data_length.max_ibi = mrl.ibi_len;
}
}
}
return ret;
}
/* i3c ccc setmwl_bc <device> <max write length> */
static int cmd_i3c_ccc_setmwl_bc(const struct shell *sh, size_t argc, char **argv)
{
const struct device *dev;
struct i3c_driver_data *data;
sys_snode_t *node;
struct i3c_ccc_mwl mwl;
int ret;
dev = device_get_binding(argv[ARGV_DEV]);
if (!dev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_DEV]);
return -ENODEV;
}
data = (struct i3c_driver_data *)dev->data;
mwl.len = strtol(argv[3], NULL, 16);
ret = i3c_ccc_do_setmwl_all(dev, &mwl);
if (ret < 0) {
shell_error(sh, "I3C: unable to send CCC SETMWL BC.");
return ret;
}
if (!sys_slist_is_empty(&data->attached_dev.devices.i3c)) {
SYS_SLIST_FOR_EACH_NODE(&data->attached_dev.devices.i3c, node) {
struct i3c_device_desc *desc =
CONTAINER_OF(node, struct i3c_device_desc, node);
desc->data_length.mwl = mwl.len;
}
}
return ret;
}
/* i3c ccc deftgts <device> */
static int cmd_i3c_ccc_deftgts(const struct shell *sh, size_t argc, char **argv)
{
const struct device *dev;
int ret;
dev = device_get_binding(argv[ARGV_DEV]);
if (!dev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_DEV]);
return -ENODEV;
}
if (!i3c_bus_has_sec_controller(dev)) {
shell_error(sh, "I3C: No secondary controller on the bus");
return -ENXIO;
}
ret = i3c_bus_deftgts(dev);
if (ret < 0) {
shell_error(sh, "I3C: unable to send CCC DEFTGTS.");
return ret;
}
return ret;
}
/* i3c ccc enttm <device> <defining byte> */
static int cmd_i3c_ccc_enttm(const struct shell *sh, size_t argc, char **argv)
{
const struct device *dev;
enum i3c_ccc_enttm_defbyte defbyte;
int ret;
dev = device_get_binding(argv[ARGV_DEV]);
if (!dev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_DEV]);
return -ENODEV;
}
defbyte = strtol(argv[2], NULL, 16);
ret = i3c_ccc_do_enttm(dev, defbyte);
if (ret < 0) {
shell_error(sh, "I3C: unable to send CCC ENTTM.");
return ret;
}
return ret;
}
/* i3c ccc rstact_bc <device> <defining byte> */
static int cmd_i3c_ccc_rstact_bc(const struct shell *sh, size_t argc, char **argv)
{
const struct device *dev;
enum i3c_ccc_rstact_defining_byte action;
int ret;
dev = device_get_binding(argv[ARGV_DEV]);
if (!dev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_DEV]);
return -ENODEV;
}
action = strtol(argv[2], NULL, 16);
ret = i3c_ccc_do_rstact_all(dev, action);
if (ret < 0) {
shell_error(sh, "I3C: unable to send CCC RSTACT BC.");
return ret;
}
return ret;
}
/* i3c ccc enec_bc <device> <defining byte> */
static int cmd_i3c_ccc_enec_bc(const struct shell *sh, size_t argc, char **argv)
{
const struct device *dev;
struct i3c_ccc_events events;
int ret;
dev = device_get_binding(argv[ARGV_DEV]);
if (!dev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_DEV]);
return -ENODEV;
}
events.events = strtol(argv[2], NULL, 16);
ret = i3c_ccc_do_events_all_set(dev, true, &events);
if (ret < 0) {
shell_error(sh, "I3C: unable to send CCC ENEC BC.");
return ret;
}
return ret;
}
/* i3c ccc disec_bc <device> <defining byte> */
static int cmd_i3c_ccc_disec_bc(const struct shell *sh, size_t argc, char **argv)
{
const struct device *dev;
struct i3c_ccc_events events;
int ret;
dev = device_get_binding(argv[ARGV_DEV]);
if (!dev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_DEV]);
return -ENODEV;
}
events.events = strtol(argv[2], NULL, 16);
ret = i3c_ccc_do_events_all_set(dev, false, &events);
if (ret < 0) {
shell_error(sh, "I3C: unable to send CCC ENEC BC.");
return ret;
}
return ret;
}
/* i3c ccc enec <device> <target> <defining byte> */
static int cmd_i3c_ccc_enec(const struct shell *sh, size_t argc, char **argv)
{
const struct device *dev, *tdev;
struct i3c_device_desc *desc;
struct i3c_ccc_events events;
int ret;
dev = device_get_binding(argv[ARGV_DEV]);
if (!dev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_DEV]);
return -ENODEV;
}
tdev = device_get_binding(argv[ARGV_TDEV]);
if (!tdev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_TDEV]);
return -ENODEV;
}
desc = get_i3c_attached_desc_from_dev_name(dev, tdev->name);
if (!desc) {
shell_error(sh, "I3C: Device %s not attached to bus.", tdev->name);
return -ENODEV;
}
events.events = strtol(argv[3], NULL, 16);
ret = i3c_ccc_do_events_set(desc, true, &events);
if (ret < 0) {
shell_error(sh, "I3C: unable to send CCC ENEC BC.");
return ret;
}
return ret;
}
/* i3c ccc disec <device> <target> <defining byte> */
static int cmd_i3c_ccc_disec(const struct shell *sh, size_t argc, char **argv)
{
const struct device *dev, *tdev;
struct i3c_device_desc *desc;
struct i3c_ccc_events events;
int ret;
dev = device_get_binding(argv[ARGV_DEV]);
if (!dev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_DEV]);
return -ENODEV;
}
tdev = device_get_binding(argv[ARGV_TDEV]);
if (!tdev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_TDEV]);
return -ENODEV;
}
desc = get_i3c_attached_desc_from_dev_name(dev, tdev->name);
if (!desc) {
shell_error(sh, "I3C: Device %s not attached to bus.", tdev->name);
return -ENODEV;
}
events.events = strtol(argv[3], NULL, 16);
ret = i3c_ccc_do_events_set(desc, false, &events);
if (ret < 0) {
shell_error(sh, "I3C: unable to send CCC ENEC BC.");
return ret;
}
return ret;
}
/* i3c ccc entas0_bc <device> */
static int cmd_i3c_ccc_entas0_bc(const struct shell *sh, size_t argc, char **argv)
{
const struct device *dev;
struct i3c_driver_data *data;
int ret;
dev = device_get_binding(argv[ARGV_DEV]);
if (!dev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_DEV]);
return -ENODEV;
}
data = (struct i3c_driver_data *)dev->data;
ret = i3c_ccc_do_entas0_all(dev);
if (ret < 0) {
shell_error(sh, "I3C: unable to send CCC ENTAS0 BC.");
return ret;
}
return ret;
}
/* i3c ccc entas1_bc <device> */
static int cmd_i3c_ccc_entas1_bc(const struct shell *sh, size_t argc, char **argv)
{
const struct device *dev;
struct i3c_driver_data *data;
int ret;
dev = device_get_binding(argv[ARGV_DEV]);
if (!dev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_DEV]);
return -ENODEV;
}
data = (struct i3c_driver_data *)dev->data;
ret = i3c_ccc_do_entas1_all(dev);
if (ret < 0) {
shell_error(sh, "I3C: unable to send CCC ENTAS1 BC.");
return ret;
}
return ret;
}
/* i3c ccc entas2_bc <device> */
static int cmd_i3c_ccc_entas2_bc(const struct shell *sh, size_t argc, char **argv)
{
const struct device *dev;
struct i3c_driver_data *data;
int ret;
dev = device_get_binding(argv[ARGV_DEV]);
if (!dev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_DEV]);
return -ENODEV;
}
data = (struct i3c_driver_data *)dev->data;
ret = i3c_ccc_do_entas2_all(dev);
if (ret < 0) {
shell_error(sh, "I3C: unable to send CCC ENTAS2 BC.");
return ret;
}
return ret;
}
/* i3c ccc entas3_bc <device> */
static int cmd_i3c_ccc_entas3_bc(const struct shell *sh, size_t argc, char **argv)
{
const struct device *dev;
struct i3c_driver_data *data;
int ret;
dev = device_get_binding(argv[ARGV_DEV]);
if (!dev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_DEV]);
return -ENODEV;
}
data = (struct i3c_driver_data *)dev->data;
ret = i3c_ccc_do_entas3_all(dev);
if (ret < 0) {
shell_error(sh, "I3C: unable to send CCC ENTAS3 BC.");
return ret;
}
return ret;
}
/* i3c ccc entas0 <device> <target> */
static int cmd_i3c_ccc_entas0(const struct shell *sh, size_t argc, char **argv)
{
const struct device *dev, *tdev;
struct i3c_device_desc *desc;
int ret;
dev = device_get_binding(argv[ARGV_DEV]);
if (!dev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_DEV]);
return -ENODEV;
}
tdev = device_get_binding(argv[ARGV_TDEV]);
if (!tdev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_TDEV]);
return -ENODEV;
}
desc = get_i3c_attached_desc_from_dev_name(dev, tdev->name);
if (!desc) {
shell_error(sh, "I3C: Device %s not attached to bus.", tdev->name);
return -ENODEV;
}
ret = i3c_ccc_do_entas0(desc);
if (ret < 0) {
shell_error(sh, "I3C: unable to send CCC ENTAS0.");
return ret;
}
return ret;
}
/* i3c ccc entas1 <device> <target> */
static int cmd_i3c_ccc_entas1(const struct shell *sh, size_t argc, char **argv)
{
const struct device *dev, *tdev;
struct i3c_device_desc *desc;
int ret;
dev = device_get_binding(argv[ARGV_DEV]);
if (!dev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_DEV]);
return -ENODEV;
}
tdev = device_get_binding(argv[ARGV_TDEV]);
if (!tdev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_TDEV]);
return -ENODEV;
}
desc = get_i3c_attached_desc_from_dev_name(dev, tdev->name);
if (!desc) {
shell_error(sh, "I3C: Device %s not attached to bus.", tdev->name);
return -ENODEV;
}
ret = i3c_ccc_do_entas1(desc);
if (ret < 0) {
shell_error(sh, "I3C: unable to send CCC ENTAS1.");
return ret;
}
return ret;
}
/* i3c ccc entas2 <device> <target> */
static int cmd_i3c_ccc_entas2(const struct shell *sh, size_t argc, char **argv)
{
const struct device *dev, *tdev;
struct i3c_device_desc *desc;
int ret;
dev = device_get_binding(argv[ARGV_DEV]);
if (!dev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_DEV]);
return -ENODEV;
}
tdev = device_get_binding(argv[ARGV_TDEV]);
if (!tdev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_TDEV]);
return -ENODEV;
}
desc = get_i3c_attached_desc_from_dev_name(dev, tdev->name);
if (!desc) {
shell_error(sh, "I3C: Device %s not attached to bus.", tdev->name);
return -ENODEV;
}
ret = i3c_ccc_do_entas2(desc);
if (ret < 0) {
shell_error(sh, "I3C: unable to send CCC ENTAS2.");
return ret;
}
return ret;
}
/* i3c ccc entas3 <device> <target> */
static int cmd_i3c_ccc_entas3(const struct shell *sh, size_t argc, char **argv)
{
const struct device *dev, *tdev;
struct i3c_device_desc *desc;
int ret;
dev = device_get_binding(argv[ARGV_DEV]);
if (!dev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_DEV]);
return -ENODEV;
}
tdev = device_get_binding(argv[ARGV_TDEV]);
if (!tdev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_TDEV]);
return -ENODEV;
}
desc = get_i3c_attached_desc_from_dev_name(dev, tdev->name);
if (!desc) {
shell_error(sh, "I3C: Device %s not attached to bus.", tdev->name);
return -ENODEV;
}
ret = i3c_ccc_do_entas3(desc);
if (ret < 0) {
shell_error(sh, "I3C: unable to send CCC ENTAS3.");
return ret;
}
return ret;
}
/* i3c ccc getstatus <device> <target> [<defining byte>] */
static int cmd_i3c_ccc_getstatus(const struct shell *sh, size_t argc, char **argv)
{
const struct device *dev, *tdev;
struct i3c_device_desc *desc;
union i3c_ccc_getstatus status;
enum i3c_ccc_getstatus_fmt fmt;
enum i3c_ccc_getstatus_defbyte defbyte = GETSTATUS_FORMAT_2_INVALID;
int ret;
dev = device_get_binding(argv[ARGV_DEV]);
if (!dev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_DEV]);
return -ENODEV;
}
tdev = device_get_binding(argv[ARGV_TDEV]);
if (!tdev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_TDEV]);
return -ENODEV;
}
desc = get_i3c_attached_desc_from_dev_name(dev, tdev->name);
if (!desc) {
shell_error(sh, "I3C: Device %s not attached to bus.", tdev->name);
return -ENODEV;
}
/* If there is a defining byte, then it is assumed to be Format 2*/
if (argc > 3) {
fmt = GETSTATUS_FORMAT_2;
defbyte = strtol(argv[3], NULL, 16);
if (defbyte != GETSTATUS_FORMAT_2_TGTSTAT || defbyte != GETSTATUS_FORMAT_2_PRECR) {
shell_error(sh, "Invalid defining byte.");
return -EINVAL;
}
} else {
fmt = GETSTATUS_FORMAT_1;
}
ret = i3c_ccc_do_getstatus(desc, &status, fmt, defbyte);
if (ret < 0) {
shell_error(sh, "I3C: unable to send CCC GETSTATUS.");
return ret;
}
if (fmt == GETSTATUS_FORMAT_2) {
if (defbyte == GETSTATUS_FORMAT_2_TGTSTAT) {
shell_print(sh, "TGTSTAT: 0x%04x", status.fmt2.tgtstat);
} else if (defbyte == GETSTATUS_FORMAT_2_PRECR) {
shell_print(sh, "PRECR: 0x%04x", status.fmt2.precr);
}
} else {
shell_print(sh, "Status: 0x%04x", status.fmt1.status);
}
return ret;
}
/* i3c ccc getcaps <device> <target> [<defining byte>] */
static int cmd_i3c_ccc_getcaps(const struct shell *sh, size_t argc, char **argv)
{
const struct device *dev, *tdev;
struct i3c_device_desc *desc;
union i3c_ccc_getcaps caps;
enum i3c_ccc_getcaps_fmt fmt;
enum i3c_ccc_getcaps_defbyte defbyte = GETCAPS_FORMAT_2_INVALID;
int ret;
dev = device_get_binding(argv[ARGV_DEV]);
if (!dev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_DEV]);
return -ENODEV;
}
tdev = device_get_binding(argv[ARGV_TDEV]);
if (!tdev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_TDEV]);
return -ENODEV;
}
desc = get_i3c_attached_desc_from_dev_name(dev, tdev->name);
if (!desc) {
shell_error(sh, "I3C: Device %s not attached to bus.", tdev->name);
return -ENODEV;
}
/* If there is a defining byte, then it is assumed to be Format 2 */
if (argc > 3) {
fmt = GETCAPS_FORMAT_2;
defbyte = strtol(argv[3], NULL, 16);
if (defbyte != GETCAPS_FORMAT_2_TGTCAPS || defbyte != GETCAPS_FORMAT_2_TESTPAT ||
defbyte != GETCAPS_FORMAT_2_CRCAPS || defbyte != GETCAPS_FORMAT_2_VTCAPS ||
defbyte != GETCAPS_FORMAT_2_DBGCAPS) {
shell_error(sh, "Invalid defining byte.");
return -EINVAL;
}
} else {
fmt = GETCAPS_FORMAT_1;
}
ret = i3c_ccc_do_getcaps(desc, &caps, fmt, defbyte);
if (ret < 0) {
shell_error(sh, "I3C: unable to send CCC GETCAPS.");
return ret;
}
if (fmt == GETCAPS_FORMAT_2) {
if (defbyte == GETCAPS_FORMAT_2_TGTCAPS) {
shell_print(sh, "TGTCAPS: 0x%02x; 0x%02x; 0x%02x; 0x%02x",
caps.fmt2.tgtcaps[0], caps.fmt2.tgtcaps[1],
caps.fmt2.tgtcaps[2], caps.fmt2.tgtcaps[3]);
} else if (defbyte == GETCAPS_FORMAT_2_TESTPAT) {
shell_print(sh, "TESTPAT: 0x%08x", caps.fmt2.testpat);
} else if (defbyte == GETCAPS_FORMAT_2_CRCAPS) {
shell_print(sh, "CRCAPS: 0x%02x; 0x%02x", caps.fmt2.crcaps[0],
caps.fmt2.crcaps[1]);
} else if (defbyte == GETCAPS_FORMAT_2_VTCAPS) {
shell_print(sh, "VTCAPS: 0x%02x; 0x%02x", caps.fmt2.vtcaps[0],
caps.fmt2.vtcaps[1]);
}
} else {
shell_print(sh, "GETCAPS: 0x%02x; 0x%02x; 0x%02x; 0x%02x",
caps.fmt1.getcaps[0], caps.fmt1.getcaps[1], caps.fmt1.getcaps[2],
caps.fmt1.getcaps[3]);
memcpy(&desc->getcaps, &caps, sizeof(desc->getcaps));
}
return ret;
}
/* i3c ccc getvendor <device> <target> <id> [<defining byte>] */
static int cmd_i3c_ccc_getvendor(const struct shell *sh, size_t argc, char **argv)
{
const struct device *dev, *tdev;
struct i3c_device_desc *desc;
uint8_t buf[MAX_I3C_BYTES] = {0};
uint8_t defbyte;
size_t num_xfer;
uint8_t id;
int err = 0;
int ret;
dev = device_get_binding(argv[ARGV_DEV]);
if (!dev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_DEV]);
return -ENODEV;
}
tdev = device_get_binding(argv[ARGV_TDEV]);
if (!tdev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_TDEV]);
return -ENODEV;
}
desc = get_i3c_attached_desc_from_dev_name(dev, tdev->name);
if (!desc) {
shell_error(sh, "I3C: Device %s not attached to bus.", tdev->name);
return -ENODEV;
}
id = (uint8_t)shell_strtoul(argv[3], 0, &err);
if (err != 0) {
shell_error(sh, "I3C: Invalid ID.");
return -EINVAL;
}
if (argc > 4) {
defbyte = strtol(argv[4], NULL, 16);
ret = i3c_ccc_do_getvendor_defbyte(desc, id, defbyte, buf, MAX_I3C_BYTES,
&num_xfer);
} else {
ret = i3c_ccc_do_getvendor(desc, id, buf, MAX_I3C_BYTES, &num_xfer);
}
if (ret < 0) {
shell_error(sh, "I3C: unable to send CCC VENDOR.");
return ret;
}
shell_hexdump(sh, buf, num_xfer);
return ret;
}
/* i3c ccc setvendor <device> <target> <id> [<bytes>] */
static int cmd_i3c_ccc_setvendor(const struct shell *sh, size_t argc, char **argv)
{
const struct device *dev, *tdev;
struct i3c_device_desc *desc;
struct i3c_driver_data *data;
uint8_t buf[MAX_I3C_BYTES] = {0};
uint8_t data_length;
uint8_t id;
int err = 0;
int ret;
int i;
dev = device_get_binding(argv[ARGV_DEV]);
if (!dev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_DEV]);
return -ENODEV;
}
tdev = device_get_binding(argv[ARGV_TDEV]);
if (!tdev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_TDEV]);
return -ENODEV;
}
desc = get_i3c_attached_desc_from_dev_name(dev, tdev->name);
if (!desc) {
shell_error(sh, "I3C: Device %s not attached to bus.", tdev->name);
return -ENODEV;
}
data = (struct i3c_driver_data *)dev->data;
id = (uint8_t)shell_strtoul(argv[3], 0, &err);
if (err != 0) {
shell_error(sh, "I3C: Invalid ID.");
return -EINVAL;
}
data_length = argc - 4;
for (i = 0; i < data_length; i++) {
buf[i] = (uint8_t)strtol(argv[4 + i], NULL, 16);
}
ret = i3c_ccc_do_setvendor(desc, id, buf, data_length);
if (ret < 0) {
shell_error(sh, "I3C: unable to send CCC VENDOR.");
return ret;
}
return ret;
}
/* i3c ccc setvendor_bc <device> <id> [<bytes>] */
static int cmd_i3c_ccc_setvendor_bc(const struct shell *sh, size_t argc, char **argv)
{
const struct device *dev;
uint8_t buf[MAX_I3C_BYTES] = {0};
uint8_t data_length;
uint8_t id;
int err = 0;
int ret;
int i;
dev = device_get_binding(argv[ARGV_DEV]);
if (!dev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_DEV]);
return -ENODEV;
}
id = (uint8_t)shell_strtoul(argv[2], 0, &err);
if (err != 0) {
shell_error(sh, "I3C: Invalid ID.");
return -EINVAL;
}
data_length = argc - 3;
for (i = 0; i < data_length; i++) {
buf[i] = (uint8_t)strtol(argv[3 + i], NULL, 16);
}
ret = i3c_ccc_do_setvendor_all(dev, id, buf, data_length);
if (ret < 0) {
shell_error(sh, "I3C: unable to send CCC VENDOR.");
return ret;
}
return ret;
}
/* i3c ccc getmxds <device> <target> [<defining byte>] */
static int cmd_i3c_ccc_getmxds(const struct shell *sh, size_t argc, char **argv)
{
const struct device *dev, *tdev;
struct i3c_device_desc *desc;
union i3c_ccc_getmxds mxds;
enum i3c_ccc_getmxds_fmt fmt;
enum i3c_ccc_getmxds_defbyte defbyte = GETMXDS_FORMAT_3_INVALID;
int ret;
dev = device_get_binding(argv[ARGV_DEV]);
if (!dev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_DEV]);
return -ENODEV;
}
tdev = device_get_binding(argv[ARGV_TDEV]);
if (!tdev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_TDEV]);
return -ENODEV;
}
desc = get_i3c_attached_desc_from_dev_name(dev, tdev->name);
if (!desc) {
shell_error(sh, "I3C: Device %s not attached to bus.", tdev->name);
return -ENODEV;
}
if (!(desc->bcr & I3C_BCR_MAX_DATA_SPEED_LIMIT)) {
shell_error(sh, "I3C: Device %s does not support max data speed limit",
desc->dev->name);
return -ENOTSUP;
}
/* If there is a defining byte, then it is assumed to be Format 3 */
if (argc > 3) {
fmt = GETMXDS_FORMAT_3;
defbyte = strtol(argv[3], NULL, 16);
if (defbyte != GETMXDS_FORMAT_3_CRHDLY || defbyte != GETMXDS_FORMAT_3_WRRDTURN) {
shell_error(sh, "Invalid defining byte.");
return -EINVAL;
}
} else {
fmt = GETMXDS_FORMAT_2;
}
ret = i3c_ccc_do_getmxds(desc, &mxds, fmt, defbyte);
if (ret < 0) {
shell_error(sh, "I3C: unable to send CCC GETMXDS.");
return ret;
}
if (fmt == GETMXDS_FORMAT_3) {
if (defbyte == GETMXDS_FORMAT_3_WRRDTURN) {
shell_print(sh,
"WRRDTURN: maxwr 0x%02x; maxrd 0x%02x; maxrdturn 0x%06x",
mxds.fmt3.wrrdturn[0], mxds.fmt3.wrrdturn[1],
sys_get_le24(&mxds.fmt3.wrrdturn[2]));
/* Update values in descriptor */
desc->data_speed.maxwr = mxds.fmt3.wrrdturn[0];
desc->data_speed.maxrd = mxds.fmt3.wrrdturn[1];
desc->data_speed.max_read_turnaround = sys_get_le24(&mxds.fmt3.wrrdturn[2]);
} else if (defbyte == GETMXDS_FORMAT_3_CRHDLY) {
shell_print(sh, "CRHDLY1: 0x%02x", mxds.fmt3.crhdly1);
}
} else {
shell_print(sh, "GETMXDS: maxwr 0x%02x; maxrd 0x%02x; maxrdturn 0x%06x",
mxds.fmt2.maxwr, mxds.fmt2.maxrd, sys_get_le24(mxds.fmt2.maxrdturn));
/* Update values in descriptor */
desc->data_speed.maxwr = mxds.fmt2.maxwr;
desc->data_speed.maxrd = mxds.fmt2.maxrd;
desc->data_speed.max_read_turnaround = sys_get_le24(mxds.fmt2.maxrdturn);
}
return ret;
}
static int cmd_i3c_attach(const struct shell *sh, size_t argc, char **argv)
{
const struct device *dev, *tdev;
struct i3c_device_desc *desc;
int ret;
dev = device_get_binding(argv[ARGV_DEV]);
if (!dev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_DEV]);
return -ENODEV;
}
tdev = device_get_binding(argv[ARGV_TDEV]);
if (!tdev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_TDEV]);
return -ENODEV;
}
desc = get_i3c_list_desc_from_dev_name(dev, tdev->name);
if (!desc) {
shell_error(sh, "I3C: Device %s not attached to bus.", tdev->name);
return -ENODEV;
}
ret = i3c_attach_i3c_device(desc);
if (ret < 0) {
shell_error(sh, "I3C: unable to attach device %s.", tdev->name);
}
return ret;
}
static int cmd_i3c_reattach(const struct shell *sh, size_t argc, char **argv)
{
const struct device *dev, *tdev;
struct i3c_device_desc *desc;
uint8_t old_dyn_addr = 0;
int ret;
dev = device_get_binding(argv[ARGV_DEV]);
if (!dev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_DEV]);
return -ENODEV;
}
tdev = device_get_binding(argv[ARGV_TDEV]);
if (!tdev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_TDEV]);
return -ENODEV;
}
desc = get_i3c_attached_desc_from_dev_name(dev, tdev->name);
if (!desc) {
shell_error(sh, "I3C: Device %s not attached to bus.", tdev->name);
return -ENODEV;
}
if (argc > 2) {
old_dyn_addr = strtol(argv[2], NULL, 16);
}
ret = i3c_reattach_i3c_device(desc, old_dyn_addr);
if (ret < 0) {
shell_error(sh, "I3C: unable to reattach device %s.", tdev->name);
}
return ret;
}
static int cmd_i3c_detach(const struct shell *sh, size_t argc, char **argv)
{
const struct device *dev, *tdev;
struct i3c_device_desc *desc;
int ret;
dev = device_get_binding(argv[ARGV_DEV]);
if (!dev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_DEV]);
return -ENODEV;
}
tdev = device_get_binding(argv[ARGV_TDEV]);
if (!tdev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_TDEV]);
return -ENODEV;
}
desc = get_i3c_attached_desc_from_dev_name(dev, tdev->name);
if (!desc) {
shell_error(sh, "I3C: Device %s not attached to bus.", tdev->name);
return -ENODEV;
}
ret = i3c_detach_i3c_device(desc);
if (ret < 0) {
shell_error(sh, "I3C: unable to detach device %s.", tdev->name);
}
return ret;
}
static int cmd_i3c_i2c_attach(const struct shell *sh, size_t argc, char **argv)
{
const struct device *dev;
struct i3c_i2c_device_desc *desc;
uint16_t addr = 0;
int ret;
dev = device_get_binding(argv[ARGV_DEV]);
if (!dev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_DEV]);
return -ENODEV;
}
addr = strtol(argv[2], NULL, 16);
desc = get_i3c_i2c_list_desc_from_addr(dev, addr);
if (!desc) {
shell_error(sh, "I3C: I2C addr 0x%02x not listed with the bus.", addr);
return -ENODEV;
}
ret = i3c_attach_i2c_device(desc);
if (ret < 0) {
shell_error(sh, "I3C: unable to attach I2C addr 0x%02x.", addr);
}
return ret;
}
static int cmd_i3c_i2c_detach(const struct shell *sh, size_t argc, char **argv)
{
const struct device *dev;
struct i3c_i2c_device_desc *desc;
uint16_t addr = 0;
int ret;
dev = device_get_binding(argv[ARGV_DEV]);
if (!dev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_DEV]);
return -ENODEV;
}
addr = strtol(argv[2], NULL, 16);
desc = get_i3c_i2c_list_desc_from_addr(dev, addr);
if (!desc) {
shell_error(sh, "I3C: I2C addr 0x%02x not listed with the bus.", addr);
return -ENODEV;
}
ret = i3c_detach_i2c_device(desc);
if (ret < 0) {
shell_error(sh, "I3C: unable to detach I2C addr 0x%02x.", addr);
}
return ret;
}
/*
* This is a workaround command to perform an I2C Scan which is not as
* simple on an I3C bus as it is with the I2C Shell.
*
* This will print "I3" if an address is already assigned for an I3C
* device and it will print "I2" if an address is already assigned for
* an I2C device. It will print RS, if the address is reserved according
* to section 5.1.2.2.5 I3C Target Address Restrictions in I3C v1.1.1.
*
* This sends I2C messages without any data (i.e. stop condition after
* sending just the address). If there is an ACK for the address, it
* is assumed there is a device present.
*
* WARNING: As there is no standard I2C detection command, this code
* uses arbitrary SMBus commands (namely SMBus quick write and SMBus
* receive byte) to probe for devices. This operation can confuse
* your I2C bus, cause data loss, and is known to corrupt the Atmel
* AT24RF08 EEPROM found on many IBM Thinkpad laptops.
*
* https://manpages.debian.org/buster/i2c-tools/i2cdetect.8.en.html
*/
/* i3c i2c_scan <device> */
static int cmd_i3c_i2c_scan(const struct shell *sh, size_t argc, char **argv)
{
const struct device *dev;
struct i3c_driver_data *data;
enum i3c_addr_slot_status slot;
uint8_t cnt = 0, first = 0x04, last = 0x77;
dev = device_get_binding(argv[ARGV_DEV]);
if (!dev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_DEV]);
return -ENODEV;
}
data = (struct i3c_driver_data *)dev->data;
shell_print(sh, " 0 1 2 3 4 5 6 7 8 9 a b c d e f");
for (uint8_t i = 0; i <= last; i += 16) {
shell_fprintf_normal(sh, "%02x: ", i);
for (uint8_t j = 0; j < 16; j++) {
if (i + j < first || i + j > last) {
shell_fprintf_normal(sh, " ");
continue;
}
slot = i3c_addr_slots_status(&data->attached_dev.addr_slots, i + j);
if (slot == I3C_ADDR_SLOT_STATUS_FREE) {
struct i2c_msg msgs[1];
uint8_t dst;
int ret;
struct i3c_i2c_device_desc desc = {
.bus = dev,
.addr = i + j,
.lvr = 0x00,
};
ret = i3c_attach_i2c_device(&desc);
if (ret < 0) {
shell_error(sh,
"I3C: unable to attach I2C addr 0x%02x.",
desc.addr);
}
/* Send the address to read from */
msgs[0].buf = &dst;
msgs[0].len = 0U;
msgs[0].flags = I2C_MSG_WRITE | I2C_MSG_STOP;
if (i2c_transfer(dev, &msgs[0], 1, i + j) == 0) {
shell_fprintf_normal(sh, "%02x ", i + j);
++cnt;
} else {
shell_fprintf_normal(sh, "-- ");
}
ret = i3c_detach_i2c_device(&desc);
if (ret < 0) {
shell_error(sh,
"I3C: unable to detach I2C addr 0x%02x.",
desc.addr);
}
} else if (slot == I3C_ADDR_SLOT_STATUS_I3C_DEV) {
shell_fprintf_normal(sh, "I3 ");
} else if (slot == I3C_ADDR_SLOT_STATUS_I2C_DEV) {
shell_fprintf_normal(sh, "I2 ");
} else if (slot == I3C_ADDR_SLOT_STATUS_RSVD) {
shell_fprintf_normal(sh, "RS ");
} else {
shell_fprintf_normal(sh, "-- ");
}
}
shell_print(sh, "");
}
shell_print(sh, "%u additional devices found on %s", cnt, argv[ARGV_DEV]);
return 0;
}
#ifdef I3C_USE_IBI
/* i3c ibi hj <device> */
static void cmd_i3c_ibi_hj(const struct shell *sh, size_t argc, char **argv)
{
const struct device *dev;
struct i3c_ibi request;
int ret;
dev = device_get_binding(argv[ARGV_DEV]);
if (!dev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_DEV]);
return -ENODEV;
}
request.ibi_type = I3C_IBI_HOTJOIN;
ret = i3c_ibi_raise(dev, &request);
if (ret != 0) {
shell_error(sh, "I3C: Unable to issue IBI HJ");
return ret;
}
shell_print(sh, "I3C: Issued IBI HJ");
}
/* i3c ibi cr <device> */
static void cmd_i3c_ibi_cr(const struct shell *sh, size_t argc, char **argv)
{
const struct device *dev;
struct i3c_ibi request;
int ret;
dev = device_get_binding(argv[ARGV_DEV]);
if (!dev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_DEV]);
return -ENODEV;
}
request.ibi_type = I3C_IBI_CONTROLLER_ROLE_REQUEST;
ret = i3c_ibi_raise(dev, &request);
if (ret != 0) {
shell_error(sh, "I3C: Unable to issue IBI CR");
return ret;
}
shell_print(sh, "I3C: Issued IBI CR");
}
/* i3c ibi tir <device> [<bytes>]*/
static void cmd_i3c_ibi_tir(const struct shell *sh, size_t argc, char **argv)
{
const struct device *dev;
struct i3c_ibi request;
uint16_t data_length;
char **data;
uint8_t buf[MAX_I3C_BYTES];
int ret;
dev = device_get_binding(argv[ARGV_DEV]);
if (!dev) {
shell_error(sh, "I3C: Device driver %s not found.", argv[ARGV_DEV]);
return -ENODEV;
}
data = argv[3];
data_length = argc - 3;
for (i = 0; i < data_length; i++) {
buf[i] = (uint8_t)strtol(data[i], NULL, 16);
}
request.ibi_type = I3C_IBI_TARGET_INTR;
request.payload = buf;
request.payload_len = data_length;
ret = i3c_ibi_raise(dev, &request);
if (ret != 0) {
shell_error(sh, "I3C: Unable to issue IBI TIR");
return ret;
}
shell_print(sh, "I3C: Issued IBI TIR");
}
#endif
static void i3c_device_list_target_name_get(size_t idx, struct shell_static_entry *entry)
{
if (idx < ARRAY_SIZE(i3c_list)) {
entry->syntax = i3c_list[idx].dev->name;
entry->handler = NULL;
entry->help = NULL;
entry->subcmd = i3c_list[idx].i3c_list_dev_subcmd;
} else {
entry->syntax = NULL;
}
}
SHELL_DYNAMIC_CMD_CREATE(dsub_i3c_device_list_name, i3c_device_list_target_name_get);
static void i3c_device_attached_target_name_get(size_t idx, struct shell_static_entry *entry)
{
if (idx < ARRAY_SIZE(i3c_list)) {
entry->syntax = i3c_list[idx].dev->name;
entry->handler = NULL;
entry->help = NULL;
entry->subcmd = i3c_list[idx].i3c_attached_dev_subcmd;
} else {
entry->syntax = NULL;
}
}
SHELL_DYNAMIC_CMD_CREATE(dsub_i3c_device_attached_name, i3c_device_attached_target_name_get);
static void i3c_device_name_get(size_t idx, struct shell_static_entry *entry)
{
if (idx < ARRAY_SIZE(i3c_list)) {
entry->syntax = i3c_list[idx].dev->name;
entry->handler = NULL;
entry->help = NULL;
entry->subcmd = NULL;
} else {
entry->syntax = NULL;
}
}
SHELL_DYNAMIC_CMD_CREATE(dsub_i3c_device_name, i3c_device_name_get);
#ifdef I3C_USE_IBI
/* L2 I3C IBI Shell Commands*/
SHELL_STATIC_SUBCMD_SET_CREATE(
sub_i3c_ibi_cmds,
SHELL_CMD_ARG(hj, &dsub_i3c_device_name,
"Send IBI HJ\n"
"Usage: ibi hj <device>",
cmd_i3c_ibi_hj, 2, 0),
SHELL_CMD_ARG(tir, &dsub_i3c_device_name,
"Send IBI TIR\n"
"Usage: ibi tir <device> [<byte1>, ...]",
cmd_i3c_ibi_tir, 2, MAX_I3C_BYTES),
SHELL_CMD_ARG(cr, &dsub_i3c_device_name,
"Send IBI CR\n"
"Usage: ibi cr <device>",
cmd_i3c_ibi_cr, 2, 0),
SHELL_SUBCMD_SET_END /* Array terminated. */
);
#endif
/* L3 I3C HDR DDR Shell Commands*/
SHELL_STATIC_SUBCMD_SET_CREATE(
sub_i3c_hdr_ddr_cmds,
SHELL_CMD_ARG(write, &dsub_i3c_device_attached_name,
"Send HDR DDR Write\n"
"Usage: hdr ddr write <device> <target> <7b cmd> [<byte1>, ...]",
cmd_i3c_hdr_ddr_write, 4, MAX_I3C_BYTES),
SHELL_CMD_ARG(read, &dsub_i3c_device_attached_name,
"Send HDR DDR Read\n"
"Usage: hdr ddr read <device> <target> <7b cmd> <bytes>",
cmd_i3c_hdr_ddr_read, 5, 0),
SHELL_SUBCMD_SET_END /* Array terminated. */
);
/* L2 I3C HDR Shell Commands*/
SHELL_STATIC_SUBCMD_SET_CREATE(
sub_i3c_hdr_cmds,
SHELL_CMD_ARG(ddr, &sub_i3c_hdr_ddr_cmds,
"Send HDR DDR\n"
"Usage: hdr ddr <sub cmd>",
NULL, 2, 0),
SHELL_SUBCMD_SET_END /* Array terminated. */
);
/* L2 I3C CCC Shell Commands*/
SHELL_STATIC_SUBCMD_SET_CREATE(
sub_i3c_ccc_cmds,
SHELL_CMD_ARG(rstdaa, &dsub_i3c_device_name,
"Send CCC RSTDAA\n"
"Usage: ccc rstdaa <device>",
cmd_i3c_ccc_rstdaa, 2, 0),
SHELL_CMD_ARG(entdaa, &dsub_i3c_device_name,
"Send CCC ENTDAA\n"
"Usage: ccc entdaa <device>",
cmd_i3c_ccc_entdaa, 2, 0),
SHELL_CMD_ARG(setaasa, &dsub_i3c_device_name,
"Send CCC SETAASA\n"
"Usage: ccc setaasa <device>",
cmd_i3c_ccc_setaasa, 2, 0),
SHELL_CMD_ARG(setdasa, &dsub_i3c_device_attached_name,
"Send CCC SETDASA\n"
"Usage: ccc setdasa <device> <target>",
cmd_i3c_ccc_setdasa, 3, 0),
SHELL_CMD_ARG(setnewda, &dsub_i3c_device_attached_name,
"Send CCC SETNEWDA\n"
"Usage: ccc setnewda <device> <target> <dynamic address>",
cmd_i3c_ccc_setnewda, 4, 0),
SHELL_CMD_ARG(getbcr, &dsub_i3c_device_attached_name,
"Send CCC GETBCR\n"
"Usage: ccc getbcr <device> <target>",
cmd_i3c_ccc_getbcr, 3, 0),
SHELL_CMD_ARG(getdcr, &dsub_i3c_device_attached_name,
"Send CCC GETDCR\n"
"Usage: ccc getdcr <device> <target>",
cmd_i3c_ccc_getdcr, 3, 0),
SHELL_CMD_ARG(getpid, &dsub_i3c_device_attached_name,
"Send CCC GETPID\n"
"Usage: ccc getpid <device> <target>",
cmd_i3c_ccc_getpid, 3, 0),
SHELL_CMD_ARG(getmrl, &dsub_i3c_device_attached_name,
"Send CCC GETMRL\n"
"Usage: ccc getmrl <device> <target>",
cmd_i3c_ccc_getmrl, 3, 0),
SHELL_CMD_ARG(getmwl, &dsub_i3c_device_attached_name,
"Send CCC GETMWL\n"
"Usage: ccc getmwl <device> <target>",
cmd_i3c_ccc_getmwl, 3, 0),
SHELL_CMD_ARG(setmrl, &dsub_i3c_device_attached_name,
"Send CCC SETMRL\n"
"Usage: ccc setmrl <device> <target> <max read length> [<max ibi length>]",
cmd_i3c_ccc_setmrl, 4, 1),
SHELL_CMD_ARG(setmwl, &dsub_i3c_device_attached_name,
"Send CCC SETMWL\n"
"Usage: ccc setmwl <device> <target> <max write length>",
cmd_i3c_ccc_setmwl, 4, 0),
SHELL_CMD_ARG(setmrl_bc, &dsub_i3c_device_name,
"Send CCC SETMRL BC\n"
"Usage: ccc setmrl_bc <device> <max read length> [<max ibi length>]",
cmd_i3c_ccc_setmrl_bc, 3, 1),
SHELL_CMD_ARG(setmwl_bc, &dsub_i3c_device_name,
"Send CCC SETMWL BC\n"
"Usage: ccc setmwl_bc <device> <max write length>",
cmd_i3c_ccc_setmwl_bc, 3, 0),
SHELL_CMD_ARG(deftgts, &dsub_i3c_device_name,
"Send CCC DEFTGTS\n"
"Usage: ccc deftgts <device>",
cmd_i3c_ccc_deftgts, 2, 0),
SHELL_CMD_ARG(enttm, &dsub_i3c_device_name,
"Send CCC ENTTM\n"
"Usage: ccc enttm <device> <defining byte>",
cmd_i3c_ccc_enttm, 3, 0),
SHELL_CMD_ARG(rstact_bc, &dsub_i3c_device_name,
"Send CCC RSTACT BC\n"
"Usage: ccc rstact_bc <device> <defining byte>",
cmd_i3c_ccc_rstact_bc, 3, 0),
SHELL_CMD_ARG(enec_bc, &dsub_i3c_device_name,
"Send CCC ENEC BC\n"
"Usage: ccc enec_bc <device> <defining byte>",
cmd_i3c_ccc_enec_bc, 3, 0),
SHELL_CMD_ARG(disec_bc, &dsub_i3c_device_name,
"Send CCC DISEC BC\n"
"Usage: ccc disec_bc <device> <defining byte>",
cmd_i3c_ccc_disec_bc, 3, 0),
SHELL_CMD_ARG(enec, &dsub_i3c_device_attached_name,
"Send CCC ENEC\n"
"Usage: ccc enec <device> <target> <defining byte>",
cmd_i3c_ccc_enec, 4, 0),
SHELL_CMD_ARG(disec, &dsub_i3c_device_attached_name,
"Send CCC DISEC\n"
"Usage: ccc disec <device> <target> <defining byte>",
cmd_i3c_ccc_disec, 4, 0),
SHELL_CMD_ARG(entas0_bc, &dsub_i3c_device_name,
"Send CCC ENTAS0 BC\n"
"Usage: ccc entas0 <device>",
cmd_i3c_ccc_entas0_bc, 2, 0),
SHELL_CMD_ARG(entas1_bc, &dsub_i3c_device_name,
"Send CCC ENTAS1 BC\n"
"Usage: ccc entas1 <device>",
cmd_i3c_ccc_entas1_bc, 2, 0),
SHELL_CMD_ARG(entas2_bc, &dsub_i3c_device_name,
"Send CCC ENTAS2 BC\n"
"Usage: ccc entas2 <device>",
cmd_i3c_ccc_entas2_bc, 2, 0),
SHELL_CMD_ARG(entas3_bc, &dsub_i3c_device_name,
"Send CCC ENTAS3 BC\n"
"Usage: ccc entas3 <device>",
cmd_i3c_ccc_entas3_bc, 2, 0),
SHELL_CMD_ARG(entas0, &dsub_i3c_device_attached_name,
"Send CCC ENTAS0\n"
"Usage: ccc entas0 <device> <target>",
cmd_i3c_ccc_entas0, 3, 0),
SHELL_CMD_ARG(entas1, &dsub_i3c_device_attached_name,
"Send CCC ENTAS1\n"
"Usage: ccc entas1 <device> <target>",
cmd_i3c_ccc_entas1, 3, 0),
SHELL_CMD_ARG(entas2, &dsub_i3c_device_attached_name,
"Send CCC ENTAS2\n"
"Usage: ccc entas2 <device> <target>",
cmd_i3c_ccc_entas2, 3, 0),
SHELL_CMD_ARG(entas3, &dsub_i3c_device_attached_name,
"Send CCC ENTAS3\n"
"Usage: ccc entas3 <device> <target>",
cmd_i3c_ccc_entas3, 3, 0),
SHELL_CMD_ARG(getstatus, &dsub_i3c_device_attached_name,
"Send CCC GETSTATUS\n"
"Usage: ccc getstatus <device> <target> [<defining byte>]",
cmd_i3c_ccc_getstatus, 3, 1),
SHELL_CMD_ARG(getcaps, &dsub_i3c_device_attached_name,
"Send CCC GETCAPS\n"
"Usage: ccc getcaps <device> <target> [<defining byte>]",
cmd_i3c_ccc_getcaps, 3, 1),
SHELL_CMD_ARG(getmxds, &dsub_i3c_device_attached_name,
"Send CCC GETMXDS\n"
"Usage: ccc getmxds <device> <target> [<defining byte>]",
cmd_i3c_ccc_getmxds, 3, 1),
SHELL_CMD_ARG(getvendor, &dsub_i3c_device_attached_name,
"Send CCC GETVENDOR\n"
"Usage: ccc getvendor <device> <target> <id> [<defining byte>]",
cmd_i3c_ccc_getvendor, 4, 1),
SHELL_CMD_ARG(setvendor, &dsub_i3c_device_attached_name,
"Send CCC SETVENDOR\n"
"Usage: ccc setvendor <device> <target> <id> [<bytes>]",
cmd_i3c_ccc_setvendor, 4, MAX_I3C_BYTES),
SHELL_CMD_ARG(setvendor_bc, &dsub_i3c_device_name,
"Send CCC SETVENDOR BC\n"
"Usage: ccc setvendor_bc <device> <id> [<bytes>]",
cmd_i3c_ccc_setvendor_bc, 3, MAX_I3C_BYTES),
SHELL_SUBCMD_SET_END /* Array terminated. */
);
/* L1 I3C Shell Commands*/
SHELL_STATIC_SUBCMD_SET_CREATE(
sub_i3c_cmds,
SHELL_CMD_ARG(info, &dsub_i3c_device_attached_name,
"Get I3C device info\n"
"Usage: info <device> [<target>]",
cmd_i3c_info, 2, 1),
SHELL_CMD_ARG(speed, &dsub_i3c_device_attached_name,
"Set I3C device speed\n"
"Usage: speed <device> <speed>",
cmd_i3c_speed, 3, 0),
SHELL_CMD_ARG(recover, &dsub_i3c_device_name,
"Recover I3C bus\n"
"Usage: recover <device>",
cmd_i3c_recover, 2, 0),
SHELL_CMD_ARG(read, &dsub_i3c_device_attached_name,
"Read bytes from an I3C device\n"
"Usage: read <device> <target> <reg> [<bytes>]",
cmd_i3c_read, 4, 1),
SHELL_CMD_ARG(read_byte, &dsub_i3c_device_attached_name,
"Read a byte from an I3C device\n"
"Usage: read_byte <device> <target> <reg>",
cmd_i3c_read_byte, 4, 0),
SHELL_CMD_ARG(write, &dsub_i3c_device_attached_name,
"Write bytes to an I3C device\n"
"Usage: write <device> <target> <reg> [<byte1>, ...]",
cmd_i3c_write, 4, MAX_I3C_BYTES),
SHELL_CMD_ARG(write_byte, &dsub_i3c_device_attached_name,
"Write a byte to an I3C device\n"
"Usage: write_byte <device> <target> <reg> <value>",
cmd_i3c_write_byte, 5, 0),
SHELL_CMD_ARG(i3c_attach, &dsub_i3c_device_list_name,
"Attach I3C device from the bus\n"
"Usage: i3c_attach <device> <target>",
cmd_i3c_attach, 3, 0),
SHELL_CMD_ARG(i3c_reattach, &dsub_i3c_device_attached_name,
"Reattach I3C device from the bus\n"
"Usage: i3c_reattach <device> <target> [<old dynamic address>]",
cmd_i3c_reattach, 3, 1),
SHELL_CMD_ARG(i3c_detach, &dsub_i3c_device_attached_name,
"Detach I3C device from the bus\n"
"Usage: i3c_detach <device> <target>",
cmd_i3c_detach, 3, 0),
SHELL_CMD_ARG(i2c_attach, &dsub_i3c_device_name,
"Attach I2C device from the bus\n"
"Usage: i2c_attach <device> <addr>",
cmd_i3c_i2c_attach, 3, 0),
SHELL_CMD_ARG(i2c_detach, &dsub_i3c_device_name,
"Detach I2C device from the bus\n"
"Usage: i2c_detach <device> <addr>",
cmd_i3c_i2c_detach, 3, 0),
SHELL_CMD_ARG(i2c_scan, &dsub_i3c_device_name,
"Scan I2C devices\n"
"Usage: i2c_scan <device>",
cmd_i3c_i2c_scan, 2, 0),
SHELL_CMD_ARG(ccc, &sub_i3c_ccc_cmds,
"Send I3C CCC\n"
"Usage: ccc <sub cmd>",
NULL, 3, 0),
SHELL_CMD_ARG(hdr, &sub_i3c_hdr_cmds,
"Send I3C HDR\n"
"Usage: hdr <sub cmd>",
NULL, 3, 0),
#ifdef I3C_USE_IBI
SHELL_CMD_ARG(ibi, &sub_i3c_ibi_cmds,
"Send I3C IBI\n"
"Usage: ibi <sub cmd>",
NULL, 3, 0),
#endif
SHELL_SUBCMD_SET_END /* Array terminated. */
);
SHELL_CMD_REGISTER(i3c, &sub_i3c_cmds, "I3C commands", NULL);