diff options
author | Justin Hibbits <jhibbits@FreeBSD.org> | 2016-02-29 03:38:00 +0000 |
---|---|---|
committer | Justin Hibbits <jhibbits@FreeBSD.org> | 2016-02-29 03:38:00 +0000 |
commit | 0aeed3e99367bed5755068d9218cd8041644ff2b (patch) | |
tree | 5b00ae419405b400d27fe05a3ed4868b17bbcb84 /sys/dev/dpaa | |
parent | b25855675953dace218f705aea63cc7437590bf8 (diff) | |
download | src-0aeed3e99367bed5755068d9218cd8041644ff2b.tar.gz src-0aeed3e99367bed5755068d9218cd8041644ff2b.zip |
Notes
Diffstat (limited to 'sys/dev/dpaa')
-rw-r--r-- | sys/dev/dpaa/bman.c | 370 | ||||
-rw-r--r-- | sys/dev/dpaa/bman.h | 204 | ||||
-rw-r--r-- | sys/dev/dpaa/bman_fdt.c | 225 | ||||
-rw-r--r-- | sys/dev/dpaa/bman_portals.c | 180 | ||||
-rw-r--r-- | sys/dev/dpaa/dpaa.c | 184 | ||||
-rw-r--r-- | sys/dev/dpaa/fman.c | 357 | ||||
-rw-r--r-- | sys/dev/dpaa/fman.h | 67 | ||||
-rw-r--r-- | sys/dev/dpaa/fman_fdt.c | 104 | ||||
-rw-r--r-- | sys/dev/dpaa/if_dtsec.c | 879 | ||||
-rw-r--r-- | sys/dev/dpaa/if_dtsec.h | 154 | ||||
-rw-r--r-- | sys/dev/dpaa/if_dtsec_fdt.c | 215 | ||||
-rw-r--r-- | sys/dev/dpaa/if_dtsec_im.c | 262 | ||||
-rw-r--r-- | sys/dev/dpaa/if_dtsec_im.h | 41 | ||||
-rw-r--r-- | sys/dev/dpaa/if_dtsec_rm.c | 654 | ||||
-rw-r--r-- | sys/dev/dpaa/if_dtsec_rm.h | 53 | ||||
-rw-r--r-- | sys/dev/dpaa/portals.h | 64 | ||||
-rw-r--r-- | sys/dev/dpaa/portals_common.c | 166 | ||||
-rw-r--r-- | sys/dev/dpaa/qman.c | 555 | ||||
-rw-r--r-- | sys/dev/dpaa/qman.h | 246 | ||||
-rw-r--r-- | sys/dev/dpaa/qman_fdt.c | 221 | ||||
-rw-r--r-- | sys/dev/dpaa/qman_portals.c | 191 |
21 files changed, 5392 insertions, 0 deletions
diff --git a/sys/dev/dpaa/bman.c b/sys/dev/dpaa/bman.c new file mode 100644 index 000000000000..02499c4902f4 --- /dev/null +++ b/sys/dev/dpaa/bman.c @@ -0,0 +1,370 @@ +/*- + * Copyright (c) 2011-2012 Semihalf. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/bus.h> +#include <sys/lock.h> +#include <sys/module.h> +#include <sys/mutex.h> +#include <sys/proc.h> +#include <sys/pcpu.h> +#include <sys/rman.h> +#include <sys/sched.h> + +#include <machine/tlb.h> + +#include "bman.h" + +devclass_t bman_devclass; + +static struct bman_softc *bman_sc; + +extern t_Handle bman_portal_setup(struct bman_softc *bsc); + +static void +bman_exception(t_Handle h_App, e_BmExceptions exception) +{ + struct bman_softc *sc; + const char *message; + + sc = h_App; + + switch (exception) { + case e_BM_EX_INVALID_COMMAND: + message = "Invalid Command Verb"; + break; + case e_BM_EX_FBPR_THRESHOLD: + message = "FBPR pool exhaused. Consider increasing " + "BMAN_MAX_BUFFERS"; + break; + case e_BM_EX_SINGLE_ECC: + message = "Single bit ECC error"; + break; + case e_BM_EX_MULTI_ECC: + message = "Multi bit ECC error"; + break; + default: + message = "Unknown error"; + } + + device_printf(sc->sc_dev, "BMAN Exception: %s.\n", message); +} + +int +bman_attach(device_t dev) +{ + struct bman_softc *sc; + t_BmRevisionInfo rev; + t_Error error; + t_BmParam bp; + + sc = device_get_softc(dev); + sc->sc_dev = dev; + bman_sc = sc; + + /* Check if MallocSmart allocator is ready */ + if (XX_MallocSmartInit() != E_OK) + return (ENXIO); + + /* Allocate resources */ + sc->sc_rrid = 0; + sc->sc_rres = bus_alloc_resource(dev, SYS_RES_MEMORY, + &sc->sc_rrid, 0, ~0, BMAN_CCSR_SIZE, RF_ACTIVE); + if (sc->sc_rres == NULL) + return (ENXIO); + + sc->sc_irid = 0; + sc->sc_ires = bus_alloc_resource_any(sc->sc_dev, SYS_RES_IRQ, + &sc->sc_irid, RF_ACTIVE | RF_SHAREABLE); + if (sc->sc_ires == NULL) + goto err; + + /* Initialize BMAN */ + memset(&bp, 0, sizeof(bp)); + bp.guestId = NCSW_MASTER_ID; + bp.baseAddress = rman_get_bushandle(sc->sc_rres); + bp.totalNumOfBuffers = BMAN_MAX_BUFFERS; + bp.f_Exception = bman_exception; + bp.h_App = sc; + bp.errIrq = (int)sc->sc_ires; + bp.partBpidBase = 0; + bp.partNumOfPools = BM_MAX_NUM_OF_POOLS; + printf("base address: %llx\n", (uint64_t)bp.baseAddress); + + sc->sc_bh = BM_Config(&bp); + if (sc->sc_bh == NULL) + goto err; + + /* Warn if there is less than 5% free FPBR's in pool */ + error = BM_ConfigFbprThreshold(sc->sc_bh, (BMAN_MAX_BUFFERS / 8) / 20); + if (error != E_OK) + goto err; + + error = BM_Init(sc->sc_bh); + if (error != E_OK) + goto err; + + error = BM_GetRevision(sc->sc_bh, &rev); + if (error != E_OK) + goto err; + + device_printf(dev, "Hardware version: %d.%d.\n", + rev.majorRev, rev.minorRev); + + return (0); + +err: + bman_detach(dev); + return (ENXIO); +} + +int +bman_detach(device_t dev) +{ + struct bman_softc *sc; + + sc = device_get_softc(dev); + + if (sc->sc_bh != NULL) + BM_Free(sc->sc_bh); + + if (sc->sc_ires != NULL) + bus_release_resource(dev, SYS_RES_IRQ, + sc->sc_irid, sc->sc_ires); + + if (sc->sc_rres != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, + sc->sc_rrid, sc->sc_rres); + + return (0); +} + +int +bman_suspend(device_t dev) +{ + + return (0); +} + +int +bman_resume(device_t dev) +{ + + return (0); +} + +int +bman_shutdown(device_t dev) +{ + + return (0); +} + +/* + * BMAN API + */ + +t_Handle +bman_pool_create(uint8_t *bpid, uint16_t bufferSize, uint16_t maxBuffers, + uint16_t minBuffers, uint16_t allocBuffers, t_GetBufFunction *f_GetBuf, + t_PutBufFunction *f_PutBuf, uint32_t dep_sw_entry, uint32_t dep_sw_exit, + uint32_t dep_hw_entry, uint32_t dep_hw_exit, + t_BmDepletionCallback *f_Depletion, t_Handle h_BufferPool, + t_PhysToVirt *f_PhysToVirt, t_VirtToPhys *f_VirtToPhys) +{ + uint32_t thresholds[MAX_DEPLETION_THRESHOLDS]; + struct bman_softc *sc; + t_Handle pool, portal; + t_BmPoolParam bpp; + int error; + + sc = bman_sc; + pool = NULL; + + sched_pin(); + + portal = bman_portal_setup(sc); + if (portal == NULL) + goto err; + + memset(&bpp, 0, sizeof(bpp)); + bpp.h_Bm = sc->sc_bh; + bpp.h_BmPortal = portal; + bpp.h_App = h_BufferPool; + bpp.numOfBuffers = allocBuffers; + + bpp.bufferPoolInfo.h_BufferPool = h_BufferPool; + bpp.bufferPoolInfo.f_GetBuf = f_GetBuf; + bpp.bufferPoolInfo.f_PutBuf = f_PutBuf; + bpp.bufferPoolInfo.f_PhysToVirt = f_PhysToVirt; + bpp.bufferPoolInfo.f_VirtToPhys = f_VirtToPhys; + bpp.bufferPoolInfo.bufferSize = bufferSize; + + pool = BM_POOL_Config(&bpp); + if (pool == NULL) + goto err; + + /* + * Buffer context must be disabled on FreeBSD + * as it could cause memory corruption. + */ + BM_POOL_ConfigBuffContextMode(pool, 0); + + if (minBuffers != 0 || maxBuffers != 0) { + error = BM_POOL_ConfigStockpile(pool, maxBuffers, minBuffers); + if (error != E_OK) + goto err; + } + + if (f_Depletion != NULL) { + thresholds[BM_POOL_DEP_THRESH_SW_ENTRY] = dep_sw_entry; + thresholds[BM_POOL_DEP_THRESH_SW_EXIT] = dep_sw_exit; + thresholds[BM_POOL_DEP_THRESH_HW_ENTRY] = dep_hw_entry; + thresholds[BM_POOL_DEP_THRESH_HW_EXIT] = dep_hw_exit; + error = BM_POOL_ConfigDepletion(pool, f_Depletion, thresholds); + if (error != E_OK) + goto err; + } + + error = BM_POOL_Init(pool); + if (error != E_OK) + goto err; + + *bpid = BM_POOL_GetId(pool); + sc->sc_bpool_cpu[*bpid] = PCPU_GET(cpuid); + + sched_unpin(); + + return (pool); + +err: + if (pool != NULL) + BM_POOL_Free(pool); + + sched_unpin(); + + return (NULL); +} + +int +bman_pool_destroy(t_Handle pool) +{ + struct bman_softc *sc; + + sc = bman_sc; + thread_lock(curthread); + sched_bind(curthread, sc->sc_bpool_cpu[BM_POOL_GetId(pool)]); + thread_unlock(curthread); + + BM_POOL_Free(pool); + + thread_lock(curthread); + sched_unbind(curthread); + thread_unlock(curthread); + + return (0); +} + +int +bman_pool_fill(t_Handle pool, uint16_t nbufs) +{ + struct bman_softc *sc; + t_Handle portal; + int error; + + sc = bman_sc; + sched_pin(); + + portal = bman_portal_setup(sc); + if (portal == NULL) { + sched_unpin(); + return (EIO); + } + + error = BM_POOL_FillBufs(pool, portal, nbufs); + + sched_unpin(); + + return ((error == E_OK) ? 0 : EIO); +} + +void * +bman_get_buffer(t_Handle pool) +{ + struct bman_softc *sc; + t_Handle portal; + void *buffer; + + sc = bman_sc; + sched_pin(); + + portal = bman_portal_setup(sc); + if (portal == NULL) { + sched_unpin(); + return (NULL); + } + + buffer = BM_POOL_GetBuf(pool, portal); + + sched_unpin(); + + return (buffer); +} + +int +bman_put_buffer(t_Handle pool, void *buffer) +{ + struct bman_softc *sc; + t_Handle portal; + int error; + + sc = bman_sc; + sched_pin(); + + portal = bman_portal_setup(sc); + if (portal == NULL) { + sched_unpin(); + return (EIO); + } + + error = BM_POOL_PutBuf(pool, portal, buffer); + + sched_unpin(); + + return ((error == E_OK) ? 0 : EIO); +} + +uint32_t +bman_count(t_Handle pool) +{ + + return (BM_POOL_GetCounter(pool, e_BM_POOL_COUNTERS_CONTENT)); +} diff --git a/sys/dev/dpaa/bman.h b/sys/dev/dpaa/bman.h new file mode 100644 index 000000000000..449b8172281c --- /dev/null +++ b/sys/dev/dpaa/bman.h @@ -0,0 +1,204 @@ +/*- + * Copyright (c) 2011-2012 Semihalf. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _BMAN_H +#define _BMAN_H + +#include <machine/vmparam.h> + +#include <contrib/ncsw/inc/Peripherals/bm_ext.h> + +/* + * BMAN Configuration + */ + +/* Maximum number of buffers in all BMAN pools */ +#define BMAN_MAX_BUFFERS 4096 + +/* + * Portal definitions + */ +#define BMAN_CE_PA(base) (base) +#define BMAN_CI_PA(base) ((base) + 0x100000) + +#define BMAN_PORTAL_CE_PA(base, n) \ + (BMAN_CE_PA(base) + ((n) * BMAN_PORTAL_CE_SIZE)) +#define BMAN_PORTAL_CI_PA(base, n) \ + (BMAN_CI_PA(base) + ((n) * BMAN_PORTAL_CI_SIZE)) + +#define BMAN_CCSR_SIZE 0x1000 + +struct bman_softc { + device_t sc_dev; /* device handle */ + int sc_rrid; /* register rid */ + struct resource *sc_rres; /* register resource */ + int sc_irid; /* interrupt rid */ + struct resource *sc_ires; /* interrupt resource */ + + bool sc_regs_mapped[MAXCPU]; /* register mapping status */ + + t_Handle sc_bh; /* BMAN handle */ + t_Handle sc_bph[MAXCPU]; /* BMAN portal handles */ + vm_paddr_t sc_bp_pa; /* BMAN portals PA */ + unsigned int sc_bpool_cpu[BM_MAX_NUM_OF_POOLS]; +}; + +/* + * External API + */ + +/* + * @brief Function to create BMAN pool. + * + * @param bpid The pointer to variable where Buffer Pool ID will be + * stored. + * + * @param bufferSize The size of buffers in newly created pool. + * + * @param maxBuffers The maximum number of buffers in software stockpile. + * Set to 0 if software stockpile should not be created. + * + * @param minBuffers The minimum number of buffers in software stockpile. + * Set to 0 if software stockpile should not be created. + * + * @param allocBuffers The number of buffers to preallocate during pool + * creation. + * + * @param f_GetBuf The buffer allocating function. Called only by + * bman_pool_create() and bman_pool_fill(). + * + * @param f_PutBuf The buffer freeing function. Called only by + * bman_pool_destroy(). + * + * @param dep_sw_entry The software portal depletion entry threshold. + * Set to 0 if depletion should not be signaled on + * software portal. + * + * @param dep_sw_exit The software portal depletion exit threshold. + * Set to 0 if depletion should not be signaled on + * software portal. + * + * @param dep_hw_entry The hardware portal depletion entry threshold. + * Set to 0 if depletion should not be signaled on + * hardware portal. + * + * @param dep_hw_exit The hardware portal depletion exit threshold. + * Set to 0 if depletion should not be signaled on + * hardware portal. + * + * @param f_Depletion The software portal depletion notification function. + * Set to NULL if depletion notification is not used. + * + * @param h_BufferPool The user provided buffer pool context passed to + * f_GetBuf, f_PutBuf and f_Depletion functions. + * + * @param f_PhysToVirt The PA to VA translation function. Set to NULL if + * default one should be used. + * + * @param f_VirtToPhys The VA to PA translation function. Set to NULL if + * default one should be used. + * + * @returns Handle to newly created BMAN pool or NULL on error. + * + * @cautions If pool uses software stockpile, all accesses to given + * pool must be protected by lock. Even if only hardware + * portal depletion notification is used, the caller must + * provide valid @p f_Depletion function. + */ +t_Handle bman_pool_create(uint8_t *bpid, uint16_t bufferSize, + uint16_t maxBuffers, uint16_t minBuffers, uint16_t allocBuffers, + t_GetBufFunction *f_GetBuf, t_PutBufFunction *f_PutBuf, + uint32_t dep_sw_entry, uint32_t dep_sw_exit, uint32_t dep_hw_entry, + uint32_t dep_hw_exit, t_BmDepletionCallback *f_Depletion, + t_Handle h_BufferPool, t_PhysToVirt *f_PhysToVirt, + t_VirtToPhys *f_VirtToPhys); + +/* + * @brief Fill pool with buffers. + * + * The bman_pool_fill() function fills the BMAN pool with buffers. The buffers + * are allocated through f_GetBuf function (see bman_pool_create() description). + * + * @param pool The BMAN pool handle. + * @param nbufs The number of buffers to allocate. To maximize + * performance this value should be multiple of 8. + * + * @returns Zero on success or error code on failure. + */ +int bman_pool_fill(t_Handle pool, uint16_t nbufs); + +/* + * @brief Destroy pool. + * + * The bman_pool_destroy() function destroys the BMAN pool. Buffers for pool + * are free through f_PutBuf function (see bman_pool_create() description). + * + * @param pool The BMAN pool handle. + * + * @returns Zero on success or error code on failure. + */ +int bman_pool_destroy(t_Handle pool); + +/* + * @brief Get a buffer from BMAN pool. + * + * @param pool The BMAN pool handle. + * + * @returns Pointer to the buffer or NULL if pool is empty. + */ +void *bman_get_buffer(t_Handle pool); + +/* + * @brief Put a buffer to BMAN pool. + * + * @param pool The BMAN pool handle. + * @param buffer The pointer to buffer. + * + * @returns Zero on success or error code on failure. + */ +int bman_put_buffer(t_Handle pool, void *buffer); + +/* + * @brief Count free buffers in given pool. + * + * @param pool The BMAN pool handle. + * + * @returns Number of free buffers in pool. + */ +uint32_t bman_count(t_Handle pool); + +/* + * Bus i/f + */ +int bman_attach(device_t dev); +int bman_detach(device_t dev); +int bman_suspend(device_t dev); +int bman_resume(device_t dev); +int bman_shutdown(device_t dev); + +#endif /* BMAN_H */ diff --git a/sys/dev/dpaa/bman_fdt.c b/sys/dev/dpaa/bman_fdt.c new file mode 100644 index 000000000000..63f58adb4918 --- /dev/null +++ b/sys/dev/dpaa/bman_fdt.c @@ -0,0 +1,225 @@ +/*- + * Copyright (c) 2011-2012 Semihalf. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "opt_platform.h" +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/bus.h> +#include <sys/module.h> + +#include <machine/bus.h> + +#include <dev/fdt/fdt_common.h> + +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> +#include <dev/ofw/ofw_subr.h> + +#include "bman.h" +#include "portals.h" + +#define FBMAN_DEVSTR "Freescale Buffer Manager" + +static int bman_fdt_probe(device_t); + +static device_method_t bman_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, bman_fdt_probe), + DEVMETHOD(device_attach, bman_attach), + DEVMETHOD(device_detach, bman_detach), + + DEVMETHOD(device_suspend, bman_suspend), + DEVMETHOD(device_resume, bman_resume), + DEVMETHOD(device_shutdown, bman_shutdown), + + { 0, 0 } +}; + +static driver_t bman_driver = { + "bman", + bman_methods, + sizeof(struct bman_softc), +}; + +static devclass_t bman_devclass; +DRIVER_MODULE(bman, simplebus, bman_driver, bman_devclass, 0, 0); + +static int +bman_fdt_probe(device_t dev) +{ + + if (!ofw_bus_is_compatible(dev, "fsl,bman")) + return (ENXIO); + + device_set_desc(dev, FBMAN_DEVSTR); + + return (BUS_PROBE_DEFAULT); +} + +/* + * BMAN Portals + */ +#define BMAN_PORT_DEVSTR "Freescale Buffer Manager - Portals" + +static device_probe_t bman_portals_fdt_probe; +static device_attach_t bman_portals_fdt_attach; + +static device_method_t bm_portals_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, bman_portals_fdt_probe), + DEVMETHOD(device_attach, bman_portals_fdt_attach), + DEVMETHOD(device_detach, bman_portals_detach), + + { 0, 0 } +}; + +static driver_t bm_portals_driver = { + "bman-portals", + bm_portals_methods, + sizeof(struct dpaa_portals_softc), +}; + +static devclass_t bm_portals_devclass; +DRIVER_MODULE(bman_portals, ofwbus, bm_portals_driver, bm_portals_devclass, 0, 0); + +static void +get_addr_props(phandle_t node, uint32_t *addrp, uint32_t *sizep) +{ + + *addrp = 2; + *sizep = 1; + OF_getencprop(node, "#address-cells", addrp, sizeof(*addrp)); + OF_getencprop(node, "#size-cells", sizep, sizeof(*sizep)); +} + +static int +bman_portals_fdt_probe(device_t dev) +{ + + if (!ofw_bus_is_compatible(dev, "bman-portals")) + return (ENXIO); + + device_set_desc(dev, BMAN_PORT_DEVSTR); + + return (BUS_PROBE_DEFAULT); +} + +static int +bman_portals_fdt_attach(device_t dev) +{ + struct dpaa_portals_softc *sc; + struct resource_list_entry *rle; + phandle_t node, child, cpu_node; + vm_paddr_t portal_pa; + vm_size_t portal_size; + uint32_t addr, size; + ihandle_t cpu; + int cpu_num, cpus, intr_rid; + struct dpaa_portals_devinfo di; + struct ofw_bus_devinfo ofw_di; + + cpus = 0; + sc = device_get_softc(dev); + sc->sc_dev = dev; + + node = ofw_bus_get_node(dev); + get_addr_props(node, &addr, &size); + + /* Find portals tied to CPUs */ + for (child = OF_child(node); child != 0; child = OF_peer(child)) { + if (!fdt_is_compatible(child, "fsl,bman-portal")) { + continue; + } + /* Checkout related cpu */ + if (OF_getprop(child, "cpu-handle", (void *)&cpu, + sizeof(cpu)) <= 0) { + continue; + } + /* Acquire cpu number */ + cpu_node = OF_instance_to_package(cpu); + if (OF_getencprop(cpu_node, "reg", &cpu_num, sizeof(cpu_num)) <= 0) { + device_printf(dev, "Could not retrieve CPU number.\n"); + return (ENXIO); + } + + cpus++; + + if (cpus > MAXCPU) + break; + + if (ofw_bus_gen_setup_devinfo(&ofw_di, child) != 0) { + device_printf(dev, "could not set up devinfo\n"); + continue; + } + + resource_list_init(&di.di_res); + if (ofw_bus_reg_to_rl(dev, child, addr, size, &di.di_res)) { + device_printf(dev, "%s: could not process 'reg' " + "property\n", ofw_di.obd_name); + ofw_bus_gen_destroy_devinfo(&ofw_di); + continue; + } + if (ofw_bus_intr_to_rl(dev, child, &di.di_res, &intr_rid)) { + device_printf(dev, "%s: could not process " + "'interrupts' property\n", ofw_di.obd_name); + resource_list_free(&di.di_res); + ofw_bus_gen_destroy_devinfo(&ofw_di); + continue; + } + di.di_intr_rid = intr_rid; + + ofw_reg_to_paddr(child, 0, &portal_pa, &portal_size, NULL); + rle = resource_list_find(&di.di_res, SYS_RES_MEMORY, 0); + + if (sc->sc_dp_pa == 0) + sc->sc_dp_pa = portal_pa - rle->start; + + portal_size = rle->end + 1; + rle = resource_list_find(&di.di_res, SYS_RES_MEMORY, 1); + device_printf(dev, "portal size 1: %jx\n", (uintmax_t)portal_size); + device_printf(dev, "portal size 2: %jx\n", (uintmax_t)rle->end + 1); + portal_size = ulmax(rle->end + 1, portal_size); + sc->sc_dp_size = ulmax(sc->sc_dp_size, portal_size); + device_printf(dev, "winner: %jx\n", (uintmax_t)sc->sc_dp_size); + + if (dpaa_portal_alloc_res(dev, &di, cpu_num)) + goto err; + } + device_printf(dev, "portal start: %jx, size: %jx\n", (uintmax_t)sc->sc_dp_pa, (uintmax_t)sc->sc_dp_size); + + ofw_bus_gen_destroy_devinfo(&ofw_di); + + return (bman_portals_attach(dev)); +err: + resource_list_free(&di.di_res); + ofw_bus_gen_destroy_devinfo(&ofw_di); + bman_portals_detach(dev); + return (ENXIO); +} diff --git a/sys/dev/dpaa/bman_portals.c b/sys/dev/dpaa/bman_portals.c new file mode 100644 index 000000000000..ba9997f5cb74 --- /dev/null +++ b/sys/dev/dpaa/bman_portals.c @@ -0,0 +1,180 @@ +/*- + * Copyright (c) 2012 Semihalf. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "opt_platform.h" +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/bus.h> +#include <sys/lock.h> +#include <sys/module.h> +#include <sys/mutex.h> +#include <sys/proc.h> +#include <sys/pcpu.h> +#include <sys/sched.h> + +#include <machine/bus.h> +#include <machine/tlb.h> + +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> + +#include <powerpc/mpc85xx/mpc85xx.h> + +#include "bman.h" +#include "portals.h" + +t_Handle bman_portal_setup(struct bman_softc *); + +struct dpaa_portals_softc *bp_sc; + +int +bman_portals_attach(device_t dev) +{ + struct dpaa_portals_softc *sc; + + sc = bp_sc = device_get_softc(dev); + + /* Map bman portal to physical address space */ + if (law_enable(OCP85XX_TGTIF_BMAN, sc->sc_dp_pa, sc->sc_dp_size)) { + bman_portals_detach(dev); + return (ENXIO); + } + /* Set portal properties for XX_VirtToPhys() */ + XX_PortalSetInfo(dev); + + return (bus_generic_attach(dev)); +} + +int +bman_portals_detach(device_t dev) +{ + struct dpaa_portals_softc *sc; + int i; + + bp_sc = NULL; + sc = device_get_softc(dev); + + for (i = 0; i < ARRAY_SIZE(sc->sc_dp); i++) { + if (sc->sc_dp[i].dp_ph != NULL) { + thread_lock(curthread); + sched_bind(curthread, i); + thread_unlock(curthread); + + BM_PORTAL_Free(sc->sc_dp[i].dp_ph); + + thread_lock(curthread); + sched_unbind(curthread); + thread_unlock(curthread); + } + + if (sc->sc_dp[i].dp_ires != NULL) { + XX_DeallocIntr((int)sc->sc_dp[i].dp_ires); + bus_release_resource(dev, SYS_RES_IRQ, + sc->sc_dp[i].dp_irid, sc->sc_dp[i].dp_ires); + } + } + for (i = 0; i < ARRAY_SIZE(sc->sc_rres); i++) { + if (sc->sc_rres[i] != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, + sc->sc_rrid[i], + sc->sc_rres[i]); + } + + return (0); +} + +t_Handle +bman_portal_setup(struct bman_softc *bsc) +{ + struct dpaa_portals_softc *sc; + t_BmPortalParam bpp; + t_Handle portal; + unsigned int cpu, p; + + /* Return NULL if we're not ready or while detach */ + if (bp_sc == NULL) + return (NULL); + + sc = bp_sc; + + sched_pin(); + portal = NULL; + cpu = PCPU_GET(cpuid); + + /* Check if portal is ready */ + while (atomic_cmpset_acq_32((uint32_t *)&sc->sc_dp[cpu].dp_ph, + 0, -1) == 0) { + p = atomic_load_acq_32((uint32_t *)&sc->sc_dp[cpu].dp_ph); + + /* Return if portal is already initialized */ + if (p != 0 && p != -1) { + sched_unpin(); + return ((t_Handle)p); + } + + /* Not inititialized and "owned" by another thread */ + thread_lock(curthread); + mi_switch(SW_VOL, NULL); + thread_unlock(curthread); + } + + /* Map portal registers */ + dpaa_portal_map_registers(sc); + + /* Configure and initialize portal */ + bpp.ceBaseAddress = rman_get_bushandle(sc->sc_rres[0]); + bpp.ciBaseAddress = rman_get_bushandle(sc->sc_rres[1]); + bpp.h_Bm = bsc->sc_bh; + bpp.swPortalId = cpu; + bpp.irq = (int)sc->sc_dp[cpu].dp_ires; + + portal = BM_PORTAL_Config(&bpp); + if (portal == NULL) + goto err; + + if (BM_PORTAL_Init(portal) != E_OK) + goto err; + + atomic_store_rel_32((uint32_t *)&sc->sc_dp[cpu].dp_ph, + (uint32_t)portal); + + sched_unpin(); + + return (portal); + +err: + if (portal != NULL) + BM_PORTAL_Free(portal); + + atomic_store_rel_32((uint32_t *)&sc->sc_dp[cpu].dp_ph, 0); + sched_unpin(); + + return (NULL); +} diff --git a/sys/dev/dpaa/dpaa.c b/sys/dev/dpaa/dpaa.c new file mode 100644 index 000000000000..95c04b4d5960 --- /dev/null +++ b/sys/dev/dpaa/dpaa.c @@ -0,0 +1,184 @@ +/*- + * Copyright (c) 2012 Semihalf. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/ktr.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/bus.h> +#include <sys/rman.h> +#include <sys/malloc.h> + +#include <dev/fdt/fdt_common.h> +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> +#include <dev/ofw/openfirm.h> + +#include "opt_platform.h" + +static MALLOC_DEFINE(M_DPAA, "dpaa", "dpaa devices information"); + +static int dpaa_probe(device_t dev); +static int dpaa_attach(device_t dev); + +static const struct ofw_bus_devinfo *dpaa_get_devinfo(device_t bus, + device_t child); + +struct dpaa_softc { + +}; + +struct dpaa_devinfo { + struct ofw_bus_devinfo di_ofw; +}; + +static device_method_t dpaa_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, dpaa_probe), + DEVMETHOD(device_attach, dpaa_attach), + DEVMETHOD(device_detach, bus_generic_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + + /* OFW bus interface */ + DEVMETHOD(ofw_bus_get_devinfo, dpaa_get_devinfo), + DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), + DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), + DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), + DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), + DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), + + {0, 0} +}; + +static driver_t dpaa_driver = { + "dpaa", + dpaa_methods, + sizeof(struct dpaa_softc), +}; + +static devclass_t dpaa_devclass; +DRIVER_MODULE_ORDERED(dpaa, ofwbus, dpaa_driver, dpaa_devclass, 0, 0, + SI_ORDER_ANY); + +static int +dpaa_probe(device_t dev) +{ + + if (!ofw_bus_is_compatible(dev, "fsl,dpaa")) + return (ENXIO); + + device_set_desc(dev, "Freescale Data Path Acceleration Architecture"); + + return (BUS_PROBE_DEFAULT); +} + +static int +dpaa_attach(device_t dev) +{ + device_t dev_child; + phandle_t dt_node, dt_child, enet_node; + struct dpaa_devinfo *di; + pcell_t cell_index; + + cell_index = 0; + /* + * Walk dpaa and add direct subordinates as our children. + */ + dt_node = ofw_bus_get_node(dev); + dt_child = OF_child(dt_node); + + for (; dt_child != 0; dt_child = OF_peer(dt_child)) { + + /* Check and process 'status' property. */ + if (!(fdt_is_enabled(dt_child))) + continue; + + di = (struct dpaa_devinfo *)malloc(sizeof(*di), M_DPAA, + M_WAITOK | M_ZERO); + + if (ofw_bus_gen_setup_devinfo(&di->di_ofw, dt_child) != 0) { + free(di, M_DPAA); + device_printf(dev, "could not set up devinfo\n"); + continue; + } + + /* + * dTSEC number from SoC is equal to number get from + * dts file. + */ + if (OF_getprop(dt_child, "fsl,fman-mac", + (void *)&enet_node, sizeof(enet_node)) == -1) { + device_printf(dev, "Could not get fsl,fman-mac " + "from dts\n"); + continue; + } + + if ((enet_node = OF_instance_to_package(enet_node)) == -1) { + device_printf(dev, "Could not get enet node\n"); + continue; + } + + if (OF_getprop(enet_node, "cell-index", + (void *)&cell_index, sizeof(cell_index)) == -1) { + device_printf(dev, "Could not get cell-index from enet " + "node\n"); + continue; + } + + /* Add newbus device for this FDT node */ + dev_child = device_add_child(dev, "dtsec", (int)cell_index); + if (dev_child == NULL) { + device_printf(dev, "could not add child: %s\n", + di->di_ofw.obd_name); + ofw_bus_gen_destroy_devinfo(&di->di_ofw); + free(di, M_DPAA); + continue; + } + +#ifdef DEBUG + device_printf(dev, "added child: %s\n\n", di->di_ofw.obd_name); +#endif + + device_set_ivars(dev_child, di); + } + + return (bus_generic_attach(dev)); +} + +static const struct ofw_bus_devinfo * +dpaa_get_devinfo(device_t bus, device_t child) +{ + struct dpaa_devinfo *di; + + di = device_get_ivars(child); + return (&di->di_ofw); +} diff --git a/sys/dev/dpaa/fman.c b/sys/dev/dpaa/fman.c new file mode 100644 index 000000000000..3e44db89580c --- /dev/null +++ b/sys/dev/dpaa/fman.c @@ -0,0 +1,357 @@ +/*- + * Copyright (c) 2011-2012 Semihalf. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/bus.h> +#include <sys/rman.h> +#include <sys/malloc.h> + +#include <dev/fdt/fdt_common.h> +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> + +#include "opt_platform.h" + +#include <contrib/ncsw/inc/Peripherals/fm_ext.h> +#include <contrib/ncsw/inc/Peripherals/fm_muram_ext.h> +#include <contrib/ncsw/inc/ncsw_ext.h> +#include <contrib/ncsw/integrations/fman_ucode.h> + +#include "fman.h" + + +/** + * @group FMan private defines. + * @{ + */ +enum fman_irq_enum { + FMAN_IRQ_NUM = 0, + FMAN_ERR_IRQ_NUM = 1 +}; + +enum fman_mu_ram_map { + FMAN_MURAM_OFF = 0x0, + FMAN_MURAM_SIZE = 0x28000 +}; + +struct fman_config { + device_t fman_device; + uintptr_t mem_base_addr; + int irq_num; + int err_irq_num; + uint8_t fm_id; + t_FmExceptionsCallback *exception_callback; + t_FmBusErrorCallback *bus_error_callback; +}; + +/** + * @group FMan private methods/members. + * @{ + */ +/** + * Frame Manager firmware. + * We use the same firmware for both P3041 and P2041 devices. + */ +const uint32_t fman_firmware[] = FMAN_UC_IMG; +const uint32_t fman_firmware_size = sizeof(fman_firmware); +static struct fman_softc *fm_sc = NULL; + +static t_Handle +fman_init(struct fman_softc *sc, struct fman_config *cfg) +{ + t_FmParams fm_params; + t_Handle muram_handle, fm_handle; + t_Error error; + t_FmRevisionInfo revision_info; + uint16_t clock; + uint32_t tmp, mod; + + /* MURAM configuration */ + muram_handle = FM_MURAM_ConfigAndInit(cfg->mem_base_addr + + FMAN_MURAM_OFF, FMAN_MURAM_SIZE); + if (muram_handle == NULL) { + device_printf(cfg->fman_device, "couldn't init FM MURAM module" + "\n"); + return (NULL); + } + sc->muram_handle = muram_handle; + + /* Fill in FM configuration */ + fm_params.fmId = cfg->fm_id; + /* XXX we support only one partition thus each fman has master id */ + fm_params.guestId = NCSW_MASTER_ID; + + fm_params.baseAddr = cfg->mem_base_addr; + fm_params.h_FmMuram = muram_handle; + + /* Get FMan clock in Hz */ + if ((tmp = fman_get_clock(sc)) == 0) + return (NULL); + + /* Convert FMan clock to MHz */ + clock = (uint16_t)(tmp / 1000000); + mod = tmp % 1000000; + + if (mod >= 500000) + ++clock; + + fm_params.fmClkFreq = clock; + fm_params.f_Exception = cfg->exception_callback; + fm_params.f_BusError = cfg->bus_error_callback; + fm_params.h_App = cfg->fman_device; + fm_params.irq = cfg->irq_num; + fm_params.errIrq = cfg->err_irq_num; + + fm_params.firmware.size = fman_firmware_size; + fm_params.firmware.p_Code = (uint32_t*)fman_firmware; + + fm_handle = FM_Config(&fm_params); + if (fm_handle == NULL) { + device_printf(cfg->fman_device, "couldn't configure FM " + "module\n"); + goto err; + } + + FM_ConfigResetOnInit(fm_handle, TRUE); + + error = FM_Init(fm_handle); + if (error != E_OK) { + device_printf(cfg->fman_device, "couldn't init FM module\n"); + goto err2; + } + + error = FM_GetRevision(fm_handle, &revision_info); + if (error != E_OK) { + device_printf(cfg->fman_device, "couldn't get FM revision\n"); + goto err2; + } + + device_printf(cfg->fman_device, "Hardware version: %d.%d.\n", + revision_info.majorRev, revision_info.minorRev); + + return (fm_handle); + +err2: + FM_Free(fm_handle); +err: + FM_MURAM_Free(muram_handle); + return (NULL); +} + +static void +fman_exception_callback(t_Handle app_handle, e_FmExceptions exception) +{ + struct fman_softc *sc; + + sc = app_handle; + device_printf(sc->dev, "FMan exception occurred.\n"); +} + +static void +fman_error_callback(t_Handle app_handle, e_FmPortType port_type, + uint8_t port_id, uint64_t addr, uint8_t tnum, uint16_t liodn) +{ + struct fman_softc *sc; + + sc = app_handle; + device_printf(sc->dev, "FMan error occurred.\n"); +} +/** @} */ + + +/** + * @group FMan driver interface. + * @{ + */ + +int +fman_get_handle(t_Handle *fmh) +{ + + if (fm_sc == NULL) + return (ENOMEM); + + *fmh = fm_sc->fm_handle; + + return (0); +} + +int +fman_get_muram_handle(t_Handle *muramh) +{ + + if (fm_sc == NULL) + return (ENOMEM); + + *muramh = fm_sc->muram_handle; + + return (0); +} + +int +fman_get_bushandle(vm_offset_t *fm_base) +{ + + if (fm_sc == NULL) + return (ENOMEM); + + *fm_base = rman_get_bushandle(fm_sc->mem_res); + + return (0); +} + +int +fman_attach(device_t dev) +{ + struct fman_softc *sc; + struct fman_config cfg; + + sc = device_get_softc(dev); + sc->dev = dev; + fm_sc = sc; + + /* Check if MallocSmart allocator is ready */ + if (XX_MallocSmartInit() != E_OK) { + device_printf(dev, "could not initialize smart allocator.\n"); + return (ENXIO); + } + + XX_TrackInit(); + + sc->mem_rid = 0; + sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid, + RF_ACTIVE); + if (!sc->mem_res) { + device_printf(dev, "could not allocate memory.\n"); + return (ENXIO); + } + + sc->irq_rid = 0; + sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irq_rid, + RF_ACTIVE); + if (!sc->irq_res) { + device_printf(dev, "could not allocate interrupt.\n"); + goto err; + } + + /* + * XXX: Fix FMan interrupt. This is workaround for the issue with + * interrupts directed to multiple CPUs by the interrupts subsystem. + * Workaround is to bind the interrupt to only one CPU0. + */ + XX_FmanFixIntr(rman_get_start(sc->irq_res)); + + sc->err_irq_rid = 1; + sc->err_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, + &sc->err_irq_rid, RF_ACTIVE | RF_SHAREABLE); + if (!sc->err_irq_res) { + device_printf(dev, "could not allocate error interrupt.\n"); + goto err; + } + + /* Set FMan configuration */ + cfg.fman_device = dev; + cfg.fm_id = device_get_unit(dev); + cfg.mem_base_addr = rman_get_bushandle(sc->mem_res); + cfg.irq_num = (int)sc->irq_res; + cfg.err_irq_num = (int)sc->err_irq_res; + cfg.exception_callback = fman_exception_callback; + cfg.bus_error_callback = fman_error_callback; + + sc->fm_handle = fman_init(sc, &cfg); + if (sc->fm_handle == NULL) { + device_printf(dev, "could not be configured\n"); + return (ENXIO); + } + + return (bus_generic_attach(dev)); + +err: + fman_detach(dev); + return (ENXIO); +} + +int +fman_detach(device_t dev) +{ + struct fman_softc *sc; + + sc = device_get_softc(dev); + + if (sc->muram_handle) { + FM_MURAM_Free(sc->muram_handle); + } + + if (sc->fm_handle) { + FM_Free(sc->fm_handle); + } + + if (sc->mem_res) { + bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, + sc->mem_res); + } + + if (sc->irq_res) { + bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, + sc->irq_res); + } + + if (sc->irq_res) { + bus_release_resource(dev, SYS_RES_IRQ, sc->err_irq_rid, + sc->err_irq_res); + } + + return (0); +} + +int +fman_suspend(device_t dev) +{ + + return (0); +} + +int +fman_resume(device_t dev) +{ + + return (0); +} + +int +fman_shutdown(device_t dev) +{ + + return (0); +} + +/** @} */ diff --git a/sys/dev/dpaa/fman.h b/sys/dev/dpaa/fman.h new file mode 100644 index 000000000000..5a421e056423 --- /dev/null +++ b/sys/dev/dpaa/fman.h @@ -0,0 +1,67 @@ +/*- + * Copyright (c) 2011-2012 Semihalf. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef FMAN_H_ +#define FMAN_H_ + +/** + * FMan driver instance data. + */ +struct fman_softc { + device_t dev; + struct resource *mem_res; + struct resource *irq_res; + struct resource *err_irq_res; + int mem_rid; + int irq_rid; + int err_irq_rid; + + t_Handle fm_handle; + t_Handle muram_handle; +}; + + +/** + * @group QMan bus interface. + * @{ + */ +int fman_attach(device_t dev); +int fman_detach(device_t dev); +int fman_suspend(device_t dev); +int fman_resume(device_t dev); +int fman_shutdown(device_t dev); +int fman_read_ivar(device_t dev, device_t child, int index, + uintptr_t *result); +/** @} */ + +uint32_t fman_get_clock(struct fman_softc *sc); +int fman_get_handle(t_Handle *fmh); +int fman_get_muram_handle(t_Handle *muramh); +int fman_get_bushandle(vm_offset_t *fm_base); + +#endif /* FMAN_H_ */ diff --git a/sys/dev/dpaa/fman_fdt.c b/sys/dev/dpaa/fman_fdt.c new file mode 100644 index 000000000000..89c9eca1cb5b --- /dev/null +++ b/sys/dev/dpaa/fman_fdt.c @@ -0,0 +1,104 @@ +/*- + * Copyright (c) 2012 Semihalf. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/bus.h> +#include <sys/module.h> + +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> + +#include <contrib/ncsw/inc/ncsw_ext.h> +#include <contrib/ncsw/inc/enet_ext.h> + +#include "fman.h" + +#define FFMAN_DEVSTR "Freescale Frame Manager" + +static int fman_fdt_probe(device_t dev); + +static device_method_t fman_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, fman_fdt_probe), + DEVMETHOD(device_attach, fman_attach), + DEVMETHOD(device_detach, fman_detach), + + DEVMETHOD(device_shutdown, fman_shutdown), + DEVMETHOD(device_suspend, fman_suspend), + DEVMETHOD(device_resume, fman_resume), + + { 0, 0 } +}; + +static driver_t fman_driver = { + "fman", + fman_methods, + sizeof(struct fman_softc), +}; + +static devclass_t fman_devclass; +EARLY_DRIVER_MODULE(fman, simplebus, fman_driver, fman_devclass, 0, 0, + BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); + + +static int +fman_fdt_probe(device_t dev) +{ + + if (!ofw_bus_is_compatible(dev, "fsl,fman")) + return (ENXIO); + + device_set_desc(dev, FFMAN_DEVSTR); + + return (BUS_PROBE_DEFAULT); +} + +uint32_t +fman_get_clock(struct fman_softc *sc) +{ + device_t dev; + phandle_t node; + pcell_t fman_clock; + + dev = sc->dev; + node = ofw_bus_get_node(dev); + + if ((OF_getprop(node, "clock-frequency", &fman_clock, + sizeof(fman_clock)) <= 0) || (fman_clock == 0)) { + device_printf(dev, "could not acquire correct frequency " + "from DTS\n"); + + return (0); + } + + return ((uint32_t)fman_clock); +} + diff --git a/sys/dev/dpaa/if_dtsec.c b/sys/dev/dpaa/if_dtsec.c new file mode 100644 index 000000000000..249d605bb42e --- /dev/null +++ b/sys/dev/dpaa/if_dtsec.c @@ -0,0 +1,879 @@ +/*- + * Copyright (c) 2011-2012 Semihalf. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/bus.h> +#include <sys/rman.h> +#include <sys/malloc.h> +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <sys/sysctl.h> +#include <sys/sockio.h> + +#include <machine/bus.h> +#include <machine/resource.h> + +#include <net/ethernet.h> +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_types.h> +#include <net/if_arp.h> + +#include <dev/fdt/fdt_common.h> +#include <dev/mii/mii.h> +#include <dev/mii/miivar.h> +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> +#include <dev/ofw/openfirm.h> + +#include "miibus_if.h" + +#include <contrib/ncsw/inc/Peripherals/fm_mac_ext.h> +#include <contrib/ncsw/inc/Peripherals/fm_port_ext.h> +#include <contrib/ncsw/inc/xx_ext.h> + +#include "fman.h" +#include "if_dtsec.h" +#include "if_dtsec_im.h" +#include "if_dtsec_rm.h" + + +/** + * @group dTSEC private defines. + * @{ + */ +/** + * dTSEC FMan MAC exceptions info struct. + */ +struct dtsec_fm_mac_ex_str { + const int num; + const char *str; +}; + +/* XXX: Handle to FM_MAC instance of dTSEC0 */ +/* From QorIQ Data Path Acceleration Architecture Reference Manual, Rev 2, page + * 3-37, "The MII management hardware is shared by all dTSECs... only through + * the MIIM registers of dTSEC1 can external PHY's be accessed and configured." + */ +static t_Handle dtsec_mdio_mac_handle; +/** @} */ + + +/** + * @group FMan MAC routines. + * @{ + */ +#define DTSEC_MAC_EXCEPTIONS_END (-1) + +/** + * FMan MAC exceptions. + */ +static const struct dtsec_fm_mac_ex_str dtsec_fm_mac_exceptions[] = { + { e_FM_MAC_EX_10G_MDIO_SCAN_EVENTMDIO, "MDIO scan event" }, + { e_FM_MAC_EX_10G_MDIO_CMD_CMPL, "MDIO command completion" }, + { e_FM_MAC_EX_10G_REM_FAULT, "Remote fault" }, + { e_FM_MAC_EX_10G_LOC_FAULT, "Local fault" }, + { e_FM_MAC_EX_10G_1TX_ECC_ER, "Transmit frame ECC error" }, + { e_FM_MAC_EX_10G_TX_FIFO_UNFL, "Transmit FIFO underflow" }, + { e_FM_MAC_EX_10G_TX_FIFO_OVFL, "Receive FIFO overflow" }, + { e_FM_MAC_EX_10G_TX_ER, "Transmit frame error" }, + { e_FM_MAC_EX_10G_RX_FIFO_OVFL, "Receive FIFO overflow" }, + { e_FM_MAC_EX_10G_RX_ECC_ER, "Receive frame ECC error" }, + { e_FM_MAC_EX_10G_RX_JAB_FRM, "Receive jabber frame" }, + { e_FM_MAC_EX_10G_RX_OVRSZ_FRM, "Receive oversized frame" }, + { e_FM_MAC_EX_10G_RX_RUNT_FRM, "Receive runt frame" }, + { e_FM_MAC_EX_10G_RX_FRAG_FRM, "Receive fragment frame" }, + { e_FM_MAC_EX_10G_RX_LEN_ER, "Receive payload length error" }, + { e_FM_MAC_EX_10G_RX_CRC_ER, "Receive CRC error" }, + { e_FM_MAC_EX_10G_RX_ALIGN_ER, "Receive alignment error" }, + { e_FM_MAC_EX_1G_BAB_RX, "Babbling receive error" }, + { e_FM_MAC_EX_1G_RX_CTL, "Receive control (pause frame) interrupt" }, + { e_FM_MAC_EX_1G_GRATEFUL_TX_STP_COMPLET, "Graceful transmit stop " + "complete" }, + { e_FM_MAC_EX_1G_BAB_TX, "Babbling transmit error" }, + { e_FM_MAC_EX_1G_TX_CTL, "Transmit control (pause frame) interrupt" }, + { e_FM_MAC_EX_1G_TX_ERR, "Transmit error" }, + { e_FM_MAC_EX_1G_LATE_COL, "Late collision" }, + { e_FM_MAC_EX_1G_COL_RET_LMT, "Collision retry limit" }, + { e_FM_MAC_EX_1G_TX_FIFO_UNDRN, "Transmit FIFO underrun" }, + { e_FM_MAC_EX_1G_MAG_PCKT, "Magic Packet detected when dTSEC is in " + "Magic Packet detection mode" }, + { e_FM_MAC_EX_1G_MII_MNG_RD_COMPLET, "MII management read completion" }, + { e_FM_MAC_EX_1G_MII_MNG_WR_COMPLET, "MII management write completion" }, + { e_FM_MAC_EX_1G_GRATEFUL_RX_STP_COMPLET, "Graceful receive stop " + "complete" }, + { e_FM_MAC_EX_1G_TX_DATA_ERR, "Internal data error on transmit" }, + { e_FM_MAC_EX_1G_RX_DATA_ERR, "Internal data error on receive" }, + { e_FM_MAC_EX_1G_1588_TS_RX_ERR, "Time-Stamp Receive Error" }, + { e_FM_MAC_EX_1G_RX_MIB_CNT_OVFL, "MIB counter overflow" }, + { DTSEC_MAC_EXCEPTIONS_END, "" } +}; + +static const char * +dtsec_fm_mac_ex_to_str(e_FmMacExceptions exception) +{ + int i; + + for (i = 0; dtsec_fm_mac_exceptions[i].num != exception && + dtsec_fm_mac_exceptions[i].num != DTSEC_MAC_EXCEPTIONS_END; ++i) + ; + + if (dtsec_fm_mac_exceptions[i].num == DTSEC_MAC_EXCEPTIONS_END) + return ("<Unknown Exception>"); + + return (dtsec_fm_mac_exceptions[i].str); +} + +static void +dtsec_fm_mac_mdio_event_callback(t_Handle h_App, + e_FmMacExceptions exception) +{ + struct dtsec_softc *sc; + + sc = h_App; + device_printf(sc->sc_dev, "MDIO event %i: %s.\n", exception, + dtsec_fm_mac_ex_to_str(exception)); +} + +static void +dtsec_fm_mac_exception_callback(t_Handle app, e_FmMacExceptions exception) +{ + struct dtsec_softc *sc; + + sc = app; + device_printf(sc->sc_dev, "MAC exception %i: %s.\n", exception, + dtsec_fm_mac_ex_to_str(exception)); +} + +static void +dtsec_fm_mac_free(struct dtsec_softc *sc) +{ + if (sc->sc_mach == NULL) + return; + + FM_MAC_Disable(sc->sc_mach, e_COMM_MODE_RX_AND_TX); + FM_MAC_Free(sc->sc_mach); + sc->sc_mach = NULL; +} + +static int +dtsec_fm_mac_init(struct dtsec_softc *sc, uint8_t *mac) +{ + t_FmMacParams params; + t_Error error; + + memset(¶ms, 0, sizeof(params)); + memcpy(¶ms.addr, mac, sizeof(params.addr)); + + params.baseAddr = sc->sc_fm_base + sc->sc_mac_mem_offset; + params.enetMode = sc->sc_mac_enet_mode; + params.macId = sc->sc_eth_id; + params.mdioIrq = sc->sc_mac_mdio_irq; + params.f_Event = dtsec_fm_mac_mdio_event_callback; + params.f_Exception = dtsec_fm_mac_exception_callback; + params.h_App = sc; + params.h_Fm = sc->sc_fmh; + + sc->sc_mach = FM_MAC_Config(¶ms); + if (sc->sc_hidden) + return (0); + if (sc->sc_mach == NULL) { + device_printf(sc->sc_dev, "couldn't configure FM_MAC module.\n" + ); + return (ENXIO); + } + + error = FM_MAC_ConfigResetOnInit(sc->sc_mach, TRUE); + if (error != E_OK) { + device_printf(sc->sc_dev, "couldn't enable reset on init " + "feature.\n"); + dtsec_fm_mac_free(sc); + return (ENXIO); + } + + /* Do not inform about pause frames */ + error = FM_MAC_ConfigException(sc->sc_mach, e_FM_MAC_EX_1G_RX_CTL, + FALSE); + if (error != E_OK) { + device_printf(sc->sc_dev, "couldn't disable pause frames " + "exception.\n"); + dtsec_fm_mac_free(sc); + return (ENXIO); + } + + error = FM_MAC_Init(sc->sc_mach); + if (error != E_OK) { + device_printf(sc->sc_dev, "couldn't initialize FM_MAC module." + "\n"); + dtsec_fm_mac_free(sc); + return (ENXIO); + } + + return (0); +} +/** @} */ + + +/** + * @group FMan PORT routines. + * @{ + */ +static const char * +dtsec_fm_port_ex_to_str(e_FmPortExceptions exception) +{ + + switch (exception) { + case e_FM_PORT_EXCEPTION_IM_BUSY: + return ("IM: RX busy"); + default: + return ("<Unknown Exception>"); + } +} + +void +dtsec_fm_port_rx_exception_callback(t_Handle app, + e_FmPortExceptions exception) +{ + struct dtsec_softc *sc; + + sc = app; + device_printf(sc->sc_dev, "RX exception: %i: %s.\n", exception, + dtsec_fm_port_ex_to_str(exception)); +} + +void +dtsec_fm_port_tx_exception_callback(t_Handle app, + e_FmPortExceptions exception) +{ + struct dtsec_softc *sc; + + sc = app; + device_printf(sc->sc_dev, "TX exception: %i: %s.\n", exception, + dtsec_fm_port_ex_to_str(exception)); +} + +e_FmPortType +dtsec_fm_port_rx_type(enum eth_dev_type type) +{ + switch (type) { + case ETH_DTSEC: + return (e_FM_PORT_TYPE_RX); + case ETH_10GSEC: + return (e_FM_PORT_TYPE_RX_10G); + default: + return (e_FM_PORT_TYPE_DUMMY); + } +} + +e_FmPortType +dtsec_fm_port_tx_type(enum eth_dev_type type) +{ + + switch (type) { + case ETH_DTSEC: + return (e_FM_PORT_TYPE_TX); + case ETH_10GSEC: + return (e_FM_PORT_TYPE_TX_10G); + default: + return (e_FM_PORT_TYPE_DUMMY); + } +} + +static void +dtsec_fm_port_free_both(struct dtsec_softc *sc) +{ + if (sc->sc_rxph) { + FM_PORT_Free(sc->sc_rxph); + sc->sc_rxph = NULL; + } + + if (sc->sc_txph) { + FM_PORT_Free(sc->sc_txph); + sc->sc_txph = NULL; + } +} +/** @} */ + + +/** + * @group IFnet routines. + * @{ + */ +static int +dtsec_if_enable_locked(struct dtsec_softc *sc) +{ + int error; + + DTSEC_LOCK_ASSERT(sc); + + error = FM_MAC_Enable(sc->sc_mach, e_COMM_MODE_RX_AND_TX); + if (error != E_OK) + return (EIO); + + error = FM_PORT_Enable(sc->sc_rxph); + if (error != E_OK) + return (EIO); + + error = FM_PORT_Enable(sc->sc_txph); + if (error != E_OK) + return (EIO); + + sc->sc_ifnet->if_drv_flags |= IFF_DRV_RUNNING; + + /* Refresh link state */ + dtsec_miibus_statchg(sc->sc_dev); + + return (0); +} + +static int +dtsec_if_disable_locked(struct dtsec_softc *sc) +{ + int error; + + DTSEC_LOCK_ASSERT(sc); + + error = FM_MAC_Disable(sc->sc_mach, e_COMM_MODE_RX_AND_TX); + if (error != E_OK) + return (EIO); + + error = FM_PORT_Disable(sc->sc_rxph); + if (error != E_OK) + return (EIO); + + error = FM_PORT_Disable(sc->sc_txph); + if (error != E_OK) + return (EIO); + + sc->sc_ifnet->if_drv_flags &= ~IFF_DRV_RUNNING; + + return (0); +} + +static int +dtsec_if_ioctl(struct ifnet *ifp, u_long command, caddr_t data) +{ + struct dtsec_softc *sc; + struct ifreq *ifr; + int error; + + sc = ifp->if_softc; + ifr = (struct ifreq *)data; + error = 0; + + /* Basic functionality to achieve media status reports */ + switch (command) { + case SIOCSIFFLAGS: + DTSEC_LOCK(sc); + + if (sc->sc_ifnet->if_flags & IFF_UP) + error = dtsec_if_enable_locked(sc); + else + error = dtsec_if_disable_locked(sc); + + DTSEC_UNLOCK(sc); + break; + + case SIOCGIFMEDIA: + case SIOCSIFMEDIA: + error = ifmedia_ioctl(ifp, ifr, &sc->sc_mii->mii_media, + command); + break; + + default: + error = ether_ioctl(ifp, command, data); + } + + return (error); +} + +static void +dtsec_if_tick(void *arg) +{ + struct dtsec_softc *sc; + + sc = arg; + + /* TODO */ + DTSEC_LOCK(sc); + + mii_tick(sc->sc_mii); + callout_reset(&sc->sc_tick_callout, hz, dtsec_if_tick, sc); + + DTSEC_UNLOCK(sc); +} + +static void +dtsec_if_deinit_locked(struct dtsec_softc *sc) +{ + + DTSEC_LOCK_ASSERT(sc); + + DTSEC_UNLOCK(sc); + callout_drain(&sc->sc_tick_callout); + DTSEC_LOCK(sc); +} + +static void +dtsec_if_init_locked(struct dtsec_softc *sc) +{ + int error; + + DTSEC_LOCK_ASSERT(sc); + + /* Set MAC address */ + error = FM_MAC_ModifyMacAddr(sc->sc_mach, + (t_EnetAddr *)IF_LLADDR(sc->sc_ifnet)); + if (error != E_OK) { + device_printf(sc->sc_dev, "couldn't set MAC address.\n"); + goto err; + } + + /* Start MII polling */ + if (sc->sc_mii) + callout_reset(&sc->sc_tick_callout, hz, dtsec_if_tick, sc); + + if (sc->sc_ifnet->if_flags & IFF_UP) { + error = dtsec_if_enable_locked(sc); + if (error != 0) + goto err; + } else { + error = dtsec_if_disable_locked(sc); + if (error != 0) + goto err; + } + + return; + +err: + dtsec_if_deinit_locked(sc); + device_printf(sc->sc_dev, "initialization error.\n"); + return; +} + +static void +dtsec_if_init(void *data) +{ + struct dtsec_softc *sc; + + sc = data; + + DTSEC_LOCK(sc); + dtsec_if_init_locked(sc); + DTSEC_UNLOCK(sc); +} + +static void +dtsec_if_start(struct ifnet *ifp) +{ + struct dtsec_softc *sc; + + sc = ifp->if_softc; + DTSEC_LOCK(sc); + sc->sc_start_locked(sc); + DTSEC_UNLOCK(sc); +} + +static void +dtsec_if_watchdog(struct ifnet *ifp) +{ + /* TODO */ +} +/** @} */ + + +/** + * @group IFmedia routines. + * @{ + */ +static int +dtsec_ifmedia_upd(struct ifnet *ifp) +{ + struct dtsec_softc *sc = ifp->if_softc; + + DTSEC_LOCK(sc); + mii_mediachg(sc->sc_mii); + DTSEC_UNLOCK(sc); + + return (0); +} + +static void +dtsec_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) +{ + struct dtsec_softc *sc = ifp->if_softc; + + DTSEC_LOCK(sc); + + mii_pollstat(sc->sc_mii); + + ifmr->ifm_active = sc->sc_mii->mii_media_active; + ifmr->ifm_status = sc->sc_mii->mii_media_status; + + DTSEC_UNLOCK(sc); +} +/** @} */ + + +/** + * @group dTSEC bus interface. + * @{ + */ +static void +dtsec_configure_mode(struct dtsec_softc *sc) +{ + char tunable[64]; + + snprintf(tunable, sizeof(tunable), "%s.independent_mode", + device_get_nameunit(sc->sc_dev)); + + sc->sc_mode = DTSEC_MODE_REGULAR; + TUNABLE_INT_FETCH(tunable, &sc->sc_mode); + + if (sc->sc_mode == DTSEC_MODE_REGULAR) { + sc->sc_port_rx_init = dtsec_rm_fm_port_rx_init; + sc->sc_port_tx_init = dtsec_rm_fm_port_tx_init; + sc->sc_start_locked = dtsec_rm_if_start_locked; + } else { + sc->sc_port_rx_init = dtsec_im_fm_port_rx_init; + sc->sc_port_tx_init = dtsec_im_fm_port_tx_init; + sc->sc_start_locked = dtsec_im_if_start_locked; + } + + device_printf(sc->sc_dev, "Configured for %s mode.\n", + (sc->sc_mode == DTSEC_MODE_REGULAR) ? "regular" : "independent"); +} + +int +dtsec_attach(device_t dev) +{ + struct dtsec_softc *sc; + int error; + struct ifnet *ifp; + + sc = device_get_softc(dev); + + sc->sc_dev = dev; + sc->sc_mac_mdio_irq = NO_IRQ; + sc->sc_eth_id = device_get_unit(dev); + + + /* Check if MallocSmart allocator is ready */ + if (XX_MallocSmartInit() != E_OK) + return (ENXIO); + + XX_TrackInit(); + + /* Init locks */ + mtx_init(&sc->sc_lock, device_get_nameunit(dev), + "DTSEC Global Lock", MTX_DEF); + + mtx_init(&sc->sc_mii_lock, device_get_nameunit(dev), + "DTSEC MII Lock", MTX_DEF); + + /* Init callouts */ + callout_init(&sc->sc_tick_callout, CALLOUT_MPSAFE); + + /* Read configuraton */ + if ((error = fman_get_handle(&sc->sc_fmh)) != 0) + return (error); + + if ((error = fman_get_muram_handle(&sc->sc_muramh)) != 0) + return (error); + + if ((error = fman_get_bushandle(&sc->sc_fm_base)) != 0) + return (error); + + /* Configure working mode */ + dtsec_configure_mode(sc); + + /* If we are working in regular mode configure BMAN and QMAN */ + if (sc->sc_mode == DTSEC_MODE_REGULAR) { + /* Create RX buffer pool */ + error = dtsec_rm_pool_rx_init(sc); + if (error != 0) + return (EIO); + + /* Create RX frame queue range */ + error = dtsec_rm_fqr_rx_init(sc); + if (error != 0) + return (EIO); + + /* Create frame info pool */ + error = dtsec_rm_fi_pool_init(sc); + if (error != 0) + return (EIO); + + /* Create TX frame queue range */ + error = dtsec_rm_fqr_tx_init(sc); + if (error != 0) + return (EIO); + } + + /* Init FMan MAC module. */ + error = dtsec_fm_mac_init(sc, sc->sc_mac_addr); + if (error != 0) { + dtsec_detach(dev); + return (ENXIO); + } + + /* + * XXX: All phys are connected to MDIO interface of the first dTSEC + * device (dTSEC0). We have to save handle to the FM_MAC instance of + * dTSEC0, which is used later during phy's registers accesses. Another + * option would be adding new property to DTS pointing to correct dTSEC + * instance, of which FM_MAC handle has to be used for phy's registers + * accesses. We did not want to add new properties to DTS, thus this + * quite ugly hack. + */ + if (sc->sc_eth_id == 0) + dtsec_mdio_mac_handle = sc->sc_mach; + if (sc->sc_hidden) + return (0); + + /* Init FMan TX port */ + error = sc->sc_port_tx_init(sc, device_get_unit(sc->sc_dev)); + if (error != 0) { + dtsec_detach(dev); + return (ENXIO); + } + + /* Init FMan RX port */ + error = sc->sc_port_rx_init(sc, device_get_unit(sc->sc_dev)); + if (error != 0) { + dtsec_detach(dev); + return (ENXIO); + } + + /* Create network interface for upper layers */ + ifp = sc->sc_ifnet = if_alloc(IFT_ETHER); + if (ifp == NULL) { + device_printf(sc->sc_dev, "if_alloc() failed.\n"); + dtsec_detach(dev); + return (ENOMEM); + } + + ifp->if_softc = sc; + ifp->if_mtu = ETHERMTU; /* TODO: Configure */ + ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST; + ifp->if_init = dtsec_if_init; + ifp->if_start = dtsec_if_start; + ifp->if_ioctl = dtsec_if_ioctl; + ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; + + if (sc->sc_phy_addr >= 0) + if_initname(ifp, device_get_name(sc->sc_dev), + device_get_unit(sc->sc_dev)); + else + if_initname(ifp, "dtsec_phy", device_get_unit(sc->sc_dev)); + + /* TODO */ +#if 0 + IFQ_SET_MAXLEN(&ifp->if_snd, TSEC_TX_NUM_DESC - 1); + ifp->if_snd.ifq_drv_maxlen = TSEC_TX_NUM_DESC - 1; + IFQ_SET_READY(&ifp->if_snd); +#endif + ifp->if_capabilities = 0; /* TODO: Check */ + ifp->if_capenable = ifp->if_capabilities; + + /* Attach PHY(s) */ + error = mii_attach(sc->sc_dev, &sc->sc_mii_dev, ifp, dtsec_ifmedia_upd, + dtsec_ifmedia_sts, BMSR_DEFCAPMASK, sc->sc_phy_addr, + MII_OFFSET_ANY, 0); + if (error) { + device_printf(sc->sc_dev, "attaching PHYs failed: %d\n", error); + dtsec_detach(sc->sc_dev); + return (error); + } + sc->sc_mii = device_get_softc(sc->sc_mii_dev); + + /* Attach to stack */ + ether_ifattach(ifp, sc->sc_mac_addr); + + return (0); +} + +int +dtsec_detach(device_t dev) +{ + struct dtsec_softc *sc; + if_t ifp; + + sc = device_get_softc(dev); + ifp = sc->sc_ifnet; + + if (device_is_attached(dev)) { + ether_ifdetach(ifp); + /* Shutdown interface */ + DTSEC_LOCK(sc); + dtsec_if_deinit_locked(sc); + DTSEC_UNLOCK(sc); + } + + if (sc->sc_ifnet) { + if_free(sc->sc_ifnet); + sc->sc_ifnet = NULL; + } + + if (sc->sc_mode == DTSEC_MODE_REGULAR) { + /* Free RX/TX FQRs */ + dtsec_rm_fqr_rx_free(sc); + dtsec_rm_fqr_tx_free(sc); + + /* Free frame info pool */ + dtsec_rm_fi_pool_free(sc); + + /* Free RX buffer pool */ + dtsec_rm_pool_rx_free(sc); + } + + dtsec_fm_mac_free(sc); + dtsec_fm_port_free_both(sc); + + /* Destroy lock */ + mtx_destroy(&sc->sc_lock); + + return (0); +} + +int +dtsec_suspend(device_t dev) +{ + + return (0); +} + +int +dtsec_resume(device_t dev) +{ + + return (0); +} + +int +dtsec_shutdown(device_t dev) +{ + + return (0); +} +/** @} */ + + +/** + * @group MII bus interface. + * @{ + */ +int +dtsec_miibus_readreg(device_t dev, int phy, int reg) +{ + struct dtsec_softc *sc; + uint16_t data; + t_Error error; + + sc = device_get_softc(dev); + + if (phy != sc->sc_phy_addr) + return (0xFFFF); + + DTSEC_MII_LOCK(sc); + error = FM_MAC_MII_ReadPhyReg(dtsec_mdio_mac_handle, phy, reg, &data); + DTSEC_MII_UNLOCK(sc); + if (error != E_OK) { + device_printf(dev, "Error while reading from PHY (NetCommSw " + "error: %d)\n", error); + return (0xFFFF); + } + + return ((int)data); +} + +int +dtsec_miibus_writereg(device_t dev, int phy, int reg, int value) +{ + struct dtsec_softc *sc; + t_Error error; + + sc = device_get_softc(dev); + + if (phy != sc->sc_phy_addr) + return (EINVAL); + + DTSEC_MII_LOCK(sc); + error = FM_MAC_MII_WritePhyReg(dtsec_mdio_mac_handle, phy, reg, value); + DTSEC_MII_UNLOCK(sc); + if (error != E_OK) { + device_printf(dev, "Error while writing to PHY (NetCommSw " + "error: %d).\n", error); + return (EIO); + } + + return (0); +} + +void +dtsec_miibus_statchg(device_t dev) +{ + struct dtsec_softc *sc; + e_EnetSpeed speed; + bool duplex; + int error; + + sc = device_get_softc(dev); + + DTSEC_LOCK_ASSERT(sc); + + duplex = ((sc->sc_mii->mii_media_active & IFM_GMASK) == IFM_FDX); + + switch (IFM_SUBTYPE(sc->sc_mii->mii_media_active)) { + case IFM_1000_T: + case IFM_1000_SX: + speed = e_ENET_SPEED_1000; + break; + + case IFM_100_TX: + speed = e_ENET_SPEED_100; + break; + + case IFM_10_T: + speed = e_ENET_SPEED_10; + break; + + default: + speed = e_ENET_SPEED_10; + } + + error = FM_MAC_AdjustLink(sc->sc_mach, speed, duplex); + if (error != E_OK) + device_printf(sc->sc_dev, "error while adjusting MAC speed.\n"); +} +/** @} */ diff --git a/sys/dev/dpaa/if_dtsec.h b/sys/dev/dpaa/if_dtsec.h new file mode 100644 index 000000000000..2e2e28fe24f9 --- /dev/null +++ b/sys/dev/dpaa/if_dtsec.h @@ -0,0 +1,154 @@ +/*- + * Copyright (c) 2011-2012 Semihalf. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef IF_DTSEC_H_ +#define IF_DTSEC_H_ + +/** + * @group dTSEC common API. + * @{ + */ +#define DTSEC_MODE_REGULAR 0 +#define DTSEC_MODE_INDEPENDENT 1 + +#define DTSEC_LOCK(sc) mtx_lock(&(sc)->sc_lock) +#define DTSEC_UNLOCK(sc) mtx_unlock(&(sc)->sc_lock) +#define DTSEC_LOCK_ASSERT(sc) mtx_assert(&(sc)->sc_lock, MA_OWNED) +#define DTSEC_MII_LOCK(sc) mtx_lock(&(sc)->sc_mii_lock) +#define DTSEC_MII_UNLOCK(sc) mtx_unlock(&(sc)->sc_mii_lock) + +enum eth_dev_type { + ETH_DTSEC = 0x1, + ETH_10GSEC = 0x2 +}; + +struct dtsec_softc { + /* XXX MII bus requires that struct ifnet is first!!! */ + struct ifnet *sc_ifnet; + + device_t sc_dev; + struct mtx sc_lock; + int sc_mode; + + /* Methods */ + int (*sc_port_rx_init) + (struct dtsec_softc *sc, int unit); + int (*sc_port_tx_init) + (struct dtsec_softc *sc, int unit); + void (*sc_start_locked) + (struct dtsec_softc *sc); + + /* dTSEC data */ + enum eth_dev_type sc_eth_dev_type; + uint8_t sc_eth_id; + uintptr_t sc_mac_mem_offset; + e_EnetMode sc_mac_enet_mode; + int sc_mac_mdio_irq; + uint8_t sc_mac_addr[6]; + int sc_port_rx_hw_id; + int sc_port_tx_hw_id; + uint32_t sc_port_tx_qman_chan; + int sc_phy_addr; + bool sc_hidden; + + /* Params from fman_bus driver */ + vm_offset_t sc_fm_base; + t_Handle sc_fmh; + t_Handle sc_muramh; + + t_Handle sc_mach; + t_Handle sc_rxph; + t_Handle sc_txph; + + /* MII data */ + struct mii_data *sc_mii; + device_t sc_mii_dev; + struct mtx sc_mii_lock; + + struct callout sc_tick_callout; + + /* RX Pool */ + t_Handle sc_rx_pool; + uint8_t sc_rx_bpid; + uma_zone_t sc_rx_zone; + char sc_rx_zname[64]; + + /* RX Frame Queue */ + t_Handle sc_rx_fqr; + uint32_t sc_rx_fqid; + + /* TX Frame Queue */ + t_Handle sc_tx_fqr; + bool sc_tx_fqr_full; + t_Handle sc_tx_conf_fqr; + uint32_t sc_tx_conf_fqid; + + /* Frame Info Zone */ + uma_zone_t sc_fi_zone; + char sc_fi_zname[64]; +}; +/** @} */ + + +/** + * @group dTSEC FMan PORT API. + * @{ + */ +enum dtsec_fm_port_params { + FM_PORT_LIODN_BASE = 0, + FM_PORT_LIODN_OFFSET = 0, + FM_PORT_MEM_ID = 0, + FM_PORT_MEM_ATTR = MEMORY_ATTR_CACHEABLE, + FM_PORT_BUFFER_SIZE = MCLBYTES, +}; + +e_FmPortType dtsec_fm_port_rx_type(enum eth_dev_type type); +void dtsec_fm_port_rx_exception_callback(t_Handle app, + e_FmPortExceptions exception); +void dtsec_fm_port_tx_exception_callback(t_Handle app, + e_FmPortExceptions exception); +e_FmPortType dtsec_fm_port_tx_type(enum eth_dev_type type); +/** @} */ + + +/** + * @group dTSEC bus interface. + * @{ + */ +int dtsec_attach(device_t dev); +int dtsec_detach(device_t dev); +int dtsec_suspend(device_t dev); +int dtsec_resume(device_t dev); +int dtsec_shutdown(device_t dev); +int dtsec_miibus_readreg(device_t dev, int phy, int reg); +int dtsec_miibus_writereg(device_t dev, int phy, int reg, + int value); +void dtsec_miibus_statchg(device_t dev); +/** @} */ + +#endif /* IF_DTSEC_H_ */ diff --git a/sys/dev/dpaa/if_dtsec_fdt.c b/sys/dev/dpaa/if_dtsec_fdt.c new file mode 100644 index 000000000000..25b2cfc2a712 --- /dev/null +++ b/sys/dev/dpaa/if_dtsec_fdt.c @@ -0,0 +1,215 @@ +/*- + * Copyright (c) 2012 Semihalf. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/bus.h> +#include <sys/module.h> +#include <sys/socket.h> + +#include <powerpc/mpc85xx/mpc85xx.h> + +#include <net/if.h> +#include <net/if_media.h> + +#include <dev/mii/mii.h> +#include <dev/mii/miivar.h> + +#include <dev/fdt/fdt_common.h> +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> +#include <dev/ofw/openfirm.h> + +#include "miibus_if.h" + +#include <contrib/ncsw/inc/Peripherals/fm_port_ext.h> +#include <contrib/ncsw/inc/xx_ext.h> + +#include "if_dtsec.h" + + +static int dtsec_fdt_probe(device_t dev); +static int dtsec_fdt_attach(device_t dev); + +static device_method_t dtsec_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, dtsec_fdt_probe), + DEVMETHOD(device_attach, dtsec_fdt_attach), + DEVMETHOD(device_detach, dtsec_detach), + + DEVMETHOD(device_shutdown, dtsec_shutdown), + DEVMETHOD(device_suspend, dtsec_suspend), + DEVMETHOD(device_resume, dtsec_resume), + + /* Bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + DEVMETHOD(bus_driver_added, bus_generic_driver_added), + + /* MII interface */ + DEVMETHOD(miibus_readreg, dtsec_miibus_readreg), + DEVMETHOD(miibus_writereg, dtsec_miibus_writereg), + DEVMETHOD(miibus_statchg, dtsec_miibus_statchg), + + { 0, 0 } +}; + +static driver_t dtsec_driver = { + "dtsec", + dtsec_methods, + sizeof(struct dtsec_softc), +}; + +static devclass_t dtsec_devclass; +DRIVER_MODULE(dtsec, dpaa, dtsec_driver, dtsec_devclass, 0, 0); +DRIVER_MODULE(miibus, dtsec, miibus_driver, miibus_devclass, 0, 0); +MODULE_DEPEND(dtsec, ether, 1, 1, 1); +MODULE_DEPEND(dtsec, miibus, 1, 1, 1); + +static int +dtsec_fdt_probe(device_t dev) +{ + + if (!ofw_bus_is_compatible(dev, "fsl,dpa-ethernet")) + return (ENXIO); + + device_set_desc(dev, "Freescale Data Path Triple Speed Ethernet " + "Controller"); + + return (BUS_PROBE_DEFAULT); +} + +static int +dtsec_fdt_attach(device_t dev) +{ + struct dtsec_softc *sc; + phandle_t node, enet_node, phy_node; + phandle_t fman_rxtx_node[2]; + char phy_type[6]; + + sc = device_get_softc(dev); + node = ofw_bus_get_node(dev); + + if (OF_getprop(node, "fsl,fman-mac", (void *)&enet_node, + sizeof(enet_node)) == -1) { + device_printf(dev, "Could not load fsl,fman-mac property " + "from DTS\n"); + return (ENXIO); + } + + enet_node = OF_instance_to_package(enet_node); + + if (OF_getprop(enet_node, "local-mac-address", + (void *)sc->sc_mac_addr, 6) == -1) { + if (device_get_unit(dev) != 0) { + device_printf(dev, + "Could not load local-mac-addr property " + "from DTS\n"); + return (ENXIO); + } + sc->sc_hidden = true; + } + + /* Get link speed */ + if (fdt_is_compatible(enet_node, "fsl,fman-1g-mac") != 0) + sc->sc_eth_dev_type = ETH_DTSEC; + else if (fdt_is_compatible(enet_node, "fsl,fman-10g-mac") != 0) + sc->sc_eth_dev_type = ETH_10GSEC; + else + return(ENXIO); + + /* Get MAC memory offset in SoC */ + if (OF_getprop(enet_node, "reg", (void *)&sc->sc_mac_mem_offset, + sizeof(sc->sc_mac_mem_offset)) <= 0) + return (ENXIO); + + /* Get PHY address */ + if (OF_getprop(enet_node, "phy-handle", (void *)&phy_node, + sizeof(phy_node)) <= 0) + return (ENXIO); + + phy_node = OF_instance_to_package(phy_node); + + if (OF_getprop(phy_node, "reg", (void *)&sc->sc_phy_addr, + sizeof(sc->sc_phy_addr)) <= 0) + return (ENXIO); + + /* Get PHY connection type */ + if (OF_getprop(enet_node, "phy-connection-type", (void *)phy_type, + sizeof(phy_type)) <= 0) + return (ENXIO); + + if (!strcmp(phy_type, "sgmii")) + sc->sc_mac_enet_mode = e_ENET_MODE_SGMII_1000; + else if (!strcmp(phy_type, "rgmii")) + sc->sc_mac_enet_mode = e_ENET_MODE_RGMII_1000; + else if (!strcmp(phy_type, "xgmii")) + /* We set 10 Gigabit mode flag however we don't support it */ + sc->sc_mac_enet_mode = e_ENET_MODE_XGMII_10000; + else + return (ENXIO); + + /* Get RX/TX port handles */ + if (OF_getprop(enet_node, "fsl,port-handles", (void *)fman_rxtx_node, + sizeof(fman_rxtx_node)) <= 0) + return (ENXIO); + + if (fman_rxtx_node[0] == 0) + return (ENXIO); + + if (fman_rxtx_node[1] == 0) + return (ENXIO); + + fman_rxtx_node[0] = OF_instance_to_package(fman_rxtx_node[0]); + fman_rxtx_node[1] = OF_instance_to_package(fman_rxtx_node[1]); + + if (fdt_is_compatible(fman_rxtx_node[0], "fsl,fman-port-1g-rx") == 0) + return (ENXIO); + + if (fdt_is_compatible(fman_rxtx_node[1], "fsl,fman-port-1g-tx") == 0) + return (ENXIO); + + /* Get RX port HW id */ + if (OF_getprop(fman_rxtx_node[0], "reg", (void *)&sc->sc_port_rx_hw_id, + sizeof(sc->sc_port_rx_hw_id)) <= 0) + return (ENXIO); + + /* Get TX port HW id */ + if (OF_getprop(fman_rxtx_node[1], "reg", (void *)&sc->sc_port_tx_hw_id, + sizeof(sc->sc_port_tx_hw_id)) <= 0) + return (ENXIO); + + /* Get QMan channel */ + if (OF_getprop(fman_rxtx_node[1], "fsl,qman-channel-id", + (void *)&sc->sc_port_tx_qman_chan, + sizeof(sc->sc_port_tx_qman_chan)) <= 0) + return (ENXIO); + + return (dtsec_attach(dev)); +} diff --git a/sys/dev/dpaa/if_dtsec_im.c b/sys/dev/dpaa/if_dtsec_im.c new file mode 100644 index 000000000000..335f489cc591 --- /dev/null +++ b/sys/dev/dpaa/if_dtsec_im.c @@ -0,0 +1,262 @@ +/*- + * Copyright (c) 2012 Semihalf. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/bus.h> +#include <sys/rman.h> +#include <sys/malloc.h> +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <sys/sysctl.h> +#include <sys/sockio.h> + +#include <net/ethernet.h> +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_types.h> +#include <net/if_arp.h> + +#include <dev/mii/mii.h> +#include <dev/mii/miivar.h> + +#include "miibus_if.h" + +#include <contrib/ncsw/inc/Peripherals/fm_mac_ext.h> +#include <contrib/ncsw/inc/Peripherals/fm_port_ext.h> +#include <contrib/ncsw/inc/xx_ext.h> + +#include "fman.h" +#include "if_dtsec.h" +#include "if_dtsec_im.h" + + +/** + * @group dTSEC FMan PORT routines. + * @{ + */ +static e_RxStoreResponse +dtsec_im_fm_port_rx_callback(t_Handle app, uint8_t *data, uint16_t length, + uint16_t status, uint8_t position, t_Handle buf_context) +{ + struct dtsec_softc *sc; + struct mbuf *m; + + /* TODO STATUS / Position checking */ + sc = app; + + m = m_devget(data, length, 0, sc->sc_ifnet, NULL); + if (m) + (*sc->sc_ifnet->if_input)(sc->sc_ifnet, m); + + XX_FreeSmart(data); + + return (e_RX_STORE_RESPONSE_CONTINUE); +} + +static void +dtsec_im_fm_port_tx_conf_callback(t_Handle app, uint8_t *data, uint16_t status, + t_Handle buf_context) +{ + + /* TODO: Check status */ + XX_FreeSmart(data); +} + +static uint8_t * +dtsec_im_fm_port_rx_get_buf(t_Handle buffer_pool, t_Handle *buf_context_handle) +{ + struct dtsec_softc *sc; + uint8_t *buffer; + + sc = buffer_pool; + + buffer = XX_MallocSmart(FM_PORT_BUFFER_SIZE, 0, sizeof(void *)); + if (!buffer) + device_printf(sc->sc_dev, "couldn't allocate RX buffer.\n"); + + return (buffer); +} + +static t_Error +dtsec_im_fm_port_rx_put_buf(t_Handle buffer_pool, uint8_t *buffer, + t_Handle buf_context) +{ + + XX_FreeSmart(buffer); + return (E_OK); +} + +int +dtsec_im_fm_port_rx_init(struct dtsec_softc *sc, int unit) +{ + t_FmPortParams params; + t_BufferPoolInfo *pool_params; + t_FmPortImRxTxParams *im_params; + t_Error error; + + memset(¶ms, 0, sizeof(params)); + + params.baseAddr = sc->sc_fm_base + sc->sc_port_rx_hw_id; + params.h_Fm = sc->sc_fmh; + params.portType = dtsec_fm_port_rx_type(sc->sc_eth_dev_type); + params.portId = sc->sc_eth_id; + params.independentModeEnable = TRUE; + params.liodnBase = FM_PORT_LIODN_BASE; + params.f_Exception = dtsec_fm_port_rx_exception_callback; + params.h_App = sc; + + im_params = ¶ms.specificParams.imRxTxParams; + im_params->h_FmMuram = sc->sc_muramh; + im_params->liodnOffset = FM_PORT_LIODN_OFFSET; + im_params->dataMemId = FM_PORT_MEM_ID; + im_params->dataMemAttributes = FM_PORT_MEM_ATTR; + im_params->f_RxStore = dtsec_im_fm_port_rx_callback; + + pool_params = ¶ms.specificParams.imRxTxParams.rxPoolParams; + pool_params->h_BufferPool = sc; + pool_params->f_GetBuf = dtsec_im_fm_port_rx_get_buf; + pool_params->f_PutBuf = dtsec_im_fm_port_rx_put_buf; + pool_params->bufferSize = FM_PORT_BUFFER_SIZE; + + sc->sc_rxph = FM_PORT_Config(¶ms); + if (sc->sc_rxph == NULL) { + device_printf(sc->sc_dev, "couldn't configure FM Port RX.\n"); + return (ENXIO); + } + + error = FM_PORT_Init(sc->sc_rxph); + if (error != E_OK) { + device_printf(sc->sc_dev, "couldn't initialize FM Port RX.\n"); + FM_PORT_Free(sc->sc_rxph); + return (ENXIO); + } + + if (bootverbose) + device_printf(sc->sc_dev, "RX hw port 0x%02x initialized.\n", + sc->sc_port_rx_hw_id); + + return (0); +} + +int +dtsec_im_fm_port_tx_init(struct dtsec_softc *sc, int unit) +{ + t_FmPortParams params; + t_FmPortImRxTxParams *im_params; + t_Error error; + + memset(¶ms, 0, sizeof(params)); + + params.baseAddr = sc->sc_fm_base + sc->sc_port_tx_hw_id; + params.h_Fm = sc->sc_fmh; + params.portType = dtsec_fm_port_tx_type(sc->sc_eth_dev_type); + params.portId = unit; + params.independentModeEnable = TRUE; + params.liodnBase = FM_PORT_LIODN_BASE; + params.f_Exception = dtsec_fm_port_tx_exception_callback; + params.h_App = sc; + + im_params = ¶ms.specificParams.imRxTxParams; + im_params->h_FmMuram = sc->sc_muramh; + im_params->liodnOffset = FM_PORT_LIODN_OFFSET; + im_params->dataMemId = FM_PORT_MEM_ID; + im_params->dataMemAttributes = FM_PORT_MEM_ATTR; + im_params->f_TxConf = dtsec_im_fm_port_tx_conf_callback; + + sc->sc_txph = FM_PORT_Config(¶ms); + if (sc->sc_txph == NULL) { + device_printf(sc->sc_dev, "couldn't configure FM Port TX.\n"); + return (ENXIO); + } + + error = FM_PORT_Init(sc->sc_txph); + if (error != E_OK) { + device_printf(sc->sc_dev, "couldn't initialize FM Port TX.\n"); + FM_PORT_Free(sc->sc_txph); + return (ENXIO); + } + + if (bootverbose) + device_printf(sc->sc_dev, "TX hw port 0x%02x initialized.\n", + sc->sc_port_tx_hw_id); + + return (0); +} +/** @} */ + + +/** + * @group dTSEC IFnet routines. + * @{ + */ +void +dtsec_im_if_start_locked(struct dtsec_softc *sc) +{ + uint8_t *buffer; + uint16_t length; + struct mbuf *m; + int error; + + DTSEC_LOCK_ASSERT(sc); + /* TODO: IFF_DRV_OACTIVE */ + + if ((sc->sc_mii->mii_media_status & IFM_ACTIVE) == 0) + return; + + if ((sc->sc_ifnet->if_drv_flags & IFF_DRV_RUNNING) != IFF_DRV_RUNNING) + return; + + while (!IFQ_DRV_IS_EMPTY(&sc->sc_ifnet->if_snd)) { + IFQ_DRV_DEQUEUE(&sc->sc_ifnet->if_snd, m); + if (m == NULL) + break; + + length = m_length(m, NULL); + buffer = XX_MallocSmart(length, 0, sizeof(void *)); + if (!buffer) { + m_freem(m); + break; + } + + m_copydata(m, 0, length, buffer); + m_freem(m); + + error = FM_PORT_ImTx(sc->sc_txph, buffer, length, TRUE, buffer); + if (error != E_OK) { + /* TODO: Ring full */ + XX_FreeSmart(buffer); + break; + } + } +} +/** @} */ diff --git a/sys/dev/dpaa/if_dtsec_im.h b/sys/dev/dpaa/if_dtsec_im.h new file mode 100644 index 000000000000..922bea4ac77e --- /dev/null +++ b/sys/dev/dpaa/if_dtsec_im.h @@ -0,0 +1,41 @@ +/*- + * Copyright (c) 2012 Semihalf. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef IF_DTSEC_IM_H_ +#define IF_DTSEC_IM_H_ + +/** + * @group dTSEC Independent Mode API. + * @{ + */ +int dtsec_im_fm_port_tx_init(struct dtsec_softc *sc, int unit); +int dtsec_im_fm_port_rx_init(struct dtsec_softc *sc, int unit); +void dtsec_im_if_start_locked(struct dtsec_softc *sc); +/** @} */ + +#endif /* IF_DTSEC_IM_H_ */ diff --git a/sys/dev/dpaa/if_dtsec_rm.c b/sys/dev/dpaa/if_dtsec_rm.c new file mode 100644 index 000000000000..30ab3265d0bc --- /dev/null +++ b/sys/dev/dpaa/if_dtsec_rm.c @@ -0,0 +1,654 @@ +/*- + * Copyright (c) 2012 Semihalf. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/bus.h> +#include <sys/rman.h> +#include <sys/malloc.h> +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <sys/sysctl.h> +#include <sys/sockio.h> + +#include <net/ethernet.h> +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_types.h> +#include <net/if_arp.h> + +#include <dev/mii/mii.h> +#include <dev/mii/miivar.h> + +#include "miibus_if.h" + +#include <contrib/ncsw/inc/Peripherals/fm_mac_ext.h> +#include <contrib/ncsw/inc/Peripherals/fm_port_ext.h> +#include <contrib/ncsw/inc/xx_ext.h> + +#include "fman.h" +#include "bman.h" +#include "qman.h" +#include "if_dtsec.h" +#include "if_dtsec_rm.h" + + +/** + * @group dTSEC RM private defines. + * @{ + */ +#define DTSEC_BPOOLS_USED (1) +#define DTSEC_MAX_TX_QUEUE_LEN 256 + +struct dtsec_rm_frame_info { + struct mbuf *fi_mbuf; + t_DpaaSGTE fi_sgt[DPAA_NUM_OF_SG_TABLE_ENTRY]; +}; + +enum dtsec_rm_pool_params { + DTSEC_RM_POOL_RX_LOW_MARK = 16, + DTSEC_RM_POOL_RX_HIGH_MARK = 64, + DTSEC_RM_POOL_RX_MAX_SIZE = 256, + + DTSEC_RM_POOL_FI_LOW_MARK = 16, + DTSEC_RM_POOL_FI_HIGH_MARK = 64, + DTSEC_RM_POOL_FI_MAX_SIZE = 256, +}; + +enum dtsec_rm_fqr_params { + DTSEC_RM_FQR_RX_CHANNEL = e_QM_FQ_CHANNEL_POOL1, + DTSEC_RM_FQR_RX_WQ = 1, + DTSEC_RM_FQR_TX_CONF_CHANNEL = e_QM_FQ_CHANNEL_SWPORTAL0, + DTSEC_RM_FQR_TX_WQ = 1, + DTSEC_RM_FQR_TX_CONF_WQ = 1 +}; +/** @} */ + + +/** + * @group dTSEC Frame Info routines. + * @{ + */ +void +dtsec_rm_fi_pool_free(struct dtsec_softc *sc) +{ + + if (sc->sc_fi_zone != NULL) + uma_zdestroy(sc->sc_fi_zone); +} + +int +dtsec_rm_fi_pool_init(struct dtsec_softc *sc) +{ + + snprintf(sc->sc_fi_zname, sizeof(sc->sc_fi_zname), "%s: Frame Info", + device_get_nameunit(sc->sc_dev)); + + sc->sc_fi_zone = uma_zcreate(sc->sc_fi_zname, + sizeof(struct dtsec_rm_frame_info), NULL, NULL, NULL, NULL, + sizeof(void *), 0); + if (sc->sc_fi_zone == NULL) + return (EIO); + + return (0); +} + +static struct dtsec_rm_frame_info * +dtsec_rm_fi_alloc(struct dtsec_softc *sc) +{ + struct dtsec_rm_frame_info *fi; + + fi = uma_zalloc(sc->sc_fi_zone, M_NOWAIT); + + return (fi); +} + +static void +dtsec_rm_fi_free(struct dtsec_softc *sc, struct dtsec_rm_frame_info *fi) +{ + + XX_UntrackAddress(fi); + uma_zfree(sc->sc_fi_zone, fi); +} +/** @} */ + + +/** + * @group dTSEC FMan PORT routines. + * @{ + */ +int +dtsec_rm_fm_port_rx_init(struct dtsec_softc *sc, int unit) +{ + t_FmPortParams params; + t_FmPortRxParams *rx_params; + t_FmPortExtPools *pool_params; + t_Error error; + + memset(¶ms, 0, sizeof(params)); + + params.baseAddr = sc->sc_fm_base + sc->sc_port_rx_hw_id; + params.h_Fm = sc->sc_fmh; + params.portType = dtsec_fm_port_rx_type(sc->sc_eth_dev_type); + params.portId = sc->sc_eth_id; + params.independentModeEnable = FALSE; + params.liodnBase = FM_PORT_LIODN_BASE; + params.f_Exception = dtsec_fm_port_rx_exception_callback; + params.h_App = sc; + + rx_params = ¶ms.specificParams.rxParams; + rx_params->errFqid = sc->sc_rx_fqid; + rx_params->dfltFqid = sc->sc_rx_fqid; + rx_params->liodnOffset = 0; + + pool_params = &rx_params->extBufPools; + pool_params->numOfPoolsUsed = DTSEC_BPOOLS_USED; + pool_params->extBufPool->id = sc->sc_rx_bpid; + pool_params->extBufPool->size = FM_PORT_BUFFER_SIZE; + + sc->sc_rxph = FM_PORT_Config(¶ms); + if (sc->sc_rxph == NULL) { + device_printf(sc->sc_dev, "couldn't configure FM Port RX.\n"); + return (ENXIO); + } + + error = FM_PORT_Init(sc->sc_rxph); + if (error != E_OK) { + device_printf(sc->sc_dev, "couldn't initialize FM Port RX.\n"); + FM_PORT_Free(sc->sc_rxph); + return (ENXIO); + } + + if (bootverbose) + device_printf(sc->sc_dev, "RX hw port 0x%02x initialized.\n", + sc->sc_port_rx_hw_id); + + return (0); +} + +int +dtsec_rm_fm_port_tx_init(struct dtsec_softc *sc, int unit) +{ + t_FmPortParams params; + t_FmPortNonRxParams *tx_params; + t_Error error; + + memset(¶ms, 0, sizeof(params)); + + params.baseAddr = sc->sc_fm_base + sc->sc_port_tx_hw_id; + params.h_Fm = sc->sc_fmh; + params.portType = dtsec_fm_port_tx_type(sc->sc_eth_dev_type); + params.portId = sc->sc_eth_id; + params.independentModeEnable = FALSE; + params.liodnBase = FM_PORT_LIODN_BASE; + params.f_Exception = dtsec_fm_port_tx_exception_callback; + params.h_App = sc; + + tx_params = ¶ms.specificParams.nonRxParams; + tx_params->errFqid = sc->sc_tx_conf_fqid; + tx_params->dfltFqid = sc->sc_tx_conf_fqid; + tx_params->qmChannel = sc->sc_port_tx_qman_chan; +#ifdef FM_OP_PARTITION_ERRATA_FMANx8 + tx_params->opLiodnOffset = 0; +#endif + + sc->sc_txph = FM_PORT_Config(¶ms); + if (sc->sc_txph == NULL) { + device_printf(sc->sc_dev, "couldn't configure FM Port TX.\n"); + return (ENXIO); + } + + error = FM_PORT_Init(sc->sc_txph); + if (error != E_OK) { + device_printf(sc->sc_dev, "couldn't initialize FM Port TX.\n"); + FM_PORT_Free(sc->sc_txph); + return (ENXIO); + } + + if (bootverbose) + device_printf(sc->sc_dev, "TX hw port 0x%02x initialized.\n", + sc->sc_port_tx_hw_id); + + return (0); +} +/** @} */ + + +/** + * @group dTSEC buffer pools routines. + * @{ + */ +static t_Error +dtsec_rm_pool_rx_put_buffer(t_Handle h_BufferPool, uint8_t *buffer, + t_Handle context) +{ + struct dtsec_softc *sc; + + sc = h_BufferPool; + uma_zfree(sc->sc_rx_zone, buffer); + + return (E_OK); +} + +static uint8_t * +dtsec_rm_pool_rx_get_buffer(t_Handle h_BufferPool, t_Handle *context) +{ + struct dtsec_softc *sc; + uint8_t *buffer; + + sc = h_BufferPool; + buffer = uma_zalloc(sc->sc_rx_zone, M_NOWAIT); + + return (buffer); +} + +static void +dtsec_rm_pool_rx_depleted(t_Handle h_App, bool in) +{ + struct dtsec_softc *sc; + unsigned int count; + + sc = h_App; + + if (!in) + return; + + while (1) { + count = bman_count(sc->sc_rx_pool); + if (count > DTSEC_RM_POOL_RX_HIGH_MARK) + return; + + bman_pool_fill(sc->sc_rx_pool, DTSEC_RM_POOL_RX_HIGH_MARK); + } +} + +void +dtsec_rm_pool_rx_free(struct dtsec_softc *sc) +{ + + if (sc->sc_rx_pool != NULL) + bman_pool_destroy(sc->sc_rx_pool); + + if (sc->sc_rx_zone != NULL) + uma_zdestroy(sc->sc_rx_zone); +} + +int +dtsec_rm_pool_rx_init(struct dtsec_softc *sc) +{ + + /* FM_PORT_BUFFER_SIZE must be less than PAGE_SIZE */ + CTASSERT(FM_PORT_BUFFER_SIZE < PAGE_SIZE); + + snprintf(sc->sc_rx_zname, sizeof(sc->sc_rx_zname), "%s: RX Buffers", + device_get_nameunit(sc->sc_dev)); + + sc->sc_rx_zone = uma_zcreate(sc->sc_rx_zname, FM_PORT_BUFFER_SIZE, NULL, + NULL, NULL, NULL, FM_PORT_BUFFER_SIZE, 0); + if (sc->sc_rx_zone == NULL) + return (EIO); + + sc->sc_rx_pool = bman_pool_create(&sc->sc_rx_bpid, FM_PORT_BUFFER_SIZE, + 0, 0, DTSEC_RM_POOL_RX_MAX_SIZE, dtsec_rm_pool_rx_get_buffer, + dtsec_rm_pool_rx_put_buffer, DTSEC_RM_POOL_RX_LOW_MARK, + DTSEC_RM_POOL_RX_HIGH_MARK, 0, 0, dtsec_rm_pool_rx_depleted, sc, NULL, + NULL); + if (sc->sc_rx_pool == NULL) { + dtsec_rm_pool_rx_free(sc); + return (EIO); + } + + return (0); +} +/** @} */ + + +/** + * @group dTSEC Frame Queue Range routines. + * @{ + */ +static void +dtsec_rm_fqr_mext_free(struct mbuf *m, void *buffer, void *arg) +{ + struct dtsec_softc *sc; + + sc = arg; + if (bman_count(sc->sc_rx_pool) <= DTSEC_RM_POOL_RX_MAX_SIZE) + bman_put_buffer(sc->sc_rx_pool, buffer); + else + dtsec_rm_pool_rx_put_buffer(arg, buffer, NULL); +} + +static e_RxStoreResponse +dtsec_rm_fqr_rx_callback(t_Handle app, t_Handle fqr, t_Handle portal, + uint32_t fqid_off, t_DpaaFD *frame) +{ + struct dtsec_softc *sc; + struct mbuf *m; + + m = NULL; + sc = app; + + KASSERT(DPAA_FD_GET_FORMAT(frame) == e_DPAA_FD_FORMAT_TYPE_SHORT_SBSF, + ("%s(): Got unsupported frame format 0x%02X!", __func__, + DPAA_FD_GET_FORMAT(frame))); + + KASSERT(DPAA_FD_GET_OFFSET(frame) == 0, + ("%s(): Only offset 0 is supported!", __func__)); + + if (DPAA_FD_GET_STATUS(frame) != 0) { + device_printf(sc->sc_dev, "RX error: 0x%08X\n", + DPAA_FD_GET_STATUS(frame)); + goto err; + } + + m = m_gethdr(M_NOWAIT, MT_HEADER); + if (m == NULL) + goto err; + + MEXTADD(m, DPAA_FD_GET_ADDR(frame), FM_PORT_BUFFER_SIZE, + dtsec_rm_fqr_mext_free, DPAA_FD_GET_ADDR(frame), sc, 0, + EXT_NET_DRV); + + m->m_pkthdr.rcvif = sc->sc_ifnet; + m->m_len = DPAA_FD_GET_LENGTH(frame); + m_fixhdr(m); + + (*sc->sc_ifnet->if_input)(sc->sc_ifnet, m); + + return (e_RX_STORE_RESPONSE_CONTINUE); + +err: + bman_put_buffer(sc->sc_rx_pool, DPAA_FD_GET_ADDR(frame)); + if (m != NULL) + m_freem(m); + + return (e_RX_STORE_RESPONSE_CONTINUE); +} + +static e_RxStoreResponse +dtsec_rm_fqr_tx_confirm_callback(t_Handle app, t_Handle fqr, t_Handle portal, + uint32_t fqid_off, t_DpaaFD *frame) +{ + struct dtsec_rm_frame_info *fi; + struct dtsec_softc *sc; + unsigned int qlen; + t_DpaaSGTE *sgt0; + + sc = app; + + if (DPAA_FD_GET_STATUS(frame) != 0) + device_printf(sc->sc_dev, "TX error: 0x%08X\n", + DPAA_FD_GET_STATUS(frame)); + + /* + * We are storing struct dtsec_rm_frame_info in first entry + * of scatter-gather table. + */ + sgt0 = DPAA_FD_GET_ADDR(frame); + fi = DPAA_SGTE_GET_ADDR(sgt0); + + /* Free transmitted frame */ + m_freem(fi->fi_mbuf); + dtsec_rm_fi_free(sc, fi); + + qlen = qman_fqr_get_counter(sc->sc_tx_conf_fqr, 0, + e_QM_FQR_COUNTERS_FRAME); + + if (qlen == 0) { + DTSEC_LOCK(sc); + + if (sc->sc_tx_fqr_full) { + sc->sc_tx_fqr_full = 0; + dtsec_rm_if_start_locked(sc); + } + + DTSEC_UNLOCK(sc); + } + + return (e_RX_STORE_RESPONSE_CONTINUE); +} + +void +dtsec_rm_fqr_rx_free(struct dtsec_softc *sc) +{ + + if (sc->sc_rx_fqr) + qman_fqr_free(sc->sc_rx_fqr); +} + +int +dtsec_rm_fqr_rx_init(struct dtsec_softc *sc) +{ + t_Error error; + t_Handle fqr; + + /* Default Frame Queue */ + fqr = qman_fqr_create(1, DTSEC_RM_FQR_RX_CHANNEL, DTSEC_RM_FQR_RX_WQ, + FALSE, 0, FALSE, FALSE, TRUE, FALSE, 0, 0, 0); + if (fqr == NULL) { + device_printf(sc->sc_dev, "could not create default RX queue" + "\n"); + return (EIO); + } + + sc->sc_rx_fqr = fqr; + sc->sc_rx_fqid = qman_fqr_get_base_fqid(fqr); + + error = qman_fqr_register_cb(fqr, dtsec_rm_fqr_rx_callback, sc); + if (error != E_OK) { + device_printf(sc->sc_dev, "could not register RX callback\n"); + dtsec_rm_fqr_rx_free(sc); + return (EIO); + } + + return (0); +} + +void +dtsec_rm_fqr_tx_free(struct dtsec_softc *sc) +{ + + if (sc->sc_tx_fqr) + qman_fqr_free(sc->sc_tx_fqr); + + if (sc->sc_tx_conf_fqr) + qman_fqr_free(sc->sc_tx_conf_fqr); +} + +int +dtsec_rm_fqr_tx_init(struct dtsec_softc *sc) +{ + t_Error error; + t_Handle fqr; + + /* TX Frame Queue */ + fqr = qman_fqr_create(1, sc->sc_port_tx_qman_chan, + DTSEC_RM_FQR_TX_WQ, FALSE, 0, FALSE, FALSE, TRUE, FALSE, 0, 0, 0); + if (fqr == NULL) { + device_printf(sc->sc_dev, "could not create default TX queue" + "\n"); + return (EIO); + } + + sc->sc_tx_fqr = fqr; + + /* TX Confirmation Frame Queue */ + fqr = qman_fqr_create(1, DTSEC_RM_FQR_TX_CONF_CHANNEL, + DTSEC_RM_FQR_TX_CONF_WQ, FALSE, 0, FALSE, FALSE, TRUE, FALSE, 0, 0, + 0); + if (fqr == NULL) { + device_printf(sc->sc_dev, "could not create TX confirmation " + "queue\n"); + dtsec_rm_fqr_tx_free(sc); + return (EIO); + } + + sc->sc_tx_conf_fqr = fqr; + sc->sc_tx_conf_fqid = qman_fqr_get_base_fqid(fqr); + + error = qman_fqr_register_cb(fqr, dtsec_rm_fqr_tx_confirm_callback, sc); + if (error != E_OK) { + device_printf(sc->sc_dev, "could not register TX confirmation " + "callback\n"); + dtsec_rm_fqr_tx_free(sc); + return (EIO); + } + + return (0); +} +/** @} */ + + +/** + * @group dTSEC IFnet routines. + * @{ + */ +void +dtsec_rm_if_start_locked(struct dtsec_softc *sc) +{ + vm_size_t dsize, psize, ssize; + struct dtsec_rm_frame_info *fi; + unsigned int qlen, i; + struct mbuf *m0, *m; + vm_offset_t vaddr; + vm_paddr_t paddr; + t_DpaaFD fd; + + DTSEC_LOCK_ASSERT(sc); + /* TODO: IFF_DRV_OACTIVE */ + + if ((sc->sc_mii->mii_media_status & IFM_ACTIVE) == 0) + return; + + if ((sc->sc_ifnet->if_drv_flags & IFF_DRV_RUNNING) != IFF_DRV_RUNNING) + return; + + while (!IFQ_DRV_IS_EMPTY(&sc->sc_ifnet->if_snd)) { + /* Check length of the TX queue */ + qlen = qman_fqr_get_counter(sc->sc_tx_fqr, 0, + e_QM_FQR_COUNTERS_FRAME); + + if (qlen >= DTSEC_MAX_TX_QUEUE_LEN) { + sc->sc_tx_fqr_full = 1; + return; + } + + fi = dtsec_rm_fi_alloc(sc); + if (fi == NULL) + return; + + IFQ_DRV_DEQUEUE(&sc->sc_ifnet->if_snd, m0); + if (m0 == NULL) { + dtsec_rm_fi_free(sc, fi); + return; + } + + i = 0; + m = m0; + psize = 0; + dsize = 0; + fi->fi_mbuf = m0; + while (m && i < DPAA_NUM_OF_SG_TABLE_ENTRY) { + if (m->m_len == 0) + continue; + + /* + * First entry in scatter-gather table is used to keep + * pointer to frame info structure. + */ + DPAA_SGTE_SET_ADDR(&fi->fi_sgt[i], (void *)fi); + DPAA_SGTE_SET_LENGTH(&fi->fi_sgt[i], 0); + + DPAA_SGTE_SET_EXTENSION(&fi->fi_sgt[i], 0); + DPAA_SGTE_SET_FINAL(&fi->fi_sgt[i], 0); + DPAA_SGTE_SET_BPID(&fi->fi_sgt[i], 0); + DPAA_SGTE_SET_OFFSET(&fi->fi_sgt[i], 0); + i++; + + dsize = m->m_len; + vaddr = (vm_offset_t)m->m_data; + while (dsize > 0 && i < DPAA_NUM_OF_SG_TABLE_ENTRY) { + paddr = XX_VirtToPhys((void *)vaddr); + ssize = PAGE_SIZE - (paddr & PAGE_MASK); + if (m->m_len < ssize) + ssize = m->m_len; + + DPAA_SGTE_SET_ADDR(&fi->fi_sgt[i], + (void *)vaddr); + DPAA_SGTE_SET_LENGTH(&fi->fi_sgt[i], ssize); + + DPAA_SGTE_SET_EXTENSION(&fi->fi_sgt[i], 0); + DPAA_SGTE_SET_FINAL(&fi->fi_sgt[i], 0); + DPAA_SGTE_SET_BPID(&fi->fi_sgt[i], 0); + DPAA_SGTE_SET_OFFSET(&fi->fi_sgt[i], 0); + + dsize -= ssize; + vaddr += ssize; + psize += ssize; + i++; + } + + if (dsize > 0) + break; + + m = m->m_next; + } + + /* Check if SG table was constructed properly */ + if (m != NULL || dsize != 0) { + dtsec_rm_fi_free(sc, fi); + m_freem(m0); + continue; + } + + DPAA_SGTE_SET_FINAL(&fi->fi_sgt[i-1], 1); + + DPAA_FD_SET_ADDR(&fd, fi->fi_sgt); + DPAA_FD_SET_LENGTH(&fd, psize); + DPAA_FD_SET_FORMAT(&fd, e_DPAA_FD_FORMAT_TYPE_SHORT_MBSF); + + DPAA_FD_SET_DD(&fd, 0); + DPAA_FD_SET_PID(&fd, 0); + DPAA_FD_SET_BPID(&fd, 0); + DPAA_FD_SET_OFFSET(&fd, 0); + DPAA_FD_SET_STATUS(&fd, 0); + + DTSEC_UNLOCK(sc); + if (qman_fqr_enqueue(sc->sc_tx_fqr, 0, &fd) != E_OK) { + dtsec_rm_fi_free(sc, fi); + m_freem(m0); + } + DTSEC_LOCK(sc); + } +} +/** @} */ diff --git a/sys/dev/dpaa/if_dtsec_rm.h b/sys/dev/dpaa/if_dtsec_rm.h new file mode 100644 index 000000000000..b79b44facac8 --- /dev/null +++ b/sys/dev/dpaa/if_dtsec_rm.h @@ -0,0 +1,53 @@ +/*- + * Copyright (c) 2012 Semihalf. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef IF_DTSEC_RM_H_ +#define IF_DTSEC_RM_H_ + +/** + * @group dTSEC Regular Mode API. + * @{ + */ +int dtsec_rm_fm_port_rx_init(struct dtsec_softc *sc, int unit); +int dtsec_rm_fm_port_tx_init(struct dtsec_softc *sc, int unit); + +void dtsec_rm_if_start_locked(struct dtsec_softc *sc); + +int dtsec_rm_pool_rx_init(struct dtsec_softc *sc); +void dtsec_rm_pool_rx_free(struct dtsec_softc *sc); + +int dtsec_rm_fi_pool_init(struct dtsec_softc *sc); +void dtsec_rm_fi_pool_free(struct dtsec_softc *sc); + +int dtsec_rm_fqr_rx_init(struct dtsec_softc *sc); +int dtsec_rm_fqr_tx_init(struct dtsec_softc *sc); +void dtsec_rm_fqr_rx_free(struct dtsec_softc *sc); +void dtsec_rm_fqr_tx_free(struct dtsec_softc *sc); +/** @} */ + +#endif /* IF_DTSEC_RM_H_ */ diff --git a/sys/dev/dpaa/portals.h b/sys/dev/dpaa/portals.h new file mode 100644 index 000000000000..3f006768f97e --- /dev/null +++ b/sys/dev/dpaa/portals.h @@ -0,0 +1,64 @@ +/*- + * Copyright (c) 2012 Semihalf. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +typedef struct dpaa_portal { + int dp_irid; /* interrupt rid */ + struct resource *dp_ires; /* interrupt resource */ + + bool dp_regs_mapped; /* register mapping status */ + + t_Handle dp_ph; /* portal's handle */ + vm_paddr_t dp_ce_pa; /* portal's CE area PA */ + vm_paddr_t dp_ci_pa; /* portal's CI area PA */ + uint32_t dp_ce_size; /* portal's CE area size */ + uint32_t dp_ci_size; /* portal's CI area size */ + uint32_t dp_intr_num; /* portal's intr. number */ +} dpaa_portal_t; + +struct dpaa_portals_softc { + device_t sc_dev; /* device handle */ + vm_paddr_t sc_dp_pa; /* portal's PA */ + uint32_t sc_dp_size; /* portal's size */ + int sc_rrid[2]; /* memory rid */ + struct resource *sc_rres[2]; /* memory resource */ + dpaa_portal_t sc_dp[MAXCPU]; +}; + +struct dpaa_portals_devinfo { + struct resource_list di_res; + int di_intr_rid; +}; + +int bman_portals_attach(device_t); +int bman_portals_detach(device_t); + +int qman_portals_attach(device_t); +int qman_portals_detach(device_t); + +int dpaa_portal_alloc_res(device_t, struct dpaa_portals_devinfo *, int); +void dpaa_portal_map_registers(struct dpaa_portals_softc *); diff --git a/sys/dev/dpaa/portals_common.c b/sys/dev/dpaa/portals_common.c new file mode 100644 index 000000000000..e540077b7555 --- /dev/null +++ b/sys/dev/dpaa/portals_common.c @@ -0,0 +1,166 @@ +/*- + * Copyright (c) 2012 Semihalf. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "opt_platform.h" +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/bus.h> +#include <sys/proc.h> +#include <sys/pcpu.h> +#include <sys/rman.h> +#include <sys/sched.h> + +#include <vm/vm.h> +#include <vm/pmap.h> + +#include <machine/resource.h> +#include <machine/tlb.h> + +#include <contrib/ncsw/inc/error_ext.h> +#include <contrib/ncsw/inc/xx_ext.h> + +#include "portals.h" + + +int +dpaa_portal_alloc_res(device_t dev, struct dpaa_portals_devinfo *di, int cpu) +{ + struct dpaa_portals_softc *sc = device_get_softc(dev); + struct resource_list_entry *rle; + int err; + struct resource_list *res; + + /* Check if MallocSmart allocator is ready */ + if (XX_MallocSmartInit() != E_OK) + return (ENXIO); + + res = &di->di_res; + + /* + * Allocate memory. + * Reserve only one pair of CE/CI virtual memory regions + * for all CPUs, in order to save the space. + */ + if (sc->sc_rres[0] == NULL) { + /* Cache enabled area */ + rle = resource_list_find(res, SYS_RES_MEMORY, 0); + sc->sc_rrid[0] = 0; + sc->sc_rres[0] = bus_alloc_resource(dev, + SYS_RES_MEMORY, &sc->sc_rrid[0], rle->start + sc->sc_dp_pa, + rle->end + sc->sc_dp_pa, rle->count, RF_ACTIVE); + pmap_change_attr((vm_offset_t)rman_get_bushandle(sc->sc_rres[0]), + rle->count, VM_MEMATTR_CACHEABLE); + if (sc->sc_rres[0] == NULL) { + device_printf(dev, "Could not allocate memory.\n"); + return (ENXIO); + } + /* Cache inhibited area */ + rle = resource_list_find(res, SYS_RES_MEMORY, 1); + sc->sc_rrid[1] = 1; + sc->sc_rres[1] = bus_alloc_resource(dev, + SYS_RES_MEMORY, &sc->sc_rrid[1], rle->start + sc->sc_dp_pa, + rle->end + sc->sc_dp_pa, rle->count, RF_ACTIVE); + if (sc->sc_rres[1] == NULL) { + device_printf(dev, "Could not allocate memory.\n"); + bus_release_resource(dev, SYS_RES_MEMORY, + sc->sc_rrid[0], sc->sc_rres[0]); + return (ENXIO); + } + sc->sc_dp[PCPU_GET(cpuid)].dp_regs_mapped = 1; + } + /* Acquire portal's CE_PA and CI_PA */ + rle = resource_list_find(res, SYS_RES_MEMORY, 0); + sc->sc_dp[cpu].dp_ce_pa = rle->start + sc->sc_dp_pa; + sc->sc_dp[cpu].dp_ce_size = rle->count; + rle = resource_list_find(res, SYS_RES_MEMORY, 1); + sc->sc_dp[cpu].dp_ci_pa = rle->start + sc->sc_dp_pa; + sc->sc_dp[cpu].dp_ci_size = rle->count; + + /* Allocate interrupts */ + rle = resource_list_find(res, SYS_RES_IRQ, 0); + sc->sc_dp[cpu].dp_irid = 0; + sc->sc_dp[cpu].dp_ires = bus_alloc_resource(dev, + SYS_RES_IRQ, &sc->sc_dp[cpu].dp_irid, rle->start, rle->end, + rle->count, RF_ACTIVE); + /* Save interrupt number for later use */ + sc->sc_dp[cpu].dp_intr_num = rle->start; + + if (sc->sc_dp[cpu].dp_ires == NULL) { + device_printf(dev, "Could not allocate irq.\n"); + return (ENXIO); + } + + err = XX_PreallocAndBindIntr((int)sc->sc_dp[cpu].dp_ires, cpu); + + if (err != E_OK) { + device_printf(dev, "Could not prealloc and bind interrupt\n"); + bus_release_resource(dev, SYS_RES_IRQ, + sc->sc_dp[cpu].dp_irid, sc->sc_dp[cpu].dp_ires); + sc->sc_dp[cpu].dp_ires = NULL; + return (ENXIO); + } + +#if 0 + err = bus_generic_config_intr(dev, rle->start, di->di_intr_trig, + di->di_intr_pol); + if (err != 0) { + device_printf(dev, "Could not configure interrupt\n"); + bus_release_resource(dev, SYS_RES_IRQ, + sc->sc_dp[cpu].dp_irid, sc->sc_dp[cpu].dp_ires); + sc->sc_dp[cpu].dp_ires = NULL; + return (err); + } +#endif + + return (0); +} + +void +dpaa_portal_map_registers(struct dpaa_portals_softc *sc) +{ + unsigned int cpu; + + sched_pin(); + cpu = PCPU_GET(cpuid); + if (sc->sc_dp[cpu].dp_regs_mapped) + goto out; + + tlb1_set_entry(rman_get_bushandle(sc->sc_rres[0]), + sc->sc_dp[cpu].dp_ce_pa, sc->sc_dp[cpu].dp_ce_size, + _TLB_ENTRY_MEM); + tlb1_set_entry(rman_get_bushandle(sc->sc_rres[1]), + sc->sc_dp[cpu].dp_ci_pa, sc->sc_dp[cpu].dp_ci_size, + _TLB_ENTRY_IO); + + sc->sc_dp[cpu].dp_regs_mapped = 1; + +out: + sched_unpin(); +} diff --git a/sys/dev/dpaa/qman.c b/sys/dev/dpaa/qman.c new file mode 100644 index 000000000000..69de8ab11cbd --- /dev/null +++ b/sys/dev/dpaa/qman.c @@ -0,0 +1,555 @@ +/*- + * Copyright (c) 2011-2012 Semihalf. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/bus.h> +#include <sys/lock.h> +#include <sys/module.h> +#include <sys/mutex.h> +#include <sys/proc.h> +#include <sys/pcpu.h> +#include <sys/rman.h> +#include <sys/sched.h> +#include <sys/smp.h> + +#include <machine/bus.h> +#include <machine/resource.h> +#include <machine/tlb.h> + +#include "qman.h" +#include "portals.h" + +extern struct dpaa_portals_softc *qp_sc; +static struct qman_softc *qman_sc; + +extern t_Handle qman_portal_setup(struct qman_softc *qsc); + +static void +qman_exception(t_Handle app, e_QmExceptions exception) +{ + struct qman_softc *sc; + const char *message; + + sc = app; + + switch (exception) { + case e_QM_EX_CORENET_INITIATOR_DATA: + message = "Initiator Data Error"; + break; + case e_QM_EX_CORENET_TARGET_DATA: + message = "CoreNet Target Data Error"; + break; + case e_QM_EX_CORENET_INVALID_TARGET_TRANSACTION: + message = "Invalid Target Transaction"; + break; + case e_QM_EX_PFDR_THRESHOLD: + message = "PFDR Low Watermark Interrupt"; + break; + case e_QM_EX_PFDR_ENQUEUE_BLOCKED: + message = "PFDR Enqueues Blocked Interrupt"; + break; + case e_QM_EX_SINGLE_ECC: + message = "Single Bit ECC Error Interrupt"; + break; + case e_QM_EX_MULTI_ECC: + message = "Multi Bit ECC Error Interrupt"; + break; + case e_QM_EX_INVALID_COMMAND: + message = "Invalid Command Verb Interrupt"; + break; + case e_QM_EX_DEQUEUE_DCP: + message = "Invalid Dequeue Direct Connect Portal Interrupt"; + break; + case e_QM_EX_DEQUEUE_FQ: + message = "Invalid Dequeue FQ Interrupt"; + break; + case e_QM_EX_DEQUEUE_SOURCE: + message = "Invalid Dequeue Source Interrupt"; + break; + case e_QM_EX_DEQUEUE_QUEUE: + message = "Invalid Dequeue Queue Interrupt"; + break; + case e_QM_EX_ENQUEUE_OVERFLOW: + message = "Invalid Enqueue Overflow Interrupt"; + break; + case e_QM_EX_ENQUEUE_STATE: + message = "Invalid Enqueue State Interrupt"; + break; + case e_QM_EX_ENQUEUE_CHANNEL: + message = "Invalid Enqueue Channel Interrupt"; + break; + case e_QM_EX_ENQUEUE_QUEUE: + message = "Invalid Enqueue Queue Interrupt"; + break; + case e_QM_EX_CG_STATE_CHANGE: + message = "CG change state notification"; + break; + default: + message = "Unknown error"; + } + + device_printf(sc->sc_dev, "QMan Exception: %s.\n", message); +} + +/** + * General received frame callback. + * This is called, when user did not register his own callback for a given + * frame queue range (fqr). + */ +e_RxStoreResponse +qman_received_frame_callback(t_Handle app, t_Handle qm_fqr, t_Handle qm_portal, + uint32_t fqid_offset, t_DpaaFD *frame) +{ + struct qman_softc *sc; + + sc = app; + + device_printf(sc->sc_dev, "dummy callback for received frame.\n"); + return (e_RX_STORE_RESPONSE_CONTINUE); +} + +/** + * General rejected frame callback. + * This is called, when user did not register his own callback for a given + * frame queue range (fqr). + */ +e_RxStoreResponse +qman_rejected_frame_callback(t_Handle app, t_Handle qm_fqr, t_Handle qm_portal, + uint32_t fqid_offset, t_DpaaFD *frame, + t_QmRejectedFrameInfo *qm_rejected_frame_info) +{ + struct qman_softc *sc; + + sc = app; + + device_printf(sc->sc_dev, "dummy callback for rejected frame.\n"); + return (e_RX_STORE_RESPONSE_CONTINUE); +} + +int +qman_attach(device_t dev) +{ + struct qman_softc *sc; + t_QmParam qp; + t_Error error; + t_QmRevisionInfo rev; + + sc = device_get_softc(dev); + sc->sc_dev = dev; + qman_sc = sc; + + if (XX_MallocSmartInit() != E_OK) { + device_printf(dev, "could not initialize smart allocator.\n"); + return (ENXIO); + } + + sched_pin(); + + /* Allocate resources */ + sc->sc_rrid = 0; + sc->sc_rres = bus_alloc_resource(dev, SYS_RES_MEMORY, + &sc->sc_rrid, 0, ~0, QMAN_CCSR_SIZE, RF_ACTIVE); + if (sc->sc_rres == NULL) { + device_printf(dev, "could not allocate memory.\n"); + goto err; + } + + sc->sc_irid = 0; + sc->sc_ires = bus_alloc_resource_any(dev, SYS_RES_IRQ, + &sc->sc_irid, RF_ACTIVE | RF_SHAREABLE); + if (sc->sc_ires == NULL) { + device_printf(dev, "could not allocate error interrupt.\n"); + goto err; + } + + if (qp_sc == NULL) + goto err; + + dpaa_portal_map_registers(qp_sc); + + /* Initialize QMan */ + qp.guestId = NCSW_MASTER_ID; + qp.baseAddress = rman_get_bushandle(sc->sc_rres); + qp.swPortalsBaseAddress = rman_get_bushandle(qp_sc->sc_rres[0]); + qp.liodn = 0; + qp.totalNumOfFqids = QMAN_MAX_FQIDS; + qp.fqdMemPartitionId = NCSW_MASTER_ID; + qp.pfdrMemPartitionId = NCSW_MASTER_ID; + qp.f_Exception = qman_exception; + qp.h_App = sc; + qp.errIrq = (int)sc->sc_ires; + qp.partFqidBase = QMAN_FQID_BASE; + qp.partNumOfFqids = QMAN_MAX_FQIDS; + qp.partCgsBase = 0; + qp.partNumOfCgs = 0; + + sc->sc_qh = QM_Config(&qp); + if (sc->sc_qh == NULL) { + device_printf(dev, "could not be configured\n"); + goto err; + } + + error = QM_Init(sc->sc_qh); + if (error != E_OK) { + device_printf(dev, "could not be initialized\n"); + goto err; + } + + error = QM_GetRevision(sc->sc_qh, &rev); + if (error != E_OK) { + device_printf(dev, "could not get QMan revision\n"); + goto err; + } + + device_printf(dev, "Hardware version: %d.%d.\n", + rev.majorRev, rev.minorRev); + + sched_unpin(); + + qman_portal_setup(sc); + + return (0); + +err: + sched_unpin(); + qman_detach(dev); + return (ENXIO); +} + +int +qman_detach(device_t dev) +{ + struct qman_softc *sc; + + sc = device_get_softc(dev); + + if (sc->sc_qh) + QM_Free(sc->sc_qh); + + if (sc->sc_ires != NULL) + XX_DeallocIntr((int)sc->sc_ires); + + if (sc->sc_ires != NULL) + bus_release_resource(dev, SYS_RES_IRQ, + sc->sc_irid, sc->sc_ires); + + if (sc->sc_rres != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, + sc->sc_rrid, sc->sc_rres); + + return (0); +} + +int +qman_suspend(device_t dev) +{ + + return (0); +} + +int +qman_resume(device_t dev) +{ + + return (0); +} + +int +qman_shutdown(device_t dev) +{ + + return (0); +} + + +/** + * @group QMan API functions implementation. + * @{ + */ + +t_Handle +qman_fqr_create(uint32_t fqids_num, e_QmFQChannel channel, uint8_t wq, + bool force_fqid, uint32_t fqid_or_align, bool init_parked, + bool hold_active, bool prefer_in_cache, bool congst_avoid_ena, + t_Handle congst_group, int8_t overhead_accounting_len, + uint32_t tail_drop_threshold) +{ + struct qman_softc *sc; + t_QmFqrParams fqr; + unsigned int cpu; + t_Handle fqrh, portal; + + sc = qman_sc; + + sched_pin(); + cpu = PCPU_GET(cpuid); + + /* Ensure we have got QMan port initialized */ + portal = qman_portal_setup(sc); + if (portal == NULL) { + device_printf(sc->sc_dev, "could not setup QMan portal\n"); + goto err; + } + + fqr.h_Qm = sc->sc_qh; + fqr.h_QmPortal = portal; + fqr.initParked = init_parked; + fqr.holdActive = hold_active; + fqr.preferInCache = prefer_in_cache; + + /* We do not support stashing */ + fqr.useContextAForStash = FALSE; + fqr.p_ContextA = 0; + fqr.p_ContextB = 0; + + fqr.channel = channel; + fqr.wq = wq; + fqr.shadowMode = FALSE; + fqr.numOfFqids = fqids_num; + + /* FQID */ + fqr.useForce = force_fqid; + if (force_fqid) { + fqr.qs.frcQ.fqid = fqid_or_align; + } else { + fqr.qs.nonFrcQs.align = fqid_or_align; + } + + /* Congestion Avoidance */ + fqr.congestionAvoidanceEnable = congst_avoid_ena; + if (congst_avoid_ena) { + fqr.congestionAvoidanceParams.h_QmCg = congst_group; + fqr.congestionAvoidanceParams.overheadAccountingLength = + overhead_accounting_len; + fqr.congestionAvoidanceParams.fqTailDropThreshold = + tail_drop_threshold; + } else { + fqr.congestionAvoidanceParams.h_QmCg = 0; + fqr.congestionAvoidanceParams.overheadAccountingLength = 0; + fqr.congestionAvoidanceParams.fqTailDropThreshold = 0; + } + + fqrh = QM_FQR_Create(&fqr); + if (fqrh == NULL) { + device_printf(sc->sc_dev, "could not create Frame Queue Range" + "\n"); + goto err; + } + + sc->sc_fqr_cpu[QM_FQR_GetFqid(fqrh)] = PCPU_GET(cpuid); + + sched_unpin(); + + return (fqrh); + +err: + sched_unpin(); + + return (NULL); +} + +t_Error +qman_fqr_free(t_Handle fqr) +{ + struct qman_softc *sc; + t_Error error; + + sc = qman_sc; + thread_lock(curthread); + sched_bind(curthread, sc->sc_fqr_cpu[QM_FQR_GetFqid(fqr)]); + thread_unlock(curthread); + + error = QM_FQR_Free(fqr); + + thread_lock(curthread); + sched_unbind(curthread); + thread_unlock(curthread); + + return (error); +} + +t_Error +qman_fqr_register_cb(t_Handle fqr, t_QmReceivedFrameCallback *callback, + t_Handle app) +{ + struct qman_softc *sc; + t_Error error; + t_Handle portal; + + sc = qman_sc; + sched_pin(); + + /* Ensure we have got QMan port initialized */ + portal = qman_portal_setup(sc); + if (portal == NULL) { + device_printf(sc->sc_dev, "could not setup QMan portal\n"); + sched_unpin(); + return (E_NOT_SUPPORTED); + } + + error = QM_FQR_RegisterCB(fqr, callback, app); + + sched_unpin(); + + return (error); +} + +t_Error +qman_fqr_enqueue(t_Handle fqr, uint32_t fqid_off, t_DpaaFD *frame) +{ + struct qman_softc *sc; + t_Error error; + t_Handle portal; + + sc = qman_sc; + sched_pin(); + + /* Ensure we have got QMan port initialized */ + portal = qman_portal_setup(sc); + if (portal == NULL) { + device_printf(sc->sc_dev, "could not setup QMan portal\n"); + sched_unpin(); + return (E_NOT_SUPPORTED); + } + + error = QM_FQR_Enqueue(fqr, portal, fqid_off, frame); + + sched_unpin(); + + return (error); +} + +uint32_t +qman_fqr_get_counter(t_Handle fqr, uint32_t fqid_off, + e_QmFqrCounters counter) +{ + struct qman_softc *sc; + uint32_t val; + t_Handle portal; + + sc = qman_sc; + sched_pin(); + + /* Ensure we have got QMan port initialized */ + portal = qman_portal_setup(sc); + if (portal == NULL) { + device_printf(sc->sc_dev, "could not setup QMan portal\n"); + sched_unpin(); + return (0); + } + + val = QM_FQR_GetCounter(fqr, portal, fqid_off, counter); + + sched_unpin(); + + return (val); +} + +t_Error +qman_fqr_pull_frame(t_Handle fqr, uint32_t fqid_off, t_DpaaFD *frame) +{ + struct qman_softc *sc; + t_Error error; + t_Handle portal; + + sc = qman_sc; + sched_pin(); + + /* Ensure we have got QMan port initialized */ + portal = qman_portal_setup(sc); + if (portal == NULL) { + device_printf(sc->sc_dev, "could not setup QMan portal\n"); + sched_unpin(); + return (E_NOT_SUPPORTED); + } + + error = QM_FQR_PullFrame(fqr, portal, fqid_off, frame); + + sched_unpin(); + + return (error); +} + +uint32_t +qman_fqr_get_base_fqid(t_Handle fqr) +{ + struct qman_softc *sc; + uint32_t val; + t_Handle portal; + + sc = qman_sc; + sched_pin(); + + /* Ensure we have got QMan port initialized */ + portal = qman_portal_setup(sc); + if (portal == NULL) { + device_printf(sc->sc_dev, "could not setup QMan portal\n"); + sched_unpin(); + return (0); + } + + val = QM_FQR_GetFqid(fqr); + + sched_unpin(); + + return (val); +} + +t_Error +qman_poll(e_QmPortalPollSource source) +{ + struct qman_softc *sc; + t_Error error; + t_Handle portal; + + sc = qman_sc; + sched_pin(); + + /* Ensure we have got QMan port initialized */ + portal = qman_portal_setup(sc); + if (portal == NULL) { + device_printf(sc->sc_dev, "could not setup QMan portal\n"); + sched_unpin(); + return (E_NOT_SUPPORTED); + } + + error = QM_Poll(sc->sc_qh, source); + + sched_unpin(); + + return (error); +} + +/* + * TODO: add polling and/or congestion support. + */ + +/** @} */ diff --git a/sys/dev/dpaa/qman.h b/sys/dev/dpaa/qman.h new file mode 100644 index 000000000000..97331bbedeee --- /dev/null +++ b/sys/dev/dpaa/qman.h @@ -0,0 +1,246 @@ +/*- + * Copyright (c) 2011-2012 Semihalf. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _QMAN_H +#define _QMAN_H + +#include <machine/vmparam.h> + +#include <contrib/ncsw/inc/Peripherals/qm_ext.h> + + +/** + * @group QMan private defines/declarations + * @{ + */ +/** + * Maximum number of frame queues in all QMans. + */ +#define QMAN_MAX_FQIDS 16 + +/** + * Pool channel common to all software portals. + * @note Value of 0 reflects the e_QM_FQ_CHANNEL_POOL1 from e_QmFQChannel + * type used in qman_fqr_create(). + */ +#define QMAN_COMMON_POOL_CHANNEL 0 + +#define QMAN_FQID_BASE 1 + +#define QMAN_CCSR_SIZE 0x1000 + +/* + * Portal defines + */ +#define QMAN_CE_PA(base) (base) +#define QMAN_CI_PA(base) ((base) + 0x100000) + +#define QMAN_PORTAL_CE_PA(base, n) \ + (QMAN_CE_PA(base) + ((n) * QMAN_PORTAL_CE_SIZE)) +#define QMAN_PORTAL_CI_PA(base, n) \ + (QMAN_CI_PA(base) + ((n) * QMAN_PORTAL_CI_SIZE)) + +struct qman_softc { + device_t sc_dev; /* device handle */ + int sc_rrid; /* register rid */ + struct resource *sc_rres; /* register resource */ + int sc_irid; /* interrupt rid */ + struct resource *sc_ires; /* interrupt resource */ + + bool sc_regs_mapped[MAXCPU]; + + t_Handle sc_qh; /* QMAN handle */ + t_Handle sc_qph[MAXCPU]; /* QMAN portal handles */ + vm_paddr_t sc_qp_pa; /* QMAN portal PA */ + + int sc_fqr_cpu[QMAN_MAX_FQIDS]; +}; +/** @> */ + + +/** + * @group QMan bus interface + * @{ + */ +int qman_attach(device_t dev); +int qman_detach(device_t dev); +int qman_suspend(device_t dev); +int qman_resume(device_t dev); +int qman_shutdown(device_t dev); +/** @> */ + + +/** + * @group QMan API + * @{ + */ + +/** + * Create Frame Queue Range. + * + * @param fqids_num Number of frame queues in the range. + * + * @param channel Dedicated channel serviced by this + * Frame Queue Range. + * + * @param wq Work Queue Number within the channel. + * + * @param force_fqid If TRUE, fore allocation of specific + * FQID. Notice that there can not be two + * frame queues with the same ID in the + * system. + * + * @param fqid_or_align FQID if @force_fqid == TRUE, alignment + * of FQIDs entries otherwise. + * + * @param init_parked If TRUE, FQ state is initialized to + * "parked" state on creation. Otherwise, + * to "scheduled" state. + * + * @param hold_active If TRUE, the FQ may be held in the + * portal in "held active" state in + * anticipation of more frames being + * dequeued from it after the head frame + * is removed from the FQ and the dequeue + * response is returned. If FALSE the + * "held_active" state of the FQ is not + * allowed. This affects only on queues + * destined to software portals. Refer to + * the 6.3.4.6 of DPAA Reference Manual. + * + * @param prefer_in_cache If TRUE, prefer this FQR to be in QMan + * internal cache memory for all states. + * + * @param congst_avoid_ena If TRUE, enable congestion avoidance + * mechanism. + * + * @param congst_group A handle to the congestion group. Only + * relevant when @congst_avoid_ena == TRUE. + * + * @param overhead_accounting_len For each frame add this number for CG + * calculation (may be negative), if 0 - + * disable feature. + * + * @param tail_drop_threshold If not 0 - enable tail drop on this + * FQR. + * + * @return A handle to newly created FQR object. + */ +t_Handle qman_fqr_create(uint32_t fqids_num, e_QmFQChannel channel, uint8_t wq, + bool force_fqid, uint32_t fqid_or_align, bool init_parked, + bool hold_active, bool prefer_in_cache, bool congst_avoid_ena, + t_Handle congst_group, int8_t overhead_accounting_len, + uint32_t tail_drop_threshold); + +/** + * Free Frame Queue Range. + * + * @param fqr A handle to FQR to be freed. + * @return E_OK on success; error code otherwise. + */ +t_Error qman_fqr_free(t_Handle fqr); + +/** + * Register the callback function. + * The callback function will be called when a frame comes from this FQR. + * + * @param fqr A handle to FQR. + * @param callback A pointer to the callback function. + * @param app A pointer to the user's data. + * @return E_OK on success; error code otherwise. + */ +t_Error qman_fqr_register_cb(t_Handle fqr, t_QmReceivedFrameCallback *callback, + t_Handle app); + +/** + * Enqueue a frame on a given FQR. + * + * @param fqr A handle to FQR. + * @param fqid_off FQID offset wihin the FQR. + * @param frame A frame to be enqueued to the transmission. + * @return E_OK on success; error code otherwise. + */ +t_Error qman_fqr_enqueue(t_Handle fqr, uint32_t fqid_off, t_DpaaFD *frame); + +/** + * Get one of the FQR counter's value. + * + * @param fqr A handle to FQR. + * @param fqid_off FQID offset within the FQR. + * @param counter The requested counter. + * @return Counter's current value. + */ +uint32_t qman_fqr_get_counter(t_Handle fqr, uint32_t fqid_off, + e_QmFqrCounters counter); + +/** + * Pull frame from FQR. + * + * @param fqr A handle to FQR. + * @param fqid_off FQID offset within the FQR. + * @param frame The received frame. + * @return E_OK on success; error code otherwise. + */ +t_Error qman_fqr_pull_frame(t_Handle fqr, uint32_t fqid_off, t_DpaaFD *frame); + +/** + * Get base FQID of the FQR. + * @param fqr A handle to FQR. + * @return Base FQID of the FQR. + */ +uint32_t qman_fqr_get_base_fqid(t_Handle fqr); + +/** + * Poll frames from QMan. + * This polls frames from the current software portal. + * + * @param source Type of frames to be polled. + * @return E_OK on success; error otherwise. + */ +t_Error qman_poll(e_QmPortalPollSource source); + +/** + * General received frame callback. + * This is called, when user did not register his own callback for a given + * frame queue range (fqr). + */ +e_RxStoreResponse qman_received_frame_callback(t_Handle app, t_Handle qm_fqr, + t_Handle qm_portal, uint32_t fqid_offset, t_DpaaFD *frame); + +/** + * General rejected frame callback. + * This is called, when user did not register his own callback for a given + * frame queue range (fqr). + */ +e_RxStoreResponse qman_rejected_frame_callback(t_Handle app, t_Handle qm_fqr, + t_Handle qm_portal, uint32_t fqid_offset, t_DpaaFD *frame, + t_QmRejectedFrameInfo *qm_rejected_frame_info); + +/** @} */ + +#endif /* QMAN_H */ diff --git a/sys/dev/dpaa/qman_fdt.c b/sys/dev/dpaa/qman_fdt.c new file mode 100644 index 000000000000..4df8c05b84e0 --- /dev/null +++ b/sys/dev/dpaa/qman_fdt.c @@ -0,0 +1,221 @@ +/*- + * Copyright (c) 2011-2012 Semihalf. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "opt_platform.h" +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/bus.h> +#include <sys/module.h> + +#include <machine/bus.h> + +#include <dev/fdt/fdt_common.h> + +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> +#include <dev/ofw/ofw_subr.h> + +#include "qman.h" +#include "portals.h" + +#define FBMAN_DEVSTR "Freescale Queue Manager" + +static int qman_fdt_probe(device_t); + +static device_method_t qman_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, qman_fdt_probe), + DEVMETHOD(device_attach, qman_attach), + DEVMETHOD(device_detach, qman_detach), + + DEVMETHOD(device_suspend, qman_suspend), + DEVMETHOD(device_resume, qman_resume), + DEVMETHOD(device_shutdown, qman_shutdown), + + { 0, 0 } +}; + +static driver_t qman_driver = { + "qman", + qman_methods, + sizeof(struct qman_softc), +}; + +static devclass_t qman_devclass; +DRIVER_MODULE(qman, simplebus, qman_driver, qman_devclass, 0, 0); + +static int +qman_fdt_probe(device_t dev) +{ + + if (!ofw_bus_is_compatible(dev, "fsl,qman")) + return (ENXIO); + + device_set_desc(dev, FBMAN_DEVSTR); + + return (BUS_PROBE_DEFAULT); +} + +/* + * BMAN Portals + */ +#define BMAN_PORT_DEVSTR "Freescale Queue Manager - Portals" + +static device_probe_t qman_portals_fdt_probe; +static device_attach_t qman_portals_fdt_attach; + +static device_method_t bm_portals_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, qman_portals_fdt_probe), + DEVMETHOD(device_attach, qman_portals_fdt_attach), + DEVMETHOD(device_detach, qman_portals_detach), + + { 0, 0 } +}; + +static driver_t bm_portals_driver = { + "qman-portals", + bm_portals_methods, + sizeof(struct dpaa_portals_softc), +}; + +static devclass_t bm_portals_devclass; +DRIVER_MODULE(qman_portals, ofwbus, bm_portals_driver, bm_portals_devclass, 0, 0); + +static void +get_addr_props(phandle_t node, uint32_t *addrp, uint32_t *sizep) +{ + + *addrp = 2; + *sizep = 1; + OF_getencprop(node, "#address-cells", addrp, sizeof(*addrp)); + OF_getencprop(node, "#size-cells", sizep, sizeof(*sizep)); +} + +static int +qman_portals_fdt_probe(device_t dev) +{ + + if (!ofw_bus_is_compatible(dev, "qman-portals")) + return (ENXIO); + + device_set_desc(dev, BMAN_PORT_DEVSTR); + + return (BUS_PROBE_DEFAULT); +} + +static int +qman_portals_fdt_attach(device_t dev) +{ + struct dpaa_portals_softc *sc; + struct resource_list_entry *rle; + phandle_t node, child, cpu_node; + vm_paddr_t portal_pa; + vm_size_t portal_size; + uint32_t addr, size; + ihandle_t cpu; + int cpu_num, cpus, intr_rid; + struct dpaa_portals_devinfo di; + struct ofw_bus_devinfo ofw_di; + + cpus = 0; + sc = device_get_softc(dev); + sc->sc_dev = dev; + + node = ofw_bus_get_node(dev); + get_addr_props(node, &addr, &size); + + /* Find portals tied to CPUs */ + for (child = OF_child(node); child != 0; child = OF_peer(child)) { + if (!fdt_is_compatible(child, "fsl,qman-portal")) { + continue; + } + /* Checkout related cpu */ + if (OF_getprop(child, "cpu-handle", (void *)&cpu, + sizeof(cpu)) <= 0) { + continue; + } + /* Acquire cpu number */ + cpu_node = OF_instance_to_package(cpu); + if (OF_getencprop(cpu_node, "reg", &cpu_num, sizeof(cpu_num)) <= 0) { + device_printf(dev, "Could not retrieve CPU number.\n"); + return (ENXIO); + } + + cpus++; + + if (cpus > MAXCPU) + break; + + if (ofw_bus_gen_setup_devinfo(&ofw_di, child) != 0) { + device_printf(dev, "could not set up devinfo\n"); + continue; + } + + resource_list_init(&di.di_res); + if (ofw_bus_reg_to_rl(dev, child, addr, size, &di.di_res)) { + device_printf(dev, "%s: could not process 'reg' " + "property\n", ofw_di.obd_name); + ofw_bus_gen_destroy_devinfo(&ofw_di); + continue; + } + if (ofw_bus_intr_to_rl(dev, child, &di.di_res, &intr_rid)) { + device_printf(dev, "%s: could not process " + "'interrupts' property\n", ofw_di.obd_name); + resource_list_free(&di.di_res); + ofw_bus_gen_destroy_devinfo(&ofw_di); + continue; + } + di.di_intr_rid = intr_rid; + + ofw_reg_to_paddr(child, 0, &portal_pa, &portal_size, NULL); + rle = resource_list_find(&di.di_res, SYS_RES_MEMORY, 0); + + if (sc->sc_dp_pa == 0) + sc->sc_dp_pa = portal_pa - rle->start; + + portal_size = rle->end + 1; + rle = resource_list_find(&di.di_res, SYS_RES_MEMORY, 1); + portal_size = ulmax(rle->end + 1, portal_size); + sc->sc_dp_size = ulmax(sc->sc_dp_size, portal_size); + + if (dpaa_portal_alloc_res(dev, &di, cpu_num)) + goto err; + } + + ofw_bus_gen_destroy_devinfo(&ofw_di); + + return (qman_portals_attach(dev)); +err: + resource_list_free(&di.di_res); + ofw_bus_gen_destroy_devinfo(&ofw_di); + qman_portals_detach(dev); + return (ENXIO); +} diff --git a/sys/dev/dpaa/qman_portals.c b/sys/dev/dpaa/qman_portals.c new file mode 100644 index 000000000000..8f39307aeb96 --- /dev/null +++ b/sys/dev/dpaa/qman_portals.c @@ -0,0 +1,191 @@ +/*- + * Copyright (c) 2012 Semihalf. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "opt_platform.h" +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/bus.h> +#include <sys/lock.h> +#include <sys/module.h> +#include <sys/mutex.h> +#include <sys/proc.h> +#include <sys/pcpu.h> +#include <sys/sched.h> + +#include <machine/bus.h> +#include <machine/tlb.h> + +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> + +#include <powerpc/mpc85xx/mpc85xx.h> + +#include "qman.h" +#include "portals.h" + +extern e_RxStoreResponse qman_received_frame_callback(t_Handle, t_Handle, + t_Handle, uint32_t, t_DpaaFD *); +extern e_RxStoreResponse qman_rejected_frame_callback(t_Handle, t_Handle, + t_Handle, uint32_t, t_DpaaFD *, t_QmRejectedFrameInfo *); + +t_Handle qman_portal_setup(struct qman_softc *); + +struct dpaa_portals_softc *qp_sc; + +int +qman_portals_attach(device_t dev) +{ + struct dpaa_portals_softc *sc; + + sc = qp_sc = device_get_softc(dev); + + /* Map bman portal to physical address space */ + if (law_enable(OCP85XX_TGTIF_QMAN, sc->sc_dp_pa, sc->sc_dp_size)) { + qman_portals_detach(dev); + return (ENXIO); + } + /* Set portal properties for XX_VirtToPhys() */ + XX_PortalSetInfo(dev); + + return (bus_generic_attach(dev)); +} + +int +qman_portals_detach(device_t dev) +{ + struct dpaa_portals_softc *sc; + int i; + + qp_sc = NULL; + sc = device_get_softc(dev); + + for (i = 0; i < ARRAY_SIZE(sc->sc_dp); i++) { + if (sc->sc_dp[i].dp_ph != NULL) { + thread_lock(curthread); + sched_bind(curthread, i); + thread_unlock(curthread); + + QM_PORTAL_Free(sc->sc_dp[i].dp_ph); + + thread_lock(curthread); + sched_unbind(curthread); + thread_unlock(curthread); + } + + if (sc->sc_dp[i].dp_ires != NULL) { + XX_DeallocIntr((int)sc->sc_dp[i].dp_ires); + bus_release_resource(dev, SYS_RES_IRQ, + sc->sc_dp[i].dp_irid, sc->sc_dp[i].dp_ires); + } + } + for (i = 0; i < ARRAY_SIZE(sc->sc_rres); i++) { + if (sc->sc_rres[i] != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, + sc->sc_rrid[i], + sc->sc_rres[i]); + } + + return (0); +} + +t_Handle +qman_portal_setup(struct qman_softc *qsc) +{ + struct dpaa_portals_softc *sc; + t_QmPortalParam qpp; + unsigned int cpu, p; + t_Handle portal; + + /* Return NULL if we're not ready or while detach */ + if (qp_sc == NULL) + return (NULL); + + sc = qp_sc; + + sched_pin(); + portal = NULL; + cpu = PCPU_GET(cpuid); + + /* Check if portal is ready */ + while (atomic_cmpset_acq_32((uint32_t *)&sc->sc_dp[cpu].dp_ph, + 0, -1) == 0) { + p = atomic_load_acq_32((uint32_t *)&sc->sc_dp[cpu].dp_ph); + + /* Return if portal is already initialized */ + if (p != 0 && p != -1) { + sched_unpin(); + return ((t_Handle)p); + } + + /* Not inititialized and "owned" by another thread */ + thread_lock(curthread); + mi_switch(SW_VOL, NULL); + thread_unlock(curthread); + } + + /* Map portal registers */ + dpaa_portal_map_registers(sc); + + /* Configure and initialize portal */ + qpp.ceBaseAddress = rman_get_bushandle(sc->sc_rres[0]); + qpp.ciBaseAddress = rman_get_bushandle(sc->sc_rres[1]); + qpp.h_Qm = qsc->sc_qh; + qpp.swPortalId = cpu; + qpp.irq = (int)sc->sc_dp[cpu].dp_ires; + qpp.fdLiodnOffset = 0; + qpp.f_DfltFrame = qman_received_frame_callback; + qpp.f_RejectedFrame = qman_rejected_frame_callback; + qpp.h_App = qsc; + + portal = QM_PORTAL_Config(&qpp); + if (portal == NULL) + goto err; + + if (QM_PORTAL_Init(portal) != E_OK) + goto err; + + if (QM_PORTAL_AddPoolChannel(portal, QMAN_COMMON_POOL_CHANNEL) != E_OK) + goto err; + + atomic_store_rel_32((uint32_t *)&sc->sc_dp[cpu].dp_ph, + (uint32_t)portal); + sched_unpin(); + + return (portal); + +err: + if (portal != NULL) + QM_PORTAL_Free(portal); + + atomic_store_rel_32((uint32_t *)&sc->sc_dp[cpu].dp_ph, 0); + sched_unpin(); + + return (NULL); +} |