1 /* $NetBSD: hp.c,v 1.31 2002/11/01 11:31:55 mrg 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 #include <sys/param.h> 44 #include <sys/systm.h> 45 #include <sys/device.h> 46 #include <sys/disklabel.h> 47 #include <sys/disk.h> 48 #include <sys/dkio.h> 49 #include <sys/buf.h> 50 #include <sys/stat.h> 51 #include <sys/ioccom.h> 52 #include <sys/fcntl.h> 53 #include <sys/syslog.h> 54 #include <sys/reboot.h> 55 #include <sys/conf.h> 56 #include <sys/event.h> 57 58 #include <machine/bus.h> 59 #include <machine/trap.h> 60 #include <machine/pte.h> 61 #include <machine/mtpr.h> 62 #include <machine/cpu.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 struct device 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(struct device *, struct cfdata *, void *); 81 void hpattach(struct device *, struct device *, 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(hp, sizeof(struct hp_softc), 87 hpmatch, hpattach, NULL, NULL); 88 89 dev_type_open(hpopen); 90 dev_type_close(hpclose); 91 dev_type_read(hpread); 92 dev_type_write(hpwrite); 93 dev_type_ioctl(hpioctl); 94 dev_type_strategy(hpstrategy); 95 dev_type_size(hpsize); 96 97 const struct bdevsw hp_bdevsw = { 98 hpopen, hpclose, hpstrategy, hpioctl, nulldump, hpsize, D_DISK 99 }; 100 101 const struct cdevsw hp_cdevsw = { 102 hpopen, hpclose, hpread, hpwrite, hpioctl, 103 nostop, notty, nopoll, nommap, nokqfilter, D_DISK 104 }; 105 106 #define HP_WCSR(reg, val) \ 107 bus_space_write_4(sc->sc_iot, sc->sc_ioh, (reg), (val)) 108 #define HP_RCSR(reg) \ 109 bus_space_read_4(sc->sc_iot, sc->sc_ioh, (reg)) 110 111 112 /* 113 * Check if this is a disk drive; done by checking type from mbaattach. 114 */ 115 int 116 hpmatch(struct device *parent, struct cfdata *cf, void *aux) 117 { 118 struct mba_attach_args *ma = aux; 119 120 if (cf->cf_loc[MBACF_DRIVE] != MBACF_DRIVE_DEFAULT && 121 cf->cf_loc[MBACF_DRIVE] != ma->ma_unit) 122 return 0; 123 124 if (ma->ma_devtyp != MB_RP) 125 return 0; 126 127 return 1; 128 } 129 130 /* 131 * Disk drive found; fake a disklabel and try to read the real one. 132 * If the on-disk label can't be read; we lose. 133 */ 134 void 135 hpattach(struct device *parent, struct device *self, void *aux) 136 { 137 struct hp_softc *sc = (void *)self; 138 struct mba_softc *ms = (void *)parent; 139 struct disklabel *dl; 140 struct mba_attach_args *ma = aux; 141 char *msg; 142 143 sc->sc_iot = ma->ma_iot; 144 sc->sc_ioh = ma->ma_ioh; 145 /* 146 * Init the common struct for both the adapter and its slaves. 147 */ 148 bufq_alloc(&sc->sc_md.md_q, BUFQ_DISKSORT|BUFQ_SORT_CYLINDER); 149 sc->sc_md.md_softc = (void *)sc; /* Pointer to this softc */ 150 sc->sc_md.md_mba = (void *)parent; /* Pointer to parent softc */ 151 sc->sc_md.md_start = hpstart; /* Disk start routine */ 152 sc->sc_md.md_attn = hpattn; /* Disk attention routine */ 153 sc->sc_md.md_finish = hpfinish; /* Disk xfer finish routine */ 154 155 ms->sc_md[ma->ma_unit] = &sc->sc_md; /* Per-unit backpointer */ 156 157 /* 158 * Init and attach the disk structure. 159 */ 160 sc->sc_disk.dk_name = sc->sc_dev.dv_xname; 161 disk_attach(&sc->sc_disk); 162 163 /* 164 * Fake a disklabel to be able to read in the real label. 165 */ 166 dl = sc->sc_disk.dk_label; 167 168 dl->d_secsize = DEV_BSIZE; 169 dl->d_ntracks = 1; 170 dl->d_nsectors = 32; 171 dl->d_secpercyl = 32; 172 173 /* 174 * Read in label. 175 */ 176 if ((msg = readdisklabel(makedev(0, self->dv_unit * 8), hpstrategy, 177 dl, NULL)) != NULL) 178 printf(": %s", msg); 179 printf(": %s, size = %d sectors\n", dl->d_typename, dl->d_secperunit); 180 } 181 182 183 void 184 hpstrategy(struct buf *bp) 185 { 186 struct hp_softc *sc; 187 struct buf *gp; 188 int unit, s, err; 189 struct disklabel *lp; 190 191 unit = DISKUNIT(bp->b_dev); 192 sc = hp_cd.cd_devs[unit]; 193 lp = sc->sc_disk.dk_label; 194 195 err = bounds_check_with_label(bp, lp, sc->sc_wlabel); 196 if (err < 0) 197 goto done; 198 199 bp->b_rawblkno = 200 bp->b_blkno + lp->d_partitions[DISKPART(bp->b_dev)].p_offset; 201 bp->b_cylinder = bp->b_rawblkno / lp->d_secpercyl; 202 203 s = splbio(); 204 205 gp = BUFQ_PEEK(&sc->sc_md.md_q); 206 BUFQ_PUT(&sc->sc_md.md_q, bp); 207 if (gp == 0) 208 mbaqueue(&sc->sc_md); 209 210 splx(s); 211 return; 212 213 done: 214 bp->b_resid = bp->b_bcount; 215 biodone(bp); 216 } 217 218 /* 219 * Start transfer on given disk. Called from mbastart(). 220 */ 221 void 222 hpstart(struct mba_device *md) 223 { 224 struct hp_softc *sc = md->md_softc; 225 struct disklabel *lp = sc->sc_disk.dk_label; 226 struct buf *bp = BUFQ_PEEK(&md->md_q); 227 unsigned bn, cn, sn, tn; 228 229 /* 230 * Collect statistics. 231 */ 232 disk_busy(&sc->sc_disk); 233 sc->sc_disk.dk_seek++; 234 235 bn = bp->b_rawblkno; 236 if (bn) { 237 cn = bn / lp->d_secpercyl; 238 sn = bn % lp->d_secpercyl; 239 tn = sn / lp->d_nsectors; 240 sn = sn % lp->d_nsectors; 241 } else 242 cn = sn = tn = 0; 243 244 HP_WCSR(HP_DC, cn); 245 HP_WCSR(HP_DA, (tn << 8) | sn); 246 if (bp->b_flags & B_READ) 247 HP_WCSR(HP_CS1, HPCS_READ); 248 else 249 HP_WCSR(HP_CS1, HPCS_WRITE); 250 } 251 252 int 253 hpopen(dev_t dev, int flag, int fmt, struct proc *p) 254 { 255 struct hp_softc *sc; 256 int unit, part; 257 258 unit = DISKUNIT(dev); 259 if (unit >= hp_cd.cd_ndevs) 260 return ENXIO; 261 sc = hp_cd.cd_devs[unit]; 262 if (sc == 0) 263 return ENXIO; 264 265 part = DISKPART(dev); 266 267 if (part >= sc->sc_disk.dk_label->d_npartitions) 268 return ENXIO; 269 270 switch (fmt) { 271 case S_IFCHR: 272 sc->sc_disk.dk_copenmask |= (1 << part); 273 break; 274 275 case S_IFBLK: 276 sc->sc_disk.dk_bopenmask |= (1 << part); 277 break; 278 } 279 sc->sc_disk.dk_openmask = 280 sc->sc_disk.dk_copenmask | sc->sc_disk.dk_bopenmask; 281 282 return 0; 283 } 284 285 int 286 hpclose(dev_t dev, int flag, int fmt, struct proc *p) 287 { 288 struct hp_softc *sc; 289 int unit, part; 290 291 unit = DISKUNIT(dev); 292 sc = hp_cd.cd_devs[unit]; 293 294 part = DISKPART(dev); 295 296 switch (fmt) { 297 case S_IFCHR: 298 sc->sc_disk.dk_copenmask &= ~(1 << part); 299 break; 300 301 case S_IFBLK: 302 sc->sc_disk.dk_bopenmask &= ~(1 << part); 303 break; 304 } 305 sc->sc_disk.dk_openmask = 306 sc->sc_disk.dk_copenmask | sc->sc_disk.dk_bopenmask; 307 308 return 0; 309 } 310 311 int 312 hpioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p) 313 { 314 struct hp_softc *sc = hp_cd.cd_devs[DISKUNIT(dev)]; 315 struct disklabel *lp = sc->sc_disk.dk_label; 316 int error; 317 318 switch (cmd) { 319 case DIOCGDINFO: 320 bcopy(lp, addr, sizeof (struct disklabel)); 321 return 0; 322 323 case DIOCGPART: 324 ((struct partinfo *)addr)->disklab = lp; 325 ((struct partinfo *)addr)->part = 326 &lp->d_partitions[DISKPART(dev)]; 327 break; 328 329 case DIOCSDINFO: 330 if ((flag & FWRITE) == 0) 331 return EBADF; 332 333 return setdisklabel(lp, (struct disklabel *)addr, 0, 0); 334 335 case DIOCWDINFO: 336 if ((flag & FWRITE) == 0) 337 error = EBADF; 338 else { 339 sc->sc_wlabel = 1; 340 error = writedisklabel(dev, hpstrategy, lp, 0); 341 sc->sc_wlabel = 0; 342 } 343 return error; 344 case DIOCWLABEL: 345 if ((flag & FWRITE) == 0) 346 return EBADF; 347 sc->sc_wlabel = 1; 348 break; 349 350 default: 351 return ENOTTY; 352 } 353 return 0; 354 } 355 356 /* 357 * Called when a transfer is finished. Check if transfer went OK, 358 * Return info about what-to-do-now. 359 */ 360 enum xfer_action 361 hpfinish(struct mba_device *md, int mbasr, int *attn) 362 { 363 struct hp_softc *sc = md->md_softc; 364 struct buf *bp = BUFQ_PEEK(&md->md_q); 365 int er1, er2, bc; 366 unsigned byte; 367 368 er1 = HP_RCSR(HP_ER1); 369 er2 = HP_RCSR(HP_ER2); 370 HP_WCSR(HP_ER1, 0); 371 HP_WCSR(HP_ER2, 0); 372 373 hper1: 374 switch (ffs(er1) - 1) { 375 case -1: 376 HP_WCSR(HP_ER1, 0); 377 goto hper2; 378 379 case HPER1_DCK: /* Corrected? data read. Just notice. */ 380 bc = bus_space_read_4(md->md_mba->sc_iot, 381 md->md_mba->sc_ioh, MBA_BC); 382 byte = ~(bc >> 16); 383 diskerr(buf, hp_cd.cd_name, "soft ecc", LOG_PRINTF, 384 btodb(bp->b_bcount - byte), sc->sc_disk.dk_label); 385 er1 &= ~(1<<HPER1_DCK); 386 break; 387 388 default: 389 printf("drive error :%s er1 %x er2 %x\n", 390 sc->sc_dev.dv_xname, er1, er2); 391 HP_WCSR(HP_ER1, 0); 392 HP_WCSR(HP_ER2, 0); 393 goto hper2; 394 } 395 goto hper1; 396 397 hper2: 398 mbasr &= ~(MBASR_DTBUSY|MBASR_DTCMP|MBASR_ATTN); 399 if (mbasr) 400 printf("massbuss error :%s %x\n", 401 sc->sc_dev.dv_xname, mbasr); 402 403 BUFQ_PEEK(&md->md_q)->b_resid = 0; 404 disk_unbusy(&sc->sc_disk, BUFQ_PEEK(&md->md_q)->b_bcount, 405 (bp->b_flags & B_READ)); 406 return XFER_FINISH; 407 } 408 409 /* 410 * Non-data transfer interrupt; like volume change. 411 */ 412 int 413 hpattn(struct mba_device *md) 414 { 415 struct hp_softc *sc = md->md_softc; 416 int er1, er2; 417 418 er1 = HP_RCSR(HP_ER1); 419 er2 = HP_RCSR(HP_ER2); 420 421 printf("%s: Attention! er1 %x er2 %x\n", 422 sc->sc_dev.dv_xname, er1, er2); 423 return 0; 424 } 425 426 427 int 428 hpsize(dev_t dev) 429 { 430 int size, unit = DISKUNIT(dev); 431 struct hp_softc *sc; 432 433 if (unit >= hp_cd.cd_ndevs || hp_cd.cd_devs[unit] == 0) 434 return -1; 435 436 sc = hp_cd.cd_devs[unit]; 437 size = sc->sc_disk.dk_label->d_partitions[DISKPART(dev)].p_size * 438 (sc->sc_disk.dk_label->d_secsize / DEV_BSIZE); 439 440 return size; 441 } 442 443 int 444 hpread(dev_t dev, struct uio *uio, int ioflag) 445 { 446 return (physio(hpstrategy, NULL, dev, B_READ, minphys, uio)); 447 } 448 449 int 450 hpwrite(dev_t dev, struct uio *uio, int ioflag) 451 { 452 return (physio(hpstrategy, NULL, dev, B_WRITE, minphys, uio)); 453 } 454