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