Skip to content

block/swim: Fixes and improvements - #31

Open
blktests-ci-block-trial[bot] wants to merge 31 commits into
master_basefrom
series/355=>master
Open

block/swim: Fixes and improvements#31
blktests-ci-block-trial[bot] wants to merge 31 commits into
master_basefrom
series/355=>master

Conversation

@blktests-ci-block-trial

Copy link
Copy Markdown

Pull request for series with
subject: block/swim: Fixes and improvements
version: 1
url: http://redsun45:8000/project/linux-block/list/?series=355

fthain added 30 commits July 22, 2026 11:41
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 when LSTRB is driven low, CA0-CA2 get driven high.

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.

Fixes: 8852ecd ("m68k: mac - Add SWIM floppy support")
Signed-off-by: Finn Thain <fthain@linux-m68k.org>
Reviewed-by: Laurent Vivier <laurent@vivier.eu>
Before 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().

Fixes: 8852ecd ("m68k: mac - Add SWIM floppy support")
Signed-off-by: Finn Thain <fthain@linux-m68k.org>
Reviewed-by: Laurent Vivier <laurent@vivier.eu>
According 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.

Fixes: 8852ecd ("m68k: mac - Add SWIM floppy support")
Signed-off-by: Finn Thain <fthain@linux-m68k.org>
Reviewed-by: Laurent Vivier <laurent@vivier.eu>
This fixes multi-sector reads on a Sony drive. The Mitsubishi drive
doesn't seem to care either way.

Fixes: 8852ecd ("m68k: mac - Add SWIM floppy support")
Signed-off-by: Finn Thain <fthain@linux-m68k.org>
Reviewed-by: Laurent Vivier <laurent@vivier.eu>
The 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.

Fixes: 8852ecd ("m68k: mac - Add SWIM floppy support")
Signed-off-by: Finn Thain <fthain@linux-m68k.org>
Reviewed-by: Laurent Vivier <laurent@vivier.eu>
The 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.

Fixes: 8852ecd ("m68k: mac - Add SWIM floppy support")
Signed-off-by: Finn Thain <fthain@linux-m68k.org>
Reviewed-by: Laurent Vivier <laurent@vivier.eu>
Some 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.

This is chip initialization, so do this in swim_floppy_init() rather
than floppy_open(). Drop the udelay() which was apparently copied and
pasted from swim3.c, where it relates to interrupts (of which this chip
has none).

Cc: Joshua Thompson <funaho@jurai.org>
Fixes: 8852ecd ("m68k: mac - Add SWIM floppy support")
Signed-off-by: Finn Thain <fthain@linux-m68k.org>
Reviewed-by: Laurent Vivier <laurent@vivier.eu>
The spindle motor should not be running when a disk is to be inserted.
Don't start the motor while the drive is empty.

Fixes: 8852ecd ("m68k: mac - Add SWIM floppy support")
Signed-off-by: Finn Thain <fthain@linux-m68k.org>
Reviewed-by: Laurent Vivier <laurent@vivier.eu>
Track 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. Move the heads to the edge of the disk before
ejecting so that recalibration happens quickly.

Fixes: 8852ecd ("m68k: mac - Add SWIM floppy support")
Signed-off-by: Finn Thain <fthain@linux-m68k.org>
Reviewed-by: Laurent Vivier <laurent@vivier.eu>
The 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.

Fixes: 8852ecd ("m68k: mac - Add SWIM floppy support")
Signed-off-by: Finn Thain <fthain@linux-m68k.org>
Reviewed-by: Laurent Vivier <laurent@vivier.eu>
When 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.

Fixes: 8852ecd ("m68k: mac - Add SWIM floppy support")
Signed-off-by: Finn Thain <fthain@linux-m68k.org>
Reviewed-by: Laurent Vivier <laurent@vivier.eu>
Initialize the error result once only. Update the result only after a
successful read.

Signed-off-by: Finn Thain <fthain@linux-m68k.org>
Reviewed-by: Laurent Vivier <laurent@vivier.eu>
After 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.

