[PATCH 01/19] ARM: mvebu: add board support for Netgear RN102
Sascha Hauer
s.hauer at pengutronix.de
Mon Jul 27 06:27:48 PDT 2026
Hi Luca,
Thanks for the series. Support for more consumer devices is greatly
appreciated.
On 2026-07-23 15:57, Luca Lauro via B4 Relay wrote:
> From: Luca Lauro <famlauro93l at gmail.com>
Signed-off-by is missing, see https://barebox.org/doc/latest/devel/contributing.html#license-and-dco
>
> ---
> arch/arm/boards/Makefile | 1 +
> arch/arm/boards/netgear-rn102/Makefile | 4 +
> arch/arm/boards/netgear-rn102/board.c | 243 +++++++++++++++++++++++++++++++++
> rebuild-series.sh | 39 ++++++
> 4 files changed, 287 insertions(+)
>
> diff --git a/arch/arm/boards/Makefile b/arch/arm/boards/Makefile
> index dd2f2c324e..aca1b45a81 100644
> --- a/arch/arm/boards/Makefile
> +++ b/arch/arm/boards/Makefile
> @@ -64,6 +64,7 @@ obj-$(CONFIG_MACH_MARVELL_ARMADA_XP_DB) += marvell-armada-xp-db/
> obj-$(CONFIG_MACH_MX23EVK) += freescale-mx23-evk/
> obj-$(CONFIG_MACH_MX28EVK) += freescale-mx28-evk/
> obj-$(CONFIG_MACH_MYIRTECH_X335X) += myirtech-x335x/
> +obj-$(CONFIG_MACH_NETGEAR_RN102) += netgear-rn102/
> obj-$(CONFIG_MACH_NETGEAR_RN104) += netgear-rn104/
> obj-$(CONFIG_MACH_NETGEAR_RN2120) += netgear-rn2120/
> obj-$(CONFIG_MACH_NVIDIA_BEAVER) += nvidia-beaver/
> diff --git a/arch/arm/boards/netgear-rn102/Makefile b/arch/arm/boards/netgear-rn102/Makefile
> new file mode 100644
> index 0000000000..da63d2625f
> --- /dev/null
> +++ b/arch/arm/boards/netgear-rn102/Makefile
> @@ -0,0 +1,4 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +
> +obj-y += board.o
> +lwl-y += lowlevel.o
> diff --git a/arch/arm/boards/netgear-rn102/board.c b/arch/arm/boards/netgear-rn102/board.c
> new file mode 100644
> index 0000000000..c6673e9356
> --- /dev/null
> +++ b/arch/arm/boards/netgear-rn102/board.c
> @@ -0,0 +1,243 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +
> +#include <common.h>
> +#include <init.h>
> +#include <gpio.h>
> +#include <driver.h>
> +#include <linux/device.h>
> +#include <linux/mbus.h>
> +#include <mach/mvebu/armada-370-xp-regs.h>
> +#include <bbu.h>
> +
> +/*
> + * Early GPIO0 MMIO
> + *
> + * GPIO driver arrives too late for the disks to be
> + * ready in time for AHCI driver probe.
> + * So we use GPIO0 direct access for:
> + * - reading disk presence monitoring pins
> + * - enable powerup for bays that detect disk presence
> + * - display bay status with dedicated LEDs
> + */
> +#define GPIO0_BASE (ARMADA_370_XP_INT_REGS_BASE + 0x18100)
> +#define GPIO_OUT 0x00
> +#define GPIO_OUT_EN 0x04
> +#define GPIO_BLINK_EN 0x08
> +#define GPIO_IN 0x10
> +#define GPIO_BLINK_CNT_SEL 0x20
> +#define GPIO_BLINK_CNT_A_ON 0xc0
> +#define GPIO_BLINK_CNT_A_OFF 0xc4
> +
> +static inline void gpio0_set_output(int pin)
> +{
> + u32 v = readl(GPIO0_BASE + GPIO_OUT_EN);
Please use Kernel coding style for barebox. Means, use tabs instead of
spaces and likely more stuff ./scripts/checkpatch.pl mourns about.
These functions seem to duplicate GPIO support. We already have a GPIO
driver, so these could likely be dropped in favour for regular
gpio_set_value and friends.
> +
> + v &= ~(1 << pin);
> + writel(v, GPIO0_BASE + GPIO_OUT_EN);
> +}
> +
> +static inline void gpio0_set_input(int pin)
> +{
> + u32 v = readl(GPIO0_BASE + GPIO_OUT_EN);
> +
> + v |= (1 << pin);
> + writel(v, GPIO0_BASE + GPIO_OUT_EN);
> +}
> +
> +static inline void gpio0_write(int pin, int val)
> +{
> + u32 v = readl(GPIO0_BASE + GPIO_OUT);
> +
> + if (val)
> + v |= (1 << pin);
> + else
> + v &= ~(1 << pin);
> +
> + writel(v, GPIO0_BASE + GPIO_OUT);
> +}
> +
> +static inline int gpio0_read(int pin)
> +{
> + return !!(readl(GPIO0_BASE + GPIO_IN) & (1 << pin));
> +}
> +
> +static void gpio0_blink(int pin, int on_ms, int off_ms)
> +{
> + u32 v;
> +
> + gpio0_set_output(pin);
> +
> + /* set blink counter A on and off time in core clok cycles */
> + writel(10*on_ms, GPIO0_BASE + GPIO_BLINK_CNT_A_ON);
> + writel(10*off_ms, GPIO0_BASE + GPIO_BLINK_CNT_A_OFF);
> +
> + /* use blink counter A for selected pin */
> + v = readl(GPIO0_BASE + GPIO_BLINK_CNT_SEL);
> + v &= ~(1 << pin);
> + writel(v, GPIO0_BASE + GPIO_BLINK_CNT_SEL);
> +
> + /* enable blink for selected pin */
> + v = readl(GPIO0_BASE + GPIO_BLINK_EN);
> + v |= (1 << pin);
> + writel(v, GPIO0_BASE + GPIO_BLINK_EN);
> +}
> +
> +static void gpio0_blink_disable(int pin)
> +{
> + u32 v;
> +
> + v = readl(GPIO0_BASE + GPIO_BLINK_EN);
> + v &= ~(1 << pin);
> + writel(v, GPIO0_BASE + GPIO_BLINK_EN);
> +}
> +
> +/* HDD bays description */
> +
> +enum disk_state {
> + DISK_ABSENT = 0,
> + DISK_PRESENT,
> + DISK_READY,
> +};
> +
> +struct rn102_disk_bay {
> + int gpio_detect; /* input, active-low */
> + int gpio_power; /* output */
> + int gpio_led; /* output, active-low */
> + enum disk_state state;
> +};
> +
> +#define RN102_NUM_DISK_BAYS 2
> +
> +static struct rn102_disk_bay rn102_bays[RN102_NUM_DISK_BAYS] = {
> + { 12, 13, 15, DISK_ABSENT }, /* Bay 1, default to disk absent */
> + { 10, 11, 14, DISK_ABSENT }, /* Bay 2, default to disk absent */
> +};
> +
> +static void setup_bays(void) {
> + pr_info("Early disk power-on...\n");
> +
> + for (int i = 0; i < RN102_NUM_DISK_BAYS; i++) {
> + int present;
> +
> + gpio0_set_input(rn102_bays[i].gpio_detect);
> + present = (gpio0_read(rn102_bays[i].gpio_detect) == 0);
> +
> + gpio0_set_output(rn102_bays[i].gpio_power);
> +
> + if (present) {
> + pr_info("Bay %d: disk detected, powering on\n", i + 1);
> + gpio0_blink(rn102_bays[i].gpio_led, 500, 500);
> + gpio0_write(rn102_bays[i].gpio_power, 1);
> + rn102_bays[i].state = DISK_PRESENT;
> + } else {
> + pr_info("Bay %d empty\n", i + 1);
> + gpio0_write(rn102_bays[i].gpio_led, 0);
> + gpio0_write(rn102_bays[i].gpio_power, 0);
> + rn102_bays[i].state = DISK_ABSENT;
> + }
> + }
> +}
> +
> +/*
> + * After waiting for present disks spinup,
> + * we ask explicitly for corresponding ATA devices probe.
> + */
> +static void init_disks(void) {
> + pr_info("Waiting for disks spinup...\n");
> + mdelay(8000);
> +
> + pr_info("Detecting ATA devices:\n");
> + for (int i = 0; i < RN102_NUM_DISK_BAYS; i++) {
> + char name[8];
> + struct device *dev;
> +
> + if (rn102_bays[i].state != DISK_PRESENT) {
> + gpio0_write(rn102_bays[i].gpio_power, 0);
> + gpio0_blink_disable(rn102_bays[i].gpio_led);
> + gpio0_write(rn102_bays[i].gpio_led, 0);
> + continue;
> + }
> +
> + snprintf(name, sizeof(name), "ata%d", i);
> +
> + dev = get_device_by_name(name);
> + if (!dev) {
> + pr_warn("%s not found\n", name);
> + gpio0_write(rn102_bays[i].gpio_power, 0);
> + gpio0_blink(rn102_bays[i].gpio_led, 1000, 1000);
> + continue;
> + }
> +
> + pr_info("Connecting %s to disk %d\n", name, i + 1);
> + device_detect(dev);
> + gpio0_blink_disable(rn102_bays[i].gpio_led);
> + gpio0_write(rn102_bays[i].gpio_led, 1);
> + rn102_bays[i].state = DISK_READY;
> + }
> +}
> +
> +/*
> + * USB0 → DRAM MBUS windows
> + *
> + * frontal USB 2.0 port of RN102 is connected to the SoC usb0.
> + * Here we enable MBUS windows toward all the DRAM using
> + * informations already gathered by mvebu_mbus_dram_info().
> + */
> +// #define USB0_BRIDGE_BASE (ARMADA_370_XP_USB_BASE + 0x300)
> +// #define USB_WIN_CTRL(n) (USB0_BRIDGE_BASE + 0x20 + (n) * 0x10)
> +// #define USB_WIN_BASE(n) (USB0_BRIDGE_BASE + 0x24 + (n) * 0x10)
> +
> +static void setup_usb0(void) {
> + // /* Window0: 512MB @ 0x00000000 */
> + // writel(0x00000000, USB_WIN_BASE(0));
> + // writel(0x1FFF0E01, USB_WIN_CTRL(0));
> +
> + // /* Window1: 512MB @ 0xB0000000 (placeholder) */
> + // writel(0xB0000000, USB_WIN_BASE(1));
> + // writel(0x1FFE841, USB_WIN_CTRL(1));
> +
> + writel(0x2, 0xf1051404); /* enable force suspend */
> + u32 pwr = readl(0xf1051400);
> + pwr &= ~BIT(2);
> + writel(pwr, 0xf1051400); /* force SUSPENDM=0 */
> +}
> +
> +
> +/* Early init: setup specific hardware without blocking initialization. */
> +static int rn102_early_poweron(void)
> +{
> + writel(0xC6, 0xf1020228); /* CFU configuration */
> + setup_usb0();
> + setup_bays();
> +
> + return 0;
> +}
> +postcore_initcall(rn102_early_poweron);
A single barebox image can be compiled for multiple boards/SoCs, so
such an initcall will be executed on other boards once this board is
compiled in. You either have to protect them with some
if (!of_machine_is_compatible("foo"))
return 0;
Preferred way is to write the board specific code as a platform driver
which of-matches against the root node of the device tree, see other
boards following this pattern.
> +
> +static int rn102_init(void)
> +{
> + init_disks();
> +
> + return 0;
> +}
> +device_initcall(rn102_init);
> +
> +
> +/* BareBox Update handlers */
> +static int rn102_register_bbu(void)
> +{
> + bbu_register_std_file_update("bootloader", 0,
> + "/dev/nand0.bootloader",
> + filetype_kwbimage_v1);
> +
> + bbu_register_std_file_update("kernel", 0,
> + "/dev/nand0.kernel",
> + filetype_arm_zimage);
> +
> + bbu_register_std_file_update("minirootfs", 0,
> + "/dev/nand0.minirootfs",
> + filetype_gzip);
> +
> + return 0;
> +}
> +late_initcall(rn102_register_bbu);
> diff --git a/rebuild-series.sh b/rebuild-series.sh
This shouldn't be part of the series.
Sascha
--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
More information about the barebox
mailing list