* UNIBUS disk driver with overlapped seeks and ECC recovery.
#define DELAY(N) { register int d; d = N; while (--d > 0); }
/* struct uba_minfo sc_minfo; */
/* 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 upcntrlr(), upslave(), updgo(), upintr();
struct uba_minfo
*upminfo
[NSC21
];
struct uba_dinfo
*updinfo
[NUP
];
struct uba_minfo up_minfo
[NSC21
];
/* there is no reason for this to be a global structure, it
is only known/used locally, it would be better combined
with up_softc - but that would mean I would have to alter
more than I want to just now. Similarly, there is no longer
any real need for upminfo, but the code still uses it so ...
struct uba_driver updriver
=
{ upcntrlr
, upslave
, updgo
, 4, 0, upstd
, "up", updinfo
};
32, 19, 32*19, 815, up_sizes
, /* 9300 */
32, 19, 32*19, 823, up_sizes
, /* so cdc will boot */
32, 10, 32*10, 823, fj_sizes
, /* fujitsu 160m */
P1200
, M1200
, P1200
, M1200
,
struct buf rupbuf
; /* GROT */
int upwstart
, upwatch(); /* Have started guardian */
((struct device
*)reg
)->upcs1
|= (IE
|RDY
);
return(1); /* just assume it is us (for now) */
upslave(ui
, reg
, slaveno
, uban
)
register struct device
*upaddr
= (struct device
*)reg
;
register struct uba_minfo
*um
;
upaddr
->upcs1
= 0; /* conservative */
/*** we should check device type (return 0 if we don't like it) ***/
/*** and set type index in ui->ui_type, but we KNOW all we are ***/
/*** going to see at the minute is a 9300, and the index for a ***/
/*** 9300 is 0, which is the value already in ui->ui_type, so ..***/
for (sc21
= 0; sc21
< NSC21
; sc21
++) {
if (um
->um_alive
== 0) { /* this is a new ctrlr */
um
->um_num
= sc21
; /* not needed after up_softc
upminfo
[sc21
] = um
; /* just till upminfo vanishes */
if (um
->um_addr
== reg
&& um
->um_ubanum
== uban
)
return(0); /* too many sc21's */
timeout(upwatch
, (caddr_t
)0, HZ
);
dk_mspw[UPDK_N+unit] = .0000020345;
register struct uba_dinfo
*ui
;
register struct uba_minfo
*um
;
register struct upst
*st
;
int xunit
= minor(bp
->b_dev
) & 07;
sz
= (sz
+511) >> 9; /* transfer size in 512 byte sectors */
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
;
disksort(&uputab
[ui
->ui_unit
], bp
);
if (uputab
[ui
->ui_unit
].b_active
== 0) {
if (bp
->b_actf
&& bp
->b_active
== 0)
(void) upstart(ui
->ui_mi
);
register struct uba_dinfo
*ui
;
register struct buf
*bp
, *dp
;
register struct uba_minfo
*um
;
register struct device
*upaddr
;
register struct upst
*st
;
/* SC21 cancels commands if you say cs1 = IE, so dont */
/* being ultra-cautious, we clear as bits only in upintr() */
dk_busy
&= ~(1<<ui
->ui_dk
);
dp
= &uputab
[ui
->ui_unit
];
if ((bp
= dp
->b_actf
) == NULL
)
/* dont confuse controller by giving SEARCH while dt in progress */
if (um
->um_tab
.b_active
) {
up_softc
[um
->um_num
].sc_softas
|= 1<<ui
->ui_slave
;
upaddr
= (struct device
*)um
->um_addr
;
upaddr
->upcs2
= ui
->ui_slave
;
if ((upaddr
->upds
& VV
) == 0) {
upaddr
->upcs1
= IE
|DCLR
|GO
;
upaddr
->upcs1
= IE
|PRESET
|GO
;
if ((upaddr
->upds
& (DPR
|MOL
)) != (DPR
|MOL
))
sn
= (sn
+ st
->nsect
- upSDIST
) % st
->nsect
;
goto search
; /* Not on-cylinder */
/**** WHAT SHOULD THIS BE NOW ???
goto done; /* Ok just to be on-cylinder */
csn
= (upaddr
->upla
>>6) - sn
- 1;
if (csn
> st
->nsect
- upRDIST
)
upaddr->upcs1 = IE|SEEK|GO;
upaddr
->upcs1
= IE
|SEARCH
|GO
;
if (um
->um_tab
.b_actf
== NULL
)
um
->um_tab
.b_actl
->b_forw
= dp
;
register struct uba_minfo
*um
;
register struct buf
*bp
, *dp
;
register struct uba_dinfo
*ui
;
register struct device
*upaddr
;
register struct upst
*st
;
if ((dp
= um
->um_tab
.b_actf
) == NULL
)
if ((bp
= dp
->b_actf
) == NULL
) {
um
->um_tab
.b_actf
= dp
->b_forw
;
* Mark the controller busy, and multi-part disk address.
* Select the unit on which the i/o is to take place.
ui
= updinfo
[dkunit(bp
)];
upaddr
= (struct device
*)ui
->ui_addr
;
if ((upaddr
->upcs2
& 07) != dn
)
up_softc
[um
->um_num
].sc_info
=
ubasetup(ui
->ui_ubanum
, bp
, UBA_NEEDBDP
|UBA_CANTWAIT
);
* If drive is not present and on-line, then
* get rid of this with an error and loop to get
* rid of the rest of its queued requests.
* (Then on to any other ready drives.)
if ((upaddr
->upds
& (DPR
|MOL
)) != (DPR
|MOL
)) {
printf("!DPR || !MOL, unit %d, ds %o", dn
, upaddr
->upds
);
if ((upaddr
->upds
& (DPR
|MOL
)) != (DPR
|MOL
)) {
dp
->b_actf
= bp
->av_forw
;
/* A funny place to do this ... */
ubarelse(ui
->ui_ubanum
, &up_softc
[um
->um_num
].sc_info
);
printf("-- came back\n");
* If this is a retry, then with the 16'th retry we
* begin to try offsetting the heads to recover the data.
if (um
->um_tab
.b_errcnt
>= 16 && (bp
->b_flags
&B_READ
) != 0) {
upaddr
->upof
= up_offset
[um
->um_tab
.b_errcnt
& 017] | FMT22
;
upaddr
->upcs1
= IE
|OFFSET
|GO
;
while (upaddr
->upds
& PIP
)
* Now set up the transfer, retrieving the high
* 2 bits of the UNIBUS address from the information
* returned by ubasetup() for the cs1 register bits 8 and 9.
upaddr
->updc
= bp
->b_cylin
;
upaddr
->upda
= (tn
<< 8) + sn
;
upaddr
->upba
= up_softc
[um
->um_num
].sc_info
;
upaddr
->upwc
= -bp
->b_bcount
/ sizeof (short);
cmd
= (up_softc
[um
->um_num
].sc_info
>> 8) & 0x300;
if (bp
->b_flags
& B_READ
)
* This is a controller busy situation.
* Record in dk slot NUP+UPDK_N (after last drive)
* unless there aren't that many slots reserved for
* us in which case we record this as a drive busy
* (if there is room for that).
dk_wds
[unit
] += bp
->b_bcount
>>6;
* Handle a device interrupt.
* If the transferring drive needs attention, service it
* retrying on error or beginning next transfer.
* Service all other ready drives, calling ustart to transfer
* their blocks to the ready queue in um->um_tab, and then restart
* the controller if there is anything to do.
register struct buf
*bp
, *dp
;
register struct uba_minfo
*um
= upminfo
[sc21
];
register struct uba_dinfo
*ui
;
register struct device
*upaddr
= (struct device
*)um
->um_addr
;
int as
= upaddr
->upas
& 0377;
up_softc
[um
->um_num
].sc_wticks
= 0;
if (um
->um_tab
.b_active
) {
if ((upaddr
->upcs1
& RDY
) == 0) {
printf("!RDY: cs1 %o, ds %o, wc %d\n", upaddr
->upcs1
,
upaddr
->upds
, upaddr
->upwc
);
printf("as=%d act %d %d %d\n", as
, um
->um_tab
.b_active
,
uputab
[0].b_active
, uputab
[1].b_active
);
ui
= updinfo
[dkunit(bp
)];
dk_busy
&= ~(1 << ui
->ui_dk
);
upaddr
->upcs2
= ui
->ui_slave
;
if ((upaddr
->upds
&ERR
) || (upaddr
->upcs1
&TRE
)) {
while ((upaddr
->upds
& DRY
) == 0)
if (++um
->um_tab
.b_errcnt
> 28 || upaddr
->uper1
&WLE
)
um
->um_tab
.b_active
= 0; /* force retry */
if (um
->um_tab
.b_errcnt
> 27) {
cs2
= (int)upaddr
->upcs2
;
deverror(bp
, cs2
, (int)upaddr
->uper1
);
if ((upaddr
->uper1
&(DCK
|ECH
))==DCK
&& upecc(ui
))
upaddr
->upcs1
= TRE
|IE
|DCLR
|GO
;
if ((um
->um_tab
.b_errcnt
&07) == 4) {
upaddr
->upcs1
= RECAL
|GO
|IE
;
while(upaddr
->upds
& PIP
)
if (um
->um_tab
.b_errcnt
== 28 && cs2
&(NEM
|MXF
)) {
if (um
->um_tab
.b_active
) {
if (um
->um_tab
.b_errcnt
>= 16) {
upaddr
->upcs1
= RTC
|GO
|IE
;
while (upaddr
->upds
& PIP
)
um
->um_tab
.b_actf
= dp
->b_forw
;
dp
->b_actf
= bp
->av_forw
;
bp
->b_resid
= (-upaddr
->upwc
* sizeof(short));
printf("resid %d ds %o er? %o %o %o\n",
bp
->b_resid
, upaddr
->upds
,
upaddr
->uper1
, upaddr
->uper2
, upaddr
->uper3
);
up_softc
[um
->um_num
].sc_softas
&= ~(1<<ui
->ui_slave
);
ubarelse(ui
->ui_ubanum
, &up_softc
[um
->um_num
].sc_info
);
as
|= up_softc
[um
->um_num
].sc_softas
;
for (unit
= 0; unit
< NUP
; unit
++) {
if ((ui
= updinfo
[unit
]) == 0 || ui
->ui_mi
!= um
)
if (um
->um_tab
.b_actf
&& um
->um_tab
.b_active
== 0)
physio(upstrategy
, &rupbuf
, dev
, B_READ
, minphys
);
physio(upstrategy
, &rupbuf
, 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_dinfo
*ui
;
register struct device
*up
= (struct device
*)ui
->ui_addr
;
register struct buf
*bp
= uputab
[ui
->ui_unit
].b_actf
;
register struct uba_minfo
*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(up_softc
[um
->um_num
].sc_info
&0x3ffff) + npf
;
o
= (int)bp
->b_un
.b_addr
& PGOFSET
;
printf("%D ", bp
->b_blkno
+npf
);
up
->upof
= FMT22
; /* == RTC ???? */
* 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.
ubp
->uba_dpr
[(up_softc
[um
->um_num
].sc_info
>>28)&0x0f] |= UBA_BNE
;
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
= TRE
|IE
|DCLR
|GO
;
sn
= bn
%st
->nspc
+ npf
+ 1;
up
->upda
= (tn
<< 8) | sn
;
ubaddr
= (int)ptob(reg
+1) + o
;
cmd
= (ubaddr
>> 8) & 0x300;
* Reset driver after UBA init.
* Cancel software state of all pending transfers
* and restart all units and the controller.
register struct uba_minfo
*um
;
register struct uba_dinfo
*ui
;
for (sc21
= 0; sc21
< NSC21
; sc21
++) {
if ((um
= upminfo
[sc21
]) == 0)
if (um
->um_ubanum
!= uban
)
DELAY(15000000); /* give it time to self-test */
um
->um_tab
.b_actf
= um
->um_tab
.b_actl
= 0;
if (up_softc
[um
->um_num
].sc_info
) {
printf("<%d>", (up_softc
[um
->um_num
].sc_info
>>28)&0xf);
ubarelse(um
->um_ubanum
, &up_softc
[um
->um_num
].sc_info
);
((struct device
*)(um
->um_addr
))->upcs2
= CLR
;
for (unit
= 0; unit
< NUP
; unit
++) {
if ((ui
= updinfo
[unit
]) == 0)
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 controller
register struct uba_minfo
*um
;
timeout(upwatch
, (caddr_t
)0, HZ
);
for (sc21
= 0; sc21
< NSC21
; sc21
++) {
if (um
->um_tab
.b_active
== 0) {
for (unit
= 0; unit
< NUP
; unit
++)
if (updinfo
[unit
]->ui_mi
== um
&&
up_softc
[sc21
].sc_wticks
= 0;
up_softc
[sc21
].sc_wticks
++;
if (up_softc
[sc21
].sc_wticks
>= 20) {
up_softc
[sc21
].sc_wticks
= 0;
printf("LOST INTERRUPT RESET");
int num
, blk
, unit
, nsect
, ntrak
, nspc
;
register struct uba_regs
*uba
;
register struct uba_dinfo
*ui
;
#define phys1(cast, addr) ((cast)((int)addr & 0x7fffffff))
#define phys(cast, addr) phys1(cast, phys1(cast *, &addr))
ui
= phys(struct uba_dinfo
*, updinfo
[unit
]);
uba
= phys(struct uba_hd
*, ui
->ui_hd
)->uh_physuba
;
uba
->uba_cr
= UBA_ADINIT
;
uba
->uba_cr
= UBA_IFS
|UBA_BRIE
|UBA_USEFIE
|UBA_SUEFIE
;
while ((uba
->uba_cnfgr
& UBA_UBIC
) == 0)
upaddr
= (struct device
*)ui
->ui_physaddr
;
while ((upaddr
->upcs1
&DVA
) == 0)
if ((upaddr
->upds
& VV
) == 0) {
upaddr
->upcs1
= PRESET
|GO
;
if ((upaddr
->upds
& (DPR
|MOL
)) != (DPR
|MOL
)) {
printf("up !DPR || !MOL\n");
st
= phys1(struct upst
*, &upst
[ui
->ui_type
]);
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) | UBA_MRV
;
bn
= dumplo
+ btop(start
);
cn
= bn
/nspc
+ sizes
[minor(dev
)&07].cyloff
;
rp
= (short *) &upaddr
->upda
;
*--rp
= -blk
*NBPG
/ sizeof (short);
} while ((upaddr
->upcs1
& RDY
) == 0);
printf("up dump dsk err: (%d,%d,%d) cs1=%x, er1=%x\n",
cn
, tn
, sn
, upaddr
->upcs1
, upaddr
->uper1
);