xref: /netbsd-src/sys/dev/qbus/rl.c (revision c7fb772b85b2b5d4cfb282f868f454b4701534fd)
1*c7fb772bSthorpej /*	$NetBSD: rl.c,v 1.53 2021/08/07 16:19:15 thorpej Exp $	*/
211379e0aSragge 
311379e0aSragge /*
411379e0aSragge  * Copyright (c) 2000 Ludd, University of Lule}, Sweden. All rights reserved.
511379e0aSragge  *
611379e0aSragge  * Redistribution and use in source and binary forms, with or without
711379e0aSragge  * modification, are permitted provided that the following conditions
811379e0aSragge  * are met:
911379e0aSragge  * 1. Redistributions of source code must retain the above copyright
1011379e0aSragge  *    notice, this list of conditions and the following disclaimer.
1111379e0aSragge  * 2. Redistributions in binary form must reproduce the above copyright
1211379e0aSragge  *    notice, this list of conditions and the following disclaimer in the
1311379e0aSragge  *    documentation and/or other materials provided with the distribution.
1411379e0aSragge  *
1511379e0aSragge  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1611379e0aSragge  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1711379e0aSragge  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1811379e0aSragge  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1911379e0aSragge  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2011379e0aSragge  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2111379e0aSragge  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2211379e0aSragge  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2311379e0aSragge  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2411379e0aSragge  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2511379e0aSragge  */
2611379e0aSragge 
2711379e0aSragge /*
2811379e0aSragge  * RL11/RLV11/RLV12 disk controller driver and
2911379e0aSragge  * RL01/RL02 disk device driver.
3011379e0aSragge  *
3111379e0aSragge  * TODO:
3211379e0aSragge  *	Handle disk errors more gracefully
3311379e0aSragge  *	Do overlapping seeks on multiple drives
3411379e0aSragge  *
3511379e0aSragge  * Implementation comments:
3611379e0aSragge  *
3711379e0aSragge  */
3811379e0aSragge 
39a3746e00Slukem #include <sys/cdefs.h>
40*c7fb772bSthorpej __KERNEL_RCSID(0, "$NetBSD: rl.c,v 1.53 2021/08/07 16:19:15 thorpej Exp $");
41a3746e00Slukem 
4211379e0aSragge #include <sys/param.h>
4311379e0aSragge #include <sys/device.h>
4411379e0aSragge #include <sys/systm.h>
4511379e0aSragge #include <sys/conf.h>
4611379e0aSragge #include <sys/disk.h>
4711379e0aSragge #include <sys/disklabel.h>
4811379e0aSragge #include <sys/buf.h>
4905f25dccSyamt #include <sys/bufq.h>
5011379e0aSragge #include <sys/stat.h>
5111379e0aSragge #include <sys/dkio.h>
5211379e0aSragge #include <sys/fcntl.h>
53e0cc03a0Sjdolecek #include <sys/event.h>
5411379e0aSragge 
5511379e0aSragge #include <ufs/ufs/dinode.h>
5611379e0aSragge #include <ufs/ffs/fs.h>
5711379e0aSragge 
58a2a38285Sad #include <sys/bus.h>
5911379e0aSragge 
6011379e0aSragge #include <dev/qbus/ubavar.h>
6111379e0aSragge #include <dev/qbus/rlreg.h>
62f3082cc9Sragge #include <dev/qbus/rlvar.h>
6311379e0aSragge 
6411379e0aSragge #include "ioconf.h"
6511379e0aSragge #include "locators.h"
6611379e0aSragge 
67dfba8166Smatt static	int rlcmatch(device_t, cfdata_t, void *);
68dfba8166Smatt static	void rlcattach(device_t, device_t, void *);
6911379e0aSragge static	int rlcprint(void *, const char *);
7011379e0aSragge static	void rlcintr(void *);
71dfba8166Smatt static	int rlmatch(device_t, cfdata_t, void *);
72dfba8166Smatt static	void rlattach(device_t, device_t, void *);
7311379e0aSragge static	void rlcstart(struct rlc_softc *, struct buf *);
7411379e0aSragge static	void waitcrdy(struct rlc_softc *);
75dfba8166Smatt static	void rlcreset(device_t);
7611379e0aSragge 
77dfba8166Smatt CFATTACH_DECL_NEW(rlc, sizeof(struct rlc_softc),
78b75a007dSthorpej     rlcmatch, rlcattach, NULL, NULL);
7911379e0aSragge 
80dfba8166Smatt CFATTACH_DECL_NEW(rl, sizeof(struct rl_softc),
81b75a007dSthorpej     rlmatch, rlattach, NULL, NULL);
8211379e0aSragge 
83dfba8166Smatt static dev_type_open(rlopen);
84dfba8166Smatt static dev_type_close(rlclose);
85dfba8166Smatt static dev_type_read(rlread);
86dfba8166Smatt static dev_type_write(rlwrite);
87dfba8166Smatt static dev_type_ioctl(rlioctl);
88dfba8166Smatt static dev_type_strategy(rlstrategy);
89dfba8166Smatt static dev_type_dump(rldump);
90dfba8166Smatt static dev_type_size(rlpsize);
9177a6b82bSgehenna 
9277a6b82bSgehenna const struct bdevsw rl_bdevsw = {
93a68f9396Sdholland 	.d_open = rlopen,
94a68f9396Sdholland 	.d_close = rlclose,
95a68f9396Sdholland 	.d_strategy = rlstrategy,
96a68f9396Sdholland 	.d_ioctl = rlioctl,
97a68f9396Sdholland 	.d_dump = rldump,
98a68f9396Sdholland 	.d_psize = rlpsize,
998c70ef39Sdholland 	.d_discard = nodiscard,
100a68f9396Sdholland 	.d_flag = D_DISK
10177a6b82bSgehenna };
10277a6b82bSgehenna 
10377a6b82bSgehenna const struct cdevsw rl_cdevsw = {
104a68f9396Sdholland 	.d_open = rlopen,
105a68f9396Sdholland 	.d_close = rlclose,
106a68f9396Sdholland 	.d_read = rlread,
107a68f9396Sdholland 	.d_write = rlwrite,
108a68f9396Sdholland 	.d_ioctl = rlioctl,
109a68f9396Sdholland 	.d_stop = nostop,
110a68f9396Sdholland 	.d_tty = notty,
111a68f9396Sdholland 	.d_poll = nopoll,
112a68f9396Sdholland 	.d_mmap = nommap,
113a68f9396Sdholland 	.d_kqfilter = nokqfilter,
114f9228f42Sdholland 	.d_discard = nodiscard,
115a68f9396Sdholland 	.d_flag = D_DISK
11677a6b82bSgehenna };
11777a6b82bSgehenna 
11811379e0aSragge #define	MAXRLXFER (RL_BPS * RL_SPT)
11911379e0aSragge 
12011379e0aSragge #define	RL_WREG(reg, val) \
12111379e0aSragge 	bus_space_write_2(sc->sc_iot, sc->sc_ioh, (reg), (val))
12211379e0aSragge #define RL_RREG(reg) \
12311379e0aSragge 	bus_space_read_2(sc->sc_iot, sc->sc_ioh, (reg))
12411379e0aSragge 
125dfba8166Smatt static const char * const rlstates[] = {
126f3082cc9Sragge 	"drive not loaded",
127f3082cc9Sragge 	"drive spinning up",
128f3082cc9Sragge 	"drive brushes out",
129f3082cc9Sragge 	"drive loading heads",
130f3082cc9Sragge 	"drive seeking",
131f3082cc9Sragge 	"drive ready",
132f3082cc9Sragge 	"drive unloading heads",
133f3082cc9Sragge 	"drive spun down",
134f3082cc9Sragge };
135f3082cc9Sragge 
136dfba8166Smatt static const struct dkdriver rldkdriver = {
1376f00c789Smlelstv 	.d_strategy = rlstrategy,
1386f00c789Smlelstv 	.d_minphys = minphys
139ecb2cb31Sthorpej };
140ecb2cb31Sthorpej 
141a4a700a7Sragge static const char *
rlstate(struct rlc_softc * sc,int unit)142f3082cc9Sragge rlstate(struct rlc_softc *sc, int unit)
143f3082cc9Sragge {
144f3082cc9Sragge 	int i = 0;
145f3082cc9Sragge 
146f3082cc9Sragge 	do {
147f3082cc9Sragge 		RL_WREG(RL_DA, RLDA_GS);
148f3082cc9Sragge 		RL_WREG(RL_CS, RLCS_GS|(unit << RLCS_USHFT));
149f3082cc9Sragge 		waitcrdy(sc);
150f3082cc9Sragge 	} while (((RL_RREG(RL_CS) & RLCS_ERR) != 0) && i++ < 10);
151f3082cc9Sragge 	if (i == 10)
152f3082cc9Sragge 		return NULL;
153f3082cc9Sragge 	i = RL_RREG(RL_MP) & RLMP_STATUS;
154f3082cc9Sragge 	return rlstates[i];
155f3082cc9Sragge }
156f3082cc9Sragge 
15711379e0aSragge void
waitcrdy(struct rlc_softc * sc)15811379e0aSragge waitcrdy(struct rlc_softc *sc)
15911379e0aSragge {
16011379e0aSragge 	int i;
16111379e0aSragge 
16211379e0aSragge 	for (i = 0; i < 1000; i++) {
16311379e0aSragge 		DELAY(10000);
16411379e0aSragge 		if (RL_RREG(RL_CS) & RLCS_CRDY)
16511379e0aSragge 			return;
16611379e0aSragge 	}
167dfba8166Smatt 	aprint_error_dev(sc->sc_dev, "never got ready\n"); /* ?panic? */
16811379e0aSragge }
16911379e0aSragge 
17011379e0aSragge int
rlcprint(void * aux,const char * name)17111379e0aSragge rlcprint(void *aux, const char *name)
17211379e0aSragge {
17311379e0aSragge 	struct rlc_attach_args *ra = aux;
17411379e0aSragge 
17511379e0aSragge 	if (name)
17672a7af27Sthorpej 		aprint_normal("RL0%d at %s",
17772a7af27Sthorpej 		    ra->type & RLMP_DT ? '2' : '1', name);
17872a7af27Sthorpej 	aprint_normal(" drive %d", ra->hwid);
17911379e0aSragge 	return UNCONF;
18011379e0aSragge }
18111379e0aSragge 
18211379e0aSragge /*
18311379e0aSragge  * Force the controller to interrupt.
18411379e0aSragge  */
18511379e0aSragge int
rlcmatch(device_t parent,cfdata_t cf,void * aux)186dfba8166Smatt rlcmatch(device_t parent, cfdata_t cf, void *aux)
18711379e0aSragge {
18811379e0aSragge 	struct uba_attach_args *ua = aux;
18911379e0aSragge 	struct rlc_softc ssc, *sc = &ssc;
19011379e0aSragge 	int i;
19111379e0aSragge 
19211379e0aSragge 	sc->sc_iot = ua->ua_iot;
19311379e0aSragge 	sc->sc_ioh = ua->ua_ioh;
19411379e0aSragge 	/* Force interrupt by issuing a "Get Status" command */
19511379e0aSragge 	RL_WREG(RL_DA, RLDA_GS);
19611379e0aSragge 	RL_WREG(RL_CS, RLCS_GS|RLCS_IE);
19711379e0aSragge 
19811379e0aSragge 	for (i = 0; i < 100; i++) {
19911379e0aSragge 		DELAY(100000);
20011379e0aSragge 		if (RL_RREG(RL_CS) & RLCS_CRDY)
20111379e0aSragge 			return 1;
20211379e0aSragge 	}
20311379e0aSragge 	return 0;
20411379e0aSragge }
20511379e0aSragge 
20611379e0aSragge void
rlcattach(device_t parent,device_t self,void * aux)207dfba8166Smatt rlcattach(device_t parent, device_t self, void *aux)
20811379e0aSragge {
209ceb94256Sthorpej 	struct rlc_softc *sc = device_private(self);
21011379e0aSragge 	struct uba_attach_args *ua = aux;
21111379e0aSragge 	struct rlc_attach_args ra;
21211379e0aSragge 	int i, error;
21311379e0aSragge 
214dfba8166Smatt 	sc->sc_dev = self;
215dfba8166Smatt 	sc->sc_uh = device_private(parent);
21611379e0aSragge 	sc->sc_iot = ua->ua_iot;
21711379e0aSragge 	sc->sc_ioh = ua->ua_ioh;
21811379e0aSragge 	sc->sc_dmat = ua->ua_dmat;
2190bd304e5Smatt 	uba_intr_establish(ua->ua_icookie, ua->ua_cvec,
2200bd304e5Smatt 		rlcintr, sc, &sc->sc_intrcnt);
2212f85fe7aSmatt 	evcnt_attach_dynamic(&sc->sc_intrcnt, EVCNT_TYPE_INTR, ua->ua_evcnt,
222dfba8166Smatt 		device_xname(sc->sc_dev), "intr");
223f3082cc9Sragge 	uba_reset_establish(rlcreset, self);
224f3082cc9Sragge 
22511379e0aSragge 	printf("\n");
22611379e0aSragge 
22711379e0aSragge 	/*
22811379e0aSragge 	 * The RL11 can only have one transfer going at a time,
22911379e0aSragge 	 * and max transfer size is one track, so only one dmamap
23011379e0aSragge 	 * is needed.
23111379e0aSragge 	 */
23211379e0aSragge 	error = bus_dmamap_create(sc->sc_dmat, MAXRLXFER, 1, MAXRLXFER, 0,
23311379e0aSragge 	    BUS_DMA_ALLOCNOW, &sc->sc_dmam);
23411379e0aSragge 	if (error) {
235dfba8166Smatt 		aprint_error(": Failed to allocate DMA map, error %d\n", error);
23611379e0aSragge 		return;
23711379e0aSragge 	}
238aec75b1cSyamt 	bufq_alloc(&sc->sc_q, "disksort", BUFQ_SORT_CYLINDER);
23911379e0aSragge 	for (i = 0; i < RL_MAXDPC; i++) {
24011379e0aSragge 		waitcrdy(sc);
24111379e0aSragge 		RL_WREG(RL_DA, RLDA_GS|RLDA_RST);
24211379e0aSragge 		RL_WREG(RL_CS, RLCS_GS|(i << RLCS_USHFT));
24311379e0aSragge 		waitcrdy(sc);
24411379e0aSragge 		ra.type = RL_RREG(RL_MP);
24511379e0aSragge 		ra.hwid = i;
24611379e0aSragge 		if ((RL_RREG(RL_CS) & RLCS_ERR) == 0)
247*c7fb772bSthorpej 			config_found(sc->sc_dev, &ra, rlcprint, CFARGS_NONE);
24811379e0aSragge 	}
24911379e0aSragge }
25011379e0aSragge 
25111379e0aSragge int
rlmatch(device_t parent,cfdata_t cf,void * aux)252dfba8166Smatt rlmatch(device_t parent, cfdata_t cf, void *aux)
25311379e0aSragge {
25411379e0aSragge 	struct rlc_attach_args *ra = aux;
25511379e0aSragge 
25611379e0aSragge 	if (cf->cf_loc[RLCCF_DRIVE] != RLCCF_DRIVE_DEFAULT &&
25711379e0aSragge 	    cf->cf_loc[RLCCF_DRIVE] != ra->hwid)
25811379e0aSragge 		return 0;
25911379e0aSragge 	return 1;
26011379e0aSragge }
26111379e0aSragge 
26211379e0aSragge void
rlattach(device_t parent,device_t self,void * aux)263dfba8166Smatt rlattach(device_t parent, device_t self, void *aux)
26411379e0aSragge {
265ceb94256Sthorpej 	struct rl_softc *rc = device_private(self);
26611379e0aSragge 	struct rlc_attach_args *ra = aux;
26711379e0aSragge 	struct disklabel *dl;
26811379e0aSragge 
269dfba8166Smatt 	rc->rc_dev = self;
270dfba8166Smatt 	rc->rc_rlc = device_private(parent);
27111379e0aSragge 	rc->rc_hwid = ra->hwid;
272dfba8166Smatt 	disk_init(&rc->rc_disk, device_xname(rc->rc_dev), &rldkdriver);
27311379e0aSragge 	disk_attach(&rc->rc_disk);
27411379e0aSragge 	dl = rc->rc_disk.dk_label;
27511379e0aSragge 	dl->d_npartitions = 3;
27611379e0aSragge 	strcpy(dl->d_typename, "RL01");
27711379e0aSragge 	if (ra->type & RLMP_DT)
27811379e0aSragge 		dl->d_typename[3] = '2';
27911379e0aSragge 	dl->d_secsize = DEV_BSIZE; /* XXX - wrong, but OK for now */
28011379e0aSragge 	dl->d_nsectors = RL_SPT/2;
28111379e0aSragge 	dl->d_ntracks = RL_SPD;
28211379e0aSragge 	dl->d_ncylinders = ra->type & RLMP_DT ? RL_TPS02 : RL_TPS01;
28311379e0aSragge 	dl->d_secpercyl = dl->d_nsectors * dl->d_ntracks;
28411379e0aSragge 	dl->d_secperunit = dl->d_ncylinders * dl->d_secpercyl;
28511379e0aSragge 	dl->d_partitions[0].p_size = dl->d_partitions[2].p_size =
28611379e0aSragge 	    dl->d_secperunit;
28711379e0aSragge 	dl->d_partitions[0].p_offset = dl->d_partitions[2].p_offset = 0;
28811379e0aSragge 	dl->d_interleave = dl->d_headswitch = 1;
28911379e0aSragge 	dl->d_bbsize = BBSIZE;
290f918a51dShe 	dl->d_sbsize = SBLOCKSIZE;
29111379e0aSragge 	dl->d_rpm = 2400;
292c182898bSchristos 	dl->d_type = DKTYPE_DEC;
293dfba8166Smatt 	printf(": %s, %s\n", dl->d_typename, rlstate(rc->rc_rlc, ra->hwid));
294ecb2cb31Sthorpej 
295ecb2cb31Sthorpej 	/*
296ecb2cb31Sthorpej 	 * XXX We should try to discovery wedges here, but
297ecb2cb31Sthorpej 	 * XXX that would mean loading up the pack and being
298ecb2cb31Sthorpej 	 * XXX able to do I/O.  Should use config_defer() here.
299ecb2cb31Sthorpej 	 */
30011379e0aSragge }
30111379e0aSragge 
30211379e0aSragge int
rlopen(dev_t dev,int flag,int fmt,struct lwp * l)30395e1ffb1Schristos rlopen(dev_t dev, int flag, int fmt, struct lwp *l)
30411379e0aSragge {
305dfba8166Smatt 	struct rl_softc * const rc = device_lookup_private(&rl_cd, DISKUNIT(dev));
30611379e0aSragge 	struct rlc_softc *sc;
307dfba8166Smatt 	int error, part, mask;
308dfba8166Smatt 	struct disklabel *dl;
309d91455ceSdsl 	const char *msg;
310f3082cc9Sragge 
31111379e0aSragge 	/*
31211379e0aSragge 	 * Make sure this is a reasonable open request.
31311379e0aSragge 	 */
314dfba8166Smatt 	if (rc == NULL)
31511379e0aSragge 		return ENXIO;
31611379e0aSragge 
317dfba8166Smatt 	sc = rc->rc_rlc;
318ecb2cb31Sthorpej 	part = DISKPART(dev);
319ecb2cb31Sthorpej 
320b5a9ff06Sad 	mutex_enter(&rc->rc_disk.dk_openlock);
321ecb2cb31Sthorpej 
322ecb2cb31Sthorpej 	/*
323ecb2cb31Sthorpej 	 * If there are wedges, and this is not RAW_PART, then we
324ecb2cb31Sthorpej 	 * need to fail.
325ecb2cb31Sthorpej 	 */
326ecb2cb31Sthorpej 	if (rc->rc_disk.dk_nwedges != 0 && part != RAW_PART) {
327ecb2cb31Sthorpej 		error = EBUSY;
328ecb2cb31Sthorpej 		goto bad1;
329ecb2cb31Sthorpej 	}
330ecb2cb31Sthorpej 
331f3082cc9Sragge 	/* Check that the disk actually is useable */
332f3082cc9Sragge 	msg = rlstate(sc, rc->rc_hwid);
333f3082cc9Sragge 	if (msg == NULL || msg == rlstates[RLMP_UNLOAD] ||
334ecb2cb31Sthorpej 	    msg == rlstates[RLMP_SPUNDOWN]) {
335ecb2cb31Sthorpej 		error = ENXIO;
336ecb2cb31Sthorpej 		goto bad1;
337ecb2cb31Sthorpej 	}
33811379e0aSragge 	/*
33911379e0aSragge 	 * If this is the first open; read in where on the disk we are.
34011379e0aSragge 	 */
34111379e0aSragge 	dl = rc->rc_disk.dk_label;
34211379e0aSragge 	if (rc->rc_state == DK_CLOSED) {
34311379e0aSragge 		u_int16_t mp;
34477a6b82bSgehenna 		int maj;
34511379e0aSragge 		RL_WREG(RL_CS, RLCS_RHDR|(rc->rc_hwid << RLCS_USHFT));
34611379e0aSragge 		waitcrdy(sc);
34711379e0aSragge 		mp = RL_RREG(RL_MP);
34811379e0aSragge 		rc->rc_head = ((mp & RLMP_HS) == RLMP_HS);
34911379e0aSragge 		rc->rc_cyl = (mp >> 7) & 0777;
35011379e0aSragge 		rc->rc_state = DK_OPEN;
35111379e0aSragge 		/* Get disk label */
35277a6b82bSgehenna 		maj = cdevsw_lookup_major(&rl_cdevsw);
35377a6b82bSgehenna 		if ((msg = readdisklabel(MAKEDISKDEV(maj,
354dfba8166Smatt 		    device_unit(rc->rc_dev), RAW_PART), rlstrategy, dl, NULL)))
355dfba8166Smatt 			aprint_normal_dev(rc->rc_dev, "%s", msg);
356dfba8166Smatt 		aprint_normal_dev(rc->rc_dev, "size %d sectors\n",
357dfba8166Smatt 		    dl->d_secperunit);
35811379e0aSragge 	}
359ecb2cb31Sthorpej 	if (part >= dl->d_npartitions) {
360ecb2cb31Sthorpej 		error = ENXIO;
361ecb2cb31Sthorpej 		goto bad1;
362ecb2cb31Sthorpej 	}
36311379e0aSragge 
36411379e0aSragge 	mask = 1 << part;
36511379e0aSragge 	switch (fmt) {
36611379e0aSragge 	case S_IFCHR:
36711379e0aSragge 		rc->rc_disk.dk_copenmask |= mask;
36811379e0aSragge 		break;
36911379e0aSragge 	case S_IFBLK:
37011379e0aSragge 		rc->rc_disk.dk_bopenmask |= mask;
37111379e0aSragge 		break;
37211379e0aSragge 	}
37311379e0aSragge 	rc->rc_disk.dk_openmask |= mask;
374b5a9ff06Sad 	error = 0;
375ecb2cb31Sthorpej  bad1:
376b5a9ff06Sad 	mutex_exit(&rc->rc_disk.dk_openlock);
377ecb2cb31Sthorpej 	return (error);
37811379e0aSragge }
37911379e0aSragge 
38011379e0aSragge int
rlclose(dev_t dev,int flag,int fmt,struct lwp * l)38195e1ffb1Schristos rlclose(dev_t dev, int flag, int fmt, struct lwp *l)
38211379e0aSragge {
383b5a9ff06Sad 	int unit = DISKUNIT(dev);
384ddb23852Sdrochner 	struct rl_softc *rc = device_lookup_private(&rl_cd, unit);
38511379e0aSragge 	int mask = (1 << DISKPART(dev));
38611379e0aSragge 
387b5a9ff06Sad 	mutex_enter(&rc->rc_disk.dk_openlock);
388ecb2cb31Sthorpej 
38911379e0aSragge 	switch (fmt) {
39011379e0aSragge 	case S_IFCHR:
39111379e0aSragge 		rc->rc_disk.dk_copenmask &= ~mask;
39211379e0aSragge 		break;
39311379e0aSragge 	case S_IFBLK:
39411379e0aSragge 		rc->rc_disk.dk_bopenmask &= ~mask;
39511379e0aSragge 		break;
39611379e0aSragge 	}
39711379e0aSragge 	rc->rc_disk.dk_openmask =
39811379e0aSragge 	    rc->rc_disk.dk_copenmask | rc->rc_disk.dk_bopenmask;
39911379e0aSragge 
40011379e0aSragge 	if (rc->rc_disk.dk_openmask == 0)
40111379e0aSragge 		rc->rc_state = DK_CLOSED; /* May change pack */
402b5a9ff06Sad 	mutex_exit(&rc->rc_disk.dk_openlock);
40311379e0aSragge 	return 0;
40411379e0aSragge }
40511379e0aSragge 
40611379e0aSragge void
rlstrategy(struct buf * bp)40711379e0aSragge rlstrategy(struct buf *bp)
40811379e0aSragge {
409dfba8166Smatt 	struct rl_softc * const rc = device_lookup_private(&rl_cd, DISKUNIT(bp->b_dev));
41011379e0aSragge 	struct disklabel *lp;
411dfba8166Smatt 	int s;
412dfba8166Smatt 
413dfba8166Smatt 	if (rc == NULL || rc->rc_state != DK_OPEN) /* How did we end up here at all? */
41411379e0aSragge 		panic("rlstrategy: state impossible");
41511379e0aSragge 
41611379e0aSragge 	lp = rc->rc_disk.dk_label;
417dfba8166Smatt 	if (bounds_check_with_label(&rc->rc_disk, bp, 1) <= 0)
41811379e0aSragge 		goto done;
41911379e0aSragge 
42011379e0aSragge 	if (bp->b_bcount == 0)
42111379e0aSragge 		goto done;
42211379e0aSragge 
42311379e0aSragge 	bp->b_rawblkno =
42411379e0aSragge 	    bp->b_blkno + lp->d_partitions[DISKPART(bp->b_dev)].p_offset;
42511379e0aSragge 	bp->b_cylinder = bp->b_rawblkno / lp->d_secpercyl;
42611379e0aSragge 
427ae66f2eaSthorpej 	s = splbio();
42870de9736Syamt 	bufq_put(rc->rc_rlc->sc_q, bp);
429dfba8166Smatt 	rlcstart(rc->rc_rlc, 0);
43011379e0aSragge 	splx(s);
43111379e0aSragge 	return;
43211379e0aSragge 
43311379e0aSragge done:	biodone(bp);
43411379e0aSragge }
43511379e0aSragge 
43611379e0aSragge int
rlioctl(dev_t dev,u_long cmd,void * addr,int flag,struct lwp * l)43753524e44Schristos rlioctl(dev_t dev, u_long cmd, void *addr, int flag, struct lwp *l)
43811379e0aSragge {
439ddb23852Sdrochner 	struct rl_softc *rc = device_lookup_private(&rl_cd, DISKUNIT(dev));
44011379e0aSragge 	struct disklabel *lp = rc->rc_disk.dk_label;
4413be6bb24Schristos 	int error;
442e2d1c1f9Sfvdl #ifdef __HAVE_OLD_DISKLABEL
443c60db2e9Schristos 	struct disklabel newlabel;
444e2d1c1f9Sfvdl #endif
44511379e0aSragge 
446c60db2e9Schristos 	error = disk_ioctl(&rc->rc_disk, dev, cmd, addr, flag, l);
4473be6bb24Schristos 	if (error != EPASSTHROUGH)
4483be6bb24Schristos 		return error;
4493be6bb24Schristos 	else
4503be6bb24Schristos 		error = 0;
4513be6bb24Schristos 
45211379e0aSragge 	switch (cmd) {
45311379e0aSragge 	case DIOCSDINFO:
45411379e0aSragge 	case DIOCWDINFO:
455e2d1c1f9Sfvdl #ifdef __HAVE_OLD_DISKLABEL
456e2d1c1f9Sfvdl 	case ODIOCWDINFO:
457e2d1c1f9Sfvdl 	case ODIOCSDINFO:
458e2d1c1f9Sfvdl #endif
459e2d1c1f9Sfvdl 	{
460e2d1c1f9Sfvdl 		struct disklabel *tp;
461e2d1c1f9Sfvdl 
462e2d1c1f9Sfvdl #ifdef __HAVE_OLD_DISKLABEL
463e2d1c1f9Sfvdl 		if (cmd == ODIOCSDINFO || cmd == ODIOCWDINFO) {
464e2d1c1f9Sfvdl 			memset(&newlabel, 0, sizeof newlabel);
465e2d1c1f9Sfvdl 			memcpy(&newlabel, addr, sizeof (struct olddisklabel));
466e2d1c1f9Sfvdl 			tp = &newlabel;
467e2d1c1f9Sfvdl 		} else
468e2d1c1f9Sfvdl #endif
469e2d1c1f9Sfvdl 		tp = (struct disklabel *)addr;
470e2d1c1f9Sfvdl 
47111379e0aSragge 		if ((flag & FWRITE) == 0)
4723be6bb24Schristos 			error = EBADF;
473ecb2cb31Sthorpej 		else {
474b5a9ff06Sad 			mutex_enter(&rc->rc_disk.dk_openlock);
4753be6bb24Schristos 			error = ((
476e2d1c1f9Sfvdl #ifdef __HAVE_OLD_DISKLABEL
477e2d1c1f9Sfvdl 			       cmd == ODIOCSDINFO ||
478e2d1c1f9Sfvdl #endif
479e2d1c1f9Sfvdl 			       cmd == DIOCSDINFO) ?
480e2d1c1f9Sfvdl 			    setdisklabel(lp, tp, 0, 0) :
48111379e0aSragge 			    writedisklabel(dev, rlstrategy, lp, 0));
482b5a9ff06Sad 			mutex_exit(&rc->rc_disk.dk_openlock);
483ecb2cb31Sthorpej 		}
48411379e0aSragge 		break;
485e2d1c1f9Sfvdl 	}
48611379e0aSragge 
48711379e0aSragge 	case DIOCWLABEL:
48811379e0aSragge 		if ((flag & FWRITE) == 0)
4893be6bb24Schristos 			error = EBADF;
49011379e0aSragge 		break;
49111379e0aSragge 
49211379e0aSragge 	default:
4933be6bb24Schristos 		error = ENOTTY;
494dfba8166Smatt 		break;
49511379e0aSragge 	}
4963be6bb24Schristos 	return error;
49711379e0aSragge }
49811379e0aSragge 
49911379e0aSragge int
rlpsize(dev_t dev)500dfba8166Smatt rlpsize(dev_t dev)
50111379e0aSragge {
502dfba8166Smatt 	struct rl_softc * const rc = device_lookup_private(&rl_cd, DISKUNIT(dev));
50311379e0aSragge 	struct disklabel *dl;
504dfba8166Smatt 	int size;
50511379e0aSragge 
506dfba8166Smatt 	if (rc == NULL)
50711379e0aSragge 		return -1;
50811379e0aSragge 	dl = rc->rc_disk.dk_label;
50911379e0aSragge 	size = dl->d_partitions[DISKPART(dev)].p_size *
51011379e0aSragge 	    (dl->d_secsize / DEV_BSIZE);
51111379e0aSragge 	return size;
51211379e0aSragge }
51311379e0aSragge 
51411379e0aSragge int
rldump(dev_t dev,daddr_t blkno,void * va,size_t size)51553524e44Schristos rldump(dev_t dev, daddr_t blkno, void *va, size_t size)
51611379e0aSragge {
51711379e0aSragge 	/* Not likely... */
51811379e0aSragge 	return 0;
51911379e0aSragge }
52011379e0aSragge 
52111379e0aSragge int
rlread(dev_t dev,struct uio * uio,int ioflag)52211379e0aSragge rlread(dev_t dev, struct uio *uio, int ioflag)
52311379e0aSragge {
52411379e0aSragge 	return (physio(rlstrategy, NULL, dev, B_READ, minphys, uio));
52511379e0aSragge }
52611379e0aSragge 
52711379e0aSragge int
rlwrite(dev_t dev,struct uio * uio,int ioflag)52811379e0aSragge rlwrite(dev_t dev, struct uio *uio, int ioflag)
52911379e0aSragge {
53011379e0aSragge 	return (physio(rlstrategy, NULL, dev, B_WRITE, minphys, uio));
53111379e0aSragge }
53211379e0aSragge 
533dfba8166Smatt static const char * const rlerr[] = {
53411379e0aSragge 	"no",
53511379e0aSragge 	"operation incomplete",
53611379e0aSragge 	"read data CRC",
53711379e0aSragge 	"header CRC",
53811379e0aSragge 	"data late",
53911379e0aSragge 	"header not found",
54011379e0aSragge 	"",
54111379e0aSragge 	"",
5423f9984fcSwiz 	"non-existent memory",
54311379e0aSragge 	"memory parity error",
54411379e0aSragge 	"",
54511379e0aSragge 	"",
54611379e0aSragge 	"",
54711379e0aSragge 	"",
54811379e0aSragge 	"",
54911379e0aSragge 	"",
55011379e0aSragge };
55111379e0aSragge 
55211379e0aSragge void
rlcintr(void * arg)55311379e0aSragge rlcintr(void *arg)
55411379e0aSragge {
55511379e0aSragge 	struct rlc_softc *sc = arg;
55611379e0aSragge 	struct buf *bp;
55711379e0aSragge 	u_int16_t cs;
55811379e0aSragge 
55911379e0aSragge 	bp = sc->sc_active;
56011379e0aSragge 	if (bp == 0) {
561dfba8166Smatt 		aprint_error_dev(sc->sc_dev, "strange interrupt\n");
56211379e0aSragge 		return;
56311379e0aSragge 	}
56411379e0aSragge 	bus_dmamap_unload(sc->sc_dmat, sc->sc_dmam);
56511379e0aSragge 	sc->sc_active = 0;
56611379e0aSragge 	cs = RL_RREG(RL_CS);
56711379e0aSragge 	if (cs & RLCS_ERR) {
56811379e0aSragge 		int error = (cs & RLCS_ERRMSK) >> 10;
56911379e0aSragge 
570dfba8166Smatt 		aprint_error_dev(sc->sc_dev, "%s\n", rlerr[error]);
57111379e0aSragge 		bp->b_error = EIO;
57211379e0aSragge 		bp->b_resid = bp->b_bcount;
57311379e0aSragge 		sc->sc_bytecnt = 0;
57411379e0aSragge 	}
57511379e0aSragge 	if (sc->sc_bytecnt == 0) /* Finished transfer */
57611379e0aSragge 		biodone(bp);
57711379e0aSragge 	rlcstart(sc, sc->sc_bytecnt ? bp : 0);
57811379e0aSragge }
57911379e0aSragge 
58011379e0aSragge /*
58111379e0aSragge  * Start routine. First position the disk to the given position,
58211379e0aSragge  * then start reading/writing. An optimization would be to be able
58311379e0aSragge  * to handle overlapping seeks between disks.
58411379e0aSragge  */
58511379e0aSragge void
rlcstart(struct rlc_softc * sc,struct buf * ob)58611379e0aSragge rlcstart(struct rlc_softc *sc, struct buf *ob)
58711379e0aSragge {
58811379e0aSragge 	struct disklabel *lp;
58911379e0aSragge 	struct rl_softc *rc;
59011379e0aSragge 	struct buf *bp;
59111379e0aSragge 	int bn, cn, sn, tn, blks, err;
59211379e0aSragge 
59311379e0aSragge 	if (sc->sc_active)
59411379e0aSragge 		return;	/* Already doing something */
59511379e0aSragge 
59611379e0aSragge 	if (ob == 0) {
59770de9736Syamt 		bp = bufq_get(sc->sc_q);
59811379e0aSragge 		if (bp == NULL)
59911379e0aSragge 			return;	/* Nothing to do */
600071aed40Sthorpej 		sc->sc_bufaddr = bp->b_data;
60111379e0aSragge 		sc->sc_diskblk = bp->b_rawblkno;
60211379e0aSragge 		sc->sc_bytecnt = bp->b_bcount;
60311379e0aSragge 		bp->b_resid = 0;
60411379e0aSragge 	} else
60511379e0aSragge 		bp = ob;
60611379e0aSragge 	sc->sc_active = bp;
60711379e0aSragge 
608ddb23852Sdrochner 	rc = device_lookup_private(&rl_cd, DISKUNIT(bp->b_dev));
60911379e0aSragge 	bn = sc->sc_diskblk;
61011379e0aSragge 	lp = rc->rc_disk.dk_label;
61111379e0aSragge 	if (bn) {
61211379e0aSragge 		cn = bn / lp->d_secpercyl;
61311379e0aSragge 		sn = bn % lp->d_secpercyl;
61411379e0aSragge 		tn = sn / lp->d_nsectors;
61511379e0aSragge 		sn = sn % lp->d_nsectors;
61611379e0aSragge 	} else
61711379e0aSragge 		cn = sn = tn = 0;
61811379e0aSragge 
61911379e0aSragge 	/*
62011379e0aSragge 	 * Check if we have to position disk first.
62111379e0aSragge 	 */
62211379e0aSragge 	if (rc->rc_cyl != cn || rc->rc_head != tn) {
62311379e0aSragge 		u_int16_t da = RLDA_SEEK;
62411379e0aSragge 		if (cn > rc->rc_cyl)
62511379e0aSragge 			da |= ((cn - rc->rc_cyl) << RLDA_CYLSHFT) | RLDA_DIR;
62611379e0aSragge 		else
62711379e0aSragge 			da |= ((rc->rc_cyl - cn) << RLDA_CYLSHFT);
62811379e0aSragge 		if (tn)
62911379e0aSragge 			da |= RLDA_HSSEEK;
63011379e0aSragge 		waitcrdy(sc);
63111379e0aSragge 		RL_WREG(RL_DA, da);
63211379e0aSragge 		RL_WREG(RL_CS, RLCS_SEEK | (rc->rc_hwid << RLCS_USHFT));
63311379e0aSragge 		waitcrdy(sc);
63411379e0aSragge 		rc->rc_cyl = cn;
63511379e0aSragge 		rc->rc_head = tn;
63611379e0aSragge 	}
63711379e0aSragge 	RL_WREG(RL_DA, (cn << RLDA_CYLSHFT) | (tn ? RLDA_HSRW : 0) | (sn << 1));
63811379e0aSragge 	blks = sc->sc_bytecnt/DEV_BSIZE;
63911379e0aSragge 
64011379e0aSragge 	if (sn + blks > RL_SPT/2)
64111379e0aSragge 		blks = RL_SPT/2 - sn;
64211379e0aSragge 	RL_WREG(RL_MP, -(blks*DEV_BSIZE)/2);
64311379e0aSragge 	err = bus_dmamap_load(sc->sc_dmat, sc->sc_dmam, sc->sc_bufaddr,
644f3082cc9Sragge 	    (blks*DEV_BSIZE), (bp->b_flags & B_PHYS ? bp->b_proc : 0),
645f3082cc9Sragge 	    BUS_DMA_NOWAIT);
64611379e0aSragge 	if (err)
64711379e0aSragge 		panic("%s: bus_dmamap_load failed: %d",
648dfba8166Smatt 		    device_xname(sc->sc_dev), err);
64911379e0aSragge 	RL_WREG(RL_BA, (sc->sc_dmam->dm_segs[0].ds_addr & 0xffff));
65011379e0aSragge 
65111379e0aSragge 	/* Count up vars */
652bf9a718bShe 	sc->sc_bufaddr = (char *)sc->sc_bufaddr + (blks*DEV_BSIZE);
65311379e0aSragge 	sc->sc_diskblk += blks;
65411379e0aSragge 	sc->sc_bytecnt -= (blks*DEV_BSIZE);
65511379e0aSragge 
65611379e0aSragge 	if (bp->b_flags & B_READ)
65711379e0aSragge 		RL_WREG(RL_CS, RLCS_IE|RLCS_RD|(rc->rc_hwid << RLCS_USHFT));
65811379e0aSragge 	else
65911379e0aSragge 		RL_WREG(RL_CS, RLCS_IE|RLCS_WD|(rc->rc_hwid << RLCS_USHFT));
66011379e0aSragge }
6616f6bea9eSragge 
662f3082cc9Sragge /*
663f3082cc9Sragge  * Called once per controller when an ubareset occurs.
664f3082cc9Sragge  * Retracts all disks and restarts active transfers.
665f3082cc9Sragge  */
6666f6bea9eSragge void
rlcreset(device_t dev)667dfba8166Smatt rlcreset(device_t dev)
6686f6bea9eSragge {
669dfba8166Smatt 	struct rlc_softc *sc = device_private(dev);
670f3082cc9Sragge 	struct rl_softc *rc;
671f3082cc9Sragge 	int i;
6726f6bea9eSragge 	u_int16_t mp;
6736f6bea9eSragge 
674f3082cc9Sragge 	for (i = 0; i < rl_cd.cd_ndevs; i++) {
675dfba8166Smatt 		if ((rc = device_lookup_private(&rl_cd, i)) == NULL)
676f3082cc9Sragge 			continue;
6776f6bea9eSragge 		if (rc->rc_state != DK_OPEN)
678f3082cc9Sragge 			continue;
679dfba8166Smatt 		if (rc->rc_rlc != sc)
680dfba8166Smatt 			continue;
681f3082cc9Sragge 
6826f6bea9eSragge 		RL_WREG(RL_CS, RLCS_RHDR|(rc->rc_hwid << RLCS_USHFT));
6836f6bea9eSragge 		waitcrdy(sc);
6846f6bea9eSragge 		mp = RL_RREG(RL_MP);
6856f6bea9eSragge 		rc->rc_head = ((mp & RLMP_HS) == RLMP_HS);
6866f6bea9eSragge 		rc->rc_cyl = (mp >> 7) & 0777;
687f3082cc9Sragge 	}
6886f6bea9eSragge 	if (sc->sc_active == 0)
6896f6bea9eSragge 		return;
6906f6bea9eSragge 
69170de9736Syamt 	bufq_put(sc->sc_q, sc->sc_active);
6926f6bea9eSragge 	sc->sc_active = 0;
6936f6bea9eSragge 	rlcstart(sc, 0);
6946f6bea9eSragge }
695