NEC Aterm WG2200HP is a 2.4/5 GHz band 11ac (Wi-Fi 5) router, based on
QCA9558.
Specification:
- SoC : Qualcomm Atheros QCA9558
- RAM : DDR2 128 MiB (2x ESMT M14D5121632A)
- Flash : SPI-NOR 16 MiB (Macronix MX25L12835FM2I-10G)
- WLAN : 2.4/5 GHz
- 2.4 GHz : 3T3R (Qualcomm Atheros QCA9558 (SoC))
- 5 GHz : 4T4R (Qualcomm Atheros QCA9984)
- Ethernet : 5x 10/100/1000 Mbps
- switch : Qualcomm Atheros QCA8337
- LEDs/Keys (GPIO) : 12x/4x
- UART : through-hole on PCB
- assignment : 3.3V, GND, NC, TX, RX from tri-angle marking
- settings : 9600n8
- USB : 1x USB 2.0 Type-A
- Power : 12 VDC, 1.5 A (Max: 20 W)
- Stock OS : NetBSD based
Flash instruction using initramfs-factory.bin image:
1. Connect and open serial console
2. Power on WG2200HP and interrupt bootloader by ESC key
3. Login to the bootloader CLI with a password "chiron"
4. Start TFTP server by "tftpd" command
5. Upload initramfs-factory.bin via tftp from your computer
example (Windows): tftp -i 192.168.0.1 PUT initramfs-factory.bin
6. Boot initramfs image by "boot" command
7. On the initramfs image, back up the stock bootloader and firmware if
needed
8. Upload (or download) uboot.bin and sysupgrade.bin image to the device
9. Rplace the bootloader with a uboot.bin image
mtd write <uboot.bin image> bootloader
10. Perform sysupgrade with a sysupgrade.bin image
11. Wait ~120 seconds to complete flashing
Notes:
- All LEDs are connected to the Diodes PI4IOE5V9539LE I2C Expander chip.
(compatible with NXP PCA9539)
- The stock bootloader requires an unknown filesystem on firmware area
in the flash. Booting of OpenWrt from that filesystem cannot be
handled, so the bootloader needs to be replaced to mainline U-Boot
before OpenWrt installation.
- The data length of blocks in firmware image will be checked
(4M < threshold < 6M) on the stock WebUI of all versions, and
initramfs-factory.bin image of OpenWrt has the larger block data for
initramfs image. So that image cannot be applied to the stock WebUI
at all.
MAC addresses:
LAN : 98:F1:99:xx:xx:7C (config, 0x6 (hex))
WAN : 98:F1:99:xx:xx:7D (config, 0xc (hex))
2.4 GHz: 98:F1:99:xx:xx:7E (config, 0x0 (hex))
5 GHz : 98:F1:99:xx:xx:7F (config, 0x12 (hex))
Signed-off-by: INAGAKI Hiroshi <musashino.open@gmail.com>
Link: https://github.com/openwrt/openwrt/pull/17584
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
360 lines
10 KiB
C
360 lines
10 KiB
C
/*
|
|
* LZMA compressed kernel loader for Atheros AR7XXX/AR9XXX based boards
|
|
*
|
|
* Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org>
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#include <stddef.h>
|
|
#include "config.h"
|
|
#include "printf.h"
|
|
#include "ar71xx_regs.h"
|
|
|
|
#define READREG(r) *(volatile unsigned int *)(r)
|
|
#define WRITEREG(r,v) *(volatile unsigned int *)(r) = v
|
|
|
|
#define KSEG1ADDR(_x) (((_x) & 0x1fffffff) | 0xa0000000)
|
|
|
|
#define UART_BASE 0xb8020000
|
|
|
|
#define UART_TX 0
|
|
#define UART_LSR 5
|
|
|
|
#define UART_LSR_THRE 0x20
|
|
|
|
#define UART_READ(r) READREG(UART_BASE + 4 * (r))
|
|
#define UART_WRITE(r,v) WRITEREG(UART_BASE + 4 * (r), (v))
|
|
|
|
void board_putc(int ch)
|
|
{
|
|
while (((UART_READ(UART_LSR)) & UART_LSR_THRE) == 0);
|
|
UART_WRITE(UART_TX, ch);
|
|
while (((UART_READ(UART_LSR)) & UART_LSR_THRE) == 0);
|
|
}
|
|
|
|
#ifdef CONFIG_BOARD_TL_WR1043ND_V1
|
|
static void tlwr1043nd_init(void)
|
|
{
|
|
unsigned int reg = KSEG1ADDR(AR71XX_RESET_BASE);
|
|
unsigned int t;
|
|
|
|
t = READREG(reg + AR913X_RESET_REG_RESET_MODULE);
|
|
t |= AR71XX_RESET_GE0_PHY;
|
|
WRITEREG(reg + AR913X_RESET_REG_RESET_MODULE, t);
|
|
/* flush write */
|
|
t = READREG(reg + AR913X_RESET_REG_RESET_MODULE);
|
|
}
|
|
#else
|
|
static inline void tlwr1043nd_init(void) {}
|
|
#endif
|
|
|
|
#if defined(CONFIG_BOARD_MERAKI_MR18) || \
|
|
defined(CONFIG_BOARD_NEC_WG1400HP) || \
|
|
defined(CONFIG_BOARD_NEC_WG1800HP) || \
|
|
defined(CONFIG_BOARD_NEC_WG1800HP2) || \
|
|
defined(CONFIG_BOARD_NEC_WG2200HP)
|
|
|
|
static int extract_qca955x_sgmii_res_cal(void)
|
|
{
|
|
unsigned int base;
|
|
unsigned int reversed_sgmii_value;
|
|
|
|
unsigned int otp_value, otp_per_val, rbias_per, read_data;
|
|
unsigned int rbias_pos_or_neg;
|
|
unsigned int sgmii_res_cal_value;
|
|
int res_cal_val;
|
|
|
|
base = KSEG1ADDR(QCA955X_OTP_BASE);
|
|
|
|
WRITEREG(base + QCA955X_OTP_REG_INTF2, 0x7d);
|
|
WRITEREG(base + QCA955X_OTP_REG_LDO_CTRL, 0x00);
|
|
|
|
while (READREG(base + QCA955X_OTP_REG_LDO_STATUS) &
|
|
QCA955X_OTP_LDO_STATUS_POWER_ON)
|
|
;
|
|
|
|
READREG(base + QCA955X_OTP_REG_MEM_0 + 4);
|
|
|
|
while (!(READREG(base + QCA955X_OTP_REG_STATUS0) &
|
|
QCA955X_OTP_STATUS0_EFUSE_VALID))
|
|
;
|
|
|
|
read_data = READREG(base + QCA955X_OTP_REG_STATUS1);
|
|
|
|
if (!(read_data & 0x1fff))
|
|
return 0;
|
|
|
|
if (read_data & 0x00001000)
|
|
otp_value = (read_data & 0xfc0) >> 6;
|
|
else
|
|
otp_value = read_data & 0x3f;
|
|
|
|
if (otp_value > 31) {
|
|
otp_per_val = 63 - otp_value;
|
|
rbias_pos_or_neg = 1;
|
|
} else {
|
|
otp_per_val = otp_value;
|
|
rbias_pos_or_neg = 0;
|
|
}
|
|
|
|
rbias_per = otp_per_val * 15;
|
|
|
|
if (rbias_pos_or_neg == 1)
|
|
res_cal_val = (rbias_per + 34) / 21;
|
|
else if (rbias_per > 34)
|
|
res_cal_val = -((rbias_per - 34) / 21);
|
|
else
|
|
res_cal_val = (34 - rbias_per) / 21;
|
|
|
|
sgmii_res_cal_value = (8 + res_cal_val) & 0xf;
|
|
|
|
reversed_sgmii_value = (sgmii_res_cal_value & 8) >> 3;
|
|
reversed_sgmii_value |= (sgmii_res_cal_value & 4) >> 1;
|
|
reversed_sgmii_value |= (sgmii_res_cal_value & 2) << 1;
|
|
reversed_sgmii_value |= (sgmii_res_cal_value & 1) << 3;
|
|
printf("SGMII cal value = 0x%x\n", reversed_sgmii_value);
|
|
return reversed_sgmii_value;
|
|
}
|
|
|
|
#define QCA955X_SGMII_SERDES_RES_CALIBRATION BIT(23)
|
|
#define QCA955X_SGMII_SERDES_RES_CALIBRATION_MASK 0xf
|
|
#define QCA955X_SGMII_SERDES_RES_CALIBRATION_SHIFT 23
|
|
#define QCA955X_SGMII_SERDES_LOCK_DETECT_STATUS BIT(15)
|
|
#define QCA955X_PLL_ETH_SGMII_SERDES_LOCK_DETECT BIT(2)
|
|
#define QCA955X_PLL_ETH_SGMII_SERDES_PLL_REFCLK BIT(1)
|
|
#define QCA955X_PLL_ETH_SGMII_SERDES_EN_PLL BIT(0)
|
|
#define QCA955X_PLL_CLK_CTRL_REG 0x08
|
|
#define QCA955X_PLL_ETH_XMII_CONTROL_REG 0x28
|
|
#define QCA955X_PLL_ETH_SGMII_CONTROL_REG 0x48
|
|
#define QCA955X_PLL_ETH_SGMII_SERDES_REG 0x4c
|
|
|
|
static void qca955x_device_reset_clear(unsigned int mask)
|
|
{
|
|
unsigned int t, reg;
|
|
|
|
reg = KSEG1ADDR(AR71XX_RESET_BASE +
|
|
QCA955X_RESET_REG_RESET_MODULE);
|
|
|
|
t = READREG(reg);
|
|
WRITEREG(reg, t & ~mask);
|
|
}
|
|
|
|
static void setup_qca955x_eth_serdes_cal(unsigned int sgmii_value)
|
|
{
|
|
unsigned int ethbase, pllbase, t;
|
|
|
|
ethbase = KSEG1ADDR(QCA955X_GMAC_BASE);
|
|
pllbase = KSEG1ADDR(AR71XX_PLL_BASE);
|
|
|
|
/* To Check the locking of the SGMII PLL */
|
|
t = READREG(ethbase + QCA955X_GMAC_REG_SGMII_SERDES);
|
|
t &= ~(QCA955X_SGMII_SERDES_RES_CALIBRATION_MASK <<
|
|
QCA955X_SGMII_SERDES_RES_CALIBRATION_SHIFT);
|
|
t |= (sgmii_value & QCA955X_SGMII_SERDES_RES_CALIBRATION_MASK) <<
|
|
QCA955X_SGMII_SERDES_RES_CALIBRATION_SHIFT;
|
|
WRITEREG(ethbase + QCA955X_GMAC_REG_SGMII_SERDES, t);
|
|
|
|
WRITEREG(pllbase + QCA955X_PLL_ETH_SGMII_SERDES_REG,
|
|
QCA955X_PLL_ETH_SGMII_SERDES_LOCK_DETECT |
|
|
QCA955X_PLL_ETH_SGMII_SERDES_PLL_REFCLK |
|
|
QCA955X_PLL_ETH_SGMII_SERDES_EN_PLL)
|
|
;
|
|
|
|
qca955x_device_reset_clear(QCA955X_RESET_SGMII_ANALOG);
|
|
qca955x_device_reset_clear(QCA955X_RESET_SGMII);
|
|
|
|
while (!(READREG(ethbase + QCA955X_GMAC_REG_SGMII_SERDES) &
|
|
QCA955X_SGMII_SERDES_LOCK_DETECT_STATUS))
|
|
;
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_BOARD_MERAKI_MR18
|
|
static inline void mr18_init(void)
|
|
{
|
|
int res;
|
|
|
|
printf("Meraki MR18\n");
|
|
|
|
res = extract_qca955x_sgmii_res_cal();
|
|
if (res >= 0)
|
|
setup_qca955x_eth_serdes_cal(res);
|
|
|
|
}
|
|
#else
|
|
static inline void mr18_init(void) { }
|
|
#endif
|
|
|
|
#if defined(CONFIG_BOARD_HUAWEI_AP5030DN) || defined(CONFIG_BOARD_HUAWEI_AP6010DN)
|
|
static inline void huawei_ap_init(void)
|
|
{
|
|
const unsigned int watchdog_gpio = 15;
|
|
unsigned int gpiobase, reg;
|
|
|
|
gpiobase = KSEG1ADDR(AR71XX_GPIO_BASE);
|
|
|
|
printf("Huawei AP\n");
|
|
|
|
reg = READREG(gpiobase + AR71XX_GPIO_REG_OE);
|
|
WRITEREG(gpiobase + AR71XX_GPIO_REG_OE,
|
|
reg & ~(1 << watchdog_gpio));
|
|
|
|
/* Set GPIO15 MUX to output CLK_OBS5 (= CPU_CLK/4)
|
|
* or CLK_OBS4 (= AHB_CLK/2) to keep the watchdog happy
|
|
* until wdt-gpio takes over
|
|
*/
|
|
reg = READREG(gpiobase + AR934X_GPIO_REG_OUT_FUNC3);
|
|
#if defined(CONFIG_BOARD_HUAWEI_AP5030DN)
|
|
WRITEREG(gpiobase + AR934X_GPIO_REG_OUT_FUNC3,
|
|
reg | (QCA955X_GPIO_OUTSEL_CLK_OBS5 << 24));
|
|
#elif defined(CONFIG_BOARD_HUAWEI_AP6010DN)
|
|
WRITEREG(gpiobase + AR934X_GPIO_REG_OUT_FUNC3,
|
|
reg | (AR934X_GPIO_OUTSEL_CLK_OBS4 << 24));
|
|
#endif
|
|
}
|
|
#else
|
|
static inline void huawei_ap_init(void) {}
|
|
#endif
|
|
|
|
#if defined(CONFIG_BOARD_NEC_WG1400HP) || \
|
|
defined(CONFIG_BOARD_NEC_WG1800HP) || \
|
|
defined(CONFIG_BOARD_NEC_WG1800HP2) || \
|
|
defined(CONFIG_BOARD_NEC_WG2200HP) || \
|
|
defined(CONFIG_BOARD_NEC_WG600HP) || \
|
|
defined(CONFIG_BOARD_NEC_WR8750N) || \
|
|
defined(CONFIG_BOARD_NEC_WR9500N)
|
|
static inline void nec_aterm_reset_common(void)
|
|
{
|
|
unsigned int reg = KSEG1ADDR(AR71XX_RESET_BASE);
|
|
|
|
#ifndef LOADADDR
|
|
/*
|
|
* This is for initramfs-factory image.
|
|
* When the system was reset by power source or FULL_CHIP_RESET
|
|
* and started from the OEM bootloader with a dummy tp data
|
|
* (this loader), reset again by timeout of the watchdog timer
|
|
* to load an actual OpenWrt initramfs image in firmware block
|
|
* in a factory image.
|
|
* Note: On the stock firmware, TP block contains a POST function
|
|
* and sub commands of "tp" command.
|
|
*
|
|
* Behaviors of OEM bootloader:
|
|
*
|
|
* - reset by watchdog (ex.: rebooting on the stock firmware):
|
|
* called as "SOFT-RESET", boot a firmware without POST
|
|
*
|
|
* - reset by FULL_CHIP_RESET (or powering on):
|
|
* called as "HARD-RESET", run POST and boot a firmware
|
|
*/
|
|
printf("\n## booted with dummy tp (lzma-loader),"
|
|
" waiting reset... (count: 0x%08x) ##\n",
|
|
READREG(reg + AR71XX_RESET_REG_WDOG));
|
|
while (1);
|
|
#endif
|
|
/*
|
|
* set maximum watchdog count to avoid reset while
|
|
* booting from stock bootloader
|
|
*/
|
|
WRITEREG(reg + AR71XX_RESET_REG_WDOG, 0xffffffff);
|
|
}
|
|
#endif
|
|
|
|
#if defined(CONFIG_BOARD_NEC_WG600HP) || \
|
|
defined(CONFIG_BOARD_NEC_WR8750N) || \
|
|
defined(CONFIG_BOARD_NEC_WR9500N)
|
|
|
|
#define AR934X_PLL_SWITCH_CLK_CTRL_REG 0x24
|
|
#define AR934X_PLL_SWITCH_CLK_CTRL_SWITCHCLK_SEL BIT(0)
|
|
|
|
static inline void nec_aterm_init(void)
|
|
{
|
|
unsigned int reg, val;
|
|
|
|
printf("NEC Aterm series (AR9344)\n");
|
|
|
|
/* set REFCLK=40MHz to switch PLL */
|
|
reg = KSEG1ADDR(AR71XX_PLL_BASE);
|
|
val = READREG(reg + AR934X_PLL_SWITCH_CLK_CTRL_REG);
|
|
val &= ~AR934X_PLL_SWITCH_CLK_CTRL_SWITCHCLK_SEL;
|
|
WRITEREG(reg + AR934X_PLL_SWITCH_CLK_CTRL_REG, val);
|
|
|
|
nec_aterm_reset_common();
|
|
|
|
/*
|
|
* deassert some RESET bits not handled by drivers
|
|
* and mainline U-Boot
|
|
*
|
|
* - ETH_SWITCH(_ANALOG): eth0
|
|
* - RTC : wmac
|
|
*/
|
|
reg = KSEG1ADDR(AR71XX_RESET_BASE);
|
|
val = READREG(reg + AR934X_RESET_REG_RESET_MODULE);
|
|
val &= ~(AR934X_RESET_ETH_SWITCH | AR934X_RESET_ETH_SWITCH_ANALOG |
|
|
AR934X_RESET_RTC);
|
|
WRITEREG(reg + AR934X_RESET_REG_RESET_MODULE, val);
|
|
}
|
|
#elif defined(CONFIG_BOARD_NEC_WG1400HP) || \
|
|
defined(CONFIG_BOARD_NEC_WG1800HP) || \
|
|
defined(CONFIG_BOARD_NEC_WG1800HP2) || \
|
|
defined(CONFIG_BOARD_NEC_WG2200HP)
|
|
|
|
#define QCA955X_GMAC_MR_AN_CONTROL_PHY_RESET_MASK BIT(15)
|
|
#define QCA955X_GMAC_MR_AN_CONTROL_FULL_DUPLEX_MASK BIT(8)
|
|
#define QCA955X_GMAC_MR_AN_CONTROL_SPEED_SEL1_MASK BIT(6)
|
|
|
|
#define QCA955X_GMAC_SGMII_CONFIG_SPEED_SHIFT 6
|
|
#define QCA955X_GMAC_SGMII_CONFIG_SPEED_1000M 0x2
|
|
#define QCA955X_GMAC_SGMII_CONFIG_FORCE_SPEED_MASK BIT(5)
|
|
#define QCA955X_GMAC_SGMII_CONFIG_MODE_CTRL_SHIFT 0
|
|
#define QCA955X_GMAC_SGMII_CONFIG_MODE_SGMII 0x2
|
|
|
|
static inline void nec_aterm_init(void)
|
|
{
|
|
unsigned int reg, val;
|
|
int ret;
|
|
|
|
printf("NEC Aterm series (QCA9558)\n");
|
|
|
|
nec_aterm_reset_common();
|
|
|
|
printf("\nCalibrating SGMII\n");
|
|
ret = extract_qca955x_sgmii_res_cal();
|
|
if (ret >= 0)
|
|
setup_qca955x_eth_serdes_cal(ret);
|
|
|
|
/* set SGMII force mode to make eth1 working */
|
|
printf("\nConfiguring SGMII force mode\n");
|
|
reg = KSEG1ADDR(QCA955X_GMAC_BASE);
|
|
WRITEREG(reg + QCA955X_GMAC_REG_SGMII_CONFIG,
|
|
QCA955X_GMAC_SGMII_CONFIG_SPEED_1000M
|
|
<< QCA955X_GMAC_SGMII_CONFIG_SPEED_SHIFT |
|
|
QCA955X_GMAC_SGMII_CONFIG_FORCE_SPEED_MASK |
|
|
QCA955X_GMAC_SGMII_CONFIG_MODE_SGMII
|
|
<< QCA955X_GMAC_SGMII_CONFIG_MODE_CTRL_SHIFT);
|
|
printf(" SGMII_CONFIG : 0x%08x\n",
|
|
READREG(reg + QCA955X_GMAC_REG_SGMII_CONFIG));
|
|
val = QCA955X_GMAC_MR_AN_CONTROL_FULL_DUPLEX_MASK |
|
|
QCA955X_GMAC_MR_AN_CONTROL_SPEED_SEL1_MASK;
|
|
WRITEREG(reg + QCA955X_GMAC_REG_MR_AN_CONTROL,
|
|
val | QCA955X_GMAC_MR_AN_CONTROL_PHY_RESET_MASK);
|
|
printf(" MR_AN_CONTROL: 0x%08x\n",
|
|
READREG(reg + QCA955X_GMAC_REG_MR_AN_CONTROL));
|
|
WRITEREG(reg + QCA955X_GMAC_REG_MR_AN_CONTROL, val);
|
|
printf(" MR_AN_CONTROL: 0x%08x\n",
|
|
READREG(reg + QCA955X_GMAC_REG_MR_AN_CONTROL));
|
|
}
|
|
#else
|
|
static inline void nec_aterm_init(void) {}
|
|
#endif
|
|
|
|
void board_init(void)
|
|
{
|
|
tlwr1043nd_init();
|
|
mr18_init();
|
|
huawei_ap_init();
|
|
nec_aterm_init();
|
|
}
|