Commit c85e1497 authored by Linus Torvalds's avatar Linus Torvalds

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

Pull i3c updates from Alexandre Belloni:
 "This cycle, there are new features for the Designware controller and
  fixes for the other IPs:

   - dw: optional apb clock and power management support, IBI handling
     fixes

   - mipi-i3c-hci: IBI handling fixes

   - svc: a few fixes"

* tag 'i3c/for-6.11' of git://git.kernel.org/pub/scm/linux/kernel/git/i3c/linux:
  dt-bindings: i3c: add header for generic I3C flags
  i3c: master: svc: Fix error code in svc_i3c_master_do_daa_locked()
  i3c: master: Enhance i3c_bus_type visibility for device searching & event monitoring
  i3c: dw: Add power management support
  i3c: dw: Add some functions for reusability
  i3c: dw: Save timing registers and other values
  i3c: master: svc: Improve DAA STOP handle code logic
  i3c: dw: Add optional apb clock
  i3c: dw: Use new *_enabled clk API
  dt-bindings: i3c: dw: Add apb clock binding
  i3c: master: svc: Convert comma to semicolon
  i3c: mipi-i3c-hci: Round IBI data chunk size to HW supported value
  i3c: mipi-i3c-hci: Error out instead on BUG_ON() in IBI DMA setup
  i3c: mipi-i3c-hci: Set IBI Status and Data Ring base addresses
  i3c: mipi-i3c-hci: Switch to lower_32_bits()/upper_32_bits() helpers
  i3c: dw: Remove ibi_capable property
  i3c: dw: Fix IBI intr programming
  i3c: dw: Fix clearing queue thld
  i3c: mipi-i3c-hci: Fix number of DAT/DCT entries for HCI versions < 1.1
  i3c: master: svc: resend target address when get NACK
