Skip to content

Commit e389b1d

Browse files
jk-ozlabsalexandrebelloni
authored andcommitted
i3c: dw: Add support for in-band interrupts
This change adds support for receiving and dequeueing i3c IBIs. By setting struct dw_i3c_master->ibi_capable before probe, a platform implementation can select the IBI-enabled version of the i3c_master_ops, enabling the global IBI infrastrcture for that controller. Signed-off-by: Jeremy Kerr <jk@codeconstruct.com.au> Reviewed-by: Joel Stanley <joel@jms.id.au> Link: https://lore.kernel.org/r/79daeefd7ccb7c935d0c159149df21a6c9a73ffa.1680161823.git.jk@codeconstruct.com.au Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
1 parent e2d4310 commit e389b1d

2 files changed

Lines changed: 289 additions & 3 deletions

File tree

drivers/i3c/master/dw-i3c-master.c

Lines changed: 278 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,22 @@
7676

7777
#define RX_TX_DATA_PORT 0x14
7878
#define IBI_QUEUE_STATUS 0x18
79+
#define IBI_QUEUE_STATUS_IBI_ID(x) (((x) & GENMASK(15, 8)) >> 8)
80+
#define IBI_QUEUE_STATUS_DATA_LEN(x) ((x) & GENMASK(7, 0))
81+
#define IBI_QUEUE_IBI_ADDR(x) (IBI_QUEUE_STATUS_IBI_ID(x) >> 1)
82+
#define IBI_QUEUE_IBI_RNW(x) (IBI_QUEUE_STATUS_IBI_ID(x) & BIT(0))
83+
#define IBI_TYPE_MR(x) \
84+
((IBI_QUEUE_IBI_ADDR(x) != I3C_HOT_JOIN_ADDR) && !IBI_QUEUE_IBI_RNW(x))
85+
#define IBI_TYPE_HJ(x) \
86+
((IBI_QUEUE_IBI_ADDR(x) == I3C_HOT_JOIN_ADDR) && !IBI_QUEUE_IBI_RNW(x))
87+
#define IBI_TYPE_SIRQ(x) \
88+
((IBI_QUEUE_IBI_ADDR(x) != I3C_HOT_JOIN_ADDR) && IBI_QUEUE_IBI_RNW(x))
89+
7990
#define QUEUE_THLD_CTRL 0x1c
91+
#define QUEUE_THLD_CTRL_IBI_STAT_MASK GENMASK(31, 24)
92+
#define QUEUE_THLD_CTRL_IBI_STAT(x) (((x) - 1) << 24)
93+
#define QUEUE_THLD_CTRL_IBI_DATA_MASK GENMASK(20, 16)
94+
#define QUEUE_THLD_CTRL_IBI_DATA(x) ((x) << 16)
8095
#define QUEUE_THLD_CTRL_RESP_BUF_MASK GENMASK(15, 8)
8196
#define QUEUE_THLD_CTRL_RESP_BUF(x) (((x) - 1) << 8)
8297

@@ -186,6 +201,8 @@
186201
#define EXTENDED_CAPABILITY 0xe8
187202
#define SLAVE_CONFIG 0xec
188203

