* [PATCH 0/1] scsi: arm: Convert acornscsi-io.S to C
@ 2026-10-22 18:36 Breno Rodrigues Alves
2026-10-22 18:36 ` [PATCH] " Breno Rodrigues Alves
` (3 more replies)
0 siblings, 4 replies; 15+ messages in thread
From: Breno Rodrigues Alves @ 2026-10-22 18:36 UTC (permalink / raw)
To: linux
Cc: James.Bottomley, mkp, linux-arm-kernel, linux-scsi, linux-kernel,
Breno Rodrigues Alves
This series contains a single mechanical conversion of the Acorn SCSI
card I/O helpers from ARM assembly to C.
The new acornscsi-io.h carries the prototypes for both helpers and is
included from acornscsi-io.c and acornscsi.c, replacing the
block-scoped extern declarations that the latter used to carry. The
first argument is now typed as (void __iomem *), matching what
acornscsi.c actually passes; the assembly version had no C prototype,
so the original comment declaring that argument as unsigned int was
never enforced.
No functional change is intended for even-length transfers, which is
what all current callers use. Odd-length transfers now no longer lose
the final byte.
Testing:
- userspace unit test mocking readl/writel/writeb verified byte-exact
equivalence with the original assembly for transfer lengths 0, 2,
4, 8, 16, 32, 64, 100, 512 and 1000
- cross-compiled for ARM with arm-linux-gnueabi on rpc_defconfig; the
module links cleanly and nm shows both helpers as global text
symbols
- checkpatch.pl --strict reports 0 errors, 0 warnings, 0 checks
(FILE_PATH_CHANGES ignored: drivers/scsi/arm/ is already covered by
MAINTAINERS)
Breno (1):
scsi: arm: Convert acornscsi-io.S to C
drivers/scsi/arm/acornscsi-io.S | 116 ------------
drivers/scsi/arm/acornscsi-io.c | 170 ++++++++++++++++++++++++
drivers/scsi/arm/acornscsi-io.h | 15 ++++
drivers/scsi/arm/acornscsi.c | 3 +-
4 files changed, 184 insertions(+), 137 deletions(-)
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH] scsi: arm: Convert acornscsi-io.S to C
2026-10-22 18:36 [PATCH 0/1] scsi: arm: Convert acornscsi-io.S to C Breno Rodrigues Alves
@ 2026-10-22 18:36 ` Breno Rodrigues Alves
2026-10-22 18:52 ` [PATCH v2 0/1] " Breno Rodrigues Alves
` (2 subsequent siblings)
3 siblings, 0 replies; 15+ messages in thread
From: Breno Rodrigues Alves @ 2026-10-22 18:36 UTC (permalink / raw)
To: linux
Cc: James.Bottomley, mkp, linux-arm-kernel, linux-scsi, linux-kernel,
Breno Rodrigues Alves
The Acorn SCSI expansion card presents each 16-bit SCSI data halfword
on the low half of a 32-bit card access. The hand-written assembly
in acornscsi-io.S implements the two directions of the data path:
- __acornscsi_in() : packs pairs of 16-bit card reads into 32-bit
memory words
- __acornscsi_out() : expands 16-bit memory halfwords into 32-bit
card writes (duplicating the halfword in both
halves for the bulk of the transfer)
Both routines are simple pointer walks that a modern compiler handles
just as well as hand-written assembly, so replace the .S file with an
equivalent C implementation. The conversion also happens to handle
odd transfer lengths correctly: the original assembly drops the final
odd byte.
The C version takes its arguments as (void __iomem *, char *, int),
matching the prototype actually used by acornscsi.c. The assembly
version had no C prototype, so the original comment declaring the
first argument as unsigned int was never enforced; using the caller's
type removes that latent inconsistency and lets the MMIO accessors
do the right thing on any architecture.
No functional change is intended for the normal case of even-length
transfers, which is what all current callers of these helpers use.
No Makefile change is required: acornscsi_mod-objs already lists
acornscsi-io.o, and the build system will pick up the new .c file in
preference to the removed .S file.
Signed-off-by: Breno Rodrigues Alves <breno3011alves@gmail.com>
---
drivers/scsi/arm/acornscsi-io.S | 135 -------------------------
drivers/scsi/arm/acornscsi-io.c | 168 ++++++++++++++++++++++++++++++++
drivers/scsi/arm/acornscsi-io.h | 14 +++
drivers/scsi/arm/acornscsi.c | 4 +-
4 files changed, 184 insertions(+), 137 deletions(-)
delete mode 100644 drivers/scsi/arm/acornscsi-io.S
create mode 100644 drivers/scsi/arm/acornscsi-io.c
create mode 100644 drivers/scsi/arm/acornscsi-io.h
diff --git a/drivers/scsi/arm/acornscsi-io.S b/drivers/scsi/arm/acornscsi-io.S
deleted file mode 100644
index fdd7237bb..000000000
--- a/drivers/scsi/arm/acornscsi-io.S
+++ /dev/null
@@ -1,135 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * linux/drivers/acorn/scsi/acornscsi-io.S: Acorn SCSI card IO
- */
-#include <linux/linkage.h>
-
-#include <asm/assembler.h>
-#include <mach/hardware.h>
-
-#if defined(__APCS_32__)
-#define LOADREGS(t,r,l...) ldm##t r, l
-#elif defined(__APCS_26__)
-#define LOADREGS(t,r,l...) ldm##t r, l##^
-#endif
-
-@ Purpose: transfer a block of data from the acorn scsi card to memory
-@ Proto : void acornscsi_in(unsigned int addr_start, char *buffer, int length)
-@ Returns: nothing
-
- .align
-ENTRY(__acornscsi_in)
- stmfd sp!, {r4 - r7, lr}
- bic r0, r0, #3
- mov lr, #0xff
- orr lr, lr, #0xff00
-acornscsi_in16lp:
- subs r2, r2, #16
- bmi acornscsi_in8
- ldmia r0!, {r3, r4, r5, r6}
- and r3, r3, lr
- orr r3, r3, r4, lsl #16
- and r4, r5, lr
- orr r4, r4, r6, lsl #16
- ldmia r0!, {r5, r6, r7, ip}
- and r5, r5, lr
- orr r5, r5, r6, lsl #16
- and r6, r7, lr
- orr r6, r6, ip, lsl #16
- stmia r1!, {r3 - r6}
- bne acornscsi_in16lp
- LOADREGS(fd, sp!, {r4 - r7, pc})
-
-acornscsi_in8: adds r2, r2, #8
- bmi acornscsi_in4
- ldmia r0!, {r3, r4, r5, r6}
- and r3, r3, lr
- orr r3, r3, r4, lsl #16
- and r4, r5, lr
- orr r4, r4, r6, lsl #16
- stmia r1!, {r3 - r4}
- LOADREGS(eqfd, sp!, {r4 - r7, pc})
- sub r2, r2, #8
-
-acornscsi_in4: adds r2, r2, #4
- bmi acornscsi_in2
- ldmia r0!, {r3, r4}
- and r3, r3, lr
- orr r3, r3, r4, lsl #16
- str r3, [r1], #4
- LOADREGS(eqfd, sp!, {r4 - r7, pc})
- sub r2, r2, #4
-
-acornscsi_in2: adds r2, r2, #2
- ldr r3, [r0], #4
- and r3, r3, lr
- strb r3, [r1], #1
- mov r3, r3, lsr #8
- strplb r3, [r1], #1
- LOADREGS(fd, sp!, {r4 - r7, pc})
-
-@ Purpose: transfer a block of data from memory to the acorn scsi card
-@ Proto : void acornscsi_in(unsigned int addr_start, char *buffer, int length)
-@ Returns: nothing
-
-ENTRY(__acornscsi_out)
- stmfd sp!, {r4 - r6, lr}
- bic r0, r0, #3
-acornscsi_out16lp:
- subs r2, r2, #16
- bmi acornscsi_out8
- ldmia r1!, {r4, r6, ip, lr}
- mov r3, r4, lsl #16
- orr r3, r3, r3, lsr #16
- mov r4, r4, lsr #16
- orr r4, r4, r4, lsl #16
- mov r5, r6, lsl #16
- orr r5, r5, r5, lsr #16
- mov r6, r6, lsr #16
- orr r6, r6, r6, lsl #16
- stmia r0!, {r3, r4, r5, r6}
- mov r3, ip, lsl #16
- orr r3, r3, r3, lsr #16
- mov r4, ip, lsr #16
- orr r4, r4, r4, lsl #16
- mov ip, lr, lsl #16
- orr ip, ip, ip, lsr #16
- mov lr, lr, lsr #16
- orr lr, lr, lr, lsl #16
- stmia r0!, {r3, r4, ip, lr}
- bne acornscsi_out16lp
- LOADREGS(fd, sp!, {r4 - r6, pc})
-
-acornscsi_out8: adds r2, r2, #8
- bmi acornscsi_out4
- ldmia r1!, {r4, r6}
- mov r3, r4, lsl #16
- orr r3, r3, r3, lsr #16
- mov r4, r4, lsr #16
- orr r4, r4, r4, lsl #16
- mov r5, r6, lsl #16
- orr r5, r5, r5, lsr #16
- mov r6, r6, lsr #16
- orr r6, r6, r6, lsl #16
- stmia r0!, {r3, r4, r5, r6}
- LOADREGS(eqfd, sp!, {r4 - r6, pc})
-
- sub r2, r2, #8
-acornscsi_out4: adds r2, r2, #4
- bmi acornscsi_out2
- ldr r4, [r1], #4
- mov r3, r4, lsl #16
- orr r3, r3, r3, lsr #16
- mov r4, r4, lsr #16
- orr r4, r4, r4, lsl #16
- stmia r0!, {r3, r4}
- LOADREGS(eqfd, sp!, {r4 - r6, pc})
-
- sub r2, r2, #4
-acornscsi_out2: adds r2, r2, #2
- ldr r3, [r1], #2
- strb r3, [r0], #1
- mov r3, r3, lsr #8
- strplb r3, [r0], #1
- LOADREGS(fd, sp!, {r4 - r6, pc})
-
diff --git a/drivers/scsi/arm/acornscsi-io.c b/drivers/scsi/arm/acornscsi-io.c
new file mode 100644
index 000000000..ad16d984d
--- /dev/null
+++ b/drivers/scsi/arm/acornscsi-io.c
@@ -0,0 +1,168 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Acorn SCSI card I/O
+ *
+ * The Acorn SCSI expansion card exposes each 16-bit SCSI data
+ * halfword on the low half of a 32-bit card access:
+ *
+ * - on reads, one 16-bit value is returned per 32-bit card load;
+ * consecutive card reads are packed into 32-bit memory words
+ *
+ * - on writes, a 16-bit memory halfword is expanded into a 32-bit
+ * card word. The original assembly duplicates the halfword into
+ * both halves of the card word for the bulk of the transfer and
+ * writes only the low 16 bits of the trailing partial word,
+ * since the hardware samples only the low half.
+ */
+
+#include <linux/align.h>
+#include <linux/io.h>
+#include <linux/types.h>
+#include "acornscsi-io.h"
+
+/*
+ * Transfer a block of data from the Acorn SCSI card into memory.
+ *
+ * The card address is rounded down to a 32-bit boundary. Each
+ * 32-bit read from the card yields one valid 16-bit halfword in
+ * the low half; pairs of card reads are packed into single 32-bit
+ * memory words.
+ */
+void __acornscsi_in(void __iomem *addr, char *buffer, int length)
+{
+ const u32 __iomem *card = PTR_ALIGN((const u32 __iomem *)addr, 4);
+ u8 *buf = (u8 *)buffer;
+
+ while (length >= 16) {
+ u32 w0 = readl(card + 0), w1 = readl(card + 1);
+ u32 w2 = readl(card + 2), w3 = readl(card + 3);
+ u32 w4 = readl(card + 4), w5 = readl(card + 5);
+ u32 w6 = readl(card + 6), w7 = readl(card + 7);
+
+ card += 8;
+
+ ((u32 *)buf)[0] = (w0 & 0xffff) | (w1 << 16);
+ ((u32 *)buf)[1] = (w2 & 0xffff) | (w3 << 16);
+ ((u32 *)buf)[2] = (w4 & 0xffff) | (w5 << 16);
+ ((u32 *)buf)[3] = (w6 & 0xffff) | (w7 << 16);
+
+ buf += 16;
+ length -= 16;
+ }
+
+ while (length >= 8) {
+ u32 w0 = readl(card + 0), w1 = readl(card + 1);
+ u32 w2 = readl(card + 2), w3 = readl(card + 3);
+
+ card += 4;
+
+ ((u32 *)buf)[0] = (w0 & 0xffff) | (w1 << 16);
+ ((u32 *)buf)[1] = (w2 & 0xffff) | (w3 << 16);
+
+ buf += 8;
+ length -= 8;
+ }
+
+ while (length >= 4) {
+ u32 w0 = readl(card + 0), w1 = readl(card + 1);
+
+ card += 2;
+
+ ((u32 *)buf)[0] = (w0 & 0xffff) | (w1 << 16);
+
+ buf += 4;
+ length -= 4;
+ }
+
+ while (length >= 2) {
+ u32 w0 = readl(card);
+
+ card++;
+
+ buf[0] = w0 & 0xff;
+ buf[1] = (w0 >> 8) & 0xff;
+
+ buf += 2;
+ length -= 2;
+ }
+
+ if (length >= 1) {
+ u32 w0 = readl(card);
+
+ buf[0] = w0 & 0xff;
+ }
+}
+
+/*
+ * Transfer a block of data from memory to the Acorn SCSI card.
+ *
+ * Each 16-bit value read from memory is expanded into a 32-bit card
+ * word, with the halfword duplicated in both halves (matching the
+ * original assembly). For the trailing partial halfword the card
+ * only samples the low 16 bits, so two byte stores are sufficient.
+ */
+void __acornscsi_out(void __iomem *addr, char *buffer, int length)
+{
+ u32 __iomem *card = PTR_ALIGN((u32 __iomem *)addr, 4);
+ const u8 *buf = (const u8 *)buffer;
+
+ while (length >= 16) {
+ u32 v0 = ((const u32 *)buf)[0];
+ u32 v1 = ((const u32 *)buf)[1];
+ u32 v2 = ((const u32 *)buf)[2];
+ u32 v3 = ((const u32 *)buf)[3];
+
+ writel((v0 & 0xffff) | ((v0 & 0xffff) << 16), card + 0);
+ writel((v0 >> 16) | ((v0 >> 16) << 16), card + 1);
+ writel((v1 & 0xffff) | ((v1 & 0xffff) << 16), card + 2);
+ writel((v1 >> 16) | ((v1 >> 16) << 16), card + 3);
+ writel((v2 & 0xffff) | ((v2 & 0xffff) << 16), card + 4);
+ writel((v2 >> 16) | ((v2 >> 16) << 16), card + 5);
+ writel((v3 & 0xffff) | ((v3 & 0xffff) << 16), card + 6);
+ writel((v3 >> 16) | ((v3 >> 16) << 16), card + 7);
+
+ card += 8;
+ buf += 16;
+ length -= 16;
+ }
+
+ while (length >= 8) {
+ u32 v0 = ((const u32 *)buf)[0];
+ u32 v1 = ((const u32 *)buf)[1];
+
+ writel((v0 & 0xffff) | ((v0 & 0xffff) << 16), card + 0);
+ writel((v0 >> 16) | ((v0 >> 16) << 16), card + 1);
+ writel((v1 & 0xffff) | ((v1 & 0xffff) << 16), card + 2);
+ writel((v1 >> 16) | ((v1 >> 16) << 16), card + 3);
+
+ card += 4;
+ buf += 8;
+ length -= 8;
+ }
+
+ while (length >= 4) {
+ u32 v0 = ((const u32 *)buf)[0];
+
+ writel((v0 & 0xffff) | ((v0 & 0xffff) << 16), card + 0);
+ writel((v0 >> 16) | ((v0 >> 16) << 16), card + 1);
+
+ card += 2;
+ buf += 4;
+ length -= 4;
+ }
+
+ while (length >= 2) {
+ u16 v = *(const u16 *)buf;
+ void __iomem *cp = (void __iomem *)card;
+
+ writeb(v & 0xff, cp);
+ writeb(v >> 8, cp + 1);
+
+ card = (u32 __iomem *)(cp + 2);
+ buf += 2;
+ length -= 2;
+ }
+
+ if (length >= 1)
+ writeb(*buf, (void __iomem *)card);
+}
diff --git a/drivers/scsi/arm/acornscsi-io.h b/drivers/scsi/arm/acornscsi-io.h
new file mode 100644
index 000000000..64acfb8bb
--- /dev/null
+++ b/drivers/scsi/arm/acornscsi-io.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Acorn SCSI card I/O
+ */
+
+#ifndef _ACORNSCSI_IO_H
+#define _ACORNSCSI_IO_H
+
+#include <linux/io.h>
+
+void __acornscsi_in(void __iomem *addr, char *buffer, int length);
+void __acornscsi_out(void __iomem *addr, char *buffer, int length);
+
+#endif /* _ACORNSCSI_IO_H */
diff --git a/drivers/scsi/arm/acornscsi.c b/drivers/scsi/arm/acornscsi.c
index 79d7d7336..263a1bf24 100644
--- a/drivers/scsi/arm/acornscsi.c
+++ b/drivers/scsi/arm/acornscsi.c
@@ -140,6 +140,8 @@
#include <scsi/scsicam.h>
+#include "acornscsi-io.h"
+
#define VER_MAJOR 2
#define VER_MINOR 0
#define VER_PATCH 6
@@ -886,7 +888,6 @@ static
void acornscsi_data_read(AS_Host *host, char *ptr,
unsigned int start_addr, unsigned int length)
{
- extern void __acornscsi_in(void __iomem *, char *buf, int len);
unsigned int page, offset, len = length;
page = (start_addr >> 12);
@@ -931,7 +932,6 @@ static
void acornscsi_data_write(AS_Host *host, char *ptr,
unsigned int start_addr, unsigned int length)
{
- extern void __acornscsi_out(void __iomem *, char *buf, int len);
unsigned int page, offset, len = length;
page = (start_addr >> 12);
--
2.56.0
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v2 0/1] scsi: arm: Convert acornscsi-io.S to C
2026-10-22 18:36 [PATCH 0/1] scsi: arm: Convert acornscsi-io.S to C Breno Rodrigues Alves
2026-10-22 18:36 ` [PATCH] " Breno Rodrigues Alves
@ 2026-10-22 18:52 ` Breno Rodrigues Alves
2026-10-22 18:52 ` [PATCH v2] " Breno Rodrigues Alves
2026-10-22 20:30 ` [PATCH v3] " Breno Rodrigues Alves
2026-10-22 21:02 ` [PATCH v4] " Breno Rodrigues Alves
3 siblings, 1 reply; 15+ messages in thread
From: Breno Rodrigues Alves @ 2026-10-22 18:52 UTC (permalink / raw)
To: linux
Cc: James.Bottomley, mkp, linux-arm-kernel, linux-scsi, linux-kernel,
Breno Rodrigues Alves
v2 addresses two issues raised by the sashiko bot:
- PTR_ALIGN() rounds up, but the assembly's "bic r0, r0, #3"
rounds down. Use PTR_ALIGN_DOWN() to match, otherwise an odd
on-card offset shifts the entire transfer by one 16-bit halfword
and can walk past the 4 KiB card window.
- In __acornscsi_out(), the trailing byte loop advanced by 2 bytes
into the unused upper half of the current card word. Advance by
a full 32-bit word (card++) so the trailing byte lands in the
low half of the next card word, which is where the hardware
actually samples it.
No functional change for even-length transfers. The odd-length
behaviour is now correct for both directions.
Breno (1):
scsi: arm: Convert acornscsi-io.S to C
drivers/scsi/arm/acornscsi-io.S | 116 ------------
drivers/scsi/arm/acornscsi-io.c | 168 ++++++++++++++++++++++
drivers/scsi/arm/acornscsi-io.h | 15 +++
drivers/scsi/arm/acornscsi.c | 3 +-
4 files changed, 184 insertions(+), 137 deletions(-)
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v2] scsi: arm: Convert acornscsi-io.S to C
2026-10-22 18:52 ` [PATCH v2 0/1] " Breno Rodrigues Alves
@ 2026-10-22 18:52 ` Breno Rodrigues Alves
2026-10-22 19:22 ` Bart Van Assche
0 siblings, 1 reply; 15+ messages in thread
From: Breno Rodrigues Alves @ 2026-10-22 18:52 UTC (permalink / raw)
To: linux
Cc: James.Bottomley, mkp, linux-arm-kernel, linux-scsi, linux-kernel,
Breno Rodrigues Alves
The Acorn SCSI expansion card presents each 16-bit SCSI data halfword
on the low half of a 32-bit card access. The hand-written assembly
in acornscsi-io.S implements the two directions of the data path:
- __acornscsi_in() : packs pairs of 16-bit card reads into 32-bit
memory words
- __acornscsi_out() : expands 16-bit memory halfwords into 32-bit
card writes (duplicating the halfword in both
halves for the bulk of the transfer)
Both routines are simple pointer walks that a modern compiler handles
just as well as hand-written assembly, so replace the .S file with an
equivalent C implementation.
The C version takes its arguments as (void __iomem *, char *, int),
matching the prototype actually used by acornscsi.c. The assembly
version had no C prototype, so the original comment declaring the
first argument as unsigned int was never enforced; using the caller's
type removes that latent inconsistency and lets the MMIO accessors
do the right thing on any architecture.
Note on odd transfer lengths: the original assembly dropped the final
odd byte when the tail reached the two-byte handler, leaving the byte
unwritten (out) or unread (in). The C version advances the MMIO
pointer by a full 32-bit card word in the trailing loop, so the
remaining odd byte is transferred through the low half of the next
card word, which is where the hardware actually samples it. Callers
in acornscsi.c pass even lengths in practice, so this is a latent-bug
fix, not a behaviour change that affects existing systems.
No Makefile change is required: acornscsi_mod-objs already lists
acornscsi-io.o, and the build system will pick up the new .c file in
preference to the removed .S file.
Signed-off-by: Breno Rodrigues Alves <breno3011alves@gmail.com>
---
drivers/scsi/arm/acornscsi-io.S | 135 -------------------------
drivers/scsi/arm/acornscsi-io.c | 168 ++++++++++++++++++++++++++++++++
drivers/scsi/arm/acornscsi-io.h | 14 +++
drivers/scsi/arm/acornscsi.c | 4 +-
4 files changed, 184 insertions(+), 137 deletions(-)
delete mode 100644 drivers/scsi/arm/acornscsi-io.S
create mode 100644 drivers/scsi/arm/acornscsi-io.c
create mode 100644 drivers/scsi/arm/acornscsi-io.h
diff --git a/drivers/scsi/arm/acornscsi-io.S b/drivers/scsi/arm/acornscsi-io.S
deleted file mode 100644
index fdd7237bb..000000000
--- a/drivers/scsi/arm/acornscsi-io.S
+++ /dev/null
@@ -1,135 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * linux/drivers/acorn/scsi/acornscsi-io.S: Acorn SCSI card IO
- */
-#include <linux/linkage.h>
-
-#include <asm/assembler.h>
-#include <mach/hardware.h>
-
-#if defined(__APCS_32__)
-#define LOADREGS(t,r,l...) ldm##t r, l
-#elif defined(__APCS_26__)
-#define LOADREGS(t,r,l...) ldm##t r, l##^
-#endif
-
-@ Purpose: transfer a block of data from the acorn scsi card to memory
-@ Proto : void acornscsi_in(unsigned int addr_start, char *buffer, int length)
-@ Returns: nothing
-
- .align
-ENTRY(__acornscsi_in)
- stmfd sp!, {r4 - r7, lr}
- bic r0, r0, #3
- mov lr, #0xff
- orr lr, lr, #0xff00
-acornscsi_in16lp:
- subs r2, r2, #16
- bmi acornscsi_in8
- ldmia r0!, {r3, r4, r5, r6}
- and r3, r3, lr
- orr r3, r3, r4, lsl #16
- and r4, r5, lr
- orr r4, r4, r6, lsl #16
- ldmia r0!, {r5, r6, r7, ip}
- and r5, r5, lr
- orr r5, r5, r6, lsl #16
- and r6, r7, lr
- orr r6, r6, ip, lsl #16
- stmia r1!, {r3 - r6}
- bne acornscsi_in16lp
- LOADREGS(fd, sp!, {r4 - r7, pc})
-
-acornscsi_in8: adds r2, r2, #8
- bmi acornscsi_in4
- ldmia r0!, {r3, r4, r5, r6}
- and r3, r3, lr
- orr r3, r3, r4, lsl #16
- and r4, r5, lr
- orr r4, r4, r6, lsl #16
- stmia r1!, {r3 - r4}
- LOADREGS(eqfd, sp!, {r4 - r7, pc})
- sub r2, r2, #8
-
-acornscsi_in4: adds r2, r2, #4
- bmi acornscsi_in2
- ldmia r0!, {r3, r4}
- and r3, r3, lr
- orr r3, r3, r4, lsl #16
- str r3, [r1], #4
- LOADREGS(eqfd, sp!, {r4 - r7, pc})
- sub r2, r2, #4
-
-acornscsi_in2: adds r2, r2, #2
- ldr r3, [r0], #4
- and r3, r3, lr
- strb r3, [r1], #1
- mov r3, r3, lsr #8
- strplb r3, [r1], #1
- LOADREGS(fd, sp!, {r4 - r7, pc})
-
-@ Purpose: transfer a block of data from memory to the acorn scsi card
-@ Proto : void acornscsi_in(unsigned int addr_start, char *buffer, int length)
-@ Returns: nothing
-
-ENTRY(__acornscsi_out)
- stmfd sp!, {r4 - r6, lr}
- bic r0, r0, #3
-acornscsi_out16lp:
- subs r2, r2, #16
- bmi acornscsi_out8
- ldmia r1!, {r4, r6, ip, lr}
- mov r3, r4, lsl #16
- orr r3, r3, r3, lsr #16
- mov r4, r4, lsr #16
- orr r4, r4, r4, lsl #16
- mov r5, r6, lsl #16
- orr r5, r5, r5, lsr #16
- mov r6, r6, lsr #16
- orr r6, r6, r6, lsl #16
- stmia r0!, {r3, r4, r5, r6}
- mov r3, ip, lsl #16
- orr r3, r3, r3, lsr #16
- mov r4, ip, lsr #16
- orr r4, r4, r4, lsl #16
- mov ip, lr, lsl #16
- orr ip, ip, ip, lsr #16
- mov lr, lr, lsr #16
- orr lr, lr, lr, lsl #16
- stmia r0!, {r3, r4, ip, lr}
- bne acornscsi_out16lp
- LOADREGS(fd, sp!, {r4 - r6, pc})
-
-acornscsi_out8: adds r2, r2, #8
- bmi acornscsi_out4
- ldmia r1!, {r4, r6}
- mov r3, r4, lsl #16
- orr r3, r3, r3, lsr #16
- mov r4, r4, lsr #16
- orr r4, r4, r4, lsl #16
- mov r5, r6, lsl #16
- orr r5, r5, r5, lsr #16
- mov r6, r6, lsr #16
- orr r6, r6, r6, lsl #16
- stmia r0!, {r3, r4, r5, r6}
- LOADREGS(eqfd, sp!, {r4 - r6, pc})
-
- sub r2, r2, #8
-acornscsi_out4: adds r2, r2, #4
- bmi acornscsi_out2
- ldr r4, [r1], #4
- mov r3, r4, lsl #16
- orr r3, r3, r3, lsr #16
- mov r4, r4, lsr #16
- orr r4, r4, r4, lsl #16
- stmia r0!, {r3, r4}
- LOADREGS(eqfd, sp!, {r4 - r6, pc})
-
- sub r2, r2, #4
-acornscsi_out2: adds r2, r2, #2
- ldr r3, [r1], #2
- strb r3, [r0], #1
- mov r3, r3, lsr #8
- strplb r3, [r0], #1
- LOADREGS(fd, sp!, {r4 - r6, pc})
-
diff --git a/drivers/scsi/arm/acornscsi-io.c b/drivers/scsi/arm/acornscsi-io.c
new file mode 100644
index 000000000..d882441ea
--- /dev/null
+++ b/drivers/scsi/arm/acornscsi-io.c
@@ -0,0 +1,168 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Acorn SCSI card I/O
+ *
+ * The Acorn SCSI expansion card exposes each 16-bit SCSI data
+ * halfword on the low half of a 32-bit card access:
+ *
+ * - on reads, one 16-bit value is returned per 32-bit card load;
+ * consecutive card reads are packed into 32-bit memory words
+ *
+ * - on writes, a 16-bit memory halfword is expanded into a 32-bit
+ * card word. The original assembly duplicates the halfword into
+ * both halves of the card word for the bulk of the transfer and
+ * writes only the low 16 bits of the trailing partial word,
+ * since the hardware samples only the low half.
+ */
+
+#include <linux/align.h>
+#include <linux/io.h>
+#include <linux/types.h>
+#include "acornscsi-io.h"
+
+/*
+ * Transfer a block of data from the Acorn SCSI card into memory.
+ *
+ * The card address is rounded down to a 32-bit boundary. Each
+ * 32-bit read from the card yields one valid 16-bit halfword in
+ * the low half; pairs of card reads are packed into single 32-bit
+ * memory words.
+ */
+void __acornscsi_in(void __iomem *addr, char *buffer, int length)
+{
+ const u32 __iomem *card = PTR_ALIGN_DOWN((const u32 __iomem *)addr, 4);
+ u8 *buf = (u8 *)buffer;
+
+ while (length >= 16) {
+ u32 w0 = readl(card + 0), w1 = readl(card + 1);
+ u32 w2 = readl(card + 2), w3 = readl(card + 3);
+ u32 w4 = readl(card + 4), w5 = readl(card + 5);
+ u32 w6 = readl(card + 6), w7 = readl(card + 7);
+
+ card += 8;
+
+ ((u32 *)buf)[0] = (w0 & 0xffff) | (w1 << 16);
+ ((u32 *)buf)[1] = (w2 & 0xffff) | (w3 << 16);
+ ((u32 *)buf)[2] = (w4 & 0xffff) | (w5 << 16);
+ ((u32 *)buf)[3] = (w6 & 0xffff) | (w7 << 16);
+
+ buf += 16;
+ length -= 16;
+ }
+
+ while (length >= 8) {
+ u32 w0 = readl(card + 0), w1 = readl(card + 1);
+ u32 w2 = readl(card + 2), w3 = readl(card + 3);
+
+ card += 4;
+
+ ((u32 *)buf)[0] = (w0 & 0xffff) | (w1 << 16);
+ ((u32 *)buf)[1] = (w2 & 0xffff) | (w3 << 16);
+
+ buf += 8;
+ length -= 8;
+ }
+
+ while (length >= 4) {
+ u32 w0 = readl(card + 0), w1 = readl(card + 1);
+
+ card += 2;
+
+ ((u32 *)buf)[0] = (w0 & 0xffff) | (w1 << 16);
+
+ buf += 4;
+ length -= 4;
+ }
+
+ while (length >= 2) {
+ u32 w0 = readl(card);
+
+ card++;
+
+ buf[0] = w0 & 0xff;
+ buf[1] = (w0 >> 8) & 0xff;
+
+ buf += 2;
+ length -= 2;
+ }
+
+ if (length >= 1) {
+ u32 w0 = readl(card);
+
+ buf[0] = w0 & 0xff;
+ }
+}
+
+/*
+ * Transfer a block of data from memory to the Acorn SCSI card.
+ *
+ * Each 16-bit value read from memory is expanded into a 32-bit card
+ * word, with the halfword duplicated in both halves (matching the
+ * original assembly). For the trailing partial halfword the card
+ * only samples the low 16 bits, so two byte stores are sufficient.
+ */
+void __acornscsi_out(void __iomem *addr, char *buffer, int length)
+{
+ u32 __iomem *card = PTR_ALIGN_DOWN((u32 __iomem *)addr, 4);
+ const u8 *buf = (const u8 *)buffer;
+
+ while (length >= 16) {
+ u32 v0 = ((const u32 *)buf)[0];
+ u32 v1 = ((const u32 *)buf)[1];
+ u32 v2 = ((const u32 *)buf)[2];
+ u32 v3 = ((const u32 *)buf)[3];
+
+ writel((v0 & 0xffff) | ((v0 & 0xffff) << 16), card + 0);
+ writel((v0 >> 16) | ((v0 >> 16) << 16), card + 1);
+ writel((v1 & 0xffff) | ((v1 & 0xffff) << 16), card + 2);
+ writel((v1 >> 16) | ((v1 >> 16) << 16), card + 3);
+ writel((v2 & 0xffff) | ((v2 & 0xffff) << 16), card + 4);
+ writel((v2 >> 16) | ((v2 >> 16) << 16), card + 5);
+ writel((v3 & 0xffff) | ((v3 & 0xffff) << 16), card + 6);
+ writel((v3 >> 16) | ((v3 >> 16) << 16), card + 7);
+
+ card += 8;
+ buf += 16;
+ length -= 16;
+ }
+
+ while (length >= 8) {
+ u32 v0 = ((const u32 *)buf)[0];
+ u32 v1 = ((const u32 *)buf)[1];
+
+ writel((v0 & 0xffff) | ((v0 & 0xffff) << 16), card + 0);
+ writel((v0 >> 16) | ((v0 >> 16) << 16), card + 1);
+ writel((v1 & 0xffff) | ((v1 & 0xffff) << 16), card + 2);
+ writel((v1 >> 16) | ((v1 >> 16) << 16), card + 3);
+
+ card += 4;
+ buf += 8;
+ length -= 8;
+ }
+
+ while (length >= 4) {
+ u32 v0 = ((const u32 *)buf)[0];
+
+ writel((v0 & 0xffff) | ((v0 & 0xffff) << 16), card + 0);
+ writel((v0 >> 16) | ((v0 >> 16) << 16), card + 1);
+
+ card += 2;
+ buf += 4;
+ length -= 4;
+ }
+
+ while (length >= 2) {
+ u16 v = *(const u16 *)buf;
+ void __iomem *cp = (void __iomem *)card;
+
+ writeb(v & 0xff, cp);
+ writeb(v >> 8, cp + 1);
+
+ card++;
+ buf += 2;
+ length -= 2;
+ }
+
+ if (length >= 1)
+ writeb(*buf, (void __iomem *)card);
+}
diff --git a/drivers/scsi/arm/acornscsi-io.h b/drivers/scsi/arm/acornscsi-io.h
new file mode 100644
index 000000000..64acfb8bb
--- /dev/null
+++ b/drivers/scsi/arm/acornscsi-io.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Acorn SCSI card I/O
+ */
+
+#ifndef _ACORNSCSI_IO_H
+#define _ACORNSCSI_IO_H
+
+#include <linux/io.h>
+
+void __acornscsi_in(void __iomem *addr, char *buffer, int length);
+void __acornscsi_out(void __iomem *addr, char *buffer, int length);
+
+#endif /* _ACORNSCSI_IO_H */
diff --git a/drivers/scsi/arm/acornscsi.c b/drivers/scsi/arm/acornscsi.c
index 79d7d7336..263a1bf24 100644
--- a/drivers/scsi/arm/acornscsi.c
+++ b/drivers/scsi/arm/acornscsi.c
@@ -140,6 +140,8 @@
#include <scsi/scsicam.h>
+#include "acornscsi-io.h"
+
#define VER_MAJOR 2
#define VER_MINOR 0
#define VER_PATCH 6
@@ -886,7 +888,6 @@ static
void acornscsi_data_read(AS_Host *host, char *ptr,
unsigned int start_addr, unsigned int length)
{
- extern void __acornscsi_in(void __iomem *, char *buf, int len);
unsigned int page, offset, len = length;
page = (start_addr >> 12);
@@ -931,7 +932,6 @@ static
void acornscsi_data_write(AS_Host *host, char *ptr,
unsigned int start_addr, unsigned int length)
{
- extern void __acornscsi_out(void __iomem *, char *buf, int len);
unsigned int page, offset, len = length;
page = (start_addr >> 12);
--
2.56.0
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v2] scsi: arm: Convert acornscsi-io.S to C
2026-10-22 18:52 ` [PATCH v2] " Breno Rodrigues Alves
@ 2026-10-22 19:22 ` Bart Van Assche
2026-10-22 20:30 ` Breno Rodrigues Alves
2026-10-22 21:02 ` Breno Rodrigues Alves
0 siblings, 2 replies; 15+ messages in thread
From: Bart Van Assche @ 2026-10-22 19:22 UTC (permalink / raw)
To: Breno Rodrigues Alves, linux
Cc: James.Bottomley, mkp, linux-arm-kernel, linux-scsi, linux-kernel
On 10/22/26 11:52 AM, Breno Rodrigues Alves wrote:
> + while (length >= 16) {
> + u32 w0 = readl(card + 0), w1 = readl(card + 1);
> + u32 w2 = readl(card + 2), w3 = readl(card + 3);
> + u32 w4 = readl(card + 4), w5 = readl(card + 5);
> + u32 w6 = readl(card + 6), w7 = readl(card + 7);
> +
> + card += 8;
> +
> + ((u32 *)buf)[0] = (w0 & 0xffff) | (w1 << 16);
> + ((u32 *)buf)[1] = (w2 & 0xffff) | (w3 << 16);
> + ((u32 *)buf)[2] = (w4 & 0xffff) | (w5 << 16);
> + ((u32 *)buf)[3] = (w6 & 0xffff) | (w7 << 16);
> +
> + buf += 16;
> + length -= 16;
> + }
Why eight readl() calls instead of a loop? Additionally, please use
put_unaligned_be32() instead of open-coding it. Is the code below
equivalent to the loop above?
while (length >= 16) {
for (int i = 0; i < 4; i++) {
u16 w0 = readl(card + 0);
u32 w1 = readl(card + 1);
put_unaligned_be32(w0 | (w1 << 16), buf);
card += 2;
buf += 4;
}
length -= 16;
}
Thanks,
Bart.
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v2] scsi: arm: Convert acornscsi-io.S to C
2026-10-22 19:22 ` Bart Van Assche
@ 2026-10-22 20:30 ` Breno Rodrigues Alves
2026-10-22 21:02 ` Breno Rodrigues Alves
1 sibling, 0 replies; 15+ messages in thread
From: Breno Rodrigues Alves @ 2026-10-22 20:30 UTC (permalink / raw)
To: Bart Van Assche
Cc: linux, James.Bottomley, mkp, linux-arm-kernel, linux-scsi,
linux-kernel, Breno Rodrigues Alves
Hi Bart,
Thanks for the review.
> Why eight readl() calls instead of a loop?
The eight readl() calls mirror the assembly's unrolled 16-byte body
(4x ldmia + 4x stmia over 16 bytes). Collapsed into a loop in v3 —
the compiler unrolls it for the aligned case.
> Additionally, please use put_unaligned_be32() instead of open-coding
> it. Is the code below equivalent to the loop above?
Almost, but not quite — two subtleties:
1. w0 is declared as u16, which truncates the readl() result. It
happens to work because the compose step only uses the low 16
bits, but u32 is less surprising. Fixed in v3.
2. put_unaligned_be32() would byte-swap relative to the original
assembly. The asm did a plain 32-bit store:
and r3, r3, lr ; r3 = w0 & 0xffff
orr r3, r3, r4, lsl #16 ; r3 |= w1 << 16
str r3, [r1] ; plain 32-bit store
On little-endian ARM that writes [w0_lo, w0_hi, w1_lo, w1_hi],
i.e. put_unaligned_le32() semantics. put_unaligned_be32()
writes [w1_hi, w1_lo, w0_hi, w0_lo] — reversed.
acornscsi_data_read() feeds SCSI payload bytes verbatim into the
upper layer, so that reversal would corrupt the transferred data.
v3 uses put_unaligned_le32() / get_unaligned_le32() / le16().
Note: <asm/unaligned.h> was removed in 6.12; the include is now
<linux/unaligned.h>.
v3 follows as a reply to this message. The 16-, 8- and 4-byte bodies
are now a single 4-byte loop per direction.
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v3] scsi: arm: Convert acornscsi-io.S to C
2026-10-22 18:36 [PATCH 0/1] scsi: arm: Convert acornscsi-io.S to C Breno Rodrigues Alves
2026-10-22 18:36 ` [PATCH] " Breno Rodrigues Alves
2026-10-22 18:52 ` [PATCH v2 0/1] " Breno Rodrigues Alves
@ 2026-10-22 20:30 ` Breno Rodrigues Alves
2026-10-22 20:49 ` Bart Van Assche
2026-10-22 21:02 ` [PATCH v4] " Breno Rodrigues Alves
3 siblings, 1 reply; 15+ messages in thread
From: Breno Rodrigues Alves @ 2026-10-22 20:30 UTC (permalink / raw)
To: linux
Cc: Bart Van Assche, James.Bottomley, mkp, linux-arm-kernel,
linux-scsi, linux-kernel, Breno Rodrigues Alves
The Acorn SCSI expansion card presents each 16-bit SCSI data halfword
on the low half of a 32-bit card access. The hand-written assembly
in acornscsi-io.S implements the two directions of the data path:
- __acornscsi_in() : packs pairs of 16-bit card reads into 32-bit
memory words
- __acornscsi_out() : expands 16-bit memory halfwords into 32-bit
card writes (duplicating the halfword in both
halves for the bulk of the transfer)
Both routines are simple pointer walks that a modern compiler handles
just as well as hand-written assembly, so replace the .S file with an
equivalent C implementation.
The C version takes its arguments as (void __iomem *, char *, int),
matching the prototype actually used by acornscsi.c. The assembly
version had no C prototype, so the original comment declaring the
first argument as unsigned int was never enforced; using the caller's
type removes that latent inconsistency and lets the MMIO accessors
do the right thing on any architecture.
The 16-, 8- and 4-byte unrolled bodies from the assembly are folded
into a single 4-byte loop per direction and expressed with
put_unaligned_le32() / get_unaligned_le32(), which the compiler
unrolls exactly as before. The LE variants are used because the
assembly performed a plain 32-bit store on little-endian ARM, which
emits bytes in little-endian order and is what the SCSI payload
expects.
Note on odd transfer lengths: the original assembly dropped the final
odd byte when the tail reached the two-byte handler, leaving the byte
unwritten (out) or unread (in). The C version advances the MMIO
pointer by a full 32-bit card word in the trailing loop, so the
remaining odd byte is transferred through the low half of the next
card word, which is where the hardware actually samples it. Callers
in acornscsi.c pass even lengths in practice, so this is a latent-bug
fix, not a behaviour change that affects existing systems.
No Makefile change is required: acornscsi_mod-objs already lists
acornscsi-io.o, and the build system will pick up the new .c file in
preference to the removed .S file.
Signed-off-by: Breno Rodrigues Alves <breno3011alves@gmail.com>
---
drivers/scsi/arm/acornscsi-io.S | 135 --------------------------------
drivers/scsi/arm/acornscsi-io.c | 106 +++++++++++++++++++++++++
drivers/scsi/arm/acornscsi-io.h | 14 ++++
drivers/scsi/arm/acornscsi.c | 4 +-
4 files changed, 122 insertions(+), 137 deletions(-)
delete mode 100644 drivers/scsi/arm/acornscsi-io.S
create mode 100644 drivers/scsi/arm/acornscsi-io.c
create mode 100644 drivers/scsi/arm/acornscsi-io.h
diff --git a/drivers/scsi/arm/acornscsi-io.S b/drivers/scsi/arm/acornscsi-io.S
deleted file mode 100644
index fdd7237bb..000000000
--- a/drivers/scsi/arm/acornscsi-io.S
+++ /dev/null
@@ -1,135 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * linux/drivers/acorn/scsi/acornscsi-io.S: Acorn SCSI card IO
- */
-#include <linux/linkage.h>
-
-#include <asm/assembler.h>
-#include <mach/hardware.h>
-
-#if defined(__APCS_32__)
-#define LOADREGS(t,r,l...) ldm##t r, l
-#elif defined(__APCS_26__)
-#define LOADREGS(t,r,l...) ldm##t r, l##^
-#endif
-
-@ Purpose: transfer a block of data from the acorn scsi card to memory
-@ Proto : void acornscsi_in(unsigned int addr_start, char *buffer, int length)
-@ Returns: nothing
-
- .align
-ENTRY(__acornscsi_in)
- stmfd sp!, {r4 - r7, lr}
- bic r0, r0, #3
- mov lr, #0xff
- orr lr, lr, #0xff00
-acornscsi_in16lp:
- subs r2, r2, #16
- bmi acornscsi_in8
- ldmia r0!, {r3, r4, r5, r6}
- and r3, r3, lr
- orr r3, r3, r4, lsl #16
- and r4, r5, lr
- orr r4, r4, r6, lsl #16
- ldmia r0!, {r5, r6, r7, ip}
- and r5, r5, lr
- orr r5, r5, r6, lsl #16
- and r6, r7, lr
- orr r6, r6, ip, lsl #16
- stmia r1!, {r3 - r6}
- bne acornscsi_in16lp
- LOADREGS(fd, sp!, {r4 - r7, pc})
-
-acornscsi_in8: adds r2, r2, #8
- bmi acornscsi_in4
- ldmia r0!, {r3, r4, r5, r6}
- and r3, r3, lr
- orr r3, r3, r4, lsl #16
- and r4, r5, lr
- orr r4, r4, r6, lsl #16
- stmia r1!, {r3 - r4}
- LOADREGS(eqfd, sp!, {r4 - r7, pc})
- sub r2, r2, #8
-
-acornscsi_in4: adds r2, r2, #4
- bmi acornscsi_in2
- ldmia r0!, {r3, r4}
- and r3, r3, lr
- orr r3, r3, r4, lsl #16
- str r3, [r1], #4
- LOADREGS(eqfd, sp!, {r4 - r7, pc})
- sub r2, r2, #4
-
-acornscsi_in2: adds r2, r2, #2
- ldr r3, [r0], #4
- and r3, r3, lr
- strb r3, [r1], #1
- mov r3, r3, lsr #8
- strplb r3, [r1], #1
- LOADREGS(fd, sp!, {r4 - r7, pc})
-
-@ Purpose: transfer a block of data from memory to the acorn scsi card
-@ Proto : void acornscsi_in(unsigned int addr_start, char *buffer, int length)
-@ Returns: nothing
-
-ENTRY(__acornscsi_out)
- stmfd sp!, {r4 - r6, lr}
- bic r0, r0, #3
-acornscsi_out16lp:
- subs r2, r2, #16
- bmi acornscsi_out8
- ldmia r1!, {r4, r6, ip, lr}
- mov r3, r4, lsl #16
- orr r3, r3, r3, lsr #16
- mov r4, r4, lsr #16
- orr r4, r4, r4, lsl #16
- mov r5, r6, lsl #16
- orr r5, r5, r5, lsr #16
- mov r6, r6, lsr #16
- orr r6, r6, r6, lsl #16
- stmia r0!, {r3, r4, r5, r6}
- mov r3, ip, lsl #16
- orr r3, r3, r3, lsr #16
- mov r4, ip, lsr #16
- orr r4, r4, r4, lsl #16
- mov ip, lr, lsl #16
- orr ip, ip, ip, lsr #16
- mov lr, lr, lsr #16
- orr lr, lr, lr, lsl #16
- stmia r0!, {r3, r4, ip, lr}
- bne acornscsi_out16lp
- LOADREGS(fd, sp!, {r4 - r6, pc})
-
-acornscsi_out8: adds r2, r2, #8
- bmi acornscsi_out4
- ldmia r1!, {r4, r6}
- mov r3, r4, lsl #16
- orr r3, r3, r3, lsr #16
- mov r4, r4, lsr #16
- orr r4, r4, r4, lsl #16
- mov r5, r6, lsl #16
- orr r5, r5, r5, lsr #16
- mov r6, r6, lsr #16
- orr r6, r6, r6, lsl #16
- stmia r0!, {r3, r4, r5, r6}
- LOADREGS(eqfd, sp!, {r4 - r6, pc})
-
- sub r2, r2, #8
-acornscsi_out4: adds r2, r2, #4
- bmi acornscsi_out2
- ldr r4, [r1], #4
- mov r3, r4, lsl #16
- orr r3, r3, r3, lsr #16
- mov r4, r4, lsr #16
- orr r4, r4, r4, lsl #16
- stmia r0!, {r3, r4}
- LOADREGS(eqfd, sp!, {r4 - r6, pc})
-
- sub r2, r2, #4
-acornscsi_out2: adds r2, r2, #2
- ldr r3, [r1], #2
- strb r3, [r0], #1
- mov r3, r3, lsr #8
- strplb r3, [r0], #1
- LOADREGS(fd, sp!, {r4 - r6, pc})
-
diff --git a/drivers/scsi/arm/acornscsi-io.c b/drivers/scsi/arm/acornscsi-io.c
new file mode 100644
index 000000000..c907090bd
--- /dev/null
+++ b/drivers/scsi/arm/acornscsi-io.c
@@ -0,0 +1,106 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Acorn SCSI card I/O
+ *
+ * The Acorn SCSI expansion card exposes each 16-bit SCSI data
+ * halfword on the low half of a 32-bit card access:
+ *
+ * - on reads, one 16-bit value is returned per 32-bit card load;
+ * consecutive card reads are packed into 32-bit memory words
+ *
+ * - on writes, a 16-bit memory halfword is expanded into a 32-bit
+ * card word. The original assembly duplicates the halfword into
+ * both halves of the card word for the bulk of the transfer and
+ * writes only the low 16 bits of the trailing partial word,
+ * since the hardware samples only the low half.
+ */
+
+#include <linux/unaligned.h>
+#include <linux/align.h>
+#include <linux/io.h>
+#include <linux/types.h>
+
+#include "acornscsi-io.h"
+
+/*
+ * Transfer a block of data from the Acorn SCSI card into memory.
+ *
+ * The card address is rounded down to a 32-bit boundary. Each
+ * 32-bit read from the card yields one valid 16-bit halfword in
+ * the low half; pairs of card reads are packed into single 32-bit
+ * memory words.
+ */
+void __acornscsi_in(void __iomem *addr, char *buffer, int length)
+{
+ const u32 __iomem *card = PTR_ALIGN_DOWN((const u32 __iomem *)addr, 4);
+ u8 *buf = (u8 *)buffer;
+
+ while (length >= 4) {
+ u32 w0 = readl(card + 0);
+ u32 w1 = readl(card + 1);
+
+ put_unaligned_le32((w0 & 0xffff) | (w1 << 16), buf);
+
+ card += 2;
+ buf += 4;
+ length -= 4;
+ }
+
+ while (length >= 2) {
+ u32 w0 = readl(card);
+
+ put_unaligned_le16(w0 & 0xffff, buf);
+
+ card++;
+ buf += 2;
+ length -= 2;
+ }
+
+ if (length >= 1) {
+ u32 w0 = readl(card);
+
+ *buf = w0 & 0xff;
+ }
+}
+
+/*
+ * Transfer a block of data from memory to the Acorn SCSI card.
+ *
+ * Each 16-bit value read from memory is expanded into a 32-bit card
+ * word, with the halfword duplicated in both halves (matching the
+ * original assembly). For the trailing partial halfword the card
+ * only samples the low 16 bits, so two byte stores are sufficient.
+ */
+void __acornscsi_out(void __iomem *addr, char *buffer, int length)
+{
+ u32 __iomem *card = PTR_ALIGN_DOWN((u32 __iomem *)addr, 4);
+ const u8 *buf = (const u8 *)buffer;
+
+ while (length >= 4) {
+ u32 v = get_unaligned_le32(buf);
+ u32 lo = v & 0xffff;
+ u32 hi = v >> 16;
+
+ writel(lo | (lo << 16), card + 0);
+ writel(hi | (hi << 16), card + 1);
+
+ card += 2;
+ buf += 4;
+ length -= 4;
+ }
+
+ while (length >= 2) {
+ u16 v = get_unaligned_le16(buf);
+ void __iomem *cp = (void __iomem *)card;
+
+ writeb(v & 0xff, cp);
+ writeb((v >> 8) & 0xff, cp + 1);
+
+ card++;
+ buf += 2;
+ length -= 2;
+ }
+
+ if (length >= 1)
+ writeb(*buf, (void __iomem *)card);
+}
diff --git a/drivers/scsi/arm/acornscsi-io.h b/drivers/scsi/arm/acornscsi-io.h
new file mode 100644
index 000000000..64acfb8bb
--- /dev/null
+++ b/drivers/scsi/arm/acornscsi-io.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Acorn SCSI card I/O
+ */
+
+#ifndef _ACORNSCSI_IO_H
+#define _ACORNSCSI_IO_H
+
+#include <linux/io.h>
+
+void __acornscsi_in(void __iomem *addr, char *buffer, int length);
+void __acornscsi_out(void __iomem *addr, char *buffer, int length);
+
+#endif /* _ACORNSCSI_IO_H */
diff --git a/drivers/scsi/arm/acornscsi.c b/drivers/scsi/arm/acornscsi.c
index 79d7d7336..263a1bf24 100644
--- a/drivers/scsi/arm/acornscsi.c
+++ b/drivers/scsi/arm/acornscsi.c
@@ -140,6 +140,8 @@
#include <scsi/scsicam.h>
+#include "acornscsi-io.h"
+
#define VER_MAJOR 2
#define VER_MINOR 0
#define VER_PATCH 6
@@ -886,7 +888,6 @@ static
void acornscsi_data_read(AS_Host *host, char *ptr,
unsigned int start_addr, unsigned int length)
{
- extern void __acornscsi_in(void __iomem *, char *buf, int len);
unsigned int page, offset, len = length;
page = (start_addr >> 12);
@@ -931,7 +932,6 @@ static
void acornscsi_data_write(AS_Host *host, char *ptr,
unsigned int start_addr, unsigned int length)
{
- extern void __acornscsi_out(void __iomem *, char *buf, int len);
unsigned int page, offset, len = length;
page = (start_addr >> 12);
--
2.56.0
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v3] scsi: arm: Convert acornscsi-io.S to C
2026-10-22 20:30 ` [PATCH v3] " Breno Rodrigues Alves
@ 2026-10-22 20:49 ` Bart Van Assche
0 siblings, 0 replies; 15+ messages in thread
From: Bart Van Assche @ 2026-10-22 20:49 UTC (permalink / raw)
To: Breno Rodrigues Alves, linux
Cc: James.Bottomley, mkp, linux-arm-kernel, linux-scsi, linux-kernel
On 10/22/26 1:30 PM, Breno Rodrigues Alves wrote:
> + while (length >= 2) {
Shouldn't this "while" be changed into "if"?
> + u16 v = get_unaligned_le16(buf);
> + void __iomem *cp = (void __iomem *)card;
> +
> + writeb(v & 0xff, cp);
> + writeb((v >> 8) & 0xff, cp + 1);
The above code can be simplified as shown below, isn't it?
void __iomem *cp = (void __iomem *)card;
writeb(buf[0], cp);
writeb(buf[1], cp + 1);
> + if (length >= 1)
> + writeb(*buf, (void __iomem *)card);
Isn't the above (void __iomem *) cast superfluous?
Thanks,
Bart.
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v2] scsi: arm: Convert acornscsi-io.S to C
2026-10-22 19:22 ` Bart Van Assche
2026-10-22 20:30 ` Breno Rodrigues Alves
@ 2026-10-22 21:02 ` Breno Rodrigues Alves
1 sibling, 0 replies; 15+ messages in thread
From: Breno Rodrigues Alves @ 2026-10-22 21:02 UTC (permalink / raw)
To: Bart Van Assche
Cc: linux, James.Bottomley, mkp, linux-arm-kernel, linux-scsi,
linux-kernel, Breno Rodrigues Alves
Hi Bart,
> Shouldn't this "while" be changed into "if"?
Yes. After the preceding while (length >= 4) loop, length is in
{0, 1, 2, 3}, so the trailing loop runs at most once. The same
applies to the corresponding loop in __acornscsi_in(); both are now
'if' in v4.
> The above code can be simplified as shown below, isn't it?
>
> void __iomem *cp = (void __iomem *)card;
>
> writeb(buf[0], cp);
> writeb(buf[1], cp + 1);
Agreed. v4 drops get_unaligned_le16() in __acornscsi_out() and
writes the two bytes directly. Note that __acornscsi_in() still
needs put_unaligned_le16() because the bytes come from the low
half of the 32-bit card word and buf may be unaligned.
> Isn't the above (void __iomem *) cast superfluous?
Yes. u32 __iomem * converts implicitly to void __iomem *. Removed
in v4. The cast on the earlier 'cp' variable is kept because cp is
used for byte-level pointer arithmetic (cp + 1 must advance one
byte, not one 32-bit word).
v4 follows.
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v4] scsi: arm: Convert acornscsi-io.S to C
2026-10-22 18:36 [PATCH 0/1] scsi: arm: Convert acornscsi-io.S to C Breno Rodrigues Alves
` (2 preceding siblings ...)
2026-10-22 20:30 ` [PATCH v3] " Breno Rodrigues Alves
@ 2026-10-22 21:02 ` Breno Rodrigues Alves
2026-10-22 21:36 ` Bart Van Assche
2026-10-22 21:44 ` Jonathan Corbet
3 siblings, 2 replies; 15+ messages in thread
From: Breno Rodrigues Alves @ 2026-10-22 21:02 UTC (permalink / raw)
To: linux
Cc: Bart Van Assche, James.Bottomley, mkp, linux-arm-kernel,
linux-scsi, linux-kernel, Breno Rodrigues Alves
The Acorn SCSI expansion card presents each 16-bit SCSI data halfword
on the low half of a 32-bit card access. The hand-written assembly
in acornscsi-io.S implements the two directions of the data path:
- __acornscsi_in() : packs pairs of 16-bit card reads into 32-bit
memory words
- __acornscsi_out() : expands 16-bit memory halfwords into 32-bit
card writes (duplicating the halfword in both
halves for the bulk of the transfer)
Both routines are simple pointer walks that a modern compiler handles
just as well as hand-written assembly, so replace the .S file with an
equivalent C implementation.
The C version takes its arguments as (void __iomem *, char *, int),
matching the prototype actually used by acornscsi.c. The assembly
version had no C prototype, so the original comment declaring the
first argument as unsigned int was never enforced; using the caller's
type removes that latent inconsistency and lets the MMIO accessors
do the right thing on any architecture.
The 16-, 8- and 4-byte unrolled bodies from the assembly are folded
into a single 4-byte loop per direction and expressed with
put_unaligned_le32() / get_unaligned_le32(), which the compiler
unrolls exactly as before. The LE variants are used because the
assembly performed a plain 32-bit store on little-endian ARM, which
emits bytes in little-endian order and is what the SCSI payload
expects.
Note on odd transfer lengths: the original assembly dropped the final
odd byte when the tail reached the two-byte handler, leaving the byte
unwritten (out) or unread (in). The C version advances the MMIO
pointer by a full 32-bit card word in the trailing loop, so the
remaining odd byte is transferred through the low half of the next
card word, which is where the hardware actually samples it. Callers
in acornscsi.c pass even lengths in practice, so this is a latent-bug
fix, not a behaviour change that affects existing systems.
No Makefile change is required: acornscsi_mod-objs already lists
acornscsi-io.o, and the build system will pick up the new .c file in
preference to the removed .S file.
Signed-off-by: Breno Rodrigues Alves <breno3011alves@gmail.com>
---
drivers/scsi/arm/acornscsi-io.S | 135 --------------------------------
drivers/scsi/arm/acornscsi-io.c | 105 +++++++++++++++++++++++++
drivers/scsi/arm/acornscsi-io.h | 14 ++++
drivers/scsi/arm/acornscsi.c | 4 +-
4 files changed, 121 insertions(+), 137 deletions(-)
delete mode 100644 drivers/scsi/arm/acornscsi-io.S
create mode 100644 drivers/scsi/arm/acornscsi-io.c
create mode 100644 drivers/scsi/arm/acornscsi-io.h
diff --git a/drivers/scsi/arm/acornscsi-io.S b/drivers/scsi/arm/acornscsi-io.S
deleted file mode 100644
index fdd7237bb..000000000
--- a/drivers/scsi/arm/acornscsi-io.S
+++ /dev/null
@@ -1,135 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * linux/drivers/acorn/scsi/acornscsi-io.S: Acorn SCSI card IO
- */
-#include <linux/linkage.h>
-
-#include <asm/assembler.h>
-#include <mach/hardware.h>
-
-#if defined(__APCS_32__)
-#define LOADREGS(t,r,l...) ldm##t r, l
-#elif defined(__APCS_26__)
-#define LOADREGS(t,r,l...) ldm##t r, l##^
-#endif
-
-@ Purpose: transfer a block of data from the acorn scsi card to memory
-@ Proto : void acornscsi_in(unsigned int addr_start, char *buffer, int length)
-@ Returns: nothing
-
- .align
-ENTRY(__acornscsi_in)
- stmfd sp!, {r4 - r7, lr}
- bic r0, r0, #3
- mov lr, #0xff
- orr lr, lr, #0xff00
-acornscsi_in16lp:
- subs r2, r2, #16
- bmi acornscsi_in8
- ldmia r0!, {r3, r4, r5, r6}
- and r3, r3, lr
- orr r3, r3, r4, lsl #16
- and r4, r5, lr
- orr r4, r4, r6, lsl #16
- ldmia r0!, {r5, r6, r7, ip}
- and r5, r5, lr
- orr r5, r5, r6, lsl #16
- and r6, r7, lr
- orr r6, r6, ip, lsl #16
- stmia r1!, {r3 - r6}
- bne acornscsi_in16lp
- LOADREGS(fd, sp!, {r4 - r7, pc})
-
-acornscsi_in8: adds r2, r2, #8
- bmi acornscsi_in4
- ldmia r0!, {r3, r4, r5, r6}
- and r3, r3, lr
- orr r3, r3, r4, lsl #16
- and r4, r5, lr
- orr r4, r4, r6, lsl #16
- stmia r1!, {r3 - r4}
- LOADREGS(eqfd, sp!, {r4 - r7, pc})
- sub r2, r2, #8
-
-acornscsi_in4: adds r2, r2, #4
- bmi acornscsi_in2
- ldmia r0!, {r3, r4}
- and r3, r3, lr
- orr r3, r3, r4, lsl #16
- str r3, [r1], #4
- LOADREGS(eqfd, sp!, {r4 - r7, pc})
- sub r2, r2, #4
-
-acornscsi_in2: adds r2, r2, #2
- ldr r3, [r0], #4
- and r3, r3, lr
- strb r3, [r1], #1
- mov r3, r3, lsr #8
- strplb r3, [r1], #1
- LOADREGS(fd, sp!, {r4 - r7, pc})
-
-@ Purpose: transfer a block of data from memory to the acorn scsi card
-@ Proto : void acornscsi_in(unsigned int addr_start, char *buffer, int length)
-@ Returns: nothing
-
-ENTRY(__acornscsi_out)
- stmfd sp!, {r4 - r6, lr}
- bic r0, r0, #3
-acornscsi_out16lp:
- subs r2, r2, #16
- bmi acornscsi_out8
- ldmia r1!, {r4, r6, ip, lr}
- mov r3, r4, lsl #16
- orr r3, r3, r3, lsr #16
- mov r4, r4, lsr #16
- orr r4, r4, r4, lsl #16
- mov r5, r6, lsl #16
- orr r5, r5, r5, lsr #16
- mov r6, r6, lsr #16
- orr r6, r6, r6, lsl #16
- stmia r0!, {r3, r4, r5, r6}
- mov r3, ip, lsl #16
- orr r3, r3, r3, lsr #16
- mov r4, ip, lsr #16
- orr r4, r4, r4, lsl #16
- mov ip, lr, lsl #16
- orr ip, ip, ip, lsr #16
- mov lr, lr, lsr #16
- orr lr, lr, lr, lsl #16
- stmia r0!, {r3, r4, ip, lr}
- bne acornscsi_out16lp
- LOADREGS(fd, sp!, {r4 - r6, pc})
-
-acornscsi_out8: adds r2, r2, #8
- bmi acornscsi_out4
- ldmia r1!, {r4, r6}
- mov r3, r4, lsl #16
- orr r3, r3, r3, lsr #16
- mov r4, r4, lsr #16
- orr r4, r4, r4, lsl #16
- mov r5, r6, lsl #16
- orr r5, r5, r5, lsr #16
- mov r6, r6, lsr #16
- orr r6, r6, r6, lsl #16
- stmia r0!, {r3, r4, r5, r6}
- LOADREGS(eqfd, sp!, {r4 - r6, pc})
-
- sub r2, r2, #8
-acornscsi_out4: adds r2, r2, #4
- bmi acornscsi_out2
- ldr r4, [r1], #4
- mov r3, r4, lsl #16
- orr r3, r3, r3, lsr #16
- mov r4, r4, lsr #16
- orr r4, r4, r4, lsl #16
- stmia r0!, {r3, r4}
- LOADREGS(eqfd, sp!, {r4 - r6, pc})
-
- sub r2, r2, #4
-acornscsi_out2: adds r2, r2, #2
- ldr r3, [r1], #2
- strb r3, [r0], #1
- mov r3, r3, lsr #8
- strplb r3, [r0], #1
- LOADREGS(fd, sp!, {r4 - r6, pc})
-
diff --git a/drivers/scsi/arm/acornscsi-io.c b/drivers/scsi/arm/acornscsi-io.c
new file mode 100644
index 000000000..08071963d
--- /dev/null
+++ b/drivers/scsi/arm/acornscsi-io.c
@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Acorn SCSI card I/O
+ *
+ * The Acorn SCSI expansion card exposes each 16-bit SCSI data
+ * halfword on the low half of a 32-bit card access:
+ *
+ * - on reads, one 16-bit value is returned per 32-bit card load;
+ * consecutive card reads are packed into 32-bit memory words
+ *
+ * - on writes, a 16-bit memory halfword is expanded into a 32-bit
+ * card word. The original assembly duplicates the halfword into
+ * both halves of the card word for the bulk of the transfer and
+ * writes only the low 16 bits of the trailing partial word,
+ * since the hardware samples only the low half.
+ */
+
+#include <linux/align.h>
+#include <linux/io.h>
+#include <linux/types.h>
+#include <linux/unaligned.h>
+
+#include "acornscsi-io.h"
+
+/*
+ * Transfer a block of data from the Acorn SCSI card into memory.
+ *
+ * The card address is rounded down to a 32-bit boundary. Each
+ * 32-bit read from the card yields one valid 16-bit halfword in
+ * the low half; pairs of card reads are packed into single 32-bit
+ * memory words.
+ */
+void __acornscsi_in(void __iomem *addr, char *buffer, int length)
+{
+ const u32 __iomem *card = PTR_ALIGN_DOWN((const u32 __iomem *)addr, 4);
+ u8 *buf = (u8 *)buffer;
+
+ while (length >= 4) {
+ u32 w0 = readl(card + 0);
+ u32 w1 = readl(card + 1);
+
+ put_unaligned_le32((w0 & 0xffff) | (w1 << 16), buf);
+
+ card += 2;
+ buf += 4;
+ length -= 4;
+ }
+
+ if (length >= 2) {
+ u32 w0 = readl(card);
+
+ put_unaligned_le16(w0 & 0xffff, buf);
+
+ card++;
+ buf += 2;
+ length -= 2;
+ }
+
+ if (length >= 1) {
+ u32 w0 = readl(card);
+
+ *buf = w0 & 0xff;
+ }
+}
+
+/*
+ * Transfer a block of data from memory to the Acorn SCSI card.
+ *
+ * Each 16-bit value read from memory is expanded into a 32-bit card
+ * word, with the halfword duplicated in both halves (matching the
+ * original assembly). For the trailing partial halfword the card
+ * only samples the low 16 bits, so two byte stores are sufficient.
+ */
+void __acornscsi_out(void __iomem *addr, char *buffer, int length)
+{
+ u32 __iomem *card = PTR_ALIGN_DOWN((u32 __iomem *)addr, 4);
+ const u8 *buf = (const u8 *)buffer;
+
+ while (length >= 4) {
+ u32 v = get_unaligned_le32(buf);
+ u32 lo = v & 0xffff;
+ u32 hi = v >> 16;
+
+ writel(lo | (lo << 16), card + 0);
+ writel(hi | (hi << 16), card + 1);
+
+ card += 2;
+ buf += 4;
+ length -= 4;
+ }
+
+ if (length >= 2) {
+ void __iomem *cp = (void __iomem *)card;
+
+ writeb(buf[0], cp);
+ writeb(buf[1], cp + 1);
+
+ card++;
+ buf += 2;
+ length -= 2;
+ }
+
+ if (length >= 1)
+ writeb(*buf, card);
+}
diff --git a/drivers/scsi/arm/acornscsi-io.h b/drivers/scsi/arm/acornscsi-io.h
new file mode 100644
index 000000000..64acfb8bb
--- /dev/null
+++ b/drivers/scsi/arm/acornscsi-io.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Acorn SCSI card I/O
+ */
+
+#ifndef _ACORNSCSI_IO_H
+#define _ACORNSCSI_IO_H
+
+#include <linux/io.h>
+
+void __acornscsi_in(void __iomem *addr, char *buffer, int length);
+void __acornscsi_out(void __iomem *addr, char *buffer, int length);
+
+#endif /* _ACORNSCSI_IO_H */
diff --git a/drivers/scsi/arm/acornscsi.c b/drivers/scsi/arm/acornscsi.c
index 79d7d7336..263a1bf24 100644
--- a/drivers/scsi/arm/acornscsi.c
+++ b/drivers/scsi/arm/acornscsi.c
@@ -140,6 +140,8 @@
#include <scsi/scsicam.h>
+#include "acornscsi-io.h"
+
#define VER_MAJOR 2
#define VER_MINOR 0
#define VER_PATCH 6
@@ -886,7 +888,6 @@ static
void acornscsi_data_read(AS_Host *host, char *ptr,
unsigned int start_addr, unsigned int length)
{
- extern void __acornscsi_in(void __iomem *, char *buf, int len);
unsigned int page, offset, len = length;
page = (start_addr >> 12);
@@ -931,7 +932,6 @@ static
void acornscsi_data_write(AS_Host *host, char *ptr,
unsigned int start_addr, unsigned int length)
{
- extern void __acornscsi_out(void __iomem *, char *buf, int len);
unsigned int page, offset, len = length;
page = (start_addr >> 12);
--
2.56.0
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v4] scsi: arm: Convert acornscsi-io.S to C
2026-10-22 21:02 ` [PATCH v4] " Breno Rodrigues Alves
@ 2026-10-22 21:36 ` Bart Van Assche
2026-10-22 21:44 ` Jonathan Corbet
1 sibling, 0 replies; 15+ messages in thread
From: Bart Van Assche @ 2026-10-22 21:36 UTC (permalink / raw)
To: Breno Rodrigues Alves, linux
Cc: James.Bottomley, mkp, linux-arm-kernel, linux-scsi, linux-kernel
On 10/22/26 2:02 PM, Breno Rodrigues Alves wrote:
> Both routines are simple pointer walks that a modern compiler handles
> just as well as hand-written assembly, so replace the .S file with an
> equivalent C implementation.
Reviewed-by: Bart Van Assche <bvanassche@acm.org>
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v4] scsi: arm: Convert acornscsi-io.S to C
2026-10-22 21:02 ` [PATCH v4] " Breno Rodrigues Alves
2026-10-22 21:36 ` Bart Van Assche
@ 2026-10-22 21:44 ` Jonathan Corbet
2026-10-22 21:48 ` Breno Rodrigues Alves
1 sibling, 1 reply; 15+ messages in thread
From: Jonathan Corbet @ 2026-10-22 21:44 UTC (permalink / raw)
To: Breno Rodrigues Alves, linux
Cc: Bart Van Assche, James.Bottomley, mkp, linux-arm-kernel,
linux-scsi, linux-kernel, Breno Rodrigues Alves
Breno Rodrigues Alves <breno3011alves@gmail.com> writes:
> The Acorn SCSI expansion card presents each 16-bit SCSI data halfword
> on the low half of a 32-bit card access. The hand-written assembly
> in acornscsi-io.S implements the two directions of the data path:
>
> - __acornscsi_in() : packs pairs of 16-bit card reads into 32-bit
> memory words
> - __acornscsi_out() : expands 16-bit memory halfwords into 32-bit
> card writes (duplicating the halfword in both
> halves for the bulk of the transfer)
>
> Both routines are simple pointer walks that a modern compiler handles
> just as well as hand-written assembly, so replace the .S file with an
> equivalent C implementation.
How have you tested this change?
jon
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v4] scsi: arm: Convert acornscsi-io.S to C
2026-10-22 21:44 ` Jonathan Corbet
@ 2026-10-22 21:48 ` Breno Rodrigues Alves
2026-10-22 22:15 ` Jonathan Corbet
0 siblings, 1 reply; 15+ messages in thread
From: Breno Rodrigues Alves @ 2026-10-22 21:48 UTC (permalink / raw)
To: Jonathan Corbet
Cc: linux, Bart Van Assche, James.Bottomley, mkp, linux-arm-kernel,
linux-scsi, linux-kernel, Breno Rodrigues Alves
Hi Jonathan,
Fair question - the change is a conversion, and I do not have a RiscPC
with an Acorn SCSI card to test on real hardware. Here is what I did
test:
1. Userspace unit test with a mocked MMIO layer
I built a small harness that compiles acornscsi-io.c unmodified
against stub <linux/io.h> and <linux/types.h> headers. The stubs
map readl()/writel()/writeb() onto a byte-addressable "card" buffer
and a plain userspace memory buffer. For each transfer length L in
{0, 2, 4, 8, 16, 32, 64, 100, 512, 1000}:
- __acornscsi_in: I filled the "card" with L bytes of a known
pattern (each 32-bit card word carrying one 16-bit halfword in
the low half, garbage in the high half) and verified that the
output buffer matched the pattern byte-for-byte.
- __acornscsi_out: I filled a userspace buffer with a known
pattern, called the function, and verified that each 32-bit card
word held the expected halfword duplicated in both halves.
All 20 cases pass. This is the harness that caught the two issues
the sashiko bot later reported independently - it is not just a
smoke test.
2. Cross-compile for the target
Built with arm-linux-gnueabi-gcc on rpc_defconfig with
CONFIG_SCSI_ACORNSCSI_3=m. The module links cleanly
(acornscsi_mod.ko), and nm on acornscsi-io.o shows:
00000000 T __acornscsi_in
00000104 T __acornscsi_out
objdump on the resulting object shows that the alignment step
compiles to "bic sl, r0, #3", matching the original assembly's
"bic r0, r0, #3".
3. Apply to a clean tree and rebuild
Copied the tree, git reset --hard to the parent commit, applied the
patch with "git am". It applies cleanly; re-running the same
compile command produces the same object. This confirms the patch
file itself is intact and does not depend on my working tree state.
4. checkpatch.pl --strict --no-tree on the patch itself
Reports 0 errors, 0 warnings (with FILE_PATH_CHANGES ignored, as
drivers/scsi/arm/ is already covered by MAINTAINERS).
What I did NOT test:
- Real hardware. I do not have an Acorn RiscPC or the SCSI expansion
card. The unit test proves equivalence with the original assembly
semantics, but it cannot prove that the semantics match the actual
hardware - only that the C version does the same thing the assembly
did.
- Odd transfer lengths through acornscsi.c. All current callers pass
even lengths, so the trailing-byte path is only exercised by the
unit test, not by the driver end-to-end.
- Interrupt handling under load. This is unchanged by the patch (the
IRQ path is untouched), so I did not add specific coverage.
If there is a specific test or configuration you would like me to run,
please let me know.
Breno
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v4] scsi: arm: Convert acornscsi-io.S to C
2026-10-22 21:48 ` Breno Rodrigues Alves
@ 2026-10-22 22:15 ` Jonathan Corbet
2026-10-22 22:28 ` Breno Rodrigues Alves
0 siblings, 1 reply; 15+ messages in thread
From: Jonathan Corbet @ 2026-10-22 22:15 UTC (permalink / raw)
To: Breno Rodrigues Alves
Cc: linux, Bart Van Assche, James.Bottomley, mkp, linux-arm-kernel,
linux-scsi, linux-kernel, Breno Rodrigues Alves
Breno Rodrigues Alves <breno3011alves@gmail.com> writes:
> What I did NOT test:
>
> - Real hardware. I do not have an Acorn RiscPC or the SCSI expansion
> card. The unit test proves equivalence with the original assembly
> semantics, but it cannot prove that the semantics match the actual
> hardware - only that the C version does the same thing the assembly
> did.
>
> - Odd transfer lengths through acornscsi.c. All current callers pass
> even lengths, so the trailing-byte path is only exercised by the
> unit test, not by the driver end-to-end.
>
> - Interrupt handling under load. This is unchanged by the patch (the
> IRQ path is untouched), so I did not add specific coverage.
>
> If there is a specific test or configuration you would like me to run,
> please let me know.
I guess what I *really* want to know is why you are messing with a
driver for hardware you don't have - for a computer which was
discontinued in 1998 - and translating a file that has not seen a
significant change in the entire Git era? What benefit does this bring
to anybody?
You seem to be on a real mission to do these translations, but they are
not valuable for their own sake. The kernel does not lack for churn as
it is.
jon
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH v4] scsi: arm: Convert acornscsi-io.S to C
2026-10-22 22:15 ` Jonathan Corbet
@ 2026-10-22 22:28 ` Breno Rodrigues Alves
0 siblings, 0 replies; 15+ messages in thread
From: Breno Rodrigues Alves @ 2026-10-22 22:28 UTC (permalink / raw)
To: Jonathan Corbet
Cc: linux, Bart Van Assche, James.Bottomley, mkp, linux-arm-kernel,
linux-scsi, linux-kernel, Breno Rodrigues Alves
Hi Jonathan,
You are right, and I should have been clearer about the motivation.
The reason this patch exists is that the original assembly has a latent
bug: for odd transfer lengths, it drops the final byte. The C version
fixes that, and the odd-length case is now exercised by the userspace
unit test. But that bug cannot be triggered by any current caller, so
even the bug-fix justification is weak - it is a fix for a path that
nothing currently exercises, in a driver for hardware almost nobody
still has.
You are also right about the broader point: "assembly to C" on its own
is not a justification. The kernel's assembly is there for reasons, and
translating it for its own sake is churn that reviewers have to pay for.
I have been approaching this as a translation exercise, and that is not
the right frame. I will not propose further conversions of that kind
without a concrete functional benefit - something that fixes a bug that
can actually be hit, or enables a change that cannot be done in assembly.
Bart's Reviewed-by: stands on the technical merits of the change as a
change (the C code is equivalent to the assembly for even lengths and
correct for odd ones). Your concern is a different question - whether
the change should exist at all. Both are valid, and the decision
belongs to the SCSI maintainers, not to me.
If the consensus is that this patch is not worth the churn, I am happy
to withdraw it. I would rather withdraw it than have it sit in the tree
as an example of exactly the kind of change you are warning against.
Thanks for the direct feedback - it is a useful correction to how I was
approaching this, and more valuable than the patch itself.
Breno
^ permalink raw reply [flat|nested] 15+ messages in thread
end of thread, other threads:[~2026-10-22 22:28 UTC | newest]
Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-22 18:36 [PATCH 0/1] scsi: arm: Convert acornscsi-io.S to C Breno Rodrigues Alves
2026-10-22 18:36 ` [PATCH] " Breno Rodrigues Alves
2026-10-22 18:52 ` [PATCH v2 0/1] " Breno Rodrigues Alves
2026-10-22 18:52 ` [PATCH v2] " Breno Rodrigues Alves
2026-10-22 19:22 ` Bart Van Assche
2026-10-22 20:30 ` Breno Rodrigues Alves
2026-10-22 21:02 ` Breno Rodrigues Alves
2026-10-22 20:30 ` [PATCH v3] " Breno Rodrigues Alves
2026-10-22 20:49 ` Bart Van Assche
2026-10-22 21:02 ` [PATCH v4] " Breno Rodrigues Alves
2026-10-22 21:36 ` Bart Van Assche
2026-10-22 21:44 ` Jonathan Corbet
2026-10-22 21:48 ` Breno Rodrigues Alves
2026-10-22 22:15 ` Jonathan Corbet
2026-10-22 22:28 ` Breno Rodrigues Alves
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®