I3C for 6.15

Core:
  - Fix a possible NULL pointer dereference due to IBI coming when the target
    driver is not yet probed.
 
 Drivers:
  - mipi-i3c-hci: Use I2C DMA-safe api
  - svc: add Nuvoton npcm845 support
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEBqsFVZXh8s/0O5JiY6TcMGxwOjIFAmfsY9QACgkQY6TcMGxw
 OjKS1g//aSPDC6TjvHLlm2AZnbTmbf0Z5skABHeL1043g5Q5tutTw/+4NtB/qOEe
 +q8gLpdjU3Vm1npKKRX5t+ywWgNRV9Sq7FaBMDz2USAPMTMKjKCEatx/6p2/zaai
 DX6ZHoVuVin35GcK3xszQ/SdY2saH+J0fFoX2ab7taLjbNt10GOFGzRyM6GErmW5
 Zn9gOkD9SudYnjfnpfdHLMFA4MhTqhTKGEH1fZVRbahR9dc21xkf75+fWe2ggqmT
 O3H7Vdf8OFihMhESjZ9XX6HMf8o9gkmR/XJt430daA1++XwGDciTGDXD9LrYwgU5
 fJI36hN5W2kTgAwIQJE/fCock0WhMpnIOQEethX/9QVNNPqLyHGBPk4zXyAPmpan
 O0eQueMLZoMxL/q4iThMDWWQAO5U91YOCeQ1eFha1dVGu/njrav3H+t0yZTqUu9I
 BOhdnk7g3FehR/aRADjTeA7VJpS3LPFVznpXKF1sx6OfqGPdeTJi8+5uRibdVfVS
 kRQG8DWuvpkvSeIOHCWHW/N6G0FML/hNKGqXFmVZsMrzwG7A5oCZ6irJiRorKc94
 aO3Q4rZvR2tFoEtDgHQAOMCfMos/KbtJZ+d7bPQr+aCyA67tbC+eXD5/K6+kgVc+
 l9ZIqMeWQfH8omg6drrgfjqCcDAly2Yga7I4NoWTLdYo2VjPWXA=
 =pT5I
 -----END PGP SIGNATURE-----

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

Pull i3c updates from Alexandre Belloni:
 "The silvaco driver gets support for the integration of the IP in the
  Nuvoton npcm845 SoC. There is also a fix for a possible NULL pointer
  dereference that can happen with early IBIs. Summary:

  Core:

    - Fix a possible NULL pointer dereference due to IBI coming when the
      target driver is not yet probed.

  Drivers:

    - mipi-i3c-hci: Use I2C DMA-safe api

    - svc: add Nuvoton npcm845 support"

* tag 'i3c/for-6.15' of git://git.kernel.org/pub/scm/linux/kernel/git/i3c/linux:
  i3c: Add NULL pointer check in i3c_master_queue_ibi()
  i3c: master: Drop duplicate check before calling OF APIs
  i3c: master: svc: Fix implicit fallthrough in svc_i3c_master_ibi_work()
  i3c: master: svc: Fix missing STOP for master request
  i3c: master: svc: Use readsb helper for reading MDB
  i3c: master: svc: Fix missing the IBI rules
  i3c: master: svc: Fix i3c_master_get_free_addr return check
  i3c: master: svc: Fix npcm845 DAA process corruption
  i3c: master: svc: Fix npcm845 invalid slvstart event
  i3c: master: svc: Fix npcm845 FIFO empty issue
  i3c: master: svc: Add support for Nuvoton npcm845 i3c
  dt-bindings: i3c: silvaco: Add npcm845 compatible string
  dt-bindings: i3c: dw: Add power-domains
  i3c: master: svc: Flush FIFO before sending Dynamic Address Assignment(DAA)
  i3c: mipi-i3c-hci: Use I2C DMA-safe api
  i3c: Remove the const qualifier from i2c_msg pointer in i2c_xfers API
  MAINTAINERS: Add Frank Li to Silvaco I3C
  MAINTAINERS: Remove Conor Culhane from Silvaco I3C
