123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316 |
- // SPDX-License-Identifier: BSD-3-Clause
- /*
- * Copyright (c) 2020, MIPI Alliance, Inc.
- *
- * Author: Nicolas Pitre <[email protected]>
- *
- * 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, 0x4)
- #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, 0x2)
- #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,
- };
|