1 /*- 2 * Copyright (c) 1999,2000 Jonathan Lemon 3 * All rights reserved. 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 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD$ 27 */ 28 29 /* 30 * Disk driver for Compaq SMART RAID adapters. 31 */ 32 33 #include <sys/param.h> 34 #include <sys/systm.h> 35 #include <sys/kernel.h> 36 37 #include <sys/bio.h> 38 #include <sys/bus.h> 39 #include <sys/conf.h> 40 #include <sys/cons.h> 41 #include <sys/devicestat.h> 42 #include <sys/disk.h> 43 44 #include <machine/bus_memio.h> 45 #include <machine/bus_pio.h> 46 #include <machine/bus.h> 47 #include <sys/rman.h> 48 49 #include <vm/vm.h> 50 #include <vm/pmap.h> 51 #include <machine/md_var.h> 52 53 #include <dev/ida/idareg.h> 54 #include <dev/ida/idavar.h> 55 56 /* prototypes */ 57 static int idad_probe(device_t dev); 58 static int idad_attach(device_t dev); 59 static int idad_detach(device_t dev); 60 61 static d_open_t idad_open; 62 static d_close_t idad_close; 63 static d_strategy_t idad_strategy; 64 static d_dump_t idad_dump; 65 66 #define IDAD_CDEV_MAJOR 109 67 68 static struct cdevsw id_cdevsw = { 69 /* open */ idad_open, 70 /* close */ idad_close, 71 /* read */ physread, 72 /* write */ physwrite, 73 /* ioctl */ noioctl, 74 /* poll */ nopoll, 75 /* mmap */ nommap, 76 /* strategy */ idad_strategy, 77 /* name */ "idad", 78 /* maj */ IDAD_CDEV_MAJOR, 79 /* dump */ idad_dump, 80 /* psize */ nopsize, 81 /* flags */ D_DISK, 82 }; 83 84 static devclass_t idad_devclass; 85 static struct cdevsw idaddisk_cdevsw; 86 static int disks_registered = 0; 87 88 static device_method_t idad_methods[] = { 89 DEVMETHOD(device_probe, idad_probe), 90 DEVMETHOD(device_attach, idad_attach), 91 DEVMETHOD(device_detach, idad_detach), 92 { 0, 0 } 93 }; 94 95 static driver_t idad_driver = { 96 "idad", 97 idad_methods, 98 sizeof(struct idad_softc) 99 }; 100 101 DRIVER_MODULE(idad, ida, idad_driver, idad_devclass, 0, 0); 102 103 static __inline struct idad_softc * 104 idad_getsoftc(dev_t dev) 105 { 106 107 return ((struct idad_softc *)dev->si_drv1); 108 } 109 110 static int 111 idad_open(dev_t dev, int flags, int fmt, struct thread *td) 112 { 113 struct idad_softc *drv; 114 115 drv = idad_getsoftc(dev); 116 if (drv == NULL) 117 return (ENXIO); 118 119 drv->disk.d_sectorsize = drv->secsize; 120 drv->disk.d_mediasize = (off_t)drv->secperunit * drv->secsize; 121 drv->disk.d_fwsectors = drv->sectors; 122 drv->disk.d_fwheads = drv->heads; 123 124 return (0); 125 } 126 127 static int 128 idad_close(dev_t dev, int flags, int fmt, struct thread *td) 129 { 130 struct idad_softc *drv; 131 132 drv = idad_getsoftc(dev); 133 if (drv == NULL) 134 return (ENXIO); 135 return (0); 136 } 137 138 /* 139 * Read/write routine for a buffer. Finds the proper unit, range checks 140 * arguments, and schedules the transfer. Does not wait for the transfer 141 * to complete. Multi-page transfers are supported. All I/O requests must 142 * be a multiple of a sector in length. 143 */ 144 static void 145 idad_strategy(struct bio *bp) 146 { 147 struct idad_softc *drv; 148 int s; 149 150 drv = idad_getsoftc(bp->bio_dev); 151 if (drv == NULL) { 152 bp->bio_error = EINVAL; 153 goto bad; 154 } 155 156 /* 157 * software write protect check 158 */ 159 if (drv->flags & DRV_WRITEPROT && (bp->bio_cmd == BIO_WRITE)) { 160 bp->bio_error = EROFS; 161 goto bad; 162 } 163 164 bp->bio_driver1 = drv; 165 s = splbio(); 166 devstat_start_transaction(&drv->stats); 167 ida_submit_buf(drv->controller, bp); 168 splx(s); 169 return; 170 171 bad: 172 bp->bio_flags |= BIO_ERROR; 173 174 /* 175 * Correctly set the buf to indicate a completed transfer 176 */ 177 bp->bio_resid = bp->bio_bcount; 178 biodone(bp); 179 return; 180 } 181 182 static int 183 idad_dump(dev_t dev, void *virtual, vm_offset_t physical, off_t offset, size_t length) 184 { 185 186 struct idad_softc *drv; 187 int error = 0; 188 189 drv = idad_getsoftc(dev); 190 if (drv == NULL) 191 return (ENXIO); 192 193 drv->controller->flags &= ~IDA_INTERRUPTS; 194 195 if (length > 0) { 196 error = ida_command(drv->controller, CMD_WRITE, virtual, 197 length, drv->drive, offset / DEV_BSIZE, DMA_DATA_OUT); 198 if (error) 199 goto out; 200 } 201 out: 202 drv->controller->flags |= IDA_INTERRUPTS; 203 return (error); 204 } 205 206 void 207 idad_intr(struct bio *bp) 208 { 209 struct idad_softc *drv = (struct idad_softc *)bp->bio_driver1; 210 211 if (bp->bio_flags & BIO_ERROR) 212 bp->bio_error = EIO; 213 else 214 bp->bio_resid = 0; 215 216 biofinish(bp, &drv->stats, 0); 217 } 218 219 static int 220 idad_probe(device_t dev) 221 { 222 223 device_set_desc(dev, "Compaq Logical Drive"); 224 return (0); 225 } 226 227 static int 228 idad_attach(device_t dev) 229 { 230 struct ida_drive_info dinfo; 231 struct idad_softc *drv; 232 device_t parent; 233 dev_t dsk; 234 int error; 235 236 drv = (struct idad_softc *)device_get_softc(dev); 237 parent = device_get_parent(dev); 238 drv->controller = (struct ida_softc *)device_get_softc(parent); 239 drv->unit = device_get_unit(dev); 240 drv->drive = drv->controller->num_drives; 241 drv->controller->num_drives++; 242 243 error = ida_command(drv->controller, CMD_GET_LOG_DRV_INFO, 244 &dinfo, sizeof(dinfo), drv->drive, 0, DMA_DATA_IN); 245 if (error) { 246 device_printf(dev, "CMD_GET_LOG_DRV_INFO failed\n"); 247 return (ENXIO); 248 } 249 250 drv->cylinders = dinfo.ncylinders; 251 drv->heads = dinfo.nheads; 252 drv->sectors = dinfo.nsectors; 253 drv->secsize = dinfo.secsize == 0 ? 512 : dinfo.secsize; 254 drv->secperunit = dinfo.secperunit; 255 256 /* XXX 257 * other initialization 258 */ 259 device_printf(dev, "%uMB (%u sectors), blocksize=%d\n", 260 drv->secperunit / ((1024 * 1024) / drv->secsize), 261 drv->secperunit, drv->secsize); 262 263 devstat_add_entry(&drv->stats, "idad", drv->unit, drv->secsize, 264 DEVSTAT_NO_ORDERED_TAGS, 265 DEVSTAT_TYPE_STORARRAY| DEVSTAT_TYPE_IF_OTHER, 266 DEVSTAT_PRIORITY_ARRAY); 267 268 dsk = disk_create(drv->unit, &drv->disk, 0, 269 &id_cdevsw, &idaddisk_cdevsw); 270 271 dsk->si_drv1 = drv; 272 dsk->si_iosize_max = DFLTPHYS; /* XXX guess? */ 273 disks_registered++; 274 275 return (0); 276 } 277 278 static int 279 idad_detach(device_t dev) 280 { 281 struct idad_softc *drv; 282 283 drv = (struct idad_softc *)device_get_softc(dev); 284 devstat_remove_entry(&drv->stats); 285 286 if (--disks_registered == 0) 287 cdevsw_remove(&idaddisk_cdevsw); 288 return (0); 289 } 290