Merge pull request #1407 from kernelkit/add-support-for-bpi-r3-mini

Add support for bpi r3 mini
This commit is contained in:
Mattias Walström
2026-02-22 13:11:11 +01:00
committed by GitHub
26 changed files with 2555 additions and 53 deletions
+1
View File
@@ -5,6 +5,7 @@ config BR2_PACKAGE_BANANAPI_BPI_R3
select BR2_PACKAGE_LINUX_FIRMWARE
select BR2_PACKAGE_LINUX_FIRMWARE_MEDIATEK
select BR2_PACKAGE_LINUX_FIRMWARE_MEDIATEK_MT7986
select BR2_PACKAGE_LINUX_FIRMWARE_AIROHA_EN8811H
select SDCARD_AUX
help
Build Banana PI R3 support
+78 -44
View File
@@ -1,20 +1,29 @@
# Banana Pi BPI-R3
# Banana Pi BPI-R3 / BPI-R3 Mini
<img src="bananapi-bpi-r3.webp" alt="The board" width=800 padding=10>
## Overview
The Banana Pi R3 is a high-performance networking board with full Infix
support for all enabled features including switched Ethernet ports, WiFi,
and SFP interfaces.
The Banana Pi BPI-R3 and BPI-R3 Mini are high-performance networking
boards with full Infix support for all enabled features including
Ethernet, WiFi, and SFP interfaces.
Both boards share the same SoC and most peripherals. The key
differences are:
| Feature | BPI-R3 | BPI-R3 Mini |
|-----------------|--------------------|-------------------------|
| Ethernet switch | Yes (4x LAN + WAN) | No (WAN/LAN ports only) |
| SD card slot | Yes | No |
### Hardware Features
- MediaTek MT7986 ARM Cortex-A53 quad-core processor @ 2.0 GHz
- 2 GB DDR4 RAM
- 8 GB eMMC storage + microSD card slot
- 5x Gigabit Ethernet ports (4x LAN, 1x WAN)
- 2x SFP cages for fiber connectivity (1G/2.5G)
- 8 GB eMMC storage
- microSD card slot (BPI-R3 only)
- 5x Gigabit Ethernet ports with switch core (BPI-R3 only)
- 2x SFP cages for fiber connectivity (1G/2.5G) (BPI-R3 only)
- Dual-band WiFi (2.4 GHz + 5 GHz)
- USB 3.0 port
- Mini PCIe slot
@@ -23,16 +32,16 @@ and SFP interfaces.
Infix comes preconfigured with:
- **LAN ports** (lan1-lan4): Bridged for internal networking
- **LAN ports** (lan1-lan4): Bridged for internal networking (BPI-R3 only)
- **WAN port**: DHCP client enabled for internet connectivity
- **SFP ports** (sfp1, sfp2): Available for configuration
- **SFP ports** (sfp1, sfp2): Available for configuration (BPI-R3 only)
- **WiFi interfaces** (wifi0, wifi1): Available for configuration
## Getting Started
### Quick Start with SD Card
### BPI-R3: Quick Start with SD Card
The easiest way to get started is using an SD card:
The easiest way to get started with the BPI-R3 is using an SD card:
> [!NOTE]
> SD card boot works but we have observed stability issues. For production
@@ -49,6 +58,12 @@ The easiest way to get started is using an SD card:
- Connect to LAN port or console (115200 8N1)
- Default login: `admin` / `admin`
### BPI-R3 Mini: Getting Started
The BPI-R3 Mini does not have an SD card slot, so you must install
directly to eMMC. See [Installing to eMMC — BPI-R3
Mini](#bpi-r3-mini) below.
### Boot Switch Reference
The BPI-R3 has a 4-position DIP switch that controls boot media:
@@ -62,60 +77,44 @@ The BPI-R3 has a 4-position DIP switch that controls boot media:
| 1010 | SPI NAND | Boot from SPI NAND (advanced users) |
> [!NOTE]
> Switch position is read from left to right: "0" = OFF, "1" = ON.
> Switch position is read from left to right: "0" = OFF, "1" = ON.
> When the DIP switch is in the "UP" position it is OFF(0).
## Advanced: Installing to eMMC
## Installing to eMMC
For production deployments or better performance, you can install Infix
to the internal eMMC storage. This is more complex but provides faster
boot times and eliminates the external SD card.
to the internal eMMC storage.
> [!IMPORTANT]
> While Infix boots on both SD card and eMMC, we have observed stability
> issues with SD cards on this platform. **eMMC is recommended** for
> reliable operation.
### Why Use eMMC?
**Advantages:**
- More reliable than SD card (stability issues observed with SD cards)
- Faster boot and better performance
- No external SD card to manage
- More robust for industrial/embedded deployments
**Disadvantages:**
- More complex installation process
- Requires intermediate NAND boot step
- Harder to recover from errors
> While Infix boots on both SD card and eMMC on the BPI-R3, we have
> observed stability issues with SD cards on this platform. **eMMC is
> recommended** for reliable operation.
### Prerequisites
- FTDI USB-to-serial cable (3.3V) for console access
- microSD card with Infix (for initial boot)
- USB flash drive (FAT32 formatted)
- microSD card with Infix, for initial boot (BPI-R3 only)
- Downloaded files (see below)
### Required Files
Download and place these files on a FAT32-formatted USB drive:
1. **Intermediate NAND bootloader** (from Frank-W's U-Boot):
- [bpi-r3_spim-nand_bl2.img][5] (BL2 loader)
- [bpi-r3_spim-nand_fip.bin][6] (FIP image)
2. **Infix eMMC image:**
1. **Infix eMMC image:**
- [infix-bpi-r3-emmc.img][3] (Complete system image)
3. **eMMC bootloader** (extracted from):
2. **eMMC bootloader** (extracted from):
- [bpi-r3-emmc-boot-2025.01-latest.tar.gz][4]
- Extract `bl2.img` from the tarball to your USB drive
3. **Intermediate NAND bootloader** (BPI-R3 only, from Frank-W's U-Boot):
- [bpi-r3_spim-nand_bl2.img][5] (BL2 loader)
- [bpi-r3_spim-nand_fip.bin][6] (FIP image)
> [!WARNING]
> The following process involves multiple boot mode changes. Take your
> time verify each step carefully.
> time and verify each step carefully.
### Installation Steps
### BPI-R3
#### Step 1: Boot from SD card
@@ -177,13 +176,48 @@ mmc partconf 0 1 1 0
Your BPI-R3 should now boot Infix from internal eMMC storage!
### BPI-R3 Mini
The BPI-R3 Mini does not have an SD card slot, so you have to go
through the factory-installed Linux (OpenWRT) to flash the Infix
bootloader and OS to eMMC.
#### Step 1: Boot from NAND (factory default)
<img src="r3mini_nandboot2.png" alt="BPI-R3 Mini NAND boot switch position" width=400>
1. Ensure boot switches are set to NAND mode (factory default)
2. Place `infix-bpi-r3-emmc.img` and `bl2.img` on a USB drive
3. Power on and boot into Linux
4. Mount the USB drive to `/mnt`
#### Step 2: Flash Infix to eMMC
From the Linux shell:
```
echo 0 > /proc/sys/kernel/printk
dd if=/mnt/infix-bpi-r3-emmc.img of=/dev/mmcblk0
echo 0 > /sys/block/mmcblk0boot0/force_ro
dd if=/mnt/bl2.img of=/dev/mmcblk0boot0
sync
```
#### Step 3: Boot from eMMC
<img src="r3mini_emmcboot.png" alt="BPI-R3 Mini eMMC boot switch position" width=400>
1. Power off the board
2. Set boot switches to eMMC mode (see image above)
3. Power on
## Troubleshooting
### Board won't boot
- Verify boot switch positions (check twice!)
- Ensure power supply provides adequate current (12V/2A recommended)
- Try booting from SD card with switches at **0000**
- Try booting from SD card with switches at **0000** (BPI-R3 only)
### Can't break into U-Boot
@@ -195,9 +229,9 @@ Your BPI-R3 should now boot Infix from internal eMMC storage!
- Boot from NAND (**1010**) and verify eMMC image was written
- Check USB drive contents - ensure all files are present
- Re-run Step 5 (eMMC boot configuration)
- Re-run the eMMC boot configuration step
### Reverting to SD card
### Reverting to SD card (BPI-R3 only)
Simply set boot switches back to **0000** and boot from SD card. The
eMMC installation does not affect SD card functionality.
@@ -9,6 +9,7 @@ define BANANAPI_BPI_R3_LINUX_CONFIG_FIXUPS
$(call KCONFIG_ENABLE_OPT,CONFIG_MEDIATEK_WATCHDOG)
$(call KCONFIG_ENABLE_OPT,CONFIG_MEDIATEK_GE_PHY)
$(call KCONFIG_ENABLE_OPT,CONFIG_REALTEK_PHY)
$(call KCONFIG_SET_OPT,CONFIG_AIR_EN8811H_PHY,m)
$(call KCONFIG_ENABLE_OPT,CONFIG_NET_VENDOR_MEDIATEK)
$(call KCONFIG_ENABLE_OPT,CONFIG_NET_MEDIATEK_SOC)
$(call KCONFIG_SET_OPT,CONFIG_NET_DSA_MT7530,m)
+1 -1
View File
@@ -1 +1 @@
dtb-y += mediatek/mt7986a-bananapi-bpi-r3-sd.dtb mediatek/mt7986a-bananapi-bpi-r3-emmc.dtb
dtb-y += mediatek/mt7986a-bananapi-bpi-r3-sd.dtb mediatek/mt7986a-bananapi-bpi-r3-emmc.dtb mediatek/mt7986a-bananapi-bpi-r3-mini.dtb
@@ -1,2 +1,4 @@
#include <arm64/mediatek/mt7986a-bananapi-bpi-r3.dts>
#include "mt7986a-bananapi-bpi-r3.dtsi"
#include "mt7986a-bananapi-bpi-r3-emmc.dtsi"
@@ -0,0 +1,12 @@
#include <arm64/mediatek/mt7986a-bananapi-bpi-r3-mini.dts>
#include "mt7986a-bananapi-bpi-r3.dtsi"
#include "mt7986a-bananapi-bpi-r3-emmc.dtsi"
&phy0 {
compatible = "ethernet-phy-id03a2.a411";
};
&phy1 {
compatible = "ethernet-phy-id03a2.a411";
};
@@ -0,0 +1,2 @@
#include "mt7986a-bananapi-bpi-r3.dtsi"
#include "mt7986a-bananapi-bpi-r3-emmc.dtsi"
@@ -1,2 +1,4 @@
#include <arm64/mediatek/mt7986a-bananapi-bpi-r3.dts>
#include "mt7986a-bananapi-bpi-r3.dtsi"
#include "mt7986a-bananapi-bpi-r3-sd.dtsi"
@@ -1,5 +1,3 @@
#include <arm64/mediatek/mt7986a-bananapi-bpi-r3.dts>
/ {
chosen {
infix {
@@ -51,6 +51,11 @@ image #INFIX_ID##VERSION#-bpi-r3-#TARGET#.img {
size = 4096s
}
partition en8811h_fw {
size = 10M
image = "en8811h-fw.bin"
}
partition aux {
partition-uuid = D4EF35A0-0652-45A1-B3DE-D63339C82035
image = "aux.ext4"
Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

@@ -0,0 +1,445 @@
{
"ieee802-dot1ab-lldp:lldp": {
"infix-lldp:enabled": true
},
"ietf-hardware:hardware": {
"component": [
{
"name": "USB",
"class": "infix-hardware:usb",
"state": {
"admin-state": "unlocked"
}
},
{
"class": "infix-hardware:usb",
"name": "USB2",
"state": {
"admin-state": "unlocked"
}
},
{
"name": "radio0",
"class": "infix-hardware:wifi",
"infix-hardware:wifi-radio": {
"country-code": "DE",
"band": "2.4GHz",
"channel": "auto"
}
},
{
"name": "radio1",
"class": "infix-hardware:wifi",
"infix-hardware:wifi-radio": {
"country-code": "DE",
"band": "5GHz",
"channel": "auto"
}
}
]
},
"ietf-interfaces:interfaces": {
"interface": [
{
"name": "br0",
"type": "infix-if-type:bridge",
"ietf-ip:ipv4": {
"address": [
{
"ip": "192.168.0.1",
"prefix-length": 24
}
]
}
},
{
"name": "lan",
"type": "infix-if-type:ethernet",
"ietf-ip:ipv6": {},
"infix-interfaces:bridge-port": {
"bridge": "br0"
}
},
{
"name": "lo",
"type": "infix-if-type:loopback",
"ietf-ip:ipv4": {
"address": [
{
"ip": "127.0.0.1",
"prefix-length": 8
}
]
},
"ietf-ip:ipv6": {
"address": [
{
"ip": "::1",
"prefix-length": 128
}
]
}
},
{
"name": "wan",
"type": "infix-if-type:ethernet",
"ietf-ip:ipv4": {
"infix-dhcp-client:dhcp": {
"option": [
{
"id": "ntp-server"
},
{
"id": "broadcast"
},
{
"id": "domain"
},
{
"id": "hostname"
},
{
"id": "dns-server"
},
{
"id": "router"
},
{
"id": "netmask"
},
{
"id": "vendor-class",
"value": "Banana Pi BPI-R3"
}
]
}
},
"ietf-ip:ipv6": {
"infix-dhcpv6-client:dhcp": {
"option": [
{
"id": "ntp-server"
},
{
"id": "client-fqdn"
},
{
"id": "domain-search"
},
{
"id": "dns-server"
}
]
}
}
},
{
"name": "wifi0-ap",
"type": "infix-if-type:wifi",
"infix-interfaces:wifi": {
"radio": "radio0",
"access-point": {
"ssid": "Infix",
"security": {
"secret": "wifi"
}
}
},
"infix-interfaces:bridge-port": {
"bridge": "br0"
}
},
{
"name": "wifi1-ap",
"type": "infix-if-type:wifi",
"infix-interfaces:wifi": {
"radio": "radio1",
"access-point": {
"ssid": "Infix5Ghz",
"security": {
"secret": "wifi"
}
}
},
"infix-interfaces:bridge-port": {
"bridge": "br0"
}
}
]
},
"ietf-keystore:keystore": {
"asymmetric-keys": {
"asymmetric-key": [
{
"name": "genkey",
"public-key-format": "infix-crypto-types:ssh-public-key-format",
"public-key": "",
"private-key-format": "infix-crypto-types:rsa-private-key-format",
"cleartext-private-key": "",
"certificates": {}
}
]
},
"symmetric-keys": {
"symmetric-key": [
{
"name": "wifi",
"cleartext-symmetric-key": "aW5maXhpbmZpeA==",
"key-format": "infix-crypto-types:passphrase-key-format"
}
]
}
},
"ietf-netconf-acm:nacm": {
"enable-nacm": true,
"read-default": "permit",
"write-default": "permit",
"exec-default": "permit",
"groups": {
"group": [
{
"name": "admin",
"user-name": [
"admin"
]
},
{
"name": "operator",
"user-name": []
},
{
"name": "guest",
"user-name": []
}
]
},
"rule-list": [
{
"name": "admin-acl",
"group": [
"admin"
],
"rule": [
{
"name": "permit-all",
"module-name": "*",
"access-operations": "*",
"action": "permit",
"comment": "Allow 'admin' group complete access to all operations and data."
}
]
},
{
"name": "operator-acl",
"group": [
"operator"
],
"rule": [
{
"name": "permit-system-rpcs",
"module-name": "ietf-system",
"rpc-name": "*",
"access-operations": "exec",
"action": "permit",
"comment": "Operators can reboot, shutdown, and set system time."
}
]
},
{
"name": "guest-acl",
"group": [
"guest"
],
"rule": [
{
"name": "deny-all-write+exec",
"module-name": "*",
"access-operations": "create update delete exec",
"action": "deny",
"comment": "Guests cannot change anything or exec rpcs."
}
]
},
{
"name": "default-deny-all",
"group": [
"*"
],
"rule": [
{
"name": "deny-password-access",
"path": "/ietf-system:system/authentication/user/password",
"access-operations": "*",
"action": "deny",
"comment": "No user except admins can access password hashes."
},
{
"name": "deny-keystore-access",
"module-name": "ietf-keystore",
"access-operations": "*",
"action": "deny",
"comment": "No user except admins can access cryptographic keys."
},
{
"name": "deny-truststore-access",
"module-name": "ietf-truststore",
"access-operations": "*",
"action": "deny",
"comment": "No user except admins can access trust store."
}
]
}
]
},
"ietf-netconf-server:netconf-server": {
"listen": {
"endpoints": {
"endpoint": [
{
"name": "default-ssh",
"ssh": {
"tcp-server-parameters": {
"local-bind": [
{
"local-address": "::"
}
]
},
"ssh-server-parameters": {
"server-identity": {
"host-key": [
{
"name": "default-key",
"public-key": {
"central-keystore-reference": "genkey"
}
}
]
}
}
}
}
]
}
}
},
"ietf-system:system": {
"hostname": "bpi-%m",
"ntp": {
"server": [
{
"name": "default",
"udp": {
"address": "pool.ntp.org"
}
}
]
},
"authentication": {
"user": [
{
"name": "admin",
"password": "$factory$",
"infix-system:shell": "bash"
}
]
},
"infix-system:motd-banner": "Li0tLS0tLS0uCnwgIC4gLiAgfCBJbmZpeCBPUyDigJQgSW1tdXRhYmxlLkZyaWVuZGx5LlNlY3VyZQp8LS4gdiAuLXwgaHR0cHM6Ly9rZXJuZWxraXQub3JnCictJy0tLSctJwo="
},
"infix-dhcp-server:dhcp-server": {
"option": [
{
"id": "ntp-server",
"address": "auto"
},
{
"id": "dns-server",
"address": "auto"
},
{
"id": "router",
"address": "auto"
}
],
"subnet": [
{
"subnet": "192.168.0.0/24",
"pool": {
"start-address": "192.168.0.100",
"end-address": "192.168.0.250"
}
}
]
},
"infix-firewall:firewall": {
"default": "wan",
"zone": [
{
"name": "lan",
"action": "accept",
"interface": [
"br0"
]
},
{
"name": "wan",
"action": "drop",
"interface": [
"wan"
],
"service": [
"dhcpv6-client"
]
}
],
"policy": [
{
"name": "lan-to-wan",
"action": "accept",
"ingress": [
"lan"
],
"egress": [
"wan"
],
"masquerade": true
}
]
},
"infix-meta:meta": {
"version": "1.7"
},
"infix-services:mdns": {
"enabled": true
},
"infix-services:ssh": {
"enabled": true,
"hostkey": [
"genkey"
],
"listen": [
{
"name": "ipv4",
"address": "0.0.0.0",
"port": 22
},
{
"name": "ipv6",
"address": "::",
"port": 22
}
]
},
"infix-services:web": {
"enabled": true,
"console": {
"enabled": true
},
"netbrowse": {
"enabled": true
},
"restconf": {
"enabled": true
}
}
}
@@ -0,0 +1,2 @@
ACTION=="add", SUBSYSTEM=="net", DEVPATH=="/devices/platform/soc/15100000.ethernet/net/eth0", NAME="lan"
ACTION=="add", SUBSYSTEM=="net", DEVPATH=="/devices/platform/soc/15100000.ethernet/net/eth1", NAME="wan"
@@ -3,6 +3,9 @@ CONFIG_BOOTDELAY=2
# CONFIG_MMC_PCI is not set
CONFIG_ENV_IS_NOWHERE=y
# CONFIG_ENV_IS_IN_MMC is not set
CONFIG_MULTI_DTB_FIT=y
CONFIG_OF_LIST="mt7986a-bpi-r3-sd mt7986a-bpi-r3-emmc mt7986a-bpi-r3-mini"
CONFIG_USB=y
CONFIG_USB_XHCI_HCD=y
CONFIG_USB_XHCI_MTK=y
@@ -26,8 +29,17 @@ CONFIG_MTD_SPI_NAND=y
CONFIG_MTK_SPIM=y
CONFIG_MTK_SNOR=y
CONFIG_DM_MDIO=y
CONFIG_DM_ETH_PHY=y
CONFIG_PHY_ETHERNET_ID=y
CONFIG_PHY_AIROHA=y
CONFIG_PHY_AIROHA_EN8811H=y
CONFIG_PHY_AIROHA_FW_IN_MMC=y
CONFIG_CMD_SF=y
CONFIG_CMD_USB=y
CONFIG_CMD_MTD=y
CONFIG_CMD_MDIO=y
CONFIG_CMD_MTDPARTS=y
CONFIG_CMD_DM=y
@@ -15,6 +15,10 @@
scriptaddr = "0x48000000";
ramdisk_addr_r = "0x4A000000";
en8811h_fw_part = "0#en8811h_fw";
en8811h_fw_dm_dir = "EthMD32.dm.bin";
en8811h_fw_dsp_dir = "EthMD32.DSP.bin";
/* This is a development platform, keep
* developer mode statically enabled.
*/
+2
View File
@@ -11,6 +11,8 @@ BR2_SSP_NONE=y
BR2_INIT_NONE=y
BR2_SYSTEM_BIN_SH_NONE=y
# BR2_PACKAGE_BUSYBOX is not set
BR2_PACKAGE_LINUX_FIRMWARE=y
BR2_PACKAGE_LINUX_FIRMWARE_AIROHA_EN8811H=y
# BR2_PACKAGE_IFUPDOWN_SCRIPTS is not set
# BR2_TARGET_ROOTFS_TAR is not set
BR2_TARGET_ARM_TRUSTED_FIRMWARE=y
+5
View File
@@ -12,11 +12,16 @@ All notable changes to the project are documented in this file.
- Upgrade Buildroot to 2025.02.11 (LTS)
- Add support for Microchip SAMA7G54-EK Evaluation Kit, Arm Cortex-A7
- Add GPS/GNSS receiver support with NTP reference clock integration
- Add support for [Banana Pi R3 Mini][BPI-R3-MINI], a 2 port router with 2 WiFi chip,
uses the same bootloader as BPI-R3 (eMMC-version)
### Fixes
N/A
[BPI-R3-MINI]: https://wiki.banana-pi.org/Banana_Pi_BPI-R3_Mini
[v26.01.0][] - 2026-02-03
-------------------------
@@ -1,8 +1,11 @@
From 989ef8436df2ee48d308981098c44b1b8257c23b Mon Sep 17 00:00:00 2001
From 9a9885324c70d607c9c79814441dbc829e212e41 Mon Sep 17 00:00:00 2001
From: Tobias Waldekranz <tobias@waldekranz.com>
Date: Mon, 10 Jun 2024 13:25:31 +0200
Subject: [PATCH 8/8] hush: Remove Ctrl-C detection in loops
Organization: Addiva Elektronik
Subject: [PATCH 1/7] hush: Remove Ctrl-C detection in loops
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Organization: Wires
Assume that the original intent was to emulate SIGINT to a shell. This
only works as expected if the loop in question is the ouermost, and
@@ -14,15 +17,16 @@ Disable this behavior and delegate the problem of loop termination to
the writer of the script instead.
Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
Signed-off-by: Mattias Walström <lazzer@gmail.com>
---
common/cli_hush.c | 7 -------
1 file changed, 7 deletions(-)
diff --git a/common/cli_hush.c b/common/cli_hush.c
index 171069f5f4..d6d487893f 100644
index a6a8edce1f4..0711e05a779 100644
--- a/common/cli_hush.c
+++ b/common/cli_hush.c
@@ -1796,13 +1796,6 @@ static int run_list_real(struct pipe *pi)
@@ -1792,13 +1792,6 @@ static int run_list_real(struct pipe *pi)
for (; pi; pi = (flag_restore != 0) ? rpipe : pi->next) {
if (pi->r_mode == RES_WHILE || pi->r_mode == RES_UNTIL ||
pi->r_mode == RES_FOR) {
@@ -37,5 +41,5 @@ index 171069f5f4..d6d487893f 100644
if (!rpipe) {
flag_rep = 0;
--
2.34.1
2.43.0
@@ -1,3 +1,20 @@
From 7efc3aa61fbc84b2c0c959e737257d2de2e8020c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mattias=20Walstr=C3=B6m?= <lazzer@gmail.com>
Date: Sat, 6 Sep 2025 22:18:27 +0200
Subject: [PATCH 2/7] cmd: new command rpidisplay
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Organization: Wires
Signed-off-by: Mattias Walström <lazzer@gmail.com>
---
cmd/Kconfig | 14 ++++++++
cmd/Makefile | 2 +-
cmd/rpi_display.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 96 insertions(+), 1 deletion(-)
create mode 100644 cmd/rpi_display.c
diff --git a/cmd/Kconfig b/cmd/Kconfig
index 1d7ddb4ed36..8572dc4353e 100644
--- a/cmd/Kconfig
@@ -130,3 +147,6 @@ index 00000000000..07abdc08552
+ "Returns: 0=connected, 1=not detected, 2=no video\n"
+ "Usage: if testrpidisplay; then echo DSI found; fi"
+);
--
2.43.0
@@ -0,0 +1,48 @@
From c646eef3ee304ae49ad7e3f998ed08bfa0efb7b9 Mon Sep 17 00:00:00 2001
From: Mihai Sain <mihai.sain@microchip.com>
Date: Fri, 5 May 2023 13:28:31 +0300
Subject: [PATCH 3/7] arm: dts: at91: sama7g5ek: increase clock for sdmmc from
25 MHz to 50 MHz
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Organization: Wires
Current clock for sdmmc0 and sdmmc1 is 25 MHz because of the caps forced
by the mainline kernel.
Remove the caps in order to increase the clock to 50 MHz. This will
improve the boot time when reading the kernel binary. Tested on
sama7g5ek rev 5 using mmcinfo command.
Signed-off-by: Mihai Sain <mihai.sain@microchip.com>
Signed-off-by: Mattias Walström <lazzer@gmail.com>
---
arch/arm/dts/at91-sama7g5ek.dts | 4 ----
1 file changed, 4 deletions(-)
diff --git a/arch/arm/dts/at91-sama7g5ek.dts b/arch/arm/dts/at91-sama7g5ek.dts
index 9b247fcaf66..5bbbdd8ed76 100644
--- a/arch/arm/dts/at91-sama7g5ek.dts
+++ b/arch/arm/dts/at91-sama7g5ek.dts
@@ -776,8 +776,6 @@
&sdmmc0 {
bus-width = <8>;
non-removable;
- no-1-8-v;
- sdhci-caps-mask = <0x0 0x00200000>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_sdmmc0_default>;
status = "okay";
@@ -785,8 +783,6 @@
&sdmmc1 {
bus-width = <4>;
- no-1-8-v;
- sdhci-caps-mask = <0x0 0x00200000>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_sdmmc1_default>;
status = "okay";
--
2.43.0
@@ -0,0 +1,37 @@
From 5a530e4eebf64887fd6b449dd45df04eccd07cad Mon Sep 17 00:00:00 2001
From: Joachim Wiberg <troglobit@gmail.com>
Date: Thu, 12 Feb 2026 10:00:02 +0100
Subject: [PATCH 4/7] arm: dts: at91: sama7g5ek supports high-speed on mmc0
(eMMC)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Organization: Wires
- eMMC high-speed timing is supported
- eMMC hardware reset is supported
Tested on sama7g5ek rev 5.
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
Signed-off-by: Mattias Walström <lazzer@gmail.com>
---
arch/arm/dts/at91-sama7g5ek.dts | 2 ++
1 file changed, 2 insertions(+)
diff --git a/arch/arm/dts/at91-sama7g5ek.dts b/arch/arm/dts/at91-sama7g5ek.dts
index 5bbbdd8ed76..d2d16bd5f1f 100644
--- a/arch/arm/dts/at91-sama7g5ek.dts
+++ b/arch/arm/dts/at91-sama7g5ek.dts
@@ -776,6 +776,8 @@
&sdmmc0 {
bus-width = <8>;
non-removable;
+ cap-mmc-highspeed;
+ cap-mmc-hw-reset;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_sdmmc0_default>;
status = "okay";
--
2.43.0
@@ -0,0 +1,980 @@
From be250151366460ee318ca47bb15ec23e5e322a84 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mattias=20Walstr=C3=B6m?= <lazzer@gmail.com>
Date: Wed, 18 Feb 2026 17:00:09 +0100
Subject: [PATCH 5/7] net: phy: Add the Airoha EN8811H PHY driver
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Organization: Wires
Add the driver for the Airoha EN8811H 2.5 Gigabit PHY. The PHY supports
100/1000/2500 Mbps with auto negotiation only.
The driver uses two firmware files, for which updated versions are added to
linux-firmware already.
Locating the AIROHA FW within the filesystem at the designated partition
and path will trigger its automatic loading and writing to the PHY via MDIO.
If need board specific loading override,
please override the en8811h_read_fw function on board or architecture level.
Linux upstream AIROHA EN8811H driver commit:
71e79430117d56c409c5ea485a263bc0d8083390
Based on the Linux upstream AIROHA EN8811H driver code(air_en8811h.c),
I have modified the relevant process to align with the U-Boot boot sequence.
and have validated this on Banana Pi BPI-R3 Mini.
Signed-off-by: Lucien.Jheng <lucienzx159@gmail.com>
Signed-off-by: Mattias Walström <lazzer@gmail.com>
---
drivers/net/phy/Kconfig | 25 +
drivers/net/phy/Makefile | 1 +
drivers/net/phy/air_en8811h.c | 887 ++++++++++++++++++++++++++++++++++
3 files changed, 913 insertions(+)
create mode 100644 drivers/net/phy/air_en8811h.c
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 13e73810ad6..4f7451bf0c1 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -83,6 +83,31 @@ config PHY_ADIN
help
Add support for configuring RGMII on Analog Devices ADIN PHYs.
+menuconfig PHY_AIROHA
+ bool "Airoha Ethernet PHYs support"
+
+config PHY_AIROHA_EN8811H
+ bool "Airoha Ethernet EN8811H support"
+ depends on PHY_AIROHA
+ help
+ AIROHA EN8811H supported.
+
+choice
+ prompt "Location of the Airoha PHY firmware"
+ default PHY_AIROHA_FW_IN_MMC
+ depends on PHY_AIROHA_EN8811H
+
+config PHY_AIROHA_FW_IN_MMC
+ bool "Airoha firmware in MMC partition"
+ help
+ Airoha PHYs use firmware which can be loaded automatically
+ from storage directly attached to the PHY, and then loaded
+ via MDIO commands by the boot loader. The firmware is loaded
+ from a file specified by the U-Boot environment variables
+ en8811h_fw_part, en8811h_fw_dm_dir, and en8811h_fw_dsp_dir.
+
+endchoice
+
menuconfig PHY_AQUANTIA
bool "Aquantia Ethernet PHYs support"
select PHY_GIGE
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index 2487f366e1c..87dee3c15b9 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_MV88E6352_SWITCH) += mv88e6352.o
obj-$(CONFIG_PHYLIB) += phy.o
obj-$(CONFIG_PHYLIB_10G) += generic_10g.o
obj-$(CONFIG_PHY_ADIN) += adin.o
+obj-$(CONFIG_PHY_AIROHA_EN8811H) += air_en8811h.o
obj-$(CONFIG_PHY_AQUANTIA) += aquantia.o
obj-$(CONFIG_PHY_ATHEROS) += atheros.o
obj-$(CONFIG_PHY_BROADCOM) += broadcom.o
diff --git a/drivers/net/phy/air_en8811h.c b/drivers/net/phy/air_en8811h.c
new file mode 100644
index 00000000000..d856d48b4a5
--- /dev/null
+++ b/drivers/net/phy/air_en8811h.c
@@ -0,0 +1,887 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Driver for the Airoha EN8811H 2.5 Gigabit PHY.
+ *
+ * Limitations of the EN8811H:
+ * - Only full duplex supported
+ * - Forced speed (AN off) is not supported by hardware (100Mbps)
+ *
+ * Source originated from linux air_en8811h.c
+ *
+ * Copyright (C) 2025 Airoha Technology Corp.
+ */
+#include <phy.h>
+#include <errno.h>
+#include <log.h>
+#include <env.h>
+#include <malloc.h>
+#include <fs.h>
+#include <asm/unaligned.h>
+#include <linux/iopoll.h>
+#include <linux/bitops.h>
+#include <linux/compat.h>
+#include <dm/device_compat.h>
+#include <u-boot/crc.h>
+
+#define EN8811H_PHY_ID 0x03a2a411
+
+#define AIR_FW_ADDR_DM 0x00000000
+#define AIR_FW_ADDR_DSP 0x00100000
+
+#define EN8811H_MD32_DM_SIZE 0x4000
+#define EN8811H_MD32_DSP_SIZE 0x20000
+
+ #define EN8811H_FW_CTRL_1 0x0f0018
+ #define EN8811H_FW_CTRL_1_START 0x0
+ #define EN8811H_FW_CTRL_1_FINISH 0x1
+ #define EN8811H_FW_CTRL_2 0x800000
+ #define EN8811H_FW_CTRL_2_LOADING BIT(11)
+
+ /* MII Registers */
+ #define AIR_AUX_CTRL_STATUS 0x1d
+ #define AIR_AUX_CTRL_STATUS_SPEED_MASK GENMASK(4, 2)
+ #define AIR_AUX_CTRL_STATUS_SPEED_100 0x4
+ #define AIR_AUX_CTRL_STATUS_SPEED_1000 0x8
+ #define AIR_AUX_CTRL_STATUS_SPEED_2500 0xc
+
+#define AIR_EXT_PAGE_ACCESS 0x1f
+#define AIR_PHY_PAGE_STANDARD 0x0000
+#define AIR_PHY_PAGE_EXTENDED_4 0x0004
+
+/* MII Registers Page 4*/
+#define AIR_BPBUS_MODE 0x10
+#define AIR_BPBUS_MODE_ADDR_FIXED 0x0000
+#define AIR_BPBUS_MODE_ADDR_INCR BIT(15)
+#define AIR_BPBUS_WR_ADDR_HIGH 0x11
+#define AIR_BPBUS_WR_ADDR_LOW 0x12
+#define AIR_BPBUS_WR_DATA_HIGH 0x13
+#define AIR_BPBUS_WR_DATA_LOW 0x14
+#define AIR_BPBUS_RD_ADDR_HIGH 0x15
+#define AIR_BPBUS_RD_ADDR_LOW 0x16
+#define AIR_BPBUS_RD_DATA_HIGH 0x17
+#define AIR_BPBUS_RD_DATA_LOW 0x18
+
+/* Registers on MDIO_MMD_VEND1 */
+#define EN8811H_PHY_FW_STATUS 0x8009
+#define EN8811H_PHY_READY 0x02
+
+/* Registers on MDIO_MMD_VEND2 */
+#define AIR_PHY_LED_BCR 0x021
+#define AIR_PHY_LED_BCR_MODE_MASK GENMASK(1, 0)
+#define AIR_PHY_LED_BCR_TIME_TEST BIT(2)
+#define AIR_PHY_LED_BCR_CLK_EN BIT(3)
+#define AIR_PHY_LED_BCR_EXT_CTRL BIT(15)
+
+#define AIR_PHY_LED_DUR_ON 0x022
+
+#define AIR_PHY_LED_DUR_BLINK 0x023
+
+#define AIR_PHY_LED_ON(i) (0x024 + ((i) * 2))
+#define AIR_PHY_LED_ON_MASK (GENMASK(6, 0) | BIT(8))
+#define AIR_PHY_LED_ON_LINK1000 BIT(0)
+#define AIR_PHY_LED_ON_LINK100 BIT(1)
+#define AIR_PHY_LED_ON_LINK10 BIT(2)
+#define AIR_PHY_LED_ON_LINKDOWN BIT(3)
+#define AIR_PHY_LED_ON_FDX BIT(4) /* Full duplex */
+#define AIR_PHY_LED_ON_HDX BIT(5) /* Half duplex */
+#define AIR_PHY_LED_ON_FORCE_ON BIT(6)
+#define AIR_PHY_LED_ON_LINK2500 BIT(8)
+#define AIR_PHY_LED_ON_POLARITY BIT(14)
+#define AIR_PHY_LED_ON_ENABLE BIT(15)
+
+#define AIR_PHY_LED_BLINK(i) (0x025 + ((i) * 2))
+#define AIR_PHY_LED_BLINK_1000TX BIT(0)
+#define AIR_PHY_LED_BLINK_1000RX BIT(1)
+#define AIR_PHY_LED_BLINK_100TX BIT(2)
+#define AIR_PHY_LED_BLINK_100RX BIT(3)
+#define AIR_PHY_LED_BLINK_10TX BIT(4)
+#define AIR_PHY_LED_BLINK_10RX BIT(5)
+#define AIR_PHY_LED_BLINK_COLLISION BIT(6)
+#define AIR_PHY_LED_BLINK_RX_CRC_ERR BIT(7)
+#define AIR_PHY_LED_BLINK_RX_IDLE_ERR BIT(8)
+#define AIR_PHY_LED_BLINK_FORCE_BLINK BIT(9)
+#define AIR_PHY_LED_BLINK_2500TX BIT(10)
+#define AIR_PHY_LED_BLINK_2500RX BIT(11)
+
+#define EN8811H_FW_VERSION 0x3b3c
+
+#define EN8811H_POLARITY 0xca0f8
+#define EN8811H_POLARITY_TX_NORMAL BIT(0)
+#define EN8811H_POLARITY_RX_REVERSE BIT(1)
+
+#define EN8811H_CLK_CGM 0xcf958
+#define EN8811H_CLK_CGM_CKO BIT(26)
+#define EN8811H_HWTRAP1 0xcf914
+#define EN8811H_HWTRAP1_CKO BIT(12)
+
+#define air_upper_16_bits(n) ((u16)((n) >> 16))
+#define air_lower_16_bits(n) ((u16)((n) & 0xffff))
+#define clear_bit(bit, bitmap) __clear_bit(bit, bitmap)
+
+/* Led definitions */
+#define EN8811H_LED_COUNT 3
+
+struct led {
+ unsigned long rules;
+ unsigned long state;
+};
+
+enum {
+ AIR_PHY_LED_STATE_FORCE_ON,
+ AIR_PHY_LED_STATE_FORCE_BLINK,
+};
+
+enum {
+ AIR_PHY_LED_DUR_BLINK_32MS,
+ AIR_PHY_LED_DUR_BLINK_64MS,
+ AIR_PHY_LED_DUR_BLINK_128MS,
+ AIR_PHY_LED_DUR_BLINK_256MS,
+ AIR_PHY_LED_DUR_BLINK_512MS,
+ AIR_PHY_LED_DUR_BLINK_1024MS,
+};
+
+enum {
+ AIR_LED_DISABLE,
+ AIR_LED_ENABLE,
+};
+
+enum {
+ AIR_ACTIVE_LOW,
+ AIR_ACTIVE_HIGH,
+};
+
+enum {
+ AIR_LED_MODE_DISABLE,
+ AIR_LED_MODE_USER_DEFINE,
+};
+
+/* Trigger specific enum */
+enum air_led_trigger_netdev_modes {
+ AIR_TRIGGER_NETDEV_LINK = 0,
+ AIR_TRIGGER_NETDEV_LINK_10,
+ AIR_TRIGGER_NETDEV_LINK_100,
+ AIR_TRIGGER_NETDEV_LINK_1000,
+ AIR_TRIGGER_NETDEV_LINK_2500,
+ AIR_TRIGGER_NETDEV_LINK_5000,
+ AIR_TRIGGER_NETDEV_LINK_10000,
+ AIR_TRIGGER_NETDEV_HALF_DUPLEX,
+ AIR_TRIGGER_NETDEV_FULL_DUPLEX,
+ AIR_TRIGGER_NETDEV_TX,
+ AIR_TRIGGER_NETDEV_RX,
+ AIR_TRIGGER_NETDEV_TX_ERR,
+ AIR_TRIGGER_NETDEV_RX_ERR,
+
+ /* Keep last */
+ __AIR_TRIGGER_NETDEV_MAX,
+};
+
+/* Default LED setup:
+ * GPIO5 <-> LED0 On: Link detected, blink Rx/Tx
+ * GPIO4 <-> LED1 On: Link detected at 2500 and 1000 Mbps
+ * GPIO3 <-> LED2 On: Link detected at 2500 and 100 Mbps
+ */
+#define AIR_DEFAULT_TRIGGER_LED0 (BIT(AIR_TRIGGER_NETDEV_LINK) | \
+ BIT(AIR_TRIGGER_NETDEV_RX) | \
+ BIT(AIR_TRIGGER_NETDEV_TX))
+#define AIR_DEFAULT_TRIGGER_LED1 (BIT(AIR_TRIGGER_NETDEV_LINK_2500) | \
+ BIT(AIR_TRIGGER_NETDEV_LINK_1000))
+#define AIR_DEFAULT_TRIGGER_LED2 (BIT(AIR_TRIGGER_NETDEV_LINK_2500) | \
+ BIT(AIR_TRIGGER_NETDEV_LINK_100))
+
+#define AIR_PHY_LED_DUR_UNIT 781
+#define AIR_PHY_LED_DUR (AIR_PHY_LED_DUR_UNIT << AIR_PHY_LED_DUR_BLINK_64MS)
+
+struct en8811h_priv {
+ int firmware_version;
+ bool mcu_needs_restart;
+ struct led led[EN8811H_LED_COUNT];
+};
+
+static int air_phy_read_page(struct phy_device *phydev)
+{
+ return phy_read(phydev, MDIO_DEVAD_NONE, AIR_EXT_PAGE_ACCESS);
+}
+
+static int air_phy_write_page(struct phy_device *phydev, int page)
+{
+ return phy_write(phydev, MDIO_DEVAD_NONE, AIR_EXT_PAGE_ACCESS, page);
+}
+
+int air_phy_select_page(struct phy_device *phydev, int page)
+{
+ int ret, oldpage;
+
+ oldpage = air_phy_read_page(phydev);
+ if (oldpage < 0)
+ return oldpage;
+
+ if (oldpage != page) {
+ ret = air_phy_write_page(phydev, page);
+ if (ret < 0)
+ return ret;
+ }
+
+ return oldpage;
+}
+
+int air_phy_restore_page(struct phy_device *phydev, int oldpage, int ret)
+{
+ int r;
+
+ if (oldpage < 0)
+ return oldpage;
+
+ r = air_phy_write_page(phydev, oldpage);
+ if (ret >= 0 && r < 0)
+ ret = r;
+
+ return ret;
+}
+
+static int air_buckpbus_reg_write(struct phy_device *phydev,
+ u32 pbus_address, u32 pbus_data)
+{
+ int ret, saved_page;
+
+ saved_page = air_phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4);
+ if (saved_page < 0)
+ return saved_page;
+
+ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_MODE,
+ AIR_BPBUS_MODE_ADDR_FIXED);
+ if (ret < 0)
+ goto restore_page;
+
+ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_ADDR_HIGH,
+ air_upper_16_bits(pbus_address));
+ if (ret < 0)
+ goto restore_page;
+
+ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_ADDR_LOW,
+ air_lower_16_bits(pbus_address));
+ if (ret < 0)
+ goto restore_page;
+
+ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_DATA_HIGH,
+ air_upper_16_bits(pbus_data));
+ if (ret < 0)
+ goto restore_page;
+
+ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_DATA_LOW,
+ air_lower_16_bits(pbus_data));
+ if (ret < 0)
+ goto restore_page;
+
+restore_page:
+ if (ret < 0)
+ printf("%s 0x%08x failed: %d\n", __func__,
+ pbus_address, ret);
+
+ return air_phy_restore_page(phydev, saved_page, ret);
+}
+
+static int air_buckpbus_reg_read(struct phy_device *phydev,
+ u32 pbus_address, u32 *pbus_data)
+{
+ int pbus_data_low, pbus_data_high;
+ int ret = 0, saved_page;
+
+ saved_page = air_phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4);
+ if (saved_page < 0)
+ return saved_page;
+
+ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_MODE,
+ AIR_BPBUS_MODE_ADDR_FIXED);
+ if (ret < 0)
+ goto restore_page;
+
+ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_ADDR_HIGH,
+ air_upper_16_bits(pbus_address));
+ if (ret < 0)
+ goto restore_page;
+
+ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_ADDR_LOW,
+ air_lower_16_bits(pbus_address));
+ if (ret < 0)
+ goto restore_page;
+
+ pbus_data_high = phy_read(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_DATA_HIGH);
+ if (pbus_data_high < 0) {
+ ret = pbus_data_high;
+ goto restore_page;
+ }
+
+ pbus_data_low = phy_read(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_DATA_LOW);
+ if (pbus_data_low < 0) {
+ ret = pbus_data_low;
+ goto restore_page;
+ }
+
+ *pbus_data = pbus_data_low | (pbus_data_high << 16);
+
+restore_page:
+ if (ret < 0)
+ printf("%s 0x%08x failed: %d\n", __func__,
+ pbus_address, ret);
+
+ return air_phy_restore_page(phydev, saved_page, ret);
+}
+
+static int air_buckpbus_reg_modify(struct phy_device *phydev,
+ u32 pbus_address, u32 mask, u32 set)
+{
+ int pbus_data_low, pbus_data_high;
+ u32 pbus_data_old, pbus_data_new;
+ int ret = 0, saved_page;
+
+ saved_page = air_phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4);
+ if (saved_page < 0)
+ return saved_page;
+
+ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_MODE,
+ AIR_BPBUS_MODE_ADDR_FIXED);
+ if (ret < 0)
+ goto restore_page;
+
+ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_ADDR_HIGH,
+ air_upper_16_bits(pbus_address));
+ if (ret < 0)
+ goto restore_page;
+
+ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_ADDR_LOW,
+ air_lower_16_bits(pbus_address));
+ if (ret < 0)
+ goto restore_page;
+
+ pbus_data_high = phy_read(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_DATA_HIGH);
+ if (pbus_data_high < 0)
+ return pbus_data_high;
+
+ pbus_data_low = phy_read(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_DATA_LOW);
+ if (pbus_data_low < 0)
+ return pbus_data_low;
+
+ pbus_data_old = pbus_data_low | (pbus_data_high << 16);
+ pbus_data_new = (pbus_data_old & ~mask) | set;
+ if (pbus_data_new == pbus_data_old)
+ return 0;
+
+ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_ADDR_HIGH,
+ air_upper_16_bits(pbus_address));
+ if (ret < 0)
+ goto restore_page;
+
+ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_ADDR_LOW,
+ air_lower_16_bits(pbus_address));
+ if (ret < 0)
+ goto restore_page;
+
+ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_DATA_HIGH,
+ air_upper_16_bits(pbus_data_new));
+ if (ret < 0)
+ goto restore_page;
+
+ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_DATA_LOW,
+ air_lower_16_bits(pbus_data_new));
+ if (ret < 0)
+ goto restore_page;
+
+restore_page:
+ if (ret < 0)
+ printf("%s 0x%08x failed: %d\n", __func__,
+ pbus_address, ret);
+
+ return air_phy_restore_page(phydev, saved_page, ret);
+}
+
+static int air_write_buf(struct phy_device *phydev, unsigned long address,
+ unsigned long array_size, const unsigned char *buffer)
+{
+ unsigned int offset;
+ int ret, saved_page;
+ u16 val;
+
+ saved_page = air_phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4);
+ if (saved_page < 0)
+ return saved_page;
+
+ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_MODE,
+ AIR_BPBUS_MODE_ADDR_INCR);
+ if (ret < 0)
+ goto restore_page;
+
+ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_ADDR_HIGH,
+ air_upper_16_bits(address));
+ if (ret < 0)
+ goto restore_page;
+
+ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_ADDR_LOW,
+ air_lower_16_bits(address));
+ if (ret < 0)
+ goto restore_page;
+
+ for (offset = 0; offset < array_size; offset += 4) {
+ val = get_unaligned_le16(&buffer[offset + 2]);
+ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_DATA_HIGH, val);
+ if (ret < 0)
+ goto restore_page;
+
+ val = get_unaligned_le16(&buffer[offset]);
+ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_DATA_LOW, val);
+ if (ret < 0)
+ goto restore_page;
+ }
+
+restore_page:
+ if (ret < 0)
+ printf("%s 0x%08lx failed: %d\n", __func__,
+ address, ret);
+
+ return air_phy_restore_page(phydev, saved_page, ret);
+}
+
+static int en8811h_wait_mcu_ready(struct phy_device *phydev)
+{
+ int ret, reg_value;
+
+ /* Because of mdio-lock, may have to wait for multiple loads */
+ ret = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1,
+ EN8811H_PHY_FW_STATUS, reg_value,
+ reg_value == EN8811H_PHY_READY,
+ 20000, 7500000, true);
+ if (ret) {
+ printf("MCU not ready: 0x%x\n", reg_value);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+__weak int en8811h_read_fw(void **addr)
+{
+ const char *fw_dm, *fw_dsp, *fw_part;
+ u32 ca_crc32;
+ void *buffer;
+ loff_t read;
+ int ret;
+
+ if (!IS_ENABLED(CONFIG_PHY_AIROHA_FW_IN_MMC))
+ return -EOPNOTSUPP;
+
+ /* Allocate memory to store both DM and DSP firmware */
+ buffer = malloc(EN8811H_MD32_DM_SIZE + EN8811H_MD32_DSP_SIZE);
+ if (!buffer) {
+ printf("Failed to allocate memory for firmware\n");
+ return -ENOMEM;
+ }
+
+ /* Get the partition name where the firmware is stored */
+ fw_part = env_get("en8811h_fw_part");
+ if (!fw_part) {
+ printf("Error: env var en8811h_fw_part not set.\n");
+ ret = -EINVAL;
+ goto err_free;
+ }
+
+ /* Get the DM firmware file path */
+ fw_dm = env_get("en8811h_fw_dm_dir");
+ if (!fw_dm) {
+ printf("Error: env var en8811h_fw_dm_dir not set.\n");
+ ret = -EINVAL;
+ goto err_free;
+ }
+
+ /* Get the DSP firmware file path */
+ fw_dsp = env_get("en8811h_fw_dsp_dir");
+ if (!fw_dsp) {
+ printf("Error: env var en8811h_fw_dsp_dir not set.\n");
+ ret = -EINVAL;
+ goto err_free;
+ }
+
+ /* Load DM firmware */
+ ret = fs_set_blk_dev("mmc", fw_part, FS_TYPE_ANY);
+ if (ret < 0)
+ goto err_free;
+
+ /* Read DM firmware file into the start of buffer */
+ ret = fs_read(fw_dm, (ulong)buffer, 0, EN8811H_MD32_DM_SIZE, &read);
+ if (ret < 0) {
+ printf("Failed to read DM firmware: %s\n", fw_dm);
+ goto err_free;
+ }
+
+ /* Calculate CRC32 of DM firmware for verification */
+ ca_crc32 = crc32(0, (unsigned char *)buffer, EN8811H_MD32_DM_SIZE);
+ debug("DM crc32: 0x%x\n", ca_crc32);
+
+ /* Load DSP firmware */
+ ret = fs_set_blk_dev("mmc", fw_part, FS_TYPE_ANY);
+ if (ret < 0)
+ goto err_free;
+
+ /* Read DSP firmware file into buffer after DM section */
+ ret = fs_read(fw_dsp, (ulong)buffer + EN8811H_MD32_DM_SIZE, 0,
+ EN8811H_MD32_DSP_SIZE, &read);
+ if (ret < 0) {
+ printf("Failed to read DSP firmware: %s\n", fw_dsp);
+ goto err_free;
+ }
+
+ /* Calculate CRC32 of DSP firmware for verification */
+ ca_crc32 = crc32(0, (unsigned char *)buffer + EN8811H_MD32_DM_SIZE,
+ EN8811H_MD32_DSP_SIZE);
+ debug("DSP crc32: 0x%x\n", ca_crc32);
+
+ *addr = buffer;
+ debug("Found Airoha Firmware.\n");
+
+ return 0;
+
+err_free:
+ free(buffer);
+ return ret;
+}
+
+static int en8811h_load_firmware(struct phy_device *phydev)
+{
+ struct en8811h_priv *priv = phydev->priv;
+ void *buffer = NULL;
+ int ret;
+
+ if (!IS_ENABLED(CONFIG_PHY_AIROHA_FW_IN_MMC)) {
+ printf("Airoha EN8811H firmware loading not implemented\n");
+ return -EOPNOTSUPP;
+ }
+
+ ret = en8811h_read_fw(&buffer);
+ if (ret < 0)
+ goto en8811h_load_firmware_out;
+
+ ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1,
+ EN8811H_FW_CTRL_1_START);
+ if (ret < 0)
+ goto en8811h_load_firmware_out;
+
+ ret = air_buckpbus_reg_modify(phydev, EN8811H_FW_CTRL_2,
+ EN8811H_FW_CTRL_2_LOADING,
+ EN8811H_FW_CTRL_2_LOADING);
+ if (ret < 0)
+ goto en8811h_load_firmware_out;
+
+ ret = air_write_buf(phydev, AIR_FW_ADDR_DM, EN8811H_MD32_DM_SIZE,
+ (unsigned char *)buffer);
+ if (ret < 0)
+ goto en8811h_load_firmware_out;
+
+ ret = air_write_buf(phydev, AIR_FW_ADDR_DSP, EN8811H_MD32_DSP_SIZE,
+ (unsigned char *)buffer + EN8811H_MD32_DM_SIZE);
+ if (ret < 0)
+ goto en8811h_load_firmware_out;
+
+ ret = air_buckpbus_reg_modify(phydev, EN8811H_FW_CTRL_2,
+ EN8811H_FW_CTRL_2_LOADING, 0);
+ if (ret < 0)
+ goto en8811h_load_firmware_out;
+
+ ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1,
+ EN8811H_FW_CTRL_1_FINISH);
+ if (ret < 0)
+ goto en8811h_load_firmware_out;
+
+ ret = en8811h_wait_mcu_ready(phydev);
+
+ air_buckpbus_reg_read(phydev, EN8811H_FW_VERSION,
+ &priv->firmware_version);
+ printf("MD32 firmware version: %08x\n",
+ priv->firmware_version);
+
+en8811h_load_firmware_out:
+ free(buffer);
+ if (ret < 0)
+ printf("Firmware loading failed: %d\n", ret);
+
+ return ret;
+}
+
+static int en8811h_restart_mcu(struct phy_device *phydev)
+{
+ int ret;
+
+ ret = phy_write_mmd(phydev, 0x1e, 0x8009, 0x0);
+ if (ret < 0)
+ return ret;
+
+ ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1,
+ EN8811H_FW_CTRL_1_START);
+ if (ret < 0)
+ return ret;
+
+ return air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1,
+ EN8811H_FW_CTRL_1_FINISH);
+}
+
+static int air_led_hw_control_set(struct phy_device *phydev, u8 index,
+ unsigned long rules)
+{
+ struct en8811h_priv *priv = phydev->priv;
+ u16 on = 0, blink = 0;
+ int ret;
+
+ if (index >= EN8811H_LED_COUNT)
+ return -EINVAL;
+
+ priv->led[index].rules = rules;
+
+ if (rules & BIT(AIR_TRIGGER_NETDEV_FULL_DUPLEX))
+ on |= AIR_PHY_LED_ON_FDX;
+
+ if (rules & (BIT(AIR_TRIGGER_NETDEV_LINK_10) | BIT(AIR_TRIGGER_NETDEV_LINK)))
+ on |= AIR_PHY_LED_ON_LINK10;
+
+ if (rules & (BIT(AIR_TRIGGER_NETDEV_LINK_100) | BIT(AIR_TRIGGER_NETDEV_LINK)))
+ on |= AIR_PHY_LED_ON_LINK100;
+
+ if (rules & (BIT(AIR_TRIGGER_NETDEV_LINK_1000) | BIT(AIR_TRIGGER_NETDEV_LINK)))
+ on |= AIR_PHY_LED_ON_LINK1000;
+
+ if (rules & (BIT(AIR_TRIGGER_NETDEV_LINK_2500) | BIT(AIR_TRIGGER_NETDEV_LINK)))
+ on |= AIR_PHY_LED_ON_LINK2500;
+
+ if (rules & BIT(AIR_TRIGGER_NETDEV_RX)) {
+ blink |= AIR_PHY_LED_BLINK_10RX |
+ AIR_PHY_LED_BLINK_100RX |
+ AIR_PHY_LED_BLINK_1000RX |
+ AIR_PHY_LED_BLINK_2500RX;
+ }
+
+ if (rules & BIT(AIR_TRIGGER_NETDEV_TX)) {
+ blink |= AIR_PHY_LED_BLINK_10TX |
+ AIR_PHY_LED_BLINK_100TX |
+ AIR_PHY_LED_BLINK_1000TX |
+ AIR_PHY_LED_BLINK_2500TX;
+ }
+
+ if (blink || on) {
+ /* switch hw-control on, so led-on and led-blink are off */
+ clear_bit(AIR_PHY_LED_STATE_FORCE_ON,
+ &priv->led[index].state);
+ clear_bit(AIR_PHY_LED_STATE_FORCE_BLINK,
+ &priv->led[index].state);
+ } else {
+ priv->led[index].rules = 0;
+ }
+
+ ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_ON(index),
+ AIR_PHY_LED_ON_MASK, on);
+
+ if (ret < 0)
+ return ret;
+
+ return phy_write_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_BLINK(index),
+ blink);
+};
+
+static int air_led_init(struct phy_device *phydev, u8 index, u8 state, u8 pol)
+{
+ int val = 0;
+ int err;
+
+ if (index >= EN8811H_LED_COUNT)
+ return -EINVAL;
+
+ if (state == AIR_LED_ENABLE)
+ val |= AIR_PHY_LED_ON_ENABLE;
+ else
+ val &= ~AIR_PHY_LED_ON_ENABLE;
+
+ if (pol == AIR_ACTIVE_HIGH)
+ val |= AIR_PHY_LED_ON_POLARITY;
+ else
+ val &= ~AIR_PHY_LED_ON_POLARITY;
+
+ err = phy_write_mmd(phydev, 0x1f, AIR_PHY_LED_ON(index), val);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int air_leds_init(struct phy_device *phydev, int num, u16 dur, int mode)
+{
+ struct en8811h_priv *priv = phydev->priv;
+ int ret, i;
+
+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_DUR_BLINK,
+ dur);
+ if (ret < 0)
+ return ret;
+
+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_DUR_ON,
+ dur >> 1);
+ if (ret < 0)
+ return ret;
+
+ switch (mode) {
+ case AIR_LED_MODE_DISABLE:
+ ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_BCR,
+ AIR_PHY_LED_BCR_EXT_CTRL |
+ AIR_PHY_LED_BCR_MODE_MASK, 0);
+ break;
+ case AIR_LED_MODE_USER_DEFINE:
+ ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_BCR,
+ AIR_PHY_LED_BCR_EXT_CTRL |
+ AIR_PHY_LED_BCR_CLK_EN,
+ AIR_PHY_LED_BCR_EXT_CTRL |
+ AIR_PHY_LED_BCR_CLK_EN);
+ if (ret < 0)
+ return ret;
+ break;
+ default:
+ printf("LED mode %d is not supported\n", mode);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < num; ++i) {
+ ret = air_led_init(phydev, i, AIR_LED_ENABLE, AIR_ACTIVE_HIGH);
+ if (ret < 0) {
+ printf("LED%d init failed: %d\n", i, ret);
+ return ret;
+ }
+ air_led_hw_control_set(phydev, i, priv->led[i].rules);
+ }
+
+ return 0;
+}
+
+static int en8811h_config(struct phy_device *phydev)
+{
+ struct en8811h_priv *priv = phydev->priv;
+ ofnode node = phy_get_ofnode(phydev);
+ u32 pbus_value = 0;
+ int ret = 0;
+
+ /* If restart happened in .probe(), no need to restart now */
+ if (priv->mcu_needs_restart) {
+ ret = en8811h_restart_mcu(phydev);
+ if (ret < 0)
+ return ret;
+ } else {
+ ret = en8811h_load_firmware(phydev);
+ if (ret) {
+ printf("Load firmware fail.\n");
+ return ret;
+ }
+ /* Next calls to .config() mcu needs to restart */
+ priv->mcu_needs_restart = true;
+ }
+
+ ret = phy_write_mmd(phydev, 0x1e, 0x800c, 0x0);
+ if (ret < 0)
+ return ret;
+ ret = phy_write_mmd(phydev, 0x1e, 0x800d, 0x0);
+ if (ret < 0)
+ return ret;
+ ret = phy_write_mmd(phydev, 0x1e, 0x800e, 0x1101);
+ if (ret < 0)
+ return ret;
+ ret = phy_write_mmd(phydev, 0x1e, 0x800f, 0x0002);
+ if (ret < 0)
+ return ret;
+
+ /* Serdes polarity */
+ pbus_value = 0;
+ if (ofnode_read_bool(node, "airoha,pnswap-rx"))
+ pbus_value |= EN8811H_POLARITY_RX_REVERSE;
+ else
+ pbus_value &= ~EN8811H_POLARITY_RX_REVERSE;
+ if (ofnode_read_bool(node, "airoha,pnswap-tx"))
+ pbus_value &= ~EN8811H_POLARITY_TX_NORMAL;
+ else
+ pbus_value |= EN8811H_POLARITY_TX_NORMAL;
+ ret = air_buckpbus_reg_modify(phydev, EN8811H_POLARITY,
+ EN8811H_POLARITY_RX_REVERSE |
+ EN8811H_POLARITY_TX_NORMAL, pbus_value);
+ if (ret < 0)
+ return ret;
+
+ ret = air_leds_init(phydev, EN8811H_LED_COUNT, AIR_PHY_LED_DUR,
+ AIR_LED_MODE_USER_DEFINE);
+ if (ret < 0) {
+ printf("Failed to disable leds: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int en8811h_parse_status(struct phy_device *phydev)
+{
+ int ret = 0, reg_value;
+
+ phydev->duplex = DUPLEX_FULL;
+
+ reg_value = phy_read(phydev, MDIO_DEVAD_NONE, AIR_AUX_CTRL_STATUS);
+ if (reg_value < 0)
+ return reg_value;
+
+ switch (reg_value & AIR_AUX_CTRL_STATUS_SPEED_MASK) {
+ case AIR_AUX_CTRL_STATUS_SPEED_2500:
+ phydev->speed = SPEED_2500;
+ break;
+ case AIR_AUX_CTRL_STATUS_SPEED_1000:
+ phydev->speed = SPEED_1000;
+ break;
+ case AIR_AUX_CTRL_STATUS_SPEED_100:
+ phydev->speed = SPEED_100;
+ break;
+ default:
+ printf("Auto-neg error, defaulting to 100M/FD\n");
+ phydev->speed = SPEED_100;
+ break;
+ }
+
+ return ret;
+}
+
+static int en8811h_startup(struct phy_device *phydev)
+{
+ int ret = 0;
+
+ ret = genphy_update_link(phydev);
+ if (ret)
+ return ret;
+
+ return en8811h_parse_status(phydev);
+}
+
+static int en8811h_probe(struct phy_device *phydev)
+{
+ struct en8811h_priv *priv;
+
+ priv = malloc(sizeof(*priv));
+ if (!priv)
+ return -ENOMEM;
+ memset(priv, 0, sizeof(*priv));
+
+ priv->led[0].rules = AIR_DEFAULT_TRIGGER_LED0;
+ priv->led[1].rules = AIR_DEFAULT_TRIGGER_LED1;
+ priv->led[2].rules = AIR_DEFAULT_TRIGGER_LED2;
+
+ /* mcu has just restarted after firmware load */
+ priv->mcu_needs_restart = false;
+
+ phydev->priv = priv;
+
+ return 0;
+}
+
+U_BOOT_PHY_DRIVER(en8811h) = {
+ .name = "Airoha EN8811H",
+ .uid = EN8811H_PHY_ID,
+ .mask = 0x0ffffff0,
+ .config = &en8811h_config,
+ .probe = &en8811h_probe,
+ .startup = &en8811h_startup,
+ .shutdown = &genphy_shutdown,
+};
--
2.43.0
@@ -0,0 +1,275 @@
From a987f92a768291669eec1a8c5d413190bd9241ec Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mattias=20Walstr=C3=B6m?= <lazzer@gmail.com>
Date: Wed, 18 Feb 2026 17:00:42 +0100
Subject: [PATCH 6/7] Add bpi-r3-mini device tree
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Organization: Wires
Signed-off-by: Mattias Walström <lazzer@gmail.com>
---
arch/arm/dts/Makefile | 1 +
arch/arm/dts/mt7986a-bpi-r3-mini.dts | 238 +++++++++++++++++++++++++++
2 files changed, 239 insertions(+)
create mode 100644 arch/arm/dts/mt7986a-bpi-r3-mini.dts
diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile
index 6ad59aeed5f..3561e4c3651 100644
--- a/arch/arm/dts/Makefile
+++ b/arch/arm/dts/Makefile
@@ -1199,6 +1199,7 @@ dtb-$(CONFIG_ARCH_MEDIATEK) += \
mt7981-sd-rfb.dtb \
mt7986a-bpi-r3-sd.dtb \
mt7986a-bpi-r3-emmc.dtb \
+ mt7986a-bpi-r3-mini.dtb \
mt7986a-rfb.dtb \
mt7986b-rfb.dtb \
mt7986a-sd-rfb.dtb \
diff --git a/arch/arm/dts/mt7986a-bpi-r3-mini.dts b/arch/arm/dts/mt7986a-bpi-r3-mini.dts
new file mode 100644
index 00000000000..b8d0a01c9ed
--- /dev/null
+++ b/arch/arm/dts/mt7986a-bpi-r3-mini.dts
@@ -0,0 +1,238 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+/dts-v1/;
+#include "mt7986.dtsi"
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/input/linux-event-codes.h>
+
+/ {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ model = "Bananapi BPi-R3 Mini";
+ compatible = "mediatek,mt7986", "mediatek,mt7986-rfb";
+
+ chosen {
+ stdout-path = &uart0;
+ tick-timer = &timer0;
+ };
+
+ memory@40000000 {
+ device_type = "memory";
+ reg = <0x40000000 0x80000000>;
+ };
+
+ gpio-keys {
+ compatible = "gpio-keys";
+
+ button-reset {
+ label = "reset";
+ linux,code = <KEY_RESTART>;
+ gpios = <&gpio 7 GPIO_ACTIVE_LOW>;
+ };
+ };
+
+ leds {
+ compatible = "gpio-leds";
+
+ status_led: led-0 {
+ label = "green:status";
+ gpios = <&gpio 19 GPIO_ACTIVE_HIGH>;
+ };
+
+ led-1 {
+ label = "blue:wlan2g";
+ gpios = <&gpio 1 GPIO_ACTIVE_HIGH>;
+ };
+
+ led-2 {
+ label = "blue:wlan5g";
+ gpios = <&gpio 2 GPIO_ACTIVE_HIGH>;
+ };
+ };
+
+ reg_1p8v: regulator-1p8v {
+ compatible = "regulator-fixed";
+ regulator-name = "fixed-1.8V";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+
+ reg_3p3v: regulator-3p3v {
+ compatible = "regulator-fixed";
+ regulator-name = "fixed-3.3V";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-boot-on;
+ regulator-always-on;
+ };
+};
+
+&eth {
+ status = "okay";
+ pinctrl-names = "default";
+ pinctrl-0 = <&mdio_pins>;
+
+ mediatek,gmac-id = <0>;
+ phy-mode = "2500base-x";
+ phy-handle = <&phy14>;
+
+ phy14: eth-phy@e {
+ compatible = "ethernet-phy-id03a2.a411";
+ reg = <14>;
+
+ airoha,pnswap-rx;
+
+ reset-gpios = <&gpio 49 GPIO_ACTIVE_LOW>;
+ reset-assert-us = <10000>;
+ reset-deassert-us = <20000>;
+ };
+};
+
+&mmc0 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&mmc0_pins_default>;
+ bus-width = <8>;
+ max-frequency = <200000000>;
+ cap-mmc-highspeed;
+ cap-mmc-hw-reset;
+ vmmc-supply = <&reg_3p3v>;
+ vqmmc-supply = <&reg_1p8v>;
+ non-removable;
+ status = "okay";
+};
+
+&pinctrl {
+ mdio_pins: mdio-pins {
+ mux {
+ function = "eth";
+ groups = "mdc_mdio";
+ };
+
+ conf-en8811-pwr-a {
+ pins = "GPIO_11";
+ drive-strength = <MTK_DRIVE_8mA>;
+ bias-pull-down = <MTK_PUPD_SET_R1R0_11>;
+ output-low;
+ };
+
+ conf-en8811-pwr-b {
+ pins = "GPIO_12";
+ drive-strength = <MTK_DRIVE_8mA>;
+ bias-pull-down = <MTK_PUPD_SET_R1R0_11>;
+ output-low;
+ };
+ };
+
+ mmc0_pins_default: mmc0default {
+ mux {
+ function = "flash";
+ groups = "emmc_51";
+ };
+
+ conf-cmd-dat {
+ pins = "EMMC_DATA_0", "EMMC_DATA_1", "EMMC_DATA_2",
+ "EMMC_DATA_3", "EMMC_DATA_4", "EMMC_DATA_5",
+ "EMMC_DATA_6", "EMMC_DATA_7", "EMMC_CMD";
+ input-enable;
+ drive-strength = <MTK_DRIVE_4mA>;
+ bias-pull-up = <MTK_PUPD_SET_R1R0_01>;
+ };
+
+ conf-clk {
+ pins = "EMMC_CK";
+ drive-strength = <MTK_DRIVE_6mA>;
+ bias-pull-down = <MTK_PUPD_SET_R1R0_10>;
+ };
+
+ conf-dsl {
+ pins = "EMMC_DSL";
+ bias-pull-down = <MTK_PUPD_SET_R1R0_10>;
+ };
+
+ conf-rst {
+ pins = "EMMC_RSTB";
+ drive-strength = <MTK_DRIVE_4mA>;
+ bias-pull-up = <MTK_PUPD_SET_R1R0_01>;
+ };
+ };
+
+ spi_flash_pins: spi0-pins-func-1 {
+ mux {
+ function = "flash";
+ groups = "spi0", "spi0_wp_hold";
+ };
+
+ conf-pu {
+ pins = "SPI2_CS", "SPI2_HOLD", "SPI2_WP";
+ drive-strength = <MTK_DRIVE_8mA>;
+ bias-pull-up = <MTK_PUPD_SET_R1R0_11>;
+ };
+
+ conf-pd {
+ pins = "SPI2_CLK", "SPI2_MOSI", "SPI2_MISO";
+ drive-strength = <MTK_DRIVE_8mA>;
+ bias-pull-down = <MTK_PUPD_SET_R1R0_11>;
+ };
+ };
+
+ pwm_pins: pwm0-pins-func-1 {
+ mux {
+ function = "pwm";
+ groups = "pwm0";
+ };
+ };
+};
+
+&pwm {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pwm_pins>;
+ status = "okay";
+};
+
+&spi0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&spi_flash_pins>;
+ status = "okay";
+ must_tx;
+ enhance_timing;
+ dma_ext;
+ ipm_design;
+ support_quad;
+ tick_dly = <1>;
+ sample_sel = <0>;
+
+ spi_nand@0 {
+ compatible = "spi-nand";
+ reg = <0>;
+ spi-max-frequency = <20000000>;
+
+ partitions {
+ compatible = "fixed-partitions";
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ partition@0 {
+ label = "bl2";
+ reg = <0x0 0x200000>;
+ };
+
+ partition@200000 {
+ label = "ubi";
+ reg = <0x200000 0x7e00000>;
+ };
+ };
+ };
+
+};
+
+&uart0 {
+ status = "okay";
+};
+
+&watchdog {
+ status = "disabled";
+};
--
2.43.0
@@ -0,0 +1,562 @@
From 57b3cd8612a8da852f0796d97e6a7526cc4742cc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mattias=20Walstr=C3=B6m?= <lazzer@gmail.com>
Date: Wed, 18 Feb 2026 17:02:23 +0100
Subject: [PATCH 7/7] bpi-r3/r4: Add probe for specific model
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Organization: Wires
Probe for bpi-r3 and bpi-r3-mini and select correct device tree,
prepare for bpi-r4 (not supported yet, but soon), for bpi-r3,
look if there are phys connected, if so it is a mini, else it is
regular bpi-r3.
Signed-off-by: Mattias Walström <lazzer@gmail.com>
---
arch/arm/mach-mediatek/Kconfig | 3 +
board/mediatek/mt7986/Kconfig | 10 ++
board/mediatek/mt7986/Makefile | 1 +
board/mediatek/mt7986/bpir3.c | 229 +++++++++++++++++++++++++++
board/mediatek/mt7988/Kconfig | 9 ++
board/mediatek/mt7988/Makefile | 1 +
board/mediatek/mt7988/bpir4.c | 33 ++++
configs/mt7986a_bpir3_emmc_defconfig | 2 +
configs/mt7986a_bpir3_mini_defconfig | 66 ++++++++
configs/mt7986a_bpir3_sd_defconfig | 2 +
configs/mt7988a_bpir4_defconfig | 85 ++++++++++
11 files changed, 441 insertions(+)
create mode 100644 board/mediatek/mt7986/Kconfig
create mode 100644 board/mediatek/mt7986/bpir3.c
create mode 100644 board/mediatek/mt7988/Kconfig
create mode 100644 board/mediatek/mt7988/bpir4.c
create mode 100644 configs/mt7986a_bpir3_mini_defconfig
create mode 100644 configs/mt7988a_bpir4_defconfig
diff --git a/arch/arm/mach-mediatek/Kconfig b/arch/arm/mach-mediatek/Kconfig
index ff1fdee5c8d..def8a564d90 100644
--- a/arch/arm/mach-mediatek/Kconfig
+++ b/arch/arm/mach-mediatek/Kconfig
@@ -153,4 +153,7 @@ config MTK_BROM_HEADER_INFO
default "media=snand;nandinfo=2k+64" if TARGET_MT7981 || TARGET_MT7986 || TARGET_MT7988
default "lk=1" if TARGET_MT7623
+source "board/mediatek/mt7986/Kconfig"
+source "board/mediatek/mt7988/Kconfig"
+
endif
diff --git a/board/mediatek/mt7986/Kconfig b/board/mediatek/mt7986/Kconfig
new file mode 100644
index 00000000000..cea150b52d8
--- /dev/null
+++ b/board/mediatek/mt7986/Kconfig
@@ -0,0 +1,10 @@
+if TARGET_MT7986
+
+config BOARD_BPI_R3
+ bool "BananaPi BPI-R3 / BPI-R3-mini board support"
+ select BOARD_LATE_INIT
+ help
+ Enable runtime variant detection for BananaPi BPI-R3 and BPI-R3-mini,
+ selecting the correct device tree from a FIT image.
+
+endif
diff --git a/board/mediatek/mt7986/Makefile b/board/mediatek/mt7986/Makefile
index 7bb84fa2f4e..b006ee150bd 100644
--- a/board/mediatek/mt7986/Makefile
+++ b/board/mediatek/mt7986/Makefile
@@ -1,3 +1,4 @@
# SPDX-License-Identifier: GPL-2.0
obj-y += mt7986_rfb.o
+obj-$(CONFIG_BOARD_BPI_R3) += bpir3.o
diff --git a/board/mediatek/mt7986/bpir3.c b/board/mediatek/mt7986/bpir3.c
new file mode 100644
index 00000000000..01eef55bb74
--- /dev/null
+++ b/board/mediatek/mt7986/bpir3.c
@@ -0,0 +1,229 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * BPI-R3 / BPI-R3-mini board variant detection
+ */
+
+#include <env.h>
+#include <fdt_support.h>
+#include <image.h>
+#include <asm/global_data.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+enum bpir3_variant {
+ BPIR3,
+ BPIR3_MINI,
+};
+
+/*
+ * Detect BPI-R3 vs BPI-R3-mini by probing for the Airoha EN8811H PHY.
+ *
+ * BPI-R3-mini: EN8811H is directly on the MDIO bus at addr 14.
+ * GPIO49 (active-low) is its reset; PHYSID1 = 0x03a2.
+ * BPI-R3: MT7531AE switch via SGMII; nothing at MDIO addr 14 → 0xffff.
+ *
+ * NOTE: I2C 0x50 is NOT usable — BPI-R3 SFP cage EEPROMs occupy 0x500x5b.
+ */
+
+/*
+ * MT7986 GPIO — gpio_base = 0x1001f000
+ *
+ * DIR register: base + 0x000 + (pin/32)*0x10, 1 bit/pin (1=output)
+ * DOUT register: base + 0x100 + (pin/32)*0x10, 1 bit/pin
+ * MODE register: base + 0x300 + (pin*4/32)*0x10, 4 bits/pin (0=GPIO, 1=eth…)
+ */
+#define MT7986_GPIO_BASE 0x1001f000UL
+#define GPIO_DIR_REG(p) (MT7986_GPIO_BASE + 0x000 + ((p) / 32) * 0x10)
+#define GPIO_DOUT_REG(p) (MT7986_GPIO_BASE + 0x100 + ((p) / 32) * 0x10)
+#define GPIO_MODE_REG(p) (MT7986_GPIO_BASE + 0x300 + (((p) * 4) / 32) * 0x10)
+#define GPIO_MODE_SHIFT(p) (((p) * 4) % 32)
+
+/* EN8811H PHY reset: GPIO49, active-low */
+#define EN8811H_RST_GPIO 49
+/* EN8811H power control lines: active-low enables on some BPI-R3-mini dts */
+#define EN8811H_PWR_A_GPIO 11
+#define EN8811H_PWR_B_GPIO 12
+/* Alternative EN8811H power control lines used by other mini trees */
+#define EN8811H_PWR2_A_GPIO 16
+#define EN8811H_PWR2_B_GPIO 17
+
+/* MDC/MDIO pins: GPIO67 = MDC, GPIO68 = MDIO, both on function 1 ("eth") */
+#define MDC_GPIO 67
+#define MDIO_GPIO 68
+
+/*
+ * MT7986 Frame Engine: ethernet@15100000, GMAC at +0x10000
+ *
+ * PPSC (+0x0000): MDC clock config — bits[29:24] = divider, bit[4] = turbo
+ * PIAC (+0x0004): PHY indirect access — write then poll PHY_ACS_ST clear
+ */
+#define GMAC_BASE 0x15110000UL
+#define GMAC_PPSC (GMAC_BASE + 0x0000)
+#define GMAC_PIAC (GMAC_BASE + 0x0004)
+
+#define PPSC_MDC_CFG_MASK GENMASK(29, 24)
+#define PPSC_MDC_CFG(d) (((d) & 0x3f) << 24)
+#define PPSC_MDC_TURBO BIT(4)
+/* divider = 10 → MDC ≈ 2.5 MHz from 25 MHz reference */
+#define MDC_DIVIDER 10
+
+#define PIAC_ACS_ST BIT(31)
+#define PIAC_REG_S 25
+#define PIAC_PHY_S 20
+#define PIAC_CMD_S 18
+#define PIAC_ST_S 16
+#define PIAC_DATA_MASK 0xffffU
+#define PIAC_CMD_READ 2
+#define PIAC_ST_C22 1
+
+/* EN8811H PHY ID register values */
+#define EN8811H_PHYSID1 0x03a2
+#define EN8811H_PHYSID2_MASK 0xfff0
+#define EN8811H_PHYSID2 0xa410
+#define MII_PHYSID1 0x02 /* IEEE MII register 2 */
+#define MII_PHYSID2 0x03 /* IEEE MII register 3 */
+
+/* Set a GPIO pin's mode field (0 = GPIO, 1 = eth function, …) */
+static void gpio_set_mode(unsigned int pin, unsigned int func)
+{
+ clrsetbits_le32(GPIO_MODE_REG(pin),
+ 0xf << GPIO_MODE_SHIFT(pin),
+ (func & 0xf) << GPIO_MODE_SHIFT(pin));
+}
+
+/* Configure a GPIO pin as output and drive it high or low */
+static void gpio_out(unsigned int pin, int val)
+{
+ setbits_le32(GPIO_DIR_REG(pin), BIT(pin % 32));
+ if (val)
+ setbits_le32(GPIO_DOUT_REG(pin), BIT(pin % 32));
+ else
+ clrbits_le32(GPIO_DOUT_REG(pin), BIT(pin % 32));
+}
+
+/*
+ * Clause-22 MDIO read via the MT7986 GMAC PHY indirect access register.
+ * Returns the 16-bit register value, or -1 on timeout.
+ */
+static int mdio_read_c22(unsigned int phy, unsigned int reg)
+{
+ u32 val;
+ int i;
+
+ val = (PIAC_ST_C22 << PIAC_ST_S) |
+ (PIAC_CMD_READ << PIAC_CMD_S) |
+ (phy << PIAC_PHY_S) |
+ (reg << PIAC_REG_S);
+ writel(val | PIAC_ACS_ST, GMAC_PIAC);
+
+ for (i = 0; i < 5000; i++) {
+ val = readl(GMAC_PIAC);
+ if (!(val & PIAC_ACS_ST))
+ return (int)(val & PIAC_DATA_MASK);
+ udelay(1);
+ }
+
+ return -1; /* timeout */
+}
+
+static void enable_mini_power_lines(unsigned int gpio_a, unsigned int gpio_b)
+{
+ gpio_set_mode(gpio_a, 0); /* mode 0 = GPIO */
+ gpio_set_mode(gpio_b, 0); /* mode 0 = GPIO */
+ gpio_out(gpio_a, 0); /* active-low enable */
+ gpio_out(gpio_b, 0); /* active-low enable */
+}
+
+static void pulse_mini_phy_reset(void)
+{
+ gpio_set_mode(EN8811H_RST_GPIO, 0); /* mode 0 = GPIO */
+ gpio_out(EN8811H_RST_GPIO, 0); /* assert reset */
+ mdelay(10);
+ gpio_out(EN8811H_RST_GPIO, 1); /* deassert reset */
+ mdelay(20);
+}
+
+static bool is_en8811_id(int id1, int id2)
+{
+ return id1 == EN8811H_PHYSID1 &&
+ (id2 & EN8811H_PHYSID2_MASK) == EN8811H_PHYSID2;
+}
+
+static int probe_en8811_addr(unsigned int phy_addr)
+{
+ int id1, id2;
+
+ id1 = mdio_read_c22(phy_addr, MII_PHYSID1);
+ id2 = mdio_read_c22(phy_addr, MII_PHYSID2);
+
+ if (id1 < 0 || id2 < 0)
+ return 0;
+
+ return is_en8811_id(id1, id2);
+}
+
+static enum bpir3_variant detect_bpir3_variant(void)
+{
+ unsigned int pwr_a[] = { EN8811H_PWR_A_GPIO, EN8811H_PWR2_A_GPIO };
+ unsigned int pwr_b[] = { EN8811H_PWR_B_GPIO, EN8811H_PWR2_B_GPIO };
+ unsigned int i;
+
+ /* Switch GPIO67 (MDC) and GPIO68 (MDIO) to eth function */
+ gpio_set_mode(MDC_GPIO, 1);
+ gpio_set_mode(MDIO_GPIO, 1);
+
+ /* Configure MDC clock: enable turbo + set safe divider (~2.5 MHz) */
+ setbits_le32(GMAC_PPSC, PPSC_MDC_TURBO);
+ clrsetbits_le32(GMAC_PPSC, PPSC_MDC_CFG_MASK, PPSC_MDC_CFG(MDC_DIVIDER));
+
+ /*
+ * Enable EN8811H power rails and probe; some mini boards use
+ * GPIO11/12, others GPIO16/17.
+ */
+ for (i = 0; i < ARRAY_SIZE(pwr_a); i++) {
+ enable_mini_power_lines(pwr_a[i], pwr_b[i]);
+ pulse_mini_phy_reset();
+
+ if (probe_en8811_addr(14) || probe_en8811_addr(15)) {
+ /* Assert reset; DM eth-phy will deassert with proper delays */
+ gpio_out(EN8811H_RST_GPIO, 0);
+ return BPIR3_MINI;
+ }
+ }
+
+ return BPIR3;
+}
+
+int board_fit_config_name_match(const char *name)
+{
+ static int variant = -1;
+
+ if (variant < 0)
+ variant = detect_bpir3_variant();
+
+ switch (variant) {
+ case BPIR3_MINI:
+ return strcmp(name, "mt7986a-bpi-r3-mini") ? -1 : 0;
+ case BPIR3:
+ default:
+ /*
+ * Accept both sd and emmc variants of BPI-R3;
+ * storage type is determined by spl_boot_device(), not here.
+ */
+ return (strcmp(name, "mt7986a-bpi-r3-sd") == 0 ||
+ strcmp(name, "mt7986a-bpi-r3-emmc") == 0) ? 0 : -1;
+ }
+}
+
+int board_late_init(void)
+{
+ const char *model = fdt_getprop(gd->fdt_blob, 0, "model", NULL);
+
+ if (model && strstr(model, "Mini"))
+ env_set("fdtfile", "mediatek/mt7986a-bananapi-bpi-r3-mini.dtb");
+
+ return 0;
+}
diff --git a/board/mediatek/mt7988/Kconfig b/board/mediatek/mt7988/Kconfig
new file mode 100644
index 00000000000..24c70417ec5
--- /dev/null
+++ b/board/mediatek/mt7988/Kconfig
@@ -0,0 +1,9 @@
+if TARGET_MT7988
+
+config BOARD_BPI_R4
+ bool "BananaPi BPI-R4 / BPI-R4-2g5 board support"
+ help
+ Enable runtime variant detection for BananaPi BPI-R4 and BPI-R4-2g5,
+ selecting the correct device tree from a FIT image.
+
+endif
diff --git a/board/mediatek/mt7988/Makefile b/board/mediatek/mt7988/Makefile
index f1249ab3715..157ee371eae 100644
--- a/board/mediatek/mt7988/Makefile
+++ b/board/mediatek/mt7988/Makefile
@@ -1,3 +1,4 @@
# SPDX-License-Identifier: GPL-2.0
obj-y += mt7988_rfb.o
+obj-$(CONFIG_BOARD_BPI_R4) += bpir4.o
diff --git a/board/mediatek/mt7988/bpir4.c b/board/mediatek/mt7988/bpir4.c
new file mode 100644
index 00000000000..46e22d38135
--- /dev/null
+++ b/board/mediatek/mt7988/bpir4.c
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * BPI-R4 / BPI-R4-2g5 board variant detection
+ */
+
+#include <image.h>
+#include <linux/string.h>
+
+enum bpir4_variant {
+ BPIR4,
+ BPIR4_2G5,
+};
+
+/*
+ * Detect which BPI-R4 variant this board is.
+ * TODO: implement real hardware detection
+ */
+static enum bpir4_variant detect_bpir4_variant(void)
+{
+ /* Stub: always report BPI-R4 */
+ return BPIR4;
+}
+
+int board_fit_config_name_match(const char *name)
+{
+ switch (detect_bpir4_variant()) {
+ case BPIR4_2G5:
+ return strcmp(name, "mt7988a-bananapi-bpi-r4-2g5") ? -1 : 0;
+ case BPIR4:
+ default:
+ return strcmp(name, "mt7988a-bananapi-bpi-r4") ? -1 : 0;
+ }
+}
diff --git a/configs/mt7986a_bpir3_emmc_defconfig b/configs/mt7986a_bpir3_emmc_defconfig
index ef6a4822c18..193acb4a9b4 100644
--- a/configs/mt7986a_bpir3_emmc_defconfig
+++ b/configs/mt7986a_bpir3_emmc_defconfig
@@ -9,6 +9,8 @@ CONFIG_ENV_SIZE=0x80000
CONFIG_ENV_OFFSET=0x300000
CONFIG_DEFAULT_DEVICE_TREE="mt7986a-bpi-r3-emmc"
CONFIG_TARGET_MT7986=y
+CONFIG_BOARD_BPI_R3=y
+CONFIG_SPL_LOAD_FIT=y
CONFIG_SYS_LOAD_ADDR=0x46000000
CONFIG_DEBUG_UART_BASE=0x11002000
CONFIG_DEBUG_UART_CLOCK=40000000
diff --git a/configs/mt7986a_bpir3_mini_defconfig b/configs/mt7986a_bpir3_mini_defconfig
new file mode 100644
index 00000000000..39e44555c55
--- /dev/null
+++ b/configs/mt7986a_bpir3_mini_defconfig
@@ -0,0 +1,66 @@
+CONFIG_ARM=y
+CONFIG_SYS_HAS_NONCACHED_MEMORY=y
+CONFIG_POSITION_INDEPENDENT=y
+CONFIG_ARCH_MEDIATEK=y
+CONFIG_TEXT_BASE=0x41e00000
+CONFIG_SYS_MALLOC_F_LEN=0x4000
+CONFIG_NR_DRAM_BANKS=1
+CONFIG_ENV_SIZE=0x80000
+CONFIG_ENV_OFFSET=0x300000
+CONFIG_DEFAULT_DEVICE_TREE="mt7986a-bpi-r3-mini"
+CONFIG_TARGET_MT7986=y
+CONFIG_BOARD_BPI_R3=y
+CONFIG_SPL_LOAD_FIT=y
+CONFIG_SYS_LOAD_ADDR=0x46000000
+CONFIG_DEBUG_UART_BASE=0x11002000
+CONFIG_DEBUG_UART_CLOCK=40000000
+CONFIG_DEBUG_UART=y
+# CONFIG_EFI_LOADER is not set
+# CONFIG_AUTOBOOT is not set
+CONFIG_DEFAULT_FDT_FILE="mt7986a-bpi-r3-mini"
+CONFIG_SYS_CBSIZE=512
+CONFIG_SYS_PBSIZE=1049
+CONFIG_LOGLEVEL=7
+CONFIG_LOG=y
+CONFIG_SYS_PROMPT="BPI-R3> "
+# CONFIG_BOOTM_NETBSD is not set
+# CONFIG_BOOTM_PLAN9 is not set
+# CONFIG_BOOTM_RTEMS is not set
+# CONFIG_BOOTM_VXWORKS is not set
+# CONFIG_CMD_ELF is not set
+# CONFIG_CMD_UNLZ4 is not set
+# CONFIG_CMD_UNZIP is not set
+CONFIG_CMD_GPIO=y
+CONFIG_CMD_GPT=y
+CONFIG_CMD_GPT_RENAME=y
+CONFIG_CMD_LSBLK=y
+CONFIG_CMD_MMC=y
+CONFIG_CMD_PART=y
+CONFIG_CMD_READ=y
+CONFIG_CMD_PING=y
+CONFIG_CMD_SMC=y
+CONFIG_CMD_FAT=y
+CONFIG_CMD_FS_GENERIC=y
+CONFIG_PARTITION_TYPE_GUID=y
+CONFIG_ENV_OVERWRITE=y
+CONFIG_ENV_IS_IN_MMC=y
+CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG=y
+CONFIG_NET_RANDOM_ETHADDR=y
+CONFIG_REGMAP=y
+CONFIG_SYSCON=y
+CONFIG_CLK=y
+CONFIG_MMC_HS200_SUPPORT=y
+CONFIG_MMC_MTK=y
+CONFIG_PHY_FIXED=y
+CONFIG_MEDIATEK_ETH=y
+CONFIG_PINCTRL=y
+CONFIG_PINCONF=y
+CONFIG_PINCTRL_MT7986=y
+CONFIG_POWER_DOMAIN=y
+CONFIG_MTK_POWER_DOMAIN=y
+CONFIG_DM_REGULATOR=y
+CONFIG_DM_REGULATOR_FIXED=y
+CONFIG_DM_SERIAL=y
+CONFIG_MTK_SERIAL=y
+CONFIG_FAT_WRITE=y
+CONFIG_HEXDUMP=y
diff --git a/configs/mt7986a_bpir3_sd_defconfig b/configs/mt7986a_bpir3_sd_defconfig
index 3d971f5c313..e498b58a4a5 100644
--- a/configs/mt7986a_bpir3_sd_defconfig
+++ b/configs/mt7986a_bpir3_sd_defconfig
@@ -9,6 +9,8 @@ CONFIG_ENV_SIZE=0x80000
CONFIG_ENV_OFFSET=0x300000
CONFIG_DEFAULT_DEVICE_TREE="mt7986a-bpi-r3-sd"
CONFIG_TARGET_MT7986=y
+CONFIG_BOARD_BPI_R3=y
+CONFIG_SPL_LOAD_FIT=y
CONFIG_SYS_LOAD_ADDR=0x46000000
CONFIG_DEBUG_UART_BASE=0x11002000
CONFIG_DEBUG_UART_CLOCK=40000000
diff --git a/configs/mt7988a_bpir4_defconfig b/configs/mt7988a_bpir4_defconfig
new file mode 100644
index 00000000000..e2e128ec29d
--- /dev/null
+++ b/configs/mt7988a_bpir4_defconfig
@@ -0,0 +1,85 @@
+CONFIG_ARM=y
+CONFIG_SYS_HAS_NONCACHED_MEMORY=y
+CONFIG_POSITION_INDEPENDENT=y
+CONFIG_ARCH_MEDIATEK=y
+CONFIG_TEXT_BASE=0x41e00000
+CONFIG_SYS_MALLOC_F_LEN=0x4000
+CONFIG_NR_DRAM_BANKS=1
+CONFIG_DEFAULT_DEVICE_TREE="mt7988a-bananapi-bpi-r4"
+CONFIG_TARGET_MT7988=y
+CONFIG_BOARD_BPI_R4=y
+CONFIG_SPL_LOAD_FIT=y
+CONFIG_SYS_LOAD_ADDR=0x46000000
+CONFIG_DEBUG_UART_BASE=0x11000000
+CONFIG_DEBUG_UART_CLOCK=40000000
+CONFIG_DEBUG_UART=y
+# CONFIG_EFI_LOADER is not set
+# CONFIG_AUTOBOOT is not set
+CONFIG_DEFAULT_FDT_FILE="mt7988a-bananapi-bpi-r4"
+CONFIG_SYS_CBSIZE=512
+CONFIG_SYS_PBSIZE=1049
+CONFIG_LOGLEVEL=7
+CONFIG_LOG=y
+CONFIG_SYS_PROMPT="BPI-R4> "
+# CONFIG_BOOTM_NETBSD is not set
+# CONFIG_BOOTM_PLAN9 is not set
+# CONFIG_BOOTM_RTEMS is not set
+# CONFIG_BOOTM_VXWORKS is not set
+# CONFIG_CMD_ELF is not set
+CONFIG_CMD_CLK=y
+CONFIG_CMD_DM=y
+CONFIG_CMD_GPIO=y
+CONFIG_CMD_PWM=y
+CONFIG_CMD_MMC=y
+CONFIG_CMD_MTD=y
+CONFIG_CMD_PING=y
+CONFIG_CMD_SMC=y
+CONFIG_DOS_PARTITION=y
+CONFIG_EFI_PARTITION=y
+CONFIG_PARTITION_TYPE_GUID=y
+CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG=y
+CONFIG_USE_IPADDR=y
+CONFIG_IPADDR="192.168.1.1"
+CONFIG_USE_NETMASK=y
+CONFIG_NETMASK="255.255.255.0"
+CONFIG_USE_SERVERIP=y
+CONFIG_SERVERIP="192.168.1.2"
+CONFIG_PROT_TCP=y
+CONFIG_NET_RANDOM_ETHADDR=y
+CONFIG_REGMAP=y
+CONFIG_SYSCON=y
+CONFIG_CLK=y
+CONFIG_MMC_HS200_SUPPORT=y
+CONFIG_MMC_MTK=y
+CONFIG_MTD=y
+CONFIG_DM_MTD=y
+CONFIG_MTD_SPI_NAND=y
+CONFIG_DM_SPI_FLASH=y
+CONFIG_SPI_FLASH_SFDP_SUPPORT=y
+CONFIG_SPI_FLASH_EON=y
+CONFIG_SPI_FLASH_GIGADEVICE=y
+CONFIG_SPI_FLASH_ISSI=y
+CONFIG_SPI_FLASH_MACRONIX=y
+CONFIG_SPI_FLASH_SPANSION=y
+CONFIG_SPI_FLASH_STMICRO=y
+CONFIG_SPI_FLASH_WINBOND=y
+CONFIG_SPI_FLASH_XMC=y
+CONFIG_SPI_FLASH_XTX=y
+CONFIG_SPI_FLASH_MTD=y
+CONFIG_PHY_FIXED=y
+CONFIG_MEDIATEK_ETH=y
+CONFIG_PINCTRL=y
+CONFIG_PINCONF=y
+CONFIG_PINCTRL_MT7988=y
+CONFIG_POWER_DOMAIN=y
+CONFIG_MTK_POWER_DOMAIN=y
+CONFIG_DM_PWM=y
+CONFIG_PWM_MTK=y
+CONFIG_RAM=y
+CONFIG_DM_SERIAL=y
+CONFIG_MTK_SERIAL=y
+CONFIG_SPI=y
+CONFIG_DM_SPI=y
+CONFIG_MTK_SPIM=y
+CONFIG_LZO=y
+CONFIG_HEXDUMP=y
--
2.43.0
+49
View File
@@ -259,6 +259,50 @@ download_bootloader()
log "Bootloader ready in temporary directory"
}
# Build EN8811H firmware partition image as an ext4 filesystem containing
# EthMD32.dm.bin and EthMD32.DSP.bin. Returns 0 when created, 1 when source
# files are missing.
prepare_en8811h_fw_image()
{
src_dir="$1"
out_img="$2"
dm_fw="${src_dir}/EthMD32.dm.bin"
dsp_fw="${src_dir}/EthMD32.DSP.bin"
fw_staging=""
[ -f "$dm_fw" ] && [ -f "$dsp_fw" ] || return 1
fw_staging=$(mktemp -d)
cp "$dm_fw" "$fw_staging/EthMD32.dm.bin"
cp "$dsp_fw" "$fw_staging/EthMD32.DSP.bin"
rm -f "$out_img"
truncate -s 2M "$out_img"
if command -v mke2fs >/dev/null 2>&1; then
mke2fs -q -t ext4 -L en8811h_fw -d "$fw_staging" "$out_img"
elif command -v mkfs.ext4 >/dev/null 2>&1; then
mkfs.ext4 -q -L en8811h_fw -d "$fw_staging" "$out_img"
else
rm -rf "$fw_staging"
die "mke2fs/mkfs.ext4 not found, cannot create en8811h-fw.bin"
fi
rm -rf "$fw_staging"
return 0
}
maybe_prepare_en8811h_fw_image()
{
out_img="${BINARIES_DIR}/en8811h-fw.bin"
[ -f "$out_img" ] && return 0
prepare_en8811h_fw_image "${ROOT_DIR}/images/airoha" "$out_img" || \
prepare_en8811h_fw_image "${ROOT_DIR}/airoha" "$out_img" || \
prepare_en8811h_fw_image "${BINARIES_DIR}/airoha" "$out_img" || return 0
}
# Discover boot files for Raspberry Pi boot partition
# Scans rpi-firmware directory and builds file list for genimage
discover_rpi_boot_files()
@@ -519,6 +563,11 @@ if [ -n "$DOWNLOAD_BOOT" ]; then
fi
fi
if [ "$BOARD" = "bananapi-bpi-r3" ]; then
maybe_prepare_en8811h_fw_image
[ -f "${BINARIES_DIR}/en8811h-fw.bin" ] || die "Missing EN8811H firmware image: ${BINARIES_DIR}/en8811h-fw.bin"
fi
# Template expansion
log "Generating genimage configuration for $BOARD..."