#include <sys/param.h>
#include <lib/libsa/stand.h>
#include "boot.h"
int FDC_PORT[] = {
0x3f0,
};
#define FDC_DOR(x) (FDC_PORT[x] + 0x2)
#define FDC_STATUS(x) (FDC_PORT[x] + 0x4)
#define FDC_DATA(x) (FDC_PORT[x] + 0x5)
#define FDC_RATE(x) (FDC_PORT[x] + 0x7)
#define FDC_IRQ 6
#define FD_DMA_CHAN 2
#define RQM 0x80
#define DIO 0x40
#define NON_DMA 0x20
#define CMD_BUSY 0x10
#define ST0_IC_MASK 0xc0
#define ST1_EN 0x80
#define DOR_DMAEN 0x08
#define DOR_RESET 0x04
#define CMD_RECALIBRATE 0x07
#define CMD_SENSE_INT 0x08
#define CMD_DRV_SENSE 0x04
#define CMD_SEEK 0x0f
#define CMD_FORMAT 0x4d
#define CMD_READ 0x46
#define CMD_WRITE 0xc5
#define CMD_VERIFY 0xf6
#define CMD_READID 0x4a
#define CMD_SPECIFY 0x03
#define CMD_CONFIG 0x13
#define CMD_VERSION 0x10
#define SPECIFY1 ((0x0d<<4)|0x0f)
#define SPECIFY2 ((0x01<<1)|0)
#define STATUS_MAX 16
#define RESULT_VERSION 0x90
#define RESULT_SEEK 0x20
struct fdd_type {
int seccount;
int secsize;
int datalen;
int gap;
int gaplen;
int cylinder;
int maxseccount;
int step;
int rate;
int heads;
int f_gap;
int mselect;
char *type_name;
};
typedef struct fdd_type FDDTYPE;
#define FDTYPE_MAX 5
FDDTYPE fdd_types[FDTYPE_MAX] = {
{ 18,2,0xff,0x1b,0x54,80,2880,1,0,2,0x6c,0,"2HQ" },
{ 8,3,0xff,0x35,0x74,77,1232,1,0,2,0x54,1,"2HD" },
{ 15,2,0xff,0x1b,0x54,80,2400,1,0,2,0x54,1,"2HC" },
{ 9,2,0xff,0x23,0x50,80,1440,1,2,2,0x50,1,"2DD9" },
{ 8,2,0xff,0x3a,0x50,80,1280,1,2,2,0x50,1,"2DD8" },
};
int fdsectors[] = {128, 256, 512, 1024, 2048, 4096};
#define SECTOR_MAX 4096
#define FDBLK (fdsectors[un->un_type->secsize])
#define START_CYL 0
#define START_SECTOR 1
#define DELAY(x) delay(100000 * x)
#define INT_TIMEOUT 3000000
#define CTLR_MAX 1
#define UNIT_MAX 2
struct fd_unit {
int ctlr;
int unit;
u_int un_flags;
int stat[STATUS_MAX];
FDDTYPE *un_type;
};
typedef struct fd_unit FD_UNIT;
FD_UNIT fd_unit[CTLR_MAX][UNIT_MAX];
#define INT_ALIVE 0x00000001
#define INT_READY 0x00000002
#define INT_BUSY 0x00000004
#define TIMEOUT 10000000
#define ND_TIMEOUT 10000000
#define SUCCESS 0
#define FAIL -1
int fdinit(FD_UNIT *);
int fdopen(struct open_file *, int, int);
int fdclose(struct open_file *);
int fdioctl(struct open_file *, u_long, void *);
int fdstrategy(void *, int, daddr_t, size_t, void *, size_t *);
int fdc_out(int, int);
int fdc_in(int, u_char *);
int fdc_intr_wait(void);
int fd_check(FD_UNIT *);
void motor_on(int, int);
void motor_off(int, int);
void fdReset(int);
void fdRecalibrate(int, int);
void fdSpecify(int);
void fdDriveStatus(int, int, int, int *);
int fdSeek(int, int, int);
int fdSenseInt(int, int *);
int fdReadWrite(FD_UNIT *, int, int, int, int, u_char *);
void irq_init(void);
int irq_polling(int, int);
void dma_setup(u_char *, int, int, int);
int dma_finished(int);
int
fdinit(FD_UNIT *un)
{
int ctlr = un->ctlr;
u_char result;
#if 0
irq_init();
#endif
fdReset(ctlr);
if (fdc_out(ctlr, CMD_VERSION) != SUCCESS) {
printf ("fdc%d:fatal error: CMD_VERSION cmd fail\n", ctlr);
return (FAIL);
}
if (fdc_in(ctlr, &result) != SUCCESS) {
printf ("fdc%d:fatal error: CMD_VERSION exec fail\n", ctlr);
return (FAIL);
}
if (result != (u_char)RESULT_VERSION) {
printf ("fdc%d:fatal error: unknown version fdc\n", ctlr);
return (FAIL);
}
un->un_flags = INT_ALIVE;
return (SUCCESS);
}
int
fdopen(struct open_file *f, int ctlr, int unit)
{
FD_UNIT *un;
int *stat;
if (ctlr >= CTLR_MAX)
return (ENXIO);
if (unit >= UNIT_MAX)
return (ENXIO);
un = &fd_unit[ctlr][unit];
stat = un->stat;
if (!(un->un_flags & INT_ALIVE)) {
if (fdinit(un) != SUCCESS)
return (ENXIO);
}
motor_on(ctlr, unit);
fdRecalibrate(ctlr, unit);
fdSenseInt(ctlr, stat);
if (stat[1] != START_CYL) {
printf("fdc%d: unit:%d recalibrate failed. status:0x%x cyl:%d\n",
ctlr, unit, stat[0], stat[1]);
motor_off(ctlr, unit);
return (EIO);
}
if (fd_check(un) != SUCCESS)
return (EIO);
f->f_devdata = (void *)un;
return (SUCCESS);
}
int
fdclose(struct open_file *f)
{
FD_UNIT *un = f->f_devdata;
fdRecalibrate(un->ctlr, un->unit);
fdSenseInt(un->ctlr, un->stat);
motor_off(un->ctlr, un->unit);
un->un_flags = 0;
return (SUCCESS);
}
int
fdioctl(struct open_file *f, u_long cmd, void *arg)
{
switch (cmd) {
default:
return (EIO);
}
return (SUCCESS);
}
int
fdstrategy(void *devdata, int func, daddr_t blk, size_t size, void *buf,
size_t *rsize)
{
int sectrac, cyl, head, sec;
FD_UNIT *un = devdata;
int ctlr = un->ctlr;
int unit = un->unit;
int *stat = un->stat;
long blknum;
int fd_skip = 0;
u_char *cbuf = (u_char *)buf;
if (un->un_flags & INT_BUSY) {
return (ENXIO);
}
fdDriveStatus(ctlr, unit, 0, stat);
sectrac = un->un_type->seccount;
*rsize = 0;
while (fd_skip < size) {
blknum = (u_long)blk * DEV_BSIZE/FDBLK + fd_skip/FDBLK;
cyl = blknum / (sectrac * 2);
fdSeek(ctlr, unit, cyl);
fdSenseInt(ctlr, stat);
if (!(stat[0] & RESULT_SEEK)) {
printf("fdc%d: unit:%d seek failed."
"status:0x%x cyl:%d pcyl:%d\n",
ctlr, unit, stat[0], cyl, stat[1]);
goto bad;
}
sec = blknum % (sectrac * 2);
head = sec / sectrac;
sec = sec % sectrac + 1;
if (fdReadWrite(un, func, cyl, head, sec, cbuf) == FAIL) {
printf("fdc%d: unit%d fdReadWrite error [%s]\n",
ctlr, unit, (func==F_READ?"READ":"WRITE"));
goto bad;
}
*rsize += FDBLK;
cbuf += FDBLK;
fd_skip += FDBLK;
}
return (SUCCESS);
bad:
return (FAIL);
}
int
fd_check(FD_UNIT *un)
{
int ctlr = un->ctlr;
int unit = un->unit;
int *stat = un->stat;
int type;
static u_char sec_buff[SECTOR_MAX];
un->un_type = (FDDTYPE *)FAIL;
for (type = 0; type < FDTYPE_MAX; type++) {
un->un_type = &fdd_types[type];
outb(FDC_RATE(ctlr), un->un_type->rate);
fdSpecify(ctlr);
fdSeek(ctlr, unit, START_CYL);
fdSenseInt(ctlr, stat);
if (!(stat[0] & RESULT_SEEK) || stat[1] != START_CYL) {
printf("fdc%d: unit:%d seek failed. status:0x%x\n",
ctlr, unit, stat[0]);
goto bad;
}
if (fdReadWrite(un, F_READ,
START_CYL, 0, START_SECTOR, sec_buff) == FAIL) {
continue;
}
break;
}
if (un->un_type == (FDDTYPE *)FAIL) {
printf("fdc%d: unit:%d check disk type failed.\n",
ctlr, unit);
goto bad;
}
return (SUCCESS);
bad:
return (FAIL);
}
int
fdc_out(int ctlr, int cmd)
{
volatile int status;
int time_out;
time_out = TIMEOUT;
while (((status = inb(FDC_STATUS(ctlr))) & (RQM | DIO))
!= (RQM | 0) && time_out-- > 0);
if (time_out <= 0) {
printf("fdc_out: timeout status = 0x%x\n", status);
return (FAIL);
}
outb(FDC_DATA(ctlr), cmd);
return (SUCCESS);
}
int
fdc_in(int ctlr, u_char *data)
{
volatile int status;
int time_out;
time_out = TIMEOUT;
while ((status = inb(FDC_STATUS(ctlr)) & (RQM | DIO))
!= (RQM | DIO) && time_out-- > 0) {
if (status == RQM) {
printf("fdc_in:error:ready for output\n");
return (FAIL);
}
}
if (time_out <= 0) {
printf("fdc_in:input ready timeout\n");
return (FAIL);
}
if (data) *data = (u_char)inb(FDC_DATA(ctlr));
return (SUCCESS);
}
int
fdc_intr_wait(void)
{
return (irq_polling(FDC_IRQ, INT_TIMEOUT));
}
void
motor_on(int ctlr, int unit)
{
outb(FDC_DOR(ctlr), DOR_RESET | DOR_DMAEN | unit
| (1 << (unit + 4)));
DELAY(1);
}
void
motor_off(int ctlr, int unit)
{
outb(FDC_DOR(ctlr), DOR_RESET);
if (fdc_intr_wait() == FAIL)
printf("fdc: motor off failed.\n");
}
void
fdReset(int ctlr)
{
outb(FDC_DOR(ctlr), 0);
DELAY(3);
outb(FDC_DOR(ctlr), DOR_RESET);
DELAY(8);
}
void
fdRecalibrate(int ctlr, int unit)
{
fdc_out(ctlr, CMD_RECALIBRATE);
fdc_out(ctlr, unit);
if (fdc_intr_wait() == FAIL)
printf("fdc: recalibrate Timeout\n");
}
void
fdSpecify(int ctlr)
{
fdc_out(ctlr, CMD_SPECIFY);
fdc_out(ctlr, SPECIFY1);
fdc_out(ctlr, SPECIFY2);
}
void
fdDriveStatus(int ctlr, register int unit, register int head,
register int *stat)
{
u_char result;
fdc_out(ctlr, CMD_DRV_SENSE);
fdc_out(ctlr, (head << 2) | unit);
fdc_in(ctlr, &result);
*stat = (int)(result & 0xff);
}
int
fdSeek(int ctlr, int unit, int cyl)
{
int ret_val = 0;
fdc_out(ctlr, CMD_SEEK);
fdc_out(ctlr, unit);
fdc_out(ctlr, cyl);
if (fdc_intr_wait() == FAIL) {
printf("fdc: fdSeek Timeout\n");
ret_val = FAIL;
}
return(ret_val);
}
int
fdSenseInt(int ctlr, int *stat)
{
u_char result;
fdc_out(ctlr, CMD_SENSE_INT);
fdc_in(ctlr, &result);
*stat++ = (int)(result & 0xff);
fdc_in(ctlr, &result);
*stat++ = (int)(result & 0xff);
return (0);
}
int
fdReadWrite(FD_UNIT *un, int func, int cyl, int head, int sec, u_char *adrs)
{
int i;
int ctlr = un->ctlr;
int unit = un->unit;
int *stat = un->stat;
u_char result;
#if 0
printf("%s:", (func == F_READ ? "READ" : "WRITE"));
printf("cyl = %d", cyl);
printf("head = %d", head);
printf("sec = %d", sec);
printf("secsize = %d", un->un_type->secsize);
printf("seccount = %d", un->un_type->seccount);
printf("gap = %d", un->un_type->gap);
printf("datalen = %d\n", un->un_type->datalen);
#endif
dma_setup(adrs, FDBLK, func, FD_DMA_CHAN);
fdc_out(ctlr, (func == F_READ ? CMD_READ : CMD_WRITE));
fdc_out(ctlr, (head<<2) | unit);
fdc_out(ctlr, cyl);
fdc_out(ctlr, head);
fdc_out(ctlr, sec);
fdc_out(ctlr, un->un_type->secsize);
fdc_out(ctlr, un->un_type->seccount);
fdc_out(ctlr, un->un_type->gap);
fdc_out(ctlr, un->un_type->datalen);
if (fdc_intr_wait() == FAIL) {
printf("fdc: DMA transfer Timeout\n");
return (FAIL);
}
for (i = 0; i < 7; i++) {
fdc_in(ctlr, &result);
stat[i] = (int)(result & 0xff);
}
if (stat[0] & ST0_IC_MASK) {
if ((stat[1] & ~ST1_EN) || stat[2])
goto bad;
}
if (!dma_finished(FD_DMA_CHAN)) {
printf("DMA not finished\n");
goto bad;
}
return (SUCCESS);
bad:
printf(" func: %s\n", (func == F_READ ? "F_READ" : "F_WRITE"));
printf(" st0 = 0x%x\n", stat[0]);
printf(" st1 = 0x%x\n", stat[1]);
printf(" st2 = 0x%x\n", stat[2]);
printf(" c = 0x%x\n", stat[3]);
printf(" h = 0x%x\n", stat[4]);
printf(" r = 0x%x\n", stat[5]);
printf(" n = 0x%x\n", stat[6]);
return (FAIL);
}
#define INT_CTL0 0x20
#define INT_CTL1 0x21
#define INT2_CTL0 0xA0
#define INT2_CTL1 0xA1
#define CASCADE_IRQ 2
#define ICW1_AT 0x11
#define ICW4_AT 0x01
#define OCW3_PL 0x0e
#define OCW2_CLEAR 0x20
u_int INT_MASK;
u_int INT2_MASK;
void
irq_init(void)
{
outb(INT_CTL0, ICW1_AT);
outb(INT_CTL1, 0);
outb(INT_CTL1, (1 << CASCADE_IRQ));
outb(INT_CTL1, ICW4_AT);
outb(INT_CTL1, (INT_MASK = ~(1 << CASCADE_IRQ)));
outb(INT2_CTL0, ICW1_AT);
outb(INT2_CTL1, 8);
outb(INT2_CTL1, CASCADE_IRQ);
outb(INT2_CTL1, ICW4_AT);
outb(INT2_CTL1, (INT2_MASK = ~0));
}
int
irq_polling(int irq_no, int timeout)
{
int irc_no;
int data;
int ret;
if (irq_no > 8) irc_no = 1;
else irc_no = 0;
outb(irc_no ? INT2_CTL1 : INT_CTL1, ~(1 << (irq_no >> (irc_no * 3))));
while (--timeout > 0) {
outb(irc_no ? INT2_CTL0 : INT_CTL0, OCW3_PL);
data = inb(irc_no ? INT2_CTL0 : INT_CTL0);
if (data & 0x80) {
if ((irq_no >> (irc_no * 3)) == (data & 0x7)) {
ret = SUCCESS;
break;
}
}
}
if (!timeout) ret = FAIL;
if (irc_no) {
outb(INT2_CTL0, OCW2_CLEAR | (irq_no >> 3));
outb(INT_CTL0, OCW2_CLEAR | CASCADE_IRQ);
} else {
outb(INT_CTL0, OCW2_CLEAR | irq_no);
}
outb(INT_CTL1, INT_MASK);
outb(INT2_CTL1, INT2_MASK);
return (ret);
}
#define DMA_ADDR 0x004
#define DMA_LTOP 0x081
#define DMA_HTOP 0x481
#define DMA_COUNT 0x005
#define DMA_DEVCON 0x008
#define DMA_SR 0x008
#define DMA_RESET 0x00D
#define DMA_FLIPFLOP 0x00C
#define DMA_MODE 0x00B
#define DMA_INIT 0x00A
#define DMA_RESET_VAL 0x06
#define DMA_READ 0x46
#define DMA_WRITE 0x4A
void
dma_setup(u_char *buf, int size, int func, int chan)
{
u_long pbuf = local_to_PCI((u_long)buf);
#if 0
outb(DMA_RESET, 0);
DELAY(1);
outb(DMA_DEVCON, 0x00);
outb(DMA_INIT, DMA_RESET_VAL);
#endif
outb(DMA_MODE, func == F_READ ? DMA_READ : DMA_WRITE);
outb(DMA_FLIPFLOP, 0);
outb(DMA_ADDR, (int)pbuf >> 0);
outb(DMA_ADDR, (int)pbuf >> 8);
outb(DMA_LTOP, (int)pbuf >> 16);
outb(DMA_HTOP, (int)pbuf >> 24);
outb(DMA_COUNT, (size - 1) >> 0);
outb(DMA_COUNT, (size - 1) >> 8);
outb(DMA_INIT, chan);
}
int
dma_finished(int chan)
{
return ((inb(DMA_SR) & 0x0f) == (1 << chan));
}