Re: [PATCH 3/6] m68k: mac - Add SWIM floppy support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Jens,
Is this OK for you to go in through the m68k tree?
Thanks!
On Sun, Mar 1, 2009 at 10:21, Geert Uytterhoeven <geert@xxxxxxxxxxxxxx> wrote:> From: Laurent Vivier <laurent@xxxxxxxxxxxx>>> It allows to read data from a floppy, but not to write to, and to eject the> floppy (useful on our Mac without eject button).>> Signed-off-by: Laurent Vivier <Laurent@xxxxxxxxxxxx>> Signed-off-by: Geert Uytterhoeven <geert@xxxxxxxxxxxxxx>> Cc: Jens Axboe <axboe@xxxxxxxxx>> --->  arch/m68k/mac/config.c   |   44 ++>  arch/m68k/mac/via.c      |    9 +>  drivers/block/Kconfig    |    7 +>  drivers/block/Makefile   |    3 +>  drivers/block/swim.c     |  995 ++++++++++++++++++++++++++++++++++++++++++++++>  drivers/block/swim_asm.S |  247 ++++++++++++>  6 files changed, 1305 insertions(+), 0 deletions(-)>  create mode 100644 drivers/block/swim.c>  create mode 100644 drivers/block/swim_asm.S>> diff --git a/arch/m68k/mac/config.c b/arch/m68k/mac/config.c> index 3a1c0b2..be01798 100644> --- a/arch/m68k/mac/config.c> +++ b/arch/m68k/mac/config.c> @@ -22,6 +22,7 @@>  /* keyb */>  #include <linux/init.h>>  #include <linux/vt_kern.h>> +#include <linux/platform_device.h>>>  #define BOOTINFO_COMPAT_1_0>  #include <asm/setup.h>> @@ -43,6 +44,10 @@>  #include <asm/mac_oss.h>>  #include <asm/mac_psc.h>>> +/* platform device info */> +> +#define SWIM_IO_SIZE 0x2000    /* SWIM IO resource size */> +>  /* Mac bootinfo struct */>>  struct mac_booter_data mac_bi_data;> @@ -870,3 +875,42 @@ static void mac_get_model(char *str)>        strcpy(str, "Macintosh ");>        strcat(str, macintosh_config->name);>  }> +> +static struct resource swim_resources[1];> +> +static struct platform_device swim_device = {> +       .name           = "swim",> +       .id             = -1,> +       .num_resources  = ARRAY_SIZE(swim_resources),> +       .resource       = swim_resources,> +};> +> +static struct platform_device *mac_platform_devices[] __initdata = {> +       &swim_device> +};> +> +int __init mac_platform_init(void)> +{> +       u8 *swim_base;> +> +       switch (macintosh_config->floppy_type) {> +       case MAC_FLOPPY_SWIM_ADDR1:> +               swim_base = (u8 *)(VIA1_BASE + 0x1E000);> +               break;> +       case MAC_FLOPPY_SWIM_ADDR2:> +               swim_base = (u8 *)(VIA1_BASE + 0x16000);> +               break;> +       default:> +               return 0;> +       }> +> +       swim_resources[0].name = "swim-regs";> +       swim_resources[0].start = (resource_size_t)swim_base;> +       swim_resources[0].end = (resource_size_t)(swim_base + SWIM_IO_SIZE);> +       swim_resources[0].flags = IORESOURCE_MEM;> +> +       return platform_add_devices(mac_platform_devices,> +                                   ARRAY_SIZE(mac_platform_devices));> +}> +> +arch_initcall(mac_platform_init);> diff --git a/arch/m68k/mac/via.c b/arch/m68k/mac/via.c> index 7d97ba5..11bce3c 100644> --- a/arch/m68k/mac/via.c> +++ b/arch/m68k/mac/via.c> @@ -645,3 +645,12 @@ int via_irq_pending(int irq)>        }>        return 0;>  }> +> +void via1_set_head(int head)> +{> +       if (head == 0)> +               via1[vBufA] &= ~VIA1A_vHeadSel;> +       else> +               via1[vBufA] |= VIA1A_vHeadSel;> +}> +EXPORT_SYMBOL(via1_set_head);> diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig> index 0344a8a..e7b8aa0 100644> --- a/drivers/block/Kconfig> +++ b/drivers/block/Kconfig> @@ -45,6 +45,13 @@ config MAC_FLOPPY>          If you have a SWIM-3 (Super Woz Integrated Machine 3; from Apple)>          floppy controller, say Y here. Most commonly found in PowerMacs.>> +config BLK_DEV_SWIM> +       tristate "Support for SWIM Macintosh floppy"> +       depends on M68K && MAC> +       help> +         You should select this option if you want floppy support> +         and you don't have a II, IIfx, Q900, Q950 or AV series.> +>  config AMIGA_Z2RAM>        tristate "Amiga Zorro II ramdisk support">        depends on ZORRO> diff --git a/drivers/block/Makefile b/drivers/block/Makefile> index 204332b..b32b7f9 100644> --- a/drivers/block/Makefile> +++ b/drivers/block/Makefile> @@ -6,6 +6,7 @@>  #>>  obj-$(CONFIG_MAC_FLOPPY)       += swim3.o> +obj-$(CONFIG_BLK_DEV_SWIM)     += swim_mod.o>  obj-$(CONFIG_BLK_DEV_FD)       += floppy.o>  obj-$(CONFIG_AMIGA_FLOPPY)     += amiflop.o>  obj-$(CONFIG_PS3_DISK)         += ps3disk.o> @@ -32,3 +33,5 @@ obj-$(CONFIG_BLK_DEV_UB)      += ub.o>  obj-$(CONFIG_BLK_DEV_HD)       += hd.o>>  obj-$(CONFIG_XEN_BLKDEV_FRONTEND)      += xen-blkfront.o> +> +swim_mod-objs  := swim.o swim_asm.o> diff --git a/drivers/block/swim.c b/drivers/block/swim.c> new file mode 100644> index 0000000..d22cc38> --- /dev/null> +++ b/drivers/block/swim.c> @@ -0,0 +1,995 @@> +/*> + * Driver for SWIM (Sander Woz Integrated Machine) floppy controller> + *> + * Copyright (C) 2004,2008 Laurent Vivier <Laurent@xxxxxxxxxxxx>> + *> + * based on Alastair Bridgewater SWIM analysis, 2001> + * based on SWIM3 driver (c) Paul Mackerras, 1996> + * based on netBSD IWM driver (c) 1997, 1998 Hauke Fath.> + *> + * 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.> + *> + * 2004-08-21 (lv) - Initial implementation> + * 2008-10-30 (lv) - Port to 2.6> + */> +> +#include <linux/module.h>> +#include <linux/fd.h>> +#include <linux/blkdev.h>> +#include <linux/hdreg.h>> +#include <linux/kernel.h>> +#include <linux/delay.h>> +#include <linux/platform_device.h>> +> +#include <asm/macintosh.h>> +#include <asm/mac_via.h>> +> +#define CARDNAME "swim"> +> +struct sector_header {> +       unsigned char side;> +       unsigned char track;> +       unsigned char sector;> +       unsigned char size;> +       unsigned char crc0;> +       unsigned char crc1;> +} __attribute__((packed));> +> +#define DRIVER_VERSION "Version 0.2 (2008-10-30)"> +> +#define REG(x) unsigned char x, x ## _pad[0x200 - 1];> +> +struct swim {> +       REG(write_data)> +       REG(write_mark)> +       REG(write_CRC)> +       REG(write_parameter)> +       REG(write_phase)> +       REG(write_setup)> +       REG(write_mode0)> +       REG(write_mode1)> +> +       REG(read_data)> +       REG(read_mark)> +       REG(read_error)> +       REG(read_parameter)> +       REG(read_phase)> +       REG(read_setup)> +       REG(read_status)> +       REG(read_handshake)> +} __attribute__((packed));> +> +#define swim_write(base, reg, v)       out_8(&(base)->write_##reg, (v))> +#define swim_read(base, reg)           in_8(&(base)->read_##reg)> +> +/* IWM registers */> +> +struct iwm {> +       REG(ph0L)> +       REG(ph0H)> +       REG(ph1L)> +       REG(ph1H)> +       REG(ph2L)> +       REG(ph2H)> +       REG(ph3L)> +       REG(ph3H)> +       REG(mtrOff)> +       REG(mtrOn)> +       REG(intDrive)> +       REG(extDrive)> +       REG(q6L)> +       REG(q6H)> +       REG(q7L)> +       REG(q7H)> +} __attribute__((packed));> +> +#define iwm_write(base, reg, v)        out_8(&(base)->reg, (v))> +#define iwm_read(base, reg)            in_8(&(base)->reg)> +> +/* bits in phase register */> +> +#define SEEK_POSITIVE  0x070> +#define SEEK_NEGATIVE  0x074> +#define STEP           0x071> +#define MOTOR_ON       0x072> +#define MOTOR_OFF      0x076> +#define INDEX          0x073> +#define EJECT          0x077> +#define SETMFM         0x171> +#define SETGCR         0x175> +> +#define RELAX          0x033> +#define LSTRB          0x008> +> +#define CA_MASK                0x077> +> +/* Select values for swim_select and swim_readbit */> +> +#define READ_DATA_0    0x074> +#define TWOMEG_DRIVE   0x075> +#define SINGLE_SIDED   0x076> +#define DRIVE_PRESENT  0x077> +#define DISK_IN                0x170> +#define WRITE_PROT     0x171> +#define TRACK_ZERO     0x172> +#define TACHO          0x173> +#define READ_DATA_1    0x174> +#define MFM_MODE       0x175> +#define SEEK_COMPLETE  0x176> +#define ONEMEG_MEDIA   0x177> +> +/* Bits in handshake register */> +> +#define MARK_BYTE      0x01> +#define CRC_ZERO       0x02> +#define RDDATA         0x04> +#define SENSE          0x08> +#define MOTEN          0x10> +#define ERROR          0x20> +#define DAT2BYTE       0x40> +#define DAT1BYTE       0x80> +> +/* bits in setup register */> +> +#define S_INV_WDATA    0x01> +#define S_3_5_SELECT   0x02> +#define S_GCR          0x04> +#define S_FCLK_DIV2    0x08> +#define S_ERROR_CORR   0x10> +#define S_IBM_DRIVE    0x20> +#define S_GCR_WRITE    0x40> +#define S_TIMEOUT      0x80> +> +/* bits in mode register */> +> +#define CLFIFO         0x01> +#define ENBL1          0x02> +#define ENBL2          0x04> +#define ACTION         0x08> +#define WRITE_MODE     0x10> +#define HEDSEL         0x20> +#define MOTON          0x80> +> +/*----------------------------------------------------------------------------*/> +> +enum drive_location {> +       INTERNAL_DRIVE = 0x02,> +       EXTERNAL_DRIVE = 0x04,> +};> +> +enum media_type {> +       DD_MEDIA,> +       HD_MEDIA,> +};> +> +struct floppy_state {> +> +       /* physical properties */> +> +       enum drive_location location;   /* internal or external drive */> +       int              head_number;   /* single- or double-sided drive */> +> +       /* media */> +> +       int              disk_in;> +       int              ejected;> +       enum media_type  type;> +       int              write_protected;> +> +       int              total_secs;> +       int              secpercyl;> +       int              secpertrack;> +> +       /* in-use information */> +> +       int             track;> +       int             ref_count;> +> +       struct gendisk *disk;> +> +       /* parent controller */> +> +       struct swim_priv *swd;> +};> +> +enum motor_action {> +       OFF,> +       ON,> +};> +> +enum head {> +       LOWER_HEAD = 0,> +       UPPER_HEAD = 1,> +};> +> +#define FD_MAX_UNIT    2> +> +struct swim_priv {> +       struct swim __iomem *base;> +       spinlock_t lock;> +       struct request_queue *queue;> +       int floppy_count;> +       struct floppy_state unit[FD_MAX_UNIT];> +};> +> +extern int swim_read_sector_header(struct swim __iomem *base,> +                                  struct sector_header *header);> +extern int swim_read_sector_data(struct swim __iomem *base,> +                                unsigned char *data);> +> +static inline void set_swim_mode(struct swim __iomem *base, int enable)> +{> +       struct iwm __iomem *iwm_base;> +       unsigned long flags;> +> +       if (!enable) {> +               swim_write(base, mode0, 0xf8);> +               return;> +       }> +> +       iwm_base = (struct iwm __iomem *)base;> +       local_irq_save(flags);> +> +       iwm_read(iwm_base, q7L);> +       iwm_read(iwm_base, mtrOff);> +       iwm_read(iwm_base, q6H);> +> +       iwm_write(iwm_base, q7H, 0x57);> +       iwm_write(iwm_base, q7H, 0x17);> +       iwm_write(iwm_base, q7H, 0x57);> +       iwm_write(iwm_base, q7H, 0x57);> +> +       local_irq_restore(flags);> +}> +> +static inline int get_swim_mode(struct swim __iomem *base)> +{> +       unsigned long flags;> +> +       local_irq_save(flags);> +> +       swim_write(base, phase, 0xf5);> +       if (swim_read(base, phase) != 0xf5)> +               goto is_iwm;> +       swim_write(base, phase, 0xf6);> +       if (swim_read(base, phase) != 0xf6)> +               goto is_iwm;> +       swim_write(base, phase, 0xf7);> +       if (swim_read(base, phase) != 0xf7)> +               goto is_iwm;> +       local_irq_restore(flags);> +       return 1;> +is_iwm:> +       local_irq_restore(flags);> +       return 0;> +}> +> +static inline void swim_select(struct swim __iomem *base, int sel)> +{> +       swim_write(base, phase, RELAX);> +> +       via1_set_head(sel & 0x100);> +> +       swim_write(base, phase, sel & CA_MASK);> +}> +> +static inline void swim_action(struct swim __iomem *base, int action)> +{> +       unsigned long flags;> +> +       local_irq_save(flags);> +> +       swim_select(base, action);> +       udelay(1);> +       swim_write(base, phase, (LSTRB<<4) | LSTRB);> +       udelay(1);> +       swim_write(base, phase, (LSTRB<<4) | ((~LSTRB) & 0x0F));> +       udelay(1);> +> +       local_irq_restore(flags);> +}> +> +static inline int swim_readbit(struct swim __iomem *base, int bit)> +{> +       int stat;> +> +       swim_select(base, bit);> +> +       udelay(10);> +> +       stat = swim_read(base, handshake);> +> +       return (stat & SENSE) == 0;> +}> +> +static inline void swim_drive(struct swim __iomem *base,> +                             enum drive_location location)> +{> +       if (location == INTERNAL_DRIVE) {> +               swim_write(base, mode0, EXTERNAL_DRIVE); /* clear drive 1 bit */> +               swim_write(base, mode1, INTERNAL_DRIVE); /* set drive 0 bit */> +       } else if (location == EXTERNAL_DRIVE) {> +               swim_write(base, mode0, INTERNAL_DRIVE); /* clear drive 0 bit */> +               swim_write(base, mode1, EXTERNAL_DRIVE); /* set drive 1 bit */> +       }> +}> +> +static inline void swim_motor(struct swim __iomem *base,> +                             enum motor_action action)> +{> +       if (action == ON) {> +               int i;> +> +               swim_action(base, MOTOR_ON);> +> +               for (i = 0; i < 2*HZ; i++) {> +                       swim_select(base, RELAX);> +                       if (swim_readbit(base, MOTOR_ON))> +                               break;> +                       current->state = TASK_INTERRUPTIBLE;> +                       schedule_timeout(1);> +               }> +       } else if (action == OFF) {> +               swim_action(base, MOTOR_OFF);> +               swim_select(base, RELAX);> +       }> +}> +> +static inline void swim_eject(struct swim __iomem *base)> +{> +       int i;> +> +       swim_action(base, EJECT);> +> +       for (i = 0; i < 2*HZ; i++) {> +               swim_select(base, RELAX);> +               if (!swim_readbit(base, DISK_IN))> +                       break;> +               current->state = TASK_INTERRUPTIBLE;> +               schedule_timeout(1);> +       }> +       swim_select(base, RELAX);> +}> +> +static inline void swim_head(struct swim __iomem *base, enum head head)> +{> +       /* wait drive is ready */> +> +       if (head == UPPER_HEAD)> +               swim_select(base, READ_DATA_1);> +       else if (head == LOWER_HEAD)> +               swim_select(base, READ_DATA_0);> +}> +> +static inline int swim_step(struct swim __iomem *base)> +{> +       int wait;> +> +       swim_action(base, STEP);> +> +       for (wait = 0; wait < HZ; wait++) {> +> +               current->state = TASK_INTERRUPTIBLE;> +               schedule_timeout(1);> +> +               swim_select(base, RELAX);> +               if (!swim_readbit(base, STEP))> +                       return 0;> +       }> +       return -1;> +}> +> +static inline int swim_track00(struct swim __iomem *base)> +{> +       int try;> +> +       swim_action(base, SEEK_NEGATIVE);> +> +       for (try = 0; try < 100; try++) {> +> +               swim_select(base, RELAX);> +               if (swim_readbit(base, TRACK_ZERO))> +                       break;> +> +               if (swim_step(base))> +                       return -1;> +       }> +> +       if (swim_readbit(base, TRACK_ZERO))> +               return 0;> +> +       return -1;> +}> +> +static inline int swim_seek(struct swim __iomem *base, int step)> +{> +       if (step == 0)> +               return 0;> +> +       if (step < 0) {> +               swim_action(base, SEEK_NEGATIVE);> +               step = -step;> +       } else> +               swim_action(base, SEEK_POSITIVE);> +> +       for ( ; step > 0; step--) {> +               if (swim_step(base))> +                       return -1;> +       }> +> +       return 0;> +}> +> +static inline int swim_track(struct floppy_state *fs,  int track)> +{> +       struct swim __iomem *base = fs->swd->base;> +       int ret;> +> +       ret = swim_seek(base, track - fs->track);> +> +       if (ret == 0)> +               fs->track = track;> +       else {> +               swim_track00(base);> +               fs->track = 0;> +       }> +> +       return ret;> +}> +> +static int floppy_eject(struct floppy_state *fs)> +{> +       struct swim __iomem *base = fs->swd->base;> +> +       swim_drive(base, fs->location);> +       swim_motor(base, OFF);> +       swim_eject(base);> +> +       fs->disk_in = 0;> +       fs->ejected = 1;> +> +       return 0;> +}> +> +static inline int swim_read_sector(struct floppy_state *fs,> +                                  int side, int track,> +                                  int sector, unsigned char *buffer)> +{> +       struct swim __iomem *base = fs->swd->base;> +       unsigned long flags;> +       struct sector_header header;> +       int ret = -1;> +       short i;> +> +       swim_track(fs, track);> +> +       swim_write(base, mode1, MOTON);> +       swim_head(base, side);> +       swim_write(base, mode0, side);> +> +       local_irq_save(flags);> +       for (i = 0; i < 36; i++) {> +               ret = swim_read_sector_header(base, &header);> +               if (!ret && (header.sector == sector)) {> +                       /* found */> +> +                       ret = swim_read_sector_data(base, buffer);> +                       break;> +               }> +       }> +       local_irq_restore(flags);> +> +       swim_write(base, mode0, MOTON);> +> +       if ((header.side != side)  || (header.track != track) ||> +            (header.sector != sector))> +               return 0;> +> +       return ret;> +}> +> +static int floppy_read_sectors(struct floppy_state *fs,> +                              int req_sector, int sectors_nb,> +                              unsigned char *buffer)> +{> +       struct swim __iomem *base = fs->swd->base;> +       int ret;> +       int side, track, sector;> +       int i, try;> +> +> +       swim_drive(base, fs->location);> +       for (i = req_sector; i < req_sector + sectors_nb; i++) {> +               int x;> +               track = i / fs->secpercyl;> +               x = i % fs->secpercyl;> +               side = x / fs->secpertrack;> +               sector = x % fs->secpertrack + 1;> +> +               try = 5;> +               do {> +                       ret = swim_read_sector(fs, side, track, sector,> +                                               buffer);> +                       if (try-- == 0)> +                               return -1;> +               } while (ret != 512);> +> +               buffer += ret;> +       }> +> +       return 0;> +}> +> +static void redo_fd_request(struct request_queue *q)> +{> +       struct request *req;> +       struct floppy_state *fs;> +> +       while ((req = elv_next_request(q))) {> +> +               fs = req->rq_disk->private_data;> +               if (req->sector < 0 || req->sector >= fs->total_secs) {> +                       end_request(req, 0);> +                       continue;> +               }> +               if (req->current_nr_sectors == 0) {> +                       end_request(req, 1);> +                       continue;> +               }> +               if (!fs->disk_in) {> +                       end_request(req, 0);> +                       continue;> +               }> +               if (rq_data_dir(req) == WRITE) {> +                       if (fs->write_protected) {> +                               end_request(req, 0);> +                               continue;> +                       }> +               }> +               switch (rq_data_dir(req)) {> +               case WRITE:> +                       /* NOT IMPLEMENTED */> +                       end_request(req, 0);> +                       break;> +               case READ:> +                       if (floppy_read_sectors(fs, req->sector,> +                                               req->current_nr_sectors,> +                                               req->buffer)) {> +                               end_request(req, 0);> +                               continue;> +                       }> +                       req->nr_sectors -= req->current_nr_sectors;> +                       req->sector += req->current_nr_sectors;> +                       req->buffer += req->current_nr_sectors * 512;> +                       end_request(req, 1);> +                       break;> +               }> +       }> +}> +> +static void do_fd_request(struct request_queue *q)> +{> +       redo_fd_request(q);> +}> +> +static struct floppy_struct floppy_type[4] = {> +       {    0,  0, 0,  0, 0, 0x00, 0x00, 0x00, 0x00, NULL }, /* no testing   */> +       {  720,  9, 1, 80, 0, 0x2A, 0x02, 0xDF, 0x50, NULL }, /* 360KB SS 3.5"*/> +       { 1440,  9, 2, 80, 0, 0x2A, 0x02, 0xDF, 0x50, NULL }, /* 720KB 3.5"   */> +       { 2880, 18, 2, 80, 0, 0x1B, 0x00, 0xCF, 0x6C, NULL }, /* 1.44MB 3.5"  */> +};> +> +static int get_floppy_geometry(struct floppy_state *fs, int type,> +                              struct floppy_struct **g)> +{> +       if (type >= ARRAY_SIZE(floppy_type))> +               return -EINVAL;> +> +       if (type)> +               *g = &floppy_type[type];> +       else if (fs->type == HD_MEDIA) /* High-Density media */> +               *g = &floppy_type[3];> +       else if (fs->head_number == 2) /* double-sided */> +               *g = &floppy_type[2];> +       else> +               *g = &floppy_type[1];> +> +       return 0;> +}> +> +static void setup_medium(struct floppy_state *fs)> +{> +       struct swim __iomem *base = fs->swd->base;> +> +       if (swim_readbit(base, DISK_IN)) {> +               struct floppy_struct *g;> +               fs->disk_in = 1;> +               fs->write_protected = swim_readbit(base, WRITE_PROT);> +               fs->type = swim_readbit(base, ONEMEG_MEDIA);> +> +               if (swim_track00(base))> +                       printk(KERN_ERR> +                               "SWIM: cannot move floppy head to track 0\n");> +> +               swim_track00(base);> +> +               get_floppy_geometry(fs, 0, &g);> +               fs->total_secs = g->size;> +               fs->secpercyl = g->head * g->sect;> +               fs->secpertrack = g->sect;> +               fs->track = 0;> +       } else {> +               fs->disk_in = 0;> +       }> +}> +> +static int floppy_open(struct block_device *bdev, fmode_t mode)> +{> +       struct floppy_state *fs = bdev->bd_disk->private_data;> +       struct swim __iomem *base = fs->swd->base;> +       int err;> +> +       if (fs->ref_count == -1 || (fs->ref_count && mode & FMODE_EXCL))> +               return -EBUSY;> +> +       if (mode & FMODE_EXCL)> +               fs->ref_count = -1;> +       else> +               fs->ref_count++;> +> +       swim_write(base, setup, S_IBM_DRIVE  | S_FCLK_DIV2);> +       udelay(10);> +       swim_drive(base, INTERNAL_DRIVE);> +       swim_motor(base, ON);> +       swim_action(base, SETMFM);> +       if (fs->ejected)> +               setup_medium(fs);> +       if (!fs->disk_in) {> +               err = -ENXIO;> +               goto out;> +       }> +> +       if (mode & FMODE_NDELAY)> +               return 0;> +> +       if (mode & (FMODE_READ|FMODE_WRITE)) {> +               check_disk_change(bdev);> +               if ((mode & FMODE_WRITE) && fs->write_protected) {> +                       err = -EROFS;> +                       goto out;> +               }> +       }> +       return 0;> +out:> +       if (fs->ref_count < 0)> +               fs->ref_count = 0;> +       else if (fs->ref_count > 0)> +               --fs->ref_count;> +> +       if (fs->ref_count == 0)> +               swim_motor(base, OFF);> +       return err;> +}> +> +static int floppy_release(struct gendisk *disk, fmode_t mode)> +{> +       struct floppy_state *fs = disk->private_data;> +       struct swim __iomem *base = fs->swd->base;> +> +       if (fs->ref_count < 0)> +               fs->ref_count = 0;> +       else if (fs->ref_count > 0)> +               --fs->ref_count;> +> +       if (fs->ref_count == 0)> +               swim_motor(base, OFF);> +> +       return 0;> +}> +> +static int floppy_ioctl(struct block_device *bdev, fmode_t mode,> +                       unsigned int cmd, unsigned long param)> +{> +       struct floppy_state *fs = bdev->bd_disk->private_data;> +       int err;> +> +       if ((cmd & 0x80) && !capable(CAP_SYS_ADMIN))> +                       return -EPERM;> +> +       switch (cmd) {> +       case FDEJECT:> +               if (fs->ref_count != 1)> +                       return -EBUSY;> +               err = floppy_eject(fs);> +               return err;> +> +       case FDGETPRM:> +               if (copy_to_user((void __user *) param, (void *) &floppy_type,> +                                sizeof(struct floppy_struct)))> +                       return -EFAULT;> +               break;> +> +       default:> +               printk(KERN_DEBUG "SWIM floppy_ioctl: unknown cmd %d\n",> +                      cmd);> +               return -ENOSYS;> +       }> +       return 0;> +}> +> +static int floppy_getgeo(struct block_device *bdev, struct hd_geometry *geo)> +{> +       struct floppy_state *fs = bdev->bd_disk->private_data;> +       struct floppy_struct *g;> +       int ret;> +> +       ret = get_floppy_geometry(fs, 0, &g);> +       if (ret)> +               return ret;> +> +       geo->heads = g->head;> +       geo->sectors = g->sect;> +       geo->cylinders = g->track;> +> +       return 0;> +}> +> +static int floppy_check_change(struct gendisk *disk)> +{> +       struct floppy_state *fs = disk->private_data;> +> +       return fs->ejected;> +}> +> +static int floppy_revalidate(struct gendisk *disk)> +{> +       struct floppy_state *fs = disk->private_data;> +       struct swim __iomem *base = fs->swd->base;> +> +       swim_drive(base, fs->location);> +> +       if (fs->ejected)> +               setup_medium(fs);> +> +       if (!fs->disk_in)> +               swim_motor(base, OFF);> +       else> +               fs->ejected = 0;> +> +       return !fs->disk_in;> +}> +> +static struct block_device_operations floppy_fops = {> +       .owner           = THIS_MODULE,> +       .open            = floppy_open,> +       .release         = floppy_release,> +       .locked_ioctl    = floppy_ioctl,> +       .getgeo          = floppy_getgeo,> +       .media_changed   = floppy_check_change,> +       .revalidate_disk = floppy_revalidate,> +};> +> +static struct kobject *floppy_find(dev_t dev, int *part, void *data)> +{> +       struct swim_priv *swd = data;> +       int drive = (*part & 3);> +> +       if (drive > swd->floppy_count)> +               return NULL;> +> +       *part = 0;> +       return get_disk(swd->unit[drive].disk);> +}> +> +static int __devinit swim_add_floppy(struct swim_priv *swd,> +                                    enum drive_location location)> +{> +       struct floppy_state *fs = &swd->unit[swd->floppy_count];> +       struct swim __iomem *base = swd->base;> +> +       fs->location = location;> +> +       swim_drive(base, location);> +> +       swim_motor(base, OFF);> +> +       if (swim_readbit(base, SINGLE_SIDED))> +               fs->head_number = 1;> +       else> +               fs->head_number = 2;> +       fs->ref_count = 0;> +       fs->ejected = 1;> +> +       swd->floppy_count++;> +> +       return 0;> +}> +> +static int __devinit swim_floppy_init(struct swim_priv *swd)> +{> +       int err;> +       int drive;> +       struct swim __iomem *base = swd->base;> +> +       /* scan floppy drives */> +> +       swim_drive(base, INTERNAL_DRIVE);> +       if (swim_readbit(base, DRIVE_PRESENT))> +               swim_add_floppy(swd, INTERNAL_DRIVE);> +       swim_drive(base, EXTERNAL_DRIVE);> +       if (swim_readbit(base, DRIVE_PRESENT))> +               swim_add_floppy(swd, EXTERNAL_DRIVE);> +> +       /* register floppy drives */> +> +       err = register_blkdev(FLOPPY_MAJOR, "fd");> +       if (err) {> +               printk(KERN_ERR "Unable to get major %d for SWIM floppy\n",> +                      FLOPPY_MAJOR);> +               return -EBUSY;> +       }> +> +       for (drive = 0; drive < swd->floppy_count; drive++) {> +               swd->unit[drive].disk = alloc_disk(1);> +               if (swd->unit[drive].disk == NULL) {> +                       err = -ENOMEM;> +                       goto exit_put_disks;> +               }> +               swd->unit[drive].swd = swd;> +       }> +> +       swd->queue = blk_init_queue(do_fd_request, &swd->lock);> +       if (!swd->queue) {> +               err = -ENOMEM;> +               goto exit_put_disks;> +       }> +> +       for (drive = 0; drive < swd->floppy_count; drive++) {> +               swd->unit[drive].disk->flags = GENHD_FL_REMOVABLE;> +               swd->unit[drive].disk->major = FLOPPY_MAJOR;> +               swd->unit[drive].disk->first_minor = drive;> +               sprintf(swd->unit[drive].disk->disk_name, "fd%d", drive);> +               swd->unit[drive].disk->fops = &floppy_fops;> +               swd->unit[drive].disk->private_data = &swd->unit[drive];> +               swd->unit[drive].disk->queue = swd->queue;> +               set_capacity(swd->unit[drive].disk, 2880);> +               add_disk(swd->unit[drive].disk);> +       }> +> +       blk_register_region(MKDEV(FLOPPY_MAJOR, 0), 256, THIS_MODULE,> +                           floppy_find, NULL, swd);> +> +       return 0;> +> +exit_put_disks:> +       unregister_blkdev(FLOPPY_MAJOR, "fd");> +       while (drive--)> +               put_disk(swd->unit[drive].disk);> +       return err;> +}> +> +static int __devinit swim_probe(struct platform_device *dev)> +{> +       struct resource *res;> +       struct swim __iomem *swim_base;> +       struct swim_priv *swd;> +       int ret;> +> +       res = platform_get_resource_byname(dev, IORESOURCE_MEM, "swim-regs");> +       if (!res) {> +               ret = -ENODEV;> +               goto out;> +       }> +> +       if (!request_mem_region(res->start, resource_size(res), CARDNAME)) {> +               ret = -EBUSY;> +               goto out;> +       }> +> +       swim_base = ioremap(res->start, resource_size(res));> +       if (!swim_base) {> +               return -ENOMEM;> +               goto out_release_io;> +       }> +> +       /* probe device */> +> +       set_swim_mode(swim_base, 1);> +       if (!get_swim_mode(swim_base)) {> +               printk(KERN_INFO "SWIM device not found !\n");> +               ret = -ENODEV;> +               goto out_iounmap;> +       }> +> +       /* set platform driver data */> +> +       swd = kzalloc(sizeof(struct swim_priv), GFP_KERNEL);> +       if (!swd) {> +               ret = -ENOMEM;> +               goto out_iounmap;> +       }> +       platform_set_drvdata(dev, swd);> +> +       swd->base = swim_base;> +> +       ret = swim_floppy_init(swd);> +       if (ret)> +               goto out_kfree;> +> +       return 0;> +> +out_kfree:> +       platform_set_drvdata(dev, NULL);> +       kfree(swd);> +out_iounmap:> +       iounmap(swim_base);> +out_release_io:> +       release_mem_region(res->start, resource_size(res));> +out:> +       return ret;> +}> +> +static int __devexit swim_remove(struct platform_device *dev)> +{> +       struct swim_priv *swd = platform_get_drvdata(dev);> +       int drive;> +       struct resource *res;> +> +       blk_unregister_region(MKDEV(FLOPPY_MAJOR, 0), 256);> +> +       for (drive = 0; drive < swd->floppy_count; drive++) {> +               del_gendisk(swd->unit[drive].disk);> +               put_disk(swd->unit[drive].disk);> +       }> +> +       unregister_blkdev(FLOPPY_MAJOR, "fd");> +> +       blk_cleanup_queue(swd->queue);> +> +       /* eject floppies */> +> +       for (drive = 0; drive < swd->floppy_count; drive++)> +               floppy_eject(&swd->unit[drive]);> +> +       iounmap(swd->base);> +> +       res = platform_get_resource_byname(dev, IORESOURCE_MEM, "swim-regs");> +       if (res)> +               release_mem_region(res->start, resource_size(res));> +> +       platform_set_drvdata(dev, NULL);> +       kfree(swd);> +> +       return 0;> +}> +> +static struct platform_driver swim_driver = {> +       .probe  = swim_probe,> +       .remove = __devexit_p(swim_remove),> +       .driver   = {> +               .name   = CARDNAME,> +               .owner  = THIS_MODULE,> +       },> +};> +> +static int __init swim_init(void)> +{> +       printk(KERN_INFO "SWIM floppy driver %s\n", DRIVER_VERSION);> +> +       return platform_driver_register(&swim_driver);> +}> +module_init(swim_init);> +> +static void __exit swim_exit(void)> +{> +       platform_driver_unregister(&swim_driver);> +}> +module_exit(swim_exit);> +> +MODULE_DESCRIPTION("Driver for SWIM floppy controller");> +MODULE_LICENSE("GPL");> +MODULE_AUTHOR("Laurent Vivier <laurent@xxxxxxxxxxxx>");> +MODULE_ALIAS_BLOCKDEV_MAJOR(FLOPPY_MAJOR);> diff --git a/drivers/block/swim_asm.S b/drivers/block/swim_asm.S> new file mode 100644> index 0000000..c966820> --- /dev/null> +++ b/drivers/block/swim_asm.S> @@ -0,0 +1,247 @@> +/*> + * low-level functions for the SWIM floppy controller> + *> + * needs assembly language because is very timing dependent> + * this controller exists only on macintosh 680x0 based> + *> + * Copyright (C) 2004,2008 Laurent Vivier <Laurent@xxxxxxxxxxxx>> + *> + * based on Alastair Bridgewater SWIM analysis, 2001> + * based on netBSD IWM driver (c) 1997, 1998 Hauke Fath.> + *> + * 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.> + *> + * 2004-08-21 (lv) - Initial implementation> + * 2008-11-05 (lv) - add get_swim_mode> + */> +> +       .equ    write_data,     0x0000> +       .equ    write_mark,     0x0200> +       .equ    write_CRC,      0x0400> +       .equ    write_parameter,0x0600> +       .equ    write_phase,    0x0800> +       .equ    write_setup,    0x0a00> +       .equ    write_mode0,    0x0c00> +       .equ    write_mode1,    0x0e00> +       .equ    read_data,      0x1000> +       .equ    read_mark,      0x1200> +       .equ    read_error,     0x1400> +       .equ    read_parameter, 0x1600> +       .equ    read_phase,     0x1800> +       .equ    read_setup,     0x1a00> +       .equ    read_status,    0x1c00> +       .equ    read_handshake, 0x1e00> +> +       .equ    o_side, 0> +       .equ    o_track, 1> +       .equ    o_sector, 2> +       .equ    o_size, 3> +       .equ    o_crc0, 4> +       .equ    o_crc1, 5> +> +       .equ    seek_time, 30000> +       .equ    max_retry, 40> +       .equ    sector_size, 512> +> +       .global swim_read_sector_header> +swim_read_sector_header:> +       link    %a6, #0> +       moveml  %d1-%d5/%a0-%a4,%sp@-> +       movel   %a6@(0x0c), %a4> +       bsr     mfm_read_addrmark> +       moveml  %sp@+, %d1-%d5/%a0-%a4> +       unlk    %a6> +       rts> +> +sector_address_mark:> +       .byte   0xa1, 0xa1, 0xa1, 0xfe> +sector_data_mark:> +       .byte   0xa1, 0xa1, 0xa1, 0xfb> +> +mfm_read_addrmark:> +       movel   %a6@(0x08), %a3> +       lea     %a3@(read_handshake), %a2> +       lea     %a3@(read_mark), %a3> +       moveq   #-1, %d0> +       movew   #seek_time, %d2> +> +wait_header_init:> +       tstb    %a3@(read_error - read_mark)> +       moveb   #0x18, %a3@(write_mode0 - read_mark)> +       moveb   #0x01, %a3@(write_mode1 - read_mark)> +       moveb   #0x01, %a3@(write_mode0 - read_mark)> +       tstb    %a3@(read_error - read_mark)> +       moveb   #0x08, %a3@(write_mode1 - read_mark)> +> +       lea     sector_address_mark, %a0> +       moveq   #3, %d1> +> +wait_addr_mark_byte:> +> +       tstb    %a2@> +       dbmi    %d2, wait_addr_mark_byte> +       bpl     header_exit> +> +       moveb   %a3@, %d3> +       cmpb    %a0@+, %d3> +       dbne    %d1, wait_addr_mark_byte> +       bne     wait_header_init> +> +       moveq   #max_retry, %d2> +> +amark0:        tstb    %a2@> +       dbmi    %d2, amark0> +       bpl     signal_nonyb> +> +       moveb   %a3@, %a4@(o_track)> +> +       moveq   #max_retry, %d2> +> +amark1:        tstb    %a2@> +       dbmi    %d2, amark1> +       bpl     signal_nonyb> +> +       moveb   %a3@, %a4@(o_side)> +> +       moveq   #max_retry, %d2> +> +amark2:        tstb    %a2@> +       dbmi    %d2, amark2> +       bpl     signal_nonyb> +> +       moveb   %a3@, %a4@(o_sector)> +> +       moveq   #max_retry, %d2> +> +amark3:        tstb    %a2@> +       dbmi    %d2, amark3> +       bpl     signal_nonyb> +> +       moveb   %a3@, %a4@(o_size)> +> +       moveq   #max_retry, %d2> +> +crc0:  tstb    %a2@> +       dbmi    %d2, crc0> +       bpl     signal_nonyb> +> +       moveb   %a3@, %a4@(o_crc0)> +> +       moveq   #max_retry, %d2> +> +crc1:  tstb    %a2@> +       dbmi    %d2, crc1> +       bpl     signal_nonyb> +> +       moveb   %a3@, %a4@(o_crc1)> +> +       tstb    %a3@(read_error - read_mark)> +> +header_exit:> +       moveq   #0, %d0> +       moveb   #0x18, %a3@(write_mode0 - read_mark)> +       rts> +signal_nonyb:> +       moveq   #-1, %d0> +       moveb   #0x18, %a3@(write_mode0 - read_mark)> +       rts> +> +       .global swim_read_sector_data> +swim_read_sector_data:> +       link    %a6, #0> +       moveml  %d1-%d5/%a0-%a5,%sp@-> +       movel   %a6@(0x0c), %a4> +       bsr     mfm_read_data> +       moveml  %sp@+, %d1-%d5/%a0-%a5> +       unlk    %a6> +       rts> +> +mfm_read_data:> +       movel   %a6@(0x08), %a3> +       lea     %a3@(read_handshake), %a2> +       lea     %a3@(read_data), %a5> +       lea     %a3@(read_mark), %a3> +       movew   #seek_time, %d2> +> +wait_data_init:> +       tstb    %a3@(read_error - read_mark)> +       moveb   #0x18, %a3@(write_mode0 - read_mark)> +       moveb   #0x01, %a3@(write_mode1 - read_mark)> +       moveb   #0x01, %a3@(write_mode0 - read_mark)> +       tstb    %a3@(read_error - read_mark)> +       moveb   #0x08, %a3@(write_mode1 - read_mark)> +> +       lea     sector_data_mark, %a0> +       moveq   #3, %d1> +> +       /* wait data address mark */> +> +wait_data_mark_byte:> +> +       tstb    %a2@> +       dbmi    %d2, wait_data_mark_byte> +       bpl     data_exit> +> +       moveb   %a3@, %d3> +       cmpb    %a0@+, %d3> +       dbne    %d1, wait_data_mark_byte> +       bne     wait_data_init> +> +       /* read data */> +> +       tstb    %a3@(read_error - read_mark)> +> +       movel   #sector_size-1, %d4             /* sector size */> +read_new_data:> +       movew   #max_retry, %d2> +read_data_loop:> +       moveb   %a2@, %d5> +       andb    #0xc0, %d5> +       dbne    %d2, read_data_loop> +       beq     data_exit> +       moveb   %a5@, %a4@+> +       andb    #0x40, %d5> +       dbne    %d4, read_new_data> +       beq     exit_loop> +       moveb   %a5@, %a4@+> +       dbra    %d4, read_new_data> +exit_loop:> +> +       /* read CRC */> +> +       movew   #max_retry, %d2> +data_crc0:> +> +       tstb    %a2@> +       dbmi    %d2, data_crc0> +       bpl     data_exit> +> +       moveb   %a3@, %d5> +> +       moveq   #max_retry, %d2> +> +data_crc1:> +> +       tstb    %a2@> +       dbmi    %d2, data_crc1> +       bpl     data_exit> +> +       moveb   %a3@, %d5> +> +       tstb    %a3@(read_error - read_mark)> +> +       moveb   #0x18, %a3@(write_mode0 - read_mark)> +> +       /* return number of bytes read */> +> +       movel   #sector_size, %d0> +       addw    #1, %d4> +       subl    %d4, %d0> +       rts> +data_exit:> +       moveb   #0x18, %a3@(write_mode0 - read_mark)> +       moveq   #-1, %d0> +       rts> --> 1.6.1.3>>


-- Gr{oetje,eeting}s,
						Geert
--Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@xxxxxxxxxxxxxx
In personal conversations with technical people, I call myself a hacker. Butwhen I'm talking to journalists I just say "programmer" or something like that.							    -- Linus Torvalds��.n��������+%������w��{.n�����{��n����ܨ}���Ơz�j:+v�����w����ޙ��&�)ߡ�a����z�ޗ���ݢj��w�f


[Index of Archives]     [Video for Linux]     [Yosemite News]     [Linux S/390]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux