-
Notifications
You must be signed in to change notification settings - Fork 886
Driver for the I3C Controller IP Core #2480
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
1458e52
to
054d520
Compare
index = 0; | ||
for (status0 = readl(master->regs + REG_FIFO_STATUS); | ||
!(status0 & FIFO_STATUS_CMDR_EMPTY); | ||
status0 = readl(master->regs + REG_FIFO_STATUS)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
any chance of this going into an infinite loop?
if yes, maybe add a timeout?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, for each command CMD sent, one command receipt CMDR is received.
drivers/i3c/master/adi-i3c-master.c
Outdated
pending = readl(master->regs + REG_IRQ_PENDING); | ||
|
||
spin_lock(&master->xferqueue.lock); | ||
adi_i3c_master_end_xfer_locked(master, pending); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this may be a lot of work for an IRQ handler? (not sure, just guessing here);
if it is too much work (squeezed into an IRQ handler) it may make sense to add a threaded_irq
handler too;
(still open for debate on this point)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Expected number of AXI R/W per interrupt type:
- CMDR: 2 Reads (4-bytes from the CMDR plus 4-bytes of SDI data) + [2x#CMD Writes from next transfer, if any]
- IBI: 1 Read (4-bytes)
- DAA: 2 Reads + 1 Write (6-bytes R, 1-byte W)
Plus 1 Write to clear the interrupt.
2-bytes per CMD
is always less than SPI Engine's commands {[REG_CONFIG;REG_CLK_DIV,REG_WORD_LEN];CS;Transfer;Sleep;CS;SYNC}
|
||
ret = xfer->ret; | ||
cmd->err = adi_i3c_cmd_get_err(&xfer->cmds[0]); | ||
kfree(xfer); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i'm curios if this is correct;
AFAICT, adi_i3c_master_queue_xfer()
may have added this to a list, so, now it's free'd?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so, the idea that makes it hard (for me to visualize this, is): when wait_for_completion_timeout()
finishes, is there a chance that xfer
will still be part of a list?
the whole mechanism from adi_i3c_master_queue_xfer()
&& adi_i3c_master_unqueue_xfer()
(see below example from adi_i3c_master_unqueue_xfer()
), makes it hard to say if list_del_init()
has been called when we get here:
if (master->xferqueue.cur == xfer)
master->xferqueue.cur = NULL;
else
list_del_init(&xfer->node)
it may be an idea to move the kfree
handling in the same place where list_del_init()
(and other stuff) happens;
that may make it more difficult to get the xfer->ret
value and the other errors;
what about doing it the other way around?
pass xfer->cmd = cmd
(at the start of the transfer) and work on it inside adi_i3c_master_unqueue_xfer()
and other handlers
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Design flaws TLDR:
- no FIFO flushing on unqueue_xfer (fixed by sw core reset).
- wait_for_completion_timeout is blocking (seem to be the normal).
current transfer: master->xferqueue.cur
queue: master->xferqueue
Pipeline:
When a xfer is queued:
- If current transfer is null, set it to xfer and start transfer.
- If not, append to xfer to queue.
When a xfer finishes, triggered by the first CMDR received and iterated through all CMDR (bus transfers are faster than AXI reads), get the next xfer from the queue and:
- if it is not null; remove xfer from queue and set it as current transfer and start transfer.
- if it is null, current transfer is set null
Finally, in normal operation wait_for_completion_timeout should never expire, but if it does:
- if xfer is current transfer, set it to null
- If not, remove the node from the queued list
A flaw of the flow is that the FIFOs are not flushed when unqueue_xfer is called; so if a CMDR from current transfer arrives (super) late it will "sit" on the FIFO and miss-belong to the next xfer.
Still, wait_for_completion_timeout should never happen since the bus never hangs (unlike I²C), unless there is a bug in the HDL.
So, overall the solution is to flush the FIFOs on timeout? As is, a core reset will flush all FIFOs
For the o.g. questions
AFAICT, adi_i3c_master_queue_xfer() may have added this to a list, so, now it's free'd?
Even if it is added to a list, the completion is only satisfied on complete(&xfer->comp)
at end_xfer_locked
when wait_for_completion_timeout() finishes, is there a chance that xfer will still be part of a list?
- In normal operation (no timeout) no, it exed as current transfer before
end_xfer_locked
- on timeout, no,
unqueue_xfer
removed it form the list.
At driver level, we call either i3c_device_do_priv_xfers
, passing a buffer at the each xfer of the xfers array;
or even use the regmap API, e.g, regmap_raw_read
; both eventually call the controller priv_xfers
.
for (i = 0; i < nxfers; i++) | ||
xfers[i].err = adi_i3c_cmd_get_err(&xfer->cmds[i]); | ||
|
||
kfree(xfer); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same comment here;
AFAICT, adi_i3c_master_queue_xfer()
may have added this to a list, so, now it's free'd?`
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See long comment.
adi_i3c_master_unqueue_xfer(master, xfer); | ||
|
||
ret = xfer->ret; | ||
kfree(xfer); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same comment here
AFAICT, adi_i3c_master_queue_xfer()
may have added this to a list, so, now it's free'd?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See long comment.
drivers/i3c/master/adi-i3c-master.c
Outdated
if (!wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000))) | ||
adi_i3c_master_unqueue_xfer(master, xfer); | ||
|
||
ret = xfer->ret; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this ret
is assigned bu never used from here-on
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
There was also an id assigned but never used that I removed.
And a commented //sleep
that I swear I had removed
drivers/i3c/master/adi-i3c-master.c
Outdated
writel(readl(master->regs + REG_IRQ_MASK) | IRQ_PENDING_IBI_PENDING, | ||
master->regs + REG_IRQ_MASK); | ||
|
||
ret = i3c_master_enec_locked(m, dev->info.dyn_addr, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return i3c_master_enec_locked()
directly?
or will there be more code added after this later?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, done
Add nodes for ADI I3C Controller IP core. Signed-off-by: Jorge Marques <[email protected]>
6e819ad
to
d5537c5
Compare
Forced pushed to resolve conflict at MAINTAINERS file due to rebase on the main branch + some I2C rework. |
Known design issue under fix elaboration: |
ADI I3C Controller is an AXI-interfaced HDL IP that implements an I3C Controller with i2c backwards-compatibility and IBI support. I3C speed is configurable, while the i2c speed is locked at the lowest instantiated speed of the controller. Signed-off-by: Jorge Marques <[email protected]>
V0->V1 Changes:
Why:
|
Will upstream the driver, no reason to merge this to main |
PR Description
Even though the IP Core has an Offload engine, I didn't implement it here to not make things harder upstream; As is, matches the current abstraction methods.
Using the upstream
master/slave
nomenclature for coherence, our IP core uses the speccontroller/peripheral
.Something that might catch the attention of reviewers is the call-back/entry point for IBIs,
That is because it is allocated per driver device, along these lines:
PR Type
PR Checklist