summaryrefslogtreecommitdiffstats
path: root/drivers/block
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1997-09-12 01:29:55 +0000
committerRalf Baechle <ralf@linux-mips.org>1997-09-12 01:29:55 +0000
commit545f435ebcfd94a1e7c20b46efe81b4d6ac4e698 (patch)
treee9ce4bc598d06374bda906f18365984bf22a526a /drivers/block
parent4291a610eef89d0d5c69d9a10ee6560e1aa36c74 (diff)
Merge with Linux 2.1.55. More bugfixes and goodies from my private
CVS archive.
Diffstat (limited to 'drivers/block')
-rw-r--r--drivers/block/Makefile4
-rw-r--r--drivers/block/floppy.c9
-rw-r--r--drivers/block/genhd.c130
-rw-r--r--drivers/block/hd.c43
-rw-r--r--drivers/block/ide.c4
-rw-r--r--drivers/block/ll_rw_blk.c6
-rw-r--r--drivers/block/raid0.c32
-rw-r--r--drivers/block/rd.c2
-rw-r--r--drivers/block/swim3.c1011
9 files changed, 1197 insertions, 44 deletions
diff --git a/drivers/block/Makefile b/drivers/block/Makefile
index f8ec1abd9..de13e072a 100644
--- a/drivers/block/Makefile
+++ b/drivers/block/Makefile
@@ -22,6 +22,10 @@ MOD_LIST_NAME := BLOCK_MODULES
LX_OBJS :=
MX_OBJS :=
+ifeq ($(CONFIG_MAC_FLOPPY),y)
+L_OBJS += swim3.o
+endif
+
ifeq ($(CONFIG_BLK_DEV_FD),y)
L_OBJS += floppy.o
else
diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c
index 2799eb04c..04ea60ff8 100644
--- a/drivers/block/floppy.c
+++ b/drivers/block/floppy.c
@@ -161,11 +161,6 @@ static inline int __get_order(unsigned long size);
#include <linux/blk.h>
#include <linux/cdrom.h> /* for the compatibility eject ioctl */
-
-#ifndef FLOPPY_MOTOR_MASK
-#define FLOPPY_MOTOR_MASK 0xf0
-#endif
-
#ifndef fd_get_dma_residue
#define fd_get_dma_residue() get_dma_residue(FLOPPY_DMA)
#endif
@@ -3968,6 +3963,10 @@ __initfunc(int floppy_init(void))
}
fdc_state[0].address = FDC1;
+ if (fdc_state[0].address == -1) {
+ unregister_blkdev(MAJOR_NR,"fd");
+ return -ENODEV;
+ }
#if N_FDC > 1
fdc_state[1].address = FDC2;
#endif
diff --git a/drivers/block/genhd.c b/drivers/block/genhd.c
index 1ee709737..c6b0a02b6 100644
--- a/drivers/block/genhd.c
+++ b/drivers/block/genhd.c
@@ -740,6 +740,132 @@ rdb_done:
}
#endif /* CONFIG_AMIGA_PARTITION */
+#ifdef CONFIG_MAC_PARTITION
+#include <linux/ctype.h>
+
+/*
+ * Code to understand MacOS partition tables.
+ */
+
+#define MAC_PARTITION_MAGIC 0x504d
+
+/* type field value for A/UX or other Unix partitions */
+#define APPLE_AUX_TYPE "Apple_UNIX_SVR2"
+
+struct mac_partition {
+ __u16 signature; /* expected to be MAC_PARTITION_MAGIC */
+ __u16 res1;
+ __u32 map_count; /* # blocks in partition map */
+ __u32 start_block; /* absolute starting block # of partition */
+ __u32 block_count; /* number of blocks in partition */
+ char name[32]; /* partition name */
+ char type[32]; /* string type description */
+ __u32 data_start; /* rel block # of first data block */
+ __u32 data_count; /* number of data blocks */
+ __u32 status; /* partition status bits */
+ __u32 boot_start;
+ __u32 boot_size;
+ __u32 boot_load;
+ __u32 boot_load2;
+ __u32 boot_entry;
+ __u32 boot_entry2;
+ __u32 boot_cksum;
+ char processor[16]; /* identifies ISA of boot */
+ /* there is more stuff after this that we don't need */
+};
+
+#define MAC_STATUS_BOOTABLE 8 /* partition is bootable */
+
+#define MAC_DRIVER_MAGIC 0x4552
+
+/* Driver descriptor structure, in block 0 */
+struct mac_driver_desc {
+ __u16 signature; /* expected to be MAC_DRIVER_MAGIC */
+ __u16 block_size;
+ __u32 block_count;
+ /* ... more stuff */
+};
+
+static int mac_partition(struct gendisk *hd, kdev_t dev, unsigned long fsec)
+{
+ struct buffer_head *bh;
+ int blk, blocks_in_map;
+ int dev_bsize, dev_pos, pos;
+ unsigned secsize;
+ int first_bootable = 1;
+ struct mac_partition *part;
+ struct mac_driver_desc *md;
+
+ dev_bsize = get_ptable_blocksize(dev);
+ dev_pos = 0;
+ /* Get 0th block and look at the first partition map entry. */
+ if ((bh = bread(dev, 0, dev_bsize)) == 0) {
+ printk("%s: error reading partition table\n",
+ kdevname(dev));
+ return -1;
+ }
+ md = (struct mac_driver_desc *) bh->b_data;
+ if (be16_to_cpu(md->signature) != MAC_DRIVER_MAGIC) {
+ brelse(bh);
+ return 0;
+ }
+ secsize = be16_to_cpu(md->block_size);
+ if (secsize >= dev_bsize) {
+ brelse(bh);
+ dev_pos = secsize;
+ if ((bh = bread(dev, secsize/dev_bsize, dev_bsize)) == 0) {
+ printk("%s: error reading partition table\n",
+ kdevname(dev));
+ return -1;
+ }
+ }
+ part = (struct mac_partition *) (bh->b_data + secsize - dev_pos);
+ if (be16_to_cpu(part->signature) != MAC_PARTITION_MAGIC) {
+ brelse(bh);
+ return 0; /* not a MacOS disk */
+ }
+ blocks_in_map = be32_to_cpu(part->map_count);
+ for (blk = 1; blk <= blocks_in_map; ++blk) {
+ pos = blk * secsize;
+ if (pos >= dev_pos + dev_bsize) {
+ brelse(bh);
+ dev_pos = pos;
+ if ((bh = bread(dev, pos/dev_bsize, dev_bsize)) == 0) {
+ printk("%s: error reading partition table\n",
+ kdevname(dev));
+ return -1;
+ }
+ }
+ part = (struct mac_partition *) (bh->b_data + pos - dev_pos);
+ if (be16_to_cpu(part->signature) != MAC_PARTITION_MAGIC)
+ break;
+ blocks_in_map = be32_to_cpu(part->map_count);
+ add_partition(hd, current_minor,
+ fsec + be32_to_cpu(part->start_block) * (secsize/512),
+ be32_to_cpu(part->block_count) * (secsize/512));
+
+#ifdef CONFIG_PMAC
+ /*
+ * If this is the first bootable partition, tell the
+ * setup code, in case it wants to make this the root.
+ */
+ if (first_bootable
+ && (be32_to_cpu(part->status) & MAC_STATUS_BOOTABLE)
+ && strcasecmp(part->processor, "powerpc") == 0) {
+ note_bootable_part(dev, blk);
+ first_bootable = 0;
+ }
+#endif /* CONFIG_PMAC */
+
+ ++current_minor;
+ }
+ brelse(bh);
+ printk("\n");
+ return 1;
+}
+
+#endif /* CONFIG_MAC_PARTITION */
+
#ifdef CONFIG_ATARI_PARTITION
#include <asm/atari_rootsec.h>
@@ -916,6 +1042,10 @@ static void check_partition(struct gendisk *hd, kdev_t dev)
if(atari_partition(hd, dev, first_sector))
return;
#endif
+#ifdef CONFIG_MAC_PARTITION
+ if (mac_partition(hd, dev, first_sector))
+ return;
+#endif
#ifdef CONFIG_SGI_PARTITION
if(sgi_partition(hd, dev, first_sector))
return;
diff --git a/drivers/block/hd.c b/drivers/block/hd.c
index 62e2f8da3..558a1b2d2 100644
--- a/drivers/block/hd.c
+++ b/drivers/block/hd.c
@@ -584,7 +584,7 @@ static int hd_ioctl(struct inode * inode, struct file * file,
unsigned int cmd, unsigned long arg)
{
struct hd_geometry *loc = (struct hd_geometry *) arg;
- int dev, err;
+ int dev;
if ((!inode) || !(inode->i_rdev))
return -EINVAL;
@@ -593,19 +593,15 @@ static int hd_ioctl(struct inode * inode, struct file * file,
return -EINVAL;
switch (cmd) {
case HDIO_GETGEO:
+ {
+ struct hd_geometry g;
if (!loc) return -EINVAL;
- err = verify_area(VERIFY_WRITE, loc, sizeof(*loc));
- if (err)
- return err;
- put_user(hd_info[dev].head,
- (char *) &loc->heads);
- put_user(hd_info[dev].sect,
- (char *) &loc->sectors);
- put_user(hd_info[dev].cyl,
- (short *) &loc->cylinders);
- put_user(hd[MINOR(inode->i_rdev)].start_sect,
- (long *) &loc->start);
- return 0;
+ g.heads = hd_info[dev].head;
+ g.sectors = hd_info[dev].sect;
+ g.cylinders = hd_info[dev].cyl;
+ g.start = hd[MINOR(inode->i_rdev)].start_sect;
+ return copy_to_user(loc, &g, sizeof g) ? -EFAULT : 0;
+ }
case BLKRASET:
if(!suser()) return -EACCES;
if(arg > 0xff) return -EINVAL;
@@ -613,18 +609,12 @@ static int hd_ioctl(struct inode * inode, struct file * file,
return 0;
case BLKRAGET:
if (!arg) return -EINVAL;
- err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long));
- if (err)
- return err;
- put_user(read_ahead[MAJOR(inode->i_rdev)],(long *) arg);
- return 0;
+ return put_user(read_ahead[MAJOR(inode->i_rdev)],
+ (long *) arg);
case BLKGETSIZE: /* Return device size */
if (!arg) return -EINVAL;
- err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long));
- if (err)
- return err;
- put_user(hd[MINOR(inode->i_rdev)].nr_sects, (long *) arg);
- return 0;
+ return put_user(hd[MINOR(inode->i_rdev)].nr_sects,
+ (long *) arg);
case BLKFLSBUF:
if(!suser()) return -EACCES;
fsync_dev(inode->i_rdev);
@@ -817,7 +807,7 @@ __initfunc(int hd_init(void))
/*
* This routine is called to flush all partitions and partition tables
- * for a changed scsi disk, and then re-read the new partition table.
+ * for a changed disk, and then re-read the new partition table.
* If we are revalidating a disk because of a media change, then we
* enter with usage == 0. If we are using an ioctl, we automatically have
* usage == 1 (we need an open channel to use an ioctl :-), so this
@@ -850,8 +840,11 @@ static int revalidate_hddisk(kdev_t dev, int maxusage)
for (i=max_p - 1; i >=0 ; i--) {
int minor = start + i;
kdev_t devi = MKDEV(MAJOR_NR, minor);
+ struct super_block *sb = get_super(devi);
+
sync_dev(devi);
- invalidate_inodes(devi);
+ if (sb)
+ invalidate_inodes(sb);
invalidate_buffers(devi);
gdev->part[minor].start_sect = 0;
gdev->part[minor].nr_sects = 0;
diff --git a/drivers/block/ide.c b/drivers/block/ide.c
index 61b792f74..1a5fdc5a8 100644
--- a/drivers/block/ide.c
+++ b/drivers/block/ide.c
@@ -1611,8 +1611,10 @@ int ide_revalidate_disk(kdev_t i_rdev)
for (p = 0; p < (1<<PARTN_BITS); ++p) {
if (drive->part[p].nr_sects > 0) {
kdev_t devp = MKDEV(major, minor+p);
+ struct super_block * sb = get_super(devp);
fsync_dev (devp);
- invalidate_inodes (devp);
+ if (sb)
+ invalidate_inodes(sb);
invalidate_buffers (devp);
}
drive->part[p].start_sect = 0;
diff --git a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c
index 6e1d27eea..759785e85 100644
--- a/drivers/block/ll_rw_blk.c
+++ b/drivers/block/ll_rw_blk.c
@@ -681,10 +681,14 @@ __initfunc(int blk_dev_init(void))
#ifdef CONFIG_BLK_DEV_EZ
ez_init();
#endif
+#ifdef CONFIG_MAC_FLOPPY
+ swim3_init();
+#endif
#ifdef CONFIG_BLK_DEV_FD
floppy_init();
#else
-#if !defined(CONFIG_SGI) && !defined (__mc68000__)
+#if !defined(CONFIG_SGI) && !defined (__mc68000__) \
+ && !defined(CONFIG_PMAC) && !defined(__sparc__)
outb_p(0xc, 0x3f2);
#endif
#endif
diff --git a/drivers/block/raid0.c b/drivers/block/raid0.c
index dddf5dd8d..6d269745b 100644
--- a/drivers/block/raid0.c
+++ b/drivers/block/raid0.c
@@ -20,13 +20,13 @@
#include <linux/module.h>
#include <linux/md.h>
#include <linux/raid0.h>
-#include <linux/malloc.h>
+#include <linux/vmalloc.h>
#define MAJOR_NR MD_MAJOR
#define MD_DRIVER
#define MD_PERSONALITY
-static void create_strip_zones (int minor, struct md_dev *mddev)
+static int create_strip_zones (int minor, struct md_dev *mddev)
{
int i, j, c=0;
int current_offset=0;
@@ -50,8 +50,8 @@ static void create_strip_zones (int minor, struct md_dev *mddev)
c=0;
}
- data->strip_zone=kmalloc (sizeof(struct strip_zone)*data->nr_strip_zones,
- GFP_KERNEL);
+ if ((data->strip_zone=vmalloc(sizeof(struct strip_zone)*data->nr_strip_zones)) == NULL)
+ return 1;
data->smallest=NULL;
@@ -81,6 +81,7 @@ static void create_strip_zones (int minor, struct md_dev *mddev)
data->strip_zone[i-1].size) : 0;
current_offset=smallest_by_zone->size;
}
+ return 0;
}
static int raid0_run (int minor, struct md_dev *mddev)
@@ -90,17 +91,26 @@ static int raid0_run (int minor, struct md_dev *mddev)
MOD_INC_USE_COUNT;
- mddev->private=kmalloc (sizeof (struct raid0_data), GFP_KERNEL);
+ if ((mddev->private=vmalloc (sizeof (struct raid0_data))) == NULL) return 1;
data=(struct raid0_data *) mddev->private;
- create_strip_zones (minor, mddev);
+ if (create_strip_zones (minor, mddev))
+ {
+ vfree(data);
+ return 1;
+ }
nb_zone=data->nr_zones=
md_size[minor]/data->smallest->size +
(md_size[minor]%data->smallest->size ? 1 : 0);
-
- data->hash_table=kmalloc (sizeof (struct raid0_hash)*nb_zone, GFP_KERNEL);
+ printk ("raid0 : Allocating %d bytes for hash.\n",sizeof(struct raid0_hash)*nb_zone);
+ if ((data->hash_table=vmalloc (sizeof (struct raid0_hash)*nb_zone)) == NULL)
+ {
+ vfree(data->strip_zone);
+ vfree(data);
+ return 1;
+ }
size=data->strip_zone[cur].size;
i=0;
@@ -142,9 +152,9 @@ static int raid0_stop (int minor, struct md_dev *mddev)
{
struct raid0_data *data=(struct raid0_data *) mddev->private;
- kfree (data->hash_table);
- kfree (data->strip_zone);
- kfree (data);
+ vfree (data->hash_table);
+ vfree (data->strip_zone);
+ vfree (data);
MOD_DEC_USE_COUNT;
return 0;
diff --git a/drivers/block/rd.c b/drivers/block/rd.c
index 7d7a3931c..b8239df9b 100644
--- a/drivers/block/rd.c
+++ b/drivers/block/rd.c
@@ -447,7 +447,7 @@ __initfunc(static void rd_load_image(kdev_t device,int offset))
struct inode inode, out_inode;
struct file infile, outfile;
struct dentry in_dentry, out_dentry;
- unsigned short fs;
+ unsigned long fs;
kdev_t ram_device;
int nblocks, i;
char *buf;
diff --git a/drivers/block/swim3.c b/drivers/block/swim3.c
new file mode 100644
index 000000000..4dc442673
--- /dev/null
+++ b/drivers/block/swim3.c
@@ -0,0 +1,1011 @@
+/*
+ * Driver for the SWIM3 (Super Woz Integrated Machine 3)
+ * floppy controller found on Power Macintoshes.
+ *
+ * Copyright (C) 1996 Paul Mackerras.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/stddef.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/fd.h>
+#include <linux/ioctl.h>
+#include <asm/io.h>
+#include <asm/dbdma.h>
+#include <asm/prom.h>
+#include <asm/uaccess.h>
+
+#define MAJOR_NR FLOPPY_MAJOR
+#include <linux/blk.h>
+
+static int floppy_blocksizes[2] = {512};
+static int floppy_sizes[2] = {2880};
+
+enum swim_state {
+ idle,
+ locating,
+ seeking,
+ settling,
+ do_transfer,
+ jogging,
+ available,
+ revalidating,
+ ejecting
+};
+
+#define REG(x) unsigned char x; char x ## _pad[15];
+
+/*
+ * The names for these registers mostly represent speculation on my part.
+ * It will be interesting to see how close they are to the names Apple uses.
+ */
+struct swim3 {
+ REG(data);
+ REG(usecs); /* counts down at 1MHz */
+ REG(error);
+ REG(mode);
+ REG(select); /* controls CA0, CA1, CA2 and LSTRB signals */
+ REG(reg5);
+ REG(control); /* writing bits clears them */
+ REG(status); /* writing bits sets them in control */
+ REG(intr);
+ REG(nseek); /* # tracks to seek */
+ REG(ctrack); /* current track number */
+ REG(csect); /* current sector number */
+ REG(ssize); /* sector size code?? */
+ REG(sector); /* sector # to read or write */
+ REG(nsect); /* # sectors to read or write */
+ REG(intr_enable);
+};
+
+#define control_bic control
+#define control_bis status
+
+/* Bits in select register */
+#define CA_MASK 7
+#define LSTRB 8
+
+/* Bits in control register */
+#define DO_SEEK 0x80
+#define SELECT 0x20
+#define WRITE_SECTORS 0x10
+#define SCAN_TRACK 0x08
+#define DRIVE_ENABLE 0x02
+#define INTR_ENABLE 0x01
+
+/* Bits in status register */
+#define DATA 0x08
+
+/* Bits in intr and intr_enable registers */
+#define ERROR 0x20
+#define DATA_CHANGED 0x10
+#define TRANSFER_DONE 0x08
+#define SEEN_SECTOR 0x04
+#define SEEK_DONE 0x02
+
+/* Select values for swim3_action */
+#define SEEK_POSITIVE 0
+#define SEEK_NEGATIVE 4
+#define STEP 1
+#define MOTOR_ON 2
+#define MOTOR_OFF 6
+#define EJECT 7
+
+/* Select values for swim3_select and swim3_readbit */
+#define STEP_DIR 0
+#define STEPPING 1
+#define MOTOR_ON 2
+#define RELAX 3
+#define READ_DATA_0 4
+#define SINGLE_SIDED 6
+#define DRIVE_PRESENT 7
+#define DISK_IN 8
+#define WRITE_PROT 9
+#define TRACK_ZERO 10
+#define TACHO 11
+#define READ_DATA_1 12
+#define SEEK_COMPLETE 14
+
+struct floppy_state {
+ enum swim_state state;
+ volatile struct swim3 *swim3; /* hardware registers */
+ struct dbdma_regs *dma; /* DMA controller registers */
+ int swim3_intr; /* interrupt number for SWIM3 */
+ int dma_intr; /* interrupt number for DMA channel */
+ int cur_cyl; /* cylinder head is on, or -1 */
+ int cur_sector; /* last sector we saw go past */
+ int req_cyl; /* the cylinder for the current r/w request */
+ int head; /* head number ditto */
+ int req_sector; /* sector number ditto */
+ int scount; /* # sectors we're transferring at present */
+ int retries;
+ int secpercyl; /* disk geometry information */
+ int secpertrack;
+ int total_secs;
+ int write_prot; /* 1 if write-protected, 0 if not, -1 dunno */
+ struct dbdma_cmd *dma_cmd;
+ int ref_count;
+ int expect_cyl;
+ struct timer_list timeout;
+ int ejected;
+ struct wait_queue *wait;
+ int wanted;
+ char dbdma_cmd_space[5 * sizeof(struct dbdma_cmd)];
+};
+
+static struct floppy_state floppy_states[1];
+
+static unsigned short write_preamble[] = {
+ 0x4e4e, 0x4e4e, 0x4e4e, 0x4e4e, 0x4e4e, /* gap field */
+ 0, 0, 0, 0, 0, 0, /* sync field */
+ 0x99a1, 0x99a1, 0x99a1, 0x99fb, /* data address mark */
+ 0x990f /* init CRC generator */
+};
+
+static unsigned short write_postamble[] = {
+ 0x9904, /* insert CRC */
+ 0x4e4e, 0x4e4e,
+ 0x9908, /* stop writing */
+ 0, 0, 0, 0, 0, 0
+};
+
+static void swim3_select(struct floppy_state *fs, int sel);
+static void swim3_action(struct floppy_state *fs, int action);
+static int swim3_readbit(struct floppy_state *fs, int bit);
+static void do_fd_request(void);
+static void start_request(struct floppy_state *fs);
+static void scan_track(struct floppy_state *fs);
+static void seek_track(struct floppy_state *fs, int n);
+static void init_dma(struct dbdma_cmd *cp, int cmd, void *buf, int count);
+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 xfer_timeout(unsigned long data);
+static void 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);
+static void release_drive(struct floppy_state *fs);
+static int fd_eject(struct floppy_state *fs);
+static int floppy_ioctl(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long param);
+static int floppy_open(struct inode *inode, struct file *filp);
+static int floppy_release(struct inode *inode, struct file *filp);
+static long floppy_read(struct inode *inode, struct file *filp,
+ char *buf, unsigned long count);
+static long floppy_write(struct inode *inode, struct file *filp,
+ const char *buf, unsigned long count);
+static int floppy_check_change(kdev_t dev);
+static int floppy_revalidate(kdev_t dev);
+int swim3_init(void);
+
+#define IOCTL_MODE_BIT 8
+#define OPEN_WRITE_BIT 16
+
+static void swim3_select(struct floppy_state *fs, int sel)
+{
+ volatile struct swim3 *sw = fs->swim3;
+
+ out_8(&sw->select, RELAX);
+ if (sel & 8)
+ out_8(&sw->control_bis, SELECT);
+ else
+ out_8(&sw->control_bic, SELECT);
+ out_8(&sw->select, sel & CA_MASK);
+}
+
+static void swim3_action(struct floppy_state *fs, int action)
+{
+ volatile struct swim3 *sw = fs->swim3;
+
+ swim3_select(fs, action);
+ udelay(1);
+ sw->select |= LSTRB; eieio();
+ udelay(2);
+ sw->select &= ~LSTRB; eieio();
+ udelay(1);
+ out_8(&sw->select, RELAX);
+}
+
+static int swim3_readbit(struct floppy_state *fs, int bit)
+{
+ volatile struct swim3 *sw = fs->swim3;
+ int stat;
+
+ swim3_select(fs, bit);
+ udelay(1);
+ stat = in_8(&sw->status);
+ out_8(&sw->select, RELAX);
+ return (stat & DATA) == 0;
+}
+
+static void do_fd_request(void)
+{
+ start_request(&floppy_states[0]);
+ sti();
+}
+
+static void start_request(struct floppy_state *fs)
+{
+ int drive;
+ unsigned long x;
+
+ if (fs->state == idle && fs->wanted) {
+ fs->state = available;
+ wake_up(&fs->wait);
+ return;
+ }
+ while (CURRENT && fs->state == idle) {
+ if (MAJOR(CURRENT->rq_dev) != MAJOR_NR)
+ panic(DEVICE_NAME ": request list destroyed");
+ if (CURRENT->bh && !buffer_locked(CURRENT->bh))
+ panic(DEVICE_NAME ": block not locked");
+#if 0
+ printk("do_fd_req: dev=%x cmd=%d sec=%ld nr_sec=%ld buf=%p\n",
+ kdev_t_to_nr(CURRENT->rq_dev), CURRENT->cmd,
+ CURRENT->sector, CURRENT->nr_sectors, CURRENT->buffer);
+ printk(" rq_status=%d errors=%d current_nr_sectors=%ld\n",
+ CURRENT->rq_status, CURRENT->errors, CURRENT->current_nr_sectors);
+#endif
+
+ drive = MINOR(CURRENT->rq_dev);
+ if (drive != 0) {
+ end_request(0);
+ continue;
+ }
+ if (CURRENT->sector < 0 || CURRENT->sector >= fs->total_secs) {
+ end_request(0);
+ continue;
+ }
+ if (CURRENT->current_nr_sectors == 0) {
+ end_request(1);
+ continue;
+ }
+ if (fs->ejected) {
+ end_request(0);
+ continue;
+ }
+
+ if (CURRENT->cmd == WRITE) {
+ if (fs->write_prot < 0)
+ fs->write_prot = swim3_readbit(fs, WRITE_PROT);
+ if (fs->write_prot) {
+ end_request(0);
+ continue;
+ }
+ }
+
+ fs->req_cyl = CURRENT->sector / fs->secpercyl;
+ x = CURRENT->sector % fs->secpercyl;
+ fs->head = x / fs->secpertrack;
+ fs->req_sector = x % fs->secpertrack + 1;
+ fs->state = do_transfer;
+ fs->retries = 0;
+
+ act(fs);
+ }
+}
+
+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 */
+ out_8(&sw->control_bis, SCAN_TRACK);
+ /* enable intr when track found */
+ out_8(&sw->intr_enable, ERROR | SEEN_SECTOR);
+ /* enable timeout */
+ fs->timeout.expires = jiffies + HZ;
+ fs->timeout.function = scan_timeout;
+ fs->timeout.data = (unsigned long) fs;
+ add_timer(&fs->timeout);
+}
+
+static inline void seek_track(struct floppy_state *fs, int n)
+{
+ volatile struct swim3 *sw = fs->swim3;
+
+ if (n >= 0) {
+ swim3_action(fs, SEEK_POSITIVE);
+ sw->nseek = n;
+ } else {
+ swim3_action(fs, SEEK_NEGATIVE);
+ sw->nseek = -n;
+ }
+ fs->expect_cyl = (fs->cur_cyl > 0)? fs->cur_cyl + n: -1;
+ swim3_select(fs, STEP);
+ out_8(&sw->control_bis, DO_SEEK);
+ /* enable intr when seek finished */
+ out_8(&sw->intr_enable, ERROR | SEEK_DONE);
+ /* enable timeout */
+ fs->timeout.expires = jiffies + HZ/2;
+ fs->timeout.function = seek_timeout;
+ fs->timeout.data = (unsigned long) fs;
+ add_timer(&fs->timeout);
+}
+
+static inline void init_dma(struct dbdma_cmd *cp, int cmd,
+ void *buf, int count)
+{
+ st_le16(&cp->req_count, count);
+ st_le16(&cp->command, cmd);
+ st_le32(&cp->phy_addr, virt_to_bus(buf));
+ cp->xfer_status = 0;
+}
+
+static inline void setup_transfer(struct floppy_state *fs)
+{
+ int n;
+ volatile struct swim3 *sw = fs->swim3;
+ struct dbdma_cmd *cp = fs->dma_cmd;
+ struct dbdma_regs *dr = fs->dma;
+
+ if (CURRENT->current_nr_sectors <= 0) {
+ printk(KERN_ERR "swim3: transfer 0 sectors?\n");
+ return;
+ }
+ if (CURRENT->cmd == WRITE)
+ n = 1;
+ else {
+ n = fs->secpertrack - fs->req_sector + 1;
+ if (n > CURRENT->current_nr_sectors)
+ n = CURRENT->current_nr_sectors;
+ }
+ fs->scount = n;
+ swim3_select(fs, fs->head? READ_DATA_1: READ_DATA_0);
+ out_8(&sw->sector, fs->req_sector);
+ out_8(&sw->nsect, n);
+ out_8(&sw->ssize, 0);
+ st_le32(&dr->cmdptr, virt_to_bus(cp));
+ if (CURRENT->cmd == WRITE) {
+ /* Set up 3 dma commands: write preamble, data, postamble */
+ init_dma(cp, OUTPUT_MORE, write_preamble, sizeof(write_preamble));
+ ++cp;
+ init_dma(cp, OUTPUT_MORE, CURRENT->buffer, 512);
+ ++cp;
+ init_dma(cp, OUTPUT_MORE, write_postamble, sizeof(write_postamble));
+ } else {
+ init_dma(cp, INPUT_MORE, CURRENT->buffer, n * 512);
+ }
+ ++cp;
+ out_le16(&cp->command, DBDMA_STOP);
+ out_le32(&dr->control, (RUN << 16) | RUN);
+ out_8(&sw->control_bis,
+ (CURRENT->cmd == WRITE? WRITE_SECTORS: 0) | SCAN_TRACK);
+ /* enable intr when transfer complete */
+ out_8(&sw->intr_enable, ERROR | TRANSFER_DONE);
+ /* enable timeout */
+ fs->timeout.expires = jiffies + 2*HZ;
+ fs->timeout.function = xfer_timeout;
+ fs->timeout.data = (unsigned long) fs;
+ add_timer(&fs->timeout);
+}
+
+static void act(struct floppy_state *fs)
+{
+ volatile struct swim3 *sw = fs->swim3;
+
+ for (;;) {
+ switch (fs->state) {
+ case idle:
+ return; /* XXX shouldn't get here */
+
+ case locating:
+ if (swim3_readbit(fs, TRACK_ZERO)) {
+ fs->cur_cyl = 0;
+ if (fs->req_cyl == 0)
+ fs->state = do_transfer;
+ else
+ fs->state = seeking;
+ break;
+ }
+ scan_track(fs);
+ return;
+
+ case seeking:
+ if (fs->cur_cyl < 0) {
+ fs->expect_cyl = -1;
+ fs->state = locating;
+ break;
+ }
+ if (fs->req_cyl == fs->cur_cyl) {
+ printk("whoops, seeking 0\n");
+ fs->state = do_transfer;
+ break;
+ }
+ seek_track(fs, fs->req_cyl - fs->cur_cyl);
+ return;
+
+ case settling:
+ /* wait for SEEK_COMPLETE to become true */
+ swim3_select(fs, SEEK_COMPLETE);
+ udelay(1);
+ out_8(&sw->intr_enable, ERROR | DATA_CHANGED);
+ in_8(&sw->intr); /* clear DATA_CHANGED */
+ if (in_8(&sw->status) & DATA) {
+ /* seek_complete is not yet true */
+ fs->timeout.expires = jiffies + HZ/2;
+ fs->timeout.function = seek_timeout;
+ fs->timeout.data = (unsigned long) fs;
+ add_timer(&fs->timeout);
+ return;
+ }
+ out_8(&sw->intr_enable, 0);
+ in_8(&sw->intr);
+ fs->state = locating;
+ break;
+
+ case do_transfer:
+ if (fs->cur_cyl != fs->req_cyl) {
+ if (fs->retries > 5) {
+ end_request(0);
+ fs->state = idle;
+ return;
+ }
+ fs->state = seeking;
+ break;
+ }
+ setup_transfer(fs);
+ return;
+
+ case jogging:
+ seek_track(fs, -5);
+ return;
+
+ default:
+ printk(KERN_ERR"swim3: unknown state %d\n", fs->state);
+ return;
+ }
+ }
+}
+
+static void scan_timeout(unsigned long data)
+{
+ struct floppy_state *fs = (struct floppy_state *) data;
+ volatile struct swim3 *sw = fs->swim3;
+
+ out_8(&sw->control_bic, SCAN_TRACK);
+ out_8(&sw->select, RELAX);
+ out_8(&sw->intr_enable, 0);
+ fs->cur_cyl = -1;
+ if (fs->retries > 5) {
+ end_request(0);
+ fs->state = idle;
+ start_request(fs);
+ } else {
+ fs->state = jogging;
+ act(fs);
+ }
+}
+
+static void seek_timeout(unsigned long data)
+{
+ struct floppy_state *fs = (struct floppy_state *) data;
+ volatile struct swim3 *sw = fs->swim3;
+
+ 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"); */
+ fs->state = locating;
+ act(fs);
+ return;
+ }
+ printk(KERN_ERR "swim3: seek timeout\n");
+ end_request(0);
+ fs->state = idle;
+ start_request(fs);
+}
+
+static void xfer_timeout(unsigned long data)
+{
+ struct floppy_state *fs = (struct floppy_state *) data;
+ volatile struct swim3 *sw = fs->swim3;
+ struct dbdma_regs *dr = fs->dma;
+ struct dbdma_cmd *cp = fs->dma_cmd;
+ unsigned long s;
+
+ st_le32(&dr->control, RUN << 16);
+ out_8(&sw->intr_enable, 0);
+ out_8(&sw->control_bic, WRITE_SECTORS | SCAN_TRACK);
+ out_8(&sw->select, RELAX);
+ if (CURRENT->cmd == WRITE)
+ ++cp;
+ if (ld_le16(&cp->xfer_status) != 0)
+ s = fs->scount - ((ld_le16(&cp->res_count) + 511) >> 9);
+ else
+ s = 0;
+ CURRENT->sector += s;
+ CURRENT->current_nr_sectors -= s;
+ printk(KERN_ERR "swim3: timeout %sing sector %ld\n",
+ (CURRENT->cmd==WRITE? "writ": "read"), CURRENT->sector);
+ end_request(0);
+ fs->state = idle;
+ start_request(fs);
+}
+
+static void 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;
+ int intr, err, n;
+ int stat, resid;
+ struct dbdma_regs *dr;
+ struct dbdma_cmd *cp;
+
+ err = in_8(&sw->error);
+ intr = in_8(&sw->intr);
+ if ((intr & ERROR) && fs->state != do_transfer)
+ printk(KERN_ERR "swim3_interrupt, state=%d, cmd=%x, intr=%x, err=%x\n",
+ fs->state, CURRENT->cmd, intr, err);
+ switch (fs->state) {
+ case locating:
+ if (intr & SEEN_SECTOR) {
+ out_8(&sw->control_bic, SCAN_TRACK);
+ out_8(&sw->select, RELAX);
+ out_8(&sw->intr_enable, 0);
+ del_timer(&fs->timeout);
+ if (sw->ctrack == 0xff) {
+ printk(KERN_ERR "swim3: seen sector but cyl=ff?\n");
+ fs->cur_cyl = -1;
+ if (fs->retries > 5) {
+ end_request(0);
+ fs->state = idle;
+ start_request(fs);
+ } else {
+ fs->state = jogging;
+ act(fs);
+ }
+ break;
+ }
+ fs->cur_cyl = sw->ctrack;
+ fs->cur_sector = sw->csect;
+ if (fs->expect_cyl != -1 && fs->expect_cyl != fs->cur_cyl)
+ printk(KERN_ERR "swim3: expected cyl %d, got %d\n",
+ fs->expect_cyl, fs->cur_cyl);
+ fs->state = do_transfer;
+ act(fs);
+ }
+ break;
+ case seeking:
+ case jogging:
+ if (sw->nseek == 0) {
+ out_8(&sw->control_bic, DO_SEEK);
+ out_8(&sw->select, RELAX);
+ out_8(&sw->intr_enable, 0);
+ del_timer(&fs->timeout);
+ if (fs->state == seeking)
+ ++fs->retries;
+ fs->state = settling;
+ act(fs);
+ }
+ break;
+ case settling:
+ out_8(&sw->intr_enable, 0);
+ del_timer(&fs->timeout);
+ act(fs);
+ break;
+ case do_transfer:
+ if ((intr & (ERROR | TRANSFER_DONE)) == 0)
+ break;
+ dr = fs->dma;
+ cp = fs->dma_cmd;
+ st_le32(&dr->control, RUN << 16);
+ out_8(&sw->intr_enable, 0);
+ out_8(&sw->control_bic, WRITE_SECTORS | SCAN_TRACK);
+ out_8(&sw->select, RELAX);
+ del_timer(&fs->timeout);
+ if (CURRENT->cmd == WRITE)
+ ++cp;
+ stat = ld_le16(&cp->xfer_status);
+ resid = ld_le16(&cp->res_count);
+ if (intr & ERROR) {
+ n = fs->scount - 1 - resid / 512;
+ if (n > 0) {
+ CURRENT->sector += n;
+ CURRENT->current_nr_sectors -= n;
+ CURRENT->buffer += n * 512;
+ fs->req_sector += n;
+ }
+ if (fs->retries < 5) {
+ ++fs->retries;
+ act(fs);
+ } else {
+ printk("swim3: error %sing block %ld (err=%x)\n",
+ CURRENT->cmd == WRITE? "writ": "read",
+ CURRENT->sector, err);
+ end_request(0);
+ fs->state = idle;
+ }
+ } else {
+ if ((stat & ACTIVE) == 0 || resid != 0) {
+ /* musta been an error */
+ printk(KERN_ERR "swim3: fd dma: stat=%x resid=%d\n", stat, resid);
+ printk(KERN_ERR " state=%d, cmd=%x, intr=%x, err=%x\n",
+ fs->state, CURRENT->cmd, intr, err);
+ end_request(0);
+ fs->state = idle;
+ start_request(fs);
+ break;
+ }
+ CURRENT->sector += fs->scount;
+ CURRENT->current_nr_sectors -= fs->scount;
+ CURRENT->buffer += fs->scount * 512;
+ if (CURRENT->current_nr_sectors <= 0) {
+ end_request(1);
+ fs->state = idle;
+ } else {
+ fs->req_sector += fs->scount;
+ if (fs->req_sector > fs->secpertrack) {
+ fs->req_sector -= fs->secpertrack;
+ if (++fs->head > 1) {
+ fs->head = 0;
+ ++fs->req_cyl;
+ }
+ }
+ act(fs);
+ }
+ }
+ if (fs->state == idle)
+ start_request(fs);
+ break;
+ default:
+ printk(KERN_ERR "swim3: don't know what to do in state %d\n", fs->state);
+ }
+}
+
+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)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ if (fs->state != idle) {
+ ++fs->wanted;
+ while (fs->state != available) {
+ if (interruptible
+ && (current->signal & ~current->blocked)) {
+ --fs->wanted;
+ restore_flags(flags);
+ return -EINTR;
+ }
+ interruptible_sleep_on(&fs->wait);
+ }
+ --fs->wanted;
+ }
+ fs->state = state;
+ restore_flags(flags);
+ return 0;
+}
+
+static void release_drive(struct floppy_state *fs)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ fs->state = idle;
+ start_request(fs);
+ restore_flags(flags);
+}
+
+static int fd_eject(struct floppy_state *fs)
+{
+ int err, n;
+
+ err = grab_drive(fs, ejecting, 1);
+ if (err)
+ return err;
+ swim3_action(fs, EJECT);
+ for (n = 2*HZ; n > 0; --n) {
+ if (swim3_readbit(fs, RELAX))
+ break;
+ if ((current->signal & ~current->blocked) != 0) {
+ err = -EINTR;
+ break;
+ }
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + 1;
+ schedule();
+ }
+ fs->ejected = 1;
+ release_drive(fs);
+ return err;
+}
+
+static struct floppy_struct floppy_type =
+ { 2880,18,2,80,0,0x1B,0x00,0xCF,0x6C,NULL }; /* 7 1.44MB 3.5" */
+
+static int floppy_ioctl(struct inode *inode, struct file *filp,
+ unsigned int cmd, unsigned long param)
+{
+ struct floppy_state *fs;
+ int err;
+
+ if (((cmd & 0x80) && !suser())
+ || ((cmd & 0x40) && !(filp && (filp->f_mode & IOCTL_MODE_BIT))))
+ return -EPERM;
+
+ fs = &floppy_states[0];
+ switch (cmd) {
+ case FDEJECT:
+ if (fs->ref_count != 1)
+ return -EBUSY;
+ err = fd_eject(fs);
+ return err;
+ case FDGETPRM:
+ err = copy_to_user((void *) param, (void *) &floppy_type,
+ sizeof(struct floppy_struct));
+ return err;
+ }
+ return -ENOIOCTLCMD;
+}
+
+static int floppy_open(struct inode *inode, struct file *filp)
+{
+ struct floppy_state *fs;
+ volatile struct swim3 *sw;
+ int n, err;
+
+ if (MINOR(inode->i_rdev) != 0)
+ return -ENODEV;
+ fs = &floppy_states[0];
+ sw = fs->swim3;
+ err = 0;
+ if (fs->ref_count == 0) {
+ 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))
+ break;
+ if ((current->signal & ~current->blocked) != 0) {
+ err = -EINTR;
+ break;
+ }
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + 1;
+ schedule();
+ }
+ if (err == 0 && (swim3_readbit(fs, SEEK_COMPLETE) == 0
+ || swim3_readbit(fs, DISK_IN) == 0))
+ err = -ENXIO;
+ swim3_action(fs, 9);
+
+ } else if (fs->ref_count == -1 || filp->f_flags & O_EXCL)
+ return -EBUSY;
+
+ if (err == 0 && filp && (filp->f_flags & O_NDELAY) == 0
+ && (filp->f_mode & 3)) {
+ check_disk_change(inode->i_rdev);
+ if (fs->ejected)
+ err = -ENXIO;
+ }
+
+ if (err == 0 && filp && (filp->f_flags & (O_WRONLY | O_RDWR))) {
+ if (fs->write_prot < 0)
+ fs->write_prot = swim3_readbit(fs, WRITE_PROT);
+ if (fs->write_prot)
+ err = -EROFS;
+ }
+
+ if (err) {
+ if (fs->ref_count == 0) {
+ swim3_action(fs, MOTOR_OFF);
+ out_8(&sw->control_bic, DRIVE_ENABLE | INTR_ENABLE);
+ }
+ return err;
+ }
+
+ if (filp->f_flags & O_EXCL)
+ fs->ref_count = -1;
+ else
+ ++fs->ref_count;
+
+ /* Allow ioctls if we have write-permissions even if read-only open */
+ if ((filp->f_mode & 2) || (permission(inode, 2) == 0))
+ filp->f_mode |= IOCTL_MODE_BIT;
+ if (filp->f_mode & 2)
+ filp->f_mode |= OPEN_WRITE_BIT;
+
+ return 0;
+}
+
+static int floppy_release(struct inode *inode, struct file *filp)
+{
+ struct floppy_state *fs;
+ volatile struct swim3 *sw;
+
+ if (MINOR(inode->i_rdev) != 0)
+ return -ENXIO;
+ fs = &floppy_states[0];
+ if (filp == 0 || (filp->f_mode & (2 | OPEN_WRITE_BIT)))
+ block_fsync(inode, filp);
+ sw = fs->swim3;
+ if (fs->ref_count > 0 && --fs->ref_count == 0) {
+ swim3_action(fs, MOTOR_OFF);
+ out_8(&sw->control_bic, 0xff);
+ }
+ return 0;
+}
+
+static int floppy_check_change(kdev_t dev)
+{
+ struct floppy_state *fs;
+
+ if (MAJOR(dev) != MAJOR_NR || MINOR(dev) != 0)
+ return 0;
+ fs = &floppy_states[0];
+ return fs->ejected;
+}
+
+static int floppy_revalidate(kdev_t dev)
+{
+ struct floppy_state *fs;
+ volatile struct swim3 *sw;
+ int ret, n;
+
+ if (MAJOR(dev) != MAJOR_NR || MINOR(dev) != 0)
+ return 0;
+ fs = &floppy_states[0];
+ 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);
+ fs->write_prot = -1;
+ fs->cur_cyl = -1;
+ for (n = HZ; n > 0; --n) {
+ if (swim3_readbit(fs, SEEK_COMPLETE))
+ break;
+ if ((current->signal & ~current->blocked) != 0)
+ break;
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + 1;
+ schedule();
+ }
+ ret = swim3_readbit(fs, SEEK_COMPLETE) == 0
+ || swim3_readbit(fs, DISK_IN) == 0;
+ if (ret)
+ swim3_action(fs, MOTOR_OFF);
+ else {
+ fs->ejected = 0;
+ swim3_action(fs, 9);
+ }
+
+ release_drive(fs);
+ return ret;
+}
+
+static long floppy_read(struct inode *inode, struct file *filp,
+ char *buf, unsigned long count)
+{
+ struct floppy_state *fs;
+
+ if (MINOR(inode->i_rdev) != 0)
+ return -ENODEV;
+ fs = &floppy_states[0];
+ if (fs->ejected)
+ return -ENXIO;
+ return block_read(inode, filp, buf, count);
+}
+
+static long floppy_write(struct inode *inode, struct file *filp,
+ const char *buf, unsigned long count)
+{
+ struct floppy_state *fs;
+
+ if (MINOR(inode->i_rdev) != 0)
+ return -ENODEV;
+ fs = &floppy_states[0];
+ if (fs->ejected)
+ return -ENXIO;
+ return block_write(inode, filp, buf, count);
+}
+
+static void floppy_off(unsigned int nr)
+{
+}
+
+static struct file_operations floppy_fops = {
+ NULL, /* lseek */
+ floppy_read, /* read */
+ floppy_write, /* write */
+ NULL, /* readdir */
+ NULL, /* poll */
+ floppy_ioctl, /* ioctl */
+ NULL, /* mmap */
+ floppy_open, /* open */
+ floppy_release, /* release */
+ block_fsync, /* fsync */
+ NULL, /* fasync */
+ floppy_check_change, /* check_media_change */
+ floppy_revalidate, /* revalidate */
+};
+
+int swim3_init(void)
+{
+ struct device_node *swims;
+ struct floppy_state *fs = &floppy_states[0];
+ volatile struct swim3 *sw;
+
+ swims = find_devices("swim3");
+ if (swims == NULL)
+ return 0;
+
+ if (swims->next != NULL)
+ printk(KERN_ERR "Warning: only using first SWIM3 floppy controller\n");
+ if (swims->n_addrs != 2 || swims->n_intrs != 2) {
+ printk(KERN_ERR "swim3: expecting 2 addrs and 2 intrs! (%d, %d)\n",
+ swims->n_addrs, swims->n_intrs);
+ return -EINVAL;
+ }
+
+ if (register_blkdev(MAJOR_NR, "fd", &floppy_fops)) {
+ printk(KERN_ERR "Unable to get major %d for floppy\n",
+ MAJOR_NR);
+ return -EBUSY;
+ }
+ blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+ blksize_size[MAJOR_NR] = floppy_blocksizes;
+ blk_size[MAJOR_NR] = floppy_sizes;
+
+ memset(fs, 0, sizeof(*fs));
+ fs->state = idle;
+ fs->swim3 = (volatile struct swim3 *) swims->addrs[0].address;
+ fs->dma = (struct dbdma_regs *) swims->addrs[1].address;
+ fs->swim3_intr = swims->intrs[0];
+ fs->dma_intr = swims->intrs[1];
+ fs->cur_cyl = -1;
+ fs->cur_sector = -1;
+ fs->secpercyl = 36;
+ fs->secpertrack = 18;
+ fs->total_secs = 2880;
+
+ fs->dma_cmd = (struct dbdma_cmd *) DBDMA_ALIGN(fs->dbdma_cmd_space);
+ memset(fs->dma_cmd, 0, 2 * sizeof(struct dbdma_cmd));
+ st_le16(&fs->dma_cmd[1].command, DBDMA_STOP);
+
+ if (request_irq(fs->swim3_intr, swim3_interrupt, 0, "SWIM3", fs)) {
+ printk(KERN_ERR "Couldn't get irq %d for SWIM3\n", fs->swim3_intr);
+ return -EBUSY;
+ }
+ if (request_irq(fs->dma_intr, fd_dma_interrupt, 0, "SWIM3-dma", fs)) {
+ printk(KERN_ERR "Couldn't get irq %d for SWIM3 DMA",
+ fs->dma_intr);
+ return -EBUSY;
+ }
+
+ sw = fs->swim3;
+ out_8(&sw->mode, 0x95);
+ out_8(&sw->control_bic, 0xff);
+ out_8(&sw->reg5, 0x28);
+
+ do_floppy = NULL;
+
+ printk(KERN_INFO "fd0: SWIM3 floppy controller\n");
+
+ return 0;
+}