2022-02-14 10:04:31

by Manivannan Sadhasivam

[permalink] [raw]
Subject: [PATCH v3 20/25] bus: mhi: ep: Add support for processing command ring

Add support for processing the command ring. Command ring is used by the
host to issue channel specific commands to the ep device. Following
commands are supported:

1. Start channel
2. Stop channel
3. Reset channel

Once the device receives the command doorbell interrupt from host, it
executes the command and generates a command completion event to the
host in the primary event ring.

Signed-off-by: Manivannan Sadhasivam <[email protected]>
---
drivers/bus/mhi/ep/main.c | 151 ++++++++++++++++++++++++++++++++++++++
1 file changed, 151 insertions(+)

diff --git a/drivers/bus/mhi/ep/main.c b/drivers/bus/mhi/ep/main.c
index 6378ac5c7e37..4c2ee517832c 100644
--- a/drivers/bus/mhi/ep/main.c
+++ b/drivers/bus/mhi/ep/main.c
@@ -21,6 +21,7 @@

static DEFINE_IDA(mhi_ep_cntrl_ida);

+static int mhi_ep_create_device(struct mhi_ep_cntrl *mhi_cntrl, u32 ch_id);
static int mhi_ep_destroy_device(struct device *dev, void *data);

static int mhi_ep_send_event(struct mhi_ep_cntrl *mhi_cntrl, u32 ring_idx,
@@ -185,6 +186,156 @@ void mhi_ep_unmap_free(struct mhi_ep_cntrl *mhi_cntrl, u64 pci_addr, phys_addr_t
mhi_cntrl->free_addr(mhi_cntrl, phys - offset, virt - offset, size);
}

+int mhi_ep_process_cmd_ring(struct mhi_ep_ring *ring, struct mhi_ep_ring_element *el)
+{
+ struct mhi_ep_cntrl *mhi_cntrl = ring->mhi_cntrl;
+ struct device *dev = &mhi_cntrl->mhi_dev->dev;
+ struct mhi_result result = {};
+ struct mhi_ep_chan *mhi_chan;
+ struct mhi_ep_ring *ch_ring;
+ u32 tmp, ch_id;
+ int ret;
+
+ ch_id = MHI_TRE_GET_CMD_CHID(el);
+ mhi_chan = &mhi_cntrl->mhi_chan[ch_id];
+ ch_ring = &mhi_cntrl->mhi_chan[ch_id].ring;
+
+ switch (MHI_TRE_GET_CMD_TYPE(el)) {
+ case MHI_PKT_TYPE_START_CHAN_CMD:
+ dev_dbg(dev, "Received START command for channel (%d)\n", ch_id);
+
+ mutex_lock(&mhi_chan->lock);
+ /* Initialize and configure the corresponding channel ring */
+ if (!ch_ring->started) {
+ ret = mhi_ep_ring_start(mhi_cntrl, ch_ring,
+ (union mhi_ep_ring_ctx *)&mhi_cntrl->ch_ctx_cache[ch_id]);
+ if (ret) {
+ dev_err(dev, "Failed to start ring for channel (%d)\n", ch_id);
+ ret = mhi_ep_send_cmd_comp_event(mhi_cntrl,
+ MHI_EV_CC_UNDEFINED_ERR);
+ if (ret)
+ dev_err(dev, "Error sending completion event (%d)\n",
+ MHI_EV_CC_UNDEFINED_ERR);
+
+ goto err_unlock;
+ }
+ }
+
+ /* Enable DB for the channel */
+ mhi_ep_mmio_enable_chdb_a7(mhi_cntrl, ch_id);
+
+ /* Set channel state to RUNNING */
+ mhi_chan->state = MHI_CH_STATE_RUNNING;
+ tmp = le32_to_cpu(mhi_cntrl->ch_ctx_cache[ch_id].chcfg);
+ tmp &= ~CHAN_CTX_CHSTATE_MASK;
+ tmp |= FIELD_PREP(CHAN_CTX_CHSTATE_MASK, MHI_CH_STATE_RUNNING);
+ mhi_cntrl->ch_ctx_cache[ch_id].chcfg = cpu_to_le32(tmp);
+
+ ret = mhi_ep_send_cmd_comp_event(mhi_cntrl, MHI_EV_CC_SUCCESS);
+ if (ret) {
+ dev_err(dev, "Error sending command completion event (%d)\n",
+ MHI_EV_CC_SUCCESS);
+ goto err_unlock;
+ }
+
+ mutex_unlock(&mhi_chan->lock);
+
+ /*
+ * Create MHI device only during UL channel start. Since the MHI
+ * channels operate in a pair, we'll associate both UL and DL
+ * channels to the same device.
+ *
+ * We also need to check for mhi_dev != NULL because, the host
+ * will issue START_CHAN command during resume and we don't
+ * destroy the device during suspend.
+ */
+ if (!(ch_id % 2) && !mhi_chan->mhi_dev) {
+ ret = mhi_ep_create_device(mhi_cntrl, ch_id);
+ if (ret) {
+ dev_err(dev, "Error creating device for channel (%d)\n", ch_id);
+ return ret;
+ }
+ }
+
+ break;
+ case MHI_PKT_TYPE_STOP_CHAN_CMD:
+ dev_dbg(dev, "Received STOP command for channel (%d)\n", ch_id);
+ if (!ch_ring->started) {
+ dev_err(dev, "Channel (%d) not opened\n", ch_id);
+ return -ENODEV;
+ }
+
+ mutex_lock(&mhi_chan->lock);
+ /* Disable DB for the channel */
+ mhi_ep_mmio_disable_chdb_a7(mhi_cntrl, ch_id);
+
+ /* Send channel disconnect status to client drivers */
+ result.transaction_status = -ENOTCONN;
+ result.bytes_xferd = 0;
+ mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result);
+
+ /* Set channel state to STOP */
+ mhi_chan->state = MHI_CH_STATE_STOP;
+ tmp = le32_to_cpu(mhi_cntrl->ch_ctx_cache[ch_id].chcfg);
+ tmp &= ~CHAN_CTX_CHSTATE_MASK;
+ tmp |= FIELD_PREP(CHAN_CTX_CHSTATE_MASK, MHI_CH_STATE_STOP);
+ mhi_cntrl->ch_ctx_cache[ch_id].chcfg = cpu_to_le32(tmp);
+
+ ret = mhi_ep_send_cmd_comp_event(mhi_cntrl, MHI_EV_CC_SUCCESS);
+ if (ret) {
+ dev_err(dev, "Error sending command completion event (%d)\n",
+ MHI_EV_CC_SUCCESS);
+ goto err_unlock;
+ }
+
+ mutex_unlock(&mhi_chan->lock);
+ break;
+ case MHI_PKT_TYPE_RESET_CHAN_CMD:
+ dev_dbg(dev, "Received STOP command for channel (%d)\n", ch_id);
+ if (!ch_ring->started) {
+ dev_err(dev, "Channel (%d) not opened\n", ch_id);
+ return -ENODEV;
+ }
+
+ mutex_lock(&mhi_chan->lock);
+ /* Stop and reset the transfer ring */
+ mhi_ep_ring_reset(mhi_cntrl, ch_ring);
+
+ /* Send channel disconnect status to client driver */
+ result.transaction_status = -ENOTCONN;
+ result.bytes_xferd = 0;
+ mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result);
+
+ /* Set channel state to DISABLED */
+ mhi_chan->state = MHI_CH_STATE_DISABLED;
+ tmp = le32_to_cpu(mhi_cntrl->ch_ctx_cache[ch_id].chcfg);
+ tmp &= ~CHAN_CTX_CHSTATE_MASK;
+ tmp |= FIELD_PREP(CHAN_CTX_CHSTATE_MASK, MHI_CH_STATE_DISABLED);
+ mhi_cntrl->ch_ctx_cache[ch_id].chcfg = cpu_to_le32(tmp);
+
+ ret = mhi_ep_send_cmd_comp_event(mhi_cntrl, MHI_EV_CC_SUCCESS);
+ if (ret) {
+ dev_err(dev, "Error sending command completion event (%d)\n",
+ MHI_EV_CC_SUCCESS);
+ goto err_unlock;
+ }
+
+ mutex_unlock(&mhi_chan->lock);
+ break;
+ default:
+ dev_err(dev, "Invalid command received: %d for channel (%d)\n",
+ MHI_TRE_GET_CMD_TYPE(el), ch_id);
+ return -EINVAL;
+ }
+
+ return 0;
+
+err_unlock:
+ mutex_unlock(&mhi_chan->lock);
+
+ return ret;
+}
+
static int mhi_ep_cache_host_cfg(struct mhi_ep_cntrl *mhi_cntrl)
{
struct device *dev = &mhi_cntrl->mhi_dev->dev;
--
2.25.1


2022-02-16 06:23:11

by Alex Elder

[permalink] [raw]
Subject: Re: [PATCH v3 20/25] bus: mhi: ep: Add support for processing command ring

On 2/12/22 12:21 PM, Manivannan Sadhasivam wrote:
> Add support for processing the command ring. Command ring is used by the
> host to issue channel specific commands to the ep device. Following
> commands are supported:
>
> 1. Start channel
> 2. Stop channel
> 3. Reset channel
>
> Once the device receives the command doorbell interrupt from host, it
> executes the command and generates a command completion event to the
> host in the primary event ring.
>
> Signed-off-by: Manivannan Sadhasivam <[email protected]>

I'll let you consider my few comments below, but whether or not you
address them, this looks OK to me.

Reviewed-by: Alex Elder <[email protected]>

> ---
> drivers/bus/mhi/ep/main.c | 151 ++++++++++++++++++++++++++++++++++++++
> 1 file changed, 151 insertions(+)
>
> diff --git a/drivers/bus/mhi/ep/main.c b/drivers/bus/mhi/ep/main.c
> index 6378ac5c7e37..4c2ee517832c 100644
> --- a/drivers/bus/mhi/ep/main.c
> +++ b/drivers/bus/mhi/ep/main.c
> @@ -21,6 +21,7 @@
>
> static DEFINE_IDA(mhi_ep_cntrl_ida);
>
> +static int mhi_ep_create_device(struct mhi_ep_cntrl *mhi_cntrl, u32 ch_id);
> static int mhi_ep_destroy_device(struct device *dev, void *data);
>
> static int mhi_ep_send_event(struct mhi_ep_cntrl *mhi_cntrl, u32 ring_idx,
> @@ -185,6 +186,156 @@ void mhi_ep_unmap_free(struct mhi_ep_cntrl *mhi_cntrl, u64 pci_addr, phys_addr_t
> mhi_cntrl->free_addr(mhi_cntrl, phys - offset, virt - offset, size);
> }
>
> +int mhi_ep_process_cmd_ring(struct mhi_ep_ring *ring, struct mhi_ep_ring_element *el)
> +{
> + struct mhi_ep_cntrl *mhi_cntrl = ring->mhi_cntrl;
> + struct device *dev = &mhi_cntrl->mhi_dev->dev;
> + struct mhi_result result = {};
> + struct mhi_ep_chan *mhi_chan;
> + struct mhi_ep_ring *ch_ring;
> + u32 tmp, ch_id;
> + int ret;
> +
> + ch_id = MHI_TRE_GET_CMD_CHID(el);
> + mhi_chan = &mhi_cntrl->mhi_chan[ch_id];
> + ch_ring = &mhi_cntrl->mhi_chan[ch_id].ring;
> +
> + switch (MHI_TRE_GET_CMD_TYPE(el)) {

No MHI_PKT_TYPE_NOOP_CMD?

> + case MHI_PKT_TYPE_START_CHAN_CMD:
> + dev_dbg(dev, "Received START command for channel (%d)\n", ch_id);
> +
> + mutex_lock(&mhi_chan->lock);
> + /* Initialize and configure the corresponding channel ring */
> + if (!ch_ring->started) {
> + ret = mhi_ep_ring_start(mhi_cntrl, ch_ring,
> + (union mhi_ep_ring_ctx *)&mhi_cntrl->ch_ctx_cache[ch_id]);
> + if (ret) {
> + dev_err(dev, "Failed to start ring for channel (%d)\n", ch_id);
> + ret = mhi_ep_send_cmd_comp_event(mhi_cntrl,
> + MHI_EV_CC_UNDEFINED_ERR);
> + if (ret)
> + dev_err(dev, "Error sending completion event (%d)\n",
> + MHI_EV_CC_UNDEFINED_ERR);

Print the value of ret in the above message (not UNDEFINED_ERR).

> +
> + goto err_unlock;
> + }
> + }
> +
> + /* Enable DB for the channel */
> + mhi_ep_mmio_enable_chdb_a7(mhi_cntrl, ch_id);

If an error occurs later, this will be enabled. Is that what
you want? Maybe wait to enable the doorbell until everything
else succeeds.

> +
> + /* Set channel state to RUNNING */
> + mhi_chan->state = MHI_CH_STATE_RUNNING;
> + tmp = le32_to_cpu(mhi_cntrl->ch_ctx_cache[ch_id].chcfg);
> + tmp &= ~CHAN_CTX_CHSTATE_MASK;
> + tmp |= FIELD_PREP(CHAN_CTX_CHSTATE_MASK, MHI_CH_STATE_RUNNING);
> + mhi_cntrl->ch_ctx_cache[ch_id].chcfg = cpu_to_le32(tmp);
> +
> + ret = mhi_ep_send_cmd_comp_event(mhi_cntrl, MHI_EV_CC_SUCCESS);
> + if (ret) {
> + dev_err(dev, "Error sending command completion event (%d)\n",
> + MHI_EV_CC_SUCCESS);
> + goto err_unlock;
> + }
> +
> + mutex_unlock(&mhi_chan->lock);
> +
> + /*
> + * Create MHI device only during UL channel start. Since the MHI
> + * channels operate in a pair, we'll associate both UL and DL
> + * channels to the same device.
> + *
> + * We also need to check for mhi_dev != NULL because, the host
> + * will issue START_CHAN command during resume and we don't
> + * destroy the device during suspend.
> + */
> + if (!(ch_id % 2) && !mhi_chan->mhi_dev) {
> + ret = mhi_ep_create_device(mhi_cntrl, ch_id);
> + if (ret) {

If this occurs, the host will already have been told the
request completed successfully. Is that a problem that
can/should be avoided?

> + dev_err(dev, "Error creating device for channel (%d)\n", ch_id);
> + return ret;
> + }
> + }
> +
> + break;
> + case MHI_PKT_TYPE_STOP_CHAN_CMD:
> + dev_dbg(dev, "Received STOP command for channel (%d)\n", ch_id);
> + if (!ch_ring->started) {
> + dev_err(dev, "Channel (%d) not opened\n", ch_id);
> + return -ENODEV;
> + }
> +
> + mutex_lock(&mhi_chan->lock);
> + /* Disable DB for the channel */
> + mhi_ep_mmio_disable_chdb_a7(mhi_cntrl, ch_id);
> +
> + /* Send channel disconnect status to client drivers */
> + result.transaction_status = -ENOTCONN;
> + result.bytes_xferd = 0;
> + mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result);
> +
> + /* Set channel state to STOP */
> + mhi_chan->state = MHI_CH_STATE_STOP;
> + tmp = le32_to_cpu(mhi_cntrl->ch_ctx_cache[ch_id].chcfg);
> + tmp &= ~CHAN_CTX_CHSTATE_MASK;
> + tmp |= FIELD_PREP(CHAN_CTX_CHSTATE_MASK, MHI_CH_STATE_STOP);
> + mhi_cntrl->ch_ctx_cache[ch_id].chcfg = cpu_to_le32(tmp);
> +
> + ret = mhi_ep_send_cmd_comp_event(mhi_cntrl, MHI_EV_CC_SUCCESS);
> + if (ret) {
> + dev_err(dev, "Error sending command completion event (%d)\n",
> + MHI_EV_CC_SUCCESS);
> + goto err_unlock;
> + }
> +
> + mutex_unlock(&mhi_chan->lock);
> + break;
> + case MHI_PKT_TYPE_RESET_CHAN_CMD:
> + dev_dbg(dev, "Received STOP command for channel (%d)\n", ch_id);
> + if (!ch_ring->started) {
> + dev_err(dev, "Channel (%d) not opened\n", ch_id);
> + return -ENODEV;
> + }
> +
> + mutex_lock(&mhi_chan->lock);
> + /* Stop and reset the transfer ring */
> + mhi_ep_ring_reset(mhi_cntrl, ch_ring);
> +
> + /* Send channel disconnect status to client driver */
> + result.transaction_status = -ENOTCONN;
> + result.bytes_xferd = 0;
> + mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result);
> +
> + /* Set channel state to DISABLED */
> + mhi_chan->state = MHI_CH_STATE_DISABLED;
> + tmp = le32_to_cpu(mhi_cntrl->ch_ctx_cache[ch_id].chcfg);
> + tmp &= ~CHAN_CTX_CHSTATE_MASK;
> + tmp |= FIELD_PREP(CHAN_CTX_CHSTATE_MASK, MHI_CH_STATE_DISABLED);
> + mhi_cntrl->ch_ctx_cache[ch_id].chcfg = cpu_to_le32(tmp);
> +
> + ret = mhi_ep_send_cmd_comp_event(mhi_cntrl, MHI_EV_CC_SUCCESS);
> + if (ret) {
> + dev_err(dev, "Error sending command completion event (%d)\n",
> + MHI_EV_CC_SUCCESS);
> + goto err_unlock;
> + }
> +
> + mutex_unlock(&mhi_chan->lock);
> + break;
> + default:
> + dev_err(dev, "Invalid command received: %d for channel (%d)\n",
> + MHI_TRE_GET_CMD_TYPE(el), ch_id);
> + return -EINVAL;
> + }
> +
> + return 0;
> +
> +err_unlock:
> + mutex_unlock(&mhi_chan->lock);
> +
> + return ret;
> +}
> +
> static int mhi_ep_cache_host_cfg(struct mhi_ep_cntrl *mhi_cntrl)
> {
> struct device *dev = &mhi_cntrl->mhi_dev->dev;

2022-02-22 13:20:10

by Manivannan Sadhasivam

[permalink] [raw]
Subject: Re: [PATCH v3 20/25] bus: mhi: ep: Add support for processing command ring

On Tue, Feb 15, 2022 at 04:40:01PM -0600, Alex Elder wrote:
> On 2/12/22 12:21 PM, Manivannan Sadhasivam wrote:
> > Add support for processing the command ring. Command ring is used by the
> > host to issue channel specific commands to the ep device. Following
> > commands are supported:
> >
> > 1. Start channel
> > 2. Stop channel
> > 3. Reset channel
> >
> > Once the device receives the command doorbell interrupt from host, it
> > executes the command and generates a command completion event to the
> > host in the primary event ring.
> >
> > Signed-off-by: Manivannan Sadhasivam <[email protected]>
>
> I'll let you consider my few comments below, but whether or not you
> address them, this looks OK to me.
>
> Reviewed-by: Alex Elder <[email protected]>
>
> > ---
> > drivers/bus/mhi/ep/main.c | 151 ++++++++++++++++++++++++++++++++++++++
> > 1 file changed, 151 insertions(+)
> >
> > diff --git a/drivers/bus/mhi/ep/main.c b/drivers/bus/mhi/ep/main.c
> > index 6378ac5c7e37..4c2ee517832c 100644
> > --- a/drivers/bus/mhi/ep/main.c
> > +++ b/drivers/bus/mhi/ep/main.c
> > @@ -21,6 +21,7 @@
> > static DEFINE_IDA(mhi_ep_cntrl_ida);
> > +static int mhi_ep_create_device(struct mhi_ep_cntrl *mhi_cntrl, u32 ch_id);
> > static int mhi_ep_destroy_device(struct device *dev, void *data);
> > static int mhi_ep_send_event(struct mhi_ep_cntrl *mhi_cntrl, u32 ring_idx,
> > @@ -185,6 +186,156 @@ void mhi_ep_unmap_free(struct mhi_ep_cntrl *mhi_cntrl, u64 pci_addr, phys_addr_t
> > mhi_cntrl->free_addr(mhi_cntrl, phys - offset, virt - offset, size);
> > }
> > +int mhi_ep_process_cmd_ring(struct mhi_ep_ring *ring, struct mhi_ep_ring_element *el)
> > +{
> > + struct mhi_ep_cntrl *mhi_cntrl = ring->mhi_cntrl;
> > + struct device *dev = &mhi_cntrl->mhi_dev->dev;
> > + struct mhi_result result = {};
> > + struct mhi_ep_chan *mhi_chan;
> > + struct mhi_ep_ring *ch_ring;
> > + u32 tmp, ch_id;
> > + int ret;
> > +
> > + ch_id = MHI_TRE_GET_CMD_CHID(el);
> > + mhi_chan = &mhi_cntrl->mhi_chan[ch_id];
> > + ch_ring = &mhi_cntrl->mhi_chan[ch_id].ring;
> > +
> > + switch (MHI_TRE_GET_CMD_TYPE(el)) {
>
> No MHI_PKT_TYPE_NOOP_CMD?
>

Not now.

> > + case MHI_PKT_TYPE_START_CHAN_CMD:
> > + dev_dbg(dev, "Received START command for channel (%d)\n", ch_id);
> > +
> > + mutex_lock(&mhi_chan->lock);
> > + /* Initialize and configure the corresponding channel ring */
> > + if (!ch_ring->started) {
> > + ret = mhi_ep_ring_start(mhi_cntrl, ch_ring,
> > + (union mhi_ep_ring_ctx *)&mhi_cntrl->ch_ctx_cache[ch_id]);
> > + if (ret) {
> > + dev_err(dev, "Failed to start ring for channel (%d)\n", ch_id);
> > + ret = mhi_ep_send_cmd_comp_event(mhi_cntrl,
> > + MHI_EV_CC_UNDEFINED_ERR);
> > + if (ret)
> > + dev_err(dev, "Error sending completion event (%d)\n",
> > + MHI_EV_CC_UNDEFINED_ERR);
>
> Print the value of ret in the above message (not UNDEFINED_ERR).
>
> > +
> > + goto err_unlock;
> > + }
> > + }
> > +
> > + /* Enable DB for the channel */
> > + mhi_ep_mmio_enable_chdb_a7(mhi_cntrl, ch_id);
>
> If an error occurs later, this will be enabled. Is that what
> you want? Maybe wait to enable the doorbell until everything
> else succeeds.
>

Makes sense. Will move this to the end.

> > +
> > + /* Set channel state to RUNNING */
> > + mhi_chan->state = MHI_CH_STATE_RUNNING;
> > + tmp = le32_to_cpu(mhi_cntrl->ch_ctx_cache[ch_id].chcfg);
> > + tmp &= ~CHAN_CTX_CHSTATE_MASK;
> > + tmp |= FIELD_PREP(CHAN_CTX_CHSTATE_MASK, MHI_CH_STATE_RUNNING);
> > + mhi_cntrl->ch_ctx_cache[ch_id].chcfg = cpu_to_le32(tmp);
> > +
> > + ret = mhi_ep_send_cmd_comp_event(mhi_cntrl, MHI_EV_CC_SUCCESS);
> > + if (ret) {
> > + dev_err(dev, "Error sending command completion event (%d)\n",
> > + MHI_EV_CC_SUCCESS);
> > + goto err_unlock;
> > + }
> > +
> > + mutex_unlock(&mhi_chan->lock);
> > +
> > + /*
> > + * Create MHI device only during UL channel start. Since the MHI
> > + * channels operate in a pair, we'll associate both UL and DL
> > + * channels to the same device.
> > + *
> > + * We also need to check for mhi_dev != NULL because, the host
> > + * will issue START_CHAN command during resume and we don't
> > + * destroy the device during suspend.
> > + */
> > + if (!(ch_id % 2) && !mhi_chan->mhi_dev) {
> > + ret = mhi_ep_create_device(mhi_cntrl, ch_id);
> > + if (ret) {
>
> If this occurs, the host will already have been told the
> request completed successfully. Is that a problem that
> can/should be avoided?
>

This should result in SYSERR. Will handle.

Thanks,
Mani