Skip to content

Commit 4008a74

Browse files
stanleychuysalexandrebelloni
authored andcommitted
i3c: master: svc: Fix npcm845 FIFO empty issue
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. Fill the FIFO in advance to prevent FIFO from becoming empty. Reviewed-by: Frank Li <[email protected]> Signed-off-by: Stanley Chu <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Alexandre Belloni <[email protected]>
1 parent 98d8760 commit 4008a74

File tree

1 file changed

+61
-10
lines changed

1 file changed

+61
-10
lines changed

drivers/i3c/master/svc-i3c-master.c

Lines changed: 61 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@
113113
#define SVC_I3C_MWDATAHE 0x0BC
114114
#define SVC_I3C_MRDATAB 0x0C0
115115
#define SVC_I3C_MRDATAH 0x0C8
116+
#define SVC_I3C_MWDATAB1 0x0CC
116117
#define SVC_I3C_MWMSG_SDR 0x0D0
117118
#define SVC_I3C_MRMSG_SDR 0x0D4
118119
#define SVC_I3C_MWMSG_DDR 0x0D8
@@ -133,6 +134,16 @@
133134
#define SVC_I3C_EVENT_IBI GENMASK(7, 0)
134135
#define SVC_I3C_EVENT_HOTJOIN BIT(31)
135136

137+
/*
138+
* SVC_I3C_QUIRK_FIFO_EMPTY:
139+
* I3C HW stalls the write transfer if the transmit FIFO becomes empty,
140+
* when new data is written to FIFO, I3C HW resumes the transfer but
141+
* the first transmitted data bit may have the wrong value.
142+
* Workaround:
143+
* Fill the FIFO in advance to prevent FIFO from becoming empty.
144+
*/
145+
#define SVC_I3C_QUIRK_FIFO_EMPTY BIT(0)
146+
136147
struct svc_i3c_cmd {
137148
u8 addr;
138149
bool rnw;
@@ -236,6 +247,11 @@ struct svc_i3c_i2c_dev_data {
236247
struct i3c_generic_ibi_pool *ibi_pool;
237248
};
238249

250+
static inline bool svc_has_quirk(struct svc_i3c_master *master, u32 quirk)
251+
{
252+
return (master->drvdata->quirks & quirk);
253+
}
254+
239255
static inline bool is_events_enabled(struct svc_i3c_master *master, u32 mask)
240256
{
241257
return !!(master->enabled_events & mask);
@@ -894,7 +910,7 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master,
894910
u8 *addrs, unsigned int *count)
895911
{
896912
u64 prov_id[SVC_I3C_MAX_DEVS] = {}, nacking_prov_id = 0;
897-
unsigned int dev_nb = 0, last_addr = 0;
913+
unsigned int dev_nb = 0, last_addr = 0, dyn_addr;
898914
u32 reg;
899915
int ret, i;
900916

@@ -939,6 +955,25 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master,
939955
if (SVC_I3C_MSTATUS_RXPEND(reg)) {
940956
u8 data[6];
941957

958+
/*
959+
* One slave sends its ID to request for address assignment,
960+
* prefilling the dynamic address can reduce SCL clock stalls
961+
* and also fix the SVC_I3C_QUIRK_FIFO_EMPTY quirk.
962+
*
963+
* Ideally, prefilling before the processDAA command is better.
964+
* However, it requires an additional check to write the dyn_addr
965+
* at the right time because the driver needs to write the processDAA
966+
* command twice for one assignment.
967+
* Prefilling here is safe and efficient because the FIFO starts
968+
* filling within a few hundred nanoseconds, which is significantly
969+
* faster compared to the 64 SCL clock cycles.
970+
*/
971+
dyn_addr = i3c_master_get_free_addr(&master->base, last_addr + 1);
972+
if (dyn_addr < 0)
973+
return -ENOSPC;
974+
975+
writel(dyn_addr, master->regs + SVC_I3C_MWDATAB);
976+
942977
/*
943978
* We only care about the 48-bit provisioned ID yet to
944979
* be sure a device does not nack an address twice.
@@ -1017,21 +1052,16 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master,
10171052
if (ret)
10181053
break;
10191054

1020-
/* Give the slave device a suitable dynamic address */
1021-
ret = i3c_master_get_free_addr(&master->base, last_addr + 1);
1022-
if (ret < 0)
1023-
break;
1024-
1025-
addrs[dev_nb] = ret;
1055+
addrs[dev_nb] = dyn_addr;
10261056
dev_dbg(master->dev, "DAA: device %d assigned to 0x%02x\n",
10271057
dev_nb, addrs[dev_nb]);
1028-
1029-
writel(addrs[dev_nb], master->regs + SVC_I3C_MWDATAB);
10301058
last_addr = addrs[dev_nb++];
10311059
}
10321060

10331061
/* Need manual issue STOP except for Complete condition */
10341062
svc_i3c_master_emit_stop(master);
1063+
svc_i3c_master_flush_fifo(master);
1064+
10351065
return ret;
10361066
}
10371067

@@ -1228,6 +1258,24 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
12281258
SVC_I3C_MCTRL_RDTERM(*actual_len),
12291259
master->regs + SVC_I3C_MCTRL);
12301260

1261+
/*
1262+
* The entire transaction can consist of multiple write transfers.
1263+
* Prefilling before EmitStartAddr causes the data to be emitted
1264+
* immediately, becoming part of the previous transfer.
1265+
* The only way to work around this hardware issue is to let the
1266+
* FIFO start filling as soon as possible after EmitStartAddr.
1267+
*/
1268+
if (svc_has_quirk(master, SVC_I3C_QUIRK_FIFO_EMPTY) && !rnw && xfer_len) {
1269+
u32 end = xfer_len > SVC_I3C_FIFO_SIZE ? 0 : SVC_I3C_MWDATAB_END;
1270+
u32 len = min_t(u32, xfer_len, SVC_I3C_FIFO_SIZE);
1271+
1272+
writesb(master->regs + SVC_I3C_MWDATAB1, out, len - 1);
1273+
/* Mark END bit if this is the last byte */
1274+
writel(out[len - 1] | end, master->regs + SVC_I3C_MWDATAB);
1275+
xfer_len -= len;
1276+
out += len;
1277+
}
1278+
12311279
ret = readl_poll_timeout(master->regs + SVC_I3C_MSTATUS, reg,
12321280
SVC_I3C_MSTATUS_MCTRLDONE(reg), 0, 1000);
12331281
if (ret)
@@ -1316,6 +1364,7 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
13161364
emit_stop:
13171365
svc_i3c_master_emit_stop(master);
13181366
svc_i3c_master_clear_merrwarn(master);
1367+
svc_i3c_master_flush_fifo(master);
13191368

13201369
return ret;
13211370
}
@@ -1970,7 +2019,9 @@ static const struct dev_pm_ops svc_i3c_pm_ops = {
19702019
svc_i3c_runtime_resume, NULL)
19712020
};
19722021

1973-
static const struct svc_i3c_drvdata npcm845_drvdata = {};
2022+
static const struct svc_i3c_drvdata npcm845_drvdata = {
2023+
.quirks = SVC_I3C_QUIRK_FIFO_EMPTY,
2024+
};
19742025

19752026
static const struct svc_i3c_drvdata svc_default_drvdata = {};
19762027

0 commit comments

Comments
 (0)