diff options
author | Tom Rini <trini@konsulko.com> | 2016-11-07 18:16:00 (GMT) |
---|---|---|
committer | Tom Rini <trini@konsulko.com> | 2016-11-07 18:16:00 (GMT) |
commit | d8c4eb60f8d1af74116eaee4f53adc937e618d6f (patch) | |
tree | ccdab4ffc0c3c32a1d788f33674f9f092830b7d0 | |
parent | dac09fc10b71045ac261a9b8a05d34029ebec2ae (diff) | |
parent | aa555fe9f07a21b3bbdab15aea594f3869e5ab22 (diff) | |
download | u-boot-d8c4eb60f8d1af74116eaee4f53adc937e618d6f.tar.xz |
Merge branch 'master' of git://git.denx.de/u-boot-net
-rw-r--r-- | arch/arm/dts/tegra186-p2771-0000.dtsi | 5 | ||||
-rw-r--r-- | arch/arm/dts/tegra186.dtsi | 20 | ||||
-rw-r--r-- | arch/arm/mach-tegra/board186.c | 7 | ||||
-rw-r--r-- | arch/arm/mach-tegra/tegra186/Makefile | 1 | ||||
-rw-r--r-- | arch/arm/mach-tegra/tegra186/nvtboot_board.c | 54 | ||||
-rw-r--r-- | configs/p2771-0000-000_defconfig | 1 | ||||
-rw-r--r-- | configs/p2771-0000-500_defconfig | 1 | ||||
-rw-r--r-- | doc/device-tree-bindings/net/snps,dwc-qos-ethernet.txt | 166 | ||||
-rw-r--r-- | drivers/net/Kconfig | 11 | ||||
-rw-r--r-- | drivers/net/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/dwc_eth_qos.c | 1552 | ||||
-rw-r--r-- | drivers/net/mvgbe.c | 25 | ||||
-rw-r--r-- | drivers/net/phy/micrel.c | 23 | ||||
-rw-r--r-- | include/micrel.h | 3 | ||||
-rw-r--r-- | net/eth-uclass.c | 3 |
15 files changed, 1849 insertions, 24 deletions
diff --git a/arch/arm/dts/tegra186-p2771-0000.dtsi b/arch/arm/dts/tegra186-p2771-0000.dtsi index 6e07108..54b2539 100644 --- a/arch/arm/dts/tegra186-p2771-0000.dtsi +++ b/arch/arm/dts/tegra186-p2771-0000.dtsi @@ -25,6 +25,11 @@ reg = <0x0 0x80000000 0x0 0x60000000>; }; + ethernet@2490000 { + status = "okay"; + phy-reset-gpios = <&gpio_main TEGRA_MAIN_GPIO(M, 4) GPIO_ACTIVE_LOW>; + }; + i2c@3160000 { status = "okay"; }; diff --git a/arch/arm/dts/tegra186.dtsi b/arch/arm/dts/tegra186.dtsi index f878b65..dd9e3b8 100644 --- a/arch/arm/dts/tegra186.dtsi +++ b/arch/arm/dts/tegra186.dtsi @@ -31,6 +31,26 @@ #interrupt-cells = <2>; }; + ethernet@2490000 { + compatible = "nvidia,tegra186-eqos", "snps,dwc-qos-ethernet-4.10"; + reg = <0x0 0x02490000 0x0 0x10000>; + interrupts = <GIC_SPI 194 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&bpmp TEGRA186_CLK_AXI_CBB>, + <&bpmp TEGRA186_CLK_EQOS_AXI>, + <&bpmp TEGRA186_CLK_EQOS_RX>, + <&bpmp TEGRA186_CLK_EQOS_PTP_REF>, + <&bpmp TEGRA186_CLK_EQOS_TX>; + clock-names = "slave_bus", + "master_bus", + "rx", + "ptp_ref", + "tx"; + resets = <&bpmp TEGRA186_RESET_EQOS>; + reset-names = "eqos"; + phy-mode = "rgmii"; + status = "disabled"; + }; + uarta: serial@3100000 { compatible = "nvidia,tegra186-uart", "nvidia,tegra20-uart"; reg = <0x0 0x03100000 0x0 0x10000>; diff --git a/arch/arm/mach-tegra/board186.c b/arch/arm/mach-tegra/board186.c index a071758..691c3fd 100644 --- a/arch/arm/mach-tegra/board186.c +++ b/arch/arm/mach-tegra/board186.c @@ -24,7 +24,12 @@ int board_init(void) return tegra_board_init(); } -int board_late_init(void) +__weak int tegra_soc_board_init_late(void) { return 0; } + +int board_late_init(void) +{ + return tegra_soc_board_init_late(); +} diff --git a/arch/arm/mach-tegra/tegra186/Makefile b/arch/arm/mach-tegra/tegra186/Makefile index 7f46a05..56f3378 100644 --- a/arch/arm/mach-tegra/tegra186/Makefile +++ b/arch/arm/mach-tegra/tegra186/Makefile @@ -4,5 +4,6 @@ obj-y += ../board186.o obj-y += cache.o +obj-y += nvtboot_board.o obj-y += nvtboot_ll.o obj-y += nvtboot_mem.o diff --git a/arch/arm/mach-tegra/tegra186/nvtboot_board.c b/arch/arm/mach-tegra/tegra186/nvtboot_board.c new file mode 100644 index 0000000..1d78346 --- /dev/null +++ b/arch/arm/mach-tegra/tegra186/nvtboot_board.c @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2016, NVIDIA CORPORATION. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <fdt_support.h> +#include <fdtdec.h> +#include <asm/arch/tegra.h> + +extern unsigned long nvtboot_boot_x0; + +/* + * Attempt to use /chosen/nvidia,ether-mac in the nvtboot DTB to U-Boot's + * ethaddr environment variable if possible. + */ +static int set_ethaddr_from_nvtboot(void) +{ + const void *nvtboot_blob = (void *)nvtboot_boot_x0; + int ret, node, len; + const u32 *prop; + + /* Already a valid address in the environment? If so, keep it */ + if (getenv("ethaddr")) + return 0; + + node = fdt_path_offset(nvtboot_blob, "/chosen"); + if (node < 0) { + printf("Can't find /chosen node in nvtboot DTB\n"); + return node; + } + prop = fdt_getprop(nvtboot_blob, node, "nvidia,ether-mac", &len); + if (!prop) { + printf("Can't find nvidia,ether-mac property in nvtboot DTB\n"); + return -ENOENT; + } + + ret = setenv("ethaddr", (void *)prop); + if (ret) { + printf("Failed to set ethaddr from nvtboot DTB: %d\n", ret); + return ret; + } + + return 0; +} + +int tegra_soc_board_init_late(void) +{ + /* Ignore errors here; not all cases care about Ethernet addresses */ + set_ethaddr_from_nvtboot(); + + return 0; +} diff --git a/configs/p2771-0000-000_defconfig b/configs/p2771-0000-000_defconfig index f638117..61bb3a1 100644 --- a/configs/p2771-0000-000_defconfig +++ b/configs/p2771-0000-000_defconfig @@ -30,6 +30,7 @@ CONFIG_CMD_FS_GENERIC=y # CONFIG_BLK is not set CONFIG_TEGRA186_BPMP_I2C=y # CONFIG_DM_MMC_OPS is not set +CONFIG_DWC_ETH_QOS=y CONFIG_E1000=y CONFIG_RTL8169=y CONFIG_PCI=y diff --git a/configs/p2771-0000-500_defconfig b/configs/p2771-0000-500_defconfig index baeb8f3..79f4808 100644 --- a/configs/p2771-0000-500_defconfig +++ b/configs/p2771-0000-500_defconfig @@ -30,6 +30,7 @@ CONFIG_CMD_FS_GENERIC=y # CONFIG_BLK is not set CONFIG_TEGRA186_BPMP_I2C=y # CONFIG_DM_MMC_OPS is not set +CONFIG_DWC_ETH_QOS=y CONFIG_E1000=y CONFIG_RTL8169=y CONFIG_PCI=y diff --git a/doc/device-tree-bindings/net/snps,dwc-qos-ethernet.txt b/doc/device-tree-bindings/net/snps,dwc-qos-ethernet.txt new file mode 100644 index 0000000..d93f71c --- /dev/null +++ b/doc/device-tree-bindings/net/snps,dwc-qos-ethernet.txt @@ -0,0 +1,166 @@ +* Synopsys DWC Ethernet QoS IP version 4.10 driver (GMAC) + +This binding supports the Synopsys Designware Ethernet QoS (Quality Of Service) +IP block. The IP supports multiple options for bus type, clocking and reset +structure, and feature list. Consequently, a number of properties and list +entries in properties are marked as optional, or only required in specific HW +configurations. + +Required properties: +- compatible: One of: + - "axis,artpec6-eqos", "snps,dwc-qos-ethernet-4.10" + Represents the IP core when integrated into the Axis ARTPEC-6 SoC. + - "nvidia,tegra186-eqos", "snps,dwc-qos-ethernet-4.10" + Represents the IP core when integrated into the NVIDIA Tegra186 SoC. + - "snps,dwc-qos-ethernet-4.10" + This combination is deprecated. It should be treated as equivalent to + "axis,artpec6-eqos", "snps,dwc-qos-ethernet-4.10". It is supported to be + compatible with earlier revisions of this binding. +- reg: Address and length of the register set for the device +- clocks: Phandle and clock specifiers for each entry in clock-names, in the + same order. See ../clock/clock-bindings.txt. +- clock-names: May contain any/all of the following depending on the IP + configuration, in any order: + - "tx" + The EQOS transmit path clock. The HW signal name is clk_tx_i. + In some configurations (e.g. GMII/RGMII), this clock also drives the PHY TX + path. In other configurations, other clocks (such as tx_125, rmii) may + drive the PHY TX path. + - "rx" + The EQOS receive path clock. The HW signal name is clk_rx_i. + In some configurations (e.g. GMII/RGMII), this clock is derived from the + PHY's RX clock output. In other configurations, other clocks (such as + rx_125, rmii) may drive the EQOS RX path. + In cases where the PHY clock is directly fed into the EQOS receive path + without intervening logic, the DT need not represent this clock, since it + is assumed to be fully under the control of the PHY device/driver. In + cases where SoC integration adds additional logic to this path, such as a + SW-controlled clock gate, this clock should be represented in DT. + - "slave_bus" + The CPU/slave-bus (CSR) interface clock. This applies to any bus type; + APB, AHB, AXI, etc. The HW signal name is hclk_i (AHB) or clk_csr_i (other + buses). + - "master_bus" + The master bus interface clock. Only required in configurations that use a + separate clock for the master and slave bus interfaces. The HW signal name + is hclk_i (AHB) or aclk_i (AXI). + - "ptp_ref" + The PTP reference clock. The HW signal name is clk_ptp_ref_i. + - "phy_ref_clk" + This clock is deprecated and should not be used by new compatible values. + It is equivalent to "tx". + - "apb_pclk" + This clock is deprecated and should not be used by new compatible values. + It is equivalent to "slave_bus". + + Note: Support for additional IP configurations may require adding the + following clocks to this list in the future: clk_rx_125_i, clk_tx_125_i, + clk_pmarx_0_i, clk_pmarx1_i, clk_rmii_i, clk_revmii_rx_i, clk_revmii_tx_i. + Configurations exist where multiple similar clocks are used at once, e.g. all + of clk_rx_125_i, clk_pmarx_0_i, clk_pmarx1_i. For this reason it is best to + extend the binding with a separate clock-names entry for each of those RX + clocks, rather than repurposing the existing "rx" clock-names entry as a + generic/logical clock in a similar fashion to "master_bus" and "slave_bus". + This will allow easy support for configurations that support multiple PHY + interfaces using a mux, and hence need to have explicit control over + specific RX clocks. + + The following compatible values require the following set of clocks: + - "nvidia,tegra186-eqos", "snps,dwc-qos-ethernet-4.10": + - "slave_bus" + - "master_bus" + - "rx" + - "tx" + - "ptp_ref" + - "axis,artpec6-eqos", "snps,dwc-qos-ethernet-4.10": + - "slave_bus" + - "master_bus" + - "tx" + - "ptp_ref" + - "snps,dwc-qos-ethernet-4.10" (deprecated): + - "phy_ref_clk" + - "apb_clk" +- interrupt-parent: Should be the phandle for the interrupt controller + that services interrupts for this device +- interrupts: Should contain the core's combined interrupt signal +- phy-mode: See ethernet.txt file in the same directory +- resets: Phandle and reset specifiers for each entry in reset-names, in the + same order. See ../reset/reset.txt. +- reset-names: May contain any/all of the following depending on the IP + configuration, in any order: + - "eqos". The reset to the entire module. The HW signal name is hreset_n + (AHB) or aresetn_i (AXI). + + The following compatible values require the following set of resets: + (the reset properties may be omitted if empty) + - "nvidia,tegra186-eqos", "snps,dwc-qos-ethernet-4.10": + - "eqos". + - "axis,artpec6-eqos", "snps,dwc-qos-ethernet-4.10": + - None. + - "snps,dwc-qos-ethernet-4.10" (deprecated): + - None. + +Optional properties: +- dma-coherent: Present if dma operations are coherent +- mac-address: See ethernet.txt in the same directory +- local-mac-address: See ethernet.txt in the same directory +- phy-reset-gpios: Phandle and specifier for any GPIO used to reset the PHY. + See ../gpio/gpio.txt. +- snps,en-lpi: If present it enables use of the AXI low-power interface +- snps,write-requests: Number of write requests that the AXI port can issue. + It depends on the SoC configuration. +- snps,read-requests: Number of read requests that the AXI port can issue. + It depends on the SoC configuration. +- snps,burst-map: Bitmap of allowed AXI burst lengts, with the LSB + representing 4, then 8 etc. +- snps,txpbl: DMA Programmable burst length for the TX DMA +- snps,rxpbl: DMA Programmable burst length for the RX DMA +- snps,en-tx-lpi-clockgating: Enable gating of the MAC TX clock during + TX low-power mode. +- phy-handle: See ethernet.txt file in the same directory +- mdio device tree subnode: When the GMAC has a phy connected to its local + mdio, there must be device tree subnode with the following + required properties: + - compatible: Must be "snps,dwc-qos-ethernet-mdio". + - #address-cells: Must be <1>. + - #size-cells: Must be <0>. + + For each phy on the mdio bus, there must be a node with the following + fields: + + - reg: phy id used to communicate to phy. + - device_type: Must be "ethernet-phy". + - fixed-mode device tree subnode: see fixed-link.txt in the same directory + +Examples: +ethernet2@40010000 { + clock-names = "phy_ref_clk", "apb_pclk"; + clocks = <&clkc 17>, <&clkc 15>; + compatible = "snps,dwc-qos-ethernet-4.10"; + interrupt-parent = <&intc>; + interrupts = <0x0 0x1e 0x4>; + reg = <0x40010000 0x4000>; + phy-handle = <&phy2>; + phy-mode = "gmii"; + phy-reset-gpios = <&gpioctlr 43 GPIO_ACTIVE_LOW>; + + snps,en-tx-lpi-clockgating; + snps,en-lpi; + snps,write-requests = <2>; + snps,read-requests = <16>; + snps,burst-map = <0x7>; + snps,txpbl = <8>; + snps,rxpbl = <2>; + + dma-coherent; + + mdio { + #address-cells = <0x1>; + #size-cells = <0x0>; + phy2: phy@1 { + compatible = "ethernet-phy-ieee802.3-c22"; + device_type = "ethernet-phy"; + reg = <0x1>; + }; + }; +}; diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 7b9961d..f25d3ff 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -64,6 +64,17 @@ config ALTERA_TSE Please find details on the "Triple-Speed Ethernet MegaCore Function Resource Center" of Altera. +config DWC_ETH_QOS + bool "Synopsys DWC Ethernet QOS device support" + depends on DM_ETH + select PHYLIB + help + This driver supports the Synopsys Designware Ethernet QOS (Quality + Of Service) IP block. The IP supports many options for bus type, + clocking/reset structure, and feature list. This driver currently + supports the specific configuration used in NVIDIA's Tegra186 chip, + but should be extensible to other combinations quite easily. + config E1000 bool "Intel PRO/1000 Gigabit Ethernet support" help diff --git a/drivers/net/Makefile b/drivers/net/Makefile index a448526..9a7bfc6 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -76,3 +76,4 @@ obj-$(CONFIG_FSL_MC_ENET) += ldpaa_eth/ obj-$(CONFIG_FSL_MEMAC) += fm/memac_phy.o obj-$(CONFIG_VSC9953) += vsc9953.o obj-$(CONFIG_PIC32_ETH) += pic32_mdio.o pic32_eth.o +obj-$(CONFIG_DWC_ETH_QOS) += dwc_eth_qos.o diff --git a/drivers/net/dwc_eth_qos.c b/drivers/net/dwc_eth_qos.c new file mode 100644 index 0000000..81eeba2 --- /dev/null +++ b/drivers/net/dwc_eth_qos.c @@ -0,0 +1,1552 @@ +/* + * Copyright (c) 2016, NVIDIA CORPORATION. + * + * SPDX-License-Identifier: GPL-2.0 + * + * Portions based on U-Boot's rtl8169.c. + */ + +/* + * This driver supports the Synopsys Designware Ethernet QOS (Quality Of + * Service) IP block. The IP supports multiple options for bus type, clocking/ + * reset structure, and feature list. + * + * The driver is written such that generic core logic is kept separate from + * configuration-specific logic. Code that interacts with configuration- + * specific resources is split out into separate functions to avoid polluting + * common code. If/when this driver is enhanced to support multiple + * configurations, the core code should be adapted to call all configuration- + * specific functions through function pointers, with the definition of those + * function pointers being supplied by struct udevice_id eqos_ids[]'s .data + * field. + * + * The following configurations are currently supported: + * tegra186: + * NVIDIA's Tegra186 chip. This configuration uses an AXI master/DMA bus, an + * AHB slave/register bus, contains the DMA, MTL, and MAC sub-blocks, and + * supports a single RGMII PHY. This configuration also has SW control over + * all clock and reset signals to the HW block. + */ + +#include <common.h> +#include <clk.h> +#include <dm.h> +#include <errno.h> +#include <memalign.h> +#include <miiphy.h> +#include <net.h> +#include <netdev.h> +#include <phy.h> +#include <reset.h> +#include <wait_bit.h> +#include <asm/gpio.h> +#include <asm/io.h> + +/* Core registers */ + +#define EQOS_MAC_REGS_BASE 0x000 +struct eqos_mac_regs { + uint32_t configuration; /* 0x000 */ + uint32_t unused_004[(0x070 - 0x004) / 4]; /* 0x004 */ + uint32_t q0_tx_flow_ctrl; /* 0x070 */ + uint32_t unused_070[(0x090 - 0x074) / 4]; /* 0x074 */ + uint32_t rx_flow_ctrl; /* 0x090 */ + uint32_t unused_094; /* 0x094 */ + uint32_t txq_prty_map0; /* 0x098 */ + uint32_t unused_09c; /* 0x09c */ + uint32_t rxq_ctrl0; /* 0x0a0 */ + uint32_t unused_0a4; /* 0x0a4 */ + uint32_t rxq_ctrl2; /* 0x0a8 */ + uint32_t unused_0ac[(0x0dc - 0x0ac) / 4]; /* 0x0ac */ + uint32_t us_tic_counter; /* 0x0dc */ + uint32_t unused_0e0[(0x11c - 0x0e0) / 4]; /* 0x0e0 */ + uint32_t hw_feature0; /* 0x11c */ + uint32_t hw_feature1; /* 0x120 */ + uint32_t hw_feature2; /* 0x124 */ + uint32_t unused_128[(0x200 - 0x128) / 4]; /* 0x128 */ + uint32_t mdio_address; /* 0x200 */ + uint32_t mdio_data; /* 0x204 */ + uint32_t unused_208[(0x300 - 0x208) / 4]; /* 0x208 */ + uint32_t address0_high; /* 0x300 */ + uint32_t address0_low; /* 0x304 */ +}; + +#define EQOS_MAC_CONFIGURATION_GPSLCE BIT(23) +#define EQOS_MAC_CONFIGURATION_CST BIT(21) +#define EQOS_MAC_CONFIGURATION_ACS BIT(20) +#define EQOS_MAC_CONFIGURATION_WD BIT(19) +#define EQOS_MAC_CONFIGURATION_JD BIT(17) +#define EQOS_MAC_CONFIGURATION_JE BIT(16) +#define EQOS_MAC_CONFIGURATION_PS BIT(15) +#define EQOS_MAC_CONFIGURATION_FES BIT(14) +#define EQOS_MAC_CONFIGURATION_DM BIT(13) +#define EQOS_MAC_CONFIGURATION_TE BIT(1) +#define EQOS_MAC_CONFIGURATION_RE BIT(0) + +#define EQOS_MAC_Q0_TX_FLOW_CTRL_PT_SHIFT 16 +#define EQOS_MAC_Q0_TX_FLOW_CTRL_PT_MASK 0xffff +#define EQOS_MAC_Q0_TX_FLOW_CTRL_TFE BIT(1) + +#define EQOS_MAC_RX_FLOW_CTRL_RFE BIT(0) + +#define EQOS_MAC_TXQ_PRTY_MAP0_PSTQ0_SHIFT 0 +#define EQOS_MAC_TXQ_PRTY_MAP0_PSTQ0_MASK 0xff + +#define EQOS_MAC_RXQ_CTRL0_RXQ0EN_SHIFT 0 +#define EQOS_MAC_RXQ_CTRL0_RXQ0EN_MASK 3 +#define EQOS_MAC_RXQ_CTRL0_RXQ0EN_NOT_ENABLED 0 +#define EQOS_MAC_RXQ_CTRL0_RXQ0EN_ENABLED_DCB 2 + +#define EQOS_MAC_RXQ_CTRL2_PSRQ0_SHIFT 0 +#define EQOS_MAC_RXQ_CTRL2_PSRQ0_MASK 0xff + +#define EQOS_MAC_HW_FEATURE1_TXFIFOSIZE_SHIFT 6 +#define EQOS_MAC_HW_FEATURE1_TXFIFOSIZE_MASK 0x1f +#define EQOS_MAC_HW_FEATURE1_RXFIFOSIZE_SHIFT 0 +#define EQOS_MAC_HW_FEATURE1_RXFIFOSIZE_MASK 0x1f + +#define EQOS_MAC_MDIO_ADDRESS_PA_SHIFT 21 +#define EQOS_MAC_MDIO_ADDRESS_RDA_SHIFT 16 +#define EQOS_MAC_MDIO_ADDRESS_CR_SHIFT 8 +#define EQOS_MAC_MDIO_ADDRESS_CR_20_35 2 +#define EQOS_MAC_MDIO_ADDRESS_SKAP BIT(4) +#define EQOS_MAC_MDIO_ADDRESS_GOC_SHIFT 2 +#define EQOS_MAC_MDIO_ADDRESS_GOC_READ 3 +#define EQOS_MAC_MDIO_ADDRESS_GOC_WRITE 1 +#define EQOS_MAC_MDIO_ADDRESS_C45E BIT(1) +#define EQOS_MAC_MDIO_ADDRESS_GB BIT(0) + +#define EQOS_MAC_MDIO_DATA_GD_MASK 0xffff + +#define EQOS_MTL_REGS_BASE 0xd00 +struct eqos_mtl_regs { + uint32_t txq0_operation_mode; /* 0xd00 */ + uint32_t unused_d04; /* 0xd04 */ + uint32_t txq0_debug; /* 0xd08 */ + uint32_t unused_d0c[(0xd18 - 0xd0c) / 4]; /* 0xd0c */ + uint32_t txq0_quantum_weight; /* 0xd18 */ + uint32_t unused_d1c[(0xd30 - 0xd1c) / 4]; /* 0xd1c */ + uint32_t rxq0_operation_mode; /* 0xd30 */ + uint32_t unused_d34; /* 0xd34 */ + uint32_t rxq0_debug; /* 0xd38 */ +}; + +#define EQOS_MTL_TXQ0_OPERATION_MODE_TQS_SHIFT 16 +#define EQOS_MTL_TXQ0_OPERATION_MODE_TQS_MASK 0x1ff +#define EQOS_MTL_TXQ0_OPERATION_MODE_TXQEN_SHIFT 2 +#define EQOS_MTL_TXQ0_OPERATION_MODE_TXQEN_MASK 3 +#define EQOS_MTL_TXQ0_OPERATION_MODE_TXQEN_ENABLED 2 +#define EQOS_MTL_TXQ0_OPERATION_MODE_TSF BIT(1) +#define EQOS_MTL_TXQ0_OPERATION_MODE_FTQ BIT(0) + +#define EQOS_MTL_TXQ0_DEBUG_TXQSTS BIT(4) +#define EQOS_MTL_TXQ0_DEBUG_TRCSTS_SHIFT 1 +#define EQOS_MTL_TXQ0_DEBUG_TRCSTS_MASK 3 + +#define EQOS_MTL_RXQ0_OPERATION_MODE_RQS_SHIFT 20 +#define EQOS_MTL_RXQ0_OPERATION_MODE_RQS_MASK 0x3ff +#define EQOS_MTL_RXQ0_OPERATION_MODE_RFD_SHIFT 14 +#define EQOS_MTL_RXQ0_OPERATION_MODE_RFD_MASK 0x3f +#define EQOS_MTL_RXQ0_OPERATION_MODE_RFA_SHIFT 8 +#define EQOS_MTL_RXQ0_OPERATION_MODE_RFA_MASK 0x3f +#define EQOS_MTL_RXQ0_OPERATION_MODE_EHFC BIT(7) +#define EQOS_MTL_RXQ0_OPERATION_MODE_RSF BIT(5) + +#define EQOS_MTL_RXQ0_DEBUG_PRXQ_SHIFT 16 +#define EQOS_MTL_RXQ0_DEBUG_PRXQ_MASK 0x7fff +#define EQOS_MTL_RXQ0_DEBUG_RXQSTS_SHIFT 4 +#define EQOS_MTL_RXQ0_DEBUG_RXQSTS_MASK 3 + +#define EQOS_DMA_REGS_BASE 0x1000 +struct eqos_dma_regs { + uint32_t mode; /* 0x1000 */ + uint32_t sysbus_mode; /* 0x1004 */ + uint32_t unused_1008[(0x1100 - 0x1008) / 4]; /* 0x1008 */ + uint32_t ch0_control; /* 0x1100 */ + uint32_t ch0_tx_control; /* 0x1104 */ + uint32_t ch0_rx_control; /* 0x1108 */ + uint32_t unused_110c; /* 0x110c */ + uint32_t ch0_txdesc_list_haddress; /* 0x1110 */ + uint32_t ch0_txdesc_list_address; /* 0x1114 */ + uint32_t ch0_rxdesc_list_haddress; /* 0x1118 */ + uint32_t ch0_rxdesc_list_address; /* 0x111c */ + uint32_t ch0_txdesc_tail_pointer; /* 0x1120 */ + uint32_t unused_1124; /* 0x1124 */ + uint32_t ch0_rxdesc_tail_pointer; /* 0x1128 */ + uint32_t ch0_txdesc_ring_length; /* 0x112c */ + uint32_t ch0_rxdesc_ring_length; /* 0x1130 */ +}; + +#define EQOS_DMA_MODE_SWR BIT(0) + +#define EQOS_DMA_SYSBUS_MODE_RD_OSR_LMT_SHIFT 16 +#define EQOS_DMA_SYSBUS_MODE_RD_OSR_LMT_MASK 0xf +#define EQOS_DMA_SYSBUS_MODE_EAME BIT(11) +#define EQOS_DMA_SYSBUS_MODE_BLEN16 BIT(3) +#define EQOS_DMA_SYSBUS_MODE_BLEN8 BIT(2) +#define EQOS_DMA_SYSBUS_MODE_BLEN4 BIT(1) + +#define EQOS_DMA_CH0_CONTROL_PBLX8 BIT(16) + +#define EQOS_DMA_CH0_TX_CONTROL_TXPBL_SHIFT 16 +#define EQOS_DMA_CH0_TX_CONTROL_TXPBL_MASK 0x3f +#define EQOS_DMA_CH0_TX_CONTROL_OSP BIT(4) +#define EQOS_DMA_CH0_TX_CONTROL_ST BIT(0) + +#define EQOS_DMA_CH0_RX_CONTROL_RXPBL_SHIFT 16 +#define EQOS_DMA_CH0_RX_CONTROL_RXPBL_MASK 0x3f +#define EQOS_DMA_CH0_RX_CONTROL_RBSZ_SHIFT 1 +#define EQOS_DMA_CH0_RX_CONTROL_RBSZ_MASK 0x3fff +#define EQOS_DMA_CH0_RX_CONTROL_SR BIT(0) + +/* These registers are Tegra186-specific */ +#define EQOS_TEGRA186_REGS_BASE 0x8800 +struct eqos_tegra186_regs { + uint32_t sdmemcomppadctrl; /* 0x8800 */ + uint32_t auto_cal_config; /* 0x8804 */ + uint32_t unused_8808; /* 0x8808 */ + uint32_t auto_cal_status; /* 0x880c */ +}; + +#define EQOS_SDMEMCOMPPADCTRL_PAD_E_INPUT_OR_E_PWRD BIT(31) + +#define EQOS_AUTO_CAL_CONFIG_START BIT(31) +#define EQOS_AUTO_CAL_CONFIG_ENABLE BIT(29) + +#define EQOS_AUTO_CAL_STATUS_ACTIVE BIT(31) + +/* Descriptors */ + +#define EQOS_DESCRIPTOR_WORDS 4 +#define EQOS_DESCRIPTOR_SIZE (EQOS_DESCRIPTOR_WORDS * 4) +/* We assume ARCH_DMA_MINALIGN >= 16; 16 is the EQOS HW minimum */ +#define EQOS_DESCRIPTOR_ALIGN ARCH_DMA_MINALIGN +#define EQOS_DESCRIPTORS_TX 4 +#define EQOS_DESCRIPTORS_RX 4 +#define EQOS_DESCRIPTORS_NUM (EQOS_DESCRIPTORS_TX + EQOS_DESCRIPTORS_RX) +#define EQOS_DESCRIPTORS_SIZE ALIGN(EQOS_DESCRIPTORS_NUM * \ + EQOS_DESCRIPTOR_SIZE, ARCH_DMA_MINALIGN) +#define EQOS_BUFFER_ALIGN ARCH_DMA_MINALIGN +#define EQOS_MAX_PACKET_SIZE ALIGN(1568, ARCH_DMA_MINALIGN) +#define EQOS_RX_BUFFER_SIZE (EQOS_DESCRIPTORS_RX * EQOS_MAX_PACKET_SIZE) + +/* + * Warn if the cache-line size is larger than the descriptor size. In such + * cases the driver will likely fail because the CPU needs to flush the cache + * when requeuing RX buffers, therefore descriptors written by the hardware + * may be discarded. Architectures with full IO coherence, such as x86, do not + * experience this issue, and hence are excluded from this condition. + * + * This can be fixed by defining CONFIG_SYS_NONCACHED_MEMORY which will cause + * the driver to allocate descriptors from a pool of non-cached memory. + */ +#if EQOS_DESCRIPTOR_SIZE < ARCH_DMA_MINALIGN +#if !defined(CONFIG_SYS_NONCACHED_MEMORY) && \ + !defined(CONFIG_SYS_DCACHE_OFF) && !defined(CONFIG_X86) +#warning Cache line size is larger than descriptor size +#endif +#endif + +struct eqos_desc { + u32 des0; + u32 des1; + u32 des2; + u32 des3; +}; + +#define EQOS_DESC3_OWN BIT(31) +#define EQOS_DESC3_FD BIT(29) +#define EQOS_DESC3_LD BIT(28) +#define EQOS_DESC3_BUF1V BIT(24) + +struct eqos_config { + bool reg_access_always_ok; +}; + +struct eqos_priv { + struct udevice *dev; + const struct eqos_config *config; + fdt_addr_t regs; + struct eqos_mac_regs *mac_regs; + struct eqos_mtl_regs *mtl_regs; + struct eqos_dma_regs *dma_regs; + struct eqos_tegra186_regs *tegra186_regs; + struct reset_ctl reset_ctl; + struct gpio_desc phy_reset_gpio; + struct clk clk_master_bus; + struct clk clk_rx; + struct clk clk_ptp_ref; + struct clk clk_tx; + struct clk clk_slave_bus; + struct mii_dev *mii; + struct phy_device *phy; + void *descs; + struct eqos_desc *tx_descs; + struct eqos_desc *rx_descs; + int tx_desc_idx, rx_desc_idx; + void *tx_dma_buf; + void *rx_dma_buf; + void *rx_pkt; + bool started; + bool reg_access_ok; +}; + +/* + * TX and RX descriptors are 16 bytes. This causes problems with the cache + * maintenance on CPUs where the cache-line size exceeds the size of these + * descriptors. What will happen is that when the driver receives a packet + * it will be immediately requeued for the hardware to reuse. The CPU will + * therefore need to flush the cache-line containing the descriptor, which + * will cause all other descriptors in the same cache-line to be flushed + * along with it. If one of those descriptors had been written to by the + * device those changes (and the associated packet) will be lost. + * + * To work around this, we make use of non-cached memory if available. If + * descriptors are mapped uncached there's no need to manually flush them + * or invalidate them. + * + * Note that this only applies to descriptors. The packet data buffers do + * not have the same constraints since they are 1536 bytes large, so they + * are unlikely to share cache-lines. + */ +static void *eqos_alloc_descs(unsigned int num) +{ +#ifdef CONFIG_SYS_NONCACHED_MEMORY + return (void *)noncached_alloc(EQOS_DESCRIPTORS_SIZE, + EQOS_DESCRIPTOR_ALIGN); +#else + return memalign(EQOS_DESCRIPTOR_ALIGN, EQOS_DESCRIPTORS_SIZE); +#endif +} + +static void eqos_free_descs(void *descs) +{ +#ifdef CONFIG_SYS_NONCACHED_MEMORY + /* FIXME: noncached_alloc() has no opposite */ +#else + free(descs); +#endif +} + +static void eqos_inval_desc(void *desc) +{ +#ifndef CONFIG_SYS_NONCACHED_MEMORY + unsigned long start = (unsigned long)desc & ~(ARCH_DMA_MINALIGN - 1); + unsigned long end = ALIGN(start + EQOS_DESCRIPTOR_SIZE, + ARCH_DMA_MINALIGN); + + invalidate_dcache_range(start, end); +#endif +} + +static void eqos_flush_desc(void *desc) +{ +#ifndef CONFIG_SYS_NONCACHED_MEMORY + flush_cache((unsigned long)desc, EQOS_DESCRIPTOR_SIZE); +#endif +} + +static void eqos_inval_buffer(void *buf, size_t size) +{ + unsigned long start = (unsigned long)buf & ~(ARCH_DMA_MINALIGN - 1); + unsigned long end = ALIGN(start + size, ARCH_DMA_MINALIGN); + + invalidate_dcache_range(start, end); +} + +static void eqos_flush_buffer(void *buf, size_t size) +{ + flush_cache((unsigned long)buf, size); +} + +static int eqos_mdio_wait_idle(struct eqos_priv *eqos) +{ + return wait_for_bit(__func__, &eqos->mac_regs->mdio_address, + EQOS_MAC_MDIO_ADDRESS_GB, false, 1000000, true); +} + +static int eqos_mdio_read(struct mii_dev *bus, int mdio_addr, int mdio_devad, + int mdio_reg) +{ + struct eqos_priv *eqos = bus->priv; + u32 val; + int ret; + + debug("%s(dev=%p, addr=%x, reg=%d):\n", __func__, eqos->dev, mdio_addr, + mdio_reg); + + ret = eqos_mdio_wait_idle(eqos); + if (ret) { + error("MDIO not idle at entry"); + return ret; + } + + val = readl(&eqos->mac_regs->mdio_address); + val &= EQOS_MAC_MDIO_ADDRESS_SKAP | + EQOS_MAC_MDIO_ADDRESS_C45E; + val |= (mdio_addr << EQOS_MAC_MDIO_ADDRESS_PA_SHIFT) | + (mdio_reg << EQOS_MAC_MDIO_ADDRESS_RDA_SHIFT) | + (EQOS_MAC_MDIO_ADDRESS_CR_20_35 << + EQOS_MAC_MDIO_ADDRESS_CR_SHIFT) | + (EQOS_MAC_MDIO_ADDRESS_GOC_READ << + EQOS_MAC_MDIO_ADDRESS_GOC_SHIFT) | + EQOS_MAC_MDIO_ADDRESS_GB; + writel(val, &eqos->mac_regs->mdio_address); + + udelay(10); + + ret = eqos_mdio_wait_idle(eqos); + if (ret) { + error("MDIO read didn't complete"); + return ret; + } + + val = readl(&eqos->mac_regs->mdio_data); + val &= EQOS_MAC_MDIO_DATA_GD_MASK; + + debug("%s: val=%x\n", __func__, val); + + return val; +} + +static int eqos_mdio_write(struct mii_dev *bus, int mdio_addr, int mdio_devad, + int mdio_reg, u16 mdio_val) +{ + struct eqos_priv *eqos = bus->priv; + u32 val; + int ret; + + debug("%s(dev=%p, addr=%x, reg=%d, val=%x):\n", __func__, eqos->dev, + mdio_addr, mdio_reg, mdio_val); + + ret = eqos_mdio_wait_idle(eqos); + if (ret) { + error("MDIO not idle at entry"); + return ret; + } + + writel(mdio_val, &eqos->mac_regs->mdio_data); + + val = readl(&eqos->mac_regs->mdio_address); + val &= EQOS_MAC_MDIO_ADDRESS_SKAP | + EQOS_MAC_MDIO_ADDRESS_C45E; + val |= (mdio_addr << EQOS_MAC_MDIO_ADDRESS_PA_SHIFT) | + (mdio_reg << EQOS_MAC_MDIO_ADDRESS_RDA_SHIFT) | + (EQOS_MAC_MDIO_ADDRESS_CR_20_35 << + EQOS_MAC_MDIO_ADDRESS_CR_SHIFT) | + (EQOS_MAC_MDIO_ADDRESS_GOC_WRITE << + EQOS_MAC_MDIO_ADDRESS_GOC_SHIFT) | + EQOS_MAC_MDIO_ADDRESS_GB; + writel(val, &eqos->mac_regs->mdio_address); + + udelay(10); + + ret = eqos_mdio_wait_idle(eqos); + if (ret) { + error("MDIO read didn't complete"); + return ret; + } + + return 0; +} + +static int eqos_start_clks_tegra186(struct udevice *dev) +{ + struct eqos_priv *eqos = dev_get_priv(dev); + int ret; + + debug("%s(dev=%p):\n", __func__, dev); + + ret = clk_enable(&eqos->clk_slave_bus); + if (ret < 0) { + error("clk_enable(clk_slave_bus) failed: %d", ret); + goto err; + } + + ret = clk_enable(&eqos->clk_master_bus); + if (ret < 0) { + error("clk_enable(clk_master_bus) failed: %d", ret); + goto err_disable_clk_slave_bus; + } + + ret = clk_enable(&eqos->clk_rx); + if (ret < 0) { + error("clk_enable(clk_rx) failed: %d", ret); + goto err_disable_clk_master_bus; + } + + ret = clk_enable(&eqos->clk_ptp_ref); + if (ret < 0) { + error("clk_enable(clk_ptp_ref) failed: %d", ret); + goto err_disable_clk_rx; + } + + ret = clk_set_rate(&eqos->clk_ptp_ref, 125 * 1000 * 1000); + if (ret < 0) { + error("clk_set_rate(clk_ptp_ref) failed: %d", ret); + goto err_disable_clk_ptp_ref; + } + + ret = clk_enable(&eqos->clk_tx); + if (ret < 0) { + error("clk_enable(clk_tx) failed: %d", ret); + goto err_disable_clk_ptp_ref; + } + + debug("%s: OK\n", __func__); + return 0; + +err_disable_clk_ptp_ref: + clk_disable(&eqos->clk_ptp_ref); +err_disable_clk_rx: + clk_disable(&eqos->clk_rx); +err_disable_clk_master_bus: + clk_disable(&eqos->clk_master_bus); +err_disable_clk_slave_bus: + clk_disable(&eqos->clk_slave_bus); +err: + debug("%s: FAILED: %d\n", __func__, ret); + return ret; +} + +void eqos_stop_clks_tegra186(struct udevice *dev) +{ + struct eqos_priv *eqos = dev_get_priv(dev); + + debug("%s(dev=%p):\n", __func__, dev); + + clk_disable(&eqos->clk_tx); + clk_disable(&eqos->clk_ptp_ref); + clk_disable(&eqos->clk_rx); + clk_disable(&eqos->clk_master_bus); + clk_disable(&eqos->clk_slave_bus); + + debug("%s: OK\n", __func__); +} + +static int eqos_start_resets_tegra186(struct udevice *dev) +{ + struct eqos_priv *eqos = dev_get_priv(dev); + int ret; + + debug("%s(dev=%p):\n", __func__, dev); + + ret = dm_gpio_set_value(&eqos->phy_reset_gpio, 1); + if (ret < 0) { + error("dm_gpio_set_value(phy_reset, assert) failed: %d", ret); + return ret; + } + + udelay(2); + + ret = dm_gpio_set_value(&eqos->phy_reset_gpio, 0); + if (ret < 0) { + error("dm_gpio_set_value(phy_reset, deassert) failed: %d", ret); + return ret; + } + + ret = reset_assert(&eqos->reset_ctl); + if (ret < 0) { + error("reset_assert() failed: %d", ret); + return ret; + } + + udelay(2); + + ret = reset_deassert(&eqos->reset_ctl); + if (ret < 0) { + error("reset_deassert() failed: %d", ret); + return ret; + } + + debug("%s: OK\n", __func__); + return 0; +} + +static int eqos_stop_resets_tegra186(struct udevice *dev) +{ + struct eqos_priv *eqos = dev_get_priv(dev); + + reset_assert(&eqos->reset_ctl); + dm_gpio_set_value(&eqos->phy_reset_gpio, 1); + + return 0; +} + +static int eqos_calibrate_pads_tegra186(struct udevice *dev) +{ + struct eqos_priv *eqos = dev_get_priv(dev); + int ret; + + debug("%s(dev=%p):\n", __func__, dev); + + setbits_le32(&eqos->tegra186_regs->sdmemcomppadctrl, + EQOS_SDMEMCOMPPADCTRL_PAD_E_INPUT_OR_E_PWRD); + + udelay(1); + + setbits_le32(&eqos->tegra186_regs->auto_cal_config, + EQOS_AUTO_CAL_CONFIG_START | EQOS_AUTO_CAL_CONFIG_ENABLE); + + ret = wait_for_bit(__func__, &eqos->tegra186_regs->auto_cal_status, + EQOS_AUTO_CAL_STATUS_ACTIVE, true, 10, false); + if (ret) { + error("calibrate didn't start"); + goto failed; + } + + ret = wait_for_bit(__func__, &eqos->tegra186_regs->auto_cal_status, + EQOS_AUTO_CAL_STATUS_ACTIVE, false, 10, false); + if (ret) { + error("calibrate didn't finish"); + goto failed; + } + + ret = 0; + +failed: + clrbits_le32(&eqos->tegra186_regs->sdmemcomppadctrl, + EQOS_SDMEMCOMPPADCTRL_PAD_E_INPUT_OR_E_PWRD); + + debug("%s: returns %d\n", __func__, ret); + + return ret; +} + +static int eqos_disable_calibration_tegra186(struct udevice *dev) +{ + struct eqos_priv *eqos = dev_get_priv(dev); + + debug("%s(dev=%p):\n", __func__, dev); + + clrbits_le32(&eqos->tegra186_regs->auto_cal_config, + EQOS_AUTO_CAL_CONFIG_ENABLE); + + return 0; +} + +static ulong eqos_get_tick_clk_rate_tegra186(struct udevice *dev) +{ + struct eqos_priv *eqos = dev_get_priv(dev); + + return clk_get_rate(&eqos->clk_slave_bus); +} + +static int eqos_set_full_duplex(struct udevice *dev) +{ + struct eqos_priv *eqos = dev_get_priv(dev); + + debug("%s(dev=%p):\n", __func__, dev); + + setbits_le32(&eqos->mac_regs->configuration, EQOS_MAC_CONFIGURATION_DM); + + return 0; +} + +static int eqos_set_half_duplex(struct udevice *dev) +{ + struct eqos_priv *eqos = dev_get_priv(dev); + + debug("%s(dev=%p):\n", __func__, dev); + + clrbits_le32(&eqos->mac_regs->configuration, EQOS_MAC_CONFIGURATION_DM); + + /* WAR: Flush TX queue when switching to half-duplex */ + setbits_le32(&eqos->mtl_regs->txq0_operation_mode, + EQOS_MTL_TXQ0_OPERATION_MODE_FTQ); + + return 0; +} + +static int eqos_set_gmii_speed(struct udevice *dev) +{ + struct eqos_priv *eqos = dev_get_priv(dev); + + debug("%s(dev=%p):\n", __func__, dev); + + clrbits_le32(&eqos->mac_regs->configuration, + EQOS_MAC_CONFIGURATION_PS | EQOS_MAC_CONFIGURATION_FES); + + return 0; +} + +static int eqos_set_mii_speed_100(struct udevice *dev) +{ + struct eqos_priv *eqos = dev_get_priv(dev); + + debug("%s(dev=%p):\n", __func__, dev); + + setbits_le32(&eqos->mac_regs->configuration, + EQOS_MAC_CONFIGURATION_PS | EQOS_MAC_CONFIGURATION_FES); + + return 0; +} + +static int eqos_set_mii_speed_10(struct udevice *dev) +{ + struct eqos_priv *eqos = dev_get_priv(dev); + + debug("%s(dev=%p):\n", __func__, dev); + + clrsetbits_le32(&eqos->mac_regs->configuration, + EQOS_MAC_CONFIGURATION_FES, EQOS_MAC_CONFIGURATION_PS); + + return 0; +} + +static int eqos_set_tx_clk_speed_tegra186(struct udevice *dev) +{ + struct eqos_priv *eqos = dev_get_priv(dev); + ulong rate; + int ret; + + debug("%s(dev=%p):\n", __func__, dev); + + switch (eqos->phy->speed) { + case SPEED_1000: + rate = 125 * 1000 * 1000; + break; + case SPEED_100: + rate = 25 * 1000 * 1000; + break; + case SPEED_10: + rate = 2.5 * 1000 * 1000; + break; + default: + error("invalid speed %d", eqos->phy->speed); + return -EINVAL; + } + + ret = clk_set_rate(&eqos->clk_tx, rate); + if (ret < 0) { + error("clk_set_rate(tx_clk, %lu) failed: %d", rate, ret); + return ret; + } + + return 0; +} + +static int eqos_adjust_link(struct udevice *dev) +{ + struct eqos_priv *eqos = dev_get_priv(dev); + int ret; + bool en_calibration; + + debug("%s(dev=%p):\n", __func__, dev); + + if (eqos->phy->duplex) + ret = eqos_set_full_duplex(dev); + else + ret = eqos_set_half_duplex(dev); + if (ret < 0) { + error("eqos_set_*_duplex() failed: %d", ret); + return ret; + } + + switch (eqos->phy->speed) { + case SPEED_1000: + en_calibration = true; + ret = eqos_set_gmii_speed(dev); + break; + case SPEED_100: + en_calibration = true; + ret = eqos_set_mii_speed_100(dev); + break; + case SPEED_10: + en_calibration = false; + ret = eqos_set_mii_speed_10(dev); + break; + default: + error("invalid speed %d", eqos->phy->speed); + return -EINVAL; + } + if (ret < 0) { + error("eqos_set_*mii_speed*() failed: %d", ret); + return ret; + } + + if (en_calibration) { + ret = eqos_calibrate_pads_tegra186(dev); + if (ret < 0) { + error("eqos_calibrate_pads_tegra186() failed: %d", ret); + return ret; + } + } else { + ret = eqos_disable_calibration_tegra186(dev); + if (ret < 0) { + error("eqos_disable_calibration_tegra186() failed: %d", + ret); + return ret; + } + } + + ret = eqos_set_tx_clk_speed_tegra186(dev); + if (ret < 0) { + error("eqos_set_tx_clk_speed_tegra186() failed: %d", ret); + return ret; + } + + return 0; +} + +static int eqos_write_hwaddr(struct udevice *dev) +{ + struct eth_pdata *plat = dev_get_platdata(dev); + struct eqos_priv *eqos = dev_get_priv(dev); + uint32_t val; + + /* + * This function may be called before start() or after stop(). At that + * time, on at least some configurations of the EQoS HW, all clocks to + * the EQoS HW block will be stopped, and a reset signal applied. If + * any register access is attempted in this state, bus timeouts or CPU + * hangs may occur. This check prevents that. + * + * A simple solution to this problem would be to not implement + * write_hwaddr(), since start() always writes the MAC address into HW + * anyway. However, it is desirable to implement write_hwaddr() to + * support the case of SW that runs subsequent to U-Boot which expects + * the MAC address to already be programmed into the EQoS registers, + * which must happen irrespective of whether the U-Boot user (or + * scripts) actually made use of the EQoS device, and hence + * irrespective of whether start() was ever called. + * + * Note that this requirement by subsequent SW is not valid for + * Tegra186, and is likely not valid for any non-PCI instantiation of + * the EQoS HW block. This function is implemented solely as + * future-proofing with the expectation the driver will eventually be + * ported to some system where the expectation above is true. + */ + if (!eqos->config->reg_access_always_ok && !eqos->reg_access_ok) + return 0; + + /* Update the MAC address */ + val = (plat->enetaddr[5] << 8) | + (plat->enetaddr[4]); + writel(val, &eqos->mac_regs->address0_high); + val = (plat->enetaddr[3] << 24) | + (plat->enetaddr[2] << 16) | + (plat->enetaddr[1] << 8) | + (plat->enetaddr[0]); + writel(val, &eqos->mac_regs->address0_low); + + return 0; +} + +static int eqos_start(struct udevice *dev) +{ + struct eqos_priv *eqos = dev_get_priv(dev); + int ret, i; + ulong rate; + u32 val, tx_fifo_sz, rx_fifo_sz, tqs, rqs, pbl; + ulong last_rx_desc; + + debug("%s(dev=%p):\n", __func__, dev); + + eqos->tx_desc_idx = 0; + eqos->rx_desc_idx = 0; + + ret = eqos_start_clks_tegra186(dev); + if (ret < 0) { + error("eqos_start_clks_tegra186() failed: %d", ret); + goto err; + } + + ret = eqos_start_resets_tegra186(dev); + if (ret < 0) { + error("eqos_start_resets_tegra186() failed: %d", ret); + goto err_stop_clks; + } + + udelay(10); + + eqos->reg_access_ok = true; + + ret = wait_for_bit(__func__, &eqos->dma_regs->mode, + EQOS_DMA_MODE_SWR, false, 10, false); + if (ret) { + error("EQOS_DMA_MODE_SWR stuck"); + goto err_stop_resets; + } + + ret = eqos_calibrate_pads_tegra186(dev); + if (ret < 0) { + error("eqos_calibrate_pads_tegra186() failed: %d", ret); + goto err_stop_resets; + } + + rate = eqos_get_tick_clk_rate_tegra186(dev); + val = (rate / 1000000) - 1; + writel(val, &eqos->mac_regs->us_tic_counter); + + eqos->phy = phy_connect(eqos->mii, 0, dev, 0); + if (!eqos->phy) { + error("phy_connect() failed"); + goto err_stop_resets; + } + ret = phy_config(eqos->phy); + if (ret < 0) { + error("phy_config() failed: %d", ret); + goto err_shutdown_phy; + } + ret = phy_startup(eqos->phy); + if (ret < 0) { + error("phy_startup() failed: %d", ret); + goto err_shutdown_phy; + } + + if (!eqos->phy->link) { + error("No link"); + goto err_shutdown_phy; + } + + ret = eqos_adjust_link(dev); + if (ret < 0) { + error("eqos_adjust_link() failed: %d", ret); + goto err_shutdown_phy; + } + + /* Configure MTL */ + + /* Enable Store and Forward mode for TX */ + /* Program Tx operating mode */ + setbits_le32(&eqos->mtl_regs->txq0_operation_mode, + EQOS_MTL_TXQ0_OPERATION_MODE_TSF | + (EQOS_MTL_TXQ0_OPERATION_MODE_TXQEN_ENABLED << + EQOS_MTL_TXQ0_OPERATION_MODE_TXQEN_SHIFT)); + + /* Transmit Queue weight */ + writel(0x10, &eqos->mtl_regs->txq0_quantum_weight); + + /* Enable Store and Forward mode for RX, since no jumbo frame */ + setbits_le32(&eqos->mtl_regs->rxq0_operation_mode, + EQOS_MTL_RXQ0_OPERATION_MODE_RSF); + + /* Transmit/Receive queue fifo size; use all RAM for 1 queue */ + val = readl(&eqos->mac_regs->hw_feature1); + tx_fifo_sz = (val >> EQOS_MAC_HW_FEATURE1_TXFIFOSIZE_SHIFT) & + EQOS_MAC_HW_FEATURE1_TXFIFOSIZE_MASK; + rx_fifo_sz = (val >> EQOS_MAC_HW_FEATURE1_RXFIFOSIZE_SHIFT) & + EQOS_MAC_HW_FEATURE1_RXFIFOSIZE_MASK; + + /* + * r/tx_fifo_sz is encoded as log2(n / 128). Undo that by shifting. + * r/tqs is encoded as (n / 256) - 1. + */ + tqs = (128 << tx_fifo_sz) / 256 - 1; + rqs = (128 << rx_fifo_sz) / 256 - 1; + + clrsetbits_le32(&eqos->mtl_regs->txq0_operation_mode, + EQOS_MTL_TXQ0_OPERATION_MODE_TQS_MASK << + EQOS_MTL_TXQ0_OPERATION_MODE_TQS_SHIFT, + tqs << EQOS_MTL_TXQ0_OPERATION_MODE_TQS_SHIFT); + clrsetbits_le32(&eqos->mtl_regs->rxq0_operation_mode, + EQOS_MTL_RXQ0_OPERATION_MODE_RQS_MASK << + EQOS_MTL_RXQ0_OPERATION_MODE_RQS_SHIFT, + rqs << EQOS_MTL_RXQ0_OPERATION_MODE_RQS_SHIFT); + + /* Flow control used only if each channel gets 4KB or more FIFO */ + if (rqs >= ((4096 / 256) - 1)) { + u32 rfd, rfa; + + setbits_le32(&eqos->mtl_regs->rxq0_operation_mode, + EQOS_MTL_RXQ0_OPERATION_MODE_EHFC); + + /* + * Set Threshold for Activating Flow Contol space for min 2 + * frames ie, (1500 * 1) = 1500 bytes. + * + * Set Threshold for Deactivating Flow Contol for space of + * min 1 frame (frame size 1500bytes) in receive fifo + */ + if (rqs == ((4096 / 256) - 1)) { + /* + * This violates the above formula because of FIFO size + * limit therefore overflow may occur inspite of this. + */ + rfd = 0x3; /* Full-3K */ + rfa = 0x1; /* Full-1.5K */ + } else if (rqs == ((8192 / 256) - 1)) { + rfd = 0x6; /* Full-4K */ + rfa = 0xa; /* Full-6K */ + } else if (rqs == ((16384 / 256) - 1)) { + rfd = 0x6; /* Full-4K */ + rfa = 0x12; /* Full-10K */ + } else { + rfd = 0x6; /* Full-4K */ + rfa = 0x1E; /* Full-16K */ + } + + clrsetbits_le32(&eqos->mtl_regs->rxq0_operation_mode, + (EQOS_MTL_RXQ0_OPERATION_MODE_RFD_MASK << + EQOS_MTL_RXQ0_OPERATION_MODE_RFD_SHIFT) | + (EQOS_MTL_RXQ0_OPERATION_MODE_RFA_MASK << + EQOS_MTL_RXQ0_OPERATION_MODE_RFA_SHIFT), + (rfd << + EQOS_MTL_RXQ0_OPERATION_MODE_RFD_SHIFT) | + (rfa << + EQOS_MTL_RXQ0_OPERATION_MODE_RFA_SHIFT)); + } + + /* Configure MAC */ + + clrsetbits_le32(&eqos->mac_regs->rxq_ctrl0, + EQOS_MAC_RXQ_CTRL0_RXQ0EN_MASK << + EQOS_MAC_RXQ_CTRL0_RXQ0EN_SHIFT, + EQOS_MAC_RXQ_CTRL0_RXQ0EN_ENABLED_DCB << + EQOS_MAC_RXQ_CTRL0_RXQ0EN_SHIFT); + + /* Set TX flow control parameters */ + /* Set Pause Time */ + setbits_le32(&eqos->mac_regs->q0_tx_flow_ctrl, + 0xffff << EQOS_MAC_Q0_TX_FLOW_CTRL_PT_SHIFT); + /* Assign priority for TX flow control */ + clrbits_le32(&eqos->mac_regs->txq_prty_map0, + EQOS_MAC_TXQ_PRTY_MAP0_PSTQ0_MASK << + EQOS_MAC_TXQ_PRTY_MAP0_PSTQ0_SHIFT); + /* Assign priority for RX flow control */ + clrbits_le32(&eqos->mac_regs->rxq_ctrl2, + EQOS_MAC_RXQ_CTRL2_PSRQ0_MASK << + EQOS_MAC_RXQ_CTRL2_PSRQ0_SHIFT); + /* Enable flow control */ + setbits_le32(&eqos->mac_regs->q0_tx_flow_ctrl, + EQOS_MAC_Q0_TX_FLOW_CTRL_TFE); + setbits_le32(&eqos->mac_regs->rx_flow_ctrl, + EQOS_MAC_RX_FLOW_CTRL_RFE); + + clrsetbits_le32(&eqos->mac_regs->configuration, + EQOS_MAC_CONFIGURATION_GPSLCE | + EQOS_MAC_CONFIGURATION_WD | + EQOS_MAC_CONFIGURATION_JD | + EQOS_MAC_CONFIGURATION_JE, + EQOS_MAC_CONFIGURATION_CST | + EQOS_MAC_CONFIGURATION_ACS); + + eqos_write_hwaddr(dev); + + /* Configure DMA */ + + /* Enable OSP mode */ + setbits_le32(&eqos->dma_regs->ch0_tx_control, + EQOS_DMA_CH0_TX_CONTROL_OSP); + + /* RX buffer size. Must be a multiple of bus width */ + clrsetbits_le32(&eqos->dma_regs->ch0_rx_control, + EQOS_DMA_CH0_RX_CONTROL_RBSZ_MASK << + EQOS_DMA_CH0_RX_CONTROL_RBSZ_SHIFT, + EQOS_MAX_PACKET_SIZE << + EQOS_DMA_CH0_RX_CONTROL_RBSZ_SHIFT); + + setbits_le32(&eqos->dma_regs->ch0_control, + EQOS_DMA_CH0_CONTROL_PBLX8); + + /* + * Burst length must be < 1/2 FIFO size. + * FIFO size in tqs is encoded as (n / 256) - 1. + * Each burst is n * 8 (PBLX8) * 16 (AXI width) == 128 bytes. + * Half of n * 256 is n * 128, so pbl == tqs, modulo the -1. + */ + pbl = tqs + 1; + if (pbl > 32) + pbl = 32; + clrsetbits_le32(&eqos->dma_regs->ch0_tx_control, + EQOS_DMA_CH0_TX_CONTROL_TXPBL_MASK << + EQOS_DMA_CH0_TX_CONTROL_TXPBL_SHIFT, + pbl << EQOS_DMA_CH0_TX_CONTROL_TXPBL_SHIFT); + + clrsetbits_le32(&eqos->dma_regs->ch0_rx_control, + EQOS_DMA_CH0_RX_CONTROL_RXPBL_MASK << + EQOS_DMA_CH0_RX_CONTROL_RXPBL_SHIFT, + 8 << EQOS_DMA_CH0_RX_CONTROL_RXPBL_SHIFT); + + /* DMA performance configuration */ + val = (2 << EQOS_DMA_SYSBUS_MODE_RD_OSR_LMT_SHIFT) | + EQOS_DMA_SYSBUS_MODE_EAME | EQOS_DMA_SYSBUS_MODE_BLEN16 | + EQOS_DMA_SYSBUS_MODE_BLEN8 | EQOS_DMA_SYSBUS_MODE_BLEN4; + writel(val, &eqos->dma_regs->sysbus_mode); + + /* Set up descriptors */ + + memset(eqos->descs, 0, EQOS_DESCRIPTORS_SIZE); + for (i = 0; i < EQOS_DESCRIPTORS_RX; i++) { + struct eqos_desc *rx_desc = &(eqos->rx_descs[i]); + rx_desc->des0 = (u32)(ulong)(eqos->rx_dma_buf + + (i * EQOS_MAX_PACKET_SIZE)); + rx_desc->des3 |= EQOS_DESC3_OWN | EQOS_DESC3_BUF1V; + } + flush_cache((unsigned long)eqos->descs, EQOS_DESCRIPTORS_SIZE); + + writel(0, &eqos->dma_regs->ch0_txdesc_list_haddress); + writel((ulong)eqos->tx_descs, &eqos->dma_regs->ch0_txdesc_list_address); + writel(EQOS_DESCRIPTORS_TX - 1, + &eqos->dma_regs->ch0_txdesc_ring_length); + + writel(0, &eqos->dma_regs->ch0_rxdesc_list_haddress); + writel((ulong)eqos->rx_descs, &eqos->dma_regs->ch0_rxdesc_list_address); + writel(EQOS_DESCRIPTORS_RX - 1, + &eqos->dma_regs->ch0_rxdesc_ring_length); + + /* Enable everything */ + + setbits_le32(&eqos->mac_regs->configuration, + EQOS_MAC_CONFIGURATION_TE | EQOS_MAC_CONFIGURATION_RE); + + setbits_le32(&eqos->dma_regs->ch0_tx_control, + EQOS_DMA_CH0_TX_CONTROL_ST); + setbits_le32(&eqos->dma_regs->ch0_rx_control, + EQOS_DMA_CH0_RX_CONTROL_SR); + + /* TX tail pointer not written until we need to TX a packet */ + /* + * Point RX tail pointer at last descriptor. Ideally, we'd point at the + * first descriptor, implying all descriptors were available. However, + * that's not distinguishable from none of the descriptors being + * available. + */ + last_rx_desc = (ulong)&(eqos->rx_descs[(EQOS_DESCRIPTORS_RX - 1)]); + writel(last_rx_desc, &eqos->dma_regs->ch0_rxdesc_tail_pointer); + + eqos->started = true; + + debug("%s: OK\n", __func__); + return 0; + +err_shutdown_phy: + phy_shutdown(eqos->phy); + eqos->phy = NULL; +err_stop_resets: + eqos_stop_resets_tegra186(dev); +err_stop_clks: + eqos_stop_clks_tegra186(dev); +err: + error("FAILED: %d", ret); + return ret; +} + +void eqos_stop(struct udevice *dev) +{ + struct eqos_priv *eqos = dev_get_priv(dev); + int i; + + debug("%s(dev=%p):\n", __func__, dev); + + if (!eqos->started) + return; + eqos->started = false; + eqos->reg_access_ok = false; + + /* Disable TX DMA */ + clrbits_le32(&eqos->dma_regs->ch0_tx_control, + EQOS_DMA_CH0_TX_CONTROL_ST); + + /* Wait for TX all packets to drain out of MTL */ + for (i = 0; i < 1000000; i++) { + u32 val = readl(&eqos->mtl_regs->txq0_debug); + u32 trcsts = (val >> EQOS_MTL_TXQ0_DEBUG_TRCSTS_SHIFT) & + EQOS_MTL_TXQ0_DEBUG_TRCSTS_MASK; + u32 txqsts = val & EQOS_MTL_TXQ0_DEBUG_TXQSTS; + if ((trcsts != 1) && (!txqsts)) + break; + } + + /* Turn off MAC TX and RX */ + clrbits_le32(&eqos->mac_regs->configuration, + EQOS_MAC_CONFIGURATION_TE | EQOS_MAC_CONFIGURATION_RE); + + /* Wait for all RX packets to drain out of MTL */ + for (i = 0; i < 1000000; i++) { + u32 val = readl(&eqos->mtl_regs->rxq0_debug); + u32 prxq = (val >> EQOS_MTL_RXQ0_DEBUG_PRXQ_SHIFT) & + EQOS_MTL_RXQ0_DEBUG_PRXQ_MASK; + u32 rxqsts = (val >> EQOS_MTL_RXQ0_DEBUG_RXQSTS_SHIFT) & + EQOS_MTL_RXQ0_DEBUG_RXQSTS_MASK; + if ((!prxq) && (!rxqsts)) + break; + } + + /* Turn off RX DMA */ + clrbits_le32(&eqos->dma_regs->ch0_rx_control, + EQOS_DMA_CH0_RX_CONTROL_SR); + + if (eqos->phy) { + phy_shutdown(eqos->phy); + eqos->phy = NULL; + } + eqos_stop_resets_tegra186(dev); + eqos_stop_clks_tegra186(dev); + + debug("%s: OK\n", __func__); +} + +int eqos_send(struct udevice *dev, void *packet, int length) +{ + struct eqos_priv *eqos = dev_get_priv(dev); + struct eqos_desc *tx_desc; + int i; + + debug("%s(dev=%p, packet=%p, length=%d):\n", __func__, dev, packet, + length); + + memcpy(eqos->tx_dma_buf, packet, length); + eqos_flush_buffer(eqos->tx_dma_buf, length); + + tx_desc = &(eqos->tx_descs[eqos->tx_desc_idx]); + eqos->tx_desc_idx++; + eqos->tx_desc_idx %= EQOS_DESCRIPTORS_TX; + + tx_desc->des0 = (ulong)eqos->tx_dma_buf; + tx_desc->des1 = 0; + tx_desc->des2 = length; + /* + * Make sure that if HW sees the _OWN write below, it will see all the + * writes to the rest of the descriptor too. + */ + mb(); + tx_desc->des3 = EQOS_DESC3_OWN | EQOS_DESC3_FD | EQOS_DESC3_LD | length; + eqos_flush_desc(tx_desc); + + writel((ulong)(tx_desc + 1), &eqos->dma_regs->ch0_txdesc_tail_pointer); + + for (i = 0; i < 1000000; i++) { + eqos_inval_desc(tx_desc); + if (!(readl(&tx_desc->des3) & EQOS_DESC3_OWN)) + return 0; + udelay(1); + } + + debug("%s: TX timeout\n", __func__); + + return -ETIMEDOUT; +} + +int eqos_recv(struct udevice *dev, int flags, uchar **packetp) +{ + struct eqos_priv *eqos = dev_get_priv(dev); + struct eqos_desc *rx_desc; + int length; + + debug("%s(dev=%p, flags=%x):\n", __func__, dev, flags); + + rx_desc = &(eqos->rx_descs[eqos->rx_desc_idx]); + if (rx_desc->des3 & EQOS_DESC3_OWN) { + debug("%s: RX packet not available\n", __func__); + return -EAGAIN; + } + + *packetp = eqos->rx_dma_buf + + (eqos->rx_desc_idx * EQOS_MAX_PACKET_SIZE); + length = rx_desc->des3 & 0x7fff; + debug("%s: *packetp=%p, length=%d\n", __func__, *packetp, length); + + eqos_inval_buffer(*packetp, length); + + return length; +} + +int eqos_free_pkt(struct udevice *dev, uchar *packet, int length) +{ + struct eqos_priv *eqos = dev_get_priv(dev); + uchar *packet_expected; + struct eqos_desc *rx_desc; + + debug("%s(packet=%p, length=%d)\n", __func__, packet, length); + + packet_expected = eqos->rx_dma_buf + + (eqos->rx_desc_idx * EQOS_MAX_PACKET_SIZE); + if (packet != packet_expected) { + debug("%s: Unexpected packet (expected %p)\n", __func__, + packet_expected); + return -EINVAL; + } + + rx_desc = &(eqos->rx_descs[eqos->rx_desc_idx]); + rx_desc->des0 = (u32)(ulong)packet; + rx_desc->des1 = 0; + rx_desc->des2 = 0; + /* + * Make sure that if HW sees the _OWN write below, it will see all the + * writes to the rest of the descriptor too. + */ + mb(); + rx_desc->des3 |= EQOS_DESC3_OWN | EQOS_DESC3_BUF1V; + eqos_flush_desc(rx_desc); + + writel((ulong)rx_desc, &eqos->dma_regs->ch0_rxdesc_tail_pointer); + + eqos->rx_desc_idx++; + eqos->rx_desc_idx %= EQOS_DESCRIPTORS_RX; + + return 0; +} + +static int eqos_probe_resources_core(struct udevice *dev) +{ + struct eqos_priv *eqos = dev_get_priv(dev); + int ret; + + debug("%s(dev=%p):\n", __func__, dev); + + eqos->descs = eqos_alloc_descs(EQOS_DESCRIPTORS_TX + + EQOS_DESCRIPTORS_RX); + if (!eqos->descs) { + debug("%s: eqos_alloc_descs() failed\n", __func__); + ret = -ENOMEM; + goto err; + } + eqos->tx_descs = (struct eqos_desc *)eqos->descs; + eqos->rx_descs = (eqos->tx_descs + EQOS_DESCRIPTORS_TX); + debug("%s: tx_descs=%p, rx_descs=%p\n", __func__, eqos->tx_descs, + eqos->rx_descs); + + eqos->tx_dma_buf = memalign(EQOS_BUFFER_ALIGN, EQOS_MAX_PACKET_SIZE); + if (!eqos->tx_dma_buf) { + debug("%s: memalign(tx_dma_buf) failed\n", __func__); + ret = -ENOMEM; + goto err_free_descs; + } + debug("%s: rx_dma_buf=%p\n", __func__, eqos->rx_dma_buf); + + eqos->rx_dma_buf = memalign(EQOS_BUFFER_ALIGN, EQOS_RX_BUFFER_SIZE); + if (!eqos->rx_dma_buf) { + debug("%s: memalign(rx_dma_buf) failed\n", __func__); + ret = -ENOMEM; + goto err_free_tx_dma_buf; + } + debug("%s: tx_dma_buf=%p\n", __func__, eqos->tx_dma_buf); + + eqos->rx_pkt = malloc(EQOS_MAX_PACKET_SIZE); + if (!eqos->rx_pkt) { + debug("%s: malloc(rx_pkt) failed\n", __func__); + ret = -ENOMEM; + goto err_free_rx_dma_buf; + } + debug("%s: rx_pkt=%p\n", __func__, eqos->rx_pkt); + + debug("%s: OK\n", __func__); + return 0; + +err_free_rx_dma_buf: + free(eqos->rx_dma_buf); +err_free_tx_dma_buf: + free(eqos->tx_dma_buf); +err_free_descs: + eqos_free_descs(eqos->descs); +err: + + debug("%s: returns %d\n", __func__, ret); + return ret; +} + +static int eqos_remove_resources_core(struct udevice *dev) +{ + struct eqos_priv *eqos = dev_get_priv(dev); + + debug("%s(dev=%p):\n", __func__, dev); + + free(eqos->rx_pkt); + free(eqos->rx_dma_buf); + free(eqos->tx_dma_buf); + eqos_free_descs(eqos->descs); + + debug("%s: OK\n", __func__); + return 0; +} + +static int eqos_probe_resources_tegra186(struct udevice *dev) +{ + struct eqos_priv *eqos = dev_get_priv(dev); + int ret; + + debug("%s(dev=%p):\n", __func__, dev); + + ret = reset_get_by_name(dev, "eqos", &eqos->reset_ctl); + if (ret) { + error("reset_get_by_name(rst) failed: %d", ret); + return ret; + } + + ret = gpio_request_by_name(dev, "phy-reset-gpios", 0, + &eqos->phy_reset_gpio, + GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE); + if (ret) { + error("gpio_request_by_name(phy reset) failed: %d", ret); + goto err_free_reset_eqos; + } + + ret = clk_get_by_name(dev, "slave_bus", &eqos->clk_slave_bus); + if (ret) { + error("clk_get_by_name(slave_bus) failed: %d", ret); + goto err_free_gpio_phy_reset; + } + + ret = clk_get_by_name(dev, "master_bus", &eqos->clk_master_bus); + if (ret) { + error("clk_get_by_name(master_bus) failed: %d", ret); + goto err_free_clk_slave_bus; + } + + ret = clk_get_by_name(dev, "rx", &eqos->clk_rx); + if (ret) { + error("clk_get_by_name(rx) failed: %d", ret); + goto err_free_clk_master_bus; + } + + ret = clk_get_by_name(dev, "ptp_ref", &eqos->clk_ptp_ref); + if (ret) { + error("clk_get_by_name(ptp_ref) failed: %d", ret); + goto err_free_clk_rx; + return ret; + } + + ret = clk_get_by_name(dev, "tx", &eqos->clk_tx); + if (ret) { + error("clk_get_by_name(tx) failed: %d", ret); + goto err_free_clk_ptp_ref; + } + + debug("%s: OK\n", __func__); + return 0; + +err_free_clk_ptp_ref: + clk_free(&eqos->clk_ptp_ref); +err_free_clk_rx: + clk_free(&eqos->clk_rx); +err_free_clk_master_bus: + clk_free(&eqos->clk_master_bus); +err_free_clk_slave_bus: + clk_free(&eqos->clk_slave_bus); +err_free_gpio_phy_reset: + dm_gpio_free(dev, &eqos->phy_reset_gpio); +err_free_reset_eqos: + reset_free(&eqos->reset_ctl); + + debug("%s: returns %d\n", __func__, ret); + return ret; +} + +static int eqos_remove_resources_tegra186(struct udevice *dev) +{ + struct eqos_priv *eqos = dev_get_priv(dev); + + debug("%s(dev=%p):\n", __func__, dev); + + clk_free(&eqos->clk_tx); + clk_free(&eqos->clk_ptp_ref); + clk_free(&eqos->clk_rx); + clk_free(&eqos->clk_slave_bus); + clk_free(&eqos->clk_master_bus); + dm_gpio_free(dev, &eqos->phy_reset_gpio); + reset_free(&eqos->reset_ctl); + + debug("%s: OK\n", __func__); + return 0; +} + +static int eqos_probe(struct udevice *dev) +{ + struct eqos_priv *eqos = dev_get_priv(dev); + int ret; + + debug("%s(dev=%p):\n", __func__, dev); + + eqos->dev = dev; + eqos->config = (void *)dev_get_driver_data(dev); + + eqos->regs = dev_get_addr(dev); + if (eqos->regs == FDT_ADDR_T_NONE) { + error("dev_get_addr() failed"); + return -ENODEV; + } + eqos->mac_regs = (void *)(eqos->regs + EQOS_MAC_REGS_BASE); + eqos->mtl_regs = (void *)(eqos->regs + EQOS_MTL_REGS_BASE); + eqos->dma_regs = (void *)(eqos->regs + EQOS_DMA_REGS_BASE); + eqos->tegra186_regs = (void *)(eqos->regs + EQOS_TEGRA186_REGS_BASE); + + ret = eqos_probe_resources_core(dev); + if (ret < 0) { + error("eqos_probe_resources_core() failed: %d", ret); + return ret; + } + + ret = eqos_probe_resources_tegra186(dev); + if (ret < 0) { + error("eqos_probe_resources_tegra186() failed: %d", ret); + goto err_remove_resources_core; + } + + eqos->mii = mdio_alloc(); + if (!eqos->mii) { + error("mdio_alloc() failed"); + goto err_remove_resources_tegra; + } + eqos->mii->read = eqos_mdio_read; + eqos->mii->write = eqos_mdio_write; + eqos->mii->priv = eqos; + strcpy(eqos->mii->name, dev->name); + + ret = mdio_register(eqos->mii); + if (ret < 0) { + error("mdio_register() failed: %d", ret); + goto err_free_mdio; + } + + debug("%s: OK\n", __func__); + return 0; + +err_free_mdio: + mdio_free(eqos->mii); +err_remove_resources_tegra: + eqos_remove_resources_tegra186(dev); +err_remove_resources_core: + eqos_remove_resources_core(dev); + + debug("%s: returns %d\n", __func__, ret); + return ret; +} + +static int eqos_remove(struct udevice *dev) +{ + struct eqos_priv *eqos = dev_get_priv(dev); + + debug("%s(dev=%p):\n", __func__, dev); + + mdio_unregister(eqos->mii); + mdio_free(eqos->mii); + eqos_remove_resources_tegra186(dev); + eqos_probe_resources_core(dev); + + debug("%s: OK\n", __func__); + return 0; +} + +static const struct eth_ops eqos_ops = { + .start = eqos_start, + .stop = eqos_stop, + .send = eqos_send, + .recv = eqos_recv, + .free_pkt = eqos_free_pkt, + .write_hwaddr = eqos_write_hwaddr, +}; + +static const struct eqos_config eqos_tegra186_config = { + .reg_access_always_ok = false, +}; + +static const struct udevice_id eqos_ids[] = { + { + .compatible = "nvidia,tegra186-eqos", + .data = (ulong)&eqos_tegra186_config + }, + { } +}; + +U_BOOT_DRIVER(eth_eqos) = { + .name = "eth_eqos", + .id = UCLASS_ETH, + .of_match = eqos_ids, + .probe = eqos_probe, + .remove = eqos_remove, + .ops = &eqos_ops, + .priv_auto_alloc_size = sizeof(struct eqos_priv), + .platdata_auto_alloc_size = sizeof(struct eth_pdata), +}; diff --git a/drivers/net/mvgbe.c b/drivers/net/mvgbe.c index c784cdc..f833efb 100644 --- a/drivers/net/mvgbe.c +++ b/drivers/net/mvgbe.c @@ -177,25 +177,6 @@ static int smi_reg_write(struct mii_dev *bus, int phy_adr, int devad, } #endif -#if defined(CONFIG_PHYLIB) -int mvgbe_phy_read(struct mii_dev *bus, int phy_addr, int dev_addr, - int reg_addr) -{ - u16 data; - int ret; - ret = smi_reg_read(bus->name, phy_addr, reg_addr, &data); - if (ret) - return ret; - return data; -} - -int mvgbe_phy_write(struct mii_dev *bus, int phy_addr, int dev_addr, - int reg_addr, u16 data) -{ - return smi_reg_write(bus->name, phy_addr, reg_addr, data); -} -#endif - /* Stop and checks all queues */ static void stop_queue(u32 * qreg) { @@ -676,8 +657,8 @@ int mvgbe_phylib_init(struct eth_device *dev, int phyid) printf("mdio_alloc failed\n"); return -ENOMEM; } - bus->read = mvgbe_phy_read; - bus->write = mvgbe_phy_write; + bus->read = smi_reg_read; + bus->write = smi_reg_write; strcpy(bus->name, dev->name); ret = mdio_register(bus); @@ -688,7 +669,7 @@ int mvgbe_phylib_init(struct eth_device *dev, int phyid) } /* Set phy address of the port */ - mvgbe_phy_write(bus, MV_PHY_ADR_REQUEST, 0, MV_PHY_ADR_REQUEST, phyid); + smi_reg_write(bus, MV_PHY_ADR_REQUEST, 0, MV_PHY_ADR_REQUEST, phyid); phydev = phy_connect(bus, phyid, dev, PHY_INTERFACE_MODE_RGMII); if (!phydev) { diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c index 6b313a9..28a1401 100644 --- a/drivers/net/phy/micrel.c +++ b/drivers/net/phy/micrel.c @@ -415,11 +415,31 @@ static int ksz9031_of_config(struct phy_device *phydev) return 0; } + +static int ksz9031_center_flp_timing(struct phy_device *phydev) +{ + struct phy_driver *drv = phydev->drv; + int ret = 0; + + if (!drv || !drv->writeext) + return -EOPNOTSUPP; + + ret = drv->writeext(phydev, 0, 0, MII_KSZ9031_FLP_BURST_TX_LO, 0x1A80); + if (ret) + return ret; + + ret = drv->writeext(phydev, 0, 0, MII_KSZ9031_FLP_BURST_TX_HI, 0x6); + return ret; +} #else static int ksz9031_of_config(struct phy_device *phydev) { return 0; } +static int ksz9031_center_flp_timing(struct phy_device *phydev) +{ + return 0; +} #endif /* Accessors to extended registers*/ @@ -472,6 +492,9 @@ static int ksz9031_config(struct phy_device *phydev) ret = ksz9031_of_config(phydev); if (ret) return ret; + ret = ksz9031_center_flp_timing(phydev); + if (ret) + return ret; return genphy_config(phydev); } diff --git a/include/micrel.h b/include/micrel.h index e6d145d..3e6b531 100644 --- a/include/micrel.h +++ b/include/micrel.h @@ -20,6 +20,9 @@ #define MII_KSZ9031_EXT_RGMII_TX_DATA_SKEW 0x6 #define MII_KSZ9031_EXT_RGMII_CLOCK_SKEW 0x8 +#define MII_KSZ9031_FLP_BURST_TX_LO 0x3 +#define MII_KSZ9031_FLP_BURST_TX_HI 0x4 + /* Registers */ #define MMD_ACCESS_CONTROL 0xd #define MMD_ACCESS_REG_DATA 0xe diff --git a/net/eth-uclass.c b/net/eth-uclass.c index a32961e..1d13011 100644 --- a/net/eth-uclass.c +++ b/net/eth-uclass.c @@ -512,7 +512,8 @@ static int eth_post_probe(struct udevice *dev) eth_setenv_enetaddr_by_index("eth", dev->seq, pdata->enetaddr); printf("\nWarning: %s using MAC address from ROM\n", dev->name); - } else if (is_zero_ethaddr(pdata->enetaddr)) { + } else if (is_zero_ethaddr(pdata->enetaddr) || + !is_valid_ethaddr(pdata->enetaddr)) { #ifdef CONFIG_NET_RANDOM_ETHADDR net_random_ethaddr(pdata->enetaddr); printf("\nWarning: %s (eth%d) using random MAC address - %pM\n", |