block/swim: Fixes and improvements
Subsystems (3)
v3 cover block/swim: Fixes and improvements
I recently had a need for the 'swim' driver but found that it was too buggy to be useful for my purposes. I had two aims in mind--
Firstly, I needed this driver to help me test my new BMoW Floppy Emu for emulation correctness. Being that Linux is open source and MacOS is not, this driver should be ideal for that.
Secondly, I needed to realign the heads in some disk drives. The swim driver has most of the code needed for a feedback loop for manual re-alignment, so I wrote the remaining code and pushed it to a repo here:
https://github.com/fthain/linux/commits/swim/
These patches fix all the bugs I found. They improve stability, compatibility and performance. Additional patches improve source code quality by removing redundant code and cleaning up a bit.
This patch series was successfully tested on the following systems.
Mac Quadra 650 - SWIM 2 with Mitsubishi FDD
Mac LC III - SWIM 2 with Mitsubishi FDD and Sony FDD
Mac Quadra 700 - SWIM with Sony FDD
Mac IIvx - SWIM with Sony FDD
---
Changed since v1:
- Patches 1/31, 19/31, 23/31 were revised as per Laurent's remarks.
- Some minor improvements to patches 9/31, 13/31 and 16/31.
- Added patch 24/32 to fix a bug causing silent IO corruption instead of an IO error.
Changed since v2:
- Reviewed-by tags were dropped from patches 1/32, 9/32, 13/32 and 16/32 because of unreviewed changes.
- Better assembly code in patch 13/32.
- Patch 23/32 improved upon to reduce excessive interrupt latency when medium errors are encountered.
- Patch 7/32 was split in two, and readability was improved as requested by Geert.
- Geert's acked-by tag was added.
---
Finn Thain (33):
swim: Assert strobe with stable outputs
swim: Select appropriate drive once only
swim: Enable the drive when probing
swim: Don't disable drive after every sector
swim: Perform ISM/IWM mode switching according to specs
swim: Configure parameter memory
swim: Refactor SWIM setup
swim: Enable clock divider only where appropriate
swim: Don't start motor until medium is present
swim: Recalibrate when drive is probed
swim: Add track zero recalibration delay
swim: Handle FIFO timeout error
swim: Simplify return value initialization
swim: Check for CRC errors
swim: Check error register during sector read
swim: Don't use the mark register to read data
swim: Fix buffer overflow
swim: Convert to blocking queue
swim: Remove redundant RELAX actions
swim: Deduplicate polling loops
swim: Check drive ready bit
swim: Revisit delays
swim: Remove pointless mode0 register write
swim: Don't needlessly re-read sectors
swim: Don't search beyond the first data mark
swim: Remove pointless specifiers
swim: Move swd initialization
swim: Add some helpful references
swim: Remove unused macro definitions
swim: Clean up whitespace
swim: Define macros for constants
swim: Define symbols for constants
swim: Unexport global symbols
arch/m68k/mac/config.c | 31 ++- drivers/block/swim.c | 451 ++++++++++++++++++++------------------- drivers/block/swim_asm.S | 356 +++++++++++++++--------------- 3 files changed, 452 insertions(+), 386 deletions(-)
Signature · 1 line
2.52.0
PATCH v3 1/33 swim: Assert strobe with stable outputs
Inside Macintosh says, "Be sure that you don't change CA0-CA2 or SEL while LSTRB is high". Unfortunately, those bits do change. The CA0-CA2 outputs become inputs when (LSTRB << 4) | LSTRB gets written to the phase register. Then LSTRB is driven low (with CA0-CA2 bits set).
This is a problem because the drive interprets a STEP command as an EJECT command when these pins float high. This occurs intermittently, perhaps because interrupts are disabled and the race condition happens to end well. However, when I add code to step the heads with interrupts enabled, the disk always ejects.
Keep the four phase pin directions set to output and hold their levels constant during LSTRB signalling. Introduce the PHASE_PIN_DIRECTION macro to separate the pin configuration from the logic level changes.
---
Changed since v1:
- Added the PHASE_PIN_DIRECTION macro to improve readability.
- Re-arranged macro definition groups so as to agree with their descriptions.
Changed since v2:
- Dropped reviewed-by tag due to unreviewed changes made since v1.
--- drivers/block/swim.c | 62 ++++++++++++++++++++++---------------------- 1 file changed, 31 insertions(+), 31 deletions(-)
drivers/block/swim.c +31 -31
@@ -87,37 +87,37 @@ struct iwm {#define iwm_write (base, reg, v) out_8(&(base)->reg, (v))#define iwm_read (base, reg) in_8(&(base)->reg) -/* bits in phase register */ +/* 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 +#define RELAX 0x03 +#define LSTRB 0x08 +#define CA_MASK 0x07 +#define PHASE_PIN_DIR 0xF0/* 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 +#define SEEK_POSITIVE 0x000 +#define SEEK_NEGATIVE 0x004 +#define STEP 0x001 +#define MOTOR_ON 0x002 +#define MOTOR_OFF 0x006 +#define INDEX 0x003 +#define EJECT 0x007 +#define SETMFM 0x101 +#define SETGCR 0x105 + +#define READ_DATA_0 0x004 +#define ONEMEG_DRIVE 0x005 +#define SINGLE_SIDED 0x006 +#define DRIVE_PRESENT 0x007 +#define DISK_IN 0x100 +#define WRITE_PROT 0x101 +#define TRACK_ZERO 0x102 +#define TACHO 0x103 +#define READ_DATA_1 0x104 +#define GCR_MODE 0x105 +#define SEEK_COMPLETE 0x106 +#define TWOMEG_MEDIA 0x107/* Bits in handshake register */
@@ -269,11 +269,11 @@ static inline int get_swim_mode(struct swim __iomem *base)static inline void swim_select (struct swim __iomem *base,int sel) { -swim_write (base, phase,RELAX ); +swim_write (base, phase,RELAX |PHASE_PIN_DIR );via1_set_head (sel &0x100 ); -swim_write (base, phase, sel &CA_MASK ); +swim_write (base, phase, (sel &CA_MASK ) |PHASE_PIN_DIR ); }static inline void swim_action (struct swim __iomem *base,int action)
@@ -284,9 +284,9 @@ static inline void swim_action(struct swim __iomem *base, int action)swim_select (base, action);udelay (1 ); -swim_write (base, phase, (LSTRB <<4 ) |LSTRB ); +swim_write (base, phase,LSTRB | action |PHASE_PIN_DIR );udelay (1 ); -swim_write (base, phase, (LSTRB <<4 ) | ((~LSTRB ) &0x0F )); +swim_write (base, phase, action |PHASE_PIN_DIR );udelay (1 );local_irq_restore (flags);
Signature · 1 line
2.52.0
PATCH v3 2/33 swim: Select appropriate drive once only
In reply to Finn ThainBefore turning off the spindle motor, call swim_drive() to select the appropriate drive. Remove the swim_drive() call from swim_add_floppy() because it was already called by swim_floppy_init().
--- drivers/block/swim.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/block/swim.c +3 -3
@@ -679,8 +679,10 @@ static void floppy_release(struct gendisk *disk)else if (fs->ref_count >0 ) --fs->ref_count; -if (fs->ref_count ==0 ) +if (fs->ref_count ==0 ) { +swim_drive (base, fs->location);swim_motor (base,OFF ); + }mutex_unlock (&swim_mutex); }
@@ -752,8 +754,6 @@ static int swim_add_floppy(struct swim_priv *swd, enum drive_location location) fs->location = location; -swim_drive (base, location); -swim_motor (base,OFF ); fs->type =HD_MEDIA ;
Signature · 1 line
2.52.0
PATCH v3 3/33 swim: Enable the drive when probing
In reply to Finn ThainAccording to the ISM spec, the MOTON bit "causes the Enable 1 and Enable 2 signals to be turned on to the drive." It doesn't actually turn on the motor. When selecting a drive, enable MOTON. Disable it upon eject or release. This fixes detection of the Sony FDHD drive.
--- drivers/block/swim.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-)
drivers/block/swim.c +15 -3
@@ -154,8 +154,9 @@ struct iwm {/*----------------------------------------------------------------------------*/ enum drive_location { -INTERNAL_DRIVE =0x02 , -EXTERNAL_DRIVE =0x04 , +NO_DRIVE =0 , +INTERNAL_DRIVE =BIT (1 ), +EXTERNAL_DRIVE =BIT (2 ), };enum media_type {
@@ -311,9 +312,15 @@ static inline void swim_drive(struct swim __iomem *base,if (location ==INTERNAL_DRIVE ) {swim_write (base, mode0,EXTERNAL_DRIVE );/* clear drive 1 bit */ swim_write (base, mode1,INTERNAL_DRIVE );/* set drive 0 bit */ +swim_write (base, mode1,MOTON ); }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 */ +swim_write (base, mode1,MOTON ); + }else { +swim_write (base, mode0,INTERNAL_DRIVE ); +swim_write (base, mode0,EXTERNAL_DRIVE ); +swim_write (base, mode0,MOTON ); } }
@@ -447,6 +454,7 @@ static int floppy_eject(struct floppy_state *fs)swim_drive (base, fs->location);swim_motor (base,OFF );swim_eject (base); +swim_drive (base,NO_DRIVE ); fs->disk_in =0 ; fs->ejected =1 ;
@@ -652,8 +660,10 @@ static int floppy_open(struct gendisk *disk, blk_mode_t mode)else if (fs->ref_count >0 ) --fs->ref_count; -if (fs->ref_count ==0 ) +if (fs->ref_count ==0 ) {swim_motor (base,OFF ); +swim_drive (base,NO_DRIVE ); + }return err; }
@@ -682,6 +692,7 @@ static void floppy_release(struct gendisk *disk)if (fs->ref_count ==0 ) {swim_drive (base, fs->location);swim_motor (base,OFF ); +swim_drive (base,NO_DRIVE ); }mutex_unlock (&swim_mutex); }
@@ -804,6 +815,7 @@ static int swim_floppy_init(struct swim_priv *swd)if (swim_readbit (base,DRIVE_PRESENT ) && !swim_readbit (base,ONEMEG_DRIVE ))swim_add_floppy (swd,EXTERNAL_DRIVE ); +swim_drive (base,NO_DRIVE );/* register floppy drives */
Signature · 1 line
2.52.0
PATCH v3 4/33 swim: Don't disable drive after every sector
In reply to Finn ThainThis fixes multi-sector reads on a Sony drive. The Mitsubishi drive doesn't seem to care either way.
--- drivers/block/swim.c | 4 ---- 1 file changed, 4 deletions(-)
drivers/block/swim.c +0 -4
@@ -473,8 +473,6 @@ static inline int swim_read_sector(struct floppy_state *fs,short i;swim_track (fs, track); - -swim_write (base, mode1,MOTON );swim_head (base, side);swim_write (base, mode0, side);
@@ -490,8 +488,6 @@ static inline int swim_read_sector(struct floppy_state *fs, }local_irq_restore (flags); -swim_write (base, mode0,MOTON ); -if ((header.side != side) || (header.track != track) || (header.sector != sector))return 0 ;
Signature · 1 line
2.52.0
PATCH v3 5/33 swim: Perform ISM/IWM mode switching according to specs
In reply to Finn ThainThe SWIM spec says, "MOTOREN must be low to switch modes" and "after switching from ISM to IWM, the very first command must be a clear L7".
The ISM spec says, MOTOREN "must not be cleared until after the Action bit is cleared". Perform those operations in the correct sequence.
When switching to ISM mode, the Mode register has to be selected with a particular sequence of bit flips. Set q7 low then q6 low then mtrOff.
--- drivers/block/swim.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-)
drivers/block/swim.c +7 -3
@@ -149,6 +149,7 @@ struct iwm {#define ACTION 0x08#define WRITE_MODE 0x10#define HEDSEL 0x20 +#define ISM_SELECT 0x40#define MOTON 0x80/*----------------------------------------------------------------------------*/
@@ -223,18 +224,21 @@ extern int swim_read_sector_data(struct swim __iomem *base,static DEFINE_MUTEX (swim_mutex);static inline void set_swim_mode (struct swim __iomem *base,int enable) { -struct iwm __iomem *iwm_base; +struct iwm __iomem *iwm_base = (struct iwm __iomem *)base;unsigned long flags;if (!enable) { -swim_write (base, mode0,0xf8 ); +swim_write (base, mode0,ACTION ); +swim_write (base, mode0,ENBL1 |ENBL2 |MOTON ); +swim_write (base, mode0,ISM_SELECT ); +iwm_read (iwm_base, q7L);return ; } - iwm_base = (struct iwm __iomem *)base;local_irq_save (flags);iwm_read (iwm_base, q7L); +iwm_read (iwm_base, q6L);iwm_read (iwm_base, mtrOff);iwm_read (iwm_base, q6H);
Signature · 1 line
2.52.0
PATCH v3 6/33 swim: Configure parameter memory
In reply to Finn ThainThe original SWIM chip has a 16 byte memory to store parameters which control read/write timing. The SWIM 2 chip retains the last 4 parameters which control write pre-compensation, but omits the first 12. Hence SWIM 2 can perform reads without any parameter memory setup but SWIM 1 cannot.
Configure the SWIM parameter memory with the appropriate values so that SWIM 1 can read too. The parameters used here were observed in SWIM chip memory, using Macsbug, while MacOS was reading from an MFM floppy disk.
--- drivers/block/swim.c | 13 +++++++++++++ 1 file changed, 13 insertions(+)
drivers/block/swim.c +13 -0
@@ -796,6 +796,17 @@ static void swim_cleanup_floppy_disk(struct floppy_state *fs)blk_mq_free_tag_set (&fs->tag_set); } +static void swim_set_parameters (struct swim __iomem *base) +{ +unsigned int i; +static const u8 mem[] = {0x18 ,0x41 ,0x2e ,0x2e ,0x18 ,0x18 ,0x1b ,0x1b , +0x2f ,0x2f ,0x19 ,0x19 ,0x97 ,0x1b ,0x57 ,0x3b , }; + +swim_write (base, mode0,0 );/* reset parameter memory index */ +for (i =0 ; i <16 ; ++i) +swim_write (base, parameter, mem[i]); +} +static int swim_floppy_init (struct swim_priv *swd) {struct queue_limits lim = {
@@ -805,6 +816,8 @@ static int swim_floppy_init(struct swim_priv *swd)int drive;struct swim __iomem *base = swd->base; +swim_set_parameters (base); +/* scan floppy drives */ swim_drive (base,INTERNAL_DRIVE );
Signature · 1 line
2.52.0
PATCH v3 7/33 swim: Refactor SWIM setup
In reply to Finn ThainThe write to the setup register is really chip initialization, so do it in swim_floppy_init() rather than floppy_open(). Drop the udelay(), which was apparently copied and pasted from swim3.c where it presumably relates to interrupts, of which this chip has none. (See also mkLinux SWIM 3 driver.)
--- drivers/block/swim.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
drivers/block/swim.c +3 -2
@@ -628,8 +628,7 @@ static int floppy_open(struct gendisk *disk, blk_mode_t mode) fs->ref_count =-1 ; else fs->ref_count++; -swim_write (base, setup,S_IBM_DRIVE |S_FCLK_DIV2 ); -udelay (10 ); +swim_drive (base, fs->location);swim_motor (base,ON );swim_action (base,SETMFM );
@@ -816,6 +815,8 @@ static int swim_floppy_init(struct swim_priv *swd)int drive;struct swim __iomem *base = swd->base; +swim_write (base, setup,S_IBM_DRIVE |S_FCLK_DIV2 ); +swim_set_parameters (base);/* scan floppy drives */
Signature · 1 line
2.52.0
PATCH v3 8/33 swim: Enable clock divider only where appropriate
In reply to Finn ThainSome models have a 16 MHz FCLK oscillator and others 32 MHz. Put this information into the swim device platform_data so that the driver can correctly enable the clock divider. When this is enabled incorrectly, nothing can be read and failures from the Error Correction Machine are flagged in the error register.
---
Changed since v2:
- Code motion was split off into a separate patch.
- Use 'bool fast_fclk' instead of 'unsigned int data'.
--- arch/m68k/mac/config.c | 31 ++++++++++++++++++++++++++++++- drivers/block/swim.c | 9 ++++++--- 2 files changed, 36 insertions(+), 4 deletions(-)
arch/m68k/mac/config.c +30 -1
@@ -984,8 +984,37 @@ static int __init mac_platform_init(void) .start = swim_base, .end = swim_base + 0x1FFF, }; +bool fast_fclk = false; + +/* Models in this list are supposed to run FCLK at 32 MHz */ +switch (macintosh_config->ident) { +case MAC_MODEL_CCL : +case MAC_MODEL_CCLII : +case MAC_MODEL_LCIII : +case MAC_MODEL_Q605 : +case MAC_MODEL_Q605_ACC : +case MAC_MODEL_Q610 : +case MAC_MODEL_Q630 : +case MAC_MODEL_Q650 : +case MAC_MODEL_Q800 : +case MAC_MODEL_P460 : +case MAC_MODEL_P475 : +case MAC_MODEL_P475F : +case MAC_MODEL_P520 : +case MAC_MODEL_P550 : +case MAC_MODEL_P575 : +case MAC_MODEL_P588 : +case MAC_MODEL_TV : +case MAC_MODEL_C610 : +case MAC_MODEL_C650 : +case MAC_MODEL_PB190 : +case MAC_MODEL_PB520 : + fast_fclk = true; +break ; + } -platform_device_register_simple ("swim" ,-1 , &swim_rsrc,1 ); +platform_device_register_resndata (NULL ,"swim" ,-1 , &swim_rsrc,1 , + &fast_fclk,sizeof (fast_fclk)); }/*
drivers/block/swim.c +6 -3
@@ -806,8 +806,11 @@ static void swim_set_parameters(struct swim __iomem *base)swim_write (base, parameter, mem[i]); } -static int swim_floppy_init (struct swim_priv *swd) +static int swim_floppy_init (struct platform_device *pdev) { +struct swim_priv *swd =platform_get_drvdata (pdev); +bool *data = pdev->dev.platform_data; +bool fast_fclk = data && *data;struct queue_limits lim = { .features =BLK_FEAT_ROTATIONAL , };
@@ -815,7 +818,7 @@ static int swim_floppy_init(struct swim_priv *swd)int drive;struct swim __iomem *base = swd->base; -swim_write (base, setup,S_IBM_DRIVE |S_FCLK_DIV2 ); +swim_write (base, setup,S_IBM_DRIVE | (fast_fclk ?S_FCLK_DIV2 :0 ));swim_set_parameters (base);
@@ -931,7 +934,7 @@ static int swim_probe(struct platform_device *dev) swd->base = swim_base; - ret =swim_floppy_init (swd); + ret =swim_floppy_init (dev);if (ret) goto out_kfree;
Signature · 1 line
2.52.0
PATCH v3 9/33 swim: Don't start motor until medium is present
In reply to Finn ThainThe spindle motor should not be running when a disk is to be inserted. Don't start the motor while the drive is empty.
--- drivers/block/swim.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
drivers/block/swim.c +4 -2
@@ -630,8 +630,7 @@ static int floppy_open(struct gendisk *disk, blk_mode_t mode) fs->ref_count++;swim_drive (base, fs->location); -swim_motor (base,ON ); -swim_action (base,SETMFM ); +if (fs->ejected)setup_medium (fs);if (!fs->disk_in) {
@@ -639,6 +638,9 @@ static int floppy_open(struct gendisk *disk, blk_mode_t mode) goto out; } +swim_motor (base,ON ); +swim_action (base,SETMFM ); +set_capacity (fs->disk, fs->total_secs);if (mode &BLK_OPEN_NDELAY )
Signature · 1 line
2.52.0
PATCH v3 10/33 swim: Recalibrate when drive is probed
In reply to Finn ThainTrack zero recalibration can be slow and is normally done only once i.e. during system POST or boot-up. Recalibrate once after the drive is probed rather than every time the device is opened. Don't register the drive if recalibration fails. Park the heads before ejecting.
---
Changed since v1:
- Refactor swim_add_floppy() slightly. Start spindle motor before recalibration if a disk is loaded.
- Change return type of swim_add_floppy() to void.
- Park heads at track 40 because that's what MacOS does.
Changed since v2:
- Dropped reviewed-by tag due to unreviewed changes made since v1.
--- drivers/block/swim.c | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-)
drivers/block/swim.c +17 -20
@@ -412,6 +412,7 @@ static inline int swim_track00(struct swim __iomem *base)if (swim_readbit (base,TRACK_ZERO ))return 0 ; +pr_err ("swim: track zero recalibration failed\n" );return -1 ; }
@@ -456,6 +457,7 @@ static int floppy_eject(struct floppy_state *fs)struct swim __iomem *base = fs->swd->base;swim_drive (base, fs->location); +swim_track (fs,40 );swim_motor (base,OFF );swim_eject (base);swim_drive (base,NO_DRIVE );
@@ -596,13 +598,6 @@ static void setup_medium(struct floppy_state *fs)struct floppy_struct *g; fs->disk_in =1 ; fs->write_protected =swim_readbit (base,WRITE_PROT ); - -if (swim_track00 (base)) -printk (KERN_ERR -"SWIM: cannot move floppy head to track 0\n" ); - -swim_track00 (base); - fs->type =swim_readbit (base,TWOMEG_MEDIA ) ?HD_MEDIA :DD_MEDIA ; fs->head_number =swim_readbit (base,SINGLE_SIDED ) ?1 :2 ;
@@ -610,7 +605,6 @@ static void setup_medium(struct floppy_state *fs) fs->total_secs = g->size; fs->secpercyl = g->head * g->sect; fs->secpertrack = g->sect; - fs->track =0 ; }else { fs->disk_in =0 ; }
@@ -759,24 +753,33 @@ static const struct block_device_operations floppy_fops = { .check_events = floppy_check_events, }; -static int swim_add_floppy (struct swim_priv *swd,enum drive_location location) +static void swim_add_floppy (struct swim_priv *swd,enum drive_location location) {struct floppy_state *fs = &swd->unit[swd->floppy_count];struct swim __iomem *base = swd->base; - fs->location = location; +swim_drive (base, location); +if (!swim_readbit (base,DRIVE_PRESENT ) || +swim_readbit (base,ONEMEG_DRIVE )) + goto out; +if (swim_readbit (base,DISK_IN )) +swim_motor (base,ON ); +if (swim_track00 (base)) + goto out; -swim_motor (base,OFF ); + fs->location = location; fs->type =HD_MEDIA ; fs->head_number =2 ; fs->ref_count =0 ; fs->ejected =1 ; + fs->track =0 ; swd->floppy_count++; -return 0 ; +out: +swim_motor (base,OFF ); }static const struct blk_mq_ops swim_mq_ops = {
@@ -826,14 +829,8 @@ static int swim_floppy_init(struct platform_device *pdev)/* scan floppy drives */ -swim_drive (base,INTERNAL_DRIVE ); -if (swim_readbit (base,DRIVE_PRESENT ) && - !swim_readbit (base,ONEMEG_DRIVE )) -swim_add_floppy (swd,INTERNAL_DRIVE ); -swim_drive (base,EXTERNAL_DRIVE ); -if (swim_readbit (base,DRIVE_PRESENT ) && - !swim_readbit (base,ONEMEG_DRIVE )) -swim_add_floppy (swd,EXTERNAL_DRIVE ); +swim_add_floppy (swd,INTERNAL_DRIVE ); +swim_add_floppy (swd,EXTERNAL_DRIVE );swim_drive (base,NO_DRIVE );/* register floppy drives */
Signature · 1 line
2.52.0
PATCH v3 11/33 swim: Add track zero recalibration delay
In reply to Finn ThainThe UPD72070 spec indicates that the track zero sensor can take 3 ms to stabilize following a STEP command so add a call to msleep(). Remove the duplicate swim_readbit() call as there's no need for that once the sensor signal has stabilized.
--- drivers/block/swim.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-)
drivers/block/swim.c +4 -5
@@ -402,16 +402,15 @@ static inline int swim_track00(struct swim __iomem *base)for (try =0 ; try <100 ; try++) {swim_select (base,RELAX ); +msleep (3 ); +if (swim_readbit (base,TRACK_ZERO )) -break ; +return 0 ;if (swim_step (base)) -return -1 ; +break ; } -if (swim_readbit (base,TRACK_ZERO )) -return 0 ; -pr_err ("swim: track zero recalibration failed\n" );return -1 ; }
Signature · 1 line
2.52.0
PATCH v3 12/33 swim: Handle FIFO timeout error
In reply to Finn ThainWhen polling the FIFO for a mark byte in the sector header, don't return zero if the timeout counter has expired, return an error code.
--- drivers/block/swim_asm.S | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
drivers/block/swim_asm.S +1 -2
@@ -79,7 +79,7 @@ wait_addr_mark_byte: tstb %a2@ dbmi %d2, wait_addr_mark_byte - bpl header_exit + bpl signal_nonyb moveb %a3@, %d3 cmpb %a0@+, %d3
@@ -136,7 +136,6 @@ crc1: tstb %a2@ tstb %a3@(read_error - read_mark) -header_exit: moveq #0, %d0 moveb #0x18, %a3@(write_mode0 - read_mark) rts
Signature · 1 line
2.52.0
PATCH v3 13/33 swim: Simplify return value initialization
In reply to Finn ThainInitialize the error result once only. Update the result only after a successful read.
--- drivers/block/swim_asm.S | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-)
drivers/block/swim_asm.S +3 -8
@@ -47,6 +47,7 @@ swim_read_sector_header: link %a6, #0 moveml %d1-%d5/%a0-%a4,%sp@- movel %a6@(0x0c), %a4 + moveq #-1, %d0 bsr mfm_read_addrmark moveml %sp@+, %d1-%d5/%a0-%a4 unlk %a6
@@ -61,7 +62,6 @@ mfm_read_addrmark: movel %a6@(0x08), %a3 lea %a3@(read_handshake), %a2 lea %a3@(read_mark), %a3 - moveq #-1, %d0 movew #seek_time, %d2 wait_header_init:
@@ -137,10 +137,7 @@ crc1: tstb %a2@ tstb %a3@(read_error - read_mark) moveq #0, %d0 - moveb #0x18, %a3@(write_mode0 - read_mark) - rts signal_nonyb: - moveq #-1, %d0 moveb #0x18, %a3@(write_mode0 - read_mark) rts
@@ -149,6 +146,7 @@ swim_read_sector_data: link %a6, #0 moveml %d1-%d5/%a0-%a5,%sp@- movel %a6@(0x0c), %a4 + moveq #-1, %d0 bsr mfm_read_data moveml %sp@+, %d1-%d5/%a0-%a5 unlk %a6
@@ -228,15 +226,12 @@ data_crc1: tstb %a3@(read_error - read_mark) - moveb #0x18, %a3@(write_mode0 - read_mark) - /* return number of bytes read */ movel #sector_size, %d0 addw #1, %d4 subl %d4, %d0 - rts + data_exit: moveb #0x18, %a3@(write_mode0 - read_mark) - moveq #-1, %d0 rts
Signature · 1 line
2.52.0
PATCH v3 14/33 swim: Check for CRC errors
In reply to Finn ThainAfter reading either the sector header or sector data, examine that flag in the handshake register which holds the result of the CRC calculation. CRC validation has to take place with the last byte still in the FIFO. This flag can't be checked by the caller because by then all bytes will have been retrieved from the FIFO. Return an error code when appropriate.
---
Changed since v1:
- Load handshake register a second time because that's what MacOS does.
Changed since v2:
- Tidied up some hasty changes which appeared in v2.
- Dropped reviewed-by tag due to unreviewed changes made since v1.
--- drivers/block/swim_asm.S | 10 ++++++++++ 1 file changed, 10 insertions(+)
drivers/block/swim_asm.S +10 -0
@@ -42,6 +42,8 @@ .equ max_retry, 40 .equ sector_size, 512 + .equ .Lhr_crc_error, 0x02 + .global swim_read_sector_header swim_read_sector_header: link %a6, #0
@@ -134,6 +136,10 @@ crc1: tstb %a2@ moveb %a3@, %a4@(o_crc1) + moveb %a2@, %d5 + andb #.Lhr_crc_error, %d5 + bne signal_nonyb + tstb %a3@(read_error - read_mark) moveq #0, %d0
@@ -224,6 +230,10 @@ data_crc1: moveb %a3@, %d5 + moveb %a2@, %d5 + andb #.Lhr_crc_error, %d5 + bne data_exit + tstb %a3@(read_error - read_mark) /* return number of bytes read */
Signature · 1 line
2.52.0
PATCH v3 15/33 swim: Check error register during sector read
In reply to Finn ThainClear the error register only once before a sector read operation. Don't clear it afterwards -- the caller needs it. Check the error register in swim_read_sector() and return the appropriate error when necessary. Fully validate the sector header. Don't terminate the search loop early just because an erroneous sector header showed up.
--- drivers/block/swim.c | 13 +++++++------ drivers/block/swim_asm.S | 8 -------- 2 files changed, 7 insertions(+), 14 deletions(-)
drivers/block/swim.c +7 -6
@@ -483,20 +483,21 @@ static inline int swim_read_sector(struct floppy_state *fs,local_irq_save (flags);for (i =0 ; i <36 ; i++) { - ret =swim_read_sector_header (base, &header); -if (!ret && (header.sector == sector)) { +if (swim_read_sector_header (base, &header) || +swim_read (base, error) || header.track != track || + header.side != side || header.size !=2 ) +continue ; +if (header.sector == sector) {/* found */ ret =swim_read_sector_data (base, buffer); +if (swim_read (base, error)) + ret = -EIO ;break ; } }local_irq_restore (flags); -if ((header.side != side) || (header.track != track) || - (header.sector != sector)) -return 0 ; -return ret; }
drivers/block/swim_asm.S +0 -8
@@ -67,7 +67,6 @@ mfm_read_addrmark: movew #seek_time, %d2 wait_header_init: - tstb %a3@(read_error - read_mark) moveb #0x18, %a3@(write_mode0 - read_mark) moveb #0x01, %a3@(write_mode1 - read_mark) moveb #0x01, %a3@(write_mode0 - read_mark)
@@ -140,8 +139,6 @@ crc1: tstb %a2@ andb #.Lhr_crc_error, %d5 bne signal_nonyb - tstb %a3@(read_error - read_mark) - moveq #0, %d0 signal_nonyb: moveb #0x18, %a3@(write_mode0 - read_mark)
@@ -166,7 +163,6 @@ mfm_read_data: movew #seek_time, %d2 wait_data_init: - tstb %a3@(read_error - read_mark) moveb #0x18, %a3@(write_mode0 - read_mark) moveb #0x01, %a3@(write_mode1 - read_mark) moveb #0x01, %a3@(write_mode0 - read_mark)
@@ -191,8 +187,6 @@ wait_data_mark_byte: /* read data */ - tstb %a3@(read_error - read_mark) - movel #sector_size-1, %d4 /* sector size */ read_new_data: movew #max_retry, %d2
@@ -234,8 +228,6 @@ data_crc1: andb #.Lhr_crc_error, %d5 bne data_exit - tstb %a3@(read_error - read_mark) - /* return number of bytes read */ movel #sector_size, %d0
Signature · 1 line
2.52.0
PATCH v3 16/33 swim: Don't use the mark register to read data
In reply to Finn ThainIf an unexpected mark byte were to be read from the data register, an error would be flagged. But no error gets flagged when such a byte is read from the mark register, which is misleading. Always use the data register except when a mark byte is expected.
--- drivers/block/swim_asm.S | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-)
drivers/block/swim_asm.S +11 -10
@@ -47,11 +47,11 @@ .global swim_read_sector_header swim_read_sector_header: link %a6, #0 - moveml %d1-%d5/%a0-%a4,%sp@- + moveml %d1-%d5/%a0-%a5,%sp@- movel %a6@(0x0c), %a4 moveq #-1, %d0 bsr mfm_read_addrmark - moveml %sp@+, %d1-%d5/%a0-%a4 + moveml %sp@+, %d1-%d5/%a0-%a5 unlk %a6 rts
@@ -63,6 +63,7 @@ sector_data_mark: mfm_read_addrmark: movel %a6@(0x08), %a3 lea %a3@(read_handshake), %a2 + lea %a3@(read_data), %a5 lea %a3@(read_mark), %a3 movew #seek_time, %d2
@@ -93,7 +94,7 @@ amark0: tstb %a2@ dbmi %d2, amark0 bpl signal_nonyb - moveb %a3@, %a4@(o_track) + moveb %a5@, %a4@(o_track) moveq #max_retry, %d2
@@ -101,7 +102,7 @@ amark1: tstb %a2@ dbmi %d2, amark1 bpl signal_nonyb - moveb %a3@, %a4@(o_side) + moveb %a5@, %a4@(o_side) moveq #max_retry, %d2
@@ -109,7 +110,7 @@ amark2: tstb %a2@ dbmi %d2, amark2 bpl signal_nonyb - moveb %a3@, %a4@(o_sector) + moveb %a5@, %a4@(o_sector) moveq #max_retry, %d2
@@ -117,7 +118,7 @@ amark3: tstb %a2@ dbmi %d2, amark3 bpl signal_nonyb - moveb %a3@, %a4@(o_size) + moveb %a5@, %a4@(o_size) moveq #max_retry, %d2
@@ -125,7 +126,7 @@ crc0: tstb %a2@ dbmi %d2, crc0 bpl signal_nonyb - moveb %a3@, %a4@(o_crc0) + moveb %a5@, %a4@(o_crc0) moveq #max_retry, %d2
@@ -133,7 +134,7 @@ crc1: tstb %a2@ dbmi %d2, crc1 bpl signal_nonyb - moveb %a3@, %a4@(o_crc1) + moveb %a5@, %a4@(o_crc1) moveb %a2@, %d5 andb #.Lhr_crc_error, %d5
@@ -212,7 +213,7 @@ data_crc0: dbmi %d2, data_crc0 bpl data_exit - moveb %a3@, %d5 + moveb %a5@, %d5 moveq #max_retry, %d2
@@ -222,7 +223,7 @@ data_crc1: dbmi %d2, data_crc1 bpl data_exit - moveb %a3@, %d5 + moveb %a5@, %d5 moveb %a2@, %d5 andb #.Lhr_crc_error, %d5
Signature · 1 line
2.52.0
PATCH v3 17/33 swim: Fix buffer overflow
In reply to Finn ThainThe effect of this bug can be observed as swim_read_sector_data() inexplicably returning -5, or an error flag indicating that a mark byte was read from the data register, or other odd behviour.
When copying bytes from the chip FIFO to the read buffer, the driver keeps count of the remaining buffer space using register %d4. A counter in register %d2 serves as a timeout. The driver polls (%a2), the handshake register, until flags indicate that byte(s) have arrived in the FIFO.
movel #sector_size-1, %d4
read_new_data:
movew #max_retry, %d2
read_data_loop:
moveb %a2@, %d5
andb #0xc0, %d5
dbne %d2, read_data_loop
beq data_exit
moveb %a5@, %a4@+
andb #0x40, %d5
dbne %d4, read_new_data
beq exit_loopNote that the exit_loop branch depends upon a flag in the handshake register and not on the remaining buffer space. Hence there may be no branch to exit_loop after %d4 is decremented to -1 (i.e. full buffer).
moveb %a5@, %a4@+
dbra %d4, read_new_dataexit_loop:
Here is a second decrement of %d4 which can now reach -2. But the buffer bounds check is a comparison with -1, which is now ineffective. Hence the loop will continue copying until %d2 eventually reaches -1.
Fix this bug by terminating the loop as soon as %d4 or %d2 reach -1. Reset the timeout whenever a byte is copied.
---
Changed since v1:
- Avoid jumping to a redundant AND.B.
- Avoid a second handshake register access when there's already a byte in the FIFO.
Changed since v2:
- Dropped reviewed-by tag due to unreviewed changes made since v1.
--- drivers/block/swim_asm.S | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-)
drivers/block/swim_asm.S +9 -7
@@ -43,6 +43,8 @@ .equ sector_size, 512 .equ .Lhr_crc_error, 0x02 + .equ .Lhr_fifo_2bytes, 0x40 + .equ .Lhr_fifo_1byte, 0x80 .global swim_read_sector_header swim_read_sector_header:
@@ -189,20 +191,20 @@ wait_data_mark_byte: /* read data */ movel #sector_size-1, %d4 /* sector size */ -read_new_data: movew #max_retry, %d2 read_data_loop: moveb %a2@, %d5 - andb #0xc0, %d5 + andb #(.Lhr_fifo_1byte + .Lhr_fifo_2bytes), %d5 dbne %d2, read_data_loop beq data_exit + moveq #max_retry, %d2 moveb %a5@, %a4@+ - andb #0x40, %d5 - dbne %d4, read_new_data - beq exit_loop + dbra %d4, 1f + bra data_crc0 +1: andb #.Lhr_fifo_2bytes, %d5 + beq read_data_loop moveb %a5@, %a4@+ - dbra %d4, read_new_data -exit_loop: + dbra %d4, read_data_loop /* read CRC */
Signature · 1 line
2.52.0
PATCH v3 18/33 swim: Convert to blocking queue
In reply to Finn ThainThese drives are slow: completing a request can take hundreds of milliseconds. Delays are managed by disabling interrupts judiciously and sleeping opportunistically.
As of commit e3896d77b702 ("swim: convert to blk-mq"), a spinlock is taken in irq mode as soon as a request is issued. That lock is held for the duration of the request. Hence the driver sleeps while holding the lock which is forbidden.
Adopt BLK_MQ_F_BLOCKING and remove the spinlock. Use a mutex to serialize requests from the two request queues. (The chip cannot simultaneously process requests on both internal and external drive.)
--- drivers/block/swim.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-)
drivers/block/swim.c +3 -8
@@ -211,7 +211,6 @@ enum head {struct swim_priv {struct swim __iomem *base; -spinlock_t lock;int floppy_count;struct floppy_state unit[FD_MAX_UNIT ]; };
@@ -537,12 +536,10 @@ static blk_status_t swim_queue_rq(struct blk_mq_hw_ctx *hctx, const struct blk_mq_queue_data *bd) {struct floppy_state *fs = hctx->queue->queuedata; -struct swim_priv *swd = fs->swd;struct request *req = bd->rq;blk_status_t err; -if (!spin_trylock_irq (&swd->lock)) -return BLK_STS_DEV_RESOURCE ; +mutex_lock (&swim_mutex);blk_mq_start_request (req);
@@ -560,7 +557,7 @@ static blk_status_t swim_queue_rq(struct blk_mq_hw_ctx *hctx, err =BLK_STS_OK ; out: -spin_unlock_irq (&swd->lock); +mutex_unlock (&swim_mutex);return err; }
@@ -842,11 +839,9 @@ static int swim_floppy_init(struct platform_device *pdev)return -EBUSY ; } -spin_lock_init (&swd->lock); -for (drive =0 ; drive < swd->floppy_count; drive++) { err =blk_mq_alloc_sq_tag_set (&swd->unit[drive].tag_set, - &swim_mq_ops,2 ,0 ); + &swim_mq_ops,2 ,BLK_MQ_F_BLOCKING );if (err) goto exit_put_disks;
Signature · 1 line
2.52.0
PATCH v3 19/33 swim: Remove redundant RELAX actions
In reply to Finn ThainWherever we have a swim_select() or swim_readbit() call there is an implicit RELAX. That means the caller doesn't have to do it. Remove the redundant code.
BTW, Inside Macintosh says, "Be sure [...] that CA0 and CA1 are set high before changing SEL." Hence the RELAX found in swim_select(). The SwimIII driver in mkLinux also has that. But the swim3.c driver in Linux is odd:
it scatters RELAX actions around as though SEL was not actually under its control... In anycase, swim.c really does control SEL so there's no need for that here.
--- drivers/block/swim.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-)
drivers/block/swim.c +1 -7
@@ -336,7 +336,6 @@ static inline void swim_motor(struct swim __iomem *base,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 );
@@ -344,7 +343,7 @@ static inline void swim_motor(struct swim __iomem *base, } }else if (action ==OFF ) {swim_action (base,MOTOR_OFF ); -swim_select (base,RELAX ); +swim_write (base, phase,RELAX |PHASE_PIN_DIR ); } }
@@ -355,13 +354,11 @@ static inline void swim_eject(struct swim __iomem *base)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)
@@ -385,7 +382,6 @@ static inline int swim_step(struct swim __iomem *base)set_current_state (TASK_INTERRUPTIBLE );schedule_timeout (1 ); -swim_select (base,RELAX );if (!swim_readbit (base,STEP ))return 0 ; }
@@ -399,8 +395,6 @@ static inline int swim_track00(struct swim __iomem *base)swim_action (base,SEEK_NEGATIVE );for (try =0 ; try <100 ; try++) { - -swim_select (base,RELAX );msleep (3 );if (swim_readbit (base,TRACK_ZERO ))
Signature · 1 line
2.52.0
PATCH v3 20/33 swim: Deduplicate polling loops
In reply to Finn ThainReplace duplicated polling loops with poll_timeout_us(). Change the interruptible sleep to uninterruptible because signal delivery shouldn't be allowed to shorten delays required by the drive hardware.
Change the timeout for the !STEP transition to 20 ms in accordance with the maximum interval required by the UPD72070 spec. The existing 1 second timeout is impractical considering the number of steps in a typical seek.
Change the return type of swim_readbit() to bool because that way the bit names make sense i.e. the reader doesn't have to remember to invert the active-low logic used for drive signals.
---
Changed since v1:
- Dropped swim_readbit_timeout_atomic() as it's no longer needed.
--- drivers/block/swim.c | 39 ++++++++------------------------------- 1 file changed, 8 insertions(+), 31 deletions(-)
drivers/block/swim.c +8 -31
@@ -19,6 +19,7 @@#include <linux/major.h> #include <linux/mutex.h> #include <linux/hdreg.h> +#include <linux/iopoll.h> #include <linux/kernel.h> #include <linux/delay.h> #include <linux/platform_device.h>
@@ -296,7 +297,7 @@ static inline void swim_action(struct swim __iomem *base, int action)local_irq_restore (flags); } -static inline int swim_readbit (struct swim __iomem *base,int bit) +static inline bool swim_readbit (struct swim __iomem *base,int bit) {int stat;
@@ -309,6 +310,9 @@ static inline int swim_readbit(struct swim __iomem *base, int bit)return (stat &SENSE ) ==0 ; } +#define swim_readbit_timeout (base, bit, val, timeout_us) \ + poll_timeout_us(, swim_readbit(base, bit) == val, 1000, timeout_us, false) +static inline void swim_drive (struct swim __iomem *base,enum drive_location location) {
@@ -331,16 +335,8 @@ 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++) { -if (swim_readbit (base,MOTOR_ON )) -break ; -set_current_state (TASK_INTERRUPTIBLE ); -schedule_timeout (1 ); - } +swim_readbit_timeout (base,MOTOR_ON , true,2000 *1000 ); }else if (action ==OFF ) {swim_action (base,MOTOR_OFF );swim_write (base, phase,RELAX |PHASE_PIN_DIR );
@@ -349,16 +345,8 @@ static inline void swim_motor(struct swim __iomem *base,static inline void swim_eject (struct swim __iomem *base) { -int i; -swim_action (base,EJECT ); - -for (i =0 ; i <2 *HZ ; i++) { -if (!swim_readbit (base,DISK_IN )) -break ; -set_current_state (TASK_INTERRUPTIBLE ); -schedule_timeout (1 ); - } +swim_readbit_timeout (base,DISK_IN , false,2000 *1000 ); }static inline void swim_head (struct swim __iomem *base,enum head head)
@@ -373,19 +361,8 @@ static inline void swim_head(struct swim __iomem *base, enum head head)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 ); - -if (!swim_readbit (base,STEP )) -return 0 ; - } -return -1 ; +return swim_readbit_timeout (base,STEP , false,20 *1000 ); }static inline int swim_track00 (struct swim __iomem *base)
Signature · 1 line
2.52.0
PATCH v3 21/33 swim: Check drive ready bit
In reply to Finn ThainThe drive provides a readiness signal that has to be tested before certain commands are issued to the drive. Rename the SEEK_COMPLETE flag as READY because that's how it's known in the documentation as well as the mkLinux source code.
Poll for that signal after stepping the heads and also after switching to MFM mode, as that's what mkLinux does. Check for readiness when stepping because testing shows that some drives require this.
--- drivers/block/swim.c | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-)
drivers/block/swim.c +22 -7
@@ -117,7 +117,7 @@ struct iwm {#define TACHO 0x103#define READ_DATA_1 0x104#define GCR_MODE 0x105 -#define SEEK_COMPLETE 0x106 +#define READY 0x106#define TWOMEG_MEDIA 0x107/* Bits in handshake register */
@@ -313,6 +313,14 @@ static inline bool swim_readbit(struct swim __iomem *base, int bit)#define swim_readbit_timeout (base, bit, val, timeout_us) \ poll_timeout_us(, swim_readbit(base, bit) == val, 1000, timeout_us, false) +#define swim_READY_timeout (base) \ +({ \ + int ret = swim_readbit_timeout(base, READY, true, 1000 * 1000); \ + if (ret) \ + printk(KERN_DEBUG "%s: drive not ready\n", __func__); \ + ret; \ +}) +static inline void swim_drive (struct swim __iomem *base,enum drive_location location) {
@@ -351,8 +359,6 @@ static inline void swim_eject(struct swim __iomem *base)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 )
@@ -387,20 +393,25 @@ static inline int swim_track00(struct swim __iomem *base)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 + }else if (step >0 )swim_action (base,SEEK_POSITIVE ); +swim_READY_timeout (base); + +if (step ==0 ) +return 0 ; +for ( ; step >0 ; step--) {if (swim_step (base))return -1 ; } +msleep (30 ); +swim_READY_timeout (base); +return 0 ; }
@@ -482,6 +493,8 @@ static blk_status_t floppy_read_sectors(struct floppy_state *fs,swim_drive (base, fs->location); +swim_READY_timeout (base); +for (i = req_sector; i < req_sector + sectors_nb; i++) {int x; track = i / fs->secpercyl;
@@ -602,6 +615,8 @@ static int floppy_open(struct gendisk *disk, blk_mode_t mode)swim_motor (base,ON );swim_action (base,SETMFM ); +msleep (30 ); +swim_READY_timeout (base);set_capacity (fs->disk, fs->total_secs);
Signature · 1 line
2.52.0
PATCH v3 22/33 swim: Revisit delays
In reply to Finn ThainAFAIK, timing requirements for the various FDHD drive mechanisms aren't well documented. But we do have the UPD72070 spec and secondary sources like swim3.c and mkLinux source code. This patch is needed to satisfy the requirements in the UPD72070 spec and follows mkLinux.
Change the LSTRB pulse to 2 microseconds, because this is what mkLinux does. Inside Macintosh says, "Hold LSTRB high for at least one usec but not more than one msec".
When a disk is ejected, pause before de-asserting /ENBL. Wait 150 us after the STEP command for valid signalling. Pause for 1 us after setting the step direction before sending the STEP command.
--- drivers/block/swim.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
drivers/block/swim.c +5 -1
@@ -290,7 +290,7 @@ static inline void swim_action(struct swim __iomem *base, int action)swim_select (base, action);udelay (1 );swim_write (base, phase,LSTRB | action |PHASE_PIN_DIR ); -udelay (1 ); +udelay (2 );swim_write (base, phase, action |PHASE_PIN_DIR );udelay (1 );
@@ -337,6 +337,7 @@ static inline void swim_drive(struct swim __iomem *base,swim_write (base, mode0,EXTERNAL_DRIVE );swim_write (base, mode0,MOTON ); } +udelay (1 ); } static inline void swim_motor(struct swim __iomem *base,
@@ -355,6 +356,7 @@ static inline void swim_eject(struct swim __iomem *base) {swim_action (base,EJECT );swim_readbit_timeout (base,DISK_IN , false,2000 *1000 ); +msleep (1 ); }static inline void swim_head (struct swim __iomem *base,enum head head)
@@ -368,6 +370,7 @@ static inline void swim_head(struct swim __iomem *base, enum head head)static inline int swim_step (struct swim __iomem *base) {swim_action (base,STEP ); +udelay (150 );return swim_readbit_timeout (base,STEP , false,20 *1000 ); }
@@ -398,6 +401,7 @@ static inline int swim_seek(struct swim __iomem *base, int step) step = -step; }else if (step >0 )swim_action (base,SEEK_POSITIVE ); +udelay (1 );swim_READY_timeout (base);
Signature · 1 line
2.52.0
PATCH v3 23/33 swim: Remove pointless mode0 register write
In reply to Finn ThainThis write has no effect so remove it. (If side == 0 then no mode bit gets cleared. If side == 1 then mode bit 0 gets cleared but that's pointless because that bit is already clear here.)
--- drivers/block/swim.c | 1 - 1 file changed, 1 deletion(-)
drivers/block/swim.c +0 -1
@@ -464,7 +464,6 @@ static inline int swim_read_sector(struct floppy_state *fs,swim_track (fs, track);swim_head (base, side); -swim_write (base, mode0, side);local_irq_save (flags);for (i =0 ; i <36 ; i++) {
Signature · 1 line
2.52.0
PATCH v3 24/33 swim: Don't needlessly re-read sectors
In reply to Finn Thainfloppy_read_sectors() is confusing because the `track' variable seems to conflate tracks and cylinders. Rename this variable, eliminate a division operation and adopt suitable integer types.
For readahead to work effectively, small sequential reads should not require waiting for spindle rotation. Unfortunately, the present algorithm is very inefficient and does a lot of unnecessary waiting.
E.g. if the device is asked to read sectors 1 thru 16, and if sector 9 happens to be under the heads, the driver will proceed to read sectors 9 thru 18, but discard the results, while it waits for sector 1 to arrive.
If sector 1 couldn't be read on the first attempt and needs a retry, the driver will proceed to read sectors 2 thru 18, but discard the results, while it waits for sector 1 to come around again.
In between reading sector 1 and sector 2, the driver needlessly calls swim_track() and swim_head() again. But what's worse is re-enabling interrupts after each sector, because on a 68030 system this can result in a full rotation between sectors (which would be a 200 ms wait).
Floppy drivers usually implement a track cache that can be filled in a single rotation to solve such problems. But I think there is a simpler solution.
After stepping the heads, use a sector bitmap to record sectors that were successfully read from the present track. Read (or retry, if need be) the requested sectors in whatever sequence they become available. Keep interrupts disabled until the whole track has passed under the read head.
swim_read_sector() assumes that it can search a whole track by reading a fixed number of sector headers (essentially, fs->secpertrack) but this assumes no false sector headers are matched in the sector contents. To prevent that, call swim_read_sector_data() unconditionally after any valid sector header is matched.
---
Changed since v1:
- Use GENMASK() macro.
- Use fs->secpertrack instead of hard-coding the high-density value.
- Don't use a failure counter.
- Use unsigned integers where appropriate.
- Call swim_read_sector_data() whenever swim_read_sector_header() is successful. A NULL pointer is passed to indicate that no data is to be copied into the buffer.
Changed since v2:
- Loop for fs->secpertrack + 1 iterations because the gap search will match the index gap as well as every sector gap.
- Reinstate the failure counter because that way interrupts aren't disabled for too long when a medium error is encountered.
- Improved commit log text.
--- drivers/block/swim.c | 99 ++++++++++++++++++++++------------------ drivers/block/swim_asm.S | 12 ++++- 2 files changed, 65 insertions(+), 46 deletions(-)
drivers/block/swim.c +55 -44
@@ -180,9 +180,9 @@ struct floppy_state {enum media_type type;int write_protected; -int total_secs; -int secpercyl; -int secpertrack; +unsigned int total_secs; +unsigned int secpercyl; +unsigned int secpertrack;/* in-use information */
@@ -452,68 +452,79 @@ static int floppy_eject(struct floppy_state *fs)return 0 ; } -static inline int swim_read_sector (struct floppy_state *fs, -int side,int track, -int sector,unsigned char *buffer) +static unsigned int swim_read_sector_range (struct floppy_state *fs, +unsigned int side,unsigned int track, +unsigned int start,unsigned int count, +unsigned char *buffer) {struct swim __iomem *base = fs->swd->base;unsigned long flags;struct sector_header header; -int ret =-1 ; -short i; +unsigned int i, bits =0 ; -swim_track (fs, track); -swim_head (base, side); +if (count >0 ) { + count =min (count, fs->secpertrack); + bits =GENMASK (count -1 ,0 ); + }local_irq_save (flags); -for (i =0 ; i <36 ; i++) { -if (swim_read_sector_header (base, &header) || -swim_read (base, error) || header.track != track || - header.side != side || header.size !=2 ) -continue ; -if (header.sector == sector) { -/* found */ - - ret =swim_read_sector_data (base, buffer); -if (swim_read (base, error)) - ret = -EIO ; +for (i =0 ; i < fs->secpertrack +1 ; i++) { +if (bits ==0 )/* All sectors were read ok */ break ; + +if (swim_read_sector_header (base, &header) ==0 && +swim_read (base, error) ==0 ) { +unsigned int offset = header.sector - start; +unsigned char *buf =NULL ; +int len; + +if (header.track == track && header.side == side && + header.size ==2 && header.sector >= start && + header.sector < start + count && + (bits &BIT (offset))) + buf = buffer +512 * offset; + len =swim_read_sector_data (base, buf); +if (swim_read (base, error) ==0 && buf && len ==512 ) + bits &= ~BIT (offset); } }local_irq_restore (flags); -return ret; +return bits ?ffs (bits) -1 : count;/* No. of contiguous ok sectors */ }static blk_status_t floppy_read_sectors (struct floppy_state *fs, -int req_sector,int sectors_nb, -unsigned char *buffer) +unsigned int req_sector, +unsigned int sectors_nb, +unsigned char *buffer) {struct swim __iomem *base = fs->swd->base; -int ret; -int side, track, sector; -int i, try; - +unsigned int try =0 ;swim_drive (base, fs->location);swim_READY_timeout (base); -for (i = req_sector; i < req_sector + sectors_nb; i++) { -int x; - track = i / fs->secpercyl; - x = i % fs->secpercyl; - side = x / fs->secpertrack; - sector = x % fs->secpertrack +1 ; - - try =5 ; -do { - ret =swim_read_sector (fs, side, track, sector, - buffer); -if (try-- ==0 ) -return BLK_STS_IOERR ; - }while (ret !=512 ); - - buffer += ret; +while (sectors_nb) { +unsigned int cyl, x, head, sector, n, n_ok; + + cyl = req_sector / fs->secpercyl; + x = req_sector % fs->secpercyl; + head = (x >= fs->secpertrack) ?1 :0 ; + sector = x % fs->secpertrack; + n =min (sectors_nb, fs->secpertrack - sector); + +swim_track (fs, cyl); +swim_head (base, head); + + n_ok =swim_read_sector_range (fs, head, cyl, sector +1 , n, buffer); +if (n_ok == n) + try =0 ; +else if (++try >=5 ) +return BLK_STS_IOERR ; + + buffer +=512 * n_ok; + sectors_nb -= n_ok; + req_sector += n_ok; }return 0 ;
drivers/block/swim_asm.S +10 -2
@@ -198,12 +198,14 @@ read_data_loop: dbne %d2, read_data_loop beq data_exit moveq #max_retry, %d2 - moveb %a5@, %a4@+ + moveb %a5@, %d3 + bsr .Lmaybe_store dbra %d4, 1f bra data_crc0 1: andb #.Lhr_fifo_2bytes, %d5 beq read_data_loop - moveb %a5@, %a4@+ + moveb %a5@, %d3 + bsr .Lmaybe_store dbra %d4, read_data_loop /* read CRC */
@@ -240,3 +242,9 @@ data_crc1: data_exit: moveb #0x18, %a3@(write_mode0 - read_mark) rts + +.Lmaybe_store: + tstl %a4 + beq 9f + moveb %d3, %a4@+ +9: rts
Signature · 1 line
2.52.0
PATCH v3 25/33 swim: Don't search beyond the first data mark
In reply to Finn ThainThe ISM chip does an automatic MFM gap/sync search when the Action bit is first set. That search may stop at any of a) post-index gap, b) address field gap or c) data field gap. To find the next sector header, the driver need not search at all. It only has to validate the mark bytes.
Once the sector address mark has been validated, swim_read_sector_data() is called to read the sector contents. Between the sector address and data fields lies an intra-sector gap followed by a data field mark. After this mark is validated, the 512-byte data area is read into the IO request buffer.
Problem is, if any byte in the data field mark is mis-read, the driver searches the whole sector and then reaches the data field mark in the following sector. The wrong sector is then read into the buffer, and swim_read_sector_data() returns success. The request is silently corrupted.
The existing limit on polling loop iterations does constrain the search distance but is inherently tied to CPU speed. This is probably the reason why corruption was only observed on a 68030 system.
Discontinue the mark search when the mark bytes fail validation.
--- drivers/block/swim_asm.S | 73 ++++++++++++++++++++++------------------ 1 file changed, 40 insertions(+), 33 deletions(-)
drivers/block/swim_asm.S +40 -33
@@ -41,18 +41,45 @@ .equ seek_time, 30000 .equ max_retry, 40 .equ sector_size, 512 + .equ .Lmark_sequence_len, 4 .equ .Lhr_crc_error, 0x02 .equ .Lhr_fifo_2bytes, 0x40 .equ .Lhr_fifo_1byte, 0x80 +.Lmfm_mark_check: + /* + * This subroutine reads and validates a mark byte sequence. + * On entry, %a1 and %d4 shall hold the location and length (resp.) + * of the mark byte array. + * %a2 and %a3 shall hold the locations of the handshake and mark + * registers. + * Returns zero in %d1 for success. + */ + + moveq #-1, %d1 + subq #1, %d4 + movew #seek_time, %d2 + +5: tstb %a2@ + dbmi %d2, 5b + bpl 6f + + moveb %a3@, %d3 + cmpb %a1@+, %d3 + dbne %d4, 5b + bne 6f + + moveq #0, %d1 +6: rts + .global swim_read_sector_header swim_read_sector_header: link %a6, #0 moveml %d1-%d5/%a0-%a5,%sp@- movel %a6@(0x0c), %a4 moveq #-1, %d0 - bsr mfm_read_addrmark + bsr .Lmfm_read_header moveml %sp@+, %d1-%d5/%a0-%a5 unlk %a6 rts
@@ -62,33 +89,25 @@ sector_address_mark: sector_data_mark: .byte 0xa1, 0xa1, 0xa1, 0xfb -mfm_read_addrmark: +.Lmfm_read_header: movel %a6@(0x08), %a3 lea %a3@(read_handshake), %a2 lea %a3@(read_data), %a5 lea %a3@(read_mark), %a3 - movew #seek_time, %d2 -wait_header_init: moveb #0x18, %a3@(write_mode0 - read_mark) moveb #0x01, %a3@(write_mode1 - read_mark) moveb #0x01, %a3@(write_mode0 - read_mark) tstb %a3@(read_error - read_mark) moveb #0x08, %a3@(write_mode1 - read_mark) - lea sector_address_mark, %a0 - moveq #3, %d1 - -wait_addr_mark_byte: - - tstb %a2@ - dbmi %d2, wait_addr_mark_byte - bpl signal_nonyb + lea sector_address_mark, %a1 + moveq #.Lmark_sequence_len, %d4 + bsr .Lmfm_mark_check + tstl %d1 + bne signal_nonyb - moveb %a3@, %d3 - cmpb %a0@+, %d3 - dbne %d1, wait_addr_mark_byte - bne wait_header_init + /* read header */ moveq #max_retry, %d2
@@ -163,30 +182,18 @@ mfm_read_data: lea %a3@(read_handshake), %a2 lea %a3@(read_data), %a5 lea %a3@(read_mark), %a3 - movew #seek_time, %d2 -wait_data_init: moveb #0x18, %a3@(write_mode0 - read_mark) moveb #0x01, %a3@(write_mode1 - read_mark) moveb #0x01, %a3@(write_mode0 - read_mark) tstb %a3@(read_error - read_mark) moveb #0x08, %a3@(write_mode1 - read_mark) - lea sector_data_mark, %a0 - moveq #3, %d1 - - /* wait data address mark */ - -wait_data_mark_byte: - - tstb %a2@ - dbmi %d2, wait_data_mark_byte - bpl data_exit - - moveb %a3@, %d3 - cmpb %a0@+, %d3 - dbne %d1, wait_data_mark_byte - bne wait_data_init + lea sector_data_mark, %a1 + moveq #.Lmark_sequence_len, %d4 + bsr .Lmfm_mark_check + tstl %d1 + bne data_exit /* read data */
Signature · 1 line
2.52.0
PATCH v3 26/33 swim: Remove pointless specifiers
In reply to Finn ThainIf the compiler made these functions "as fast as possible" that wouldn't actually help because they involve slow mechanical operations. Remove pointless inline function specifiers.
--- drivers/block/swim.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-)
drivers/block/swim.c +10 -10
@@ -222,7 +222,7 @@ 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) +static void set_swim_mode (struct swim __iomem *base,int enable) {struct iwm __iomem *iwm_base = (struct iwm __iomem *)base;unsigned long flags;
@@ -250,7 +250,7 @@ static inline void set_swim_mode(struct swim __iomem *base, int enable)local_irq_restore (flags); } -static inline int get_swim_mode (struct swim __iomem *base) +static int get_swim_mode (struct swim __iomem *base) {unsigned long flags;
@@ -321,7 +321,7 @@ static inline bool swim_readbit(struct swim __iomem *base, int bit) ret; \ }) -static inline void swim_drive (struct swim __iomem *base, +static void swim_drive (struct swim __iomem *base,enum drive_location location) {if (location ==INTERNAL_DRIVE ) {
@@ -340,8 +340,8 @@ static inline void swim_drive(struct swim __iomem *base,udelay (1 ); } -static inline void swim_motor (struct swim __iomem *base, -enum motor_action action) +static void swim_motor (struct swim __iomem *base, +enum motor_action action) {if (action ==ON ) {swim_action (base,MOTOR_ON );
@@ -352,7 +352,7 @@ static inline void swim_motor(struct swim __iomem *base, } } -static inline void swim_eject (struct swim __iomem *base) +static void swim_eject (struct swim __iomem *base) {swim_action (base,EJECT );swim_readbit_timeout (base,DISK_IN , false,2000 *1000 );
@@ -367,14 +367,14 @@ static inline void swim_head(struct swim __iomem *base, enum head head)swim_select (base,READ_DATA_0 ); } -static inline int swim_step (struct swim __iomem *base) +static int swim_step (struct swim __iomem *base) {swim_action (base,STEP );udelay (150 );return swim_readbit_timeout (base,STEP , false,20 *1000 ); } -static inline int swim_track00 (struct swim __iomem *base) +static int swim_track00 (struct swim __iomem *base) {int try;
@@ -394,7 +394,7 @@ static inline int swim_track00(struct swim __iomem *base)return -1 ; } -static inline int swim_seek (struct swim __iomem *base,int step) +static int swim_seek (struct swim __iomem *base,int step) {if (step <0 ) {swim_action (base,SEEK_NEGATIVE );
@@ -419,7 +419,7 @@ static inline int swim_seek(struct swim __iomem *base, int step)return 0 ; } -static inline int swim_track (struct floppy_state *fs,int track) +static int swim_track (struct floppy_state *fs,int track) {struct swim __iomem *base = fs->swd->base;int ret;
Signature · 1 line
2.52.0
PATCH v3 27/33 swim: Move swd initialization
In reply to Finn ThainFor better readability, initialize the swd backpointer along with the other floppy_state struct members. No functional change.
--- drivers/block/swim.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
drivers/block/swim.c +1 -2
@@ -764,6 +764,7 @@ static void swim_add_floppy(struct swim_priv *swd, enum drive_location location)if (swim_track00 (base)) goto out; + fs->swd = swd; fs->location = location; fs->type =HD_MEDIA ;
@@ -853,8 +854,6 @@ static int swim_floppy_init(struct platform_device *pdev) err =PTR_ERR (swd->unit[drive].disk); goto exit_put_disks; } - - swd->unit[drive].swd = swd; }for (drive =0 ; drive < swd->floppy_count; drive++) {
Signature · 1 line
2.52.0
PATCH v3 28/33 swim: Add some helpful references
In reply to Finn ThainThese documents relate to the IWM, ISM, SWIM 1, 2, 3 and associated disk drives. No functional change.
--- drivers/block/swim.c | 8 ++++++++ 1 file changed, 8 insertions(+)
drivers/block/swim.c +8 -0
@@ -8,6 +8,14 @@* based on SWIM3 driver (c) Paul Mackerras, 1996 * based on netBSD IWM driver (c) 1997, 1998 Hauke Fath. * +* See also: +* Inside Macintosh, vol. III, ch. 2 +* https://archive.org/details/SWIMDesignDocs +* NEC uPD72070 FDC datasheet +* mkLinux source file swimiiicommonhal.c +* http://www.mac.linux-m68k.org/devel/iwm.php.html +* MAME source file sonydriv.cpp +* * 2004-08-21 (lv) - Initial implementation * 2008-10-30 (lv) - Port to 2.6 */
Signature · 1 line
2.52.0
PATCH v3 29/33 swim: Remove unused macro definitions
In reply to Finn ThainAlso remove the horizontal rule at the end of the macro definitions as it doesn't any add value, IMO.
--- drivers/block/swim.c | 14 -------------- 1 file changed, 14 deletions(-)
drivers/block/swim.c +0 -14
@@ -130,25 +130,14 @@ struct iwm {/* 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 */
@@ -157,12 +146,9 @@ struct iwm {#define ENBL2 0x04#define ACTION 0x08#define WRITE_MODE 0x10 -#define HEDSEL 0x20#define ISM_SELECT 0x40#define MOTON 0x80 -/*----------------------------------------------------------------------------*/ -enum drive_location {NO_DRIVE =0 ,INTERNAL_DRIVE =BIT (1 ),
Signature · 1 line
2.52.0
PATCH v3 30/33 swim: Clean up whitespace
In reply to Finn ThainNo functional changes.
--- drivers/block/swim.c | 46 +++++++++++++++++++++------------------- drivers/block/swim_asm.S | 14 +++--------- 2 files changed, 27 insertions(+), 33 deletions(-)
drivers/block/swim.c +24 -22
@@ -2,7 +2,7 @@/* * Driver for SWIM (Sander Woz Integrated Machine) floppy controller * -* Copyright (C) 2004,2008 Laurent Vivier <[email protected]> +* Copyright (C) 2004, 2008 Laurent Vivier <[email protected]> * * based on Alastair Bridgewater SWIM analysis, 2001 * based on SWIM3 driver (c) Paul Mackerras, 1996
@@ -69,7 +69,7 @@ struct swim {REG (read_handshake) } __attribute__((packed)); -#define swim_write (base, reg, v) out_8(&(base)->write_##reg, (v)) +#define swim_write (base, reg, v) out_8(&(base)->write_##reg, (v))#define swim_read (base, reg) in_8(&(base)->read_##reg)/* IWM registers */
@@ -93,7 +93,7 @@ struct iwm {REG (q7H) } __attribute__((packed)); -#define iwm_write (base, reg, v) out_8(&(base)->reg, (v)) +#define iwm_write (base, reg, v) out_8(&(base)->reg, (v))#define iwm_read (base, reg) in_8(&(base)->reg)/* Bits in phase register */
@@ -165,23 +165,23 @@ struct floppy_state {/* physical properties */ enum drive_location location;/* internal or external drive */ -int head_number;/* single- or double-sided drive */ +int head_number;/* single- or double-sided drive */ /* media */ -int disk_in; -int ejected; -enum media_type type; -int write_protected; +int disk_in; +int ejected; +enum media_type type; +int write_protected; -unsigned int total_secs; -unsigned int secpercyl; -unsigned int secpertrack; +unsigned int total_secs; +unsigned int secpercyl; +unsigned int secpertrack;/* in-use information */ -int track; -int ref_count; +int track; +int ref_count;bool registered;struct gendisk *disk;
@@ -216,6 +216,7 @@ extern int swim_read_sector_data(struct swim __iomem *base, unsigned char *data);static DEFINE_MUTEX (swim_mutex); +static void set_swim_mode (struct swim __iomem *base,int enable) {struct iwm __iomem *iwm_base = (struct iwm __iomem *)base;
@@ -402,7 +403,7 @@ static int swim_seek(struct swim __iomem *base, int step)if (step ==0 )return 0 ; -for ( ; step >0 ; step--) { +for (; step >0 ; step--) {if (swim_step (base))return -1 ; }
@@ -413,7 +414,7 @@ static int swim_seek(struct swim __iomem *base, int step)return 0 ; } -static int swim_track (struct floppy_state *fs,int track) +static int swim_track (struct floppy_state *fs,int track) {struct swim __iomem *base = fs->swd->base;int ret;
@@ -555,10 +556,10 @@ static blk_status_t swim_queue_rq(struct blk_mq_hw_ctx *hctx, }static struct floppy_struct floppy_type[4 ] = { - {0 ,0 ,0 ,0 ,0 ,0x00 ,0x00 ,0x00 ,0x00 ,NULL },/* no testing */ + {0 ,0 ,0 ,0 ,0 ,0x00 ,0x00 ,0x00 ,0x00 ,NULL },/* no testing */ {720 ,9 ,1 ,80 ,0 ,0x2A ,0x02 ,0xDF ,0x50 ,NULL },/* 360KB SS 3.5"*/ - {1440 ,9 ,2 ,80 ,0 ,0x2A ,0x02 ,0xDF ,0x50 ,NULL },/* 720KB 3.5" */ - {2880 ,18 ,2 ,80 ,0 ,0x1B ,0x00 ,0xCF ,0x6C ,NULL },/* 1.44MB 3.5" */ + {1440 ,9 ,2 ,80 ,0 ,0x2A ,0x02 ,0xDF ,0x50 ,NULL },/* 720KB 3.5" */ + {2880 ,18 ,2 ,80 ,0 ,0x1B ,0x00 ,0xCF ,0x6C ,NULL },/* 1.44MB 3.5" */ }; static int get_floppy_geometry(struct floppy_state *fs, int type,
@@ -585,10 +586,11 @@ static void setup_medium(struct floppy_state *fs)if (swim_readbit (base,DISK_IN )) {struct floppy_struct *g; + fs->disk_in =1 ; fs->write_protected =swim_readbit (base,WRITE_PROT ); fs->type =swim_readbit (base,TWOMEG_MEDIA ) ? -HD_MEDIA :DD_MEDIA ; +HD_MEDIA :DD_MEDIA ; fs->head_number =swim_readbit (base,SINGLE_SIDED ) ?1 :2 ;get_floppy_geometry (fs,0 , &g); fs->total_secs = g->size;
@@ -690,7 +692,7 @@ static int floppy_ioctl(struct block_device *bdev, blk_mode_t mode,int err;if ((cmd &0x80 ) && !capable (CAP_SYS_ADMIN )) -return -EPERM ; +return -EPERM ;switch (cmd) {case FDEJECT :
@@ -809,7 +811,7 @@ static int swim_floppy_init(struct platform_device *pdev)bool *data = pdev->dev.platform_data;bool fast_fclk = data && *data;struct queue_limits lim = { - .features =BLK_FEAT_ROTATIONAL , + .features =BLK_FEAT_ROTATIONAL , };int err;int drive;
@@ -961,7 +963,7 @@ static void swim_remove(struct platform_device *dev)static struct platform_driver swim_driver = { .probe = swim_probe, .remove = swim_remove, - .driver = { + .driver = { .name =CARDNAME , }, };
drivers/block/swim_asm.S +3 -11
@@ -5,7 +5,7 @@ * needs assembly language because is very timing dependent * this controller exists only on macintosh 680x0 based * - * Copyright (C) 2004,2008 Laurent Vivier <[email protected]> + * Copyright (C) 2004, 2008 Laurent Vivier <[email protected]> * * based on Alastair Bridgewater SWIM analysis, 2001 * based on netBSD IWM driver (c) 1997, 1998 Hauke Fath.
@@ -110,7 +110,6 @@ sector_data_mark: /* read header */ moveq #max_retry, %d2 - amark0: tstb %a2@ dbmi %d2, amark0 bpl signal_nonyb
@@ -118,7 +117,6 @@ amark0: tstb %a2@ moveb %a5@, %a4@(o_track) moveq #max_retry, %d2 - amark1: tstb %a2@ dbmi %d2, amark1 bpl signal_nonyb
@@ -126,7 +124,6 @@ amark1: tstb %a2@ moveb %a5@, %a4@(o_side) moveq #max_retry, %d2 - amark2: tstb %a2@ dbmi %d2, amark2 bpl signal_nonyb
@@ -134,7 +131,6 @@ amark2: tstb %a2@ moveb %a5@, %a4@(o_sector) moveq #max_retry, %d2 - amark3: tstb %a2@ dbmi %d2, amark3 bpl signal_nonyb
@@ -142,7 +138,6 @@ amark3: tstb %a2@ moveb %a5@, %a4@(o_size) moveq #max_retry, %d2 - crc0: tstb %a2@ dbmi %d2, crc0 bpl signal_nonyb
@@ -150,7 +145,6 @@ crc0: tstb %a2@ moveb %a5@, %a4@(o_crc0) moveq #max_retry, %d2 - crc1: tstb %a2@ dbmi %d2, crc1 bpl signal_nonyb
@@ -197,13 +191,14 @@ mfm_read_data: /* read data */ - movel #sector_size-1, %d4 /* sector size */ + movel #sector_size - 1, %d4 /* sector size */ movew #max_retry, %d2 read_data_loop: moveb %a2@, %d5 andb #(.Lhr_fifo_1byte + .Lhr_fifo_2bytes), %d5 dbne %d2, read_data_loop beq data_exit + moveq #max_retry, %d2 moveb %a5@, %d3 bsr .Lmaybe_store
@@ -219,7 +214,6 @@ read_data_loop: movew #max_retry, %d2 data_crc0: - tstb %a2@ dbmi %d2, data_crc0 bpl data_exit
@@ -227,9 +221,7 @@ data_crc0: moveb %a5@, %d5 moveq #max_retry, %d2 - data_crc1: - tstb %a2@ dbmi %d2, data_crc1 bpl data_exit
Signature · 1 line
2.52.0
PATCH v3 31/33 swim: Define macros for constants
In reply to Finn ThainDefine a SEL_MASK macro to name the anonymous constant. Define STEPPING rather than re-use STEP because the latter is a command bit macro (see also GCR_MODE vs. SETGCR). No functional change, just better readability.
--- drivers/block/swim.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)
drivers/block/swim.c +5 -2
@@ -105,6 +105,8 @@ struct iwm {/* Select values for swim_select and swim_readbit */ +#define SEL_MASK 0x100 +#define SEEK_POSITIVE 0x000#define SEEK_NEGATIVE 0x004#define STEP 0x001
@@ -115,6 +117,7 @@ struct iwm {#define SETMFM 0x101#define SETGCR 0x105 +#define STEPPING 0x001#define READ_DATA_0 0x004#define ONEMEG_DRIVE 0x005#define SINGLE_SIDED 0x006
@@ -271,7 +274,7 @@ static inline void swim_select(struct swim __iomem *base, int sel) {swim_write (base, phase,RELAX |PHASE_PIN_DIR ); -via1_set_head (sel &0x100 ); +via1_set_head (sel &SEL_MASK );swim_write (base, phase, (sel &CA_MASK ) |PHASE_PIN_DIR ); }
@@ -366,7 +369,7 @@ static int swim_step(struct swim __iomem *base) {swim_action (base,STEP );udelay (150 ); -return swim_readbit_timeout (base,STEP , false,20 *1000 ); +return swim_readbit_timeout (base,STEPPING , false,20 *1000 ); }static int swim_track00 (struct swim __iomem *base)
Signature · 1 line
2.52.0
PATCH v3 32/33 swim: Define symbols for constants
In reply to Finn ThainDefine local symbols to give some meaning to anonymous constants. No functional change.
--- drivers/block/swim_asm.S | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-)
drivers/block/swim_asm.S +14 -10
@@ -43,6 +43,10 @@ .equ sector_size, 512 .equ .Lmark_sequence_len, 4 + .equ .Lmr_clear_fifo, 0x01 + .equ .Lmr_action, 0x08 + .equ .Lmr_write_action, 0x18 + .equ .Lhr_crc_error, 0x02 .equ .Lhr_fifo_2bytes, 0x40 .equ .Lhr_fifo_1byte, 0x80
@@ -95,11 +99,11 @@ sector_data_mark: lea %a3@(read_data), %a5 lea %a3@(read_mark), %a3 - moveb #0x18, %a3@(write_mode0 - read_mark) - moveb #0x01, %a3@(write_mode1 - read_mark) - moveb #0x01, %a3@(write_mode0 - read_mark) + moveb #.Lmr_write_action, %a3@(write_mode0 - read_mark) + moveb #.Lmr_clear_fifo, %a3@(write_mode1 - read_mark) + moveb #.Lmr_clear_fifo, %a3@(write_mode0 - read_mark) tstb %a3@(read_error - read_mark) - moveb #0x08, %a3@(write_mode1 - read_mark) + moveb #.Lmr_action, %a3@(write_mode1 - read_mark) lea sector_address_mark, %a1 moveq #.Lmark_sequence_len, %d4
@@ -157,7 +161,7 @@ crc1: tstb %a2@ moveq #0, %d0 signal_nonyb: - moveb #0x18, %a3@(write_mode0 - read_mark) + moveb #.Lmr_write_action, %a3@(write_mode0 - read_mark) rts .global swim_read_sector_data
@@ -177,11 +181,11 @@ mfm_read_data: lea %a3@(read_data), %a5 lea %a3@(read_mark), %a3 - moveb #0x18, %a3@(write_mode0 - read_mark) - moveb #0x01, %a3@(write_mode1 - read_mark) - moveb #0x01, %a3@(write_mode0 - read_mark) + moveb #.Lmr_write_action, %a3@(write_mode0 - read_mark) + moveb #.Lmr_clear_fifo, %a3@(write_mode1 - read_mark) + moveb #.Lmr_clear_fifo, %a3@(write_mode0 - read_mark) tstb %a3@(read_error - read_mark) - moveb #0x08, %a3@(write_mode1 - read_mark) + moveb #.Lmr_action, %a3@(write_mode1 - read_mark) lea sector_data_mark, %a1 moveq #.Lmark_sequence_len, %d4
@@ -239,7 +243,7 @@ data_crc1: subl %d4, %d0 data_exit: - moveb #0x18, %a3@(write_mode0 - read_mark) + moveb #.Lmr_write_action, %a3@(write_mode0 - read_mark) rts .Lmaybe_store:
Signature · 1 line
2.52.0
PATCH v3 33/33 swim: Unexport global symbols
In reply to Finn ThainThese symbols aren't used outside of this file so use local ones. No functional change.
--- drivers/block/swim_asm.S | 218 ++++++++++++++++++++------------------- 1 file changed, 112 insertions(+), 106 deletions(-)
drivers/block/swim_asm.S +112 -106
@@ -14,33 +14,33 @@ * 2008-11-05 (lv) - add get_swim_mode */ - .equ write_data, 0x0000 - .equ write_mark, 0x0200 - .equ write_CRC, 0x0400 - .equ write_parameter,0x0600 - .equ write_phase, 0x0800 - .equ write_setup, 0x0a00 - .equ write_mode0, 0x0c00 - .equ write_mode1, 0x0e00 - .equ read_data, 0x1000 - .equ read_mark, 0x1200 - .equ read_error, 0x1400 - .equ read_parameter, 0x1600 - .equ read_phase, 0x1800 - .equ read_setup, 0x1a00 - .equ read_status, 0x1c00 - .equ read_handshake, 0x1e00 - - .equ o_side, 0 - .equ o_track, 1 - .equ o_sector, 2 - .equ o_size, 3 - .equ o_crc0, 4 - .equ o_crc1, 5 - - .equ seek_time, 30000 - .equ max_retry, 40 - .equ sector_size, 512 + .equ .Lwrite_data, 0x0000 + .equ .Lwrite_mark, 0x0200 + .equ .Lwrite_CRC, 0x0400 + .equ .Lwrite_parameter, 0x0600 + .equ .Lwrite_phase, 0x0800 + .equ .Lwrite_setup, 0x0a00 + .equ .Lwrite_mode0, 0x0c00 + .equ .Lwrite_mode1, 0x0e00 + .equ .Lread_data, 0x1000 + .equ .Lread_mark, 0x1200 + .equ .Lread_error, 0x1400 + .equ .Lread_parameter, 0x1600 + .equ .Lread_phase, 0x1800 + .equ .Lread_setup, 0x1a00 + .equ .Lread_status, 0x1c00 + .equ .Lread_handshake, 0x1e00 + + .equ .Lo_side, 0 + .equ .Lo_track, 1 + .equ .Lo_sector, 2 + .equ .Lo_size, 3 + .equ .Lo_crc0, 4 + .equ .Lo_crc1, 5 + + .equ .Lseek_time, 30000 + .equ .Lmax_retry, 40 + .equ .Lsector_size, 512 .equ .Lmark_sequence_len, 4 .equ .Lmr_clear_fifo, 0x01
@@ -63,7 +63,7 @@ moveq #-1, %d1 subq #1, %d4 - movew #seek_time, %d2 + movew #.Lseek_time, %d2 5: tstb %a2@ dbmi %d2, 5b
@@ -88,80 +88,86 @@ swim_read_sector_header: unlk %a6 rts -sector_address_mark: +.Lsector_address_mark: .byte 0xa1, 0xa1, 0xa1, 0xfe -sector_data_mark: +.Lsector_data_mark: .byte 0xa1, 0xa1, 0xa1, 0xfb .Lmfm_read_header: movel %a6@(0x08), %a3 - lea %a3@(read_handshake), %a2 - lea %a3@(read_data), %a5 - lea %a3@(read_mark), %a3 + lea %a3@(.Lread_handshake), %a2 + lea %a3@(.Lread_data), %a5 + lea %a3@(.Lread_mark), %a3 - moveb #.Lmr_write_action, %a3@(write_mode0 - read_mark) - moveb #.Lmr_clear_fifo, %a3@(write_mode1 - read_mark) - moveb #.Lmr_clear_fifo, %a3@(write_mode0 - read_mark) - tstb %a3@(read_error - read_mark) - moveb #.Lmr_action, %a3@(write_mode1 - read_mark) + moveb #.Lmr_write_action, %a3@(.Lwrite_mode0 - .Lread_mark) + moveb #.Lmr_clear_fifo, %a3@(.Lwrite_mode1 - .Lread_mark) + moveb #.Lmr_clear_fifo, %a3@(.Lwrite_mode0 - .Lread_mark) + tstb %a3@(.Lread_error - .Lread_mark) + moveb #.Lmr_action, %a3@(.Lwrite_mode1 - .Lread_mark) - lea sector_address_mark, %a1 + lea .Lsector_address_mark, %a1 moveq #.Lmark_sequence_len, %d4 bsr .Lmfm_mark_check tstl %d1 - bne signal_nonyb + bne .Lsignal_nonyb /* read header */ - moveq #max_retry, %d2 -amark0: tstb %a2@ - dbmi %d2, amark0 - bpl signal_nonyb + moveq #.Lmax_retry, %d2 +.Lamark0: + tstb %a2@ + dbmi %d2, .Lamark0 + bpl .Lsignal_nonyb - moveb %a5@, %a4@(o_track) + moveb %a5@, %a4@(.Lo_track) - moveq #max_retry, %d2 -amark1: tstb %a2@ - dbmi %d2, amark1 - bpl signal_nonyb + moveq #.Lmax_retry, %d2 +.Lamark1: + tstb %a2@ + dbmi %d2, .Lamark1 + bpl .Lsignal_nonyb - moveb %a5@, %a4@(o_side) + moveb %a5@, %a4@(.Lo_side) - moveq #max_retry, %d2 -amark2: tstb %a2@ - dbmi %d2, amark2 - bpl signal_nonyb + moveq #.Lmax_retry, %d2 +.Lamark2: + tstb %a2@ + dbmi %d2, .Lamark2 + bpl .Lsignal_nonyb - moveb %a5@, %a4@(o_sector) + moveb %a5@, %a4@(.Lo_sector) - moveq #max_retry, %d2 -amark3: tstb %a2@ - dbmi %d2, amark3 - bpl signal_nonyb + moveq #.Lmax_retry, %d2 +.Lamark3: + tstb %a2@ + dbmi %d2, .Lamark3 + bpl .Lsignal_nonyb - moveb %a5@, %a4@(o_size) + moveb %a5@, %a4@(.Lo_size) - moveq #max_retry, %d2 -crc0: tstb %a2@ - dbmi %d2, crc0 - bpl signal_nonyb + moveq #.Lmax_retry, %d2 +.Lcrc0: + tstb %a2@ + dbmi %d2, .Lcrc0 + bpl .Lsignal_nonyb - moveb %a5@, %a4@(o_crc0) + moveb %a5@, %a4@(.Lo_crc0) - moveq #max_retry, %d2 -crc1: tstb %a2@ - dbmi %d2, crc1 - bpl signal_nonyb + moveq #.Lmax_retry, %d2 +.Lcrc1: + tstb %a2@ + dbmi %d2, .Lcrc1 + bpl .Lsignal_nonyb - moveb %a5@, %a4@(o_crc1) + moveb %a5@, %a4@(.Lo_crc1) moveb %a2@, %d5 andb #.Lhr_crc_error, %d5 - bne signal_nonyb + bne .Lsignal_nonyb moveq #0, %d0 -signal_nonyb: - moveb #.Lmr_write_action, %a3@(write_mode0 - read_mark) +.Lsignal_nonyb: + moveb #.Lmr_write_action, %a3@(.Lwrite_mode0 - .Lread_mark) rts .global swim_read_sector_data
@@ -170,80 +176,80 @@ swim_read_sector_data: moveml %d1-%d5/%a0-%a5,%sp@- movel %a6@(0x0c), %a4 moveq #-1, %d0 - bsr mfm_read_data + bsr .Lmfm_read_data moveml %sp@+, %d1-%d5/%a0-%a5 unlk %a6 rts -mfm_read_data: +.Lmfm_read_data: movel %a6@(0x08), %a3 - lea %a3@(read_handshake), %a2 - lea %a3@(read_data), %a5 - lea %a3@(read_mark), %a3 + lea %a3@(.Lread_handshake), %a2 + lea %a3@(.Lread_data), %a5 + lea %a3@(.Lread_mark), %a3 - moveb #.Lmr_write_action, %a3@(write_mode0 - read_mark) - moveb #.Lmr_clear_fifo, %a3@(write_mode1 - read_mark) - moveb #.Lmr_clear_fifo, %a3@(write_mode0 - read_mark) - tstb %a3@(read_error - read_mark) - moveb #.Lmr_action, %a3@(write_mode1 - read_mark) + moveb #.Lmr_write_action, %a3@(.Lwrite_mode0 - .Lread_mark) + moveb #.Lmr_clear_fifo, %a3@(.Lwrite_mode1 - .Lread_mark) + moveb #.Lmr_clear_fifo, %a3@(.Lwrite_mode0 - .Lread_mark) + tstb %a3@(.Lread_error - .Lread_mark) + moveb #.Lmr_action, %a3@(.Lwrite_mode1 - .Lread_mark) - lea sector_data_mark, %a1 + lea .Lsector_data_mark, %a1 moveq #.Lmark_sequence_len, %d4 bsr .Lmfm_mark_check tstl %d1 - bne data_exit + bne .Ldata_exit /* read data */ - movel #sector_size - 1, %d4 /* sector size */ - movew #max_retry, %d2 -read_data_loop: + movel #.Lsector_size - 1, %d4 /* sector size */ + movew #.Lmax_retry, %d2 +.Lread_data_loop: moveb %a2@, %d5 andb #(.Lhr_fifo_1byte + .Lhr_fifo_2bytes), %d5 - dbne %d2, read_data_loop - beq data_exit + dbne %d2, .Lread_data_loop + beq .Ldata_exit - moveq #max_retry, %d2 + moveq #.Lmax_retry, %d2 moveb %a5@, %d3 bsr .Lmaybe_store dbra %d4, 1f - bra data_crc0 + bra .Ldata_crc0 1: andb #.Lhr_fifo_2bytes, %d5 - beq read_data_loop + beq .Lread_data_loop moveb %a5@, %d3 bsr .Lmaybe_store - dbra %d4, read_data_loop + dbra %d4, .Lread_data_loop /* read CRC */ - movew #max_retry, %d2 -data_crc0: + movew #.Lmax_retry, %d2 +.Ldata_crc0: tstb %a2@ - dbmi %d2, data_crc0 - bpl data_exit + dbmi %d2, .Ldata_crc0 + bpl .Ldata_exit moveb %a5@, %d5 - moveq #max_retry, %d2 -data_crc1: + moveq #.Lmax_retry, %d2 +.Ldata_crc1: tstb %a2@ - dbmi %d2, data_crc1 - bpl data_exit + dbmi %d2, .Ldata_crc1 + bpl .Ldata_exit moveb %a5@, %d5 moveb %a2@, %d5 andb #.Lhr_crc_error, %d5 - bne data_exit + bne .Ldata_exit /* return number of bytes read */ - movel #sector_size, %d0 + movel #.Lsector_size, %d0 addw #1, %d4 subl %d4, %d0 -data_exit: - moveb #.Lmr_write_action, %a3@(write_mode0 - read_mark) +.Ldata_exit: + moveb #.Lmr_write_action, %a3@(.Lwrite_mode0 - .Lread_mark) rts .Lmaybe_store:
Signature · 1 line
2.52.0
This patch series was successfully tested on a Mac IIci.
Quoted 83 lines · Finn Thain
I recently had a need for the 'swim' driver but found that it was too buggy to be useful for my purposes. I had two aims in mind--
Firstly, I needed this driver to help me test my new BMoW Floppy Emu for emulation correctness. Being that Linux is open source and MacOS is not, this driver should be ideal for that.
Secondly, I needed to realign the heads in some disk drives. The swim driver has most of the code needed for a feedback loop for manual re-alignment, so I wrote the remaining code and pushed it to a repo here:
https://github.com/fthain/linux/commits/swim/These patches fix all the bugs I found. They improve stability, compatibility and performance. Additional patches improve source code quality by removing redundant code and cleaning up a bit.
This patch series was successfully tested on the following systems.
Mac Quadra 650 - SWIM 2 with Mitsubishi FDD
Mac LC III - SWIM 2 with Mitsubishi FDD and Sony FDD
Mac Quadra 700 - SWIM with Sony FDD
Mac IIvx - SWIM with Sony FDD---
Changed since v1:
- Patches 1/31, 19/31, 23/31 were revised as per Laurent's remarks.
- Some minor improvements to patches 9/31, 13/31 and 16/31.
- Added patch 24/32 to fix a bug causing silent IO corruption instead of an IO error.Changed since v2:
- Reviewed-by tags were dropped from patches 1/32, 9/32, 13/32 and 16/32 because of unreviewed changes.
- Better assembly code in patch 13/32.
- Patch 23/32 improved upon to reduce excessive interrupt latency when medium errors are encountered.
- Patch 7/32 was split in two, and readability was improved as requested by Geert.
- Geert's acked-by tag was added.---
Finn Thain (33):
swim: Assert strobe with stable outputs
swim: Select appropriate drive once only
swim: Enable the drive when probing
swim: Don't disable drive after every sector
swim: Perform ISM/IWM mode switching according to specs
swim: Configure parameter memory
swim: Refactor SWIM setup
swim: Enable clock divider only where appropriate
swim: Don't start motor until medium is present
swim: Recalibrate when drive is probed
swim: Add track zero recalibration delay
swim: Handle FIFO timeout error
swim: Simplify return value initialization
swim: Check for CRC errors
swim: Check error register during sector read
swim: Don't use the mark register to read data
swim: Fix buffer overflow
swim: Convert to blocking queue
swim: Remove redundant RELAX actions
swim: Deduplicate polling loops
swim: Check drive ready bit
swim: Revisit delays
swim: Remove pointless mode0 register write
swim: Don't needlessly re-read sectors
swim: Don't search beyond the first data mark
swim: Remove pointless specifiers
swim: Move swd initialization
swim: Add some helpful references
swim: Remove unused macro definitions
swim: Clean up whitespace
swim: Define macros for constants
swim: Define symbols for constants
swim: Unexport global symbolsarch/m68k/mac/config.c | 31 ++- drivers/block/swim.c | 451 ++++++++++++++++++++------------------- drivers/block/swim_asm.S | 356 +++++++++++++++--------------- 3 files changed, 452 insertions(+), 386 deletions(-)
Quoted 8 lines · Finn Thain
I recently had a need for the 'swim' driver but found that it was too buggy to be useful for my purposes. I had two aims in mind--
Firstly, I needed this driver to help me test my new BMoW Floppy Emu for emulation correctness. Being that Linux is open source and MacOS is not, this driver should be ideal for that.
[...]
Applied, thanks!
[01/33] swim: Assert strobe with stable outputs
commit: adcc410bfb41715fef198a0975db4ed594a49bb7
[02/33] swim: Select appropriate drive once only
commit: a2d22b3f25c388f9fbce7bf36a45a8900dce4aa3
[03/33] swim: Enable the drive when probing
commit: 57d88c8f4ea34a4dd7c5351bd026e225c7283f3f
[04/33] swim: Don't disable drive after every sector
commit: 05a7c4b197279cfb3ec3df26146ce085a2438aec
[05/33] swim: Perform ISM/IWM mode switching according to specs
commit: 52981bb358d1b5c38040b00f81e2cf973f9148ad
[06/33] swim: Configure parameter memory
commit: 994a778d966592d0e211deaf6e436fcb21413935
[07/33] swim: Refactor SWIM setup
commit: 62977e8ea98e99dbf86609e65930e6607398e1c5
[08/33] swim: Enable clock divider only where appropriate
commit: 3c02a14c89962c88ed0abb3cce4101021c63149c
[09/33] swim: Don't start motor until medium is present
commit: dbbaaae83cb277d76e70426c0f0ce289e867ed77
[10/33] swim: Recalibrate when drive is probed
commit: ba087774a1493ade524f8a783461fa8be9c75039
[11/33] swim: Add track zero recalibration delay
commit: b9c1095048537cc9563f12d0a24f2f0df129e4d5
[12/33] swim: Handle FIFO timeout error
commit: 2e9bcbecee68431b689d16b23bc5529db686f8d4
[13/33] swim: Simplify return value initialization
commit: 385a44e78702762e2fce796cf37b6f465d86a87f
[14/33] swim: Check for CRC errors
commit: 01bff87152e6748fbac9ccd645d3d5f490d75ddc
[15/33] swim: Check error register during sector read
commit: d66bdef0a3083fed1a0fc4f0ee1c85b26476b21c
[16/33] swim: Don't use the mark register to read data
commit: cb7923133ec18e67e2d6718e29426a4423af6b73
[17/33] swim: Fix buffer overflow
commit: a54870a4ae4962acf341eb6ca010cae02af1a6ea
[18/33] swim: Convert to blocking queue
commit: 92353a04763726f70fea6be90957c50fc18215e0
[19/33] swim: Remove redundant RELAX actions
commit: 04cb277e6841b55b950b42063fe11501b5d1676a
[20/33] swim: Deduplicate polling loops
commit: b736189c93c9ad1056521a4229ec2f252b714b5a
[21/33] swim: Check drive ready bit
commit: abae04f5bed924590e98fe1519566a6e0144e6b5
[22/33] swim: Revisit delays
commit: bbcd3e38a4f355d6b0320f3f6916df96c109f2ad
[23/33] swim: Remove pointless mode0 register write
commit: 151848934fa4696067a8ca861cef5efe7aa1e046
[24/33] swim: Don't needlessly re-read sectors
commit: 22fbea97bc18fd099730572bf9aeba9879d62b02
[25/33] swim: Don't search beyond the first data mark
commit: 79eafa4ccb74be023fd2cae87d75425d6eb903bb
[26/33] swim: Remove pointless specifiers
commit: c516ded6f6da38b64ce9f69c88e65a65927296bb
[27/33] swim: Move swd initialization
commit: c0862cf48fae35571489b79b1c84db16691f2ad8
[28/33] swim: Add some helpful references
commit: bd4a21b8452d48be48cbc66693d27ff3c1f88d9b
[29/33] swim: Remove unused macro definitions
commit: 7d7c6d91f5657b2979cae7c7dcb49d2523efd894
[30/33] swim: Clean up whitespace
commit: e97cbb470cf94ce018558c899bcad387f69c99f5
[31/33] swim: Define macros for constants
commit: d4b2519065d6db2eb7cd36d178591058452da3e0
[32/33] swim: Define symbols for constants
commit: 75f47f0910f4f4289c11409398c05999aec19f37
[33/33] swim: Unexport global symbols
commit: e7a6727deab0d300171ee059b7dbcdbdb3c46797Best regards,
Signature · 1 line
Jens Axboe