#include <sys/param.h>
#include <sys/systm.h>
#include <machine/fdt.h>
#include <armv7/armv7/armv7var.h>
#include <armv7/omap/prcmvar.h>
#include <armv7/omap/edmavar.h>
#include <dev/ofw/openfirm.h>
#include <dev/ofw/fdt.h>
#define DEVNAME(s) ((s)->sc_dev.dv_xname)
struct edma_softc {
struct device sc_dev;
bus_space_tag_t sc_iot;
bus_space_handle_t sc_tpcc;
void *sc_ih_comp;
edma_intr_cb_t sc_intr_cb[64];
void *sc_intr_dat[64];
};
#define EDMA_NUM_DMA_CHANS 64
#define EDMA_NUM_QDMA_CHANS 8
#define EDMA_TPCC_DHCM(x) (0x100 + (x * 4))
#define EDMA_REG_X(x) (0x1000 + (0x200 * x))
#define EDMA_TPCC_PID 0x0
#define EDMA_TPCC_EMCR 0x308
#define EDMA_TPCC_EMCRH 0x30c
#define EDMA_TPCC_CCERRCLR 0x31c
#define EDMA_TPCC_DRAE0 0x340
#define EDMA_TPCC_DRAEH0 0x344
#define EDMA_TPCC_ESR 0x1010
#define EDMA_TPCC_ESRH 0x1014
#define EDMA_TPCC_EESR 0x1030
#define EDMA_TPCC_EESRH 0x1034
#define EDMA_TPCC_SECR 0x1040
#define EDMA_TPCC_SECRH 0x1044
#define EDMA_TPCC_IER 0x1050
#define EDMA_TPCC_IERH 0x1054
#define EDMA_TPCC_IECR 0x1058
#define EDMA_TPCC_IECRH 0x105c
#define EDMA_TPCC_IESR 0x1060
#define EDMA_TPCC_IESRH 0x1064
#define EDMA_TPCC_IPR 0x1068
#define EDMA_TPCC_IPRH 0x106c
#define EDMA_TPCC_ICR 0x1070
#define EDMA_TPCC_ICRH 0x1074
#define EDMA_TPCC_IEVAL 0x1078
#define EDMA_TPCC_OPT(x) (0x4000 + (x * 0x20))
#define TPCC_READ_4(sc, reg) \
(bus_space_read_4((sc)->sc_iot, (sc)->sc_tpcc, (reg)))
#define TPCC_WRITE_4(sc, reg, val) \
(bus_space_write_4((sc)->sc_iot, (sc)->sc_tpcc, (reg), (val)))
#define TPCC_SET(sc, reg, val) \
(TPCC_WRITE_4((sc), (reg), (TPCC_READ_4(sc, reg) | (val))))
#define TPCC_FILTSET(sc, reg, val, filt) \
(TPCC_WRITE_4((sc), (reg), (TPCC_READ_4(sc, reg) & (filt)) | (val)))
struct edma_softc *edma_sc;
int edma_match(struct device *, void *, void *);
void edma_attach(struct device *, struct device *, void *);
int edma_comp_intr(void *);
const struct cfattach edma_ca = {
sizeof(struct edma_softc), edma_match, edma_attach
};
struct cfdriver edma_cd = {
NULL, "edma", DV_DULL
};
int
edma_match(struct device *parent, void *match, void *aux)
{
struct fdt_attach_args *faa = aux;
return OF_is_compatible(faa->fa_node, "ti,edma3-tpcc");
}
void
edma_attach(struct device *parent, struct device *self, void *aux)
{
struct fdt_attach_args *faa = aux;
struct edma_softc *sc = (struct edma_softc *)self;
uint32_t rev;
int i;
if (faa->fa_nreg < 1)
return;
sc->sc_iot = faa->fa_iot;
if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
faa->fa_reg[0].size, 0, &sc->sc_tpcc)) {
printf("%s: bus_space_map failed for TPCC\n", DEVNAME(sc));
return ;
}
prcm_enablemodule(PRCM_TPCC);
prcm_enablemodule(PRCM_TPTC0);
rev = TPCC_READ_4(sc, EDMA_TPCC_PID);
printf(" rev %d.%d\n", rev >> 4 & 0xf, rev & 0xf);
sc->sc_ih_comp = arm_intr_establish_fdt(faa->fa_node, IPL_VM,
edma_comp_intr, sc, DEVNAME(sc));
if (sc->sc_ih_comp == NULL) {
printf("%s: unable to establish interrupt comp\n", DEVNAME(sc));
bus_space_unmap(sc->sc_iot, sc->sc_tpcc,
faa->fa_reg[0].size);
return ;
}
edma_sc = sc;
TPCC_WRITE_4(sc, EDMA_TPCC_EMCR, 0xffffffff);
TPCC_WRITE_4(sc, EDMA_TPCC_EMCRH, 0xffffffff);
TPCC_WRITE_4(sc, EDMA_TPCC_CCERRCLR, 0xffffffff);
for (i = 0; i < EDMA_NUM_DMA_CHANS; i++)
TPCC_WRITE_4(sc, EDMA_TPCC_DHCM(i), i << 5);
TPCC_WRITE_4(sc, EDMA_TPCC_DRAE0, 0xffffffff);
TPCC_WRITE_4(sc, EDMA_TPCC_DRAEH0, 0xffffffff);
return ;
}
int
edma_comp_intr(void *arg)
{
struct edma_softc *sc = arg;
uint32_t ipr, iprh;
int i;
ipr = TPCC_READ_4(sc, EDMA_TPCC_IPR);
iprh = TPCC_READ_4(sc, EDMA_TPCC_IPRH);
for (i = 0; i < (EDMA_NUM_DMA_CHANS/2); i++) {
if (ISSET(ipr, (1<<i))) {
TPCC_WRITE_4(sc, EDMA_TPCC_ICR, (1<<i));
if (sc->sc_intr_cb[i])
sc->sc_intr_cb[i](sc->sc_intr_dat[i]);
}
}
for (i = 0; i < (EDMA_NUM_DMA_CHANS/2); i++) {
if (ISSET(iprh, (1<<i))) {
TPCC_WRITE_4(sc, EDMA_TPCC_ICRH, (1<<i));
if (sc->sc_intr_cb[i + 32])
sc->sc_intr_cb[i + 32](sc->sc_intr_dat[i + 32]);
}
}
TPCC_WRITE_4(sc, EDMA_TPCC_IEVAL, 1);
return (1);
}
int
edma_intr_dma_en(uint32_t ch, edma_intr_cb_t cb, void *dat)
{
if (edma_sc == NULL || ch >= EDMA_NUM_DMA_CHANS)
return (EINVAL);
edma_sc->sc_intr_cb[ch] = cb;
edma_sc->sc_intr_dat[ch] = dat;
if (ch < 32) {
TPCC_WRITE_4(edma_sc, EDMA_TPCC_IESR, 1 << ch);
TPCC_WRITE_4(edma_sc, EDMA_TPCC_IESR + EDMA_REG_X(0), 1 << ch);
} else {
TPCC_WRITE_4(edma_sc, EDMA_TPCC_IESRH, 1 << (ch - 32));
TPCC_WRITE_4(edma_sc, EDMA_TPCC_IESRH + EDMA_REG_X(0),
1 << (ch - 32));
}
return (0);
}
int
edma_intr_dma_dis(uint32_t ch)
{
if (edma_sc == NULL || ch >= EDMA_NUM_DMA_CHANS)
return (EINVAL);
if (ch < 32)
TPCC_WRITE_4(edma_sc, EDMA_TPCC_IECR, 1 << ch);
else
TPCC_WRITE_4(edma_sc, EDMA_TPCC_IECRH, 1 << (ch - 32));
edma_sc->sc_intr_cb[ch] = NULL;
edma_sc->sc_intr_dat[ch] = NULL;
return (0);
}
int
edma_trig_xfer_man(uint32_t ch)
{
if (edma_sc == NULL || ch >= EDMA_NUM_DMA_CHANS)
return (EINVAL);
if (ch < 32) {
if (ISSET(TPCC_READ_4(edma_sc, EDMA_TPCC_IER), 1 << ch))
TPCC_WRITE_4(edma_sc, EDMA_TPCC_IEVAL, 1);
TPCC_WRITE_4(edma_sc, EDMA_TPCC_ICR, 1 << ch);
TPCC_WRITE_4(edma_sc, EDMA_TPCC_EMCR, 1 << ch);
TPCC_WRITE_4(edma_sc, EDMA_TPCC_ESR, 1 << ch);
} else {
if (ISSET(TPCC_READ_4(edma_sc, EDMA_TPCC_IERH), 1 << (ch - 32)))
TPCC_WRITE_4(edma_sc, EDMA_TPCC_IEVAL, 1);
TPCC_WRITE_4(edma_sc, EDMA_TPCC_ICRH, 1 << (ch - 32));
TPCC_WRITE_4(edma_sc, EDMA_TPCC_EMCRH, 1 << (ch - 32));
TPCC_WRITE_4(edma_sc, EDMA_TPCC_ESRH, 1 << (ch - 32));
}
return (0);
}
int
edma_trig_xfer_by_dev(uint32_t ch)
{
if (edma_sc == NULL || ch >= EDMA_NUM_DMA_CHANS)
return (EINVAL);
if (ch < 32) {
if (ISSET(TPCC_READ_4(edma_sc, EDMA_TPCC_IER), 1 << ch))
TPCC_WRITE_4(edma_sc, EDMA_TPCC_IEVAL, 1);
TPCC_WRITE_4(edma_sc, EDMA_TPCC_ICR, 1 << ch);
TPCC_WRITE_4(edma_sc, EDMA_TPCC_SECR, 1 << ch);
TPCC_WRITE_4(edma_sc, EDMA_TPCC_EMCR, 1 << ch);
TPCC_WRITE_4(edma_sc, EDMA_TPCC_EESR, 1 << ch);
} else {
if (ISSET(TPCC_READ_4(edma_sc, EDMA_TPCC_IERH), 1 << (ch - 32)))
TPCC_WRITE_4(edma_sc, EDMA_TPCC_IEVAL, 1);
TPCC_WRITE_4(edma_sc, EDMA_TPCC_ICRH, 1 << (ch - 32));
TPCC_WRITE_4(edma_sc, EDMA_TPCC_SECRH, 1 << (ch - 32));
TPCC_WRITE_4(edma_sc, EDMA_TPCC_EMCRH, 1 << (ch - 32));
TPCC_WRITE_4(edma_sc, EDMA_TPCC_EESRH, 1 << (ch - 32));
}
return (0);
}
void
edma_param_write(uint32_t ch, struct edma_param *params)
{
bus_space_write_region_4(edma_sc->sc_iot, edma_sc->sc_tpcc,
EDMA_TPCC_OPT(ch), (uint32_t *)params, 8);
}
void
edma_param_read(uint32_t ch, struct edma_param *params)
{
bus_space_read_region_4(edma_sc->sc_iot, edma_sc->sc_tpcc,
EDMA_TPCC_OPT(ch), (uint32_t *)params, 8);
}