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 2/5] ARM: shmobile: r8a7791: add i2c master nodes to dtsi
From: Wolfram Sang @ 2014-02-17  9:31 UTC (permalink / raw)
  To: Geert Uytterhoeven
  Cc: Magnus Damm, SH-Linux, linux-arm-kernel@lists.infradead.org,
	devicetree@vger.kernel.org, Simon Horman
In-Reply-To: <CAMuHMdV5KNBcFh5E0bKaLNboZUbuXmMkzdYozqWLvro7Kwjhjw@mail.gmail.com>

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

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.


[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 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:29 UTC (permalink / raw)
  To: Vinod Koul
  Cc: Srikanth Thokala, dan.j.williams, michal.simek, Grant Likely,
	robh+dt, Levente Kurusa, Lars-Peter Clausen, andriy.shevchenko,
	dmaengine, linux-arm-kernel, linux-kernel@vger.kernel.org,
	devicetree
In-Reply-To: <20140217084316.GY10628@intel.com>

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.

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/

^ permalink raw reply

* Re: [PATCH 1/2] mfd: palmas: support IRQ inversion at the board level
From: Lee Jones @ 2014-02-17  9:26 UTC (permalink / raw)
  To: Stephen Warren
  Cc: Samuel Ortiz, devicetree-u79uwXL29TY76Z2rM5mHXA, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA, J Keerthy, Ian Lartey,
	Stefan Agner, josephl-DDmLM1+adcrQT0dZR+AlfA,
	ldewangan-DDmLM1+adcrQT0dZR+AlfA, Stephen Warren
In-Reply-To: <1392415108-4365-1-git-send-email-swarren-3lzwWm7+Weoh9ZMKESR00Q@public.gmane.org>

> From: Stephen Warren <swarren-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
> 
> Some boards or SoCs have an inverter between the PMIC IRQ output pin and
> the IRQ controller input signal.
> 
> The IRQ specifier in DT is meant to represent the IRQ flags at the input
> to the IRQ controller.
> 
> The Palmas HW's IRQ output has configurable polarity. The driver
> currently selects the output polarity by querying the input polarity at
> the IRQ controller. This works fine if the IRQ signal is routed directly
> from the PMIC to the IRQ controller with no intervening logic. However,
> if the signal is inverted between the two, this automatic polarity
> selection gets the wrong answer.
> 
> Add an additional optional DT and platform data parameter which indicates
> that such an inversion occurs. If this option is enabled, the Palmas
> driver will configure its IRQ output to the opposite polarity of the IRQ
> controller's input.
> 
> An alternative would have been to add a new non-optional DT parameter to
> indicate the exact desired output polarity. However, this would have been
> an incompatible change to the DT binding.
> 
> Signed-off-by: Stephen Warren <swarren-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
> ---
> If this patch could be applied to its own branch (w/ signed tag) in the
> MFD tree, that would great; then I can pull patch 1/2 into the Tegra tree
> so that I can apply patch 2/2 to the Tegra tree. Thanks.
> ---
>  Documentation/devicetree/bindings/mfd/palmas.txt | 6 ++++++
>  drivers/mfd/palmas.c                             | 4 ++++
>  include/linux/mfd/palmas.h                       | 1 +
>  3 files changed, 11 insertions(+)

For the core changes:
  Acked-by: Lee Jones <lee.jones-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>

NB: The DT guys prefer documentation to be submitted in a separate
patch these days.

-- 
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

^ permalink raw reply

* Platform device naming vs. compatible property naming
From: Geert Uytterhoeven @ 2014-02-17  9:24 UTC (permalink / raw)
  To: devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org

Hi all,

Is there a specific reason we haven't started using the same naming scheme
for platform devices as for compatible properties?

Gr{oetje,eeting}s,

                        Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert-Td1EMuHUCqxL1ZNQvxDV9g@public.gmane.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds
--
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] can: xilinx CAN controller support.
From: Kedareswara rao Appana @ 2014-02-17  9:23 UTC (permalink / raw)
  To: wg-5Yr1BZd7O62+XT7JhA+gdA, mkl-bIcnvbaLZ9MEGnE8C9+IrQ,
	michal.simek-gjFFaj9aHVfQT0dZR+AlfA,
	grant.likely-QSEj5FYQhm4dnm+yROfE0A,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, linux-can-u79uwXL29TY76Z2rM5mHXA
  Cc: netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Kedareswara rao Appana

This patch adds xilinx CAN controller support.
This driver supports both ZYNQ CANPS and Soft IP
AXI CAN controller.

Signed-off-by: Kedareswara rao Appana <appanad-gjFFaj9aHVfQT0dZR+AlfA@public.gmane.org>
---
This patch is rebased on the 3.14 rc3 kernel.
Changes for v3:
- Updated the driver with the review comments
- Modified the transmit logic as per Marc suggestion.
- Enabling the clocks when the interface is up to reduce the
  Power consumption.
Changes for v2:
- Updated with the review comments.
- Removed unnecessary debug prints.
- included tx,rx fifo depths in ZYNQ CANPS case also.
---
 .../devicetree/bindings/net/can/xilinx_can.txt     |   45 +
 drivers/net/can/Kconfig                            |    7 +
 drivers/net/can/Makefile                           |    1 +
 drivers/net/can/xilinx_can.c                       | 1139 ++++++++++++++++++++
 4 files changed, 1192 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/net/can/xilinx_can.txt
 create mode 100644 drivers/net/can/xilinx_can.c

diff --git a/Documentation/devicetree/bindings/net/can/xilinx_can.txt b/Documentation/devicetree/bindings/net/can/xilinx_can.txt
new file mode 100644
index 0000000..0e57103
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/can/xilinx_can.txt
@@ -0,0 +1,45 @@
+Xilinx Axi CAN/Zynq CANPS controller Device Tree Bindings
+---------------------------------------------------------
+
+Required properties:
+- compatible		: Should be "xlnx,zynq-can-1.00.a" for Zynq CAN
+			  controllers and "xlnx,axi-can-1.00.a" for Axi CAN
+			  controllers.
+- reg			: Physical base address and size of the Axi CAN/Zynq
+			  CANPS registers map.
+- interrupts		: Property with a value describing the interrupt
+			  number.
+- interrupt-parent	: Must be core interrupt controller
+- clock-names		: List of input clock names - "ref_clk", "aper_clk"
+			  (See clock bindings for details. Two clocks are
+			   required for Zynq CAN. For Axi CAN
+			   case it is one(ref_clk)).
+- clocks		: Clock phandles (see clock bindings for details).
+- tx-fifo-depth		: Can Tx fifo depth.
+- rx-fifo-depth		: Can Rx fifo depth.
+
+
+Example:
+
+For Zynq CANPS Dts file:
+	zynq_can_0: zynq-can@e0008000 {
+			compatible = "xlnx,zynq-can-1.00.a";
+			clocks = <&clkc 19>, <&clkc 36>;
+			clock-names = "ref_clk", "aper_clk";
+			reg = <0xe0008000 0x1000>;
+			interrupts = <0 28 4>;
+			interrupt-parent = <&intc>;
+			tx-fifo-depth = <0x40>;
+			rx-fifo-depth = <0x40>;
+		};
+For Axi CAN Dts file:
+	axi_can_0: axi-can@40000000 {
+			compatible = "xlnx,axi-can-1.00.a";
+			clocks = <&clkc 0>;
+			clock-names = "ref_clk" ;
+			reg = <0x40000000 0x10000>;
+			interrupt-parent = <&intc>;
+			interrupts = <0 59 1>;
+			tx-fifo-depth = <0x40>;
+			rx-fifo-depth = <0x40>;
+		};
diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig
index 9e7d95d..b180239 100644
--- a/drivers/net/can/Kconfig
+++ b/drivers/net/can/Kconfig
@@ -125,6 +125,13 @@ config CAN_GRCAN
 	  endian syntheses of the cores would need some modifications on
 	  the hardware level to work.
 
+config CAN_XILINXCAN
+	tristate "Xilinx CAN"
+	depends on ARCH_ZYNQ || MICROBLAZE
+	---help---
+	  Xilinx CAN driver. This driver supports both soft AXI CAN IP and
+	  Zynq CANPS IP.
+
 source "drivers/net/can/mscan/Kconfig"
 
 source "drivers/net/can/sja1000/Kconfig"
diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile
index c744039..0b8e11e 100644
--- a/drivers/net/can/Makefile
+++ b/drivers/net/can/Makefile
@@ -25,5 +25,6 @@ obj-$(CONFIG_CAN_JANZ_ICAN3)	+= janz-ican3.o
 obj-$(CONFIG_CAN_FLEXCAN)	+= flexcan.o
 obj-$(CONFIG_PCH_CAN)		+= pch_can.o
 obj-$(CONFIG_CAN_GRCAN)		+= grcan.o
+obj-$(CONFIG_CAN_XILINXCAN)	+= xilinx_can.o
 
 ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
