// SPDX-License-Identifier: GPL-2.0
/*
* Renesas RZ/G2L DMA Controller Driver
*
* Based on imx-dma.c
*
* Copyright (C) 2021 Renesas Electronics Corp.
* Copyright 2010 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
* Copyright 2012 Javier Martin, Vista Silicon <javier.martin@vista-silicon.com>
*/
#include <linux/bitfield.h>
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
#include <linux/interrupt.h>
#include <linux/iopoll.h>
#include <linux/irqchip/irq-renesas-rzv2h.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_dma.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include "../dmaengine.h"
#include "../virt-dma.h"
enum rz_dmac_prep_type {
RZ_DMAC_DESC_MEMCPY,
RZ_DMAC_DESC_SLAVE_SG,
};
struct rz_lmdesc {
u32 header;
u32 sa;
u32 da;
u32 tb;
u32 chcfg;
u32 chitvl;
u32 chext;
u32 nxla;
};
struct rz_dmac_desc {
struct virt_dma_desc vd;
dma_addr_t src;
dma_addr_t dest;
size_t len;
struct list_head node;
enum dma_transfer_direction direction;
enum rz_dmac_prep_type type;
/* For slave sg */
struct scatterlist *sg;
unsigned int sgcount;
};
#define to_rz_dmac_desc(d) container_of(d, struct rz_dmac_desc, vd)
struct rz_dmac_chan {
struct virt_dma_chan vc;
void __iomem *ch_base;
void __iomem *ch_cmn_base;
unsigned int index;
int irq;
struct rz_dmac_desc *desc;
int descs_allocated;
dma_addr_t src_per_address;
dma_addr_t dst_per_address;
u32 chcfg;
u32 chctrl;
int mid_rid;
struct list_head ld_free;
struct list_head ld_queue;
struct list_head ld_active;
struct {
struct rz_lmdesc *base;
struct rz_lmdesc *head;
struct rz_lmdesc *tail;
dma_addr_t base_dma;
} lmdesc;
};
#define to_rz_dmac_chan(c) container_of(c, struct rz_dmac_chan, vc.chan)
struct rz_dmac_icu {
struct platform_device *pdev;
u8 dmac_index;
};
struct rz_dmac {
struct dma_device engine;
struct rz_dmac_icu icu;
struct device *dev;
struct reset_control *rstc;
void __iomem *base;
void __iomem *ext_base;
unsigned int n_channels;
struct rz_dmac_chan *channels;
bool has_icu;
DECLARE_BITMAP(modules, 1024);
};
#define to_rz_dmac(d) container_of(d, struct rz_dmac, engine)
/*
* -----------------------------------------------------------------------------
* Registers
*/
#define CHSTAT 0x0024
#define CHCTRL 0x0028
#define CHCFG 0x002c
#define NXLA 0x0038
#define DCTRL 0x0000
#define EACH_CHANNEL_OFFSET 0x0040
#define CHANNEL_0_7_OFFSET 0x0000
#define CHANNEL_0_7_COMMON_BASE 0x0300
#define CHANNEL_8_15_OFFSET 0x0400
#define CHANNEL_8_15_COMMON_BASE 0x0700
#define CHSTAT_ER BIT(4)
#define CHSTAT_EN BIT(0)
#define CHCTRL_CLRINTMSK BIT(17)
#define CHCTRL_CLRSUS BIT(9)
#define CHCTRL_CLRTC BIT(6)
#define CHCTRL_CLREND BIT(5)
#define CHCTRL_CLRRQ BIT(4)
#define CHCTRL_SWRST BIT(3)
#define CHCTRL_STG BIT(2)
#define CHCTRL_CLREN BIT(1)
#define CHCTRL_SETEN BIT(0)
#define CHCTRL_DEFAULT (CHCTRL_CLRINTMSK | CHCTRL_CLRSUS | \
CHCTRL_CLRTC | CHCTRL_CLREND | \
CHCTRL_CLRRQ | CHCTRL_SWRST | \
CHCTRL_CLREN)
#define CHCFG_DMS BIT(31)
#define CHCFG_DEM BIT(24)
#define CHCFG_DAD BIT(21)
#define CHCFG_SAD BIT(20)
#define CHCFG_REQD BIT(3)
#define CHCFG_SEL(bits) ((bits) & 0x07)
#define CHCFG_MEM_COPY (0x80400008)
#define CHCFG_FILL_DDS_MASK GENMASK(19, 16)
#define CHCFG_FILL_SDS_MASK GENMASK(15, 12)
#define CHCFG_FILL_TM(a) (((a) & BIT(5)) << 22)
#define CHCFG_FILL_AM(a) (((a) & GENMASK(4, 2)) << 6)
#define CHCFG_FILL_LVL(a) (((a) & BIT(1)) << 5)
#define CHCFG_FILL_HIEN(a) (((a) & BIT(0)) << 5)
#define MID_RID_MASK GENMASK(9, 0)
#define CHCFG_MASK GENMASK(15, 10)
#define CHCFG_DS_INVALID 0xFF
#define DCTRL_LVINT BIT(1)
#define DCTRL_PR BIT(0)
#define DCTRL_DEFAULT (DCTRL_LVINT | DCTRL_PR)
/* LINK MODE DESCRIPTOR */
#define HEADER_LV BIT(0)
#define RZ_DMAC_MAX_CHAN_DESCRIPTORS 16
#define RZ_DMAC_MAX_CHANNELS 16
#define DMAC_NR_LMDESC 64
/* RZ/V2H ICU related */
#define RZV2H_MAX_DMAC_INDEX 4
/*
* -----------------------------------------------------------------------------
* Device access
*/
static void rz_dmac_writel(struct rz_dmac *dmac, unsigned int val,
unsigned int offset)
{
writel(val, dmac->base + offset);
}
static void rz_dmac_ext_writel(struct rz_dmac *dmac, unsigned int val,
unsigned int offset)
{
writel(val, dmac->ext_base + offset);
}
static u32 rz_dmac_ext_readl(struct rz_dmac *dmac, unsigned int offset)
{
return readl(dmac->