* [PATCH v4 6/6] dmaengine: shdma: add support for SUDMAC
@ 2012-01-11 7:28 Shimoda, Yoshihiro
2012-01-12 16:29 ` Guennadi Liakhovetski
` (7 more replies)
0 siblings, 8 replies; 9+ messages in thread
From: Shimoda, Yoshihiro @ 2012-01-11 7:28 UTC (permalink / raw)
To: linux-sh
The SH7757's USB module has SUDMAC. The SUDMAC's registers are imcompatible
with SH DMAC. However, since the SUDMAC is a very simple module, we can
reuse the shdma driver for SUDMAC by a few modification.
Signed-off-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
---
about v4:
- No change
drivers/dma/shdma.c | 102 +++++++++++++++++++++++++++++++++++++++++++-----
include/linux/sh_dma.h | 46 +++++++++++++++++++++
2 files changed, 138 insertions(+), 10 deletions(-)
diff --git a/drivers/dma/shdma.c b/drivers/dma/shdma.c
index 0f9d5f2..eb5ca48 100644
--- a/drivers/dma/shdma.c
+++ b/drivers/dma/shdma.c
@@ -58,6 +58,11 @@ static unsigned long sh_dmae_slave_used[BITS_TO_LONGS(SH_DMA_SLAVE_NUMBER)];
static void sh_dmae_chan_ld_cleanup(struct sh_dmae_chan *sh_chan, bool all);
+static int sh_dmae_is_sudmac(struct sh_dmae_device *shdev)
+{
+ return shdev->pdata->sudmac;
+}
+
static void sh_dmae_writel(struct sh_dmae_chan *sh_dc, u32 data, u32 reg)
{
__raw_writel(data, sh_dc->base + reg / sizeof(u32));
@@ -68,6 +73,39 @@ static u32 sh_dmae_readl(struct sh_dmae_chan *sh_dc, u32 reg)
return __raw_readl(sh_dc->base + reg / sizeof(u32));
}
+static void sh_dmae_sudmac_chcr_write(struct sh_dmae_chan *sh_dc, u32 data)
+{
+ struct sh_dmae_device *shdev = to_sh_dev(sh_dc);
+
+ if (!(data & CHCR_TE)) /* clear interrupt status only */
+ sh_dmae_writel(sh_dc, CH0ENDC, DINTSTSCLR);
+
+ if (data & shdev->chcr_ie_bit)
+ sh_dmae_writel(sh_dc, CH0ENDE, DINTCTRL);
+ else
+ sh_dmae_writel(sh_dc, 0, DINTCTRL);
+
+ if (data & CHCR_DE)
+ sh_dmae_writel(sh_dc, DEN, CH0DEN);
+ else
+ sh_dmae_writel(sh_dc, 0, CH0DEN);
+}
+
+static u32 sh_dmae_sudmac_chcr_read(struct sh_dmae_chan *sh_dc)
+{
+ struct sh_dmae_device *shdev = to_sh_dev(sh_dc);
+ u32 chcr = 0;
+
+ if (sh_dmae_readl(sh_dc, DINTSTS) & CH0ENDS)
+ chcr |= CHCR_TE;
+ if (sh_dmae_readl(sh_dc, DINTCTRL) & CH0ENDE)
+ chcr |= shdev->chcr_ie_bit;
+ if (sh_dmae_readl(sh_dc, CH0DEN) & DEN)
+ chcr |= CHCR_DE;
+
+ return chcr;
+}
+
static u16 dmaor_read(struct sh_dmae_device *shdev)
{
u32 __iomem *addr = shdev->chan_reg + DMAOR / sizeof(u32);
@@ -92,14 +130,20 @@ static void chcr_write(struct sh_dmae_chan *sh_dc, u32 data)
{
struct sh_dmae_device *shdev = to_sh_dev(sh_dc);
- sh_dmae_writel(sh_dc, data, shdev->chcr_offset);
+ if (sh_dmae_is_sudmac(shdev))
+ sh_dmae_sudmac_chcr_write(sh_dc, data);
+ else
+ sh_dmae_writel(sh_dc, data, shdev->chcr_offset);
}
static u32 chcr_read(struct sh_dmae_chan *sh_dc)
{
struct sh_dmae_device *shdev = to_sh_dev(sh_dc);
- return sh_dmae_readl(sh_dc, shdev->chcr_offset);
+ if (sh_dmae_is_sudmac(shdev))
+ return sh_dmae_sudmac_chcr_read(sh_dc);
+ else
+ return sh_dmae_readl(sh_dc, shdev->chcr_offset);
}
/*
@@ -112,6 +156,9 @@ static void sh_dmae_ctl_stop(struct sh_dmae_device *shdev)
unsigned short dmaor;
unsigned long flags;
+ if (sh_dmae_is_sudmac(shdev))
+ return;
+
spin_lock_irqsave(&sh_dmae_lock, flags);
dmaor = dmaor_read(shdev);
@@ -125,6 +172,9 @@ static int sh_dmae_rst(struct sh_dmae_device *shdev)
unsigned short dmaor;
unsigned long flags;
+ if (sh_dmae_is_sudmac(shdev))
+ return 0;
+
spin_lock_irqsave(&sh_dmae_lock, flags);
dmaor = dmaor_read(shdev) & ~(DMAOR_NMIF | DMAOR_AE | DMAOR_DME);
@@ -159,6 +209,9 @@ static unsigned int calc_xmit_shift(struct sh_dmae_chan *sh_chan, u32 chcr)
int cnt = ((chcr & pdata->ts_low_mask) >> pdata->ts_low_shift) |
((chcr & pdata->ts_high_mask) >> pdata->ts_high_shift);
+ if (sh_dmae_is_sudmac(shdev))
+ return 0;
+
if (cnt >= pdata->ts_shift_num)
cnt = 0;
@@ -171,6 +224,9 @@ static u32 log2size_to_chcr(struct sh_dmae_chan *sh_chan, int l2size)
struct sh_dmae_pdata *pdata = shdev->pdata;
int i;
+ if (sh_dmae_is_sudmac(shdev))
+ return 0;
+
for (i = 0; i < pdata->ts_shift_num; i++)
if (pdata->ts_shift[i] = l2size)
break;
@@ -184,9 +240,17 @@ static u32 log2size_to_chcr(struct sh_dmae_chan *sh_chan, int l2size)
static void dmae_set_reg(struct sh_dmae_chan *sh_chan, struct sh_dmae_regs *hw)
{
- sh_dmae_writel(sh_chan, hw->sar, SAR);
- sh_dmae_writel(sh_chan, hw->dar, DAR);
- sh_dmae_writel(sh_chan, hw->tcr >> sh_chan->xmit_shift, TCR);
+ struct sh_dmae_device *shdev = to_sh_dev(sh_chan);
+
+ if (sh_dmae_is_sudmac(shdev)) {
+ sh_dmae_writel(sh_chan, LBA_WAIT | RCVENDM, CH0CFG);
+ sh_dmae_writel(sh_chan, hw->sar, CH0BA);
+ sh_dmae_writel(sh_chan, hw->tcr, CH0BBC);
+ } else {
+ sh_dmae_writel(sh_chan, hw->sar, SAR);
+ sh_dmae_writel(sh_chan, hw->dar, DAR);
+ sh_dmae_writel(sh_chan, hw->tcr >> sh_chan->xmit_shift, TCR);
+ }
}
static void dmae_start(struct sh_dmae_chan *sh_chan)
@@ -493,6 +557,7 @@ static struct sh_desc *sh_dmae_add_desc(struct sh_dmae_chan *sh_chan,
unsigned long flags, dma_addr_t *dest, dma_addr_t *src, size_t *len,
struct sh_desc **first, enum dma_data_direction direction)
{
+ struct sh_dmae_device *shdev = to_sh_dev(sh_chan);
struct sh_desc *new;
size_t copy_size;
@@ -508,7 +573,14 @@ static struct sh_desc *sh_dmae_add_desc(struct sh_dmae_chan *sh_chan,
copy_size = min(*len, (size_t)SH_DMA_TCR_MAX + 1);
- new->hw.sar = *src;
+ /*
+ * SUDMAC has a CHnBA register only. So, the driver uses "hw.sar"
+ * even if transfer direction is DMA_FROM_DEVICE.
+ */
+ if (sh_dmae_is_sudmac(shdev) && direction = DMA_FROM_DEVICE)
+ new->hw.sar = *dest;
+ else
+ new->hw.sar = *src;
new->hw.dar = *dest;
new->hw.tcr = copy_size;
@@ -701,8 +773,10 @@ static int sh_dmae_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
/* Record partial transfer */
struct sh_desc *desc = list_entry(sh_chan->ld_queue.next,
struct sh_desc, node);
- desc->partial = (desc->hw.tcr - sh_dmae_readl(sh_chan, TCR)) <<
- sh_chan->xmit_shift;
+ struct sh_dmae_device *shdev = to_sh_dev(sh_chan);
+ if (!sh_dmae_is_sudmac(shdev))
+ desc->partial = (desc->hw.tcr - sh_dmae_readl(sh_chan,
+ TCR)) << sh_chan->xmit_shift;
}
spin_unlock_irqrestore(&sh_chan->desc_lock, flags);
@@ -989,9 +1063,17 @@ static irqreturn_t sh_dmae_err(int irq, void *data)
static void dmae_do_tasklet(unsigned long data)
{
struct sh_dmae_chan *sh_chan = (struct sh_dmae_chan *)data;
+ struct sh_dmae_device *shdev = to_sh_dev(sh_chan);
struct sh_desc *desc;
- u32 sar_buf = sh_dmae_readl(sh_chan, SAR);
- u32 dar_buf = sh_dmae_readl(sh_chan, DAR);
+ u32 sar_buf, dar_buf;
+
+ if (sh_dmae_is_sudmac(shdev)) {
+ sar_buf = sh_dmae_readl(sh_chan, CH0CA);
+ dar_buf = sh_dmae_readl(sh_chan, CH0CA);
+ } else {
+ sar_buf = sh_dmae_readl(sh_chan, SAR);
+ dar_buf = sh_dmae_readl(sh_chan, DAR);
+ }
spin_lock_irq(&sh_chan->desc_lock);
list_for_each_entry(desc, &sh_chan->ld_queue, node) {
diff --git a/include/linux/sh_dma.h b/include/linux/sh_dma.h
index a6c82cc..46be54f 100644
--- a/include/linux/sh_dma.h
+++ b/include/linux/sh_dma.h
@@ -72,6 +72,7 @@ struct sh_dmae_pdata {
unsigned int dmaor_is_32bit:1;
unsigned int needs_tend_set:1;
unsigned int no_dmars:1;
+ unsigned int sudmac:1;
};
/* DMA register */
@@ -111,4 +112,49 @@ struct sh_dmae_pdata {
#define CHCR_TE 0x00000002
#define CHCR_IE 0x00000004
+/* SUDMAC register */
+#define CH0CFG 0x00
+#define CH1CFG 0x04
+#define CH0BA 0x10
+#define CH1BA 0x14
+#define CH0BBC 0x18
+#define CH1BBC 0x1C
+#define CH0CA 0x20
+#define CH1CA 0x24
+#define CH0CBC 0x28
+#define CH1CBC 0x2C
+#define CH0DEN 0x30
+#define CH1DEN 0x34
+#define DSTSCLR 0x38
+#define DBUFCTRL 0x3C
+#define DINTCTRL 0x40
+#define DINTSTS 0x44
+#define DINTSTSCLR 0x48
+#define CH0SHCTRL 0x50
+#define CH1SHCTRL 0x54
+
+/* Definitions for the SUDMAC */
+#define SENDBUFM 0x1000 /* b12: Transmit Buffer Mode */
+#define RCVENDM 0x0100 /* b8: Receive Data Transfer End Mode */
+#define LBA_WAIT 0x0030 /* b5-4: Local Bus Access Wait */
+#define DEN 0x0001 /* b0: DMA Transfer Enable */
+#define CH1STCLR 0x0002 /* b1: Ch1 DMA Status Clear */
+#define CH0STCLR 0x0001 /* b0: Ch0 DMA Status Clear */
+#define CH1BUFW 0x0200 /* b9: Ch1 DMA Buffer Data Transfer Enable */
+#define CH0BUFW 0x0100 /* b8: Ch0 DMA Buffer Data Transfer Enable */
+#define CH1BUFS 0x0002 /* b1: Ch1 DMA Buffer Data Status */
+#define CH0BUFS 0x0001 /* b0: Ch0 DMA Buffer Data Status */
+#define CH1ERRE 0x0200 /* b9: Ch1 SHwy Res Err Detect Int Enable */
+#define CH0ERRE 0x0100 /* b8: Ch0 SHwy Res Err Detect Int Enable */
+#define CH1ENDE 0x0002 /* b1: Ch1 DMA Transfer End Int Enable */
+#define CH0ENDE 0x0001 /* b0: Ch0 DMA Transfer End Int Enable */
+#define CH1ERRS 0x0200 /* b9: Ch1 SHwy Res Err Detect Int Status */
+#define CH0ERRS 0x0100 /* b8: Ch0 SHwy Res Err Detect Int Status */
+#define CH1ENDS 0x0002 /* b1: Ch1 DMA Transfer End Int Status */
+#define CH0ENDS 0x0001 /* b0: Ch0 DMA Transfer End Int Status */
+#define CH1ERRC 0x0200 /* b9: Ch1 SHwy Res Err Detect Int Stat Clear */
+#define CH0ERRC 0x0100 /* b8: Ch0 SHwy Res Err Detect Int Stat Clear */
+#define CH1ENDC 0x0002 /* b1: Ch1 DMA Transfer End Int Stat Clear */
+#define CH0ENDC 0x0001 /* b0: Ch0 DMA Transfer End Int Stat Clear */
+
#endif
--
1.7.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH v4 6/6] dmaengine: shdma: add support for SUDMAC
2012-01-11 7:28 [PATCH v4 6/6] dmaengine: shdma: add support for SUDMAC Shimoda, Yoshihiro
@ 2012-01-12 16:29 ` Guennadi Liakhovetski
2012-01-13 8:03 ` Shimoda, Yoshihiro
` (6 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Guennadi Liakhovetski @ 2012-01-12 16:29 UTC (permalink / raw)
To: linux-sh
Hello Shimoda-san
On Wed, 11 Jan 2012, Shimoda, Yoshihiro wrote:
> The SH7757's USB module has SUDMAC. The SUDMAC's registers are imcompatible
> with SH DMAC. However, since the SUDMAC is a very simple module, we can
> reuse the shdma driver for SUDMAC by a few modification.
>
> Signed-off-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
I'm quite happy with patches 1-5 of this series! So, if you wanted, you
could add my ack for them, but Paul prefers v3. Well, I agree, that having
strings in the header is not crucial. As long as we make sure to update
platforms before the driver - nothing will break. That said, I would find
it nicer to see those patches in this series - just for completeness.
As for either converting both error and channel IRQs to named resources in
one or separate patches - I don't have a strong preference there either,
so, I can live with v3 too:-) Actually, there's a slight problem with
using named IRQs for channels: since we can have several resources with
channel IRQ ranges or with single IRQs per device, we cannot use
platform_get_resource_byname() for all of them - only for the first one,
which kind of makes this effort of converting all channel IRQ resources to
named ones - pointless...
Now, what concerns this patch. It is hard for me to judge, since I don't
have an sh7757 datasheet, but from the patch and from your comment "The
SUDMAC's registers are imcompatible with SH DMAC" I really begin to doubt,
whether it makes sense to combine them in one driver. AFAICS, the hardware
handling is indeed completely different. The only things, that you reuse
in the driver, are the dmaengine API implementation, which is pretty
standard, and the transfer descriptor list management, which is indeed a
bit tricky. My suggestion would be to split the current shdma.c driver
into two parts - hardware specific and the logic, and reuse the latter for
your new sudmac driver, which would then become a separate file. I'd have
to think a bit, what's the best way to do this: either
1. keep the main part of the driver with the logic as now, extracting
hardware-specific parts in a separate file and linking respective
functions to the main module via function pointers in an operations
struct, or
2. extract only the most essential and complicated logic functions into a
helper library and use it for both shdma and sudmac drivers.
What do you think? I'd need some time to think about this and come up with
a patch.
Thanks
Guennadi
> ---
> about v4:
> - No change
>
> drivers/dma/shdma.c | 102 +++++++++++++++++++++++++++++++++++++++++++-----
> include/linux/sh_dma.h | 46 +++++++++++++++++++++
> 2 files changed, 138 insertions(+), 10 deletions(-)
>
> diff --git a/drivers/dma/shdma.c b/drivers/dma/shdma.c
> index 0f9d5f2..eb5ca48 100644
> --- a/drivers/dma/shdma.c
> +++ b/drivers/dma/shdma.c
> @@ -58,6 +58,11 @@ static unsigned long sh_dmae_slave_used[BITS_TO_LONGS(SH_DMA_SLAVE_NUMBER)];
>
> static void sh_dmae_chan_ld_cleanup(struct sh_dmae_chan *sh_chan, bool all);
>
> +static int sh_dmae_is_sudmac(struct sh_dmae_device *shdev)
> +{
> + return shdev->pdata->sudmac;
> +}
> +
> static void sh_dmae_writel(struct sh_dmae_chan *sh_dc, u32 data, u32 reg)
> {
> __raw_writel(data, sh_dc->base + reg / sizeof(u32));
> @@ -68,6 +73,39 @@ static u32 sh_dmae_readl(struct sh_dmae_chan *sh_dc, u32 reg)
> return __raw_readl(sh_dc->base + reg / sizeof(u32));
> }
>
> +static void sh_dmae_sudmac_chcr_write(struct sh_dmae_chan *sh_dc, u32 data)
> +{
> + struct sh_dmae_device *shdev = to_sh_dev(sh_dc);
> +
> + if (!(data & CHCR_TE)) /* clear interrupt status only */
> + sh_dmae_writel(sh_dc, CH0ENDC, DINTSTSCLR);
> +
> + if (data & shdev->chcr_ie_bit)
> + sh_dmae_writel(sh_dc, CH0ENDE, DINTCTRL);
> + else
> + sh_dmae_writel(sh_dc, 0, DINTCTRL);
> +
> + if (data & CHCR_DE)
> + sh_dmae_writel(sh_dc, DEN, CH0DEN);
> + else
> + sh_dmae_writel(sh_dc, 0, CH0DEN);
> +}
> +
> +static u32 sh_dmae_sudmac_chcr_read(struct sh_dmae_chan *sh_dc)
> +{
> + struct sh_dmae_device *shdev = to_sh_dev(sh_dc);
> + u32 chcr = 0;
> +
> + if (sh_dmae_readl(sh_dc, DINTSTS) & CH0ENDS)
> + chcr |= CHCR_TE;
> + if (sh_dmae_readl(sh_dc, DINTCTRL) & CH0ENDE)
> + chcr |= shdev->chcr_ie_bit;
> + if (sh_dmae_readl(sh_dc, CH0DEN) & DEN)
> + chcr |= CHCR_DE;
> +
> + return chcr;
> +}
> +
> static u16 dmaor_read(struct sh_dmae_device *shdev)
> {
> u32 __iomem *addr = shdev->chan_reg + DMAOR / sizeof(u32);
> @@ -92,14 +130,20 @@ static void chcr_write(struct sh_dmae_chan *sh_dc, u32 data)
> {
> struct sh_dmae_device *shdev = to_sh_dev(sh_dc);
>
> - sh_dmae_writel(sh_dc, data, shdev->chcr_offset);
> + if (sh_dmae_is_sudmac(shdev))
> + sh_dmae_sudmac_chcr_write(sh_dc, data);
> + else
> + sh_dmae_writel(sh_dc, data, shdev->chcr_offset);
> }
>
> static u32 chcr_read(struct sh_dmae_chan *sh_dc)
> {
> struct sh_dmae_device *shdev = to_sh_dev(sh_dc);
>
> - return sh_dmae_readl(sh_dc, shdev->chcr_offset);
> + if (sh_dmae_is_sudmac(shdev))
> + return sh_dmae_sudmac_chcr_read(sh_dc);
> + else
> + return sh_dmae_readl(sh_dc, shdev->chcr_offset);
> }
>
> /*
> @@ -112,6 +156,9 @@ static void sh_dmae_ctl_stop(struct sh_dmae_device *shdev)
> unsigned short dmaor;
> unsigned long flags;
>
> + if (sh_dmae_is_sudmac(shdev))
> + return;
> +
> spin_lock_irqsave(&sh_dmae_lock, flags);
>
> dmaor = dmaor_read(shdev);
> @@ -125,6 +172,9 @@ static int sh_dmae_rst(struct sh_dmae_device *shdev)
> unsigned short dmaor;
> unsigned long flags;
>
> + if (sh_dmae_is_sudmac(shdev))
> + return 0;
> +
> spin_lock_irqsave(&sh_dmae_lock, flags);
>
> dmaor = dmaor_read(shdev) & ~(DMAOR_NMIF | DMAOR_AE | DMAOR_DME);
> @@ -159,6 +209,9 @@ static unsigned int calc_xmit_shift(struct sh_dmae_chan *sh_chan, u32 chcr)
> int cnt = ((chcr & pdata->ts_low_mask) >> pdata->ts_low_shift) |
> ((chcr & pdata->ts_high_mask) >> pdata->ts_high_shift);
>
> + if (sh_dmae_is_sudmac(shdev))
> + return 0;
> +
> if (cnt >= pdata->ts_shift_num)
> cnt = 0;
>
> @@ -171,6 +224,9 @@ static u32 log2size_to_chcr(struct sh_dmae_chan *sh_chan, int l2size)
> struct sh_dmae_pdata *pdata = shdev->pdata;
> int i;
>
> + if (sh_dmae_is_sudmac(shdev))
> + return 0;
> +
> for (i = 0; i < pdata->ts_shift_num; i++)
> if (pdata->ts_shift[i] = l2size)
> break;
> @@ -184,9 +240,17 @@ static u32 log2size_to_chcr(struct sh_dmae_chan *sh_chan, int l2size)
>
> static void dmae_set_reg(struct sh_dmae_chan *sh_chan, struct sh_dmae_regs *hw)
> {
> - sh_dmae_writel(sh_chan, hw->sar, SAR);
> - sh_dmae_writel(sh_chan, hw->dar, DAR);
> - sh_dmae_writel(sh_chan, hw->tcr >> sh_chan->xmit_shift, TCR);
> + struct sh_dmae_device *shdev = to_sh_dev(sh_chan);
> +
> + if (sh_dmae_is_sudmac(shdev)) {
> + sh_dmae_writel(sh_chan, LBA_WAIT | RCVENDM, CH0CFG);
> + sh_dmae_writel(sh_chan, hw->sar, CH0BA);
> + sh_dmae_writel(sh_chan, hw->tcr, CH0BBC);
> + } else {
> + sh_dmae_writel(sh_chan, hw->sar, SAR);
> + sh_dmae_writel(sh_chan, hw->dar, DAR);
> + sh_dmae_writel(sh_chan, hw->tcr >> sh_chan->xmit_shift, TCR);
> + }
> }
>
> static void dmae_start(struct sh_dmae_chan *sh_chan)
> @@ -493,6 +557,7 @@ static struct sh_desc *sh_dmae_add_desc(struct sh_dmae_chan *sh_chan,
> unsigned long flags, dma_addr_t *dest, dma_addr_t *src, size_t *len,
> struct sh_desc **first, enum dma_data_direction direction)
> {
> + struct sh_dmae_device *shdev = to_sh_dev(sh_chan);
> struct sh_desc *new;
> size_t copy_size;
>
> @@ -508,7 +573,14 @@ static struct sh_desc *sh_dmae_add_desc(struct sh_dmae_chan *sh_chan,
>
> copy_size = min(*len, (size_t)SH_DMA_TCR_MAX + 1);
>
> - new->hw.sar = *src;
> + /*
> + * SUDMAC has a CHnBA register only. So, the driver uses "hw.sar"
> + * even if transfer direction is DMA_FROM_DEVICE.
> + */
> + if (sh_dmae_is_sudmac(shdev) && direction = DMA_FROM_DEVICE)
> + new->hw.sar = *dest;
> + else
> + new->hw.sar = *src;
> new->hw.dar = *dest;
> new->hw.tcr = copy_size;
>
> @@ -701,8 +773,10 @@ static int sh_dmae_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
> /* Record partial transfer */
> struct sh_desc *desc = list_entry(sh_chan->ld_queue.next,
> struct sh_desc, node);
> - desc->partial = (desc->hw.tcr - sh_dmae_readl(sh_chan, TCR)) <<
> - sh_chan->xmit_shift;
> + struct sh_dmae_device *shdev = to_sh_dev(sh_chan);
> + if (!sh_dmae_is_sudmac(shdev))
> + desc->partial = (desc->hw.tcr - sh_dmae_readl(sh_chan,
> + TCR)) << sh_chan->xmit_shift;
> }
> spin_unlock_irqrestore(&sh_chan->desc_lock, flags);
>
> @@ -989,9 +1063,17 @@ static irqreturn_t sh_dmae_err(int irq, void *data)
> static void dmae_do_tasklet(unsigned long data)
> {
> struct sh_dmae_chan *sh_chan = (struct sh_dmae_chan *)data;
> + struct sh_dmae_device *shdev = to_sh_dev(sh_chan);
> struct sh_desc *desc;
> - u32 sar_buf = sh_dmae_readl(sh_chan, SAR);
> - u32 dar_buf = sh_dmae_readl(sh_chan, DAR);
> + u32 sar_buf, dar_buf;
> +
> + if (sh_dmae_is_sudmac(shdev)) {
> + sar_buf = sh_dmae_readl(sh_chan, CH0CA);
> + dar_buf = sh_dmae_readl(sh_chan, CH0CA);
> + } else {
> + sar_buf = sh_dmae_readl(sh_chan, SAR);
> + dar_buf = sh_dmae_readl(sh_chan, DAR);
> + }
>
> spin_lock_irq(&sh_chan->desc_lock);
> list_for_each_entry(desc, &sh_chan->ld_queue, node) {
> diff --git a/include/linux/sh_dma.h b/include/linux/sh_dma.h
> index a6c82cc..46be54f 100644
> --- a/include/linux/sh_dma.h
> +++ b/include/linux/sh_dma.h
> @@ -72,6 +72,7 @@ struct sh_dmae_pdata {
> unsigned int dmaor_is_32bit:1;
> unsigned int needs_tend_set:1;
> unsigned int no_dmars:1;
> + unsigned int sudmac:1;
> };
>
> /* DMA register */
> @@ -111,4 +112,49 @@ struct sh_dmae_pdata {
> #define CHCR_TE 0x00000002
> #define CHCR_IE 0x00000004
>
> +/* SUDMAC register */
> +#define CH0CFG 0x00
> +#define CH1CFG 0x04
> +#define CH0BA 0x10
> +#define CH1BA 0x14
> +#define CH0BBC 0x18
> +#define CH1BBC 0x1C
> +#define CH0CA 0x20
> +#define CH1CA 0x24
> +#define CH0CBC 0x28
> +#define CH1CBC 0x2C
> +#define CH0DEN 0x30
> +#define CH1DEN 0x34
> +#define DSTSCLR 0x38
> +#define DBUFCTRL 0x3C
> +#define DINTCTRL 0x40
> +#define DINTSTS 0x44
> +#define DINTSTSCLR 0x48
> +#define CH0SHCTRL 0x50
> +#define CH1SHCTRL 0x54
> +
> +/* Definitions for the SUDMAC */
> +#define SENDBUFM 0x1000 /* b12: Transmit Buffer Mode */
> +#define RCVENDM 0x0100 /* b8: Receive Data Transfer End Mode */
> +#define LBA_WAIT 0x0030 /* b5-4: Local Bus Access Wait */
> +#define DEN 0x0001 /* b0: DMA Transfer Enable */
> +#define CH1STCLR 0x0002 /* b1: Ch1 DMA Status Clear */
> +#define CH0STCLR 0x0001 /* b0: Ch0 DMA Status Clear */
> +#define CH1BUFW 0x0200 /* b9: Ch1 DMA Buffer Data Transfer Enable */
> +#define CH0BUFW 0x0100 /* b8: Ch0 DMA Buffer Data Transfer Enable */
> +#define CH1BUFS 0x0002 /* b1: Ch1 DMA Buffer Data Status */
> +#define CH0BUFS 0x0001 /* b0: Ch0 DMA Buffer Data Status */
> +#define CH1ERRE 0x0200 /* b9: Ch1 SHwy Res Err Detect Int Enable */
> +#define CH0ERRE 0x0100 /* b8: Ch0 SHwy Res Err Detect Int Enable */
> +#define CH1ENDE 0x0002 /* b1: Ch1 DMA Transfer End Int Enable */
> +#define CH0ENDE 0x0001 /* b0: Ch0 DMA Transfer End Int Enable */
> +#define CH1ERRS 0x0200 /* b9: Ch1 SHwy Res Err Detect Int Status */
> +#define CH0ERRS 0x0100 /* b8: Ch0 SHwy Res Err Detect Int Status */
> +#define CH1ENDS 0x0002 /* b1: Ch1 DMA Transfer End Int Status */
> +#define CH0ENDS 0x0001 /* b0: Ch0 DMA Transfer End Int Status */
> +#define CH1ERRC 0x0200 /* b9: Ch1 SHwy Res Err Detect Int Stat Clear */
> +#define CH0ERRC 0x0100 /* b8: Ch0 SHwy Res Err Detect Int Stat Clear */
> +#define CH1ENDC 0x0002 /* b1: Ch1 DMA Transfer End Int Stat Clear */
> +#define CH0ENDC 0x0001 /* b0: Ch0 DMA Transfer End Int Stat Clear */
> +
> #endif
> --
> 1.7.1
>
---
Guennadi Liakhovetski, Ph.D.
Freelance Open-Source Software Developer
http://www.open-technology.de/
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v4 6/6] dmaengine: shdma: add support for SUDMAC
2012-01-11 7:28 [PATCH v4 6/6] dmaengine: shdma: add support for SUDMAC Shimoda, Yoshihiro
2012-01-12 16:29 ` Guennadi Liakhovetski
@ 2012-01-13 8:03 ` Shimoda, Yoshihiro
2012-01-13 8:14 ` Paul Mundt
` (5 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Shimoda, Yoshihiro @ 2012-01-13 8:03 UTC (permalink / raw)
To: linux-sh
Hello Guennadi-san,
Thank you very much for your comment!
2012/01/13 1:29, Guennadi Liakhovetski wrote:
> Hello Shimoda-san
>
> On Wed, 11 Jan 2012, Shimoda, Yoshihiro wrote:
>
>> The SH7757's USB module has SUDMAC. The SUDMAC's registers are imcompatible
>> with SH DMAC. However, since the SUDMAC is a very simple module, we can
>> reuse the shdma driver for SUDMAC by a few modification.
>>
>> Signed-off-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
>
> I'm quite happy with patches 1-5 of this series! So, if you wanted, you
> could add my ack for them, but Paul prefers v3. Well, I agree, that having
> strings in the header is not crucial. As long as we make sure to update
> platforms before the driver - nothing will break. That said, I would find
> it nicer to see those patches in this series - just for completeness.
>
> As for either converting both error and channel IRQs to named resources in
> one or separate patches - I don't have a strong preference there either,
> so, I can live with v3 too:-) Actually, there's a slight problem with
> using named IRQs for channels: since we can have several resources with
> channel IRQ ranges or with single IRQs per device, we cannot use
> platform_get_resource_byname() for all of them - only for the first one,
> which kind of makes this effort of converting all channel IRQ resources to
> named ones - pointless...
I think so. The v4 patches use platform_get_resource_byname() for the channels,
but it also use platform_get_resource() later...
> Now, what concerns this patch. It is hard for me to judge, since I don't
> have an sh7757 datasheet, but from the patch and from your comment "The
> SUDMAC's registers are imcompatible with SH DMAC" I really begin to doubt,
> whether it makes sense to combine them in one driver. AFAICS, the hardware
> handling is indeed completely different. The only things, that you reuse
> in the driver, are the dmaengine API implementation, which is pretty
> standard, and the transfer descriptor list management, which is indeed a
> bit tricky. My suggestion would be to split the current shdma.c driver
> into two parts - hardware specific and the logic, and reuse the latter for
> your new sudmac driver, which would then become a separate file. I'd have
> to think a bit, what's the best way to do this: either
>
> 1. keep the main part of the driver with the logic as now, extracting
> hardware-specific parts in a separate file and linking respective
> functions to the main module via function pointers in an operations
> struct, or
>
> 2. extract only the most essential and complicated logic functions into a
> helper library and use it for both shdma and sudmac drivers.
>
> What do you think? I'd need some time to think about this and come up with
> a patch.
Umm, at the moment, I cannot judge which is good. I also need more time...
Best regards,
Yoshihiro Shimoda
> Thanks
> Guennadi
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v4 6/6] dmaengine: shdma: add support for SUDMAC
2012-01-11 7:28 [PATCH v4 6/6] dmaengine: shdma: add support for SUDMAC Shimoda, Yoshihiro
2012-01-12 16:29 ` Guennadi Liakhovetski
2012-01-13 8:03 ` Shimoda, Yoshihiro
@ 2012-01-13 8:14 ` Paul Mundt
2012-01-13 8:35 ` Guennadi Liakhovetski
` (4 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Paul Mundt @ 2012-01-13 8:14 UTC (permalink / raw)
To: linux-sh
On Thu, Jan 12, 2012 at 05:29:06PM +0100, Guennadi Liakhovetski wrote:
> As for either converting both error and channel IRQs to named resources in
> one or separate patches - I don't have a strong preference there either,
> so, I can live with v3 too:-) Actually, there's a slight problem with
> using named IRQs for channels: since we can have several resources with
> channel IRQ ranges or with single IRQs per device, we cannot use
> platform_get_resource_byname() for all of them - only for the first one,
> which kind of makes this effort of converting all channel IRQ resources to
> named ones - pointless...
>
Can you point to an example on the platform data side that you're
thinking about? The IRQ range thing doesn't strike me as being inherently
unsupportable by way of _byname(), but you are correct that _byname()
assumes one matching resource and doesn't support iterating (although we
could presumably extend the API for this if necessary).
> Now, what concerns this patch. It is hard for me to judge, since I don't
> have an sh7757 datasheet, but from the patch and from your comment "The
> SUDMAC's registers are imcompatible with SH DMAC" I really begin to doubt,
> whether it makes sense to combine them in one driver. AFAICS, the hardware
> handling is indeed completely different. The only things, that you reuse
> in the driver, are the dmaengine API implementation, which is pretty
> standard, and the transfer descriptor list management, which is indeed a
> bit tricky. My suggestion would be to split the current shdma.c driver
> into two parts - hardware specific and the logic, and reuse the latter for
> your new sudmac driver, which would then become a separate file. I'd have
> to think a bit, what's the best way to do this: either
>
> 1. keep the main part of the driver with the logic as now, extracting
> hardware-specific parts in a separate file and linking respective
> functions to the main module via function pointers in an operations
> struct, or
>
> 2. extract only the most essential and complicated logic functions into a
> helper library and use it for both shdma and sudmac drivers.
>
> What do you think? I'd need some time to think about this and come up with
> a patch.
>
Yes, I agree that this is probably the best way to go. The SUDMAC
layering on top of the shdma driver is pretty nasty, as it's largely
bypassing all of the hardware-specific bits anyways, suggesting that it
is really better off being split out.
My initial concern was whether breaking it out would make sense or not
given that we don't know what other DMACs will end up looking like, but
it looks like the SUDMAC case already does a good enough job of bypassing
all of the SH DMAC hardware bits that we more or less need a full
abstraction for the hardware side already, making the point moot.
Splitting out the logic functions in to a helper library looks to be the
cleanest way forward. The SUDMAC bits aren't going to make it in for 3.3
anyways, so we do have some time/wiggle room to get this done
cleanly/properly for 3.4.
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v4 6/6] dmaengine: shdma: add support for SUDMAC
2012-01-11 7:28 [PATCH v4 6/6] dmaengine: shdma: add support for SUDMAC Shimoda, Yoshihiro
` (2 preceding siblings ...)
2012-01-13 8:14 ` Paul Mundt
@ 2012-01-13 8:35 ` Guennadi Liakhovetski
2012-01-19 16:48 ` Guennadi Liakhovetski
` (3 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Guennadi Liakhovetski @ 2012-01-13 8:35 UTC (permalink / raw)
To: linux-sh
On Fri, 13 Jan 2012, Paul Mundt wrote:
> On Thu, Jan 12, 2012 at 05:29:06PM +0100, Guennadi Liakhovetski wrote:
> > As for either converting both error and channel IRQs to named resources in
> > one or separate patches - I don't have a strong preference there either,
> > so, I can live with v3 too:-) Actually, there's a slight problem with
> > using named IRQs for channels: since we can have several resources with
> > channel IRQ ranges or with single IRQs per device, we cannot use
> > platform_get_resource_byname() for all of them - only for the first one,
> > which kind of makes this effort of converting all channel IRQ resources to
> > named ones - pointless...
> >
> Can you point to an example on the platform data side that you're
> thinking about?
E.g. arch/sh/kernel/cpu/sh4a/setup-sh7724.c::sh7724_dmae0_resources, dmae1
is similar, same for sh7722.
> The IRQ range thing doesn't strike me as being inherently
> unsupportable by way of _byname(),
Hm, you don't mean adding extra names for each such case?...
> but you are correct that _byname()
> assumes one matching resource and doesn't support iterating (although we
> could presumably extend the API for this if necessary).
That would be a good option, yes. But whether it's worth it for this one
case...
> > Now, what concerns this patch. It is hard for me to judge, since I don't
> > have an sh7757 datasheet, but from the patch and from your comment "The
> > SUDMAC's registers are imcompatible with SH DMAC" I really begin to doubt,
> > whether it makes sense to combine them in one driver. AFAICS, the hardware
> > handling is indeed completely different. The only things, that you reuse
> > in the driver, are the dmaengine API implementation, which is pretty
> > standard, and the transfer descriptor list management, which is indeed a
> > bit tricky. My suggestion would be to split the current shdma.c driver
> > into two parts - hardware specific and the logic, and reuse the latter for
> > your new sudmac driver, which would then become a separate file. I'd have
> > to think a bit, what's the best way to do this: either
> >
> > 1. keep the main part of the driver with the logic as now, extracting
> > hardware-specific parts in a separate file and linking respective
> > functions to the main module via function pointers in an operations
> > struct, or
> >
> > 2. extract only the most essential and complicated logic functions into a
> > helper library and use it for both shdma and sudmac drivers.
> >
> > What do you think? I'd need some time to think about this and come up with
> > a patch.
> >
> Yes, I agree that this is probably the best way to go. The SUDMAC
> layering on top of the shdma driver is pretty nasty, as it's largely
> bypassing all of the hardware-specific bits anyways, suggesting that it
> is really better off being split out.
>
> My initial concern was whether breaking it out would make sense or not
> given that we don't know what other DMACs will end up looking like, but
> it looks like the SUDMAC case already does a good enough job of bypassing
> all of the SH DMAC hardware bits that we more or less need a full
> abstraction for the hardware side already, making the point moot.
>
> Splitting out the logic functions in to a helper library looks to be the
> cleanest way forward. The SUDMAC bits aren't going to make it in for 3.3
> anyways, so we do have some time/wiggle room to get this done
> cleanly/properly for 3.4.
Yeah... A library would probably be cleaner, but hardware-specific
callbacks, like OHCI and some other drivers do it, is likely easier:-)
Thanks
Guennadi
---
Guennadi Liakhovetski, Ph.D.
Freelance Open-Source Software Developer
http://www.open-technology.de/
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v4 6/6] dmaengine: shdma: add support for SUDMAC
2012-01-11 7:28 [PATCH v4 6/6] dmaengine: shdma: add support for SUDMAC Shimoda, Yoshihiro
` (3 preceding siblings ...)
2012-01-13 8:35 ` Guennadi Liakhovetski
@ 2012-01-19 16:48 ` Guennadi Liakhovetski
2012-01-20 2:07 ` Shimoda, Yoshihiro
` (2 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: Guennadi Liakhovetski @ 2012-01-19 16:48 UTC (permalink / raw)
To: linux-sh
Hello Shimoda-san
On Fri, 13 Jan 2012, Shimoda, Yoshihiro wrote:
> Hello Guennadi-san,
>
> Thank you very much for your comment!
>
> 2012/01/13 1:29, Guennadi Liakhovetski wrote:
[snip]
> > What do you think? I'd need some time to think about this and come up with
> > a patch.
>
> Umm, at the moment, I cannot judge which is good. I also need more time...
You probably have seen my yesterday's patches
http://thread.gmane.org/gmane.linux.alsa.devel/93681
What I'd like to ask you in this respect: do you have an idea, when you
would have time to try to implement your SUDMAC driver on top of that
series? Would be good to know whether any further changes to the "Simple
dmaengine driver" are required.
Thanks
Guennadi
---
Guennadi Liakhovetski, Ph.D.
Freelance Open-Source Software Developer
http://www.open-technology.de/
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v4 6/6] dmaengine: shdma: add support for SUDMAC
2012-01-11 7:28 [PATCH v4 6/6] dmaengine: shdma: add support for SUDMAC Shimoda, Yoshihiro
` (4 preceding siblings ...)
2012-01-19 16:48 ` Guennadi Liakhovetski
@ 2012-01-20 2:07 ` Shimoda, Yoshihiro
2012-01-21 16:15 ` Guennadi Liakhovetski
2012-01-23 8:42 ` Shimoda, Yoshihiro
7 siblings, 0 replies; 9+ messages in thread
From: Shimoda, Yoshihiro @ 2012-01-20 2:07 UTC (permalink / raw)
To: linux-sh
Hello Guennadi-san,
2012/01/20 1:48, Guennadi Liakhovetski wrote:
> Hello Shimoda-san
>
> On Fri, 13 Jan 2012, Shimoda, Yoshihiro wrote:
>
>> Hello Guennadi-san,
>>
>> Thank you very much for your comment!
>>
>> 2012/01/13 1:29, Guennadi Liakhovetski wrote:
>
> [snip]
>
>>> What do you think? I'd need some time to think about this and come up with
>>> a patch.
>>
>> Umm, at the moment, I cannot judge which is good. I also need more time...
>
> You probably have seen my yesterday's patches
>
> http://thread.gmane.org/gmane.linux.alsa.devel/93681
Thank you very much for the patches!
> What I'd like to ask you in this respect: do you have an idea, when you
> would have time to try to implement your SUDMAC driver on top of that
> series? Would be good to know whether any further changes to the "Simple
> dmaengine driver" are required.
I will study the dma-simple driver, and I will implement the SUDMAC driver
using this.
By the way, when I applied the "dma: shdma: convert to the simple DMA library" patch,
I couldn't apply it. (Many "Hunk #nn FAILED" output...)
So, would you tell me your using kernel commit?
I used today's "torbalds/linux.git" and "slave-dma.git".
Best regards,
Yoshihiro Shimoda
> Thanks
> Guennadi
> ---
> Guennadi Liakhovetski, Ph.D.
> Freelance Open-Source Software Developer
> http://www.open-technology.de/
>
--
Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
EC No.
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v4 6/6] dmaengine: shdma: add support for SUDMAC
2012-01-11 7:28 [PATCH v4 6/6] dmaengine: shdma: add support for SUDMAC Shimoda, Yoshihiro
` (5 preceding siblings ...)
2012-01-20 2:07 ` Shimoda, Yoshihiro
@ 2012-01-21 16:15 ` Guennadi Liakhovetski
2012-01-23 8:42 ` Shimoda, Yoshihiro
7 siblings, 0 replies; 9+ messages in thread
From: Guennadi Liakhovetski @ 2012-01-21 16:15 UTC (permalink / raw)
To: linux-sh
Hello Shimoda-san
On Fri, 20 Jan 2012, Shimoda, Yoshihiro wrote:
> I will study the dma-simple driver, and I will implement the SUDMAC driver
> using this.
Great, thanks!
> By the way, when I applied the "dma: shdma: convert to the simple DMA library" patch,
> I couldn't apply it. (Many "Hunk #nn FAILED" output...)
> So, would you tell me your using kernel commit?
> I used today's "torbalds/linux.git" and "slave-dma.git".
I think, the easiest for you would be to pull from
git://github.com/lyakh/linux.git
and use the "shmobile" branch, that already includes all my patches, that
you need. In fact, you only need a couple of them, so, you can also drop
all my mmc patches, that you don't need.
Thanks
Guennadi
---
Guennadi Liakhovetski, Ph.D.
Freelance Open-Source Software Developer
http://www.open-technology.de/
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v4 6/6] dmaengine: shdma: add support for SUDMAC
2012-01-11 7:28 [PATCH v4 6/6] dmaengine: shdma: add support for SUDMAC Shimoda, Yoshihiro
` (6 preceding siblings ...)
2012-01-21 16:15 ` Guennadi Liakhovetski
@ 2012-01-23 8:42 ` Shimoda, Yoshihiro
7 siblings, 0 replies; 9+ messages in thread
From: Shimoda, Yoshihiro @ 2012-01-23 8:42 UTC (permalink / raw)
To: linux-sh
Hello Guennadi-san,
2012/01/22 1:15, Guennadi Liakhovetski wrote:
>> By the way, when I applied the "dma: shdma: convert to the simple DMA library" patch,
>> I couldn't apply it. (Many "Hunk #nn FAILED" output...)
>> So, would you tell me your using kernel commit?
>> I used today's "torbalds/linux.git" and "slave-dma.git".
>
> I think, the easiest for you would be to pull from
>
> git://github.com/lyakh/linux.git
>
> and use the "shmobile" branch, that already includes all my patches, that
> you need. In fact, you only need a couple of them, so, you can also drop
> all my mmc patches, that you don't need.
Thank you very much for the information.
I could implment the sudmac driver using the dma-simple driver.
I wrote the sudmac driver for reference below.
After the dma-simple driver is applied, I will submit this sudmac driver:
---
drivers/dma/Kconfig | 9
drivers/dma/Makefile | 1
drivers/dma/sudmac.c | 484 +++++++++++++++++++++++++++++++++++++++++++++++++
drivers/dma/sudmac.h | 64 ++++++
include/linux/sudmac.h | 92 +++++++++
5 files changed, 650 insertions(+)
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index 2696552..bcbd6ae 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -161,6 +161,15 @@ config SH_DMAE
help
Enable support for the Renesas SuperH DMA controllers.
+config SUDMAC
+ tristate "Renesas SUDMAC support"
+ depends on SUPERH && SH_DMA
+ depends on !SH_DMA_API
+ select DMA_ENGINE
+ select DMA_SIMPLE
+ help
+ Enable support for the Renesas SUDMAC controllers.
+
config COH901318
bool "ST-Ericsson COH901318 DMA support"
select DMA_ENGINE
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index d63f773..4a09f3c 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -16,6 +16,7 @@ obj-$(CONFIG_AT_HDMAC) += at_hdmac.o
obj-$(CONFIG_MX3_IPU) += ipu/
obj-$(CONFIG_TXX9_DMAC) += txx9dmac.o
obj-$(CONFIG_SH_DMAE) += shdma.o
+obj-$(CONFIG_SUDMAC) += sudmac.o
obj-$(CONFIG_COH901318) += coh901318.o coh901318_lli.o
obj-$(CONFIG_AMCC_PPC440SPE_ADMA) += ppc4xx/
obj-$(CONFIG_IMX_SDMA) += imx-sdma.o
diff --git a/drivers/dma/sudmac.c b/drivers/dma/sudmac.c
new file mode 100644
index 0000000..68efe78
--- /dev/null
+++ b/drivers/dma/sudmac.c
@@ -0,0 +1,484 @@
+/*
+ * Renesas SUDMAC support
+ *
+ * Copyright (C) 2012 Renesas Solutions Corp.
+ *
+ * base is drivers/dma/shdma.c
+ * Copyright (C) 2011-2012 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ * Copyright (C) 2009 Nobuhiro Iwamatsu <iwamatsu.nobuhiro@renesas.com>
+ * Copyright (C) 2009 Renesas Solutions, Inc. All rights reserved.
+ * Copyright (C) 2007 Freescale Semiconductor, Inc. All rights reserved.
+ *
+ * This 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.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/dmaengine.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/sudmac.h>
+#include <linux/kdebug.h>
+#include <linux/spinlock.h>
+#include <linux/rculist.h>
+#include "sudmac.h"
+
+#define SH_DMAE_DRV_NAME "sudmac"
+
+static void sudmac_writel(struct sudmac_chan *sh_dc, u32 data, u16 reg)
+{
+ __raw_writel(data, sh_dc->base + reg);
+}
+
+static u32 sudmac_readl(struct sudmac_chan *sh_dc, u16 reg)
+{
+ return __raw_readl(sh_dc->base + reg);
+}
+
+static bool dmae_is_busy(struct sudmac_chan *sh_chan)
+{
+ u32 den = sudmac_readl(sh_chan, sh_chan->den);
+
+ if (den)
+ return true; /* working */
+
+ return false; /* idle */
+}
+
+static void dmae_set_reg(struct sudmac_chan *sh_chan, struct sudmac_regs *hw)
+{
+ sudmac_writel(sh_chan, LBA_WAIT | RCVENDM, sh_chan->cfg);
+ sudmac_writel(sh_chan, hw->ba, sh_chan->ba);
+ sudmac_writel(sh_chan, hw->bbc, sh_chan->bbc);
+}
+
+static void dmae_start(struct sudmac_chan *sh_chan)
+{
+ sudmac_writel(sh_chan, sh_chan->dint_end_bit, DINTCTRL);
+ sudmac_writel(sh_chan, DEN, sh_chan->den);
+}
+
+static void sudmac_start_xfer(struct dma_simple_chan *schan,
+ struct dma_simple_desc *sdesc)
+{
+ struct sudmac_chan *sh_chan = to_chan(schan);
+ struct sudmac_desc *sh_desc = to_desc(sdesc);
+
+ dev_dbg(sh_chan->simple_chan.dev, "Queue #%d to %d: %u, %x (%s)\n",
+ sdesc->async_tx.cookie, sh_chan->simple_chan.id,
+ sh_desc->hw.bbc, sh_desc->hw.ba,
+ (sdesc->direction = DMA_DEV_TO_MEM) ? "read" : "write");
+ dmae_set_reg(sh_chan, &sh_desc->hw);
+ dmae_start(sh_chan);
+}
+
+static bool sudmac_channel_busy(struct dma_simple_chan *schan)
+{
+ struct sudmac_chan *sh_chan = to_chan(schan);
+
+ return dmae_is_busy(sh_chan);
+}
+
+static void sudmac_setup_xfer(struct dma_simple_chan *schan,
+ struct dma_simple_slave *sslave)
+{
+ /* Nothing */
+}
+
+static const struct sudmac_slave_config *dmae_find_slave(
+ struct sudmac_chan *sh_chan, struct sudmac_slave *slave)
+{
+ struct sudmac_device *shdev = to_sudmac(sh_chan);
+ struct sudmac_pdata *pdata = shdev->pdata;
+ const struct sudmac_slave_config *cfg;
+ int i;
+
+ for (i = 0, cfg = pdata->slave; i < pdata->slave_num; i++, cfg++)
+ if (cfg->slave_id = slave->simple_slave.slave_id)
+ return cfg;
+
+ return NULL;
+}
+
+static int sudmac_set_slave(struct dma_simple_chan *schan,
+ struct dma_simple_slave *sslave)
+{
+ struct sudmac_chan *sh_chan = to_chan(schan);
+ struct sudmac_slave *slave = to_slave(sslave);
+ const struct sudmac_slave_config *cfg = dmae_find_slave(sh_chan, slave);
+
+ if (!cfg)
+ return -ENODEV;
+
+ slave->config = cfg;
+
+ return 0;
+}
+
+static void dmae_halt(struct sudmac_chan *sh_chan)
+{
+ sudmac_writel(sh_chan, 0, sh_chan->den);
+ sudmac_writel(sh_chan, ~sh_chan->dint_end_bit, DINTCTRL);
+ sudmac_writel(sh_chan, sh_chan->dint_end_bit, DINTSTSCLR);
+}
+
+static int sudmac_desc_setup(struct dma_simple_chan *schan,
+ struct dma_simple_desc *sdesc,
+ dma_addr_t src, dma_addr_t dst, size_t *len)
+{
+ struct sudmac_desc *sh_desc = to_desc(sdesc);
+
+ if (*len > schan->max_xfer_len)
+ *len = schan->max_xfer_len;
+
+ if (sdesc->direction = DMA_DEV_TO_MEM)
+ sh_desc->hw.ba = dst;
+ else
+ sh_desc->hw.ba = src;
+ sh_desc->hw.bbc = *len;
+
+ return 0;
+}
+
+static void sudmac_halt(struct dma_simple_chan *schan)
+{
+ struct sudmac_chan *sh_chan = to_chan(schan);
+
+ dmae_halt(sh_chan);
+}
+
+static irqreturn_t sudmac_interrupt(int irq, void *data)
+{
+ irqreturn_t ret = IRQ_NONE;
+ struct sudmac_chan *sh_chan = data;
+ u32 dintstat = sudmac_readl(sh_chan, DINTSTS);
+
+ dma_simple_lock(&sh_chan->simple_chan);
+
+ if (dintstat & CH0ENDS) {
+ /* DMA stop */
+ dmae_halt(sh_chan);
+
+ ret = IRQ_HANDLED;
+ /* clean up completed, submit new transfers */
+ dma_simple_reload(&sh_chan->simple_chan);
+ }
+
+ dma_simple_unlock(&sh_chan->simple_chan);
+
+ return ret;
+}
+
+static bool sudmac_desc_completed(struct dma_simple_chan *schan,
+ struct dma_simple_desc *sdesc)
+{
+ struct sudmac_chan *sh_chan = to_chan(schan);
+ struct sudmac_desc *sh_desc = to_desc(sdesc);
+ u32 ca = sudmac_readl(sh_chan, sh_chan->ca);
+
+ return ((sh_desc->hw.ba + sh_desc->hw.bbc) = ca);
+}
+
+static int __devinit sudmac_chan_probe(struct sudmac_device *shdev, int id,
+ int irq, unsigned long flags)
+{
+ struct dma_simple_dev *sdev = &shdev->simple_dev;
+ struct platform_device *pdev = to_platform_device(sdev->dma_dev.dev);
+ struct sudmac_chan *sh_chan;
+ struct dma_simple_chan *schan;
+ u16 channel_offset;
+ int err;
+
+ sh_chan = kzalloc(sizeof(struct sudmac_chan), GFP_KERNEL);
+ if (!sh_chan) {
+ dev_err(sdev->dma_dev.dev,
+ "No free memory for allocating dma channels!\n");
+ return -ENOMEM;
+ }
+
+ schan = &sh_chan->simple_chan;
+ schan->max_xfer_len = 64 * 1024 * 1024 - 1;
+
+ dma_simple_chan_probe(sdev, schan, id);
+
+ sh_chan->irq = irq;
+ sh_chan->base = shdev->chan_reg;
+
+ /* Registers offset */
+ channel_offset = shdev->pdata->channel->channel_no ? 4 : 0;
+
+ sh_chan->cfg = CH0CFG + channel_offset;
+ sh_chan->ba = CH0BA + channel_offset;
+ sh_chan->bbc = CH0BBC + channel_offset;
+ sh_chan->ca = CH0CA + channel_offset;
+ sh_chan->cbc = CH0CBC + channel_offset;
+ sh_chan->den = CH0DEN + channel_offset;
+
+ /* Registers bit offset */
+ sh_chan->dint_end_bit = shdev->pdata->channel->channel_no ?
+ CH1ENDE : CH0ENDE;
+
+ /* set up channel irq */
+ if (pdev->id >= 0)
+ snprintf(sh_chan->dev_id, sizeof(sh_chan->dev_id),
+ "sudmac%d.%d", pdev->id, id);
+ else
+ snprintf(sh_chan->dev_id, sizeof(sh_chan->dev_id),
+ "sudmac%d", id);
+
+ err = request_irq(irq, &sudmac_interrupt, flags,
+ sh_chan->dev_id, sh_chan);
+ if (err) {
+ dev_err(sdev->dma_dev.dev,
+ "DMA channel %d request_irq error %d\n",
+ id, err);
+ goto err_no_irq;
+ }
+
+ shdev->chan[id] = sh_chan;
+ return 0;
+
+err_no_irq:
+ dma_simple_chan_remove(schan);
+ kfree(sh_chan);
+ return err;
+}
+
+static void sudmac_chan_remove(struct sudmac_device *shdev)
+{
+ struct dma_device *dma_dev = &shdev->simple_dev.dma_dev;
+ struct dma_simple_chan *schan;
+ int i;
+
+ dma_simple_for_each_chan(schan, &shdev->simple_dev, i) {
+ struct sudmac_chan *sh_chan = to_chan(schan);
+
+ BUG_ON(!schan);
+
+ dma_simple_chan_remove(schan);
+ kfree(sh_chan);
+ }
+ dma_dev->chancnt = 0;
+}
+
+static void sudmac_shutdown(struct platform_device *pdev)
+{
+}
+
+static int sudmac_runtime_suspend(struct device *dev)
+{
+ return 0;
+}
+
+static int sudmac_runtime_resume(struct device *dev)
+{
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int sudmac_suspend(struct device *dev)
+{
+ return 0;
+}
+
+static int sudmac_resume(struct device *dev)
+{
+ return 0;
+}
+#else
+#define sudmac_suspend NULL
+#define sudmac_resume NULL
+#endif
+
+const struct dev_pm_ops sudmac_pm = {
+ .suspend = sudmac_suspend,
+ .resume = sudmac_resume,
+ .runtime_suspend = sudmac_runtime_suspend,
+ .runtime_resume = sudmac_runtime_resume,
+};
+
+static dma_addr_t sudmac_slave_addr(struct dma_simple_chan *schan)
+{
+ struct sudmac_slave *param = schan->dma_chan.private;
+
+ /*
+ * Implicit BUG_ON(!param)
+ * if (param != NULL), this is a successfully requested slave channel,
+ * therefore param->config != NULL too.
+ */
+ return param->config->addr;
+}
+
+static struct dma_simple_desc *sudmac_embedded_desc(void *buf, int i)
+{
+ return &((struct sudmac_desc *)buf)[i].simple_desc;
+}
+
+static const struct dma_simple_ops sudmac_simple_ops = {
+ .desc_completed = sudmac_desc_completed,
+ .halt_channel = sudmac_halt,
+ .channel_busy = sudmac_channel_busy,
+ .slave_addr = sudmac_slave_addr,
+ .desc_setup = sudmac_desc_setup,
+ .set_slave = sudmac_set_slave,
+ .setup_xfer = sudmac_setup_xfer,
+ .start_xfer = sudmac_start_xfer,
+ .embedded_desc = sudmac_embedded_desc,
+};
+
+static int __devinit sudmac_probe(struct platform_device *pdev)
+{
+ struct sudmac_pdata *pdata = pdev->dev.platform_data;
+ int err, i;
+ struct sudmac_device *shdev;
+ struct dma_device *dma_dev;
+ struct resource *chan, *irq_res;
+
+ /* get platform data */
+ if (!pdata)
+ return -ENODEV;
+
+ chan = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!chan || !irq_res)
+ return -ENODEV;
+
+ if (!request_mem_region(chan->start, resource_size(chan), pdev->name)) {
+ dev_err(&pdev->dev, "DMAC register region already claimed\n");
+ return -EBUSY;
+ }
+
+ err = -ENOMEM;
+ shdev = kzalloc(sizeof(struct sudmac_device), GFP_KERNEL);
+ if (!shdev) {
+ dev_err(&pdev->dev, "Not enough memory\n");
+ goto ealloc;
+ }
+
+ dma_dev = &shdev->simple_dev.dma_dev;
+
+ shdev->chan_reg = ioremap(chan->start, resource_size(chan));
+ if (!shdev->chan_reg)
+ goto emapchan;
+
+ dma_cap_set(DMA_SLAVE, dma_dev->cap_mask);
+
+ /* Default transfer size of 32 bytes requires 32-byte alignment */
+ shdev->simple_dev.ops = &sudmac_simple_ops;
+ shdev->simple_dev.desc_size = sizeof(struct sudmac_desc);
+ err = dma_simple_init(&pdev->dev, &shdev->simple_dev,
+ pdata->channel_num);
+ if (err < 0)
+ goto esimple;
+
+ /* platform data */
+ shdev->pdata = pdev->dev.platform_data;
+
+ platform_set_drvdata(pdev, shdev);
+
+ pm_runtime_enable(&pdev->dev);
+ err = pm_runtime_get_sync(&pdev->dev);
+ if (err < 0)
+ dev_err(&pdev->dev, "%s(): GET = %d\n", __func__, err);
+
+ /* Create DMA Channel */
+ for (i = 0; i < pdata->channel_num; i++) {
+ err = sudmac_chan_probe(shdev, i, irq_res->start, IRQF_SHARED);
+ if (err)
+ goto chan_probe_err;
+ }
+
+ pm_runtime_put(&pdev->dev);
+
+ err = dma_async_device_register(&shdev->simple_dev.dma_dev);
+ if (err < 0)
+ goto edmadevreg;
+
+ return err;
+
+edmadevreg:
+ pm_runtime_get(&pdev->dev);
+
+chan_probe_err:
+ sudmac_chan_remove(shdev);
+ free_irq(irq_res->start, shdev);
+
+ pm_runtime_put(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+
+ platform_set_drvdata(pdev, NULL);
+ dma_simple_cleanup(&shdev->simple_dev);
+esimple:
+ iounmap(shdev->chan_reg);
+ synchronize_rcu();
+emapchan:
+ kfree(shdev);
+ealloc:
+ release_mem_region(chan->start, resource_size(chan));
+
+ return err;
+}
+
+static int __devexit sudmac_remove(struct platform_device *pdev)
+{
+ struct sudmac_device *shdev = platform_get_drvdata(pdev);
+ struct dma_device *dma_dev = &shdev->simple_dev.dma_dev;
+ struct resource *res;
+ int irq = platform_get_irq(pdev, 0);
+
+ dma_async_device_unregister(dma_dev);
+
+ free_irq(irq, shdev);
+
+ pm_runtime_disable(&pdev->dev);
+
+ sudmac_chan_remove(shdev);
+ dma_simple_cleanup(&shdev->simple_dev);
+
+ iounmap(shdev->chan_reg);
+
+ platform_set_drvdata(pdev, NULL);
+
+ synchronize_rcu();
+ kfree(shdev);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res)
+ release_mem_region(res->start, resource_size(res));
+
+ return 0;
+}
+
+static struct platform_driver sudmac_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .pm = &sudmac_pm,
+ .name = SH_DMAE_DRV_NAME,
+ },
+ .remove = __devexit_p(sudmac_remove),
+ .shutdown = sudmac_shutdown,
+};
+
+static int __init sudmac_init(void)
+{
+ return platform_driver_probe(&sudmac_driver, sudmac_probe);
+}
+module_init(sudmac_init);
+
+static void __exit sudmac_exit(void)
+{
+ platform_driver_unregister(&sudmac_driver);
+}
+module_exit(sudmac_exit);
+
+MODULE_AUTHOR("Yoshihiro Shimoda");
+MODULE_DESCRIPTION("Renesas SUDMAC driver");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("0.1.0");
+MODULE_ALIAS("platform:" SH_DMAE_DRV_NAME);
diff --git a/drivers/dma/sudmac.h b/drivers/dma/sudmac.h
new file mode 100644
index 0000000..3b62ca3
--- /dev/null
+++ b/drivers/dma/sudmac.h
@@ -0,0 +1,64 @@
+/*
+ * Renesas SUDMAC support
+ *
+ * Copyright (C) 2012 Renesas Solutions Corp.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+#ifndef __DMA_SUDMAC_H
+#define __DMA_SUDMAC_H
+
+#include <linux/dma-simple.h>
+#include <linux/dmaengine.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+
+#define SH_DMAE_MAX_CHANNELS 2
+struct device;
+
+struct sudmac_chan {
+ struct dma_simple_chan simple_chan;
+ int irq;
+ void __iomem *base;
+ char dev_id[16]; /* unique name per DMAC of channel */
+
+ /* Registers offset */
+ u16 cfg;
+ u16 ba;
+ u16 bbc;
+ u16 ca;
+ u16 cbc;
+ u16 den;
+
+ /* Registers bit offset */
+ u32 dint_end_bit; /* DSTSCLR, DINTCTRL, DINTSTS, DINTSTSCLR */
+};
+
+struct sudmac_device {
+ struct dma_simple_dev simple_dev;
+ struct sudmac_chan *chan[SH_DMAE_MAX_CHANNELS];
+ struct sudmac_pdata *pdata;
+ u32 __iomem *chan_reg;
+};
+
+struct sudmac_regs {
+ u32 ba; /* CHnBA / Base Address */
+ u32 bbc; /* CHnBBC / Base Byte Count */
+};
+
+struct sudmac_desc {
+ struct sudmac_regs hw;
+ struct dma_simple_desc simple_desc;
+};
+
+#define to_chan(schan) container_of(schan, struct sudmac_chan, simple_chan)
+#define to_slave(sslave) container_of(sslave, struct sudmac_slave, simple_slave)
+#define to_desc(sdesc) container_of(sdesc, struct sudmac_desc, simple_desc)
+#define to_sudmac(chan) container_of(chan->simple_chan.dma_chan.device,\
+ struct sudmac_device, simple_dev.dma_dev)
+
+#endif /* __DMA_SHDMA_H */
diff --git a/include/linux/sudmac.h b/include/linux/sudmac.h
new file mode 100644
index 0000000..e6bf10d
--- /dev/null
+++ b/include/linux/sudmac.h
@@ -0,0 +1,92 @@
+/*
+ * Header for the SUDMAC driver
+ *
+ * Copyright (C) 2012 Renesas Solutions Corp.
+ *
+ * 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.
+ */
+#ifndef SUDMAC_H
+#define SUDMAC_H
+
+#include <linux/dma-simple.h>
+#include <linux/dmaengine.h>
+#include <linux/list.h>
+#include <linux/types.h>
+
+struct device;
+
+/*
+ * Supplied by platforms to specify, how a DMA channel has to be configured for
+ * a certain peripheral
+ */
+struct sudmac_slave_config {
+ unsigned int slave_id;
+ dma_addr_t addr;
+};
+
+/* Used by slave DMA clients to request DMA to/from a specific peripheral */
+struct sudmac_slave {
+ struct dma_simple_slave simple_slave; /* Set by the platform */
+ struct device *dma_dev; /* Set by the platform */
+ const struct sudmac_slave_config *config; /* Set by the driver */
+};
+
+struct sudmac_channel {
+ unsigned int channel_no;
+};
+
+struct sudmac_pdata {
+ const struct sudmac_slave_config *slave;
+ int slave_num;
+ const struct sudmac_channel *channel;
+ int channel_num;
+};
+
+/* SUDMAC register */
+#define CH0CFG 0x00
+#define CH1CFG 0x04
+#define CH0BA 0x10
+#define CH1BA 0x14
+#define CH0BBC 0x18
+#define CH1BBC 0x1C
+#define CH0CA 0x20
+#define CH1CA 0x24
+#define CH0CBC 0x28
+#define CH1CBC 0x2C
+#define CH0DEN 0x30
+#define CH1DEN 0x34
+#define DSTSCLR 0x38
+#define DBUFCTRL 0x3C
+#define DINTCTRL 0x40
+#define DINTSTS 0x44
+#define DINTSTSCLR 0x48
+#define CH0SHCTRL 0x50
+#define CH1SHCTRL 0x54
+
+/* Definitions for the SUDMAC */
+#define SENDBUFM 0x1000 /* b12: Transmit Buffer Mode */
+#define RCVENDM 0x0100 /* b8: Receive Data Transfer End Mode */
+#define LBA_WAIT 0x0030 /* b5-4: Local Bus Access Wait */
+#define DEN 0x0001 /* b0: DMA Transfer Enable */
+#define CH1STCLR 0x0002 /* b1: Ch1 DMA Status Clear */
+#define CH0STCLR 0x0001 /* b0: Ch0 DMA Status Clear */
+#define CH1BUFW 0x0200 /* b9: Ch1 DMA Buffer Data Transfer Enable */
+#define CH0BUFW 0x0100 /* b8: Ch0 DMA Buffer Data Transfer Enable */
+#define CH1BUFS 0x0002 /* b1: Ch1 DMA Buffer Data Status */
+#define CH0BUFS 0x0001 /* b0: Ch0 DMA Buffer Data Status */
+#define CH1ERRE 0x0200 /* b9: Ch1 SHwy Res Err Detect Int Enable */
+#define CH0ERRE 0x0100 /* b8: Ch0 SHwy Res Err Detect Int Enable */
+#define CH1ENDE 0x0002 /* b1: Ch1 DMA Transfer End Int Enable */
+#define CH0ENDE 0x0001 /* b0: Ch0 DMA Transfer End Int Enable */
+#define CH1ERRS 0x0200 /* b9: Ch1 SHwy Res Err Detect Int Status */
+#define CH0ERRS 0x0100 /* b8: Ch0 SHwy Res Err Detect Int Status */
+#define CH1ENDS 0x0002 /* b1: Ch1 DMA Transfer End Int Status */
+#define CH0ENDS 0x0001 /* b0: Ch0 DMA Transfer End Int Status */
+#define CH1ERRC 0x0200 /* b9: Ch1 SHwy Res Err Detect Int Stat Clear */
+#define CH0ERRC 0x0100 /* b8: Ch0 SHwy Res Err Detect Int Stat Clear */
+#define CH1ENDC 0x0002 /* b1: Ch1 DMA Transfer End Int Stat Clear */
+#define CH0ENDC 0x0001 /* b0: Ch0 DMA Transfer End Int Stat Clear */
+
+#endif
---
Best regards,
Yoshihiro Shimoda
> Thanks
> Guennadi
> ---
> Guennadi Liakhovetski, Ph.D.
> Freelance Open-Source Software Developer
> http://www.open-technology.de/
>
^ permalink raw reply related [flat|nested] 9+ messages in thread
end of thread, other threads:[~2012-01-23 8:42 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-01-11 7:28 [PATCH v4 6/6] dmaengine: shdma: add support for SUDMAC Shimoda, Yoshihiro
2012-01-12 16:29 ` Guennadi Liakhovetski
2012-01-13 8:03 ` Shimoda, Yoshihiro
2012-01-13 8:14 ` Paul Mundt
2012-01-13 8:35 ` Guennadi Liakhovetski
2012-01-19 16:48 ` Guennadi Liakhovetski
2012-01-20 2:07 ` Shimoda, Yoshihiro
2012-01-21 16:15 ` Guennadi Liakhovetski
2012-01-23 8:42 ` Shimoda, Yoshihiro
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).