#include <sys/cdefs.h>
#include "opt_platform.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bio.h>
#include <sys/endian.h>
#include <sys/kernel.h>
#include <sys/kthread.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/module.h>
#include <sys/mutex.h>
#include <sys/rman.h>
#include <geom/geom_disk.h>
#include <machine/bus.h>
#include <dev/clk/clk.h>
#include <dev/fdt/fdt_common.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <vm/pmap.h>
#include "flex_spi.h"
static MALLOC_DEFINE(SECTOR_BUFFER, "flex_spi", "FSL QSPI sector buffer memory");
#define AHB_LUT_ID 31
#define MHZ(x) ((x)*1000*1000)
#define SPI_DEFAULT_CLK_RATE (MHZ(10))
static int driver_flags = 0;
SYSCTL_NODE(_hw, OID_AUTO, flex_spi, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
"FlexSPI driver parameters");
SYSCTL_INT(_hw_flex_spi, OID_AUTO, driver_flags, CTLFLAG_RDTUN, &driver_flags, 0,
"Configuration flags and quirks");
static struct ofw_compat_data flex_spi_compat_data[] = {
{"nxp,lx2160a-fspi", true},
{NULL, false}
};
struct flex_spi_flash_info {
char* name;
uint32_t jedecid;
uint32_t sectorsize;
uint32_t sectorcount;
uint32_t erasesize;
uint32_t maxclk;
};
static struct flex_spi_flash_info flex_spi_flash_info[] = {
{"W25Q128JW", 0x001860ef, 64*1024, 256, 4096, MHZ(100)},
{NULL, 0, 0, 0, 0, 0}
};
struct flex_spi_softc
{
device_t dev;
unsigned int flags;
struct bio_queue_head bio_queue;
struct mtx disk_mtx;
struct disk *disk;
struct proc *p;
unsigned int taskstate;
uint8_t *buf;
struct resource *ahb_mem_res;
struct resource *mem_res;
clk_t fspi_clk_en;
clk_t fspi_clk;
uint64_t fspi_clk_en_hz;
uint64_t fspi_clk_hz;
uint64_t fspi_max_clk;
uint32_t quirks;
uint32_t sectorsize;
uint32_t sectorcount;
uint32_t erasesize;
};
static int flex_spi_read(struct flex_spi_softc *sc, off_t offset, caddr_t data,
size_t count);
static int flex_spi_write(struct flex_spi_softc *sc, off_t offset,
uint8_t *data, size_t size);
static int flex_spi_attach(device_t dev);
static int flex_spi_probe(device_t dev);
static int flex_spi_detach(device_t dev);
static int flex_spi_open(struct disk *dp);
static int flex_spi_close(struct disk *dp);
static int flex_spi_ioctl(struct disk *, u_long, void *, int, struct thread *);
static void flex_spi_strategy(struct bio *bp);
static int flex_spi_getattr(struct bio *bp);
static void flex_spi_task(void *arg);
static uint32_t
read_reg(struct flex_spi_softc *sc, uint32_t offset)
{
return ((bus_read_4(sc->mem_res, offset)));
}
static void
write_reg(struct flex_spi_softc *sc, uint32_t offset, uint32_t value)
{
bus_write_4(sc->mem_res, offset, (value));
}
static int
reg_read_poll_tout(struct flex_spi_softc *sc, uint32_t offset, uint32_t mask,
uint32_t delay_us, uint32_t iterations, bool positive)
{
uint32_t reg;
uint32_t condition = 0;
do {
reg = read_reg(sc, offset);
if (positive)
condition = ((reg & mask) == 0);
else
condition = ((reg & mask) != 0);
if (condition == 0)
break;
DELAY(delay_us);
} while (condition && (--iterations > 0));
return (condition != 0);
}
static int
flex_spi_clk_setup(struct flex_spi_softc *sc, uint32_t rate)
{
int ret = 0;
ret |= clk_disable(sc->fspi_clk_en);
ret |= clk_disable(sc->fspi_clk);
ret |= clk_set_freq(sc->fspi_clk, rate, 0);
sc->fspi_clk_hz = rate;
ret |= clk_enable(sc->fspi_clk_en);
ret |= clk_enable(sc->fspi_clk);
if (ret)
return (EINVAL);
return (0);
}
static void
flex_spi_prepare_lut(struct flex_spi_softc *sc, uint8_t op)
{
uint32_t lut_id;
uint32_t lut;
write_reg(sc, FSPI_LUTKEY, FSPI_LUTKEY_VALUE);
write_reg(sc, FSPI_LCKCR, FSPI_LCKER_UNLOCK);
lut_id = 0;
switch (op) {
case LUT_FLASH_CMD_JEDECID:
lut = LUT_DEF(0, LUT_CMD, LUT_PAD(1), FSPI_CMD_READ_IDENT);
lut |= LUT_DEF(1, LUT_NXP_READ, LUT_PAD(1), 0);
write_reg(sc, FSPI_LUT_REG(lut_id), lut);
write_reg(sc, FSPI_LUT_REG(lut_id) + 4, 0);
break;
case LUT_FLASH_CMD_READ:
lut = LUT_DEF(0, LUT_CMD, LUT_PAD(1), FSPI_CMD_FAST_READ);
lut |= LUT_DEF(1, LUT_ADDR, LUT_PAD(1), 3*8);
write_reg(sc, FSPI_LUT_REG(lut_id), lut);
lut = LUT_DEF(0, LUT_DUMMY, LUT_PAD(1), 1*8);
lut |= LUT_DEF(1, LUT_NXP_READ, LUT_PAD(1), 0);
write_reg(sc, FSPI_LUT_REG(lut_id) + 4, lut);
write_reg(sc, FSPI_LUT_REG(lut_id) + 8, 0);
break;
case LUT_FLASH_CMD_STATUS_READ:
lut = LUT_DEF(0, LUT_CMD, LUT_PAD(1), FSPI_CMD_READ_STATUS);
lut |= LUT_DEF(1, LUT_NXP_READ, LUT_PAD(1), 0);
write_reg(sc, FSPI_LUT_REG(lut_id), lut);
write_reg(sc, FSPI_LUT_REG(lut_id) + 4, 0);
break;
case LUT_FLASH_CMD_PAGE_PROGRAM:
lut = LUT_DEF(0, LUT_CMD, LUT_PAD(1), FSPI_CMD_PAGE_PROGRAM);
lut |= LUT_DEF(1, LUT_ADDR, LUT_PAD(1), 3*8);
write_reg(sc, FSPI_LUT_REG(lut_id), lut);
lut = LUT_DEF(0, LUT_NXP_WRITE, LUT_PAD(1), 0);
write_reg(sc, FSPI_LUT_REG(lut_id) + 4, lut);
write_reg(sc, FSPI_LUT_REG(lut_id) + 8, 0);
break;
case LUT_FLASH_CMD_WRITE_ENABLE:
lut = LUT_DEF(0, LUT_CMD, LUT_PAD(1), FSPI_CMD_WRITE_ENABLE);
write_reg(sc, FSPI_LUT_REG(lut_id), lut);
write_reg(sc, FSPI_LUT_REG(lut_id) + 4, 0);
break;
case LUT_FLASH_CMD_WRITE_DISABLE:
lut = LUT_DEF(0, LUT_CMD, LUT_PAD(1), FSPI_CMD_WRITE_DISABLE);
write_reg(sc, FSPI_LUT_REG(lut_id), lut);
write_reg(sc, FSPI_LUT_REG(lut_id) + 4, 0);
break;
case LUT_FLASH_CMD_SECTOR_ERASE:
lut = LUT_DEF(0, LUT_CMD, LUT_PAD(1), FSPI_CMD_SECTOR_ERASE);
lut |= LUT_DEF(1, LUT_ADDR, LUT_PAD(1), 3*8);
write_reg(sc, FSPI_LUT_REG(lut_id), lut);
write_reg(sc, FSPI_LUT_REG(lut_id) + 4, 0);
break;
default:
write_reg(sc, FSPI_LUT_REG(lut_id), 0);
}
write_reg(sc, FSPI_LUTKEY, FSPI_LUTKEY_VALUE);
write_reg(sc, FSPI_LCKCR, FSPI_LCKER_LOCK);
}
static void
flex_spi_prepare_ahb_lut(struct flex_spi_softc *sc)
{
uint32_t lut_id;
uint32_t lut;
write_reg(sc, FSPI_LUTKEY, FSPI_LUTKEY_VALUE);
write_reg(sc, FSPI_LCKCR, FSPI_LCKER_UNLOCK);
lut_id = AHB_LUT_ID;
lut = LUT_DEF(0, LUT_CMD, LUT_PAD(1), FSPI_CMD_FAST_READ);
lut |= LUT_DEF(1, LUT_ADDR, LUT_PAD(1), 3*8);
write_reg(sc, FSPI_LUT_REG(lut_id), lut);
lut = LUT_DEF(0, LUT_DUMMY, LUT_PAD(1), 1*8);
lut |= LUT_DEF(1, LUT_NXP_READ, LUT_PAD(1), 0);
write_reg(sc, FSPI_LUT_REG(lut_id) + 4, lut);
write_reg(sc, FSPI_LUT_REG(lut_id) + 8, 0);
write_reg(sc, FSPI_LUTKEY, FSPI_LUTKEY_VALUE);
write_reg(sc, FSPI_LCKCR, FSPI_LCKER_LOCK);
}
#define DIR_READ 0
#define DIR_WRITE 1
static void
flex_spi_read_rxfifo(struct flex_spi_softc *sc, uint8_t *buf, uint8_t size)
{
int i, ret, reg;
for (i = 0; i < size; i += 4) {
if (i % 8 == 0) {
ret = reg_read_poll_tout(sc, FSPI_INTR, FSPI_INTR_IPRXWA,
1, 50000, 1);
if (ret)
device_printf(sc->dev,
"timed out waiting for FSPI_INTR_IPRXWA\n");
}
if (i % 8 == 0)
reg = read_reg(sc, FSPI_RFDR);
else
reg = read_reg(sc, FSPI_RFDR + 4);
if (size >= (i + 4))
*(uint32_t *)(buf + i) = reg;
else
memcpy(buf + i, ®, size - i);
if (i % 8 != 0)
write_reg(sc, FSPI_INTR, FSPI_INTR_IPRXWA);
}
write_reg(sc, FSPI_IPRXFCR, FSPI_IPRXFCR_CLR);
write_reg(sc, FSPI_INTR, FSPI_INTR_IPRXWA);
}
static void
flex_spi_write_txfifo(struct flex_spi_softc *sc, uint8_t *buf, uint8_t size)
{
int i, ret, reg;
write_reg(sc, FSPI_IPTXFCR, FSPI_IPTXFCR_CLR);
for (i = 0; i < size; i += 4) {
if (i % 8 == 0) {
ret = reg_read_poll_tout(sc, FSPI_INTR, FSPI_INTR_IPTXWE,
1, 50000, 1);
if (ret)
device_printf(sc->dev,
"timed out waiting for FSPI_INTR_IPTXWE\n");
}
if (size >= (i + 4))
reg = *(uint32_t *)(buf + i);
else {
reg = 0;
memcpy(®, buf + i, size - i);
}
if (i % 8 == 0)
write_reg(sc, FSPI_TFDR, reg);
else
write_reg(sc, FSPI_TFDR + 4, reg);
if (i % 8 != 0)
write_reg(sc, FSPI_INTR, FSPI_INTR_IPTXWE);
}
write_reg(sc, FSPI_INTR, FSPI_INTR_IPTXWE);
}
static int
flex_spi_do_op(struct flex_spi_softc *sc, uint32_t op, uint32_t addr,
uint8_t *buf, uint8_t size, uint8_t dir)
{
uint32_t cnt = 1000, reg;
reg = read_reg(sc, FSPI_IPRXFCR);
reg &= ~FSPI_IPRXFCR_DMA_EN;
reg |= FSPI_IPRXFCR_CLR;
write_reg(sc, FSPI_IPRXFCR, reg);
flex_spi_prepare_lut(sc, op);
write_reg(sc, FSPI_IPCR0, addr);
write_reg(sc, FSPI_IPCR1, size |
(0 << FSPI_IPCR1_SEQID_SHIFT) | (0 << FSPI_IPCR1_SEQNUM_SHIFT));
if ((size != 0) && (dir == DIR_WRITE))
flex_spi_write_txfifo(sc, buf, size);
write_reg(sc, FSPI_IPCMD, FSPI_IPCMD_TRG);
do {
reg = read_reg(sc, FSPI_INTR);
if (reg & FSPI_INTR_IPCMDDONE) {
write_reg(sc, FSPI_INTR, FSPI_INTR_IPCMDDONE);
break;
}
DELAY(1);
} while (--cnt);
if (cnt == 0) {
device_printf(sc->dev, "timed out waiting for command completion\n");
return (ETIMEDOUT);
}
if ((size != 0) && (dir == DIR_READ))
flex_spi_read_rxfifo(sc, buf, size);
return (0);
}
static int
flex_spi_wait_for_controller(struct flex_spi_softc *sc)
{
int err;
err = reg_read_poll_tout(sc, FSPI_STS0,
FSPI_STS0_ARB_IDLE, 1, POLL_TOUT, 1);
return (err);
}
static int
flex_spi_wait_for_flash(struct flex_spi_softc *sc)
{
int ret;
uint32_t status = 0;
ret = flex_spi_wait_for_controller(sc);
if (ret != 0) {
device_printf(sc->dev, "%s: timed out waiting for controller", __func__);
return (ret);
}
do {
ret = flex_spi_do_op(sc, LUT_FLASH_CMD_STATUS_READ, 0, (void*)&status,
1, DIR_READ);
if (ret != 0) {
device_printf(sc->dev, "ERROR: failed to get flash status\n");
return (ret);
}
} while (status & STATUS_WIP);
return (0);
}
static int
flex_spi_identify(struct flex_spi_softc *sc)
{
int ret;
uint32_t id = 0;
struct flex_spi_flash_info *finfo = flex_spi_flash_info;
ret = flex_spi_do_op(sc, LUT_FLASH_CMD_JEDECID, 0, (void*)&id, sizeof(id), DIR_READ);
if (ret != 0) {
device_printf(sc->dev, "ERROR: failed to identify device\n");
return (ret);
}
while (finfo->jedecid != 0) {
if (id == finfo->jedecid) {
device_printf(sc->dev, "found %s Flash\n", finfo->name);
sc->sectorsize = finfo->sectorsize;
sc->sectorcount = finfo->sectorcount;
sc->erasesize = finfo->erasesize;
sc->fspi_max_clk = finfo->maxclk;
return (0);
}
finfo++;
}
return (EINVAL);
}
static inline int
flex_spi_force_ip_mode(struct flex_spi_softc *sc)
{
if (sc->quirks & FSPI_QUIRK_USE_IP_ONLY)
return (1);
if (driver_flags & FSPI_QUIRK_USE_IP_ONLY)
return (1);
return (0);
}
static int
flex_spi_read(struct flex_spi_softc *sc, off_t offset, caddr_t data,
size_t count)
{
int err;
size_t len;
err = flex_spi_wait_for_controller(sc);
if (err)
device_printf(sc->dev,
"warning: spi_read, timed out waiting for controller");
if (flex_spi_force_ip_mode(sc) != 0) {
do {
if (((offset % 4) != 0) || (count < 4)) {
*(uint8_t*)data = bus_read_1(sc->ahb_mem_res, offset);
data++;
count--;
offset++;
} else {
*(uint32_t*)data = bus_read_4(sc->ahb_mem_res, offset);
data += 4;
count -= 4;
offset += 4;
}
} while (count);
return (0);
}
do {
len = min(64, count);
err = flex_spi_do_op(sc, LUT_FLASH_CMD_READ, offset, (void*)data,
len, DIR_READ);
if (err)
return (err);
offset += len;
data += len;
count -= len;
} while (count);
return (0);
}
static int
flex_spi_write(struct flex_spi_softc *sc, off_t offset, uint8_t *data,
size_t size)
{
int ret = 0;
size_t ptr;
flex_spi_wait_for_flash(sc);
ret = flex_spi_do_op(sc, LUT_FLASH_CMD_WRITE_ENABLE, offset, NULL,
0, DIR_READ);
if (ret != 0) {
device_printf(sc->dev, "ERROR: failed to enable writes\n");
return (ret);
}
flex_spi_wait_for_flash(sc);
while (size > 0) {
uint32_t sector_base = rounddown2(offset, sc->erasesize);
size_t size_in_sector = size;
if (size_in_sector + offset > sector_base + sc->erasesize)
size_in_sector = sector_base + sc->erasesize - offset;
ret = flex_spi_read(sc, sector_base, sc->buf, sc->erasesize);
if (ret != 0) {
device_printf(sc->dev, "ERROR: failed to read sector %d\n",
sector_base);
goto exit;
}
flex_spi_wait_for_flash(sc);
ret = flex_spi_do_op(sc, LUT_FLASH_CMD_SECTOR_ERASE, offset, NULL,
0, DIR_READ);
if (ret != 0) {
device_printf(sc->dev, "ERROR: failed to erase sector %d\n",
sector_base);
goto exit;
}
memcpy(sc->buf + (offset - sector_base), data, size_in_sector);
for (ptr = 0; ptr < sc->erasesize; ptr += 32) {
flex_spi_wait_for_flash(sc);
ret = flex_spi_do_op(sc, LUT_FLASH_CMD_PAGE_PROGRAM,
sector_base + ptr, (void*)(sc->buf + ptr), 32, DIR_WRITE);
if (ret != 0) {
device_printf(sc->dev, "ERROR: failed to write address %ld\n",
sector_base + ptr);
goto exit;
}
}
size = size - size_in_sector;
offset = offset + size;
}
flex_spi_wait_for_flash(sc);
ret = flex_spi_do_op(sc, LUT_FLASH_CMD_WRITE_DISABLE, offset, (void*)sc->buf,
0, DIR_READ);
if (ret != 0) {
device_printf(sc->dev, "ERROR: failed to disable writes\n");
goto exit;
}
flex_spi_wait_for_flash(sc);
exit:
return (ret);
}
static int
flex_spi_default_setup(struct flex_spi_softc *sc)
{
int ret, i;
uint32_t reg;
ret = flex_spi_clk_setup(sc, SPI_DEFAULT_CLK_RATE);
if (ret)
return (ret);
reg = read_reg(sc, FSPI_MCR0);
reg |= FSPI_MCR0_SWRST;
write_reg(sc, FSPI_MCR0, reg);
ret = reg_read_poll_tout(sc, FSPI_MCR0, FSPI_MCR0_SWRST, 1000, POLL_TOUT, 0);
if (ret != 0) {
device_printf(sc->dev, "time out waiting for reset");
return (ret);
}
write_reg(sc, FSPI_MCR0, FSPI_MCR0_MDIS);
write_reg(sc, FSPI_DLLACR, FSPI_DLLACR_OVRDEN);
write_reg(sc, FSPI_DLLBCR, FSPI_DLLBCR_OVRDEN);
write_reg(sc, FSPI_MCR0, FSPI_MCR0_AHB_TIMEOUT(0xFF) |
FSPI_MCR0_IP_TIMEOUT(0xFF) | (uint32_t) FSPI_MCR0_OCTCOMB_EN);
reg = read_reg(sc, FSPI_MCR2);
reg = reg & ~(FSPI_MCR2_SAMEDEVICEEN);
write_reg(sc, FSPI_MCR2, reg);
for (i = 0; i < 7; i++)
write_reg(sc, FSPI_AHBRX_BUF0CR0 + 4 * i, 0);
write_reg(sc, FSPI_AHBRX_BUF7CR0, (2048 / 8 |
FSPI_AHBRXBUF0CR7_PREF));
write_reg(sc, FSPI_AHBCR, FSPI_AHBCR_PREF_EN | FSPI_AHBCR_RDADDROPT);
flex_spi_prepare_ahb_lut(sc);
write_reg(sc, FSPI_FLSHA1CR2, AHB_LUT_ID);
write_reg(sc, FSPI_FLSHA2CR2, AHB_LUT_ID);
write_reg(sc, FSPI_FLSHB1CR2, AHB_LUT_ID);
write_reg(sc, FSPI_FLSHB2CR2, AHB_LUT_ID);
write_reg(sc, FSPI_INTEN, 0);
return (0);
}
static int
flex_spi_probe(device_t dev)
{
if (!ofw_bus_status_okay(dev))
return (ENXIO);
if (!ofw_bus_search_compatible(dev, flex_spi_compat_data)->ocd_data)
return (ENXIO);
device_set_desc(dev, "NXP FlexSPI Flash");
return (BUS_PROBE_SPECIFIC);
}
static int
flex_spi_attach(device_t dev)
{
struct flex_spi_softc *sc;
phandle_t node;
int rid;
uint32_t reg;
node = ofw_bus_get_node(dev);
sc = device_get_softc(dev);
sc->dev = dev;
mtx_init(&sc->disk_mtx, "flex_spi_DISK", "QSPI disk mtx", MTX_DEF);
rid = 0;
sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
RF_ACTIVE);
rid = 1;
sc->ahb_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
RF_ACTIVE | RF_SHAREABLE);
if (sc->mem_res == NULL || sc->ahb_mem_res == NULL) {
device_printf(dev, "could not allocate resources\n");
flex_spi_detach(dev);
return (ENOMEM);
}
if ((clk_get_by_ofw_name(dev, node, "fspi_en", &sc->fspi_clk_en) != 0)
|| (clk_get_freq(sc->fspi_clk_en, &sc->fspi_clk_en_hz) != 0)) {
device_printf(dev, "could not get fspi_en clock\n");
flex_spi_detach(dev);
return (EINVAL);
}
if ((clk_get_by_ofw_name(dev, node, "fspi", &sc->fspi_clk) != 0)
|| (clk_get_freq(sc->fspi_clk, &sc->fspi_clk_hz) != 0)) {
device_printf(dev, "could not get fspi clock\n");
flex_spi_detach(dev);
return (EINVAL);
}
if (clk_enable(sc->fspi_clk_en) != 0 ||
clk_enable(sc->fspi_clk) != 0) {
device_printf(dev, "could not enable clocks\n");
flex_spi_detach(dev);
return (EINVAL);
}
reg = read_reg(sc, FSPI_INTR);
if (reg)
write_reg(sc, FSPI_INTR, reg);
if (flex_spi_default_setup(sc) != 0) {
device_printf(sc->dev, "Unable to initialize defaults\n");
flex_spi_detach(dev);
return (ENXIO);
}
if(flex_spi_identify(sc) != 0) {
device_printf(sc->dev, "Unable to identify Flash\n");
flex_spi_detach(dev);
return (ENXIO);
}
if (flex_spi_clk_setup(sc, sc->fspi_max_clk) != 0) {
device_printf(sc->dev, "Unable to set up SPI max clock\n");
flex_spi_detach(dev);
return (ENXIO);
}
sc->buf = malloc(sc->erasesize, SECTOR_BUFFER, M_WAITOK);
sc->disk = disk_alloc();
sc->disk->d_open = flex_spi_open;
sc->disk->d_close = flex_spi_close;
sc->disk->d_strategy = flex_spi_strategy;
sc->disk->d_getattr = flex_spi_getattr;
sc->disk->d_ioctl = flex_spi_ioctl;
sc->disk->d_name = "flash/qspi";
sc->disk->d_drv1 = sc;
sc->disk->d_maxsize = DFLTPHYS;
sc->disk->d_sectorsize = FLASH_SECTORSIZE;
sc->disk->d_unit = device_get_unit(sc->dev);
sc->disk->d_dump = NULL;
sc->disk->d_mediasize = sc->sectorsize * sc->sectorcount;
sc->disk->d_stripesize = sc->erasesize;
bioq_init(&sc->bio_queue);
sc->taskstate = TSTATE_RUNNING;
kproc_create(&flex_spi_task, sc, &sc->p, 0, 0, "task: qspi flash");
disk_create(sc->disk, DISK_VERSION);
return (0);
}
static int
flex_spi_detach(device_t dev)
{
struct flex_spi_softc *sc;
int err;
sc = device_get_softc(dev);
err = 0;
if (!device_is_attached(dev))
goto free_resources;
mtx_lock(&sc->disk_mtx);
if (sc->taskstate == TSTATE_RUNNING) {
sc->taskstate = TSTATE_STOPPING;
wakeup(sc->disk);
while (err == 0 && sc->taskstate != TSTATE_STOPPED) {
err = mtx_sleep(sc->disk, &sc->disk_mtx, 0, "flex_spi",
hz * 3);
if (err != 0) {
sc->taskstate = TSTATE_RUNNING;
device_printf(sc->dev,
"Failed to stop queue task\n");
}
}
}
mtx_unlock(&sc->disk_mtx);
mtx_destroy(&sc->disk_mtx);
if (err == 0 && sc->taskstate == TSTATE_STOPPED) {
disk_destroy(sc->disk);
bioq_flush(&sc->bio_queue, NULL, ENXIO);
}
free_resources:
if (sc->mem_res != NULL)
bus_release_resource(dev, SYS_RES_MEMORY,
rman_get_rid(sc->mem_res), sc->mem_res);
if (sc->ahb_mem_res != NULL)
bus_release_resource(dev, SYS_RES_MEMORY,
rman_get_rid(sc->ahb_mem_res), sc->ahb_mem_res);
if (sc->fspi_clk_en_hz)
clk_disable(sc->fspi_clk_en);
if (sc->fspi_clk_hz)
clk_disable(sc->fspi_clk);
free(sc->buf, SECTOR_BUFFER);
return (err);
}
static int
flex_spi_open(struct disk *dp)
{
return (0);
}
static int
flex_spi_close(struct disk *dp)
{
return (0);
}
static int
flex_spi_ioctl(struct disk *dp, u_long cmd, void *data, int fflag,
struct thread *td)
{
return (ENOTSUP);
}
static void
flex_spi_strategy(struct bio *bp)
{
struct flex_spi_softc *sc;
sc = (struct flex_spi_softc *)bp->bio_disk->d_drv1;
mtx_lock(&sc->disk_mtx);
bioq_disksort(&sc->bio_queue, bp);
mtx_unlock(&sc->disk_mtx);
wakeup(sc->disk);
}
static int
flex_spi_getattr(struct bio *bp)
{
struct flex_spi_softc *sc;
device_t dev;
if (bp->bio_disk == NULL || bp->bio_disk->d_drv1 == NULL) {
return (ENXIO);
}
sc = bp->bio_disk->d_drv1;
dev = sc->dev;
if (strcmp(bp->bio_attribute, "SPI::device") != 0) {
return (-1);
}
if (bp->bio_length != sizeof(dev)) {
return (EFAULT);
}
bcopy(&dev, bp->bio_data, sizeof(dev));
return (0);
}
static void
flex_spi_task(void *arg)
{
struct flex_spi_softc *sc;
struct bio *bp;
sc = (struct flex_spi_softc *)arg;
for (;;) {
mtx_lock(&sc->disk_mtx);
do {
if (sc->taskstate == TSTATE_STOPPING) {
sc->taskstate = TSTATE_STOPPED;
mtx_unlock(&sc->disk_mtx);
wakeup(sc->disk);
kproc_exit(0);
}
bp = bioq_first(&sc->bio_queue);
if (bp == NULL)
mtx_sleep(sc->disk, &sc->disk_mtx, PRIBIO,
"flex_spi", 0);
} while (bp == NULL);
bioq_remove(&sc->bio_queue, bp);
mtx_unlock(&sc->disk_mtx);
switch (bp->bio_cmd) {
case BIO_READ:
bp->bio_error = flex_spi_read(sc, bp->bio_offset,
bp->bio_data, bp->bio_bcount);
break;
case BIO_WRITE:
bp->bio_error = flex_spi_write(sc, bp->bio_offset,
bp->bio_data, bp->bio_bcount);
break;
default:
bp->bio_error = EINVAL;
}
biodone(bp);
}
}
static device_method_t flex_spi_methods[] = {
DEVMETHOD(device_probe, flex_spi_probe),
DEVMETHOD(device_attach, flex_spi_attach),
DEVMETHOD(device_detach, flex_spi_detach),
DEVMETHOD_END
};
static driver_t flex_spi_driver = {
"flex_spi",
flex_spi_methods,
sizeof(struct flex_spi_softc),
};
DRIVER_MODULE(flex_spi, simplebus, flex_spi_driver, 0, 0);
SIMPLEBUS_PNP_INFO(flex_spi_compat_data);