#include <sys/param.h>
#include <sys/kernel.h>
#include "chipc_private.h"
#include "chipcvar.h"
const char *
chipc_flash_name(chipc_flash type)
{
switch (type) {
case CHIPC_PFLASH_CFI:
return ("CFI Flash");
case CHIPC_SFLASH_ST:
case CHIPC_SFLASH_AT:
return ("SPI Flash");
case CHIPC_QSFLASH_ST:
case CHIPC_QSFLASH_AT:
return ("QSPI Flash");
case CHIPC_NFLASH:
case CHIPC_NFLASH_4706:
return ("NAND");
case CHIPC_FLASH_NONE:
default:
return ("unknown");
}
}
const char *
chipc_flash_bus_name(chipc_flash type)
{
switch (type) {
case CHIPC_PFLASH_CFI:
return ("cfi");
case CHIPC_SFLASH_ST:
case CHIPC_SFLASH_AT:
return ("spi");
case CHIPC_QSFLASH_ST:
case CHIPC_QSFLASH_AT:
return (NULL);
case CHIPC_NFLASH:
case CHIPC_NFLASH_4706:
return (NULL);
case CHIPC_FLASH_NONE:
default:
return (NULL);
}
}
const char *
chipc_sflash_device_name(chipc_flash type)
{
switch (type) {
case CHIPC_SFLASH_ST:
return ("mx25l");
case CHIPC_SFLASH_AT:
return ("at45d");
case CHIPC_QSFLASH_ST:
case CHIPC_QSFLASH_AT:
return (NULL);
case CHIPC_PFLASH_CFI:
case CHIPC_NFLASH:
case CHIPC_NFLASH_4706:
case CHIPC_FLASH_NONE:
default:
return (NULL);
}
}
int
chipc_init_child_resource(struct resource *r,
struct resource *parent, bhnd_size_t offset, bhnd_size_t size)
{
bus_space_handle_t bh, child_bh;
bus_space_tag_t bt;
uintptr_t vaddr;
int error;
vaddr = (uintptr_t) rman_get_virtual(parent);
bt = rman_get_bustag(parent);
bh = rman_get_bushandle(parent);
vaddr += offset;
error = bus_space_subregion(bt, bh, offset, size, &child_bh);
if (error)
return (error);
rman_set_virtual(r, (void *) vaddr);
rman_set_bustag(r, bt);
rman_set_bushandle(r, child_bh);
return (0);
}
int
chipc_set_irq_resource(struct chipc_softc *sc, device_t child, int rid,
u_int intr)
{
struct chipc_devinfo *dinfo;
int error;
KASSERT(device_get_parent(child) == sc->dev, ("not a direct child"));
dinfo = device_get_ivars(child);
if (dinfo->irq_mapped) {
device_printf(sc->dev, "irq already mapped for child\n");
return (ENOMEM);
}
if ((error = bhnd_map_intr(sc->dev, intr, &dinfo->irq))) {
device_printf(sc->dev, "failed to map intr %u: %d\n", intr,
error);
return (error);
}
dinfo->irq_mapped = true;
error = bus_set_resource(child, SYS_RES_IRQ, rid, dinfo->irq, 1);
if (error) {
device_printf(sc->dev, "failed to set child irq resource %d to "
"%ju: %d\n", rid, dinfo->irq, error);
bhnd_unmap_intr(sc->dev, dinfo->irq);
return (error);
}
return (0);
}
int
chipc_set_mem_resource(struct chipc_softc *sc, device_t child, int rid,
rman_res_t start, rman_res_t count, u_int port, u_int region)
{
bhnd_addr_t region_addr;
bhnd_size_t region_size;
bool isdefault;
int error;
KASSERT(device_get_parent(child) == sc->dev, ("not a direct child"));
isdefault = RMAN_IS_DEFAULT_RANGE(start, count);
error = bhnd_get_region_addr(sc->dev, BHND_PORT_DEVICE, port,
region, ®ion_addr, ®ion_size);
if (error) {
device_printf(sc->dev,
"lookup of %s%u.%u failed: %d\n",
bhnd_port_type_name(BHND_PORT_DEVICE), port, region, error);
return (error);
}
if (isdefault) {
start = 0;
count = region_size;
}
if (start > region_size || region_size - start < count) {
device_printf(sc->dev,
"%s%u.%u region cannot map requested range %#jx+%#jx\n",
bhnd_port_type_name(BHND_PORT_DEVICE), port, region, start,
count);
return (ERANGE);
}
return (bus_set_resource(child, SYS_RES_MEMORY, rid,
region_addr + start, count));
}
void
chipc_print_caps(device_t dev, struct chipc_caps *caps)
{
#define CC_TFS(_flag) (caps->_flag ? "yes" : "no")
device_printf(dev, "MIPSEB: %-3s | BP64: %s\n",
CC_TFS(mipseb), CC_TFS(backplane_64));
device_printf(dev, "UARTs: %-3hhu | UGPIO: %s\n",
caps->num_uarts, CC_TFS(uart_gpio));
device_printf(dev, "UARTClk: 0x%02x | Flash: %u\n",
caps->uart_clock, caps->flash_type);
device_printf(dev, "SPROM: %-3s | OTP: %s\n",
CC_TFS(sprom), CC_TFS(otp_size));
device_printf(dev, "CFIsz: 0x%02x | OTPsz: 0x%02x\n",
caps->cfi_width, caps->otp_size);
device_printf(dev, "ExtBus: 0x%02x | PwrCtrl: %s\n",
caps->extbus_type, CC_TFS(pwr_ctrl));
device_printf(dev, "PLL: 0x%02x | JTAGM: %s\n",
caps->pll_type, CC_TFS(jtag_master));
device_printf(dev, "PMU: %-3s | ECI: %s\n",
CC_TFS(pmu), CC_TFS(eci));
device_printf(dev, "SECI: %-3s | GSIO: %s\n",
CC_TFS(seci), CC_TFS(gsio));
device_printf(dev, "AOB: %-3s | BootROM: %s\n",
CC_TFS(aob), CC_TFS(boot_rom));
#undef CC_TFS
}
struct chipc_region *
chipc_alloc_region(struct chipc_softc *sc, bhnd_port_type type,
u_int port, u_int region)
{
struct chipc_region *cr;
int error;
if (!bhnd_is_region_valid(sc->dev, type, port, region))
return (NULL);
cr = malloc(sizeof(*cr), M_BHND, M_NOWAIT);
if (cr == NULL)
return (NULL);
cr->cr_port_type = type;
cr->cr_port_num = port;
cr->cr_region_num = region;
cr->cr_res = NULL;
cr->cr_refs = 0;
cr->cr_act_refs = 0;
error = bhnd_get_region_addr(sc->dev, type, port, region, &cr->cr_addr,
&cr->cr_count);
if (error) {
device_printf(sc->dev,
"fetching chipc region address failed: %d\n", error);
goto failed;
}
cr->cr_end = cr->cr_addr + cr->cr_count - 1;
cr->cr_rid = bhnd_get_port_rid(sc->dev, type, port, region);
return (cr);
failed:
device_printf(sc->dev, "chipc region alloc failed for %s%u.%u\n",
bhnd_port_type_name(type), port, region);
free(cr, M_BHND);
return (NULL);
}
void
chipc_free_region(struct chipc_softc *sc, struct chipc_region *cr)
{
KASSERT(cr->cr_refs == 0,
("chipc %s%u.%u region has %u active references",
bhnd_port_type_name(cr->cr_port_type), cr->cr_port_num,
cr->cr_region_num, cr->cr_refs));
if (cr->cr_res != NULL) {
bhnd_release_resource(sc->dev, cr->cr_res);
}
free(cr, M_BHND);
}
struct chipc_region *
chipc_find_region(struct chipc_softc *sc, rman_res_t start, rman_res_t end)
{
struct chipc_region *cr;
if (start > end)
return (NULL);
STAILQ_FOREACH(cr, &sc->mem_regions, cr_link) {
if (start < cr->cr_addr || end > cr->cr_end)
continue;
return (cr);
}
return (NULL);
}
struct chipc_region *
chipc_find_region_by_rid(struct chipc_softc *sc, int rid)
{
struct chipc_region *cr;
int port_rid;
STAILQ_FOREACH(cr, &sc->mem_regions, cr_link) {
port_rid = bhnd_get_port_rid(sc->dev, cr->cr_port_type,
cr->cr_port_num, cr->cr_region_num);
if (port_rid == -1 || port_rid != rid)
continue;
return (cr);
}
return (NULL);
}
int
chipc_retain_region(struct chipc_softc *sc, struct chipc_region *cr, int flags)
{
int error;
KASSERT(!(flags &~ (RF_ACTIVE|RF_ALLOCATED)), ("unsupported flags"));
CHIPC_LOCK(sc);
if (flags & RF_ALLOCATED) {
if (cr->cr_refs == 0) {
KASSERT(cr->cr_res == NULL,
("non-NULL resource has refcount"));
if (cr->cr_rid == -1) {
CHIPC_UNLOCK(sc);
return (EINVAL);
}
cr->cr_res = bhnd_alloc_resource(sc->dev,
SYS_RES_MEMORY, cr->cr_rid, cr->cr_addr,
cr->cr_end, cr->cr_count, RF_SHAREABLE);
if (cr->cr_res == NULL) {
CHIPC_UNLOCK(sc);
return (ENXIO);
}
}
cr->cr_refs++;
}
if (flags & RF_ACTIVE) {
KASSERT(cr->cr_refs > 0,
("cannot activate unallocated resource"));
if (cr->cr_act_refs == 0) {
error = bhnd_activate_resource(sc->dev, cr->cr_res);
if (error) {
CHIPC_UNLOCK(sc);
chipc_release_region(sc, cr,
flags &~ RF_ACTIVE);
return (error);
}
}
cr->cr_act_refs++;
}
CHIPC_UNLOCK(sc);
return (0);
}
int
chipc_release_region(struct chipc_softc *sc, struct chipc_region *cr,
int flags)
{
int error;
CHIPC_LOCK(sc);
error = 0;
KASSERT(cr->cr_res != NULL, ("release on NULL region resource"));
if (flags & RF_ACTIVE) {
KASSERT(cr->cr_act_refs > 0, ("RF_ACTIVE over-released"));
KASSERT(cr->cr_act_refs <= cr->cr_refs,
("RF_ALLOCATED released with RF_ACTIVE held"));
if (cr->cr_act_refs == 1) {
error = bhnd_deactivate_resource(sc->dev, cr->cr_res);
if (error)
goto done;
}
cr->cr_act_refs--;
}
if (flags & RF_ALLOCATED) {
KASSERT(cr->cr_refs > 0, ("overrelease of refs"));
if (cr->cr_refs == 1) {
error = bhnd_release_resource(sc->dev, cr->cr_res);
if (error)
goto done;
cr->cr_res = NULL;
}
cr->cr_refs--;
}
done:
CHIPC_UNLOCK(sc);
return (error);
}