Devicetree
 help / color / mirror / Atom feed
* Re: [PATCH v3 1/3] dma: Support multiple interleaved frames with non-contiguous memory
From: Lars-Peter Clausen @ 2014-02-17  9:35 UTC (permalink / raw)
  To: Srikanth Thokala
  Cc: Vinod Koul, dan.j.williams-ral2JQCrhuEAvxtiuMwx3w,
	michal.simek-gjFFaj9aHVfQT0dZR+AlfA, Grant Likely,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, Levente Kurusa,
	andriy.shevchenko-b5Z7lJ3WibVrdx17CPfAsdBPR1lH4CV8,
	dmaengine-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <CA+mB=1K4_RpgfpWCfLy=PYOCzPThNf0Z6oSEK5Vrjh=W_YabhQ-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>

On 02/17/2014 10:29 AM, Srikanth Thokala wrote:
> On Mon, Feb 17, 2014 at 2:13 PM, Vinod Koul <vinod.koul-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org> wrote:
>> On Sat, Feb 15, 2014 at 05:30:17PM +0530, Srikanth Thokala wrote:
>>> The current implementation of interleaved DMA API support multiple
>>> frames only when the memory is contiguous by incrementing src_start/
>>> dst_start members of interleaved template.
>>>
>>> But, when the memory is non-contiguous it will restrict slave device
>>> to not submit multiple frames in a batch.  This patch handles this
>>> issue by allowing the slave device to send array of interleaved dma
>>> templates each having a different memory location.
>> This seems to be missing the numbers of templates you are sending, wouldnt this
>> require sending ARRAY_SiZE too?
>>
>> And why send double pointer?
>
> Array size is not required, when we pass the double pointer.  The last
> element would be
> pointed to NULL and we could get the number of templates from this condition.
> Here is an example snippet,
>
> In slave device driver,
>
>          struct dma_interleaved_template **xts;
>
>          xts = kcalloc(frm_cnt+1, sizeof(struct
> dma_interleaved_template *), GFP_KERNEL);
>          /* Error check for xts */
>          for (i = 0; i < frm_cnt; i++) {
>                  xts[i] = kmalloc(sizeof(struct
> dma_interleaved_template), GFP_KERNEL);
>                  /* Error check for xts[i] */
>          }
>          xts[i] = NULL;
>
> In DMA engine driver,  we could get the number of frames by,
>
>          for (; xts[frmno] != NULL; frmno++);
>
> I felt this way is simpler than adding an extra argument to the API.
> Please let me know
> your opinion and suggest me a better way.

I think Vinod's suggestion of passing in an array of interleaved_templates 
and the size of the array is better than what you are currently doing.

Btw. you also need to update the current implementations and users of the 
API accordingly.