204+
#define DEV_ADDR_TABLE_IBI_MDB BIT(12)
205+
#define DEV_ADDR_TABLE_SIR_REJECT BIT(13)
189206
#define DEV_ADDR_TABLE_LEGACY_I2C_DEV BIT(31)
190207
#define DEV_ADDR_TABLE_DYNAMIC_ADDR(x) (((x) << 16) & GENMASK(23, 16))
191208
#define DEV_ADDR_TABLE_STATIC_ADDR(x) ((x) & GENMASK(6, 0))
@@ -221,6 +238,7 @@ struct dw_i3c_xfer {
221238

222239
struct dw_i3c_i2c_dev_data {
223240
u8 index;
241+
struct i3c_generic_ibi_pool *ibi_pool;
224242
};
225243

226244
static u8 even_parity(u8 p)
@@ -336,6 +354,12 @@ static void dw_i3c_master_read_rx_fifo(struct dw_i3c_master *master,
336354
return dw_i3c_master_read_fifo(master, RX_TX_DATA_PORT, bytes, nbytes);
337355
}
338356

357+
static void dw_i3c_master_read_ibi_fifo(struct dw_i3c_master *master,
358+
u8 *bytes, int nbytes)
359+
{
360+
return dw_i3c_master_read_fifo(master, IBI_QUEUE_STATUS, bytes, nbytes);
361+
}
362+
339363
static struct dw_i3c_xfer *
340364
dw_i3c_master_alloc_xfer(struct dw_i3c_master *master, unsigned int ncmds)
341365
{
@@ -605,7 +629,11 @@ static int dw_i3c_master_bus_init(struct i3c_master_controller *m)
605629
}
606630

607631
thld_ctrl = readl(master->regs + QUEUE_THLD_CTRL);
608-
thld_ctrl &= ~QUEUE_THLD_CTRL_RESP_BUF_MASK;
632+
thld_ctrl &= ~(QUEUE_THLD_CTRL_RESP_BUF_MASK |
633+
QUEUE_THLD_CTRL_IBI_STAT_MASK |
634+
QUEUE_THLD_CTRL_IBI_STAT_MASK);
635+
thld_ctrl |= QUEUE_THLD_CTRL_IBI_STAT(1) |
636+
QUEUE_THLD_CTRL_IBI_DATA(31);
609637
writel(thld_ctrl, master->regs + QUEUE_THLD_CTRL);
610638

611639
thld_ctrl = readl(master->regs + DATA_BUFFER_THLD_CTRL);
@@ -1074,6 +1102,226 @@ static void dw_i3c_master_detach_i2c_dev(struct i2c_dev_desc *dev)
10741102
kfree(data);
10751103
}
10761104

