root/kern/dev/lamebus/lhd.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. lhd_rdreg
  2. lhd_wreg
  3. lhd_code_to_errno
  4. lhd_iodone
  5. lhd_irq
  6. lhd_open
  7. lhd_close
  8. lhd_ioctl
  9. lhd_reset
  10. lhd_io
  11. config_lhd

   1 /*
   2  * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009
   3  *      The President and Fellows of Harvard College.
   4  *
   5  * Redistribution and use in source and binary forms, with or without
   6  * modification, are permitted provided that the following conditions
   7  * are met:
   8  * 1. Redistributions of source code must retain the above copyright
   9  *    notice, this list of conditions and the following disclaimer.
  10  * 2. Redistributions in binary form must reproduce the above copyright
  11  *    notice, this list of conditions and the following disclaimer in the
  12  *    documentation and/or other materials provided with the distribution.
  13  * 3. Neither the name of the University nor the names of its contributors
  14  *    may be used to endorse or promote products derived from this software
  15  *    without specific prior written permission.
  16  *
  17  * THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
  18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
  21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  27  * SUCH DAMAGE.
  28  */
  29 
  30 /*
  31  * LAMEbus hard disk (lhd) driver.
  32  */
  33 
  34 #include <types.h>
  35 #include <kern/errno.h>
  36 #include <lib.h>
  37 #include <uio.h>
  38 #include <synch.h>
  39 #include <platform/bus.h>
  40 #include <vfs.h>
  41 #include <lamebus/lhd.h>
  42 #include "autoconf.h"
  43 
  44 /* Registers (offsets within slot) */
  45 #define LHD_REG_NSECT   0   /* Number of sectors */
  46 #define LHD_REG_STAT    4   /* Status */
  47 #define LHD_REG_SECT    8   /* Sector for I/O */
  48 #define LHD_REG_RPM     12  /* Disk rotation speed (revs per minute) */
  49 
  50 /* Status codes */
  51 #define LHD_IDLE        0   /* Device idle */
  52 #define LHD_WORKING     1   /* Operation in progress */
  53 #define LHD_OK          4   /* Operation succeeded */
  54 #define LHD_INVSECT     12  /* Invalid sector requested */
  55 #define LHD_MEDIA       20  /* Media error */
  56 #define LHD_ISWRITE     2   /* OR with above: I/O is a write */
  57 #define LHD_STATEMASK   0x1d  /* mask for masking out LHD_ISWRITE */
  58 
  59 /* Buffer (offset within slot)  */
  60 #define LHD_BUFFER      32768
  61 
  62 /*
  63  * Shortcut for reading a register.
  64  */
  65 static
  66 inline
  67 uint32_t lhd_rdreg(struct lhd_softc *lh, uint32_t reg)
  68 {
  69         return bus_read_register(lh->lh_busdata, lh->lh_buspos, reg);
  70 }
  71 
  72 /*
  73  * Shortcut for writing a register.
  74  */
  75 static
  76 inline
  77 void lhd_wreg(struct lhd_softc *lh, uint32_t reg, uint32_t val)
  78 {
  79         bus_write_register(lh->lh_busdata, lh->lh_buspos, reg, val);
  80 }
  81 
  82 /*
  83  * Convert a result code from the hardware to an errno value.
  84  */
  85 static
  86 int lhd_code_to_errno(struct lhd_softc *lh, int code)
  87 {
  88         switch (code & LHD_STATEMASK) {
  89             case LHD_OK: return 0;
  90             case LHD_INVSECT: return EINVAL;
  91             case LHD_MEDIA: return EIO;
  92         }
  93         kprintf("lhd%d: Unknown result code %d\n", lh->lh_unit, code);
  94         return EAGAIN;
  95 }
  96 
  97 /*
  98  * Record that an I/O has completed: save the result and poke the
  99  * completion semaphore.
 100  */
 101 static
 102 void
 103 lhd_iodone(struct lhd_softc *lh, int err)
 104 {
 105         lh->lh_result = err;
 106         V(lh->lh_done);
 107 }
 108 
 109 /*
 110  * Interrupt handler for lhd.
 111  * Read the status register; if an operation finished, clear the status
 112  * register and report completion.
 113  */
 114 void
 115 lhd_irq(void *vlh)
 116 {
 117         struct lhd_softc *lh = vlh;
 118         uint32_t val;
 119         
 120         val = lhd_rdreg(lh, LHD_REG_STAT);
 121 
 122         switch (val & LHD_STATEMASK) {
 123             case LHD_IDLE:
 124             case LHD_WORKING:
 125                 break;
 126             case LHD_OK:
 127             case LHD_INVSECT:
 128             case LHD_MEDIA:
 129                 lhd_wreg(lh, LHD_REG_STAT, 0);
 130                 lhd_iodone(lh, lhd_code_to_errno(lh, val));
 131                 break;
 132         }
 133 }
 134 
 135 /*
 136  * Function called when we are open()'d.
 137  */
 138 static
 139 int
 140 lhd_open(struct device *d, int openflags)
 141 {
 142         /*
 143          * Don't need to do anything.
 144          */
 145         (void)d;
 146         (void)openflags;
 147 
 148         return 0;
 149 }
 150 
 151 /*
 152  * Function called when we are close()'d.
 153  */
 154 static
 155 int
 156 lhd_close(struct device *d)
 157 {
 158         /*
 159          * Don't need to do anything.
 160          */
 161         (void)d;
 162 
 163         return 0;
 164 }
 165 
 166 /*
 167  * Function for handling ioctls.
 168  */
 169 static
 170 int
 171 lhd_ioctl(struct device *d, int op, userptr_t data)
 172 {
 173         /*
 174          * We don't support any ioctls.
 175          */
 176         (void)d;
 177         (void)op;
 178         (void)data;
 179         return EIOCTL;
 180 }
 181 
 182 #if 0
 183 /*
 184  * Reset the device.
 185  * This could be used, for instance, on timeout, if you implement suitable
 186  * facilities.
 187  */
 188 static
 189 void
 190 lhd_reset(struct lhd_softc *lh)
 191 {
 192         lhd_wreg(lh, LHD_REG_STAT, 0);
 193 }
 194 #endif
 195 
 196 /*
 197  * I/O function (for both reads and writes)
 198  */
 199 static
 200 int
 201 lhd_io(struct device *d, struct uio *uio)
 202 {
 203         struct lhd_softc *lh = d->d_data;
 204 
 205         uint32_t sector = uio->uio_offset / LHD_SECTSIZE;
 206         uint32_t sectoff = uio->uio_offset % LHD_SECTSIZE;
 207         uint32_t len = uio->uio_resid / LHD_SECTSIZE;
 208         uint32_t lenoff = uio->uio_resid % LHD_SECTSIZE;
 209         uint32_t i;
 210         uint32_t statval = LHD_WORKING;
 211         int result;
 212 
 213         /* Don't allow I/O that isn't sector-aligned. */
 214         if (sectoff != 0 || lenoff != 0) {
 215                 return EINVAL;
 216         }
 217 
 218         /* Don't allow I/O past the end of the disk. */
 219         if (sector+len > lh->lh_dev.d_blocks) {
 220                 return EINVAL;
 221         }
 222 
 223         /* Set up the value to write into the status register. */
 224         if (uio->uio_rw==UIO_WRITE) {
 225                 statval |= LHD_ISWRITE;
 226         }
 227 
 228         /* Loop over all the sectors we were asked to do. */
 229         for (i=0; i<len; i++) {
 230 
 231                 /* Wait until nobody else is using the device. */
 232                 P(lh->lh_clear);
 233 
 234                 /*
 235                  * Are we writing? If so, transfer the data to the
 236                  * on-card buffer.
 237                  */
 238                 if (uio->uio_rw == UIO_WRITE) {
 239                         result = uiomove(lh->lh_buf, LHD_SECTSIZE, uio);
 240                         if (result) {
 241                                 V(lh->lh_clear);
 242                                 return result;
 243                         }
 244                 }
 245 
 246                 /* Tell it what sector we want... */
 247                 lhd_wreg(lh, LHD_REG_SECT, sector+i);
 248 
 249                 /* and start the operation. */
 250                 lhd_wreg(lh, LHD_REG_STAT, statval);
 251 
 252                 /* Now wait until the interrupt handler tells us we're done. */
 253                 P(lh->lh_done);
 254 
 255                 /* Get the result value saved by the interrupt handler. */
 256                 result = lh->lh_result;
 257 
 258                 /*
 259                  * Are we reading? If so, and if we succeeded,
 260                  * transfer the data out of the on-card buffer.
 261                  */
 262                 if (result==0 && uio->uio_rw==UIO_READ) {
 263                         result = uiomove(lh->lh_buf, LHD_SECTSIZE, uio);
 264                 }
 265 
 266                 /* Tell another thread it's cleared to go ahead. */
 267                 V(lh->lh_clear);
 268 
 269                 /* If we failed, return the error. */
 270                 if (result) {
 271                         return result;
 272                 }
 273         }
 274 
 275         return 0;
 276 }
 277 
 278 /*
 279  * Setup routine called by autoconf.c when an lhd is found.
 280  */
 281 int
 282 config_lhd(struct lhd_softc *lh, int lhdno)
 283 {
 284         char name[32];
 285 
 286         /* Figure out what our name is. */
 287         snprintf(name, sizeof(name), "lhd%d", lhdno);
 288 
 289         /* Get a pointer to the on-chip buffer. */
 290         lh->lh_buf = bus_map_area(lh->lh_busdata, lh->lh_buspos, LHD_BUFFER);
 291 
 292         /* Create the semaphores. */
 293         lh->lh_clear = sem_create("lhd-clear", 1);
 294         if (lh->lh_clear == NULL) {
 295                 return ENOMEM;
 296         }
 297         lh->lh_done = sem_create("lhd-done", 0);
 298         if (lh->lh_done == NULL) {
 299                 sem_destroy(lh->lh_clear);
 300                 lh->lh_clear = NULL;
 301                 return ENOMEM;
 302         }
 303 
 304         /* Set up the VFS device structure. */
 305         lh->lh_dev.d_open = lhd_open;
 306         lh->lh_dev.d_close = lhd_close;
 307         lh->lh_dev.d_io = lhd_io;
 308         lh->lh_dev.d_ioctl = lhd_ioctl;
 309         lh->lh_dev.d_blocks = bus_read_register(lh->lh_busdata, lh->lh_buspos,
 310                                                 LHD_REG_NSECT);
 311         lh->lh_dev.d_blocksize = LHD_SECTSIZE;
 312         lh->lh_dev.d_data = lh;
 313 
 314         /* Add the VFS device structure to the VFS device list. */
 315         return vfs_adddev(name, &lh->lh_dev, 1);
 316 }

/* [<][>][^][v][top][bottom][index][help] */