MIPS: PS2: IOP: Fix iop_write*() and actually initialise the DEV9 expansion bay - #94
MIPS: PS2: IOP: Fix iop_write*() and actually initialise the DEV9 expansion bay#94hubix94 wants to merge 2 commits into
Conversation
iop_rpc_write() sends its { addr, type, data } argument with rpo_get_addr
(3) instead of rpo_set_addr (2). LOADFILE therefore performs a read at
addr, discards data, and returns the current value, which iop_rpc_write()
passes on as a non-negative "status". Every caller that tests for err < 0
sees success while nothing has been written.
Measured on an SCPH-30004 (ROM 0150) by replaying the exp_dev_init()
sequence from iop-dev9.c through iop_writel()/iop_writew() and reading the
registers back over the SIF: with rpo_get_addr all writes "succeed" and
the SSBUS registers 0x1418/0x141c/0x1420 keep their reset values
000000ff/001a1055/000510ff and DEV9 power stays 0000; with rpo_set_addr
the same sequence reads back e01a3043/ef1a3043/00051011 and DEV9 power
becomes 0005, the expansion bay powers up and the SPEED chip answers.
Nothing in the tree besides the dead code in iop_dev9_init() calls the
write helpers yet, which is why this went unnoticed.
Fixes: 2e3a838 ("FIXME: iop_read[bwl] and iop_write[bwl]")
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Hubert Wyrzykiewicz <h.wyrzyk@gmail.com>
iop_dev9_init() returns right after iop_module_request("dev9", ...),
leaving exp_dev_init() -- the SSBUS setup, bay power-up and reset -- as
dead code. The expansion bay therefore stays unpowered and the SPEED chip
(ATA and SMAP Ethernet) is unreachable: a read of its registers from the
EE at 0xb4000000 raises a data bus error.
Remove the early return so the module does what it was written to do.
Together with the iop_rpc_write() fix this brings the bay up at module
load. Measured on an SCPH-30004 (ROM 0150): DEV9 power register 0000 ->
0005, SPEED rev1 0011 rev3 0003 rev8 0002, EMAC3 soft reset completes,
PHY DP83846 rev 3 answers on MII address 1 and reports link up at 100 Mbit
full duplex with a cable plugged in and link down without, the MAC
address reads from the serial EEPROM with a matching checksum, and the
EE can read the SPEED registers directly once the bay is powered.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Hubert Wyrzykiewicz <h.wyrzyk@gmail.com>
| int err; | ||
|
|
||
| err = sif_rpc(&load_file_rpc_client, rpo_get_addr, | ||
| err = sif_rpc(&load_file_rpc_client, rpo_set_addr, |
There was a problem hiding this comment.
This looks like a valid fix for a typo, in coded which isn’t used, as the commit message says.
| if (err < 0) | ||
| return err; | ||
|
|
||
| return 0; |
There was a problem hiding this comment.
I think this module was mostly abandoned, because it wasn’t clear what it was supposed to do. The drivers/ata/pata_ps2.c driver, for example, uses iopmod/module/dev9.c instead.
What are you trying to do with the expansion bay?
There was a problem hiding this comment.
For the expansion bay, I just wanted to plug in an Ethernet cable and connect to the console over SSH. I know there aren’t any drivers yet, but I downloaded the PS2 Linux DVD ISO and extracted the drivers from there. After 2–3 attempts, Claude managed to get it working - I was able to assign an IP address and connect to the PS2 over SSH :)
I know you can also get a USB-to-Ethernet adapter, but I didn’t want to buy anything extra for the PS2.
There was a problem hiding this comment.
Ah, that’s nice. :-) I’m thinking it might be best to postpone pull requests until this network driver is functional and ready to be merged by itself. Does your driver work with the SCPH-700xx models as well? Does it use DMA efficiently, and work with other expansion bay drivers such as the ATA driver?
There was a problem hiding this comment.
Answers to your three questions, plus where the driver stands.
SCPH-700xx: untested. I have one console, an SCPH-30004 with the adapter in the bay. Nothing in the driver is model-aware - the PHY is hardwired to MDIO address 1 and the register map is the adapter's. Probe does refuse cleanly on unexpected hardware: it bails out if SPEED rev1 reads 0000/ffff or rev3 lacks the SMAP capability bit.
DMA: none. Data moves by PIO from the EE through a bounce buffer, where Sony's driver used SmapDmaWrite/SmapDmaRead on the IOP side. Measured over TCP with SSH on top: 2.4 MB/s (~19 Mbit/s) transmit, 1.4 MB/s (~11 Mbit/s) receive, sustained over 300 MB without a stall. Porting the DMA path is the obvious next step for throughput.
Coexistence with pata_ps2: yes. With SMAP up and an SSH session running, modprobe pata_ps2 returns 0 and ata1: PATA max UDMA/66 irq 110 appears; the network keeps running and throughput does not change. ata.irx finds the bay already powered and skips its own power-up sequence, and the interrupts are disjoint - 110 for ATA against 114/115/116 for SMAP.
What the driver is: an EE-side port of Sony's 2.4.17 smap.c onto 5.4 - net_device_ops, NAPI, phylib with its own MDIO bus, the three SPEED interrupts taken through the IOP relay, MAC read from the EEPROM. It survives module reload without a reboot, and it loads from the initramfs at boot, so the console takes a DHCP lease and starts SSH on its own.
One question where your view would save me guessing. The SPEED interrupt mask at offset 0x2a belongs to the IOP (spd_enable_irq/spd_disable_irq), and drivers/ps2/iop-irq.c implements only irq_startup/irq_shutdown, no irq_mask/irq_unmask. An EE-side driver therefore cannot mask its own interrupt for the duration of a NAPI poll without racing the IOP. Is the relay meant to be the long-term path for a bay device, or should a SMAP driver carry an IOP-side counterpart the way Sony's did?
The driver sits on a branch in my fork with the in-tree plumbing done: drivers/net/ethernet/ps2/ with its own Kconfig entry, and the platform device with the register window and three named interrupts in arch/mips/ps2/devices.c. It stays there until you say otherwise - say the word and I turn it into an RFC pull request. The iop_rpc_write() typo fix can go separately as a one-liner.
I guess I'll just close this PR and create a new one with drivers working on SCPH-30004?
There was a problem hiding this comment.
SCPH-700xx: untested. I have one console, an SCPH-30004 with the adapter in the bay. Nothing in the driver is model-aware - the PHY is hardwired to MDIO address 1 and the register map is the adapter's. Probe does refuse cleanly on unexpected hardware: it bails out if SPEED
rev1reads0000/fffforrev3lacks the SMAP capability bit.
My understanding is that SCPH-700xx models have slightly different Ethernet hardware, and therefore there are two separate network drivers for Linux 2.6. I don’t know what the differences are. Having two similar network drivers is awkward, though, so if at all possible, I’m hoping we can combine both into a single driver, but there’s no rush: someone else can adapt it to include SCPH-700xx hardware support later on.
DMA: none. Data moves by PIO from the EE through a bounce buffer, where Sony's driver used
SmapDmaWrite/SmapDmaReadon the IOP side. Measured over TCP with SSH on top: 2.4 MB/s (~19 Mbit/s) transmit, 1.4 MB/s (~11 Mbit/s) receive, sustained over 300 MB without a stall. Porting the DMA path is the obvious next step for throughput.
Nice. If the network driver doesn’t interfere with other drivers, I think it’s a great initial prototype, which can be extended with DMA etc. to improve performance later on. There’s great value in a simple driver, as long as it doesn’t cause problems for other drivers.
One question where your view would save me guessing. The SPEED interrupt mask at offset
0x2abelongs to the IOP (spd_enable_irq/spd_disable_irq), anddrivers/ps2/iop-irq.cimplements onlyirq_startup/irq_shutdown, noirq_mask/irq_unmask. An EE-side driver therefore cannot mask its own interrupt for the duration of a NAPI poll without racing the IOP. Is the relay meant to be the long-term path for a bay device, or should a SMAP driver carry an IOP-side counterpart the way Sony's did?
Long-term I think we should off-load as much driver work from the EE to the IOP as reasonably possible. So I definitely think it should have an IOP counterpart, that does DEV9 initialisation, similar to the ATA driver, handles interrupts as much as possible, and, eventually at a later time, perhaps other kinds of network processing (maybe network packet checksumming and so on?), and also DMA, to reduce the processing burden on the EE.
So, I think we should retire and remove linux/drivers/ps2/iop-dev9.c, which I assume the network driver won’t need once it has its own IOP module counterpart instead?
The IRQ relay module is mostly meant to simplify initial driver development (the USB driver, for example), to quickly and easily get something working, but as you’ve noticed, it has limitations, and for high-quality, high-performance drivers, it’s typically not the best alternative. We could retire and remove it as well, once all drivers have their own IOP modules.
The driver sits on a branch in my fork with the in-tree plumbing done:
drivers/net/ethernet/ps2/with its own Kconfig entry, and the platform device with the register window and three named interrupts inarch/mips/ps2/devices.c. It stays there until you say otherwise - say the word and I turn it into an RFC pull request. Theiop_rpc_write()typo fix can go separately as a one-liner.
Great! At a first glance, I think it looks promising, but I think it should have its IOP module counterpart for DEV9 initialisation, proper interrupt handling, etc.
I guess I'll just close this PR and create a new one with drivers working on SCPH-30004?
Yes, thanks!

