Re: [PATCH 05/20] bus: mhi: ep: Add support for registering MHI endpoint controllers
From: Manivannan Sadhasivam
Date: Thu Feb 03 2022 - 09:50:50 EST
On Wed, Jan 05, 2022 at 06:26:51PM -0600, Alex Elder wrote:
> On 12/2/21 5:35 AM, Manivannan Sadhasivam wrote:
> > This commit adds support for registering MHI endpoint controller drivers
> > with the MHI endpoint stack. MHI endpoint controller drivers manages
> > the interaction with the host machines such as x86. They are also the
> > MHI endpoint bus master in charge of managing the physical link between the
> > host and endpoint device.
> >
> > The endpoint controller driver encloses all information about the
> > underlying physical bus like PCIe. The registration process involves
> > parsing the channel configuration and allocating an MHI EP device.
> >
> > Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@xxxxxxxxxx>
>
> See below. Lots of little things, some I've said before.
>
> > ---
> > drivers/bus/mhi/Kconfig | 1 +
> > drivers/bus/mhi/Makefile | 3 +
> > drivers/bus/mhi/ep/Kconfig | 10 ++
> > drivers/bus/mhi/ep/Makefile | 2 +
> > drivers/bus/mhi/ep/internal.h | 158 +++++++++++++++++++++++
> > drivers/bus/mhi/ep/main.c | 231 ++++++++++++++++++++++++++++++++++
> > include/linux/mhi_ep.h | 140 +++++++++++++++++++++
> > 7 files changed, 545 insertions(+)
> > create mode 100644 drivers/bus/mhi/ep/Kconfig
> > create mode 100644 drivers/bus/mhi/ep/Makefile
> > create mode 100644 drivers/bus/mhi/ep/internal.h
> > create mode 100644 drivers/bus/mhi/ep/main.c
> > create mode 100644 include/linux/mhi_ep.h
> >
[...]
> > +/* MHI register definitions */
> > +#define MHIREGLEN 0x100
>
> I really think it would be nice if these could be common between the
> host and endpoint.
>
done
> > +#define MHIVER 0x108
> > +#define MHICFG 0x110
> > +#define CHDBOFF 0x118
> > +#define ERDBOFF 0x120
> > +#define BHIOFF 0x128
> > +#define DEBUGOFF 0x130
> > +#define MHICTRL 0x138
> > +#define MHISTATUS 0x148
> > +#define CCABAP_LOWER 0x158
> > +#define CCABAP_HIGHER 0x15c
> > +#define ECABAP_LOWER 0x160
> > +#define ECABAP_HIGHER 0x164
> > +#define CRCBAP_LOWER 0x168
> > +#define CRCBAP_HIGHER 0x16c
> > +#define CRDB_LOWER 0x170
> > +#define CRDB_HIGHER 0x174
> > +#define MHICTRLBASE_LOWER 0x180
> > +#define MHICTRLBASE_HIGHER 0x184
> > +#define MHICTRLLIMIT_LOWER 0x188
> > +#define MHICTRLLIMIT_HIGHER 0x18c
> > +#define MHIDATABASE_LOWER 0x198
> > +#define MHIDATABASE_HIGHER 0x19c
> > +#define MHIDATALIMIT_LOWER 0x1a0
> > +#define MHIDATALIMIT_HIGHER 0x1a4
>
> It wouldn't hurt to have a one or two line comment explaining how
> these compute the offset for a given channel or event ring's
> doorbell register.
>
> I think you could use decimal for the multiplier (8 rather than 0x8),
> but maybe you prefer not mixing that with a hex base offset.
>
> Overall though, take a look at the macros you define like this.
> See if you can decide on whether you can settle on a consistent
> form. Some places you use decimal, others hex. It's not a
> big deal, but consistency always helps.
>
Will look.
> > +#define CHDB_LOWER_n(n) (0x400 + 0x8 * (n))
> > +#define CHDB_HIGHER_n(n) (0x404 + 0x8 * (n))
> > +#define ERDB_LOWER_n(n) (0x800 + 0x8 * (n))
> > +#define ERDB_HIGHER_n(n) (0x804 + 0x8 * (n))
> > +#define BHI_INTVEC 0x220
> > +#define BHI_EXECENV 0x228
> > +#define BHI_IMGTXDB 0x218
> > +
>
> Will the AP always be an "A7"?
>
That's the register defined in the register manual. So I'd like to keep it for
now.
> > +#define MHI_CTRL_INT_STATUS_A7 0x4
> > +#define MHI_CTRL_INT_STATUS_A7_MSK BIT(0)
> > +#define MHI_CTRL_INT_STATUS_CRDB_MSK BIT(1)
> > +#define MHI_CHDB_INT_STATUS_A7_n(n) (0x28 + 0x4 * (n))
> > +#define MHI_ERDB_INT_STATUS_A7_n(n) (0x38 + 0x4 * (n))
> > +
> > +#define MHI_CTRL_INT_CLEAR_A7 0x4c
> > +#define MHI_CTRL_INT_MMIO_WR_CLEAR BIT(2)
> > +#define MHI_CTRL_INT_CRDB_CLEAR BIT(1)
> > +#define MHI_CTRL_INT_CRDB_MHICTRL_CLEAR BIT(0)
> > +
> > +#define MHI_CHDB_INT_CLEAR_A7_n(n) (0x70 + 0x4 * (n))
> > +#define MHI_CHDB_INT_CLEAR_A7_n_CLEAR_ALL GENMASK(31, 0)
> > +#define MHI_ERDB_INT_CLEAR_A7_n(n) (0x80 + 0x4 * (n))
> > +#define MHI_ERDB_INT_CLEAR_A7_n_CLEAR_ALL GENMASK(31, 0)
> > +
>
> The term "MASK" here might be confusing. Does a bit set in
> this mask register indicate an interrupt is enabled, or
> disabled (masked)? A comment (here or where used) could
> clear it up without renaming the symbol.
>
I agree that it is confusing but that's how the platform defines it. Will add a
comment though.
> > +#define MHI_CTRL_INT_MASK_A7 0x94
> > +#define MHI_CTRL_INT_MASK_A7_MASK_MASK GENMASK(1, 0)
> > +#define MHI_CTRL_MHICTRL_MASK BIT(0)
> > +#define MHI_CTRL_MHICTRL_SHFT 0
> > +#define MHI_CTRL_CRDB_MASK BIT(1)
> > +#define MHI_CTRL_CRDB_SHFT 1
>
> Use SHIFT or SHFT (not both), consistently. (But get rid of
> this shift definition, and others like it...)
>
Done
> > +#define MHI_CHDB_INT_MASK_A7_n(n) (0xb8 + 0x4 * (n))
> > +#define MHI_CHDB_INT_MASK_A7_n_EN_ALL GENMASK(31, 0)
> > +#define MHI_ERDB_INT_MASK_A7_n(n) (0xc8 + 0x4 * (n))
> > +#define MHI_ERDB_INT_MASK_A7_n_EN_ALL GENMASK(31, 0)
> > +
> > +#define NR_OF_CMD_RINGS 1
>
> Is there ever any reason to believe there will be more than one
> command ring for a given MHI instance? I kept seeing loops over
> NR_OF_CMD_RINGS, and it just seemed silly.
>
It was added for future compatibility and the spec doesn't mention that there is
only one command ring.
> > +#define MHI_MASK_ROWS_CH_EV_DB 4
> > +#define MHI_MASK_CH_EV_LEN 32
> > +
> > +/* Generic context */
>
> Maybe define the entire structure as packed and aligned.
>
Justified in previous patch.
> > +struct mhi_generic_ctx {
> > + __u32 reserved0;
> > + __u32 reserved1;
> > + __u32 reserved2;
> > +
> > + __u64 rbase __packed __aligned(4);
> > + __u64 rlen __packed __aligned(4);
> > + __u64 rp __packed __aligned(4);
> > + __u64 wp __packed __aligned(4);
> > +};
>
> Are these structures defined separately for host and endpoint?
> (I've lost track... If they are, it would be better to define
> them in common.)
>
Channel, Event and command contexts are common and they are already defined in
common.h. This one is specific to endpoint.
> > +
> > +enum mhi_ep_ring_state {
> > + RING_STATE_UINT = 0,
>
> I think "UINT" is a *terrible* abbreviation to represent
> "uninitialized".
>
> > + RING_STATE_IDLE,
>
> Since there are only two states, uninitialized or idle, maybe
> you can get rid of this enum definition and just define the
> ring state with "bool initialized".
>
okay
> > +};
> > +
> > +enum mhi_ep_ring_type {
>
> Is the value 0 significant to hardware? If not, there's no need
> to define the numeric value on this first symbol.
>
> > + RING_TYPE_CMD = 0,
> > + RING_TYPE_ER,
> > + RING_TYPE_CH,
>
> I don't think you ever use RING_TYPE_INVALID, so it does
> not need to be defined.
>
ack
> > + RING_TYPE_INVALID,
> > +};
> > +
>
> I prefer a more meaningful structure definition than this (as
> mentioned in I think the first patch).
>
> > +struct mhi_ep_ring_element {
> > + u64 ptr;
> > + u32 dword[2];
> > +};
> > +
> > +/* Transfer ring element type */
>
> Not transfer ring, just ring. Command, transfer, and event
> ring descriptors are different things.
>
ack
> > +union mhi_ep_ring_ctx {
> > + struct mhi_cmd_ctxt cmd;
> > + struct mhi_event_ctxt ev;
> > + struct mhi_chan_ctxt ch;
> > + struct mhi_generic_ctx generic;
> > +};
> > +
[...]
> > +static void mhi_ep_release_device(struct device *dev)
> > +{
> > + struct mhi_ep_device *mhi_dev = to_mhi_ep_device(dev);
> > +
> > + /*
> > + * We need to set the mhi_chan->mhi_dev to NULL here since the MHI
> > + * devices for the channels will only get created if the mhi_dev
> > + * associated with it is NULL.
>
> Maybe say where in the code what the comment above says happens.
>
okay
> > + */
> > + if (mhi_dev->ul_chan)
> > + mhi_dev->ul_chan->mhi_dev = NULL;
> > +
> > + if (mhi_dev->dl_chan)
> > + mhi_dev->dl_chan->mhi_dev = NULL;
> > +
> > + kfree(mhi_dev);
> > +}
> > +
> > +static struct mhi_ep_device *mhi_ep_alloc_device(struct mhi_ep_cntrl *mhi_cntrl)
> > +{
> > + struct mhi_ep_device *mhi_dev;
> > + struct device *dev;
> > +
> > + mhi_dev = kzalloc(sizeof(*mhi_dev), GFP_KERNEL);
> > + if (!mhi_dev)
> > + return ERR_PTR(-ENOMEM);
> > +
> > + dev = &mhi_dev->dev;
> > + device_initialize(dev);
> > + dev->bus = &mhi_ep_bus_type;
> > + dev->release = mhi_ep_release_device;
> > +
>
> I think you should pass the MHI device type as argument here, and
> set it within this function. Then use it in the test below, rather
> than assuming the mhi_dev pointer will be NULL for the controller
> only. Maybe you should set the mhi_dev pointer here as well.
>
Makes sense.
I was trying to align with host MHI stack that does the same way.
But I'll change it in ep and will do the same on host later.
> > + if (mhi_cntrl->mhi_dev) {
> > + /* for MHI client devices, parent is the MHI controller device */
> > + dev->parent = &mhi_cntrl->mhi_dev->dev;
> > + } else {
> > + /* for MHI controller device, parent is the bus device (e.g. PCI EPF) */
> > + dev->parent = mhi_cntrl->cntrl_dev;
> > + }
> > +
> > + mhi_dev->mhi_cntrl = mhi_cntrl;
> > +
> > + return mhi_dev;
> > +}
> > +
> > +static int parse_ch_cfg(struct mhi_ep_cntrl *mhi_cntrl,
> > + const struct mhi_ep_cntrl_config *config)
> > +{
> > + const struct mhi_ep_channel_config *ch_cfg;
> > + struct device *dev = mhi_cntrl->cntrl_dev;
> > + u32 chan, i;
> > + int ret = -EINVAL;
> > +
> > + mhi_cntrl->max_chan = config->max_channels;
> > +
> > + /*
> > + * Allocate max_channels supported by the MHI endpoint and populate
> > + * only the defined channels
> > + */
> > + mhi_cntrl->mhi_chan = kcalloc(mhi_cntrl->max_chan, sizeof(*mhi_cntrl->mhi_chan),
> > + GFP_KERNEL);
> > + if (!mhi_cntrl->mhi_chan)
> > + return -ENOMEM;
> > +
> > + for (i = 0; i < config->num_channels; i++) {
> > + struct mhi_ep_chan *mhi_chan;
> > +
> > + ch_cfg = &config->ch_cfg[i];
> > +
> > + chan = ch_cfg->num;
> > + if (chan >= mhi_cntrl->max_chan) {
> > + dev_err(dev, "Channel %d not available\n", chan);
> > + goto error_chan_cfg;
> > + }
> > +
> > + mhi_chan = &mhi_cntrl->mhi_chan[chan];
> > + mhi_chan->name = ch_cfg->name;
> > + mhi_chan->chan = chan;
> > + mhi_chan->dir = ch_cfg->dir;
> > + mutex_init(&mhi_chan->lock);
>
> Move the error check below earlier, before assigning other values.
>
ack
> > + /* Bi-directional and direction less channels are not supported */
> > + if (mhi_chan->dir == DMA_BIDIRECTIONAL || mhi_chan->dir == DMA_NONE) {
> > + dev_err(dev, "Invalid channel configuration\n");
> > + goto error_chan_cfg;
> > + }
> > + }
> > +
> > + return 0;
> > +
> > +error_chan_cfg:
> > + kfree(mhi_cntrl->mhi_chan);
> > +
> > + return ret;
> > +}
> > +
> > +/*
> > + * Allocate channel and command rings here. Event rings will be allocated
> > + * in mhi_ep_power_up() as the config comes from the host.
> > + */
> > +int mhi_ep_register_controller(struct mhi_ep_cntrl *mhi_cntrl,
> > + const struct mhi_ep_cntrl_config *config)
> > +{
> > + struct mhi_ep_device *mhi_dev;
>
> Perhaps you could use a convention like "ep_dev" (and later, "ep_drv")
> to represent an mhi_ep_device, different from "mhi_dev" representing
> an mhi_device.
>
This is done to align with the host MHI stack, so that it'll be easy to spot
the MHI device pointer.
> > + int ret;
> > +
> > + if (!mhi_cntrl || !mhi_cntrl->cntrl_dev)
> > + return -EINVAL;
> > +
> > + ret = parse_ch_cfg(mhi_cntrl, config);
> > + if (ret)
> > + return ret;
> > +
>
> NR_OF_CMD_RINGS is 1, and I think always will be, right? This and
> elsewhere could be simplified if we just accept that.
>
For now yes, but it could be changed in future.
> > + mhi_cntrl->mhi_cmd = kcalloc(NR_OF_CMD_RINGS, sizeof(*mhi_cntrl->mhi_cmd), GFP_KERNEL);
> > + if (!mhi_cntrl->mhi_cmd) {
> > + ret = -ENOMEM;
> > + goto err_free_ch;
> > + }
> > +
> > + /* Set controller index */
> > + mhi_cntrl->index = ida_alloc(&mhi_ep_cntrl_ida, GFP_KERNEL);
> > + if (mhi_cntrl->index < 0) {
> > + ret = mhi_cntrl->index;
> > + goto err_free_cmd;
> > + }
> > +
> > + /* Allocate the controller device */
> > + mhi_dev = mhi_ep_alloc_device(mhi_cntrl);
> > + if (IS_ERR(mhi_dev)) {
> > + dev_err(mhi_cntrl->cntrl_dev, "Failed to allocate controller device\n");
> > + ret = PTR_ERR(mhi_dev);
> > + goto err_ida_free;
> > + }
> > +
> > + mhi_dev->dev_type = MHI_DEVICE_CONTROLLER;
> > + dev_set_name(&mhi_dev->dev, "mhi_ep%d", mhi_cntrl->index);
> > + mhi_dev->name = dev_name(&mhi_dev->dev);
> > +
> > + ret = device_add(&mhi_dev->dev);
> > + if (ret)
> > + goto err_release_dev;
>
> goto err_put_device?
>
okay
> > +
> > + mhi_cntrl->mhi_dev = mhi_dev;
> > +
> > + dev_dbg(&mhi_dev->dev, "MHI EP Controller registered\n");
> > +
> > + return 0;
> > +
> > +err_release_dev:
> > + put_device(&mhi_dev->dev);
> > +err_ida_free:
> > + ida_free(&mhi_ep_cntrl_ida, mhi_cntrl->index);
> > +err_free_cmd:
> > + kfree(mhi_cntrl->mhi_cmd);
> > +err_free_ch:
> > + kfree(mhi_cntrl->mhi_chan);
> > +
> > + return ret;
> > +}
> > +EXPORT_SYMBOL_GPL(mhi_ep_register_controller);
> > +
[...]
> > +struct mhi_ep_device {
> > + struct device dev;
> > + struct mhi_ep_cntrl *mhi_cntrl;
> > + const struct mhi_device_id *id;
> > + const char *name;
> > + struct mhi_ep_chan *ul_chan;
> > + struct mhi_ep_chan *dl_chan;
>
> Could the dev_type just be: bool controller?
>
Again, this is done the same way as host. Will change it later if needed.
> > + enum mhi_device_type dev_type;
> > + int ul_chan_id;
>
> Can't you ust use ul_chan->chan and dl_chan->chan?
>
> In any case, I think the channel ids should be u32.
>
This is not used for now as well. I'll just remove it.
Thanks,
Mani