// SPDX-License-Identifier: GPL-2.0-or-later/* * Driver for SWIM (Sander Woz Integrated Machine) floppy controller * * Copyright (C) 2004,2008 Laurent Vivier <Laurent@lvivier.info> * * 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. * * 2004-08-21 (lv) - Initial implementation * 2008-10-30 (lv) - Port to 2.6 */ #include <linux/module.h>#include <linux/fd.h>#include <linux/slab.h>#include <linux/blk-mq.h>#include <linux/major.h>#include <linux/mutex.h>#include <linux/hdreg.h>#include <linux/kernel.h>#include <linux/delay.h>#include <linux/platform_device.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 ONEMEG_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 GCR_MODE 0x175#define SEEK_COMPLETE 0x176#define TWOMEG_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; bool registered; struct gendisk *disk; struct blk_mq_tag_set tag_set; /* 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; 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 DEFINE_MUTEX(swim_mutex);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; set_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; set_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++) { set_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 blk_status_t 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;Lines 1–500 of 970. Load more
// SPDX-License-Identifier: GPL-2.0-or-later /* * Driver for SWIM (Sander Woz Integrated Machine) floppy controller * * Copyright (C) 2004,2008 Laurent Vivier <Laurent@lvivier.info> * * 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. * * 2004-08-21 (lv) - Initial implementation * 2008-10-30 (lv) - Port to 2.6 */ #include <linux/module.h> #include <linux/fd.h> #include <linux/slab.h> #include <linux/blk-mq.h> #include <linux/major.h> #include <linux/mutex.h> #include <linux/hdreg.h> #include <linux/kernel.h> #include <linux/delay.h> #include <linux/platform_device.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 ONEMEG_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 GCR_MODE 0x175 #define SEEK_COMPLETE 0x176 #define TWOMEG_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; bool registered; struct gendisk *disk; struct blk_mq_tag_set tag_set; /* 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; 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 DEFINE_MUTEX(swim_mutex); 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; set_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; set_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++) { set_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 blk_status_t 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;