Skip to content

Commit

Permalink
Merge tag 'i3c/for-6.14' of git://git.kernel.org/pub/scm/linux/kernel…
Browse files Browse the repository at this point in the history
…/git/i3c/linux

Pull i3c updates from Alexandre Belloni:
 "The main change is the addition of PCI bus support for mipi-i3c-hci.

  I'm also carrying an hwmon patch as it makes use of the bitops
  addition that is then mainly used by i3c drivers.

  Core:
   - Improve initialization of numbered I2C adapters

  Drivers:
   - use parity8 helper
   - dw: fix possible use-after-free
   - mipi-i3c-hci: add support for PCI bus host
   - svc: many fixes for IBI and hotjoin"

* tag 'i3c/for-6.14' of git://git.kernel.org/pub/scm/linux/kernel/git/i3c/linux:
  i3c: master: Improve initialization of numbered I2C adapters
  i3c: master: Fix missing 'ret' assignment in set_speed()
  i3c: cdns: use parity8 helper instead of open coding it
  i3c: mipi-i3c-hci: use parity8 helper instead of open coding it
  i3c: dw: use parity8 helper instead of open coding it
  hwmon: (spd5118) Use generic parity calculation
  bitops: add generic parity calculation for u8
  i3c: mipi-i3c-hci: Add support for MIPI I3C HCI on PCI bus
  i3c: mipi-i3c-hci: Add Intel specific quirk to ring resuming
  i3c: fix kdoc parameter description for module_i3c_i2c_driver()
  i3c: dw: Fix use-after-free in dw_i3c_master driver due to race condition
  • Loading branch information
torvalds committed Jan 24, 2025
2 parents ae8b53a + 5eb6d35 commit 9d5db4e
Show file tree
Hide file tree
Showing 11 changed files with 227 additions and 34 deletions.
8 changes: 1 addition & 7 deletions drivers/hwmon/spd5118.c
Original file line number Diff line number Diff line change
Expand Up @@ -291,20 +291,14 @@ static umode_t spd5118_is_visible(const void *_data, enum hwmon_sensor_types typ
}
}

static inline bool spd5118_parity8(u8 w)
{
w ^= w >> 4;
return (0x6996 >> (w & 0xf)) & 1;
}

