xref: /openbmc/linux/drivers/block/swim.c (revision 1448a2a5)
18852ecd9SLaurent Vivier /*
28852ecd9SLaurent Vivier  * Driver for SWIM (Sander Woz Integrated Machine) floppy controller
38852ecd9SLaurent Vivier  *
48852ecd9SLaurent Vivier  * Copyright (C) 2004,2008 Laurent Vivier <Laurent@lvivier.info>
58852ecd9SLaurent Vivier  *
68852ecd9SLaurent Vivier  * based on Alastair Bridgewater SWIM analysis, 2001
78852ecd9SLaurent Vivier  * based on SWIM3 driver (c) Paul Mackerras, 1996
88852ecd9SLaurent Vivier  * based on netBSD IWM driver (c) 1997, 1998 Hauke Fath.
98852ecd9SLaurent Vivier  *
108852ecd9SLaurent Vivier  * This program is free software; you can redistribute it and/or
118852ecd9SLaurent Vivier  * modify it under the terms of the GNU General Public License
128852ecd9SLaurent Vivier  * as published by the Free Software Foundation; either version
138852ecd9SLaurent Vivier  * 2 of the License, or (at your option) any later version.
148852ecd9SLaurent Vivier  *
158852ecd9SLaurent Vivier  * 2004-08-21 (lv) - Initial implementation
168852ecd9SLaurent Vivier  * 2008-10-30 (lv) - Port to 2.6
178852ecd9SLaurent Vivier  */
188852ecd9SLaurent Vivier 
198852ecd9SLaurent Vivier #include <linux/module.h>
208852ecd9SLaurent Vivier #include <linux/fd.h>
215a0e3ad6STejun Heo #include <linux/slab.h>
228852ecd9SLaurent Vivier #include <linux/blkdev.h>
232a48fc0aSArnd Bergmann #include <linux/mutex.h>
248852ecd9SLaurent Vivier #include <linux/hdreg.h>
258852ecd9SLaurent Vivier #include <linux/kernel.h>
268852ecd9SLaurent Vivier #include <linux/delay.h>
278852ecd9SLaurent Vivier #include <linux/platform_device.h>
288852ecd9SLaurent Vivier 
298852ecd9SLaurent Vivier #include <asm/mac_via.h>
308852ecd9SLaurent Vivier 
318852ecd9SLaurent Vivier #define CARDNAME "swim"
328852ecd9SLaurent Vivier 
338852ecd9SLaurent Vivier struct sector_header {
348852ecd9SLaurent Vivier 	unsigned char side;
358852ecd9SLaurent Vivier 	unsigned char track;
368852ecd9SLaurent Vivier 	unsigned char sector;
378852ecd9SLaurent Vivier 	unsigned char size;
388852ecd9SLaurent Vivier 	unsigned char crc0;
398852ecd9SLaurent Vivier 	unsigned char crc1;
408852ecd9SLaurent Vivier } __attribute__((packed));
418852ecd9SLaurent Vivier 
428852ecd9SLaurent Vivier #define DRIVER_VERSION "Version 0.2 (2008-10-30)"
438852ecd9SLaurent Vivier 
448852ecd9SLaurent Vivier #define REG(x)	unsigned char x, x ## _pad[0x200 - 1];
458852ecd9SLaurent Vivier 
468852ecd9SLaurent Vivier struct swim {
478852ecd9SLaurent Vivier 	REG(write_data)
488852ecd9SLaurent Vivier 	REG(write_mark)
498852ecd9SLaurent Vivier 	REG(write_CRC)
508852ecd9SLaurent Vivier 	REG(write_parameter)
518852ecd9SLaurent Vivier 	REG(write_phase)
528852ecd9SLaurent Vivier 	REG(write_setup)
538852ecd9SLaurent Vivier 	REG(write_mode0)
548852ecd9SLaurent Vivier 	REG(write_mode1)
558852ecd9SLaurent Vivier 
568852ecd9SLaurent Vivier 	REG(read_data)
578852ecd9SLaurent Vivier 	REG(read_mark)
588852ecd9SLaurent Vivier 	REG(read_error)
598852ecd9SLaurent Vivier 	REG(read_parameter)
608852ecd9SLaurent Vivier 	REG(read_phase)
618852ecd9SLaurent Vivier 	REG(read_setup)
628852ecd9SLaurent Vivier 	REG(read_status)
638852ecd9SLaurent Vivier 	REG(read_handshake)
648852ecd9SLaurent Vivier } __attribute__((packed));
658852ecd9SLaurent Vivier 
668852ecd9SLaurent Vivier #define swim_write(base, reg, v) 	out_8(&(base)->write_##reg, (v))
678852ecd9SLaurent Vivier #define swim_read(base, reg)		in_8(&(base)->read_##reg)
688852ecd9SLaurent Vivier 
698852ecd9SLaurent Vivier /* IWM registers */
708852ecd9SLaurent Vivier 
718852ecd9SLaurent Vivier struct iwm {
728852ecd9SLaurent Vivier 	REG(ph0L)
738852ecd9SLaurent Vivier 	REG(ph0H)
748852ecd9SLaurent Vivier 	REG(ph1L)
758852ecd9SLaurent Vivier 	REG(ph1H)
768852ecd9SLaurent Vivier 	REG(ph2L)
778852ecd9SLaurent Vivier 	REG(ph2H)
788852ecd9SLaurent Vivier 	REG(ph3L)
798852ecd9SLaurent Vivier 	REG(ph3H)
808852ecd9SLaurent Vivier 	REG(mtrOff)
818852ecd9SLaurent Vivier 	REG(mtrOn)
828852ecd9SLaurent Vivier 	REG(intDrive)
838852ecd9SLaurent Vivier 	REG(extDrive)
848852ecd9SLaurent Vivier 	REG(q6L)
858852ecd9SLaurent Vivier 	REG(q6H)
868852ecd9SLaurent Vivier 	REG(q7L)
878852ecd9SLaurent Vivier 	REG(q7H)
888852ecd9SLaurent Vivier } __attribute__((packed));
898852ecd9SLaurent Vivier 
908852ecd9SLaurent Vivier #define iwm_write(base, reg, v) 	out_8(&(base)->reg, (v))
918852ecd9SLaurent Vivier #define iwm_read(base, reg)		in_8(&(base)->reg)
928852ecd9SLaurent Vivier 
938852ecd9SLaurent Vivier /* bits in phase register */
948852ecd9SLaurent Vivier 
958852ecd9SLaurent Vivier #define SEEK_POSITIVE	0x070
968852ecd9SLaurent Vivier #define SEEK_NEGATIVE	0x074
978852ecd9SLaurent Vivier #define STEP		0x071
988852ecd9SLaurent Vivier #define MOTOR_ON	0x072
998852ecd9SLaurent Vivier #define MOTOR_OFF	0x076
1008852ecd9SLaurent Vivier #define INDEX		0x073
1018852ecd9SLaurent Vivier #define EJECT		0x077
1028852ecd9SLaurent Vivier #define SETMFM		0x171
1038852ecd9SLaurent Vivier #define SETGCR		0x175
1048852ecd9SLaurent Vivier 
1058852ecd9SLaurent Vivier #define RELAX		0x033
1068852ecd9SLaurent Vivier #define LSTRB		0x008
1078852ecd9SLaurent Vivier 
1088852ecd9SLaurent Vivier #define CA_MASK		0x077
1098852ecd9SLaurent Vivier 
1108852ecd9SLaurent Vivier /* Select values for swim_select and swim_readbit */
1118852ecd9SLaurent Vivier 
1128852ecd9SLaurent Vivier #define READ_DATA_0	0x074
11356a1c5eeSFinn Thain #define ONEMEG_DRIVE	0x075
1148852ecd9SLaurent Vivier #define SINGLE_SIDED	0x076
1158852ecd9SLaurent Vivier #define DRIVE_PRESENT	0x077
1168852ecd9SLaurent Vivier #define DISK_IN		0x170
1178852ecd9SLaurent Vivier #define WRITE_PROT	0x171
1188852ecd9SLaurent Vivier #define TRACK_ZERO	0x172
1198852ecd9SLaurent Vivier #define TACHO		0x173
1208852ecd9SLaurent Vivier #define READ_DATA_1	0x174
12156a1c5eeSFinn Thain #define GCR_MODE	0x175
1228852ecd9SLaurent Vivier #define SEEK_COMPLETE	0x176
12356a1c5eeSFinn Thain #define TWOMEG_MEDIA	0x177
1248852ecd9SLaurent Vivier 
1258852ecd9SLaurent Vivier /* Bits in handshake register */
1268852ecd9SLaurent Vivier 
1278852ecd9SLaurent Vivier #define MARK_BYTE	0x01
1288852ecd9SLaurent Vivier #define CRC_ZERO	0x02
1298852ecd9SLaurent Vivier #define RDDATA		0x04
1308852ecd9SLaurent Vivier #define SENSE		0x08
1318852ecd9SLaurent Vivier #define MOTEN		0x10
1328852ecd9SLaurent Vivier #define ERROR		0x20
1338852ecd9SLaurent Vivier #define DAT2BYTE	0x40
1348852ecd9SLaurent Vivier #define DAT1BYTE	0x80
1358852ecd9SLaurent Vivier 
1368852ecd9SLaurent Vivier /* bits in setup register */
1378852ecd9SLaurent Vivier 
1388852ecd9SLaurent Vivier #define S_INV_WDATA	0x01
1398852ecd9SLaurent Vivier #define S_3_5_SELECT	0x02
1408852ecd9SLaurent Vivier #define S_GCR		0x04
1418852ecd9SLaurent Vivier #define S_FCLK_DIV2	0x08
1428852ecd9SLaurent Vivier #define S_ERROR_CORR	0x10
1438852ecd9SLaurent Vivier #define S_IBM_DRIVE	0x20
1448852ecd9SLaurent Vivier #define S_GCR_WRITE	0x40
1458852ecd9SLaurent Vivier #define S_TIMEOUT	0x80
1468852ecd9SLaurent Vivier 
1478852ecd9SLaurent Vivier /* bits in mode register */
1488852ecd9SLaurent Vivier 
1498852ecd9SLaurent Vivier #define CLFIFO		0x01
1508852ecd9SLaurent Vivier #define ENBL1		0x02
1518852ecd9SLaurent Vivier #define ENBL2		0x04
1528852ecd9SLaurent Vivier #define ACTION		0x08
1538852ecd9SLaurent Vivier #define WRITE_MODE	0x10
1548852ecd9SLaurent Vivier #define HEDSEL		0x20
1558852ecd9SLaurent Vivier #define MOTON		0x80
1568852ecd9SLaurent Vivier 
1578852ecd9SLaurent Vivier /*----------------------------------------------------------------------------*/
1588852ecd9SLaurent Vivier 
1598852ecd9SLaurent Vivier enum drive_location {
1608852ecd9SLaurent Vivier 	INTERNAL_DRIVE = 0x02,
1618852ecd9SLaurent Vivier 	EXTERNAL_DRIVE = 0x04,
1628852ecd9SLaurent Vivier };
1638852ecd9SLaurent Vivier 
1648852ecd9SLaurent Vivier enum media_type {
1658852ecd9SLaurent Vivier 	DD_MEDIA,
1668852ecd9SLaurent Vivier 	HD_MEDIA,
1678852ecd9SLaurent Vivier };
1688852ecd9SLaurent Vivier 
1698852ecd9SLaurent Vivier struct floppy_state {
1708852ecd9SLaurent Vivier 
1718852ecd9SLaurent Vivier 	/* physical properties */
1728852ecd9SLaurent Vivier 
1738852ecd9SLaurent Vivier 	enum drive_location location;	/* internal or external drive */
1748852ecd9SLaurent Vivier 	int		 head_number;	/* single- or double-sided drive */
1758852ecd9SLaurent Vivier 
1768852ecd9SLaurent Vivier 	/* media */
1778852ecd9SLaurent Vivier 
1788852ecd9SLaurent Vivier 	int		 disk_in;
1798852ecd9SLaurent Vivier 	int		 ejected;
1808852ecd9SLaurent Vivier 	enum media_type	 type;
1818852ecd9SLaurent Vivier 	int		 write_protected;
1828852ecd9SLaurent Vivier 
1838852ecd9SLaurent Vivier 	int		 total_secs;
1848852ecd9SLaurent Vivier 	int		 secpercyl;
1858852ecd9SLaurent Vivier 	int		 secpertrack;
1868852ecd9SLaurent Vivier 
1878852ecd9SLaurent Vivier 	/* in-use information */
1888852ecd9SLaurent Vivier 
1898852ecd9SLaurent Vivier 	int		track;
1908852ecd9SLaurent Vivier 	int		ref_count;
1918852ecd9SLaurent Vivier 
1928852ecd9SLaurent Vivier 	struct gendisk *disk;
1938852ecd9SLaurent Vivier 
1948852ecd9SLaurent Vivier 	/* parent controller */
1958852ecd9SLaurent Vivier 
1968852ecd9SLaurent Vivier 	struct swim_priv *swd;
1978852ecd9SLaurent Vivier };
1988852ecd9SLaurent Vivier 
1998852ecd9SLaurent Vivier enum motor_action {
2008852ecd9SLaurent Vivier 	OFF,
2018852ecd9SLaurent Vivier 	ON,
2028852ecd9SLaurent Vivier };
2038852ecd9SLaurent Vivier 
2048852ecd9SLaurent Vivier enum head {
2058852ecd9SLaurent Vivier 	LOWER_HEAD = 0,
2068852ecd9SLaurent Vivier 	UPPER_HEAD = 1,
2078852ecd9SLaurent Vivier };
2088852ecd9SLaurent Vivier 
2098852ecd9SLaurent Vivier #define FD_MAX_UNIT	2
2108852ecd9SLaurent Vivier 
2118852ecd9SLaurent Vivier struct swim_priv {
2128852ecd9SLaurent Vivier 	struct swim __iomem *base;
2138852ecd9SLaurent Vivier 	spinlock_t lock;
214103db8b2SOmar Sandoval 	int fdc_queue;
2158852ecd9SLaurent Vivier 	int floppy_count;
2168852ecd9SLaurent Vivier 	struct floppy_state unit[FD_MAX_UNIT];
2178852ecd9SLaurent Vivier };
2188852ecd9SLaurent Vivier 
2198852ecd9SLaurent Vivier extern int swim_read_sector_header(struct swim __iomem *base,
2208852ecd9SLaurent Vivier 				   struct sector_header *header);
2218852ecd9SLaurent Vivier extern int swim_read_sector_data(struct swim __iomem *base,
2228852ecd9SLaurent Vivier 				 unsigned char *data);
2238852ecd9SLaurent Vivier 
2242a48fc0aSArnd Bergmann static DEFINE_MUTEX(swim_mutex);
2258852ecd9SLaurent Vivier static inline void set_swim_mode(struct swim __iomem *base, int enable)
2268852ecd9SLaurent Vivier {
2278852ecd9SLaurent Vivier 	struct iwm __iomem *iwm_base;
2288852ecd9SLaurent Vivier 	unsigned long flags;
2298852ecd9SLaurent Vivier 
2308852ecd9SLaurent Vivier 	if (!enable) {
2318852ecd9SLaurent Vivier 		swim_write(base, mode0, 0xf8);
2328852ecd9SLaurent Vivier 		return;
2338852ecd9SLaurent Vivier 	}
2348852ecd9SLaurent Vivier 
2358852ecd9SLaurent Vivier 	iwm_base = (struct iwm __iomem *)base;
2368852ecd9SLaurent Vivier 	local_irq_save(flags);
2378852ecd9SLaurent Vivier 
2388852ecd9SLaurent Vivier 	iwm_read(iwm_base, q7L);
2398852ecd9SLaurent Vivier 	iwm_read(iwm_base, mtrOff);
2408852ecd9SLaurent Vivier 	iwm_read(iwm_base, q6H);
2418852ecd9SLaurent Vivier 
2428852ecd9SLaurent Vivier 	iwm_write(iwm_base, q7H, 0x57);
2438852ecd9SLaurent Vivier 	iwm_write(iwm_base, q7H, 0x17);
2448852ecd9SLaurent Vivier 	iwm_write(iwm_base, q7H, 0x57);
2458852ecd9SLaurent Vivier 	iwm_write(iwm_base, q7H, 0x57);
2468852ecd9SLaurent Vivier 
2478852ecd9SLaurent Vivier 	local_irq_restore(flags);
2488852ecd9SLaurent Vivier }
2498852ecd9SLaurent Vivier 
2508852ecd9SLaurent Vivier static inline int get_swim_mode(struct swim __iomem *base)
2518852ecd9SLaurent Vivier {
2528852ecd9SLaurent Vivier 	unsigned long flags;
2538852ecd9SLaurent Vivier 
2548852ecd9SLaurent Vivier 	local_irq_save(flags);
2558852ecd9SLaurent Vivier 
2568852ecd9SLaurent Vivier 	swim_write(base, phase, 0xf5);
2578852ecd9SLaurent Vivier 	if (swim_read(base, phase) != 0xf5)
2588852ecd9SLaurent Vivier 		goto is_iwm;
2598852ecd9SLaurent Vivier 	swim_write(base, phase, 0xf6);
2608852ecd9SLaurent Vivier 	if (swim_read(base, phase) != 0xf6)
2618852ecd9SLaurent Vivier 		goto is_iwm;
2628852ecd9SLaurent Vivier 	swim_write(base, phase, 0xf7);
2638852ecd9SLaurent Vivier 	if (swim_read(base, phase) != 0xf7)
2648852ecd9SLaurent Vivier 		goto is_iwm;
2658852ecd9SLaurent Vivier 	local_irq_restore(flags);
2668852ecd9SLaurent Vivier 	return 1;
2678852ecd9SLaurent Vivier is_iwm:
2688852ecd9SLaurent Vivier 	local_irq_restore(flags);
2698852ecd9SLaurent Vivier 	return 0;
2708852ecd9SLaurent Vivier }
2718852ecd9SLaurent Vivier 
2728852ecd9SLaurent Vivier static inline void swim_select(struct swim __iomem *base, int sel)
2738852ecd9SLaurent Vivier {
2748852ecd9SLaurent Vivier 	swim_write(base, phase, RELAX);
2758852ecd9SLaurent Vivier 
2768852ecd9SLaurent Vivier 	via1_set_head(sel & 0x100);
2778852ecd9SLaurent Vivier 
2788852ecd9SLaurent Vivier 	swim_write(base, phase, sel & CA_MASK);
2798852ecd9SLaurent Vivier }
2808852ecd9SLaurent Vivier 
2818852ecd9SLaurent Vivier static inline void swim_action(struct swim __iomem *base, int action)
2828852ecd9SLaurent Vivier {
2838852ecd9SLaurent Vivier 	unsigned long flags;
2848852ecd9SLaurent Vivier 
2858852ecd9SLaurent Vivier 	local_irq_save(flags);
2868852ecd9SLaurent Vivier 
2878852ecd9SLaurent Vivier 	swim_select(base, action);
2888852ecd9SLaurent Vivier 	udelay(1);
2898852ecd9SLaurent Vivier 	swim_write(base, phase, (LSTRB<<4) | LSTRB);
2908852ecd9SLaurent Vivier 	udelay(1);
2918852ecd9SLaurent Vivier 	swim_write(base, phase, (LSTRB<<4) | ((~LSTRB) & 0x0F));
2928852ecd9SLaurent Vivier 	udelay(1);
2938852ecd9SLaurent Vivier 
2948852ecd9SLaurent Vivier 	local_irq_restore(flags);
2958852ecd9SLaurent Vivier }
2968852ecd9SLaurent Vivier 
2978852ecd9SLaurent Vivier static inline int swim_readbit(struct swim __iomem *base, int bit)
2988852ecd9SLaurent Vivier {
2998852ecd9SLaurent Vivier 	int stat;
3008852ecd9SLaurent Vivier 
3018852ecd9SLaurent Vivier 	swim_select(base, bit);
3028852ecd9SLaurent Vivier 
3038852ecd9SLaurent Vivier 	udelay(10);
3048852ecd9SLaurent Vivier 
3058852ecd9SLaurent Vivier 	stat = swim_read(base, handshake);
3068852ecd9SLaurent Vivier 
3078852ecd9SLaurent Vivier 	return (stat & SENSE) == 0;
3088852ecd9SLaurent Vivier }
3098852ecd9SLaurent Vivier 
3108852ecd9SLaurent Vivier static inline void swim_drive(struct swim __iomem *base,
3118852ecd9SLaurent Vivier 			      enum drive_location location)
3128852ecd9SLaurent Vivier {
3138852ecd9SLaurent Vivier 	if (location == INTERNAL_DRIVE) {
3148852ecd9SLaurent Vivier 		swim_write(base, mode0, EXTERNAL_DRIVE); /* clear drive 1 bit */
3158852ecd9SLaurent Vivier 		swim_write(base, mode1, INTERNAL_DRIVE); /* set drive 0 bit */
3168852ecd9SLaurent Vivier 	} else if (location == EXTERNAL_DRIVE) {
3178852ecd9SLaurent Vivier 		swim_write(base, mode0, INTERNAL_DRIVE); /* clear drive 0 bit */
3188852ecd9SLaurent Vivier 		swim_write(base, mode1, EXTERNAL_DRIVE); /* set drive 1 bit */
3198852ecd9SLaurent Vivier 	}
3208852ecd9SLaurent Vivier }
3218852ecd9SLaurent Vivier 
3228852ecd9SLaurent Vivier static inline void swim_motor(struct swim __iomem *base,
3238852ecd9SLaurent Vivier 			      enum motor_action action)
3248852ecd9SLaurent Vivier {
3258852ecd9SLaurent Vivier 	if (action == ON) {
3268852ecd9SLaurent Vivier 		int i;
3278852ecd9SLaurent Vivier 
3288852ecd9SLaurent Vivier 		swim_action(base, MOTOR_ON);
3298852ecd9SLaurent Vivier 
3308852ecd9SLaurent Vivier 		for (i = 0; i < 2*HZ; i++) {
3318852ecd9SLaurent Vivier 			swim_select(base, RELAX);
3328852ecd9SLaurent Vivier 			if (swim_readbit(base, MOTOR_ON))
3338852ecd9SLaurent Vivier 				break;
3348852ecd9SLaurent Vivier 			current->state = TASK_INTERRUPTIBLE;
3358852ecd9SLaurent Vivier 			schedule_timeout(1);
3368852ecd9SLaurent Vivier 		}
3378852ecd9SLaurent Vivier 	} else if (action == OFF) {
3388852ecd9SLaurent Vivier 		swim_action(base, MOTOR_OFF);
3398852ecd9SLaurent Vivier 		swim_select(base, RELAX);
3408852ecd9SLaurent Vivier 	}
3418852ecd9SLaurent Vivier }
3428852ecd9SLaurent Vivier 
3438852ecd9SLaurent Vivier static inline void swim_eject(struct swim __iomem *base)
3448852ecd9SLaurent Vivier {
3458852ecd9SLaurent Vivier 	int i;
3468852ecd9SLaurent Vivier 
3478852ecd9SLaurent Vivier 	swim_action(base, EJECT);
3488852ecd9SLaurent Vivier 
3498852ecd9SLaurent Vivier 	for (i = 0; i < 2*HZ; i++) {
3508852ecd9SLaurent Vivier 		swim_select(base, RELAX);
3518852ecd9SLaurent Vivier 		if (!swim_readbit(base, DISK_IN))
3528852ecd9SLaurent Vivier 			break;
3538852ecd9SLaurent Vivier 		current->state = TASK_INTERRUPTIBLE;
3548852ecd9SLaurent Vivier 		schedule_timeout(1);
3558852ecd9SLaurent Vivier 	}
3568852ecd9SLaurent Vivier 	swim_select(base, RELAX);
3578852ecd9SLaurent Vivier }
3588852ecd9SLaurent Vivier 
3598852ecd9SLaurent Vivier static inline void swim_head(struct swim __iomem *base, enum head head)
3608852ecd9SLaurent Vivier {
3618852ecd9SLaurent Vivier 	/* wait drive is ready */
3628852ecd9SLaurent Vivier 
3638852ecd9SLaurent Vivier 	if (head == UPPER_HEAD)
3648852ecd9SLaurent Vivier 		swim_select(base, READ_DATA_1);
3658852ecd9SLaurent Vivier 	else if (head == LOWER_HEAD)
3668852ecd9SLaurent Vivier 		swim_select(base, READ_DATA_0);
3678852ecd9SLaurent Vivier }
3688852ecd9SLaurent Vivier 
3698852ecd9SLaurent Vivier static inline int swim_step(struct swim __iomem *base)
3708852ecd9SLaurent Vivier {
3718852ecd9SLaurent Vivier 	int wait;
3728852ecd9SLaurent Vivier 
3738852ecd9SLaurent Vivier 	swim_action(base, STEP);
3748852ecd9SLaurent Vivier 
3758852ecd9SLaurent Vivier 	for (wait = 0; wait < HZ; wait++) {
3768852ecd9SLaurent Vivier 
3778852ecd9SLaurent Vivier 		current->state = TASK_INTERRUPTIBLE;
3788852ecd9SLaurent Vivier 		schedule_timeout(1);
3798852ecd9SLaurent Vivier 
3808852ecd9SLaurent Vivier 		swim_select(base, RELAX);
3818852ecd9SLaurent Vivier 		if (!swim_readbit(base, STEP))
3828852ecd9SLaurent Vivier 			return 0;
3838852ecd9SLaurent Vivier 	}
3848852ecd9SLaurent Vivier 	return -1;
3858852ecd9SLaurent Vivier }
3868852ecd9SLaurent Vivier 
3878852ecd9SLaurent Vivier static inline int swim_track00(struct swim __iomem *base)
3888852ecd9SLaurent Vivier {
3898852ecd9SLaurent Vivier 	int try;
3908852ecd9SLaurent Vivier 
3918852ecd9SLaurent Vivier 	swim_action(base, SEEK_NEGATIVE);
3928852ecd9SLaurent Vivier 
3938852ecd9SLaurent Vivier 	for (try = 0; try < 100; try++) {
3948852ecd9SLaurent Vivier 
3958852ecd9SLaurent Vivier 		swim_select(base, RELAX);
3968852ecd9SLaurent Vivier 		if (swim_readbit(base, TRACK_ZERO))
3978852ecd9SLaurent Vivier 			break;
3988852ecd9SLaurent Vivier 
3998852ecd9SLaurent Vivier 		if (swim_step(base))
4008852ecd9SLaurent Vivier 			return -1;
4018852ecd9SLaurent Vivier 	}
4028852ecd9SLaurent Vivier 
4038852ecd9SLaurent Vivier 	if (swim_readbit(base, TRACK_ZERO))
4048852ecd9SLaurent Vivier 		return 0;
4058852ecd9SLaurent Vivier 
4068852ecd9SLaurent Vivier 	return -1;
4078852ecd9SLaurent Vivier }
4088852ecd9SLaurent Vivier 
4098852ecd9SLaurent Vivier static inline int swim_seek(struct swim __iomem *base, int step)
4108852ecd9SLaurent Vivier {
4118852ecd9SLaurent Vivier 	if (step == 0)
4128852ecd9SLaurent Vivier 		return 0;
4138852ecd9SLaurent Vivier 
4148852ecd9SLaurent Vivier 	if (step < 0) {
4158852ecd9SLaurent Vivier 		swim_action(base, SEEK_NEGATIVE);
4168852ecd9SLaurent Vivier 		step = -step;
4178852ecd9SLaurent Vivier 	} else
4188852ecd9SLaurent Vivier 		swim_action(base, SEEK_POSITIVE);
4198852ecd9SLaurent Vivier 
4208852ecd9SLaurent Vivier 	for ( ; step > 0; step--) {
4218852ecd9SLaurent Vivier 		if (swim_step(base))
4228852ecd9SLaurent Vivier 			return -1;
4238852ecd9SLaurent Vivier 	}
4248852ecd9SLaurent Vivier 
4258852ecd9SLaurent Vivier 	return 0;
4268852ecd9SLaurent Vivier }
4278852ecd9SLaurent Vivier 
4288852ecd9SLaurent Vivier static inline int swim_track(struct floppy_state *fs,  int track)
4298852ecd9SLaurent Vivier {
4308852ecd9SLaurent Vivier 	struct swim __iomem *base = fs->swd->base;
4318852ecd9SLaurent Vivier 	int ret;
4328852ecd9SLaurent Vivier 
4338852ecd9SLaurent Vivier 	ret = swim_seek(base, track - fs->track);
4348852ecd9SLaurent Vivier 
4358852ecd9SLaurent Vivier 	if (ret == 0)
4368852ecd9SLaurent Vivier 		fs->track = track;
4378852ecd9SLaurent Vivier 	else {
4388852ecd9SLaurent Vivier 		swim_track00(base);
4398852ecd9SLaurent Vivier 		fs->track = 0;
4408852ecd9SLaurent Vivier 	}
4418852ecd9SLaurent Vivier 
4428852ecd9SLaurent Vivier 	return ret;
4438852ecd9SLaurent Vivier }
4448852ecd9SLaurent Vivier 
4458852ecd9SLaurent Vivier static int floppy_eject(struct floppy_state *fs)
4468852ecd9SLaurent Vivier {
4478852ecd9SLaurent Vivier 	struct swim __iomem *base = fs->swd->base;
4488852ecd9SLaurent Vivier 
4498852ecd9SLaurent Vivier 	swim_drive(base, fs->location);
4508852ecd9SLaurent Vivier 	swim_motor(base, OFF);
4518852ecd9SLaurent Vivier 	swim_eject(base);
4528852ecd9SLaurent Vivier 
4538852ecd9SLaurent Vivier 	fs->disk_in = 0;
4548852ecd9SLaurent Vivier 	fs->ejected = 1;
4558852ecd9SLaurent Vivier 
4568852ecd9SLaurent Vivier 	return 0;
4578852ecd9SLaurent Vivier }
4588852ecd9SLaurent Vivier 
4598852ecd9SLaurent Vivier static inline int swim_read_sector(struct floppy_state *fs,
4608852ecd9SLaurent Vivier 				   int side, int track,
4618852ecd9SLaurent Vivier 				   int sector, unsigned char *buffer)
4628852ecd9SLaurent Vivier {
4638852ecd9SLaurent Vivier 	struct swim __iomem *base = fs->swd->base;
4648852ecd9SLaurent Vivier 	unsigned long flags;
4658852ecd9SLaurent Vivier 	struct sector_header header;
4668852ecd9SLaurent Vivier 	int ret = -1;
4678852ecd9SLaurent Vivier 	short i;
4688852ecd9SLaurent Vivier 
4698852ecd9SLaurent Vivier 	swim_track(fs, track);
4708852ecd9SLaurent Vivier 
4718852ecd9SLaurent Vivier 	swim_write(base, mode1, MOTON);
4728852ecd9SLaurent Vivier 	swim_head(base, side);
4738852ecd9SLaurent Vivier 	swim_write(base, mode0, side);
4748852ecd9SLaurent Vivier 
4758852ecd9SLaurent Vivier 	local_irq_save(flags);
4768852ecd9SLaurent Vivier 	for (i = 0; i < 36; i++) {
4778852ecd9SLaurent Vivier 		ret = swim_read_sector_header(base, &header);
4788852ecd9SLaurent Vivier 		if (!ret && (header.sector == sector)) {
4798852ecd9SLaurent Vivier 			/* found */
4808852ecd9SLaurent Vivier 
4818852ecd9SLaurent Vivier 			ret = swim_read_sector_data(base, buffer);
4828852ecd9SLaurent Vivier 			break;
4838852ecd9SLaurent Vivier 		}
4848852ecd9SLaurent Vivier 	}
4858852ecd9SLaurent Vivier 	local_irq_restore(flags);
4868852ecd9SLaurent Vivier 
4878852ecd9SLaurent Vivier 	swim_write(base, mode0, MOTON);
4888852ecd9SLaurent Vivier 
4898852ecd9SLaurent Vivier 	if ((header.side != side)  || (header.track != track) ||
4908852ecd9SLaurent Vivier 	     (header.sector != sector))
4918852ecd9SLaurent Vivier 		return 0;
4928852ecd9SLaurent Vivier 
4938852ecd9SLaurent Vivier 	return ret;
4948852ecd9SLaurent Vivier }
4958852ecd9SLaurent Vivier 
4962a842acaSChristoph Hellwig static blk_status_t floppy_read_sectors(struct floppy_state *fs,
4978852ecd9SLaurent Vivier 			       int req_sector, int sectors_nb,
4988852ecd9SLaurent Vivier 			       unsigned char *buffer)
4998852ecd9SLaurent Vivier {
5008852ecd9SLaurent Vivier 	struct swim __iomem *base = fs->swd->base;
5018852ecd9SLaurent Vivier 	int ret;
5028852ecd9SLaurent Vivier 	int side, track, sector;
5038852ecd9SLaurent Vivier 	int i, try;
5048852ecd9SLaurent Vivier 
5058852ecd9SLaurent Vivier 
5068852ecd9SLaurent Vivier 	swim_drive(base, fs->location);
5078852ecd9SLaurent Vivier 	for (i = req_sector; i < req_sector + sectors_nb; i++) {
5088852ecd9SLaurent Vivier 		int x;
5098852ecd9SLaurent Vivier 		track = i / fs->secpercyl;
5108852ecd9SLaurent Vivier 		x = i % fs->secpercyl;
5118852ecd9SLaurent Vivier 		side = x / fs->secpertrack;
5128852ecd9SLaurent Vivier 		sector = x % fs->secpertrack + 1;
5138852ecd9SLaurent Vivier 
5148852ecd9SLaurent Vivier 		try = 5;
5158852ecd9SLaurent Vivier 		do {
5168852ecd9SLaurent Vivier 			ret = swim_read_sector(fs, side, track, sector,
5178852ecd9SLaurent Vivier 						buffer);
5188852ecd9SLaurent Vivier 			if (try-- == 0)
5192a842acaSChristoph Hellwig 				return BLK_STS_IOERR;
5208852ecd9SLaurent Vivier 		} while (ret != 512);
5218852ecd9SLaurent Vivier 
5228852ecd9SLaurent Vivier 		buffer += ret;
5238852ecd9SLaurent Vivier 	}
5248852ecd9SLaurent Vivier 
5258852ecd9SLaurent Vivier 	return 0;
5268852ecd9SLaurent Vivier }
5278852ecd9SLaurent Vivier 
528103db8b2SOmar Sandoval static struct request *swim_next_request(struct swim_priv *swd)
5298852ecd9SLaurent Vivier {
530103db8b2SOmar Sandoval 	struct request_queue *q;
531103db8b2SOmar Sandoval 	struct request *rq;
532103db8b2SOmar Sandoval 	int old_pos = swd->fdc_queue;
533103db8b2SOmar Sandoval 
534103db8b2SOmar Sandoval 	do {
535103db8b2SOmar Sandoval 		q = swd->unit[swd->fdc_queue].disk->queue;
536103db8b2SOmar Sandoval 		if (++swd->fdc_queue == swd->floppy_count)
537103db8b2SOmar Sandoval 			swd->fdc_queue = 0;
538103db8b2SOmar Sandoval 		if (q) {
539103db8b2SOmar Sandoval 			rq = blk_fetch_request(q);
540103db8b2SOmar Sandoval 			if (rq)
541103db8b2SOmar Sandoval 				return rq;
542103db8b2SOmar Sandoval 		}
543103db8b2SOmar Sandoval 	} while (swd->fdc_queue != old_pos);
544103db8b2SOmar Sandoval 
545103db8b2SOmar Sandoval 	return NULL;
546103db8b2SOmar Sandoval }
547103db8b2SOmar Sandoval 
548103db8b2SOmar Sandoval static void do_fd_request(struct request_queue *q)
549103db8b2SOmar Sandoval {
550103db8b2SOmar Sandoval 	struct swim_priv *swd = q->queuedata;
5518852ecd9SLaurent Vivier 	struct request *req;
5528852ecd9SLaurent Vivier 	struct floppy_state *fs;
5538852ecd9SLaurent Vivier 
554103db8b2SOmar Sandoval 	req = swim_next_request(swd);
55506b0608eSTejun Heo 	while (req) {
5562a842acaSChristoph Hellwig 		blk_status_t err = BLK_STS_IOERR;
5578852ecd9SLaurent Vivier 
5588852ecd9SLaurent Vivier 		fs = req->rq_disk->private_data;
55906b0608eSTejun Heo 		if (blk_rq_pos(req) >= fs->total_secs)
56006b0608eSTejun Heo 			goto done;
56106b0608eSTejun Heo 		if (!fs->disk_in)
56206b0608eSTejun Heo 			goto done;
56306b0608eSTejun Heo 		if (rq_data_dir(req) == WRITE && fs->write_protected)
56406b0608eSTejun Heo 			goto done;
56506b0608eSTejun Heo 
5668852ecd9SLaurent Vivier 		switch (rq_data_dir(req)) {
5678852ecd9SLaurent Vivier 		case WRITE:
5688852ecd9SLaurent Vivier 			/* NOT IMPLEMENTED */
5698852ecd9SLaurent Vivier 			break;
5708852ecd9SLaurent Vivier 		case READ:
57106b0608eSTejun Heo 			err = floppy_read_sectors(fs, blk_rq_pos(req),
57283096ebfSTejun Heo 						  blk_rq_cur_sectors(req),
573b4f42e28SJens Axboe 						  bio_data(req->bio));
5748852ecd9SLaurent Vivier 			break;
5758852ecd9SLaurent Vivier 		}
57606b0608eSTejun Heo 	done:
5779934c8c0STejun Heo 		if (!__blk_end_request_cur(req, err))
578103db8b2SOmar Sandoval 			req = swim_next_request(swd);
5798852ecd9SLaurent Vivier 	}
5808852ecd9SLaurent Vivier }
5818852ecd9SLaurent Vivier 
5828852ecd9SLaurent Vivier static struct floppy_struct floppy_type[4] = {
5838852ecd9SLaurent Vivier 	{    0,  0, 0,  0, 0, 0x00, 0x00, 0x00, 0x00, NULL }, /* no testing   */
5848852ecd9SLaurent Vivier 	{  720,  9, 1, 80, 0, 0x2A, 0x02, 0xDF, 0x50, NULL }, /* 360KB SS 3.5"*/
5858852ecd9SLaurent Vivier 	{ 1440,  9, 2, 80, 0, 0x2A, 0x02, 0xDF, 0x50, NULL }, /* 720KB 3.5"   */
5868852ecd9SLaurent Vivier 	{ 2880, 18, 2, 80, 0, 0x1B, 0x00, 0xCF, 0x6C, NULL }, /* 1.44MB 3.5"  */
5878852ecd9SLaurent Vivier };
5888852ecd9SLaurent Vivier 
5898852ecd9SLaurent Vivier static int get_floppy_geometry(struct floppy_state *fs, int type,
5908852ecd9SLaurent Vivier 			       struct floppy_struct **g)
5918852ecd9SLaurent Vivier {
5928852ecd9SLaurent Vivier 	if (type >= ARRAY_SIZE(floppy_type))
5938852ecd9SLaurent Vivier 		return -EINVAL;
5948852ecd9SLaurent Vivier 
5958852ecd9SLaurent Vivier 	if (type)
5968852ecd9SLaurent Vivier 		*g = &floppy_type[type];
5978852ecd9SLaurent Vivier 	else if (fs->type == HD_MEDIA) /* High-Density media */
5988852ecd9SLaurent Vivier 		*g = &floppy_type[3];
5998852ecd9SLaurent Vivier 	else if (fs->head_number == 2) /* double-sided */
6008852ecd9SLaurent Vivier 		*g = &floppy_type[2];
6018852ecd9SLaurent Vivier 	else
6028852ecd9SLaurent Vivier 		*g = &floppy_type[1];
6038852ecd9SLaurent Vivier 
6048852ecd9SLaurent Vivier 	return 0;
6058852ecd9SLaurent Vivier }
6068852ecd9SLaurent Vivier 
6078852ecd9SLaurent Vivier static void setup_medium(struct floppy_state *fs)
6088852ecd9SLaurent Vivier {
6098852ecd9SLaurent Vivier 	struct swim __iomem *base = fs->swd->base;
6108852ecd9SLaurent Vivier 
6118852ecd9SLaurent Vivier 	if (swim_readbit(base, DISK_IN)) {
6128852ecd9SLaurent Vivier 		struct floppy_struct *g;
6138852ecd9SLaurent Vivier 		fs->disk_in = 1;
6148852ecd9SLaurent Vivier 		fs->write_protected = swim_readbit(base, WRITE_PROT);
6158852ecd9SLaurent Vivier 
6168852ecd9SLaurent Vivier 		if (swim_track00(base))
6178852ecd9SLaurent Vivier 			printk(KERN_ERR
6188852ecd9SLaurent Vivier 				"SWIM: cannot move floppy head to track 0\n");
6198852ecd9SLaurent Vivier 
6208852ecd9SLaurent Vivier 		swim_track00(base);
6218852ecd9SLaurent Vivier 
6225a13388dSFinn Thain 		fs->type = swim_readbit(base, TWOMEG_MEDIA) ?
6235a13388dSFinn Thain 			HD_MEDIA : DD_MEDIA;
6245a13388dSFinn Thain 		fs->head_number = swim_readbit(base, SINGLE_SIDED) ? 1 : 2;
6258852ecd9SLaurent Vivier 		get_floppy_geometry(fs, 0, &g);
6268852ecd9SLaurent Vivier 		fs->total_secs = g->size;
6278852ecd9SLaurent Vivier 		fs->secpercyl = g->head * g->sect;
6288852ecd9SLaurent Vivier 		fs->secpertrack = g->sect;
6298852ecd9SLaurent Vivier 		fs->track = 0;
6308852ecd9SLaurent Vivier 	} else {
6318852ecd9SLaurent Vivier 		fs->disk_in = 0;
6328852ecd9SLaurent Vivier 	}
6338852ecd9SLaurent Vivier }
6348852ecd9SLaurent Vivier 
6358852ecd9SLaurent Vivier static int floppy_open(struct block_device *bdev, fmode_t mode)
6368852ecd9SLaurent Vivier {
6378852ecd9SLaurent Vivier 	struct floppy_state *fs = bdev->bd_disk->private_data;
6388852ecd9SLaurent Vivier 	struct swim __iomem *base = fs->swd->base;
6398852ecd9SLaurent Vivier 	int err;
6408852ecd9SLaurent Vivier 
6418852ecd9SLaurent Vivier 	if (fs->ref_count == -1 || (fs->ref_count && mode & FMODE_EXCL))
6428852ecd9SLaurent Vivier 		return -EBUSY;
6438852ecd9SLaurent Vivier 
6448852ecd9SLaurent Vivier 	if (mode & FMODE_EXCL)
6458852ecd9SLaurent Vivier 		fs->ref_count = -1;
6468852ecd9SLaurent Vivier 	else
6478852ecd9SLaurent Vivier 		fs->ref_count++;
6488852ecd9SLaurent Vivier 
6498852ecd9SLaurent Vivier 	swim_write(base, setup, S_IBM_DRIVE  | S_FCLK_DIV2);
6508852ecd9SLaurent Vivier 	udelay(10);
651b3906535SFinn Thain 	swim_drive(base, fs->location);
6528852ecd9SLaurent Vivier 	swim_motor(base, ON);
6538852ecd9SLaurent Vivier 	swim_action(base, SETMFM);
6548852ecd9SLaurent Vivier 	if (fs->ejected)
6558852ecd9SLaurent Vivier 		setup_medium(fs);
6568852ecd9SLaurent Vivier 	if (!fs->disk_in) {
6578852ecd9SLaurent Vivier 		err = -ENXIO;
6588852ecd9SLaurent Vivier 		goto out;
6598852ecd9SLaurent Vivier 	}
6608852ecd9SLaurent Vivier 
6615a13388dSFinn Thain 	set_capacity(fs->disk, fs->total_secs);
6625a13388dSFinn Thain 
6638852ecd9SLaurent Vivier 	if (mode & FMODE_NDELAY)
6648852ecd9SLaurent Vivier 		return 0;
6658852ecd9SLaurent Vivier 
6668852ecd9SLaurent Vivier 	if (mode & (FMODE_READ|FMODE_WRITE)) {
6678852ecd9SLaurent Vivier 		check_disk_change(bdev);
6688852ecd9SLaurent Vivier 		if ((mode & FMODE_WRITE) && fs->write_protected) {
6698852ecd9SLaurent Vivier 			err = -EROFS;
6708852ecd9SLaurent Vivier 			goto out;
6718852ecd9SLaurent Vivier 		}
6728852ecd9SLaurent Vivier 	}
6738852ecd9SLaurent Vivier 	return 0;
6748852ecd9SLaurent Vivier out:
6758852ecd9SLaurent Vivier 	if (fs->ref_count < 0)
6768852ecd9SLaurent Vivier 		fs->ref_count = 0;
6778852ecd9SLaurent Vivier 	else if (fs->ref_count > 0)
6788852ecd9SLaurent Vivier 		--fs->ref_count;
6798852ecd9SLaurent Vivier 
6808852ecd9SLaurent Vivier 	if (fs->ref_count == 0)
6818852ecd9SLaurent Vivier 		swim_motor(base, OFF);
6828852ecd9SLaurent Vivier 	return err;
6838852ecd9SLaurent Vivier }
6848852ecd9SLaurent Vivier 
6856e9624b8SArnd Bergmann static int floppy_unlocked_open(struct block_device *bdev, fmode_t mode)
6866e9624b8SArnd Bergmann {
6876e9624b8SArnd Bergmann 	int ret;
6886e9624b8SArnd Bergmann 
6892a48fc0aSArnd Bergmann 	mutex_lock(&swim_mutex);
6906e9624b8SArnd Bergmann 	ret = floppy_open(bdev, mode);
6912a48fc0aSArnd Bergmann 	mutex_unlock(&swim_mutex);
6926e9624b8SArnd Bergmann 
6936e9624b8SArnd Bergmann 	return ret;
6946e9624b8SArnd Bergmann }
6956e9624b8SArnd Bergmann 
696db2a144bSAl Viro static void floppy_release(struct gendisk *disk, fmode_t mode)
6978852ecd9SLaurent Vivier {
6988852ecd9SLaurent Vivier 	struct floppy_state *fs = disk->private_data;
6998852ecd9SLaurent Vivier 	struct swim __iomem *base = fs->swd->base;
7008852ecd9SLaurent Vivier 
7012a48fc0aSArnd Bergmann 	mutex_lock(&swim_mutex);
7028852ecd9SLaurent Vivier 	if (fs->ref_count < 0)
7038852ecd9SLaurent Vivier 		fs->ref_count = 0;
7048852ecd9SLaurent Vivier 	else if (fs->ref_count > 0)
7058852ecd9SLaurent Vivier 		--fs->ref_count;
7068852ecd9SLaurent Vivier 
7078852ecd9SLaurent Vivier 	if (fs->ref_count == 0)
7088852ecd9SLaurent Vivier 		swim_motor(base, OFF);
7092a48fc0aSArnd Bergmann 	mutex_unlock(&swim_mutex);
7108852ecd9SLaurent Vivier }
7118852ecd9SLaurent Vivier 
7128852ecd9SLaurent Vivier static int floppy_ioctl(struct block_device *bdev, fmode_t mode,
7138852ecd9SLaurent Vivier 			unsigned int cmd, unsigned long param)
7148852ecd9SLaurent Vivier {
7158852ecd9SLaurent Vivier 	struct floppy_state *fs = bdev->bd_disk->private_data;
7168852ecd9SLaurent Vivier 	int err;
7178852ecd9SLaurent Vivier 
7188852ecd9SLaurent Vivier 	if ((cmd & 0x80) && !capable(CAP_SYS_ADMIN))
7198852ecd9SLaurent Vivier 			return -EPERM;
7208852ecd9SLaurent Vivier 
7218852ecd9SLaurent Vivier 	switch (cmd) {
7228852ecd9SLaurent Vivier 	case FDEJECT:
7238852ecd9SLaurent Vivier 		if (fs->ref_count != 1)
7248852ecd9SLaurent Vivier 			return -EBUSY;
7252a48fc0aSArnd Bergmann 		mutex_lock(&swim_mutex);
7268852ecd9SLaurent Vivier 		err = floppy_eject(fs);
7272a48fc0aSArnd Bergmann 		mutex_unlock(&swim_mutex);
7288852ecd9SLaurent Vivier 		return err;
7298852ecd9SLaurent Vivier 
7308852ecd9SLaurent Vivier 	case FDGETPRM:
7318852ecd9SLaurent Vivier 		if (copy_to_user((void __user *) param, (void *) &floppy_type,
7328852ecd9SLaurent Vivier 				 sizeof(struct floppy_struct)))
7338852ecd9SLaurent Vivier 			return -EFAULT;
7348852ecd9SLaurent Vivier 		return 0;
7358852ecd9SLaurent Vivier 	}
7368e2ab5a4SFinn Thain 	return -ENOTTY;
7378e2ab5a4SFinn Thain }
7388852ecd9SLaurent Vivier 
7398852ecd9SLaurent Vivier static int floppy_getgeo(struct block_device *bdev, struct hd_geometry *geo)
7408852ecd9SLaurent Vivier {
7418852ecd9SLaurent Vivier 	struct floppy_state *fs = bdev->bd_disk->private_data;
7428852ecd9SLaurent Vivier 	struct floppy_struct *g;
7438852ecd9SLaurent Vivier 	int ret;
7448852ecd9SLaurent Vivier 
7458852ecd9SLaurent Vivier 	ret = get_floppy_geometry(fs, 0, &g);
7468852ecd9SLaurent Vivier 	if (ret)
7478852ecd9SLaurent Vivier 		return ret;
7488852ecd9SLaurent Vivier 
7498852ecd9SLaurent Vivier 	geo->heads = g->head;
7508852ecd9SLaurent Vivier 	geo->sectors = g->sect;
7518852ecd9SLaurent Vivier 	geo->cylinders = g->track;
7528852ecd9SLaurent Vivier 
7538852ecd9SLaurent Vivier 	return 0;
7548852ecd9SLaurent Vivier }
7558852ecd9SLaurent Vivier 
7564bbde777STejun Heo static unsigned int floppy_check_events(struct gendisk *disk,
7574bbde777STejun Heo 					unsigned int clearing)
7588852ecd9SLaurent Vivier {
7598852ecd9SLaurent Vivier 	struct floppy_state *fs = disk->private_data;
7608852ecd9SLaurent Vivier 
7614bbde777STejun Heo 	return fs->ejected ? DISK_EVENT_MEDIA_CHANGE : 0;
7628852ecd9SLaurent Vivier }
7638852ecd9SLaurent Vivier 
7648852ecd9SLaurent Vivier static int floppy_revalidate(struct gendisk *disk)
7658852ecd9SLaurent Vivier {
7668852ecd9SLaurent Vivier 	struct floppy_state *fs = disk->private_data;
7678852ecd9SLaurent Vivier 	struct swim __iomem *base = fs->swd->base;
7688852ecd9SLaurent Vivier 
7698852ecd9SLaurent Vivier 	swim_drive(base, fs->location);
7708852ecd9SLaurent Vivier 
7718852ecd9SLaurent Vivier 	if (fs->ejected)
7728852ecd9SLaurent Vivier 		setup_medium(fs);
7738852ecd9SLaurent Vivier 
7748852ecd9SLaurent Vivier 	if (!fs->disk_in)
7758852ecd9SLaurent Vivier 		swim_motor(base, OFF);
7768852ecd9SLaurent Vivier 	else
7778852ecd9SLaurent Vivier 		fs->ejected = 0;
7788852ecd9SLaurent Vivier 
7798852ecd9SLaurent Vivier 	return !fs->disk_in;
7808852ecd9SLaurent Vivier }
7818852ecd9SLaurent Vivier 
78283d5cde4SAlexey Dobriyan static const struct block_device_operations floppy_fops = {
7838852ecd9SLaurent Vivier 	.owner		 = THIS_MODULE,
7846e9624b8SArnd Bergmann 	.open		 = floppy_unlocked_open,
7858852ecd9SLaurent Vivier 	.release	 = floppy_release,
7868a6cfeb6SArnd Bergmann 	.ioctl		 = floppy_ioctl,
7878852ecd9SLaurent Vivier 	.getgeo		 = floppy_getgeo,
7884bbde777STejun Heo 	.check_events	 = floppy_check_events,
7898852ecd9SLaurent Vivier 	.revalidate_disk = floppy_revalidate,
7908852ecd9SLaurent Vivier };
7918852ecd9SLaurent Vivier 
7928852ecd9SLaurent Vivier static struct kobject *floppy_find(dev_t dev, int *part, void *data)
7938852ecd9SLaurent Vivier {
7948852ecd9SLaurent Vivier 	struct swim_priv *swd = data;
7958852ecd9SLaurent Vivier 	int drive = (*part & 3);
7968852ecd9SLaurent Vivier 
7977ae6a2b6SFinn Thain 	if (drive >= swd->floppy_count)
7988852ecd9SLaurent Vivier 		return NULL;
7998852ecd9SLaurent Vivier 
8008852ecd9SLaurent Vivier 	*part = 0;
8013079c22eSJan Kara 	return get_disk_and_module(swd->unit[drive].disk);
8028852ecd9SLaurent Vivier }
8038852ecd9SLaurent Vivier 
8048d85fce7SGreg Kroah-Hartman static int swim_add_floppy(struct swim_priv *swd, enum drive_location location)
8058852ecd9SLaurent Vivier {
8068852ecd9SLaurent Vivier 	struct floppy_state *fs = &swd->unit[swd->floppy_count];
8078852ecd9SLaurent Vivier 	struct swim __iomem *base = swd->base;
8088852ecd9SLaurent Vivier 
8098852ecd9SLaurent Vivier 	fs->location = location;
8108852ecd9SLaurent Vivier 
8118852ecd9SLaurent Vivier 	swim_drive(base, location);
8128852ecd9SLaurent Vivier 
8138852ecd9SLaurent Vivier 	swim_motor(base, OFF);
8148852ecd9SLaurent Vivier 
8155a13388dSFinn Thain 	fs->type = HD_MEDIA;
8168852ecd9SLaurent Vivier 	fs->head_number = 2;
8175a13388dSFinn Thain 
8188852ecd9SLaurent Vivier 	fs->ref_count = 0;
8198852ecd9SLaurent Vivier 	fs->ejected = 1;
8208852ecd9SLaurent Vivier 
8218852ecd9SLaurent Vivier 	swd->floppy_count++;
8228852ecd9SLaurent Vivier 
8238852ecd9SLaurent Vivier 	return 0;
8248852ecd9SLaurent Vivier }
8258852ecd9SLaurent Vivier 
8268d85fce7SGreg Kroah-Hartman static int swim_floppy_init(struct swim_priv *swd)
8278852ecd9SLaurent Vivier {
8288852ecd9SLaurent Vivier 	int err;
8298852ecd9SLaurent Vivier 	int drive;
8308852ecd9SLaurent Vivier 	struct swim __iomem *base = swd->base;
8318852ecd9SLaurent Vivier 
8328852ecd9SLaurent Vivier 	/* scan floppy drives */
8338852ecd9SLaurent Vivier 
8348852ecd9SLaurent Vivier 	swim_drive(base, INTERNAL_DRIVE);
8358a500df6SFinn Thain 	if (swim_readbit(base, DRIVE_PRESENT) &&
8368a500df6SFinn Thain 	    !swim_readbit(base, ONEMEG_DRIVE))
8378852ecd9SLaurent Vivier 		swim_add_floppy(swd, INTERNAL_DRIVE);
8388852ecd9SLaurent Vivier 	swim_drive(base, EXTERNAL_DRIVE);
8398a500df6SFinn Thain 	if (swim_readbit(base, DRIVE_PRESENT) &&
8408a500df6SFinn Thain 	    !swim_readbit(base, ONEMEG_DRIVE))
8418852ecd9SLaurent Vivier 		swim_add_floppy(swd, EXTERNAL_DRIVE);
8428852ecd9SLaurent Vivier 
8438852ecd9SLaurent Vivier 	/* register floppy drives */
8448852ecd9SLaurent Vivier 
8458852ecd9SLaurent Vivier 	err = register_blkdev(FLOPPY_MAJOR, "fd");
8468852ecd9SLaurent Vivier 	if (err) {
8478852ecd9SLaurent Vivier 		printk(KERN_ERR "Unable to get major %d for SWIM floppy\n",
8488852ecd9SLaurent Vivier 		       FLOPPY_MAJOR);
8498852ecd9SLaurent Vivier 		return -EBUSY;
8508852ecd9SLaurent Vivier 	}
8518852ecd9SLaurent Vivier 
852103db8b2SOmar Sandoval 	spin_lock_init(&swd->lock);
853103db8b2SOmar Sandoval 
8548852ecd9SLaurent Vivier 	for (drive = 0; drive < swd->floppy_count; drive++) {
8558852ecd9SLaurent Vivier 		swd->unit[drive].disk = alloc_disk(1);
8568852ecd9SLaurent Vivier 		if (swd->unit[drive].disk == NULL) {
8578852ecd9SLaurent Vivier 			err = -ENOMEM;
8588852ecd9SLaurent Vivier 			goto exit_put_disks;
8598852ecd9SLaurent Vivier 		}
860103db8b2SOmar Sandoval 		swd->unit[drive].disk->queue = blk_init_queue(do_fd_request,
861103db8b2SOmar Sandoval 							      &swd->lock);
862103db8b2SOmar Sandoval 		if (!swd->unit[drive].disk->queue) {
8638852ecd9SLaurent Vivier 			err = -ENOMEM;
8648852ecd9SLaurent Vivier 			goto exit_put_disks;
8658852ecd9SLaurent Vivier 		}
8668fc45044SChristoph Hellwig 		blk_queue_bounce_limit(swd->unit[drive].disk->queue,
8678fc45044SChristoph Hellwig 				BLK_BOUNCE_HIGH);
868103db8b2SOmar Sandoval 		swd->unit[drive].disk->queue->queuedata = swd;
869103db8b2SOmar Sandoval 		swd->unit[drive].swd = swd;
870103db8b2SOmar Sandoval 	}
8718852ecd9SLaurent Vivier 
8728852ecd9SLaurent Vivier 	for (drive = 0; drive < swd->floppy_count; drive++) {
8738852ecd9SLaurent Vivier 		swd->unit[drive].disk->flags = GENHD_FL_REMOVABLE;
8748852ecd9SLaurent Vivier 		swd->unit[drive].disk->major = FLOPPY_MAJOR;
8758852ecd9SLaurent Vivier 		swd->unit[drive].disk->first_minor = drive;
8768852ecd9SLaurent Vivier 		sprintf(swd->unit[drive].disk->disk_name, "fd%d", drive);
8778852ecd9SLaurent Vivier 		swd->unit[drive].disk->fops = &floppy_fops;
8788852ecd9SLaurent Vivier 		swd->unit[drive].disk->private_data = &swd->unit[drive];
8798852ecd9SLaurent Vivier 		set_capacity(swd->unit[drive].disk, 2880);
8808852ecd9SLaurent Vivier 		add_disk(swd->unit[drive].disk);
8818852ecd9SLaurent Vivier 	}
8828852ecd9SLaurent Vivier 
8838852ecd9SLaurent Vivier 	blk_register_region(MKDEV(FLOPPY_MAJOR, 0), 256, THIS_MODULE,
8848852ecd9SLaurent Vivier 			    floppy_find, NULL, swd);
8858852ecd9SLaurent Vivier 
8868852ecd9SLaurent Vivier 	return 0;
8878852ecd9SLaurent Vivier 
8888852ecd9SLaurent Vivier exit_put_disks:
8898852ecd9SLaurent Vivier 	unregister_blkdev(FLOPPY_MAJOR, "fd");
8901448a2a5SOmar Sandoval 	do {
8911448a2a5SOmar Sandoval 		struct gendisk *disk = swd->unit[drive].disk;
8921448a2a5SOmar Sandoval 
8931448a2a5SOmar Sandoval 		if (disk) {
8941448a2a5SOmar Sandoval 			if (disk->queue) {
8951448a2a5SOmar Sandoval 				blk_cleanup_queue(disk->queue);
8961448a2a5SOmar Sandoval 				disk->queue = NULL;
8971448a2a5SOmar Sandoval 			}
8981448a2a5SOmar Sandoval 			put_disk(disk);
8991448a2a5SOmar Sandoval 		}
9001448a2a5SOmar Sandoval 	} while (drive--);
9018852ecd9SLaurent Vivier 	return err;
9028852ecd9SLaurent Vivier }
9038852ecd9SLaurent Vivier 
9048d85fce7SGreg Kroah-Hartman static int swim_probe(struct platform_device *dev)
9058852ecd9SLaurent Vivier {
9068852ecd9SLaurent Vivier 	struct resource *res;
9078852ecd9SLaurent Vivier 	struct swim __iomem *swim_base;
9088852ecd9SLaurent Vivier 	struct swim_priv *swd;
9098852ecd9SLaurent Vivier 	int ret;
9108852ecd9SLaurent Vivier 
9112724daf4SFinn Thain 	res = platform_get_resource(dev, IORESOURCE_MEM, 0);
9128852ecd9SLaurent Vivier 	if (!res) {
9138852ecd9SLaurent Vivier 		ret = -ENODEV;
9148852ecd9SLaurent Vivier 		goto out;
9158852ecd9SLaurent Vivier 	}
9168852ecd9SLaurent Vivier 
9178852ecd9SLaurent Vivier 	if (!request_mem_region(res->start, resource_size(res), CARDNAME)) {
9188852ecd9SLaurent Vivier 		ret = -EBUSY;
9198852ecd9SLaurent Vivier 		goto out;
9208852ecd9SLaurent Vivier 	}
9218852ecd9SLaurent Vivier 
922b64576cbSFinn Thain 	swim_base = (struct swim __iomem *)res->start;
9238852ecd9SLaurent Vivier 	if (!swim_base) {
924957d6bf6SJoe Perches 		ret = -ENOMEM;
9258852ecd9SLaurent Vivier 		goto out_release_io;
9268852ecd9SLaurent Vivier 	}
9278852ecd9SLaurent Vivier 
9288852ecd9SLaurent Vivier 	/* probe device */
9298852ecd9SLaurent Vivier 
9308852ecd9SLaurent Vivier 	set_swim_mode(swim_base, 1);
9318852ecd9SLaurent Vivier 	if (!get_swim_mode(swim_base)) {
9328852ecd9SLaurent Vivier 		printk(KERN_INFO "SWIM device not found !\n");
9338852ecd9SLaurent Vivier 		ret = -ENODEV;
934b64576cbSFinn Thain 		goto out_release_io;
9358852ecd9SLaurent Vivier 	}
9368852ecd9SLaurent Vivier 
9378852ecd9SLaurent Vivier 	/* set platform driver data */
9388852ecd9SLaurent Vivier 
9398852ecd9SLaurent Vivier 	swd = kzalloc(sizeof(struct swim_priv), GFP_KERNEL);
9408852ecd9SLaurent Vivier 	if (!swd) {
9418852ecd9SLaurent Vivier 		ret = -ENOMEM;
942b64576cbSFinn Thain 		goto out_release_io;
9438852ecd9SLaurent Vivier 	}
9448852ecd9SLaurent Vivier 	platform_set_drvdata(dev, swd);
9458852ecd9SLaurent Vivier 
9468852ecd9SLaurent Vivier 	swd->base = swim_base;
9478852ecd9SLaurent Vivier 
9488852ecd9SLaurent Vivier 	ret = swim_floppy_init(swd);
9498852ecd9SLaurent Vivier 	if (ret)
9508852ecd9SLaurent Vivier 		goto out_kfree;
9518852ecd9SLaurent Vivier 
9528852ecd9SLaurent Vivier 	return 0;
9538852ecd9SLaurent Vivier 
9548852ecd9SLaurent Vivier out_kfree:
9558852ecd9SLaurent Vivier 	kfree(swd);
9568852ecd9SLaurent Vivier out_release_io:
9578852ecd9SLaurent Vivier 	release_mem_region(res->start, resource_size(res));
9588852ecd9SLaurent Vivier out:
9598852ecd9SLaurent Vivier 	return ret;
9608852ecd9SLaurent Vivier }
9618852ecd9SLaurent Vivier 
9628d85fce7SGreg Kroah-Hartman static int swim_remove(struct platform_device *dev)
9638852ecd9SLaurent Vivier {
9648852ecd9SLaurent Vivier 	struct swim_priv *swd = platform_get_drvdata(dev);
9658852ecd9SLaurent Vivier 	int drive;
9668852ecd9SLaurent Vivier 	struct resource *res;
9678852ecd9SLaurent Vivier 
9688852ecd9SLaurent Vivier 	blk_unregister_region(MKDEV(FLOPPY_MAJOR, 0), 256);
9698852ecd9SLaurent Vivier 
9708852ecd9SLaurent Vivier 	for (drive = 0; drive < swd->floppy_count; drive++) {
9718852ecd9SLaurent Vivier 		del_gendisk(swd->unit[drive].disk);
972103db8b2SOmar Sandoval 		blk_cleanup_queue(swd->unit[drive].disk->queue);
9738852ecd9SLaurent Vivier 		put_disk(swd->unit[drive].disk);
9748852ecd9SLaurent Vivier 	}
9758852ecd9SLaurent Vivier 
9768852ecd9SLaurent Vivier 	unregister_blkdev(FLOPPY_MAJOR, "fd");
9778852ecd9SLaurent Vivier 
9788852ecd9SLaurent Vivier 	/* eject floppies */
9798852ecd9SLaurent Vivier 
9808852ecd9SLaurent Vivier 	for (drive = 0; drive < swd->floppy_count; drive++)
9818852ecd9SLaurent Vivier 		floppy_eject(&swd->unit[drive]);
9828852ecd9SLaurent Vivier 
9832724daf4SFinn Thain 	res = platform_get_resource(dev, IORESOURCE_MEM, 0);
9848852ecd9SLaurent Vivier 	if (res)
9858852ecd9SLaurent Vivier 		release_mem_region(res->start, resource_size(res));
9868852ecd9SLaurent Vivier 
9878852ecd9SLaurent Vivier 	kfree(swd);
9888852ecd9SLaurent Vivier 
9898852ecd9SLaurent Vivier 	return 0;
9908852ecd9SLaurent Vivier }
9918852ecd9SLaurent Vivier 
9928852ecd9SLaurent Vivier static struct platform_driver swim_driver = {
9938852ecd9SLaurent Vivier 	.probe  = swim_probe,
9948d85fce7SGreg Kroah-Hartman 	.remove = swim_remove,
9958852ecd9SLaurent Vivier 	.driver   = {
9968852ecd9SLaurent Vivier 		.name	= CARDNAME,
9978852ecd9SLaurent Vivier 	},
9988852ecd9SLaurent Vivier };
9998852ecd9SLaurent Vivier 
10008852ecd9SLaurent Vivier static int __init swim_init(void)
10018852ecd9SLaurent Vivier {
10028852ecd9SLaurent Vivier 	printk(KERN_INFO "SWIM floppy driver %s\n", DRIVER_VERSION);
10038852ecd9SLaurent Vivier 
10048852ecd9SLaurent Vivier 	return platform_driver_register(&swim_driver);
10058852ecd9SLaurent Vivier }
10068852ecd9SLaurent Vivier module_init(swim_init);
10078852ecd9SLaurent Vivier 
10088852ecd9SLaurent Vivier static void __exit swim_exit(void)
10098852ecd9SLaurent Vivier {
10108852ecd9SLaurent Vivier 	platform_driver_unregister(&swim_driver);
10118852ecd9SLaurent Vivier }
10128852ecd9SLaurent Vivier module_exit(swim_exit);
10138852ecd9SLaurent Vivier 
10148852ecd9SLaurent Vivier MODULE_DESCRIPTION("Driver for SWIM floppy controller");
10158852ecd9SLaurent Vivier MODULE_LICENSE("GPL");
10168852ecd9SLaurent Vivier MODULE_AUTHOR("Laurent Vivier <laurent@lvivier.info>");
10178852ecd9SLaurent Vivier MODULE_ALIAS_BLOCKDEV_MAJOR(FLOPPY_MAJOR);
1018