1 /* $NetBSD: bestcomm.c,v 1.1 2026/06/27 13:28:34 rkujawa Exp $ */ 2 3 /*- 4 * Copyright (c) 2009, 2026 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Radoslaw Kujawa and Robert Swindells. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /* 33 * Driver for the MPC5200B BestComm SDMA engine. 34 * 35 * BestComm is a microcoded processor that runs DMA tasks from on-chip SRAM. 36 */ 37 38 #include <sys/cdefs.h> 39 __KERNEL_RCSID(0, "$NetBSD: bestcomm.c,v 1.1 2026/06/27 13:28:34 rkujawa Exp $"); 40 41 #include <sys/param.h> 42 #include <sys/systm.h> 43 #include <sys/device.h> 44 #include <sys/bus.h> 45 46 #include <dev/ofw/openfirm.h> 47 48 #include <machine/autoconf.h> 49 50 #include <powerpc/pic/picvar.h> 51 52 #include <powerpc/mpc5200/obiovar.h> 53 #include <powerpc/mpc5200/mpc5200reg.h> 54 #include <powerpc/mpc5200/bestcommreg.h> 55 #include <powerpc/mpc5200/sramvar.h> 56 #include <powerpc/mpc5200/bestcommvar.h> 57 #include <powerpc/mpc5200/bestcomm_image.h> 58 59 /* 60 * The task table MUST be aligned so that the function-descriptor relocation 61 * (which replaces a descriptor's high 24 bits with the TaskBar's) is exact. 62 */ 63 #define BESTCOMM_TASKBAR_ALIGN 0x100 64 65 #define BESTCOMM_CASCADE_IRQ 64 66 67 static int bestcomm_match(device_t, cfdata_t, void *); 68 static void bestcomm_attach(device_t, device_t, void *); 69 static void bestcomm_load_image(device_t); 70 71 CFATTACH_DECL_NEW(bestcomm, sizeof(struct bestcomm_softc), 72 bestcomm_match, bestcomm_attach, NULL, NULL); 73 74 static void bestcomm_enable_irq(struct pic_ops *, int, int); 75 static void bestcomm_disable_irq(struct pic_ops *, int); 76 static int bestcomm_get_irq(struct pic_ops *, int); 77 static void bestcomm_ack_irq(struct pic_ops *, int); 78 static void bestcomm_establish_irq(struct pic_ops *, int, int, int); 79 80 static struct bestcomm_softc *bestcomm_sc; 81 82 static inline uint32_t 83 sdma_read(struct bestcomm_ops *b, bus_size_t off) 84 { 85 return bus_space_read_4(b->bst, b->bsh, off); 86 } 87 88 static inline void 89 sdma_write(struct bestcomm_ops *b, bus_size_t off, uint32_t val) 90 { 91 bus_space_write_4(b->bst, b->bsh, off, val); 92 } 93 94 static int 95 bestcomm_match(device_t parent, cfdata_t cf, void *aux) 96 { 97 struct obio_attach_args *oba = aux; 98 char compat[40]; 99 int len; 100 101 if (strcmp(oba->obio_name, "bestcomm") == 0 || 102 strcmp(oba->obio_name, "sdma") == 0) 103 return 1; 104 105 len = OF_getprop(oba->obio_node, "compatible", compat, sizeof(compat)); 106 if (len > 0 && 107 (strcmp(compat, "mpc5200-bestcomm") == 0 || 108 strcmp(compat, "mpc5200b-bestcomm") == 0)) 109 return 1; 110 111 return 0; 112 } 113 114 static void 115 bestcomm_attach(device_t parent, device_t self, void *aux) 116 { 117 struct bestcomm_softc *sc = device_private(self); 118 struct obio_attach_args *oba = aux; 119 struct bestcomm_ops *b = &sc->sc_pic; 120 struct pic_ops *pic = &b->pic; 121 bus_size_t size; 122 int i; 123 124 sc->sc_dev = self; 125 sc->sc_dmat = oba->obio_dmat; 126 b->bst = oba->obio_bst; 127 128 size = oba->obio_size != 0 ? oba->obio_size : SDMA_REG_SIZE; 129 if (bus_space_map(b->bst, oba->obio_addr, size, 0, &b->bsh) != 0) { 130 aprint_error(": can't map registers\n"); 131 return; 132 } 133 134 /* 135 * Bring the engine to an idle state 136 */ 137 for (i = 0; i < SDMA_NTASKS / 2; i++) 138 sdma_write(b, SDMA_TCR + i * sizeof(uint32_t), 0); 139 140 b->int_mask = 0xffffffff; 141 sdma_write(b, SDMA_INT_MASK, b->int_mask); 142 sdma_write(b, SDMA_INT_PEND, SDMA_INT_IMPL); 143 144 pic->pic_cookie = b; 145 pic->pic_numintrs = SDMA_NTASKS; 146 pic->pic_enable_irq = bestcomm_enable_irq; 147 pic->pic_reenable_irq = bestcomm_enable_irq; 148 pic->pic_disable_irq = bestcomm_disable_irq; 149 pic->pic_get_irq = bestcomm_get_irq; 150 pic->pic_ack_irq = bestcomm_ack_irq; 151 pic->pic_establish_irq = bestcomm_establish_irq; 152 pic->pic_finish_setup = NULL; 153 strlcpy(pic->pic_name, device_xname(self), sizeof(pic->pic_name)); 154 155 pic_add(pic); 156 157 /* 158 * Cascade the secondary controller onto the SIU 159 */ 160 sc->sc_cascade = intr_establish(BESTCOMM_CASCADE_IRQ, IST_LEVEL, 161 IPL_NET, pic_handle_intr, pic); 162 163 bestcomm_sc = sc; 164 165 aprint_normal(": BestComm SDMA, %d tasks, %d interrupt sources\n", 166 SDMA_NTASKS, SDMA_NTASKS); 167 168 config_interrupts(self, bestcomm_load_image); 169 } 170 171 bool 172 bestcomm_available(void) 173 { 174 return bestcomm_sc != NULL; 175 } 176 177 /* 178 * Set the low 16 bits (the signed increment) of an increment word while 179 * preserving the high half, matching the task layout. 180 */ 181 static void 182 bestcomm_set_incr(volatile uint32_t *incw, int value) 183 { 184 *incw = (*incw & 0xffff0000) | ((uint16_t)value); 185 } 186 187 /* 188 * Set up a single-pointer FIFO-to-memory BD task (the FEC receive shape). 189 */ 190 int 191 bestcomm_bd_setup(struct bestcomm_bdring *br, int task, 192 const struct bestcomm_bd_layout *l, bus_addr_t fifo_pa, u_int nbd, 193 uint32_t maxbuf, int datasize, int initiator, int prio) 194 { 195 struct bestcomm_softc *sc = bestcomm_sc; 196 struct bestcomm_ops *b; 197 struct bestcomm_tdt *tdt; 198 volatile uint32_t *var, *inc; 199 bus_addr_t ring_pa, tcr_pa; 200 uint8_t szbyte; 201 202 if (sc == NULL || sc->sc_image_kva == NULL) 203 return ENXIO; 204 b = &sc->sc_pic; 205 206 ring_pa = sram_alloc(nbd * sizeof(uint32_t) * 2, sizeof(uint32_t) * 2); 207 if (ring_pa == 0) 208 return ENOMEM; 209 210 br->br_task = task; 211 br->br_bd_pa = ring_pa; 212 br->br_bd = sram_kva(ring_pa); 213 br->br_nbd = nbd; 214 br->br_bdflag = l->bdflag; 215 memset(br->br_bd, 0, nbd * sizeof(uint32_t) * 2); 216 217 tdt = (struct bestcomm_tdt *)((char *)sc->sc_image_kva + 218 task * sizeof(struct bestcomm_tdt)); 219 var = (volatile uint32_t *)sram_kva(tdt->tdt_var); 220 inc = var + 24; 221 222 tcr_pa = MPC5200_MBAR_DEFAULT + MPC5200_REG_SDMA + SDMA_TCR + task * 2; 223 224 var[l->fifo_var] = fifo_pa; /* peripheral FIFO */ 225 var[l->enable_var] = tcr_pa; /* task's own TCR */ 226 var[l->base_var] = ring_pa; /* BD ring base */ 227 var[l->base_var + 1] = ring_pa + (nbd - 1) * 8; /* last */ 228 var[l->base_var + 2] = ring_pa; /* current/start */ 229 var[l->bytes_var] = maxbuf; /* per-buffer cap */ 230 if (l->drd_var >= 0) /* flag-carrying DRD */ 231 var[l->drd_var] = tdt->tdt_start + l->drd_off; 232 bestcomm_set_incr(&inc[0], -datasize); /* IncrBytes */ 233 bestcomm_set_incr(&inc[1], datasize); /* memory-side incr */ 234 bestcomm_set_incr(&inc[2], 1); /* misalign adjust */ 235 236 ((volatile uint8_t *)&tdt->tdt_fdt)[3] = 0x07; /* task pragma */ 237 238 /* datasize transfer size on both sides (even/odd nibble). */ 239 szbyte = bus_space_read_1(b->bst, b->bsh, SDMA_SIZE_BYTE(task)); 240 if (task & 1) 241 szbyte = (szbyte & 0xf0) | SDMA_SIZE_FIELD(datasize, datasize); 242 else 243 szbyte = (szbyte & 0x0f) | 244 (SDMA_SIZE_FIELD(datasize, datasize) << 4); 245 bus_space_write_1(b->bst, b->bsh, SDMA_SIZE_BYTE(task), szbyte); 246 247 /* Auto-restart the task after each buffer; leave it disabled for now. */ 248 bus_space_write_2(b->bst, b->bsh, SDMA_TCR_TASK(task), 249 SDMA_TCR_AUTOSTART | (task & SDMA_TCR_AUTOTASK_MASK)); 250 251 /* Give the (image-baked) hardware initiator a scheduling priority. */ 252 bus_space_write_1(b->bst, b->bsh, SDMA_IPR_INIT(initiator), prio & 0x07); 253 254 __asm volatile ("sync" ::: "memory"); 255 return 0; 256 } 257 258 /* 259 * Release a BD task's resources 260 */ 261 void 262 bestcomm_bd_teardown(struct bestcomm_bdring *br) 263 { 264 if (br->br_bd_pa == 0) 265 return; 266 267 bestcomm_task_stop(br->br_task); 268 sram_free(br->br_bd_pa, br->br_nbd * sizeof(uint32_t) * 2); 269 br->br_bd = NULL; 270 br->br_bd_pa = 0; 271 br->br_nbd = 0; 272 } 273 274 void 275 bestcomm_bd_post(struct bestcomm_bdring *br, u_int idx, bus_addr_t buf_pa, 276 uint32_t size, uint32_t flags) 277 { 278 volatile uint32_t *bd = (volatile uint32_t *)br->br_bd + idx * 2; 279 uint32_t status; 280 281 if (br->br_bdflag) 282 status = (flags & 0x0c000000) | (size & 0x03ffffff); 283 else 284 status = size & 0x7fffffff; 285 286 bd[1] = buf_pa; /* data pointer */ 287 __asm volatile ("sync" ::: "memory"); 288 bd[0] = status | BESTCOMM_BD_READY; /* arm for the engine */ 289 __asm volatile ("sync" ::: "memory"); 290 } 291 292 uint32_t 293 bestcomm_bd_status(struct bestcomm_bdring *br, u_int idx) 294 { 295 volatile uint32_t *bd = (volatile uint32_t *)br->br_bd + idx * 2; 296 297 return bd[0]; 298 } 299 300 /* 301 * Stamp a runtime initiator into a task 302 */ 303 void 304 bestcomm_task_set_initiator(int task, int initiator, const uint16_t *drd_offs, 305 u_int ndrd) 306 { 307 struct bestcomm_softc *sc = bestcomm_sc; 308 struct bestcomm_ops *b; 309 struct bestcomm_tdt *tdt; 310 uint16_t tcr; 311 u_int i; 312 bool ext = false; 313 314 if (sc == NULL || sc->sc_image_kva == NULL) 315 return; 316 b = &sc->sc_pic; 317 tdt = (struct bestcomm_tdt *)((char *)sc->sc_image_kva + 318 task * sizeof(struct bestcomm_tdt)); 319 320 /* Task own-initiator field in the TCR (preserve the other bits). */ 321 tcr = bus_space_read_2(b->bst, b->bsh, SDMA_TCR_TASK(task)); 322 tcr = (tcr & ~SDMA_TCR_INIT_MASK) | 323 (((uint16_t)initiator << SDMA_TCR_INIT_SHIFT) & SDMA_TCR_INIT_MASK); 324 bus_space_write_2(b->bst, b->bsh, SDMA_TCR_TASK(task), tcr); 325 326 for (i = 0; i < ndrd; i++) { 327 volatile uint32_t *drd = 328 (volatile uint32_t *)sram_kva(tdt->tdt_start + drd_offs[i]); 329 330 if (!ext) { 331 if (((*drd & SDMA_DRD_INIT_MASK) >> SDMA_DRD_INIT_SHIFT) != 332 SDMA_INITIATOR_ALWAYS) 333 *drd = (*drd & ~SDMA_DRD_INIT_MASK) | 334 ((uint32_t)initiator << SDMA_DRD_INIT_SHIFT); 335 ext = (*drd & SDMA_DRD_EXT) != 0; 336 } else { 337 /* This word is a DRD extension operand; skip, then 338 * resume looking for descriptors once it ends. */ 339 ext = (*drd & SDMA_DRD_EXT) != 0; 340 } 341 } 342 343 __asm volatile ("sync" ::: "memory"); 344 } 345 346 void 347 bestcomm_task_start(int task) 348 { 349 struct bestcomm_ops *b = &bestcomm_sc->sc_pic; 350 uint16_t tcr; 351 352 tcr = bus_space_read_2(b->bst, b->bsh, SDMA_TCR_TASK(task)); 353 bus_space_write_2(b->bst, b->bsh, SDMA_TCR_TASK(task), 354 tcr | SDMA_TCR_ENABLE); 355 } 356 357 void 358 bestcomm_task_stop(int task) 359 { 360 struct bestcomm_ops *b = &bestcomm_sc->sc_pic; 361 uint16_t tcr; 362 363 tcr = bus_space_read_2(b->bst, b->bsh, SDMA_TCR_TASK(task)); 364 bus_space_write_2(b->bst, b->bsh, SDMA_TCR_TASK(task), 365 tcr & ~SDMA_TCR_ENABLE); 366 } 367 368 int 369 bestcomm_task_irq(int task) 370 { 371 return bestcomm_sc->sc_pic.pic.pic_intrbase + task; 372 } 373 374 uint32_t 375 bestcomm_intpend(void) 376 { 377 return sdma_read(&bestcomm_sc->sc_pic, SDMA_INT_PEND); 378 } 379 380 uint16_t 381 bestcomm_task_tcr(int task) 382 { 383 struct bestcomm_ops *b = &bestcomm_sc->sc_pic; 384 385 return bus_space_read_2(b->bst, b->bsh, SDMA_TCR_TASK(task)); 386 } 387 388 /* 389 * Load the task microcode image into SRAM and point the engine at it. 390 */ 391 static void 392 bestcomm_load_image(device_t self) 393 { 394 struct bestcomm_softc *sc = device_private(self); 395 struct bestcomm_ops *b = &sc->sc_pic; 396 struct bestcomm_tdt *tdt; 397 bus_addr_t pa; 398 void *kva; 399 uint32_t i; 400 401 if (!sram_available()) { 402 aprint_error_dev(self, "no SRAM; SDMA image not loaded\n"); 403 return; 404 } 405 406 pa = sram_alloc(bestcomm_image_bytes, BESTCOMM_TASKBAR_ALIGN); 407 if (pa == 0) { 408 aprint_error_dev(self, "no SRAM space for %u-byte SDMA image\n", 409 bestcomm_image_bytes); 410 return; 411 } 412 kva = sram_kva(pa); 413 414 memcpy(kva, bestcomm_image, bestcomm_image_bytes); 415 416 /* Relocate each task descriptor's pointer words by the TaskBar base. */ 417 tdt = (struct bestcomm_tdt *)((char *)kva + bestcomm_image_entry); 418 for (i = 0; i < bestcomm_image_ntasks; i++) { 419 tdt[i].tdt_start += pa; 420 tdt[i].tdt_stop += pa; 421 tdt[i].tdt_var += pa; 422 tdt[i].tdt_fdt = (pa & 0xffffff00) + tdt[i].tdt_fdt; 423 tdt[i].tdt_context += pa; 424 } 425 426 sc->sc_taskbar = pa; 427 sc->sc_image_kva = kva; 428 sdma_write(b, SDMA_TASKBAR, pa); 429 430 aprint_normal_dev(self, 431 "loaded %u-task SDMA image (%u bytes) at SRAM 0x%08jx\n", 432 bestcomm_image_ntasks, bestcomm_image_bytes, (uintmax_t)pa); 433 } 434 435 /* 436 * Secondary PIC for the 16 task-completion events. 437 */ 438 static void 439 bestcomm_enable_irq(struct pic_ops *pic, int irq, int type) 440 { 441 struct bestcomm_ops *b = pic->pic_cookie; 442 443 b->int_mask &= ~SDMA_INT_TASK(irq); 444 sdma_write(b, SDMA_INT_MASK, b->int_mask); 445 } 446 447 static void 448 bestcomm_disable_irq(struct pic_ops *pic, int irq) 449 { 450 struct bestcomm_ops *b = pic->pic_cookie; 451 452 b->int_mask |= SDMA_INT_TASK(irq); 453 sdma_write(b, SDMA_INT_MASK, b->int_mask); 454 } 455 456 static int 457 bestcomm_get_irq(struct pic_ops *pic, int mode) 458 { 459 struct bestcomm_ops *b = pic->pic_cookie; 460 uint32_t pending; 461 462 pending = sdma_read(b, SDMA_INT_PEND) & ~b->int_mask & 463 SDMA_INT_TASK_MASK; 464 if (pending == 0) 465 return 255; 466 467 return ffs(pending) - 1; /* lowest-numbered pending task */ 468 } 469 470 static void 471 bestcomm_ack_irq(struct pic_ops *pic, int irq) 472 { 473 struct bestcomm_ops *b = pic->pic_cookie; 474 475 /* IntPend is write-1-to-clear. */ 476 sdma_write(b, SDMA_INT_PEND, SDMA_INT_TASK(irq)); 477 } 478 479 static void 480 bestcomm_establish_irq(struct pic_ops *pic, int irq, int type, int maxlevel) 481 { 482 /* Per-task priority is left at its reset default for now. */ 483 } 484 485