OpenWrt Forum Archive

Topic: Latest Mikrotik Groove 52HPn without NAND

The content of this topic has been archived on 14 Apr 2018. There are no obvious gaps in this topic, but there may still be some posts missing at the end.

I found that the latest Mikrotik 52HPn radios do not contain a NAND flash chip.

These devices contain a 16MB WInBond W25Q128FV Serial SPI flash and unpopulated NAND flash pads.

Is there an OpenWRT driver for this SPI flash configuration on the groove radios?

The mach-rb91x.c module defines a second SPI bus for the SPI flash with a CS on GPIO#23 using the following:

    {
        .bus_num    = 0,
        .chip_select    = 1,
        .max_speed_hz    = 10000000,
        .modalias    = "74x164",
        .platform_data    = &rb711gr100_ssr_data,
        .controller_data = &rb711gr100_spi1_cdata
    }

However, the 74x164 driver requires CONFIG_OF, and none of the Mikrotik devices support OpenFirmware.  Nowhere in the ath79 setup code actually calls gpiochip_add to create a gpio_chip for this device.

Does anyone know how the second serial SPI flash chips are actually connected on the Mikrotik Groove radios?

@jpolsonaz, any recent luck with this?

Yes, I finally got these Groove 52HPn radios working!

It turns out that when Mikrotik dropped the NAND flash, they also swapped out the previous 128K w25x10 NOR flash with a 16MB w25q128 flash chip.

Normally, the flash driver would auto-detect the larger flash chip based on the JEDEC id, but the current mach-rb91x.c defines the flash chip as a 64K pm25lv512 which doesn't have any JEDEC id so the flash driver couldn't auto-detect the flash chip.

However, a more difficult problem was building a kernel image for NOR flash which the Mikrotik routerboot could recognize.  Mikrotik routerboot only recognizes yaffs file systems, and the Linux yaffs driver only understands NAND flash, not NOR flash!  Mikrotik evidently uses custom patches to their yaffs driver which requires a non-standard 1K block size with 16-byte OOB data written between each NOR flash block.

After much searching, I found that LEDE includes support for many SPI NOR flash based Mikrotik devices, although they don't yet support the 52HPn.  However, LEDE includes a new tool named kernel2minor (ie. kernel to Mikrotik NOR).  This tool takes a standard compressed Linux kernel and creates a properly aligned yaffs image which can be written to either NAND or NOR flash on Mikrotik devices.  This tool eliminates the need for a yaffs2 driver in the Linux kernel to just write the silly Mikrotik kernel image!

Below are my patches to the mach-rb91x.c driver to handle the Groove 52HPn routers.  Note that I haven't tested these changes for compatibility with the previously supported rb711 board, however I don't think I broke anything unless it actually uses a 64K pm25lv512 without a JEDEC id. I have a hunch that this board probably uses a flash chip which does support JEDEC.

diff --git a/target/linux/ar71xx/files/arch/mips/ath79/mach-rb91x.c b/target/linux/ar71xx/files/arch/mips/ath79/mach-rb91x.c
index 75382be..0fd03ff 100644
--- a/target/linux/ar71xx/files/arch/mips/ath79/mach-rb91x.c
+++ b/target/linux/ar71xx/files/arch/mips/ath79/mach-rb91x.c
@@ -41,14 +42,6 @@
#include "pci.h"
#include "routerboot.h"

-#define RB_ROUTERBOOT_OFFSET    0x0000
-#define RB_ROUTERBOOT_MIN_SIZE    0xb000
-#define RB_HARD_CFG_SIZE    0x1000
-#define RB_BIOS_OFFSET        0xd000
-#define RB_BIOS_SIZE        0x1000
-#define RB_SOFT_CFG_OFFSET    0xf000
-#define RB_SOFT_CFG_SIZE    0x1000
-
#define RB91X_FLAG_USB        BIT(0)
#define RB91X_FLAG_PCIE        BIT(1)

@@ -94,26 +87,37 @@ struct rb_board_info {
static struct mtd_partition rb711gr100_spi_partitions[] = {
    {
        .name        = "routerboot",
-        .offset        = RB_ROUTERBOOT_OFFSET,
+        .offset        = 0,
+        .size        = 0x0e000,
        .mask_flags    = MTD_WRITEABLE,
    }, {
        .name        = "hard_config",
-        .size        = RB_HARD_CFG_SIZE,
-        .mask_flags    = MTD_WRITEABLE,
-    }, {
-        .name        = "bios",
-        .offset        = RB_BIOS_OFFSET,
-        .size        = RB_BIOS_SIZE,
+                .offset        = 0x0e000,
+        .size        = 0x01000,
        .mask_flags    = MTD_WRITEABLE,
    }, {
        .name        = "soft_config",
-        .size        = RB_SOFT_CFG_SIZE,
-    }
+                .offset        = 0x0f000,
+        .size        = 0x01000,
+    }, {
+        .name        = "kernel",
+                .offset        = 0x020000,
+        .size        = 0x180000,
+        }, {
+        .name        = "rootfs",
+                .offset        = 0x1a0000,
+        .size        = 0x180000,
+        }, {
+        .name        = "ubifs",
+                .offset        = 0x320000,
+        .size        = MTDPART_SIZ_FULL
+        },
};

static struct flash_platform_data rb711gr100_spi_flash_data = {
+        .type        = "m25p05",
    .parts        = rb711gr100_spi_partitions,
-    .nr_parts    = ARRAY_SIZE(rb711gr100_spi_partitions),
+    .nr_parts    = 3,
};

static int rb711gr100_gpio_latch_gpios[AR934X_GPIO_COUNT] __initdata = {
@@ -223,12 +227,25 @@ static struct gpio_led rb711gr100_leds[] __initdata = {
    },
};

+static struct at803x_platform_data rb91x_at803x_data = {
+    .disable_smarteee = 1,
+    .enable_rgmii_rx_delay = 1,
+    .enable_rgmii_tx_delay = 1,
+};
+
+static struct mdio_board_info rb91x_mdio0_info[] = {
+    {
+        .bus_id = "ag71xx-mdio.0",
+        .phy_addr = 0,
+        .platform_data = &rb91x_at803x_data,
+    },
+};
+
static void __init rb711gr100_init_partitions(const struct rb_info *info)
{
    rb711gr100_spi_partitions[0].size = info->hard_cfg_offs;
    rb711gr100_spi_partitions[1].offset = info->hard_cfg_offs;
-
-    rb711gr100_spi_partitions[3].offset = info->soft_cfg_offs;
+    rb711gr100_spi_partitions[2].offset = info->soft_cfg_offs;
}

void __init rb711gr100_wlan_init(void)
@@ -293,10 +310,14 @@ static void __init rb711gr100_setup(void)
               ARRAY_SIZE(rb711gr100_spi_info));

    ath79_setup_ar934x_eth_cfg(AR934X_ETH_CFG_RGMII_GMAC0 |
+                   AR934X_ETH_CFG_RXD_DELAY |
                   AR934X_ETH_CFG_SW_ONLY_MODE);

    ath79_register_mdio(0, 0x0);

+    mdiobus_register_board_info(rb91x_mdio0_info,
+                    ARRAY_SIZE(rb91x_mdio0_info));
+
    ath79_init_mac(ath79_eth0_data.mac_addr, ath79_mac_base, 0);
    ath79_eth0_data.phy_if_mode = PHY_INTERFACE_MODE_RGMII;
    ath79_eth0_data.phy_mask = BIT(0);
@@ -328,3 +349,69 @@ static void __init rb711gr100_setup(void)
}

MIPS_MACHINE_NONAME(ATH79_MACH_RB_711GR100, "711Gr100", rb711gr100_setup);
+
+static struct gpio_led rbgroove_leds_gpio[] __initdata = {
+       {
+               .name           = "rbgroove:wlan:1",
+               .gpio           = 0,
+               .active_low     = 0,
+       }, {
+               .name           = "rbgroove:wlan:2",
+               .gpio           = 1,
+               .active_low     = 0,
+       }, {
+               .name           = "rbgroove:wlan:3",
+               .gpio           = 2,
+               .active_low     = 0,
+       }
+};
+
+static int nand_disabled;
+
+static int __init no_nand(char *str)
+{
+    nand_disabled = 1;
+    return 0;
+}
+
+early_param("no-nand", no_nand);
+
+static void __init rbgroove_setup(void)
+{
+       const struct rb_info *info;
+
+       info = rb_init_info((void *) KSEG1ADDR(0x1f000000), 0x20000);
+       if (!info)
+              return;
+
+       rb711gr100_init_partitions(info);
+       if (nand_disabled) {
+           printk(KERN_NOTICE "using SPI flash for root\n");
+           rb711gr100_spi_flash_data.nr_parts = ARRAY_SIZE(rb711gr100_spi_partitions);
+       }
+       ath79_register_spi(&rb711gr100_spi_data, rb711gr100_spi_info, 1);
+
+       ath79_setup_ar934x_eth_cfg(AR934X_ETH_CFG_MII_GMAC0);
+
+       ath79_register_mdio(0, 0x0);
+
+       ath79_init_mac(ath79_eth0_data.mac_addr, ath79_mac_base, 0);
+       ath79_eth0_data.phy_if_mode = PHY_INTERFACE_MODE_MII;
+       ath79_eth0_data.phy_mask = BIT(0);
+
+       ath79_register_eth(0);
+       rb711gr100_wlan_init();
+
+       if (!nand_disabled) {
+           printk(KERN_NOTICE "using NAND flash for root\n");
+           platform_device_register_data(NULL, "rb91x-nand", -1,
+                                         &rb711gr100_nand_data,
+                                         sizeof(rb711gr100_nand_data));
+       }
+
+       ath79_register_leds_gpio(-1, ARRAY_SIZE(rbgroove_leds_gpio),
+                                rbgroove_leds_gpio);
+}
+
+MIPS_MACHINE(ATH79_MACH_RB_GROOVE, "groove-52", "MikroTik 52HPn",
+       rbgroove_setup);