Fixes: 8852ecd ("m68k: mac - Add SWIM floppy support")
Signed-off-by: Finn Thain <fthain@linux-m68k.org>
Reviewed-by: Laurent Vivier <laurent@vivier.eu>
Clear 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.

Fixes: 8852ecd ("m68k: mac - Add SWIM floppy support")
Signed-off-by: Finn Thain <fthain@linux-m68k.org>
Reviewed-by: Laurent Vivier <laurent@vivier.eu>
If 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.

Fixes: 8852ecd ("m68k: mac - Add SWIM floppy support")
Signed-off-by: Finn Thain <fthain@linux-m68k.org>
Reviewed-by: Laurent Vivier <laurent@vivier.eu>
The 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_loop

Note 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_data
exit_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.

Fixes: 8852ecd ("m68k: mac - Add SWIM floppy support")
Signed-off-by: Finn Thain <fthain@linux-m68k.org>
Reviewed-by: Laurent Vivier <laurent@vivier.eu>
These drives are slow: completing a request can take hundreds of
milliseconds. Delays are managed by disabling interrupts judiciously and
sleeping opportunistically.

As of commit e3896d7 ("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.)

Cc: Omar Sandoval <osandov@fb.com>
Fixes: e3896d7 ("swim: convert to blk-mq")
Signed-off-by: Finn Thain <fthain@linux-m68k.org>
Reviewed-by: Laurent Vivier <laurent@vivier.eu>
Wherever 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.

Signed-off-by: Finn Thain <fthain@linux-m68k.org>
Reviewed-by: Laurent Vivier <laurent@vivier.eu>
Replace 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.

Fixes: 8852ecd ("m68k: mac - Add SWIM floppy support")
Signed-off-by: Finn Thain <fthain@linux-m68k.org>
The 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.

Fixes: 8852ecd ("m68k: mac - Add SWIM floppy support")
Signed-off-by: Finn Thain <fthain@linux-m68k.org>
AFAIK, 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.

Fixes: 8852ecd ("m68k: mac - Add SWIM floppy support")
Signed-off-by: Finn Thain <fthain@linux-m68k.org>
This 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.)

Signed-off-by: Finn Thain <fthain@linux-m68k.org>
floppy_read_sectors() is confusing because variable names seem to conflate
tracks and cylinders. Rename the track 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 18, 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 that, on a 68030
system, re-enabling interrupts after each sector read can result in a full
rotation between sectors (that is a 3 ms wait).

Floppy drivers often implement a track cache, that can be filled in a
single rotation, to solve problems like these. But I think this solution
is much simpler.

For each request, use a sector bitmap to keep a record of sectors read
successfully. Read or retry, as necessary, the requested sectors in
whatever sequence they arrive in. Keep interrupts disabled until the
whole track has passed under the read head.

Fixes: 8852ecd ("m68k: mac - Add SWIM floppy support")
Signed-off-by: Finn Thain <fthain@linux-m68k.org>
If 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.

Signed-off-by: Finn Thain <fthain@linux-m68k.org>
For better readability, initialize the swd backpointer along with the
other floppy_state struct members. No functional change.

Signed-off-by: Finn Thain <fthain@linux-m68k.org>
These documents relate to the IWM, ISM, SWIM 1, 2, 3 and associated
disk drives. No functional change.

Signed-off-by: Finn Thain <fthain@linux-m68k.org>
Also remove the horizontal rule at the end of the macro definitions as
it doesn't any add value, IMO.

Signed-off-by: Finn Thain <fthain@linux-m68k.org>
No functional changes.

Signed-off-by: Finn Thain <fthain@linux-m68k.org>
Define 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.

Signed-off-by: Finn Thain <fthain@linux-m68k.org>
Define local symbols to give some meaning to anonymous constants.
No functional change.

Signed-off-by: Finn Thain <fthain@linux-m68k.org>
These symbols aren't used outside of this file so use local ones.
No functional change.

Signed-off-by: Finn Thain <fthain@linux-m68k.org>
@blktests-ci-block-trial

Copy link
Copy Markdown
Author

Upstream branch: 248951d
series: http://redsun45:8000/project/linux-block/list/?series=355
version: 1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant