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