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