1eb69ed7fSRuslan Bukin /*- 2eb69ed7fSRuslan Bukin * Copyright (c) 2009 Oleksandr Tymoshenko. All rights reserved. 3eb69ed7fSRuslan Bukin * Copyright (c) 2017 Ruslan Bukin <br@bsdpad.com> 4eb69ed7fSRuslan Bukin * Copyright (c) 2018 Ian Lepore. All rights reserved. 5f86e6000SWarner Losh * Copyright (c) 2006 M. Warner Losh <imp@FreeBSD.org> 6eb69ed7fSRuslan Bukin * 7eb69ed7fSRuslan Bukin * This software was developed by SRI International and the University of 8eb69ed7fSRuslan Bukin * Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237 9eb69ed7fSRuslan Bukin * ("CTSRD"), as part of the DARPA CRASH research programme. 10eb69ed7fSRuslan Bukin * 11eb69ed7fSRuslan Bukin * Redistribution and use in source and binary forms, with or without 12eb69ed7fSRuslan Bukin * modification, are permitted provided that the following conditions 13eb69ed7fSRuslan Bukin * are met: 14eb69ed7fSRuslan Bukin * 1. Redistributions of source code must retain the above copyright 15eb69ed7fSRuslan Bukin * notice, this list of conditions and the following disclaimer. 16eb69ed7fSRuslan Bukin * 2. Redistributions in binary form must reproduce the above copyright 17eb69ed7fSRuslan Bukin * notice, this list of conditions and the following disclaimer in the 18eb69ed7fSRuslan Bukin * documentation and/or other materials provided with the distribution. 19eb69ed7fSRuslan Bukin * 20eb69ed7fSRuslan Bukin * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21eb69ed7fSRuslan Bukin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22eb69ed7fSRuslan Bukin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23eb69ed7fSRuslan Bukin * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24eb69ed7fSRuslan Bukin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25eb69ed7fSRuslan Bukin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26eb69ed7fSRuslan Bukin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27eb69ed7fSRuslan Bukin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28eb69ed7fSRuslan Bukin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29eb69ed7fSRuslan Bukin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30eb69ed7fSRuslan Bukin * SUCH DAMAGE. 31eb69ed7fSRuslan Bukin */ 32eb69ed7fSRuslan Bukin 33eb69ed7fSRuslan Bukin /* n25q Quad SPI Flash driver. */ 34eb69ed7fSRuslan Bukin 35eb69ed7fSRuslan Bukin #include <sys/cdefs.h> 36eb69ed7fSRuslan Bukin __FBSDID("$FreeBSD$"); 37eb69ed7fSRuslan Bukin 38eb69ed7fSRuslan Bukin #include "opt_platform.h" 39eb69ed7fSRuslan Bukin 40eb69ed7fSRuslan Bukin #include <sys/param.h> 41eb69ed7fSRuslan Bukin #include <sys/systm.h> 42eb69ed7fSRuslan Bukin #include <sys/bio.h> 43eb69ed7fSRuslan Bukin #include <sys/bus.h> 44eb69ed7fSRuslan Bukin #include <sys/conf.h> 45eb69ed7fSRuslan Bukin #include <sys/kernel.h> 46eb69ed7fSRuslan Bukin #include <sys/kthread.h> 47eb69ed7fSRuslan Bukin #include <sys/lock.h> 48eb69ed7fSRuslan Bukin #include <sys/mbuf.h> 49eb69ed7fSRuslan Bukin #include <sys/malloc.h> 50eb69ed7fSRuslan Bukin #include <sys/module.h> 51eb69ed7fSRuslan Bukin #include <sys/mutex.h> 52eb69ed7fSRuslan Bukin #include <geom/geom_disk.h> 53eb69ed7fSRuslan Bukin 54eb69ed7fSRuslan Bukin #include <machine/bus.h> 55eb69ed7fSRuslan Bukin 56eb69ed7fSRuslan Bukin #include <dev/fdt/fdt_common.h> 57eb69ed7fSRuslan Bukin #include <dev/ofw/ofw_bus_subr.h> 58eb69ed7fSRuslan Bukin #include <dev/ofw/openfirm.h> 59eb69ed7fSRuslan Bukin 60eb69ed7fSRuslan Bukin #include <dev/flash/mx25lreg.h> 61eb69ed7fSRuslan Bukin 62eb69ed7fSRuslan Bukin #include "qspi_if.h" 63eb69ed7fSRuslan Bukin 64eb69ed7fSRuslan Bukin #define N25Q_DEBUG 65eb69ed7fSRuslan Bukin #undef N25Q_DEBUG 66eb69ed7fSRuslan Bukin 67eb69ed7fSRuslan Bukin #ifdef N25Q_DEBUG 68eb69ed7fSRuslan Bukin #define dprintf(fmt, ...) printf(fmt, ##__VA_ARGS__) 69eb69ed7fSRuslan Bukin #else 70eb69ed7fSRuslan Bukin #define dprintf(fmt, ...) 71eb69ed7fSRuslan Bukin #endif 72eb69ed7fSRuslan Bukin 73eb69ed7fSRuslan Bukin #define FL_NONE 0x00 74eb69ed7fSRuslan Bukin #define FL_ERASE_4K 0x01 75eb69ed7fSRuslan Bukin #define FL_ERASE_32K 0x02 76eb69ed7fSRuslan Bukin #define FL_ENABLE_4B_ADDR 0x04 77eb69ed7fSRuslan Bukin #define FL_DISABLE_4B_ADDR 0x08 78eb69ed7fSRuslan Bukin 79eb69ed7fSRuslan Bukin /* 80eb69ed7fSRuslan Bukin * Define the sectorsize to be a smaller size rather than the flash 81eb69ed7fSRuslan Bukin * sector size. Trying to run FFS off of a 64k flash sector size 82eb69ed7fSRuslan Bukin * results in a completely un-usable system. 83eb69ed7fSRuslan Bukin */ 84eb69ed7fSRuslan Bukin #define FLASH_SECTORSIZE 512 85eb69ed7fSRuslan Bukin 86eb69ed7fSRuslan Bukin struct n25q_flash_ident { 87eb69ed7fSRuslan Bukin const char *name; 88eb69ed7fSRuslan Bukin uint8_t manufacturer_id; 89eb69ed7fSRuslan Bukin uint16_t device_id; 90eb69ed7fSRuslan Bukin unsigned int sectorsize; 91eb69ed7fSRuslan Bukin unsigned int sectorcount; 92eb69ed7fSRuslan Bukin unsigned int flags; 93eb69ed7fSRuslan Bukin }; 94eb69ed7fSRuslan Bukin 95eb69ed7fSRuslan Bukin struct n25q_softc { 96eb69ed7fSRuslan Bukin device_t dev; 97eb69ed7fSRuslan Bukin bus_space_tag_t bst; 98eb69ed7fSRuslan Bukin bus_space_handle_t bsh; 99eb69ed7fSRuslan Bukin void *ih; 100eb69ed7fSRuslan Bukin struct resource *res[3]; 101eb69ed7fSRuslan Bukin 102eb69ed7fSRuslan Bukin uint8_t sc_manufacturer_id; 103eb69ed7fSRuslan Bukin uint16_t device_id; 104eb69ed7fSRuslan Bukin unsigned int sc_sectorsize; 105eb69ed7fSRuslan Bukin struct mtx sc_mtx; 106eb69ed7fSRuslan Bukin struct disk *sc_disk; 107eb69ed7fSRuslan Bukin struct proc *sc_p; 108eb69ed7fSRuslan Bukin struct bio_queue_head sc_bio_queue; 109eb69ed7fSRuslan Bukin unsigned int sc_flags; 110eb69ed7fSRuslan Bukin unsigned int sc_taskstate; 111eb69ed7fSRuslan Bukin }; 112eb69ed7fSRuslan Bukin 113eb69ed7fSRuslan Bukin #define TSTATE_STOPPED 0 114eb69ed7fSRuslan Bukin #define TSTATE_STOPPING 1 115eb69ed7fSRuslan Bukin #define TSTATE_RUNNING 2 116eb69ed7fSRuslan Bukin 117eb69ed7fSRuslan Bukin #define N25Q_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) 118eb69ed7fSRuslan Bukin #define N25Q_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) 119eb69ed7fSRuslan Bukin #define N25Q_LOCK_INIT(_sc) \ 120eb69ed7fSRuslan Bukin mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->dev), \ 121eb69ed7fSRuslan Bukin "n25q", MTX_DEF) 122eb69ed7fSRuslan Bukin #define N25Q_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); 123eb69ed7fSRuslan Bukin #define N25Q_ASSERT_LOCKED(_sc) \ 124eb69ed7fSRuslan Bukin mtx_assert(&_sc->sc_mtx, MA_OWNED); 125eb69ed7fSRuslan Bukin #define N25Q_ASSERT_UNLOCKED(_sc) \ 126eb69ed7fSRuslan Bukin mtx_assert(&_sc->sc_mtx, MA_NOTOWNED); 127eb69ed7fSRuslan Bukin 128eb69ed7fSRuslan Bukin static struct ofw_compat_data compat_data[] = { 129eb69ed7fSRuslan Bukin { "n25q00aa", 1 }, 130eb69ed7fSRuslan Bukin { NULL, 0 }, 131eb69ed7fSRuslan Bukin }; 132eb69ed7fSRuslan Bukin 133eb69ed7fSRuslan Bukin /* disk routines */ 134eb69ed7fSRuslan Bukin static int n25q_open(struct disk *dp); 135eb69ed7fSRuslan Bukin static int n25q_close(struct disk *dp); 136eb69ed7fSRuslan Bukin static int n25q_ioctl(struct disk *, u_long, void *, int, struct thread *); 137eb69ed7fSRuslan Bukin static void n25q_strategy(struct bio *bp); 138eb69ed7fSRuslan Bukin static int n25q_getattr(struct bio *bp); 139eb69ed7fSRuslan Bukin static void n25q_task(void *arg); 140eb69ed7fSRuslan Bukin 1416dfd0500SIan Lepore static struct n25q_flash_ident flash_devices[] = { 142eb69ed7fSRuslan Bukin { "n25q00", 0x20, 0xbb21, (64 * 1024), 2048, FL_ENABLE_4B_ADDR}, 143eb69ed7fSRuslan Bukin }; 144eb69ed7fSRuslan Bukin 145eb69ed7fSRuslan Bukin static int 146eb69ed7fSRuslan Bukin n25q_wait_for_device_ready(device_t dev) 147eb69ed7fSRuslan Bukin { 148eb69ed7fSRuslan Bukin device_t pdev; 149eb69ed7fSRuslan Bukin uint8_t status; 150eb69ed7fSRuslan Bukin int err; 151eb69ed7fSRuslan Bukin 152eb69ed7fSRuslan Bukin pdev = device_get_parent(dev); 153eb69ed7fSRuslan Bukin 154eb69ed7fSRuslan Bukin do { 155eb69ed7fSRuslan Bukin err = QSPI_READ_REG(pdev, dev, CMD_READ_STATUS, &status, 1); 156eb69ed7fSRuslan Bukin } while (err == 0 && (status & STATUS_WIP)); 157eb69ed7fSRuslan Bukin 158eb69ed7fSRuslan Bukin return (err); 159eb69ed7fSRuslan Bukin } 160eb69ed7fSRuslan Bukin 161eb69ed7fSRuslan Bukin static struct n25q_flash_ident* 162eb69ed7fSRuslan Bukin n25q_get_device_ident(struct n25q_softc *sc) 163eb69ed7fSRuslan Bukin { 164eb69ed7fSRuslan Bukin uint8_t manufacturer_id; 165eb69ed7fSRuslan Bukin uint16_t dev_id; 166eb69ed7fSRuslan Bukin device_t pdev; 167eb69ed7fSRuslan Bukin uint8_t data[4]; 168eb69ed7fSRuslan Bukin int i; 169eb69ed7fSRuslan Bukin 170eb69ed7fSRuslan Bukin pdev = device_get_parent(sc->dev); 171eb69ed7fSRuslan Bukin 172eb69ed7fSRuslan Bukin QSPI_READ_REG(pdev, sc->dev, CMD_READ_IDENT, (uint8_t *)&data[0], 4); 173eb69ed7fSRuslan Bukin 174eb69ed7fSRuslan Bukin manufacturer_id = data[0]; 175eb69ed7fSRuslan Bukin dev_id = (data[1] << 8) | (data[2]); 176eb69ed7fSRuslan Bukin 177eb69ed7fSRuslan Bukin for (i = 0; i < nitems(flash_devices); i++) { 178eb69ed7fSRuslan Bukin if ((flash_devices[i].manufacturer_id == manufacturer_id) && 179eb69ed7fSRuslan Bukin (flash_devices[i].device_id == dev_id)) 180eb69ed7fSRuslan Bukin return &flash_devices[i]; 181eb69ed7fSRuslan Bukin } 182eb69ed7fSRuslan Bukin 183eb69ed7fSRuslan Bukin printf("Unknown SPI flash device. Vendor: %02x, device id: %04x\n", 184eb69ed7fSRuslan Bukin manufacturer_id, dev_id); 185eb69ed7fSRuslan Bukin 186eb69ed7fSRuslan Bukin return (NULL); 187eb69ed7fSRuslan Bukin } 188eb69ed7fSRuslan Bukin 189eb69ed7fSRuslan Bukin static int 190eb69ed7fSRuslan Bukin n25q_write(device_t dev, struct bio *bp, off_t offset, caddr_t data, off_t count) 191eb69ed7fSRuslan Bukin { 192eb69ed7fSRuslan Bukin struct n25q_softc *sc; 193eb69ed7fSRuslan Bukin device_t pdev; 194eb69ed7fSRuslan Bukin int err; 195eb69ed7fSRuslan Bukin 196eb69ed7fSRuslan Bukin pdev = device_get_parent(dev); 197eb69ed7fSRuslan Bukin sc = device_get_softc(dev); 198eb69ed7fSRuslan Bukin 199eb69ed7fSRuslan Bukin dprintf("%s: offset 0x%llx count %lld bytes\n", __func__, offset, count); 200eb69ed7fSRuslan Bukin 201eb69ed7fSRuslan Bukin err = QSPI_ERASE(pdev, dev, offset); 202eb69ed7fSRuslan Bukin if (err != 0) { 203eb69ed7fSRuslan Bukin return (err); 204eb69ed7fSRuslan Bukin } 205eb69ed7fSRuslan Bukin 206eb69ed7fSRuslan Bukin err = QSPI_WRITE(pdev, dev, bp, offset, data, count); 207eb69ed7fSRuslan Bukin 208eb69ed7fSRuslan Bukin return (err); 209eb69ed7fSRuslan Bukin } 210eb69ed7fSRuslan Bukin 211eb69ed7fSRuslan Bukin static int 212eb69ed7fSRuslan Bukin n25q_read(device_t dev, struct bio *bp, off_t offset, caddr_t data, off_t count) 213eb69ed7fSRuslan Bukin { 214eb69ed7fSRuslan Bukin struct n25q_softc *sc; 215eb69ed7fSRuslan Bukin device_t pdev; 216eb69ed7fSRuslan Bukin int err; 217eb69ed7fSRuslan Bukin 218eb69ed7fSRuslan Bukin pdev = device_get_parent(dev); 219eb69ed7fSRuslan Bukin sc = device_get_softc(dev); 220eb69ed7fSRuslan Bukin 221eb69ed7fSRuslan Bukin dprintf("%s: offset 0x%llx count %lld bytes\n", __func__, offset, count); 222eb69ed7fSRuslan Bukin 223eb69ed7fSRuslan Bukin /* 224eb69ed7fSRuslan Bukin * Enforce the disk read sectorsize not the erase sectorsize. 225eb69ed7fSRuslan Bukin * In this way, smaller read IO is possible,dramatically 226eb69ed7fSRuslan Bukin * speeding up filesystem/geom_compress access. 227eb69ed7fSRuslan Bukin */ 228eb69ed7fSRuslan Bukin if (count % sc->sc_disk->d_sectorsize != 0 229eb69ed7fSRuslan Bukin || offset % sc->sc_disk->d_sectorsize != 0) { 230eb69ed7fSRuslan Bukin printf("EIO\n"); 231eb69ed7fSRuslan Bukin return (EIO); 232eb69ed7fSRuslan Bukin } 233eb69ed7fSRuslan Bukin 234eb69ed7fSRuslan Bukin err = QSPI_READ(pdev, dev, bp, offset, data, count); 235eb69ed7fSRuslan Bukin 236eb69ed7fSRuslan Bukin return (err); 237eb69ed7fSRuslan Bukin } 238eb69ed7fSRuslan Bukin 239eb69ed7fSRuslan Bukin static int 240eb69ed7fSRuslan Bukin n25q_set_4b_mode(device_t dev, uint8_t command) 241eb69ed7fSRuslan Bukin { 242eb69ed7fSRuslan Bukin struct n25q_softc *sc; 243eb69ed7fSRuslan Bukin device_t pdev; 244eb69ed7fSRuslan Bukin int err; 245eb69ed7fSRuslan Bukin 246eb69ed7fSRuslan Bukin pdev = device_get_parent(dev); 247eb69ed7fSRuslan Bukin sc = device_get_softc(dev); 248eb69ed7fSRuslan Bukin 249eb69ed7fSRuslan Bukin err = QSPI_WRITE_REG(pdev, dev, command, 0, 0); 250eb69ed7fSRuslan Bukin 251eb69ed7fSRuslan Bukin return (err); 252eb69ed7fSRuslan Bukin } 253eb69ed7fSRuslan Bukin 254eb69ed7fSRuslan Bukin static int 255eb69ed7fSRuslan Bukin n25q_probe(device_t dev) 256eb69ed7fSRuslan Bukin { 257eb69ed7fSRuslan Bukin int i; 258eb69ed7fSRuslan Bukin 259eb69ed7fSRuslan Bukin if (!ofw_bus_status_okay(dev)) 260eb69ed7fSRuslan Bukin return (ENXIO); 261eb69ed7fSRuslan Bukin 262eb69ed7fSRuslan Bukin /* First try to match the compatible property to the compat_data */ 263eb69ed7fSRuslan Bukin if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 1) 264eb69ed7fSRuslan Bukin goto found; 265eb69ed7fSRuslan Bukin 266eb69ed7fSRuslan Bukin /* 267eb69ed7fSRuslan Bukin * Next, try to find a compatible device using the names in the 268eb69ed7fSRuslan Bukin * flash_devices structure 269eb69ed7fSRuslan Bukin */ 270eb69ed7fSRuslan Bukin for (i = 0; i < nitems(flash_devices); i++) 271eb69ed7fSRuslan Bukin if (ofw_bus_is_compatible(dev, flash_devices[i].name)) 272eb69ed7fSRuslan Bukin goto found; 273eb69ed7fSRuslan Bukin 274eb69ed7fSRuslan Bukin return (ENXIO); 275eb69ed7fSRuslan Bukin found: 276eb69ed7fSRuslan Bukin device_set_desc(dev, "Micron n25q"); 277eb69ed7fSRuslan Bukin 278eb69ed7fSRuslan Bukin return (0); 279eb69ed7fSRuslan Bukin } 280eb69ed7fSRuslan Bukin 281eb69ed7fSRuslan Bukin static int 282eb69ed7fSRuslan Bukin n25q_attach(device_t dev) 283eb69ed7fSRuslan Bukin { 284eb69ed7fSRuslan Bukin struct n25q_flash_ident *ident; 285eb69ed7fSRuslan Bukin struct n25q_softc *sc; 286eb69ed7fSRuslan Bukin 287eb69ed7fSRuslan Bukin sc = device_get_softc(dev); 288eb69ed7fSRuslan Bukin sc->dev = dev; 289eb69ed7fSRuslan Bukin 290eb69ed7fSRuslan Bukin N25Q_LOCK_INIT(sc); 291eb69ed7fSRuslan Bukin 292eb69ed7fSRuslan Bukin ident = n25q_get_device_ident(sc); 293eb69ed7fSRuslan Bukin if (ident == NULL) { 294eb69ed7fSRuslan Bukin return (ENXIO); 295eb69ed7fSRuslan Bukin } 296eb69ed7fSRuslan Bukin 297eb69ed7fSRuslan Bukin n25q_wait_for_device_ready(sc->dev); 298eb69ed7fSRuslan Bukin 299eb69ed7fSRuslan Bukin sc->sc_disk = disk_alloc(); 300eb69ed7fSRuslan Bukin sc->sc_disk->d_open = n25q_open; 301eb69ed7fSRuslan Bukin sc->sc_disk->d_close = n25q_close; 302eb69ed7fSRuslan Bukin sc->sc_disk->d_strategy = n25q_strategy; 303eb69ed7fSRuslan Bukin sc->sc_disk->d_getattr = n25q_getattr; 304eb69ed7fSRuslan Bukin sc->sc_disk->d_ioctl = n25q_ioctl; 305eb69ed7fSRuslan Bukin sc->sc_disk->d_name = "flash/qspi"; 306eb69ed7fSRuslan Bukin sc->sc_disk->d_drv1 = sc; 307eb69ed7fSRuslan Bukin sc->sc_disk->d_maxsize = DFLTPHYS; 308eb69ed7fSRuslan Bukin sc->sc_disk->d_sectorsize = FLASH_SECTORSIZE; 309eb69ed7fSRuslan Bukin sc->sc_disk->d_mediasize = (ident->sectorsize * ident->sectorcount); 310eb69ed7fSRuslan Bukin sc->sc_disk->d_unit = device_get_unit(sc->dev); 311eb69ed7fSRuslan Bukin sc->sc_disk->d_dump = NULL; 312eb69ed7fSRuslan Bukin /* Sectorsize for erase operations */ 313eb69ed7fSRuslan Bukin sc->sc_sectorsize = ident->sectorsize; 314eb69ed7fSRuslan Bukin sc->sc_flags = ident->flags; 315eb69ed7fSRuslan Bukin 316eb69ed7fSRuslan Bukin if (sc->sc_flags & FL_ENABLE_4B_ADDR) 317eb69ed7fSRuslan Bukin n25q_set_4b_mode(dev, CMD_ENTER_4B_MODE); 318eb69ed7fSRuslan Bukin 319eb69ed7fSRuslan Bukin if (sc->sc_flags & FL_DISABLE_4B_ADDR) 320eb69ed7fSRuslan Bukin n25q_set_4b_mode(dev, CMD_EXIT_4B_MODE); 321eb69ed7fSRuslan Bukin 322eb69ed7fSRuslan Bukin /* NB: use stripesize to hold the erase/region size for RedBoot */ 323eb69ed7fSRuslan Bukin sc->sc_disk->d_stripesize = ident->sectorsize; 324eb69ed7fSRuslan Bukin 325eb69ed7fSRuslan Bukin disk_create(sc->sc_disk, DISK_VERSION); 326eb69ed7fSRuslan Bukin bioq_init(&sc->sc_bio_queue); 327eb69ed7fSRuslan Bukin 328eb69ed7fSRuslan Bukin kproc_create(&n25q_task, sc, &sc->sc_p, 0, 0, "task: n25q flash"); 329eb69ed7fSRuslan Bukin sc->sc_taskstate = TSTATE_RUNNING; 330eb69ed7fSRuslan Bukin 331eb69ed7fSRuslan Bukin device_printf(sc->dev, "%s, sector %d bytes, %d sectors\n", 332eb69ed7fSRuslan Bukin ident->name, ident->sectorsize, ident->sectorcount); 333eb69ed7fSRuslan Bukin 334eb69ed7fSRuslan Bukin return (0); 335eb69ed7fSRuslan Bukin } 336eb69ed7fSRuslan Bukin 337eb69ed7fSRuslan Bukin static int 338eb69ed7fSRuslan Bukin n25q_detach(device_t dev) 339eb69ed7fSRuslan Bukin { 340eb69ed7fSRuslan Bukin struct n25q_softc *sc; 341eb69ed7fSRuslan Bukin int err; 342eb69ed7fSRuslan Bukin 343eb69ed7fSRuslan Bukin sc = device_get_softc(dev); 344eb69ed7fSRuslan Bukin err = 0; 345eb69ed7fSRuslan Bukin 346eb69ed7fSRuslan Bukin N25Q_LOCK(sc); 347eb69ed7fSRuslan Bukin if (sc->sc_taskstate == TSTATE_RUNNING) { 348eb69ed7fSRuslan Bukin sc->sc_taskstate = TSTATE_STOPPING; 349eb69ed7fSRuslan Bukin wakeup(sc); 350eb69ed7fSRuslan Bukin while (err == 0 && sc->sc_taskstate != TSTATE_STOPPED) { 351eb69ed7fSRuslan Bukin err = msleep(sc, &sc->sc_mtx, 0, "n25q", hz * 3); 352eb69ed7fSRuslan Bukin if (err != 0) { 353eb69ed7fSRuslan Bukin sc->sc_taskstate = TSTATE_RUNNING; 354eb69ed7fSRuslan Bukin device_printf(sc->dev, 355eb69ed7fSRuslan Bukin "Failed to stop queue task\n"); 356eb69ed7fSRuslan Bukin } 357eb69ed7fSRuslan Bukin } 358eb69ed7fSRuslan Bukin } 359eb69ed7fSRuslan Bukin N25Q_UNLOCK(sc); 360eb69ed7fSRuslan Bukin 361eb69ed7fSRuslan Bukin if (err == 0 && sc->sc_taskstate == TSTATE_STOPPED) { 362eb69ed7fSRuslan Bukin disk_destroy(sc->sc_disk); 363eb69ed7fSRuslan Bukin bioq_flush(&sc->sc_bio_queue, NULL, ENXIO); 364eb69ed7fSRuslan Bukin N25Q_LOCK_DESTROY(sc); 365eb69ed7fSRuslan Bukin } 366eb69ed7fSRuslan Bukin return (err); 367eb69ed7fSRuslan Bukin } 368eb69ed7fSRuslan Bukin 369eb69ed7fSRuslan Bukin static int 370eb69ed7fSRuslan Bukin n25q_open(struct disk *dp) 371eb69ed7fSRuslan Bukin { 372eb69ed7fSRuslan Bukin 373eb69ed7fSRuslan Bukin return (0); 374eb69ed7fSRuslan Bukin } 375eb69ed7fSRuslan Bukin 376eb69ed7fSRuslan Bukin static int 377eb69ed7fSRuslan Bukin n25q_close(struct disk *dp) 378eb69ed7fSRuslan Bukin { 379eb69ed7fSRuslan Bukin 380eb69ed7fSRuslan Bukin return (0); 381eb69ed7fSRuslan Bukin } 382eb69ed7fSRuslan Bukin 383eb69ed7fSRuslan Bukin static int 384eb69ed7fSRuslan Bukin n25q_ioctl(struct disk *dp, u_long cmd, void *data, 385eb69ed7fSRuslan Bukin int fflag, struct thread *td) 386eb69ed7fSRuslan Bukin { 387eb69ed7fSRuslan Bukin 388eb69ed7fSRuslan Bukin return (EINVAL); 389eb69ed7fSRuslan Bukin } 390eb69ed7fSRuslan Bukin 391eb69ed7fSRuslan Bukin static void 392eb69ed7fSRuslan Bukin n25q_strategy(struct bio *bp) 393eb69ed7fSRuslan Bukin { 394eb69ed7fSRuslan Bukin struct n25q_softc *sc; 395eb69ed7fSRuslan Bukin 396eb69ed7fSRuslan Bukin sc = (struct n25q_softc *)bp->bio_disk->d_drv1; 397eb69ed7fSRuslan Bukin 398eb69ed7fSRuslan Bukin N25Q_LOCK(sc); 399eb69ed7fSRuslan Bukin bioq_disksort(&sc->sc_bio_queue, bp); 400eb69ed7fSRuslan Bukin wakeup(sc); 401eb69ed7fSRuslan Bukin N25Q_UNLOCK(sc); 402eb69ed7fSRuslan Bukin } 403eb69ed7fSRuslan Bukin 404eb69ed7fSRuslan Bukin static int 405eb69ed7fSRuslan Bukin n25q_getattr(struct bio *bp) 406eb69ed7fSRuslan Bukin { 407eb69ed7fSRuslan Bukin struct n25q_softc *sc; 408eb69ed7fSRuslan Bukin device_t dev; 409eb69ed7fSRuslan Bukin 410eb69ed7fSRuslan Bukin if (bp->bio_disk == NULL || bp->bio_disk->d_drv1 == NULL) { 411eb69ed7fSRuslan Bukin return (ENXIO); 412eb69ed7fSRuslan Bukin } 413eb69ed7fSRuslan Bukin 414eb69ed7fSRuslan Bukin sc = bp->bio_disk->d_drv1; 415eb69ed7fSRuslan Bukin dev = sc->dev; 416eb69ed7fSRuslan Bukin 417eb69ed7fSRuslan Bukin if (strcmp(bp->bio_attribute, "SPI::device") == 0) { 418eb69ed7fSRuslan Bukin if (bp->bio_length != sizeof(dev)) { 419eb69ed7fSRuslan Bukin return (EFAULT); 420eb69ed7fSRuslan Bukin } 421eb69ed7fSRuslan Bukin bcopy(&dev, bp->bio_data, sizeof(dev)); 422eb69ed7fSRuslan Bukin return (0); 423eb69ed7fSRuslan Bukin } 424eb69ed7fSRuslan Bukin 425eb69ed7fSRuslan Bukin return (-1); 426eb69ed7fSRuslan Bukin } 427eb69ed7fSRuslan Bukin 428eb69ed7fSRuslan Bukin static void 429eb69ed7fSRuslan Bukin n25q_task(void *arg) 430eb69ed7fSRuslan Bukin { 431eb69ed7fSRuslan Bukin struct n25q_softc *sc; 432eb69ed7fSRuslan Bukin struct bio *bp; 433eb69ed7fSRuslan Bukin device_t dev; 434eb69ed7fSRuslan Bukin 435eb69ed7fSRuslan Bukin sc = (struct n25q_softc *)arg; 436eb69ed7fSRuslan Bukin 437eb69ed7fSRuslan Bukin dev = sc->dev; 438eb69ed7fSRuslan Bukin 439eb69ed7fSRuslan Bukin for (;;) { 440eb69ed7fSRuslan Bukin N25Q_LOCK(sc); 441eb69ed7fSRuslan Bukin do { 442eb69ed7fSRuslan Bukin if (sc->sc_taskstate == TSTATE_STOPPING) { 443eb69ed7fSRuslan Bukin sc->sc_taskstate = TSTATE_STOPPED; 444eb69ed7fSRuslan Bukin N25Q_UNLOCK(sc); 445eb69ed7fSRuslan Bukin wakeup(sc); 446eb69ed7fSRuslan Bukin kproc_exit(0); 447eb69ed7fSRuslan Bukin } 448eb69ed7fSRuslan Bukin bp = bioq_first(&sc->sc_bio_queue); 449eb69ed7fSRuslan Bukin if (bp == NULL) 450eb69ed7fSRuslan Bukin msleep(sc, &sc->sc_mtx, PRIBIO, "jobqueue", hz); 451eb69ed7fSRuslan Bukin } while (bp == NULL); 452eb69ed7fSRuslan Bukin bioq_remove(&sc->sc_bio_queue, bp); 453eb69ed7fSRuslan Bukin N25Q_UNLOCK(sc); 454eb69ed7fSRuslan Bukin 455eb69ed7fSRuslan Bukin switch (bp->bio_cmd) { 456eb69ed7fSRuslan Bukin case BIO_READ: 457eb69ed7fSRuslan Bukin bp->bio_error = n25q_read(dev, bp, bp->bio_offset, 458eb69ed7fSRuslan Bukin bp->bio_data, bp->bio_bcount); 459eb69ed7fSRuslan Bukin break; 460eb69ed7fSRuslan Bukin case BIO_WRITE: 461eb69ed7fSRuslan Bukin bp->bio_error = n25q_write(dev, bp, bp->bio_offset, 462eb69ed7fSRuslan Bukin bp->bio_data, bp->bio_bcount); 463eb69ed7fSRuslan Bukin break; 464eb69ed7fSRuslan Bukin default: 465*d176b803SScott Long bp->bio_error = EOPNOTSUPP; 466eb69ed7fSRuslan Bukin } 467eb69ed7fSRuslan Bukin 468eb69ed7fSRuslan Bukin biodone(bp); 469eb69ed7fSRuslan Bukin } 470eb69ed7fSRuslan Bukin } 471eb69ed7fSRuslan Bukin 472eb69ed7fSRuslan Bukin static devclass_t n25q_devclass; 473eb69ed7fSRuslan Bukin 474eb69ed7fSRuslan Bukin static device_method_t n25q_methods[] = { 475eb69ed7fSRuslan Bukin /* Device interface */ 476eb69ed7fSRuslan Bukin DEVMETHOD(device_probe, n25q_probe), 477eb69ed7fSRuslan Bukin DEVMETHOD(device_attach, n25q_attach), 478eb69ed7fSRuslan Bukin DEVMETHOD(device_detach, n25q_detach), 479eb69ed7fSRuslan Bukin 480eb69ed7fSRuslan Bukin { 0, 0 } 481eb69ed7fSRuslan Bukin }; 482eb69ed7fSRuslan Bukin 483eb69ed7fSRuslan Bukin static driver_t n25q_driver = { 484eb69ed7fSRuslan Bukin "n25q", 485eb69ed7fSRuslan Bukin n25q_methods, 486eb69ed7fSRuslan Bukin sizeof(struct n25q_softc), 487eb69ed7fSRuslan Bukin }; 488eb69ed7fSRuslan Bukin 489eb69ed7fSRuslan Bukin DRIVER_MODULE(n25q, simplebus, n25q_driver, n25q_devclass, 0, 0); 490