Perfect! I have it patched and compiled. What is the flashing procedure?

The partition table that I defined mach-rb91x.c creates relatively small 1.5MB partitions for the kernel and rootfs file systems, so you need to make sure your kernel and squashfs root partition are smaller than that.  This leaves a 12MB partition which I use for a writable ubifs file system.  If your partition requirements are different, you'll need to change the partition table in mach-rb91x.c.

First, you must build the kernel2minor tool from the the following location: https://github.com/adron-s/kernel2minor.git. Then you can get the Groove running using the following steps.

1) Create the a kernel.yaffs NOR flash image with a command like the following:

   kernel2minor -e -k openwrt-ar71xx-mikrotik-vmlinux-lzma.elf -r kernel.yaffs -s 1024

2) Setup a bootp/tftp server to boot the ramdisk image from openwrt-ar71xx-mikrotik-vmlinux-initramfs-lzma.elf.

3) Hold down the Groove reset button and power-up to boot your kernel via tftp.

4) Copy the kernel from kernel.yaffs and the root file system from openwrt-ar71xx-mikrotik-root.squashfs-64k to /tmp on the Groove radio.

5) Flash the kernel with "mtd erase kernel && mtd write kernel.yaffs kernel"

6) Flash the root file system with "mtd erase rootfs && mtd write openwrt-ar71xx-mikrotik-root.squashfs-64k"

7) You can do whatever you wish with the ubifs partition as an overlay.  In my case, I create a UBI file system with the following commands (executed on the target).

    ubiformat /dev/mtd5 -q -y -s 1
    ubiattach /dev/ubi_ctrl -m 5
    blocks=$(ubinfo /dev/ubi0 | grep 'available logical' | sed 's/.*: \([0-9]*\) (.*/\1/')
    ubimkvol /dev/ubi0 -N root -S $blocks

I've designed my own installers for performing these operations on the target, since the building of static flash images like OpenWRT does is somewhat difficult to support.  However, you need the ubi-utils package in your root squashfs image to be able to format the UBI partition on the target.

If you prefer, you can use JFFS2 for the /overlay file system.

Seems simple enough. I really appreciate it.

I am familiar with the Mikrotik procedures, specifically booting with the ramdisk image, but I seem to be having an issue booting from the ramdisk image. After the image is sent to the Groove, nothing happens for a couple of minutes and then the Groove reboots into RouterOS.

Note that after you boot, the device will have a static IP of 192.168.1.1 and ssh enabled.

The root password on the device is "guard4640".

I must be doing something wrong.

$ ifconfig eno1 192.168.88.10 up
$ dnsmasq -i eno1 --dhcp-range=192.168.88.100,192.168.88.200 \
--dhcp-boot=openwrt-ar71xx-mikrotik-vmlinux-initramfs-lzma.elf \
--enable-tftp --tftp-root=/home/admin/mikrotik/groove -d -u admin -p0 -K --log-dhcp --bootp-dynamic

The image is sent to the Groove...

dnsmasq: started, version 2.76 DNS disabled
dnsmasq: compile time options: IPv6 GNU-getopt DBus i18n IDN DHCP DHCPv6 no-Lua TFTP conntrack ipset auth DNSSEC loop-detect inotify
dnsmasq-dhcp: DHCP, IP range 192.168.88.100 -- 192.168.88.200, lease time 1h
dnsmasq-tftp: TFTP root is /home/admin/mikrotik/groove/ 
dnsmasq-dhcp: 2558249946 available DHCP range: 192.168.88.100 -- 192.168.88.200
dnsmasq-dhcp: 2558249946 vendor class: Mips_boot
dnsmasq-dhcp: 2558249946 tags: bootp, eno1
dnsmasq-dhcp: 2558249946 BOOTP(eno1) 192.168.88.118 6c:3b:6b:3c:8a:a5 
dnsmasq-dhcp: 2558249946 bootfile name: openwrt-ar71xx-mikrotik-vmlinux-initramfs-lzma.elf

Then nothing seems to happen on the device after the beep. No DHCP acquisition, setting a static yielded no response on 192.168.1.1. I did notice it did not reboot into RouterOS though - perhaps a good sign.

(Last edited by robo108 on 8 Jun 2017, 14:51)

I wasn't expecting any luck, but I have the same luck with the Groove52ac. Any thoughts, @jpolsonaz?

(Last edited by robo108 on 20 Jun 2017, 23:41)

The discussion might have continued from here.