| Author | Tokens | Token Proportion | Commits | Commit Proportion |
|---|---|---|---|---|
| Nico Pitre | 3797 | 70.55% | 2 | 4.26% |
| Adrian Hunter | 1289 | 23.95% | 19 | 40.43% |
| Jarkko Nikula | 166 | 3.08% | 13 | 27.66% |
| Shyam Sundar S K | 102 | 1.90% | 5 | 10.64% |
| Frank Li | 8 | 0.15% | 1 | 2.13% |
| Billy Tsai | 7 | 0.13% | 1 | 2.13% |
| Wolfram Sang | 5 | 0.09% | 1 | 2.13% |
| Nathan Chancellor | 3 | 0.06% | 1 | 2.13% |
| Kees Cook | 2 | 0.04% | 1 | 2.13% |
| Uwe Kleine-König | 2 | 0.04% | 2 | 4.26% |
| Linus Torvalds | 1 | 0.02% | 1 | 2.13% |
| Total | 5382 | 47 |
// SPDX-License-Identifier: BSD-3-Clause /* * Copyright (c) 2020, MIPI Alliance, Inc. * * Author: Nicolas Pitre <npitre@baylibre.com> * * Core driver code with main interface to the I3C subsystem. */ #include <linux/bitfield.h> #include <linux/device.h> #include <linux/errno.h> #include <linux/i3c/master.h> #include <linux/interrupt.h> #include <linux/iopoll.h> #include <linux/module.h> #include <linux/platform_data/mipi-i3c-hci.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include "hci.h" #include "ext_caps.h" #include "cmd.h" #include "dat.h" /* * Host Controller Capabilities and Operation Registers */ #define HCI_VERSION 0x00 /* HCI Version (in BCD) */ #define HC_CONTROL 0x04 #define HC_CONTROL_BUS_ENABLE BIT(31) #define HC_CONTROL_RESUME BIT(30) #define HC_CONTROL_ABORT BIT(29) #define HC_CONTROL_HALT_ON_CMD_TIMEOUT BIT(12) #define HC_CONTROL_HOT_JOIN_CTRL BIT(8) /* Hot-Join ACK/NACK Control */ #define HC_CONTROL_I2C_TARGET_PRESENT BIT(7) #define HC_CONTROL_PIO_MODE BIT(6) /* DMA/PIO Mode Selector */ #define HC_CONTROL_DATA_BIG_ENDIAN BIT(4) #define HC_CONTROL_IBA_INCLUDE BIT(0) /* Include I3C Broadcast Address */ #define MASTER_DEVICE_ADDR 0x08 /* Master Device Address */ #define MASTER_DYNAMIC_ADDR_VALID BIT(31) /* Dynamic Address is Valid */ #define MASTER_DYNAMIC_ADDR(v) FIELD_PREP(GENMASK(22, 16), v) #define HC_CAPABILITIES 0x0c #define HC_CAP_SG_DC_EN BIT(30) #define HC_CAP_SG_IBI_EN BIT(29) #define HC_CAP_SG_CR_EN BIT(28) #define HC_CAP_MAX_DATA_LENGTH GENMASK(24, 22) #define HC_CAP_CMD_SIZE GENMASK(21, 20) #define HC_CAP_DIRECT_COMMANDS_EN BIT(18) #define HC_CAP_MULTI_LANE_EN BIT(15) #define HC_CAP_CMD_CCC_DEFBYTE BIT(10) #define HC_CAP_HDR_BT_EN BIT(8) #define HC_CAP_HDR_TS_EN BIT(7) #define HC_CAP_HDR_DDR_EN BIT(6) #define HC_CAP_NON_CURRENT_MASTER_CAP BIT(5) /* master handoff capable */ #define HC_CAP_DATA_BYTE_CFG_EN BIT(4) /* endian selection possible */ #define HC_CAP_AUTO_COMMAND BIT(3) #define HC_CAP_COMBO_COMMAND BIT(2) #define RESET_CONTROL 0x10 #define BUS_RESET BIT(31) #define BUS_RESET_TYPE GENMASK(30, 29) #define IBI_QUEUE_RST BIT(5) #define RX_FIFO_RST BIT(4) #define TX_FIFO_RST BIT(3) #define RESP_QUEUE_RST BIT(2) #define CMD_QUEUE_RST BIT(1) #define SOFT_RST BIT(0) /* Core Reset */ #define PRESENT_STATE 0x14 #define STATE_CURRENT_MASTER BIT(2) #define INTR_STATUS 0x20 #define INTR_STATUS_ENABLE 0x24 #define INTR_SIGNAL_ENABLE 0x28 #define INTR_FORCE 0x2c #define INTR_HC_CMD_SEQ_UFLOW_STAT BIT(12) /* Cmd Sequence Underflow */ #define INTR_HC_SEQ_CANCEL BIT(11) /* HC Cancelled Transaction Sequence */ #define INTR_HC_INTERNAL_ERR BIT(10) /* HC Internal Error */ #define DAT_SECTION 0x30 /* Device Address Table */ #define DAT_ENTRY_SIZE GENMASK(31, 28) #define DAT_TABLE_SIZE GENMASK(18, 12) #define DAT_TABLE_OFFSET GENMASK(11, 0) #define DCT_SECTION 0x34 /* Device Characteristics Table */ #define DCT_ENTRY_SIZE GENMASK(31, 28) #define DCT_TABLE_INDEX GENMASK(23, 19) #define DCT_TABLE_SIZE GENMASK(18, 12) #define DCT_TABLE_OFFSET GENMASK(11, 0) #define RING_HEADERS_SECTION 0x38 #define RING_HEADERS_OFFSET GENMASK(15, 0) #define PIO_SECTION 0x3c #define PIO_REGS_OFFSET GENMASK(15, 0) /* PIO Offset */ #define EXT_CAPS_SECTION 0x40 #define EXT_CAPS_OFFSET GENMASK(15, 0) #define IBI_NOTIFY_CTRL 0x58 /* IBI Notify Control */ #define IBI_NOTIFY_SIR_REJECTED BIT(3) /* Rejected Target Interrupt Request */ #define IBI_NOTIFY_MR_REJECTED BIT(1) /* Rejected Master Request Control */ #define IBI_NOTIFY_HJ_REJECTED BIT(0) /* Rejected Hot-Join Control */ #define DEV_CTX_BASE_LO 0x60 #define DEV_CTX_BASE_HI 0x64 static inline struct i3c_hci *to_i3c_hci(struct i3c_master_controller *m) { return container_of(m, struct i3c_hci, master); } static void i3c_hci_set_master_dyn_addr(struct i3c_hci *hci) { reg_write(MASTER_DEVICE_ADDR, MASTER_DYNAMIC_ADDR(hci->dyn_addr) | MASTER_DYNAMIC_ADDR_VALID); } static int i3c_hci_bus_init(struct i3c_master_controller *m) { struct i3c_hci *hci = to_i3c_hci(m); struct i3c_device_info info; int ret; if (hci->cmd == &mipi_i3c_hci_cmd_v1) { ret = mipi_i3c_hci_dat_v1.init(hci); if (ret) return ret; } ret = i3c_master_get_free_addr(m, 0); if (ret < 0) return ret; hci->dyn_addr = ret; i3c_hci_set_master_dyn_addr(hci); memset(&info, 0, sizeof(info)); info.dyn_addr = hci->dyn_addr; ret = i3c_master_set_info(m, &info); if (ret) return ret; ret = hci->io->init(hci); if (ret) return ret; /* Set RESP_BUF_THLD to 0(n) to get 1(n+1) response */ if (hci->quirks & HCI_QUIRK_RESP_BUF_THLD) amd_set_resp_buf_thld(hci); scoped_guard(spinlock_irqsave, &hci->lock) hci->irq_inactive = false; /* Enable bus with Hot-Join disabled */ reg_set(HC_CONTROL, HC_CONTROL_BUS_ENABLE | HC_CONTROL_HOT_JOIN_CTRL); dev_dbg(&hci->master.dev, "HC_CONTROL = %#x", reg_read(HC_CONTROL)); return 0; } /* Bus disable should never fail, so be generous with the timeout */ #define BUS_DISABLE_TIMEOUT_US (500 * USEC_PER_MSEC) static int i3c_hci_bus_disable(struct i3c_hci *hci) { u32 regval; int ret; reg_clear(HC_CONTROL, HC_CONTROL_BUS_ENABLE); /* Ensure controller is disabled */ ret = readx_poll_timeout(reg_read, HC_CONTROL, regval, !(regval & HC_CONTROL_BUS_ENABLE), 0, BUS_DISABLE_TIMEOUT_US); if (ret) dev_err(&hci->master.dev, "%s: Failed to disable bus\n", __func__); return ret; } static int i3c_hci_software_reset(struct i3c_hci *hci) { u32 regval; int ret; /* * SOFT_RST must be clear before we write to it. * Then we must wait until it clears again. */ ret = readx_poll_timeout(reg_read, RESET_CONTROL, regval, !(regval & SOFT_RST), 0, 10 * USEC_PER_MSEC); if (ret) { dev_err(&hci->master.dev, "%s: Software reset stuck\n", __func__); return ret; } reg_write(RESET_CONTROL, SOFT_RST); ret = readx_poll_timeout(reg_read, RESET_CONTROL, regval, !(regval & SOFT_RST), 0, 10 * USEC_PER_MSEC); if (ret) { dev_err(&hci->master.dev, "%s: Software reset failed\n", __func__); return ret; } return 0; } void i3c_hci_sync_irq_inactive(struct i3c_hci *hci) { struct platform_device *pdev = to_platform_device(hci->master.dev.parent); int irq = platform_get_irq(pdev, 0); reg_write(INTR_SIGNAL_ENABLE, 0x0); synchronize_irq(irq); scoped_guard(spinlock_irqsave, &hci->lock) hci->irq_inactive = true; } static void i3c_hci_bus_cleanup(struct i3c_master_controller *m) { struct i3c_hci *hci = to_i3c_hci(m); if (i3c_hci_bus_disable(hci)) i3c_hci_software_reset(hci); hci->io->cleanup(hci); } void mipi_i3c_hci_resume(struct i3c_hci *hci) { reg_set(HC_CONTROL, HC_CONTROL_RESUME); } /* located here rather than pio.c because needed bits are in core reg space */ void mipi_i3c_hci_pio_reset(struct i3c_hci *hci) { reg_write(RESET_CONTROL, RX_FIFO_RST | TX_FIFO_RST | RESP_QUEUE_RST); } /* located here rather than dct.c because needed bits are in core reg space */ void mipi_i3c_hci_dct_index_reset(struct i3c_hci *hci) { reg_write(DCT_SECTION, FIELD_PREP(DCT_TABLE_INDEX, 0)); } int i3c_hci_process_xfer(struct i3c_hci *hci, struct hci_xfer *xfer, int n) { struct completion *done = xfer[n - 1].completion; unsigned long timeout = xfer[n - 1].timeout; int ret; ret = hci->io->queue_xfer(hci, xfer, n); if (ret) return ret; if (!wait_for_completion_timeout(done, timeout)) { if (hci->io->dequeue_xfer(hci, xfer, n)) { dev_err(&hci->master.dev, "%s: timeout error\n", __func__); return -ETIMEDOUT; } return 0; } if (hci->io->handle_error) { bool error = false; for (int i = 0; i < n && !error; i++) error = RESP_STATUS(xfer[i].response); if (error) return hci->io->handle_error(hci, xfer, n); } return 0; } static int i3c_hci_send_ccc_cmd(struct i3c_master_controller *m, struct i3c_ccc_cmd *ccc) { struct i3c_hci *hci = to_i3c_hci(m); struct hci_xfer *xfer; bool raw = !!(hci->quirks & HCI_QUIRK_RAW_CCC); bool prefixed = raw && !!(ccc->id & I3C_CCC_DIRECT); unsigned int nxfers = ccc->ndests + prefixed; DECLARE_COMPLETION_ONSTACK(done); int i, last, ret = 0; dev_dbg(&hci->master.dev, "cmd=%#x rnw=%d ndests=%d data[0].len=%d", ccc->id, ccc->rnw, ccc->ndests, ccc->dests[0].payload.len); xfer = hci_alloc_xfer(nxfers); if (!xfer) return -ENOMEM; if (prefixed) { xfer->data = NULL; xfer->data_len = 0; xfer->rnw = false; hci->cmd->prep_ccc(hci, xfer, I3C_BROADCAST_ADDR, ccc->id, true); xfer++; } for (i = 0; i < nxfers - prefixed; i++) { xfer[i].data = ccc->dests[i].payload.data; xfer[i].data_len = ccc->dests[i].payload.len; xfer[i].rnw = ccc->rnw; ret = hci->cmd->prep_ccc(hci, &xfer[i], ccc->dests[i].addr, ccc->id, raw); if (ret) goto out; xfer[i].cmd_desc[0] |= CMD_0_ROC; } last = i - 1; xfer[last].cmd_desc[0] |= CMD_0_TOC; xfer[last].completion = &done; xfer[last].timeout = HZ; if (prefixed) xfer--; ret = i3c_hci_process_xfer(hci, xfer, nxfers); if (ret) goto out; for (i = prefixed; i < nxfers; i++) { if (ccc->rnw) ccc->dests[i - prefixed].payload.len = RESP_DATA_LENGTH(xfer[i].response); switch (RESP_STATUS(xfer[i].response)) { case RESP_SUCCESS: continue; case RESP_ERR_ADDR_HEADER: case RESP_ERR_NACK: ccc->err = I3C_ERROR_M2; fallthrough; default: ret = -EIO; goto out; } } if (ccc->rnw) dev_dbg(&hci->master.dev, "got: %*ph", ccc->dests[0].payload.len, ccc->dests[0].payload.data); out: hci_free_xfer(xfer, nxfers); return ret; } static int i3c_hci_daa(struct i3c_master_controller *m) { struct i3c_hci *hci = to_i3c_hci(m); return hci->cmd->perform_daa(hci); } static int i3c_hci_i3c_xfers(struct i3c_dev_desc *dev, struct i3c_xfer *i3c_xfers, int nxfers, enum i3c_xfer_mode mode) { struct i3c_master_controller *m = i3c_dev_get_master(dev); struct i3c_hci *hci = to_i3c_hci(m); struct hci_xfer *xfer; DECLARE_COMPLETION_ONSTACK(done); unsigned int size_limit; int i, last, ret = 0; dev_dbg(&hci->master.dev, "nxfers = %d", nxfers); xfer = hci_alloc_xfer(nxfers); if (!xfer) return -ENOMEM; size_limit = 1U << (16 + FIELD_GET(HC_CAP_MAX_DATA_LENGTH, hci->caps)); for (i = 0; i < nxfers; i++) { xfer[i].data_len = i3c_xfers[i].len; ret = -EFBIG; if (xfer[i].data_len >= size_limit) goto out; xfer[i].rnw = i3c_xfers[i].rnw; if (i3c_xfers[i].rnw) { xfer[i].data = i3c_xfers[i].data.in; } else { /* silence the const qualifier warning with a cast */ xfer[i].data = (void *) i3c_xfers[i].data.out; } hci->cmd->prep_i3c_xfer(hci, dev, &xfer[i]); xfer[i].cmd_desc[0] |= CMD_0_ROC; } last = i - 1; xfer[last].cmd_desc[0] |= CMD_0_TOC; xfer[last].completion = &done; xfer[last].timeout = HZ; ret = i3c_hci_process_xfer(hci, xfer, nxfers); if (ret) goto out; for (i = 0; i < nxfers; i++) { if (i3c_xfers[i].rnw) i3c_xfers[i].len = RESP_DATA_LENGTH(xfer[i].response); if (RESP_STATUS(xfer[i].response) != RESP_SUCCESS) { ret = -EIO; goto out; } } out: hci_free_xfer(xfer, nxfers); return ret; } static int i3c_hci_i2c_xfers(struct i2c_dev_desc *dev, struct i2c_msg *i2c_xfers, int nxfers) { struct i3c_master_controller *m = i2c_dev_get_master(dev); struct i3c_hci *hci = to_i3c_hci(m); struct hci_xfer *xfer; DECLARE_COMPLETION_ONSTACK(done); int i, last, ret = 0; dev_dbg(&hci->master.dev, "nxfers = %d", nxfers); xfer = hci_alloc_xfer(nxfers); if (!xfer) return -ENOMEM; for (i = 0; i < nxfers; i++) { xfer[i].data = i2c_xfers[i].buf; xfer[i].data_len = i2c_xfers[i].len; xfer[i].rnw = i2c_xfers[i].flags & I2C_M_RD; hci->cmd->prep_i2c_xfer(hci, dev, &xfer[i]); xfer[i].cmd_desc[0] |= CMD_0_ROC; } last = i - 1; xfer[last].cmd_desc[0] |= CMD_0_TOC; xfer[last].completion = &done; xfer[last].timeout = m->i2c.timeout; ret = i3c_hci_process_xfer(hci, xfer, nxfers); if (ret) goto out; for (i = 0; i < nxfers; i++) { if (RESP_STATUS(xfer[i].response) != RESP_SUCCESS) { ret = -EIO; goto out; } } out: hci_free_xfer(xfer, nxfers); return ret; } static int i3c_hci_attach_i3c_dev(struct i3c_dev_desc *dev) { struct i3c_master_controller *m = i3c_dev_get_master(dev); struct i3c_hci *hci = to_i3c_hci(m); struct i3c_hci_dev_data *dev_data; int ret; dev_data = kzalloc_obj(*dev_data); if (!dev_data) return -ENOMEM; if (hci->cmd == &mipi_i3c_hci_cmd_v1) { ret = mipi_i3c_hci_dat_v1.alloc_entry(hci); if (ret < 0) { kfree(dev_data); return ret; } mipi_i3c_hci_dat_v1.set_dynamic_addr(hci, ret, dev->info.dyn_addr ?: dev->info.static_addr); dev_data->dat_idx = ret; } i3c_dev_set_master_data(dev, dev_data); return 0; } static int i3c_hci_reattach_i3c_dev(struct i3c_dev_desc *dev, u8 old_dyn_addr) { struct i3c_master_controller *m = i3c_dev_get_master(dev); struct i3c_hci *hci = to_i3c_hci(m); struct i3c_hci_dev_data *dev_data = i3c_dev_get_master_data(dev); if (hci->cmd == &mipi_i3c_hci_cmd_v1) mipi_i3c_hci_dat_v1.set_dynamic_addr(hci, dev_data->dat_idx, dev->info.dyn_addr); return 0; } static void i3c_hci_detach_i3c_dev(struct i3c_dev_desc *dev) { struct i3c_master_controller *m = i3c_dev_get_master(dev); struct i3c_hci *hci = to_i3c_hci(m); struct i3c_hci_dev_data *dev_data = i3c_dev_get_master_data(dev); i3c_dev_set_master_data(dev, NULL); if (hci->cmd == &mipi_i3c_hci_cmd_v1) mipi_i3c_hci_dat_v1.free_entry(hci, dev_data->dat_idx); kfree(dev_data); } static int i3c_hci_attach_i2c_dev(struct i2c_dev_desc *dev) { struct i3c_master_controller *m = i2c_dev_get_master(dev); struct i3c_hci *hci = to_i3c_hci(m); struct i3c_hci_dev_data *dev_data; int ret; if (hci->cmd != &mipi_i3c_hci_cmd_v1) return 0; dev_data = kzalloc_obj(*dev_data); if (!dev_data) return -ENOMEM; ret = mipi_i3c_hci_dat_v1.alloc_entry(hci); if (ret < 0) { kfree(dev_data); return ret; } mipi_i3c_hci_dat_v1.set_static_addr(hci, ret, dev->addr); mipi_i3c_hci_dat_v1.set_flags(hci, ret, DAT_0_I2C_DEVICE, 0); dev_data->dat_idx = ret; i2c_dev_set_master_data(dev, dev_data); return 0; } static void i3c_hci_detach_i2c_dev(struct i2c_dev_desc *dev) { struct i3c_master_controller *m = i2c_dev_get_master(dev); struct i3c_hci *hci = to_i3c_hci(m); struct i3c_hci_dev_data *dev_data = i2c_dev_get_master_data(dev); if (dev_data) { i2c_dev_set_master_data(dev, NULL); if (hci->cmd == &mipi_i3c_hci_cmd_v1) mipi_i3c_hci_dat_v1.free_entry(hci, dev_data->dat_idx); kfree(dev_data); } } static int i3c_hci_request_ibi(struct i3c_dev_desc *dev, const struct i3c_ibi_setup *req) { struct i3c_master_controller *m = i3c_dev_get_master(dev); struct i3c_hci *hci = to_i3c_hci(m); struct i3c_hci_dev_data *dev_data = i3c_dev_get_master_data(dev); unsigned int dat_idx = dev_data->dat_idx; if (req->max_payload_len != 0) mipi_i3c_hci_dat_v1.set_flags(hci, dat_idx, DAT_0_IBI_PAYLOAD, 0); else mipi_i3c_hci_dat_v1.clear_flags(hci, dat_idx, DAT_0_IBI_PAYLOAD, 0); return hci->io->request_ibi(hci, dev, req); } static void i3c_hci_free_ibi(struct i3c_dev_desc *dev) { struct i3c_master_controller *m = i3c_dev_get_master(dev); struct i3c_hci *hci = to_i3c_hci(m); hci->io->free_ibi(hci, dev); } static int i3c_hci_enable_ibi(struct i3c_dev_desc *dev) { struct i3c_master_controller *m = i3c_dev_get_master(dev); struct i3c_hci *hci = to_i3c_hci(m); struct i3c_hci_dev_data *dev_data = i3c_dev_get_master_data(dev); mipi_i3c_hci_dat_v1.clear_flags(hci, dev_data->dat_idx, DAT_0_SIR_REJECT, 0); return i3c_master_enec_locked(m, dev->info.dyn_addr, I3C_CCC_EVENT_SIR); } static int i3c_hci_disable_ibi(struct i3c_dev_desc *dev) { struct i3c_master_controller *m = i3c_dev_get_master(dev); struct i3c_hci *hci = to_i3c_hci(m); struct i3c_hci_dev_data *dev_data = i3c_dev_get_master_data(dev); mipi_i3c_hci_dat_v1.set_flags(hci, dev_data->dat_idx, DAT_0_SIR_REJECT, 0); return i3c_master_disec_locked(m, dev->info.dyn_addr, I3C_CCC_EVENT_SIR); } static void i3c_hci_recycle_ibi_slot(struct i3c_dev_desc *dev, struct i3c_ibi_slot *slot) { struct i3c_master_controller *m = i3c_dev_get_master(dev); struct i3c_hci *hci = to_i3c_hci(m); hci->io->recycle_ibi_slot(hci, dev, slot); } static const struct i3c_master_controller_ops i3c_hci_ops = { .bus_init = i3c_hci_bus_init, .bus_cleanup = i3c_hci_bus_cleanup, .do_daa = i3c_hci_daa, .send_ccc_cmd = i3c_hci_send_ccc_cmd, .i3c_xfers = i3c_hci_i3c_xfers, .i2c_xfers = i3c_hci_i2c_xfers, .attach_i3c_dev = i3c_hci_attach_i3c_dev, .reattach_i3c_dev = i3c_hci_reattach_i3c_dev, .detach_i3c_dev = i3c_hci_detach_i3c_dev, .attach_i2c_dev = i3c_hci_attach_i2c_dev, .detach_i2c_dev = i3c_hci_detach_i2c_dev, .request_ibi = i3c_hci_request_ibi, .free_ibi = i3c_hci_free_ibi, .enable_ibi = i3c_hci_enable_ibi, .disable_ibi = i3c_hci_disable_ibi, .recycle_ibi_slot = i3c_hci_recycle_ibi_slot, }; static irqreturn_t i3c_hci_irq_handler(int irq, void *dev_id) { struct i3c_hci *hci = dev_id; irqreturn_t result = IRQ_NONE; u32 val; guard(spinlock)(&hci->lock); /* * The IRQ can be shared, so the handler may be called when the IRQ is * due to a different device. That could happen when runtime suspended, * so exit immediately if IRQs are not expected for this device. */ if (hci->irq_inactive) return IRQ_NONE; val = reg_read(INTR_STATUS); reg_write(INTR_STATUS, val); dev_dbg(&hci->master.dev, "INTR_STATUS %#x", val); if (val) result = IRQ_HANDLED; if (val & INTR_HC_SEQ_CANCEL) { dev_dbg(&hci->master.dev, "Host Controller Cancelled Transaction Sequence\n"); val &= ~INTR_HC_SEQ_CANCEL; } if (val & INTR_HC_INTERNAL_ERR) { dev_err(&hci->master.dev, "Host Controller Internal Error\n"); val &= ~INTR_HC_INTERNAL_ERR; } if (val) dev_warn_once(&hci->master.dev, "unexpected INTR_STATUS %#x\n", val); if (hci->io->irq_handler(hci)) result = IRQ_HANDLED; return result; } static inline bool is_version_1_1_or_newer(struct i3c_hci *hci) { return hci->version_major > 1 || (hci->version_major == 1 && hci->version_minor > 0); } static int i3c_hci_set_io_mode(struct i3c_hci *hci, bool dma) { bool pio_mode; if (dma) reg_clear(HC_CONTROL, HC_CONTROL_PIO_MODE); else reg_set(HC_CONTROL, HC_CONTROL_PIO_MODE); if (!is_version_1_1_or_newer(hci)) return 0; pio_mode = reg_read(HC_CONTROL) & HC_CONTROL_PIO_MODE; if ((dma && pio_mode) || (!dma && !pio_mode)) { dev_err(&hci->master.dev, "%s mode is stuck\n", pio_mode ? "PIO" : "DMA"); return -EIO; } return 0; } static int i3c_hci_reset_and_init(struct i3c_hci *hci) { u32 regval; int ret; ret = i3c_hci_software_reset(hci); if (ret) return -ENXIO; /* Disable all interrupts */ reg_write(INTR_SIGNAL_ENABLE, 0x0); /* * Only allow bit 31:10 signal updates because * Bit 0:9 are reserved in IP version >= 0.8 * Bit 0:5 are defined in IP version < 0.8 but not handled by PIO code */ reg_write(INTR_STATUS_ENABLE, GENMASK(31, 10)); /* Make sure our data ordering fits the host's */ regval = reg_read(HC_CONTROL); if (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) { if (!(regval & HC_CONTROL_DATA_BIG_ENDIAN)) { regval |= HC_CONTROL_DATA_BIG_ENDIAN; reg_write(HC_CONTROL, regval); regval = reg_read(HC_CONTROL); if (!(regval & HC_CONTROL_DATA_BIG_ENDIAN)) { dev_err(&hci->master.dev, "cannot set BE mode\n"); return -EOPNOTSUPP; } } } else { if (regval & HC_CONTROL_DATA_BIG_ENDIAN) { regval &= ~HC_CONTROL_DATA_BIG_ENDIAN; reg_write(HC_CONTROL, regval); regval = reg_read(HC_CONTROL); if (regval & HC_CONTROL_DATA_BIG_ENDIAN) { dev_err(&hci->master.dev, "cannot clear BE mode\n"); return -EOPNOTSUPP; } } } if (hci->io) { ret = i3c_hci_set_io_mode(hci, hci->io == &mipi_i3c_hci_dma); } else { /* Try activating DMA operations first */ if (hci->RHS_regs) { ret = i3c_hci_set_io_mode(hci, true); if (!ret) { hci->io = &mipi_i3c_hci_dma; dev_dbg(&hci->master.dev, "Using DMA\n"); } } /* If no DMA, try PIO */ if (!hci->io && hci->PIO_regs) { ret = i3c_hci_set_io_mode(hci, false); if (!ret) { hci->io = &mipi_i3c_hci_pio; dev_dbg(&hci->master.dev, "Using PIO\n"); } } if (!hci->io) { dev_err(&hci->master.dev, "neither DMA nor PIO can be used\n"); ret = ret ?: -EINVAL; } } if (ret) return ret; /* Configure OD and PP timings for AMD platforms */ if (hci->quirks & HCI_QUIRK_OD_PP_TIMING) amd_set_od_pp_timing(hci); return 0; } static int i3c_hci_runtime_suspend(struct device *dev) { struct i3c_hci *hci = dev_get_drvdata(dev); int ret; ret = i3c_hci_bus_disable(hci); if (ret) { /* Fall back to software reset to disable the bus */ ret = i3c_hci_software_reset(hci); i3c_hci_sync_irq_inactive(hci); return ret; } hci->io->suspend(hci); return 0; } static int i3c_hci_runtime_resume(struct device *dev) { struct i3c_hci *hci = dev_get_drvdata(dev); int ret; ret = i3c_hci_reset_and_init(hci); if (ret) return -EIO; i3c_hci_set_master_dyn_addr(hci); mipi_i3c_hci_dat_v1.restore(hci); hci->io->resume(hci); scoped_guard(spinlock_irqsave, &hci->lock) hci->irq_inactive = false; /* Enable bus with Hot-Join disabled */ reg_set(HC_CONTROL, HC_CONTROL_BUS_ENABLE | HC_CONTROL_HOT_JOIN_CTRL); return 0; } static int i3c_hci_suspend(struct device *dev) { struct i3c_hci *hci = dev_get_drvdata(dev); if (!(hci->quirks & HCI_QUIRK_RPM_ALLOWED)) return 0; return pm_runtime_force_suspend(dev); } static int i3c_hci_resume_common(struct device *dev, bool rstdaa) { struct i3c_hci *hci = dev_get_drvdata(dev); int ret; if (!(hci->quirks & HCI_QUIRK_RPM_ALLOWED)) return 0; ret = pm_runtime_force_resume(dev); if (ret) return ret; ret = i3c_master_do_daa_ext(&hci->master, rstdaa); if (ret) dev_err(dev, "Dynamic Address Assignment failed on resume, error %d\n", ret); /* * I3C devices may have retained their dynamic address anyway. Do not * fail the resume because of DAA error. */ return 0; } static int i3c_hci_resume(struct device *dev) { return i3c_hci_resume_common(dev, false); } static int i3c_hci_restore(struct device *dev) { return i3c_hci_resume_common(dev, true); } #define DEFAULT_AUTOSUSPEND_DELAY_MS 1000 static void i3c_hci_rpm_enable(struct device *dev) { struct i3c_hci *hci = dev_get_drvdata(dev); pm_runtime_set_autosuspend_delay(dev, DEFAULT_AUTOSUSPEND_DELAY_MS); pm_runtime_use_autosuspend(dev); devm_pm_runtime_set_active_enabled(dev); hci->master.rpm_allowed = true; } static int i3c_hci_init(struct i3c_hci *hci) { bool size_in_dwords; u32 regval, offset; int ret; /* Validate HCI hardware version */ regval = reg_read(HCI_VERSION); hci->version_major = (regval >> 8) & 0xf; hci->version_minor = (regval >> 4) & 0xf; hci->revision = regval & 0xf; dev_notice(&hci->master.dev, "MIPI I3C HCI v%u.%u r%02u\n", hci->version_major, hci->version_minor, hci->revision); /* known versions */ switch (regval & ~0xf) { case 0x100: /* version 1.0 */ case 0x110: /* version 1.1 */ case 0x200: /* version 2.0 */ break; default: dev_err(&hci->master.dev, "unsupported HCI version\n"); return -EPROTONOSUPPORT; } hci->caps = reg_read(HC_CAPABILITIES); dev_dbg(&hci->master.dev, "caps = %#x", hci->caps); size_in_dwords = hci->version_major < 1 || (hci->version_major == 1 && hci->version_minor < 1); regval = reg_read(DAT_SECTION); offset = FIELD_GET(DAT_TABLE_OFFSET, regval); hci->DAT_regs = offset ? hci->base_regs + offset : NULL; hci->DAT_entries = FIELD_GET(DAT_TABLE_SIZE, regval); hci->DAT_entry_size = FIELD_GET(DAT_ENTRY_SIZE, regval) ? 0 : 8; if (size_in_dwords) hci->DAT_entries = 4 * hci->DAT_entries / hci->DAT_entry_size; dev_dbg(&hci->master.dev, "DAT: %u %u-bytes entries at offset %#x\n", hci->DAT_entries, hci->DAT_entry_size, offset); regval = reg_read(DCT_SECTION); offset = FIELD_GET(DCT_TABLE_OFFSET, regval); hci->DCT_regs = offset ? hci->base_regs + offset : NULL; hci->DCT_entries = FIELD_GET(DCT_TABLE_SIZE, regval); hci->DCT_entry_size = FIELD_GET(DCT_ENTRY_SIZE, regval) ? 0 : 16; if (size_in_dwords) hci->DCT_entries = 4 * hci->DCT_entries / hci->DCT_entry_size; dev_dbg(&hci->master.dev, "DCT: %u %u-bytes entries at offset %#x\n", hci->DCT_entries, hci->DCT_entry_size, offset); regval = reg_read(RING_HEADERS_SECTION); offset = FIELD_GET(RING_HEADERS_OFFSET, regval); hci->RHS_regs = offset ? hci->base_regs + offset : NULL; dev_dbg(&hci->master.dev, "Ring Headers at offset %#x\n", offset); regval = reg_read(PIO_SECTION); offset = FIELD_GET(PIO_REGS_OFFSET, regval); hci->PIO_regs = offset ? hci->base_regs + offset : NULL; dev_dbg(&hci->master.dev, "PIO section at offset %#x\n", offset); regval = reg_read(EXT_CAPS_SECTION); offset = FIELD_GET(EXT_CAPS_OFFSET, regval); hci->EXTCAPS_regs = offset ? hci->base_regs + offset : NULL; dev_dbg(&hci->master.dev, "Extended Caps at offset %#x\n", offset); ret = i3c_hci_parse_ext_caps(hci); if (ret) return ret; /* Select our command descriptor model */ switch (FIELD_GET(HC_CAP_CMD_SIZE, hci->caps)) { case 0: hci->cmd = &mipi_i3c_hci_cmd_v1; break; case 1: hci->cmd = &mipi_i3c_hci_cmd_v2; break; default: dev_err(&hci->master.dev, "wrong CMD_SIZE capability value\n"); return -EINVAL; } /* Quirk for HCI_QUIRK_PIO_MODE on AMD platforms */ if (hci->quirks & HCI_QUIRK_PIO_MODE) hci->RHS_regs = NULL; return i3c_hci_reset_and_init(hci); } static int i3c_hci_probe(struct platform_device *pdev) { const struct mipi_i3c_hci_platform_data *pdata = pdev->dev.platform_data; struct i3c_hci *hci; int irq, ret; hci = devm_kzalloc(&pdev->dev, sizeof(*hci), GFP_KERNEL); if (!hci) return -ENOMEM; spin_lock_init(&hci->lock); mutex_init(&hci->control_mutex); /* * Multi-bus instances share the same MMIO address range, but not * necessarily in separate contiguous sub-ranges. To avoid overlapping * mappings, provide base_regs from the parent mapping. */ if (pdata) hci->base_regs = pdata->base_regs; if (!hci->base_regs) { hci->base_regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(hci->base_regs)) return PTR_ERR(hci->base_regs); } platform_set_drvdata(pdev, hci); /* temporary for dev_printk's, to be replaced in i3c_master_register */ hci->master.dev.init_name = dev_name(&pdev->dev); hci->quirks = (unsigned long)device_get_match_data(&pdev->dev); if (!hci->quirks && platform_get_device_id(pdev)) hci->quirks = platform_get_device_id(pdev)->driver_data; ret = i3c_hci_init(hci); if (ret) return ret; hci->irq_inactive = true; irq = platform_get_irq(pdev, 0); ret = devm_request_irq(&pdev->dev, irq, i3c_hci_irq_handler, IRQF_SHARED, NULL, hci); if (ret) return ret; if (hci->quirks & HCI_QUIRK_RPM_ALLOWED) i3c_hci_rpm_enable(&pdev->dev); return i3c_master_register(&hci->master, &pdev->dev, &i3c_hci_ops, false); } static void i3c_hci_remove(struct platform_device *pdev) { struct i3c_hci *hci = platform_get_drvdata(pdev); i3c_master_unregister(&hci->master); } static const __maybe_unused struct of_device_id i3c_hci_of_match[] = { { .compatible = "mipi-i3c-hci", }, {}, }; MODULE_DEVICE_TABLE(of, i3c_hci_of_match); static const struct acpi_device_id i3c_hci_acpi_match[] = { { "AMDI5017", HCI_QUIRK_PIO_MODE | HCI_QUIRK_OD_PP_TIMING | HCI_QUIRK_RESP_BUF_THLD }, {} }; MODULE_DEVICE_TABLE(acpi, i3c_hci_acpi_match); static const struct platform_device_id i3c_hci_driver_ids[] = { { .name = "intel-lpss-i3c", HCI_QUIRK_RPM_ALLOWED }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, i3c_hci_driver_ids); static const struct dev_pm_ops i3c_hci_pm_ops = { .suspend = pm_sleep_ptr(i3c_hci_suspend), .resume = pm_sleep_ptr(i3c_hci_resume), .freeze = pm_sleep_ptr(i3c_hci_suspend), .thaw = pm_sleep_ptr(i3c_hci_resume), .poweroff = pm_sleep_ptr(i3c_hci_suspend), .restore = pm_sleep_ptr(i3c_hci_restore), RUNTIME_PM_OPS(i3c_hci_runtime_suspend, i3c_hci_runtime_resume, NULL) }; static struct platform_driver i3c_hci_driver = { .probe = i3c_hci_probe, .remove = i3c_hci_remove, .id_table = i3c_hci_driver_ids, .driver = { .name = "mipi-i3c-hci", .of_match_table = of_match_ptr(i3c_hci_of_match), .acpi_match_table = i3c_hci_acpi_match, .pm = pm_ptr(&i3c_hci_pm_ops), }, }; module_platform_driver(i3c_hci_driver); MODULE_ALIAS("platform:mipi-i3c-hci"); MODULE_AUTHOR("Nicolas Pitre <npitre@baylibre.com>"); MODULE_DESCRIPTION("MIPI I3C HCI driver"); MODULE_LICENSE("Dual BSD/GPL");
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1