* ping: [PATCH] GXT4000P and GXT6500P support
From: nello martuscielli @ 2011-10-04 11:56 UTC (permalink / raw)
To: linuxppc-dev, linux-fbdev-devel; +Cc: Nico Macrionitisl
ping for: http://lists.ozlabs.org/pipermail/linuxppc-dev/2009-June/072678.html
This patch adds support for GXT4000P and GXT6500P cards found on some
IBM pSeries machines.
GXT4000P/6000P and GXT4500P/6500P couples are identical from
software's point of view and are based on the same Raster Engine
(RC1000), except for a different reference clock for the PLL.
GXT6x00P models are equipped with an additional Geometry Engine
(GT1000) but this driver doesn't use it.
I see that even after 2 years, this patch still hasn't been added to
the official Linux kernel.
You will appreciate how little code this took to save everybody with a
GXT6500P card some trouble.
cheers,
Nello
--
CRUX PPC user on IBM POWER4+
^ permalink raw reply
* Re: [PATCH] libata: Convert LED disk trigger from IDE to libata
From: nello martuscielli @ 2011-10-04 11:35 UTC (permalink / raw)
To: Jörg Sommer; +Cc: Nico Macrionitis, linuxppc-dev
Hi Jorg,
you submitted the patch slightly modified by Acrux (from CruxPPC) [1]
Anyway this patch is widely used by any ibook/powerbook owners with
great satisfaction.
I also use this patch from Jan 2011 and i'd like to see it upstreamed.
Btw, guess you should add:
Signed-off-by: Joseph Jezak <josejx@gentoo.org>
Acked-by: Nico Macrionitis <acrux@cruxppc.org>
cheers,
Nello
[1] http://forums.gentoo.org/viewtopic-p-6550023.html#6550023
^ permalink raw reply
* Re: Request_irq fails for IRQ2
From: Vijay Nikam @ 2011-10-04 9:25 UTC (permalink / raw)
To: smitha.vanga; +Cc: scottwood, linuxppc-dev
In-Reply-To: <40631E9A2581F14BA60888C87A76A1FE0145F8@HYD-MKD-MBX4.wipro.com>
Smitha,
Do you have the entry of this device node in your DTS file?
if yes, then is the device is getting registered properly?
If you are writing platform driver then you can use 'platform_get_irq'
it returns the irq number,
which you can pass in the 'request_irq'. If it is a simple character
driver then to determine the
virtual irq number you can use 'ioremap', in this call you can pass
the HW IRQ number e.g. 20
and then pass in the 'request_irq'.
But in both cases you have to have this device node in your device
tree and should be registered,
otherwise it should be returning error and no virtual irq will be allocated=
.
As per your return value it seems the device node is not getting
registered, perhaps the entry is
missing in DTS file. Try with just ioremap and see if virtual irq is
returned properly.
Kind Regards,
Vijay Nikam
On Mon, Oct 3, 2011 at 7:57 PM, <smitha.vanga@wipro.com> wrote:
>
> Hi Scott,
>
> I try to request an IRQ (IRQ2 and IRQ3 which are ineterrupt no 20 and 21 =
in
> mpc8247)in my driver . The
> Call fails in setup_irq in Manage.c at /kernel/irq.
>
> Setup _irq returns -ENOSYS
>
> if (desc->irq_data.chip =3D=3D &no_irq_chip)
> =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 return -ENOSYS;
>
>
> I found that I need to pass the virtual interrupt number instead of hardw=
are
> interrupt number.
> So I added below piece of code
>
> Below is the call to request irq in my driver.
>
> virq =3D irq_create_mapping(NULL, CPLD1_INTERRUPT);
>
>
>
>
> =A0=A0 if ((ret =3D request_irq(virq,cpld_irq_handler, 0, GPIO_CHAR_PATH,
> NULL))!=3D0)
> =A0=A0 {
> =A0=A0=A0=A0=A0 printk(KERN_ERR "gpio_init: Could not grab IRQ line for C=
PLD ret =3D
> %d\n",ret);
> =A0=A0=A0=A0=A0=A0=A0 =A0 goto err1;
> =A0=A0 }
>
>
> Now it fails in irq_create_mapping=A0=A0 with NO_IRQ error.
>
> if (controller =3D=3D NULL)
> =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 host =3D irq_default_host;
> else
> =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 host =3D irq_find_host(cont=
roller);
> if (host =3D=3D NULL) {
> =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 printk(KERN_WARNING "irq: n=
o irq host found for %s !\n",
> =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0=A0=A0=A0=A0=A0 controll=
er->full_name);
> =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 return NO_IRQ;
> =A0=A0=A0=A0=A0=A0=A0 }
>
>
> I just don't know what I should pass for host , also when I pass NULL for
> host . I see the default host is NULL..
> Could you please help me. My project delivery is near , I need=A0 help so=
on.
>
>
> Regards,
> Smitha
>
>
>
> Please do not print this email unless it is absolutely necessary.
>
> The information contained in this electronic message and any attachments =
to
> this message are intended for the exclusive use of the addressee(s) and m=
ay
> contain proprietary, confidential or privileged information. If you are n=
ot
> the intended recipient, you should not disseminate, distribute or copy th=
is
> e-mail. Please notify the sender immediately and destroy all copies of th=
is
> message and any attachments.
>
> WARNING: Computer viruses can be transmitted via email. The recipient sho=
uld
> check this email and any attachments for the presence of viruses. The
> company accepts no liability for any damage caused by any virus transmitt=
ed
> by this email.
>
> www.wipro.com
>
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/linuxppc-dev
>
^ permalink raw reply
* Re: [PATCH] PSeries: Cancel RTAS event scan before firmware flash
From: Subrata Modak @ 2011-10-04 7:54 UTC (permalink / raw)
To: Ravi K Nittala
Cc: Anton Blanchard, naveedaus, Michael Neuling, Pavaman, suzuki,
ranittal, Vishu, linuxppc-dev, Divya Vikas
In-Reply-To: <20111004074932.27428.81900.stgit@localhost6.localdomain6>
Also,
On Tue, 2011-10-04 at 13:19 +0530, Ravi K Nittala wrote:
> The RTAS firmware flash update is conducted using an RTAS call that is
> serialized by lock_rtas() which uses spin_lock. While the flash is in
> progress, rtasd performs scan for any RTAS events that are generated by
> the system. rtasd keeps scanning for the RTAS events generated on the
> machine. This is performed via workqueue mechanism. The rtas_event_scan()
> also uses an RTAS call to scan the events, eventually trying to acquire
> the spin_lock before issuing the request.
>
> The flash update takes a while to complete and during this time, any other
> RTAS call has to wait. In this case, rtas_event_scan() waits for a long time
> on the spin_lock resulting in a soft lockup.
>
> Fix: Just before the flash update is performed, the queued rtas_event_scan()
> work item is cancelled from the work queue so that there is no other RTAS
> call issued while the flash is in progress. After the flash completes, the
> system reboots and the rtas_event_scan() is rescheduled.
>
> Signed-off-by: Suzuki Poulose <suzuki@in.ibm.com>
> Signed-off-by: Ravi Nittala <ravi.nittala@in.ibm.com>
Reported-by: Divya Vikas <divya.vikas@in.ibm.com>
Regards--
Subrata
>
> ---
> arch/powerpc/include/asm/rtas.h | 6 ++++++
> arch/powerpc/kernel/rtas_flash.c | 6 ++++++
> arch/powerpc/kernel/rtasd.c | 7 +++++++
> 3 files changed, 19 insertions(+), 0 deletions(-)
>
> diff --git a/arch/powerpc/include/asm/rtas.h b/arch/powerpc/include/asm/rtas.h
> index 58625d1..754723b 100644
> --- a/arch/powerpc/include/asm/rtas.h
> +++ b/arch/powerpc/include/asm/rtas.h
> @@ -245,6 +245,12 @@ extern int early_init_dt_scan_rtas(unsigned long node,
>
> extern void pSeries_log_error(char *buf, unsigned int err_type, int fatal);
>
> +#ifdef CONFIG_PPC_RTAS_DAEMON
> +extern void rtas_cancel_event_scan(void);
> +#else
> +static inline void rtas_cancel_event_scan(void) { }
> +#endif
> +
> /* Error types logged. */
> #define ERR_FLAG_ALREADY_LOGGED 0x0
> #define ERR_FLAG_BOOT 0x1 /* log was pulled from NVRAM on boot */
> diff --git a/arch/powerpc/kernel/rtas_flash.c b/arch/powerpc/kernel/rtas_flash.c
> index e037c74..4174b4b 100644
> --- a/arch/powerpc/kernel/rtas_flash.c
> +++ b/arch/powerpc/kernel/rtas_flash.c
> @@ -568,6 +568,12 @@ static void rtas_flash_firmware(int reboot_type)
> }
>
> /*
> + * Just before starting the firmware flash, cancel the event scan work
> + * to avoid any soft lockup issues.
> + */
> + rtas_cancel_event_scan();
> +
> + /*
> * NOTE: the "first" block must be under 4GB, so we create
> * an entry with no data blocks in the reserved buffer in
> * the kernel data segment.
> diff --git a/arch/powerpc/kernel/rtasd.c b/arch/powerpc/kernel/rtasd.c
> index 481ef06..1045ff4 100644
> --- a/arch/powerpc/kernel/rtasd.c
> +++ b/arch/powerpc/kernel/rtasd.c
> @@ -472,6 +472,13 @@ static void start_event_scan(void)
> &event_scan_work, event_scan_delay);
> }
>
> +/* Cancel the rtas event scan work */
> +void rtas_cancel_event_scan(void)
> +{
> + cancel_delayed_work_sync(&event_scan_work);
> +}
> +EXPORT_SYMBOL_GPL(rtas_cancel_event_scan);
> +
> static int __init rtas_init(void)
> {
> struct proc_dir_entry *entry;
>
^ permalink raw reply
* [PATCH] PSeries: Cancel RTAS event scan before firmware flash
From: Ravi K Nittala @ 2011-10-04 7:49 UTC (permalink / raw)
To: benh
Cc: Anton Blanchard, Subrata Modak, Michael Neuling, suzuki, ranittal,
linuxppc-dev, Divya Vikas
The RTAS firmware flash update is conducted using an RTAS call that is
serialized by lock_rtas() which uses spin_lock. While the flash is in
progress, rtasd performs scan for any RTAS events that are generated by
the system. rtasd keeps scanning for the RTAS events generated on the
machine. This is performed via workqueue mechanism. The rtas_event_scan()
also uses an RTAS call to scan the events, eventually trying to acquire
the spin_lock before issuing the request.
The flash update takes a while to complete and during this time, any other
RTAS call has to wait. In this case, rtas_event_scan() waits for a long time
on the spin_lock resulting in a soft lockup.
Fix: Just before the flash update is performed, the queued rtas_event_scan()
work item is cancelled from the work queue so that there is no other RTAS
call issued while the flash is in progress. After the flash completes, the
system reboots and the rtas_event_scan() is rescheduled.
Signed-off-by: Suzuki Poulose <suzuki@in.ibm.com>
Signed-off-by: Ravi Nittala <ravi.nittala@in.ibm.com>
---
arch/powerpc/include/asm/rtas.h | 6 ++++++
arch/powerpc/kernel/rtas_flash.c | 6 ++++++
arch/powerpc/kernel/rtasd.c | 7 +++++++
3 files changed, 19 insertions(+), 0 deletions(-)
diff --git a/arch/powerpc/include/asm/rtas.h b/arch/powerpc/include/asm/rtas.h
index 58625d1..754723b 100644
--- a/arch/powerpc/include/asm/rtas.h
+++ b/arch/powerpc/include/asm/rtas.h
@@ -245,6 +245,12 @@ extern int early_init_dt_scan_rtas(unsigned long node,
extern void pSeries_log_error(char *buf, unsigned int err_type, int fatal);
+#ifdef CONFIG_PPC_RTAS_DAEMON
+extern void rtas_cancel_event_scan(void);
+#else
+static inline void rtas_cancel_event_scan(void) { }
+#endif
+
/* Error types logged. */
#define ERR_FLAG_ALREADY_LOGGED 0x0
#define ERR_FLAG_BOOT 0x1 /* log was pulled from NVRAM on boot */
diff --git a/arch/powerpc/kernel/rtas_flash.c b/arch/powerpc/kernel/rtas_flash.c
index e037c74..4174b4b 100644
--- a/arch/powerpc/kernel/rtas_flash.c
+++ b/arch/powerpc/kernel/rtas_flash.c
@@ -568,6 +568,12 @@ static void rtas_flash_firmware(int reboot_type)
}
/*
+ * Just before starting the firmware flash, cancel the event scan work
+ * to avoid any soft lockup issues.
+ */
+ rtas_cancel_event_scan();
+
+ /*
* NOTE: the "first" block must be under 4GB, so we create
* an entry with no data blocks in the reserved buffer in
* the kernel data segment.
diff --git a/arch/powerpc/kernel/rtasd.c b/arch/powerpc/kernel/rtasd.c
index 481ef06..1045ff4 100644
--- a/arch/powerpc/kernel/rtasd.c
+++ b/arch/powerpc/kernel/rtasd.c
@@ -472,6 +472,13 @@ static void start_event_scan(void)
&event_scan_work, event_scan_delay);
}
+/* Cancel the rtas event scan work */
+void rtas_cancel_event_scan(void)
+{
+ cancel_delayed_work_sync(&event_scan_work);
+}
+EXPORT_SYMBOL_GPL(rtas_cancel_event_scan);
+
static int __init rtas_init(void)
{
struct proc_dir_entry *entry;
^ permalink raw reply related
* RE: Request_irq fails for IRQ2
From: smitha.vanga @ 2011-10-04 6:15 UTC (permalink / raw)
To: scottwood; +Cc: linuxppc-dev
In-Reply-To: <4E89EFB2.6060704@freescale.com>
[-- Attachment #1: Type: text/plain, Size: 1050 bytes --]
Hi Scott,
Thank you. But I am not very clear where exactly I need to call the irq_set_default_host().
And also for the second fix I did not understand or to make cpm2_pic_host non-static (in which case you should pass that).
in th previous mail what exactlyy you mean.
Thanks & Regards,
Smitha
Please do not print this email unless it is absolutely necessary.
The information contained in this electronic message and any attachments to this message are intended for the exclusive use of the addressee(s) and may contain proprietary, confidential or privileged information. If you are not the intended recipient, you should not disseminate, distribute or copy this e-mail. Please notify the sender immediately and destroy all copies of this message and any attachments.
WARNING: Computer viruses can be transmitted via email. The recipient should check this email and any attachments for the presence of viruses. The company accepts no liability for any damage caused by any virus transmitted by this email.
www.wipro.com
[-- Attachment #2: Type: text/html, Size: 1791 bytes --]
^ permalink raw reply
* Re: [PATCH] mlx4_en: fix transmit of packages when blue frame is enabled
From: Benjamin Herrenschmidt @ 2011-10-04 6:02 UTC (permalink / raw)
To: Thadeu Lima de Souza Cascardo
Cc: netdev@vger.kernel.org, Eli Cohen, eli@dev.mellanox.co.il,
linuxppc-dev, Yevgeny Petrilin
In-Reply-To: <20111003205358.GB3596@oc1711230544.ibm.com>
On Mon, 2011-10-03 at 17:53 -0300, Thadeu Lima de Souza Cascardo wrote:
.../...
> > Can you also send me the output of ethtool -i?
> > It seems that there is a problem with write combining on Power processors, we will check this issue.
> >
> > Yevgeny
>
> Hello, Yevgeny.
>
> You will find the output of ethtool -i below.
>
> I am copying Ben and powerpc list, in case this is an issue with Power
> processors. They can provide us some more insight into this.
May I get some background please ? :-)
I'm not aware of a specific issue with write combining but I'd need to
know more about what you are doing and the code to do it to comment on
whether it should work or not.
Cheers,
Ben.
^ permalink raw reply
* Re: [PATCH] memory hotplug: Correct page reservation checking
From: Andrew Morton @ 2011-10-04 0:50 UTC (permalink / raw)
To: Nathan Fontenot; +Cc: Greg KH, linuxppc-dev, linux-kernel
In-Reply-To: <4E8098B9.1080702@austin.ibm.com>
On Mon, 26 Sep 2011 10:22:33 -0500
Nathan Fontenot <nfont@austin.ibm.com> wrote:
> The check to ensure that pages of recently added memory sections are correctly
> marked as reserved before trying to online the memory is broken. The request
> to online the memory fails with the following:
>
> kernel: section number XXX page number 256 not reserved, was it already online?
>
> This updates the page reservation checking to check the pages of each memory
> section of the memory block being onlined individually.
Why was this only noticed now? Is there something unusual about the
way in which you're using it, or has nobody ever used this code, or...?
^ permalink raw reply
* Re: [PATCH] mlx4_en: fix transmit of packages when blue frame is enabled
From: Thadeu Lima de Souza Cascardo @ 2011-10-03 20:53 UTC (permalink / raw)
To: Yevgeny Petrilin
Cc: netdev@vger.kernel.org, eli@dev.mellanox.co.il, linuxppc-dev,
Eli Cohen
In-Reply-To: <953B660C027164448AE903364AC447D2235DAA9D@MTLDAG02.mtl.com>
On Mon, Oct 03, 2011 at 02:56:08PM +0000, Yevgeny Petrilin wrote:
> > Hello, Yevgeny.
> >
> > We use a MT26448 (lspci -v output bellow) on a POWER7. Any other
> > information, tests or debug patches you want me to try, just tell me.
> >
> > I expected this was really not the proper fix, but thought it would be
> > better to send it than just guess. Any clues what the problem might be?
> > Perhaps, we would have to disable blue frame for this particular device?
> >
> > Regards,
> > Cascardo.
> >
> > ---
> > lspci output
> > 0006:01:00.0 Ethernet controller: Mellanox Technologies MT26448 [ConnectX EN 10GigE, PCIe 2.0 5GT/s] (rev b0)
> > ---
> > lspci -v output
> > 0006:01:00.0 0200: 15b3:6750 (rev b0)
> > Subsystem: 1014:0416
> > Flags: bus master, fast devsel, latency 0, IRQ 31
> > Memory at 3da47be00000 (64-bit, non-prefetchable) [size=1M]
> > Memory at 3da47c000000 (64-bit, prefetchable) [size=32M]
> > Expansion ROM at 3da47bf00000 [disabled] [size=1M]
> > Capabilities: [40] Power Management version 3
> > Capabilities: [48] Vital Product Data
> > Capabilities: [9c] MSI-X: Enable+ Count=128 Masked-
> > Capabilities: [60] Express Endpoint, MSI 00
> > Capabilities: [100] Alternative Routing-ID Interpretation (ARI)
> > Capabilities: [148] Device Serial Number 00-02-c9-03-00-0f-50-be
> > Kernel driver in use: mlx4_core
> > Kernel modules: mlx4_en, mlx4_core
> > ---
>
> Cascardo,
>
> Can you also send me the output of ethtool -i?
> It seems that there is a problem with write combining on Power processors, we will check this issue.
>
> Yevgeny
Hello, Yevgeny.
You will find the output of ethtool -i below.
I am copying Ben and powerpc list, in case this is an issue with Power
processors. They can provide us some more insight into this.
Thanks,
Cascardo.
---
# ethtool -i eth10
driver: mlx4_en
version: 1.5.4.1 (March 2011)
firmware-version: 2.9.4130
bus-info: 0006:01:00.0
^ permalink raw reply
* Re: [PATCH v2] powerpc: book3e: WSP: Add Chroma as a new WSP/PowerEN platform.
From: Scott Wood @ 2011-10-03 18:02 UTC (permalink / raw)
To: Jimi Xenidis; +Cc: linuxppc-dev
In-Reply-To: <1317349675-18661-1-git-send-email-jimix@pobox.com>
On 09/29/2011 09:27 PM, Jimi Xenidis wrote:
> diff --git a/arch/powerpc/platforms/wsp/Kconfig b/arch/powerpc/platforms/wsp/Kconfig
> index ea2811c..a3eef8e 100644
> --- a/arch/powerpc/platforms/wsp/Kconfig
> +++ b/arch/powerpc/platforms/wsp/Kconfig
> @@ -1,6 +1,7 @@
> config PPC_WSP
> bool
> select PPC_A2
> + select GENERIC_TBSYNC
> select PPC_ICSWX
> select PPC_SCOM
> select PPC_XICS
> @@ -8,14 +9,20 @@ config PPC_WSP
> select PCI
> select PPC_IO_WORKAROUNDS if PCI
> select PPC_INDIRECT_PIO if PCI
> + select PPC_WSP_COPRO
> default n
>
> menu "WSP platform selection"
> depends on PPC_BOOK3E_64
>
> config PPC_PSR2
> - bool "PSR-2 platform"
> - select GENERIC_TBSYNC
> + bool "PowerEN System Reference Platform 2"
> + select EPAPR_BOOT
> + select PPC_WSP
> + default y
> +
> +config PPC_CHROMA
> + bool "PowerEN PCIe Chroma Card"
> select EPAPR_BOOT
> select PPC_WSP
> default y
This is an existing problem with PSR2, but please don't hide "default y"
in a menu (at least make it a menuconfig). As is, it's not obvious from
looking at the toplevel platforms menu that these platforms are enabled
at all.
Further, PPC_WSP doesn't build on non-SMP (undefined references to
boot_cpuid and get_hard_smp_processor_id in ics.c), but the platforms
that select it don't depend on SMP.
-Scott
^ permalink raw reply
* RE: [RFC PATCH 2/2 -mm] RapidIO: TSI721 Add DMA Engine support
From: Bounine, Alexandre @ 2011-10-03 17:53 UTC (permalink / raw)
To: Andrew Morton; +Cc: Vinod Koul, linux-kernel, linuxppc-dev
In-Reply-To: <20110930151544.31875132.akpm00@gmail.com>
Andrew Morton wroye:
>=20
> On Fri, 30 Sep 2011 17:38:35 -0400
> Alexandre Bounine <alexandre.bounine@idt.com> wrote:
>=20
> > Adds support for DMA Engine API.
> >
> > Includes following changes:
> > - Modifies BDMA register offset definitions to support per-channel
> handling
> > - Separates BDMA channel reserved for RIO Maintenance requests
> > - Adds DMA Engine callback routines
> >
> > ...
> >
> > 5 files changed, 1029 insertions(+), 90 deletions(-)
>=20
> hm, what a lot of code.
This is mostly new stuff for that driver.
>=20
> > +config TSI721_DMA
> > + bool "IDT Tsi721 RapidIO DMA support"
> > + depends on RAPIDIO_TSI721
> > + default "n"
> > + select RAPIDIO_DMA_ENGINE
> > + help
> > + Enable DMA support for IDT Tsi721 PCIe-to-SRIO controller.
>=20
> Do we really need to offer this decision to the user? If possible it
> would be better to always enable the feature where that makes sense.
> Better code coverage, less maintenance effort, more effective testing
> effort, possibly cleaner code.
Agree. Influence of dmaengine here ;)
But we still need RAPIDIO_DMA_ENGINE option to control DMA
configuration for devices that are RIO targets only.=20
>=20
> >
> > ...
> >
> > +static int tsi721_bdma_ch_init(struct tsi721_bdma_chan *chan)
> > +{
> > + struct tsi721_dma_desc *bd_ptr;
> > + struct device *dev =3D chan->dchan.device->dev;
> > + u64 *sts_ptr;
> > + dma_addr_t bd_phys;
> > + dma_addr_t sts_phys;
> > + int sts_size;
> > + int bd_num =3D chan->bd_num;
> > +
> > + dev_dbg(dev, "Init Block DMA Engine, CH%d\n", chan->id);
> > +
> > + /* Allocate space for DMA descriptors */
> > + bd_ptr =3D dma_alloc_coherent(dev,
> > + bd_num * sizeof(struct tsi721_dma_desc),
> > + &bd_phys, GFP_KERNEL);
> > + if (!bd_ptr)
> > + return -ENOMEM;
> > +
> > + chan->bd_phys =3D bd_phys;
> > + chan->bd_base =3D bd_ptr;
> > +
> > + memset(bd_ptr, 0, bd_num * sizeof(struct tsi721_dma_desc));
> > +
> > + dev_dbg(dev, "DMA descriptors @ %p (phys =3D %llx)\n",
> > + bd_ptr, (unsigned long long)bd_phys);
> > +
> > + /* Allocate space for descriptor status FIFO */
> > + sts_size =3D (bd_num >=3D TSI721_DMA_MINSTSSZ) ?
> > + bd_num : TSI721_DMA_MINSTSSZ;
> > + sts_size =3D roundup_pow_of_two(sts_size);
> > + sts_ptr =3D dma_alloc_coherent(dev,
> > + sts_size * sizeof(struct
tsi721_dma_sts),
> > + &sts_phys, GFP_KERNEL);
> > + if (!sts_ptr) {
> > + /* Free space allocated for DMA descriptors */
> > + dma_free_coherent(dev,
> > + bd_num * sizeof(struct
tsi721_dma_desc),
> > + bd_ptr, bd_phys);
> > + chan->bd_base =3D NULL;
> > + return -ENOMEM;
> > + }
> > +
> > + chan->sts_phys =3D sts_phys;
> > + chan->sts_base =3D sts_ptr;
> > + chan->sts_size =3D sts_size;
> > +
> > + memset(sts_ptr, 0, sts_size);
>=20
> You meant
I really need it here. That status block tracks progress by keeping
non-zero addresses of processed descriptors.
=20
>=20
> --- a/drivers/rapidio/devices/tsi721.c~rapidio-tsi721-add-dma-engine-
> support-fix
> +++ a/drivers/rapidio/devices/tsi721.c
> @@ -1006,7 +1006,7 @@ static int tsi721_bdma_maint_init(struct
> priv->mdma.sts_base =3D sts_ptr;
> priv->mdma.sts_size =3D sts_size;
>=20
> - memset(sts_ptr, 0, sts_size);
> + memset(sts_ptr, 0, sts_size * sizeof(struct tsi721_dma_sts));
>=20
> dev_dbg(&priv->pdev->dev,
> "desc status FIFO @ %p (phys =3D %llx) size=3D0x%x\n",
>=20
> However that's at least two instances where you wanted a
> dma_zalloc_coherent(). How's about we give ourselves one?
Does this mean that I am on hook for it as a "most frequent user"?
>=20
>=20
> > + dev_dbg(dev,
> > + "desc status FIFO @ %p (phys =3D %llx) size=3D0x%x\n",
> > + sts_ptr, (unsigned long long)sts_phys, sts_size);
> > +
> > + /* Initialize DMA descriptors ring */
> > + bd_ptr[bd_num - 1].type_id =3D cpu_to_le32(DTYPE3 << 29);
> > + bd_ptr[bd_num - 1].next_lo =3D cpu_to_le32((u64)bd_phys &
> > +
TSI721_DMAC_DPTRL_MASK);
> > + bd_ptr[bd_num - 1].next_hi =3D cpu_to_le32((u64)bd_phys >> 32);
> > +
> > + /* Setup DMA descriptor pointers */
> > + iowrite32(((u64)bd_phys >> 32),
> > + chan->regs + TSI721_DMAC_DPTRH);
> > + iowrite32(((u64)bd_phys & TSI721_DMAC_DPTRL_MASK),
> > + chan->regs + TSI721_DMAC_DPTRL);
> > +
> > + /* Setup descriptor status FIFO */
> > + iowrite32(((u64)sts_phys >> 32),
> > + chan->regs + TSI721_DMAC_DSBH);
> > + iowrite32(((u64)sts_phys & TSI721_DMAC_DSBL_MASK),
> > + chan->regs + TSI721_DMAC_DSBL);
> > + iowrite32(TSI721_DMAC_DSSZ_SIZE(sts_size),
> > + chan->regs + TSI721_DMAC_DSSZ);
> > +
> > + /* Clear interrupt bits */
> > + iowrite32(TSI721_DMAC_INT_ALL,
> > + chan->regs + TSI721_DMAC_INT);
> > +
> > + ioread32(chan->regs + TSI721_DMAC_INT);
> > +
> > + /* Toggle DMA channel initialization */
> > + iowrite32(TSI721_DMAC_CTL_INIT, chan->regs + TSI721_DMAC_CTL);
> > + ioread32(chan->regs + TSI721_DMAC_CTL);
> > + chan->wr_count =3D chan->wr_count_next =3D 0;
> > + chan->sts_rdptr =3D 0;
> > + udelay(10);
> > +
> > + return 0;
> > +}
> > +
> >
> > ...
> >
> > +{
> > + /* Disable BDMA channel interrupts */
> > + iowrite32(0, chan->regs + TSI721_DMAC_INTE);
> > +
> > + tasklet_schedule(&chan->tasklet);
>=20
> I'm not seeing any tasklet_disable()s on the shutdown/rmmod paths. Is
> there anything here which prevents shutdown races against a
> still-pending tasklet?
Marked for review.
>=20
> > +}
> > +
> >
> > ...
> >
> > +static
> > +int tsi721_fill_desc(struct tsi721_bdma_chan *chan, struct
> tsi721_tx_desc *desc,
> > + struct scatterlist *sg, enum dma_rtype rtype, u32 sys_size)
> > +{
> > + struct tsi721_dma_desc *bd_ptr =3D desc->hw_desc;
> > + u64 rio_addr;
> > +
> > + if (sg_dma_len(sg) > TSI721_DMAD_BCOUNT1 + 1) {
> > + dev_err(chan->dchan.device->dev, "SG element is too
> large\n");
> > + return -EINVAL;
> > + }
> > +
> > + dev_dbg(chan->dchan.device->dev,
> > + "desc: 0x%llx, addr: 0x%llx len: 0x%x\n",
> > + (u64)desc->txd.phys, (unsigned long
> long)sg_dma_address(sg),
> > + sg_dma_len(sg));
> > +
> > + dev_dbg(chan->dchan.device->dev, "bd_ptr =3D %p did=3D%d
> raddr=3D0x%llx\n",
> > + bd_ptr, desc->destid, desc->rio_addr);
> > +
> > + /* Initialize DMA descriptor */
> > + bd_ptr->type_id =3D cpu_to_le32((DTYPE1 << 29) |
> > + (rtype << 19) | desc->destid);
> > + if (desc->interrupt)
> > + bd_ptr->type_id |=3D cpu_to_le32(TSI721_DMAD_IOF);
> > + bd_ptr->bcount =3D cpu_to_le32(((desc->rio_addr & 0x3) << 30) |
> > + (sys_size << 26) |
sg_dma_len(sg));
> > + rio_addr =3D (desc->rio_addr >> 2) |
> > + ((u64)(desc->rio_addr_u & 0x3) << 62);
> > + bd_ptr->raddr_lo =3D cpu_to_le32(rio_addr & 0xffffffff);
> > + bd_ptr->raddr_hi =3D cpu_to_le32(rio_addr >> 32);
> > + bd_ptr->t1.bufptr_lo =3D cpu_to_le32(
> > + (u64)sg_dma_address(sg) &
0xffffffff);
> > + bd_ptr->t1.bufptr_hi =3D cpu_to_le32((u64)sg_dma_address(sg) >>
> 32);
> > + bd_ptr->t1.s_dist =3D 0;
> > + bd_ptr->t1.s_size =3D 0;
> > +
> > + mb();
>=20
> Mystery barrier needs a comment explaining why it's here, please.
This
> is almost always the case with barriers.
Marked for review.
>=20
> > + return 0;
> > +}
> > +
> >
> > ...
> >
> > +static int tsi721_alloc_chan_resources(struct dma_chan *dchan)
> > +{
> > + struct tsi721_bdma_chan *chan =3D to_tsi721_chan(dchan);
> > + struct tsi721_device *priv =3D to_tsi721(dchan->device);
> > + struct tsi721_tx_desc *desc =3D NULL;
> > + LIST_HEAD(tmp_list);
> > + int i;
> > + int rc;
> > +
> > + if (chan->bd_base)
> > + return chan->bd_num - 1;
> > +
> > + /* Initialize BDMA channel */
> > + if (tsi721_bdma_ch_init(chan)) {
> > + dev_err(dchan->device->dev, "Unable to initialize data
DMA"
> > + " channel %d, aborting\n", chan->id);
> > + return -ENOMEM;
> > + }
> > +
> > + /* Allocate matching number of logical descriptors */
> > + desc =3D kzalloc((chan->bd_num - 1) * sizeof(struct
> tsi721_tx_desc),
> > + GFP_KERNEL);
>=20
> kcalloc() would be a better fit here.
Agree. Would look more clear.
>=20
> > + if (!desc) {
> > + dev_err(dchan->device->dev,
> > + "Failed to allocate logical descriptors\n");
> > + rc =3D -ENOMEM;
> > + goto err_out;
> > + }
> > +
> > + chan->tx_desc =3D desc;
> > +
> > + for (i =3D 0; i < chan->bd_num - 1; i++) {
> > + dma_async_tx_descriptor_init(&desc[i].txd, dchan);
> > + desc[i].txd.tx_submit =3D tsi721_tx_submit;
> > + desc[i].txd.flags =3D DMA_CTRL_ACK;
> > + INIT_LIST_HEAD(&desc[i].tx_list);
> > + list_add_tail(&desc[i].desc_node, &tmp_list);
> > + }
> > +
> > + spin_lock_bh(&chan->lock);
> > + list_splice(&tmp_list, &chan->free_list);
> > + chan->completed_cookie =3D dchan->cookie =3D 1;
> > + spin_unlock_bh(&chan->lock);
> > +
> > +#ifdef CONFIG_PCI_MSI
> > + if (priv->flags & TSI721_USING_MSIX) {
> > + /* Request interrupt service if we are in MSI-X mode */
> > + rc =3D request_irq(
> > + priv->msix[TSI721_VECT_DMA0_DONE +
chan->id].vector,
> > + tsi721_bdma_msix, 0,
> > + priv->msix[TSI721_VECT_DMA0_DONE + chan-
> >id].irq_name,
> > + (void *)chan);
> > +
> > + if (rc) {
> > + dev_dbg(dchan->device->dev,
> > + "Unable to allocate MSI-X interrupt for
"
> > + "BDMA%d-DONE\n", chan->id);
> > + goto err_out;
> > + }
> > +
> > + rc =3D request_irq(priv->msix[TSI721_VECT_DMA0_INT +
> > + chan->id].vector,
> > + tsi721_bdma_msix, 0,
> > + priv->msix[TSI721_VECT_DMA0_INT +
chan->id].irq_name,
> > + (void *)chan);
> > +
> > + if (rc) {
> > + dev_dbg(dchan->device->dev,
> > + "Unable to allocate MSI-X interrupt for
"
> > + "BDMA%d-INT\n", chan->id);
> > + free_irq(
> > + priv->msix[TSI721_VECT_DMA0_DONE +
> > + chan->id].vector,
> > + (void *)chan);
> > + rc =3D -EIO;
> > + goto err_out;
> > + }
> > + }
> > +#endif /* CONFIG_PCI_MSI */
> > +
> > + tsi721_bdma_interrupt_enable(chan, 1);
> > +
> > + return chan->bd_num - 1;
> > +
> > +err_out:
> > + kfree(desc);
> > + tsi721_bdma_ch_free(chan);
> > + return rc;
> > +}
> > +
> >
> > ...
> >
> > +static
> > +enum dma_status tsi721_tx_status(struct dma_chan *dchan,
> dma_cookie_t cookie,
> > + struct dma_tx_state *txstate)
> > +{
> > + struct tsi721_bdma_chan *bdma_chan =3D to_tsi721_chan(dchan);
> > + dma_cookie_t last_used;
> > + dma_cookie_t last_completed;
> > + int ret;
> > +
> > + spin_lock_irq(&bdma_chan->lock);
> > + last_completed =3D bdma_chan->completed_cookie;
> > + last_used =3D dchan->cookie;
> > + spin_unlock_irq(&bdma_chan->lock);
> > +
> > + ret =3D dma_async_is_complete(cookie, last_completed, last_used);
> > +
> > + dma_set_tx_state(txstate, last_completed, last_used, 0);
> > +
> > + dev_dbg(dchan->device->dev,
> > + "%s: exit, ret: %d, last_completed: %d, last_used:
%d\n",
> > + __func__, ret, last_completed, last_used);
> > +
> > + return ret;
> > +}
> > +
> > +static void tsi721_issue_pending(struct dma_chan *dchan)
> > +{
> > + struct tsi721_bdma_chan *chan =3D to_tsi721_chan(dchan);
> > +
> > + dev_dbg(dchan->device->dev, "%s: Entry\n", __func__);
> > +
> > + if (tsi721_dma_is_idle(chan)) {
> > + spin_lock_bh(&chan->lock);
> > + tsi721_advance_work(chan);
> > + spin_unlock_bh(&chan->lock);
> > + } else
> > + dev_dbg(dchan->device->dev,
> > + "%s: DMA channel still busy\n", __func__);
> > +}
>=20
> I really don't like that a "struct tsi721_bdma_chan *" is called
"chan"
> in come places and "bdma_chan" in others. "bdma_chan" is better.
>=20
Agree. "bdma_chan" gives more device-specific meaning.
Opposite comment that I have heard was that this driver uses "dma" too
much.
Will unify to "bdma_chan".
> The code takes that lock with spin_lock_bh() in some places and
> spin_lock_irq() in others. I trust there's some method to it all ;)
> Has
> it been carefully tested with lockdep enabled?
Ooops. Another prove that global replace does not work.
Cleared spin_lock_irqsave() well though ;)
lockdep is enabled on my test machine and it did not complain in
this case. I am using a test adopted from one in dmaengine and it
calls both routines that have spin_lock_irq().=20
>=20
> >
> > ...
> >
Thank you,
Alex.
^ permalink raw reply
* [PATCH] libata: Convert LED disk trigger from IDE to libata
From: Jörg Sommer @ 2011-10-03 16:07 UTC (permalink / raw)
To: Benjamin Herrenschmidt, linuxppc-dev, linux-kernel, linux-ide,
Richard Purdie, David S. Miller, Jeff Garzik, Paul Mackerras
Cc: Jörg Sommer
This patch converts the trigger for the LED at the front of Apple's
iBooks to libata. It's merely a replacement of the string ide by disk.
The patch is taken from http://dev.gentoo.org/~josejx/ata.patch. I've
asked Joseph Jezak if he intends to send this patch upstream, but as he
did not reply I'll do so.
---
arch/powerpc/configs/pmac32_defconfig | 2 +-
arch/powerpc/configs/ppc6xx_defconfig | 2 +-
drivers/ata/libata-core.c | 4 ++
drivers/ide/ide-disk.c | 2 +-
drivers/leds/Kconfig | 10 +++---
drivers/leds/Makefile | 2 +-
drivers/leds/ledtrig-disk.c | 64 +++++++++++++++++++++++++++++++++
drivers/leds/ledtrig-ide-disk.c | 64 ---------------------------------
drivers/macintosh/Kconfig | 11 +++---
drivers/macintosh/via-pmu-led.c | 4 +-
include/linux/leds.h | 6 ++--
11 files changed, 87 insertions(+), 84 deletions(-)
create mode 100644 drivers/leds/ledtrig-disk.c
delete mode 100644 drivers/leds/ledtrig-ide-disk.c
diff --git a/arch/powerpc/configs/pmac32_defconfig b/arch/powerpc/configs/pmac32_defconfig
index f8b394a..cff5d4e 100644
--- a/arch/powerpc/configs/pmac32_defconfig
+++ b/arch/powerpc/configs/pmac32_defconfig
@@ -180,7 +180,7 @@ CONFIG_ADB=y
CONFIG_ADB_CUDA=y
CONFIG_ADB_PMU=y
CONFIG_ADB_PMU_LED=y
-CONFIG_ADB_PMU_LED_IDE=y
+CONFIG_ADB_PMU_LED_DISK=y
CONFIG_PMAC_APM_EMU=m
CONFIG_PMAC_MEDIABAY=y
CONFIG_PMAC_BACKLIGHT=y
diff --git a/arch/powerpc/configs/ppc6xx_defconfig b/arch/powerpc/configs/ppc6xx_defconfig
index 04360f9..c56464d 100644
--- a/arch/powerpc/configs/ppc6xx_defconfig
+++ b/arch/powerpc/configs/ppc6xx_defconfig
@@ -471,7 +471,7 @@ CONFIG_ADB=y
CONFIG_ADB_CUDA=y
CONFIG_ADB_PMU=y
CONFIG_ADB_PMU_LED=y
-CONFIG_ADB_PMU_LED_IDE=y
+CONFIG_ADB_PMU_LED_DISK=y
CONFIG_PMAC_APM_EMU=y
CONFIG_PMAC_MEDIABAY=y
CONFIG_PMAC_BACKLIGHT=y
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index 4a3a5ae..d37e3a2 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -66,6 +66,7 @@
#include <asm/byteorder.h>
#include <linux/cdrom.h>
#include <linux/ratelimit.h>
+#include <linux/leds.h>
#include "libata.h"
#include "libata-transport.h"
@@ -4823,6 +4824,9 @@ void ata_qc_complete(struct ata_queued_cmd *qc)
{
struct ata_port *ap = qc->ap;
+ /* Trigger the LED (if available) */
+ ledtrig_disk_activity();
+
/* XXX: New EH and old EH use different mechanisms to
* synchronize EH with regular execution path.
*
diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c
index 2747980..cb25bd6 100644
--- a/drivers/ide/ide-disk.c
+++ b/drivers/ide/ide-disk.c
@@ -186,7 +186,7 @@ static ide_startstop_t ide_do_rw_disk(ide_drive_t *drive, struct request *rq,
BUG_ON(drive->dev_flags & IDE_DFLAG_BLOCKED);
BUG_ON(rq->cmd_type != REQ_TYPE_FS);
- ledtrig_ide_activity();
+ ledtrig_disk_activity();
pr_debug("%s: %sing: block=%llu, sectors=%u, buffer=0x%08lx\n",
drive->name, rq_data_dir(rq) == READ ? "read" : "writ",
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index b591e72..1934d8a 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -404,12 +404,12 @@ config LEDS_TRIGGER_TIMER
If unsure, say Y.
-config LEDS_TRIGGER_IDE_DISK
- bool "LED IDE Disk Trigger"
- depends on IDE_GD_ATA
- depends on LEDS_TRIGGERS
+config LEDS_TRIGGER_DISK
+ bool "LED Disk Trigger"
+ depends on IDE_GD_ATA || ATA
+ depends on LEDS_TRIGGERS
help
- This allows LEDs to be controlled by IDE disk activity.
+ This allows LEDs to be controlled by disk activity.
If unsure, say Y.
config LEDS_TRIGGER_HEARTBEAT
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index bbfd2e3..350cc89 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -49,7 +49,7 @@ obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o
# LED Triggers
obj-$(CONFIG_LEDS_TRIGGER_TIMER) += ledtrig-timer.o
-obj-$(CONFIG_LEDS_TRIGGER_IDE_DISK) += ledtrig-ide-disk.o
+obj-$(CONFIG_LEDS_TRIGGER_DISK) += ledtrig-disk.o
obj-$(CONFIG_LEDS_TRIGGER_HEARTBEAT) += ledtrig-heartbeat.o
obj-$(CONFIG_LEDS_TRIGGER_BACKLIGHT) += ledtrig-backlight.o
obj-$(CONFIG_LEDS_TRIGGER_GPIO) += ledtrig-gpio.o
diff --git a/drivers/leds/ledtrig-disk.c b/drivers/leds/ledtrig-disk.c
new file mode 100644
index 0000000..0ebb2eb
--- /dev/null
+++ b/drivers/leds/ledtrig-disk.c
@@ -0,0 +1,64 @@
+/*
+ * LED Disk Activity Trigger
+ *
+ * Copyright 2006 Openedhand Ltd.
+ *
+ * Author: Richard Purdie <rpurdie@openedhand.com>
+ *
+ * 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/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/leds.h>
+
+static void ledtrig_disk_timerfunc(unsigned long data);
+
+DEFINE_LED_TRIGGER(ledtrig_disk);
+static DEFINE_TIMER(ledtrig_disk_timer, ledtrig_disk_timerfunc, 0, 0);
+static int disk_activity;
+static int disk_lastactivity;
+
+void ledtrig_disk_activity(void)
+{
+ disk_activity++;
+ if (!timer_pending(&ledtrig_disk_timer))
+ mod_timer(&ledtrig_disk_timer, jiffies + msecs_to_jiffies(10));
+}
+EXPORT_SYMBOL(ledtrig_disk_activity);
+
+static void ledtrig_disk_timerfunc(unsigned long data)
+{
+ if (disk_lastactivity != disk_activity) {
+ disk_lastactivity = disk_activity;
+ /* INT_MAX will set each LED to its maximum brightness */
+ led_trigger_event(ledtrig_disk, INT_MAX);
+ mod_timer(&ledtrig_disk_timer, jiffies + msecs_to_jiffies(10));
+ } else {
+ led_trigger_event(ledtrig_disk, LED_OFF);
+ }
+}
+
+static int __init ledtrig_disk_init(void)
+{
+ led_trigger_register_simple("disk-activity", &ledtrig_disk);
+ return 0;
+}
+
+static void __exit ledtrig_disk_exit(void)
+{
+ led_trigger_unregister_simple(ledtrig_disk);
+}
+
+module_init(ledtrig_disk_init);
+module_exit(ledtrig_disk_exit);
+
+MODULE_AUTHOR("Richard Purdie <rpurdie@openedhand.com>");
+MODULE_DESCRIPTION("LED Disk Activity Trigger");
+MODULE_LICENSE("GPL");
diff --git a/drivers/leds/ledtrig-ide-disk.c b/drivers/leds/ledtrig-ide-disk.c
deleted file mode 100644
index ec099fc..0000000
--- a/drivers/leds/ledtrig-ide-disk.c
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * LED IDE-Disk Activity Trigger
- *
- * Copyright 2006 Openedhand Ltd.
- *
- * Author: Richard Purdie <rpurdie@openedhand.com>
- *
- * 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/jiffies.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/timer.h>
-#include <linux/leds.h>
-
-static void ledtrig_ide_timerfunc(unsigned long data);
-
-DEFINE_LED_TRIGGER(ledtrig_ide);
-static DEFINE_TIMER(ledtrig_ide_timer, ledtrig_ide_timerfunc, 0, 0);
-static int ide_activity;
-static int ide_lastactivity;
-
-void ledtrig_ide_activity(void)
-{
- ide_activity++;
- if (!timer_pending(&ledtrig_ide_timer))
- mod_timer(&ledtrig_ide_timer, jiffies + msecs_to_jiffies(10));
-}
-EXPORT_SYMBOL(ledtrig_ide_activity);
-
-static void ledtrig_ide_timerfunc(unsigned long data)
-{
- if (ide_lastactivity != ide_activity) {
- ide_lastactivity = ide_activity;
- /* INT_MAX will set each LED to its maximum brightness */
- led_trigger_event(ledtrig_ide, INT_MAX);
- mod_timer(&ledtrig_ide_timer, jiffies + msecs_to_jiffies(10));
- } else {
- led_trigger_event(ledtrig_ide, LED_OFF);
- }
-}
-
-static int __init ledtrig_ide_init(void)
-{
- led_trigger_register_simple("ide-disk", &ledtrig_ide);
- return 0;
-}
-
-static void __exit ledtrig_ide_exit(void)
-{
- led_trigger_unregister_simple(ledtrig_ide);
-}
-
-module_init(ledtrig_ide_init);
-module_exit(ledtrig_ide_exit);
-
-MODULE_AUTHOR("Richard Purdie <rpurdie@openedhand.com>");
-MODULE_DESCRIPTION("LED IDE Disk Activity Trigger");
-MODULE_LICENSE("GPL");
diff --git a/drivers/macintosh/Kconfig b/drivers/macintosh/Kconfig
index fa51af1..aeb7b2a 100644
--- a/drivers/macintosh/Kconfig
+++ b/drivers/macintosh/Kconfig
@@ -96,17 +96,16 @@ config ADB_PMU_LED
Support the front LED on Power/iBooks as a generic LED that can
be triggered by any of the supported triggers. To get the
behaviour of the old CONFIG_BLK_DEV_IDE_PMAC_BLINK, select this
- and the ide-disk LED trigger and configure appropriately through
- sysfs.
+ and the disk LED trigger and configure appropriately through sysfs.
-config ADB_PMU_LED_IDE
- bool "Use front LED as IDE LED by default"
+config ADB_PMU_LED_DISK
+ bool "Use front LED as a disk LED by default"
depends on ADB_PMU_LED
depends on LEDS_CLASS
select LEDS_TRIGGERS
- select LEDS_TRIGGER_IDE_DISK
+ select LEDS_TRIGGER_DISK
help
- This option makes the front LED default to the IDE trigger
+ This option makes the front LED default to the disk trigger
so that it blinks on IDE activity.
config PMAC_SMU
diff --git a/drivers/macintosh/via-pmu-led.c b/drivers/macintosh/via-pmu-led.c
index 19c3718..ae067ab 100644
--- a/drivers/macintosh/via-pmu-led.c
+++ b/drivers/macintosh/via-pmu-led.c
@@ -73,8 +73,8 @@ static void pmu_led_set(struct led_classdev *led_cdev,
static struct led_classdev pmu_led = {
.name = "pmu-led::front",
-#ifdef CONFIG_ADB_PMU_LED_IDE
- .default_trigger = "ide-disk",
+#ifdef CONFIG_ADB_PMU_LED_DISK
+ .default_trigger = "disk-activity",
#endif
.brightness_set = pmu_led_set,
};
diff --git a/include/linux/leds.h b/include/linux/leds.h
index 5884def..3413358 100644
--- a/include/linux/leds.h
+++ b/include/linux/leds.h
@@ -161,10 +161,10 @@ extern void led_trigger_blink(struct led_trigger *trigger,
#endif
/* Trigger specific functions */
-#ifdef CONFIG_LEDS_TRIGGER_IDE_DISK
-extern void ledtrig_ide_activity(void);
+#ifdef CONFIG_LEDS_TRIGGER_DISK
+extern void ledtrig_disk_activity(void);
#else
-#define ledtrig_ide_activity() do {} while(0)
+#define ledtrig_disk_activity() do {} while(0)
#endif
/*
--
1.7.6.3
^ permalink raw reply related
* Re: Request_irq fails for IRQ2
From: Scott Wood @ 2011-10-03 17:24 UTC (permalink / raw)
To: smitha.vanga; +Cc: linuxppc-dev
In-Reply-To: <40631E9A2581F14BA60888C87A76A1FE0145F8@HYD-MKD-MBX4.wipro.com>
On 10/03/2011 09:27 AM, smitha.vanga@wipro.com wrote:
> I just don't know what I should pass for host , also when I pass NULL
> for host . I see the default host is NULL..
> Could you please help me. My project delivery is near , I need help soon.
Ideally you should just use the device tree to describe this device,
then you wouldn't have to deal with finding the pointer to the controller.
Otherwise, modify the cpm2_pic driver to either call
irq_set_default_host() (in which case NULL should work) or to make
cpm2_pic_host non-static (in which case you should pass that).
-Scott
^ permalink raw reply
* RE: [RFC PATCH 1/2] RapidIO: Add DMA Engine support for RIO data transfers
From: Bounine, Alexandre @ 2011-10-03 16:52 UTC (permalink / raw)
To: Vinod Koul, Dan; +Cc: linux-kernel, akpm, linuxppc-dev
In-Reply-To: <1317492077.1573.1839.camel@vkoul-udesk3>
Vmlub2QgS291bCB3cm90ZToNCj4gDQo+IE9uIEZyaSwgMjAxMS0wOS0zMCBhdCAxNzozOCAtMDQw
MCwgQWxleGFuZHJlIEJvdW5pbmUgd3JvdGU6DQo+IFBsZWFzZSBDQyAqbWFpbnRhaW5lcnMqIG9u
IHlvdXIgcGF0Y2hlcywgZ2V0X21haW50YWluZXJzLnBsIHdpbGwgdGVsbA0KPiB5b3Ugd2hvLiBB
ZGRpbmcgRGFuIGhlcmUNCg0KQmFzZWQgb24gaHR0cHM6Ly9sa21sLm9yZy9sa21sLzIwMTEvMi8x
NC82NyBhbmQgdXNlIG9mIERNQV9TTEFWRSBpbiB0aGlzDQpwYXRjaCBJIGRlY2lkZWQgdGhhdCB5
b3UgYXJlIHRoZSBiZXN0IG1hdGNoIGFtb25nIHR3byBhbmQgdGhlcmUgaXMgbm8gcmVhc29uDQp0
byBkaXN0dXJiIERhbiA7KSANCg0KPiA+IEFkZHMgRE1BIEVuZ2luZSBmcmFtZXdvcmsgc3VwcG9y
dCBpbnRvIFJhcGlkSU8gc3Vic3lzdGVtLg0KPiA+IFVzZXMgRE1BIEVuZ2luZSBETUFfU0xBVkUg
aW50ZXJmYWNlIHRvIGdlbmVyYXRlIGRhdGEgdHJhbnNmZXJzIHRvL2Zyb20gcmVtb3RlDQo+ID4g
UmFwaWRJTyB0YXJnZXQgZGV2aWNlcy4gVXNlcyBzY2F0dGVybGlzdCB0byBkZXNjcmliZSBsb2Nh
bCBkYXRhIGJ1ZmZlciBhbmQNCj4gPiBkbWFfY2hhbi5wcml2YXRlIG1lbWJlciB0byBwYXNzIHRh
cmdldCBzcGVjaWZpYyBpbmZvcm1hdGlvbi4gU3VwcG9ydHMgZmxhdA0KPiA+IGRhdGEgYnVmZmVy
IG9ubHkgZm9yIGEgcmVtb3RlIHNpZGUuDQo+IFRoZSB3YXkgZG1hZW5naW5lIHdvcmtzIHRvZGF5
IGlzIHRoYXQgaXQgZG9lc24ndCBrbm93IGFueXRoaW5nIGFib3V0DQo+IGNsaWVudCBzdWJzeXN0
ZW0uIEJ1dCB0aGlzIGJyaW5ncyBpbiBhIHN1YnN5c3RlbSBkZXRhaWxzIHRvIGRtYWVuZ2luZQ0K
PiB3aGljaCBJIGRvbid0IGFncmVlIHdpdGggeWV0Lg0KPiBXaHkgY2FuJ3Qgd2UgYWJzdHJhY3Qg
dGhpcyBvdXQ/Pw0KDQpUaGUgb25seSB0aGluZyB0aGF0IGJyaW5ncyBzdWJzeXN0ZW0ga25vd2xl
ZGdlIGludG8gZG1hZW5naW5lIGlzIERNQV9SQVBJRElPIGZsYWcuDQpJIGFtIGFjdHVhbGx5IG9u
IHRoZSBmZW5jZSBhYm91dCB0aGlzLiBGcm9tIFJhcGlkSU8gc2lkZSBwb2ludCBvZiB2aWV3IEkg
ZG8gbm90DQpuZWVkIHRoYXQgZmxhZyBhdCBhbGwuDQpSYXBpZElPIHVzZXMgYSBmaWx0ZXIgcm91
dGluZSB0aGF0IGlzIHN1ZmZpY2llbnQgdG8gaWRlbnRpZnkgZG1hZW5naW5lIGNoYW5uZWxzDQph
c3NvY2lhdGVkIHdpdGggc3BlY2lmaWMgUmFwaWRJTyBtcG9ydC4gVXNlIG9mIERNQV9TTEFWRSBm
bGFnIGlzIHNhZmUgaGVyZS4NClVzZSBvZiBwcml2YXRlIG1lbWJlciBvZiBkbWFfY2hhbiBpcyAi
cHJpdmF0ZSIgYnVzaW5lc3Mgb2YgUmFwaWRJTyBhbmQgZG9lcw0Kbm90IGJyZWFrIGFueXRoaW5n
LiANCg0KTXkgY29uY2VybiBoZXJlIGlzIHRoYXQgb3RoZXIgc3Vic3lzdGVtcyBtYXkgdXNlL3Jl
cXVlc3QgRE1BX1NMQVZFIGNoYW5uZWwocykgYXMgd2VsbA0KYW5kIHdyb25nZnVsbHkgYWNxdWly
ZSBvbmUgdGhhdCBiZWxvbmdzIHRvIFJhcGlkSU8uIEluIHRoaXMgY2FzZSBzZXBhcmF0aW9uIHdp
dGggYW5vdGhlcg0KZmxhZyBtYXkgaGF2ZSBhIHNlbnNlIC0gaXQgaXMgcG9zc2libGUgdG8gaGF2
ZSBhIHN5c3RlbSB0aGF0IHVzZXMgUmFwaWRJTw0KYW5kIG90aGVyICJ0cmFkaXRpb25hbCIgRE1B
IHNsYXZlIGNoYW5uZWwuDQoNClRoaXMgaXMgd2h5IEkgcHV0IHRoYXQgcHJvcG9zZWQgaW50ZXJm
YWNlIGZvciBkaXNjdXNzaW9uIGluc3RlYWQgb2Yga2VlcGluZyBldmVyeXRoaW5nDQppbnNpZGUg
b2YgUmFwaWRJTy4NCklmIHlvdSB0aGluayB0aGF0IHNpdHVhdGlvbiBhYm92ZSB3aWxsIG5vdCBo
YXBwZW4gSSB3aWxsIGJlIGhhcHB5IHRvIHJlbW92ZQ0KdGhhdCBzdWJzeXN0ZW0ga25vd2xlZGdl
IGZyb20gZG1hZW5naW5lIGZpbGVzLg0KDQpNeSBtb3N0IHJlY2VudCB0ZXN0IGltcGxlbWVudGF0
aW9uIHJ1bnMgd2l0aG91dCBETUFfUkFQSURJTyBmbGFnIHRob3VnaC4NCg0KPiANCj4gQWZ0ZXIg
Z29pbmcgdGhydSB0aGUgcGF0Y2gsIEkgZG8gbm90IGJlbGlldmUgdGhhdCB0aGlzIHRoaXMgaXMg
Y2FzZSBvZg0KPiBTTEFWRSB0cmFuc2ZlcnMsIERhbiBjYW4geW91IHBsZWFzZSB0YWtlIGEgbG9v
ayBhdCB0aGlzIHBhdGNoDQoNCkkgYWdyZWUsIHRoaXMgaXMgbm90IGEgY2FzZSBvZiAicHVyZSIg
c2xhdmUgdHJhbnNmZXJzIGJ1dCBleGlzdGluZyBETUFfU0xBVkUNCmludGVyZmFjZSBmaXRzIHdl
bGwgaW50byB0aGUgUmFwaWRJTyBvcGVyYXRpb25zLg0KDQpGaXJzdCwgd2UgaGF2ZSBvbmx5IG9u
ZSBtZW1vcnkgbWFwcGVkIGxvY2F0aW9uIG9uIHRoZSBob3N0IHNpZGUuIFdlIHRyYW5zZmVyDQpk
YXRhIHRvL2Zyb20gbG9jYXRpb24gdGhhdCBpcyBub3QgbWFwcGVkIGludG8gbWVtb3J5IG9uIHRo
ZSBzYW1lIHNpZGUuICANCg0KU2Vjb25kLCBoYXZpbmcgYWJpbGl0eSB0byBwYXNzIHByaXZhdGUg
dGFyZ2V0IGluZm9ybWF0aW9uIGFsbG93cyBtZSB0byBwYXNzDQppbmZvcm1hdGlvbiBhYm91dCBy
ZW1vdGUgdGFyZ2V0IGRldmljZSBvbiBwZXItdHJhbnNmZXIgYmFzaXMuDQoNCj4gDQo+IA0KPiA+
IFNpZ25lZC1vZmYtYnk6IEFsZXhhbmRyZSBCb3VuaW5lIDxhbGV4YW5kcmUuYm91bmluZUBpZHQu
Y29tPg0KPiA+IENjOiBWaW5vZCBLb3VsIDx2aW5vZC5rb3VsQGludGVsLmNvbT4NCj4gPiBDYzog
S3VtYXIgR2FsYSA8Z2FsYWtAa2VybmVsLmNyYXNoaW5nLm9yZz4NCj4gPiBDYzogTWF0dCBQb3J0
ZXIgPG1wb3J0ZXJAa2VybmVsLmNyYXNoaW5nLm9yZz4NCj4gPiBDYzogTGkgWWFuZyA8bGVvbGlA
ZnJlZXNjYWxlLmNvbT4NCj4gPiAtLS0NCj4gPiAgZHJpdmVycy9kbWEvZG1hZW5naW5lLmMgICB8
ICAgIDQgKysNCi4uLiBza2lwIC4uLg0KPiA+ICsjaWZkZWYgQ09ORklHX1JBUElESU9fRE1BX0VO
R0lORQ0KPiA+ICsNCj4gPiArI2luY2x1ZGUgPGxpbnV4L2RtYWVuZ2luZS5oPg0KPiA+ICsNCi4u
LiBza2lwIC4uLg0KPiA+ICsgKi8NCj4gPiArc3RydWN0IGRtYV9hc3luY190eF9kZXNjcmlwdG9y
ICpyaW9fZG1hX3ByZXBfc2xhdmVfc2coc3RydWN0IHJpb19kZXYgKnJkZXYsDQo+ID4gKwlzdHJ1
Y3QgZG1hX2NoYW4gKmRjaGFuLCBzdHJ1Y3QgcmlvX2RtYV9kYXRhICpkYXRhLA0KPiA+ICsJZW51
bSBkbWFfZGF0YV9kaXJlY3Rpb24gZGlyZWN0aW9uLCB1bnNpZ25lZCBsb25nIGZsYWdzKQ0KPiA+
ICt7DQo+ID4gKwlzdHJ1Y3QgZG1hX2FzeW5jX3R4X2Rlc2NyaXB0b3IgKnR4ZCA9IE5VTEw7DQo+
ID4gKwlzdHJ1Y3QgcmlvX2RtYV9leHQgcmlvX2V4dDsNCj4gPiArDQo+ID4gKwlyaW9fZXh0LmRl
c3RpZCA9IHJkZXYtPmRlc3RpZDsNCj4gPiArCXJpb19leHQucmlvX2FkZHJfdSA9IGRhdGEtPnJp
b19hZGRyX3U7DQo+ID4gKwlyaW9fZXh0LnJpb19hZGRyID0gZGF0YS0+cmlvX2FkZHI7DQo+ID4g
KwlyaW9fZXh0LndyX3R5cGUgPSBkYXRhLT53cl90eXBlOw0KPiA+ICsJZGNoYW4tPnByaXZhdGUg
PSAmcmlvX2V4dDsNCj4gPiArDQo+ID4gKwl0eGQgPSBkY2hhbi0+ZGV2aWNlLT5kZXZpY2VfcHJl
cF9zbGF2ZV9zZyhkY2hhbiwgZGF0YS0+c2csIGRhdGEtDQo+ID5zZ19sZW4sDQo+ID4gKwkJCQkJ
CSAgZGlyZWN0aW9uLCBmbGFncyk7DQo+ID4gKw0KPiA+ICsJcmV0dXJuIHR4ZDsNCj4gPiArfQ0K
PiA+ICtFWFBPUlRfU1lNQk9MX0dQTChyaW9fZG1hX3ByZXBfc2xhdmVfc2cpOw0KPiBZb3Ugc2hv
dWxkIG1vdmUgdGhlIHJkZXYgYW5kIGRhdGEgdG8gZG1hX3NsYXZlX2NvbmZpZywgdGhhdCB3YXkg
eW91DQo+IHNob3VsZCBiZSBhYmxlIHRvIHVzZSB0aGUgZXhpc3RpbmcgX3ByZXBfc2xhdmVfc2cg
ZnVuY3Rpb24uDQogDQpSYXBpZElPIG5ldHdvcmsgdXN1YWxseSBoYXMgbW9yZSB0aGFuIG9uZSBk
ZXZpY2UgYXR0YWNoZWQgdG8gaXQgYW5kDQpzaW5nbGUgRE1BIGNoYW5uZWwgbWF5IHNlcnZpY2Ug
ZGF0YSB0cmFuc2ZlcnMgdG8vZnJvbSBzZXZlcmFsIGRldmljZXMuDQpJbiB0aGlzIGNhc2UgZGV2
aWNlIGluZm9ybWF0aW9uIHNob3VsZCBiZSBwYXNzZWQgb24gcGVyLXRyYW5zZmVyIGJhc2lzLg0K
DQo+ID4gKw0KPiA+ICsjZW5kaWYgLyogQ09ORklHX1JBUElESU9fRE1BX0VOR0lORSAqLw0KPiA+
ICsNCi4uLiBza2lwIC4uLg0KPiA+ICsgKg0KPiA+ICsgKiBOb3RlOiBSYXBpZElPIHNwZWNpZmlj
YXRpb24gZGVmaW5lcyB3cml0ZSAoTldSSVRFKSBhbmQNCj4gPiArICogd3JpdGUtd2l0aC1yZXNw
b25zZSAoTldSSVRFX1IpIGRhdGEgdHJhbnNmZXIgb3BlcmF0aW9ucy4NCj4gPiArICogRXhpc3Rp
bmcgRE1BIGNvbnRyb2xsZXJzIHRoYXQgc2VydmljZSBSYXBpZElPIG1heSB1c2Ugb25lIG9mIHRo
ZXNlIG9wZXJhdGlvbnMNCj4gPiArICogZm9yIGVudGlyZSBkYXRhIHRyYW5zZmVyIG9yIHRoZWly
IGNvbWJpbmF0aW9uIHdpdGggb25seSB0aGUgbGFzdCBkYXRhIHBhY2tldA0KPiA+ICsgKiByZXF1
aXJlcyByZXNwb25zZS4NCj4gPiArICovDQo+ID4gK2VudW0gcmlvX3dyaXRlX3R5cGUgew0KPiA+
ICsJUkRXX0RFRkFVTFQsCQkvKiBkZWZhdWx0IG1ldGhvZCB1c2VkIGJ5IERNQSBkcml2ZXIgKi8N
Cj4gPiArCVJEV19BTExfTldSSVRFLAkJLyogYWxsIHBhY2tldHMgdXNlIE5XUklURSAqLw0KPiA+
ICsJUkRXX0FMTF9OV1JJVEVfUiwJLyogYWxsIHBhY2tldHMgdXNlIE5XUklURV9SICovDQo+ID4g
KwlSRFdfTEFTVF9OV1JJVEVfUiwJLyogbGFzdCBwYWNrZXQgdXNlcyBOV1JJVEVfUiwgYWxsIG90
aGVyIC0gTldSSVRFICovDQo+ID4gK307DQo+IFdoeSBub3QgdXNlIHRoZSBjdXJyZW50IG1lY2hh
bmlzbSBvZiBzcGVjaWZ5aW5nIGNhbGxiYWNrIG9yIEFDSyBmbGFncw0KPiBpZiB5b3Ugd2FudCBh
IHJlc3BvbnNlIG9yIG5vdC4NCg0KVGhhdCByZXNwb25zZSBpcyBoYW5kbGVkIGJ5IFJhcGlkSU8g
aGFyZHdhcmUgYW5kIGVuc3VyZXMgcmVsaWFibGUNCnBhY2tldCBkZWxpdmVyeSB3aGVuIHJlc3Bv
bnNlIGlzIHVzZWQuIFVzZXIgbWF5IG5vdCBuZWVkIGNhbGxiYWNrIG9yIEFDSw0KZm9yIGhpcyBv
cGVyYXRpb24gKGluIHRlcm1zIG9mIGRtYWVuZ2luZSkgYnV0IGVycm9yIGhhbmRsaW5nIHdpbGwg
YmUgaW5pdGlhdGVkDQppZiB0aGVyZSBpcyBubyByZXNwb25zZSBmcm9tIHRoZSB0YXJnZXQgZGV2
aWNlLiANCiANCj4gDQo+ID4gKw0KPiA+ICtzdHJ1Y3QgcmlvX2RtYV9leHQgew0KPiA+ICsJdTE2
IGRlc3RpZDsNCj4gPiArCXU2NCByaW9fYWRkcjsJLyogbG93IDY0LWJpdHMgb2YgNjYtYml0IFJh
cGlkSU8gYWRkcmVzcyAqLw0KPiA+ICsJdTggIHJpb19hZGRyX3U7ICAvKiB1cHBlciAyLWJpdHMg
b2YgNjYtYml0IFJhcGlkSU8gYWRkcmVzcyAqLw0KPiA+ICsJZW51bSByaW9fd3JpdGVfdHlwZSB3
cl90eXBlOyAvKiBwcmVmZXJyZWQgUklPIHdyaXRlIG9wZXJhdGlvbiB0eXBlICovDQo+ID4gK307
DQo+IHdpbGwgdGhpcyBhZGRyZXNzIHRyYW5zbGF0ZWQgdG8gYSBkbWFfYWRkcl90IG9yIG5vdD8N
Cg0KTm8uIFRoaXMgaXMgYSBSYXBpZElPIHNwZWNpZmljIGFkZHJlc3Mgb24gdGhlIHJlbW90ZSBk
ZXZpY2UuDQoNCj4gPiArDQo+ID4gK3N0cnVjdCByaW9fZG1hX2RhdGEgew0KPiA+ICsJLyogTG9j
YWwgZGF0YSAoYXMgc2NhdHRlcmxpc3QpICovDQo+ID4gKwlzdHJ1Y3Qgc2NhdHRlcmxpc3QJKnNn
OwkvKiBJL08gc2NhdHRlciBsaXN0ICovDQo+ID4gKwl1bnNpZ25lZCBpbnQJCXNnX2xlbjsJLyog
c2l6ZSBvZiBzY2F0dGVyIGxpc3QgKi8NCj4gPiArCS8qIFJlbW90ZSBkZXZpY2UgYWRkcmVzcyAo
ZmxhdCBidWZmZXIpICovDQouLi4uIHNraXAgLi4uDQo+ID4gICAqIHJpb19uYW1lIC0gR2V0IHRo
ZSB1bmlxdWUgUklPIGRldmljZSBpZGVudGlmaWVyDQo+ID4gICAqIEByZGV2OiBSSU8gZGV2aWNl
DQo+IA0KPiANCj4gLS0NCj4gflZpbm9kDQoNClJlZ2FyZHMsDQoNCkFsZXguDQoNCg==
^ permalink raw reply
* RE: Request_irq fails for IRQ2
From: smitha.vanga @ 2011-10-03 14:27 UTC (permalink / raw)
To: scottwood; +Cc: linuxppc-dev
[-- Attachment #1: Type: text/plain, Size: 2060 bytes --]
Hi Scott,
I try to request an IRQ (IRQ2 and IRQ3 which are ineterrupt no 20 and 21 in mpc8247)in my driver . The
Call fails in setup_irq in Manage.c at /kernel/irq.
Setup _irq returns -ENOSYS
if (desc->irq_data.chip == &no_irq_chip)
return -ENOSYS;
I found that I need to pass the virtual interrupt number instead of hardware interrupt number.
So I added below piece of code
Below is the call to request irq in my driver.
virq = irq_create_mapping(NULL, CPLD1_INTERRUPT);
if ((ret = request_irq(virq,cpld_irq_handler, 0, GPIO_CHAR_PATH, NULL))!=0)
{
printk(KERN_ERR "gpio_init: Could not grab IRQ line for CPLD ret = %d\n",ret);
goto err1;
}
Now it fails in irq_create_mapping with NO_IRQ error.
if (controller == NULL)
host = irq_default_host;
else
host = irq_find_host(controller);
if (host == NULL) {
printk(KERN_WARNING "irq: no irq host found for %s !\n",
controller->full_name);
return NO_IRQ;
}
I just don't know what I should pass for host , also when I pass NULL for host . I see the default host is NULL..
Could you please help me. My project delivery is near , I need help soon.
Regards,
Smitha
Please do not print this email unless it is absolutely necessary.
The information contained in this electronic message and any attachments to this message are intended for the exclusive use of the addressee(s) and may contain proprietary, confidential or privileged information. If you are not the intended recipient, you should not disseminate, distribute or copy this e-mail. Please notify the sender immediately and destroy all copies of this message and any attachments.
WARNING: Computer viruses can be transmitted via email. The recipient should check this email and any attachments for the presence of viruses. The company accepts no liability for any damage caused by any virus transmitted by this email.
www.wipro.com
[-- Attachment #2: Type: text/html, Size: 5718 bytes --]
^ permalink raw reply
* Request_irq fails for IRQ2
From: smitha.vanga @ 2011-10-03 12:31 UTC (permalink / raw)
To: scottwood; +Cc: linuxppc-dev
In-Reply-To: <4E57FD3D.6090809@freescale.com>
[-- Attachment #1: Type: text/plain, Size: 1348 bytes --]
Hi Scott,
I try to request an IRQ (IRQ2 and IRQ3 which are ineterrupt no 20 and 21 in mpc8247)in my driver . The
Call fails in setup_irq in Manage.c at /kernel/irq.
Setup _irq returns -ENOSYS
if (desc->irq_data.chip == &no_irq_chip)
return -ENOSYS;
I just want to know why it fails. Below is the call to request irq in my driver.
#define CPLD1_INTERRUPT 20
if (request_irq(CPLD1_INTERRUPT,cpld_irq_handler, 0, GPIO_CHAR_PATH, NULL))
{
printk(KERN_ERR "gpio_init: Could not grab IRQ line for CPLD\n");
goto err1;
}
Regards,
Smitha
Please do not print this email unless it is absolutely necessary.
The information contained in this electronic message and any attachments to this message are intended for the exclusive use of the addressee(s) and may contain proprietary, confidential or privileged information. If you are not the intended recipient, you should not disseminate, distribute or copy this e-mail. Please notify the sender immediately and destroy all copies of this message and any attachments.
WARNING: Computer viruses can be transmitted via email. The recipient should check this email and any attachments for the presence of viruses. The company accepts no liability for any damage caused by any virus transmitted by this email.
www.wipro.com
[-- Attachment #2: Type: text/html, Size: 2609 bytes --]
^ permalink raw reply
* Re: [RFC PATCH 2/2 -mm] RapidIO: TSI721 Add DMA Engine support
From: Vinod Koul @ 2011-10-01 18:06 UTC (permalink / raw)
To: Alexandre Bounine, Dan; +Cc: linux-kernel, akpm, linuxppc-dev
In-Reply-To: <1317418715-9666-2-git-send-email-alexandre.bounine@idt.com>
On Fri, 2011-09-30 at 17:38 -0400, Alexandre Bounine wrote:
> Adds support for DMA Engine API.
>
> Includes following changes:
> - Modifies BDMA register offset definitions to support per-channel handling
> - Separates BDMA channel reserved for RIO Maintenance requests
> - Adds DMA Engine callback routines
Dan please review this, I donot agree with approach here
>
> Signed-off-by: Alexandre Bounine <alexandre.bounine@idt.com>
> Cc: Vinod Koul <vinod.koul@intel.com>
> Cc: Kumar Gala <galak@kernel.crashing.org>
> Cc: Matt Porter <mporter@kernel.crashing.org>
> Cc: Li Yang <leoli@freescale.com>
> ---
> drivers/rapidio/devices/Kconfig | 8 +
> drivers/rapidio/devices/Makefile | 1 +
> drivers/rapidio/devices/tsi721.c | 201 ++++++----
> drivers/rapidio/devices/tsi721.h | 107 ++++-
> drivers/rapidio/devices/tsi721_dma.c | 802 ++++++++++++++++++++++++++++++++++
> 5 files changed, 1029 insertions(+), 90 deletions(-)
> create mode 100644 drivers/rapidio/devices/tsi721_dma.c
>
> diff --git a/drivers/rapidio/devices/Kconfig b/drivers/rapidio/devices/Kconfig
> index 12a9d7f..3a2db3d 100644
> --- a/drivers/rapidio/devices/Kconfig
> +++ b/drivers/rapidio/devices/Kconfig
> @@ -8,3 +8,11 @@ config RAPIDIO_TSI721
> default "n"
> ---help---
> Include support for IDT Tsi721 PCI Express Serial RapidIO controller.
> +
> +config TSI721_DMA
> + bool "IDT Tsi721 RapidIO DMA support"
> + depends on RAPIDIO_TSI721
> + default "n"
> + select RAPIDIO_DMA_ENGINE
> + help
> + Enable DMA support for IDT Tsi721 PCIe-to-SRIO controller.
> diff --git a/drivers/rapidio/devices/Makefile b/drivers/rapidio/devices/Makefile
> index 3b7b4e2..8cbce45 100644
> --- a/drivers/rapidio/devices/Makefile
> +++ b/drivers/rapidio/devices/Makefile
> @@ -3,3 +3,4 @@
> #
>
> obj-$(CONFIG_RAPIDIO_TSI721) += tsi721.o
> +obj-$(CONFIG_TSI721_DMA) += tsi721_dma.o
> diff --git a/drivers/rapidio/devices/tsi721.c b/drivers/rapidio/devices/tsi721.c
> index 5225930..5e893a6 100644
> --- a/drivers/rapidio/devices/tsi721.c
> +++ b/drivers/rapidio/devices/tsi721.c
> @@ -108,6 +108,7 @@ static int tsi721_maint_dma(struct tsi721_device *priv, u32 sys_size,
> u16 destid, u8 hopcount, u32 offset, int len,
> u32 *data, int do_wr)
> {
> + void __iomem *regs = priv->regs + TSI721_DMAC_BASE(priv->mdma.ch_id);
> struct tsi721_dma_desc *bd_ptr;
> u32 rd_count, swr_ptr, ch_stat;
> int i, err = 0;
> @@ -116,10 +117,9 @@ static int tsi721_maint_dma(struct tsi721_device *priv, u32 sys_size,
> if (offset > (RIO_MAINT_SPACE_SZ - len) || (len != sizeof(u32)))
> return -EINVAL;
>
> - bd_ptr = priv->bdma[TSI721_DMACH_MAINT].bd_base;
> + bd_ptr = priv->mdma.bd_base;
>
> - rd_count = ioread32(
> - priv->regs + TSI721_DMAC_DRDCNT(TSI721_DMACH_MAINT));
> + rd_count = ioread32(regs + TSI721_DMAC_DRDCNT);
>
> /* Initialize DMA descriptor */
> bd_ptr[0].type_id = cpu_to_le32((DTYPE2 << 29) | (op << 19) | destid);
> @@ -134,19 +134,18 @@ static int tsi721_maint_dma(struct tsi721_device *priv, u32 sys_size,
> mb();
>
> /* Start DMA operation */
> - iowrite32(rd_count + 2,
> - priv->regs + TSI721_DMAC_DWRCNT(TSI721_DMACH_MAINT));
> - ioread32(priv->regs + TSI721_DMAC_DWRCNT(TSI721_DMACH_MAINT));
> + iowrite32(rd_count + 2, regs + TSI721_DMAC_DWRCNT);
> + ioread32(regs + TSI721_DMAC_DWRCNT);
> i = 0;
>
> /* Wait until DMA transfer is finished */
> - while ((ch_stat = ioread32(priv->regs +
> - TSI721_DMAC_STS(TSI721_DMACH_MAINT))) & TSI721_DMAC_STS_RUN) {
> + while ((ch_stat = ioread32(regs + TSI721_DMAC_STS))
> + & TSI721_DMAC_STS_RUN) {
> udelay(1);
> if (++i >= 5000000) {
> dev_dbg(&priv->pdev->dev,
> "%s : DMA[%d] read timeout ch_status=%x\n",
> - __func__, TSI721_DMACH_MAINT, ch_stat);
> + __func__, priv->mdma.ch_id, ch_stat);
> if (!do_wr)
> *data = 0xffffffff;
> err = -EIO;
> @@ -162,13 +161,10 @@ static int tsi721_maint_dma(struct tsi721_device *priv, u32 sys_size,
> __func__, ch_stat);
> dev_dbg(&priv->pdev->dev, "OP=%d : destid=%x hc=%x off=%x\n",
> do_wr ? MAINT_WR : MAINT_RD, destid, hopcount, offset);
> - iowrite32(TSI721_DMAC_INT_ALL,
> - priv->regs + TSI721_DMAC_INT(TSI721_DMACH_MAINT));
> - iowrite32(TSI721_DMAC_CTL_INIT,
> - priv->regs + TSI721_DMAC_CTL(TSI721_DMACH_MAINT));
> + iowrite32(TSI721_DMAC_INT_ALL, regs + TSI721_DMAC_INT);
> + iowrite32(TSI721_DMAC_CTL_INIT, regs + TSI721_DMAC_CTL);
> udelay(10);
> - iowrite32(0, priv->regs +
> - TSI721_DMAC_DWRCNT(TSI721_DMACH_MAINT));
> + iowrite32(0, regs + TSI721_DMAC_DWRCNT);
> udelay(1);
> if (!do_wr)
> *data = 0xffffffff;
> @@ -184,8 +180,8 @@ static int tsi721_maint_dma(struct tsi721_device *priv, u32 sys_size,
> * NOTE: Skipping check and clear FIFO entries because we are waiting
> * for transfer to be completed.
> */
> - swr_ptr = ioread32(priv->regs + TSI721_DMAC_DSWP(TSI721_DMACH_MAINT));
> - iowrite32(swr_ptr, priv->regs + TSI721_DMAC_DSRP(TSI721_DMACH_MAINT));
> + swr_ptr = ioread32(regs + TSI721_DMAC_DSWP);
> + iowrite32(swr_ptr, regs + TSI721_DMAC_DSRP);
> err_out:
>
> return err;
> @@ -540,6 +536,22 @@ static irqreturn_t tsi721_irqhandler(int irq, void *ptr)
> tsi721_pw_handler(mport);
> }
>
> +#ifdef CONFIG_TSI721_DMA
> + if (dev_int & TSI721_DEV_INT_BDMA_CH) {
> + int ch;
> +
> + if (dev_ch_int & TSI721_INT_BDMA_CHAN_M) {
> + dev_dbg(&priv->pdev->dev,
> + "IRQ from DMA channel 0x%08x\n", dev_ch_int);
> +
> + for (ch = 0; ch < TSI721_DMA_MAXCH; ch++) {
> + if (!(dev_ch_int & TSI721_INT_BDMA_CHAN(ch)))
> + continue;
> + tsi721_bdma_handler(&priv->bdma[ch]);
> + }
> + }
> + }
> +#endif
> return IRQ_HANDLED;
> }
>
> @@ -552,18 +564,26 @@ static void tsi721_interrupts_init(struct tsi721_device *priv)
> priv->regs + TSI721_SR_CHINT(IDB_QUEUE));
> iowrite32(TSI721_SR_CHINT_IDBQRCV,
> priv->regs + TSI721_SR_CHINTE(IDB_QUEUE));
> - iowrite32(TSI721_INT_SR2PC_CHAN(IDB_QUEUE),
> - priv->regs + TSI721_DEV_CHAN_INTE);
>
> /* Enable SRIO MAC interrupts */
> iowrite32(TSI721_RIO_EM_DEV_INT_EN_INT,
> priv->regs + TSI721_RIO_EM_DEV_INT_EN);
>
> + /* Enable interrupts from channels in use */
> +#ifdef CONFIG_TSI721_DMA
> + intr = TSI721_INT_SR2PC_CHAN(IDB_QUEUE) |
> + (TSI721_INT_BDMA_CHAN_M &
> + ~TSI721_INT_BDMA_CHAN(TSI721_DMACH_MAINT));
> +#else
> + intr = TSI721_INT_SR2PC_CHAN(IDB_QUEUE);
> +#endif
> + iowrite32(intr, priv->regs + TSI721_DEV_CHAN_INTE);
> +
> if (priv->flags & TSI721_USING_MSIX)
> intr = TSI721_DEV_INT_SRIO;
> else
> intr = TSI721_DEV_INT_SR2PC_CH | TSI721_DEV_INT_SRIO |
> - TSI721_DEV_INT_SMSG_CH;
> + TSI721_DEV_INT_SMSG_CH | TSI721_DEV_INT_BDMA_CH;
>
> iowrite32(intr, priv->regs + TSI721_DEV_INTE);
> ioread32(priv->regs + TSI721_DEV_INTE);
> @@ -714,12 +734,29 @@ static int tsi721_enable_msix(struct tsi721_device *priv)
> TSI721_MSIX_OMSG_INT(i);
> }
>
> +#ifdef CONFIG_TSI721_DMA
> + /*
> + * Initialize MSI-X entries for Block DMA Engine:
> + * this driver supports XXX DMA channels
> + * (one is reserved for SRIO maintenance transactions)
> + */
> + for (i = 0; i < TSI721_DMA_CHNUM; i++) {
> + entries[TSI721_VECT_DMA0_DONE + i].entry =
> + TSI721_MSIX_DMACH_DONE(i);
> + entries[TSI721_VECT_DMA0_INT + i].entry =
> + TSI721_MSIX_DMACH_INT(i);
> + }
> +#endif /* CONFIG_TSI721_DMA */
> +
> err = pci_enable_msix(priv->pdev, entries, ARRAY_SIZE(entries));
> if (err) {
> if (err > 0)
> dev_info(&priv->pdev->dev,
> "Only %d MSI-X vectors available, "
> "not using MSI-X\n", err);
> + else
> + dev_err(&priv->pdev->dev,
> + "Failed to enable MSI-X (err=%d)\n", err);
> return err;
> }
>
> @@ -759,6 +796,22 @@ static int tsi721_enable_msix(struct tsi721_device *priv)
> i, pci_name(priv->pdev));
> }
>
> +#ifdef CONFIG_TSI721_DMA
> + for (i = 0; i < TSI721_DMA_CHNUM; i++) {
> + priv->msix[TSI721_VECT_DMA0_DONE + i].vector =
> + entries[TSI721_VECT_DMA0_DONE + i].vector;
> + snprintf(priv->msix[TSI721_VECT_DMA0_DONE + i].irq_name,
> + IRQ_DEVICE_NAME_MAX, DRV_NAME "-dmad%d@pci:%s",
> + i, pci_name(priv->pdev));
> +
> + priv->msix[TSI721_VECT_DMA0_INT + i].vector =
> + entries[TSI721_VECT_DMA0_INT + i].vector;
> + snprintf(priv->msix[TSI721_VECT_DMA0_INT + i].irq_name,
> + IRQ_DEVICE_NAME_MAX, DRV_NAME "-dmai%d@pci:%s",
> + i, pci_name(priv->pdev));
> + }
> +#endif /* CONFIG_TSI721_DMA */
> +
> return 0;
> }
> #endif /* CONFIG_PCI_MSI */
> @@ -889,20 +942,34 @@ static void tsi721_doorbell_free(struct tsi721_device *priv)
> priv->idb_base = NULL;
> }
>
> -static int tsi721_bdma_ch_init(struct tsi721_device *priv, int chnum)
> +/**
> + * tsi721_bdma_maint_init - Initialize maintenance request BDMA channel.
> + * @priv: pointer to tsi721 private data
> + *
> + * Initialize BDMA channel allocated for RapidIO maintenance read/write
> + * request generation
> + * Returns %0 on success or %-ENOMEM on failure.
> + */
> +static int tsi721_bdma_maint_init(struct tsi721_device *priv)
> {
> struct tsi721_dma_desc *bd_ptr;
> u64 *sts_ptr;
> dma_addr_t bd_phys, sts_phys;
> int sts_size;
> - int bd_num = priv->bdma[chnum].bd_num;
> + int bd_num = 2;
> + void __iomem *regs;
>
> - dev_dbg(&priv->pdev->dev, "Init Block DMA Engine, CH%d\n", chnum);
> + dev_dbg(&priv->pdev->dev,
> + "Init Block DMA Engine for Maintenance requests, CH%d\n",
> + TSI721_DMACH_MAINT);
>
> /*
> * Initialize DMA channel for maintenance requests
> */
>
> + priv->mdma.ch_id = TSI721_DMACH_MAINT;
> + regs = priv->regs + TSI721_DMAC_BASE(TSI721_DMACH_MAINT);
> +
> /* Allocate space for DMA descriptors */
> bd_ptr = dma_alloc_coherent(&priv->pdev->dev,
> bd_num * sizeof(struct tsi721_dma_desc),
> @@ -910,8 +977,9 @@ static int tsi721_bdma_ch_init(struct tsi721_device *priv, int chnum)
> if (!bd_ptr)
> return -ENOMEM;
>
> - priv->bdma[chnum].bd_phys = bd_phys;
> - priv->bdma[chnum].bd_base = bd_ptr;
> + priv->mdma.bd_num = bd_num;
> + priv->mdma.bd_phys = bd_phys;
> + priv->mdma.bd_base = bd_ptr;
>
> memset(bd_ptr, 0, bd_num * sizeof(struct tsi721_dma_desc));
>
> @@ -930,13 +998,13 @@ static int tsi721_bdma_ch_init(struct tsi721_device *priv, int chnum)
> dma_free_coherent(&priv->pdev->dev,
> bd_num * sizeof(struct tsi721_dma_desc),
> bd_ptr, bd_phys);
> - priv->bdma[chnum].bd_base = NULL;
> + priv->mdma.bd_base = NULL;
> return -ENOMEM;
> }
>
> - priv->bdma[chnum].sts_phys = sts_phys;
> - priv->bdma[chnum].sts_base = sts_ptr;
> - priv->bdma[chnum].sts_size = sts_size;
> + priv->mdma.sts_phys = sts_phys;
> + priv->mdma.sts_base = sts_ptr;
> + priv->mdma.sts_size = sts_size;
>
> memset(sts_ptr, 0, sts_size);
>
> @@ -951,83 +1019,61 @@ static int tsi721_bdma_ch_init(struct tsi721_device *priv, int chnum)
> bd_ptr[bd_num - 1].next_hi = cpu_to_le32((u64)bd_phys >> 32);
>
> /* Setup DMA descriptor pointers */
> - iowrite32(((u64)bd_phys >> 32),
> - priv->regs + TSI721_DMAC_DPTRH(chnum));
> + iowrite32(((u64)bd_phys >> 32), regs + TSI721_DMAC_DPTRH);
> iowrite32(((u64)bd_phys & TSI721_DMAC_DPTRL_MASK),
> - priv->regs + TSI721_DMAC_DPTRL(chnum));
> + regs + TSI721_DMAC_DPTRL);
>
> /* Setup descriptor status FIFO */
> - iowrite32(((u64)sts_phys >> 32),
> - priv->regs + TSI721_DMAC_DSBH(chnum));
> + iowrite32(((u64)sts_phys >> 32), regs + TSI721_DMAC_DSBH);
> iowrite32(((u64)sts_phys & TSI721_DMAC_DSBL_MASK),
> - priv->regs + TSI721_DMAC_DSBL(chnum));
> + regs + TSI721_DMAC_DSBL);
> iowrite32(TSI721_DMAC_DSSZ_SIZE(sts_size),
> - priv->regs + TSI721_DMAC_DSSZ(chnum));
> + regs + TSI721_DMAC_DSSZ);
>
> /* Clear interrupt bits */
> - iowrite32(TSI721_DMAC_INT_ALL,
> - priv->regs + TSI721_DMAC_INT(chnum));
> + iowrite32(TSI721_DMAC_INT_ALL, regs + TSI721_DMAC_INT);
>
> - ioread32(priv->regs + TSI721_DMAC_INT(chnum));
> + ioread32(regs + TSI721_DMAC_INT);
>
> /* Toggle DMA channel initialization */
> - iowrite32(TSI721_DMAC_CTL_INIT, priv->regs + TSI721_DMAC_CTL(chnum));
> - ioread32(priv->regs + TSI721_DMAC_CTL(chnum));
> + iowrite32(TSI721_DMAC_CTL_INIT, regs + TSI721_DMAC_CTL);
> + ioread32(regs + TSI721_DMAC_CTL);
> udelay(10);
>
> return 0;
> }
>
> -static int tsi721_bdma_ch_free(struct tsi721_device *priv, int chnum)
> +static int tsi721_bdma_maint_free(struct tsi721_device *priv)
> {
> u32 ch_stat;
> + struct tsi721_bdma_maint *mdma = &priv->mdma;
> + void __iomem *regs = priv->regs + TSI721_DMAC_BASE(mdma->ch_id);
>
> - if (priv->bdma[chnum].bd_base == NULL)
> + if (mdma->bd_base == NULL)
> return 0;
>
> /* Check if DMA channel still running */
> - ch_stat = ioread32(priv->regs + TSI721_DMAC_STS(chnum));
> + ch_stat = ioread32(regs + TSI721_DMAC_STS);
> if (ch_stat & TSI721_DMAC_STS_RUN)
> return -EFAULT;
>
> /* Put DMA channel into init state */
> - iowrite32(TSI721_DMAC_CTL_INIT,
> - priv->regs + TSI721_DMAC_CTL(chnum));
> + iowrite32(TSI721_DMAC_CTL_INIT, regs + TSI721_DMAC_CTL);
>
> /* Free space allocated for DMA descriptors */
> dma_free_coherent(&priv->pdev->dev,
> - priv->bdma[chnum].bd_num * sizeof(struct tsi721_dma_desc),
> - priv->bdma[chnum].bd_base, priv->bdma[chnum].bd_phys);
> - priv->bdma[chnum].bd_base = NULL;
> + mdma->bd_num * sizeof(struct tsi721_dma_desc),
> + mdma->bd_base, mdma->bd_phys);
> + mdma->bd_base = NULL;
>
> /* Free space allocated for status FIFO */
> dma_free_coherent(&priv->pdev->dev,
> - priv->bdma[chnum].sts_size * sizeof(struct tsi721_dma_sts),
> - priv->bdma[chnum].sts_base, priv->bdma[chnum].sts_phys);
> - priv->bdma[chnum].sts_base = NULL;
> - return 0;
> -}
> -
> -static int tsi721_bdma_init(struct tsi721_device *priv)
> -{
> - /* Initialize BDMA channel allocated for RapidIO maintenance read/write
> - * request generation
> - */
> - priv->bdma[TSI721_DMACH_MAINT].bd_num = 2;
> - if (tsi721_bdma_ch_init(priv, TSI721_DMACH_MAINT)) {
> - dev_err(&priv->pdev->dev, "Unable to initialize maintenance DMA"
> - " channel %d, aborting\n", TSI721_DMACH_MAINT);
> - return -ENOMEM;
> - }
> -
> + mdma->sts_size * sizeof(struct tsi721_dma_sts),
> + mdma->sts_base, mdma->sts_phys);
> + mdma->sts_base = NULL;
> return 0;
> }
>
> -static void tsi721_bdma_free(struct tsi721_device *priv)
> -{
> - tsi721_bdma_ch_free(priv, TSI721_DMACH_MAINT);
> -}
> -
> /* Enable Inbound Messaging Interrupts */
> static void
> tsi721_imsg_interrupt_enable(struct tsi721_device *priv, int ch,
> @@ -2043,7 +2089,8 @@ static void tsi721_disable_ints(struct tsi721_device *priv)
>
> /* Disable all BDMA Channel interrupts */
> for (ch = 0; ch < TSI721_DMA_MAXCH; ch++)
> - iowrite32(0, priv->regs + TSI721_DMAC_INTE(ch));
> + iowrite32(0,
> + priv->regs + TSI721_DMAC_BASE(ch) + TSI721_DMAC_INTE);
>
> /* Disable all general BDMA interrupts */
> iowrite32(0, priv->regs + TSI721_BDMA_INTE);
> @@ -2292,7 +2339,7 @@ static int __devinit tsi721_probe(struct pci_dev *pdev,
> tsi721_init_pc2sr_mapping(priv);
> tsi721_init_sr2pc_mapping(priv);
>
> - if (tsi721_bdma_init(priv)) {
> + if (tsi721_bdma_maint_init(priv)) {
> dev_err(&pdev->dev, "BDMA initialization failed, aborting\n");
> err = -ENOMEM;
> goto err_unmap_bars;
> @@ -2312,12 +2359,16 @@ static int __devinit tsi721_probe(struct pci_dev *pdev,
> if (err)
> goto err_free_consistent;
>
> +#ifdef CONFIG_TSI721_DMA
> + tsi721_register_dma(priv);
> +#endif
> +
> return 0;
>
> err_free_consistent:
> tsi721_doorbell_free(priv);
> err_free_bdma:
> - tsi721_bdma_free(priv);
> + tsi721_bdma_maint_free(priv);
> err_unmap_bars:
> if (priv->regs)
> iounmap(priv->regs);
> diff --git a/drivers/rapidio/devices/tsi721.h b/drivers/rapidio/devices/tsi721.h
> index 58be4de..2f756dc 100644
> --- a/drivers/rapidio/devices/tsi721.h
> +++ b/drivers/rapidio/devices/tsi721.h
> @@ -21,6 +21,10 @@
> #ifndef __TSI721_H
> #define __TSI721_H
>
> +#ifdef CONFIG_TSI721_DMA
> +#include <linux/dmaengine.h>
> +#endif
> +
> #define DRV_NAME "tsi721"
>
> #define DEFAULT_HOPCOUNT 0xff
> @@ -165,6 +169,8 @@
> #define TSI721_DEV_INTE 0x29840
> #define TSI721_DEV_INT 0x29844
> #define TSI721_DEV_INTSET 0x29848
> +#define TSI721_DEV_INT_BDMA_CH 0x00002000
> +#define TSI721_DEV_INT_BDMA_NCH 0x00001000
> #define TSI721_DEV_INT_SMSG_CH 0x00000800
> #define TSI721_DEV_INT_SMSG_NCH 0x00000400
> #define TSI721_DEV_INT_SR2PC_CH 0x00000200
> @@ -179,6 +185,8 @@
> #define TSI721_INT_IMSG_CHAN(x) (1 << (16 + (x)))
> #define TSI721_INT_OMSG_CHAN_M 0x0000ff00
> #define TSI721_INT_OMSG_CHAN(x) (1 << (8 + (x)))
> +#define TSI721_INT_BDMA_CHAN_M 0x000000ff
> +#define TSI721_INT_BDMA_CHAN(x) (1 << (x))
>
> /*
> * PC2SR block registers
> @@ -233,14 +241,16 @@
> * x = 0..7
> */
>
> -#define TSI721_DMAC_DWRCNT(x) (0x51000 + (x) * 0x1000)
> -#define TSI721_DMAC_DRDCNT(x) (0x51004 + (x) * 0x1000)
> +#define TSI721_DMAC_BASE(x) (0x51000 + (x) * 0x1000)
> +
> +#define TSI721_DMAC_DWRCNT 0x000
> +#define TSI721_DMAC_DRDCNT 0x004
>
> -#define TSI721_DMAC_CTL(x) (0x51008 + (x) * 0x1000)
> +#define TSI721_DMAC_CTL 0x008
> #define TSI721_DMAC_CTL_SUSP 0x00000002
> #define TSI721_DMAC_CTL_INIT 0x00000001
>
> -#define TSI721_DMAC_INT(x) (0x5100c + (x) * 0x1000)
> +#define TSI721_DMAC_INT 0x00c
> #define TSI721_DMAC_INT_STFULL 0x00000010
> #define TSI721_DMAC_INT_DONE 0x00000008
> #define TSI721_DMAC_INT_SUSP 0x00000004
> @@ -248,34 +258,33 @@
> #define TSI721_DMAC_INT_IOFDONE 0x00000001
> #define TSI721_DMAC_INT_ALL 0x0000001f
>
> -#define TSI721_DMAC_INTSET(x) (0x51010 + (x) * 0x1000)
> +#define TSI721_DMAC_INTSET 0x010
>
> -#define TSI721_DMAC_STS(x) (0x51014 + (x) * 0x1000)
> +#define TSI721_DMAC_STS 0x014
> #define TSI721_DMAC_STS_ABORT 0x00400000
> #define TSI721_DMAC_STS_RUN 0x00200000
> #define TSI721_DMAC_STS_CS 0x001f0000
>
> -#define TSI721_DMAC_INTE(x) (0x51018 + (x) * 0x1000)
> +#define TSI721_DMAC_INTE 0x018
>
> -#define TSI721_DMAC_DPTRL(x) (0x51024 + (x) * 0x1000)
> +#define TSI721_DMAC_DPTRL 0x024
> #define TSI721_DMAC_DPTRL_MASK 0xffffffe0
>
> -#define TSI721_DMAC_DPTRH(x) (0x51028 + (x) * 0x1000)
> +#define TSI721_DMAC_DPTRH 0x028
>
> -#define TSI721_DMAC_DSBL(x) (0x5102c + (x) * 0x1000)
> +#define TSI721_DMAC_DSBL 0x02c
> #define TSI721_DMAC_DSBL_MASK 0xffffffc0
>
> -#define TSI721_DMAC_DSBH(x) (0x51030 + (x) * 0x1000)
> +#define TSI721_DMAC_DSBH 0x030
>
> -#define TSI721_DMAC_DSSZ(x) (0x51034 + (x) * 0x1000)
> +#define TSI721_DMAC_DSSZ 0x034
> #define TSI721_DMAC_DSSZ_SIZE_M 0x0000000f
> #define TSI721_DMAC_DSSZ_SIZE(size) (__fls(size) - 4)
>
> -
> -#define TSI721_DMAC_DSRP(x) (0x51038 + (x) * 0x1000)
> +#define TSI721_DMAC_DSRP 0x038
> #define TSI721_DMAC_DSRP_MASK 0x0007ffff
>
> -#define TSI721_DMAC_DSWP(x) (0x5103c + (x) * 0x1000)
> +#define TSI721_DMAC_DSWP 0x03c
> #define TSI721_DMAC_DSWP_MASK 0x0007ffff
>
> #define TSI721_BDMA_INTE 0x5f000
> @@ -624,7 +633,48 @@ enum tsi721_smsg_int_flag {
>
> /* Structures */
>
> +#ifdef CONFIG_TSI721_DMA
> +
> +struct tsi721_tx_desc {
> + struct dma_async_tx_descriptor txd;
> + struct tsi721_dma_desc *hw_desc;
> + u16 destid;
> + /* low 64-bits of 66-bit RIO address */
> + u64 rio_addr;
> + /* upper 2-bits of 66-bit RIO address */
> + u8 rio_addr_u;
> + bool interrupt;
> + struct list_head desc_node;
> + struct list_head tx_list;
> +};
> +
> struct tsi721_bdma_chan {
> + int id;
> + void __iomem *regs;
> + int bd_num; /* number of buffer descriptors */
> + void *bd_base; /* start of DMA descriptors */
> + dma_addr_t bd_phys;
> + void *sts_base; /* start of DMA BD status FIFO */
> + dma_addr_t sts_phys;
> + int sts_size;
> + u32 sts_rdptr;
> + u32 wr_count;
> + u32 wr_count_next;
> +
> + struct dma_chan dchan;
> + struct tsi721_tx_desc *tx_desc;
> + spinlock_t lock;
> + struct list_head active_list;
> + struct list_head queue;
> + struct list_head free_list;
> + dma_cookie_t completed_cookie;
> + struct tasklet_struct tasklet;
> +};
> +
> +#endif /* CONFIG_TSI721_DMA */
> +
> +struct tsi721_bdma_maint {
> + int ch_id; /* BDMA channel number */
> int bd_num; /* number of buffer descriptors */
> void *bd_base; /* start of DMA descriptors */
> dma_addr_t bd_phys;
> @@ -719,6 +769,24 @@ enum tsi721_msix_vect {
> TSI721_VECT_IMB1_INT,
> TSI721_VECT_IMB2_INT,
> TSI721_VECT_IMB3_INT,
> +#ifdef CONFIG_TSI721_DMA
> + TSI721_VECT_DMA0_DONE,
> + TSI721_VECT_DMA1_DONE,
> + TSI721_VECT_DMA2_DONE,
> + TSI721_VECT_DMA3_DONE,
> + TSI721_VECT_DMA4_DONE,
> + TSI721_VECT_DMA5_DONE,
> + TSI721_VECT_DMA6_DONE,
> + TSI721_VECT_DMA7_DONE,
> + TSI721_VECT_DMA0_INT,
> + TSI721_VECT_DMA1_INT,
> + TSI721_VECT_DMA2_INT,
> + TSI721_VECT_DMA3_INT,
> + TSI721_VECT_DMA4_INT,
> + TSI721_VECT_DMA5_INT,
> + TSI721_VECT_DMA6_INT,
> + TSI721_VECT_DMA7_INT,
> +#endif /* CONFIG_TSI721_DMA */
> TSI721_VECT_MAX
> };
>
> @@ -752,7 +820,11 @@ struct tsi721_device {
> u32 pw_discard_count;
>
> /* BDMA Engine */
> + struct tsi721_bdma_maint mdma; /* Maintenance rd/wr request channel */
> +
> +#ifdef CONFIG_TSI721_DMA
> struct tsi721_bdma_chan bdma[TSI721_DMA_CHNUM];
> +#endif
>
> /* Inbound Messaging */
> int imsg_init[TSI721_IMSG_CHNUM];
> @@ -763,4 +835,9 @@ struct tsi721_device {
> struct tsi721_omsg_ring omsg_ring[TSI721_OMSG_CHNUM];
> };
>
> +#ifdef CONFIG_TSI721_DMA
> +extern void tsi721_bdma_handler(struct tsi721_bdma_chan *chan);
> +extern int __devinit tsi721_register_dma(struct tsi721_device *priv);
> +#endif
> +
> #endif
> diff --git a/drivers/rapidio/devices/tsi721_dma.c b/drivers/rapidio/devices/tsi721_dma.c
> new file mode 100644
> index 0000000..75a27a0
> --- /dev/null
> +++ b/drivers/rapidio/devices/tsi721_dma.c
> @@ -0,0 +1,802 @@
> +/*
> + * DMA Engine support for Tsi721 PCIExpress-to-SRIO bridge
> + *
> + * Copyright 2011 Integrated Device Technology, Inc.
> + * Alexandre Bounine <alexandre.bounine@idt.com>
> + *
> + * 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, write to the Free Software Foundation, Inc., 59
> + * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
> + */
> +
> +#include <linux/io.h>
> +#include <linux/errno.h>
> +#include <linux/init.h>
> +#include <linux/ioport.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/pci.h>
> +#include <linux/rio.h>
> +#include <linux/rio_drv.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/interrupt.h>
> +#include <linux/kfifo.h>
> +#include <linux/delay.h>
> +
> +#include "tsi721.h"
> +
> +static inline struct tsi721_bdma_chan *to_tsi721_chan(struct dma_chan *chan)
> +{
> + return container_of(chan, struct tsi721_bdma_chan, dchan);
> +}
> +
> +static inline struct tsi721_device *to_tsi721(struct dma_device *ddev)
> +{
> + return container_of(ddev, struct rio_mport, dma)->priv;
> +}
> +
> +static inline
> +struct tsi721_tx_desc *to_tsi721_desc(struct dma_async_tx_descriptor *txd)
> +{
> + return container_of(txd, struct tsi721_tx_desc, txd);
> +}
> +
> +static inline
> +struct tsi721_tx_desc *tsi721_dma_first_active(struct tsi721_bdma_chan *chan)
> +{
> + return list_first_entry(&chan->active_list,
> + struct tsi721_tx_desc, desc_node);
> +}
> +
> +static int tsi721_bdma_ch_init(struct tsi721_bdma_chan *chan)
> +{
> + struct tsi721_dma_desc *bd_ptr;
> + struct device *dev = chan->dchan.device->dev;
> + u64 *sts_ptr;
> + dma_addr_t bd_phys;
> + dma_addr_t sts_phys;
> + int sts_size;
> + int bd_num = chan->bd_num;
> +
> + dev_dbg(dev, "Init Block DMA Engine, CH%d\n", chan->id);
> +
> + /* Allocate space for DMA descriptors */
> + bd_ptr = dma_alloc_coherent(dev,
> + bd_num * sizeof(struct tsi721_dma_desc),
> + &bd_phys, GFP_KERNEL);
> + if (!bd_ptr)
> + return -ENOMEM;
> +
> + chan->bd_phys = bd_phys;
> + chan->bd_base = bd_ptr;
> +
> + memset(bd_ptr, 0, bd_num * sizeof(struct tsi721_dma_desc));
> +
> + dev_dbg(dev, "DMA descriptors @ %p (phys = %llx)\n",
> + bd_ptr, (unsigned long long)bd_phys);
> +
> + /* Allocate space for descriptor status FIFO */
> + sts_size = (bd_num >= TSI721_DMA_MINSTSSZ) ?
> + bd_num : TSI721_DMA_MINSTSSZ;
> + sts_size = roundup_pow_of_two(sts_size);
> + sts_ptr = dma_alloc_coherent(dev,
> + sts_size * sizeof(struct tsi721_dma_sts),
> + &sts_phys, GFP_KERNEL);
> + if (!sts_ptr) {
> + /* Free space allocated for DMA descriptors */
> + dma_free_coherent(dev,
> + bd_num * sizeof(struct tsi721_dma_desc),
> + bd_ptr, bd_phys);
> + chan->bd_base = NULL;
> + return -ENOMEM;
> + }
> +
> + chan->sts_phys = sts_phys;
> + chan->sts_base = sts_ptr;
> + chan->sts_size = sts_size;
> +
> + memset(sts_ptr, 0, sts_size);
> +
> + dev_dbg(dev,
> + "desc status FIFO @ %p (phys = %llx) size=0x%x\n",
> + sts_ptr, (unsigned long long)sts_phys, sts_size);
> +
> + /* Initialize DMA descriptors ring */
> + bd_ptr[bd_num - 1].type_id = cpu_to_le32(DTYPE3 << 29);
> + bd_ptr[bd_num - 1].next_lo = cpu_to_le32((u64)bd_phys &
> + TSI721_DMAC_DPTRL_MASK);
> + bd_ptr[bd_num - 1].next_hi = cpu_to_le32((u64)bd_phys >> 32);
> +
> + /* Setup DMA descriptor pointers */
> + iowrite32(((u64)bd_phys >> 32),
> + chan->regs + TSI721_DMAC_DPTRH);
> + iowrite32(((u64)bd_phys & TSI721_DMAC_DPTRL_MASK),
> + chan->regs + TSI721_DMAC_DPTRL);
> +
> + /* Setup descriptor status FIFO */
> + iowrite32(((u64)sts_phys >> 32),
> + chan->regs + TSI721_DMAC_DSBH);
> + iowrite32(((u64)sts_phys & TSI721_DMAC_DSBL_MASK),
> + chan->regs + TSI721_DMAC_DSBL);
> + iowrite32(TSI721_DMAC_DSSZ_SIZE(sts_size),
> + chan->regs + TSI721_DMAC_DSSZ);
> +
> + /* Clear interrupt bits */
> + iowrite32(TSI721_DMAC_INT_ALL,
> + chan->regs + TSI721_DMAC_INT);
> +
> + ioread32(chan->regs + TSI721_DMAC_INT);
> +
> + /* Toggle DMA channel initialization */
> + iowrite32(TSI721_DMAC_CTL_INIT, chan->regs + TSI721_DMAC_CTL);
> + ioread32(chan->regs + TSI721_DMAC_CTL);
> + chan->wr_count = chan->wr_count_next = 0;
> + chan->sts_rdptr = 0;
> + udelay(10);
> +
> + return 0;
> +}
> +
> +static int tsi721_bdma_ch_free(struct tsi721_bdma_chan *chan)
> +{
> + u32 ch_stat;
> +
> + if (chan->bd_base == NULL)
> + return 0;
> +
> + /* Check if DMA channel still running */
> + ch_stat = ioread32(chan->regs + TSI721_DMAC_STS);
> + if (ch_stat & TSI721_DMAC_STS_RUN)
> + return -EFAULT;
> +
> + /* Put DMA channel into init state */
> + iowrite32(TSI721_DMAC_CTL_INIT, chan->regs + TSI721_DMAC_CTL);
> +
> + /* Free space allocated for DMA descriptors */
> + dma_free_coherent(chan->dchan.device->dev,
> + chan->bd_num * sizeof(struct tsi721_dma_desc),
> + chan->bd_base, chan->bd_phys);
> + chan->bd_base = NULL;
> +
> + /* Free space allocated for status FIFO */
> + dma_free_coherent(chan->dchan.device->dev,
> + chan->sts_size * sizeof(struct tsi721_dma_sts),
> + chan->sts_base, chan->sts_phys);
> + chan->sts_base = NULL;
> + return 0;
> +}
> +
> +static
> +void tsi721_bdma_interrupt_enable(struct tsi721_bdma_chan *chan, int enable)
> +{
> + if (enable) {
> + /* Clear pending BDMA channel interrupts */
> + iowrite32(TSI721_DMAC_INT_ALL, chan->regs + TSI721_DMAC_INT);
> + ioread32(chan->regs + TSI721_DMAC_INT);
> + /* Enable BDMA channel interrupts */
> + iowrite32(TSI721_DMAC_INT_ALL, chan->regs + TSI721_DMAC_INTE);
> + } else {
> + /* Disable BDMA channel interrupts */
> + iowrite32(0, chan->regs + TSI721_DMAC_INTE);
> + /* Clear pending BDMA channel interrupts */
> + iowrite32(TSI721_DMAC_INT_ALL, chan->regs + TSI721_DMAC_INT);
> + }
> +
> +}
> +
> +static bool tsi721_dma_is_idle(struct tsi721_bdma_chan *chan)
> +{
> + u32 sts;
> +
> + sts = ioread32(chan->regs + TSI721_DMAC_STS);
> + return ((sts & TSI721_DMAC_STS_RUN) == 0);
> +}
> +
> +void tsi721_bdma_handler(struct tsi721_bdma_chan *chan)
> +{
> + /* Disable BDMA channel interrupts */
> + iowrite32(0, chan->regs + TSI721_DMAC_INTE);
> +
> + tasklet_schedule(&chan->tasklet);
> +}
> +
> +#ifdef CONFIG_PCI_MSI
> +/**
> + * tsi721_omsg_msix - MSI-X interrupt handler for outbound messaging
> + * @irq: Linux interrupt number
> + * @ptr: Pointer to interrupt-specific data (mport structure)
> + *
> + * Handles outbound messaging interrupts signaled using MSI-X.
> + */
> +static irqreturn_t tsi721_bdma_msix(int irq, void *ptr)
> +{
> + struct tsi721_bdma_chan *chan = ptr;
> +
> + tsi721_bdma_handler(chan);
> + return IRQ_HANDLED;
> +}
> +#endif /* CONFIG_PCI_MSI */
> +
> +/* Must be called with the spinlock held */
> +static void tsi721_start_dma(struct tsi721_bdma_chan *chan)
> +{
> + if (!tsi721_dma_is_idle(chan)) {
> + dev_err(chan->dchan.device->dev,
> + "BUG: Attempt to start non-idle channel\n");
> + return;
> + }
> +
> + if (chan->wr_count == chan->wr_count_next) {
> + dev_err(chan->dchan.device->dev,
> + "BUG: Attempt to start DMA with no BDs ready\n");
> + return;
> + }
> +
> + dev_dbg(chan->dchan.device->dev,
> + "tx_chan: %p, chan: %d, regs: %p\n",
> + chan, chan->dchan.chan_id, chan->regs);
> +
> + iowrite32(chan->wr_count_next, chan->regs + TSI721_DMAC_DWRCNT);
> + ioread32(chan->regs + TSI721_DMAC_DWRCNT);
> +
> + chan->wr_count = chan->wr_count_next;
> +}
> +
> +static void tsi721_desc_put(struct tsi721_bdma_chan *chan,
> + struct tsi721_tx_desc *desc)
> +{
> + dev_dbg(chan->dchan.device->dev, "Put desc: %p into free list\n", desc);
> +
> + if (desc) {
> + spin_lock_bh(&chan->lock);
> + list_splice_init(&desc->tx_list, &chan->free_list);
> + list_add(&desc->desc_node, &chan->free_list);
> + chan->wr_count_next = chan->wr_count;
> + spin_unlock_bh(&chan->lock);
> + }
> +}
> +
> +static struct tsi721_tx_desc *tsi721_desc_get(struct tsi721_bdma_chan *chan)
> +{
> + struct tsi721_tx_desc *tx_desc, *_tx_desc;
> + struct tsi721_tx_desc *ret = NULL;
> + int i;
> +
> + spin_lock_bh(&chan->lock);
> + list_for_each_entry_safe(tx_desc, _tx_desc,
> + &chan->free_list, desc_node) {
> + if (async_tx_test_ack(&tx_desc->txd)) {
> + list_del(&tx_desc->desc_node);
> + ret = tx_desc;
> + break;
> + }
> + dev_dbg(chan->dchan.device->dev,
> + "desc %p not ACKed\n", tx_desc);
> + }
> +
> + i = chan->wr_count_next % chan->bd_num;
> + if (i == chan->bd_num - 1) {
> + i = 0;
> + chan->wr_count_next++; /* skip link descriptor */
> + }
> +
> + chan->wr_count_next++;
> + tx_desc->txd.phys = chan->bd_phys + i * sizeof(struct tsi721_dma_desc);
> + tx_desc->hw_desc = &((struct tsi721_dma_desc *)chan->bd_base)[i];
> +
> + spin_unlock_bh(&chan->lock);
> +
> + return ret;
> +}
> +
> +static
> +int tsi721_fill_desc(struct tsi721_bdma_chan *chan, struct tsi721_tx_desc *desc,
> + struct scatterlist *sg, enum dma_rtype rtype, u32 sys_size)
> +{
> + struct tsi721_dma_desc *bd_ptr = desc->hw_desc;
> + u64 rio_addr;
> +
> + if (sg_dma_len(sg) > TSI721_DMAD_BCOUNT1 + 1) {
> + dev_err(chan->dchan.device->dev, "SG element is too large\n");
> + return -EINVAL;
> + }
> +
> + dev_dbg(chan->dchan.device->dev,
> + "desc: 0x%llx, addr: 0x%llx len: 0x%x\n",
> + (u64)desc->txd.phys, (unsigned long long)sg_dma_address(sg),
> + sg_dma_len(sg));
> +
> + dev_dbg(chan->dchan.device->dev, "bd_ptr = %p did=%d raddr=0x%llx\n",
> + bd_ptr, desc->destid, desc->rio_addr);
> +
> + /* Initialize DMA descriptor */
> + bd_ptr->type_id = cpu_to_le32((DTYPE1 << 29) |
> + (rtype << 19) | desc->destid);
> + if (desc->interrupt)
> + bd_ptr->type_id |= cpu_to_le32(TSI721_DMAD_IOF);
> + bd_ptr->bcount = cpu_to_le32(((desc->rio_addr & 0x3) << 30) |
> + (sys_size << 26) | sg_dma_len(sg));
> + rio_addr = (desc->rio_addr >> 2) |
> + ((u64)(desc->rio_addr_u & 0x3) << 62);
> + bd_ptr->raddr_lo = cpu_to_le32(rio_addr & 0xffffffff);
> + bd_ptr->raddr_hi = cpu_to_le32(rio_addr >> 32);
> + bd_ptr->t1.bufptr_lo = cpu_to_le32(
> + (u64)sg_dma_address(sg) & 0xffffffff);
> + bd_ptr->t1.bufptr_hi = cpu_to_le32((u64)sg_dma_address(sg) >> 32);
> + bd_ptr->t1.s_dist = 0;
> + bd_ptr->t1.s_size = 0;
> +
> + mb();
> +
> + return 0;
> +}
> +
> +static void tsi721_dma_chain_complete(struct tsi721_bdma_chan *chan,
> + struct tsi721_tx_desc *desc)
> +{
> + struct dma_async_tx_descriptor *txd = &desc->txd;
> + dma_async_tx_callback callback = txd->callback;
> + void *param = txd->callback_param;
> +
> + list_splice_init(&desc->tx_list, &chan->free_list);
> + list_move(&desc->desc_node, &chan->free_list);
> + chan->completed_cookie = txd->cookie;
> +
> + if (callback)
> + callback(param);
> +}
> +
> +static void tsi721_dma_complete_all(struct tsi721_bdma_chan *chan)
> +{
> + struct tsi721_tx_desc *desc, *_d;
> + LIST_HEAD(list);
> +
> + BUG_ON(!tsi721_dma_is_idle(chan));
> +
> + if (!list_empty(&chan->queue))
> + tsi721_start_dma(chan);
> +
> + list_splice_init(&chan->active_list, &list);
> + list_splice_init(&chan->queue, &chan->active_list);
> +
> + list_for_each_entry_safe(desc, _d, &list, desc_node)
> + tsi721_dma_chain_complete(chan, desc);
> +}
> +
> +static void tsi721_clr_stat(struct tsi721_bdma_chan *chan)
> +{
> + u32 srd_ptr;
> + u64 *sts_ptr;
> + int i, j;
> +
> + /* Check and clear descriptor status FIFO entries */
> + srd_ptr = chan->sts_rdptr;
> + sts_ptr = chan->sts_base;
> + j = srd_ptr * 8;
> + while (sts_ptr[j]) {
> + for (i = 0; i < 8 && sts_ptr[j]; i++, j++)
> + sts_ptr[j] = 0;
> +
> + ++srd_ptr;
> + srd_ptr %= chan->sts_size;
> + j = srd_ptr * 8;
> + }
> +
> + iowrite32(srd_ptr, chan->regs + TSI721_DMAC_DSRP);
> + chan->sts_rdptr = srd_ptr;
> +}
> +
> +static void tsi721_advance_work(struct tsi721_bdma_chan *chan)
> +{
> + if (list_empty(&chan->active_list) ||
> + list_is_singular(&chan->active_list)) {
> + dev_dbg(chan->dchan.device->dev,
> + "%s: Active_list empty\n", __func__);
> + tsi721_dma_complete_all(chan);
> + } else {
> + dev_dbg(chan->dchan.device->dev,
> + "%s: Active_list NOT empty\n", __func__);
> + tsi721_dma_chain_complete(chan, tsi721_dma_first_active(chan));
> + tsi721_start_dma(chan);
> + }
> +}
> +
> +static void tsi721_dma_tasklet(unsigned long data)
> +{
> + struct tsi721_bdma_chan *chan = (struct tsi721_bdma_chan *)data;
> + u32 dmac_int, dmac_sts;
> +
> + dmac_int = ioread32(chan->regs + TSI721_DMAC_INT);
> + dev_dbg(chan->dchan.device->dev, "%s: DMAC%d_INT = 0x%x\n",
> + __func__, chan->id, dmac_int);
> + /* Clear channel interrupts */
> + iowrite32(dmac_int, chan->regs + TSI721_DMAC_INT);
> +
> + if (dmac_int & TSI721_DMAC_INT_ERR) {
> + dmac_sts = ioread32(chan->regs + TSI721_DMAC_STS);
> + dev_err(chan->dchan.device->dev,
> + "%s: DMA ERROR - DMAC%d_STS = 0x%x\n",
> + __func__, chan->id, dmac_sts);
> + }
> +
> + if (dmac_int & TSI721_DMAC_INT_STFULL) {
> + dev_err(chan->dchan.device->dev,
> + "%s: DMAC%d descriptor status FIFO is full\n",
> + __func__, chan->id);
> + }
> +
> + if (dmac_int & (TSI721_DMAC_INT_DONE | TSI721_DMAC_INT_IOFDONE)) {
> + tsi721_clr_stat(chan);
> + spin_lock(&chan->lock);
> + tsi721_advance_work(chan);
> + spin_unlock(&chan->lock);
> + }
> +
> + /* Re-Enable BDMA channel interrupts */
> + iowrite32(TSI721_DMAC_INT_ALL, chan->regs + TSI721_DMAC_INTE);
> +}
> +
> +static dma_cookie_t tsi721_tx_submit(struct dma_async_tx_descriptor *txd)
> +{
> + struct tsi721_tx_desc *desc = to_tsi721_desc(txd);
> + struct tsi721_bdma_chan *chan = to_tsi721_chan(txd->chan);
> + dma_cookie_t cookie;
> +
> + spin_lock_bh(&chan->lock);
> +
> + cookie = txd->chan->cookie;
> + if (++cookie < 0)
> + cookie = 1;
> + txd->chan->cookie = cookie;
> + txd->cookie = cookie;
> +
> + if (list_empty(&chan->active_list)) {
> + list_add_tail(&desc->desc_node, &chan->active_list);
> + tsi721_start_dma(chan);
> + } else {
> + list_add_tail(&desc->desc_node, &chan->queue);
> + }
> +
> + spin_unlock_bh(&chan->lock);
> + return cookie;
> +}
> +
> +static int tsi721_alloc_chan_resources(struct dma_chan *dchan)
> +{
> + struct tsi721_bdma_chan *chan = to_tsi721_chan(dchan);
> + struct tsi721_device *priv = to_tsi721(dchan->device);
> + struct tsi721_tx_desc *desc = NULL;
> + LIST_HEAD(tmp_list);
> + int i;
> + int rc;
> +
> + if (chan->bd_base)
> + return chan->bd_num - 1;
> +
> + /* Initialize BDMA channel */
> + if (tsi721_bdma_ch_init(chan)) {
> + dev_err(dchan->device->dev, "Unable to initialize data DMA"
> + " channel %d, aborting\n", chan->id);
> + return -ENOMEM;
> + }
> +
> + /* Allocate matching number of logical descriptors */
> + desc = kzalloc((chan->bd_num - 1) * sizeof(struct tsi721_tx_desc),
> + GFP_KERNEL);
> + if (!desc) {
> + dev_err(dchan->device->dev,
> + "Failed to allocate logical descriptors\n");
> + rc = -ENOMEM;
> + goto err_out;
> + }
> +
> + chan->tx_desc = desc;
> +
> + for (i = 0; i < chan->bd_num - 1; i++) {
> + dma_async_tx_descriptor_init(&desc[i].txd, dchan);
> + desc[i].txd.tx_submit = tsi721_tx_submit;
> + desc[i].txd.flags = DMA_CTRL_ACK;
> + INIT_LIST_HEAD(&desc[i].tx_list);
> + list_add_tail(&desc[i].desc_node, &tmp_list);
> + }
> +
> + spin_lock_bh(&chan->lock);
> + list_splice(&tmp_list, &chan->free_list);
> + chan->completed_cookie = dchan->cookie = 1;
> + spin_unlock_bh(&chan->lock);
> +
> +#ifdef CONFIG_PCI_MSI
> + if (priv->flags & TSI721_USING_MSIX) {
> + /* Request interrupt service if we are in MSI-X mode */
> + rc = request_irq(
> + priv->msix[TSI721_VECT_DMA0_DONE + chan->id].vector,
> + tsi721_bdma_msix, 0,
> + priv->msix[TSI721_VECT_DMA0_DONE + chan->id].irq_name,
> + (void *)chan);
> +
> + if (rc) {
> + dev_dbg(dchan->device->dev,
> + "Unable to allocate MSI-X interrupt for "
> + "BDMA%d-DONE\n", chan->id);
> + goto err_out;
> + }
> +
> + rc = request_irq(priv->msix[TSI721_VECT_DMA0_INT +
> + chan->id].vector,
> + tsi721_bdma_msix, 0,
> + priv->msix[TSI721_VECT_DMA0_INT + chan->id].irq_name,
> + (void *)chan);
> +
> + if (rc) {
> + dev_dbg(dchan->device->dev,
> + "Unable to allocate MSI-X interrupt for "
> + "BDMA%d-INT\n", chan->id);
> + free_irq(
> + priv->msix[TSI721_VECT_DMA0_DONE +
> + chan->id].vector,
> + (void *)chan);
> + rc = -EIO;
> + goto err_out;
> + }
> + }
> +#endif /* CONFIG_PCI_MSI */
> +
> + tsi721_bdma_interrupt_enable(chan, 1);
> +
> + return chan->bd_num - 1;
> +
> +err_out:
> + kfree(desc);
> + tsi721_bdma_ch_free(chan);
> + return rc;
> +}
> +
> +static void tsi721_free_chan_resources(struct dma_chan *dchan)
> +{
> + struct tsi721_bdma_chan *chan = to_tsi721_chan(dchan);
> + struct tsi721_device *priv = to_tsi721(dchan->device);
> + LIST_HEAD(list);
> +
> + dev_dbg(dchan->device->dev, "%s: Entry\n", __func__);
> +
> + BUG_ON(!list_empty(&chan->active_list));
> + BUG_ON(!list_empty(&chan->queue));
> +
> + spin_lock_irq(&chan->lock);
> + list_splice_init(&chan->free_list, &list);
> + spin_unlock_irq(&chan->lock);
> +
> + tsi721_bdma_interrupt_enable(chan, 0);
> +
> +#ifdef CONFIG_PCI_MSI
> + if (priv->flags & TSI721_USING_MSIX) {
> + free_irq(priv->msix[TSI721_VECT_DMA0_DONE + chan->id].vector,
> + (void *)chan);
> + free_irq(priv->msix[TSI721_VECT_DMA0_INT + chan->id].vector,
> + (void *)chan);
> + }
> +#endif /* CONFIG_PCI_MSI */
> +
> + tsi721_bdma_ch_free(chan);
> + kfree(chan->tx_desc);
> +}
> +
> +static
> +enum dma_status tsi721_tx_status(struct dma_chan *dchan, dma_cookie_t cookie,
> + struct dma_tx_state *txstate)
> +{
> + struct tsi721_bdma_chan *bdma_chan = to_tsi721_chan(dchan);
> + dma_cookie_t last_used;
> + dma_cookie_t last_completed;
> + int ret;
> +
> + spin_lock_irq(&bdma_chan->lock);
> + last_completed = bdma_chan->completed_cookie;
> + last_used = dchan->cookie;
> + spin_unlock_irq(&bdma_chan->lock);
> +
> + ret = dma_async_is_complete(cookie, last_completed, last_used);
> +
> + dma_set_tx_state(txstate, last_completed, last_used, 0);
> +
> + dev_dbg(dchan->device->dev,
> + "%s: exit, ret: %d, last_completed: %d, last_used: %d\n",
> + __func__, ret, last_completed, last_used);
> +
> + return ret;
> +}
> +
> +static void tsi721_issue_pending(struct dma_chan *dchan)
> +{
> + struct tsi721_bdma_chan *chan = to_tsi721_chan(dchan);
> +
> + dev_dbg(dchan->device->dev, "%s: Entry\n", __func__);
> +
> + if (tsi721_dma_is_idle(chan)) {
> + spin_lock_bh(&chan->lock);
> + tsi721_advance_work(chan);
> + spin_unlock_bh(&chan->lock);
> + } else
> + dev_dbg(dchan->device->dev,
> + "%s: DMA channel still busy\n", __func__);
> +}
> +
> +static
> +struct dma_async_tx_descriptor *tsi721_prep_slave_sg(struct dma_chan *dchan,
> + struct scatterlist *sgl, unsigned int sg_len,
> + enum dma_data_direction direction, unsigned long flags)
> +{
> + struct tsi721_bdma_chan *bdma_chan = to_tsi721_chan(dchan);
> + struct tsi721_tx_desc *desc = NULL;
> + struct tsi721_tx_desc *first = NULL;
> + struct scatterlist *sg;
> + struct rio_dma_ext *rext = dchan->private;
> + u64 rio_addr = rext->rio_addr; /* FIXME: assuming 64-bit rio_addr for now */
> + unsigned int i;
> + u32 sys_size = dma_to_mport(dchan->device)->sys_size;
> + enum dma_rtype rtype;
> +
> + if (!sgl || !sg_len) {
> + dev_err(dchan->device->dev, "%s: No SG list\n", __func__);
> + return NULL;
> + }
> +
> + if (direction == DMA_FROM_DEVICE)
> + rtype = NREAD;
> + else if (direction == DMA_TO_DEVICE) {
> + switch (rext->wr_type) {
> + case RDW_ALL_NWRITE:
> + rtype = ALL_NWRITE;
> + break;
> + case RDW_ALL_NWRITE_R:
> + rtype = ALL_NWRITE_R;
> + break;
> + case RDW_LAST_NWRITE_R:
> + default:
> + rtype = LAST_NWRITE_R;
> + break;
> + }
> + } else {
> + dev_err(dchan->device->dev,
> + "%s: Unsupported DMA direction option\n", __func__);
> + return NULL;
> + }
> +
> + for_each_sg(sgl, sg, sg_len, i) {
> + int err;
> +
> + dev_dbg(dchan->device->dev, "%s: sg #%d\n", __func__, i);
> + desc = tsi721_desc_get(bdma_chan);
> + if (!desc) {
> + dev_err(dchan->device->dev,
> + "Not enough descriptors available\n");
> + goto err_desc_get;
> + }
> +
> + if (sg_is_last(sg))
> + desc->interrupt = (flags & DMA_PREP_INTERRUPT) != 0;
> + else
> + desc->interrupt = false;
> +
> + desc->destid = rext->destid;
> + desc->rio_addr = rio_addr;
> + desc->rio_addr_u = 0;
> +
> + err = tsi721_fill_desc(bdma_chan, desc, sg, rtype, sys_size);
> + if (err) {
> + dev_err(dchan->device->dev,
> + "Failed to build desc: %d\n", err);
> + goto err_desc_get;
> + }
> +
> + rio_addr += sg_dma_len(sg);
> +
> + if (!first)
> + first = desc;
> + else
> + list_add_tail(&desc->desc_node, &first->tx_list);
> + }
> +
> + first->txd.cookie = -EBUSY;
> + desc->txd.flags = flags;
> +
> + return &first->txd;
> +
> +err_desc_get:
> + tsi721_desc_put(bdma_chan, first);
> + return NULL;
> +}
> +
> +static int tsi721_device_control(struct dma_chan *dchan, enum dma_ctrl_cmd cmd,
> + unsigned long arg)
> +{
> + struct tsi721_bdma_chan *chan = to_tsi721_chan(dchan);
> + struct tsi721_tx_desc *desc, *_d;
> + LIST_HEAD(list);
> +
> + dev_dbg(dchan->device->dev, "%s: Entry\n", __func__);
> +
> + if (cmd != DMA_TERMINATE_ALL)
> + return -ENXIO;
> +
> + spin_lock_bh(&chan->lock);
> +
> + /* make sure to stop the transfer */
> + iowrite32(TSI721_DMAC_CTL_SUSP, chan->regs + TSI721_DMAC_CTL);
> +
> + list_splice_init(&chan->active_list, &list);
> + list_splice_init(&chan->queue, &list);
> +
> + list_for_each_entry_safe(desc, _d, &list, desc_node)
> + tsi721_dma_chain_complete(chan, desc);
> +
> + spin_unlock_bh(&chan->lock);
> +
> + return 0;
> +}
> +
> +int __devinit tsi721_register_dma(struct tsi721_device *priv)
> +{
> + int i;
> + int nr_channels = TSI721_DMA_MAXCH;
> + int err;
> + struct rio_mport *mport = priv->mport;
> +
> + mport->dma.dev = &priv->pdev->dev;
> + mport->dma.chancnt = nr_channels;
> +
> + INIT_LIST_HEAD(&mport->dma.channels);
> +
> + for (i = 0; i < nr_channels; i++) {
> + struct tsi721_bdma_chan *bdma_chan = &priv->bdma[i];
> +
> + if (i == TSI721_DMACH_MAINT)
> + continue;
> +
> + bdma_chan->bd_num = 64;
> + bdma_chan->regs = priv->regs + TSI721_DMAC_BASE(i);
> +
> + bdma_chan->dchan.device = &mport->dma;
> + bdma_chan->dchan.cookie = 1;
> + bdma_chan->dchan.chan_id = i;
> + bdma_chan->id = i;
> +
> + spin_lock_init(&bdma_chan->lock);
> +
> + INIT_LIST_HEAD(&bdma_chan->active_list);
> + INIT_LIST_HEAD(&bdma_chan->queue);
> + INIT_LIST_HEAD(&bdma_chan->free_list);
> +
> + tasklet_init(&bdma_chan->tasklet, tsi721_dma_tasklet,
> + (unsigned long)bdma_chan);
> + list_add_tail(&bdma_chan->dchan.device_node,
> + &mport->dma.channels);
> + }
> +
> + dma_cap_zero(mport->dma.cap_mask);
> + dma_cap_set(DMA_PRIVATE, mport->dma.cap_mask);
> + dma_cap_set(DMA_RAPIDIO, mport->dma.cap_mask);
> +
> + mport->dma.device_alloc_chan_resources = tsi721_alloc_chan_resources;
> + mport->dma.device_free_chan_resources = tsi721_free_chan_resources;
> + mport->dma.device_tx_status = tsi721_tx_status;
> + mport->dma.device_issue_pending = tsi721_issue_pending;
> + mport->dma.device_prep_slave_sg = tsi721_prep_slave_sg;
> + mport->dma.device_control = tsi721_device_control;
> +
> + err = dma_async_device_register(&mport->dma);
> + if (err)
> + dev_err(&priv->pdev->dev, "Failed to register DMA device\n");
> +
> + return err;
> +}
--
~Vinod
^ permalink raw reply
* Re: [RFC PATCH 1/2] RapidIO: Add DMA Engine support for RIO data transfers
From: Vinod Koul @ 2011-10-01 18:01 UTC (permalink / raw)
To: Alexandre Bounine, Dan; +Cc: linux-kernel, akpm, linuxppc-dev
In-Reply-To: <1317418715-9666-1-git-send-email-alexandre.bounine@idt.com>
On Fri, 2011-09-30 at 17:38 -0400, Alexandre Bounine wrote:
Please CC *maintainers* on your patches, get_maintainers.pl will tell
you who. Adding Dan here
> Adds DMA Engine framework support into RapidIO subsystem.
> Uses DMA Engine DMA_SLAVE interface to generate data transfers to/from remote
> RapidIO target devices. Uses scatterlist to describe local data buffer and
> dma_chan.private member to pass target specific information. Supports flat
> data buffer only for a remote side.
The way dmaengine works today is that it doesn't know anything about
client subsystem. But this brings in a subsystem details to dmaengine
which I don't agree with yet.
Why can't we abstract this out??
After going thru the patch, I do not believe that this this is case of
SLAVE transfers, Dan can you please take a look at this patch
> Signed-off-by: Alexandre Bounine <alexandre.bounine@idt.com>
> Cc: Vinod Koul <vinod.koul@intel.com>
> Cc: Kumar Gala <galak@kernel.crashing.org>
> Cc: Matt Porter <mporter@kernel.crashing.org>
> Cc: Li Yang <leoli@freescale.com>
> ---
> drivers/dma/dmaengine.c | 4 ++
> drivers/rapidio/Kconfig | 6 +++
> drivers/rapidio/rio.c | 79 +++++++++++++++++++++++++++++++++++++++++++++
> include/linux/dmaengine.h | 1 +
> include/linux/rio.h | 47 ++++++++++++++++++++++++++
> include/linux/rio_drv.h | 9 +++++
> 6 files changed, 146 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c
> index b48967b..11fdc2c 100644
> --- a/drivers/dma/dmaengine.c
> +++ b/drivers/dma/dmaengine.c
> @@ -699,6 +699,10 @@ int dma_async_device_register(struct dma_device *device)
> !device->device_prep_dma_cyclic);
> BUG_ON(dma_has_cap(DMA_SLAVE, device->cap_mask) &&
> !device->device_control);
> + BUG_ON(dma_has_cap(DMA_RAPIDIO, device->cap_mask) &&
> + !device->device_prep_slave_sg);
> + BUG_ON(dma_has_cap(DMA_RAPIDIO, device->cap_mask) &&
> + !device->device_control);
>
> BUG_ON(!device->device_alloc_chan_resources);
> BUG_ON(!device->device_free_chan_resources);
> diff --git a/drivers/rapidio/Kconfig b/drivers/rapidio/Kconfig
> index bc87192..c4aa279 100644
> --- a/drivers/rapidio/Kconfig
> +++ b/drivers/rapidio/Kconfig
> @@ -34,3 +34,9 @@ config RAPIDIO_DEBUG
> If you are unsure about this, say N here.
>
> source "drivers/rapidio/switches/Kconfig"
> +
> +# This option to be turned on by a device selection
> +config RAPIDIO_DMA_ENGINE
> + bool
> + select DMADEVICES
> + select DMA_ENGINE
> diff --git a/drivers/rapidio/rio.c b/drivers/rapidio/rio.c
> index 86c9a09..e5905fc 100644
> --- a/drivers/rapidio/rio.c
> +++ b/drivers/rapidio/rio.c
> @@ -1121,6 +1121,85 @@ int rio_std_route_clr_table(struct rio_mport *mport, u16 destid, u8 hopcount,
> return 0;
> }
>
> +#ifdef CONFIG_RAPIDIO_DMA_ENGINE
> +
> +#include <linux/dmaengine.h>
> +
> +static bool rio_chan_filter(struct dma_chan *chan, void *arg)
> +{
> + struct rio_dev *rdev = arg;
> +
> + /* Check that DMA device belongs to the right MPORT */
> + return (rdev->net->hport ==
> + container_of(chan->device, struct rio_mport, dma));
> +}
> +
> +/**
> + * rio_request_dma - request RapidIO capable DMA channel that supports
> + * specified target RapidIO device.
> + * @rdev: RIO device control structure
> + *
> + * Returns pointer to allocated DMA channel or NULL if failed.
> + */
> +struct dma_chan *rio_request_dma(struct rio_dev *rdev)
> +{
> + dma_cap_mask_t mask;
> + struct dma_chan *dchan;
> +
> + dma_cap_zero(mask);
> + dma_cap_set(DMA_RAPIDIO, mask);
> + dchan = dma_request_channel(mask, rio_chan_filter, rdev);
> +
> + return dchan;
> +}
> +EXPORT_SYMBOL_GPL(rio_request_dma);
> +
> +/**
> + * rio_release_dma - release specified DMA channel
> + * @dchan: DMA channel to release
> + */
> +void rio_release_dma(struct dma_chan *dchan)
> +{
> + dma_release_channel(dchan);
> +}
> +EXPORT_SYMBOL_GPL(rio_release_dma);
> +
> +/**
> + * rio_dma_prep_slave_sg - RapidIO specific wrapper
> + * for device_prep_slave_sg callback defined by DMAENGINE.
> + * @rdev: RIO device control structure
> + * @dchan: DMA channel to configure
> + * @data: RIO specific data descriptor
> + * @direction: DMA data transfer direction (TO or FROM the device)
> + * @flags: dmaengine defined flags
> + *
> + * Initializes RapidIO capable DMA channel for the specified data transfer.
> + * Uses DMA channel private extension to pass information related to remote
> + * target RIO device.
> + * Returns pointer to DMA transaction descriptor or NULL if failed.
> + */
> +struct dma_async_tx_descriptor *rio_dma_prep_slave_sg(struct rio_dev *rdev,
> + struct dma_chan *dchan, struct rio_dma_data *data,
> + enum dma_data_direction direction, unsigned long flags)
> +{
> + struct dma_async_tx_descriptor *txd = NULL;
> + struct rio_dma_ext rio_ext;
> +
> + rio_ext.destid = rdev->destid;
> + rio_ext.rio_addr_u = data->rio_addr_u;
> + rio_ext.rio_addr = data->rio_addr;
> + rio_ext.wr_type = data->wr_type;
> + dchan->private = &rio_ext;
> +
> + txd = dchan->device->device_prep_slave_sg(dchan, data->sg, data->sg_len,
> + direction, flags);
> +
> + return txd;
> +}
> +EXPORT_SYMBOL_GPL(rio_dma_prep_slave_sg);
You should move the rdev and data to dma_slave_config, that way you
should be able to use the existing _prep_slave_sg function.
> +
> +#endif /* CONFIG_RAPIDIO_DMA_ENGINE */
> +
> static void rio_fixup_device(struct rio_dev *dev)
> {
> }
> diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h
> index 8fbf40e..867b685 100644
> --- a/include/linux/dmaengine.h
> +++ b/include/linux/dmaengine.h
> @@ -70,6 +70,7 @@ enum dma_transaction_type {
> DMA_PRIVATE,
> DMA_ASYNC_TX,
> DMA_SLAVE,
> + DMA_RAPIDIO,
> DMA_CYCLIC,
> };
>
> diff --git a/include/linux/rio.h b/include/linux/rio.h
> index 4d50611..a6d1054c 100644
> --- a/include/linux/rio.h
> +++ b/include/linux/rio.h
> @@ -20,6 +20,9 @@
> #include <linux/errno.h>
> #include <linux/device.h>
> #include <linux/rio_regs.h>
> +#ifdef CONFIG_RAPIDIO_DMA_ENGINE
> +#include <linux/dmaengine.h>
> +#endif
>
> #define RIO_NO_HOPCOUNT -1
> #define RIO_INVALID_DESTID 0xffff
> @@ -254,6 +257,9 @@ struct rio_mport {
> u32 phys_efptr;
> unsigned char name[40];
> void *priv; /* Master port private data */
> +#ifdef CONFIG_RAPIDIO_DMA_ENGINE
> + struct dma_device dma;
> +#endif
> };
>
> /**
> @@ -395,6 +401,47 @@ union rio_pw_msg {
> u32 raw[RIO_PW_MSG_SIZE/sizeof(u32)];
> };
>
> +#ifdef CONFIG_RAPIDIO_DMA_ENGINE
> +
> +/**
> + * enum rio_write_type - RIO write transaction types used in DMA transfers
> + *
> + * Note: RapidIO specification defines write (NWRITE) and
> + * write-with-response (NWRITE_R) data transfer operations.
> + * Existing DMA controllers that service RapidIO may use one of these operations
> + * for entire data transfer or their combination with only the last data packet
> + * requires response.
> + */
> +enum rio_write_type {
> + RDW_DEFAULT, /* default method used by DMA driver */
> + RDW_ALL_NWRITE, /* all packets use NWRITE */
> + RDW_ALL_NWRITE_R, /* all packets use NWRITE_R */
> + RDW_LAST_NWRITE_R, /* last packet uses NWRITE_R, all other - NWRITE */
> +};
Why not use the current mechanism of specifying callback or ACK flags if
you want a response or not.
> +
> +struct rio_dma_ext {
> + u16 destid;
> + u64 rio_addr; /* low 64-bits of 66-bit RapidIO address */
> + u8 rio_addr_u; /* upper 2-bits of 66-bit RapidIO address */
> + enum rio_write_type wr_type; /* preferred RIO write operation type */
> +};
will this address translated to a dma_addr_t or not?
> +
> +struct rio_dma_data {
> + /* Local data (as scatterlist) */
> + struct scatterlist *sg; /* I/O scatter list */
> + unsigned int sg_len; /* size of scatter list */
> + /* Remote device address (flat buffer) */
> + u64 rio_addr; /* low 64-bits of 66-bit RapidIO address */
> + u8 rio_addr_u; /* upper 2-bits of 66-bit RapidIO address */
> + enum rio_write_type wr_type; /* preferred RIO write operation type */
> +};
> +
> +static inline struct rio_mport *dma_to_mport(struct dma_device *ddev)
> +{
> + return container_of(ddev, struct rio_mport, dma);
> +}
> +#endif /* CONFIG_RAPIDIO_DMA_ENGINE */
> +
> /* Architecture and hardware-specific functions */
> extern int rio_register_mport(struct rio_mport *);
> extern int rio_open_inb_mbox(struct rio_mport *, void *, int, int);
> diff --git a/include/linux/rio_drv.h b/include/linux/rio_drv.h
> index 229b3ca..d140996 100644
> --- a/include/linux/rio_drv.h
> +++ b/include/linux/rio_drv.h
> @@ -378,6 +378,15 @@ void rio_unregister_driver(struct rio_driver *);
> struct rio_dev *rio_dev_get(struct rio_dev *);
> void rio_dev_put(struct rio_dev *);
>
> +#ifdef CONFIG_RAPIDIO_DMA_ENGINE
> +extern struct dma_chan *rio_request_dma(struct rio_dev *rdev);
> +extern void rio_release_dma(struct dma_chan *dchan);
> +extern struct dma_async_tx_descriptor *rio_dma_prep_slave_sg(
> + struct rio_dev *rdev, struct dma_chan *dchan,
> + struct rio_dma_data *data, enum dma_data_direction direction,
> + unsigned long flags);
> +#endif
> +
> /**
> * rio_name - Get the unique RIO device identifier
> * @rdev: RIO device
--
~Vinod
^ permalink raw reply
* Re: [RFC PATCH 2/2 -mm] RapidIO: TSI721 Add DMA Engine support
From: Andrew Morton @ 2011-09-30 22:15 UTC (permalink / raw)
To: Alexandre Bounine; +Cc: Vinod Koul, linux-kernel, linuxppc-dev
In-Reply-To: <1317418715-9666-2-git-send-email-alexandre.bounine@idt.com>
On Fri, 30 Sep 2011 17:38:35 -0400
Alexandre Bounine <alexandre.bounine@idt.com> wrote:
> Adds support for DMA Engine API.
>
> Includes following changes:
> - Modifies BDMA register offset definitions to support per-channel handling
> - Separates BDMA channel reserved for RIO Maintenance requests
> - Adds DMA Engine callback routines
>
> ...
>
> 5 files changed, 1029 insertions(+), 90 deletions(-)
hm, what a lot of code.
> +config TSI721_DMA
> + bool "IDT Tsi721 RapidIO DMA support"
> + depends on RAPIDIO_TSI721
> + default "n"
> + select RAPIDIO_DMA_ENGINE
> + help
> + Enable DMA support for IDT Tsi721 PCIe-to-SRIO controller.
Do we really need to offer this decision to the user? If possible it
would be better to always enable the feature where that makes sense.
Better code coverage, less maintenance effort, more effective testing
effort, possibly cleaner code.
>
> ...
>
> +static int tsi721_bdma_ch_init(struct tsi721_bdma_chan *chan)
> +{
> + struct tsi721_dma_desc *bd_ptr;
> + struct device *dev = chan->dchan.device->dev;
> + u64 *sts_ptr;
> + dma_addr_t bd_phys;
> + dma_addr_t sts_phys;
> + int sts_size;
> + int bd_num = chan->bd_num;
> +
> + dev_dbg(dev, "Init Block DMA Engine, CH%d\n", chan->id);
> +
> + /* Allocate space for DMA descriptors */
> + bd_ptr = dma_alloc_coherent(dev,
> + bd_num * sizeof(struct tsi721_dma_desc),
> + &bd_phys, GFP_KERNEL);
> + if (!bd_ptr)
> + return -ENOMEM;
> +
> + chan->bd_phys = bd_phys;
> + chan->bd_base = bd_ptr;
> +
> + memset(bd_ptr, 0, bd_num * sizeof(struct tsi721_dma_desc));
> +
> + dev_dbg(dev, "DMA descriptors @ %p (phys = %llx)\n",
> + bd_ptr, (unsigned long long)bd_phys);
> +
> + /* Allocate space for descriptor status FIFO */
> + sts_size = (bd_num >= TSI721_DMA_MINSTSSZ) ?
> + bd_num : TSI721_DMA_MINSTSSZ;
> + sts_size = roundup_pow_of_two(sts_size);
> + sts_ptr = dma_alloc_coherent(dev,
> + sts_size * sizeof(struct tsi721_dma_sts),
> + &sts_phys, GFP_KERNEL);
> + if (!sts_ptr) {
> + /* Free space allocated for DMA descriptors */
> + dma_free_coherent(dev,
> + bd_num * sizeof(struct tsi721_dma_desc),
> + bd_ptr, bd_phys);
> + chan->bd_base = NULL;
> + return -ENOMEM;
> + }
> +
> + chan->sts_phys = sts_phys;
> + chan->sts_base = sts_ptr;
> + chan->sts_size = sts_size;
> +
> + memset(sts_ptr, 0, sts_size);
You meant
--- a/drivers/rapidio/devices/tsi721.c~rapidio-tsi721-add-dma-engine-support-fix
+++ a/drivers/rapidio/devices/tsi721.c
@@ -1006,7 +1006,7 @@ static int tsi721_bdma_maint_init(struct
priv->mdma.sts_base = sts_ptr;
priv->mdma.sts_size = sts_size;
- memset(sts_ptr, 0, sts_size);
+ memset(sts_ptr, 0, sts_size * sizeof(struct tsi721_dma_sts));
dev_dbg(&priv->pdev->dev,
"desc status FIFO @ %p (phys = %llx) size=0x%x\n",
However that's at least two instances where you wanted a
dma_zalloc_coherent(). How's about we give ourselves one?
> + dev_dbg(dev,
> + "desc status FIFO @ %p (phys = %llx) size=0x%x\n",
> + sts_ptr, (unsigned long long)sts_phys, sts_size);
> +
> + /* Initialize DMA descriptors ring */
> + bd_ptr[bd_num - 1].type_id = cpu_to_le32(DTYPE3 << 29);
> + bd_ptr[bd_num - 1].next_lo = cpu_to_le32((u64)bd_phys &
> + TSI721_DMAC_DPTRL_MASK);
> + bd_ptr[bd_num - 1].next_hi = cpu_to_le32((u64)bd_phys >> 32);
> +
> + /* Setup DMA descriptor pointers */
> + iowrite32(((u64)bd_phys >> 32),
> + chan->regs + TSI721_DMAC_DPTRH);
> + iowrite32(((u64)bd_phys & TSI721_DMAC_DPTRL_MASK),
> + chan->regs + TSI721_DMAC_DPTRL);
> +
> + /* Setup descriptor status FIFO */
> + iowrite32(((u64)sts_phys >> 32),
> + chan->regs + TSI721_DMAC_DSBH);
> + iowrite32(((u64)sts_phys & TSI721_DMAC_DSBL_MASK),
> + chan->regs + TSI721_DMAC_DSBL);
> + iowrite32(TSI721_DMAC_DSSZ_SIZE(sts_size),
> + chan->regs + TSI721_DMAC_DSSZ);
> +
> + /* Clear interrupt bits */
> + iowrite32(TSI721_DMAC_INT_ALL,
> + chan->regs + TSI721_DMAC_INT);
> +
> + ioread32(chan->regs + TSI721_DMAC_INT);
> +
> + /* Toggle DMA channel initialization */
> + iowrite32(TSI721_DMAC_CTL_INIT, chan->regs + TSI721_DMAC_CTL);
> + ioread32(chan->regs + TSI721_DMAC_CTL);
> + chan->wr_count = chan->wr_count_next = 0;
> + chan->sts_rdptr = 0;
> + udelay(10);
> +
> + return 0;
> +}
> +
>
> ...
>
> +{
> + /* Disable BDMA channel interrupts */
> + iowrite32(0, chan->regs + TSI721_DMAC_INTE);
> +
> + tasklet_schedule(&chan->tasklet);
I'm not seeing any tasklet_disable()s on the shutdown/rmmod paths. Is
there anything here which prevents shutdown races against a
still-pending tasklet?
> +}
> +
>
> ...
>
> +static
> +int tsi721_fill_desc(struct tsi721_bdma_chan *chan, struct tsi721_tx_desc *desc,
> + struct scatterlist *sg, enum dma_rtype rtype, u32 sys_size)
> +{
> + struct tsi721_dma_desc *bd_ptr = desc->hw_desc;
> + u64 rio_addr;
> +
> + if (sg_dma_len(sg) > TSI721_DMAD_BCOUNT1 + 1) {
> + dev_err(chan->dchan.device->dev, "SG element is too large\n");
> + return -EINVAL;
> + }
> +
> + dev_dbg(chan->dchan.device->dev,
> + "desc: 0x%llx, addr: 0x%llx len: 0x%x\n",
> + (u64)desc->txd.phys, (unsigned long long)sg_dma_address(sg),
> + sg_dma_len(sg));
> +
> + dev_dbg(chan->dchan.device->dev, "bd_ptr = %p did=%d raddr=0x%llx\n",
> + bd_ptr, desc->destid, desc->rio_addr);
> +
> + /* Initialize DMA descriptor */
> + bd_ptr->type_id = cpu_to_le32((DTYPE1 << 29) |
> + (rtype << 19) | desc->destid);
> + if (desc->interrupt)
> + bd_ptr->type_id |= cpu_to_le32(TSI721_DMAD_IOF);
> + bd_ptr->bcount = cpu_to_le32(((desc->rio_addr & 0x3) << 30) |
> + (sys_size << 26) | sg_dma_len(sg));
> + rio_addr = (desc->rio_addr >> 2) |
> + ((u64)(desc->rio_addr_u & 0x3) << 62);
> + bd_ptr->raddr_lo = cpu_to_le32(rio_addr & 0xffffffff);
> + bd_ptr->raddr_hi = cpu_to_le32(rio_addr >> 32);
> + bd_ptr->t1.bufptr_lo = cpu_to_le32(
> + (u64)sg_dma_address(sg) & 0xffffffff);
> + bd_ptr->t1.bufptr_hi = cpu_to_le32((u64)sg_dma_address(sg) >> 32);
> + bd_ptr->t1.s_dist = 0;
> + bd_ptr->t1.s_size = 0;
> +
> + mb();
Mystery barrier needs a comment explaining why it's here, please. This
is almost always the case with barriers.
> + return 0;
> +}
> +
>
> ...
>
> +static int tsi721_alloc_chan_resources(struct dma_chan *dchan)
> +{
> + struct tsi721_bdma_chan *chan = to_tsi721_chan(dchan);
> + struct tsi721_device *priv = to_tsi721(dchan->device);
> + struct tsi721_tx_desc *desc = NULL;
> + LIST_HEAD(tmp_list);
> + int i;
> + int rc;
> +
> + if (chan->bd_base)
> + return chan->bd_num - 1;
> +
> + /* Initialize BDMA channel */
> + if (tsi721_bdma_ch_init(chan)) {
> + dev_err(dchan->device->dev, "Unable to initialize data DMA"
> + " channel %d, aborting\n", chan->id);
> + return -ENOMEM;
> + }
> +
> + /* Allocate matching number of logical descriptors */
> + desc = kzalloc((chan->bd_num - 1) * sizeof(struct tsi721_tx_desc),
> + GFP_KERNEL);
kcalloc() would be a better fit here.
> + if (!desc) {
> + dev_err(dchan->device->dev,
> + "Failed to allocate logical descriptors\n");
> + rc = -ENOMEM;
> + goto err_out;
> + }
> +
> + chan->tx_desc = desc;
> +
> + for (i = 0; i < chan->bd_num - 1; i++) {
> + dma_async_tx_descriptor_init(&desc[i].txd, dchan);
> + desc[i].txd.tx_submit = tsi721_tx_submit;
> + desc[i].txd.flags = DMA_CTRL_ACK;
> + INIT_LIST_HEAD(&desc[i].tx_list);
> + list_add_tail(&desc[i].desc_node, &tmp_list);
> + }
> +
> + spin_lock_bh(&chan->lock);
> + list_splice(&tmp_list, &chan->free_list);
> + chan->completed_cookie = dchan->cookie = 1;
> + spin_unlock_bh(&chan->lock);
> +
> +#ifdef CONFIG_PCI_MSI
> + if (priv->flags & TSI721_USING_MSIX) {
> + /* Request interrupt service if we are in MSI-X mode */
> + rc = request_irq(
> + priv->msix[TSI721_VECT_DMA0_DONE + chan->id].vector,
> + tsi721_bdma_msix, 0,
> + priv->msix[TSI721_VECT_DMA0_DONE + chan->id].irq_name,
> + (void *)chan);
> +
> + if (rc) {
> + dev_dbg(dchan->device->dev,
> + "Unable to allocate MSI-X interrupt for "
> + "BDMA%d-DONE\n", chan->id);
> + goto err_out;
> + }
> +
> + rc = request_irq(priv->msix[TSI721_VECT_DMA0_INT +
> + chan->id].vector,
> + tsi721_bdma_msix, 0,
> + priv->msix[TSI721_VECT_DMA0_INT + chan->id].irq_name,
> + (void *)chan);
> +
> + if (rc) {
> + dev_dbg(dchan->device->dev,
> + "Unable to allocate MSI-X interrupt for "
> + "BDMA%d-INT\n", chan->id);
> + free_irq(
> + priv->msix[TSI721_VECT_DMA0_DONE +
> + chan->id].vector,
> + (void *)chan);
> + rc = -EIO;
> + goto err_out;
> + }
> + }
> +#endif /* CONFIG_PCI_MSI */
> +
> + tsi721_bdma_interrupt_enable(chan, 1);
> +
> + return chan->bd_num - 1;
> +
> +err_out:
> + kfree(desc);
> + tsi721_bdma_ch_free(chan);
> + return rc;
> +}
> +
>
> ...
>
> +static
> +enum dma_status tsi721_tx_status(struct dma_chan *dchan, dma_cookie_t cookie,
> + struct dma_tx_state *txstate)
> +{
> + struct tsi721_bdma_chan *bdma_chan = to_tsi721_chan(dchan);
> + dma_cookie_t last_used;
> + dma_cookie_t last_completed;
> + int ret;
> +
> + spin_lock_irq(&bdma_chan->lock);
> + last_completed = bdma_chan->completed_cookie;
> + last_used = dchan->cookie;
> + spin_unlock_irq(&bdma_chan->lock);
> +
> + ret = dma_async_is_complete(cookie, last_completed, last_used);
> +
> + dma_set_tx_state(txstate, last_completed, last_used, 0);
> +
> + dev_dbg(dchan->device->dev,
> + "%s: exit, ret: %d, last_completed: %d, last_used: %d\n",
> + __func__, ret, last_completed, last_used);
> +
> + return ret;
> +}
> +
> +static void tsi721_issue_pending(struct dma_chan *dchan)
> +{
> + struct tsi721_bdma_chan *chan = to_tsi721_chan(dchan);
> +
> + dev_dbg(dchan->device->dev, "%s: Entry\n", __func__);
> +
> + if (tsi721_dma_is_idle(chan)) {
> + spin_lock_bh(&chan->lock);
> + tsi721_advance_work(chan);
> + spin_unlock_bh(&chan->lock);
> + } else
> + dev_dbg(dchan->device->dev,
> + "%s: DMA channel still busy\n", __func__);
> +}
I really don't like that a "struct tsi721_bdma_chan *" is called "chan"
in come places and "bdma_chan" in others. "bdma_chan" is better.
The code takes that lock with spin_lock_bh() in some places and
spin_lock_irq() in others. I trust there's some method to it all ;) Has
it been carefully tested with lockdep enabled?
>
> ...
>
^ permalink raw reply
* [RFC PATCH 2/2 -mm] RapidIO: TSI721 Add DMA Engine support
From: Alexandre Bounine @ 2011-09-30 21:38 UTC (permalink / raw)
To: akpm, linux-kernel, linuxppc-dev; +Cc: Vinod Koul, Alexandre Bounine
In-Reply-To: <1317418715-9666-1-git-send-email-alexandre.bounine@idt.com>
Adds support for DMA Engine API.
Includes following changes:
- Modifies BDMA register offset definitions to support per-channel handling
- Separates BDMA channel reserved for RIO Maintenance requests
- Adds DMA Engine callback routines
Signed-off-by: Alexandre Bounine <alexandre.bounine@idt.com>
Cc: Vinod Koul <vinod.koul@intel.com>
Cc: Kumar Gala <galak@kernel.crashing.org>
Cc: Matt Porter <mporter@kernel.crashing.org>
Cc: Li Yang <leoli@freescale.com>
---
drivers/rapidio/devices/Kconfig | 8 +
drivers/rapidio/devices/Makefile | 1 +
drivers/rapidio/devices/tsi721.c | 201 ++++++----
drivers/rapidio/devices/tsi721.h | 107 ++++-
drivers/rapidio/devices/tsi721_dma.c | 802 ++++++++++++++++++++++++++++++++++
5 files changed, 1029 insertions(+), 90 deletions(-)
create mode 100644 drivers/rapidio/devices/tsi721_dma.c
diff --git a/drivers/rapidio/devices/Kconfig b/drivers/rapidio/devices/Kconfig
index 12a9d7f..3a2db3d 100644
--- a/drivers/rapidio/devices/Kconfig
+++ b/drivers/rapidio/devices/Kconfig
@@ -8,3 +8,11 @@ config RAPIDIO_TSI721
default "n"
---help---
Include support for IDT Tsi721 PCI Express Serial RapidIO controller.
+
+config TSI721_DMA
+ bool "IDT Tsi721 RapidIO DMA support"
+ depends on RAPIDIO_TSI721
+ default "n"
+ select RAPIDIO_DMA_ENGINE
+ help
+ Enable DMA support for IDT Tsi721 PCIe-to-SRIO controller.
diff --git a/drivers/rapidio/devices/Makefile b/drivers/rapidio/devices/Makefile
index 3b7b4e2..8cbce45 100644
--- a/drivers/rapidio/devices/Makefile
+++ b/drivers/rapidio/devices/Makefile
@@ -3,3 +3,4 @@
#
obj-$(CONFIG_RAPIDIO_TSI721) += tsi721.o
+obj-$(CONFIG_TSI721_DMA) += tsi721_dma.o
diff --git a/drivers/rapidio/devices/tsi721.c b/drivers/rapidio/devices/tsi721.c
index 5225930..5e893a6 100644
--- a/drivers/rapidio/devices/tsi721.c
+++ b/drivers/rapidio/devices/tsi721.c
@@ -108,6 +108,7 @@ static int tsi721_maint_dma(struct tsi721_device *priv, u32 sys_size,
u16 destid, u8 hopcount, u32 offset, int len,
u32 *data, int do_wr)
{
+ void __iomem *regs = priv->regs + TSI721_DMAC_BASE(priv->mdma.ch_id);
struct tsi721_dma_desc *bd_ptr;
u32 rd_count, swr_ptr, ch_stat;
int i, err = 0;
@@ -116,10 +117,9 @@ static int tsi721_maint_dma(struct tsi721_device *priv, u32 sys_size,
if (offset > (RIO_MAINT_SPACE_SZ - len) || (len != sizeof(u32)))
return -EINVAL;
- bd_ptr = priv->bdma[TSI721_DMACH_MAINT].bd_base;
+ bd_ptr = priv->mdma.bd_base;
- rd_count = ioread32(
- priv->regs + TSI721_DMAC_DRDCNT(TSI721_DMACH_MAINT));
+ rd_count = ioread32(regs + TSI721_DMAC_DRDCNT);
/* Initialize DMA descriptor */
bd_ptr[0].type_id = cpu_to_le32((DTYPE2 << 29) | (op << 19) | destid);
@@ -134,19 +134,18 @@ static int tsi721_maint_dma(struct tsi721_device *priv, u32 sys_size,
mb();
/* Start DMA operation */
- iowrite32(rd_count + 2,
- priv->regs + TSI721_DMAC_DWRCNT(TSI721_DMACH_MAINT));
- ioread32(priv->regs + TSI721_DMAC_DWRCNT(TSI721_DMACH_MAINT));
+ iowrite32(rd_count + 2, regs + TSI721_DMAC_DWRCNT);
+ ioread32(regs + TSI721_DMAC_DWRCNT);
i = 0;
/* Wait until DMA transfer is finished */
- while ((ch_stat = ioread32(priv->regs +
- TSI721_DMAC_STS(TSI721_DMACH_MAINT))) & TSI721_DMAC_STS_RUN) {
+ while ((ch_stat = ioread32(regs + TSI721_DMAC_STS))
+ & TSI721_DMAC_STS_RUN) {
udelay(1);
if (++i >= 5000000) {
dev_dbg(&priv->pdev->dev,
"%s : DMA[%d] read timeout ch_status=%x\n",
- __func__, TSI721_DMACH_MAINT, ch_stat);
+ __func__, priv->mdma.ch_id, ch_stat);
if (!do_wr)
*data = 0xffffffff;
err = -EIO;
@@ -162,13 +161,10 @@ static int tsi721_maint_dma(struct tsi721_device *priv, u32 sys_size,
__func__, ch_stat);
dev_dbg(&priv->pdev->dev, "OP=%d : destid=%x hc=%x off=%x\n",
do_wr ? MAINT_WR : MAINT_RD, destid, hopcount, offset);
- iowrite32(TSI721_DMAC_INT_ALL,
- priv->regs + TSI721_DMAC_INT(TSI721_DMACH_MAINT));
- iowrite32(TSI721_DMAC_CTL_INIT,
- priv->regs + TSI721_DMAC_CTL(TSI721_DMACH_MAINT));
+ iowrite32(TSI721_DMAC_INT_ALL, regs + TSI721_DMAC_INT);
+ iowrite32(TSI721_DMAC_CTL_INIT, regs + TSI721_DMAC_CTL);
udelay(10);
- iowrite32(0, priv->regs +
- TSI721_DMAC_DWRCNT(TSI721_DMACH_MAINT));
+ iowrite32(0, regs + TSI721_DMAC_DWRCNT);
udelay(1);
if (!do_wr)
*data = 0xffffffff;
@@ -184,8 +180,8 @@ static int tsi721_maint_dma(struct tsi721_device *priv, u32 sys_size,
* NOTE: Skipping check and clear FIFO entries because we are waiting
* for transfer to be completed.
*/
- swr_ptr = ioread32(priv->regs + TSI721_DMAC_DSWP(TSI721_DMACH_MAINT));
- iowrite32(swr_ptr, priv->regs + TSI721_DMAC_DSRP(TSI721_DMACH_MAINT));
+ swr_ptr = ioread32(regs + TSI721_DMAC_DSWP);
+ iowrite32(swr_ptr, regs + TSI721_DMAC_DSRP);
err_out:
return err;
@@ -540,6 +536,22 @@ static irqreturn_t tsi721_irqhandler(int irq, void *ptr)
tsi721_pw_handler(mport);
}
+#ifdef CONFIG_TSI721_DMA
+ if (dev_int & TSI721_DEV_INT_BDMA_CH) {
+ int ch;
+
+ if (dev_ch_int & TSI721_INT_BDMA_CHAN_M) {
+ dev_dbg(&priv->pdev->dev,
+ "IRQ from DMA channel 0x%08x\n", dev_ch_int);
+
+ for (ch = 0; ch < TSI721_DMA_MAXCH; ch++) {
+ if (!(dev_ch_int & TSI721_INT_BDMA_CHAN(ch)))
+ continue;
+ tsi721_bdma_handler(&priv->bdma[ch]);
+ }
+ }
+ }
+#endif
return IRQ_HANDLED;
}
@@ -552,18 +564,26 @@ static void tsi721_interrupts_init(struct tsi721_device *priv)
priv->regs + TSI721_SR_CHINT(IDB_QUEUE));
iowrite32(TSI721_SR_CHINT_IDBQRCV,
priv->regs + TSI721_SR_CHINTE(IDB_QUEUE));
- iowrite32(TSI721_INT_SR2PC_CHAN(IDB_QUEUE),
- priv->regs + TSI721_DEV_CHAN_INTE);
/* Enable SRIO MAC interrupts */
iowrite32(TSI721_RIO_EM_DEV_INT_EN_INT,
priv->regs + TSI721_RIO_EM_DEV_INT_EN);
+ /* Enable interrupts from channels in use */
+#ifdef CONFIG_TSI721_DMA
+ intr = TSI721_INT_SR2PC_CHAN(IDB_QUEUE) |
+ (TSI721_INT_BDMA_CHAN_M &
+ ~TSI721_INT_BDMA_CHAN(TSI721_DMACH_MAINT));
+#else
+ intr = TSI721_INT_SR2PC_CHAN(IDB_QUEUE);
+#endif
+ iowrite32(intr, priv->regs + TSI721_DEV_CHAN_INTE);
+
if (priv->flags & TSI721_USING_MSIX)
intr = TSI721_DEV_INT_SRIO;
else
intr = TSI721_DEV_INT_SR2PC_CH | TSI721_DEV_INT_SRIO |
- TSI721_DEV_INT_SMSG_CH;
+ TSI721_DEV_INT_SMSG_CH | TSI721_DEV_INT_BDMA_CH;
iowrite32(intr, priv->regs + TSI721_DEV_INTE);
ioread32(priv->regs + TSI721_DEV_INTE);
@@ -714,12 +734,29 @@ static int tsi721_enable_msix(struct tsi721_device *priv)
TSI721_MSIX_OMSG_INT(i);
}
+#ifdef CONFIG_TSI721_DMA
+ /*
+ * Initialize MSI-X entries for Block DMA Engine:
+ * this driver supports XXX DMA channels
+ * (one is reserved for SRIO maintenance transactions)
+ */
+ for (i = 0; i < TSI721_DMA_CHNUM; i++) {
+ entries[TSI721_VECT_DMA0_DONE + i].entry =
+ TSI721_MSIX_DMACH_DONE(i);
+ entries[TSI721_VECT_DMA0_INT + i].entry =
+ TSI721_MSIX_DMACH_INT(i);
+ }
+#endif /* CONFIG_TSI721_DMA */
+
err = pci_enable_msix(priv->pdev, entries, ARRAY_SIZE(entries));
if (err) {
if (err > 0)
dev_info(&priv->pdev->dev,
"Only %d MSI-X vectors available, "
"not using MSI-X\n", err);
+ else
+ dev_err(&priv->pdev->dev,
+ "Failed to enable MSI-X (err=%d)\n", err);
return err;
}
@@ -759,6 +796,22 @@ static int tsi721_enable_msix(struct tsi721_device *priv)
i, pci_name(priv->pdev));
}
+#ifdef CONFIG_TSI721_DMA
+ for (i = 0; i < TSI721_DMA_CHNUM; i++) {
+ priv->msix[TSI721_VECT_DMA0_DONE + i].vector =
+ entries[TSI721_VECT_DMA0_DONE + i].vector;
+ snprintf(priv->msix[TSI721_VECT_DMA0_DONE + i].irq_name,
+ IRQ_DEVICE_NAME_MAX, DRV_NAME "-dmad%d@pci:%s",
+ i, pci_name(priv->pdev));
+
+ priv->msix[TSI721_VECT_DMA0_INT + i].vector =
+ entries[TSI721_VECT_DMA0_INT + i].vector;
+ snprintf(priv->msix[TSI721_VECT_DMA0_INT + i].irq_name,
+ IRQ_DEVICE_NAME_MAX, DRV_NAME "-dmai%d@pci:%s",
+ i, pci_name(priv->pdev));
+ }
+#endif /* CONFIG_TSI721_DMA */
+
return 0;
}
#endif /* CONFIG_PCI_MSI */
@@ -889,20 +942,34 @@ static void tsi721_doorbell_free(struct tsi721_device *priv)
priv->idb_base = NULL;
}
-static int tsi721_bdma_ch_init(struct tsi721_device *priv, int chnum)
+/**
+ * tsi721_bdma_maint_init - Initialize maintenance request BDMA channel.
+ * @priv: pointer to tsi721 private data
+ *
+ * Initialize BDMA channel allocated for RapidIO maintenance read/write
+ * request generation
+ * Returns %0 on success or %-ENOMEM on failure.
+ */
+static int tsi721_bdma_maint_init(struct tsi721_device *priv)
{
struct tsi721_dma_desc *bd_ptr;
u64 *sts_ptr;
dma_addr_t bd_phys, sts_phys;
int sts_size;
- int bd_num = priv->bdma[chnum].bd_num;
+ int bd_num = 2;
+ void __iomem *regs;
- dev_dbg(&priv->pdev->dev, "Init Block DMA Engine, CH%d\n", chnum);
+ dev_dbg(&priv->pdev->dev,
+ "Init Block DMA Engine for Maintenance requests, CH%d\n",
+ TSI721_DMACH_MAINT);
/*
* Initialize DMA channel for maintenance requests
*/
+ priv->mdma.ch_id = TSI721_DMACH_MAINT;
+ regs = priv->regs + TSI721_DMAC_BASE(TSI721_DMACH_MAINT);
+
/* Allocate space for DMA descriptors */
bd_ptr = dma_alloc_coherent(&priv->pdev->dev,
bd_num * sizeof(struct tsi721_dma_desc),
@@ -910,8 +977,9 @@ static int tsi721_bdma_ch_init(struct tsi721_device *priv, int chnum)
if (!bd_ptr)
return -ENOMEM;
- priv->bdma[chnum].bd_phys = bd_phys;
- priv->bdma[chnum].bd_base = bd_ptr;
+ priv->mdma.bd_num = bd_num;
+ priv->mdma.bd_phys = bd_phys;
+ priv->mdma.bd_base = bd_ptr;
memset(bd_ptr, 0, bd_num * sizeof(struct tsi721_dma_desc));
@@ -930,13 +998,13 @@ static int tsi721_bdma_ch_init(struct tsi721_device *priv, int chnum)
dma_free_coherent(&priv->pdev->dev,
bd_num * sizeof(struct tsi721_dma_desc),
bd_ptr, bd_phys);
- priv->bdma[chnum].bd_base = NULL;
+ priv->mdma.bd_base = NULL;
return -ENOMEM;
}
- priv->bdma[chnum].sts_phys = sts_phys;
- priv->bdma[chnum].sts_base = sts_ptr;
- priv->bdma[chnum].sts_size = sts_size;
+ priv->mdma.sts_phys = sts_phys;
+ priv->mdma.sts_base = sts_ptr;
+ priv->mdma.sts_size = sts_size;
memset(sts_ptr, 0, sts_size);
@@ -951,83 +1019,61 @@ static int tsi721_bdma_ch_init(struct tsi721_device *priv, int chnum)
bd_ptr[bd_num - 1].next_hi = cpu_to_le32((u64)bd_phys >> 32);
/* Setup DMA descriptor pointers */
- iowrite32(((u64)bd_phys >> 32),
- priv->regs + TSI721_DMAC_DPTRH(chnum));
+ iowrite32(((u64)bd_phys >> 32), regs + TSI721_DMAC_DPTRH);
iowrite32(((u64)bd_phys & TSI721_DMAC_DPTRL_MASK),
- priv->regs + TSI721_DMAC_DPTRL(chnum));
+ regs + TSI721_DMAC_DPTRL);
/* Setup descriptor status FIFO */
- iowrite32(((u64)sts_phys >> 32),
- priv->regs + TSI721_DMAC_DSBH(chnum));
+ iowrite32(((u64)sts_phys >> 32), regs + TSI721_DMAC_DSBH);
iowrite32(((u64)sts_phys & TSI721_DMAC_DSBL_MASK),
- priv->regs + TSI721_DMAC_DSBL(chnum));
+ regs + TSI721_DMAC_DSBL);
iowrite32(TSI721_DMAC_DSSZ_SIZE(sts_size),
- priv->regs + TSI721_DMAC_DSSZ(chnum));
+ regs + TSI721_DMAC_DSSZ);
/* Clear interrupt bits */
- iowrite32(TSI721_DMAC_INT_ALL,
- priv->regs + TSI721_DMAC_INT(chnum));
+ iowrite32(TSI721_DMAC_INT_ALL, regs + TSI721_DMAC_INT);
- ioread32(priv->regs + TSI721_DMAC_INT(chnum));
+ ioread32(regs + TSI721_DMAC_INT);
/* Toggle DMA channel initialization */
- iowrite32(TSI721_DMAC_CTL_INIT, priv->regs + TSI721_DMAC_CTL(chnum));
- ioread32(priv->regs + TSI721_DMAC_CTL(chnum));
+ iowrite32(TSI721_DMAC_CTL_INIT, regs + TSI721_DMAC_CTL);
+ ioread32(regs + TSI721_DMAC_CTL);
udelay(10);
return 0;
}
-static int tsi721_bdma_ch_free(struct tsi721_device *priv, int chnum)
+static int tsi721_bdma_maint_free(struct tsi721_device *priv)
{
u32 ch_stat;
+ struct tsi721_bdma_maint *mdma = &priv->mdma;
+ void __iomem *regs = priv->regs + TSI721_DMAC_BASE(mdma->ch_id);
- if (priv->bdma[chnum].bd_base == NULL)
+ if (mdma->bd_base == NULL)
return 0;
/* Check if DMA channel still running */
- ch_stat = ioread32(priv->regs + TSI721_DMAC_STS(chnum));
+ ch_stat = ioread32(regs + TSI721_DMAC_STS);
if (ch_stat & TSI721_DMAC_STS_RUN)
return -EFAULT;
/* Put DMA channel into init state */
- iowrite32(TSI721_DMAC_CTL_INIT,
- priv->regs + TSI721_DMAC_CTL(chnum));
+ iowrite32(TSI721_DMAC_CTL_INIT, regs + TSI721_DMAC_CTL);
/* Free space allocated for DMA descriptors */
dma_free_coherent(&priv->pdev->dev,
- priv->bdma[chnum].bd_num * sizeof(struct tsi721_dma_desc),
- priv->bdma[chnum].bd_base, priv->bdma[chnum].bd_phys);
- priv->bdma[chnum].bd_base = NULL;
+ mdma->bd_num * sizeof(struct tsi721_dma_desc),
+ mdma->bd_base, mdma->bd_phys);
+ mdma->bd_base = NULL;
/* Free space allocated for status FIFO */
dma_free_coherent(&priv->pdev->dev,
- priv->bdma[chnum].sts_size * sizeof(struct tsi721_dma_sts),
- priv->bdma[chnum].sts_base, priv->bdma[chnum].sts_phys);
- priv->bdma[chnum].sts_base = NULL;
- return 0;
-}
-
-static int tsi721_bdma_init(struct tsi721_device *priv)
-{
- /* Initialize BDMA channel allocated for RapidIO maintenance read/write
- * request generation
- */
- priv->bdma[TSI721_DMACH_MAINT].bd_num = 2;
- if (tsi721_bdma_ch_init(priv, TSI721_DMACH_MAINT)) {
- dev_err(&priv->pdev->dev, "Unable to initialize maintenance DMA"
- " channel %d, aborting\n", TSI721_DMACH_MAINT);
- return -ENOMEM;
- }
-
+ mdma->sts_size * sizeof(struct tsi721_dma_sts),
+ mdma->sts_base, mdma->sts_phys);
+ mdma->sts_base = NULL;
return 0;
}
-static void tsi721_bdma_free(struct tsi721_device *priv)
-{
- tsi721_bdma_ch_free(priv, TSI721_DMACH_MAINT);
-}
-
/* Enable Inbound Messaging Interrupts */
static void
tsi721_imsg_interrupt_enable(struct tsi721_device *priv, int ch,
@@ -2043,7 +2089,8 @@ static void tsi721_disable_ints(struct tsi721_device *priv)
/* Disable all BDMA Channel interrupts */
for (ch = 0; ch < TSI721_DMA_MAXCH; ch++)
- iowrite32(0, priv->regs + TSI721_DMAC_INTE(ch));
+ iowrite32(0,
+ priv->regs + TSI721_DMAC_BASE(ch) + TSI721_DMAC_INTE);
/* Disable all general BDMA interrupts */
iowrite32(0, priv->regs + TSI721_BDMA_INTE);
@@ -2292,7 +2339,7 @@ static int __devinit tsi721_probe(struct pci_dev *pdev,
tsi721_init_pc2sr_mapping(priv);
tsi721_init_sr2pc_mapping(priv);
- if (tsi721_bdma_init(priv)) {
+ if (tsi721_bdma_maint_init(priv)) {
dev_err(&pdev->dev, "BDMA initialization failed, aborting\n");
err = -ENOMEM;
goto err_unmap_bars;
@@ -2312,12 +2359,16 @@ static int __devinit tsi721_probe(struct pci_dev *pdev,
if (err)
goto err_free_consistent;
+#ifdef CONFIG_TSI721_DMA
+ tsi721_register_dma(priv);
+#endif
+
return 0;
err_free_consistent:
tsi721_doorbell_free(priv);
err_free_bdma:
- tsi721_bdma_free(priv);
+ tsi721_bdma_maint_free(priv);
err_unmap_bars:
if (priv->regs)
iounmap(priv->regs);
diff --git a/drivers/rapidio/devices/tsi721.h b/drivers/rapidio/devices/tsi721.h
index 58be4de..2f756dc 100644
--- a/drivers/rapidio/devices/tsi721.h
+++ b/drivers/rapidio/devices/tsi721.h
@@ -21,6 +21,10 @@
#ifndef __TSI721_H
#define __TSI721_H
+#ifdef CONFIG_TSI721_DMA
+#include <linux/dmaengine.h>
+#endif
+
#define DRV_NAME "tsi721"
#define DEFAULT_HOPCOUNT 0xff
@@ -165,6 +169,8 @@
#define TSI721_DEV_INTE 0x29840
#define TSI721_DEV_INT 0x29844
#define TSI721_DEV_INTSET 0x29848
+#define TSI721_DEV_INT_BDMA_CH 0x00002000
+#define TSI721_DEV_INT_BDMA_NCH 0x00001000
#define TSI721_DEV_INT_SMSG_CH 0x00000800
#define TSI721_DEV_INT_SMSG_NCH 0x00000400
#define TSI721_DEV_INT_SR2PC_CH 0x00000200
@@ -179,6 +185,8 @@
#define TSI721_INT_IMSG_CHAN(x) (1 << (16 + (x)))
#define TSI721_INT_OMSG_CHAN_M 0x0000ff00
#define TSI721_INT_OMSG_CHAN(x) (1 << (8 + (x)))
+#define TSI721_INT_BDMA_CHAN_M 0x000000ff
+#define TSI721_INT_BDMA_CHAN(x) (1 << (x))
/*
* PC2SR block registers
@@ -233,14 +241,16 @@
* x = 0..7
*/
-#define TSI721_DMAC_DWRCNT(x) (0x51000 + (x) * 0x1000)
-#define TSI721_DMAC_DRDCNT(x) (0x51004 + (x) * 0x1000)
+#define TSI721_DMAC_BASE(x) (0x51000 + (x) * 0x1000)
+
+#define TSI721_DMAC_DWRCNT 0x000
+#define TSI721_DMAC_DRDCNT 0x004
-#define TSI721_DMAC_CTL(x) (0x51008 + (x) * 0x1000)
+#define TSI721_DMAC_CTL 0x008
#define TSI721_DMAC_CTL_SUSP 0x00000002
#define TSI721_DMAC_CTL_INIT 0x00000001
-#define TSI721_DMAC_INT(x) (0x5100c + (x) * 0x1000)
+#define TSI721_DMAC_INT 0x00c
#define TSI721_DMAC_INT_STFULL 0x00000010
#define TSI721_DMAC_INT_DONE 0x00000008
#define TSI721_DMAC_INT_SUSP 0x00000004
@@ -248,34 +258,33 @@
#define TSI721_DMAC_INT_IOFDONE 0x00000001
#define TSI721_DMAC_INT_ALL 0x0000001f
-#define TSI721_DMAC_INTSET(x) (0x51010 + (x) * 0x1000)
+#define TSI721_DMAC_INTSET 0x010
-#define TSI721_DMAC_STS(x) (0x51014 + (x) * 0x1000)
+#define TSI721_DMAC_STS 0x014
#define TSI721_DMAC_STS_ABORT 0x00400000
#define TSI721_DMAC_STS_RUN 0x00200000
#define TSI721_DMAC_STS_CS 0x001f0000
-#define TSI721_DMAC_INTE(x) (0x51018 + (x) * 0x1000)
+#define TSI721_DMAC_INTE 0x018
-#define TSI721_DMAC_DPTRL(x) (0x51024 + (x) * 0x1000)
+#define TSI721_DMAC_DPTRL 0x024
#define TSI721_DMAC_DPTRL_MASK 0xffffffe0
-#define TSI721_DMAC_DPTRH(x) (0x51028 + (x) * 0x1000)
+#define TSI721_DMAC_DPTRH 0x028
-#define TSI721_DMAC_DSBL(x) (0x5102c + (x) * 0x1000)
+#define TSI721_DMAC_DSBL 0x02c
#define TSI721_DMAC_DSBL_MASK 0xffffffc0
-#define TSI721_DMAC_DSBH(x) (0x51030 + (x) * 0x1000)
+#define TSI721_DMAC_DSBH 0x030
-#define TSI721_DMAC_DSSZ(x) (0x51034 + (x) * 0x1000)
+#define TSI721_DMAC_DSSZ 0x034
#define TSI721_DMAC_DSSZ_SIZE_M 0x0000000f
#define TSI721_DMAC_DSSZ_SIZE(size) (__fls(size) - 4)
-
-#define TSI721_DMAC_DSRP(x) (0x51038 + (x) * 0x1000)
+#define TSI721_DMAC_DSRP 0x038
#define TSI721_DMAC_DSRP_MASK 0x0007ffff
-#define TSI721_DMAC_DSWP(x) (0x5103c + (x) * 0x1000)
+#define TSI721_DMAC_DSWP 0x03c
#define TSI721_DMAC_DSWP_MASK 0x0007ffff
#define TSI721_BDMA_INTE 0x5f000
@@ -624,7 +633,48 @@ enum tsi721_smsg_int_flag {
/* Structures */
+#ifdef CONFIG_TSI721_DMA
+
+struct tsi721_tx_desc {
+ struct dma_async_tx_descriptor txd;
+ struct tsi721_dma_desc *hw_desc;
+ u16 destid;
+ /* low 64-bits of 66-bit RIO address */
+ u64 rio_addr;
+ /* upper 2-bits of 66-bit RIO address */
+ u8 rio_addr_u;
+ bool interrupt;
+ struct list_head desc_node;
+ struct list_head tx_list;
+};
+
struct tsi721_bdma_chan {
+ int id;
+ void __iomem *regs;
+ int bd_num; /* number of buffer descriptors */
+ void *bd_base; /* start of DMA descriptors */
+ dma_addr_t bd_phys;
+ void *sts_base; /* start of DMA BD status FIFO */
+ dma_addr_t sts_phys;
+ int sts_size;
+ u32 sts_rdptr;
+ u32 wr_count;
+ u32 wr_count_next;
+
+ struct dma_chan dchan;
+ struct tsi721_tx_desc *tx_desc;
+ spinlock_t lock;
+ struct list_head active_list;
+ struct list_head queue;
+ struct list_head free_list;
+ dma_cookie_t completed_cookie;
+ struct tasklet_struct tasklet;
+};
+
+#endif /* CONFIG_TSI721_DMA */
+
+struct tsi721_bdma_maint {
+ int ch_id; /* BDMA channel number */
int bd_num; /* number of buffer descriptors */
void *bd_base; /* start of DMA descriptors */
dma_addr_t bd_phys;
@@ -719,6 +769,24 @@ enum tsi721_msix_vect {
TSI721_VECT_IMB1_INT,
TSI721_VECT_IMB2_INT,
TSI721_VECT_IMB3_INT,
+#ifdef CONFIG_TSI721_DMA
+ TSI721_VECT_DMA0_DONE,
+ TSI721_VECT_DMA1_DONE,
+ TSI721_VECT_DMA2_DONE,
+ TSI721_VECT_DMA3_DONE,
+ TSI721_VECT_DMA4_DONE,
+ TSI721_VECT_DMA5_DONE,
+ TSI721_VECT_DMA6_DONE,
+ TSI721_VECT_DMA7_DONE,
+ TSI721_VECT_DMA0_INT,
+ TSI721_VECT_DMA1_INT,
+ TSI721_VECT_DMA2_INT,
+ TSI721_VECT_DMA3_INT,
+ TSI721_VECT_DMA4_INT,
+ TSI721_VECT_DMA5_INT,
+ TSI721_VECT_DMA6_INT,
+ TSI721_VECT_DMA7_INT,
+#endif /* CONFIG_TSI721_DMA */
TSI721_VECT_MAX
};
@@ -752,7 +820,11 @@ struct tsi721_device {
u32 pw_discard_count;
/* BDMA Engine */
+ struct tsi721_bdma_maint mdma; /* Maintenance rd/wr request channel */
+
+#ifdef CONFIG_TSI721_DMA
struct tsi721_bdma_chan bdma[TSI721_DMA_CHNUM];
+#endif
/* Inbound Messaging */
int imsg_init[TSI721_IMSG_CHNUM];
@@ -763,4 +835,9 @@ struct tsi721_device {
struct tsi721_omsg_ring omsg_ring[TSI721_OMSG_CHNUM];
};
+#ifdef CONFIG_TSI721_DMA
+extern void tsi721_bdma_handler(struct tsi721_bdma_chan *chan);
+extern int __devinit tsi721_register_dma(struct tsi721_device *priv);
+#endif
+
#endif
diff --git a/drivers/rapidio/devices/tsi721_dma.c b/drivers/rapidio/devices/tsi721_dma.c
new file mode 100644
index 0000000..75a27a0
--- /dev/null
+++ b/drivers/rapidio/devices/tsi721_dma.c
@@ -0,0 +1,802 @@
+/*
+ * DMA Engine support for Tsi721 PCIExpress-to-SRIO bridge
+ *
+ * Copyright 2011 Integrated Device Technology, Inc.
+ * Alexandre Bounine <alexandre.bounine@idt.com>
+ *
+ * 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, write to the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/io.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/rio.h>
+#include <linux/rio_drv.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/delay.h>
+
+#include "tsi721.h"
+
+static inline struct tsi721_bdma_chan *to_tsi721_chan(struct dma_chan *chan)
+{
+ return container_of(chan, struct tsi721_bdma_chan, dchan);
+}
+
+static inline struct tsi721_device *to_tsi721(struct dma_device *ddev)
+{
+ return container_of(ddev, struct rio_mport, dma)->priv;
+}
+
+static inline
+struct tsi721_tx_desc *to_tsi721_desc(struct dma_async_tx_descriptor *txd)
+{
+ return container_of(txd, struct tsi721_tx_desc, txd);
+}
+
+static inline
+struct tsi721_tx_desc *tsi721_dma_first_active(struct tsi721_bdma_chan *chan)
+{
+ return list_first_entry(&chan->active_list,
+ struct tsi721_tx_desc, desc_node);
+}
+
+static int tsi721_bdma_ch_init(struct tsi721_bdma_chan *chan)
+{
+ struct tsi721_dma_desc *bd_ptr;
+ struct device *dev = chan->dchan.device->dev;
+ u64 *sts_ptr;
+ dma_addr_t bd_phys;
+ dma_addr_t sts_phys;
+ int sts_size;
+ int bd_num = chan->bd_num;
+
+ dev_dbg(dev, "Init Block DMA Engine, CH%d\n", chan->id);
+
+ /* Allocate space for DMA descriptors */
+ bd_ptr = dma_alloc_coherent(dev,
+ bd_num * sizeof(struct tsi721_dma_desc),
+ &bd_phys, GFP_KERNEL);
+ if (!bd_ptr)
+ return -ENOMEM;
+
+ chan->bd_phys = bd_phys;
+ chan->bd_base = bd_ptr;
+
+ memset(bd_ptr, 0, bd_num * sizeof(struct tsi721_dma_desc));
+
+ dev_dbg(dev, "DMA descriptors @ %p (phys = %llx)\n",
+ bd_ptr, (unsigned long long)bd_phys);
+
+ /* Allocate space for descriptor status FIFO */
+ sts_size = (bd_num >= TSI721_DMA_MINSTSSZ) ?
+ bd_num : TSI721_DMA_MINSTSSZ;
+ sts_size = roundup_pow_of_two(sts_size);
+ sts_ptr = dma_alloc_coherent(dev,
+ sts_size * sizeof(struct tsi721_dma_sts),
+ &sts_phys, GFP_KERNEL);
+ if (!sts_ptr) {
+ /* Free space allocated for DMA descriptors */
+ dma_free_coherent(dev,
+ bd_num * sizeof(struct tsi721_dma_desc),
+ bd_ptr, bd_phys);
+ chan->bd_base = NULL;
+ return -ENOMEM;
+ }
+
+ chan->sts_phys = sts_phys;
+ chan->sts_base = sts_ptr;
+ chan->sts_size = sts_size;
+
+ memset(sts_ptr, 0, sts_size);
+
+ dev_dbg(dev,
+ "desc status FIFO @ %p (phys = %llx) size=0x%x\n",
+ sts_ptr, (unsigned long long)sts_phys, sts_size);
+
+ /* Initialize DMA descriptors ring */
+ bd_ptr[bd_num - 1].type_id = cpu_to_le32(DTYPE3 << 29);
+ bd_ptr[bd_num - 1].next_lo = cpu_to_le32((u64)bd_phys &
+ TSI721_DMAC_DPTRL_MASK);
+ bd_ptr[bd_num - 1].next_hi = cpu_to_le32((u64)bd_phys >> 32);
+
+ /* Setup DMA descriptor pointers */
+ iowrite32(((u64)bd_phys >> 32),
+ chan->regs + TSI721_DMAC_DPTRH);
+ iowrite32(((u64)bd_phys & TSI721_DMAC_DPTRL_MASK),
+ chan->regs + TSI721_DMAC_DPTRL);
+
+ /* Setup descriptor status FIFO */
+ iowrite32(((u64)sts_phys >> 32),
+ chan->regs + TSI721_DMAC_DSBH);
+ iowrite32(((u64)sts_phys & TSI721_DMAC_DSBL_MASK),
+ chan->regs + TSI721_DMAC_DSBL);
+ iowrite32(TSI721_DMAC_DSSZ_SIZE(sts_size),
+ chan->regs + TSI721_DMAC_DSSZ);
+
+ /* Clear interrupt bits */
+ iowrite32(TSI721_DMAC_INT_ALL,
+ chan->regs + TSI721_DMAC_INT);
+
+ ioread32(chan->regs + TSI721_DMAC_INT);
+
+ /* Toggle DMA channel initialization */
+ iowrite32(TSI721_DMAC_CTL_INIT, chan->regs + TSI721_DMAC_CTL);
+ ioread32(chan->regs + TSI721_DMAC_CTL);
+ chan->wr_count = chan->wr_count_next = 0;
+ chan->sts_rdptr = 0;
+ udelay(10);
+
+ return 0;
+}
+
+static int tsi721_bdma_ch_free(struct tsi721_bdma_chan *chan)
+{
+ u32 ch_stat;
+
+ if (chan->bd_base == NULL)
+ return 0;
+
+ /* Check if DMA channel still running */
+ ch_stat = ioread32(chan->regs + TSI721_DMAC_STS);
+ if (ch_stat & TSI721_DMAC_STS_RUN)
+ return -EFAULT;
+
+ /* Put DMA channel into init state */
+ iowrite32(TSI721_DMAC_CTL_INIT, chan->regs + TSI721_DMAC_CTL);
+
+ /* Free space allocated for DMA descriptors */
+ dma_free_coherent(chan->dchan.device->dev,
+ chan->bd_num * sizeof(struct tsi721_dma_desc),
+ chan->bd_base, chan->bd_phys);
+ chan->bd_base = NULL;
+
+ /* Free space allocated for status FIFO */
+ dma_free_coherent(chan->dchan.device->dev,
+ chan->sts_size * sizeof(struct tsi721_dma_sts),
+ chan->sts_base, chan->sts_phys);
+ chan->sts_base = NULL;
+ return 0;
+}
+
+static
+void tsi721_bdma_interrupt_enable(struct tsi721_bdma_chan *chan, int enable)
+{
+ if (enable) {
+ /* Clear pending BDMA channel interrupts */
+ iowrite32(TSI721_DMAC_INT_ALL, chan->regs + TSI721_DMAC_INT);
+ ioread32(chan->regs + TSI721_DMAC_INT);
+ /* Enable BDMA channel interrupts */
+ iowrite32(TSI721_DMAC_INT_ALL, chan->regs + TSI721_DMAC_INTE);
+ } else {
+ /* Disable BDMA channel interrupts */
+ iowrite32(0, chan->regs + TSI721_DMAC_INTE);
+ /* Clear pending BDMA channel interrupts */
+ iowrite32(TSI721_DMAC_INT_ALL, chan->regs + TSI721_DMAC_INT);
+ }
+
+}
+
+static bool tsi721_dma_is_idle(struct tsi721_bdma_chan *chan)
+{
+ u32 sts;
+
+ sts = ioread32(chan->regs + TSI721_DMAC_STS);
+ return ((sts & TSI721_DMAC_STS_RUN) == 0);
+}
+
+void tsi721_bdma_handler(struct tsi721_bdma_chan *chan)
+{
+ /* Disable BDMA channel interrupts */
+ iowrite32(0, chan->regs + TSI721_DMAC_INTE);
+
+ tasklet_schedule(&chan->tasklet);
+}
+
+#ifdef CONFIG_PCI_MSI
+/**
+ * tsi721_omsg_msix - MSI-X interrupt handler for outbound messaging
+ * @irq: Linux interrupt number
+ * @ptr: Pointer to interrupt-specific data (mport structure)
+ *
+ * Handles outbound messaging interrupts signaled using MSI-X.
+ */
+static irqreturn_t tsi721_bdma_msix(int irq, void *ptr)
+{
+ struct tsi721_bdma_chan *chan = ptr;
+
+ tsi721_bdma_handler(chan);
+ return IRQ_HANDLED;
+}
+#endif /* CONFIG_PCI_MSI */
+
+/* Must be called with the spinlock held */
+static void tsi721_start_dma(struct tsi721_bdma_chan *chan)
+{
+ if (!tsi721_dma_is_idle(chan)) {
+ dev_err(chan->dchan.device->dev,
+ "BUG: Attempt to start non-idle channel\n");
+ return;
+ }
+
+ if (chan->wr_count == chan->wr_count_next) {
+ dev_err(chan->dchan.device->dev,
+ "BUG: Attempt to start DMA with no BDs ready\n");
+ return;
+ }
+
+ dev_dbg(chan->dchan.device->dev,
+ "tx_chan: %p, chan: %d, regs: %p\n",
+ chan, chan->dchan.chan_id, chan->regs);
+
+ iowrite32(chan->wr_count_next, chan->regs + TSI721_DMAC_DWRCNT);
+ ioread32(chan->regs + TSI721_DMAC_DWRCNT);
+
+ chan->wr_count = chan->wr_count_next;
+}
+
+static void tsi721_desc_put(struct tsi721_bdma_chan *chan,
+ struct tsi721_tx_desc *desc)
+{
+ dev_dbg(chan->dchan.device->dev, "Put desc: %p into free list\n", desc);
+
+ if (desc) {
+ spin_lock_bh(&chan->lock);
+ list_splice_init(&desc->tx_list, &chan->free_list);
+ list_add(&desc->desc_node, &chan->free_list);
+ chan->wr_count_next = chan->wr_count;
+ spin_unlock_bh(&chan->lock);
+ }
+}
+
+static struct tsi721_tx_desc *tsi721_desc_get(struct tsi721_bdma_chan *chan)
+{
+ struct tsi721_tx_desc *tx_desc, *_tx_desc;
+ struct tsi721_tx_desc *ret = NULL;
+ int i;
+
+ spin_lock_bh(&chan->lock);
+ list_for_each_entry_safe(tx_desc, _tx_desc,
+ &chan->free_list, desc_node) {
+ if (async_tx_test_ack(&tx_desc->txd)) {
+ list_del(&tx_desc->desc_node);
+ ret = tx_desc;
+ break;
+ }
+ dev_dbg(chan->dchan.device->dev,
+ "desc %p not ACKed\n", tx_desc);
+ }
+
+ i = chan->wr_count_next % chan->bd_num;
+ if (i == chan->bd_num - 1) {
+ i = 0;
+ chan->wr_count_next++; /* skip link descriptor */
+ }
+
+ chan->wr_count_next++;
+ tx_desc->txd.phys = chan->bd_phys + i * sizeof(struct tsi721_dma_desc);
+ tx_desc->hw_desc = &((struct tsi721_dma_desc *)chan->bd_base)[i];
+
+ spin_unlock_bh(&chan->lock);
+
+ return ret;
+}
+
+static
+int tsi721_fill_desc(struct tsi721_bdma_chan *chan, struct tsi721_tx_desc *desc,
+ struct scatterlist *sg, enum dma_rtype rtype, u32 sys_size)
+{
+ struct tsi721_dma_desc *bd_ptr = desc->hw_desc;
+ u64 rio_addr;
+
+ if (sg_dma_len(sg) > TSI721_DMAD_BCOUNT1 + 1) {
+ dev_err(chan->dchan.device->dev, "SG element is too large\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(chan->dchan.device->dev,
+ "desc: 0x%llx, addr: 0x%llx len: 0x%x\n",
+ (u64)desc->txd.phys, (unsigned long long)sg_dma_address(sg),
+ sg_dma_len(sg));
+
+ dev_dbg(chan->dchan.device->dev, "bd_ptr = %p did=%d raddr=0x%llx\n",
+ bd_ptr, desc->destid, desc->rio_addr);
+
+ /* Initialize DMA descriptor */
+ bd_ptr->type_id = cpu_to_le32((DTYPE1 << 29) |
+ (rtype << 19) | desc->destid);
+ if (desc->interrupt)
+ bd_ptr->type_id |= cpu_to_le32(TSI721_DMAD_IOF);
+ bd_ptr->bcount = cpu_to_le32(((desc->rio_addr & 0x3) << 30) |
+ (sys_size << 26) | sg_dma_len(sg));
+ rio_addr = (desc->rio_addr >> 2) |
+ ((u64)(desc->rio_addr_u & 0x3) << 62);
+ bd_ptr->raddr_lo = cpu_to_le32(rio_addr & 0xffffffff);
+ bd_ptr->raddr_hi = cpu_to_le32(rio_addr >> 32);
+ bd_ptr->t1.bufptr_lo = cpu_to_le32(
+ (u64)sg_dma_address(sg) & 0xffffffff);
+ bd_ptr->t1.bufptr_hi = cpu_to_le32((u64)sg_dma_address(sg) >> 32);
+ bd_ptr->t1.s_dist = 0;
+ bd_ptr->t1.s_size = 0;
+
+ mb();
+
+ return 0;
+}
+
+static void tsi721_dma_chain_complete(struct tsi721_bdma_chan *chan,
+ struct tsi721_tx_desc *desc)
+{
+ struct dma_async_tx_descriptor *txd = &desc->txd;
+ dma_async_tx_callback callback = txd->callback;
+ void *param = txd->callback_param;
+
+ list_splice_init(&desc->tx_list, &chan->free_list);
+ list_move(&desc->desc_node, &chan->free_list);
+ chan->completed_cookie = txd->cookie;
+
+ if (callback)
+ callback(param);
+}
+
+static void tsi721_dma_complete_all(struct tsi721_bdma_chan *chan)
+{
+ struct tsi721_tx_desc *desc, *_d;
+ LIST_HEAD(list);
+
+ BUG_ON(!tsi721_dma_is_idle(chan));
+
+ if (!list_empty(&chan->queue))
+ tsi721_start_dma(chan);
+
+ list_splice_init(&chan->active_list, &list);
+ list_splice_init(&chan->queue, &chan->active_list);
+
+ list_for_each_entry_safe(desc, _d, &list, desc_node)
+ tsi721_dma_chain_complete(chan, desc);
+}
+
+static void tsi721_clr_stat(struct tsi721_bdma_chan *chan)
+{
+ u32 srd_ptr;
+ u64 *sts_ptr;
+ int i, j;
+
+ /* Check and clear descriptor status FIFO entries */
+ srd_ptr = chan->sts_rdptr;
+ sts_ptr = chan->sts_base;
+ j = srd_ptr * 8;
+ while (sts_ptr[j]) {
+ for (i = 0; i < 8 && sts_ptr[j]; i++, j++)
+ sts_ptr[j] = 0;
+
+ ++srd_ptr;
+ srd_ptr %= chan->sts_size;
+ j = srd_ptr * 8;
+ }
+
+ iowrite32(srd_ptr, chan->regs + TSI721_DMAC_DSRP);
+ chan->sts_rdptr = srd_ptr;
+}
+
+static void tsi721_advance_work(struct tsi721_bdma_chan *chan)
+{
+ if (list_empty(&chan->active_list) ||
+ list_is_singular(&chan->active_list)) {
+ dev_dbg(chan->dchan.device->dev,
+ "%s: Active_list empty\n", __func__);
+ tsi721_dma_complete_all(chan);
+ } else {
+ dev_dbg(chan->dchan.device->dev,
+ "%s: Active_list NOT empty\n", __func__);
+ tsi721_dma_chain_complete(chan, tsi721_dma_first_active(chan));
+ tsi721_start_dma(chan);
+ }
+}
+
+static void tsi721_dma_tasklet(unsigned long data)
+{
+ struct tsi721_bdma_chan *chan = (struct tsi721_bdma_chan *)data;
+ u32 dmac_int, dmac_sts;
+
+ dmac_int = ioread32(chan->regs + TSI721_DMAC_INT);
+ dev_dbg(chan->dchan.device->dev, "%s: DMAC%d_INT = 0x%x\n",
+ __func__, chan->id, dmac_int);
+ /* Clear channel interrupts */
+ iowrite32(dmac_int, chan->regs + TSI721_DMAC_INT);
+
+ if (dmac_int & TSI721_DMAC_INT_ERR) {
+ dmac_sts = ioread32(chan->regs + TSI721_DMAC_STS);
+ dev_err(chan->dchan.device->dev,
+ "%s: DMA ERROR - DMAC%d_STS = 0x%x\n",
+ __func__, chan->id, dmac_sts);
+ }
+
+ if (dmac_int & TSI721_DMAC_INT_STFULL) {
+ dev_err(chan->dchan.device->dev,
+ "%s: DMAC%d descriptor status FIFO is full\n",
+ __func__, chan->id);
+ }
+
+ if (dmac_int & (TSI721_DMAC_INT_DONE | TSI721_DMAC_INT_IOFDONE)) {
+ tsi721_clr_stat(chan);
+ spin_lock(&chan->lock);
+ tsi721_advance_work(chan);
+ spin_unlock(&chan->lock);
+ }
+
+ /* Re-Enable BDMA channel interrupts */
+ iowrite32(TSI721_DMAC_INT_ALL, chan->regs + TSI721_DMAC_INTE);
+}
+
+static dma_cookie_t tsi721_tx_submit(struct dma_async_tx_descriptor *txd)
+{
+ struct tsi721_tx_desc *desc = to_tsi721_desc(txd);
+ struct tsi721_bdma_chan *chan = to_tsi721_chan(txd->chan);
+ dma_cookie_t cookie;
+
+ spin_lock_bh(&chan->lock);
+
+ cookie = txd->chan->cookie;
+ if (++cookie < 0)
+ cookie = 1;
+ txd->chan->cookie = cookie;
+ txd->cookie = cookie;
+
+ if (list_empty(&chan->active_list)) {
+ list_add_tail(&desc->desc_node, &chan->active_list);
+ tsi721_start_dma(chan);
+ } else {
+ list_add_tail(&desc->desc_node, &chan->queue);
+ }
+
+ spin_unlock_bh(&chan->lock);
+ return cookie;
+}
+
+static int tsi721_alloc_chan_resources(struct dma_chan *dchan)
+{
+ struct tsi721_bdma_chan *chan = to_tsi721_chan(dchan);
+ struct tsi721_device *priv = to_tsi721(dchan->device);
+ struct tsi721_tx_desc *desc = NULL;
+ LIST_HEAD(tmp_list);
+ int i;
+ int rc;
+
+ if (chan->bd_base)
+ return chan->bd_num - 1;
+
+ /* Initialize BDMA channel */
+ if (tsi721_bdma_ch_init(chan)) {
+ dev_err(dchan->device->dev, "Unable to initialize data DMA"
+ " channel %d, aborting\n", chan->id);
+ return -ENOMEM;
+ }
+
+ /* Allocate matching number of logical descriptors */
+ desc = kzalloc((chan->bd_num - 1) * sizeof(struct tsi721_tx_desc),
+ GFP_KERNEL);
+ if (!desc) {
+ dev_err(dchan->device->dev,
+ "Failed to allocate logical descriptors\n");
+ rc = -ENOMEM;
+ goto err_out;
+ }
+
+ chan->tx_desc = desc;
+
+ for (i = 0; i < chan->bd_num - 1; i++) {
+ dma_async_tx_descriptor_init(&desc[i].txd, dchan);
+ desc[i].txd.tx_submit = tsi721_tx_submit;
+ desc[i].txd.flags = DMA_CTRL_ACK;
+ INIT_LIST_HEAD(&desc[i].tx_list);
+ list_add_tail(&desc[i].desc_node, &tmp_list);
+ }
+
+ spin_lock_bh(&chan->lock);
+ list_splice(&tmp_list, &chan->free_list);
+ chan->completed_cookie = dchan->cookie = 1;
+ spin_unlock_bh(&chan->lock);
+
+#ifdef CONFIG_PCI_MSI
+ if (priv->flags & TSI721_USING_MSIX) {
+ /* Request interrupt service if we are in MSI-X mode */
+ rc = request_irq(
+ priv->msix[TSI721_VECT_DMA0_DONE + chan->id].vector,
+ tsi721_bdma_msix, 0,
+ priv->msix[TSI721_VECT_DMA0_DONE + chan->id].irq_name,
+ (void *)chan);
+
+ if (rc) {
+ dev_dbg(dchan->device->dev,
+ "Unable to allocate MSI-X interrupt for "
+ "BDMA%d-DONE\n", chan->id);
+ goto err_out;
+ }
+
+ rc = request_irq(priv->msix[TSI721_VECT_DMA0_INT +
+ chan->id].vector,
+ tsi721_bdma_msix, 0,
+ priv->msix[TSI721_VECT_DMA0_INT + chan->id].irq_name,
+ (void *)chan);
+
+ if (rc) {
+ dev_dbg(dchan->device->dev,
+ "Unable to allocate MSI-X interrupt for "
+ "BDMA%d-INT\n", chan->id);
+ free_irq(
+ priv->msix[TSI721_VECT_DMA0_DONE +
+ chan->id].vector,
+ (void *)chan);
+ rc = -EIO;
+ goto err_out;
+ }
+ }
+#endif /* CONFIG_PCI_MSI */
+
+ tsi721_bdma_interrupt_enable(chan, 1);
+
+ return chan->bd_num - 1;
+
+err_out:
+ kfree(desc);
+ tsi721_bdma_ch_free(chan);
+ return rc;
+}
+
+static void tsi721_free_chan_resources(struct dma_chan *dchan)
+{
+ struct tsi721_bdma_chan *chan = to_tsi721_chan(dchan);
+ struct tsi721_device *priv = to_tsi721(dchan->device);
+ LIST_HEAD(list);
+
+ dev_dbg(dchan->device->dev, "%s: Entry\n", __func__);
+
+ BUG_ON(!list_empty(&chan->active_list));
+ BUG_ON(!list_empty(&chan->queue));
+
+ spin_lock_irq(&chan->lock);
+ list_splice_init(&chan->free_list, &list);
+ spin_unlock_irq(&chan->lock);
+
+ tsi721_bdma_interrupt_enable(chan, 0);
+
+#ifdef CONFIG_PCI_MSI
+ if (priv->flags & TSI721_USING_MSIX) {
+ free_irq(priv->msix[TSI721_VECT_DMA0_DONE + chan->id].vector,
+ (void *)chan);
+ free_irq(priv->msix[TSI721_VECT_DMA0_INT + chan->id].vector,
+ (void *)chan);
+ }
+#endif /* CONFIG_PCI_MSI */
+
+ tsi721_bdma_ch_free(chan);
+ kfree(chan->tx_desc);
+}
+
+static
+enum dma_status tsi721_tx_status(struct dma_chan *dchan, dma_cookie_t cookie,
+ struct dma_tx_state *txstate)
+{
+ struct tsi721_bdma_chan *bdma_chan = to_tsi721_chan(dchan);
+ dma_cookie_t last_used;
+ dma_cookie_t last_completed;
+ int ret;
+
+ spin_lock_irq(&bdma_chan->lock);
+ last_completed = bdma_chan->completed_cookie;
+ last_used = dchan->cookie;
+ spin_unlock_irq(&bdma_chan->lock);
+
+ ret = dma_async_is_complete(cookie, last_completed, last_used);
+
+ dma_set_tx_state(txstate, last_completed, last_used, 0);
+
+ dev_dbg(dchan->device->dev,
+ "%s: exit, ret: %d, last_completed: %d, last_used: %d\n",
+ __func__, ret, last_completed, last_used);
+
+ return ret;
+}
+
+static void tsi721_issue_pending(struct dma_chan *dchan)
+{
+ struct tsi721_bdma_chan *chan = to_tsi721_chan(dchan);
+
+ dev_dbg(dchan->device->dev, "%s: Entry\n", __func__);
+
+ if (tsi721_dma_is_idle(chan)) {
+ spin_lock_bh(&chan->lock);
+ tsi721_advance_work(chan);
+ spin_unlock_bh(&chan->lock);
+ } else
+ dev_dbg(dchan->device->dev,
+ "%s: DMA channel still busy\n", __func__);
+}
+
+static
+struct dma_async_tx_descriptor *tsi721_prep_slave_sg(struct dma_chan *dchan,
+ struct scatterlist *sgl, unsigned int sg_len,
+ enum dma_data_direction direction, unsigned long flags)
+{
+ struct tsi721_bdma_chan *bdma_chan = to_tsi721_chan(dchan);
+ struct tsi721_tx_desc *desc = NULL;
+ struct tsi721_tx_desc *first = NULL;
+ struct scatterlist *sg;
+ struct rio_dma_ext *rext = dchan->private;
+ u64 rio_addr = rext->rio_addr; /* FIXME: assuming 64-bit rio_addr for now */
+ unsigned int i;
+ u32 sys_size = dma_to_mport(dchan->device)->sys_size;
+ enum dma_rtype rtype;
+
+ if (!sgl || !sg_len) {
+ dev_err(dchan->device->dev, "%s: No SG list\n", __func__);
+ return NULL;
+ }
+
+ if (direction == DMA_FROM_DEVICE)
+ rtype = NREAD;
+ else if (direction == DMA_TO_DEVICE) {
+ switch (rext->wr_type) {
+ case RDW_ALL_NWRITE:
+ rtype = ALL_NWRITE;
+ break;
+ case RDW_ALL_NWRITE_R:
+ rtype = ALL_NWRITE_R;
+ break;
+ case RDW_LAST_NWRITE_R:
+ default:
+ rtype = LAST_NWRITE_R;
+ break;
+ }
+ } else {
+ dev_err(dchan->device->dev,
+ "%s: Unsupported DMA direction option\n", __func__);
+ return NULL;
+ }
+
+ for_each_sg(sgl, sg, sg_len, i) {
+ int err;
+
+ dev_dbg(dchan->device->dev, "%s: sg #%d\n", __func__, i);
+ desc = tsi721_desc_get(bdma_chan);
+ if (!desc) {
+ dev_err(dchan->device->dev,
+ "Not enough descriptors available\n");
+ goto err_desc_get;
+ }
+
+ if (sg_is_last(sg))
+ desc->interrupt = (flags & DMA_PREP_INTERRUPT) != 0;
+ else
+ desc->interrupt = false;
+
+ desc->destid = rext->destid;
+ desc->rio_addr = rio_addr;
+ desc->rio_addr_u = 0;
+
+ err = tsi721_fill_desc(bdma_chan, desc, sg, rtype, sys_size);
+ if (err) {
+ dev_err(dchan->device->dev,
+ "Failed to build desc: %d\n", err);
+ goto err_desc_get;
+ }
+
+ rio_addr += sg_dma_len(sg);
+
+ if (!first)
+ first = desc;
+ else
+ list_add_tail(&desc->desc_node, &first->tx_list);
+ }
+
+ first->txd.cookie = -EBUSY;
+ desc->txd.flags = flags;
+
+ return &first->txd;
+
+err_desc_get:
+ tsi721_desc_put(bdma_chan, first);
+ return NULL;
+}
+
+static int tsi721_device_control(struct dma_chan *dchan, enum dma_ctrl_cmd cmd,
+ unsigned long arg)
+{
+ struct tsi721_bdma_chan *chan = to_tsi721_chan(dchan);
+ struct tsi721_tx_desc *desc, *_d;
+ LIST_HEAD(list);
+
+ dev_dbg(dchan->device->dev, "%s: Entry\n", __func__);
+
+ if (cmd != DMA_TERMINATE_ALL)
+ return -ENXIO;
+
+ spin_lock_bh(&chan->lock);
+
+ /* make sure to stop the transfer */
+ iowrite32(TSI721_DMAC_CTL_SUSP, chan->regs + TSI721_DMAC_CTL);
+
+ list_splice_init(&chan->active_list, &list);
+ list_splice_init(&chan->queue, &list);
+
+ list_for_each_entry_safe(desc, _d, &list, desc_node)
+ tsi721_dma_chain_complete(chan, desc);
+
+ spin_unlock_bh(&chan->lock);
+
+ return 0;
+}
+
+int __devinit tsi721_register_dma(struct tsi721_device *priv)
+{
+ int i;
+ int nr_channels = TSI721_DMA_MAXCH;
+ int err;
+ struct rio_mport *mport = priv->mport;
+
+ mport->dma.dev = &priv->pdev->dev;
+ mport->dma.chancnt = nr_channels;
+
+ INIT_LIST_HEAD(&mport->dma.channels);
+
+ for (i = 0; i < nr_channels; i++) {
+ struct tsi721_bdma_chan *bdma_chan = &priv->bdma[i];
+
+ if (i == TSI721_DMACH_MAINT)
+ continue;
+
+ bdma_chan->bd_num = 64;
+ bdma_chan->regs = priv->regs + TSI721_DMAC_BASE(i);
+
+ bdma_chan->dchan.device = &mport->dma;
+ bdma_chan->dchan.cookie = 1;
+ bdma_chan->dchan.chan_id = i;
+ bdma_chan->id = i;
+
+ spin_lock_init(&bdma_chan->lock);
+
+ INIT_LIST_HEAD(&bdma_chan->active_list);
+ INIT_LIST_HEAD(&bdma_chan->queue);
+ INIT_LIST_HEAD(&bdma_chan->free_list);
+
+ tasklet_init(&bdma_chan->tasklet, tsi721_dma_tasklet,
+ (unsigned long)bdma_chan);
+ list_add_tail(&bdma_chan->dchan.device_node,
+ &mport->dma.channels);
+ }
+
+ dma_cap_zero(mport->dma.cap_mask);
+ dma_cap_set(DMA_PRIVATE, mport->dma.cap_mask);
+ dma_cap_set(DMA_RAPIDIO, mport->dma.cap_mask);
+
+ mport->dma.device_alloc_chan_resources = tsi721_alloc_chan_resources;
+ mport->dma.device_free_chan_resources = tsi721_free_chan_resources;
+ mport->dma.device_tx_status = tsi721_tx_status;
+ mport->dma.device_issue_pending = tsi721_issue_pending;
+ mport->dma.device_prep_slave_sg = tsi721_prep_slave_sg;
+ mport->dma.device_control = tsi721_device_control;
+
+ err = dma_async_device_register(&mport->dma);
+ if (err)
+ dev_err(&priv->pdev->dev, "Failed to register DMA device\n");
+
+ return err;
+}
--
1.7.6
^ permalink raw reply related
* [RFC PATCH 1/2] RapidIO: Add DMA Engine support for RIO data transfers
From: Alexandre Bounine @ 2011-09-30 21:38 UTC (permalink / raw)
To: akpm, linux-kernel, linuxppc-dev; +Cc: Vinod Koul, Alexandre Bounine
Adds DMA Engine framework support into RapidIO subsystem.
Uses DMA Engine DMA_SLAVE interface to generate data transfers to/from remote
RapidIO target devices. Uses scatterlist to describe local data buffer and
dma_chan.private member to pass target specific information. Supports flat
data buffer only for a remote side.
Signed-off-by: Alexandre Bounine <alexandre.bounine@idt.com>
Cc: Vinod Koul <vinod.koul@intel.com>
Cc: Kumar Gala <galak@kernel.crashing.org>
Cc: Matt Porter <mporter@kernel.crashing.org>
Cc: Li Yang <leoli@freescale.com>
---
drivers/dma/dmaengine.c | 4 ++
drivers/rapidio/Kconfig | 6 +++
drivers/rapidio/rio.c | 79 +++++++++++++++++++++++++++++++++++++++++++++
include/linux/dmaengine.h | 1 +
include/linux/rio.h | 47 ++++++++++++++++++++++++++
include/linux/rio_drv.h | 9 +++++
6 files changed, 146 insertions(+), 0 deletions(-)
diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c
index b48967b..11fdc2c 100644
--- a/drivers/dma/dmaengine.c
+++ b/drivers/dma/dmaengine.c
@@ -699,6 +699,10 @@ int dma_async_device_register(struct dma_device *device)
!device->device_prep_dma_cyclic);
BUG_ON(dma_has_cap(DMA_SLAVE, device->cap_mask) &&
!device->device_control);
+ BUG_ON(dma_has_cap(DMA_RAPIDIO, device->cap_mask) &&
+ !device->device_prep_slave_sg);
+ BUG_ON(dma_has_cap(DMA_RAPIDIO, device->cap_mask) &&
+ !device->device_control);
BUG_ON(!device->device_alloc_chan_resources);
BUG_ON(!device->device_free_chan_resources);
diff --git a/drivers/rapidio/Kconfig b/drivers/rapidio/Kconfig
index bc87192..c4aa279 100644
--- a/drivers/rapidio/Kconfig
+++ b/drivers/rapidio/Kconfig
@@ -34,3 +34,9 @@ config RAPIDIO_DEBUG
If you are unsure about this, say N here.
source "drivers/rapidio/switches/Kconfig"
+
+# This option to be turned on by a device selection
+config RAPIDIO_DMA_ENGINE
+ bool
+ select DMADEVICES
+ select DMA_ENGINE
diff --git a/drivers/rapidio/rio.c b/drivers/rapidio/rio.c
index 86c9a09..e5905fc 100644
--- a/drivers/rapidio/rio.c
+++ b/drivers/rapidio/rio.c
@@ -1121,6 +1121,85 @@ int rio_std_route_clr_table(struct rio_mport *mport, u16 destid, u8 hopcount,
return 0;
}
+#ifdef CONFIG_RAPIDIO_DMA_ENGINE
+
+#include <linux/dmaengine.h>
+
+static bool rio_chan_filter(struct dma_chan *chan, void *arg)
+{
+ struct rio_dev *rdev = arg;
+
+ /* Check that DMA device belongs to the right MPORT */
+ return (rdev->net->hport ==
+ container_of(chan->device, struct rio_mport, dma));
+}
+
+/**
+ * rio_request_dma - request RapidIO capable DMA channel that supports
+ * specified target RapidIO device.
+ * @rdev: RIO device control structure
+ *
+ * Returns pointer to allocated DMA channel or NULL if failed.
+ */
+struct dma_chan *rio_request_dma(struct rio_dev *rdev)
+{
+ dma_cap_mask_t mask;
+ struct dma_chan *dchan;
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_RAPIDIO, mask);
+ dchan = dma_request_channel(mask, rio_chan_filter, rdev);
+
+ return dchan;
+}
+EXPORT_SYMBOL_GPL(rio_request_dma);
+
+/**
+ * rio_release_dma - release specified DMA channel
+ * @dchan: DMA channel to release
+ */
+void rio_release_dma(struct dma_chan *dchan)
+{
+ dma_release_channel(dchan);
+}
+EXPORT_SYMBOL_GPL(rio_release_dma);
+
+/**
+ * rio_dma_prep_slave_sg - RapidIO specific wrapper
+ * for device_prep_slave_sg callback defined by DMAENGINE.
+ * @rdev: RIO device control structure
+ * @dchan: DMA channel to configure
+ * @data: RIO specific data descriptor
+ * @direction: DMA data transfer direction (TO or FROM the device)
+ * @flags: dmaengine defined flags
+ *
+ * Initializes RapidIO capable DMA channel for the specified data transfer.
+ * Uses DMA channel private extension to pass information related to remote
+ * target RIO device.
+ * Returns pointer to DMA transaction descriptor or NULL if failed.
+ */
+struct dma_async_tx_descriptor *rio_dma_prep_slave_sg(struct rio_dev *rdev,
+ struct dma_chan *dchan, struct rio_dma_data *data,
+ enum dma_data_direction direction, unsigned long flags)
+{
+ struct dma_async_tx_descriptor *txd = NULL;
+ struct rio_dma_ext rio_ext;
+
+ rio_ext.destid = rdev->destid;
+ rio_ext.rio_addr_u = data->rio_addr_u;
+ rio_ext.rio_addr = data->rio_addr;
+ rio_ext.wr_type = data->wr_type;
+ dchan->private = &rio_ext;
+
+ txd = dchan->device->device_prep_slave_sg(dchan, data->sg, data->sg_len,
+ direction, flags);
+
+ return txd;
+}
+EXPORT_SYMBOL_GPL(rio_dma_prep_slave_sg);
+
+#endif /* CONFIG_RAPIDIO_DMA_ENGINE */
+
static void rio_fixup_device(struct rio_dev *dev)
{
}
diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h
index 8fbf40e..867b685 100644
--- a/include/linux/dmaengine.h
+++ b/include/linux/dmaengine.h
@@ -70,6 +70,7 @@ enum dma_transaction_type {
DMA_PRIVATE,
DMA_ASYNC_TX,
DMA_SLAVE,
+ DMA_RAPIDIO,
DMA_CYCLIC,
};
diff --git a/include/linux/rio.h b/include/linux/rio.h
index 4d50611..a6d1054c 100644
--- a/include/linux/rio.h
+++ b/include/linux/rio.h
@@ -20,6 +20,9 @@
#include <linux/errno.h>
#include <linux/device.h>
#include <linux/rio_regs.h>
+#ifdef CONFIG_RAPIDIO_DMA_ENGINE
+#include <linux/dmaengine.h>
+#endif
#define RIO_NO_HOPCOUNT -1
#define RIO_INVALID_DESTID 0xffff
@@ -254,6 +257,9 @@ struct rio_mport {
u32 phys_efptr;
unsigned char name[40];
void *priv; /* Master port private data */
+#ifdef CONFIG_RAPIDIO_DMA_ENGINE
+ struct dma_device dma;
+#endif
};
/**
@@ -395,6 +401,47 @@ union rio_pw_msg {
u32 raw[RIO_PW_MSG_SIZE/sizeof(u32)];
};
+#ifdef CONFIG_RAPIDIO_DMA_ENGINE
+
+/**
+ * enum rio_write_type - RIO write transaction types used in DMA transfers
+ *
+ * Note: RapidIO specification defines write (NWRITE) and
+ * write-with-response (NWRITE_R) data transfer operations.
+ * Existing DMA controllers that service RapidIO may use one of these operations
+ * for entire data transfer or their combination with only the last data packet
+ * requires response.
+ */
+enum rio_write_type {
+ RDW_DEFAULT, /* default method used by DMA driver */
+ RDW_ALL_NWRITE, /* all packets use NWRITE */
+ RDW_ALL_NWRITE_R, /* all packets use NWRITE_R */
+ RDW_LAST_NWRITE_R, /* last packet uses NWRITE_R, all other - NWRITE */
+};
+
+struct rio_dma_ext {
+ u16 destid;
+ u64 rio_addr; /* low 64-bits of 66-bit RapidIO address */
+ u8 rio_addr_u; /* upper 2-bits of 66-bit RapidIO address */
+ enum rio_write_type wr_type; /* preferred RIO write operation type */
+};
+
+struct rio_dma_data {
+ /* Local data (as scatterlist) */
+ struct scatterlist *sg; /* I/O scatter list */
+ unsigned int sg_len; /* size of scatter list */
+ /* Remote device address (flat buffer) */
+ u64 rio_addr; /* low 64-bits of 66-bit RapidIO address */
+ u8 rio_addr_u; /* upper 2-bits of 66-bit RapidIO address */
+ enum rio_write_type wr_type; /* preferred RIO write operation type */
+};
+
+static inline struct rio_mport *dma_to_mport(struct dma_device *ddev)
+{
+ return container_of(ddev, struct rio_mport, dma);
+}
+#endif /* CONFIG_RAPIDIO_DMA_ENGINE */
+
/* Architecture and hardware-specific functions */
extern int rio_register_mport(struct rio_mport *);
extern int rio_open_inb_mbox(struct rio_mport *, void *, int, int);
diff --git a/include/linux/rio_drv.h b/include/linux/rio_drv.h
index 229b3ca..d140996 100644
--- a/include/linux/rio_drv.h
+++ b/include/linux/rio_drv.h
@@ -378,6 +378,15 @@ void rio_unregister_driver(struct rio_driver *);
struct rio_dev *rio_dev_get(struct rio_dev *);
void rio_dev_put(struct rio_dev *);
+#ifdef CONFIG_RAPIDIO_DMA_ENGINE
+extern struct dma_chan *rio_request_dma(struct rio_dev *rdev);
+extern void rio_release_dma(struct dma_chan *dchan);
+extern struct dma_async_tx_descriptor *rio_dma_prep_slave_sg(
+ struct rio_dev *rdev, struct dma_chan *dchan,
+ struct rio_dma_data *data, enum dma_data_direction direction,
+ unsigned long flags);
+#endif
+
/**
* rio_name - Get the unique RIO device identifier
* @rdev: RIO device
--
1.7.6
^ permalink raw reply related
* Re: [PATCH] mtd: m25p80: add EON flash EN25Q32B into spi flash id table
From: Mike Frysinger @ 2011-09-30 15:00 UTC (permalink / raw)
To: Shaohui Xie; +Cc: linux-mtd, linuxppc-dev
In-Reply-To: <1317366518-11403-1-git-send-email-Shaohui.Xie@freescale.com>
Acked-by: Mike Frysinger <vapier@gentoo.org>
-mike
^ permalink raw reply
* [PATCH v3] powerpc: book3e: WSP: Add Chroma as a new WSP/PowerEN platform.
From: Jimi Xenidis @ 2011-09-30 14:26 UTC (permalink / raw)
To: linuxppc-dev
This patch add the Chroma platform to WSP/PowerEN, which is a PCIe
card (a defconfig is included).
The card includes an H8 service processor that is used to manage the
card. The H8 is connected over the second serial UART on the PowerEN
chip so this patch includes a simple 16550 driver to enable
communication, mostly for "power off" and "rebooting".
This patch also includes a, WSP specific, "halt" method that will shut
of all A2 cores but still leave power on at the chip level. This is
desirable, especially if you wish to interrogate the chip with a
hardware probe after the halt.
Signed-off-by: Jimi Xenidis <jimix@pobox.com>
---
Re: galak@kernel.crashing.org
use make savedefconfig
ARGH!: apparently make savedefconfig does not clean up the INITRAMFS_SOURCE
---
arch/powerpc/configs/chroma_defconfig | 307 +++++++++++++++++++++++++++++++++
arch/powerpc/platforms/wsp/Kconfig | 11 +-
arch/powerpc/platforms/wsp/Makefile | 8 +-
arch/powerpc/platforms/wsp/chroma.c | 56 ++++++
arch/powerpc/platforms/wsp/h8.c | 134 ++++++++++++++
arch/powerpc/platforms/wsp/psr2.c | 56 ++-----
arch/powerpc/platforms/wsp/wsp.c | 115 ++++++++++++
arch/powerpc/platforms/wsp/wsp.h | 16 ++-
8 files changed, 651 insertions(+), 52 deletions(-)
create mode 100644 arch/powerpc/configs/chroma_defconfig
create mode 100644 arch/powerpc/platforms/wsp/chroma.c
create mode 100644 arch/powerpc/platforms/wsp/h8.c
create mode 100644 arch/powerpc/platforms/wsp/wsp.c
diff --git a/arch/powerpc/configs/chroma_defconfig b/arch/powerpc/configs/chroma_defconfig
new file mode 100644
index 0000000..acf7fb2
--- /dev/null
+++ b/arch/powerpc/configs/chroma_defconfig
@@ -0,0 +1,307 @@
+CONFIG_PPC64=y
+CONFIG_PPC_BOOK3E_64=y
+# CONFIG_VIRT_CPU_ACCOUNTING is not set
+CONFIG_SMP=y
+CONFIG_NR_CPUS=256
+CONFIG_EXPERIMENTAL=y
+CONFIG_SYSVIPC=y
+CONFIG_POSIX_MQUEUE=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_TASKSTATS=y
+CONFIG_TASK_DELAY_ACCT=y
+CONFIG_TASK_XACCT=y
+CONFIG_TASK_IO_ACCOUNTING=y
+CONFIG_AUDIT=y
+CONFIG_AUDITSYSCALL=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_BUF_SHIFT=19
+CONFIG_CGROUPS=y
+CONFIG_CGROUP_DEVICE=y
+CONFIG_CPUSETS=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_RESOURCE_COUNTERS=y
+CONFIG_CGROUP_MEM_RES_CTLR=y
+CONFIG_CGROUP_MEM_RES_CTLR_SWAP=y
+CONFIG_NAMESPACES=y
+CONFIG_RELAY=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_INITRAMFS_SOURCE=""
+CONFIG_RD_BZIP2=y
+CONFIG_RD_LZMA=y
+CONFIG_INITRAMFS_COMPRESSION_GZIP=y
+CONFIG_KALLSYMS_ALL=y
+CONFIG_EMBEDDED=y
+CONFIG_PERF_COUNTERS=y
+CONFIG_PROFILING=y
+CONFIG_OPROFILE=y
+CONFIG_KPROBES=y
+CONFIG_MODULES=y
+CONFIG_MODULE_FORCE_LOAD=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+CONFIG_MODVERSIONS=y
+CONFIG_MODULE_SRCVERSION_ALL=y
+CONFIG_SCOM_DEBUGFS=y
+CONFIG_PPC_A2_DD2=y
+CONFIG_KVM_GUEST=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_HZ_100=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_BINFMT_MISC=y
+CONFIG_NUMA=y
+# CONFIG_MIGRATION is not set
+CONFIG_PPC_64K_PAGES=y
+CONFIG_SCHED_SMT=y
+CONFIG_CMDLINE_BOOL=y
+CONFIG_CMDLINE=""
+# CONFIG_SECCOMP is not set
+CONFIG_PCIEPORTBUS=y
+# CONFIG_PCIEASPM is not set
+CONFIG_PCI_MSI=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_XFRM_USER=m
+CONFIG_XFRM_SUB_POLICY=y
+CONFIG_XFRM_STATISTICS=y
+CONFIG_NET_KEY=m
+CONFIG_NET_KEY_MIGRATE=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_IP_ROUTE_MULTIPATH=y
+CONFIG_IP_ROUTE_VERBOSE=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+CONFIG_NET_IPIP=y
+CONFIG_IP_MROUTE=y
+CONFIG_IP_PIMSM_V1=y
+CONFIG_IP_PIMSM_V2=y
+CONFIG_SYN_COOKIES=y
+CONFIG_INET_AH=m
+CONFIG_INET_ESP=m
+CONFIG_INET_IPCOMP=m
+CONFIG_IPV6=y
+CONFIG_IPV6_PRIVACY=y
+CONFIG_IPV6_ROUTER_PREF=y
+CONFIG_IPV6_ROUTE_INFO=y
+CONFIG_IPV6_OPTIMISTIC_DAD=y
+CONFIG_INET6_AH=y
+CONFIG_INET6_ESP=y
+CONFIG_INET6_IPCOMP=y
+CONFIG_IPV6_MIP6=y
+CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION=y
+CONFIG_IPV6_TUNNEL=y
+CONFIG_IPV6_MULTIPLE_TABLES=y
+CONFIG_IPV6_SUBTREES=y
+CONFIG_IPV6_MROUTE=y
+CONFIG_IPV6_PIMSM_V2=y
+CONFIG_NETFILTER=y
+CONFIG_NF_CONNTRACK=m
+CONFIG_NF_CONNTRACK_EVENTS=y
+CONFIG_NF_CT_PROTO_UDPLITE=m
+CONFIG_NF_CONNTRACK_FTP=m
+CONFIG_NF_CONNTRACK_IRC=m
+CONFIG_NF_CONNTRACK_TFTP=m
+CONFIG_NF_CT_NETLINK=m
+CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m
+CONFIG_NETFILTER_XT_TARGET_CONNMARK=m
+CONFIG_NETFILTER_XT_TARGET_MARK=m
+CONFIG_NETFILTER_XT_TARGET_NFLOG=m
+CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m
+CONFIG_NETFILTER_XT_TARGET_TCPMSS=m
+CONFIG_NETFILTER_XT_MATCH_COMMENT=m
+CONFIG_NETFILTER_XT_MATCH_CONNBYTES=m
+CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=m
+CONFIG_NETFILTER_XT_MATCH_CONNMARK=m
+CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m
+CONFIG_NETFILTER_XT_MATCH_DCCP=m
+CONFIG_NETFILTER_XT_MATCH_DSCP=m
+CONFIG_NETFILTER_XT_MATCH_ESP=m
+CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=m
+CONFIG_NETFILTER_XT_MATCH_HELPER=m
+CONFIG_NETFILTER_XT_MATCH_IPRANGE=m
+CONFIG_NETFILTER_XT_MATCH_LENGTH=m
+CONFIG_NETFILTER_XT_MATCH_LIMIT=m
+CONFIG_NETFILTER_XT_MATCH_MAC=m
+CONFIG_NETFILTER_XT_MATCH_MARK=m
+CONFIG_NETFILTER_XT_MATCH_MULTIPORT=m
+CONFIG_NETFILTER_XT_MATCH_OWNER=m
+CONFIG_NETFILTER_XT_MATCH_POLICY=m
+CONFIG_NETFILTER_XT_MATCH_PKTTYPE=m
+CONFIG_NETFILTER_XT_MATCH_QUOTA=m
+CONFIG_NETFILTER_XT_MATCH_RATEEST=m
+CONFIG_NETFILTER_XT_MATCH_REALM=m
+CONFIG_NETFILTER_XT_MATCH_RECENT=m
+CONFIG_NETFILTER_XT_MATCH_SCTP=m
+CONFIG_NETFILTER_XT_MATCH_STATE=m
+CONFIG_NETFILTER_XT_MATCH_STATISTIC=m
+CONFIG_NETFILTER_XT_MATCH_STRING=m
+CONFIG_NETFILTER_XT_MATCH_TCPMSS=m
+CONFIG_NETFILTER_XT_MATCH_TIME=m
+CONFIG_NETFILTER_XT_MATCH_U32=m
+CONFIG_NF_CONNTRACK_IPV4=m
+CONFIG_IP_NF_QUEUE=m
+CONFIG_IP_NF_IPTABLES=m
+CONFIG_IP_NF_MATCH_AH=m
+CONFIG_IP_NF_MATCH_ECN=m
+CONFIG_IP_NF_MATCH_TTL=m
+CONFIG_IP_NF_FILTER=m
+CONFIG_IP_NF_TARGET_REJECT=m
+CONFIG_IP_NF_TARGET_LOG=m
+CONFIG_IP_NF_TARGET_ULOG=m
+CONFIG_NF_NAT=m
+CONFIG_IP_NF_TARGET_MASQUERADE=m
+CONFIG_IP_NF_TARGET_NETMAP=m
+CONFIG_IP_NF_TARGET_REDIRECT=m
+CONFIG_NET_TCPPROBE=y
+# CONFIG_WIRELESS is not set
+CONFIG_NET_9P=y
+CONFIG_NET_9P_DEBUG=y
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_DEVTMPFS=y
+CONFIG_MTD=y
+CONFIG_MTD_CHAR=y
+CONFIG_MTD_BLOCK=y
+CONFIG_MTD_CFI=y
+CONFIG_MTD_CFI_ADV_OPTIONS=y
+CONFIG_MTD_CFI_LE_BYTE_SWAP=y
+CONFIG_MTD_CFI_INTELEXT=y
+CONFIG_MTD_CFI_AMDSTD=y
+CONFIG_MTD_CFI_STAA=y
+CONFIG_MTD_PHYSMAP_OF=y
+CONFIG_PROC_DEVICETREE=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_CRYPTOLOOP=y
+CONFIG_BLK_DEV_NBD=m
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_SIZE=65536
+CONFIG_CDROM_PKTCDVD=y
+CONFIG_MISC_DEVICES=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_BLK_DEV_SR=y
+CONFIG_BLK_DEV_SR_VENDOR=y
+CONFIG_CHR_DEV_SG=y
+CONFIG_SCSI_MULTI_LUN=y
+CONFIG_SCSI_CONSTANTS=y
+CONFIG_SCSI_SPI_ATTRS=y
+CONFIG_SCSI_FC_ATTRS=y
+CONFIG_SCSI_ISCSI_ATTRS=m
+CONFIG_SCSI_SAS_ATTRS=m
+CONFIG_SCSI_SRP_ATTRS=y
+CONFIG_ATA=y
+CONFIG_SATA_AHCI=y
+CONFIG_SATA_SIL24=y
+CONFIG_SATA_MV=y
+CONFIG_SATA_SIL=y
+CONFIG_PATA_CMD64X=y
+CONFIG_PATA_MARVELL=y
+CONFIG_PATA_SIL680=y
+CONFIG_MD=y
+CONFIG_BLK_DEV_MD=y
+CONFIG_MD_LINEAR=y
+CONFIG_BLK_DEV_DM=y
+CONFIG_DM_CRYPT=y
+CONFIG_DM_SNAPSHOT=y
+CONFIG_DM_MIRROR=y
+CONFIG_DM_ZERO=y
+CONFIG_DM_UEVENT=y
+CONFIG_NETDEVICES=y
+CONFIG_TUN=y
+CONFIG_E1000E=y
+CONFIG_TIGON3=y
+# CONFIG_WLAN is not set
+# CONFIG_INPUT is not set
+# CONFIG_SERIO is not set
+# CONFIG_VT is not set
+CONFIG_DEVPTS_MULTIPLE_INSTANCES=y
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_HW_RANDOM=y
+CONFIG_RAW_DRIVER=y
+CONFIG_MAX_RAW_DEVS=1024
+# CONFIG_HWMON is not set
+# CONFIG_VGA_ARB is not set
+# CONFIG_USB_SUPPORT is not set
+CONFIG_EDAC=y
+CONFIG_EDAC_MM_EDAC=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_DS1511=y
+CONFIG_RTC_DRV_DS1553=y
+CONFIG_EXT2_FS=y
+CONFIG_EXT2_FS_XATTR=y
+CONFIG_EXT2_FS_POSIX_ACL=y
+CONFIG_EXT2_FS_SECURITY=y
+CONFIG_EXT2_FS_XIP=y
+CONFIG_EXT3_FS=y
+# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
+CONFIG_EXT3_FS_POSIX_ACL=y
+CONFIG_EXT3_FS_SECURITY=y
+CONFIG_EXT4_FS=y
+# CONFIG_DNOTIFY is not set
+CONFIG_FUSE_FS=y
+CONFIG_ISO9660_FS=y
+CONFIG_JOLIET=y
+CONFIG_ZISOFS=y
+CONFIG_UDF_FS=m
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_PROC_KCORE=y
+CONFIG_TMPFS=y
+CONFIG_TMPFS_POSIX_ACL=y
+CONFIG_CONFIGFS_FS=m
+CONFIG_CRAMFS=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3=y
+CONFIG_NFS_V3_ACL=y
+CONFIG_NFS_V4=y
+CONFIG_NFS_V4_1=y
+CONFIG_ROOT_NFS=y
+CONFIG_CIFS=y
+CONFIG_CIFS_WEAK_PW_HASH=y
+CONFIG_CIFS_XATTR=y
+CONFIG_CIFS_POSIX=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ASCII=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_CRC_CCITT=m
+CONFIG_CRC_T10DIF=y
+CONFIG_LIBCRC32C=m
+CONFIG_PRINTK_TIME=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_STRIP_ASM_SYMS=y
+CONFIG_DETECT_HUNG_TASK=y
+# CONFIG_SCHED_DEBUG is not set
+CONFIG_DEBUG_INFO=y
+CONFIG_FTRACE_SYSCALLS=y
+CONFIG_PPC_EMULATED_STATS=y
+CONFIG_XMON=y
+CONFIG_XMON_DEFAULT=y
+CONFIG_VIRQ_DEBUG=y
+CONFIG_PPC_EARLY_DEBUG=y
+CONFIG_KEYS_DEBUG_PROC_KEYS=y
+CONFIG_CRYPTO_NULL=m
+CONFIG_CRYPTO_TEST=m
+CONFIG_CRYPTO_CCM=m
+CONFIG_CRYPTO_GCM=m
+CONFIG_CRYPTO_PCBC=m
+CONFIG_CRYPTO_MICHAEL_MIC=m
+CONFIG_CRYPTO_SHA256=m
+CONFIG_CRYPTO_SHA512=m
+CONFIG_CRYPTO_TGR192=m
+CONFIG_CRYPTO_WP512=m
+CONFIG_CRYPTO_AES=m
+CONFIG_CRYPTO_ANUBIS=m
+CONFIG_CRYPTO_BLOWFISH=m
+CONFIG_CRYPTO_CAST5=m
+CONFIG_CRYPTO_CAST6=m
+CONFIG_CRYPTO_KHAZAD=m
+CONFIG_CRYPTO_SALSA20=m
+CONFIG_CRYPTO_SERPENT=m
+CONFIG_CRYPTO_TEA=m
+CONFIG_CRYPTO_TWOFISH=m
+CONFIG_CRYPTO_LZO=m
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
+CONFIG_VIRTUALIZATION=y
diff --git a/arch/powerpc/platforms/wsp/Kconfig b/arch/powerpc/platforms/wsp/Kconfig
index ea2811c..a3eef8e 100644
--- a/arch/powerpc/platforms/wsp/Kconfig
+++ b/arch/powerpc/platforms/wsp/Kconfig
@@ -1,6 +1,7 @@
config PPC_WSP
bool
select PPC_A2
+ select GENERIC_TBSYNC
select PPC_ICSWX
select PPC_SCOM
select PPC_XICS
@@ -8,14 +9,20 @@ config PPC_WSP
select PCI
select PPC_IO_WORKAROUNDS if PCI
select PPC_INDIRECT_PIO if PCI
+ select PPC_WSP_COPRO
default n
menu "WSP platform selection"
depends on PPC_BOOK3E_64
config PPC_PSR2
- bool "PSR-2 platform"
- select GENERIC_TBSYNC
+ bool "PowerEN System Reference Platform 2"
+ select EPAPR_BOOT
+ select PPC_WSP
+ default y
+
+config PPC_CHROMA
+ bool "PowerEN PCIe Chroma Card"
select EPAPR_BOOT
select PPC_WSP
default y
diff --git a/arch/powerpc/platforms/wsp/Makefile b/arch/powerpc/platforms/wsp/Makefile
index a1486b4..56817ac 100644
--- a/arch/powerpc/platforms/wsp/Makefile
+++ b/arch/powerpc/platforms/wsp/Makefile
@@ -1,8 +1,10 @@
ccflags-y += -mno-minimal-toc
-obj-y += setup.o ics.o
-obj-$(CONFIG_PPC_PSR2) += psr2.o opb_pic.o
+obj-y += setup.o ics.o wsp.o
+obj-$(CONFIG_PPC_PSR2) += psr2.o
+obj-$(CONFIG_PPC_CHROMA) += chroma.o h8.o
+obj-$(CONFIG_PPC_WSP) += opb_pic.o
obj-$(CONFIG_PPC_WSP) += scom_wsp.o
obj-$(CONFIG_SMP) += smp.o scom_smp.o
obj-$(CONFIG_PCI) += wsp_pci.o
-obj-$(CONFIG_PCI_MSI) += msi.o
\ No newline at end of file
+obj-$(CONFIG_PCI_MSI) += msi.o
diff --git a/arch/powerpc/platforms/wsp/chroma.c b/arch/powerpc/platforms/wsp/chroma.c
new file mode 100644
index 0000000..ca6fa26
--- /dev/null
+++ b/arch/powerpc/platforms/wsp/chroma.c
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2008-2011, IBM 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.
+ */
+
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/of.h>
+#include <linux/smp.h>
+#include <linux/time.h>
+
+#include <asm/machdep.h>
+#include <asm/system.h>
+#include <asm/udbg.h>
+
+#include "ics.h"
+#include "wsp.h"
+
+void __init chroma_setup_arch(void)
+{
+ wsp_setup_arch();
+ wsp_setup_h8();
+
+}
+
+static int __init chroma_probe(void)
+{
+ unsigned long root = of_get_flat_dt_root();
+
+ if (!of_flat_dt_is_compatible(root, "ibm,wsp-chroma"))
+ return 0;
+
+ return 1;
+}
+
+define_machine(chroma_md) {
+ .name = "Chroma PCIe",
+ .probe = chroma_probe,
+ .setup_arch = chroma_setup_arch,
+ .restart = wsp_h8_restart,
+ .power_off = wsp_h8_power_off,
+ .halt = wsp_halt,
+ .calibrate_decr = generic_calibrate_decr,
+ .init_IRQ = wsp_setup_irq,
+ .progress = udbg_progress,
+ .power_save = book3e_idle,
+};
+
+machine_arch_initcall(chroma_md, wsp_probe_devices);
diff --git a/arch/powerpc/platforms/wsp/h8.c b/arch/powerpc/platforms/wsp/h8.c
new file mode 100644
index 0000000..d18e6cc
--- /dev/null
+++ b/arch/powerpc/platforms/wsp/h8.c
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2008-2011, IBM 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/io.h>
+
+#include "wsp.h"
+
+/*
+ * The UART connection to the H8 is over ttyS1 which is just a 16550.
+ * We assume that FW has it setup right and no one messes with it.
+ */
+
+
+static u8 __iomem *h8;
+
+#define RBR 0 /* Receiver Buffer Register */
+#define THR 0 /* Transmitter Holding Register */
+#define LSR 5 /* Line Status Register */
+#define LSR_DR 0x01 /* LSR value for Data-Ready */
+#define LSR_THRE 0x20 /* LSR value for Transmitter-Holding-Register-Empty */
+static void wsp_h8_putc(int c)
+{
+ u8 lsr;
+
+ do {
+ lsr = readb(h8 + LSR);
+ } while ((lsr & LSR_THRE) != LSR_THRE);
+ writeb(c, h8 + THR);
+}
+
+static int wsp_h8_getc(void)
+{
+ u8 lsr;
+
+ do {
+ lsr = readb(h8 + LSR);
+ } while ((lsr & LSR_DR) != LSR_DR);
+
+ return readb(h8 + RBR);
+}
+
+static void wsp_h8_puts(const char *s, int sz)
+{
+ int i;
+
+ for (i = 0; i < sz; i++) {
+ wsp_h8_putc(s[i]);
+
+ /* no flow control so wait for echo */
+ wsp_h8_getc();
+ }
+ wsp_h8_putc('\r');
+ wsp_h8_putc('\n');
+}
+
+static void wsp_h8_terminal_cmd(const char *cmd, int sz)
+{
+ hard_irq_disable();
+ wsp_h8_puts(cmd, sz);
+ /* should never return, but just in case */
+ for (;;)
+ continue;
+}
+
+
+void wsp_h8_restart(char *cmd)
+{
+ static const char restart[] = "warm-reset";
+
+ (void)cmd;
+ wsp_h8_terminal_cmd(restart, sizeof(restart) - 1);
+}
+
+void wsp_h8_power_off(void)
+{
+ static const char off[] = "power-off";
+
+ wsp_h8_terminal_cmd(off, sizeof(off) - 1);
+}
+
+static void __iomem *wsp_h8_getaddr(void)
+{
+ struct device_node *aliases;
+ struct device_node *uart;
+ struct property *path;
+ void __iomem *va = NULL;
+
+ /*
+ * there is nothing in the devtree to tell us which is mapped
+ * to the H8, but se know it is the second serial port.
+ */
+
+ aliases = of_find_node_by_path("/aliases");
+ if (aliases == NULL)
+ return NULL;
+
+ path = of_find_property(aliases, "serial1", NULL);
+ if (path == NULL)
+ goto out;
+
+ uart = of_find_node_by_path(path->value);
+ if (uart == NULL)
+ goto out;
+
+ va = of_iomap(uart, 0);
+
+ /* remove it so no one messes with it */
+ of_detach_node(uart);
+ of_node_put(uart);
+
+out:
+ of_node_put(aliases);
+
+ return va;
+}
+
+void __init wsp_setup_h8(void)
+{
+ h8 = wsp_h8_getaddr();
+
+ /* Devtree change? lets hard map it anyway */
+ if (h8 == NULL) {
+ pr_warn("UART to H8 could not be found");
+ h8 = ioremap(0xffc0008000ULL, 0x100);
+ }
+}
diff --git a/arch/powerpc/platforms/wsp/psr2.c b/arch/powerpc/platforms/wsp/psr2.c
index 166f2e4..0c1ae06 100644
--- a/arch/powerpc/platforms/wsp/psr2.c
+++ b/arch/powerpc/platforms/wsp/psr2.c
@@ -14,10 +14,10 @@
#include <linux/mm.h>
#include <linux/of.h>
#include <linux/smp.h>
+#include <linux/time.h>
#include <asm/machdep.h>
#include <asm/system.h>
-#include <asm/time.h>
#include <asm/udbg.h>
#include "ics.h"
@@ -27,7 +27,8 @@
static void psr2_spin(void)
{
hard_irq_disable();
- for (;;) ;
+ for (;;)
+ continue;
}
static void psr2_restart(char *cmd)
@@ -35,65 +36,32 @@ static void psr2_restart(char *cmd)
psr2_spin();
}
-static int psr2_probe_devices(void)
-{
- struct device_node *np;
-
- /* Our RTC is a ds1500. It seems to be programatically compatible
- * with the ds1511 for which we have a driver so let's use that
- */
- np = of_find_compatible_node(NULL, NULL, "dallas,ds1500");
- if (np != NULL) {
- struct resource res;
- if (of_address_to_resource(np, 0, &res) == 0)
- platform_device_register_simple("ds1511", 0, &res, 1);
- }
- return 0;
-}
-machine_arch_initcall(psr2_md, psr2_probe_devices);
-
-static void __init psr2_setup_arch(void)
-{
- /* init to some ~sane value until calibrate_delay() runs */
- loops_per_jiffy = 50000000;
-
- scom_init_wsp();
-
- /* Setup SMP callback */
-#ifdef CONFIG_SMP
- a2_setup_smp();
-#endif
-#ifdef CONFIG_PCI
- wsp_setup_pci();
-#endif
-
-}
-
static int __init psr2_probe(void)
{
unsigned long root = of_get_flat_dt_root();
+ if (of_flat_dt_is_compatible(root, "ibm,wsp-chroma")) {
+ /* chroma systems also claim they are psr2s */
+ return 0;
+ }
+
if (!of_flat_dt_is_compatible(root, "ibm,psr2"))
return 0;
return 1;
}
-static void __init psr2_init_irq(void)
-{
- wsp_init_irq();
- opb_pic_init();
-}
-
define_machine(psr2_md) {
.name = "PSR2 A2",
.probe = psr2_probe,
- .setup_arch = psr2_setup_arch,
+ .setup_arch = wsp_setup_arch,
.restart = psr2_restart,
.power_off = psr2_spin,
.halt = psr2_spin,
.calibrate_decr = generic_calibrate_decr,
- .init_IRQ = psr2_init_irq,
+ .init_IRQ = wsp_setup_irq,
.progress = udbg_progress,
.power_save = book3e_idle,
};
+
+machine_arch_initcall(psr2_md, wsp_probe_devices);
diff --git a/arch/powerpc/platforms/wsp/wsp.c b/arch/powerpc/platforms/wsp/wsp.c
new file mode 100644
index 0000000..d25cc96
--- /dev/null
+++ b/arch/powerpc/platforms/wsp/wsp.c
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2008-2011, IBM 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/smp.h>
+#include <linux/delay.h>
+#include <linux/time.h>
+
+#include <asm/scom.h>
+
+#include "wsp.h"
+#include "ics.h"
+
+#define WSP_SOC_COMPATIBLE "ibm,wsp-soc"
+#define PBIC_COMPATIBLE "ibm,wsp-pbic"
+#define COPRO_COMPATIBLE "ibm,wsp-coprocessor"
+
+static int __init wsp_probe_buses(void)
+{
+ static __initdata struct of_device_id bus_ids[] = {
+ /*
+ * every node in between needs to be here or you won't
+ * find it
+ */
+ { .compatible = WSP_SOC_COMPATIBLE, },
+ { .compatible = PBIC_COMPATIBLE, },
+ { .compatible = COPRO_COMPATIBLE, },
+ {},
+ };
+ of_platform_bus_probe(NULL, bus_ids, NULL);
+
+ return 0;
+}
+
+void __init wsp_setup_arch(void)
+{
+ /* init to some ~sane value until calibrate_delay() runs */
+ loops_per_jiffy = 50000000;
+
+ scom_init_wsp();
+
+ /* Setup SMP callback */
+#ifdef CONFIG_SMP
+ a2_setup_smp();
+#endif
+#ifdef CONFIG_PCI
+ wsp_setup_pci();
+#endif
+}
+
+void __init wsp_setup_irq(void)
+{
+ wsp_init_irq();
+ opb_pic_init();
+}
+
+
+int __init wsp_probe_devices(void)
+{
+ struct device_node *np;
+
+ /* Our RTC is a ds1500. It seems to be programatically compatible
+ * with the ds1511 for which we have a driver so let's use that
+ */
+ np = of_find_compatible_node(NULL, NULL, "dallas,ds1500");
+ if (np != NULL) {
+ struct resource res;
+ if (of_address_to_resource(np, 0, &res) == 0)
+ platform_device_register_simple("ds1511", 0, &res, 1);
+ }
+
+ wsp_probe_buses();
+
+ return 0;
+}
+
+void wsp_halt(void)
+{
+ u64 val;
+ scom_map_t m;
+ struct device_node *dn;
+ struct device_node *mine;
+ struct device_node *me;
+
+ me = of_get_cpu_node(smp_processor_id(), NULL);
+ mine = scom_find_parent(me);
+
+ /* This will halt all the A2s but not power off the chip */
+ for_each_node_with_property(dn, "scom-controller") {
+ if (dn == mine)
+ continue;
+ m = scom_map(dn, 0, 1);
+
+ /* read-modify-write it so the HW probe does not get
+ * confused */
+ val = scom_read(m, 0);
+ val |= 1;
+ scom_write(m, 0, val);
+ scom_unmap(m);
+ }
+ m = scom_map(mine, 0, 1);
+ val = scom_read(m, 0);
+ val |= 1;
+ scom_write(m, 0, val);
+ /* should never return */
+ scom_unmap(m);
+}
diff --git a/arch/powerpc/platforms/wsp/wsp.h b/arch/powerpc/platforms/wsp/wsp.h
index 3347981..10c1d1f 100644
--- a/arch/powerpc/platforms/wsp/wsp.h
+++ b/arch/powerpc/platforms/wsp/wsp.h
@@ -6,15 +6,25 @@
/* Devtree compatible strings for major devices */
#define PCIE_COMPATIBLE "ibm,wsp-pciex"
+extern void wsp_setup_arch(void);
+extern void wsp_setup_irq(void);
+extern int wsp_probe_devices(void);
+extern void wsp_halt(void);
+
extern void wsp_setup_pci(void);
extern void scom_init_wsp(void);
extern void a2_setup_smp(void);
extern int a2_scom_startup_cpu(unsigned int lcpu, int thr_idx,
struct device_node *np);
-int smp_a2_cpu_bootable(unsigned int nr);
-int __devinit smp_a2_kick_cpu(int nr);
+extern int smp_a2_cpu_bootable(unsigned int nr);
+extern int __devinit smp_a2_kick_cpu(int nr);
+
+extern void opb_pic_init(void);
-void opb_pic_init(void);
+/* chroma specific managment */
+extern void wsp_h8_restart(char *cmd);
+extern void wsp_h8_power_off(void);
+extern void __init wsp_setup_h8(void);
#endif /* __WSP_H */
--
1.7.0.4
^ permalink raw reply related
* Enabling MBX in MPC5121
From: Einar Már Björgvinsson @ 2011-09-30 14:16 UTC (permalink / raw)
To: linuxppc-dev@lists.ozlabs.org
[-- Attachment #1: Type: text/plain, Size: 271 bytes --]
Hi all
I am interested in enabling the GPU on the MPC5121 for 2d/3d acceleration. I've read some misleading informations about the status of support in Linux for this.
What is the status today ? is it easily possible ? if so, how ?
regards
Einar M. Björgvinsson
[-- Attachment #2: Type: text/html, Size: 629 bytes --]
^ permalink raw reply
* [PATCH] mtd: m25p80: add EON flash EN25Q32B into spi flash id table
From: Shaohui Xie @ 2011-09-30 7:08 UTC (permalink / raw)
To: linuxppc-dev; +Cc: linux-mtd, Shaohui Xie
Add support for EON spi flash EN25Q32B, which is not listed in id table,
need to add it in the id table to support the EON flash.
Signed-off-by: Shaohui Xie <Shaohui.Xie@freescale.com>
Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
---
drivers/mtd/devices/m25p80.c | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index 35180e4..4e20c4d 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -671,6 +671,7 @@ static const struct spi_device_id m25p_ids[] = {
/* EON -- en25xxx */
{ "en25f32", INFO(0x1c3116, 0, 64 * 1024, 64, SECT_4K) },
{ "en25p32", INFO(0x1c2016, 0, 64 * 1024, 64, 0) },
+ { "en25q32b", INFO(0x1c3016, 0, 64 * 1024, 64, 0) },
{ "en25p64", INFO(0x1c2017, 0, 64 * 1024, 128, 0) },
/* Intel/Numonyx -- xxxs33b */
--
1.6.4
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox