in master/svc-i3c-master.c [728:831]
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;
u32 reg;
int ret, i;
while (true) {
/* Enter/proceed with DAA */
writel(SVC_I3C_MCTRL_REQUEST_PROC_DAA |
SVC_I3C_MCTRL_TYPE_I3C |
SVC_I3C_MCTRL_IBIRESP_NACK |
SVC_I3C_MCTRL_DIR(SVC_I3C_MCTRL_DIR_WRITE),
master->regs + SVC_I3C_MCTRL);
/*
* Either one slave will send its ID, or the assignment process
* is done.
*/
ret = readl_poll_timeout_atomic(master->regs + SVC_I3C_MSTATUS,
reg,
SVC_I3C_MSTATUS_RXPEND(reg) |
SVC_I3C_MSTATUS_MCTRLDONE(reg),
1, 1000);
if (ret)
return ret;
if (SVC_I3C_MSTATUS_RXPEND(reg)) {
u8 data[6];
/*
* We only care about the 48-bit provisional ID yet to
* be sure a device does not nack an address twice.
* Otherwise, we would just need to flush the RX FIFO.
*/
ret = svc_i3c_master_readb(master, data, 6);
if (ret)
return ret;
for (i = 0; i < 6; i++)
prov_id[dev_nb] |= (u64)(data[i]) << (8 * (5 - i));
/* We do not care about the BCR and DCR yet */
ret = svc_i3c_master_readb(master, data, 2);
if (ret)
return ret;
} else if (SVC_I3C_MSTATUS_MCTRLDONE(reg)) {
if (SVC_I3C_MSTATUS_STATE_IDLE(reg) &&
SVC_I3C_MSTATUS_COMPLETE(reg)) {
/*
* All devices received and acked they dynamic
* address, this is the natural end of the DAA
* procedure.
*/
break;
} else if (SVC_I3C_MSTATUS_NACKED(reg)) {
/*
* A slave device nacked the address, this is
* allowed only once, DAA will be stopped and
* then resumed. The same device is supposed to
* answer again immediately and shall ack the
* address this time.
*/
if (prov_id[dev_nb] == nacking_prov_id)
return -EIO;
dev_nb--;
nacking_prov_id = prov_id[dev_nb];
svc_i3c_master_emit_stop(master);
continue;
} else {
return -EIO;
}
}
/* Wait for the slave to be ready to receive its address */
ret = readl_poll_timeout_atomic(master->regs + SVC_I3C_MSTATUS,
reg,
SVC_I3C_MSTATUS_MCTRLDONE(reg) &&
SVC_I3C_MSTATUS_STATE_DAA(reg) &&
SVC_I3C_MSTATUS_BETWEEN(reg),
0, 1000);
if (ret)
return ret;
/* Give the slave device a suitable dynamic address */
ret = i3c_master_get_free_addr(&master->base, last_addr + 1);
if (ret < 0)
return ret;
addrs[dev_nb] = ret;
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++];
}
*count = dev_nb;
return 0;
}