ramips: add support for Fon FON2601
authorNOGUCHI Hiroshi <drvlabo@gmail.com>
Thu, 25 Jul 2019 23:11:48 +0000 (08:11 +0900)
committerPetr Štetiar <ynezz@true.cz>
Fri, 26 Jul 2019 06:09:16 +0000 (08:09 +0200)
FON2601 is a wireless router.

Specification:
- SoC: Mediatek MT7620A (580MHz)
- RAM: 128 MiB
- ROM: 16 MiB SPI Flash
- Wireless:
   for 11b/g/n (upto 300 Mbps):  MT7620A built-in WMAC
   for 11a/n/ac (upto 867 Mbps): MT7662E
- Ethernet LAN: 1 port, upto 100 Mbps
- Ethernet WAN: 1 port, upto 1000 Mbps
- USB: 1 port (USB 2.0 host)
- LEDs: 4 (all can be controlled by SoC's GPIO)
- buttons: 1 (Displayed as "WPS" on enclosure)
- serial port: 57600n8
 pins: Vcc(3.3V), Rx, Tx, GND
(left to right, viewed from outside of board)

Installation (only available via UART):
  1. download sysupgrade binary image by wget command
  2. write sysupgrade binary image to Flash
     command is:
       mtd write sysupgrade.bin firmware
  3. reboot

Important Notice:
  Only one button is displayed as "WPS" on enclosure.
  However, it is configured as "reset" (factory resetting feature).

Signed-off-by: NOGUCHI Hiroshi <drvlabo@gmail.com>
[removed unrelated openwrt-keyring revert, missing -Wall for uimage_padhdr]
Signed-off-by: Petr Štetiar <ynezz@true.cz>
target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_uimage.c
target/linux/ramips/base-files/etc/board.d/02_network
target/linux/ramips/dts/mt7620a_fon_fon2601.dts [new file with mode: 0644]
target/linux/ramips/image/Makefile
target/linux/ramips/image/mt7620.mk
tools/firmware-utils/Makefile
tools/firmware-utils/src/uimage_padhdr.c [new file with mode: 0644]

index aba1c83ad65cdf6a56c1683366454367c16f3fa0..091403ae9186baa1827e942bf3948783aba04a4f 100644 (file)
@@ -24,9 +24,9 @@
 
 /*
  * uimage_header itself is only 64B, but it may be prepended with another data.
- * Currently the biggest size is for Edimax devices: 20B + 64B
+ * Currently the biggest size is for Fon(Foxconn) devices: 64B + 32B
  */
-#define MAX_HEADER_LEN         84
+#define MAX_HEADER_LEN         96
 
 #define IH_MAGIC       0x27051956      /* Image Magic Number           */
 #define IH_NMLEN               32      /* Image Name Length            */
@@ -80,12 +80,12 @@ read_uimage_header(struct mtd_info *mtd, size_t offset, u_char *buf,
  * __mtdsplit_parse_uimage - scan partition and create kernel + rootfs parts
  *
  * @find_header: function to call for a block of data that will return offset
- *      of a valid uImage header if found
+ *      and tail padding length of a valid uImage header if found
  */
 static int __mtdsplit_parse_uimage(struct mtd_info *master,
-                                  const struct mtd_partition **pparts,
-                                  struct mtd_part_parser_data *data,
-                                  ssize_t (*find_header)(u_char *buf, size_t len))
+                  const struct mtd_partition **pparts,
+                  struct mtd_part_parser_data *data,
+                  ssize_t (*find_header)(u_char *buf, size_t len, int *extralen))
 {
        struct mtd_partition *parts;
        u_char *buf;
@@ -97,6 +97,7 @@ static int __mtdsplit_parse_uimage(struct mtd_info *master,
        size_t rootfs_size = 0;
        int uimage_part, rf_part;
        int ret;
+       int extralen;
        enum mtdsplit_part_type type;
 
        nr_parts = 2;
@@ -120,7 +121,8 @@ static int __mtdsplit_parse_uimage(struct mtd_info *master,
                if (ret)
                        continue;
 
-               ret = find_header(buf, MAX_HEADER_LEN);
+               extralen = 0;
+               ret = find_header(buf, MAX_HEADER_LEN, &extralen);
                if (ret < 0) {
                        pr_debug("no valid uImage found in \"%s\" at offset %llx\n",
                                 master->name, (unsigned long long) offset);
@@ -128,7 +130,9 @@ static int __mtdsplit_parse_uimage(struct mtd_info *master,
                }
                header = (struct uimage_header *)(buf + ret);
 
-               uimage_size = sizeof(*header) + be32_to_cpu(header->ih_size) + ret;
+               uimage_size = sizeof(*header) +
+                               be32_to_cpu(header->ih_size) + ret + extralen;
+
                if ((offset + uimage_size) > master->size) {
                        pr_debug("uImage exceeds MTD device \"%s\"\n",
                                 master->name);
@@ -206,7 +210,7 @@ err_free_parts:
        return ret;
 }
 
-static ssize_t uimage_verify_default(u_char *buf, size_t len)
+static ssize_t uimage_verify_default(u_char *buf, size_t len, int *extralen)
 {
        struct uimage_header *header = (struct uimage_header *)buf;
 
@@ -269,7 +273,7 @@ static struct mtd_part_parser uimage_generic_parser = {
 #define FW_MAGIC_WNDR3700V2    0x33373031
 #define FW_MAGIC_WPN824N       0x31313030
 
-static ssize_t uimage_verify_wndr3700(u_char *buf, size_t len)
+static ssize_t uimage_verify_wndr3700(u_char *buf, size_t len, int *extralen)
 {
        struct uimage_header *header = (struct uimage_header *)buf;
        uint8_t expected_type = IH_TYPE_FILESYSTEM;
@@ -332,7 +336,7 @@ static struct mtd_part_parser uimage_netgear_parser = {
 #define FW_EDIMAX_OFFSET       20
 #define FW_MAGIC_EDIMAX                0x43535953
 
-static ssize_t uimage_find_edimax(u_char *buf, size_t len)
+static ssize_t uimage_find_edimax(u_char *buf, size_t len, int *extralen)
 {
        u32 *magic;
 
@@ -345,7 +349,7 @@ static ssize_t uimage_find_edimax(u_char *buf, size_t len)
        if (be32_to_cpu(*magic) != FW_MAGIC_EDIMAX)
                return -EINVAL;
 
-       if (!uimage_verify_default(buf + FW_EDIMAX_OFFSET, len))
+       if (!uimage_verify_default(buf + FW_EDIMAX_OFFSET, len, extralen))
                return FW_EDIMAX_OFFSET;
 
        return -EINVAL;
@@ -377,6 +381,49 @@ static struct mtd_part_parser uimage_edimax_parser = {
        .type = MTD_PARSER_TYPE_FIRMWARE,
 };
 
+
+/**************************************************
+ * Fon(Foxconn)
+ **************************************************/
+
+#define FONFXC_PAD_LEN         32
+
+static ssize_t uimage_find_fonfxc(u_char *buf, size_t len, int *extralen)
+{
+       if (uimage_verify_default(buf, len, extralen) < 0)
+               return -EINVAL;
+
+       *extralen = FONFXC_PAD_LEN;
+
+       return 0;
+}
+
+static int
+mtdsplit_uimage_parse_fonfxc(struct mtd_info *master,
+                             const struct mtd_partition **pparts,
+                             struct mtd_part_parser_data *data)
+{
+       return __mtdsplit_parse_uimage(master, pparts, data,
+                                      uimage_find_fonfxc);
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0)
+static const struct of_device_id mtdsplit_uimage_fonfxc_of_match_table[] = {
+       { .compatible = "fonfxc,uimage" },
+       {},
+};
+#endif
+
+static struct mtd_part_parser uimage_fonfxc_parser = {
+       .owner = THIS_MODULE,
+       .name = "fonfxc-fw",
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0)
+       .of_match_table = mtdsplit_uimage_fonfxc_of_match_table,
+#endif
+       .parse_fn = mtdsplit_uimage_parse_fonfxc,
+       .type = MTD_PARSER_TYPE_FIRMWARE,
+};
+
 /**************************************************
  * Init
  **************************************************/
@@ -386,6 +433,7 @@ static int __init mtdsplit_uimage_init(void)
        register_mtd_parser(&uimage_generic_parser);
        register_mtd_parser(&uimage_netgear_parser);
        register_mtd_parser(&uimage_edimax_parser);
+       register_mtd_parser(&uimage_fonfxc_parser);
 
        return 0;
 }
index 95bd2a44f141ce19fc90eb1641a142aed31f1fc6..2f9a02256e31ee87cfad7c916db4aa16341c2698 100755 (executable)
@@ -329,6 +329,10 @@ ramips_setup_interfaces()
                ucidef_add_switch "switch1" \
                        "1:lan" "2:lan" "3:lan" "4:lan" "0:wan" "6@eth0"
                ;;
+       fon,fon2601)
+               ucidef_add_switch "switch0" \
+                       "0:lan" "4:wan" "6@eth0"
+               ;;
        gehua,ghl-r-001)
                ucidef_add_switch "switch0" \
                        "0:lan" "1:lan" "2:lan" "4:wan" "6@eth0"
diff --git a/target/linux/ramips/dts/mt7620a_fon_fon2601.dts b/target/linux/ramips/dts/mt7620a_fon_fon2601.dts
new file mode 100644 (file)
index 0000000..33329a1
--- /dev/null
@@ -0,0 +1,166 @@
+// SPDX-License-Identifier: GPL-2.0
+/dts-v1/;
+
+#include "mt7620a.dtsi"
+
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/input/input.h>
+
+/ {
+       compatible = "fon,fon2601", "ralink,mt7620a-soc";
+       model = "Fon FON2601";
+
+       aliases {
+               led-boot = &led_power;
+               led-failsafe = &led_power;
+               led-running = &led_power;
+               led-upgrade = &led_power;
+       };
+
+       leds {
+               compatible = "gpio-leds";
+
+               led_power: power_r {
+                       label = "fon2601:red:power";
+                       gpios = <&gpio0 9 GPIO_ACTIVE_LOW>;
+               };
+
+               internet_g {
+                       label = "fon2601:green:internet";
+                       gpios = <&gpio0 10 GPIO_ACTIVE_LOW>;
+               };
+
+               net_g {
+                       label = "fon2601:green:net";
+                       gpios = <&gpio0 12 GPIO_ACTIVE_LOW>;
+               };
+
+               wifi_g {
+                       label = "fon2601:green:wifi";
+                       gpios = <&gpio0 14 GPIO_ACTIVE_LOW>;
+               };
+       };
+
+       keys {
+               compatible = "gpio-keys";
+
+               reset {
+                       label = "reset";
+                       gpios = <&gpio0 2 GPIO_ACTIVE_LOW>;
+                       linux,code = <KEY_RESTART>;
+               };
+       };
+};
+
+&spi0 {
+       status = "okay";
+
+       flash@0 {
+               compatible = "jedec,spi-nor";
+               reg = <0>;
+               spi-max-frequency = <10000000>;
+
+               partitions {
+                       compatible = "fixed-partitions";
+                       #address-cells = <1>;
+                       #size-cells = <1>;
+
+                       partition@0 {
+                               label = "u-boot";
+                               reg = <0x0 0x30000>;
+                               read-only;
+                       };
+
+                       partition@30000 {
+                               label = "u-boot-env";
+                               reg = <0x30000 0x10000>;
+                               read-only;
+                       };
+
+                       factory: partition@40000 {
+                               label = "factory";
+                               reg = <0x40000 0x10000>;
+                               read-only;
+                       };
+
+                       partition@50000 {
+                               compatible = "fonfxc,uimage";
+                               label = "firmware";
+                               reg = <0x50000 0xf90000>;
+                       };
+
+                       partition@fe0000 {
+                               label = "board_data";
+                               reg = <0xfe0000 0x20000>;
+                               read-only;
+                       };
+               };
+       };
+};
+
+&state_default {
+       gpio {
+               ralink,group = "i2c", "uartf";
+               ralink,function = "gpio";
+       };
+       nd_sd {
+               ralink,group = "nd_sd";
+               ralink,function = "sd";
+       };
+       spi_cs {
+               ralink,group = "spi refclk";
+               ralink,function = "spi refclk";
+       };
+};
+
+&ethernet {
+       pinctrl-names = "default";
+       pinctrl-0 = <&rgmii2_pins &mdio_pins>;
+
+       mtd-mac-address = <&factory 0x4>;
+
+       port@4 {
+               status = "okay";
+               phy-handle = <&phy4>;
+               phy-mode = "rgmii";
+       };
+
+       mdio-bus {
+               status = "okay";
+
+               phy4: ethernet-phy@4 {
+                       reg = <4>;
+                       phy-mode = "rgmii";
+               };
+       };
+};
+
+&gsw {
+       mediatek,port4 = "gmac";
+};
+
+&wmac {
+       ralink,mtd-eeprom = <&factory 0>;
+       pinctrl-names = "default";
+       pinctrl-0 = <&pa_pins>, <&wled_pins>;
+};
+
+&pcie {
+       status = "okay";
+};
+&pcie0 {
+       wifi@0,0 {
+               compatible = "pci14c3,7662";
+               reg = <0x0000 0 0 0 0>;
+               mediatek,mtd-eeprom = <&factory 0x8000>;
+               ieee80211-freq-limit = <5000000 6000000>;
+       };
+};
+
+&ehci {
+       status = "okay";
+};
+
+&ohci {
+       status = "okay";
+};
index 36f95b6d1cea8d46c3753dbeebcaf0622ca36bdb..816146ece55e2ec8dc9e12dc8c999aa31c416a63 100644 (file)
@@ -147,6 +147,11 @@ define Build/sercom-footer
        $(call Build/sercom-seal,-f)
 endef
 
+define Build/fonfxcimage
+       uimage_padhdr -i $@ -o $@.new
+       mv $@.new $@
+endef
+
 ifeq ($(SUBTARGET),rt288x)
 include rt288x.mk
 endif
index d7b06076da6bedcb0844e020420aeb71b2d218f2..455f83616850fefaa279324178a4c44f708ef4c1 100644 (file)
@@ -372,6 +372,19 @@ define Device/elecom_wrh-300cr
 endef
 TARGET_DEVICES += elecom_wrh-300cr
 
+define Device/fon_fon2601
+  MTK_SOC := mt7620a
+  IMAGE_SIZE := 15936k
+  DEVICE_VENDOR := Fon
+  DEVICE_MODEL := FON2601
+  DEVICE_PACKAGES := kmod-mt76x2 kmod-usb2 kmod-usb-ohci
+  KERNEL_INITRAMFS := $$(KERNEL) | fonfxcimage
+  IMAGE/sysupgrade.bin := append-kernel | append-rootfs |\
+                       fonfxcimage |\
+                       pad-rootfs | append-metadata | check-size $$$$(IMAGE_SIZE)
+endef
+TARGET_DEVICES += fon_fon2601
+
 define Device/glinet_gl-mt300a
   MTK_SOC := mt7620a
   IMAGE_SIZE := 15872k
index d318788ac542801f9cbd671845b2c6aa4f2e299f..2d2c96ce0afa2df4f1f5a772e63b155d7018be1a 100644 (file)
@@ -92,6 +92,7 @@ define Host/Compile
        $(call cc,dns313-header, -Wall)
        $(call cc,mksercommfw, -Wall)
        $(call cc,nec-enc, -Wall --std=gnu99)
+       $(call cc,uimage_padhdr, -Wall -lz)
 endef
 
 define Host/Install
diff --git a/tools/firmware-utils/src/uimage_padhdr.c b/tools/firmware-utils/src/uimage_padhdr.c
new file mode 100644 (file)
index 0000000..b5fb97d
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * uimage_padhdr.c : add zero paddings after the tail of uimage header
+ *
+ * Copyright (C) 2019 NOGUCHI Hiroshi <drvlabo@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License,
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <arpa/inet.h>
+#include <zlib.h>
+
+
+/* from u-boot/include/image.h */
+#define IH_MAGIC       0x27051956      /* Image Magic Number           */
+#define IH_NMLEN               32      /* Image Name Length            */
+
+/*
+ * Legacy format image header,
+ * all data in network byte order (aka natural aka bigendian).
+ */
+typedef struct image_header {
+       uint32_t        ih_magic;       /* Image Header Magic Number    */
+       uint32_t        ih_hcrc;        /* Image Header CRC Checksum    */
+       uint32_t        ih_time;        /* Image Creation Timestamp     */
+       uint32_t        ih_size;        /* Image Data Size              */
+       uint32_t        ih_load;        /* Data  Load  Address          */
+       uint32_t        ih_ep;          /* Entry Point Address          */
+       uint32_t        ih_dcrc;        /* Image Data CRC Checksum      */
+       uint8_t         ih_os;          /* Operating System             */
+       uint8_t         ih_arch;        /* CPU architecture             */
+       uint8_t         ih_type;        /* Image Type                   */
+       uint8_t         ih_comp;        /* Compression Type             */
+       uint8_t         ih_name[IH_NMLEN];      /* Image Name           */
+} image_header_t;
+
+
+/* default padding size */
+#define        IH_PAD_BYTES            (32)
+
+
+static void usage(char *prog)
+{
+       fprintf(stderr,
+               "%s -i <input_uimage_file> -o <output_file> [-l <padding bytes>]\n",
+               prog);
+}
+
+int main(int argc, char *argv[])
+{
+       struct stat statbuf;
+       u_int8_t *filebuf;
+       int ifd;
+       int ofd;
+       ssize_t rsz;
+       u_int32_t crc_recalc;
+       image_header_t *imgh;
+       int opt;
+       char *infname = NULL;
+       char *outfname = NULL;
+       int padsz = IH_PAD_BYTES;
+       int ltmp;
+
+       while ((opt = getopt(argc, argv, "i:o:l:")) != -1) {
+               switch (opt) {
+               case 'i':
+                       infname = optarg;
+                       break;
+               case 'o':
+                       outfname = optarg;
+                       break;
+               case 'l':
+                       ltmp = strtol(optarg, NULL, 0);
+                       if (ltmp > 0)
+                               padsz = ltmp;
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       if (!infname || !outfname) {
+               usage(argv[0]);
+               exit(1);
+       }
+
+       if (stat(infname, &statbuf) < 0) {
+               fprintf(stderr,
+                       "could not find input file. (errno = %d)\n", errno);
+               exit(1);
+       }
+
+       filebuf = malloc(statbuf.st_size + padsz);
+       if (!filebuf) {
+               fprintf(stderr, "buffer allocation failed\n");
+               exit(1);
+       }
+
+       ifd = open(infname, O_RDONLY);
+       if (ifd < 0) {
+               fprintf(stderr,
+                       "could not open input file. (errno = %d)\n", errno);
+               exit(1);
+       }
+
+       ofd = open(outfname, O_WRONLY | O_CREAT, 0644);
+       if (ofd < 0) {
+               fprintf(stderr,
+                       "could not open output file. (errno = %d)\n", errno);
+               exit(1);
+       }
+
+       rsz = read(ifd, filebuf, sizeof(*imgh));
+       if (rsz != sizeof(*imgh)) {
+               fprintf(stderr,
+                       "could not read input file (errno = %d).\n", errno);
+               exit(1);
+       }
+
+       memset(&(filebuf[sizeof(*imgh)]), 0, padsz);
+
+       rsz = read(ifd, &(filebuf[sizeof(*imgh) + padsz]),
+                               statbuf.st_size - sizeof(*imgh));
+       if (rsz != (int32_t)(statbuf.st_size - sizeof(*imgh))) {
+               fprintf(stderr,
+                       "could not read input file (errno = %d).\n", errno);
+               exit(1);
+       }
+
+       imgh = (image_header_t *)filebuf;
+
+       imgh->ih_hcrc = 0;
+       crc_recalc = crc32(0, filebuf, sizeof(*imgh) + padsz);
+       imgh->ih_hcrc = htonl(crc_recalc);
+
+       rsz = write(ofd, filebuf, statbuf.st_size + padsz);
+       if (rsz != (int32_t)statbuf.st_size + padsz) {
+               fprintf(stderr,
+                       "could not write output file (errnor = %d).\n", errno);
+               exit(1);
+       }
+
+       return 0;
+}