parents 1fcaa5db 24168c5e
...@@ -91,6 +91,7 @@ patternProperties: ...@@ -91,6 +91,7 @@ patternProperties:
- const: 0 - const: 0
- description: | - description: |
Shall encode the I3C LVR (Legacy Virtual Register): Shall encode the I3C LVR (Legacy Virtual Register):
See include/dt-bindings/i3c/i3c.h
bit[31:8]: unused/ignored bit[31:8]: unused/ignored
bit[7:5]: I2C device index. Possible values: bit[7:5]: I2C device index. Possible values:
* 0: I2C device has a 50 ns spike filter * 0: I2C device has a 50 ns spike filter
...@@ -153,6 +154,8 @@ additionalProperties: true ...@@ -153,6 +154,8 @@ additionalProperties: true
examples: examples:
- | - |
#include <dt-bindings/i3c/i3c.h>
i3c@d040000 { i3c@d040000 {
compatible = "cdns,i3c-master"; compatible = "cdns,i3c-master";
clocks = <&coreclock>, <&i3csysclock>; clocks = <&coreclock>, <&i3csysclock>;
...@@ -166,7 +169,7 @@ examples: ...@@ -166,7 +169,7 @@ examples:
/* I2C device. */ /* I2C device. */
eeprom@57 { eeprom@57 {
compatible = "atmel,24c01"; compatible = "atmel,24c01";
reg = <0x57 0x0 0x10>; reg = <0x57 0x0 (I2C_FM | I2C_FILTER)>;
pagesize = <0x8>; pagesize = <0x8>;
}; };
......
...@@ -20,7 +20,16 @@ properties: ...@@ -20,7 +20,16 @@ properties:
maxItems: 1 maxItems: 1
clocks: clocks:
maxItems: 1 minItems: 1
items:
- description: Core clock
- description: APB clock
clock-names:
minItems: 1
items:
- const: core
- const: apb
interrupts: interrupts:
maxItems: 1 maxItems: 1
......
...@@ -10655,6 +10655,7 @@ F: Documentation/ABI/testing/sysfs-bus-i3c ...@@ -10655,6 +10655,7 @@ F: Documentation/ABI/testing/sysfs-bus-i3c
F: Documentation/devicetree/bindings/i3c/ F: Documentation/devicetree/bindings/i3c/
F: Documentation/driver-api/i3c F: Documentation/driver-api/i3c
F: drivers/i3c/ F: drivers/i3c/
F: include/dt-bindings/i3c/
F: include/linux/i3c/ F: include/linux/i3c/
IBM Operation Panel Input Driver IBM Operation Panel Input Driver
......
...@@ -10,8 +10,6 @@ ...@@ -10,8 +10,6 @@
#include <linux/i3c/master.h> #include <linux/i3c/master.h>
extern const struct bus_type i3c_bus_type;
void i3c_bus_normaluse_lock(struct i3c_bus *bus); void i3c_bus_normaluse_lock(struct i3c_bus *bus);
void i3c_bus_normaluse_unlock(struct i3c_bus *bus); void i3c_bus_normaluse_unlock(struct i3c_bus *bus);
......
...@@ -342,6 +342,7 @@ const struct bus_type i3c_bus_type = { ...@@ -342,6 +342,7 @@ const struct bus_type i3c_bus_type = {
.probe = i3c_device_probe, .probe = i3c_device_probe,
.remove = i3c_device_remove, .remove = i3c_device_remove,
}; };
EXPORT_SYMBOL_GPL(i3c_bus_type);
static enum i3c_addr_slot_status static enum i3c_addr_slot_status
i3c_bus_get_addr_slot_status(struct i3c_bus *bus, u16 addr) i3c_bus_get_addr_slot_status(struct i3c_bus *bus, u16 addr)
......
...@@ -156,7 +156,6 @@ static int ast2600_i3c_probe(struct platform_device *pdev) ...@@ -156,7 +156,6 @@ static int ast2600_i3c_probe(struct platform_device *pdev)
i3c->sda_pullup); i3c->sda_pullup);
i3c->dw.platform_ops = &ast2600_i3c_ops; i3c->dw.platform_ops = &ast2600_i3c_ops;
i3c->dw.ibi_capable = true;
return dw_i3c_common_probe(&i3c->dw, pdev); return dw_i3c_common_probe(&i3c->dw, pdev);
} }
......
This diff is collapsed.
...@@ -19,11 +19,13 @@ struct dw_i3c_master_caps { ...@@ -19,11 +19,13 @@ struct dw_i3c_master_caps {
struct dw_i3c_dat_entry { struct dw_i3c_dat_entry {
u8 addr; u8 addr;
bool is_i2c_addr;
struct i3c_dev_desc *ibi_dev; struct i3c_dev_desc *ibi_dev;
}; };
struct dw_i3c_master { struct dw_i3c_master {
struct i3c_master_controller base; struct i3c_master_controller base;
struct device *dev;
u16 maxdevs; u16 maxdevs;
u16 datstartaddr; u16 datstartaddr;
u32 free_pos; u32 free_pos;
...@@ -36,10 +38,18 @@ struct dw_i3c_master { ...@@ -36,10 +38,18 @@ struct dw_i3c_master {
void __iomem *regs; void __iomem *regs;
struct reset_control *core_rst; struct reset_control *core_rst;
struct clk *core_clk; struct clk *core_clk;
struct clk *pclk;
char version[5]; char version[5];
char type[5]; char type[5];
bool ibi_capable; u32 sir_rej_mask;
bool i2c_slv_prsnt;
u32 dev_addr;
u32 i3c_pp_timing;
u32 i3c_od_timing;
u32 ext_lcnt_timing;
u32 bus_free_timing;
u32 i2c_fm_timing;
u32 i2c_fmp_timing;
/* /*
* Per-device hardware data, used to manage the device address table * Per-device hardware data, used to manage the device address table
* (DAT) * (DAT)
......
...@@ -631,6 +631,7 @@ static irqreturn_t i3c_hci_irq_handler(int irq, void *dev_id) ...@@ -631,6 +631,7 @@ static irqreturn_t i3c_hci_irq_handler(int irq, void *dev_id)
static int i3c_hci_init(struct i3c_hci *hci) static int i3c_hci_init(struct i3c_hci *hci)
{ {
u32 regval, offset; u32 regval, offset;
bool size_in_dwords;
int ret; int ret;
/* Validate HCI hardware version */ /* Validate HCI hardware version */
...@@ -654,11 +655,16 @@ static int i3c_hci_init(struct i3c_hci *hci) ...@@ -654,11 +655,16 @@ static int i3c_hci_init(struct i3c_hci *hci)
hci->caps = reg_read(HC_CAPABILITIES); hci->caps = reg_read(HC_CAPABILITIES);
DBG("caps = %#x", hci->caps); DBG("caps = %#x", hci->caps);
size_in_dwords = hci->version_major < 1 ||
(hci->version_major == 1 && hci->version_minor < 1);
regval = reg_read(DAT_SECTION); regval = reg_read(DAT_SECTION);
offset = FIELD_GET(DAT_TABLE_OFFSET, regval); offset = FIELD_GET(DAT_TABLE_OFFSET, regval);
hci->DAT_regs = offset ? hci->base_regs + offset : NULL; hci->DAT_regs = offset ? hci->base_regs + offset : NULL;
hci->DAT_entries = FIELD_GET(DAT_TABLE_SIZE, regval); hci->DAT_entries = FIELD_GET(DAT_TABLE_SIZE, regval);
hci->DAT_entry_size = FIELD_GET(DAT_ENTRY_SIZE, regval) ? 0 : 8; hci->DAT_entry_size = FIELD_GET(DAT_ENTRY_SIZE, regval) ? 0 : 8;
if (size_in_dwords)
hci->DAT_entries = 4 * hci->DAT_entries / hci->DAT_entry_size;
dev_info(&hci->master.dev, "DAT: %u %u-bytes entries at offset %#x\n", dev_info(&hci->master.dev, "DAT: %u %u-bytes entries at offset %#x\n",
hci->DAT_entries, hci->DAT_entry_size, offset); hci->DAT_entries, hci->DAT_entry_size, offset);
...@@ -667,6 +673,8 @@ static int i3c_hci_init(struct i3c_hci *hci) ...@@ -667,6 +673,8 @@ static int i3c_hci_init(struct i3c_hci *hci)
hci->DCT_regs = offset ? hci->base_regs + offset : NULL; hci->DCT_regs = offset ? hci->base_regs + offset : NULL;
hci->DCT_entries = FIELD_GET(DCT_TABLE_SIZE, regval); hci->DCT_entries = FIELD_GET(DCT_TABLE_SIZE, regval);
hci->DCT_entry_size = FIELD_GET(DCT_ENTRY_SIZE, regval) ? 0 : 16; hci->DCT_entry_size = FIELD_GET(DCT_ENTRY_SIZE, regval) ? 0 : 16;
if (size_in_dwords)
hci->DCT_entries = 4 * hci->DCT_entries / hci->DCT_entry_size;
dev_info(&hci->master.dev, "DCT: %u %u-bytes entries at offset %#x\n", dev_info(&hci->master.dev, "DCT: %u %u-bytes entries at offset %#x\n",
hci->DCT_entries, hci->DCT_entry_size, offset); hci->DCT_entries, hci->DCT_entry_size, offset);
......
...@@ -147,21 +147,6 @@ struct hci_dma_dev_ibi_data { ...@@ -147,21 +147,6 @@ struct hci_dma_dev_ibi_data {
unsigned int max_len; unsigned int max_len;
}; };
static inline u32 lo32(dma_addr_t physaddr)
{
return physaddr;
}
static inline u32 hi32(dma_addr_t physaddr)
{
/* trickery to avoid compiler warnings on 32-bit build targets */
if (sizeof(dma_addr_t) > 4) {
u64 hi = physaddr;
return hi >> 32;
}
return 0;
}
static void hci_dma_cleanup(struct i3c_hci *hci) static void hci_dma_cleanup(struct i3c_hci *hci)
{ {
struct hci_rings_data *rings = hci->io_data; struct hci_rings_data *rings = hci->io_data;
...@@ -265,10 +250,10 @@ static int hci_dma_init(struct i3c_hci *hci) ...@@ -265,10 +250,10 @@ static int hci_dma_init(struct i3c_hci *hci)
if (!rh->xfer || !rh->resp || !rh->src_xfers) if (!rh->xfer || !rh->resp || !rh->src_xfers)
goto err_out; goto err_out;
rh_reg_write(CMD_RING_BASE_LO, lo32(rh->xfer_dma)); rh_reg_write(CMD_RING_BASE_LO, lower_32_bits(rh->xfer_dma));
rh_reg_write(CMD_RING_BASE_HI, hi32(rh->xfer_dma)); rh_reg_write(CMD_RING_BASE_HI, upper_32_bits(rh->xfer_dma));
rh_reg_write(RESP_RING_BASE_LO, lo32(rh->resp_dma)); rh_reg_write(RESP_RING_BASE_LO, lower_32_bits(rh->resp_dma));
rh_reg_write(RESP_RING_BASE_HI, hi32(rh->resp_dma)); rh_reg_write(RESP_RING_BASE_HI, upper_32_bits(rh->resp_dma));
regval = FIELD_PREP(CR_RING_SIZE, rh->xfer_entries); regval = FIELD_PREP(CR_RING_SIZE, rh->xfer_entries);
rh_reg_write(CR_SETUP, regval); rh_reg_write(CR_SETUP, regval);
...@@ -294,7 +279,17 @@ static int hci_dma_init(struct i3c_hci *hci) ...@@ -294,7 +279,17 @@ static int hci_dma_init(struct i3c_hci *hci)
rh->ibi_chunk_sz = dma_get_cache_alignment(); rh->ibi_chunk_sz = dma_get_cache_alignment();
rh->ibi_chunk_sz *= IBI_CHUNK_CACHELINES; rh->ibi_chunk_sz *= IBI_CHUNK_CACHELINES;
BUG_ON(rh->ibi_chunk_sz > 256); /*
* Round IBI data chunk size to number of bytes supported by
* the HW. Chunk size can be 2^n number of DWORDs which is the
* same as 2^(n+2) bytes, where n is 0..6.
*/
rh->ibi_chunk_sz = umax(4, rh->ibi_chunk_sz);
rh->ibi_chunk_sz = roundup_pow_of_two(rh->ibi_chunk_sz);
if (rh->ibi_chunk_sz > 256) {
ret = -EINVAL;
goto err_out;
}
ibi_status_ring_sz = rh->ibi_status_sz * rh->ibi_status_entries; ibi_status_ring_sz = rh->ibi_status_sz * rh->ibi_status_entries;
ibi_data_ring_sz = rh->ibi_chunk_sz * rh->ibi_chunks_total; ibi_data_ring_sz = rh->ibi_chunk_sz * rh->ibi_chunks_total;
...@@ -315,6 +310,11 @@ static int hci_dma_init(struct i3c_hci *hci) ...@@ -315,6 +310,11 @@ static int hci_dma_init(struct i3c_hci *hci)
goto err_out; goto err_out;
} }
rh_reg_write(IBI_STATUS_RING_BASE_LO, lower_32_bits(rh->ibi_status_dma));
rh_reg_write(IBI_STATUS_RING_BASE_HI, upper_32_bits(rh->ibi_status_dma));
rh_reg_write(IBI_DATA_RING_BASE_LO, lower_32_bits(rh->ibi_data_dma));
rh_reg_write(IBI_DATA_RING_BASE_HI, upper_32_bits(rh->ibi_data_dma));
regval = FIELD_PREP(IBI_STATUS_RING_SIZE, regval = FIELD_PREP(IBI_STATUS_RING_SIZE,
rh->ibi_status_entries) | rh->ibi_status_entries) |
FIELD_PREP(IBI_DATA_CHUNK_SIZE, FIELD_PREP(IBI_DATA_CHUNK_SIZE,
...@@ -404,8 +404,8 @@ static int hci_dma_queue_xfer(struct i3c_hci *hci, ...@@ -404,8 +404,8 @@ static int hci_dma_queue_xfer(struct i3c_hci *hci,
hci_dma_unmap_xfer(hci, xfer_list, i); hci_dma_unmap_xfer(hci, xfer_list, i);
return -ENOMEM; return -ENOMEM;
} }
*ring_data++ = lo32(xfer->data_dma); *ring_data++ = lower_32_bits(xfer->data_dma);
*ring_data++ = hi32(xfer->data_dma); *ring_data++ = upper_32_bits(xfer->data_dma);
} else { } else {
*ring_data++ = 0; *ring_data++ = 0;
*ring_data++ = 0; *ring_data++ = 0;
......
...@@ -790,7 +790,20 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master, ...@@ -790,7 +790,20 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master,
int ret, i; int ret, i;
while (true) { while (true) {
/* Enter/proceed with DAA */ /* SVC_I3C_MCTRL_REQUEST_PROC_DAA have two mode, ENTER DAA or PROCESS DAA.
*
* ENTER DAA:
* 1 will issue START, 7E, ENTDAA, and then emits 7E/R to process first target.
* 2 Stops just before the new Dynamic Address (DA) is to be emitted.
*
* PROCESS DAA:
* 1 The DA is written using MWDATAB or ADDR bits 6:0.
* 2 ProcessDAA is requested again to write the new address, and then starts the
* next (START, 7E, ENTDAA) unless marked to STOP; an MSTATUS indicating NACK
* means DA was not accepted (e.g. parity error). If PROCESSDAA is NACKed on the
* 7E/R, which means no more Slaves need a DA, then a COMPLETE will be signaled
* (along with DONE), and a STOP issued automatically.
*/
writel(SVC_I3C_MCTRL_REQUEST_PROC_DAA | writel(SVC_I3C_MCTRL_REQUEST_PROC_DAA |
SVC_I3C_MCTRL_TYPE_I3C | SVC_I3C_MCTRL_TYPE_I3C |
SVC_I3C_MCTRL_IBIRESP_NACK | SVC_I3C_MCTRL_IBIRESP_NACK |
...@@ -807,7 +820,7 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master, ...@@ -807,7 +820,7 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master,
SVC_I3C_MSTATUS_MCTRLDONE(reg), SVC_I3C_MSTATUS_MCTRLDONE(reg),
1, 1000); 1, 1000);
if (ret) if (ret)
return ret; break;
if (SVC_I3C_MSTATUS_RXPEND(reg)) { if (SVC_I3C_MSTATUS_RXPEND(reg)) {
u8 data[6]; u8 data[6];
...@@ -819,7 +832,7 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master, ...@@ -819,7 +832,7 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master,
*/ */
ret = svc_i3c_master_readb(master, data, 6); ret = svc_i3c_master_readb(master, data, 6);
if (ret) if (ret)
return ret; break;
for (i = 0; i < 6; i++) for (i = 0; i < 6; i++)
prov_id[dev_nb] |= (u64)(data[i]) << (8 * (5 - i)); prov_id[dev_nb] |= (u64)(data[i]) << (8 * (5 - i));
...@@ -827,7 +840,7 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master, ...@@ -827,7 +840,7 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master,
/* We do not care about the BCR and DCR yet */ /* We do not care about the BCR and DCR yet */
ret = svc_i3c_master_readb(master, data, 2); ret = svc_i3c_master_readb(master, data, 2);
if (ret) if (ret)
return ret; break;
} else if (SVC_I3C_MSTATUS_MCTRLDONE(reg)) { } else if (SVC_I3C_MSTATUS_MCTRLDONE(reg)) {
if (SVC_I3C_MSTATUS_STATE_IDLE(reg) && if (SVC_I3C_MSTATUS_STATE_IDLE(reg) &&
SVC_I3C_MSTATUS_COMPLETE(reg)) { SVC_I3C_MSTATUS_COMPLETE(reg)) {
...@@ -835,12 +848,23 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master, ...@@ -835,12 +848,23 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master,
* All devices received and acked they dynamic * All devices received and acked they dynamic
* address, this is the natural end of the DAA * address, this is the natural end of the DAA
* procedure. * procedure.
*
* Hardware will auto emit STOP at this case.
*/ */
break; *count = dev_nb;
return 0;
} else if (SVC_I3C_MSTATUS_NACKED(reg)) { } else if (SVC_I3C_MSTATUS_NACKED(reg)) {
/* No I3C devices attached */ /* No I3C devices attached */
if (dev_nb == 0) if (dev_nb == 0) {
/*
* Hardware can't treat first NACK for ENTAA as normal
* COMPLETE. So need manual emit STOP.
*/
ret = 0;
*count = 0;
break; break;
}
/* /*
* A slave device nacked the address, this is * A slave device nacked the address, this is
...@@ -849,8 +873,10 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master, ...@@ -849,8 +873,10 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master,
* answer again immediately and shall ack the * answer again immediately and shall ack the
* address this time. * address this time.
*/ */
if (prov_id[dev_nb] == nacking_prov_id) if (prov_id[dev_nb] == nacking_prov_id) {
return -EIO; ret = -EIO;
break;
}
dev_nb--; dev_nb--;
nacking_prov_id = prov_id[dev_nb]; nacking_prov_id = prov_id[dev_nb];
...@@ -858,7 +884,7 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master, ...@@ -858,7 +884,7 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master,
continue; continue;
} else { } else {
return -EIO; break;
} }
} }
...@@ -870,12 +896,12 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master, ...@@ -870,12 +896,12 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master,
SVC_I3C_MSTATUS_BETWEEN(reg), SVC_I3C_MSTATUS_BETWEEN(reg),
0, 1000); 0, 1000);
if (ret) if (ret)
return ret; break;
/* Give the slave device a suitable dynamic address */ /* Give the slave device a suitable dynamic address */
ret = i3c_master_get_free_addr(&master->base, last_addr + 1); ret = i3c_master_get_free_addr(&master->base, last_addr + 1);
if (ret < 0) if (ret < 0)
return ret; break;
addrs[dev_nb] = ret; addrs[dev_nb] = ret;
dev_dbg(master->dev, "DAA: device %d assigned to 0x%02x\n", dev_dbg(master->dev, "DAA: device %d assigned to 0x%02x\n",
...@@ -885,9 +911,9 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master, ...@@ -885,9 +911,9 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master,
last_addr = addrs[dev_nb++]; last_addr = addrs[dev_nb++];
} }
*count = dev_nb; /* Need manual issue STOP except for Complete condition */
svc_i3c_master_emit_stop(master);
return 0; return ret;
} }
static int svc_i3c_update_ibirules(struct svc_i3c_master *master) static int svc_i3c_update_ibirules(struct svc_i3c_master *master)
...@@ -961,11 +987,10 @@ static int svc_i3c_master_do_daa(struct i3c_master_controller *m) ...@@ -961,11 +987,10 @@ static int svc_i3c_master_do_daa(struct i3c_master_controller *m)
spin_lock_irqsave(&master->xferqueue.lock, flags); spin_lock_irqsave(&master->xferqueue.lock, flags);
ret = svc_i3c_master_do_daa_locked(master, addrs, &dev_nb); ret = svc_i3c_master_do_daa_locked(master, addrs, &dev_nb);
spin_unlock_irqrestore(&master->xferqueue.lock, flags); spin_unlock_irqrestore(&master->xferqueue.lock, flags);
if (ret) {
svc_i3c_master_emit_stop(master);
svc_i3c_master_clear_merrwarn(master); svc_i3c_master_clear_merrwarn(master);
if (ret)
goto rpm_out; goto rpm_out;
}
/* Register all devices who participated to the core */ /* Register all devices who participated to the core */
for (i = 0; i < dev_nb; i++) { for (i = 0; i < dev_nb; i++) {
...@@ -1052,12 +1077,15 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master, ...@@ -1052,12 +1077,15 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
u8 *in, const u8 *out, unsigned int xfer_len, u8 *in, const u8 *out, unsigned int xfer_len,
unsigned int *actual_len, bool continued) unsigned int *actual_len, bool continued)
{ {
int retry = 2;
u32 reg; u32 reg;
int ret; int ret;
/* clean SVC_I3C_MINT_IBIWON w1c bits */ /* clean SVC_I3C_MINT_IBIWON w1c bits */
writel(SVC_I3C_MINT_IBIWON, master->regs + SVC_I3C_MSTATUS); writel(SVC_I3C_MINT_IBIWON, master->regs + SVC_I3C_MSTATUS);
while (retry--) {
writel(SVC_I3C_MCTRL_REQUEST_START_ADDR | writel(SVC_I3C_MCTRL_REQUEST_START_ADDR |
xfer_type | xfer_type |
SVC_I3C_MCTRL_IBIRESP_NACK | SVC_I3C_MCTRL_IBIRESP_NACK |
...@@ -1072,10 +1100,37 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master, ...@@ -1072,10 +1100,37 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
goto emit_stop; goto emit_stop;
if (readl(master->regs + SVC_I3C_MERRWARN) & SVC_I3C_MERRWARN_NACK) { if (readl(master->regs + SVC_I3C_MERRWARN) & SVC_I3C_MERRWARN_NACK) {
/*
* According to I3C Spec 1.1.1, 11-Jun-2021, section: 5.1.2.2.3.
* If the Controller chooses to start an I3C Message with an I3C Dynamic
* Address, then special provisions shall be made because that same I3C
* Target may be initiating an IBI or a Controller Role Request. So, one of
* three things may happen: (skip 1, 2)
*
* 3. The Addresses match and the RnW bits also match, and so neither
* Controller nor Target will ACK since both are expecting the other side to
* provide ACK. As a result, each side might think it had "won" arbitration,
* but neither side would continue, as each would subsequently see that the
* other did not provide ACK.
* ...
* For either value of RnW: Due to the NACK, the Controller shall defer the
* Private Write or Private Read, and should typically transmit the Target
* Address again after a Repeated START (i.e., the next one or any one prior
* to a STOP in the Frame). Since the Address Header following a Repeated
* START is not arbitrated, the Controller will always win (see Section
* 5.1.2.2.4).
*/
if (retry && addr != 0x7e) {
writel(SVC_I3C_MERRWARN_NACK, master->regs + SVC_I3C_MERRWARN);
} else {
ret = -ENXIO; ret = -ENXIO;
*actual_len = 0; *actual_len = 0;
goto emit_stop; goto emit_stop;
} }
} else {
break;
}
}
/* /*
* According to I3C spec ver 1.1.1, 5.1.2.2.3 Consequence of Controller Starting a Frame * According to I3C spec ver 1.1.1, 5.1.2.2.3 Consequence of Controller Starting a Frame
...@@ -1321,7 +1376,7 @@ static int svc_i3c_master_send_direct_ccc_cmd(struct svc_i3c_master *master, ...@@ -1321,7 +1376,7 @@ static int svc_i3c_master_send_direct_ccc_cmd(struct svc_i3c_master *master,
cmd->addr = ccc->dests[0].addr; cmd->addr = ccc->dests[0].addr;
cmd->rnw = ccc->rnw; cmd->rnw = ccc->rnw;
cmd->in = ccc->rnw ? ccc->dests[0].payload.data : NULL; cmd->in = ccc->rnw ? ccc->dests[0].payload.data : NULL;
cmd->out = ccc->rnw ? NULL : ccc->dests[0].payload.data, cmd->out = ccc->rnw ? NULL : ccc->dests[0].payload.data;
cmd->len = xfer_len; cmd->len = xfer_len;
cmd->actual_len = actual_len; cmd->actual_len = actual_len;
cmd->continued = false; cmd->continued = false;
......
/* SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause */
/*
* Copyright 2024 NXP
*/
#ifndef _DT_BINDINGS_I3C_I3C_H
#define _DT_BINDINGS_I3C_I3C_H
#define I2C_FM (1 << 4)
#define I2C_FM_PLUS (0 << 4)
#define I2C_FILTER (0 << 5)
#define I2C_NO_FILTER_HIGH_FREQUENCY (1 << 5)
#define I2C_NO_FILTER_LOW_FREQUENCY (2 << 5)
#endif
...@@ -33,6 +33,7 @@ enum { ...@@ -33,6 +33,7 @@ enum {
struct i3c_master_controller; struct i3c_master_controller;
struct i3c_bus; struct i3c_bus;
struct i3c_device; struct i3c_device;
extern const struct bus_type i3c_bus_type;
/** /**
* struct i3c_i2c_dev_desc - Common part of the I3C/I2C device descriptor * struct i3c_i2c_dev_desc - Common part of the I3C/I2C device descriptor
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment