1 /* $NetBSD: octeon_fpa.c,v 1.1 2015/04/29 08:32:01 hikaru Exp $ */ 2 3 /* 4 * Copyright (c) 2007 Internet Initiative Japan, Inc. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #undef FPADEBUG 30 31 #include <sys/cdefs.h> 32 __KERNEL_RCSID(0, "$NetBSD: octeon_fpa.c,v 1.1 2015/04/29 08:32:01 hikaru Exp $"); 33 34 #include "opt_octeon.h" 35 36 #include "opt_octeon.h" 37 38 #include <sys/param.h> 39 #include <sys/systm.h> 40 #include <sys/types.h> 41 #include <sys/malloc.h> 42 43 #include <sys/bus.h> 44 #include <machine/locore.h> 45 #include <machine/vmparam.h> 46 47 #include <mips/cavium/octeonvar.h> 48 #include <mips/cavium/include/iobusvar.h> 49 #include <mips/cavium/dev/octeon_fpavar.h> 50 #include <mips/cavium/dev/octeon_fpareg.h> 51 52 #ifdef FPADEBUG 53 #define DPRINTF(x) printf x 54 #else 55 #define DPRINTF(x) 56 #endif 57 58 #define _DMA_NSEGS 1 59 #define _DMA_BUFLEN 0x01000000 60 61 /* pool descriptor */ 62 struct octeon_fpa_desc { 63 }; 64 65 struct octeon_fpa_softc { 66 int sc_initialized; 67 68 bus_space_tag_t sc_regt; 69 bus_space_handle_t sc_regh; 70 71 bus_space_tag_t sc_opst; 72 bus_space_handle_t sc_opsh; 73 74 bus_dma_tag_t sc_dmat; 75 76 struct octeon_fpa_desc sc_descs[8]; 77 78 #ifdef OCTEON_ETH_DEBUG 79 struct evcnt sc_ev_fpaq7perr; 80 struct evcnt sc_ev_fpaq7coff; 81 struct evcnt sc_ev_fpaq7und; 82 struct evcnt sc_ev_fpaq6perr; 83 struct evcnt sc_ev_fpaq6coff; 84 struct evcnt sc_ev_fpaq6und; 85 struct evcnt sc_ev_fpaq5perr; 86 struct evcnt sc_ev_fpaq5coff; 87 #endif 88 }; 89 90 void octeon_fpa_bootstrap(struct octeon_config *); 91 void octeon_fpa_reset(void); 92 void octeon_fpa_int_enable(struct octeon_fpa_softc *, int); 93 void octeon_fpa_buf_dma_alloc(struct octeon_fpa_buf *); 94 95 static void octeon_fpa_init(struct octeon_fpa_softc *); 96 #ifdef notyet 97 static uint64_t octeon_fpa_iobdma(struct octeon_fpa_softc *, int, int); 98 #endif 99 100 #ifdef OCTEON_ETH_DEBUG 101 void octeon_fpa_intr_evcnt_attach(struct octeon_fpa_softc *); 102 void octeon_fpa_intr_rml(void *); 103 #endif 104 105 static struct octeon_fpa_softc octeon_fpa_softc; 106 107 /* ---- global functions */ 108 109 void 110 octeon_fpa_bootstrap(struct octeon_config *mcp) 111 { 112 struct octeon_fpa_softc *sc = &octeon_fpa_softc; 113 114 sc->sc_regt = &mcp->mc_iobus_bust; 115 sc->sc_opst = &mcp->mc_iobus_bust; 116 sc->sc_dmat = &mcp->mc_iobus_dmat; 117 118 octeon_fpa_init(sc); 119 } 120 121 void 122 octeon_fpa_reset(void) 123 { 124 /* XXX */ 125 } 126 127 #ifdef OCTEON_ETH_DEBUG 128 int octeon_fpa_intr_rml_verbose; 129 struct evcnt octeon_fpa_intr_evcnt; 130 131 static const struct octeon_evcnt_entry octeon_fpa_intr_evcnt_entries[] = { 132 #define _ENTRY(name, type, parent, descr) \ 133 OCTEON_EVCNT_ENTRY(struct octeon_fpa_softc, name, type, parent, descr) 134 _ENTRY(fpaq7perr, MISC, NULL, "fpa q7 pointer"), 135 _ENTRY(fpaq7coff, MISC, NULL, "fpa q7 counter offset"), 136 _ENTRY(fpaq7und, MISC, NULL, "fpa q7 underflow"), 137 _ENTRY(fpaq6perr, MISC, NULL, "fpa q6 pointer"), 138 _ENTRY(fpaq6coff, MISC, NULL, "fpa q6 counter offset"), 139 _ENTRY(fpaq6und, MISC, NULL, "fpa q6 underflow"), 140 _ENTRY(fpaq5perr, MISC, NULL, "fpa q5 pointer"), 141 _ENTRY(fpaq5coff, MISC, NULL, "fpa q5 counter offset"), 142 #undef _ENTRY 143 }; 144 145 void 146 octeon_fpa_intr_evcnt_attach(struct octeon_fpa_softc *sc) 147 { 148 OCTEON_EVCNT_ATTACH_EVCNTS(sc, octeon_fpa_intr_evcnt_entries, "fpa0"); 149 } 150 151 void 152 octeon_fpa_intr_rml(void *arg) 153 { 154 struct octeon_fpa_softc *sc; 155 uint64_t reg; 156 157 octeon_fpa_intr_evcnt.ev_count++; 158 sc = &octeon_fpa_softc; 159 KASSERT(sc != NULL); 160 reg = octeon_fpa_int_summary(); 161 if (octeon_fpa_intr_rml_verbose) 162 printf("%s: FPA_INT_SUM=0x%016" PRIx64 "\n", __func__, reg); 163 if (reg & FPA_INT_SUM_Q7_PERR) 164 OCTEON_EVCNT_INC(sc, fpaq7perr); 165 if (reg & FPA_INT_SUM_Q7_COFF) 166 OCTEON_EVCNT_INC(sc, fpaq7coff); 167 if (reg & FPA_INT_SUM_Q7_UND) 168 OCTEON_EVCNT_INC(sc, fpaq7und); 169 if (reg & FPA_INT_SUM_Q6_PERR) 170 OCTEON_EVCNT_INC(sc, fpaq6perr); 171 if (reg & FPA_INT_SUM_Q6_COFF) 172 OCTEON_EVCNT_INC(sc, fpaq6coff); 173 if (reg & FPA_INT_SUM_Q6_UND) 174 OCTEON_EVCNT_INC(sc, fpaq6und); 175 if (reg & FPA_INT_SUM_Q5_PERR) 176 OCTEON_EVCNT_INC(sc, fpaq5perr); 177 if (reg & FPA_INT_SUM_Q5_COFF) 178 OCTEON_EVCNT_INC(sc, fpaq5coff); 179 } 180 181 void 182 octeon_fpa_int_enable(struct octeon_fpa_softc *sc, int enable) 183 { 184 const uint64_t int_xxx = 185 FPA_INT_ENB_Q7_PERR | FPA_INT_ENB_Q7_COFF | FPA_INT_ENB_Q7_UND | 186 FPA_INT_ENB_Q6_PERR | FPA_INT_ENB_Q6_COFF | FPA_INT_ENB_Q6_UND | 187 FPA_INT_ENB_Q5_PERR | FPA_INT_ENB_Q5_COFF | FPA_INT_ENB_Q5_UND | 188 FPA_INT_ENB_Q4_PERR | FPA_INT_ENB_Q4_COFF | FPA_INT_ENB_Q4_UND | 189 FPA_INT_ENB_Q3_PERR | FPA_INT_ENB_Q3_COFF | FPA_INT_ENB_Q3_UND | 190 FPA_INT_ENB_Q2_PERR | FPA_INT_ENB_Q2_COFF | FPA_INT_ENB_Q2_UND | 191 FPA_INT_ENB_Q1_PERR | FPA_INT_ENB_Q1_COFF | FPA_INT_ENB_Q1_UND | 192 FPA_INT_ENB_Q0_PERR | FPA_INT_ENB_Q0_COFF | FPA_INT_ENB_Q0_UND | 193 FPA_INT_ENB_FED1_DBE | FPA_INT_ENB_FED1_SBE | 194 FPA_INT_ENB_FED0_DBE | FPA_INT_ENB_FED0_SBE; 195 196 bus_space_write_8(sc->sc_regt, sc->sc_regh, FPA_INT_SUM_OFFSET, 197 int_xxx); 198 if (enable) 199 bus_space_write_8(sc->sc_regt, sc->sc_regh, FPA_INT_ENB_OFFSET, 200 int_xxx); 201 } 202 203 uint64_t 204 octeon_fpa_int_summary(void) 205 { 206 struct octeon_fpa_softc *sc = &octeon_fpa_softc; 207 uint64_t summary; 208 209 summary = bus_space_read_8(sc->sc_regt, sc->sc_regh, FPA_INT_SUM_OFFSET); 210 bus_space_write_8(sc->sc_regt, sc->sc_regh, FPA_INT_SUM_OFFSET, summary); 211 return summary; 212 } 213 #endif 214 215 int 216 octeon_fpa_buf_init(int poolno, size_t size, size_t nelems, 217 struct octeon_fpa_buf **rfb) 218 { 219 struct octeon_fpa_softc *sc = &octeon_fpa_softc; 220 struct octeon_fpa_buf *fb; 221 int nsegs; 222 paddr_t paddr; 223 224 nsegs = 1/* XXX */; 225 fb = malloc(sizeof(*fb) + sizeof(*fb->fb_dma_segs) * nsegs, M_DEVBUF, 226 M_WAITOK | M_ZERO); 227 if (fb == NULL) 228 return 1; 229 fb->fb_poolno = poolno; 230 fb->fb_size = size; 231 fb->fb_nelems = nelems; 232 fb->fb_len = size * nelems; 233 fb->fb_dmat = sc->sc_dmat; 234 fb->fb_dma_segs = (void *)(fb + 1); 235 fb->fb_dma_nsegs = nsegs; 236 237 octeon_fpa_buf_dma_alloc(fb); 238 239 for (paddr = fb->fb_paddr; paddr < fb->fb_paddr + fb->fb_len; 240 paddr += fb->fb_size) 241 octeon_fpa_buf_put_paddr(fb, paddr); 242 243 *rfb = fb; 244 245 return 0; 246 } 247 248 void * 249 octeon_fpa_buf_get(struct octeon_fpa_buf *fb) 250 { 251 paddr_t paddr; 252 vaddr_t addr; 253 254 paddr = octeon_fpa_buf_get_paddr(fb); 255 if (paddr == 0) 256 addr = 0; 257 else 258 addr = fb->fb_addr + (vaddr_t/* XXX */)(paddr - fb->fb_paddr); 259 return (void *)addr; 260 } 261 262 void 263 octeon_fpa_buf_dma_alloc(struct octeon_fpa_buf *fb) 264 { 265 int status; 266 int nsegs; 267 void *va; 268 269 status = bus_dmamap_create(fb->fb_dmat, fb->fb_len, 270 fb->fb_len / PAGE_SIZE, /* # of segments */ 271 fb->fb_len, /* we don't use s/g for FPA buf */ 272 PAGE_SIZE, /* OCTEON hates >PAGE_SIZE boundary */ 273 0, &fb->fb_dmah); 274 if (status != 0) 275 panic("%s failed", "bus_dmamap_create"); 276 277 status = bus_dmamem_alloc(fb->fb_dmat, fb->fb_len, 128, 0, 278 fb->fb_dma_segs, fb->fb_dma_nsegs, &nsegs, 0); 279 if (status != 0 || fb->fb_dma_nsegs != nsegs) 280 panic("%s failed", "bus_dmamem_alloc"); 281 282 status = bus_dmamem_map(fb->fb_dmat, fb->fb_dma_segs, fb->fb_dma_nsegs, 283 fb->fb_len, &va, 0); 284 if (status != 0) 285 panic("%s failed", "bus_dmamem_map"); 286 287 status = bus_dmamap_load(fb->fb_dmat, fb->fb_dmah, va, fb->fb_len, 288 NULL, /* kernel */ 289 0); 290 if (status != 0) 291 panic("%s failed", "bus_dmamap_load"); 292 293 fb->fb_addr = (vaddr_t)va; 294 fb->fb_paddr = fb->fb_dma_segs[0].ds_addr; 295 } 296 297 uint64_t 298 octeon_fpa_query(int poolno) 299 { 300 struct octeon_fpa_softc *sc = &octeon_fpa_softc; 301 302 return bus_space_read_8(sc->sc_regt, sc->sc_regh, 303 FPA_QUE0_AVAILABLE_OFFSET + sizeof(uint64_t) * poolno); 304 } 305 306 /* ---- local functions */ 307 308 static inline void octeon_fpa_init_bus(struct octeon_fpa_softc *); 309 static inline void octeon_fpa_init_bus_space(struct octeon_fpa_softc *); 310 static inline void octeon_fpa_init_regs(struct octeon_fpa_softc *); 311 312 void 313 octeon_fpa_init(struct octeon_fpa_softc *sc) 314 { 315 if (sc->sc_initialized != 0) 316 panic("%s: already initialized", __func__); 317 sc->sc_initialized = 1; 318 319 octeon_fpa_init_bus(sc); 320 octeon_fpa_init_regs(sc); 321 #ifdef OCTEON_ETH_DEBUG 322 octeon_fpa_int_enable(sc, 1); 323 octeon_fpa_intr_evcnt_attach(sc); 324 #endif 325 } 326 327 void 328 octeon_fpa_init_bus(struct octeon_fpa_softc *sc) 329 { 330 octeon_fpa_init_bus_space(sc); 331 } 332 333 void 334 octeon_fpa_init_bus_space(struct octeon_fpa_softc *sc) 335 { 336 int status; 337 338 status = bus_space_map(sc->sc_regt, FPA_BASE, FPA_SIZE, 0, &sc->sc_regh); 339 if (status != 0) 340 panic("can't map %s space", "register"); 341 342 status = bus_space_map(sc->sc_opst, 343 0x0001180028000000ULL/* XXX */, 0x0200/* XXX */, 0, &sc->sc_opsh); 344 if (status != 0) 345 panic("can't map %s space", "operations"); 346 } 347 348 void 349 octeon_fpa_init_regs(struct octeon_fpa_softc *sc) 350 { 351 352 bus_space_write_8(sc->sc_regt, sc->sc_regh, FPA_CTL_STATUS_OFFSET, 353 FPA_CTL_STATUS_ENB); 354 355 /* XXX */ 356 #ifdef OCTEON_ETH_DEBUG 357 bus_space_write_8(sc->sc_regt, sc->sc_regh, FPA_INT_ENB_OFFSET, 358 FPA_INT_ENB_Q7_PERR | FPA_INT_ENB_Q7_COFF | FPA_INT_ENB_Q7_UND | 359 FPA_INT_ENB_Q6_PERR | FPA_INT_ENB_Q6_COFF | FPA_INT_ENB_Q6_UND | 360 FPA_INT_ENB_Q5_PERR | FPA_INT_ENB_Q5_COFF | FPA_INT_ENB_Q5_UND | 361 FPA_INT_ENB_Q4_PERR | FPA_INT_ENB_Q4_COFF | FPA_INT_ENB_Q4_UND | 362 FPA_INT_ENB_Q3_PERR | FPA_INT_ENB_Q3_COFF | FPA_INT_ENB_Q3_UND | 363 FPA_INT_ENB_Q2_PERR | FPA_INT_ENB_Q2_COFF | FPA_INT_ENB_Q2_UND | 364 FPA_INT_ENB_Q1_PERR | FPA_INT_ENB_Q1_COFF | FPA_INT_ENB_Q1_UND | 365 FPA_INT_ENB_Q0_PERR | FPA_INT_ENB_Q0_COFF | FPA_INT_ENB_Q0_UND | 366 FPA_INT_ENB_FED1_DBE | FPA_INT_ENB_FED1_SBE | 367 FPA_INT_ENB_FED0_DBE | FPA_INT_ENB_FED0_SBE); 368 #endif 369 } 370 371 int 372 octeon_fpa_available_fpa_pool(int *available, int pool_no) { 373 struct octeon_fpa_softc *sc = &octeon_fpa_softc; 374 size_t offset; 375 uint64_t tmp; 376 377 switch (pool_no) { 378 case OCTEON_POOL_NO_PKT: 379 offset = FPA_QUE0_AVAILABLE_OFFSET; 380 break; 381 case OCTEON_POOL_NO_WQE: 382 offset = FPA_QUE1_AVAILABLE_OFFSET; 383 break; 384 case OCTEON_POOL_NO_CMD: 385 offset = FPA_QUE2_AVAILABLE_OFFSET; 386 break; 387 case OCTEON_POOL_NO_SG: 388 offset = FPA_QUE3_AVAILABLE_OFFSET; 389 break; 390 case OCTEON_POOL_NO_XXX_4: 391 offset = FPA_QUE4_AVAILABLE_OFFSET; 392 break; 393 case OCTEON_POOL_NO_XXX_5: 394 offset = FPA_QUE5_AVAILABLE_OFFSET; 395 break; 396 case OCTEON_POOL_NO_XXX_6: 397 offset = FPA_QUE6_AVAILABLE_OFFSET; 398 break; 399 case OCTEON_POOL_NO_DUMP: 400 offset = FPA_QUE7_AVAILABLE_OFFSET; 401 break; 402 default: 403 return EINVAL; 404 } 405 tmp = bus_space_read_8(sc->sc_regt, sc->sc_regh, offset); 406 if (available) { 407 *available = (int)(tmp & FPA_QUEX_AVAILABLE_QUE_SIZ); 408 } 409 410 return 0; 411 } 412 413 #ifdef OCTEON_ETH_DEBUG 414 void octeon_fpa_dump_regs(void); 415 void octeon_fpa_dump_bufs(void); 416 void octeon_fpa_dump_buf(int); 417 418 #define _ENTRY(x) { #x, x##_BITS, x##_OFFSET } 419 420 struct octeon_fpa_dump_reg_ { 421 const char *name; 422 const char *format; 423 size_t offset; 424 }; 425 426 static const struct octeon_fpa_dump_reg_ octeon_fpa_dump_regs_[] = { 427 _ENTRY(FPA_INT_SUM), 428 _ENTRY(FPA_INT_ENB), 429 _ENTRY(FPA_CTL_STATUS), 430 _ENTRY(FPA_QUE0_AVAILABLE), 431 _ENTRY(FPA_QUE1_AVAILABLE), 432 _ENTRY(FPA_QUE2_AVAILABLE), 433 _ENTRY(FPA_QUE3_AVAILABLE), 434 _ENTRY(FPA_QUE4_AVAILABLE), 435 _ENTRY(FPA_QUE5_AVAILABLE), 436 _ENTRY(FPA_QUE6_AVAILABLE), 437 _ENTRY(FPA_QUE7_AVAILABLE), 438 _ENTRY(FPA_WART_CTL), 439 _ENTRY(FPA_WART_STATUS), 440 _ENTRY(FPA_BIST_STATUS), 441 _ENTRY(FPA_QUE0_PAGE_INDEX), 442 _ENTRY(FPA_QUE1_PAGE_INDEX), 443 _ENTRY(FPA_QUE2_PAGE_INDEX), 444 _ENTRY(FPA_QUE3_PAGE_INDEX), 445 _ENTRY(FPA_QUE4_PAGE_INDEX), 446 _ENTRY(FPA_QUE5_PAGE_INDEX), 447 _ENTRY(FPA_QUE6_PAGE_INDEX), 448 _ENTRY(FPA_QUE7_PAGE_INDEX), 449 _ENTRY(FPA_QUE_EXP), 450 _ENTRY(FPA_QUE_ACT), 451 }; 452 453 static const char *octeon_fpa_dump_bufs_[8] = { 454 [0] = "recv", 455 [1] = "wq", 456 [2] = "cmdbuf", 457 [3] = "gbuf", 458 }; 459 460 void 461 octeon_fpa_dump(void) 462 { 463 octeon_fpa_dump_regs(); 464 octeon_fpa_dump_bufs(); 465 } 466 467 void 468 octeon_fpa_dump_regs(void) 469 { 470 struct octeon_fpa_softc *sc = &octeon_fpa_softc; 471 const struct octeon_fpa_dump_reg_ *reg; 472 uint64_t tmp; 473 char buf[512]; 474 int i; 475 476 for (i = 0; i < (int)__arraycount(octeon_fpa_dump_regs_); i++) { 477 reg = &octeon_fpa_dump_regs_[i]; 478 tmp = bus_space_read_8(sc->sc_regt, sc->sc_regh, reg->offset); 479 if (reg->format == NULL) { 480 snprintf(buf, sizeof(buf), "%16" PRIx64, tmp); 481 } else { 482 snprintb(buf, sizeof(buf), reg->format, tmp); 483 } 484 printf("\t%-24s: %s\n", reg->name, buf); 485 } 486 } 487 488 /* 489 * XXX assume pool 7 is unused! 490 */ 491 void 492 octeon_fpa_dump_bufs(void) 493 { 494 int i; 495 496 for (i = 0; i < 8; i++) 497 octeon_fpa_dump_buf(i); 498 } 499 500 void 501 octeon_fpa_dump_buf(int pool) 502 { 503 int i; 504 uint64_t ptr; 505 const char *name; 506 507 name = octeon_fpa_dump_bufs_[pool]; 508 if (name == NULL) 509 return; 510 printf("%s pool:\n", name); 511 for (i = 0; (ptr = octeon_fpa_load(pool)) != 0; i++) { 512 printf("\t%016" PRIx64 "%s", ptr, ((i % 4) == 3) ? "\n" : ""); 513 octeon_fpa_store(ptr, OCTEON_POOL_NO_DUMP, 0); 514 } 515 if (i % 4 != 3) 516 printf("\n"); 517 printf("total = %d buffers\n", i); 518 while ((ptr = octeon_fpa_load(OCTEON_POOL_NO_DUMP)) != 0) 519 octeon_fpa_store(ptr, pool, 0); 520 } 521 #endif 522