diff --git a/arch/mips/configs/tplink-wdr4300_defconfig b/arch/mips/configs/tplink-wdr4300_defconfig index 63189b7..46093d2 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 @@ -32,6 +32,10 @@ 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_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_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 diff --git a/arch/mips/dts/ar9331.dtsi b/arch/mips/dts/ar9331.dtsi new file mode 100644 index 0000000..b4b8b76 --- /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/ar9344-tl-wdr4300-v1.7.dts b/arch/mips/dts/ar9344-tl-wdr4300-v1.7.dts new file mode 100644 index 0000000..d16cab0 --- /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.dtsi b/arch/mips/dts/ar9344.dtsi index 0838e8d..0a7171b 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>; }; }; @@ -29,23 +30,51 @@ #address-cells = <1>; #size-cells = <1>; - 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>; + apb { + compatible = "simple-bus"; + ranges; #address-cells = <1>; - #size-cells = <0>; + #size-cells = <1>; + + uart0: uart@18020000 { + compatible = "ns16550a", "qca,ar9344-uart0"; + reg = <0x18020000 0x20>; + + reg-shift = <2>; + reg-io-width = <4>; + big-endian; + + 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>; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + }; + + mac0: mac@19000000 { + compatible = "qca,ar9344-gmac0"; + reg = <0x18070000 0x00000100>, + <0x19000000 0x01000000>; + reg-names = "gmac", "ge0"; + phy-mode = "rgmii"; status = "disabled"; }; diff --git a/arch/mips/dts/ar9344_tl_wdr4300.dts b/arch/mips/dts/ar9344_tl_wdr4300.dts deleted file mode 100644 index b02c1d7..0000000 --- a/arch/mips/dts/ar9344_tl_wdr4300.dts +++ /dev/null @@ -1,63 +0,0 @@ -/dts-v1/; - -#include -#include - -#include "ar9344.dtsi" - -/ { - model = "TP-Link WDR4300"; - compatible = "tplink,tl-wdr4300"; - - aliases { - serial0 = &uart0; - }; - - 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>; - read-only; - }; - - partition@80000 { - label = "barebox-environment"; - reg = <0x80000 0x10000>; - }; - }; -}; diff --git a/arch/mips/dts/tplink-mr3020.dts b/arch/mips/dts/tplink-mr3020.dts index eaae11e..6f1ad13 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"; +}; diff --git a/arch/mips/mach-ath79/include/mach/ar71xx_regs.h b/arch/mips/mach-ath79/include/mach/ar71xx_regs.h index f56c3f7..31d33b3 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/clk/Makefile b/drivers/clk/Makefile index a36a8db..55ac953 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -9,7 +9,8 @@ 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/ obj-$(CONFIG_MACH_VEXPRESS) += vexpress/ diff --git a/drivers/clk/clk-ar9344.c b/drivers/clk/clk-ar9344.c new file mode 100644 index 0000000..c3c49fb --- /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); diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index c3980e7..9d69b6a 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -204,6 +204,13 @@ 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 42ea208..86c8ac3 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -30,3 +30,4 @@ 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 0000000..a422aac --- /dev/null +++ b/drivers/net/ag71xx.c @@ -0,0 +1,684 @@ +/* + * 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; + dev->priv = edev; + edev->priv = priv; + edev->parent = dev; + + 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; + miibus->parent = dev; + + /* 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 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, }, + { /* sentinel */ } +}; + +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); diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index d30f65b..ea2e062 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -8,6 +8,11 @@ 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 10732f8..13b8f65 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 0000000..a0b1002 --- /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);