4c9a4bee11777d8fb4d86891a216f2973f6fd0c5
* Copyright (c) 1982 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
* @(#)idc.c 6.12 (Berkeley) %G%
#define printd if(idcdebug)printf
#define trace(a,b) {*trp++ = *(int*)a; *trp++ = (int)b; if(trp>&idctrb[998])trp=idctrb;}
* IDC (RB730) disk driver
* There can only ever be one IDC on a machine,
* and only on a VAX-11/730. We take advantage
* of that to simplify the driver.
#include "../machine/pte.h"
int sc_bcnt
; /* number of bytes to transfer */
int sc_resid
; /* total number of bytes to transfer */
int sc_ubaddr
; /* Unibus address of data */
short sc_unit
; /* unit doing transfer */
short sc_softas
; /* software attention summary bits */
} sc_un
; /* prototype disk address register */
#define dar_dar dar_l /* the whole disk address */
#define dar_cyl dar_w[1] /* cylinder address */
#define dar_trk dar_b[1] /* track */
#define dar_sect dar_b[0] /* sector */
#define sc_dar sc_un.dar_dar
#define sc_cyl sc_un.dar_cyl
#define sc_trk sc_un.dar_trk
#define sc_sect sc_un.dar_sect
#define idcunit(dev) (minor(dev) >> 3)
/* THIS SHOULD BE READ OFF THE PACK, PER DRIVE */
15884, 0, /* A=cyl 0 thru 399 */
4480, 400, /* B=cyl 400 thru 510 */
20480, 0, /* C=cyl 0 thru 511 */
15884, 0, /* A=cyl 0 thru 36 */
33440, 37, /* B=cyl 37 thru 114 */
242606, 0, /* C=cyl 0 thru 558 */
82080, 115, /* G=cyl 115 thru 304 */
110143, 305, /* H=cyl 305 thru 558 */
/* END OF STUFF WHICH SHOULD BE READ IN PER DISK */
int idcprobe(), idcslave(), idcattach(), idcdgo(), idcintr();
struct uba_ctlr
*idcminfo
[NIDC
];
struct uba_device
*idcdinfo
[NRB
];
u_short idcstd
[] = { 0174400, 0};
struct uba_driver idcdriver
=
{ idcprobe
, idcslave
, idcattach
, idcdgo
, idcstd
, "rb", idcdinfo
, "idc", idcminfo
, 0 };
union idc_dar idccyl
[NRB
];
256, NRB02SECT
, NRB02TRK
, NRB02SECT
*NRB02TRK
, NRB02CYL
, rb02_sizes
,
512, NRB80SECT
, NRB80TRK
, NRB80SECT
*NRB80TRK
, NRB80CYL
, rb80_sizes
,
int idcwstart
, idcwticks
, idcwatch();
register struct idcdevice
*idcaddr
;
br
= 0; cvec
= br
; br
= cvec
;
idcaddr
= (struct idcdevice
*)((caddr_t
)uba_hd
[0].uh_uba
+ 0x200);
idcaddr
->idccsr
= IDC_ATTN
|IDC_IE
;
while ((idcaddr
->idccsr
& IDC_CRDY
) == 0)
idcaddr
->idccsr
= IDC_ATTN
|IDC_CRDY
;
return (sizeof (struct idcdevice
));
register struct idcdevice
*idcaddr
;
idcaddr
= (struct idcdevice
*)((caddr_t
)uba_hd
[0].uh_uba
+ 0x200);
idcaddr
->idcmpr
= IDCGS_GETSTAT
;
idcaddr
->idccsr
= IDC_GETSTAT
|(ui
->ui_slave
<<8);
(void) idcwait(idcaddr
, 0);
idcaddr
->idccsr
= IDC_CRDY
|(1<<(ui
->ui_slave
+16));
(void) idcwait(idcaddr
, 0);
/* read header to synchronize microcode */
idcaddr
->idccsr
= (ui
->ui_slave
<<8)|IDC_RHDR
;
(void) idcwait(idcaddr
, 0);
i
= idcaddr
->idcmpr
; /* read header word 1 */
i
= idcaddr
->idcmpr
; /* read header word 2 */
if ((idcaddr
->idccsr
& (IDC_ERR
|IDC_R80
)) == IDC_R80
)
else if ((idcaddr
->idccsr
& (IDC_DE
|IDC_R80
)) == 0)
* RB02 may not have pack spun up, just look for drive error.
register struct uba_device
*ui
;
* Fix all addresses to correspond
* to the "real" IDC address.
ui
->ui_mi
->um_addr
= ui
->ui_addr
= (caddr_t
)uba_hd
[0].uh_uba
+ 0x200;
ui
->ui_physaddr
= (caddr_t
)uba_hd
[0].uh_physuba
+ 0x200;
timeout(idcwatch
, (caddr_t
)0, hz
);
dk_mspw
[ui
->ui_dk
] = 1.0 / (60 * NRB80SECT
* 256);
dk_mspw
[ui
->ui_dk
] = 1.0 / (60 * NRB02SECT
* 128);
idccyl
[ui
->ui_unit
].dar_dar
= -1;
register int unit
= idcunit(dev
);
register struct uba_device
*ui
;
if (unit
>= NRB
|| (ui
= idcdinfo
[unit
]) == 0 || ui
->ui_alive
== 0)
register struct uba_device
*ui
;
register struct idcst
*st
;
int xunit
= minor(bp
->b_dev
) & 07;
sz
= (bp
->b_bcount
+511) >> 9;
unit
= idcunit(bp
->b_dev
);
if (ui
== 0 || ui
->ui_alive
== 0) {
st
= &idcst
[ui
->ui_type
];
(bn
= bp
->b_blkno
)+sz
> st
->sizes
[xunit
].nblocks
) {
if (bp
->b_blkno
== st
->sizes
[xunit
].nblocks
) {
bp
->b_resid
= bp
->b_bcount
;
bp
->b_cylin
= bn
/st
->nspc
+ st
->sizes
[xunit
].cyloff
;
dp
= &idcutab
[ui
->ui_unit
];
if (bp
->b_actf
&& bp
->b_active
== 0)
(void) idcstart(ui
->ui_mi
);
register struct uba_device
*ui
;
register struct buf
*bp
, *dp
;
register struct uba_ctlr
*um
;
register struct idcdevice
*idcaddr
;
register struct idcst
*st
;
dk_busy
&= ~(1<<ui
->ui_dk
);
dp
= &idcutab
[ui
->ui_unit
];
idcaddr
= (struct idcdevice
*)um
->um_addr
;
if (um
->um_tab
.b_active
) {
idc_softc
.sc_softas
|= 1<<unit
;
trace("umac",idc_softc
.sc_softas
);
if ((bp
= dp
->b_actf
) == NULL
) {
trace("dpac",dp
->b_active
);
st
= &idcst
[ui
->ui_type
];
cyltrk
.dar_cyl
= bp
->b_cylin
;
cyltrk
.dar_trk
= (bn
/ st
->nsect
) % st
->ntrak
;
printd("idcustart, unit %d, cyltrk 0x%x\n", unit
, cyltrk
.dar_dar
);
* If on cylinder, no need to seek.
if (cyltrk
.dar_dar
== idccyl
[ui
->ui_unit
].dar_dar
)
* RB80 can change heads (tracks) just by loading
* the disk address register, perform optimization
* here instead of doing a full seek.
if (ui
->ui_type
&& cyltrk
.dar_cyl
== idccyl
[ui
->ui_unit
].dar_cyl
) {
idcaddr
->idccsr
= IDC_CRDY
|IDC_IE
|IDC_SEEK
|(unit
<<8);
idcaddr
->idcdar
= cyltrk
.dar_dar
;
idccyl
[ui
->ui_unit
].dar_dar
= cyltrk
.dar_dar
;
* Need to do a full seek. Select the unit, clear
* its attention bit, set the command, load the
* disk address register, and then go.
IDC_CRDY
|IDC_IE
|IDC_SEEK
|(unit
<<8)|(1<<(unit
+16));
idcaddr
->idcdar
= cyltrk
.dar_dar
;
idccyl
[ui
->ui_unit
].dar_dar
= cyltrk
.dar_dar
;
idcaddr
->idccsr
= IDC_IE
|IDC_SEEK
|(unit
<<8);
* RB80's initiate seeks very quickly. Wait for it
* to come ready rather than taking the interrupt.
if (idcwait(idcaddr
, 10) == 0)
idcaddr
->idccsr
&= ~IDC_ATTN
;
/* has the seek completed? */
if (idcaddr
->idccsr
& IDC_DRDY
) {
IDC_CRDY
|IDC_IE
|IDC_SEEK
|(unit
<<8)|(1<<(unit
+16));
printd(", idccsr = 0x%x\n", idcaddr
->idccsr
);
trace("!=2",dp
->b_active
);
if (um
->um_tab
.b_actf
== NULL
)
trace("!NUL",um
->um_tab
.b_actl
);
um
->um_tab
.b_actl
->b_forw
= dp
;
register struct uba_ctlr
*um
;
register struct buf
*bp
, *dp
;
register struct uba_device
*ui
;
register struct idcdevice
*idcaddr
;
register struct idc_softc
*sc
;
if ((dp
= um
->um_tab
.b_actf
) == NULL
) {
if ((bp
= dp
->b_actf
) == NULL
) {
um
->um_tab
.b_actf
= dp
->b_forw
;
ui
= idcdinfo
[idcunit(bp
->b_dev
)];
st
= &idcst
[ui
->ui_type
];
sc
->sc_cyl
= bp
->b_cylin
;
idcaddr
= (struct idcdevice
*)ui
->ui_addr
;
printd("idcstart, unit %d, dar 0x%x", ui
->ui_slave
, sc
->sc_dar
);
if (bp
->b_flags
& B_READ
)
cmd
= IDC_IE
|IDC_READ
|(ui
->ui_slave
<<8);
cmd
= IDC_IE
|IDC_WRITE
|(ui
->ui_slave
<<8);
idcaddr
->idccsr
= IDC_CRDY
|cmd
;
if ((idcaddr
->idccsr
&IDC_DRDY
) == 0) {
printf("rb%d: not ready\n", idcunit(bp
->b_dev
));
dp
->b_actf
= bp
->av_forw
;
idccyl
[ui
->ui_unit
].dar_dar
= sc
->sc_dar
;
idccyl
[ui
->ui_unit
].dar_sect
= 0;
sn
= (st
->nsect
- sn
) * st
->nbps
;
sc
->sc_resid
= bp
->b_bcount
;
sc
->sc_unit
= ui
->ui_slave
;
printd(", bcr 0x%x, cmd 0x%x\n", sn
, cmd
);
register struct uba_ctlr
*um
;
register struct idcdevice
*idcaddr
= (struct idcdevice
*)um
->um_addr
;
register struct idc_softc
*sc
= &idc_softc
;
* VERY IMPORTANT: must load registers in this order.
idcaddr
->idcbar
= sc
->sc_ubaddr
= um
->um_ubinfo
&0x3ffff;
idcaddr
->idcbcr
= -sc
->sc_bcnt
;
idcaddr
->idcdar
= sc
->sc_dar
;
printd("idcdgo, ubinfo 0x%x, cmd 0x%x\n", um
->um_ubinfo
, um
->um_cmd
);
idcaddr
->idccsr
= um
->um_cmd
;
/*** CLEAR SPURIOUS ATTN ON R80? ***/
register struct uba_ctlr
*um
= idcminfo
[idc
];
register struct uba_device
*ui
;
register struct idcdevice
*idcaddr
= (struct idcdevice
*)um
->um_addr
;
register struct idc_softc
*sc
= &idc_softc
;
register struct buf
*bp
, *dp
;
int unit
, as
, er
, cmd
, ds
= 0;
printd("idcintr, idccsr 0x%x", idcaddr
->idccsr
);
trace("intr", um
->um_tab
.b_active
);
if (um
->um_tab
.b_active
== 2) {
* Process a data transfer complete interrupt.
ui
= idcdinfo
[idcunit(bp
->b_dev
)];
st
= &idcst
[ui
->ui_type
];
idcaddr
->idccsr
= IDC_IE
|IDC_CRDY
|(unit
<<8);
if ((er
= idcaddr
->idccsr
) & IDC_ERR
) {
idcaddr
->idcmpr
= IDCGS_GETSTAT
;
idcaddr
->idccsr
= IDC_GETSTAT
|(unit
<<8);
(void) idcwait(idcaddr
, 0);
IDC_IE
|IDC_CRDY
|(1<<(unit
+16));
printd(", er 0x%x, ds 0x%x", er
, ds
);
printf("rb%d: write locked\n",
} else if (++um
->um_tab
.b_errcnt
> 28 || er
&IDC_HARD
) {
printf("csr=%b ds=%b\n", er
, IDCCSR_BITS
, ds
,
ui
->ui_type
?IDCRB80DS_BITS
:IDCRB02DS_BITS
);
} else if (er
& IDC_DCK
) {
switch ((int)(er
& IDC_ECS
)) {
/* recoverable error, set up for retry */
if ((sc
->sc_resid
-= sc
->sc_bcnt
) != 0) {
sc
->sc_ubaddr
+= sc
->sc_bcnt
;
* Current transfer is complete, have
* we overflowed to the next track?
if ((sc
->sc_sect
+= sc
->sc_bcnt
/st
->nbps
) == st
->nsect
) {
if (++sc
->sc_trk
== st
->ntrak
) {
} else if (ui
->ui_type
) {
* RB80 can change heads just by
* loading the disk address register.
idcaddr
->idccsr
= IDC_SEEK
|IDC_CRDY
|
printd(", change to track 0x%x", sc
->sc_dar
);
idcaddr
->idcdar
= sc
->sc_dar
;
idccyl
[ui
->ui_unit
].dar_dar
= sc
->sc_dar
;
idccyl
[ui
->ui_unit
].dar_sect
= 0;
* Changing tracks on RB02 or cylinders
cmd
= IDC_IE
|IDC_SEEK
|(unit
<<8);
idcaddr
->idccsr
= cmd
|IDC_CRDY
;
idcaddr
->idcdar
= sc
->sc_dar
;
printd(", seek to 0x%x\n", sc
->sc_dar
);
idccyl
[ui
->ui_unit
].dar_dar
= sc
->sc_dar
;
idccyl
[ui
->ui_unit
].dar_sect
= 0;
if (idcwait(idcaddr
, 10) == 0)
idcaddr
->idccsr
&= ~IDC_ATTN
;
if (idcaddr
->idccsr
& IDC_DRDY
)
* Continue transfer on current track.
sc
->sc_bcnt
= (st
->nsect
-sc
->sc_sect
)*st
->nbps
;
if (sc
->sc_bcnt
> sc
->sc_resid
)
sc
->sc_bcnt
= sc
->sc_resid
;
if (bp
->b_flags
& B_READ
)
cmd
= IDC_IE
|IDC_READ
|(unit
<<8);
cmd
= IDC_IE
|IDC_WRITE
|(unit
<<8);
idcaddr
->idccsr
= cmd
|IDC_CRDY
;
idcaddr
->idcbar
= sc
->sc_ubaddr
;
idcaddr
->idcbcr
= -sc
->sc_bcnt
;
idcaddr
->idcdar
= sc
->sc_dar
;
printd(", continue I/O 0x%x, 0x%x\n", sc
->sc_dar
, sc
->sc_bcnt
);
* Entire transfer is done, clean up.
dk_busy
&= ~(1 << ui
->ui_dk
);
um
->um_tab
.b_actf
= dp
->b_forw
;
dp
->b_actf
= bp
->av_forw
;
trace("done", dp
); trace(&um
->um_tab
.b_actf
, dp
->b_actf
);
bp
->b_resid
= sc
->sc_resid
;
printd(", iodone, resid 0x%x\n", bp
->b_resid
);
} else if (um
->um_tab
.b_active
== 1) {
* Got an interrupt while setting up for a command
* or doing a mid-transfer seek. Save any attentions
* for later and process a mid-transfer seek complete.
idcaddr
->idccsr
= IDC_IE
|IDC_CRDY
|(as
&IDC_ATTN
);
sc
->sc_softas
|= as
& ~(1<<unit
);
printd(", seek1 complete");
printd(", as1 %o\n", as
);
* Process any seek initiated or complete interrupts.
idcaddr
->idccsr
= IDC_IE
|IDC_CRDY
|(as
&IDC_ATTN
);
as
= ((as
>> 16) & 0xf) | sc
->sc_softas
;
for (unit
= 0; unit
< NRB
; unit
++)
idcaddr
->idccsr
= IDC_IE
|IDC_CRDY
|(unit
<<8);
printd(", attn unit %d", unit
);
if (idcaddr
->idccsr
& IDC_DRDY
)
printd(", unsol. intr. unit %d", unit
);
if (um
->um_tab
.b_actf
&& um
->um_tab
.b_active
== 0) {
trace("stum",um
->um_tab
.b_actf
);
register struct idcdevice
*addr
;
while (--n
&& (addr
->idccsr
& IDC_CRDY
) == 0)
register int unit
= idcunit(dev
);
return (physio(idcstrategy
, &ridcbuf
[unit
], dev
, B_READ
, minphys
, uio
));
register int unit
= idcunit(dev
);
return (physio(idcstrategy
, &ridcbuf
[unit
], dev
, B_WRITE
, minphys
, uio
));
register struct uba_device
*ui
;
register struct idcdevice
*idc
= (struct idcdevice
*)ui
->ui_addr
;
register struct buf
*bp
= idcutab
[ui
->ui_unit
].b_actf
;
register struct uba_ctlr
*um
= ui
->ui_mi
;
register struct idcst
*st
;
struct uba_regs
*ubp
= ui
->ui_hd
->uh_uba
;
npf
= btop(idc
->idcbcr
+ idc_softc
.sc_bcnt
) - 1;;
reg
= btop(idc_softc
.sc_ubaddr
) + npf
;
o
= (int)bp
->b_un
.b_addr
& PGOFSET
;
st
= &idcst
[ui
->ui_type
];
um
->um_tab
.b_active
= 1; /* Either complete or continuing... */
log(LOG_WARNING
, "rb%d%c: soft ecc sn%d\n", idcunit(bp
->b_dev
),
'a'+(minor(bp
->b_dev
)&07),
(cn
*st
->ntrak
+ tn
) * st
->nsect
+ sn
+ npf
);
i
= idc
->idceccpos
- 1; /* -1 makes 0 origin */
while (i
< 512 && (int)ptob(npf
)+i
< idc_softc
.sc_bcnt
&& bit
> -11) {
* addr = ptob(ubp->uba_map[reg+btop(byte)].pg_pfnum)+
* but this generates an extzv which hangs the UNIBUS.
addr
= ptob(*(int *)&ubp
->uba_map
[reg
+btop(byte
)]&0x1fffff)+
putmemc(addr
, getmemc(addr
)^(mask
<<bit
));
idc_softc
.sc_bcnt
+= idc
->idcbcr
;
um
->um_tab
.b_errcnt
= 0; /* error has been corrected */
register struct uba_ctlr
*um
;
register struct uba_device
*ui
;
if ((um
= idcminfo
[0]) == 0 || um
->um_ubanum
!= uban
||
um
->um_tab
.b_actf
= um
->um_tab
.b_actl
= 0;
printf("<%d>", (um
->um_ubinfo
>>28)&0xf);
for (unit
= 0; unit
< NRB
; unit
++) {
if ((ui
= idcdinfo
[unit
]) == 0 || ui
->ui_alive
== 0)
idcutab
[unit
].b_active
= 0;
register struct uba_ctlr
*um
;
timeout(idcwatch
, (caddr_t
)0, hz
);
if (um
== 0 || um
->um_alive
== 0)
if (um
->um_tab
.b_active
== 0) {
for (unit
= 0; unit
< NRB
; unit
++)
if (idcutab
[unit
].b_active
)
printf("idc0: lost interrupt\n");
struct idcdevice
*idcaddr
;
register struct uba_regs
*uba
;
register struct uba_device
*ui
;
#define phys(cast, addr) ((cast)((int)addr & 0x7fffffff))
ui
= phys(struct uba_device
*, idcdinfo
[unit
]);
uba
= phys(struct uba_hd
*, ui
->ui_hd
)->uh_physuba
;
idcaddr
= (struct idcdevice
*)ui
->ui_physaddr
;
if (idcwait(idcaddr
, 100) == 0)
* Since we can only transfer one track at a time, and
* the rl02 has 256 byte sectors, all the calculations
* are done in terms of physical sectors (i.e. num and blk
* are in sectors not NBPG blocks.
st
= phys(struct idcst
*, &idcst
[ui
->ui_type
]);
sizes
= phys(struct size
*, st
->sizes
);
if (dumplo
+ maxfree
>= sizes
[minor(dev
)&07].nblocks
)
num
= sizes
[minor(dev
)&07].nblocks
- dumplo
;
bn
= (dumplo
+ btop(start
)) * nspg
;
dar
.dar_cyl
= bn
/ st
->nspc
+ sizes
[minor(dev
)&07].cyloff
;
dar
.dar_trk
= bn
/ st
->nsect
;
dar
.dar_sect
= bn
% st
->nsect
;
blk
= st
->nsect
- dar
.dar_sect
;
for (i
= 0; i
< (blk
+ nspg
- 1) / nspg
; i
++)
*(int *)io
++ = (btop(start
)+i
) | (1<<21) | UBAMR_MRV
;
idcaddr
->idccsr
= IDC_CRDY
| IDC_SEEK
| unit
<<8;
if ((idcaddr
->idccsr
&IDC_DRDY
) == 0)
idcaddr
->idcdar
= dar
.dar_dar
;
idcaddr
->idccsr
= IDC_SEEK
| unit
<< 8;
while ((idcaddr
->idccsr
& (IDC_CRDY
|IDC_DRDY
))
if (idcaddr
->idccsr
& IDC_ERR
) {
printf("rb%d: seek, csr=%b\n",
unit
, idcaddr
->idccsr
, IDCCSR_BITS
);
idcaddr
->idccsr
= IDC_CRDY
| IDC_WRITE
| unit
<<8;
if ((idcaddr
->idccsr
&IDC_DRDY
) == 0)
idcaddr
->idcbar
= 0; /* start addr 0 */
idcaddr
->idcbcr
= - (blk
* st
->nbps
);
idcaddr
->idcdar
= dar
.dar_dar
;
idcaddr
->idccsr
= IDC_WRITE
| unit
<< 8;
while ((idcaddr
->idccsr
& (IDC_CRDY
|IDC_DRDY
))
if (idcaddr
->idccsr
& IDC_ERR
) {
printf("rb%d: write, csr=%b\n",
unit
, idcaddr
->idccsr
, IDCCSR_BITS
);
if (unit
>= NRB
|| (ui
= idcdinfo
[unit
]) == 0 || ui
->ui_alive
== 0)
st
= &idcst
[ui
->ui_type
];
return (st
->sizes
[minor(dev
) & 07].nblocks
);