Re: [PATCH v5 05/10] soc: qcom: Add AOSS QMP communication driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Fri 01 Feb 15:36 PST 2019, Doug Anderson wrote:

> Hi,
> 
> On Wed, Jan 30, 2019 at 4:40 PM Bjorn Andersson
> <bjorn.andersson@xxxxxxxxxx> wrote:
> > +++ b/drivers/soc/qcom/aoss-qmp.c
> > @@ -0,0 +1,317 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright (c) 2018, Linaro Ltd
> > + */
> > +#include <linux/interrupt.h>
> > +#include <linux/io.h>
> > +#include <linux/mailbox_client.h>
> > +#include <linux/module.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/soc/qcom/aoss-qmp.h>
> > +
> > +#define QMP_DESC_MAGIC                 0x0
> > +#define QMP_DESC_VERSION               0x4
> > +#define QMP_DESC_FEATURES              0x8
> > +
> > +#define QMP_DESC_UCORE_LINK_STATE      0xc
> > +#define QMP_DESC_UCORE_LINK_STATE_ACK  0x10
> > +#define QMP_DESC_UCORE_CH_STATE                0x14
> > +#define QMP_DESC_UCORE_CH_STATE_ACK    0x18
> > +#define QMP_DESC_UCORE_MBOX_SIZE       0x1c
> > +#define QMP_DESC_UCORE_MBOX_OFFSET     0x20
> > +
> > +#define QMP_DESC_MCORE_LINK_STATE      0x24
> > +#define QMP_DESC_MCORE_LINK_STATE_ACK  0x28
> > +#define QMP_DESC_MCORE_CH_STATE                0x2c
> > +#define QMP_DESC_MCORE_CH_STATE_ACK    0x30
> > +#define QMP_DESC_MCORE_MBOX_SIZE       0x34
> > +#define QMP_DESC_MCORE_MBOX_OFFSET     0x38
> 
> I sure wish something in this file told me what a mcore and a ucore
> were.  The only thing I can think of is that an "m" core is two "u"
> cores flipped upside down and placed really close to each other.
> ...if we had 6 upside down "u" cores we'd have an "mmm" core.  Mmm,
> core.
> 

I had to look at the code again to figure out which side was which, so
I'll add a comment here to indicate which is which.

