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