Merge tag 'i3c/for-6.18' of git://git.kernel.org/pub/scm/linux/kernel/git/i3c/linux

Pull i3c updates from Alexandre Belloni:
 "New driver:
   - Analog Devices I3C Controller

  Subsystem:
   - fix big-endian FIFO transfers
   - fix default I2C adapter timeout value

  Drivers:
   - dw: shutdown support
   - mipi-i3c-hci: Intel Wildcat Lake-U support, IOMMU support
   - renesas: RZ/V2H(P) and RZ/V2N support"

* tag 'i3c/for-6.18' of git://git.kernel.org/pub/scm/linux/kernel/git/i3c/linux: (22 commits)
  i3c: fix big-endian FIFO transfers
  i3c: master: adi: fix number of bytes written to fifo
  i3c: Remove superfluous FIXME
  i3c: master: adi: fix header location
  i3c: dw: Add shutdown support to dw_i3c_master driver
  i3c: renesas: Simplify return statement in 'renesas_i3c_daa'
  dt-bindings: i3c: renesas,i3c: Add RZ/V2H(P) and RZ/V2N support
  i3c: master: svc: Recycle unused IBI slot
  i3c: master: svc: Use manual response for IBI events
  i3c: master: Add driver for Analog Devices I3C Controller IP
  dt-bindings: i3c: Add adi-i3c-master
  i3c: Fix default I2C adapter timeout value
  i3c: mipi-i3c-hci: Convert remaining DBG() prints to dev_dbg()
  i3c: mipi-i3c-hci: Remove function enter DBG() printouts
  i3c: mipi-i3c-hci: Uniform ring number printouts
  i3c: mipi-i3c-hci: Remove nonexistent ring interrupt
  i3c: mipi-i3c-hci: Change interrupt status prints to dev_dbg()
  i3c: mipi-i3c-hci: Use own DMA bounce buffer management for I2C transfers
  i3c: mipi-i3c-hci: Use physical device pointer with DMA API
  i3c: mipi-i3c-hci: Use core helpers for DMA mapping and bounce buffering
  ...
This commit is contained in:
Linus Torvalds
2025-10-01 15:50:27 -07:00
20 changed files with 1410 additions and 168 deletions

View File

@@ -0,0 +1,72 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/i3c/adi,i3c-master.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Analog Devices I3C Controller
description:
FPGA-based I3C controller designed to interface with I3C and I2C peripherals,
implementing a subset of the I3C-basic specification. The IP core is tested
on arm, microblaze, and arm64 architectures.
https://analogdevicesinc.github.io/hdl/library/i3c_controller
maintainers:
- Jorge Marques <jorge.marques@analog.com>
properties:
compatible:
const: adi,i3c-master-v1
reg:
maxItems: 1
clocks:
minItems: 1
items:
- description: The AXI interconnect clock, drives the register map.
- description:
The secondary clock, drives the internal logic asynchronously to the
register map. The presence of this entry states that the IP Core was
synthesized with a second clock input, and the absence of this entry
indicates a topology where a single clock input drives all the
internal logic.
clock-names:
minItems: 1
items:
- const: axi
- const: i3c
interrupts:
maxItems: 1
required:
- compatible
- reg
- clocks
- clock-names
- interrupts
allOf:
- $ref: i3c.yaml#
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
i3c@44a00000 {
compatible = "adi,i3c-master-v1";
reg = <0x44a00000 0x1000>;
interrupts = <3 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clkc 15>, <&clkc 15>;
clock-names = "axi", "i3c";
#address-cells = <3>;
#size-cells = <0>;
/* I3C and I2C devices */
};

View File

@@ -4,7 +4,7 @@
$id: http://devicetree.org/schemas/i3c/renesas,i3c.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Renesas RZ/G3S and RZ/G3E I3C Bus Interface
title: Renesas I3C Bus Interface
maintainers:
- Wolfram Sang <wsa+renesas@sang-engineering.com>
@@ -12,10 +12,16 @@ maintainers:
properties:
compatible:
items:
- enum:
- renesas,r9a08g045-i3c # RZ/G3S
- renesas,r9a09g047-i3c # RZ/G3E
oneOf:
- items:
- enum:
- renesas,r9a08g045-i3c # RZ/G3S
- renesas,r9a09g047-i3c # RZ/G3E
- items:
- enum:
- renesas,r9a09g056-i3c # RZ/V2N
- renesas,r9a09g057-i3c # RZ/V2H(P)
- const: renesas,r9a09g047-i3c
reg:
maxItems: 1

View File

@@ -11674,6 +11674,12 @@ S: Maintained
F: Documentation/devicetree/bindings/i3c/aspeed,ast2600-i3c.yaml
F: drivers/i3c/master/ast2600-i3c-master.c
I3C DRIVER FOR ANALOG DEVICES I3C CONTROLLER IP
M: Jorge Marques <jorge.marques@analog.com>
S: Maintained
F: Documentation/devicetree/bindings/i3c/adi,i3c-master.yaml
F: drivers/i3c/master/adi-i3c-master.c
I3C DRIVER FOR CADENCE I3C MASTER IP
M: Przemysław Gaj <pgaj@cadence.com>
S: Maintained

View File

@@ -38,7 +38,11 @@ static inline void i3c_writel_fifo(void __iomem *addr, const void *buf,
u32 tmp = 0;
memcpy(&tmp, buf + (nbytes & ~3), nbytes & 3);
writel(tmp, addr);
/*
* writesl() instead of writel() to keep FIFO
* byteorder on big-endian targets
*/
writesl(addr, &tmp, 1);
}
}
@@ -55,7 +59,11 @@ static inline void i3c_readl_fifo(const void __iomem *addr, void *buf,
if (nbytes & 3) {
u32 tmp;
tmp = readl(addr);
/*
* readsl() instead of readl() to keep FIFO
* byteorder on big-endian targets
*/
readsl(addr, &tmp, 1);
memcpy(buf + (nbytes & ~3), &tmp, nbytes & 3);
}
}

View File

