freebsd-dev/sys/dev/isf/isf.c
Robert Watson 6a347d023b Merge Perforce changeset 219952 to head:
Make different bus attachments for Altera and Terasice
  device drivers share the same devclass_t.

Sponsored by:	DARPA, AFRL
2013-01-13 16:57:11 +00:00

742 lines
20 KiB
C

/*-
* Copyright (c) 2012 Robert N. M. Watson
* Copyright (c) 2012 SRI International
* All rights reserved.
*
* This software was developed by SRI International and the University of
* Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
* ("CTSRD"), as part of the DARPA CRASH research programme.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/module.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/conf.h>
#include <sys/endian.h>
#include <sys/bio.h>
#include <sys/kthread.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/mutex.h>
#include <sys/rman.h>
#include <sys/taskqueue.h>
#include <machine/bus.h>
#include <machine/resource.h>
#include <geom/geom_disk.h>
#include <dev/isf/isf.h>
/* Read Mode */
#define ISF_CMD_RA 0xFF /* Read Array mode */
#define ISF_CMD_RSR 0x70 /* Read Status Register mode */
#define ISF_CMD_RDI 0x90 /* Read Device ID/Config Reg mode */
#define ISF_CMD_RQ 0x98 /* Read Query mode */
#define ISF_CMD_CSR 0x50 /* Clear Status Register */
/* Write Mode */
#define ISF_CMD_WPS 0x40 /* Word Program Setup */
#define ISF_CMD_BPS 0xE8 /* Buffered Program Setup */
#define ISF_CMD_BPC 0xD0 /* Buffered Program Confirm */
/* Erase Mode */
#define ISF_CMD_BES 0x20 /* Block Erase Setup */
#define ISF_CMD_BEC 0xD0 /* Block Erase Confirm */
/* Block Locking/Unlocking */
#define ISF_CMD_LBS 0x60 /* Lock Block Setup */
#define ISF_CMD_LB 0x01 /* Lock Block */
#define ISF_CMD_UB 0xD0 /* Unlock Block */
/*
* Read Device Identifier registers.
*
* NOTE: ISF_RDIR_BLC is relative to the block base address.
*/
#define ISF_REG_MC 0x00 /* Manufacture Code */
#define ISF_REG_ID 0x01 /* Device ID Code */
#define ISF_REG_BLC 0x02 /* Block Lock Configuration */
#define ISF_REG_RCR 0x05 /* Read Configuration Register */
/*
* Protection Registers
*/
#define ISF_REG_L0 0x80 /* Lock Register 0 */
#define ISF_REG_FPP 0x81 /* 64-bit Factory Protection Register */
#define ISF_REG_UPP 0x85 /* 64-bit User Protection Register */
#define ISF_REG_L1 0x89 /* Lock Register 1 */
#define ISF_REG_PP1 0x8A /* 128-bit Protection Register 1 */
#define ISF_REG_PP2 0x92 /* 128-bit Protection Register 2 */
#define ISF_REG_PP3 0x9A /* 128-bit Protection Register 3 */
#define ISF_REG_PP4 0xA2 /* 128-bit Protection Register 4 */
#define ISF_REG_PP5 0xAA /* 128-bit Protection Register 5 */
#define ISF_REG_PP6 0xB2 /* 128-bit Protection Register 6 */
#define ISF_REG_PP7 0xBA /* 128-bit Protection Register 7 */
#define ISF_REG_PP8 0xC2 /* 128-bit Protection Register 8 */
#define ISF_REG_PP9 0xCA /* 128-bit Protection Register 9 */
#define ISF_REG_PP10 0xD2 /* 128-bit Protection Register 10 */
#define ISF_REG_PP11 0xDA /* 128-bit Protection Register 11 */
#define ISF_REG_PP12 0xE2 /* 128-bit Protection Register 12 */
#define ISF_REG_PP13 0xEA /* 128-bit Protection Register 13 */
#define ISF_REG_PP14 0xF2 /* 128-bit Protection Register 14 */
#define ISF_REG_PP15 0xFA /* 128-bit Protection Register 15 */
#define ISF_REG_PP16 0x102 /* 128-bit Protection Register 16 */
#define ISF_SR_BWS (1 << 0) /* BEFP Status */
#define ISF_SR_BLS (1 << 1) /* Block-Locked Status */
#define ISF_SR_PSS (1 << 2) /* Program Suspend Status */
#define ISF_SR_VPPS (1 << 3) /* Vpp Status */
#define ISF_SR_PS (1 << 4) /* Program Status */
#define ISF_SR_ES (1 << 5) /* Erase Status */
#define ISF_SR_ESS (1 << 6) /* Erase Suspend Status */
#define ISF_SR_DWS (1 << 7) /* Device Write Status */
#define ISF_SR_FSC_MASK (ISF_SR_VPPS | ISF_SR_PS | ISF_SR_BLS)
#define ISF_BUFFER_PROGRAM
MALLOC_DEFINE(M_ISF, "isf_data", "Intel StrateFlash driver");
static int isf_debug = 0;
static struct isf_chips {
uint16_t chip_id;
size_t chip_size;
const char *chip_desc;
} chip_ids[] = {
{ 0x8817, 0x0800000, "64-Mbit Top Parameter" },
{ 0x881A, 0x0800000, "64-Mbit Bottom Parameter" },
{ 0x8818, 0x1000000, "128-Mbit Top Parameter" },
{ 0x881B, 0x1000000, "128-Mbit Bottom Parameter" },
{ 0x8919, 0x2000000, "256-Mbit Top Parameter" },
{ 0x891C, 0x2000000, "256-Mbit Bottom Parameter" },
{ 0x8961, 0x2000000, "512-Mbit package (half)" },
{ 0x0000, 0x0000000, NULL }
};
static void isf_task(void *arg);
/*
* Device driver for the Intel StrataFlash NOR flash device. This
* implementation is known to work with 256Mb instances of the device, but may
* also work with other 64/128/512Mb parts without much work. Multiple
* device instances should be used when multiple parts are in the same
* physical package, due to variable block size support in the StrataFlash
* part.
*/
devclass_t isf_devclass;
static uint16_t
isf_read_reg(struct isf_softc *sc, uint16_t reg)
{
if (isf_debug)
device_printf(sc->isf_dev, "isf_read_reg(0x%02x)\n", reg);
return (le16toh(bus_read_2(sc->isf_res, reg * 2)));
}
static uint64_t
isf_read_reg64(struct isf_softc *sc, uint16_t reg)
{
uint64_t val;
uint16_t *val16 = (uint16_t *)&val;
if (isf_debug)
device_printf(sc->isf_dev, "isf_read_reg64(0x%02x)\n", reg);
val16[0] = bus_read_2(sc->isf_res, reg * 2);
val16[1] = bus_read_2(sc->isf_res, (reg+1) * 2);
val16[2] = bus_read_2(sc->isf_res, (reg+2) * 2);
val16[3] = bus_read_2(sc->isf_res, (reg+3) * 2);
return(le64toh(val));
}
static uint16_t
isf_read_off(struct isf_softc *sc, off_t off)
{
KASSERT(off >= 0, ("%s: negative offset\n", __func__));
KASSERT(off < sc->isf_disk->d_mediasize,
("%s: offset out side address space 0x%08jx \n", __func__,
(intmax_t)off));
if (isf_debug)
device_printf(sc->isf_dev, "isf_read_off(0x%08jx)\n",
(intmax_t)off);
return (le16toh(bus_read_2(sc->isf_res, off)));
}
static void
isf_write_cmd(struct isf_softc *sc, off_t off, uint16_t cmd)
{
if (isf_debug)
device_printf(sc->isf_dev, "isf_write_cmd(0x%08jx, 0x%02x)\n",
off, cmd);
bus_write_2(sc->isf_res, off, htole16(cmd));
}
static uint16_t
isf_read_status(struct isf_softc *sc, off_t off)
{
isf_write_cmd(sc, off/2, ISF_CMD_RSR);
return isf_read_off(sc, off);
}
static void
isf_clear_status(struct isf_softc *sc)
{
isf_write_cmd(sc, 0, ISF_CMD_CSR);
}
static int
isf_full_status_check(struct isf_softc *sc, off_t off)
{
int error = 0;
uint16_t status;
status = isf_read_status(sc, off);
if (status & ISF_SR_VPPS) {
device_printf(sc->isf_dev, "Vpp Range Error\n");
error = EIO;
} else if (status & ISF_SR_PS) {
device_printf(sc->isf_dev, "Program Error\n");
error = EIO;
} else if (status & ISF_SR_BLS) {
device_printf(sc->isf_dev, "Device Protect Error\n");
error = EIO;
}
isf_clear_status(sc);
return(error);
}
static int
isf_full_erase_status_check(struct isf_softc *sc, off_t off)
{
int error = 0;
uint16_t status;
status = isf_read_status(sc, off);
if (status & ISF_SR_VPPS) {
device_printf(sc->isf_dev, "Vpp Range Error\n");
error = EIO;
} else if (status & (ISF_SR_PS|ISF_SR_ES)) {
device_printf(sc->isf_dev, "Command Sequence Error\n");
error = EIO;
} else if (status & ISF_SR_ES) {
device_printf(sc->isf_dev, "Block Erase Error\n");
error = EIO;
} else if (status & ISF_SR_BLS) {
device_printf(sc->isf_dev, "Block Locked Error\n");
error = EIO;
}
isf_clear_status(sc);
return(error);
}
static void
isf_unlock_block(struct isf_softc *sc, off_t off)
{
isf_write_cmd(sc, off, ISF_CMD_LBS);
isf_write_cmd(sc, off, ISF_CMD_UB);
isf_write_cmd(sc, off, ISF_CMD_RA);
}
static void
isf_lock_block(struct isf_softc *sc, off_t off)
{
isf_write_cmd(sc, off, ISF_CMD_LBS);
isf_write_cmd(sc, off, ISF_CMD_LB);
isf_write_cmd(sc, off, ISF_CMD_RA);
}
static void
isf_read(struct isf_softc *sc, off_t off, void *data, size_t len)
{
KASSERT((uintptr_t)data % 2 == 0,
("%s: unaligned data %p", __func__, data));
KASSERT((len <= ISF_SECTORSIZE) && (len % 2 == 0),
("%s: invalid length %ju", __func__, len));
KASSERT(off % ISF_SECTORSIZE == 0,
("%s: invalid offset %ju\n", __func__, off));
/*
* It is not permitted to read blocks that are in the process of
* being erased, but we know they will be all 1's after the
* erase so just report that value if asked about a block that
* is being erased.
*/
if (sc->isf_bstate[off / ISF_ERASE_BLOCK] == BS_ERASING)
memset(data, 0xFF, len);
else
bus_read_region_2(sc->isf_res, off, (uint16_t *)data, len / 2);
}
static int
isf_write(struct isf_softc *sc, off_t off, void *data, size_t len)
{
int cycles, error = 0;
uint16_t *dp;
uint16_t status;
off_t coff;
KASSERT((uintptr_t)data % 2 == 0,
("%s: unaligned data %p", __func__, data));
KASSERT((len <= ISF_SECTORSIZE) && (len % 2 == 0),
("%s: invalid length %ju", __func__, len));
KASSERT(off % ISF_SECTORSIZE == 0,
("%s: invalid offset %ju\n", __func__, off));
KASSERT(!sc->isf_erasing,
("%s: trying to write while erasing\n", __func__));
KASSERT(sc->isf_bstate[off / ISF_ERASE_BLOCK] != BS_ERASING,
("%s: block being erased at %ju\n", __func__, off));
isf_unlock_block(sc, off);
#ifdef ISF_BUFFER_PROGRAM
for (dp = data, coff = off; dp - (uint16_t *)data < len / 2;
dp += 32, coff += 64) {
isf_clear_status(sc);
isf_write_cmd(sc, coff, ISF_CMD_BPS);
cycles = 0xFFFF;
while ( !(isf_read_off(sc, coff) & ISF_SR_DWS) ) {
if (cycles-- == 0) {
device_printf(sc->isf_dev, "timeout waiting"
" for write to start at 0x08%jx\n",
(intmax_t)coff);
return (EIO);
}
isf_write_cmd(sc, coff, ISF_CMD_BPS);
}
/* When writing N blocks, send N-1 as the count */
isf_write_cmd(sc, coff, 31);
bus_write_region_2(sc->isf_res, coff, dp, 32);
isf_write_cmd(sc, coff, ISF_CMD_BPC);
status = isf_read_off(sc, coff);
cycles = 0xFFFFF;
while ( !(status & ISF_SR_DWS) ) {
if (cycles-- == 0) {
device_printf(sc->isf_dev, "timeout waiting"
" for write to complete at 0x08%jx\n",
(intmax_t)coff);
error = EIO;
break;
}
status = isf_read_off(sc, coff);
}
isf_full_status_check(sc, off);
isf_write_cmd(sc, coff, ISF_CMD_RA);
}
#else
for (dp = data, coff = off; dp - (uint16_t *)data < len / 2;
dp++, coff += 2) {
isf_write_cmd(sc, coff, ISF_CMD_WPS);
bus_write_2(sc->isf_res, coff, *dp);
status = isf_read_off(sc, coff);
cycles=0xFFFFF;
while ( !(status & ISF_SR_DWS) ) {
if (cycles-- == 0) {
device_printf(sc->isf_dev, "timeout waiting"
" for write to complete at 0x08%jx\n",
(intmax_t)coff);
error = EIO;
break;
}
status = isf_read_off(sc, coff);
}
}
isf_full_status_check(sc, off);
isf_write_cmd(sc, coff, ISF_CMD_RA);
#endif
isf_lock_block(sc, off);
return error;
}
static void
isf_erase_at(struct isf_softc *sc, off_t off)
{
int cycles;
uint16_t status;
isf_unlock_block(sc, off);
isf_clear_status(sc);
isf_write_cmd(sc, off, ISF_CMD_BES);
isf_write_cmd(sc, off, ISF_CMD_BEC);
cycles=0xFFFFFF;
status = isf_read_off(sc, off);
while ( !(status & ISF_SR_DWS) ) {
#ifdef NOTYET
ISF_SLEEP(sc, sc, hz);
#endif
if (cycles-- == 0) {
device_printf(sc->isf_dev,
"Giving up on erase\n");
break;
}
status = isf_read_off(sc, off);
}
isf_full_erase_status_check(sc, off);
isf_lock_block(sc, off);
isf_write_cmd(sc, off, ISF_CMD_RA);
}
static void
isf_erase_range(struct isf_softc *sc, off_t blk_off, size_t size)
{
off_t off;
off_t ms = sc->isf_disk->d_mediasize;
KASSERT(blk_off % ISF_ERASE_BLOCK == 0,
("%s: invalid offset %ju\n", __func__, blk_off));
ISF_LOCK_ASSERT(sc);
for (off = blk_off; off < blk_off + size; off += ISF_ERASE_BLOCK) {
sc->isf_bstate[off / ISF_ERASE_BLOCK] = BS_ERASING;
/*
* The first or last 128K is four blocks depending which
* part this is. For now, just assume both are and
* erase four times.
*/
if (off == 0 || ms - off == ISF_ERASE_BLOCK) {
isf_erase_at(sc, off);
isf_erase_at(sc, off + 0x08000);
isf_erase_at(sc, off + 0x10000);
isf_erase_at(sc, off + 0x18000);
} else
isf_erase_at(sc, off);
sc->isf_bstate[off / ISF_ERASE_BLOCK] = BS_STEADY;
}
}
/*
* disk(9) methods.
*/
static int
isf_disk_ioctl(struct disk *disk, u_long cmd, void *data, int fflag,
struct thread *td)
{
int error = 0;
struct isf_softc *sc = disk->d_drv1;
struct isf_range *ir;
switch (cmd) {
case ISF_ERASE:
ir = data;
if (ir->ir_off % ISF_ERASE_BLOCK != 0 ||
ir->ir_off >= disk->d_mediasize ||
ir->ir_size == 0 ||
ir->ir_size % ISF_ERASE_BLOCK != 0 ||
ir->ir_off + ir->ir_size > disk->d_mediasize) {
error = EINVAL;
break;
}
ISF_LOCK(sc);
if (sc->isf_erasing) {
ISF_UNLOCK(sc);
error = EBUSY;
break;
}
sc->isf_erasing = 1;
isf_erase_range(sc, ir->ir_off, ir->ir_size);
sc->isf_erasing = 0;
ISF_UNLOCK(sc);
break;
default:
error = EINVAL;
}
return (error);
}
static void
isf_disk_strategy(struct bio *bp)
{
struct isf_softc *sc = bp->bio_disk->d_drv1;
/*
* We advertise a block size and maximum I/O size up the stack; catch
* any attempts to not follow the rules.
*/
KASSERT(bp->bio_bcount == ISF_SECTORSIZE,
("%s: I/O size not %d", __func__, ISF_SECTORSIZE));
ISF_LOCK(sc);
bioq_disksort(&sc->isf_bioq, bp);
ISF_WAKEUP(sc);
ISF_UNLOCK(sc);
}
static void
isf_task(void *arg)
{
struct isf_softc *sc = arg;
struct bio *bp;
int ss = sc->isf_disk->d_sectorsize;
int error, i;
for (;;) {
ISF_LOCK(sc);
do {
bp = bioq_first(&sc->isf_bioq);
if (bp == NULL) {
if (sc->isf_doomed)
kproc_exit(0);
else
ISF_SLEEP(sc, sc, 0);
}
} while (bp == NULL);
bioq_remove(&sc->isf_bioq, bp);
error = 0;
switch (bp->bio_cmd) {
case BIO_READ:
isf_read(sc, bp->bio_pblkno * ss, bp->bio_data,
bp->bio_bcount);
break;
case BIO_WRITE:
/*
* In principle one could suspend the in-progress
* erase, process any pending writes to other
* blocks and then proceed, but that seems
* overly complex for the likely usage modes.
*/
if (sc->isf_erasing) {
error = EBUSY;
break;
}
/*
* Read in the block we want to write and check that
* we're only setting bits to 0. If an erase would
* be required return an I/O error.
*/
isf_read(sc, bp->bio_pblkno * ss, sc->isf_rbuf,
bp->bio_bcount);
for (i = 0; i < bp->bio_bcount / 2; i++)
if ((sc->isf_rbuf[i] &
((uint16_t *)bp->bio_data)[i]) !=
((uint16_t *)bp->bio_data)[i]) {
device_printf(sc->isf_dev, "write"
" requires erase at 0x%08jx\n",
bp->bio_pblkno * ss);
error = EIO;
break;
}
if (error != 0)
break;
error = isf_write(sc, bp->bio_pblkno * ss,
bp->bio_data, bp->bio_bcount);
break;
default:
panic("%s: unsupported I/O operation %d", __func__,
bp->bio_cmd);
}
if (error == 0)
biodone(bp);
else
biofinish(bp, NULL, error);
ISF_UNLOCK(sc);
}
}
static void
isf_dump_info(struct isf_softc *sc)
{
int i;
int32_t reg;
isf_write_cmd(sc, 0, ISF_CMD_RDI);
device_printf(sc->isf_dev, "manufacturer code: 0x%04x\n",
isf_read_reg(sc, ISF_REG_MC));
device_printf(sc->isf_dev, "device id code: 0x%04x\n",
isf_read_reg(sc, ISF_REG_ID));
device_printf(sc->isf_dev, "read config register: 0x%04x\n",
isf_read_reg(sc, ISF_REG_RCR));
device_printf(sc->isf_dev, "lock register 0: 0x%04x\n",
isf_read_reg(sc, ISF_REG_L0));
device_printf(sc->isf_dev, "lock register 1: 0x%04x\n",
isf_read_reg(sc, ISF_REG_L1));
device_printf(sc->isf_dev, "factory PPR: 0x%016jx\n",
(uintmax_t)isf_read_reg64(sc, ISF_REG_FPP));
device_printf(sc->isf_dev, "user PPR (64-bit): 0x%016jx\n",
(uintmax_t)isf_read_reg64(sc, ISF_REG_UPP));
for (reg = ISF_REG_PP1, i = 1; reg <= ISF_REG_PP16; reg += 8, i++) {
/* XXX: big-endian ordering of uint64_t's */
device_printf(sc->isf_dev,
"user PPR [%02d]: 0x%016jx%016jx\n", i,
(uintmax_t)isf_read_reg64(sc, reg+4),
(uintmax_t)isf_read_reg64(sc, reg));
}
isf_write_cmd(sc, 0, ISF_CMD_RA);
}
static void
isf_disk_insert(struct isf_softc *sc, off_t mediasize)
{
struct disk *disk;
sc->isf_doomed = 0;
sc->isf_erasing = 0;
sc->isf_bstate = malloc(sizeof(*sc->isf_bstate) *
(mediasize / ISF_ERASE_BLOCK), M_ISF, M_ZERO | M_WAITOK);
kproc_create(&isf_task, sc, &sc->isf_proc, 0, 0, "isf");
disk = disk_alloc();
disk->d_drv1 = sc;
disk->d_name = "isf";
disk->d_unit = sc->isf_unit;
disk->d_strategy = isf_disk_strategy;
disk->d_ioctl = isf_disk_ioctl;
disk->d_sectorsize = ISF_SECTORSIZE;
disk->d_mediasize = mediasize;
disk->d_maxsize = ISF_SECTORSIZE;
sc->isf_disk = disk;
if (bootverbose)
isf_dump_info(sc);
disk_create(disk, DISK_VERSION);
device_printf(sc->isf_dev, "%juM flash device\n",
(uintmax_t)disk->d_mediasize / (1024 * 1024));
}
static void
isf_disk_remove(struct isf_softc *sc)
{
struct disk *disk;
ISF_LOCK_ASSERT(sc);
KASSERT(sc->isf_disk != NULL, ("%s: isf_disk NULL", __func__));
sc->isf_doomed = 1;
ISF_WAKEUP(sc);
ISF_SLEEP(sc, sc->isf_proc, 0);
/*
* XXXRW: Is it OK to call disk_destroy() under the mutex, or should
* we be deferring that to the calling context once it is released?
*/
disk = sc->isf_disk;
disk_gone(disk);
disk_destroy(disk);
sc->isf_disk = NULL;
free(sc->isf_bstate, M_ISF);
device_printf(sc->isf_dev, "flash device removed\n");
}
int
isf_attach(struct isf_softc *sc)
{
uint16_t id;
u_long start, size;
struct isf_chips *cp = chip_ids;
start = rman_get_start(sc->isf_res);
if (start % 2 != 0) {
device_printf(sc->isf_dev,
"Unsupported flash start alignment %lu\n",
start);
return (ENXIO);
}
isf_write_cmd(sc, 0, ISF_CMD_RDI);
id = isf_read_reg(sc, ISF_REG_ID);
while (cp->chip_id != id)
cp++;
if (cp->chip_desc == NULL) {
device_printf(sc->isf_dev,
"Unsupported device ID 0x%04x\n", id);
return (ENXIO);
}
isf_write_cmd(sc, 0, ISF_CMD_RA);
size = rman_get_size(sc->isf_res);
if (size != cp->chip_size) {
device_printf(sc->isf_dev,
"Unsupported flash size %lu\n", size);
return (ENXIO);
}
bioq_init(&sc->isf_bioq);
ISF_LOCK_INIT(sc);
sc->isf_disk = NULL;
isf_disk_insert(sc, size);
return(0);
}
void
isf_detach(struct isf_softc *sc)
{
/*
* Simulate a disk removal if one is present to deal with any pending
* or queued I/O. This will occur as a result of a device driver
* detach -- the Intel StrataFlash has no notion of removal itself.
*
* XXXRW: Is the locking here right?
*/
ISF_LOCK(sc);
isf_disk_remove(sc);
bioq_flush(&sc->isf_bioq, NULL, ENXIO);
KASSERT(bioq_first(&sc->isf_bioq) == NULL,
("%s: non-empty bioq", __func__));
ISF_UNLOCK(sc);
ISF_LOCK_DESTROY(sc);
}