Case Study: A4091 / 53C710 SCSI driver¶
This is the deep end of the drivers pillar: writing a brand-new SCSI host adapter driver for Amix, for a board the OS was never meant to support, and getting Amix to boot with root, swap and filesystem entirely on it. It pulls together the device-driver model, Zorro AUTOCONFIG, the kernel build and the boot process โ all the other driver pages applied at once to a real board.
The work is first-party and reproduced locally in Amiberry on the real Amix 2.1c distribution.
Claims tagged โ
below were reproduced on the running system; ๐ก claims are emulation-only and need
real-hardware confirmation; ๐ด marks a belief we held and disproved. The source is the
A4091-on-Amix project โ src/a4091-wr.c, the src/kernel-patches/, and the lab
notebook NOTES.md.
What the A4091 is¶
The A4091 is Commodore's Zorro III SCSI-2 host adapter, built on the NCR/Symbios 53C710 SCSI I/O processor โ . It is the high-end successor to the Zorro II A2091 (WD33C93), intended for the A3000T/A4000T class.
Its AUTOCONFIG identity and memory window โ
(src/a4091-wr.c, docs/a4091-53c710-reference.md ยง0):
| Property | Value | Source |
|---|---|---|
| AutoConfig manufacturer | 0x0202 (Commodore) |
a4091-wr.c A4091_PROD |
| AutoConfig product | 0x54 |
a4091-wr.c A4091_PROD |
| Combined product id | 0x02020054 |
a4091-wr.c #define A4091_PROD 0x02020054 |
er_Type |
0x90 (Zorro III) |
reference ยง0 |
| Physical board base | 0x40000000 |
a4091-wr.c a4091map() fallback |
| Board window | 0x01000000 (16 MB), sparse |
reference ยง1 |
| 53C710 register block | board + 0x00800000 โ 0x40800000 |
a4091-wr.c SIOP_OFF |
Amix (Commodore's SVR4.0 port to 68030 Amigas, the "2.1c" distribution โ ) shipped with no 53C710 driver and is widely described as Zorro II only โ โ see Zorro AUTOCONFIG for drivers and Supported hardware. The stock kernel only knows the A2090/A2091 (WD33C93) and the A3000 on-board SCSI. This project wrote a native 53C710 block driver and made the A4091 a first-class, auto-detected SCSI controller alongside them.
The Zorro III obstacle and the sptalloc() solution¶
The board is in Zorro III space, and that is exactly where the stock drivers' trick breaks down.
๐ด "Zorro III is unaddressable" โ was thought absolute, is actually a TT-register gap with a
work-around. Amix's 68030 maps physical address space with two Transparent Translation
registers (amiga/ml/ttrap.s) โ
:
tt0 = 0x003F0143 maps phys 0x00000000 - 0x3FFFFFFF
tt1 = 0x807F0143 maps phys 0x80000000 - 0xFFFFFFFF
That leaves 0x40000000 - 0x7FFFFFFF an unmapped gap โ and the A4091 sits in it โ
. Zorro II
boards are โค 24-bit (e.g. the A3000 SCSI at 0xDD0000), so they fall inside TT0; that is why the
stock WD33C93 drivers can simply dereference the address autocon() returns. Widening TT0 to cover
the gap is unsafe โ amiga/ml/syms.s places the page-mapped u-area at 0x40000000 โ
.
The fix is the kernel primitive sptalloc(npages, prot, pfn, flag), which page-table-maps
physical pages into kernel virtual address space โ
. The driver maps the board's AutoConfig page and
the 53C710 register block (src/a4091-wr.c, a4091map()):
#define SIOP_OFF 0x00800000 /* 53C710 register block within the board window */
...
acfg = (volatile uchar *)sptalloc( 1, PG_V, phystopfn( (paddr_t)base), 0); /* board base */
siop = (volatile uchar *)sptalloc( 1, PG_V, phystopfn( (paddr_t)base + SIOP_OFF), 0); /* 53C710 regs */
Post-boot these return working kernel VAs (observed siop = 0x401CA000) and the 53C710 is fully
addressable โ
. DMA is unaffected: the 53C710 bus-masters to Amix RAM at phys 0x07800000+,
which is inside TT0, so DMA targets are reachable no matter where the board's registers land in
kernel VA โ
. (See Zorro AUTOCONFIG for autocon() and the Zorro III
detection that complements this mapping.)
The 53C710 driver model โ how it plugs into the Amix SCSI stack¶
โ
The Amix SCSI stack (amiga/alien/) layers like this:
The command unit handed down the stack is struct sdcom (sd.h): cdb[12], the data addr/nbyte,
unit/card, the reading flag, status/okay, and an intr() completion callback โ
. See
the device-driver model for the general switch-table mechanism this sits on top of.
The card registry in sd.c¶
sd.c carries a registry table scsicard[] mapping AutoConfig product ids to queue functions. The
patch adds one row for the A4091 (src/kernel-patches/sd.c) โ
:
static struct {
uint pn; /* product number */
bool (*f)( ); /* queuing function */
char *hardwarename;
} scsicard[] = {
0x0202F003, &a3091queue, "A3000 Internal SCSI",
0x02020001, &a2090queue, "A2090 SCSI",
0x02020003, &a2091queue, "A2091 SCSI",
0x02020054, &a4091queue, "A4091 SCSI", /* <- our addition */
};
At the first root open, sd.c init() calls autocon(productid, โฆ) for each row and insert()s the
cards it finds, sorted ascending by board base address โ
:
static void
init( )
{
if (not queue->f)
for (i=0; i<nel( scsicard); ++i) {
int c = 0;
while (autocon( scsicard[i].pn, c, &a, &o))
insert( scsicard[i].f, c++, a, scsicard[i].hardwarename);
}
}
So on a machine with both controllers, the A3000 SCSI (0xDD0000) becomes queue[0] (card 0)
and the A4091 (0x40000000) becomes queue[1] (card 1) โ the lower base sorts first โ
. This
ordering is the entire story behind booting root from the A4091 โ see
the boot process: root on an A4091 and the phantom-A3000 fix in
Zorro AUTOCONFIG.
The matching build patch is src/kernel-patches/alien-Makefile, which adds a4091.o to the
amiga/alien OBJ list.
The driver itself: a4091-wr.c¶
The production driver is src/a4091-wr.c (the READ+WRITE driver) โ
. Its key pieces:
a4091map()โsptalloc-maps the board base and the register block (above), and caches the mapping so it only happens once (if (siop) return 0). Falls back to the known base0x40000000/ size0x01000000ifautocon()does not find the board โ .a4091init()โ a 53C710 soft reset followed by minimal polled-mode register init. Reset is via ISTAT bit 6 (not DCNTL โ that is the older 53C700) and is not self-clearing, so it is set then explicitly cleared. The init writesSCNTL0=0xCC,SCNTL1=0x20,DCNTL=0x00,DMODE=0xE0(burst-8 | FC2, the A4091 value), the one-hot host idSCID = 1 << 7 = 0x80, and the A4091 bus-arbitration errata bitCTEST8 |= 0x01(SM). No SCSI/DMA interrupts are enabled โ . All register writes go toreg + 0x40(the A4091 write-shadowWRSHADOW), reads atreg + 0; accesses are 8- or 32-bit only, never 16-bit โ .a4091queue(c, cp)โ drives any CDB from the caller'ssdcomthrough a table-indirect 53C710 SCRIPTS program (inq_script[], a staticulong[]that lives in kernel.data, so its bus address equals&inq_script[0]under the TT0 identity mapping). CDB length is derived from the SCSI group code (top 3 bits of the opcode โ 6 / 10 / 12 bytes). The DSA (struct siop_ds) binds IDENTIFY / CDB / status / message / data buffers; the DATA phase dispatches on the live bus phase, so one SCRIPTS program performs both READ(10) and WRITE(10). Data DMAs straight intocp->addrโ the caller's buffer, already a physical address viadd.c'svtop()โ .
The SCRIPTS program's data-phase dispatch (from inq_script[] in src/a4091-wr.c) is what makes one
program serve reads and writes:
0x808b0000, 0x00000018, /* JUMP dataout, WHEN DATA_OUT (write) */
0x818b0000, 0x00000020, /* JUMP datain, WHEN DATA_IN (read) */
0x838b0000, 0x00000020, /* JUMP status, WHEN STATUS (no data) */
The DSA table binds the caller's buffer for the data move โ :
Completion: the one honest ๐ก in the driver¶
After launching SCRIPTS (a write to R_DSP), the driver reads completion with a single ISTAT read:
WR32( R_DSP, (ulong)inq_script);
/* nopoll completion -- single ISTAT read (emulated target instant) */
istat = RD8(R_ISTAT);
...
else if ((dstat & DSTAT_SIR) && dsps == 0xff00) rc = 0;
The emulated target answers synchronously, so that one read already sees DIP set and
dsps = 0xff00 (the SCRIPTS "done" INT vector) โ
. A physical disk is not instant โ real
hardware needs a bounded poll loop or interrupt-driven completion ๐ก. The hooks for that already
exist: an empty a4091intr() and the int2_tbl level-2 ISR slot (the A2090/A2091 use the same
table โ see device list). Keep this ๐ก until it is
run on metal.
For the full 53C710 register map, SCRIPTS encoding, one-hot SELECT-ID encoding, the soft-reset
sequence, the write-shadow at reg+0x40, and the emulator's full-window SIGSEGV hazard, see the
project's docs/a4091-53c710-reference.md (the verified chip reference behind this driver).
The A4091 disk's /dev numbering¶
โ With both an A3000 and an A4091 present, the A4091 (card 1) target 0 slice 0 is reached at:
The cN in the name is computed, not stored โ see the SCSI minor-number scheme on the
device list. From
amiga/alien/sd.h:
sdunit(dev) = (dev>>0) & 7 /* SCSI target id 0-7 */
sdcard(dev) = (dev>>3) & 1 /* card index (queue[] slot) -- only 0 or 1; SDCARDS = 2 */
sdpart(dev) = (dev>>4) & 7 /* slice/partition */
So cN = sdcard*8 + sdunit: c8d0s0 = card 1, target 0, slice 0 = makedevice(18, 8); the stock
A3000-root device c6d0s1 = card 0, target 6, slice 1 = makedevice(18, 22) โ
. A dev_t is
(major << 18) | minor โ
.
The READ/WRITE path was validated end to end โ :
- Raw SCSI passthrough via the
gsioctluserspace path (src/gsio.c,/dev/scsimajor 11) โ INQUIRY, READ(10), WRITE(10) all returning correct data. mkfs ufs+mounton the A4091 disk, with files written and surviving a reboot (genuine persistence to the disk behind the A4091, not a RAM artefact).
Driver variants โ the evolution¶
Only a4091-wr.c is the production driver; the earlier variants are kept under src/ to show the
incremental bring-up path โ
:
| File | Stage |
|---|---|
a4091.c |
detection-only (proves sptalloc + register read) |
a4091-nopoll.c |
INQUIRY only |
a4091-blk.c |
READ-only block device |
a4091-wr.c |
READ + WRITE โ production |
a4091-wr-dbg.c |
same as production + A4: printf diagnostics (used to trace the early-boot path) |
Build & install¶
The driver is a standard kernel-relink job (kernel build & install), with the A4091-specific steps:
- Drop the driver
src/a4091-wr.cinto the kernel tree as/usr/sys/amiga/alien/a4091.c. - Register it in the Makefile โ add
a4091.oto theamiga/alienOBJlist (src/kernel-patches/alien-Makefile). - Apply the SCSI-stack patch โ the
scsicard[]row insd.c(src/kernel-patches/sd.c). - Apply the auto-detect patch โ the chipset-gated
autocon()insupport.c(src/kernel-patches/support.c); this is what lets one kernel boot both an A3000 and an A4000+A4091. The why is on Zorro AUTOCONFIG and the boot process. - Build with the clean-gate. The Amix
ldintermittently corrupts the linked kernel (the "D245 boot-breaker"); relink until the checksum recurs rather than trusting a singlemake. Usetools/build-clean-kernel.shand verify withtools/relsim.py/checkunix.cโ full detail on kernel build & install. Nevermake installan unverified kernel onto your working disk โ it rewrites the boot partition and bricks it if the kernel is corrupt. - Install carefully onto a throwaway / clone disk โ see adding drivers to a custom boot disk and the "boot root on the A4091" install recipe on the boot process.
The SCRIPTS source (reference/scripts/inq-wr.ss) is assembled with the NetBSD ncr53cxxx
assembler from the open-source a4091.device project, which
also provides the A4091 autoboot ROM used by the emulator
(Amix on Amiberry).
Status¶
| Claim | Status |
|---|---|
| A4091/53C710 driver detects, reads and writes; root + swap + filesystem on the A4091; multi-user, networking | โ emulation-proven (Amiberry, real Amix 2.1c) |
| One universal kernel boots both A3000(ECS) and A4000(AGA) + A4091 | โ emulation-proven, one binary |
| Bounded-poll / interrupt-driven completion (real disks are not instant) | ๐ก pending โ single-ISTAT completion is an emulation shortcut |
| Real-hardware validation on a physical A3000 and A4000 + A4091 | ๐ก pending |
See also¶
- Zorro AUTOCONFIG for drivers โ
autocon(), the Zorro III /sptallocquestion, and the chipset-gated auto-detection that registers the A3000 SCSI only when present. - Building & installing a kernel โ the
makeโrelocunixโmake bootpartflow and the D245 clean-gate that this driver's build depends on. - The boot process โ booting Amix with root on the A4091, and the rootdev โ card-index device numbering.
- Device list โ the SCSI block/char major numbers and the card/target/slice minor scheme.
- Supported hardware & requirements โ the Zorro II / Zorro III rules the A4091 work pushes against.
- Amix on Amiberry โ the A4091-in-Amiberry config and the A3000(ECS) vs A4000(AGA) Kickstart finding.
- The Amix device-driver model โ the switch-table mechanism underneath the SCSI stack.
Sources¶
- The A4091-on-Amix project โ
NOTES.mdยง1โยง20 (lab notebook, reproduced locally โ ) andsrc//tools/. src/a4091-wr.cโ the 53C710 READ+WRITE driver (A4091_PROD 0x02020054,SIOP_OFF 0x00800000,sptalloc()mapping,inq_script[],a4091queue()); SCRIPTS from the a4091-software repo'sncr53cxxxsource.src/kernel-patches/{sd.c,support.c,alien-Makefile}โ the kernel diffs (thescsicard[]row, the chipset-gatedautocon(), thea4091.oOBJ entry).docs/a4091-53c710-reference.mdin the project repo โ the verified 53C710 register/SCRIPTS detail behind this driver.- a4091.device open-source project: https://github.com/A4091/a4091-software (A4091 ROM +
ncr53cxxxSCRIPTS assembler).