Re: [PATCH 2/2] i2c: designware: Add AMD PSP I2C bus support

From: Jan Dąbroś
Date: Fri Jan 28 2022 - 09:46:33 EST


pt., 21 sty 2022 o 18:52 Andy Shevchenko
<andriy.shevchenko@xxxxxxxxxxxxxxx> napisał(a):
>
> On Fri, Jan 21, 2022 at 11:20:19AM +0100, Jan Dąbroś wrote:
> > czw., 20 sty 2022 o 15:21 Andy Shevchenko
> > <andriy.shevchenko@xxxxxxxxxxxxxxx> napisał(a):
> > > On Thu, Jan 20, 2022 at 01:16:21AM +0100, Jan Dabros wrote:
>
> ...
>
> > > > Add new entry in MAINTAINERS file to cover new module.
> > >
> > > It's confusing. You added yourself as a reviewer for I2C DesignWare driver,
> > > which is great, but not described in the commit message.
> >
> > Should I rephrase this sentence (to be more specific that I may be
> > helpful for reviewing amdpsp.c module) or rather you mean to exclude
> > drivers/i2c/busses/i2c-designware-amdpsp.c from the rest of
> > designware-* modules and create separate entry?
> >
> > Actually initially I wasn't planning to modify MAINTAINERS (after all
> > I'm not an I2C DesignWare expert) until run checkpatch.pl which
> > recommended to do so when adding new file. Eventually for me it made
> > some sense since I have a platform equipped with AMD Cezanne SoC and I
> > will be able to review and test potential changes in
> > i2c-designware-amdpsp.c or in general around semaphore areas.
> >
> > This may also work with different model, similar to how you pointed me
> > to Hans as an owner of Bay Trail platform who is acquinted with how
> > its i2c semaphore is working. I will simply remove myself from the
> > MAINTAINERS file and you can add me to the threads if there is
> > anything requiring my help.
> >
> > Let me know which way is working for you. I just thought it is not
> > good to leave you alone with a new module which you cannot actually
> > test and don't have deep insight about how PSP-x86 communications
> > works.
>
> You have a few options:
> - leave it for us (but it probably won't go well in long-term as you noticed)
> - add yourself as a reviewer (it doesn't require to review everything, but
> you will get all i2c DesignWare driver changes)

I think this is the best option. So I will leave initial change to the
MAINTAINERS as is, but will put few more words of description into
commit message.

> - add a new MAINTAINERS database entry where you can put yourself for that
> file even as a maintainaer
>
> ...
>
> > > > +#include <asm/msr.h>
> > >
> > > Usually linux/* followed by asm/*.
> > >
> > > > +#include <linux/i2c.h>
> > > > +#include <linux/psp-sev.h>
> > >
> > > types.h?
> >
> > I need to take a deeper look at the headers included here, especially
> > considering errors pointed by kernel test robot. Not sure why I
> > haven't seen any issues on my setup.
>
> The problem here is not so visible. Headers should be a compromise between
> what is really used and what we may include for that. There are headers
> that guaranteed to be included by others, otherwise it will be an implicit
> dependency which is not good in cases of generic headers, such as types.h.

ACK. I included couple of new headers in v2.

>
> ...
>
> > > > +union psp_req_buffer_hdr {
> > > > + struct {
> > > > + u32 total_size;
> > > > + u32 status;
> > > > + } __packed;
> > >
> > > What does packet bring you here?
> >
> > In this particular case binary-wise nothing, I can as well drop this.
> > It may be necessary if there are some changes in this structs fields
> > in future (e.g changing total_size to u8), since PSP expects every
> > field to be byte-aligned.
>
> _packed will make another thing which you probably won't need, it brings
> the entire structure to be possible on unaligned addresses and then some
> warnings from compiler may be issued even if there is no problem in the
> flow.
>
> Read this discussion: https://lore.kernel.org/linux-media/20220110224656.266536-1-sakari.ailus@xxxxxxxxxxxxxxx/

OK, I see your point - thanks for this pointer, something new for me.

>
> > > > + u64 hdr_val;
> > >
> > > And why does this not have the same alignment since it's also part of
> > > the union?
> >
> > __packed is not about alignment of the whole struct/union
>
> It's. See above.

ACK.

>
> > but about
> > lack of padding between its fields. As above - in this particular case
> > with two u32 it doesn't matter.
>
> ...
>
> > > > +struct psp_i2c_req {
> > > > + union psp_req_buffer_hdr hdr;
> > > > + enum psp_i2c_req_type type;
> > >
> > > > +} __packed __aligned(32);
> > >
> > > Can you explain, what this means and how it's supposed to work?
> >
> > This means that each instance of the struct should be aligned (32)
> > while at the same time no padding within members - thus this may
> > result in non-aligned addresses of members.
>
> 32 bytes? And on unaligned address at the same time.

Yes, it was initial recommendation. I left this as is in v2, will work
with AMD to see if we can lower (get rid of) this requirement.

>
> ...
>
> > > > +union psp_mbox_cmd_reg {
> > > > + struct psp_mbox_cmd_fields {
> > > > + u16 mbox_status;
> > > > + u8 mbox_cmd;
> > > > + u8 reserved:6;
> > > > + u8 recovery:1;
> > > > + u8 ready:1;
> > >
> > > > + } __packed fields;
> > >
> > > So, what is the __packed purpose here?
> >
> > As in all above cases - considering current layout of members and
> > their sizes dropping `__packed` will not results in any errors.
> >
> > However PSP expects all members os structs within shared buffers to be
> > byte-aligned, that's why I've added this attributes to be on the safe
> > side. If you think this doesn't make sense, I can remove them - in
> > (very unlikely) case of changes, one will need to add this specifier.
>
> I guess you don't need them at all in this case in any of the data structure
> you created here.

ACK, I removed unnecessary __packed attributes in v2.

> ...
>
> > > > + struct psp_mbox *mbox = (struct psp_mbox *)mbox_iomem;
> > >
> > > Heck, no!
> >
> > I need to get acquinted to the kernel-reviewer language:) Pardon my
> > ignorance, but just to be sure I get what you mean here:
> > I'm using global mbox_iomem to keep address of PSP mailbox in IO
> > memory. Casting this to struct psp_mbox layout here, to make access
> > more convenient.
> > Your point here is that:
> > 1. I should move the assignment out from the variable declaration part
> > of this function;
> > 2. I should use ioremap/iounmap each time in psp_send_cmd instead of
> > using it once in `probe` and unmap in `remove`?
> > I thought about this option as to be less effective performance-wise
> > (even though I can get rid of one global variable).
> > 3. Something else?
>
> Casting an __iomem pointer to the some struct without keeping it. I believe
> sparse should blow because of this.

Oh right.. pretty obvious. Fixed.

>
> ...
>
> > > > + /* Fill address of command-response buffer */
> > > > + writeq((uintptr_t)__psp_pa((void *)req), &mbox->i2c_req_addr);
> > >
> > > What does this voodoo mean?!
> >
> > Basically I need to take physical address (__psp_pa) of request buffer
> > req and write this down into mailbox IO memory.
> > This should be spread into more lines with some comments, is this your point?
>
> It needs much better comment explaining what is this address and its meaning
> for the hardware and why you need physical address here (DMA?). For me it looks
> like a voodoo. Ah, and not using phys_addr_t / dma_addr_t / etc type here, but
> uintptr_t just adds a confusion.

ACK.

> ...
>
> > > > + start = jiffies;
> > > > + do {
> > > > + if (psp_send_cmd(req)) {
> > > > + ret = -EIO;
> > > > + goto cleanup;
> > > > + }
> > > > +
> > > > + status = check_i2c_req_sts(req);
> > > > + if (!status) {
> > > > + dev_dbg(psp_i2c_dev, "Request accepted by PSP after %ums\n",
> > > > + jiffies_to_msecs(jiffies - start));
> > > > + ret = 0;
> > > > + goto cleanup;
> > > > + } else if (status == -EBUSY) {
> > > > + retry_cnt--;
> > > > + } else {
> > > > + ret = -EIO;
> > > > + goto cleanup;
> > > > + };
> > > > +
> > > > + /* IF EBUSY, give PSP time to finish its i2c activities */
> > > > + mdelay(PSP_I2C_REQ_RETRY_DELAY_MSEC);
> > > > + } while (retry_cnt);
> > >
> > > NIH iopoll.h API(s).
> >
> > I don't think macros avaialble in iopoll.h are suitable here.
> > Procedure above is not about simply reading some IO and waiting for
> > particular condition to be met with this particular value. Eventually
> > `psp_send_cmd()` invokes `psp_wait_cmd()` where I'm using
> > `readl_poll_timeout()`, so on lower level I'm making use of this API.
> > However here I don't see any obvious method how to incorporate
> > iopoll.h API to reach my goal.
>
> You do not go to clean up if and only if status == -EBUSY, so here we have
> a condition. the rest can be moved to a function that you wrap by
> read_poll_timeout_atomic() (pay attention to the macro name).
> Here is the question, btw, why _atomic()? I.o.w. why mdelay() and not msleep()?

Right, this was another error - above code shouldn't be executed in
atomic context. I used read_poll_timeout() in v2.

> > > > + ret = -ETIMEDOUT;
>
> ...
>
> > > Handle errors first.
>
> > Addressing above two comments - what do you think about below:
> > if (status) {
> > if (status == -ETIMEDOUT)
> > dev_err(psp_i2c_dev, "Timed out waiting for PSP to
> > release I2C bus\n");
> > else
> > dev_err(psp_i2c_dev, "PSP communication error\n");
>
> > dev_err(psp_i2c_dev, "PSP communication error\n");
>
> This dup message is not needed, otherwise fine to me.

ACK.

>
> > psp_i2c_mbox_fail = true;
> > goto cleanup;
> > }
> >
> > psp_i2c_sem_acquired = jiffies;
> > psp_i2c_access_count++;
> > (...)
>
> ...
>
> > > > +static int i2c_dw_probe_lock_support(struct dw_i2c_dev *dev)
> > > > +{
> > > > + int ret;
> > > > + int i;
> > > > +
> > > > + dev->semaphore_idx = -1;
> > > > +
> > > > + for (i = 0; i < ARRAY_SIZE(i2c_dw_semaphore_cb_table); i++) {
> > >
> > > > + if (!i2c_dw_semaphore_cb_table[i].probe)
> > > > + continue;
> > >
> > > Huh?
> >
> > Just to be sure I get your point.
> > Once I found terminating entry, I will get out of the loop and return
> > 0 as there are no semaphores to be "applied". Actually I should
> > probably use `break;` here as there shouldn't be a case when certain
> > type of semaphore installs without `probe` being implemented.
>
> Yes, that's what I though, and rather using ARRAY_SIZE(), use a terminator you
> provided.
>
> Originally you have used two approaches which seems competing with each other:
> - ARRAY_SIZE
> - terminator entry
>
> And on top of that so confusing continue on top of not-found ->probe() that
> makes me think what the meaning of the entry that has set ->remove(), but no
> ->probe().
>
> That said, I would rather see something like
>
> struct ... *p = &...;
>
> while (p->probe) {
> ...
> p++;
> }

ACK.

> --
> With Best Regards,
> Andy Shevchenko
>
>