mirror of
git://git.yoctoproject.org/linux-yocto.git
synced 2026-01-27 12:47:24 +01:00
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:
commit
1df7752800
4
CREDITS
4
CREDITS
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -14,7 +14,9 @@ allOf:
|
|||
|
||||
properties:
|
||||
compatible:
|
||||
const: silvaco,i3c-master-v1
|
||||
enum:
|
||||
- nuvoton,npcm845-i3c
|
||||
- silvaco,i3c-master-v1
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
|
|
|||
|
|
@ -34,6 +34,9 @@ properties:
|
|||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user