// SPDX-License-Identifier: GPL-2.0+
//
// Copyright (c) 2013-2014 Freescale Semiconductor, Inc
// Copyright (c) 2017 Sysam, Angelo Dureghello <angelo@sysam.it>
#include <linux/cleanup.h>
#include <linux/clk.h>
#include <linux/dmapool.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/dma-mapping.h>
#include <linux/pm_runtime.h>
#include <linux/pm_domain.h>
#include "fsl-edma-common.h"
#define EDMA_CR 0x00
#define EDMA_ES 0x04
#define EDMA_ERQ 0x0C
#define EDMA_EEI 0x14
#define EDMA_SERQ 0x1B
#define EDMA_CERQ 0x1A
#define EDMA_SEEI 0x19
#define EDMA_CEEI 0x18
#define EDMA_CINT 0x1F
#define EDMA_CERR 0x1E
#define EDMA_SSRT 0x1D
#define EDMA_CDNE 0x1C
#define EDMA_INTR 0x24
#define EDMA_ERR 0x2C
#define EDMA64_ERQH 0x08
#define EDMA64_EEIH 0x10
#define EDMA64_SERQ 0x18
#define EDMA64_CERQ 0x19
#define EDMA64_SEEI 0x1a
#define EDMA64_CEEI 0x1b
#define EDMA64_CINT 0x1c
#define EDMA64_CERR 0x1d
#define EDMA64_SSRT 0x1e
#define EDMA64_CDNE 0x1f
#define EDMA64_INTH 0x20
#define EDMA64_INTL 0x24
#define EDMA64_ERRH 0x28
#define EDMA64_ERRL 0x2c
void fsl_edma_tx_chan_handler(struct fsl_edma_chan *fsl_chan)
{
spin_lock(&fsl_chan->vchan.lock);
if (!fsl_chan->edesc) {
/* terminate_all called before */
spin_unlock(&fsl_chan->vchan.lock);
return;
}
if (!fsl_chan->edesc->iscyclic) {
list_del(&fsl_chan->edesc->vdesc.node);
vchan_cookie_complete(&fsl_chan->edesc->vdesc);
fsl_chan->edesc = NULL;
fsl_chan->status = DMA_COMPLETE;
} else {
vchan_cyclic_callback(&fsl_chan->edesc->vdesc);
}
if (!fsl_chan->edesc)
fsl_edma_xfer_desc(fsl_chan);
spin_unlock(&fsl_chan->vchan.lock);
}
static void fsl_edma3_enable_request(struct fsl_edma_chan *fsl_chan)
{
u32 val, flags;
flags = fsl_edma_drvflags(fsl_chan);
val = edma_readl_chreg(fsl_chan, ch_sbr);
if (fsl_chan->is_rxchan)
val |= EDMA_V3_CH_SBR_RD;
else
val |= EDMA_V3_CH_SBR_WR;
if (fsl_chan->is_remote)
val &= ~(EDMA_V3_CH_SBR_RD | EDMA_V3_CH_SBR_WR);
edma_writel_chreg(fsl_chan, val, ch_sbr);
if (flags & FSL_EDMA_DRV_HAS_CHMUX) {
/*
* ch_mux: With the exception of 0, attempts to write a value
* already in use will be forced to 0.
*/
if (!edma_readl(fsl_chan->edma, fsl_chan->mux_addr))
edma_writel(fsl_chan->edma, fsl_chan->srcid, fsl_chan->mux_addr);
}
val = edma_readl_chreg(fsl_chan, ch_csr);
val |= EDMA_V3_CH_CSR_ERQ | EDMA_V3_CH_CSR_EEI;
edma_writel_chreg(fsl_chan, val, ch_csr);
}
static void fsl_edma_enable_request(struct fsl_edma_chan *fsl_chan)
{
struct edma_regs *regs = &fsl_chan->edma->regs;
u32 ch = fsl_chan->vchan.chan.chan_id;
if (fsl_edma_drvflags(fsl_chan) & FSL_EDMA_DRV_SPLIT_REG)
return fsl_edma3_enable_request(fsl_chan);
if (fsl_chan->edma->drvdata->flags & FSL_EDMA_DRV_WRAP_IO) {
edma_writeb(fsl_chan->edma, EDMA_SEEI_SEEI(ch), regs->seei);
edma_writeb(fsl_chan->edma, ch, regs->serq);
} else {
/* ColdFire is big endian, and accesses natively
* big endian I/O peripherals
*/
iowrite8(EDMA_SEEI_SEEI(ch), regs->seei);
iowrite8(ch, regs->serq);
}
}
static void fsl_edma3_disable_request(struct fsl_edma_chan *fsl_chan)
{
u32 val = edma_readl_chreg(fsl_chan, ch_csr);
u32 flags;
flags = fsl_edma_drvflags(fsl_chan);
if (flags & FSL_EDMA_DRV_HAS_CHMUX)
edma_writel(fsl_chan->edma, 0, fsl_chan->mux_addr);
val &= ~EDMA_V3_CH_CSR_ERQ;
edma_writel_chreg(fsl_chan, val, ch_csr);
}
void fsl_edma_disable_request(struct fsl_edma_chan *fsl_chan)
{
struct edma_regs *regs = &fsl_chan->edma->regs;
u32 ch = fsl_chan->vchan.chan.chan_id;
if (fsl_edma_drvflags(fsl_chan) & FSL_EDMA_DRV_SPLIT_REG)
return fsl_edma3_disable_request(fsl_chan);
if (fsl_chan->edma->drvdata->flags & FSL_EDMA_DRV_WRAP_IO) {
edma_writeb(fsl_chan->edma, ch, regs->cerq);
edma_writeb(fsl_chan->edma, EDMA_CEEI_CEEI(ch), regs->ceei);
} else {
/* ColdFire is big endian, and accesses natively
* big endian I/O peripherals
*/
iowrite8(ch, regs->cerq);
iowrite8(EDMA_CEEI_CEEI(ch), regs->ceei);
}
}
static void mux_configure8(struct fsl_edma_chan *fsl_chan, void __iomem *addr,
u32 off, u32 slot, bool enable)
{
u8 val8;
if (enable)
val8 = EDMAMUX_CHCFG_ENBL |