]> git.neil.brown.name Git - history.git/commitdiff
ppc32: Bring back PowerMac swim3 floppy driver into working state
authorBenjamin Herrenschmidt <benh@kernel.crashing.org>
Fri, 6 Feb 2004 04:14:13 +0000 (15:14 +1100)
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>
Fri, 6 Feb 2004 04:14:13 +0000 (15:14 +1100)
Forward port Paul's latest fixes from 2.4. This driver still need
significant work to fit into the "macio" model properly, among
others.

drivers/block/swim3.c

index 4bf309826005c71d5c16ed76551fc54984a76677..e4cbabf61514d372eaca6f9c59bf1e21894bb5b6 100644 (file)
 #include <linux/delay.h>
 #include <linux/fd.h>
 #include <linux/ioctl.h>
+#include <linux/blkdev.h>
 #include <linux/devfs_fs_kernel.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
 #include <asm/io.h>
 #include <asm/dbdma.h>
 #include <asm/prom.h>
@@ -144,7 +147,7 @@ struct swim3 {
 #define RELAX          3       /* also eject in progress */
 #define READ_DATA_0    4
 #define TWOMEG_DRIVE   5
-#define SINGLE_SIDED   6
+#define SINGLE_SIDED   6       /* drive or diskette is 4MB type? */
 #define DRIVE_PRESENT  7
 #define DISK_IN                8
 #define WRITE_PROT     9
@@ -184,6 +187,7 @@ struct floppy_state {
        int     req_sector;     /* sector number ditto */
        int     scount;         /* # sectors we're transferring at present */
        int     retries;
+       int     settle_time;
        int     secpercyl;      /* disk geometry information */
        int     secpertrack;
        int     total_secs;
@@ -232,8 +236,9 @@ static void setup_transfer(struct floppy_state *fs);
 static void act(struct floppy_state *fs);
 static void scan_timeout(unsigned long data);
 static void seek_timeout(unsigned long data);
+static void settle_timeout(unsigned long data);
 static void xfer_timeout(unsigned long data);
-static void swim3_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static irqreturn_t swim3_interrupt(int irq, void *dev_id, struct pt_regs *regs);
 /*static void fd_dma_interrupt(int irq, void *dev_id, struct pt_regs *regs);*/
 static int grab_drive(struct floppy_state *fs, enum swim_state state,
                      int interruptible);
@@ -274,7 +279,6 @@ static void swim3_action(struct floppy_state *fs, int action)
        udelay(2);
        out_8(&sw->select, sw->select & ~LSTRB);
        udelay(1);
-       out_8(&sw->select, RELAX);
 }
 
 static int swim3_readbit(struct floppy_state *fs, int bit)
@@ -283,9 +287,8 @@ static int swim3_readbit(struct floppy_state *fs, int bit)
        int stat;
 
        swim3_select(fs, bit);
-       udelay(10);
+       udelay(1);
        stat = in_8(&sw->status);
-       out_8(&sw->select, RELAX);
        return (stat & DATA) == 0;
 }
 
@@ -374,13 +377,13 @@ static void set_timeout(struct floppy_state *fs, int nticks,
 static inline void scan_track(struct floppy_state *fs)
 {
        volatile struct swim3 *sw = fs->swim3;
-       int xx;
 
        swim3_select(fs, READ_DATA_0);
-       xx = sw->intr;          /* clear SEEN_SECTOR bit */
+       in_8(&sw->intr);                /* clear SEEN_SECTOR bit */
+       in_8(&sw->error);
+       out_8(&sw->intr_enable, SEEN_SECTOR);
        out_8(&sw->control_bis, DO_ACTION);
        /* enable intr when track found */
-       out_8(&sw->intr_enable, ERROR_INTR | SEEN_SECTOR);
        set_timeout(fs, HZ, scan_timeout);      /* enable timeout */
 }
 
@@ -395,12 +398,14 @@ static inline void seek_track(struct floppy_state *fs, int n)
                swim3_action(fs, SEEK_NEGATIVE);
                sw->nseek = -n;
        }
-       fs->expect_cyl = (fs->cur_cyl > 0)? fs->cur_cyl + n: -1;
+       fs->expect_cyl = (fs->cur_cyl >= 0)? fs->cur_cyl + n: -1;
        swim3_select(fs, STEP);
-       out_8(&sw->control_bis, DO_SEEK);
+       in_8(&sw->error);
        /* enable intr when seek finished */