1105+
static int dw_i3c_master_request_ibi(struct i3c_dev_desc *dev,
1106+
const struct i3c_ibi_setup *req)
1107+
{
1108+
struct dw_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev);
1109+
struct i3c_master_controller *m = i3c_dev_get_master(dev);
1110+
struct dw_i3c_master *master = to_dw_i3c_master(m);
1111+
unsigned long flags;
1112+
1113+
data->ibi_pool = i3c_generic_ibi_alloc_pool(dev, req);
1114+
if (IS_ERR(data->ibi_pool))
1115+
return PTR_ERR(data->ibi_pool);
1116+
1117+
spin_lock_irqsave(&master->devs_lock, flags);
1118+
master->devs[data->index].ibi_dev = dev;
1119+
spin_unlock_irqrestore(&master->devs_lock, flags);
1120+
1121+
return 0;
1122+
}
1123+
1124+
static void dw_i3c_master_free_ibi(struct i3c_dev_desc *dev)
1125+
{
1126+
struct dw_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev);
1127+
struct i3c_master_controller *m = i3c_dev_get_master(dev);
1128+
struct dw_i3c_master *master = to_dw_i3c_master(m);
1129+
unsigned long flags;
1130+
1131+
spin_lock_irqsave(&master->devs_lock, flags);
1132+
master->devs[data->index].ibi_dev = NULL;
1133+
spin_unlock_irqrestore(&master->devs_lock, flags);
1134+
1135+
i3c_generic_ibi_free_pool(data->ibi_pool);
1136+
data->ibi_pool = NULL;
1137+
}
1138+
1139+
static void dw_i3c_master_set_sir_enabled(struct dw_i3c_master *master,
1140+
struct i3c_dev_desc *dev,
1141+
u8 idx, bool enable)
1142+
{
1143+
unsigned long flags;
1144+
u32 dat_entry, reg;
1145+
bool global;
1146+
1147+
dat_entry = DEV_ADDR_TABLE_LOC(master->datstartaddr, idx);
1148+
1149+
spin_lock_irqsave(&master->devs_lock, flags);
1150+
reg = readl(master->regs + dat_entry);
1151+
if (enable) {
1152+
reg &= ~DEV_ADDR_TABLE_SIR_REJECT;
1153+
if (dev->info.bcr & I3C_BCR_IBI_PAYLOAD)
1154+
reg |= DEV_ADDR_TABLE_IBI_MDB;
1155+
} else {
1156+
reg |= DEV_ADDR_TABLE_SIR_REJECT;
1157+
}
1158+
writel(reg, master->regs + dat_entry);
1159+
1160+
reg = readl(master->regs + IBI_SIR_REQ_REJECT);
1161+
if (enable) {
1162+
global = reg == 0xffffffff;
1163+
reg &= ~BIT(idx);
1164+
} else {
1165+
global = reg == 0;
1166+
reg |= BIT(idx);
1167+
}
1168+
writel(reg, master->regs + IBI_SIR_REQ_REJECT);
1169+
1170+
if (global) {
1171+
reg = readl(master->regs + INTR_STATUS_EN);
1172+
reg &= ~INTR_IBI_THLD_STAT;
1173+
if (enable)
1174+
reg |= INTR_IBI_THLD_STAT;
1175+
writel(reg, master->regs + INTR_STATUS_EN);
1176+
1177+
reg = readl(master->regs + INTR_SIGNAL_EN);
1178+
reg &= ~INTR_IBI_THLD_STAT;
1179+
if (enable)
1180+
reg |= INTR_IBI_THLD_STAT;
1181+
writel(reg, master->regs + INTR_SIGNAL_EN);
1182+
}
1183+
1184+
spin_unlock_irqrestore(&master->devs_lock, flags);
1185+
}
1186+
1187+
static int dw_i3c_master_enable_ibi(struct i3c_dev_desc *dev)
1188+
{
1189+
struct dw_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev);
1190+
struct i3c_master_controller *m = i3c_dev_get_master(dev);
1191+
struct dw_i3c_master *master = to_dw_i3c_master(m);
1192+
int rc;
1193+
1194+
dw_i3c_master_set_sir_enabled(master, dev, data->index, true);
1195+
1196+
rc = i3c_master_enec_locked(m, dev->info.dyn_addr, I3C_CCC_EVENT_SIR);
1197+
1198+
if (rc)
1199+
dw_i3c_master_set_sir_enabled(master, dev, data->index, false);
1200+
1201+
return rc;
1202+
}
1203+
1204+
static int dw_i3c_master_disable_ibi(struct i3c_dev_desc *dev)
1205+
{
1206+
struct dw_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev);
1207+
struct i3c_master_controller *m = i3c_dev_get_master(dev);
1208+
struct dw_i3c_master *master = to_dw_i3c_master(m);
1209+
int rc;
1210+
1211+
rc = i3c_master_disec_locked(m, dev->info.dyn_addr, I3C_CCC_EVENT_SIR);
1212+
if (rc)
1213+
return rc;
1214+
1215+
dw_i3c_master_set_sir_enabled(master, dev, data->index, false);
1216+
1217+
return 0;
1218+
}
1219+
1220+
static void dw_i3c_master_recycle_ibi_slot(struct i3c_dev_desc *dev,
1221+
struct i3c_ibi_slot *slot)
1222+
{
1223+
struct dw_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev);
1224+
1225+
i3c_generic_ibi_recycle_slot(data->ibi_pool, slot);
1226+
}
1227+
1228+
static void dw_i3c_master_drain_ibi_queue(struct dw_i3c_master *master,
1229+
int len)
1230+
{
1231+
int i;
1232+
1233+
for (i = 0; i < DIV_ROUND_UP(len, 4); i++)
1234+
readl(master->regs + IBI_QUEUE_STATUS);
1235+
}
1236+
1237+
static void dw_i3c_master_handle_ibi_sir(struct dw_i3c_master *master,
1238+
u32 status)
1239+
{
1240+
struct dw_i3c_i2c_dev_data *data;
1241+
struct i3c_ibi_slot *slot;
1242+
struct i3c_dev_desc *dev;
1243+
unsigned long flags;
1244+
u8 addr, len;
1245+
int idx;
1246+
1247+
addr = IBI_QUEUE_IBI_ADDR(status);
1248+
len = IBI_QUEUE_STATUS_DATA_LEN(status);
1249+
1250+
spin_lock_irqsave(&master->devs_lock, flags);
1251+
idx = dw_i3c_master_get_addr_pos(master, addr);
1252+
if (idx < 0) {
1253+
dev_dbg_ratelimited(&master->base.dev,
1254+
"IBI from unknown addr 0x%x\n", addr);
1255+
goto err_drain;
1256+
}
1257+
1258+
dev = master->devs[idx].ibi_dev;
1259+
if (!dev || !dev->ibi) {
1260+
dev_dbg_ratelimited(&master->base.dev,
1261+
"IBI from non-requested dev idx %d\n", idx);
1262+
goto err_drain;
1263+
}
1264+
1265+
data = i3c_dev_get_master_data(dev);
1266+
slot = i3c_generic_ibi_get_free_slot(data->ibi_pool);
1267+
if (!slot) {
1268+
dev_dbg_ratelimited(&master->base.dev,
1269+
"No IBI slots available\n");
1270+
goto err_drain;
1271+
}
1272+
1273+
if (dev->ibi->max_payload_len < len) {
1274+
dev_dbg_ratelimited(&master->base.dev,
1275+
"IBI payload len %d greater than max %d\n",
1276+
len, dev->ibi->max_payload_len);
1277+
goto err_drain;
1278+
}
1279+
1280+
if (len) {
1281+
dw_i3c_master_read_ibi_fifo(master, slot->data, len);
1282+
slot->len = len;
1283+
}
1284+
i3c_master_queue_ibi(dev, slot);
1285+
1286+
spin_unlock_irqrestore(&master->devs_lock, flags);
1287+
1288+
return;
1289+
1290+
err_drain:
1291+
dw_i3c_master_drain_ibi_queue(master, len);
1292+
1293+
spin_unlock_irqrestore(&master->devs_lock, flags);
1294+
}
1295+
1296+
/* "ibis": referring to In-Band Interrupts, and not
1297+
* https://en.wikipedia.org/wiki/Australian_white_ibis. The latter should
1298+
* not be handled.
1299+
*/
1300+
static void dw_i3c_master_irq_handle_ibis(struct dw_i3c_master *master)
1301+
{
1302+
unsigned int i, len, n_ibis;
1303+
u32 reg;
1304+
1305+
reg = readl(master->regs + QUEUE_STATUS_LEVEL);
1306+
n_ibis = QUEUE_STATUS_IBI_STATUS_CNT(reg);
1307+
if (!n_ibis)
1308+
return;
1309+
1310+
for (i = 0; i < n_ibis; i++) {
1311+
reg = readl(master->regs + IBI_QUEUE_STATUS);
1312+
1313+
if (IBI_TYPE_SIRQ(reg)) {
1314+
dw_i3c_master_handle_ibi_sir(master, reg);
1315+
} else {
1316+
len = IBI_QUEUE_STATUS_DATA_LEN(reg);
1317+
dev_info(&master->base.dev,
1318+
"unsupported IBI type 0x%lx len %d\n",
1319+
IBI_QUEUE_STATUS_IBI_ID(reg), len);
1320+
dw_i3c_master_drain_ibi_queue(master, len);
1321+
}
1322+
}
1323+
}
1324+
10771325
static irqreturn_t dw_i3c_master_irq_handler(int irq, void *dev_id)
10781326
{
10791327
struct dw_i3c_master *master = dev_id;
@@ -1092,6 +1340,9 @@ static irqreturn_t dw_i3c_master_irq_handler(int irq, void *dev_id)
10921340
writel(INTR_TRANSFER_ERR_STAT, master->regs + INTR_STATUS);
10931341
spin_unlock(&master->xferqueue.lock);
10941342

1343+
if (status & INTR_IBI_THLD_STAT)
1344+
dw_i3c_master_irq_handle_ibis(master);
1345+
10951346
return IRQ_HANDLED;
10961347
}
10971348

