I3C for 6.7
Core: - handle IBI in the proper order Drivers: - cdns: fix status register access - mipi-i3c-hci: many fixes now that the driver has been actually tested - svc: many IBI fixes, correct compatible string, fix hot join corner cases -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEBqsFVZXh8s/0O5JiY6TcMGxwOjIFAmVGzKMACgkQY6TcMGxw OjKcIxAAiPLfjxU6c9tbGfOOvV/rM9gDKZvGmFeawJMIP6LAEGkdp6AvU2bhoy7j zKcEYCYPVvdY7Rh9HiQrcoSXjgN5XPlc5hoEyW4E+OoWyXC2FA+8DItmSpRaF+Sn KqsPlLjbNKhUTd1Y+tzVGqHOCLuD+U4EQOZNqaAFNiU6wSXaVRo7Idvwznv9B6u9 RhJIcjPfh8M7Rr5DtKFr+zw/8TQB5maj4cWMZC0iphxSXCs6GlkVlxwI2S3PuPUI AbZJg/aGmczkce164dIzo5cBYL4Rq9JIqOUUpEhVBlKblSzKqIu0WM28KF7eASg5 Y/4YOh8xKO0NZtveBwxTkaO2LaJ1phqEVSfkxtXLH+fnlqQTyHWyyOFuYKm2UTKR vdASQeHo/rhYK/7qeGPjoYi8QRXNwI94oYgCuTNFr0l3SFocIHYJA81dpZtZs83l hIpUyfC1v9edXaCe9gdZb/pUw9akOoqYi6wm33YvRWKYC2W1qeLoH0hB1XSF39mP eXwNoF8Odp+e1BCgoVGWAVDDEUAT1b270ZmBLM+2qjP1UoIgARevDFEFnelvcyCQ dte0GRSOHtfbgcjL75+P/RcyGguY2TgpvFgTSv7tcc5lh5lYsEk775/pONahESmV 3Jjf+8q0R31gWPQNQEDxsp1tpPXvM5YaQzy9u45/CWYoEfH/Z4s= =rFcf -----END PGP SIGNATURE----- Merge tag 'i3c/for-6.7' of git://git.kernel.org/pub/scm/linux/kernel/git/i3c/linux Pull i3c updates from Alexandre Belloni: "There are now more fixes because as stated in my previous pull request, people now have access to actual hardware. Core: - handle IBI in the proper order Drivers: - cdns: fix status register access - mipi-i3c-hci: many fixes now that the driver has been actually tested - svc: many IBI fixes, correct compatible string, fix hot join corner cases" * tag 'i3c/for-6.7' of git://git.kernel.org/pub/scm/linux/kernel/git/i3c/linux: (29 commits) i3c: master: handle IBIs in order they came i3c: master: mipi-i3c-hci: Fix a kernel panic for accessing DAT_data. i3c: master: svc: fix compatibility string mismatch with binding doc i3c: master: svc: fix random hot join failure since timeout error i3c: master: svc: fix SDA keep low when polling IBIWON timeout happen i3c: master: svc: fix check wrong status register in irq handler i3c: master: svc: fix ibi may not return mandatory data byte i3c: master: svc: fix wrong data return when IBI happen during start frame i3c: master: svc: fix race condition in ibi work thread i3c: Fix typo "Provisional ID" to "Provisioned ID" i3c: Fix potential refcount leak in i3c_master_register_new_i3c_devs i3c: mipi-i3c-hci: Resume controller after aborted transfer i3c: mipi-i3c-hci: Resume controller explicitly i3c: mipi-i3c-hci: Fix missing xfer->completion in hci_cmd_v1_daa() i3c: mipi-i3c-hci: Do not unmap region not mapped for transfer i3c: mipi-i3c-hci: Set number of SW enabled Ring Bundles earlier i3c: mipi-i3c-hci: Fix race between bus cleanup and interrupt i3c: mipi-i3c-hci: Set ring start request together with enable i3c: mipi-i3c-hci: Remove BUG() when Ring Abort request times out i3c: mipi-i3c-hci: Fix out of bounds access in hci_dma_irq_handler ...
This commit is contained in:
commit
1c41041124
@ -67,7 +67,7 @@ What: /sys/bus/i3c/devices/i3c-<bus-id>/pid
|
||||
KernelVersion: 5.0
|
||||
Contact: linux-i3c@vger.kernel.org
|
||||
Description:
|
||||
PID stands for Provisional ID and is used to uniquely identify
|
||||
PID stands for Provisioned ID and is used to uniquely identify
|
||||
a device on a bus. This PID contains information about the
|
||||
vendor, the part and an instance ID so that several devices of
|
||||
the same type can be connected on the same bus.
|
||||
@ -123,7 +123,7 @@ What: /sys/bus/i3c/devices/i3c-<bus-id>/<bus-id>-<device-pid>/pid
|
||||
KernelVersion: 5.0
|
||||
Contact: linux-i3c@vger.kernel.org
|
||||
Description:
|
||||
PID stands for Provisional ID and is used to uniquely identify
|
||||
PID stands for Provisioned ID and is used to uniquely identify
|
||||
a device on a bus. This PID contains information about the
|
||||
vendor, the part and an instance ID so that several devices of
|
||||
the same type can be connected on the same bus.
|
||||
|
@ -125,12 +125,12 @@ patternProperties:
|
||||
minimum: 0
|
||||
maximum: 0x7f
|
||||
- description: |
|
||||
First half of the Provisional ID (following the PID
|
||||
First half of the Provisioned ID (following the PID
|
||||
definition provided by the I3C specification).
|
||||
|
||||
Contains the manufacturer ID left-shifted by 1.
|
||||
- description: |
|
||||
Second half of the Provisional ID (following the PID
|
||||
Second half of the Provisioned ID (following the PID
|
||||
definition provided by the I3C specification).
|
||||
|
||||
Contains the ORing of the part ID left-shifted by 16,
|
||||
|
@ -71,8 +71,8 @@ During DAA, each I3C device reports 3 important things:
|
||||
related capabilities
|
||||
* DCR: Device Characteristic Register. This 8-bit register describes the
|
||||
functionalities provided by the device
|
||||
* Provisional ID: A 48-bit unique identifier. On a given bus there should be no
|
||||
Provisional ID collision, otherwise the discovery mechanism may fail.
|
||||
* Provisioned ID: A 48-bit unique identifier. On a given bus there should be no
|
||||
Provisioned ID collision, otherwise the discovery mechanism may fail.
|
||||
|
||||
I3C slave events
|
||||
================
|
||||
|
@ -1556,9 +1556,11 @@ i3c_master_register_new_i3c_devs(struct i3c_master_controller *master)
|
||||
desc->dev->dev.of_node = desc->boardinfo->of_node;
|
||||
|
||||
ret = device_register(&desc->dev->dev);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
dev_err(&master->dev,
|
||||
"Failed to add I3C device (err = %d)\n", ret);
|
||||
put_device(&desc->dev->dev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2340,7 +2342,7 @@ static int i3c_master_i2c_adapter_init(struct i3c_master_controller *master)
|
||||
adap->dev.parent = master->dev.parent;
|
||||
adap->owner = master->dev.parent->driver->owner;
|
||||
adap->algo = &i3c_master_i2c_algo;
|
||||
strncpy(adap->name, dev_name(master->dev.parent), sizeof(adap->name));
|
||||
strscpy(adap->name, dev_name(master->dev.parent), sizeof(adap->name));
|
||||
|
||||
/* FIXME: Should we allow i3c masters to override these values? */
|
||||
adap->timeout = 1000;
|
||||
@ -2403,7 +2405,7 @@ static void i3c_master_unregister_i3c_devs(struct i3c_master_controller *master)
|
||||
void i3c_master_queue_ibi(struct i3c_dev_desc *dev, struct i3c_ibi_slot *slot)
|
||||
{
|
||||
atomic_inc(&dev->ibi->pending_ibis);
|
||||
queue_work(dev->common.master->wq, &slot->work);
|
||||
queue_work(dev->ibi->wq, &slot->work);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(i3c_master_queue_ibi);
|
||||
|
||||
@ -2660,6 +2662,10 @@ int i3c_master_register(struct i3c_master_controller *master,
|
||||
device_initialize(&master->dev);
|
||||
dev_set_name(&master->dev, "i3c-%d", i3cbus->id);
|
||||
|
||||
master->dev.dma_mask = parent->dma_mask;
|
||||
master->dev.coherent_dma_mask = parent->coherent_dma_mask;
|
||||
master->dev.dma_parms = parent->dma_parms;
|
||||
|
||||
ret = of_populate_i3c_bus(master);
|
||||
if (ret)
|
||||
goto err_put_dev;
|
||||
@ -2848,6 +2854,12 @@ int i3c_dev_request_ibi_locked(struct i3c_dev_desc *dev,
|
||||
if (!ibi)
|
||||
return -ENOMEM;
|
||||
|
||||
ibi->wq = alloc_ordered_workqueue(dev_name(i3cdev_to_dev(dev->dev)), WQ_MEM_RECLAIM);
|
||||
if (!ibi->wq) {
|
||||
kfree(ibi);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
atomic_set(&ibi->pending_ibis, 0);
|
||||
init_completion(&ibi->all_ibis_handled);
|
||||
ibi->handler = req->handler;
|
||||
@ -2875,6 +2887,12 @@ void i3c_dev_free_ibi_locked(struct i3c_dev_desc *dev)
|
||||
WARN_ON(i3c_dev_disable_ibi_locked(dev));
|
||||
|
||||
master->ops->free_ibi(dev);
|
||||
|
||||
if (dev->ibi->wq) {
|
||||
destroy_workqueue(dev->ibi->wq);
|
||||
dev->ibi->wq = NULL;
|
||||
}
|
||||
|
||||
kfree(dev->ibi);
|
||||
dev->ibi = NULL;
|
||||
}
|
||||
|
@ -233,7 +233,7 @@ struct dw_i3c_xfer {
|
||||
struct completion comp;
|
||||
int ret;
|
||||
unsigned int ncmds;
|
||||
struct dw_i3c_cmd cmds[];
|
||||
struct dw_i3c_cmd cmds[] __counted_by(ncmds);
|
||||
};
|
||||
|
||||
struct dw_i3c_i2c_dev_data {
|
||||
|
@ -191,7 +191,7 @@
|
||||
#define SLV_STATUS1_HJ_DIS BIT(18)
|
||||
#define SLV_STATUS1_MR_DIS BIT(17)
|
||||
#define SLV_STATUS1_PROT_ERR BIT(16)
|
||||
#define SLV_STATUS1_DA(x) (((s) & GENMASK(15, 9)) >> 9)
|
||||
#define SLV_STATUS1_DA(s) (((s) & GENMASK(15, 9)) >> 9)
|
||||
#define SLV_STATUS1_HAS_DA BIT(8)
|
||||
#define SLV_STATUS1_DDR_RX_FULL BIT(7)
|
||||
#define SLV_STATUS1_DDR_TX_FULL BIT(6)
|
||||
@ -387,7 +387,7 @@ struct cdns_i3c_xfer {
|
||||
struct completion comp;
|
||||
int ret;
|
||||
unsigned int ncmds;
|
||||
struct cdns_i3c_cmd cmds[];
|
||||
struct cdns_i3c_cmd cmds[] __counted_by(ncmds);
|
||||
};
|
||||
|
||||
struct cdns_i3c_data {
|
||||
@ -1623,13 +1623,13 @@ static int cdns_i3c_master_probe(struct platform_device *pdev)
|
||||
/* Device ID0 is reserved to describe this master. */
|
||||
master->maxdevs = CONF_STATUS0_DEVS_NUM(val);
|
||||
master->free_rr_slots = GENMASK(master->maxdevs, 1);
|
||||
master->caps.ibirfifodepth = CONF_STATUS0_IBIR_DEPTH(val);
|
||||
master->caps.cmdrfifodepth = CONF_STATUS0_CMDR_DEPTH(val);
|
||||
|
||||
val = readl(master->regs + CONF_STATUS1);
|
||||
master->caps.cmdfifodepth = CONF_STATUS1_CMD_DEPTH(val);
|
||||
master->caps.rxfifodepth = CONF_STATUS1_RX_DEPTH(val);
|
||||
master->caps.txfifodepth = CONF_STATUS1_TX_DEPTH(val);
|
||||
master->caps.ibirfifodepth = CONF_STATUS0_IBIR_DEPTH(val);
|
||||
master->caps.cmdrfifodepth = CONF_STATUS0_CMDR_DEPTH(val);
|
||||
|
||||
spin_lock_init(&master->ibi.lock);
|
||||
master->ibi.num_slots = CONF_STATUS1_IBI_HW_RES(val);
|
||||
|
@ -332,6 +332,7 @@ static int hci_cmd_v1_daa(struct i3c_hci *hci)
|
||||
CMD_A0_DEV_COUNT(1) |
|
||||
CMD_A0_ROC | CMD_A0_TOC;
|
||||
xfer->cmd_desc[1] = 0;
|
||||
xfer->completion = &done;
|
||||
hci->io->queue_xfer(hci, xfer, 1);
|
||||
if (!wait_for_completion_timeout(&done, HZ) &&
|
||||
hci->io->dequeue_xfer(hci, xfer, 1)) {
|
||||
|
@ -161,10 +161,12 @@ static int i3c_hci_bus_init(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 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);
|
||||
if (hci->cmd == &mipi_i3c_hci_cmd_v1)
|
||||
mipi_i3c_hci_dat_v1.cleanup(hci);
|
||||
@ -172,8 +174,7 @@ static void i3c_hci_bus_cleanup(struct i3c_master_controller *m)
|
||||
|
||||
void mipi_i3c_hci_resume(struct i3c_hci *hci)
|
||||
{
|
||||
/* the HC_CONTROL_RESUME bit is R/W1C so just read and write back */
|
||||
reg_write(HC_CONTROL, reg_read(HC_CONTROL));
|
||||
reg_set(HC_CONTROL, HC_CONTROL_RESUME);
|
||||
}
|
||||
|
||||
/* located here rather than pio.c because needed bits are in core reg space */
|
||||
@ -610,17 +611,17 @@ static int i3c_hci_init(struct i3c_hci *hci)
|
||||
offset = FIELD_GET(DAT_TABLE_OFFSET, regval);
|
||||
hci->DAT_regs = offset ? hci->base_regs + offset : NULL;
|
||||
hci->DAT_entries = FIELD_GET(DAT_TABLE_SIZE, regval);
|
||||
hci->DAT_entry_size = FIELD_GET(DAT_ENTRY_SIZE, regval);
|
||||
hci->DAT_entry_size = FIELD_GET(DAT_ENTRY_SIZE, regval) ? 0 : 8;
|
||||
dev_info(&hci->master.dev, "DAT: %u %u-bytes entries at offset %#x\n",
|
||||
hci->DAT_entries, hci->DAT_entry_size * 4, offset);
|
||||
hci->DAT_entries, hci->DAT_entry_size, offset);
|
||||
|
||||
regval = reg_read(DCT_SECTION);
|
||||
offset = FIELD_GET(DCT_TABLE_OFFSET, regval);
|
||||
hci->DCT_regs = offset ? hci->base_regs + offset : NULL;
|
||||
hci->DCT_entries = FIELD_GET(DCT_TABLE_SIZE, regval);
|
||||
hci->DCT_entry_size = FIELD_GET(DCT_ENTRY_SIZE, regval);
|
||||
hci->DCT_entry_size = FIELD_GET(DCT_ENTRY_SIZE, regval) ? 0 : 16;
|
||||
dev_info(&hci->master.dev, "DCT: %u %u-bytes entries at offset %#x\n",
|
||||
hci->DCT_entries, hci->DCT_entry_size * 4, offset);
|
||||
hci->DCT_entries, hci->DCT_entry_size, offset);
|
||||
|
||||
regval = reg_read(RING_HEADERS_SECTION);
|
||||
offset = FIELD_GET(RING_HEADERS_OFFSET, regval);
|
||||
@ -787,6 +788,7 @@ static struct platform_driver i3c_hci_driver = {
|
||||
},
|
||||
};
|
||||
module_platform_driver(i3c_hci_driver);
|
||||
MODULE_ALIAS("platform:mipi-i3c-hci");
|
||||
|
||||
MODULE_AUTHOR("Nicolas Pitre <npitre@baylibre.com>");
|
||||
MODULE_DESCRIPTION("MIPI I3C HCI driver");
|
||||
|
@ -64,15 +64,17 @@ static int hci_dat_v1_init(struct i3c_hci *hci)
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/* use a bitmap for faster free slot search */
|
||||
hci->DAT_data = bitmap_zalloc(hci->DAT_entries, GFP_KERNEL);
|
||||
if (!hci->DAT_data)
|
||||
return -ENOMEM;
|
||||
if (!hci->DAT_data) {
|
||||
/* use a bitmap for faster free slot search */
|
||||
hci->DAT_data = bitmap_zalloc(hci->DAT_entries, GFP_KERNEL);
|
||||
if (!hci->DAT_data)
|
||||
return -ENOMEM;
|
||||
|
||||
/* clear them */
|
||||
for (dat_idx = 0; dat_idx < hci->DAT_entries; dat_idx++) {
|
||||
dat_w0_write(dat_idx, 0);
|
||||
dat_w1_write(dat_idx, 0);
|
||||
/* clear them */
|
||||
for (dat_idx = 0; dat_idx < hci->DAT_entries; dat_idx++) {
|
||||
dat_w0_write(dat_idx, 0);
|
||||
dat_w1_write(dat_idx, 0);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -87,7 +89,13 @@ static void hci_dat_v1_cleanup(struct i3c_hci *hci)
|
||||
static int hci_dat_v1_alloc_entry(struct i3c_hci *hci)
|
||||
{
|
||||
unsigned int dat_idx;
|
||||
int ret;
|
||||
|
||||
if (!hci->DAT_data) {
|
||||
ret = hci_dat_v1_init(hci);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
dat_idx = find_first_zero_bit(hci->DAT_data, hci->DAT_entries);
|
||||
if (dat_idx >= hci->DAT_entries)
|
||||
return -ENOENT;
|
||||
@ -103,7 +111,8 @@ static void hci_dat_v1_free_entry(struct i3c_hci *hci, unsigned int dat_idx)
|
||||
{
|
||||
dat_w0_write(dat_idx, 0);
|
||||
dat_w1_write(dat_idx, 0);
|
||||
__clear_bit(dat_idx, hci->DAT_data);
|
||||
if (hci->DAT_data)
|
||||
__clear_bit(dat_idx, hci->DAT_data);
|
||||
}
|
||||
|
||||
static void hci_dat_v1_set_dynamic_addr(struct i3c_hci *hci,
|
||||
|
@ -139,7 +139,7 @@ struct hci_rh_data {
|
||||
|
||||
struct hci_rings_data {
|
||||
unsigned int total;
|
||||
struct hci_rh_data headers[];
|
||||
struct hci_rh_data headers[] __counted_by(total);
|
||||
};
|
||||
|
||||
struct hci_dma_dev_ibi_data {
|
||||
@ -229,6 +229,9 @@ static int hci_dma_init(struct i3c_hci *hci)
|
||||
hci->io_data = rings;
|
||||
rings->total = nr_rings;
|
||||
|
||||
regval = FIELD_PREP(MAX_HEADER_COUNT, rings->total);
|
||||
rhs_reg_write(CONTROL, regval);
|
||||
|
||||
for (i = 0; i < rings->total; i++) {
|
||||
u32 offset = rhs_reg_read(RHn_OFFSET(i));
|
||||
|
||||
@ -325,11 +328,10 @@ static int hci_dma_init(struct i3c_hci *hci)
|
||||
rh_reg_write(INTR_SIGNAL_ENABLE, regval);
|
||||
|
||||
ring_ready:
|
||||
rh_reg_write(RING_CONTROL, RING_CTRL_ENABLE);
|
||||
rh_reg_write(RING_CONTROL, RING_CTRL_ENABLE |
|
||||
RING_CTRL_RUN_STOP);
|
||||
}
|
||||
|
||||
regval = FIELD_PREP(MAX_HEADER_COUNT, rings->total);
|
||||
rhs_reg_write(CONTROL, regval);
|
||||
return 0;
|
||||
|
||||
err_out:
|
||||
@ -345,6 +347,8 @@ static void hci_dma_unmap_xfer(struct i3c_hci *hci,
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
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);
|
||||
@ -450,10 +454,9 @@ static bool hci_dma_dequeue_xfer(struct i3c_hci *hci,
|
||||
/*
|
||||
* We're deep in it if ever this condition is ever met.
|
||||
* Hardware might still be writing to memory, etc.
|
||||
* Better suspend the world than risking silent corruption.
|
||||
*/
|
||||
dev_crit(&hci->master.dev, "unable to abort the ring\n");
|
||||
BUG();
|
||||
WARN_ON(1);
|
||||
}
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
@ -734,7 +737,7 @@ static bool hci_dma_irq_handler(struct i3c_hci *hci, unsigned int mask)
|
||||
unsigned int i;
|
||||
bool handled = false;
|
||||
|
||||
for (i = 0; mask && i < 8; i++) {
|
||||
for (i = 0; mask && i < rings->total; i++) {
|
||||
struct hci_rh_data *rh;
|
||||
u32 status;
|
||||
|
||||
@ -756,9 +759,11 @@ static bool hci_dma_irq_handler(struct i3c_hci *hci, unsigned int mask)
|
||||
if (status & INTR_RING_OP)
|
||||
complete(&rh->op_done);
|
||||
|
||||
if (status & INTR_TRANSFER_ABORT)
|
||||
if (status & INTR_TRANSFER_ABORT) {
|
||||
dev_notice_ratelimited(&hci->master.dev,
|
||||
"ring %d: Transfer Aborted\n", i);
|
||||
mipi_i3c_hci_resume(hci);
|
||||
}
|
||||
if (status & INTR_WARN_INS_STOP_MODE)
|
||||
dev_warn_ratelimited(&hci->master.dev,
|
||||
"ring %d: Inserted Stop on Mode Change\n", i);
|
||||
|
@ -93,6 +93,7 @@
|
||||
#define SVC_I3C_MINTMASKED 0x098
|
||||
#define SVC_I3C_MERRWARN 0x09C
|
||||
#define SVC_I3C_MERRWARN_NACK BIT(2)
|
||||
#define SVC_I3C_MERRWARN_TIMEOUT BIT(20)
|
||||
#define SVC_I3C_MDMACTRL 0x0A0
|
||||
#define SVC_I3C_MDATACTRL 0x0AC
|
||||
#define SVC_I3C_MDATACTRL_FLUSHTB BIT(0)
|
||||
@ -143,7 +144,7 @@ struct svc_i3c_xfer {
|
||||
int ret;
|
||||
unsigned int type;
|
||||
unsigned int ncmds;
|
||||
struct svc_i3c_cmd cmds[];
|
||||
struct svc_i3c_cmd cmds[] __counted_by(ncmds);
|
||||
};
|
||||
|
||||
struct svc_i3c_regs_save {
|
||||
@ -175,6 +176,7 @@ struct svc_i3c_regs_save {
|
||||
* @ibi.slots: Available IBI slots
|
||||
* @ibi.tbq_slot: To be queued IBI slot
|
||||
* @ibi.lock: IBI lock
|
||||
* @lock: Transfer lock, protect between IBI work thread and callbacks from master
|
||||
*/
|
||||
struct svc_i3c_master {
|
||||
struct i3c_master_controller base;
|
||||
@ -203,6 +205,7 @@ struct svc_i3c_master {
|
||||
/* Prevent races within IBI handlers */
|
||||
spinlock_t lock;
|
||||
} ibi;
|
||||
struct mutex lock;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -225,6 +228,14 @@ static bool svc_i3c_master_error(struct svc_i3c_master *master)
|
||||
if (SVC_I3C_MSTATUS_ERRWARN(mstatus)) {
|
||||
merrwarn = readl(master->regs + SVC_I3C_MERRWARN);
|
||||
writel(merrwarn, master->regs + SVC_I3C_MERRWARN);
|
||||
|
||||
/* Ignore timeout error */
|
||||
if (merrwarn & SVC_I3C_MERRWARN_TIMEOUT) {
|
||||
dev_dbg(master->dev, "Warning condition: MSTATUS 0x%08x, MERRWARN 0x%08x\n",
|
||||
mstatus, merrwarn);
|
||||
return false;
|
||||
}
|
||||
|
||||
dev_err(master->dev,
|
||||
"Error condition: MSTATUS 0x%08x, MERRWARN 0x%08x\n",
|
||||
mstatus, merrwarn);
|
||||
@ -331,6 +342,7 @@ static int svc_i3c_master_handle_ibi(struct svc_i3c_master *master,
|
||||
struct i3c_ibi_slot *slot;
|
||||
unsigned int count;
|
||||
u32 mdatactrl;
|
||||
int ret, val;
|
||||
u8 *buf;
|
||||
|
||||
slot = i3c_generic_ibi_get_free_slot(data->ibi_pool);
|
||||
@ -340,6 +352,13 @@ static int svc_i3c_master_handle_ibi(struct svc_i3c_master *master,
|
||||
slot->len = 0;
|
||||
buf = slot->data;
|
||||
|
||||
ret = readl_relaxed_poll_timeout(master->regs + SVC_I3C_MSTATUS, val,
|
||||
SVC_I3C_MSTATUS_COMPLETE(val), 0, 1000);
|
||||
if (ret) {
|
||||
dev_err(master->dev, "Timeout when polling for COMPLETE\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
while (SVC_I3C_MSTATUS_RXPEND(readl(master->regs + SVC_I3C_MSTATUS)) &&
|
||||
slot->len < SVC_I3C_FIFO_SIZE) {
|
||||
mdatactrl = readl(master->regs + SVC_I3C_MDATACTRL);
|
||||
@ -384,6 +403,7 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)
|
||||
u32 status, val;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&master->lock);
|
||||
/* Acknowledge the incoming interrupt with the AUTOIBI mechanism */
|
||||
writel(SVC_I3C_MCTRL_REQUEST_AUTO_IBI |
|
||||
SVC_I3C_MCTRL_IBIRESP_AUTO,
|
||||
@ -394,6 +414,7 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)
|
||||
SVC_I3C_MSTATUS_IBIWON(val), 0, 1000);
|
||||
if (ret) {
|
||||
dev_err(master->dev, "Timeout when polling for IBIWON\n");
|
||||
svc_i3c_master_emit_stop(master);
|
||||
goto reenable_ibis;
|
||||
}
|
||||
|
||||
@ -460,12 +481,13 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)
|
||||
|
||||
reenable_ibis:
|
||||
svc_i3c_master_enable_interrupts(master, SVC_I3C_MINT_SLVSTART);
|
||||
mutex_unlock(&master->lock);
|
||||
}
|
||||
|
||||
static irqreturn_t svc_i3c_master_irq_handler(int irq, void *dev_id)
|
||||
{
|
||||
struct svc_i3c_master *master = (struct svc_i3c_master *)dev_id;
|
||||
u32 active = readl(master->regs + SVC_I3C_MINTMASKED);
|
||||
u32 active = readl(master->regs + SVC_I3C_MSTATUS);
|
||||
|
||||
if (!SVC_I3C_MSTATUS_SLVSTART(active))
|
||||
return IRQ_NONE;
|
||||
@ -765,7 +787,7 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master,
|
||||
u8 data[6];
|
||||
|
||||
/*
|
||||
* We only care about the 48-bit provisional ID yet to
|
||||
* We only care about the 48-bit provisioned ID yet to
|
||||
* be sure a device does not nack an address twice.
|
||||
* Otherwise, we would just need to flush the RX FIFO.
|
||||
*/
|
||||
@ -1007,6 +1029,9 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
|
||||
u32 reg;
|
||||
int ret;
|
||||
|
||||
/* clean SVC_I3C_MINT_IBIWON w1c bits */
|
||||
writel(SVC_I3C_MINT_IBIWON, master->regs + SVC_I3C_MSTATUS);
|
||||
|
||||
writel(SVC_I3C_MCTRL_REQUEST_START_ADDR |
|
||||
xfer_type |
|
||||
SVC_I3C_MCTRL_IBIRESP_NACK |
|
||||
@ -1025,6 +1050,23 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
|
||||
goto emit_stop;
|
||||
}
|
||||
|
||||
/*
|
||||
* According to I3C spec ver 1.1.1, 5.1.2.2.3 Consequence of Controller Starting a Frame
|
||||
* with I3C Target Address.
|
||||
*
|
||||
* The I3C Controller normally should start a Frame, the Address may be arbitrated, and so
|
||||
* the Controller shall monitor to see whether an In-Band Interrupt request, a Controller
|
||||
* Role Request (i.e., Secondary Controller requests to become the Active Controller), or
|
||||
* a Hot-Join Request has been made.
|
||||
*
|
||||
* If missed IBIWON check, the wrong data will be return. When IBIWON happen, return failure
|
||||
* and yield the above events handler.
|
||||
*/
|
||||
if (SVC_I3C_MSTATUS_IBIWON(reg)) {
|
||||
ret = -ENXIO;
|
||||
goto emit_stop;
|
||||
}
|
||||
|
||||
if (rnw)
|
||||
ret = svc_i3c_master_read(master, in, xfer_len);
|
||||
else
|
||||
@ -1204,9 +1246,11 @@ static int svc_i3c_master_send_bdcast_ccc_cmd(struct svc_i3c_master *master,
|
||||
cmd->read_len = 0;
|
||||
cmd->continued = false;
|
||||
|
||||
mutex_lock(&master->lock);
|
||||
svc_i3c_master_enqueue_xfer(master, xfer);
|
||||
if (!wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000)))
|
||||
svc_i3c_master_dequeue_xfer(master, xfer);
|
||||
mutex_unlock(&master->lock);
|
||||
|
||||
ret = xfer->ret;
|
||||
kfree(buf);
|
||||
@ -1250,9 +1294,11 @@ static int svc_i3c_master_send_direct_ccc_cmd(struct svc_i3c_master *master,
|
||||
cmd->read_len = read_len;
|
||||
cmd->continued = false;
|
||||
|
||||
mutex_lock(&master->lock);
|
||||
svc_i3c_master_enqueue_xfer(master, xfer);
|
||||
if (!wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000)))
|
||||
svc_i3c_master_dequeue_xfer(master, xfer);
|
||||
mutex_unlock(&master->lock);
|
||||
|
||||
if (cmd->read_len != xfer_len)
|
||||
ccc->dests[0].payload.len = cmd->read_len;
|
||||
@ -1309,9 +1355,11 @@ static int svc_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
|
||||
cmd->continued = (i + 1) < nxfers;
|
||||
}
|
||||
|
||||
mutex_lock(&master->lock);
|
||||
svc_i3c_master_enqueue_xfer(master, xfer);
|
||||
if (!wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000)))
|
||||
svc_i3c_master_dequeue_xfer(master, xfer);
|
||||
mutex_unlock(&master->lock);
|
||||
|
||||
ret = xfer->ret;
|
||||
svc_i3c_master_free_xfer(xfer);
|
||||
@ -1347,9 +1395,11 @@ static int svc_i3c_master_i2c_xfers(struct i2c_dev_desc *dev,
|
||||
cmd->continued = (i + 1 < nxfers);
|
||||
}
|
||||
|
||||
mutex_lock(&master->lock);
|
||||
svc_i3c_master_enqueue_xfer(master, xfer);
|
||||
if (!wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000)))
|
||||
svc_i3c_master_dequeue_xfer(master, xfer);
|
||||
mutex_unlock(&master->lock);
|
||||
|
||||
ret = xfer->ret;
|
||||
svc_i3c_master_free_xfer(xfer);
|
||||
@ -1540,6 +1590,8 @@ static int svc_i3c_master_probe(struct platform_device *pdev)
|
||||
|
||||
INIT_WORK(&master->hj_work, svc_i3c_master_hj_work);
|
||||
INIT_WORK(&master->ibi_work, svc_i3c_master_ibi_work);
|
||||
mutex_init(&master->lock);
|
||||
|
||||
ret = devm_request_irq(dev, master->irq, svc_i3c_master_irq_handler,
|
||||
IRQF_NO_SUSPEND, "svc-i3c-irq", master);
|
||||
if (ret)
|
||||
@ -1651,7 +1703,7 @@ static const struct dev_pm_ops svc_i3c_pm_ops = {
|
||||
};
|
||||
|
||||
static const struct of_device_id svc_i3c_master_of_match_tbl[] = {
|
||||
{ .compatible = "silvaco,i3c-master" },
|
||||
{ .compatible = "silvaco,i3c-master-v1"},
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, svc_i3c_master_of_match_tbl);
|
||||
|
@ -96,7 +96,7 @@ enum i3c_dcr {
|
||||
|
||||
/**
|
||||
* struct i3c_device_info - I3C device information
|
||||
* @pid: Provisional ID
|
||||
* @pid: Provisioned ID
|
||||
* @bcr: Bus Characteristic Register
|
||||
* @dcr: Device Characteristic Register
|
||||
* @static_addr: static/I2C address
|
||||
|
@ -135,6 +135,7 @@ struct i3c_ibi_slot {
|
||||
* rejected by the master
|
||||
* @num_slots: number of IBI slots reserved for this device
|
||||
* @enabled: reflect the IBI status
|
||||
* @wq: workqueue used to execute IBI handlers.
|
||||
* @handler: IBI handler specified at i3c_device_request_ibi() call time. This
|
||||
* handler will be called from the controller workqueue, and as such
|
||||
* is allowed to sleep (though it is recommended to process the IBI
|
||||
@ -157,6 +158,7 @@ struct i3c_device_ibi_info {
|
||||
unsigned int max_payload_len;
|
||||
unsigned int num_slots;
|
||||
unsigned int enabled;
|
||||
struct workqueue_struct *wq;
|
||||
void (*handler)(struct i3c_device *dev,
|
||||
const struct i3c_ibi_payload *payload);
|
||||
};
|
||||
@ -172,7 +174,7 @@ struct i3c_device_ibi_info {
|
||||
* assigned a dynamic address by the master. Will be used during
|
||||
* bus initialization to assign it a specific dynamic address
|
||||
* before starting DAA (Dynamic Address Assignment)
|
||||
* @pid: I3C Provisional ID exposed by the device. This is a unique identifier
|
||||
* @pid: I3C Provisioned ID exposed by the device. This is a unique identifier
|
||||
* that may be used to attach boardinfo to i3c_dev_desc when the device
|
||||
* does not have a static address
|
||||
* @of_node: optional DT node in case the device has been described in the DT
|
||||
@ -475,7 +477,7 @@ struct i3c_master_controller_ops {
|
||||
* @boardinfo.i2c: list of I2C boardinfo objects
|
||||
* @boardinfo: board-level information attached to devices connected on the bus
|
||||
* @bus: I3C bus exposed by this master
|
||||
* @wq: workqueue used to execute IBI handlers. Can also be used by master
|
||||
* @wq: workqueue which can be used by master
|
||||
* drivers if they need to postpone operations that need to take place
|
||||
* in a thread context. Typical examples are Hot Join processing which
|
||||
* requires taking the bus lock in maintenance, which in turn, can only
|
||||
|
Loading…
x
Reference in New Issue
Block a user