// SPDX-License-Identifier: GPL-2.0-only/* * drivers/block/ataflop.c * * Copyright (C) 1993 Greg Harp * Atari Support by Bjoern Brauel, Roman Hodek * * Big cleanup Sep 11..14 1994 Roman Hodek: * - Driver now works interrupt driven * - Support for two drives; should work, but I cannot test that :-( * - Reading is done in whole tracks and buffered to speed up things * - Disk change detection and drive deselecting after motor-off * similar to TOS * - Autodetection of disk format (DD/HD); untested yet, because I * don't have an HD drive :-( * * Fixes Nov 13 1994 Martin Schaller: * - Autodetection works now * - Support for 5 1/4'' disks * - Removed drive type (unknown on atari) * - Do seeks with 8 Mhz * * Changes by Andreas Schwab: * - After errors in multiple read mode try again reading single sectors * (Feb 1995): * - Clean up error handling * - Set blk_size for proper size checking * - Initialize track register when testing presence of floppy * - Implement some ioctl's * * Changes by Torsten Lang: * - When probing the floppies we should add the FDCCMDADD_H flag since * the FDC will otherwise wait forever when no disk is inserted... * * ++ Freddi Aschwanden (fa) 20.9.95 fixes for medusa: * - MFPDELAY() after each FDC access -> atari * - more/other disk formats * - DMA to the block buffer directly if we have a 32bit DMA * - for medusa, the step rate is always 3ms * - on medusa, use only cache_push() * Roman: * - Make disk format numbering independent from minors * - Let user set max. supported drive type (speeds up format * detection, saves buffer space) * * Roman 10/15/95: * - implement some more ioctls * - disk formatting * * Andreas 95/12/12: * - increase gap size at start of track for HD/ED disks * * Michael (MSch) 11/07/96: * - implemented FDSETPRM and FDDEFPRM ioctl * * Andreas (97/03/19): * - implemented missing BLK* ioctls * * Things left to do: * - Formatting * - Maybe a better strategy for disk change detection (does anyone * know one?) */ #include <linux/module.h> #include <linux/fd.h>#include <linux/delay.h>#include <linux/init.h>#include <linux/blk-mq.h>#include <linux/major.h>#include <linux/mutex.h>#include <linux/completion.h>#include <linux/wait.h> #include <asm/atariints.h>#include <asm/atari_stdma.h>#include <asm/atari_stram.h> #define FD_MAX_UNITS 2 #undef DEBUG static DEFINE_MUTEX(ataflop_mutex);static struct request *fd_request; /* * WD1772 stuff */ /* register codes */ #define FDCSELREG_STP (0x80) /* command/status register */#define FDCSELREG_TRA (0x82) /* track register */#define FDCSELREG_SEC (0x84) /* sector register */#define FDCSELREG_DTA (0x86) /* data register */ /* register names for FDC_READ/WRITE macros */ #define FDCREG_CMD 0#define FDCREG_STATUS 0#define FDCREG_TRACK 2#define FDCREG_SECTOR 4#define FDCREG_DATA 6 /* command opcodes */ #define FDCCMD_RESTORE (0x00) /* - */#define FDCCMD_SEEK (0x10) /* | */#define FDCCMD_STEP (0x20) /* | TYP 1 Commands */#define FDCCMD_STIN (0x40) /* | */#define FDCCMD_STOT (0x60) /* - */#define FDCCMD_RDSEC (0x80) /* - TYP 2 Commands */#define FDCCMD_WRSEC (0xa0) /* - " */#define FDCCMD_RDADR (0xc0) /* - */#define FDCCMD_RDTRA (0xe0) /* | TYP 3 Commands */#define FDCCMD_WRTRA (0xf0) /* - */#define FDCCMD_FORCI (0xd0) /* - TYP 4 Command */ /* command modifier bits */ #define FDCCMDADD_SR6 (0x00) /* step rate settings */#define FDCCMDADD_SR12 (0x01)#define FDCCMDADD_SR2 (0x02)#define FDCCMDADD_SR3 (0x03)#define FDCCMDADD_V (0x04) /* verify */#define FDCCMDADD_H (0x08) /* wait for spin-up */#define FDCCMDADD_U (0x10) /* update track register */#define FDCCMDADD_M (0x10) /* multiple sector access */#define FDCCMDADD_E (0x04) /* head settling flag */#define FDCCMDADD_P (0x02) /* precompensation off */#define FDCCMDADD_A0 (0x01) /* DAM flag */ /* status register bits */ #define FDCSTAT_MOTORON (0x80) /* motor on */#define FDCSTAT_WPROT (0x40) /* write protected (FDCCMD_WR*) */#define FDCSTAT_SPINUP (0x20) /* motor speed stable (Type I) */#define FDCSTAT_DELDAM (0x20) /* sector has deleted DAM (Type II+III) */#define FDCSTAT_RECNF (0x10) /* record not found */#define FDCSTAT_CRC (0x08) /* CRC error */#define FDCSTAT_TR00 (0x04) /* Track 00 flag (Type I) */#define FDCSTAT_LOST (0x04) /* Lost Data (Type II+III) */#define FDCSTAT_IDX (0x02) /* Index status (Type I) */#define FDCSTAT_DRQ (0x02) /* DRQ status (Type II+III) */#define FDCSTAT_BUSY (0x01) /* FDC is busy */ /* PSG Port A Bit Nr 0 .. Side Sel .. 0 -> Side 1 1 -> Side 2 */#define DSKSIDE (0x01) #define DSKDRVNONE (0x06)#define DSKDRV0 (0x02)#define DSKDRV1 (0x04) /* step rates */#define FDCSTEP_6 0x00#define FDCSTEP_12 0x01#define FDCSTEP_2 0x02#define FDCSTEP_3 0x03 struct atari_format_descr { int track; /* to be formatted */ int head; /* "" "" */ int sect_offset; /* offset of first sector */}; /* Disk types: DD, HD, ED */static struct atari_disk_type { const char *name; unsigned spt; /* sectors per track */ unsigned blocks; /* total number of blocks */ unsigned fdc_speed; /* fdc_speed setting */ unsigned stretch; /* track doubling ? */} atari_disk_type[] = { { "d360", 9, 720, 0, 0}, /* 0: 360kB diskette */ { "D360", 9, 720, 0, 1}, /* 1: 360kb in 720k or 1.2MB drive */ { "D720", 9,1440, 0, 0}, /* 2: 720kb in 720k or 1.2MB drive */ { "D820", 10,1640, 0, 0}, /* 3: DD disk with 82 tracks/10 sectors *//* formats above are probed for type DD */#define MAX_TYPE_DD 3 { "h1200",15,2400, 3, 0}, /* 4: 1.2MB diskette */ { "H1440",18,2880, 3, 0}, /* 5: 1.4 MB diskette (HD) */ { "H1640",20,3280, 3, 0}, /* 6: 1.64MB diskette (fat HD) 82 tr 20 sec *//* formats above are probed for types DD and HD */#define MAX_TYPE_HD 6 { "E2880",36,5760, 3, 0}, /* 7: 2.8 MB diskette (ED) */ { "E3280",40,6560, 3, 0}, /* 8: 3.2 MB diskette (fat ED) 82 tr 40 sec *//* formats above are probed for types DD, HD and ED */#define MAX_TYPE_ED 8/* types below are never autoprobed */ { "H1680",21,3360, 3, 0}, /* 9: 1.68MB diskette (fat HD) 80 tr 21 sec */ { "h410",10,820, 0, 1}, /* 10: 410k diskette 41 tr 10 sec, stretch */ { "h1476",18,2952, 3, 0}, /* 11: 1.48MB diskette 82 tr 18 sec */ { "H1722",21,3444, 3, 0}, /* 12: 1.72MB diskette 82 tr 21 sec */ { "h420",10,840, 0, 1}, /* 13: 420k diskette 42 tr 10 sec, stretch */ { "H830",10,1660, 0, 0}, /* 14: 820k diskette 83 tr 10 sec */ { "h1494",18,2952, 3, 0}, /* 15: 1.49MB diskette 83 tr 18 sec */ { "H1743",21,3486, 3, 0}, /* 16: 1.74MB diskette 83 tr 21 sec */ { "h880",11,1760, 0, 0}, /* 17: 880k diskette 80 tr 11 sec */ { "D1040",13,2080, 0, 0}, /* 18: 1.04MB diskette 80 tr 13 sec */ { "D1120",14,2240, 0, 0}, /* 19: 1.12MB diskette 80 tr 14 sec */ { "h1600",20,3200, 3, 0}, /* 20: 1.60MB diskette 80 tr 20 sec */ { "H1760",22,3520, 3, 0}, /* 21: 1.76MB diskette 80 tr 22 sec */ { "H1920",24,3840, 3, 0}, /* 22: 1.92MB diskette 80 tr 24 sec */ { "E3200",40,6400, 3, 0}, /* 23: 3.2MB diskette 80 tr 40 sec */ { "E3520",44,7040, 3, 0}, /* 24: 3.52MB diskette 80 tr 44 sec */ { "E3840",48,7680, 3, 0}, /* 25: 3.84MB diskette 80 tr 48 sec */ { "H1840",23,3680, 3, 0}, /* 26: 1.84MB diskette 80 tr 23 sec */ { "D800",10,1600, 0, 0}, /* 27: 800k diskette 80 tr 10 sec */}; static int StartDiskType[] = { MAX_TYPE_DD, MAX_TYPE_HD, MAX_TYPE_ED}; #define TYPE_DD 0#define TYPE_HD 1#define TYPE_ED 2 static int DriveType = TYPE_HD; static DEFINE_SPINLOCK(ataflop_lock); /* Array for translating minors into disk formats */static struct { int index; unsigned drive_types;} minor2disktype[] = { { 0, TYPE_DD }, /* 1: d360 */ { 4, TYPE_HD }, /* 2: h1200 */ { 1, TYPE_DD }, /* 3: D360 */ { 2, TYPE_DD }, /* 4: D720 */ { 1, TYPE_DD }, /* 5: h360 = D360 */ { 2, TYPE_DD }, /* 6: h720 = D720 */ { 5, TYPE_HD }, /* 7: H1440 */ { 7, TYPE_ED }, /* 8: E2880 *//* some PC formats :-) */ { 8, TYPE_ED }, /* 9: E3280 <- was "CompaQ" == E2880 for PC */ { 5, TYPE_HD }, /* 10: h1440 = H1440 */ { 9, TYPE_HD }, /* 11: H1680 */ { 10, TYPE_DD }, /* 12: h410 */ { 3, TYPE_DD }, /* 13: H820 <- == D820, 82x10 */ { 11, TYPE_HD }, /* 14: h1476 */ { 12, TYPE_HD }, /* 15: H1722 */ { 13, TYPE_DD }, /* 16: h420 */ { 14, TYPE_DD }, /* 17: H830 */ { 15, TYPE_HD }, /* 18: h1494 */ { 16, TYPE_HD }, /* 19: H1743 */ { 17, TYPE_DD }, /* 20: h880 */ { 18, TYPE_DD }, /* 21: D1040 */ { 19, TYPE_DD }, /* 22: D1120 */ { 20, TYPE_HD }, /* 23: h1600 */ { 21, TYPE_HD }, /* 24: H1760 */ { 22, TYPE_HD }, /* 25: H1920 */ { 23, TYPE_ED }, /* 26: E3200 */ { 24, TYPE_ED }, /* 27: E3520 */ { 25, TYPE_ED }, /* 28: E3840 */ { 26, TYPE_HD }, /* 29: H1840 */ { 27, TYPE_DD }, /* 30: D800 */ { 6, TYPE_HD }, /* 31: H1640 <- was H1600 == h1600 for PC */}; #define NUM_DISK_MINORS ARRAY_SIZE(minor2disktype) /* * Maximum disk size (in kilobytes). This default is used whenever the * current disk size is unknown. */#define MAX_DISK_SIZE 3280 /* * MSch: User-provided type information. 'drive' points to * the respective entry of this array. Set by FDSETPRM ioctls. */static struct atari_disk_type user_params[FD_MAX_UNITS]; /* * User-provided permanent type information. 'drive' points to * the respective entry of this array. Set by FDDEFPRM ioctls, * restored upon disk change by floppy_revalidate() if valid (as seen by * default_params[].blocks > 0 - a bit in unit[].flags might be used for this?) */static struct atari_disk_type default_params[FD_MAX_UNITS]; /* current info on each unit */static struct atari_floppy_struct { int connected; /* !=0 : drive is connected */ int autoprobe; /* !=0 : do autoprobe */ struct atari_disk_type *disktype; /* current type of disk */ int track; /* current head position or -1 if unknown */ unsigned int steprate; /* steprate setting */ unsigned int wpstat; /* current state of WP signal (for disk change detection) */ int flags; /* flags */ struct gendisk *disk[NUM_DISK_MINORS]; bool registered[NUM_DISK_MINORS]; int ref; int type; struct blk_mq_tag_set tag_set; int error_count;} unit[FD_MAX_UNITS]; #define UD unit[drive]#define UDT unit[drive].disktype#define SUD unit[SelectedDrive]#define SUDT unit[SelectedDrive].disktype #define FDC_READ(reg) ({ \ /* unsigned long __flags; */ \ unsigned short __val; \ /* local_irq_save(__flags); */ \ dma_wd.dma_mode_status = 0x80 | (reg); \ udelay(25); \ __val = dma_wd.fdc_acces_seccount; \ MFPDELAY(); \ /* local_irq_restore(__flags); */ \ __val & 0xff; \}) #define FDC_WRITE(reg,val) \ do { \ /* unsigned long __flags; */ \ /* local_irq_save(__flags); */ \ dma_wd.dma_mode_status = 0x80 | (reg); \ udelay(25); \ dma_wd.fdc_acces_seccount = (val); \ MFPDELAY(); \ /* local_irq_restore(__flags); */ \ } while(0) /* Buffering variables: * First, there is a DMA buffer in ST-RAM that is used for floppy DMA * operations. Second, a track buffer is used to cache a whole track * of the disk to save read operations. These are two separate buffers * because that allows write operations without clearing the track buffer. */ static int MaxSectors[] = { 11, 22, 44};static int BufferSize[] = { 15*512, 30*512, 60*512}; #define BUFFER_SIZE (BufferSize[DriveType]) unsigned char *DMABuffer; /* buffer for writes */static unsigned long PhysDMABuffer; /* physical address */ static int UseTrackbuffer = -1; /* Do track buffering? */module_param(UseTrackbuffer, int, 0); unsigned char *TrackBuffer; /* buffer for reads */static unsigned long PhysTrackBuffer; /* physical address */static int BufferDrive, BufferSide, BufferTrack;static int read_track; /* non-zero if we are reading whole tracks */ #define SECTOR_BUFFER(sec) (TrackBuffer + ((sec)-1)*512)#define IS_BUFFERED(drive,side,track) \ (BufferDrive == (drive) && BufferSide == (side) && BufferTrack == (track)) /* * These are global variables, as that's the easiest way to give * information to interrupts. They are the data used for the current * request. */static int SelectedDrive = 0;static int ReqCmd, ReqBlock;static int ReqSide, ReqTrack, ReqSector, ReqCnt;static int HeadSettleFlag = 0;static unsigned char *ReqData, *ReqBuffer;static int MotorOn = 0, MotorOffTrys;static int IsFormatting = 0, FormatError; static int UserSteprate[FD_MAX_UNITS] = { -1, -1 };module_param_array(UserSteprate, int, NULL, 0); static DECLARE_COMPLETION(format_wait); static unsigned long changed_floppies = 0xff, fake_change = 0;#define CHECK_CHANGE_DELAY HZ/2 #define FD_MOTOR_OFF_DELAY (3*HZ)#define FD_MOTOR_OFF_MAXTRY (10*20) #define FLOPPY_TIMEOUT (6*HZ)#define RECALIBRATE_ERRORS 4 /* After this many errors the drive * will be recalibrated. */#define MAX_ERRORS 8 /* After this many errors the driver * will give up. */ /* * The driver is trying to determine the correct media format * while Probing is set. fd_rwsec_done() clears it after a * successful access. */static int Probing = 0; /* This flag is set when a dummy seek is necessary to make the WP * status bit accessible. */static int NeedSeek = 0; #ifdef DEBUG#define DPRINT(a) printk a#else#define DPRINT(a)#endif /***************************** Prototypes *****************************/ static void fd_select_side( int side );static void fd_select_drive( int drive );static void fd_deselect( void );static void fd_motor_off_timer(struct timer_list *unused);static void check_change(struct timer_list *unused);static irqreturn_t floppy_irq (int irq, void *dummy);static void fd_error( void );static int do_format(int drive, int type, struct atari_format_descr *desc);static void do_fd_action( int drive );static void fd_calibrate( void );static void fd_calibrate_done( int status );static void fd_seek( void );static void fd_seek_done( int status );static void fd_rwsec( void );static void fd_readtrack_check(struct timer_list *unused);static void fd_rwsec_done( int status );static void fd_rwsec_done1(int status);static void fd_writetrack( void );static void fd_writetrack_done( int status );static void fd_times_out(struct timer_list *unused);static void finish_fdc( void );static void finish_fdc_done( int dummy );static void setup_req_params( int drive );static int fd_locked_ioctl(struct block_device *bdev, blk_mode_t mode, unsigned int cmd, unsigned long param);static void fd_probe( int drive );static int fd_test_drive_present( int drive );static void config_types( void );static int floppy_open(struct gendisk *disk, blk_mode_t mode);static void floppy_release(struct gendisk *disk); /************************* End of Prototypes **************************/ static DEFINE_TIMER(motor_off_timer, fd_motor_off_timer);static DEFINE_TIMER(readtrack_timer, fd_readtrack_check);static DEFINE_TIMER(timeout_timer, fd_times_out);static DEFINE_TIMER(fd_timer, check_change); static void fd_end_request_cur(blk_status_t err){ DPRINT(("fd_end_request_cur(), bytes %d of %d\n", blk_rq_cur_bytes(fd_request), blk_rq_bytes(fd_request))); if (!blk_update_request(fd_request, err, blk_rq_cur_bytes(fd_request))) { DPRINT(("calling __blk_mq_end_request()\n")); __blk_mq_end_request(fd_request, err); fd_request = NULL; } else { /* requeue rest of request */ DPRINT(("calling blk_mq_requeue_request()\n")); blk_mq_requeue_request(fd_request, true); fd_request = NULL; }} static inline void start_motor_off_timer(void){ mod_timer(&motor_off_timer, jiffies + FD_MOTOR_OFF_DELAY); MotorOffTrys = 0;} static inline void start_check_change_timer( void ){ mod_timer(&fd_timer, jiffies + CHECK_CHANGE_DELAY);} static inline void start_timeout(void){ mod_timer(&timeout_timer, jiffies + FLOPPY_TIMEOUT);} static inline void stop_timeout(void){ timer_delete(&timeout_timer);} /* Select the side to use. */Lines 1–500 of 2204. Load more
// SPDX-License-Identifier: GPL-2.0-only /* * drivers/block/ataflop.c * * Copyright (C) 1993 Greg Harp * Atari Support by Bjoern Brauel, Roman Hodek * * Big cleanup Sep 11..14 1994 Roman Hodek: * - Driver now works interrupt driven * - Support for two drives; should work, but I cannot test that :-( * - Reading is done in whole tracks and buffered to speed up things * - Disk change detection and drive deselecting after motor-off * similar to TOS * - Autodetection of disk format (DD/HD); untested yet, because I * don't have an HD drive :-( * * Fixes Nov 13 1994 Martin Schaller: * - Autodetection works now * - Support for 5 1/4'' disks * - Removed drive type (unknown on atari) * - Do seeks with 8 Mhz * * Changes by Andreas Schwab: * - After errors in multiple read mode try again reading single sectors * (Feb 1995): * - Clean up error handling * - Set blk_size for proper size checking * - Initialize track register when testing presence of floppy * - Implement some ioctl's * * Changes by Torsten Lang: * - When probing the floppies we should add the FDCCMDADD_H flag since * the FDC will otherwise wait forever when no disk is inserted... * * ++ Freddi Aschwanden (fa) 20.9.95 fixes for medusa: * - MFPDELAY() after each FDC access -> atari * - more/other disk formats * - DMA to the block buffer directly if we have a 32bit DMA * - for medusa, the step rate is always 3ms * - on medusa, use only cache_push() * Roman: * - Make disk format numbering independent from minors * - Let user set max. supported drive type (speeds up format * detection, saves buffer space) * * Roman 10/15/95: * - implement some more ioctls * - disk formatting * * Andreas 95/12/12: * - increase gap size at start of track for HD/ED disks * * Michael (MSch) 11/07/96: * - implemented FDSETPRM and FDDEFPRM ioctl * * Andreas (97/03/19): * - implemented missing BLK* ioctls * * Things left to do: * - Formatting * - Maybe a better strategy for disk change detection (does anyone * know one?) */ #include <linux/module.h> #include <linux/fd.h> #include <linux/delay.h> #include <linux/init.h> #include <linux/blk-mq.h> #include <linux/major.h> #include <linux/mutex.h> #include <linux/completion.h> #include <linux/wait.h> #include <asm/atariints.h> #include <asm/atari_stdma.h> #include <asm/atari_stram.h> #define FD_MAX_UNITS 2 #undef DEBUG static DEFINE_MUTEX(ataflop_mutex); static struct request *fd_request; /* * WD1772 stuff */ /* register codes */ #define FDCSELREG_STP (0x80) /* command/status register */ #define FDCSELREG_TRA (0x82) /* track register */ #define FDCSELREG_SEC (0x84) /* sector register */ #define FDCSELREG_DTA (0x86) /* data register */ /* register names for FDC_READ/WRITE macros */ #define FDCREG_CMD 0 #define FDCREG_STATUS 0 #define FDCREG_TRACK 2 #define FDCREG_SECTOR 4 #define FDCREG_DATA 6 /* command opcodes */ #define FDCCMD_RESTORE (0x00) /* - */ #define FDCCMD_SEEK (0x10) /* | */ #define FDCCMD_STEP (0x20) /* | TYP 1 Commands */ #define FDCCMD_STIN (0x40) /* | */ #define FDCCMD_STOT (0x60) /* - */ #define FDCCMD_RDSEC (0x80) /* - TYP 2 Commands */ #define FDCCMD_WRSEC (0xa0) /* - " */ #define FDCCMD_RDADR (0xc0) /* - */ #define FDCCMD_RDTRA (0xe0) /* | TYP 3 Commands */ #define FDCCMD_WRTRA (0xf0) /* - */ #define FDCCMD_FORCI (0xd0) /* - TYP 4 Command */ /* command modifier bits */ #define FDCCMDADD_SR6 (0x00) /* step rate settings */ #define FDCCMDADD_SR12 (0x01) #define FDCCMDADD_SR2 (0x02) #define FDCCMDADD_SR3 (0x03) #define FDCCMDADD_V (0x04) /* verify */ #define FDCCMDADD_H (0x08) /* wait for spin-up */ #define FDCCMDADD_U (0x10) /* update track register */ #define FDCCMDADD_M (0x10) /* multiple sector access */ #define FDCCMDADD_E (0x04) /* head settling flag */ #define FDCCMDADD_P (0x02) /* precompensation off */ #define FDCCMDADD_A0 (0x01) /* DAM flag */ /* status register bits */ #define FDCSTAT_MOTORON (0x80) /* motor on */ #define FDCSTAT_WPROT (0x40) /* write protected (FDCCMD_WR*) */ #define FDCSTAT_SPINUP (0x20) /* motor speed stable (Type I) */ #define FDCSTAT_DELDAM (0x20) /* sector has deleted DAM (Type II+III) */ #define FDCSTAT_RECNF (0x10) /* record not found */ #define FDCSTAT_CRC (0x08) /* CRC error */ #define FDCSTAT_TR00 (0x04) /* Track 00 flag (Type I) */ #define FDCSTAT_LOST (0x04) /* Lost Data (Type II+III) */ #define FDCSTAT_IDX (0x02) /* Index status (Type I) */ #define FDCSTAT_DRQ (0x02) /* DRQ status (Type II+III) */ #define FDCSTAT_BUSY (0x01) /* FDC is busy */ /* PSG Port A Bit Nr 0 .. Side Sel .. 0 -> Side 1 1 -> Side 2 */ #define DSKSIDE (0x01) #define DSKDRVNONE (0x06) #define DSKDRV0 (0x02) #define DSKDRV1 (0x04) /* step rates */ #define FDCSTEP_6 0x00 #define FDCSTEP_12 0x01 #define FDCSTEP_2 0x02 #define FDCSTEP_3 0x03 struct atari_format_descr { int track; /* to be formatted */ int head; /* "" "" */ int sect_offset; /* offset of first sector */ }; /* Disk types: DD, HD, ED */ static struct atari_disk_type { const char *name; unsigned spt; /* sectors per track */ unsigned blocks; /* total number of blocks */ unsigned fdc_speed; /* fdc_speed setting */ unsigned stretch; /* track doubling ? */ } atari_disk_type[] = { { "d360", 9, 720, 0, 0}, /* 0: 360kB diskette */ { "D360", 9, 720, 0, 1}, /* 1: 360kb in 720k or 1.2MB drive */ { "D720", 9,1440, 0, 0}, /* 2: 720kb in 720k or 1.2MB drive */ { "D820", 10,1640, 0, 0}, /* 3: DD disk with 82 tracks/10 sectors */ /* formats above are probed for type DD */ #define MAX_TYPE_DD 3 { "h1200",15,2400, 3, 0}, /* 4: 1.2MB diskette */ { "H1440",18,2880, 3, 0}, /* 5: 1.4 MB diskette (HD) */ { "H1640",20,3280, 3, 0}, /* 6: 1.64MB diskette (fat HD) 82 tr 20 sec */ /* formats above are probed for types DD and HD */ #define MAX_TYPE_HD 6 { "E2880",36,5760, 3, 0}, /* 7: 2.8 MB diskette (ED) */ { "E3280",40,6560, 3, 0}, /* 8: 3.2 MB diskette (fat ED) 82 tr 40 sec */ /* formats above are probed for types DD, HD and ED */ #define MAX_TYPE_ED 8 /* types below are never autoprobed */ { "H1680",21,3360, 3, 0}, /* 9: 1.68MB diskette (fat HD) 80 tr 21 sec */ { "h410",10,820, 0, 1}, /* 10: 410k diskette 41 tr 10 sec, stretch */ { "h1476",18,2952, 3, 0}, /* 11: 1.48MB diskette 82 tr 18 sec */ { "H1722",21,3444, 3, 0}, /* 12: 1.72MB diskette 82 tr 21 sec */ { "h420",10,840, 0, 1}, /* 13: 420k diskette 42 tr 10 sec, stretch */ { "H830",10,1660, 0, 0}, /* 14: 820k diskette 83 tr 10 sec */ { "h1494",18,2952, 3, 0}, /* 15: 1.49MB diskette 83 tr 18 sec */ { "H1743",21,3486, 3, 0}, /* 16: 1.74MB diskette 83 tr 21 sec */ { "h880",11,1760, 0, 0}, /* 17: 880k diskette 80 tr 11 sec */ { "D1040",13,2080, 0, 0}, /* 18: 1.04MB diskette 80 tr 13 sec */ { "D1120",14,2240, 0, 0}, /* 19: 1.12MB diskette 80 tr 14 sec */ { "h1600",20,3200, 3, 0}, /* 20: 1.60MB diskette 80 tr 20 sec */ { "H1760",22,3520, 3, 0}, /* 21: 1.76MB diskette 80 tr 22 sec */ { "H1920",24,3840, 3, 0}, /* 22: 1.92MB diskette 80 tr 24 sec */ { "E3200",40,6400, 3, 0}, /* 23: 3.2MB diskette 80 tr 40 sec */ { "E3520",44,7040, 3, 0}, /* 24: 3.52MB diskette 80 tr 44 sec */ { "E3840",48,7680, 3, 0}, /* 25: 3.84MB diskette 80 tr 48 sec */ { "H1840",23,3680, 3, 0}, /* 26: 1.84MB diskette 80 tr 23 sec */ { "D800",10,1600, 0, 0}, /* 27: 800k diskette 80 tr 10 sec */ }; static int StartDiskType[] = { MAX_TYPE_DD, MAX_TYPE_HD, MAX_TYPE_ED }; #define TYPE_DD 0 #define TYPE_HD 1 #define TYPE_ED 2 static int DriveType = TYPE_HD; static DEFINE_SPINLOCK(ataflop_lock); /* Array for translating minors into disk formats */ static struct { int index; unsigned drive_types; } minor2disktype[] = { { 0, TYPE_DD }, /* 1: d360 */ { 4, TYPE_HD }, /* 2: h1200 */ { 1, TYPE_DD }, /* 3: D360 */ { 2, TYPE_DD }, /* 4: D720 */ { 1, TYPE_DD }, /* 5: h360 = D360 */ { 2, TYPE_DD }, /* 6: h720 = D720 */ { 5, TYPE_HD }, /* 7: H1440 */ { 7, TYPE_ED }, /* 8: E2880 */ /* some PC formats :-) */ { 8, TYPE_ED }, /* 9: E3280 <- was "CompaQ" == E2880 for PC */ { 5, TYPE_HD }, /* 10: h1440 = H1440 */ { 9, TYPE_HD }, /* 11: H1680 */ { 10, TYPE_DD }, /* 12: h410 */ { 3, TYPE_DD }, /* 13: H820 <- == D820, 82x10 */ { 11, TYPE_HD }, /* 14: h1476 */ { 12, TYPE_HD }, /* 15: H1722 */ { 13, TYPE_DD }, /* 16: h420 */ { 14, TYPE_DD }, /* 17: H830 */ { 15, TYPE_HD }, /* 18: h1494 */ { 16, TYPE_HD }, /* 19: H1743 */ { 17, TYPE_DD }, /* 20: h880 */ { 18, TYPE_DD }, /* 21: D1040 */ { 19, TYPE_DD }, /* 22: D1120 */ { 20, TYPE_HD }, /* 23: h1600 */ { 21, TYPE_HD }, /* 24: H1760 */ { 22, TYPE_HD }, /* 25: H1920 */ { 23, TYPE_ED }, /* 26: E3200 */ { 24, TYPE_ED }, /* 27: E3520 */ { 25, TYPE_ED }, /* 28: E3840 */ { 26, TYPE_HD }, /* 29: H1840 */ { 27, TYPE_DD }, /* 30: D800 */ { 6, TYPE_HD }, /* 31: H1640 <- was H1600 == h1600 for PC */ }; #define NUM_DISK_MINORS ARRAY_SIZE(minor2disktype) /* * Maximum disk size (in kilobytes). This default is used whenever the * current disk size is unknown. */ #define MAX_DISK_SIZE 3280 /* * MSch: User-provided type information. 'drive' points to * the respective entry of this array. Set by FDSETPRM ioctls. */ static struct atari_disk_type user_params[FD_MAX_UNITS]; /* * User-provided permanent type information. 'drive' points to * the respective entry of this array. Set by FDDEFPRM ioctls, * restored upon disk change by floppy_revalidate() if valid (as seen by * default_params[].blocks > 0 - a bit in unit[].flags might be used for this?) */ static struct atari_disk_type default_params[FD_MAX_UNITS]; /* current info on each unit */ static struct atari_floppy_struct { int connected; /* !=0 : drive is connected */ int autoprobe; /* !=0 : do autoprobe */ struct atari_disk_type *disktype; /* current type of disk */ int track; /* current head position or -1 if unknown */ unsigned int steprate; /* steprate setting */ unsigned int wpstat; /* current state of WP signal (for disk change detection) */ int flags; /* flags */ struct gendisk *disk[NUM_DISK_MINORS]; bool registered[NUM_DISK_MINORS]; int ref; int type; struct blk_mq_tag_set tag_set; int error_count; } unit[FD_MAX_UNITS]; #define UD unit[drive] #define UDT unit[drive].disktype #define SUD unit[SelectedDrive] #define SUDT unit[SelectedDrive].disktype #define FDC_READ(reg) ({ \ /* unsigned long __flags; */ \ unsigned short __val; \ /* local_irq_save(__flags); */ \ dma_wd.dma_mode_status = 0x80 | (reg); \ udelay(25); \ __val = dma_wd.fdc_acces_seccount; \ MFPDELAY(); \ /* local_irq_restore(__flags); */ \ __val & 0xff; \ }) #define FDC_WRITE(reg,val) \ do { \ /* unsigned long __flags; */ \ /* local_irq_save(__flags); */ \ dma_wd.dma_mode_status = 0x80 | (reg); \ udelay(25); \ dma_wd.fdc_acces_seccount = (val); \ MFPDELAY(); \ /* local_irq_restore(__flags); */ \ } while(0) /* Buffering variables: * First, there is a DMA buffer in ST-RAM that is used for floppy DMA * operations. Second, a track buffer is used to cache a whole track * of the disk to save read operations. These are two separate buffers * because that allows write operations without clearing the track buffer. */ static int MaxSectors[] = { 11, 22, 44 }; static int BufferSize[] = { 15*512, 30*512, 60*512 }; #define BUFFER_SIZE (BufferSize[DriveType]) unsigned char *DMABuffer; /* buffer for writes */ static unsigned long PhysDMABuffer; /* physical address */ static int UseTrackbuffer = -1; /* Do track buffering? */ module_param(UseTrackbuffer, int, 0); unsigned char *TrackBuffer; /* buffer for reads */ static unsigned long PhysTrackBuffer; /* physical address */ static int BufferDrive, BufferSide, BufferTrack; static int read_track; /* non-zero if we are reading whole tracks */ #define SECTOR_BUFFER(sec) (TrackBuffer + ((sec)-1)*512) #define IS_BUFFERED(drive,side,track) \ (BufferDrive == (drive) && BufferSide == (side) && BufferTrack == (track)) /* * These are global variables, as that's the easiest way to give * information to interrupts. They are the data used for the current * request. */ static int SelectedDrive = 0; static int ReqCmd, ReqBlock; static int ReqSide, ReqTrack, ReqSector, ReqCnt; static int HeadSettleFlag = 0; static unsigned char *ReqData, *ReqBuffer; static int MotorOn = 0, MotorOffTrys; static int IsFormatting = 0, FormatError; static int UserSteprate[FD_MAX_UNITS] = { -1, -1 }; module_param_array(UserSteprate, int, NULL, 0); static DECLARE_COMPLETION(format_wait); static unsigned long changed_floppies = 0xff, fake_change = 0; #define CHECK_CHANGE_DELAY HZ/2 #define FD_MOTOR_OFF_DELAY (3*HZ) #define FD_MOTOR_OFF_MAXTRY (10*20) #define FLOPPY_TIMEOUT (6*HZ) #define RECALIBRATE_ERRORS 4 /* After this many errors the drive * will be recalibrated. */ #define MAX_ERRORS 8 /* After this many errors the driver * will give up. */ /* * The driver is trying to determine the correct media format * while Probing is set. fd_rwsec_done() clears it after a * successful access. */ static int Probing = 0; /* This flag is set when a dummy seek is necessary to make the WP * status bit accessible. */ static int NeedSeek = 0; #ifdef DEBUG #define DPRINT(a) printk a #else #define DPRINT(a) #endif /***************************** Prototypes *****************************/ static void fd_select_side( int side ); static void fd_select_drive( int drive ); static void fd_deselect( void ); static void fd_motor_off_timer(struct timer_list *unused); static void check_change(struct timer_list *unused); static irqreturn_t floppy_irq (int irq, void *dummy); static void fd_error( void ); static int do_format(int drive, int type, struct atari_format_descr *desc); static void do_fd_action( int drive ); static void fd_calibrate( void ); static void fd_calibrate_done( int status ); static void fd_seek( void ); static void fd_seek_done( int status ); static void fd_rwsec( void ); static void fd_readtrack_check(struct timer_list *unused); static void fd_rwsec_done( int status ); static void fd_rwsec_done1(int status); static void fd_writetrack( void ); static void fd_writetrack_done( int status ); static void fd_times_out(struct timer_list *unused); static void finish_fdc( void ); static void finish_fdc_done( int dummy ); static void setup_req_params( int drive ); static int fd_locked_ioctl(struct block_device *bdev, blk_mode_t mode, unsigned int cmd, unsigned long param); static void fd_probe( int drive ); static int fd_test_drive_present( int drive ); static void config_types( void ); static int floppy_open(struct gendisk *disk, blk_mode_t mode); static void floppy_release(struct gendisk *disk); /************************* End of Prototypes **************************/ static DEFINE_TIMER(motor_off_timer, fd_motor_off_timer); static DEFINE_TIMER(readtrack_timer, fd_readtrack_check); static DEFINE_TIMER(timeout_timer, fd_times_out); static DEFINE_TIMER(fd_timer, check_change); static void fd_end_request_cur(blk_status_t err) { DPRINT(("fd_end_request_cur(), bytes %d of %d\n", blk_rq_cur_bytes(fd_request), blk_rq_bytes(fd_request))); if (!blk_update_request(fd_request, err, blk_rq_cur_bytes(fd_request))) { DPRINT(("calling __blk_mq_end_request()\n")); __blk_mq_end_request(fd_request, err); fd_request = NULL; } else { /* requeue rest of request */ DPRINT(("calling blk_mq_requeue_request()\n")); blk_mq_requeue_request(fd_request, true); fd_request = NULL; } } static inline void start_motor_off_timer(void) { mod_timer(&motor_off_timer, jiffies + FD_MOTOR_OFF_DELAY); MotorOffTrys = 0; } static inline void start_check_change_timer( void ) { mod_timer(&fd_timer, jiffies + CHECK_CHANGE_DELAY); } static inline void start_timeout(void) { mod_timer(&timeout_timer, jiffies + FLOPPY_TIMEOUT); } static inline void stop_timeout(void) { timer_delete(&timeout_timer); } /* Select the side to use. */