Commit cd3d6477 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull i3c updates from Alexandre Belloni:
 "This adds support for the I3C HCI controller of the AMD SoC which as
  expected requires quirks. Also fixes for the other drivers, including
  rate selection fixes for svc.

  Core:
   - allow adjusting first broadcast address speed

  Drivers:
   - cdns: few fixes
   - mipi-i3c-hci: Add AMD SoC I3C controller support and quirks, fix
     get_i3c_mode
   - svc: adjust rates, fix race condition"

* tag 'i3c/for-6.12' of git://git.kernel.org/pub/scm/linux/kernel/git/i3c/linux:
  i3c: master: svc: Fix use after free vulnerability in svc_i3c_master Driver Due to Race Condition
  i3c: master: cdns: Fix use after free vulnerability in cdns_i3c_master Driver Due to Race Condition
  i3c: master: svc: adjust SDR according to i3c spec
  i3c: master: svc: use slow speed for first broadcast address
  i3c: master: support to adjust first broadcast address speed
  i3c/master: cmd_v1: Fix the rule for getting i3c mode
  i3c: master: cdns: fix module autoloading
  i3c: mipi-i3c-hci: Add a quirk to set Response buffer threshold
  i3c: mipi-i3c-hci: Add a quirk to set timing parameters
  i3c: mipi-i3c-hci: Relocate helper macros to HCI header file
  i3c: mipi-i3c-hci: Add a quirk to set PIO mode
  i3c: mipi-i3c-hci: Read HC_CONTROL_PIO_MODE only after i3c hci v1.1
  i3c: mipi-i3c-hci: Add AMDI5017 ACPI ID to the I3C Support List
parents ba0c0cb5 61850725
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -1868,6 +1868,12 @@ static int i3c_master_bus_init(struct i3c_master_controller *master)
		goto err_bus_cleanup;
	}

	if (master->ops->set_speed) {
		ret = master->ops->set_speed(master, I3C_OPEN_DRAIN_SLOW_SPEED);
		if (ret)
			goto err_bus_cleanup;
	}

	/*
	 * Reset all dynamic address that may have been assigned before
	 * (assigned by the bootloader for example).
@@ -1876,6 +1882,12 @@ static int i3c_master_bus_init(struct i3c_master_controller *master)
	if (ret && ret != I3C_ERROR_M2)
		goto err_bus_cleanup;

	if (master->ops->set_speed) {
		master->ops->set_speed(master, I3C_OPEN_DRAIN_NORMAL_SPEED);
		if (ret)
			goto err_bus_cleanup;
	}

	/* Disable all slave events before starting DAA. */
	ret = i3c_master_disec_locked(master, I3C_BROADCAST_ADDR,
				      I3C_CCC_EVENT_SIR | I3C_CCC_EVENT_MR |
+2 −0
Original line number Diff line number Diff line
@@ -1562,6 +1562,7 @@ static const struct of_device_id cdns_i3c_master_of_ids[] = {
	{ .compatible = "cdns,i3c-master", .data = &cdns_i3c_devdata },
	{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, cdns_i3c_master_of_ids);

static int cdns_i3c_master_probe(struct platform_device *pdev)
{
@@ -1666,6 +1667,7 @@ static void cdns_i3c_master_remove(struct platform_device *pdev)
{
	struct cdns_i3c_master *master = platform_get_drvdata(pdev);

	cancel_work_sync(&master->hj_work);
	i3c_master_unregister(&master->base);

	clk_disable_unprepare(master->sysclk);
+2 −1
Original line number Diff line number Diff line
@@ -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
+5 −7
Original line number Diff line number Diff line
@@ -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_SDR3;
	return MODE_I3C_SDR4;
	return MODE_I3C_Fm_FmP;
}

static enum hci_cmd_mode get_i2c_mode(struct i3c_hci *hci)
+27 −9
Original line number Diff line number Diff line
@@ -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);
Loading