18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  drivers/block/ataflop.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright (C) 1993  Greg Harp
68c2ecf20Sopenharmony_ci *  Atari Support by Bjoern Brauel, Roman Hodek
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci *  Big cleanup Sep 11..14 1994 Roman Hodek:
98c2ecf20Sopenharmony_ci *   - Driver now works interrupt driven
108c2ecf20Sopenharmony_ci *   - Support for two drives; should work, but I cannot test that :-(
118c2ecf20Sopenharmony_ci *   - Reading is done in whole tracks and buffered to speed up things
128c2ecf20Sopenharmony_ci *   - Disk change detection and drive deselecting after motor-off
138c2ecf20Sopenharmony_ci *     similar to TOS
148c2ecf20Sopenharmony_ci *   - Autodetection of disk format (DD/HD); untested yet, because I
158c2ecf20Sopenharmony_ci *     don't have an HD drive :-(
168c2ecf20Sopenharmony_ci *
178c2ecf20Sopenharmony_ci *  Fixes Nov 13 1994 Martin Schaller:
188c2ecf20Sopenharmony_ci *   - Autodetection works now
198c2ecf20Sopenharmony_ci *   - Support for 5 1/4'' disks
208c2ecf20Sopenharmony_ci *   - Removed drive type (unknown on atari)
218c2ecf20Sopenharmony_ci *   - Do seeks with 8 Mhz
228c2ecf20Sopenharmony_ci *
238c2ecf20Sopenharmony_ci *  Changes by Andreas Schwab:
248c2ecf20Sopenharmony_ci *   - After errors in multiple read mode try again reading single sectors
258c2ecf20Sopenharmony_ci *  (Feb 1995):
268c2ecf20Sopenharmony_ci *   - Clean up error handling
278c2ecf20Sopenharmony_ci *   - Set blk_size for proper size checking
288c2ecf20Sopenharmony_ci *   - Initialize track register when testing presence of floppy
298c2ecf20Sopenharmony_ci *   - Implement some ioctl's
308c2ecf20Sopenharmony_ci *
318c2ecf20Sopenharmony_ci *  Changes by Torsten Lang:
328c2ecf20Sopenharmony_ci *   - When probing the floppies we should add the FDCCMDADD_H flag since
338c2ecf20Sopenharmony_ci *     the FDC will otherwise wait forever when no disk is inserted...
348c2ecf20Sopenharmony_ci *
358c2ecf20Sopenharmony_ci * ++ Freddi Aschwanden (fa) 20.9.95 fixes for medusa:
368c2ecf20Sopenharmony_ci *  - MFPDELAY() after each FDC access -> atari
378c2ecf20Sopenharmony_ci *  - more/other disk formats
388c2ecf20Sopenharmony_ci *  - DMA to the block buffer directly if we have a 32bit DMA
398c2ecf20Sopenharmony_ci *  - for medusa, the step rate is always 3ms
408c2ecf20Sopenharmony_ci *  - on medusa, use only cache_push()
418c2ecf20Sopenharmony_ci * Roman:
428c2ecf20Sopenharmony_ci *  - Make disk format numbering independent from minors
438c2ecf20Sopenharmony_ci *  - Let user set max. supported drive type (speeds up format
448c2ecf20Sopenharmony_ci *    detection, saves buffer space)
458c2ecf20Sopenharmony_ci *
468c2ecf20Sopenharmony_ci * Roman 10/15/95:
478c2ecf20Sopenharmony_ci *  - implement some more ioctls
488c2ecf20Sopenharmony_ci *  - disk formatting
498c2ecf20Sopenharmony_ci *
508c2ecf20Sopenharmony_ci * Andreas 95/12/12:
518c2ecf20Sopenharmony_ci *  - increase gap size at start of track for HD/ED disks
528c2ecf20Sopenharmony_ci *
538c2ecf20Sopenharmony_ci * Michael (MSch) 11/07/96:
548c2ecf20Sopenharmony_ci *  - implemented FDSETPRM and FDDEFPRM ioctl
558c2ecf20Sopenharmony_ci *
568c2ecf20Sopenharmony_ci * Andreas (97/03/19):
578c2ecf20Sopenharmony_ci *  - implemented missing BLK* ioctls
588c2ecf20Sopenharmony_ci *
598c2ecf20Sopenharmony_ci *  Things left to do:
608c2ecf20Sopenharmony_ci *   - Formatting
618c2ecf20Sopenharmony_ci *   - Maybe a better strategy for disk change detection (does anyone
628c2ecf20Sopenharmony_ci *     know one?)
638c2ecf20Sopenharmony_ci */
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci#include <linux/module.h>
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci#include <linux/fd.h>
688c2ecf20Sopenharmony_ci#include <linux/delay.h>
698c2ecf20Sopenharmony_ci#include <linux/init.h>
708c2ecf20Sopenharmony_ci#include <linux/blk-mq.h>
718c2ecf20Sopenharmony_ci#include <linux/mutex.h>
728c2ecf20Sopenharmony_ci#include <linux/completion.h>
738c2ecf20Sopenharmony_ci#include <linux/wait.h>
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci#include <asm/atariints.h>
768c2ecf20Sopenharmony_ci#include <asm/atari_stdma.h>
778c2ecf20Sopenharmony_ci#include <asm/atari_stram.h>
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci#define	FD_MAX_UNITS 2
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci#undef DEBUG
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(ataflop_mutex);
848c2ecf20Sopenharmony_cistatic struct request *fd_request;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci/*
878c2ecf20Sopenharmony_ci * WD1772 stuff
888c2ecf20Sopenharmony_ci */
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci/* register codes */
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci#define FDCSELREG_STP   (0x80)   /* command/status register */
938c2ecf20Sopenharmony_ci#define FDCSELREG_TRA   (0x82)   /* track register */
948c2ecf20Sopenharmony_ci#define FDCSELREG_SEC   (0x84)   /* sector register */
958c2ecf20Sopenharmony_ci#define FDCSELREG_DTA   (0x86)   /* data register */
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci/* register names for FDC_READ/WRITE macros */
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci#define FDCREG_CMD		0
1008c2ecf20Sopenharmony_ci#define FDCREG_STATUS	0
1018c2ecf20Sopenharmony_ci#define FDCREG_TRACK	2
1028c2ecf20Sopenharmony_ci#define FDCREG_SECTOR	4
1038c2ecf20Sopenharmony_ci#define FDCREG_DATA		6
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci/* command opcodes */
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci#define FDCCMD_RESTORE  (0x00)   /*  -                   */
1088c2ecf20Sopenharmony_ci#define FDCCMD_SEEK     (0x10)   /*   |                  */
1098c2ecf20Sopenharmony_ci#define FDCCMD_STEP     (0x20)   /*   |  TYP 1 Commands  */
1108c2ecf20Sopenharmony_ci#define FDCCMD_STIN     (0x40)   /*   |                  */
1118c2ecf20Sopenharmony_ci#define FDCCMD_STOT     (0x60)   /*  -                   */
1128c2ecf20Sopenharmony_ci#define FDCCMD_RDSEC    (0x80)   /*  -   TYP 2 Commands  */
1138c2ecf20Sopenharmony_ci#define FDCCMD_WRSEC    (0xa0)   /*  -          "        */
1148c2ecf20Sopenharmony_ci#define FDCCMD_RDADR    (0xc0)   /*  -                   */
1158c2ecf20Sopenharmony_ci#define FDCCMD_RDTRA    (0xe0)   /*   |  TYP 3 Commands  */
1168c2ecf20Sopenharmony_ci#define FDCCMD_WRTRA    (0xf0)   /*  -                   */
1178c2ecf20Sopenharmony_ci#define FDCCMD_FORCI    (0xd0)   /*  -   TYP 4 Command   */
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci/* command modifier bits */
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci#define FDCCMDADD_SR6   (0x00)   /* step rate settings */
1228c2ecf20Sopenharmony_ci#define FDCCMDADD_SR12  (0x01)
1238c2ecf20Sopenharmony_ci#define FDCCMDADD_SR2   (0x02)
1248c2ecf20Sopenharmony_ci#define FDCCMDADD_SR3   (0x03)
1258c2ecf20Sopenharmony_ci#define FDCCMDADD_V     (0x04)   /* verify */
1268c2ecf20Sopenharmony_ci#define FDCCMDADD_H     (0x08)   /* wait for spin-up */
1278c2ecf20Sopenharmony_ci#define FDCCMDADD_U     (0x10)   /* update track register */
1288c2ecf20Sopenharmony_ci#define FDCCMDADD_M     (0x10)   /* multiple sector access */
1298c2ecf20Sopenharmony_ci#define FDCCMDADD_E     (0x04)   /* head settling flag */
1308c2ecf20Sopenharmony_ci#define FDCCMDADD_P     (0x02)   /* precompensation off */
1318c2ecf20Sopenharmony_ci#define FDCCMDADD_A0    (0x01)   /* DAM flag */
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci/* status register bits */
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci#define	FDCSTAT_MOTORON	(0x80)   /* motor on */
1368c2ecf20Sopenharmony_ci#define	FDCSTAT_WPROT	(0x40)   /* write protected (FDCCMD_WR*) */
1378c2ecf20Sopenharmony_ci#define	FDCSTAT_SPINUP	(0x20)   /* motor speed stable (Type I) */
1388c2ecf20Sopenharmony_ci#define	FDCSTAT_DELDAM	(0x20)   /* sector has deleted DAM (Type II+III) */
1398c2ecf20Sopenharmony_ci#define	FDCSTAT_RECNF	(0x10)   /* record not found */
1408c2ecf20Sopenharmony_ci#define	FDCSTAT_CRC		(0x08)   /* CRC error */
1418c2ecf20Sopenharmony_ci#define	FDCSTAT_TR00	(0x04)   /* Track 00 flag (Type I) */
1428c2ecf20Sopenharmony_ci#define	FDCSTAT_LOST	(0x04)   /* Lost Data (Type II+III) */
1438c2ecf20Sopenharmony_ci#define	FDCSTAT_IDX		(0x02)   /* Index status (Type I) */
1448c2ecf20Sopenharmony_ci#define	FDCSTAT_DRQ		(0x02)   /* DRQ status (Type II+III) */
1458c2ecf20Sopenharmony_ci#define	FDCSTAT_BUSY	(0x01)   /* FDC is busy */
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci/* PSG Port A Bit Nr 0 .. Side Sel .. 0 -> Side 1  1 -> Side 2 */
1498c2ecf20Sopenharmony_ci#define DSKSIDE     (0x01)
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci#define DSKDRVNONE  (0x06)
1528c2ecf20Sopenharmony_ci#define DSKDRV0     (0x02)
1538c2ecf20Sopenharmony_ci#define DSKDRV1     (0x04)
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci/* step rates */
1568c2ecf20Sopenharmony_ci#define	FDCSTEP_6	0x00
1578c2ecf20Sopenharmony_ci#define	FDCSTEP_12	0x01
1588c2ecf20Sopenharmony_ci#define	FDCSTEP_2	0x02
1598c2ecf20Sopenharmony_ci#define	FDCSTEP_3	0x03
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_cistruct atari_format_descr {
1628c2ecf20Sopenharmony_ci	int track;		/* to be formatted */
1638c2ecf20Sopenharmony_ci	int head;		/*   ""     ""     */
1648c2ecf20Sopenharmony_ci	int sect_offset;	/* offset of first sector */
1658c2ecf20Sopenharmony_ci};
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci/* Disk types: DD, HD, ED */
1688c2ecf20Sopenharmony_cistatic struct atari_disk_type {
1698c2ecf20Sopenharmony_ci	const char	*name;
1708c2ecf20Sopenharmony_ci	unsigned	spt;		/* sectors per track */
1718c2ecf20Sopenharmony_ci	unsigned	blocks;		/* total number of blocks */
1728c2ecf20Sopenharmony_ci	unsigned	fdc_speed;	/* fdc_speed setting */
1738c2ecf20Sopenharmony_ci	unsigned 	stretch;	/* track doubling ? */
1748c2ecf20Sopenharmony_ci} atari_disk_type[] = {
1758c2ecf20Sopenharmony_ci	{ "d360",  9, 720, 0, 0},	/*  0: 360kB diskette */
1768c2ecf20Sopenharmony_ci	{ "D360",  9, 720, 0, 1},	/*  1: 360kb in 720k or 1.2MB drive */
1778c2ecf20Sopenharmony_ci	{ "D720",  9,1440, 0, 0},	/*  2: 720kb in 720k or 1.2MB drive */
1788c2ecf20Sopenharmony_ci	{ "D820", 10,1640, 0, 0},	/*  3: DD disk with 82 tracks/10 sectors */
1798c2ecf20Sopenharmony_ci/* formats above are probed for type DD */
1808c2ecf20Sopenharmony_ci#define	MAX_TYPE_DD 3
1818c2ecf20Sopenharmony_ci	{ "h1200",15,2400, 3, 0},	/*  4: 1.2MB diskette */
1828c2ecf20Sopenharmony_ci	{ "H1440",18,2880, 3, 0},	/*  5: 1.4 MB diskette (HD) */
1838c2ecf20Sopenharmony_ci	{ "H1640",20,3280, 3, 0},	/*  6: 1.64MB diskette (fat HD) 82 tr 20 sec */
1848c2ecf20Sopenharmony_ci/* formats above are probed for types DD and HD */
1858c2ecf20Sopenharmony_ci#define	MAX_TYPE_HD 6
1868c2ecf20Sopenharmony_ci	{ "E2880",36,5760, 3, 0},	/*  7: 2.8 MB diskette (ED) */
1878c2ecf20Sopenharmony_ci	{ "E3280",40,6560, 3, 0},	/*  8: 3.2 MB diskette (fat ED) 82 tr 40 sec */
1888c2ecf20Sopenharmony_ci/* formats above are probed for types DD, HD and ED */
1898c2ecf20Sopenharmony_ci#define	MAX_TYPE_ED 8
1908c2ecf20Sopenharmony_ci/* types below are never autoprobed */
1918c2ecf20Sopenharmony_ci	{ "H1680",21,3360, 3, 0},	/*  9: 1.68MB diskette (fat HD) 80 tr 21 sec */
1928c2ecf20Sopenharmony_ci	{ "h410",10,820, 0, 1},		/* 10: 410k diskette 41 tr 10 sec, stretch */
1938c2ecf20Sopenharmony_ci	{ "h1476",18,2952, 3, 0},	/* 11: 1.48MB diskette 82 tr 18 sec */
1948c2ecf20Sopenharmony_ci	{ "H1722",21,3444, 3, 0},	/* 12: 1.72MB diskette 82 tr 21 sec */
1958c2ecf20Sopenharmony_ci	{ "h420",10,840, 0, 1},		/* 13: 420k diskette 42 tr 10 sec, stretch */
1968c2ecf20Sopenharmony_ci	{ "H830",10,1660, 0, 0},	/* 14: 820k diskette 83 tr 10 sec */
1978c2ecf20Sopenharmony_ci	{ "h1494",18,2952, 3, 0},	/* 15: 1.49MB diskette 83 tr 18 sec */
1988c2ecf20Sopenharmony_ci	{ "H1743",21,3486, 3, 0},	/* 16: 1.74MB diskette 83 tr 21 sec */
1998c2ecf20Sopenharmony_ci	{ "h880",11,1760, 0, 0},	/* 17: 880k diskette 80 tr 11 sec */
2008c2ecf20Sopenharmony_ci	{ "D1040",13,2080, 0, 0},	/* 18: 1.04MB diskette 80 tr 13 sec */
2018c2ecf20Sopenharmony_ci	{ "D1120",14,2240, 0, 0},	/* 19: 1.12MB diskette 80 tr 14 sec */
2028c2ecf20Sopenharmony_ci	{ "h1600",20,3200, 3, 0},	/* 20: 1.60MB diskette 80 tr 20 sec */
2038c2ecf20Sopenharmony_ci	{ "H1760",22,3520, 3, 0},	/* 21: 1.76MB diskette 80 tr 22 sec */
2048c2ecf20Sopenharmony_ci	{ "H1920",24,3840, 3, 0},	/* 22: 1.92MB diskette 80 tr 24 sec */
2058c2ecf20Sopenharmony_ci	{ "E3200",40,6400, 3, 0},	/* 23: 3.2MB diskette 80 tr 40 sec */
2068c2ecf20Sopenharmony_ci	{ "E3520",44,7040, 3, 0},	/* 24: 3.52MB diskette 80 tr 44 sec */
2078c2ecf20Sopenharmony_ci	{ "E3840",48,7680, 3, 0},	/* 25: 3.84MB diskette 80 tr 48 sec */
2088c2ecf20Sopenharmony_ci	{ "H1840",23,3680, 3, 0},	/* 26: 1.84MB diskette 80 tr 23 sec */
2098c2ecf20Sopenharmony_ci	{ "D800",10,1600, 0, 0},	/* 27: 800k diskette 80 tr 10 sec */
2108c2ecf20Sopenharmony_ci};
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_cistatic int StartDiskType[] = {
2138c2ecf20Sopenharmony_ci	MAX_TYPE_DD,
2148c2ecf20Sopenharmony_ci	MAX_TYPE_HD,
2158c2ecf20Sopenharmony_ci	MAX_TYPE_ED
2168c2ecf20Sopenharmony_ci};
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci#define	TYPE_DD		0
2198c2ecf20Sopenharmony_ci#define	TYPE_HD		1
2208c2ecf20Sopenharmony_ci#define	TYPE_ED		2
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_cistatic int DriveType = TYPE_HD;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(ataflop_lock);
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci/* Array for translating minors into disk formats */
2278c2ecf20Sopenharmony_cistatic struct {
2288c2ecf20Sopenharmony_ci	int 	 index;
2298c2ecf20Sopenharmony_ci	unsigned drive_types;
2308c2ecf20Sopenharmony_ci} minor2disktype[] = {
2318c2ecf20Sopenharmony_ci	{  0, TYPE_DD },	/*  1: d360 */
2328c2ecf20Sopenharmony_ci	{  4, TYPE_HD },	/*  2: h1200 */
2338c2ecf20Sopenharmony_ci	{  1, TYPE_DD },	/*  3: D360 */
2348c2ecf20Sopenharmony_ci	{  2, TYPE_DD },	/*  4: D720 */
2358c2ecf20Sopenharmony_ci	{  1, TYPE_DD },	/*  5: h360 = D360 */
2368c2ecf20Sopenharmony_ci	{  2, TYPE_DD },	/*  6: h720 = D720 */
2378c2ecf20Sopenharmony_ci	{  5, TYPE_HD },	/*  7: H1440 */
2388c2ecf20Sopenharmony_ci	{  7, TYPE_ED },	/*  8: E2880 */
2398c2ecf20Sopenharmony_ci/* some PC formats :-) */
2408c2ecf20Sopenharmony_ci	{  8, TYPE_ED },	/*  9: E3280    <- was "CompaQ" == E2880 for PC */
2418c2ecf20Sopenharmony_ci	{  5, TYPE_HD },	/* 10: h1440 = H1440 */
2428c2ecf20Sopenharmony_ci	{  9, TYPE_HD },	/* 11: H1680 */
2438c2ecf20Sopenharmony_ci	{ 10, TYPE_DD },	/* 12: h410  */
2448c2ecf20Sopenharmony_ci	{  3, TYPE_DD },	/* 13: H820     <- == D820, 82x10 */
2458c2ecf20Sopenharmony_ci	{ 11, TYPE_HD },	/* 14: h1476 */
2468c2ecf20Sopenharmony_ci	{ 12, TYPE_HD },	/* 15: H1722 */
2478c2ecf20Sopenharmony_ci	{ 13, TYPE_DD },	/* 16: h420  */
2488c2ecf20Sopenharmony_ci	{ 14, TYPE_DD },	/* 17: H830  */
2498c2ecf20Sopenharmony_ci	{ 15, TYPE_HD },	/* 18: h1494 */
2508c2ecf20Sopenharmony_ci	{ 16, TYPE_HD },	/* 19: H1743 */
2518c2ecf20Sopenharmony_ci	{ 17, TYPE_DD },	/* 20: h880  */
2528c2ecf20Sopenharmony_ci	{ 18, TYPE_DD },	/* 21: D1040 */
2538c2ecf20Sopenharmony_ci	{ 19, TYPE_DD },	/* 22: D1120 */
2548c2ecf20Sopenharmony_ci	{ 20, TYPE_HD },	/* 23: h1600 */
2558c2ecf20Sopenharmony_ci	{ 21, TYPE_HD },	/* 24: H1760 */
2568c2ecf20Sopenharmony_ci	{ 22, TYPE_HD },	/* 25: H1920 */
2578c2ecf20Sopenharmony_ci	{ 23, TYPE_ED },	/* 26: E3200 */
2588c2ecf20Sopenharmony_ci	{ 24, TYPE_ED },	/* 27: E3520 */
2598c2ecf20Sopenharmony_ci	{ 25, TYPE_ED },	/* 28: E3840 */
2608c2ecf20Sopenharmony_ci	{ 26, TYPE_HD },	/* 29: H1840 */
2618c2ecf20Sopenharmony_ci	{ 27, TYPE_DD },	/* 30: D800  */
2628c2ecf20Sopenharmony_ci	{  6, TYPE_HD },	/* 31: H1640    <- was H1600 == h1600 for PC */
2638c2ecf20Sopenharmony_ci};
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci#define NUM_DISK_MINORS ARRAY_SIZE(minor2disktype)
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci/*
2688c2ecf20Sopenharmony_ci * Maximum disk size (in kilobytes). This default is used whenever the
2698c2ecf20Sopenharmony_ci * current disk size is unknown.
2708c2ecf20Sopenharmony_ci */
2718c2ecf20Sopenharmony_ci#define MAX_DISK_SIZE 3280
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci/*
2748c2ecf20Sopenharmony_ci * MSch: User-provided type information. 'drive' points to
2758c2ecf20Sopenharmony_ci * the respective entry of this array. Set by FDSETPRM ioctls.
2768c2ecf20Sopenharmony_ci */
2778c2ecf20Sopenharmony_cistatic struct atari_disk_type user_params[FD_MAX_UNITS];
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci/*
2808c2ecf20Sopenharmony_ci * User-provided permanent type information. 'drive' points to
2818c2ecf20Sopenharmony_ci * the respective entry of this array.  Set by FDDEFPRM ioctls,
2828c2ecf20Sopenharmony_ci * restored upon disk change by floppy_revalidate() if valid (as seen by
2838c2ecf20Sopenharmony_ci * default_params[].blocks > 0 - a bit in unit[].flags might be used for this?)
2848c2ecf20Sopenharmony_ci */
2858c2ecf20Sopenharmony_cistatic struct atari_disk_type default_params[FD_MAX_UNITS];
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci/* current info on each unit */
2888c2ecf20Sopenharmony_cistatic struct atari_floppy_struct {
2898c2ecf20Sopenharmony_ci	int connected;				/* !=0 : drive is connected */
2908c2ecf20Sopenharmony_ci	int autoprobe;				/* !=0 : do autoprobe	    */
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	struct atari_disk_type	*disktype;	/* current type of disk */
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	int track;		/* current head position or -1 if
2958c2ecf20Sopenharmony_ci				   unknown */
2968c2ecf20Sopenharmony_ci	unsigned int steprate;	/* steprate setting */
2978c2ecf20Sopenharmony_ci	unsigned int wpstat;	/* current state of WP signal (for
2988c2ecf20Sopenharmony_ci				   disk change detection) */
2998c2ecf20Sopenharmony_ci	int flags;		/* flags */
3008c2ecf20Sopenharmony_ci	struct gendisk *disk;
3018c2ecf20Sopenharmony_ci	int ref;
3028c2ecf20Sopenharmony_ci	int type;
3038c2ecf20Sopenharmony_ci	struct blk_mq_tag_set tag_set;
3048c2ecf20Sopenharmony_ci} unit[FD_MAX_UNITS];
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci#define	UD	unit[drive]
3078c2ecf20Sopenharmony_ci#define	UDT	unit[drive].disktype
3088c2ecf20Sopenharmony_ci#define	SUD	unit[SelectedDrive]
3098c2ecf20Sopenharmony_ci#define	SUDT	unit[SelectedDrive].disktype
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci#define FDC_READ(reg) ({			\
3138c2ecf20Sopenharmony_ci    /* unsigned long __flags; */		\
3148c2ecf20Sopenharmony_ci    unsigned short __val;			\
3158c2ecf20Sopenharmony_ci    /* local_irq_save(__flags); */		\
3168c2ecf20Sopenharmony_ci    dma_wd.dma_mode_status = 0x80 | (reg);	\
3178c2ecf20Sopenharmony_ci    udelay(25);					\
3188c2ecf20Sopenharmony_ci    __val = dma_wd.fdc_acces_seccount;		\
3198c2ecf20Sopenharmony_ci    MFPDELAY();					\
3208c2ecf20Sopenharmony_ci    /* local_irq_restore(__flags); */		\
3218c2ecf20Sopenharmony_ci    __val & 0xff;				\
3228c2ecf20Sopenharmony_ci})
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci#define FDC_WRITE(reg,val)			\
3258c2ecf20Sopenharmony_ci    do {					\
3268c2ecf20Sopenharmony_ci	/* unsigned long __flags; */		\
3278c2ecf20Sopenharmony_ci	/* local_irq_save(__flags); */		\
3288c2ecf20Sopenharmony_ci	dma_wd.dma_mode_status = 0x80 | (reg);	\
3298c2ecf20Sopenharmony_ci	udelay(25);				\
3308c2ecf20Sopenharmony_ci	dma_wd.fdc_acces_seccount = (val);	\
3318c2ecf20Sopenharmony_ci	MFPDELAY();				\
3328c2ecf20Sopenharmony_ci        /* local_irq_restore(__flags); */	\
3338c2ecf20Sopenharmony_ci    } while(0)
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci/* Buffering variables:
3378c2ecf20Sopenharmony_ci * First, there is a DMA buffer in ST-RAM that is used for floppy DMA
3388c2ecf20Sopenharmony_ci * operations. Second, a track buffer is used to cache a whole track
3398c2ecf20Sopenharmony_ci * of the disk to save read operations. These are two separate buffers
3408c2ecf20Sopenharmony_ci * because that allows write operations without clearing the track buffer.
3418c2ecf20Sopenharmony_ci */
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_cistatic int MaxSectors[] = {
3448c2ecf20Sopenharmony_ci	11, 22, 44
3458c2ecf20Sopenharmony_ci};
3468c2ecf20Sopenharmony_cistatic int BufferSize[] = {
3478c2ecf20Sopenharmony_ci	15*512, 30*512, 60*512
3488c2ecf20Sopenharmony_ci};
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci#define	BUFFER_SIZE	(BufferSize[DriveType])
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ciunsigned char *DMABuffer;			  /* buffer for writes */
3538c2ecf20Sopenharmony_cistatic unsigned long PhysDMABuffer;   /* physical address */
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_cistatic int UseTrackbuffer = -1;		  /* Do track buffering? */
3568c2ecf20Sopenharmony_cimodule_param(UseTrackbuffer, int, 0);
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ciunsigned char *TrackBuffer;			  /* buffer for reads */
3598c2ecf20Sopenharmony_cistatic unsigned long PhysTrackBuffer; /* physical address */
3608c2ecf20Sopenharmony_cistatic int BufferDrive, BufferSide, BufferTrack;
3618c2ecf20Sopenharmony_cistatic int read_track;		/* non-zero if we are reading whole tracks */
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci#define	SECTOR_BUFFER(sec)	(TrackBuffer + ((sec)-1)*512)
3648c2ecf20Sopenharmony_ci#define	IS_BUFFERED(drive,side,track) \
3658c2ecf20Sopenharmony_ci    (BufferDrive == (drive) && BufferSide == (side) && BufferTrack == (track))
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci/*
3688c2ecf20Sopenharmony_ci * These are global variables, as that's the easiest way to give
3698c2ecf20Sopenharmony_ci * information to interrupts. They are the data used for the current
3708c2ecf20Sopenharmony_ci * request.
3718c2ecf20Sopenharmony_ci */
3728c2ecf20Sopenharmony_cistatic int SelectedDrive = 0;
3738c2ecf20Sopenharmony_cistatic int ReqCmd, ReqBlock;
3748c2ecf20Sopenharmony_cistatic int ReqSide, ReqTrack, ReqSector, ReqCnt;
3758c2ecf20Sopenharmony_cistatic int HeadSettleFlag = 0;
3768c2ecf20Sopenharmony_cistatic unsigned char *ReqData, *ReqBuffer;
3778c2ecf20Sopenharmony_cistatic int MotorOn = 0, MotorOffTrys;
3788c2ecf20Sopenharmony_cistatic int IsFormatting = 0, FormatError;
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_cistatic int UserSteprate[FD_MAX_UNITS] = { -1, -1 };
3818c2ecf20Sopenharmony_cimodule_param_array(UserSteprate, int, NULL, 0);
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_cistatic DECLARE_COMPLETION(format_wait);
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_cistatic unsigned long changed_floppies = 0xff, fake_change = 0;
3868c2ecf20Sopenharmony_ci#define	CHECK_CHANGE_DELAY	HZ/2
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci#define	FD_MOTOR_OFF_DELAY	(3*HZ)
3898c2ecf20Sopenharmony_ci#define	FD_MOTOR_OFF_MAXTRY	(10*20)
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci#define FLOPPY_TIMEOUT		(6*HZ)
3928c2ecf20Sopenharmony_ci#define RECALIBRATE_ERRORS	4	/* After this many errors the drive
3938c2ecf20Sopenharmony_ci					 * will be recalibrated. */
3948c2ecf20Sopenharmony_ci#define MAX_ERRORS		8	/* After this many errors the driver
3958c2ecf20Sopenharmony_ci					 * will give up. */
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci/*
3998c2ecf20Sopenharmony_ci * The driver is trying to determine the correct media format
4008c2ecf20Sopenharmony_ci * while Probing is set. fd_rwsec_done() clears it after a
4018c2ecf20Sopenharmony_ci * successful access.
4028c2ecf20Sopenharmony_ci */
4038c2ecf20Sopenharmony_cistatic int Probing = 0;
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci/* This flag is set when a dummy seek is necessary to make the WP
4068c2ecf20Sopenharmony_ci * status bit accessible.
4078c2ecf20Sopenharmony_ci */
4088c2ecf20Sopenharmony_cistatic int NeedSeek = 0;
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci#ifdef DEBUG
4128c2ecf20Sopenharmony_ci#define DPRINT(a)	printk a
4138c2ecf20Sopenharmony_ci#else
4148c2ecf20Sopenharmony_ci#define DPRINT(a)
4158c2ecf20Sopenharmony_ci#endif
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci/***************************** Prototypes *****************************/
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_cistatic void fd_select_side( int side );
4208c2ecf20Sopenharmony_cistatic void fd_select_drive( int drive );
4218c2ecf20Sopenharmony_cistatic void fd_deselect( void );
4228c2ecf20Sopenharmony_cistatic void fd_motor_off_timer(struct timer_list *unused);
4238c2ecf20Sopenharmony_cistatic void check_change(struct timer_list *unused);
4248c2ecf20Sopenharmony_cistatic irqreturn_t floppy_irq (int irq, void *dummy);
4258c2ecf20Sopenharmony_cistatic void fd_error( void );
4268c2ecf20Sopenharmony_cistatic int do_format(int drive, int type, struct atari_format_descr *desc);
4278c2ecf20Sopenharmony_cistatic void do_fd_action( int drive );
4288c2ecf20Sopenharmony_cistatic void fd_calibrate( void );
4298c2ecf20Sopenharmony_cistatic void fd_calibrate_done( int status );
4308c2ecf20Sopenharmony_cistatic void fd_seek( void );
4318c2ecf20Sopenharmony_cistatic void fd_seek_done( int status );
4328c2ecf20Sopenharmony_cistatic void fd_rwsec( void );
4338c2ecf20Sopenharmony_cistatic void fd_readtrack_check(struct timer_list *unused);
4348c2ecf20Sopenharmony_cistatic void fd_rwsec_done( int status );
4358c2ecf20Sopenharmony_cistatic void fd_rwsec_done1(int status);
4368c2ecf20Sopenharmony_cistatic void fd_writetrack( void );
4378c2ecf20Sopenharmony_cistatic void fd_writetrack_done( int status );
4388c2ecf20Sopenharmony_cistatic void fd_times_out(struct timer_list *unused);
4398c2ecf20Sopenharmony_cistatic void finish_fdc( void );
4408c2ecf20Sopenharmony_cistatic void finish_fdc_done( int dummy );
4418c2ecf20Sopenharmony_cistatic void setup_req_params( int drive );
4428c2ecf20Sopenharmony_cistatic int fd_locked_ioctl(struct block_device *bdev, fmode_t mode, unsigned int
4438c2ecf20Sopenharmony_ci                     cmd, unsigned long param);
4448c2ecf20Sopenharmony_cistatic void fd_probe( int drive );
4458c2ecf20Sopenharmony_cistatic int fd_test_drive_present( int drive );
4468c2ecf20Sopenharmony_cistatic void config_types( void );
4478c2ecf20Sopenharmony_cistatic int floppy_open(struct block_device *bdev, fmode_t mode);
4488c2ecf20Sopenharmony_cistatic void floppy_release(struct gendisk *disk, fmode_t mode);
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci/************************* End of Prototypes **************************/
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_cistatic DEFINE_TIMER(motor_off_timer, fd_motor_off_timer);
4538c2ecf20Sopenharmony_cistatic DEFINE_TIMER(readtrack_timer, fd_readtrack_check);
4548c2ecf20Sopenharmony_cistatic DEFINE_TIMER(timeout_timer, fd_times_out);
4558c2ecf20Sopenharmony_cistatic DEFINE_TIMER(fd_timer, check_change);
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_cistatic void fd_end_request_cur(blk_status_t err)
4588c2ecf20Sopenharmony_ci{
4598c2ecf20Sopenharmony_ci	if (!blk_update_request(fd_request, err,
4608c2ecf20Sopenharmony_ci				blk_rq_cur_bytes(fd_request))) {
4618c2ecf20Sopenharmony_ci		__blk_mq_end_request(fd_request, err);
4628c2ecf20Sopenharmony_ci		fd_request = NULL;
4638c2ecf20Sopenharmony_ci	}
4648c2ecf20Sopenharmony_ci}
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_cistatic inline void start_motor_off_timer(void)
4678c2ecf20Sopenharmony_ci{
4688c2ecf20Sopenharmony_ci	mod_timer(&motor_off_timer, jiffies + FD_MOTOR_OFF_DELAY);
4698c2ecf20Sopenharmony_ci	MotorOffTrys = 0;
4708c2ecf20Sopenharmony_ci}
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_cistatic inline void start_check_change_timer( void )
4738c2ecf20Sopenharmony_ci{
4748c2ecf20Sopenharmony_ci	mod_timer(&fd_timer, jiffies + CHECK_CHANGE_DELAY);
4758c2ecf20Sopenharmony_ci}
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_cistatic inline void start_timeout(void)
4788c2ecf20Sopenharmony_ci{
4798c2ecf20Sopenharmony_ci	mod_timer(&timeout_timer, jiffies + FLOPPY_TIMEOUT);
4808c2ecf20Sopenharmony_ci}
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_cistatic inline void stop_timeout(void)
4838c2ecf20Sopenharmony_ci{
4848c2ecf20Sopenharmony_ci	del_timer(&timeout_timer);
4858c2ecf20Sopenharmony_ci}
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci/* Select the side to use. */
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_cistatic void fd_select_side( int side )
4908c2ecf20Sopenharmony_ci{
4918c2ecf20Sopenharmony_ci	unsigned long flags;
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	/* protect against various other ints mucking around with the PSG */
4948c2ecf20Sopenharmony_ci	local_irq_save(flags);
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	sound_ym.rd_data_reg_sel = 14; /* Select PSG Port A */
4978c2ecf20Sopenharmony_ci	sound_ym.wd_data = (side == 0) ? sound_ym.rd_data_reg_sel | 0x01 :
4988c2ecf20Sopenharmony_ci	                                 sound_ym.rd_data_reg_sel & 0xfe;
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci	local_irq_restore(flags);
5018c2ecf20Sopenharmony_ci}
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci/* Select a drive, update the FDC's track register and set the correct
5058c2ecf20Sopenharmony_ci * clock speed for this disk's type.
5068c2ecf20Sopenharmony_ci */
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_cistatic void fd_select_drive( int drive )
5098c2ecf20Sopenharmony_ci{
5108c2ecf20Sopenharmony_ci	unsigned long flags;
5118c2ecf20Sopenharmony_ci	unsigned char tmp;
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	if (drive == SelectedDrive)
5148c2ecf20Sopenharmony_ci	  return;
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	/* protect against various other ints mucking around with the PSG */
5178c2ecf20Sopenharmony_ci	local_irq_save(flags);
5188c2ecf20Sopenharmony_ci	sound_ym.rd_data_reg_sel = 14; /* Select PSG Port A */
5198c2ecf20Sopenharmony_ci	tmp = sound_ym.rd_data_reg_sel;
5208c2ecf20Sopenharmony_ci	sound_ym.wd_data = (tmp | DSKDRVNONE) & ~(drive == 0 ? DSKDRV0 : DSKDRV1);
5218c2ecf20Sopenharmony_ci	atari_dont_touch_floppy_select = 1;
5228c2ecf20Sopenharmony_ci	local_irq_restore(flags);
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	/* restore track register to saved value */
5258c2ecf20Sopenharmony_ci	FDC_WRITE( FDCREG_TRACK, UD.track );
5268c2ecf20Sopenharmony_ci	udelay(25);
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	/* select 8/16 MHz */
5298c2ecf20Sopenharmony_ci	if (UDT)
5308c2ecf20Sopenharmony_ci		if (ATARIHW_PRESENT(FDCSPEED))
5318c2ecf20Sopenharmony_ci			dma_wd.fdc_speed = UDT->fdc_speed;
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	SelectedDrive = drive;
5348c2ecf20Sopenharmony_ci}
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci/* Deselect both drives. */
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_cistatic void fd_deselect( void )
5408c2ecf20Sopenharmony_ci{
5418c2ecf20Sopenharmony_ci	unsigned long flags;
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci	/* protect against various other ints mucking around with the PSG */
5448c2ecf20Sopenharmony_ci	local_irq_save(flags);
5458c2ecf20Sopenharmony_ci	atari_dont_touch_floppy_select = 0;
5468c2ecf20Sopenharmony_ci	sound_ym.rd_data_reg_sel=14;	/* Select PSG Port A */
5478c2ecf20Sopenharmony_ci	sound_ym.wd_data = (sound_ym.rd_data_reg_sel |
5488c2ecf20Sopenharmony_ci			    (MACH_IS_FALCON ? 3 : 7)); /* no drives selected */
5498c2ecf20Sopenharmony_ci	/* On Falcon, the drive B select line is used on the printer port, so
5508c2ecf20Sopenharmony_ci	 * leave it alone... */
5518c2ecf20Sopenharmony_ci	SelectedDrive = -1;
5528c2ecf20Sopenharmony_ci	local_irq_restore(flags);
5538c2ecf20Sopenharmony_ci}
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci/* This timer function deselects the drives when the FDC switched the
5578c2ecf20Sopenharmony_ci * motor off. The deselection cannot happen earlier because the FDC
5588c2ecf20Sopenharmony_ci * counts the index signals, which arrive only if one drive is selected.
5598c2ecf20Sopenharmony_ci */
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_cistatic void fd_motor_off_timer(struct timer_list *unused)
5628c2ecf20Sopenharmony_ci{
5638c2ecf20Sopenharmony_ci	unsigned char status;
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	if (SelectedDrive < 0)
5668c2ecf20Sopenharmony_ci		/* no drive selected, needn't deselect anyone */
5678c2ecf20Sopenharmony_ci		return;
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci	if (stdma_islocked())
5708c2ecf20Sopenharmony_ci		goto retry;
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci	status = FDC_READ( FDCREG_STATUS );
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	if (!(status & 0x80)) {
5758c2ecf20Sopenharmony_ci		/* motor already turned off by FDC -> deselect drives */
5768c2ecf20Sopenharmony_ci		MotorOn = 0;
5778c2ecf20Sopenharmony_ci		fd_deselect();
5788c2ecf20Sopenharmony_ci		return;
5798c2ecf20Sopenharmony_ci	}
5808c2ecf20Sopenharmony_ci	/* not yet off, try again */
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci  retry:
5838c2ecf20Sopenharmony_ci	/* Test again later; if tested too often, it seems there is no disk
5848c2ecf20Sopenharmony_ci	 * in the drive and the FDC will leave the motor on forever (or,
5858c2ecf20Sopenharmony_ci	 * at least until a disk is inserted). So we'll test only twice
5868c2ecf20Sopenharmony_ci	 * per second from then on...
5878c2ecf20Sopenharmony_ci	 */
5888c2ecf20Sopenharmony_ci	mod_timer(&motor_off_timer,
5898c2ecf20Sopenharmony_ci		  jiffies + (MotorOffTrys++ < FD_MOTOR_OFF_MAXTRY ? HZ/20 : HZ/2));
5908c2ecf20Sopenharmony_ci}
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci/* This function is repeatedly called to detect disk changes (as good
5948c2ecf20Sopenharmony_ci * as possible) and keep track of the current state of the write protection.
5958c2ecf20Sopenharmony_ci */
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_cistatic void check_change(struct timer_list *unused)
5988c2ecf20Sopenharmony_ci{
5998c2ecf20Sopenharmony_ci	static int    drive = 0;
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci	unsigned long flags;
6028c2ecf20Sopenharmony_ci	unsigned char old_porta;
6038c2ecf20Sopenharmony_ci	int			  stat;
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci	if (++drive > 1 || !UD.connected)
6068c2ecf20Sopenharmony_ci		drive = 0;
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	/* protect against various other ints mucking around with the PSG */
6098c2ecf20Sopenharmony_ci	local_irq_save(flags);
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci	if (!stdma_islocked()) {
6128c2ecf20Sopenharmony_ci		sound_ym.rd_data_reg_sel = 14;
6138c2ecf20Sopenharmony_ci		old_porta = sound_ym.rd_data_reg_sel;
6148c2ecf20Sopenharmony_ci		sound_ym.wd_data = (old_porta | DSKDRVNONE) &
6158c2ecf20Sopenharmony_ci			               ~(drive == 0 ? DSKDRV0 : DSKDRV1);
6168c2ecf20Sopenharmony_ci		stat = !!(FDC_READ( FDCREG_STATUS ) & FDCSTAT_WPROT);
6178c2ecf20Sopenharmony_ci		sound_ym.wd_data = old_porta;
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci		if (stat != UD.wpstat) {
6208c2ecf20Sopenharmony_ci			DPRINT(( "wpstat[%d] = %d\n", drive, stat ));
6218c2ecf20Sopenharmony_ci			UD.wpstat = stat;
6228c2ecf20Sopenharmony_ci			set_bit (drive, &changed_floppies);
6238c2ecf20Sopenharmony_ci		}
6248c2ecf20Sopenharmony_ci	}
6258c2ecf20Sopenharmony_ci	local_irq_restore(flags);
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	start_check_change_timer();
6288c2ecf20Sopenharmony_ci}
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci/* Handling of the Head Settling Flag: This flag should be set after each
6328c2ecf20Sopenharmony_ci * seek operation, because we don't use seeks with verify.
6338c2ecf20Sopenharmony_ci */
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_cistatic inline void set_head_settle_flag(void)
6368c2ecf20Sopenharmony_ci{
6378c2ecf20Sopenharmony_ci	HeadSettleFlag = FDCCMDADD_E;
6388c2ecf20Sopenharmony_ci}
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_cistatic inline int get_head_settle_flag(void)
6418c2ecf20Sopenharmony_ci{
6428c2ecf20Sopenharmony_ci	int	tmp = HeadSettleFlag;
6438c2ecf20Sopenharmony_ci	HeadSettleFlag = 0;
6448c2ecf20Sopenharmony_ci	return( tmp );
6458c2ecf20Sopenharmony_ci}
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_cistatic inline void copy_buffer(void *from, void *to)
6488c2ecf20Sopenharmony_ci{
6498c2ecf20Sopenharmony_ci	ulong *p1 = (ulong *)from, *p2 = (ulong *)to;
6508c2ecf20Sopenharmony_ci	int cnt;
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci	for (cnt = 512/4; cnt; cnt--)
6538c2ecf20Sopenharmony_ci		*p2++ = *p1++;
6548c2ecf20Sopenharmony_ci}
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_ci/* General Interrupt Handling */
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_cistatic void (*FloppyIRQHandler)( int status ) = NULL;
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_cistatic irqreturn_t floppy_irq (int irq, void *dummy)
6648c2ecf20Sopenharmony_ci{
6658c2ecf20Sopenharmony_ci	unsigned char status;
6668c2ecf20Sopenharmony_ci	void (*handler)( int );
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci	handler = xchg(&FloppyIRQHandler, NULL);
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ci	if (handler) {
6718c2ecf20Sopenharmony_ci		nop();
6728c2ecf20Sopenharmony_ci		status = FDC_READ( FDCREG_STATUS );
6738c2ecf20Sopenharmony_ci		DPRINT(("FDC irq, status = %02x handler = %08lx\n",status,(unsigned long)handler));
6748c2ecf20Sopenharmony_ci		handler( status );
6758c2ecf20Sopenharmony_ci	}
6768c2ecf20Sopenharmony_ci	else {
6778c2ecf20Sopenharmony_ci		DPRINT(("FDC irq, no handler\n"));
6788c2ecf20Sopenharmony_ci	}
6798c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
6808c2ecf20Sopenharmony_ci}
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci/* Error handling: If some error happened, retry some times, then
6848c2ecf20Sopenharmony_ci * recalibrate, then try again, and fail after MAX_ERRORS.
6858c2ecf20Sopenharmony_ci */
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_cistatic void fd_error( void )
6888c2ecf20Sopenharmony_ci{
6898c2ecf20Sopenharmony_ci	if (IsFormatting) {
6908c2ecf20Sopenharmony_ci		IsFormatting = 0;
6918c2ecf20Sopenharmony_ci		FormatError = 1;
6928c2ecf20Sopenharmony_ci		complete(&format_wait);
6938c2ecf20Sopenharmony_ci		return;
6948c2ecf20Sopenharmony_ci	}
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci	if (!fd_request)
6978c2ecf20Sopenharmony_ci		return;
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci	fd_request->error_count++;
7008c2ecf20Sopenharmony_ci	if (fd_request->error_count >= MAX_ERRORS) {
7018c2ecf20Sopenharmony_ci		printk(KERN_ERR "fd%d: too many errors.\n", SelectedDrive );
7028c2ecf20Sopenharmony_ci		fd_end_request_cur(BLK_STS_IOERR);
7038c2ecf20Sopenharmony_ci	}
7048c2ecf20Sopenharmony_ci	else if (fd_request->error_count == RECALIBRATE_ERRORS) {
7058c2ecf20Sopenharmony_ci		printk(KERN_WARNING "fd%d: recalibrating\n", SelectedDrive );
7068c2ecf20Sopenharmony_ci		if (SelectedDrive != -1)
7078c2ecf20Sopenharmony_ci			SUD.track = -1;
7088c2ecf20Sopenharmony_ci	}
7098c2ecf20Sopenharmony_ci}
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci#define	SET_IRQ_HANDLER(proc) do { FloppyIRQHandler = (proc); } while(0)
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_ci/* ---------- Formatting ---------- */
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci#define FILL(n,val)		\
7198c2ecf20Sopenharmony_ci    do {			\
7208c2ecf20Sopenharmony_ci	memset( p, val, n );	\
7218c2ecf20Sopenharmony_ci	p += n;			\
7228c2ecf20Sopenharmony_ci    } while(0)
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_cistatic int do_format(int drive, int type, struct atari_format_descr *desc)
7258c2ecf20Sopenharmony_ci{
7268c2ecf20Sopenharmony_ci	struct request_queue *q = unit[drive].disk->queue;
7278c2ecf20Sopenharmony_ci	unsigned char	*p;
7288c2ecf20Sopenharmony_ci	int sect, nsect;
7298c2ecf20Sopenharmony_ci	unsigned long	flags;
7308c2ecf20Sopenharmony_ci	int ret;
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_ci	blk_mq_freeze_queue(q);
7338c2ecf20Sopenharmony_ci	blk_mq_quiesce_queue(q);
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ci	local_irq_save(flags);
7368c2ecf20Sopenharmony_ci	stdma_lock(floppy_irq, NULL);
7378c2ecf20Sopenharmony_ci	atari_turnon_irq( IRQ_MFP_FDC ); /* should be already, just to be sure */
7388c2ecf20Sopenharmony_ci	local_irq_restore(flags);
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci	if (type) {
7418c2ecf20Sopenharmony_ci		if (--type >= NUM_DISK_MINORS ||
7428c2ecf20Sopenharmony_ci		    minor2disktype[type].drive_types > DriveType) {
7438c2ecf20Sopenharmony_ci			ret = -EINVAL;
7448c2ecf20Sopenharmony_ci			goto out;
7458c2ecf20Sopenharmony_ci		}
7468c2ecf20Sopenharmony_ci		type = minor2disktype[type].index;
7478c2ecf20Sopenharmony_ci		UDT = &atari_disk_type[type];
7488c2ecf20Sopenharmony_ci	}
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci	if (!UDT || desc->track >= UDT->blocks/UDT->spt/2 || desc->head >= 2) {
7518c2ecf20Sopenharmony_ci		ret = -EINVAL;
7528c2ecf20Sopenharmony_ci		goto out;
7538c2ecf20Sopenharmony_ci	}
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci	nsect = UDT->spt;
7568c2ecf20Sopenharmony_ci	p = TrackBuffer;
7578c2ecf20Sopenharmony_ci	/* The track buffer is used for the raw track data, so its
7588c2ecf20Sopenharmony_ci	   contents become invalid! */
7598c2ecf20Sopenharmony_ci	BufferDrive = -1;
7608c2ecf20Sopenharmony_ci	/* stop deselect timer */
7618c2ecf20Sopenharmony_ci	del_timer( &motor_off_timer );
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ci	FILL( 60 * (nsect / 9), 0x4e );
7648c2ecf20Sopenharmony_ci	for( sect = 0; sect < nsect; ++sect ) {
7658c2ecf20Sopenharmony_ci		FILL( 12, 0 );
7668c2ecf20Sopenharmony_ci		FILL( 3, 0xf5 );
7678c2ecf20Sopenharmony_ci		*p++ = 0xfe;
7688c2ecf20Sopenharmony_ci		*p++ = desc->track;
7698c2ecf20Sopenharmony_ci		*p++ = desc->head;
7708c2ecf20Sopenharmony_ci		*p++ = (nsect + sect - desc->sect_offset) % nsect + 1;
7718c2ecf20Sopenharmony_ci		*p++ = 2;
7728c2ecf20Sopenharmony_ci		*p++ = 0xf7;
7738c2ecf20Sopenharmony_ci		FILL( 22, 0x4e );
7748c2ecf20Sopenharmony_ci		FILL( 12, 0 );
7758c2ecf20Sopenharmony_ci		FILL( 3, 0xf5 );
7768c2ecf20Sopenharmony_ci		*p++ = 0xfb;
7778c2ecf20Sopenharmony_ci		FILL( 512, 0xe5 );
7788c2ecf20Sopenharmony_ci		*p++ = 0xf7;
7798c2ecf20Sopenharmony_ci		FILL( 40, 0x4e );
7808c2ecf20Sopenharmony_ci	}
7818c2ecf20Sopenharmony_ci	FILL( TrackBuffer+BUFFER_SIZE-p, 0x4e );
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci	IsFormatting = 1;
7848c2ecf20Sopenharmony_ci	FormatError = 0;
7858c2ecf20Sopenharmony_ci	ReqTrack = desc->track;
7868c2ecf20Sopenharmony_ci	ReqSide  = desc->head;
7878c2ecf20Sopenharmony_ci	do_fd_action( drive );
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci	wait_for_completion(&format_wait);
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci	ret = FormatError ? -EIO : 0;
7928c2ecf20Sopenharmony_ciout:
7938c2ecf20Sopenharmony_ci	blk_mq_unquiesce_queue(q);
7948c2ecf20Sopenharmony_ci	blk_mq_unfreeze_queue(q);
7958c2ecf20Sopenharmony_ci	return ret;
7968c2ecf20Sopenharmony_ci}
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci/* do_fd_action() is the general procedure for a fd request: All
8008c2ecf20Sopenharmony_ci * required parameter settings (drive select, side select, track
8018c2ecf20Sopenharmony_ci * position) are checked and set if needed. For each of these
8028c2ecf20Sopenharmony_ci * parameters and the actual reading or writing exist two functions:
8038c2ecf20Sopenharmony_ci * one that starts the setting (or skips it if possible) and one
8048c2ecf20Sopenharmony_ci * callback for the "done" interrupt. Each done func calls the next
8058c2ecf20Sopenharmony_ci * set function to propagate the request down to fd_rwsec_done().
8068c2ecf20Sopenharmony_ci */
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_cistatic void do_fd_action( int drive )
8098c2ecf20Sopenharmony_ci{
8108c2ecf20Sopenharmony_ci	DPRINT(("do_fd_action\n"));
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci	if (UseTrackbuffer && !IsFormatting) {
8138c2ecf20Sopenharmony_ci	repeat:
8148c2ecf20Sopenharmony_ci	    if (IS_BUFFERED( drive, ReqSide, ReqTrack )) {
8158c2ecf20Sopenharmony_ci		if (ReqCmd == READ) {
8168c2ecf20Sopenharmony_ci		    copy_buffer( SECTOR_BUFFER(ReqSector), ReqData );
8178c2ecf20Sopenharmony_ci		    if (++ReqCnt < blk_rq_cur_sectors(fd_request)) {
8188c2ecf20Sopenharmony_ci			/* read next sector */
8198c2ecf20Sopenharmony_ci			setup_req_params( drive );
8208c2ecf20Sopenharmony_ci			goto repeat;
8218c2ecf20Sopenharmony_ci		    }
8228c2ecf20Sopenharmony_ci		    else {
8238c2ecf20Sopenharmony_ci			/* all sectors finished */
8248c2ecf20Sopenharmony_ci			fd_end_request_cur(BLK_STS_OK);
8258c2ecf20Sopenharmony_ci			return;
8268c2ecf20Sopenharmony_ci		    }
8278c2ecf20Sopenharmony_ci		}
8288c2ecf20Sopenharmony_ci		else {
8298c2ecf20Sopenharmony_ci		    /* cmd == WRITE, pay attention to track buffer
8308c2ecf20Sopenharmony_ci		     * consistency! */
8318c2ecf20Sopenharmony_ci		    copy_buffer( ReqData, SECTOR_BUFFER(ReqSector) );
8328c2ecf20Sopenharmony_ci		}
8338c2ecf20Sopenharmony_ci	    }
8348c2ecf20Sopenharmony_ci	}
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_ci	if (SelectedDrive != drive)
8378c2ecf20Sopenharmony_ci		fd_select_drive( drive );
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_ci	if (UD.track == -1)
8408c2ecf20Sopenharmony_ci		fd_calibrate();
8418c2ecf20Sopenharmony_ci	else if (UD.track != ReqTrack << UDT->stretch)
8428c2ecf20Sopenharmony_ci		fd_seek();
8438c2ecf20Sopenharmony_ci	else if (IsFormatting)
8448c2ecf20Sopenharmony_ci		fd_writetrack();
8458c2ecf20Sopenharmony_ci	else
8468c2ecf20Sopenharmony_ci		fd_rwsec();
8478c2ecf20Sopenharmony_ci}
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci/* Seek to track 0 if the current track is unknown */
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_cistatic void fd_calibrate( void )
8538c2ecf20Sopenharmony_ci{
8548c2ecf20Sopenharmony_ci	if (SUD.track >= 0) {
8558c2ecf20Sopenharmony_ci		fd_calibrate_done( 0 );
8568c2ecf20Sopenharmony_ci		return;
8578c2ecf20Sopenharmony_ci	}
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_ci	if (ATARIHW_PRESENT(FDCSPEED))
8608c2ecf20Sopenharmony_ci		dma_wd.fdc_speed = 0;   /* always seek with 8 Mhz */
8618c2ecf20Sopenharmony_ci	DPRINT(("fd_calibrate\n"));
8628c2ecf20Sopenharmony_ci	SET_IRQ_HANDLER( fd_calibrate_done );
8638c2ecf20Sopenharmony_ci	/* we can't verify, since the speed may be incorrect */
8648c2ecf20Sopenharmony_ci	FDC_WRITE( FDCREG_CMD, FDCCMD_RESTORE | SUD.steprate );
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci	NeedSeek = 1;
8678c2ecf20Sopenharmony_ci	MotorOn = 1;
8688c2ecf20Sopenharmony_ci	start_timeout();
8698c2ecf20Sopenharmony_ci	/* wait for IRQ */
8708c2ecf20Sopenharmony_ci}
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_cistatic void fd_calibrate_done( int status )
8748c2ecf20Sopenharmony_ci{
8758c2ecf20Sopenharmony_ci	DPRINT(("fd_calibrate_done()\n"));
8768c2ecf20Sopenharmony_ci	stop_timeout();
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_ci	/* set the correct speed now */
8798c2ecf20Sopenharmony_ci	if (ATARIHW_PRESENT(FDCSPEED))
8808c2ecf20Sopenharmony_ci		dma_wd.fdc_speed = SUDT->fdc_speed;
8818c2ecf20Sopenharmony_ci	if (status & FDCSTAT_RECNF) {
8828c2ecf20Sopenharmony_ci		printk(KERN_ERR "fd%d: restore failed\n", SelectedDrive );
8838c2ecf20Sopenharmony_ci		fd_error();
8848c2ecf20Sopenharmony_ci	}
8858c2ecf20Sopenharmony_ci	else {
8868c2ecf20Sopenharmony_ci		SUD.track = 0;
8878c2ecf20Sopenharmony_ci		fd_seek();
8888c2ecf20Sopenharmony_ci	}
8898c2ecf20Sopenharmony_ci}
8908c2ecf20Sopenharmony_ci
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci/* Seek the drive to the requested track. The drive must have been
8938c2ecf20Sopenharmony_ci * calibrated at some point before this.
8948c2ecf20Sopenharmony_ci */
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_cistatic void fd_seek( void )
8978c2ecf20Sopenharmony_ci{
8988c2ecf20Sopenharmony_ci	if (SUD.track == ReqTrack << SUDT->stretch) {
8998c2ecf20Sopenharmony_ci		fd_seek_done( 0 );
9008c2ecf20Sopenharmony_ci		return;
9018c2ecf20Sopenharmony_ci	}
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_ci	if (ATARIHW_PRESENT(FDCSPEED)) {
9048c2ecf20Sopenharmony_ci		dma_wd.fdc_speed = 0;	/* always seek witch 8 Mhz */
9058c2ecf20Sopenharmony_ci		MFPDELAY();
9068c2ecf20Sopenharmony_ci	}
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_ci	DPRINT(("fd_seek() to track %d\n",ReqTrack));
9098c2ecf20Sopenharmony_ci	FDC_WRITE( FDCREG_DATA, ReqTrack << SUDT->stretch);
9108c2ecf20Sopenharmony_ci	udelay(25);
9118c2ecf20Sopenharmony_ci	SET_IRQ_HANDLER( fd_seek_done );
9128c2ecf20Sopenharmony_ci	FDC_WRITE( FDCREG_CMD, FDCCMD_SEEK | SUD.steprate );
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_ci	MotorOn = 1;
9158c2ecf20Sopenharmony_ci	set_head_settle_flag();
9168c2ecf20Sopenharmony_ci	start_timeout();
9178c2ecf20Sopenharmony_ci	/* wait for IRQ */
9188c2ecf20Sopenharmony_ci}
9198c2ecf20Sopenharmony_ci
9208c2ecf20Sopenharmony_ci
9218c2ecf20Sopenharmony_cistatic void fd_seek_done( int status )
9228c2ecf20Sopenharmony_ci{
9238c2ecf20Sopenharmony_ci	DPRINT(("fd_seek_done()\n"));
9248c2ecf20Sopenharmony_ci	stop_timeout();
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_ci	/* set the correct speed */
9278c2ecf20Sopenharmony_ci	if (ATARIHW_PRESENT(FDCSPEED))
9288c2ecf20Sopenharmony_ci		dma_wd.fdc_speed = SUDT->fdc_speed;
9298c2ecf20Sopenharmony_ci	if (status & FDCSTAT_RECNF) {
9308c2ecf20Sopenharmony_ci		printk(KERN_ERR "fd%d: seek error (to track %d)\n",
9318c2ecf20Sopenharmony_ci				SelectedDrive, ReqTrack );
9328c2ecf20Sopenharmony_ci		/* we don't know exactly which track we are on now! */
9338c2ecf20Sopenharmony_ci		SUD.track = -1;
9348c2ecf20Sopenharmony_ci		fd_error();
9358c2ecf20Sopenharmony_ci	}
9368c2ecf20Sopenharmony_ci	else {
9378c2ecf20Sopenharmony_ci		SUD.track = ReqTrack << SUDT->stretch;
9388c2ecf20Sopenharmony_ci		NeedSeek = 0;
9398c2ecf20Sopenharmony_ci		if (IsFormatting)
9408c2ecf20Sopenharmony_ci			fd_writetrack();
9418c2ecf20Sopenharmony_ci		else
9428c2ecf20Sopenharmony_ci			fd_rwsec();
9438c2ecf20Sopenharmony_ci	}
9448c2ecf20Sopenharmony_ci}
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_ci
9478c2ecf20Sopenharmony_ci/* This does the actual reading/writing after positioning the head
9488c2ecf20Sopenharmony_ci * over the correct track.
9498c2ecf20Sopenharmony_ci */
9508c2ecf20Sopenharmony_ci
9518c2ecf20Sopenharmony_cistatic int MultReadInProgress = 0;
9528c2ecf20Sopenharmony_ci
9538c2ecf20Sopenharmony_ci
9548c2ecf20Sopenharmony_cistatic void fd_rwsec( void )
9558c2ecf20Sopenharmony_ci{
9568c2ecf20Sopenharmony_ci	unsigned long paddr, flags;
9578c2ecf20Sopenharmony_ci	unsigned int  rwflag, old_motoron;
9588c2ecf20Sopenharmony_ci	unsigned int track;
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_ci	DPRINT(("fd_rwsec(), Sec=%d, Access=%c\n",ReqSector, ReqCmd == WRITE ? 'w' : 'r' ));
9618c2ecf20Sopenharmony_ci	if (ReqCmd == WRITE) {
9628c2ecf20Sopenharmony_ci		if (ATARIHW_PRESENT(EXTD_DMA)) {
9638c2ecf20Sopenharmony_ci			paddr = virt_to_phys(ReqData);
9648c2ecf20Sopenharmony_ci		}
9658c2ecf20Sopenharmony_ci		else {
9668c2ecf20Sopenharmony_ci			copy_buffer( ReqData, DMABuffer );
9678c2ecf20Sopenharmony_ci			paddr = PhysDMABuffer;
9688c2ecf20Sopenharmony_ci		}
9698c2ecf20Sopenharmony_ci		dma_cache_maintenance( paddr, 512, 1 );
9708c2ecf20Sopenharmony_ci		rwflag = 0x100;
9718c2ecf20Sopenharmony_ci	}
9728c2ecf20Sopenharmony_ci	else {
9738c2ecf20Sopenharmony_ci		if (read_track)
9748c2ecf20Sopenharmony_ci			paddr = PhysTrackBuffer;
9758c2ecf20Sopenharmony_ci		else
9768c2ecf20Sopenharmony_ci			paddr = ATARIHW_PRESENT(EXTD_DMA) ?
9778c2ecf20Sopenharmony_ci				virt_to_phys(ReqData) : PhysDMABuffer;
9788c2ecf20Sopenharmony_ci		rwflag = 0;
9798c2ecf20Sopenharmony_ci	}
9808c2ecf20Sopenharmony_ci
9818c2ecf20Sopenharmony_ci	fd_select_side( ReqSide );
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci	/* Start sector of this operation */
9848c2ecf20Sopenharmony_ci	FDC_WRITE( FDCREG_SECTOR, read_track ? 1 : ReqSector );
9858c2ecf20Sopenharmony_ci	MFPDELAY();
9868c2ecf20Sopenharmony_ci	/* Cheat for track if stretch != 0 */
9878c2ecf20Sopenharmony_ci	if (SUDT->stretch) {
9888c2ecf20Sopenharmony_ci		track = FDC_READ( FDCREG_TRACK);
9898c2ecf20Sopenharmony_ci		MFPDELAY();
9908c2ecf20Sopenharmony_ci		FDC_WRITE( FDCREG_TRACK, track >> SUDT->stretch);
9918c2ecf20Sopenharmony_ci	}
9928c2ecf20Sopenharmony_ci	udelay(25);
9938c2ecf20Sopenharmony_ci
9948c2ecf20Sopenharmony_ci	/* Setup DMA */
9958c2ecf20Sopenharmony_ci	local_irq_save(flags);
9968c2ecf20Sopenharmony_ci	dma_wd.dma_lo = (unsigned char)paddr;
9978c2ecf20Sopenharmony_ci	MFPDELAY();
9988c2ecf20Sopenharmony_ci	paddr >>= 8;
9998c2ecf20Sopenharmony_ci	dma_wd.dma_md = (unsigned char)paddr;
10008c2ecf20Sopenharmony_ci	MFPDELAY();
10018c2ecf20Sopenharmony_ci	paddr >>= 8;
10028c2ecf20Sopenharmony_ci	if (ATARIHW_PRESENT(EXTD_DMA))
10038c2ecf20Sopenharmony_ci		st_dma_ext_dmahi = (unsigned short)paddr;
10048c2ecf20Sopenharmony_ci	else
10058c2ecf20Sopenharmony_ci		dma_wd.dma_hi = (unsigned char)paddr;
10068c2ecf20Sopenharmony_ci	MFPDELAY();
10078c2ecf20Sopenharmony_ci	local_irq_restore(flags);
10088c2ecf20Sopenharmony_ci
10098c2ecf20Sopenharmony_ci	/* Clear FIFO and switch DMA to correct mode */
10108c2ecf20Sopenharmony_ci	dma_wd.dma_mode_status = 0x90 | rwflag;
10118c2ecf20Sopenharmony_ci	MFPDELAY();
10128c2ecf20Sopenharmony_ci	dma_wd.dma_mode_status = 0x90 | (rwflag ^ 0x100);
10138c2ecf20Sopenharmony_ci	MFPDELAY();
10148c2ecf20Sopenharmony_ci	dma_wd.dma_mode_status = 0x90 | rwflag;
10158c2ecf20Sopenharmony_ci	MFPDELAY();
10168c2ecf20Sopenharmony_ci
10178c2ecf20Sopenharmony_ci	/* How many sectors for DMA */
10188c2ecf20Sopenharmony_ci	dma_wd.fdc_acces_seccount = read_track ? SUDT->spt : 1;
10198c2ecf20Sopenharmony_ci
10208c2ecf20Sopenharmony_ci	udelay(25);
10218c2ecf20Sopenharmony_ci
10228c2ecf20Sopenharmony_ci	/* Start operation */
10238c2ecf20Sopenharmony_ci	dma_wd.dma_mode_status = FDCSELREG_STP | rwflag;
10248c2ecf20Sopenharmony_ci	udelay(25);
10258c2ecf20Sopenharmony_ci	SET_IRQ_HANDLER( fd_rwsec_done );
10268c2ecf20Sopenharmony_ci	dma_wd.fdc_acces_seccount =
10278c2ecf20Sopenharmony_ci	  (get_head_settle_flag() |
10288c2ecf20Sopenharmony_ci	   (rwflag ? FDCCMD_WRSEC : (FDCCMD_RDSEC | (read_track ? FDCCMDADD_M : 0))));
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_ci	old_motoron = MotorOn;
10318c2ecf20Sopenharmony_ci	MotorOn = 1;
10328c2ecf20Sopenharmony_ci	NeedSeek = 1;
10338c2ecf20Sopenharmony_ci	/* wait for interrupt */
10348c2ecf20Sopenharmony_ci
10358c2ecf20Sopenharmony_ci	if (read_track) {
10368c2ecf20Sopenharmony_ci		/* If reading a whole track, wait about one disk rotation and
10378c2ecf20Sopenharmony_ci		 * then check if all sectors are read. The FDC will even
10388c2ecf20Sopenharmony_ci		 * search for the first non-existent sector and need 1 sec to
10398c2ecf20Sopenharmony_ci		 * recognise that it isn't present :-(
10408c2ecf20Sopenharmony_ci		 */
10418c2ecf20Sopenharmony_ci		MultReadInProgress = 1;
10428c2ecf20Sopenharmony_ci		mod_timer(&readtrack_timer,
10438c2ecf20Sopenharmony_ci			  /* 1 rot. + 5 rot.s if motor was off  */
10448c2ecf20Sopenharmony_ci			  jiffies + HZ/5 + (old_motoron ? 0 : HZ));
10458c2ecf20Sopenharmony_ci	}
10468c2ecf20Sopenharmony_ci	start_timeout();
10478c2ecf20Sopenharmony_ci}
10488c2ecf20Sopenharmony_ci
10498c2ecf20Sopenharmony_ci
10508c2ecf20Sopenharmony_cistatic void fd_readtrack_check(struct timer_list *unused)
10518c2ecf20Sopenharmony_ci{
10528c2ecf20Sopenharmony_ci	unsigned long flags, addr, addr2;
10538c2ecf20Sopenharmony_ci
10548c2ecf20Sopenharmony_ci	local_irq_save(flags);
10558c2ecf20Sopenharmony_ci
10568c2ecf20Sopenharmony_ci	if (!MultReadInProgress) {
10578c2ecf20Sopenharmony_ci		/* This prevents a race condition that could arise if the
10588c2ecf20Sopenharmony_ci		 * interrupt is triggered while the calling of this timer
10598c2ecf20Sopenharmony_ci		 * callback function takes place. The IRQ function then has
10608c2ecf20Sopenharmony_ci		 * already cleared 'MultReadInProgress'  when flow of control
10618c2ecf20Sopenharmony_ci		 * gets here.
10628c2ecf20Sopenharmony_ci		 */
10638c2ecf20Sopenharmony_ci		local_irq_restore(flags);
10648c2ecf20Sopenharmony_ci		return;
10658c2ecf20Sopenharmony_ci	}
10668c2ecf20Sopenharmony_ci
10678c2ecf20Sopenharmony_ci	/* get the current DMA address */
10688c2ecf20Sopenharmony_ci	/* ++ f.a. read twice to avoid being fooled by switcher */
10698c2ecf20Sopenharmony_ci	addr = 0;
10708c2ecf20Sopenharmony_ci	do {
10718c2ecf20Sopenharmony_ci		addr2 = addr;
10728c2ecf20Sopenharmony_ci		addr = dma_wd.dma_lo & 0xff;
10738c2ecf20Sopenharmony_ci		MFPDELAY();
10748c2ecf20Sopenharmony_ci		addr |= (dma_wd.dma_md & 0xff) << 8;
10758c2ecf20Sopenharmony_ci		MFPDELAY();
10768c2ecf20Sopenharmony_ci		if (ATARIHW_PRESENT( EXTD_DMA ))
10778c2ecf20Sopenharmony_ci			addr |= (st_dma_ext_dmahi & 0xffff) << 16;
10788c2ecf20Sopenharmony_ci		else
10798c2ecf20Sopenharmony_ci			addr |= (dma_wd.dma_hi & 0xff) << 16;
10808c2ecf20Sopenharmony_ci		MFPDELAY();
10818c2ecf20Sopenharmony_ci	} while(addr != addr2);
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_ci	if (addr >= PhysTrackBuffer + SUDT->spt*512) {
10848c2ecf20Sopenharmony_ci		/* already read enough data, force an FDC interrupt to stop
10858c2ecf20Sopenharmony_ci		 * the read operation
10868c2ecf20Sopenharmony_ci		 */
10878c2ecf20Sopenharmony_ci		SET_IRQ_HANDLER( NULL );
10888c2ecf20Sopenharmony_ci		MultReadInProgress = 0;
10898c2ecf20Sopenharmony_ci		local_irq_restore(flags);
10908c2ecf20Sopenharmony_ci		DPRINT(("fd_readtrack_check(): done\n"));
10918c2ecf20Sopenharmony_ci		FDC_WRITE( FDCREG_CMD, FDCCMD_FORCI );
10928c2ecf20Sopenharmony_ci		udelay(25);
10938c2ecf20Sopenharmony_ci
10948c2ecf20Sopenharmony_ci		/* No error until now -- the FDC would have interrupted
10958c2ecf20Sopenharmony_ci		 * otherwise!
10968c2ecf20Sopenharmony_ci		 */
10978c2ecf20Sopenharmony_ci		fd_rwsec_done1(0);
10988c2ecf20Sopenharmony_ci	}
10998c2ecf20Sopenharmony_ci	else {
11008c2ecf20Sopenharmony_ci		/* not yet finished, wait another tenth rotation */
11018c2ecf20Sopenharmony_ci		local_irq_restore(flags);
11028c2ecf20Sopenharmony_ci		DPRINT(("fd_readtrack_check(): not yet finished\n"));
11038c2ecf20Sopenharmony_ci		mod_timer(&readtrack_timer, jiffies + HZ/5/10);
11048c2ecf20Sopenharmony_ci	}
11058c2ecf20Sopenharmony_ci}
11068c2ecf20Sopenharmony_ci
11078c2ecf20Sopenharmony_ci
11088c2ecf20Sopenharmony_cistatic void fd_rwsec_done( int status )
11098c2ecf20Sopenharmony_ci{
11108c2ecf20Sopenharmony_ci	DPRINT(("fd_rwsec_done()\n"));
11118c2ecf20Sopenharmony_ci
11128c2ecf20Sopenharmony_ci	if (read_track) {
11138c2ecf20Sopenharmony_ci		del_timer(&readtrack_timer);
11148c2ecf20Sopenharmony_ci		if (!MultReadInProgress)
11158c2ecf20Sopenharmony_ci			return;
11168c2ecf20Sopenharmony_ci		MultReadInProgress = 0;
11178c2ecf20Sopenharmony_ci	}
11188c2ecf20Sopenharmony_ci	fd_rwsec_done1(status);
11198c2ecf20Sopenharmony_ci}
11208c2ecf20Sopenharmony_ci
11218c2ecf20Sopenharmony_cistatic void fd_rwsec_done1(int status)
11228c2ecf20Sopenharmony_ci{
11238c2ecf20Sopenharmony_ci	unsigned int track;
11248c2ecf20Sopenharmony_ci
11258c2ecf20Sopenharmony_ci	stop_timeout();
11268c2ecf20Sopenharmony_ci
11278c2ecf20Sopenharmony_ci	/* Correct the track if stretch != 0 */
11288c2ecf20Sopenharmony_ci	if (SUDT->stretch) {
11298c2ecf20Sopenharmony_ci		track = FDC_READ( FDCREG_TRACK);
11308c2ecf20Sopenharmony_ci		MFPDELAY();
11318c2ecf20Sopenharmony_ci		FDC_WRITE( FDCREG_TRACK, track << SUDT->stretch);
11328c2ecf20Sopenharmony_ci	}
11338c2ecf20Sopenharmony_ci
11348c2ecf20Sopenharmony_ci	if (!UseTrackbuffer) {
11358c2ecf20Sopenharmony_ci		dma_wd.dma_mode_status = 0x90;
11368c2ecf20Sopenharmony_ci		MFPDELAY();
11378c2ecf20Sopenharmony_ci		if (!(dma_wd.dma_mode_status & 0x01)) {
11388c2ecf20Sopenharmony_ci			printk(KERN_ERR "fd%d: DMA error\n", SelectedDrive );
11398c2ecf20Sopenharmony_ci			goto err_end;
11408c2ecf20Sopenharmony_ci		}
11418c2ecf20Sopenharmony_ci	}
11428c2ecf20Sopenharmony_ci	MFPDELAY();
11438c2ecf20Sopenharmony_ci
11448c2ecf20Sopenharmony_ci	if (ReqCmd == WRITE && (status & FDCSTAT_WPROT)) {
11458c2ecf20Sopenharmony_ci		printk(KERN_NOTICE "fd%d: is write protected\n", SelectedDrive );
11468c2ecf20Sopenharmony_ci		goto err_end;
11478c2ecf20Sopenharmony_ci	}
11488c2ecf20Sopenharmony_ci	if ((status & FDCSTAT_RECNF) &&
11498c2ecf20Sopenharmony_ci	    /* RECNF is no error after a multiple read when the FDC
11508c2ecf20Sopenharmony_ci	       searched for a non-existent sector! */
11518c2ecf20Sopenharmony_ci	    !(read_track && FDC_READ(FDCREG_SECTOR) > SUDT->spt)) {
11528c2ecf20Sopenharmony_ci		if (Probing) {
11538c2ecf20Sopenharmony_ci			if (SUDT > atari_disk_type) {
11548c2ecf20Sopenharmony_ci			    if (SUDT[-1].blocks > ReqBlock) {
11558c2ecf20Sopenharmony_ci				/* try another disk type */
11568c2ecf20Sopenharmony_ci				SUDT--;
11578c2ecf20Sopenharmony_ci				set_capacity(unit[SelectedDrive].disk,
11588c2ecf20Sopenharmony_ci							SUDT->blocks);
11598c2ecf20Sopenharmony_ci			    } else
11608c2ecf20Sopenharmony_ci				Probing = 0;
11618c2ecf20Sopenharmony_ci			}
11628c2ecf20Sopenharmony_ci			else {
11638c2ecf20Sopenharmony_ci				if (SUD.flags & FTD_MSG)
11648c2ecf20Sopenharmony_ci					printk(KERN_INFO "fd%d: Auto-detected floppy type %s\n",
11658c2ecf20Sopenharmony_ci					       SelectedDrive, SUDT->name );
11668c2ecf20Sopenharmony_ci				Probing=0;
11678c2ecf20Sopenharmony_ci			}
11688c2ecf20Sopenharmony_ci		} else {
11698c2ecf20Sopenharmony_ci/* record not found, but not probing. Maybe stretch wrong ? Restart probing */
11708c2ecf20Sopenharmony_ci			if (SUD.autoprobe) {
11718c2ecf20Sopenharmony_ci				SUDT = atari_disk_type + StartDiskType[DriveType];
11728c2ecf20Sopenharmony_ci				set_capacity(unit[SelectedDrive].disk,
11738c2ecf20Sopenharmony_ci							SUDT->blocks);
11748c2ecf20Sopenharmony_ci				Probing = 1;
11758c2ecf20Sopenharmony_ci			}
11768c2ecf20Sopenharmony_ci		}
11778c2ecf20Sopenharmony_ci		if (Probing) {
11788c2ecf20Sopenharmony_ci			if (ATARIHW_PRESENT(FDCSPEED)) {
11798c2ecf20Sopenharmony_ci				dma_wd.fdc_speed = SUDT->fdc_speed;
11808c2ecf20Sopenharmony_ci				MFPDELAY();
11818c2ecf20Sopenharmony_ci			}
11828c2ecf20Sopenharmony_ci			setup_req_params( SelectedDrive );
11838c2ecf20Sopenharmony_ci			BufferDrive = -1;
11848c2ecf20Sopenharmony_ci			do_fd_action( SelectedDrive );
11858c2ecf20Sopenharmony_ci			return;
11868c2ecf20Sopenharmony_ci		}
11878c2ecf20Sopenharmony_ci
11888c2ecf20Sopenharmony_ci		printk(KERN_ERR "fd%d: sector %d not found (side %d, track %d)\n",
11898c2ecf20Sopenharmony_ci		       SelectedDrive, FDC_READ (FDCREG_SECTOR), ReqSide, ReqTrack );
11908c2ecf20Sopenharmony_ci		goto err_end;
11918c2ecf20Sopenharmony_ci	}
11928c2ecf20Sopenharmony_ci	if (status & FDCSTAT_CRC) {
11938c2ecf20Sopenharmony_ci		printk(KERN_ERR "fd%d: CRC error (side %d, track %d, sector %d)\n",
11948c2ecf20Sopenharmony_ci		       SelectedDrive, ReqSide, ReqTrack, FDC_READ (FDCREG_SECTOR) );
11958c2ecf20Sopenharmony_ci		goto err_end;
11968c2ecf20Sopenharmony_ci	}
11978c2ecf20Sopenharmony_ci	if (status & FDCSTAT_LOST) {
11988c2ecf20Sopenharmony_ci		printk(KERN_ERR "fd%d: lost data (side %d, track %d, sector %d)\n",
11998c2ecf20Sopenharmony_ci		       SelectedDrive, ReqSide, ReqTrack, FDC_READ (FDCREG_SECTOR) );
12008c2ecf20Sopenharmony_ci		goto err_end;
12018c2ecf20Sopenharmony_ci	}
12028c2ecf20Sopenharmony_ci
12038c2ecf20Sopenharmony_ci	Probing = 0;
12048c2ecf20Sopenharmony_ci
12058c2ecf20Sopenharmony_ci	if (ReqCmd == READ) {
12068c2ecf20Sopenharmony_ci		if (!read_track) {
12078c2ecf20Sopenharmony_ci			void *addr;
12088c2ecf20Sopenharmony_ci			addr = ATARIHW_PRESENT( EXTD_DMA ) ? ReqData : DMABuffer;
12098c2ecf20Sopenharmony_ci			dma_cache_maintenance( virt_to_phys(addr), 512, 0 );
12108c2ecf20Sopenharmony_ci			if (!ATARIHW_PRESENT( EXTD_DMA ))
12118c2ecf20Sopenharmony_ci				copy_buffer (addr, ReqData);
12128c2ecf20Sopenharmony_ci		} else {
12138c2ecf20Sopenharmony_ci			dma_cache_maintenance( PhysTrackBuffer, MaxSectors[DriveType] * 512, 0 );
12148c2ecf20Sopenharmony_ci			BufferDrive = SelectedDrive;
12158c2ecf20Sopenharmony_ci			BufferSide  = ReqSide;
12168c2ecf20Sopenharmony_ci			BufferTrack = ReqTrack;
12178c2ecf20Sopenharmony_ci			copy_buffer (SECTOR_BUFFER (ReqSector), ReqData);
12188c2ecf20Sopenharmony_ci		}
12198c2ecf20Sopenharmony_ci	}
12208c2ecf20Sopenharmony_ci
12218c2ecf20Sopenharmony_ci	if (++ReqCnt < blk_rq_cur_sectors(fd_request)) {
12228c2ecf20Sopenharmony_ci		/* read next sector */
12238c2ecf20Sopenharmony_ci		setup_req_params( SelectedDrive );
12248c2ecf20Sopenharmony_ci		do_fd_action( SelectedDrive );
12258c2ecf20Sopenharmony_ci	}
12268c2ecf20Sopenharmony_ci	else {
12278c2ecf20Sopenharmony_ci		/* all sectors finished */
12288c2ecf20Sopenharmony_ci		fd_end_request_cur(BLK_STS_OK);
12298c2ecf20Sopenharmony_ci	}
12308c2ecf20Sopenharmony_ci	return;
12318c2ecf20Sopenharmony_ci
12328c2ecf20Sopenharmony_ci  err_end:
12338c2ecf20Sopenharmony_ci	BufferDrive = -1;
12348c2ecf20Sopenharmony_ci	fd_error();
12358c2ecf20Sopenharmony_ci}
12368c2ecf20Sopenharmony_ci
12378c2ecf20Sopenharmony_ci
12388c2ecf20Sopenharmony_cistatic void fd_writetrack( void )
12398c2ecf20Sopenharmony_ci{
12408c2ecf20Sopenharmony_ci	unsigned long paddr, flags;
12418c2ecf20Sopenharmony_ci	unsigned int track;
12428c2ecf20Sopenharmony_ci
12438c2ecf20Sopenharmony_ci	DPRINT(("fd_writetrack() Tr=%d Si=%d\n", ReqTrack, ReqSide ));
12448c2ecf20Sopenharmony_ci
12458c2ecf20Sopenharmony_ci	paddr = PhysTrackBuffer;
12468c2ecf20Sopenharmony_ci	dma_cache_maintenance( paddr, BUFFER_SIZE, 1 );
12478c2ecf20Sopenharmony_ci
12488c2ecf20Sopenharmony_ci	fd_select_side( ReqSide );
12498c2ecf20Sopenharmony_ci
12508c2ecf20Sopenharmony_ci	/* Cheat for track if stretch != 0 */
12518c2ecf20Sopenharmony_ci	if (SUDT->stretch) {
12528c2ecf20Sopenharmony_ci		track = FDC_READ( FDCREG_TRACK);
12538c2ecf20Sopenharmony_ci		MFPDELAY();
12548c2ecf20Sopenharmony_ci		FDC_WRITE(FDCREG_TRACK,track >> SUDT->stretch);
12558c2ecf20Sopenharmony_ci	}
12568c2ecf20Sopenharmony_ci	udelay(40);
12578c2ecf20Sopenharmony_ci
12588c2ecf20Sopenharmony_ci	/* Setup DMA */
12598c2ecf20Sopenharmony_ci	local_irq_save(flags);
12608c2ecf20Sopenharmony_ci	dma_wd.dma_lo = (unsigned char)paddr;
12618c2ecf20Sopenharmony_ci	MFPDELAY();
12628c2ecf20Sopenharmony_ci	paddr >>= 8;
12638c2ecf20Sopenharmony_ci	dma_wd.dma_md = (unsigned char)paddr;
12648c2ecf20Sopenharmony_ci	MFPDELAY();
12658c2ecf20Sopenharmony_ci	paddr >>= 8;
12668c2ecf20Sopenharmony_ci	if (ATARIHW_PRESENT( EXTD_DMA ))
12678c2ecf20Sopenharmony_ci		st_dma_ext_dmahi = (unsigned short)paddr;
12688c2ecf20Sopenharmony_ci	else
12698c2ecf20Sopenharmony_ci		dma_wd.dma_hi = (unsigned char)paddr;
12708c2ecf20Sopenharmony_ci	MFPDELAY();
12718c2ecf20Sopenharmony_ci	local_irq_restore(flags);
12728c2ecf20Sopenharmony_ci
12738c2ecf20Sopenharmony_ci	/* Clear FIFO and switch DMA to correct mode */
12748c2ecf20Sopenharmony_ci	dma_wd.dma_mode_status = 0x190;
12758c2ecf20Sopenharmony_ci	MFPDELAY();
12768c2ecf20Sopenharmony_ci	dma_wd.dma_mode_status = 0x90;
12778c2ecf20Sopenharmony_ci	MFPDELAY();
12788c2ecf20Sopenharmony_ci	dma_wd.dma_mode_status = 0x190;
12798c2ecf20Sopenharmony_ci	MFPDELAY();
12808c2ecf20Sopenharmony_ci
12818c2ecf20Sopenharmony_ci	/* How many sectors for DMA */
12828c2ecf20Sopenharmony_ci	dma_wd.fdc_acces_seccount = BUFFER_SIZE/512;
12838c2ecf20Sopenharmony_ci	udelay(40);
12848c2ecf20Sopenharmony_ci
12858c2ecf20Sopenharmony_ci	/* Start operation */
12868c2ecf20Sopenharmony_ci	dma_wd.dma_mode_status = FDCSELREG_STP | 0x100;
12878c2ecf20Sopenharmony_ci	udelay(40);
12888c2ecf20Sopenharmony_ci	SET_IRQ_HANDLER( fd_writetrack_done );
12898c2ecf20Sopenharmony_ci	dma_wd.fdc_acces_seccount = FDCCMD_WRTRA | get_head_settle_flag();
12908c2ecf20Sopenharmony_ci
12918c2ecf20Sopenharmony_ci	MotorOn = 1;
12928c2ecf20Sopenharmony_ci	start_timeout();
12938c2ecf20Sopenharmony_ci	/* wait for interrupt */
12948c2ecf20Sopenharmony_ci}
12958c2ecf20Sopenharmony_ci
12968c2ecf20Sopenharmony_ci
12978c2ecf20Sopenharmony_cistatic void fd_writetrack_done( int status )
12988c2ecf20Sopenharmony_ci{
12998c2ecf20Sopenharmony_ci	DPRINT(("fd_writetrack_done()\n"));
13008c2ecf20Sopenharmony_ci
13018c2ecf20Sopenharmony_ci	stop_timeout();
13028c2ecf20Sopenharmony_ci
13038c2ecf20Sopenharmony_ci	if (status & FDCSTAT_WPROT) {
13048c2ecf20Sopenharmony_ci		printk(KERN_NOTICE "fd%d: is write protected\n", SelectedDrive );
13058c2ecf20Sopenharmony_ci		goto err_end;
13068c2ecf20Sopenharmony_ci	}
13078c2ecf20Sopenharmony_ci	if (status & FDCSTAT_LOST) {
13088c2ecf20Sopenharmony_ci		printk(KERN_ERR "fd%d: lost data (side %d, track %d)\n",
13098c2ecf20Sopenharmony_ci				SelectedDrive, ReqSide, ReqTrack );
13108c2ecf20Sopenharmony_ci		goto err_end;
13118c2ecf20Sopenharmony_ci	}
13128c2ecf20Sopenharmony_ci
13138c2ecf20Sopenharmony_ci	complete(&format_wait);
13148c2ecf20Sopenharmony_ci	return;
13158c2ecf20Sopenharmony_ci
13168c2ecf20Sopenharmony_ci  err_end:
13178c2ecf20Sopenharmony_ci	fd_error();
13188c2ecf20Sopenharmony_ci}
13198c2ecf20Sopenharmony_ci
13208c2ecf20Sopenharmony_cistatic void fd_times_out(struct timer_list *unused)
13218c2ecf20Sopenharmony_ci{
13228c2ecf20Sopenharmony_ci	atari_disable_irq( IRQ_MFP_FDC );
13238c2ecf20Sopenharmony_ci	if (!FloppyIRQHandler) goto end; /* int occurred after timer was fired, but
13248c2ecf20Sopenharmony_ci					  * before we came here... */
13258c2ecf20Sopenharmony_ci
13268c2ecf20Sopenharmony_ci	SET_IRQ_HANDLER( NULL );
13278c2ecf20Sopenharmony_ci	/* If the timeout occurred while the readtrack_check timer was
13288c2ecf20Sopenharmony_ci	 * active, we need to cancel it, else bad things will happen */
13298c2ecf20Sopenharmony_ci	if (UseTrackbuffer)
13308c2ecf20Sopenharmony_ci		del_timer( &readtrack_timer );
13318c2ecf20Sopenharmony_ci	FDC_WRITE( FDCREG_CMD, FDCCMD_FORCI );
13328c2ecf20Sopenharmony_ci	udelay( 25 );
13338c2ecf20Sopenharmony_ci
13348c2ecf20Sopenharmony_ci	printk(KERN_ERR "floppy timeout\n" );
13358c2ecf20Sopenharmony_ci	fd_error();
13368c2ecf20Sopenharmony_ci  end:
13378c2ecf20Sopenharmony_ci	atari_enable_irq( IRQ_MFP_FDC );
13388c2ecf20Sopenharmony_ci}
13398c2ecf20Sopenharmony_ci
13408c2ecf20Sopenharmony_ci
13418c2ecf20Sopenharmony_ci/* The (noop) seek operation here is needed to make the WP bit in the
13428c2ecf20Sopenharmony_ci * FDC status register accessible for check_change. If the last disk
13438c2ecf20Sopenharmony_ci * operation would have been a RDSEC, this bit would always read as 0
13448c2ecf20Sopenharmony_ci * no matter what :-( To save time, the seek goes to the track we're
13458c2ecf20Sopenharmony_ci * already on.
13468c2ecf20Sopenharmony_ci */
13478c2ecf20Sopenharmony_ci
13488c2ecf20Sopenharmony_cistatic void finish_fdc( void )
13498c2ecf20Sopenharmony_ci{
13508c2ecf20Sopenharmony_ci	if (!NeedSeek) {
13518c2ecf20Sopenharmony_ci		finish_fdc_done( 0 );
13528c2ecf20Sopenharmony_ci	}
13538c2ecf20Sopenharmony_ci	else {
13548c2ecf20Sopenharmony_ci		DPRINT(("finish_fdc: dummy seek started\n"));
13558c2ecf20Sopenharmony_ci		FDC_WRITE (FDCREG_DATA, SUD.track);
13568c2ecf20Sopenharmony_ci		SET_IRQ_HANDLER( finish_fdc_done );
13578c2ecf20Sopenharmony_ci		FDC_WRITE (FDCREG_CMD, FDCCMD_SEEK);
13588c2ecf20Sopenharmony_ci		MotorOn = 1;
13598c2ecf20Sopenharmony_ci		start_timeout();
13608c2ecf20Sopenharmony_ci		/* we must wait for the IRQ here, because the ST-DMA
13618c2ecf20Sopenharmony_ci		   is released immediately afterwards and the interrupt
13628c2ecf20Sopenharmony_ci		   may be delivered to the wrong driver. */
13638c2ecf20Sopenharmony_ci	  }
13648c2ecf20Sopenharmony_ci}
13658c2ecf20Sopenharmony_ci
13668c2ecf20Sopenharmony_ci
13678c2ecf20Sopenharmony_cistatic void finish_fdc_done( int dummy )
13688c2ecf20Sopenharmony_ci{
13698c2ecf20Sopenharmony_ci	unsigned long flags;
13708c2ecf20Sopenharmony_ci
13718c2ecf20Sopenharmony_ci	DPRINT(("finish_fdc_done entered\n"));
13728c2ecf20Sopenharmony_ci	stop_timeout();
13738c2ecf20Sopenharmony_ci	NeedSeek = 0;
13748c2ecf20Sopenharmony_ci
13758c2ecf20Sopenharmony_ci	if (timer_pending(&fd_timer) && time_before(fd_timer.expires, jiffies + 5))
13768c2ecf20Sopenharmony_ci		/* If the check for a disk change is done too early after this
13778c2ecf20Sopenharmony_ci		 * last seek command, the WP bit still reads wrong :-((
13788c2ecf20Sopenharmony_ci		 */
13798c2ecf20Sopenharmony_ci		mod_timer(&fd_timer, jiffies + 5);
13808c2ecf20Sopenharmony_ci	else
13818c2ecf20Sopenharmony_ci		start_check_change_timer();
13828c2ecf20Sopenharmony_ci	start_motor_off_timer();
13838c2ecf20Sopenharmony_ci
13848c2ecf20Sopenharmony_ci	local_irq_save(flags);
13858c2ecf20Sopenharmony_ci	stdma_release();
13868c2ecf20Sopenharmony_ci	local_irq_restore(flags);
13878c2ecf20Sopenharmony_ci
13888c2ecf20Sopenharmony_ci	DPRINT(("finish_fdc() finished\n"));
13898c2ecf20Sopenharmony_ci}
13908c2ecf20Sopenharmony_ci
13918c2ecf20Sopenharmony_ci/* The detection of disk changes is a dark chapter in Atari history :-(
13928c2ecf20Sopenharmony_ci * Because the "Drive ready" signal isn't present in the Atari
13938c2ecf20Sopenharmony_ci * hardware, one has to rely on the "Write Protect". This works fine,
13948c2ecf20Sopenharmony_ci * as long as no write protected disks are used. TOS solves this
13958c2ecf20Sopenharmony_ci * problem by introducing tri-state logic ("maybe changed") and
13968c2ecf20Sopenharmony_ci * looking at the serial number in block 0. This isn't possible for
13978c2ecf20Sopenharmony_ci * Linux, since the floppy driver can't make assumptions about the
13988c2ecf20Sopenharmony_ci * filesystem used on the disk and thus the contents of block 0. I've
13998c2ecf20Sopenharmony_ci * chosen the method to always say "The disk was changed" if it is
14008c2ecf20Sopenharmony_ci * unsure whether it was. This implies that every open or mount
14018c2ecf20Sopenharmony_ci * invalidates the disk buffers if you work with write protected
14028c2ecf20Sopenharmony_ci * disks. But at least this is better than working with incorrect data
14038c2ecf20Sopenharmony_ci * due to unrecognised disk changes.
14048c2ecf20Sopenharmony_ci */
14058c2ecf20Sopenharmony_ci
14068c2ecf20Sopenharmony_cistatic unsigned int floppy_check_events(struct gendisk *disk,
14078c2ecf20Sopenharmony_ci					unsigned int clearing)
14088c2ecf20Sopenharmony_ci{
14098c2ecf20Sopenharmony_ci	struct atari_floppy_struct *p = disk->private_data;
14108c2ecf20Sopenharmony_ci	unsigned int drive = p - unit;
14118c2ecf20Sopenharmony_ci	if (test_bit (drive, &fake_change)) {
14128c2ecf20Sopenharmony_ci		/* simulated change (e.g. after formatting) */
14138c2ecf20Sopenharmony_ci		return DISK_EVENT_MEDIA_CHANGE;
14148c2ecf20Sopenharmony_ci	}
14158c2ecf20Sopenharmony_ci	if (test_bit (drive, &changed_floppies)) {
14168c2ecf20Sopenharmony_ci		/* surely changed (the WP signal changed at least once) */
14178c2ecf20Sopenharmony_ci		return DISK_EVENT_MEDIA_CHANGE;
14188c2ecf20Sopenharmony_ci	}
14198c2ecf20Sopenharmony_ci	if (UD.wpstat) {
14208c2ecf20Sopenharmony_ci		/* WP is on -> could be changed: to be sure, buffers should be
14218c2ecf20Sopenharmony_ci		 * invalidated...
14228c2ecf20Sopenharmony_ci		 */
14238c2ecf20Sopenharmony_ci		return DISK_EVENT_MEDIA_CHANGE;
14248c2ecf20Sopenharmony_ci	}
14258c2ecf20Sopenharmony_ci
14268c2ecf20Sopenharmony_ci	return 0;
14278c2ecf20Sopenharmony_ci}
14288c2ecf20Sopenharmony_ci
14298c2ecf20Sopenharmony_cistatic int floppy_revalidate(struct gendisk *disk)
14308c2ecf20Sopenharmony_ci{
14318c2ecf20Sopenharmony_ci	struct atari_floppy_struct *p = disk->private_data;
14328c2ecf20Sopenharmony_ci	unsigned int drive = p - unit;
14338c2ecf20Sopenharmony_ci
14348c2ecf20Sopenharmony_ci	if (test_bit(drive, &changed_floppies) ||
14358c2ecf20Sopenharmony_ci	    test_bit(drive, &fake_change) ||
14368c2ecf20Sopenharmony_ci	    p->disktype == 0) {
14378c2ecf20Sopenharmony_ci		if (UD.flags & FTD_MSG)
14388c2ecf20Sopenharmony_ci			printk(KERN_ERR "floppy: clear format %p!\n", UDT);
14398c2ecf20Sopenharmony_ci		BufferDrive = -1;
14408c2ecf20Sopenharmony_ci		clear_bit(drive, &fake_change);
14418c2ecf20Sopenharmony_ci		clear_bit(drive, &changed_floppies);
14428c2ecf20Sopenharmony_ci		/* MSch: clearing geometry makes sense only for autoprobe
14438c2ecf20Sopenharmony_ci		   formats, for 'permanent user-defined' parameter:
14448c2ecf20Sopenharmony_ci		   restore default_params[] here if flagged valid! */
14458c2ecf20Sopenharmony_ci		if (default_params[drive].blocks == 0)
14468c2ecf20Sopenharmony_ci			UDT = NULL;
14478c2ecf20Sopenharmony_ci		else
14488c2ecf20Sopenharmony_ci			UDT = &default_params[drive];
14498c2ecf20Sopenharmony_ci	}
14508c2ecf20Sopenharmony_ci	return 0;
14518c2ecf20Sopenharmony_ci}
14528c2ecf20Sopenharmony_ci
14538c2ecf20Sopenharmony_ci
14548c2ecf20Sopenharmony_ci/* This sets up the global variables describing the current request. */
14558c2ecf20Sopenharmony_ci
14568c2ecf20Sopenharmony_cistatic void setup_req_params( int drive )
14578c2ecf20Sopenharmony_ci{
14588c2ecf20Sopenharmony_ci	int block = ReqBlock + ReqCnt;
14598c2ecf20Sopenharmony_ci
14608c2ecf20Sopenharmony_ci	ReqTrack = block / UDT->spt;
14618c2ecf20Sopenharmony_ci	ReqSector = block - ReqTrack * UDT->spt + 1;
14628c2ecf20Sopenharmony_ci	ReqSide = ReqTrack & 1;
14638c2ecf20Sopenharmony_ci	ReqTrack >>= 1;
14648c2ecf20Sopenharmony_ci	ReqData = ReqBuffer + 512 * ReqCnt;
14658c2ecf20Sopenharmony_ci
14668c2ecf20Sopenharmony_ci	if (UseTrackbuffer)
14678c2ecf20Sopenharmony_ci		read_track = (ReqCmd == READ && fd_request->error_count == 0);
14688c2ecf20Sopenharmony_ci	else
14698c2ecf20Sopenharmony_ci		read_track = 0;
14708c2ecf20Sopenharmony_ci
14718c2ecf20Sopenharmony_ci	DPRINT(("Request params: Si=%d Tr=%d Se=%d Data=%08lx\n",ReqSide,
14728c2ecf20Sopenharmony_ci			ReqTrack, ReqSector, (unsigned long)ReqData ));
14738c2ecf20Sopenharmony_ci}
14748c2ecf20Sopenharmony_ci
14758c2ecf20Sopenharmony_cistatic void ataflop_commit_rqs(struct blk_mq_hw_ctx *hctx)
14768c2ecf20Sopenharmony_ci{
14778c2ecf20Sopenharmony_ci	spin_lock_irq(&ataflop_lock);
14788c2ecf20Sopenharmony_ci	atari_disable_irq(IRQ_MFP_FDC);
14798c2ecf20Sopenharmony_ci	finish_fdc();
14808c2ecf20Sopenharmony_ci	atari_enable_irq(IRQ_MFP_FDC);
14818c2ecf20Sopenharmony_ci	spin_unlock_irq(&ataflop_lock);
14828c2ecf20Sopenharmony_ci}
14838c2ecf20Sopenharmony_ci
14848c2ecf20Sopenharmony_cistatic blk_status_t ataflop_queue_rq(struct blk_mq_hw_ctx *hctx,
14858c2ecf20Sopenharmony_ci				     const struct blk_mq_queue_data *bd)
14868c2ecf20Sopenharmony_ci{
14878c2ecf20Sopenharmony_ci	struct atari_floppy_struct *floppy = bd->rq->rq_disk->private_data;
14888c2ecf20Sopenharmony_ci	int drive = floppy - unit;
14898c2ecf20Sopenharmony_ci	int type = floppy->type;
14908c2ecf20Sopenharmony_ci
14918c2ecf20Sopenharmony_ci	spin_lock_irq(&ataflop_lock);
14928c2ecf20Sopenharmony_ci	if (fd_request) {
14938c2ecf20Sopenharmony_ci		spin_unlock_irq(&ataflop_lock);
14948c2ecf20Sopenharmony_ci		return BLK_STS_DEV_RESOURCE;
14958c2ecf20Sopenharmony_ci	}
14968c2ecf20Sopenharmony_ci	if (!stdma_try_lock(floppy_irq, NULL))  {
14978c2ecf20Sopenharmony_ci		spin_unlock_irq(&ataflop_lock);
14988c2ecf20Sopenharmony_ci		return BLK_STS_RESOURCE;
14998c2ecf20Sopenharmony_ci	}
15008c2ecf20Sopenharmony_ci	fd_request = bd->rq;
15018c2ecf20Sopenharmony_ci	blk_mq_start_request(fd_request);
15028c2ecf20Sopenharmony_ci
15038c2ecf20Sopenharmony_ci	atari_disable_irq( IRQ_MFP_FDC );
15048c2ecf20Sopenharmony_ci
15058c2ecf20Sopenharmony_ci	IsFormatting = 0;
15068c2ecf20Sopenharmony_ci
15078c2ecf20Sopenharmony_ci	if (!UD.connected) {
15088c2ecf20Sopenharmony_ci		/* drive not connected */
15098c2ecf20Sopenharmony_ci		printk(KERN_ERR "Unknown Device: fd%d\n", drive );
15108c2ecf20Sopenharmony_ci		fd_end_request_cur(BLK_STS_IOERR);
15118c2ecf20Sopenharmony_ci		goto out;
15128c2ecf20Sopenharmony_ci	}
15138c2ecf20Sopenharmony_ci
15148c2ecf20Sopenharmony_ci	if (type == 0) {
15158c2ecf20Sopenharmony_ci		if (!UDT) {
15168c2ecf20Sopenharmony_ci			Probing = 1;
15178c2ecf20Sopenharmony_ci			UDT = atari_disk_type + StartDiskType[DriveType];
15188c2ecf20Sopenharmony_ci			set_capacity(floppy->disk, UDT->blocks);
15198c2ecf20Sopenharmony_ci			UD.autoprobe = 1;
15208c2ecf20Sopenharmony_ci		}
15218c2ecf20Sopenharmony_ci	}
15228c2ecf20Sopenharmony_ci	else {
15238c2ecf20Sopenharmony_ci		/* user supplied disk type */
15248c2ecf20Sopenharmony_ci		if (--type >= NUM_DISK_MINORS) {
15258c2ecf20Sopenharmony_ci			printk(KERN_WARNING "fd%d: invalid disk format", drive );
15268c2ecf20Sopenharmony_ci			fd_end_request_cur(BLK_STS_IOERR);
15278c2ecf20Sopenharmony_ci			goto out;
15288c2ecf20Sopenharmony_ci		}
15298c2ecf20Sopenharmony_ci		if (minor2disktype[type].drive_types > DriveType)  {
15308c2ecf20Sopenharmony_ci			printk(KERN_WARNING "fd%d: unsupported disk format", drive );
15318c2ecf20Sopenharmony_ci			fd_end_request_cur(BLK_STS_IOERR);
15328c2ecf20Sopenharmony_ci			goto out;
15338c2ecf20Sopenharmony_ci		}
15348c2ecf20Sopenharmony_ci		type = minor2disktype[type].index;
15358c2ecf20Sopenharmony_ci		UDT = &atari_disk_type[type];
15368c2ecf20Sopenharmony_ci		set_capacity(floppy->disk, UDT->blocks);
15378c2ecf20Sopenharmony_ci		UD.autoprobe = 0;
15388c2ecf20Sopenharmony_ci	}
15398c2ecf20Sopenharmony_ci
15408c2ecf20Sopenharmony_ci	/* stop deselect timer */
15418c2ecf20Sopenharmony_ci	del_timer( &motor_off_timer );
15428c2ecf20Sopenharmony_ci
15438c2ecf20Sopenharmony_ci	ReqCnt = 0;
15448c2ecf20Sopenharmony_ci	ReqCmd = rq_data_dir(fd_request);
15458c2ecf20Sopenharmony_ci	ReqBlock = blk_rq_pos(fd_request);
15468c2ecf20Sopenharmony_ci	ReqBuffer = bio_data(fd_request->bio);
15478c2ecf20Sopenharmony_ci	setup_req_params( drive );
15488c2ecf20Sopenharmony_ci	do_fd_action( drive );
15498c2ecf20Sopenharmony_ci
15508c2ecf20Sopenharmony_ci	if (bd->last)
15518c2ecf20Sopenharmony_ci		finish_fdc();
15528c2ecf20Sopenharmony_ci	atari_enable_irq( IRQ_MFP_FDC );
15538c2ecf20Sopenharmony_ci
15548c2ecf20Sopenharmony_ciout:
15558c2ecf20Sopenharmony_ci	spin_unlock_irq(&ataflop_lock);
15568c2ecf20Sopenharmony_ci	return BLK_STS_OK;
15578c2ecf20Sopenharmony_ci}
15588c2ecf20Sopenharmony_ci
15598c2ecf20Sopenharmony_cistatic int fd_locked_ioctl(struct block_device *bdev, fmode_t mode,
15608c2ecf20Sopenharmony_ci		    unsigned int cmd, unsigned long param)
15618c2ecf20Sopenharmony_ci{
15628c2ecf20Sopenharmony_ci	struct gendisk *disk = bdev->bd_disk;
15638c2ecf20Sopenharmony_ci	struct atari_floppy_struct *floppy = disk->private_data;
15648c2ecf20Sopenharmony_ci	int drive = floppy - unit;
15658c2ecf20Sopenharmony_ci	int type = floppy->type;
15668c2ecf20Sopenharmony_ci	struct atari_format_descr fmt_desc;
15678c2ecf20Sopenharmony_ci	struct atari_disk_type *dtp;
15688c2ecf20Sopenharmony_ci	struct floppy_struct getprm;
15698c2ecf20Sopenharmony_ci	int settype;
15708c2ecf20Sopenharmony_ci	struct floppy_struct setprm;
15718c2ecf20Sopenharmony_ci	void __user *argp = (void __user *)param;
15728c2ecf20Sopenharmony_ci
15738c2ecf20Sopenharmony_ci	switch (cmd) {
15748c2ecf20Sopenharmony_ci	case FDGETPRM:
15758c2ecf20Sopenharmony_ci		if (type) {
15768c2ecf20Sopenharmony_ci			if (--type >= NUM_DISK_MINORS)
15778c2ecf20Sopenharmony_ci				return -ENODEV;
15788c2ecf20Sopenharmony_ci			if (minor2disktype[type].drive_types > DriveType)
15798c2ecf20Sopenharmony_ci				return -ENODEV;
15808c2ecf20Sopenharmony_ci			type = minor2disktype[type].index;
15818c2ecf20Sopenharmony_ci			dtp = &atari_disk_type[type];
15828c2ecf20Sopenharmony_ci			if (UD.flags & FTD_MSG)
15838c2ecf20Sopenharmony_ci			    printk (KERN_ERR "floppy%d: found dtp %p name %s!\n",
15848c2ecf20Sopenharmony_ci			        drive, dtp, dtp->name);
15858c2ecf20Sopenharmony_ci		}
15868c2ecf20Sopenharmony_ci		else {
15878c2ecf20Sopenharmony_ci			if (!UDT)
15888c2ecf20Sopenharmony_ci				return -ENXIO;
15898c2ecf20Sopenharmony_ci			else
15908c2ecf20Sopenharmony_ci				dtp = UDT;
15918c2ecf20Sopenharmony_ci		}
15928c2ecf20Sopenharmony_ci		memset((void *)&getprm, 0, sizeof(getprm));
15938c2ecf20Sopenharmony_ci		getprm.size = dtp->blocks;
15948c2ecf20Sopenharmony_ci		getprm.sect = dtp->spt;
15958c2ecf20Sopenharmony_ci		getprm.head = 2;
15968c2ecf20Sopenharmony_ci		getprm.track = dtp->blocks/dtp->spt/2;
15978c2ecf20Sopenharmony_ci		getprm.stretch = dtp->stretch;
15988c2ecf20Sopenharmony_ci		if (copy_to_user(argp, &getprm, sizeof(getprm)))
15998c2ecf20Sopenharmony_ci			return -EFAULT;
16008c2ecf20Sopenharmony_ci		return 0;
16018c2ecf20Sopenharmony_ci	}
16028c2ecf20Sopenharmony_ci	switch (cmd) {
16038c2ecf20Sopenharmony_ci	case FDSETPRM:
16048c2ecf20Sopenharmony_ci	case FDDEFPRM:
16058c2ecf20Sopenharmony_ci	        /*
16068c2ecf20Sopenharmony_ci		 * MSch 7/96: simple 'set geometry' case: just set the
16078c2ecf20Sopenharmony_ci		 * 'default' device params (minor == 0).
16088c2ecf20Sopenharmony_ci		 * Currently, the drive geometry is cleared after each
16098c2ecf20Sopenharmony_ci		 * disk change and subsequent revalidate()! simple
16108c2ecf20Sopenharmony_ci		 * implementation of FDDEFPRM: save geometry from a
16118c2ecf20Sopenharmony_ci		 * FDDEFPRM call and restore it in floppy_revalidate() !
16128c2ecf20Sopenharmony_ci		 */
16138c2ecf20Sopenharmony_ci
16148c2ecf20Sopenharmony_ci		/* get the parameters from user space */
16158c2ecf20Sopenharmony_ci		if (floppy->ref != 1 && floppy->ref != -1)
16168c2ecf20Sopenharmony_ci			return -EBUSY;
16178c2ecf20Sopenharmony_ci		if (copy_from_user(&setprm, argp, sizeof(setprm)))
16188c2ecf20Sopenharmony_ci			return -EFAULT;
16198c2ecf20Sopenharmony_ci		/*
16208c2ecf20Sopenharmony_ci		 * first of all: check for floppy change and revalidate,
16218c2ecf20Sopenharmony_ci		 * or the next access will revalidate - and clear UDT :-(
16228c2ecf20Sopenharmony_ci		 */
16238c2ecf20Sopenharmony_ci
16248c2ecf20Sopenharmony_ci		if (floppy_check_events(disk, 0))
16258c2ecf20Sopenharmony_ci		        floppy_revalidate(disk);
16268c2ecf20Sopenharmony_ci
16278c2ecf20Sopenharmony_ci		if (UD.flags & FTD_MSG)
16288c2ecf20Sopenharmony_ci		    printk (KERN_INFO "floppy%d: setting size %d spt %d str %d!\n",
16298c2ecf20Sopenharmony_ci			drive, setprm.size, setprm.sect, setprm.stretch);
16308c2ecf20Sopenharmony_ci
16318c2ecf20Sopenharmony_ci		/* what if type > 0 here? Overwrite specified entry ? */
16328c2ecf20Sopenharmony_ci		if (type) {
16338c2ecf20Sopenharmony_ci		        /* refuse to re-set a predefined type for now */
16348c2ecf20Sopenharmony_ci			return -EINVAL;
16358c2ecf20Sopenharmony_ci		}
16368c2ecf20Sopenharmony_ci
16378c2ecf20Sopenharmony_ci		/*
16388c2ecf20Sopenharmony_ci		 * type == 0: first look for a matching entry in the type list,
16398c2ecf20Sopenharmony_ci		 * and set the UD.disktype field to use the perdefined entry.
16408c2ecf20Sopenharmony_ci		 * TODO: add user-defined format to head of autoprobe list ?
16418c2ecf20Sopenharmony_ci		 * Useful to include the user-type for future autodetection!
16428c2ecf20Sopenharmony_ci		 */
16438c2ecf20Sopenharmony_ci
16448c2ecf20Sopenharmony_ci		for (settype = 0; settype < NUM_DISK_MINORS; settype++) {
16458c2ecf20Sopenharmony_ci			int setidx = 0;
16468c2ecf20Sopenharmony_ci			if (minor2disktype[settype].drive_types > DriveType) {
16478c2ecf20Sopenharmony_ci				/* skip this one, invalid for drive ... */
16488c2ecf20Sopenharmony_ci				continue;
16498c2ecf20Sopenharmony_ci			}
16508c2ecf20Sopenharmony_ci			setidx = minor2disktype[settype].index;
16518c2ecf20Sopenharmony_ci			dtp = &atari_disk_type[setidx];
16528c2ecf20Sopenharmony_ci
16538c2ecf20Sopenharmony_ci			/* found matching entry ?? */
16548c2ecf20Sopenharmony_ci			if (   dtp->blocks  == setprm.size
16558c2ecf20Sopenharmony_ci			    && dtp->spt     == setprm.sect
16568c2ecf20Sopenharmony_ci			    && dtp->stretch == setprm.stretch ) {
16578c2ecf20Sopenharmony_ci				if (UD.flags & FTD_MSG)
16588c2ecf20Sopenharmony_ci				    printk (KERN_INFO "floppy%d: setting %s %p!\n",
16598c2ecf20Sopenharmony_ci				        drive, dtp->name, dtp);
16608c2ecf20Sopenharmony_ci				UDT = dtp;
16618c2ecf20Sopenharmony_ci				set_capacity(floppy->disk, UDT->blocks);
16628c2ecf20Sopenharmony_ci
16638c2ecf20Sopenharmony_ci				if (cmd == FDDEFPRM) {
16648c2ecf20Sopenharmony_ci				  /* save settings as permanent default type */
16658c2ecf20Sopenharmony_ci				  default_params[drive].name    = dtp->name;
16668c2ecf20Sopenharmony_ci				  default_params[drive].spt     = dtp->spt;
16678c2ecf20Sopenharmony_ci				  default_params[drive].blocks  = dtp->blocks;
16688c2ecf20Sopenharmony_ci				  default_params[drive].fdc_speed = dtp->fdc_speed;
16698c2ecf20Sopenharmony_ci				  default_params[drive].stretch = dtp->stretch;
16708c2ecf20Sopenharmony_ci				}
16718c2ecf20Sopenharmony_ci
16728c2ecf20Sopenharmony_ci				return 0;
16738c2ecf20Sopenharmony_ci			}
16748c2ecf20Sopenharmony_ci
16758c2ecf20Sopenharmony_ci		}
16768c2ecf20Sopenharmony_ci
16778c2ecf20Sopenharmony_ci		/* no matching disk type found above - setting user_params */
16788c2ecf20Sopenharmony_ci
16798c2ecf20Sopenharmony_ci	       	if (cmd == FDDEFPRM) {
16808c2ecf20Sopenharmony_ci			/* set permanent type */
16818c2ecf20Sopenharmony_ci			dtp = &default_params[drive];
16828c2ecf20Sopenharmony_ci		} else
16838c2ecf20Sopenharmony_ci			/* set user type (reset by disk change!) */
16848c2ecf20Sopenharmony_ci			dtp = &user_params[drive];
16858c2ecf20Sopenharmony_ci
16868c2ecf20Sopenharmony_ci		dtp->name   = "user format";
16878c2ecf20Sopenharmony_ci		dtp->blocks = setprm.size;
16888c2ecf20Sopenharmony_ci		dtp->spt    = setprm.sect;
16898c2ecf20Sopenharmony_ci		if (setprm.sect > 14)
16908c2ecf20Sopenharmony_ci			dtp->fdc_speed = 3;
16918c2ecf20Sopenharmony_ci		else
16928c2ecf20Sopenharmony_ci			dtp->fdc_speed = 0;
16938c2ecf20Sopenharmony_ci		dtp->stretch = setprm.stretch;
16948c2ecf20Sopenharmony_ci
16958c2ecf20Sopenharmony_ci		if (UD.flags & FTD_MSG)
16968c2ecf20Sopenharmony_ci			printk (KERN_INFO "floppy%d: blk %d spt %d str %d!\n",
16978c2ecf20Sopenharmony_ci				drive, dtp->blocks, dtp->spt, dtp->stretch);
16988c2ecf20Sopenharmony_ci
16998c2ecf20Sopenharmony_ci		/* sanity check */
17008c2ecf20Sopenharmony_ci		if (setprm.track != dtp->blocks/dtp->spt/2 ||
17018c2ecf20Sopenharmony_ci		    setprm.head != 2)
17028c2ecf20Sopenharmony_ci			return -EINVAL;
17038c2ecf20Sopenharmony_ci
17048c2ecf20Sopenharmony_ci		UDT = dtp;
17058c2ecf20Sopenharmony_ci		set_capacity(floppy->disk, UDT->blocks);
17068c2ecf20Sopenharmony_ci
17078c2ecf20Sopenharmony_ci		return 0;
17088c2ecf20Sopenharmony_ci	case FDMSGON:
17098c2ecf20Sopenharmony_ci		UD.flags |= FTD_MSG;
17108c2ecf20Sopenharmony_ci		return 0;
17118c2ecf20Sopenharmony_ci	case FDMSGOFF:
17128c2ecf20Sopenharmony_ci		UD.flags &= ~FTD_MSG;
17138c2ecf20Sopenharmony_ci		return 0;
17148c2ecf20Sopenharmony_ci	case FDSETEMSGTRESH:
17158c2ecf20Sopenharmony_ci		return -EINVAL;
17168c2ecf20Sopenharmony_ci	case FDFMTBEG:
17178c2ecf20Sopenharmony_ci		return 0;
17188c2ecf20Sopenharmony_ci	case FDFMTTRK:
17198c2ecf20Sopenharmony_ci		if (floppy->ref != 1 && floppy->ref != -1)
17208c2ecf20Sopenharmony_ci			return -EBUSY;
17218c2ecf20Sopenharmony_ci		if (copy_from_user(&fmt_desc, argp, sizeof(fmt_desc)))
17228c2ecf20Sopenharmony_ci			return -EFAULT;
17238c2ecf20Sopenharmony_ci		return do_format(drive, type, &fmt_desc);
17248c2ecf20Sopenharmony_ci	case FDCLRPRM:
17258c2ecf20Sopenharmony_ci		UDT = NULL;
17268c2ecf20Sopenharmony_ci		/* MSch: invalidate default_params */
17278c2ecf20Sopenharmony_ci		default_params[drive].blocks  = 0;
17288c2ecf20Sopenharmony_ci		set_capacity(floppy->disk, MAX_DISK_SIZE * 2);
17298c2ecf20Sopenharmony_ci		fallthrough;
17308c2ecf20Sopenharmony_ci	case FDFMTEND:
17318c2ecf20Sopenharmony_ci	case FDFLUSH:
17328c2ecf20Sopenharmony_ci		/* invalidate the buffer track to force a reread */
17338c2ecf20Sopenharmony_ci		BufferDrive = -1;
17348c2ecf20Sopenharmony_ci		set_bit(drive, &fake_change);
17358c2ecf20Sopenharmony_ci		if (bdev_check_media_change(bdev))
17368c2ecf20Sopenharmony_ci			floppy_revalidate(bdev->bd_disk);
17378c2ecf20Sopenharmony_ci		return 0;
17388c2ecf20Sopenharmony_ci	default:
17398c2ecf20Sopenharmony_ci		return -EINVAL;
17408c2ecf20Sopenharmony_ci	}
17418c2ecf20Sopenharmony_ci}
17428c2ecf20Sopenharmony_ci
17438c2ecf20Sopenharmony_cistatic int fd_ioctl(struct block_device *bdev, fmode_t mode,
17448c2ecf20Sopenharmony_ci			     unsigned int cmd, unsigned long arg)
17458c2ecf20Sopenharmony_ci{
17468c2ecf20Sopenharmony_ci	int ret;
17478c2ecf20Sopenharmony_ci
17488c2ecf20Sopenharmony_ci	mutex_lock(&ataflop_mutex);
17498c2ecf20Sopenharmony_ci	ret = fd_locked_ioctl(bdev, mode, cmd, arg);
17508c2ecf20Sopenharmony_ci	mutex_unlock(&ataflop_mutex);
17518c2ecf20Sopenharmony_ci
17528c2ecf20Sopenharmony_ci	return ret;
17538c2ecf20Sopenharmony_ci}
17548c2ecf20Sopenharmony_ci
17558c2ecf20Sopenharmony_ci/* Initialize the 'unit' variable for drive 'drive' */
17568c2ecf20Sopenharmony_ci
17578c2ecf20Sopenharmony_cistatic void __init fd_probe( int drive )
17588c2ecf20Sopenharmony_ci{
17598c2ecf20Sopenharmony_ci	UD.connected = 0;
17608c2ecf20Sopenharmony_ci	UDT  = NULL;
17618c2ecf20Sopenharmony_ci
17628c2ecf20Sopenharmony_ci	if (!fd_test_drive_present( drive ))
17638c2ecf20Sopenharmony_ci		return;
17648c2ecf20Sopenharmony_ci
17658c2ecf20Sopenharmony_ci	UD.connected = 1;
17668c2ecf20Sopenharmony_ci	UD.track     = 0;
17678c2ecf20Sopenharmony_ci	switch( UserSteprate[drive] ) {
17688c2ecf20Sopenharmony_ci	case 2:
17698c2ecf20Sopenharmony_ci		UD.steprate = FDCSTEP_2;
17708c2ecf20Sopenharmony_ci		break;
17718c2ecf20Sopenharmony_ci	case 3:
17728c2ecf20Sopenharmony_ci		UD.steprate = FDCSTEP_3;
17738c2ecf20Sopenharmony_ci		break;
17748c2ecf20Sopenharmony_ci	case 6:
17758c2ecf20Sopenharmony_ci		UD.steprate = FDCSTEP_6;
17768c2ecf20Sopenharmony_ci		break;
17778c2ecf20Sopenharmony_ci	case 12:
17788c2ecf20Sopenharmony_ci		UD.steprate = FDCSTEP_12;
17798c2ecf20Sopenharmony_ci		break;
17808c2ecf20Sopenharmony_ci	default: /* should be -1 for "not set by user" */
17818c2ecf20Sopenharmony_ci		if (ATARIHW_PRESENT( FDCSPEED ) || MACH_IS_MEDUSA)
17828c2ecf20Sopenharmony_ci			UD.steprate = FDCSTEP_3;
17838c2ecf20Sopenharmony_ci		else
17848c2ecf20Sopenharmony_ci			UD.steprate = FDCSTEP_6;
17858c2ecf20Sopenharmony_ci		break;
17868c2ecf20Sopenharmony_ci	}
17878c2ecf20Sopenharmony_ci	MotorOn = 1;	/* from probe restore operation! */
17888c2ecf20Sopenharmony_ci}
17898c2ecf20Sopenharmony_ci
17908c2ecf20Sopenharmony_ci
17918c2ecf20Sopenharmony_ci/* This function tests the physical presence of a floppy drive (not
17928c2ecf20Sopenharmony_ci * whether a disk is inserted). This is done by issuing a restore
17938c2ecf20Sopenharmony_ci * command, waiting max. 2 seconds (that should be enough to move the
17948c2ecf20Sopenharmony_ci * head across the whole disk) and looking at the state of the "TR00"
17958c2ecf20Sopenharmony_ci * signal. This should now be raised if there is a drive connected
17968c2ecf20Sopenharmony_ci * (and there is no hardware failure :-) Otherwise, the drive is
17978c2ecf20Sopenharmony_ci * declared absent.
17988c2ecf20Sopenharmony_ci */
17998c2ecf20Sopenharmony_ci
18008c2ecf20Sopenharmony_cistatic int __init fd_test_drive_present( int drive )
18018c2ecf20Sopenharmony_ci{
18028c2ecf20Sopenharmony_ci	unsigned long timeout;
18038c2ecf20Sopenharmony_ci	unsigned char status;
18048c2ecf20Sopenharmony_ci	int ok;
18058c2ecf20Sopenharmony_ci
18068c2ecf20Sopenharmony_ci	if (drive >= (MACH_IS_FALCON ? 1 : 2)) return( 0 );
18078c2ecf20Sopenharmony_ci	fd_select_drive( drive );
18088c2ecf20Sopenharmony_ci
18098c2ecf20Sopenharmony_ci	/* disable interrupt temporarily */
18108c2ecf20Sopenharmony_ci	atari_turnoff_irq( IRQ_MFP_FDC );
18118c2ecf20Sopenharmony_ci	FDC_WRITE (FDCREG_TRACK, 0xff00);
18128c2ecf20Sopenharmony_ci	FDC_WRITE( FDCREG_CMD, FDCCMD_RESTORE | FDCCMDADD_H | FDCSTEP_6 );
18138c2ecf20Sopenharmony_ci
18148c2ecf20Sopenharmony_ci	timeout = jiffies + 2*HZ+HZ/2;
18158c2ecf20Sopenharmony_ci	while (time_before(jiffies, timeout))
18168c2ecf20Sopenharmony_ci		if (!(st_mfp.par_dt_reg & 0x20))
18178c2ecf20Sopenharmony_ci			break;
18188c2ecf20Sopenharmony_ci
18198c2ecf20Sopenharmony_ci	status = FDC_READ( FDCREG_STATUS );
18208c2ecf20Sopenharmony_ci	ok = (status & FDCSTAT_TR00) != 0;
18218c2ecf20Sopenharmony_ci
18228c2ecf20Sopenharmony_ci	/* force interrupt to abort restore operation (FDC would try
18238c2ecf20Sopenharmony_ci	 * about 50 seconds!) */
18248c2ecf20Sopenharmony_ci	FDC_WRITE( FDCREG_CMD, FDCCMD_FORCI );
18258c2ecf20Sopenharmony_ci	udelay(500);
18268c2ecf20Sopenharmony_ci	status = FDC_READ( FDCREG_STATUS );
18278c2ecf20Sopenharmony_ci	udelay(20);
18288c2ecf20Sopenharmony_ci
18298c2ecf20Sopenharmony_ci	if (ok) {
18308c2ecf20Sopenharmony_ci		/* dummy seek command to make WP bit accessible */
18318c2ecf20Sopenharmony_ci		FDC_WRITE( FDCREG_DATA, 0 );
18328c2ecf20Sopenharmony_ci		FDC_WRITE( FDCREG_CMD, FDCCMD_SEEK );
18338c2ecf20Sopenharmony_ci		while( st_mfp.par_dt_reg & 0x20 )
18348c2ecf20Sopenharmony_ci			;
18358c2ecf20Sopenharmony_ci		status = FDC_READ( FDCREG_STATUS );
18368c2ecf20Sopenharmony_ci	}
18378c2ecf20Sopenharmony_ci
18388c2ecf20Sopenharmony_ci	atari_turnon_irq( IRQ_MFP_FDC );
18398c2ecf20Sopenharmony_ci	return( ok );
18408c2ecf20Sopenharmony_ci}
18418c2ecf20Sopenharmony_ci
18428c2ecf20Sopenharmony_ci
18438c2ecf20Sopenharmony_ci/* Look how many and which kind of drives are connected. If there are
18448c2ecf20Sopenharmony_ci * floppies, additionally start the disk-change and motor-off timers.
18458c2ecf20Sopenharmony_ci */
18468c2ecf20Sopenharmony_ci
18478c2ecf20Sopenharmony_cistatic void __init config_types( void )
18488c2ecf20Sopenharmony_ci{
18498c2ecf20Sopenharmony_ci	int drive, cnt = 0;
18508c2ecf20Sopenharmony_ci
18518c2ecf20Sopenharmony_ci	/* for probing drives, set the FDC speed to 8 MHz */
18528c2ecf20Sopenharmony_ci	if (ATARIHW_PRESENT(FDCSPEED))
18538c2ecf20Sopenharmony_ci		dma_wd.fdc_speed = 0;
18548c2ecf20Sopenharmony_ci
18558c2ecf20Sopenharmony_ci	printk(KERN_INFO "Probing floppy drive(s):\n");
18568c2ecf20Sopenharmony_ci	for( drive = 0; drive < FD_MAX_UNITS; drive++ ) {
18578c2ecf20Sopenharmony_ci		fd_probe( drive );
18588c2ecf20Sopenharmony_ci		if (UD.connected) {
18598c2ecf20Sopenharmony_ci			printk(KERN_INFO "fd%d\n", drive);
18608c2ecf20Sopenharmony_ci			++cnt;
18618c2ecf20Sopenharmony_ci		}
18628c2ecf20Sopenharmony_ci	}
18638c2ecf20Sopenharmony_ci
18648c2ecf20Sopenharmony_ci	if (FDC_READ( FDCREG_STATUS ) & FDCSTAT_BUSY) {
18658c2ecf20Sopenharmony_ci		/* If FDC is still busy from probing, give it another FORCI
18668c2ecf20Sopenharmony_ci		 * command to abort the operation. If this isn't done, the FDC
18678c2ecf20Sopenharmony_ci		 * will interrupt later and its IRQ line stays low, because
18688c2ecf20Sopenharmony_ci		 * the status register isn't read. And this will block any
18698c2ecf20Sopenharmony_ci		 * interrupts on this IRQ line :-(
18708c2ecf20Sopenharmony_ci		 */
18718c2ecf20Sopenharmony_ci		FDC_WRITE( FDCREG_CMD, FDCCMD_FORCI );
18728c2ecf20Sopenharmony_ci		udelay(500);
18738c2ecf20Sopenharmony_ci		FDC_READ( FDCREG_STATUS );
18748c2ecf20Sopenharmony_ci		udelay(20);
18758c2ecf20Sopenharmony_ci	}
18768c2ecf20Sopenharmony_ci
18778c2ecf20Sopenharmony_ci	if (cnt > 0) {
18788c2ecf20Sopenharmony_ci		start_motor_off_timer();
18798c2ecf20Sopenharmony_ci		if (cnt == 1) fd_select_drive( 0 );
18808c2ecf20Sopenharmony_ci		start_check_change_timer();
18818c2ecf20Sopenharmony_ci	}
18828c2ecf20Sopenharmony_ci}
18838c2ecf20Sopenharmony_ci
18848c2ecf20Sopenharmony_ci/*
18858c2ecf20Sopenharmony_ci * floppy_open check for aliasing (/dev/fd0 can be the same as
18868c2ecf20Sopenharmony_ci * /dev/PS0 etc), and disallows simultaneous access to the same
18878c2ecf20Sopenharmony_ci * drive with different device numbers.
18888c2ecf20Sopenharmony_ci */
18898c2ecf20Sopenharmony_ci
18908c2ecf20Sopenharmony_cistatic int floppy_open(struct block_device *bdev, fmode_t mode)
18918c2ecf20Sopenharmony_ci{
18928c2ecf20Sopenharmony_ci	struct atari_floppy_struct *p = bdev->bd_disk->private_data;
18938c2ecf20Sopenharmony_ci	int type  = MINOR(bdev->bd_dev) >> 2;
18948c2ecf20Sopenharmony_ci
18958c2ecf20Sopenharmony_ci	DPRINT(("fd_open: type=%d\n",type));
18968c2ecf20Sopenharmony_ci	if (p->ref && p->type != type)
18978c2ecf20Sopenharmony_ci		return -EBUSY;
18988c2ecf20Sopenharmony_ci
18998c2ecf20Sopenharmony_ci	if (p->ref == -1 || (p->ref && mode & FMODE_EXCL))
19008c2ecf20Sopenharmony_ci		return -EBUSY;
19018c2ecf20Sopenharmony_ci
19028c2ecf20Sopenharmony_ci	if (mode & FMODE_EXCL)
19038c2ecf20Sopenharmony_ci		p->ref = -1;
19048c2ecf20Sopenharmony_ci	else
19058c2ecf20Sopenharmony_ci		p->ref++;
19068c2ecf20Sopenharmony_ci
19078c2ecf20Sopenharmony_ci	p->type = type;
19088c2ecf20Sopenharmony_ci
19098c2ecf20Sopenharmony_ci	if (mode & FMODE_NDELAY)
19108c2ecf20Sopenharmony_ci		return 0;
19118c2ecf20Sopenharmony_ci
19128c2ecf20Sopenharmony_ci	if (mode & (FMODE_READ|FMODE_WRITE)) {
19138c2ecf20Sopenharmony_ci		if (bdev_check_media_change(bdev))
19148c2ecf20Sopenharmony_ci			floppy_revalidate(bdev->bd_disk);
19158c2ecf20Sopenharmony_ci		if (mode & FMODE_WRITE) {
19168c2ecf20Sopenharmony_ci			if (p->wpstat) {
19178c2ecf20Sopenharmony_ci				if (p->ref < 0)
19188c2ecf20Sopenharmony_ci					p->ref = 0;
19198c2ecf20Sopenharmony_ci				else
19208c2ecf20Sopenharmony_ci					p->ref--;
19218c2ecf20Sopenharmony_ci				return -EROFS;
19228c2ecf20Sopenharmony_ci			}
19238c2ecf20Sopenharmony_ci		}
19248c2ecf20Sopenharmony_ci	}
19258c2ecf20Sopenharmony_ci	return 0;
19268c2ecf20Sopenharmony_ci}
19278c2ecf20Sopenharmony_ci
19288c2ecf20Sopenharmony_cistatic int floppy_unlocked_open(struct block_device *bdev, fmode_t mode)
19298c2ecf20Sopenharmony_ci{
19308c2ecf20Sopenharmony_ci	int ret;
19318c2ecf20Sopenharmony_ci
19328c2ecf20Sopenharmony_ci	mutex_lock(&ataflop_mutex);
19338c2ecf20Sopenharmony_ci	ret = floppy_open(bdev, mode);
19348c2ecf20Sopenharmony_ci	mutex_unlock(&ataflop_mutex);
19358c2ecf20Sopenharmony_ci
19368c2ecf20Sopenharmony_ci	return ret;
19378c2ecf20Sopenharmony_ci}
19388c2ecf20Sopenharmony_ci
19398c2ecf20Sopenharmony_cistatic void floppy_release(struct gendisk *disk, fmode_t mode)
19408c2ecf20Sopenharmony_ci{
19418c2ecf20Sopenharmony_ci	struct atari_floppy_struct *p = disk->private_data;
19428c2ecf20Sopenharmony_ci	mutex_lock(&ataflop_mutex);
19438c2ecf20Sopenharmony_ci	if (p->ref < 0)
19448c2ecf20Sopenharmony_ci		p->ref = 0;
19458c2ecf20Sopenharmony_ci	else if (!p->ref--) {
19468c2ecf20Sopenharmony_ci		printk(KERN_ERR "floppy_release with fd_ref == 0");
19478c2ecf20Sopenharmony_ci		p->ref = 0;
19488c2ecf20Sopenharmony_ci	}
19498c2ecf20Sopenharmony_ci	mutex_unlock(&ataflop_mutex);
19508c2ecf20Sopenharmony_ci}
19518c2ecf20Sopenharmony_ci
19528c2ecf20Sopenharmony_cistatic const struct block_device_operations floppy_fops = {
19538c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
19548c2ecf20Sopenharmony_ci	.open		= floppy_unlocked_open,
19558c2ecf20Sopenharmony_ci	.release	= floppy_release,
19568c2ecf20Sopenharmony_ci	.ioctl		= fd_ioctl,
19578c2ecf20Sopenharmony_ci	.check_events	= floppy_check_events,
19588c2ecf20Sopenharmony_ci};
19598c2ecf20Sopenharmony_ci
19608c2ecf20Sopenharmony_cistatic const struct blk_mq_ops ataflop_mq_ops = {
19618c2ecf20Sopenharmony_ci	.queue_rq = ataflop_queue_rq,
19628c2ecf20Sopenharmony_ci	.commit_rqs = ataflop_commit_rqs,
19638c2ecf20Sopenharmony_ci};
19648c2ecf20Sopenharmony_ci
19658c2ecf20Sopenharmony_cistatic struct kobject *floppy_find(dev_t dev, int *part, void *data)
19668c2ecf20Sopenharmony_ci{
19678c2ecf20Sopenharmony_ci	int drive = *part & 3;
19688c2ecf20Sopenharmony_ci	int type  = *part >> 2;
19698c2ecf20Sopenharmony_ci	if (drive >= FD_MAX_UNITS || type > NUM_DISK_MINORS)
19708c2ecf20Sopenharmony_ci		return NULL;
19718c2ecf20Sopenharmony_ci	*part = 0;
19728c2ecf20Sopenharmony_ci	return get_disk_and_module(unit[drive].disk);
19738c2ecf20Sopenharmony_ci}
19748c2ecf20Sopenharmony_ci
19758c2ecf20Sopenharmony_cistatic int __init atari_floppy_init (void)
19768c2ecf20Sopenharmony_ci{
19778c2ecf20Sopenharmony_ci	int i;
19788c2ecf20Sopenharmony_ci	int ret;
19798c2ecf20Sopenharmony_ci
19808c2ecf20Sopenharmony_ci	if (!MACH_IS_ATARI)
19818c2ecf20Sopenharmony_ci		/* Amiga, Mac, ... don't have Atari-compatible floppy :-) */
19828c2ecf20Sopenharmony_ci		return -ENODEV;
19838c2ecf20Sopenharmony_ci
19848c2ecf20Sopenharmony_ci	if (register_blkdev(FLOPPY_MAJOR,"fd"))
19858c2ecf20Sopenharmony_ci		return -EBUSY;
19868c2ecf20Sopenharmony_ci
19878c2ecf20Sopenharmony_ci	for (i = 0; i < FD_MAX_UNITS; i++) {
19888c2ecf20Sopenharmony_ci		unit[i].disk = alloc_disk(1);
19898c2ecf20Sopenharmony_ci		if (!unit[i].disk) {
19908c2ecf20Sopenharmony_ci			ret = -ENOMEM;
19918c2ecf20Sopenharmony_ci			goto err;
19928c2ecf20Sopenharmony_ci		}
19938c2ecf20Sopenharmony_ci
19948c2ecf20Sopenharmony_ci		unit[i].disk->queue = blk_mq_init_sq_queue(&unit[i].tag_set,
19958c2ecf20Sopenharmony_ci							   &ataflop_mq_ops, 2,
19968c2ecf20Sopenharmony_ci							   BLK_MQ_F_SHOULD_MERGE);
19978c2ecf20Sopenharmony_ci		if (IS_ERR(unit[i].disk->queue)) {
19988c2ecf20Sopenharmony_ci			put_disk(unit[i].disk);
19998c2ecf20Sopenharmony_ci			ret = PTR_ERR(unit[i].disk->queue);
20008c2ecf20Sopenharmony_ci			unit[i].disk->queue = NULL;
20018c2ecf20Sopenharmony_ci			goto err;
20028c2ecf20Sopenharmony_ci		}
20038c2ecf20Sopenharmony_ci	}
20048c2ecf20Sopenharmony_ci
20058c2ecf20Sopenharmony_ci	if (UseTrackbuffer < 0)
20068c2ecf20Sopenharmony_ci		/* not set by user -> use default: for now, we turn
20078c2ecf20Sopenharmony_ci		   track buffering off for all Medusas, though it
20088c2ecf20Sopenharmony_ci		   could be used with ones that have a counter
20098c2ecf20Sopenharmony_ci		   card. But the test is too hard :-( */
20108c2ecf20Sopenharmony_ci		UseTrackbuffer = !MACH_IS_MEDUSA;
20118c2ecf20Sopenharmony_ci
20128c2ecf20Sopenharmony_ci	/* initialize variables */
20138c2ecf20Sopenharmony_ci	SelectedDrive = -1;
20148c2ecf20Sopenharmony_ci	BufferDrive = -1;
20158c2ecf20Sopenharmony_ci
20168c2ecf20Sopenharmony_ci	DMABuffer = atari_stram_alloc(BUFFER_SIZE+512, "ataflop");
20178c2ecf20Sopenharmony_ci	if (!DMABuffer) {
20188c2ecf20Sopenharmony_ci		printk(KERN_ERR "atari_floppy_init: cannot get dma buffer\n");
20198c2ecf20Sopenharmony_ci		ret = -ENOMEM;
20208c2ecf20Sopenharmony_ci		goto err;
20218c2ecf20Sopenharmony_ci	}
20228c2ecf20Sopenharmony_ci	TrackBuffer = DMABuffer + 512;
20238c2ecf20Sopenharmony_ci	PhysDMABuffer = atari_stram_to_phys(DMABuffer);
20248c2ecf20Sopenharmony_ci	PhysTrackBuffer = virt_to_phys(TrackBuffer);
20258c2ecf20Sopenharmony_ci	BufferDrive = BufferSide = BufferTrack = -1;
20268c2ecf20Sopenharmony_ci
20278c2ecf20Sopenharmony_ci	for (i = 0; i < FD_MAX_UNITS; i++) {
20288c2ecf20Sopenharmony_ci		unit[i].track = -1;
20298c2ecf20Sopenharmony_ci		unit[i].flags = 0;
20308c2ecf20Sopenharmony_ci		unit[i].disk->major = FLOPPY_MAJOR;
20318c2ecf20Sopenharmony_ci		unit[i].disk->first_minor = i;
20328c2ecf20Sopenharmony_ci		sprintf(unit[i].disk->disk_name, "fd%d", i);
20338c2ecf20Sopenharmony_ci		unit[i].disk->fops = &floppy_fops;
20348c2ecf20Sopenharmony_ci		unit[i].disk->events = DISK_EVENT_MEDIA_CHANGE;
20358c2ecf20Sopenharmony_ci		unit[i].disk->private_data = &unit[i];
20368c2ecf20Sopenharmony_ci		set_capacity(unit[i].disk, MAX_DISK_SIZE * 2);
20378c2ecf20Sopenharmony_ci		add_disk(unit[i].disk);
20388c2ecf20Sopenharmony_ci	}
20398c2ecf20Sopenharmony_ci
20408c2ecf20Sopenharmony_ci	blk_register_region(MKDEV(FLOPPY_MAJOR, 0), 256, THIS_MODULE,
20418c2ecf20Sopenharmony_ci				floppy_find, NULL, NULL);
20428c2ecf20Sopenharmony_ci
20438c2ecf20Sopenharmony_ci	printk(KERN_INFO "Atari floppy driver: max. %cD, %strack buffering\n",
20448c2ecf20Sopenharmony_ci	       DriveType == 0 ? 'D' : DriveType == 1 ? 'H' : 'E',
20458c2ecf20Sopenharmony_ci	       UseTrackbuffer ? "" : "no ");
20468c2ecf20Sopenharmony_ci	config_types();
20478c2ecf20Sopenharmony_ci
20488c2ecf20Sopenharmony_ci	return 0;
20498c2ecf20Sopenharmony_ci
20508c2ecf20Sopenharmony_cierr:
20518c2ecf20Sopenharmony_ci	while (--i >= 0) {
20528c2ecf20Sopenharmony_ci		struct gendisk *disk = unit[i].disk;
20538c2ecf20Sopenharmony_ci
20548c2ecf20Sopenharmony_ci		blk_cleanup_queue(disk->queue);
20558c2ecf20Sopenharmony_ci		blk_mq_free_tag_set(&unit[i].tag_set);
20568c2ecf20Sopenharmony_ci		put_disk(unit[i].disk);
20578c2ecf20Sopenharmony_ci	}
20588c2ecf20Sopenharmony_ci
20598c2ecf20Sopenharmony_ci	unregister_blkdev(FLOPPY_MAJOR, "fd");
20608c2ecf20Sopenharmony_ci	return ret;
20618c2ecf20Sopenharmony_ci}
20628c2ecf20Sopenharmony_ci
20638c2ecf20Sopenharmony_ci#ifndef MODULE
20648c2ecf20Sopenharmony_cistatic int __init atari_floppy_setup(char *str)
20658c2ecf20Sopenharmony_ci{
20668c2ecf20Sopenharmony_ci	int ints[3 + FD_MAX_UNITS];
20678c2ecf20Sopenharmony_ci	int i;
20688c2ecf20Sopenharmony_ci
20698c2ecf20Sopenharmony_ci	if (!MACH_IS_ATARI)
20708c2ecf20Sopenharmony_ci		return 0;
20718c2ecf20Sopenharmony_ci
20728c2ecf20Sopenharmony_ci	str = get_options(str, 3 + FD_MAX_UNITS, ints);
20738c2ecf20Sopenharmony_ci
20748c2ecf20Sopenharmony_ci	if (ints[0] < 1) {
20758c2ecf20Sopenharmony_ci		printk(KERN_ERR "ataflop_setup: no arguments!\n" );
20768c2ecf20Sopenharmony_ci		return 0;
20778c2ecf20Sopenharmony_ci	}
20788c2ecf20Sopenharmony_ci	else if (ints[0] > 2+FD_MAX_UNITS) {
20798c2ecf20Sopenharmony_ci		printk(KERN_ERR "ataflop_setup: too many arguments\n" );
20808c2ecf20Sopenharmony_ci	}
20818c2ecf20Sopenharmony_ci
20828c2ecf20Sopenharmony_ci	if (ints[1] < 0 || ints[1] > 2)
20838c2ecf20Sopenharmony_ci		printk(KERN_ERR "ataflop_setup: bad drive type\n" );
20848c2ecf20Sopenharmony_ci	else
20858c2ecf20Sopenharmony_ci		DriveType = ints[1];
20868c2ecf20Sopenharmony_ci
20878c2ecf20Sopenharmony_ci	if (ints[0] >= 2)
20888c2ecf20Sopenharmony_ci		UseTrackbuffer = (ints[2] > 0);
20898c2ecf20Sopenharmony_ci
20908c2ecf20Sopenharmony_ci	for( i = 3; i <= ints[0] && i-3 < FD_MAX_UNITS; ++i ) {
20918c2ecf20Sopenharmony_ci		if (ints[i] != 2 && ints[i] != 3 && ints[i] != 6 && ints[i] != 12)
20928c2ecf20Sopenharmony_ci			printk(KERN_ERR "ataflop_setup: bad steprate\n" );
20938c2ecf20Sopenharmony_ci		else
20948c2ecf20Sopenharmony_ci			UserSteprate[i-3] = ints[i];
20958c2ecf20Sopenharmony_ci	}
20968c2ecf20Sopenharmony_ci	return 1;
20978c2ecf20Sopenharmony_ci}
20988c2ecf20Sopenharmony_ci
20998c2ecf20Sopenharmony_ci__setup("floppy=", atari_floppy_setup);
21008c2ecf20Sopenharmony_ci#endif
21018c2ecf20Sopenharmony_ci
21028c2ecf20Sopenharmony_cistatic void __exit atari_floppy_exit(void)
21038c2ecf20Sopenharmony_ci{
21048c2ecf20Sopenharmony_ci	int i;
21058c2ecf20Sopenharmony_ci	blk_unregister_region(MKDEV(FLOPPY_MAJOR, 0), 256);
21068c2ecf20Sopenharmony_ci	for (i = 0; i < FD_MAX_UNITS; i++) {
21078c2ecf20Sopenharmony_ci		del_gendisk(unit[i].disk);
21088c2ecf20Sopenharmony_ci		blk_cleanup_queue(unit[i].disk->queue);
21098c2ecf20Sopenharmony_ci		blk_mq_free_tag_set(&unit[i].tag_set);
21108c2ecf20Sopenharmony_ci		put_disk(unit[i].disk);
21118c2ecf20Sopenharmony_ci	}
21128c2ecf20Sopenharmony_ci	unregister_blkdev(FLOPPY_MAJOR, "fd");
21138c2ecf20Sopenharmony_ci
21148c2ecf20Sopenharmony_ci	del_timer_sync(&fd_timer);
21158c2ecf20Sopenharmony_ci	atari_stram_free( DMABuffer );
21168c2ecf20Sopenharmony_ci}
21178c2ecf20Sopenharmony_ci
21188c2ecf20Sopenharmony_cimodule_init(atari_floppy_init)
21198c2ecf20Sopenharmony_cimodule_exit(atari_floppy_exit)
21208c2ecf20Sopenharmony_ci
21218c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
2122