Skip to content

MIPS: PS2: IOP: Fix iop_write*() and actually initialise the DEV9 expansion bay - #94

Closed
hubix94 wants to merge 2 commits into
frno7:ps2-mainfrom
hubix94:ps2-dev9-power-up
Closed

MIPS: PS2: IOP: Fix iop_write*() and actually initialise the DEV9 expansion bay#94
hubix94 wants to merge 2 commits into
frno7:ps2-mainfrom
hubix94:ps2-dev9-power-up

Conversation

@hubix94

@hubix94 hubix94 commented Sep 5, 2026

Copy link
Copy Markdown

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 RPC

iop_rpc_write() sends its { addr, type, data } argument with rpo_get_addr (3) where it needs rpo_set_addr (2). LOADFILE performs a read at addr, discards data, and returns the current value, which comes back as a non-negative "status" -- so every caller that tests err < 0 sees 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 calls iop_write*() yet, which is why it went unnoticed.

2. iop_dev9_init() returns before exp_dev_init()

An early return 0; right after iop_module_request("dev9", ...) leaves the whole SSBUS setup / power-up / reset sequence as dead code.

Measurement

SCPH-30004, PAL, ROM 0150, ps2-main at 77174dd. An out-of-tree probe module replays the exp_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:

IOP DEV9 before: rev 0030 power 0000 reg1462 0000
IOP SSBUS before: 1418 000000ff 141c 001a1055 1420 000510ff
IOP DEV9: running the expansion-bay init sequence
IOP DEV9: init sequence finished
IOP DEV9 after: rev 0030 power 0000 reg1462 0000
IOP SSBUS after: 1418 000000ff 141c 001a1055 1420 000510ff

Same sequence, same console, writing with LOADFILE function 2 (set_addr):

IOP DEV9: running the expansion-bay init sequence
IOP write path: SSBUS read back as written -- writes WORK
IOP DEV9: bay is off, powering up
IOP DEV9 after: rev 0030 power 0005 reg1462 0000
IOP SSBUS after: 1418 e01a3043 141c ef1a3043 1420 00051011
SPEED: rev 0000 rev1 0011 rev3 0003 rev8 0002
EMAC3: soft reset OK
PHY: National Semiconductor DP83846 identified, revision 3
PHY: phystat=0x0715 -> link UP, speed 100M, duplex full, autoneg complete
EEPROM: MAC 00:04:1f:2e:37:1c, checksum stored 4e56 computed 4e56 -> OK

With the cable unplugged the PHY reports link down and anlpar 0000; with it plugged in, link up at 100 Mbit full duplex and anlpar c5e1. Once the bay is powered the EE can also read the SPEED registers directly at 0xb4000000 (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-dev9 loaded by the initramfs rcS), same console, boot log:

[   12.115322] dev9: Expansion device interface
[   12.130391] dev9: Expansion device power on
[   13.180974] dev9: Interface initialized
[   13.193666] dev9: speed: 11 3 2
[   13.204194] rev-test: rev1 11
[   13.214131] rev-test: rev3 3
[   13.223766] rev-test: rev8 2

The probe loaded afterwards without any power-up of its own finds power 0005, SSBUS e01a3043 / 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_ps2 was not exercised in this test.

Happy to test anything further on this console.

🤖 Generated with Claude Code

Hubert Wyrzykiewicz and others added 2 commits September 5, 2026 17:06
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,

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like a valid fix for a typo, in coded which isn’t used, as the commit message says.

Comment thread drivers/ps2/iop-dev9.c
if (err < 0)
return err;

return 0;

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

@hubix94 hubix94 Sep 5, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

obraz :D

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

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/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.

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 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?

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 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.

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!

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants