* Copyright (c) 1990 The Regents of the University of California.
* This code is derived from software contributed to Berkeley by
* @(#)fd.c 5.2 (Berkeley) %G%
/****************************************************************************/
/****************************************************************************/
#include "machine/isa/device.h"
#include "machine/isa/fdreg.h"
#define FDUNIT(s) ((s)&1)
#define FDTYPE(s) (((s)>>1)&7)
int sectrac
; /* sectors per track */
int secsize
; /* size code for sectors */
int datalen
; /* data len when secsize = 0 */
int gap
; /* gap len between sectors */
int tracks
; /* total num of tracks */
int size
; /* size of disk in sectors */
int steptrac
; /* steps per cylinder */
int trans
; /* transfer speed code */
struct fd_type fd_types
[NUMTYPES
] = {
{ 18,2,0xFF,0x1B,80,2880,1,0 }, /* 1.44 meg HD 3.5in floppy */
{ 15,2,0xFF,0x1B,80,2400,1,0 }, /* 1.2 meg HD floppy */
{ 9,2,0xFF,0x23,40,720,2,1 }, /* 360k floppy in 1.2meg drive */
{ 9,2,0xFF,0x2A,40,720,1,1 }, /* 360k floppy in DD drive */
int type
; /* Drive type (HD, DD */
int active
; /* Drive activity boolean */
int motor
; /* Motor on flag */
struct buf head
; /* Head of buf chain */
struct buf rhead
; /* Raw head of buf chain */
/* state needed for current transfer */
static fdc
; /* floppy disk controller io base register */
static int fd_track
= -1;
make sure bounce buffer for DMA is aligned since the DMA chip
doesn't roll over properly over a 64k boundary
extern struct buf
*dma_bounce
[];
/****************************************************************************/
/* autoconfiguration stuff */
/****************************************************************************/
int fdprobe(), fdattach(), fd_turnoff();
struct isa_driver fddriver
= {
/* Set transfer to 500kbps */
/****************************************************************************/
/****************************************************************************/
register struct buf
*bp
; /* IO operation to perform */
register struct buf
*dp
,*dp0
,*dp1
;
unit
= FDUNIT(minor(bp
->b_dev
));
type
= FDTYPE(minor(bp
->b_dev
));
printf("fdstrat%d, blk = %d, bcount = %d, addr = %x|",
unit
, bp
->b_blkno
, bp
->b_bcount
,bp
->b_un
.b_addr
);
if ((unit
>= NFD
) || (bp
->b_blkno
< 0)) {
printf("fdstrat: unit = %d, blkno = %d, bcount = %d\n",
unit
, bp
->b_blkno
, bp
->b_bcount
);
pg("fd:error in fdstrategy");
* Set up block calculations.
blknum
= (unsigned long) bp
->b_blkno
* DEV_BSIZE
/FDBLK
;
nblocks
= fd_types
[type
].size
;
if (blknum
+ (bp
->b_bcount
/ FDBLK
) > nblocks
) {
bp
->b_resid
= bp
->b_bcount
;
bp
->b_cylin
= blknum
/ (fd_types
[type
].sectrac
* 2);
dp
= &fd_unit
[unit
].head
;
dp
->b_step
= (fd_types
[fd_unit
[unit
].type
].steptrac
);
if ((dp0
->b_active
== 0)&&(dp1
->b_active
== 0)) {
fd_track
= -1; /* force seek on first xfer */
untimeout(fd_turnoff
,unit
);
fdstart(unit
); /* start drive if idle */
/****************************************************************************/
/* motor control stuff */
/****************************************************************************/
outb(fdc
+fdout
,unit
| (reset
? 0 : 0xC) | (m0
? 16 : 0) | (m1
? 32 : 0));
if (unit
) set_motor(0,0);
/****************************************************************************/
/****************************************************************************/
while ((i
= inb(fdc
+fdsts
) & (NE7_DIO
|NE7_RQM
)) != (NE7_DIO
|NE7_RQM
))
if (i
== NE7_RQM
) return -1;
if (fd_status
[i
] < 0) break;
printf("FD bad status :%X %X %X %X %X %X %X\n",
fd_status
[0], fd_status
[1], fd_status
[2], fd_status
[3],
fd_status
[4], fd_status
[5], fd_status
[6] );
r
= (inb(fdc
+fdsts
) & (NE7_DIO
|NE7_RQM
));
if (r
==(NE7_DIO
|NE7_RQM
)) {
dump_stat(); /* error: direction. eat up output */
/* printf("Error r = %d:",r); */
printf("New MAX = %d\n",maxcnt
);
/* see if fdc responding */
if (inb(fdc
+fdsts
)& NE7_RQM
) return 0;
/****************************************************************************/
/****************************************************************************/
int unit
= FDUNIT(minor(dev
));
int type
= FDTYPE(minor(dev
));
if (unit
>= NFD
) return(ENXIO
);
if (type
>= NUMTYPES
) return(ENXIO
);
if (check_fdc()) return(EBUSY);
/* Set proper disk type, only allow one type */
/****************************************************************************/
/****************************************************************************/
* Routines to do raw IO for a unit.
fdread(dev
, uio
) /* character read routine */
int unit
= FDUNIT(minor(dev
)) ;
if (unit
>= NFD
) return(ENXIO
);
return(physio(fdstrategy
,&fd_unit
[unit
].rhead
,dev
,B_READ
,minphys
,uio
));
fdwrite(dev
, uio
) /* character write routine */
int unit
= FDUNIT(minor(dev
)) ;
if (unit
>= NFD
) return(ENXIO
);
return(physio(fdstrategy
,&fd_unit
[unit
].rhead
,dev
,B_WRITE
,minphys
,uio
));
/****************************************************************************/
/****************************************************************************/
register struct buf
*dp
,*bp
;
if (!fd_unit
[unit
].motor
) {
timeout(fdstart
,unit
,hz
);
/* make sure drive is selected as well as on */
dp
= &fd_unit
[unit
].head
;
if (fd_unit
[unit
].reset
) fd_state
= 1;
printf("Seek %d %d\n", bp
->b_cylin
, dp
->b_step
);
if (bp
->b_cylin
!= fd_track
) {
/* Seek necessary, never quite sure where head is at! */
out_fdc(15); /* Seek function */
out_fdc(unit
); /* Drive number */
out_fdc(bp
->b_cylin
* dp
->b_step
);
dp
= &fd_unit
[fd_drive
].head
;
printf("Timeout drive status %X\n",i
);
printf("ST0 = %X, PCN = %X\n",i
,j
);
/****************************************************************************/
/****************************************************************************/
register struct buf
*dp
,*bp
;
int read
,head
,trac
,sec
,i
,s
,sectrac
,cyl
;
printf("state %d, vec %d, dr %d|",fd_state
,vec
,fd_drive
);
dp
= &fd_unit
[fd_drive
].head
;
read
= bp
->b_flags
& B_READ
;
ft
= &fd_types
[FDTYPE(bp
->b_dev
)];
case 1 : /* SEEK DONE, START DMA */
/* Make sure seek really happened*/
if (!(i
&0x20) || (cyl
!= bp
->b_cylin
*dp
->b_step
)) {
printf("Stray int ST0 = %X, PCN = %X:",i
,cyl
);
at_dma(read
,bp
->b_un
.b_addr
+fd_skip
,FDBLK
, fd_dmachan
);
blknum
= (unsigned long)bp
->b_blkno
*DEV_BSIZE
/FDBLK
sec
= blknum
% (sectrac
* 2);
if (read
) out_fdc(0xE6); /* READ */
else out_fdc(0xC5); /* WRITE */
out_fdc(head
<< 2 | fd_drive
); /* head & unit */
out_fdc(fd_track
); /* track */
out_fdc(sec
); /* sector XXX +1? */
out_fdc(ft
->secsize
); /* sector size */
out_fdc(sectrac
); /* sectors/track */
out_fdc(ft
->gap
); /* gap size */
out_fdc(ft
->datalen
); /* data length */
timeout(fd_timeout
,2,hz
);
case 2 : /* IO DONE, post-analyze */
printf("status0 err %d:",fd_status
[0]);
printf("status1 err %d:",fd_status[0]);
printf("status2 err %d:",fd_status[0]);
if (!kernel_space(bp
->b_un
.b_addr
+fd_skip
)) {
if (read
) bcopy(dma_bounce
[fd_dmachan
]->b_un
.b_addr
,
bp
->b_un
.b_addr
+fd_skip
, FDBLK
);
if (fd_skip
>= bp
->b_bcount
) {
printf("DONE %d|", bp
->b_blkno
);
dp
->b_actf
= bp
->av_forw
;
/* set up next transfer */
blknum
= (unsigned long)bp
->b_blkno
*DEV_BSIZE
/FDBLK
bp
->b_cylin
= (blknum
/ (ft
->sectrac
* 2));
if (bp
->b_cylin
!= fd_track
) {
out_fdc(15); /* Seek function */
out_fdc(fd_drive
);/* Drive number */
out_fdc(bp
->b_cylin
* dp
->b_step
);
printf("Seek %d %d\n", bp
->b_cylin
, dp
->b_step
);
out_fdc(15); /* Seek function */
out_fdc(fd_drive
);/* Drive number */
out_fdc(bp
->b_cylin
* dp
->b_step
);
out_fdc(3); /* specify command */
out_fdc(7); /* Recalibrate Function */
/* Try a reset, keep motor on */
outb(fdc
+fdctl
,ft
->trans
);
printf("Unexpected FD int->");
printf("ST0 = %X, PCN = %X\n",i
,sec
);
printf("intr status :%X %X %X %X %X %X %X ",
fd_status
[0], fd_status
[1], fd_status
[2], fd_status
[3],
fd_status
[4], fd_status
[5], fd_status
[6] );
printf("FD err %X %X %X %X %X %X %X\n",
fd_status
[0], fd_status
[1], fd_status
[2], fd_status
[3],
fd_status
[4], fd_status
[5], fd_status
[6] );
bp
->b_resid
= bp
->b_bcount
- fd_skip
;
dp
->b_actf
= bp
->av_forw
;
nextstate : After a transfer is done, continue processing
requests on the current drive queue. If empty, go to
the other drives queue. If that is empty too, timeout
to turn off the current drive in 5 seconds, and go
to state 0 (not expecting any interrupts).
dpother
= &fd_unit
[fd_drive
? 0 : 1].head
;
if (dp
->b_actf
) fdstart(fd_drive
);
else if (dpother
->b_actf
) {
untimeout(fd_turnoff
,fd_drive
);
timeout(fd_turnoff
,fd_drive
,5*hz
);
untimeout(fd_turnoff
,fd_drive
);
timeout(fd_turnoff
,fd_drive
,5*hz
);