* UNIBUS disk driver with overlapped seeks and ECC recovery.
* Add reading of bad sector information and disk layout from sector 1
* Add bad sector forwarding code
* Check multiple drive handling
* Check unibus reset code
* Check that offset recovery code, etc works
/* THIS SHOULD BE READ OFF THE PACK, PER DRIVE */
15884, 0, /* A=cyl 0 thru 26 */
33440, 27, /* B=cyl 27 thru 81 */
495520, 0, /* C=cyl 0 thru 814 */
15884, 562, /* D=cyl 562 thru 588 */
55936, 589, /* E=cyl 589 thru 680 */
81472, 681, /* F=cyl 681 thru 814 */
153824, 562, /* G=cyl 562 thru 814 */
291346, 82, /* H=cyl 82 thru 561 */
15884, 0, /* A=cyl 0 thru 49 */
33440, 50, /* B=cyl 50 thru 154 */
263360, 0, /* C=cyl 0 thru 822 */
213760, 155, /* H=cyl 155 thru 822 */
/* END OF STUFF WHICH SHOULD BE READ IN PER DISK */
#define _upSDIST 2 /* 1.0 msec */
#define _upRDIST 4 /* 2.0 msec */
int upprobe(), upslave(), upattach(), updgo(), upintr();
struct uba_ctlr
*upminfo
[NSC
];
struct uba_device
*updinfo
[NUP
];
struct uba_device
*upip
[NSC
][4];
u_short upstd
[] = { 0776700, 0774400, 0776300, 0 };
struct uba_driver scdriver
=
{ upprobe
, upslave
, upattach
, updgo
, upstd
, "up", updinfo
, "sc", upminfo
};
32, 19, 32*19, 823, up_sizes
, /* 9300/cdc */
/* 9300 actually has 815 cylinders... */
32, 10, 32*10, 823, fj_sizes
, /* fujitsu 160m */
UP_P400
, UP_M400
, UP_P400
, UP_M400
, UP_P800
, UP_M800
, UP_P800
, UP_M800
,
UP_P1200
, UP_M1200
, UP_P1200
, UP_M1200
, 0, 0, 0, 0
int upwstart
, upwatch(); /* Have started guardian */
br
= 0; cvec
= br
; br
= cvec
;
((struct updevice
*)reg
)->upcs1
= UP_IE
|UP_RDY
;
((struct updevice
*)reg
)->upcs1
= 0;
register struct updevice
*upaddr
= (struct updevice
*)reg
;
upaddr
->upcs1
= 0; /* conservative */
upaddr
->upcs2
= ui
->ui_slave
;
if (upaddr
->upcs2
&UP_NED
) {
upaddr
->upcs1
= UP_DCLR
|UP_GO
;
register struct uba_device
*ui
;
register struct updevice
*upaddr
;
timeout(upwatch
, (caddr_t
)0, hz
);
dk_mspw
[ui
->ui_dk
] = .0000020345;
upip
[ui
->ui_ctlr
][ui
->ui_slave
] = ui
;
up_softc
[ui
->ui_ctlr
].sc_ndrive
++;
upaddr
= (struct updevice
*)ui
->ui_addr
;
upaddr
->upcs2
= ui
->ui_slave
;
register struct uba_device
*ui
;
register struct upst
*st
;
int xunit
= minor(bp
->b_dev
) & 07;
sz
= (bp
->b_bcount
+511) >> 9;
if (ui
== 0 || ui
->ui_alive
== 0)
(bn
= dkblock(bp
))+sz
> st
->sizes
[xunit
].nblocks
)
bp
->b_cylin
= bn
/st
->nspc
+ st
->sizes
[xunit
].cyloff
;
dp
= &uputab
[ui
->ui_unit
];
if (bp
->b_actf
&& bp
->b_active
== 0)
(void) upstart(ui
->ui_mi
);
* Seek the drive to be where the data is
* and then generate another interrupt
* to actually start the transfer.
* If there is only one drive on the controller,
* or we are very close to the data, don't
* bother with the search. If called after
* searching once, don't bother to look where
* we are, just queue for transfer (to avoid
* positioning forever without transferrring.)
register struct uba_device
*ui
;
register struct buf
*bp
, *dp
;
register struct uba_ctlr
*um
;
register struct updevice
*upaddr
;
register struct upst
*st
;
* The SC21 cancels commands if you just say
* so we are cautious about handling of cs1.
* Also don't bother to clear as bits other than in upintr().
dk_busy
&= ~(1<<ui
->ui_dk
);
dp
= &uputab
[ui
->ui_unit
];
if ((bp
= dp
->b_actf
) == NULL
)
* If the controller is active, just remember
* that this device would like to be positioned...
* if we tried to position now we would confuse the SC21.
if (um
->um_tab
.b_active
) {
up_softc
[um
->um_ctlr
].sc_softas
|= 1<<ui
->ui_slave
;
* If we have already positioned this drive,
* then just put it on the ready queue.
upaddr
= (struct updevice
*)um
->um_addr
;
upaddr
->upcs2
= ui
->ui_slave
;
* If drive has just come up,
if ((upaddr
->upds
& UP_VV
) == 0) {
/* SHOULD WARN SYSTEM THAT THIS HAPPENED */
upaddr
->upcs1
= UP_IE
|UP_DCLR
|UP_GO
;
upaddr
->upcs1
= UP_IE
|UP_PRESET
|UP_GO
;
* If drive is offline, forget about positioning.
if ((upaddr
->upds
& (UP_DPR
|UP_MOL
)) != (UP_DPR
|UP_MOL
))
* If there is only one drive,
if (up_softc
[um
->um_ctlr
].sc_ndrive
== 1)
* Figure out where this transfer is going to
* and see if we are close enough to justify not searching.
sn
= (sn
+ st
->nsect
- upSDIST
) % st
->nsect
;
if (bp
->b_cylin
- upaddr
->updc
)
goto search
; /* Not on-cylinder */
goto done
; /* Ok just to be on-cylinder */
csn
= (upaddr
->upla
>>6) - sn
- 1;
if (csn
> st
->nsect
- upRDIST
)
upaddr
->updc
= bp
->b_cylin
;
* Not on cylinder at correct position,
upaddr
->upcs1
= UP_IE
|UP_SEEK
|UP_GO
;
upaddr
->upcs1
= UP_IE
|UP_SEARCH
|UP_GO
;
* Mark unit busy for iostat.
* Put it on the ready queue for the controller
* (unless its already there.)
if (um
->um_tab
.b_actf
== NULL
)
um
->um_tab
.b_actl
->b_forw
= dp
;
* Start up a transfer on a drive.
register struct uba_ctlr
*um
;
register struct buf
*bp
, *dp
;
register struct uba_device
*ui
;
register struct updevice
*upaddr
;
int dn
, sn
, tn
, cmd
, waitdry
;
* Pull a request off the controller queue
if ((dp
= um
->um_tab
.b_actf
) == NULL
)
if ((bp
= dp
->b_actf
) == NULL
) {
um
->um_tab
.b_actf
= dp
->b_forw
;
* Mark controller busy, and
* determine destination of this request.
ui
= updinfo
[dkunit(bp
)];
upaddr
= (struct updevice
*)ui
->ui_addr
;
* Select drive if not selected already.
if ((upaddr
->upcs2
&07) != dn
)
* Check that it is ready and online
while ((upaddr
->upds
&UP_DRY
) == 0) {
if ((upaddr
->upds
& UP_DREADY
) != UP_DREADY
) {
printf("up%d: not ready", dkunit(bp
));
if ((upaddr
->upds
& UP_DREADY
) != UP_DREADY
) {
dp
->b_actf
= bp
->av_forw
;
* Oh, well, sometimes this
* happens, for reasons unknown.
* Setup for the transfer, and get in the
upaddr
->updc
= bp
->b_cylin
;
upaddr
->upda
= (tn
<< 8) + sn
;
upaddr
->upwc
= -bp
->b_bcount
/ sizeof (short);
if (bp
->b_flags
& B_READ
)
cmd
= UP_IE
|UP_RCOM
|UP_GO
;
cmd
= UP_IE
|UP_WCOM
|UP_GO
;
* Now all ready to go, stuff the registers.
register struct updevice
*upaddr
= (struct updevice
*)um
->um_addr
;
upaddr
->upba
= um
->um_ubinfo
;
upaddr
->upcs1
= um
->um_cmd
|((um
->um_ubinfo
>>8)&0x300);
* Handle a disk interrupt.
register struct buf
*bp
, *dp
;
register struct uba_ctlr
*um
= upminfo
[sc21
];
register struct uba_device
*ui
;
register struct updevice
*upaddr
= (struct updevice
*)um
->um_addr
;
struct up_softc
*sc
= &up_softc
[um
->um_ctlr
];
int as
= (upaddr
->upas
& 0377) | sc
->sc_softas
;
* If controller wasn't transferring, then this is an
* interrupt for attention status on seeking drives.
if (um
->um_tab
.b_active
== 0) {
if (upaddr
->upcs1
& UP_TRE
)
* Release unibus resources and flush data paths.
* Get device and block structures, and a pointer
* to the uba_device for the drive. Select the drive.
ui
= updinfo
[dkunit(bp
)];
dk_busy
&= ~(1 << ui
->ui_dk
);
if ((upaddr
->upcs2
&07) != ui
->ui_slave
)
upaddr
->upcs2
= ui
->ui_slave
;
* Check for and process errors on
* either the drive or the controller.
if ((upaddr
->upds
&UP_ERR
) || (upaddr
->upcs1
&UP_TRE
)) {
while ((upaddr
->upds
& UP_DRY
) == 0) {
if (upaddr
->uper1
&UP_WLE
) {
* Give up on write locked devices
printf("up%d: write locked\n", dkunit(bp
));
} else if (++um
->um_tab
.b_errcnt
> 27) {
* After 28 retries (16 without offset, and
* 12 with offset positioning) give up.
printf("cs2=%b er1=%b er2=%b\n",
upaddr
->upcs2
, UPCS2_BITS
,
upaddr
->uper1
, UPER1_BITS
,
upaddr
->uper2
, UPER2_BITS
);
* If a soft ecc, correct it (continuing
* by returning if necessary.
* Otherwise fall through and retry the transfer
um
->um_tab
.b_active
= 0; /* force retry */
if ((upaddr
->uper1
&(UP_DCK
|UP_ECH
))==UP_DCK
)
* Clear drive error and, every eight attempts,
* (starting with the fourth)
* recalibrate to clear the slate.
upaddr
->upcs1
= UP_TRE
|UP_IE
|UP_DCLR
|UP_GO
;
if ((um
->um_tab
.b_errcnt
&07) == 4 && um
->um_tab
.b_active
== 0) {
upaddr
->upcs1
= UP_RECAL
|UP_IE
|UP_GO
;
* Advance recalibration finite state machine
* if recalibrate in progress, through
upaddr
->updc
= bp
->b_cylin
;
upaddr
->upcs1
= UP_SEEK
|UP_IE
|UP_GO
;
if (um
->um_tab
.b_errcnt
< 16 || (bp
->b_flags
&B_READ
) == 0)
upaddr
->upof
= up_offset
[um
->um_tab
.b_errcnt
& 017] | UP_FMT22
;
upaddr
->upcs1
= UP_IE
|UP_OFFSET
|UP_GO
;
* If still ``active'', then don't need any more retries.
if (um
->um_tab
.b_active
) {
* If we were offset positioning,
if (um
->um_tab
.b_errcnt
>= 16) {
upaddr
->upcs1
= UP_RTC
|UP_GO
|UP_IE
;
while (upaddr
->upds
& UP_PIP
)
um
->um_tab
.b_actf
= dp
->b_forw
;
dp
->b_actf
= bp
->av_forw
;
bp
->b_resid
= (-upaddr
->upwc
* sizeof(short));
* If this unit has more work to do,
* then start it up right away.
as
&= ~(1<<ui
->ui_slave
);
* Process other units which need attention.
* For each unit which needs attention, call
* the unit start routine to place the slave
* on the controller device queue.
unit
--; /* was 1 origin */
if (upustart(upip
[sc21
][unit
]))
* If the controller is not transferring, but
* there are devices ready to transfer, start
if (um
->um_tab
.b_actf
&& um
->um_tab
.b_active
== 0)
register int unit
= minor(dev
) >> 3;
physio(upstrategy
, &rupbuf
[unit
], dev
, B_READ
, minphys
);
register int unit
= minor(dev
) >> 3;
physio(upstrategy
, &rupbuf
[unit
], dev
, B_WRITE
, minphys
);
* Correct an ECC error, and restart the i/o to complete
* the transfer if necessary. This is quite complicated because
* the transfer may be going to an odd memory address base and/or
* across a page boundary.
register struct uba_device
*ui
;
register struct updevice
*up
= (struct updevice
*)ui
->ui_addr
;
register struct buf
*bp
= uputab
[ui
->ui_unit
].b_actf
;
register struct uba_ctlr
*um
= ui
->ui_mi
;
register struct upst
*st
;
struct uba_regs
*ubp
= ui
->ui_hd
->uh_uba
;
int reg
, bit
, byte
, npf
, mask
, o
, cmd
, ubaddr
;
* Npf is the number of sectors transferred before the sector
* containing the ECC error, and reg is the UBA register
* mapping (the first part of) the transfer.
* O is offset within a memory page of the first byte transferred.
npf
= btop((up
->upwc
* sizeof(short)) + bp
->b_bcount
) - 1;
reg
= btop(um
->um_ubinfo
&0x3ffff) + npf
;
o
= (int)bp
->b_un
.b_addr
& PGOFSET
;
printf("up%d%c: soft ecc sn%d\n", dkunit(bp
),
'a'+(minor(bp
->b_dev
)&07), bp
->b_blkno
+ npf
);
* Flush the buffered data path, and compute the
* byte and bit position of the error. The variable i
* is the byte offset in the transfer, the variable byte
* is the offset from a page boundary in main memory.
i
= up
->upec1
- 1; /* -1 makes 0 origin */
* Correct while possible bits remain of mask. Since mask
* contains 11 bits, we continue while the bit offset is > -11.
* Also watch out for end of this block and the end of the whole
while (i
< 512 && (int)ptob(npf
)+i
< bp
->b_bcount
&& bit
> -11) {
addr
= ptob(ubp
->uba_map
[reg
+btop(byte
)].pg_pfnum
)+
putmemc(addr
, getmemc(addr
)^(mask
<<bit
));
um
->um_tab
.b_active
++; /* Either complete or continuing... */
* Have to continue the transfer... clear the drive,
* and compute the position where the transfer is to continue.
* We have completed npf+1 sectors of the transfer already;
* restart at offset o of next sector (i.e. in UBA register reg+1).
up
->upcs1
= UP_TRE
|UP_IE
|UP_DCLR
|UP_GO
;
sn
= bn
%st
->nspc
+ npf
+ 1;
up
->upda
= (tn
<< 8) | sn
;
ubaddr
= (int)ptob(reg
+1) + o
;
cmd
= (ubaddr
>> 8) & 0x300;
cmd
|= UP_IE
|UP_GO
|UP_RCOM
;
* Reset driver after UBA init.
* Cancel software state of all pending transfers
* and restart all units and the controller.
register struct uba_ctlr
*um
;
register struct uba_device
*ui
;
for (sc21
= 0; sc21
< NSC
; sc21
++) {
if ((um
= upminfo
[sc21
]) == 0 || um
->um_ubanum
!= uban
||
um
->um_tab
.b_actf
= um
->um_tab
.b_actl
= 0;
up_softc
[sc21
].sc_recal
= 0;
printf("<%d>", (um
->um_ubinfo
>>28)&0xf);
((struct updevice
*)(um
->um_addr
))->upcs2
= UP_CLR
;
for (unit
= 0; unit
< NUP
; unit
++) {
if ((ui
= updinfo
[unit
]) == 0)
if (ui
->ui_alive
== 0 || ui
->ui_mi
!= um
)
uputab
[unit
].b_active
= 0;
* Wake up every second and if an interrupt is pending
* but nothing has happened increment a counter.
* If nothing happens for 20 seconds, reset the UNIBUS
register struct uba_ctlr
*um
;
register struct up_softc
*sc
;
timeout(upwatch
, (caddr_t
)0, hz
);
for (sc21
= 0; sc21
< NSC
; sc21
++) {
if (um
== 0 || um
->um_alive
== 0)
if (um
->um_tab
.b_active
== 0) {
for (unit
= 0; unit
< NUP
; unit
++)
if (uputab
[unit
].b_active
&&
updinfo
[unit
]->ui_mi
== um
)
if (sc
->sc_wticks
>= 20) {
printf("sc%d: lost interrupt\n", sc21
);
register struct uba_regs
*uba
;
register struct uba_device
*ui
;
#define phys(cast, addr) ((cast)((int)addr & 0x7fffffff))
ui
= phys(struct uba_device
*, updinfo
[unit
]);
uba
= phys(struct uba_hd
*, ui
->ui_hd
)->uh_physuba
;
upaddr
= (struct updevice
*)ui
->ui_physaddr
;
if ((upaddr
->upcs1
&UP_DVA
) == 0)
if ((upaddr
->upds
& UP_VV
) == 0) {
upaddr
->upcs1
= UP_DCLR
|UP_GO
;
upaddr
->upcs1
= UP_PRESET
|UP_GO
;
if ((upaddr
->upds
& UP_DREADY
) != UP_DREADY
)
sizes
= phys(struct size
*, st
->sizes
);
if (dumplo
< 0 || dumplo
+ num
>= sizes
[minor(dev
)&07].nblocks
)
blk
= num
> DBSIZE
? DBSIZE
: num
;
for (i
= 0; i
< blk
; i
++)
*(int *)io
++ = (btop(start
)+i
) | (1<<21) | UBAMR_MRV
;
bn
= dumplo
+ btop(start
);
cn
= bn
/st
->nspc
+ sizes
[minor(dev
)&07].cyloff
;
rp
= (short *) &upaddr
->upda
;
*--rp
= -blk
*NBPG
/ sizeof (short);
} while ((upaddr
->upcs1
& UP_RDY
) == 0);
if (upaddr
->upcs1
&UP_ERR
)