/*
* Copyright 2015 Robert Jarzmik <robert.jarzmik@free.fr>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/err.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <linux/dmaengine.h>
#include <linux/platform_device.h>
#include <linux/device.h>
#include <linux/platform_data/mmp_dma.h>
#include <linux/dmapool.h>
#include <linux/of_device.h>
#include <linux/of_dma.h>
#include <linux/of.h>
#include <linux/wait.h>
#include <linux/dma/pxa-dma.h>
#include "dmaengine.h"
#include "virt-dma.h"
#define DCSR(n) (0x0000 + ((n) << 2))
#define DALGN(n) 0x00a0
#define DINT 0x00f0
#define DDADR(n) (0x0200 + ((n) << 4))
#define DSADR(n) (0x0204 + ((n) << 4))
#define DTADR(n) (0x0208 + ((n) << 4))
#define DCMD(n) (0x020c + ((n) << 4))
#define PXA_DCSR_RUN BIT(31) /* Run Bit (read / write) */
#define PXA_DCSR_NODESC BIT(30) /* No-Descriptor Fetch (read / write) */
#define PXA_DCSR_STOPIRQEN BIT(29) /* Stop Interrupt Enable (R/W) */
#define PXA_DCSR_REQPEND BIT(8) /* Request Pending (read-only) */
#define PXA_DCSR_STOPSTATE BIT(3) /* Stop State (read-only) */
#define PXA_DCSR_ENDINTR BIT(2) /* End Interrupt (read / write) */
#define PXA_DCSR_STARTINTR BIT(1) /* Start Interrupt (read / write) */
#define PXA_DCSR_BUSERR BIT(0) /* Bus Error Interrupt (read / write) */
#define PXA_DCSR_EORIRQEN BIT(28) /* End of Receive IRQ Enable (R/W) */
#define PXA_DCSR_EORJMPEN BIT(27) /* Jump to next descriptor on EOR */
#define PXA_DCSR_EORSTOPEN BIT(26) /* STOP on an EOR */
#define PXA_DCSR_SETCMPST BIT(25) /* Set Descriptor Compare Status */
#define PXA_DCSR_CLRCMPST BIT(24) /* Clear Descriptor Compare Status */
#define PXA_DCSR_CMPST BIT(10) /* The Descriptor Compare Status */
#define PXA_DCSR_EORINTR BIT(9) /* The end of Receive */
#define DRCMR_MAPVLD BIT(7) /* Map Valid (read / write) */
#define DRCMR_CHLNUM 0x1f /* mask for Channel Number (read / write) */
#define DDADR_DESCADDR 0xfffffff0 /* Address of next descriptor (mask) */
#define DDADR_STOP BIT(0) /* Stop (read / write) */
#define PXA_DCMD_INCSRCADDR BIT(31) /* Source Address Increment Setting. */
#define PXA_DCMD_INCTRGADDR BIT(30) /* Target Address Increment Setting. */
#define PXA_DCMD_FLOWSRC BIT(29) /* Flow Control by the source. */
#define PXA_DCMD_FLOWTRG BIT(28) /* Flow Control by the target. */
#define PXA_DCMD_STARTIRQEN BIT(22) /* Start Interrupt Enable */
#define PXA_DCMD_ENDIRQEN BIT(21) /* End Interrupt Enable */
#define PXA_DCMD_ENDIAN BIT(18) /* Device Endian-ness. */
#define PXA_DCMD_BURST8 (1 << 16) /* 8 byte burst */
#define PXA_DCMD_BURST16 (2 << 16) /* 16 byte burst */
#define PXA_DCMD_BURST32 (3 << 16) /* 32 byte burst */
#define PXA_DCMD_WIDTH1 (1 << 14) /* 1 byte width */
#define PXA_DCMD_WIDTH2 (2 << 14) /* 2 byte width (HalfWord) */
#define PXA_DCMD_WIDTH4 (3 << 14) /* 4 byte width (Word) */
#define PXA_DCMD_LENGTH 0x01fff /* length mask (max = 8K - 1) */
#define PDMA_ALIGNMENT 3
#define PDMA_MAX_DESC_BYTES (PXA_DCMD_LENGTH & ~((1 << PDMA_ALIGNMENT) - 1))
struct pxad_desc_hw {
u32 ddadr; /* Points to the next descriptor + flags */
u32 dsadr; /* DSADR value for the current transfer */
u32 dtadr; /* DTADR value for the current transfer */
u32 dcmd; /* DCMD value for the current transfer */
} __aligned(16);
struct pxad_desc_sw {
struct virt_dma_desc vd; /* Virtual descriptor */
int nb_desc; /* Number of hw. descriptors */
size_t len; /* Number of bytes xfered */
dma_addr_t first; /* First descriptor's addr */
/* At least one descriptor has an src/dst address not multiple of 8 */
bool misaligned;
bool cyclic;
struct dma_pool *desc_pool; /* Channel's used allocator */
struct pxad_desc_hw *hw_desc[]; /* DMA coherent descriptors */
};
struct pxad_phy {
int idx;
void __iomem *base;
struct pxad_chan *vchan;
};
struct pxad_chan {
struct virt_dma_chan vc; /* Virtual channel */
u32 drcmr; /* Requestor of the channel */
enum pxad_chan_prio prio; /* Required priority of phy */
/*
* At least one desc_sw in submitted or issued transfers on this channel
* has one address such as: addr % 8 != 0. This implies the DALGN
* setting on the phy.
*/
bool misaligned;
struct dma_slave_config cfg; /* Runtime config */
/* protected by vc->lock */
struct pxad_phy *phy;
struct dma_pool *desc_pool; /* Descriptors pool */
dma_cookie_t bus_error;
wait_queue_head_t wq_state;
};
struct pxad_device {
struct dma_device slave;
int nr_chans;
int nr_requestors;
void __iomem *base;
struct pxad_phy *phys;
spinlock_t phy_lock; /* Phy association */
#ifdef CONFIG_DEBUG_FS
struct dentry *dbgfs_root;
struct dentry *dbgfs_state;
struct dentry **dbgfs_chan;
#endif
};
#define tx_to_pxad_desc(tx) \
container_of(tx, struct pxad_desc_sw, async_tx)
#define to_pxad_chan(dchan) \
container_of(dchan, struct pxad_chan, vc.chan)
#define to_pxad_dev(dmadev) \
container_of(dmadev, struct pxad_device, slave)
#define to_pxad_sw_desc(_vd) \
container_of((_vd), struct pxad_desc_sw, vd)
#define _phy_readl_relaxed(phy, _reg) \
readl_relaxed((phy)->base + _reg((phy)->idx))
#define phy_readl_relaxed(phy, _reg) \
({ \
u32 _v; \
_v = readl_relaxed((phy)->base + _reg((phy)->idx)); \
dev_vdbg(&phy->vchan->vc.chan.dev->device, \
"%s(): readl(%s): 0x%08x\n", __func__, #_reg, \
_v); \
_v; \
})
#define phy_writel(phy, val, _reg) \
do { \
writel((val), (phy)->base + _reg((phy)->idx)); \
dev_vdbg(&phy->vchan->vc.chan.dev->device, \
"%s(): writel(0x%08x, %s)\n", \
__func__, (u32)(val), #_reg); \
} while (0)
#define phy_writel_relaxed(phy, val, _reg) \
do { \
writel_relaxed((val), (phy)->base + _reg((phy)->idx)); \
dev_vdbg(&phy->vchan->vc.chan.dev->device, \
"%s(): writel_relaxed(0x%08x, %s)\n", \
__func__, (u32)(val), #_reg); \
} while (0)
static unsigned int pxad_drcmr(unsigned int line)
{
if (line < 64)
return 0x100 + line * 4;
return 0x1000 + line * 4;
}
/*
* Debug fs
*/
#ifdef CONFIG_DEBUG_FS
#include <linux/debugfs.h>
#include <linux/uaccess.h>
#include <linux/seq_file.h>
static int dbg_show_requester_chan(struct seq_file *s, void *p)
{
struct pxad_phy *phy = s->private;
int i;
u32 drcmr;
seq_printf(s, "DMA channel %d requester :\n", phy->idx);
for (i = 0; i < 70; i++) {
drcmr = readl_relaxed(phy->base + pxad_drcmr(i));
if ((drcmr & DRCMR_CHLNUM) == phy->idx)
seq_printf(s, "\tRequester %d (MAPVLD=%d)\n", i,
!!(drcmr & DRCMR_MAPVLD));
}
return 0;
}
static inline int dbg_burst_from_dcmd(u32 dcmd)
{
int burst = (dcmd >> 16) & 0x3;
return burst ? 4 << burst : 0;
}
static int is_phys_valid(unsigned long addr)
{
return pfn_valid(__phys_to_pfn(addr));
}
#define PXA_DCSR_
|