Home | History | Annotate | Line # | Download | only in mba
hp.c revision 1.46
      1 /*	$NetBSD: hp.c,v 1.46 2008/03/11 05:34:02 matt 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.46 2008/03/11 05:34:02 matt 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