-       out_8(&sw->intr_enable, ERROR_INTR | SEEK_DONE);
-       set_timeout(fs, HZ/2, seek_timeout);    /* enable timeout */
+       out_8(&sw->intr_enable, SEEK_DONE);
+       out_8(&sw->control_bis, DO_SEEK);
+       set_timeout(fs, 3*HZ, seek_timeout);    /* enable timeout */
+       fs->settle_time = 0;
 }
 
 static inline void init_dma(struct dbdma_cmd *cp, int cmd,
@@ -448,18 +453,21 @@ static inline void setup_transfer(struct floppy_state *fs)
        }
        ++cp;
        out_le16(&cp->command, DBDMA_STOP);
+       out_8(&sw->control_bic, DO_ACTION | WRITE_SECTORS);
+       in_8(&sw->error);
+       out_8(&sw->control_bic, DO_ACTION | WRITE_SECTORS);
+       if (rq_data_dir(fd_req) == WRITE)
+               out_8(&sw->control_bis, WRITE_SECTORS);
+       in_8(&sw->intr);
        out_le32(&dr->control, (RUN << 16) | RUN);
-       out_8(&sw->control_bis,
-             (rq_data_dir(fd_req) == WRITE? WRITE_SECTORS: 0) | DO_ACTION);
        /* enable intr when transfer complete */
