-/* $OpenBSD: apldma.c,v 1.1 2022/02/02 22:55:57 kettenis Exp $ */
+/* $OpenBSD: apldma.c,v 1.2 2022/07/27 20:18:46 kettenis Exp $ */
/*
* Copyright (c) 2022 Mark Kettenis <kettenis@openbsd.org>
*
* dropped as soon as official bindings are available.
*/
+/*
+ * The device tree bindings for this hardware use separate Tx and Rx
+ * channels with Tx channels using even channel numbers and Rx
+ * channels using odd channel numbers.
+ */
+
#define DMA_TX_EN 0x0000
#define DMA_TX_EN_CLR 0x0004
#define DMA_TX_INTR 0x0034
-#define DMA_TX_CTL(chan) (0x8000 + (chan) * 0x400)
+#define DMA_TX_CTL(chan) (0x8000 + ((chan) / 2) * 0x400)
#define DMA_TX_CTL_RESET_RINGS (1 << 0)
-#define DMA_TX_INTRSTAT(chan) (0x8014 + (chan) * 0x400)
+#define DMA_TX_INTRSTAT(chan) (0x8014 + ((chan) / 2) * 0x400)
#define DMA_TX_INTRSTAT_DESC_DONE (1 << 0)
#define DMA_TX_INTRSTAT_ERR (1 << 6)
-#define DMA_TX_INTRMASK(chan) (0x8024 + (chan) * 0x400)
+#define DMA_TX_INTRMASK(chan) (0x8024 + ((chan) / 2) * 0x400)
#define DMA_TX_INTRMASK_DESC_DONE (1 << 0)
#define DMA_TX_INTRMASK_ERR (1 << 6)
-#define DMA_TX_BUS_WIDTH(chan) (0x8040 + (chan) * 0x400)
+#define DMA_TX_BUS_WIDTH(chan) (0x8040 + ((chan) / 2) * 0x400)
#define DMA_TX_BUS_WIDTH_8BIT (0 << 0)
#define DMA_TX_BUS_WIDTH_16BIT (1 << 0)
#define DMA_TX_BUS_WIDTH_32BIT (2 << 0)
#define DMA_TX_BUS_WIDTH_FRAME_2_WORDS (1 << 4)
#define DMA_TX_BUS_WIDTH_FRAME_4_WORDS (2 << 4)
-#define DMA_TX_BURST_SIZE(chan) (0x8054 + (chan) * 0x400)
-#define DMA_TX_RESIDUE(chan) (0x8064 + (chan) * 0x400)
-#define DMA_TX_DESC_RING(chan) (0x8070 + (chan) * 0x400)
+#define DMA_TX_BURST_SIZE(chan) (0x8054 + ((chan) / 2) * 0x400)
+#define DMA_TX_BURST_SIZE_MAGIC 0x00c00060
+#define DMA_TX_RESIDUE(chan) (0x8064 + ((chan) / 2) * 0x400)
+#define DMA_TX_DESC_RING(chan) (0x8070 + ((chan) / 2) * 0x400)
#define DMA_TX_DESC_RING_FULL (1 << 9)
-#define DMA_TX_REPORT_RING(chan) (0x8074 + (chan) * 0x400)
+#define DMA_TX_REPORT_RING(chan) (0x8074 + ((chan) / 2) * 0x400)
#define DMA_TX_REPORT_RING_EMPTY (1 << 8)
-#define DMA_TX_DESC_WRITE(chan) (0x10000 + (chan) * 4)
-#define DMA_TX_REPORT_READ(chan) (0x10100 + (chan) * 4)
+#define DMA_TX_DESC_WRITE(chan) (0x10000 + ((chan) / 2) * 4)
+#define DMA_TX_REPORT_READ(chan) (0x10100 + ((chan) / 2) * 4)
#define DMA_DESC_NOTIFY (1 << 16)
#define DMA_NUM_DESCRIPTORS 4
-#define DMA_NUM_CHANNELS 4
+#define DMA_NUM_INTERRUPTS 4
#define HREAD4(sc, reg) \
(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
int sc_node;
void *sc_ih;
- struct apldma_channel *sc_ac[DMA_NUM_CHANNELS];
+ int sc_nchannels;
+ struct apldma_channel **sc_ac;
};
struct apldma_softc *apldma_sc;
return;
}
+ sc->sc_nchannels = OF_getpropint(faa->fa_node, "dma-channels", 0);
+ if (sc->sc_nchannels == 0) {
+ printf(": no DMA channels\n");
+ goto unmap;
+ }
+ sc->sc_ac = mallocarray(sc->sc_nchannels,
+ sizeof(struct apldma_channel), M_DEVBUF, M_WAITOK | M_ZERO);
+
sc->sc_dmat = faa->fa_dmat;
sc->sc_node = faa->fa_node;
apldma_intr, sc, sc->sc_dev.dv_xname);
if (sc->sc_ih == NULL) {
printf(": can't establish interrupt\n");
- goto unmap;
+ goto free;
}
printf("\n");
apldma_sc = sc;
return;
+free:
+ free(sc->sc_ac, M_DEVBUF,
+ sc->sc_nchannels * sizeof(struct apldma_channel));
unmap:
bus_space_unmap(sc->sc_iot, sc->sc_ioh, faa->fa_reg[0].size);
}
unsigned int chan, i;
intr = HREAD4(sc, DMA_TX_INTR);
- for (chan = 0; chan < DMA_NUM_CHANNELS; chan++) {
- if ((intr & (1 << chan)) == 0)
+ for (chan = 0; chan < sc->sc_nchannels; chan += 2) {
+ if ((intr & (1 << (chan / 2))) == 0)
continue;
intrstat = HREAD4(sc, DMA_TX_INTRSTAT(chan));
if ((intrstat & DMA_TX_INTRSTAT_DESC_DONE) == 0)
continue;
-
+
for (i = 0; i < DMA_NUM_DESCRIPTORS; i++) {
uint32_t status;
struct apldma_softc *sc = apldma_sc;
struct apldma_channel *ac;
- if (chan >= DMA_NUM_CHANNELS)
+ if (chan >= sc->sc_nchannels)
+ return NULL;
+
+ /* We only support Tx channels for now. */
+ if ((chan % 2) != 0)
return NULL;
ac = malloc(sizeof(*ac), M_DEVBUF, M_WAITOK);
default:
return EINVAL;
}
+ HWRITE4(sc, DMA_TX_BURST_SIZE(ac->ac_chan), DMA_TX_BURST_SIZE_MAGIC);
/* Reset rings. */
HWRITE4(sc, DMA_TX_CTL(ac->ac_chan), DMA_TX_CTL_RESET_RINGS);
apldma_fill_descriptors(ac);
/* Start DMA transfer. */
- HWRITE4(sc, DMA_TX_EN, 1 << ac->ac_chan);
+ HWRITE4(sc, DMA_TX_EN, 1 << (ac->ac_chan / 2));
return 0;
}
struct apldma_softc *sc = ac->ac_sc;
/* Stop DMA transfer. */
- HWRITE4(sc, DMA_TX_EN_CLR, 1 << ac->ac_chan);
+ HWRITE4(sc, DMA_TX_EN_CLR, 1 << (ac->ac_chan / 2));
/* Mask all interrupts. */
HWRITE4(sc, DMA_TX_INTRMASK(ac->ac_chan), 0);