From 13aa36a5a62eb2490a8bfdf18c356425cc5ad5df Mon Sep 17 00:00:00 2001 From: Hubert Wyrzykiewicz Date: Sat, 5 Sep 2026 17:06:21 +0200 Subject: [PATCH 1/5] MIPS: PS2: IOP: Fix iop_write{b,w,l}() issuing the LOADFILE read RPC 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: 2e3a8389d92a ("FIXME: iop_read[bwl] and iop_write[bwl]") Co-Authored-By: Claude Fable 5.1 Signed-off-by: Hubert Wyrzykiewicz --- drivers/ps2/iop-module-request.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/ps2/iop-module-request.c b/drivers/ps2/iop-module-request.c index 9f246d051fb7ab..83e758722bb77a 100644 --- a/drivers/ps2/iop-module-request.c +++ b/drivers/ps2/iop-module-request.c @@ -932,7 +932,7 @@ static int iop_rpc_write(const u32 data, s32 status; int err; - err = sif_rpc(&load_file_rpc_client, rpo_get_addr, + err = sif_rpc(&load_file_rpc_client, rpo_set_addr, &arg, sizeof(arg), &status, sizeof(status)); return err < 0 ? err : status; From 7ed4ab540a12b5335b5e95cce0882c32b6ff9a51 Mon Sep 17 00:00:00 2001 From: Hubert Wyrzykiewicz Date: Sat, 5 Sep 2026 17:06:22 +0200 Subject: [PATCH 2/5] MIPS: PS2: IOP: Actually initialise the DEV9 expansion bay 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 Signed-off-by: Hubert Wyrzykiewicz --- drivers/ps2/iop-dev9.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/ps2/iop-dev9.c b/drivers/ps2/iop-dev9.c index a91cdd97023e54..dbebed60ef0e33 100644 --- a/drivers/ps2/iop-dev9.c +++ b/drivers/ps2/iop-dev9.c @@ -252,8 +252,6 @@ static int __init iop_dev9_init(void) if (err < 0) return err; - return 0; - err = iop_dev9_read_rev(&dev9.rev); if (err < 0) { printk("iop_dev9_init: err %d\n", err); From f64debcbc208a3f9157623a9bca64be4dba36668 Mon Sep 17 00:00:00 2001 From: Hubert Wyrzykiewicz Date: Sun, 6 Sep 2026 19:27:53 +0200 Subject: [PATCH 3/5] net: ps2: Add PlayStation 2 SMAP Ethernet driver The SMAP part of the SPEED chip in the PlayStation 2 expansion bay is an IBM EMAC3 MAC with a National DP83846 PHY on MII address 1, a 4 KiB TX FIFO, a 16 KiB RX FIFO and two rings of 64 buffer descriptors, all memory-mapped at physical 0x14000000 on the EE side. The window answers once the DEV9 expansion bay is powered; the driver checks that through the IOP before it touches the window, because an unpowered bay turns every EE access into a data bus error. The register layout, the FIFO and buffer-descriptor protocol, the EMAC3 defaults and the EEPROM bit-bang sequence come from Sony's driver in the PlayStation 2 Linux kit, linux-2.4.17/drivers/ps2/smap.c, GPL v2. The driver structure is new for 5.4: net_device_ops, NAPI, and phylib with its own MDIO bus instead of the original link-check thread. Data moves by programmed I/O, 32-bit accesses through a bounce buffer because skb data is only 2-byte aligned. The IOP-side DMA of the original driver is not ported. Measured on an SCPH-30004 over TCP with SSH on top: 2.4 MB/s transmit and 1.4 MB/s receive, sustained over 300 MB. The three SPEED interrupts reach the EE through the IOP interrupt relay. Because the SPEED interrupt mask register belongs to the IOP, the driver never touches it and acknowledges its sources through SMAP_INTR_CLR, as the original did. It also clears TXDNV and RXDNV, which the IOP side does not acknowledge; left standing they keep the interrupt line asserted and the interface stops receiving interrupts under load. A poll parameter selects interrupt mode with a polling fallback (default), interrupts only, or polling only. The fallback exists because the interrupt relay for a bay device had never been exercised, and an interrupt-only driver that stays silent says nothing about whether the data path works. Signed-off-by: Hubert Wyrzykiewicz Co-Authored-By: Claude Opus 5 --- drivers/net/ethernet/Kconfig | 1 + drivers/net/ethernet/Makefile | 1 + drivers/net/ethernet/ps2/Kconfig | 37 + drivers/net/ethernet/ps2/Makefile | 6 + drivers/net/ethernet/ps2/ps2-smap-regs.h | 343 +++++ drivers/net/ethernet/ps2/ps2-smap.c | 1653 ++++++++++++++++++++++ 6 files changed, 2041 insertions(+) create mode 100644 drivers/net/ethernet/ps2/Kconfig create mode 100644 drivers/net/ethernet/ps2/Makefile create mode 100644 drivers/net/ethernet/ps2/ps2-smap-regs.h create mode 100644 drivers/net/ethernet/ps2/ps2-smap.c diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig index 2d4eb2f280a129..b06f9dc6a1a971 100644 --- a/drivers/net/ethernet/Kconfig +++ b/drivers/net/ethernet/Kconfig @@ -158,6 +158,7 @@ config ETHOC source "drivers/net/ethernet/packetengines/Kconfig" source "drivers/net/ethernet/pasemi/Kconfig" source "drivers/net/ethernet/pensando/Kconfig" +source "drivers/net/ethernet/ps2/Kconfig" source "drivers/net/ethernet/qlogic/Kconfig" source "drivers/net/ethernet/qualcomm/Kconfig" source "drivers/net/ethernet/rdc/Kconfig" diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile index 05abebc17804f4..dbde126f7648cc 100644 --- a/drivers/net/ethernet/Makefile +++ b/drivers/net/ethernet/Makefile @@ -96,3 +96,4 @@ obj-$(CONFIG_NET_VENDOR_XILINX) += xilinx/ obj-$(CONFIG_NET_VENDOR_XIRCOM) += xircom/ obj-$(CONFIG_NET_VENDOR_SYNOPSYS) += synopsys/ obj-$(CONFIG_NET_VENDOR_PENSANDO) += pensando/ +obj-$(CONFIG_NET_VENDOR_PS2) += ps2/ diff --git a/drivers/net/ethernet/ps2/Kconfig b/drivers/net/ethernet/ps2/Kconfig new file mode 100644 index 00000000000000..a8407c3246deeb --- /dev/null +++ b/drivers/net/ethernet/ps2/Kconfig @@ -0,0 +1,37 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# PlayStation 2 network device configuration +# + +config NET_VENDOR_PS2 + bool "PlayStation 2 devices" + default y + depends on SONY_PS2 + help + If you have a network (Ethernet) card belonging to this class, say Y. + + Note that the answer to this question doesn't directly affect the + kernel: saying N will just cause the configurator to skip all the + questions about PlayStation 2 cards. If you say Y, you will be asked + for your specific card in the following questions. + +if NET_VENDOR_PS2 + +config PS2_SMAP + tristate "PlayStation 2 SMAP Ethernet" + depends on SONY_PS2 + select PHYLIB + help + Ethernet support for the SMAP part of the SPEED chip found in the + PlayStation 2 expansion bay network adaptor and, on later models, + soldered to the board. The chip combines an IBM EMAC3 MAC with a + National DP83846 PHY and is reachable from the EE once the DEV9 + expansion bay is powered. + + Data is moved by programmed I/O; the IOP-side DMA of the original + Sony driver is not implemented. + + To compile this driver as a module, choose M here. The module will + be called ps2-smap. + +endif # NET_VENDOR_PS2 diff --git a/drivers/net/ethernet/ps2/Makefile b/drivers/net/ethernet/ps2/Makefile new file mode 100644 index 00000000000000..f9a73a5b574a98 --- /dev/null +++ b/drivers/net/ethernet/ps2/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Makefile for the PlayStation 2 network device drivers. +# + +obj-$(CONFIG_PS2_SMAP) += ps2-smap.o diff --git a/drivers/net/ethernet/ps2/ps2-smap-regs.h b/drivers/net/ethernet/ps2/ps2-smap-regs.h new file mode 100644 index 00000000000000..94c9136de12ffd --- /dev/null +++ b/drivers/net/ethernet/ps2/ps2-smap-regs.h @@ -0,0 +1,343 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * PlayStation 2 SMAP (Ethernet) register definitions + * + * Derived from the original PlayStation 2 Linux kit driver: + * + * linux-2.2.1/drivers/ps2/smap.h + * Copyright (C) 2001 Sony Computer Entertainment Inc. + * GNU General Public License Version 2 + * + * Obtained from the "Linux for PlayStation 2" DISC2, SRPMS/KERNEL_1.RPM + * (kernel-2.2.1_ps2-7.src.rpm). Names are kept identical to the original + * so that the two files can be diffed against each other; only the types + * and the accessor macros were modernised. + * + * The MAC is an IBM EMAC3 core sitting inside the SPEED chip of the + * expansion bay. The PHY is a National Semiconductor DP83846 ("DsPHYTER") + * on MII address 1. + */ + +#ifndef __PS2_SMAP_REGS_H__ +#define __PS2_SMAP_REGS_H__ + +/* + * The SPEED chip is mapped at physical 0x14000000. arch/mips/ps2/memory.c + * calls set_io_port_base(CKSEG1), so inw(SPD_REGBASE + x) resolves to + * 0xb4000000 + x -- exactly the SMAP_BASE the Sony driver used. This is + * also how drivers/ata/pata_ps2.c reaches the same chip. + */ +#define SPD_REGBASE 0x14000000 + +/* SPEED identification, offsets as used by pata_ps2.c and ps2sdk. */ +#define SPD_R_REV 0x00 +#define SPD_R_REV_1 0x02 +#define SPD_R_REV_3 0x04 +#define SPD_R_REV_8 0x0e + +/* + * Capability bits in SPD_R_REV_3. These names come from ps2sdk, not from + * any Sony header we hold, so treat a mismatch here as "unverified" rather + * than as a hardware fault. The raw value is always logged as well. + */ +#define SPD_CAPS_SMAP (1 << 0) +#define SPD_CAPS_ATA (1 << 1) +#define SPD_CAPS_UART (1 << 3) +#define SPD_CAPS_DVR (1 << 4) + +/* SMAP register window, relative to SPD_REGBASE. */ +#define SMAP_BD_BASE 0x3000 +#define SMAP_BD_BASE_TX (SMAP_BD_BASE + 0x0000) +#define SMAP_BD_BASE_RX (SMAP_BD_BASE + 0x0200) +#define SMAP_BD_SIZE 512 +#define SMAP_BD_MAX_ENTRY 64 + +#define SMAP_TXBUFBASE 0x1000 +#define SMAP_TXBUFSIZE (4 * 1024) +#define SMAP_RXBUFBASE 0x4000 +#define SMAP_RXBUFSIZE (16 * 1024) + +/* Serial EEPROM bit-banged through the PIO port. */ +#define SMAP_PIOPORT_DIR 0x2c +#define SMAP_PIOPORT_IN 0x2e +#define SMAP_PIOPORT_OUT 0x2e +#define PP_DOUT (1 << 4) /* data out, read */ +#define PP_DIN (1 << 5) /* data in, write */ +#define PP_SCLK (1 << 6) /* clock, write */ +#define PP_CSEL (1 << 7) /* chip sel, write */ +#define PP_OP_READ 2 /* 2b'10 */ + +/* SMAP-level interrupts (not EMAC3's own). */ +#define SMAP_INTR_STAT 0x28 +#define SMAP_INTR_CLR 0x128 +#define SMAP_INTR_ENABLE 0x2a +#define INTR_EMAC3 (1 << 6) +#define INTR_RXEND (1 << 5) +#define INTR_TXEND (1 << 4) +/* RXDNV and TXDNV mean the descriptor is not valid. */ +#define INTR_RXDNV (1 << 3) +#define INTR_TXDNV (1 << 2) +#define INTR_BITMSK 0x7c + +#define SMAP_BD_MODE 0x102 +#define BD_SWAP (1 << 0) + +#define SMAP_TXFIFO_CTRL 0x1000 +#define TXFIFO_RESET (1 << 0) +#define SMAP_TXFIFO_WR_PTR 0x1004 +#define SMAP_TXFIFO_FRAME_CNT 0x100c +#define SMAP_TXFIFO_FRAME_INC 0x1010 +#define SMAP_TXFIFO_DATA 0x1100 + +#define SMAP_RXFIFO_CTRL 0x1030 +#define RXFIFO_RESET (1 << 0) +#define SMAP_RXFIFO_RD_PTR 0x1034 +#define SMAP_RXFIFO_FRAME_CNT 0x103c +#define SMAP_RXFIFO_FRAME_DEC 0x1040 +#define SMAP_RXFIFO_DATA 0x1200 + +#define SMAP_FIFO_ADDR 0x1300 +#define FIFO_CMD_READ (1 << 1) +#define FIFO_DATA_SWAP (1 << 0) +#define SMAP_FIFO_DATA 0x1308 + +/* + * EMAC3. Every 32-bit EMAC3 register is reached as two 16-bit accesses, + * high half first -- the SPEED bus is 16 bits wide. See emac3_read(). + */ +#define SMAP_EMAC3_BASE 0x2000 +#define SMAP_EMAC3_MODE0 (SMAP_EMAC3_BASE + 0x00) +#define E3_RXMAC_IDLE (1 << 31) +#define E3_TXMAC_IDLE (1 << 30) +#define E3_SOFT_RESET (1 << 29) +#define E3_TXMAC_ENABLE (1 << 28) +#define E3_RXMAC_ENABLE (1 << 27) + +#define SMAP_EMAC3_MODE1 (SMAP_EMAC3_BASE + 0x04) +#define E3_FDX_ENABLE (1 << 31) +#define E3_IGNORE_SQE (1 << 24) +#define E3_MEDIA_10M (0 << 22) +#define E3_MEDIA_100M (1 << 22) +#define E3_MEDIA_MSK (3 << 22) +#define E3_RXFIFO_2K (2 << 20) +#define E3_TXFIFO_1K (1 << 18) +#define E3_TXREQ0_SINGLE (0 << 15) +#define E3_TXREQ1_SINGLE (0 << 13) + +#define SMAP_EMAC3_TxMODE0 (SMAP_EMAC3_BASE + 0x08) +#define SMAP_EMAC3_TxMODE1 (SMAP_EMAC3_BASE + 0x0c) +#define SMAP_EMAC3_RxMODE (SMAP_EMAC3_BASE + 0x10) +#define E3_RX_STRIP_PAD (1 << 31) +#define E3_RX_STRIP_FCS (1 << 30) +#define E3_RX_PROMISC (1 << 24) +#define E3_RX_INDIVID_ADDR (1 << 22) +#define E3_RX_BCAST (1 << 20) +#define E3_RX_MCAST (1 << 19) + +#define SMAP_EMAC3_INTR_STAT (SMAP_EMAC3_BASE + 0x14) +#define SMAP_EMAC3_INTR_ENABLE (SMAP_EMAC3_BASE + 0x18) +#define SMAP_EMAC3_ADDR_HI (SMAP_EMAC3_BASE + 0x1c) +#define SMAP_EMAC3_ADDR_LO (SMAP_EMAC3_BASE + 0x20) +#define SMAP_EMAC3_PAUSE_TIMER (SMAP_EMAC3_BASE + 0x2c) +#define SMAP_EMAC3_INTER_FRAME_GAP (SMAP_EMAC3_BASE + 0x58) + +#define SMAP_EMAC3_STA_CTRL (SMAP_EMAC3_BASE + 0x5c) +#define E3_PHY_DATA_MSK 0xffff +#define E3_PHY_DATA_BITSFT 16 +#define E3_PHY_OP_COMP (1 << 15) /* operation complete */ +#define E3_PHY_ERR_READ (1 << 14) +#define E3_PHY_READ (1 << 12) +#define E3_PHY_WRITE (2 << 12) +#define E3_PHY_50M (0 << 10) +#define E3_PHY_ADDR_MSK 0x1f +#define E3_PHY_ADDR_BITSFT 5 +#define E3_PHY_REG_ADDR_MSK 0x1f + +#define SMAP_EMAC3_TX_THRESHOLD (SMAP_EMAC3_BASE + 0x60) +#define SMAP_EMAC3_RX_WATERMARK (SMAP_EMAC3_BASE + 0x64) +#define SMAP_EMAC3_TX_OCTETS (SMAP_EMAC3_BASE + 0x68) +#define SMAP_EMAC3_RX_OCTETS (SMAP_EMAC3_BASE + 0x6c) + +/* National Semiconductor DP83846 "DsPHYTER". */ +#define NS_OUI 0x080017 +#define DsPHYTER_ADDRESS 0x1 + +#define DsPHYTER_BMCR 0x00 +#define PHY_BMCR_RST (1 << 15) +#define PHY_BMCR_100M (1 << 13) +#define PHY_BMCR_ANEN (1 << 12) +#define PHY_BMCR_PWDN (1 << 11) +#define PHY_BMCR_RSAN (1 << 9) +#define PHY_BMCR_DUPM (1 << 8) + +#define DsPHYTER_BMSR 0x01 +#define PHY_BMSR_ANCP (1 << 5) /* autoneg complete */ +#define PHY_BMSR_LINK (1 << 2) /* link status */ + +#define DsPHYTER_PHYIDR1 0x02 +#define PHY_IDR1_VAL (((NS_OUI << 2) >> 8) & 0xffff) +#define DsPHYTER_PHYIDR2 0x03 +#define PHY_IDR2_VMDL 0x2 +#define PHY_IDR2_VAL ((((NS_OUI << 10) & 0xfc00)) | \ + (((PHY_IDR2_VMDL << 4) & 0x3f0))) +#define PHY_IDR2_MSK 0xfff0 +#define PHY_IDR2_REV_MSK 0x000f + +#define DsPHYTER_ANAR 0x04 +#define DsPHYTER_ANLPAR 0x05 + +/* Extended registers. */ +#define DsPHYTER_PHYSTS 0x10 +#define PHY_STS_ANCP (1 << 4) /* autoneg complete */ +#define PHY_STS_LPBK (1 << 3) /* loopback */ +#define PHY_STS_DUPS (1 << 2) /* 1:FDX 0:HDX */ +#define PHY_STS_SPDS (1 << 1) /* 1:10M 0:100M */ +#define PHY_STS_LINK (1 << 0) /* link status */ + +#define SMAP_LOOP_COUNT 10000 + +/* + * Additions for the driver (as opposed to the probe), all transcribed from + * linux-2.4.17/drivers/ps2/smap.h of the BlackRhino kit, same origin and + * licence as above. + */ + +/* Frame sizes. */ +#define SMAP_TXMAXSIZE (6 + 6 + 2 + 1500) +#define SMAP_RXMAXSIZE (6 + 6 + 2 + 1500 + 4) +#define SMAP_RXMINSIZE 14 + +/* Buffer descriptors: 8 bytes each, 64 per ring, 16-bit fields. */ +#define SMAP_BD_CTRL_STAT 0x0 +#define SMAP_BD_RESERVED 0x2 +#define SMAP_BD_LENGTH 0x4 +#define SMAP_BD_POINTER 0x6 +#define SMAP_BD_ENTRY_SIZE 8 + +/* TX control / status */ +/* Set by the driver, cleared by the hardware. */ +#define SMAP_BD_TX_READY (1 << 15) +#define SMAP_BD_TX_GENFCS (1 << 9) /* generate FCS */ +#define SMAP_BD_TX_GENPAD (1 << 8) /* generate padding */ +#define SMAP_BD_TX_INSSA (1 << 7) +#define SMAP_BD_TX_RPLSA (1 << 6) +#define SMAP_BD_TX_INSVLAN (1 << 5) +#define SMAP_BD_TX_RPLVLAN (1 << 4) +#define SMAP_BD_TX_BADFCS (1 << 9) /* status meanings */ +#define SMAP_BD_TX_BADPKT (1 << 8) +#define SMAP_BD_TX_LOSSCR (1 << 7) +#define SMAP_BD_TX_EDEFER (1 << 6) +#define SMAP_BD_TX_ECOLL (1 << 5) +#define SMAP_BD_TX_LCOLL (1 << 4) +#define SMAP_BD_TX_MCOLL (1 << 3) +#define SMAP_BD_TX_SCOLL (1 << 2) +#define SMAP_BD_TX_UNDERRUN (1 << 1) +#define SMAP_BD_TX_SQE (1 << 0) + +/* RX control / status */ +/* Set by the driver, cleared by the hardware. */ +#define SMAP_BD_RX_EMPTY (1 << 15) +#define SMAP_BD_RX_OVERRUN (1 << 9) +#define SMAP_BD_RX_PFRM (1 << 8) +#define SMAP_BD_RX_BADFRM (1 << 7) +#define SMAP_BD_RX_RUNTFRM (1 << 6) +#define SMAP_BD_RX_SHORTEVNT (1 << 5) +#define SMAP_BD_RX_ALIGNERR (1 << 4) +#define SMAP_BD_RX_BADFCS (1 << 3) +#define SMAP_BD_RX_FRMTOOLONG (1 << 2) +#define SMAP_BD_RX_OUTRANGE (1 << 1) +#define SMAP_BD_RX_INRANGE (1 << 0) + +#define SMAP_DMA_MODE 0x24 + +/* EMAC3 MODE1, the rest of it. */ +#define E3_INLPBK_ENABLE (1 << 30) +#define E3_VLAN_ENABLE (1 << 29) +#define E3_FLOWCTRL_ENABLE (1 << 28) +#define E3_ALLOW_PF (1 << 27) +#define E3_ALLOW_EXTMNGIF (1 << 25) +#define E3_TXREQ0_MULTI (1 << 15) +#define E3_TXREQ0_DEPEND (2 << 15) +#define E3_TXREQ1_MULTI (1 << 13) +#define E3_JUMBO_ENABLE (1 << 12) +#define SMAP_EMAC3_MODE1_DEF (E3_FDX_ENABLE | E3_IGNORE_SQE | \ + E3_MEDIA_100M | E3_RXFIFO_2K | \ + E3_TXFIFO_1K | E3_TXREQ0_MULTI | \ + E3_TXREQ1_SINGLE) + +/* EMAC3 TxMODE0 */ +#define E3_TX_GNP_0 (1 << 31) /* get new packet */ +#define E3_TX_GNP_1 (1 << 30) +#define E3_TX_GNP_DEPEND (1 << 29) +#define E3_TX_FIRST_CHANNEL (1 << 28) + +/* EMAC3 TxMODE1 */ +#define E3_TX_LOW_REQ_MSK 0x1f +#define E3_TX_LOW_REQ_BITSFT 27 +#define E3_TX_URG_REQ_MSK 0xff +#define E3_TX_URG_REQ_BITSFT 16 + +/* EMAC3 RxMODE, the rest of it. */ +#define E3_RX_RX_RUNT_FRAME (1 << 29) +#define E3_RX_RX_FCS_ERR (1 << 28) +#define E3_RX_RX_TOO_LONG_ERR (1 << 27) +#define E3_RX_RX_IN_RANGE_ERR (1 << 26) +#define E3_RX_PROP_PF (1 << 25) +#define E3_RX_PROMISC_MCAST (1 << 23) +#define E3_RX_INDIVID_HASH (1 << 21) + +/* EMAC3 interrupt status / enable */ +/* E3_INTR_OVERRUN does not work, per Sony. */ +#define E3_INTR_OVERRUN (1 << 25) +#define E3_INTR_PF (1 << 24) +#define E3_INTR_BAD_FRAME (1 << 23) +#define E3_INTR_RUNT_FRAME (1 << 22) +#define E3_INTR_SHORT_EVENT (1 << 21) +#define E3_INTR_ALIGN_ERR (1 << 20) +#define E3_INTR_BAD_FCS (1 << 19) +#define E3_INTR_TOO_LONG (1 << 18) +#define E3_INTR_OUT_RANGE_ERR (1 << 17) +#define E3_INTR_IN_RANGE_ERR (1 << 16) +#define E3_INTR_DEAD_DEPEND (1 << 9) +#define E3_INTR_DEAD_0 (1 << 8) +#define E3_INTR_SQE_ERR_0 (1 << 7) +#define E3_INTR_TX_ERR_0 (1 << 6) +#define E3_INTR_DEAD_1 (1 << 5) +#define E3_INTR_SQE_ERR_1 (1 << 4) +#define E3_INTR_TX_ERR_1 (1 << 3) +#define E3_INTR_MMAOP_SUCCESS (1 << 1) +#define E3_INTR_MMAOP_FAIL (1 << 0) +#define E3_INTR_ALL (E3_INTR_OVERRUN | E3_INTR_PF | \ + E3_INTR_BAD_FRAME | \ + E3_INTR_RUNT_FRAME | \ + E3_INTR_SHORT_EVENT | \ + E3_INTR_ALIGN_ERR | \ + E3_INTR_BAD_FCS | E3_INTR_TOO_LONG | \ + E3_INTR_OUT_RANGE_ERR | \ + E3_INTR_IN_RANGE_ERR | \ + E3_INTR_DEAD_DEPEND | \ + E3_INTR_DEAD_0 | \ + E3_INTR_SQE_ERR_0 | \ + E3_INTR_TX_ERR_0 | \ + E3_INTR_DEAD_1 | E3_INTR_SQE_ERR_1 | \ + E3_INTR_TX_ERR_1 | \ + E3_INTR_MMAOP_SUCCESS | \ + E3_INTR_MMAOP_FAIL) +#define E3_DEAD_ALL (E3_INTR_DEAD_DEPEND | \ + E3_INTR_DEAD_0 | E3_INTR_DEAD_1) + +/* EMAC3 TX threshold and RX watermark fields. */ +#define E3_TX_THRESHLD_MSK 0x1f +#define E3_TX_THRESHLD_BITSFT 27 +#define E3_RX_LO_WATER_MSK 0x1ff +#define E3_RX_LO_WATER_BITSFT 23 +#define E3_RX_HI_WATER_MSK 0x1ff +#define E3_RX_HI_WATER_BITSFT 7 + +/* More EMAC3 registers. */ +#define SMAP_EMAC3_GROUP_HASH1 (SMAP_EMAC3_BASE + 0x40) +#define SMAP_EMAC3_GROUP_HASH2 (SMAP_EMAC3_BASE + 0x44) +#define SMAP_EMAC3_GROUP_HASH3 (SMAP_EMAC3_BASE + 0x48) +#define SMAP_EMAC3_GROUP_HASH4 (SMAP_EMAC3_BASE + 0x4c) + +#endif /* __PS2_SMAP_REGS_H__ */ diff --git a/drivers/net/ethernet/ps2/ps2-smap.c b/drivers/net/ethernet/ps2/ps2-smap.c new file mode 100644 index 00000000000000..cc2303f099cb19 --- /dev/null +++ b/drivers/net/ethernet/ps2/ps2-smap.c @@ -0,0 +1,1653 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PlayStation 2 SMAP Ethernet driver -- EE side, programmed I/O + * + * Copyright (C) 2026 Hubert Wyrzykiewicz + * + * Derived from the original PlayStation 2 Linux kit driver: + * + * linux-2.4.17/drivers/ps2/smap.c + * Copyright (C) 2001, 2002 Sony Computer Entertainment Inc. + * GNU General Public License Version 2 + * + * (BlackRhino GNU/Linux kit), itself a descendant of the 2.2.1 driver on + * "Linux for PlayStation 2" DISC2. The register layout, the FIFO and + * buffer-descriptor protocol, the EMAC3 default values and the EEPROM + * bit-bang sequence are Sony's. The driver structure -- net_device_ops, + * NAPI, phylib instead of a link-check thread -- is new for Linux 5.4. + * + * Hardware. The SPEED chip in the expansion bay sits at physical + * 0x14000000 on the EE side. It contains an IBM EMAC3 MAC with a National + * DP83846 PHY on MII address 1, a 4 KiB TX FIFO, a 16 KiB RX FIFO and two + * rings of 64 buffer descriptors, all memory-mapped. The EE reaches all of + * it directly once the bay is powered, which drivers/ps2/iop-dev9.c does at + * module load (without that every EE access raises a data bus error). The + * platform device itself, with the register window and the three relayed + * interrupts, is registered by arch/mips/ps2/devices.c. SPEED interrupts + * arrive on the IOP and are relayed to the EE by iopmod's irqrelay as + * IRQ_IOP_SPD_*; the IOP side demultiplexes the SPEED interrupt status + * register, the EE side clears the sources through SMAP_INTR_CLR, as Sony's + * driver did. + * + * Data moves by PIO: 32-bit reads and writes of the FIFO data ports from + * the EE, through a bounce buffer because skb data is only 2-byte aligned. + * The IOP-side DMA that Sony's DMA relay module provided is not ported; + * DHCP and SSH do not need it. + * + * Measured on an SCPH-30004 (ROM 0150) with a probe module before this + * driver was written: SPEED rev1 0011 rev3 0003, EMAC3 soft reset OK, PHY + * id 2000/5c23 (DP83846 rev 3), link up at 100 Mbit full duplex with a + * cable, MAC from the EEPROM with a valid checksum. + * + * Poll-mode fallback. The one thing above that no measurement covers is + * the interrupt path: the SPEED virtual IRQs have never been delivered to + * the EE on this console, because pata_ps2 registers IRQ_IOP_SPD_ATA0 but + * with no disk in the bay nothing ever fires it. If the relay turns out + * not to work, an interrupt-only driver does nothing at all and says + * nothing about whether the data path is sound. So a timer watches for + * work that arrived without an interrupt, and switches the driver to + * polling when it finds any; see the poll parameter. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include "ps2-smap-regs.h" + +#define DRV_NAME "ps2-smap" +#define DRV_VERSION "8" + +/* DEV9 power register on the IOP, checked before the first EE access. */ +#define IOP_DEV9_POWER 0xbf80146c +#define DEV9_POWER_ON 0x4 + +/* Keep two descriptors in hand, as Sony did. */ +#define SMAP_TXBD_LIMIT (SMAP_BD_MAX_ENTRY - 2) + +/* Bounce buffers: one maximum frame, whole 32-bit words. */ +#define SMAP_BOUNCE_SIZE ALIGN(SMAP_RXMAXSIZE, 4) + +/* Everything the EE ever clears in SMAP_INTR_CLR. */ +#define SMAP_INTR_ALL (INTR_EMAC3 | INTR_RXEND | INTR_TXEND | \ + INTR_RXDNV | INTR_TXDNV) + +/* Interrupts we actually take: frame done in each direction, MAC errors. */ +#define SMAP_INTR_USED (INTR_RXEND | INTR_TXEND | INTR_EMAC3) + +/* + * EMAC3 events worth an interrupt: a dead transmitter and TX errors, the + * set Open PS2 Loader's smap module uses. RX errors are already visible + * in the descriptor status and are counted there. + */ +#define SMAP_E3_INTR_USED (E3_DEAD_ALL | E3_INTR_TX_ERR_0 | \ + E3_INTR_SQE_ERR_0 | E3_INTR_TX_ERR_1 | \ + E3_INTR_SQE_ERR_1) + +#define SMAP_TX_ERR_MASK (SMAP_BD_TX_BADFCS | SMAP_BD_TX_BADPKT | \ + SMAP_BD_TX_LOSSCR | SMAP_BD_TX_EDEFER | \ + SMAP_BD_TX_ECOLL | SMAP_BD_TX_LCOLL | \ + SMAP_BD_TX_UNDERRUN | SMAP_BD_TX_SQE) + +/* + * Poll-mode fallback. + * + * poll=0 interrupts only, fail visibly if the relay is dead + * poll=1 interrupts, with an automatic switch to polling (default) + * poll=2 polling from the start, no interrupts requested at all + * + * The watchdog runs while the interface is up and only trips on proof: a + * descriptor the hardware has finished with while not one interrupt has + * been delivered. A quiet network never trips it. + */ +enum { + SMAP_POLL_IRQ = 0, + SMAP_POLL_AUTO = 1, + SMAP_POLL_ONLY = 2, +}; + +static int poll_mode = SMAP_POLL_AUTO; +module_param_named(poll, poll_mode, int, 0444); +MODULE_PARM_DESC(poll, "0 = interrupts only, 1 = interrupts with automatic fallback to polling (default), 2 = polling only"); + +static int poll_ms = 10; +module_param_named(poll_ms, poll_ms, int, 0444); +MODULE_PARM_DESC(poll_ms, "polling interval in ms once polling (default 10)"); + +#define SMAP_WATCHDOG_MS 500 + +/* + * Watchdog ticks with work pending and no interrupt before we give up on the + * interrupt path. More than one, so that a descriptor which completed just + * before a tick is not mistaken for a dead relay. + */ +#define SMAP_STALL_TICKS 3 + +#define SMAP_RX_ERR_MASK (SMAP_BD_RX_OVERRUN | SMAP_BD_RX_PFRM | \ + SMAP_BD_RX_BADFRM | SMAP_BD_RX_RUNTFRM | \ + SMAP_BD_RX_SHORTEVNT | SMAP_BD_RX_ALIGNERR | \ + SMAP_BD_RX_BADFCS | SMAP_BD_RX_FRMTOOLONG | \ + SMAP_BD_RX_OUTRANGE | SMAP_BD_RX_INRANGE) + +struct smap_priv { + struct net_device *ndev; + struct platform_device *pdev; + void __iomem *base; + + /* + * Protects the SMAP interrupt enable register, the TX ring state + * and the FIFO write pointer, and MAC enable/disable sequences. + * Taken from hard IRQ context, so always irqsave elsewhere. + */ + spinlock_t lock; + + struct napi_struct napi; + + struct mii_bus *mii; + struct phy_device *phydev; + u32 mode1; /* EMAC3 MODE1 as currently programmed */ + bool link; + + /* TX ring: FIFO write offset, free FIFO bytes, descriptor indices. */ + u16 txbwp; + int txfree; + int txbds; /* next descriptor to fill */ + int txbdi; /* next descriptor to reclaim */ + int txused; + u32 *txbuf; + + /* RX ring. */ + int rxbdi; + u32 *rxbuf; + + u8 ppwc; /* shadow of the write-only EEPROM port */ + + int irq_rx, irq_tx, irq_emac3; + bool irqs_requested; + + /* Poll-mode fallback. */ + struct timer_list timer; + bool polling; + unsigned int irq_count; + unsigned int irq_count_seen; /* irq_count at the previous tick */ + unsigned int stall_ticks; /* ticks with work, no interrupt */ + unsigned int dnv_events; /* descriptor-not-valid bits cleared */ + unsigned long poll_period; + + u32 emac3_events; +}; + +/* -------------------------------------------------------- register access */ + +static inline u8 smap_r8(struct smap_priv *p, u32 off) +{ + return readb(p->base + off); +} + +static inline u16 smap_r16(struct smap_priv *p, u32 off) +{ + return readw(p->base + off); +} + +static inline u32 smap_r32(struct smap_priv *p, u32 off) +{ + return readl(p->base + off); +} + +static inline void smap_w8(struct smap_priv *p, u32 off, u8 v) +{ + writeb(v, p->base + off); +} + +static inline void smap_w16(struct smap_priv *p, u32 off, u16 v) +{ + writew(v, p->base + off); +} + +static inline void smap_w32(struct smap_priv *p, u32 off, u32 v) +{ + writel(v, p->base + off); +} + +/* EMAC3 is a 32-bit core behind a 16-bit bus: high half first, as Sony. */ +static u32 emac3_read(struct smap_priv *p, u32 off) +{ + u16 hi = smap_r16(p, off); + u16 lo = smap_r16(p, off + 2); + + return ((u32)hi << 16) | lo; +} + +static void emac3_write(struct smap_priv *p, u32 off, u32 v) +{ + smap_w16(p, off, (v >> 16) & 0xffff); + smap_w16(p, off + 2, v & 0xffff); +} + +/* Buffer descriptors live in SPEED memory as four 16-bit words each. */ +#define TXBD(i, f) (SMAP_BD_BASE_TX + (i) * SMAP_BD_ENTRY_SIZE + (f)) +#define RXBD(i, f) (SMAP_BD_BASE_RX + (i) * SMAP_BD_ENTRY_SIZE + (f)) + +/* Called with p->lock held. */ +static void smap_intr_set(struct smap_priv *p, u16 bits, bool on) +{ + u16 v = smap_r16(p, SMAP_INTR_ENABLE); + + v = on ? (v | bits) : (v & ~bits); + smap_w16(p, SMAP_INTR_ENABLE, v); +} + +/* ------------------------------------------------------------------ MDIO */ + +static int smap_mdio_wait(struct smap_priv *p) +{ + int i; + + for (i = 0; i < 1000; i++) { + if (emac3_read(p, SMAP_EMAC3_STA_CTRL) & E3_PHY_OP_COMP) + return 0; + udelay(10); + } + + return -ETIMEDOUT; +} + +static int __smap_mdio_read(struct smap_priv *p, int addr, int reg) +{ + u32 sta; + int err; + + err = smap_mdio_wait(p); + if (err) + return err; + + emac3_write(p, SMAP_EMAC3_STA_CTRL, + E3_PHY_READ | E3_PHY_50M | + ((addr & E3_PHY_ADDR_MSK) << E3_PHY_ADDR_BITSFT) | + (reg & E3_PHY_REG_ADDR_MSK)); + + err = smap_mdio_wait(p); + if (err) + return err; + + /* + * Sony re-reads here: "it may be needed to re-read to get correct + * phy data". + */ + sta = emac3_read(p, SMAP_EMAC3_STA_CTRL); + if (sta & E3_PHY_ERR_READ) + return -EIO; + + return (sta >> E3_PHY_DATA_BITSFT) & E3_PHY_DATA_MSK; +} + +static int __smap_mdio_write(struct smap_priv *p, int addr, int reg, u16 val) +{ + int err; + + err = smap_mdio_wait(p); + if (err) + return err; + + emac3_write(p, SMAP_EMAC3_STA_CTRL, + ((u32)val << E3_PHY_DATA_BITSFT) | + E3_PHY_WRITE | E3_PHY_50M | + ((addr & E3_PHY_ADDR_MSK) << E3_PHY_ADDR_BITSFT) | + (reg & E3_PHY_REG_ADDR_MSK)); + + return smap_mdio_wait(p); +} + +static int smap_mdio_read(struct mii_bus *bus, int addr, int reg) +{ + return __smap_mdio_read(bus->priv, addr, reg); +} + +static int smap_mdio_write(struct mii_bus *bus, int addr, int reg, u16 val) +{ + return __smap_mdio_write(bus->priv, addr, reg, val); +} + +/* + * Take the PHY out of power-down if it is there. + * + * phy_disconnect() -> phy_detach() -> phy_suspend() -> genphy_suspend() sets + * BMCR_PDOWN, so every unload of this module leaves the DP83846 powered down. + * EMAC3's soft reset needs the clock the PHY supplies, and with the PHY + * asleep the reset never completes: measured as "EMAC3 soft reset did not + * complete" and "probe of ps2-smap failed with error -145" (ETIMEDOUT on + * MIPS) on every reload, and never on a cold boot, where the PHY is running. + * + * Talks to the PHY over raw MDIO because it is needed in two places where + * phylib is not around: before the soft reset in probe, and after + * phy_disconnect() at teardown. + * + * Returns 1 if the PHY was asleep and has been woken, 0 if it was already + * awake, and a negative error if MDIO did not answer. Version 9 returned + * false for the last two cases alike, which are opposite diagnoses; the + * callers now log what actually happened, because on hardware it turned out + * to be the MDIO error every time. + */ +static int smap_phy_wake(struct smap_priv *p) +{ + int bmcr = __smap_mdio_read(p, DsPHYTER_ADDRESS, MII_BMCR); + + if (bmcr < 0) + return bmcr; + + if (!(bmcr & BMCR_PDOWN)) + return 0; + + __smap_mdio_write(p, DsPHYTER_ADDRESS, MII_BMCR, bmcr & ~BMCR_PDOWN); + /* The DP83846 wants a moment before it clocks again. */ + msleep(20); + + return 1; +} + +/* ---------------------------------------------------------------- EEPROM */ + +/* + * 93C46-style serial EEPROM bit-banged through the SMAP PIO port, holding + * the MAC address in words 0-2 and their sum in word 3. Straight from + * Sony's smap.c; only the accessors changed. + */ +static void ee_clk(struct smap_priv *p, int c) +{ + p->ppwc = c ? (p->ppwc | PP_SCLK) : (p->ppwc & ~PP_SCLK); + smap_w8(p, SMAP_PIOPORT_OUT, p->ppwc); +} + +static void ee_set_d(struct smap_priv *p, int d) +{ + p->ppwc = d ? (p->ppwc | PP_DIN) : (p->ppwc & ~PP_DIN); +} + +static void ee_set_s(struct smap_priv *p, int s) +{ + p->ppwc = s ? (p->ppwc | PP_CSEL) : (p->ppwc & ~PP_CSEL); +} + +static void ee_clock_out(struct smap_priv *p, int val) +{ + ee_set_d(p, val); + ee_clk(p, 0); + udelay(1); + ee_clk(p, 1); + udelay(1); + ee_clk(p, 0); + udelay(1); +} + +static int ee_clock_in(struct smap_priv *p) +{ + int r; + + ee_set_d(p, 0); + ee_clk(p, 0); + udelay(1); + ee_clk(p, 1); + udelay(1); + r = (smap_r8(p, SMAP_PIOPORT_IN) >> 4) & 1; + ee_clk(p, 0); + udelay(1); + + return r; +} + +static void ee_read_words(struct smap_priv *p, u8 addr, u16 *data, int n) +{ + int i; + + smap_w8(p, SMAP_PIOPORT_DIR, PP_SCLK | PP_CSEL | PP_DIN); + + ee_set_s(p, 0); + ee_set_d(p, 0); + ee_clk(p, 0); + udelay(1); + + ee_set_s(p, 1); + ee_set_d(p, 0); + ee_clk(p, 0); + udelay(1); + + ee_clock_out(p, 1); /* start bit */ + ee_clock_out(p, (PP_OP_READ >> 1) & 1); /* op code */ + ee_clock_out(p, PP_OP_READ & 1); + + addr &= 0x3f; + for (i = 0; i < 6; i++) { + ee_clock_out(p, (addr & 0x20) ? 1 : 0); + addr <<= 1; + } + + while (n--) { + u16 w = 0; + + for (i = 0; i < 16; i++) { + w <<= 1; + w |= ee_clock_in(p); + } + *data++ = w; + } + + ee_set_s(p, 0); + ee_set_d(p, 0); + ee_clk(p, 0); + udelay(2); +} + +static int smap_read_mac(struct smap_priv *p, u8 *mac) +{ + unsigned long flags; + u16 raw[3], cksum, sum = 0; + int i; + + spin_lock_irqsave(&p->lock, flags); + ee_read_words(p, 0x0, raw, 3); + ee_read_words(p, 0x3, &cksum, 1); + spin_unlock_irqrestore(&p->lock, flags); + + for (i = 0; i < 3; i++) { + sum += raw[i]; + mac[2 * i] = raw[i] & 0xff; + mac[2 * i + 1] = raw[i] >> 8; + } + + return sum == cksum ? 0 : -EINVAL; +} + +/* -------------------------------------------------------------- hardware */ + +static int smap_wait_clear8(struct smap_priv *p, u32 off, u8 bit) +{ + int i; + + for (i = 0; i < 10000; i++) { + if (!(smap_r8(p, off) & bit)) + return 0; + udelay(1); + } + + return -ETIMEDOUT; +} + +static int smap_fifo_reset(struct smap_priv *p) +{ + int err = 0; + + smap_w8(p, SMAP_TXFIFO_CTRL, TXFIFO_RESET); + smap_w8(p, SMAP_RXFIFO_CTRL, RXFIFO_RESET); + + if (smap_wait_clear8(p, SMAP_TXFIFO_CTRL, TXFIFO_RESET)) { + netdev_err(p->ndev, "TX FIFO reset did not complete\n"); + err = -ETIMEDOUT; + } + if (smap_wait_clear8(p, SMAP_RXFIFO_CTRL, RXFIFO_RESET)) { + netdev_err(p->ndev, "RX FIFO reset did not complete\n"); + err = -ETIMEDOUT; + } + + return err; +} + +static int smap_emac3_soft_reset(struct smap_priv *p) +{ + int i; + + emac3_write(p, SMAP_EMAC3_MODE0, E3_SOFT_RESET); + for (i = 0; i < 10000; i++) { + if (!(emac3_read(p, SMAP_EMAC3_MODE0) & E3_SOFT_RESET)) + return 0; + udelay(1); + } + + return -ETIMEDOUT; +} + +/* Called with p->lock held. */ +static void smap_mac_enable(struct smap_priv *p) +{ + emac3_write(p, SMAP_EMAC3_MODE0, E3_TXMAC_ENABLE | E3_RXMAC_ENABLE); +} + +/* Called with p->lock held. Waits for both MACs to go idle, as Sony did. */ +static void smap_mac_disable(struct smap_priv *p) +{ + u32 v; + int i; + + v = emac3_read(p, SMAP_EMAC3_MODE0); + v &= ~(E3_TXMAC_ENABLE | E3_RXMAC_ENABLE); + emac3_write(p, SMAP_EMAC3_MODE0, v); + + for (i = 0; i < 10000; i++) { + v = emac3_read(p, SMAP_EMAC3_MODE0); + if ((v & E3_RXMAC_IDLE) && (v & E3_TXMAC_IDLE)) + return; + udelay(1); + } + + netdev_warn(p->ndev, + "EMAC3 still running after disable (mode0 %08x)\n", v); +} + +static void smap_emac3_set_defaults(struct smap_priv *p) +{ + const u8 *mac = p->ndev->dev_addr; + + emac3_write(p, SMAP_EMAC3_ADDR_HI, (mac[0] << 8) | mac[1]); + emac3_write(p, SMAP_EMAC3_ADDR_LO, + ((u32)mac[2] << 24) | ((u32)mac[3] << 16) | + ((u32)mac[4] << 8) | mac[5]); + + emac3_write(p, SMAP_EMAC3_INTER_FRAME_GAP, 4); + + emac3_write(p, SMAP_EMAC3_RxMODE, + E3_RX_STRIP_PAD | E3_RX_STRIP_FCS | + E3_RX_INDIVID_ADDR | E3_RX_BCAST); + + /* TX FIFO request priorities: low 7*8 = 56, urgent 15*8 = 120. */ + emac3_write(p, SMAP_EMAC3_TxMODE1, + (7 << E3_TX_LOW_REQ_BITSFT) | (15 << E3_TX_URG_REQ_BITSFT)); + + /* TX threshold (12+1)*64 = 832 bytes. */ + emac3_write(p, SMAP_EMAC3_TX_THRESHOLD, + (12 & E3_TX_THRESHLD_MSK) << E3_TX_THRESHLD_BITSFT); + + /* RX watermarks: low 16*8 = 128, high 128*8 = 1024. */ + emac3_write(p, SMAP_EMAC3_RX_WATERMARK, + ((16 & E3_RX_LO_WATER_MSK) << E3_RX_LO_WATER_BITSFT) | + ((128 & E3_RX_HI_WATER_MSK) << E3_RX_HI_WATER_BITSFT)); +} + +static void smap_bd_init(struct smap_priv *p) +{ + int i; + + for (i = 0; i < SMAP_BD_MAX_ENTRY; i++) { + smap_w16(p, TXBD(i, SMAP_BD_CTRL_STAT), 0); + smap_w16(p, TXBD(i, SMAP_BD_RESERVED), 0); + smap_w16(p, TXBD(i, SMAP_BD_LENGTH), 0); + smap_w16(p, TXBD(i, SMAP_BD_POINTER), 0); + + smap_w16(p, RXBD(i, SMAP_BD_CTRL_STAT), SMAP_BD_RX_EMPTY); + smap_w16(p, RXBD(i, SMAP_BD_RESERVED), 0); + smap_w16(p, RXBD(i, SMAP_BD_LENGTH), 0); + smap_w16(p, RXBD(i, SMAP_BD_POINTER), 0); + } + + p->txbwp = 0; + p->txfree = SMAP_TXBUFSIZE; + p->txbds = p->txbdi = p->txused = 0; + p->rxbdi = 0; +} + +/* + * Sony's smap_reset(RESET_INIT) minus the PHY, which phylib owns: quiet + * the interrupts, reset the FIFOs and the EMAC3, program the defaults and + * clear the rings. Leaves the MACs disabled. + */ +static int smap_hw_init(struct smap_priv *p) +{ + unsigned long flags; + u32 mode0; + int woken; + int err; + + spin_lock_irqsave(&p->lock, flags); + smap_intr_set(p, SMAP_INTR_ALL, false); + smap_w16(p, SMAP_INTR_CLR, SMAP_INTR_ALL); + emac3_write(p, SMAP_EMAC3_INTR_ENABLE, 0); + emac3_write(p, SMAP_EMAC3_INTR_STAT, E3_INTR_ALL); + spin_unlock_irqrestore(&p->lock, flags); + + smap_w8(p, SMAP_BD_MODE, 0); /* no byte swap */ + + /* + * Version 10, and this is the whole point of it. Measured on hardware + * on 2026-09-06: after a failed reset MODE0 reads 20000000, that is + * E3_SOFT_RESET still asserted, and the BMCR read then fails with + * -ETIMEDOUT. MDIO runs through EMAC3's own STA_CTRL register, so + * while EMAC3 sits in reset there is no way to reach the PHY at all - + * which means the wake-up that versions 7 to 9 attempted after a failed + * reset could never work. It has to happen before it. + * + * So: clear a reset left over by a previous attempt, then wake the PHY + * while MDIO is still usable, and only then reset the block. + */ + mode0 = emac3_read(p, SMAP_EMAC3_MODE0); + if (mode0 & E3_SOFT_RESET) { + netdev_warn(p->ndev, + "EMAC3 was left in soft reset (mode0 %08x), clearing it\n", + mode0); + emac3_write(p, SMAP_EMAC3_MODE0, 0); + msleep(20); + } + + woken = smap_phy_wake(p); + if (woken < 0) + netdev_warn(p->ndev, + "PHY BMCR read failed with %d before the reset: MDIO is not answering\n", + woken); + else if (woken) + netdev_info(p->ndev, + "PHY was powered down, woke it up before the EMAC3 reset\n"); + + err = smap_fifo_reset(p); + if (err) + return err; + + err = smap_emac3_soft_reset(p); + if (err) { + /* + * Kept as the second line of defence for the case where the + * PHY falls asleep between the wake-up above and this reset. + * The MODE0 read-back tells two failures apart: 20000000 means + * the reset bit is still set and the block never cleared it, + * 00000000 means the register window itself reads as zero and + * the whole SPEED side is gone. + */ + netdev_err(p->ndev, "EMAC3 reset timed out, mode0 %08x\n", + emac3_read(p, SMAP_EMAC3_MODE0)); + + woken = smap_phy_wake(p); + if (woken < 0) + netdev_err(p->ndev, + "PHY BMCR read failed with %d: MDIO is not answering, so the PHY cannot be woken here\n", + woken); + else if (!woken) + netdev_err(p->ndev, + "PHY is not powered down, so the stuck EMAC3 reset has another cause\n"); + else { + netdev_info(p->ndev, "PHY was powered down, woke it up and retrying the EMAC3 reset\n"); + err = smap_emac3_soft_reset(p); + } + + if (err) { + netdev_err(p->ndev, + "EMAC3 soft reset did not complete\n"); + return err; + } + } + + emac3_write(p, SMAP_EMAC3_MODE1, p->mode1); + + smap_w16(p, SMAP_INTR_CLR, SMAP_INTR_ALL); + emac3_write(p, SMAP_EMAC3_INTR_STAT, E3_INTR_ALL); + + smap_emac3_set_defaults(p); + smap_bd_init(p); + + return 0; +} + +/* -------------------------------------------------------------------- TX */ + +/* Called with p->lock held. */ +static void smap_tx_reclaim(struct smap_priv *p) +{ + struct net_device *ndev = p->ndev; + + while (p->txused > 0) { + u16 stat = smap_r16(p, TXBD(p->txbdi, SMAP_BD_CTRL_STAT)); + u16 len; + + if (stat & SMAP_BD_TX_READY) + break; + + len = smap_r16(p, TXBD(p->txbdi, SMAP_BD_LENGTH)); + p->txfree += ALIGN(len, 4); + + if (stat & SMAP_TX_ERR_MASK) { + ndev->stats.tx_errors++; + if (stat & (SMAP_BD_TX_ECOLL | SMAP_BD_TX_LCOLL)) + ndev->stats.tx_aborted_errors++; + if (stat & SMAP_BD_TX_LOSSCR) + ndev->stats.tx_carrier_errors++; + if (stat & SMAP_BD_TX_UNDERRUN) + ndev->stats.tx_fifo_errors++; + netdev_dbg(ndev, "tx bd %d status %04x\n", + p->txbdi, stat); + } else { + ndev->stats.tx_packets++; + ndev->stats.tx_bytes += len; + } + if (stat & (SMAP_BD_TX_MCOLL | SMAP_BD_TX_SCOLL)) + ndev->stats.collisions++; + + p->txused--; + p->txbdi = (p->txbdi + 1) % SMAP_BD_MAX_ENTRY; + } +} + +/* Room for one more maximum-size frame? Called with p->lock held. */ +static bool smap_tx_room(struct smap_priv *p) +{ + return p->txused < SMAP_TXBD_LIMIT && + p->txfree >= ALIGN(SMAP_TXMAXSIZE, 4); +} + +static netdev_tx_t smap_start_xmit(struct sk_buff *skb, struct net_device *ndev) +{ + struct smap_priv *p = netdev_priv(ndev); + unsigned int len = skb->len; + unsigned int txlen, off, i; + unsigned long flags; + const u32 *w; + + if (len > SMAP_TXMAXSIZE) { + ndev->stats.tx_dropped++; + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; + } + txlen = ALIGN(len, 4); + + spin_lock_irqsave(&p->lock, flags); + + smap_tx_reclaim(p); + if (p->txused >= SMAP_TXBD_LIMIT || p->txfree < txlen) { + netif_stop_queue(ndev); + spin_unlock_irqrestore(&p->lock, flags); + return NETDEV_TX_BUSY; + } + + /* + * Bounce: skb data is 2 (mod 4) aligned, the FIFO wants whole 32-bit + * words, and the padding bytes must be zero. + */ + memcpy(p->txbuf, skb->data, len); + if (txlen > len) + memset((u8 *)p->txbuf + len, 0, txlen - len); + + /* Memory -> FIFO, then a descriptor, then the MAC. Sony's order. */ + off = p->txbwp; + smap_w16(p, SMAP_TXFIFO_WR_PTR, off); + w = p->txbuf; + for (i = 0; i < txlen; i += 4) + smap_w32(p, SMAP_TXFIFO_DATA, *w++); + + smap_w16(p, TXBD(p->txbds, SMAP_BD_LENGTH), len); + smap_w16(p, TXBD(p->txbds, SMAP_BD_POINTER), SMAP_TXBUFBASE + off); + smap_w8(p, SMAP_TXFIFO_FRAME_INC, 1); + smap_w16(p, TXBD(p->txbds, SMAP_BD_CTRL_STAT), + SMAP_BD_TX_READY | SMAP_BD_TX_GENFCS | SMAP_BD_TX_GENPAD); + + p->txused++; + p->txbds = (p->txbds + 1) % SMAP_BD_MAX_ENTRY; + p->txfree -= txlen; + p->txbwp = (off + txlen) % SMAP_TXBUFSIZE; + + emac3_write(p, SMAP_EMAC3_TxMODE0, E3_TX_GNP_0); + + if (!smap_tx_room(p)) + netif_stop_queue(ndev); + + spin_unlock_irqrestore(&p->lock, flags); + + dev_kfree_skb_any(skb); + + return NETDEV_TX_OK; +} + +static void smap_tx_timeout(struct net_device *ndev) +{ + struct smap_priv *p = netdev_priv(ndev); + unsigned long flags; + + spin_lock_irqsave(&p->lock, flags); + netdev_warn(ndev, "tx timeout: used %d free %d bd[%d] %04x intr %04x mode0 %08x\n", + p->txused, p->txfree, p->txbdi, + smap_r16(p, TXBD(p->txbdi, SMAP_BD_CTRL_STAT)), + smap_r16(p, SMAP_INTR_STAT), + emac3_read(p, SMAP_EMAC3_MODE0)); + smap_tx_reclaim(p); + emac3_write(p, SMAP_EMAC3_TxMODE0, E3_TX_GNP_0); + ndev->stats.tx_errors++; + if (smap_tx_room(p)) + netif_wake_queue(ndev); + spin_unlock_irqrestore(&p->lock, flags); +} + +/* -------------------------------------------------------------------- RX */ + +/* + * Every access to the SPEED register block goes through p->lock, including + * the receive FIFO drain below. Sony's driver does the same in its PIO + * path -- rhino-2.4.17-smap/smap.c:642 wraps the RXFIFO_RD_PTR store and + * the RXFIFO_DATA loop in the very spinlock that guards the transmit FIFO. + * Reading the receive FIFO unlocked from NAPI works for as long as nothing + * transmits at the same time, which is why ping, DHCP and short SSH + * commands never showed it, and a sustained transfer killed the interface + * within seconds. + * + * The buffer copy, the skb allocation and napi_gro_receive() stay outside + * the lock: they touch no register, and holding a spinlock with interrupts + * off across an allocation would be far worse than the race it closes. + */ +static int smap_rx(struct smap_priv *p, int budget) +{ + struct net_device *ndev = p->ndev; + unsigned long flags; + int work = 0; + + while (work < budget) { + u16 stat, len, ptr; + unsigned int rxlen, i; + struct sk_buff *skb; + bool deliver; + u32 *w; + + spin_lock_irqsave(&p->lock, flags); + + stat = smap_r16(p, RXBD(p->rxbdi, SMAP_BD_CTRL_STAT)); + if (stat & SMAP_BD_RX_EMPTY) { + spin_unlock_irqrestore(&p->lock, flags); + break; + } + + len = smap_r16(p, RXBD(p->rxbdi, SMAP_BD_LENGTH)); + ptr = smap_r16(p, RXBD(p->rxbdi, SMAP_BD_POINTER)); + + deliver = !(stat & SMAP_RX_ERR_MASK) && len >= SMAP_RXMINSIZE && + len <= SMAP_RXMAXSIZE; + + if (deliver) { + /* FIFO -> memory: whole words via the bounce buffer. */ + rxlen = ALIGN(len, 4); + smap_w16(p, SMAP_RXFIFO_RD_PTR, ptr & 0x3ffc); + w = p->rxbuf; + for (i = 0; i < rxlen; i += 4) + *w++ = smap_r32(p, SMAP_RXFIFO_DATA); + } else { + ndev->stats.rx_errors++; + if (stat & SMAP_BD_RX_BADFCS) + ndev->stats.rx_crc_errors++; + if (stat & (SMAP_BD_RX_FRMTOOLONG | SMAP_BD_RX_RUNTFRM | + SMAP_BD_RX_SHORTEVNT)) + ndev->stats.rx_length_errors++; + if (stat & SMAP_BD_RX_ALIGNERR) + ndev->stats.rx_frame_errors++; + if (stat & SMAP_BD_RX_OVERRUN) + ndev->stats.rx_fifo_errors++; + netdev_dbg(ndev, "rx bd %d status %04x len %u\n", + p->rxbdi, stat, len); + } + + /* Release the descriptor before handing the packet up. */ + smap_w8(p, SMAP_RXFIFO_FRAME_DEC, 1); + smap_w16(p, RXBD(p->rxbdi, SMAP_BD_CTRL_STAT), + SMAP_BD_RX_EMPTY); + p->rxbdi = (p->rxbdi + 1) % SMAP_BD_MAX_ENTRY; + + spin_unlock_irqrestore(&p->lock, flags); + + work++; + if (!deliver) + continue; + + skb = netdev_alloc_skb_ip_align(ndev, len); + if (!skb) { + ndev->stats.rx_dropped++; + continue; + } + skb_put_data(skb, p->rxbuf, len); + skb->protocol = eth_type_trans(skb, ndev); + ndev->stats.rx_packets++; + ndev->stats.rx_bytes += len; + napi_gro_receive(&p->napi, skb); + } + + return work; +} + +static int smap_poll(struct napi_struct *napi, int budget) +{ + struct smap_priv *p = container_of(napi, struct smap_priv, napi); + struct net_device *ndev = p->ndev; + unsigned long flags; + u16 stat; + int work; + + /* + * Acknowledge everything we own before touching the rings. + * + * RXEND/TXEND: normally the handler has just done this, but this + * path is also reached from the watchdog timer -- in polling mode + * and when the interrupt path has stalled -- and then nothing else + * drops the line. Clearing first and reading the rings second is + * the safe order: a frame that lands after the clear sets the bit + * again and raises a fresh edge. + * + * TXDNV/RXDNV: not in SMAP_INTR_USED, so the IOP never acknowledges + * them (it clears STAT & MASK only). Whether a stale one can hold + * the line on its own is not established -- the proven killer is + * the END race in smap_rxtx_irq() -- but Sony's driver never lets + * them sit either (rhino-2.4.17-smap/smap.c:331-335, :755-758), and + * clearing costs one write and needs no mask bit. + */ + spin_lock_irqsave(&p->lock, flags); + stat = smap_r16(p, SMAP_INTR_STAT) & + (INTR_RXEND | INTR_TXEND | INTR_TXDNV | INTR_RXDNV); + if (stat) { + smap_w16(p, SMAP_INTR_CLR, stat); + if (stat & (INTR_TXDNV | INTR_RXDNV)) + p->dnv_events++; + } + spin_unlock_irqrestore(&p->lock, flags); + + work = smap_rx(p, budget); + + spin_lock_irqsave(&p->lock, flags); + smap_tx_reclaim(p); + if (netif_queue_stopped(ndev) && smap_tx_room(p)) + netif_wake_queue(ndev); + spin_unlock_irqrestore(&p->lock, flags); + + /* + * Nothing to unmask: the sources were never masked. A frame that + * lands between the last ring check and napi_complete_done() has + * raised a fresh edge, so the handler runs again; if it finds us + * still running, napi_schedule_prep() records NAPI_STATE_MISSED and + * napi_complete_done() reschedules us. Nothing is lost. + */ + if (work < budget) + napi_complete_done(napi, work); + + return work; +} + +/* --------------------------------------------------- poll-mode fallback */ + +/* + * Has the hardware finished with a descriptor? Read without the lock: the + * two descriptor reads go straight to the chip, and p->txused only makes + * the test miss one round if it changes underneath, which the next tick + * catches. + */ +static bool smap_work_pending(struct smap_priv *p) +{ + if (!(smap_r16(p, RXBD(p->rxbdi, SMAP_BD_CTRL_STAT)) & + SMAP_BD_RX_EMPTY)) + return true; + + if (p->txused > 0 && + !(smap_r16(p, TXBD(p->txbdi, SMAP_BD_CTRL_STAT)) & + SMAP_BD_TX_READY)) + return true; + + return false; +} + +static void smap_timer_fn(struct timer_list *t) +{ + struct smap_priv *p = from_timer(p, t, timer); + struct net_device *ndev = p->ndev; + unsigned long period = msecs_to_jiffies(SMAP_WATCHDOG_MS); + + if (!p->polling) { + unsigned int count = p->irq_count; + + /* + * Two faults look identical from here and both kill the + * interface, so both end the same way -- in polling mode: + * a relay that never delivers anything at all, and a relay + * that falls silent after having worked for a while. The + * earlier version of this test only caught the first, by + * asking whether irq_count was still zero; on hardware the + * interrupts stopped after 834 of them and the interface + * stayed dead with the fallback never arming. Comparing + * against the previous tick catches both. + */ + if (!smap_work_pending(p) || count != p->irq_count_seen) { + p->irq_count_seen = count; + p->stall_ticks = 0; + goto rearm; + } + + if (++p->stall_ticks < SMAP_STALL_TICKS) + goto rearm; + + /* + * Registers, not guesses: this is the only moment at which + * the chip can be asked what it thinks is going on, and the + * answer says whether the interrupt mask was lost (enable + * missing the RXEND/TXEND bits) or whether the chip stopped + * raising the line with the mask still intact. + */ + netdev_warn(ndev, "SPEED interrupts stopped after %u (dnv cleared %u): intr stat %04x enable %04x, rx bd[%d] %04x, tx bd[%d] %04x, txused %d\n", + count, p->dnv_events, + smap_r16(p, SMAP_INTR_STAT), + smap_r16(p, SMAP_INTR_ENABLE), + p->rxbdi, + smap_r16(p, RXBD(p->rxbdi, SMAP_BD_CTRL_STAT)), + p->txbdi, + smap_r16(p, TXBD(p->txbdi, SMAP_BD_CTRL_STAT)), + p->txused); + netdev_warn(ndev, "switching to polling every %d ms; the data path is unaffected, this is the interrupt path alone\n", + poll_ms); + p->polling = true; + } + + if (napi_schedule_prep(&p->napi)) + __napi_schedule(&p->napi); + + period = p->poll_period; + +rearm: + mod_timer(&p->timer, jiffies + period); +} + +/* ------------------------------------------------------------ interrupts */ + +/* + * RXEND and TXEND, relayed from the IOP. Clear the source first and only + * then look at the rings: anything that arrives after the clear raises the + * line again on its own. + * + * We deliberately do NOT mask the sources for the duration of the NAPI run, + * although that is what a normal driver would do. The interrupt mask at + * offset 0x2a belongs to the IOP: iopmod's spd_enable_irq____() and + * spd_disable_irq____() (iopmod/builtin/spd-irq.c:61 and :68) read-modify- + * write it, and drivers/ps2/iop-irq.c wires only irq_startup and + * irq_shutdown, so the IOP touches that register at request_irq() and + * free_irq() time and never again. Two processors doing an unsynchronised + * read-modify-write on one hardware register lose each other's stores, and + * a lost unmask is permanent -- nothing on the IOP side ever puts the bit + * back. Measured on hardware: the status register ends up at 0x4034 with + * RXEND and TXEND pending and no interrupts being delivered at all. + * + * Leaving the sources unmasked costs an interrupt entry that finds NAPI + * already scheduled, which napi_schedule_prep() rejects for free. That is + * far cheaper than losing the interface. + */ +static irqreturn_t smap_rxtx_irq(int irq, void *dev_id) +{ + struct net_device *ndev = dev_id; + struct smap_priv *p = netdev_priv(ndev); + + /* + * Clear BOTH END sources, whichever virtual IRQ brought us here. + * + * The SPEED chip has one interrupt line, into an edge-triggered INTC + * on the IOP. On the rising edge the IOP snapshots STAT & MASK and + * relays one virtual IRQ per bit it saw (iopmod/builtin/spd-irq.c + * :71-84). A source that sets after that snapshot raises no new + * edge -- the line is already high. Versions 1-7 then cleared only + * the bit their own IRQ stood for, so the other source kept the line + * high, the INTC never saw another edge, and no SPEED interrupt was + * delivered again. That is exactly the state the hardware showed: + * STAT 4034 with RXEND and TXEND both pending, MASK 0070 intact, + * counters frozen, while USB (relayed the same way) kept working. + * + * Sony's driver had one handler for the whole chip: it read STAT, + * cleared every END it found, and even cross-checked the other + * ring's frame counter "for race condition of TxEND/RxEND" + * (rhino-2.4.17-smap/smap.c:876-936). With one handler per source + * we get the same effect by acknowledging both here; NAPI drains + * both rings regardless, so nothing is lost by clearing a source + * before its ring has been read. + */ + smap_w16(p, SMAP_INTR_CLR, INTR_RXEND | INTR_TXEND); + p->irq_count++; + + if (napi_schedule_prep(&p->napi)) + __napi_schedule(&p->napi); + + return IRQ_HANDLED; +} + +static irqreturn_t smap_emac3_irq(int irq, void *dev_id) +{ + struct net_device *ndev = dev_id; + struct smap_priv *p = netdev_priv(ndev); + u32 stat; + + smap_w16(p, SMAP_INTR_CLR, INTR_EMAC3); + p->irq_count++; + + stat = emac3_read(p, SMAP_EMAC3_INTR_STAT); + emac3_write(p, SMAP_EMAC3_INTR_STAT, stat); + p->emac3_events++; + + if (stat & E3_DEAD_ALL) { + /* Transmitter stalled: ask for the next packet again. */ + ndev->stats.tx_errors++; + emac3_write(p, SMAP_EMAC3_TxMODE0, E3_TX_GNP_0); + } + if (stat & (E3_INTR_TX_ERR_0 | E3_INTR_TX_ERR_1)) + ndev->stats.tx_errors++; + + if (net_ratelimit()) + netdev_dbg(ndev, "emac3 interrupt status %08x\n", stat); + + return IRQ_HANDLED; +} + +static void smap_free_irqs(struct smap_priv *p) +{ + if (!p->irqs_requested) + return; + free_irq(p->irq_rx, p->ndev); + free_irq(p->irq_tx, p->ndev); + free_irq(p->irq_emac3, p->ndev); + p->irqs_requested = false; +} + +/* + * Each request is an RPC to the IOP IRQ relay, which also sets the bit in + * the SPEED interrupt mask on its side; each free releases it again. + */ +static int smap_request_irqs(struct smap_priv *p) +{ + struct net_device *ndev = p->ndev; + int err; + + err = request_irq(p->irq_rx, smap_rxtx_irq, 0, "ps2-smap-rx", ndev); + if (err) { + netdev_err(ndev, "cannot get RXEND irq %d (%d)\n", + p->irq_rx, err); + return err; + } + err = request_irq(p->irq_tx, smap_rxtx_irq, 0, "ps2-smap-tx", ndev); + if (err) { + netdev_err(ndev, "cannot get TXEND irq %d (%d)\n", + p->irq_tx, err); + goto err_tx; + } + err = request_irq(p->irq_emac3, smap_emac3_irq, 0, "ps2-smap-emac3", + ndev); + if (err) { + netdev_err(ndev, "cannot get EMAC3 irq %d (%d)\n", + p->irq_emac3, err); + goto err_emac3; + } + p->irqs_requested = true; + + return 0; + +err_emac3: + free_irq(p->irq_tx, ndev); +err_tx: + free_irq(p->irq_rx, ndev); + return err; +} + +/* ----------------------------------------------------------------- phylib */ + +static void smap_adjust_link(struct net_device *ndev) +{ + struct smap_priv *p = netdev_priv(ndev); + struct phy_device *phydev = p->phydev; + unsigned long flags; + u32 mode1; + + if (!phydev->link) { + if (p->link) { + p->link = false; + phy_print_status(phydev); + } + return; + } + + mode1 = SMAP_EMAC3_MODE1_DEF & + ~(E3_FDX_ENABLE | E3_FLOWCTRL_ENABLE | E3_ALLOW_PF | + E3_MEDIA_MSK); + if (phydev->duplex == DUPLEX_FULL) + mode1 |= E3_FDX_ENABLE | E3_FLOWCTRL_ENABLE | E3_ALLOW_PF; + mode1 |= phydev->speed == SPEED_100 ? E3_MEDIA_100M : E3_MEDIA_10M; + + if (!p->link || mode1 != p->mode1) { + spin_lock_irqsave(&p->lock, flags); + smap_mac_disable(p); + emac3_write(p, SMAP_EMAC3_MODE1, mode1); + p->mode1 = mode1; + if (netif_running(ndev)) + smap_mac_enable(p); + spin_unlock_irqrestore(&p->lock, flags); + } + + p->link = true; + phy_print_status(phydev); +} + +static int smap_mdio_setup(struct smap_priv *p) +{ + struct net_device *ndev = p->ndev; + struct phy_device *phydev; + struct mii_bus *bus; + int err; + + bus = mdiobus_alloc(); + if (!bus) + return -ENOMEM; + + bus->name = "ps2-smap-mdio"; + snprintf(bus->id, MII_BUS_ID_SIZE, "%s", DRV_NAME); + bus->read = smap_mdio_read; + bus->write = smap_mdio_write; + bus->priv = p; + bus->parent = &p->pdev->dev; + bus->phy_mask = ~BIT(DsPHYTER_ADDRESS); /* only address 1 is wired */ + + err = mdiobus_register(bus); + if (err) { + netdev_err(ndev, "mdiobus_register failed (%d)\n", err); + mdiobus_free(bus); + return err; + } + p->mii = bus; + + phydev = phy_find_first(bus); + if (!phydev) { + netdev_err(ndev, "no PHY found on MII address %d\n", + DsPHYTER_ADDRESS); + err = -ENODEV; + goto err_unregister; + } + + err = phy_connect_direct(ndev, phydev, smap_adjust_link, + PHY_INTERFACE_MODE_MII); + if (err) { + netdev_err(ndev, "phy_connect_direct failed (%d)\n", err); + goto err_unregister; + } + p->phydev = phydev; + + /* 10/100 only; the PHY reports so itself, but be explicit. */ + phy_remove_link_mode(phydev, ETHTOOL_LINK_MODE_1000baseT_Half_BIT); + phy_remove_link_mode(phydev, ETHTOOL_LINK_MODE_1000baseT_Full_BIT); + + phy_attached_info(phydev); + + return 0; + +err_unregister: + mdiobus_unregister(bus); + mdiobus_free(bus); + p->mii = NULL; + return err; +} + +static void smap_mdio_teardown(struct smap_priv *p) +{ + if (p->phydev) { + phy_disconnect(p->phydev); + p->phydev = NULL; + /* + * phy_disconnect() has just powered the PHY down. Wake it + * while the chip is still ours, or the next probe of this + * driver fails at the EMAC3 reset; see smap_phy_wake(). + */ + smap_phy_wake(p); + } + if (p->mii) { + mdiobus_unregister(p->mii); + mdiobus_free(p->mii); + p->mii = NULL; + } +} + +/* --------------------------------------------------------- net_device_ops */ + +static int smap_open(struct net_device *ndev) +{ + struct smap_priv *p = netdev_priv(ndev); + unsigned long flags; + int err; + + err = smap_hw_init(p); + if (err) + return err; + + p->irq_count = 0; + p->irq_count_seen = 0; + p->stall_ticks = 0; + p->dnv_events = 0; + p->polling = (poll_mode == SMAP_POLL_ONLY); + + if (!p->polling) { + err = smap_request_irqs(p); + if (err) + return err; + } + + napi_enable(&p->napi); + + spin_lock_irqsave(&p->lock, flags); + smap_w16(p, SMAP_INTR_CLR, SMAP_INTR_ALL); + emac3_write(p, SMAP_EMAC3_INTR_STAT, E3_INTR_ALL); + emac3_write(p, SMAP_EMAC3_INTR_ENABLE, SMAP_E3_INTR_USED); + smap_intr_set(p, SMAP_INTR_USED, true); + smap_mac_enable(p); + spin_unlock_irqrestore(&p->lock, flags); + + p->link = false; + phy_start(p->phydev); + + netif_start_queue(ndev); + + if (poll_mode != SMAP_POLL_IRQ) + mod_timer(&p->timer, jiffies + + (p->polling ? p->poll_period : + msecs_to_jiffies(SMAP_WATCHDOG_MS))); + + netdev_info(ndev, "open: %s, intr enable %04x stat %04x, emac3 mode0 %08x mode1 %08x\n", + p->polling ? "polling only" : + poll_mode == SMAP_POLL_AUTO ? + "interrupts, polling on standby" : + "interrupts only", + smap_r16(p, SMAP_INTR_ENABLE), smap_r16(p, SMAP_INTR_STAT), + emac3_read(p, SMAP_EMAC3_MODE0), + emac3_read(p, SMAP_EMAC3_MODE1)); + + return 0; +} + +static int smap_close(struct net_device *ndev) +{ + struct smap_priv *p = netdev_priv(ndev); + unsigned long flags; + + netif_stop_queue(ndev); + del_timer_sync(&p->timer); + phy_stop(p->phydev); + napi_disable(&p->napi); + + spin_lock_irqsave(&p->lock, flags); + smap_intr_set(p, SMAP_INTR_ALL, false); + emac3_write(p, SMAP_EMAC3_INTR_ENABLE, 0); + smap_mac_disable(p); + smap_w16(p, SMAP_INTR_CLR, SMAP_INTR_ALL); + emac3_write(p, SMAP_EMAC3_INTR_STAT, E3_INTR_ALL); + spin_unlock_irqrestore(&p->lock, flags); + + smap_free_irqs(p); + + netdev_info(ndev, "closed: %lu rx, %lu tx, %u interrupts, %u emac3 events, %u dnv cleared, %s\n", + ndev->stats.rx_packets, ndev->stats.tx_packets, + p->irq_count, p->emac3_events, p->dnv_events, + p->polling ? "ran in polling mode" : "ran on interrupts"); + + return 0; +} + +static void smap_set_rx_mode(struct net_device *ndev) +{ + struct smap_priv *p = netdev_priv(ndev); + unsigned long flags; + u32 v; + + v = E3_RX_STRIP_PAD | E3_RX_STRIP_FCS | E3_RX_INDIVID_ADDR | + E3_RX_BCAST; + if (ndev->flags & IFF_PROMISC) + v |= E3_RX_PROMISC; + else if ((ndev->flags & IFF_ALLMULTI) || !netdev_mc_empty(ndev)) + v |= E3_RX_PROMISC_MCAST; /* no hash filter: take all */ + + spin_lock_irqsave(&p->lock, flags); + smap_mac_disable(p); + emac3_write(p, SMAP_EMAC3_RxMODE, v); + if (netif_running(ndev)) + smap_mac_enable(p); + spin_unlock_irqrestore(&p->lock, flags); +} + +static int smap_set_mac_address(struct net_device *ndev, void *addr) +{ + struct smap_priv *p = netdev_priv(ndev); + unsigned long flags; + int err; + + err = eth_mac_addr(ndev, addr); + if (err) + return err; + + spin_lock_irqsave(&p->lock, flags); + smap_mac_disable(p); + smap_emac3_set_defaults(p); + if (netif_running(ndev)) + smap_mac_enable(p); + spin_unlock_irqrestore(&p->lock, flags); + + return 0; +} + +static int smap_ioctl(struct net_device *ndev, struct ifreq *ifr, int cmd) +{ + struct smap_priv *p = netdev_priv(ndev); + + if (!netif_running(ndev) || !p->phydev) + return -EINVAL; + + return phy_mii_ioctl(p->phydev, ifr, cmd); +} + +static const struct net_device_ops smap_netdev_ops = { + .ndo_open = smap_open, + .ndo_stop = smap_close, + .ndo_start_xmit = smap_start_xmit, + .ndo_set_rx_mode = smap_set_rx_mode, + .ndo_set_mac_address = smap_set_mac_address, + .ndo_validate_addr = eth_validate_addr, + .ndo_tx_timeout = smap_tx_timeout, + .ndo_do_ioctl = smap_ioctl, +}; + +static void smap_get_drvinfo(struct net_device *ndev, + struct ethtool_drvinfo *info) +{ + strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); + strlcpy(info->version, DRV_VERSION, sizeof(info->version)); + strlcpy(info->bus_info, "ee:14000000", sizeof(info->bus_info)); +} + +static const struct ethtool_ops smap_ethtool_ops = { + .get_drvinfo = smap_get_drvinfo, + .get_link = ethtool_op_get_link, + .get_link_ksettings = phy_ethtool_get_link_ksettings, + .set_link_ksettings = phy_ethtool_set_link_ksettings, + .nway_reset = phy_ethtool_nway_reset, +}; + +/* ----------------------------------------------------------- platform glue */ + +static int smap_probe(struct platform_device *pdev) +{ + struct net_device *ndev; + struct smap_priv *p; + struct resource *res, *res_smap; + void __iomem *base; + u16 power, rev1, rev3; + u8 mac[ETH_ALEN]; + int irq_rx, irq_tx, irq_emac3; + int err; + + /* + * The EE window raises a data bus error while the bay is unpowered, + * so ask the IOP first. drivers/ps2/iop-dev9.c powers the bay at + * module load when it carries the 2026-09-05 fixes. + */ + err = iop_readw(&power, IOP_DEV9_POWER); + if (err < 0) { + pr_err(DRV_NAME ": cannot read the DEV9 power register (%d); is iop-dev9 loaded?\n", + err); + return err; + } + if (!(power & DEV9_POWER_ON)) { + pr_err(DRV_NAME ": expansion bay is not powered (DEV9 power %04x); this needs a kernel whose iop-dev9 initialises the bay\n", + power); + return -ENODEV; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "speed"); + res_smap = platform_get_resource_byname(pdev, IORESOURCE_MEM, "smap"); + if (!res || !res_smap) + return -ENODEV; + + /* + * One mapping spanning both resources, so that register offsets stay + * relative to the start of the chip window, as the hardware has them. + * The gap in the middle holds the ATA registers of the same chip, + * which pata-ps2 owns; that is why the two resources above describe + * only what this driver touches. + */ + base = devm_ioremap(&pdev->dev, res->start, + res_smap->end - res->start + 1); + if (!base) + return -ENOMEM; + + rev1 = readw(base + SPD_R_REV_1); + rev3 = readw(base + SPD_R_REV_3); + if (rev1 == 0xffff || rev1 == 0x0000 || !(rev3 & SPD_CAPS_SMAP)) { + pr_err(DRV_NAME ": SPEED rev1 %04x rev3 %04x: no SMAP here\n", + rev1, rev3); + return -ENODEV; + } + + irq_rx = platform_get_irq_byname(pdev, "rx"); + irq_tx = platform_get_irq_byname(pdev, "tx"); + irq_emac3 = platform_get_irq_byname(pdev, "emac3"); + if (irq_rx < 0 || irq_tx < 0 || irq_emac3 < 0) + return -ENODEV; + + ndev = alloc_etherdev(sizeof(*p)); + if (!ndev) + return -ENOMEM; + + p = netdev_priv(ndev); + p->ndev = ndev; + p->pdev = pdev; + p->base = base; + SET_NETDEV_DEV(ndev, &pdev->dev); + p->mode1 = SMAP_EMAC3_MODE1_DEF; + p->irq_rx = irq_rx; + p->irq_tx = irq_tx; + p->irq_emac3 = irq_emac3; + spin_lock_init(&p->lock); + + if (poll_mode < SMAP_POLL_IRQ || poll_mode > SMAP_POLL_ONLY) { + pr_warn(DRV_NAME ": poll=%d out of range, using %d\n", + poll_mode, SMAP_POLL_AUTO); + poll_mode = SMAP_POLL_AUTO; + } + if (poll_ms < 1) + poll_ms = 1; + p->poll_period = max(msecs_to_jiffies(poll_ms), 1UL); + timer_setup(&p->timer, smap_timer_fn, 0); + + p->txbuf = kzalloc(SMAP_BOUNCE_SIZE, GFP_KERNEL); + p->rxbuf = kzalloc(SMAP_BOUNCE_SIZE, GFP_KERNEL); + if (!p->txbuf || !p->rxbuf) { + err = -ENOMEM; + goto err_free; + } + + if (smap_read_mac(p, mac) == 0) { + ether_addr_copy(ndev->dev_addr, mac); + } else { + pr_warn(DRV_NAME ": EEPROM checksum mismatch, using a random MAC\n"); + eth_hw_addr_random(ndev); + } + + err = smap_hw_init(p); + if (err) + goto err_free; + + err = smap_mdio_setup(p); + if (err) + goto err_free; + + ndev->netdev_ops = &smap_netdev_ops; + ndev->ethtool_ops = &smap_ethtool_ops; + ndev->watchdog_timeo = 5 * HZ; + netif_napi_add(ndev, &p->napi, smap_poll, 16); + netif_carrier_off(ndev); + + err = register_netdev(ndev); + if (err) { + pr_err(DRV_NAME ": register_netdev failed (%d)\n", err); + goto err_mdio; + } + + netdev_info(ndev, "PlayStation 2 SMAP at %08x, SPEED rev1 %04x rev3 %04x, MAC %pM, irqs %d/%d/%d\n", + (unsigned int)res->start, rev1, rev3, ndev->dev_addr, + p->irq_rx, p->irq_tx, p->irq_emac3); + + platform_set_drvdata(pdev, ndev); + + return 0; + +err_mdio: + netif_napi_del(&p->napi); + smap_mdio_teardown(p); +err_free: + kfree(p->txbuf); + kfree(p->rxbuf); + free_netdev(ndev); + return err; +} + +static int smap_remove(struct platform_device *pdev) +{ + struct net_device *ndev = platform_get_drvdata(pdev); + struct smap_priv *p = netdev_priv(ndev); + + unregister_netdev(ndev); + del_timer_sync(&p->timer); + netif_napi_del(&p->napi); + smap_mdio_teardown(p); + kfree(p->txbuf); + kfree(p->rxbuf); + free_netdev(ndev); + + return 0; +} + +static struct platform_driver smap_driver = { + .remove = smap_remove, + .driver = { + .name = DRV_NAME, + }, +}; + +/* ------------------------------------------------------------ module init */ + +/* + * Nothing in the board setup or in a device tree describes the SMAP, so the + * module creates the platform device it then binds to. The parent device is + * not cosmetic: phy_attach_direct() reads netdev->dev.parent->driver->owner + * without checking either pointer, so connecting a PHY to a parentless + * net_device oopses the kernel at virtual address 0x38. + */ +/* + * platform_driver_probe() reports a probe that bound no device as -ENODEV, so + * loading the module fails outright instead of leaving a driver with no eth0 + * behind. The reason itself has already gone to the log by then. + */ +module_platform_driver_probe(smap_driver, smap_probe); + +MODULE_AUTHOR("Hubert Wyrzykiewicz"); +MODULE_AUTHOR("Sony Computer Entertainment Inc."); +MODULE_DESCRIPTION("PlayStation 2 SMAP Ethernet driver"); +MODULE_LICENSE("GPL"); From 0c91df5bf338c2b2dc2590a18be8e7c25fd16e2f Mon Sep 17 00:00:00 2001 From: Hubert Wyrzykiewicz Date: Sun, 6 Sep 2026 19:28:01 +0200 Subject: [PATCH 4/5] MIPS: PS2: Register the SMAP Ethernet device Add the platform device the SMAP driver binds to: the 16 KiB SPEED register window at physical 0x14000000, which holds the registers, both FIFO data ports and the two buffer descriptor rings, and the three interrupts relayed from the IOP, named rx, tx and emac3 so the driver does not depend on their order. Enable the driver as a module in ps2_defconfig. Signed-off-by: Hubert Wyrzykiewicz Co-Authored-By: Claude Opus 5 --- arch/mips/configs/ps2_defconfig | 1 + arch/mips/ps2/devices.c | 56 +++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/arch/mips/configs/ps2_defconfig b/arch/mips/configs/ps2_defconfig index 4d2ab26ec7c47b..ac56f0af6b5110 100644 --- a/arch/mips/configs/ps2_defconfig +++ b/arch/mips/configs/ps2_defconfig @@ -932,6 +932,7 @@ CONFIG_NET_VENDOR_MELLANOX=y # CONFIG_NET_VENDOR_NI is not set # CONFIG_ETHOC is not set # CONFIG_NET_VENDOR_PENSANDO is not set +CONFIG_PS2_SMAP=m # CONFIG_NET_VENDOR_QUALCOMM is not set # CONFIG_NET_VENDOR_RENESAS is not set # CONFIG_NET_VENDOR_ROCKER is not set diff --git a/arch/mips/ps2/devices.c b/arch/mips/ps2/devices.c index 6fcfd377a3555c..239bfacbe38259 100644 --- a/arch/mips/ps2/devices.c +++ b/arch/mips/ps2/devices.c @@ -73,6 +73,61 @@ static struct platform_device pata_device = { .resource = pata_resources, }; +/* + * The SMAP part of the SPEED chip in the expansion bay. The chip window + * starts at physical 0x14000000 and holds the ATA registers as well, at + * IOP_PATA_BASE, so SMAP is described as the two ranges it actually uses + * rather than as one block: the revision and interrupt registers below the + * ATA window, and everything from the buffer descriptor mode register up, + * which covers both FIFO data ports, the EMAC3 registers and the two + * descriptor rings. Declaring the whole window instead would collide with + * the ATA device above and leave SMAP unregistered. + * + * The hardware only answers once iop-dev9 has powered the expansion bay, + * which the driver verifies through the IOP before it touches the window. + */ +#define SPEED_BASE 0x14000000 + +static struct resource smap_resources[] = { + [0] = { + .name = "speed", + .start = SPEED_BASE, + .end = SPEED_BASE + 0x3f, + .flags = IORESOURCE_MEM, + }, + [1] = { + .name = "smap", + .start = SPEED_BASE + 0x100, + .end = SPEED_BASE + 0x3fff, + .flags = IORESOURCE_MEM, + }, + [2] = { + .name = "rx", + .start = IRQ_IOP_SPD_RXEND, + .end = IRQ_IOP_SPD_RXEND, + .flags = IORESOURCE_IRQ, + }, + [3] = { + .name = "tx", + .start = IRQ_IOP_SPD_TXEND, + .end = IRQ_IOP_SPD_TXEND, + .flags = IORESOURCE_IRQ, + }, + [4] = { + .name = "emac3", + .start = IRQ_IOP_SPD_EMAC3, + .end = IRQ_IOP_SPD_EMAC3, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device smap_device = { + .name = "ps2-smap", + .id = -1, + .num_resources = ARRAY_SIZE(smap_resources), + .resource = smap_resources, +}; + static struct resource gs_resources[] = { [0] = { .name = "Graphics Synthesizer", @@ -113,6 +168,7 @@ static struct platform_device *ps2_platform_devices[] __initdata = { &iop_device, &ohci_device, &pata_device, + &smap_device, &gs_device, &gs_drm_device, /* FIXME */ &rtc_device, From c496d848b845027e6f645fe3cb0830020f1936ee Mon Sep 17 00:00:00 2001 From: Hubert Wyrzykiewicz Date: Sun, 6 Sep 2026 21:59:28 +0200 Subject: [PATCH 5/5] net: ps2: Close the interface on machine shutdown Restarting the machine resets the I/O processor, and with it the DEV9 expansion bay: the SPEED register window stops answering and every access from the EE raises a data bus error. The driver's watchdog timer walks into that half a second later, in interrupt context: reboot: Restarting system Data bus error, epc == c0024020, ra == c0024398 epc : readw+0x8/0x18 [ps2_smap] ra : smap_timer_fn+0x44/0x1bc [ps2_smap] Kernel panic - not syncing: Fatal exception in interrupt Add a shutdown callback that closes the interface while the hardware is still there to be closed, the way ohci-ps2 has always done through usb_hcd_platform_shutdown(). device_shutdown() runs it before the machine is restarted, so the timer is stopped, the interrupts are released and the MACs are disabled before anything touches the bay. Signed-off-by: Hubert Wyrzykiewicz Co-Authored-By: Claude Opus 5 --- drivers/net/ethernet/ps2/ps2-smap.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/drivers/net/ethernet/ps2/ps2-smap.c b/drivers/net/ethernet/ps2/ps2-smap.c index cc2303f099cb19..e05c291ca2eb5c 100644 --- a/drivers/net/ethernet/ps2/ps2-smap.c +++ b/drivers/net/ethernet/ps2/ps2-smap.c @@ -60,6 +60,7 @@ #include #include #include +#include #include #include #include @@ -1624,8 +1625,28 @@ static int smap_remove(struct platform_device *pdev) return 0; } +static void smap_shutdown(struct platform_device *pdev) +{ + struct net_device *ndev = platform_get_drvdata(pdev); + + /* + * Restarting the machine resets the IOP, and with it the expansion + * bay: the register window stops answering and every access from the + * EE raises a data bus error. The driver's own timer would walk into + * that half a second later, in interrupt context, and take the kernel + * down with a panic on its way out. Close the interface here, while + * the hardware is still there to be closed. + */ + if (ndev && netif_running(ndev)) { + rtnl_lock(); + dev_close(ndev); + rtnl_unlock(); + } +} + static struct platform_driver smap_driver = { .remove = smap_remove, + .shutdown = smap_shutdown, .driver = { .name = DRV_NAME, },