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