* Re: [PATCH v5 1/2] dmaengine: Add context parameter to prep_dma_sg and prep_interleaved_dma
From: Vinod Koul @ 2012-03-26 6:24 UTC (permalink / raw)
To: Ravi Kumar V
Cc: tsoni, Russell King, Ira W. Snyder, Srinidhi Kasagar,
linux-arm-msm, linux-kernel, Zhang Wei, Bryan Huntsman, Al Viro,
Barry Song, Daniel Walker, Dan Williams, linuxppc-dev,
David Brown, linux-arm-kernel
In-Reply-To: <1332426167-25213-1-git-send-email-kumarrav@codeaurora.org>
On Thu, 2012-03-22 at 19:52 +0530, Ravi Kumar V wrote:
> Add new context parameter to DMA SG and Interleaveid mode for passing
Typo ^^^^^^^^^^^^
> per transfer specific private data, using this it enables the
> dma devices which needs to pass the parameters which changes per
> each transfer
>
> Signed-off-by: Ravi Kumar V <kumarrav@codeaurora.org>
> ---
> drivers/dma/fsldma.c | 2 +-
> drivers/dma/sirf-dma.c | 2 +-
> drivers/dma/ste_dma40.c | 2 +-
> drivers/misc/carma/carma-fpga-program.c | 2 +-
> drivers/misc/carma/carma-fpga.c | 7 +++----
> include/linux/dmaengine.h | 22 ++++++++++++++++++++--
> 6 files changed, 27 insertions(+), 10 deletions(-)
>
> diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c
> index b98070c..f9f77db 100644
> --- a/drivers/dma/fsldma.c
> +++ b/drivers/dma/fsldma.c
> @@ -645,7 +645,7 @@ fail:
> static struct dma_async_tx_descriptor *fsl_dma_prep_sg(struct dma_chan *dchan,
> struct scatterlist *dst_sg, unsigned int dst_nents,
> struct scatterlist *src_sg, unsigned int src_nents,
> - unsigned long flags)
> + unsigned long flags, void *context)
> {
> struct fsl_desc_sw *first = NULL, *prev = NULL, *new = NULL;
> struct fsldma_chan *chan = to_fsl_chan(dchan);
> diff --git a/drivers/dma/sirf-dma.c b/drivers/dma/sirf-dma.c
> index 2333810..ff4d344 100644
> --- a/drivers/dma/sirf-dma.c
> +++ b/drivers/dma/sirf-dma.c
> @@ -428,7 +428,7 @@ sirfsoc_dma_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
>
> static struct dma_async_tx_descriptor *sirfsoc_dma_prep_interleaved(
> struct dma_chan *chan, struct dma_interleaved_template *xt,
> - unsigned long flags)
> + unsigned long flags, void *context)
> {
> struct sirfsoc_dma *sdma = dma_chan_to_sirfsoc_dma(chan);
> struct sirfsoc_dma_chan *schan = dma_chan_to_sirfsoc_dma_chan(chan);
> diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c
> index cc5ecbc..2f58ba9 100644
> --- a/drivers/dma/ste_dma40.c
> +++ b/drivers/dma/ste_dma40.c
> @@ -2287,7 +2287,7 @@ static struct dma_async_tx_descriptor *
> d40_prep_memcpy_sg(struct dma_chan *chan,
> struct scatterlist *dst_sg, unsigned int dst_nents,
> struct scatterlist *src_sg, unsigned int src_nents,
> - unsigned long dma_flags)
> + unsigned long dma_flags, void *context)
> {
> if (dst_nents != src_nents)
> return NULL;
> diff --git a/drivers/misc/carma/carma-fpga-program.c b/drivers/misc/carma/carma-fpga-program.c
> index a2d25e4..3739a12 100644
> --- a/drivers/misc/carma/carma-fpga-program.c
> +++ b/drivers/misc/carma/carma-fpga-program.c
> @@ -530,7 +530,7 @@ static noinline int fpga_program_dma(struct fpga_dev *priv)
> }
>
> /* setup and submit the DMA transaction */
> - tx = chan->device->device_prep_dma_sg(chan,
> + tx = chan->device->dmaengine_prep_dma_sg(chan,
> table.sgl, num_pages,
> vb->sglist, vb->sglen, 0);
No the idea is that clients will not know anything about additionlay
parameter hence avoiding abuse. You need to reread the patches sent by
Alexandre.
You need to
1) add wrappers over interleaved api which dont expose this additional
parameter
2) move existing users to use these wrappers
3) add a new API which has your additional argument (not an opaque
object) and this calls .device_xx callback with additional arg.
4. Above can be under conditional of your specific subsystem where these
parameters are valid.
> if (!tx) {
> diff --git a/drivers/misc/carma/carma-fpga.c b/drivers/misc/carma/carma-fpga.c
> index 14e974b2..be0baf6 100644
> --- a/drivers/misc/carma/carma-fpga.c
> +++ b/drivers/misc/carma/carma-fpga.c
> @@ -638,10 +638,9 @@ static int data_submit_dma(struct fpga_device *priv, struct data_buf *buf)
> */
>
> /* setup the scatterlist to scatterlist transfer */
> - tx = chan->device->device_prep_dma_sg(chan,
> - dst_sg, dst_nents,
> - src_sg, src_nents,
> - 0);
> + tx = dmaengine_prep_dma_sg(chan, dst_sg, dst_nents,
> + src_sg, src_nents,
> + 0);
> if (!tx) {
> dev_err(priv->dev, "unable to prep scatterlist DMA\n");
> return -ENOMEM;
> diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h
> index 679b349..68a57da 100644
> --- a/include/linux/dmaengine.h
> +++ b/include/linux/dmaengine.h
> @@ -570,7 +570,7 @@ struct dma_device {
> struct dma_chan *chan,
> struct scatterlist *dst_sg, unsigned int dst_nents,
> struct scatterlist *src_sg, unsigned int src_nents,
> - unsigned long flags);
> + unsigned long flags, void *context);
>
> struct dma_async_tx_descriptor *(*device_prep_slave_sg)(
> struct dma_chan *chan, struct scatterlist *sgl,
> @@ -581,7 +581,7 @@ struct dma_device {
> size_t period_len, enum dma_transfer_direction direction);
> struct dma_async_tx_descriptor *(*device_prep_interleaved_dma)(
> struct dma_chan *chan, struct dma_interleaved_template *xt,
> - unsigned long flags);
> + unsigned long flags, void *context);
> int (*device_control)(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
> unsigned long arg);
>
> @@ -615,6 +615,24 @@ static inline struct dma_async_tx_descriptor *dmaengine_prep_slave_single(
> return chan->device->device_prep_slave_sg(chan, &sg, 1, dir, flags);
> }
>
> +static inline struct dma_async_tx_descriptor *dmaengine_prep_dma_sg(
> + struct dma_chan *chan, struct scatterlist *dst_sg,
> + unsigned int dst_nents, struct scatterlist *src_sg,
> + unsigned int src_nents, unsigned long flags)
> +{
> + return chan->device->device_prep_dma_sg(chan, dst_sg, dst_nents,
> + src_sg, src_nents, flags, NULL);
> +}
> +
> +static inline struct dma_async_tx_descriptor *dmaengine_prep_interleaved_dma(
> + struct dma_chan *chan,
> + struct dma_interleaved_template *xt,
> + unsigned long flags)
> +{
> + return chan->device->device_prep_interleaved_dma(chan, xt,
> + flags, NULL);
> +}
> +
> static inline int dmaengine_terminate_all(struct dma_chan *chan)
> {
> return dmaengine_device_control(chan, DMA_TERMINATE_ALL, 0);
--
~Vinod
^ permalink raw reply
* Re: [PATCH v5 1/2] dmaengine: Add context parameter to prep_dma_sg and prep_interleaved_dma
From: Trilok Soni @ 2012-03-26 6:55 UTC (permalink / raw)
To: Vinod Koul
Cc: Russell King, Ravi Kumar V, Ira W. Snyder, Srinidhi Kasagar,
linux-arm-msm, linux-kernel, Zhang Wei, Bryan Huntsman, Al Viro,
Barry Song, Daniel Walker, Dan Williams, linuxppc-dev,
David Brown, linux-arm-kernel
In-Reply-To: <1332743073.19804.26.camel@vkoul-udesk3>
On 3/26/2012 11:54 AM, Vinod Koul wrote:
> On Thu, 2012-03-22 at 19:52 +0530, Ravi Kumar V wrote:
>> Add new context parameter to DMA SG and Interleaveid mode for passing
> Typo ^^^^^^^^^^^^
Sorry, we will fix this.
>> diff --git a/drivers/misc/carma/carma-fpga-program.c b/drivers/misc/carma/carma-fpga-program.c
>> index a2d25e4..3739a12 100644
>> --- a/drivers/misc/carma/carma-fpga-program.c
>> +++ b/drivers/misc/carma/carma-fpga-program.c
>> @@ -530,7 +530,7 @@ static noinline int fpga_program_dma(struct fpga_dev *priv)
>> }
>>
>> /* setup and submit the DMA transaction */
>> - tx = chan->device->device_prep_dma_sg(chan,
>> + tx = chan->device->dmaengine_prep_dma_sg(chan,
>> table.sgl, num_pages,
>> vb->sglist, vb->sglen, 0);
> No the idea is that clients will not know anything about additionlay
> parameter hence avoiding abuse. You need to reread the patches sent by
> Alexandre.
>
> You need to
> 1) add wrappers over interleaved api which dont expose this additional
> parameter
> 2) move existing users to use these wrappers
Above two steps would be exactly same as
https://lkml.org/lkml/2012/3/8/401 - [PATCH 1/2 V2] dmaengine/dma_slave:
introduce inline wrappers, right?
> 3) add a new API which has your additional argument (not an opaque
> object) and this calls .device_xx callback with additional arg.
> 4. Above can be under conditional of your specific subsystem where these
> parameters are valid.
Now, this would be different from what Alexandre had submitted, since he
had added "void *context" parameter directly to existing callbacks
dma_slave_sg and and cyclic under struct dma_device.
And you prefer that we add new callbacks under "struct dma_device" for
our specific requirement with new name and with that extra non-opaque
object. After that add wrappers for these two new callbacks say
dmaengine_prep_dma_sg_ext and dmagengine_prep_interleaved_dma_ext (same
name goes for callback).
---Trilok Soni
--
--
Sent by a consultant of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
^ permalink raw reply
* Re: [PATCH v5 1/2] dmaengine: Add context parameter to prep_dma_sg and prep_interleaved_dma
From: Vinod Koul @ 2012-03-26 7:24 UTC (permalink / raw)
To: Trilok Soni
Cc: Russell King, Ravi Kumar V, Ira W. Snyder, Srinidhi Kasagar,
linux-arm-msm, linux-kernel, Zhang Wei, Bryan Huntsman, Al Viro,
Barry Song, Daniel Walker, Dan Williams, linuxppc-dev,
David Brown, linux-arm-kernel
In-Reply-To: <4F7012FC.10203@codeaurora.org>
On Mon, 2012-03-26 at 12:25 +0530, Trilok Soni wrote:
> >
> > You need to
> > 1) add wrappers over interleaved api which dont expose this
> additional
> > parameter
> > 2) move existing users to use these wrappers
>
> Above two steps would be exactly same as
> https://lkml.org/lkml/2012/3/8/401 - [PATCH 1/2 V2]
> dmaengine/dma_slave:
> introduce inline wrappers, right?
>
> > 3) add a new API which has your additional argument (not an opaque
> > object) and this calls .device_xx callback with additional arg.
> > 4. Above can be under conditional of your specific subsystem where
> these
> > parameters are valid.
>
> Now, this would be different from what Alexandre had submitted, since
> he
> had added "void *context" parameter directly to existing callbacks
> dma_slave_sg and and cyclic under struct dma_device.
In that case the wrappers existed, so he modified them and ensured all
clients use these and not the .device callbacks
So you need to create the new wrappers without any context parameter and
ensure all clients use these.
>
> And you prefer that we add new callbacks under "struct dma_device"
> for
> our specific requirement with new name and with that extra non-opaque
> object. After that add wrappers for these two new callbacks say
> dmaengine_prep_dma_sg_ext and dmagengine_prep_interleaved_dma_ext
> (same
> name goes for callback).
>
>
--
~Vinod
^ permalink raw reply
* Re: [PATCH v2.1 01/10] cpu: Introduce clear_tasks_mm_cpumask() helper
From: Peter Zijlstra @ 2012-03-26 7:59 UTC (permalink / raw)
To: Oleg Nesterov
Cc: Mike Frysinger, user-mode-linux-devel, linux-sh,
Richard Weinberger, linux-kernel, uclinux-dist-devel, linux-mm,
Anton Vorontsov, Paul Mundt, John Stultz, KOSAKI Motohiro,
Russell King, Andrew Morton, linuxppc-dev, linux-arm-kernel
In-Reply-To: <20120325174210.GA23605@redhat.com>
On Sun, 2012-03-25 at 19:42 +0200, Oleg Nesterov wrote:
> __cpu_disable() is called by __stop_machine(), we know that nobody
> can preempt us and other CPUs can do nothing.=20
It would be very good to not rely on that though, I would love to get
rid of the stop_machine usage in cpu hotplug some day.
^ permalink raw reply
* Re: kilauea compilation breaks with v3.3 kernel and ELDK 4.2
From: Josh Boyer @ 2012-03-26 13:36 UTC (permalink / raw)
To: Benjamin Herrenschmidt; +Cc: linuxppc-dev, Wolfgang Denk, robert.karl.berger
In-Reply-To: <1332633218.2882.16.camel@pasglop>
On Sat, Mar 24, 2012 at 7:53 PM, Benjamin Herrenschmidt
<benh@kernel.crashing.org> wrote:
> On Wed, 2012-03-21 at 17:25 +0100, Wolfgang Denk wrote:
>> > > The problem is that for ppc-linux-gcc (GCC) 4.2.2 (which comes
>> with the
>> > > ELDK 4.2) this assembly instruction is not known and the build
>> breaks.
>> >
>> > Sigh. =A0GCC 4.2 is pretty old at this point.
>>
>> But still rock-solid ... =A0We use it a lot, especially as reference for
>> more recent (and sometimes more broken) versions of GCC.
>
> Isn't this a binutils rather than gcc issue ?
Pretty much.
josh
^ permalink raw reply
* [PATCH] powerpc: Support lower minimum entitlement for virtual processors
From: Robert Jennings @ 2012-03-23 21:22 UTC (permalink / raw)
To: Benjamin Herrenschmidt; +Cc: Robert Jennings, linuxppc-dev
This patch changes the architecture vector to advertise support for a
lower minimum virtual processor entitled capacity. The default
minimum without this patch is 10%, this patch specifies 5%. This will
allow 20 LPARs per CPU rather than only 10.
Signed-off-by: Robert Jennings <rcj@linux.vnet.ibm.com>
---
arch/powerpc/kernel/prom_init.c | 8 ++++++--
1 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/arch/powerpc/kernel/prom_init.c b/arch/powerpc/kernel/prom_init.c
index eca626e..3d882ea 100644
--- a/arch/powerpc/kernel/prom_init.c
+++ b/arch/powerpc/kernel/prom_init.c
@@ -689,6 +689,9 @@ static void __init early_cmdline_parse(void)
#define OV3_VMX 0x40 /* VMX/Altivec */
#define OV3_DFP 0x20 /* decimal FP */
+/* Option vector 4: IBM PAPR implementation */
+#define OV4_MIN_ENT_CAP 0x05 /* minimum VP entitled capacity */
+
/* Option vector 5: PAPR/OF options supported */
#define OV5_LPAR 0x80 /* logical partitioning supported */
#define OV5_SPLPAR 0x40 /* shared-processor LPAR supported */
@@ -753,8 +756,9 @@ static unsigned char ibm_architecture_vec[] = {
OV3_FP | OV3_VMX | OV3_DFP,
/* option vector 4: IBM PAPR implementation */
- 2 - 2, /* length */
+ 3 - 2, /* length */
0, /* don't halt */
+ OV4_MIN_ENT_CAP, /* minimum VP entitled capacity */
/* option vector 5: PAPR/OF options */
13 - 2, /* length */
@@ -771,7 +775,7 @@ static unsigned char ibm_architecture_vec[] = {
* must match by the macro below. Update the definition if
* the structure layout changes.
*/
-#define IBM_ARCH_VEC_NRCORES_OFFSET 100
+#define IBM_ARCH_VEC_NRCORES_OFFSET 101
W(NR_CPUS), /* number of cores supported */
/* option vector 6: IBM PAPR hints */
--
1.7.0.4
^ permalink raw reply related
* Re: [PATCH 00/17] Platform Facilities Option and crypto accelerator driver
From: Kent Yoder @ 2012-03-26 16:10 UTC (permalink / raw)
To: Kumar Gala; +Cc: rcj, linuxppc-dev, linux-kernel, linux-crypto
In-Reply-To: <06B7F1E7-951D-4753-BE00-7B0C84BACDAE@kernel.crashing.org>
>
> From what I can tell this isn't ISA level instructions and thus should NOT be in arch/powerpc. This should be moved into drivers/crypto
That makes sense. I'll move them over in my next submission.
Kent
> - k
^ permalink raw reply
* Re: [PATCH v2.1 01/10] cpu: Introduce clear_tasks_mm_cpumask() helper
From: Oleg Nesterov @ 2012-03-26 17:04 UTC (permalink / raw)
To: Peter Zijlstra
Cc: Mike Frysinger, user-mode-linux-devel, linux-sh,
Richard Weinberger, linux-kernel, uclinux-dist-devel, linux-mm,
Anton Vorontsov, Paul Mundt, John Stultz, KOSAKI Motohiro,
Russell King, Andrew Morton, linuxppc-dev, linux-arm-kernel
In-Reply-To: <1332748746.16159.62.camel@twins>
On 03/26, Peter Zijlstra wrote:
>
> On Sun, 2012-03-25 at 19:42 +0200, Oleg Nesterov wrote:
> > __cpu_disable() is called by __stop_machine(), we know that nobody
> > can preempt us and other CPUs can do nothing.
>
> It would be very good to not rely on that though,
Yes, yes, perhaps I wasn't clear but I think the patches are fine.
> I would love to get
> rid of the stop_machine usage in cpu hotplug some day.
Interesting... Why? I mean, why do you dislike stop_machine() in
_cpu_down() ? Just curious.
Oleg.
^ permalink raw reply
* Re: [PATCH v2.1 01/10] cpu: Introduce clear_tasks_mm_cpumask() helper
From: Peter Zijlstra @ 2012-03-26 17:23 UTC (permalink / raw)
To: Oleg Nesterov
Cc: Mike Frysinger, user-mode-linux-devel, linux-sh,
Richard Weinberger, linux-kernel, uclinux-dist-devel, linux-mm,
Anton Vorontsov, Paul Mundt, John Stultz, KOSAKI Motohiro,
Russell King, Andrew Morton, linuxppc-dev, linux-arm-kernel
In-Reply-To: <20120326170443.GA25229@redhat.com>
On Mon, 2012-03-26 at 19:04 +0200, Oleg Nesterov wrote:
> Interesting... Why? I mean, why do you dislike stop_machine() in
> _cpu_down() ? Just curious.
It disturbs all cpus, the -rt people don't like that their FIFO tasks
don't get to run, the trading people don't like their RDMA poll loops to
be interrupted.. etc.
Now arguably, one should simply not do hotplug crap while such things
are running, and mostly that's a perfectly fine constraint. But it
doesn't help that people view cpu hotplug as a power savings or resource
provisioning 'feature' and there's userspace daemons that plug
on-demand.
But my ultimate goal is to completely remove synchronization that is
actively machine wide, since we all know that as long as such stuff
exists people will want to use it.
Now I don't know we'll ever fully get there -- see the BKL saga -- but
its worth trying I think. The module unload and esp. the text_poke usage
of stop_machine are much worse offenders, since both those are
relatively common and much harder to avoid.
^ permalink raw reply
* [PATCH] powerpc: 512x: Fix mpc5121_clk_get()
From: Richard Weinberger @ 2012-03-26 19:01 UTC (permalink / raw)
To: linuxppc-dev; +Cc: agust, paulus, linux-kernel, Richard Weinberger
If try_module_get() fails, mpc5121_clk_get() might return
a wrong clock.
Signed-off-by: Richard Weinberger <richard@nod.at>
---
arch/powerpc/platforms/512x/clock.c | 6 ++++--
1 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/arch/powerpc/platforms/512x/clock.c b/arch/powerpc/platforms/512x/clock.c
index 1d8700f..9f771e0 100644
--- a/arch/powerpc/platforms/512x/clock.c
+++ b/arch/powerpc/platforms/512x/clock.c
@@ -54,14 +54,16 @@ static DEFINE_MUTEX(clocks_mutex);
static struct clk *mpc5121_clk_get(struct device *dev, const char *id)
{
struct clk *p, *clk = ERR_PTR(-ENOENT);
- int dev_match = 0;
- int id_match = 0;
+ int dev_match;
+ int id_match;
if (dev == NULL || id == NULL)
return clk;
mutex_lock(&clocks_mutex);
list_for_each_entry(p, &clocks, node) {
+ dev_match = id_match = 0;
+
if (dev == p->dev)
dev_match++;
if (strcmp(id, p->name) == 0)
--
1.7.7.3
^ permalink raw reply related
* [PATCH V2 1/2] rapidio: add DMA engine support for RIO data transfers
From: Alexandre Bounine @ 2012-03-26 20:45 UTC (permalink / raw)
To: akpm, vinod.koul, dan.j.williams, linux-kernel, linuxppc-dev
Cc: Alexandre Bounine, paul.gortmaker
Adds DMA Engine framework support into RapidIO subsystem.
Uses DMA Engine DMA_SLAVE interface to generate data transfers to/from
remote RapidIO target devices.
Introduces RapidIO-specific wrapper for prep_slave_sg() interface
with an extra parameter to pass target specific information.
Uses scatterlist to describe local data buffer. Address flat data buffer
on a remote side.
Signed-off-by: Alexandre Bounine <alexandre.bounine@idt.com>
---
This patch is applicable to linux-next-20120326 and after.
v2: Uses updated DMA engine prep_slave_sg() interface.
See https://lkml.org/lkml/2012/3/8/373 for more details.
drivers/rapidio/Kconfig | 14 ++++++++
drivers/rapidio/rio.c | 81 +++++++++++++++++++++++++++++++++++++++++++++
include/linux/dmaengine.h | 12 +++++++
include/linux/rio.h | 47 ++++++++++++++++++++++++++
include/linux/rio_drv.h | 9 +++++
5 files changed, 163 insertions(+), 0 deletions(-)
diff --git a/drivers/rapidio/Kconfig b/drivers/rapidio/Kconfig
index bc87192..6194d35 100644
--- a/drivers/rapidio/Kconfig
+++ b/drivers/rapidio/Kconfig
@@ -22,6 +22,20 @@ config RAPIDIO_ENABLE_RX_TX_PORTS
ports for Input/Output direction to allow other traffic
than Maintenance transfers.
+config RAPIDIO_DMA_ENGINE
+ bool "DMA Engine support for RapidIO"
+ depends on RAPIDIO
+ select DMADEVICES
+ select DMA_ENGINE
+ help
+ Say Y here if you want to use DMA Engine frameork for RapidIO data
+ transfers to/from target RIO devices. RapidIO uses NREAD and
+ NWRITE (NWRITE_R, SWRITE) requests to transfer data between local
+ memory and memory on remote target device. You need a DMA controller
+ capable to perform data transfers to/from RapidIO.
+
+ If you are unsure about this, say Y here.
+
config RAPIDIO_DEBUG
bool "RapidIO subsystem debug messages"
depends on RAPIDIO
diff --git a/drivers/rapidio/rio.c b/drivers/rapidio/rio.c
index 86c9a09..c40665a 100644
--- a/drivers/rapidio/rio.c
+++ b/drivers/rapidio/rio.c
@@ -1121,6 +1121,87 @@ int rio_std_route_clr_table(struct rio_mport *mport, u16 destid, u8 hopcount,
return 0;
}
+#ifdef CONFIG_RAPIDIO_DMA_ENGINE
+
+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_SLAVE, 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_transfer_direction direction, unsigned long flags)
+{
+ struct dma_async_tx_descriptor *txd = NULL;
+ struct rio_dma_ext rio_ext;
+
+ if (dchan->device->device_prep_slave_sg == NULL) {
+ pr_err("%s: prep_rio_sg == NULL\n", __func__);
+ return NULL;
+ }
+
+ 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;
+
+ txd = dmaengine_prep_rio_sg(dchan, data->sg, data->sg_len,
+ direction, flags, &rio_ext);
+
+ 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 676f967..3596a3a 100644
--- a/include/linux/dmaengine.h
+++ b/include/linux/dmaengine.h
@@ -633,6 +633,18 @@ static inline struct dma_async_tx_descriptor *dmaengine_prep_slave_sg(
dir, flags, NULL);
}
+#ifdef CONFIG_RAPIDIO_DMA_ENGINE
+struct rio_dma_ext;
+static inline struct dma_async_tx_descriptor *dmaengine_prep_rio_sg(
+ struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len,
+ enum dma_transfer_direction dir, unsigned long flags,
+ struct rio_dma_ext *rio_ext)
+{
+ return chan->device->device_prep_slave_sg(chan, sgl, sg_len,
+ dir, flags, rio_ext);
+}
+#endif
+
static inline struct dma_async_tx_descriptor *dmaengine_prep_dma_cyclic(
struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len,
size_t period_len, enum dma_transfer_direction dir)
diff --git a/include/linux/rio.h b/include/linux/rio.h
index 4d50611..a90ebad 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, others - 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 7f07470..31ad146 100644
--- a/include/linux/rio_drv.h
+++ b/include/linux/rio_drv.h
@@ -377,6 +377,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_transfer_direction direction, unsigned long flags);
+#endif
+
/**
* rio_name - Get the unique RIO device identifier
* @rdev: RIO device
--
1.7.8.4
^ permalink raw reply related
* [PATCH V2 2/2] rapidio/tsi721: add DMA engine support
From: Alexandre Bounine @ 2012-03-26 20:45 UTC (permalink / raw)
To: akpm, vinod.koul, dan.j.williams, linux-kernel, linuxppc-dev
Cc: Alexandre Bounine, paul.gortmaker
In-Reply-To: <1332794725-1928-1-git-send-email-alexandre.bounine@idt.com>
Adds support for DMA Engine API into Tsi721 mport driver.
Includes following changes for Tsi721 driver:
- 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>
---
This patch is applicable to linux-next-20120326 and after.
v2: Address review comments,
Switched to use dma_zalloc_coherent,
Switched to use dma_transfer_direction enumerator,
Uses updated prep_slave_sg() interface.
drivers/rapidio/devices/Makefile | 3 +
drivers/rapidio/devices/tsi721.c | 211 ++++++----
drivers/rapidio/devices/tsi721.h | 105 ++++-
drivers/rapidio/devices/tsi721_dma.c | 823 ++++++++++++++++++++++++++++++++++
4 files changed, 1050 insertions(+), 92 deletions(-)
create mode 100644 drivers/rapidio/devices/tsi721_dma.c
diff --git a/drivers/rapidio/devices/Makefile b/drivers/rapidio/devices/Makefile
index 3b7b4e2..7b62860 100644
--- a/drivers/rapidio/devices/Makefile
+++ b/drivers/rapidio/devices/Makefile
@@ -3,3 +3,6 @@
#
obj-$(CONFIG_RAPIDIO_TSI721) += tsi721.o
+ifeq ($(CONFIG_RAPIDIO_DMA_ENGINE),y)
+obj-$(CONFIG_RAPIDIO_TSI721) += tsi721_dma.o
+endif
diff --git a/drivers/rapidio/devices/tsi721.c b/drivers/rapidio/devices/tsi721.c
index 30d2072..722246c 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;
@@ -541,6 +537,22 @@ static irqreturn_t tsi721_irqhandler(int irq, void *ptr)
tsi721_pw_handler(mport);
}
+#ifdef CONFIG_RAPIDIO_DMA_ENGINE
+ 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;
}
@@ -553,18 +565,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_RAPIDIO_DMA_ENGINE
+ 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);
@@ -715,12 +735,29 @@ static int tsi721_enable_msix(struct tsi721_device *priv)
TSI721_MSIX_OMSG_INT(i);
}
+#ifdef CONFIG_RAPIDIO_DMA_ENGINE
+ /*
+ * 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_RAPIDIO_DMA_ENGINE */
+
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;
}
@@ -760,6 +797,22 @@ static int tsi721_enable_msix(struct tsi721_device *priv)
i, pci_name(priv->pdev));
}
+#ifdef CONFIG_RAPIDIO_DMA_ENGINE
+ 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_RAPIDIO_DMA_ENGINE */
+
return 0;
}
#endif /* CONFIG_PCI_MSI */
@@ -888,20 +941,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_zalloc_coherent(&priv->pdev->dev,
bd_num * sizeof(struct tsi721_dma_desc),
@@ -909,8 +976,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;
dev_dbg(&priv->pdev->dev, "DMA descriptors @ %p (phys = %llx)\n",
bd_ptr, (unsigned long long)bd_phys);
@@ -927,13 +995,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;
dev_dbg(&priv->pdev->dev,
"desc status FIFO @ %p (phys = %llx) size=0x%x\n",
@@ -946,83 +1014,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,
@@ -2035,7 +2081,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);
@@ -2104,6 +2151,7 @@ static int __devinit tsi721_setup_mport(struct tsi721_device *priv)
mport->phy_type = RIO_PHY_SERIAL;
mport->priv = (void *)priv;
mport->phys_efptr = 0x100;
+ priv->mport = mport;
INIT_LIST_HEAD(&mport->dbells);
@@ -2129,17 +2177,21 @@ static int __devinit tsi721_setup_mport(struct tsi721_device *priv)
if (!err) {
tsi721_interrupts_init(priv);
ops->pwenable = tsi721_pw_enable;
- } else
+ } else {
dev_err(&pdev->dev, "Unable to get assigned PCI IRQ "
"vector %02X err=0x%x\n", pdev->irq, err);
+ goto err_exit;
+ }
+#ifdef CONFIG_RAPIDIO_DMA_ENGINE
+ tsi721_register_dma(priv);
+#endif
/* Enable SRIO link */
iowrite32(ioread32(priv->regs + TSI721_DEVCTL) |
TSI721_DEVCTL_SRBOOT_CMPL,
priv->regs + TSI721_DEVCTL);
rio_register_mport(mport);
- priv->mport = mport;
if (mport->host_deviceid >= 0)
iowrite32(RIO_PORT_GEN_HOST | RIO_PORT_GEN_MASTER |
@@ -2149,6 +2201,11 @@ static int __devinit tsi721_setup_mport(struct tsi721_device *priv)
iowrite32(0, priv->regs + (0x100 + RIO_PORT_GEN_CTL_CSR));
return 0;
+
+err_exit:
+ kfree(mport);
+ kfree(ops);
+ return err;
}
static int __devinit tsi721_probe(struct pci_dev *pdev,
@@ -2294,7 +2351,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;
@@ -2319,7 +2376,7 @@ static int __devinit tsi721_probe(struct pci_dev *pdev,
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 1c226b3..59de9d7 100644
--- a/drivers/rapidio/devices/tsi721.h
+++ b/drivers/rapidio/devices/tsi721.h
@@ -167,6 +167,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
@@ -181,6 +183,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
@@ -235,14 +239,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_CTL(x) (0x51008 + (x) * 0x1000)
+#define TSI721_DMAC_DWRCNT 0x000
+#define TSI721_DMAC_DRDCNT 0x004
+
+#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
@@ -250,34 +256,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
@@ -612,6 +617,8 @@ enum dma_rtype {
#define TSI721_DMACH_MAINT 0 /* DMA channel for maint requests */
#define TSI721_DMACH_MAINT_NBD 32 /* Number of BDs for maint requests */
+#define TSI721_DMACH_DMA 1 /* DMA channel for data transfers */
+
#define MSG_DMA_ENTRY_INX_TO_SIZE(x) ((0x10 << (x)) & 0xFFFF0)
enum tsi721_smsg_int_flag {
@@ -626,7 +633,48 @@ enum tsi721_smsg_int_flag {
/* Structures */
+#ifdef CONFIG_RAPIDIO_DMA_ENGINE
+
+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_RAPIDIO_DMA_ENGINE */
+
+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;
@@ -721,6 +769,24 @@ enum tsi721_msix_vect {
TSI721_VECT_IMB1_INT,
TSI721_VECT_IMB2_INT,
TSI721_VECT_IMB3_INT,
+#ifdef CONFIG_RAPIDIO_DMA_ENGINE
+ 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_RAPIDIO_DMA_ENGINE */
TSI721_VECT_MAX
};
@@ -754,7 +820,11 @@ struct tsi721_device {
u32 pw_discard_count;
/* BDMA Engine */
+ struct tsi721_bdma_maint mdma; /* Maintenance rd/wr request channel */
+
+#ifdef CONFIG_RAPIDIO_DMA_ENGINE
struct tsi721_bdma_chan bdma[TSI721_DMA_CHNUM];
+#endif
/* Inbound Messaging */
int imsg_init[TSI721_IMSG_CHNUM];
@@ -765,4 +835,9 @@ struct tsi721_device {
struct tsi721_omsg_ring omsg_ring[TSI721_OMSG_CHNUM];
};
+#ifdef CONFIG_RAPIDIO_DMA_ENGINE
+extern void tsi721_bdma_handler(struct tsi721_bdma_chan *bdma_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..92e06a5
--- /dev/null
+++ b/drivers/rapidio/devices/tsi721_dma.c
@@ -0,0 +1,823 @@
+/*
+ * 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 *bdma_chan)
+{
+ return list_first_entry(&bdma_chan->active_list,
+ struct tsi721_tx_desc, desc_node);
+}
+
+static int tsi721_bdma_ch_init(struct tsi721_bdma_chan *bdma_chan)
+{
+ struct tsi721_dma_desc *bd_ptr;
+ struct device *dev = bdma_chan->dchan.device->dev;
+ u64 *sts_ptr;
+ dma_addr_t bd_phys;
+ dma_addr_t sts_phys;
+ int sts_size;
+ int bd_num = bdma_chan->bd_num;
+
+ dev_dbg(dev, "Init Block DMA Engine, CH%d\n", bdma_chan->id);
+
+ /* Allocate space for DMA descriptors */
+ bd_ptr = dma_zalloc_coherent(dev,
+ bd_num * sizeof(struct tsi721_dma_desc),
+ &bd_phys, GFP_KERNEL);
+ if (!bd_ptr)
+ return -ENOMEM;
+
+ bdma_chan->bd_phys = bd_phys;
+ bdma_chan->bd_base = bd_ptr;
+
+ 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_zalloc_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);
+ bdma_chan->bd_base = NULL;
+ return -ENOMEM;
+ }
+
+ bdma_chan->sts_phys = sts_phys;
+ bdma_chan->sts_base = sts_ptr;
+ bdma_chan->sts_size = 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),
+ bdma_chan->regs + TSI721_DMAC_DPTRH);
+ iowrite32(((u64)bd_phys & TSI721_DMAC_DPTRL_MASK),
+ bdma_chan->regs + TSI721_DMAC_DPTRL);
+
+ /* Setup descriptor status FIFO */
+ iowrite32(((u64)sts_phys >> 32),
+ bdma_chan->regs + TSI721_DMAC_DSBH);
+ iowrite32(((u64)sts_phys & TSI721_DMAC_DSBL_MASK),
+ bdma_chan->regs + TSI721_DMAC_DSBL);
+ iowrite32(TSI721_DMAC_DSSZ_SIZE(sts_size),
+ bdma_chan->regs + TSI721_DMAC_DSSZ);
+
+ /* Clear interrupt bits */
+ iowrite32(TSI721_DMAC_INT_ALL,
+ bdma_chan->regs + TSI721_DMAC_INT);
+
+ ioread32(bdma_chan->regs + TSI721_DMAC_INT);
+
+ /* Toggle DMA channel initialization */
+ iowrite32(TSI721_DMAC_CTL_INIT, bdma_chan->regs + TSI721_DMAC_CTL);
+ ioread32(bdma_chan->regs + TSI721_DMAC_CTL);
+ bdma_chan->wr_count = bdma_chan->wr_count_next = 0;
+ bdma_chan->sts_rdptr = 0;
+ udelay(10);
+
+ return 0;
+}
+
+static int tsi721_bdma_ch_free(struct tsi721_bdma_chan *bdma_chan)
+{
+ u32 ch_stat;
+
+ if (bdma_chan->bd_base == NULL)
+ return 0;
+
+ /* Check if DMA channel still running */
+ ch_stat = ioread32(bdma_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, bdma_chan->regs + TSI721_DMAC_CTL);
+
+ /* Free space allocated for DMA descriptors */
+ dma_free_coherent(bdma_chan->dchan.device->dev,
+ bdma_chan->bd_num * sizeof(struct tsi721_dma_desc),
+ bdma_chan->bd_base, bdma_chan->bd_phys);
+ bdma_chan->bd_base = NULL;
+
+ /* Free space allocated for status FIFO */
+ dma_free_coherent(bdma_chan->dchan.device->dev,
+ bdma_chan->sts_size * sizeof(struct tsi721_dma_sts),
+ bdma_chan->sts_base, bdma_chan->sts_phys);
+ bdma_chan->sts_base = NULL;
+ return 0;
+}
+
+static void
+tsi721_bdma_interrupt_enable(struct tsi721_bdma_chan *bdma_chan, int enable)
+{
+ if (enable) {
+ /* Clear pending BDMA channel interrupts */
+ iowrite32(TSI721_DMAC_INT_ALL,
+ bdma_chan->regs + TSI721_DMAC_INT);
+ ioread32(bdma_chan->regs + TSI721_DMAC_INT);
+ /* Enable BDMA channel interrupts */
+ iowrite32(TSI721_DMAC_INT_ALL,
+ bdma_chan->regs + TSI721_DMAC_INTE);
+ } else {
+ /* Disable BDMA channel interrupts */
+ iowrite32(0, bdma_chan->regs + TSI721_DMAC_INTE);
+ /* Clear pending BDMA channel interrupts */
+ iowrite32(TSI721_DMAC_INT_ALL,
+ bdma_chan->regs + TSI721_DMAC_INT);
+ }
+
+}
+
+static bool tsi721_dma_is_idle(struct tsi721_bdma_chan *bdma_chan)
+{
+ u32 sts;
+
+ sts = ioread32(bdma_chan->regs + TSI721_DMAC_STS);
+ return ((sts & TSI721_DMAC_STS_RUN) == 0);
+}
+
+void tsi721_bdma_handler(struct tsi721_bdma_chan *bdma_chan)
+{
+ /* Disable BDMA channel interrupts */
+ iowrite32(0, bdma_chan->regs + TSI721_DMAC_INTE);
+
+ tasklet_schedule(&bdma_chan->tasklet);
+}
+
+#ifdef CONFIG_PCI_MSI
+/**
+ * tsi721_omsg_msix - MSI-X interrupt handler for BDMA channels
+ * @irq: Linux interrupt number
+ * @ptr: Pointer to interrupt-specific data (BDMA channel structure)
+ *
+ * Handles BDMA channel interrupts signaled using MSI-X.
+ */
+static irqreturn_t tsi721_bdma_msix(int irq, void *ptr)
+{
+ struct tsi721_bdma_chan *bdma_chan = ptr;
+
+ tsi721_bdma_handler(bdma_chan);
+ return IRQ_HANDLED;
+}
+#endif /* CONFIG_PCI_MSI */
+
+/* Must be called with the spinlock held */
+static void tsi721_start_dma(struct tsi721_bdma_chan *bdma_chan)
+{
+ if (!tsi721_dma_is_idle(bdma_chan)) {
+ dev_err(bdma_chan->dchan.device->dev,
+ "BUG: Attempt to start non-idle channel\n");
+ return;
+ }
+
+ if (bdma_chan->wr_count == bdma_chan->wr_count_next) {
+ dev_err(bdma_chan->dchan.device->dev,
+ "BUG: Attempt to start DMA with no BDs ready\n");
+ return;
+ }
+
+ dev_dbg(bdma_chan->dchan.device->dev,
+ "tx_chan: %p, chan: %d, regs: %p\n",
+ bdma_chan, bdma_chan->dchan.chan_id, bdma_chan->regs);
+
+ iowrite32(bdma_chan->wr_count_next,
+ bdma_chan->regs + TSI721_DMAC_DWRCNT);
+ ioread32(bdma_chan->regs + TSI721_DMAC_DWRCNT);
+
+ bdma_chan->wr_count = bdma_chan->wr_count_next;
+}
+
+static void tsi721_desc_put(struct tsi721_bdma_chan *bdma_chan,
+ struct tsi721_tx_desc *desc)
+{
+ dev_dbg(bdma_chan->dchan.device->dev,
+ "Put desc: %p into free list\n", desc);
+
+ if (desc) {
+ spin_lock_bh(&bdma_chan->lock);
+ list_splice_init(&desc->tx_list, &bdma_chan->free_list);
+ list_add(&desc->desc_node, &bdma_chan->free_list);
+ bdma_chan->wr_count_next = bdma_chan->wr_count;
+ spin_unlock_bh(&bdma_chan->lock);
+ }
+}
+
+static
+struct tsi721_tx_desc *tsi721_desc_get(struct tsi721_bdma_chan *bdma_chan)
+{
+ struct tsi721_tx_desc *tx_desc, *_tx_desc;
+ struct tsi721_tx_desc *ret = NULL;
+ int i;
+
+ spin_lock_bh(&bdma_chan->lock);
+ list_for_each_entry_safe(tx_desc, _tx_desc,
+ &bdma_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(bdma_chan->dchan.device->dev,
+ "desc %p not ACKed\n", tx_desc);
+ }
+
+ i = bdma_chan->wr_count_next % bdma_chan->bd_num;
+ if (i == bdma_chan->bd_num - 1) {
+ i = 0;
+ bdma_chan->wr_count_next++; /* skip link descriptor */
+ }
+
+ bdma_chan->wr_count_next++;
+ tx_desc->txd.phys = bdma_chan->bd_phys +
+ i * sizeof(struct tsi721_dma_desc);
+ tx_desc->hw_desc = &((struct tsi721_dma_desc *)bdma_chan->bd_base)[i];
+
+ spin_unlock_bh(&bdma_chan->lock);
+
+ return ret;
+}
+
+static int
+tsi721_fill_desc(struct tsi721_bdma_chan *bdma_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(bdma_chan->dchan.device->dev,
+ "SG element is too large\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(bdma_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(bdma_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;
+
+ return 0;
+}
+
+static void tsi721_dma_chain_complete(struct tsi721_bdma_chan *bdma_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, &bdma_chan->free_list);
+ list_move(&desc->desc_node, &bdma_chan->free_list);
+ bdma_chan->completed_cookie = txd->cookie;
+
+ if (callback)
+ callback(param);
+}
+
+static void tsi721_dma_complete_all(struct tsi721_bdma_chan *bdma_chan)
+{
+ struct tsi721_tx_desc *desc, *_d;
+ LIST_HEAD(list);
+
+ BUG_ON(!tsi721_dma_is_idle(bdma_chan));
+
+ if (!list_empty(&bdma_chan->queue))
+ tsi721_start_dma(bdma_chan);
+
+ list_splice_init(&bdma_chan->active_list, &list);
+ list_splice_init(&bdma_chan->queue, &bdma_chan->active_list);
+
+ list_for_each_entry_safe(desc, _d, &list, desc_node)
+ tsi721_dma_chain_complete(bdma_chan, desc);
+}
+
+static void tsi721_clr_stat(struct tsi721_bdma_chan *bdma_chan)
+{
+ u32 srd_ptr;
+ u64 *sts_ptr;
+ int i, j;
+
+ /* Check and clear descriptor status FIFO entries */
+ srd_ptr = bdma_chan->sts_rdptr;
+ sts_ptr = bdma_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 %= bdma_chan->sts_size;
+ j = srd_ptr * 8;
+ }
+
+ iowrite32(srd_ptr, bdma_chan->regs + TSI721_DMAC_DSRP);
+ bdma_chan->sts_rdptr = srd_ptr;
+}
+
+static void tsi721_advance_work(struct tsi721_bdma_chan *bdma_chan)
+{
+ if (list_empty(&bdma_chan->active_list) ||
+ list_is_singular(&bdma_chan->active_list)) {
+ dev_dbg(bdma_chan->dchan.device->dev,
+ "%s: Active_list empty\n", __func__);
+ tsi721_dma_complete_all(bdma_chan);
+ } else {
+ dev_dbg(bdma_chan->dchan.device->dev,
+ "%s: Active_list NOT empty\n", __func__);
+ tsi721_dma_chain_complete(bdma_chan,
+ tsi721_dma_first_active(bdma_chan));
+ tsi721_start_dma(bdma_chan);
+ }
+}
+
+static void tsi721_dma_tasklet(unsigned long data)
+{
+ struct tsi721_bdma_chan *bdma_chan = (struct tsi721_bdma_chan *)data;
+ u32 dmac_int, dmac_sts;
+
+ dmac_int = ioread32(bdma_chan->regs + TSI721_DMAC_INT);
+ dev_dbg(bdma_chan->dchan.device->dev, "%s: DMAC%d_INT = 0x%x\n",
+ __func__, bdma_chan->id, dmac_int);
+ /* Clear channel interrupts */
+ iowrite32(dmac_int, bdma_chan->regs + TSI721_DMAC_INT);
+
+ if (dmac_int & TSI721_DMAC_INT_ERR) {
+ dmac_sts = ioread32(bdma_chan->regs + TSI721_DMAC_STS);
+ dev_err(bdma_chan->dchan.device->dev,
+ "%s: DMA ERROR - DMAC%d_STS = 0x%x\n",
+ __func__, bdma_chan->id, dmac_sts);
+ }
+
+ if (dmac_int & TSI721_DMAC_INT_STFULL) {
+ dev_err(bdma_chan->dchan.device->dev,
+ "%s: DMAC%d descriptor status FIFO is full\n",
+ __func__, bdma_chan->id);
+ }
+
+ if (dmac_int & (TSI721_DMAC_INT_DONE | TSI721_DMAC_INT_IOFDONE)) {
+ tsi721_clr_stat(bdma_chan);
+ spin_lock(&bdma_chan->lock);
+ tsi721_advance_work(bdma_chan);
+ spin_unlock(&bdma_chan->lock);
+ }
+
+ /* Re-Enable BDMA channel interrupts */
+ iowrite32(TSI721_DMAC_INT_ALL, bdma_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 *bdma_chan = to_tsi721_chan(txd->chan);
+ dma_cookie_t cookie;
+
+ spin_lock_bh(&bdma_chan->lock);
+
+ cookie = txd->chan->cookie;
+ if (++cookie < 0)
+ cookie = 1;
+ txd->chan->cookie = cookie;
+ txd->cookie = cookie;
+
+ if (list_empty(&bdma_chan->active_list)) {
+ list_add_tail(&desc->desc_node, &bdma_chan->active_list);
+ tsi721_start_dma(bdma_chan);
+ } else {
+ list_add_tail(&desc->desc_node, &bdma_chan->queue);
+ }
+
+ spin_unlock_bh(&bdma_chan->lock);
+ return cookie;
+}
+
+static int tsi721_alloc_chan_resources(struct dma_chan *dchan)
+{
+ struct tsi721_bdma_chan *bdma_chan = to_tsi721_chan(dchan);
+#ifdef CONFIG_PCI_MSI
+ struct tsi721_device *priv = to_tsi721(dchan->device);
+#endif
+ struct tsi721_tx_desc *desc = NULL;
+ LIST_HEAD(tmp_list);
+ int i;
+ int rc;
+
+ if (bdma_chan->bd_base)
+ return bdma_chan->bd_num - 1;
+
+ /* Initialize BDMA channel */
+ if (tsi721_bdma_ch_init(bdma_chan)) {
+ dev_err(dchan->device->dev, "Unable to initialize data DMA"
+ " channel %d, aborting\n", bdma_chan->id);
+ return -ENOMEM;
+ }
+
+ /* Alocate matching number of logical descriptors */
+ desc = kcalloc((bdma_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;
+ }
+
+ bdma_chan->tx_desc = desc;
+
+ for (i = 0; i < bdma_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(&bdma_chan->lock);
+ list_splice(&tmp_list, &bdma_chan->free_list);
+ bdma_chan->completed_cookie = dchan->cookie = 1;
+ spin_unlock_bh(&bdma_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 +
+ bdma_chan->id].vector,
+ tsi721_bdma_msix, 0,
+ priv->msix[TSI721_VECT_DMA0_DONE +
+ bdma_chan->id].irq_name,
+ (void *)bdma_chan);
+
+ if (rc) {
+ dev_dbg(dchan->device->dev,
+ "Unable to allocate MSI-X interrupt for "
+ "BDMA%d-DONE\n", bdma_chan->id);
+ goto err_out;
+ }
+
+ rc = request_irq(priv->msix[TSI721_VECT_DMA0_INT +
+ bdma_chan->id].vector,
+ tsi721_bdma_msix, 0,
+ priv->msix[TSI721_VECT_DMA0_INT +
+ bdma_chan->id].irq_name,
+ (void *)bdma_chan);
+
+ if (rc) {
+ dev_dbg(dchan->device->dev,
+ "Unable to allocate MSI-X interrupt for "
+ "BDMA%d-INT\n", bdma_chan->id);
+ free_irq(
+ priv->msix[TSI721_VECT_DMA0_DONE +
+ bdma_chan->id].vector,
+ (void *)bdma_chan);
+ rc = -EIO;
+ goto err_out;
+ }
+ }
+#endif /* CONFIG_PCI_MSI */
+
+ tasklet_enable(&bdma_chan->tasklet);
+ tsi721_bdma_interrupt_enable(bdma_chan, 1);
+
+ return bdma_chan->bd_num - 1;
+
+err_out:
+ kfree(desc);
+ tsi721_bdma_ch_free(bdma_chan);
+ return rc;
+}
+
+static void tsi721_free_chan_resources(struct dma_chan *dchan)
+{
+ struct tsi721_bdma_chan *bdma_chan = to_tsi721_chan(dchan);
+#ifdef CONFIG_PCI_MSI
+ struct tsi721_device *priv = to_tsi721(dchan->device);
+#endif
+ LIST_HEAD(list);
+
+ dev_dbg(dchan->device->dev, "%s: Entry\n", __func__);
+
+ if (bdma_chan->bd_base == NULL)
+ return;
+
+ BUG_ON(!list_empty(&bdma_chan->active_list));
+ BUG_ON(!list_empty(&bdma_chan->queue));
+
+ tasklet_disable(&bdma_chan->tasklet);
+
+ spin_lock_bh(&bdma_chan->lock);
+ list_splice_init(&bdma_chan->free_list, &list);
+ spin_unlock_bh(&bdma_chan->lock);
+
+ tsi721_bdma_interrupt_enable(bdma_chan, 0);
+
+#ifdef CONFIG_PCI_MSI
+ if (priv->flags & TSI721_USING_MSIX) {
+ free_irq(priv->msix[TSI721_VECT_DMA0_DONE +
+ bdma_chan->id].vector, (void *)bdma_chan);
+ free_irq(priv->msix[TSI721_VECT_DMA0_INT +
+ bdma_chan->id].vector, (void *)bdma_chan);
+ }
+#endif /* CONFIG_PCI_MSI */
+
+ tsi721_bdma_ch_free(bdma_chan);
+ kfree(bdma_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_bh(&bdma_chan->lock);
+ last_completed = bdma_chan->completed_cookie;
+ last_used = dchan->cookie;
+ spin_unlock_bh(&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 *bdma_chan = to_tsi721_chan(dchan);
+
+ dev_dbg(dchan->device->dev, "%s: Entry\n", __func__);
+
+ if (tsi721_dma_is_idle(bdma_chan)) {
+ spin_lock_bh(&bdma_chan->lock);
+ tsi721_advance_work(bdma_chan);
+ spin_unlock_bh(&bdma_chan->lock);
+ } else
+ dev_dbg(dchan->device->dev,
+ "%s: DMA channel still busy\n", __func__);
+}
+
+static
+struct dma_async_tx_descriptor *tsi721_prep_rio_sg(struct dma_chan *dchan,
+ struct scatterlist *sgl, unsigned int sg_len,
+ enum dma_transfer_direction dir, unsigned long flags,
+ void *tinfo)
+{
+ 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 = tinfo;
+ u64 rio_addr = rext->rio_addr; /* limited to 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 (dir == DMA_DEV_TO_MEM)
+ rtype = NREAD;
+ else if (dir == DMA_MEM_TO_DEV) {
+ 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 *bdma_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(&bdma_chan->lock);
+
+ /* make sure to stop the transfer */
+ iowrite32(TSI721_DMAC_CTL_SUSP, bdma_chan->regs + TSI721_DMAC_CTL);
+
+ list_splice_init(&bdma_chan->active_list, &list);
+ list_splice_init(&bdma_chan->queue, &list);
+
+ list_for_each_entry_safe(desc, _d, &list, desc_node)
+ tsi721_dma_chain_complete(bdma_chan, desc);
+
+ spin_unlock_bh(&bdma_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);
+ tasklet_disable(&bdma_chan->tasklet);
+ 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_SLAVE, 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_rio_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.8.4
^ permalink raw reply related
* [PATCH v2] powerpc+sparc/vio: Modernize driver registration
From: Benjamin Herrenschmidt @ 2012-03-27 5:06 UTC (permalink / raw)
To: David Miller, Greg KH; +Cc: Kent Yoder, linuxppc-dev, linux-kernel
This makes vio_register_driver() get the module owner & name at compile
time like PCI drivers do, and adds a name pointer directly in struct
vio_driver to avoid having to explicitly initialize the embedded
struct device.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
v2. Fix the debug printk on powerpc
Remove the duplicate assignment on sparc
arch/powerpc/include/asm/vio.h | 10 +++++++++-
arch/powerpc/kernel/vio.c | 12 ++++++++----
arch/sparc/include/asm/vio.h | 9 ++++++++-
arch/sparc/kernel/ds.c | 5 +----
arch/sparc/kernel/vio.c | 8 ++++++--
drivers/block/sunvdc.c | 5 +----
drivers/net/ethernet/ibm/ibmveth.c | 7 ++-----
drivers/net/ethernet/sun/sunvnet.c | 5 +----
drivers/scsi/ibmvscsi/ibmvfc.c | 7 ++-----
drivers/scsi/ibmvscsi/ibmvscsi.c | 7 ++-----
drivers/scsi/ibmvscsi/ibmvstgt.c | 5 +----
drivers/tty/hvc/hvc_vio.c | 7 ++-----
drivers/tty/hvc/hvcs.c | 5 +----
13 files changed, 44 insertions(+), 48 deletions(-)
diff --git a/arch/powerpc/include/asm/vio.h b/arch/powerpc/include/asm/vio.h
index 0a290a1..6bfd5ff 100644
--- a/arch/powerpc/include/asm/vio.h
+++ b/arch/powerpc/include/asm/vio.h
@@ -69,6 +69,7 @@ struct vio_dev {
};
struct vio_driver {
+ const char *name;
const struct vio_device_id *id_table;
int (*probe)(struct vio_dev *dev, const struct vio_device_id *id);
int (*remove)(struct vio_dev *dev);
@@ -76,10 +77,17 @@ struct vio_driver {
* be loaded in a CMO environment if it uses DMA.
*/
unsigned long (*get_desired_dma)(struct vio_dev *dev);
+ const struct dev_pm_ops *pm;
struct device_driver driver;
};
-extern int vio_register_driver(struct vio_driver *drv);
+extern int __vio_register_driver(struct vio_driver *drv, struct module *owner,
+ const char *mod_name);
+/*
+ * vio_register_driver must be a macro so that KBUILD_MODNAME can be expanded
+ */
+#define vio_register_driver(driver) \
+ __vio_register_driver(driver, THIS_MODULE, KBUILD_MODNAME)
extern void vio_unregister_driver(struct vio_driver *drv);
extern int vio_cmo_entitlement_update(size_t);
diff --git a/arch/powerpc/kernel/vio.c b/arch/powerpc/kernel/vio.c
index bca3fc4..b2f7c84 100644
--- a/arch/powerpc/kernel/vio.c
+++ b/arch/powerpc/kernel/vio.c
@@ -1159,17 +1159,21 @@ static int vio_bus_remove(struct device *dev)
* vio_register_driver: - Register a new vio driver
* @drv: The vio_driver structure to be registered.
*/
-int vio_register_driver(struct vio_driver *viodrv)
+int __vio_register_driver(struct vio_driver *viodrv, struct module *owner,
+ const char *mod_name)
{
- printk(KERN_DEBUG "%s: driver %s registering\n", __func__,
- viodrv->driver.name);
+ pr_debug("%s: driver %s registering\n", __func__, viodrv->name);
/* fill in 'struct driver' fields */
+ viodrv->driver.name = viodrv->name;
+ viodrv->driver.pm = viodrv->pm;
viodrv->driver.bus = &vio_bus_type;
+ viodrv->driver.owner = owner;
+ viodrv->driver.mod_name = mod_name;
return driver_register(&viodrv->driver);
}
-EXPORT_SYMBOL(vio_register_driver);
+EXPORT_SYMBOL(__vio_register_driver);
/**
* vio_unregister_driver - Remove registration of vio driver.
diff --git a/arch/sparc/include/asm/vio.h b/arch/sparc/include/asm/vio.h
index 9d83d3b..432afa8 100644
--- a/arch/sparc/include/asm/vio.h
+++ b/arch/sparc/include/asm/vio.h
@@ -284,6 +284,7 @@ struct vio_dev {
};
struct vio_driver {
+ const char *name;
struct list_head node;
const struct vio_device_id *id_table;
int (*probe)(struct vio_dev *dev, const struct vio_device_id *id);
@@ -371,7 +372,13 @@ do { if (vio->debug & VIO_DEBUG_##TYPE) \
vio->vdev->channel_id, ## a); \
} while (0)
-extern int vio_register_driver(struct vio_driver *drv);
+extern int __vio_register_driver(struct vio_driver *drv, struct module *owner,
+ const char *mod_name);
+/*
+ * vio_register_driver must be a macro so that KBUILD_MODNAME can be expanded
+ */
+#define vio_register_driver(driver) \
+ __vio_register_driver(driver, THIS_MODULE, KBUILD_MODNAME)
extern void vio_unregister_driver(struct vio_driver *drv);
static inline struct vio_driver *to_vio_driver(struct device_driver *drv)
diff --git a/arch/sparc/kernel/ds.c b/arch/sparc/kernel/ds.c
index 381edcd..fea13c7 100644
--- a/arch/sparc/kernel/ds.c
+++ b/arch/sparc/kernel/ds.c
@@ -1244,10 +1244,7 @@ static struct vio_driver ds_driver = {
.id_table = ds_match,
.probe = ds_probe,
.remove = ds_remove,
- .driver = {
- .name = "ds",
- .owner = THIS_MODULE,
- }
+ .name = "ds",
};
static int __init ds_init(void)
diff --git a/arch/sparc/kernel/vio.c b/arch/sparc/kernel/vio.c
index f67e28e..5cffdc5 100644
--- a/arch/sparc/kernel/vio.c
+++ b/arch/sparc/kernel/vio.c
@@ -119,13 +119,17 @@ static struct bus_type vio_bus_type = {
.remove = vio_device_remove,
};
-int vio_register_driver(struct vio_driver *viodrv)
+int __vio_register_driver(struct vio_driver *viodrv, struct module *owner,
+ const char *mod_name)
{
viodrv->driver.bus = &vio_bus_type;
+ viodrv->driver.name = viodrv->name;
+ viodrv->driver.owner = owner;
+ viodrv->driver.mod_name = mod_name;
return driver_register(&viodrv->driver);
}
-EXPORT_SYMBOL(vio_register_driver);
+EXPORT_SYMBOL(__vio_register_driver);
void vio_unregister_driver(struct vio_driver *viodrv)
{
diff --git a/drivers/block/sunvdc.c b/drivers/block/sunvdc.c
index 48e8fee..9dcf76a 100644
--- a/drivers/block/sunvdc.c
+++ b/drivers/block/sunvdc.c
@@ -839,10 +839,7 @@ static struct vio_driver vdc_port_driver = {
.id_table = vdc_port_match,
.probe = vdc_port_probe,
.remove = vdc_port_remove,
- .driver = {
- .name = "vdc_port",
- .owner = THIS_MODULE,
- }
+ .name = "vdc_port",
};
static int __init vdc_init(void)
diff --git a/drivers/net/ethernet/ibm/ibmveth.c b/drivers/net/ethernet/ibm/ibmveth.c
index e877371..9010cea 100644
--- a/drivers/net/ethernet/ibm/ibmveth.c
+++ b/drivers/net/ethernet/ibm/ibmveth.c
@@ -1616,11 +1616,8 @@ static struct vio_driver ibmveth_driver = {
.probe = ibmveth_probe,
.remove = ibmveth_remove,
.get_desired_dma = ibmveth_get_desired_dma,
- .driver = {
- .name = ibmveth_driver_name,
- .owner = THIS_MODULE,
- .pm = &ibmveth_pm_ops,
- }
+ .name = ibmveth_driver_name,
+ .pm = &ibmveth_pm_ops,
};
static int __init ibmveth_module_init(void)
diff --git a/drivers/net/ethernet/sun/sunvnet.c b/drivers/net/ethernet/sun/sunvnet.c
index 92a037a..38e3ae9 100644
--- a/drivers/net/ethernet/sun/sunvnet.c
+++ b/drivers/net/ethernet/sun/sunvnet.c
@@ -1259,10 +1259,7 @@ static struct vio_driver vnet_port_driver = {
.id_table = vnet_port_match,
.probe = vnet_port_probe,
.remove = vnet_port_remove,
- .driver = {
- .name = "vnet_port",
- .owner = THIS_MODULE,
- }
+ .name = "vnet_port",
};
static int __init vnet_init(void)
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c
index bdfa223..134a0ae 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc.c
@@ -4890,11 +4890,8 @@ static struct vio_driver ibmvfc_driver = {
.probe = ibmvfc_probe,
.remove = ibmvfc_remove,
.get_desired_dma = ibmvfc_get_desired_dma,
- .driver = {
- .name = IBMVFC_NAME,
- .owner = THIS_MODULE,
- .pm = &ibmvfc_pm_ops,
- }
+ .name = IBMVFC_NAME,
+ .pm = &ibmvfc_pm_ops,
};
static struct fc_function_template ibmvfc_transport_functions = {
diff --git a/drivers/scsi/ibmvscsi/ibmvscsi.c b/drivers/scsi/ibmvscsi/ibmvscsi.c
index e984951..3a6c474 100644
--- a/drivers/scsi/ibmvscsi/ibmvscsi.c
+++ b/drivers/scsi/ibmvscsi/ibmvscsi.c
@@ -2061,11 +2061,8 @@ static struct vio_driver ibmvscsi_driver = {
.probe = ibmvscsi_probe,
.remove = ibmvscsi_remove,
.get_desired_dma = ibmvscsi_get_desired_dma,
- .driver = {
- .name = "ibmvscsi",
- .owner = THIS_MODULE,
- .pm = &ibmvscsi_pm_ops,
- }
+ .name = "ibmvscsi",
+ .pm = &ibmvscsi_pm_ops,
};
static struct srp_function_template ibmvscsi_transport_functions = {
diff --git a/drivers/scsi/ibmvscsi/ibmvstgt.c b/drivers/scsi/ibmvscsi/ibmvstgt.c
index 2256bab..aa7ed81 100644
--- a/drivers/scsi/ibmvscsi/ibmvstgt.c
+++ b/drivers/scsi/ibmvscsi/ibmvstgt.c
@@ -918,10 +918,7 @@ static struct vio_driver ibmvstgt_driver = {
.id_table = ibmvstgt_device_table,
.probe = ibmvstgt_probe,
.remove = ibmvstgt_remove,
- .driver = {
- .name = "ibmvscsis",
- .owner = THIS_MODULE,
- }
+ .name = "ibmvscsis",
};
static int get_system_info(void)
diff --git a/drivers/tty/hvc/hvc_vio.c b/drivers/tty/hvc/hvc_vio.c
index 3a0d53d..ee30779 100644
--- a/drivers/tty/hvc/hvc_vio.c
+++ b/drivers/tty/hvc/hvc_vio.c
@@ -310,11 +310,8 @@ static int __devexit hvc_vio_remove(struct vio_dev *vdev)
static struct vio_driver hvc_vio_driver = {
.id_table = hvc_driver_table,
.probe = hvc_vio_probe,
- .remove = __devexit_p(hvc_vio_remove),
- .driver = {
- .name = hvc_driver_name,
- .owner = THIS_MODULE,
- }
+ .remove = hvc_vio_remove,
+ .name = hvc_driver_name,
};
static int __init hvc_vio_init(void)
diff --git a/drivers/tty/hvc/hvcs.c b/drivers/tty/hvc/hvcs.c
index d237591..3436436 100644
--- a/drivers/tty/hvc/hvcs.c
+++ b/drivers/tty/hvc/hvcs.c
@@ -879,10 +879,7 @@ static struct vio_driver hvcs_vio_driver = {
.id_table = hvcs_driver_table,
.probe = hvcs_probe,
.remove = __devexit_p(hvcs_remove),
- .driver = {
- .name = hvcs_driver_name,
- .owner = THIS_MODULE,
- }
+ .name = hvcs_driver_name,
};
/* Only called from hvcs_get_pi please */
^ permalink raw reply related
* Re: [PATCH v2] powerpc+sparc/vio: Modernize driver registration
From: David Miller @ 2012-03-27 5:53 UTC (permalink / raw)
To: benh; +Cc: gregkh, key, linuxppc-dev, linux-kernel
In-Reply-To: <1332824790.2882.51.camel@pasglop>
From: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Date: Tue, 27 Mar 2012 16:06:30 +1100
> This makes vio_register_driver() get the module owner & name at compile
> time like PCI drivers do, and adds a name pointer directly in struct
> vio_driver to avoid having to explicitly initialize the embedded
> struct device.
>
> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Acked-by: David S. Miller <davem@davemloft.net>
^ permalink raw reply
* [PATCH] powerpc/perf: Fix instruction address sampling on 970 and Power4
From: Benjamin Herrenschmidt @ 2012-03-27 6:47 UTC (permalink / raw)
To: linuxppc-dev; +Cc: Anton Blanchard, Paul Mackerras
970 and Power4 don't support "continuous sampling" which means that
when we aren't in marked instruction sampling mode (marked events),
SIAR isn't updated with the last instruction sampled before the
perf interrupt. On those processors, we must thus use the exception
SRR0 value as the sampled instruction pointer.
Those processors also don't support the SIPR and SIHV bits in MMCRA
which means we need some kind of heuristic to decide if SIAR values
represent kernel or user addresses.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
arch/powerpc/include/asm/perf_event_server.h | 2 +
arch/powerpc/perf/core-book3s.c | 46 +++++++++++++++++++++++---
arch/powerpc/perf/power4-pmu.c | 1 +
arch/powerpc/perf/ppc970-pmu.c | 1 +
4 files changed, 45 insertions(+), 5 deletions(-)
diff --git a/arch/powerpc/include/asm/perf_event_server.h b/arch/powerpc/include/asm/perf_event_server.h
index 1a8093f..078019b 100644
--- a/arch/powerpc/include/asm/perf_event_server.h
+++ b/arch/powerpc/include/asm/perf_event_server.h
@@ -47,6 +47,8 @@ struct power_pmu {
*/
#define PPMU_LIMITED_PMC5_6 1 /* PMC5/6 have limited function */
#define PPMU_ALT_SIPR 2 /* uses alternate posn for SIPR/HV */
+#define PPMU_NO_SIPR 4 /* no SIPR/HV in MMCRA at all */
+#define PPMU_NO_CONT_SAMPLING 8 /* no continuous sampling */
/*
* Values for flags to get_alternatives()
diff --git a/arch/powerpc/perf/core-book3s.c b/arch/powerpc/perf/core-book3s.c
index c2e27ed..02aee03 100644
--- a/arch/powerpc/perf/core-book3s.c
+++ b/arch/powerpc/perf/core-book3s.c
@@ -116,14 +116,45 @@ static inline void perf_get_data_addr(struct pt_regs *regs, u64 *addrp)
*addrp = mfspr(SPRN_SDAR);
}
+static inline u32 perf_flags_from_msr(struct pt_regs *regs)
+{
+ if (regs->msr & MSR_PR)
+ return PERF_RECORD_MISC_USER;
+ if ((regs->msr & MSR_HV) && freeze_events_kernel != MMCR0_FCHV)
+ return PERF_RECORD_MISC_HYPERVISOR;
+ return PERF_RECORD_MISC_KERNEL;
+}
+
static inline u32 perf_get_misc_flags(struct pt_regs *regs)
{
unsigned long mmcra = regs->dsisr;
unsigned long sihv = MMCRA_SIHV;
unsigned long sipr = MMCRA_SIPR;
+ /* Not a PMU interrupt: Make up flags from regs->msr */
if (TRAP(regs) != 0xf00)
- return 0; /* not a PMU interrupt */
+ return perf_flags_from_msr(regs);
+
+ /*
+ * If we don't support continuous sampling and this
+ * is not a marked event, same deal
+ */
+ if ((ppmu->flags & PPMU_NO_CONT_SAMPLING) &&
+ !(mmcra & MMCRA_SAMPLE_ENABLE))
+ return perf_flags_from_msr(regs);
+
+ /*
+ * If we don't have flags in MMCRA, rather than using
+ * the MSR, we intuit the flags from the address in
+ * SIAR which should give slightly more reliable
+ * results
+ */
+ if (ppmu->flags & PPMU_NO_SIPR) {
+ unsigned long siar = mfspr(SPRN_SIAR);
+ if (siar >= PAGE_OFFSET)
+ return PERF_RECORD_MISC_KERNEL;
+ return PERF_RECORD_MISC_USER;
+ }
if (ppmu->flags & PPMU_ALT_SIPR) {
sihv = POWER6_MMCRA_SIHV;
@@ -1299,13 +1330,18 @@ unsigned long perf_misc_flags(struct pt_regs *regs)
*/
unsigned long perf_instruction_pointer(struct pt_regs *regs)
{
- unsigned long ip;
+ unsigned long mmcra = regs->dsisr;
+ /* Not a PMU interrupt */
if (TRAP(regs) != 0xf00)
- return regs->nip; /* not a PMU interrupt */
+ return regs->nip;
+
+ /* Processor doesn't support sampling non marked events */
+ if ((ppmu->flags & PPMU_NO_CONT_SAMPLING) &&
+ !(mmcra & MMCRA_SAMPLE_ENABLE))
+ return regs->nip;
- ip = mfspr(SPRN_SIAR) + perf_ip_adjust(regs);
- return ip;
+ return mfspr(SPRN_SIAR) + perf_ip_adjust(regs);
}
static bool pmc_overflow(unsigned long val)
diff --git a/arch/powerpc/perf/power4-pmu.c b/arch/powerpc/perf/power4-pmu.c
index b4f1dda..9103a1d 100644
--- a/arch/powerpc/perf/power4-pmu.c
+++ b/arch/powerpc/perf/power4-pmu.c
@@ -607,6 +607,7 @@ static struct power_pmu power4_pmu = {
.n_generic = ARRAY_SIZE(p4_generic_events),
.generic_events = p4_generic_events,
.cache_events = &power4_cache_events,
+ .flags = PPMU_NO_SIPR | PPMU_NO_CONT_SAMPLING,
};
static int __init init_power4_pmu(void)
diff --git a/arch/powerpc/perf/ppc970-pmu.c b/arch/powerpc/perf/ppc970-pmu.c
index 111eb25..20139ce 100644
--- a/arch/powerpc/perf/ppc970-pmu.c
+++ b/arch/powerpc/perf/ppc970-pmu.c
@@ -487,6 +487,7 @@ static struct power_pmu ppc970_pmu = {
.n_generic = ARRAY_SIZE(ppc970_generic_events),
.generic_events = ppc970_generic_events,
.cache_events = &ppc970_cache_events,
+ .flags = PPMU_NO_SIPR | PPMU_NO_CONT_SAMPLING,
};
static int __init init_ppc970_pmu(void)
^ permalink raw reply related
* Re: [PATCH v2] powerpc+sparc/vio: Modernize driver registration
From: Benjamin Herrenschmidt @ 2012-03-27 6:50 UTC (permalink / raw)
To: gregkh; +Cc: key, linuxppc-dev, linux-kernel, David Miller
In-Reply-To: <20120327.015316.1552473998755517441.davem@davemloft.net>
On Tue, 2012-03-27 at 01:53 -0400, David Miller wrote:
> From: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> Date: Tue, 27 Mar 2012 16:06:30 +1100
>
> > This makes vio_register_driver() get the module owner & name at compile
> > time like PCI drivers do, and adds a name pointer directly in struct
> > vio_driver to avoid having to explicitly initialize the embedded
> > struct device.
> >
> > Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
>
> Acked-by: David S. Miller <davem@davemloft.net>
> --
Greg, do you want to carry this or should I ? In the later case, I'd
rather send it to Linus now than having it hanging around til the next
merge window :-)
Cheers,
Ben.
^ permalink raw reply
* RE: [GIT PULL] DMA-mapping framework updates for 3.4
From: Marek Szyprowski @ 2012-03-27 9:59 UTC (permalink / raw)
To: 'Linus Torvalds', 'Dave Airlie'
Cc: linux-mips, linux-ia64, linux-sh, linux-mm, sparclinux,
linux-arch, 'Jonathan Corbet', x86,
'Arnd Bergmann', microblaze-uclinux, linaro-mm-sig,
Andrzej Pietrasiewicz, 'Thomas Gleixner',
linux-arm-kernel, discuss, linux-kernel,
'FUJITA Tomonori', 'Kyungmin Park', linux-alpha,
'Andrew Morton', linuxppc-dev
In-Reply-To: <CA+55aFy9oxMrfm-+deMqV=XnFOa98aKXqW+8PR-P-zOARtC2BQ@mail.gmail.com>
Hi Linus,
On Friday, March 23, 2012 10:36 PM Linus Torvalds wrote:
> On Tue, Mar 20, 2012 at 12:24 AM, Marek Szyprowski
> <m.szyprowski@samsung.com> wrote:
> >
> > =A0git://git.infradead.org/users/kmpark/linux-samsung =
dma-mapping-next
> >
> > Those patches introduce a new alloc method (with support for memory
> > attributes) in dma_map_ops structure, which will later replace
> > dma_alloc_coherent and dma_alloc_writecombine functions.
>=20
> So I'm quite unhappy with these patches.
>=20
> Here's just the few problems I saw from some *very* quick look-through
> of the git tree:
>=20
> - I'm not seeing ack's from the architecture maintainers for the
> patches that change some architecture.
Ok, I've asked personally each respective maintainer for an ack or=20
comments. Before I've sent my pull request there were only a few =
comments
on the mailing lists, but this topic have been discussed at ELC-E in =
Prague
and Linaro Memory-management summit in Budapest (May 2011).
=20
> - Even more importantly, what I really want is acks and comments from
> the people who are expected to *use* this.
The plan is to use it as a base for further cleanup in the dma-mapping=20
implementations, especially on ARM architecture. The changes are =
designed
in such a way to keep compatibility with the existing users of the API.=20
ARM will be the first architecture which will use the new attributes.=20
The main clients for this new API will be mainly multimedia drivers =
(v4l2,
drm) and dma_buf buffer sharing. The advantage of this approach is the=20
fact that the same drivers can be used on other architectures without =
any
changes in the dma calls. The attributes which are not supported by the
architecture will be simply ignored.
> - it looks like patches break compilation half-way through the
> series. Just one example I noticed: the "x86 adaptation" patch changes
> the functions in lib/swiotlb.c, but afaik ia64 *also* uses those. So
> now ia64 is broken until a couple of patches later. I suspect there
> are other examples like that.
Ok, I missed this and I will fix this issue asap.
> - the sign-off chains are odd. What happened there? Several patches
> are signed off by Kyungmin Park, but he doesn't seem to be "in the
> chain" at all. Whazzup? (*)
That was caused by our internal flow of the patches, but I see that it=20
made only a lot of confusion. I got my own git repository at=20
git.linaro.org and I will resolve these sign-off issues correctly there.
> - Finally, how/why are "dma attributes" different from the per-device
> dma limits ("device_dma_parameters")
Device dma parameters are global for all dma mapping operations for the=20
specified device, while dma attributes can be set for each allocation or
mapping call. Dma attributes are mainly used to provide some hints to =
the
dma mapping core, which might improve speed/performance/throughput for
some particular sw&hw architectures. Unsupported attributes are ignored,
so the in the worst case a driver gets coherent mapping.
Best regards
--=20
Marek Szyprowski
Samsung Poland R&D Center
^ permalink raw reply
* [PATCH 1/1] Add support 2 SATA ports for Maui and change filename from sata_dwc_460ex.c to sata_dwc_4xx.c
From: Thang Q. Nguyen @ 2012-03-27 10:26 UTC (permalink / raw)
To: Benjamin Herrenschmidt, Paul Mackerras, David Miller, Josh Boyer,
Stefan Roese
Cc: netdev, Thang Q. Nguyen, linuxppc-dev, linux-kernel
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 66245 bytes --]
Support 2 native SATA ports on APM821XX and change the existing file name from sata_dwc_460ex.c to sata_dwc_4xx.c. This supports both 460EX and APM821XX.
Signed-off-by: Thang Q. Nguyen <tqnguyen@apm.com>
---
arch/powerpc/boot/dts/bluestone.dts | 21 +
drivers/ata/Makefile | 2 +-
drivers/ata/sata_dwc_4xx.c | 2178 +++++++++++++++++++++++++++++++++++
3 files changed, 2200 insertions(+), 1 deletions(-)
create mode 100644 drivers/ata/sata_dwc_4xx.c
diff --git a/arch/powerpc/boot/dts/bluestone.dts b/arch/powerpc/boot/dts/bluestone.dts
index 2a56a0d..1396e2c 100644
--- a/arch/powerpc/boot/dts/bluestone.dts
+++ b/arch/powerpc/boot/dts/bluestone.dts
@@ -145,6 +145,27 @@
/*RXDE*/ 0x5 0x4>;
};
+ /* SATA DWC devices */
+ SATA0: sata@bffd1000 {
+ compatible = "amcc,sata-apm821xx";
+ reg = <4 0xbffd1000 0x800 /* SATA0 */
+ 4 0xbffd0800 0x400>; /* AHBDMA */
+ dma-channel=<0>;
+ interrupt-parent = <&UIC0>;
+ interrupts = <26 4 /* SATA0 */
+ 25 4>; /* AHBDMA */
+ };
+
+ SATA1: sata@bffd1800 {
+ compatible = "amcc,sata-apm821xx";
+ reg = <4 0xbffd1800 0x800 /* SATA1 */
+ 4 0xbffd0800 0x400>; /* AHBDMA */
+ dma-channel=<1>;
+ interrupt-parent = <&UIC0>;
+ interrupts = <27 4 /* SATA1 */
+ 25 4>; /* AHBDMA */
+ };
+
POB0: opb {
compatible = "ibm,opb";
#address-cells = <1>;
diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile
index 6ece5b7..d225c0c 100644
--- a/drivers/ata/Makefile
+++ b/drivers/ata/Makefile
@@ -8,7 +8,7 @@ obj-$(CONFIG_SATA_AHCI_PLATFORM) += ahci_platform.o libahci.o
obj-$(CONFIG_SATA_FSL) += sata_fsl.o
obj-$(CONFIG_SATA_INIC162X) += sata_inic162x.o
obj-$(CONFIG_SATA_SIL24) += sata_sil24.o
-obj-$(CONFIG_SATA_DWC) += sata_dwc_460ex.o
+obj-$(CONFIG_SATA_DWC) += sata_dwc_4xx.o
# SFF w/ custom DMA
obj-$(CONFIG_PDC_ADMA) += pdc_adma.o
diff --git a/drivers/ata/sata_dwc_4xx.c b/drivers/ata/sata_dwc_4xx.c
new file mode 100644
index 0000000..f354155
--- /dev/null
+++ b/drivers/ata/sata_dwc_4xx.c
@@ -0,0 +1,2178 @@
+/*
+ * drivers/ata/sata_dwc_4xx.c
+ *
+ * Synopsys DesignWare Cores (DWC) SATA host driver
+ *
+ * Author: Mark Miesfeld <mmiesfeld@amcc.com>
+ *
+ * Ported from 2.6.19.2 to 2.6.25/26 by Stefan Roese <sr@denx.de>
+ * Copyright 2008 DENX Software Engineering
+ *
+ * Based on versions provided by AMCC and Synopsys which are:
+ * Copyright 2006 Applied Micro Circuits Corporation
+ * COPYRIGHT (C) 2005 SYNOPSYS, INC. ALL RIGHTS RESERVED
+ *
+ * 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.
+ *
+ * CHANGES:
+ * - Version 1.4:
+ * + Change filename from sata_dwc_460ex.c to sata_dwc_4xx.c
+ * + This driver supports more than one SATA port. Each SATA port has its
+ * own private attribute. Move sata_dwc_host_priv structure to
+ * sata_dwc_device and sata_dwc_device_port structures.
+ */
+
+#ifdef CONFIG_SATA_DWC_DEBUG
+#define DEBUG
+#endif
+
+#ifdef CONFIG_SATA_DWC_VDEBUG
+#define VERBOSE_DEBUG
+#define DEBUG_NCQ
+#endif
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/libata.h>
+#include <linux/slab.h>
+#include "libata.h"
+
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_cmnd.h>
+
+/* These two are defined in "libata.h" */
+#undef DRV_NAME
+#undef DRV_VERSION
+#define DRV_NAME "sata-dwc"
+#define DRV_VERSION "1.4"
+
+#define DMA_NUM_CHANS 2
+#define DMA_NUM_CHAN_REGS 8
+
+#define AHB_DMA_BRST_DFLT 64 /* 16 data items burst length*/
+
+struct dmareg {
+ u32 low; /* Low bits 0-31 */
+ u32 high; /* High bits 32-63 */
+};
+
+/* DMA Per Channel registers */
+struct dma_chan_regs {
+ struct dmareg sar; /* Source Address */
+ struct dmareg dar; /* Destination address */
+ struct dmareg llp; /* Linked List Pointer */
+ struct dmareg ctl; /* Control */
+ struct dmareg sstat; /* Source Status not implemented in core */
+ struct dmareg dstat; /* Destination Status not implemented in core*/
+ struct dmareg sstatar; /* Source Status Address not impl in core */
+ struct dmareg dstatar; /* Destination Status Address not implemente */
+ struct dmareg cfg; /* Config */
+ struct dmareg sgr; /* Source Gather */
+ struct dmareg dsr; /* Destination Scatter */
+};
+
+/* Generic Interrupt Registers */
+struct dma_interrupt_regs {
+ struct dmareg tfr; /* Transfer Interrupt */
+ struct dmareg block; /* Block Interrupt */
+ struct dmareg srctran; /* Source Transfer Interrupt */
+ struct dmareg dsttran; /* Dest Transfer Interrupt */
+ struct dmareg error; /* Error */
+};
+
+struct ahb_dma_regs {
+ struct dma_chan_regs chan_regs[DMA_NUM_CHAN_REGS];
+ struct dma_interrupt_regs interrupt_raw; /* Raw Interrupt */
+ struct dma_interrupt_regs interrupt_status; /* Interrupt Status */
+ struct dma_interrupt_regs interrupt_mask; /* Interrupt Mask */
+ struct dma_interrupt_regs interrupt_clear; /* Interrupt Clear */
+ struct dmareg statusInt; /* Interrupt combined*/
+ struct dmareg rq_srcreg; /* Src Trans Req */
+ struct dmareg rq_dstreg; /* Dst Trans Req */
+ struct dmareg rq_sgl_srcreg; /* Sngl Src Trans Req*/
+ struct dmareg rq_sgl_dstreg; /* Sngl Dst Trans Req*/
+ struct dmareg rq_lst_srcreg; /* Last Src Trans Req*/
+ struct dmareg rq_lst_dstreg; /* Last Dst Trans Req*/
+ struct dmareg dma_cfg; /* DMA Config */
+ struct dmareg dma_chan_en; /* DMA Channel Enable*/
+ struct dmareg dma_id; /* DMA ID */
+ struct dmareg dma_test; /* DMA Test */
+ struct dmareg res1; /* reserved */
+ struct dmareg res2; /* reserved */
+ /*
+ * DMA Comp Params
+ * Param 6 = dma_param[0], Param 5 = dma_param[1],
+ * Param 4 = dma_param[2] ...
+ */
+ struct dmareg dma_params[6];
+};
+
+/* Data structure for linked list item */
+struct lli {
+ u32 sar; /* Source Address */
+ u32 dar; /* Destination address */
+ u32 llp; /* Linked List Pointer */
+ struct dmareg ctl; /* Control */
+ struct dmareg dstat; /* Destination Status */
+};
+
+enum {
+ SATA_DWC_DMAC_LLI_SZ = (sizeof(struct lli)),
+ SATA_DWC_DMAC_LLI_NUM = 256,
+ SATA_DWC_DMAC_LLI_TBL_SZ = (SATA_DWC_DMAC_LLI_SZ * \
+ SATA_DWC_DMAC_LLI_NUM),
+ SATA_DWC_DMAC_TWIDTH_BYTES = 4,
+ SATA_DWC_DMAC_CTRL_TSIZE_MAX = (0x00000800 * \
+ SATA_DWC_DMAC_TWIDTH_BYTES),
+};
+
+/* Host Controller ID */
+enum {
+ APM_460EX_SATA = 0,
+ APM_821XX_SATA = 1,
+};
+
+/* DMA Register Operation Bits */
+enum {
+ DMA_EN = 0x00000001, /* Enable AHB DMA */
+ DMA_CTL_LLP_SRCEN = 0x10000000, /* Blk chain enable Src */
+ DMA_CTL_LLP_DSTEN = 0x08000000, /* Blk chain enable Dst */
+};
+
+#define DMA_CTL_BLK_TS(size) ((size) & 0x000000FFF) /* Blk Transfer size */
+#define DMA_CHANNEL(ch) (0x00000001 << (ch)) /* Select channel */
+ /* Enable channel */
+#define DMA_ENABLE_CHAN(ch) (0x00000101 << (ch))
+ /* Disable channel */
+#define DMA_DISABLE_CHAN(ch) (0x000000100 << (ch))
+ /* Transfer Type & Flow Controller */
+#define DMA_CTL_TTFC(type) (((type) & 0x7) << 20)
+#define DMA_CTL_SMS(num) (((num) & 0x3) << 25) /* Src Master Select */
+#define DMA_CTL_DMS(num) (((num) & 0x3) << 23)/* Dst Master Select */
+ /* Src Burst Transaction Length */
+#define DMA_CTL_SRC_MSIZE(size) (((size) & 0x7) << 14)
+ /* Dst Burst Transaction Length */
+#define DMA_CTL_DST_MSIZE(size) (((size) & 0x7) << 11)
+ /* Source Transfer Width */
+#define DMA_CTL_SRC_TRWID(size) (((size) & 0x7) << 4)
+ /* Destination Transfer Width */
+#define DMA_CTL_DST_TRWID(size) (((size) & 0x7) << 1)
+
+/* Assign HW handshaking interface (x) to destination / source peripheral */
+#define DMA_CFG_HW_HS_DEST(int_num) (((int_num) & 0xF) << 11)
+#define DMA_CFG_HW_HS_SRC(int_num) (((int_num) & 0xF) << 7)
+#define DMA_CFG_HW_CH_PRIOR(int_num) (((int_num) & 0xF) << 5)
+#define DMA_LLP_LMS(addr, master) (((addr) & 0xfffffffc) | (master))
+
+/*
+ * This define is used to set block chaining disabled in the control low
+ * register. It is already in little endian format so it can be &'d dirctly.
+ * It is essentially: cpu_to_le32(~(DMA_CTL_LLP_SRCEN | DMA_CTL_LLP_DSTEN))
+ */
+enum {
+ DMA_CTL_LLP_DISABLE_LE32 = 0xffffffe7,
+ DMA_CTL_TTFC_P2M_DMAC = 0x00000002, /* Per to mem, DMAC cntr */
+ DMA_CTL_TTFC_M2P_PER = 0x00000003, /* Mem to per, peripheral cntr */
+ DMA_CTL_SINC_INC = 0x00000000, /* Source Address Increment */
+ DMA_CTL_SINC_DEC = 0x00000200,
+ DMA_CTL_SINC_NOCHANGE = 0x00000400,
+ DMA_CTL_DINC_INC = 0x00000000, /* Destination Address Increment */
+ DMA_CTL_DINC_DEC = 0x00000080,
+ DMA_CTL_DINC_NOCHANGE = 0x00000100,
+ DMA_CTL_INT_EN = 0x00000001, /* Interrupt Enable */
+
+/* Channel Configuration Register high bits */
+ DMA_CFG_FCMOD_REQ = 0x00000001, /* Flow Control - request based */
+ DMA_CFG_PROTCTL = (0x00000003 << 2),/* Protection Control */
+
+/* Channel Configuration Register low bits */
+ DMA_CFG_RELD_DST = 0x80000000, /* Reload Dest / Src Addr */
+ DMA_CFG_RELD_SRC = 0x40000000,
+ DMA_CFG_HS_SELSRC = 0x00000800, /* Software handshake Src/ Dest */
+ DMA_CFG_HS_SELDST = 0x00000400,
+ DMA_CFG_FIFOEMPTY = (0x00000001 << 9), /* FIFO Empty bit */
+
+/* Channel Linked List Pointer Register */
+ DMA_LLP_AHBMASTER1 = 0, /* List Master Select */
+ DMA_LLP_AHBMASTER2 = 1,
+
+ SATA_DWC_MAX_PORTS = 1,
+
+ SATA_DWC_SCR_OFFSET = 0x24,
+ SATA_DWC_REG_OFFSET = 0x64,
+};
+
+/* DWC SATA Registers */
+struct sata_dwc_regs {
+ u32 fptagr; /* 1st party DMA tag */
+ u32 fpbor; /* 1st party DMA buffer offset */
+ u32 fptcr; /* 1st party DMA Xfr count */
+ u32 dmacr; /* DMA Control */
+ u32 dbtsr; /* DMA Burst Transac size */
+ u32 intpr; /* Interrupt Pending */
+ u32 intmr; /* Interrupt Mask */
+ u32 errmr; /* Error Mask */
+ u32 llcr; /* Link Layer Control */
+ u32 phycr; /* PHY Control */
+ u32 physr; /* PHY Status */
+ u32 rxbistpd; /* Recvd BIST pattern def register */
+ u32 rxbistpd1; /* Recvd BIST data dword1 */
+ u32 rxbistpd2; /* Recvd BIST pattern data dword2 */
+ u32 txbistpd; /* Trans BIST pattern def register */
+ u32 txbistpd1; /* Trans BIST data dword1 */
+ u32 txbistpd2; /* Trans BIST data dword2 */
+ u32 bistcr; /* BIST Control Register */
+ u32 bistfctr; /* BIST FIS Count Register */
+ u32 bistsr; /* BIST Status Register */
+ u32 bistdecr; /* BIST Dword Error count register */
+ u32 res[15]; /* Reserved locations */
+ u32 testr; /* Test Register */
+ u32 versionr; /* Version Register */
+ u32 idr; /* ID Register */
+ u32 unimpl[192]; /* Unimplemented */
+ u32 dmadr[256]; /* FIFO Locations in DMA Mode */
+};
+
+enum {
+ SCR_SCONTROL_DET_ENABLE = 0x00000001,
+ SCR_SSTATUS_DET_PRESENT = 0x00000001,
+ SCR_SERROR_DIAG_X = 0x04000000,
+/* DWC SATA Register Operations */
+ SATA_DWC_TXFIFO_DEPTH = 0x01FF,
+ SATA_DWC_RXFIFO_DEPTH = 0x01FF,
+ SATA_DWC_DMACR_TMOD_TXCHEN = 0x00000004,
+ SATA_DWC_DMACR_TXCHEN = (0x00000001 | SATA_DWC_DMACR_TMOD_TXCHEN),
+ SATA_DWC_DMACR_RXCHEN = (0x00000002 | SATA_DWC_DMACR_TMOD_TXCHEN),
+ SATA_DWC_DMACR_TXRXCH_CLEAR = SATA_DWC_DMACR_TMOD_TXCHEN,
+ SATA_DWC_INTPR_DMAT = 0x00000001,
+ SATA_DWC_INTPR_NEWFP = 0x00000002,
+ SATA_DWC_INTPR_PMABRT = 0x00000004,
+ SATA_DWC_INTPR_ERR = 0x00000008,
+ SATA_DWC_INTPR_NEWBIST = 0x00000010,
+ SATA_DWC_INTPR_IPF = 0x10000000,
+ SATA_DWC_INTMR_DMATM = 0x00000001,
+ SATA_DWC_INTMR_NEWFPM = 0x00000002,
+ SATA_DWC_INTMR_PMABRTM = 0x00000004,
+ SATA_DWC_INTMR_ERRM = 0x00000008,
+ SATA_DWC_INTMR_NEWBISTM = 0x00000010,
+ SATA_DWC_LLCR_SCRAMEN = 0x00000001,
+ SATA_DWC_LLCR_DESCRAMEN = 0x00000002,
+ SATA_DWC_LLCR_RPDEN = 0x00000004,
+/* This is all error bits, zero's are reserved fields. */
+ SATA_DWC_SERROR_ERR_BITS = 0x0FFF0F03
+};
+
+#define SATA_DWC_SCR0_SPD_GET(v) (((v) >> 4) & 0x0000000F)
+#define SATA_DWC_DMACR_TX_CLEAR(v) (((v) & ~SATA_DWC_DMACR_TXCHEN) |\
+ SATA_DWC_DMACR_TMOD_TXCHEN)
+#define SATA_DWC_DMACR_RX_CLEAR(v) (((v) & ~SATA_DWC_DMACR_RXCHEN) |\
+ SATA_DWC_DMACR_TMOD_TXCHEN)
+#define SATA_DWC_DBTSR_MWR(size) (((size)/4) & SATA_DWC_TXFIFO_DEPTH)
+#define SATA_DWC_DBTSR_MRD(size) ((((size)/4) & SATA_DWC_RXFIFO_DEPTH)\
+ << 16)
+struct sata_dwc_device {
+ struct device *dev; /* generic device struct */
+ struct ata_host *host;
+ u8 *reg_base;
+ struct sata_dwc_regs *sata_dwc_regs; /* DW Synopsys SATA specific */
+ u8 *scr_base;
+ int dma_channel; /* DWC SATA DMA channel */
+ int irq_dma;
+ int hostID;
+};
+
+#define SATA_DWC_QCMD_MAX 32
+
+struct sata_dwc_device_port {
+ struct sata_dwc_device *hsdev;
+ struct lli *llit[SATA_DWC_QCMD_MAX]; /* DMA LLI table */
+ dma_addr_t llit_dma[SATA_DWC_QCMD_MAX];
+ u32 dma_chan[SATA_DWC_QCMD_MAX];
+ int dma_pending[SATA_DWC_QCMD_MAX];
+ u32 sata_dwc_sactive_issued;
+ u32 sata_dwc_sactive_queued;
+ u32 dma_interrupt_count;
+};
+
+/*
+ * Commonly used DWC SATA driver Macros
+ */
+#define HSDEV_FROM_HOST(host) ((struct sata_dwc_device *)\
+ (host)->private_data)
+#define HSDEV_FROM_AP(ap) ((struct sata_dwc_device *)\
+ (ap)->host->private_data)
+#define HSDEVP_FROM_AP(ap) ((struct sata_dwc_device_port *)\
+ (ap)->private_data)
+#define HSDEV_FROM_QC(qc) ((struct sata_dwc_device *)\
+ (qc)->ap->host->private_data)
+#define HSDEV_FROM_HSDEVP(p) ((struct sata_dwc_device *)\
+ (hsdevp)->hsdev)
+
+enum {
+ SATA_DWC_DMA_PENDING_NONE = 0,
+ SATA_DWC_DMA_PENDING_TX = 1,
+ SATA_DWC_DMA_PENDING_RX = 2,
+};
+
+/*
+ * Globals
+ */
+static struct sata_dwc_device *dwc_dev_list[2];
+static struct ahb_dma_regs *sata_dma_regs;
+/*
+ * Prototypes
+ */
+static void sata_dwc_bmdma_start_by_tag(struct ata_queued_cmd *qc, u8 tag);
+static int sata_dwc_qc_complete(struct ata_port *ap, struct ata_queued_cmd *qc,
+ u32 check_status);
+static void sata_dwc_port_stop(struct ata_port *ap);
+static void sata_dwc_clear_dmacr(struct sata_dwc_device_port *hsdevp, u8 tag);
+static int dwc_dma_init(struct sata_dwc_device *hsdev, int irq);
+static int dwc_dma_xfer_setup(struct ata_port *ap, dma_addr_t dma_lli);
+static void dwc_dma_xfer_start(int dma_ch);
+static void sata_dwc_init_port(struct ata_port *ap);
+
+
+static const char *get_prot_descript(u8 protocol)
+{
+ switch ((enum ata_tf_protocols)protocol) {
+ case ATA_PROT_NODATA:
+ return "ATA no data";
+ case ATA_PROT_PIO:
+ return "ATA PIO";
+ case ATA_PROT_DMA:
+ return "ATA DMA";
+ case ATA_PROT_NCQ:
+ return "ATA NCQ";
+ case ATAPI_PROT_NODATA:
+ return "ATAPI no data";
+ case ATAPI_PROT_PIO:
+ return "ATAPI PIO";
+ case ATAPI_PROT_DMA:
+ return "ATAPI DMA";
+ default:
+ return "unknown";
+ }
+}
+
+static const char *get_dma_dir_descript(int dma_dir)
+{
+ switch ((enum dma_data_direction)dma_dir) {
+ case DMA_BIDIRECTIONAL:
+ return "bidirectional";
+ case DMA_TO_DEVICE:
+ return "to device";
+ case DMA_FROM_DEVICE:
+ return "from device";
+ default:
+ return "none";
+ }
+}
+
+static void sata_dwc_tf_dump(struct device *dwc_dev, struct ata_taskfile *tf)
+{
+ dev_vdbg(dwc_dev, "taskfile cmd: 0x%02x protocol: %s flags:"
+ "0x%lx device: %x\n", tf->command,
+ get_prot_descript(tf->protocol), tf->flags, tf->device);
+ dev_vdbg(dwc_dev, "feature: 0x%02x nsect: 0x%x lbal: 0x%x "
+ "lbam: 0x%x lbah: 0x%x\n", tf->feature, tf->nsect, tf->lbal,
+ tf->lbam, tf->lbah);
+ dev_vdbg(dwc_dev, "hob_feature: 0x%02x hob_nsect: 0x%x "
+ "hob_lbal: 0x%x hob_lbam: 0x%x hob_lbah: 0x%x\n",
+ tf->hob_feature, tf->hob_nsect, tf->hob_lbal, tf->hob_lbam,
+ tf->hob_lbah);
+}
+
+/*
+ * Calculate value to be programmed in register corresponding to data length
+ * This value is effectively the log(base 2) of the length
+ */
+static int get_burst_length_encode(int datalength)
+{
+ int items = datalength >> 2; /* div by 4 to get lword count */
+
+ if (items >= 64)
+ return 5;
+
+ if (items >= 32)
+ return 4;
+
+ if (items >= 16)
+ return 3;
+
+ if (items >= 8)
+ return 2;
+
+ if (items >= 4)
+ return 1;
+
+ return 0;
+}
+
+/*
+ * Clear channel interrupt. No interrupt for the specified channel
+ * generated until it is enabled again.
+ */
+static void clear_chan_interrupts(int c)
+{
+ out_le32(&(sata_dma_regs->interrupt_clear.tfr.low), DMA_CHANNEL(c));
+ out_le32(&(sata_dma_regs->interrupt_clear.block.low), DMA_CHANNEL(c));
+ out_le32(&(sata_dma_regs->interrupt_clear.srctran.low),
+ DMA_CHANNEL(c));
+ out_le32(&(sata_dma_regs->interrupt_clear.dsttran.low),
+ DMA_CHANNEL(c));
+ out_le32(&(sata_dma_regs->interrupt_clear.error.low), DMA_CHANNEL(c));
+}
+
+/*
+ * Check if the selected DMA channel is currently enabled.
+ */
+static int sata_dwc_dma_chk_en(int ch)
+{
+ u32 dma_chan_reg;
+ /* Read the DMA channel register */
+ dma_chan_reg = in_le32(&(sata_dma_regs->dma_chan_en.low));
+ /* Check if it is currently enabled */
+ if (dma_chan_reg & DMA_CHANNEL(ch))
+ return 1;
+ return 0;
+}
+
+/*
+ * Terminate the current DMA transfer
+ *
+ * Refer to the "Abnormal Transfer Termination" section
+ * Disable the corresponding bit in the ChEnReg register
+ * and poll that register to until the channel is terminated.
+ */
+static void sata_dwc_dma_terminate(struct ata_port *ap, int dma_ch)
+{
+ int enabled = sata_dwc_dma_chk_en(dma_ch);
+ /* If the channel is currenly in use, release it. */
+ if (enabled) {
+ dev_dbg(ap->dev,
+ "%s terminate DMA on channel=%d (mask=0x%08x) ...",
+ __func__, dma_ch, DMA_DISABLE_CHAN(dma_ch));
+ dev_dbg(ap->dev, "ChEnReg=0x%08x\n",
+ in_le32(&(sata_dma_regs->dma_chan_en.low)));
+ /* Disable the selected channel */
+ out_le32(&(sata_dma_regs->dma_chan_en.low),
+ DMA_DISABLE_CHAN(dma_ch));
+
+ /* Wait for the channel is disabled */
+ do {
+ enabled = sata_dwc_dma_chk_en(dma_ch);
+ ndelay(1000);
+ } while (enabled);
+ dev_dbg(ap->dev, "done\n");
+ }
+}
+
+/*
+ * Check if the DMA channel is currently available for transferring data
+ * on the specified ata_port.
+ */
+static int dma_request_channel(struct ata_port *ap)
+{
+ struct sata_dwc_device *hsdev = HSDEV_FROM_AP(ap);
+
+ /* Check if the channel is not currently in use */
+ if (!(in_le32(&(sata_dma_regs->dma_chan_en.low)) &\
+ DMA_CHANNEL(hsdev->dma_channel)))
+ return hsdev->dma_channel;
+
+ dev_err(ap->dev, "%s Channel %d is currently in use\n", __func__,
+ hsdev->dma_channel);
+ return -1;
+}
+
+/*
+ * Processing DMA transfer complete interrupt
+ */
+static irqreturn_t dwc_dma_interrupt(int irq, struct sata_dwc_device *hsdev)
+{
+ int chan;
+ u32 tfr_reg, err_reg;
+ unsigned long flags;
+ struct ata_host *host = (struct ata_host *)hsdev->host;
+ struct ata_port *ap;
+ struct sata_dwc_device_port *hsdevp;
+ u8 tag;
+ unsigned int port = 0;
+
+ spin_lock_irqsave(&host->lock, flags);
+ ap = host->ports[port];
+ hsdevp = HSDEVP_FROM_AP(ap);
+
+ /*
+ * Find the right tag value for the current DMA transfer.
+ * In case of NCQ transfer, tag for the current transfer is set to
+ * active_tag.
+ * For DMA transfer, tag is 0 (active_tag=ATA_TAG_POISION).
+ */
+ if (ap->link.active_tag != ATA_TAG_POISON)
+ tag = ap->link.active_tag;
+ else
+ tag = 0;
+
+ tfr_reg = in_le32(&(sata_dma_regs->interrupt_status.tfr.low));
+ err_reg = in_le32(&(sata_dma_regs->interrupt_status.error.low));
+ dev_dbg(ap->dev, "eot=0x%08x err=0x%08x pending=%d active port=%d\n",
+ tfr_reg, err_reg, hsdevp->dma_pending[tag], port);
+
+ chan = hsdev->dma_channel;
+ if (chan >= 0) {
+ if (tfr_reg & DMA_CHANNEL(chan)) {
+ /* Clear DMA config after transfer complete */
+ sata_dwc_clear_dmacr(hsdevp, tag);
+
+ /* Clear the interrupt */
+ out_le32(&(sata_dma_regs->interrupt_clear.tfr.low),
+ DMA_CHANNEL(chan));
+ }
+
+ /* Check for error interrupt, not expect error happens */
+ if (unlikely(err_reg & DMA_CHANNEL(chan))) {
+ dev_err(ap->dev, "error interrupt err_reg=0x%08x\n",
+ err_reg);
+
+ /* Clear the interrupt. */
+ out_le32(&(sata_dma_regs->interrupt_clear\
+ .error.low),
+ DMA_CHANNEL(chan));
+ }
+ }
+ hsdevp->dma_pending[tag] = SATA_DWC_DMA_PENDING_NONE;
+
+ if (hsdevp->sata_dwc_sactive_queued == 0)
+ ap->link.active_tag = ATA_TAG_POISON;
+
+ spin_unlock_irqrestore(&host->lock, flags);
+ return IRQ_HANDLED;
+}
+
+/*
+ * Handle DMA transfer complete interrupt. This checks and passes the
+ * processing to the appropriate ATA port.
+ */
+static irqreturn_t dma_dwc_handler(int irq, void *hsdev_instance)
+{
+ u32 tfr_reg, err_reg;
+ int chan;
+
+ tfr_reg = in_le32(&(sata_dma_regs->interrupt_status.tfr.low));
+ err_reg = in_le32(&(sata_dma_regs->interrupt_status.error.low));
+
+ for (chan = 0; chan < DMA_NUM_CHANS; chan++) {
+ /* Check for end-of-transfer interrupt. */
+ if (tfr_reg & DMA_CHANNEL(chan))
+ dwc_dma_interrupt(0, dwc_dev_list[chan]);
+
+ /* Check for error interrupt. */
+ if (err_reg & DMA_CHANNEL(chan))
+ dwc_dma_interrupt(0, dwc_dev_list[chan]);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * Registers ISR for a particular DMA channel interrupt
+ */
+static int dma_request_interrupts(struct sata_dwc_device *hsdev, int irq)
+{
+ int retval;
+
+ /* Unmask error interrupt */
+ out_le32(&sata_dma_regs->interrupt_mask.error.low,
+ in_le32(&sata_dma_regs->interrupt_mask.error.low) |
+ DMA_ENABLE_CHAN(hsdev->dma_channel));
+
+ /* Unmask end-of-transfer interrupt */
+ out_le32(&sata_dma_regs->interrupt_mask.tfr.low,
+ in_le32(&sata_dma_regs->interrupt_mask.tfr.low) |
+ DMA_ENABLE_CHAN(hsdev->dma_channel));
+
+ retval = request_irq(irq, dma_dwc_handler, IRQF_SHARED, "SATA DMA",
+ hsdev);
+ if (retval) {
+ dev_err(hsdev->dev, "%s: could not get IRQ %d\n",\
+ __func__, irq);
+ return -ENODEV;
+ }
+
+ /* Mark this interrupt as requested */
+ hsdev->irq_dma = irq;
+
+ return 0;
+}
+
+/*
+ * The Synopsis driver has a comment proposing that better performance
+ * is possible by only enabling interrupts on the last item in the linked list.
+ * However, it seems that could be a problem if an error happened on one of the
+ * first items. The transfer would halt, but no error interrupt would occur.
+ * Currently this function sets interrupts enabled for each linked list item:
+ * DMA_CTL_INT_EN.
+ */
+static int map_sg_to_lli(struct ata_port *ap, struct scatterlist *sg,
+ int num_elems, struct lli *lli, dma_addr_t dma_lli,
+ void __iomem *dmadr_addr, int dir)
+{
+ struct device *dwc_dev = ap->dev;
+ struct sata_dwc_device_port *hsdevp = HSDEVP_FROM_AP(ap);
+ int i, idx = 0;
+ int fis_len = 0;
+ dma_addr_t next_llp;
+ int bl;
+ u32 sms_val, dms_val;
+
+ dev_dbg(dwc_dev, "%s: sg=%p nelem=%d lli=%p dma_lli=0x%08x"
+ " dmadr=0x%08x\n", __func__, sg, num_elems, lli, (u32)dma_lli,
+ (u32)dmadr_addr);
+
+ bl = get_burst_length_encode(AHB_DMA_BRST_DFLT);
+
+ for (i = 0; i < num_elems; i++, sg++) {
+ u32 addr, offset;
+ u32 sg_len, len;
+
+ addr = (u32) sg_dma_address(sg);
+ sg_len = sg_dma_len(sg);
+
+ dev_dbg(dwc_dev, "%s: elem=%d sg_addr=0x%x sg_len"
+ "=%d\n", __func__, i, addr, sg_len);
+
+ while (sg_len) {
+ if (unlikely(idx >= SATA_DWC_DMAC_LLI_NUM)) {
+ /* The LLI table is not large enough. */
+ dev_err(dwc_dev, "LLI table overrun "
+ "(idx=%d)\n", idx);
+ break;
+ }
+ len = (sg_len > SATA_DWC_DMAC_CTRL_TSIZE_MAX) ?
+ SATA_DWC_DMAC_CTRL_TSIZE_MAX : sg_len;
+
+ offset = addr & 0xffff;
+ if ((offset + sg_len) > 0x10000)
+ len = 0x10000 - offset;
+
+ /*
+ * Make sure a LLI block is not created that will span
+ * 8K max FIS boundary. If the block spans such a FIS
+ * boundary, there is a chance that a DMA burst will
+ * cross that boundary -- this results in an error in
+ * the host controller.
+ */
+ if (unlikely(fis_len + len > 8192)) {
+ dev_dbg(dwc_dev, "SPLITTING: fis_len="
+ "%d(0x%x) len=%d(0x%x)\n", fis_len,
+ fis_len, len, len);
+ len = 8192 - fis_len;
+ fis_len = 0;
+ } else {
+ fis_len += len;
+ }
+ if (fis_len == 8192)
+ fis_len = 0;
+
+ /*
+ * Set DMA addresses and lower half of control register
+ * based on direction.
+ */
+ if (hsdevp->hsdev->hostID == APM_821XX_SATA) {
+ sms_val = 1+hsdevp->hsdev->dma_channel;
+ dms_val = 0;
+ } else {
+ sms_val = 0;
+ dms_val = 1+hsdevp->hsdev->dma_channel;
+ }
+
+ if (dir == DMA_FROM_DEVICE) {
+ lli[idx].dar = cpu_to_le32(addr);
+ lli[idx].sar = cpu_to_le32((u32)dmadr_addr);
+
+ lli[idx].ctl.low = cpu_to_le32(
+ DMA_CTL_TTFC(DMA_CTL_TTFC_P2M_DMAC) |
+ DMA_CTL_SMS(sms_val) |
+ DMA_CTL_DMS(dms_val) |
+ DMA_CTL_SRC_MSIZE(bl) |
+ DMA_CTL_DST_MSIZE(bl) |
+ DMA_CTL_SINC_NOCHANGE |
+ DMA_CTL_SRC_TRWID(2) |
+ DMA_CTL_DST_TRWID(2) |
+ DMA_CTL_INT_EN |
+ DMA_CTL_LLP_SRCEN |
+ DMA_CTL_LLP_DSTEN);
+ } else { /* DMA_TO_DEVICE */
+ lli[idx].sar = cpu_to_le32(addr);
+ lli[idx].dar = cpu_to_le32((u32)dmadr_addr);
+
+ lli[idx].ctl.low = cpu_to_le32(
+ DMA_CTL_TTFC(DMA_CTL_TTFC_M2P_PER) |
+ DMA_CTL_SMS(dms_val) |
+ DMA_CTL_DMS(sms_val) |
+ DMA_CTL_SRC_MSIZE(bl) |
+ DMA_CTL_DST_MSIZE(bl) |
+ DMA_CTL_DINC_NOCHANGE |
+ DMA_CTL_SRC_TRWID(2) |
+ DMA_CTL_DST_TRWID(2) |
+ DMA_CTL_INT_EN |
+ DMA_CTL_LLP_SRCEN |
+ DMA_CTL_LLP_DSTEN);
+ }
+
+ dev_dbg(dwc_dev, "%s setting ctl.high len: "
+ "0x%08x val: 0x%08x\n", __func__,
+ len, DMA_CTL_BLK_TS(len / 4));
+
+ /* Program the LLI CTL high register */
+ lli[idx].ctl.high = cpu_to_le32(DMA_CTL_BLK_TS\
+ (len / 4));
+
+ /* Program the next pointer. The next pointer must be
+ * the physical address, not the virtual address.
+ */
+ next_llp = (dma_lli + ((idx + 1) * sizeof(struct \
+ lli)));
+
+ /* The last 2 bits encode the list master select. */
+ if (hsdevp->hsdev->hostID == APM_460EX_SATA) {
+ next_llp = DMA_LLP_LMS(next_llp,
+ DMA_LLP_AHBMASTER2);
+ } else {
+ next_llp = DMA_LLP_LMS(next_llp,
+ DMA_LLP_AHBMASTER1);
+ }
+
+ lli[idx].llp = cpu_to_le32(next_llp);
+ idx++;
+ sg_len -= len;
+ addr += len;
+ }
+ }
+
+ /*
+ * The last next ptr has to be zero and the last control low register
+ * has to have LLP_SRC_EN and LLP_DST_EN (linked list pointer source
+ * and destination enable) set back to 0 (disabled.) This is what tells
+ * the core that this is the last item in the linked list.
+ */
+ if (likely(idx)) {
+ lli[idx-1].llp = 0x00000000;
+ lli[idx-1].ctl.low &= DMA_CTL_LLP_DISABLE_LE32;
+
+ /* Flush cache to memory */
+ dma_cache_sync(NULL, lli, (sizeof(struct lli) * idx),
+ DMA_BIDIRECTIONAL);
+ }
+
+ return idx;
+}
+
+/*
+ * Enables the DMA channel to start transferring data
+ */
+static void dwc_dma_xfer_start(int dma_ch)
+{
+ /* Enable the DMA channel */
+ out_le32(&(sata_dma_regs->dma_chan_en.low),
+ in_le32(&(sata_dma_regs->dma_chan_en.low)) |
+ DMA_ENABLE_CHAN(dma_ch));
+}
+
+
+static int dwc_dma_xfer_setup(struct ata_port *ap, dma_addr_t dma_lli)
+{
+ int dma_ch;
+ struct sata_dwc_device *hsdev = HSDEV_FROM_AP(ap);
+ /* Acquire DMA channel */
+ dma_ch = dma_request_channel(ap);
+ if (unlikely(dma_ch == -1)) {
+ dev_err(ap->dev, "%s: dma channel unavailable\n", __func__);
+ return -EAGAIN;
+ }
+
+ clear_chan_interrupts(dma_ch);
+
+ /* Program the CFG register. */
+ out_le32(&(sata_dma_regs->chan_regs[dma_ch].cfg.high),
+ DMA_CFG_HW_HS_SRC(dma_ch) | DMA_CFG_HW_HS_DEST(dma_ch) | \
+ DMA_CFG_PROTCTL | DMA_CFG_FCMOD_REQ);
+
+ out_le32(&(sata_dma_regs->chan_regs[dma_ch].cfg.low),
+ DMA_CFG_HW_CH_PRIOR(dma_ch));
+
+ /* Program the address of the linked list */
+ if (hsdev->hostID == APM_460EX_SATA) {
+ out_le32(&(sata_dma_regs->chan_regs[dma_ch].llp.low),
+ DMA_LLP_LMS(dma_lli, DMA_LLP_AHBMASTER2));
+ } else {
+ out_le32(&(sata_dma_regs->chan_regs[dma_ch].llp.low),
+ DMA_LLP_LMS(dma_lli, DMA_LLP_AHBMASTER1));
+ }
+
+ /* Program the CTL register with src enable / dst enable */
+ out_le32(&(sata_dma_regs->chan_regs[dma_ch].ctl.low),
+ DMA_CTL_LLP_SRCEN | DMA_CTL_LLP_DSTEN);
+ return dma_ch;
+}
+
+/*
+ * Initializes the SATA DMA driver
+ */
+static int dwc_dma_init(struct sata_dwc_device *hsdev, int irq)
+{
+ int err;
+
+ err = dma_request_interrupts(hsdev, irq);
+ if (err) {
+ dev_err(hsdev->dev, "%s: dma_request_interrupts returns %d\n",
+ __func__, err);
+ return err;
+ }
+
+ /* Enabe DMA support */
+ out_le32(&(sata_dma_regs->dma_cfg.low), DMA_EN);
+
+ dev_notice(hsdev->dev, "DMA initialized\n");
+ dev_dbg(hsdev->dev, "SATA DMA registers=0x%p\n", sata_dma_regs);
+
+ return 0;
+}
+
+static int sata_dwc_scr_read(struct ata_link *link, unsigned int scr, u32 *val)
+{
+ if (unlikely(scr > SCR_NOTIFICATION)) {
+ dev_err(link->ap->dev, "%s: Incorrect SCR offset 0x%02x\n",
+ __func__, scr);
+ return -EINVAL;
+ }
+
+ *val = in_le32((void *)link->ap->ioaddr.scr_addr + (scr * 4));
+ dev_dbg(link->ap->dev, "%s: id=%d reg=%d val=0x%08x\n",
+ __func__, link->ap->print_id, scr, *val);
+
+ return 0;
+}
+
+static int sata_dwc_scr_write(struct ata_link *link, unsigned int scr, u32 val)
+{
+ dev_dbg(link->ap->dev, "%s: id=%d reg=%d val=val=0x%08x\n",
+ __func__, link->ap->print_id, scr, val);
+ if (unlikely(scr > SCR_NOTIFICATION)) {
+ dev_err(link->ap->dev, "%s: Incorrect SCR offset 0x%02x\n",
+ __func__, scr);
+ return -EINVAL;
+ }
+ out_le32((void *)link->ap->ioaddr.scr_addr + (scr * 4), val);
+
+ return 0;
+}
+
+static u32 core_scr_read(struct ata_port *ap, unsigned int scr)
+{
+ struct sata_dwc_device *hsdev = HSDEV_FROM_AP(ap);
+ return in_le32((void __iomem *)hsdev->scr_base + (scr * 4));
+}
+
+
+static void core_scr_write(struct ata_port *ap, unsigned int scr, u32 val)
+{
+ struct sata_dwc_device *hsdev = HSDEV_FROM_AP(ap);
+ out_le32((void __iomem *)hsdev->scr_base + (scr * 4), val);
+}
+
+static void clear_serror(struct ata_port *ap)
+{
+ struct sata_dwc_device *hsdev = HSDEV_FROM_AP(ap);
+ out_le32((void __iomem *)hsdev->scr_base + 4,
+ in_le32((void __iomem *)hsdev->scr_base + 4));
+
+}
+
+static void clear_interrupt_bit(struct sata_dwc_device *hsdev, u32 bit)
+{
+ out_le32(&hsdev->sata_dwc_regs->intpr,
+ in_le32(&hsdev->sata_dwc_regs->intpr));
+}
+
+/*
+ * Porting the ata_bus_softreset function from the libata-sff.c library.
+ */
+static int sata_dwc_bus_softreset(struct ata_port *ap, unsigned int devmask,
+ unsigned long deadline)
+{
+ struct ata_ioports *ioaddr = &ap->ioaddr;
+
+ DPRINTK("ata%u: bus reset via SRST\n", ap->print_id);
+
+ /* Software reset. causes dev0 to be selected */
+ iowrite8(ap->ctl, ioaddr->ctl_addr);
+ udelay(20); /* FIXME: flush */
+ iowrite8(ap->ctl | ATA_SRST, ioaddr->ctl_addr);
+ udelay(20); /* FIXME: flush */
+ iowrite8(ap->ctl, ioaddr->ctl_addr);
+ ap->last_ctl = ap->ctl;
+
+ /* Wait the port to become ready */
+ return ata_sff_wait_after_reset(&ap->link, devmask, deadline);
+}
+
+/*
+ * Do soft reset on the current SATA link.
+ */
+static int sata_dwc_softreset(struct ata_link *link, unsigned int *classes,
+ unsigned long deadline)
+{
+ int rc;
+ u8 err;
+ struct ata_port *ap = link->ap;
+ unsigned int devmask = 0;
+ struct sata_dwc_device *hsdev = HSDEV_FROM_AP(ap);
+
+ /* Select device 0 again */
+ ap->ops->sff_dev_select(ap, 0);
+
+ DPRINTK("about to softreset, devmask=%x\n", devmask);
+ rc = sata_dwc_bus_softreset(ap, devmask, deadline);
+
+ /* If link is occupied, -ENODEV too is an error */
+ if (rc && (rc != -ENODEV || sata_scr_valid(link))) {
+ ata_link_printk(link, KERN_ERR, "SRST failed(errno=%d)\n", rc);
+ return rc;
+ }
+
+ /* Determine by signature whether we have ATA or ATAPI devices */
+ classes[0] = ata_sff_dev_classify(&link->device[0],
+ devmask & (1 << 0), &err);
+
+ DPRINTK("EXIT, classes[0]=%u [1]=%u\n", classes[0], classes[1]);
+ clear_serror(link->ap);
+
+ /* Terminate DMA if it is currently in use */
+ sata_dwc_dma_terminate(link->ap, hsdev->dma_channel);
+
+ return rc;
+}
+
+/*
+ * Reset all internal parameters to default value.
+ * This function should be called in hardreset
+ */
+static void dwc_reset_internal_params(struct ata_port *ap)
+{
+ struct sata_dwc_device_port *hsdevp = HSDEVP_FROM_AP(ap);
+ int tag;
+ for (tag = 0; tag < SATA_DWC_QCMD_MAX; tag++)
+ hsdevp->dma_pending[tag] = SATA_DWC_DMA_PENDING_NONE;
+
+ hsdevp->sata_dwc_sactive_issued = 0;
+ hsdevp->sata_dwc_sactive_queued = 0;
+}
+
+static int sata_dwc_hardreset(struct ata_link *link, unsigned int *classes,
+ unsigned long deadline)
+{
+ int rc;
+ const unsigned long *timing = sata_ehc_deb_timing(&link->eh_context);
+ bool online;
+
+ /* Reset internal parameters to default values */
+ dwc_reset_internal_params(link->ap);
+
+ /* Call standard hard reset */
+ rc = sata_link_hardreset(link, timing, deadline, &online, NULL);
+
+ /* Reconfigure the port after hard reset */
+ if (ata_link_online(link))
+ sata_dwc_init_port(link->ap);
+
+ return online ? -EAGAIN : rc;
+}
+
+static u32 qcmd_tag_to_mask(u8 tag)
+{
+ return 0x00000001 << (tag & 0x1f);
+}
+
+static void sata_dwc_error_intr(struct ata_port *ap,
+ struct sata_dwc_device *hsdev, uint intpr)
+{
+ struct sata_dwc_device_port *hsdevp = HSDEVP_FROM_AP(ap);
+ struct ata_eh_info *ehi = &ap->link.eh_info;
+ unsigned int err_mask = 0, action = 0;
+ struct ata_queued_cmd *qc;
+ u32 serror;
+ u8 status, tag;
+ u32 err_reg;
+
+ ata_ehi_clear_desc(ehi);
+
+ serror = core_scr_read(ap, SCR_ERROR);
+ status = ap->ops->sff_check_status(ap);
+
+ err_reg = in_le32(&(sata_dma_regs->interrupt_status.error.low));
+ tag = ap->link.active_tag;
+
+ dev_err(ap->dev, "%s SCR_ERROR=0x%08x intpr=0x%08x status=0x%08x "
+ " pending=%d dma_err_status=0x%08x\n",
+ __func__, serror, intpr, status, hsdevp->dma_pending[tag],
+ err_reg);
+
+ /* Clear error register and interrupt bit */
+ clear_serror(ap);
+ clear_interrupt_bit(hsdev, SATA_DWC_INTPR_ERR);
+
+ /* This is the only error happening now. TODO check for exact error */
+ err_mask |= AC_ERR_HOST_BUS;
+ action |= ATA_EH_RESET;
+
+ /* Pass this on to EH */
+ ehi->serror |= serror;
+ ehi->action |= action;
+
+ qc = ata_qc_from_tag(ap, tag);
+ if (qc)
+ qc->err_mask |= err_mask;
+ else
+ ehi->err_mask |= err_mask;
+
+ ata_port_abort(ap);
+}
+
+/*
+ * This Interrupt handler called via port ops registered function.
+ */
+static irqreturn_t sata_dwc_isr(int irq, void *dev_instance)
+{
+ struct ata_host *host = (struct ata_host *)dev_instance;
+ struct sata_dwc_device *hsdev = HSDEV_FROM_HOST(host);
+ struct ata_port *ap;
+ struct ata_queued_cmd *qc;
+ u8 status, tag;
+ int handled, port = 0;
+ int num_lli;
+ uint intpr, sactive, tag_mask;
+ struct sata_dwc_device_port *hsdevp;
+ u32 mask;
+
+ spin_lock(&host->lock);
+
+ /* Read the interrupt register */
+ intpr = in_le32(&hsdev->sata_dwc_regs->intpr);
+
+ ap = host->ports[port];
+ hsdevp = HSDEVP_FROM_AP(ap);
+
+ dev_dbg(ap->dev, "%s intpr=0x%08x active_tag=%d\n", __func__, intpr,
+ ap->link.active_tag);
+
+ /* Check for error interrupt */
+ if (intpr & SATA_DWC_INTPR_ERR) {
+ sata_dwc_error_intr(ap, hsdev, intpr);
+ handled = 1;
+ goto DONE;
+ }
+
+ /* Check for DMA SETUP FIS (FP DMA) interrupt */
+ if (intpr & SATA_DWC_INTPR_NEWFP) {
+ clear_interrupt_bit(hsdev, SATA_DWC_INTPR_NEWFP);
+ if (ap->qc_allocated == 0x0) {
+ handled = 1;
+ goto DONE;
+ }
+
+ tag = (u8)(in_le32(&hsdev->sata_dwc_regs->fptagr));
+ mask = qcmd_tag_to_mask(tag);
+ dev_dbg(ap->dev, "%s: NEWFP tag=%d\n", __func__, tag);
+ if ((hsdevp->sata_dwc_sactive_queued & mask) == 0)
+ dev_warn(ap->dev, "CMD tag=%d not pending?\n", tag);
+
+ qc = ata_qc_from_tag(ap, tag);
+ /*
+ * Start FP DMA for NCQ command. At this point the tag is the
+ * active tag. It is the tag that matches the command about to
+ * be completed.
+ */
+ if (qc) {
+ hsdevp->sata_dwc_sactive_issued |= mask;
+ /* Prevent to issue more commands */
+ qc->ap->link.active_tag = tag;
+ qc->dev->link->sactive |= (1 << qc->tag);
+ num_lli = map_sg_to_lli(ap, qc->sg, qc->n_elem, \
+ hsdevp->llit[tag], hsdevp->llit_dma[tag], \
+ (void *__iomem)(&hsdev->sata_dwc_regs->dmadr), \
+ qc->dma_dir);
+ sata_dwc_bmdma_start_by_tag(qc, tag);
+ wmb();
+ qc->ap->hsm_task_state = HSM_ST_LAST;
+ } else {
+ hsdevp->sata_dwc_sactive_issued &= ~mask;
+ dev_warn(ap->dev, "No QC available for tag %d (intpr="
+ "0x%08x, qc_allocated=0x%08x, qc_active=0x%08x)\n", tag,\
+ intpr, ap->qc_allocated, ap->qc_active);
+ }
+
+ handled = 1;
+ goto DONE;
+ }
+
+ sactive = core_scr_read(ap, SCR_ACTIVE);
+ tag_mask = (hsdevp->sata_dwc_sactive_issued | sactive) ^ sactive;
+
+ /*
+ * If no sactive issued and tag_mask is zero then this is not NCQ.
+ * Do actions for transfer completion interrupt.
+ */
+ if (hsdevp->sata_dwc_sactive_issued == 0 && tag_mask == 0) {
+ if (ap->link.active_tag == ATA_TAG_POISON)
+ tag = 0;
+ else
+ tag = ap->link.active_tag;
+ qc = ata_qc_from_tag(ap, tag);
+
+ /* Device interrupt without active qc? */
+ if (unlikely(!qc || (qc->tf.flags & ATA_TFLAG_POLLING))) {
+ dev_err(ap->dev, "%s interrupt with no active qc "
+ "qc=%p\n", __func__, qc);
+ ap->ops->sff_check_status(ap);
+ handled = 1;
+ goto DONE;
+ }
+ /* Get current status and clear interrupt */
+ status = ap->ops->sff_check_status(ap);
+
+ if (status & ATA_ERR) {
+ dev_dbg(ap->dev, "interrupt ATA_ERR (0x%x)\n", status);
+ sata_dwc_qc_complete(ap, qc, 1);
+ handled = 1;
+ goto DONE;
+ }
+
+ dev_dbg(ap->dev, "%s non-NCQ cmd interrupt, protocol: %s\n",
+ __func__, get_prot_descript(qc->tf.protocol));
+DRVSTILLBUSY:
+ /* Do complete action for the current QC */
+ if (ata_is_dma(qc->tf.protocol)) {
+ sata_dwc_qc_complete(ap, qc, 1);
+ } else if ((ata_is_pio(qc->tf.protocol)) ||
+ (ata_is_nodata(qc->tf.protocol))) {
+ ata_sff_hsm_move(ap, qc, status, 0);
+ } else {
+ if (unlikely(sata_dwc_qc_complete(ap, qc, 1)))
+ goto DRVSTILLBUSY;
+ }
+
+ handled = 1;
+ goto DONE;
+ }
+
+ /*
+ * This is a NCQ command. At this point we need to figure out for which
+ * tags we have gotten a completion interrupt. One interrupt may serve
+ * as completion for more than one operation when commands are queued
+ * (NCQ). We need to process each completed command.
+ */
+ if (sactive != 0 || hsdevp->sata_dwc_sactive_issued > 1 || \
+ tag_mask > 1) {
+ dev_dbg(ap->dev, "%s NCQ:sactive=0x%08x sactive_issued=0x%08x"
+ "tag_mask=0x%08x\n", __func__, sactive,
+ hsdevp->sata_dwc_sactive_issued, tag_mask);
+ }
+
+ if (unlikely((tag_mask | hsdevp->sata_dwc_sactive_issued) != \
+ hsdevp->sata_dwc_sactive_issued)) {
+ dev_warn(ap->dev, "Bad tag mask? sactive=0x%08x "
+ "sata_dwc_sactive_issued=0x%08x tag_mask"
+ "=0x%08x\n", sactive, hsdevp->sata_dwc_sactive_issued,
+ tag_mask);
+ }
+
+ /* Read just to clear ... not bad if currently still busy */
+ status = ap->ops->sff_check_status(ap);
+ dev_dbg(ap->dev, "%s ATA status register=0x%x\n", __func__, status);
+
+ for (tag = 0; tag < 32; tag++) {
+ if (tag_mask & qcmd_tag_to_mask(tag)) {
+ qc = ata_qc_from_tag(ap, tag);
+ if (!qc) {
+ dev_info(ap->dev, "error: Tag %d is set but " \
+ "not available\n", tag);
+ continue;
+ }
+ sata_dwc_qc_complete(ap, qc, 1);
+ }
+ }
+ handled = 1;
+
+DONE:
+ spin_unlock(&host->lock);
+ return IRQ_RETVAL(handled);
+}
+
+static void sata_dwc_clear_dmacr(struct sata_dwc_device_port *hsdevp, u8 tag)
+{
+ struct sata_dwc_device *hsdev = HSDEV_FROM_HSDEVP(hsdevp);
+
+ if (hsdevp->dma_pending[tag] == SATA_DWC_DMA_PENDING_RX) {
+ out_le32(&(hsdev->sata_dwc_regs->dmacr),
+ SATA_DWC_DMACR_RX_CLEAR(
+ in_le32(&(hsdev->sata_dwc_regs->dmacr))));
+ } else if (hsdevp->dma_pending[tag] == SATA_DWC_DMA_PENDING_TX) {
+ out_le32(&(hsdev->sata_dwc_regs->dmacr),
+ SATA_DWC_DMACR_TX_CLEAR(
+ in_le32(&(hsdev->sata_dwc_regs->dmacr))));
+ } else {
+ /*
+ * This should not happen, it indicates the driver is out of
+ * sync. If it does happen, clear dmacr anyway.
+ */
+ dev_err(hsdev->dev, "%s DMA protocol RX and"
+ " TX DMA not pending tag=0x%02x pending=%d"
+ " dmacr: 0x%08x\n", __func__, tag,
+ hsdevp->dma_pending[tag],
+ in_le32(&(hsdev->sata_dwc_regs->dmacr)));
+ out_le32(&(hsdev->sata_dwc_regs->dmacr),
+ SATA_DWC_DMACR_TXRXCH_CLEAR);
+ }
+}
+
+
+static int sata_dwc_qc_complete(struct ata_port *ap, struct ata_queued_cmd *qc,
+ u32 check_status)
+{
+ u8 status;
+ int i;
+ u8 tag = qc->tag;
+ struct sata_dwc_device_port *hsdevp = HSDEVP_FROM_AP(ap);
+ u32 serror;
+ dev_dbg(ap->dev, "%s checkstatus? %x\n", __func__, check_status);
+
+ /* Check main status, clearing INTRQ */
+ status = ap->ops->sff_check_status(ap);
+
+ if (check_status) {
+ i = 0;
+ while (status & ATA_BUSY) {
+ if (++i > 10)
+ break;
+ status = ap->ops->sff_check_altstatus(ap);
+ };
+
+ if (unlikely(status & ATA_BUSY))
+ dev_err(ap->dev, "QC complete cmd=0x%02x STATUS BUSY "
+ "(0x%02x) [%d]\n", qc->tf.command, status, i);
+ serror = core_scr_read(ap, SCR_ERROR);
+ if (unlikely(serror & SATA_DWC_SERROR_ERR_BITS))
+ dev_err(ap->dev, "****** SERROR=0x%08x ******\n",
+ serror);
+ }
+ dev_dbg(ap->dev, "QC complete cmd=0x%02x status=0x%02x ata%u:"
+ " protocol=%d\n", qc->tf.command, status, ap->print_id,
+ qc->tf.protocol);
+
+ hsdevp->sata_dwc_sactive_issued &= ~qcmd_tag_to_mask(tag);
+
+ /* Complete taskfile transaction (does not read SCR registers) */
+ if (ata_is_atapi(qc->tf.protocol))
+ ata_sff_hsm_move(ap, qc, status, 0);
+ else
+ ata_qc_complete(qc);
+
+ if (hsdevp->sata_dwc_sactive_queued == 0)
+ ap->link.active_tag = ATA_TAG_POISON;
+
+ return 0;
+}
+
+static void sata_dwc_enable_interrupts(struct sata_dwc_device *hsdev)
+{
+ /* Enable selective interrupts by setting the interrupt maskregister*/
+ out_le32(&hsdev->sata_dwc_regs->intmr,
+ SATA_DWC_INTMR_ERRM |
+ SATA_DWC_INTMR_NEWFPM |
+ SATA_DWC_INTMR_PMABRTM |
+ SATA_DWC_INTMR_DMATM |
+ SATA_DWC_INTPR_IPF);
+ /*
+ * Unmask the error bits that should trigger an error interrupt by
+ * setting the error mask register.
+ */
+ out_le32(&hsdev->sata_dwc_regs->errmr, SATA_DWC_SERROR_ERR_BITS);
+
+ dev_dbg(hsdev->dev, "%s: INTMR = 0x%08x, ERRMR = 0x%08x\n",
+ __func__, in_le32(&hsdev->sata_dwc_regs->intmr),
+ in_le32(&hsdev->sata_dwc_regs->errmr));
+}
+
+static void sata_dwc_init_port(struct ata_port *ap)
+{
+ struct sata_dwc_device *hsdev = HSDEV_FROM_AP(ap);
+
+ /* Configure DMA */
+ if (ap->port_no == 0) {
+ dev_dbg(ap->dev, "%s: clearing TXCHEN, RXCHEN in DMAC\n",
+ __func__);
+
+ /* Clear all transmit/receive bits */
+ out_le32(&hsdev->sata_dwc_regs->dmacr,
+ SATA_DWC_DMACR_TXRXCH_CLEAR);
+
+ dev_dbg(ap->dev, "%s: setting burst size DBTSR\n", __func__);
+ out_le32(&hsdev->sata_dwc_regs->dbtsr,
+ (SATA_DWC_DBTSR_MWR(AHB_DMA_BRST_DFLT) |
+ SATA_DWC_DBTSR_MRD(AHB_DMA_BRST_DFLT)));
+ }
+
+ /* Enable interrupts */
+ sata_dwc_enable_interrupts(hsdev);
+}
+
+static void sata_dwc_setup_port(struct ata_ioports *port, unsigned long base)
+{
+ port->cmd_addr = (void *)base + 0x00;
+ port->data_addr = (void *)base + 0x00;
+
+ port->error_addr = (void *)base + 0x04;
+ port->feature_addr = (void *)base + 0x04;
+
+ port->nsect_addr = (void *)base + 0x08;
+
+ port->lbal_addr = (void *)base + 0x0c;
+ port->lbam_addr = (void *)base + 0x10;
+ port->lbah_addr = (void *)base + 0x14;
+
+ port->device_addr = (void *)base + 0x18;
+ port->command_addr = (void *)base + 0x1c;
+ port->status_addr = (void *)base + 0x1c;
+
+ port->altstatus_addr = (void *)base + 0x20;
+ port->ctl_addr = (void *)base + 0x20;
+}
+
+/*
+ * Allocates the scatter gather LLI table for AHB DMA
+ */
+static int sata_dwc_port_start(struct ata_port *ap)
+{
+ int err = 0;
+ struct sata_dwc_device *hsdev;
+ struct sata_dwc_device_port *hsdevp = NULL;
+ struct device *pdev;
+ u32 sstatus;
+ int i;
+
+ hsdev = HSDEV_FROM_AP(ap);
+
+ dev_dbg(ap->dev, "%s: port_no=%d\n", __func__, ap->port_no);
+
+ hsdev->host = ap->host;
+ pdev = ap->host->dev;
+ if (!pdev) {
+ dev_err(ap->dev, "%s: no ap->host->dev\n", __func__);
+ err = -ENODEV;
+ goto CLEANUP;
+ }
+
+ /* Allocate Port Struct */
+ hsdevp = kzalloc(sizeof(*hsdevp), GFP_KERNEL);
+ if (!hsdevp) {
+ dev_err(ap->dev, "%s: kmalloc failed for hsdevp\n", __func__);
+ err = -ENOMEM;
+ goto CLEANUP;
+ }
+ memset(hsdevp, 0, sizeof(*hsdevp));
+ hsdevp->hsdev = hsdev;
+
+ ap->bmdma_prd = 0; /* set these so libata doesn't use them */
+ ap->bmdma_prd_dma = 0;
+
+ /*
+ * DMA - Assign scatter gather LLI table. We can't use the libata
+ * version since it's PRD is IDE PCI specific.
+ */
+ for (i = 0; i < SATA_DWC_QCMD_MAX; i++) {
+ hsdevp->llit[i] = dma_alloc_coherent(pdev,
+ SATA_DWC_DMAC_LLI_TBL_SZ,
+ &(hsdevp->llit_dma[i]),
+ GFP_ATOMIC);
+ if (!hsdevp->llit[i]) {
+ dev_err(ap->dev, "%s: dma_alloc_coherent failed\n",
+ __func__);
+ err = -ENOMEM;
+ goto CLEANUP;
+ }
+ }
+
+ if (ap->port_no == 0) {
+ dev_dbg(ap->dev, "%s: clearing TXCHEN, RXCHEN in DMAC\n",
+ __func__);
+ out_le32(&hsdev->sata_dwc_regs->dmacr,
+ SATA_DWC_DMACR_TXRXCH_CLEAR);
+
+ dev_dbg(ap->dev, "%s: setting burst size in DBTSR\n",
+ __func__);
+ out_le32(&hsdev->sata_dwc_regs->dbtsr,
+ (SATA_DWC_DBTSR_MWR(AHB_DMA_BRST_DFLT) |
+ SATA_DWC_DBTSR_MRD(AHB_DMA_BRST_DFLT)));
+ }
+
+ /* Clear any error bits before libata starts issuing commands */
+ clear_serror(ap);
+ ap->private_data = hsdevp;
+
+ /* Are we in Gen I or II */
+ sstatus = core_scr_read(ap, SCR_STATUS);
+ switch (SATA_DWC_SCR0_SPD_GET(sstatus)) {
+ case 0x0:
+ dev_info(ap->dev, "**** No neg speed (nothing attached?)\n");
+ break;
+ case 0x1:
+ dev_info(ap->dev, "**** GEN I speed rate negotiated\n");
+ break;
+ case 0x2:
+ dev_info(ap->dev, "**** GEN II speed rate negotiated\n");
+ break;
+ }
+
+ dev_dbg(ap->dev, "%s: done\n", __func__);
+ return 0;
+
+CLEANUP:
+ sata_dwc_port_stop(ap);
+ kfree(hsdevp);
+ dev_dbg(ap->dev, "%s: fail\n", __func__);
+
+ return err;
+}
+
+static void sata_dwc_port_stop(struct ata_port *ap)
+{
+ int i;
+ struct sata_dwc_device_port *hsdevp = HSDEVP_FROM_AP(ap);
+
+ dev_dbg(ap->dev, "%s: ap->id = %d\n", __func__, ap->print_id);
+
+ if (hsdevp) {
+ /* De-allocate LLI table */
+ for (i = 0; i < SATA_DWC_QCMD_MAX; i++) {
+ dma_free_coherent(ap->host->dev,
+ SATA_DWC_DMAC_LLI_TBL_SZ,
+ hsdevp->llit[i], hsdevp->llit_dma[i]);
+ }
+
+ kfree(hsdevp);
+ }
+ ap->private_data = NULL;
+}
+
+/*
+ * As our SATA is master only, no dev_select function needed.
+ * This just overwrite the ata_sff_dev_select() function in
+ * libata-sff
+ */
+void sata_dwc_dev_select(struct ata_port *ap, unsigned int device)
+{
+ ndelay(100);
+}
+
+/**
+ * Filter ATAPI cmds which are unsuitable for DMA.
+ *
+ * The bmdma engines cannot handle speculative data sizes
+ * (bytecount under/over flow). So only allow DMA for
+ * data transfer commands with known data sizes.
+ */
+static int sata_dwc_check_atapi_dma(struct ata_queued_cmd *qc)
+{
+ struct scsi_cmnd *scmd = qc->scsicmd;
+ int pio = 1; /* ATAPI DMA disabled by default */
+ unsigned int lba;
+
+ if (scmd) {
+ switch (scmd->cmnd[0]) {
+ case WRITE_6:
+ case WRITE_10:
+ case WRITE_12:
+ case READ_6:
+ case READ_10:
+ case READ_12:
+ pio = 0; /* DMA is safe */
+ break;
+ }
+
+ /* Command WRITE_10 with LBA between -45150 (FFFF4FA2)
+ * and -1 (FFFFFFFF) shall use PIO mode */
+ if (scmd->cmnd[0] == WRITE_10) {
+ lba = (scmd->cmnd[2] << 24) |
+ (scmd->cmnd[3] << 16) |
+ (scmd->cmnd[4] << 8) |
+ scmd->cmnd[5];
+ if (lba >= 0xFFFF4FA2)
+ pio = 1;
+ }
+ /*
+ * WORK AROUND: Fix DMA issue when blank CD/DVD disc
+ * in the drive and user use the 'fdisk -l' command.
+ * No DMA data returned so we can not complete the QC.
+ */
+ if (scmd->cmnd[0] == READ_10) {
+ lba = (scmd->cmnd[2] << 24) |
+ (scmd->cmnd[3] << 16) |
+ (scmd->cmnd[4] << 8) |
+ scmd->cmnd[5];
+ if (lba < 0x20)
+ pio = 1;
+ }
+ }
+ dev_dbg(qc->ap->dev, "%s - using %s mode for command cmd=0x%02x\n", \
+ __func__, (pio ? "PIO" : "DMA"), scmd->cmnd[0]);
+ return pio;
+}
+
+/*
+ * Keeps track of individual command tag ids and calls ata_exec_command
+ * in libata
+ */
+static void sata_dwc_exec_command_by_tag(struct ata_port *ap,
+ struct ata_taskfile *tf,
+ u8 tag)
+{
+ unsigned long flags;
+ struct sata_dwc_device_port *hsdevp = HSDEVP_FROM_AP(ap);
+
+ dev_dbg(ap->dev, "%s cmd(0x%02x): %s tag=%d\n", __func__, tf->command,
+ ata_get_cmd_descript(tf->command), tag);
+
+ spin_lock_irqsave(&ap->host->lock, flags);
+ hsdevp->sata_dwc_sactive_queued |= qcmd_tag_to_mask(tag);
+ spin_unlock_irqrestore(&ap->host->lock, flags);
+ /*
+ * Clear SError before executing a new command.
+ * sata_dwc_scr_write and read can not be used here. Clearing the PM
+ * managed SError register for the disk needs to be done before the
+ * task file is loaded.
+ */
+ clear_serror(ap);
+ ata_sff_exec_command(ap, tf);
+}
+
+
+static void sata_dwc_bmdma_setup(struct ata_queued_cmd *qc)
+{
+ u8 tag = qc->tag;
+
+ if (ata_is_ncq(qc->tf.protocol)) {
+ dev_dbg(qc->ap->dev, "%s: ap->link.sactive=0x%08x tag=%d\n",
+ __func__, qc->ap->link.sactive, tag);
+ } else {
+ tag = 0;
+ }
+
+ sata_dwc_exec_command_by_tag(qc->ap, &qc->tf, tag);
+}
+
+static void sata_dwc_bmdma_start_by_tag(struct ata_queued_cmd *qc, u8 tag)
+{
+ int start_dma;
+ u32 reg, dma_chan;
+ struct sata_dwc_device *hsdev = HSDEV_FROM_QC(qc);
+ struct ata_port *ap = qc->ap;
+ struct sata_dwc_device_port *hsdevp = HSDEVP_FROM_AP(ap);
+ int dir = qc->dma_dir;
+
+ /* Configure DMA before starting data transfer */
+ dma_chan = dwc_dma_xfer_setup(ap, hsdevp->llit_dma[tag]);
+ if (unlikely(dma_chan < 0)) {
+ dev_err(ap->dev, "%s: dma channel unavailable\n", __func__);
+ /* Offending this QC as no channel available for transfer */
+ qc->err_mask |= AC_ERR_TIMEOUT;
+ return;
+ }
+
+ /* Check if DMA should be started */
+ hsdevp->dma_chan[tag] = dma_chan;
+ if (hsdevp->sata_dwc_sactive_queued & qcmd_tag_to_mask(tag)) {
+ start_dma = 1;
+ if (dir == DMA_TO_DEVICE)
+ hsdevp->dma_pending[tag] = SATA_DWC_DMA_PENDING_TX;
+ else
+ hsdevp->dma_pending[tag] = SATA_DWC_DMA_PENDING_RX;
+ } else {
+ dev_err(ap->dev, "%s: No pending cmd at tag %d\n",
+ __func__, tag);
+ start_dma = 0;
+ }
+
+ dev_dbg(ap->dev, "%s qc=%p tag: %x cmd: 0x%02x dma_dir: %s "
+ "start_dma? %x\n", __func__, qc, tag, qc->tf.command,
+ get_dma_dir_descript(qc->dma_dir), start_dma);
+ sata_dwc_tf_dump(hsdev->dev, &(qc->tf));
+
+ /* Enable to start DMA transfer */
+ if (start_dma) {
+ reg = core_scr_read(ap, SCR_ERROR);
+ if (unlikely(reg & SATA_DWC_SERROR_ERR_BITS)) {
+ dev_err(ap->dev, "%s: ****** SError=0x%08x ******\n",
+ __func__, reg);
+ }
+
+ if (dir == DMA_TO_DEVICE) {
+ out_le32(&hsdev->sata_dwc_regs->dmacr,
+ SATA_DWC_DMACR_TXCHEN);
+ } else {
+ out_le32(&hsdev->sata_dwc_regs->dmacr,
+ SATA_DWC_DMACR_RXCHEN);
+ }
+
+ /* Enable AHB DMA transfer on the specified channel */
+ dwc_dma_xfer_start(dma_chan);
+ hsdevp->sata_dwc_sactive_queued &= ~qcmd_tag_to_mask(tag);
+ }
+}
+
+static void sata_dwc_bmdma_start(struct ata_queued_cmd *qc)
+{
+ u8 tag = qc->tag;
+
+ if (ata_is_ncq(qc->tf.protocol)) {
+ dev_dbg(qc->ap->dev, "%s: ap->link.sactive=0x%08x tag=%d\n",
+ __func__, qc->ap->link.sactive, tag);
+ } else {
+ tag = 0;
+ }
+ dev_dbg(qc->ap->dev, "%s\n", __func__);
+ sata_dwc_bmdma_start_by_tag(qc, tag);
+}
+
+static u8 sata_dwc_dma_status(struct ata_port *ap)
+{
+ u32 status = 0;
+ u32 tfr_reg, err_reg;
+ struct sata_dwc_device *hsdev = HSDEV_FROM_AP(ap);
+
+ /* Check DMA register for status */
+ tfr_reg = in_le32(&(sata_dma_regs->interrupt_status.tfr.low));
+ err_reg = in_le32(&(sata_dma_regs->interrupt_status.error.low));
+
+ if (unlikely(err_reg & DMA_CHANNEL(hsdev->dma_channel)))
+ status = ATA_DMA_ERR | ATA_DMA_INTR;
+ else if (tfr_reg & DMA_CHANNEL(hsdev->dma_channel))
+ status = ATA_DMA_INTR;
+ return status;
+}
+
+/*
+ * Prepare for a particular queued command based on tag
+ */
+static void sata_dwc_qc_prep_by_tag(struct ata_queued_cmd *qc, u8 tag)
+{
+ struct scatterlist *sg = qc->sg;
+ struct ata_port *ap = qc->ap;
+ int num_lli;
+ struct sata_dwc_device *hsdev = HSDEV_FROM_AP(ap);
+ struct sata_dwc_device_port *hsdevp = HSDEVP_FROM_AP(ap);
+
+ if ((qc->dma_dir == DMA_NONE) || (qc->tf.protocol == ATA_PROT_PIO))
+ return;
+ dev_dbg(ap->dev, "%s: port=%d dma dir=%s n_elem=%d\n",
+ __func__, ap->port_no, get_dma_dir_descript(qc->dma_dir),
+ qc->n_elem);
+
+ if (!ata_is_ncq(qc->tf.protocol)) {
+ num_lli = map_sg_to_lli(qc->ap, sg, qc->n_elem,
+ hsdevp->llit[tag], hsdevp->llit_dma[tag],
+ (void *__iomem)(&hsdev->sata_dwc_regs->dmadr),
+ qc->dma_dir);
+ }
+}
+
+int sata_dwc_qc_defer(struct ata_queued_cmd *qc)
+{
+ struct ata_port *ap = qc->ap;
+ struct sata_dwc_device_port *hsdevp = HSDEVP_FROM_AP(ap);
+ u8 status;
+ int ret;
+
+ dev_dbg(qc->ap->dev, "%s -\n", __func__);
+ ret = ata_std_qc_defer(qc);
+ if (ret) {
+ printk(KERN_DEBUG "STD Defer %s cmd %s tag=%d\n",
+ (ret == ATA_DEFER_LINK) ? "LINK" : "PORT",
+ ata_get_cmd_descript(qc->tf.command), qc->tag);
+ return ret;
+ }
+
+ /* Check the SATA host for busy status */
+ if (ata_is_ncq(qc->tf.protocol)) {
+ status = ap->ops->sff_check_altstatus(ap);
+ if (status & ATA_BUSY) {
+ dev_dbg(ap->dev,
+ "Defer PORT cmd %s tag=%d as host is busy\n",
+ ata_get_cmd_descript(qc->tf.command), qc->tag);
+ return ATA_DEFER_PORT;/*HOST BUSY*/
+ }
+
+ /* This will prevent collision error */
+ if (hsdevp->sata_dwc_sactive_issued) {
+ dev_dbg(ap->dev, "Defer PORT cmd %s with tag %d " \
+ "because another dma xfer is outstanding\n",
+ ata_get_cmd_descript(qc->tf.command), qc->tag);
+
+ return ATA_DEFER_PORT;/*DEVICE&HOST BUSY*/
+ }
+
+ }
+
+ return 0;
+}
+
+void sata_dwc_exec_command(struct ata_port *ap, const struct ata_taskfile *tf)
+{
+ iowrite8(tf->command, ap->ioaddr.command_addr);
+ /* If we have an mmio device with no ctl and no altstatus
+ * method, this will fail. No such devices are known to exist.
+ */
+ if (ap->ioaddr.altstatus_addr)
+ ioread8(ap->ioaddr.altstatus_addr);
+
+ ndelay(400);
+}
+
+static unsigned int sata_dwc_qc_issue(struct ata_queued_cmd *qc)
+{
+ u32 sactive;
+ u8 tag = qc->tag;
+ struct ata_port *ap = qc->ap;
+ struct sata_dwc_device_port *hsdevp = HSDEVP_FROM_AP(qc->ap);
+ u8 status;
+
+#ifdef DEBUG_NCQ
+ if (qc->tag > 0 || ap->link.sactive > 1)
+ dev_info(ap->dev, "%s ap id=%d cmd(0x%02x)=%s qc tag=%d "
+ "prot=%s ap active_tag=0x%08x ap sactive=0x%08x\n",
+ __func__, ap->print_id, qc->tf.command,
+ ata_get_cmd_descript(qc->tf.command),
+ qc->tag, get_prot_descript(qc->tf.protocol),
+ ap->link.active_tag, ap->link.sactive);
+#endif
+
+ if (!ata_is_ncq(qc->tf.protocol))
+ tag = 0;
+ sata_dwc_qc_prep_by_tag(qc, tag);
+
+ if (ata_is_ncq(qc->tf.protocol)) {
+ status = ap->ops->sff_check_altstatus(ap);
+ if (status & ATA_BUSY) {
+ /* Ignore the QC when device is BUSY */
+ sactive = core_scr_read(qc->ap, SCR_ACTIVE);
+ dev_info(ap->dev, "Ignore current QC as device BUSY"
+ "tag=%d, sactive=0x%08x)\n", qc->tag, sactive);
+ return AC_ERR_SYSTEM;
+ }
+
+ if (hsdevp->sata_dwc_sactive_issued)
+ return AC_ERR_SYSTEM;
+
+ sactive = core_scr_read(qc->ap, SCR_ACTIVE);
+ sactive |= (0x00000001 << tag);
+ qc->dev->link->sactive |= (0x00000001 << tag);
+ core_scr_write(qc->ap, SCR_ACTIVE, sactive);
+
+ dev_dbg(qc->ap->dev, "%s: tag=%d ap->link.sactive = 0x%08x "
+ "sactive=0x%x\n", __func__, tag, qc->ap->link.sactive,
+ sactive);
+
+ ap->ops->sff_tf_load(ap, &qc->tf);
+ sata_dwc_exec_command_by_tag(ap, &qc->tf, qc->tag);
+ } else {
+ ap->link.active_tag = qc->tag;
+ /* Pass QC to libata-sff to process */
+ ata_bmdma_qc_issue(qc);
+ }
+ return 0;
+}
+
+/*
+ * Prepare for a particular queued command
+ */
+
+static void sata_dwc_qc_prep(struct ata_queued_cmd *qc)
+{
+ if ((qc->dma_dir == DMA_NONE) || (qc->tf.protocol == ATA_PROT_PIO)
+ || (qc->tf.protocol == ATAPI_PROT_PIO))
+ return;
+
+#ifdef DEBUG_NCQ
+ if (qc->tag > 0)
+ dev_info(qc->ap->dev, "%s: qc->tag=%d ap->active_tag=0x%08x\n",
+ __func__, qc->tag, qc->ap->link.active_tag);
+#endif
+}
+
+/*
+ * Get the QC currently used for transferring data
+ */
+static struct ata_queued_cmd *sata_dwc_get_active_qc(struct ata_port *ap)
+{
+ struct ata_queued_cmd *qc;
+
+ qc = ata_qc_from_tag(ap, ap->link.active_tag);
+ if (qc && !(qc->tf.flags & ATA_TFLAG_POLLING))
+ return qc;
+ return NULL;
+}
+
+/*
+ * dwc_lost_interrupt - check and process if interrupt is lost.
+ * @ap: ATA port
+ *
+ * Process the command when it is timeout.
+ * Check to see if interrupt is lost. If yes, complete the qc.
+ */
+static void sata_dwc_lost_interrupt(struct ata_port *ap)
+{
+ u8 status;
+ struct sata_dwc_device *hsdev = HSDEV_FROM_AP(ap);
+ struct ata_queued_cmd *qc;
+
+ dev_dbg(ap->dev, "%s -\n", __func__);
+ /* Only one outstanding command per SFF channel */
+ qc = sata_dwc_get_active_qc(ap);
+ /* We cannot lose an interrupt on a non-existent or polled command */
+ if (!qc)
+ return;
+
+ /* See if the controller thinks it is still busy - if so the command
+ isn't a lost IRQ but is still in progress */
+ status = ap->ops->sff_check_altstatus(ap);
+ if (status & ATA_BUSY) {
+ ata_port_printk(ap, KERN_INFO, "%s - ATA_BUSY\n", __func__);
+ return;
+ }
+
+ /* There was a command running, we are no longer busy and we have
+ no interrupt. */
+ ata_link_printk(qc->dev->link, KERN_WARNING,
+ "lost interrupt (Status 0x%x)\n", status);
+
+ if (sata_dwc_dma_chk_en(hsdev->dma_channel)) {
+ /* When DMA does transfer does not complete,
+ see if DMA fails */
+ qc->err_mask |= AC_ERR_DEV;
+ ap->hsm_task_state = HSM_ST_ERR;
+ sata_dwc_dma_terminate(ap, hsdev->dma_channel);
+ }
+ sata_dwc_qc_complete(ap, qc, 1);
+}
+
+
+static void sata_dwc_error_handler(struct ata_port *ap)
+{
+ bool thaw = false;
+ struct ata_queued_cmd *qc;
+ u8 status = ap->ops->bmdma_status(ap);
+
+ qc = sata_dwc_get_active_qc(ap);
+ /* In case of DMA timeout, process it. */
+ if (qc && ata_is_dma(qc->tf.protocol)) {
+ if ((qc->err_mask == AC_ERR_TIMEOUT)
+ && (status & ATA_DMA_ERR)) {
+ qc->err_mask = AC_ERR_HOST_BUS;
+ thaw = true;
+ }
+
+ if (thaw) {
+ ap->ops->sff_check_status(ap);
+ if (ap->ops->sff_irq_clear)
+ ap->ops->sff_irq_clear(ap);
+ }
+ }
+ if (thaw)
+ ata_eh_thaw_port(ap);
+
+ ata_sff_error_handler(ap);
+}
+
+u8 sata_dwc_check_status(struct ata_port *ap)
+{
+ return ioread8(ap->ioaddr.status_addr);
+}
+
+u8 sata_dwc_check_altstatus(struct ata_port *ap)
+{
+ return ioread8(ap->ioaddr.altstatus_addr);
+}
+
+/*
+ * scsi mid-layer and libata interface structures
+ */
+static struct scsi_host_template sata_dwc_sht = {
+ ATA_NCQ_SHT(DRV_NAME),
+ .sg_tablesize = LIBATA_MAX_PRD,
+ /*
+ * test-only: Currently this driver doesn't handle NCQ
+ * correctly. We enable NCQ but set the queue depth to a
+ * max of 1. This will get fixed in in a future release.
+ */
+ .can_queue = ATA_DEF_QUEUE, /*ATA_MAX_QUEUE, */
+ .dma_boundary = ATA_DMA_BOUNDARY,
+};
+
+static struct ata_port_operations sata_dwc_ops = {
+ .inherits = &ata_sff_port_ops,
+
+ .error_handler = sata_dwc_error_handler,
+ .softreset = sata_dwc_softreset,
+ .hardreset = sata_dwc_hardreset,
+
+ .qc_defer = sata_dwc_qc_defer,
+ .qc_prep = sata_dwc_qc_prep,
+ .qc_issue = sata_dwc_qc_issue,
+
+ .scr_read = sata_dwc_scr_read,
+ .scr_write = sata_dwc_scr_write,
+
+ .port_start = sata_dwc_port_start,
+ .port_stop = sata_dwc_port_stop,
+
+ .check_atapi_dma = sata_dwc_check_atapi_dma,
+ .bmdma_setup = sata_dwc_bmdma_setup,
+ .bmdma_start = sata_dwc_bmdma_start,
+ .bmdma_status = sata_dwc_dma_status,
+
+ .sff_dev_select = sata_dwc_dev_select,
+ .sff_check_status = sata_dwc_check_status,
+ .sff_check_altstatus = sata_dwc_check_altstatus,
+ .sff_exec_command = sata_dwc_exec_command,
+
+ .lost_interrupt = sata_dwc_lost_interrupt,
+};
+
+static const struct ata_port_info sata_dwc_port_info[] = {
+ {
+ .flags = ATA_FLAG_SATA | ATA_FLAG_NCQ,
+ .pio_mask = ATA_PIO4,
+ .udma_mask = ATA_UDMA6,
+ .port_ops = &sata_dwc_ops,
+ },
+};
+
+static int sata_dwc_probe(struct platform_device *ofdev)
+{
+ struct sata_dwc_device *hsdev;
+ u32 idr, versionr;
+ char *ver = (char *)&versionr;
+ u8 *base = NULL;
+ int err = 0;
+ int irq, rc;
+ struct ata_host *host;
+ struct ata_port_info pi = sata_dwc_port_info[0];
+ const struct ata_port_info *ppi[] = { &pi, NULL };
+
+ const unsigned int *dma_chan;
+
+ /* Check if device is declared in device tree */
+ if (!of_device_is_available(ofdev->dev.of_node)) {
+ printk(KERN_INFO "%s: Port disabled via device-tree\n",
+ ofdev->dev.of_node->full_name);
+ return 0;
+ }
+
+ /* Allocate DWC SATA device */
+ hsdev = kmalloc(sizeof(*hsdev), GFP_KERNEL);
+ if (hsdev == NULL) {
+ dev_err(&ofdev->dev, "kmalloc failed for hsdev\n");
+ err = -ENOMEM;
+ goto error_out_5;
+ }
+ memset(hsdev, 0, sizeof(*hsdev));
+
+ /* Identify host controller using compatible attribute */
+ if (of_device_is_compatible(ofdev->dev.of_node, "amcc,sata-460ex")) {
+ printk(KERN_INFO "\n\nSATA is compatible for sata-460ex\n\n");
+ hsdev->hostID = APM_460EX_SATA;
+ } else {
+ printk(KERN_INFO "\n\nSATA is compatible for sata-821xx\n\n");
+ hsdev->hostID = APM_821XX_SATA;
+ }
+
+ /* Identify SATA controller index from the cell-index property */
+ dma_chan = of_get_property(ofdev->dev.of_node, "dma-channel", NULL);
+ if (dma_chan) {
+ dev_notice(&ofdev->dev, "Getting DMA channel %d\n", *dma_chan);
+ hsdev->dma_channel = *dma_chan;
+ } else {
+ hsdev->dma_channel = 0;
+ }
+
+ /* Get base address from device tree */
+ base = of_iomap(ofdev->dev.of_node, 0);
+ if (!base) {
+ dev_err(&ofdev->dev,
+ "ioremap failed for SATA register address\n");
+ err = -ENODEV;
+ goto error_out_4;
+ }
+ hsdev->reg_base = base;
+ dev_dbg(&ofdev->dev, "ioremap done for SATA register address\n");
+
+ /* Synopsys DWC SATA specific Registers */
+ hsdev->sata_dwc_regs = (void *__iomem)(base + SATA_DWC_REG_OFFSET);
+
+ /* Allocate and fill host */
+ host = ata_host_alloc_pinfo(&ofdev->dev, ppi, SATA_DWC_MAX_PORTS);
+ if (!host) {
+ dev_err(&ofdev->dev, "ata_host_alloc_pinfo failed\n");
+ err = -ENOMEM;
+ goto error_out_4;
+ }
+
+ host->private_data = hsdev;
+
+ /* Setup port */
+ host->ports[0]->ioaddr.cmd_addr = base;
+ host->ports[0]->ioaddr.scr_addr = base + SATA_DWC_SCR_OFFSET;
+ hsdev->scr_base = (u8 *)(base + SATA_DWC_SCR_OFFSET);
+ sata_dwc_setup_port(&host->ports[0]->ioaddr, (unsigned long)base);
+
+ /* Read the ID and Version Registers */
+ idr = in_le32(&hsdev->sata_dwc_regs->idr);
+ versionr = in_le32(&hsdev->sata_dwc_regs->versionr);
+ dev_notice(&ofdev->dev, "id %d, controller version %c.%c%c\n",
+ idr, ver[0], ver[1], ver[2]);
+
+ /* Get SATA DMA interrupt number */
+ irq = irq_of_parse_and_map(ofdev->dev.of_node, 1);
+ if (irq == NO_IRQ) {
+ dev_err(&ofdev->dev, "no SATA DMA irq\n");
+ err = -ENODEV;
+ goto error_out_3;
+ }
+
+ /* Save dev for later use in dev_xxx() routines */
+ hsdev->dev = &ofdev->dev;
+
+ /* Init glovbal dev list */
+ dwc_dev_list[hsdev->dma_channel] = hsdev;
+
+ /* Get physical SATA DMA register base address */
+ if (sata_dma_regs == NULL) {
+ sata_dma_regs = of_iomap(ofdev->dev.of_node, 1);
+ if (sata_dma_regs == NULL) {
+ dev_err(&ofdev->dev,
+ "ioremap failed for AHBDMA register address\n");
+ err = -ENODEV;
+ goto error_out_2;
+ }
+
+ /* Initialize AHB DMAC */
+ rc = dwc_dma_init(hsdev, irq);
+ if (rc != 0)
+ goto error_out_1;
+ }
+
+ /* Enable SATA Interrupts */
+ sata_dwc_enable_interrupts(hsdev);
+
+ /* Get SATA interrupt number */
+ irq = irq_of_parse_and_map(ofdev->dev.of_node, 0);
+ if (irq == NO_IRQ) {
+ dev_err(&ofdev->dev, "no SATA irq\n");
+ err = -ENODEV;
+ goto error_out_1;
+ }
+
+ /*
+ * Now, register with libATA core, this will also initiate the
+ * device discovery process, invoking our port_start() handler &
+ * error_handler() to execute a dummy Softreset EH session
+ */
+ rc = ata_host_activate(host, irq, sata_dwc_isr, 0, &sata_dwc_sht);
+
+ if (rc != 0)
+ dev_err(&ofdev->dev, "failed to activate host");
+
+ dev_set_drvdata(&ofdev->dev, host);
+
+ return 0;
+
+error_out_1:
+ iounmap(sata_dma_regs);
+
+error_out_2:
+ free_irq(hsdev->irq_dma, hsdev);
+
+error_out_3:
+ iounmap(base);
+
+error_out_4:
+ kfree(hsdev);
+
+error_out_5:
+ return err;
+}
+
+static int sata_dwc_remove(struct platform_device *ofdev)
+{
+ struct device *dev = &ofdev->dev;
+ struct ata_host *host = dev_get_drvdata(dev);
+ struct sata_dwc_device *hsdev = host->private_data;
+
+ ata_host_detach(host);
+ dev_set_drvdata(dev, NULL);
+
+ /* Free SATA DMA resources */
+ iounmap(sata_dma_regs);
+ free_irq(hsdev->irq_dma, hsdev);
+
+ /* Free internal resources */
+ iounmap(hsdev->reg_base);
+ kfree(hsdev);
+ kfree(host);
+ dev_dbg(&ofdev->dev, "done\n");
+ return 0;
+}
+
+static const struct of_device_id sata_dwc_match[] = {
+ { .compatible = "amcc,sata-460ex", },
+ { .compatible = "amcc,sata-apm821xx", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, sata_dwc_match);
+
+static struct platform_driver sata_dwc_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = sata_dwc_match,
+ },
+ .probe = sata_dwc_probe,
+ .remove = sata_dwc_remove,
+};
+
+static int __init sata_dwc_init(void)
+{
+ return platform_driver_register(&sata_dwc_driver);
+}
+
+static void __exit sata_dwc_exit(void)
+{
+ platform_driver_unregister(&sata_dwc_driver);
+}
+
+module_init(sata_dwc_init);
+module_exit(sata_dwc_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mark Miesfeld <mmiesfeld@amcc.com>");
+MODULE_DESCRIPTION("DesignWare Cores SATA controller low lever driver");
+MODULE_VERSION(DRV_VERSION);
--
1.7.3.4
CONFIDENTIALITY NOTICE: This e-mail message, including any attachments,
is for the sole use of the intended recipient(s) and contains information
that is confidential and proprietary to AppliedMicro Corporation or its subsidiaries.
It is to be used solely for the purpose of furthering the parties' business relationship.
All unauthorized review, use, disclosure or distribution is prohibited.
If you are not the intended recipient, please contact the sender by reply e-mail
and destroy all copies of the original message.
^ permalink raw reply related
* Re: [PATCH 1/1] Add support 2 SATA ports for Maui and change filename from sata_dwc_460ex.c to sata_dwc_4xx.c
From: Stefan Roese @ 2012-03-27 11:16 UTC (permalink / raw)
To: Thang Q. Nguyen
Cc: linux-kernel, Paul Mackerras, netdev, linuxppc-dev, David Miller
In-Reply-To: <1332843983-24254-1-git-send-email-tqnguyen@apm.com>
On Tuesday 27 March 2012 12:26:23 Thang Q. Nguyen wrote:
> Support 2 native SATA ports on APM821XX and change the existing file name
> from sata_dwc_460ex.c to sata_dwc_4xx.c. This supports both 460EX and
> APM821XX.
>
> Signed-off-by: Thang Q. Nguyen <tqnguyen@apm.com>
> ---
> arch/powerpc/boot/dts/bluestone.dts | 21 +
> drivers/ata/Makefile | 2 +-
> drivers/ata/sata_dwc_4xx.c | 2178 +++++++++++++++++++++++++++++++++++
> 3 files changed, 2200 insertions(+), 1 deletions(-)
> create mode 100644 drivers/ata/sata_dwc_4xx.c
Please use -M option (find-renames) while generating this patch to see
changes in the driver. And you missed to remove the "old" driver.
Thanks,
Stefan
^ permalink raw reply
* [PATCH 0/4] powerpc/mpic: Enhancements for FSL MPIC.
From: Varun Sethi @ 2012-03-27 12:14 UTC (permalink / raw)
To: Linuxppc-dev; +Cc: Varun Sethi
This patchset adds/fixes the following functionality specific to the
FSL MPIC:
1. Fix support for timer group B interrupts. Previously these were
not getting initialized.
2. Use the MPIC_LARGE_VECTORS flag while intializing FSL MPIC.
This prevents us from eating in to hardware vector number
space (MSIs) while setting up internal sources.
3. Cleanup MPIC internal source vector allocation, by moving it to a
separate function.
4.Cascaded handling for the MPIC error interrupt. This is possible
with FSL MPIC version >= 4.1.
The patches are based on "next" branch of Benjamin Herrenschmidt's powerpc
linux tree.
Varun Sethi (4):
Support timer group B on Freescale chip.
Use the MPIC_LARGE_VECTORS flag for FSL MPIC.
Move internal interrupt source vector allocation to a separate
function.
Mpic error interrupt support.
arch/powerpc/include/asm/mpic.h | 30 +++++-
arch/powerpc/sysdev/mpic.c | 245 ++++++++++++++++++++++++++++++++++-----
2 files changed, 241 insertions(+), 34 deletions(-)
--
1.7.2.2
^ permalink raw reply
* [PATCH 1/4] powerpc/mpic: finish supporting timer group B on Freescale chips
From: Varun Sethi @ 2012-03-27 12:15 UTC (permalink / raw)
To: Linuxppc-dev; +Cc: Scott Wood
From: Scott Wood <scottwood@freescale.com>
Previously, these interrupts would be mapped, but the offset
calculation was broken, and only the first group was initialized.
Signed-off-by: Scott Wood <scottwood@freescale.com>
---
arch/powerpc/include/asm/mpic.h | 5 +++
arch/powerpc/sysdev/mpic.c | 58 ++++++++++++++++++++++++++++-----------
2 files changed, 47 insertions(+), 16 deletions(-)
diff --git a/arch/powerpc/include/asm/mpic.h b/arch/powerpc/include/asm/mpic.h
index d7e3fec..30e3b29 100644
--- a/arch/powerpc/include/asm/mpic.h
+++ b/arch/powerpc/include/asm/mpic.h
@@ -63,6 +63,7 @@
*/
#define MPIC_TIMER_BASE 0x01100
#define MPIC_TIMER_STRIDE 0x40
+#define MPIC_TIMER_GROUP_STRIDE 0x1000
#define MPIC_TIMER_CURRENT_CNT 0x00000
#define MPIC_TIMER_BASE_CNT 0x00010
@@ -110,6 +111,9 @@
#define MPIC_VECPRI_SENSE_MASK 0x00400000
#define MPIC_IRQ_DESTINATION 0x00010
+#define MPIC_FSL_BRR1 0x00000
+#define MPIC_FSL_BRR1_VER 0x0000ffff
+
#define MPIC_MAX_IRQ_SOURCES 2048
#define MPIC_MAX_CPUS 32
#define MPIC_MAX_ISU 32
@@ -299,6 +303,7 @@ struct mpic
phys_addr_t paddr;
/* The various ioremap'ed bases */
+ struct mpic_reg_bank thiscpuregs;
struct mpic_reg_bank gregs;
struct mpic_reg_bank tmregs;
struct mpic_reg_bank cpuregs[MPIC_MAX_CPUS];
diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c
index 16eb743..8be1ea2 100644
--- a/arch/powerpc/sysdev/mpic.c
+++ b/arch/powerpc/sysdev/mpic.c
@@ -6,7 +6,7 @@
* with various broken implementations of this HW.
*
* Copyright (C) 2004 Benjamin Herrenschmidt, IBM Corp.
- * Copyright 2010-2011 Freescale Semiconductor, Inc.
+ * Copyright 2010-2012 Freescale Semiconductor, Inc.
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive
@@ -221,24 +221,24 @@ static inline void _mpic_ipi_write(struct mpic *mpic, unsigned int ipi, u32 valu
_mpic_write(mpic->reg_type, &mpic->gregs, offset, value);
}
-static inline u32 _mpic_tm_read(struct mpic *mpic, unsigned int tm)
+static inline unsigned int mpic_tm_offset(struct mpic *mpic, unsigned int tm)
{
- unsigned int offset = MPIC_INFO(TIMER_VECTOR_PRI) +
- ((tm & 3) * MPIC_INFO(TIMER_STRIDE));
+ return (tm >> 2) * MPIC_TIMER_GROUP_STRIDE +
+ (tm & 3) * MPIC_INFO(TIMER_STRIDE);
+}
- if (tm >= 4)
- offset += 0x1000 / 4;
+static inline u32 _mpic_tm_read(struct mpic *mpic, unsigned int tm)
+{
+ unsigned int offset = mpic_tm_offset(mpic, tm) +
+ MPIC_INFO(TIMER_VECTOR_PRI);
return _mpic_read(mpic->reg_type, &mpic->tmregs, offset);
}
static inline void _mpic_tm_write(struct mpic *mpic, unsigned int tm, u32 value)
{
- unsigned int offset = MPIC_INFO(TIMER_VECTOR_PRI) +
- ((tm & 3) * MPIC_INFO(TIMER_STRIDE));
-
- if (tm >= 4)
- offset += 0x1000 / 4;
+ unsigned int offset = mpic_tm_offset(mpic, tm) +
+ MPIC_INFO(TIMER_VECTOR_PRI);
_mpic_write(mpic->reg_type, &mpic->tmregs, offset, value);
}
@@ -1281,6 +1281,16 @@ struct mpic * __init mpic_alloc(struct device_node *node,
mpic_map(mpic, mpic->paddr, &mpic->gregs, MPIC_INFO(GREG_BASE), 0x1000);
mpic_map(mpic, mpic->paddr, &mpic->tmregs, MPIC_INFO(TIMER_BASE), 0x1000);
+ if (mpic->flags & MPIC_FSL) {
+ /*
+ * Yes, Freescale really did put global registers in the
+ * magic per-cpu area -- and they don't even show up in the
+ * non-magic per-cpu copies that this driver normally uses.
+ */
+ mpic_map(mpic, mpic->paddr, &mpic->thiscpuregs,
+ MPIC_CPU_THISBASE, 0x1000);
+ }
+
/* Reset */
/* When using a device-node, reset requests are only honored if the MPIC
@@ -1428,6 +1438,7 @@ void __init mpic_set_default_senses(struct mpic *mpic, u8 *senses, int count)
void __init mpic_init(struct mpic *mpic)
{
int i, cpu;
+ int num_timers = 4;
BUG_ON(mpic->num_sources == 0);
@@ -1436,15 +1447,30 @@ void __init mpic_init(struct mpic *mpic)
/* Set current processor priority to max */
mpic_cpu_write(MPIC_INFO(CPU_CURRENT_TASK_PRI), 0xf);
+ if (mpic->flags & MPIC_FSL) {
+ u32 brr1 = _mpic_read(mpic->reg_type, &mpic->thiscpuregs,
+ MPIC_FSL_BRR1);
+ u32 version = brr1 & MPIC_FSL_BRR1_VER;
+
+ /*
+ * Timer group B is present at the latest in MPIC 3.1 (e.g.
+ * mpc8536). It is not present in MPIC 2.0 (e.g. mpc8544).
+ * I don't know about the status of intermediate versions (or
+ * whether they even exist).
+ */
+ if (version >= 0x0301)
+ num_timers = 8;
+ }
+
/* Initialize timers to our reserved vectors and mask them for now */
- for (i = 0; i < 4; i++) {
+ for (i = 0; i < num_timers; i++) {
+ unsigned int offset = mpic_tm_offset(mpic, i);
+
mpic_write(mpic->tmregs,
- i * MPIC_INFO(TIMER_STRIDE) +
- MPIC_INFO(TIMER_DESTINATION),
+ offset + MPIC_INFO(TIMER_DESTINATION),
1 << hard_smp_processor_id());
mpic_write(mpic->tmregs,
- i * MPIC_INFO(TIMER_STRIDE) +
- MPIC_INFO(TIMER_VECTOR_PRI),
+ offset + MPIC_INFO(TIMER_VECTOR_PRI),
MPIC_VECPRI_MASK |
(9 << MPIC_VECPRI_PRIORITY_SHIFT) |
(mpic->timer_vecs[0] + i));
--
1.7.2.2
^ permalink raw reply related
* [PATCH 2/4] powerpc/mpic: Use the MPIC_LARGE_VECTORS flag for FSL MPIC.
From: Varun Sethi @ 2012-03-27 12:15 UTC (permalink / raw)
To: Linuxppc-dev; +Cc: Varun Sethi
FSL MPIC supports 16 bit vectors so our vector number space isn't
restricted to 256 vectors. We should use the MPIC_LARG_VECTORS flag
while intializing the MPIC. This also prevents us from eating in to
hardware vector number space (MSIs) while setting up internal sources.
Signed-off-by: Varun Sethi <Varun.Sethi@freescale.com>
---
arch/powerpc/sysdev/mpic.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c
index 8be1ea2..33520dd 100644
--- a/arch/powerpc/sysdev/mpic.c
+++ b/arch/powerpc/sysdev/mpic.c
@@ -1191,7 +1191,7 @@ struct mpic * __init mpic_alloc(struct device_node *node,
if (of_get_property(node, "single-cpu-affinity", NULL))
flags |= MPIC_SINGLE_DEST_CPU;
if (of_device_is_compatible(node, "fsl,mpic"))
- flags |= MPIC_FSL;
+ flags |= MPIC_FSL | MPIC_LARGE_VECTORS;
mpic = kzalloc(sizeof(struct mpic), GFP_KERNEL);
if (mpic == NULL)
--
1.7.2.2
^ permalink raw reply related
* [PATCH 3/4] powerpc/mpic: Move internal interrupt source vector allocation to a separate function.
From: Varun Sethi @ 2012-03-27 12:16 UTC (permalink / raw)
To: Linuxppc-dev; +Cc: Varun Sethi
Allocate vector numbers for MPIC internal interrupt sources (IPIs and Timers) in a
separate function.
Signed-off-by: Varun Sethi <Varun.Sethi@freescale.com>
---
arch/powerpc/include/asm/mpic.h | 7 +++++--
arch/powerpc/sysdev/mpic.c | 30 +++++++++++++++++-------------
2 files changed, 22 insertions(+), 15 deletions(-)
diff --git a/arch/powerpc/include/asm/mpic.h b/arch/powerpc/include/asm/mpic.h
index 30e3b29..3929b4b 100644
--- a/arch/powerpc/include/asm/mpic.h
+++ b/arch/powerpc/include/asm/mpic.h
@@ -118,6 +118,9 @@
#define MPIC_MAX_CPUS 32
#define MPIC_MAX_ISU 32
+#define MPIC_MAX_TIMER 8
+#define MPIC_MAX_IPI 4
+
/*
* Tsi108 implementation of MPIC has many differences from the original one
*/
@@ -284,8 +287,8 @@ struct mpic
unsigned int senses_count;
/* vector numbers used for internal sources (ipi/timers) */
- unsigned int ipi_vecs[4];
- unsigned int timer_vecs[8];
+ unsigned int ipi_vecs[MPIC_MAX_IPI];
+ unsigned int timer_vecs[MPIC_MAX_TIMER];
/* Spurious vector to program into unused sources */
unsigned int spurious_vec;
diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c
index 33520dd..c4da1d5 100644
--- a/arch/powerpc/sysdev/mpic.c
+++ b/arch/powerpc/sysdev/mpic.c
@@ -996,7 +996,8 @@ static int mpic_host_map(struct irq_host *h, unsigned int virq,
}
#endif /* CONFIG_SMP */
- if (hw >= mpic->timer_vecs[0] && hw <= mpic->timer_vecs[7]) {
+ if (hw >= mpic->timer_vecs[0] &&
+ hw <= mpic->timer_vecs[MPIC_MAX_TIMER - 1]) {
WARN_ON(mpic->flags & MPIC_SECONDARY);
DBG("mpic: mapping as timer\n");
@@ -1133,6 +1134,19 @@ static struct irq_host_ops mpic_host_ops = {
.xlate = mpic_host_xlate,
};
+static void mpic_alloc_int_sources(struct mpic *mpic, int intvec_top)
+{
+ int i, intvec;
+
+ intvec = intvec_top;
+
+ for (i = MPIC_MAX_IPI - 1; i >= 0; i--)
+ mpic->ipi_vecs[i] = --intvec;
+
+ for (i = MPIC_MAX_TIMER - 1; i >= 0; i--)
+ mpic->timer_vecs[i] = --intvec;
+}
+
/*
* Exported functions
*/
@@ -1228,18 +1242,6 @@ struct mpic * __init mpic_alloc(struct device_node *node,
else
intvec_top = 255;
- mpic->timer_vecs[0] = intvec_top - 12;
- mpic->timer_vecs[1] = intvec_top - 11;
- mpic->timer_vecs[2] = intvec_top - 10;
- mpic->timer_vecs[3] = intvec_top - 9;
- mpic->timer_vecs[4] = intvec_top - 8;
- mpic->timer_vecs[5] = intvec_top - 7;
- mpic->timer_vecs[6] = intvec_top - 6;
- mpic->timer_vecs[7] = intvec_top - 5;
- mpic->ipi_vecs[0] = intvec_top - 4;
- mpic->ipi_vecs[1] = intvec_top - 3;
- mpic->ipi_vecs[2] = intvec_top - 2;
- mpic->ipi_vecs[3] = intvec_top - 1;
mpic->spurious_vec = intvec_top;
/* Look for protected sources */
@@ -1365,6 +1367,8 @@ struct mpic * __init mpic_alloc(struct device_node *node,
mpic->isu_shift = 1 + __ilog2(mpic->isu_size - 1);
mpic->isu_mask = (1 << mpic->isu_shift) - 1;
+ mpic_alloc_int_sources(mpic, intvec_top);
+
mpic->irqhost = irq_alloc_host(mpic->node, IRQ_HOST_MAP_LINEAR,
last_irq + 1, &mpic_host_ops,
intvec_top + 1);
--
1.7.2.2
^ permalink raw reply related
* [PATCH 4/4] powerpc/mpic: FSL MPIC error interrupt support.
From: Varun Sethi @ 2012-03-27 12:17 UTC (permalink / raw)
To: Linuxppc-dev; +Cc: Varun Sethi
All SOC device error interrupts are muxed and delivered to the core as a single
MPIC error interrupt. Currently all the device drivers requiring access to device
errors have to register for the MPIC error interrupt as a shared interrupt.
With this patch we add interrupt demuxing capability in the mpic driver, allowing
device drivers to register for their individual error interrupts. This is achieved
by handling error interrupts in a cascaded fashion.
MPIC error interrupt is handled by the "error_int_handler", which subsequently demuxes
it using the EISR and delivers it to the respective drivers.
The error interrupt capability is dependent on the MPIC EIMR register, which was
introduced in FSL MPIC version 4.1 (P4080 rev2). So, error interrupt demuxing capability
is dependent on the MPIC version and can be used for versions >= 4.1.
Signed-off-by: Varun Sethi <Varun.Sethi@freescale.com>
---
arch/powerpc/include/asm/mpic.h | 18 +++++
arch/powerpc/sysdev/mpic.c | 155 ++++++++++++++++++++++++++++++++++++++-
2 files changed, 171 insertions(+), 2 deletions(-)
diff --git a/arch/powerpc/include/asm/mpic.h b/arch/powerpc/include/asm/mpic.h
index 3929b4b..db51015 100644
--- a/arch/powerpc/include/asm/mpic.h
+++ b/arch/powerpc/include/asm/mpic.h
@@ -114,12 +114,21 @@
#define MPIC_FSL_BRR1 0x00000
#define MPIC_FSL_BRR1_VER 0x0000ffff
+/*
+ * Error interrupt registers
+ */
+
+#define MPIC_ERR_INT_BASE 0x3900
+#define MPIC_ERR_INT_EISR 0x0000
+#define MPIC_ERR_INT_EIMR 0x0010
+
#define MPIC_MAX_IRQ_SOURCES 2048
#define MPIC_MAX_CPUS 32
#define MPIC_MAX_ISU 32
#define MPIC_MAX_TIMER 8
#define MPIC_MAX_IPI 4
+#define MPIC_MAX_ERR 32
/*
* Tsi108 implementation of MPIC has many differences from the original one
@@ -273,6 +282,7 @@ struct mpic
struct irq_chip hc_ipi;
#endif
struct irq_chip hc_tm;
+ struct irq_chip hc_err;
const char *name;
/* Flags */
unsigned int flags;
@@ -289,6 +299,8 @@ struct mpic
/* vector numbers used for internal sources (ipi/timers) */
unsigned int ipi_vecs[MPIC_MAX_IPI];
unsigned int timer_vecs[MPIC_MAX_TIMER];
+ /* vector numbers used for FSL MPIC error interrupts */
+ unsigned int err_int_vecs[MPIC_MAX_ERR];
/* Spurious vector to program into unused sources */
unsigned int spurious_vec;
@@ -311,6 +323,10 @@ struct mpic
struct mpic_reg_bank tmregs;
struct mpic_reg_bank cpuregs[MPIC_MAX_CPUS];
struct mpic_reg_bank isus[MPIC_MAX_ISU];
+ struct mpic_reg_bank err_regs;
+
+ /* error interrupt config */
+ u32 err_int_config_done;
/* Protected sources */
unsigned long *protected;
@@ -376,6 +392,8 @@ struct mpic
#define MPIC_NO_RESET 0x00004000
/* Freescale MPIC (compatible includes "fsl,mpic") */
#define MPIC_FSL 0x00008000
+/* Freescale MPIC supports EIMR (error interrupt mask register)*/
+#define MPIC_FSL_HAS_EIMR 0x00010000
/* MPIC HW modification ID */
#define MPIC_REGSET_MASK 0xf0000000
diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c
index c4da1d5..b0ff465 100644
--- a/arch/powerpc/sysdev/mpic.c
+++ b/arch/powerpc/sysdev/mpic.c
@@ -221,6 +221,17 @@ static inline void _mpic_ipi_write(struct mpic *mpic, unsigned int ipi, u32 valu
_mpic_write(mpic->reg_type, &mpic->gregs, offset, value);
}
+static inline u32 _mpic_err_read(struct mpic *mpic, unsigned int err_reg)
+{
+ return _mpic_read(mpic->reg_type, &mpic->err_regs, err_reg);
+}
+
+static inline void _mpic_err_write(struct mpic *mpic, unsigned int err_reg,
+ u32 value)
+{
+ _mpic_write(mpic->reg_type, &mpic->err_regs, err_reg, value);
+}
+
static inline unsigned int mpic_tm_offset(struct mpic *mpic, unsigned int tm)
{
return (tm >> 2) * MPIC_TIMER_GROUP_STRIDE +
@@ -295,6 +306,8 @@ static inline void _mpic_irq_write(struct mpic *mpic, unsigned int src_no,
#define mpic_ipi_write(i,v) _mpic_ipi_write(mpic,(i),(v))
#define mpic_tm_read(i) _mpic_tm_read(mpic,(i))
#define mpic_tm_write(i,v) _mpic_tm_write(mpic,(i),(v))
+#define mpic_err_read(i) _mpic_err_read(mpic, (i))
+#define mpic_err_write(i, v) _mpic_err_write(mpic, (i), (v))
#define mpic_cpu_read(i) _mpic_cpu_read(mpic,(i))
#define mpic_cpu_write(i,v) _mpic_cpu_write(mpic,(i),(v))
#define mpic_irq_read(s,r) _mpic_irq_read(mpic,(s),(r))
@@ -821,6 +834,86 @@ static void mpic_mask_tm(struct irq_data *d)
mpic_tm_read(src);
}
+static void mpic_mask_err(struct irq_data *d)
+{
+ u32 eimr;
+ struct mpic *mpic = mpic_from_irq_data(d);
+ unsigned int src = virq_to_hw(d->irq) - mpic->err_int_vecs[0];
+ unsigned int err_reg_offset = MPIC_INFO(ERR_INT_EIMR);
+
+ eimr = mpic_err_read(err_reg_offset);
+ eimr |= (0x80000000 >> src);
+ mpic_err_write(err_reg_offset, eimr);
+}
+
+static void mpic_unmask_err(struct irq_data *d)
+{
+ u32 eimr;
+ struct mpic *mpic = mpic_from_irq_data(d);
+ unsigned int src = virq_to_hw(d->irq) - mpic->err_int_vecs[0];
+ unsigned int err_reg_offset = MPIC_INFO(ERR_INT_EIMR);
+
+ eimr = mpic_err_read(err_reg_offset);
+ eimr &= ~(0x80000000 >> src);
+ mpic_err_write(err_reg_offset, eimr);
+}
+
+static irqreturn_t error_int_handler(int irq, void *data)
+{
+ struct mpic *mpic = (struct mpic *) data;
+ unsigned int eisr_offset = MPIC_INFO(ERR_INT_EISR);
+ unsigned int eimr_offset = MPIC_INFO(ERR_INT_EIMR);
+ u32 eisr, eimr;
+ int errint;
+ unsigned int cascade_irq;
+
+ eisr = mpic_err_read(eisr_offset);
+ eimr = mpic_err_read(eimr_offset);
+
+ if (!(eisr & ~eimr))
+ return IRQ_NONE;
+
+ while (eisr) {
+ errint = __ffs(eisr);
+ cascade_irq = irq_linear_revmap(mpic->irqhost,
+ mpic->err_int_vecs[31 - errint]);
+ WARN_ON(cascade_irq == NO_IRQ);
+ if (cascade_irq != NO_IRQ) {
+ generic_handle_irq(cascade_irq);
+ } else {
+ eimr |= 1 << errint;
+ mpic_err_write(eimr_offset, eimr);
+ }
+ eisr &= ~(1 << errint);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int mpic_err_int_init(struct mpic *mpic, irq_hw_number_t irqnum)
+{
+ unsigned int virq;
+ unsigned int offset = MPIC_INFO(ERR_INT_EIMR);
+ int ret;
+
+ virq = irq_create_mapping(mpic->irqhost, irqnum);
+ if (virq == NO_IRQ) {
+ pr_err("Error interrupt setup failed\n");
+ return -ENOSPC;
+ }
+
+ mpic_err_write(offset, ~0);
+
+ ret = request_irq(virq, error_int_handler, IRQF_NO_THREAD,
+ "mpic-error-int", mpic);
+ if (ret) {
+ pr_err("Failed to register error interrupt handler\n");
+ return ret;
+ }
+
+ return 0;
+}
+
int mpic_set_affinity(struct irq_data *d, const struct cpumask *cpumask,
bool force)
{
@@ -947,6 +1040,12 @@ static struct irq_chip mpic_ipi_chip = {
};
#endif /* CONFIG_SMP */
+static struct irq_chip mpic_err_chip = {
+ .irq_disable = mpic_mask_err,
+ .irq_mask = mpic_mask_err,
+ .irq_unmask = mpic_unmask_err,
+};
+
static struct irq_chip mpic_tm_chip = {
.irq_mask = mpic_mask_tm,
.irq_unmask = mpic_unmask_tm,
@@ -984,8 +1083,19 @@ static int mpic_host_map(struct irq_host *h, unsigned int virq,
if (mpic->protected && test_bit(hw, mpic->protected))
return -EINVAL;
+ if ((mpic->flags & MPIC_FSL_HAS_EIMR) &&
+ hw >= mpic->err_int_vecs[0]) {
+ WARN_ON(mpic->flags & MPIC_SECONDARY);
+
+ DBG("mpic: mapping as Error Interrupt\n");
+ irq_set_chip_data(virq, mpic);
+ irq_set_chip_and_handler(virq, &mpic->hc_err,
+ handle_simple_irq);
+ return 0;
+ }
#ifdef CONFIG_SMP
- else if (hw >= mpic->ipi_vecs[0]) {
+ if (hw >= mpic->ipi_vecs[0] &&
+ hw <= mpic->ipi_vecs[MPIC_MAX_IPI - 1]) {
WARN_ON(mpic->flags & MPIC_SECONDARY);
DBG("mpic: mapping as IPI\n");
@@ -1066,7 +1176,23 @@ static int mpic_host_xlate(struct irq_host *h, struct device_node *ct,
*/
switch (intspec[2]) {
case 0:
- case 1: /* no EISR/EIMR support for now, treat as shared IRQ */
+ break;
+ case 1:
+ if (!(mpic->flags & MPIC_FSL_HAS_EIMR))
+ break;
+
+ if (intspec[3] >= ARRAY_SIZE(mpic->err_int_vecs))
+ return -EINVAL;
+
+ if (!mpic->err_int_config_done) {
+ int ret;
+ ret = mpic_err_int_init(mpic, intspec[0]);
+ if (ret)
+ return ret;
+ mpic->err_int_config_done = 1;
+ }
+
+ *out_hwirq = mpic->err_int_vecs[intspec[3]];
break;
case 2:
if (intspec[0] >= ARRAY_SIZE(mpic->ipi_vecs))
@@ -1140,6 +1266,11 @@ static void mpic_alloc_int_sources(struct mpic *mpic, int intvec_top)
intvec = intvec_top;
+ if (mpic->flags & MPIC_FSL_HAS_EIMR) {
+ for (i = MPIC_MAX_ERR - 1; i >= 0; i--)
+ mpic->err_int_vecs[i] = --intvec;
+ }
+
for (i = MPIC_MAX_IPI - 1; i >= 0; i--)
mpic->ipi_vecs[i] = --intvec;
@@ -1284,6 +1415,8 @@ struct mpic * __init mpic_alloc(struct device_node *node,
mpic_map(mpic, mpic->paddr, &mpic->tmregs, MPIC_INFO(TIMER_BASE), 0x1000);
if (mpic->flags & MPIC_FSL) {
+ u32 brr1, version;
+
/*
* Yes, Freescale really did put global registers in the
* magic per-cpu area -- and they don't even show up in the
@@ -1291,6 +1424,24 @@ struct mpic * __init mpic_alloc(struct device_node *node,
*/
mpic_map(mpic, mpic->paddr, &mpic->thiscpuregs,
MPIC_CPU_THISBASE, 0x1000);
+
+ brr1 = _mpic_read(mpic->reg_type, &mpic->thiscpuregs,
+ MPIC_FSL_BRR1);
+ version = brr1 & MPIC_FSL_BRR1_VER;
+
+ /* Error interrupt mask register (EIMR) is required for
+ * handling individual device error interrupts. EIMR
+ * was added in MPIC version 4.1.
+ */
+ if (version >= 0x401) {
+ mpic->hc_err = mpic_err_chip;
+ mpic->hc_err.name = mpic->name;
+ /* Map error interrupt registers */
+ mpic_map(mpic, mpic->paddr, &mpic->err_regs,
+ MPIC_INFO(ERR_INT_BASE), 0x1000);
+ mpic->flags |= MPIC_FSL_HAS_EIMR;
+ }
+
}
/* Reset */
--
1.7.2.2
^ permalink raw reply related
* Re: [PATCH 3/4] powerpc/mpic: Move internal interrupt source vector allocation to a separate function.
From: Kumar Gala @ 2012-03-27 13:24 UTC (permalink / raw)
To: Varun Sethi; +Cc: Linuxppc-dev
In-Reply-To: <1332850586-23610-1-git-send-email-Varun.Sethi@freescale.com>
On Mar 27, 2012, at 7:16 AM, Varun Sethi wrote:
> Allocate vector numbers for MPIC internal interrupt sources (IPIs and =
Timers) in a=20
> separate function.
>=20
Explain why you are making this change.
> Signed-off-by: Varun Sethi <Varun.Sethi@freescale.com>
> ---
> arch/powerpc/include/asm/mpic.h | 7 +++++--
> arch/powerpc/sysdev/mpic.c | 30 +++++++++++++++++-------------
> 2 files changed, 22 insertions(+), 15 deletions(-)
>=20
> diff --git a/arch/powerpc/include/asm/mpic.h =
b/arch/powerpc/include/asm/mpic.h
> index 30e3b29..3929b4b 100644
> --- a/arch/powerpc/include/asm/mpic.h
> +++ b/arch/powerpc/include/asm/mpic.h
> @@ -118,6 +118,9 @@
> #define MPIC_MAX_CPUS 32
> #define MPIC_MAX_ISU 32
>=20
> +#define MPIC_MAX_TIMER 8
> +#define MPIC_MAX_IPI 4
> +
> /*
> * Tsi108 implementation of MPIC has many differences from the =
original one
> */
> @@ -284,8 +287,8 @@ struct mpic
> unsigned int senses_count;
>=20
> /* vector numbers used for internal sources (ipi/timers) */
> - unsigned int ipi_vecs[4];
> - unsigned int timer_vecs[8];
> + unsigned int ipi_vecs[MPIC_MAX_IPI];
> + unsigned int timer_vecs[MPIC_MAX_TIMER];
>=20
> /* Spurious vector to program into unused sources */
> unsigned int spurious_vec;
> diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c
> index 33520dd..c4da1d5 100644
> --- a/arch/powerpc/sysdev/mpic.c
> +++ b/arch/powerpc/sysdev/mpic.c
> @@ -996,7 +996,8 @@ static int mpic_host_map(struct irq_host *h, =
unsigned int virq,
> }
> #endif /* CONFIG_SMP */
>=20
> - if (hw >=3D mpic->timer_vecs[0] && hw <=3D mpic->timer_vecs[7]) =
{
> + if (hw >=3D mpic->timer_vecs[0] &&
> + hw <=3D mpic->timer_vecs[MPIC_MAX_TIMER - 1]) {
> WARN_ON(mpic->flags & MPIC_SECONDARY);
>=20
> DBG("mpic: mapping as timer\n");
> @@ -1133,6 +1134,19 @@ static struct irq_host_ops mpic_host_ops =3D {
> .xlate =3D mpic_host_xlate,
> };
>=20
> +static void mpic_alloc_int_sources(struct mpic *mpic, int intvec_top)
> +{
> + int i, intvec;
> +
> + intvec =3D intvec_top;
> +
local intvec is pointless.
> + for (i =3D MPIC_MAX_IPI - 1; i >=3D 0; i--)
> + mpic->ipi_vecs[i] =3D --intvec;
> +
> + for (i =3D MPIC_MAX_TIMER - 1; i >=3D 0; i--)
> + mpic->timer_vecs[i] =3D --intvec;
> +}
> +
> /*
> * Exported functions
> */
> @@ -1228,18 +1242,6 @@ struct mpic * __init mpic_alloc(struct =
device_node *node,
> else
> intvec_top =3D 255;
>=20
> - mpic->timer_vecs[0] =3D intvec_top - 12;
> - mpic->timer_vecs[1] =3D intvec_top - 11;
> - mpic->timer_vecs[2] =3D intvec_top - 10;
> - mpic->timer_vecs[3] =3D intvec_top - 9;
> - mpic->timer_vecs[4] =3D intvec_top - 8;
> - mpic->timer_vecs[5] =3D intvec_top - 7;
> - mpic->timer_vecs[6] =3D intvec_top - 6;
> - mpic->timer_vecs[7] =3D intvec_top - 5;
> - mpic->ipi_vecs[0] =3D intvec_top - 4;
> - mpic->ipi_vecs[1] =3D intvec_top - 3;
> - mpic->ipi_vecs[2] =3D intvec_top - 2;
> - mpic->ipi_vecs[3] =3D intvec_top - 1;
> mpic->spurious_vec =3D intvec_top;
>=20
> /* Look for protected sources */
> @@ -1365,6 +1367,8 @@ struct mpic * __init mpic_alloc(struct =
device_node *node,
> mpic->isu_shift =3D 1 + __ilog2(mpic->isu_size - 1);
> mpic->isu_mask =3D (1 << mpic->isu_shift) - 1;
>=20
> + mpic_alloc_int_sources(mpic, intvec_top);
> +
> mpic->irqhost =3D irq_alloc_host(mpic->node, =
IRQ_HOST_MAP_LINEAR,
> last_irq + 1, &mpic_host_ops,
> intvec_top + 1);
> --=20
> 1.7.2.2
>=20
>=20
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/linuxppc-dev
^ permalink raw reply
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