@@ -1110,6 +1361,26 @@ static const struct i3c_master_controller_ops dw_mipi_i3c_ops = {
11101361
.i2c_xfers = dw_i3c_master_i2c_xfers,
11111362
};
11121363

1364+
static const struct i3c_master_controller_ops dw_mipi_i3c_ibi_ops = {
1365+
.bus_init = dw_i3c_master_bus_init,
1366+
.bus_cleanup = dw_i3c_master_bus_cleanup,
1367+
.attach_i3c_dev = dw_i3c_master_attach_i3c_dev,
1368+
.reattach_i3c_dev = dw_i3c_master_reattach_i3c_dev,
1369+
.detach_i3c_dev = dw_i3c_master_detach_i3c_dev,
1370+
.do_daa = dw_i3c_master_daa,
1371+
.supports_ccc_cmd = dw_i3c_master_supports_ccc_cmd,
1372+
.send_ccc_cmd = dw_i3c_master_send_ccc_cmd,
1373+
.priv_xfers = dw_i3c_master_priv_xfers,
1374+
.attach_i2c_dev = dw_i3c_master_attach_i2c_dev,
1375+
.detach_i2c_dev = dw_i3c_master_detach_i2c_dev,
1376+
.i2c_xfers = dw_i3c_master_i2c_xfers,
1377+
.request_ibi = dw_i3c_master_request_ibi,
1378+
.free_ibi = dw_i3c_master_free_ibi,
1379+
.enable_ibi = dw_i3c_master_enable_ibi,
1380+
.disable_ibi = dw_i3c_master_disable_ibi,
1381+
.recycle_ibi_slot = dw_i3c_master_recycle_ibi_slot,
1382+
};
1383+
11131384
/* default platform ops implementations */
11141385
static int dw_i3c_platform_init_nop(struct dw_i3c_master *i3c)
11151386
{
@@ -1123,6 +1394,7 @@ static const struct dw_i3c_platform_ops dw_i3c_platform_ops_default = {
11231394
int dw_i3c_common_probe(struct dw_i3c_master *master,
11241395
struct platform_device *pdev)
11251396
{
1397+
const struct i3c_master_controller_ops *ops;
11261398
int ret, irq;
11271399

11281400
if (!master->platform_ops)
@@ -1172,8 +1444,11 @@ int dw_i3c_common_probe(struct dw_i3c_master *master,
11721444
master->maxdevs = ret >> 16;
11731445
master->free_pos = GENMASK(master->maxdevs - 1, 0);
11741446

1175-
ret = i3c_master_register(&master->base, &pdev->dev,
1176-
&dw_mipi_i3c_ops, false);
1447+
ops = &dw_mipi_i3c_ops;
1448+
if (master->ibi_capable)
1449+
ops = &dw_mipi_i3c_ibi_ops;
1450+
1451+
ret = i3c_master_register(&master->base, &pdev->dev, ops, false);
11771452
if (ret)
11781453
goto err_assert_rst;
11791454

drivers/i3c/master/dw-i3c-master.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ struct dw_i3c_master_caps {
1919

2020
struct dw_i3c_dat_entry {
2121
u8 addr;
22+
struct i3c_dev_desc *ibi_dev;
2223
};
2324

2425
struct dw_i3c_master {
@@ -37,12 +38,22 @@ struct dw_i3c_master {
3738
struct clk *core_clk;
3839
char version[5];
3940
char type[5];
41+
bool ibi_capable;
4042

4143
/*
4244
* Per-device hardware data, used to manage the device address table
4345
* (DAT)
46+
*
47+
* Locking: the devs array may be referenced in IRQ context while
48+
* processing an IBI. However, IBIs (for a specific device, which
49+
* implies a specific DAT entry) can only happen while interrupts are
50+
* requested for that device, which is serialised against other
51+
* insertions/removals from the array by the global i3c infrastructure.
52+
* So, devs_lock protects against concurrent updates to devs->ibi_dev
53+
* between request_ibi/free_ibi and the IBI irq event.
4454
*/
4555
struct dw_i3c_dat_entry devs[DW_I3C_MAX_DEVS];
56+
spinlock_t devs_lock;
4657

4758
/* platform-specific data */
4859
const struct dw_i3c_platform_ops *platform_ops;

0 commit comments

Comments
 (0)