diff --git a/drivers/net/can/xilinx_can.c b/drivers/net/can/xilinx_can.c
new file mode 100644
index 0000000..e660c68
--- /dev/null
+++ b/drivers/net/can/xilinx_can.c
@@ -0,0 +1,1139 @@
+/* Xilinx CAN device driver
+ *
+ * Copyright (C) 2012 - 2014 Xilinx, Inc.
+ * Copyright (C) 2009 PetaLogix. All rights reserved.
+ *
+ * Description:
+ * This driver is developed for Axi CAN IP and for Zynq CANPS Controller.
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/skbuff.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/can/dev.h>
+#include <linux/can/error.h>
+#include <linux/can/led.h>
+
+#define DRIVER_NAME	"XILINX_CAN"
+
+/* CAN registers set */
+enum xcan_reg {
+	XCAN_SRR_OFFSET		= 0x00, /* Software reset */
+	XCAN_MSR_OFFSET		= 0x04, /* Mode select */
+	XCAN_BRPR_OFFSET	= 0x08, /* Baud rate prescaler */
+	XCAN_BTR_OFFSET		= 0x0C, /* Bit timing */
+	XCAN_ECR_OFFSET		= 0x10, /* Error counter */
+	XCAN_ESR_OFFSET		= 0x14, /* Error status */
+	XCAN_SR_OFFSET		= 0x18, /* Status */
+	XCAN_ISR_OFFSET		= 0x1C, /* Interrupt status */
+	XCAN_IER_OFFSET		= 0x20, /* Interrupt enable */
+	XCAN_ICR_OFFSET		= 0x24, /* Interrupt clear */
+	XCAN_TXFIFO_ID_OFFSET	= 0x30,/* TX FIFO ID */
+	XCAN_TXFIFO_DLC_OFFSET	= 0x34, /* TX FIFO DLC */
+	XCAN_TXFIFO_DW1_OFFSET	= 0x38, /* TX FIFO Data Word 1 */
+	XCAN_TXFIFO_DW2_OFFSET	= 0x3C, /* TX FIFO Data Word 2 */
+	XCAN_RXFIFO_ID_OFFSET	= 0x50, /* RX FIFO ID */
+	XCAN_RXFIFO_DLC_OFFSET	= 0x54, /* RX FIFO DLC */
+	XCAN_RXFIFO_DW1_OFFSET	= 0x58, /* RX FIFO Data Word 1 */
+	XCAN_RXFIFO_DW2_OFFSET	= 0x5C, /* RX FIFO Data Word 2 */
+};
+
+/* CAN register bit masks - XCAN_<REG>_<BIT>_MASK */
+#define XCAN_SRR_CEN_MASK		0x00000002 /* CAN enable */
+#define XCAN_SRR_RESET_MASK		0x00000001 /* Soft Reset the CAN core */
+#define XCAN_MSR_LBACK_MASK		0x00000002 /* Loop back mode select */
+#define XCAN_MSR_SLEEP_MASK		0x00000001 /* Sleep mode select */
+#define XCAN_BRPR_BRP_MASK		0x000000FF /* Baud rate prescaler */
+#define XCAN_BTR_SJW_MASK		0x00000180 /* Synchronous jump width */
+#define XCAN_BTR_TS2_MASK		0x00000070 /* Time segment 2 */
+#define XCAN_BTR_TS1_MASK		0x0000000F /* Time segment 1 */
+#define XCAN_ECR_REC_MASK		0x0000FF00 /* Receive error counter */
+#define XCAN_ECR_TEC_MASK		0x000000FF /* Transmit error counter */
+#define XCAN_ESR_ACKER_MASK		0x00000010 /* ACK error */
+#define XCAN_ESR_BERR_MASK		0x00000008 /* Bit error */
+#define XCAN_ESR_STER_MASK		0x00000004 /* Stuff error */
+#define XCAN_ESR_FMER_MASK		0x00000002 /* Form error */
+#define XCAN_ESR_CRCER_MASK		0x00000001 /* CRC error */
+#define XCAN_SR_TXFLL_MASK		0x00000400 /* TX FIFO is full */
+#define XCAN_SR_ESTAT_MASK		0x00000180 /* Error status */
+#define XCAN_SR_ERRWRN_MASK		0x00000040 /* Error warning */
+#define XCAN_SR_NORMAL_MASK		0x00000008 /* Normal mode */
+#define XCAN_SR_LBACK_MASK		0x00000002 /* Loop back mode */
+#define XCAN_SR_CONFIG_MASK		0x00000001 /* Configuration mode */
+#define XCAN_IXR_TXFEMP_MASK		0x00004000 /* TX FIFO Empty */
+#define XCAN_IXR_WKUP_MASK		0x00000800 /* Wake up interrupt */
+#define XCAN_IXR_SLP_MASK		0x00000400 /* Sleep interrupt */
+#define XCAN_IXR_BSOFF_MASK		0x00000200 /* Bus off interrupt */
+#define XCAN_IXR_ERROR_MASK		0x00000100 /* Error interrupt */
+#define XCAN_IXR_RXNEMP_MASK		0x00000080 /* RX FIFO NotEmpty intr */
+#define XCAN_IXR_RXOFLW_MASK		0x00000040 /* RX FIFO Overflow intr */
+#define XCAN_IXR_RXOK_MASK		0x00000010 /* Message received intr */
+#define XCAN_IXR_TXOK_MASK		0x00000002 /* TX successful intr */
+#define XCAN_IXR_ARBLST_MASK		0x00000001 /* Arbitration lost intr */
+#define XCAN_IDR_ID1_MASK		0xFFE00000 /* Standard msg identifier */
+#define XCAN_IDR_SRR_MASK		0x00100000 /* Substitute remote TXreq */
+#define XCAN_IDR_IDE_MASK		0x00080000 /* Identifier extension */
+#define XCAN_IDR_ID2_MASK		0x0007FFFE /* Extended message ident */
+#define XCAN_IDR_RTR_MASK		0x00000001 /* Remote TX request */
+#define XCAN_DLCR_DLC_MASK		0xF0000000 /* Data length code */
+
+#define XCAN_INTR_ALL		(XCAN_IXR_TXOK_MASK | XCAN_IXR_BSOFF_MASK |\
+				 XCAN_IXR_WKUP_MASK | XCAN_IXR_SLP_MASK | \
+				 XCAN_IXR_RXNEMP_MASK | XCAN_IXR_ERROR_MASK | \
+				 XCAN_IXR_ARBLST_MASK | XCAN_IXR_RXOK_MASK)
+
+/* CAN register bit shift - XCAN_<REG>_<BIT>_SHIFT */
+#define XCAN_BTR_SJW_SHIFT		7  /* Synchronous jump width */
+#define XCAN_BTR_TS2_SHIFT		4  /* Time segment 2 */
+#define XCAN_IDR_ID1_SHIFT		21 /* Standard Messg Identifier */
+#define XCAN_IDR_ID2_SHIFT		1  /* Extended Message Identifier */
+#define XCAN_DLCR_DLC_SHIFT		28 /* Data length code */
+#define XCAN_ESR_REC_SHIFT		8  /* Rx Error Count */
+
+/* CAN frame length constants */
+#define XCAN_ECHO_SKB_MAX		64
+#define XCAN_FRAME_MAX_DATA_LEN		8
+#define XCAN_TIMEOUT			(1 * HZ)
+
+/**
+ * struct xcan_priv - This definition define CAN driver instance
+ * @can:			CAN private data structure.
+ * @tx_head:			Tx CAN packets ready to send on the queue
+ * @tx_tail:			Tx CAN packets successfully sended on the queue
+ * @xcan_echo_skb_max_tx:	Maximum number packets the driver can send
+ * @xcan_echo_skb_max_rx:	Maximum number packets the driver can receive
+ * @napi:			NAPI structure
+ * @read_reg:			For reading data from CAN registers
+ * @write_reg:			For writing data to CAN registers
+ * @dev:			Network device data structure
+ * @reg_base:			Ioremapped address to registers
+ * @irq_flags:			For request_irq()
+ * @aperclk:			Pointer to struct clk
+ * @devclk:			Pointer to struct clk
+ */
+struct xcan_priv {
+	struct can_priv can;
+	unsigned int tx_head;
+	unsigned int tx_tail;
+	u32 xcan_echo_skb_max_tx;
+	u32 xcan_echo_skb_max_rx;
+	struct napi_struct napi;
+	u32 (*read_reg)(const struct xcan_priv *priv, enum xcan_reg reg);
+	void (*write_reg)(const struct xcan_priv *priv, enum xcan_reg reg,
+			u32 val);
+	struct net_device *dev;
+	void __iomem *reg_base;
+	unsigned long irq_flags;
+	struct clk *aperclk;
+	struct clk *devclk;
+};
+
+/* CAN Bittiming constants as per Xilinx CAN specs */
+static const struct can_bittiming_const xcan_bittiming_const = {
+	.name = DRIVER_NAME,
+	.tseg1_min = 1,
+	.tseg1_max = 16,
+	.tseg2_min = 1,
+	.tseg2_max = 8,
+	.sjw_max = 4,
+	.brp_min = 1,
+	.brp_max = 256,
+	.brp_inc = 1,
+};
+
+/**
+ * xcan_write_reg - Write a value to the device register
+ * @priv:	Driver private data structure
+ * @reg:	Register offset
+ * @val:	Value to write at the Register offset
+ *
+ * Write data to the paricular CAN register
+ */
+static void xcan_write_reg(const struct xcan_priv *priv, enum xcan_reg reg,
+			u32 val)
+{
+	writel(val, priv->reg_base + reg);
+}
+
+/**
+ * xcan_read_reg - Read a value from the device register
+ * @priv:	Driver private data structure
+ * @reg:	Register offset
+ *
+ * Read data from the particular CAN register
+ * Return: value read from the CAN register
+ */
+static u32 xcan_read_reg(const struct xcan_priv *priv, enum xcan_reg reg)
+{
+	return readl(priv->reg_base + reg);
+}
+
+/**
+ * set_reset_mode - Resets the CAN device mode
+ * @ndev:	Pointer to net_device structure
+ *
+ * This is the driver reset mode routine.The driver
+ * enters into configuration mode.
+ *
+ * Return: 0 on success and failure value on error
+ */
+static int set_reset_mode(struct net_device *ndev)
+{
+	struct xcan_priv *priv = netdev_priv(ndev);
+	unsigned long timeout;
+
+	priv->can.state = CAN_STATE_STOPPED;
+
+	timeout = jiffies + XCAN_TIMEOUT;
+	while (!(priv->read_reg(priv, XCAN_SR_OFFSET) & XCAN_SR_CONFIG_MASK)) {
+		if (time_after(jiffies, timeout)) {
+			netdev_warn(ndev, "timedout waiting for config mode\n");
+			return -ETIMEDOUT;
+		}
+		usleep_range(500, 10000);
+	}
+
+	return 0;
+}
+
+/**
+ * xcan_set_bittiming - CAN set bit timing routine
+ * @ndev:	Pointer to net_device structure
+ *
+ * This is the driver set bittiming  routine.
+ * Return: 0 on success and failure value on error
+ */
+static int xcan_set_bittiming(struct net_device *ndev)
+{
+	struct xcan_priv *priv = netdev_priv(ndev);
+	struct can_bittiming *bt = &priv->can.bittiming;
+	u32 btr0, btr1;
+	u32 is_config_mode;
+
+	/* Check whether Xilinx CAN is in configuration mode.
+	 * It cannot set bit timing if Xilinx CAN is not in configuration mode.
+	 */
+	is_config_mode = priv->read_reg(priv, XCAN_SR_OFFSET) &
+				XCAN_SR_CONFIG_MASK;
+	if (!is_config_mode) {
+		netdev_alert(ndev,
+			"Cannot set bittiming can is not in config mode\n");
+		return -EPERM;
+	}
+
+	/* Setting Baud Rate prescalar value in BRPR Register */
+	btr0 = (bt->brp - 1) & XCAN_BRPR_BRP_MASK;
+
+	/* Setting Time Segment 1 in BTR Register */
+	btr1 = (bt->prop_seg + bt->phase_seg1 - 1) & XCAN_BTR_TS1_MASK;
+
+	/* Setting Time Segment 2 in BTR Register */
+	btr1 |= (bt->phase_seg2 - 1) << XCAN_BTR_TS2_SHIFT;
+
+	/* Setting Synchronous jump width in BTR Register */
+	btr1 |= (bt->sjw - 1) << XCAN_BTR_SJW_SHIFT;
+
+	priv->write_reg(priv, XCAN_BRPR_OFFSET, btr0);
+	priv->write_reg(priv, XCAN_BTR_OFFSET, btr1);
+
+	netdev_dbg(ndev, "BRPR=0x%08x, BTR=0x%08x\n",
+			priv->read_reg(priv, XCAN_BRPR_OFFSET),
+			priv->read_reg(priv, XCAN_BTR_OFFSET));
+
+	return 0;
+}
+
+/**
+ * xcan_chip_start - This the drivers start routine
+ * @ndev:	Pointer to net_device structure
+ *
+ * This is the drivers start routine.
+ * Based on the State of the CAN device it puts
+ * the CAN device into a proper mode.
+ *
+ * Return: 0 on success and failure value on error
+ */
+static int xcan_chip_start(struct net_device *ndev)
+{
+	struct xcan_priv *priv = netdev_priv(ndev);
+	u32 err;
+	unsigned long timeout;
+
+	/* Check if it is in reset mode */
+	err = set_reset_mode(ndev);
+	if (err < 0)
+		return err;
+
+	/* Enable interrupts */
+	priv->write_reg(priv, XCAN_IER_OFFSET, XCAN_INTR_ALL);
+
+	/* Check whether it is loopback mode or normal mode  */
+	if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK)
+		/* Put device into loopback mode */
+		priv->write_reg(priv, XCAN_MSR_OFFSET, XCAN_MSR_LBACK_MASK);
+	else
+		/* The device is in normal mode */
+		priv->write_reg(priv, XCAN_MSR_OFFSET, 0);
+
+	if (priv->can.state == CAN_STATE_STOPPED) {
+		/* Enable Xilinx CAN */
+		priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_CEN_MASK);
+		priv->can.state = CAN_STATE_ERROR_ACTIVE;
+		timeout = jiffies + XCAN_TIMEOUT;
+		if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) {
+			while ((priv->read_reg(priv, XCAN_SR_OFFSET)
+					& XCAN_SR_LBACK_MASK) == 0) {
+				if (time_after(jiffies, timeout)) {
+					netdev_warn(ndev,
+						"timedout for loopback mode\n");
+					return -ETIMEDOUT;
+				}
+				usleep_range(500, 10000);
+			}
+		} else {
+			while ((priv->read_reg(priv, XCAN_SR_OFFSET)
+					& XCAN_SR_NORMAL_MASK) == 0) {
+				if (time_after(jiffies, timeout)) {
+					netdev_warn(ndev,
+						"timedout for normal mode\n");
+					return -ETIMEDOUT;
+				}
+				usleep_range(500, 10000);
+			}
+		}
+		netdev_dbg(ndev, "status:#x%08x\n",
+				priv->read_reg(priv, XCAN_SR_OFFSET));
+	}
+	priv->can.state = CAN_STATE_ERROR_ACTIVE;
+	return 0;
+}
+
+/**
+ * xcan_do_set_mode - This sets the mode of the driver
+ * @ndev:	Pointer to net_device structure
+ * @mode:	Tells the mode of the driver
+ *
+ * This check the drivers state and calls the
+ * the corresponding modes to set.
+ *
+ * Return: 0 on success and failure value on error
+ */
+static int xcan_do_set_mode(struct net_device *ndev, enum can_mode mode)
+{
+	int ret;
+
+	switch (mode) {
+	case CAN_MODE_START:
+		ret = xcan_chip_start(ndev);
+		if (ret < 0)
+			netdev_err(ndev, "xcan_chip_start failed!\n");
+		netif_wake_queue(ndev);
+		break;
+	default:
+		ret = -EOPNOTSUPP;
+		break;
+	}
+
+	return ret;
+}
+
+/**
+ * xcan_start_xmit - Starts the transmission
+ * @skb:	sk_buff pointer that contains data to be Txed
+ * @ndev:	Pointer to net_device structure
+ *
+ * This function is invoked from upper layers to initiate transmission. This
+ * function uses the next available free txbuff and populates their fields to
+ * start the transmission.
+ *
+ * Return: 0 on success and failure value on error
+ */
+static int xcan_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+	struct xcan_priv *priv = netdev_priv(ndev);
+	struct net_device_stats *stats = &ndev->stats;
+	struct can_frame *cf = (struct can_frame *)skb->data;
+	u32 id, dlc, data[2] = {0, 0};
+
+	if (can_dropped_invalid_skb(ndev, skb))
+		return NETDEV_TX_OK;
+
+	/* Check if the TX buffer is full */
+	if (unlikely(priv->read_reg(priv, XCAN_SR_OFFSET) &
+			XCAN_SR_TXFLL_MASK)) {
+		netif_stop_queue(ndev);
+		netdev_err(ndev, "BUG!, TX FIFO full when queue awake!\n");
+		return NETDEV_TX_BUSY;
+	}
+
+	/* Watch carefully on the bit sequence */
+	if (cf->can_id & CAN_EFF_FLAG) {
+		/* Extended CAN ID format */
+		id = ((cf->can_id & CAN_EFF_MASK) << XCAN_IDR_ID2_SHIFT) &
+			XCAN_IDR_ID2_MASK;
+		id |= (((cf->can_id & CAN_EFF_MASK) >>
+			(CAN_EFF_ID_BITS-CAN_SFF_ID_BITS)) <<
+			XCAN_IDR_ID1_SHIFT) & XCAN_IDR_ID1_MASK;
+
+		/* The substibute remote TX request bit should be "1"
+		 * for extended frames as in the Xilinx CAN datasheet
+		 */
+		id |= XCAN_IDR_IDE_MASK | XCAN_IDR_SRR_MASK;
+
+		if (cf->can_id & CAN_RTR_FLAG)
+			/* Extended frames remote TX request */
+			id |= XCAN_IDR_RTR_MASK;
+	} else {
+		/* Standard CAN ID format */
+		id = ((cf->can_id & CAN_SFF_MASK) << XCAN_IDR_ID1_SHIFT) &
+			XCAN_IDR_ID1_MASK;
+
+		if (cf->can_id & CAN_RTR_FLAG)
+			/* Extended frames remote TX request */
+			id |= XCAN_IDR_SRR_MASK;
+	}
+
+	dlc = cf->can_dlc << XCAN_DLCR_DLC_SHIFT;
+
+	if (cf->can_dlc > 0)
+		data[0] = be32_to_cpup((__be32 *)(cf->data + 0));
+	if (cf->can_dlc > 4)
+		data[1] = be32_to_cpup((__be32 *)(cf->data + 4));
+
+	can_put_echo_skb(skb, ndev, priv->tx_head % priv->xcan_echo_skb_max_tx);
+	priv->tx_head++;
+
+	/* Write the Frame to Xilinx CAN TX FIFO */
+	priv->write_reg(priv, XCAN_TXFIFO_ID_OFFSET, id);
+	/* If the CAN frame is RTR frame this write triggers tranmission */
+	priv->write_reg(priv, XCAN_TXFIFO_DLC_OFFSET, dlc);
+	if (!(cf->can_id & CAN_RTR_FLAG)) {
+		priv->write_reg(priv, XCAN_TXFIFO_DW1_OFFSET, data[0]);
+		/* If the CAN frame is Standard/Extended frame this
+		 * write triggers tranmission
+		 */
+		priv->write_reg(priv, XCAN_TXFIFO_DW2_OFFSET, data[1]);
+		stats->tx_bytes += cf->can_dlc;
+	}
+
+	/* Check if the TX buffer is full */
+	if ((priv->tx_head - priv->tx_tail) == priv->xcan_echo_skb_max_tx)
+		netif_stop_queue(ndev);
+
+	return NETDEV_TX_OK;
+}
+
+/**
+ * xcan_rx -  Is called from CAN isr to complete the received
+ *		frame  processing
+ * @ndev:	Pointer to net_device structure
+ *
+ * This function is invoked from the CAN isr(poll) to process the Rx frames. It
+ * does minimal processing and invokes "netif_receive_skb" to complete further
+ * processing.
+ * Return: 0 on success and negative error value on error
+ */
+static int xcan_rx(struct net_device *ndev)
+{
+	struct xcan_priv *priv = netdev_priv(ndev);
+	struct net_device_stats *stats = &ndev->stats;
+	struct can_frame *cf;
+	struct sk_buff *skb;
+	u32 id_xcan, dlc, data[2] = {0, 0};
+
+	skb = alloc_can_skb(ndev, &cf);
+	if (!skb)
+		return -ENOMEM;
+
+	/* Read a frame from Xilinx zynq CANPS */
+	id_xcan = priv->read_reg(priv, XCAN_RXFIFO_ID_OFFSET);
+	dlc = priv->read_reg(priv, XCAN_RXFIFO_DLC_OFFSET) >>
+				XCAN_DLCR_DLC_SHIFT;
+
+	/* Change Xilinx CAN data length format to socketCAN data format */
+	cf->can_dlc = get_can_dlc(dlc);
+
+	/* Change Xilinx CAN ID format to socketCAN ID format */
+	if (id_xcan & XCAN_IDR_IDE_MASK) {
+		/* The received frame is an Extended format frame */
+		cf->can_id = (id_xcan & XCAN_IDR_ID1_MASK) >> 3;
+		cf->can_id |= (id_xcan & XCAN_IDR_ID2_MASK) >>
+				XCAN_IDR_ID2_SHIFT;
+		cf->can_id |= CAN_EFF_FLAG;
+		if (id_xcan & XCAN_IDR_RTR_MASK)
+			cf->can_id |= CAN_RTR_FLAG;
+	} else {
+		/* The received frame is a standard format frame */
+		cf->can_id = (id_xcan & XCAN_IDR_ID1_MASK) >>
+				XCAN_IDR_ID1_SHIFT;
+		if (id_xcan & XCAN_IDR_RTR_MASK)
+			cf->can_id |= CAN_RTR_FLAG;
+	}
+
+	if (!(id_xcan & XCAN_IDR_RTR_MASK)) {
+		data[0] = priv->read_reg(priv, XCAN_RXFIFO_DW1_OFFSET);
+		data[1] = priv->read_reg(priv, XCAN_RXFIFO_DW2_OFFSET);
+
+		/* Change Xilinx CAN data format to socketCAN data format */
+		*(__be32 *)(cf->data) = cpu_to_be32(data[0]);
+		if (cf->can_dlc > 4)
+			*(__be32 *)(cf->data + 4) = cpu_to_be32(data[1]);
+	}
+	can_led_event(ndev, CAN_LED_EVENT_RX);
+
+	netif_receive_skb(skb);
+
+	stats->rx_bytes += cf->can_dlc;
+	stats->rx_packets++;
+	return 0;
+}
+
+/**
+ * xcan_err_interrupt - error frame Isr
+ * @ndev:	net_device pointer
+ * @isr:	interrupt status register value
+ *
+ * This is the CAN error interrupt and it will
+ * check the the type of error and forward the error
+ * frame to upper layers.
+ */
+static void xcan_err_interrupt(struct net_device *ndev, u32 isr)
+{
+	struct xcan_priv *priv = netdev_priv(ndev);
+	struct net_device_stats *stats = &ndev->stats;
+	struct can_frame *cf;
+	struct sk_buff *skb;
+	u32 err_status, status;
+
+	skb = alloc_can_err_skb(ndev, &cf);
+	if (!skb) {
+		netdev_err(ndev, "alloc_can_err_skb() failed!\n");
+		return;
+	}
+
+	err_status = priv->read_reg(priv, XCAN_ESR_OFFSET);
+	priv->write_reg(priv, XCAN_ESR_OFFSET, err_status);
+	status = priv->read_reg(priv, XCAN_SR_OFFSET);
+
+	if (isr & XCAN_IXR_BSOFF_MASK) {
+		priv->can.state = CAN_STATE_BUS_OFF;
+		cf->can_id |= CAN_ERR_BUSOFF;
+		priv->can.can_stats.bus_off++;
+		/* Leave device in Config Mode in bus-off state */
+		priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_RESET_MASK);
+		can_bus_off(ndev);
+	} else if ((status & XCAN_SR_ESTAT_MASK) == XCAN_SR_ESTAT_MASK) {
+		cf->can_id |= CAN_ERR_CRTL;
+		priv->can.state = CAN_STATE_ERROR_PASSIVE;
+		priv->can.can_stats.error_passive++;
+		cf->data[1] |= CAN_ERR_CRTL_RX_PASSIVE |
+					CAN_ERR_CRTL_TX_PASSIVE;
+	} else if (status & XCAN_SR_ERRWRN_MASK) {
+		cf->can_id |= CAN_ERR_CRTL;
+		priv->can.state = CAN_STATE_ERROR_WARNING;
+		priv->can.can_stats.error_warning++;
+		cf->data[1] |= CAN_ERR_CRTL_RX_WARNING |
+					CAN_ERR_CRTL_TX_WARNING;
+	}
+
+	/* Check for Arbitration lost interrupt */
+	if (isr & XCAN_IXR_ARBLST_MASK) {
+		cf->can_id |= CAN_ERR_LOSTARB;
+		cf->data[0] = CAN_ERR_LOSTARB_UNSPEC;
+		priv->can.can_stats.arbitration_lost++;
+	}
+
+	/* Check for RX FIFO Overflow interrupt */
+	if (isr & XCAN_IXR_RXOFLW_MASK) {
+		cf->can_id |= CAN_ERR_CRTL;
+		cf->data[1] |= CAN_ERR_CRTL_RX_OVERFLOW;
+		stats->rx_over_errors++;
+		stats->rx_errors++;
+		priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_RESET_MASK);
+	}
+
+	/* Check for error interrupt */
+	if (isr & XCAN_IXR_ERROR_MASK) {
+		cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
+		cf->data[2] |= CAN_ERR_PROT_UNSPEC;
+
+		/* Check for Ack error interrupt */
+		if (err_status & XCAN_ESR_ACKER_MASK) {
+			cf->can_id |= CAN_ERR_ACK;
+			cf->data[3] |= CAN_ERR_PROT_LOC_ACK;
+			stats->tx_errors++;
+		}
+
+		/* Check for Bit error interrupt */
+		if (err_status & XCAN_ESR_BERR_MASK) {
+			cf->can_id |= CAN_ERR_PROT;
+			cf->data[2] = CAN_ERR_PROT_BIT;
+			stats->tx_errors++;
+		}
+
+		/* Check for Stuff error interrupt */
+		if (err_status & XCAN_ESR_STER_MASK) {
+			cf->can_id |= CAN_ERR_PROT;
+			cf->data[2] = CAN_ERR_PROT_STUFF;
+			stats->rx_errors++;
+		}
+
+		/* Check for Form error interrupt */
+		if (err_status & XCAN_ESR_FMER_MASK) {
+			cf->can_id |= CAN_ERR_PROT;
+			cf->data[2] = CAN_ERR_PROT_FORM;
+			stats->rx_errors++;
+		}
+
+		/* Check for CRC error interrupt */
+		if (err_status & XCAN_ESR_CRCER_MASK) {
+			cf->can_id |= CAN_ERR_PROT;
+			cf->data[3] = CAN_ERR_PROT_LOC_CRC_SEQ |
+					CAN_ERR_PROT_LOC_CRC_DEL;
+			stats->rx_errors++;
+		}
+			priv->can.can_stats.bus_error++;
+	}
+
+	netif_rx(skb);
+	stats->rx_packets++;
+	stats->rx_bytes += cf->can_dlc;
+
+	netdev_dbg(ndev, "%s: error status register:0x%x\n",
+			__func__, priv->read_reg(priv, XCAN_ESR_OFFSET));
+}
+
+/**
+ * xcan_state_interrupt - It will check the state of the CAN device
+ * @ndev:	net_device pointer
+ * @isr:	interrupt status register value
+ *
+ * This will checks the state of the CAN device
+ * and puts the device into appropriate state.
+ */
+static void xcan_state_interrupt(struct net_device *ndev, u32 isr)
+{
+	struct xcan_priv *priv = netdev_priv(ndev);
+
+	/* Check for Sleep interrupt if set put CAN device in sleep state */
+	if (isr & XCAN_IXR_SLP_MASK)
+		priv->can.state = CAN_STATE_SLEEPING;
+
+	/* Check for Wake up interrupt if set put CAN device in Active state */
+	if (isr & XCAN_IXR_WKUP_MASK)
+		priv->can.state = CAN_STATE_ERROR_ACTIVE;
+}
+
+/**
+ * xcan_rx_poll - Poll routine for rx packets (NAPI)
+ * @napi:	napi structure pointer
+ * @quota:	Max number of rx packets to be processed.
+ *
+ * This is the poll routine for rx part.
+ * It will process the packets maximux quota value.
+ *
+ * Return: number of packets received
+ */
+static int xcan_rx_poll(struct napi_struct *napi, int quota)
+{
+	struct net_device *ndev = napi->dev;
+	struct xcan_priv *priv = netdev_priv(ndev);
+	u32 isr, ier;
+	int work_done = 0;
+
+	isr = priv->read_reg(priv, XCAN_ISR_OFFSET);
+	while ((isr & XCAN_IXR_RXNEMP_MASK) && (work_done < quota)) {
+		if (isr & XCAN_IXR_RXOK_MASK) {
+			priv->write_reg(priv, XCAN_ICR_OFFSET,
+				XCAN_IXR_RXOK_MASK);
+			if (xcan_rx(ndev) < 0)
+				return work_done;
+			work_done++;
+		} else {
+			priv->write_reg(priv, XCAN_ICR_OFFSET,
+				XCAN_IXR_RXNEMP_MASK);
+			break;
+		}
+		priv->write_reg(priv, XCAN_ICR_OFFSET, XCAN_IXR_RXNEMP_MASK);
+		isr = priv->read_reg(priv, XCAN_ISR_OFFSET);
+	}
+
+	if (work_done < quota) {
+		napi_complete(napi);
+		ier = priv->read_reg(priv, XCAN_IER_OFFSET);
+		ier |= (XCAN_IXR_RXOK_MASK | XCAN_IXR_RXNEMP_MASK);
+		priv->write_reg(priv, XCAN_IER_OFFSET, ier);
+	}
+	return work_done;
+}
+
+/**
+ * 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) {
+		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);
+}
+
+/**
+ * xcan_interrupt - CAN Isr
+ * @irq:	irq number
+ * @dev_id:	device id poniter
+ *
+ * This is the xilinx CAN Isr. It checks for the type of interrupt
+ * and invokes the corresponding ISR.
+ *
+ * Return:
+ * IRQ_NONE - If CAN device is in sleep mode, IRQ_HANDLED otherwise
+ */
+static irqreturn_t xcan_interrupt(int irq, void *dev_id)
+{
+	struct net_device *ndev = (struct net_device *)dev_id;
+	struct xcan_priv *priv = netdev_priv(ndev);
+	u32 isr, ier;
+
+	/* Get the interrupt status from Xilinx CAN */
+	isr = priv->read_reg(priv, XCAN_ISR_OFFSET);
+	if (!isr)
+		return IRQ_NONE;
+
+	netdev_dbg(ndev, "%s: isr:#x%08x, err:#x%08x\n", __func__,
+			isr, priv->read_reg(priv, XCAN_ESR_OFFSET));
+
+	/* Check for the type of interrupt and Processing it */
+	if (isr & (XCAN_IXR_SLP_MASK | XCAN_IXR_WKUP_MASK)) {
+		priv->write_reg(priv, XCAN_ICR_OFFSET, (XCAN_IXR_SLP_MASK |
+				XCAN_IXR_WKUP_MASK));
+		xcan_state_interrupt(ndev, isr);
+	}
+
+	/* Check for Tx interrupt and Processing it */
+	if (isr & XCAN_IXR_TXOK_MASK) {
+		priv->write_reg(priv, XCAN_ICR_OFFSET, XCAN_IXR_TXOK_MASK);
+		xcan_tx_interrupt(ndev);
+	}
+
+	/* Check for the type of error interrupt and Processing it */
+	if (isr & (XCAN_IXR_ERROR_MASK | XCAN_IXR_RXOFLW_MASK |
+			XCAN_IXR_BSOFF_MASK | XCAN_IXR_ARBLST_MASK)) {
+		priv->write_reg(priv, XCAN_ICR_OFFSET, (XCAN_IXR_ERROR_MASK |
+				XCAN_IXR_RXOFLW_MASK | XCAN_IXR_BSOFF_MASK |
+				XCAN_IXR_ARBLST_MASK));
+		xcan_err_interrupt(ndev, isr);
+	}
+
+	/* Check for the type of receive interrupt and Processing it */
+	if (isr & (XCAN_IXR_RXNEMP_MASK | XCAN_IXR_RXOK_MASK)) {
+		ier = priv->read_reg(priv, XCAN_IER_OFFSET);
+		ier &= ~(XCAN_IXR_RXNEMP_MASK | XCAN_IXR_RXOK_MASK);
+		priv->write_reg(priv, XCAN_IER_OFFSET, ier);
+		napi_schedule(&priv->napi);
+	}
+	return IRQ_HANDLED;
+}
+
+/**
+ * xcan_stop - Driver stop routine
+ * @ndev:	Pointer to net_device structure
+ *
+ * This is the drivers stop routine. It will disable the
+ * interrupts and put the device into configuration mode.
+ */
+static void xcan_stop(struct net_device *ndev)
+{
+	struct xcan_priv *priv = netdev_priv(ndev);
+	u32 ier;
+
+	/* Disable interrupts and leave the can in configuration mode */
+	ier = priv->read_reg(priv, XCAN_IER_OFFSET);
+	ier &= ~XCAN_INTR_ALL;
+	priv->write_reg(priv, XCAN_IER_OFFSET, ier);
+	priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_RESET_MASK);
+	priv->can.state = CAN_STATE_STOPPED;
+}
+
+/**
+ * xcan_open - Driver open routine
+ * @ndev:	Pointer to net_device structure
+ *
+ * This is the driver open routine.
+ * Return: 0 on success and failure value on error
+ */
+static int xcan_open(struct net_device *ndev)
+{
+	struct xcan_priv *priv = netdev_priv(ndev);
+	int ret;
+
+	ret = request_irq(ndev->irq, xcan_interrupt, priv->irq_flags,
+			ndev->name, (void *)ndev);
+	if (ret < 0) {
+		netdev_err(ndev, "Irq allocation for CAN failed\n");
+		goto err;
+	}
+
+	ret = clk_prepare_enable(priv->devclk);
+	if (ret) {
+		netdev_err(ndev, "unable to enable device clock\n");
+		goto err;
+	}
+
+	ret = clk_prepare_enable(priv->aperclk);
+	if (ret) {
+		netdev_err(ndev, "unable to enable aper clock\n");
+		goto err_aperclk;
+	}
+
+	/* Set chip into reset mode */
+	ret = set_reset_mode(ndev);
+	if (ret < 0) {
+		netdev_err(ndev, "mode resetting failed failed!\n");
+		goto err_devclk;
+	}
+
+	/* Common open */
+	ret = open_candev(ndev);
+	if (ret)
+		goto err_devclk;
+
+	ret = xcan_chip_start(ndev);
+	if (ret < 0)
+		netdev_err(ndev, "xcan_chip_start failed!\n");
+
+
+	can_led_event(ndev, CAN_LED_EVENT_OPEN);
+	napi_enable(&priv->napi);
+	netif_start_queue(ndev);
+
+	return 0;
+
+err_aperclk:
+	free_irq(ndev->irq, ndev);
+	clk_disable_unprepare(priv->devclk);
+err_devclk:
+	free_irq(ndev->irq, ndev);
+	clk_disable_unprepare(priv->devclk);
+	clk_disable_unprepare(priv->aperclk);
+err:
+	return ret;
+}
+
+/**
+ * xcan_close - Driver close routine
+ * @ndev:	Pointer to net_device structure
+ *
+ * Return: 0 always
+ */
+static int xcan_close(struct net_device *ndev)
+{
+	struct xcan_priv *priv = netdev_priv(ndev);
+
+	netif_stop_queue(ndev);
+	napi_disable(&priv->napi);
+	xcan_stop(ndev);
+	clk_disable_unprepare(priv->aperclk);
+	clk_disable_unprepare(priv->devclk);
+	free_irq(ndev->irq, ndev);
+	close_candev(ndev);
+
+	can_led_event(ndev, CAN_LED_EVENT_STOP);
+
+	return 0;
+}
+
+/**
+ * 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);
+	return 0;
+
+err_clk:
+	clk_disable_unprepare(priv->devclk);
+err:
+	return ret;
+}
+
+static const struct net_device_ops xcan_netdev_ops = {
+	.ndo_open	= xcan_open,
+	.ndo_stop	= xcan_close,
+	.ndo_start_xmit	= xcan_start_xmit,
+};
+
+#ifdef CONFIG_PM_SLEEP
+/**
+ * xcan_suspend - Suspend method for the driver
+ * @_dev:	Address of the platform_device structure
+ *
+ * Put the driver into low power mode.
+ * Return: 0 always
+ */
+static int xcan_suspend(struct device *_dev)
+{
+	struct platform_device *pdev = container_of(_dev,
+			struct platform_device, dev);
+	struct net_device *ndev = platform_get_drvdata(pdev);
+	struct xcan_priv *priv = netdev_priv(ndev);
+
+	if (netif_running(ndev)) {
+		netif_stop_queue(ndev);
+		netif_device_detach(ndev);
+	}
+
+	priv->write_reg(priv, XCAN_MSR_OFFSET, XCAN_MSR_SLEEP_MASK);
+	priv->can.state = CAN_STATE_SLEEPING;
+
+	clk_disable(priv->aperclk);
+	clk_disable(priv->devclk);
+
+	return 0;
+}
+
+/**
+ * xcan_resume - Resume from suspend
+ * @dev:	Address of the platformdevice structure
+ *
+ * Resume operation after suspend.
+ * Return: 0 on success and failure value on error
+ */
+static int xcan_resume(struct device *dev)
+{
+	struct platform_device *pdev = container_of(dev,
+			struct platform_device, dev);
+	struct net_device *ndev = platform_get_drvdata(pdev);
+	struct xcan_priv *priv = netdev_priv(ndev);
+	int ret;
+
+	ret = clk_enable(priv->aperclk);
+	if (ret) {
+		dev_err(dev, "Cannot enable clock.\n");
+		return ret;
+	}
+	ret = clk_enable(priv->devclk);
+	if (ret) {
+		dev_err(dev, "Cannot enable clock.\n");
+		return ret;
+	}
+
+	priv->write_reg(priv, XCAN_MSR_OFFSET, 0);
+	priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_CEN_MASK);
+	priv->can.state = CAN_STATE_ERROR_ACTIVE;
+
+	if (netif_running(ndev)) {
+		netif_device_attach(ndev);
+		netif_start_queue(ndev);
+	}
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(xcan_dev_pm_ops, xcan_suspend, xcan_resume);
+
+/**
+ * xcan_probe - Platform registration call
+ * @pdev:	Handle to the platform device structure
+ *
+ * This function does all the memory allocation and registration for the CAN
+ * device.
+ *
+ * Return: 0 on success and failure value on error
+ */
+static int xcan_probe(struct platform_device *pdev)
+{
+	struct resource *res; /* IO mem resources */
+	struct net_device *ndev;
+	struct xcan_priv *priv;
+	int ret;
+
+	/* Create a CAN device instance */
+	ndev = alloc_candev(sizeof(struct xcan_priv), XCAN_ECHO_SKB_MAX);
+	if (!ndev)
+		return -ENOMEM;
+
+	priv = netdev_priv(ndev);
+	priv->dev = ndev;
+	priv->can.bittiming_const = &xcan_bittiming_const;
+	priv->can.do_set_bittiming = xcan_set_bittiming;
+	priv->can.do_set_mode = xcan_do_set_mode;
+	priv->can.do_get_berr_counter = xcan_get_berr_counter;
+	priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK |
+					CAN_CTRLMODE_BERR_REPORTING;
+
+	/* Get IRQ for the device */
+	ndev->irq = platform_get_irq(pdev, 0);
+	ndev->flags |= IFF_ECHO;	/* We support local echo */
+
+	platform_set_drvdata(pdev, ndev);
+	SET_NETDEV_DEV(ndev, &pdev->dev);
+	ndev->netdev_ops = &xcan_netdev_ops;
+
+	/* Get the virtual base address for the device */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	priv->reg_base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(priv->reg_base)) {
+		ret = PTR_ERR(priv->reg_base);
+		goto err_free;
+	}
+	ndev->mem_start = res->start;
+	ndev->mem_end = res->end;
+
+	priv->write_reg = xcan_write_reg;
+	priv->read_reg = xcan_read_reg;
+
+	ret = of_property_read_u32(pdev->dev.of_node, "tx-fifo-depth",
+				&priv->xcan_echo_skb_max_tx);
+	if (ret < 0)
+		goto err_free;
+
+	ret = of_property_read_u32(pdev->dev.of_node, "rx-fifo-depth",
+				&priv->xcan_echo_skb_max_rx);
+	if (ret < 0)
+		goto err_free;
+
+	/* Getting the CAN devclk info */
+	priv->devclk = devm_clk_get(&pdev->dev, "ref_clk");
+	if (IS_ERR(priv->devclk)) {
+		dev_err(&pdev->dev, "Device clock not found.\n");
+		ret = PTR_ERR(priv->devclk);
+		goto err_free;
+	}
+
+	/* Check for type of CAN device */
+	if (of_device_is_compatible(pdev->dev.of_node,
+				    "xlnx,zynq-can-1.00.a")) {
+		priv->aperclk = devm_clk_get(&pdev->dev, "aper_clk");
+		if (IS_ERR(priv->aperclk)) {
+			dev_err(&pdev->dev, "aper clock not found\n");
+			ret = PTR_ERR(priv->aperclk);
+			goto err_free;
+		}
+	} else {
+		priv->aperclk = priv->devclk;
+	}
+
+	priv->can.clock.freq = clk_get_rate(priv->devclk);
+
+	netif_napi_add(ndev, &priv->napi, xcan_rx_poll,
+				priv->xcan_echo_skb_max_rx);
+
+	ret = register_candev(ndev);
+	if (ret) {
+		dev_err(&pdev->dev, "fail to register failed (err=%d)\n", ret);
+		goto err_free;
+	}
+
+	devm_can_led_init(ndev);
+	dev_info(&pdev->dev,
+			"reg_base=0x%p irq=%d clock=%d, tx fifo depth:%d\n",
+			priv->reg_base, ndev->irq, priv->can.clock.freq,
+			priv->xcan_echo_skb_max_tx);
+
+	return 0;
+
+err_free:
+	free_candev(ndev);
+
+	return ret;
+}
+
+/**
+ * xcan_remove - Unregister the device after releasing the resources
+ * @pdev:	Handle to the platform device structure
+ *
+ * This function frees all the resources allocated to the device.
+ * Return: 0 always
+ */
+static int xcan_remove(struct platform_device *pdev)
+{
+	struct net_device *ndev = platform_get_drvdata(pdev);
+	struct xcan_priv *priv = netdev_priv(ndev);
+
+	if (set_reset_mode(ndev) < 0)
+		netdev_err(ndev, "mode resetting failed!\n");
+
+	unregister_candev(ndev);
+	netif_napi_del(&priv->napi);
+
+	free_candev(ndev);
+
+	return 0;
+}
+
+/* Match table for OF platform binding */
+static struct of_device_id xcan_of_match[] = {
+	{ .compatible = "xlnx,zynq-can-1.00.a", },
+	{ .compatible = "xlnx,axi-can-1.00.a", },
+	{ /* end of list */ },
+};
+MODULE_DEVICE_TABLE(of, xcan_of_match);
+
+static struct platform_driver xcan_driver = {
+	.probe = xcan_probe,
+	.remove	= xcan_remove,
+	.driver	= {
+		.owner = THIS_MODULE,
+		.name = DRIVER_NAME,
+		.pm = &xcan_dev_pm_ops,
+		.of_match_table	= xcan_of_match,
+	},
+};
+
+module_platform_driver(xcan_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Xilinx Inc");
+MODULE_DESCRIPTION("Xilinx CAN interface");
-- 
1.7.4


--
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

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

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.

Gr{oetje,eeting}s,

                        Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

^ permalink raw reply

* Re: [PATCH v4] bus: imx-weim: support CS GPR configuration
From: Shawn Guo @ 2014-02-17  9:22 UTC (permalink / raw)
  To: Philippe De Muyter
  Cc: Alexander Shiyan,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Huang Shijie,
	kernel-bIcnvbaLZ9MEGnE8C9+IrQ, devicetree-u79uwXL29TY76Z2rM5mHXA,
	Rob Herring, Mark Rutland
In-Reply-To: <20140217085221.GB23095-NqYOdiUDesgPnqCj3zZnUQ@public.gmane.org>

On Mon, Feb 17, 2014 at 09:52:21AM +0100, Philippe De Muyter wrote:
> On Sun, Feb 16, 2014 at 10:31:16PM +0800, Shawn Guo wrote:
> > On Sun, Feb 16, 2014 at 06:22:44PM +0400, Alexander Shiyan wrote:
> > > Воскресенье, 16 февраля 2014, 22:03 +08:00 от Shawn Guo <shawn.guo-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>:
> > > > For imx50-weim and imx6q-weim type of devices, there might a WEIM CS
> > > > space configuration register in General Purpose Register controller,
> > > > e.g. IOMUXC_GPR1 on i.MX6Q.
> > > ...
> > > > +static int __init imx_weim_gpr_setup(struct platform_device *pdev)
> > > > +{
> > > > +	struct device_node *np = pdev->dev.of_node;
> > > > +	struct property *prop;
> > > > +	const __be32 *p;
> > > > +	struct regmap *gpr;
> > > > +	u32 gprvals[4] = {
> > > > +		05,	/* CS0(128M) CS1(0M)  CS2(0M)  CS3(0M) */
> > > > +		033,	/* CS0(64M)  CS1(64M) CS2(0M)  CS3(0M) */
> > > > +		0113,	/* CS0(64M)  CS1(32M) CS2(32M) CS3(0M) */
> > > > +		01111,	/* CS0(64M)  CS1(32M) CS2(32M) CS3(0M) */
> > > > +	};
> > > > +	u32 gprval = 0;
> > > > +	u32 val;
> > > > +	int cs = 0;
> > > > +	int i = 0;
> > > > +
> > > > +	gpr = syscon_regmap_lookup_by_phandle(np, "fsl,weim-cs-gpr");
> > > > +	if (IS_ERR(gpr)) {
> > > > +		dev_dbg(&pdev->dev, "failed to find weim-cs-gpr\n");
> > > > +		return 0;
> > > 
> > > Only one comment:
> > > You do not use these error codes in the probe(),
> > > so let's declare this function as void.
> > 
> > Oh, yes.  I should check return of imx_weim_gpr_setup() in probe().
> > Will add in the next version.
> 
> Well, as all the error cases are already covered by error messages, there is
> no added value in testing that again in probe().  As Alexander wrote, you can
> declare imx_weim_gpr_setup() as 'void'.

Hmm, shouldn't we stop probing when running into the error of invalid
ranges configuration?

Shawn

--
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 v4] bus: imx-weim: support CS GPR configuration
From: Shawn Guo @ 2014-02-17  9:20 UTC (permalink / raw)
  To: Philippe De Muyter
  Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	Alexander Shiyan, Huang Shijie, kernel-bIcnvbaLZ9MEGnE8C9+IrQ,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Rob Herring, Mark Rutland
In-Reply-To: <20140217084843.GA23095-NqYOdiUDesgPnqCj3zZnUQ@public.gmane.org>

On Mon, Feb 17, 2014 at 09:48:43AM +0100, Philippe De Muyter wrote:
> > @@ -19,6 +24,26 @@ Required properties:
> >  
> >  			   <cs-number> 0 <physical address of mapping> <size>
> >  
> > +Optional properties:
> > +
> > + - fsl,weim-cs-gpr:	For "fsl,imx50-weim" and "fsl,imx6q-weim" type of
> > +			devices, it should be the phandle to the system General
> > +			Purpose Register controller that contains WEIM CS GPR
> > +			register, e.g. IOMUXC_GPR1 on i.MX6Q.  IOMUXC_GPR1[11:0]
> > +			should be set up as one of the following 4 possible
> > +			values depending on the CS space configuration.
> > +
> > +			IOMUXC_GPR1[11:0]    CS0    CS1    CS2    CS3
> > +			---------------------------------------------
> > +				05	    128M     0M     0M     0M
> > +				033          64M    64M     0M     0M
> > +				0113         64M    32M    32M     0M
> > +				01111        32M    32M    32M    32M
> > +
> > +			In case that the property is absent, the reset value or
> > +			what bootloader sets up in IOMUXC_GPR1[11:0] will be
> > +			used.
> > +
> >  Timing property for child nodes. It is mandatory, not optional.
> >  
> >   - fsl,weim-cs-timing:	The timing array, contains timing values for the
> 
> Could you also add the following line to the example in the same file :
> 
> 		fsl,weim-cs-gpr = <&gpr>;
> 
> It took me some time to figure it out :)

Okay.  I will wait comments from DT folks on the binding update for a
while, and then send out v5.

> 
> > diff --git a/drivers/bus/imx-weim.c b/drivers/bus/imx-weim.c
> > index 3ef58c8..35e9529 100644
> > --- a/drivers/bus/imx-weim.c
> > +++ b/drivers/bus/imx-weim.c
> > @@ -11,6 +11,9 @@
> >  #include <linux/clk.h>
> >  #include <linux/io.h>
> >  #include <linux/of_device.h>
> > +#include <linux/mfd/syscon.h>
> > +#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
> > +#include <linux/regmap.h>
> >  
> >  struct imx_weim_devtype {
> >  	unsigned int	cs_count;
> > @@ -56,6 +59,55 @@ static const struct of_device_id weim_id_table[] = {
> >  };
> >  MODULE_DEVICE_TABLE(of, weim_id_table);
> >  
> > +static int __init imx_weim_gpr_setup(struct platform_device *pdev)
> > +{
> > +	struct device_node *np = pdev->dev.of_node;
> > +	struct property *prop;
> > +	const __be32 *p;
> > +	struct regmap *gpr;
> > +	u32 gprvals[4] = {
> > +		05,	/* CS0(128M) CS1(0M)  CS2(0M)  CS3(0M) */
> > +		033,	/* CS0(64M)  CS1(64M) CS2(0M)  CS3(0M) */
> > +		0113,	/* CS0(64M)  CS1(32M) CS2(32M) CS3(0M) */
> > +		01111,	/* CS0(64M)  CS1(32M) CS2(32M) CS3(0M) */
> 
> 			   CS0(32M)			CS3(32M)

Sure, thanks.

Shawn

> 
> > +	};
> > +	u32 gprval = 0;
> > +	u32 val;
> > +	int cs = 0;
> > +	int i = 0;
> > +
> > +	gpr = syscon_regmap_lookup_by_phandle(np, "fsl,weim-cs-gpr");
> > +	if (IS_ERR(gpr)) {
> > +		dev_dbg(&pdev->dev, "failed to find weim-cs-gpr\n");
> > +		return 0;
> > +	}
> > +
> > +	of_property_for_each_u32(np, "ranges", prop, p, val) {
> > +		if (i % 4 == 0) {
> > +			cs = val;
> > +		} else if (i % 4 == 3 && val) {
> > +			val = (val / SZ_32M) | 1;
> > +			gprval |= val << cs * 3;
> > +		}
> > +		i++;
> > +	}
> > +
> > +	if (i == 0 || i % 4)
> > +		goto err;
> > +
> > +	for (i = 0; i < ARRAY_SIZE(gprvals); i++) {
> > +		if (gprval == gprvals[i]) {
> > +			/* Found it. Set up IOMUXC_GPR1[11:0] with it. */
> > +			regmap_update_bits(gpr, IOMUXC_GPR1, 0xfff, gprval);
> > +			return 0;
> > +		}
> > +	}
> > +
> > +err:
> > +	dev_err(&pdev->dev, "Invalid 'ranges' configuration\n");
> > +	return -EINVAL;
> > +}
> > +
> >  /* Parse and set the timing for this device. */
> >  static int __init weim_timing_setup(struct device_node *np, void __iomem *base,
> >  				    const struct imx_weim_devtype *devtype)
> > @@ -92,6 +144,9 @@ static int __init weim_parse_dt(struct platform_device *pdev,
> >  	struct device_node *child;
> >  	int ret;
> >  
> > +	if (devtype == &imx50_weim_devtype)
> > +		imx_weim_gpr_setup(pdev);
> > +
> >  	for_each_child_of_node(pdev->dev.of_node, child) {
> >  		if (!child->name)
> >  			continue;
> > -- 
> > 1.7.9.5
> > 
> 
> -- 
> Philippe De Muyter +32 2 6101532 Macq SA rue de l'Aeronef 2 B-1140 Bruxelles

--
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 2/5] ARM: shmobile: r8a7791: add i2c master nodes to dtsi
From: Wolfram Sang @ 2014-02-17  9:19 UTC (permalink / raw)
  To: Geert Uytterhoeven
  Cc: Magnus Damm, SH-Linux, linux-arm-kernel@lists.infradead.org,
	devicetree@vger.kernel.org, Simon Horman
In-Reply-To: <CAMuHMdW8MLknykkfu9XS5CDpuJcRNyPz+GV5bhUNGndxFacnQw@mail.gmail.com>

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


> 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?


[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply

* Re: [PATCH 2/5] ARM: shmobile: r8a7791: add i2c master nodes to dtsi
From: Wolfram Sang @ 2014-02-17  9:12 UTC (permalink / raw)
  To: Magnus Damm
  Cc: SH-Linux, linux-arm-kernel@lists.infradead.org, devicetree,
	Simon Horman
In-Reply-To: <CANqRtoSDxQfSw5PJB6-UcL-UuMySBkoCqfC+1H=o-EBA18ouLQ@mail.gmail.com>

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


> > 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.

> So far we've dealt with this by updating the driver and only relying
> on the actual SoC name as suffix.

OK, for consistency reasons I will resend.


[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply

* Re: [PATCH 2/5] ARM: shmobile: r8a7791: add i2c master nodes to dtsi
From: Geert Uytterhoeven @ 2014-02-17  9:11 UTC (permalink / raw)
  To: Magnus Damm
  Cc: Wolfram Sang, SH-Linux, linux-arm-kernel@lists.infradead.org,
	devicetree@vger.kernel.org, Simon Horman
In-Reply-To: <CANqRtoSDxQfSw5PJB6-UcL-UuMySBkoCqfC+1H=o-EBA18ouLQ@mail.gmail.com>

On Mon, Feb 17, 2014 at 9:02 AM, Magnus Damm <magnus.damm@gmail.com> wrote:
> On Mon, Feb 17, 2014 at 4:54 PM, Wolfram Sang <wsa@the-dreams.de> wrote:
>> On Sun, Feb 16, 2014 at 10:40:55AM +0100, Wolfram Sang wrote:
>>> From: Wolfram Sang <wsa@sang-engineering.com>
>>>
>>> Signed-off-by: Wolfram Sang <wsa@sang-engineering.com>
>>
>> From your other mail:
>>
>> "[2/5] needs to be reworked to exclude the r8a7790 compatible string."
>>
>>> +             compatible = "renesas,i2c-r8a7791", "renesas,i2c-r8a7790";
>>
>> Why is that? From my knowledge, you start with the exact compatible
>> property and hardware compatible entries may follow.

Thanks for posting the link to the device tree wiki/quoting from it.
I had faint memories of that quote, but couldn't remember where I read it.

> 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.
>
> So far we've dealt with this by updating the driver and only relying
> on the actual SoC name as suffix.
>
> I'm sure there are tons of opinions. =)

Hehe ;-)

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.

In our case, this probably won't happen, as we will have "renesas,i2c-r8a7791"
in the driver, but the driver could be forked in between the addition of
"renesas,i2c-r8a7790" and "renesas,i2c-r8a7791" in our non-ideal world.

Gr{oetje,eeting}s,

                        Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

^ permalink raw reply

* RE: [PATCHv11 2/2] dma: Add Freescale eDMA engine driver support
From: Jingchang Lu @ 2014-02-17  9:08 UTC (permalink / raw)
  To: vinod.koul@intel.com
  Cc: dan.j.williams@intel.com, arnd@arndb.de, shawn.guo@linaro.org,
	pawel.moll@arm.com, mark.rutland@arm.com, swarren@wwwdotorg.org,
	linux-kernel@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org,
	Jingchang Lu
In-Reply-To: <ca19105670be4af882ed72d3e004a6c7@BLUPR03MB472.namprd03.prod.outlook.com>

Hi, Vinod,

    Could you please help merge this patch? We are having some other upstreaming
patches using the dma functionality pending the acceptance of the eDMA driver.
    Many thanks!

Best Regards,
Jingchang

> -----Original Message-----
> From: Lu Jingchang-B35083
> Sent: Monday, January 27, 2014 1:20 PM
> To: Lu Jingchang-B35083; vinod.koul@intel.com
> Cc: dan.j.williams@intel.com; arnd@arndb.de; shawn.guo@linaro.org;
> pawel.moll@arm.com; mark.rutland@arm.com; swarren@wwwdotorg.org; linux-
> kernel@vger.kernel.org; linux-arm-kernel@lists.infradead.org;
> devicetree@vger.kernel.org; Wang Huan-B18965
> Subject: RE: [PATCHv11 2/2] dma: Add Freescale eDMA engine driver support
> 
> Hi, Vinod,
> 
>   Let me give some more explanation on the eDMA engine pause and
> termination here:
> The eDMA engine is a request-driven controller, it manage all channels in
> one engine and schedule them to perform each one's transfer when one's
> dma request arrive.
> When a dma request of a specific channel is received, the channel's
> appropriate TCD Parameter contents are loaded into the eDMA engine, and
> the appropriate reads and writes Perform until the minor byte transfer
> count has transferred, the number of bytes to transfer per request is
> determined by the salve's characteristics, such as the FIFO size, and the
> dma request condition is also determined by specific slave, such as FIFO
> empty.
> And to the transfer a bunch of data need many dma requests.
>   So if the dma request enable bit of a channel is cleared, there will be
> no further dma Request received by the eDMA engine, thus the channel will
> never be scheduled to run by the eDMA engine, the channel is paused,
> halted, also as stopped. If the channel need to transfer the remained
> data with the previous setting, just set the dma request enable bit, the
> transfer will complete with slave's dma request.(resume) If the
> parameters need be changed, corresponding register parameters can be
> reprogrammed, after all is ok, the dma request enable bit can be set to
> enable a new dma transfer.(terminate)
>   So is this ok and could it be merged, thanks!
> 
> 
> Best Regards,
> Jingchang
> 
> 
> > -----Original Message-----
> > From: Jingchang Lu [mailto:b35083@freescale.com]
> > Sent: Monday, January 20, 2014 5:24 PM
> > To: vinod.koul@intel.com
> > Cc: dan.j.williams@intel.com; arnd@arndb.de; shawn.guo@linaro.org;
> > pawel.moll@arm.com; mark.rutland@arm.com; swarren@wwwdotorg.org; linux-
> > kernel@vger.kernel.org; linux-arm-kernel@lists.infradead.org;
> > devicetree@vger.kernel.org; Lu Jingchang-B35083; Wang Huan-B18965
> > Subject: [PATCHv11 2/2] dma: Add Freescale eDMA engine driver support
> >
> > Add Freescale enhanced direct memory(eDMA) controller support.
> > This module can be found on Vybrid and LS-1 SoCs.
> >
> > Signed-off-by: Alison Wang <b18965@freescale.com>
> > Signed-off-by: Jingchang Lu <b35083@freescale.com>
> > Acked-by: Arnd Bergmann <arnd@arndb.de>
> > ---
> > changes in v11:
> >  Add dma device_slave_caps definition.
> >
> > changes in v10:
> >  define fsl_edma_mutex in fsl_edma_engine instead of global.
> >  minor changes of binding description.
> >
> > changes in v9:
> >  define endian's operating functions instead of macro definition.
> >  remove the filter function, using dma_get_slave_channel instead.
> >
> > changes in v8:
> >  change the edma driver according eDMA dts change.
> >  add big-endian and little-endian handling.
> >
> >  no changes in v4 ~ v7.
> >
> >  changes in v3:
> >   add vf610 edma dt-bindings namespace with prefix VF610_*.
> >
> >  changes in v2:
> >   using generic dma-channels property instead of fsl,dma-channels.
> >
> >  Documentation/devicetree/bindings/dma/fsl-edma.txt |  76 ++
> >  drivers/dma/Kconfig                                |  10 +
> >  drivers/dma/Makefile                               |   1 +
> >  drivers/dma/fsl-edma.c                             | 975
> > +++++++++++++++++++++
> >  4 files changed, 1062 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/dma/fsl-edma.txt
> >  create mode 100644 drivers/dma/fsl-edma.c


^ permalink raw reply

* [PATCH v3 15/15] mfd: max14577: Add device tree bindings document
From: Krzysztof Kozlowski @ 2014-02-17  9:05 UTC (permalink / raw)
  To: MyungJoo Ham, Chanwoo Choi, Samuel Ortiz, Lee Jones, Mark Brown,
	linux-kernel, linux-arm-kernel
  Cc: Marek Szyprowski, Bartlomiej Zolnierkiewicz, Kyungmin Park,
	Tomasz Figa, Dmitry Eremin-Solenikov, David Woodhouse,
	Krzysztof Kozlowski, devicetree, Rob Herring, Pawel Moll,
	Mark Rutland
In-Reply-To: <1392627950-26927-1-git-send-email-k.kozlowski@samsung.com>

Add document describing device tree bindings for MAX14577 MFD
drivers: MFD core, extcon, regulator and charger.

Both MAX14577 and MAX77836 chipsets are documented.

Signed-off-by: Krzysztof Kozlowski <k.kozlowski@samsung.com>
Cc: Kyungmin Park <kyungmin.park@samsung.com>
Cc: Tomasz Figa <t.figa@samsung.com>
Cc: Marek Szyprowski <m.szyprowski@samsung.com>
Cc: devicetree@vger.kernel.org
Cc: Rob Herring <robh+dt@kernel.org>
Cc: Pawel Moll <pawel.moll@arm.com>
Cc: Mark Rutland <mark.rutland@arm.com>
---
 Documentation/devicetree/bindings/mfd/max14577.txt |  149 ++++++++++++++++++++
 1 file changed, 149 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mfd/max14577.txt

diff --git a/Documentation/devicetree/bindings/mfd/max14577.txt b/Documentation/devicetree/bindings/mfd/max14577.txt
new file mode 100644
index 000000000000..111115b0a6f3
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/max14577.txt
@@ -0,0 +1,149 @@
+Maxim MAX14577/77836 Multi-Function Device
+
+MAX14577 is a Multi-Function Device with Micro-USB Interface Circuit, Li+
+Battery Charger and SFOUT LDO output for powering USB devices. It is
+interfaced to host controller using I2C.
+
+MAX77836 additionally contains PMIC (with two LDO regulators) and Fuel Gauge.
+
+
+Required properties:
+- compatible : Must be "maxim,max14577" or "maxim,max77836".
+- reg : I2C slave address for the max14577 chip (0x25 for max14577/max77836)
+- interrupts : IRQ line for the chip.
+- interrupt-parent :  The parent interrupt controller.
+
+
+Optional nodes:
+- max14577-muic/max77836-muic :
+	Node used only by extcon consumers.
+	Required properties:
+		- compatible : "maxim,max14577-muic" or "maxim,max77836-muic"
+
+- regulators :
+	Required properties:
+		- compatible : "maxim,max14577-regulator"
+			or "maxim,max77836-regulator"
+
+	May contain a sub-node per regulator from the list below. Each
+	sub-node should contain the constraints and initialization information
+	for that regulator. See regulator.txt for a description of standard
+	properties for these sub-nodes.
+
+	List of valid regulator names:
+	- for max14577: CHARGER, SAFEOUT.
+	- for max77836: CHARGER, SAFEOUT, LDO1, LDO2.
+
+	The SAFEOUT is a fixed voltage regulator so there is no need to specify
+	voltages for it.
+
+ - charger :
+	Required properties:
+		- compatible : "maxim,max14577-charger"
+			or "maxim,max77836-charger"
+		- maxim,fast-charge-timer : Timer in hours to trigger the
+			INT3/MBCCHGERR interrupt; Valid values:
+			- 5, 6 or 7 (hours),
+			- 0 to disable.
+		- maxim,fast-charge-uamp : Current in uA for Fast Charge;
+			Valid values:
+			- for max14577: 90000 - 950000;
+			- for max77836: 45000 - 475000;
+		- maxim,eoc-uamp : Current in uA for End-Of-Charge mode;
+			Valid values:
+			- for max14577: 50000 - 200000;
+			- for max77836: 5000 - 100000;
+		- maxim,ovp-uvolt : OverVoltage Protection Threshold in uV;
+			In an overvoltage condition, INT asserts and charging
+			stops. Valid values:
+			- 6000000, 6500000, 7000000, 7500000;
+		- maxim,constant-uvolt : Battery Constant Voltage in uV;
+			Valid values:
+			- 4000000 - 4280000 (step by 20000);
+			- 4350000;
+
+
+Example:
+
+#include <dt-bindings/interrupt-controller/irq.h>
+
+max14577@25 {
+	compatible = "maxim,max14577";
+	reg = <0x25>;
+	interrupt-parent = <&gpx1>;
+	interrupts = <5 IRQ_TYPE_NONE>;
+
+	muic: max14577-muic {
+		compatible = "maxim,max14577-muic";
+	};
+
+	regulators {
+		compatible = "maxim,max14577-regulator";
+
+		SAFEOUT {
+			regulator-name = "SAFEOUT";
+		};
+		CHARGER {
+			regulator-name = "CHARGER";
+			regulator-min-microamp = <90000>;
+			regulator-max-microamp = <950000>;
+			regulator-boot-on;
+		};
+	};
+
+	charger {
+		compatible = "maxim,max14577-charger";
+
+		maxim,fast-charge-timer = <6>;
+		maxim,constant-uvolt = <4350000>;
+		maxim,fast-charge-uamp = <450000>;
+		maxim,eoc-uamp = <50000>;
+		maxim,ovp-uvolt = <6500000>;
+	};
+};
+
+
+max77836@25 {
+	compatible = "maxim,max77836";
+	reg = <0x25>;
+	interrupt-parent = <&gpx1>;
+	interrupts = <5 IRQ_TYPE_NONE>;
+
+	muic: max77836-muic {
+		compatible = "maxim,max77836-muic";
+	};
+
+	regulators {
+		compatible = "maxim,max77836-regulator";
+
+		SAFEOUT {
+			regulator-name = "SAFEOUT";
+		};
+		CHARGER {
+			regulator-name = "CHARGER";
+			regulator-min-microamp = <90000>;
+			regulator-max-microamp = <950000>;
+			regulator-boot-on;
+		};
+		LDO1 {
+			regulator-name = "LDO1";
+			regulator-min-microvolt = <2700000>;
+			regulator-max-microvolt = <2700000>;
+		};
+		LDO2 {
+			regulator-name = "LDO2";
+			regulator-min-microvolt = <800000>;
+			regulator-max-microvolt = <3950000>;
+		};
+	};
+
+	charger {
+		compatible = "maxim,max77836-charger";
+
+		maxim,fast-charge-timer = <6>;
+		maxim,constant-uvolt = <4350000>;
+		maxim,fast-charge-uamp = <225000>;
+		maxim,eoc-uamp = <7500>;
+		maxim,ovp-uvolt = <6500000>;
+	};
+};
-- 
1.7.9.5

^ permalink raw reply related

* Re: [Patch v5 1/2] dmaengine: add Qualcomm BAM dma driver
From: Vinod Koul @ 2014-02-17  8:54 UTC (permalink / raw)
  To: Andy Gross
  Cc: Dan Williams, dmaengine-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-arm-msm-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <20140211205852.GA10744-zC7DfRvBq/JWk0Htik3J/w@public.gmane.org>

On Tue, Feb 11, 2014 at 02:58:52PM -0600, Andy Gross wrote:
> On Tue, Feb 11, 2014 at 11:00:48PM +0530, Vinod Koul wrote:
> > On Tue, Feb 04, 2014 at 02:42:35PM -0600, Andy Gross wrote:
> > > Add the DMA engine driver for the QCOM Bus Access Manager (BAM) DMA controller
> > > found in the MSM 8x74 platforms.
> > > 
> 
> > > +/**
> > > + * bam_alloc_chan - Allocate channel resources for DMA channel.
> > > + * @chan: specified channel
> > > + *
> > > + * This function allocates the FIFO descriptor memory
> > > + */
> > > +static int bam_alloc_chan(struct dma_chan *chan)
> > > +{
> > > +	struct bam_chan *bchan = to_bam_chan(chan);
> > > +	struct bam_device *bdev = bchan->bdev;
> > > +
> > > +	/* allocate FIFO descriptor space, but only if necessary */
> > > +	if (!bchan->fifo_virt) {
> > > +		bchan->fifo_virt = dma_alloc_writecombine(bdev->dev,
> > > +					BAM_DESC_FIFO_SIZE, &bchan->fifo_phys,
> > > +					GFP_KERNEL);
> > > +
> > > +		if (!bchan->fifo_virt) {
> > > +			dev_err(bdev->dev, "Failed to allocate desc fifo\n");
> > > +			return -ENOMEM;
> > > +		}
> > > +	}
> > > +
> > > +	return BAM_DESC_FIFO_SIZE;
> > But you cna have SW descriptors more than HW ones and issue new ones once HW is
> > done with them. Why tie the limit to what HW supports and not create a better
> > driver?
> 
> Given that the virt-dma only allows me to have one outstanding transaction that
> consumes the fifo, and that I allocate descriptors from kzalloc, this would be
> as many as you can get until you go OOM.
well you can update virt-dma to queue up multiple descriptors to HW is thats
supported :)
> 
> The slave_sg() doesn't pull from a pool of descriptors.  It uses kzalloc.  So
> what value should I use for return value?  Most drivers use 1.
Lots do 0 as they dont allocate descriptor here, they allocate when the
.prep_xxx call gets invoked, which is what I suspect from you case too.

> 
> [....]
> 
> > > +static void bam_slave_config(struct bam_chan *bchan,
> > > +		struct dma_slave_config *cfg)
> > > +{
> > > +	struct bam_device *bdev = bchan->bdev;
> > > +	u32 maxburst;
> > > +
> > > +	if (bchan->slave.direction == DMA_DEV_TO_MEM)
> > > +		maxburst = bchan->slave.src_maxburst = cfg->src_maxburst;
> > > +	else
> > > +		maxburst = bchan->slave.dst_maxburst = cfg->dst_maxburst;
> > can you remove usage of slave.direction have save both. I am going to remove
> > this member...
> > 
> 
> Yes. no problem.
> 
> [....]
> 
> > > +
> > > +
> > > +	/* allocate enough room to accomodate the number of entries */
> > > +	async_desc = kzalloc(sizeof(*async_desc) +
> > > +			(sg_len * sizeof(struct bam_desc_hw)), GFP_NOWAIT);
> > this seems to assume that each sg entry length will not exceed the HW capablity.
> > Does engine support any length descriptor, if not you may want to split to
> > multiple.
> 
> Isn't this what the dma_set_max_seg_size supposed to fix?  The client is not
> supposed to send segments larger than the max segment you can take.  If that is
> true, then I have no issues.
Thats is true too, but do we want to limit this in driver ? This is more of a SW
limitation of who breaks up. for some like audio it makese sense but other not
too sure...

-- 
~Vinod
--
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 v4] bus: imx-weim: support CS GPR configuration
From: Philippe De Muyter @ 2014-02-17  8:52 UTC (permalink / raw)
  To: Shawn Guo
  Cc: Alexander Shiyan,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Huang Shijie,
	kernel-bIcnvbaLZ9MEGnE8C9+IrQ, devicetree-u79uwXL29TY76Z2rM5mHXA,
	Rob Herring, Mark Rutland
In-Reply-To: <20140216143114.GH2946-rvtDTF3kK1ictlrPMvKcciBecyulp+rMXqFh9Ls21Oc@public.gmane.org>

On Sun, Feb 16, 2014 at 10:31:16PM +0800, Shawn Guo wrote:
> On Sun, Feb 16, 2014 at 06:22:44PM +0400, Alexander Shiyan wrote:
> > Воскресенье, 16 февраля 2014, 22:03 +08:00 от Shawn Guo <shawn.guo-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>:
> > > For imx50-weim and imx6q-weim type of devices, there might a WEIM CS
> > > space configuration register in General Purpose Register controller,
> > > e.g. IOMUXC_GPR1 on i.MX6Q.
> > ...
> > > +static int __init imx_weim_gpr_setup(struct platform_device *pdev)
> > > +{
> > > +	struct device_node *np = pdev->dev.of_node;
> > > +	struct property *prop;
> > > +	const __be32 *p;
> > > +	struct regmap *gpr;
> > > +	u32 gprvals[4] = {
> > > +		05,	/* CS0(128M) CS1(0M)  CS2(0M)  CS3(0M) */
> > > +		033,	/* CS0(64M)  CS1(64M) CS2(0M)  CS3(0M) */
> > > +		0113,	/* CS0(64M)  CS1(32M) CS2(32M) CS3(0M) */
> > > +		01111,	/* CS0(64M)  CS1(32M) CS2(32M) CS3(0M) */
> > > +	};
> > > +	u32 gprval = 0;
> > > +	u32 val;
> > > +	int cs = 0;
> > > +	int i = 0;
> > > +
> > > +	gpr = syscon_regmap_lookup_by_phandle(np, "fsl,weim-cs-gpr");
> > > +	if (IS_ERR(gpr)) {
> > > +		dev_dbg(&pdev->dev, "failed to find weim-cs-gpr\n");
> > > +		return 0;
> > 
> > Only one comment:
> > You do not use these error codes in the probe(),
> > so let's declare this function as void.
> 
> Oh, yes.  I should check return of imx_weim_gpr_setup() in probe().
> Will add in the next version.

Well, as all the error cases are already covered by error messages, there is
no added value in testing that again in probe().  As Alexander wrote, you can
declare imx_weim_gpr_setup() as 'void'.

Philippe

-- 
Philippe De Muyter +32 2 6101532 Macq SA rue de l'Aeronef 2 B-1140 Bruxelles
--
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 v4] bus: imx-weim: support CS GPR configuration
From: Philippe De Muyter @ 2014-02-17  8:48 UTC (permalink / raw)
  To: Shawn Guo
  Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	Alexander Shiyan, Huang Shijie, kernel-bIcnvbaLZ9MEGnE8C9+IrQ,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Rob Herring, Mark Rutland
In-Reply-To: <1392559381-30842-1-git-send-email-shawn.guo-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>

On Sun, Feb 16, 2014 at 10:03:01PM +0800, Shawn Guo wrote:
> For imx50-weim and imx6q-weim type of devices, there might a WEIM CS
> space configuration register in General Purpose Register controller,
> e.g. IOMUXC_GPR1 on i.MX6Q.
> 
> Depending on which configuration of the following 4 is chosen for given
> system, IOMUXC_GPR1[11:0] should be set up as 05, 033, 0113 or 01111
> correspondingly.
> 
> 	CS0(128M) CS1(0M)  CS2(0M)  CS3(0M)
> 	CS0(64M)  CS1(64M) CS2(0M)  CS3(0M)
> 	CS0(64M)  CS1(32M) CS2(32M) CS3(0M)
> 	CS0(32M)  CS1(32M) CS2(32M) CS3(32M)
> 
> The patch creates a function for such type of devices, which scans
> 'ranges' property of WEIM node and build the GPR value incrementally.
> Thus the WEIM CS GPR can be set up automatically at boot time.
> 
> Signed-off-by: Shawn Guo <shawn.guo-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> ---
> Changes since v3:
>  - Add DT property fsl,weim-cs-gpr back, so that the support can work
>    for more i.MX SoCs than just i.MX6Q.
> 
>  Documentation/devicetree/bindings/bus/imx-weim.txt |   27 +++++++++-
>  drivers/bus/imx-weim.c                             |   55 ++++++++++++++++++++
>  2 files changed, 81 insertions(+), 1 deletion(-)
> 
> diff --git a/Documentation/devicetree/bindings/bus/imx-weim.txt b/Documentation/devicetree/bindings/bus/imx-weim.txt
> index 0fd76c4..363c970 100644
> --- a/Documentation/devicetree/bindings/bus/imx-weim.txt
> +++ b/Documentation/devicetree/bindings/bus/imx-weim.txt
> @@ -8,7 +8,12 @@ The actual devices are instantiated from the child nodes of a WEIM node.
>  
>  Required properties:
>  
> - - compatible:		Should be set to "fsl,<soc>-weim"
> + - compatible:		Should contain one of the following:
> +			  "fsl,imx1-weim"
> +			  "fsl,imx27-weim"
> +			  "fsl,imx51-weim"
> +			  "fsl,imx50-weim"
> +			  "fsl,imx6q-weim"
>   - reg:			A resource specifier for the register space
>  			(see the example below)
>   - clocks:		the clock, see the example below.
> @@ -19,6 +24,26 @@ Required properties:
>  
>  			   <cs-number> 0 <physical address of mapping> <size>
>  
> +Optional properties:
> +
> + - fsl,weim-cs-gpr:	For "fsl,imx50-weim" and "fsl,imx6q-weim" type of
> +			devices, it should be the phandle to the system General
> +			Purpose Register controller that contains WEIM CS GPR
> +			register, e.g. IOMUXC_GPR1 on i.MX6Q.  IOMUXC_GPR1[11:0]
> +			should be set up as one of the following 4 possible
> +			values depending on the CS space configuration.
> +
> +			IOMUXC_GPR1[11:0]    CS0    CS1    CS2    CS3
> +			---------------------------------------------
> +				05	    128M     0M     0M     0M
> +				033          64M    64M     0M     0M
> +				0113         64M    32M    32M     0M
> +				01111        32M    32M    32M    32M
> +
> +			In case that the property is absent, the reset value or
> +			what bootloader sets up in IOMUXC_GPR1[11:0] will be
> +			used.
> +
>  Timing property for child nodes. It is mandatory, not optional.
>  
>   - fsl,weim-cs-timing:	The timing array, contains timing values for the

Could you also add the following line to the example in the same file :

		fsl,weim-cs-gpr = <&gpr>;

It took me some time to figure it out :)

> diff --git a/drivers/bus/imx-weim.c b/drivers/bus/imx-weim.c
> index 3ef58c8..35e9529 100644
> --- a/drivers/bus/imx-weim.c
> +++ b/drivers/bus/imx-weim.c
> @@ -11,6 +11,9 @@
>  #include <linux/clk.h>
>  #include <linux/io.h>
>  #include <linux/of_device.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
> +#include <linux/regmap.h>
>  
>  struct imx_weim_devtype {
>  	unsigned int	cs_count;
> @@ -56,6 +59,55 @@ static const struct of_device_id weim_id_table[] = {
>  };
>  MODULE_DEVICE_TABLE(of, weim_id_table);
>  
> +static int __init imx_weim_gpr_setup(struct platform_device *pdev)
> +{
> +	struct device_node *np = pdev->dev.of_node;
> +	struct property *prop;
> +	const __be32 *p;
> +	struct regmap *gpr;
> +	u32 gprvals[4] = {
> +		05,	/* CS0(128M) CS1(0M)  CS2(0M)  CS3(0M) */
> +		033,	/* CS0(64M)  CS1(64M) CS2(0M)  CS3(0M) */
> +		0113,	/* CS0(64M)  CS1(32M) CS2(32M) CS3(0M) */
> +		01111,	/* CS0(64M)  CS1(32M) CS2(32M) CS3(0M) */

			   CS0(32M)			CS3(32M)

> +	};
> +	u32 gprval = 0;
> +	u32 val;
> +	int cs = 0;
> +	int i = 0;
> +
> +	gpr = syscon_regmap_lookup_by_phandle(np, "fsl,weim-cs-gpr");
> +	if (IS_ERR(gpr)) {
> +		dev_dbg(&pdev->dev, "failed to find weim-cs-gpr\n");
> +		return 0;
> +	}
> +
> +	of_property_for_each_u32(np, "ranges", prop, p, val) {
> +		if (i % 4 == 0) {
> +			cs = val;
> +		} else if (i % 4 == 3 && val) {
> +			val = (val / SZ_32M) | 1;
> +			gprval |= val << cs * 3;
> +		}
> +		i++;
> +	}
> +
> +	if (i == 0 || i % 4)
> +		goto err;
> +
> +	for (i = 0; i < ARRAY_SIZE(gprvals); i++) {
> +		if (gprval == gprvals[i]) {
> +			/* Found it. Set up IOMUXC_GPR1[11:0] with it. */
> +			regmap_update_bits(gpr, IOMUXC_GPR1, 0xfff, gprval);
> +			return 0;
> +		}
> +	}
> +
> +err:
> +	dev_err(&pdev->dev, "Invalid 'ranges' configuration\n");
> +	return -EINVAL;
> +}
> +
>  /* Parse and set the timing for this device. */
>  static int __init weim_timing_setup(struct device_node *np, void __iomem *base,
>  				    const struct imx_weim_devtype *devtype)
> @@ -92,6 +144,9 @@ static int __init weim_parse_dt(struct platform_device *pdev,
>  	struct device_node *child;
>  	int ret;
>  
> +	if (devtype == &imx50_weim_devtype)
> +		imx_weim_gpr_setup(pdev);
> +
>  	for_each_child_of_node(pdev->dev.of_node, child) {
>  		if (!child->name)
>  			continue;
> -- 
> 1.7.9.5
> 

-- 
Philippe De Muyter +32 2 6101532 Macq SA rue de l'Aeronef 2 B-1140 Bruxelles
--
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: Vinod Koul @ 2014-02-17  8:43 UTC (permalink / raw)
  To: Srikanth Thokala
  Cc: dan.j.williams, michal.simek, grant.likely, robh+dt, levex, lars,
	andriy.shevchenko, dmaengine, linux-arm-kernel, linux-kernel,
	devicetree
In-Reply-To: <1392465619-27614-2-git-send-email-sthokal@xilinx.com>

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?

-- 
~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

-- 

^ permalink raw reply

* [PATCH v3 11/13] Documentation: mfd: s2mps11: Document support for S2MPS14
From: Krzysztof Kozlowski @ 2014-02-17  8:40 UTC (permalink / raw)
  To: Sangbeom Kim, Samuel Ortiz, Lee Jones, linux-kernel,
	linux-samsung-soc
  Cc: Kyungmin Park, Marek Szyprowski, Bartlomiej Zolnierkiewicz,
	Tomasz Figa, Yadwinder Singh Brar, Krzysztof Kozlowski,
	Mark Brown, Liam Girdwood, devicetree, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala
In-Reply-To: <1392626427-4518-1-git-send-email-k.kozlowski@samsung.com>

Add bindings documentation for S2MPS14 device to the s2mps11 driver.

Signed-off-by: Krzysztof Kozlowski <k.kozlowski@samsung.com>
Cc: Mark Brown <broonie@kernel.org>
Cc: Liam Girdwood <lgirdwood@gmail.com>
Cc: Tomasz Figa <t.figa@samsung.com>
Cc: devicetree@vger.kernel.org
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>
Acked-by: Tomasz Figa <t.figa@samsung.com>
---
 Documentation/devicetree/bindings/mfd/s2mps11.txt |   12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/Documentation/devicetree/bindings/mfd/s2mps11.txt b/Documentation/devicetree/bindings/mfd/s2mps11.txt
index 15ee89c3cc7b..f69bec294f02 100644
--- a/Documentation/devicetree/bindings/mfd/s2mps11.txt
+++ b/Documentation/devicetree/bindings/mfd/s2mps11.txt
@@ -1,5 +1,5 @@
 
-* Samsung S2MPS11 Voltage and Current Regulator
+* Samsung S2MPS11 and S2MPS14 Voltage and Current Regulator
 
 The Samsung S2MPS11 is a multi-function device which includes voltage and
 current regulators, RTC, charger controller and other sub-blocks. It is
@@ -7,7 +7,7 @@ interfaced to the host controller using an I2C interface. Each sub-block is
 addressed by the host system using different I2C slave addresses.
 
 Required properties:
-- compatible: Should be "samsung,s2mps11-pmic".
+- compatible: Should be "samsung,s2mps11-pmic" or "samsung,s2mps14-pmic".
 - reg: Specifies the I2C slave address of the pmic block. It should be 0x66.
 
 Optional properties:
@@ -59,10 +59,14 @@ supports. Note: The 'n' in LDOn and BUCKn represents the LDO or BUCK number
 as per the datasheet of s2mps11.
 
 	- LDOn
-		  - valid values for n are 1 to 38
+		  - valid values for n are:
+			- S2MPS11: 1 to 38
+			- S2MPS14: 1 to 25
 		  - Example: LDO1, LD02, LDO28
 	- BUCKn
-		  - valid values for n are 1 to 10.
+		  - valid values for n are:
+			- S2MPS11: 1 to 10
+			- S2MPS14: 1 to 5
 		  - Example: BUCK1, BUCK2, BUCK9
 
 Example:
-- 
1.7.9.5

^ permalink raw reply related

* Re: [PATCH 2/2] ARM: tegra: fix Dalmore PMIC IRQ polarity
From: Laxman Dewangan @ 2014-02-17  8:32 UTC (permalink / raw)
  To: Stephen Warren, Samuel Ortiz, Lee Jones
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, J Keerthy,
	Ian Lartey, Stefan Agner, Joseph Lo, Stephen Warren
In-Reply-To: <1392415108-4365-2-git-send-email-swarren-3lzwWm7+Weoh9ZMKESR00Q@public.gmane.org>

On Saturday 15 February 2014 03:28 AM, Stephen Warren wrote:
> From: Stephen Warren <swarren-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
>
> The Tegra PMC's resume-from-sleep logic wants an active-low IRQ input
> from the PMIC. However, the PMIC IRQ is also routed to the GIC, which
> only supports active high IRQs (or rising edge). Hence, the signal must
> be inverted in the PMC before being routed to the GIC. This implies that
> the PMC DT property nvidia,invert-interrupt must be set, and it is.
>
> The PMIC's DT interrupts property must represent the IRQ level at the
> GIC, since that is the PMIC's parent IRQ controller. Fix the PMIC's
> interrupts property to correctly describe the GIC input polarity.
>
> However, the PMIC IRQ output's polarity is programmable in HW, and by
> default follows the parent IRQ controller's input polarity. We need to
> have an active-low output due to the inversion inside the Tegra PMC.
> Hence, add the ti,irq-externally-inverted property to the PMIC.
>
Looks good to me.

Acked-by: Laxman Dewangan <ldewangan-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>

^ permalink raw reply

* Re: [PATCH 1/2] mfd: palmas: support IRQ inversion at the board level
From: Laxman Dewangan @ 2014-02-17  8:31 UTC (permalink / raw)
  To: Stephen Warren, Samuel Ortiz, Lee Jones
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, J Keerthy,
	Ian Lartey, Stefan Agner, Joseph Lo, Stephen Warren
In-Reply-To: <1392415108-4365-1-git-send-email-swarren-3lzwWm7+Weoh9ZMKESR00Q@public.gmane.org>

On Saturday 15 February 2014 03:28 AM, Stephen Warren wrote:
> From: Stephen Warren <swarren-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
>
> Some boards or SoCs have an inverter between the PMIC IRQ output pin and
> the IRQ controller input signal.
>
> The IRQ specifier in DT is meant to represent the IRQ flags at the input
> to the IRQ controller.
>

Looks good to me.
Acked-by: Laxman Dewangan <ldewangan-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>

^ permalink raw reply

* Re: [RFC PATCH] i2c: new bus driver for efm32
From: Uwe Kleine-König @ 2014-02-17  8:27 UTC (permalink / raw)
  To: linux-i2c-u79uwXL29TY76Z2rM5mHXA, Wolfram Sang
  Cc: kernel-bIcnvbaLZ9MEGnE8C9+IrQ,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1392027598-29015-1-git-send-email-u.kleine-koenig-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>

On Mon, Feb 10, 2014 at 11:19:58AM +0100, Uwe Kleine-König wrote:
> This was tested on a EFM32GG-DK3750 devboard that has a temperature
> sensor and an eeprom on its i2c bus.
> 
> Signed-off-by: Uwe Kleine-König <u.kleine-koenig-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
ping

Thanks
Uwe

> ---
> Hello,
> 
> there are two places marked with a triple-X where I don't know if I did
> it right. The one is "Do I need to protect from .master_xfer being
> called again before the previous call returned?" The other is what to
> return in the .functionality callback.
> 
> Also note there is a matching entry in MAINTAINERS for "ARM/ENERGY MICRO
> (SILICON LABS) EFM32 SUPPORT".
> 
> Best regards
> Uwe
> 
>  drivers/i2c/busses/Kconfig              |   7 +
>  drivers/i2c/busses/Makefile             |   1 +
>  drivers/i2c/busses/i2c-efm32.c          | 527 ++++++++++++++++++++++++++++++++
>  include/linux/platform_data/efm32-i2c.h |  15 +
>  4 files changed, 550 insertions(+)
>  create mode 100644 drivers/i2c/busses/i2c-efm32.c
>  create mode 100644 include/linux/platform_data/efm32-i2c.h
> 
> diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
> index f5ed03164d86..7322ff0c4c60 100644
> --- a/drivers/i2c/busses/Kconfig
> +++ b/drivers/i2c/busses/Kconfig
> @@ -432,6 +432,13 @@ config I2C_DESIGNWARE_PCI
>  	  This driver can also be built as a module.  If so, the module
>  	  will be called i2c-designware-pci.
>  
> +config I2C_EFM32
> +	tristate "EFM32 I2C controller"
> +	depends on OF && (ARCH_EFM32 || COMPILE_TEST)
> +	help
> +	  This driver supports the i2c block found in Energy Micro's EFM32
> +	  SoCs.
> +
>  config I2C_EG20T
>  	tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) I2C"
>  	depends on PCI
> diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
> index a08931fe73e1..2a56ab181851 100644
> --- a/drivers/i2c/busses/Makefile
> +++ b/drivers/i2c/busses/Makefile
> @@ -41,6 +41,7 @@ obj-$(CONFIG_I2C_DESIGNWARE_PLATFORM)	+= i2c-designware-platform.o
>  i2c-designware-platform-objs := i2c-designware-platdrv.o
>  obj-$(CONFIG_I2C_DESIGNWARE_PCI)	+= i2c-designware-pci.o
>  i2c-designware-pci-objs := i2c-designware-pcidrv.o
> +obj-$(CONFIG_I2C_EFM32)		+= i2c-efm32.o
>  obj-$(CONFIG_I2C_EG20T)		+= i2c-eg20t.o
>  obj-$(CONFIG_I2C_EXYNOS5)	+= i2c-exynos5.o
>  obj-$(CONFIG_I2C_GPIO)		+= i2c-gpio.o
> diff --git a/drivers/i2c/busses/i2c-efm32.c b/drivers/i2c/busses/i2c-efm32.c
> new file mode 100644
> index 000000000000..9e860abcab63
> --- /dev/null
> +++ b/drivers/i2c/busses/i2c-efm32.c
> @@ -0,0 +1,527 @@
> +/*
> + * Copyright (C) 2014 Uwe Kleine-Koenig for Pengutronix
> + *
> + * 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.
> + */
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/i2c.h>
> +#include <linux/io.h>
> +#include <linux/interrupt.h>
> +#include <linux/err.h>
> +#include <linux/clk.h>
> +#include <linux/platform_data/efm32-i2c.h>
> +
> +#define DRIVER_NAME "efm32-i2c"
> +
> +#define MASK_VAL(mask, val)		((val << __ffs(mask)) & mask)
> +
> +#define REG_CTRL		0x00
> +#define REG_CTRL_EN			0x00001
> +#define REG_CTRL_SLAVE			0x00002
> +#define REG_CTRL_AUTOACK		0x00004
> +#define REG_CTRL_AUTOSE			0x00008
> +#define REG_CTRL_AUTOSN			0x00010
> +#define REG_CTRL_ARBDIS			0x00020
> +#define REG_CTRL_GCAMEN			0x00040
> +#define REG_CTRL_CLHR__MASK		0x00300
> +#define REG_CTRL_BITO__MASK		0x03000
> +#define REG_CTRL_BITO_OFF		0x00000
> +#define REG_CTRL_BITO_40PCC		0x01000
> +#define REG_CTRL_BITO_80PCC		0x02000
> +#define REG_CTRL_BITO_160PCC		0x03000
> +#define REG_CTRL_GIBITO			0x08000
> +#define REG_CTRL_CLTO__MASK		0x70000
> +#define REG_CTRL_CLTO_OFF		0x00000
> +
> +#define REG_CMD			0x04
> +#define REG_CMD_START			0x00001
> +#define REG_CMD_STOP			0x00002
> +#define REG_CMD_ACK			0x00004
> +#define REG_CMD_NACK			0x00008
> +#define REG_CMD_CONT			0x00010
> +#define REG_CMD_ABORT			0x00020
> +#define REG_CMD_CLEARTX			0x00040
> +#define REG_CMD_CLEARPC			0x00080
> +
> +#define REG_STATE		0x08
> +#define REG_STATE_BUSY			0x00001
> +#define REG_STATE_MASTER		0x00002
> +#define REG_STATE_TRANSMITTER		0x00004
> +#define REG_STATE_NACKED		0x00008
> +#define REG_STATE_BUSHOLD		0x00010
> +#define REG_STATE_STATE__MASK		0x000e0
> +#define REG_STATE_STATE_IDLE		0x00000
> +#define REG_STATE_STATE_WAIT		0x00020
> +#define REG_STATE_STATE_START		0x00040
> +#define REG_STATE_STATE_ADDR		0x00060
> +#define REG_STATE_STATE_ADDRACK		0x00080
> +#define REG_STATE_STATE_DATA		0x000a0
> +#define REG_STATE_STATE_DATAACK		0x000c0
> +
> +#define REG_STATUS		0x0c
> +#define REG_STATUS_PSTART		0x00001
> +#define REG_STATUS_PSTOP		0x00002
> +#define REG_STATUS_PACK			0x00004
> +#define REG_STATUS_PNACK		0x00008
> +#define REG_STATUS_PCONT		0x00010
> +#define REG_STATUS_PABORT		0x00020
> +#define REG_STATUS_TXC			0x00040
> +#define REG_STATUS_TXBL			0x00080
> +#define REG_STATUS_RXDATAV		0x00100
> +
> +#define REG_CLKDIV		0x10
> +#define REG_CLKDIV_DIV__MASK		0x001ff
> +#define REG_CLKDIV_DIV(div)		MASK_VAL(REG_CLKDIV_DIV__MASK, (div))
> +
> +#define REG_SADDR		0x14
> +#define REG_SADDRMASK		0x18
> +#define REG_RXDATA		0x1c
> +#define REG_RXDATAP		0x20
> +#define REG_TXDATA		0x24
> +#define REG_IF			0x28
> +#define REG_IF_START			0x00001
> +#define REG_IF_RSTART			0x00002
> +#define REG_IF_ADDR			0x00004
> +#define REG_IF_TXC			0x00008
> +#define REG_IF_TXBL			0x00010
> +#define REG_IF_RXDATAV			0x00020
> +#define REG_IF_ACK			0x00040
> +#define REG_IF_NACK			0x00080
> +#define REG_IF_MSTOP			0x00100
> +#define REG_IF_ARBLOST			0x00200
> +#define REG_IF_BUSERR			0x00400
> +#define REG_IF_BUSHOLD			0x00800
> +#define REG_IF_TXOF			0x01000
> +#define REG_IF_RXUF			0x02000
> +#define REG_IF_BITO			0x04000
> +#define REG_IF_CLTO			0x08000
> +#define REG_IF_SSTOP			0x10000
> +
> +#define REG_IFS			0x2c
> +#define REG_IFC			0x30
> +#define REG_IFC__MASK			0x1ffcf
> +
> +#define REG_IEN			0x34
> +
> +#define REG_ROUTE		0x38
> +#define REG_ROUTE_SDAPEN		0x00001
> +#define REG_ROUTE_SCLPEN		0x00002
> +#define REG_ROUTE_LOCATION__MASK	0x00700
> +#define REG_ROUTE_LOCATION(n)		MASK_VAL(REG_ROUTE_LOCATION__MASK, (n))
> +
> +struct efm32_i2c_ddata {
> +	struct i2c_adapter adapter;
> +	spinlock_t lock;
> +
> +	struct clk *clk;
> +	void __iomem *base;
> +	unsigned int irq;
> +	struct efm32_i2c_pdata pdata;
> +
> +	/* transfer data */
> +	struct completion done;
> +	struct i2c_msg *msgs;
> +	size_t num_msgs;
> +	size_t current_word, current_msg;
> +};
> +
> +static u32 efm32_i2c_read32(struct efm32_i2c_ddata *ddata, unsigned offset)
> +{
> +	return readl(ddata->base + offset);
> +}
> +
> +static void efm32_i2c_write32(struct efm32_i2c_ddata *ddata,
> +		unsigned offset, u32 value)
> +{
> +	writel(value, ddata->base + offset);
> +}
> +
> +static void efm32_i2c_send_next_msg(struct efm32_i2c_ddata *ddata)
> +{
> +	struct i2c_msg *cur_msg = &ddata->msgs[ddata->current_msg];
> +
> +	dev_dbg(&ddata->adapter.dev, "send msg %zu/%zu (addr = %x, flags = %x, if = %08x)\n",
> +			ddata->current_msg, ddata->num_msgs, cur_msg->addr,
> +			cur_msg->flags, efm32_i2c_read32(ddata, REG_IF));
> +	efm32_i2c_write32(ddata, REG_CMD, REG_CMD_START);
> +	efm32_i2c_write32(ddata, REG_TXDATA, cur_msg->addr << 1 |
> +			(cur_msg->flags & I2C_M_RD ? 1 : 0));
> +}
> +
> +static void efm32_i2c_send_next_byte(struct efm32_i2c_ddata *ddata)
> +{
> +	struct i2c_msg *cur_msg = &ddata->msgs[ddata->current_msg];
> +	dev_dbg(&ddata->adapter.dev, "%s, %zu %zu\n",
> +			__func__, ddata->current_word, cur_msg->len);
> +	if (ddata->current_word >= cur_msg->len) {
> +		/* cur_msg completely transferred */
> +		ddata->current_word = 0;
> +		ddata->current_msg += 1;
> +
> +		if (ddata->current_msg >= ddata->num_msgs) {
> +			dev_dbg(&ddata->adapter.dev, "Stop\n");
> +			efm32_i2c_write32(ddata, REG_CMD, REG_CMD_STOP);
> +			complete(&ddata->done);
> +		} else {
> +			efm32_i2c_send_next_msg(ddata);
> +		}
> +	} else {
> +		dev_dbg(&ddata->adapter.dev, "send byte %zu/%zu\n",
> +				ddata->current_word, cur_msg->len);
> +		efm32_i2c_write32(ddata, REG_TXDATA,
> +				cur_msg->buf[ddata->current_word++]);
> +	}
> +}
> +
> +static void efm32_i2c_recv_next_byte(struct efm32_i2c_ddata *ddata)
> +{
> +	struct i2c_msg *cur_msg = &ddata->msgs[ddata->current_msg];
> +
> +	cur_msg->buf[ddata->current_word] = efm32_i2c_read32(ddata, REG_RXDATA);
> +	dev_dbg(&ddata->adapter.dev, "recv byte %zu/%zu: 0x%02hhx\n",
> +			ddata->current_word, cur_msg->len, cur_msg->buf[ddata->current_word]);
> +	ddata->current_word += 1;
> +	if (ddata->current_word >= cur_msg->len) {
> +		/* cur_msg completely transferred */
> +		ddata->current_word = 0;
> +		ddata->current_msg += 1;
> +
> +		efm32_i2c_write32(ddata, REG_CMD, REG_CMD_NACK);
> +
> +		if (ddata->current_msg >= ddata->num_msgs) {
> +			efm32_i2c_write32(ddata, REG_CMD, REG_CMD_STOP);
> +			complete(&ddata->done);
> +		} else {
> +			efm32_i2c_send_next_msg(ddata);
> +		}
> +	} else {
> +		efm32_i2c_write32(ddata, REG_CMD, REG_CMD_ACK);
> +	}
> +}
> +
> +static irqreturn_t efm32_i2c_irq(int irq, void *dev_id)
> +{
> +	struct efm32_i2c_ddata *ddata = dev_id;
> +	struct i2c_msg *cur_msg = &ddata->msgs[ddata->current_msg];
> +	u32 irqflag = efm32_i2c_read32(ddata, REG_IF);
> +	u32 state = efm32_i2c_read32(ddata, REG_STATE);
> +
> +	dev_dbg(&ddata->adapter.dev, "irq: if: %08x, state: %08x, status: %08x\n",
> +			irqflag, state, efm32_i2c_read32(ddata, REG_STATUS));
> +	efm32_i2c_write32(ddata, REG_IFC, irqflag & REG_IFC__MASK);
> +
> +	switch (state & REG_STATE_STATE__MASK) {
> +	case REG_STATE_STATE_IDLE:
> +		/* arbitration lost? */
> +		complete(&ddata->done);
> +		break;
> +	case REG_STATE_STATE_WAIT:
> +		/* huh, this shouldn't happen */
> +		BUG();
> +		break;
> +	case REG_STATE_STATE_START:
> +		/* "caller" is expected to send an address */
> +		break;
> +	case REG_STATE_STATE_ADDR:
> +		/* wait for Ack or NAck of slave */
> +		break;
> +	case REG_STATE_STATE_ADDRACK:
> +		if (state & REG_STATE_NACKED) {
> +			efm32_i2c_write32(ddata, REG_CMD, REG_CMD_STOP);
> +			complete(&ddata->done);
> +		} else if (cur_msg->flags & I2C_M_RD) {
> +			/* wait for slave to send first data byte */
> +		} else {
> +			efm32_i2c_send_next_byte(ddata);
> +		}
> +		break;
> +	case REG_STATE_STATE_DATA:
> +		if (cur_msg->flags & I2C_M_RD) {
> +			efm32_i2c_recv_next_byte(ddata);
> +		} else {
> +			/* wait for Ack or Nack of slave */
> +		}
> +		break;
> +	case REG_STATE_STATE_DATAACK:
> +		if (state & REG_STATE_NACKED) {
> +			efm32_i2c_write32(ddata, REG_CMD, REG_CMD_STOP);
> +			complete(&ddata->done);
> +		} else {
> +			efm32_i2c_send_next_byte(ddata);
> +		}
> +	}
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int efm32_i2c_master_xfer(struct i2c_adapter *adap,
> +		struct i2c_msg *msgs, int num)
> +{
> +	struct efm32_i2c_ddata *ddata = i2c_get_adapdata(adap);
> +	int ret = -EBUSY;
> +
> +	spin_lock_irq(&ddata->lock);
> +
> +	if (ddata->msgs)
> +		/*
> +		 * XXX: can .master_xfer be called when the previous is still
> +		 * running?
> +		 */
> +		goto out_unlock;
> +
> +	ddata->msgs = msgs;
> +	ddata->num_msgs = num;
> +	ddata->current_word = 0;
> +	ddata->current_msg = 0;
> +
> +	init_completion(&ddata->done);
> +
> +	dev_dbg(&ddata->adapter.dev, "state: %08x, status: %08x\n",
> +			efm32_i2c_read32(ddata, REG_STATE),
> +			efm32_i2c_read32(ddata, REG_STATUS));
> +
> +	efm32_i2c_send_next_msg(ddata);
> +
> +	spin_unlock_irq(&ddata->lock);
> +
> +	wait_for_completion(&ddata->done);
> +
> +	spin_lock_irq(&ddata->lock);
> +
> +	if (ddata->current_msg >= ddata->num_msgs)
> +		ret = ddata->num_msgs;
> +	else
> +		ret = -EIO;
> +
> +	ddata->msgs = NULL;
> +
> +out_unlock:
> +	spin_unlock_irq(&ddata->lock);
> +	return ret;
> +}
> +
> +static u32 efm32_i2c_functionality(struct i2c_adapter *adap)
> +{
> +	/* XXX: some more? */
> +	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
> +}
> +
> +static const struct i2c_algorithm efm32_i2c_algo = {
> +	.master_xfer = efm32_i2c_master_xfer,
> +	.functionality = efm32_i2c_functionality,
> +};
> +
> +static u32 efm32_i2c_get_configured_location(struct efm32_i2c_ddata *ddata)
> +{
> +	u32 reg = efm32_i2c_read32(ddata, REG_ROUTE);
> +
> +	return (reg & REG_ROUTE_LOCATION__MASK) >>
> +		__ffs(REG_ROUTE_LOCATION__MASK);
> +}
> +
> +static int efm32_i2c_probe_dt(struct platform_device *pdev,
> +		struct efm32_i2c_ddata *ddata)
> +{
> +	struct device_node *np = pdev->dev.of_node;
> +	u32 location, frequency;
> +	int ret;
> +
> +	if (!np)
> +		return 1;
> +
> +	ret = of_property_read_u32(np, "location", &location);
> +	if (!ret) {
> +		dev_dbg(&pdev->dev, "using location %u\n", location);
> +	} else {
> +		/* default to location configured in hardware */
> +		location = efm32_i2c_get_configured_location(ddata);
> +
> +		dev_info(&pdev->dev, "fall back to location %u\n", location);
> +	}
> +
> +	ddata->pdata.location = location;
> +
> +	ret = of_property_read_u32(np, "clock-frequency", &frequency);
> +	if (!ret) {
> +		dev_dbg(&pdev->dev, "using frequency %u\n", frequency);
> +	} else {
> +		frequency = 100000;
> +		dev_info(&pdev->dev, "defaulting to 100 kHz\n");
> +	}
> +	ddata->pdata.frequency = frequency;
> +
> +	/* i2c core takes care about bus numbering using an alias */
> +	ddata->adapter.nr = -1;
> +
> +	return 0;
> +}
> +
> +static int efm32_i2c_probe(struct platform_device *pdev)
> +{
> +	struct efm32_i2c_ddata *ddata;
> +	struct resource *res;
> +	unsigned long rate;
> +	int ret;
> +	u32 clkdiv;
> +
> +	ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
> +	if (!ddata) {
> +		dev_dbg(&pdev->dev, "failed to allocate private data\n");
> +		return -ENOMEM;
> +	}
> +	platform_set_drvdata(pdev, ddata);
> +
> +	strlcpy(ddata->adapter.name, pdev->name, sizeof(ddata->adapter.name));
> +	ddata->adapter.owner = THIS_MODULE;
> +	ddata->adapter.algo = &efm32_i2c_algo;
> +	ddata->adapter.dev.parent = &pdev->dev;
> +	ddata->adapter.dev.of_node = pdev->dev.of_node;
> +	i2c_set_adapdata(&ddata->adapter, ddata);
> +
> +	spin_lock_init(&ddata->lock);
> +
> +	ddata->clk = devm_clk_get(&pdev->dev, NULL);
> +	if (IS_ERR(ddata->clk)) {
> +		ret = PTR_ERR(ddata->clk);
> +		dev_err(&pdev->dev, "failed to get clock: %d\n", ret);
> +		return ret;
> +	}
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res) {
> +		dev_err(&pdev->dev, "failed to determine base address\n");
> +		return -ENODEV;
> +	}
> +
> +	if (resource_size(res) < 0x42) {
> +		dev_err(&pdev->dev, "memory resource too small\n");
> +		return -EINVAL;
> +	}
> +
> +	ddata->base = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(ddata->base))
> +		return PTR_ERR(ddata->base);
> +
> +	ret = platform_get_irq(pdev, 0);
> +	if (ret <= 0) {
> +		dev_err(&pdev->dev, "failed to get irq (%d)\n", ret);
> +		if (!ret)
> +			ret = -EINVAL;
> +		return ret;
> +	}
> +
> +	ddata->irq = ret;
> +
> +	ret = clk_prepare_enable(ddata->clk);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "failed to enable clock (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	ret = efm32_i2c_probe_dt(pdev, ddata);
> +	if (ret > 0) {
> +		/* not created by device tree */
> +		const struct efm32_i2c_pdata *pdata =
> +			dev_get_platdata(&pdev->dev);
> +
> +		if (pdata)
> +			ddata->pdata = *pdata;
> +		else {
> +			ddata->pdata.location =
> +				efm32_i2c_get_configured_location(ddata);
> +			ddata->pdata.frequency = 100000;
> +		}
> +
> +		ddata->adapter.nr = pdev->id;
> +	} else if (ret < 0) {
> +		goto err_disable_clk;
> +	}
> +
> +	rate = clk_get_rate(ddata->clk);
> +	if (!rate) {
> +		dev_err(&pdev->dev, "there is no input clock available\n");
> +		ret = -EIO;
> +		goto err_disable_clk;
> +	}
> +	clkdiv = DIV_ROUND_UP(rate, 8 * ddata->pdata.frequency) - 1;
> +	if (clkdiv >= 0x200) {
> +		dev_err(&pdev->dev,
> +				"input clock too fast (%lu) to divide down to bus freq (%lu)",
> +				rate, ddata->pdata.frequency);
> +		ret = -EIO;
> +		goto err_disable_clk;
> +	}
> +
> +	dev_dbg(&pdev->dev, "input clock = %lu, bus freq = %lu, clkdiv = %lu\n",
> +			rate, ddata->pdata.frequency, (unsigned long)clkdiv);
> +	efm32_i2c_write32(ddata, REG_CLKDIV, REG_CLKDIV_DIV(clkdiv));
> +
> +	efm32_i2c_write32(ddata, REG_ROUTE, REG_ROUTE_SDAPEN |
> +			REG_ROUTE_SCLPEN |
> +			REG_ROUTE_LOCATION(ddata->pdata.location));
> +
> +	efm32_i2c_write32(ddata, REG_CTRL, REG_CTRL_EN |
> +			REG_CTRL_BITO_160PCC | 0 * REG_CTRL_GIBITO);
> +
> +	efm32_i2c_write32(ddata, REG_IFC, REG_IFC__MASK);
> +	efm32_i2c_write32(ddata, REG_IEN, REG_IF_TXC | REG_IF_ACK | REG_IF_NACK
> +			| REG_IF_ARBLOST | REG_IF_BUSERR | REG_IF_RXDATAV);
> +
> +	/* to make bus idle */
> +	efm32_i2c_write32(ddata, REG_CMD, REG_CMD_ABORT);
> +
> +	ret = request_irq(ddata->irq, efm32_i2c_irq, 0, DRIVER_NAME, ddata);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "failed to request irq (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	ret = i2c_add_numbered_adapter(&ddata->adapter);
> +	if (ret) {
> +		dev_err(&pdev->dev, "failed to add i2c adapter (%d)\n", ret);
> +		free_irq(ddata->irq, ddata);
> +
> +err_disable_clk:
> +		clk_disable_unprepare(ddata->clk);
> +	}
> +	return ret;
> +}
> +
> +static int efm32_i2c_remove(struct platform_device *pdev)
> +{
> +	struct efm32_i2c_ddata *ddata = platform_get_drvdata(pdev);
> +
> +	free_irq(ddata->irq, ddata);
> +	clk_disable_unprepare(ddata->clk);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id efm32_i2c_dt_ids[] = {
> +	{
> +		.compatible = "efm32,i2c",
> +	}, {
> +		/* sentinel */
> +	}
> +};
> +MODULE_DEVICE_TABLE(of, efm32_i2c_dt_ids);
> +
> +static struct platform_driver efm32_i2c_driver = {
> +	.probe = efm32_i2c_probe,
> +	.remove = efm32_i2c_remove,
> +
> +	.driver = {
> +		.name = DRIVER_NAME,
> +		.owner = THIS_MODULE,
> +		.of_match_table = efm32_i2c_dt_ids,
> +	},
> +};
> +module_platform_driver(efm32_i2c_driver);
> +
> +MODULE_AUTHOR("Uwe Kleine-Koenig <u.kleine-koenig-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>");
> +MODULE_DESCRIPTION("EFM32 i2c driver");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:" DRIVER_NAME);
> diff --git a/include/linux/platform_data/efm32-i2c.h b/include/linux/platform_data/efm32-i2c.h
> new file mode 100644
> index 000000000000..5e175db68bb6
> --- /dev/null
> +++ b/include/linux/platform_data/efm32-i2c.h
> @@ -0,0 +1,15 @@
> +#ifndef __LINUX_PLATFORM_DATA_EFM32_I2C_H__
> +#define __LINUX_PLATFORM_DATA_EFM32_I2C_H__
> +
> +#include <linux/types.h>
> +
> +/**
> + * struct efm32_i2c_pdata
> + * @location: pinmux location for the I/O pins (to be written to the ROUTE
> + * 	register)
> + */
> +struct efm32_i2c_pdata {
> +	u8 location;
> +	unsigned long frequency; /* in Hz */
> +};
> +#endif /* ifndef __LINUX_PLATFORM_DATA_EFM32_I2C_H__ */
> -- 
> 1.8.5.2
> 
> 

-- 
Pengutronix e.K.                           | Uwe Kleine-König            |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |

^ permalink raw reply

* Re: [PATCH 1/3] mmc: dw_mmc-socfpga: Remove the SOCFPGA specific platform for dw_mmc
From: Steffen Trumtrar @ 2014-02-17  8:22 UTC (permalink / raw)
  To: dinguyen
  Cc: linux-mmc, dinh.linux, devicetree, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala, Seungwon Jeon,
	Jaehoon Chung, Chris Ball
In-Reply-To: <1392622244-18015-1-git-send-email-dinguyen@altera.com>

On Mon, Feb 17, 2014 at 01:30:42AM -0600, dinguyen@altera.com wrote:
> From: Dinh Nguyen <dinguyen@altera.com>
> 
> It turns now that the only really platform specific code that is needed for
> SOCFPGA is using the SDMMC_CMD_USE_HOLD_REG in the prepare_command function.
> Since the Rockchip already has this functionality, re-use the code that is
> already in dw_mmc-pltfm.c.
> 
> Signed-off-by: Dinh Nguyen <dinguyen@altera.com>
> Acked-by: Jaehoon Chung <jh80.chung@samsung.com>
> 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: Seungwon Jeon <tgih.jun@samsung.com>
> Cc: Jaehoon Chung <jh80.chung@samsung.com>
> Cc: Chris Ball <chris@printf.net>
> ---
>  drivers/mmc/host/Kconfig          |    8 ---
>  drivers/mmc/host/Makefile         |    1 -
>  drivers/mmc/host/dw_mmc-socfpga.c |  138 -------------------------------------
>  3 files changed, 147 deletions(-)
>  delete mode 100644 drivers/mmc/host/dw_mmc-socfpga.c
> 
> diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
> index 1384f67..82cc34d 100644
> --- a/drivers/mmc/host/Kconfig
> +++ b/drivers/mmc/host/Kconfig
> @@ -580,14 +580,6 @@ config MMC_DW_EXYNOS
>  	  Synopsys DesignWare Memory Card Interface driver. Select this option
>  	  for platforms based on Exynos4 and Exynos5 SoC's.
>  
> -config MMC_DW_SOCFPGA
> -	tristate "SOCFPGA specific extensions for Synopsys DW Memory Card Interface"
> -	depends on MMC_DW && MFD_SYSCON
> -	select MMC_DW_PLTFM
> -	help
> -	  This selects support for Altera SoCFPGA specific extensions to the
> -	  Synopsys DesignWare Memory Card Interface driver.
> -
>  config MMC_DW_K3
>  	tristate "K3 specific extensions for Synopsys DW Memory Card Interface"
>  	depends on MMC_DW
> diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
> index 3483b6b..f162f87a0 100644
> --- a/drivers/mmc/host/Makefile
> +++ b/drivers/mmc/host/Makefile
> @@ -43,7 +43,6 @@ obj-$(CONFIG_SDH_BFIN)		+= bfin_sdh.o
>  obj-$(CONFIG_MMC_DW)		+= dw_mmc.o
>  obj-$(CONFIG_MMC_DW_PLTFM)	+= dw_mmc-pltfm.o
>  obj-$(CONFIG_MMC_DW_EXYNOS)	+= dw_mmc-exynos.o
> -obj-$(CONFIG_MMC_DW_SOCFPGA)	+= dw_mmc-socfpga.o
>  obj-$(CONFIG_MMC_DW_K3)		+= dw_mmc-k3.o
>  obj-$(CONFIG_MMC_DW_PCI)	+= dw_mmc-pci.o
>  obj-$(CONFIG_MMC_SH_MMCIF)	+= sh_mmcif.o
> diff --git a/drivers/mmc/host/dw_mmc-socfpga.c b/drivers/mmc/host/dw_mmc-socfpga.c
> deleted file mode 100644
> index 3e8e53a..0000000
> --- a/drivers/mmc/host/dw_mmc-socfpga.c
> +++ /dev/null
> @@ -1,138 +0,0 @@
> -/*
> - * Altera SoCFPGA Specific Extensions for Synopsys DW Multimedia Card Interface
> - * driver
> - *
> - *  Copyright (C) 2012, Samsung Electronics Co., Ltd.
> - *  Copyright (C) 2013 Altera Corporation
> - *
> - * 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.
> - *
> - * Taken from dw_mmc-exynos.c
> - */
> -#include <linux/clk.h>
> -#include <linux/mfd/syscon.h>
> -#include <linux/mmc/host.h>
> -#include <linux/mmc/dw_mmc.h>
> -#include <linux/module.h>
> -#include <linux/of.h>
> -#include <linux/platform_device.h>
> -#include <linux/regmap.h>
> -
> -#include "dw_mmc.h"
> -#include "dw_mmc-pltfm.h"
> -
> -#define SYSMGR_SDMMCGRP_CTRL_OFFSET		0x108
> -#define DRV_CLK_PHASE_SHIFT_SEL_MASK	0x7
> -#define SYSMGR_SDMMC_CTRL_SET(smplsel, drvsel)          \
> -	((((smplsel) & 0x7) << 3) | (((drvsel) & 0x7) << 0))
> -
> -/* SOCFPGA implementation specific driver private data */
> -struct dw_mci_socfpga_priv_data {
> -	u8	ciu_div; /* card interface unit divisor */
> -	u32	hs_timing; /* bitmask for CIU clock phase shift */
> -	struct regmap   *sysreg; /* regmap for system manager register */
> -};
> -
> -static int dw_mci_socfpga_priv_init(struct dw_mci *host)
> -{
> -	return 0;
> -}
> -
> -static int dw_mci_socfpga_setup_clock(struct dw_mci *host)
> -{
> -	struct dw_mci_socfpga_priv_data *priv = host->priv;
> -
> -	clk_disable_unprepare(host->ciu_clk);
> -	regmap_write(priv->sysreg, SYSMGR_SDMMCGRP_CTRL_OFFSET,
> -		priv->hs_timing);
> -	clk_prepare_enable(host->ciu_clk);
> -
> -	host->bus_hz /= (priv->ciu_div + 1);
> -	return 0;
> -}
> -
> -static void dw_mci_socfpga_prepare_command(struct dw_mci *host, u32 *cmdr)
> -{
> -	struct dw_mci_socfpga_priv_data *priv = host->priv;
> -
> -	if (priv->hs_timing & DRV_CLK_PHASE_SHIFT_SEL_MASK)
> -		*cmdr |= SDMMC_CMD_USE_HOLD_REG;
> -}
> -
> -static int dw_mci_socfpga_parse_dt(struct dw_mci *host)
> -{
> -	struct dw_mci_socfpga_priv_data *priv;
> -	struct device_node *np = host->dev->of_node;
> -	u32 timing[2];
> -	u32 div = 0;
> -	int ret;
> -
> -	priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
> -	if (!priv) {
> -		dev_err(host->dev, "mem alloc failed for private data\n");
> -		return -ENOMEM;
> -	}
> -
> -	priv->sysreg = syscon_regmap_lookup_by_compatible("altr,sys-mgr");
> -	if (IS_ERR(priv->sysreg)) {
> -		dev_err(host->dev, "regmap for altr,sys-mgr lookup failed.\n");
> -		return PTR_ERR(priv->sysreg);
> -	}
> -
> -	ret = of_property_read_u32(np, "altr,dw-mshc-ciu-div", &div);
> -	if (ret)
> -		dev_info(host->dev, "No dw-mshc-ciu-div specified, assuming 1");
> -	priv->ciu_div = div;
> -
> -	ret = of_property_read_u32_array(np,
> -			"altr,dw-mshc-sdr-timing", timing, 2);
> -	if (ret)
> -		return ret;
> -
> -	priv->hs_timing = SYSMGR_SDMMC_CTRL_SET(timing[0], timing[1]);
> -	host->priv = priv;
> -	return 0;
> -}
> -
> -static const struct dw_mci_drv_data socfpga_drv_data = {
> -	.init			= dw_mci_socfpga_priv_init,
> -	.setup_clock		= dw_mci_socfpga_setup_clock,
> -	.prepare_command	= dw_mci_socfpga_prepare_command,
> -	.parse_dt		= dw_mci_socfpga_parse_dt,
> -};
> -
> -static const struct of_device_id dw_mci_socfpga_match[] = {
> -	{ .compatible = "altr,socfpga-dw-mshc",
> -			.data = &socfpga_drv_data, },
> -	{},
> -};
> -MODULE_DEVICE_TABLE(of, dw_mci_socfpga_match);
> -
> -static int dw_mci_socfpga_probe(struct platform_device *pdev)
> -{
> -	const struct dw_mci_drv_data *drv_data;
> -	const struct of_device_id *match;
> -
> -	match = of_match_node(dw_mci_socfpga_match, pdev->dev.of_node);
> -	drv_data = match->data;
> -	return dw_mci_pltfm_register(pdev, drv_data);
> -}
> -
> -static struct platform_driver dw_mci_socfpga_pltfm_driver = {
> -	.probe		= dw_mci_socfpga_probe,
> -	.remove		= __exit_p(dw_mci_pltfm_remove),
> -	.driver		= {
> -		.name		= "dwmmc_socfpga",
> -		.of_match_table	= dw_mci_socfpga_match,
> -		.pm		= &dw_mci_pltfm_pmops,
> -	},
> -};
> -
> -module_platform_driver(dw_mci_socfpga_pltfm_driver);
> -
> -MODULE_DESCRIPTION("Altera SOCFPGA Specific DW-MSHC Driver Extension");
> -MODULE_LICENSE("GPL v2");
> -MODULE_ALIAS("platform:dwmmc-socfpga");
> -- 

Apart from comments on 2/3 and 3/3, whole series

	Acked-by: Steffen Trumtrar <s.trumtrar@pengutronix.de>

and

	Tested-by: Steffen Trumtrar <s.trumtrar@pengutronix.de>

Regards,
Steffen

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

^ permalink raw reply

* Re: [PATCH 3/3] dts: socfpga: Add support for SD/MMC on the SOCFPGA platform
From: Steffen Trumtrar @ 2014-02-17  8:18 UTC (permalink / raw)
  To: dinguyen
  Cc: linux-mmc, dinh.linux, devicetree, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala, Seungwon Jeon,
	Jaehoon Chung, Chris Ball
In-Reply-To: <1392622244-18015-3-git-send-email-dinguyen@altera.com>

On Mon, Feb 17, 2014 at 01:30:44AM -0600, dinguyen@altera.com wrote:
> From: Dinh Nguyen <dinguyen@altera.com>
> 
> Introduce "altr,socfpga-dw-mshc" to enable Altera's SOCFPGA platform specific
> implementation of the dwc_mmc driver.
> 
> Also add the "syscon" binding to the "altr,sys-mgr" node. The clock
> driver can use the syscon driver to toggle the register for the SD/MMC
> clock phase shift settings.
> 
> Signed-off-by: Dinh Nguyen <dinguyen@altera.com>
> 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: Seungwon Jeon <tgih.jun@samsung.com>
> Cc: Jaehoon Chung <jh80.chung@samsung.com>
> Cc: Chris Ball <chris@printf.net>
> ---
>  .../devicetree/bindings/mmc/socfpga-dw-mshc.txt    |   23 ++++++++++++++++++++
>  arch/arm/boot/dts/socfpga.dtsi                     |   13 ++++++++++-
>  arch/arm/boot/dts/socfpga_arria5.dtsi              |   11 ++++++++++
>  arch/arm/boot/dts/socfpga_cyclone5.dtsi            |   11 ++++++++++
>  arch/arm/boot/dts/socfpga_vt.dts                   |   11 ++++++++++
>  5 files changed, 68 insertions(+), 1 deletion(-)
>  create mode 100644 Documentation/devicetree/bindings/mmc/socfpga-dw-mshc.txt
> 
> diff --git a/Documentation/devicetree/bindings/mmc/socfpga-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/socfpga-dw-mshc.txt
> new file mode 100644
> index 0000000..4897bea
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mmc/socfpga-dw-mshc.txt
> @@ -0,0 +1,23 @@
> +* Altera SOCFPGA specific extensions to the Synopsys Designware Mobile
> +  Storage Host Controller
> +
> +The Synopsys designware mobile storage host controller is used to interface
> +a SoC with storage medium such as eMMC or SD/MMC cards. This file documents
> +differences between the core Synopsys dw mshc controller properties described
> +by synopsys-dw-mshc.txt and the properties used by the Altera SOCFPGA specific
> +extensions to the Synopsys Designware Mobile Storage Host Controller.
> +
> +Required Properties:
> +
> +* compatible: should be
> +	- "altr,socfpga-dw-mshc": for Altera's SOCFPGA platform
> +
> +Example:
> +
> +	mmc: dwmmc0@ff704000 {
> +		compatible = "altr,socfpga-dw-mshc";
> +		reg = <0xff704000 0x1000>;
> +		interrupts = <0 129 4>;
> +		#address-cells = <1>;
> +		#size-cells = <0>;
> +	};
> diff --git a/arch/arm/boot/dts/socfpga.dtsi b/arch/arm/boot/dts/socfpga.dtsi
> index 3d62f47..967e27f 100644
> --- a/arch/arm/boot/dts/socfpga.dtsi
> +++ b/arch/arm/boot/dts/socfpga.dtsi
> @@ -474,6 +474,17 @@
>  			arm,data-latency = <2 1 1>;
>  		};
>  
> +		mmc: dwmmc0@ff704000 {
> +			compatible = "altr,socfpga-dw-mshc";
> +			reg = <0xff704000 0x1000>;
> +			interrupts = <0 139 4>;
> +			fifo-depth = <0x400>;
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +			clocks = <&l4_mp_clk>, <&sdmmc_clk>;
> +			clock-names = "biu", "ciu";
> +		};
> +
>  		/* Local timer */
>  		timer@fffec600 {
>  			compatible = "arm,cortex-a9-twd-timer";
> @@ -528,7 +539,7 @@
>  		};
>  
>  		sysmgr@ffd08000 {
> -				compatible = "altr,sys-mgr";
> +				compatible = "altr,sys-mgr", "syscon";
>  				reg = <0xffd08000 0x4000>;
>  			};
>  	};

Could you also fix the indentation here, while your touching the node?
This one seems to be the only one left, that uses weird indentation.

Regards,
Steffen


-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

^ permalink raw reply

* Re: [PATCH 2/3] mmc: dw_mmc: Add support for SOCFPGA's platform specific implementation
From: Steffen Trumtrar @ 2014-02-17  8:15 UTC (permalink / raw)
  To: dinguyen
  Cc: linux-mmc, dinh.linux, devicetree, Rob Herring, Pawel Moll,
	Mark Rutland, Ian Campbell, Kumar Gala, Seungwon Jeon,
	Jaehoon Chung, Chris Ball
In-Reply-To: <1392622244-18015-2-git-send-email-dinguyen@altera.com>

Hi!

On Mon, Feb 17, 2014 at 01:30:43AM -0600, dinguyen@altera.com wrote:
> From: Dinh Nguyen <dinguyen@altera.com>
> 
> Like the rockchip, Altera's SOCFPGA platform specific implementation of the
> dw_mmc driver requires using the HOLD register for SD commands.
> 
> Signed-off-by: Dinh Nguyen <dinguyen@altera.com>
> 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: Seungwon Jeon <tgih.jun@samsung.com>
> Cc: Jaehoon Chung <jh80.chung@samsung.com>
> Cc: Chris Ball <chris@printf.net>
> ---
>  drivers/mmc/host/dw_mmc-pltfm.c |    6 ++++++
>  1 file changed, 6 insertions(+)
> 
> diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c
> index 5c49656..88047cc 100644
> --- a/drivers/mmc/host/dw_mmc-pltfm.c
> +++ b/drivers/mmc/host/dw_mmc-pltfm.c
> @@ -34,6 +34,10 @@ static const struct dw_mci_drv_data rockchip_drv_data = {
>  	.prepare_command	= dw_mci_rockchip_prepare_command,
>  };
>  
> +static const struct dw_mci_drv_data socfpga_drv_data = {
> +	.prepare_command	= dw_mci_rockchip_prepare_command,
> +};
> +
>  int dw_mci_pltfm_register(struct platform_device *pdev,
>  			  const struct dw_mci_drv_data *drv_data)
>  {
> @@ -92,6 +96,8 @@ static const struct of_device_id dw_mci_pltfm_match[] = {
>  	{ .compatible = "snps,dw-mshc", },
>  	{ .compatible = "rockchip,rk2928-dw-mshc",
>  		.data = &rockchip_drv_data },
> +	{ .compatible = "altr,socfpga-dw-mshc",
> +		.data = &socfpga_drv_data },
>  	{},
>  };
>  MODULE_DEVICE_TABLE(of, dw_mci_pltfm_match);
> -- 

Not really a bad problem, but wouldn't it be better to rename
	dw_mci_rockchip_prepare_command -> dw_mci_hold_reg_prepare_command
then? Has nothing to do with just rockchip anymore.

Regards,
Steffen

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

^ permalink raw reply


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