diff options
Diffstat (limited to 'drivers/i3c/master/mipi-i3c-hci/core.c')
| -rw-r--r-- | drivers/i3c/master/mipi-i3c-hci/core.c | 169 |
1 files changed, 68 insertions, 101 deletions
diff --git a/drivers/i3c/master/mipi-i3c-hci/core.c b/drivers/i3c/master/mipi-i3c-hci/core.c index d7e966a25583..47e42cb4dbe7 100644 --- a/drivers/i3c/master/mipi-i3c-hci/core.c +++ b/drivers/i3c/master/mipi-i3c-hci/core.c @@ -12,7 +12,6 @@ #include <linux/errno.h> #include <linux/i3c/master.h> #include <linux/interrupt.h> -#include <linux/io.h> #include <linux/iopoll.h> #include <linux/module.h> #include <linux/platform_device.h> @@ -27,11 +26,6 @@ * Host Controller Capabilities and Operation Registers */ -#define reg_read(r) readl(hci->base_regs + (r)) -#define reg_write(r, v) writel(v, hci->base_regs + (r)) -#define reg_set(r, v) reg_write(r, reg_read(r) | (v)) -#define reg_clear(r, v) reg_write(r, reg_read(r) & ~(v)) - #define HCI_VERSION 0x00 /* HCI Version (in BCD) */ #define HC_CONTROL 0x04 @@ -84,10 +78,8 @@ #define INTR_SIGNAL_ENABLE 0x28 #define INTR_FORCE 0x2c #define INTR_HC_CMD_SEQ_UFLOW_STAT BIT(12) /* Cmd Sequence Underflow */ -#define INTR_HC_RESET_CANCEL BIT(11) /* HC Cancelled Reset */ +#define INTR_HC_SEQ_CANCEL BIT(11) /* HC Cancelled Transaction Sequence */ #define INTR_HC_INTERNAL_ERR BIT(10) /* HC Internal Error */ -#define INTR_HC_PIO BIT(8) /* cascaded PIO interrupt */ -#define INTR_HC_RINGS GENMASK(7, 0) #define DAT_SECTION 0x30 /* Device Address Table */ #define DAT_ENTRY_SIZE GENMASK(31, 28) @@ -129,8 +121,6 @@ static int i3c_hci_bus_init(struct i3c_master_controller *m) struct i3c_device_info info; int ret; - DBG(""); - if (hci->cmd == &mipi_i3c_hci_cmd_v1) { ret = mipi_i3c_hci_dat_v1.init(hci); if (ret) @@ -152,8 +142,12 @@ static int i3c_hci_bus_init(struct i3c_master_controller *m) 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); + reg_set(HC_CONTROL, HC_CONTROL_BUS_ENABLE); - DBG("HC_CONTROL = %#x", reg_read(HC_CONTROL)); + dev_dbg(&hci->master.dev, "HC_CONTROL = %#x", reg_read(HC_CONTROL)); return 0; } @@ -163,8 +157,6 @@ static void i3c_hci_bus_cleanup(struct i3c_master_controller *m) struct i3c_hci *hci = to_i3c_hci(m); struct platform_device *pdev = to_platform_device(m->dev.parent); - DBG(""); - reg_clear(HC_CONTROL, HC_CONTROL_BUS_ENABLE); synchronize_irq(platform_get_irq(pdev, 0)); hci->io->cleanup(hci); @@ -200,8 +192,8 @@ static int i3c_hci_send_ccc_cmd(struct i3c_master_controller *m, DECLARE_COMPLETION_ONSTACK(done); int i, last, ret = 0; - DBG("cmd=%#x rnw=%d ndests=%d data[0].len=%d", - ccc->id, ccc->rnw, ccc->ndests, ccc->dests[0].payload.len); + 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) @@ -259,8 +251,8 @@ static int i3c_hci_send_ccc_cmd(struct i3c_master_controller *m, } if (ccc->rnw) - DBG("got: %*ph", - ccc->dests[0].payload.len, ccc->dests[0].payload.data); + dev_dbg(&hci->master.dev, "got: %*ph", + ccc->dests[0].payload.len, ccc->dests[0].payload.data); out: hci_free_xfer(xfer, nxfers); @@ -271,39 +263,9 @@ static int i3c_hci_daa(struct i3c_master_controller *m) { struct i3c_hci *hci = to_i3c_hci(m); - DBG(""); - return hci->cmd->perform_daa(hci); } -static int i3c_hci_alloc_safe_xfer_buf(struct i3c_hci *hci, - struct hci_xfer *xfer) -{ - if (hci->io != &mipi_i3c_hci_dma || - xfer->data == NULL || !is_vmalloc_addr(xfer->data)) - return 0; - - if (xfer->rnw) - xfer->bounce_buf = kzalloc(xfer->data_len, GFP_KERNEL); - else - xfer->bounce_buf = kmemdup(xfer->data, - xfer->data_len, GFP_KERNEL); - - return xfer->bounce_buf == NULL ? -ENOMEM : 0; -} - -static void i3c_hci_free_safe_xfer_buf(struct i3c_hci *hci, - struct hci_xfer *xfer) -{ - if (hci->io != &mipi_i3c_hci_dma || xfer->bounce_buf == NULL) - return; - - if (xfer->rnw) - memcpy(xfer->data, xfer->bounce_buf, xfer->data_len); - - kfree(xfer->bounce_buf); -} - static int i3c_hci_priv_xfers(struct i3c_dev_desc *dev, struct i3c_priv_xfer *i3c_xfers, int nxfers) @@ -315,7 +277,7 @@ static int i3c_hci_priv_xfers(struct i3c_dev_desc *dev, unsigned int size_limit; int i, last, ret = 0; - DBG("nxfers = %d", nxfers); + dev_dbg(&hci->master.dev, "nxfers = %d", nxfers); xfer = hci_alloc_xfer(nxfers); if (!xfer) @@ -337,9 +299,6 @@ static int i3c_hci_priv_xfers(struct i3c_dev_desc *dev, } hci->cmd->prep_i3c_xfer(hci, dev, &xfer[i]); xfer[i].cmd_desc[0] |= CMD_0_ROC; - ret = i3c_hci_alloc_safe_xfer_buf(hci, &xfer[i]); - if (ret) - goto out; } last = i - 1; xfer[last].cmd_desc[0] |= CMD_0_TOC; @@ -363,15 +322,12 @@ static int i3c_hci_priv_xfers(struct i3c_dev_desc *dev, } out: - for (i = 0; i < nxfers; i++) - i3c_hci_free_safe_xfer_buf(hci, &xfer[i]); - hci_free_xfer(xfer, nxfers); return ret; } static int i3c_hci_i2c_xfers(struct i2c_dev_desc *dev, - const struct i2c_msg *i2c_xfers, int nxfers) + 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); @@ -379,7 +335,7 @@ static int i3c_hci_i2c_xfers(struct i2c_dev_desc *dev, DECLARE_COMPLETION_ONSTACK(done); int i, last, ret = 0; - DBG("nxfers = %d", nxfers); + dev_dbg(&hci->master.dev, "nxfers = %d", nxfers); xfer = hci_alloc_xfer(nxfers); if (!xfer) @@ -391,9 +347,6 @@ static int i3c_hci_i2c_xfers(struct i2c_dev_desc *dev, 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; - ret = i3c_hci_alloc_safe_xfer_buf(hci, &xfer[i]); - if (ret) - goto out; } last = i - 1; xfer[last].cmd_desc[0] |= CMD_0_TOC; @@ -402,7 +355,7 @@ static int i3c_hci_i2c_xfers(struct i2c_dev_desc *dev, ret = hci->io->queue_xfer(hci, xfer, nxfers); if (ret) goto out; - if (!wait_for_completion_timeout(&done, HZ) && + if (!wait_for_completion_timeout(&done, m->i2c.timeout) && hci->io->dequeue_xfer(hci, xfer, nxfers)) { ret = -ETIME; goto out; @@ -415,9 +368,6 @@ static int i3c_hci_i2c_xfers(struct i2c_dev_desc *dev, } out: - for (i = 0; i < nxfers; i++) - i3c_hci_free_safe_xfer_buf(hci, &xfer[i]); - hci_free_xfer(xfer, nxfers); return ret; } @@ -429,8 +379,6 @@ static int i3c_hci_attach_i3c_dev(struct i3c_dev_desc *dev) struct i3c_hci_dev_data *dev_data; int ret; - DBG(""); - dev_data = kzalloc(sizeof(*dev_data), GFP_KERNEL); if (!dev_data) return -ENOMEM; @@ -440,7 +388,8 @@ static int i3c_hci_attach_i3c_dev(struct i3c_dev_desc *dev) kfree(dev_data); return ret; } - mipi_i3c_hci_dat_v1.set_dynamic_addr(hci, ret, dev->info.dyn_addr); + 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); @@ -453,8 +402,6 @@ static int i3c_hci_reattach_i3c_dev(struct i3c_dev_desc *dev, u8 old_dyn_addr) struct i3c_hci *hci = to_i3c_hci(m); struct i3c_hci_dev_data *dev_data = i3c_dev_get_master_data(dev); - DBG(""); - 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); @@ -467,8 +414,6 @@ static void i3c_hci_detach_i3c_dev(struct i3c_dev_desc *dev) struct i3c_hci *hci = to_i3c_hci(m); struct i3c_hci_dev_data *dev_data = i3c_dev_get_master_data(dev); - DBG(""); - 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); @@ -482,8 +427,6 @@ static int i3c_hci_attach_i2c_dev(struct i2c_dev_desc *dev) struct i3c_hci_dev_data *dev_data; int ret; - DBG(""); - if (hci->cmd != &mipi_i3c_hci_cmd_v1) return 0; dev_data = kzalloc(sizeof(*dev_data), GFP_KERNEL); @@ -507,8 +450,6 @@ static void i3c_hci_detach_i2c_dev(struct i2c_dev_desc *dev) struct i3c_hci *hci = to_i3c_hci(m); struct i3c_hci_dev_data *dev_data = i2c_dev_get_master_data(dev); - DBG(""); - if (dev_data) { i2c_dev_set_master_data(dev, NULL); if (hci->cmd == &mipi_i3c_hci_cmd_v1) @@ -595,34 +536,27 @@ static irqreturn_t i3c_hci_irq_handler(int irq, void *dev_id) u32 val; val = reg_read(INTR_STATUS); - DBG("INTR_STATUS = %#x", val); + reg_write(INTR_STATUS, val); + dev_dbg(&hci->master.dev, "INTR_STATUS %#x", val); - if (val) { - reg_write(INTR_STATUS, val); - } else { - /* v1.0 does not have PIO cascaded notification bits */ - val |= INTR_HC_PIO; - } + if (val) + result = IRQ_HANDLED; - if (val & INTR_HC_RESET_CANCEL) { - DBG("cancelled reset"); - val &= ~INTR_HC_RESET_CANCEL; + 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 & INTR_HC_PIO) { - hci->io->irq_handler(hci, 0); - val &= ~INTR_HC_PIO; - } - if (val & INTR_HC_RINGS) { - hci->io->irq_handler(hci, val & INTR_HC_RINGS); - val &= ~INTR_HC_RINGS; - } + if (val) - dev_err(&hci->master.dev, "unexpected INTR_STATUS %#x\n", val); - else + dev_warn_once(&hci->master.dev, + "unexpected INTR_STATUS %#x\n", val); + + if (hci->io->irq_handler(hci)) result = IRQ_HANDLED; return result; @@ -630,6 +564,7 @@ static irqreturn_t i3c_hci_irq_handler(int irq, void *dev_id) static int i3c_hci_init(struct i3c_hci *hci) { + bool size_in_dwords, mode_selector; u32 regval, offset; int ret; @@ -652,13 +587,18 @@ static int i3c_hci_init(struct i3c_hci *hci) } hci->caps = reg_read(HC_CAPABILITIES); - DBG("caps = %#x", hci->caps); + 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_info(&hci->master.dev, "DAT: %u %u-bytes entries at offset %#x\n", hci->DAT_entries, hci->DAT_entry_size, offset); @@ -667,6 +607,8 @@ static int i3c_hci_init(struct i3c_hci *hci) 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_info(&hci->master.dev, "DCT: %u %u-bytes entries at offset %#x\n", hci->DCT_entries, hci->DCT_entry_size, offset); @@ -704,9 +646,14 @@ static int i3c_hci_init(struct i3c_hci *hci) if (ret) return -ENXIO; - /* Disable all interrupts and allow all signal updates */ + /* Disable all interrupts */ reg_write(INTR_SIGNAL_ENABLE, 0x0); - reg_write(INTR_STATUS_ENABLE, 0xffffffff); + /* + * 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); @@ -745,10 +692,17 @@ static int i3c_hci_init(struct i3c_hci *hci) return -EINVAL; } + mode_selector = hci->version_major > 1 || + (hci->version_major == 1 && hci->version_minor > 0); + + /* Quirk for HCI_QUIRK_PIO_MODE on AMD platforms */ + if (hci->quirks & HCI_QUIRK_PIO_MODE) + hci->RHS_regs = NULL; + /* Try activating DMA operations first */ if (hci->RHS_regs) { reg_clear(HC_CONTROL, HC_CONTROL_PIO_MODE); - if (reg_read(HC_CONTROL) & HC_CONTROL_PIO_MODE) { + if (mode_selector && (reg_read(HC_CONTROL) & HC_CONTROL_PIO_MODE)) { dev_err(&hci->master.dev, "PIO mode is stuck\n"); ret = -EIO; } else { @@ -760,7 +714,7 @@ static int i3c_hci_init(struct i3c_hci *hci) /* If no DMA, try PIO */ if (!hci->io && hci->PIO_regs) { reg_set(HC_CONTROL, HC_CONTROL_PIO_MODE); - if (!(reg_read(HC_CONTROL) & HC_CONTROL_PIO_MODE)) { + if (mode_selector && !(reg_read(HC_CONTROL) & HC_CONTROL_PIO_MODE)) { dev_err(&hci->master.dev, "DMA mode is stuck\n"); ret = -EIO; } else { @@ -776,6 +730,10 @@ static int i3c_hci_init(struct i3c_hci *hci) 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; } @@ -795,6 +753,8 @@ static int i3c_hci_probe(struct platform_device *pdev) /* 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); + ret = i3c_hci_init(hci); if (ret) return ret; @@ -826,12 +786,19 @@ static const __maybe_unused struct of_device_id i3c_hci_of_match[] = { }; 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 struct platform_driver i3c_hci_driver = { .probe = i3c_hci_probe, - .remove_new = i3c_hci_remove, + .remove = i3c_hci_remove, .driver = { .name = "mipi-i3c-hci", .of_match_table = of_match_ptr(i3c_hci_of_match), + .acpi_match_table = i3c_hci_acpi_match, }, }; module_platform_driver(i3c_hci_driver); |
