* [RFC] sh7751(r): Adding support for PCI-DMA
@ 2008-09-26 11:50 Roni Feldman
2008-09-29 11:49 ` Paul Mundt
` (3 more replies)
0 siblings, 4 replies; 5+ messages in thread
From: Roni Feldman @ 2008-09-26 11:50 UTC (permalink / raw)
To: linux-sh
Hello everyone,
I've added very basic PCI-DMA support for the sh7751's. I'll be happy
to see if this works on
different machines since it has been tested only on one platform.
Also, it would be interesting
to know if there are other SH-4(A) devices that support this.
Best regards,
Roni
---
From: Roni Feldman <roni.feldman@gmail.com>
Adding support for pci-to-memory and memory-to-pci DMA
transfers via the PCIC.
Signed-off-by: Roni Feldman <roni.feldman@gmail.com>
---
arch/sh/drivers/pci/Kconfig | 7 +
arch/sh/drivers/pci/Makefile | 1 +
arch/sh/drivers/pci/pci-dma-sh7751.c | 228 ++++++++++++++++++++++++++
arch/sh/include/cpu-sh4/cpu/pci-dma-sh7751.h | 25 +++
4 files changed, 261 insertions(+), 0 deletions(-)
diff --git a/arch/sh/drivers/pci/Kconfig b/arch/sh/drivers/pci/Kconfig
index 7e816ed..05691a5 100644
--- a/arch/sh/drivers/pci/Kconfig
+++ b/arch/sh/drivers/pci/Kconfig
@@ -35,3 +35,10 @@ config PCI_AUTO_UPDATE_RESOURCES
with its resources updated beyond what they are when the device
is powered up, set this to N. Everyone else will want this as Y.
+config SH7751_PCI_DMA
+ bool "PCI DMA support for SH7751(R)"
+ depends on (PCI && (CPU_SUBTYPE_SH7751 || CPU_SUBTYPE_SH7751R))
+ default n
+ help
+ PCI-DMA support for fast PCI-to-memory or memory-to-PCI transfers.
+
diff --git a/arch/sh/drivers/pci/Makefile b/arch/sh/drivers/pci/Makefile
index 847e908..c7726a6 100644
--- a/arch/sh/drivers/pci/Makefile
+++ b/arch/sh/drivers/pci/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_PCI_AUTO) += pci-auto.o
obj-$(CONFIG_CPU_SUBTYPE_SH7751) += pci-sh7751.o ops-sh4.o
obj-$(CONFIG_CPU_SUBTYPE_SH7751R) += pci-sh7751.o ops-sh4.o
+obj-$(CONFIG_SH7751_PCI_DMA) += pci-dma-sh7751.o
obj-$(CONFIG_CPU_SUBTYPE_SH7763) += pci-sh7780.o ops-sh4.o
obj-$(CONFIG_CPU_SUBTYPE_SH7780) += pci-sh7780.o ops-sh4.o
obj-$(CONFIG_CPU_SUBTYPE_SH7785) += pci-sh7780.o ops-sh4.o
diff --git a/arch/sh/drivers/pci/pci-dma-sh7751.c
b/arch/sh/drivers/pci/pci-dma-sh7751.c
new file mode 100755
index 0000000..7b86cf7
--- /dev/null
+++ b/arch/sh/drivers/pci/pci-dma-sh7751.c
@@ -0,0 +1,228 @@
+/*
+ * PCI DMA operaions for SH7751, SH7751R.
+ *
+ * 2008 Roni Feldman
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License v2. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/semaphore.h>
+
+#include "pci-sh4.h"
+
+#define IRQ_PCI_SYSERROR 64
+#define IRQ_PCI_ERROR 71
+#define PCI_DMA_HIGHEST_IRQ 68
+#define MAX_PCI_DMA_CHANNELS 4
+
+static struct semaphore pci_dma_sem;
+
+struct pci_dma_registers {
+ unsigned long local_addr;
+ unsigned long remote_addr;
+ unsigned long count;
+ unsigned long control;
+};
+
+struct pci_dma_channel {
+ atomic_t busy;
+ int chan;
+ const char * name;
+ struct semaphore transfer_complete;
+ struct pci_dma_registers regs;
+};
+
+static struct pci_dma_channel pci_dma_channels[MAX_PCI_DMA_CHANNELS] = {
+ [0 ... MAX_PCI_DMA_CHANNELS-1] = {
+ .busy = ATOMIC_INIT(0),
+ },
+ [0] = { .name = "PCI-DMA 0", .chan = 0, .regs = {
+ .local_addr = SH4_PCIDLA0,
+ .remote_addr = SH4_PCIDPA0,
+ .count = SH4_PCIDTC0,
+ .control = SH4_PCIDCR0,
+ }
+ },
+ [1] = { .name = "PCI-DMA 1", .chan = 1, .regs = {
+ .local_addr = SH4_PCIDLA1,
+ .remote_addr = SH4_PCIDPA1,
+ .count = SH4_PCIDTC1,
+ .control = SH4_PCIDCR1,
+ }
+ },
+ [2] = { .name = "PCI-DMA 2", .chan = 2, .regs = {
+ .local_addr = SH4_PCIDLA2,
+ .remote_addr = SH4_PCIDPA2,
+ .count = SH4_PCIDTC2,
+ .control = SH4_PCIDCR2,
+ }
+ },
+ [3] = { .name = "PCI-DMA 3", .chan = 3, .regs = {
+ .local_addr = SH4_PCIDLA3,
+ .remote_addr = SH4_PCIDPA3,
+ .count = SH4_PCIDTC3,
+ .control = SH4_PCIDCR3,
+ },
+ }
+};
+
+static inline int get_pci_dmte_irq(int channel)
+{
+ if (channel < 0 || channel >= MAX_PCI_DMA_CHANNELS) {
+ return -EINVAL;
+ }
+ return (PCI_DMA_HIGHEST_IRQ - channel);
+}
+
+int request_pci_dma(void)
+{
+ int chan;
+ down(&pci_dma_sem);
+ for (chan = 0; chan < MAX_PCI_DMA_CHANNELS; chan++) {
+ if (!atomic_xchg(&pci_dma_channels[chan].busy, 1)) {
+ return chan;
+ }
+ }
+ return -EINVAL; // never reached
+}
+EXPORT_SYMBOL(request_pci_dma);
+
+int request_pci_dma_noblock(void)
+{
+ int chan;
+ if (!down_trylock(&pci_dma_sem)) {
+ return -EBUSY;
+ }
+ for (chan = 0; chan < MAX_PCI_DMA_CHANNELS; chan++) {
+ if (!atomic_xchg(&pci_dma_channels[chan].busy, 1)) {
+ return chan;
+ }
+ }
+ return -EINVAL; // never reached
+}
+EXPORT_SYMBOL(request_pci_dma_noblock);
+
+void free_pci_dma(int channel)
+{
+ atomic_set(&pci_dma_channels[channel].busy, 0);
+ up(&pci_dma_sem);
+}
+EXPORT_SYMBOL(free_pci_dma);
+
+void pci_dma_start_transfer(int channel, unsigned long local_address,
+ unsigned long remote_address, unsigned long count,
+ int mode)
+{
+ unsigned long control;
+ struct pci_dma_channel * chan = &pci_dma_channels[channel];
+
+ control = (SH4_PCIDCR_ALGN | SH4_PCIDCR_INTM | SH4_PCIDCR_STRT);
+ if (PCI_DMA_TODEVICE = mode) {
+ control |= SH4_PCIDCR_DIR;
+ }
+
+ pci_write_reg(local_address, chan->regs.local_addr);
+ pci_write_reg(remote_address, chan->regs.remote_addr);
+ pci_write_reg(count, chan->regs.count);
+
+ pci_write_reg(control, chan->regs.control);
+}
+EXPORT_SYMBOL(pci_dma_start_transfer);
+
+int pci_dma_wait_for_completion(int channel)
+{
+ int val;
+ down(&pci_dma_channels[channel].transfer_complete);
+ val = pci_read_reg(pci_dma_channels[channel].regs.control) & SH4_PCIDCR_MAST;
+ if (val) {
+ return -EREMOTEIO;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(pci_dma_wait_for_completion);
+
+static irqreturn_t pci_dma_tei_handler(int irq, void * dev_id)
+{
+ int chan = (int)dev_id;
+ // clear the interrupt
+ pci_write_reg(SH4_PCIDCR_INTS, pci_dma_channels[chan].regs.control);
+ if (pci_read_reg(SH4_PCIDCR_INTS) & SH4_PCIDCR_INTS) {
+ printk(KERN_CRIT "PCI: dma error: transfer status incorrect\n");
+ }
+ up(&pci_dma_channels[chan].transfer_complete);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t pci_error_handler(int irq, void * dev_id)
+{
+ unsigned long pciint, pciaint;
+ pciint = pci_read_reg(SH4_PCIINT);
+ pciaint = pci_read_reg(SH4_PCIAINT);
+ printk(KERN_CRIT "PCI: transfer error: PCIINT 0x%08lx, PCIAINT 0x%08lx\n",
+ pciint, pciaint);
+ pci_write_reg(pciint, SH4_PCIINT);
+ pci_write_reg(pciaint, SH4_PCIAINT);
+ return IRQ_HANDLED;
+}
+
+static int __init pci_dma_init(void)
+{
+ int chan;
+
+ sema_init(&pci_dma_sem, MAX_PCI_DMA_CHANNELS);
+
+ /* set pci-dma arbitration mode to round robin */
+ pci_write_reg(SH4_PCIDMABT_RRBN, SH4_PCIDMABT);
+
+ /* setup irq for transfer end */
+ for (chan = 0; chan < MAX_PCI_DMA_CHANNELS; chan++) {
+ sema_init(&pci_dma_channels[chan].transfer_complete, 0);
+ if (request_irq(get_pci_dmte_irq(chan), pci_dma_tei_handler,
+ IRQF_DISABLED, pci_dma_channels[chan].name, (void *)chan)) {
+ printk(KERN_CRIT "PCI: error registering TEI handler\n");
+ return -EINVAL;
+ }
+ }
+
+ if (request_irq(IRQ_PCI_ERROR, pci_error_handler,
+ IRQF_DISABLED, "PCI error", NULL)) {
+ printk(KERN_CRIT "PCI: error registering IRQ_PCI_ERROR\n");
+ goto err_free_irqs;
+ }
+
+ if (request_irq(IRQ_PCI_SYSERROR, pci_error_handler,
+ IRQF_DISABLED, "PCI system error", NULL)) {
+ printk(KERN_CRIT "PCI: error registering IRQ_PCI_SYSERROR\n");
+ goto err_free_irqs_pcierror;
+ }
+
+ return 0;
+
+err_free_irqs_pcierror:
+ free_irq(IRQ_PCI_ERROR, NULL);
+err_free_irqs:
+ for (chan = 0; chan < MAX_PCI_DMA_CHANNELS; chan++) {
+ free_irq(get_pci_dmte_irq(chan), (void *)chan);
+ }
+ return -EINVAL;
+}
+
+static void __exit pci_dma_exit(void)
+{
+ int chan;
+ for (chan = 0; chan < MAX_PCI_DMA_CHANNELS; chan++) {
+ free_irq(get_pci_dmte_irq(chan), (void *)chan);
+ }
+
+ free_irq(IRQ_PCI_ERROR, NULL);
+ free_irq(IRQ_PCI_SYSERROR, NULL);
+
+}
+
+subsys_initcall(pci_dma_init);
+module_exit(pci_dma_exit);
diff --git a/arch/sh/include/cpu-sh4/cpu/pci-dma-sh7751.h
b/arch/sh/include/cpu-sh4/cpu/pci-dma-sh7751.h
new file mode 100755
index 0000000..57f8c47
--- /dev/null
+++ b/arch/sh/include/cpu-sh4/cpu/pci-dma-sh7751.h
@@ -0,0 +1,25 @@
+/*
+ * PCI DMA operaions for SH7751, SH7751R.
+ *
+ * 2008 Roni Feldman
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License v2. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+/* Requests a PCI-DMA channel. Can block. */
+int request_pci_dma(void);
+
+/* Requests a PCI-DMA channel. Returns if there are non free. */
+int request_pci_dma_noblock(void);
+
+void free_pci_dma(int channel);
+
+/* Activates the DMA transfer. Does not block. */
+void pci_dma_start_transfer(int channel, unsigned long local_address,
+ unsigned long remote_address, unsigned long count,
+ int mode);
+
+/* Blocks until the transfer is done. */
+int pci_dma_wait_for_completion(int channel);
^ permalink raw reply related [flat|nested] 5+ messages in thread* Re: [RFC] sh7751(r): Adding support for PCI-DMA
2008-09-26 11:50 [RFC] sh7751(r): Adding support for PCI-DMA Roni Feldman
@ 2008-09-29 11:49 ` Paul Mundt
2008-09-29 21:02 ` Roni Feldman
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Paul Mundt @ 2008-09-29 11:49 UTC (permalink / raw)
To: linux-sh
On Fri, Sep 26, 2008 at 02:50:10PM +0300, Roni Feldman wrote:
> I've added very basic PCI-DMA support for the sh7751's. I'll be happy
> to see if this works on different machines since it has been tested
> only on one platform. Also, it would be interesting to know if there
> are other SH-4(A) devices that support this.
>
Before really being able to comment on this, how exactly are you
attempting to use this exposed API? Are you attempting to tie this in to
the DMA mapping ops?
^ permalink raw reply [flat|nested] 5+ messages in thread* Re: [RFC] sh7751(r): Adding support for PCI-DMA
2008-09-26 11:50 [RFC] sh7751(r): Adding support for PCI-DMA Roni Feldman
2008-09-29 11:49 ` Paul Mundt
@ 2008-09-29 21:02 ` Roni Feldman
2008-09-30 1:56 ` Paul Mundt
2008-10-04 16:14 ` Roni Feldman
3 siblings, 0 replies; 5+ messages in thread
From: Roni Feldman @ 2008-09-29 21:02 UTC (permalink / raw)
To: linux-sh
Hey Paul,
The code only supplies a transfer mechanism, that is - a way to start
it and a way to be notified when it ends. If I'm not mistaken the
PCI-DMA mapping thing is supposed to behave like any other DMA
mechanism in the system. You just use pci_map_*, do a transfer, and
then pci_unmap_* it. That's what I do in my code, at least.
On Mon, Sep 29, 2008 at 2:49 PM, Paul Mundt <lethal@linux-sh.org> wrote:
> On Fri, Sep 26, 2008 at 02:50:10PM +0300, Roni Feldman wrote:
>> I've added very basic PCI-DMA support for the sh7751's. I'll be happy
>> to see if this works on different machines since it has been tested
>> only on one platform. Also, it would be interesting to know if there
>> are other SH-4(A) devices that support this.
>>
> Before really being able to comment on this, how exactly are you
> attempting to use this exposed API? Are you attempting to tie this in to
> the DMA mapping ops?
>
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [RFC] sh7751(r): Adding support for PCI-DMA
2008-09-26 11:50 [RFC] sh7751(r): Adding support for PCI-DMA Roni Feldman
2008-09-29 11:49 ` Paul Mundt
2008-09-29 21:02 ` Roni Feldman
@ 2008-09-30 1:56 ` Paul Mundt
2008-10-04 16:14 ` Roni Feldman
3 siblings, 0 replies; 5+ messages in thread
From: Paul Mundt @ 2008-09-30 1:56 UTC (permalink / raw)
To: linux-sh
On Tue, Sep 30, 2008 at 12:02:57AM +0300, Roni Feldman wrote:
> Hey Paul,
>
> The code only supplies a transfer mechanism, that is - a way to start
> it and a way to be notified when it ends. If I'm not mistaken the
> PCI-DMA mapping thing is supposed to behave like any other DMA
> mechanism in the system. You just use pci_map_*, do a transfer, and
> then pci_unmap_* it. That's what I do in my code, at least.
>
Yes, but the actual user of this code is missing from your patch, which
is why I was wondering how you were attempting to use it. If you can post
the patch for that also, then we can look at what needs to be done for
tying it in generically.
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [RFC] sh7751(r): Adding support for PCI-DMA
2008-09-26 11:50 [RFC] sh7751(r): Adding support for PCI-DMA Roni Feldman
` (2 preceding siblings ...)
2008-09-30 1:56 ` Paul Mundt
@ 2008-10-04 16:14 ` Roni Feldman
3 siblings, 0 replies; 5+ messages in thread
From: Roni Feldman @ 2008-10-04 16:14 UTC (permalink / raw)
To: linux-sh
Ah, I see. I'm using this on a custom platform as a mechanism for
message delivery between the PC and the card on which the SH-4 sits.
But I get your point -- if nobody besides me seems to need it / use
it, then there's no need to add it to the tree.
As for "generic tying" -- from what I've seen, most of the PCI
mechanisms in the kernel are concerned with memory/PCI exposure, and
not the act of transfer itself, since most of the time the CPU doesn't
do the transfer. I couldn't think of a good way to insert this into
the existing DMA code.
If you want a simple example:
---
u32 val;
int retval;
dma_addr_t dma_addr;
int chan;
val = 0xAABBCCDD;
chan = request_pci_dma();
dma_addr = pci_map_single(NULL, (void *)&val, sizeof(val), PCI_DMA_TODEVICE);
pci_dma_start_transfer(chan, dma_addr, SOME_PCI_ADDRESS, sizeof(val),
PCI_DMA_TODEVICE);
retval = pci_dma_wait_for_completion(chan);
free_pci_dma(chan);
pci_unmap_single ...
---
Anyway, feel free to put it on hold until somebody finds use for it.
Roni.
On Tue, Sep 30, 2008 at 4:56 AM, Paul Mundt <lethal@linux-sh.org> wrote:
>
> On Tue, Sep 30, 2008 at 12:02:57AM +0300, Roni Feldman wrote:
> > Hey Paul,
> >
> > The code only supplies a transfer mechanism, that is - a way to start
> > it and a way to be notified when it ends. If I'm not mistaken the
> > PCI-DMA mapping thing is supposed to behave like any other DMA
> > mechanism in the system. You just use pci_map_*, do a transfer, and
> > then pci_unmap_* it. That's what I do in my code, at least.
> >
> Yes, but the actual user of this code is missing from your patch, which
> is why I was wondering how you were attempting to use it. If you can post
> the patch for that also, then we can look at what needs to be done for
> tying it in generically.
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2008-10-04 16:14 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-09-26 11:50 [RFC] sh7751(r): Adding support for PCI-DMA Roni Feldman
2008-09-29 11:49 ` Paul Mundt
2008-09-29 21:02 ` Roni Feldman
2008-09-30 1:56 ` Paul Mundt
2008-10-04 16:14 ` Roni Feldman
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox