18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Driver for the SWIM3 (Super Woz Integrated Machine 3)
48c2ecf20Sopenharmony_ci * floppy controller found on Power Macintoshes.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Copyright (C) 1996 Paul Mackerras.
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci/*
108c2ecf20Sopenharmony_ci * TODO:
118c2ecf20Sopenharmony_ci * handle 2 drives
128c2ecf20Sopenharmony_ci * handle GCR disks
138c2ecf20Sopenharmony_ci */
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#undef DEBUG
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include <linux/stddef.h>
188c2ecf20Sopenharmony_ci#include <linux/kernel.h>
198c2ecf20Sopenharmony_ci#include <linux/sched/signal.h>
208c2ecf20Sopenharmony_ci#include <linux/timer.h>
218c2ecf20Sopenharmony_ci#include <linux/delay.h>
228c2ecf20Sopenharmony_ci#include <linux/fd.h>
238c2ecf20Sopenharmony_ci#include <linux/ioctl.h>
248c2ecf20Sopenharmony_ci#include <linux/blk-mq.h>
258c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
268c2ecf20Sopenharmony_ci#include <linux/mutex.h>
278c2ecf20Sopenharmony_ci#include <linux/module.h>
288c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
298c2ecf20Sopenharmony_ci#include <linux/wait.h>
308c2ecf20Sopenharmony_ci#include <asm/io.h>
318c2ecf20Sopenharmony_ci#include <asm/dbdma.h>
328c2ecf20Sopenharmony_ci#include <asm/prom.h>
338c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
348c2ecf20Sopenharmony_ci#include <asm/mediabay.h>
358c2ecf20Sopenharmony_ci#include <asm/machdep.h>
368c2ecf20Sopenharmony_ci#include <asm/pmac_feature.h>
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci#define MAX_FLOPPIES	2
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(swim3_mutex);
418c2ecf20Sopenharmony_cistatic struct gendisk *disks[MAX_FLOPPIES];
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cienum swim_state {
448c2ecf20Sopenharmony_ci	idle,
458c2ecf20Sopenharmony_ci	locating,
468c2ecf20Sopenharmony_ci	seeking,
478c2ecf20Sopenharmony_ci	settling,
488c2ecf20Sopenharmony_ci	do_transfer,
498c2ecf20Sopenharmony_ci	jogging,
508c2ecf20Sopenharmony_ci	available,
518c2ecf20Sopenharmony_ci	revalidating,
528c2ecf20Sopenharmony_ci	ejecting
538c2ecf20Sopenharmony_ci};
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci#define REG(x)	unsigned char x; char x ## _pad[15];
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci/*
588c2ecf20Sopenharmony_ci * The names for these registers mostly represent speculation on my part.
598c2ecf20Sopenharmony_ci * It will be interesting to see how close they are to the names Apple uses.
608c2ecf20Sopenharmony_ci */
618c2ecf20Sopenharmony_cistruct swim3 {
628c2ecf20Sopenharmony_ci	REG(data);
638c2ecf20Sopenharmony_ci	REG(timer);		/* counts down at 1MHz */
648c2ecf20Sopenharmony_ci	REG(error);
658c2ecf20Sopenharmony_ci	REG(mode);
668c2ecf20Sopenharmony_ci	REG(select);		/* controls CA0, CA1, CA2 and LSTRB signals */
678c2ecf20Sopenharmony_ci	REG(setup);
688c2ecf20Sopenharmony_ci	REG(control);		/* writing bits clears them */
698c2ecf20Sopenharmony_ci	REG(status);		/* writing bits sets them in control */
708c2ecf20Sopenharmony_ci	REG(intr);
718c2ecf20Sopenharmony_ci	REG(nseek);		/* # tracks to seek */
728c2ecf20Sopenharmony_ci	REG(ctrack);		/* current track number */
738c2ecf20Sopenharmony_ci	REG(csect);		/* current sector number */
748c2ecf20Sopenharmony_ci	REG(gap3);		/* size of gap 3 in track format */
758c2ecf20Sopenharmony_ci	REG(sector);		/* sector # to read or write */
768c2ecf20Sopenharmony_ci	REG(nsect);		/* # sectors to read or write */
778c2ecf20Sopenharmony_ci	REG(intr_enable);
788c2ecf20Sopenharmony_ci};
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci#define control_bic	control
818c2ecf20Sopenharmony_ci#define control_bis	status
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci/* Bits in select register */
848c2ecf20Sopenharmony_ci#define CA_MASK		7
858c2ecf20Sopenharmony_ci#define LSTRB		8
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci/* Bits in control register */
888c2ecf20Sopenharmony_ci#define DO_SEEK		0x80
898c2ecf20Sopenharmony_ci#define FORMAT		0x40
908c2ecf20Sopenharmony_ci#define SELECT		0x20
918c2ecf20Sopenharmony_ci#define WRITE_SECTORS	0x10
928c2ecf20Sopenharmony_ci#define DO_ACTION	0x08
938c2ecf20Sopenharmony_ci#define DRIVE2_ENABLE	0x04
948c2ecf20Sopenharmony_ci#define DRIVE_ENABLE	0x02
958c2ecf20Sopenharmony_ci#define INTR_ENABLE	0x01
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci/* Bits in status register */
988c2ecf20Sopenharmony_ci#define FIFO_1BYTE	0x80
998c2ecf20Sopenharmony_ci#define FIFO_2BYTE	0x40
1008c2ecf20Sopenharmony_ci#define ERROR		0x20
1018c2ecf20Sopenharmony_ci#define DATA		0x08
1028c2ecf20Sopenharmony_ci#define RDDATA		0x04
1038c2ecf20Sopenharmony_ci#define INTR_PENDING	0x02
1048c2ecf20Sopenharmony_ci#define MARK_BYTE	0x01
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci/* Bits in intr and intr_enable registers */
1078c2ecf20Sopenharmony_ci#define ERROR_INTR	0x20
1088c2ecf20Sopenharmony_ci#define DATA_CHANGED	0x10
1098c2ecf20Sopenharmony_ci#define TRANSFER_DONE	0x08
1108c2ecf20Sopenharmony_ci#define SEEN_SECTOR	0x04
1118c2ecf20Sopenharmony_ci#define SEEK_DONE	0x02
1128c2ecf20Sopenharmony_ci#define TIMER_DONE	0x01
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci/* Bits in error register */
1158c2ecf20Sopenharmony_ci#define ERR_DATA_CRC	0x80
1168c2ecf20Sopenharmony_ci#define ERR_ADDR_CRC	0x40
1178c2ecf20Sopenharmony_ci#define ERR_OVERRUN	0x04
1188c2ecf20Sopenharmony_ci#define ERR_UNDERRUN	0x01
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci/* Bits in setup register */
1218c2ecf20Sopenharmony_ci#define S_SW_RESET	0x80
1228c2ecf20Sopenharmony_ci#define S_GCR_WRITE	0x40
1238c2ecf20Sopenharmony_ci#define S_IBM_DRIVE	0x20
1248c2ecf20Sopenharmony_ci#define S_TEST_MODE	0x10
1258c2ecf20Sopenharmony_ci#define S_FCLK_DIV2	0x08
1268c2ecf20Sopenharmony_ci#define S_GCR		0x04
1278c2ecf20Sopenharmony_ci#define S_COPY_PROT	0x02
1288c2ecf20Sopenharmony_ci#define S_INV_WDATA	0x01
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci/* Select values for swim3_action */
1318c2ecf20Sopenharmony_ci#define SEEK_POSITIVE	0
1328c2ecf20Sopenharmony_ci#define SEEK_NEGATIVE	4
1338c2ecf20Sopenharmony_ci#define STEP		1
1348c2ecf20Sopenharmony_ci#define MOTOR_ON	2
1358c2ecf20Sopenharmony_ci#define MOTOR_OFF	6
1368c2ecf20Sopenharmony_ci#define INDEX		3
1378c2ecf20Sopenharmony_ci#define EJECT		7
1388c2ecf20Sopenharmony_ci#define SETMFM		9
1398c2ecf20Sopenharmony_ci#define SETGCR		13
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci/* Select values for swim3_select and swim3_readbit */
1428c2ecf20Sopenharmony_ci#define STEP_DIR	0
1438c2ecf20Sopenharmony_ci#define STEPPING	1
1448c2ecf20Sopenharmony_ci#define MOTOR_ON	2
1458c2ecf20Sopenharmony_ci#define RELAX		3	/* also eject in progress */
1468c2ecf20Sopenharmony_ci#define READ_DATA_0	4
1478c2ecf20Sopenharmony_ci#define ONEMEG_DRIVE	5
1488c2ecf20Sopenharmony_ci#define SINGLE_SIDED	6	/* drive or diskette is 4MB type? */
1498c2ecf20Sopenharmony_ci#define DRIVE_PRESENT	7
1508c2ecf20Sopenharmony_ci#define DISK_IN		8
1518c2ecf20Sopenharmony_ci#define WRITE_PROT	9
1528c2ecf20Sopenharmony_ci#define TRACK_ZERO	10
1538c2ecf20Sopenharmony_ci#define TACHO		11
1548c2ecf20Sopenharmony_ci#define READ_DATA_1	12
1558c2ecf20Sopenharmony_ci#define GCR_MODE	13
1568c2ecf20Sopenharmony_ci#define SEEK_COMPLETE	14
1578c2ecf20Sopenharmony_ci#define TWOMEG_MEDIA	15
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci/* Definitions of values used in writing and formatting */
1608c2ecf20Sopenharmony_ci#define DATA_ESCAPE	0x99
1618c2ecf20Sopenharmony_ci#define GCR_SYNC_EXC	0x3f
1628c2ecf20Sopenharmony_ci#define GCR_SYNC_CONV	0x80
1638c2ecf20Sopenharmony_ci#define GCR_FIRST_MARK	0xd5
1648c2ecf20Sopenharmony_ci#define GCR_SECOND_MARK	0xaa
1658c2ecf20Sopenharmony_ci#define GCR_ADDR_MARK	"\xd5\xaa\x00"
1668c2ecf20Sopenharmony_ci#define GCR_DATA_MARK	"\xd5\xaa\x0b"
1678c2ecf20Sopenharmony_ci#define GCR_SLIP_BYTE	"\x27\xaa"
1688c2ecf20Sopenharmony_ci#define GCR_SELF_SYNC	"\x3f\xbf\x1e\x34\x3c\x3f"
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci#define DATA_99		"\x99\x99"
1718c2ecf20Sopenharmony_ci#define MFM_ADDR_MARK	"\x99\xa1\x99\xa1\x99\xa1\x99\xfe"
1728c2ecf20Sopenharmony_ci#define MFM_INDEX_MARK	"\x99\xc2\x99\xc2\x99\xc2\x99\xfc"
1738c2ecf20Sopenharmony_ci#define MFM_GAP_LEN	12
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_cistruct floppy_state {
1768c2ecf20Sopenharmony_ci	enum swim_state	state;
1778c2ecf20Sopenharmony_ci	struct swim3 __iomem *swim3;	/* hardware registers */
1788c2ecf20Sopenharmony_ci	struct dbdma_regs __iomem *dma;	/* DMA controller registers */
1798c2ecf20Sopenharmony_ci	int	swim3_intr;	/* interrupt number for SWIM3 */
1808c2ecf20Sopenharmony_ci	int	dma_intr;	/* interrupt number for DMA channel */
1818c2ecf20Sopenharmony_ci	int	cur_cyl;	/* cylinder head is on, or -1 */
1828c2ecf20Sopenharmony_ci	int	cur_sector;	/* last sector we saw go past */
1838c2ecf20Sopenharmony_ci	int	req_cyl;	/* the cylinder for the current r/w request */
1848c2ecf20Sopenharmony_ci	int	head;		/* head number ditto */
1858c2ecf20Sopenharmony_ci	int	req_sector;	/* sector number ditto */
1868c2ecf20Sopenharmony_ci	int	scount;		/* # sectors we're transferring at present */
1878c2ecf20Sopenharmony_ci	int	retries;
1888c2ecf20Sopenharmony_ci	int	settle_time;
1898c2ecf20Sopenharmony_ci	int	secpercyl;	/* disk geometry information */
1908c2ecf20Sopenharmony_ci	int	secpertrack;
1918c2ecf20Sopenharmony_ci	int	total_secs;
1928c2ecf20Sopenharmony_ci	int	write_prot;	/* 1 if write-protected, 0 if not, -1 dunno */
1938c2ecf20Sopenharmony_ci	struct dbdma_cmd *dma_cmd;
1948c2ecf20Sopenharmony_ci	int	ref_count;
1958c2ecf20Sopenharmony_ci	int	expect_cyl;
1968c2ecf20Sopenharmony_ci	struct timer_list timeout;
1978c2ecf20Sopenharmony_ci	int	timeout_pending;
1988c2ecf20Sopenharmony_ci	int	ejected;
1998c2ecf20Sopenharmony_ci	wait_queue_head_t wait;
2008c2ecf20Sopenharmony_ci	int	wanted;
2018c2ecf20Sopenharmony_ci	struct macio_dev *mdev;
2028c2ecf20Sopenharmony_ci	char	dbdma_cmd_space[5 * sizeof(struct dbdma_cmd)];
2038c2ecf20Sopenharmony_ci	int	index;
2048c2ecf20Sopenharmony_ci	struct request *cur_req;
2058c2ecf20Sopenharmony_ci	struct blk_mq_tag_set tag_set;
2068c2ecf20Sopenharmony_ci};
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci#define swim3_err(fmt, arg...)	dev_err(&fs->mdev->ofdev.dev, "[fd%d] " fmt, fs->index, arg)
2098c2ecf20Sopenharmony_ci#define swim3_warn(fmt, arg...)	dev_warn(&fs->mdev->ofdev.dev, "[fd%d] " fmt, fs->index, arg)
2108c2ecf20Sopenharmony_ci#define swim3_info(fmt, arg...)	dev_info(&fs->mdev->ofdev.dev, "[fd%d] " fmt, fs->index, arg)
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci#ifdef DEBUG
2138c2ecf20Sopenharmony_ci#define swim3_dbg(fmt, arg...)	dev_dbg(&fs->mdev->ofdev.dev, "[fd%d] " fmt, fs->index, arg)
2148c2ecf20Sopenharmony_ci#else
2158c2ecf20Sopenharmony_ci#define swim3_dbg(fmt, arg...)	do { } while(0)
2168c2ecf20Sopenharmony_ci#endif
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_cistatic struct floppy_state floppy_states[MAX_FLOPPIES];
2198c2ecf20Sopenharmony_cistatic int floppy_count = 0;
2208c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(swim3_lock);
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_cistatic unsigned short write_preamble[] = {
2238c2ecf20Sopenharmony_ci	0x4e4e, 0x4e4e, 0x4e4e, 0x4e4e, 0x4e4e,	/* gap field */
2248c2ecf20Sopenharmony_ci	0, 0, 0, 0, 0, 0,			/* sync field */
2258c2ecf20Sopenharmony_ci	0x99a1, 0x99a1, 0x99a1, 0x99fb,		/* data address mark */
2268c2ecf20Sopenharmony_ci	0x990f					/* no escape for 512 bytes */
2278c2ecf20Sopenharmony_ci};
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_cistatic unsigned short write_postamble[] = {
2308c2ecf20Sopenharmony_ci	0x9904,					/* insert CRC */
2318c2ecf20Sopenharmony_ci	0x4e4e, 0x4e4e,
2328c2ecf20Sopenharmony_ci	0x9908,					/* stop writing */
2338c2ecf20Sopenharmony_ci	0, 0, 0, 0, 0, 0
2348c2ecf20Sopenharmony_ci};
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_cistatic void seek_track(struct floppy_state *fs, int n);
2378c2ecf20Sopenharmony_cistatic void init_dma(struct dbdma_cmd *cp, int cmd, void *buf, int count);
2388c2ecf20Sopenharmony_cistatic void act(struct floppy_state *fs);
2398c2ecf20Sopenharmony_cistatic void scan_timeout(struct timer_list *t);
2408c2ecf20Sopenharmony_cistatic void seek_timeout(struct timer_list *t);
2418c2ecf20Sopenharmony_cistatic void settle_timeout(struct timer_list *t);
2428c2ecf20Sopenharmony_cistatic void xfer_timeout(struct timer_list *t);
2438c2ecf20Sopenharmony_cistatic irqreturn_t swim3_interrupt(int irq, void *dev_id);
2448c2ecf20Sopenharmony_ci/*static void fd_dma_interrupt(int irq, void *dev_id);*/
2458c2ecf20Sopenharmony_cistatic int grab_drive(struct floppy_state *fs, enum swim_state state,
2468c2ecf20Sopenharmony_ci		      int interruptible);
2478c2ecf20Sopenharmony_cistatic void release_drive(struct floppy_state *fs);
2488c2ecf20Sopenharmony_cistatic int fd_eject(struct floppy_state *fs);
2498c2ecf20Sopenharmony_cistatic int floppy_ioctl(struct block_device *bdev, fmode_t mode,
2508c2ecf20Sopenharmony_ci			unsigned int cmd, unsigned long param);
2518c2ecf20Sopenharmony_cistatic int floppy_open(struct block_device *bdev, fmode_t mode);
2528c2ecf20Sopenharmony_cistatic void floppy_release(struct gendisk *disk, fmode_t mode);
2538c2ecf20Sopenharmony_cistatic unsigned int floppy_check_events(struct gendisk *disk,
2548c2ecf20Sopenharmony_ci					unsigned int clearing);
2558c2ecf20Sopenharmony_cistatic int floppy_revalidate(struct gendisk *disk);
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_cistatic bool swim3_end_request(struct floppy_state *fs, blk_status_t err, unsigned int nr_bytes)
2588c2ecf20Sopenharmony_ci{
2598c2ecf20Sopenharmony_ci	struct request *req = fs->cur_req;
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	swim3_dbg("  end request, err=%d nr_bytes=%d, cur_req=%p\n",
2628c2ecf20Sopenharmony_ci		  err, nr_bytes, req);
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	if (err)
2658c2ecf20Sopenharmony_ci		nr_bytes = blk_rq_cur_bytes(req);
2668c2ecf20Sopenharmony_ci	if (blk_update_request(req, err, nr_bytes))
2678c2ecf20Sopenharmony_ci		return true;
2688c2ecf20Sopenharmony_ci	__blk_mq_end_request(req, err);
2698c2ecf20Sopenharmony_ci	fs->cur_req = NULL;
2708c2ecf20Sopenharmony_ci	return false;
2718c2ecf20Sopenharmony_ci}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_cistatic void swim3_select(struct floppy_state *fs, int sel)
2748c2ecf20Sopenharmony_ci{
2758c2ecf20Sopenharmony_ci	struct swim3 __iomem *sw = fs->swim3;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	out_8(&sw->select, RELAX);
2788c2ecf20Sopenharmony_ci	if (sel & 8)
2798c2ecf20Sopenharmony_ci		out_8(&sw->control_bis, SELECT);
2808c2ecf20Sopenharmony_ci	else
2818c2ecf20Sopenharmony_ci		out_8(&sw->control_bic, SELECT);
2828c2ecf20Sopenharmony_ci	out_8(&sw->select, sel & CA_MASK);
2838c2ecf20Sopenharmony_ci}
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_cistatic void swim3_action(struct floppy_state *fs, int action)
2868c2ecf20Sopenharmony_ci{
2878c2ecf20Sopenharmony_ci	struct swim3 __iomem *sw = fs->swim3;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	swim3_select(fs, action);
2908c2ecf20Sopenharmony_ci	udelay(1);
2918c2ecf20Sopenharmony_ci	out_8(&sw->select, sw->select | LSTRB);
2928c2ecf20Sopenharmony_ci	udelay(2);
2938c2ecf20Sopenharmony_ci	out_8(&sw->select, sw->select & ~LSTRB);
2948c2ecf20Sopenharmony_ci	udelay(1);
2958c2ecf20Sopenharmony_ci}
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_cistatic int swim3_readbit(struct floppy_state *fs, int bit)
2988c2ecf20Sopenharmony_ci{
2998c2ecf20Sopenharmony_ci	struct swim3 __iomem *sw = fs->swim3;
3008c2ecf20Sopenharmony_ci	int stat;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	swim3_select(fs, bit);
3038c2ecf20Sopenharmony_ci	udelay(1);
3048c2ecf20Sopenharmony_ci	stat = in_8(&sw->status);
3058c2ecf20Sopenharmony_ci	return (stat & DATA) == 0;
3068c2ecf20Sopenharmony_ci}
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_cistatic blk_status_t swim3_queue_rq(struct blk_mq_hw_ctx *hctx,
3098c2ecf20Sopenharmony_ci				   const struct blk_mq_queue_data *bd)
3108c2ecf20Sopenharmony_ci{
3118c2ecf20Sopenharmony_ci	struct floppy_state *fs = hctx->queue->queuedata;
3128c2ecf20Sopenharmony_ci	struct request *req = bd->rq;
3138c2ecf20Sopenharmony_ci	unsigned long x;
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	spin_lock_irq(&swim3_lock);
3168c2ecf20Sopenharmony_ci	if (fs->cur_req || fs->state != idle) {
3178c2ecf20Sopenharmony_ci		spin_unlock_irq(&swim3_lock);
3188c2ecf20Sopenharmony_ci		return BLK_STS_DEV_RESOURCE;
3198c2ecf20Sopenharmony_ci	}
3208c2ecf20Sopenharmony_ci	blk_mq_start_request(req);
3218c2ecf20Sopenharmony_ci	fs->cur_req = req;
3228c2ecf20Sopenharmony_ci	if (fs->mdev->media_bay &&
3238c2ecf20Sopenharmony_ci	    check_media_bay(fs->mdev->media_bay) != MB_FD) {
3248c2ecf20Sopenharmony_ci		swim3_dbg("%s", "  media bay absent, dropping req\n");
3258c2ecf20Sopenharmony_ci		swim3_end_request(fs, BLK_STS_IOERR, 0);
3268c2ecf20Sopenharmony_ci		goto out;
3278c2ecf20Sopenharmony_ci	}
3288c2ecf20Sopenharmony_ci	if (fs->ejected) {
3298c2ecf20Sopenharmony_ci		swim3_dbg("%s", "  disk ejected\n");
3308c2ecf20Sopenharmony_ci		swim3_end_request(fs, BLK_STS_IOERR, 0);
3318c2ecf20Sopenharmony_ci		goto out;
3328c2ecf20Sopenharmony_ci	}
3338c2ecf20Sopenharmony_ci	if (rq_data_dir(req) == WRITE) {
3348c2ecf20Sopenharmony_ci		if (fs->write_prot < 0)
3358c2ecf20Sopenharmony_ci			fs->write_prot = swim3_readbit(fs, WRITE_PROT);
3368c2ecf20Sopenharmony_ci		if (fs->write_prot) {
3378c2ecf20Sopenharmony_ci			swim3_dbg("%s", "  try to write, disk write protected\n");
3388c2ecf20Sopenharmony_ci			swim3_end_request(fs, BLK_STS_IOERR, 0);
3398c2ecf20Sopenharmony_ci			goto out;
3408c2ecf20Sopenharmony_ci		}
3418c2ecf20Sopenharmony_ci	}
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	/*
3448c2ecf20Sopenharmony_ci	 * Do not remove the cast. blk_rq_pos(req) is now a sector_t and can be
3458c2ecf20Sopenharmony_ci	 * 64 bits, but it will never go past 32 bits for this driver anyway, so
3468c2ecf20Sopenharmony_ci	 * we can safely cast it down and not have to do a 64/32 division
3478c2ecf20Sopenharmony_ci	 */
3488c2ecf20Sopenharmony_ci	fs->req_cyl = ((long)blk_rq_pos(req)) / fs->secpercyl;
3498c2ecf20Sopenharmony_ci	x = ((long)blk_rq_pos(req)) % fs->secpercyl;
3508c2ecf20Sopenharmony_ci	fs->head = x / fs->secpertrack;
3518c2ecf20Sopenharmony_ci	fs->req_sector = x % fs->secpertrack + 1;
3528c2ecf20Sopenharmony_ci	fs->state = do_transfer;
3538c2ecf20Sopenharmony_ci	fs->retries = 0;
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	act(fs);
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ciout:
3588c2ecf20Sopenharmony_ci	spin_unlock_irq(&swim3_lock);
3598c2ecf20Sopenharmony_ci	return BLK_STS_OK;
3608c2ecf20Sopenharmony_ci}
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_cistatic void set_timeout(struct floppy_state *fs, int nticks,
3638c2ecf20Sopenharmony_ci			void (*proc)(struct timer_list *t))
3648c2ecf20Sopenharmony_ci{
3658c2ecf20Sopenharmony_ci	if (fs->timeout_pending)
3668c2ecf20Sopenharmony_ci		del_timer(&fs->timeout);
3678c2ecf20Sopenharmony_ci	fs->timeout.expires = jiffies + nticks;
3688c2ecf20Sopenharmony_ci	fs->timeout.function = proc;
3698c2ecf20Sopenharmony_ci	add_timer(&fs->timeout);
3708c2ecf20Sopenharmony_ci	fs->timeout_pending = 1;
3718c2ecf20Sopenharmony_ci}
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_cistatic inline void scan_track(struct floppy_state *fs)
3748c2ecf20Sopenharmony_ci{
3758c2ecf20Sopenharmony_ci	struct swim3 __iomem *sw = fs->swim3;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	swim3_select(fs, READ_DATA_0);
3788c2ecf20Sopenharmony_ci	in_8(&sw->intr);		/* clear SEEN_SECTOR bit */
3798c2ecf20Sopenharmony_ci	in_8(&sw->error);
3808c2ecf20Sopenharmony_ci	out_8(&sw->intr_enable, SEEN_SECTOR);
3818c2ecf20Sopenharmony_ci	out_8(&sw->control_bis, DO_ACTION);
3828c2ecf20Sopenharmony_ci	/* enable intr when track found */
3838c2ecf20Sopenharmony_ci	set_timeout(fs, HZ, scan_timeout);	/* enable timeout */
3848c2ecf20Sopenharmony_ci}
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_cistatic inline void seek_track(struct floppy_state *fs, int n)
3878c2ecf20Sopenharmony_ci{
3888c2ecf20Sopenharmony_ci	struct swim3 __iomem *sw = fs->swim3;
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	if (n >= 0) {
3918c2ecf20Sopenharmony_ci		swim3_action(fs, SEEK_POSITIVE);
3928c2ecf20Sopenharmony_ci		sw->nseek = n;
3938c2ecf20Sopenharmony_ci	} else {
3948c2ecf20Sopenharmony_ci		swim3_action(fs, SEEK_NEGATIVE);
3958c2ecf20Sopenharmony_ci		sw->nseek = -n;
3968c2ecf20Sopenharmony_ci	}
3978c2ecf20Sopenharmony_ci	fs->expect_cyl = (fs->cur_cyl >= 0)? fs->cur_cyl + n: -1;
3988c2ecf20Sopenharmony_ci	swim3_select(fs, STEP);
3998c2ecf20Sopenharmony_ci	in_8(&sw->error);
4008c2ecf20Sopenharmony_ci	/* enable intr when seek finished */
4018c2ecf20Sopenharmony_ci	out_8(&sw->intr_enable, SEEK_DONE);
4028c2ecf20Sopenharmony_ci	out_8(&sw->control_bis, DO_SEEK);
4038c2ecf20Sopenharmony_ci	set_timeout(fs, 3*HZ, seek_timeout);	/* enable timeout */
4048c2ecf20Sopenharmony_ci	fs->settle_time = 0;
4058c2ecf20Sopenharmony_ci}
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_cistatic inline void init_dma(struct dbdma_cmd *cp, int cmd,
4088c2ecf20Sopenharmony_ci			    void *buf, int count)
4098c2ecf20Sopenharmony_ci{
4108c2ecf20Sopenharmony_ci	cp->req_count = cpu_to_le16(count);
4118c2ecf20Sopenharmony_ci	cp->command = cpu_to_le16(cmd);
4128c2ecf20Sopenharmony_ci	cp->phy_addr = cpu_to_le32(virt_to_bus(buf));
4138c2ecf20Sopenharmony_ci	cp->xfer_status = 0;
4148c2ecf20Sopenharmony_ci}
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_cistatic inline void setup_transfer(struct floppy_state *fs)
4178c2ecf20Sopenharmony_ci{
4188c2ecf20Sopenharmony_ci	int n;
4198c2ecf20Sopenharmony_ci	struct swim3 __iomem *sw = fs->swim3;
4208c2ecf20Sopenharmony_ci	struct dbdma_cmd *cp = fs->dma_cmd;
4218c2ecf20Sopenharmony_ci	struct dbdma_regs __iomem *dr = fs->dma;
4228c2ecf20Sopenharmony_ci	struct request *req = fs->cur_req;
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	if (blk_rq_cur_sectors(req) <= 0) {
4258c2ecf20Sopenharmony_ci		swim3_warn("%s", "Transfer 0 sectors ?\n");
4268c2ecf20Sopenharmony_ci		return;
4278c2ecf20Sopenharmony_ci	}
4288c2ecf20Sopenharmony_ci	if (rq_data_dir(req) == WRITE)
4298c2ecf20Sopenharmony_ci		n = 1;
4308c2ecf20Sopenharmony_ci	else {
4318c2ecf20Sopenharmony_ci		n = fs->secpertrack - fs->req_sector + 1;
4328c2ecf20Sopenharmony_ci		if (n > blk_rq_cur_sectors(req))
4338c2ecf20Sopenharmony_ci			n = blk_rq_cur_sectors(req);
4348c2ecf20Sopenharmony_ci	}
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	swim3_dbg("  setup xfer at sect %d (of %d) head %d for %d\n",
4378c2ecf20Sopenharmony_ci		  fs->req_sector, fs->secpertrack, fs->head, n);
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	fs->scount = n;
4408c2ecf20Sopenharmony_ci	swim3_select(fs, fs->head? READ_DATA_1: READ_DATA_0);
4418c2ecf20Sopenharmony_ci	out_8(&sw->sector, fs->req_sector);
4428c2ecf20Sopenharmony_ci	out_8(&sw->nsect, n);
4438c2ecf20Sopenharmony_ci	out_8(&sw->gap3, 0);
4448c2ecf20Sopenharmony_ci	out_le32(&dr->cmdptr, virt_to_bus(cp));
4458c2ecf20Sopenharmony_ci	if (rq_data_dir(req) == WRITE) {
4468c2ecf20Sopenharmony_ci		/* Set up 3 dma commands: write preamble, data, postamble */
4478c2ecf20Sopenharmony_ci		init_dma(cp, OUTPUT_MORE, write_preamble, sizeof(write_preamble));
4488c2ecf20Sopenharmony_ci		++cp;
4498c2ecf20Sopenharmony_ci		init_dma(cp, OUTPUT_MORE, bio_data(req->bio), 512);
4508c2ecf20Sopenharmony_ci		++cp;
4518c2ecf20Sopenharmony_ci		init_dma(cp, OUTPUT_LAST, write_postamble, sizeof(write_postamble));
4528c2ecf20Sopenharmony_ci	} else {
4538c2ecf20Sopenharmony_ci		init_dma(cp, INPUT_LAST, bio_data(req->bio), n * 512);
4548c2ecf20Sopenharmony_ci	}
4558c2ecf20Sopenharmony_ci	++cp;
4568c2ecf20Sopenharmony_ci	out_le16(&cp->command, DBDMA_STOP);
4578c2ecf20Sopenharmony_ci	out_8(&sw->control_bic, DO_ACTION | WRITE_SECTORS);
4588c2ecf20Sopenharmony_ci	in_8(&sw->error);
4598c2ecf20Sopenharmony_ci	out_8(&sw->control_bic, DO_ACTION | WRITE_SECTORS);
4608c2ecf20Sopenharmony_ci	if (rq_data_dir(req) == WRITE)
4618c2ecf20Sopenharmony_ci		out_8(&sw->control_bis, WRITE_SECTORS);
4628c2ecf20Sopenharmony_ci	in_8(&sw->intr);
4638c2ecf20Sopenharmony_ci	out_le32(&dr->control, (RUN << 16) | RUN);
4648c2ecf20Sopenharmony_ci	/* enable intr when transfer complete */
4658c2ecf20Sopenharmony_ci	out_8(&sw->intr_enable, TRANSFER_DONE);
4668c2ecf20Sopenharmony_ci	out_8(&sw->control_bis, DO_ACTION);
4678c2ecf20Sopenharmony_ci	set_timeout(fs, 2*HZ, xfer_timeout);	/* enable timeout */
4688c2ecf20Sopenharmony_ci}
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_cistatic void act(struct floppy_state *fs)
4718c2ecf20Sopenharmony_ci{
4728c2ecf20Sopenharmony_ci	for (;;) {
4738c2ecf20Sopenharmony_ci		swim3_dbg("  act loop, state=%d, req_cyl=%d, cur_cyl=%d\n",
4748c2ecf20Sopenharmony_ci			  fs->state, fs->req_cyl, fs->cur_cyl);
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci		switch (fs->state) {
4778c2ecf20Sopenharmony_ci		case idle:
4788c2ecf20Sopenharmony_ci			return;		/* XXX shouldn't get here */
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci		case locating:
4818c2ecf20Sopenharmony_ci			if (swim3_readbit(fs, TRACK_ZERO)) {
4828c2ecf20Sopenharmony_ci				swim3_dbg("%s", "    locate track 0\n");
4838c2ecf20Sopenharmony_ci				fs->cur_cyl = 0;
4848c2ecf20Sopenharmony_ci				if (fs->req_cyl == 0)
4858c2ecf20Sopenharmony_ci					fs->state = do_transfer;
4868c2ecf20Sopenharmony_ci				else
4878c2ecf20Sopenharmony_ci					fs->state = seeking;
4888c2ecf20Sopenharmony_ci				break;
4898c2ecf20Sopenharmony_ci			}
4908c2ecf20Sopenharmony_ci			scan_track(fs);
4918c2ecf20Sopenharmony_ci			return;
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci		case seeking:
4948c2ecf20Sopenharmony_ci			if (fs->cur_cyl < 0) {
4958c2ecf20Sopenharmony_ci				fs->expect_cyl = -1;
4968c2ecf20Sopenharmony_ci				fs->state = locating;
4978c2ecf20Sopenharmony_ci				break;
4988c2ecf20Sopenharmony_ci			}
4998c2ecf20Sopenharmony_ci			if (fs->req_cyl == fs->cur_cyl) {
5008c2ecf20Sopenharmony_ci				swim3_warn("%s", "Whoops, seeking 0\n");
5018c2ecf20Sopenharmony_ci				fs->state = do_transfer;
5028c2ecf20Sopenharmony_ci				break;
5038c2ecf20Sopenharmony_ci			}
5048c2ecf20Sopenharmony_ci			seek_track(fs, fs->req_cyl - fs->cur_cyl);
5058c2ecf20Sopenharmony_ci			return;
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci		case settling:
5088c2ecf20Sopenharmony_ci			/* check for SEEK_COMPLETE after 30ms */
5098c2ecf20Sopenharmony_ci			fs->settle_time = (HZ + 32) / 33;
5108c2ecf20Sopenharmony_ci			set_timeout(fs, fs->settle_time, settle_timeout);
5118c2ecf20Sopenharmony_ci			return;
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci		case do_transfer:
5148c2ecf20Sopenharmony_ci			if (fs->cur_cyl != fs->req_cyl) {
5158c2ecf20Sopenharmony_ci				if (fs->retries > 5) {
5168c2ecf20Sopenharmony_ci					swim3_err("Wrong cylinder in transfer, want: %d got %d\n",
5178c2ecf20Sopenharmony_ci						  fs->req_cyl, fs->cur_cyl);
5188c2ecf20Sopenharmony_ci					swim3_end_request(fs, BLK_STS_IOERR, 0);
5198c2ecf20Sopenharmony_ci					fs->state = idle;
5208c2ecf20Sopenharmony_ci					return;
5218c2ecf20Sopenharmony_ci				}
5228c2ecf20Sopenharmony_ci				fs->state = seeking;
5238c2ecf20Sopenharmony_ci				break;
5248c2ecf20Sopenharmony_ci			}
5258c2ecf20Sopenharmony_ci			setup_transfer(fs);
5268c2ecf20Sopenharmony_ci			return;
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci		case jogging:
5298c2ecf20Sopenharmony_ci			seek_track(fs, -5);
5308c2ecf20Sopenharmony_ci			return;
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci		default:
5338c2ecf20Sopenharmony_ci			swim3_err("Unknown state %d\n", fs->state);
5348c2ecf20Sopenharmony_ci			return;
5358c2ecf20Sopenharmony_ci		}
5368c2ecf20Sopenharmony_ci	}
5378c2ecf20Sopenharmony_ci}
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_cistatic void scan_timeout(struct timer_list *t)
5408c2ecf20Sopenharmony_ci{
5418c2ecf20Sopenharmony_ci	struct floppy_state *fs = from_timer(fs, t, timeout);
5428c2ecf20Sopenharmony_ci	struct swim3 __iomem *sw = fs->swim3;
5438c2ecf20Sopenharmony_ci	unsigned long flags;
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	swim3_dbg("* scan timeout, state=%d\n", fs->state);
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	spin_lock_irqsave(&swim3_lock, flags);
5488c2ecf20Sopenharmony_ci	fs->timeout_pending = 0;
5498c2ecf20Sopenharmony_ci	out_8(&sw->control_bic, DO_ACTION | WRITE_SECTORS);
5508c2ecf20Sopenharmony_ci	out_8(&sw->select, RELAX);
5518c2ecf20Sopenharmony_ci	out_8(&sw->intr_enable, 0);
5528c2ecf20Sopenharmony_ci	fs->cur_cyl = -1;
5538c2ecf20Sopenharmony_ci	if (fs->retries > 5) {
5548c2ecf20Sopenharmony_ci		swim3_end_request(fs, BLK_STS_IOERR, 0);
5558c2ecf20Sopenharmony_ci		fs->state = idle;
5568c2ecf20Sopenharmony_ci	} else {
5578c2ecf20Sopenharmony_ci		fs->state = jogging;
5588c2ecf20Sopenharmony_ci		act(fs);
5598c2ecf20Sopenharmony_ci	}
5608c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&swim3_lock, flags);
5618c2ecf20Sopenharmony_ci}
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_cistatic void seek_timeout(struct timer_list *t)
5648c2ecf20Sopenharmony_ci{
5658c2ecf20Sopenharmony_ci	struct floppy_state *fs = from_timer(fs, t, timeout);
5668c2ecf20Sopenharmony_ci	struct swim3 __iomem *sw = fs->swim3;
5678c2ecf20Sopenharmony_ci	unsigned long flags;
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci	swim3_dbg("* seek timeout, state=%d\n", fs->state);
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	spin_lock_irqsave(&swim3_lock, flags);
5728c2ecf20Sopenharmony_ci	fs->timeout_pending = 0;
5738c2ecf20Sopenharmony_ci	out_8(&sw->control_bic, DO_SEEK);
5748c2ecf20Sopenharmony_ci	out_8(&sw->select, RELAX);
5758c2ecf20Sopenharmony_ci	out_8(&sw->intr_enable, 0);
5768c2ecf20Sopenharmony_ci	swim3_err("%s", "Seek timeout\n");
5778c2ecf20Sopenharmony_ci	swim3_end_request(fs, BLK_STS_IOERR, 0);
5788c2ecf20Sopenharmony_ci	fs->state = idle;
5798c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&swim3_lock, flags);
5808c2ecf20Sopenharmony_ci}
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_cistatic void settle_timeout(struct timer_list *t)
5838c2ecf20Sopenharmony_ci{
5848c2ecf20Sopenharmony_ci	struct floppy_state *fs = from_timer(fs, t, timeout);
5858c2ecf20Sopenharmony_ci	struct swim3 __iomem *sw = fs->swim3;
5868c2ecf20Sopenharmony_ci	unsigned long flags;
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	swim3_dbg("* settle timeout, state=%d\n", fs->state);
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci	spin_lock_irqsave(&swim3_lock, flags);
5918c2ecf20Sopenharmony_ci	fs->timeout_pending = 0;
5928c2ecf20Sopenharmony_ci	if (swim3_readbit(fs, SEEK_COMPLETE)) {
5938c2ecf20Sopenharmony_ci		out_8(&sw->select, RELAX);
5948c2ecf20Sopenharmony_ci		fs->state = locating;
5958c2ecf20Sopenharmony_ci		act(fs);
5968c2ecf20Sopenharmony_ci		goto unlock;
5978c2ecf20Sopenharmony_ci	}
5988c2ecf20Sopenharmony_ci	out_8(&sw->select, RELAX);
5998c2ecf20Sopenharmony_ci	if (fs->settle_time < 2*HZ) {
6008c2ecf20Sopenharmony_ci		++fs->settle_time;
6018c2ecf20Sopenharmony_ci		set_timeout(fs, 1, settle_timeout);
6028c2ecf20Sopenharmony_ci		goto unlock;
6038c2ecf20Sopenharmony_ci	}
6048c2ecf20Sopenharmony_ci	swim3_err("%s", "Seek settle timeout\n");
6058c2ecf20Sopenharmony_ci	swim3_end_request(fs, BLK_STS_IOERR, 0);
6068c2ecf20Sopenharmony_ci	fs->state = idle;
6078c2ecf20Sopenharmony_ci unlock:
6088c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&swim3_lock, flags);
6098c2ecf20Sopenharmony_ci}
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_cistatic void xfer_timeout(struct timer_list *t)
6128c2ecf20Sopenharmony_ci{
6138c2ecf20Sopenharmony_ci	struct floppy_state *fs = from_timer(fs, t, timeout);
6148c2ecf20Sopenharmony_ci	struct swim3 __iomem *sw = fs->swim3;
6158c2ecf20Sopenharmony_ci	struct dbdma_regs __iomem *dr = fs->dma;
6168c2ecf20Sopenharmony_ci	unsigned long flags;
6178c2ecf20Sopenharmony_ci	int n;
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	swim3_dbg("* xfer timeout, state=%d\n", fs->state);
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci	spin_lock_irqsave(&swim3_lock, flags);
6228c2ecf20Sopenharmony_ci	fs->timeout_pending = 0;
6238c2ecf20Sopenharmony_ci	out_le32(&dr->control, RUN << 16);
6248c2ecf20Sopenharmony_ci	/* We must wait a bit for dbdma to stop */
6258c2ecf20Sopenharmony_ci	for (n = 0; (in_le32(&dr->status) & ACTIVE) && n < 1000; n++)
6268c2ecf20Sopenharmony_ci		udelay(1);
6278c2ecf20Sopenharmony_ci	out_8(&sw->intr_enable, 0);
6288c2ecf20Sopenharmony_ci	out_8(&sw->control_bic, WRITE_SECTORS | DO_ACTION);
6298c2ecf20Sopenharmony_ci	out_8(&sw->select, RELAX);
6308c2ecf20Sopenharmony_ci	swim3_err("Timeout %sing sector %ld\n",
6318c2ecf20Sopenharmony_ci	       (rq_data_dir(fs->cur_req)==WRITE? "writ": "read"),
6328c2ecf20Sopenharmony_ci	       (long)blk_rq_pos(fs->cur_req));
6338c2ecf20Sopenharmony_ci	swim3_end_request(fs, BLK_STS_IOERR, 0);
6348c2ecf20Sopenharmony_ci	fs->state = idle;
6358c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&swim3_lock, flags);
6368c2ecf20Sopenharmony_ci}
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_cistatic irqreturn_t swim3_interrupt(int irq, void *dev_id)
6398c2ecf20Sopenharmony_ci{
6408c2ecf20Sopenharmony_ci	struct floppy_state *fs = (struct floppy_state *) dev_id;
6418c2ecf20Sopenharmony_ci	struct swim3 __iomem *sw = fs->swim3;
6428c2ecf20Sopenharmony_ci	int intr, err, n;
6438c2ecf20Sopenharmony_ci	int stat, resid;
6448c2ecf20Sopenharmony_ci	struct dbdma_regs __iomem *dr;
6458c2ecf20Sopenharmony_ci	struct dbdma_cmd *cp;
6468c2ecf20Sopenharmony_ci	unsigned long flags;
6478c2ecf20Sopenharmony_ci	struct request *req = fs->cur_req;
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci	swim3_dbg("* interrupt, state=%d\n", fs->state);
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci	spin_lock_irqsave(&swim3_lock, flags);
6528c2ecf20Sopenharmony_ci	intr = in_8(&sw->intr);
6538c2ecf20Sopenharmony_ci	err = (intr & ERROR_INTR)? in_8(&sw->error): 0;
6548c2ecf20Sopenharmony_ci	if ((intr & ERROR_INTR) && fs->state != do_transfer)
6558c2ecf20Sopenharmony_ci		swim3_err("Non-transfer error interrupt: state=%d, dir=%x, intr=%x, err=%x\n",
6568c2ecf20Sopenharmony_ci			  fs->state, rq_data_dir(req), intr, err);
6578c2ecf20Sopenharmony_ci	switch (fs->state) {
6588c2ecf20Sopenharmony_ci	case locating:
6598c2ecf20Sopenharmony_ci		if (intr & SEEN_SECTOR) {
6608c2ecf20Sopenharmony_ci			out_8(&sw->control_bic, DO_ACTION | WRITE_SECTORS);
6618c2ecf20Sopenharmony_ci			out_8(&sw->select, RELAX);
6628c2ecf20Sopenharmony_ci			out_8(&sw->intr_enable, 0);
6638c2ecf20Sopenharmony_ci			del_timer(&fs->timeout);
6648c2ecf20Sopenharmony_ci			fs->timeout_pending = 0;
6658c2ecf20Sopenharmony_ci			if (sw->ctrack == 0xff) {
6668c2ecf20Sopenharmony_ci				swim3_err("%s", "Seen sector but cyl=ff?\n");
6678c2ecf20Sopenharmony_ci				fs->cur_cyl = -1;
6688c2ecf20Sopenharmony_ci				if (fs->retries > 5) {
6698c2ecf20Sopenharmony_ci					swim3_end_request(fs, BLK_STS_IOERR, 0);
6708c2ecf20Sopenharmony_ci					fs->state = idle;
6718c2ecf20Sopenharmony_ci				} else {
6728c2ecf20Sopenharmony_ci					fs->state = jogging;
6738c2ecf20Sopenharmony_ci					act(fs);
6748c2ecf20Sopenharmony_ci				}
6758c2ecf20Sopenharmony_ci				break;
6768c2ecf20Sopenharmony_ci			}
6778c2ecf20Sopenharmony_ci			fs->cur_cyl = sw->ctrack;
6788c2ecf20Sopenharmony_ci			fs->cur_sector = sw->csect;
6798c2ecf20Sopenharmony_ci			if (fs->expect_cyl != -1 && fs->expect_cyl != fs->cur_cyl)
6808c2ecf20Sopenharmony_ci				swim3_err("Expected cyl %d, got %d\n",
6818c2ecf20Sopenharmony_ci					  fs->expect_cyl, fs->cur_cyl);
6828c2ecf20Sopenharmony_ci			fs->state = do_transfer;
6838c2ecf20Sopenharmony_ci			act(fs);
6848c2ecf20Sopenharmony_ci		}
6858c2ecf20Sopenharmony_ci		break;
6868c2ecf20Sopenharmony_ci	case seeking:
6878c2ecf20Sopenharmony_ci	case jogging:
6888c2ecf20Sopenharmony_ci		if (sw->nseek == 0) {
6898c2ecf20Sopenharmony_ci			out_8(&sw->control_bic, DO_SEEK);
6908c2ecf20Sopenharmony_ci			out_8(&sw->select, RELAX);
6918c2ecf20Sopenharmony_ci			out_8(&sw->intr_enable, 0);
6928c2ecf20Sopenharmony_ci			del_timer(&fs->timeout);
6938c2ecf20Sopenharmony_ci			fs->timeout_pending = 0;
6948c2ecf20Sopenharmony_ci			if (fs->state == seeking)
6958c2ecf20Sopenharmony_ci				++fs->retries;
6968c2ecf20Sopenharmony_ci			fs->state = settling;
6978c2ecf20Sopenharmony_ci			act(fs);
6988c2ecf20Sopenharmony_ci		}
6998c2ecf20Sopenharmony_ci		break;
7008c2ecf20Sopenharmony_ci	case settling:
7018c2ecf20Sopenharmony_ci		out_8(&sw->intr_enable, 0);
7028c2ecf20Sopenharmony_ci		del_timer(&fs->timeout);
7038c2ecf20Sopenharmony_ci		fs->timeout_pending = 0;
7048c2ecf20Sopenharmony_ci		act(fs);
7058c2ecf20Sopenharmony_ci		break;
7068c2ecf20Sopenharmony_ci	case do_transfer:
7078c2ecf20Sopenharmony_ci		if ((intr & (ERROR_INTR | TRANSFER_DONE)) == 0)
7088c2ecf20Sopenharmony_ci			break;
7098c2ecf20Sopenharmony_ci		out_8(&sw->intr_enable, 0);
7108c2ecf20Sopenharmony_ci		out_8(&sw->control_bic, WRITE_SECTORS | DO_ACTION);
7118c2ecf20Sopenharmony_ci		out_8(&sw->select, RELAX);
7128c2ecf20Sopenharmony_ci		del_timer(&fs->timeout);
7138c2ecf20Sopenharmony_ci		fs->timeout_pending = 0;
7148c2ecf20Sopenharmony_ci		dr = fs->dma;
7158c2ecf20Sopenharmony_ci		cp = fs->dma_cmd;
7168c2ecf20Sopenharmony_ci		if (rq_data_dir(req) == WRITE)
7178c2ecf20Sopenharmony_ci			++cp;
7188c2ecf20Sopenharmony_ci		/*
7198c2ecf20Sopenharmony_ci		 * Check that the main data transfer has finished.
7208c2ecf20Sopenharmony_ci		 * On writing, the swim3 sometimes doesn't use
7218c2ecf20Sopenharmony_ci		 * up all the bytes of the postamble, so we can still
7228c2ecf20Sopenharmony_ci		 * see DMA active here.  That doesn't matter as long
7238c2ecf20Sopenharmony_ci		 * as all the sector data has been transferred.
7248c2ecf20Sopenharmony_ci		 */
7258c2ecf20Sopenharmony_ci		if ((intr & ERROR_INTR) == 0 && cp->xfer_status == 0) {
7268c2ecf20Sopenharmony_ci			/* wait a little while for DMA to complete */
7278c2ecf20Sopenharmony_ci			for (n = 0; n < 100; ++n) {
7288c2ecf20Sopenharmony_ci				if (cp->xfer_status != 0)
7298c2ecf20Sopenharmony_ci					break;
7308c2ecf20Sopenharmony_ci				udelay(1);
7318c2ecf20Sopenharmony_ci				barrier();
7328c2ecf20Sopenharmony_ci			}
7338c2ecf20Sopenharmony_ci		}
7348c2ecf20Sopenharmony_ci		/* turn off DMA */
7358c2ecf20Sopenharmony_ci		out_le32(&dr->control, (RUN | PAUSE) << 16);
7368c2ecf20Sopenharmony_ci		stat = le16_to_cpu(cp->xfer_status);
7378c2ecf20Sopenharmony_ci		resid = le16_to_cpu(cp->res_count);
7388c2ecf20Sopenharmony_ci		if (intr & ERROR_INTR) {
7398c2ecf20Sopenharmony_ci			n = fs->scount - 1 - resid / 512;
7408c2ecf20Sopenharmony_ci			if (n > 0) {
7418c2ecf20Sopenharmony_ci				blk_update_request(req, 0, n << 9);
7428c2ecf20Sopenharmony_ci				fs->req_sector += n;
7438c2ecf20Sopenharmony_ci			}
7448c2ecf20Sopenharmony_ci			if (fs->retries < 5) {
7458c2ecf20Sopenharmony_ci				++fs->retries;
7468c2ecf20Sopenharmony_ci				act(fs);
7478c2ecf20Sopenharmony_ci			} else {
7488c2ecf20Sopenharmony_ci				swim3_err("Error %sing block %ld (err=%x)\n",
7498c2ecf20Sopenharmony_ci				       rq_data_dir(req) == WRITE? "writ": "read",
7508c2ecf20Sopenharmony_ci				       (long)blk_rq_pos(req), err);
7518c2ecf20Sopenharmony_ci				swim3_end_request(fs, BLK_STS_IOERR, 0);
7528c2ecf20Sopenharmony_ci				fs->state = idle;
7538c2ecf20Sopenharmony_ci			}
7548c2ecf20Sopenharmony_ci		} else {
7558c2ecf20Sopenharmony_ci			if ((stat & ACTIVE) == 0 || resid != 0) {
7568c2ecf20Sopenharmony_ci				/* musta been an error */
7578c2ecf20Sopenharmony_ci				swim3_err("fd dma error: stat=%x resid=%d\n", stat, resid);
7588c2ecf20Sopenharmony_ci				swim3_err("  state=%d, dir=%x, intr=%x, err=%x\n",
7598c2ecf20Sopenharmony_ci					  fs->state, rq_data_dir(req), intr, err);
7608c2ecf20Sopenharmony_ci				swim3_end_request(fs, BLK_STS_IOERR, 0);
7618c2ecf20Sopenharmony_ci				fs->state = idle;
7628c2ecf20Sopenharmony_ci				break;
7638c2ecf20Sopenharmony_ci			}
7648c2ecf20Sopenharmony_ci			fs->retries = 0;
7658c2ecf20Sopenharmony_ci			if (swim3_end_request(fs, 0, fs->scount << 9)) {
7668c2ecf20Sopenharmony_ci				fs->req_sector += fs->scount;
7678c2ecf20Sopenharmony_ci				if (fs->req_sector > fs->secpertrack) {
7688c2ecf20Sopenharmony_ci					fs->req_sector -= fs->secpertrack;
7698c2ecf20Sopenharmony_ci					if (++fs->head > 1) {
7708c2ecf20Sopenharmony_ci						fs->head = 0;
7718c2ecf20Sopenharmony_ci						++fs->req_cyl;
7728c2ecf20Sopenharmony_ci					}
7738c2ecf20Sopenharmony_ci				}
7748c2ecf20Sopenharmony_ci				act(fs);
7758c2ecf20Sopenharmony_ci			} else
7768c2ecf20Sopenharmony_ci				fs->state = idle;
7778c2ecf20Sopenharmony_ci		}
7788c2ecf20Sopenharmony_ci		break;
7798c2ecf20Sopenharmony_ci	default:
7808c2ecf20Sopenharmony_ci		swim3_err("Don't know what to do in state %d\n", fs->state);
7818c2ecf20Sopenharmony_ci	}
7828c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&swim3_lock, flags);
7838c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
7848c2ecf20Sopenharmony_ci}
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_ci/*
7878c2ecf20Sopenharmony_cistatic void fd_dma_interrupt(int irq, void *dev_id)
7888c2ecf20Sopenharmony_ci{
7898c2ecf20Sopenharmony_ci}
7908c2ecf20Sopenharmony_ci*/
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_ci/* Called under the mutex to grab exclusive access to a drive */
7938c2ecf20Sopenharmony_cistatic int grab_drive(struct floppy_state *fs, enum swim_state state,
7948c2ecf20Sopenharmony_ci		      int interruptible)
7958c2ecf20Sopenharmony_ci{
7968c2ecf20Sopenharmony_ci	unsigned long flags;
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_ci	swim3_dbg("%s", "-> grab drive\n");
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_ci	spin_lock_irqsave(&swim3_lock, flags);
8018c2ecf20Sopenharmony_ci	if (fs->state != idle && fs->state != available) {
8028c2ecf20Sopenharmony_ci		++fs->wanted;
8038c2ecf20Sopenharmony_ci		/* this will enable irqs in order to sleep */
8048c2ecf20Sopenharmony_ci		if (!interruptible)
8058c2ecf20Sopenharmony_ci			wait_event_lock_irq(fs->wait,
8068c2ecf20Sopenharmony_ci                                        fs->state == available,
8078c2ecf20Sopenharmony_ci                                        swim3_lock);
8088c2ecf20Sopenharmony_ci		else if (wait_event_interruptible_lock_irq(fs->wait,
8098c2ecf20Sopenharmony_ci					fs->state == available,
8108c2ecf20Sopenharmony_ci					swim3_lock)) {
8118c2ecf20Sopenharmony_ci			--fs->wanted;
8128c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&swim3_lock, flags);
8138c2ecf20Sopenharmony_ci			return -EINTR;
8148c2ecf20Sopenharmony_ci		}
8158c2ecf20Sopenharmony_ci		--fs->wanted;
8168c2ecf20Sopenharmony_ci	}
8178c2ecf20Sopenharmony_ci	fs->state = state;
8188c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&swim3_lock, flags);
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_ci	return 0;
8218c2ecf20Sopenharmony_ci}
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_cistatic void release_drive(struct floppy_state *fs)
8248c2ecf20Sopenharmony_ci{
8258c2ecf20Sopenharmony_ci	struct request_queue *q = disks[fs->index]->queue;
8268c2ecf20Sopenharmony_ci	unsigned long flags;
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci	swim3_dbg("%s", "-> release drive\n");
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci	spin_lock_irqsave(&swim3_lock, flags);
8318c2ecf20Sopenharmony_ci	fs->state = idle;
8328c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&swim3_lock, flags);
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_ci	blk_mq_freeze_queue(q);
8358c2ecf20Sopenharmony_ci	blk_mq_quiesce_queue(q);
8368c2ecf20Sopenharmony_ci	blk_mq_unquiesce_queue(q);
8378c2ecf20Sopenharmony_ci	blk_mq_unfreeze_queue(q);
8388c2ecf20Sopenharmony_ci}
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_cistatic int fd_eject(struct floppy_state *fs)
8418c2ecf20Sopenharmony_ci{
8428c2ecf20Sopenharmony_ci	int err, n;
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci	err = grab_drive(fs, ejecting, 1);
8458c2ecf20Sopenharmony_ci	if (err)
8468c2ecf20Sopenharmony_ci		return err;
8478c2ecf20Sopenharmony_ci	swim3_action(fs, EJECT);
8488c2ecf20Sopenharmony_ci	for (n = 20; n > 0; --n) {
8498c2ecf20Sopenharmony_ci		if (signal_pending(current)) {
8508c2ecf20Sopenharmony_ci			err = -EINTR;
8518c2ecf20Sopenharmony_ci			break;
8528c2ecf20Sopenharmony_ci		}
8538c2ecf20Sopenharmony_ci		swim3_select(fs, RELAX);
8548c2ecf20Sopenharmony_ci		schedule_timeout_interruptible(1);
8558c2ecf20Sopenharmony_ci		if (swim3_readbit(fs, DISK_IN) == 0)
8568c2ecf20Sopenharmony_ci			break;
8578c2ecf20Sopenharmony_ci	}
8588c2ecf20Sopenharmony_ci	swim3_select(fs, RELAX);
8598c2ecf20Sopenharmony_ci	udelay(150);
8608c2ecf20Sopenharmony_ci	fs->ejected = 1;
8618c2ecf20Sopenharmony_ci	release_drive(fs);
8628c2ecf20Sopenharmony_ci	return err;
8638c2ecf20Sopenharmony_ci}
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_cistatic struct floppy_struct floppy_type =
8668c2ecf20Sopenharmony_ci	{ 2880,18,2,80,0,0x1B,0x00,0xCF,0x6C,NULL };	/*  7 1.44MB 3.5"   */
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_cistatic int floppy_locked_ioctl(struct block_device *bdev, fmode_t mode,
8698c2ecf20Sopenharmony_ci			unsigned int cmd, unsigned long param)
8708c2ecf20Sopenharmony_ci{
8718c2ecf20Sopenharmony_ci	struct floppy_state *fs = bdev->bd_disk->private_data;
8728c2ecf20Sopenharmony_ci	int err;
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_ci	if ((cmd & 0x80) && !capable(CAP_SYS_ADMIN))
8758c2ecf20Sopenharmony_ci		return -EPERM;
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_ci	if (fs->mdev->media_bay &&
8788c2ecf20Sopenharmony_ci	    check_media_bay(fs->mdev->media_bay) != MB_FD)
8798c2ecf20Sopenharmony_ci		return -ENXIO;
8808c2ecf20Sopenharmony_ci
8818c2ecf20Sopenharmony_ci	switch (cmd) {
8828c2ecf20Sopenharmony_ci	case FDEJECT:
8838c2ecf20Sopenharmony_ci		if (fs->ref_count != 1)
8848c2ecf20Sopenharmony_ci			return -EBUSY;
8858c2ecf20Sopenharmony_ci		err = fd_eject(fs);
8868c2ecf20Sopenharmony_ci		return err;
8878c2ecf20Sopenharmony_ci	case FDGETPRM:
8888c2ecf20Sopenharmony_ci	        if (copy_to_user((void __user *) param, &floppy_type,
8898c2ecf20Sopenharmony_ci				 sizeof(struct floppy_struct)))
8908c2ecf20Sopenharmony_ci			return -EFAULT;
8918c2ecf20Sopenharmony_ci		return 0;
8928c2ecf20Sopenharmony_ci	}
8938c2ecf20Sopenharmony_ci	return -ENOTTY;
8948c2ecf20Sopenharmony_ci}
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_cistatic int floppy_ioctl(struct block_device *bdev, fmode_t mode,
8978c2ecf20Sopenharmony_ci				 unsigned int cmd, unsigned long param)
8988c2ecf20Sopenharmony_ci{
8998c2ecf20Sopenharmony_ci	int ret;
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_ci	mutex_lock(&swim3_mutex);
9028c2ecf20Sopenharmony_ci	ret = floppy_locked_ioctl(bdev, mode, cmd, param);
9038c2ecf20Sopenharmony_ci	mutex_unlock(&swim3_mutex);
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_ci	return ret;
9068c2ecf20Sopenharmony_ci}
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_cistatic int floppy_open(struct block_device *bdev, fmode_t mode)
9098c2ecf20Sopenharmony_ci{
9108c2ecf20Sopenharmony_ci	struct floppy_state *fs = bdev->bd_disk->private_data;
9118c2ecf20Sopenharmony_ci	struct swim3 __iomem *sw = fs->swim3;
9128c2ecf20Sopenharmony_ci	int n, err = 0;
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_ci	if (fs->ref_count == 0) {
9158c2ecf20Sopenharmony_ci		if (fs->mdev->media_bay &&
9168c2ecf20Sopenharmony_ci		    check_media_bay(fs->mdev->media_bay) != MB_FD)
9178c2ecf20Sopenharmony_ci			return -ENXIO;
9188c2ecf20Sopenharmony_ci		out_8(&sw->setup, S_IBM_DRIVE | S_FCLK_DIV2);
9198c2ecf20Sopenharmony_ci		out_8(&sw->control_bic, 0xff);
9208c2ecf20Sopenharmony_ci		out_8(&sw->mode, 0x95);
9218c2ecf20Sopenharmony_ci		udelay(10);
9228c2ecf20Sopenharmony_ci		out_8(&sw->intr_enable, 0);
9238c2ecf20Sopenharmony_ci		out_8(&sw->control_bis, DRIVE_ENABLE | INTR_ENABLE);
9248c2ecf20Sopenharmony_ci		swim3_action(fs, MOTOR_ON);
9258c2ecf20Sopenharmony_ci		fs->write_prot = -1;
9268c2ecf20Sopenharmony_ci		fs->cur_cyl = -1;
9278c2ecf20Sopenharmony_ci		for (n = 0; n < 2 * HZ; ++n) {
9288c2ecf20Sopenharmony_ci			if (n >= HZ/30 && swim3_readbit(fs, SEEK_COMPLETE))
9298c2ecf20Sopenharmony_ci				break;
9308c2ecf20Sopenharmony_ci			if (signal_pending(current)) {
9318c2ecf20Sopenharmony_ci				err = -EINTR;
9328c2ecf20Sopenharmony_ci				break;
9338c2ecf20Sopenharmony_ci			}
9348c2ecf20Sopenharmony_ci			swim3_select(fs, RELAX);
9358c2ecf20Sopenharmony_ci			schedule_timeout_interruptible(1);
9368c2ecf20Sopenharmony_ci		}
9378c2ecf20Sopenharmony_ci		if (err == 0 && (swim3_readbit(fs, SEEK_COMPLETE) == 0
9388c2ecf20Sopenharmony_ci				 || swim3_readbit(fs, DISK_IN) == 0))
9398c2ecf20Sopenharmony_ci			err = -ENXIO;
9408c2ecf20Sopenharmony_ci		swim3_action(fs, SETMFM);
9418c2ecf20Sopenharmony_ci		swim3_select(fs, RELAX);
9428c2ecf20Sopenharmony_ci
9438c2ecf20Sopenharmony_ci	} else if (fs->ref_count == -1 || mode & FMODE_EXCL)
9448c2ecf20Sopenharmony_ci		return -EBUSY;
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_ci	if (err == 0 && (mode & FMODE_NDELAY) == 0
9478c2ecf20Sopenharmony_ci	    && (mode & (FMODE_READ|FMODE_WRITE))) {
9488c2ecf20Sopenharmony_ci		if (bdev_check_media_change(bdev))
9498c2ecf20Sopenharmony_ci			floppy_revalidate(bdev->bd_disk);
9508c2ecf20Sopenharmony_ci		if (fs->ejected)
9518c2ecf20Sopenharmony_ci			err = -ENXIO;
9528c2ecf20Sopenharmony_ci	}
9538c2ecf20Sopenharmony_ci
9548c2ecf20Sopenharmony_ci	if (err == 0 && (mode & FMODE_WRITE)) {
9558c2ecf20Sopenharmony_ci		if (fs->write_prot < 0)
9568c2ecf20Sopenharmony_ci			fs->write_prot = swim3_readbit(fs, WRITE_PROT);
9578c2ecf20Sopenharmony_ci		if (fs->write_prot)
9588c2ecf20Sopenharmony_ci			err = -EROFS;
9598c2ecf20Sopenharmony_ci	}
9608c2ecf20Sopenharmony_ci
9618c2ecf20Sopenharmony_ci	if (err) {
9628c2ecf20Sopenharmony_ci		if (fs->ref_count == 0) {
9638c2ecf20Sopenharmony_ci			swim3_action(fs, MOTOR_OFF);
9648c2ecf20Sopenharmony_ci			out_8(&sw->control_bic, DRIVE_ENABLE | INTR_ENABLE);
9658c2ecf20Sopenharmony_ci			swim3_select(fs, RELAX);
9668c2ecf20Sopenharmony_ci		}
9678c2ecf20Sopenharmony_ci		return err;
9688c2ecf20Sopenharmony_ci	}
9698c2ecf20Sopenharmony_ci
9708c2ecf20Sopenharmony_ci	if (mode & FMODE_EXCL)
9718c2ecf20Sopenharmony_ci		fs->ref_count = -1;
9728c2ecf20Sopenharmony_ci	else
9738c2ecf20Sopenharmony_ci		++fs->ref_count;
9748c2ecf20Sopenharmony_ci
9758c2ecf20Sopenharmony_ci	return 0;
9768c2ecf20Sopenharmony_ci}
9778c2ecf20Sopenharmony_ci
9788c2ecf20Sopenharmony_cistatic int floppy_unlocked_open(struct block_device *bdev, fmode_t mode)
9798c2ecf20Sopenharmony_ci{
9808c2ecf20Sopenharmony_ci	int ret;
9818c2ecf20Sopenharmony_ci
9828c2ecf20Sopenharmony_ci	mutex_lock(&swim3_mutex);
9838c2ecf20Sopenharmony_ci	ret = floppy_open(bdev, mode);
9848c2ecf20Sopenharmony_ci	mutex_unlock(&swim3_mutex);
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_ci	return ret;
9878c2ecf20Sopenharmony_ci}
9888c2ecf20Sopenharmony_ci
9898c2ecf20Sopenharmony_cistatic void floppy_release(struct gendisk *disk, fmode_t mode)
9908c2ecf20Sopenharmony_ci{
9918c2ecf20Sopenharmony_ci	struct floppy_state *fs = disk->private_data;
9928c2ecf20Sopenharmony_ci	struct swim3 __iomem *sw = fs->swim3;
9938c2ecf20Sopenharmony_ci
9948c2ecf20Sopenharmony_ci	mutex_lock(&swim3_mutex);
9958c2ecf20Sopenharmony_ci	if (fs->ref_count > 0)
9968c2ecf20Sopenharmony_ci		--fs->ref_count;
9978c2ecf20Sopenharmony_ci	else if (fs->ref_count == -1)
9988c2ecf20Sopenharmony_ci		fs->ref_count = 0;
9998c2ecf20Sopenharmony_ci	if (fs->ref_count == 0) {
10008c2ecf20Sopenharmony_ci		swim3_action(fs, MOTOR_OFF);
10018c2ecf20Sopenharmony_ci		out_8(&sw->control_bic, 0xff);
10028c2ecf20Sopenharmony_ci		swim3_select(fs, RELAX);
10038c2ecf20Sopenharmony_ci	}
10048c2ecf20Sopenharmony_ci	mutex_unlock(&swim3_mutex);
10058c2ecf20Sopenharmony_ci}
10068c2ecf20Sopenharmony_ci
10078c2ecf20Sopenharmony_cistatic unsigned int floppy_check_events(struct gendisk *disk,
10088c2ecf20Sopenharmony_ci					unsigned int clearing)
10098c2ecf20Sopenharmony_ci{
10108c2ecf20Sopenharmony_ci	struct floppy_state *fs = disk->private_data;
10118c2ecf20Sopenharmony_ci	return fs->ejected ? DISK_EVENT_MEDIA_CHANGE : 0;
10128c2ecf20Sopenharmony_ci}
10138c2ecf20Sopenharmony_ci
10148c2ecf20Sopenharmony_cistatic int floppy_revalidate(struct gendisk *disk)
10158c2ecf20Sopenharmony_ci{
10168c2ecf20Sopenharmony_ci	struct floppy_state *fs = disk->private_data;
10178c2ecf20Sopenharmony_ci	struct swim3 __iomem *sw;
10188c2ecf20Sopenharmony_ci	int ret, n;
10198c2ecf20Sopenharmony_ci
10208c2ecf20Sopenharmony_ci	if (fs->mdev->media_bay &&
10218c2ecf20Sopenharmony_ci	    check_media_bay(fs->mdev->media_bay) != MB_FD)
10228c2ecf20Sopenharmony_ci		return -ENXIO;
10238c2ecf20Sopenharmony_ci
10248c2ecf20Sopenharmony_ci	sw = fs->swim3;
10258c2ecf20Sopenharmony_ci	grab_drive(fs, revalidating, 0);
10268c2ecf20Sopenharmony_ci	out_8(&sw->intr_enable, 0);
10278c2ecf20Sopenharmony_ci	out_8(&sw->control_bis, DRIVE_ENABLE);
10288c2ecf20Sopenharmony_ci	swim3_action(fs, MOTOR_ON);	/* necessary? */
10298c2ecf20Sopenharmony_ci	fs->write_prot = -1;
10308c2ecf20Sopenharmony_ci	fs->cur_cyl = -1;
10318c2ecf20Sopenharmony_ci	mdelay(1);
10328c2ecf20Sopenharmony_ci	for (n = HZ; n > 0; --n) {
10338c2ecf20Sopenharmony_ci		if (swim3_readbit(fs, SEEK_COMPLETE))
10348c2ecf20Sopenharmony_ci			break;
10358c2ecf20Sopenharmony_ci		if (signal_pending(current))
10368c2ecf20Sopenharmony_ci			break;
10378c2ecf20Sopenharmony_ci		swim3_select(fs, RELAX);
10388c2ecf20Sopenharmony_ci		schedule_timeout_interruptible(1);
10398c2ecf20Sopenharmony_ci	}
10408c2ecf20Sopenharmony_ci	ret = swim3_readbit(fs, SEEK_COMPLETE) == 0
10418c2ecf20Sopenharmony_ci		|| swim3_readbit(fs, DISK_IN) == 0;
10428c2ecf20Sopenharmony_ci	if (ret)
10438c2ecf20Sopenharmony_ci		swim3_action(fs, MOTOR_OFF);
10448c2ecf20Sopenharmony_ci	else {
10458c2ecf20Sopenharmony_ci		fs->ejected = 0;
10468c2ecf20Sopenharmony_ci		swim3_action(fs, SETMFM);
10478c2ecf20Sopenharmony_ci	}
10488c2ecf20Sopenharmony_ci	swim3_select(fs, RELAX);
10498c2ecf20Sopenharmony_ci
10508c2ecf20Sopenharmony_ci	release_drive(fs);
10518c2ecf20Sopenharmony_ci	return ret;
10528c2ecf20Sopenharmony_ci}
10538c2ecf20Sopenharmony_ci
10548c2ecf20Sopenharmony_cistatic const struct block_device_operations floppy_fops = {
10558c2ecf20Sopenharmony_ci	.open		= floppy_unlocked_open,
10568c2ecf20Sopenharmony_ci	.release	= floppy_release,
10578c2ecf20Sopenharmony_ci	.ioctl		= floppy_ioctl,
10588c2ecf20Sopenharmony_ci	.check_events	= floppy_check_events,
10598c2ecf20Sopenharmony_ci};
10608c2ecf20Sopenharmony_ci
10618c2ecf20Sopenharmony_cistatic const struct blk_mq_ops swim3_mq_ops = {
10628c2ecf20Sopenharmony_ci	.queue_rq = swim3_queue_rq,
10638c2ecf20Sopenharmony_ci};
10648c2ecf20Sopenharmony_ci
10658c2ecf20Sopenharmony_cistatic void swim3_mb_event(struct macio_dev* mdev, int mb_state)
10668c2ecf20Sopenharmony_ci{
10678c2ecf20Sopenharmony_ci	struct floppy_state *fs = macio_get_drvdata(mdev);
10688c2ecf20Sopenharmony_ci	struct swim3 __iomem *sw;
10698c2ecf20Sopenharmony_ci
10708c2ecf20Sopenharmony_ci	if (!fs)
10718c2ecf20Sopenharmony_ci		return;
10728c2ecf20Sopenharmony_ci
10738c2ecf20Sopenharmony_ci	sw = fs->swim3;
10748c2ecf20Sopenharmony_ci
10758c2ecf20Sopenharmony_ci	if (mb_state != MB_FD)
10768c2ecf20Sopenharmony_ci		return;
10778c2ecf20Sopenharmony_ci
10788c2ecf20Sopenharmony_ci	/* Clear state */
10798c2ecf20Sopenharmony_ci	out_8(&sw->intr_enable, 0);
10808c2ecf20Sopenharmony_ci	in_8(&sw->intr);
10818c2ecf20Sopenharmony_ci	in_8(&sw->error);
10828c2ecf20Sopenharmony_ci}
10838c2ecf20Sopenharmony_ci
10848c2ecf20Sopenharmony_cistatic int swim3_add_device(struct macio_dev *mdev, int index)
10858c2ecf20Sopenharmony_ci{
10868c2ecf20Sopenharmony_ci	struct device_node *swim = mdev->ofdev.dev.of_node;
10878c2ecf20Sopenharmony_ci	struct floppy_state *fs = &floppy_states[index];
10888c2ecf20Sopenharmony_ci	int rc = -EBUSY;
10898c2ecf20Sopenharmony_ci
10908c2ecf20Sopenharmony_ci	fs->mdev = mdev;
10918c2ecf20Sopenharmony_ci	fs->index = index;
10928c2ecf20Sopenharmony_ci
10938c2ecf20Sopenharmony_ci	/* Check & Request resources */
10948c2ecf20Sopenharmony_ci	if (macio_resource_count(mdev) < 2) {
10958c2ecf20Sopenharmony_ci		swim3_err("%s", "No address in device-tree\n");
10968c2ecf20Sopenharmony_ci		return -ENXIO;
10978c2ecf20Sopenharmony_ci	}
10988c2ecf20Sopenharmony_ci	if (macio_irq_count(mdev) < 1) {
10998c2ecf20Sopenharmony_ci		swim3_err("%s", "No interrupt in device-tree\n");
11008c2ecf20Sopenharmony_ci		return -ENXIO;
11018c2ecf20Sopenharmony_ci	}
11028c2ecf20Sopenharmony_ci	if (macio_request_resource(mdev, 0, "swim3 (mmio)")) {
11038c2ecf20Sopenharmony_ci		swim3_err("%s", "Can't request mmio resource\n");
11048c2ecf20Sopenharmony_ci		return -EBUSY;
11058c2ecf20Sopenharmony_ci	}
11068c2ecf20Sopenharmony_ci	if (macio_request_resource(mdev, 1, "swim3 (dma)")) {
11078c2ecf20Sopenharmony_ci		swim3_err("%s", "Can't request dma resource\n");
11088c2ecf20Sopenharmony_ci		macio_release_resource(mdev, 0);
11098c2ecf20Sopenharmony_ci		return -EBUSY;
11108c2ecf20Sopenharmony_ci	}
11118c2ecf20Sopenharmony_ci	dev_set_drvdata(&mdev->ofdev.dev, fs);
11128c2ecf20Sopenharmony_ci
11138c2ecf20Sopenharmony_ci	if (mdev->media_bay == NULL)
11148c2ecf20Sopenharmony_ci		pmac_call_feature(PMAC_FTR_SWIM3_ENABLE, swim, 0, 1);
11158c2ecf20Sopenharmony_ci
11168c2ecf20Sopenharmony_ci	fs->state = idle;
11178c2ecf20Sopenharmony_ci	fs->swim3 = (struct swim3 __iomem *)
11188c2ecf20Sopenharmony_ci		ioremap(macio_resource_start(mdev, 0), 0x200);
11198c2ecf20Sopenharmony_ci	if (fs->swim3 == NULL) {
11208c2ecf20Sopenharmony_ci		swim3_err("%s", "Couldn't map mmio registers\n");
11218c2ecf20Sopenharmony_ci		rc = -ENOMEM;
11228c2ecf20Sopenharmony_ci		goto out_release;
11238c2ecf20Sopenharmony_ci	}
11248c2ecf20Sopenharmony_ci	fs->dma = (struct dbdma_regs __iomem *)
11258c2ecf20Sopenharmony_ci		ioremap(macio_resource_start(mdev, 1), 0x200);
11268c2ecf20Sopenharmony_ci	if (fs->dma == NULL) {
11278c2ecf20Sopenharmony_ci		swim3_err("%s", "Couldn't map dma registers\n");
11288c2ecf20Sopenharmony_ci		iounmap(fs->swim3);
11298c2ecf20Sopenharmony_ci		rc = -ENOMEM;
11308c2ecf20Sopenharmony_ci		goto out_release;
11318c2ecf20Sopenharmony_ci	}
11328c2ecf20Sopenharmony_ci	fs->swim3_intr = macio_irq(mdev, 0);
11338c2ecf20Sopenharmony_ci	fs->dma_intr = macio_irq(mdev, 1);
11348c2ecf20Sopenharmony_ci	fs->cur_cyl = -1;
11358c2ecf20Sopenharmony_ci	fs->cur_sector = -1;
11368c2ecf20Sopenharmony_ci	fs->secpercyl = 36;
11378c2ecf20Sopenharmony_ci	fs->secpertrack = 18;
11388c2ecf20Sopenharmony_ci	fs->total_secs = 2880;
11398c2ecf20Sopenharmony_ci	init_waitqueue_head(&fs->wait);
11408c2ecf20Sopenharmony_ci
11418c2ecf20Sopenharmony_ci	fs->dma_cmd = (struct dbdma_cmd *) DBDMA_ALIGN(fs->dbdma_cmd_space);
11428c2ecf20Sopenharmony_ci	memset(fs->dma_cmd, 0, 2 * sizeof(struct dbdma_cmd));
11438c2ecf20Sopenharmony_ci	fs->dma_cmd[1].command = cpu_to_le16(DBDMA_STOP);
11448c2ecf20Sopenharmony_ci
11458c2ecf20Sopenharmony_ci	if (mdev->media_bay == NULL || check_media_bay(mdev->media_bay) == MB_FD)
11468c2ecf20Sopenharmony_ci		swim3_mb_event(mdev, MB_FD);
11478c2ecf20Sopenharmony_ci
11488c2ecf20Sopenharmony_ci	if (request_irq(fs->swim3_intr, swim3_interrupt, 0, "SWIM3", fs)) {
11498c2ecf20Sopenharmony_ci		swim3_err("%s", "Couldn't request interrupt\n");
11508c2ecf20Sopenharmony_ci		pmac_call_feature(PMAC_FTR_SWIM3_ENABLE, swim, 0, 0);
11518c2ecf20Sopenharmony_ci		goto out_unmap;
11528c2ecf20Sopenharmony_ci	}
11538c2ecf20Sopenharmony_ci
11548c2ecf20Sopenharmony_ci	timer_setup(&fs->timeout, NULL, 0);
11558c2ecf20Sopenharmony_ci
11568c2ecf20Sopenharmony_ci	swim3_info("SWIM3 floppy controller %s\n",
11578c2ecf20Sopenharmony_ci		mdev->media_bay ? "in media bay" : "");
11588c2ecf20Sopenharmony_ci
11598c2ecf20Sopenharmony_ci	return 0;
11608c2ecf20Sopenharmony_ci
11618c2ecf20Sopenharmony_ci out_unmap:
11628c2ecf20Sopenharmony_ci	iounmap(fs->dma);
11638c2ecf20Sopenharmony_ci	iounmap(fs->swim3);
11648c2ecf20Sopenharmony_ci
11658c2ecf20Sopenharmony_ci out_release:
11668c2ecf20Sopenharmony_ci	macio_release_resource(mdev, 0);
11678c2ecf20Sopenharmony_ci	macio_release_resource(mdev, 1);
11688c2ecf20Sopenharmony_ci
11698c2ecf20Sopenharmony_ci	return rc;
11708c2ecf20Sopenharmony_ci}
11718c2ecf20Sopenharmony_ci
11728c2ecf20Sopenharmony_cistatic int swim3_attach(struct macio_dev *mdev,
11738c2ecf20Sopenharmony_ci			const struct of_device_id *match)
11748c2ecf20Sopenharmony_ci{
11758c2ecf20Sopenharmony_ci	struct floppy_state *fs;
11768c2ecf20Sopenharmony_ci	struct gendisk *disk;
11778c2ecf20Sopenharmony_ci	int rc;
11788c2ecf20Sopenharmony_ci
11798c2ecf20Sopenharmony_ci	if (floppy_count >= MAX_FLOPPIES)
11808c2ecf20Sopenharmony_ci		return -ENXIO;
11818c2ecf20Sopenharmony_ci
11828c2ecf20Sopenharmony_ci	if (floppy_count == 0) {
11838c2ecf20Sopenharmony_ci		rc = register_blkdev(FLOPPY_MAJOR, "fd");
11848c2ecf20Sopenharmony_ci		if (rc)
11858c2ecf20Sopenharmony_ci			return rc;
11868c2ecf20Sopenharmony_ci	}
11878c2ecf20Sopenharmony_ci
11888c2ecf20Sopenharmony_ci	disk = alloc_disk(1);
11898c2ecf20Sopenharmony_ci	if (disk == NULL) {
11908c2ecf20Sopenharmony_ci		rc = -ENOMEM;
11918c2ecf20Sopenharmony_ci		goto out_unregister;
11928c2ecf20Sopenharmony_ci	}
11938c2ecf20Sopenharmony_ci
11948c2ecf20Sopenharmony_ci	fs = &floppy_states[floppy_count];
11958c2ecf20Sopenharmony_ci	memset(fs, 0, sizeof(*fs));
11968c2ecf20Sopenharmony_ci
11978c2ecf20Sopenharmony_ci	disk->queue = blk_mq_init_sq_queue(&fs->tag_set, &swim3_mq_ops, 2,
11988c2ecf20Sopenharmony_ci						BLK_MQ_F_SHOULD_MERGE);
11998c2ecf20Sopenharmony_ci	if (IS_ERR(disk->queue)) {
12008c2ecf20Sopenharmony_ci		rc = PTR_ERR(disk->queue);
12018c2ecf20Sopenharmony_ci		disk->queue = NULL;
12028c2ecf20Sopenharmony_ci		goto out_put_disk;
12038c2ecf20Sopenharmony_ci	}
12048c2ecf20Sopenharmony_ci	blk_queue_bounce_limit(disk->queue, BLK_BOUNCE_HIGH);
12058c2ecf20Sopenharmony_ci	disk->queue->queuedata = fs;
12068c2ecf20Sopenharmony_ci
12078c2ecf20Sopenharmony_ci	rc = swim3_add_device(mdev, floppy_count);
12088c2ecf20Sopenharmony_ci	if (rc)
12098c2ecf20Sopenharmony_ci		goto out_cleanup_queue;
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_ci	disk->major = FLOPPY_MAJOR;
12128c2ecf20Sopenharmony_ci	disk->first_minor = floppy_count;
12138c2ecf20Sopenharmony_ci	disk->fops = &floppy_fops;
12148c2ecf20Sopenharmony_ci	disk->private_data = fs;
12158c2ecf20Sopenharmony_ci	disk->events = DISK_EVENT_MEDIA_CHANGE;
12168c2ecf20Sopenharmony_ci	disk->flags |= GENHD_FL_REMOVABLE;
12178c2ecf20Sopenharmony_ci	sprintf(disk->disk_name, "fd%d", floppy_count);
12188c2ecf20Sopenharmony_ci	set_capacity(disk, 2880);
12198c2ecf20Sopenharmony_ci	add_disk(disk);
12208c2ecf20Sopenharmony_ci
12218c2ecf20Sopenharmony_ci	disks[floppy_count++] = disk;
12228c2ecf20Sopenharmony_ci	return 0;
12238c2ecf20Sopenharmony_ci
12248c2ecf20Sopenharmony_ciout_cleanup_queue:
12258c2ecf20Sopenharmony_ci	blk_cleanup_queue(disk->queue);
12268c2ecf20Sopenharmony_ci	disk->queue = NULL;
12278c2ecf20Sopenharmony_ci	blk_mq_free_tag_set(&fs->tag_set);
12288c2ecf20Sopenharmony_ciout_put_disk:
12298c2ecf20Sopenharmony_ci	put_disk(disk);
12308c2ecf20Sopenharmony_ciout_unregister:
12318c2ecf20Sopenharmony_ci	if (floppy_count == 0)
12328c2ecf20Sopenharmony_ci		unregister_blkdev(FLOPPY_MAJOR, "fd");
12338c2ecf20Sopenharmony_ci	return rc;
12348c2ecf20Sopenharmony_ci}
12358c2ecf20Sopenharmony_ci
12368c2ecf20Sopenharmony_cistatic const struct of_device_id swim3_match[] =
12378c2ecf20Sopenharmony_ci{
12388c2ecf20Sopenharmony_ci	{
12398c2ecf20Sopenharmony_ci	.name		= "swim3",
12408c2ecf20Sopenharmony_ci	},
12418c2ecf20Sopenharmony_ci	{
12428c2ecf20Sopenharmony_ci	.compatible	= "ohare-swim3"
12438c2ecf20Sopenharmony_ci	},
12448c2ecf20Sopenharmony_ci	{
12458c2ecf20Sopenharmony_ci	.compatible	= "swim3"
12468c2ecf20Sopenharmony_ci	},
12478c2ecf20Sopenharmony_ci	{ /* end of list */ }
12488c2ecf20Sopenharmony_ci};
12498c2ecf20Sopenharmony_ci
12508c2ecf20Sopenharmony_cistatic struct macio_driver swim3_driver =
12518c2ecf20Sopenharmony_ci{
12528c2ecf20Sopenharmony_ci	.driver = {
12538c2ecf20Sopenharmony_ci		.name 		= "swim3",
12548c2ecf20Sopenharmony_ci		.of_match_table	= swim3_match,
12558c2ecf20Sopenharmony_ci	},
12568c2ecf20Sopenharmony_ci	.probe		= swim3_attach,
12578c2ecf20Sopenharmony_ci#ifdef CONFIG_PMAC_MEDIABAY
12588c2ecf20Sopenharmony_ci	.mediabay_event	= swim3_mb_event,
12598c2ecf20Sopenharmony_ci#endif
12608c2ecf20Sopenharmony_ci#if 0
12618c2ecf20Sopenharmony_ci	.suspend	= swim3_suspend,
12628c2ecf20Sopenharmony_ci	.resume		= swim3_resume,
12638c2ecf20Sopenharmony_ci#endif
12648c2ecf20Sopenharmony_ci};
12658c2ecf20Sopenharmony_ci
12668c2ecf20Sopenharmony_ci
12678c2ecf20Sopenharmony_ciint swim3_init(void)
12688c2ecf20Sopenharmony_ci{
12698c2ecf20Sopenharmony_ci	macio_register_driver(&swim3_driver);
12708c2ecf20Sopenharmony_ci	return 0;
12718c2ecf20Sopenharmony_ci}
12728c2ecf20Sopenharmony_ci
12738c2ecf20Sopenharmony_cimodule_init(swim3_init)
12748c2ecf20Sopenharmony_ci
12758c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
12768c2ecf20Sopenharmony_ciMODULE_AUTHOR("Paul Mackerras");
12778c2ecf20Sopenharmony_ciMODULE_ALIAS_BLOCKDEV_MAJOR(FLOPPY_MAJOR);
1278