@@ -8,6 +8,7 @@
#include <linux/atomic.h>
#include <linux/bug.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/export.h>
#include <linux/kernel.h>
@@ -1727,6 +1728,79 @@ int i3c_master_do_daa(struct i3c_master_controller *master)
}
EXPORT_SYMBOL_GPL(i3c_master_do_daa);
/**
* i3c_master_dma_map_single() - Map buffer for single DMA transfer
* @dev: device object of a device doing DMA
* @buf: destination/source buffer for DMA
* @len: length of transfer
* @force_bounce: true, force to use a bounce buffer,
* false, function will auto check is a bounce buffer required
* @dir: DMA direction
*
* Map buffer for a DMA transfer and allocate a bounce buffer if required.
*
* Return: I3C DMA transfer descriptor or NULL in case of error.
*/
struct i3c_dma *i3c_master_dma_map_single(struct device *dev, void *buf,
size_t len, bool force_bounce, enum dma_data_direction dir)
{
struct i3c_dma *dma_xfer __free(kfree) = NULL;
void *bounce __free(kfree) = NULL;
void *dma_buf = buf;
dma_xfer = kzalloc(sizeof(*dma_xfer), GFP_KERNEL);
if (!dma_xfer)
return NULL;
dma_xfer->dev = dev;
dma_xfer->buf = buf;
dma_xfer->dir = dir;
dma_xfer->len = len;
dma_xfer->map_len = len;
if (is_vmalloc_addr(buf))
force_bounce = true;
if (force_bounce) {
dma_xfer->map_len = ALIGN(len, cache_line_size());
if (dir == DMA_FROM_DEVICE)
bounce = kzalloc(dma_xfer->map_len, GFP_KERNEL);
else
bounce = kmemdup(buf, dma_xfer->map_len, GFP_KERNEL);
if (!bounce)
return NULL;
dma_buf = bounce;
}
dma_xfer->addr = dma_map_single(dev, dma_buf, dma_xfer->map_len, dir);
if (dma_mapping_error(dev, dma_xfer->addr))
return NULL;
dma_xfer->bounce_buf = no_free_ptr(bounce);
return no_free_ptr(dma_xfer);
}
EXPORT_SYMBOL_GPL(i3c_master_dma_map_single);
/**
* i3c_master_dma_unmap_single() - Unmap buffer after DMA
* @dma_xfer: DMA transfer and mapping descriptor
*
* Unmap buffer and cleanup DMA transfer descriptor.
*/
void i3c_master_dma_unmap_single(struct i3c_dma *dma_xfer)
{
dma_unmap_single(dma_xfer->dev, dma_xfer->addr,
dma_xfer->map_len, dma_xfer->dir);
if (dma_xfer->bounce_buf) {
if (dma_xfer->dir == DMA_FROM_DEVICE)
memcpy(dma_xfer->buf, dma_xfer->bounce_buf,
dma_xfer->len);
kfree(dma_xfer->bounce_buf);
}
kfree(dma_xfer);
}
EXPORT_SYMBOL_GPL(i3c_master_dma_unmap_single);
/**
* i3c_master_set_info() - set master device information
* @master: master used to send frames on the bus
@@ -2490,9 +2564,7 @@ static int i3c_master_i2c_adapter_init(struct i3c_master_controller *master)
adap->owner = master->dev.parent->driver->owner;
adap->algo = &i3c_master_i2c_algo;
strscpy(adap->name, dev_name(master->dev.parent), sizeof(adap->name));
/* FIXME: Should we allow i3c masters to override these values? */
adap->timeout = 1000;
adap->timeout = HZ;
adap->retries = 3;
id = of_alias_get_id(master->dev.of_node, "i2c");

View File

@@ -1,4 +1,15 @@
# SPDX-License-Identifier: GPL-2.0-only
config ADI_I3C_MASTER
tristate "Analog Devices I3C master driver"
depends on HAS_IOMEM
help
Support for Analog Devices I3C Controller IP, an AXI-interfaced IP
core that supports I3C and I2C devices, multiple speed-grades and I3C
IBIs.
This driver can also be built as a module. If so, the module will be
called adi-i3c-master.
config CDNS_I3C_MASTER
tristate "Cadence I3C master driver"
depends on HAS_IOMEM

View File

@@ -1,4 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_ADI_I3C_MASTER) += adi-i3c-master.o
obj-$(CONFIG_CDNS_I3C_MASTER) += i3c-master-cdns.o
obj-$(CONFIG_DW_I3C_MASTER) += dw-i3c-master.o
obj-$(CONFIG_AST2600_I3C_MASTER) += ast2600-i3c-master.o

File diff suppressed because it is too large Load Diff

View File

