#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/bus.h>
#include <sys/lock.h>
#include <sys/malloc.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/bus.h>
#include <machine/tlb.h>
#include "bman.h"
#include "dpaa_common.h"
#include "bman_var.h"
#define BMAN_POOL_SWDET(n) (0x000 + 4 * (n))
#define BMAN_POOL_HWDET(n) (0x100 + 4 * (n))
#define BMAN_POOL_SWDXT(n) (0x200 + 4 * (n))
#define BMAN_POOL_HWDXT(n) (0x300 + 4 * (n))
#define FBPR_FP_LWIT 0x804
#define BMAN_IP_REV_1 0x0bf8
#define IP_MAJ_S 8
#define IP_MAJ_M 0x0000ff00
#define IP_MIN_M 0x000000ff
#define BMAN_IP_REV_2 0x0bfc
#define BMAN_FBPR_BARE 0x0c00
#define BMAN_FBPR_BAR 0x0c04
#define BMAN_FBPR_AR 0x0c10
#define BMAN_LIODNR 0x0d08
#define BMAN_POOL_CONTENT(n) (0x0600 + 4 * (n))
#define BMAN_ECSR 0x0a00
#define BMAN_ECIR 0x0a04
#define ECIR_PORTAL(r) (((r) >> 24) & 0x0f)
#define ECIR_VERB(r) (((r) >> 16) & 0x07)
#define ECIR_R 0x00080000
#define ECIR_POOL(r) ((r) & 0x3f)
#define BMAN_CECR 0x0a34
#define BMAN_CEAR 0x0a38
#define BMAN_AECR 0x0a34
#define BMAN_AEAR 0x0a38
#define BMAN_ERR_ISR 0x0e00
#define BMAN_ERR_IER 0x0e04
#define BMAN_ERR_ISDR 0x0e08
#define ERR_EMAI 0x00000040
#define ERR_EMCI 0x00000020
#define ERR_IVCI 0x00000010
#define ERR_FLWI 0x00000008
#define ERR_MBEI 0x00000004
#define ERR_SBEI 0x00000002
#define ERR_BSCN 0x00000001
static MALLOC_DEFINE(M_BMAN, "bman", "DPAA Buffer Manager structures");
static struct bman_softc *bman_sc;
static void
bman_isr(void *arg)
{
struct bman_softc *sc = arg;
uint32_t ier, isr, isr_bit;
uint32_t reg;
ier = bus_read_4(sc->sc_rres, BMAN_ERR_IER);
isr = bus_read_4(sc->sc_rres, BMAN_ERR_ISR);
isr_bit = (isr & ier);
if (isr_bit == 0)
goto end;
if (isr_bit & ERR_EMAI) {
device_printf(sc->sc_dev, "External memory access error\n");
reg = bus_read_4(sc->sc_rres, BMAN_AECR);
if (reg <= 63)
device_printf(sc->sc_dev, " pool %d\n", reg);
else
device_printf(sc->sc_dev, " FBPR free list\n");
reg = bus_read_4(sc->sc_rres, BMAN_AEAR);
device_printf(sc->sc_dev, " offset: %#x\n", reg);
}
if (isr_bit & ERR_EMCI) {
device_printf(sc->sc_dev, "External memory corruption error\n");
reg = bus_read_4(sc->sc_rres, BMAN_CECR);
if (reg <= 63)
device_printf(sc->sc_dev, " pool %d\n", reg);
else
device_printf(sc->sc_dev, " FBPR free list\n");
reg = bus_read_4(sc->sc_rres, BMAN_CEAR);
device_printf(sc->sc_dev, " offset: %#x\n", reg);
}
if (isr_bit & ERR_IVCI) {
reg = bus_read_4(sc->sc_rres, BMAN_ECIR);
device_printf(sc->sc_dev, "Invalid verb command\n");
device_printf(sc->sc_dev, "Portal: %d, ring: %s\n",
ECIR_POOL(reg), (reg & ECIR_R) ? "RCR" : "Command");
device_printf(sc->sc_dev, "verb: 0x%02x, pool: %d\n",
ECIR_VERB(reg), ECIR_POOL(reg));
}
if (isr_bit & (ERR_MBEI | ERR_SBEI)) {
if (isr_bit & ERR_MBEI)
device_printf(sc->sc_dev, "Multi-bit ECC error\n");
if (isr_bit & ERR_MBEI)
device_printf(sc->sc_dev, "Single-bit ECC error\n");
}
end:
bus_write_4(sc->sc_rres, BMAN_ERR_ISR, isr);
}
static void
bman_get_version(struct bman_softc *sc)
{
uint32_t reg = bus_read_4(sc->sc_rres, BMAN_IP_REV_1);
sc->sc_major = (reg & IP_MAJ_M) >> IP_MAJ_S;
sc->sc_minor = (reg & IP_MIN_M);
}
static int
bman_set_memory(struct bman_softc *sc, vm_paddr_t pa, vm_size_t size)
{
vm_paddr_t bar_pa;
if ((pa & (size - 1)) != 0 || (size & (size - 1)) != 0) {
device_printf(sc->sc_dev,
"invalid memory configuration: pa: %#jx, size: %#jx\n",
(uintmax_t)pa, (uintmax_t)size);
return (ENXIO);
}
bar_pa = bus_read_4(sc->sc_rres, BMAN_FBPR_BARE);
bar_pa <<= 32;
bar_pa |= bus_read_4(sc->sc_rres, BMAN_FBPR_BAR);
if (bar_pa != 0 && bar_pa != pa) {
device_printf(sc->sc_dev,
"attempted to reinitialize BMan with different BAR\n");
return (ENOMEM);
} else if (bar_pa == pa)
return (0);
bus_write_4(sc->sc_rres, BMAN_FBPR_BARE, pa >> 32);
bus_write_4(sc->sc_rres, BMAN_FBPR_BAR, pa & 0xffffffff);
bus_write_4(sc->sc_rres, BMAN_FBPR_AR, ilog2(size) - 1);
return (0);
}
int
bman_attach(device_t dev)
{
struct bman_softc *sc;
vm_paddr_t bp_pa;
size_t bp_size;
int bp_count;
sc = device_get_softc(dev);
sc->sc_dev = dev;
bman_sc = sc;
sc->sc_rrid = 0;
sc->sc_rres = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
sc->sc_rrid, 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;
bman_get_version(sc);
if (sc->sc_major == 2 && sc->sc_minor == 0)
bp_count = BMAN_MAX_POOLS_1023;
else
bp_count = BMAN_MAX_POOLS;
bus_write_4(sc->sc_rres, BMAN_LIODNR, 0);
sc->sc_vmem = vmem_create("BMan Pools", 0, bp_count, 1, 0, M_WAITOK);
dpaa_map_private_memory(dev, 0, "fsl,bman-fbpr", &bp_pa, &bp_size);
bman_set_memory(sc, bp_pa, bp_size);
bus_write_4(sc->sc_rres, FBPR_FP_LWIT, (bp_size / 8) / 20);
bus_write_4(sc->sc_rres, BMAN_ERR_ISR, 0xffffffff);
bus_write_4(sc->sc_rres, BMAN_ERR_IER, 0xffffffff);
bus_write_4(sc->sc_rres, BMAN_ERR_ISDR, 0);
if (bus_setup_intr(dev, sc->sc_ires, INTR_TYPE_NET, NULL, bman_isr,
sc, &sc->sc_icookie) != 0)
goto err;
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_vmem != NULL)
vmem_destroy(sc->sc_vmem);
if (sc->sc_icookie != NULL)
bus_teardown_intr(dev, sc->sc_ires, sc->sc_icookie);
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);
}
struct bman_pool *
bman_new_pool(void)
{
struct bman_softc *sc;
vmem_addr_t bpid;
struct bman_pool *pool;
sc = bman_sc;
pool = NULL;
if (vmem_alloc(sc->sc_vmem, 1, M_FIRSTFIT | M_NOWAIT, &bpid) != 0)
return (NULL);
pool = malloc(sizeof(*pool), M_BMAN, M_WAITOK | M_ZERO);
pool->bpid = bpid;
return (pool);
}
struct bman_pool *
bman_pool_create(uint8_t *bpid, uint16_t buffer_size, uint16_t max_buffers,
uint32_t dep_sw_entry, uint32_t dep_sw_exit,
uint32_t dep_hw_entry, uint32_t dep_hw_exit,
bm_depletion_handler dep_cb, void *arg)
{
struct bman_softc *sc;
struct bman_pool *bp;
sc = bman_sc;
bp = bman_new_pool();
if (bpid != NULL)
*bpid = bp->bpid;
if (dep_cb) {
bp->dep_cb = dep_cb;
bus_write_4(sc->sc_rres, BMAN_POOL_SWDET(bp->bpid),
dep_sw_entry);
bus_write_4(sc->sc_rres, BMAN_POOL_SWDXT(bp->bpid),
dep_sw_exit);
bus_write_4(sc->sc_rres, BMAN_POOL_HWDET(bp->bpid),
dep_hw_entry);
bus_write_4(sc->sc_rres, BMAN_POOL_HWDXT(bp->bpid),
dep_hw_exit);
bp->arg = arg;
bman_portal_enable_scn(DPCPU_GET(bman_affine_portal), bp);
}
return (bp);
}
int
bman_pool_destroy(struct bman_pool *pool)
{
if (bman_count(pool) != 0)
return (EBUSY);
vmem_free(bman_sc->sc_vmem, pool->bpid, 1);
free(pool, M_BMAN);
return (0);
}
int
bman_put_buffers(struct bman_pool *pool, struct bman_buffer *buffers, int count)
{
struct bman_portal_softc *portal;
int error;
critical_enter();
portal = DPCPU_GET(bman_affine_portal);
if (portal == NULL) {
critical_exit();
return (EIO);
}
while (count > 0) {
int c = min(count, 8);
error = bman_release(pool, buffers, c);
buffers += c;
count -= c;
}
critical_exit();
return (error);
}
uint32_t
bman_get_bpid(struct bman_pool *pool)
{
return (pool->bpid);
}
uint32_t
bman_count(struct bman_pool *pool)
{
return (bus_read_4(bman_sc->sc_rres, BMAN_POOL_CONTENT(pool->bpid)));
}