1 /* $NetBSD: hp.c,v 1.48 2010/12/14 23:38:30 matt Exp $ */ 2 /* 3 * Copyright (c) 1996 Ludd, University of Lule}, Sweden. 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. All advertising materials mentioning features or use of this software 15 * must display the following acknowledgement: 16 * This product includes software developed at Ludd, University of 17 * Lule}, Sweden and its contributors. 18 * 4. The name of the author may not be used to endorse or promote products 19 * derived from this software without specific prior written permission 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 /* 34 * Simple device driver routine for massbuss disks. 35 * TODO: 36 * Fix support for Standard DEC BAD144 bad block forwarding. 37 * Be able to to handle soft/hard transfer errors. 38 * Handle non-data transfer interrupts. 39 * Autoconfiguration of disk drives 'on the fly'. 40 * Handle disk media changes. 41 * Dual-port operations should be supported. 42 */ 43 44 #include <sys/cdefs.h> 45 __KERNEL_RCSID(0, "$NetBSD: hp.c,v 1.48 2010/12/14 23:38:30 matt Exp $"); 46 47 #include <sys/param.h> 48 #include <sys/systm.h> 49 #include <sys/bus.h> 50 #include <sys/cpu.h> 51 #include <sys/device.h> 52 #include <sys/disklabel.h> 53 #include <sys/disk.h> 54 #include <sys/dkio.h> 55 #include <sys/buf.h> 56 #include <sys/bufq.h> 57 #include <sys/stat.h> 58 #include <sys/ioccom.h> 59 #include <sys/fcntl.h> 60 #include <sys/conf.h> 61 #include <sys/event.h> 62 #include <sys/syslog.h> 63 64 #include <vax/mba/mbavar.h> 65 #include <vax/mba/mbareg.h> 66 #include <vax/mba/hpreg.h> 67 68 #include "ioconf.h" 69 #include "locators.h" 70 71 struct hp_softc { 72 device_t sc_dev; 73 struct disk sc_disk; 74 bus_space_tag_t sc_iot; 75 bus_space_handle_t sc_ioh; 76 struct mba_device sc_md; /* Common struct used by mbaqueue. */ 77 int sc_wlabel; /* Disklabel area is writable */ 78 }; 79 80 int hpmatch(device_t, cfdata_t, void *); 81 void hpattach(device_t, device_t, void *); 82 void hpstart(struct mba_device *); 83 int hpattn(struct mba_device *); 84 enum xfer_action hpfinish(struct mba_device *, int, int *); 85 86 CFATTACH_DECL_NEW(hp, sizeof(struct hp_softc), 87 hpmatch, hpattach, NULL, NULL); 88 89 static dev_type_open(hpopen); 90 static dev_type_close(hpclose); 91 static dev_type_read(hpread); 92 static dev_type_write(hpwrite); 93 static dev_type_ioctl(hpioctl); 94 static dev_type_strategy(hpstrategy); 95 static dev_type_size(hppsize); 96 97 const struct bdevsw hp_bdevsw = { 98 .d_open = hpopen, 99 .d_close = hpclose, 100 .d_strategy = hpstrategy, 101 .d_ioctl = hpioctl, 102 .d_dump = nulldump, 103 .d_psize = hppsize, 104 .d_flag = D_DISK 105 }; 106 107 const struct cdevsw hp_cdevsw = { 108 .d_open = hpopen, 109 .d_close = hpclose, 110 .d_read = hpread, 111 .d_write = hpwrite, 112 .d_ioctl = hpioctl, 113 .d_stop = nostop, 114 .d_tty = notty, 115 .d_poll = nopoll, 116 .d_mmap = nommap, 117 .d_kqfilter = nokqfilter, 118 .d_flag = D_DISK 119 }; 120 121 #define HP_WCSR(reg, val) \ 122 bus_space_write_4(sc->sc_iot, sc->sc_ioh, (reg), (val)) 123 #define HP_RCSR(reg) \ 124 bus_space_read_4(sc->sc_iot, sc->sc_ioh, (reg)) 125 126 127 /* 128 * Check if this is a disk drive; done by checking type from mbaattach. 129 */ 130 int 131 hpmatch(device_t parent, cfdata_t cf, void *aux) 132 { 133 struct mba_attach_args * const ma = aux; 134 135 if (cf->cf_loc[MBACF_DRIVE] != MBACF_DRIVE_DEFAULT && 136 cf->cf_loc[MBACF_DRIVE] != ma->ma_unit) 137 return 0; 138 139 if (ma->ma_devtyp != MB_RP) 140 return 0; 141 142 return 1; 143 } 144 145 /* 146 * Disk drive found; fake a disklabel and try to read the real one. 147 * If the on-disk label can't be read; we lose. 148 */ 149 void 150 hpattach(device_t parent, device_t self, void *aux) 151 { 152 struct hp_softc * const sc = device_private(self); 153 struct mba_softc * const ms = device_private(parent); 154 struct mba_attach_args * const ma = aux; 155 struct disklabel *dl; 156 const char *msg; 157 158 sc->sc_dev = self; 159 sc->sc_iot = ma->ma_iot; 160 sc->sc_ioh = ma->ma_ioh; 161 162 /* 163 * Init the common struct for both the adapter and its slaves. 164 */ 165 bufq_alloc(&sc->sc_md.md_q, "disksort", BUFQ_SORT_CYLINDER); 166 sc->sc_md.md_softc = sc; /* Pointer to this softc */ 167 sc->sc_md.md_mba = ms; /* Pointer to parent softc */ 168 sc->sc_md.md_start = hpstart; /* Disk start routine */ 169 sc->sc_md.md_attn = hpattn; /* Disk attention routine */ 170 sc->sc_md.md_finish = hpfinish; /* Disk xfer finish routine */ 171 172 ms->sc_md[ma->ma_unit] = &sc->sc_md; /* Per-unit backpointer */ 173 174 /* 175 * Init and attach the disk structure. 176 */ 177 disk_init(&sc->sc_disk, device_xname(sc->sc_dev), NULL); 178 disk_attach(&sc->sc_disk); 179 180 /* 181 * Fake a disklabel to be able to read in the real label. 182 */ 183 dl = sc->sc_disk.dk_label; 184 185 dl->d_secsize = DEV_BSIZE; 186 dl->d_ntracks = 1; 187 dl->d_nsectors = 32; 188 dl->d_secpercyl = 32; 189 190 /* 191 * Read in label. 192 */ 193 if ((msg = readdisklabel(makedev(0, device_unit(self) * 8), hpstrategy, 194 dl, NULL)) != NULL) 195 printf(": %s", msg); 196 printf(": %s, size = %d sectors\n", dl->d_typename, dl->d_secperunit); 197 } 198 199 200 void 201 hpstrategy(struct buf *bp) 202 { 203 struct hp_softc *sc; 204 struct buf *gp; 205 struct disklabel *lp; 206 int unit, s, err; 207 208 unit = DISKUNIT(bp->b_dev); 209 sc = device_lookup_private(&hp_cd, unit); 210 lp = sc->sc_disk.dk_label; 211 212 err = bounds_check_with_label(&sc->sc_disk, bp, sc->sc_wlabel); 213 if (err <= 0) 214 goto done; 215 216 bp->b_rawblkno = 217 bp->b_blkno + lp->d_partitions[DISKPART(bp->b_dev)].p_offset; 218 bp->b_cylinder = bp->b_rawblkno / lp->d_secpercyl; 219 220 s = splbio(); 221 222 gp = bufq_peek(sc->sc_md.md_q); 223 bufq_put(sc->sc_md.md_q, bp); 224 if (gp == 0) 225 mbaqueue(&sc->sc_md); 226 227 splx(s); 228 return; 229 230 done: 231 bp->b_resid = bp->b_bcount; 232 biodone(bp); 233 } 234 235 /* 236 * Start transfer on given disk. Called from mbastart(). 237 */ 238 void 239 hpstart(struct mba_device *md) 240 { 241 struct hp_softc * const sc = md->md_softc; 242 struct disklabel * const lp = sc->sc_disk.dk_label; 243 struct buf *bp = bufq_peek(md->md_q); 244 unsigned bn, cn, sn, tn; 245 246 /* 247 * Collect statistics. 248 */ 249 disk_busy(&sc->sc_disk); 250 iostat_seek(sc->sc_disk.dk_stats); 251 252 bn = bp->b_rawblkno; 253 if (bn) { 254 cn = bn / lp->d_secpercyl; 255 sn = bn % lp->d_secpercyl; 256 tn = sn / lp->d_nsectors; 257 sn = sn % lp->d_nsectors; 258 } else 259 cn = sn = tn = 0; 260 261 HP_WCSR(HP_DC, cn); 262 HP_WCSR(HP_DA, (tn << 8) | sn); 263 if (bp->b_flags & B_READ) 264 HP_WCSR(HP_CS1, HPCS_READ); 265 else 266 HP_WCSR(HP_CS1, HPCS_WRITE); 267 } 268 269 int 270 hpopen(dev_t dev, int flag, int fmt, struct lwp *l) 271 { 272 struct hp_softc *sc; 273 int part = DISKPART(dev); 274 275 sc = device_lookup_private(&hp_cd, DISKUNIT(dev)); 276 if (sc == NULL) 277 return ENXIO; 278 279 if (part >= sc->sc_disk.dk_label->d_npartitions) 280 return ENXIO; 281 282 switch (fmt) { 283 case S_IFCHR: 284 sc->sc_disk.dk_copenmask |= (1 << part); 285 break; 286 287 case S_IFBLK: 288 sc->sc_disk.dk_bopenmask |= (1 << part); 289 break; 290 } 291 sc->sc_disk.dk_openmask = 292 sc->sc_disk.dk_copenmask | sc->sc_disk.dk_bopenmask; 293 294 return 0; 295 } 296 297 int 298 hpclose(dev_t dev, int flag, int fmt, struct lwp *l) 299 { 300 struct hp_softc * const sc = device_lookup_private(&hp_cd, DISKUNIT(dev)); 301 const int part = DISKPART(dev); 302 303 switch (fmt) { 304 case S_IFCHR: 305 sc->sc_disk.dk_copenmask &= ~(1 << part); 306 break; 307 308 case S_IFBLK: 309 sc->sc_disk.dk_bopenmask &= ~(1 << part); 310 break; 311 } 312 sc->sc_disk.dk_openmask = 313 sc->sc_disk.dk_copenmask | sc->sc_disk.dk_bopenmask; 314 315 return 0; 316 } 317 318 int 319 hpioctl(dev_t dev, u_long cmd, void *addr, int flag, struct lwp *l) 320 { 321 struct hp_softc * const sc = device_lookup_private(&hp_cd, DISKUNIT(dev)); 322 struct disklabel * const lp = sc->sc_disk.dk_label; 323 int error; 324 325 switch (cmd) { 326 case DIOCGDINFO: 327 *(struct disklabel *)addr = *lp; 328 return 0; 329 330 case DIOCGPART: 331 ((struct partinfo *)addr)->disklab = lp; 332 ((struct partinfo *)addr)->part = 333 &lp->d_partitions[DISKPART(dev)]; 334 break; 335 336 case DIOCSDINFO: 337 if ((flag & FWRITE) == 0) 338 return EBADF; 339 340 return setdisklabel(lp, (struct disklabel *)addr, 0, 0); 341 342 case DIOCWDINFO: 343 if ((flag & FWRITE) == 0) 344 error = EBADF; 345 else { 346 sc->sc_wlabel = 1; 347 error = writedisklabel(dev, hpstrategy, lp, 0); 348 sc->sc_wlabel = 0; 349 } 350 return error; 351 case DIOCWLABEL: 352 if ((flag & FWRITE) == 0) 353 return EBADF; 354 sc->sc_wlabel = 1; 355 break; 356 357 default: 358 return ENOTTY; 359 } 360 return 0; 361 } 362 363 /* 364 * Called when a transfer is finished. Check if transfer went OK, 365 * Return info about what-to-do-now. 366 */ 367 enum xfer_action 368 hpfinish(struct mba_device *md, int mbasr, int *attn) 369 { 370 struct hp_softc * const sc = md->md_softc; 371 struct buf *bp = bufq_peek(md->md_q); 372 int er1, er2, bc; 373 unsigned byte; 374 375 er1 = HP_RCSR(HP_ER1); 376 er2 = HP_RCSR(HP_ER2); 377 HP_WCSR(HP_ER1, 0); 378 HP_WCSR(HP_ER2, 0); 379 380 hper1: 381 switch (ffs(er1) - 1) { 382 case -1: 383 HP_WCSR(HP_ER1, 0); 384 goto hper2; 385 386 case HPER1_DCK: /* Corrected? data read. Just notice. */ 387 bc = bus_space_read_4(md->md_mba->sc_iot, 388 md->md_mba->sc_ioh, MBA_BC); 389 byte = ~(bc >> 16); 390 diskerr(bp, hp_cd.cd_name, "soft ecc", LOG_PRINTF, 391 btodb(bp->b_bcount - byte), sc->sc_disk.dk_label); 392 er1 &= ~(1<<HPER1_DCK); 393 break; 394 395 default: 396 aprint_error_dev(sc->sc_dev, "drive error: er1 %x er2 %x\n", 397 er1, er2); 398 HP_WCSR(HP_ER1, 0); 399 HP_WCSR(HP_ER2, 0); 400 goto hper2; 401 } 402 goto hper1; 403 404 hper2: 405 mbasr &= ~(MBASR_DTBUSY|MBASR_DTCMP|MBASR_ATTN); 406 if (mbasr) 407 aprint_error_dev(sc->sc_dev, "massbuss error: %x\n", mbasr); 408 409 bufq_peek(md->md_q)->b_resid = 0; 410 disk_unbusy(&sc->sc_disk, bufq_peek(md->md_q)->b_bcount, 411 (bp->b_flags & B_READ)); 412 return XFER_FINISH; 413 } 414 415 /* 416 * Non-data transfer interrupt; like volume change. 417 */ 418 int 419 hpattn(struct mba_device *md) 420 { 421 struct hp_softc * const sc = md->md_softc; 422 int er1, er2; 423 424 er1 = HP_RCSR(HP_ER1); 425 er2 = HP_RCSR(HP_ER2); 426 427 aprint_error_dev(sc->sc_dev, "Attention! er1 %x er2 %x\n", er1, er2); 428 return 0; 429 } 430 431 432 int 433 hppsize(dev_t dev) 434 { 435 struct hp_softc * const sc = device_lookup_private(&hp_cd, DISKUNIT(dev)); 436 const int part = DISKPART(dev); 437 438 if (sc == NULL || part >= sc->sc_disk.dk_label->d_npartitions) 439 return -1; 440 441 return sc->sc_disk.dk_label->d_partitions[part].p_size * 442 (sc->sc_disk.dk_label->d_secsize / DEV_BSIZE); 443 } 444 445 int 446 hpread(dev_t dev, struct uio *uio, int ioflag) 447 { 448 return (physio(hpstrategy, NULL, dev, B_READ, minphys, uio)); 449 } 450 451 int 452 hpwrite(dev_t dev, struct uio *uio, int ioflag) 453 { 454 return (physio(hpstrategy, NULL, dev, B_WRITE, minphys, uio)); 455 } 456