@@ -1737,6 +1737,28 @@ static const struct dev_pm_ops dw_i3c_pm_ops = {
SET_RUNTIME_PM_OPS(dw_i3c_master_runtime_suspend, dw_i3c_master_runtime_resume, NULL)
};
static void dw_i3c_shutdown(struct platform_device *pdev)
{
struct dw_i3c_master *master = platform_get_drvdata(pdev);
int ret;
ret = pm_runtime_resume_and_get(master->dev);
if (ret < 0) {
dev_err(master->dev,
"<%s> cannot resume i3c bus master, err: %d\n",
__func__, ret);
return;
}
cancel_work_sync(&master->hj_work);
/* Disable interrupts */
writel((u32)~INTR_ALL, master->regs + INTR_STATUS_EN);
writel((u32)~INTR_ALL, master->regs + INTR_SIGNAL_EN);
pm_runtime_put_autosuspend(master->dev);
}
static const struct of_device_id dw_i3c_master_of_match[] = {
{ .compatible = "snps,dw-i3c-master-1.00a", },
{},
@@ -1752,6 +1774,7 @@ MODULE_DEVICE_TABLE(acpi, amd_i3c_device_match);
static struct platform_driver dw_i3c_driver = {
.probe = dw_i3c_probe,
.remove = dw_i3c_remove,
.shutdown = dw_i3c_shutdown,
.driver = {
.name = "dw-i3c-master",
.of_match_table = dw_i3c_master_of_match,

View File

@@ -317,7 +317,9 @@ static int hci_cmd_v1_daa(struct i3c_hci *hci)
break;
next_addr = ret;
DBG("next_addr = 0x%02x, DAA using DAT %d", next_addr, dat_idx);
dev_dbg(&hci->master.dev,
"next_addr = 0x%02x, DAA using DAT %d",
next_addr, dat_idx);
mipi_i3c_hci_dat_v1.set_dynamic_addr(hci, dat_idx, next_addr);
mipi_i3c_hci_dct_index_reset(hci);
@@ -349,8 +351,9 @@ static int hci_cmd_v1_daa(struct i3c_hci *hci)
}
i3c_hci_dct_get_val(hci, 0, &pid, &dcr, &bcr);
DBG("assigned address %#x to device PID=0x%llx DCR=%#x BCR=%#x",
next_addr, pid, dcr, bcr);
dev_dbg(&hci->master.dev,
"assigned address %#x to device PID=0x%llx DCR=%#x BCR=%#x",
next_addr, pid, dcr, bcr);
mipi_i3c_hci_dat_v1.free_entry(hci, dat_idx);
dat_idx = -1;

View File

@@ -261,7 +261,7 @@ static int hci_cmd_v2_daa(struct i3c_hci *hci)
if (ret < 0)
break;
next_addr = ret;
DBG("next_addr = 0x%02x", next_addr);
dev_dbg(&hci->master.dev, "next_addr = 0x%02x", next_addr);
xfer[0].cmd_tid = hci_get_tid();
xfer[0].cmd_desc[0] =
CMD_0_ATTR_A |
@@ -293,8 +293,9 @@ static int hci_cmd_v2_daa(struct i3c_hci *hci)
pid = (pid << 32) | device_id[0];
bcr = FIELD_GET(W1_MASK(55, 48), device_id[1]);
dcr = FIELD_GET(W1_MASK(63, 56), device_id[1]);
DBG("assigned address %#x to device PID=0x%llx DCR=%#x BCR=%#x",
next_addr, pid, dcr, bcr);
dev_dbg(&hci->master.dev,
"assigned address %#x to device PID=0x%llx DCR=%#x BCR=%#x",
next_addr, pid, dcr, bcr);
/*
* TODO: Extend the subsystem layer to allow for registering
* new device and provide BCR/DCR/PID at the same time.

View File

@@ -121,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)
@@ -149,7 +147,7 @@ static int i3c_hci_bus_init(struct i3c_master_controller *m)
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;
}
@@ -159,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);
@@ -196,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)
@@ -255,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);
@@ -267,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)
@@ -311,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)
@@ -333,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;
@@ -359,9 +322,6 @@ 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;
}
@@ -375,14 +335,14 @@ 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)
return -ENOMEM;
for (i = 0; i < nxfers; i++) {
xfer[i].data = i2c_get_dma_safe_msg_buf(&i2c_xfers[i], 1);
xfer[i].data = i2c_xfers[i].buf;
xfer[i].data_len = i2c_xfers[i].len;
xfer[i].rnw = i2c_xfers[i].flags & I2C_M_RD;
hci->cmd->prep_i2c_xfer(hci, dev, &xfer[i]);
@@ -408,10 +368,6 @@ static int i3c_hci_i2c_xfers(struct i2c_dev_desc *dev,
}
out:
for (i = 0; i < nxfers; i++)
i2c_put_dma_safe_msg_buf(xfer[i].data, &i2c_xfers[i],
ret ? false : true);
hci_free_xfer(xfer, nxfers);
return ret;
}
@@ -423,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;
@@ -448,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);
@@ -462,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);
@@ -477,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);
@@ -502,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)
@@ -591,7 +537,7 @@ static irqreturn_t i3c_hci_irq_handler(int irq, void *dev_id)
val = reg_read(INTR_STATUS);
reg_write(INTR_STATUS, val);
DBG("INTR_STATUS = %#x", val);
dev_dbg(&hci->master.dev, "INTR_STATUS %#x", val);
if (val)
result = IRQ_HANDLED;
@@ -641,7 +587,7 @@ 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);

View File

@@ -14,6 +14,7 @@
#include <linux/errno.h>
#include <linux/i3c/master.h>
#include <linux/io.h>
#include <linux/pci.h>
#include "hci.h"
#include "cmd.h"
@@ -76,7 +77,6 @@
#define INTR_TRANSFER_COMPLETION BIT(11)
#define INTR_RING_OP BIT(10)
#define INTR_TRANSFER_ERR BIT(9)
#define INTR_WARN_INS_STOP_MODE BIT(7)
#define INTR_IBI_RING_FULL BIT(6)
#define INTR_TRANSFER_ABORT BIT(5)
@@ -138,6 +138,7 @@ struct hci_rh_data {
};
struct hci_rings_data {
struct device *sysdev;
unsigned int total;
struct hci_rh_data headers[] __counted_by(total);
};
@@ -165,20 +166,20 @@ static void hci_dma_cleanup(struct i3c_hci *hci)
rh_reg_write(IBI_SETUP, 0);
if (rh->xfer)
dma_free_coherent(&hci->master.dev,
dma_free_coherent(rings->sysdev,
rh->xfer_struct_sz * rh->xfer_entries,
rh->xfer, rh->xfer_dma);
if (rh->resp)
dma_free_coherent(&hci->master.dev,
dma_free_coherent(rings->sysdev,
rh->resp_struct_sz * rh->xfer_entries,
rh->resp, rh->resp_dma);
kfree(rh->src_xfers);
if (rh->ibi_status)
dma_free_coherent(&hci->master.dev,
dma_free_coherent(rings->sysdev,
rh->ibi_status_sz * rh->ibi_status_entries,
rh->ibi_status, rh->ibi_status_dma);
if (rh->ibi_data_dma)
dma_unmap_single(&hci->master.dev, rh->ibi_data_dma,
dma_unmap_single(rings->sysdev, rh->ibi_data_dma,
rh->ibi_chunk_sz * rh->ibi_chunks_total,
DMA_FROM_DEVICE);
kfree(rh->ibi_data);
@@ -194,11 +195,23 @@ static int hci_dma_init(struct i3c_hci *hci)
{
struct hci_rings_data *rings;
struct hci_rh_data *rh;
struct device *sysdev;
u32 regval;
unsigned int i, nr_rings, xfers_sz, resps_sz;
unsigned int ibi_status_ring_sz, ibi_data_ring_sz;
int ret;
/*
* Set pointer to a physical device that does DMA and has IOMMU setup
* done for it in case of enabled IOMMU and use it with the DMA API.
* Here such device is either
* "mipi-i3c-hci" platform device (OF/ACPI enumeration) parent or
* grandparent (PCI enumeration).
*/
sysdev = hci->master.dev.parent;
if (sysdev->parent && dev_is_pci(sysdev->parent))
sysdev = sysdev->parent;
regval = rhs_reg_read(CONTROL);
nr_rings = FIELD_GET(MAX_HEADER_COUNT_CAP, regval);
dev_info(&hci->master.dev, "%d DMA rings available\n", nr_rings);
@@ -213,6 +226,7 @@ static int hci_dma_init(struct i3c_hci *hci)
return -ENOMEM;
hci->io_data = rings;
rings->total = nr_rings;
rings->sysdev = sysdev;
regval = FIELD_PREP(MAX_HEADER_COUNT, rings->total);
rhs_reg_write(CONTROL, regval);
@@ -234,14 +248,15 @@ static int hci_dma_init(struct i3c_hci *hci)
regval = rh_reg_read(CR_SETUP);
rh->xfer_struct_sz = FIELD_GET(CR_XFER_STRUCT_SIZE, regval);
rh->resp_struct_sz = FIELD_GET(CR_RESP_STRUCT_SIZE, regval);
DBG("xfer_struct_sz = %d, resp_struct_sz = %d",
rh->xfer_struct_sz, rh->resp_struct_sz);
dev_dbg(&hci->master.dev,
"xfer_struct_sz = %d, resp_struct_sz = %d",
rh->xfer_struct_sz, rh->resp_struct_sz);
xfers_sz = rh->xfer_struct_sz * rh->xfer_entries;
resps_sz = rh->resp_struct_sz * rh->xfer_entries;
rh->xfer = dma_alloc_coherent(&hci->master.dev, xfers_sz,
rh->xfer = dma_alloc_coherent(rings->sysdev, xfers_sz,
&rh->xfer_dma, GFP_KERNEL);
rh->resp = dma_alloc_coherent(&hci->master.dev, resps_sz,
rh->resp = dma_alloc_coherent(rings->sysdev, resps_sz,
&rh->resp_dma, GFP_KERNEL);
rh->src_xfers =
kmalloc_array(rh->xfer_entries, sizeof(*rh->src_xfers),
@@ -263,7 +278,6 @@ static int hci_dma_init(struct i3c_hci *hci)
INTR_TRANSFER_COMPLETION |
INTR_RING_OP |
INTR_TRANSFER_ERR |
INTR_WARN_INS_STOP_MODE |
INTR_IBI_RING_FULL |
INTR_TRANSFER_ABORT);
@@ -295,16 +309,16 @@ static int hci_dma_init(struct i3c_hci *hci)
ibi_data_ring_sz = rh->ibi_chunk_sz * rh->ibi_chunks_total;
rh->ibi_status =
dma_alloc_coherent(&hci->master.dev, ibi_status_ring_sz,
dma_alloc_coherent(rings->sysdev, ibi_status_ring_sz,
&rh->ibi_status_dma, GFP_KERNEL);
rh->ibi_data = kmalloc(ibi_data_ring_sz, GFP_KERNEL);
ret = -ENOMEM;
if (!rh->ibi_status || !rh->ibi_data)
goto err_out;
rh->ibi_data_dma =
dma_map_single(&hci->master.dev, rh->ibi_data,
dma_map_single(rings->sysdev, rh->ibi_data,
ibi_data_ring_sz, DMA_FROM_DEVICE);
if (dma_mapping_error(&hci->master.dev, rh->ibi_data_dma)) {
if (dma_mapping_error(rings->sysdev, rh->ibi_data_dma)) {
rh->ibi_data_dma = 0;
ret = -ENOMEM;
goto err_out;
@@ -349,9 +363,7 @@ static void hci_dma_unmap_xfer(struct i3c_hci *hci,
xfer = xfer_list + i;
if (!xfer->data)
continue;
dma_unmap_single(&hci->master.dev,
xfer->data_dma, xfer->data_len,
xfer->rnw ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
i3c_master_dma_unmap_single(xfer->dma);
}
}
@@ -362,7 +374,6 @@ static int hci_dma_queue_xfer(struct i3c_hci *hci,
struct hci_rh_data *rh;
unsigned int i, ring, enqueue_ptr;
u32 op1_val, op2_val;
void *buf;
/* For now we only use ring 0 */
ring = 0;
@@ -373,6 +384,9 @@ static int hci_dma_queue_xfer(struct i3c_hci *hci,
for (i = 0; i < n; i++) {
struct hci_xfer *xfer = xfer_list + i;
u32 *ring_data = rh->xfer + rh->xfer_struct_sz * enqueue_ptr;
enum dma_data_direction dir = xfer->rnw ? DMA_FROM_DEVICE :
DMA_TO_DEVICE;
bool need_bounce;
/* store cmd descriptor */
*ring_data++ = xfer->cmd_desc[0];
@@ -391,21 +405,20 @@ static int hci_dma_queue_xfer(struct i3c_hci *hci,
/* 2nd and 3rd words of Data Buffer Descriptor Structure */
if (xfer->data) {
buf = xfer->bounce_buf ? xfer->bounce_buf : xfer->data;
xfer->data_dma =
dma_map_single(&hci->master.dev,
buf,
xfer->data_len,
xfer->rnw ?
DMA_FROM_DEVICE :
DMA_TO_DEVICE);
if (dma_mapping_error(&hci->master.dev,
xfer->data_dma)) {
need_bounce = device_iommu_mapped(rings->sysdev) &&
xfer->rnw &&
xfer->data_len != ALIGN(xfer->data_len, 4);
xfer->dma = i3c_master_dma_map_single(rings->sysdev,
xfer->data,
xfer->data_len,
need_bounce,
dir);
if (!xfer->dma) {
hci_dma_unmap_xfer(hci, xfer_list, i);
return -ENOMEM;
}
*ring_data++ = lower_32_bits(xfer->data_dma);
*ring_data++ = upper_32_bits(xfer->data_dma);
*ring_data++ = lower_32_bits(xfer->dma->addr);
*ring_data++ = upper_32_bits(xfer->dma->addr);
} else {
*ring_data++ = 0;
*ring_data++ = 0;
@@ -511,11 +524,11 @@ static void hci_dma_xfer_done(struct i3c_hci *hci, struct hci_rh_data *rh)
ring_resp = rh->resp + rh->resp_struct_sz * done_ptr;
resp = *ring_resp;
tid = RESP_TID(resp);
DBG("resp = 0x%08x", resp);
dev_dbg(&hci->master.dev, "resp = 0x%08x", resp);
xfer = rh->src_xfers[done_ptr];
if (!xfer) {
DBG("orphaned ring entry");
dev_dbg(&hci->master.dev, "orphaned ring entry");
} else {
hci_dma_unmap_xfer(hci, xfer, 1);
xfer->ring_entry = -1;
@@ -586,6 +599,7 @@ static void hci_dma_recycle_ibi_slot(struct i3c_hci *hci,
static void hci_dma_process_ibi(struct i3c_hci *hci, struct hci_rh_data *rh)
{
struct hci_rings_data *rings = hci->io_data;
struct i3c_dev_desc *dev;
struct i3c_hci_dev_data *dev_data;
struct hci_dma_dev_ibi_data *dev_ibi;
@@ -617,7 +631,7 @@ static void hci_dma_process_ibi(struct i3c_hci *hci, struct hci_rh_data *rh)
ring_ibi_status = rh->ibi_status + rh->ibi_status_sz * ptr;
ibi_status = *ring_ibi_status;
DBG("status = %#x", ibi_status);
dev_dbg(&hci->master.dev, "status = %#x", ibi_status);
if (ibi_status_error) {
/* we no longer care */
@@ -645,7 +659,9 @@ static void hci_dma_process_ibi(struct i3c_hci *hci, struct hci_rh_data *rh)
if (last_ptr == -1) {
/* this IBI sequence is not yet complete */
DBG("no LAST_STATUS available (e=%d d=%d)", enq_ptr, deq_ptr);
dev_dbg(&hci->master.dev,
"no LAST_STATUS available (e=%d d=%d)",
enq_ptr, deq_ptr);
return;
}
deq_ptr = last_ptr + 1;
@@ -696,7 +712,7 @@ static void hci_dma_process_ibi(struct i3c_hci *hci, struct hci_rh_data *rh)
* rh->ibi_chunk_sz;
if (first_part > ibi_size)
first_part = ibi_size;
dma_sync_single_for_cpu(&hci->master.dev, ring_ibi_data_dma,
dma_sync_single_for_cpu(rings->sysdev, ring_ibi_data_dma,
first_part, DMA_FROM_DEVICE);
memcpy(slot->data, ring_ibi_data, first_part);
@@ -705,7 +721,7 @@ static void hci_dma_process_ibi(struct i3c_hci *hci, struct hci_rh_data *rh)
/* we wrap back to the start and copy remaining data */
ring_ibi_data = rh->ibi_data;
ring_ibi_data_dma = rh->ibi_data_dma;
dma_sync_single_for_cpu(&hci->master.dev, ring_ibi_data_dma,
dma_sync_single_for_cpu(rings->sysdev, ring_ibi_data_dma,
ibi_size - first_part, DMA_FROM_DEVICE);
memcpy(slot->data + first_part, ring_ibi_data,
ibi_size - first_part);
@@ -745,7 +761,8 @@ static bool hci_dma_irq_handler(struct i3c_hci *hci)
rh = &rings->headers[i];
status = rh_reg_read(INTR_STATUS);
DBG("rh%d status: %#x", i, status);
dev_dbg(&hci->master.dev, "Ring %d: RH_INTR_STATUS %#x",
i, status);
if (!status)
continue;
rh_reg_write(INTR_STATUS, status);
@@ -761,7 +778,7 @@ static bool hci_dma_irq_handler(struct i3c_hci *hci)
u32 ring_status;
dev_notice_ratelimited(&hci->master.dev,
"ring %d: Transfer Aborted\n", i);
"Ring %d: Transfer Aborted\n", i);
mipi_i3c_hci_resume(hci);
ring_status = rh_reg_read(RING_STATUS);
if (!(ring_status & RING_STATUS_RUNNING) &&
@@ -779,12 +796,9 @@ static bool hci_dma_irq_handler(struct i3c_hci *hci)
RING_CTRL_RUN_STOP);
}
}
if (status & INTR_WARN_INS_STOP_MODE)
dev_warn_ratelimited(&hci->master.dev,
"ring %d: Inserted Stop on Mode Change\n", i);
if (status & INTR_IBI_RING_FULL)
dev_err_ratelimited(&hci->master.dev,
"ring %d: IBI Ring Full Condition\n", i);
"Ring %d: IBI Ring Full Condition\n", i);
handled = true;
}

View File

@@ -35,7 +35,7 @@ static int hci_extcap_hardware_id(struct i3c_hci *hci, void __iomem *base)
switch (hci->vendor_mipi_id) {
case MIPI_VENDOR_NXP:
hci->quirks |= HCI_QUIRK_RAW_CCC;
DBG("raw CCC quirks set");
dev_dbg(&hci->master.dev, "raw CCC quirks set");
break;
}
@@ -77,7 +77,8 @@ static int hci_extcap_xfer_modes(struct i3c_hci *hci, void __iomem *base)
for (index = 0; index < entries; index++) {
u32 mode_entry = readl(base);
DBG("mode %d: 0x%08x", index, mode_entry);
dev_dbg(&hci->master.dev, "mode %d: 0x%08x",
index, mode_entry);
/* TODO: will be needed when I3C core does more than SDR */
base += 4;
}
@@ -97,7 +98,8 @@ static int hci_extcap_xfer_rates(struct i3c_hci *hci, void __iomem *base)
dev_info(&hci->master.dev, "available data rates:\n");
for (index = 0; index < entries; index++) {
rate_entry = readl(base);
DBG("entry %d: 0x%08x", index, rate_entry);
dev_dbg(&hci->master.dev, "entry %d: 0x%08x",
index, rate_entry);
rate = FIELD_GET(XFERRATE_ACTUAL_RATE_KHZ, rate_entry);
rate_id = FIELD_GET(XFERRATE_RATE_ID, rate_entry);
mode_id = FIELD_GET(XFERRATE_MODE_ID, rate_entry);
@@ -268,7 +270,8 @@ int i3c_hci_parse_ext_caps(struct i3c_hci *hci)
cap_header = readl(curr_cap);
cap_id = FIELD_GET(CAP_HEADER_ID, cap_header);
cap_length = FIELD_GET(CAP_HEADER_LENGTH, cap_header);
DBG("id=0x%02x length=%d", cap_id, cap_length);
dev_dbg(&hci->master.dev, "id=0x%02x length=%d",
cap_id, cap_length);
if (!cap_length)
break;
if (curr_cap + cap_length * 4 >= end) {

View File

@@ -12,9 +12,6 @@
#include <linux/io.h>
/* Handy logging macro to save on line length */
#define DBG(x, ...) pr_devel("%s: " x "\n", __func__, ##__VA_ARGS__)
/* 32-bit word aware bit and mask macros */
#define W0_MASK(h, l) GENMASK((h) - 0, (l) - 0)
#define W1_MASK(h, l) GENMASK((h) - 32, (l) - 32)
@@ -94,8 +91,7 @@ struct hci_xfer {
};
struct {
/* DMA specific */
dma_addr_t data_dma;
void *bounce_buf;
struct i3c_dma *dma;
int ring_number;
int ring_entry;
};

View File

@@ -124,6 +124,9 @@ static void mipi_i3c_hci_pci_remove(struct pci_dev *pci)
}
static const struct pci_device_id mipi_i3c_hci_pci_devices[] = {
/* Wildcat Lake-U */
{ PCI_VDEVICE(INTEL, 0x4d7c), (kernel_ulong_t)&intel_info},
{ PCI_VDEVICE(INTEL, 0x4d6f), (kernel_ulong_t)&intel_info},
/* Panther Lake-H */
{ PCI_VDEVICE(INTEL, 0xe37c), (kernel_ulong_t)&intel_info},
{ PCI_VDEVICE(INTEL, 0xe36f), (kernel_ulong_t)&intel_info},

View File

@@ -213,8 +213,8 @@ static void hci_pio_cleanup(struct i3c_hci *hci)
pio_reg_write(INTR_SIGNAL_ENABLE, 0x0);
if (pio) {
DBG("status = %#x/%#x",
pio_reg_read(INTR_STATUS), pio_reg_read(INTR_SIGNAL_ENABLE));
dev_dbg(&hci->master.dev, "status = %#x/%#x",
pio_reg_read(INTR_STATUS), pio_reg_read(INTR_SIGNAL_ENABLE));
BUG_ON(pio->curr_xfer);
BUG_ON(pio->curr_rx);
BUG_ON(pio->curr_tx);
@@ -226,13 +226,17 @@ static void hci_pio_cleanup(struct i3c_hci *hci)
static void hci_pio_write_cmd(struct i3c_hci *hci, struct hci_xfer *xfer)
{
DBG("cmd_desc[%d] = 0x%08x", 0, xfer->cmd_desc[0]);
DBG("cmd_desc[%d] = 0x%08x", 1, xfer->cmd_desc[1]);
dev_dbg(&hci->master.dev, "cmd_desc[%d] = 0x%08x",
0, xfer->cmd_desc[0]);
dev_dbg(&hci->master.dev, "cmd_desc[%d] = 0x%08x",
1, xfer->cmd_desc[1]);
pio_reg_write(COMMAND_QUEUE_PORT, xfer->cmd_desc[0]);
pio_reg_write(COMMAND_QUEUE_PORT, xfer->cmd_desc[1]);
if (hci->cmd == &mipi_i3c_hci_cmd_v2) {
DBG("cmd_desc[%d] = 0x%08x", 2, xfer->cmd_desc[2]);
DBG("cmd_desc[%d] = 0x%08x", 3, xfer->cmd_desc[3]);
dev_dbg(&hci->master.dev, "cmd_desc[%d] = 0x%08x",
2, xfer->cmd_desc[2]);
dev_dbg(&hci->master.dev, "cmd_desc[%d] = 0x%08x",
3, xfer->cmd_desc[3]);
pio_reg_write(COMMAND_QUEUE_PORT, xfer->cmd_desc[2]);
pio_reg_write(COMMAND_QUEUE_PORT, xfer->cmd_desc[3]);
}
@@ -254,7 +258,8 @@ static bool hci_pio_do_rx(struct i3c_hci *hci, struct hci_pio_data *pio)
nr_words = min(xfer->data_left / 4, pio->rx_thresh_size);
/* extract data from FIFO */
xfer->data_left -= nr_words * 4;
DBG("now %d left %d", nr_words * 4, xfer->data_left);
dev_dbg(&hci->master.dev, "now %d left %d",
nr_words * 4, xfer->data_left);
while (nr_words--)
*p++ = pio_reg_read(XFER_DATA_PORT);
}
@@ -269,7 +274,7 @@ static void hci_pio_do_trailing_rx(struct i3c_hci *hci,
struct hci_xfer *xfer = pio->curr_rx;
u32 *p;
DBG("%d remaining", count);
dev_dbg(&hci->master.dev, "%d remaining", count);
p = xfer->data;
p += (xfer->data_len - xfer->data_left) / 4;
@@ -278,7 +283,8 @@ static void hci_pio_do_trailing_rx(struct i3c_hci *hci,
unsigned int nr_words = count / 4;
/* extract data from FIFO */
xfer->data_left -= nr_words * 4;
DBG("now %d left %d", nr_words * 4, xfer->data_left);
dev_dbg(&hci->master.dev, "now %d left %d",
nr_words * 4, xfer->data_left);
while (nr_words--)
*p++ = pio_reg_read(XFER_DATA_PORT);
}
@@ -321,7 +327,8 @@ static bool hci_pio_do_tx(struct i3c_hci *hci, struct hci_pio_data *pio)
nr_words = min(xfer->data_left / 4, pio->tx_thresh_size);
/* push data into the FIFO */
xfer->data_left -= nr_words * 4;
DBG("now %d left %d", nr_words * 4, xfer->data_left);
dev_dbg(&hci->master.dev, "now %d left %d",
nr_words * 4, xfer->data_left);
while (nr_words--)
pio_reg_write(XFER_DATA_PORT, *p++);
}
@@ -336,7 +343,7 @@ static bool hci_pio_do_tx(struct i3c_hci *hci, struct hci_pio_data *pio)
*/
if (!(pio_reg_read(INTR_STATUS) & STAT_TX_THLD))
return false;
DBG("trailing %d", xfer->data_left);
dev_dbg(&hci->master.dev, "trailing %d", xfer->data_left);
pio_reg_write(XFER_DATA_PORT, *p);
xfer->data_left = 0;
}
@@ -481,7 +488,7 @@ static bool hci_pio_process_resp(struct i3c_hci *hci, struct hci_pio_data *pio)
u32 resp = pio_reg_read(RESPONSE_QUEUE_PORT);
unsigned int tid = RESP_TID(resp);
DBG("resp = 0x%08x", resp);
dev_dbg(&hci->master.dev, "resp = 0x%08x", resp);
if (tid != xfer->cmd_tid) {
dev_err(&hci->master.dev,
"response tid=%d when expecting %d\n",
@@ -522,14 +529,15 @@ static bool hci_pio_process_resp(struct i3c_hci *hci, struct hci_pio_data *pio)
* still exists.
*/
if (pio->curr_rx == xfer) {
DBG("short RX ?");
dev_dbg(&hci->master.dev, "short RX ?");
pio->curr_rx = pio->curr_rx->next_data;
} else if (pio->curr_tx == xfer) {
DBG("short TX ?");
dev_dbg(&hci->master.dev, "short TX ?");
pio->curr_tx = pio->curr_tx->next_data;
} else if (xfer->data_left) {
DBG("PIO xfer count = %d after response",
xfer->data_left);
dev_dbg(&hci->master.dev,
"PIO xfer count = %d after response",
xfer->data_left);
}
pio->curr_resp = xfer->next_resp;
@@ -591,7 +599,7 @@ static int hci_pio_queue_xfer(struct i3c_hci *hci, struct hci_xfer *xfer, int n)
struct hci_xfer *prev_queue_tail;
int i;
DBG("n = %d", n);
dev_dbg(&hci->master.dev, "n = %d", n);
/* link xfer instances together and initialize data count */
for (i = 0; i < n; i++) {
@@ -611,8 +619,9 @@ static int hci_pio_queue_xfer(struct i3c_hci *hci, struct hci_xfer *xfer, int n)
if (!hci_pio_process_cmd(hci, pio))
pio->enabled_irqs |= STAT_CMD_QUEUE_READY;
pio_reg_write(INTR_SIGNAL_ENABLE, pio->enabled_irqs);
DBG("status = %#x/%#x",
pio_reg_read(INTR_STATUS), pio_reg_read(INTR_SIGNAL_ENABLE));
dev_dbg(&hci->master.dev, "status = %#x/%#x",
pio_reg_read(INTR_STATUS),
pio_reg_read(INTR_SIGNAL_ENABLE));
}
spin_unlock_irq(&pio->lock);
return 0;
@@ -686,10 +695,10 @@ static bool hci_pio_dequeue_xfer(struct i3c_hci *hci, struct hci_xfer *xfer, int
int ret;
spin_lock_irq(&pio->lock);
DBG("n=%d status=%#x/%#x", n,
pio_reg_read(INTR_STATUS), pio_reg_read(INTR_SIGNAL_ENABLE));
DBG("main_status = %#x/%#x",
readl(hci->base_regs + 0x20), readl(hci->base_regs + 0x28));
dev_dbg(&hci->master.dev, "n=%d status=%#x/%#x", n,
pio_reg_read(INTR_STATUS), pio_reg_read(INTR_SIGNAL_ENABLE));
dev_dbg(&hci->master.dev, "main_status = %#x/%#x",
readl(hci->base_regs + 0x20), readl(hci->base_regs + 0x28));
ret = hci_pio_dequeue_xfer_common(hci, pio, xfer, n);
spin_unlock_irq(&pio->lock);
@@ -733,8 +742,8 @@ static void hci_pio_err(struct i3c_hci *hci, struct hci_pio_data *pio,
mipi_i3c_hci_pio_reset(hci);
mipi_i3c_hci_resume(hci);
DBG("status=%#x/%#x",
pio_reg_read(INTR_STATUS), pio_reg_read(INTR_SIGNAL_ENABLE));
dev_dbg(&hci->master.dev, "status=%#x/%#x",
pio_reg_read(INTR_STATUS), pio_reg_read(INTR_SIGNAL_ENABLE));
}
static void hci_pio_set_ibi_thresh(struct i3c_hci *hci,
@@ -749,7 +758,7 @@ static void hci_pio_set_ibi_thresh(struct i3c_hci *hci,
if (regval != pio->reg_queue_thresh) {
pio_reg_write(QUEUE_THLD_CTRL, regval);
pio->reg_queue_thresh = regval;
DBG("%d", thresh_val);
dev_dbg(&hci->master.dev, "%d", thresh_val);
}
}
@@ -773,7 +782,8 @@ static bool hci_pio_get_ibi_segment(struct i3c_hci *hci,
/* extract the data from the IBI port */
nr_words = thresh_val;
ibi->seg_cnt -= nr_words * 4;
DBG("now %d left %d", nr_words * 4, ibi->seg_cnt);
dev_dbg(&hci->master.dev, "now %d left %d",
nr_words * 4, ibi->seg_cnt);
while (nr_words--)
*p++ = pio_reg_read(IBI_PORT);
}
@@ -791,7 +801,7 @@ static bool hci_pio_get_ibi_segment(struct i3c_hci *hci,
hci_pio_set_ibi_thresh(hci, pio, 1);
if (!(pio_reg_read(INTR_STATUS) & STAT_IBI_STATUS_THLD))
return false;
DBG("trailing %d", ibi->seg_cnt);
dev_dbg(&hci->master.dev, "trailing %d", ibi->seg_cnt);
data = pio_reg_read(IBI_PORT);
data = (__force u32) cpu_to_le32(data);
while (ibi->seg_cnt--) {
@@ -820,7 +830,7 @@ static bool hci_pio_prep_new_ibi(struct i3c_hci *hci, struct hci_pio_data *pio)
*/
ibi_status = pio_reg_read(IBI_PORT);
DBG("status = %#x", ibi_status);
dev_dbg(&hci->master.dev, "status = %#x", ibi_status);
ibi->addr = FIELD_GET(IBI_TARGET_ADDR, ibi_status);
if (ibi_status & IBI_ERROR) {
dev_err(&hci->master.dev, "IBI error from %#x\n", ibi->addr);
@@ -986,7 +996,8 @@ static bool hci_pio_irq_handler(struct i3c_hci *hci)
spin_lock(&pio->lock);
status = pio_reg_read(INTR_STATUS);
DBG("(in) status: %#x/%#x", status, pio->enabled_irqs);
dev_dbg(&hci->master.dev, "PIO_INTR_STATUS %#x/%#x",
status, pio->enabled_irqs);
status &= pio->enabled_irqs | STAT_LATENCY_WARNINGS;
if (!status) {
spin_unlock(&pio->lock);
@@ -1023,8 +1034,8 @@ static bool hci_pio_irq_handler(struct i3c_hci *hci)
pio->enabled_irqs &= ~STAT_CMD_QUEUE_READY;
pio_reg_write(INTR_SIGNAL_ENABLE, pio->enabled_irqs);
DBG("(out) status: %#x/%#x",
pio_reg_read(INTR_STATUS), pio_reg_read(INTR_SIGNAL_ENABLE));
dev_dbg(&hci->master.dev, "PIO_INTR_STATUS %#x/%#x",
pio_reg_read(INTR_STATUS), pio_reg_read(INTR_SIGNAL_ENABLE));
spin_unlock(&pio->lock);
return true;
}

View File

@@ -679,7 +679,7 @@ static int renesas_i3c_daa(struct i3c_master_controller *m)
i3c_master_add_i3c_dev_locked(m, i3c->addrs[pos]);
}
return ret < 0 ? ret : 0;
return 0;
}
static bool renesas_i3c_supports_ccc_cmd(struct i3c_master_controller *m,

View File

@@ -417,6 +417,7 @@ static int svc_i3c_master_handle_ibi(struct svc_i3c_master *master,
SVC_I3C_MSTATUS_COMPLETE(val), 0, 1000);
if (ret) {
dev_err(master->dev, "Timeout when polling for COMPLETE\n");
i3c_generic_ibi_recycle_slot(data->ibi_pool, slot);
return ret;
}
@@ -517,9 +518,24 @@ static void svc_i3c_master_ibi_isr(struct svc_i3c_master *master)
*/
writel(SVC_I3C_MINT_IBIWON, master->regs + SVC_I3C_MSTATUS);
/* Acknowledge the incoming interrupt with the AUTOIBI mechanism */
writel(SVC_I3C_MCTRL_REQUEST_AUTO_IBI |
SVC_I3C_MCTRL_IBIRESP_AUTO,
/*
* Write REQUEST_START_ADDR request to emit broadcast address for arbitration,
* instend of using AUTO_IBI.
*
* Using AutoIBI request may cause controller to remain in AutoIBI state when
* there is a glitch on SDA line (high->low->high).
* 1. SDA high->low, raising an interrupt to execute IBI isr.
* 2. SDA low->high.
* 3. IBI isr writes an AutoIBI request.
* 4. The controller will not start AutoIBI process because SDA is not low.
* 5. IBIWON polling times out.
* 6. Controller reamins in AutoIBI state and doesn't accept EmitStop request.
*/
writel(SVC_I3C_MCTRL_REQUEST_START_ADDR |
SVC_I3C_MCTRL_TYPE_I3C |
SVC_I3C_MCTRL_IBIRESP_MANUAL |
SVC_I3C_MCTRL_DIR(SVC_I3C_MCTRL_DIR_WRITE) |
SVC_I3C_MCTRL_ADDR(I3C_BROADCAST_ADDR),
master->regs + SVC_I3C_MCTRL);
/* Wait for IBIWON, should take approximately 100us */
@@ -539,10 +555,15 @@ static void svc_i3c_master_ibi_isr(struct svc_i3c_master *master)
switch (ibitype) {
case SVC_I3C_MSTATUS_IBITYPE_IBI:
dev = svc_i3c_master_dev_from_addr(master, ibiaddr);
if (!dev || !is_events_enabled(master, SVC_I3C_EVENT_IBI))
if (!dev || !is_events_enabled(master, SVC_I3C_EVENT_IBI)) {
svc_i3c_master_nack_ibi(master);
else
} else {
if (dev->info.bcr & I3C_BCR_IBI_PAYLOAD)
svc_i3c_master_ack_ibi(master, true);
else
svc_i3c_master_ack_ibi(master, false);
svc_i3c_master_handle_ibi(master, dev);
}
break;
case SVC_I3C_MSTATUS_IBITYPE_HOT_JOIN:
if (is_events_enabled(master, SVC_I3C_EVENT_HOTJOIN))

View File

@@ -558,6 +558,26 @@ struct i3c_master_controller {
#define i3c_bus_for_each_i3cdev(bus, dev) \
list_for_each_entry(dev, &(bus)->devs.i3c, common.node)
/**
* struct i3c_dma - DMA transfer and mapping descriptor
* @dev: device object of a device doing DMA
* @buf: destination/source buffer for DMA
* @len: length of transfer
* @map_len: length of DMA mapping
* @addr: mapped DMA address for a Host Controller Driver
* @dir: DMA direction
* @bounce_buf: an allocated bounce buffer if transfer needs it or NULL
*/
struct i3c_dma {
struct device *dev;
void *buf;
size_t len;
size_t map_len;
dma_addr_t addr;
enum dma_data_direction dir;
void *bounce_buf;
};
int i3c_master_do_i2c_xfers(struct i3c_master_controller *master,
const struct i2c_msg *xfers,
int nxfers);
@@ -575,6 +595,12 @@ int i3c_master_get_free_addr(struct i3c_master_controller *master,
int i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master,
u8 addr);
int i3c_master_do_daa(struct i3c_master_controller *master);
struct i3c_dma *i3c_master_dma_map_single(struct device *dev, void *ptr,
size_t len, bool force_bounce,
enum dma_data_direction dir);
void i3c_master_dma_unmap_single(struct i3c_dma *dma_xfer);
DEFINE_FREE(i3c_master_dma_unmap_single, void *,
if (_T) i3c_master_dma_unmap_single(_T))
int i3c_master_set_info(struct i3c_master_controller *master,
const struct i3c_device_info *info);