/*
* Support for COMFAST boards:
* - TL-WR881N v1 (QCA9558)
*
* Copyright (C) 2016 Piotr Dymacz
* Copyright (C) 2016 Gareth Parker
* Copyright (C) 2015 Paul Fertser
*
* 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
#include
#include
#include
#include
#include
#include
#include
#include
#include "common.h"
#include "dev-eth.h"
#include "dev-gpio-buttons.h"
#include "dev-leds-gpio.h"
#include "dev-m25p80.h"
#include "dev-wmac.h"
#include "dev-spi.h"
#include "machtypes.h"
#define CF_EXXXN_KEYS_POLL_INTERVAL 20
#define CF_EXXXN_KEYS_DEBOUNCE_INTERVAL (3 * CF_EXXXN_KEYS_POLL_INTERVAL)
/* CF-E380AC v1/v2 */
#define CF_E380AC_V1V2_GPIO_LED_SYSTEM 19
#define CF_E380AC_V1V2_GPIO_LED_WLAN 12
#define CF_E380AC_V1V2_GPIO_LED_WPS 15
#define CF_E380AC_V1V2_GPIO_BTN_RESET 16
#define ATH_MII_MGMT_CMD 0x24
#define ATH_MGMT_CMD_READ 0x1
#define ATH_MII_MGMT_ADDRESS 0x28
#define ATH_ADDR_SHIFT 8
#define ATH_MII_MGMT_CTRL 0x2c
#define ATH_MII_MGMT_STATUS 0x30
#define ATH_MII_MGMT_IND 0x34
#define ATH_MGMT_IND_BUSY (1 << 0)
#define ATH_MGMT_IND_INVALID (1 << 2)
#define QCA955X_ETH_CFG_GE0_MII_EN BIT(1)
#define QCA955X_ETH_CFG_GE0_MII_SLAVE BIT(4)
static struct gpio_led cf_e380ac_v1_leds_gpio[] __initdata = {
{
.name = "cf-e380ac-v1:green:wps",
.gpio = CF_E380AC_V1V2_GPIO_LED_WPS,
.active_low = 0,
}, {
.name = "cf-e380ac-v1:blue:wlan",
.gpio = CF_E380AC_V1V2_GPIO_LED_WLAN,
.active_low = 0,
}, {
.name = "cf-e380ac-v1:red:system",
.gpio = CF_E380AC_V1V2_GPIO_LED_SYSTEM,
.active_low = 0,
},
};
static struct gpio_keys_button cf_e380ac_v1v2_gpio_keys[] __initdata = {
{
.desc = "Reset button",
.type = EV_KEY,
.code = KEY_RESTART,
.debounce_interval = CF_EXXXN_KEYS_DEBOUNCE_INTERVAL,
.gpio = CF_E380AC_V1V2_GPIO_BTN_RESET,
.active_low = 1,
},
};
static const char *cf_e380ac_v1v2_part_probes[] = {
"tp-link",
NULL,
};
static struct flash_platform_data cf_e380ac_v1v2_flash_data = {
.part_probes = cf_e380ac_v1v2_part_probes,
};
static struct mdio_board_info cf_e380ac_v1v2_mdio0_info[] = {
{
.bus_id = "ag71xx-mdio.0",
.phy_addr = 0,
.platform_data = NULL,
},
};
static unsigned long __init ath_gmac_reg_rd(unsigned long reg)
{
void __iomem *base;
unsigned long t;
base = ioremap(AR71XX_GE0_BASE, AR71XX_GE0_SIZE);
t = __raw_readl(base + reg);
iounmap(base);
return t;
}
static void __init ath_gmac_reg_wr(unsigned long reg, unsigned long value)
{
void __iomem *base;
unsigned long t = value;
base = ioremap(AR71XX_GE0_BASE, AR71XX_GE0_SIZE);
__raw_writel(t, base + reg);
iounmap(base);
}
static void __init phy_reg_write(unsigned char phy_addr, unsigned char reg, unsigned short data)
{
unsigned short addr = (phy_addr << ATH_ADDR_SHIFT) | reg;
volatile int rddata;
unsigned short ii = 0xFFFF;
do
{
udelay(5);
rddata = ath_gmac_reg_rd(ATH_MII_MGMT_IND) & 0x1;
} while (rddata && --ii);
ath_gmac_reg_wr(ATH_MII_MGMT_ADDRESS, addr);
ath_gmac_reg_wr(ATH_MII_MGMT_CTRL, data);
do
{
udelay(5);
rddata = ath_gmac_reg_rd(ATH_MII_MGMT_IND) & 0x1;
} while (rddata && --ii);
}
static unsigned short __init phy_reg_read(unsigned char phy_addr, unsigned char reg)
{
unsigned short addr = (phy_addr << ATH_ADDR_SHIFT) | reg, val;
volatile int rddata;
unsigned short ii = 0xffff;
do
{
udelay(5);
rddata = ath_gmac_reg_rd(ATH_MII_MGMT_IND) & 0x1;
} while (rddata && --ii);
ath_gmac_reg_wr(ATH_MII_MGMT_CMD, 0x0);
ath_gmac_reg_wr(ATH_MII_MGMT_ADDRESS, addr);
ath_gmac_reg_wr(ATH_MII_MGMT_CMD, ATH_MGMT_CMD_READ);
do
{
udelay(5);
rddata = ath_gmac_reg_rd(ATH_MII_MGMT_IND) & 0x1;
} while (rddata && --ii);
val = ath_gmac_reg_rd(ATH_MII_MGMT_STATUS);
ath_gmac_reg_wr(ATH_MII_MGMT_CMD, 0x0);
return val;
}
static void __init athrs27_reg_write(unsigned int s27_addr, unsigned int s27_write_data)
{
unsigned int addr_temp;
unsigned int data;
unsigned char phy_address, reg_address;
addr_temp = (s27_addr) >> 2;
data = addr_temp >> 7;
phy_address = 0x1f;
reg_address = 0x10;
phy_reg_write(phy_address, reg_address, data);
phy_address = (0x17 & ((addr_temp >> 4) | 0x10));
reg_address = (((addr_temp << 1) & 0x1e) | 0x1);
data = (s27_write_data >> 16) & 0xffff;
phy_reg_write(phy_address, reg_address, data);
reg_address = ((addr_temp << 1) & 0x1e);
data = s27_write_data & 0xffff;
phy_reg_write(phy_address, reg_address, data);
}
static unsigned int __init athrs27_reg_read(unsigned int s27_addr)
{
unsigned int addr_temp;
unsigned int s27_rd_csr_low, s27_rd_csr_high, s27_rd_csr;
unsigned int data;
unsigned char phy_address, reg_address;
addr_temp = s27_addr >>2;
data = addr_temp >> 7;
phy_address = 0x1f;
reg_address = 0x10;
phy_reg_write(phy_address, reg_address, data);
phy_address = (0x17 & ((addr_temp >> 4) | 0x10));
reg_address = ((addr_temp << 1) & 0x1e);
s27_rd_csr_low = (unsigned int) phy_reg_read(phy_address, reg_address);
reg_address = reg_address | 0x1;
s27_rd_csr_high = (unsigned int) phy_reg_read(phy_address, reg_address);
s27_rd_csr = (s27_rd_csr_high << 16) | s27_rd_csr_low ;
return (s27_rd_csr);
}
static void __init ar8236_reset(void)
{
unsigned short i = 30;
athrs27_reg_write(0x0, athrs27_reg_read(0x0) | 0x80000000);
while (i--)
{
mdelay(100);
if (!(athrs27_reg_read(0x0) & 0x80000000))
break;
}
}
static void __init cf_e380ac_v1_setup(void)
{
u8 *mac = (u8 *) KSEG1ADDR(0x1f01fc00);
u8 *art = (u8 *) KSEG1ADDR(0x1fff1000);
u8 tmpmac[ETH_ALEN];
ath79_register_m25p80(&cf_e380ac_v1v2_flash_data);
ath79_setup_qca955x_eth_cfg(QCA955X_ETH_CFG_GE0_MII_EN | QCA955X_ETH_CFG_GE0_MII_SLAVE);
ath79_register_mdio(0, 0x0);
mdiobus_register_board_info(cf_e380ac_v1v2_mdio0_info,
ARRAY_SIZE(cf_e380ac_v1v2_mdio0_info));
ar8236_reset();
/* Wi-Fi */
ath79_init_mac(tmpmac, mac, 0);
ath79_register_wmac(art, tmpmac);
/* LAN */
ath79_init_mac(ath79_eth0_data.mac_addr, mac, 1);
/* GMAC0 is connected to the PHY4 of the internal switch */
ath79_eth0_data.mii_bus_dev = &ath79_mdio0_device.dev;
ath79_eth0_data.phy_if_mode = PHY_INTERFACE_MODE_MII;
ath79_eth0_data.phy_mask = BIT(0);
ath79_eth0_data.speed = SPEED_100;
ath79_eth0_data.duplex = DUPLEX_FULL;
ath79_register_eth(0);
ath79_register_gpio_keys_polled(-1, CF_EXXXN_KEYS_POLL_INTERVAL,
ARRAY_SIZE(cf_e380ac_v1v2_gpio_keys),
cf_e380ac_v1v2_gpio_keys);
ath79_register_leds_gpio(-1, ARRAY_SIZE(cf_e380ac_v1_leds_gpio),
cf_e380ac_v1_leds_gpio);
}
MIPS_MACHINE(ATH79_MACH_TL_WR881N_V1, "TL-WR881N-V1", "TP-LINK TL-WR881N v1", cf_e380ac_v1_setup);