162306a36Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2020, MIPI Alliance, Inc. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Author: Nicolas Pitre <npitre@baylibre.com> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * I3C HCI v2.0 Command Descriptor Handling 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Note: The I3C HCI v2.0 spec is still in flux. The code here will change. 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/bitfield.h> 1362306a36Sopenharmony_ci#include <linux/i3c/master.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include "hci.h" 1662306a36Sopenharmony_ci#include "cmd.h" 1762306a36Sopenharmony_ci#include "xfer_mode_rate.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci/* 2162306a36Sopenharmony_ci * Unified Data Transfer Command 2262306a36Sopenharmony_ci */ 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define CMD_0_ATTR_U FIELD_PREP(CMD_0_ATTR, 0x4) 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define CMD_U3_HDR_TSP_ML_CTRL(v) FIELD_PREP(W3_MASK(107, 104), v) 2762306a36Sopenharmony_ci#define CMD_U3_IDB4(v) FIELD_PREP(W3_MASK(103, 96), v) 2862306a36Sopenharmony_ci#define CMD_U3_HDR_CMD(v) FIELD_PREP(W3_MASK(103, 96), v) 2962306a36Sopenharmony_ci#define CMD_U2_IDB3(v) FIELD_PREP(W2_MASK( 95, 88), v) 3062306a36Sopenharmony_ci#define CMD_U2_HDR_BT(v) FIELD_PREP(W2_MASK( 95, 88), v) 3162306a36Sopenharmony_ci#define CMD_U2_IDB2(v) FIELD_PREP(W2_MASK( 87, 80), v) 3262306a36Sopenharmony_ci#define CMD_U2_BT_CMD2(v) FIELD_PREP(W2_MASK( 87, 80), v) 3362306a36Sopenharmony_ci#define CMD_U2_IDB1(v) FIELD_PREP(W2_MASK( 79, 72), v) 3462306a36Sopenharmony_ci#define CMD_U2_BT_CMD1(v) FIELD_PREP(W2_MASK( 79, 72), v) 3562306a36Sopenharmony_ci#define CMD_U2_IDB0(v) FIELD_PREP(W2_MASK( 71, 64), v) 3662306a36Sopenharmony_ci#define CMD_U2_BT_CMD0(v) FIELD_PREP(W2_MASK( 71, 64), v) 3762306a36Sopenharmony_ci#define CMD_U1_ERR_HANDLING(v) FIELD_PREP(W1_MASK( 63, 62), v) 3862306a36Sopenharmony_ci#define CMD_U1_ADD_FUNC(v) FIELD_PREP(W1_MASK( 61, 56), v) 3962306a36Sopenharmony_ci#define CMD_U1_COMBO_XFER W1_BIT_( 55) 4062306a36Sopenharmony_ci#define CMD_U1_DATA_LENGTH(v) FIELD_PREP(W1_MASK( 53, 32), v) 4162306a36Sopenharmony_ci#define CMD_U0_TOC W0_BIT_( 31) 4262306a36Sopenharmony_ci#define CMD_U0_ROC W0_BIT_( 30) 4362306a36Sopenharmony_ci#define CMD_U0_MAY_YIELD W0_BIT_( 29) 4462306a36Sopenharmony_ci#define CMD_U0_NACK_RCNT(v) FIELD_PREP(W0_MASK( 28, 27), v) 4562306a36Sopenharmony_ci#define CMD_U0_IDB_COUNT(v) FIELD_PREP(W0_MASK( 26, 24), v) 4662306a36Sopenharmony_ci#define CMD_U0_MODE_INDEX(v) FIELD_PREP(W0_MASK( 22, 18), v) 4762306a36Sopenharmony_ci#define CMD_U0_XFER_RATE(v) FIELD_PREP(W0_MASK( 17, 15), v) 4862306a36Sopenharmony_ci#define CMD_U0_DEV_ADDRESS(v) FIELD_PREP(W0_MASK( 14, 8), v) 4962306a36Sopenharmony_ci#define CMD_U0_RnW W0_BIT_( 7) 5062306a36Sopenharmony_ci#define CMD_U0_TID(v) FIELD_PREP(W0_MASK( 6, 3), v) 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/* 5362306a36Sopenharmony_ci * Address Assignment Command 5462306a36Sopenharmony_ci */ 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci#define CMD_0_ATTR_A FIELD_PREP(CMD_0_ATTR, 0x2) 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci#define CMD_A1_DATA_LENGTH(v) FIELD_PREP(W1_MASK( 53, 32), v) 5962306a36Sopenharmony_ci#define CMD_A0_TOC W0_BIT_( 31) 6062306a36Sopenharmony_ci#define CMD_A0_ROC W0_BIT_( 30) 6162306a36Sopenharmony_ci#define CMD_A0_XFER_RATE(v) FIELD_PREP(W0_MASK( 17, 15), v) 6262306a36Sopenharmony_ci#define CMD_A0_ASSIGN_ADDRESS(v) FIELD_PREP(W0_MASK( 14, 8), v) 6362306a36Sopenharmony_ci#define CMD_A0_TID(v) FIELD_PREP(W0_MASK( 6, 3), v) 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic unsigned int get_i3c_rate_idx(struct i3c_hci *hci) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci struct i3c_bus *bus = i3c_master_get_bus(&hci->master); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci if (bus->scl_rate.i3c >= 12000000) 7162306a36Sopenharmony_ci return XFERRATE_I3C_SDR0; 7262306a36Sopenharmony_ci if (bus->scl_rate.i3c > 8000000) 7362306a36Sopenharmony_ci return XFERRATE_I3C_SDR1; 7462306a36Sopenharmony_ci if (bus->scl_rate.i3c > 6000000) 7562306a36Sopenharmony_ci return XFERRATE_I3C_SDR2; 7662306a36Sopenharmony_ci if (bus->scl_rate.i3c > 4000000) 7762306a36Sopenharmony_ci return XFERRATE_I3C_SDR3; 7862306a36Sopenharmony_ci if (bus->scl_rate.i3c > 2000000) 7962306a36Sopenharmony_ci return XFERRATE_I3C_SDR4; 8062306a36Sopenharmony_ci return XFERRATE_I3C_SDR_FM_FMP; 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic unsigned int get_i2c_rate_idx(struct i3c_hci *hci) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci struct i3c_bus *bus = i3c_master_get_bus(&hci->master); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci if (bus->scl_rate.i2c >= 1000000) 8862306a36Sopenharmony_ci return XFERRATE_I2C_FMP; 8962306a36Sopenharmony_ci return XFERRATE_I2C_FM; 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic void hci_cmd_v2_prep_private_xfer(struct i3c_hci *hci, 9362306a36Sopenharmony_ci struct hci_xfer *xfer, 9462306a36Sopenharmony_ci u8 addr, unsigned int mode, 9562306a36Sopenharmony_ci unsigned int rate) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci u8 *data = xfer->data; 9862306a36Sopenharmony_ci unsigned int data_len = xfer->data_len; 9962306a36Sopenharmony_ci bool rnw = xfer->rnw; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci xfer->cmd_tid = hci_get_tid(); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci if (!rnw && data_len <= 5) { 10462306a36Sopenharmony_ci xfer->cmd_desc[0] = 10562306a36Sopenharmony_ci CMD_0_ATTR_U | 10662306a36Sopenharmony_ci CMD_U0_TID(xfer->cmd_tid) | 10762306a36Sopenharmony_ci CMD_U0_DEV_ADDRESS(addr) | 10862306a36Sopenharmony_ci CMD_U0_XFER_RATE(rate) | 10962306a36Sopenharmony_ci CMD_U0_MODE_INDEX(mode) | 11062306a36Sopenharmony_ci CMD_U0_IDB_COUNT(data_len); 11162306a36Sopenharmony_ci xfer->cmd_desc[1] = 11262306a36Sopenharmony_ci CMD_U1_DATA_LENGTH(0); 11362306a36Sopenharmony_ci xfer->cmd_desc[2] = 0; 11462306a36Sopenharmony_ci xfer->cmd_desc[3] = 0; 11562306a36Sopenharmony_ci switch (data_len) { 11662306a36Sopenharmony_ci case 5: 11762306a36Sopenharmony_ci xfer->cmd_desc[3] |= CMD_U3_IDB4(data[4]); 11862306a36Sopenharmony_ci fallthrough; 11962306a36Sopenharmony_ci case 4: 12062306a36Sopenharmony_ci xfer->cmd_desc[2] |= CMD_U2_IDB3(data[3]); 12162306a36Sopenharmony_ci fallthrough; 12262306a36Sopenharmony_ci case 3: 12362306a36Sopenharmony_ci xfer->cmd_desc[2] |= CMD_U2_IDB2(data[2]); 12462306a36Sopenharmony_ci fallthrough; 12562306a36Sopenharmony_ci case 2: 12662306a36Sopenharmony_ci xfer->cmd_desc[2] |= CMD_U2_IDB1(data[1]); 12762306a36Sopenharmony_ci fallthrough; 12862306a36Sopenharmony_ci case 1: 12962306a36Sopenharmony_ci xfer->cmd_desc[2] |= CMD_U2_IDB0(data[0]); 13062306a36Sopenharmony_ci fallthrough; 13162306a36Sopenharmony_ci case 0: 13262306a36Sopenharmony_ci break; 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci /* we consumed all the data with the cmd descriptor */ 13562306a36Sopenharmony_ci xfer->data = NULL; 13662306a36Sopenharmony_ci } else { 13762306a36Sopenharmony_ci xfer->cmd_desc[0] = 13862306a36Sopenharmony_ci CMD_0_ATTR_U | 13962306a36Sopenharmony_ci CMD_U0_TID(xfer->cmd_tid) | 14062306a36Sopenharmony_ci (rnw ? CMD_U0_RnW : 0) | 14162306a36Sopenharmony_ci CMD_U0_DEV_ADDRESS(addr) | 14262306a36Sopenharmony_ci CMD_U0_XFER_RATE(rate) | 14362306a36Sopenharmony_ci CMD_U0_MODE_INDEX(mode); 14462306a36Sopenharmony_ci xfer->cmd_desc[1] = 14562306a36Sopenharmony_ci CMD_U1_DATA_LENGTH(data_len); 14662306a36Sopenharmony_ci xfer->cmd_desc[2] = 0; 14762306a36Sopenharmony_ci xfer->cmd_desc[3] = 0; 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistatic int hci_cmd_v2_prep_ccc(struct i3c_hci *hci, struct hci_xfer *xfer, 15262306a36Sopenharmony_ci u8 ccc_addr, u8 ccc_cmd, bool raw) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci unsigned int mode = XFERMODE_IDX_I3C_SDR; 15562306a36Sopenharmony_ci unsigned int rate = get_i3c_rate_idx(hci); 15662306a36Sopenharmony_ci u8 *data = xfer->data; 15762306a36Sopenharmony_ci unsigned int data_len = xfer->data_len; 15862306a36Sopenharmony_ci bool rnw = xfer->rnw; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci if (raw && ccc_addr != I3C_BROADCAST_ADDR) { 16162306a36Sopenharmony_ci hci_cmd_v2_prep_private_xfer(hci, xfer, ccc_addr, mode, rate); 16262306a36Sopenharmony_ci return 0; 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci xfer->cmd_tid = hci_get_tid(); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci if (!rnw && data_len <= 4) { 16862306a36Sopenharmony_ci xfer->cmd_desc[0] = 16962306a36Sopenharmony_ci CMD_0_ATTR_U | 17062306a36Sopenharmony_ci CMD_U0_TID(xfer->cmd_tid) | 17162306a36Sopenharmony_ci CMD_U0_DEV_ADDRESS(ccc_addr) | 17262306a36Sopenharmony_ci CMD_U0_XFER_RATE(rate) | 17362306a36Sopenharmony_ci CMD_U0_MODE_INDEX(mode) | 17462306a36Sopenharmony_ci CMD_U0_IDB_COUNT(data_len + (!raw ? 0 : 1)); 17562306a36Sopenharmony_ci xfer->cmd_desc[1] = 17662306a36Sopenharmony_ci CMD_U1_DATA_LENGTH(0); 17762306a36Sopenharmony_ci xfer->cmd_desc[2] = 17862306a36Sopenharmony_ci CMD_U2_IDB0(ccc_cmd); 17962306a36Sopenharmony_ci xfer->cmd_desc[3] = 0; 18062306a36Sopenharmony_ci switch (data_len) { 18162306a36Sopenharmony_ci case 4: 18262306a36Sopenharmony_ci xfer->cmd_desc[3] |= CMD_U3_IDB4(data[3]); 18362306a36Sopenharmony_ci fallthrough; 18462306a36Sopenharmony_ci case 3: 18562306a36Sopenharmony_ci xfer->cmd_desc[2] |= CMD_U2_IDB3(data[2]); 18662306a36Sopenharmony_ci fallthrough; 18762306a36Sopenharmony_ci case 2: 18862306a36Sopenharmony_ci xfer->cmd_desc[2] |= CMD_U2_IDB2(data[1]); 18962306a36Sopenharmony_ci fallthrough; 19062306a36Sopenharmony_ci case 1: 19162306a36Sopenharmony_ci xfer->cmd_desc[2] |= CMD_U2_IDB1(data[0]); 19262306a36Sopenharmony_ci fallthrough; 19362306a36Sopenharmony_ci case 0: 19462306a36Sopenharmony_ci break; 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci /* we consumed all the data with the cmd descriptor */ 19762306a36Sopenharmony_ci xfer->data = NULL; 19862306a36Sopenharmony_ci } else { 19962306a36Sopenharmony_ci xfer->cmd_desc[0] = 20062306a36Sopenharmony_ci CMD_0_ATTR_U | 20162306a36Sopenharmony_ci CMD_U0_TID(xfer->cmd_tid) | 20262306a36Sopenharmony_ci (rnw ? CMD_U0_RnW : 0) | 20362306a36Sopenharmony_ci CMD_U0_DEV_ADDRESS(ccc_addr) | 20462306a36Sopenharmony_ci CMD_U0_XFER_RATE(rate) | 20562306a36Sopenharmony_ci CMD_U0_MODE_INDEX(mode) | 20662306a36Sopenharmony_ci CMD_U0_IDB_COUNT(!raw ? 0 : 1); 20762306a36Sopenharmony_ci xfer->cmd_desc[1] = 20862306a36Sopenharmony_ci CMD_U1_DATA_LENGTH(data_len); 20962306a36Sopenharmony_ci xfer->cmd_desc[2] = 21062306a36Sopenharmony_ci CMD_U2_IDB0(ccc_cmd); 21162306a36Sopenharmony_ci xfer->cmd_desc[3] = 0; 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci return 0; 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic void hci_cmd_v2_prep_i3c_xfer(struct i3c_hci *hci, 21862306a36Sopenharmony_ci struct i3c_dev_desc *dev, 21962306a36Sopenharmony_ci struct hci_xfer *xfer) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci unsigned int mode = XFERMODE_IDX_I3C_SDR; 22262306a36Sopenharmony_ci unsigned int rate = get_i3c_rate_idx(hci); 22362306a36Sopenharmony_ci u8 addr = dev->info.dyn_addr; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci hci_cmd_v2_prep_private_xfer(hci, xfer, addr, mode, rate); 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_cistatic void hci_cmd_v2_prep_i2c_xfer(struct i3c_hci *hci, 22962306a36Sopenharmony_ci struct i2c_dev_desc *dev, 23062306a36Sopenharmony_ci struct hci_xfer *xfer) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci unsigned int mode = XFERMODE_IDX_I2C; 23362306a36Sopenharmony_ci unsigned int rate = get_i2c_rate_idx(hci); 23462306a36Sopenharmony_ci u8 addr = dev->addr; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci hci_cmd_v2_prep_private_xfer(hci, xfer, addr, mode, rate); 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic int hci_cmd_v2_daa(struct i3c_hci *hci) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci struct hci_xfer *xfer; 24262306a36Sopenharmony_ci int ret; 24362306a36Sopenharmony_ci u8 next_addr = 0; 24462306a36Sopenharmony_ci u32 device_id[2]; 24562306a36Sopenharmony_ci u64 pid; 24662306a36Sopenharmony_ci unsigned int dcr, bcr; 24762306a36Sopenharmony_ci DECLARE_COMPLETION_ONSTACK(done); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci xfer = hci_alloc_xfer(2); 25062306a36Sopenharmony_ci if (!xfer) 25162306a36Sopenharmony_ci return -ENOMEM; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci xfer[0].data = &device_id; 25462306a36Sopenharmony_ci xfer[0].data_len = 8; 25562306a36Sopenharmony_ci xfer[0].rnw = true; 25662306a36Sopenharmony_ci xfer[0].cmd_desc[1] = CMD_A1_DATA_LENGTH(8); 25762306a36Sopenharmony_ci xfer[1].completion = &done; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci for (;;) { 26062306a36Sopenharmony_ci ret = i3c_master_get_free_addr(&hci->master, next_addr); 26162306a36Sopenharmony_ci if (ret < 0) 26262306a36Sopenharmony_ci break; 26362306a36Sopenharmony_ci next_addr = ret; 26462306a36Sopenharmony_ci DBG("next_addr = 0x%02x", next_addr); 26562306a36Sopenharmony_ci xfer[0].cmd_tid = hci_get_tid(); 26662306a36Sopenharmony_ci xfer[0].cmd_desc[0] = 26762306a36Sopenharmony_ci CMD_0_ATTR_A | 26862306a36Sopenharmony_ci CMD_A0_TID(xfer[0].cmd_tid) | 26962306a36Sopenharmony_ci CMD_A0_ROC; 27062306a36Sopenharmony_ci xfer[1].cmd_tid = hci_get_tid(); 27162306a36Sopenharmony_ci xfer[1].cmd_desc[0] = 27262306a36Sopenharmony_ci CMD_0_ATTR_A | 27362306a36Sopenharmony_ci CMD_A0_TID(xfer[1].cmd_tid) | 27462306a36Sopenharmony_ci CMD_A0_ASSIGN_ADDRESS(next_addr) | 27562306a36Sopenharmony_ci CMD_A0_ROC | 27662306a36Sopenharmony_ci CMD_A0_TOC; 27762306a36Sopenharmony_ci hci->io->queue_xfer(hci, xfer, 2); 27862306a36Sopenharmony_ci if (!wait_for_completion_timeout(&done, HZ) && 27962306a36Sopenharmony_ci hci->io->dequeue_xfer(hci, xfer, 2)) { 28062306a36Sopenharmony_ci ret = -ETIME; 28162306a36Sopenharmony_ci break; 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci if (RESP_STATUS(xfer[0].response) != RESP_SUCCESS) { 28462306a36Sopenharmony_ci ret = 0; /* no more devices to be assigned */ 28562306a36Sopenharmony_ci break; 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci if (RESP_STATUS(xfer[1].response) != RESP_SUCCESS) { 28862306a36Sopenharmony_ci ret = -EIO; 28962306a36Sopenharmony_ci break; 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci pid = FIELD_GET(W1_MASK(47, 32), device_id[1]); 29362306a36Sopenharmony_ci pid = (pid << 32) | device_id[0]; 29462306a36Sopenharmony_ci bcr = FIELD_GET(W1_MASK(55, 48), device_id[1]); 29562306a36Sopenharmony_ci dcr = FIELD_GET(W1_MASK(63, 56), device_id[1]); 29662306a36Sopenharmony_ci DBG("assigned address %#x to device PID=0x%llx DCR=%#x BCR=%#x", 29762306a36Sopenharmony_ci next_addr, pid, dcr, bcr); 29862306a36Sopenharmony_ci /* 29962306a36Sopenharmony_ci * TODO: Extend the subsystem layer to allow for registering 30062306a36Sopenharmony_ci * new device and provide BCR/DCR/PID at the same time. 30162306a36Sopenharmony_ci */ 30262306a36Sopenharmony_ci ret = i3c_master_add_i3c_dev_locked(&hci->master, next_addr); 30362306a36Sopenharmony_ci if (ret) 30462306a36Sopenharmony_ci break; 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci hci_free_xfer(xfer, 2); 30862306a36Sopenharmony_ci return ret; 30962306a36Sopenharmony_ci} 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ciconst struct hci_cmd_ops mipi_i3c_hci_cmd_v2 = { 31262306a36Sopenharmony_ci .prep_ccc = hci_cmd_v2_prep_ccc, 31362306a36Sopenharmony_ci .prep_i3c_xfer = hci_cmd_v2_prep_i3c_xfer, 31462306a36Sopenharmony_ci .prep_i2c_xfer = hci_cmd_v2_prep_i2c_xfer, 31562306a36Sopenharmony_ci .perform_daa = hci_cmd_v2_daa, 31662306a36Sopenharmony_ci}; 317