> 
> > +static int qmp_open(struct qmp *qmp)
> > +{
> > +       int ret;
> > +       u32 val;
> > +
> > +       ret = wait_event_timeout(qmp->event, qmp_magic_valid(qmp), HZ);
> 
> I'm a totally noob here, but I'm curious: what kicks this event?  Do
> we just assume that an IRQ is pending already when the probe()
> function is called?  Maybe you could add a comment?
> 
> ...or maybe you never actually get an IRQ here and just rely on the
> magic value being right at boot in which case we should just check
> qmp_magic_valid()
> 
> ...or maybe you never actually get an IRQ here and this is equivalent
> to msleep(1000) followed by a check of qmp_magic_valid()?
> 

This must be me misinterpreting the downstream driver, the magic is
already in place when we enter here.

> 
> > +       if (!ret) {
> > +               dev_err(qmp->dev, "QMP magic doesn't match\n");
> > +               return -ETIMEDOUT;
> > +       }
> > +
> > +       val = readl(qmp->msgram + QMP_DESC_VERSION);
> > +       if (val != QMP_VERSION) {
> > +               dev_err(qmp->dev, "unsupported QMP version %d\n", val);
> > +               return -EINVAL;
> > +       }
> > +
> > +       qmp->offset = readl(qmp->msgram + QMP_DESC_MCORE_MBOX_OFFSET);
> > +       qmp->size = readl(qmp->msgram + QMP_DESC_MCORE_MBOX_SIZE);
> > +       if (!qmp->size) {
> > +               dev_err(qmp->dev, "invalid mailbox size 0x%zx\n", qmp->size);
> 
> nitty nit: Can you do "%#zx" to avoid the need for the 0x?
> 

Didn't know I could do that, but that said this is conditional on
!qmp->size, so I'm dropping the 0x0 from the error...
> 
> > +               return -EINVAL;
> > +       }
> > +
> > +       /* Ack remote core's link state */
> > +       val = readl(qmp->msgram + QMP_DESC_UCORE_LINK_STATE);
> > +       writel(val, qmp->msgram + QMP_DESC_UCORE_LINK_STATE_ACK);
> > +
> > +       /* Set local core's link state to up */
> > +       writel(QMP_STATE_UP, qmp->msgram + QMP_DESC_MCORE_LINK_STATE);
> > +
> > +       qmp_kick(qmp);
> > +
> > +       ret = wait_event_timeout(qmp->event, qmp_link_acked(qmp), HZ);
> > +       if (!ret) {
> > +               dev_err(qmp->dev, "ucore didn't ack link\n");
> > +               goto timeout_close_link;
> > +       }
> > +
> > +       writel(QMP_STATE_UP, qmp->msgram + QMP_DESC_MCORE_CH_STATE);
> > +
> > +       ret = wait_event_timeout(qmp->event, qmp_ucore_channel_up(qmp), HZ);
> 
> Again maybe a noob question, but what kicks the interrupt here?  Is
> the other side looping waiting to see us write "QMP_STATE_UP" into
> "QMP_DESC_MCORE_CH_STATE" and then it sends us another interrupt?
> ...or are we just getting lucky that the condition is right to begin
> with?
> 

I guess it does, but I think there is a kick inbetween here in the
downstream driver, I'll add one for good measure.

> 
> > +       if (!ret) {
> > +               dev_err(qmp->dev, "ucore didn't open channel\n");
> > +               goto timeout_close_channel;
> > +       }
> > +
> > +       /* Ack remote core's channel state */
> > +       val = readl(qmp->msgram + QMP_DESC_UCORE_CH_STATE);
> > +       writel(val, qmp->msgram + QMP_DESC_UCORE_CH_STATE_ACK);
> 
> nit: the readl() is silly here.  Just before this you called
> qmp_ucore_channel_up() and that confirmed that the value you're
> getting here is exactly equal to "QMP_STATE_UP".  Just write that.
> 

Right

> 
> > +static int qmp_probe(struct platform_device *pdev)
> > +{
> > +       struct resource *res;
> > +       struct qmp *qmp;
> > +       int irq;
> > +       int ret;
> > +
> > +       qmp = devm_kzalloc(&pdev->dev, sizeof(*qmp), GFP_KERNEL);
> > +       if (!qmp)
> > +               return -ENOMEM;
> > +
> > +       qmp->dev = &pdev->dev;
> > +       init_waitqueue_head(&qmp->event);
> > +       mutex_init(&qmp->tx_lock);
> > +
> > +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > +       qmp->msgram = devm_ioremap_resource(&pdev->dev, res);
> > +       if (IS_ERR(qmp->msgram))
> > +               return PTR_ERR(qmp->msgram);
> > +
> > +       qmp->mbox_client.dev = &pdev->dev;
> > +       qmp->mbox_client.knows_txdone = true;
> > +       qmp->mbox_chan = mbox_request_channel(&qmp->mbox_client, 0);
> 
> nit: your code would be simplified a bit if you created
> devm_mbox_request_channel() in a prior patch.
> 
> 
> > +       if (IS_ERR(qmp->mbox_chan)) {
> > +               dev_err(&pdev->dev, "failed to acquire ipc mailbox\n");
> > +               return PTR_ERR(qmp->mbox_chan);
> > +       }
> > +
> > +       irq = platform_get_irq(pdev, 0);
> > +       ret = devm_request_irq(&pdev->dev, irq, qmp_intr, IRQF_ONESHOT,
> > +                              "aoss-qmp", qmp);
> > +       if (ret < 0) {
> > +               dev_err(&pdev->dev, "failed to request interrupt\n");
> > +               mbox_free_channel(qmp->mbox_chan);
> > +               return ret;
> > +       }
> > +
> > +       ret = qmp_open(qmp);
> > +       if (ret < 0) {
> > +               mbox_free_channel(qmp->mbox_chan);
> > +               return ret;
> > +       }
> > +
> > +       platform_set_drvdata(pdev, qmp);
> > +
> > +       if (of_property_read_bool(pdev->dev.of_node, "#power-domain-cells")) {
> > +               qmp->pd_pdev = platform_device_register_data(&pdev->dev,
> > +                                                            "aoss_qmp_pd",
> > +                                                            PLATFORM_DEVID_NONE,
> > +                                                            NULL, 0);
> > +               if (IS_ERR(qmp->pd_pdev))
> > +                       dev_err(&pdev->dev, "failed to register AOSS PD\n");
> 
> nit: I'd prefer dev_warn() for serious but non-fatal errors.  This
> appears to be non-fatal since it doesn't cause you to return an error.
> 
> ...ideally the error message should indicate that the error is being ignored.
> 

I think it makes more sense to keep this as dev_err() and actually
fail probe on this. Will update.

> 
> I'm also not 100% sure why the "aoss_qmp_pd" needs to be broken up as
> a separate driver.  I guess there is expectation that there will be
> more sub-drivers that use qmp_send() and that stuffing them all in the
> same driver would be too much?  It sure seems like your life would be
> simplified if they were just one driver though unless you think
> someone would want to enable "AOSS_QMP" without enabling
> "AOSS_QMP_PD".
> 

I think splitting them in different files makes a lot of sense, whether
they should be separate drivers or just linked to one chunk that's food
for thought.

> 
> > +static int qmp_remove(struct platform_device *pdev)
> > +{
> > +       struct qmp *qmp = platform_get_drvdata(pdev);
> > +
> > +       platform_device_unregister(qmp->pd_pdev);
> 
> Presumably the above should be prefixed with:
> 
> if (!IS_ERR(qmp->pd_pdev))
> 
> ...since it appears that the probe will return with no error if you
> fail to register the pd_pdev and thus you need to handle it being an
> error in remove.
> 

Let's just make sure this doesn't happen.

> 
> > +       mbox_free_channel(qmp->mbox_chan);
> > +       qmp_close(qmp);
> 
> nit: I always expect that remove should be in the opposite order of
> probe.  That means qmp_close() should be before mbox_free_channel().

You're right.

Thanks,
Bjorn



[Index of Archives]     [Device Tree Compilter]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux PCI Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Yosemite Backpacking]


  Powered by Linux