mirror of
https://github.com/torvalds/linux.git
synced 2026-01-25 15:03:52 +08:00
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:
72
Documentation/devicetree/bindings/i3c/adi,i3c-master.yaml
Normal file
72
Documentation/devicetree/bindings/i3c/adi,i3c-master.yaml
Normal 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 */
|
||||
};
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
1019
drivers/i3c/master/adi-i3c-master.c
Normal file
1019
drivers/i3c/master/adi-i3c-master.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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},
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user