This commit is contained in:
Linus Torvalds 2025-04-01 16:47:47 -07:00
commit 1df7752800
10 changed files with 154 additions and 36 deletions

View File

@ -855,6 +855,10 @@ N: John Crispin
E: john@phrozen.org
D: MediaTek MT7623 Gigabit ethernet support
N: Conor Culhane
E: conor.culhane@silvaco.com
D: Silvaco I3C master driver
N: Laurence Culhane
E: loz@holmes.demon.co.uk
D: Wrote the initial alpha SLIP code

View File

@ -14,7 +14,9 @@ allOf:
properties:
compatible:
const: silvaco,i3c-master-v1
enum:
- nuvoton,npcm845-i3c
- silvaco,i3c-master-v1
reg:
maxItems: 1

View File

@ -34,6 +34,9 @@ properties:
interrupts:
maxItems: 1
power-domains:
maxItems: 1
required:
- compatible
- reg

View File

@ -11265,6 +11265,7 @@ F: drivers/i3c/master/dw*
I3C SUBSYSTEM
M: Alexandre Belloni <alexandre.belloni@bootlin.com>
R: Frank Li <Frank.Li@nxp.com>
L: linux-i3c@lists.infradead.org (moderated for non-subscribers)
S: Maintained
C: irc://chat.freenode.net/linux-i3c
@ -22106,8 +22107,9 @@ F: drivers/video/fbdev/sm712*
SILVACO I3C DUAL-ROLE MASTER
M: Miquel Raynal <miquel.raynal@bootlin.com>
M: Conor Culhane <conor.culhane@silvaco.com>
M: Frank Li <Frank.Li@nxp.com>
L: linux-i3c@lists.infradead.org (moderated for non-subscribers)
L: imx@lists.linux.dev
S: Maintained
F: Documentation/devicetree/bindings/i3c/silvaco,i3c-master.yaml
F: drivers/i3c/master/svc-i3c-master.c

View File

