diff options
Diffstat (limited to 'drivers/i3c/master/mipi-i3c-hci')
-rw-r--r-- | drivers/i3c/master/mipi-i3c-hci/Makefile | 3 | ||||
-rw-r--r-- | drivers/i3c/master/mipi-i3c-hci/cmd_v1.c | 12 | ||||
-rw-r--r-- | drivers/i3c/master/mipi-i3c-hci/core.c | 36 | ||||
-rw-r--r-- | drivers/i3c/master/mipi-i3c-hci/hci.h | 10 | ||||
-rw-r--r-- | drivers/i3c/master/mipi-i3c-hci/hci_quirks.c | 44 |
5 files changed, 88 insertions, 17 deletions
diff --git a/drivers/i3c/master/mipi-i3c-hci/Makefile b/drivers/i3c/master/mipi-i3c-hci/Makefile index a658e7b8262c..1f8cd5c48fde 100644 --- a/drivers/i3c/master/mipi-i3c-hci/Makefile +++ b/drivers/i3c/master/mipi-i3c-hci/Makefile @@ -3,4 +3,5 @@ obj-$(CONFIG_MIPI_I3C_HCI) += mipi-i3c-hci.o mipi-i3c-hci-y := core.o ext_caps.o pio.o dma.o \ cmd_v1.o cmd_v2.o \ - dat_v1.o dct_v1.o + dat_v1.o dct_v1.o \ + hci_quirks.o diff --git a/drivers/i3c/master/mipi-i3c-hci/cmd_v1.c b/drivers/i3c/master/mipi-i3c-hci/cmd_v1.c index 638b054d6c92..dd636094b07f 100644 --- a/drivers/i3c/master/mipi-i3c-hci/cmd_v1.c +++ b/drivers/i3c/master/mipi-i3c-hci/cmd_v1.c @@ -123,17 +123,15 @@ static enum hci_cmd_mode get_i3c_mode(struct i3c_hci *hci) { struct i3c_bus *bus = i3c_master_get_bus(&hci->master); - if (bus->scl_rate.i3c >= 12500000) - return MODE_I3C_SDR0; if (bus->scl_rate.i3c > 8000000) - return MODE_I3C_SDR1; + return MODE_I3C_SDR0; if (bus->scl_rate.i3c > 6000000) - return MODE_I3C_SDR2; + return MODE_I3C_SDR1; if (bus->scl_rate.i3c > 4000000) - return MODE_I3C_SDR3; + return MODE_I3C_SDR2; if (bus->scl_rate.i3c > 2000000) - return MODE_I3C_SDR4; - return MODE_I3C_Fm_FmP; + return MODE_I3C_SDR3; + return MODE_I3C_SDR4; } static enum hci_cmd_mode get_i2c_mode(struct i3c_hci *hci) diff --git a/drivers/i3c/master/mipi-i3c-hci/core.c b/drivers/i3c/master/mipi-i3c-hci/core.c index 4e7d6a43ee9b..a82c47c9986d 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 @@ -152,6 +146,10 @@ 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)); @@ -630,8 +628,8 @@ 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; - bool size_in_dwords; int ret; /* Validate HCI hardware version */ @@ -753,10 +751,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 { @@ -768,7 +773,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 { @@ -784,6 +789,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; } @@ -803,6 +812,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; @@ -834,12 +845,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, .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); diff --git a/drivers/i3c/master/mipi-i3c-hci/hci.h b/drivers/i3c/master/mipi-i3c-hci/hci.h index f94d95e024be..aaa47ac47381 100644 --- a/drivers/i3c/master/mipi-i3c-hci/hci.h +++ b/drivers/i3c/master/mipi-i3c-hci/hci.h @@ -10,6 +10,7 @@ #ifndef HCI_H #define HCI_H +#include <linux/io.h> /* Handy logging macro to save on line length */ #define DBG(x, ...) pr_devel("%s: " x "\n", __func__, ##__VA_ARGS__) @@ -26,6 +27,10 @@ #define W2_BIT_(x) BIT((x) - 64) #define W3_BIT_(x) BIT((x) - 96) +#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)) struct hci_cmd_ops; @@ -135,11 +140,16 @@ struct i3c_hci_dev_data { /* list of quirks */ #define HCI_QUIRK_RAW_CCC BIT(1) /* CCC framing must be explicit */ +#define HCI_QUIRK_PIO_MODE BIT(2) /* Set PIO mode for AMD platforms */ +#define HCI_QUIRK_OD_PP_TIMING BIT(3) /* Set OD and PP timings for AMD platforms */ +#define HCI_QUIRK_RESP_BUF_THLD BIT(4) /* Set resp buf thld to 0 for AMD platforms */ /* global functions */ void mipi_i3c_hci_resume(struct i3c_hci *hci); void mipi_i3c_hci_pio_reset(struct i3c_hci *hci); void mipi_i3c_hci_dct_index_reset(struct i3c_hci *hci); +void amd_set_od_pp_timing(struct i3c_hci *hci); +void amd_set_resp_buf_thld(struct i3c_hci *hci); #endif diff --git a/drivers/i3c/master/mipi-i3c-hci/hci_quirks.c b/drivers/i3c/master/mipi-i3c-hci/hci_quirks.c new file mode 100644 index 000000000000..3b9c6e76c536 --- /dev/null +++ b/drivers/i3c/master/mipi-i3c-hci/hci_quirks.c @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * I3C HCI Quirks + * + * Copyright 2024 Advanced Micro Devices, Inc. + * + * Authors: Shyam Sundar S K <Shyam-sundar.S-k@amd.com> + * Guruvendra Punugupati <Guruvendra.Punugupati@amd.com> + */ + +#include <linux/i3c/master.h> +#include "hci.h" + +/* Timing registers */ +#define HCI_SCL_I3C_OD_TIMING 0x214 +#define HCI_SCL_I3C_PP_TIMING 0x218 +#define HCI_SDA_HOLD_SWITCH_DLY_TIMING 0x230 + +/* Timing values to configure 9MHz frequency */ +#define AMD_SCL_I3C_OD_TIMING 0x00cf00cf +#define AMD_SCL_I3C_PP_TIMING 0x00160016 + +#define QUEUE_THLD_CTRL 0xD0 + +void amd_set_od_pp_timing(struct i3c_hci *hci) +{ + u32 data; + + reg_write(HCI_SCL_I3C_OD_TIMING, AMD_SCL_I3C_OD_TIMING); + reg_write(HCI_SCL_I3C_PP_TIMING, AMD_SCL_I3C_PP_TIMING); + data = reg_read(HCI_SDA_HOLD_SWITCH_DLY_TIMING); + /* Configure maximum TX hold time */ + data |= W0_MASK(18, 16); + reg_write(HCI_SDA_HOLD_SWITCH_DLY_TIMING, data); +} + +void amd_set_resp_buf_thld(struct i3c_hci *hci) +{ + u32 data; + + data = reg_read(QUEUE_THLD_CTRL); + data = data & ~W0_MASK(15, 8); + reg_write(QUEUE_THLD_CTRL, data); +} |