// SPDX-License-Identifier: BSD-3-Clause
/*
* Copyright (c) 2020, MIPI Alliance, Inc.
*
* Author: Nicolas Pitre <npitre@baylibre.com>
*
* I3C HCI v2.0 Command Descriptor Handling
*
* Note: The I3C HCI v2.0 spec is still in flux. The code here will change.
*/
#include <linux/bitfield.h>
#include <linux/i3c/master.h>
#include "hci.h"
#include "cmd.h"
#include "xfer_mode_rate.h"
/*
* Unified Data Transfer Command
*/
#define CMD_0_ATTR_U FIELD_PREP(CMD_0_ATTR, 0 x4)
#define CMD_U3_HDR_TSP_ML_CTRL(v) FIELD_PREP(W3_MASK(107 , 104 ), v)
#define CMD_U3_IDB4(v) FIELD_PREP(W3_MASK(103 , 96 ), v)
#define CMD_U3_HDR_CMD(v) FIELD_PREP(W3_MASK(103 , 96 ), v)
#define CMD_U2_IDB3(v) FIELD_PREP(W2_MASK( 95 , 88 ), v)
#define CMD_U2_HDR_BT(v) FIELD_PREP(W2_MASK( 95 , 88 ), v)
#define CMD_U2_IDB2(v) FIELD_PREP(W2_MASK( 87 , 80 ), v)
#define CMD_U2_BT_CMD2(v) FIELD_PREP(W2_MASK( 87 , 80 ), v)
#define CMD_U2_IDB1(v) FIELD_PREP(W2_MASK( 79 , 72 ), v)
#define CMD_U2_BT_CMD1(v) FIELD_PREP(W2_MASK( 79 , 72 ), v)
#define CMD_U2_IDB0(v) FIELD_PREP(W2_MASK( 71 , 64 ), v)
#define CMD_U2_BT_CMD0(v) FIELD_PREP(W2_MASK( 71 , 64 ), v)
#define CMD_U1_ERR_HANDLING(v) FIELD_PREP(W1_MASK( 63 , 62 ), v)
#define CMD_U1_ADD_FUNC(v) FIELD_PREP(W1_MASK( 61 , 56 ), v)
#define CMD_U1_COMBO_XFER W1_BIT_( 55 )
#define CMD_U1_DATA_LENGTH(v) FIELD_PREP(W1_MASK( 53 , 32 ), v)
#define CMD_U0_TOC W0_BIT_( 31 )
#define CMD_U0_ROC W0_BIT_( 30 )
#define CMD_U0_MAY_YIELD W0_BIT_( 29 )
#define CMD_U0_NACK_RCNT(v) FIELD_PREP(W0_MASK( 28 , 27 ), v)
#define CMD_U0_IDB_COUNT(v) FIELD_PREP(W0_MASK( 26 , 24 ), v)
#define CMD_U0_MODE_INDEX(v) FIELD_PREP(W0_MASK( 22 , 18 ), v)
#define CMD_U0_XFER_RATE(v) FIELD_PREP(W0_MASK( 17 , 15 ), v)
#define CMD_U0_DEV_ADDRESS(v) FIELD_PREP(W0_MASK( 14 , 8 ), v)
#define CMD_U0_RnW W0_BIT_( 7 )
#define CMD_U0_TID(v) FIELD_PREP(W0_MASK( 6 , 3 ), v)
/*
* Address Assignment Command
*/
#define CMD_0_ATTR_A FIELD_PREP(CMD_0_ATTR, 0 x2)
#define CMD_A1_DATA_LENGTH(v) FIELD_PREP(W1_MASK( 53 , 32 ), v)
#define CMD_A0_TOC W0_BIT_( 31 )
#define CMD_A0_ROC W0_BIT_( 30 )
#define CMD_A0_XFER_RATE(v) FIELD_PREP(W0_MASK( 17 , 15 ), v)
#define CMD_A0_ASSIGN_ADDRESS(v) FIELD_PREP(W0_MASK( 14 , 8 ), v)
#define CMD_A0_TID(v) FIELD_PREP(W0_MASK( 6 , 3 ), v)
static unsigned int get_i3c_rate_idx(struct i3c_hci *hci)
{
struct i3c_bus *bus = i3c_master_get_bus(&hci->master);
if (bus->scl_rate.i3c >= 12000000 )
return XFERRATE_I3C_SDR0;
if (bus->scl_rate.i3c > 8000000 )
return XFERRATE_I3C_SDR1;
if (bus->scl_rate.i3c > 6000000 )
return XFERRATE_I3C_SDR2;
if (bus->scl_rate.i3c > 4000000 )
return XFERRATE_I3C_SDR3;
if (bus->scl_rate.i3c > 2000000 )
return XFERRATE_I3C_SDR4;
return XFERRATE_I3C_SDR_FM_FMP;
}
static unsigned int get_i2c_rate_idx(struct i3c_hci *hci)
{
struct i3c_bus *bus = i3c_master_get_bus(&hci->master);
if (bus->scl_rate.i2c >= 1000000 )
return XFERRATE_I2C_FMP;
return XFERRATE_I2C_FM;
}
static void hci_cmd_v2_prep_private_xfer(struct i3c_hci *hci,
struct hci_xfer *xfer,
u8 addr, unsigned int mode,
unsigned int rate)
{
u8 *data = xfer->data;
unsigned int data_len = xfer->data_len;
bool rnw = xfer->rnw;
xfer->cmd_tid = hci_get_tid();
if (!rnw && data_len <= 5 ) {
xfer->cmd_desc[0 ] =
CMD_0_ATTR_U |
CMD_U0_TID(xfer->cmd_tid) |
CMD_U0_DEV_ADDRESS(addr) |
CMD_U0_XFER_RATE(rate) |
CMD_U0_MODE_INDEX(mode) |
CMD_U0_IDB_COUNT(data_len);
xfer->cmd_desc[1 ] =
CMD_U1_DATA_LENGTH(0 );
xfer->cmd_desc[2 ] = 0 ;
xfer->cmd_desc[3 ] = 0 ;
switch (data_len) {
case 5 :
xfer->cmd_desc[3 ] |= CMD_U3_IDB4(data[4 ]);
fallthrough;
case 4 :
xfer->cmd_desc[2 ] |= CMD_U2_IDB3(data[3 ]);
fallthrough;
case 3 :
xfer->cmd_desc[2 ] |= CMD_U2_IDB2(data[2 ]);
fallthrough;
case 2 :
xfer->cmd_desc[2 ] |= CMD_U2_IDB1(data[1 ]);
fallthrough;
case 1 :
xfer->cmd_desc[2 ] |= CMD_U2_IDB0(data[0 ]);
fallthrough;
case 0 :
break ;
}
/* we consumed all the data with the cmd descriptor */
xfer->data = NULL;
} else {
xfer->cmd_desc[0 ] =
CMD_0_ATTR_U |
CMD_U0_TID(xfer->cmd_tid) |
(rnw ? CMD_U0_RnW : 0 ) |
CMD_U0_DEV_ADDRESS(addr) |
CMD_U0_XFER_RATE(rate) |
CMD_U0_MODE_INDEX(mode);
xfer->cmd_desc[1 ] =
CMD_U1_DATA_LENGTH(data_len);
xfer->cmd_desc[2 ] = 0 ;
xfer->cmd_desc[3 ] = 0 ;
}
}
static int hci_cmd_v2_prep_ccc(struct i3c_hci *hci, struct hci_xfer *xfer,
u8 ccc_addr, u8 ccc_cmd, bool raw)
{
unsigned int mode = XFERMODE_IDX_I3C_SDR;
unsigned int rate = get_i3c_rate_idx(hci);
u8 *data = xfer->data;
unsigned int data_len = xfer->data_len;
bool rnw = xfer->rnw;
if (raw && ccc_addr != I3C_BROADCAST_ADDR) {
hci_cmd_v2_prep_private_xfer(hci, xfer, ccc_addr, mode, rate);
return 0 ;
}
xfer->cmd_tid = hci_get_tid();
if (!rnw && data_len <= 4 ) {
xfer->cmd_desc[0 ] =
CMD_0_ATTR_U |
CMD_U0_TID(xfer->cmd_tid) |
CMD_U0_DEV_ADDRESS(ccc_addr) |
CMD_U0_XFER_RATE(rate) |
CMD_U0_MODE_INDEX(mode) |
CMD_U0_IDB_COUNT(data_len + (!raw ? 0 : 1 ));
xfer->cmd_desc[1 ] =
CMD_U1_DATA_LENGTH(0 );
xfer->cmd_desc[2 ] =
CMD_U2_IDB0(ccc_cmd);
xfer->cmd_desc[3 ] = 0 ;
switch (data_len) {
case 4 :
xfer->cmd_desc[3 ] |= CMD_U3_IDB4(data[3 ]);
fallthrough;
case 3 :
xfer->cmd_desc[2 ] |= CMD_U2_IDB3(data[2 ]);
fallthrough;
case 2 :
xfer->cmd_desc[2 ] |= CMD_U2_IDB2(data[1 ]);
fallthrough;
case 1 :
xfer->cmd_desc[2 ] |= CMD_U2_IDB1(data[0 ]);
fallthrough;
case 0 :
break ;
}
/* we consumed all the data with the cmd descriptor */
xfer->data = NULL;
} else {
xfer->cmd_desc[0 ] =
CMD_0_ATTR_U |
CMD_U0_TID(xfer->cmd_tid) |
(rnw ? CMD_U0_RnW : 0 ) |
CMD_U0_DEV_ADDRESS(ccc_addr) |
CMD_U0_XFER_RATE(rate) |
CMD_U0_MODE_INDEX(mode) |
CMD_U0_IDB_COUNT(!raw ? 0 : 1 );
xfer->cmd_desc[1 ] =
CMD_U1_DATA_LENGTH(data_len);
xfer->cmd_desc[2 ] =
CMD_U2_IDB0(ccc_cmd);
xfer->cmd_desc[3 ] = 0 ;
}
return 0 ;
}
static void hci_cmd_v2_prep_i3c_xfer(struct i3c_hci *hci,
struct i3c_dev_desc *dev,
struct hci_xfer *xfer)
{
unsigned int mode = XFERMODE_IDX_I3C_SDR;
unsigned int rate = get_i3c_rate_idx(hci);
u8 addr = dev->info.dyn_addr;
hci_cmd_v2_prep_private_xfer(hci, xfer, addr, mode, rate);
}
static void hci_cmd_v2_prep_i2c_xfer(struct i3c_hci *hci,
struct i2c_dev_desc *dev,
struct hci_xfer *xfer)
{
unsigned int mode = XFERMODE_IDX_I2C;
unsigned int rate = get_i2c_rate_idx(hci);
u8 addr = dev->addr;
hci_cmd_v2_prep_private_xfer(hci, xfer, addr, mode, rate);
}
static int hci_cmd_v2_daa(struct i3c_hci *hci)
{
struct hci_xfer *xfer;
int ret;
u8 next_addr = 0 ;
u32 device_id[2 ];
u64 pid;
unsigned int dcr, bcr;
DECLARE_COMPLETION_ONSTACK(done);
xfer = hci_alloc_xfer(2 );
if (!xfer)
return -ENOMEM;
xfer[0 ].data = &device_id;
xfer[0 ].data_len = 8 ;
xfer[0 ].rnw = true ;
xfer[0 ].cmd_desc[1 ] = CMD_A1_DATA_LENGTH(8 );
xfer[1 ].completion = &done;
for (;;) {
ret = i3c_master_get_free_addr(&hci->master, next_addr);
if (ret < 0 )
break ;
next_addr = ret;
DBG("next_addr = 0x%02x" , next_addr);
xfer[0 ].cmd_tid = hci_get_tid();
xfer[0 ].cmd_desc[0 ] =
CMD_0_ATTR_A |
CMD_A0_TID(xfer[0 ].cmd_tid) |
CMD_A0_ROC;
xfer[1 ].cmd_tid = hci_get_tid();
xfer[1 ].cmd_desc[0 ] =
CMD_0_ATTR_A |
CMD_A0_TID(xfer[1 ].cmd_tid) |
CMD_A0_ASSIGN_ADDRESS(next_addr) |
CMD_A0_ROC |
CMD_A0_TOC;
hci->io->queue_xfer(hci, xfer, 2 );
if (!wait_for_completion_timeout(&done, HZ) &&
hci->io->dequeue_xfer(hci, xfer, 2 )) {
ret = -ETIME;
break ;
}
if (RESP_STATUS(xfer[0 ].response) != RESP_SUCCESS) {
ret = 0 ; /* no more devices to be assigned */
break ;
}
if (RESP_STATUS(xfer[1 ].response) != RESP_SUCCESS) {
ret = -EIO;
break ;
}
pid = FIELD_GET(W1_MASK(47 , 32 ), device_id[1 ]);
pid = (pid << 32 ) | device_id[0 ];
bcr = FIELD_GET(W1_MASK(55 , 48 ), device_id[1 ]);
dcr = FIELD_GET(W1_MASK(63 , 56 ), device_id[1 ]);
DBG("assigned address %#x to device PID=0x%llx DCR=%#x BCR=%#x" ,
next_addr, pid, dcr, bcr);
/*
* TODO: Extend the subsystem layer to allow for registering
* new device and provide BCR/DCR/PID at the same time.
*/
ret = i3c_master_add_i3c_dev_locked(&hci->master, next_addr);
if (ret)
break ;
}
hci_free_xfer(xfer, 2 );
return ret;
}
const struct hci_cmd_ops mipi_i3c_hci_cmd_v2 = {
.prep_ccc = hci_cmd_v2_prep_ccc,
.prep_i3c_xfer = hci_cmd_v2_prep_i3c_xfer,
.prep_i2c_xfer = hci_cmd_v2_prep_i2c_xfer,
.perform_daa = hci_cmd_v2_daa,
};
Messung V0.5 in Prozent C=95 H=71 G=83
¤ Dauer der Verarbeitung: 0.10 Sekunden
(vorverarbeitet am 2026-06-08)
¤
*© Formatika GbR, Deutschland