/* $OpenBSD: edma.c,v 1.5 2015/01/22 14:33:01 krw Exp $ */ /* * Copyright (c) 2013 Sylvestre Gallon * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #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; void edma_attach(struct device *, struct device *, void *); int edma_comp_intr(void *); struct cfattach edma_ca = { sizeof(struct edma_softc), NULL, edma_attach }; struct cfdriver edma_cd = { NULL, "edma", DV_DULL }; void edma_attach(struct device *parent, struct device *self, void *aux) { struct armv7_attach_args *aa = aux; struct edma_softc *sc = (struct edma_softc *)self; uint32_t rev; int i; sc->sc_iot = aa->aa_iot; /* Map Base address for TPCC and TPCTX */ if (bus_space_map(sc->sc_iot, aa->aa_dev->mem[0].addr, aa->aa_dev->mem[0].size, 0, &sc->sc_tpcc)) { printf("%s: bus_space_map failed for TPCC\n", DEVNAME(sc)); return ; } /* Enable TPCC and TPTC0 in PRCM */ 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); /* XXX IPL_VM ? */ /* Enable interrupts line */ sc->sc_ih_comp = arm_intr_establish(aa->aa_dev->irq[0], 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, aa->aa_dev->mem[0].size); return ; } /* Set global softc */ edma_sc = sc; /* Clear Event Missed Events */ TPCC_WRITE_4(sc, EDMA_TPCC_EMCR, 0xffffffff); TPCC_WRITE_4(sc, EDMA_TPCC_EMCRH, 0xffffffff); TPCC_WRITE_4(sc, EDMA_TPCC_CCERRCLR, 0xffffffff); /* Identity Map Channels PaRAM */ for (i = 0; i < EDMA_NUM_DMA_CHANS; i++) TPCC_WRITE_4(sc, EDMA_TPCC_DHCM(i), i << 5); /* * Enable SHADOW Region 0 and only use this region * This is needed to have working intr... */ 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); /* Lookup to intr in the first 32 chans */ for (i = 0; i < (EDMA_NUM_DMA_CHANS/2); i++) { if (ISSET(ipr, (1<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<sc_intr_cb[i + 32]) sc->sc_intr_cb[i + 32](sc->sc_intr_dat[i + 32]); } } /* Trig pending intr */ 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); /* * Trig xfer * enable IEVAL only if there is an intr associated */ 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); }