#include <sys/cdefs.h>
#include "opt_rss.h"
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/bus.h>
#include <sys/rman.h>
#include <sys/module.h>
#include <sys/malloc.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/_cpuset.h>
#include <sys/cpuset.h>
#include <sys/taskqueue.h>
#include <sys/smp.h>
#include <vm/vm.h>
#include <machine/bus.h>
#include <machine/resource.h>
#include <dev/pci/pcivar.h>
#ifdef RSS
#include <net/rss_config.h>
#endif
#include "pcib_if.h"
#include "pci_if.h"
#include "dpaa2_mc.h"
#include "dpaa2_mcp.h"
#include "dpaa2_swp.h"
#include "dpaa2_swp_if.h"
#include "dpaa2_cmd_if.h"
#include "dpaa2_io.h"
#include "dpaa2_ni.h"
#include "dpaa2_channel.h"
#define DPIO_IRQ_INDEX 0
#define DPIO_POLL_MAX 32
struct resource_spec dpaa2_io_spec[] = {
#define MEM_RES_NUM (3u)
#define MEM_RID_OFF (0u)
#define MEM_RID(rid) ((rid) + MEM_RID_OFF)
{ SYS_RES_MEMORY, MEM_RID(0), RF_ACTIVE | RF_UNMAPPED },
{ SYS_RES_MEMORY, MEM_RID(1), RF_ACTIVE | RF_UNMAPPED },
{ SYS_RES_MEMORY, MEM_RID(2), RF_ACTIVE | RF_UNMAPPED | RF_OPTIONAL },
#define MCP_RES_NUM (1u)
#define MCP_RID_OFF (MEM_RID_OFF + MEM_RES_NUM)
#define MCP_RID(rid) ((rid) + MCP_RID_OFF)
{ DPAA2_DEV_MCP, MCP_RID(0), RF_ACTIVE | RF_SHAREABLE | RF_OPTIONAL },
RESOURCE_SPEC_END
};
static int dpaa2_io_setup_irqs(device_t dev);
static int dpaa2_io_release_irqs(device_t dev);
static int dpaa2_io_setup_msi(struct dpaa2_io_softc *sc);
static int dpaa2_io_release_msi(struct dpaa2_io_softc *sc);
static void dpaa2_io_intr(void *arg);
static int
dpaa2_io_probe(device_t dev)
{
device_set_desc(dev, "DPAA2 I/O");
return (BUS_PROBE_DEFAULT);
}
static int
dpaa2_io_detach(device_t dev)
{
device_t pdev = device_get_parent(dev);
device_t child = dev;
struct dpaa2_io_softc *sc = device_get_softc(dev);
struct dpaa2_devinfo *rcinfo = device_get_ivars(pdev);
struct dpaa2_devinfo *dinfo = device_get_ivars(dev);
struct dpaa2_cmd cmd;
uint16_t rc_token, io_token;
int error;
DPAA2_CMD_INIT(&cmd);
dpaa2_io_release_irqs(dev);
dpaa2_swp_free_portal(sc->swp);
error = DPAA2_CMD_RC_OPEN(dev, child, &cmd, rcinfo->id, &rc_token);
if (error) {
device_printf(dev, "%s: failed to open DPRC: error=%d\n",
__func__, error);
goto err_exit;
}
error = DPAA2_CMD_IO_OPEN(dev, child, &cmd, dinfo->id, &io_token);
if (error) {
device_printf(dev, "%s: failed to open DPIO: id=%d, error=%d\n",
__func__, dinfo->id, error);
goto close_rc;
}
error = DPAA2_CMD_IO_DISABLE(dev, child, &cmd);
if (error && bootverbose) {
device_printf(dev, "%s: failed to disable DPIO: id=%d, "
"error=%d\n", __func__, dinfo->id, error);
}
(void)DPAA2_CMD_IO_CLOSE(dev, child, &cmd);
(void)DPAA2_CMD_RC_CLOSE(dev, child, DPAA2_CMD_TK(&cmd, rc_token));
for (int i = 0; i < MEM_RES_NUM; i++) {
if (sc->res[MEM_RID(i)] == NULL) {
continue;
}
error = bus_unmap_resource(sc->dev, SYS_RES_MEMORY,
sc->res[MEM_RID(i)], &sc->map[MEM_RID(i)]);
if (error && bootverbose) {
device_printf(dev, "%s: failed to unmap memory "
"resource: rid=%d, error=%d\n", __func__, MEM_RID(i),
error);
}
}
bus_release_resources(dev, dpaa2_io_spec, sc->res);
return (0);
close_rc:
(void)DPAA2_CMD_RC_CLOSE(dev, child, DPAA2_CMD_TK(&cmd, rc_token));
err_exit:
return (error);
}
static int
dpaa2_io_attach(device_t dev)
{
device_t pdev = device_get_parent(dev);
device_t child = dev;
device_t mcp_dev;
struct dpaa2_io_softc *sc = device_get_softc(dev);
struct dpaa2_devinfo *rcinfo = device_get_ivars(pdev);
struct dpaa2_devinfo *dinfo = device_get_ivars(dev);
struct dpaa2_devinfo *mcp_dinfo;
struct dpaa2_cmd cmd;
struct resource_map_request req;
struct {
vm_memattr_t memattr;
char *label;
} map_args[MEM_RES_NUM] = {
{ VM_MEMATTR_WRITE_BACK, "cache-enabled part" },
{ VM_MEMATTR_DEVICE, "cache-inhibited part" },
{ VM_MEMATTR_DEVICE, "control registers" }
};
uint16_t rc_token, io_token;
int error;
sc->dev = dev;
sc->swp = NULL;
sc->intr = NULL;
sc->irq_resource = NULL;
error = bus_alloc_resources(sc->dev, dpaa2_io_spec, sc->res);
if (error) {
device_printf(dev, "%s: failed to allocate resources: "
"error=%d\n", __func__, error);
return (ENXIO);
}
mcp_dev = (device_t) rman_get_start(sc->res[MCP_RID(0)]);
mcp_dinfo = device_get_ivars(mcp_dev);
dinfo->portal = mcp_dinfo->portal;
for (int i = 0; i < MEM_RES_NUM; i++) {
if (sc->res[MEM_RID(i)] == NULL) {
continue;
}
resource_init_map_request(&req);
req.memattr = map_args[i].memattr;
error = bus_map_resource(sc->dev, SYS_RES_MEMORY,
sc->res[MEM_RID(i)], &req, &sc->map[MEM_RID(i)]);
if (error) {
device_printf(dev, "%s: failed to map %s: error=%d\n",
__func__, map_args[i].label, error);
goto err_exit;
}
}
DPAA2_CMD_INIT(&cmd);
error = DPAA2_CMD_RC_OPEN(dev, child, &cmd, rcinfo->id, &rc_token);
if (error) {
device_printf(dev, "%s: failed to open DPRC: error=%d\n",
__func__, error);
goto err_exit;
}
error = DPAA2_CMD_IO_OPEN(dev, child, &cmd, dinfo->id, &io_token);
if (error) {
device_printf(dev, "%s: failed to open DPIO: id=%d, error=%d\n",
__func__, dinfo->id, error);
goto close_rc;
}
error = DPAA2_CMD_IO_RESET(dev, child, &cmd);
if (error) {
device_printf(dev, "%s: failed to reset DPIO: id=%d, error=%d\n",
__func__, dinfo->id, error);
goto close_io;
}
error = DPAA2_CMD_IO_GET_ATTRIBUTES(dev, child, &cmd, &sc->attr);
if (error) {
device_printf(dev, "%s: failed to get DPIO attributes: id=%d, "
"error=%d\n", __func__, dinfo->id, error);
goto close_io;
}
error = DPAA2_CMD_IO_ENABLE(dev, child, &cmd);
if (error) {
device_printf(dev, "%s: failed to enable DPIO: id=%d, "
"error=%d\n", __func__, dinfo->id, error);
goto close_io;
}
sc->swp_desc.dpio_dev = dev;
sc->swp_desc.swp_version = sc->attr.swp_version;
sc->swp_desc.swp_clk = sc->attr.swp_clk;
sc->swp_desc.swp_id = sc->attr.swp_id;
sc->swp_desc.has_notif = sc->attr.priors_num ? true : false;
sc->swp_desc.has_8prio = sc->attr.priors_num == 8u ? true : false;
sc->swp_desc.cena_res = sc->res[0];
sc->swp_desc.cena_map = &sc->map[0];
sc->swp_desc.cinh_res = sc->res[1];
sc->swp_desc.cinh_map = &sc->map[1];
sc->swp_desc.swp_cycles_ratio = 256000 /
(sc->swp_desc.swp_clk / 1000000);
error = dpaa2_swp_init_portal(&sc->swp, &sc->swp_desc, DPAA2_SWP_DEF);
if (error) {
device_printf(dev, "%s: failed to initialize dpaa2_swp: "
"error=%d\n", __func__, error);
goto err_exit;
}
error = dpaa2_io_setup_irqs(dev);
if (error) {
device_printf(dev, "%s: failed to setup IRQs: error=%d\n",
__func__, error);
goto err_exit;
}
if (bootverbose) {
device_printf(dev, "dpio_id=%d, swp_id=%d, chan_mode=%s, "
"notif_priors=%d, swp_version=0x%x\n",
sc->attr.id, sc->attr.swp_id,
sc->attr.chan_mode == DPAA2_IO_LOCAL_CHANNEL
? "local_channel" : "no_channel", sc->attr.priors_num,
sc->attr.swp_version);
}
(void)DPAA2_CMD_IO_CLOSE(dev, child, &cmd);
(void)DPAA2_CMD_RC_CLOSE(dev, child, DPAA2_CMD_TK(&cmd, rc_token));
return (0);
close_io:
(void)DPAA2_CMD_IO_CLOSE(dev, child, DPAA2_CMD_TK(&cmd, io_token));
close_rc:
(void)DPAA2_CMD_RC_CLOSE(dev, child, DPAA2_CMD_TK(&cmd, rc_token));
err_exit:
dpaa2_io_detach(dev);
return (ENXIO);
}
static int
dpaa2_io_enq_multiple_fq(device_t iodev, uint32_t fqid,
struct dpaa2_fd *fd, int frames_n)
{
struct dpaa2_io_softc *sc = device_get_softc(iodev);
struct dpaa2_swp *swp = sc->swp;
struct dpaa2_eq_desc ed;
uint32_t flags = 0;
memset(&ed, 0, sizeof(ed));
dpaa2_swp_set_ed_norp(&ed, false);
dpaa2_swp_set_ed_fq(&ed, fqid);
return (dpaa2_swp_enq_mult(swp, &ed, fd, &flags, frames_n));
}
static int
dpaa2_io_conf_wq_channel(device_t iodev, struct dpaa2_io_notif_ctx *ctx)
{
struct dpaa2_io_softc *sc = device_get_softc(iodev);
if (ctx->cdan_en) {
return (dpaa2_swp_conf_wq_channel(sc->swp, ctx->fq_chan_id,
DPAA2_WQCHAN_WE_EN | DPAA2_WQCHAN_WE_CTX, ctx->cdan_en,
ctx->qman_ctx));
}
return (0);
}
static int
dpaa2_io_query_bp(device_t iodev, uint16_t bpid, struct dpaa2_bp_conf *conf)
{
struct dpaa2_io_softc *sc = device_get_softc(iodev);
return (dpaa2_swp_query_bp(sc->swp, bpid, conf));
}
static int
dpaa2_io_release_bufs(device_t iodev, uint16_t bpid, bus_addr_t *buf,
uint32_t buf_num)
{
struct dpaa2_io_softc *sc = device_get_softc(iodev);
return (dpaa2_swp_release_bufs(sc->swp, bpid, buf, buf_num));
}
static int
dpaa2_io_setup_irqs(device_t dev)
{
struct dpaa2_io_softc *sc = device_get_softc(dev);
int error;
dpaa2_swp_set_intr_trigger(sc->swp, DPAA2_SWP_INTR_DQRI);
dpaa2_swp_clear_intr_status(sc->swp, 0xFFFFFFFFu);
error = dpaa2_io_setup_msi(sc);
if (error) {
device_printf(dev, "%s: failed to allocate MSI: error=%d\n",
__func__, error);
return (error);
}
if ((sc->irq_resource = bus_alloc_resource_any(dev, SYS_RES_IRQ,
&sc->irq_rid[0], RF_ACTIVE | RF_SHAREABLE)) == NULL) {
device_printf(dev, "%s: failed to allocate IRQ resource\n",
__func__);
return (ENXIO);
}
if (bus_setup_intr(dev, sc->irq_resource, INTR_TYPE_NET | INTR_MPSAFE |
INTR_ENTROPY, NULL, dpaa2_io_intr, sc, &sc->intr)) {
device_printf(dev, "%s: failed to setup IRQ resource\n",
__func__);
return (ENXIO);
}
#ifdef RSS
sc->cpu = rss_getcpu(sc->attr.id % rss_getnumbuckets());
#else
sc->cpu = sc->attr.id % mp_ncpus;
#endif
CPU_SETOF(sc->cpu, &sc->cpu_mask);
bus_bind_intr(dev, sc->irq_resource, sc->cpu);
if (sc->swp_desc.has_notif)
dpaa2_swp_set_push_dequeue(sc->swp, 0, true);
return (0);
}
static int
dpaa2_io_release_irqs(device_t dev)
{
struct dpaa2_io_softc *sc = device_get_softc(dev);
if (sc->swp_desc.has_notif)
dpaa2_swp_set_push_dequeue(sc->swp, 0, false);
if (sc->intr != NULL)
bus_teardown_intr(dev, sc->irq_resource, &sc->intr);
if (sc->irq_resource != NULL)
bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid[0],
sc->irq_resource);
(void)dpaa2_io_release_msi(device_get_softc(dev));
dpaa2_swp_set_intr_trigger(sc->swp, 0);
dpaa2_swp_clear_intr_status(sc->swp, 0xFFFFFFFFu);
return (0);
}
static int
dpaa2_io_setup_msi(struct dpaa2_io_softc *sc)
{
int val;
val = pci_msi_count(sc->dev);
if (val < DPAA2_IO_MSI_COUNT)
device_printf(sc->dev, "MSI: actual=%d, expected=%d\n", val,
DPAA2_IO_MSI_COUNT);
val = MIN(val, DPAA2_IO_MSI_COUNT);
if (pci_alloc_msi(sc->dev, &val) != 0)
return (EINVAL);
for (int i = 0; i < val; i++)
sc->irq_rid[i] = i + 1;
return (0);
}
static int
dpaa2_io_release_msi(struct dpaa2_io_softc *sc)
{
int error;
error = pci_release_msi(sc->dev);
if (error) {
device_printf(sc->dev, "%s: failed to release MSI: error=%d/n",
__func__, error);
return (error);
}
return (0);
}
static void
dpaa2_io_intr(void *arg)
{
struct dpaa2_io_softc *sc = (struct dpaa2_io_softc *) arg;
struct dpaa2_io_notif_ctx *ctx[DPIO_POLL_MAX];
struct dpaa2_channel *chan;
struct dpaa2_dq dq;
uint32_t idx, status;
uint16_t flags;
int rc, cdan_n = 0;
status = dpaa2_swp_read_intr_status(sc->swp);
if (status == 0) {
return;
}
DPAA2_SWP_LOCK(sc->swp, &flags);
if (flags & DPAA2_SWP_DESTROYED) {
DPAA2_SWP_UNLOCK(sc->swp);
return;
}
for (int i = 0; i < DPIO_POLL_MAX; i++) {
rc = dpaa2_swp_dqrr_next_locked(sc->swp, &dq, &idx);
if (rc) {
break;
}
if ((dq.common.verb & DPAA2_DQRR_RESULT_MASK) ==
DPAA2_DQRR_RESULT_CDAN) {
ctx[cdan_n++] = (struct dpaa2_io_notif_ctx *) dq.scn.ctx;
} else {
}
dpaa2_swp_write_reg(sc->swp, DPAA2_SWP_CINH_DCAP, idx);
}
DPAA2_SWP_UNLOCK(sc->swp);
for (int i = 0; i < cdan_n; i++) {
chan = (struct dpaa2_channel *)ctx[i]->channel;
taskqueue_enqueue(chan->cleanup_tq, &chan->cleanup_task);
}
dpaa2_swp_clear_intr_status(sc->swp, status);
dpaa2_swp_write_reg(sc->swp, DPAA2_SWP_CINH_IIR, 0);
}
static device_method_t dpaa2_io_methods[] = {
DEVMETHOD(device_probe, dpaa2_io_probe),
DEVMETHOD(device_attach, dpaa2_io_attach),
DEVMETHOD(device_detach, dpaa2_io_detach),
DEVMETHOD(dpaa2_swp_enq_multiple_fq, dpaa2_io_enq_multiple_fq),
DEVMETHOD(dpaa2_swp_conf_wq_channel, dpaa2_io_conf_wq_channel),
DEVMETHOD(dpaa2_swp_query_bp, dpaa2_io_query_bp),
DEVMETHOD(dpaa2_swp_release_bufs, dpaa2_io_release_bufs),
DEVMETHOD_END
};
static driver_t dpaa2_io_driver = {
"dpaa2_io",
dpaa2_io_methods,
sizeof(struct dpaa2_io_softc),
};
DRIVER_MODULE(dpaa2_io, dpaa2_rc, dpaa2_io_driver, 0, 0);