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