970 lines · 19.6 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/* * Driver for SWIM (Sander Woz Integrated Machine) floppy controller * * Copyright (C) 2004,2008 Laurent Vivier <Laurent@lvivier.info> * * based on Alastair Bridgewater SWIM analysis, 2001 * based on SWIM3 driver (c) Paul Mackerras, 1996 * based on netBSD IWM driver (c) 1997, 1998 Hauke Fath. * * 2004-08-21 (lv) - Initial implementation * 2008-10-30 (lv) - Port to 2.6 */ #include <linux/module.h>#include <linux/fd.h>#include <linux/slab.h>#include <linux/blk-mq.h>#include <linux/major.h>#include <linux/mutex.h>#include <linux/hdreg.h>#include <linux/kernel.h>#include <linux/delay.h>#include <linux/platform_device.h> #include <asm/mac_via.h> #define CARDNAME "swim" struct sector_header {	unsigned char side;	unsigned char track;	unsigned char sector;	unsigned char size;	unsigned char crc0;	unsigned char crc1;} __attribute__((packed)); #define DRIVER_VERSION "Version 0.2 (2008-10-30)" #define REG(x)	unsigned char x, x ## _pad[0x200 - 1]; struct swim {	REG(write_data)	REG(write_mark)	REG(write_CRC)	REG(write_parameter)	REG(write_phase)	REG(write_setup)	REG(write_mode0)	REG(write_mode1) 	REG(read_data)	REG(read_mark)	REG(read_error)	REG(read_parameter)	REG(read_phase)	REG(read_setup)	REG(read_status)	REG(read_handshake)} __attribute__((packed)); #define swim_write(base, reg, v) 	out_8(&(base)->write_##reg, (v))#define swim_read(base, reg)		in_8(&(base)->read_##reg) /* IWM registers */ struct iwm {	REG(ph0L)	REG(ph0H)	REG(ph1L)	REG(ph1H)	REG(ph2L)	REG(ph2H)	REG(ph3L)	REG(ph3H)	REG(mtrOff)	REG(mtrOn)	REG(intDrive)	REG(extDrive)	REG(q6L)	REG(q6H)	REG(q7L)	REG(q7H)} __attribute__((packed)); #define iwm_write(base, reg, v) 	out_8(&(base)->reg, (v))#define iwm_read(base, reg)		in_8(&(base)->reg) /* bits in phase register */ #define SEEK_POSITIVE	0x070#define SEEK_NEGATIVE	0x074#define STEP		0x071#define MOTOR_ON	0x072#define MOTOR_OFF	0x076#define INDEX		0x073#define EJECT		0x077#define SETMFM		0x171#define SETGCR		0x175 #define RELAX		0x033#define LSTRB		0x008 #define CA_MASK		0x077 /* Select values for swim_select and swim_readbit */ #define READ_DATA_0	0x074#define ONEMEG_DRIVE	0x075#define SINGLE_SIDED	0x076#define DRIVE_PRESENT	0x077#define DISK_IN		0x170#define WRITE_PROT	0x171#define TRACK_ZERO	0x172#define TACHO		0x173#define READ_DATA_1	0x174#define GCR_MODE	0x175#define SEEK_COMPLETE	0x176#define TWOMEG_MEDIA	0x177 /* Bits in handshake register */ #define MARK_BYTE	0x01#define CRC_ZERO	0x02#define RDDATA		0x04#define SENSE		0x08#define MOTEN		0x10#define ERROR		0x20#define DAT2BYTE	0x40#define DAT1BYTE	0x80 /* bits in setup register */ #define S_INV_WDATA	0x01#define S_3_5_SELECT	0x02#define S_GCR		0x04#define S_FCLK_DIV2	0x08#define S_ERROR_CORR	0x10#define S_IBM_DRIVE	0x20#define S_GCR_WRITE	0x40#define S_TIMEOUT	0x80 /* bits in mode register */ #define CLFIFO		0x01#define ENBL1		0x02#define ENBL2		0x04#define ACTION		0x08#define WRITE_MODE	0x10#define HEDSEL		0x20#define MOTON		0x80 /*----------------------------------------------------------------------------*/ enum drive_location {	INTERNAL_DRIVE = 0x02,	EXTERNAL_DRIVE = 0x04,}; enum media_type {	DD_MEDIA,	HD_MEDIA,}; struct floppy_state { 	/* physical properties */ 	enum drive_location location;	/* internal or external drive */	int		 head_number;	/* single- or double-sided drive */ 	/* media */ 	int		 disk_in;	int		 ejected;	enum media_type	 type;	int		 write_protected; 	int		 total_secs;	int		 secpercyl;	int		 secpertrack; 	/* in-use information */ 	int		track;	int		ref_count;	bool registered; 	struct gendisk *disk;	struct blk_mq_tag_set tag_set; 	/* parent controller */ 	struct swim_priv *swd;}; enum motor_action {	OFF,	ON,}; enum head {	LOWER_HEAD = 0,	UPPER_HEAD = 1,}; #define FD_MAX_UNIT	2 struct swim_priv {	struct swim __iomem *base;	spinlock_t lock;	int floppy_count;	struct floppy_state unit[FD_MAX_UNIT];}; extern int swim_read_sector_header(struct swim __iomem *base,				   struct sector_header *header);extern int swim_read_sector_data(struct swim __iomem *base,				 unsigned char *data); static DEFINE_MUTEX(swim_mutex);static inline void set_swim_mode(struct swim __iomem *base, int enable){	struct iwm __iomem *iwm_base;	unsigned long flags; 	if (!enable) {		swim_write(base, mode0, 0xf8);		return;	} 	iwm_base = (struct iwm __iomem *)base;	local_irq_save(flags); 	iwm_read(iwm_base, q7L);	iwm_read(iwm_base, mtrOff);	iwm_read(iwm_base, q6H); 	iwm_write(iwm_base, q7H, 0x57);	iwm_write(iwm_base, q7H, 0x17);	iwm_write(iwm_base, q7H, 0x57);	iwm_write(iwm_base, q7H, 0x57); 	local_irq_restore(flags);} static inline int get_swim_mode(struct swim __iomem *base){	unsigned long flags; 	local_irq_save(flags); 	swim_write(base, phase, 0xf5);	if (swim_read(base, phase) != 0xf5)		goto is_iwm;	swim_write(base, phase, 0xf6);	if (swim_read(base, phase) != 0xf6)		goto is_iwm;	swim_write(base, phase, 0xf7);	if (swim_read(base, phase) != 0xf7)		goto is_iwm;	local_irq_restore(flags);	return 1;is_iwm:	local_irq_restore(flags);	return 0;} static inline void swim_select(struct swim __iomem *base, int sel){	swim_write(base, phase, RELAX); 	via1_set_head(sel & 0x100); 	swim_write(base, phase, sel & CA_MASK);} static inline void swim_action(struct swim __iomem *base, int action){	unsigned long flags; 	local_irq_save(flags); 	swim_select(base, action);	udelay(1);	swim_write(base, phase, (LSTRB<<4) | LSTRB);	udelay(1);	swim_write(base, phase, (LSTRB<<4) | ((~LSTRB) & 0x0F));	udelay(1); 	local_irq_restore(flags);} static inline int swim_readbit(struct swim __iomem *base, int bit){	int stat; 	swim_select(base, bit); 	udelay(10); 	stat = swim_read(base, handshake); 	return (stat & SENSE) == 0;} static inline void swim_drive(struct swim __iomem *base,			      enum drive_location location){	if (location == INTERNAL_DRIVE) {		swim_write(base, mode0, EXTERNAL_DRIVE); /* clear drive 1 bit */		swim_write(base, mode1, INTERNAL_DRIVE); /* set drive 0 bit */	} else if (location == EXTERNAL_DRIVE) {		swim_write(base, mode0, INTERNAL_DRIVE); /* clear drive 0 bit */		swim_write(base, mode1, EXTERNAL_DRIVE); /* set drive 1 bit */	}} static inline void swim_motor(struct swim __iomem *base,			      enum motor_action action){	if (action == ON) {		int i; 		swim_action(base, MOTOR_ON); 		for (i = 0; i < 2*HZ; i++) {			swim_select(base, RELAX);			if (swim_readbit(base, MOTOR_ON))				break;			set_current_state(TASK_INTERRUPTIBLE);			schedule_timeout(1);		}	} else if (action == OFF) {		swim_action(base, MOTOR_OFF);		swim_select(base, RELAX);	}} static inline void swim_eject(struct swim __iomem *base){	int i; 	swim_action(base, EJECT); 	for (i = 0; i < 2*HZ; i++) {		swim_select(base, RELAX);		if (!swim_readbit(base, DISK_IN))			break;		set_current_state(TASK_INTERRUPTIBLE);		schedule_timeout(1);	}	swim_select(base, RELAX);} static inline void swim_head(struct swim __iomem *base, enum head head){	/* wait drive is ready */ 	if (head == UPPER_HEAD)		swim_select(base, READ_DATA_1);	else if (head == LOWER_HEAD)		swim_select(base, READ_DATA_0);} static inline int swim_step(struct swim __iomem *base){	int wait; 	swim_action(base, STEP); 	for (wait = 0; wait < HZ; wait++) { 		set_current_state(TASK_INTERRUPTIBLE);		schedule_timeout(1); 		swim_select(base, RELAX);		if (!swim_readbit(base, STEP))			return 0;	}	return -1;} static inline int swim_track00(struct swim __iomem *base){	int try; 	swim_action(base, SEEK_NEGATIVE); 	for (try = 0; try < 100; try++) { 		swim_select(base, RELAX);		if (swim_readbit(base, TRACK_ZERO))			break; 		if (swim_step(base))			return -1;	} 	if (swim_readbit(base, TRACK_ZERO))		return 0; 	return -1;} static inline int swim_seek(struct swim __iomem *base, int step){	if (step == 0)		return 0; 	if (step < 0) {		swim_action(base, SEEK_NEGATIVE);		step = -step;	} else		swim_action(base, SEEK_POSITIVE); 	for ( ; step > 0; step--) {		if (swim_step(base))			return -1;	} 	return 0;} static inline int swim_track(struct floppy_state *fs,  int track){	struct swim __iomem *base = fs->swd->base;	int ret; 	ret = swim_seek(base, track - fs->track); 	if (ret == 0)		fs->track = track;	else {		swim_track00(base);		fs->track = 0;	} 	return ret;} static int floppy_eject(struct floppy_state *fs){	struct swim __iomem *base = fs->swd->base; 	swim_drive(base, fs->location);	swim_motor(base, OFF);	swim_eject(base); 	fs->disk_in = 0;	fs->ejected = 1; 	return 0;} static inline int swim_read_sector(struct floppy_state *fs,				   int side, int track,				   int sector, unsigned char *buffer){	struct swim __iomem *base = fs->swd->base;	unsigned long flags;	struct sector_header header;	int ret = -1;	short i; 	swim_track(fs, track); 	swim_write(base, mode1, MOTON);	swim_head(base, side);	swim_write(base, mode0, side); 	local_irq_save(flags);	for (i = 0; i < 36; i++) {		ret = swim_read_sector_header(base, &header);		if (!ret && (header.sector == sector)) {			/* found */ 			ret = swim_read_sector_data(base, buffer);			break;		}	}	local_irq_restore(flags); 	swim_write(base, mode0, MOTON); 	if ((header.side != side)  || (header.track != track) ||	     (header.sector != sector))		return 0; 	return ret;} static blk_status_t floppy_read_sectors(struct floppy_state *fs,			       int req_sector, int sectors_nb,			       unsigned char *buffer){	struct swim __iomem *base = fs->swd->base;	int ret;	int side, track, sector;

Lines 1–500 of 970.