-       out_8(&sw->intr_enable, ERROR_INTR | TRANSFER_DONE);
+       out_8(&sw->intr_enable, TRANSFER_DONE);
+       out_8(&sw->control_bis, DO_ACTION);
        set_timeout(fs, 2*HZ, xfer_timeout);    /* enable timeout */
 }
 
 static void act(struct floppy_state *fs)
 {
-       volatile struct swim3 *sw = fs->swim3;
-
        for (;;) {
                switch (fs->state) {
                case idle:
@@ -492,20 +500,10 @@ static void act(struct floppy_state *fs)
                        return;
 
                case settling:
-                       /* wait for SEEK_COMPLETE to become true */
-                       swim3_select(fs, SEEK_COMPLETE);
-                       udelay(10);
-                       out_8(&sw->intr_enable, ERROR_INTR | DATA_CHANGED);
-                       in_8(&sw->intr);        /* clear DATA_CHANGED */
-                       if (in_8(&sw->status) & DATA) {
-                               /* seek_complete is not yet true */
-                               set_timeout(fs, HZ/2, seek_timeout);
-                               return;
-                       }
-                       out_8(&sw->intr_enable, 0);
-                       in_8(&sw->intr);
-                       fs->state = locating;
-                       break;
+                       /* check for SEEK_COMPLETE after 30ms */
+                       fs->settle_time = (HZ + 32) / 33;
+                       set_timeout(fs, fs->settle_time, settle_timeout);
+                       return;
 
                case do_transfer:
                        if (fs->cur_cyl != fs->req_cyl) {
@@ -537,7 +535,7 @@ static void scan_timeout(unsigned long data)
        volatile struct swim3 *sw = fs->swim3;
 
        fs->timeout_pending = 0;
-       out_8(&sw->control_bic, DO_ACTION);
+       out_8(&sw->control_bic, DO_ACTION | WRITE_SECTORS);
        out_8(&sw->select, RELAX);
        out_8(&sw->intr_enable, 0);
        fs->cur_cyl = -1;
@@ -557,20 +555,34 @@ static void seek_timeout(unsigned long data)
        volatile struct swim3 *sw = fs->swim3;
 
        fs->timeout_pending = 0;
-       if (fs->state == settling) {
-               printk(KERN_ERR "swim3: MSI sel=%x ctrl=%x stat=%x intr=%x ie=%x\n",
-                      sw->select, sw->control, sw->status, sw->intr, sw->intr_enable);
-       }
        out_8(&sw->control_bic, DO_SEEK);
        out_8(&sw->select, RELAX);
        out_8(&sw->intr_enable, 0);
-       if (fs->state == settling && swim3_readbit(fs, SEEK_COMPLETE)) {
-               /* printk(KERN_DEBUG "swim3: missed settling interrupt\n"); */
+       printk(KERN_ERR "swim3: seek timeout\n");
+       end_request(fd_req, 0);
+       fs->state = idle;
+       start_request(fs);
+}
+
+static void settle_timeout(unsigned long data)
+{
+       struct floppy_state *fs = (struct floppy_state *) data;
+       volatile struct swim3 *sw = fs->swim3;
+
+       fs->timeout_pending = 0;
+       if (swim3_readbit(fs, SEEK_COMPLETE)) {
+               out_8(&sw->select, RELAX);
                fs->state = locating;
                act(fs);
                return;
        }
-       printk(KERN_ERR "swim3: seek timeout\n");
+       out_8(&sw->select, RELAX);
+       if (fs->settle_time < 2*HZ) {
+               ++fs->settle_time;
+               set_timeout(fs, 1, settle_timeout);
+               return;
+       }
+       printk(KERN_ERR "swim3: seek settle timeout\n");
        end_request(fd_req, 0);
        fs->state = idle;
        start_request(fs);
@@ -583,9 +595,13 @@ static void xfer_timeout(unsigned long data)
        struct dbdma_regs *dr = fs->dma;
        struct dbdma_cmd *cp = fs->dma_cmd;
        unsigned long s;
+       int n;
 
        fs->timeout_pending = 0;
        st_le32(&dr->control, RUN << 16);
+       /* We must wait a bit for dbdma to stop */
+       for (n = 0; (in_le32(&dr->status) & ACTIVE) && n < 1000; n++)
+               udelay(1);
        out_8(&sw->intr_enable, 0);
        out_8(&sw->control_bic, WRITE_SECTORS | DO_ACTION);
        out_8(&sw->select, RELAX);
@@ -604,7 +620,7 @@ static void xfer_timeout(unsigned long data)
        start_request(fs);
 }
 
-static void swim3_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+static irqreturn_t swim3_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
        struct floppy_state *fs = (struct floppy_state *) dev_id;
        volatile struct swim3 *sw = fs->swim3;
@@ -613,18 +629,15 @@ static void swim3_interrupt(int irq, void *dev_id, struct pt_regs *regs)
        struct dbdma_regs *dr;
        struct dbdma_cmd *cp;
 
-       err = in_8(&sw->error);
        intr = in_8(&sw->intr);
-#if 0
-       printk("swim3 intr state=%d intr=%x err=%x\n", fs->state, intr, err);
-#endif
+       err = (intr & ERROR_INTR)? in_8(&sw->error): 0;
        if ((intr & ERROR_INTR) && fs->state != do_transfer)
                printk(KERN_ERR "swim3_interrupt, state=%d, dir=%lx, intr=%x, err=%x\n",
                       fs->state, rq_data_dir(fd_req), intr, err);
        switch (fs->state) {
        case locating:
                if (intr & SEEN_SECTOR) {
-                       out_8(&sw->control_bic, DO_ACTION);
+                       out_8(&sw->control_bic, DO_ACTION | WRITE_SECTORS);
                        out_8(&sw->select, RELAX);
                        out_8(&sw->intr_enable, 0);
                        del_timer(&fs->timeout);
@@ -674,19 +687,33 @@ static void swim3_interrupt(int irq, void *dev_id, struct pt_regs *regs)
        case do_transfer:
                if ((intr & (ERROR_INTR | TRANSFER_DONE)) == 0)
                        break;
-               dr = fs->dma;
-               cp = fs->dma_cmd;
-               /* We must wait a bit for dbdma to complete */
-               for (n=0; (in_le32(&dr->status) & ACTIVE) && n < 1000; n++)
-                       udelay(10);
-               DBDMA_DO_STOP(dr);
                out_8(&sw->intr_enable, 0);
                out_8(&sw->control_bic, WRITE_SECTORS | DO_ACTION);
                out_8(&sw->select, RELAX);
                del_timer(&fs->timeout);
                fs->timeout_pending = 0;
+               dr = fs->dma;
+               cp = fs->dma_cmd;
                if (rq_data_dir(fd_req) == WRITE)
                        ++cp;
+               /*
+                * Check that the main data transfer has finished.
+                * On writing, the swim3 sometimes doesn't use
+                * up all the bytes of the postamble, so we can still
+                * see DMA active here.  That doesn't matter as long
+                * as all the sector data has been transferred.
+                */
+               if ((intr & ERROR_INTR) == 0 && cp->xfer_status == 0) {
+                       /* wait a little while for DMA to complete */
+                       for (n = 0; n < 100; ++n) {
+                               if (cp->xfer_status != 0)
+                                       break;
+                               udelay(1);
+                               barrier();
+                       }
+               }
+               /* turn off DMA */
+               out_le32(&dr->control, (RUN | PAUSE) << 16);
                stat = ld_le16(&cp->xfer_status);
                resid = ld_le16(&cp->res_count);
                if (intr & ERROR_INTR) {
@@ -742,6 +769,7 @@ static void swim3_interrupt(int irq, void *dev_id, struct pt_regs *regs)
        default:
                printk(KERN_ERR "swim3: don't know what to do in state %d\n", fs->state);
        }
+       return IRQ_HANDLED;
 }
 
 /*
@@ -793,16 +821,19 @@ static int fd_eject(struct floppy_state *fs)
        if (err)
                return err;
        swim3_action(fs, EJECT);
-       for (n = 2*HZ; n > 0; --n) {
-               if (swim3_readbit(fs, RELAX))
-                       break;
+       for (n = 20; n > 0; --n) {
                if (signal_pending(current)) {
                        err = -EINTR;
                        break;
                }
+               swim3_select(fs, RELAX);
                current->state = TASK_INTERRUPTIBLE;
                schedule_timeout(1);
+               if (swim3_readbit(fs, DISK_IN) == 0)
+                       break;
        }
+       swim3_select(fs, RELAX);
+       udelay(150);
        fs->ejected = 1;
        release_drive(fs);
        return err;
@@ -847,29 +878,31 @@ static int floppy_open(struct inode *inode, struct file *filp)
        if (fs->ref_count == 0) {
                if (fs->media_bay && check_media_bay(fs->media_bay, MB_FD))
                        return -ENXIO;
-               out_8(&sw->mode, 0x95);
-               out_8(&sw->control_bic, 0xff);
                out_8(&sw->setup, S_IBM_DRIVE | S_FCLK_DIV2);
+               out_8(&sw->control_bic, 0xff);
+               out_8(&sw->mode, 0x95);
                udelay(10);
                out_8(&sw->intr_enable, 0);
                out_8(&sw->control_bis, DRIVE_ENABLE | INTR_ENABLE);
                swim3_action(fs, MOTOR_ON);
                fs->write_prot = -1;
                fs->cur_cyl = -1;
-               for (n = HZ; n > 0; --n) {
-                       if (swim3_readbit(fs, SEEK_COMPLETE))
+               for (n = 0; n < 2 * HZ; ++n) {
+                       if (n >= HZ/30 && swim3_readbit(fs, SEEK_COMPLETE))
                                break;
                        if (signal_pending(current)) {
                                err = -EINTR;
                                break;
                        }
+                       swim3_select(fs, RELAX);
                        current->state = TASK_INTERRUPTIBLE;
                        schedule_timeout(1);
                }
                if (err == 0 && (swim3_readbit(fs, SEEK_COMPLETE) == 0
                                 || swim3_readbit(fs, DISK_IN) == 0))
                        err = -ENXIO;
-               swim3_action(fs, 9);
+               swim3_action(fs, SETMFM);
+               swim3_select(fs, RELAX);
 
        } else if (fs->ref_count == -1 || filp->f_flags & O_EXCL)
                return -EBUSY;
@@ -892,6 +925,7 @@ static int floppy_open(struct inode *inode, struct file *filp)
                if (fs->ref_count == 0) {
                        swim3_action(fs, MOTOR_OFF);
                        out_8(&sw->control_bic, DRIVE_ENABLE | INTR_ENABLE);
+                       swim3_select(fs, RELAX);
                }
                return err;
        }
@@ -911,6 +945,7 @@ static int floppy_release(struct inode *inode, struct file *filp)
        if (fs->ref_count > 0 && --fs->ref_count == 0) {
                swim3_action(fs, MOTOR_OFF);
                out_8(&sw->control_bic, 0xff);
+               swim3_select(fs, RELAX);
        }
        return 0;
 }
@@ -933,15 +968,17 @@ static int floppy_revalidate(struct gendisk *disk)
        sw = fs->swim3;
        grab_drive(fs, revalidating, 0);
        out_8(&sw->intr_enable, 0);
-       out_8(&sw->control_bis, DRIVE_ENABLE | INTR_ENABLE);
-       swim3_action(fs, MOTOR_ON);
+       out_8(&sw->control_bis, DRIVE_ENABLE);
+       swim3_action(fs, MOTOR_ON);     /* necessary? */
        fs->write_prot = -1;
        fs->cur_cyl = -1;
+       mdelay(1);
        for (n = HZ; n > 0; --n) {
                if (swim3_readbit(fs, SEEK_COMPLETE))
                        break;
                if (signal_pending(current))
                        break;
+               swim3_select(fs, RELAX);
                current->state = TASK_INTERRUPTIBLE;
                schedule_timeout(1);
        }
@@ -951,17 +988,14 @@ static int floppy_revalidate(struct gendisk *disk)
                swim3_action(fs, MOTOR_OFF);
        else {
                fs->ejected = 0;
-               swim3_action(fs, 9);
+               swim3_action(fs, SETMFM);
        }
+       swim3_select(fs, RELAX);
 
        release_drive(fs);
        return ret;
 }
 
-static void floppy_off(unsigned int nr)
-{
-}
-
 static struct block_device_operations floppy_fops = {
        .open           = floppy_open,
        .release        = floppy_release,
@@ -1104,3 +1138,5 @@ static int swim3_add_device(struct device_node *swim)
        
        return 0;
 }
+
+module_init(swim3_init)