Kernel/aarch64: Properly reset the Pi 5's Ethernet PHY via its reset pin

I previously didn't notice this was necessary since I always use netboot
to boot the kernel on my Pi 5. When using netboot, the firmware resets
the PHY for us.

Similar to the fan driver, this code now hardcodes the reset pin
number. In the future, it might be a better idea to use the devicetree
for this. Upstream linux and the downstream Raspberry Pi fork
unfortunately have incompatible devicetree bindings, so we will have
choose one of them.
This commit is contained in:
Sönke Holz authored and Sönke Holz committed 2026-09-21 12:14:42 +02:00
1 parent 5962730d78
commit b592b8a059
5 files changed
+31 -6

No files matched your search

+22 -3
View File
@@ -4,14 +4,19 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <Kernel/Arch/Delay.h>
#include <Kernel/Arch/aarch64/RPi/RP1/GEM.h>
#include <Kernel/Arch/aarch64/RPi/RP1/GPIO.h>
#include <Kernel/Arch/aarch64/RPi/RP1/RP1.h>
#include <Kernel/Firmware/DeviceTree/DeviceTree.h>
#include <Kernel/Net/Cadence/GEMRegisters.h>
namespace Kernel::RPi {
ErrorOr<NonnullRefPtr<RP1GEMNetworkAdapter>> RP1GEMNetworkAdapter::create(RP1& rp1, StringView interface_name, PhysicalAddress paddr, InterruptNumber interrupt_number)
// Taken from the devicetree. The reset pin is active-low.
static constexpr u32 PHY_RESET_GPIO_PIN = 32;
ErrorOr<NonnullRefPtr<RP1GEMNetworkAdapter>> RP1GEMNetworkAdapter::create(RP1& rp1, RP1GPIO& rp1_gpio, StringView interface_name, PhysicalAddress paddr, InterruptNumber interrupt_number)
{
auto registers_mapping = TRY(Memory::map_typed_writable<CadenceGEMNetworkAdapter::Registers volatile>(paddr));
@@ -28,19 +33,33 @@ ErrorOr<NonnullRefPtr<RP1GEMNetworkAdapter>> RP1GEMNetworkAdapter::create(RP1& r
return EINVAL;
}
auto adapter = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) RP1GEMNetworkAdapter(rp1, interface_name, mac_address, move(registers_mapping), interrupt_number)));
rp1_gpio.set_output_override(PHY_RESET_GPIO_PIN, RP1GPIO::OutputOverride::ForceHigh);
rp1_gpio.set_output_enable_override(PHY_RESET_GPIO_PIN, RP1GPIO::OutputEnableOverride::ForceEnable);
rp1_gpio.set_pin_function(PHY_RESET_GPIO_PIN, RP1GPIO::FUNCTION_NONE);
rp1_gpio.set_pin_enabled(PHY_RESET_GPIO_PIN, true);
auto adapter = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) RP1GEMNetworkAdapter(rp1, rp1_gpio, interface_name, mac_address, move(registers_mapping), interrupt_number)));
TRY(adapter->initialize());
return adapter;
}
RP1GEMNetworkAdapter::RP1GEMNetworkAdapter(RP1& rp1, StringView interface_name, MACAddress mac_address, Memory::TypedMapping<CadenceGEMNetworkAdapter::Registers volatile> registers, InterruptNumber interrupt_number)
RP1GEMNetworkAdapter::RP1GEMNetworkAdapter(RP1& rp1, RP1GPIO& rp1_gpio, StringView interface_name, MACAddress mac_address, Memory::TypedMapping<CadenceGEMNetworkAdapter::Registers volatile> registers, InterruptNumber interrupt_number)
: CadenceGEMNetworkAdapter(interface_name, mac_address, move(registers))
, m_rp1(rp1)
, m_rp1_gpio(rp1_gpio)
, m_interrupt_number(interrupt_number)
{
}
void RP1GEMNetworkAdapter::reset_phy()
{
// The 5 ms PHY reset time is taken from the devicetree.
m_rp1_gpio.set_output_override(PHY_RESET_GPIO_PIN, RP1GPIO::OutputOverride::ForceLow);
microseconds_delay(5'000);
m_rp1_gpio.set_output_override(PHY_RESET_GPIO_PIN, RP1GPIO::OutputOverride::ForceHigh);
}
void RP1GEMNetworkAdapter::register_and_enable_interrupt()
{
// 6.2. MSIx configuration registers
+5 -2
View File
@@ -12,18 +12,21 @@
namespace Kernel::RPi {
class RP1;
class RP1GPIO;
class RP1GEMNetworkAdapter final : public CadenceGEMNetworkAdapter {
public:
static ErrorOr<NonnullRefPtr<RP1GEMNetworkAdapter>> create(RP1&, StringView interface_name, PhysicalAddress, InterruptNumber);
static ErrorOr<NonnullRefPtr<RP1GEMNetworkAdapter>> create(RP1&, RP1GPIO&, StringView interface_name, PhysicalAddress, InterruptNumber);
private:
RP1GEMNetworkAdapter(RP1&, StringView interface_name, MACAddress, Memory::TypedMapping<CadenceGEMNetworkAdapter::Registers volatile>, InterruptNumber);
RP1GEMNetworkAdapter(RP1&, RP1GPIO&, StringView interface_name, MACAddress, Memory::TypedMapping<CadenceGEMNetworkAdapter::Registers volatile>, InterruptNumber);
virtual void reset_phy() override;
virtual void register_and_enable_interrupt() override;
virtual u32 mdio_clock_input_frequency() override;
RP1& m_rp1;
RP1GPIO& m_rp1_gpio;
InterruptNumber m_interrupt_number { 0 };
};
+1 -1
View File
@@ -77,7 +77,7 @@ ErrorOr<void> RP1::initialize()
// Chapter 7. Ethernet
auto gem_interface_name = TRY(NetworkingManagement::generate_interface_name_from_pci_address(device_identifier()));
auto gem = TRY(RP1GEMNetworkAdapter::create(*this, gem_interface_name.representable_view(), bar1_address.offset(0x10'0000), 6));
auto gem = TRY(RP1GEMNetworkAdapter::create(*this, gpio, gem_interface_name.representable_view(), bar1_address.offset(0x10'0000), 6));
NetworkingManagement::the().register_adapter(gem).release_value_but_fixme_should_propagate_errors();
+2
View File
@@ -152,6 +152,8 @@ ErrorOr<void> CadenceGEMNetworkAdapter::initialize()
m_registers->network_control |= Registers::NetworkControl::EnableManagementPort;
reset_phy();
m_mdio_handling_process = TRY(spawn_mdio_handling_task(PHY_ID));
TRY(initialize_rx_descriptors());
+1
View File
@@ -49,6 +49,7 @@ protected:
ErrorOr<void> initialize_rx_descriptors();
ErrorOr<void> initialize_tx_descriptors();
virtual void reset_phy() = 0;
virtual void register_and_enable_interrupt() = 0;
virtual u32 mdio_clock_input_frequency() = 0;