Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / arch / sparc / kernel / ebus.c
diff --git a/kernel/arch/sparc/kernel/ebus.c b/kernel/arch/sparc/kernel/ebus.c
new file mode 100644 (file)
index 0000000..acf8314
--- /dev/null
@@ -0,0 +1,256 @@
+/* ebus.c: EBUS DMA library code.
+ *
+ * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
+ * Copyright (C) 1999  David S. Miller (davem@redhat.com)
+ */
+
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+
+#include <asm/ebus_dma.h>
+#include <asm/io.h>
+
+#define EBDMA_CSR      0x00UL  /* Control/Status */
+#define EBDMA_ADDR     0x04UL  /* DMA Address */
+#define EBDMA_COUNT    0x08UL  /* DMA Count */
+
+#define EBDMA_CSR_INT_PEND     0x00000001
+#define EBDMA_CSR_ERR_PEND     0x00000002
+#define EBDMA_CSR_DRAIN                0x00000004
+#define EBDMA_CSR_INT_EN       0x00000010
+#define EBDMA_CSR_RESET                0x00000080
+#define EBDMA_CSR_WRITE                0x00000100
+#define EBDMA_CSR_EN_DMA       0x00000200
+#define EBDMA_CSR_CYC_PEND     0x00000400
+#define EBDMA_CSR_DIAG_RD_DONE 0x00000800
+#define EBDMA_CSR_DIAG_WR_DONE 0x00001000
+#define EBDMA_CSR_EN_CNT       0x00002000
+#define EBDMA_CSR_TC           0x00004000
+#define EBDMA_CSR_DIS_CSR_DRN  0x00010000
+#define EBDMA_CSR_BURST_SZ_MASK        0x000c0000
+#define EBDMA_CSR_BURST_SZ_1   0x00080000
+#define EBDMA_CSR_BURST_SZ_4   0x00000000
+#define EBDMA_CSR_BURST_SZ_8   0x00040000
+#define EBDMA_CSR_BURST_SZ_16  0x000c0000
+#define EBDMA_CSR_DIAG_EN      0x00100000
+#define EBDMA_CSR_DIS_ERR_PEND 0x00400000
+#define EBDMA_CSR_TCI_DIS      0x00800000
+#define EBDMA_CSR_EN_NEXT      0x01000000
+#define EBDMA_CSR_DMA_ON       0x02000000
+#define EBDMA_CSR_A_LOADED     0x04000000
+#define EBDMA_CSR_NA_LOADED    0x08000000
+#define EBDMA_CSR_DEV_ID_MASK  0xf0000000
+
+#define EBUS_DMA_RESET_TIMEOUT 10000
+
+static void __ebus_dma_reset(struct ebus_dma_info *p, int no_drain)
+{
+       int i;
+       u32 val = 0;
+
+       writel(EBDMA_CSR_RESET, p->regs + EBDMA_CSR);
+       udelay(1);
+
+       if (no_drain)
+               return;
+
+       for (i = EBUS_DMA_RESET_TIMEOUT; i > 0; i--) {
+               val = readl(p->regs + EBDMA_CSR);
+
+               if (!(val & (EBDMA_CSR_DRAIN | EBDMA_CSR_CYC_PEND)))
+                       break;
+               udelay(10);
+       }
+}
+
+static irqreturn_t ebus_dma_irq(int irq, void *dev_id)
+{
+       struct ebus_dma_info *p = dev_id;
+       unsigned long flags;
+       u32 csr = 0;
+
+       spin_lock_irqsave(&p->lock, flags);
+       csr = readl(p->regs + EBDMA_CSR);
+       writel(csr, p->regs + EBDMA_CSR);
+       spin_unlock_irqrestore(&p->lock, flags);
+
+       if (csr & EBDMA_CSR_ERR_PEND) {
+               printk(KERN_CRIT "ebus_dma(%s): DMA error!\n", p->name);
+               p->callback(p, EBUS_DMA_EVENT_ERROR, p->client_cookie);
+               return IRQ_HANDLED;
+       } else if (csr & EBDMA_CSR_INT_PEND) {
+               p->callback(p,
+                           (csr & EBDMA_CSR_TC) ?
+                           EBUS_DMA_EVENT_DMA : EBUS_DMA_EVENT_DEVICE,
+                           p->client_cookie);
+               return IRQ_HANDLED;
+       }
+
+       return IRQ_NONE;
+
+}
+
+int ebus_dma_register(struct ebus_dma_info *p)
+{
+       u32 csr;
+
+       if (!p->regs)
+               return -EINVAL;
+       if (p->flags & ~(EBUS_DMA_FLAG_USE_EBDMA_HANDLER |
+                        EBUS_DMA_FLAG_TCI_DISABLE))
+               return -EINVAL;
+       if ((p->flags & EBUS_DMA_FLAG_USE_EBDMA_HANDLER) && !p->callback)
+               return -EINVAL;
+       if (!strlen(p->name))
+               return -EINVAL;
+
+       __ebus_dma_reset(p, 1);
+
+       csr = EBDMA_CSR_BURST_SZ_16 | EBDMA_CSR_EN_CNT;
+
+       if (p->flags & EBUS_DMA_FLAG_TCI_DISABLE)
+               csr |= EBDMA_CSR_TCI_DIS;
+
+       writel(csr, p->regs + EBDMA_CSR);
+
+       return 0;
+}
+EXPORT_SYMBOL(ebus_dma_register);
+
+int ebus_dma_irq_enable(struct ebus_dma_info *p, int on)
+{
+       unsigned long flags;
+       u32 csr;
+
+       if (on) {
+               if (p->flags & EBUS_DMA_FLAG_USE_EBDMA_HANDLER) {
+                       if (request_irq(p->irq, ebus_dma_irq, IRQF_SHARED, p->name, p))
+                               return -EBUSY;
+               }
+
+               spin_lock_irqsave(&p->lock, flags);
+               csr = readl(p->regs + EBDMA_CSR);
+               csr |= EBDMA_CSR_INT_EN;
+               writel(csr, p->regs + EBDMA_CSR);
+               spin_unlock_irqrestore(&p->lock, flags);
+       } else {
+               spin_lock_irqsave(&p->lock, flags);
+               csr = readl(p->regs + EBDMA_CSR);
+               csr &= ~EBDMA_CSR_INT_EN;
+               writel(csr, p->regs + EBDMA_CSR);
+               spin_unlock_irqrestore(&p->lock, flags);
+
+               if (p->flags & EBUS_DMA_FLAG_USE_EBDMA_HANDLER) {
+                       free_irq(p->irq, p);
+               }
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(ebus_dma_irq_enable);
+
+void ebus_dma_unregister(struct ebus_dma_info *p)
+{
+       unsigned long flags;
+       u32 csr;
+       int irq_on = 0;
+
+       spin_lock_irqsave(&p->lock, flags);
+       csr = readl(p->regs + EBDMA_CSR);
+       if (csr & EBDMA_CSR_INT_EN) {
+               csr &= ~EBDMA_CSR_INT_EN;
+               writel(csr, p->regs + EBDMA_CSR);
+               irq_on = 1;
+       }
+       spin_unlock_irqrestore(&p->lock, flags);
+
+       if (irq_on)
+               free_irq(p->irq, p);
+}
+EXPORT_SYMBOL(ebus_dma_unregister);
+
+int ebus_dma_request(struct ebus_dma_info *p, dma_addr_t bus_addr, size_t len)
+{
+       unsigned long flags;
+       u32 csr;
+       int err;
+
+       if (len >= (1 << 24))
+               return -EINVAL;
+
+       spin_lock_irqsave(&p->lock, flags);
+       csr = readl(p->regs + EBDMA_CSR);
+       err = -EINVAL;
+       if (!(csr & EBDMA_CSR_EN_DMA))
+               goto out;
+       err = -EBUSY;
+       if (csr & EBDMA_CSR_NA_LOADED)
+               goto out;
+
+       writel(len,      p->regs + EBDMA_COUNT);
+       writel(bus_addr, p->regs + EBDMA_ADDR);
+       err = 0;
+
+out:
+       spin_unlock_irqrestore(&p->lock, flags);
+
+       return err;
+}
+EXPORT_SYMBOL(ebus_dma_request);
+
+void ebus_dma_prepare(struct ebus_dma_info *p, int write)
+{
+       unsigned long flags;
+       u32 csr;
+
+       spin_lock_irqsave(&p->lock, flags);
+       __ebus_dma_reset(p, 0);
+
+       csr = (EBDMA_CSR_INT_EN |
+              EBDMA_CSR_EN_CNT |
+              EBDMA_CSR_BURST_SZ_16 |
+              EBDMA_CSR_EN_NEXT);
+
+       if (write)
+               csr |= EBDMA_CSR_WRITE;
+       if (p->flags & EBUS_DMA_FLAG_TCI_DISABLE)
+               csr |= EBDMA_CSR_TCI_DIS;
+
+       writel(csr, p->regs + EBDMA_CSR);
+
+       spin_unlock_irqrestore(&p->lock, flags);
+}
+EXPORT_SYMBOL(ebus_dma_prepare);
+
+unsigned int ebus_dma_residue(struct ebus_dma_info *p)
+{
+       return readl(p->regs + EBDMA_COUNT);
+}
+EXPORT_SYMBOL(ebus_dma_residue);
+
+unsigned int ebus_dma_addr(struct ebus_dma_info *p)
+{
+       return readl(p->regs + EBDMA_ADDR);
+}
+EXPORT_SYMBOL(ebus_dma_addr);
+
+void ebus_dma_enable(struct ebus_dma_info *p, int on)
+{
+       unsigned long flags;
+       u32 orig_csr, csr;
+
+       spin_lock_irqsave(&p->lock, flags);
+       orig_csr = csr = readl(p->regs + EBDMA_CSR);
+       if (on)
+               csr |= EBDMA_CSR_EN_DMA;
+       else
+               csr &= ~EBDMA_CSR_EN_DMA;
+       if ((orig_csr & EBDMA_CSR_EN_DMA) !=
+           (csr & EBDMA_CSR_EN_DMA))
+               writel(csr, p->regs + EBDMA_CSR);
+       spin_unlock_irqrestore(&p->lock, flags);
+}
+EXPORT_SYMBOL(ebus_dma_enable);