summaryrefslogtreecommitdiffstats
path: root/sys/dev/dpaa/qman.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/dpaa/qman.c')
-rw-r--r--sys/dev/dpaa/qman.c555
1 files changed, 555 insertions, 0 deletions
diff --git a/sys/dev/dpaa/qman.c b/sys/dev/dpaa/qman.c
new file mode 100644
index 0000000..69de8ab
--- /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.
+ */
+
+/** @} */
OpenPOWER on IntegriCloud