Hey all,
Recently, I got my hands on a "security gateway" router from Draytek. It is the Vigor2862. (note that the name is without any suffixes; this is the model without wireless)
Over the last few weeks, I have been trying to port OpenWrt. I came quite far, but now I've really hit a wall.
The hardware
The router is based on a lantiq xRX330 series SoC. Specs are
- mips34Kc cpu (2 cores 1 thread or 1 core 2 threads; not sure...)
- 256 MB ram
- 128 MB nand
- 256 KB spi nor flash
- 5 port gigabit ethernet switch
- 2 usb 2.0 ports
- dsl (seems to be supported with closed firmware blobs, like the other lantiq soc's, but I have not looked into this yet.)
The firmware
The stock os is a proprietary RTOS from Draytek called "DrayOS". It is not linux based, and there are no GPL sources for this board.
That may make it seem hard to get this router working, but I think it is doable.
The bootloader is a barely modified u-boot fork called "Drayboot" and can be interrupted by pressing "u" while the router is booting. (a populated uart header is present)
Some commands are disabled, but tftpboot is still working fine!
Mainline support
For those who know the lantiq target a bit, there are some older soc families and then there are ar9 (xrx100 series) and vr9 which corresponds to the xrx200 line of SoC's, which is really well supported.
After these, there seem to be ar10 (xrx300) and grx390 (xrx330). Both targets have had patches make it into mainline linux. The same goes for most of the other hardware on these boards.
While this SoC is not yet supported by openwrt, there have been some efforts in the past on ar10 and grx390:
Where are we now
Based on the device trees in these pull requests, the existing targets, some gpl dumps for other lantiq devices and a dtb extracted from the Draytek firmware, I have managed to put together a device tree that makes it boot openwrt.
What works?
- core stuff to get it to boot (rcu, memory, cpu, etc.)
- nand
- spi
- gpio/pinctrl
- buttons
- leds (can manually drive them, tough haven't configured it in the dts yet; not a priority now)
- usb (including vbus power control via gpio)
What does not work
- the switch
Not looked at
- pci/pcie; my board has no actual pcie slots, so would be hard for me to test on this device
- dsl; don't have dsl at home, so again I cannot test this
The problem
The first problem was firmware. The switch has 4 GPHY's which run their own proprietary binary blobs. The upstream lantiq dsa driver already handles the loading of these blobs, I just had to add them to the tree (redistributability aside, that comes later, we can always extract them from u-boot and load them from user space or something).
The device tree segment with these gphy's was also present in the pull requests (I only found out after digging through all of the gpl dumps and reverse engineering most of it myself...).
After I added these with the proper reset lines, the leds on the switch would now turn on or off when a cable was plugged in. I think this means that the firmware is loaded properly and the gphys function.
The problem is that the ethernet phys driven by the gphys do not show up on the mdio bus and the dsa driver fails to load as it cannot find the phys.
I also don't really know what port numbers I should use, as this indicates "where" the phys are located on the mdio bus (for now I just copied the dts from dwr966). I doubt if this is the problem though, as the phys themselves don't show up on the mdio bus at all...
Any help would be greatly appreciated
grx390.dtsi
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
compatible = "lantiq,xway", "lantiq,grx390";
aliases {
serial0 = &asc1;
};
chosen {
stdout-path = "serial0:115200n8";
};
// two cores show up regardless of how many we configure here
cpus {
#address-cells = <1>;
#size-cells = <0>;
cpu@0 {
compatible = "mips,mips34Kc";
reg = <0>;
};
cpu@1 {
compatible = "mips,mips34Kc";
reg = <1>;
};
};
biu@1f800000 {
#address-cells = <1>;
#size-cells = <1>;
compatible = "lantiq,biu", "simple-bus";
reg = <0x1f800000 0x800000>;
ranges = <0x00 0x1f800000 0x7fffff>;
icu0: icu0@80200 {
/*
* merged the two seperate icu nodes into
* one and merged all the smaller register maps into
* two big ones, 1 for each icu. It is the same way on vr9.
*/
#interrupt-cells = <1>;
interrupt-controller;
compatible = "lantiq,icu";
reg = <0x80200 0xc8 /* icu0 */
0x80300 0xc8>; /* icu1 */
};
watchdog@803f0 {
compatible = "lantiq,xrx100-wdt", "lantiq,wdt";
reg = <0x803f0 0x10>;
regmap = <&rcu0>;
};
};
sram: sram@1f000000 {
#address-cells = <1>;
#size-cells = <1>;
compatible = "lantiq,sram", "simple-bus";
reg = <0x1f000000 0x800000>;
ranges = <0x00 0x1f000000 0x7fffff>;
// these three are core platform devices which the kernel checks for
// if not present, the kernel panics
eiu@101000 {
#interrupt-cells = <1>;
interrupt-controller;
compatible = "lantiq,eiu-xway";
reg = <0x101000 0x1000>;
interrupt-parent = <&icu0>;
lantiq,eiu-irqs = <166 135 66 40 41 42>;
};
pmu@102000 {
compatible = "lantiq,pmu-xway";
reg = <0x102000 0x1000>;
};
cgu@103000 {
compatible = "lantiq,cgu-xway";
reg = <0x103000 0x1000>;
// might have to add lantiq,phy_clk_src later for gphy
};
dcdc@106a00 { // good
compatible = "lantiq,dcdc-xrx200";
reg = <0x106a00 0x200>;
};
/* there might be a way to initialize this rcu via the fpi
* bus driver; lantiq,rcu property does some initialization
*
* also note that the lantiq,xrx200-rcu string has no kernel
* driver looking for it, effectively this uses the simple-mfd
* or syscon driver.
*/
rcu0: rcu@203000 {
compatible = "lantiq,xrx200-rcu", "simple-mfd", "syscon";
reg = <0x203000 0x1000>;
ranges = <0x0 0x203000 0x100>;
big-endian;
reset0: reset-controller@10 {
compatible = "lantiq,xrx200-reset";
reg = <0x10 4>, <0x14 4>;
reg-names = "reset", "status";
#reset-cells = <2>;
};
reset1: reset-controller@48 {
compatible = "lantiq,xrx200-reset";
reg = <0x48 4>, <0x24 4>;
reg-names = "reset", "status";
#reset-cells = <2>;
};
// xrx300 and xrx200 do the same
usb_phy0: usb2-phy@18 {
compatible = "lantiq,xrx200-usb2-phy";
reg = <0x18 4>, <0x38 4>;
status = "disabled";
resets = <&reset1 4 4>, <&reset0 4 4>;
reset-names = "phy", "ctrl";
#phy-cells = <0>;
};
usb_phy1: usb2-phy@34 {
compatible = "lantiq,xrx200-usb2-phy";
reg = <0x34 4>, <0x3c 4>;
status = "disabled";
resets = <&reset1 5 4>, <&reset0 4 4>;
reset-names = "phy", "ctrl";
#phy-cells = <0>;
};
reboot@10 {
compatible = "syscon-reboot";
offset = <0x10>;
mask = <0xe0000000>;
};
};
};
/*
* may need work? only vr9 uses lantiq,xrx200-fpi, the
* others use lantiq,fpi
*/
fpi: fpi@10000000 {
#address-cells = <1>;
#size-cells = <1>;
compatible = "lantiq,fpi", "simple-bus";
ranges = <0x00 0x10000000 0xeefffff>;
reg = <0x10000000 0xef00000>;
localbus: localbus@0 {
compatible = "lantiq,localbus", "simple-bus";
#address-cells = <2>;
#size-cells = <1>;
/*
* copied this from ar9/vr9/xrx330/everything else
* that seems to have the same.
*/
ranges = <0 0 0x00 0x3ffffff /* addrsel0 ??? */
1 0 0x4000000 0x4000010>; /* addrsel1 ??? */
};
gptu@e100a00 { // done
compatible = "lantiq,gptu-xway";
reg = <0xe100a00 0x100>;
interrupt-parent = <&icu0>;
interrupts = <126 127 128 129 130 131>;
};
spi: spi@e100800 {
compatible = "lantiq,xrx200-spi", "lantiq,xrx100-spi";
reg = <0xe100800 0x100>;
interrupt-parent = <&icu0>;
interrupts = <22 23 24>;
interrupt-names = "spi_rx", "spi_tx", "spi_err";
#address-cells = <1>;
#size-cells = <0>;
// pinctrl-0 = <&spi_pins>;
// pinctrl-names = "default";
status = "disabled";
};
gpio: pinmux@e100b10 {
compatible = "lantiq,xrx200-pinctrl";
#gpio-cells = <2>;
gpio-controller;
reg = <0xe100b10 0xa0>;
interrupt-parent = <&icu0>;
exin_pins: exin {
mux {
lantiq,groups = "exin3";
lantiq,function = "exin";
lantiq,pull = <2>;
};
};
mdio_pins: mdio {
mux {
lantiq,groups = "mdio";
lantiq,function = "mdio";
};
};
nand_pins: nand {
output_pins { // gpio's 24 13 49
lantiq,groups = "nand cle", "nand ale", "nand rd";
lantiq,function = "ebu";
lantiq,open-drain = <0>;
lantiq,pull = <0>;
lantiq,output = <1>;
};
input_pins {
lantiq,groups = "nand rdy"; // gpio48
lantiq,function = "ebu";
lantiq,pull = <2>;
};
};
nand_cs1_pins: nand-cs1 {
mux {
lantiq,groups = "nand cs1"; // gpio 23
lantiq,function = "ebu";
lantiq,open-drain = <0>;
lantiq,pull = <0>;
lantiq,output = <1>;
};
};
spi_pins: spi {
input_pins {
lantiq,groups = "spi_di"; // gpio 16
lantiq,function = "spi";
lantiq,pull = <2>;
};
output_pins { // gpio 17 18 15 13
lantiq,pins = "spi_do", "spi_clk", "spi_cs1"; //, "spi_cs3";
lantiq,function = "spi";
lantiq,open-drain = <0>;
lantiq,pull = <0>;
lantiq,output = <1>;
};
};
stp_pins: stp {
mux { // gpio 4 5 6
lantiq,groups = "stp";
lantiq,function = "stp";
lantiq,open-drain = <0>;
lantiq,pull = <0>;
lantiq,output = <1>;
};
};
};
stp: stp@e100bb0 {
compatible = "lantiq,gpio-stp-xway";
reg = <0xe100bb0 0x40>;
#gpio-cells = <2>;
gpio-controller;
pinctrl-0 = <&stp_pins>;
pinctrl-names = "default";
// look into dsl and values for lantiq,phyX
lantiq,shadow = <0xffff>;
lantiq,groups = <0x07>;
lantiq,phy1 = <0x03>;
lantiq,phy2 = <0x03>;
lantiq,phy3 = <0x03>;
lantiq,phy4 = <0x03>;
};
asc1: serial@e100c00 { // done
compatible = "lantiq,asc";
reg = <0xe100c00 0x400>;
interrupt-parent = <&icu0>;
interrupts = <112 113 114>;
};
dma@e104100 { // done
compatible = "lantiq,dma-xway";
reg = <0xe104100 0x800>;
};
// External Bus Unit; related to nand; not sure if confured right
ebu0: ebu@6000000 {
compatible = "lantiq,ebu-xway";
reg = <0x6000000 0x100 0x6000100 0x100>; // why two, not seen before
reg-names = "ebunand_reg", "hsnand_reg";
};
usb0: usb@e101000 {
#address-cells = <1>;
#size-cells = <0>;
status = "disabled";
compatible = "lantiq,xrx300-usb";
reg = <0xe101000 0x1000
0xe120000 0x3f000>;
interrupt-parent = <&icu0>;
interrupts = <62 91>;
dr_mode = "host";
phys = <&usb_phy0>;
phy-names = "usb2-phy";
// #phy-cells = ?
// do we need this port node?
ehci_port1: port@1 {
reg = <1>;
#trigger-source-cells = <0>;
};
};
usb1: usb@e106000 {
#address-cells = <1>;
#size-cells = <0>;
status = "disabled";
compatible = "lantiq,xrx300-usb";
reg = <0xe106000 0x1000
0xe1e0000 0x3f000>; // is the second register needed on usb1? also has an interrupt less.
interrupt-parent = <&icu0>;
interrupts = <91>;
dr_mode = "host";
phys = <&usb_phy1>;
phy-names = "usb2-phy";
ehci_port2: port@1 {
reg = <1>;
#trigger-source-cells = <0>;
};
};
gswip: switch@e108000 {
compatible = "lantiq,xrx330-gswip";
#address-cells = <1>;
#size-cells = <0>;
reg = <0xe108000 0x3000>, /* switch */
<0xe10b100 0x70>, /* mdio */
<0xe10b1d8 0x30>; /* mii */
reg-names = "switch", "mdio", "mii";
dsa,member = <0 0>;
gswip_ports: ports {
#address-cells = <1>;
#size-cells = <0>;
port@6 {
reg = <0x6>;
label = "cpu";
phy-mode = "internal";
ethernet = <ð0>;
// is this needed for the cpu port?
fixed-link {
speed = <1000>;
full-duplex;
};
};
};
gswip_mdio: mdio {
#address-cells = <1>;
#size-cells = <0>;
compatible = "latiq,xrx200-mdio";
};
gphy-fw {
compatible = "lantiq,xrx330-gphy-fw", "lantiq,gphy-fw";
lantiq,rcu = <&rcu0>;
#address-cells = <1>;
#size-cells = <0>;
/*
* found out the registers and reset specs
* from gpl dump of telekom router
* uboot was very readable and switch_api also helped.
*/
gphy0: gphy@20 {
reg = <0x20>;
resets = <&reset0 31 30>, <&reset1 6 6>;
reset-names = "gphy", "gphy2";
};
gphy1: gphy@58 {
reg = <0x58>;
resets = <&reset0 29 28>, <&reset1 7 7>;
reset-names = "gphy", "gphy2";
};
gphy2: gphy@ac {
reg = <0xac>;
resets = <&reset0 28 13>, <&reset1 8 8>;
reset-names = "gphy", "gphy2";
};
gphy3: gphy@264 {
reg = <0x264>;
resets = <&reset0 10 10>; // do we need a second reset line here?
reset-names = "gphy";
};
};
};
eth0: eth@e10b308 { // address is correct (ar10.h)
compatible = "lantiq,xrx200-net";
reg = <0xe10b308 0x30>; /* pmac */
interrupt-parent = <&icu0>;
interrupts = <73>, <72>;
interrupt-names = "tx", "rx";
resets = <&reset0 21 16>, <&reset0 8 8>, <&reset0 3 3>;
reset-names = "switch", "ppe", "ppe_dsp";
#adderss-cells = <1>;
#size-cells = <0>;
// lantiq,tx-burst-length = ?
// lantiq,rx-burst-length = ?
fixed-link {
speed = <1000>;
full-duplex;
};
};
};
};
grx390_draytek_vigor2862.dts
#include "grx390.dtsi"
#include <dt-bindings/mips/lantiq_rcu_gphy.h>
#include <dt-bindings/input/input.h>
/ {
compatible = "draytek,vigor2862", "lantiq,xway", "lantiq,grx390";
model = "Draytek Vigor2862";
chosen {
bootargs = "console=ttyLTQ0,115200";
};
memory@0 {
device_type = "memory";
reg = <0x00 0xff00000>;
};
usb0_vbus: regulator-usb0-vbus {
compatible = "regulator-fixed";
regulator-name = "USB0_VBUS";
regulator-min-microvolt = <5000000>;
regulator-max-microvolt = <5000000>;
gpios = <&stp 4 GPIO_ACTIVE_HIGH>;
enable-active-high;
};
usb1_vbus: regulator-usb1-vbus {
compatible = "regulator-fixed";
regulator-name = "USB1_VBUS";
regulator-min-microvolt = <5000000>;
regulator-max-microvolt = <5000000>;
gpios = <&stp 7 GPIO_ACTIVE_HIGH>;
enable-active-high;
};
};
&gphy0 {
lantiq,gphy-mode = <GPHY_MODE_GE>;
};
&gphy1 {
lantiq,gphy-mode = <GPHY_MODE_GE>;
};
&gphy2 {
lantiq,gphy-mode = <GPHY_MODE_GE>;
};
&gphy3 {
lantiq,gphy-mode = <GPHY_MODE_GE>;
};
&gswip {
pinctrl-0 = <&mdio_pins>;
pinctrl-names = "default";
};
&gswip_mdio {
phy1: ethernet-phy@1 {
reg = <0x01>;
};
phy2: ethernet-phy@2 {
reg = <0x02>;
};
phy3: ethernet-phy@3 {
reg = <0x03>;
};
phy4: ethernet-phy@4 {
reg = <0x04>;
};
};
/*
* port 1 2 3 4 6 are internal;
* 0 can do all types of rgmii in addition to rmii and gmii;
* 5 can do all types of rgmii in addition to rmii and internal;
*/
&gswip_ports {
port@1 {
reg = <1>;
label = "port1";
phy-mode = "internal";
phy-handle = <&phy1>;
};
port@2 {
reg = <2>;
label = "port2";
phy-mode = "internal";
phy-handle = <&phy2>;
};
port@3 {
reg = <3>;
label = "port3";
phy-mode = "internal";
phy-handle = <&phy3>;
};
port@4 {
reg = <4>;
label = "port4";
phy-mode = "internal";
phy-handle = <&phy4>;
};
};
&localbus {
// not fully working yet, partitions don't show up
nand@1 {
compatible = "lantiq,nand-xway";
lantiq,cs = <1>;
bank-width = <2>;
reg = <1 0x0 0x2000000>;
#address-cells = <1>;
#size-cells = <1>;
pinctrl-0 = <&nand_pins>, <&nand_cs1_pins>;
pinctrl-names = "default";
nand-on-flash-bbt;
// ecc?
partitions {
compatible = "fixed_partitions";
#address-cells = <1>;
#size-cells = <1>;
partition@0 {
label = "uboot";
reg = <0x00 0x100000>;
read-only;
};
partition@100000 {
label = "ubootconfigA";
reg = <0x100000 0x40000>;
read-only;
};
partition@140000 {
label = "ubootconfigB";
reg = <0x140000 0x40000>;
read-only;
};
partition@180000 {
label = "gphyfirmware";
reg = <0x180000 0x40000>;
read-only;
};
partition@1c0000 {
label = "system_sw";
reg = <0x1c0000 0x3200000>;
read-only;
};
partition@33c0000 {
label = "calibration";
reg = <0x33c0000 0x100000>;
read-only;
};
partition@34c0000 {
label = "res";
reg = <0x34c0000 0xb40000>;
read-only;
};
};
};
};
&spi {
status = "okay";
m25p80@1 {
#address-cells = <1>;
#size-cells = <1>;
compatible = "jedec,spi-nor";
reg = <1>;
spi-max-frequency = <1000000>; // 1 MHz
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
partition@0 {
reg = <0x00 0x31000>;
label = "DrayBoot (u-boot)";
read-only;
};
partition@31000 {
reg = <0x31000 0xf000>;
label = "res (unknown)";
read-only;
};
};
};
};
&usb_phy0 {
status = "okay";
};
&usb_phy1 {
status = "okay";
};
&usb0 {
status = "okay";
vbus-supply = <&usb0_vbus>;
};
&usb1 {
status = "okay";
vbus-supply = <&usb1_vbus>;
};