1 /* $OpenBSD: edma.c,v 1.8 2021/10/24 17:52:27 mpi Exp $ */
2 /*
3 * Copyright (c) 2013 Sylvestre Gallon <ccna.syl@gmail.com>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 #include <sys/param.h>
19 #include <sys/systm.h>
20
21 #include <machine/fdt.h>
22
23 #include <armv7/armv7/armv7var.h>
24 #include <armv7/omap/prcmvar.h>
25 #include <armv7/omap/edmavar.h>
26
27 #include <dev/ofw/openfirm.h>
28 #include <dev/ofw/fdt.h>
29
30 #define DEVNAME(s) ((s)->sc_dev.dv_xname)
31
32 struct edma_softc {
33 struct device sc_dev;
34
35 bus_space_tag_t sc_iot;
36 bus_space_handle_t sc_tpcc;
37
38 void *sc_ih_comp;
39 edma_intr_cb_t sc_intr_cb[64];
40 void *sc_intr_dat[64];
41 };
42
43 #define EDMA_NUM_DMA_CHANS 64
44 #define EDMA_NUM_QDMA_CHANS 8
45 #define EDMA_TPCC_DHCM(x) (0x100 + (x * 4))
46 #define EDMA_REG_X(x) (0x1000 + (0x200 * x))
47 #define EDMA_TPCC_PID 0x0
48 #define EDMA_TPCC_EMCR 0x308
49 #define EDMA_TPCC_EMCRH 0x30c
50 #define EDMA_TPCC_CCERRCLR 0x31c
51 #define EDMA_TPCC_DRAE0 0x340
52 #define EDMA_TPCC_DRAEH0 0x344
53 #define EDMA_TPCC_ESR 0x1010
54 #define EDMA_TPCC_ESRH 0x1014
55 #define EDMA_TPCC_EESR 0x1030
56 #define EDMA_TPCC_EESRH 0x1034
57 #define EDMA_TPCC_SECR 0x1040
58 #define EDMA_TPCC_SECRH 0x1044
59 #define EDMA_TPCC_IER 0x1050
60 #define EDMA_TPCC_IERH 0x1054
61 #define EDMA_TPCC_IECR 0x1058
62 #define EDMA_TPCC_IECRH 0x105c
63 #define EDMA_TPCC_IESR 0x1060
64 #define EDMA_TPCC_IESRH 0x1064
65 #define EDMA_TPCC_IPR 0x1068
66 #define EDMA_TPCC_IPRH 0x106c
67 #define EDMA_TPCC_ICR 0x1070
68 #define EDMA_TPCC_ICRH 0x1074
69 #define EDMA_TPCC_IEVAL 0x1078
70 #define EDMA_TPCC_OPT(x) (0x4000 + (x * 0x20))
71
72 #define TPCC_READ_4(sc, reg) \
73 (bus_space_read_4((sc)->sc_iot, (sc)->sc_tpcc, (reg)))
74 #define TPCC_WRITE_4(sc, reg, val) \
75 (bus_space_write_4((sc)->sc_iot, (sc)->sc_tpcc, (reg), (val)))
76 #define TPCC_SET(sc, reg, val) \
77 (TPCC_WRITE_4((sc), (reg), (TPCC_READ_4(sc, reg) | (val))))
78 #define TPCC_FILTSET(sc, reg, val, filt) \
79 (TPCC_WRITE_4((sc), (reg), (TPCC_READ_4(sc, reg) & (filt)) | (val)))
80
81 struct edma_softc *edma_sc;
82
83 int edma_match(struct device *, void *, void *);
84 void edma_attach(struct device *, struct device *, void *);
85 int edma_comp_intr(void *);
86
87 const struct cfattach edma_ca = {
88 sizeof(struct edma_softc), edma_match, edma_attach
89 };
90
91 struct cfdriver edma_cd = {
92 NULL, "edma", DV_DULL
93 };
94
95 int
edma_match(struct device * parent,void * match,void * aux)96 edma_match(struct device *parent, void *match, void *aux)
97 {
98 struct fdt_attach_args *faa = aux;
99
100 return OF_is_compatible(faa->fa_node, "ti,edma3-tpcc");
101 }
102
103 void
edma_attach(struct device * parent,struct device * self,void * aux)104 edma_attach(struct device *parent, struct device *self, void *aux)
105 {
106 struct fdt_attach_args *faa = aux;
107 struct edma_softc *sc = (struct edma_softc *)self;
108 uint32_t rev;
109 int i;
110
111 if (faa->fa_nreg < 1)
112 return;
113
114 sc->sc_iot = faa->fa_iot;
115
116 /* Map Base address for TPCC and TPCTX */
117 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
118 faa->fa_reg[0].size, 0, &sc->sc_tpcc)) {
119 printf("%s: bus_space_map failed for TPCC\n", DEVNAME(sc));
120 return ;
121 }
122
123 /* Enable TPCC and TPTC0 in PRCM */
124 prcm_enablemodule(PRCM_TPCC);
125 prcm_enablemodule(PRCM_TPTC0);
126
127 rev = TPCC_READ_4(sc, EDMA_TPCC_PID);
128 printf(" rev %d.%d\n", rev >> 4 & 0xf, rev & 0xf);
129
130 /* XXX IPL_VM ? */
131 /* Enable interrupts line */
132 sc->sc_ih_comp = arm_intr_establish_fdt(faa->fa_node, IPL_VM,
133 edma_comp_intr, sc, DEVNAME(sc));
134 if (sc->sc_ih_comp == NULL) {
135 printf("%s: unable to establish interrupt comp\n", DEVNAME(sc));
136 bus_space_unmap(sc->sc_iot, sc->sc_tpcc,
137 faa->fa_reg[0].size);
138 return ;
139 }
140
141 /* Set global softc */
142 edma_sc = sc;
143
144 /* Clear Event Missed Events */
145 TPCC_WRITE_4(sc, EDMA_TPCC_EMCR, 0xffffffff);
146 TPCC_WRITE_4(sc, EDMA_TPCC_EMCRH, 0xffffffff);
147 TPCC_WRITE_4(sc, EDMA_TPCC_CCERRCLR, 0xffffffff);
148
149 /* Identity Map Channels PaRAM */
150 for (i = 0; i < EDMA_NUM_DMA_CHANS; i++)
151 TPCC_WRITE_4(sc, EDMA_TPCC_DHCM(i), i << 5);
152
153 /*
154 * Enable SHADOW Region 0 and only use this region
155 * This is needed to have working intr...
156 */
157 TPCC_WRITE_4(sc, EDMA_TPCC_DRAE0, 0xffffffff);
158 TPCC_WRITE_4(sc, EDMA_TPCC_DRAEH0, 0xffffffff);
159
160 return ;
161 }
162
163 int
edma_comp_intr(void * arg)164 edma_comp_intr(void *arg)
165 {
166 struct edma_softc *sc = arg;
167 uint32_t ipr, iprh;
168 int i;
169
170 ipr = TPCC_READ_4(sc, EDMA_TPCC_IPR);
171 iprh = TPCC_READ_4(sc, EDMA_TPCC_IPRH);
172
173 /* Lookup to intr in the first 32 chans */
174 for (i = 0; i < (EDMA_NUM_DMA_CHANS/2); i++) {
175 if (ISSET(ipr, (1<<i))) {
176 TPCC_WRITE_4(sc, EDMA_TPCC_ICR, (1<<i));
177 if (sc->sc_intr_cb[i])
178 sc->sc_intr_cb[i](sc->sc_intr_dat[i]);
179 }
180 }
181
182 for (i = 0; i < (EDMA_NUM_DMA_CHANS/2); i++) {
183 if (ISSET(iprh, (1<<i))) {
184 TPCC_WRITE_4(sc, EDMA_TPCC_ICRH, (1<<i));
185 if (sc->sc_intr_cb[i + 32])
186 sc->sc_intr_cb[i + 32](sc->sc_intr_dat[i + 32]);
187 }
188 }
189
190 /* Trig pending intr */
191 TPCC_WRITE_4(sc, EDMA_TPCC_IEVAL, 1);
192
193 return (1);
194 }
195
196 int
edma_intr_dma_en(uint32_t ch,edma_intr_cb_t cb,void * dat)197 edma_intr_dma_en(uint32_t ch, edma_intr_cb_t cb, void *dat)
198 {
199 if (edma_sc == NULL || ch >= EDMA_NUM_DMA_CHANS)
200 return (EINVAL);
201
202 edma_sc->sc_intr_cb[ch] = cb;
203 edma_sc->sc_intr_dat[ch] = dat;
204
205 if (ch < 32) {
206 TPCC_WRITE_4(edma_sc, EDMA_TPCC_IESR, 1 << ch);
207 TPCC_WRITE_4(edma_sc, EDMA_TPCC_IESR + EDMA_REG_X(0), 1 << ch);
208 } else {
209 TPCC_WRITE_4(edma_sc, EDMA_TPCC_IESRH, 1 << (ch - 32));
210 TPCC_WRITE_4(edma_sc, EDMA_TPCC_IESRH + EDMA_REG_X(0),
211 1 << (ch - 32));
212 }
213
214 return (0);
215 }
216
217 int
edma_intr_dma_dis(uint32_t ch)218 edma_intr_dma_dis(uint32_t ch)
219 {
220 if (edma_sc == NULL || ch >= EDMA_NUM_DMA_CHANS)
221 return (EINVAL);
222
223 if (ch < 32)
224 TPCC_WRITE_4(edma_sc, EDMA_TPCC_IECR, 1 << ch);
225 else
226 TPCC_WRITE_4(edma_sc, EDMA_TPCC_IECRH, 1 << (ch - 32));
227 edma_sc->sc_intr_cb[ch] = NULL;
228 edma_sc->sc_intr_dat[ch] = NULL;
229
230 return (0);
231 }
232
233 int
edma_trig_xfer_man(uint32_t ch)234 edma_trig_xfer_man(uint32_t ch)
235 {
236 if (edma_sc == NULL || ch >= EDMA_NUM_DMA_CHANS)
237 return (EINVAL);
238
239 /*
240 * Trig xfer
241 * enable IEVAL only if there is an intr associated
242 */
243 if (ch < 32) {
244 if (ISSET(TPCC_READ_4(edma_sc, EDMA_TPCC_IER), 1 << ch))
245 TPCC_WRITE_4(edma_sc, EDMA_TPCC_IEVAL, 1);
246 TPCC_WRITE_4(edma_sc, EDMA_TPCC_ICR, 1 << ch);
247 TPCC_WRITE_4(edma_sc, EDMA_TPCC_EMCR, 1 << ch);
248 TPCC_WRITE_4(edma_sc, EDMA_TPCC_ESR, 1 << ch);
249 } else {
250 if (ISSET(TPCC_READ_4(edma_sc, EDMA_TPCC_IERH), 1 << (ch - 32)))
251 TPCC_WRITE_4(edma_sc, EDMA_TPCC_IEVAL, 1);
252 TPCC_WRITE_4(edma_sc, EDMA_TPCC_ICRH, 1 << (ch - 32));
253 TPCC_WRITE_4(edma_sc, EDMA_TPCC_EMCRH, 1 << (ch - 32));
254 TPCC_WRITE_4(edma_sc, EDMA_TPCC_ESRH, 1 << (ch - 32));
255 }
256
257 return (0);
258 }
259
260 int
edma_trig_xfer_by_dev(uint32_t ch)261 edma_trig_xfer_by_dev(uint32_t ch)
262 {
263 if (edma_sc == NULL || ch >= EDMA_NUM_DMA_CHANS)
264 return (EINVAL);
265
266 if (ch < 32) {
267 if (ISSET(TPCC_READ_4(edma_sc, EDMA_TPCC_IER), 1 << ch))
268 TPCC_WRITE_4(edma_sc, EDMA_TPCC_IEVAL, 1);
269 TPCC_WRITE_4(edma_sc, EDMA_TPCC_ICR, 1 << ch);
270 TPCC_WRITE_4(edma_sc, EDMA_TPCC_SECR, 1 << ch);
271 TPCC_WRITE_4(edma_sc, EDMA_TPCC_EMCR, 1 << ch);
272 TPCC_WRITE_4(edma_sc, EDMA_TPCC_EESR, 1 << ch);
273 } else {
274 if (ISSET(TPCC_READ_4(edma_sc, EDMA_TPCC_IERH), 1 << (ch - 32)))
275 TPCC_WRITE_4(edma_sc, EDMA_TPCC_IEVAL, 1);
276 TPCC_WRITE_4(edma_sc, EDMA_TPCC_ICRH, 1 << (ch - 32));
277 TPCC_WRITE_4(edma_sc, EDMA_TPCC_SECRH, 1 << (ch - 32));
278 TPCC_WRITE_4(edma_sc, EDMA_TPCC_EMCRH, 1 << (ch - 32));
279 TPCC_WRITE_4(edma_sc, EDMA_TPCC_EESRH, 1 << (ch - 32));
280 }
281 return (0);
282 }
283
284 void
edma_param_write(uint32_t ch,struct edma_param * params)285 edma_param_write(uint32_t ch, struct edma_param *params)
286 {
287 bus_space_write_region_4(edma_sc->sc_iot, edma_sc->sc_tpcc,
288 EDMA_TPCC_OPT(ch), (uint32_t *)params, 8);
289 }
290
291 void
edma_param_read(uint32_t ch,struct edma_param * params)292 edma_param_read(uint32_t ch, struct edma_param *params)
293 {
294 bus_space_read_region_4(edma_sc->sc_iot, edma_sc->sc_tpcc,
295 EDMA_TPCC_OPT(ch), (uint32_t *)params, 8);
296 }
297
298