I3C for 6.14
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 -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEBqsFVZXh8s/0O5JiY6TcMGxwOjIFAmeSwjsACgkQY6TcMGxw OjJluBAAnw46F+qJM6XA1pPOYCc2uvVj6kmswMl5ZVWGplvY1qCnAq8PBHQTdKIY O7EV9QejCZZxMaa/ruh9VhyLyLNo8pm6wcCNXwufhDmH1kuAP+7cWjPh1qWsMBg6 TaBD05mN7FC9YPPsAPnx9IJNSdsbrSF6bc8m+7xrweQa9guX3TH+aim/U1K4RSh4 VnpiDKHRKaymoZ5Ldkny112/1/+L64bHnn/f4M5wNC6I9j3S/enmE3zKoi4UbeNR b73GqxjB8v9Er7Stafs8iHfboNfNKS9RiJDgFHg1+5eThbN9TTU3k2ym/osUCmet WJysSc88sp/vbr7BszDtTsAQb/p87WUFleU05rc0k+v0qF7GHH1wPrEjW46rfATL JgXbKEQK3vcoYsfQhnzJC6W5PhNHTP4VOlIiwZAeGjsVSkbVZHRNez1gQa77dYu/ pTpVon6M83Mn6QLkq+LwSuO4ZkBEMna43j9hQ7OmjxKq7Xmf5WJfrd++yjjaMmbu OUbOOjDYL7D3q5t2QO96zHYXnPfZJ7BBw8J0XgyApNRcyWVXZ9iUKyP9TtI+V1Or /CUUk1tHnOZ7NcedCes0yIzrTRy0LXShHU6tioRKJCvG/PE06pIipfrJX8gtT7uz ybNaLDuLoUzIcsOGpBY2SPgirnYLxlnrITTTI7C9TFhCHPnYiSc= =esI9 -----END PGP SIGNATURE----- Merge tag 'i3c/for-6.14' of git://git.kernel.org/pub/scm/linux/kernel/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
This commit is contained in:
commit
9d5db4e3fc
11 changed files with 227 additions and 34 deletions
|
@ -291,12 +291,6 @@ 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.
|
||||
|
@ -304,7 +298,7 @@ static inline bool spd5118_parity8(u8 w)
|
|||
*/
|
||||
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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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 +
|
||||
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
148
drivers/i3c/master/mipi-i3c-hci/mipi-i3c-hci-pci.c
Normal file
148
drivers/i3c/master/mipi-i3c-hci/mipi-i3c-hci-pci.c
Normal file
|
@ -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 <jarkko.nikula@linux.intel.com>
|
||||
*/
|
||||
#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 <jarkko.nikula@intel.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("MIPI I3C HCI driver on PCI bus");
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Add table
Reference in a new issue