1 /* $NetBSD: rl.c,v 1.41 2009/03/18 17:06:50 cegger Exp $ */
4 * Copyright (c) 2000 Ludd, University of Lule}, Sweden. All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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
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.
34 * RL11/RLV11/RLV12 disk controller driver and
35 * RL01/RL02 disk device driver.
38 * Handle disk errors more gracefully
39 * Do overlapping seeks on multiple drives
41 * Implementation comments:
45 #include <sys/cdefs.h>
46 __KERNEL_RCSID(0, "$NetBSD: rl.c,v 1.41 2009/03/18 17:06:50 cegger Exp $");
48 #include <sys/param.h>
49 #include <sys/device.h>
50 #include <sys/systm.h>
53 #include <sys/disklabel.h>
58 #include <sys/fcntl.h>
59 #include <sys/event.h>
61 #include <ufs/ufs/dinode.h>
62 #include <ufs/ffs/fs.h>
66 #include <dev/qbus/ubavar.h>
67 #include <dev/qbus/rlreg.h>
68 #include <dev/qbus/rlvar.h>
73 static int rlcmatch(device_t
, cfdata_t
, void *);
74 static void rlcattach(device_t
, device_t
, void *);
75 static int rlcprint(void *, const char *);
76 static void rlcintr(void *);
77 static int rlmatch(device_t
, cfdata_t
, void *);
78 static void rlattach(device_t
, device_t
, void *);
79 static void rlcstart(struct rlc_softc
*, struct buf
*);
80 static void waitcrdy(struct rlc_softc
*);
81 static void rlcreset(device_t
);
83 CFATTACH_DECL_NEW(rlc
, sizeof(struct rlc_softc
),
84 rlcmatch
, rlcattach
, NULL
, NULL
);
86 CFATTACH_DECL_NEW(rl
, sizeof(struct rl_softc
),
87 rlmatch
, rlattach
, NULL
, NULL
);
89 static dev_type_open(rlopen
);
90 static dev_type_close(rlclose
);
91 static dev_type_read(rlread
);
92 static dev_type_write(rlwrite
);
93 static dev_type_ioctl(rlioctl
);
94 static dev_type_strategy(rlstrategy
);
95 static dev_type_dump(rldump
);
96 static dev_type_size(rlpsize
);
98 const struct bdevsw rl_bdevsw
= {
99 rlopen
, rlclose
, rlstrategy
, rlioctl
, rldump
, rlpsize
, D_DISK
102 const struct cdevsw rl_cdevsw
= {
103 rlopen
, rlclose
, rlread
, rlwrite
, rlioctl
,
104 nostop
, notty
, nopoll
, nommap
, nokqfilter
, D_DISK
107 #define MAXRLXFER (RL_BPS * RL_SPT)
109 #define RL_WREG(reg, val) \
110 bus_space_write_2(sc->sc_iot, sc->sc_ioh, (reg), (val))
111 #define RL_RREG(reg) \
112 bus_space_read_2(sc->sc_iot, sc->sc_ioh, (reg))
114 static const char * const rlstates
[] = {
118 "drive loading heads",
121 "drive unloading heads",
125 static const struct dkdriver rldkdriver
= {
130 rlstate(struct rlc_softc
*sc
, int unit
)
135 RL_WREG(RL_DA
, RLDA_GS
);
136 RL_WREG(RL_CS
, RLCS_GS
|(unit
<< RLCS_USHFT
));
138 } while (((RL_RREG(RL_CS
) & RLCS_ERR
) != 0) && i
++ < 10);
141 i
= RL_RREG(RL_MP
) & RLMP_STATUS
;
146 waitcrdy(struct rlc_softc
*sc
)
150 for (i
= 0; i
< 1000; i
++) {
152 if (RL_RREG(RL_CS
) & RLCS_CRDY
)
155 aprint_error_dev(sc
->sc_dev
, "never got ready\n"); /* ?panic? */
159 rlcprint(void *aux
, const char *name
)
161 struct rlc_attach_args
*ra
= aux
;
164 aprint_normal("RL0%d at %s",
165 ra
->type
& RLMP_DT
? '2' : '1', name
);
166 aprint_normal(" drive %d", ra
->hwid
);
171 * Force the controller to interrupt.
174 rlcmatch(device_t parent
, cfdata_t cf
, void *aux
)
176 struct uba_attach_args
*ua
= aux
;
177 struct rlc_softc ssc
, *sc
= &ssc
;
180 sc
->sc_iot
= ua
->ua_iot
;
181 sc
->sc_ioh
= ua
->ua_ioh
;
182 /* Force interrupt by issuing a "Get Status" command */
183 RL_WREG(RL_DA
, RLDA_GS
);
184 RL_WREG(RL_CS
, RLCS_GS
|RLCS_IE
);
186 for (i
= 0; i
< 100; i
++) {
188 if (RL_RREG(RL_CS
) & RLCS_CRDY
)
195 rlcattach(device_t parent
, device_t self
, void *aux
)
197 struct rlc_softc
*sc
= device_private(self
);
198 struct uba_attach_args
*ua
= aux
;
199 struct rlc_attach_args ra
;
203 sc
->sc_uh
= device_private(parent
);
204 sc
->sc_iot
= ua
->ua_iot
;
205 sc
->sc_ioh
= ua
->ua_ioh
;
206 sc
->sc_dmat
= ua
->ua_dmat
;
207 uba_intr_establish(ua
->ua_icookie
, ua
->ua_cvec
,
208 rlcintr
, sc
, &sc
->sc_intrcnt
);
209 evcnt_attach_dynamic(&sc
->sc_intrcnt
, EVCNT_TYPE_INTR
, ua
->ua_evcnt
,
210 device_xname(sc
->sc_dev
), "intr");
211 uba_reset_establish(rlcreset
, self
);
216 * The RL11 can only have one transfer going at a time,
217 * and max transfer size is one track, so only one dmamap
220 error
= bus_dmamap_create(sc
->sc_dmat
, MAXRLXFER
, 1, MAXRLXFER
, 0,
221 BUS_DMA_ALLOCNOW
, &sc
->sc_dmam
);
223 aprint_error(": Failed to allocate DMA map, error %d\n", error
);
226 bufq_alloc(&sc
->sc_q
, "disksort", BUFQ_SORT_CYLINDER
);
227 for (i
= 0; i
< RL_MAXDPC
; i
++) {
229 RL_WREG(RL_DA
, RLDA_GS
|RLDA_RST
);
230 RL_WREG(RL_CS
, RLCS_GS
|(i
<< RLCS_USHFT
));
232 ra
.type
= RL_RREG(RL_MP
);
234 if ((RL_RREG(RL_CS
) & RLCS_ERR
) == 0)
235 config_found(sc
->sc_dev
, &ra
, rlcprint
);
240 rlmatch(device_t parent
, cfdata_t cf
, void *aux
)
242 struct rlc_attach_args
*ra
= aux
;
244 if (cf
->cf_loc
[RLCCF_DRIVE
] != RLCCF_DRIVE_DEFAULT
&&
245 cf
->cf_loc
[RLCCF_DRIVE
] != ra
->hwid
)
251 rlattach(device_t parent
, device_t self
, void *aux
)
253 struct rl_softc
*rc
= device_private(self
);
254 struct rlc_attach_args
*ra
= aux
;
255 struct disklabel
*dl
;
258 rc
->rc_rlc
= device_private(parent
);
259 rc
->rc_hwid
= ra
->hwid
;
260 disk_init(&rc
->rc_disk
, device_xname(rc
->rc_dev
), &rldkdriver
);
261 disk_attach(&rc
->rc_disk
);
262 dl
= rc
->rc_disk
.dk_label
;
263 dl
->d_npartitions
= 3;
264 strcpy(dl
->d_typename
, "RL01");
265 if (ra
->type
& RLMP_DT
)
266 dl
->d_typename
[3] = '2';
267 dl
->d_secsize
= DEV_BSIZE
; /* XXX - wrong, but OK for now */
268 dl
->d_nsectors
= RL_SPT
/2;
269 dl
->d_ntracks
= RL_SPD
;
270 dl
->d_ncylinders
= ra
->type
& RLMP_DT
? RL_TPS02
: RL_TPS01
;
271 dl
->d_secpercyl
= dl
->d_nsectors
* dl
->d_ntracks
;
272 dl
->d_secperunit
= dl
->d_ncylinders
* dl
->d_secpercyl
;
273 dl
->d_partitions
[0].p_size
= dl
->d_partitions
[2].p_size
=
275 dl
->d_partitions
[0].p_offset
= dl
->d_partitions
[2].p_offset
= 0;
276 dl
->d_interleave
= dl
->d_headswitch
= 1;
277 dl
->d_bbsize
= BBSIZE
;
278 dl
->d_sbsize
= SBLOCKSIZE
;
280 dl
->d_type
= DTYPE_DEC
;
281 printf(": %s, %s\n", dl
->d_typename
, rlstate(rc
->rc_rlc
, ra
->hwid
));
284 * XXX We should try to discovery wedges here, but
285 * XXX that would mean loading up the pack and being
286 * XXX able to do I/O. Should use config_defer() here.
291 rlopen(dev_t dev
, int flag
, int fmt
, struct lwp
*l
)
293 struct rl_softc
* const rc
= device_lookup_private(&rl_cd
, DISKUNIT(dev
));
294 struct rlc_softc
*sc
;
295 int error
, part
, mask
;
296 struct disklabel
*dl
;
300 * Make sure this is a reasonable open request.
306 part
= DISKPART(dev
);
308 mutex_enter(&rc
->rc_disk
.dk_openlock
);
311 * If there are wedges, and this is not RAW_PART, then we
314 if (rc
->rc_disk
.dk_nwedges
!= 0 && part
!= RAW_PART
) {
319 /* Check that the disk actually is useable */
320 msg
= rlstate(sc
, rc
->rc_hwid
);
321 if (msg
== NULL
|| msg
== rlstates
[RLMP_UNLOAD
] ||
322 msg
== rlstates
[RLMP_SPUNDOWN
]) {
327 * If this is the first open; read in where on the disk we are.
329 dl
= rc
->rc_disk
.dk_label
;
330 if (rc
->rc_state
== DK_CLOSED
) {
333 RL_WREG(RL_CS
, RLCS_RHDR
|(rc
->rc_hwid
<< RLCS_USHFT
));
336 rc
->rc_head
= ((mp
& RLMP_HS
) == RLMP_HS
);
337 rc
->rc_cyl
= (mp
>> 7) & 0777;
338 rc
->rc_state
= DK_OPEN
;
340 maj
= cdevsw_lookup_major(&rl_cdevsw
);
341 if ((msg
= readdisklabel(MAKEDISKDEV(maj
,
342 device_unit(rc
->rc_dev
), RAW_PART
), rlstrategy
, dl
, NULL
)))
343 aprint_normal_dev(rc
->rc_dev
, "%s", msg
);
344 aprint_normal_dev(rc
->rc_dev
, "size %d sectors\n",
347 if (part
>= dl
->d_npartitions
) {
355 rc
->rc_disk
.dk_copenmask
|= mask
;
358 rc
->rc_disk
.dk_bopenmask
|= mask
;
361 rc
->rc_disk
.dk_openmask
|= mask
;
364 mutex_exit(&rc
->rc_disk
.dk_openlock
);
369 rlclose(dev_t dev
, int flag
, int fmt
, struct lwp
*l
)
371 int unit
= DISKUNIT(dev
);
372 struct rl_softc
*rc
= device_lookup_private(&rl_cd
, unit
);
373 int mask
= (1 << DISKPART(dev
));
375 mutex_enter(&rc
->rc_disk
.dk_openlock
);
379 rc
->rc_disk
.dk_copenmask
&= ~mask
;
382 rc
->rc_disk
.dk_bopenmask
&= ~mask
;
385 rc
->rc_disk
.dk_openmask
=
386 rc
->rc_disk
.dk_copenmask
| rc
->rc_disk
.dk_bopenmask
;
388 if (rc
->rc_disk
.dk_openmask
== 0)
389 rc
->rc_state
= DK_CLOSED
; /* May change pack */
390 mutex_exit(&rc
->rc_disk
.dk_openlock
);
395 rlstrategy(struct buf
*bp
)
397 struct rl_softc
* const rc
= device_lookup_private(&rl_cd
, DISKUNIT(bp
->b_dev
));
398 struct disklabel
*lp
;
401 if (rc
== NULL
|| rc
->rc_state
!= DK_OPEN
) /* How did we end up here at all? */
402 panic("rlstrategy: state impossible");
404 lp
= rc
->rc_disk
.dk_label
;
405 if (bounds_check_with_label(&rc
->rc_disk
, bp
, 1) <= 0)
408 if (bp
->b_bcount
== 0)
412 bp
->b_blkno
+ lp
->d_partitions
[DISKPART(bp
->b_dev
)].p_offset
;
413 bp
->b_cylinder
= bp
->b_rawblkno
/ lp
->d_secpercyl
;
416 bufq_put(rc
->rc_rlc
->sc_q
, bp
);
417 rlcstart(rc
->rc_rlc
, 0);
425 rlioctl(dev_t dev
, u_long cmd
, void *addr
, int flag
, struct lwp
*l
)
427 struct rl_softc
*rc
= device_lookup_private(&rl_cd
, DISKUNIT(dev
));
428 struct disklabel
*lp
= rc
->rc_disk
.dk_label
;
430 #ifdef __HAVE_OLD_DISKLABEL
431 struct disklabel newlabel
;
436 memcpy(addr
, lp
, sizeof (struct disklabel
));
439 #ifdef __HAVE_OLD_DISKLABEL
442 if (newlabel
.d_npartitions
> OLDMAXPARTITIONS
)
444 memcpy(addr
, &newlabel
, sizeof (struct olddisklabel
));
449 ((struct partinfo
*)addr
)->disklab
= lp
;
450 ((struct partinfo
*)addr
)->part
=
451 &lp
->d_partitions
[DISKPART(dev
)];
456 #ifdef __HAVE_OLD_DISKLABEL
461 struct disklabel
*tp
;
463 #ifdef __HAVE_OLD_DISKLABEL
464 if (cmd
== ODIOCSDINFO
|| cmd
== ODIOCWDINFO
) {
465 memset(&newlabel
, 0, sizeof newlabel
);
466 memcpy(&newlabel
, addr
, sizeof (struct olddisklabel
));
470 tp
= (struct disklabel
*)addr
;
472 if ((flag
& FWRITE
) == 0)
475 mutex_enter(&rc
->rc_disk
.dk_openlock
);
477 #ifdef __HAVE_OLD_DISKLABEL
478 cmd
== ODIOCSDINFO
||
481 setdisklabel(lp
, tp
, 0, 0) :
482 writedisklabel(dev
, rlstrategy
, lp
, 0));
483 mutex_exit(&rc
->rc_disk
.dk_openlock
);
489 if ((flag
& FWRITE
) == 0)
494 struct dkwedge_info
*dkw
= (void *) addr
;
496 if ((flag
& FWRITE
) == 0)
499 /* If the ioctl happens here, the parent is us. */
500 strcpy(dkw
->dkw_parent
, device_xname(rc
->rc_dev
));
501 return dkwedge_add(dkw
);
505 struct dkwedge_info
*dkw
= (void *) addr
;
507 if ((flag
& FWRITE
) == 0)
510 /* If the ioctl happens here, the parent is us. */
511 strcpy(dkw
->dkw_parent
, device_xname(rc
->rc_dev
));
512 return dkwedge_del(dkw
);
516 struct dkwedge_list
*dkwl
= (void *) addr
;
518 return dkwedge_list(&rc
->rc_disk
, dkwl
, l
);
531 struct rl_softc
* const rc
= device_lookup_private(&rl_cd
, DISKUNIT(dev
));
532 struct disklabel
*dl
;
537 dl
= rc
->rc_disk
.dk_label
;
538 size
= dl
->d_partitions
[DISKPART(dev
)].p_size
*
539 (dl
->d_secsize
/ DEV_BSIZE
);
544 rldump(dev_t dev
, daddr_t blkno
, void *va
, size_t size
)
551 rlread(dev_t dev
, struct uio
*uio
, int ioflag
)
553 return (physio(rlstrategy
, NULL
, dev
, B_READ
, minphys
, uio
));
557 rlwrite(dev_t dev
, struct uio
*uio
, int ioflag
)
559 return (physio(rlstrategy
, NULL
, dev
, B_WRITE
, minphys
, uio
));
562 static const char * const rlerr
[] = {
564 "operation incomplete",
571 "non-existent memory",
572 "memory parity error",
584 struct rlc_softc
*sc
= arg
;
590 aprint_error_dev(sc
->sc_dev
, "strange interrupt\n");
593 bus_dmamap_unload(sc
->sc_dmat
, sc
->sc_dmam
);
597 int error
= (cs
& RLCS_ERRMSK
) >> 10;
599 aprint_error_dev(sc
->sc_dev
, "%s\n", rlerr
[error
]);
601 bp
->b_resid
= bp
->b_bcount
;
604 if (sc
->sc_bytecnt
== 0) /* Finished transfer */
606 rlcstart(sc
, sc
->sc_bytecnt
? bp
: 0);
610 * Start routine. First position the disk to the given position,
611 * then start reading/writing. An optimization would be to be able
612 * to handle overlapping seeks between disks.
615 rlcstart(struct rlc_softc
*sc
, struct buf
*ob
)
617 struct disklabel
*lp
;
620 int bn
, cn
, sn
, tn
, blks
, err
;
623 return; /* Already doing something */
626 bp
= bufq_get(sc
->sc_q
);
628 return; /* Nothing to do */
629 sc
->sc_bufaddr
= bp
->b_data
;
630 sc
->sc_diskblk
= bp
->b_rawblkno
;
631 sc
->sc_bytecnt
= bp
->b_bcount
;
637 rc
= device_lookup_private(&rl_cd
, DISKUNIT(bp
->b_dev
));
639 lp
= rc
->rc_disk
.dk_label
;
641 cn
= bn
/ lp
->d_secpercyl
;
642 sn
= bn
% lp
->d_secpercyl
;
643 tn
= sn
/ lp
->d_nsectors
;
644 sn
= sn
% lp
->d_nsectors
;
649 * Check if we have to position disk first.
651 if (rc
->rc_cyl
!= cn
|| rc
->rc_head
!= tn
) {
652 u_int16_t da
= RLDA_SEEK
;
654 da
|= ((cn
- rc
->rc_cyl
) << RLDA_CYLSHFT
) | RLDA_DIR
;
656 da
|= ((rc
->rc_cyl
- cn
) << RLDA_CYLSHFT
);
661 RL_WREG(RL_CS
, RLCS_SEEK
| (rc
->rc_hwid
<< RLCS_USHFT
));
666 RL_WREG(RL_DA
, (cn
<< RLDA_CYLSHFT
) | (tn
? RLDA_HSRW
: 0) | (sn
<< 1));
667 blks
= sc
->sc_bytecnt
/DEV_BSIZE
;
669 if (sn
+ blks
> RL_SPT
/2)
670 blks
= RL_SPT
/2 - sn
;
671 RL_WREG(RL_MP
, -(blks
*DEV_BSIZE
)/2);
672 err
= bus_dmamap_load(sc
->sc_dmat
, sc
->sc_dmam
, sc
->sc_bufaddr
,
673 (blks
*DEV_BSIZE
), (bp
->b_flags
& B_PHYS
? bp
->b_proc
: 0),
676 panic("%s: bus_dmamap_load failed: %d",
677 device_xname(sc
->sc_dev
), err
);
678 RL_WREG(RL_BA
, (sc
->sc_dmam
->dm_segs
[0].ds_addr
& 0xffff));
681 sc
->sc_bufaddr
= (char *)sc
->sc_bufaddr
+ (blks
*DEV_BSIZE
);
682 sc
->sc_diskblk
+= blks
;
683 sc
->sc_bytecnt
-= (blks
*DEV_BSIZE
);
685 if (bp
->b_flags
& B_READ
)
686 RL_WREG(RL_CS
, RLCS_IE
|RLCS_RD
|(rc
->rc_hwid
<< RLCS_USHFT
));
688 RL_WREG(RL_CS
, RLCS_IE
|RLCS_WD
|(rc
->rc_hwid
<< RLCS_USHFT
));
692 * Called once per controller when an ubareset occurs.
693 * Retracts all disks and restarts active transfers.
696 rlcreset(device_t dev
)
698 struct rlc_softc
*sc
= device_private(dev
);
703 for (i
= 0; i
< rl_cd
.cd_ndevs
; i
++) {
704 if ((rc
= device_lookup_private(&rl_cd
, i
)) == NULL
)
706 if (rc
->rc_state
!= DK_OPEN
)
708 if (rc
->rc_rlc
!= sc
)
711 RL_WREG(RL_CS
, RLCS_RHDR
|(rc
->rc_hwid
<< RLCS_USHFT
));
714 rc
->rc_head
= ((mp
& RLMP_HS
) == RLMP_HS
);
715 rc
->rc_cyl
= (mp
>> 7) & 0777;
717 if (sc
->sc_active
== 0)
720 bufq_put(sc
->sc_q
, sc
->sc_active
);