>
>>
>> --
>> ~Vinod
>>
>>>
>>> Signed-off-by: Srikanth Thokala <sthokal-gjFFaj9aHVfQT0dZR+AlfA@public.gmane.org>
>>> ---
>>>   Documentation/dmaengine.txt              |    2 +-
>>>   drivers/dma/imx-dma.c                    |    3 ++-
>>>   drivers/dma/sirf-dma.c                   |    3 ++-
>>>   drivers/media/platform/m2m-deinterlace.c |    2 +-
>>>   include/linux/dmaengine.h                |    6 +++---
>>>   5 files changed, 9 insertions(+), 7 deletions(-)
>>>
>>> diff --git a/Documentation/dmaengine.txt b/Documentation/dmaengine.txt
>>> index 879b6e3..c642614 100644
>>> --- a/Documentation/dmaengine.txt
>>> +++ b/Documentation/dmaengine.txt
>>> @@ -94,7 +94,7 @@ The slave DMA usage consists of following steps:
>>>                size_t period_len, enum dma_data_direction direction);
>>>
>>>        struct dma_async_tx_descriptor *(*device_prep_interleaved_dma)(
>>> -             struct dma_chan *chan, struct dma_interleaved_template *xt,
>>> +             struct dma_chan *chan, struct dma_interleaved_template **xts,
>>>                unsigned long flags);
>>>
>>>      The peripheral driver is expected to have mapped the scatterlist for
>>> diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c
>>> index 6f9ac20..e2c52ce 100644
>>> --- a/drivers/dma/imx-dma.c
>>> +++ b/drivers/dma/imx-dma.c
>>> @@ -954,12 +954,13 @@ static struct dma_async_tx_descriptor *imxdma_prep_dma_memcpy(
>>>   }
>>>
>>>   static struct dma_async_tx_descriptor *imxdma_prep_dma_interleaved(
>>> -     struct dma_chan *chan, struct dma_interleaved_template *xt,
>>> +     struct dma_chan *chan, struct dma_interleaved_template **xts,
>>>        unsigned long flags)
>>>   {
>>>        struct imxdma_channel *imxdmac = to_imxdma_chan(chan);
>>>        struct imxdma_engine *imxdma = imxdmac->imxdma;
>>>        struct imxdma_desc *desc;
>>> +     struct dma_interleaved_template *xt = *xts;
>>>
>>>        dev_dbg(imxdma->dev, "%s channel: %d src_start=0x%llx dst_start=0x%llx\n"
>>>                "   src_sgl=%s dst_sgl=%s numf=%zu frame_size=%zu\n", __func__,
>>> diff --git a/drivers/dma/sirf-dma.c b/drivers/dma/sirf-dma.c
>>> index d4d3a31..b6a150b 100644
>>> --- a/drivers/dma/sirf-dma.c
>>> +++ b/drivers/dma/sirf-dma.c
>>> @@ -509,12 +509,13 @@ sirfsoc_dma_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
>>>   }
>>>
>>>   static struct dma_async_tx_descriptor *sirfsoc_dma_prep_interleaved(
>>> -     struct dma_chan *chan, struct dma_interleaved_template *xt,
>>> +     struct dma_chan *chan, struct dma_interleaved_template **xts,
>>>        unsigned long flags)
>>>   {
>>>        struct sirfsoc_dma *sdma = dma_chan_to_sirfsoc_dma(chan);
>>>        struct sirfsoc_dma_chan *schan = dma_chan_to_sirfsoc_dma_chan(chan);
>>>        struct sirfsoc_dma_desc *sdesc = NULL;
>>> +     struct dma_interleaved_template *xt = *xts;
>>>        unsigned long iflags;
>>>        int ret;
>>>
>>> diff --git a/drivers/media/platform/m2m-deinterlace.c b/drivers/media/platform/m2m-deinterlace.c
>>> index 6bb86b5..468110a 100644
>>> --- a/drivers/media/platform/m2m-deinterlace.c
>>> +++ b/drivers/media/platform/m2m-deinterlace.c
>>> @@ -343,7 +343,7 @@ static void deinterlace_issue_dma(struct deinterlace_ctx *ctx, int op,
>>>        ctx->xt->dst_sgl = true;
>>>        flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
>>>
>>> -     tx = dmadev->device_prep_interleaved_dma(chan, ctx->xt, flags);
>>> +     tx = dmadev->device_prep_interleaved_dma(chan, &ctx->xt, flags);
>>>        if (tx == NULL) {
>>>                v4l2_warn(&pcdev->v4l2_dev, "DMA interleaved prep error\n");
>>>                return;
>>> diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h
>>> index c5c92d5..2f77a9a 100644
>>> --- a/include/linux/dmaengine.h
>>> +++ b/include/linux/dmaengine.h
>>> @@ -675,7 +675,7 @@ struct dma_device {
>>>                size_t period_len, enum dma_transfer_direction direction,
>>>                unsigned long flags, void *context);
>>>        struct dma_async_tx_descriptor *(*device_prep_interleaved_dma)(
>>> -             struct dma_chan *chan, struct dma_interleaved_template *xt,
>>> +             struct dma_chan *chan, struct dma_interleaved_template **xts,
>>>                unsigned long flags);
>>>        int (*device_control)(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
>>>                unsigned long arg);
>>> @@ -752,10 +752,10 @@ static inline struct dma_async_tx_descriptor *dmaengine_prep_dma_cyclic(
>>>   }
>>>
>>>   static inline struct dma_async_tx_descriptor *dmaengine_prep_interleaved_dma(
>>> -             struct dma_chan *chan, struct dma_interleaved_template *xt,
>>> +             struct dma_chan *chan, struct dma_interleaved_template **xts,
>>>                unsigned long flags)
>>>   {
>>> -     return chan->device->device_prep_interleaved_dma(chan, xt, flags);
>>> +     return chan->device->device_prep_interleaved_dma(chan, xts, flags);
>>>   }
>>>
>>>   static inline int dma_get_slave_caps(struct dma_chan *chan, struct dma_slave_caps *caps)
>>> --
>>> 1.7.9.5
>>>
>>> --
>>> To unsubscribe from this list: send the line "unsubscribe dmaengine" in
>>> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
>>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>>
>> --
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
>> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>> Please read the FAQ at  http://www.tux.org/lkml/

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH] can: xilinx CAN controller support.
From: Marc Kleine-Budde @ 2014-02-17  9:37 UTC (permalink / raw)
  To: Kedareswara rao Appana, wg, michal.simek, grant.likely, robh+dt,
	linux-can
  Cc: netdev, linux-arm-kernel, linux-kernel, devicetree,
	Kedareswara rao Appana
In-Reply-To: <93c7272e-d8bf-43af-b39b-aaa0e0b70b6f@TX2EHSMHS036.ehs.local>

[-- Attachment #1: Type: text/plain, Size: 1227 bytes --]

On 02/17/2014 10:23 AM, Kedareswara rao Appana wrote:
> This patch adds xilinx CAN controller support.
> This driver supports both ZYNQ CANPS and Soft IP
> AXI CAN controller.

I just had a quick look at the driver:

[...]

> +/**
> + * xcan_tx_interrupt - Tx Done Isr
> + * @ndev:	net_device pointer
> + */
> +static void xcan_tx_interrupt(struct net_device *ndev)
> +{
> +	struct xcan_priv *priv = netdev_priv(ndev);
> +	struct net_device_stats *stats = &ndev->stats;
> +
> +	stats->tx_packets++;
> +	while (priv->tx_head - priv->tx_tail > 0) {

Note, there might be still CAN frames in the TX FIFO that have not been
transmitted yet. You have to check your hardware! What to do depends on
how you FIFO is organized.

> +		can_get_echo_skb(ndev, priv->tx_tail %
> +					priv->xcan_echo_skb_max_tx);
> +		priv->tx_tail++;
> +	}
> +
> +	netif_wake_queue(ndev);
> +	can_led_event(ndev, CAN_LED_EVENT_TX);
> +}

Marc

-- 
Pengutronix e.K.                  | Marc Kleine-Budde           |
Industrial Linux Solutions        | Phone: +49-231-2826-924     |
Vertretung West/Dortmund          | Fax:   +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686  | http://www.pengutronix.de   |


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 242 bytes --]

^ permalink raw reply

* Re: [PATCH v3 1/3] dma: Support multiple interleaved frames with non-contiguous memory
From: Srikanth Thokala @ 2014-02-17  9:42 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Srikanth Thokala, Vinod Koul,
	dan.j.williams-ral2JQCrhuEAvxtiuMwx3w,
	michal.simek-gjFFaj9aHVfQT0dZR+AlfA, Grant Likely,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, Levente Kurusa,
	andriy.shevchenko-b5Z7lJ3WibVrdx17CPfAsdBPR1lH4CV8,
	dmaengine-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <5301D7CD.5000405-Qo5EllUWu/uELgA04lAiVw@public.gmane.org>

On Mon, Feb 17, 2014 at 3:05 PM, Lars-Peter Clausen <lars-Qo5EllUWu/uELgA04lAiVw@public.gmane.org> wrote:
> On 02/17/2014 10:29 AM, Srikanth Thokala wrote:
>>
>> On Mon, Feb 17, 2014 at 2:13 PM, Vinod Koul <vinod.koul-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org> wrote:
>>>
>>> On Sat, Feb 15, 2014 at 05:30:17PM +0530, Srikanth Thokala wrote:
>>>>
>>>> The current implementation of interleaved DMA API support multiple
>>>> frames only when the memory is contiguous by incrementing src_start/
>>>> dst_start members of interleaved template.
>>>>
>>>> But, when the memory is non-contiguous it will restrict slave device
>>>> to not submit multiple frames in a batch.  This patch handles this
>>>> issue by allowing the slave device to send array of interleaved dma
>>>> templates each having a different memory location.
>>>
>>> This seems to be missing the numbers of templates you are sending,
>>> wouldnt this
>>> require sending ARRAY_SiZE too?
>>>
>>> And why send double pointer?
>>
>>
>> Array size is not required, when we pass the double pointer.  The last
>> element would be
>> pointed to NULL and we could get the number of templates from this
>> condition.
>> Here is an example snippet,
>>
>> In slave device driver,
>>
>>          struct dma_interleaved_template **xts;
>>
>>          xts = kcalloc(frm_cnt+1, sizeof(struct
>> dma_interleaved_template *), GFP_KERNEL);
>>          /* Error check for xts */
>>          for (i = 0; i < frm_cnt; i++) {
>>                  xts[i] = kmalloc(sizeof(struct
>> dma_interleaved_template), GFP_KERNEL);
>>                  /* Error check for xts[i] */
>>          }
>>          xts[i] = NULL;
>>
>> In DMA engine driver,  we could get the number of frames by,
>>
>>          for (; xts[frmno] != NULL; frmno++);
>>
>> I felt this way is simpler than adding an extra argument to the API.
>> Please let me know
>> your opinion and suggest me a better way.
>
>
> I think Vinod's suggestion of passing in an array of interleaved_templates
> and the size of the array is better than what you are currently doing.

Ok, Lars.  I will update with this in my v4. Thanks.

>
> Btw. you also need to update the current implementations and users of the
> API accordingly.

Yes, I have updated them in this patch.

Thanks
Srikanth


>
>
>>
>>>
>>> --
>>> ~Vinod
>>>
>>>>
>>>> Signed-off-by: Srikanth Thokala <sthokal-gjFFaj9aHVfQT0dZR+AlfA@public.gmane.org>
>>>> ---
>>>>   Documentation/dmaengine.txt              |    2 +-
>>>>   drivers/dma/imx-dma.c                    |    3 ++-
>>>>   drivers/dma/sirf-dma.c                   |    3 ++-
>>>>   drivers/media/platform/m2m-deinterlace.c |    2 +-
>>>>   include/linux/dmaengine.h                |    6 +++---
>>>>   5 files changed, 9 insertions(+), 7 deletions(-)
>>>>
>>>> diff --git a/Documentation/dmaengine.txt b/Documentation/dmaengine.txt
>>>> index 879b6e3..c642614 100644
>>>> --- a/Documentation/dmaengine.txt
>>>> +++ b/Documentation/dmaengine.txt
>>>> @@ -94,7 +94,7 @@ The slave DMA usage consists of following steps:
>>>>                size_t period_len, enum dma_data_direction direction);
>>>>
>>>>        struct dma_async_tx_descriptor *(*device_prep_interleaved_dma)(
>>>> -             struct dma_chan *chan, struct dma_interleaved_template
>>>> *xt,
>>>> +             struct dma_chan *chan, struct dma_interleaved_template
>>>> **xts,
>>>>                unsigned long flags);
>>>>
>>>>      The peripheral driver is expected to have mapped the scatterlist
>>>> for
>>>> diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c
>>>> index 6f9ac20..e2c52ce 100644
>>>> --- a/drivers/dma/imx-dma.c
>>>> +++ b/drivers/dma/imx-dma.c
>>>> @@ -954,12 +954,13 @@ static struct dma_async_tx_descriptor
>>>> *imxdma_prep_dma_memcpy(
>>>>   }
>>>>
>>>>   static struct dma_async_tx_descriptor *imxdma_prep_dma_interleaved(
>>>> -     struct dma_chan *chan, struct dma_interleaved_template *xt,
>>>> +     struct dma_chan *chan, struct dma_interleaved_template **xts,
>>>>        unsigned long flags)
>>>>   {
>>>>        struct imxdma_channel *imxdmac = to_imxdma_chan(chan);
>>>>        struct imxdma_engine *imxdma = imxdmac->imxdma;
>>>>        struct imxdma_desc *desc;
>>>> +     struct dma_interleaved_template *xt = *xts;
>>>>
>>>>        dev_dbg(imxdma->dev, "%s channel: %d src_start=0x%llx
>>>> dst_start=0x%llx\n"
>>>>                "   src_sgl=%s dst_sgl=%s numf=%zu frame_size=%zu\n",
>>>> __func__,
>>>> diff --git a/drivers/dma/sirf-dma.c b/drivers/dma/sirf-dma.c
>>>> index d4d3a31..b6a150b 100644
>>>> --- a/drivers/dma/sirf-dma.c
>>>> +++ b/drivers/dma/sirf-dma.c
>>>> @@ -509,12 +509,13 @@ sirfsoc_dma_tx_status(struct dma_chan *chan,
>>>> dma_cookie_t cookie,
>>>>   }
>>>>
>>>>   static struct dma_async_tx_descriptor *sirfsoc_dma_prep_interleaved(
>>>> -     struct dma_chan *chan, struct dma_interleaved_template *xt,
>>>> +     struct dma_chan *chan, struct dma_interleaved_template **xts,
>>>>        unsigned long flags)
>>>>   {
>>>>        struct sirfsoc_dma *sdma = dma_chan_to_sirfsoc_dma(chan);
>>>>        struct sirfsoc_dma_chan *schan =
>>>> dma_chan_to_sirfsoc_dma_chan(chan);
>>>>        struct sirfsoc_dma_desc *sdesc = NULL;
>>>> +     struct dma_interleaved_template *xt = *xts;
>>>>        unsigned long iflags;
>>>>        int ret;
>>>>
>>>> diff --git a/drivers/media/platform/m2m-deinterlace.c
>>>> b/drivers/media/platform/m2m-deinterlace.c
>>>> index 6bb86b5..468110a 100644
>>>> --- a/drivers/media/platform/m2m-deinterlace.c
>>>> +++ b/drivers/media/platform/m2m-deinterlace.c
>>>> @@ -343,7 +343,7 @@ static void deinterlace_issue_dma(struct
>>>> deinterlace_ctx *ctx, int op,
>>>>        ctx->xt->dst_sgl = true;
>>>>        flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
>>>>
>>>> -     tx = dmadev->device_prep_interleaved_dma(chan, ctx->xt, flags);
>>>> +     tx = dmadev->device_prep_interleaved_dma(chan, &ctx->xt, flags);
>>>>        if (tx == NULL) {
>>>>                v4l2_warn(&pcdev->v4l2_dev, "DMA interleaved prep
>>>> error\n");
>>>>                return;
>>>> diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h
>>>> index c5c92d5..2f77a9a 100644
>>>> --- a/include/linux/dmaengine.h
>>>> +++ b/include/linux/dmaengine.h
>>>> @@ -675,7 +675,7 @@ struct dma_device {
>>>>                size_t period_len, enum dma_transfer_direction direction,
>>>>                unsigned long flags, void *context);
>>>>        struct dma_async_tx_descriptor *(*device_prep_interleaved_dma)(
>>>> -             struct dma_chan *chan, struct dma_interleaved_template
>>>> *xt,
>>>> +             struct dma_chan *chan, struct dma_interleaved_template
>>>> **xts,
>>>>                unsigned long flags);
>>>>        int (*device_control)(struct dma_chan *chan, enum dma_ctrl_cmd
>>>> cmd,
>>>>                unsigned long arg);
>>>> @@ -752,10 +752,10 @@ static inline struct dma_async_tx_descriptor
>>>> *dmaengine_prep_dma_cyclic(
>>>>   }
>>>>
>>>>   static inline struct dma_async_tx_descriptor
>>>> *dmaengine_prep_interleaved_dma(
>>>> -             struct dma_chan *chan, struct dma_interleaved_template
>>>> *xt,
>>>> +             struct dma_chan *chan, struct dma_interleaved_template
>>>> **xts,
>>>>                unsigned long flags)
>>>>   {
>>>> -     return chan->device->device_prep_interleaved_dma(chan, xt, flags);
>>>> +     return chan->device->device_prep_interleaved_dma(chan, xts,
>>>> flags);
>>>>   }
>>>>
>>>>   static inline int dma_get_slave_caps(struct dma_chan *chan, struct
>>>> dma_slave_caps *caps)
>>>> --
>>>> 1.7.9.5
>>>>
>>>> --
>>>> To unsubscribe from this list: send the line "unsubscribe dmaengine" in
>>>> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
>>>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>>>
>>>
>>> --
>>> --
>>> To unsubscribe from this list: send the line "unsubscribe linux-kernel"
>>> in
>>> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
>>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>>> Please read the FAQ at  http://www.tux.org/lkml/
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH] can: xilinx CAN controller support.
From: Marc Kleine-Budde @ 2014-02-17  9:42 UTC (permalink / raw)
  To: Kedareswara rao Appana, wg, michal.simek, grant.likely, robh+dt,
	linux-can
  Cc: netdev, linux-arm-kernel, linux-kernel, devicetree,
	Kedareswara rao Appana
In-Reply-To: <93c7272e-d8bf-43af-b39b-aaa0e0b70b6f@TX2EHSMHS036.ehs.local>

[-- Attachment #1: Type: text/plain, Size: 1331 bytes --]

On 02/17/2014 10:23 AM, Kedareswara rao Appana wrote:
> +/**
> + * xcan_get_berr_counter - error counter routine
> + * @ndev:	Pointer to net_device structure
> + * @bec:	Pointer to can_berr_counter structure
> + *
> + * This is the driver error counter routine.
> + * Return: 0 always
> + */
> +static int xcan_get_berr_counter(const struct net_device *ndev,
> +					struct can_berr_counter *bec)
> +{
> +	struct xcan_priv *priv = netdev_priv(ndev);
> +	int ret;
> +
> +	ret = clk_prepare_enable(priv->devclk);
> +	if (ret)
> +		goto err;
> +
> +	ret = clk_prepare_enable(priv->aperclk);
> +	if (ret)
> +		goto err_clk;
> +
> +	bec->txerr = priv->read_reg(priv, XCAN_ECR_OFFSET) & XCAN_ECR_TEC_MASK;
> +	bec->rxerr = ((priv->read_reg(priv, XCAN_ECR_OFFSET) &
> +			XCAN_ECR_REC_MASK) >> XCAN_ESR_REC_SHIFT);

You have to disable the clock when leaving this function. Otherwise the
clocks will be unbalanced.

> +	return 0;
> +
> +err_clk:
> +	clk_disable_unprepare(priv->devclk);
> +err:
> +	return ret;
> +}

Marc

-- 
Pengutronix e.K.                  | Marc Kleine-Budde           |
Industrial Linux Solutions        | Phone: +49-231-2826-924     |
Vertretung West/Dortmund          | Fax:   +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686  | http://www.pengutronix.de   |


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 242 bytes --]

^ permalink raw reply

* Re: [PATCH v3 1/3] dma: Support multiple interleaved frames with non-contiguous memory
From: Lars-Peter Clausen @ 2014-02-17  9:44 UTC (permalink / raw)
  To: Srikanth Thokala
  Cc: Vinod Koul, dan.j.williams, michal.simek, Grant Likely, robh+dt,
	Levente Kurusa, andriy.shevchenko, dmaengine, linux-arm-kernel,
	linux-kernel@vger.kernel.org, devicetree
In-Reply-To: <CA+mB=1LjuVKYWZCaje14UtHRZ4UoLWEhYV05XyjUMgM=uKH2_A@mail.gmail.com>

On 02/17/2014 10:42 AM, Srikanth Thokala wrote:
> On Mon, Feb 17, 2014 at 3:05 PM, Lars-Peter Clausen <lars@metafoo.de> wrote:
>> On 02/17/2014 10:29 AM, Srikanth Thokala wrote:
>>>
>>> On Mon, Feb 17, 2014 at 2:13 PM, Vinod Koul <vinod.koul@intel.com> wrote:
>>>>
>>>> On Sat, Feb 15, 2014 at 05:30:17PM +0530, Srikanth Thokala wrote:
>>>>>
>>>>> The current implementation of interleaved DMA API support multiple
>>>>> frames only when the memory is contiguous by incrementing src_start/
>>>>> dst_start members of interleaved template.
>>>>>
>>>>> But, when the memory is non-contiguous it will restrict slave device
>>>>> to not submit multiple frames in a batch.  This patch handles this
>>>>> issue by allowing the slave device to send array of interleaved dma
>>>>> templates each having a different memory location.
>>>>
>>>> This seems to be missing the numbers of templates you are sending,
>>>> wouldnt this
>>>> require sending ARRAY_SiZE too?
>>>>
>>>> And why send double pointer?
>>>
>>>
>>> Array size is not required, when we pass the double pointer.  The last
>>> element would be
>>> pointed to NULL and we could get the number of templates from this
>>> condition.
>>> Here is an example snippet,
>>>
>>> In slave device driver,
>>>
>>>           struct dma_interleaved_template **xts;
>>>
>>>           xts = kcalloc(frm_cnt+1, sizeof(struct
>>> dma_interleaved_template *), GFP_KERNEL);
>>>           /* Error check for xts */
>>>           for (i = 0; i < frm_cnt; i++) {
>>>                   xts[i] = kmalloc(sizeof(struct
>>> dma_interleaved_template), GFP_KERNEL);
>>>                   /* Error check for xts[i] */
>>>           }
>>>           xts[i] = NULL;
>>>
>>> In DMA engine driver,  we could get the number of frames by,
>>>
>>>           for (; xts[frmno] != NULL; frmno++);
>>>
>>> I felt this way is simpler than adding an extra argument to the API.
>>> Please let me know
>>> your opinion and suggest me a better way.
>>
>>
>> I think Vinod's suggestion of passing in an array of interleaved_templates
>> and the size of the array is better than what you are currently doing.
>
> Ok, Lars.  I will update with this in my v4. Thanks.
>
>>
>> Btw. you also need to update the current implementations and users of the
>> API accordingly.
>
> Yes, I have updated them in this patch.

But you didn't update them accordingly to your proposed semantics. The 
caller didn't NULL terminate the array and the drivers did blindly assume 
there will always be exactly one transfer.

>
> Thanks
> Srikanth
>
>
>>
>>
>>>
>>>>
>>>> --
>>>> ~Vinod
>>>>
>>>>>
>>>>> Signed-off-by: Srikanth Thokala <sthokal@xilinx.com>
>>>>> ---
>>>>>    Documentation/dmaengine.txt              |    2 +-
>>>>>    drivers/dma/imx-dma.c                    |    3 ++-
>>>>>    drivers/dma/sirf-dma.c                   |    3 ++-
>>>>>    drivers/media/platform/m2m-deinterlace.c |    2 +-
>>>>>    include/linux/dmaengine.h                |    6 +++---
>>>>>    5 files changed, 9 insertions(+), 7 deletions(-)
>>>>>
>>>>> diff --git a/Documentation/dmaengine.txt b/Documentation/dmaengine.txt
>>>>> index 879b6e3..c642614 100644
>>>>> --- a/Documentation/dmaengine.txt
>>>>> +++ b/Documentation/dmaengine.txt
>>>>> @@ -94,7 +94,7 @@ The slave DMA usage consists of following steps:
>>>>>                 size_t period_len, enum dma_data_direction direction);
>>>>>
>>>>>         struct dma_async_tx_descriptor *(*device_prep_interleaved_dma)(
>>>>> -             struct dma_chan *chan, struct dma_interleaved_template
>>>>> *xt,
>>>>> +             struct dma_chan *chan, struct dma_interleaved_template
>>>>> **xts,
>>>>>                 unsigned long flags);
>>>>>
>>>>>       The peripheral driver is expected to have mapped the scatterlist
>>>>> for
>>>>> diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c
>>>>> index 6f9ac20..e2c52ce 100644
>>>>> --- a/drivers/dma/imx-dma.c
>>>>> +++ b/drivers/dma/imx-dma.c
>>>>> @@ -954,12 +954,13 @@ static struct dma_async_tx_descriptor
>>>>> *imxdma_prep_dma_memcpy(
>>>>>    }
>>>>>
>>>>>    static struct dma_async_tx_descriptor *imxdma_prep_dma_interleaved(
>>>>> -     struct dma_chan *chan, struct dma_interleaved_template *xt,
>>>>> +     struct dma_chan *chan, struct dma_interleaved_template **xts,
>>>>>         unsigned long flags)
>>>>>    {
>>>>>         struct imxdma_channel *imxdmac = to_imxdma_chan(chan);
>>>>>         struct imxdma_engine *imxdma = imxdmac->imxdma;
>>>>>         struct imxdma_desc *desc;
>>>>> +     struct dma_interleaved_template *xt = *xts;
>>>>>
>>>>>         dev_dbg(imxdma->dev, "%s channel: %d src_start=0x%llx
>>>>> dst_start=0x%llx\n"
>>>>>                 "   src_sgl=%s dst_sgl=%s numf=%zu frame_size=%zu\n",
>>>>> __func__,
>>>>> diff --git a/drivers/dma/sirf-dma.c b/drivers/dma/sirf-dma.c
>>>>> index d4d3a31..b6a150b 100644
>>>>> --- a/drivers/dma/sirf-dma.c
>>>>> +++ b/drivers/dma/sirf-dma.c
>>>>> @@ -509,12 +509,13 @@ sirfsoc_dma_tx_status(struct dma_chan *chan,
>>>>> dma_cookie_t cookie,
>>>>>    }
>>>>>
>>>>>    static struct dma_async_tx_descriptor *sirfsoc_dma_prep_interleaved(
>>>>> -     struct dma_chan *chan, struct dma_interleaved_template *xt,
>>>>> +     struct dma_chan *chan, struct dma_interleaved_template **xts,
>>>>>         unsigned long flags)
>>>>>    {
>>>>>         struct sirfsoc_dma *sdma = dma_chan_to_sirfsoc_dma(chan);
>>>>>         struct sirfsoc_dma_chan *schan =
>>>>> dma_chan_to_sirfsoc_dma_chan(chan);
>>>>>         struct sirfsoc_dma_desc *sdesc = NULL;
>>>>> +     struct dma_interleaved_template *xt = *xts;
>>>>>         unsigned long iflags;
>>>>>         int ret;
>>>>>
>>>>> diff --git a/drivers/media/platform/m2m-deinterlace.c
>>>>> b/drivers/media/platform/m2m-deinterlace.c
>>>>> index 6bb86b5..468110a 100644
>>>>> --- a/drivers/media/platform/m2m-deinterlace.c
>>>>> +++ b/drivers/media/platform/m2m-deinterlace.c
>>>>> @@ -343,7 +343,7 @@ static void deinterlace_issue_dma(struct
>>>>> deinterlace_ctx *ctx, int op,
>>>>>         ctx->xt->dst_sgl = true;
>>>>>         flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
>>>>>
>>>>> -     tx = dmadev->device_prep_interleaved_dma(chan, ctx->xt, flags);
>>>>> +     tx = dmadev->device_prep_interleaved_dma(chan, &ctx->xt, flags);
>>>>>         if (tx == NULL) {
>>>>>                 v4l2_warn(&pcdev->v4l2_dev, "DMA interleaved prep
>>>>> error\n");
>>>>>                 return;
>>>>> diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h
>>>>> index c5c92d5..2f77a9a 100644
>>>>> --- a/include/linux/dmaengine.h
>>>>> +++ b/include/linux/dmaengine.h
>>>>> @@ -675,7 +675,7 @@ struct dma_device {
>>>>>                 size_t period_len, enum dma_transfer_direction direction,
>>>>>                 unsigned long flags, void *context);
>>>>>         struct dma_async_tx_descriptor *(*device_prep_interleaved_dma)(
>>>>> -             struct dma_chan *chan, struct dma_interleaved_template
>>>>> *xt,
>>>>> +             struct dma_chan *chan, struct dma_interleaved_template
>>>>> **xts,
>>>>>                 unsigned long flags);
>>>>>         int (*device_control)(struct dma_chan *chan, enum dma_ctrl_cmd
>>>>> cmd,
>>>>>                 unsigned long arg);
>>>>> @@ -752,10 +752,10 @@ static inline struct dma_async_tx_descriptor
>>>>> *dmaengine_prep_dma_cyclic(
>>>>>    }
>>>>>
>>>>>    static inline struct dma_async_tx_descriptor
>>>>> *dmaengine_prep_interleaved_dma(
>>>>> -             struct dma_chan *chan, struct dma_interleaved_template
>>>>> *xt,
>>>>> +             struct dma_chan *chan, struct dma_interleaved_template
>>>>> **xts,
>>>>>                 unsigned long flags)
>>>>>    {
>>>>> -     return chan->device->device_prep_interleaved_dma(chan, xt, flags);
>>>>> +     return chan->device->device_prep_interleaved_dma(chan, xts,
>>>>> flags);
>>>>>    }
>>>>>
>>>>>    static inline int dma_get_slave_caps(struct dma_chan *chan, struct
>>>>> dma_slave_caps *caps)
>>>>> --
>>>>> 1.7.9.5
>>>>>
>>>>> --
>>>>> To unsubscribe from this list: send the line "unsubscribe dmaengine" in
>>>>> the body of a message to majordomo@vger.kernel.org
>>>>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>>>>
>>>>
>>>> --
>>>> --
>>>> To unsubscribe from this list: send the line "unsubscribe linux-kernel"
>>>> in
>>>> the body of a message to majordomo@vger.kernel.org
>>>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>>>> Please read the FAQ at  http://www.tux.org/lkml/
>>
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>> Please read the FAQ at  http://www.tux.org/lkml/

^ permalink raw reply

* Re: [PATCH v3 1/3] dma: Support multiple interleaved frames with non-contiguous memory
From: Srikanth Thokala @ 2014-02-17  9:52 UTC (permalink / raw)
  To: Lars-Peter Clausen
  Cc: Srikanth Thokala, Vinod Koul,
	dan.j.williams-ral2JQCrhuEAvxtiuMwx3w,
	michal.simek-gjFFaj9aHVfQT0dZR+AlfA, Grant Likely,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, Levente Kurusa,
	andriy.shevchenko-b5Z7lJ3WibVrdx17CPfAsdBPR1lH4CV8,
	dmaengine-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <5301DA0A.8030400-Qo5EllUWu/uELgA04lAiVw@public.gmane.org>

On Mon, Feb 17, 2014 at 3:14 PM, Lars-Peter Clausen <lars-Qo5EllUWu/uELgA04lAiVw@public.gmane.org> wrote:
> On 02/17/2014 10:42 AM, Srikanth Thokala wrote:
>>
>> On Mon, Feb 17, 2014 at 3:05 PM, Lars-Peter Clausen <lars-Qo5EllUWu/uELgA04lAiVw@public.gmane.org>
>> wrote:
>>>
>>> On 02/17/2014 10:29 AM, Srikanth Thokala wrote:
>>>>
>>>>
>>>> On Mon, Feb 17, 2014 at 2:13 PM, Vinod Koul <vinod.koul-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
>>>> wrote:
>>>>>
>>>>>
>>>>> On Sat, Feb 15, 2014 at 05:30:17PM +0530, Srikanth Thokala wrote:
>>>>>>
>>>>>>
>>>>>> The current implementation of interleaved DMA API support multiple
>>>>>> frames only when the memory is contiguous by incrementing src_start/
>>>>>> dst_start members of interleaved template.
>>>>>>
>>>>>> But, when the memory is non-contiguous it will restrict slave device
>>>>>> to not submit multiple frames in a batch.  This patch handles this
>>>>>> issue by allowing the slave device to send array of interleaved dma
>>>>>> templates each having a different memory location.
>>>>>
>>>>>
>>>>> This seems to be missing the numbers of templates you are sending,
>>>>> wouldnt this
>>>>> require sending ARRAY_SiZE too?
>>>>>
>>>>> And why send double pointer?
>>>>
>>>>
>>>>
>>>> Array size is not required, when we pass the double pointer.  The last
>>>> element would be
>>>> pointed to NULL and we could get the number of templates from this
>>>> condition.
>>>> Here is an example snippet,
>>>>
>>>> In slave device driver,
>>>>
>>>>           struct dma_interleaved_template **xts;
>>>>
>>>>           xts = kcalloc(frm_cnt+1, sizeof(struct
>>>> dma_interleaved_template *), GFP_KERNEL);
>>>>           /* Error check for xts */
>>>>           for (i = 0; i < frm_cnt; i++) {
>>>>                   xts[i] = kmalloc(sizeof(struct
>>>> dma_interleaved_template), GFP_KERNEL);
>>>>                   /* Error check for xts[i] */
>>>>           }
>>>>           xts[i] = NULL;
>>>>
>>>> In DMA engine driver,  we could get the number of frames by,
>>>>
>>>>           for (; xts[frmno] != NULL; frmno++);
>>>>
>>>> I felt this way is simpler than adding an extra argument to the API.
>>>> Please let me know
>>>> your opinion and suggest me a better way.
>>>
>>>
>>>
>>> I think Vinod's suggestion of passing in an array of
>>> interleaved_templates
>>> and the size of the array is better than what you are currently doing.
>>
>>
>> Ok, Lars.  I will update with this in my v4. Thanks.
>>
>>>
>>> Btw. you also need to update the current implementations and users of the
>>> API accordingly.
>>
>>
>> Yes, I have updated them in this patch.
>
>
> But you didn't update them accordingly to your proposed semantics. The
> caller didn't NULL terminate the array and the drivers did blindly assume
> there will always be exactly one transfer.

Ok, got it.  I will correct in v4.  Thanks for pointing this.

Srikanth

>
>
>>
>> Thanks
>> Srikanth
>>
>>
>>>
>>>
>>>>
>>>>>
>>>>> --
>>>>> ~Vinod
>>>>>
>>>>>>
>>>>>> Signed-off-by: Srikanth Thokala <sthokal-gjFFaj9aHVfQT0dZR+AlfA@public.gmane.org>
>>>>>> ---
>>>>>>    Documentation/dmaengine.txt              |    2 +-
>>>>>>    drivers/dma/imx-dma.c                    |    3 ++-
>>>>>>    drivers/dma/sirf-dma.c                   |    3 ++-
>>>>>>    drivers/media/platform/m2m-deinterlace.c |    2 +-
>>>>>>    include/linux/dmaengine.h                |    6 +++---
>>>>>>    5 files changed, 9 insertions(+), 7 deletions(-)
>>>>>>
>>>>>> diff --git a/Documentation/dmaengine.txt b/Documentation/dmaengine.txt
>>>>>> index 879b6e3..c642614 100644
>>>>>> --- a/Documentation/dmaengine.txt
>>>>>> +++ b/Documentation/dmaengine.txt
>>>>>> @@ -94,7 +94,7 @@ The slave DMA usage consists of following steps:
>>>>>>                 size_t period_len, enum dma_data_direction direction);
>>>>>>
>>>>>>         struct dma_async_tx_descriptor
>>>>>> *(*device_prep_interleaved_dma)(
>>>>>> -             struct dma_chan *chan, struct dma_interleaved_template
>>>>>> *xt,
>>>>>> +             struct dma_chan *chan, struct dma_interleaved_template
>>>>>> **xts,
>>>>>>                 unsigned long flags);
>>>>>>
>>>>>>       The peripheral driver is expected to have mapped the scatterlist
>>>>>> for
>>>>>> diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c
>>>>>> index 6f9ac20..e2c52ce 100644
>>>>>> --- a/drivers/dma/imx-dma.c
>>>>>> +++ b/drivers/dma/imx-dma.c
>>>>>> @@ -954,12 +954,13 @@ static struct dma_async_tx_descriptor
>>>>>> *imxdma_prep_dma_memcpy(
>>>>>>    }
>>>>>>
>>>>>>    static struct dma_async_tx_descriptor *imxdma_prep_dma_interleaved(
>>>>>> -     struct dma_chan *chan, struct dma_interleaved_template *xt,
>>>>>> +     struct dma_chan *chan, struct dma_interleaved_template **xts,
>>>>>>         unsigned long flags)
>>>>>>    {
>>>>>>         struct imxdma_channel *imxdmac = to_imxdma_chan(chan);
>>>>>>         struct imxdma_engine *imxdma = imxdmac->imxdma;
>>>>>>         struct imxdma_desc *desc;
>>>>>> +     struct dma_interleaved_template *xt = *xts;
>>>>>>
>>>>>>         dev_dbg(imxdma->dev, "%s channel: %d src_start=0x%llx
>>>>>> dst_start=0x%llx\n"
>>>>>>                 "   src_sgl=%s dst_sgl=%s numf=%zu frame_size=%zu\n",
>>>>>> __func__,
>>>>>> diff --git a/drivers/dma/sirf-dma.c b/drivers/dma/sirf-dma.c
>>>>>> index d4d3a31..b6a150b 100644
>>>>>> --- a/drivers/dma/sirf-dma.c
>>>>>> +++ b/drivers/dma/sirf-dma.c
>>>>>> @@ -509,12 +509,13 @@ sirfsoc_dma_tx_status(struct dma_chan *chan,
>>>>>> dma_cookie_t cookie,
>>>>>>    }
>>>>>>
>>>>>>    static struct dma_async_tx_descriptor
>>>>>> *sirfsoc_dma_prep_interleaved(
>>>>>> -     struct dma_chan *chan, struct dma_interleaved_template *xt,
>>>>>> +     struct dma_chan *chan, struct dma_interleaved_template **xts,
>>>>>>         unsigned long flags)
>>>>>>    {
>>>>>>         struct sirfsoc_dma *sdma = dma_chan_to_sirfsoc_dma(chan);
>>>>>>         struct sirfsoc_dma_chan *schan =
>>>>>> dma_chan_to_sirfsoc_dma_chan(chan);
>>>>>>         struct sirfsoc_dma_desc *sdesc = NULL;
>>>>>> +     struct dma_interleaved_template *xt = *xts;
>>>>>>         unsigned long iflags;
>>>>>>         int ret;
>>>>>>
>>>>>> diff --git a/drivers/media/platform/m2m-deinterlace.c
>>>>>> b/drivers/media/platform/m2m-deinterlace.c
>>>>>> index 6bb86b5..468110a 100644
>>>>>> --- a/drivers/media/platform/m2m-deinterlace.c
>>>>>> +++ b/drivers/media/platform/m2m-deinterlace.c
>>>>>> @@ -343,7 +343,7 @@ static void deinterlace_issue_dma(struct
>>>>>> deinterlace_ctx *ctx, int op,
>>>>>>         ctx->xt->dst_sgl = true;
>>>>>>         flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
>>>>>>
>>>>>> -     tx = dmadev->device_prep_interleaved_dma(chan, ctx->xt, flags);
>>>>>> +     tx = dmadev->device_prep_interleaved_dma(chan, &ctx->xt, flags);
>>>>>>         if (tx == NULL) {
>>>>>>                 v4l2_warn(&pcdev->v4l2_dev, "DMA interleaved prep
>>>>>> error\n");
>>>>>>                 return;
>>>>>> diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h
>>>>>> index c5c92d5..2f77a9a 100644
>>>>>> --- a/include/linux/dmaengine.h
>>>>>> +++ b/include/linux/dmaengine.h
>>>>>> @@ -675,7 +675,7 @@ struct dma_device {
>>>>>>                 size_t period_len, enum dma_transfer_direction
>>>>>> direction,
>>>>>>                 unsigned long flags, void *context);
>>>>>>         struct dma_async_tx_descriptor
>>>>>> *(*device_prep_interleaved_dma)(
>>>>>> -             struct dma_chan *chan, struct dma_interleaved_template
>>>>>> *xt,
>>>>>> +             struct dma_chan *chan, struct dma_interleaved_template
>>>>>> **xts,
>>>>>>                 unsigned long flags);
>>>>>>         int (*device_control)(struct dma_chan *chan, enum dma_ctrl_cmd
>>>>>> cmd,
>>>>>>                 unsigned long arg);
>>>>>> @@ -752,10 +752,10 @@ static inline struct dma_async_tx_descriptor
>>>>>> *dmaengine_prep_dma_cyclic(
>>>>>>    }
>>>>>>
>>>>>>    static inline struct dma_async_tx_descriptor
>>>>>> *dmaengine_prep_interleaved_dma(
>>>>>> -             struct dma_chan *chan, struct dma_interleaved_template
>>>>>> *xt,
>>>>>> +             struct dma_chan *chan, struct dma_interleaved_template
>>>>>> **xts,
>>>>>>                 unsigned long flags)
>>>>>>    {
>>>>>> -     return chan->device->device_prep_interleaved_dma(chan, xt,
>>>>>> flags);
>>>>>> +     return chan->device->device_prep_interleaved_dma(chan, xts,
>>>>>> flags);
>>>>>>    }
>>>>>>
>>>>>>    static inline int dma_get_slave_caps(struct dma_chan *chan, struct
>>>>>> dma_slave_caps *caps)
>>>>>> --
>>>>>> 1.7.9.5
>>>>>>
>>>>>> --
>>>>>> To unsubscribe from this list: send the line "unsubscribe dmaengine"
>>>>>> in
>>>>>> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
>>>>>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>>>>>
>>>>>
>>>>>
>>>>> --
>>>>> --
>>>>> To unsubscribe from this list: send the line "unsubscribe linux-kernel"
>>>>> in
>>>>> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
>>>>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>>>>> Please read the FAQ at  http://www.tux.org/lkml/
>>>
>>>
>>>
>>> --
>>> To unsubscribe from this list: send the line "unsubscribe linux-kernel"
>>> in
>>> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
>>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>>> Please read the FAQ at  http://www.tux.org/lkml/
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH v3 1/3] dma: Support multiple interleaved frames with non-contiguous memory
From: Jassi Brar @ 2014-02-17  9:57 UTC (permalink / raw)
  To: Srikanth Thokala
  Cc: Williams, Dan J, Koul, Vinod, michal.simek, grant.likely, robh+dt,
	devicetree, levex, lars, lkml, dmaengine, andriy.shevchenko,
	linux-arm-kernel@lists.infradead.org
In-Reply-To: <1392465619-27614-2-git-send-email-sthokal@xilinx.com>

On 15 February 2014 17:30, Srikanth Thokala <sthokal@xilinx.com> wrote:
> The current implementation of interleaved DMA API support multiple
> frames only when the memory is contiguous by incrementing src_start/
> dst_start members of interleaved template.
>
> But, when the memory is non-contiguous it will restrict slave device
> to not submit multiple frames in a batch.  This patch handles this
> issue by allowing the slave device to send array of interleaved dma
> templates each having a different memory location.
>
How fragmented could be memory in your case? Is it inefficient to
submit separate transfers for each segment/frame?
It will help if you could give a typical example (chunk size and gap
in bytes) of what you worry about.

Thanks,
Jassi

^ permalink raw reply

* Re: [PATCH 2/5] ARM: shmobile: r8a7791: add i2c master nodes to dtsi
From: Magnus Damm @ 2014-02-17  9:57 UTC (permalink / raw)
  To: Wolfram Sang
  Cc: SH-Linux, linux-arm-kernel@lists.infradead.org, devicetree,
	Simon Horman
In-Reply-To: <20140217091237.GC2633@katana>

On Mon, Feb 17, 2014 at 6:12 PM, Wolfram Sang <wsa@the-dreams.de> wrote:
>
>> > Why is that? From my knowledge, you start with the exact compatible
>> > property and hardware compatible entries may follow.
>>
>> I think this boils down to if they really are compatible or not. If
>> for instance a 16550 port would be compatible with 8250 on a hardware
>> level then using them in the order of "16550", "8250" makes sense. In
>> this case the r8a7791 i2c is not really strictly based on r8a7790 i2c,
>> it is just that r8a7790 has support in the driver. So it's a short cut
>> instead of actual hardware compatibility.
>
> I don't get this point. The legacy board code for koelsch and lager both
> create a platform_device with "i2c-rcar_gen2", so the cores surely must
> be compatible? Maybe the cores are not strictly based on each other, but
> compatible, yes, I'd say.

The devices may be compatible seen from the limited use case coming
from the current version of the device driver. But that does not
necessarily mean they are compatible from a hardware point of view. My
view is that the hardware should not be seen compatible unless it is
explicitly documented to be so.

For platform devices we make use of "unstable ids" or feature flags to
describe a certain part of hardware as we know it at the time of
writing the code. Then we can update the features in the driver and
make sure all in-tree boards/socs using the "unstable id" keeps on
working by for instance adjusting platform data or adding resources. I
think this is a fine model for platform devices.

In my mind the above model is quite different from how I think we
should describe devices via DT. In the DT case I think it is wrong to
rely on software defined "unstable ids" like for instance
"i2c-rcar-gen2" because they are built on our perhaps limited
interpretation of the hardware. Of course, if the hardware
documentation would be correct and we have hardware ids described in
the documentation then we can start using those as compatible strings.
Until then I feel it is safe to use the SoC name in the compatible
string.

Cheers,

/ magnus

^ permalink raw reply

* Re: [PATCH 01/10] mfd: Add TI LMU driver
From: Lee Jones @ 2014-02-17  9:57 UTC (permalink / raw)
  To: Milo Kim
  Cc: Jingoo Han, Bryan Wu, Mark Brown,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Samuel Ortiz
In-Reply-To: <1392359450-6890-1-git-send-email-milo.kim-l0cyMroinI0@public.gmane.org>

> TI LMU (Lighting Management Unit) driver supports lighting devices such like
> LM3532, LM3631, LM3633, LM3695 and LM3697.
> 
> LMU devices has common features as below.
>   - I2C interface for accessing device registers
>   - Hardware enable pin control
>   - Backlight brightness control
>   - Light effect driver for backlight and LED patterns
> 
> It contains backlight, light effect, LED and regulator driver.
> 
> Backlight
> ---------
>   It's handled by TI LMU backlight common driver and chip dependent driver.
>   Please refer to separate patches for ti-lmu-backlight.
> 
> Light effect
> ------------
>   LMU effect driver is used for setting any light effects.
>   Each device has specific time value and register map.
>   Backlight and LED driver can use consitent APIs for light effects.
> 
>   There are two lists for effect management. LMU effect list and pending list.
>   Light effect list is added when ti-lmu-effect driver is loaded by referencing
>   platform resource data.
>   However, it can be a problem because some LMU device requests the effect
>   in advance of loading ti-lmu-effect driver.
> 
>   For example, LM3532 backlight driver requests light ramp effects before
>   ti-lmu-effect is loaded.
>   Then, requested effect can not be handled because it doesn't exist in the list.
>   To solve this situation, pending request list is used.
>   If requested effect is not in the list, just insert it into the pending list.
>   And then pending request is handled as soon as the effect is added.
> 
> LED indicator
> -------------
>   LM3633 has 6 indicator LEDs. Programmable pattern is supported.
> 
> Regulator
> ---------
>   LM3631 has 5 regulators for the display bias.
> 
> Cc: Samuel Ortiz <sameo-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>
> Cc: Lee Jones <lee.jones-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> Signed-off-by: Milo Kim <milo.kim-l0cyMroinI0@public.gmane.org>
> ---
>  drivers/mfd/Kconfig                 |   12 +
>  drivers/mfd/Makefile                |    1 +
>  drivers/mfd/ti-lmu-effect.c         |  328 +++++++++++++++++++++++++
>  drivers/mfd/ti-lmu.c                |  464 +++++++++++++++++++++++++++++++++++
>  include/linux/mfd/ti-lmu-effect.h   |  109 ++++++++
>  include/linux/mfd/ti-lmu-register.h |  269 ++++++++++++++++++++
>  include/linux/mfd/ti-lmu.h          |  150 +++++++++++
>  7 files changed, 1333 insertions(+)
>  create mode 100644 drivers/mfd/ti-lmu-effect.c
>  create mode 100644 drivers/mfd/ti-lmu.c
>  create mode 100644 include/linux/mfd/ti-lmu-effect.h
>  create mode 100644 include/linux/mfd/ti-lmu-register.h
>  create mode 100644 include/linux/mfd/ti-lmu.h

<snip>

> +static u8 ti_lmu_effect_get_ramp_index(struct ti_lmu_effect *lmu_effect,
> +				       int msec)
> +{
> +	const int *table = NULL;
> +	int size = 0;
> +	int index = 0;
> +	int i;
> +
> +	switch (lmu_effect->lmu->id) {
> +	case LM3532:
> +		table = lm3532_ramp_table;
> +		size = ARRAY_SIZE(lm3532_ramp_table);
> +		break;
> +	case LM3631:
> +		table = lm3631_ramp_table;
> +		size = ARRAY_SIZE(lm3631_ramp_table);
> +		break;
> +	case LM3633:
> +	case LM3697:	/* LM3697 has same ramp table as LM3633 */
> +		table = lm3633_ramp_table;
> +		size = ARRAY_SIZE(lm3633_ramp_table);
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	if (msec <= table[0]) {
> +		index = 0;
> +		goto out;

Just:
  return 0;

> +	}
> +
> +	if (msec >= table[size-1]) {
> +		index = size - 1;
> +		goto out;

Just:
  return index;

> +	}
> +
> +	/* Find appropriate register index from the table */
> +	for (i = 1; i < size; i++) {
> +		if (msec >= table[i-1] && msec < table[i]) {
> +			index = i - 1;
> +			goto out;

Same here.

> +		}
> +	}
> +
> +	return 0;
> +out:
> +	return index;

Remove this.

> +}
> +
> +static u8 ti_lmu_effect_get_time_index(struct ti_lmu_effect *lmu_effect,
> +				       int msec)
> +{
> +	u8 idx, offset;
> +
> +	/*
> +	 * Find appropriate register index around input time value
> +	 *
> +	 *      0 <= time <= 1000 : 16ms step
> +	 *   1000 <  time <= 9700 : 131ms step, base index is 61
> +	 */
> +
> +	msec = min_t(int, msec, LMU_EFFECT_MAX_TIME_PERIOD);
> +
> +	if (msec >= 0 && msec <= 1000) {
> +		idx = msec / 16;
> +		if (idx > 1)
> +			idx--;
> +		offset = 0;
> +	} else {
> +		idx = (msec - 1000) / 131;
> +		offset = 61;

What are 131 and 61? Sounds like magic, require #defines or at least a
comment.

<snip>

> +	if (lmu_effect) {
> +		cbfunc(lmu_effect, req_id, data);
> +		goto out;

This doesn't do anything. Just return from here.

> +	}

<snip>

> +out:
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(ti_lmu_effect_request);

<snip>

> diff --git a/drivers/mfd/ti-lmu.c b/drivers/mfd/ti-lmu.c
> new file mode 100644
> index 0000000..e97d686
> --- /dev/null
> +++ b/drivers/mfd/ti-lmu.c
> @@ -0,0 +1,464 @@
> +/*
> + * TI LMU(Lighting Management Unit) Core Driver
> + *
> + * Copyright 2014 Texas Instruments
> + *
> + * Author: Milo Kim <milo.kim-l0cyMroinI0@public.gmane.org>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.

Move this to the bottom of the header.

> + * LMU MFD supports LM3532, LM3631, LM3633, LM3695 and LM3697.
> + *
> + *   LM3532: backlight + light effect
> + *   LM3631: backlight + light effect + regulators
> + *   LM3633: backlight + light effect + LED indicators
> + *   LM3695: backlight
> + *   LM3697: backlight + light effect
> + *
> + * Those devices have common features as below.
> + *
> + *   - I2C interface for accessing device registers
> + *   - Hardware enable pin control
> + *   - Backlight brightness control with current settings
> + *   - Light effect driver for backlight and LED patterns
> + */

kernel.h?
module.h?

> +#include <linux/delay.h>
> +#include <linux/err.h>
> +#include <linux/gpio.h>
> +#include <linux/i2c.h>
> +#include <linux/mfd/core.h>
> +#include <linux/mfd/ti-lmu.h>
> +#include <linux/mfd/ti-lmu-effect.h>
> +#include <linux/mfd/ti-lmu-register.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_gpio.h>
> +#include <linux/slab.h>
> +
> +#define LMU_IMAX_OFFSET		6
> +

<snip>

> +static const struct resource lm3633_effect_resources[] = {
> +	{
> +		.name  = LM3633_EFFECT_BL0_RAMPUP,
> +		.flags = IORESOURCE_REG,
> +		.start = LM3633_EFFECT_REGISTER(BL0_RAMPUP),
> +	},
> +	{
> +		.name  = LM3633_EFFECT_BL0_RAMPDOWN,
> +		.flags = IORESOURCE_REG,
> +		.start = LM3633_EFFECT_REGISTER(BL0_RAMPDN),
> +	},
> +	{
> +		.name  = LM3633_EFFECT_BL1_RAMPUP,
> +		.flags = IORESOURCE_REG,
> +		.start = LM3633_EFFECT_REGISTER(BL1_RAMPUP),
> +	},
> +	{
> +		.name  = LM3633_EFFECT_BL1_RAMPDOWN,
> +		.flags = IORESOURCE_REG,
> +		.start = LM3633_EFFECT_REGISTER(BL1_RAMPDN),
> +	},
> +	{
> +		.name  = LM3633_EFFECT_PTN_DELAY,
> +		.flags = IORESOURCE_REG,
> +		.start = LM3633_EFFECT_REGISTER(DELAY),
> +	},
> +	{
> +		.name  = LM3633_EFFECT_PTN_HIGHTIME,
> +		.flags = IORESOURCE_REG,
> +		.start = LM3633_EFFECT_REGISTER(HIGHTIME),
> +	},
> +	{
> +		.name  = LM3633_EFFECT_PTN_LOWTIME,
> +		.flags = IORESOURCE_REG,
> +		.start = LM3633_EFFECT_REGISTER(LOWTIME),
> +	},
> +	{
> +		.name  = LM3633_EFFECT_PTN0_RAMPUP,
> +		.flags = IORESOURCE_REG,
> +		.start = LM3633_EFFECT_REGISTER(PTN0_RAMPUP),
> +	},
> +	{
> +		.name  = LM3633_EFFECT_PTN0_RAMPDOWN,
> +		.flags = IORESOURCE_REG,
> +		.start = LM3633_EFFECT_REGISTER(PTN0_RAMPDN),
> +	},
> +	{
> +		.name  = LM3633_EFFECT_PTN1_RAMPUP,
> +		.flags = IORESOURCE_REG,
> +		.start = LM3633_EFFECT_REGISTER(PTN1_RAMPUP),
> +	},
> +	{
> +		.name  = LM3633_EFFECT_PTN1_RAMPDOWN,
> +		.flags = IORESOURCE_REG,
> +		.start = LM3633_EFFECT_REGISTER(PTN1_RAMPDN),
> +	},
> +	{
> +		.name  = LM3633_EFFECT_PTN_LOWBRT,
> +		.flags = IORESOURCE_REG,
> +		.start = LM3633_EFFECT_REGISTER(LOWBRT),
> +	},
> +	{
> +		.name  = LM3633_EFFECT_PTN_HIGHBRT,
> +		.flags = IORESOURCE_REG,
> +		.start = LM3633_EFFECT_REGISTER(HIGHBRT),
> +	},
> +};

Can you define a MACRO to do all of these as one liners?

<snip>

> +static int ti_lmu_parse_dt(struct device *dev, struct ti_lmu *lmu)
> +{

<snip>

> +	pdata->en_gpio = of_get_named_gpio(node, "ti,enable-gpio", 0);

There is a global DT property for this already.

> +static int ti_lmu_probe(struct i2c_client *cl, const struct i2c_device_id *id)
> +{

<snip>

> +	lmu->id = id->driver_data;
> +	switch (lmu->id) {
> +	case LM3532:
> +		lmu_regmap_config.max_register = LM3532_MAX_REGISTERS;
> +		break;
> +	case LM3631:
> +		lmu_regmap_config.max_register = LM3631_MAX_REGISTERS;
> +		break;
> +	case LM3633:
> +		lmu_regmap_config.max_register = LM3633_MAX_REGISTERS;
> +		break;
> +	case LM3695:
> +		lmu_regmap_config.max_register = LM3695_MAX_REGISTERS;
> +		break;
> +	case LM3697:
> +		lmu_regmap_config.max_register = LM3697_MAX_REGISTERS;
> +		break;
> +	default:
> +		break;
> +	}

As there are so many of these, it might be nicer to pull these out
into a seperate function.

<snip>

-- 
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH v3] phy: Add new Exynos5 USB 3.0 PHY driver
From: Vivek Gautam @ 2014-02-17 10:01 UTC (permalink / raw)
  To: Tomasz Figa
  Cc: Vivek Gautam, Linux USB Mailing List,
	linux-samsung-soc@vger.kernel.org, linux-kernel@vger.kernel.org,
	devicetree@vger.kernel.org, linux-doc, Greg KH, Kukjin Kim,
	Felipe Balbi, kishon, Kamil Debski, Sylwester Nawrocki,
	Julius Werner, Jingoo Han
In-Reply-To: <52FE3A91.1090107@samsung.com>

Hi Tomasz,


On Fri, Feb 14, 2014 at 9:17 PM, Tomasz Figa <t.figa@samsung.com> wrote:

mistakenly didn't do "Reply All", so sending it again.

> Hi Vivek,
>
>
> On 14.02.2014 14:53, Vivek Gautam wrote:
>>>>
>>>> Changes from v2:
>>>> 1) Added support for multiple PHYs (UTMI+ and PIPE3) and
>>>>      related changes in the driver structuring.
>>>
>>>
>>>
>>> I'm a bit skeptical about this separation. Can the PHY operate with just
>>> the
>>> UTMI+ or PIPE3 part enabled alone without the other? Can any PHY consumer
>>> operate this way?
>>
>>
>> Yes :-)
>> As also pointed by Kishon the PHY consumer (which is DWC3 in case of
>> Exynos5 SoC series)
>> should theoretically be able use either UTMI+ phy for High speed
>> operations or both (UTMI+ and PIPE3)
>> for Super Speed operations.
>
>
> OK, that's fine then. This is the explanation I needed, thanks.
>
>
>>>
>>> I believe the right thing to do here is to do all the initialization in
>>> .power_on() and let the driver simply call phy_power_on() when it needs
>>> the
>>> PHY and phy_power_off() otherwise.
>>
>>
>> If this is what we should be doing then what will be the purpose of
>> two separate APIs :
>> phy_power_on() and phy_init().
>> Am i missing while understanding the things.
>>
>
> I don't understand this separation as well. Operations that should be done
> together shouldn't be separated. Is there any case when you can call one of
> phy_power_on() and phy_init() without calling another one right before/after
> it?

Most of the PHY consumers need to call phy_power_on() as well as phy_init()
to get PHY working (if the PHY provider gives callback to both).
I think the idea would have been to separate out the initialization
related and power related
jobs (which may include setting up the PMUs and may be regulators ?)
But, i think it's true, if the PHY provider callback for both
phy_power_on() and phy_init()
then the consumer __has__ to call both to get things working.



-- 
Best Regards
Vivek Gautam
Samsung R&D Institute, Bangalore
India

^ permalink raw reply

* [PATCH v7 0/8] ARM: sunxi: Add driver for SD/MMC hosts found on allwinner sunxi SOCs
From: David Lanzendörfer @ 2014-02-17 10:02 UTC (permalink / raw)
  To: devicetree-u79uwXL29TY76Z2rM5mHXA, Ulf Hansson, Laurent Pinchart,
	Mike Turquette, Simon Baatz, Hans de Goede, Emilio López,
	linux-mmc-u79uwXL29TY76Z2rM5mHXA, Chris Ball,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, H Hartley Sweeten,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Tejun Heo, Maxime Ripard,
	Guennadi Liakhovetski,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

Hello
The following patchset adds support for the SD/MMC host found in the Allwinner SoCs.
It contains all the necessary modifications for clock environment and also the device
tree script modification which add it to all the boards using it.
The clock environment function needed for phase offset configuration has
been proposed and implemented by Emilio.
A lot of work and cleanup has been done by Hans de Goede. Special thanks to him!
This patchset is the 4th attempt to send this driver upstream.

Changes since v1:
-Using mmc_of_parse instead of diy dt parsing
-Adding nodes for all mmc controller to the dtsi files,
 including sofar unused controllers
-Using generic GPIO slot library for WP/CD
-Adding additional MMC device nodes into DTSI files

Changes since v2:
-Add missing Signed-off-by tags
-Stop using __raw_readl / __raw_writel so that barriers are properly used
-Adding missing new lines
-Adding missing patch for automatic reparenting of clocks

Changes since v3:
-Move clk_enable / disable into host_init / exit (Hans)
-Fix hang on boot caused by irq storm (Hans)

Changes since v4:
-moving sunxi-mci.{c/h} to sunxi-mmc.{c/h}
-removing camel cases from the defines in  sunxi-mmc.h
-moving defines out of the struct definition
 since this is bad coding style
-adding documentation for the device tree binding

Changes since v5:
-adding host initialization for when the sdio irq is enabled
 (just to make sure having a defined state at all time)
-add mmc support fixup: set pullup on cd pins
-fixup: Don't set MMC_CAP_NEEDS_POLL /  MMC_CAP_4_BIT_DATA

Changes since v6:
-fixing copyright info in sunxi-mmc.*
-s/__SUNXI_MCI_H__/__SUNXI_MMC_H__/g
-s/SDXC_RESPONSE_/SDXC_RESP_/g
-s/define/definitions <- Comment from Priit Laes

---

David Lanzendörfer (5):
      ARM: sunxi: Add driver for SD/MMC hosts found on Allwinner sunxi SoCs
      ARM: dts: sun7i: Add support for mmc
      ARM: dts: sun4i: Add support for mmc
      ARM: dts: sun5i: Add support for mmc
      ARM: sunxi: Add documentation for driver for SD/MMC hosts found on Allwinner sunxi SoCs

Emilio López (2):
      clk: sunxi: factors: automatic reparenting support
      clk: sunxi: Implement MMC phase control

Hans de Goede (1):
      ARM: sunxi: clk: export clk_sunxi_mmc_phase_control


 .../devicetree/bindings/mmc/sunxi-mmc.txt          |   32 +
 arch/arm/boot/dts/sun4i-a10-a1000.dts              |    8 
 arch/arm/boot/dts/sun4i-a10-cubieboard.dts         |    8 
 arch/arm/boot/dts/sun4i-a10.dtsi                   |   54 +
 arch/arm/boot/dts/sun5i-a10s-olinuxino-micro.dts   |   30 +
 arch/arm/boot/dts/sun5i-a10s.dtsi                  |   44 +
 arch/arm/boot/dts/sun5i-a13-olinuxino-micro.dts    |   15 
 arch/arm/boot/dts/sun5i-a13-olinuxino.dts          |   15 
 arch/arm/boot/dts/sun5i-a13.dtsi                   |   37 +
 arch/arm/boot/dts/sun7i-a20-cubieboard2.dts        |    8 
 arch/arm/boot/dts/sun7i-a20-cubietruck.dts         |    8 
 arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts    |   23 +
 arch/arm/boot/dts/sun7i-a20.dtsi                   |   61 +
 drivers/clk/sunxi/clk-factors.c                    |   36 +
 drivers/clk/sunxi/clk-sunxi.c                      |   35 +
 drivers/mmc/host/Kconfig                           |    7 
 drivers/mmc/host/Makefile                          |    2 
 drivers/mmc/host/sunxi-mmc.c                       |  876 ++++++++++++++++++++
 drivers/mmc/host/sunxi-mmc.h                       |  239 +++++
 include/linux/clk/sunxi.h                          |   22 +
 20 files changed, 1560 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mmc/sunxi-mmc.txt
 create mode 100644 drivers/mmc/host/sunxi-mmc.c
 create mode 100644 drivers/mmc/host/sunxi-mmc.h
 create mode 100644 include/linux/clk/sunxi.h

-- 
Signature

-- 
You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
For more options, visit https://groups.google.com/groups/opt_out.

^ permalink raw reply

* [PATCH v7 1/8] clk: sunxi: factors: automatic reparenting support
From: David Lanzendörfer @ 2014-02-17 10:02 UTC (permalink / raw)
  To: devicetree-u79uwXL29TY76Z2rM5mHXA, Ulf Hansson, Laurent Pinchart,
	Mike Turquette, Simon Baatz, Hans de Goede, Emilio López,
	linux-mmc-u79uwXL29TY76Z2rM5mHXA, Chris Ball,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, H Hartley Sweeten,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Tejun Heo, Maxime Ripard,
	Guennadi Liakhovetski,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <20140217095907.15040.81893.stgit-pgFh0Jf6HD9Xzn/AsuzBOg@public.gmane.org>

From: Emilio López <emilio-0Z03zUJReD5OxF6Tv1QG9Q@public.gmane.org>

This commit implements .determine_rate, so that our factor clocks can be
reparented when needed.

Signed-off-by: Emilio López <emilio-0Z03zUJReD5OxF6Tv1QG9Q@public.gmane.org>
---
 drivers/clk/sunxi/clk-factors.c |   36 ++++++++++++++++++++++++++++++++++++
 1 file changed, 36 insertions(+)

diff --git a/drivers/clk/sunxi/clk-factors.c b/drivers/clk/sunxi/clk-factors.c
index 9e23264..3806d97 100644
--- a/drivers/clk/sunxi/clk-factors.c
+++ b/drivers/clk/sunxi/clk-factors.c
@@ -77,6 +77,41 @@ static long clk_factors_round_rate(struct clk_hw *hw, unsigned long rate,
 	return rate;
 }
 
+static long clk_factors_determine_rate(struct clk_hw *hw, unsigned long rate,
+				       unsigned long *best_parent_rate,
+				       struct clk **best_parent_p)
+{
+	struct clk *clk = hw->clk, *parent, *best_parent = NULL;
+	int i, num_parents;
+	unsigned long parent_rate, best = 0, child_rate, best_child_rate = 0;
+
+	/* find the parent that can help provide the fastest rate <= rate */
+	num_parents = __clk_get_num_parents(clk);
+	for (i = 0; i < num_parents; i++) {
+		parent = clk_get_parent_by_index(clk, i);
+		if (!parent)
+			continue;
+		if (__clk_get_flags(clk) & CLK_SET_RATE_PARENT)
+			parent_rate = __clk_round_rate(parent, rate);
+		else
+			parent_rate = __clk_get_rate(parent);
+
+		child_rate = clk_factors_round_rate(hw, rate, &parent_rate);
+
+		if (child_rate <= rate && child_rate > best_child_rate) {
+			best_parent = parent;
+			best = parent_rate;
+			best_child_rate = child_rate;
+		}
+	}
+
+	if (best_parent)
+		*best_parent_p = best_parent;
+	*best_parent_rate = best;
+
+	return best_child_rate;
+}
+
 static int clk_factors_set_rate(struct clk_hw *hw, unsigned long rate,
 				unsigned long parent_rate)
 {
@@ -113,6 +148,7 @@ static int clk_factors_set_rate(struct clk_hw *hw, unsigned long rate,
 }
 
 const struct clk_ops clk_factors_ops = {
+	.determine_rate = clk_factors_determine_rate,
 	.recalc_rate = clk_factors_recalc_rate,
 	.round_rate = clk_factors_round_rate,
 	.set_rate = clk_factors_set_rate,

-- 
You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
For more options, visit https://groups.google.com/groups/opt_out.

^ permalink raw reply related

* [PATCH v7 2/8] clk: sunxi: Implement MMC phase control
From: David Lanzendörfer @ 2014-02-17 10:02 UTC (permalink / raw)
  To: devicetree-u79uwXL29TY76Z2rM5mHXA, Ulf Hansson, Laurent Pinchart,
	Mike Turquette, Simon Baatz, Hans de Goede, Emilio López,
	linux-mmc-u79uwXL29TY76Z2rM5mHXA, Chris Ball,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, H Hartley Sweeten,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Tejun Heo, Maxime Ripard,
	Guennadi Liakhovetski,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <20140217095907.15040.81893.stgit-pgFh0Jf6HD9Xzn/AsuzBOg@public.gmane.org>

From: Emilio López <emilio-0Z03zUJReD5OxF6Tv1QG9Q@public.gmane.org>

Signed-off-by: Emilio López <emilio-0Z03zUJReD5OxF6Tv1QG9Q@public.gmane.org>
---
 drivers/clk/sunxi/clk-sunxi.c |   35 +++++++++++++++++++++++++++++++++++
 1 file changed, 35 insertions(+)

diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c
index abb6c5a..33b9977 100644
--- a/drivers/clk/sunxi/clk-sunxi.c
+++ b/drivers/clk/sunxi/clk-sunxi.c
@@ -377,6 +377,41 @@ static void sun7i_a20_get_out_factors(u32 *freq, u32 parent_rate,
 
 
 /**
+ * clk_sunxi_mmc_phase_control() - configures MMC clock phase control
+ */
+
+void clk_sunxi_mmc_phase_control(struct clk_hw *hw, u8 sample, u8 output)
+{
+	#define to_clk_composite(_hw) container_of(_hw, struct clk_composite, hw)
+	#define to_clk_factors(_hw) container_of(_hw, struct clk_factors, hw)
+
+	struct clk_composite *composite = to_clk_composite(hw);
+	struct clk_hw *rate_hw = composite->rate_hw;
+	struct clk_factors *factors = to_clk_factors(rate_hw);
+	unsigned long flags = 0;
+	u32 reg;
+
+	if (factors->lock)
+		spin_lock_irqsave(factors->lock, flags);
+
+	reg = readl(factors->reg);
+
+	/* set sample clock phase control */
+	reg &= ~(0x7 << 20);
+	reg |= ((sample & 0x7) << 20);
+
+	/* set output clock phase control */
+	reg &= ~(0x7 << 8);
+	reg |= ((output & 0x7) << 8);
+
+	writel(reg, factors->reg);
+
+	if (factors->lock)
+		spin_unlock_irqrestore(factors->lock, flags);
+}
+
+
+/**
  * sunxi_factors_clk_setup() - Setup function for factor clocks
  */
 

-- 
You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
For more options, visit https://groups.google.com/groups/opt_out.

^ permalink raw reply related

* [PATCH v7 3/8] ARM: sunxi: clk: export clk_sunxi_mmc_phase_control
From: David Lanzendörfer @ 2014-02-17 10:02 UTC (permalink / raw)
  To: devicetree-u79uwXL29TY76Z2rM5mHXA, Ulf Hansson, Laurent Pinchart,
	Mike Turquette, Simon Baatz, Hans de Goede, Emilio López,
	linux-mmc-u79uwXL29TY76Z2rM5mHXA, Chris Ball,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, H Hartley Sweeten,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Tejun Heo, Maxime Ripard,
	Guennadi Liakhovetski,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <20140217095907.15040.81893.stgit-pgFh0Jf6HD9Xzn/AsuzBOg@public.gmane.org>

From: Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>

Signed-off-by: Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
---
 include/linux/clk/sunxi.h |   22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)
 create mode 100644 include/linux/clk/sunxi.h

diff --git a/include/linux/clk/sunxi.h b/include/linux/clk/sunxi.h
new file mode 100644
index 0000000..1ef5c89
--- /dev/null
+++ b/include/linux/clk/sunxi.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2013 - Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __LINUX_CLK_SUNXI_H_
+#define __LINUX_CLK_SUNXI_H_
+
+#include <linux/clk.h>
+
+void clk_sunxi_mmc_phase_control(struct clk_hw *hw, u8 sample, u8 output);
+
+#endif

^ permalink raw reply related

* [PATCH v7 4/8] ARM: sunxi: Add driver for SD/MMC hosts found on Allwinner sunxi SoCs
From: David Lanzendörfer @ 2014-02-17 10:02 UTC (permalink / raw)
  To: devicetree-u79uwXL29TY76Z2rM5mHXA, Ulf Hansson, Laurent Pinchart,
	Mike Turquette, Simon Baatz, Hans de Goede, Emilio López,
	linux-mmc-u79uwXL29TY76Z2rM5mHXA, Chris Ball,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, H Hartley Sweeten,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Tejun Heo, Maxime Ripard,
	Guennadi Liakhovetski,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <20140217095907.15040.81893.stgit-pgFh0Jf6HD9Xzn/AsuzBOg@public.gmane.org>

This is based on the driver Allwinner ships in their Android kernel sources.

Initial porting to upstream kernels done by David Lanzendörfer, additional
fixes and cleanups by Hans de Goede.

It uses dma in bus-master mode using a built-in designware idmac controller,
which is identical to the one found in the mmc-dw hosts.
The rest of the host is not identical to mmc-dw.

Signed-off-by: David Lanzendörfer <david.lanzendoerfer-Z7Kmv9EsliU@public.gmane.org>
Signed-off-by: Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
---
 drivers/mmc/host/Kconfig     |    7 
 drivers/mmc/host/Makefile    |    2 
 drivers/mmc/host/sunxi-mmc.c |  876 ++++++++++++++++++++++++++++++++++++++++++
 drivers/mmc/host/sunxi-mmc.h |  239 +++++++++++
 4 files changed, 1124 insertions(+)
 create mode 100644 drivers/mmc/host/sunxi-mmc.c
 create mode 100644 drivers/mmc/host/sunxi-mmc.h

diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 1384f67..7caf266 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -689,3 +689,10 @@ config MMC_REALTEK_PCI
 	help
 	  Say Y here to include driver code to support SD/MMC card interface
 	  of Realtek PCI-E card reader
+
+config MMC_SUNXI
+	tristate "Allwinner sunxi SD/MMC Host Controller support"
+	depends on ARCH_SUNXI
+	help
+	  This selects support for the SD/MMC Host Controller on
+	  Allwinner sunxi SoCs.
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index 3483b6b..f3c7c243 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -54,6 +54,8 @@ obj-$(CONFIG_MMC_WMT)		+= wmt-sdmmc.o
 
 obj-$(CONFIG_MMC_REALTEK_PCI)	+= rtsx_pci_sdmmc.o
 
+obj-$(CONFIG_MMC_SUNXI)		+= sunxi-mmc.o
+
 obj-$(CONFIG_MMC_SDHCI_PLTFM)		+= sdhci-pltfm.o
 obj-$(CONFIG_MMC_SDHCI_CNS3XXX)		+= sdhci-cns3xxx.o
 obj-$(CONFIG_MMC_SDHCI_ESDHC_IMX)	+= sdhci-esdhc-imx.o
diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c
new file mode 100644
index 0000000..752e913
--- /dev/null
+++ b/drivers/mmc/host/sunxi-mmc.c
@@ -0,0 +1,876 @@
+/*
+ * Driver for sunxi SD/MMC host controllers
+ * (C) Copyright 2007-2011 Reuuimlla Technology Co., Ltd.
+ * (C) Copyright 2007-2011 Aaron Maoye <leafy.myeh-jFKXxz0WcGyYHARAtoI1EgC/G2K4zDHf@public.gmane.org>
+ * (C) Copyright 2013-2014 O2S GmbH <www.o2s.ch>
+ * (C) Copyright 2013-2014 David Lanzendörfer <david.lanzendoerfer@o2s.ch>
+ * (C) Copyright 2013-2014 Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+
+#include <linux/clk.h>
+#include <linux/clk-private.h>
+#include <linux/clk/sunxi.h>
+
+#include <linux/gpio.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/scatterlist.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/regulator/consumer.h>
+
+#include <linux/of_address.h>
+#include <linux/of_gpio.h>
+#include <linux/of_platform.h>
+
+#include <linux/mmc/host.h>
+#include <linux/mmc/sd.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/core.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/slot-gpio.h>
+
+#include "sunxi-mmc.h"
+
+static int sunxi_mmc_init_host(struct mmc_host *mmc)
+{
+	u32 rval;
+	struct sunxi_mmc_host *smc_host = mmc_priv(mmc);
+	int ret;
+
+	ret =  clk_prepare_enable(smc_host->clk_ahb);
+	if (ret) {
+		dev_err(mmc_dev(smc_host->mmc), "AHB clk err %d\n", ret);
+		return ret;
+	}
+	ret =  clk_prepare_enable(smc_host->clk_mod);
+	if (ret) {
+		dev_err(mmc_dev(smc_host->mmc), "MOD clk err %d\n", ret);
+		clk_disable_unprepare(smc_host->clk_ahb);
+		return ret;
+	}
+
+	/* reset controller */
+	rval = mci_readl(smc_host, REG_GCTRL) | SDXC_HARDWARE_RESET;
+	mci_writel(smc_host, REG_GCTRL, rval);
+
+	mci_writel(smc_host, REG_FTRGL, 0x20070008);
+	mci_writel(smc_host, REG_TMOUT, 0xffffffff);
+	mci_writel(smc_host, REG_IMASK, smc_host->sdio_imask);
+	mci_writel(smc_host, REG_RINTR, 0xffffffff);
+	mci_writel(smc_host, REG_DBGC, 0xdeb);
+	mci_writel(smc_host, REG_FUNS, 0xceaa0000);
+	mci_writel(smc_host, REG_DLBA, smc_host->sg_dma);
+	rval = mci_readl(smc_host, REG_GCTRL)|SDXC_INTERRUPT_ENABLE_BIT;
+	rval &= ~SDXC_ACCESS_DONE_DIRECT;
+	mci_writel(smc_host, REG_GCTRL, rval);
+
+	return 0;
+}
+
+static void sunxi_mmc_exit_host(struct sunxi_mmc_host *smc_host)
+{
+	mci_writel(smc_host, REG_GCTRL, SDXC_HARDWARE_RESET);
+	clk_disable_unprepare(smc_host->clk_ahb);
+	clk_disable_unprepare(smc_host->clk_mod);
+}
+
+/* /\* UHS-I Operation Modes */
+/*  * DS		25MHz	12.5MB/s	3.3V */
+/*  * HS		50MHz	25MB/s		3.3V */
+/*  * SDR12	25MHz	12.5MB/s	1.8V */
+/*  * SDR25	50MHz	25MB/s		1.8V */
+/*  * SDR50	100MHz	50MB/s		1.8V */
+/*  * SDR104	208MHz	104MB/s		1.8V */
+/*  * DDR50	50MHz	50MB/s		1.8V */
+/*  * MMC Operation Modes */
+/*  * DS		26MHz	26MB/s		3/1.8/1.2V */
+/*  * HS		52MHz	52MB/s		3/1.8/1.2V */
+/*  * HSDDR	52MHz	104MB/s		3/1.8/1.2V */
+/*  * HS200	200MHz	200MB/s		1.8/1.2V */
+/*  * */
+/*  * Spec. Timing */
+/*  * SD3.0 */
+/*  * Fcclk    Tcclk   Fsclk   Tsclk   Tis     Tih     odly  RTis     RTih */
+/*  * 400K     2.5us   24M     41ns    5ns     5ns     1     2209ns   41ns */
+/*  * 25M      40ns    600M    1.67ns  5ns     5ns     3     14.99ns  5.01ns */
+/*  * 50M      20ns    600M    1.67ns  6ns     2ns     3     14.99ns  5.01ns */
+/*  * 50MDDR   20ns    600M    1.67ns  6ns     0.8ns   2     6.67ns   3.33ns */
+/*  * 104M     9.6ns   600M    1.67ns  3ns     0.8ns   1     7.93ns   1.67ns */
+/*  * 208M     4.8ns   600M    1.67ns  1.4ns   0.8ns   1     3.33ns   1.67ns */
+
+/*  * 25M      40ns    300M    3.33ns  5ns     5ns     2     13.34ns   6.66ns */
+/*  * 50M      20ns    300M    3.33ns  6ns     2ns     2     13.34ns   6.66ns */
+/*  * 50MDDR   20ns    300M    3.33ns  6ns     0.8ns   1     6.67ns    3.33ns */
+/*  * 104M     9.6ns   300M    3.33ns  3ns     0.8ns   0     7.93ns    1.67ns */
+/*  * 208M     4.8ns   300M    3.33ns  1.4ns   0.8ns   0     3.13ns    1.67ns */
+
+/*  * eMMC4.5 */
+/*  * 400K     2.5us   24M     41ns    3ns     3ns     1     2209ns    41ns */
+/*  * 25M      40ns    600M    1.67ns  3ns     3ns     3     14.99ns   5.01ns */
+/*  * 50M      20ns    600M    1.67ns  3ns     3ns     3     14.99ns   5.01ns */
+/*  * 50MDDR   20ns    600M    1.67ns  2.5ns   2.5ns   2     6.67ns    3.33ns */
+/*  * 200M     5ns     600M    1.67ns  1.4ns   0.8ns   1     3.33ns    1.67ns */
+/*  *\/ */
+
+static void sunxi_mmc_init_idma_des(struct sunxi_mmc_host *host,
+				    struct mmc_data *data)
+{
+	struct sunxi_idma_des *pdes = (struct sunxi_idma_des *)host->sg_cpu;
+	struct sunxi_idma_des *pdes_pa = (struct sunxi_idma_des *)host->sg_dma;
+	int i, max_len = (1 << host->idma_des_size_bits);
+
+	for (i = 0; i < data->sg_len; i++) {
+		pdes[i].config = SDXC_IDMAC_DES0_CH | SDXC_IDMAC_DES0_OWN |
+				 SDXC_IDMAC_DES0_DIC;
+
+		if (data->sg[i].length == max_len)
+			pdes[i].buf_size = 0; /* 0 == max_len */
+		else
+			pdes[i].buf_size = data->sg[i].length;
+
+		pdes[i].buf_addr_ptr1 = sg_dma_address(&data->sg[i]);
+		pdes[i].buf_addr_ptr2 = (u32)&pdes_pa[i + 1];
+	}
+
+	pdes[0].config |= SDXC_IDMAC_DES0_FD;
+	pdes[i - 1].config = SDXC_IDMAC_DES0_OWN | SDXC_IDMAC_DES0_LD;
+
+	wmb(); /* Ensure idma_des hit main mem before we start the idmac */
+}
+
+static enum dma_data_direction sunxi_mmc_get_dma_dir(struct mmc_data *data)
+{
+	if (data->flags & MMC_DATA_WRITE)
+		return DMA_TO_DEVICE;
+	else
+		return DMA_FROM_DEVICE;
+}
+
+static int sunxi_mmc_prepare_dma(struct sunxi_mmc_host *smc_host,
+				 struct mmc_data *data)
+{
+	u32 dma_len;
+	u32 i;
+	u32 temp;
+	struct scatterlist *sg;
+
+	dma_len = dma_map_sg(mmc_dev(smc_host->mmc), data->sg, data->sg_len,
+			     sunxi_mmc_get_dma_dir(data));
+	if (dma_len == 0) {
+		dev_err(mmc_dev(smc_host->mmc), "dma_map_sg failed\n");
+		return -ENOMEM;
+	}
+
+	for_each_sg(data->sg, sg, data->sg_len, i) {
+		if (sg->offset & 3 || sg->length & 3) {
+			dev_err(mmc_dev(smc_host->mmc),
+				"unaligned scatterlist: os %x length %d\n",
+				sg->offset, sg->length);
+			return -EINVAL;
+		}
+	}
+
+	sunxi_mmc_init_idma_des(smc_host, data);
+
+	temp = mci_readl(smc_host, REG_GCTRL);
+	temp |= SDXC_DMA_ENABLE_BIT;
+	mci_writel(smc_host, REG_GCTRL, temp);
+	temp |= SDXC_DMA_RESET;
+	mci_writel(smc_host, REG_GCTRL, temp);
+	mci_writel(smc_host, REG_DMAC, SDXC_IDMAC_SOFT_RESET);
+
+	if (!(data->flags & MMC_DATA_WRITE))
+		mci_writel(smc_host, REG_IDIE, SDXC_IDMAC_RECEIVE_INTERRUPT);
+
+	mci_writel(smc_host, REG_DMAC, SDXC_IDMAC_FIX_BURST | SDXC_IDMAC_IDMA_ON);
+
+	return 0;
+}
+
+static void sunxi_mmc_send_manual_stop(struct sunxi_mmc_host *host,
+				       struct mmc_request *req)
+{
+	u32 cmd_val = SDXC_START | SDXC_RESP_EXPIRE | SDXC_STOP_ABORT_CMD
+			| SDXC_CHECK_RESPONSE_CRC | MMC_STOP_TRANSMISSION;
+	u32 ri = 0;
+	unsigned long expire = jiffies + msecs_to_jiffies(1000);
+
+	mci_writel(host, REG_CARG, 0);
+	mci_writel(host, REG_CMDR, cmd_val);
+
+	do {
+		ri = mci_readl(host, REG_RINTR);
+	} while (!(ri & (SDXC_COMMAND_DONE | SDXC_INTERRUPT_ERROR_BIT)) &&
+		 time_before(jiffies, expire));
+
+	if (ri & SDXC_INTERRUPT_ERROR_BIT) {
+		dev_err(mmc_dev(host->mmc), "send stop command failed\n");
+		if (req->stop)
+			req->stop->resp[0] = -ETIMEDOUT;
+	} else {
+		if (req->stop)
+			req->stop->resp[0] = mci_readl(host, REG_RESP0);
+	}
+
+	mci_writel(host, REG_RINTR, 0xffff);
+}
+
+static void sunxi_mmc_dump_errinfo(struct sunxi_mmc_host *smc_host)
+{
+	struct mmc_command *cmd = smc_host->mrq->cmd;
+	struct mmc_data *data = smc_host->mrq->data;
+
+	/* For some cmds timeout is normal with sd/mmc cards */
+	if ((smc_host->int_sum & SDXC_INTERRUPT_ERROR_BIT) == SDXC_RESP_TIMEOUT &&
+			(cmd->opcode == SD_IO_SEND_OP_COND || cmd->opcode == SD_IO_RW_DIRECT))
+		return;
+
+	dev_err(mmc_dev(smc_host->mmc),
+		"smc %d err, cmd %d,%s%s%s%s%s%s%s%s%s%s !!\n",
+		smc_host->mmc->index, cmd->opcode,
+		data ? (data->flags & MMC_DATA_WRITE ? " WR" : " RD") : "",
+		smc_host->int_sum & SDXC_RESP_ERROR     ? " RE"     : "",
+		smc_host->int_sum & SDXC_RESP_CRC_ERROR  ? " RCE"    : "",
+		smc_host->int_sum & SDXC_DATA_CRC_ERROR  ? " DCE"    : "",
+		smc_host->int_sum & SDXC_RESP_TIMEOUT ? " RTO"    : "",
+		smc_host->int_sum & SDXC_DATA_TIMEOUT ? " DTO"    : "",
+		smc_host->int_sum & SDXC_FIFO_RUN_ERROR  ? " FE"     : "",
+		smc_host->int_sum & SDXC_HARD_WARE_LOCKED ? " HL"     : "",
+		smc_host->int_sum & SDXC_START_BIT_ERROR ? " SBE"    : "",
+		smc_host->int_sum & SDXC_END_BIT_ERROR   ? " EBE"    : ""
+		);
+}
+
+static void sunxi_mmc_finalize_request(struct sunxi_mmc_host *host)
+{
+	struct mmc_request *mrq;
+	unsigned long iflags;
+
+	spin_lock_irqsave(&host->lock, iflags);
+
+	mrq = host->mrq;
+	if (!mrq) {
+		spin_unlock_irqrestore(&host->lock, iflags);
+		dev_err(mmc_dev(host->mmc), "no request to finalize\n");
+		return;
+	}
+
+	if (host->int_sum & SDXC_INTERRUPT_ERROR_BIT) {
+		sunxi_mmc_dump_errinfo(host);
+		mrq->cmd->error = -ETIMEDOUT;
+		if (mrq->data)
+			mrq->data->error = -ETIMEDOUT;
+		if (mrq->stop)
+			mrq->stop->error = -ETIMEDOUT;
+	} else {
+		if (mrq->cmd->flags & MMC_RSP_136) {
+			mrq->cmd->resp[0] = mci_readl(host, REG_RESP3);
+			mrq->cmd->resp[1] = mci_readl(host, REG_RESP2);
+			mrq->cmd->resp[2] = mci_readl(host, REG_RESP1);
+			mrq->cmd->resp[3] = mci_readl(host, REG_RESP0);
+		} else {
+			mrq->cmd->resp[0] = mci_readl(host, REG_RESP0);
+		}
+		if (mrq->data)
+			mrq->data->bytes_xfered =
+				mrq->data->blocks * mrq->data->blksz;
+	}
+
+	if (mrq->data) {
+		struct mmc_data *data = mrq->data;
+		u32 temp;
+
+		mci_writel(host, REG_IDST, 0x337);
+		mci_writel(host, REG_DMAC, 0);
+		temp = mci_readl(host, REG_GCTRL);
+		mci_writel(host, REG_GCTRL, temp|SDXC_DMA_RESET);
+		temp &= ~SDXC_DMA_ENABLE_BIT;
+		mci_writel(host, REG_GCTRL, temp);
+		temp |= SDXC_FIFO_RESET;
+		mci_writel(host, REG_GCTRL, temp);
+		dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
+				     sunxi_mmc_get_dma_dir(data));
+	}
+
+	mci_writel(host, REG_RINTR, 0xffff);
+
+	dev_dbg(mmc_dev(host->mmc), "req done, resp %08x %08x %08x %08x\n",
+		mrq->cmd->resp[0], mrq->cmd->resp[1],
+		mrq->cmd->resp[2], mrq->cmd->resp[3]);
+
+	host->mrq = NULL;
+	host->int_sum = 0;
+	host->wait_dma = 0;
+
+	spin_unlock_irqrestore(&host->lock, iflags);
+
+	if (mrq->data && mrq->data->error) {
+		dev_err(mmc_dev(host->mmc),
+			"data error, sending stop command\n");
+		sunxi_mmc_send_manual_stop(host, mrq);
+	}
+
+	mmc_request_done(host->mmc, mrq);
+}
+
+static irqreturn_t sunxi_mmc_irq(int irq, void *dev_id)
+{
+	struct sunxi_mmc_host *host = dev_id;
+	u32 finalize = 0;
+	u32 sdio_int = 0;
+	u32 msk_int;
+	u32 idma_int;
+
+	spin_lock(&host->lock);
+
+	idma_int  = mci_readl(host, REG_IDST);
+	msk_int   = mci_readl(host, REG_MISTA);
+
+	dev_dbg(mmc_dev(host->mmc), "irq: rq %p mi %08x idi %08x\n",
+		host->mrq, msk_int, idma_int);
+
+	if (host->mrq) {
+		if (idma_int & SDXC_IDMAC_RECEIVE_INTERRUPT)
+			host->wait_dma = 0;
+
+		host->int_sum |= msk_int;
+
+		/* Wait for COMMAND_DONE on RESPONSE_TIMEOUT before finishing the req */
+		if ((host->int_sum & SDXC_RESP_TIMEOUT) &&
+				!(host->int_sum & SDXC_COMMAND_DONE))
+			mci_writel(host, REG_IMASK,
+				   host->sdio_imask | SDXC_COMMAND_DONE);
+		else if (host->int_sum & SDXC_INTERRUPT_ERROR_BIT)
+			finalize = 1; /* Don't wait for dma on error */
+		else if (host->int_sum & SDXC_INTERRUPT_DONE_BIT && !host->wait_dma)
+			finalize = 1; /* Done */
+
+		if (finalize) {
+			mci_writel(host, REG_IMASK, host->sdio_imask);
+			mci_writel(host, REG_IDIE, 0);
+		}
+	}
+
+	if (msk_int & SDXC_SDIO_INTERRUPT)
+		sdio_int = 1;
+
+	mci_writel(host, REG_RINTR, msk_int);
+	mci_writel(host, REG_IDST, idma_int);
+
+	spin_unlock(&host->lock);
+
+	if (finalize)
+		tasklet_schedule(&host->tasklet);
+
+	if (sdio_int)
+		mmc_signal_sdio_irq(host->mmc);
+
+	return IRQ_HANDLED;
+}
+
+static void sunxi_mmc_tasklet(unsigned long data)
+{
+	struct sunxi_mmc_host *smc_host = (struct sunxi_mmc_host *) data;
+	sunxi_mmc_finalize_request(smc_host);
+}
+
+static void sunxi_mmc_oclk_onoff(struct sunxi_mmc_host *host, u32 oclk_en)
+{
+	unsigned long expire = jiffies + msecs_to_jiffies(2000);
+	u32 rval;
+
+	rval = mci_readl(host, REG_CLKCR);
+	rval &= ~(SDXC_CARD_CLOCK_ON | SDXC_LOW_POWER_ON);
+
+	if (oclk_en)
+		rval |= SDXC_CARD_CLOCK_ON;
+
+	if (!host->io_flag)
+		rval |= SDXC_LOW_POWER_ON;
+
+	mci_writel(host, REG_CLKCR, rval);
+
+	rval = SDXC_START | SDXC_UPCLK_ONLY | SDXC_WAIT_PRE_OVER;
+	if (host->voltage_switching)
+		rval |= SDXC_VOLTAGE_SWITCH;
+	mci_writel(host, REG_CMDR, rval);
+
+	do {
+		rval = mci_readl(host, REG_CMDR);
+	} while (time_before(jiffies, expire) && (rval & SDXC_START));
+
+	if (rval & SDXC_START) {
+		dev_err(mmc_dev(host->mmc), "fatal err update clk timeout\n");
+		host->ferror = 1;
+	}
+}
+
+static void sunxi_mmc_set_clk_dly(struct sunxi_mmc_host *smc_host,
+				  u32 oclk_dly, u32 sclk_dly)
+{
+	unsigned long iflags;
+	struct clk_hw *hw = __clk_get_hw(smc_host->clk_mod);
+
+	spin_lock_irqsave(&smc_host->lock, iflags);
+	clk_sunxi_mmc_phase_control(hw, sclk_dly, oclk_dly);
+	spin_unlock_irqrestore(&smc_host->lock, iflags);
+}
+
+struct sunxi_mmc_clk_dly mmc_clk_dly[MMC_CLK_MOD_NUM] = {
+	{ MMC_CLK_400K, 0, 7 },
+	{ MMC_CLK_25M, 0, 5 },
+	{ MMC_CLK_50M, 3, 5 },
+	{ MMC_CLK_50MDDR, 2, 4 },
+	{ MMC_CLK_50MDDR_8BIT, 2, 4 },
+	{ MMC_CLK_100M, 1, 4 },
+	{ MMC_CLK_200M, 1, 4 },
+};
+
+static void sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *smc_host,
+				   unsigned int rate)
+{
+	u32 newrate;
+	u32 src_clk;
+	u32 oclk_dly;
+	u32 sclk_dly;
+	u32 temp;
+	struct sunxi_mmc_clk_dly *dly = NULL;
+
+	newrate = clk_round_rate(smc_host->clk_mod, rate);
+	if (smc_host->clk_mod_rate == newrate) {
+		dev_dbg(mmc_dev(smc_host->mmc), "clk already %d, rounded %d\n",
+			rate, newrate);
+		return;
+	}
+
+	dev_dbg(mmc_dev(smc_host->mmc), "setting clk to %d, rounded %d\n",
+		rate, newrate);
+
+	/* setting clock rate */
+	clk_disable(smc_host->clk_mod);
+	clk_set_rate(smc_host->clk_mod, newrate);
+	clk_enable(smc_host->clk_mod);
+	smc_host->clk_mod_rate = newrate = clk_get_rate(smc_host->clk_mod);
+	dev_dbg(mmc_dev(smc_host->mmc), "clk is now %d\n", newrate);
+
+	sunxi_mmc_oclk_onoff(smc_host, 0);
+	/* clear internal divider */
+	temp = mci_readl(smc_host, REG_CLKCR);
+	temp &= ~0xff;
+	mci_writel(smc_host, REG_CLKCR, temp);
+
+	/* determine delays */
+	if (rate <= 400000) {
+		dly = &mmc_clk_dly[MMC_CLK_400K];
+	} else if (rate <= 25000000) {
+		dly = &mmc_clk_dly[MMC_CLK_25M];
+	} else if (rate <= 50000000) {
+		if (smc_host->ddr) {
+			if (smc_host->bus_width == 8)
+				dly = &mmc_clk_dly[MMC_CLK_50MDDR_8BIT];
+			else
+				dly = &mmc_clk_dly[MMC_CLK_50MDDR];
+		} else {
+			dly = &mmc_clk_dly[MMC_CLK_50M];
+		}
+	} else if (rate <= 104000000) {
+		dly = &mmc_clk_dly[MMC_CLK_100M];
+	} else if (rate <= 208000000) {
+		dly = &mmc_clk_dly[MMC_CLK_200M];
+	} else {
+		dly = &mmc_clk_dly[MMC_CLK_50M];
+	}
+
+	oclk_dly = dly->oclk_dly;
+	sclk_dly = dly->sclk_dly;
+
+	src_clk = clk_get_rate(clk_get_parent(smc_host->clk_mod));
+
+	if (src_clk >= 300000000 && src_clk <= 400000000) {
+		if (oclk_dly)
+			oclk_dly--;
+		if (sclk_dly)
+			sclk_dly--;
+	}
+
+	sunxi_mmc_set_clk_dly(smc_host, oclk_dly, sclk_dly);
+	sunxi_mmc_oclk_onoff(smc_host, 1);
+
+	/* oclk_onoff sets various irq status bits, clear these */
+	mci_writel(smc_host, REG_RINTR,
+		   mci_readl(smc_host, REG_RINTR) & ~SDXC_SDIO_INTERRUPT);
+}
+
+static void sunxi_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+	struct sunxi_mmc_host *host = mmc_priv(mmc);
+	u32 temp;
+	s32 err;
+
+	/* Set the power state */
+	switch (ios->power_mode) {
+	case MMC_POWER_ON:
+		break;
+
+	case MMC_POWER_UP:
+		if (!IS_ERR(host->vmmc)) {
+			mmc_regulator_set_ocr(host->mmc, host->vmmc, ios->vdd);
+			udelay(200);
+		}
+
+		err = sunxi_mmc_init_host(mmc);
+		if (err) {
+			host->ferror = 1;
+			return;
+		}
+		enable_irq(host->irq);
+
+		dev_dbg(mmc_dev(host->mmc), "power on!\n");
+		host->ferror = 0;
+		break;
+
+	case MMC_POWER_OFF:
+		dev_dbg(mmc_dev(host->mmc), "power off!\n");
+		disable_irq(host->irq);
+		sunxi_mmc_exit_host(host);
+		if (!IS_ERR(host->vmmc))
+			mmc_regulator_set_ocr(host->mmc, host->vmmc, 0);
+		host->ferror = 0;
+		break;
+	}
+
+	/* set bus width */
+	switch (ios->bus_width) {
+	case MMC_BUS_WIDTH_1:
+		mci_writel(host, REG_WIDTH, SDXC_WIDTH1);
+		host->bus_width = 1;
+		break;
+	case MMC_BUS_WIDTH_4:
+		mci_writel(host, REG_WIDTH, SDXC_WIDTH4);
+		host->bus_width = 4;
+		break;
+	case MMC_BUS_WIDTH_8:
+		mci_writel(host, REG_WIDTH, SDXC_WIDTH8);
+		host->bus_width = 8;
+		break;
+	}
+
+	/* set ddr mode */
+	temp = mci_readl(host, REG_GCTRL);
+	if (ios->timing == MMC_TIMING_UHS_DDR50) {
+		temp |= SDXC_DDR_MODE;
+		host->ddr = 1;
+	} else {
+		temp &= ~SDXC_DDR_MODE;
+		host->ddr = 0;
+	}
+	mci_writel(host, REG_GCTRL, temp);
+
+	/* set up clock */
+	if (ios->clock && ios->power_mode) {
+		dev_dbg(mmc_dev(host->mmc), "ios->clock: %d\n", ios->clock);
+		sunxi_mmc_clk_set_rate(host, ios->clock);
+		usleep_range(50000, 55000);
+	}
+}
+
+static void sunxi_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
+{
+	struct sunxi_mmc_host *smc_host = mmc_priv(mmc);
+	unsigned long flags;
+	int ret;
+	u32 imask;
+
+	spin_lock_irqsave(&smc_host->lock, flags);
+
+	/* Make sure the controller is in a sane state before enabling irqs */
+	ret = sunxi_mmc_init_host(mmc);
+	if (ret) {
+		spin_unlock_irqrestore(&smc_host->lock, flags);
+		return;
+	}
+
+	imask = mci_readl(smc_host, REG_IMASK);
+	if (enable) {
+		smc_host->sdio_imask = SDXC_SDIO_INTERRUPT;
+		imask |= SDXC_SDIO_INTERRUPT;
+	} else {
+		smc_host->sdio_imask = 0;
+		imask &= ~SDXC_SDIO_INTERRUPT;
+	}
+	mci_writel(smc_host, REG_IMASK, imask);
+	spin_unlock_irqrestore(&smc_host->lock, flags);
+}
+
+static void sunxi_mmc_hw_reset(struct mmc_host *mmc)
+{
+	struct sunxi_mmc_host *smc_host = mmc_priv(mmc);
+	mci_writel(smc_host, REG_HWRST, 0);
+	udelay(10);
+	mci_writel(smc_host, REG_HWRST, 1);
+	udelay(300);
+}
+
+static void sunxi_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+	struct sunxi_mmc_host *host = mmc_priv(mmc);
+	struct mmc_command *cmd = mrq->cmd;
+	struct mmc_data *data = mrq->data;
+	unsigned long iflags;
+	u32 imask = SDXC_INTERRUPT_ERROR_BIT;
+	u32 cmd_val = SDXC_START | (cmd->opcode & 0x3f);
+	u32 byte_cnt = 0;
+	int ret;
+
+	if (!mmc_gpio_get_cd(mmc) || host->ferror) {
+		dev_dbg(mmc_dev(host->mmc), "no medium present\n");
+		mrq->cmd->error = -ENOMEDIUM;
+		mmc_request_done(mmc, mrq);
+		return;
+	}
+
+	if (data) {
+		byte_cnt = data->blksz * data->blocks;
+		mci_writel(host, REG_BLKSZ, data->blksz);
+		mci_writel(host, REG_BCNTR, byte_cnt);
+		ret = sunxi_mmc_prepare_dma(host, data);
+		if (ret < 0) {
+			dev_err(mmc_dev(host->mmc), "prepare DMA failed\n");
+			cmd->error = ret;
+			cmd->data->error = ret;
+			mmc_request_done(host->mmc, mrq);
+			return;
+		}
+	}
+
+	if (cmd->opcode == MMC_GO_IDLE_STATE) {
+		cmd_val |= SDXC_SEND_INIT_SEQUENCE;
+		imask |= SDXC_COMMAND_DONE;
+	}
+
+	if (cmd->opcode == SD_SWITCH_VOLTAGE) {
+		cmd_val |= SDXC_VOLTAGE_SWITCH;
+		imask |= SDXC_VOLTAGE_CHANGE_DONE;
+		host->voltage_switching = 1;
+		sunxi_mmc_oclk_onoff(host, 1);
+	}
+
+	if (cmd->flags & MMC_RSP_PRESENT) {
+		cmd_val |= SDXC_RESP_EXPIRE;
+		if (cmd->flags & MMC_RSP_136)
+			cmd_val |= SDXC_LONG_RESPONSE;
+		if (cmd->flags & MMC_RSP_CRC)
+			cmd_val |= SDXC_CHECK_RESPONSE_CRC;
+
+		if ((cmd->flags & MMC_CMD_MASK) == MMC_CMD_ADTC) {
+			cmd_val |= SDXC_DATA_EXPIRE | SDXC_WAIT_PRE_OVER;
+			if (cmd->data->flags & MMC_DATA_STREAM) {
+				imask |= SDXC_AUTO_COMMAND_DONE;
+				cmd_val |= SDXC_SEQUENCE_MODE | SDXC_SEND_AUTO_STOP;
+			}
+			if (cmd->data->stop) {
+				imask |= SDXC_AUTO_COMMAND_DONE;
+				cmd_val |= SDXC_SEND_AUTO_STOP;
+			} else
+				imask |= SDXC_DATA_OVER;
+
+			if (cmd->data->flags & MMC_DATA_WRITE)
+				cmd_val |= SDXC_WRITE;
+			else
+				host->wait_dma = 1;
+		} else
+			imask |= SDXC_COMMAND_DONE;
+	} else
+		imask |= SDXC_COMMAND_DONE;
+
+	dev_dbg(mmc_dev(host->mmc), "cmd %d(%08x) arg %x ie 0x%08x len %d\n",
+		cmd_val & 0x3f, cmd_val, cmd->arg, imask,
+		mrq->data ? mrq->data->blksz * mrq->data->blocks : 0);
+
+	spin_lock_irqsave(&host->lock, iflags);
+	host->mrq = mrq;
+	mci_writel(host, REG_IMASK, host->sdio_imask | imask);
+	spin_unlock_irqrestore(&host->lock, iflags);
+
+	mci_writel(host, REG_CARG, cmd->arg);
+	mci_writel(host, REG_CMDR, cmd_val);
+}
+
+static const struct of_device_id sunxi_mmc_of_match[] = {
+	{ .compatible = "allwinner,sun4i-a10-mmc", },
+	{ .compatible = "allwinner,sun5i-a13-mmc", },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, sunxi_mmc_of_match);
+
+static struct mmc_host_ops sunxi_mmc_ops = {
+	.request	 = sunxi_mmc_request,
+	.set_ios	 = sunxi_mmc_set_ios,
+	.get_ro		 = mmc_gpio_get_ro,
+	.get_cd		 = mmc_gpio_get_cd,
+	.enable_sdio_irq = sunxi_mmc_enable_sdio_irq,
+	.hw_reset	 = sunxi_mmc_hw_reset,
+};
+
+static int sunxi_mmc_resource_request(struct sunxi_mmc_host *host,
+				      struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	int ret;
+
+	if (of_device_is_compatible(np, "allwinner,sun4i-a10-mmc"))
+		host->idma_des_size_bits = 13;
+	else
+		host->idma_des_size_bits = 16;
+
+	host->vmmc = devm_regulator_get_optional(&pdev->dev, "vmmc");
+	if (IS_ERR(host->vmmc) && PTR_ERR(host->vmmc) == -EPROBE_DEFER)
+		return -EPROBE_DEFER;
+
+	host->reg_base = devm_ioremap_resource(&pdev->dev,
+			      platform_get_resource(pdev, IORESOURCE_MEM, 0));
+	if (IS_ERR(host->reg_base))
+		return PTR_ERR(host->reg_base);
+
+	host->clk_ahb = devm_clk_get(&pdev->dev, "ahb");
+	if (IS_ERR(host->clk_ahb)) {
+		dev_err(&pdev->dev, "Could not get ahb clock\n");
+		return PTR_ERR(host->clk_ahb);
+	}
+
+	host->clk_mod = devm_clk_get(&pdev->dev, "mod");
+	if (IS_ERR(host->clk_mod)) {
+		dev_err(&pdev->dev, "Could not get mod clock\n");
+		return PTR_ERR(host->clk_mod);
+	}
+
+	/* Make sure the controller is in a sane state before enabling irqs */
+	ret = sunxi_mmc_init_host(host->mmc);
+	if (ret)
+		return ret;
+
+	host->irq = platform_get_irq(pdev, 0);
+	ret = devm_request_irq(&pdev->dev, host->irq, sunxi_mmc_irq, 0,
+			       "sunxi-mmc", host);
+	if (ret == 0)
+		disable_irq(host->irq);
+
+	/* And put it back in reset */
+	sunxi_mmc_exit_host(host);
+
+	return ret;
+}
+
+static int sunxi_mmc_probe(struct platform_device *pdev)
+{
+	struct sunxi_mmc_host *host;
+	struct mmc_host *mmc;
+	int ret;
+
+	mmc = mmc_alloc_host(sizeof(struct sunxi_mmc_host), &pdev->dev);
+	if (!mmc) {
+		dev_err(&pdev->dev, "mmc alloc host failed\n");
+		return -ENOMEM;
+	}
+
+	ret = mmc_of_parse(mmc);
+	if (ret)
+		goto error_free_host;
+
+	host = mmc_priv(mmc);
+	host->mmc = mmc;
+	spin_lock_init(&host->lock);
+	tasklet_init(&host->tasklet, sunxi_mmc_tasklet, (unsigned long)host);
+
+	ret = sunxi_mmc_resource_request(host, pdev);
+	if (ret)
+		goto error_free_host;
+
+	host->sg_cpu = dma_alloc_coherent(&pdev->dev, PAGE_SIZE,
+					  &host->sg_dma, GFP_KERNEL);
+	if (!host->sg_cpu) {
+		dev_err(&pdev->dev, "Failed to allocate DMA descriptor mem\n");
+		ret = -ENOMEM;
+		goto error_free_host;
+	}
+
+	mmc->ops		= &sunxi_mmc_ops;
+	mmc->max_blk_count	= 8192;
+	mmc->max_blk_size	= 4096;
+	mmc->max_segs		= PAGE_SIZE / sizeof(struct sunxi_idma_des);
+	mmc->max_seg_size	= (1 << host->idma_des_size_bits);
+	mmc->max_req_size	= mmc->max_seg_size * mmc->max_segs;
+	/* 400kHz ~ 50MHz */
+	mmc->f_min		=   400000;
+	mmc->f_max		= 50000000;
+	/* available voltages */
+	if (!IS_ERR(host->vmmc))
+		mmc->ocr_avail = mmc_regulator_get_ocrmask(host->vmmc);
+	else
+		mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
+
+	mmc->caps = MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
+		MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | MMC_CAP_UHS_SDR50 |
+		MMC_CAP_UHS_DDR50 | MMC_CAP_SDIO_IRQ | MMC_CAP_DRIVER_TYPE_A;
+	mmc->caps2 = MMC_CAP2_NO_PRESCAN_POWERUP;
+
+	ret = mmc_add_host(mmc);
+
+	if (ret)
+		goto error_free_dma;
+
+	dev_info(&pdev->dev, "base:0x%p irq:%u\n", host->reg_base, host->irq);
+	platform_set_drvdata(pdev, mmc);
+	return 0;
+
+error_free_dma:
+	dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
+error_free_host:
+	mmc_free_host(mmc);
+	return ret;
+}
+
+static int sunxi_mmc_remove(struct platform_device *pdev)
+{
+	struct mmc_host	*mmc = platform_get_drvdata(pdev);
+	struct sunxi_mmc_host *host = mmc_priv(mmc);
+
+	mmc_remove_host(mmc);
+	sunxi_mmc_exit_host(host);
+	tasklet_disable(&host->tasklet);
+	dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
+	mmc_free_host(mmc);
+
+	return 0;
+}
+
+static struct platform_driver sunxi_mmc_driver = {
+	.driver = {
+		.name	= "sunxi-mmc",
+		.owner	= THIS_MODULE,
+		.of_match_table = of_match_ptr(sunxi_mmc_of_match),
+	},
+	.probe		= sunxi_mmc_probe,
+	.remove		= sunxi_mmc_remove,
+};
+module_platform_driver(sunxi_mmc_driver);
+
+MODULE_DESCRIPTION("Allwinner's SD/MMC Card Controller Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("David Lanzendörfer <david.lanzendoerfer-Z7Kmv9EsliU@public.gmane.org>");
+MODULE_ALIAS("platform:sunxi-mmc");
diff --git a/drivers/mmc/host/sunxi-mmc.h b/drivers/mmc/host/sunxi-mmc.h
new file mode 100644
index 0000000..75eaa02
--- /dev/null
+++ b/drivers/mmc/host/sunxi-mmc.h
@@ -0,0 +1,239 @@
+/*
+ * Driver for sunxi SD/MMC host controllers
+ * (C) Copyright 2007-2011 Reuuimlla Technology Co., Ltd.
+ * (C) Copyright 2007-2011 Aaron Maoye <leafy.myeh-jFKXxz0WcGyYHARAtoI1EgC/G2K4zDHf@public.gmane.org>
+ * (C) Copyright 2013-2014 O2S GmbH <www.o2s.ch>
+ * (C) Copyright 2013-2014 David Lanzendörfer <david.lanzendoerfer@o2s.ch>
+ * (C) Copyright 2013-2014 Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#ifndef __SUNXI_MMC_H__
+#define __SUNXI_MMC_H__
+
+/* register offset definitions */
+#define SDXC_REG_GCTRL	(0x00) /* SMC Global Control Register */
+#define SDXC_REG_CLKCR	(0x04) /* SMC Clock Control Register */
+#define SDXC_REG_TMOUT	(0x08) /* SMC Time Out Register */
+#define SDXC_REG_WIDTH	(0x0C) /* SMC Bus Width Register */
+#define SDXC_REG_BLKSZ	(0x10) /* SMC Block Size Register */
+#define SDXC_REG_BCNTR	(0x14) /* SMC Byte Count Register */
+#define SDXC_REG_CMDR	(0x18) /* SMC Command Register */
+#define SDXC_REG_CARG	(0x1C) /* SMC Argument Register */
+#define SDXC_REG_RESP0	(0x20) /* SMC Response Register 0 */
+#define SDXC_REG_RESP1	(0x24) /* SMC Response Register 1 */
+#define SDXC_REG_RESP2	(0x28) /* SMC Response Register 2 */
+#define SDXC_REG_RESP3	(0x2C) /* SMC Response Register 3 */
+#define SDXC_REG_IMASK	(0x30) /* SMC Interrupt Mask Register */
+#define SDXC_REG_MISTA	(0x34) /* SMC Masked Interrupt Status Register */
+#define SDXC_REG_RINTR	(0x38) /* SMC Raw Interrupt Status Register */
+#define SDXC_REG_STAS	(0x3C) /* SMC Status Register */
+#define SDXC_REG_FTRGL	(0x40) /* SMC FIFO Threshold Watermark Registe */
+#define SDXC_REG_FUNS	(0x44) /* SMC Function Select Register */
+#define SDXC_REG_CBCR	(0x48) /* SMC CIU Byte Count Register */
+#define SDXC_REG_BBCR	(0x4C) /* SMC BIU Byte Count Register */
+#define SDXC_REG_DBGC	(0x50) /* SMC Debug Enable Register */
+#define SDXC_REG_HWRST	(0x78) /* SMC Card Hardware Reset for Register */
+#define SDXC_REG_DMAC	(0x80) /* SMC IDMAC Control Register */
+#define SDXC_REG_DLBA	(0x84) /* SMC IDMAC Descriptor List Base Addre */
+#define SDXC_REG_IDST	(0x88) /* SMC IDMAC Status Register */
+#define SDXC_REG_IDIE	(0x8C) /* SMC IDMAC Interrupt Enable Register */
+#define SDXC_REG_CHDA	(0x90)
+#define SDXC_REG_CBDA	(0x94)
+
+#define mci_readl(host, reg) \
+	readl((host)->reg_base + SDXC_##reg)
+#define mci_writel(host, reg, value) \
+	writel((value), (host)->reg_base + SDXC_##reg)
+
+/* global control register bits */
+#define SDXC_SOFT_RESET		BIT(0)
+#define SDXC_FIFO_RESET		BIT(1)
+#define SDXC_DMA_RESET		BIT(2)
+#define SDXC_HARDWARE_RESET		(SDXC_SOFT_RESET|SDXC_FIFO_RESET|SDXC_DMA_RESET)
+#define SDXC_INTERRUPT_ENABLE_BIT		BIT(4)
+#define SDXC_DMA_ENABLE_BIT		BIT(5)
+#define SDXC_DEBOUNCE_ENABLE_BIT	BIT(8)
+#define SDXC_POSEDGE_LATCH_DATA	BIT(9)
+#define SDXC_DDR_MODE		BIT(10)
+#define SDXC_MEMORY_ACCESS_DONE	BIT(29)
+#define SDXC_ACCESS_DONE_DIRECT	BIT(30)
+#define SDXC_ACCESS_BY_AHB	BIT(31)
+#define SDXC_ACCESS_BY_DMA	(0U << 31)
+/* clock control bits */
+#define SDXC_CARD_CLOCK_ON		BIT(16)
+#define SDXC_LOW_POWER_ON		BIT(17)
+/* bus width */
+#define SDXC_WIDTH1		(0)
+#define SDXC_WIDTH4		(1)
+#define SDXC_WIDTH8		(2)
+/* smc command bits */
+#define SDXC_RESP_EXPIRE		BIT(6)
+#define SDXC_LONG_RESPONSE		BIT(7)
+#define SDXC_CHECK_RESPONSE_CRC	BIT(8)
+#define SDXC_DATA_EXPIRE		BIT(9)
+#define SDXC_WRITE		BIT(10)
+#define SDXC_SEQUENCE_MODE		BIT(11)
+#define SDXC_SEND_AUTO_STOP	BIT(12)
+#define SDXC_WAIT_PRE_OVER	BIT(13)
+#define SDXC_STOP_ABORT_CMD	BIT(14)
+#define SDXC_SEND_INIT_SEQUENCE	BIT(15)
+#define SDXC_UPCLK_ONLY		BIT(21)
+#define SDXC_READ_CEATA_DEV		BIT(22)
+#define SDXC_CCS_EXPIRE		BIT(23)
+#define SDXC_ENABLE_BIT_BOOT		BIT(24)
+#define SDXC_ALT_BOOT_OPTIONS		BIT(25)
+#define SDXC_BOOT_ACK_EXPIRE		BIT(26)
+#define SDXC_BOOT_ABORT		BIT(27)
+#define SDXC_VOLTAGE_SWITCH	        BIT(28)
+#define SDXC_USE_HOLD_REGISTER	        BIT(29)
+#define SDXC_START	        BIT(31)
+/* interrupt bits */
+#define SDXC_RESP_ERROR		BIT(1)
+#define SDXC_COMMAND_DONE		BIT(2)
+#define SDXC_DATA_OVER		BIT(3)
+#define SDXC_TX_DATA_REQUEST		BIT(4)
+#define SDXC_RX_DATA_REQUEST		BIT(5)
+#define SDXC_RESP_CRC_ERROR		BIT(6)
+#define SDXC_DATA_CRC_ERROR		BIT(7)
+#define SDXC_RESP_TIMEOUT	BIT(8)
+#define SDXC_DATA_TIMEOUT	BIT(9)
+#define SDXC_VOLTAGE_CHANGE_DONE		BIT(10)
+#define SDXC_FIFO_RUN_ERROR		BIT(11)
+#define SDXC_HARD_WARE_LOCKED	BIT(12)
+#define SDXC_START_BIT_ERROR	BIT(13)
+#define SDXC_AUTO_COMMAND_DONE	BIT(14)
+#define SDXC_END_BIT_ERROR		BIT(15)
+#define SDXC_SDIO_INTERRUPT		BIT(16)
+#define SDXC_CARD_INSERT		BIT(30)
+#define SDXC_CARD_REMOVE		BIT(31)
+#define SDXC_INTERRUPT_ERROR_BIT		(SDXC_RESP_ERROR | SDXC_RESP_CRC_ERROR | \
+				 SDXC_DATA_CRC_ERROR | SDXC_RESP_TIMEOUT | \
+				 SDXC_DATA_TIMEOUT | SDXC_FIFO_RUN_ERROR | \
+				 SDXC_HARD_WARE_LOCKED | SDXC_START_BIT_ERROR | \
+				 SDXC_END_BIT_ERROR) /* 0xbbc2 */
+#define SDXC_INTERRUPT_DONE_BIT		(SDXC_AUTO_COMMAND_DONE | SDXC_DATA_OVER | \
+				 SDXC_COMMAND_DONE | SDXC_VOLTAGE_CHANGE_DONE)
+/* status */
+#define SDXC_RXWL_FLAG		BIT(0)
+#define SDXC_TXWL_FLAG		BIT(1)
+#define SDXC_FIFO_EMPTY		BIT(2)
+#define SDXC_FIFO_FULL		BIT(3)
+#define SDXC_CARD_PRESENT	BIT(8)
+#define SDXC_CARD_DATA_BUSY	BIT(9)
+#define SDXC_DATA_FSM_BUSY	BIT(10)
+#define SDXC_DMA_REQUEST		BIT(31)
+#define SDXC_FIFO_SIZE		(16)
+/* Function select */
+#define SDXC_CEATA_ON		(0xceaaU << 16)
+#define SDXC_SEND_IRQ_RESPONSE		BIT(0)
+#define SDXC_SDIO_READ_WAIT		BIT(1)
+#define SDXC_ABORT_READ_DATA		BIT(2)
+#define SDXC_SEND_CCSD		BIT(8)
+#define SDXC_SEND_AUTO_STOPCCSD	BIT(9)
+#define SDXC_CEATA_DEV_INTERRUPT_ENABLE_BIT	BIT(10)
+/* IDMA controller bus mod bit field */
+#define SDXC_IDMAC_SOFT_RESET	BIT(0)
+#define SDXC_IDMAC_FIX_BURST	BIT(1)
+#define SDXC_IDMAC_IDMA_ON	BIT(7)
+#define SDXC_IDMAC_REFETCH_DES	BIT(31)
+/* IDMA status bit field */
+#define SDXC_IDMAC_TRANSMIT_INTERRUPT	BIT(0)
+#define SDXC_IDMAC_RECEIVE_INTERRUPT	BIT(1)
+#define SDXC_IDMAC_FATAL_BUS_ERROR	BIT(2)
+#define SDXC_IDMAC_DESTINATION_INVALID	BIT(4)
+#define SDXC_IDMAC_CARD_ERROR_SUM	BIT(5)
+#define SDXC_IDMAC_NORMAL_INTERRUPT_SUM	BIT(8)
+#define SDXC_IDMAC_ABNORMAL_INTERRUPT_SUM BIT(9)
+#define SDXC_IDMAC_HOST_ABORT_INTERRUPT_TX	BIT(10)
+#define SDXC_IDMAC_HOST_ABORT_INTERRUPT_RX	BIT(10)
+#define SDXC_IDMAC_IDLE		(0U << 13)
+#define SDXC_IDMAC_SUSPEND	(1U << 13)
+#define SDXC_IDMAC_DESC_READ	(2U << 13)
+#define SDXC_IDMAC_DESC_CHECK	(3U << 13)
+#define SDXC_IDMAC_READ_REQUEST_WAIT	(4U << 13)
+#define SDXC_IDMAC_WRITE_REQUEST_WAIT	(5U << 13)
+#define SDXC_IDMAC_READ		(6U << 13)
+#define SDXC_IDMAC_WRITE		(7U << 13)
+#define SDXC_IDMAC_DESC_CLOSE	(8U << 13)
+
+/*
+* If the idma-des-size-bits of property is ie 13, bufsize bits are:
+*  Bits  0-12: buf1 size
+*  Bits 13-25: buf2 size
+*  Bits 26-31: not used
+* Since we only ever set buf1 size, we can simply store it directly.
+*/
+#define SDXC_IDMAC_DES0_DIC	BIT(1)  /* disable interrupt on completion */
+#define SDXC_IDMAC_DES0_LD	BIT(2)  /* last descriptor */
+#define SDXC_IDMAC_DES0_FD	BIT(3)  /* first descriptor */
+#define SDXC_IDMAC_DES0_CH	BIT(4)  /* chain mode */
+#define SDXC_IDMAC_DES0_ER	BIT(5)  /* end of ring */
+#define SDXC_IDMAC_DES0_CES	BIT(30) /* card error summary */
+#define SDXC_IDMAC_DES0_OWN	BIT(31) /* 1-idma owns it, 0-host owns it */
+
+struct sunxi_idma_des {
+	u32	config;
+	u32	buf_size;
+	u32	buf_addr_ptr1;
+	u32	buf_addr_ptr2;
+};
+
+struct sunxi_mmc_host {
+	struct mmc_host *mmc;
+	struct regulator *vmmc;
+
+	/* IO mapping base */
+	void __iomem *reg_base;
+
+	spinlock_t lock;
+	struct tasklet_struct tasklet;
+
+	/* clock management */
+	struct clk *clk_ahb;
+	struct clk *clk_mod;
+
+	/* ios information */
+	u32		clk_mod_rate;
+	u32		bus_width;
+	u32		idma_des_size_bits;
+	u32		ddr;
+	u32		voltage_switching;
+
+	/* irq */
+	int		irq;
+	u32		int_sum;
+	u32		sdio_imask;
+
+	/* flags */
+	u32		power_on:1;
+	u32		io_flag:1;
+	u32		wait_dma:1;
+
+	dma_addr_t	sg_dma;
+	void		*sg_cpu;
+
+	struct mmc_request *mrq;
+	u32		ferror;
+};
+
+#define MMC_CLK_400K            0
+#define MMC_CLK_25M             1
+#define MMC_CLK_50M             2
+#define MMC_CLK_50MDDR          3
+#define MMC_CLK_50MDDR_8BIT     4
+#define MMC_CLK_100M            5
+#define MMC_CLK_200M            6
+#define MMC_CLK_MOD_NUM         7
+
+struct sunxi_mmc_clk_dly {
+	u32 mode;
+	u32 oclk_dly;
+	u32 sclk_dly;
+};
+
+#endif

-- 
You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
For more options, visit https://groups.google.com/groups/opt_out.

^ permalink raw reply related

* [PATCH v7 5/8] ARM: dts: sun7i: Add support for mmc
From: David Lanzendörfer @ 2014-02-17 10:02 UTC (permalink / raw)
  To: devicetree-u79uwXL29TY76Z2rM5mHXA, Ulf Hansson, Laurent Pinchart,
	Mike Turquette, Simon Baatz, Hans de Goede, Emilio López,
	linux-mmc-u79uwXL29TY76Z2rM5mHXA, Chris Ball,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, H Hartley Sweeten,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Tejun Heo, Maxime Ripard,
	Guennadi Liakhovetski,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <20140217095907.15040.81893.stgit-pgFh0Jf6HD9Xzn/AsuzBOg@public.gmane.org>

Signed-off-by: David Lanzendörfer <david.lanzendoerfer-Z7Kmv9EsliU@public.gmane.org>
Signed-off-by: Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
---
 arch/arm/boot/dts/sun7i-a20-cubieboard2.dts     |    8 +++
 arch/arm/boot/dts/sun7i-a20-cubietruck.dts      |    8 +++
 arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts |   23 +++++++++
 arch/arm/boot/dts/sun7i-a20.dtsi                |   61 +++++++++++++++++++++++
 4 files changed, 100 insertions(+)

diff --git a/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts b/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts
index 5c51cb8..ae800b6 100644
--- a/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts
+++ b/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts
@@ -34,6 +34,14 @@
 			};
 		};
 
+		mmc0: mmc@01c0f000 {
+			pinctrl-names = "default", "default";
+			pinctrl-0 = <&mmc0_pins_a>;
+			pinctrl-1 = <&mmc0_cd_pin_reference_design>;
+			cd-gpios = <&pio 7 1 0>; /* PH1 */
+			status = "okay";
+		};
+
 		pinctrl@01c20800 {
 			led_pins_cubieboard2: led_pins@0 {
 				allwinner,pins = "PH20", "PH21";
diff --git a/arch/arm/boot/dts/sun7i-a20-cubietruck.dts b/arch/arm/boot/dts/sun7i-a20-cubietruck.dts
index f9dcb61..370cef84 100644
--- a/arch/arm/boot/dts/sun7i-a20-cubietruck.dts
+++ b/arch/arm/boot/dts/sun7i-a20-cubietruck.dts
@@ -19,6 +19,14 @@
 	compatible = "cubietech,cubietruck", "allwinner,sun7i-a20";
 
 	soc@01c00000 {
+		mmc0: mmc@01c0f000 {
+			pinctrl-names = "default", "default";
+			pinctrl-0 = <&mmc0_pins_a>;
+			pinctrl-1 = <&mmc0_cd_pin_reference_design>;
+			cd-gpios = <&pio 7 1 0>; /* PH1 */
+			status = "okay";
+		};
+
 		pinctrl@01c20800 {
 			led_pins_cubietruck: led_pins@0 {
 				allwinner,pins = "PH7", "PH11", "PH20", "PH21";
diff --git a/arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts b/arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts
index ead3013..685ec06 100644
--- a/arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts
+++ b/arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts
@@ -34,7 +34,30 @@
 			};
 		};
 
+		mmc0: mmc@01c0f000 {
+			pinctrl-names = "default", "default";
+			pinctrl-0 = <&mmc0_pins_a>;
+			pinctrl-1 = <&mmc0_cd_pin_reference_design>;
+			cd-gpios = <&pio 7 1 0>; /* PH1 */
+			status = "okay";
+		};
+
+		mmc3: mmc@01c12000 {
+			pinctrl-names = "default", "default";
+			pinctrl-0 = <&mmc3_pins_a>;
+			pinctrl-1 = <&mmc3_cd_pin_olinuxinom>;
+			cd-gpios = <&pio 7 11 0>; /* PH11 */
+			status = "okay";
+		};
+
 		pinctrl@01c20800 {
+			mmc3_cd_pin_olinuxinom: mmc3_cd_pin@0 {
+				allwinner,pins = "PH11";
+				allwinner,function = "gpio_in";
+				allwinner,drive = <0>;
+				allwinner,pull = <1>;
+			};
+
 			led_pins_olinuxino: led_pins@0 {
 				allwinner,pins = "PH2";
 				allwinner,function = "gpio_out";
diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi
index 9ff0948..5b55414 100644
--- a/arch/arm/boot/dts/sun7i-a20.dtsi
+++ b/arch/arm/boot/dts/sun7i-a20.dtsi
@@ -355,6 +355,46 @@
 			#size-cells = <0>;
 		};
 
+		mmc0: mmc@01c0f000 {
+			compatible = "allwinner,sun5i-a13-mmc";
+			reg = <0x01c0f000 0x1000>;
+			clocks = <&ahb_gates 8>, <&mmc0_clk>;
+			clock-names = "ahb", "mod";
+			interrupts = <0 32 4>;
+			bus-width = <4>;
+			status = "disabled";
+		};
+
+		mmc1: mmc@01c10000 {
+			compatible = "allwinner,sun5i-a13-mmc";
+			reg = <0x01c10000 0x1000>;
+			clocks = <&ahb_gates 9>, <&mmc1_clk>;
+			clock-names = "ahb", "mod";
+			interrupts = <0 33 4>;
+			bus-width = <4>;
+			status = "disabled";
+		};
+
+		mmc2: mmc@01c11000 {
+			compatible = "allwinner,sun5i-a13-mmc";
+			reg = <0x01c11000 0x1000>;
+			clocks = <&ahb_gates 10>, <&mmc2_clk>;
+			clock-names = "ahb", "mod";
+			interrupts = <0 34 4>;
+			bus-width = <4>;
+			status = "disabled";
+		};
+
+		mmc3: mmc@01c12000 {
+			compatible = "allwinner,sun5i-a13-mmc";
+			reg = <0x01c12000 0x1000>;
+			clocks = <&ahb_gates 11>, <&mmc3_clk>;
+			clock-names = "ahb", "mod";
+			interrupts = <0 35 4>;
+			bus-width = <4>;
+			status = "disabled";
+		};
+
 		pio: pinctrl@01c20800 {
 			compatible = "allwinner,sun7i-a20-pinctrl";
 			reg = <0x01c20800 0x400>;
@@ -432,6 +472,27 @@
 				allwinner,drive = <0>;
 				allwinner,pull = <0>;
 			};
+
+			mmc0_pins_a: mmc0@0 {
+				allwinner,pins = "PF0","PF1","PF2","PF3","PF4","PF5";
+				allwinner,function = "mmc0";
+				allwinner,drive = <3>;
+				allwinner,pull = <0>;
+			};
+
+			mmc0_cd_pin_reference_design: mmc0_cd_pin@0 {
+				allwinner,pins = "PH1";
+				allwinner,function = "gpio_in";
+				allwinner,drive = <0>;
+				allwinner,pull = <1>;
+			};
+
+			mmc3_pins_a: mmc3@0 {
+				allwinner,pins = "PI4","PI5","PI6","PI7","PI8","PI9";
+				allwinner,function = "mmc3";
+				allwinner,drive = <3>;
+				allwinner,pull = <0>;
+			};
 		};
 
 		timer@01c20c00 {

-- 
You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
For more options, visit https://groups.google.com/groups/opt_out.

^ permalink raw reply related

* [PATCH v7 6/8] ARM: dts: sun4i: Add support for mmc
From: David Lanzendörfer @ 2014-02-17 10:02 UTC (permalink / raw)
  To: devicetree-u79uwXL29TY76Z2rM5mHXA, Ulf Hansson, Laurent Pinchart,
	Mike Turquette, Simon Baatz, Hans de Goede, Emilio López,
	linux-mmc-u79uwXL29TY76Z2rM5mHXA, Chris Ball,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, H Hartley Sweeten,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Tejun Heo, Maxime Ripard,
	Guennadi Liakhovetski,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <20140217095907.15040.81893.stgit-pgFh0Jf6HD9Xzn/AsuzBOg@public.gmane.org>

Signed-off-by: David Lanzendörfer <david.lanzendoerfer-Z7Kmv9EsliU@public.gmane.org>
Signed-off-by: Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
---
 arch/arm/boot/dts/sun4i-a10-a1000.dts      |    8 ++++
 arch/arm/boot/dts/sun4i-a10-cubieboard.dts |    8 ++++
 arch/arm/boot/dts/sun4i-a10.dtsi           |   54 ++++++++++++++++++++++++++++
 3 files changed, 70 insertions(+)

diff --git a/arch/arm/boot/dts/sun4i-a10-a1000.dts b/arch/arm/boot/dts/sun4i-a10-a1000.dts
index d4b081d..a879ef3 100644
--- a/arch/arm/boot/dts/sun4i-a10-a1000.dts
+++ b/arch/arm/boot/dts/sun4i-a10-a1000.dts
@@ -35,6 +35,14 @@
 			};
 		};
 
+		mmc0: mmc@01c0f000 {
+			pinctrl-names = "default", "default";
+			pinctrl-0 = <&mmc0_pins_a>;
+			pinctrl-1 = <&mmc0_cd_pin_reference_design>;
+			cd-gpios = <&pio 7 1 0>; /* PH1 */
+			status = "okay";
+		};
+
 		pinctrl@01c20800 {
 			emac_power_pin_a1000: emac_power_pin@0 {
 				allwinner,pins = "PH15";
diff --git a/arch/arm/boot/dts/sun4i-a10-cubieboard.dts b/arch/arm/boot/dts/sun4i-a10-cubieboard.dts
index b139ee6..20b976a 100644
--- a/arch/arm/boot/dts/sun4i-a10-cubieboard.dts
+++ b/arch/arm/boot/dts/sun4i-a10-cubieboard.dts
@@ -33,6 +33,14 @@
 			};
 		};
 
+		mmc0: mmc@01c0f000 {
+			pinctrl-names = "default", "default";
+			pinctrl-0 = <&mmc0_pins_a>;
+			pinctrl-1 = <&mmc0_cd_pin_reference_design>;
+			cd-gpios = <&pio 7 1 0>; /* PH1 */
+			status = "okay";
+		};
+
 		pinctrl@01c20800 {
 			led_pins_cubieboard: led_pins@0 {
 				allwinner,pins = "PH20", "PH21";
diff --git a/arch/arm/boot/dts/sun4i-a10.dtsi b/arch/arm/boot/dts/sun4i-a10.dtsi
index 9044c53..cd14961 100644
--- a/arch/arm/boot/dts/sun4i-a10.dtsi
+++ b/arch/arm/boot/dts/sun4i-a10.dtsi
@@ -330,6 +330,46 @@
 			#size-cells = <0>;
 		};
 
+		mmc0: mmc@01c0f000 {
+			compatible = "allwinner,sun5i-a13-mmc";
+			reg = <0x01c0f000 0x1000>;
+			clocks = <&ahb_gates 8>, <&mmc0_clk>;
+			clock-names = "ahb", "mod";
+			interrupts = <32>;
+			bus-width = <4>;
+			status = "disabled";
+		};
+
+		mmc1: mmc@01c10000 {
+			compatible = "allwinner,sun5i-a13-mmc";
+			reg = <0x01c10000 0x1000>;
+			clocks = <&ahb_gates 9>, <&mmc1_clk>;
+			clock-names = "ahb", "mod";
+			interrupts = <33>;
+			bus-width = <4>;
+			status = "disabled";
+		};
+
+		mmc2: mmc@01c11000 {
+			compatible = "allwinner,sun5i-a13-mmc";
+			reg = <0x01c11000 0x1000>;
+			clocks = <&ahb_gates 10>, <&mmc2_clk>;
+			clock-names = "ahb", "mod";
+			interrupts = <34>;
+			bus-width = <4>;
+			status = "disabled";
+		};
+
+		mmc3: mmc@01c12000 {
+			compatible = "allwinner,sun5i-a13-mmc";
+			reg = <0x01c12000 0x1000>;
+			clocks = <&ahb_gates 11>, <&mmc3_clk>;
+			clock-names = "ahb", "mod";
+			interrupts = <35>;
+			bus-width = <4>;
+			status = "disabled";
+		};
+
 		intc: interrupt-controller@01c20400 {
 			compatible = "allwinner,sun4i-ic";
 			reg = <0x01c20400 0x400>;
@@ -400,6 +440,20 @@
 				allwinner,drive = <0>;
 				allwinner,pull = <0>;
 			};
+
+			mmc0_pins_a: mmc0@0 {
+				allwinner,pins = "PF0","PF1","PF2","PF3","PF4","PF5";
+				allwinner,function = "mmc0";
+				allwinner,drive = <3>;
+				allwinner,pull = <0>;
+			};
+
+			mmc0_cd_pin_reference_design: mmc0_cd_pin@0 {
+				allwinner,pins = "PH1";
+				allwinner,function = "gpio_in";
+				allwinner,drive = <0>;
+				allwinner,pull = <1>;
+			};
 		};
 
 		timer@01c20c00 {

-- 
You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
For more options, visit https://groups.google.com/groups/opt_out.

^ permalink raw reply related

* [PATCH v7 7/8] ARM: dts: sun5i: Add support for mmc
From: David Lanzendörfer @ 2014-02-17 10:02 UTC (permalink / raw)
  To: devicetree-u79uwXL29TY76Z2rM5mHXA, Ulf Hansson, Laurent Pinchart,
	Mike Turquette, Simon Baatz, Hans de Goede, Emilio López,
	linux-mmc-u79uwXL29TY76Z2rM5mHXA, Chris Ball,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, H Hartley Sweeten,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Tejun Heo, Maxime Ripard,
	Guennadi Liakhovetski,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <20140217095907.15040.81893.stgit-pgFh0Jf6HD9Xzn/AsuzBOg@public.gmane.org>

Signed-off-by: David Lanzendörfer <david.lanzendoerfer-Z7Kmv9EsliU@public.gmane.org>
Signed-off-by: Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
---
 arch/arm/boot/dts/sun5i-a10s-olinuxino-micro.dts |   30 +++++++++++++++
 arch/arm/boot/dts/sun5i-a10s.dtsi                |   44 ++++++++++++++++++++++
 arch/arm/boot/dts/sun5i-a13-olinuxino-micro.dts  |   15 ++++++++
 arch/arm/boot/dts/sun5i-a13-olinuxino.dts        |   15 ++++++++
 arch/arm/boot/dts/sun5i-a13.dtsi                 |   37 +++++++++++++++++++
 5 files changed, 141 insertions(+)

diff --git a/arch/arm/boot/dts/sun5i-a10s-olinuxino-micro.dts b/arch/arm/boot/dts/sun5i-a10s-olinuxino-micro.dts
index 3c9f8b3..5c7b454 100644
--- a/arch/arm/boot/dts/sun5i-a10s-olinuxino-micro.dts
+++ b/arch/arm/boot/dts/sun5i-a10s-olinuxino-micro.dts
@@ -34,7 +34,37 @@
 			};
 		};
 
+		mmc0: mmc@01c0f000 {
+			pinctrl-names = "default", "default";
+			pinctrl-0 = <&mmc0_pins_a>;
+			pinctrl-1 = <&mmc0_cd_pin_olinuxino_micro>;
+			cd-gpios = <&pio 6 1 0>; /* PG1 */
+			status = "okay";
+		};
+
+		mmc1: mmc@01c10000 {
+			pinctrl-names = "default", "default";
+			pinctrl-0 = <&mmc1_pins_a>;
+			pinctrl-1 = <&mmc1_cd_pin_olinuxino_micro>;
+			cd-gpios = <&pio 6 13 0>; /* PG13 */
+			status = "okay";
+		};
+
 		pinctrl@01c20800 {
+			mmc0_cd_pin_olinuxino_micro: mmc0_cd_pin@0 {
+				allwinner,pins = "PG1";
+				allwinner,function = "gpio_in";
+				allwinner,drive = <0>;
+				allwinner,pull = <1>;
+			};
+
+			mmc1_cd_pin_olinuxino_micro: mmc1_cd_pin@0 {
+				allwinner,pins = "PG13";
+				allwinner,function = "gpio_in";
+				allwinner,drive = <0>;
+				allwinner,pull = <1>;
+			};
+
 			led_pins_olinuxino: led_pins@0 {
 				allwinner,pins = "PE3";
 				allwinner,function = "gpio_out";
diff --git a/arch/arm/boot/dts/sun5i-a10s.dtsi b/arch/arm/boot/dts/sun5i-a10s.dtsi
index 327e87b..b6d1de0 100644
--- a/arch/arm/boot/dts/sun5i-a10s.dtsi
+++ b/arch/arm/boot/dts/sun5i-a10s.dtsi
@@ -293,6 +293,36 @@
 			#size-cells = <0>;
 		};
 
+		mmc0: mmc@01c0f000 {
+			compatible = "allwinner,sun5i-a13-mmc";
+			reg = <0x01c0f000 0x1000>;
+			clocks = <&ahb_gates 8>, <&mmc0_clk>;
+			clock-names = "ahb", "mod";
+			interrupts = <32>;
+			bus-width = <4>;
+			status = "disabled";
+		};
+
+		mmc1: mmc@01c10000 {
+			compatible = "allwinner,sun5i-a13-mmc";
+			reg = <0x01c10000 0x1000>;
+			clocks = <&ahb_gates 9>, <&mmc1_clk>;
+			clock-names = "ahb", "mod";
+			interrupts = <33>;
+			bus-width = <4>;
+			status = "disabled";
+		};
+
+		mmc2: mmc@01c11000 {
+			compatible = "allwinner,sun5i-a13-mmc";
+			reg = <0x01c11000 0x1000>;
+			clocks = <&ahb_gates 10>, <&mmc2_clk>;
+			clock-names = "ahb", "mod";
+			interrupts = <34>;
+			bus-width = <4>;
+			status = "disabled";
+		};
+
 		intc: interrupt-controller@01c20400 {
 			compatible = "allwinner,sun4i-ic";
 			reg = <0x01c20400 0x400>;
@@ -363,6 +393,20 @@
 				allwinner,drive = <0>;
 				allwinner,pull = <0>;
 			};
+
+			mmc0_pins_a: mmc0@0 {
+				allwinner,pins = "PF0","PF1","PF2","PF3","PF4","PF5";
+				allwinner,function = "mmc0";
+				allwinner,drive = <3>;
+				allwinner,pull = <0>;
+			};
+
+			mmc1_pins_a: mmc1@0 {
+				allwinner,pins = "PG3","PG4","PG5","PG6","PG7","PG8";
+				allwinner,function = "mmc1";
+				allwinner,drive = <3>;
+				allwinner,pull = <0>;
+			};
 		};
 
 		timer@01c20c00 {
diff --git a/arch/arm/boot/dts/sun5i-a13-olinuxino-micro.dts b/arch/arm/boot/dts/sun5i-a13-olinuxino-micro.dts
index fe2ce0a..2f08bb2 100644
--- a/arch/arm/boot/dts/sun5i-a13-olinuxino-micro.dts
+++ b/arch/arm/boot/dts/sun5i-a13-olinuxino-micro.dts
@@ -20,7 +20,22 @@
 	compatible = "olimex,a13-olinuxino-micro", "allwinner,sun5i-a13";
 
 	soc@01c00000 {
+		mmc0: mmc@01c0f000 {
+			pinctrl-names = "default", "default";
+			pinctrl-0 = <&mmc0_pins_a>;
+			pinctrl-1 = <&mmc0_cd_pin_olinuxinom>;
+			cd-gpios = <&pio 6 0 0>; /* PG0 */
+			status = "okay";
+		};
+
 		pinctrl@01c20800 {
+			mmc0_cd_pin_olinuxinom: mmc0_cd_pin@0 {
+				allwinner,pins = "PG0";
+				allwinner,function = "gpio_in";
+				allwinner,drive = <0>;
+				allwinner,pull = <1>;
+			};
+
 			led_pins_olinuxinom: led_pins@0 {
 				allwinner,pins = "PG9";
 				allwinner,function = "gpio_out";
diff --git a/arch/arm/boot/dts/sun5i-a13-olinuxino.dts b/arch/arm/boot/dts/sun5i-a13-olinuxino.dts
index a4ba5ff..a7280f5 100644
--- a/arch/arm/boot/dts/sun5i-a13-olinuxino.dts
+++ b/arch/arm/boot/dts/sun5i-a13-olinuxino.dts
@@ -19,7 +19,22 @@
 	compatible = "olimex,a13-olinuxino", "allwinner,sun5i-a13";
 
 	soc@01c00000 {
+		mmc0: mmc@01c0f000 {
+			pinctrl-names = "default", "default";
+			pinctrl-0 = <&mmc0_pins_a>;
+			pinctrl-1 = <&mmc0_cd_pin_olinuxino>;
+			cd-gpios = <&pio 6 0 0>; /* PG0 */
+			status = "okay";
+		};
+
 		pinctrl@01c20800 {
+			mmc0_cd_pin_olinuxino: mmc0_cd_pin@0 {
+				allwinner,pins = "PG0";
+				allwinner,function = "gpio_in";
+				allwinner,drive = <0>;
+				allwinner,pull = <1>;
+			};
+
 			led_pins_olinuxino: led_pins@0 {
 				allwinner,pins = "PG9";
 				allwinner,function = "gpio_out";
diff --git a/arch/arm/boot/dts/sun5i-a13.dtsi b/arch/arm/boot/dts/sun5i-a13.dtsi
index f49eb13..040d304 100644
--- a/arch/arm/boot/dts/sun5i-a13.dtsi
+++ b/arch/arm/boot/dts/sun5i-a13.dtsi
@@ -274,6 +274,36 @@
 		#size-cells = <1>;
 		ranges;
 
+		mmc0: mmc@01c0f000 {
+			compatible = "allwinner,sun5i-a13-mmc";
+			reg = <0x01c0f000 0x1000>;
+			clocks = <&ahb_gates 8>, <&mmc0_clk>;
+			clock-names = "ahb", "mod";
+			interrupts = <32>;
+			bus-width = <4>;
+			status = "disabled";
+		};
+
+		mmc1: mmc@01c10000 {
+			compatible = "allwinner,sun5i-a13-mmc";
+			reg = <0x01c10000 0x1000>;
+			clocks = <&ahb_gates 9>, <&mmc1_clk>;
+			clock-names = "ahb", "mod";
+			interrupts = <33>;
+			bus-width = <4>;
+			status = "disabled";
+		};
+
+		mmc2: mmc@01c11000 {
+			compatible = "allwinner,sun5i-a13-mmc";
+			reg = <0x01c11000 0x1000>;
+			clocks = <&ahb_gates 10>, <&mmc2_clk>;
+			clock-names = "ahb", "mod";
+			interrupts = <34>;
+			bus-width = <4>;
+			status = "disabled";
+		};
+
 		intc: interrupt-controller@01c20400 {
 			compatible = "allwinner,sun4i-ic";
 			reg = <0x01c20400 0x400>;
@@ -326,6 +356,13 @@
 				allwinner,drive = <0>;
 				allwinner,pull = <0>;
 			};
+
+			mmc0_pins_a: mmc0@0 {
+				allwinner,pins = "PF0","PF1","PF2","PF3","PF4","PF5";
+				allwinner,function = "mmc0";
+				allwinner,drive = <3>;
+				allwinner,pull = <0>;
+			};
 		};
 
 		timer@01c20c00 {

-- 
You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
For more options, visit https://groups.google.com/groups/opt_out.

^ permalink raw reply related

* [PATCH v7 8/8] ARM: sunxi: Add documentation for driver for SD/MMC hosts found on Allwinner sunxi SoCs
From: David Lanzendörfer @ 2014-02-17 10:03 UTC (permalink / raw)
  To: devicetree-u79uwXL29TY76Z2rM5mHXA, Ulf Hansson, Laurent Pinchart,
	Mike Turquette, Simon Baatz, Hans de Goede, Emilio López,
	linux-mmc-u79uwXL29TY76Z2rM5mHXA, Chris Ball,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, H Hartley Sweeten,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Tejun Heo, Maxime Ripard,
	Guennadi Liakhovetski,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <20140217095907.15040.81893.stgit-pgFh0Jf6HD9Xzn/AsuzBOg@public.gmane.org>

Signed-off-by: David Lanzendörfer <david.lanzendoerfer-Z7Kmv9EsliU@public.gmane.org>
---
 .../devicetree/bindings/mmc/sunxi-mmc.txt          |   32 ++++++++++++++++++++
 1 file changed, 32 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mmc/sunxi-mmc.txt

diff --git a/Documentation/devicetree/bindings/mmc/sunxi-mmc.txt b/Documentation/devicetree/bindings/mmc/sunxi-mmc.txt
new file mode 100644
index 0000000..5ce8c5e
--- /dev/null
+++ b/Documentation/devicetree/bindings/mmc/sunxi-mmc.txt
@@ -0,0 +1,32 @@
+* Allwinner sunxi MMC controller
+
+The highspeed MMC host controller on Allwinner SoCs provides an interface
+for MMC, SD and SDIO types of memory cards.
+
+Supported maximum speeds are the ones of the eMMC standard 4.5 as well
+as the speed of SD standard 3.0.
+Absolute maximum transfer rate is 200MB/s
+
+Required properties:
+- compatible: Should be "allwinner,<revision>-<chip>-mmc".
+  The supported chips include a10, a10s, 13, a20 and a31.
+- base registers are 0x1000 appart, so the base of mmc1
+  would be 0x01c0f000+0x1000=0x01c10000(see example)
+  and so on.
+- clocks requires the reference at the ahb clock gate
+  with the correct index (mmc0 -> 8, mmc1 -> 9, and so on)
+  as well as the reference to the correct mod0 clock.
+- interrupts requires the correct IRQ line
+  (mmc0 -> 32, mmc1 -> 33, and so on)
+
+Examples:
+
+mmc0: mmc@01c0f000 {
+	compatible = "allwinner,sun5i-a13-mmc";
+	reg = <0x01c0f000 0x1000>;
+	clocks = <&ahb_gates 8>, <&mmc0_clk>;
+	clock-names = "ahb", "mod";
+	interrupts = <0 32 4>;
+	bus-width = <4>;
+	status = "disabled";
+};

-- 
You received this message because you are subscribed to the Google Groups "linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
For more options, visit https://groups.google.com/groups/opt_out.

^ permalink raw reply related

* Re: [PATCH 2/5] ARM: shmobile: r8a7791: add i2c master nodes to dtsi
From: Magnus Damm @ 2014-02-17 10:03 UTC (permalink / raw)
  To: Wolfram Sang
  Cc: Geert Uytterhoeven, SH-Linux,
	linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org,
	Simon Horman
In-Reply-To: <20140217093126.GE2633@katana>

On Mon, Feb 17, 2014 at 6:31 PM, Wolfram Sang <wsa@the-dreams.de> wrote:
> On Mon, Feb 17, 2014 at 10:23:31AM +0100, Geert Uytterhoeven wrote:
>> Hi Wolfram,
>>
>> On Mon, Feb 17, 2014 at 10:19 AM, Wolfram Sang <wsa@the-dreams.de> wrote:
>> >> I think the tricky part is when a driver for "renesas,i2c-r8a7790" is updated
>> >> with a new feature for r8a7790, which doesn't necessarily exist in r8a7791.
>> >> Then the compatible entry above will cause breakage.
>> >
>> > If the driver is updated in that way, then it MUST introduce an r8a7791
>> > compatible entry and make sure the new feature is only used on r8a7790.
>> > Otherwise it is a bug. If this is adhered, we won't have a breakage
>> > because the specific entry (here r8a7791) always comes first. Or?
>>
>> That will indeed prevent breakage. But in the distributed development world,
>> the person who modifies the driver may not be aware of the r8a7791.
>
> Then it will break even with a seperate r8a7791 compatible entry as
> well, sadly. Currently, the only thing encoded in the .data field of all
> compatibles, is which generation of the core is used. This is the same
> for r8a7790 and r8a7791 (this is why they ARE compatible ;)). So, even
> in the unlikely case of an r8a7790-only-feature, some code/reactoring to
> make the distinction is absolutely necessary.

It won't break because anyone who modifies r8a7790 support need to
make it specific to that SoC to not pull in r8a7791 into some
assumption. Just because the driver assumes something now doesn't mean
the hardware actually is compatible.

I think I understand your proposal with making an assumption of the
current state of the driver and making sure to add the new SoC
compatible value before adjusting the fallback. But this mainly seems
like a optimization to improve throughput commit-wise. I actually
proposed the same way for SDHI, but now when I think about it I
realize that there is no shortcut for adding the compatible string to
the driver. It has to be done sooner or later anyway.

Or what am I missing? =)

/ magnus

^ permalink raw reply

* [PATCH v5 1/4] ASoC: tlv320aic32x4: Support for master clock
From: Markus Pargmann @ 2014-02-17 10:04 UTC (permalink / raw)
  To: Mark Brown
  Cc: Mark Rutland, devicetree, alsa-devel, Lars-Peter Clausen,
	Pawel Moll, Ian Campbell, Liam Girdwood, Rob Herring, kernel,
	Kumar Gala, Markus Pargmann
In-Reply-To: <1392631460-32002-1-git-send-email-mpa@pengutronix.de>

Add support for a master clock passed through DT. The master clock of
the codec is only active when the codec is in use.

Cc: Rob Herring <robh+dt@kernel.org>
Cc: Pawel Moll <pawel.moll@arm.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Ian Campbell <ijc+devicetree@hellion.org.uk>
Cc: Kumar Gala <galak@codeaurora.org>
Cc: devicetree@vger.kernel.org
Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
---
 .../devicetree/bindings/sound/tlv320aic32x4.txt    |  4 ++++
 sound/soc/codecs/tlv320aic32x4.c                   | 23 ++++++++++++++++++++++
 2 files changed, 27 insertions(+)

diff --git a/Documentation/devicetree/bindings/sound/tlv320aic32x4.txt b/Documentation/devicetree/bindings/sound/tlv320aic32x4.txt
index db05510..352be7b 100644
--- a/Documentation/devicetree/bindings/sound/tlv320aic32x4.txt
+++ b/Documentation/devicetree/bindings/sound/tlv320aic32x4.txt
@@ -8,6 +8,8 @@ Required properties:
 
 Optional properties:
  - reset-gpios: Reset-GPIO phandle with args as described in gpio/gpio.txt
+ - clocks/clock-names: Clock named 'mclk' for the master clock of the codec.
+   See clock/clock-bindings.txt for information about the detailed format.
 
 
 Example:
@@ -15,4 +17,6 @@ Example:
 codec: tlv320aic32x4@18 {
 	compatible = "ti,tlv320aic32x4";
 	reg = <0x18>;
+	clocks = <&clks 201>;
+	clock-names = "mclk";
 };
diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c
index 1dd50e4..d96cb7c 100644
--- a/sound/soc/codecs/tlv320aic32x4.c
+++ b/sound/soc/codecs/tlv320aic32x4.c
@@ -33,6 +33,7 @@
 #include <linux/i2c.h>
 #include <linux/cdev.h>
 #include <linux/slab.h>
+#include <linux/clk.h>
 
 #include <sound/tlv320aic32x4.h>
 #include <sound/core.h>
@@ -67,6 +68,7 @@ struct aic32x4_priv {
 	u32 micpga_routing;
 	bool swapdacs;
 	int rstn_gpio;
+	struct clk *mclk;
 };
 
 /* 0dB min, 0.5dB steps */
@@ -487,8 +489,21 @@ static int aic32x4_mute(struct snd_soc_dai *dai, int mute)
 static int aic32x4_set_bias_level(struct snd_soc_codec *codec,
 				  enum snd_soc_bias_level level)
 {
+	struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec);
+
 	switch (level) {
 	case SND_SOC_BIAS_ON:
+		/* Switch on master clock */
+		if (!IS_ERR(aic32x4->mclk)) {
+			int ret;
+
+			ret = clk_prepare_enable(aic32x4->mclk);
+			if (ret) {
+				dev_err(codec->dev, "Failed to enable master clock\n");
+				return ret;
+			}
+		}
+
 		/* Switch on PLL */
 		snd_soc_update_bits(codec, AIC32X4_PLLPR,
 				    AIC32X4_PLLEN, AIC32X4_PLLEN);
@@ -539,6 +554,10 @@ static int aic32x4_set_bias_level(struct snd_soc_codec *codec,
 		/* Switch off BCLK_N Divider */
 		snd_soc_update_bits(codec, AIC32X4_BCLKN,
 				    AIC32X4_BCLKEN, 0);
+
+		/* Switch off master clock */
+		if (!IS_ERR(aic32x4->mclk))
+			clk_disable_unprepare(aic32x4->mclk);
 		break;
 	case SND_SOC_BIAS_OFF:
 		break;
@@ -717,6 +736,10 @@ static int aic32x4_i2c_probe(struct i2c_client *i2c,
 		aic32x4->rstn_gpio = -1;
 	}
 
+	aic32x4->mclk = devm_clk_get(&i2c->dev, "mclk");
+	if (IS_ERR(aic32x4->mclk))
+		dev_info(&i2c->dev, "No mclk found, continuing without clock\n");
+
 	if (gpio_is_valid(aic32x4->rstn_gpio)) {
 		ret = devm_gpio_request_one(&i2c->dev, aic32x4->rstn_gpio,
 				GPIOF_OUT_INIT_LOW, "tlv320aic32x4 rstn");
-- 
1.8.5.3

^ permalink raw reply related

* [PATCH v3 0/7] i.MX6 PU power domain support
From: Philipp Zabel @ 2014-02-17 10:04 UTC (permalink / raw)
  To: Shawn Guo
  Cc: Rob Herring, Mark Rutland, kernel-bIcnvbaLZ9MEGnE8C9+IrQ,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Philipp Zabel

The i.MX6Q can gate off the CPU and PU (GPU/VPU) power domains using the
Power Gating Controller (PGC) in the GPC register space. The CPU power
domain is already handled by wait state code, but the PU power domain can
be controlled using the generic power domain framework and power off the PU
supply regulator if all devices in the power domain are (runtime) suspended.

This patchset adds a GPC platform device initialized at subsys_initcall time
(after anatop regulators) that binds to the gpc device tree node and sets up
the PU power domain:

	gpc: gpc@020dc000 {
		#address-cells = <1>;
		#size-cells = <1>;
		compatible = "fsl,imx6q-gpc";
		reg = <0x020dc000 0x4000>;
		interrupts = <0 89 0x04 0 90 0x04>;
		pu-supply = <&reg_pu>;

		pd_pu: pu-power-domain@020dc260 {
			compatible = "fsl,imx6q-power-domain";
			reg = <0x020dc260 0x10>;
		};

		pd_arm: cpu-power-domain@020dc2a0 {
			compatible = "fsl,imx6q-power-domain";
			reg = <0x020dc2a0 0x10>;
		};
	};

The cpu-power-domain node is included for completeness' sake, it is not
currently used by the code.
It registers a platform bus notifier so that it can add GPU and VPU devices
to the power domain when they are bound. If finds devices to be added to the
power domain by scanning the device tree for nodes that contain a
	power-domain = <&pd_pu>;
property.

For i.MX6QDL there is only one power domain that can be disabled at runtime,
on i.MX6SL there is an additional DISPLAY power domain, which is not yet
handled by the code.

Changes since v2:
 - Only disable PU power domain on boot if CONFIG_PM_RUNTIME is enabled
 - Removed superfluous second assignment of imx6q_pu_domain.of_node

regards
Philipp

Philipp Zabel (7):
  Documentation: Add device tree bindings for Freescale i.MX GPC
  ARM: imx6: gpc: Add PU power domain for GPU/VPU
  ARM: imx6: gpc: Add pm clock support to PU power domain
  ARM: imx6: gpc: Add observed worst case latencies
  ARM: dts: imx6qdl: Allow disabling the PU regulator, add a enable ramp
    delay
  ARM: dts: imx6qdl: Add power-domain information to gpc node
  ARM: dts: imx6sl: Add power-domain information to gpc node

 .../devicetree/bindings/power/fsl,imx-gpc.txt      |  61 +++++
 arch/arm/boot/dts/imx6qdl.dtsi                     |  16 +-
 arch/arm/boot/dts/imx6sl.dtsi                      |  18 ++
 arch/arm/mach-imx/Kconfig                          |   2 +
 arch/arm/mach-imx/gpc.c                            | 256 +++++++++++++++++++++
 5 files changed, 352 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/devicetree/bindings/power/fsl,imx-gpc.txt

-- 
1.8.5.3

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* [PATCH v3 1/7] Documentation: Add device tree bindings for Freescale i.MX GPC
From: Philipp Zabel @ 2014-02-17 10:04 UTC (permalink / raw)
  To: Shawn Guo
  Cc: Rob Herring, Mark Rutland, kernel-bIcnvbaLZ9MEGnE8C9+IrQ,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Philipp Zabel
In-Reply-To: <1392631503-17283-1-git-send-email-p.zabel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>

The i.MX6 contains a power controller that controls power gating and
sequencing for the SoC's power domains.

Signed-off-by: Philipp Zabel <p.zabel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
---
 .../devicetree/bindings/power/fsl,imx-gpc.txt      | 61 ++++++++++++++++++++++
 1 file changed, 61 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/power/fsl,imx-gpc.txt

diff --git a/Documentation/devicetree/bindings/power/fsl,imx-gpc.txt b/Documentation/devicetree/bindings/power/fsl,imx-gpc.txt
new file mode 100644
index 0000000..3ec8c0e
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/fsl,imx-gpc.txt
@@ -0,0 +1,61 @@
+Freescale i.MX General Power Controller
+=======================================
+
+The i.MX6Q General Power Control (GPC) block contains DVFS load tracking
+counters and Power Gating Control (PGC) for the CPU and PU (GPU/VPU) power
+domains.
+
+Required properties:
+- compatible: Should be "fsl,imx6q-gpc"
+- reg: should be register base and length as documented in the
+  datasheet
+- interrupts: Should contain GPC interrupt request 1
+- pu-supply: Link to the LDO regulator powering the PU power domain
+- #address-cells, #size-cells: Should be <1>
+
+The gpc node should contain 'power-domain' subnodes for each power domain.
+These serve as phandle targets for devices belonging to the power domain:
+
+Power domains controlled by a PGC register set
+==============================================
+
+Required properties:
+- compatible: Should be "fsl,imx6q-power-domain"
+- reg: should be register base and length as documented in the
+  datasheet
+
+Specifying power domain for IP modules
+======================================
+
+IP cores belonging to a power domain should contain a 'power-domain' property
+that is a phandle pointing to the power-domain subnode of the gpc device node.
+
+Required properties:
+- power-domain: A phandle pointing to the power-domain device tree node
+
+
+Example:
+
+	gpc: gpc@020dc000 {
+		#address-cells = <1>;
+		#size-cells = <1>;
+		compatible = "fsl,imx6q-gpc";
+		reg = <0x020dc000 0x4000>;
+		interrupts = <0 89 0x04 0 90 0x04>;
+		pu-supply = <&reg_pu>;
+
+		pd_pu: power-domain@020dc260 {
+			compatible = "fsl,imx6q-power-domain";
+			reg = <0x020dc260 0x10>;
+		};
+	};
+
+Example of a device that is part of a power domain:
+
+	vpu: vpu@02040000 {
+		reg = <0x02040000 0x3c000>;
+		/* ... */
+		power-domain = <&pd_pu>;
+		/* ... */
+	};
+
-- 
1.8.5.3

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related

* [PATCH v3 2/7] ARM: imx6: gpc: Add PU power domain for GPU/VPU
From: Philipp Zabel @ 2014-02-17 10:04 UTC (permalink / raw)
  To: Shawn Guo
  Cc: Rob Herring, Mark Rutland, kernel-bIcnvbaLZ9MEGnE8C9+IrQ,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Philipp Zabel
In-Reply-To: <1392631503-17283-1-git-send-email-p.zabel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>

When generic pm domain support is enabled, the PGC can be used
to completely gate power to the PU power domain containing GPU3D,
GPU2D, and VPU cores.
This code triggers the PGC powerdown sequence to disable the GPU/VPU
isolation cells and gate power and then disables the PU regulator.
To reenable, the reverse powerup sequence is triggered after the PU
regulaotor is enabled again.

Signed-off-by: Philipp Zabel <p.zabel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
---
Changes since v2:
 - Only disable the power domain on boot if CONFIG_PM_RUNTIME is enabled
 - Removed superfluous second assignment of imx6q_pu_domain.of_node
---
 arch/arm/mach-imx/Kconfig |   2 +
 arch/arm/mach-imx/gpc.c   | 173 ++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 175 insertions(+)

diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig
index 33567aa..3c58f2e 100644
--- a/arch/arm/mach-imx/Kconfig
+++ b/arch/arm/mach-imx/Kconfig
@@ -808,6 +808,7 @@ config SOC_IMX6Q
 	select PL310_ERRATA_727915 if CACHE_PL310
 	select PL310_ERRATA_769419 if CACHE_PL310
 	select PM_OPP if PM
+	select PM_GENERIC_DOMAINS if PM
 
 	help
 	  This enables support for Freescale i.MX6 Quad processor.
@@ -827,6 +828,7 @@ config SOC_IMX6SL
 	select PL310_ERRATA_588369 if CACHE_PL310
 	select PL310_ERRATA_727915 if CACHE_PL310
 	select PL310_ERRATA_769419 if CACHE_PL310
+	select PM_GENERIC_DOMAINS if PM
 
 	help
 	  This enables support for Freescale i.MX6 SoloLite processor.
diff --git a/arch/arm/mach-imx/gpc.c b/arch/arm/mach-imx/gpc.c
index 586e017..c61126c 100644
--- a/arch/arm/mach-imx/gpc.c
+++ b/arch/arm/mach-imx/gpc.c
@@ -10,19 +10,32 @@
  * http://www.gnu.org/copyleft/gpl.html
  */
 
+#include <linux/clk.h>
+#include <linux/delay.h>
 #include <linux/io.h>
 #include <linux/irq.h>
 #include <linux/of.h>
 #include <linux/of_address.h>
 #include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/pm_domain.h>
+#include <linux/regulator/consumer.h>
 #include <linux/irqchip/arm-gic.h>
 #include "common.h"
+#include "hardware.h"
 
+#define GPC_CNTR		0x000
 #define GPC_IMR1		0x008
+#define GPC_PGC_GPU_PDN		0x260
+#define GPC_PGC_GPU_PUPSCR	0x264
+#define GPC_PGC_GPU_PDNSCR	0x268
 #define GPC_PGC_CPU_PDN		0x2a0
 
 #define IMR_NUM			4
 
+#define GPU_VPU_PUP_REQ		BIT(1)
+#define GPU_VPU_PDN_REQ		BIT(0)
+
 static void __iomem *gpc_base;
 static u32 gpc_wake_irqs[IMR_NUM];
 static u32 gpc_saved_imrs[IMR_NUM];
@@ -138,3 +151,163 @@ void __init imx_gpc_init(void)
 	gic_arch_extn.irq_unmask = imx_gpc_irq_unmask;
 	gic_arch_extn.irq_set_wake = imx_gpc_irq_set_wake;
 }
+
+static struct regulator *pu_reg;
+
+static int imx6q_pm_pu_power_off(struct generic_pm_domain *genpd)
+{
+	u32 val;
+	int iso, iso2sw;
+
+	/* Read ISO and ISO2SW power down delays */
+	val = readl_relaxed(gpc_base + GPC_PGC_GPU_PDNSCR);
+	iso = val & 0x3f;
+	iso2sw = (val >> 8) & 0x3f;
+
+	/* Gate off PU domain when GPU/VPU when powered down */
+	writel_relaxed(0x1, gpc_base + GPC_PGC_GPU_PDN);
+
+	/* Request GPC to power down GPU/VPU */
+	val = readl_relaxed(gpc_base + GPC_CNTR);
+	val |= GPU_VPU_PDN_REQ;
+	writel_relaxed(val, gpc_base + GPC_CNTR);
+
+	/* Wait ISO + ISO2SW IPG clock cycles */
+	ndelay((iso + iso2sw) * 1000 / 66);
+
+	regulator_disable(pu_reg);
+
+	return 0;
+}
+
+static int imx6q_pm_pu_power_on(struct generic_pm_domain *genpd)
+{
+	int ret;
+	u32 val;
+	int sw, sw2iso;
+
+	ret = regulator_enable(pu_reg);
+	if (ret) {
+		pr_err("%s: failed to enable regulator: %d\n", __func__, ret);
+		return ret;
+	}
+
+	/* Gate off PU domain when GPU/VPU when powered down */
+	writel_relaxed(0x1, gpc_base + GPC_PGC_GPU_PDN);
+
+	/* Read ISO and ISO2SW power down delays */
+	val = readl_relaxed(gpc_base + GPC_PGC_GPU_PUPSCR);
+	sw = val & 0x3f;
+	sw2iso = (val >> 8) & 0x3f;
+
+	/* Request GPC to power up GPU/VPU */
+	val = readl_relaxed(gpc_base + GPC_CNTR);
+	val |= GPU_VPU_PUP_REQ;
+	writel_relaxed(val, gpc_base + GPC_CNTR);
+
+	/* Wait ISO + ISO2SW IPG clock cycles */
+	ndelay((sw + sw2iso) * 1000 / 66);
+
+	return 0;
+}
+
+static struct generic_pm_domain imx6q_pu_domain = {
+	.name = "PU",
+	.power_off = imx6q_pm_pu_power_off,
+	.power_on = imx6q_pm_pu_power_on,
+};
+
+static int imx6q_pm_notifier_call(struct notifier_block *nb,
+				  unsigned long event, void *data)
+{
+	struct generic_pm_domain *genpd;
+	struct device *dev = data;
+	struct device_node *np;
+	int ret;
+
+	switch (event) {
+	case BUS_NOTIFY_BIND_DRIVER:
+		np = of_parse_phandle(dev->of_node, "power-domain", 0);
+		if (!np || np != imx6q_pu_domain.of_node)
+			return NOTIFY_DONE;
+
+		ret = pm_genpd_of_add_device(np, dev);
+		if (ret)
+			dev_err(dev, "failed to add to power domain: %d\n",
+				ret);
+		break;
+	case BUS_NOTIFY_UNBOUND_DRIVER:
+		genpd = dev_to_genpd(dev);
+		if (IS_ERR(genpd) || genpd != &imx6q_pu_domain)
+			return NOTIFY_DONE;
+
+		ret = pm_genpd_remove_device(genpd, dev);
+		if (ret)
+			dev_err(dev, "failed to remove from power domain: %d\n",
+				ret);
+		break;
+	}
+
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block imx6q_platform_nb = {
+	.notifier_call = imx6q_pm_notifier_call,
+};
+
+static int imx_gpc_probe(struct platform_device *pdev)
+{
+	struct device_node *np;
+	bool is_off;
+	int ret;
+
+	np = of_get_child_by_name(pdev->dev.of_node, "pu-power-domain");
+	if (!np) {
+		dev_err(&pdev->dev, "missing pu-power-domain node\n");
+		return -EINVAL;
+	}
+	imx6q_pu_domain.of_node = np;
+
+	pu_reg = devm_regulator_get(&pdev->dev, "pu");
+	if (IS_ERR(pu_reg)) {
+		ret = PTR_ERR(pu_reg);
+		dev_err(&pdev->dev, "failed to get pu regulator: %d\n", ret);
+		return ret;
+	}
+
+	/* The regulator is initially enabled */
+	ret = regulator_enable(pu_reg);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to enable pu regulator: %d\n", ret);
+		return ret;
+	}
+
+	is_off = IS_ENABLED(CONFIG_PM_RUNTIME);
+	if (is_off)
+		imx6q_pm_pu_power_off(&imx6q_pu_domain);
+
+	pm_genpd_init(&imx6q_pu_domain, NULL, is_off);
+	bus_register_notifier(&platform_bus_type, &imx6q_platform_nb);
+
+	return 0;
+}
+
+static struct of_device_id imx_gpc_dt_ids[] = {
+	{ .compatible = "fsl,imx6q-gpc" },
+	{ }
+};
+
+static struct platform_driver imx_gpc_driver = {
+	.driver = {
+		.name = "imx-gpc",
+		.owner = THIS_MODULE,
+		.of_match_table = imx_gpc_dt_ids,
+	},
+	.probe = imx_gpc_probe,
+};
+
+static int __init imx_pgc_init(void)
+{
+	return platform_driver_register(&imx_gpc_driver);
+}
+subsys_initcall(imx_pgc_init);
-- 
1.8.5.3

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related

* [PATCH v3 3/7] ARM: imx6: gpc: Add pm clock support to PU power domain
From: Philipp Zabel @ 2014-02-17 10:04 UTC (permalink / raw)
  To: Shawn Guo
  Cc: Rob Herring, Mark Rutland, kernel-bIcnvbaLZ9MEGnE8C9+IrQ,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Philipp Zabel
In-Reply-To: <1392631503-17283-1-git-send-email-p.zabel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>

Drivers still handle clocks themselves, we only enable pm clocks of the
GPU and VPU devices in the PU power domain temporarily during powerup
so that the reset machinery can work.

Signed-off-by: Philipp Zabel <p.zabel-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
---
 arch/arm/mach-imx/gpc.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 74 insertions(+)

diff --git a/arch/arm/mach-imx/gpc.c b/arch/arm/mach-imx/gpc.c
index c61126c..e16e36a 100644
--- a/arch/arm/mach-imx/gpc.c
+++ b/arch/arm/mach-imx/gpc.c
@@ -18,6 +18,7 @@
 #include <linux/of_address.h>
 #include <linux/of_irq.h>
 #include <linux/platform_device.h>
+#include <linux/pm_clock.h>
 #include <linux/pm_domain.h>
 #include <linux/regulator/consumer.h>
 #include <linux/irqchip/arm-gic.h>
@@ -182,6 +183,7 @@ static int imx6q_pm_pu_power_off(struct generic_pm_domain *genpd)
 
 static int imx6q_pm_pu_power_on(struct generic_pm_domain *genpd)
 {
+	struct pm_domain_data *pdd;
 	int ret;
 	u32 val;
 	int sw, sw2iso;
@@ -192,6 +194,10 @@ static int imx6q_pm_pu_power_on(struct generic_pm_domain *genpd)
 		return ret;
 	}
 
+	/* Enable PM clocks for all devices in the PU domain */
+	list_for_each_entry(pdd, &genpd->dev_list, list_node)
+		pm_clk_resume(pdd->dev);
+
 	/* Gate off PU domain when GPU/VPU when powered down */
 	writel_relaxed(0x1, gpc_base + GPC_PGC_GPU_PDN);
 
@@ -208,6 +214,10 @@ static int imx6q_pm_pu_power_on(struct generic_pm_domain *genpd)
 	/* Wait ISO + ISO2SW IPG clock cycles */
 	ndelay((sw + sw2iso) * 1000 / 66);
 
+	/* Disable PM clocks for all devices in the PU domain */
+	list_for_each_entry(pdd, &genpd->dev_list, list_node)
+		pm_clk_suspend(pdd->dev);
+
 	return 0;
 }
 
@@ -217,6 +227,68 @@ static struct generic_pm_domain imx6q_pu_domain = {
 	.power_on = imx6q_pm_pu_power_on,
 };
 
+int imx6q_pm_clk_add(struct device *dev)
+{
+	struct device_node *np = dev->of_node;
+	const char *con_id;
+	struct clk *clk;
+	int i = 0;
+
+	/* Add and prepare named clocks */
+	while (!of_property_read_string_index(np, "clock-names", i, &con_id)) {
+		pm_clk_add(dev, con_id);
+		clk = of_clk_get(np, i);
+		if (!IS_ERR(clk)) {
+			clk_prepare(clk);
+			clk_put(clk);
+		}
+		i++;
+	}
+
+	/* If no named clocks are given, add and prepare unnamed clock */
+	if (i == 1 && of_find_property(dev->of_node, "clocks", NULL)) {
+		pm_clk_add(dev, NULL);
+		clk = of_clk_get(np, 0);
+		if (!IS_ERR(clk)) {
+			clk_prepare(clk);
+			clk_put(clk);
+		}
+	}
+
+	return 0;
+}
+
+int imx6q_pm_clk_remove(struct device *dev)
+{
+	struct device_node *np = dev->of_node;
+	const char *con_id;
+	struct clk *clk;
+	int i = 0;
+
+	/* Remove and unprepare named clocks */
+	while (!of_property_read_string_index(np, "clock-names", i, &con_id)) {
+		pm_clk_remove(dev, con_id);
+		clk = of_clk_get(np, i);
+		if (!IS_ERR(clk)) {
+			clk_unprepare(clk);
+			clk_put(clk);
+		}
+		i++;
+	}
+
+	/* If no named clocks are given, remove and unprepare unnamed clock */
+	if (i == 1 && of_find_property(dev->of_node, "clocks", NULL)) {
+		pm_clk_remove(dev, NULL);
+		clk = of_clk_get(np, 0);
+		if (!IS_ERR(clk)) {
+			clk_unprepare(clk);
+			clk_put(clk);
+		}
+	}
+
+	return 0;
+}
+
 static int imx6q_pm_notifier_call(struct notifier_block *nb,
 				  unsigned long event, void *data)
 {
@@ -235,6 +307,7 @@ static int imx6q_pm_notifier_call(struct notifier_block *nb,
 		if (ret)
 			dev_err(dev, "failed to add to power domain: %d\n",
 				ret);
+		imx6q_pm_clk_add(dev);
 		break;
 	case BUS_NOTIFY_UNBOUND_DRIVER:
 		genpd = dev_to_genpd(dev);
@@ -245,6 +318,7 @@ static int imx6q_pm_notifier_call(struct notifier_block *nb,
 		if (ret)
 			dev_err(dev, "failed to remove from power domain: %d\n",
 				ret);
+		imx6q_pm_clk_remove(dev);
 		break;
 	}
 
-- 
1.8.5.3

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox