2204 lines · 56.5 KB · C
RawDownload
// 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.