From 266141c2ba061fb44ad5e419dfcb277fde32d6c0 Mon Sep 17 00:00:00 2001 From: Yegor Yefremov Date: Thu, 14 Sep 2017 10:25:00 +0200 Subject: net: ath79: add ag71xx Ethernet driver Signed-off-by: Yegor Yefremov Signed-off-by: Antony Pavlov Signed-off-by: Oleksij Rempel Signed-off-by: Sascha Hauer --- arch/mips/mach-ath79/include/mach/ar71xx_regs.h | 12 + drivers/net/Kconfig | 7 + drivers/net/Makefile | 1 + drivers/net/ag71xx.c | 673 ++++++++++++++++++++++++ 4 files changed, 693 insertions(+) create mode 100644 drivers/net/ag71xx.c diff --git a/arch/mips/mach-ath79/include/mach/ar71xx_regs.h b/arch/mips/mach-ath79/include/mach/ar71xx_regs.h index f56c3f724e..31d33b3c42 100644 --- a/arch/mips/mach-ath79/include/mach/ar71xx_regs.h +++ b/arch/mips/mach-ath79/include/mach/ar71xx_regs.h @@ -102,6 +102,8 @@ #define AR933X_PLL_CLOCK_CTRL_REG 0x08 #define AR933X_PLL_DITHER_FRAC_REG 0x10 #define AR933X_PLL_DITHER_REG 0x14 +#define AR933X_ETHSW_CLOCK_CONTROL_REG 0x24 +#define AR933X_ETH_XMII_CONTROL_REG 0x2c #define AR933X_PLL_CPU_CONFIG_NINT_SHIFT 10 #define AR933X_PLL_CPU_CONFIG_NINT_MASK 0x3f @@ -125,6 +127,16 @@ #define AR933X_RESET_REG_RESET_MODULE 0x1c #define AR933X_RESET_REG_BOOTSTRAP 0xac +#define AR933X_RESET_GE1_MDIO BIT(23) +#define AR933X_RESET_GE0_MDIO BIT(22) +#define AR933X_RESET_GE1_MAC BIT(13) +#define AR933X_RESET_WMAC BIT(11) +#define AR933X_RESET_GE0_MAC BIT(9) +#define AR933X_RESET_SWITCH BIT(8) +#define AR933X_RESET_USB_HOST BIT(5) +#define AR933X_RESET_USB_PHY BIT(4) +#define AR933X_RESET_USBSUS_OVERRIDE BIT(3) + #define AR71XX_RESET_FULL_CHIP BIT(24) #define AR933X_BOOTSTRAP_REF_CLK_40 BIT(0) diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index c3980e78f5..9d69b6aeb0 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -204,6 +204,13 @@ config DRIVER_NET_EFI_SNP bool "EFI SNP ethernet driver" depends on EFI_BOOTUP +config DRIVER_NET_AG71XX + bool "Atheros AG71xx ethernet driver" + depends on MACH_MIPS_ATH79 + select PHYLIB + help + This option enables support for Atheros AG71XX ethernet chip. + config DRIVER_NET_TSE depends on NIOS2 bool "Altera TSE ethernet driver" diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 42ea208e3b..86c8ac32f9 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -30,3 +30,4 @@ obj-$(CONFIG_DRIVER_NET_SMC91111) += smc91111.o obj-$(CONFIG_DRIVER_NET_TAP) += tap.o obj-$(CONFIG_DRIVER_NET_TSE) += altera_tse.o obj-$(CONFIG_DRIVER_NET_EFI_SNP) += efi-snp.o +obj-$(CONFIG_DRIVER_NET_AG71XX) += ag71xx.o diff --git a/drivers/net/ag71xx.c b/drivers/net/ag71xx.c new file mode 100644 index 0000000000..54f0bd9aa5 --- /dev/null +++ b/drivers/net/ag71xx.c @@ -0,0 +1,673 @@ +/* + * Atheros AR71xx built-in ethernet mac driver + * + * Copyright (C) 2008-2010 Gabor Juhos + * Copyright (C) 2008 Imre Kaloz + * + * Based on Atheros' AG7100 driver + * + * 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 + +#include + +/* Register offsets */ +#define AG71XX_REG_MAC_CFG1 0x0000 +#define AG71XX_REG_MAC_CFG2 0x0004 +#define AG71XX_REG_MAC_IPG 0x0008 +#define AG71XX_REG_MAC_HDX 0x000c +#define AG71XX_REG_MAC_MFL 0x0010 +#define AG71XX_REG_MII_CFG 0x0020 +#define AG71XX_REG_MII_CMD 0x0024 +#define AG71XX_REG_MII_ADDR 0x0028 +#define AG71XX_REG_MII_CTRL 0x002c +#define AG71XX_REG_MII_STATUS 0x0030 +#define AG71XX_REG_MII_IND 0x0034 +#define AG71XX_REG_MAC_IFCTL 0x0038 +#define AG71XX_REG_MAC_ADDR1 0x0040 +#define AG71XX_REG_MAC_ADDR2 0x0044 +#define AG71XX_REG_FIFO_CFG0 0x0048 +#define AG71XX_REG_FIFO_CFG1 0x004c +#define AG71XX_REG_FIFO_CFG2 0x0050 +#define AG71XX_REG_FIFO_CFG3 0x0054 +#define AG71XX_REG_FIFO_CFG4 0x0058 +#define AG71XX_REG_FIFO_CFG5 0x005c +#define AG71XX_REG_FIFO_RAM0 0x0060 +#define AG71XX_REG_FIFO_RAM1 0x0064 +#define AG71XX_REG_FIFO_RAM2 0x0068 +#define AG71XX_REG_FIFO_RAM3 0x006c +#define AG71XX_REG_FIFO_RAM4 0x0070 +#define AG71XX_REG_FIFO_RAM5 0x0074 +#define AG71XX_REG_FIFO_RAM6 0x0078 +#define AG71XX_REG_FIFO_RAM7 0x007c + +#define AG71XX_REG_TX_CTRL 0x0180 +#define AG71XX_REG_TX_DESC 0x0184 +#define AG71XX_REG_TX_STATUS 0x0188 +#define AG71XX_REG_RX_CTRL 0x018c +#define AG71XX_REG_RX_DESC 0x0190 +#define AG71XX_REG_RX_STATUS 0x0194 +#define AG71XX_REG_INT_ENABLE 0x0198 +#define AG71XX_REG_INT_STATUS 0x019c + +#define AG71XX_REG_FIFO_DEPTH 0x01a8 +#define AG71XX_REG_RX_SM 0x01b0 +#define AG71XX_REG_TX_SM 0x01b4 + +#define MAC_CFG1_TXE BIT(0) /* Tx Enable */ +#define MAC_CFG1_STX BIT(1) /* Synchronize Tx Enable */ +#define MAC_CFG1_RXE BIT(2) /* Rx Enable */ +#define MAC_CFG1_SRX BIT(3) /* Synchronize Rx Enable */ +#define MAC_CFG1_TFC BIT(4) /* Tx Flow Control Enable */ +#define MAC_CFG1_RFC BIT(5) /* Rx Flow Control Enable */ +#define MAC_CFG1_LB BIT(8) /* Loopback mode */ +#define MAC_CFG1_TX_RST BIT(18) /* Tx Reset */ +#define MAC_CFG1_RX_RST BIT(19) /* Rx Reset */ +#define MAC_CFG1_SR BIT(31) /* Soft Reset */ + +#define MAC_CFG2_FDX BIT(0) +#define MAC_CFG2_CRC_EN BIT(1) +#define MAC_CFG2_PAD_CRC_EN BIT(2) +#define MAC_CFG2_LEN_CHECK BIT(4) +#define MAC_CFG2_HUGE_FRAME_EN BIT(5) +#define MAC_CFG2_IF_1000 BIT(9) +#define MAC_CFG2_IF_10_100 BIT(8) + +#define FIFO_CFG0_WTM BIT(0) /* Watermark Module */ +#define FIFO_CFG0_RXS BIT(1) /* Rx System Module */ +#define FIFO_CFG0_RXF BIT(2) /* Rx Fabric Module */ +#define FIFO_CFG0_TXS BIT(3) /* Tx System Module */ +#define FIFO_CFG0_TXF BIT(4) /* Tx Fabric Module */ +#define FIFO_CFG0_ALL (FIFO_CFG0_WTM | FIFO_CFG0_RXS | FIFO_CFG0_RXF \ + | FIFO_CFG0_TXS | FIFO_CFG0_TXF) + +#define FIFO_CFG0_ENABLE_SHIFT 8 + +#define FIFO_CFG4_DE BIT(0) /* Drop Event */ +#define FIFO_CFG4_DV BIT(1) /* RX_DV Event */ +#define FIFO_CFG4_FC BIT(2) /* False Carrier */ +#define FIFO_CFG4_CE BIT(3) /* Code Error */ +#define FIFO_CFG4_CR BIT(4) /* CRC error */ +#define FIFO_CFG4_LM BIT(5) /* Length Mismatch */ +#define FIFO_CFG4_LO BIT(6) /* Length out of range */ +#define FIFO_CFG4_OK BIT(7) /* Packet is OK */ +#define FIFO_CFG4_MC BIT(8) /* Multicast Packet */ +#define FIFO_CFG4_BC BIT(9) /* Broadcast Packet */ +#define FIFO_CFG4_DR BIT(10) /* Dribble */ +#define FIFO_CFG4_LE BIT(11) /* Long Event */ +#define FIFO_CFG4_CF BIT(12) /* Control Frame */ +#define FIFO_CFG4_PF BIT(13) /* Pause Frame */ +#define FIFO_CFG4_UO BIT(14) /* Unsupported Opcode */ +#define FIFO_CFG4_VT BIT(15) /* VLAN tag detected */ +#define FIFO_CFG4_FT BIT(16) /* Frame Truncated */ +#define FIFO_CFG4_UC BIT(17) /* Unicast Packet */ + +#define FIFO_CFG5_DE BIT(0) /* Drop Event */ +#define FIFO_CFG5_DV BIT(1) /* RX_DV Event */ +#define FIFO_CFG5_FC BIT(2) /* False Carrier */ +#define FIFO_CFG5_CE BIT(3) /* Code Error */ +#define FIFO_CFG5_LM BIT(4) /* Length Mismatch */ +#define FIFO_CFG5_LO BIT(5) /* Length Out of Range */ +#define FIFO_CFG5_OK BIT(6) /* Packet is OK */ +#define FIFO_CFG5_MC BIT(7) /* Multicast Packet */ +#define FIFO_CFG5_BC BIT(8) /* Broadcast Packet */ +#define FIFO_CFG5_DR BIT(9) /* Dribble */ +#define FIFO_CFG5_CF BIT(10) /* Control Frame */ +#define FIFO_CFG5_PF BIT(11) /* Pause Frame */ +#define FIFO_CFG5_UO BIT(12) /* Unsupported Opcode */ +#define FIFO_CFG5_VT BIT(13) /* VLAN tag detected */ +#define FIFO_CFG5_LE BIT(14) /* Long Event */ +#define FIFO_CFG5_FT BIT(15) /* Frame Truncated */ +#define FIFO_CFG5_16 BIT(16) /* unknown */ +#define FIFO_CFG5_17 BIT(17) /* unknown */ +#define FIFO_CFG5_SF BIT(18) /* Short Frame */ +#define FIFO_CFG5_BM BIT(19) /* Byte Mode */ + +#define AG71XX_INT_TX_PS BIT(0) +#define AG71XX_INT_TX_UR BIT(1) +#define AG71XX_INT_TX_BE BIT(3) +#define AG71XX_INT_RX_PR BIT(4) +#define AG71XX_INT_RX_OF BIT(6) +#define AG71XX_INT_RX_BE BIT(7) + +#define MAC_IFCTL_SPEED BIT(16) + +#define MII_CFG_CLK_DIV_4 0 +#define MII_CFG_CLK_DIV_6 2 +#define MII_CFG_CLK_DIV_8 3 +#define MII_CFG_CLK_DIV_10 4 +#define MII_CFG_CLK_DIV_14 5 +#define MII_CFG_CLK_DIV_20 6 +#define MII_CFG_CLK_DIV_28 7 +#define MII_CFG_CLK_DIV_34 8 +#define MII_CFG_CLK_DIV_42 9 +#define MII_CFG_CLK_DIV_50 10 +#define MII_CFG_CLK_DIV_58 11 +#define MII_CFG_CLK_DIV_66 12 +#define MII_CFG_CLK_DIV_74 13 +#define MII_CFG_CLK_DIV_82 14 +#define MII_CFG_CLK_DIV_98 15 +#define MII_CFG_RESET BIT(31) + +#define MII_CMD_WRITE 0x0 +#define MII_CMD_READ 0x1 +#define MII_ADDR_SHIFT 8 +#define MII_IND_BUSY BIT(0) +#define MII_IND_INVALID BIT(2) + +#define TX_CTRL_TXE BIT(0) /* Tx Enable */ + +#define TX_STATUS_PS BIT(0) /* Packet Sent */ +#define TX_STATUS_UR BIT(1) /* Tx Underrun */ +#define TX_STATUS_BE BIT(3) /* Bus Error */ + +#define RX_CTRL_RXE BIT(0) /* Rx Enable */ + +#define RX_STATUS_PR BIT(0) /* Packet Received */ +#define RX_STATUS_OF BIT(2) /* Rx Overflow */ +#define RX_STATUS_BE BIT(3) /* Bus Error */ + +/* + * GMAC register macros + */ +#define AG71XX_ETH_CFG_RGMII_GE0 (1<<0) +#define AG71XX_ETH_CFG_MII_GE0_SLAVE (1<<4) + +enum ag71xx_type { + AG71XX_TYPE_AR9331_GE0, + AG71XX_TYPE_AR9344_GMAC0, +}; + +/* + * h/w descriptor + */ +typedef struct { + uint32_t pkt_start_addr; + + uint32_t is_empty : 1; + uint32_t res1 : 10; + uint32_t ftpp_override : 5; + uint32_t res2 : 4; + uint32_t pkt_size : 12; + + uint32_t next_desc ; +} ag7240_desc_t; + +#define NO_OF_TX_FIFOS 8 +#define NO_OF_RX_FIFOS 8 +#define TX_RING_SZ (NO_OF_TX_FIFOS * sizeof(ag7240_desc_t)) +#define MAX_RBUFF_SZ 0x600 /* 1518 rounded up */ + +#define MAX_WAIT 1000 + +struct ag71xx { + struct device_d *dev; + struct eth_device netdev; + void __iomem *regs; + void __iomem *regs_gmac; + struct mii_bus miibus; + const struct ag71xx_cfg *cfg; + + void *rx_buffer; + + unsigned char *rx_pkt[NO_OF_RX_FIFOS]; + ag7240_desc_t *fifo_tx; + ag7240_desc_t *fifo_rx; + dma_addr_t addr_tx; + dma_addr_t addr_rx; + + int next_tx; + int next_rx; +}; + +struct ag71xx_cfg { + enum ag71xx_type type; + void (*init_mii)(struct ag71xx *priv); +}; + +static inline void ag71xx_check_reg_offset(struct ag71xx *priv, int reg) +{ + switch (reg) { + case AG71XX_REG_MAC_CFG1 ... AG71XX_REG_MAC_MFL: + case AG71XX_REG_MAC_IFCTL ... AG71XX_REG_TX_SM: + case AG71XX_REG_MII_CFG ... AG71XX_REG_MII_IND: + break; + + default: + BUG(); + } +} + +static inline u32 ar7240_reg_rd(u32 reg) +{ + return __raw_readl(KSEG1ADDR(reg)); +} + +static inline void ar7240_reg_wr(u32 reg, u32 val) +{ + __raw_writel(val, KSEG1ADDR(reg)); +} + +static inline u32 ag71xx_gmac_rr(struct ag71xx *dev, int reg) +{ + return __raw_readl(dev->regs_gmac + reg); +} + +static inline void ag71xx_gmac_wr(struct ag71xx *dev, int reg, u32 val) +{ + __raw_writel(val, dev->regs_gmac + reg); +} + +static inline u32 ag71xx_rr(struct ag71xx *priv, int reg) +{ + ag71xx_check_reg_offset(priv, reg); + + return __raw_readl(priv->regs + reg); +} + +static inline void ag71xx_wr(struct ag71xx *priv, int reg, u32 val) +{ + ag71xx_check_reg_offset(priv, reg); + + __raw_writel(val, priv->regs + reg); + /* flush write */ + (void)__raw_readl(priv->regs + reg); +} + + +static int ag71xx_mii_wait(struct ag71xx *priv, int write) +{ + struct device_d *dev = priv->dev; + uint64_t start; + + start = get_time_ns(); + while (ag71xx_rr(priv, AG71XX_REG_MII_IND) & MII_IND_BUSY) { + if (!is_timeout_non_interruptible(start, 100 * USECOND)) + continue; + + dev_err(dev, "mii %s error: bus is still busy!\n", + write ? "write" : "read"); + return -ETIMEDOUT; + } + + return 0; +} + +static int ag71xx_ether_mii_read(struct mii_bus *miidev, int phy_addr, int reg) +{ + struct ag71xx *priv = miidev->priv; + const struct ag71xx_cfg *cfg = priv->cfg; + u16 addr = (phy_addr << MII_ADDR_SHIFT) | reg, val; + int ret; + + if (AG71XX_TYPE_AR9331_GE0 == cfg->type) + return 0xffff; + + ret = ag71xx_mii_wait(priv, 0); + if (ret) + return ret; + + ag71xx_wr(priv, AG71XX_REG_MII_CMD, MII_CMD_WRITE); + ag71xx_wr(priv, AG71XX_REG_MII_ADDR, addr); + ag71xx_wr(priv, AG71XX_REG_MII_CMD, MII_CMD_READ); + + ret = ag71xx_mii_wait(priv, 0); + if (ret) + return ret; + + val = ag71xx_rr(priv, AG71XX_REG_MII_STATUS); + ag71xx_wr(priv, AG71XX_REG_MII_CMD, MII_CMD_WRITE); + + return val; +} + +static int ag71xx_ether_mii_write(struct mii_bus *miidev, int phy_addr, + int reg, u16 val) +{ + struct ag71xx *priv = miidev->priv; + const struct ag71xx_cfg *cfg = priv->cfg; + u16 addr = (phy_addr << MII_ADDR_SHIFT) | reg; + int ret; + + if (AG71XX_TYPE_AR9331_GE0 == cfg->type) + return 0; + + ret = ag71xx_mii_wait(priv, 1); + if (ret) + return ret; + + ag71xx_wr(priv, AG71XX_REG_MII_ADDR, addr); + ag71xx_wr(priv, AG71XX_REG_MII_CTRL, val); + + ret = ag71xx_mii_wait(priv, 1); + if (ret) + return ret; + + return 0; +} + +static int ag71xx_ether_set_ethaddr(struct eth_device *edev, + const unsigned char *adr) +{ + return 0; +} + +static int ag71xx_ether_get_ethaddr(struct eth_device *edev, unsigned char *adr) +{ + /* We have no eeprom */ + return -ENODEV; +} + +static void ag71xx_ether_halt(struct eth_device *edev) +{ + struct ag71xx *priv = edev->priv; + struct device_d *dev = priv->dev; + uint64_t start; + + ag71xx_wr(priv, AG71XX_REG_RX_CTRL, 0); + start = get_time_ns(); + while (ag71xx_rr(priv, AG71XX_REG_RX_CTRL)) { + if (is_timeout_non_interruptible(start, 100 * USECOND)) { + dev_err(dev, "error: failed to stop device!\n"); + break; + } + } +} + +static int ag71xx_ether_rx(struct eth_device *edev) +{ + struct ag71xx *priv = edev->priv; + ag7240_desc_t *f; + unsigned int work_done; + + for (work_done = 0; work_done < NO_OF_RX_FIFOS; work_done++) { + unsigned int pktlen; + unsigned char *rx_pkt; + + f = &priv->fifo_rx[priv->next_rx]; + + if (f->is_empty) + break; + + pktlen = f->pkt_size; + rx_pkt = priv->rx_pkt[priv->next_rx]; + + /* invalidate */ + dma_sync_single_for_cpu((unsigned long)rx_pkt, pktlen, + DMA_FROM_DEVICE); + + net_receive(edev, rx_pkt, pktlen - 4); + + f->is_empty = 1; + + priv->next_rx = (priv->next_rx + 1) % NO_OF_RX_FIFOS; + } + + if (!(ag71xx_rr(priv, AG71XX_REG_RX_CTRL) & RX_CTRL_RXE)) { + f = &priv->fifo_rx[priv->next_rx]; + ag71xx_wr(priv, AG71XX_REG_RX_DESC, virt_to_phys(f)); + ag71xx_wr(priv, AG71XX_REG_RX_CTRL, RX_CTRL_RXE); + } + + return work_done; +} + +static int ag71xx_ether_send(struct eth_device *edev, void *packet, int length) +{ + struct ag71xx *priv = edev->priv; + struct device_d *dev = priv->dev; + ag7240_desc_t *f = &priv->fifo_tx[priv->next_tx]; + uint64_t start; + int ret = 0; + + /* flush */ + dma_sync_single_for_device((unsigned long)packet, length, DMA_TO_DEVICE); + + f->pkt_start_addr = virt_to_phys(packet); + f->res1 = 0; + f->pkt_size = length; + f->is_empty = 0; + ag71xx_wr(priv, AG71XX_REG_TX_DESC, virt_to_phys(f)); + ag71xx_wr(priv, AG71XX_REG_TX_CTRL, TX_CTRL_TXE); + + /* flush again?! */ + dma_sync_single_for_cpu((unsigned long)packet, length, DMA_TO_DEVICE); + + start = get_time_ns(); + while (!f->is_empty) { + if (!is_timeout_non_interruptible(start, 100 * USECOND)) + continue; + + dev_err(dev, "error: tx timed out\n"); + ret = -ETIMEDOUT; + break; + } + + f->pkt_start_addr = 0; + f->pkt_size = 0; + + priv->next_tx = (priv->next_tx + 1) % NO_OF_TX_FIFOS; + + return ret; +} + +static int ag71xx_ether_open(struct eth_device *edev) +{ + struct ag71xx *priv = edev->priv; + const struct ag71xx_cfg *cfg = priv->cfg; + + if (AG71XX_TYPE_AR9344_GMAC0 == cfg->type) + return phy_device_connect(edev, &priv->miibus, 0, + NULL, 0, PHY_INTERFACE_MODE_RGMII_TXID); + + return 0; +} + +static int ag71xx_ether_init(struct eth_device *edev) +{ + struct ag71xx *priv = edev->priv; + int i; + void *rxbuf = priv->rx_buffer; + + priv->next_rx = 0; + + for (i = 0; i < NO_OF_RX_FIFOS; i++) { + ag7240_desc_t *fr = &priv->fifo_rx[i]; + + priv->rx_pkt[i] = rxbuf; + fr->pkt_start_addr = virt_to_phys(rxbuf); + fr->pkt_size = MAX_RBUFF_SZ; + fr->is_empty = 1; + fr->next_desc = virt_to_phys(&priv->fifo_rx[(i + 1) % NO_OF_RX_FIFOS]); + + /* invalidate */ + dma_sync_single_for_device((unsigned long)rxbuf, MAX_RBUFF_SZ, + DMA_FROM_DEVICE); + + rxbuf += MAX_RBUFF_SZ; + } + + /* Clean Tx BD's */ + memset(priv->fifo_tx, 0, TX_RING_SZ); + + ag71xx_wr(priv, AG71XX_REG_RX_DESC, virt_to_phys(priv->fifo_rx)); + ag71xx_wr(priv, AG71XX_REG_RX_CTRL, RX_CTRL_RXE); + + return 0; +} + +static void ag71xx_ar9331_ge0_mii_init(struct ag71xx *priv) +{ + u32 rd; + + rd = ag71xx_rr(priv, AG71XX_REG_MAC_CFG2); + rd |= (MAC_CFG2_PAD_CRC_EN | MAC_CFG2_LEN_CHECK | MAC_CFG2_IF_10_100); + ag71xx_wr(priv, AG71XX_REG_MAC_CFG2, rd); + + /* config FIFOs */ + ag71xx_wr(priv, AG71XX_REG_FIFO_CFG0, 0x1f00); + + rd = ag71xx_gmac_rr(priv, AG71XX_REG_MAC_CFG1); + rd |= AG71XX_ETH_CFG_MII_GE0_SLAVE; + ag71xx_gmac_wr(priv, 0, rd); +} + +static void ag71xx_ar9344_gmac0_mii_init(struct ag71xx *priv) +{ + u32 rd; + + rd = ag71xx_rr(priv, AG71XX_REG_MAC_CFG2); + rd |= (MAC_CFG2_PAD_CRC_EN | MAC_CFG2_LEN_CHECK | MAC_CFG2_IF_1000); + ag71xx_wr(priv, AG71XX_REG_MAC_CFG2, rd); + + /* config FIFOs */ + ag71xx_wr(priv, AG71XX_REG_FIFO_CFG0, 0x1f00); + + ag71xx_gmac_wr(priv, AG71XX_REG_MAC_CFG1, 1); + udelay(1000); + ag71xx_wr(priv, AG71XX_REG_MII_CFG, 4 | (1 << 31)); + ag71xx_wr(priv, AG71XX_REG_MII_CFG, 4); +} + +static struct ag71xx_cfg ag71xx_cfg_ar9331_ge0 = { + .type = AG71XX_TYPE_AR9331_GE0, + .init_mii = ag71xx_ar9331_ge0_mii_init, +}; + +static struct ag71xx_cfg ag71xx_cfg_ar9344_gmac0 = { + .type = AG71XX_TYPE_AR9344_GMAC0, + .init_mii = ag71xx_ar9344_gmac0_mii_init, +}; + +static int ag71xx_probe(struct device_d *dev) +{ + void __iomem *regs, *regs_gmac; + struct mii_bus *miibus; + struct eth_device *edev; + struct ag71xx_cfg *cfg; + struct ag71xx *priv; + u32 mac_h, mac_l; + u32 rd, mask; + int ret; + + ret = dev_get_drvdata(dev, (const void **)&cfg); + if (ret) + return ret; + + regs_gmac = dev_request_mem_region_by_name(dev, "gmac"); + if (IS_ERR(regs_gmac)) + return PTR_ERR(regs_gmac); + + regs = dev_request_mem_region_by_name(dev, "ge0"); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + priv = xzalloc(sizeof(struct ag71xx)); + edev = &priv->netdev; + miibus = &priv->miibus; + edev->priv = priv; + + edev->init = ag71xx_ether_init; + edev->open = ag71xx_ether_open; + edev->send = ag71xx_ether_send; + edev->recv = ag71xx_ether_rx; + edev->halt = ag71xx_ether_halt; + edev->get_ethaddr = ag71xx_ether_get_ethaddr; + edev->set_ethaddr = ag71xx_ether_set_ethaddr; + + priv->dev = dev; + priv->regs = regs; + priv->regs_gmac = regs_gmac; + priv->cfg = cfg; + + miibus->read = ag71xx_ether_mii_read; + miibus->write = ag71xx_ether_mii_write; + miibus->priv = priv; + + /* enable switch core */ + rd = ar7240_reg_rd(AR71XX_PLL_BASE + AR933X_ETHSW_CLOCK_CONTROL_REG); + rd &= ~(0x1f); + rd |= 0x10; + if ((ar7240_reg_rd(WASP_BOOTSTRAP_REG) & WASP_REF_CLK_25) == 0) + rd |= 0x1; + ar7240_reg_wr((AR71XX_PLL_BASE + AR933X_ETHSW_CLOCK_CONTROL_REG), rd); + + if (ath79_reset_rr(AR933X_RESET_REG_RESET_MODULE) != 0) + ath79_reset_wr(AR933X_RESET_REG_RESET_MODULE, 0); + + /* reset GE0 MAC and MDIO */ + mask = AR933X_RESET_GE0_MAC | AR933X_RESET_GE0_MDIO + | AR933X_RESET_SWITCH; + + rd = ath79_reset_rr(AR933X_RESET_REG_RESET_MODULE); + rd |= mask; + ath79_reset_wr(AR933X_RESET_REG_RESET_MODULE, rd); + mdelay(100); + + rd = ath79_reset_rr(AR933X_RESET_REG_RESET_MODULE); + rd &= ~(mask); + ath79_reset_wr(AR933X_RESET_REG_RESET_MODULE, rd); + mdelay(100); + + ag71xx_wr(priv, AG71XX_REG_MAC_CFG1, + (MAC_CFG1_SR | MAC_CFG1_TX_RST | MAC_CFG1_RX_RST)); + ag71xx_wr(priv, AG71XX_REG_MAC_CFG1, + (MAC_CFG1_RXE | MAC_CFG1_TXE)); + + if (cfg->init_mii) + cfg->init_mii(priv); + + ag71xx_wr(priv, AG71XX_REG_FIFO_CFG1, 0x10ffff); + ag71xx_wr(priv, AG71XX_REG_FIFO_CFG2, 0xaaa0555); + + ag71xx_wr(priv, AG71XX_REG_FIFO_CFG4, 0x3ffff); + /* bit 19 should be set to 1 for GE0 */ + ag71xx_wr(priv, AG71XX_REG_FIFO_CFG5, (0x66b82) | (1 << 19)); + ag71xx_wr(priv, AG71XX_REG_FIFO_CFG3, 0x1f00140); + + priv->rx_buffer = xmemalign(PAGE_SIZE, NO_OF_RX_FIFOS * MAX_RBUFF_SZ); + priv->fifo_tx = dma_alloc_coherent(NO_OF_TX_FIFOS * sizeof(ag7240_desc_t), + &priv->addr_tx); + priv->fifo_rx = dma_alloc_coherent(NO_OF_RX_FIFOS * sizeof(ag7240_desc_t), + &priv->addr_rx); + priv->next_tx = 0; + + mac_l = 0x3344; + mac_h = 0x0004d980; + + ag71xx_wr(priv, AG71XX_REG_MAC_ADDR1, mac_l); + ag71xx_wr(priv, AG71XX_REG_MAC_ADDR2, mac_h); + + mdiobus_register(miibus); + eth_register(edev); + + dev_info(dev, "network device registered\n"); + + return 0; +} + +static __maybe_unused struct of_device_id ag71xx_dt_ids[] = { + { .compatible = "qca,ar9331-ge0", .data = &ag71xx_cfg_ar9331_ge0, }, + { .compatible = "qca,ar9344-gmac0", .data = &ag71xx_cfg_ar9344_gmac0, }, + { /* sentinel */ } +}; + +static struct driver_d ag71xx_driver = { + .name = "ag71xx-gmac", + .probe = ag71xx_probe, + .of_compatible = DRV_OF_COMPAT(ag71xx_dt_ids), +}; +device_platform_driver(ag71xx_driver); -- cgit v1.2.3 From 4864aa95176288ae6893f7f6075c23bbb3f92341 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Thu, 14 Sep 2017 10:25:01 +0200 Subject: add ar9331.dtsi and enable mac0 Signed-off-by: Oleksij Rempel Signed-off-by: Sascha Hauer --- arch/mips/dts/ar9331.dtsi | 12 ++++++++++++ arch/mips/dts/tplink-mr3020.dts | 5 +++++ 2 files changed, 17 insertions(+) create mode 100644 arch/mips/dts/ar9331.dtsi diff --git a/arch/mips/dts/ar9331.dtsi b/arch/mips/dts/ar9331.dtsi new file mode 100644 index 0000000000..b4b8b766b9 --- /dev/null +++ b/arch/mips/dts/ar9331.dtsi @@ -0,0 +1,12 @@ +/ { + ahb { + mac0: mac@19000000 { + compatible = "qca,ar9331-ge0"; + reg = <0x18070000 0x00000100>, + <0x19000000 0x01000000>; + reg-names = "gmac", "ge0"; + phy-mode = "mii"; + status = "disabled"; + }; + }; +}; diff --git a/arch/mips/dts/tplink-mr3020.dts b/arch/mips/dts/tplink-mr3020.dts index eaae11eddf..6f1ad13504 100644 --- a/arch/mips/dts/tplink-mr3020.dts +++ b/arch/mips/dts/tplink-mr3020.dts @@ -1,4 +1,5 @@ #include +#include "ar9331.dtsi" / { aliases { @@ -24,3 +25,7 @@ reg = <0x80000 0x10000>; }; }; + +&mac0 { + status = "okay"; +}; -- cgit v1.2.3 From 6877d4fb296ddb239462353224bd7ce8abddfbd4 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Thu, 14 Sep 2017 10:25:02 +0200 Subject: MIPS: dts: ar9344: add mac0 node this node is supported by ag71xx driver Signed-off-by: Oleksij Rempel Signed-off-by: Sascha Hauer --- arch/mips/dts/ar9344.dtsi | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/arch/mips/dts/ar9344.dtsi b/arch/mips/dts/ar9344.dtsi index 0838e8d7f7..8587511124 100644 --- a/arch/mips/dts/ar9344.dtsi +++ b/arch/mips/dts/ar9344.dtsi @@ -40,6 +40,16 @@ status = "disabled"; }; + mac0: mac@19000000 { + compatible = "qca,ar9344-gmac0"; + reg = <0x18070000 0x00000100>, + <0x19000000 0x01000000>; + reg-names = "gmac", "ge0"; + phy-mode = "rgmii"; + + status = "disabled"; + }; + spi: spi@1f000000 { compatible = "qca,ar7100-spi", "qca,ar9344-spi"; reg = <0x1f000000 0x1c>; -- cgit v1.2.3 From 2cc166f00cc400635a0d0cf1b4421804323c483b Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Thu, 14 Sep 2017 10:25:03 +0200 Subject: MIPS: dts: tl_wdr4300: enable mac0 Signed-off-by: Oleksij Rempel Signed-off-by: Sascha Hauer --- arch/mips/dts/ar9344_tl_wdr4300.dts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/mips/dts/ar9344_tl_wdr4300.dts b/arch/mips/dts/ar9344_tl_wdr4300.dts index b02c1d7307..5a4a6f3798 100644 --- a/arch/mips/dts/ar9344_tl_wdr4300.dts +++ b/arch/mips/dts/ar9344_tl_wdr4300.dts @@ -61,3 +61,7 @@ }; }; }; + +&mac0 { + status = "okay"; +}; -- cgit v1.2.3 From 6f618cdf4cc215dbea03d01fdd152b42482f84fc Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Thu, 14 Sep 2017 10:25:04 +0200 Subject: MIPS: dts: tl_wdr4300: add alias for spiflash to unify the naming with TP-Link TL-MR3020. Signed-off-by: Oleksij Rempel Signed-off-by: Sascha Hauer --- arch/mips/dts/ar9344_tl_wdr4300.dts | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/mips/dts/ar9344_tl_wdr4300.dts b/arch/mips/dts/ar9344_tl_wdr4300.dts index 5a4a6f3798..c784e6cb46 100644 --- a/arch/mips/dts/ar9344_tl_wdr4300.dts +++ b/arch/mips/dts/ar9344_tl_wdr4300.dts @@ -11,6 +11,7 @@ aliases { serial0 = &uart0; + spiflash = &spiflash; }; memory@0 { -- cgit v1.2.3 From 130fc67897ba03b3ca847c5204764e4f5bed95a4 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Thu, 14 Sep 2017 10:25:05 +0200 Subject: MIPS: dts: tl_wdr4300: remove RO flag from barebox partition we need it RW for barebox updates. Signed-off-by: Oleksij Rempel Signed-off-by: Sascha Hauer --- arch/mips/dts/ar9344_tl_wdr4300.dts | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/mips/dts/ar9344_tl_wdr4300.dts b/arch/mips/dts/ar9344_tl_wdr4300.dts index c784e6cb46..139717a6b4 100644 --- a/arch/mips/dts/ar9344_tl_wdr4300.dts +++ b/arch/mips/dts/ar9344_tl_wdr4300.dts @@ -53,7 +53,6 @@ partition@0 { label = "barebox"; reg = <0 0x80000>; - read-only; }; partition@80000 { -- cgit v1.2.3 From e9edd1ad49eda3bf1c7f973480593baf9c739ecb Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Thu, 14 Sep 2017 10:25:06 +0200 Subject: net: phy: add ar8327 driver The Atheros ar8327 is a seven port gigabit ethernet switch. This patch adds a driver for the phys in it. Signed-off-by: Oleksij Rempel Signed-off-by: Sascha Hauer --- drivers/net/phy/Kconfig | 5 + drivers/net/phy/Makefile | 1 + drivers/net/phy/ar8327.c | 280 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 286 insertions(+) create mode 100644 drivers/net/phy/ar8327.c diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index d30f65b8e6..ea2e062656 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -8,6 +8,11 @@ if PHYLIB comment "MII PHY device drivers" +config AR8327N_PHY + bool "Driver for QCA AR8327N PHYs" + ---help--- + Currently supports the AR8327N PHY. + config AT803X_PHY bool "Driver for Atheros AT803X PHYs" ---help--- diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index 10732f8070..13b8f6545d 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -1,4 +1,5 @@ obj-y += phy.o mdio_bus.o +obj-$(CONFIG_AR8327N_PHY) += ar8327.o obj-$(CONFIG_AT803X_PHY) += at803x.o obj-$(CONFIG_LXT_PHY) += lxt.o obj-$(CONFIG_MARVELL_PHY) += marvell.o diff --git a/drivers/net/phy/ar8327.c b/drivers/net/phy/ar8327.c new file mode 100644 index 0000000000..a0b10021dd --- /dev/null +++ b/drivers/net/phy/ar8327.c @@ -0,0 +1,280 @@ +/* + * Copyright (C) 2017 Oleksij Rempel + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include + +#define ATHR_PHY_MAX 5 + +/*****************/ +/* PHY Registers */ +/*****************/ +#define ATHR_PHY_CONTROL 0x00 +#define ATHR_PHY_STATUS 0x01 +#define ATHR_PHY_ID1 0x02 +#define ATHR_PHY_ID2 0x03 +#define ATHR_AUTONEG_ADVERT 0x04 +#define ATHR_LINK_PARTNER_ABILITY 0x05 +#define ATHR_AUTONEG_EXPANSION 0x06 +#define ATHR_NEXT_PAGE_TRANSMIT 0x07 +#define ATHR_LINK_PARTNER_NEXT_PAGE 0x08 +#define ATHR_1000BASET_CONTROL 0x09 +#define ATHR_1000BASET_STATUS 0x0a +#define ATHR_PHY_SPEC_CONTROL 0x10 +#define ATHR_PHY_SPEC_STATUS 0x11 +#define ATHR_DEBUG_PORT_ADDRESS 0x1d +#define ATHR_DEBUG_PORT_DATA 0x1e + +/* Advertisement register. */ +#define ATHR_ADVERTISE_ASYM_PAUSE 0x0800 +#define ATHR_ADVERTISE_PAUSE 0x0400 +#define ATHR_ADVERTISE_100FULL 0x0100 +#define ATHR_ADVERTISE_100HALF 0x0080 +#define ATHR_ADVERTISE_10FULL 0x0040 +#define ATHR_ADVERTISE_10HALF 0x0020 + +#define ATHR_ADVERTISE_ALL (ATHR_ADVERTISE_ASYM_PAUSE | ATHR_ADVERTISE_PAUSE | \ + ATHR_ADVERTISE_10HALF | ATHR_ADVERTISE_10FULL | \ + ATHR_ADVERTISE_100HALF | ATHR_ADVERTISE_100FULL) + +/* ATHR_PHY_CONTROL fields */ +#define ATHR_CTRL_SOFTWARE_RESET 0x8000 +#define ATHR_CTRL_AUTONEGOTIATION_ENABLE 0x1000 + +/* 1000BASET_CONTROL */ +#define ATHR_ADVERTISE_1000FULL 0x0200 + +/* Phy Specific status fields */ +#define ATHR_STATUS_LINK_PASS 0x0400 + +static u32 ar8327n_reg_read(struct phy_device *phydev, u32 reg_addr) +{ + u32 reg_word_addr; + u32 phy_addr, tmp_val, reg_val; + u16 phy_val; + u8 phy_reg; + + /* change reg_addr to 16-bit word address, 32-bit aligned */ + reg_word_addr = (reg_addr & 0xfffffffc) >> 1; + + /* configure register high address */ + phy_addr = 0x18; + phy_reg = 0x0; + phy_val = (u16) ((reg_word_addr >> 8) & 0x1ff); /* bit16-8 of reg address */ + mdiobus_write(phydev->bus, phy_addr, phy_reg, phy_val); + + /* For some registers such as MIBs, since it is read/clear, we should */ + /* read the lower 16-bit register then the higher one */ + + /* read register in lower address */ + phy_addr = 0x10 | ((reg_word_addr >> 5) & 0x7); /* bit7-5 of reg address */ + phy_reg = (u8) (reg_word_addr & 0x1f); /* bit4-0 of reg address */ + reg_val = (u32) mdiobus_read(phydev->bus, phy_addr, phy_reg); + + /* read register in higher address */ + reg_word_addr++; + phy_addr = 0x10 | ((reg_word_addr >> 5) & 0x7); /* bit7-5 of reg address */ + phy_reg = (u8) (reg_word_addr & 0x1f); /* bit4-0 of reg address */ + reg_val = (u32) mdiobus_read(phydev->bus, phy_addr, phy_reg); + reg_val |= (tmp_val << 16); + + return reg_val; +} + +static void ar8327n_reg_write(struct phy_device *phydev, u32 reg_addr, + u32 reg_val) +{ + u32 reg_word_addr; + u32 phy_addr; + u16 phy_val; + u8 phy_reg; + + /* change reg_addr to 16-bit word address, 32-bit aligned */ + reg_word_addr = (reg_addr & 0xfffffffc) >> 1; + + /* configure register high address */ + phy_addr = 0x18; + phy_reg = 0x0; + phy_val = (u16) ((reg_word_addr >> 8) & 0x1ff); /* bit16-8 of reg address */ + mdiobus_write(phydev->bus, phy_addr, phy_reg, phy_val); + + /* For some registers such as ARL and VLAN, since they include BUSY bit */ + /* in lower address, we should write the higher 16-bit register then the */ + /* lower one */ + + /* read register in higher address */ + reg_word_addr++; + phy_addr = 0x10 | ((reg_word_addr >> 5) & 0x7); /* bit7-5 of reg address */ + phy_reg = (u8) (reg_word_addr & 0x1f); /* bit4-0 of reg address */ + phy_val = (u16) ((reg_val >> 16) & 0xffff); + mdiobus_write(phydev->bus, phy_addr, phy_reg, phy_val); + + /* write register in lower address */ + reg_word_addr--; + phy_addr = 0x10 | ((reg_word_addr >> 5) & 0x7); /* bit7-5 of reg address */ + phy_reg = (u8) (reg_word_addr & 0x1f); /* bit4-0 of reg address */ + phy_val = (u16) (reg_val & 0xffff); + mdiobus_write(phydev->bus, phy_addr, phy_reg, phy_val); +} + +static int ar8327n_phy_is_link_alive(struct phy_device *phydev, int phy_addr) +{ + u16 val; + + val = mdiobus_read(phydev->bus, phy_addr, ATHR_PHY_SPEC_STATUS); + + return !!(val & ATHR_STATUS_LINK_PASS); +} + +static int ar8327n_phy_setup(struct phy_device *phydev) +{ + struct device_d *dev = &phydev->dev; + int phy_addr; + + /* start auto negotiation on each phy */ + for (phy_addr = 0; phy_addr < ATHR_PHY_MAX; phy_addr++) { + mdiobus_write(phydev->bus, phy_addr, ATHR_AUTONEG_ADVERT, + ATHR_ADVERTISE_ALL); + + mdiobus_write(phydev->bus, phy_addr, ATHR_1000BASET_CONTROL, + ATHR_ADVERTISE_1000FULL); + + /* Reset PHYs*/ + mdiobus_write(phydev->bus, phy_addr, ATHR_PHY_CONTROL, + ATHR_CTRL_AUTONEGOTIATION_ENABLE + | ATHR_CTRL_SOFTWARE_RESET); + } + + /* + * After the phy is reset, it takes a little while before + * it can respond properly. + */ + mdelay(1000); + + for (phy_addr = 0; phy_addr < ATHR_PHY_MAX; phy_addr++) { + int count; + + for (count = 20; count > 0; count--) { + u16 val; + val = mdiobus_read(phydev->bus, phy_addr, + ATHR_PHY_CONTROL); + + if (!(val & ATHR_CTRL_SOFTWARE_RESET)) + break; + + mdelay(150); + } + + if (!count) { + dev_err(dev, "error: port %d, negotiation timeout.\n", + phy_addr); + return -ETIMEDOUT; + } + } + + return 0; +} + +static int ar8327n_get_link(struct phy_device *phydev) +{ + int phy_addr; + int live_links = 0; + + for (phy_addr = 0; phy_addr < ATHR_PHY_MAX; phy_addr++) { + if (ar8327n_phy_is_link_alive(phydev, phy_addr)) + live_links++; + } + + return (live_links > 0); +} + +static int ar8327n_config_init(struct phy_device *phydev) +{ + struct device_d *dev = &phydev->dev; + int phy_addr = 0; + + if (phydev->interface != PHY_INTERFACE_MODE_RGMII_TXID) + return 0; + + /* if using header for register configuration, we have to */ + /* configure s17 register after frame transmission is enabled */ + + /* configure the RGMII */ + ar8327n_reg_write(phydev, 0x624, 0x7f7f7f7f); + ar8327n_reg_write(phydev, 0x10, 0x40000000); + ar8327n_reg_write(phydev, 0x4, 0x07600000); + ar8327n_reg_write(phydev, 0xc, 0x01000000); + ar8327n_reg_write(phydev, 0x7c, 0x0000007e); + + /* AR8327/AR8328 v1.0 fixup */ + if ((ar8327n_reg_read(phydev, 0x0) & 0xffff) == 0x1201) { + dev_warn(dev, "warning: untested device. PHY v1.0\n"); + for (phy_addr = 0x0; phy_addr <= ATHR_PHY_MAX; phy_addr++) { + /* For 100M waveform */ + mdiobus_write(phydev->bus, phy_addr, 0x1d, 0x0); + mdiobus_write(phydev->bus, phy_addr, 0x1e, 0x02ea); + /* Turn On Gigabit Clock */ + mdiobus_write(phydev->bus, phy_addr, 0x1d, 0x3d); + mdiobus_write(phydev->bus, phy_addr, 0x1e, 0x68a0); + } + } + + /* + * set the WAN Port(Port1) Disable Mode so + * it can not receive or transmit any frames. + */ + ar8327n_reg_write(phydev, 0x066c, + ar8327n_reg_read(phydev, 0x066c) & 0xfff8ffff); + + ar8327n_phy_setup(phydev); + + return 0; +} + +static int ar8327n_read_status(struct phy_device *phydev) +{ + /* for GMAC0 we have only one static mode */ + phydev->speed = SPEED_1000; + phydev->duplex = DUPLEX_FULL; + phydev->pause = phydev->asym_pause = 0; + phydev->link = ar8327n_get_link(phydev); + return 0; +} + +static int ar8327n_config_aneg(struct phy_device *phydev) +{ + return 0; +} + +static int ar8327n_aneg_done(struct phy_device *phydev) +{ + return BMSR_ANEGCOMPLETE; +} + +static struct phy_driver ar8327n_driver[] = { +{ + /* QCA AR8327N */ + .phy_id = 0x004dd034, + .phy_id_mask = 0xffffffef, + .drv.name = "QCA AR8327N switch", + .config_init = ar8327n_config_init, + .features = PHY_GBIT_FEATURES, + .config_aneg = &ar8327n_config_aneg, + .read_status = &ar8327n_read_status, + .aneg_done = &ar8327n_aneg_done, +}}; + +static int atheros_phy_init(void) +{ + return phy_drivers_register(ar8327n_driver, + ARRAY_SIZE(ar8327n_driver)); +} +fs_initcall(atheros_phy_init); -- cgit v1.2.3 From a93762eb31b6ccbdcc7915e694548a065c50bcbc Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Thu, 14 Sep 2017 10:25:07 +0200 Subject: MIPS: dts: tl_wdr4300: rename it to tl_wdr4300 v1.7 The minor version of TP-Link are usually big enough that it need extra vendor partition on the flash with additional configurations like PLL, CPU and RAM freqs. Visually I was able to confirm at least different SPI Flash and RAM chips. Signed-off-by: Oleksij Rempel Signed-off-by: Sascha Hauer --- arch/mips/configs/tplink-wdr4300_defconfig | 2 +- arch/mips/dts/ar9344-tl-wdr4300-v1.7.dts | 67 ++++++++++++++++++++++++++++++ arch/mips/dts/ar9344_tl_wdr4300.dts | 67 ------------------------------ 3 files changed, 68 insertions(+), 68 deletions(-) create mode 100644 arch/mips/dts/ar9344-tl-wdr4300-v1.7.dts delete mode 100644 arch/mips/dts/ar9344_tl_wdr4300.dts diff --git a/arch/mips/configs/tplink-wdr4300_defconfig b/arch/mips/configs/tplink-wdr4300_defconfig index 63189b7546..b5be221f60 100644 --- a/arch/mips/configs/tplink-wdr4300_defconfig +++ b/arch/mips/configs/tplink-wdr4300_defconfig @@ -1,5 +1,5 @@ CONFIG_BUILTIN_DTB=y -CONFIG_BUILTIN_DTB_NAME="ar9344_tl_wdr4300" +CONFIG_BUILTIN_DTB_NAME="ar9344-tl-wdr4300-v1.7" CONFIG_MACH_MIPS_ATH79=y CONFIG_BOARD_TPLINK_WDR4300=y CONFIG_PBL_IMAGE=y diff --git a/arch/mips/dts/ar9344-tl-wdr4300-v1.7.dts b/arch/mips/dts/ar9344-tl-wdr4300-v1.7.dts new file mode 100644 index 0000000000..d16cab0052 --- /dev/null +++ b/arch/mips/dts/ar9344-tl-wdr4300-v1.7.dts @@ -0,0 +1,67 @@ +/dts-v1/; + +#include +#include + +#include "ar9344.dtsi" + +/ { + model = "TP-Link WDR4300 v1.7"; + compatible = "tplink,tl-wdr4300", "tplink,tl-wdr4300-v1.7"; + + aliases { + serial0 = &uart0; + spiflash = &spiflash; + }; + + memory@0 { + device_type = "memory"; + reg = <0x0 0x8000000>; + }; + + chosen { + stdout-path = &uart0; + + environment@0 { + compatible = "barebox,environment"; + device-path = &spiflash, "partname:barebox-environment"; + }; + }; +}; + +&ref { + clock-frequency = <40000000>; +}; + +&uart0 { + status = "okay"; + clock-frequency = <40000000>; +}; + +&spi { + num-chipselects = <1>; + status = "okay"; + + /* Winbond W25Q64CV SPI flash */ + spiflash: w25q64cv@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "jedec,spi-nor", "winbond,w25q64cv"; + spi-max-frequency = <104000000>; + reg = <0>; + + partition@0 { + label = "barebox"; + reg = <0 0x80000>; + }; + + partition@80000 { + label = "barebox-environment"; + reg = <0x80000 0x10000>; + }; + }; +}; + +&mac0 { + status = "okay"; +}; diff --git a/arch/mips/dts/ar9344_tl_wdr4300.dts b/arch/mips/dts/ar9344_tl_wdr4300.dts deleted file mode 100644 index 139717a6b4..0000000000 --- a/arch/mips/dts/ar9344_tl_wdr4300.dts +++ /dev/null @@ -1,67 +0,0 @@ -/dts-v1/; - -#include -#include - -#include "ar9344.dtsi" - -/ { - model = "TP-Link WDR4300"; - compatible = "tplink,tl-wdr4300"; - - aliases { - serial0 = &uart0; - spiflash = &spiflash; - }; - - memory@0 { - device_type = "memory"; - reg = <0x0 0x8000000>; - }; - - chosen { - stdout-path = &uart0; - - environment@0 { - compatible = "barebox,environment"; - device-path = &spiflash, "partname:barebox-environment"; - }; - }; -}; - -&ref { - clock-frequency = <40000000>; -}; - -&uart0 { - status = "okay"; - clock-frequency = <40000000>; -}; - -&spi { - num-chipselects = <1>; - status = "okay"; - - /* Winbond W25Q64CV SPI flash */ - spiflash: w25q64cv@0 { - #address-cells = <1>; - #size-cells = <1>; - compatible = "jedec,spi-nor", "winbond,w25q64cv"; - spi-max-frequency = <104000000>; - reg = <0>; - - partition@0 { - label = "barebox"; - reg = <0 0x80000>; - }; - - partition@80000 { - label = "barebox-environment"; - reg = <0x80000 0x10000>; - }; - }; -}; - -&mac0 { - status = "okay"; -}; -- cgit v1.2.3 From d7fdf95a89be4bd01a286f7f6c0d379af3a19a3c Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Thu, 14 Sep 2017 10:25:08 +0200 Subject: MIPS: tplink-wdr4300_defconfig: add network support Signed-off-by: Oleksij Rempel Signed-off-by: Sascha Hauer --- arch/mips/configs/tplink-wdr4300_defconfig | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/arch/mips/configs/tplink-wdr4300_defconfig b/arch/mips/configs/tplink-wdr4300_defconfig index b5be221f60..46093d243b 100644 --- a/arch/mips/configs/tplink-wdr4300_defconfig +++ b/arch/mips/configs/tplink-wdr4300_defconfig @@ -32,6 +32,10 @@ CONFIG_CMD_LET=y CONFIG_CMD_MSLEEP=y CONFIG_CMD_READF=y CONFIG_CMD_SLEEP=y +CONFIG_CMD_DHCP=y +CONFIG_CMD_HOST=y +CONFIG_CMD_MIITOOL=y +CONFIG_CMD_PING=y CONFIG_CMD_ECHO_E=y CONFIG_CMD_EDIT=y CONFIG_CMD_READLINE=y @@ -55,15 +59,13 @@ CONFIG_CMD_TIME=y CONFIG_NET=y CONFIG_NET_NFS=y CONFIG_NET_NETCONSOLE=y -CONFIG_NET_RESOLV=y -CONFIG_NET_DHCP=y CONFIG_NET_SNTP=y CONFIG_OFDEVICE=y CONFIG_OF_BAREBOX_DRIVERS=y CONFIG_OF_BAREBOX_ENV_IN_FS=y CONFIG_DRIVER_SERIAL_NS16550=y CONFIG_DRIVER_NET_AG71XX=y -CONFIG_AT803X_PHY=y +CONFIG_AR8327N_PHY=y CONFIG_MDIO_BITBANG=y CONFIG_MDIO_GPIO=y CONFIG_DRIVER_SPI_ATH79=y @@ -74,5 +76,7 @@ CONFIG_LED=y CONFIG_LED_GPIO=y CONFIG_LED_GPIO_OF=y CONFIG_LED_TRIGGERS=y +CONFIG_FS_TFTP=y +CONFIG_FS_NFS=y CONFIG_DIGEST_SHA224_GENERIC=y CONFIG_DIGEST_SHA256_GENERIC=y -- cgit v1.2.3 From f5098c4064372b9e7b4647e9e68ec274a43baccb Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Thu, 14 Sep 2017 10:25:09 +0200 Subject: MIPS: dts: ar9344: add APB bus and move nodes which belong to APB. Signed-off-by: Oleksij Rempel Signed-off-by: Sascha Hauer --- arch/mips/dts/ar9344.dtsi | 42 +++++++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/arch/mips/dts/ar9344.dtsi b/arch/mips/dts/ar9344.dtsi index 8587511124..3e105174d5 100644 --- a/arch/mips/dts/ar9344.dtsi +++ b/arch/mips/dts/ar9344.dtsi @@ -29,15 +29,33 @@ #address-cells = <1>; #size-cells = <1>; - uart0: uart@18020000 { - compatible = "ns16550a", "qca,ar9344-uart0"; - reg = <0x18020000 0x20>; + apb { + compatible = "simple-bus"; + ranges; - reg-shift = <2>; - reg-io-width = <4>; - big-endian; + #address-cells = <1>; + #size-cells = <1>; - status = "disabled"; + uart0: uart@18020000 { + compatible = "ns16550a", "qca,ar9344-uart0"; + reg = <0x18020000 0x20>; + + reg-shift = <2>; + reg-io-width = <4>; + big-endian; + + status = "disabled"; + }; + + spi: spi@1f000000 { + compatible = "qca,ar7100-spi", "qca,ar9344-spi"; + reg = <0x1f000000 0x1c>; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; }; mac0: mac@19000000 { @@ -49,15 +67,5 @@ status = "disabled"; }; - - spi: spi@1f000000 { - compatible = "qca,ar7100-spi", "qca,ar9344-spi"; - reg = <0x1f000000 0x1c>; - - #address-cells = <1>; - #size-cells = <0>; - - status = "disabled"; - }; }; }; -- cgit v1.2.3 From 22e1ba9f3645057586abc866ed0925bd28d5b8d2 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Mon, 18 Sep 2017 18:41:45 +0200 Subject: net: ag71xx: disable eth interface on barebox shutdown If not disable, DMA RX engine will keep to write in to allocated memory area. As soon as some code or data of target OS will be placed to this area it will be overwritten by incoming network package. Signed-off-by: Oleksij Rempel Signed-off-by: Sascha Hauer --- drivers/net/ag71xx.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/net/ag71xx.c b/drivers/net/ag71xx.c index 54f0bd9aa5..1cec263600 100644 --- a/drivers/net/ag71xx.c +++ b/drivers/net/ag71xx.c @@ -578,6 +578,7 @@ static int ag71xx_probe(struct device_d *dev) priv = xzalloc(sizeof(struct ag71xx)); edev = &priv->netdev; miibus = &priv->miibus; + dev->priv = edev; edev->priv = priv; edev->init = ag71xx_ether_init; @@ -659,6 +660,13 @@ static int ag71xx_probe(struct device_d *dev) return 0; } +static void ag71xx_remove(struct device_d *dev) +{ + struct eth_device *edev = dev->priv; + + ag71xx_ether_halt(edev); +} + static __maybe_unused struct of_device_id ag71xx_dt_ids[] = { { .compatible = "qca,ar9331-ge0", .data = &ag71xx_cfg_ar9331_ge0, }, { .compatible = "qca,ar9344-gmac0", .data = &ag71xx_cfg_ar9344_gmac0, }, @@ -668,6 +676,7 @@ static __maybe_unused struct of_device_id ag71xx_dt_ids[] = { static struct driver_d ag71xx_driver = { .name = "ag71xx-gmac", .probe = ag71xx_probe, + .remove = ag71xx_remove, .of_compatible = DRV_OF_COMPAT(ag71xx_dt_ids), }; device_platform_driver(ag71xx_driver); -- cgit v1.2.3 From 22b7ab1b4e9afc15d98783f13b613c328b07080b Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Sat, 30 Sep 2017 00:10:57 +0200 Subject: net: ag71xx: define parent devices without it we are not able to set mac address from other driver. Signed-off-by: Oleksij Rempel Signed-off-by: Sascha Hauer --- drivers/net/ag71xx.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/ag71xx.c b/drivers/net/ag71xx.c index 1cec263600..a422aacdd8 100644 --- a/drivers/net/ag71xx.c +++ b/drivers/net/ag71xx.c @@ -580,6 +580,7 @@ static int ag71xx_probe(struct device_d *dev) miibus = &priv->miibus; dev->priv = edev; edev->priv = priv; + edev->parent = dev; edev->init = ag71xx_ether_init; edev->open = ag71xx_ether_open; @@ -597,6 +598,7 @@ static int ag71xx_probe(struct device_d *dev) miibus->read = ag71xx_ether_mii_read; miibus->write = ag71xx_ether_mii_write; miibus->priv = priv; + miibus->parent = dev; /* enable switch core */ rd = ar7240_reg_rd(AR71XX_PLL_BASE + AR933X_ETHSW_CLOCK_CONTROL_REG); -- cgit v1.2.3 From 4dbdf29897ab649497e5cee6ad3875ebaaa3ebee Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Sat, 30 Sep 2017 00:12:44 +0200 Subject: clk: add ar9344 clock driver Signed-off-by: Oleksij Rempel Signed-off-by: Sascha Hauer --- drivers/clk/Makefile | 3 +- drivers/clk/clk-ar9344.c | 148 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 150 insertions(+), 1 deletion(-) create mode 100644 drivers/clk/clk-ar9344.c diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index b5abe1cdf5..ddd971c607 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_ARCH_MXS) += mxs/ obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/ obj-$(CONFIG_ARCH_TEGRA) += tegra/ obj-$(CONFIG_CLK_SOCFPGA) += socfpga/ -obj-$(CONFIG_MACH_MIPS_ATH79) += clk-ar933x.o +obj-$(CONFIG_SOC_QCA_AR9331) += clk-ar933x.o +obj-$(CONFIG_SOC_QCA_AR9344) += clk-ar9344.o obj-$(CONFIG_ARCH_IMX) += imx/ obj-$(CONFIG_COMMON_CLK_AT91) += at91/ diff --git a/drivers/clk/clk-ar9344.c b/drivers/clk/clk-ar9344.c new file mode 100644 index 0000000000..c3c49fb109 --- /dev/null +++ b/drivers/clk/clk-ar9344.c @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2017 Oleksij Rempel + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 +#include +#include +#include +#include +#include + +#include +#include + +#define AR9344_CPU_PLL_CONFIG 0x00 +#define AR9344_DDR_PLL_CONFIG 0x04 +#define AR9344_OUTDIV_M 0x3 +#define AR9344_OUTDIV_S 19 +#define AR9344_REFDIV_M 0x1f +#define AR9344_REFDIV_S 12 +#define AR9344_NINT_M 0x3f +#define AR9344_NINT_S 6 +#define AR9344_NFRAC_M 0x3f +#define AR9344_NFRAC_S 0 + + +#define AR9344_CPU_DDR_CLOCK_CONTROL 0x08 +#define AR9344_CPU_FROM_CPUPLL BIT(20) +#define AR9344_CPU_PLL_BYPASS BIT(2) +#define AR9344_CPU_POST_DIV_M 0x1f +#define AR9344_CPU_POST_DIV_S 5 + +static struct clk *clks[ATH79_CLK_END]; +static struct clk_onecell_data clk_data; + +struct clk_ar9344 { + struct clk clk; + void __iomem *base; + u32 div_shift; + u32 div_mask; + const char *parent; +}; + +static unsigned long clk_ar9344_recalc_rate(struct clk *clk, + unsigned long parent_rate) +{ + struct clk_ar9344 *f = container_of(clk, struct clk_ar9344, clk); + int outdiv, refdiv, nint, nfrac; + int cpu_post_div; + u32 clock_ctrl; + u32 val; + + clock_ctrl = __raw_readl(f->base + AR9344_CPU_DDR_CLOCK_CONTROL); + cpu_post_div = ((clock_ctrl >> AR9344_CPU_POST_DIV_S) + & AR9344_CPU_POST_DIV_M) + 1; + if (clock_ctrl & AR9344_CPU_PLL_BYPASS) { + return parent_rate; + } else if (clock_ctrl & AR9344_CPU_FROM_CPUPLL) { + val = __raw_readl(f->base + AR9344_CPU_PLL_CONFIG); + } else { + val = __raw_readl(f->base + AR9344_DDR_PLL_CONFIG); + } + + outdiv = (val >> AR9344_OUTDIV_S) & AR9344_OUTDIV_M; + refdiv = (val >> AR9344_REFDIV_S) & AR9344_REFDIV_M; + nint = (val >> AR9344_NINT_S) & AR9344_NINT_M; + nfrac = (val >> AR9344_NFRAC_S) & AR9344_NFRAC_M; + + return (parent_rate * (nint + (nfrac >> 9))) / (refdiv * (1 << outdiv)); +} + +struct clk_ops clk_ar9344_ops = { + .recalc_rate = clk_ar9344_recalc_rate, +}; + +static struct clk *clk_ar9344(const char *name, const char *parent, + void __iomem *base) +{ + struct clk_ar9344 *f = xzalloc(sizeof(*f)); + + f->parent = parent; + f->base = base; + f->div_shift = 0; + f->div_mask = 0; + + f->clk.ops = &clk_ar9344_ops; + f->clk.name = name; + f->clk.parent_names = &f->parent; + f->clk.num_parents = 1; + + clk_register(&f->clk); + + return &f->clk; +} + +static void ar9344_pll_init(void __iomem *base) +{ + clks[ATH79_CLK_CPU] = clk_ar9344("cpu", "ref", base); +} + +static int ar9344_clk_probe(struct device_d *dev) +{ + struct resource *iores; + void __iomem *base; + + iores = dev_request_mem_resource(dev, 0); + if (IS_ERR(iores)) + return PTR_ERR(iores); + base = IOMEM(iores->start); + + ar9344_pll_init(base); + + clk_data.clks = clks; + clk_data.clk_num = ARRAY_SIZE(clks); + of_clk_add_provider(dev->device_node, of_clk_src_onecell_get, + &clk_data); + + return 0; +} + +static __maybe_unused struct of_device_id ar9344_clk_dt_ids[] = { + { + .compatible = "qca,ar9344-pll", + }, { + /* sentinel */ + } +}; + +static struct driver_d ar9344_clk_driver = { + .probe = ar9344_clk_probe, + .name = "ar9344_clk", + .of_compatible = DRV_OF_COMPAT(ar9344_clk_dt_ids), +}; + +static int ar9344_clk_init(void) +{ + return platform_driver_register(&ar9344_clk_driver); +} +postcore_initcall(ar9344_clk_init); -- cgit v1.2.3 From 26b4af6b970c793f2a2c975054ae51e6bbe5e4e6 Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Sat, 30 Sep 2017 00:12:45 +0200 Subject: MIPS: dts: ar9344: add pll node Signed-off-by: Oleksij Rempel Signed-off-by: Sascha Hauer --- arch/mips/dts/ar9344.dtsi | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/arch/mips/dts/ar9344.dtsi b/arch/mips/dts/ar9344.dtsi index 3e105174d5..0a7171b8dc 100644 --- a/arch/mips/dts/ar9344.dtsi +++ b/arch/mips/dts/ar9344.dtsi @@ -13,6 +13,7 @@ cpu@0 { device_type = "cpu"; compatible = "mips,mips74Kc"; + clocks = <&pll ATH79_CLK_CPU>; reg = <0>; }; }; @@ -47,6 +48,16 @@ status = "disabled"; }; + pll: pll-controller@18050000 { + compatible = "qca,ar9344-pll"; + reg = <0x18050000 0x100>; + + clocks = <&ref>; + clock-names = "ref"; + + #clock-cells = <1>; + }; + spi: spi@1f000000 { compatible = "qca,ar7100-spi", "qca,ar9344-spi"; reg = <0x1f000000 0x1c>; -- cgit v1.2.3