1 /* $NetBSD: mvxpbm.c,v 1.2 2018/05/07 09:41:10 maxv Exp $ */ 2 /* 3 * Copyright (c) 2015 Internet Initiative Japan Inc. 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 19 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 23 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 24 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 * POSSIBILITY OF SUCH DAMAGE. 26 */ 27 #include <sys/cdefs.h> 28 __KERNEL_RCSID(0, "$NetBSD: mvxpbm.c,v 1.2 2018/05/07 09:41:10 maxv Exp $"); 29 30 #include "opt_multiprocessor.h" 31 32 #include <sys/param.h> 33 #include <sys/systm.h> 34 #include <sys/device.h> 35 #include <sys/mbuf.h> 36 37 #include <dev/marvell/marvellreg.h> 38 #include <dev/marvell/marvellvar.h> 39 40 #include "mvxpbmvar.h" 41 42 #ifdef DEBUG 43 #define STATIC /* nothing */ 44 #define DPRINTF(fmt, ...) \ 45 do { \ 46 if (mvxpbm_debug >= 1) { \ 47 printf("%s: ", __func__); \ 48 printf((fmt), ##__VA_ARGS__); \ 49 } \ 50 } while (/*CONSTCOND*/0) 51 #define DPRINTFN(level , fmt, ...) \ 52 do { \ 53 if (mvxpbm_debug >= (level)) { \ 54 printf("%s: ", __func__); \ 55 printf((fmt), ##__VA_ARGS__); \ 56 } \ 57 } while (/*CONSTCOND*/0) 58 #define DPRINTDEV(dev, level, fmt, ...) \ 59 do { \ 60 if (mvxpbm_debug >= (level)) { \ 61 device_printf((dev), \ 62 "%s: "fmt , __func__, ##__VA_ARGS__); \ 63 } \ 64 } while (/*CONSTCOND*/0) 65 #define DPRINTSC(sc, level, fmt, ...) \ 66 do { \ 67 device_t dev = (sc)->sc_dev; \ 68 if (mvxpbm_debug >= (level)) { \ 69 device_printf(dev, \ 70 "%s: " fmt, __func__, ##__VA_ARGS__); \ 71 } \ 72 } while (/*CONSTCOND*/0) 73 #else 74 #define STATIC static 75 #define DPRINTF(fmt, ...) 76 #define DPRINTFN(level, fmt, ...) 77 #define DPRINTDEV(dev, level, fmt, ...) 78 #define DPRINTSC(sc, level, fmt, ...) 79 #endif 80 81 /* autoconf(9) */ 82 STATIC int mvxpbm_match(device_t, cfdata_t, void *); 83 STATIC void mvxpbm_attach(device_t, device_t, void *); 84 STATIC int mvxpbm_evcnt_attach(struct mvxpbm_softc *); 85 CFATTACH_DECL_NEW(mvxpbm_mbus, sizeof(struct mvxpbm_softc), 86 mvxpbm_match, mvxpbm_attach, NULL, NULL); 87 88 /* DMA buffers */ 89 STATIC int mvxpbm_alloc_buffer(struct mvxpbm_softc *); 90 91 /* mbuf subroutines */ 92 STATIC void mvxpbm_free_mbuf(struct mbuf *, void *, size_t, void *); 93 94 /* singleton device instance */ 95 static struct mvxpbm_softc sc_emul; 96 static struct mvxpbm_softc *sc0; 97 98 /* debug level */ 99 #ifdef DEBUG 100 static int mvxpbm_debug = 0; 101 #endif 102 103 /* 104 * autoconf(9) 105 */ 106 STATIC int 107 mvxpbm_match(device_t parent, cfdata_t match, void *aux) 108 { 109 struct marvell_attach_args *mva = aux; 110 111 if (strcmp(mva->mva_name, match->cf_name) != 0) 112 return 0; 113 if (mva->mva_unit > MVXPBM_UNIT_MAX) 114 return 0; 115 if (sc0 != NULL) 116 return 0; 117 if (mva->mva_offset != MVA_OFFSET_DEFAULT) { 118 /* Hardware BM is not supported yet. */ 119 return 0; 120 } 121 122 return 1; 123 } 124 125 STATIC void 126 mvxpbm_attach(device_t parnet, device_t self, void *aux) 127 { 128 struct marvell_attach_args *mva = aux; 129 struct mvxpbm_softc *sc = device_private(self); 130 131 aprint_naive("\n"); 132 aprint_normal(": Marvell ARMADA Buffer Manager\n"); 133 memset(sc, 0, sizeof(*sc)); 134 sc->sc_dev = self; 135 sc->sc_iot = mva->mva_iot; 136 sc->sc_dmat = mva->mva_dmat; 137 138 if (mva->mva_offset == MVA_OFFSET_DEFAULT) { 139 aprint_normal_dev(sc->sc_dev, "Software emulation.\n"); 140 sc->sc_emul = 1; 141 } 142 143 mutex_init(&sc->sc_mtx, MUTEX_DEFAULT, IPL_NET); 144 LIST_INIT(&sc->sc_free); 145 LIST_INIT(&sc->sc_inuse); 146 147 /* DMA buffers */ 148 if (mvxpbm_alloc_buffer(sc) != 0) 149 return; 150 151 /* event counters */ 152 mvxpbm_evcnt_attach(sc); 153 154 sc0 = sc; 155 return; 156 157 } 158 159 STATIC int 160 mvxpbm_evcnt_attach(struct mvxpbm_softc *sc) 161 { 162 return 0; 163 } 164 165 /* 166 * DMA buffers 167 */ 168 STATIC int 169 mvxpbm_alloc_buffer(struct mvxpbm_softc *sc) 170 { 171 bus_dma_segment_t segs; 172 char *kva, *ptr, *ptr_next, *ptr_data; 173 char *bm_buf_end; 174 uint32_t align, pad; 175 int nsegs; 176 int error; 177 178 /* 179 * set default buffer sizes. this will changed to satisfy 180 * alignment restrictions. 181 */ 182 sc->sc_chunk_count = 0; 183 sc->sc_chunk_size = MVXPBM_PACKET_SIZE; 184 sc->sc_chunk_header_size = sizeof(struct mvxpbm_chunk); 185 sc->sc_chunk_packet_offset = 64; 186 187 /* 188 * adjust bm_chunk_size, bm_chunk_header_size, bm_slotsize 189 * to satisfy alignemnt restrictions. 190 * 191 * <---------------- bm_slotsize [oct.] ------------------> 192 * <--- bm_chunk_size[oct.] ----> 193 * <--- header_size[oct] ---> <-- MBXPE_BM_SIZE[oct.] ---> 194 * +-----------------+--------+---------+-----------------+--+ 195 * | bm_chunk hdr |pad |pkt_off | packet data | | 196 * +-----------------+--------+---------+-----------------+--+ 197 * ^ ^ ^ ^ 198 * | | | | 199 * ptr ptr_data DMA here ptr_next 200 * 201 * Restrictions: 202 * - total buffer size must be multiple of MVXPBM_BUF_ALIGN 203 * - ptr must be aligned to MVXPBM_CHUNK_ALIGN 204 * - ptr_data must be aligned to MVXPEBM_DATA_ALIGN 205 * - bm_chunk_size must be multiple of 8[bytes]. 206 */ 207 /* start calclation from 0x0000.0000 */ 208 ptr = (char *)0; 209 210 /* align start of packet data */ 211 ptr_data = ptr + sc->sc_chunk_header_size; 212 align = (unsigned long)ptr_data & MVXPBM_DATA_MASK; 213 if (align != 0) { 214 pad = MVXPBM_DATA_ALIGN - align; 215 sc->sc_chunk_header_size += pad; 216 DPRINTSC(sc, 1, "added padding to BM header, %u bytes\n", pad); 217 } 218 219 /* align size of packet data */ 220 ptr_data = ptr + sc->sc_chunk_header_size; 221 ptr_next = ptr_data + MVXPBM_PACKET_SIZE; 222 align = (unsigned long)ptr_next & MVXPBM_CHUNK_MASK; 223 if (align != 0) { 224 pad = MVXPBM_CHUNK_ALIGN - align; 225 ptr_next += pad; 226 DPRINTSC(sc, 1, "added padding to BM pktbuf, %u bytes\n", pad); 227 } 228 sc->sc_slotsize = ptr_next - ptr; 229 sc->sc_chunk_size = ptr_next - ptr_data; 230 KASSERT((sc->sc_chunk_size % MVXPBM_DATA_UNIT) == 0); 231 232 /* align total buffer size to Mbus window boundary */ 233 sc->sc_buf_size = sc->sc_slotsize * MVXPBM_NUM_SLOTS; 234 align = (unsigned long)sc->sc_buf_size & MVXPBM_BUF_MASK; 235 if (align != 0) { 236 pad = MVXPBM_BUF_ALIGN - align; 237 sc->sc_buf_size += pad; 238 DPRINTSC(sc, 1, 239 "expand buffer to fit page boundary, %u bytes\n", pad); 240 } 241 242 /* 243 * get the aligned buffer from busdma(9) framework 244 */ 245 if (bus_dmamem_alloc(sc->sc_dmat, sc->sc_buf_size, MVXPBM_BUF_ALIGN, 0, 246 &segs, 1, &nsegs, BUS_DMA_NOWAIT)) { 247 aprint_error_dev(sc->sc_dev, "can't alloc BM buffers\n"); 248 return ENOBUFS; 249 } 250 if (bus_dmamem_map(sc->sc_dmat, &segs, nsegs, sc->sc_buf_size, 251 (void **)&kva, BUS_DMA_NOWAIT)) { 252 aprint_error_dev(sc->sc_dev, 253 "can't map dma buffers (%zu bytes)\n", sc->sc_buf_size); 254 error = ENOBUFS; 255 goto fail1; 256 } 257 if (bus_dmamap_create(sc->sc_dmat, sc->sc_buf_size, 1, sc->sc_buf_size, 258 0, BUS_DMA_NOWAIT, &sc->sc_buf_map)) { 259 aprint_error_dev(sc->sc_dev, "can't create dma map\n"); 260 error = ENOBUFS; 261 goto fail2; 262 } 263 if (bus_dmamap_load(sc->sc_dmat, sc->sc_buf_map, 264 kva, sc->sc_buf_size, NULL, BUS_DMA_NOWAIT)) { 265 aprint_error_dev(sc->sc_dev, "can't load dma map\n"); 266 error = ENOBUFS; 267 goto fail3; 268 } 269 sc->sc_buf = (void *)kva; 270 sc->sc_buf_pa = segs.ds_addr; 271 bm_buf_end = (void *)(kva + sc->sc_buf_size); 272 DPRINTSC(sc, 1, "memory pool at %p\n", sc->sc_buf); 273 274 /* slice the buffer */ 275 mvxpbm_lock(sc); 276 for (ptr = sc->sc_buf; ptr + sc->sc_slotsize <= bm_buf_end; 277 ptr += sc->sc_slotsize) { 278 struct mvxpbm_chunk *chunk; 279 280 /* initialzie chunk */ 281 ptr_data = ptr + sc->sc_chunk_header_size; 282 chunk = (struct mvxpbm_chunk *)ptr; 283 chunk->m = NULL; 284 chunk->sc = sc; 285 chunk->off = (ptr - sc->sc_buf); 286 chunk->pa = (paddr_t)(sc->sc_buf_pa + chunk->off); 287 chunk->buf_off = (ptr_data - sc->sc_buf); 288 chunk->buf_pa = (paddr_t)(sc->sc_buf_pa + chunk->buf_off); 289 chunk->buf_va = (vaddr_t)(sc->sc_buf + chunk->buf_off); 290 chunk->buf_size = sc->sc_chunk_size; 291 292 /* add to free list (for software management) */ 293 LIST_INSERT_HEAD(&sc->sc_free, chunk, link); 294 mvxpbm_dmamap_sync(chunk, BM_SYNC_ALL, BUS_DMASYNC_PREREAD); 295 sc->sc_chunk_count++; 296 297 DPRINTSC(sc, 9, "new chunk %p\n", (void *)chunk->buf_va); 298 } 299 mvxpbm_unlock(sc); 300 return 0; 301 302 fail3: 303 bus_dmamap_destroy(sc->sc_dmat, sc->sc_buf_map); 304 fail2: 305 bus_dmamem_unmap(sc->sc_dmat, kva, sc->sc_buf_size); 306 fail1: 307 bus_dmamem_free(sc->sc_dmat, &segs, nsegs); 308 309 return error; 310 } 311 312 /* 313 * mbuf subroutines 314 */ 315 STATIC void 316 mvxpbm_free_mbuf(struct mbuf *m, void *buf, size_t size, void *arg) 317 { 318 struct mvxpbm_chunk *chunk = (struct mvxpbm_chunk *)arg; 319 int s; 320 321 KASSERT(m != NULL); 322 KASSERT(arg != NULL); 323 324 DPRINTFN(3, "free packet %p\n", m); 325 326 chunk->m = NULL; 327 s = splvm(); 328 pool_cache_put(mb_cache, m); 329 splx(s); 330 return mvxpbm_free_chunk(chunk); 331 } 332 333 /* 334 * Exported APIs 335 */ 336 /* get mvxpbm device context */ 337 struct mvxpbm_softc * 338 mvxpbm_device(struct marvell_attach_args *mva) 339 { 340 struct mvxpbm_softc *sc; 341 342 if (sc0 != NULL) 343 return sc0; 344 if (mva == NULL) 345 return NULL; 346 347 /* allocate software emulation context */ 348 sc = &sc_emul; 349 memset(sc, 0, sizeof(*sc)); 350 sc->sc_emul = 1; 351 sc->sc_iot = mva->mva_iot; 352 sc->sc_dmat = mva->mva_dmat; 353 354 mutex_init(&sc->sc_mtx, MUTEX_DEFAULT, IPL_NET); 355 LIST_INIT(&sc->sc_free); 356 LIST_INIT(&sc->sc_inuse); 357 358 if (mvxpbm_alloc_buffer(sc) != 0) 359 return NULL; 360 mvxpbm_evcnt_attach(sc); 361 sc0 = sc; 362 return sc0; 363 } 364 365 /* allocate new memory chunk */ 366 struct mvxpbm_chunk * 367 mvxpbm_alloc(struct mvxpbm_softc *sc) 368 { 369 struct mvxpbm_chunk *chunk; 370 371 mvxpbm_lock(sc); 372 373 chunk = LIST_FIRST(&sc->sc_free); 374 if (chunk == NULL) { 375 mvxpbm_unlock(sc); 376 return NULL; 377 } 378 379 LIST_REMOVE(chunk, link); 380 LIST_INSERT_HEAD(&sc->sc_inuse, chunk, link); 381 382 mvxpbm_unlock(sc); 383 return chunk; 384 } 385 386 /* free memory chunk */ 387 void 388 mvxpbm_free_chunk(struct mvxpbm_chunk *chunk) 389 { 390 struct mvxpbm_softc *sc = chunk->sc; 391 392 KASSERT(chunk->m == NULL); 393 DPRINTFN(3, "bm chunk free\n"); 394 395 mvxpbm_lock(sc); 396 397 LIST_REMOVE(chunk, link); 398 LIST_INSERT_HEAD(&sc->sc_free, chunk, link); 399 400 mvxpbm_unlock(sc); 401 } 402 403 /* prepare mbuf header after Rx */ 404 int 405 mvxpbm_init_mbuf_hdr(struct mvxpbm_chunk *chunk) 406 { 407 struct mvxpbm_softc *sc = chunk->sc; 408 409 KASSERT(chunk->m == NULL); 410 411 /* add new mbuf header */ 412 MGETHDR(chunk->m, M_DONTWAIT, MT_DATA); 413 if (chunk->m == NULL) { 414 aprint_error_dev(sc->sc_dev, "cannot get mbuf\n"); 415 return ENOBUFS; 416 } 417 MEXTADD(chunk->m, chunk->buf_va, chunk->buf_size, 0, 418 mvxpbm_free_mbuf, chunk); 419 chunk->m->m_flags |= M_EXT_RW; 420 chunk->m->m_len = chunk->m->m_pkthdr.len = chunk->buf_size; 421 if (sc->sc_chunk_packet_offset) 422 m_adj(chunk->m, sc->sc_chunk_packet_offset); 423 424 return 0; 425 } 426 427 /* sync DMA seguments */ 428 void 429 mvxpbm_dmamap_sync(struct mvxpbm_chunk *chunk, size_t size, int ops) 430 { 431 struct mvxpbm_softc *sc = chunk->sc; 432 433 KASSERT(size <= chunk->buf_size); 434 if (size == 0) 435 size = chunk->buf_size; 436 437 bus_dmamap_sync(sc->sc_dmat, sc->sc_buf_map, chunk->buf_off, size, ops); 438 } 439 440 /* lock */ 441 void 442 mvxpbm_lock(struct mvxpbm_softc *sc) 443 { 444 mutex_enter(&sc->sc_mtx); 445 } 446 447 void 448 mvxpbm_unlock(struct mvxpbm_softc *sc) 449 { 450 mutex_exit(&sc->sc_mtx); 451 } 452 453 /* get params */ 454 const char * 455 mvxpbm_xname(struct mvxpbm_softc *sc) 456 { 457 if (sc->sc_emul) { 458 return "software_bm"; 459 } 460 return device_xname(sc->sc_dev); 461 } 462 463 size_t 464 mvxpbm_chunk_size(struct mvxpbm_softc *sc) 465 { 466 return sc->sc_chunk_size; 467 } 468 469 uint32_t 470 mvxpbm_chunk_count(struct mvxpbm_softc *sc) 471 { 472 return sc->sc_chunk_count; 473 } 474 475 off_t 476 mvxpbm_packet_offset(struct mvxpbm_softc *sc) 477 { 478 return sc->sc_chunk_packet_offset; 479 } 480 481 paddr_t 482 mvxpbm_buf_pbase(struct mvxpbm_softc *sc) 483 { 484 return sc->sc_buf_pa; 485 } 486 487 size_t 488 mvxpbm_buf_size(struct mvxpbm_softc *sc) 489 { 490 return sc->sc_buf_size; 491 } 492