/*
* Bank and vendor id are 8-bit fields with seven data bits and odd parity.
* Vendor IDs 0 and 0x7f are invalid.
* See Jedec standard JEP106BJ for details and a list of assigned vendor IDs.
*/
static bool spd5118_vendor_valid(u8 bank, u8 id)
{
if (!spd5118_parity8(bank) || !spd5118_parity8(id))
if (parity8(bank) == 0 || parity8(id) == 0)
return false;

id &= 0x7f;
Expand Down
14 changes: 11 additions & 3 deletions drivers/i3c/master.c
Original file line number Diff line number Diff line change
Expand Up @@ -1919,7 +1919,7 @@ static int i3c_master_bus_init(struct i3c_master_controller *master)
goto err_bus_cleanup;

if (master->ops->set_speed) {
master->ops->set_speed(master, I3C_OPEN_DRAIN_NORMAL_SPEED);
ret = master->ops->set_speed(master, I3C_OPEN_DRAIN_NORMAL_SPEED);
if (ret)
goto err_bus_cleanup;
}
Expand Down Expand Up @@ -2486,7 +2486,7 @@ static int i3c_master_i2c_adapter_init(struct i3c_master_controller *master)
struct i2c_adapter *adap = i3c_master_to_i2c_adapter(master);
struct i2c_dev_desc *i2cdev;
struct i2c_dev_boardinfo *i2cboardinfo;
int ret;
int ret, id = -ENODEV;

adap->dev.parent = master->dev.parent;
adap->owner = master->dev.parent->driver->owner;
Expand All @@ -2497,7 +2497,15 @@ static int i3c_master_i2c_adapter_init(struct i3c_master_controller *master)
adap->timeout = 1000;
adap->retries = 3;

ret = i2c_add_adapter(adap);
if (master->dev.of_node)
id = of_alias_get_id(master->dev.of_node, "i2c");

if (id >= 0) {
adap->nr = id;
ret = i2c_add_numbered_adapter(adap);
} else {
ret = i2c_add_adapter(adap);
}
if (ret)
return ret;

Expand Down
11 changes: 11 additions & 0 deletions drivers/i3c/master/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,14 @@ config MIPI_I3C_HCI

This driver can also be built as a module. If so, the module will be
called mipi-i3c-hci.

config MIPI_I3C_HCI_PCI
tristate "MIPI I3C Host Controller Interface PCI support"
depends on MIPI_I3C_HCI
depends on PCI
help
Support for MIPI I3C Host Controller Interface compatible hardware
on the PCI bus.

This driver can also be built as a module. If so, the module will be
called mipi-i3c-hci-pci.
15 changes: 4 additions & 11 deletions drivers/i3c/master/dw-i3c-master.c
Original file line number Diff line number Diff line change
Expand Up @@ -251,14 +251,6 @@ struct dw_i3c_i2c_dev_data {
struct i3c_generic_ibi_pool *ibi_pool;
};

static u8 even_parity(u8 p)
{
p ^= p >> 4;
p &= 0xf;

return (0x9669 >> p) & 1;
}

static bool dw_i3c_master_supports_ccc_cmd(struct i3c_master_controller *m,
const struct i3c_ccc_cmd *cmd)
{
Expand Down Expand Up @@ -848,7 +840,7 @@ static int dw_i3c_master_daa(struct i3c_master_controller *m)
struct dw_i3c_xfer *xfer;
struct dw_i3c_cmd *cmd;
u32 olddevs, newdevs;
u8 p, last_addr = 0;
u8 last_addr = 0;
int ret, pos;

ret = pm_runtime_resume_and_get(master->dev);
Expand All @@ -873,9 +865,9 @@ static int dw_i3c_master_daa(struct i3c_master_controller *m)
}

master->devs[pos].addr = ret;
p = even_parity(ret);
last_addr = ret;
ret |= (p << 7);

ret |= parity8(ret) ? 0 : BIT(7);

writel(DEV_ADDR_TABLE_DYNAMIC_ADDR(ret),
master->regs +
Expand Down Expand Up @@ -1647,6 +1639,7 @@ EXPORT_SYMBOL_GPL(dw_i3c_common_probe);

void dw_i3c_common_remove(struct dw_i3c_master *master)
{
cancel_work_sync(&master->hj_work);
i3c_master_unregister(&master->base);

pm_runtime_disable(master->dev);
Expand Down
3 changes: 1 addition & 2 deletions drivers/i3c/master/i3c-master-cdns.c
Original file line number Diff line number Diff line change
Expand Up @@ -889,8 +889,7 @@ static u32 prepare_rr0_dev_address(u32 addr)
ret |= (addr & GENMASK(9, 7)) << 6;

/* RR0[0] = ~XOR(addr[6:0]) */
if (!(hweight8(addr & 0x7f) & 1))
ret |= 1;
ret |= parity8(addr & 0x7f) ? 0 : BIT(0);

return ret;
}
Expand Down
1 change: 1 addition & 0 deletions drivers/i3c/master/mipi-i3c-hci/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ 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 \
hci_quirks.o
obj-$(CONFIG_MIPI_I3C_HCI_PCI) += mipi-i3c-hci-pci.o
11 changes: 1 addition & 10 deletions drivers/i3c/master/mipi-i3c-hci/dat_v1.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,6 @@
#define dat_w0_write(i, v) writel(v, hci->DAT_regs + (i) * 8)
#define dat_w1_write(i, v) writel(v, hci->DAT_regs + (i) * 8 + 4)

static inline bool dynaddr_parity(unsigned int addr)
{
addr |= 1 << 7;
addr += addr >> 4;
addr += addr >> 2;
addr += addr >> 1;
return (addr & 1);
}

static int hci_dat_v1_init(struct i3c_hci *hci)
{
unsigned int dat_idx;
Expand Down Expand Up @@ -123,7 +114,7 @@ static void hci_dat_v1_set_dynamic_addr(struct i3c_hci *hci,
dat_w0 = dat_w0_read(dat_idx);
dat_w0 &= ~(DAT_0_DYNAMIC_ADDRESS | DAT_0_DYNADDR_PARITY);
dat_w0 |= FIELD_PREP(DAT_0_DYNAMIC_ADDRESS, address) |
(dynaddr_parity(address) ? DAT_0_DYNADDR_PARITY : 0);
(parity8(address) ? 0 : DAT_0_DYNADDR_PARITY);
dat_w0_write(dat_idx, dat_w0);
}

Expand Down
17 changes: 17 additions & 0 deletions drivers/i3c/master/mipi-i3c-hci/dma.c
Original file line number Diff line number Diff line change
Expand Up @@ -758,9 +758,26 @@ static bool hci_dma_irq_handler(struct i3c_hci *hci)
complete(&rh->op_done);

if (status & INTR_TRANSFER_ABORT) {
u32 ring_status;

dev_notice_ratelimited(&hci->master.dev,
"ring %d: Transfer Aborted\n", i);
mipi_i3c_hci_resume(hci);
ring_status = rh_reg_read(RING_STATUS);
if (!(ring_status & RING_STATUS_RUNNING) &&
status & INTR_TRANSFER_COMPLETION &&
status & INTR_TRANSFER_ERR) {
/*
* Ring stop followed by run is an Intel
* specific required quirk after resuming the
* halted controller. Do it only when the ring
* is not in running state after a transfer
* error.
*/
rh_reg_write(RING_CONTROL, RING_CTRL_ENABLE);
rh_reg_write(RING_CONTROL, RING_CTRL_ENABLE |
RING_CTRL_RUN_STOP);
}
}
if (status & INTR_WARN_INS_STOP_MODE)
dev_warn_ratelimited(&hci->master.dev,
Expand Down
148 changes: 148 additions & 0 deletions drivers/i3c/master/mipi-i3c-hci/mipi-i3c-hci-pci.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
// SPDX-License-Identifier: GPL-2.0
/*
* PCI glue code for MIPI I3C HCI driver
*
* Copyright (C) 2024 Intel Corporation
*
* Author: Jarkko Nikula <[email protected]>
*/
#include <linux/acpi.h>
#include <linux/idr.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/platform_device.h>

struct mipi_i3c_hci_pci_info {
int (*init)(struct pci_dev *pci);
};

#define INTEL_PRIV_OFFSET 0x2b0
#define INTEL_PRIV_SIZE 0x28
#define INTEL_PRIV_RESETS 0x04
#define INTEL_PRIV_RESETS_RESET BIT(0)
#define INTEL_PRIV_RESETS_RESET_DONE BIT(1)

static DEFINE_IDA(mipi_i3c_hci_pci_ida);

static int mipi_i3c_hci_pci_intel_init(struct pci_dev *pci)
{
unsigned long timeout;
void __iomem *priv;

priv = devm_ioremap(&pci->dev,
pci_resource_start(pci, 0) + INTEL_PRIV_OFFSET,
INTEL_PRIV_SIZE);
if (!priv)
return -ENOMEM;

/* Assert reset, wait for completion and release reset */
writel(0, priv + INTEL_PRIV_RESETS);
timeout = jiffies + msecs_to_jiffies(10);
while (!(readl(priv + INTEL_PRIV_RESETS) &
INTEL_PRIV_RESETS_RESET_DONE)) {
if (time_after(jiffies, timeout))
break;
cpu_relax();
}
writel(INTEL_PRIV_RESETS_RESET, priv + INTEL_PRIV_RESETS);

return 0;
}

static struct mipi_i3c_hci_pci_info intel_info = {
.init = mipi_i3c_hci_pci_intel_init,
};

static int mipi_i3c_hci_pci_probe(struct pci_dev *pci,
const struct pci_device_id *id)
{
struct mipi_i3c_hci_pci_info *info;
struct platform_device *pdev;
struct resource res[2];
int dev_id, ret;

ret = pcim_enable_device(pci);
if (ret)
return ret;

pci_set_master(pci);

memset(&res, 0, sizeof(res));

res[0].flags = IORESOURCE_MEM;
res[0].start = pci_resource_start(pci, 0);
res[0].end = pci_resource_end(pci, 0);

res[1].flags = IORESOURCE_IRQ;
res[1].start = pci->irq;
res[1].end = pci->irq;

dev_id = ida_alloc(&mipi_i3c_hci_pci_ida, GFP_KERNEL);
if (dev_id < 0)
return dev_id;

pdev = platform_device_alloc("mipi-i3c-hci", dev_id);
if (!pdev)
return -ENOMEM;

pdev->dev.parent = &pci->dev;
device_set_node(&pdev->dev, dev_fwnode(&pci->dev));

ret = platform_device_add_resources(pdev, res, ARRAY_SIZE(res));
if (ret)
goto err;

info = (struct mipi_i3c_hci_pci_info *)id->driver_data;
if (info && info->init) {
ret = info->init(pci);
if (ret)
goto err;
}

ret = platform_device_add(pdev);
if (ret)
goto err;

pci_set_drvdata(pci, pdev);

return 0;

err:
platform_device_put(pdev);
ida_free(&mipi_i3c_hci_pci_ida, dev_id);
return ret;
}

static void mipi_i3c_hci_pci_remove(struct pci_dev *pci)
{
struct platform_device *pdev = pci_get_drvdata(pci);
int dev_id = pdev->id;

platform_device_unregister(pdev);
ida_free(&mipi_i3c_hci_pci_ida, dev_id);
}

static const struct pci_device_id mipi_i3c_hci_pci_devices[] = {
/* Panther Lake-H */
{ PCI_VDEVICE(INTEL, 0xe37c), (kernel_ulong_t)&intel_info},
{ PCI_VDEVICE(INTEL, 0xe36f), (kernel_ulong_t)&intel_info},
/* Panther Lake-P */
{ PCI_VDEVICE(INTEL, 0xe47c), (kernel_ulong_t)&intel_info},
{ PCI_VDEVICE(INTEL, 0xe46f), (kernel_ulong_t)&intel_info},
{ },
};
MODULE_DEVICE_TABLE(pci, mipi_i3c_hci_pci_devices);

static struct pci_driver mipi_i3c_hci_pci_driver = {
.name = "mipi_i3c_hci_pci",
.id_table = mipi_i3c_hci_pci_devices,
.probe = mipi_i3c_hci_pci_probe,
.remove = mipi_i3c_hci_pci_remove,
};

module_pci_driver(mipi_i3c_hci_pci_driver);

MODULE_AUTHOR("Jarkko Nikula <[email protected]>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("MIPI I3C HCI driver on PCI bus");
31 changes: 31 additions & 0 deletions include/linux/bitops.h
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,37 @@ static inline int get_count_order_long(unsigned long l)
return (int)fls_long(--l);
}

/**
* parity8 - get the parity of an u8 value
* @value: the value to be examined
*
* Determine the parity of the u8 argument.
*
* Returns:
* 0 for even parity, 1 for odd parity
*
* Note: This function informs you about the current parity. Example to bail
* out when parity is odd:
*
* if (parity8(val) == 1)
* return -EBADMSG;
*
* If you need to calculate a parity bit, you need to draw the conclusion from
* this result yourself. Example to enforce odd parity, parity bit is bit 7:
*
* if (parity8(val) == 0)
* val ^= BIT(7);
*/
static inline int parity8(u8 val)
{
/*
* One explanation of this algorithm:
* https://funloop.org/codex/problem/parity/README.html
*/
val ^= val >> 4;
return (0x6996 >> (val & 0xf)) & 1;
}

/**
* __ffs64 - find first set bit in a 64 bit word
* @word: The 64 bit word
Expand Down
2 changes: 1 addition & 1 deletion include/linux/i3c/device.h
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ static inline void i3c_i2c_driver_unregister(struct i3c_driver *i3cdrv,
* module_i3c_i2c_driver() - Register a module providing an I3C and an I2C
* driver
* @__i3cdrv: the I3C driver to register
* @__i2cdrv: the I3C driver to register
* @__i2cdrv: the I2C driver to register
*
* Provide generic init/exit functions that simply register/unregister an I3C
* and an I2C driver.
Expand Down

0 comments on commit 9d5db4e

Please sign in to comment.