@ -2276,7 +2276,7 @@ static int of_i3c_master_add_dev(struct i3c_master_controller *master,
u32 reg[3];
int ret;
if (!master || !node)
if (!master)
return -EINVAL;
ret = of_property_read_u32_array(node, "reg", reg, ARRAY_SIZE(reg));
@ -2369,14 +2369,10 @@ static u8 i3c_master_i2c_get_lvr(struct i2c_client *client)
{
/* Fall back to no spike filters and FM bus mode. */
u8 lvr = I3C_LVR_I2C_INDEX(2) | I3C_LVR_I2C_FM_MODE;
u32 reg[3];
if (client->dev.of_node) {
u32 reg[3];
if (!of_property_read_u32_array(client->dev.of_node, "reg",
reg, ARRAY_SIZE(reg)))
lvr = reg[2];
}
if (!of_property_read_u32_array(client->dev.of_node, "reg", reg, ARRAY_SIZE(reg)))
lvr = reg[2];
return lvr;
}
@ -2486,7 +2482,7 @@ static int i3c_master_i2c_adapter_init(struct i3c_master_controller *master)
struct i2c_adapter *adap = i3c_master_to_i2c_adapter(master);
struct i2c_dev_desc *i2cdev;
struct i2c_dev_boardinfo *i2cboardinfo;
int ret, id = -ENODEV;
int ret, id;
adap->dev.parent = master->dev.parent;
adap->owner = master->dev.parent->driver->owner;
@ -2497,9 +2493,7 @@ static int i3c_master_i2c_adapter_init(struct i3c_master_controller *master)
adap->timeout = 1000;
adap->retries = 3;
if (master->dev.of_node)
id = of_alias_get_id(master->dev.of_node, "i2c");
id = of_alias_get_id(master->dev.of_node, "i2c");
if (id >= 0) {
adap->nr = id;
ret = i2c_add_numbered_adapter(adap);
@ -2561,6 +2555,9 @@ 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)
{
if (!dev->ibi || !slot)
return;
atomic_inc(&dev->ibi->pending_ibis);
queue_work(dev->ibi->wq, &slot->work);
}

View File

@ -1079,7 +1079,7 @@ static void dw_i3c_master_detach_i3c_dev(struct i3c_dev_desc *dev)
}
static int dw_i3c_master_i2c_xfers(struct i2c_dev_desc *dev,
const struct i2c_msg *i2c_xfers,
struct i2c_msg *i2c_xfers,
int i2c_nxfers)
{
struct dw_i3c_i2c_dev_data *data = i2c_dev_get_master_data(dev);

View File

@ -813,7 +813,7 @@ static int cdns_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
}
static int cdns_i3c_master_i2c_xfers(struct i2c_dev_desc *dev,
const struct i2c_msg *xfers, int nxfers)
struct i2c_msg *xfers, int nxfers)
{
struct i3c_master_controller *m = i2c_dev_get_master(dev);
struct cdns_i3c_master *master = to_cdns_i3c_master(m);

View File

@ -367,7 +367,7 @@ out:
}
static int i3c_hci_i2c_xfers(struct i2c_dev_desc *dev,
const struct i2c_msg *i2c_xfers, int nxfers)
struct i2c_msg *i2c_xfers, int nxfers)
{
struct i3c_master_controller *m = i2c_dev_get_master(dev);
struct i3c_hci *hci = to_i3c_hci(m);
@ -382,14 +382,11 @@ static int i3c_hci_i2c_xfers(struct i2c_dev_desc *dev,
return -ENOMEM;
for (i = 0; i < nxfers; i++) {
xfer[i].data = i2c_xfers[i].buf;
xfer[i].data = i2c_get_dma_safe_msg_buf(&i2c_xfers[i], 1);
xfer[i].data_len = i2c_xfers[i].len;
xfer[i].rnw = i2c_xfers[i].flags & I2C_M_RD;
hci->cmd->prep_i2c_xfer(hci, dev, &xfer[i]);
xfer[i].cmd_desc[0] |= CMD_0_ROC;
ret = i3c_hci_alloc_safe_xfer_buf(hci, &xfer[i]);
if (ret)
goto out;
}
last = i - 1;
xfer[last].cmd_desc[0] |= CMD_0_TOC;
@ -412,7 +409,8 @@ static int i3c_hci_i2c_xfers(struct i2c_dev_desc *dev,
out:
for (i = 0; i < nxfers; i++)
i3c_hci_free_safe_xfer_buf(hci, &xfer[i]);
i2c_put_dma_safe_msg_buf(xfer[i].data, &i2c_xfers[i],
ret ? false : true);
hci_free_xfer(xfer, nxfers);
return ret;

View File

@ -32,6 +32,7 @@
#define SVC_I3C_MCONFIG_ODBAUD(x) FIELD_PREP(GENMASK(23, 16), (x))
#define SVC_I3C_MCONFIG_ODHPP(x) FIELD_PREP(BIT(24), (x))
#define SVC_I3C_MCONFIG_SKEW(x) FIELD_PREP(GENMASK(27, 25), (x))
#define SVC_I3C_MCONFIG_SKEW_MASK GENMASK(27, 25)
#define SVC_I3C_MCONFIG_I2CBAUD(x) FIELD_PREP(GENMASK(31, 28), (x))
#define SVC_I3C_MCTRL 0x084
@ -58,6 +59,7 @@
#define SVC_I3C_MSTATUS 0x088
#define SVC_I3C_MSTATUS_STATE(x) FIELD_GET(GENMASK(2, 0), (x))
#define SVC_I3C_MSTATUS_STATE_DAA(x) (SVC_I3C_MSTATUS_STATE(x) == 5)
#define SVC_I3C_MSTATUS_STATE_SLVREQ(x) (SVC_I3C_MSTATUS_STATE(x) == 1)
#define SVC_I3C_MSTATUS_STATE_IDLE(x) (SVC_I3C_MSTATUS_STATE(x) == 0)
#define SVC_I3C_MSTATUS_BETWEEN(x) FIELD_GET(BIT(4), (x))
#define SVC_I3C_MSTATUS_NACKED(x) FIELD_GET(BIT(5), (x))
@ -113,6 +115,7 @@
#define SVC_I3C_MWDATAHE 0x0BC
#define SVC_I3C_MRDATAB 0x0C0
#define SVC_I3C_MRDATAH 0x0C8
#define SVC_I3C_MWDATAB1 0x0CC
#define SVC_I3C_MWMSG_SDR 0x0D0
#define SVC_I3C_MRMSG_SDR 0x0D4
#define SVC_I3C_MWMSG_DDR 0x0D8
@ -133,6 +136,32 @@
#define SVC_I3C_EVENT_IBI GENMASK(7, 0)
#define SVC_I3C_EVENT_HOTJOIN BIT(31)
/*
* SVC_I3C_QUIRK_FIFO_EMPTY:
* I3C HW stalls the write transfer if the transmit FIFO becomes empty,
* when new data is written to FIFO, I3C HW resumes the transfer but
* the first transmitted data bit may have the wrong value.
* Workaround:
* Fill the FIFO in advance to prevent FIFO from becoming empty.
*/
#define SVC_I3C_QUIRK_FIFO_EMPTY BIT(0)
/*
* SVC_I3C_QUIRK_FLASE_SLVSTART:
* I3C HW may generate an invalid SlvStart event when emitting a STOP.
* If it is a true SlvStart, the MSTATUS state is SLVREQ.
*/
#define SVC_I3C_QUIRK_FALSE_SLVSTART BIT(1)
/*
* SVC_I3C_QUIRK_DAA_CORRUPT:
* When MCONFIG.SKEW=0 and MCONFIG.ODHPP=0, the ENTDAA transaction gets
* corrupted and results in a no repeated-start condition at the end of
* address assignment.
* Workaround:
* Set MCONFIG.SKEW to 1 before initiating the DAA process. After the DAA
* process is completed, return MCONFIG.SKEW to its previous value.
*/
#define SVC_I3C_QUIRK_DAA_CORRUPT BIT(2)
struct svc_i3c_cmd {
u8 addr;
bool rnw;
@ -158,6 +187,10 @@ struct svc_i3c_regs_save {
u32 mdynaddr;
};
struct svc_i3c_drvdata {
u32 quirks;
};
/**
* struct svc_i3c_master - Silvaco I3C Master structure
* @base: I3C master controller
@ -183,6 +216,7 @@ struct svc_i3c_regs_save {
* @ibi.tbq_slot: To be queued IBI slot
* @ibi.lock: IBI lock
* @lock: Transfer lock, protect between IBI work thread and callbacks from master
* @drvdata: Driver data
* @enabled_events: Bit masks for enable events (IBI, HotJoin).
* @mctrl_config: Configuration value in SVC_I3C_MCTRL for setting speed back.
*/
@ -214,6 +248,7 @@ struct svc_i3c_master {
spinlock_t lock;
} ibi;
struct mutex lock;
const struct svc_i3c_drvdata *drvdata;
u32 enabled_events;
u32 mctrl_config;
};
@ -230,6 +265,18 @@ struct svc_i3c_i2c_dev_data {
struct i3c_generic_ibi_pool *ibi_pool;
};
static inline bool svc_has_quirk(struct svc_i3c_master *master, u32 quirk)
{
return (master->drvdata->quirks & quirk);
}
static inline bool svc_has_daa_corrupt(struct svc_i3c_master *master)
{
return ((master->drvdata->quirks & SVC_I3C_QUIRK_DAA_CORRUPT) &&
!(master->mctrl_config &
(SVC_I3C_MCONFIG_SKEW_MASK | SVC_I3C_MCONFIG_ODHPP(1))));
}
static inline bool is_events_enabled(struct svc_i3c_master *master, u32 mask)
{
return !!(master->enabled_events & mask);
@ -378,7 +425,7 @@ static int svc_i3c_master_handle_ibi(struct svc_i3c_master *master,
slot->len < SVC_I3C_FIFO_SIZE) {
mdatactrl = readl(master->regs + SVC_I3C_MDATACTRL);
count = SVC_I3C_MDATACTRL_RXCOUNT(mdatactrl);
readsl(master->regs + SVC_I3C_MRDATAB, buf, count);
readsb(master->regs + SVC_I3C_MRDATAB, buf, count);
slot->len += count;
buf += count;
}
@ -545,6 +592,8 @@ static void svc_i3c_master_ibi_work(struct work_struct *work)
queue_work(master->base.wq, &master->hj_work);
break;
case SVC_I3C_MSTATUS_IBITYPE_MASTER_REQUEST:
svc_i3c_master_emit_stop(master);
break;
default:
break;
}
@ -564,6 +613,11 @@ static irqreturn_t svc_i3c_master_irq_handler(int irq, void *dev_id)
/* Clear the interrupt status */
writel(SVC_I3C_MINT_SLVSTART, master->regs + SVC_I3C_MSTATUS);
/* Ignore the false event */
if (svc_has_quirk(master, SVC_I3C_QUIRK_FALSE_SLVSTART) &&
!SVC_I3C_MSTATUS_STATE_SLVREQ(active))
return IRQ_HANDLED;
svc_i3c_master_disable_interrupts(master);
/* Handle the interrupt in a non atomic context */
@ -888,10 +942,12 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master,
u8 *addrs, unsigned int *count)
{
u64 prov_id[SVC_I3C_MAX_DEVS] = {}, nacking_prov_id = 0;
unsigned int dev_nb = 0, last_addr = 0;
unsigned int dev_nb = 0, last_addr = 0, dyn_addr = 0;
u32 reg;
int ret, i;
svc_i3c_master_flush_fifo(master);
while (true) {
/* clean SVC_I3C_MINT_IBIWON w1c bits */
writel(SVC_I3C_MINT_IBIWON, master->regs + SVC_I3C_MSTATUS);
@ -931,6 +987,26 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master,
if (SVC_I3C_MSTATUS_RXPEND(reg)) {
u8 data[6];
/*
* One slave sends its ID to request for address assignment,
* prefilling the dynamic address can reduce SCL clock stalls
* and also fix the SVC_I3C_QUIRK_FIFO_EMPTY quirk.
*
* Ideally, prefilling before the processDAA command is better.
* However, it requires an additional check to write the dyn_addr
* at the right time because the driver needs to write the processDAA
* command twice for one assignment.
* Prefilling here is safe and efficient because the FIFO starts
* filling within a few hundred nanoseconds, which is significantly
* faster compared to the 64 SCL clock cycles.
*/
ret = i3c_master_get_free_addr(&master->base, last_addr + 1);
if (ret < 0)
break;
dyn_addr = ret;
writel(dyn_addr, master->regs + SVC_I3C_MWDATAB);
/*
* We only care about the 48-bit provisioned ID yet to
* be sure a device does not nack an address twice.
@ -1009,21 +1085,16 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master,
if (ret)
break;
/* Give the slave device a suitable dynamic address */
ret = i3c_master_get_free_addr(&master->base, last_addr + 1);
if (ret < 0)
break;
addrs[dev_nb] = ret;
addrs[dev_nb] = dyn_addr;
dev_dbg(master->dev, "DAA: device %d assigned to 0x%02x\n",
dev_nb, addrs[dev_nb]);
writel(addrs[dev_nb], master->regs + SVC_I3C_MWDATAB);
last_addr = addrs[dev_nb++];
}
/* Need manual issue STOP except for Complete condition */
svc_i3c_master_emit_stop(master);
svc_i3c_master_flush_fifo(master);
return ret;
}
@ -1037,7 +1108,7 @@ static int svc_i3c_update_ibirules(struct svc_i3c_master *master)
/* Create the IBIRULES register for both cases */
i3c_bus_for_each_i3cdev(&master->base.bus, dev) {
if (I3C_BCR_DEVICE_ROLE(dev->info.bcr) == I3C_BCR_I3C_MASTER)
if (!(dev->info.bcr & I3C_BCR_IBI_REQ_CAP))
continue;
if (dev->info.bcr & I3C_BCR_IBI_PAYLOAD) {
@ -1096,7 +1167,16 @@ static int svc_i3c_master_do_daa(struct i3c_master_controller *m)
}
spin_lock_irqsave(&master->xferqueue.lock, flags);
if (svc_has_daa_corrupt(master))
writel(master->mctrl_config | SVC_I3C_MCONFIG_SKEW(1),
master->regs + SVC_I3C_MCONFIG);
ret = svc_i3c_master_do_daa_locked(master, addrs, &dev_nb);
if (svc_has_daa_corrupt(master))
writel(master->mctrl_config, master->regs + SVC_I3C_MCONFIG);
spin_unlock_irqrestore(&master->xferqueue.lock, flags);
svc_i3c_master_clear_merrwarn(master);
@ -1220,6 +1300,24 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
SVC_I3C_MCTRL_RDTERM(*actual_len),
master->regs + SVC_I3C_MCTRL);
/*
* The entire transaction can consist of multiple write transfers.
* Prefilling before EmitStartAddr causes the data to be emitted
* immediately, becoming part of the previous transfer.
* The only way to work around this hardware issue is to let the
* FIFO start filling as soon as possible after EmitStartAddr.
*/
if (svc_has_quirk(master, SVC_I3C_QUIRK_FIFO_EMPTY) && !rnw && xfer_len) {
u32 end = xfer_len > SVC_I3C_FIFO_SIZE ? 0 : SVC_I3C_MWDATAB_END;
u32 len = min_t(u32, xfer_len, SVC_I3C_FIFO_SIZE);
writesb(master->regs + SVC_I3C_MWDATAB1, out, len - 1);
/* Mark END bit if this is the last byte */
writel(out[len - 1] | end, master->regs + SVC_I3C_MWDATAB);
xfer_len -= len;
out += len;
}
ret = readl_poll_timeout(master->regs + SVC_I3C_MSTATUS, reg,
SVC_I3C_MSTATUS_MCTRLDONE(reg), 0, 1000);
if (ret)
@ -1308,6 +1406,7 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
emit_stop:
svc_i3c_master_emit_stop(master);
svc_i3c_master_clear_merrwarn(master);
svc_i3c_master_flush_fifo(master);
return ret;
}
@ -1584,7 +1683,7 @@ static int svc_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
}
static int svc_i3c_master_i2c_xfers(struct i2c_dev_desc *dev,
const struct i2c_msg *xfers,
struct i2c_msg *xfers,
int nxfers)
{
struct i3c_master_controller *m = i2c_dev_get_master(dev);
@ -1817,6 +1916,10 @@ static int svc_i3c_master_probe(struct platform_device *pdev)
if (!master)
return -ENOMEM;
master->drvdata = of_device_get_match_data(dev);
if (!master->drvdata)
return -EINVAL;
master->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(master->regs))
return PTR_ERR(master->regs);
@ -1958,8 +2061,17 @@ static const struct dev_pm_ops svc_i3c_pm_ops = {
svc_i3c_runtime_resume, NULL)
};
static const struct svc_i3c_drvdata npcm845_drvdata = {
.quirks = SVC_I3C_QUIRK_FIFO_EMPTY |
SVC_I3C_QUIRK_FALSE_SLVSTART |
SVC_I3C_QUIRK_DAA_CORRUPT,
};
static const struct svc_i3c_drvdata svc_default_drvdata = {};
static const struct of_device_id svc_i3c_master_of_match_tbl[] = {
{ .compatible = "silvaco,i3c-master-v1"},
{ .compatible = "nuvoton,npcm845-i3c", .data = &npcm845_drvdata },
{ .compatible = "silvaco,i3c-master-v1", .data = &svc_default_drvdata },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, svc_i3c_master_of_match_tbl);

View File

@ -475,7 +475,7 @@ struct i3c_master_controller_ops {
int (*attach_i2c_dev)(struct i2c_dev_desc *dev);
void (*detach_i2c_dev)(struct i2c_dev_desc *dev);
int (*i2c_xfers)(struct i2c_dev_desc *dev,
const struct i2c_msg *xfers, int nxfers);
struct i2c_msg *xfers, int nxfers);
int (*request_ibi)(struct i3c_dev_desc *dev,
const struct i3c_ibi_setup *req);
void (*free_ibi)(struct i3c_dev_desc *dev);