I3C fixes for 7.0
Subsystem: - simplify combined i3c/i2c dependencies Drivers: - dw: handle 2C properly, fix possible race condition - mipi-i3c-hci: many DMA related fixes -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEBqsFVZXh8s/0O5JiY6TcMGxwOjIFAmm13MwACgkQY6TcMGxw OjJ9EQ//ZoiUxGeiLkfqHDWq0uEQVuOnYXFhcpNkDrxBB9GUJvt3FWgrAHFt/VqV rSgzC1PfPs7TVRoRtpOXoxcj7J97a5m3BgnqeoDtKXM4aiYx5PoUf7WmOGVSGp+m 9QsQZdJre4WJz7Q4Ozb+XMO4WEgHyZYn2GPVjg2k99S45IfbKXxT5WpAeymdLqL4 Hyz1RpVqhLPdQItQMp6/AIc99L+CDCso4rrwDsRa62fPUFQdLfYPG9XdC/VRexi2 p9EUgmBg8Dw9CzniXknxzB24ZsmCsHwBhK2lfrjmTwElvHG0sUUrVttG2QbMl7MC u3KcKr8ZDAETC7498wkcgdeDA6a5gX7DKRWl9lJ7CCa4QUcCHhdKtLgLSDIhDOLf LPU0AfPZQfZd2g3ErZt6pt8nXstcqAJrbcOBbwe1bHvE4VNl947B+STyfsYTdJOD qyFKe2Qb2ZUUFWyjiAO+lN5QJBxX4fjqJPkCe+PakXxkRhrKGrCBf647UbUNcwjV qs1kdfmH4JYi7WwL1AFmWKkBAEtPGKUXBoVb2nmfArrXawYhYjyWRhAZqthg/gaj iyH/SWe0bOw3sf/bHgxYlzUP3mwqwtln6DihKHlEq3OeW6DoBkE29rc7NhuphTW0 Os9epJIbaKsbvmSV7TA3izpgZHg6qDXliprrvfP/ahSh7w/Gl6g= =Ojo2 -----END PGP SIGNATURE----- Merge tag 'i3c/fixes-for-7.0' of git://git.kernel.org/pub/scm/linux/kernel/git/i3c/linux Pull i3c fixes from Alexandre Belloni: "This introduces the I3C_OR_I2C symbol which is not a fix per se but is affecting multiple subsystems so it is included to ease synchronization. Apart from that, Adrian is mostly fixing the mipi-i3c-hci driver DMA handling, and I took the opportunity to add two fixes for the dw-i3c driver. Subsystem: - simplify combined i3c/i2c dependencies Drivers: - dw: handle 2C properly, fix possible race condition - mipi-i3c-hci: many DMA related fixes" * tag 'i3c/fixes-for-7.0' of git://git.kernel.org/pub/scm/linux/kernel/git/i3c/linux: i3c: dw-i3c-master: Set SIR_REJECT in DAT on device attach and reattach i3c: master: dw-i3c: Fix missing of_node for virtual I2C adapter i3c: mipi-i3c-hci: Fallback to software reset when bus disable fails i3c: mipi-i3c-hci: Fix handling of shared IRQs during early initialization i3c: mipi-i3c-hci: Fix race in DMA error handling in interrupt context i3c: mipi-i3c-hci: Consolidate common xfer processing logic i3c: mipi-i3c-hci: Restart DMA ring correctly after dequeue abort i3c: mipi-i3c-hci: Add missing TID field to no-op command descriptor i3c: mipi-i3c-hci: Correct RING_CTRL_ABORT handling in DMA dequeue i3c: mipi-i3c-hci: Fix race between DMA ring dequeue and interrupt handler i3c: mipi-i3c-hci: Fix race in DMA ring dequeue i3c: mipi-i3c-hci: Fix race in DMA ring enqueue for parallel xfers i3c: mipi-i3c-hci: Consolidate spinlocks i3c: mipi-i3c-hci: Factor out DMA mapping from queuing path i3c: mipi-i3c-hci: Fix Hot-Join NACK i3c: mipi-i3c-hci: Use ETIMEDOUT instead of ETIME for timeout errors i3c: simplify combined i3c/i2c dependencies
This commit is contained in:
commit
9a48d4a130
|
|
@ -1493,8 +1493,7 @@ config SENSORS_LM73
|
||||||
|
|
||||||
config SENSORS_LM75
|
config SENSORS_LM75
|
||||||
tristate "National Semiconductor LM75 and compatibles"
|
tristate "National Semiconductor LM75 and compatibles"
|
||||||
depends on I2C
|
depends on I3C_OR_I2C
|
||||||
depends on I3C || !I3C
|
|
||||||
select REGMAP_I2C
|
select REGMAP_I2C
|
||||||
select REGMAP_I3C if I3C
|
select REGMAP_I3C if I3C
|
||||||
help
|
help
|
||||||
|
|
@ -2382,8 +2381,7 @@ config SENSORS_TMP103
|
||||||
|
|
||||||
config SENSORS_TMP108
|
config SENSORS_TMP108
|
||||||
tristate "Texas Instruments TMP108"
|
tristate "Texas Instruments TMP108"
|
||||||
depends on I2C
|
depends on I3C_OR_I2C
|
||||||
depends on I3C || !I3C
|
|
||||||
select REGMAP_I2C
|
select REGMAP_I2C
|
||||||
select REGMAP_I3C if I3C
|
select REGMAP_I3C if I3C
|
||||||
help
|
help
|
||||||
|
|
|
||||||
|
|
@ -22,3 +22,15 @@ menuconfig I3C
|
||||||
if I3C
|
if I3C
|
||||||
source "drivers/i3c/master/Kconfig"
|
source "drivers/i3c/master/Kconfig"
|
||||||
endif # I3C
|
endif # I3C
|
||||||
|
|
||||||
|
config I3C_OR_I2C
|
||||||
|
tristate
|
||||||
|
default m if I3C=m
|
||||||
|
default I2C
|
||||||
|
help
|
||||||
|
Device drivers using module_i3c_i2c_driver() can use either
|
||||||
|
i2c or i3c hosts, but cannot be built-in for the kernel when
|
||||||
|
CONFIG_I3C=m.
|
||||||
|
|
||||||
|
Add 'depends on I2C_OR_I3C' in Kconfig for those drivers to
|
||||||
|
get the correct dependencies.
|
||||||
|
|
|
||||||
|
|
@ -1024,7 +1024,7 @@ static int dw_i3c_master_reattach_i3c_dev(struct i3c_dev_desc *dev,
|
||||||
master->free_pos &= ~BIT(pos);
|
master->free_pos &= ~BIT(pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
writel(DEV_ADDR_TABLE_DYNAMIC_ADDR(dev->info.dyn_addr),
|
writel(DEV_ADDR_TABLE_DYNAMIC_ADDR(dev->info.dyn_addr) | DEV_ADDR_TABLE_SIR_REJECT,
|
||||||
master->regs +
|
master->regs +
|
||||||
DEV_ADDR_TABLE_LOC(master->datstartaddr, data->index));
|
DEV_ADDR_TABLE_LOC(master->datstartaddr, data->index));
|
||||||
|
|
||||||
|
|
@ -1053,7 +1053,7 @@ static int dw_i3c_master_attach_i3c_dev(struct i3c_dev_desc *dev)
|
||||||
master->free_pos &= ~BIT(pos);
|
master->free_pos &= ~BIT(pos);
|
||||||
i3c_dev_set_master_data(dev, data);
|
i3c_dev_set_master_data(dev, data);
|
||||||
|
|
||||||
writel(DEV_ADDR_TABLE_DYNAMIC_ADDR(master->devs[pos].addr),
|
writel(DEV_ADDR_TABLE_DYNAMIC_ADDR(master->devs[pos].addr) | DEV_ADDR_TABLE_SIR_REJECT,
|
||||||
master->regs +
|
master->regs +
|
||||||
DEV_ADDR_TABLE_LOC(master->datstartaddr, data->index));
|
DEV_ADDR_TABLE_LOC(master->datstartaddr, data->index));
|
||||||
|
|
||||||
|
|
@ -1659,6 +1659,8 @@ int dw_i3c_common_probe(struct dw_i3c_master *master,
|
||||||
pm_runtime_get_noresume(&pdev->dev);
|
pm_runtime_get_noresume(&pdev->dev);
|
||||||
|
|
||||||
INIT_WORK(&master->hj_work, dw_i3c_hj_work);
|
INIT_WORK(&master->hj_work, dw_i3c_hj_work);
|
||||||
|
|
||||||
|
device_set_of_node_from_dev(&master->base.i2c.dev, &pdev->dev);
|
||||||
ret = i3c_master_register(&master->base, &pdev->dev,
|
ret = i3c_master_register(&master->base, &pdev->dev,
|
||||||
&dw_mipi_i3c_ops, false);
|
&dw_mipi_i3c_ops, false);
|
||||||
if (ret)
|
if (ret)
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
#define CMD_0_TOC W0_BIT_(31)
|
#define CMD_0_TOC W0_BIT_(31)
|
||||||
#define CMD_0_ROC W0_BIT_(30)
|
#define CMD_0_ROC W0_BIT_(30)
|
||||||
#define CMD_0_ATTR W0_MASK(2, 0)
|
#define CMD_0_ATTR W0_MASK(2, 0)
|
||||||
|
#define CMD_0_TID W0_MASK(6, 3)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Response Descriptor Structure
|
* Response Descriptor Structure
|
||||||
|
|
|
||||||
|
|
@ -331,12 +331,10 @@ static int hci_cmd_v1_daa(struct i3c_hci *hci)
|
||||||
CMD_A0_ROC | CMD_A0_TOC;
|
CMD_A0_ROC | CMD_A0_TOC;
|
||||||
xfer->cmd_desc[1] = 0;
|
xfer->cmd_desc[1] = 0;
|
||||||
xfer->completion = &done;
|
xfer->completion = &done;
|
||||||
hci->io->queue_xfer(hci, xfer, 1);
|
xfer->timeout = HZ;
|
||||||
if (!wait_for_completion_timeout(&done, HZ) &&
|
ret = i3c_hci_process_xfer(hci, xfer, 1);
|
||||||
hci->io->dequeue_xfer(hci, xfer, 1)) {
|
if (ret)
|
||||||
ret = -ETIME;
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
if ((RESP_STATUS(xfer->response) == RESP_ERR_ADDR_HEADER ||
|
if ((RESP_STATUS(xfer->response) == RESP_ERR_ADDR_HEADER ||
|
||||||
RESP_STATUS(xfer->response) == RESP_ERR_NACK) &&
|
RESP_STATUS(xfer->response) == RESP_ERR_NACK) &&
|
||||||
RESP_DATA_LENGTH(xfer->response) == 1) {
|
RESP_DATA_LENGTH(xfer->response) == 1) {
|
||||||
|
|
|
||||||
|
|
@ -253,6 +253,7 @@ static int hci_cmd_v2_daa(struct i3c_hci *hci)
|
||||||
xfer[0].rnw = true;
|
xfer[0].rnw = true;
|
||||||
xfer[0].cmd_desc[1] = CMD_A1_DATA_LENGTH(8);
|
xfer[0].cmd_desc[1] = CMD_A1_DATA_LENGTH(8);
|
||||||
xfer[1].completion = &done;
|
xfer[1].completion = &done;
|
||||||
|
xfer[1].timeout = HZ;
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
ret = i3c_master_get_free_addr(&hci->master, next_addr);
|
ret = i3c_master_get_free_addr(&hci->master, next_addr);
|
||||||
|
|
@ -272,12 +273,9 @@ static int hci_cmd_v2_daa(struct i3c_hci *hci)
|
||||||
CMD_A0_ASSIGN_ADDRESS(next_addr) |
|
CMD_A0_ASSIGN_ADDRESS(next_addr) |
|
||||||
CMD_A0_ROC |
|
CMD_A0_ROC |
|
||||||
CMD_A0_TOC;
|
CMD_A0_TOC;
|
||||||
hci->io->queue_xfer(hci, xfer, 2);
|
ret = i3c_hci_process_xfer(hci, xfer, 2);
|
||||||
if (!wait_for_completion_timeout(&done, HZ) &&
|
if (ret)
|
||||||
hci->io->dequeue_xfer(hci, xfer, 2)) {
|
|
||||||
ret = -ETIME;
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
if (RESP_STATUS(xfer[0].response) != RESP_SUCCESS) {
|
if (RESP_STATUS(xfer[0].response) != RESP_SUCCESS) {
|
||||||
ret = 0; /* no more devices to be assigned */
|
ret = 0; /* no more devices to be assigned */
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -152,7 +152,11 @@ static int i3c_hci_bus_init(struct i3c_master_controller *m)
|
||||||
if (hci->quirks & HCI_QUIRK_RESP_BUF_THLD)
|
if (hci->quirks & HCI_QUIRK_RESP_BUF_THLD)
|
||||||
amd_set_resp_buf_thld(hci);
|
amd_set_resp_buf_thld(hci);
|
||||||
|
|
||||||
reg_set(HC_CONTROL, HC_CONTROL_BUS_ENABLE);
|
scoped_guard(spinlock_irqsave, &hci->lock)
|
||||||
|
hci->irq_inactive = false;
|
||||||
|
|
||||||
|
/* Enable bus with Hot-Join disabled */
|
||||||
|
reg_set(HC_CONTROL, HC_CONTROL_BUS_ENABLE | HC_CONTROL_HOT_JOIN_CTRL);
|
||||||
dev_dbg(&hci->master.dev, "HC_CONTROL = %#x", reg_read(HC_CONTROL));
|
dev_dbg(&hci->master.dev, "HC_CONTROL = %#x", reg_read(HC_CONTROL));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -177,21 +181,51 @@ static int i3c_hci_bus_disable(struct i3c_hci *hci)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int i3c_hci_software_reset(struct i3c_hci *hci)
|
||||||
|
{
|
||||||
|
u32 regval;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SOFT_RST must be clear before we write to it.
|
||||||
|
* Then we must wait until it clears again.
|
||||||
|
*/
|
||||||
|
ret = readx_poll_timeout(reg_read, RESET_CONTROL, regval,
|
||||||
|
!(regval & SOFT_RST), 0, 10 * USEC_PER_MSEC);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&hci->master.dev, "%s: Software reset stuck\n", __func__);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
reg_write(RESET_CONTROL, SOFT_RST);
|
||||||
|
|
||||||
|
ret = readx_poll_timeout(reg_read, RESET_CONTROL, regval,
|
||||||
|
!(regval & SOFT_RST), 0, 10 * USEC_PER_MSEC);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&hci->master.dev, "%s: Software reset failed\n", __func__);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
void i3c_hci_sync_irq_inactive(struct i3c_hci *hci)
|
void i3c_hci_sync_irq_inactive(struct i3c_hci *hci)
|
||||||
{
|
{
|
||||||
struct platform_device *pdev = to_platform_device(hci->master.dev.parent);
|
struct platform_device *pdev = to_platform_device(hci->master.dev.parent);
|
||||||
int irq = platform_get_irq(pdev, 0);
|
int irq = platform_get_irq(pdev, 0);
|
||||||
|
|
||||||
reg_write(INTR_SIGNAL_ENABLE, 0x0);
|
reg_write(INTR_SIGNAL_ENABLE, 0x0);
|
||||||
hci->irq_inactive = true;
|
|
||||||
synchronize_irq(irq);
|
synchronize_irq(irq);
|
||||||
|
scoped_guard(spinlock_irqsave, &hci->lock)
|
||||||
|
hci->irq_inactive = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void i3c_hci_bus_cleanup(struct i3c_master_controller *m)
|
static void i3c_hci_bus_cleanup(struct i3c_master_controller *m)
|
||||||
{
|
{
|
||||||
struct i3c_hci *hci = to_i3c_hci(m);
|
struct i3c_hci *hci = to_i3c_hci(m);
|
||||||
|
|
||||||
i3c_hci_bus_disable(hci);
|
if (i3c_hci_bus_disable(hci))
|
||||||
|
i3c_hci_software_reset(hci);
|
||||||
hci->io->cleanup(hci);
|
hci->io->cleanup(hci);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -212,6 +246,36 @@ void mipi_i3c_hci_dct_index_reset(struct i3c_hci *hci)
|
||||||
reg_write(DCT_SECTION, FIELD_PREP(DCT_TABLE_INDEX, 0));
|
reg_write(DCT_SECTION, FIELD_PREP(DCT_TABLE_INDEX, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int i3c_hci_process_xfer(struct i3c_hci *hci, struct hci_xfer *xfer, int n)
|
||||||
|
{
|
||||||
|
struct completion *done = xfer[n - 1].completion;
|
||||||
|
unsigned long timeout = xfer[n - 1].timeout;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = hci->io->queue_xfer(hci, xfer, n);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (!wait_for_completion_timeout(done, timeout)) {
|
||||||
|
if (hci->io->dequeue_xfer(hci, xfer, n)) {
|
||||||
|
dev_err(&hci->master.dev, "%s: timeout error\n", __func__);
|
||||||
|
return -ETIMEDOUT;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hci->io->handle_error) {
|
||||||
|
bool error = false;
|
||||||
|
|
||||||
|
for (int i = 0; i < n && !error; i++)
|
||||||
|
error = RESP_STATUS(xfer[i].response);
|
||||||
|
if (error)
|
||||||
|
return hci->io->handle_error(hci, xfer, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int i3c_hci_send_ccc_cmd(struct i3c_master_controller *m,
|
static int i3c_hci_send_ccc_cmd(struct i3c_master_controller *m,
|
||||||
struct i3c_ccc_cmd *ccc)
|
struct i3c_ccc_cmd *ccc)
|
||||||
{
|
{
|
||||||
|
|
@ -252,18 +316,14 @@ static int i3c_hci_send_ccc_cmd(struct i3c_master_controller *m,
|
||||||
last = i - 1;
|
last = i - 1;
|
||||||
xfer[last].cmd_desc[0] |= CMD_0_TOC;
|
xfer[last].cmd_desc[0] |= CMD_0_TOC;
|
||||||
xfer[last].completion = &done;
|
xfer[last].completion = &done;
|
||||||
|
xfer[last].timeout = HZ;
|
||||||
|
|
||||||
if (prefixed)
|
if (prefixed)
|
||||||
xfer--;
|
xfer--;
|
||||||
|
|
||||||
ret = hci->io->queue_xfer(hci, xfer, nxfers);
|
ret = i3c_hci_process_xfer(hci, xfer, nxfers);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out;
|
goto out;
|
||||||
if (!wait_for_completion_timeout(&done, HZ) &&
|
|
||||||
hci->io->dequeue_xfer(hci, xfer, nxfers)) {
|
|
||||||
ret = -ETIME;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
for (i = prefixed; i < nxfers; i++) {
|
for (i = prefixed; i < nxfers; i++) {
|
||||||
if (ccc->rnw)
|
if (ccc->rnw)
|
||||||
ccc->dests[i - prefixed].payload.len =
|
ccc->dests[i - prefixed].payload.len =
|
||||||
|
|
@ -334,15 +394,11 @@ static int i3c_hci_i3c_xfers(struct i3c_dev_desc *dev,
|
||||||
last = i - 1;
|
last = i - 1;
|
||||||
xfer[last].cmd_desc[0] |= CMD_0_TOC;
|
xfer[last].cmd_desc[0] |= CMD_0_TOC;
|
||||||
xfer[last].completion = &done;
|
xfer[last].completion = &done;
|
||||||
|
xfer[last].timeout = HZ;
|
||||||
|
|
||||||
ret = hci->io->queue_xfer(hci, xfer, nxfers);
|
ret = i3c_hci_process_xfer(hci, xfer, nxfers);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out;
|
goto out;
|
||||||
if (!wait_for_completion_timeout(&done, HZ) &&
|
|
||||||
hci->io->dequeue_xfer(hci, xfer, nxfers)) {
|
|
||||||
ret = -ETIME;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
for (i = 0; i < nxfers; i++) {
|
for (i = 0; i < nxfers; i++) {
|
||||||
if (i3c_xfers[i].rnw)
|
if (i3c_xfers[i].rnw)
|
||||||
i3c_xfers[i].len = RESP_DATA_LENGTH(xfer[i].response);
|
i3c_xfers[i].len = RESP_DATA_LENGTH(xfer[i].response);
|
||||||
|
|
@ -382,15 +438,11 @@ static int i3c_hci_i2c_xfers(struct i2c_dev_desc *dev,
|
||||||
last = i - 1;
|
last = i - 1;
|
||||||
xfer[last].cmd_desc[0] |= CMD_0_TOC;
|
xfer[last].cmd_desc[0] |= CMD_0_TOC;
|
||||||
xfer[last].completion = &done;
|
xfer[last].completion = &done;
|
||||||
|
xfer[last].timeout = m->i2c.timeout;
|
||||||
|
|
||||||
ret = hci->io->queue_xfer(hci, xfer, nxfers);
|
ret = i3c_hci_process_xfer(hci, xfer, nxfers);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out;
|
goto out;
|
||||||
if (!wait_for_completion_timeout(&done, m->i2c.timeout) &&
|
|
||||||
hci->io->dequeue_xfer(hci, xfer, nxfers)) {
|
|
||||||
ret = -ETIME;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
for (i = 0; i < nxfers; i++) {
|
for (i = 0; i < nxfers; i++) {
|
||||||
if (RESP_STATUS(xfer[i].response) != RESP_SUCCESS) {
|
if (RESP_STATUS(xfer[i].response) != RESP_SUCCESS) {
|
||||||
ret = -EIO;
|
ret = -EIO;
|
||||||
|
|
@ -566,6 +618,8 @@ static irqreturn_t i3c_hci_irq_handler(int irq, void *dev_id)
|
||||||
irqreturn_t result = IRQ_NONE;
|
irqreturn_t result = IRQ_NONE;
|
||||||
u32 val;
|
u32 val;
|
||||||
|
|
||||||
|
guard(spinlock)(&hci->lock);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The IRQ can be shared, so the handler may be called when the IRQ is
|
* The IRQ can be shared, so the handler may be called when the IRQ is
|
||||||
* due to a different device. That could happen when runtime suspended,
|
* due to a different device. That could happen when runtime suspended,
|
||||||
|
|
@ -601,34 +655,6 @@ static irqreturn_t i3c_hci_irq_handler(int irq, void *dev_id)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int i3c_hci_software_reset(struct i3c_hci *hci)
|
|
||||||
{
|
|
||||||
u32 regval;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* SOFT_RST must be clear before we write to it.
|
|
||||||
* Then we must wait until it clears again.
|
|
||||||
*/
|
|
||||||
ret = readx_poll_timeout(reg_read, RESET_CONTROL, regval,
|
|
||||||
!(regval & SOFT_RST), 0, 10 * USEC_PER_MSEC);
|
|
||||||
if (ret) {
|
|
||||||
dev_err(&hci->master.dev, "%s: Software reset stuck\n", __func__);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
reg_write(RESET_CONTROL, SOFT_RST);
|
|
||||||
|
|
||||||
ret = readx_poll_timeout(reg_read, RESET_CONTROL, regval,
|
|
||||||
!(regval & SOFT_RST), 0, 10 * USEC_PER_MSEC);
|
|
||||||
if (ret) {
|
|
||||||
dev_err(&hci->master.dev, "%s: Software reset failed\n", __func__);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool is_version_1_1_or_newer(struct i3c_hci *hci)
|
static inline bool is_version_1_1_or_newer(struct i3c_hci *hci)
|
||||||
{
|
{
|
||||||
return hci->version_major > 1 || (hci->version_major == 1 && hci->version_minor > 0);
|
return hci->version_major > 1 || (hci->version_major == 1 && hci->version_minor > 0);
|
||||||
|
|
@ -739,8 +765,12 @@ static int i3c_hci_runtime_suspend(struct device *dev)
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = i3c_hci_bus_disable(hci);
|
ret = i3c_hci_bus_disable(hci);
|
||||||
if (ret)
|
if (ret) {
|
||||||
|
/* Fall back to software reset to disable the bus */
|
||||||
|
ret = i3c_hci_software_reset(hci);
|
||||||
|
i3c_hci_sync_irq_inactive(hci);
|
||||||
return ret;
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
hci->io->suspend(hci);
|
hci->io->suspend(hci);
|
||||||
|
|
||||||
|
|
@ -760,11 +790,13 @@ static int i3c_hci_runtime_resume(struct device *dev)
|
||||||
|
|
||||||
mipi_i3c_hci_dat_v1.restore(hci);
|
mipi_i3c_hci_dat_v1.restore(hci);
|
||||||
|
|
||||||
hci->irq_inactive = false;
|
|
||||||
|
|
||||||
hci->io->resume(hci);
|
hci->io->resume(hci);
|
||||||
|
|
||||||
reg_set(HC_CONTROL, HC_CONTROL_BUS_ENABLE);
|
scoped_guard(spinlock_irqsave, &hci->lock)
|
||||||
|
hci->irq_inactive = false;
|
||||||
|
|
||||||
|
/* Enable bus with Hot-Join disabled */
|
||||||
|
reg_set(HC_CONTROL, HC_CONTROL_BUS_ENABLE | HC_CONTROL_HOT_JOIN_CTRL);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -924,6 +956,9 @@ static int i3c_hci_probe(struct platform_device *pdev)
|
||||||
if (!hci)
|
if (!hci)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
spin_lock_init(&hci->lock);
|
||||||
|
mutex_init(&hci->control_mutex);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Multi-bus instances share the same MMIO address range, but not
|
* Multi-bus instances share the same MMIO address range, but not
|
||||||
* necessarily in separate contiguous sub-ranges. To avoid overlapping
|
* necessarily in separate contiguous sub-ranges. To avoid overlapping
|
||||||
|
|
@ -950,6 +985,8 @@ static int i3c_hci_probe(struct platform_device *pdev)
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
hci->irq_inactive = true;
|
||||||
|
|
||||||
irq = platform_get_irq(pdev, 0);
|
irq = platform_get_irq(pdev, 0);
|
||||||
ret = devm_request_irq(&pdev->dev, irq, i3c_hci_irq_handler,
|
ret = devm_request_irq(&pdev->dev, irq, i3c_hci_irq_handler,
|
||||||
IRQF_SHARED, NULL, hci);
|
IRQF_SHARED, NULL, hci);
|
||||||
|
|
|
||||||
|
|
@ -129,9 +129,8 @@ struct hci_rh_data {
|
||||||
dma_addr_t xfer_dma, resp_dma, ibi_status_dma, ibi_data_dma;
|
dma_addr_t xfer_dma, resp_dma, ibi_status_dma, ibi_data_dma;
|
||||||
unsigned int xfer_entries, ibi_status_entries, ibi_chunks_total;
|
unsigned int xfer_entries, ibi_status_entries, ibi_chunks_total;
|
||||||
unsigned int xfer_struct_sz, resp_struct_sz, ibi_status_sz, ibi_chunk_sz;
|
unsigned int xfer_struct_sz, resp_struct_sz, ibi_status_sz, ibi_chunk_sz;
|
||||||
unsigned int done_ptr, ibi_chunk_ptr;
|
unsigned int done_ptr, ibi_chunk_ptr, xfer_space;
|
||||||
struct hci_xfer **src_xfers;
|
struct hci_xfer **src_xfers;
|
||||||
spinlock_t lock;
|
|
||||||
struct completion op_done;
|
struct completion op_done;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -261,6 +260,7 @@ ring_ready:
|
||||||
|
|
||||||
rh->done_ptr = 0;
|
rh->done_ptr = 0;
|
||||||
rh->ibi_chunk_ptr = 0;
|
rh->ibi_chunk_ptr = 0;
|
||||||
|
rh->xfer_space = rh->xfer_entries;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hci_dma_init_rings(struct i3c_hci *hci)
|
static void hci_dma_init_rings(struct i3c_hci *hci)
|
||||||
|
|
@ -344,7 +344,6 @@ static int hci_dma_init(struct i3c_hci *hci)
|
||||||
goto err_out;
|
goto err_out;
|
||||||
rh = &rings->headers[i];
|
rh = &rings->headers[i];
|
||||||
rh->regs = hci->base_regs + offset;
|
rh->regs = hci->base_regs + offset;
|
||||||
spin_lock_init(&rh->lock);
|
|
||||||
init_completion(&rh->op_done);
|
init_completion(&rh->op_done);
|
||||||
|
|
||||||
rh->xfer_entries = XFER_RING_ENTRIES;
|
rh->xfer_entries = XFER_RING_ENTRIES;
|
||||||
|
|
@ -439,26 +438,63 @@ static void hci_dma_unmap_xfer(struct i3c_hci *hci,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct i3c_dma *hci_dma_map_xfer(struct device *dev, struct hci_xfer *xfer)
|
||||||
|
{
|
||||||
|
enum dma_data_direction dir = xfer->rnw ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
|
||||||
|
bool need_bounce = device_iommu_mapped(dev) && xfer->rnw && (xfer->data_len & 3);
|
||||||
|
|
||||||
|
return i3c_master_dma_map_single(dev, xfer->data, xfer->data_len, need_bounce, dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hci_dma_map_xfer_list(struct i3c_hci *hci, struct device *dev,
|
||||||
|
struct hci_xfer *xfer_list, int n)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
struct hci_xfer *xfer = xfer_list + i;
|
||||||
|
|
||||||
|
if (!xfer->data)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
xfer->dma = hci_dma_map_xfer(dev, xfer);
|
||||||
|
if (!xfer->dma) {
|
||||||
|
hci_dma_unmap_xfer(hci, xfer_list, i);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int hci_dma_queue_xfer(struct i3c_hci *hci,
|
static int hci_dma_queue_xfer(struct i3c_hci *hci,
|
||||||
struct hci_xfer *xfer_list, int n)
|
struct hci_xfer *xfer_list, int n)
|
||||||
{
|
{
|
||||||
struct hci_rings_data *rings = hci->io_data;
|
struct hci_rings_data *rings = hci->io_data;
|
||||||
struct hci_rh_data *rh;
|
struct hci_rh_data *rh;
|
||||||
unsigned int i, ring, enqueue_ptr;
|
unsigned int i, ring, enqueue_ptr;
|
||||||
u32 op1_val, op2_val;
|
u32 op1_val;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = hci_dma_map_xfer_list(hci, rings->sysdev, xfer_list, n);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
/* For now we only use ring 0 */
|
/* For now we only use ring 0 */
|
||||||
ring = 0;
|
ring = 0;
|
||||||
rh = &rings->headers[ring];
|
rh = &rings->headers[ring];
|
||||||
|
|
||||||
|
spin_lock_irq(&hci->lock);
|
||||||
|
|
||||||
|
if (n > rh->xfer_space) {
|
||||||
|
spin_unlock_irq(&hci->lock);
|
||||||
|
hci_dma_unmap_xfer(hci, xfer_list, n);
|
||||||
|
return -EBUSY;
|
||||||
|
}
|
||||||
|
|
||||||
op1_val = rh_reg_read(RING_OPERATION1);
|
op1_val = rh_reg_read(RING_OPERATION1);
|
||||||
enqueue_ptr = FIELD_GET(RING_OP1_CR_ENQ_PTR, op1_val);
|
enqueue_ptr = FIELD_GET(RING_OP1_CR_ENQ_PTR, op1_val);
|
||||||
for (i = 0; i < n; i++) {
|
for (i = 0; i < n; i++) {
|
||||||
struct hci_xfer *xfer = xfer_list + i;
|
struct hci_xfer *xfer = xfer_list + i;
|
||||||
u32 *ring_data = rh->xfer + rh->xfer_struct_sz * enqueue_ptr;
|
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 */
|
/* store cmd descriptor */
|
||||||
*ring_data++ = xfer->cmd_desc[0];
|
*ring_data++ = xfer->cmd_desc[0];
|
||||||
|
|
@ -477,18 +513,6 @@ static int hci_dma_queue_xfer(struct i3c_hci *hci,
|
||||||
|
|
||||||
/* 2nd and 3rd words of Data Buffer Descriptor Structure */
|
/* 2nd and 3rd words of Data Buffer Descriptor Structure */
|
||||||
if (xfer->data) {
|
if (xfer->data) {
|
||||||
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->dma->addr);
|
*ring_data++ = lower_32_bits(xfer->dma->addr);
|
||||||
*ring_data++ = upper_32_bits(xfer->dma->addr);
|
*ring_data++ = upper_32_bits(xfer->dma->addr);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -503,26 +527,14 @@ static int hci_dma_queue_xfer(struct i3c_hci *hci,
|
||||||
xfer->ring_entry = enqueue_ptr;
|
xfer->ring_entry = enqueue_ptr;
|
||||||
|
|
||||||
enqueue_ptr = (enqueue_ptr + 1) % rh->xfer_entries;
|
enqueue_ptr = (enqueue_ptr + 1) % rh->xfer_entries;
|
||||||
|
|
||||||
/*
|
|
||||||
* We may update the hardware view of the enqueue pointer
|
|
||||||
* only if we didn't reach its dequeue pointer.
|
|
||||||
*/
|
|
||||||
op2_val = rh_reg_read(RING_OPERATION2);
|
|
||||||
if (enqueue_ptr == FIELD_GET(RING_OP2_CR_DEQ_PTR, op2_val)) {
|
|
||||||
/* the ring is full */
|
|
||||||
hci_dma_unmap_xfer(hci, xfer_list, i + 1);
|
|
||||||
return -EBUSY;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* take care to update the hardware enqueue pointer atomically */
|
rh->xfer_space -= n;
|
||||||
spin_lock_irq(&rh->lock);
|
|
||||||
op1_val = rh_reg_read(RING_OPERATION1);
|
|
||||||
op1_val &= ~RING_OP1_CR_ENQ_PTR;
|
op1_val &= ~RING_OP1_CR_ENQ_PTR;
|
||||||
op1_val |= FIELD_PREP(RING_OP1_CR_ENQ_PTR, enqueue_ptr);
|
op1_val |= FIELD_PREP(RING_OP1_CR_ENQ_PTR, enqueue_ptr);
|
||||||
rh_reg_write(RING_OPERATION1, op1_val);
|
rh_reg_write(RING_OPERATION1, op1_val);
|
||||||
spin_unlock_irq(&rh->lock);
|
spin_unlock_irq(&hci->lock);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -534,18 +546,29 @@ static bool hci_dma_dequeue_xfer(struct i3c_hci *hci,
|
||||||
struct hci_rh_data *rh = &rings->headers[xfer_list[0].ring_number];
|
struct hci_rh_data *rh = &rings->headers[xfer_list[0].ring_number];
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
bool did_unqueue = false;
|
bool did_unqueue = false;
|
||||||
|
u32 ring_status;
|
||||||
|
|
||||||
/* stop the ring */
|
guard(mutex)(&hci->control_mutex);
|
||||||
rh_reg_write(RING_CONTROL, RING_CTRL_ABORT);
|
|
||||||
if (wait_for_completion_timeout(&rh->op_done, HZ) == 0) {
|
ring_status = rh_reg_read(RING_STATUS);
|
||||||
/*
|
if (ring_status & RING_STATUS_RUNNING) {
|
||||||
* We're deep in it if ever this condition is ever met.
|
/* stop the ring */
|
||||||
* Hardware might still be writing to memory, etc.
|
reinit_completion(&rh->op_done);
|
||||||
*/
|
rh_reg_write(RING_CONTROL, RING_CTRL_ENABLE | RING_CTRL_ABORT);
|
||||||
dev_crit(&hci->master.dev, "unable to abort the ring\n");
|
wait_for_completion_timeout(&rh->op_done, HZ);
|
||||||
WARN_ON(1);
|
ring_status = rh_reg_read(RING_STATUS);
|
||||||
|
if (ring_status & RING_STATUS_RUNNING) {
|
||||||
|
/*
|
||||||
|
* We're deep in it if ever this condition is ever met.
|
||||||
|
* Hardware might still be writing to memory, etc.
|
||||||
|
*/
|
||||||
|
dev_crit(&hci->master.dev, "unable to abort the ring\n");
|
||||||
|
WARN_ON(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spin_lock_irq(&hci->lock);
|
||||||
|
|
||||||
for (i = 0; i < n; i++) {
|
for (i = 0; i < n; i++) {
|
||||||
struct hci_xfer *xfer = xfer_list + i;
|
struct hci_xfer *xfer = xfer_list + i;
|
||||||
int idx = xfer->ring_entry;
|
int idx = xfer->ring_entry;
|
||||||
|
|
@ -559,7 +582,7 @@ static bool hci_dma_dequeue_xfer(struct i3c_hci *hci,
|
||||||
u32 *ring_data = rh->xfer + rh->xfer_struct_sz * idx;
|
u32 *ring_data = rh->xfer + rh->xfer_struct_sz * idx;
|
||||||
|
|
||||||
/* store no-op cmd descriptor */
|
/* store no-op cmd descriptor */
|
||||||
*ring_data++ = FIELD_PREP(CMD_0_ATTR, 0x7);
|
*ring_data++ = FIELD_PREP(CMD_0_ATTR, 0x7) | FIELD_PREP(CMD_0_TID, xfer->cmd_tid);
|
||||||
*ring_data++ = 0;
|
*ring_data++ = 0;
|
||||||
if (hci->cmd == &mipi_i3c_hci_cmd_v2) {
|
if (hci->cmd == &mipi_i3c_hci_cmd_v2) {
|
||||||
*ring_data++ = 0;
|
*ring_data++ = 0;
|
||||||
|
|
@ -577,15 +600,25 @@ static bool hci_dma_dequeue_xfer(struct i3c_hci *hci,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* restart the ring */
|
/* restart the ring */
|
||||||
|
mipi_i3c_hci_resume(hci);
|
||||||
rh_reg_write(RING_CONTROL, RING_CTRL_ENABLE);
|
rh_reg_write(RING_CONTROL, RING_CTRL_ENABLE);
|
||||||
|
rh_reg_write(RING_CONTROL, RING_CTRL_ENABLE | RING_CTRL_RUN_STOP);
|
||||||
|
|
||||||
|
spin_unlock_irq(&hci->lock);
|
||||||
|
|
||||||
return did_unqueue;
|
return did_unqueue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int hci_dma_handle_error(struct i3c_hci *hci, struct hci_xfer *xfer_list, int n)
|
||||||
|
{
|
||||||
|
return hci_dma_dequeue_xfer(hci, xfer_list, n) ? -EIO : 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void hci_dma_xfer_done(struct i3c_hci *hci, struct hci_rh_data *rh)
|
static void hci_dma_xfer_done(struct i3c_hci *hci, struct hci_rh_data *rh)
|
||||||
{
|
{
|
||||||
u32 op1_val, op2_val, resp, *ring_resp;
|
u32 op1_val, op2_val, resp, *ring_resp;
|
||||||
unsigned int tid, done_ptr = rh->done_ptr;
|
unsigned int tid, done_ptr = rh->done_ptr;
|
||||||
|
unsigned int done_cnt = 0;
|
||||||
struct hci_xfer *xfer;
|
struct hci_xfer *xfer;
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
|
@ -603,6 +636,7 @@ static void hci_dma_xfer_done(struct i3c_hci *hci, struct hci_rh_data *rh)
|
||||||
dev_dbg(&hci->master.dev, "orphaned ring entry");
|
dev_dbg(&hci->master.dev, "orphaned ring entry");
|
||||||
} else {
|
} else {
|
||||||
hci_dma_unmap_xfer(hci, xfer, 1);
|
hci_dma_unmap_xfer(hci, xfer, 1);
|
||||||
|
rh->src_xfers[done_ptr] = NULL;
|
||||||
xfer->ring_entry = -1;
|
xfer->ring_entry = -1;
|
||||||
xfer->response = resp;
|
xfer->response = resp;
|
||||||
if (tid != xfer->cmd_tid) {
|
if (tid != xfer->cmd_tid) {
|
||||||
|
|
@ -617,15 +651,14 @@ static void hci_dma_xfer_done(struct i3c_hci *hci, struct hci_rh_data *rh)
|
||||||
|
|
||||||
done_ptr = (done_ptr + 1) % rh->xfer_entries;
|
done_ptr = (done_ptr + 1) % rh->xfer_entries;
|
||||||
rh->done_ptr = done_ptr;
|
rh->done_ptr = done_ptr;
|
||||||
|
done_cnt += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* take care to update the software dequeue pointer atomically */
|
rh->xfer_space += done_cnt;
|
||||||
spin_lock(&rh->lock);
|
|
||||||
op1_val = rh_reg_read(RING_OPERATION1);
|
op1_val = rh_reg_read(RING_OPERATION1);
|
||||||
op1_val &= ~RING_OP1_CR_SW_DEQ_PTR;
|
op1_val &= ~RING_OP1_CR_SW_DEQ_PTR;
|
||||||
op1_val |= FIELD_PREP(RING_OP1_CR_SW_DEQ_PTR, done_ptr);
|
op1_val |= FIELD_PREP(RING_OP1_CR_SW_DEQ_PTR, done_ptr);
|
||||||
rh_reg_write(RING_OPERATION1, op1_val);
|
rh_reg_write(RING_OPERATION1, op1_val);
|
||||||
spin_unlock(&rh->lock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int hci_dma_request_ibi(struct i3c_hci *hci, struct i3c_dev_desc *dev,
|
static int hci_dma_request_ibi(struct i3c_hci *hci, struct i3c_dev_desc *dev,
|
||||||
|
|
@ -805,13 +838,10 @@ static void hci_dma_process_ibi(struct i3c_hci *hci, struct hci_rh_data *rh)
|
||||||
i3c_master_queue_ibi(dev, slot);
|
i3c_master_queue_ibi(dev, slot);
|
||||||
|
|
||||||
done:
|
done:
|
||||||
/* take care to update the ibi dequeue pointer atomically */
|
|
||||||
spin_lock(&rh->lock);
|
|
||||||
op1_val = rh_reg_read(RING_OPERATION1);
|
op1_val = rh_reg_read(RING_OPERATION1);
|
||||||
op1_val &= ~RING_OP1_IBI_DEQ_PTR;
|
op1_val &= ~RING_OP1_IBI_DEQ_PTR;
|
||||||
op1_val |= FIELD_PREP(RING_OP1_IBI_DEQ_PTR, deq_ptr);
|
op1_val |= FIELD_PREP(RING_OP1_IBI_DEQ_PTR, deq_ptr);
|
||||||
rh_reg_write(RING_OPERATION1, op1_val);
|
rh_reg_write(RING_OPERATION1, op1_val);
|
||||||
spin_unlock(&rh->lock);
|
|
||||||
|
|
||||||
/* update the chunk pointer */
|
/* update the chunk pointer */
|
||||||
rh->ibi_chunk_ptr += ibi_chunks;
|
rh->ibi_chunk_ptr += ibi_chunks;
|
||||||
|
|
@ -845,29 +875,8 @@ static bool hci_dma_irq_handler(struct i3c_hci *hci)
|
||||||
hci_dma_xfer_done(hci, rh);
|
hci_dma_xfer_done(hci, rh);
|
||||||
if (status & INTR_RING_OP)
|
if (status & INTR_RING_OP)
|
||||||
complete(&rh->op_done);
|
complete(&rh->op_done);
|
||||||
|
if (status & INTR_TRANSFER_ABORT)
|
||||||
if (status & INTR_TRANSFER_ABORT) {
|
dev_dbg(&hci->master.dev, "Ring %d: Transfer Aborted\n", i);
|
||||||
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_IBI_RING_FULL)
|
if (status & INTR_IBI_RING_FULL)
|
||||||
dev_err_ratelimited(&hci->master.dev,
|
dev_err_ratelimited(&hci->master.dev,
|
||||||
"Ring %d: IBI Ring Full Condition\n", i);
|
"Ring %d: IBI Ring Full Condition\n", i);
|
||||||
|
|
@ -883,6 +892,7 @@ const struct hci_io_ops mipi_i3c_hci_dma = {
|
||||||
.cleanup = hci_dma_cleanup,
|
.cleanup = hci_dma_cleanup,
|
||||||
.queue_xfer = hci_dma_queue_xfer,
|
.queue_xfer = hci_dma_queue_xfer,
|
||||||
.dequeue_xfer = hci_dma_dequeue_xfer,
|
.dequeue_xfer = hci_dma_dequeue_xfer,
|
||||||
|
.handle_error = hci_dma_handle_error,
|
||||||
.irq_handler = hci_dma_irq_handler,
|
.irq_handler = hci_dma_irq_handler,
|
||||||
.request_ibi = hci_dma_request_ibi,
|
.request_ibi = hci_dma_request_ibi,
|
||||||
.free_ibi = hci_dma_free_ibi,
|
.free_ibi = hci_dma_free_ibi,
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,8 @@ struct i3c_hci {
|
||||||
const struct hci_io_ops *io;
|
const struct hci_io_ops *io;
|
||||||
void *io_data;
|
void *io_data;
|
||||||
const struct hci_cmd_ops *cmd;
|
const struct hci_cmd_ops *cmd;
|
||||||
|
spinlock_t lock;
|
||||||
|
struct mutex control_mutex;
|
||||||
atomic_t next_cmd_tid;
|
atomic_t next_cmd_tid;
|
||||||
bool irq_inactive;
|
bool irq_inactive;
|
||||||
u32 caps;
|
u32 caps;
|
||||||
|
|
@ -87,6 +89,7 @@ struct hci_xfer {
|
||||||
unsigned int data_len;
|
unsigned int data_len;
|
||||||
unsigned int cmd_tid;
|
unsigned int cmd_tid;
|
||||||
struct completion *completion;
|
struct completion *completion;
|
||||||
|
unsigned long timeout;
|
||||||
union {
|
union {
|
||||||
struct {
|
struct {
|
||||||
/* PIO specific */
|
/* PIO specific */
|
||||||
|
|
@ -120,6 +123,7 @@ struct hci_io_ops {
|
||||||
bool (*irq_handler)(struct i3c_hci *hci);
|
bool (*irq_handler)(struct i3c_hci *hci);
|
||||||
int (*queue_xfer)(struct i3c_hci *hci, struct hci_xfer *xfer, int n);
|
int (*queue_xfer)(struct i3c_hci *hci, struct hci_xfer *xfer, int n);
|
||||||
bool (*dequeue_xfer)(struct i3c_hci *hci, struct hci_xfer *xfer, int n);
|
bool (*dequeue_xfer)(struct i3c_hci *hci, struct hci_xfer *xfer, int n);
|
||||||
|
int (*handle_error)(struct i3c_hci *hci, struct hci_xfer *xfer, int n);
|
||||||
int (*request_ibi)(struct i3c_hci *hci, struct i3c_dev_desc *dev,
|
int (*request_ibi)(struct i3c_hci *hci, struct i3c_dev_desc *dev,
|
||||||
const struct i3c_ibi_setup *req);
|
const struct i3c_ibi_setup *req);
|
||||||
void (*free_ibi)(struct i3c_hci *hci, struct i3c_dev_desc *dev);
|
void (*free_ibi)(struct i3c_hci *hci, struct i3c_dev_desc *dev);
|
||||||
|
|
@ -154,5 +158,6 @@ void mipi_i3c_hci_dct_index_reset(struct i3c_hci *hci);
|
||||||
void amd_set_od_pp_timing(struct i3c_hci *hci);
|
void amd_set_od_pp_timing(struct i3c_hci *hci);
|
||||||
void amd_set_resp_buf_thld(struct i3c_hci *hci);
|
void amd_set_resp_buf_thld(struct i3c_hci *hci);
|
||||||
void i3c_hci_sync_irq_inactive(struct i3c_hci *hci);
|
void i3c_hci_sync_irq_inactive(struct i3c_hci *hci);
|
||||||
|
int i3c_hci_process_xfer(struct i3c_hci *hci, struct hci_xfer *xfer, int n);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -123,7 +123,6 @@ struct hci_pio_ibi_data {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct hci_pio_data {
|
struct hci_pio_data {
|
||||||
spinlock_t lock;
|
|
||||||
struct hci_xfer *curr_xfer, *xfer_queue;
|
struct hci_xfer *curr_xfer, *xfer_queue;
|
||||||
struct hci_xfer *curr_rx, *rx_queue;
|
struct hci_xfer *curr_rx, *rx_queue;
|
||||||
struct hci_xfer *curr_tx, *tx_queue;
|
struct hci_xfer *curr_tx, *tx_queue;
|
||||||
|
|
@ -212,7 +211,6 @@ static int hci_pio_init(struct i3c_hci *hci)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
hci->io_data = pio;
|
hci->io_data = pio;
|
||||||
spin_lock_init(&pio->lock);
|
|
||||||
|
|
||||||
__hci_pio_init(hci, &size_val);
|
__hci_pio_init(hci, &size_val);
|
||||||
|
|
||||||
|
|
@ -631,7 +629,7 @@ static int hci_pio_queue_xfer(struct i3c_hci *hci, struct hci_xfer *xfer, int n)
|
||||||
xfer[i].data_left = xfer[i].data_len;
|
xfer[i].data_left = xfer[i].data_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
spin_lock_irq(&pio->lock);
|
spin_lock_irq(&hci->lock);
|
||||||
prev_queue_tail = pio->xfer_queue;
|
prev_queue_tail = pio->xfer_queue;
|
||||||
pio->xfer_queue = &xfer[n - 1];
|
pio->xfer_queue = &xfer[n - 1];
|
||||||
if (pio->curr_xfer) {
|
if (pio->curr_xfer) {
|
||||||
|
|
@ -645,7 +643,7 @@ static int hci_pio_queue_xfer(struct i3c_hci *hci, struct hci_xfer *xfer, int n)
|
||||||
pio_reg_read(INTR_STATUS),
|
pio_reg_read(INTR_STATUS),
|
||||||
pio_reg_read(INTR_SIGNAL_ENABLE));
|
pio_reg_read(INTR_SIGNAL_ENABLE));
|
||||||
}
|
}
|
||||||
spin_unlock_irq(&pio->lock);
|
spin_unlock_irq(&hci->lock);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -716,14 +714,14 @@ static bool hci_pio_dequeue_xfer(struct i3c_hci *hci, struct hci_xfer *xfer, int
|
||||||
struct hci_pio_data *pio = hci->io_data;
|
struct hci_pio_data *pio = hci->io_data;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
spin_lock_irq(&pio->lock);
|
spin_lock_irq(&hci->lock);
|
||||||
dev_dbg(&hci->master.dev, "n=%d status=%#x/%#x", n,
|
dev_dbg(&hci->master.dev, "n=%d status=%#x/%#x", n,
|
||||||
pio_reg_read(INTR_STATUS), pio_reg_read(INTR_SIGNAL_ENABLE));
|
pio_reg_read(INTR_STATUS), pio_reg_read(INTR_SIGNAL_ENABLE));
|
||||||
dev_dbg(&hci->master.dev, "main_status = %#x/%#x",
|
dev_dbg(&hci->master.dev, "main_status = %#x/%#x",
|
||||||
readl(hci->base_regs + 0x20), readl(hci->base_regs + 0x28));
|
readl(hci->base_regs + 0x20), readl(hci->base_regs + 0x28));
|
||||||
|
|
||||||
ret = hci_pio_dequeue_xfer_common(hci, pio, xfer, n);
|
ret = hci_pio_dequeue_xfer_common(hci, pio, xfer, n);
|
||||||
spin_unlock_irq(&pio->lock);
|
spin_unlock_irq(&hci->lock);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1016,15 +1014,12 @@ static bool hci_pio_irq_handler(struct i3c_hci *hci)
|
||||||
struct hci_pio_data *pio = hci->io_data;
|
struct hci_pio_data *pio = hci->io_data;
|
||||||
u32 status;
|
u32 status;
|
||||||
|
|
||||||
spin_lock(&pio->lock);
|
|
||||||
status = pio_reg_read(INTR_STATUS);
|
status = pio_reg_read(INTR_STATUS);
|
||||||
dev_dbg(&hci->master.dev, "PIO_INTR_STATUS %#x/%#x",
|
dev_dbg(&hci->master.dev, "PIO_INTR_STATUS %#x/%#x",
|
||||||
status, pio->enabled_irqs);
|
status, pio->enabled_irqs);
|
||||||
status &= pio->enabled_irqs | STAT_LATENCY_WARNINGS;
|
status &= pio->enabled_irqs | STAT_LATENCY_WARNINGS;
|
||||||
if (!status) {
|
if (!status)
|
||||||
spin_unlock(&pio->lock);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
if (status & STAT_IBI_STATUS_THLD)
|
if (status & STAT_IBI_STATUS_THLD)
|
||||||
hci_pio_process_ibi(hci, pio);
|
hci_pio_process_ibi(hci, pio);
|
||||||
|
|
@ -1058,7 +1053,6 @@ static bool hci_pio_irq_handler(struct i3c_hci *hci)
|
||||||
pio_reg_write(INTR_SIGNAL_ENABLE, pio->enabled_irqs);
|
pio_reg_write(INTR_SIGNAL_ENABLE, pio->enabled_irqs);
|
||||||
dev_dbg(&hci->master.dev, "PIO_INTR_STATUS %#x/%#x",
|
dev_dbg(&hci->master.dev, "PIO_INTR_STATUS %#x/%#x",
|
||||||
pio_reg_read(INTR_STATUS), pio_reg_read(INTR_SIGNAL_ENABLE));
|
pio_reg_read(INTR_STATUS), pio_reg_read(INTR_SIGNAL_ENABLE));
|
||||||
spin_unlock(&pio->lock);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -143,8 +143,7 @@ config MMC5633
|
||||||
tristate "MEMSIC MMC5633 3-axis magnetic sensor"
|
tristate "MEMSIC MMC5633 3-axis magnetic sensor"
|
||||||
select REGMAP_I2C
|
select REGMAP_I2C
|
||||||
select REGMAP_I3C if I3C
|
select REGMAP_I3C if I3C
|
||||||
depends on I2C
|
depends on I3C_OR_I2C
|
||||||
depends on I3C || !I3C
|
|
||||||
help
|
help
|
||||||
Say yes here to build support for the MEMSIC MMC5633 3-axis
|
Say yes here to build support for the MEMSIC MMC5633 3-axis
|
||||||
magnetic sensor.
|
magnetic sensor.
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
# SPDX-License-Identifier: GPL-2.0-only
|
# SPDX-License-Identifier: GPL-2.0-only
|
||||||
config AMD_SBRMI_I2C
|
config AMD_SBRMI_I2C
|
||||||
tristate "AMD side band RMI support"
|
tristate "AMD side band RMI support"
|
||||||
depends on I2C
|
depends on I3C_OR_I2C
|
||||||
depends on ARM || ARM64 || COMPILE_TEST
|
depends on ARM || ARM64 || COMPILE_TEST
|
||||||
select REGMAP_I2C
|
select REGMAP_I2C
|
||||||
depends on I3C || !I3C
|
|
||||||
select REGMAP_I3C if I3C
|
select REGMAP_I3C if I3C
|
||||||
help
|
help
|
||||||
Side band RMI over I2C/I3C support for AMD out of band management.
|
Side band RMI over I2C/I3C support for AMD out of band management.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue