#include <sys/cdefs.h>
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/errno.h>
#include <sys/rman.h>
#include <sys/bus.h>
#include <sys/systm.h>
#include <sys/slicer.h>
#include <machine/bus.h>
#include <dev/bhnd/bhnd_debug.h>
#include "chipc_slicer.h"
#include <dev/cfi/cfi_var.h>
#include "chipc_spi.h"
static int chipc_slicer_walk(device_t dev, struct resource *res,
struct flash_slice *slices, int *nslices);
void
chipc_register_slicer(chipc_flash flash_type)
{
switch (flash_type) {
case CHIPC_SFLASH_AT:
case CHIPC_SFLASH_ST:
flash_register_slicer(chipc_slicer_spi, FLASH_SLICES_TYPE_SPI,
TRUE);
break;
case CHIPC_PFLASH_CFI:
flash_register_slicer(chipc_slicer_cfi, FLASH_SLICES_TYPE_CFI,
TRUE);
break;
default:
break;
}
}
int
chipc_slicer_cfi(device_t dev, const char *provider __unused,
struct flash_slice *slices, int *nslices)
{
struct cfi_softc *sc;
device_t parent;
if (device_get_devclass(dev) != devclass_find("cfi"))
return (ENXIO);
if ((parent = device_get_parent(dev)) == NULL) {
BHND_ERROR_DEV(dev, "no found ChipCommon device");
return (ENXIO);
}
if (device_get_devclass(parent) != devclass_find("bhnd_chipc")) {
BHND_ERROR_DEV(dev, "no found ChipCommon device");
return (ENXIO);
}
sc = device_get_softc(dev);
return (chipc_slicer_walk(dev, sc->sc_res, slices, nslices));
}
int
chipc_slicer_spi(device_t dev, const char *provider __unused,
struct flash_slice *slices, int *nslices)
{
struct chipc_spi_softc *sc;
device_t chipc, spi, spibus;
BHND_DEBUG_DEV(dev, "initting SPI slicer: %s", device_get_name(dev));
spibus = device_get_parent(dev);
if (spibus == NULL) {
BHND_ERROR_DEV(dev, "no found ChipCommon SPI BUS device");
return (ENXIO);
}
spi = device_get_parent(spibus);
if (spi == NULL) {
BHND_ERROR_DEV(dev, "no found ChipCommon SPI device");
return (ENXIO);
}
chipc = device_get_parent(spi);
if (device_get_devclass(chipc) != devclass_find("bhnd_chipc")) {
BHND_ERROR_DEV(dev, "no found ChipCommon device");
return (ENXIO);
}
sc = device_get_softc(spi);
return (chipc_slicer_walk(dev, sc->sc_flash_res, slices, nslices));
}
static int
chipc_slicer_walk(device_t dev, struct resource *res,
struct flash_slice *slices, int *nslices)
{
uint32_t fw_len;
uint32_t fs_ofs;
uint32_t val;
uint32_t ofs_trx;
int flash_size;
*nslices = 0;
flash_size = rman_get_size(res);
ofs_trx = flash_size;
BHND_TRACE_DEV(dev, "slicer: scanning memory [%x bytes] for headers...",
flash_size);
for(uint32_t ofs = 0; ofs < flash_size; ofs+= 0x1000){
val = bus_read_4(res, ofs);
switch (val) {
case TRX_MAGIC:
if (ofs_trx < ofs) {
BHND_TRACE_DEV(dev, "stop on 2nd TRX: %x", ofs);
break;
}
BHND_TRACE("TRX found: %x", ofs);
ofs_trx = ofs;
fs_ofs = bus_read_4(res, ofs + 24);
BHND_TRACE("FS offset: %x", fs_ofs);
if (fs_ofs % 0x200 != 0) {
BHND_WARN("WARNING! filesystem offset should be"
" aligned on sector size (%d bytes)", 0x200);
BHND_WARN("ignoring TRX firmware image");
break;
}
slices[*nslices].base = ofs + fs_ofs;
fw_len = bus_read_4(res, ofs + 4);
slices[*nslices].size = fw_len - fs_ofs;
slices[*nslices].label = "rootfs";
*nslices += 1;
break;
case CFE_MAGIC:
BHND_TRACE("CFE found: %x", ofs);
break;
case NVRAM_MAGIC:
BHND_TRACE("NVRAM found: %x", ofs);
break;
default:
break;
}
}
BHND_TRACE("slicer: done");
return (0);
}