Two one-line fixes in
drivers/ps2/that together let the kernel power up the DEV9 expansion bay, which is the precondition for the SPEED chip (ATA, SMAP Ethernet) being reachable at all.1.
iop_rpc_write()issues the read RPCiop_rpc_write()sends its{ addr, type, data }argument withrpo_get_addr(3) where it needsrpo_set_addr(2). LOADFILE performs a read ataddr, discardsdata, and returns the current value, which comes back as a non-negative "status" -- so every caller that testserr < 0sees success while nothing has been written. The commit that added it, 2e3a838, carries "FIXME" in its title, so I assume it was never finished. Nothing else in the tree callsiop_write*()yet, which is why it went unnoticed.2.
iop_dev9_init()returns beforeexp_dev_init()An early
return 0;right afteriop_module_request("dev9", ...)leaves the whole SSBUS setup / power-up / reset sequence as dead code.Measurement
SCPH-30004, PAL, ROM 0150,
ps2-mainat 77174dd. An out-of-tree probe module replays theexp_dev_init()sequence and reads the DEV9 and SSBUS registers back over the SIF.Through the kernel's
iop_writel()/iop_writew()as they are today, every write returns >= 0 and nothing changes:Same sequence, same console, writing with LOADFILE function 2 (
set_addr):With the cable unplugged the PHY reports link down and
anlpar 0000; with it plugged in, link up at 100 Mbit full duplex andanlpar c5e1. Once the bay is powered the EE can also read the SPEED registers directly at0xb4000000(0000 0011 0003 0002, same as from the IOP side); before that the same read raises a data bus error.With this branch
Kernel built from this branch (
ps2_defconfig,iop-dev9loaded by the initramfsrcS), same console, boot log:The probe loaded afterwards without any power-up of its own finds
power 0005, SSBUSe01a3043 / ef1a3043 / 00051011, and reads SPEED, EMAC3, the PHY (link up at 100 Mbit full duplex with the cable in, link down without) and the MAC straight away.pata_ps2was not exercised in this test.Happy to test anything further on this console.
🤖 Generated with Claude Code