at45d.c revision 164742
1164742Simp/*- 2164742Simp * Copyright (c) 2006 M. Warner Losh. All rights reserved. 3164742Simp * 4164742Simp * Redistribution and use in source and binary forms, with or without 5164742Simp * modification, are permitted provided that the following conditions 6164742Simp * are met: 7164742Simp * 1. Redistributions of source code must retain the above copyright 8164742Simp * notice, this list of conditions and the following disclaimer. 9164742Simp * 2. Redistributions in binary form must reproduce the above copyright 10164742Simp * notice, this list of conditions and the following disclaimer in the 11164742Simp * documentation and/or other materials provided with the distribution. 12164742Simp * 13164742Simp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 14164742Simp * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 15164742Simp * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 16164742Simp * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 17164742Simp * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 18164742Simp * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 19164742Simp * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 20164742Simp * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21164742Simp * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22164742Simp * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23164742Simp */ 24164742Simp 25164742Simp#include <sys/cdefs.h> 26164742Simp__FBSDID("$FreeBSD: head/sys/dev/flash/at45d.c 164742 2006-11-29 08:05:55Z imp $"); 27164742Simp 28164742Simp#include <sys/param.h> 29164742Simp#include <sys/systm.h> 30164742Simp#include <sys/bio.h> 31164742Simp#include <sys/bus.h> 32164742Simp#include <sys/conf.h> 33164742Simp#include <sys/gpio.h> 34164742Simp#include <sys/kernel.h> 35164742Simp#include <sys/kthread.h> 36164742Simp#include <sys/lock.h> 37164742Simp#include <sys/mbuf.h> 38164742Simp#include <sys/malloc.h> 39164742Simp#include <sys/module.h> 40164742Simp#include <sys/mutex.h> 41164742Simp#include <geom/geom_disk.h> 42164742Simp 43164742Simp#include <dev/spibus/spi.h> 44164742Simp#include "spibus_if.h" 45164742Simp 46164742Simpstruct at45d_softc 47164742Simp{ 48164742Simp struct intr_config_hook config_intrhook; 49164742Simp device_t dev; 50164742Simp struct mtx sc_mtx; 51164742Simp struct disk *disk; 52164742Simp struct proc *p; 53164742Simp struct bio_queue_head bio_queue; 54164742Simp}; 55164742Simp 56164742Simp#define AT45D_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) 57164742Simp#define AT45D_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) 58164742Simp#define AT45D_LOCK_INIT(_sc) \ 59164742Simp mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->dev), \ 60164742Simp "at45d", MTX_DEF) 61164742Simp#define AT45D_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); 62164742Simp#define AT45D_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED); 63164742Simp#define AT45D_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED); 64164742Simp 65164742Simpstatic void at45d_delayed_attach(void *xsc); 66164742Simp 67164742Simp/* disk routines */ 68164742Simpstatic int at45d_open(struct disk *dp); 69164742Simpstatic int at45d_close(struct disk *dp); 70164742Simpstatic void at45d_strategy(struct bio *bp); 71164742Simpstatic void at45d_task(void *arg); 72164742Simp 73164742Simp#define CONTINUOUS_ARRAY_READ 0xE8 74164742Simp#define CONTINUOUS_ARRAY_READ_HF 0x0B 75164742Simp#define CONTINUOUS_ARRAY_READ_LF 0x03 76164742Simp#define STATUS_REGISTER_READ 0xD7 77164742Simp#define PROGRAM_THROUGH_BUFFER 0x82 78164742Simp#define MANUFACTURER_ID 0x9F 79164742Simp 80164742Simpstatic uint8_t 81164742Simpat45d_get_status(device_t dev) 82164742Simp{ 83164742Simp uint8_t txBuf[8], rxBuf[8]; 84164742Simp struct spi_command cmd; 85164742Simp int err; 86164742Simp 87164742Simp memset(&cmd, 0, sizeof(cmd)); 88164742Simp memset(txBuf, 0, sizeof(txBuf)); 89164742Simp memset(rxBuf, 0, sizeof(rxBuf)); 90164742Simp 91164742Simp txBuf[0] = STATUS_REGISTER_READ; 92164742Simp cmd.tx_cmd = txBuf; 93164742Simp cmd.rx_cmd = rxBuf; 94164742Simp cmd.rx_cmd_sz = 2; 95164742Simp cmd.tx_cmd_sz = 2; 96164742Simp err = SPIBUS_TRANSFER(device_get_parent(dev), dev, &cmd); 97164742Simp return (rxBuf[1]); 98164742Simp} 99164742Simp 100164742Simpstatic void 101164742Simpat45d_wait_for_device_ready(device_t dev) 102164742Simp{ 103164742Simp while (!(at45d_get_status(dev) & 0x80)) 104164742Simp continue; 105164742Simp} 106164742Simp 107164742Simpstatic int 108164742Simpat45d_get_mfg_info(device_t dev, uint8_t *resp) 109164742Simp{ 110164742Simp uint8_t txBuf[8], rxBuf[8]; 111164742Simp struct spi_command cmd; 112164742Simp int err; 113164742Simp 114164742Simp memset(&cmd, 0, sizeof(cmd)); 115164742Simp memset(txBuf, 0, sizeof(txBuf)); 116164742Simp memset(rxBuf, 0, sizeof(rxBuf)); 117164742Simp 118164742Simp txBuf[0] = MANUFACTURER_ID; 119164742Simp cmd.tx_cmd = &txBuf; 120164742Simp cmd.rx_cmd = &rxBuf; 121164742Simp cmd.tx_cmd_sz = 5; 122164742Simp cmd.rx_cmd_sz = 5; 123164742Simp err = SPIBUS_TRANSFER(device_get_parent(dev), dev, &cmd); 124164742Simp if (err) 125164742Simp return (err); 126164742Simp memcpy(resp, rxBuf + 1, 4); 127164742Simp // XXX We really should 'decode' the reply into some kind of 128164742Simp // XXX structure. To be generic (and not just for atmel parts) 129164742Simp // XXX we'd have to loop until we got a full reply. 130164742Simp return (0); 131164742Simp} 132164742Simp 133164742Simpstatic int 134164742Simpat45d_probe(device_t dev) 135164742Simp{ 136164742Simp device_set_desc(dev, "AT45 Flash Family"); 137164742Simp return (0); 138164742Simp} 139164742Simp 140164742Simpstatic int 141164742Simpat45d_attach(device_t dev) 142164742Simp{ 143164742Simp struct at45d_softc *sc; 144164742Simp 145164742Simp sc = device_get_softc(dev); 146164742Simp sc->dev = dev; 147164742Simp AT45D_LOCK_INIT(sc); 148164742Simp 149164742Simp /* We'll see what kind of flash we have later... */ 150164742Simp sc->config_intrhook.ich_func = at45d_delayed_attach; 151164742Simp sc->config_intrhook.ich_arg = sc; 152164742Simp if (config_intrhook_establish(&sc->config_intrhook) != 0) 153164742Simp device_printf(dev, "config_intrhook_establish failed\n"); 154164742Simp return (0); 155164742Simp} 156164742Simp 157164742Simpstatic int 158164742Simpat45d_detach(device_t dev) 159164742Simp{ 160164742Simp return EIO; 161164742Simp} 162164742Simp 163164742Simpstatic void 164164742Simpat45d_delayed_attach(void *xsc) 165164742Simp{ 166164742Simp struct at45d_softc *sc = xsc; 167164742Simp uint8_t buf[4]; 168164742Simp 169164742Simp at45d_get_mfg_info(sc->dev, buf); 170164742Simp at45d_wait_for_device_ready(sc->dev); 171164742Simp 172164742Simp sc->disk = disk_alloc(); 173164742Simp sc->disk->d_open = at45d_open; 174164742Simp sc->disk->d_close = at45d_close; 175164742Simp sc->disk->d_strategy = at45d_strategy; 176164742Simp sc->disk->d_name = "flash/spi"; 177164742Simp sc->disk->d_drv1 = sc; 178164742Simp sc->disk->d_maxsize = DFLTPHYS; 179164742Simp sc->disk->d_sectorsize = 1056; /* XXX */ 180164742Simp sc->disk->d_mediasize = 8192 * 1056; /* XXX */ 181164742Simp sc->disk->d_unit = device_get_unit(sc->dev); 182164742Simp disk_create(sc->disk, DISK_VERSION); 183164742Simp bioq_init(&sc->bio_queue); 184164742Simp kthread_create(&at45d_task, sc, &sc->p, 0, 0, "task: at45d flash"); 185164742Simp 186164742Simp config_intrhook_disestablish(&sc->config_intrhook); 187164742Simp} 188164742Simp 189164742Simpstatic int 190164742Simpat45d_open(struct disk *dp) 191164742Simp{ 192164742Simp return 0; 193164742Simp} 194164742Simp 195164742Simpstatic int 196164742Simpat45d_close(struct disk *dp) 197164742Simp{ 198164742Simp return 0; 199164742Simp} 200164742Simp 201164742Simpstatic void 202164742Simpat45d_strategy(struct bio *bp) 203164742Simp{ 204164742Simp struct at45d_softc *sc; 205164742Simp 206164742Simp sc = (struct at45d_softc *)bp->bio_disk->d_drv1; 207164742Simp AT45D_LOCK(sc); 208164742Simp bioq_disksort(&sc->bio_queue, bp); 209164742Simp wakeup(sc); 210164742Simp AT45D_UNLOCK(sc); 211164742Simp} 212164742Simp 213164742Simpstatic void 214164742Simpat45d_task(void *arg) 215164742Simp{ 216164742Simp struct at45d_softc *sc = (struct at45d_softc*)arg; 217164742Simp struct bio *bp; 218164742Simp uint8_t txBuf[8], rxBuf[8]; 219164742Simp struct spi_command cmd; 220164742Simp int sz; 221164742Simp daddr_t block, end; 222164742Simp device_t dev, pdev; 223164742Simp int err; 224164742Simp 225164742Simp for (;;) { 226164742Simp dev = sc->dev; 227164742Simp pdev = device_get_parent(dev); 228164742Simp AT45D_LOCK(sc); 229164742Simp do { 230164742Simp bp = bioq_first(&sc->bio_queue); 231164742Simp if (bp == NULL) 232164742Simp msleep(sc, &sc->sc_mtx, PRIBIO, "jobqueue", 0); 233164742Simp } while (bp == NULL); 234164742Simp bioq_remove(&sc->bio_queue, bp); 235164742Simp AT45D_UNLOCK(sc); 236164742Simp sz = sc->disk->d_sectorsize; 237164742Simp end = bp->bio_pblkno + (bp->bio_bcount / sz); 238164742Simp for (block = bp->bio_pblkno; block < end; block++) { 239164742Simp char *vaddr = bp->bio_data + (block - bp->bio_pblkno) * sz; 240164742Simp if (bp->bio_cmd == BIO_READ) { 241164742Simp txBuf[0] = CONTINUOUS_ARRAY_READ_HF; 242164742Simp cmd.tx_cmd_sz = 5; 243164742Simp cmd.rx_cmd_sz = 5; 244164742Simp } else { 245164742Simp txBuf[0] = PROGRAM_THROUGH_BUFFER; 246164742Simp cmd.tx_cmd_sz = 4; 247164742Simp cmd.rx_cmd_sz = 4; 248164742Simp } 249164742Simp // XXX only works on certain devices... Fixme 250164742Simp txBuf[1] = ((block >> 5) & 0xFF); 251164742Simp txBuf[2] = ((block << 3) & 0xF8); 252164742Simp txBuf[3] = 0; 253164742Simp txBuf[4] = 0; 254164742Simp cmd.tx_cmd = txBuf; 255164742Simp cmd.rx_cmd = rxBuf; 256164742Simp cmd.tx_data = vaddr; 257164742Simp cmd.tx_data_sz = sz; 258164742Simp cmd.rx_data = vaddr; 259164742Simp cmd.rx_data_sz = sz; 260164742Simp err = SPIBUS_TRANSFER(pdev, dev, &cmd); 261164742Simp // XXX err check? 262164742Simp } 263164742Simp biodone(bp); 264164742Simp } 265164742Simp} 266164742Simp 267164742Simpstatic devclass_t at45d_devclass; 268164742Simp 269164742Simpstatic device_method_t at45d_methods[] = { 270164742Simp /* Device interface */ 271164742Simp DEVMETHOD(device_probe, at45d_probe), 272164742Simp DEVMETHOD(device_attach, at45d_attach), 273164742Simp DEVMETHOD(device_detach, at45d_detach), 274164742Simp 275164742Simp { 0, 0 } 276164742Simp}; 277164742Simp 278164742Simpstatic driver_t at45d_driver = { 279164742Simp "at45d", 280164742Simp at45d_methods, 281164742Simp sizeof(struct at45d_softc), 282164742Simp}; 283164742Simp 284164742SimpDRIVER_MODULE(at45d, spibus, at45d_driver, at45d_devclass, 0, 0); 285