Hi folks, Attached is a rebase of a patch submitted to arm-dev that adds support for the networking devices in the QDF2400. It appears that this patch didn't make it into the previous build of the ARM kernel. This patch should apply to HEAD on https://git.centos.org/log/sig-altarch!kernel.git/sig-altarch7-aarch64 Any chance we can get this built and included in a set of install media soon? This patch mentions a lack of hardware for testing the option CONFIG_NET_VENDOR_QUALCOMM, how would we test this? Cheers! -- Brian Stinson -------------- next part -------------- >From 4b9e0f98c3a020a9e45ab5b62846104f1db14dfb Mon Sep 17 00:00:00 2001 From: Christopher Covington <cov at codeaurora.org> Date: Mon, 19 Dec 2016 12:18:40 -0500 Subject: [PATCH] QDF2400 Onboard Networking The QDF2400 family of SoCs from Qualcomm Datacenter Technologies has an on-board Gigabit Ethernet port. Include support for this hardware in CentOS AltArch by backporting 17 patches, which as of 2016-12-17 have been accepted into Linus Torvalds upstream repository. The QCA7000 configuration option, revealed by enabling CONFIG_NET_VENDOR_QUALCOMM, is left unset for now, due to lack of hardware on hand for testing. Signed-off-by: Christopher Covington <cov at codeaurora.org> --- SOURCES/4003-net-qcom-emac.patch | 6495 ++++++++++++++++++++++++++++++++++++++ SOURCES/config-centos-sig | 3 + SPECS/kernel-aarch64.spec | 8 +- 3 files changed, 6505 insertions(+), 1 deletion(-) create mode 100644 SOURCES/4003-net-qcom-emac.patch diff --git a/SOURCES/4003-net-qcom-emac.patch b/SOURCES/4003-net-qcom-emac.patch new file mode 100644 index 0000000..45e21f4 --- /dev/null +++ b/SOURCES/4003-net-qcom-emac.patch @@ -0,0 +1,6495 @@ +From 7f42b6c44827bab80dbfc65d23a04f8c45cb14b8 Mon Sep 17 00:00:00 2001 +From: Timur Tabi <timur at codeaurora.org> +Date: Wed, 31 Aug 2016 18:22:08 -0500 +Subject: [PATCH 01/17] net: emac: emac gigabit ethernet controller driver + +Add support for the Qualcomm Technologies, Inc. EMAC gigabit Ethernet +controller. + +This driver supports the following features: +1) Checksum offload. +2) Interrupt coalescing support. +3) SGMII phy. +4) phylib interface for external phy + +Based on original work by + Niranjana Vishwanathapura <nvishwan at codeaurora.org> + Gilad Avidov <gavidov at codeaurora.org> + +Signed-off-by: Timur Tabi <timur at codeaurora.org> +Signed-off-by: David S. Miller <davem at davemloft.net> +(cherry picked from commit b9b17debc69d27cd55e21ee51a5ba7fc50a426cf) +--- + .../devicetree/bindings/net/qcom-emac.txt | 111 ++ + MAINTAINERS | 6 + + drivers/net/ethernet/qualcomm/Kconfig | 12 + + drivers/net/ethernet/qualcomm/Makefile | 2 + + drivers/net/ethernet/qualcomm/emac/Makefile | 7 + + drivers/net/ethernet/qualcomm/emac/emac-mac.c | 1528 ++++++++++++++++++++ + drivers/net/ethernet/qualcomm/emac/emac-mac.h | 248 ++++ + drivers/net/ethernet/qualcomm/emac/emac-phy.c | 204 +++ + drivers/net/ethernet/qualcomm/emac/emac-phy.h | 33 + + drivers/net/ethernet/qualcomm/emac/emac-sgmii.c | 721 +++++++++ + drivers/net/ethernet/qualcomm/emac/emac-sgmii.h | 24 + + drivers/net/ethernet/qualcomm/emac/emac.c | 743 ++++++++++ + drivers/net/ethernet/qualcomm/emac/emac.h | 335 +++++ + 13 files changed, 3974 insertions(+) + create mode 100644 Documentation/devicetree/bindings/net/qcom-emac.txt + create mode 100644 drivers/net/ethernet/qualcomm/emac/Makefile + create mode 100644 drivers/net/ethernet/qualcomm/emac/emac-mac.c + create mode 100644 drivers/net/ethernet/qualcomm/emac/emac-mac.h + create mode 100644 drivers/net/ethernet/qualcomm/emac/emac-phy.c + create mode 100644 drivers/net/ethernet/qualcomm/emac/emac-phy.h + create mode 100644 drivers/net/ethernet/qualcomm/emac/emac-sgmii.c + create mode 100644 drivers/net/ethernet/qualcomm/emac/emac-sgmii.h + create mode 100644 drivers/net/ethernet/qualcomm/emac/emac.c + create mode 100644 drivers/net/ethernet/qualcomm/emac/emac.h + +diff --git a/Documentation/devicetree/bindings/net/qcom-emac.txt b/Documentation/devicetree/bindings/net/qcom-emac.txt +new file mode 100644 +index 0000000..346e6c7 +--- /dev/null ++++ b/Documentation/devicetree/bindings/net/qcom-emac.txt +@@ -0,0 +1,111 @@ ++Qualcomm Technologies EMAC Gigabit Ethernet Controller ++ ++This network controller consists of two devices: a MAC and an SGMII ++internal PHY. Each device is represented by a device tree node. A phandle ++connects the MAC node to its corresponding internal phy node. Another ++phandle points to the external PHY node. ++ ++Required properties: ++ ++MAC node: ++- compatible : Should be "qcom,fsm9900-emac". ++- reg : Offset and length of the register regions for the device ++- interrupts : Interrupt number used by this controller ++- mac-address : The 6-byte MAC address. If present, it is the default ++ MAC address. ++- internal-phy : phandle to the internal PHY node ++- phy-handle : phandle the the external PHY node ++ ++Internal PHY node: ++- compatible : Should be "qcom,fsm9900-emac-sgmii" or "qcom,qdf2432-emac-sgmii". ++- reg : Offset and length of the register region(s) for the device ++- interrupts : Interrupt number used by this controller ++ ++The external phy child node: ++- reg : The phy address ++ ++Example: ++ ++FSM9900: ++ ++soc { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ emac0: ethernet at feb20000 { ++ compatible = "qcom,fsm9900-emac"; ++ reg = <0xfeb20000 0x10000>, ++ <0xfeb36000 0x1000>; ++ interrupts = <76>; ++ ++ clocks = <&gcc 0>, <&gcc 1>, <&gcc 3>, <&gcc 4>, <&gcc 5>, ++ <&gcc 6>, <&gcc 7>; ++ clock-names = "axi_clk", "cfg_ahb_clk", "high_speed_clk", ++ "mdio_clk", "tx_clk", "rx_clk", "sys_clk"; ++ ++ internal-phy = <&emac_sgmii>; ++ ++ phy-handle = <&phy0>; ++ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ phy0: ethernet-phy at 0 { ++ reg = <0>; ++ }; ++ ++ pinctrl-names = "default"; ++ pinctrl-0 = <&mdio_pins_a>; ++ }; ++ ++ emac_sgmii: ethernet at feb38000 { ++ compatible = "qcom,fsm9900-emac-sgmii"; ++ reg = <0xfeb38000 0x1000>; ++ interrupts = <80>; ++ }; ++ ++ tlmm: pinctrl at fd510000 { ++ compatible = "qcom,fsm9900-pinctrl"; ++ ++ mdio_pins_a: mdio { ++ state { ++ pins = "gpio123", "gpio124"; ++ function = "mdio"; ++ }; ++ }; ++ }; ++ ++ ++QDF2432: ++ ++soc { ++ #address-cells = <2>; ++ #size-cells = <2>; ++ ++ emac0: ethernet at 38800000 { ++ compatible = "qcom,fsm9900-emac"; ++ reg = <0x0 0x38800000 0x0 0x10000>, ++ <0x0 0x38816000 0x0 0x1000>; ++ interrupts = <0 256 4>; ++ ++ clocks = <&gcc 0>, <&gcc 1>, <&gcc 3>, <&gcc 4>, <&gcc 5>, ++ <&gcc 6>, <&gcc 7>; ++ clock-names = "axi_clk", "cfg_ahb_clk", "high_speed_clk", ++ "mdio_clk", "tx_clk", "rx_clk", "sys_clk"; ++ ++ internal-phy = <&emac_sgmii>; ++ ++ phy-handle = <&phy0>; ++ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ phy0: ethernet-phy at 4 { ++ reg = <4>; ++ }; ++ }; ++ ++ emac_sgmii: ethernet at 410400 { ++ compatible = "qcom,qdf2432-emac-sgmii"; ++ reg = <0x0 0x00410400 0x0 0xc00>, /* Base address */ ++ <0x0 0x00410000 0x0 0x400>; /* Per-lane digital */ ++ interrupts = <0 254 1>; ++ }; +diff --git a/MAINTAINERS b/MAINTAINERS +index 5e20026..3e43b25 100644 +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -8999,6 +8999,12 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/kvalo/ath.git + S: Supported + F: drivers/net/wireless/ath/ath10k/ + ++QUALCOMM EMAC GIGABIT ETHERNET DRIVER ++M: Timur Tabi <timur at codeaurora.org> ++L: netdev at vger.kernel.org ++S: Supported ++F: drivers/net/ethernet/qualcomm/emac/ ++ + QUALCOMM HEXAGON ARCHITECTURE + M: Richard Kuo <rkuo at codeaurora.org> + L: linux-hexagon at vger.kernel.org +diff --git a/drivers/net/ethernet/qualcomm/Kconfig b/drivers/net/ethernet/qualcomm/Kconfig +index a76e380..9ba568d 100644 +--- a/drivers/net/ethernet/qualcomm/Kconfig ++++ b/drivers/net/ethernet/qualcomm/Kconfig +@@ -24,4 +24,16 @@ config QCA7000 + To compile this driver as a module, choose M here. The module + will be called qcaspi. + ++config QCOM_EMAC ++ tristate "Qualcomm Technologies, Inc. EMAC Gigabit Ethernet support" ++ select CRC32 ++ select PHYLIB ++ ---help--- ++ This driver supports the Qualcomm Technologies, Inc. Gigabit ++ Ethernet Media Access Controller (EMAC). The controller ++ supports IEEE 802.3-2002, half-duplex mode at 10/100 Mb/s, ++ full-duplex mode at 10/100/1000Mb/s, Wake On LAN (WOL) for ++ low power, Receive-Side Scaling (RSS), and IEEE 1588-2008 ++ Precision Clock Synchronization Protocol. ++ + endif # NET_VENDOR_QUALCOMM +diff --git a/drivers/net/ethernet/qualcomm/Makefile b/drivers/net/ethernet/qualcomm/Makefile +index 9da2d75..aacb0a5 100644 +--- a/drivers/net/ethernet/qualcomm/Makefile ++++ b/drivers/net/ethernet/qualcomm/Makefile +@@ -4,3 +4,5 @@ + + obj-$(CONFIG_QCA7000) += qcaspi.o + qcaspi-objs := qca_spi.o qca_framing.o qca_7k.o qca_debug.o ++ ++obj-y += emac/ +diff --git a/drivers/net/ethernet/qualcomm/emac/Makefile b/drivers/net/ethernet/qualcomm/emac/Makefile +new file mode 100644 +index 0000000..01ee144 +--- /dev/null ++++ b/drivers/net/ethernet/qualcomm/emac/Makefile +@@ -0,0 +1,7 @@ ++# ++# Makefile for the Qualcomm Technologies, Inc. EMAC Gigabit Ethernet driver ++# ++ ++obj-$(CONFIG_QCOM_EMAC) += qcom-emac.o ++ ++qcom-emac-objs := emac.o emac-mac.o emac-phy.o emac-sgmii.o +diff --git a/drivers/net/ethernet/qualcomm/emac/emac-mac.c b/drivers/net/ethernet/qualcomm/emac/emac-mac.c +new file mode 100644 +index 0000000..e97968e +--- /dev/null ++++ b/drivers/net/ethernet/qualcomm/emac/emac-mac.c +@@ -0,0 +1,1528 @@ ++/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that 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. ++ */ ++ ++/* Qualcomm Technologies, Inc. EMAC Ethernet Controller MAC layer support ++ */ ++ ++#include <linux/tcp.h> ++#include <linux/ip.h> ++#include <linux/ipv6.h> ++#include <linux/crc32.h> ++#include <linux/if_vlan.h> ++#include <linux/jiffies.h> ++#include <linux/phy.h> ++#include <linux/of.h> ++#include <net/ip6_checksum.h> ++#include "emac.h" ++#include "emac-sgmii.h" ++ ++/* EMAC base register offsets */ ++#define EMAC_MAC_CTRL 0x001480 ++#define EMAC_WOL_CTRL0 0x0014a0 ++#define EMAC_RSS_KEY0 0x0014b0 ++#define EMAC_H1TPD_BASE_ADDR_LO 0x0014e0 ++#define EMAC_H2TPD_BASE_ADDR_LO 0x0014e4 ++#define EMAC_H3TPD_BASE_ADDR_LO 0x0014e8 ++#define EMAC_INTER_SRAM_PART9 0x001534 ++#define EMAC_DESC_CTRL_0 0x001540 ++#define EMAC_DESC_CTRL_1 0x001544 ++#define EMAC_DESC_CTRL_2 0x001550 ++#define EMAC_DESC_CTRL_10 0x001554 ++#define EMAC_DESC_CTRL_12 0x001558 ++#define EMAC_DESC_CTRL_13 0x00155c ++#define EMAC_DESC_CTRL_3 0x001560 ++#define EMAC_DESC_CTRL_4 0x001564 ++#define EMAC_DESC_CTRL_5 0x001568 ++#define EMAC_DESC_CTRL_14 0x00156c ++#define EMAC_DESC_CTRL_15 0x001570 ++#define EMAC_DESC_CTRL_16 0x001574 ++#define EMAC_DESC_CTRL_6 0x001578 ++#define EMAC_DESC_CTRL_8 0x001580 ++#define EMAC_DESC_CTRL_9 0x001584 ++#define EMAC_DESC_CTRL_11 0x001588 ++#define EMAC_TXQ_CTRL_0 0x001590 ++#define EMAC_TXQ_CTRL_1 0x001594 ++#define EMAC_TXQ_CTRL_2 0x001598 ++#define EMAC_RXQ_CTRL_0 0x0015a0 ++#define EMAC_RXQ_CTRL_1 0x0015a4 ++#define EMAC_RXQ_CTRL_2 0x0015a8 ++#define EMAC_RXQ_CTRL_3 0x0015ac ++#define EMAC_BASE_CPU_NUMBER 0x0015b8 ++#define EMAC_DMA_CTRL 0x0015c0 ++#define EMAC_MAILBOX_0 0x0015e0 ++#define EMAC_MAILBOX_5 0x0015e4 ++#define EMAC_MAILBOX_6 0x0015e8 ++#define EMAC_MAILBOX_13 0x0015ec ++#define EMAC_MAILBOX_2 0x0015f4 ++#define EMAC_MAILBOX_3 0x0015f8 ++#define EMAC_MAILBOX_11 0x00160c ++#define EMAC_AXI_MAST_CTRL 0x001610 ++#define EMAC_MAILBOX_12 0x001614 ++#define EMAC_MAILBOX_9 0x001618 ++#define EMAC_MAILBOX_10 0x00161c ++#define EMAC_ATHR_HEADER_CTRL 0x001620 ++#define EMAC_CLK_GATE_CTRL 0x001814 ++#define EMAC_MISC_CTRL 0x001990 ++#define EMAC_MAILBOX_7 0x0019e0 ++#define EMAC_MAILBOX_8 0x0019e4 ++#define EMAC_MAILBOX_15 0x001bd4 ++#define EMAC_MAILBOX_16 0x001bd8 ++ ++/* EMAC_MAC_CTRL */ ++#define SINGLE_PAUSE_MODE 0x10000000 ++#define DEBUG_MODE 0x08000000 ++#define BROAD_EN 0x04000000 ++#define MULTI_ALL 0x02000000 ++#define RX_CHKSUM_EN 0x01000000 ++#define HUGE 0x00800000 ++#define SPEED(x) (((x) & 0x3) << 20) ++#define SPEED_MASK SPEED(0x3) ++#define SIMR 0x00080000 ++#define TPAUSE 0x00010000 ++#define PROM_MODE 0x00008000 ++#define VLAN_STRIP 0x00004000 ++#define PRLEN_BMSK 0x00003c00 ++#define PRLEN_SHFT 10 ++#define HUGEN 0x00000200 ++#define FLCHK 0x00000100 ++#define PCRCE 0x00000080 ++#define CRCE 0x00000040 ++#define FULLD 0x00000020 ++#define MAC_LP_EN 0x00000010 ++#define RXFC 0x00000008 ++#define TXFC 0x00000004 ++#define RXEN 0x00000002 ++#define TXEN 0x00000001 ++ ++ ++/* EMAC_WOL_CTRL0 */ ++#define LK_CHG_PME 0x20 ++#define LK_CHG_EN 0x10 ++#define MG_FRAME_PME 0x8 ++#define MG_FRAME_EN 0x4 ++#define WK_FRAME_EN 0x1 ++ ++/* EMAC_DESC_CTRL_3 */ ++#define RFD_RING_SIZE_BMSK 0xfff ++ ++/* EMAC_DESC_CTRL_4 */ ++#define RX_BUFFER_SIZE_BMSK 0xffff ++ ++/* EMAC_DESC_CTRL_6 */ ++#define RRD_RING_SIZE_BMSK 0xfff ++ ++/* EMAC_DESC_CTRL_9 */ ++#define TPD_RING_SIZE_BMSK 0xffff ++ ++/* EMAC_TXQ_CTRL_0 */ ++#define NUM_TXF_BURST_PREF_BMSK 0xffff0000 ++#define NUM_TXF_BURST_PREF_SHFT 16 ++#define LS_8023_SP 0x80 ++#define TXQ_MODE 0x40 ++#define TXQ_EN 0x20 ++#define IP_OP_SP 0x10 ++#define NUM_TPD_BURST_PREF_BMSK 0xf ++#define NUM_TPD_BURST_PREF_SHFT 0 ++ ++/* EMAC_TXQ_CTRL_1 */ ++#define JUMBO_TASK_OFFLOAD_THRESHOLD_BMSK 0x7ff ++ ++/* EMAC_TXQ_CTRL_2 */ ++#define TXF_HWM_BMSK 0xfff0000 ++#define TXF_LWM_BMSK 0xfff ++ ++/* EMAC_RXQ_CTRL_0 */ ++#define RXQ_EN BIT(31) ++#define CUT_THRU_EN BIT(30) ++#define RSS_HASH_EN BIT(29) ++#define NUM_RFD_BURST_PREF_BMSK 0x3f00000 ++#define NUM_RFD_BURST_PREF_SHFT 20 ++#define IDT_TABLE_SIZE_BMSK 0x1ff00 ++#define IDT_TABLE_SIZE_SHFT 8 ++#define SP_IPV6 0x80 ++ ++/* EMAC_RXQ_CTRL_1 */ ++#define JUMBO_1KAH_BMSK 0xf000 ++#define JUMBO_1KAH_SHFT 12 ++#define RFD_PREF_LOW_TH 0x10 ++#define RFD_PREF_LOW_THRESHOLD_BMSK 0xfc0 ++#define RFD_PREF_LOW_THRESHOLD_SHFT 6 ++#define RFD_PREF_UP_TH 0x10 ++#define RFD_PREF_UP_THRESHOLD_BMSK 0x3f ++#define RFD_PREF_UP_THRESHOLD_SHFT 0 ++ ++/* EMAC_RXQ_CTRL_2 */ ++#define RXF_DOF_THRESFHOLD 0x1a0 ++#define RXF_DOF_THRESHOLD_BMSK 0xfff0000 ++#define RXF_DOF_THRESHOLD_SHFT 16 ++#define RXF_UOF_THRESFHOLD 0xbe ++#define RXF_UOF_THRESHOLD_BMSK 0xfff ++#define RXF_UOF_THRESHOLD_SHFT 0 ++ ++/* EMAC_RXQ_CTRL_3 */ ++#define RXD_TIMER_BMSK 0xffff0000 ++#define RXD_THRESHOLD_BMSK 0xfff ++#define RXD_THRESHOLD_SHFT 0 ++ ++/* EMAC_DMA_CTRL */ ++#define DMAW_DLY_CNT_BMSK 0xf0000 ++#define DMAW_DLY_CNT_SHFT 16 ++#define DMAR_DLY_CNT_BMSK 0xf800 ++#define DMAR_DLY_CNT_SHFT 11 ++#define DMAR_REQ_PRI 0x400 ++#define REGWRBLEN_BMSK 0x380 ++#define REGWRBLEN_SHFT 7 ++#define REGRDBLEN_BMSK 0x70 ++#define REGRDBLEN_SHFT 4 ++#define OUT_ORDER_MODE 0x4 ++#define ENH_ORDER_MODE 0x2 ++#define IN_ORDER_MODE 0x1 ++ ++/* EMAC_MAILBOX_13 */ ++#define RFD3_PROC_IDX_BMSK 0xfff0000 ++#define RFD3_PROC_IDX_SHFT 16 ++#define RFD3_PROD_IDX_BMSK 0xfff ++#define RFD3_PROD_IDX_SHFT 0 ++ ++/* EMAC_MAILBOX_2 */ ++#define NTPD_CONS_IDX_BMSK 0xffff0000 ++#define NTPD_CONS_IDX_SHFT 16 ++ ++/* EMAC_MAILBOX_3 */ ++#define RFD0_CONS_IDX_BMSK 0xfff ++#define RFD0_CONS_IDX_SHFT 0 ++ ++/* EMAC_MAILBOX_11 */ ++#define H3TPD_PROD_IDX_BMSK 0xffff0000 ++#define H3TPD_PROD_IDX_SHFT 16 ++ ++/* EMAC_AXI_MAST_CTRL */ ++#define DATA_BYTE_SWAP 0x8 ++#define MAX_BOUND 0x2 ++#define MAX_BTYPE 0x1 ++ ++/* EMAC_MAILBOX_12 */ ++#define H3TPD_CONS_IDX_BMSK 0xffff0000 ++#define H3TPD_CONS_IDX_SHFT 16 ++ ++/* EMAC_MAILBOX_9 */ ++#define H2TPD_PROD_IDX_BMSK 0xffff ++#define H2TPD_PROD_IDX_SHFT 0 ++ ++/* EMAC_MAILBOX_10 */ ++#define H1TPD_CONS_IDX_BMSK 0xffff0000 ++#define H1TPD_CONS_IDX_SHFT 16 ++#define H2TPD_CONS_IDX_BMSK 0xffff ++#define H2TPD_CONS_IDX_SHFT 0 ++ ++/* EMAC_ATHR_HEADER_CTRL */ ++#define HEADER_CNT_EN 0x2 ++#define HEADER_ENABLE 0x1 ++ ++/* EMAC_MAILBOX_0 */ ++#define RFD0_PROC_IDX_BMSK 0xfff0000 ++#define RFD0_PROC_IDX_SHFT 16 ++#define RFD0_PROD_IDX_BMSK 0xfff ++#define RFD0_PROD_IDX_SHFT 0 ++ ++/* EMAC_MAILBOX_5 */ ++#define RFD1_PROC_IDX_BMSK 0xfff0000 ++#define RFD1_PROC_IDX_SHFT 16 ++#define RFD1_PROD_IDX_BMSK 0xfff ++#define RFD1_PROD_IDX_SHFT 0 ++ ++/* EMAC_MISC_CTRL */ ++#define RX_UNCPL_INT_EN 0x1 ++ ++/* EMAC_MAILBOX_7 */ ++#define RFD2_CONS_IDX_BMSK 0xfff0000 ++#define RFD2_CONS_IDX_SHFT 16 ++#define RFD1_CONS_IDX_BMSK 0xfff ++#define RFD1_CONS_IDX_SHFT 0 ++ ++/* EMAC_MAILBOX_8 */ ++#define RFD3_CONS_IDX_BMSK 0xfff ++#define RFD3_CONS_IDX_SHFT 0 ++ ++/* EMAC_MAILBOX_15 */ ++#define NTPD_PROD_IDX_BMSK 0xffff ++#define NTPD_PROD_IDX_SHFT 0 ++ ++/* EMAC_MAILBOX_16 */ ++#define H1TPD_PROD_IDX_BMSK 0xffff ++#define H1TPD_PROD_IDX_SHFT 0 ++ ++#define RXQ0_RSS_HSTYP_IPV6_TCP_EN 0x20 ++#define RXQ0_RSS_HSTYP_IPV6_EN 0x10 ++#define RXQ0_RSS_HSTYP_IPV4_TCP_EN 0x8 ++#define RXQ0_RSS_HSTYP_IPV4_EN 0x4 ++ ++/* EMAC_EMAC_WRAPPER_TX_TS_INX */ ++#define EMAC_WRAPPER_TX_TS_EMPTY BIT(31) ++#define EMAC_WRAPPER_TX_TS_INX_BMSK 0xffff ++ ++struct emac_skb_cb { ++ u32 tpd_idx; ++ unsigned long jiffies; ++}; ++ ++#define EMAC_SKB_CB(skb) ((struct emac_skb_cb *)(skb)->cb) ++#define EMAC_RSS_IDT_SIZE 256 ++#define JUMBO_1KAH 0x4 ++#define RXD_TH 0x100 ++#define EMAC_TPD_LAST_FRAGMENT 0x80000000 ++#define EMAC_TPD_TSTAMP_SAVE 0x80000000 ++ ++/* EMAC Errors in emac_rrd.word[3] */ ++#define EMAC_RRD_L4F BIT(14) ++#define EMAC_RRD_IPF BIT(15) ++#define EMAC_RRD_CRC BIT(21) ++#define EMAC_RRD_FAE BIT(22) ++#define EMAC_RRD_TRN BIT(23) ++#define EMAC_RRD_RNT BIT(24) ++#define EMAC_RRD_INC BIT(25) ++#define EMAC_RRD_FOV BIT(29) ++#define EMAC_RRD_LEN BIT(30) ++ ++/* Error bits that will result in a received frame being discarded */ ++#define EMAC_RRD_ERROR (EMAC_RRD_IPF | EMAC_RRD_CRC | EMAC_RRD_FAE | \ ++ EMAC_RRD_TRN | EMAC_RRD_RNT | EMAC_RRD_INC | \ ++ EMAC_RRD_FOV | EMAC_RRD_LEN) ++#define EMAC_RRD_STATS_DW_IDX 3 ++ ++#define EMAC_RRD(RXQ, SIZE, IDX) ((RXQ)->rrd.v_addr + (SIZE * (IDX))) ++#define EMAC_RFD(RXQ, SIZE, IDX) ((RXQ)->rfd.v_addr + (SIZE * (IDX))) ++#define EMAC_TPD(TXQ, SIZE, IDX) ((TXQ)->tpd.v_addr + (SIZE * (IDX))) ++ ++#define GET_RFD_BUFFER(RXQ, IDX) (&((RXQ)->rfd.rfbuff[(IDX)])) ++#define GET_TPD_BUFFER(RTQ, IDX) (&((RTQ)->tpd.tpbuff[(IDX)])) ++ ++#define EMAC_TX_POLL_HWTXTSTAMP_THRESHOLD 8 ++ ++#define ISR_RX_PKT (\ ++ RX_PKT_INT0 |\ ++ RX_PKT_INT1 |\ ++ RX_PKT_INT2 |\ ++ RX_PKT_INT3) ++ ++#define EMAC_MAC_IRQ_RES "core0" ++ ++void emac_mac_multicast_addr_set(struct emac_adapter *adpt, u8 *addr) ++{ ++ u32 crc32, bit, reg, mta; ++ ++ /* Calculate the CRC of the MAC address */ ++ crc32 = ether_crc(ETH_ALEN, addr); ++ ++ /* The HASH Table is an array of 2 32-bit registers. It is ++ * treated like an array of 64 bits (BitArray[hash_value]). ++ * Use the upper 6 bits of the above CRC as the hash value. ++ */ ++ reg = (crc32 >> 31) & 0x1; ++ bit = (crc32 >> 26) & 0x1F; ++ ++ mta = readl(adpt->base + EMAC_HASH_TAB_REG0 + (reg << 2)); ++ mta |= BIT(bit); ++ writel(mta, adpt->base + EMAC_HASH_TAB_REG0 + (reg << 2)); ++} ++ ++void emac_mac_multicast_addr_clear(struct emac_adapter *adpt) ++{ ++ writel(0, adpt->base + EMAC_HASH_TAB_REG0); ++ writel(0, adpt->base + EMAC_HASH_TAB_REG1); ++} ++ ++/* definitions for RSS */ ++#define EMAC_RSS_KEY(_i, _type) \ ++ (EMAC_RSS_KEY0 + ((_i) * sizeof(_type))) ++#define EMAC_RSS_TBL(_i, _type) \ ++ (EMAC_IDT_TABLE0 + ((_i) * sizeof(_type))) ++ ++/* Config MAC modes */ ++void emac_mac_mode_config(struct emac_adapter *adpt) ++{ ++ struct net_device *netdev = adpt->netdev; ++ u32 mac; ++ ++ mac = readl(adpt->base + EMAC_MAC_CTRL); ++ mac &= ~(VLAN_STRIP | PROM_MODE | MULTI_ALL | MAC_LP_EN); ++ ++ if (netdev->features & NETIF_F_HW_VLAN_CTAG_RX) ++ mac |= VLAN_STRIP; ++ ++ if (netdev->flags & IFF_PROMISC) ++ mac |= PROM_MODE; ++ ++ if (netdev->flags & IFF_ALLMULTI) ++ mac |= MULTI_ALL; ++ ++ writel(mac, adpt->base + EMAC_MAC_CTRL); ++} ++ ++/* Config descriptor rings */ ++static void emac_mac_dma_rings_config(struct emac_adapter *adpt) ++{ ++ static const unsigned short tpd_q_offset[] = { ++ EMAC_DESC_CTRL_8, EMAC_H1TPD_BASE_ADDR_LO, ++ EMAC_H2TPD_BASE_ADDR_LO, EMAC_H3TPD_BASE_ADDR_LO}; ++ static const unsigned short rfd_q_offset[] = { ++ EMAC_DESC_CTRL_2, EMAC_DESC_CTRL_10, ++ EMAC_DESC_CTRL_12, EMAC_DESC_CTRL_13}; ++ static const unsigned short rrd_q_offset[] = { ++ EMAC_DESC_CTRL_5, EMAC_DESC_CTRL_14, ++ EMAC_DESC_CTRL_15, EMAC_DESC_CTRL_16}; ++ ++ /* TPD (Transmit Packet Descriptor) */ ++ writel(upper_32_bits(adpt->tx_q.tpd.dma_addr), ++ adpt->base + EMAC_DESC_CTRL_1); ++ ++ writel(lower_32_bits(adpt->tx_q.tpd.dma_addr), ++ adpt->base + tpd_q_offset[0]); ++ ++ writel(adpt->tx_q.tpd.count & TPD_RING_SIZE_BMSK, ++ adpt->base + EMAC_DESC_CTRL_9); ++ ++ /* RFD (Receive Free Descriptor) & RRD (Receive Return Descriptor) */ ++ writel(upper_32_bits(adpt->rx_q.rfd.dma_addr), ++ adpt->base + EMAC_DESC_CTRL_0); ++ ++ writel(lower_32_bits(adpt->rx_q.rfd.dma_addr), ++ adpt->base + rfd_q_offset[0]); ++ writel(lower_32_bits(adpt->rx_q.rrd.dma_addr), ++ adpt->base + rrd_q_offset[0]); ++ ++ writel(adpt->rx_q.rfd.count & RFD_RING_SIZE_BMSK, ++ adpt->base + EMAC_DESC_CTRL_3); ++ writel(adpt->rx_q.rrd.count & RRD_RING_SIZE_BMSK, ++ adpt->base + EMAC_DESC_CTRL_6); ++ ++ writel(adpt->rxbuf_size & RX_BUFFER_SIZE_BMSK, ++ adpt->base + EMAC_DESC_CTRL_4); ++ ++ writel(0, adpt->base + EMAC_DESC_CTRL_11); ++ ++ /* Load all of the base addresses above and ensure that triggering HW to ++ * read ring pointers is flushed ++ */ ++ writel(1, adpt->base + EMAC_INTER_SRAM_PART9); ++} ++ ++/* Config transmit parameters */ ++static void emac_mac_tx_config(struct emac_adapter *adpt) ++{ ++ u32 val; ++ ++ writel((EMAC_MAX_TX_OFFLOAD_THRESH >> 3) & ++ JUMBO_TASK_OFFLOAD_THRESHOLD_BMSK, adpt->base + EMAC_TXQ_CTRL_1); ++ ++ val = (adpt->tpd_burst << NUM_TPD_BURST_PREF_SHFT) & ++ NUM_TPD_BURST_PREF_BMSK; ++ ++ val |= TXQ_MODE | LS_8023_SP; ++ val |= (0x0100 << NUM_TXF_BURST_PREF_SHFT) & ++ NUM_TXF_BURST_PREF_BMSK; ++ ++ writel(val, adpt->base + EMAC_TXQ_CTRL_0); ++ emac_reg_update32(adpt->base + EMAC_TXQ_CTRL_2, ++ (TXF_HWM_BMSK | TXF_LWM_BMSK), 0); ++} ++ ++/* Config receive parameters */ ++static void emac_mac_rx_config(struct emac_adapter *adpt) ++{ ++ u32 val; ++ ++ val = (adpt->rfd_burst << NUM_RFD_BURST_PREF_SHFT) & ++ NUM_RFD_BURST_PREF_BMSK; ++ val |= (SP_IPV6 | CUT_THRU_EN); ++ ++ writel(val, adpt->base + EMAC_RXQ_CTRL_0); ++ ++ val = readl(adpt->base + EMAC_RXQ_CTRL_1); ++ val &= ~(JUMBO_1KAH_BMSK | RFD_PREF_LOW_THRESHOLD_BMSK | ++ RFD_PREF_UP_THRESHOLD_BMSK); ++ val |= (JUMBO_1KAH << JUMBO_1KAH_SHFT) | ++ (RFD_PREF_LOW_TH << RFD_PREF_LOW_THRESHOLD_SHFT) | ++ (RFD_PREF_UP_TH << RFD_PREF_UP_THRESHOLD_SHFT); ++ writel(val, adpt->base + EMAC_RXQ_CTRL_1); ++ ++ val = readl(adpt->base + EMAC_RXQ_CTRL_2); ++ val &= ~(RXF_DOF_THRESHOLD_BMSK | RXF_UOF_THRESHOLD_BMSK); ++ val |= (RXF_DOF_THRESFHOLD << RXF_DOF_THRESHOLD_SHFT) | ++ (RXF_UOF_THRESFHOLD << RXF_UOF_THRESHOLD_SHFT); ++ writel(val, adpt->base + EMAC_RXQ_CTRL_2); ++ ++ val = readl(adpt->base + EMAC_RXQ_CTRL_3); ++ val &= ~(RXD_TIMER_BMSK | RXD_THRESHOLD_BMSK); ++ val |= RXD_TH << RXD_THRESHOLD_SHFT; ++ writel(val, adpt->base + EMAC_RXQ_CTRL_3); ++} ++ ++/* Config dma */ ++static void emac_mac_dma_config(struct emac_adapter *adpt) ++{ ++ u32 dma_ctrl = DMAR_REQ_PRI; ++ ++ switch (adpt->dma_order) { ++ case emac_dma_ord_in: ++ dma_ctrl |= IN_ORDER_MODE; ++ break; ++ case emac_dma_ord_enh: ++ dma_ctrl |= ENH_ORDER_MODE; ++ break; ++ case emac_dma_ord_out: ++ dma_ctrl |= OUT_ORDER_MODE; ++ break; ++ default: ++ break; ++ } ++ ++ dma_ctrl |= (((u32)adpt->dmar_block) << REGRDBLEN_SHFT) & ++ REGRDBLEN_BMSK; ++ dma_ctrl |= (((u32)adpt->dmaw_block) << REGWRBLEN_SHFT) & ++ REGWRBLEN_BMSK; ++ dma_ctrl |= (((u32)adpt->dmar_dly_cnt) << DMAR_DLY_CNT_SHFT) & ++ DMAR_DLY_CNT_BMSK; ++ dma_ctrl |= (((u32)adpt->dmaw_dly_cnt) << DMAW_DLY_CNT_SHFT) & ++ DMAW_DLY_CNT_BMSK; ++ ++ /* config DMA and ensure that configuration is flushed to HW */ ++ writel(dma_ctrl, adpt->base + EMAC_DMA_CTRL); ++} ++ ++/* set MAC address */ ++static void emac_set_mac_address(struct emac_adapter *adpt, u8 *addr) ++{ ++ u32 sta; ++ ++ /* for example: 00-A0-C6-11-22-33 ++ * 0<-->C6112233, 1<-->00A0. ++ */ ++ ++ /* low 32bit word */ ++ sta = (((u32)addr[2]) << 24) | (((u32)addr[3]) << 16) | ++ (((u32)addr[4]) << 8) | (((u32)addr[5])); ++ writel(sta, adpt->base + EMAC_MAC_STA_ADDR0); ++ ++ /* hight 32bit word */ ++ sta = (((u32)addr[0]) << 8) | (u32)addr[1]; ++ writel(sta, adpt->base + EMAC_MAC_STA_ADDR1); ++} ++ ++static void emac_mac_config(struct emac_adapter *adpt) ++{ ++ struct net_device *netdev = adpt->netdev; ++ unsigned int max_frame; ++ u32 val; ++ ++ emac_set_mac_address(adpt, netdev->dev_addr); ++ ++ max_frame = netdev->mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN; ++ adpt->rxbuf_size = netdev->mtu > EMAC_DEF_RX_BUF_SIZE ? ++ ALIGN(max_frame, 8) : EMAC_DEF_RX_BUF_SIZE; ++ ++ emac_mac_dma_rings_config(adpt); ++ ++ writel(netdev->mtu + ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN, ++ adpt->base + EMAC_MAX_FRAM_LEN_CTRL); ++ ++ emac_mac_tx_config(adpt); ++ emac_mac_rx_config(adpt); ++ emac_mac_dma_config(adpt); ++ ++ val = readl(adpt->base + EMAC_AXI_MAST_CTRL); ++ val &= ~(DATA_BYTE_SWAP | MAX_BOUND); ++ val |= MAX_BTYPE; ++ writel(val, adpt->base + EMAC_AXI_MAST_CTRL); ++ writel(0, adpt->base + EMAC_CLK_GATE_CTRL); ++ writel(RX_UNCPL_INT_EN, adpt->base + EMAC_MISC_CTRL); ++} ++ ++void emac_mac_reset(struct emac_adapter *adpt) ++{ ++ emac_mac_stop(adpt); ++ ++ emac_reg_update32(adpt->base + EMAC_DMA_MAS_CTRL, 0, SOFT_RST); ++ usleep_range(100, 150); /* reset may take up to 100usec */ ++ ++ /* interrupt clear-on-read */ ++ emac_reg_update32(adpt->base + EMAC_DMA_MAS_CTRL, 0, INT_RD_CLR_EN); ++} ++ ++void emac_mac_start(struct emac_adapter *adpt) ++{ ++ struct phy_device *phydev = adpt->phydev; ++ u32 mac, csr1; ++ ++ /* enable tx queue */ ++ emac_reg_update32(adpt->base + EMAC_TXQ_CTRL_0, 0, TXQ_EN); ++ ++ /* enable rx queue */ ++ emac_reg_update32(adpt->base + EMAC_RXQ_CTRL_0, 0, RXQ_EN); ++ ++ /* enable mac control */ ++ mac = readl(adpt->base + EMAC_MAC_CTRL); ++ csr1 = readl(adpt->csr + EMAC_EMAC_WRAPPER_CSR1); ++ ++ mac |= TXEN | RXEN; /* enable RX/TX */ ++ ++ /* We don't have ethtool support yet, so force flow-control mode ++ * to 'full' always. ++ */ ++ mac |= TXFC | RXFC; ++ ++ /* setup link speed */ ++ mac &= ~SPEED_MASK; ++ if (phydev->speed == SPEED_1000) { ++ mac |= SPEED(2); ++ csr1 |= FREQ_MODE; ++ } else { ++ mac |= SPEED(1); ++ csr1 &= ~FREQ_MODE; ++ } ++ ++ if (phydev->duplex == DUPLEX_FULL) ++ mac |= FULLD; ++ else ++ mac &= ~FULLD; ++ ++ /* other parameters */ ++ mac |= (CRCE | PCRCE); ++ mac |= ((adpt->preamble << PRLEN_SHFT) & PRLEN_BMSK); ++ mac |= BROAD_EN; ++ mac |= FLCHK; ++ mac &= ~RX_CHKSUM_EN; ++ mac &= ~(HUGEN | VLAN_STRIP | TPAUSE | SIMR | HUGE | MULTI_ALL | ++ DEBUG_MODE | SINGLE_PAUSE_MODE); ++ ++ writel_relaxed(csr1, adpt->csr + EMAC_EMAC_WRAPPER_CSR1); ++ ++ writel_relaxed(mac, adpt->base + EMAC_MAC_CTRL); ++ ++ /* enable interrupt read clear, low power sleep mode and ++ * the irq moderators ++ */ ++ ++ writel_relaxed(adpt->irq_mod, adpt->base + EMAC_IRQ_MOD_TIM_INIT); ++ writel_relaxed(INT_RD_CLR_EN | LPW_MODE | IRQ_MODERATOR_EN | ++ IRQ_MODERATOR2_EN, adpt->base + EMAC_DMA_MAS_CTRL); ++ ++ emac_mac_mode_config(adpt); ++ ++ emac_reg_update32(adpt->base + EMAC_ATHR_HEADER_CTRL, ++ (HEADER_ENABLE | HEADER_CNT_EN), 0); ++ ++ emac_reg_update32(adpt->csr + EMAC_EMAC_WRAPPER_CSR2, 0, WOL_EN); ++} ++ ++void emac_mac_stop(struct emac_adapter *adpt) ++{ ++ emac_reg_update32(adpt->base + EMAC_RXQ_CTRL_0, RXQ_EN, 0); ++ emac_reg_update32(adpt->base + EMAC_TXQ_CTRL_0, TXQ_EN, 0); ++ emac_reg_update32(adpt->base + EMAC_MAC_CTRL, TXEN | RXEN, 0); ++ usleep_range(1000, 1050); /* stopping mac may take upto 1msec */ ++} ++ ++/* Free all descriptors of given transmit queue */ ++static void emac_tx_q_descs_free(struct emac_adapter *adpt) ++{ ++ struct emac_tx_queue *tx_q = &adpt->tx_q; ++ unsigned int i; ++ size_t size; ++ ++ /* ring already cleared, nothing to do */ ++ if (!tx_q->tpd.tpbuff) ++ return; ++ ++ for (i = 0; i < tx_q->tpd.count; i++) { ++ struct emac_buffer *tpbuf = GET_TPD_BUFFER(tx_q, i); ++ ++ if (tpbuf->dma_addr) { ++ dma_unmap_single(adpt->netdev->dev.parent, ++ tpbuf->dma_addr, tpbuf->length, ++ DMA_TO_DEVICE); ++ tpbuf->dma_addr = 0; ++ } ++ if (tpbuf->skb) { ++ dev_kfree_skb_any(tpbuf->skb); ++ tpbuf->skb = NULL; ++ } ++ } ++ ++ size = sizeof(struct emac_buffer) * tx_q->tpd.count; ++ memset(tx_q->tpd.tpbuff, 0, size); ++ ++ /* clear the descriptor ring */ ++ memset(tx_q->tpd.v_addr, 0, tx_q->tpd.size); ++ ++ tx_q->tpd.consume_idx = 0; ++ tx_q->tpd.produce_idx = 0; ++} ++ ++/* Free all descriptors of given receive queue */ ++static void emac_rx_q_free_descs(struct emac_adapter *adpt) ++{ ++ struct device *dev = adpt->netdev->dev.parent; ++ struct emac_rx_queue *rx_q = &adpt->rx_q; ++ unsigned int i; ++ size_t size; ++ ++ /* ring already cleared, nothing to do */ ++ if (!rx_q->rfd.rfbuff) ++ return; ++ ++ for (i = 0; i < rx_q->rfd.count; i++) { ++ struct emac_buffer *rfbuf = GET_RFD_BUFFER(rx_q, i); ++ ++ if (rfbuf->dma_addr) { ++ dma_unmap_single(dev, rfbuf->dma_addr, rfbuf->length, ++ DMA_FROM_DEVICE); ++ rfbuf->dma_addr = 0; ++ } ++ if (rfbuf->skb) { ++ dev_kfree_skb(rfbuf->skb); ++ rfbuf->skb = NULL; ++ } ++ } ++ ++ size = sizeof(struct emac_buffer) * rx_q->rfd.count; ++ memset(rx_q->rfd.rfbuff, 0, size); ++ ++ /* clear the descriptor rings */ ++ memset(rx_q->rrd.v_addr, 0, rx_q->rrd.size); ++ rx_q->rrd.produce_idx = 0; ++ rx_q->rrd.consume_idx = 0; ++ ++ memset(rx_q->rfd.v_addr, 0, rx_q->rfd.size); ++ rx_q->rfd.produce_idx = 0; ++ rx_q->rfd.consume_idx = 0; ++} ++ ++/* Free all buffers associated with given transmit queue */ ++static void emac_tx_q_bufs_free(struct emac_adapter *adpt) ++{ ++ struct emac_tx_queue *tx_q = &adpt->tx_q; ++ ++ emac_tx_q_descs_free(adpt); ++ ++ kfree(tx_q->tpd.tpbuff); ++ tx_q->tpd.tpbuff = NULL; ++ tx_q->tpd.v_addr = NULL; ++ tx_q->tpd.dma_addr = 0; ++ tx_q->tpd.size = 0; ++} ++ ++/* Allocate TX descriptor ring for the given transmit queue */ ++static int emac_tx_q_desc_alloc(struct emac_adapter *adpt, ++ struct emac_tx_queue *tx_q) ++{ ++ struct emac_ring_header *ring_header = &adpt->ring_header; ++ size_t size; ++ ++ size = sizeof(struct emac_buffer) * tx_q->tpd.count; ++ tx_q->tpd.tpbuff = kzalloc(size, GFP_KERNEL); ++ if (!tx_q->tpd.tpbuff) ++ return -ENOMEM; ++ ++ tx_q->tpd.size = tx_q->tpd.count * (adpt->tpd_size * 4); ++ tx_q->tpd.dma_addr = ring_header->dma_addr + ring_header->used; ++ tx_q->tpd.v_addr = ring_header->v_addr + ring_header->used; ++ ring_header->used += ALIGN(tx_q->tpd.size, 8); ++ tx_q->tpd.produce_idx = 0; ++ tx_q->tpd.consume_idx = 0; ++ ++ return 0; ++} ++ ++/* Free all buffers associated with given transmit queue */ ++static void emac_rx_q_bufs_free(struct emac_adapter *adpt) ++{ ++ struct emac_rx_queue *rx_q = &adpt->rx_q; ++ ++ emac_rx_q_free_descs(adpt); ++ ++ kfree(rx_q->rfd.rfbuff); ++ rx_q->rfd.rfbuff = NULL; ++ ++ rx_q->rfd.v_addr = NULL; ++ rx_q->rfd.dma_addr = 0; ++ rx_q->rfd.size = 0; ++ ++ rx_q->rrd.v_addr = NULL; ++ rx_q->rrd.dma_addr = 0; ++ rx_q->rrd.size = 0; ++} ++ ++/* Allocate RX descriptor rings for the given receive queue */ ++static int emac_rx_descs_alloc(struct emac_adapter *adpt) ++{ ++ struct emac_ring_header *ring_header = &adpt->ring_header; ++ struct emac_rx_queue *rx_q = &adpt->rx_q; ++ size_t size; ++ ++ size = sizeof(struct emac_buffer) * rx_q->rfd.count; ++ rx_q->rfd.rfbuff = kzalloc(size, GFP_KERNEL); ++ if (!rx_q->rfd.rfbuff) ++ return -ENOMEM; ++ ++ rx_q->rrd.size = rx_q->rrd.count * (adpt->rrd_size * 4); ++ rx_q->rfd.size = rx_q->rfd.count * (adpt->rfd_size * 4); ++ ++ rx_q->rrd.dma_addr = ring_header->dma_addr + ring_header->used; ++ rx_q->rrd.v_addr = ring_header->v_addr + ring_header->used; ++ ring_header->used += ALIGN(rx_q->rrd.size, 8); ++ ++ rx_q->rfd.dma_addr = ring_header->dma_addr + ring_header->used; ++ rx_q->rfd.v_addr = ring_header->v_addr + ring_header->used; ++ ring_header->used += ALIGN(rx_q->rfd.size, 8); ++ ++ rx_q->rrd.produce_idx = 0; ++ rx_q->rrd.consume_idx = 0; ++ ++ rx_q->rfd.produce_idx = 0; ++ rx_q->rfd.consume_idx = 0; ++ ++ return 0; ++} ++ ++/* Allocate all TX and RX descriptor rings */ ++int emac_mac_rx_tx_rings_alloc_all(struct emac_adapter *adpt) ++{ ++ struct emac_ring_header *ring_header = &adpt->ring_header; ++ struct device *dev = adpt->netdev->dev.parent; ++ unsigned int num_tx_descs = adpt->tx_desc_cnt; ++ unsigned int num_rx_descs = adpt->rx_desc_cnt; ++ int ret; ++ ++ adpt->tx_q.tpd.count = adpt->tx_desc_cnt; ++ ++ adpt->rx_q.rrd.count = adpt->rx_desc_cnt; ++ adpt->rx_q.rfd.count = adpt->rx_desc_cnt; ++ ++ /* Ring DMA buffer. Each ring may need up to 8 bytes for alignment, ++ * hence the additional padding bytes are allocated. ++ */ ++ ring_header->size = num_tx_descs * (adpt->tpd_size * 4) + ++ num_rx_descs * (adpt->rfd_size * 4) + ++ num_rx_descs * (adpt->rrd_size * 4) + ++ 8 + 2 * 8; /* 8 byte per one Tx and two Rx rings */ ++ ++ ring_header->used = 0; ++ ring_header->v_addr = dma_zalloc_coherent(dev, ring_header->size, ++ &ring_header->dma_addr, ++ GFP_KERNEL); ++ if (!ring_header->v_addr) ++ return -ENOMEM; ++ ++ ring_header->used = ALIGN(ring_header->dma_addr, 8) - ++ ring_header->dma_addr; ++ ++ ret = emac_tx_q_desc_alloc(adpt, &adpt->tx_q); ++ if (ret) { ++ netdev_err(adpt->netdev, "error: Tx Queue alloc failed\n"); ++ goto err_alloc_tx; ++ } ++ ++ ret = emac_rx_descs_alloc(adpt); ++ if (ret) { ++ netdev_err(adpt->netdev, "error: Rx Queue alloc failed\n"); ++ goto err_alloc_rx; ++ } ++ ++ return 0; ++ ++err_alloc_rx: ++ emac_tx_q_bufs_free(adpt); ++err_alloc_tx: ++ dma_free_coherent(dev, ring_header->size, ++ ring_header->v_addr, ring_header->dma_addr); ++ ++ ring_header->v_addr = NULL; ++ ring_header->dma_addr = 0; ++ ring_header->size = 0; ++ ring_header->used = 0; ++ ++ return ret; ++} ++ ++/* Free all TX and RX descriptor rings */ ++void emac_mac_rx_tx_rings_free_all(struct emac_adapter *adpt) ++{ ++ struct emac_ring_header *ring_header = &adpt->ring_header; ++ struct device *dev = adpt->netdev->dev.parent; ++ ++ emac_tx_q_bufs_free(adpt); ++ emac_rx_q_bufs_free(adpt); ++ ++ dma_free_coherent(dev, ring_header->size, ++ ring_header->v_addr, ring_header->dma_addr); ++ ++ ring_header->v_addr = NULL; ++ ring_header->dma_addr = 0; ++ ring_header->size = 0; ++ ring_header->used = 0; ++} ++ ++/* Initialize descriptor rings */ ++static void emac_mac_rx_tx_ring_reset_all(struct emac_adapter *adpt) ++{ ++ unsigned int i; ++ ++ adpt->tx_q.tpd.produce_idx = 0; ++ adpt->tx_q.tpd.consume_idx = 0; ++ for (i = 0; i < adpt->tx_q.tpd.count; i++) ++ adpt->tx_q.tpd.tpbuff[i].dma_addr = 0; ++ ++ adpt->rx_q.rrd.produce_idx = 0; ++ adpt->rx_q.rrd.consume_idx = 0; ++ adpt->rx_q.rfd.produce_idx = 0; ++ adpt->rx_q.rfd.consume_idx = 0; ++ for (i = 0; i < adpt->rx_q.rfd.count; i++) ++ adpt->rx_q.rfd.rfbuff[i].dma_addr = 0; ++} ++ ++/* Produce new receive free descriptor */ ++static void emac_mac_rx_rfd_create(struct emac_adapter *adpt, ++ struct emac_rx_queue *rx_q, ++ dma_addr_t addr) ++{ ++ u32 *hw_rfd = EMAC_RFD(rx_q, adpt->rfd_size, rx_q->rfd.produce_idx); ++ ++ *(hw_rfd++) = lower_32_bits(addr); ++ *hw_rfd = upper_32_bits(addr); ++ ++ if (++rx_q->rfd.produce_idx == rx_q->rfd.count) ++ rx_q->rfd.produce_idx = 0; ++} ++ ++/* Fill up receive queue's RFD with preallocated receive buffers */ ++static void emac_mac_rx_descs_refill(struct emac_adapter *adpt, ++ struct emac_rx_queue *rx_q) ++{ ++ struct emac_buffer *curr_rxbuf; ++ struct emac_buffer *next_rxbuf; ++ unsigned int count = 0; ++ u32 next_produce_idx; ++ ++ next_produce_idx = rx_q->rfd.produce_idx + 1; ++ if (next_produce_idx == rx_q->rfd.count) ++ next_produce_idx = 0; ++ ++ curr_rxbuf = GET_RFD_BUFFER(rx_q, rx_q->rfd.produce_idx); ++ next_rxbuf = GET_RFD_BUFFER(rx_q, next_produce_idx); ++ ++ /* this always has a blank rx_buffer*/ ++ while (!next_rxbuf->dma_addr) { ++ struct sk_buff *skb; ++ int ret; ++ ++ skb = netdev_alloc_skb_ip_align(adpt->netdev, adpt->rxbuf_size); ++ if (!skb) ++ break; ++ ++ curr_rxbuf->dma_addr = ++ dma_map_single(adpt->netdev->dev.parent, skb->data, ++ curr_rxbuf->length, DMA_FROM_DEVICE); ++ ret = dma_mapping_error(adpt->netdev->dev.parent, ++ curr_rxbuf->dma_addr); ++ if (ret) { ++ dev_kfree_skb(skb); ++ break; ++ } ++ curr_rxbuf->skb = skb; ++ curr_rxbuf->length = adpt->rxbuf_size; ++ ++ emac_mac_rx_rfd_create(adpt, rx_q, curr_rxbuf->dma_addr); ++ next_produce_idx = rx_q->rfd.produce_idx + 1; ++ if (next_produce_idx == rx_q->rfd.count) ++ next_produce_idx = 0; ++ ++ curr_rxbuf = GET_RFD_BUFFER(rx_q, rx_q->rfd.produce_idx); ++ next_rxbuf = GET_RFD_BUFFER(rx_q, next_produce_idx); ++ count++; ++ } ++ ++ if (count) { ++ u32 prod_idx = (rx_q->rfd.produce_idx << rx_q->produce_shift) & ++ rx_q->produce_mask; ++ emac_reg_update32(adpt->base + rx_q->produce_reg, ++ rx_q->produce_mask, prod_idx); ++ } ++} ++ ++static void emac_adjust_link(struct net_device *netdev) ++{ ++ struct emac_adapter *adpt = netdev_priv(netdev); ++ struct phy_device *phydev = netdev->phydev; ++ ++ if (phydev->link) ++ emac_mac_start(adpt); ++ else ++ emac_mac_stop(adpt); ++ ++ phy_print_status(phydev); ++} ++ ++/* Bringup the interface/HW */ ++int emac_mac_up(struct emac_adapter *adpt) ++{ ++ struct net_device *netdev = adpt->netdev; ++ struct emac_irq *irq = &adpt->irq; ++ int ret; ++ ++ emac_mac_rx_tx_ring_reset_all(adpt); ++ emac_mac_config(adpt); ++ ++ ret = request_irq(irq->irq, emac_isr, 0, EMAC_MAC_IRQ_RES, irq); ++ if (ret) { ++ netdev_err(adpt->netdev, "could not request %s irq\n", ++ EMAC_MAC_IRQ_RES); ++ return ret; ++ } ++ ++ emac_mac_rx_descs_refill(adpt, &adpt->rx_q); ++ ++ ret = phy_connect_direct(netdev, adpt->phydev, emac_adjust_link, ++ PHY_INTERFACE_MODE_SGMII); ++ if (ret) { ++ netdev_err(adpt->netdev, "could not connect phy\n"); ++ free_irq(irq->irq, irq); ++ return ret; ++ } ++ ++ /* enable mac irq */ ++ writel((u32)~DIS_INT, adpt->base + EMAC_INT_STATUS); ++ writel(adpt->irq.mask, adpt->base + EMAC_INT_MASK); ++ ++ adpt->phydev->irq = PHY_IGNORE_INTERRUPT; ++ phy_start(adpt->phydev); ++ ++ napi_enable(&adpt->rx_q.napi); ++ netif_start_queue(netdev); ++ ++ return 0; ++} ++ ++/* Bring down the interface/HW */ ++void emac_mac_down(struct emac_adapter *adpt) ++{ ++ struct net_device *netdev = adpt->netdev; ++ ++ netif_stop_queue(netdev); ++ napi_disable(&adpt->rx_q.napi); ++ ++ phy_stop(adpt->phydev); ++ phy_disconnect(adpt->phydev); ++ ++ /* disable mac irq */ ++ writel(DIS_INT, adpt->base + EMAC_INT_STATUS); ++ writel(0, adpt->base + EMAC_INT_MASK); ++ synchronize_irq(adpt->irq.irq); ++ free_irq(adpt->irq.irq, &adpt->irq); ++ ++ emac_mac_reset(adpt); ++ ++ emac_tx_q_descs_free(adpt); ++ netdev_reset_queue(adpt->netdev); ++ emac_rx_q_free_descs(adpt); ++} ++ ++/* Consume next received packet descriptor */ ++static bool emac_rx_process_rrd(struct emac_adapter *adpt, ++ struct emac_rx_queue *rx_q, ++ struct emac_rrd *rrd) ++{ ++ u32 *hw_rrd = EMAC_RRD(rx_q, adpt->rrd_size, rx_q->rrd.consume_idx); ++ ++ rrd->word[3] = *(hw_rrd + 3); ++ ++ if (!RRD_UPDT(rrd)) ++ return false; ++ ++ rrd->word[4] = 0; ++ rrd->word[5] = 0; ++ ++ rrd->word[0] = *(hw_rrd++); ++ rrd->word[1] = *(hw_rrd++); ++ rrd->word[2] = *(hw_rrd++); ++ ++ if (unlikely(RRD_NOR(rrd) != 1)) { ++ netdev_err(adpt->netdev, ++ "error: multi-RFD not support yet! nor:%lu\n", ++ RRD_NOR(rrd)); ++ } ++ ++ /* mark rrd as processed */ ++ RRD_UPDT_SET(rrd, 0); ++ *hw_rrd = rrd->word[3]; ++ ++ if (++rx_q->rrd.consume_idx == rx_q->rrd.count) ++ rx_q->rrd.consume_idx = 0; ++ ++ return true; ++} ++ ++/* Produce new transmit descriptor */ ++static void emac_tx_tpd_create(struct emac_adapter *adpt, ++ struct emac_tx_queue *tx_q, struct emac_tpd *tpd) ++{ ++ u32 *hw_tpd; ++ ++ tx_q->tpd.last_produce_idx = tx_q->tpd.produce_idx; ++ hw_tpd = EMAC_TPD(tx_q, adpt->tpd_size, tx_q->tpd.produce_idx); ++ ++ if (++tx_q->tpd.produce_idx == tx_q->tpd.count) ++ tx_q->tpd.produce_idx = 0; ++ ++ *(hw_tpd++) = tpd->word[0]; ++ *(hw_tpd++) = tpd->word[1]; ++ *(hw_tpd++) = tpd->word[2]; ++ *hw_tpd = tpd->word[3]; ++} ++ ++/* Mark the last transmit descriptor as such (for the transmit packet) */ ++static void emac_tx_tpd_mark_last(struct emac_adapter *adpt, ++ struct emac_tx_queue *tx_q) ++{ ++ u32 *hw_tpd = ++ EMAC_TPD(tx_q, adpt->tpd_size, tx_q->tpd.last_produce_idx); ++ u32 tmp_tpd; ++ ++ tmp_tpd = *(hw_tpd + 1); ++ tmp_tpd |= EMAC_TPD_LAST_FRAGMENT; ++ *(hw_tpd + 1) = tmp_tpd; ++} ++ ++static void emac_rx_rfd_clean(struct emac_rx_queue *rx_q, struct emac_rrd *rrd) ++{ ++ struct emac_buffer *rfbuf = rx_q->rfd.rfbuff; ++ u32 consume_idx = RRD_SI(rrd); ++ unsigned int i; ++ ++ for (i = 0; i < RRD_NOR(rrd); i++) { ++ rfbuf[consume_idx].skb = NULL; ++ if (++consume_idx == rx_q->rfd.count) ++ consume_idx = 0; ++ } ++ ++ rx_q->rfd.consume_idx = consume_idx; ++ rx_q->rfd.process_idx = consume_idx; ++} ++ ++/* Push the received skb to upper layers */ ++static void emac_receive_skb(struct emac_rx_queue *rx_q, ++ struct sk_buff *skb, ++ u16 vlan_tag, bool vlan_flag) ++{ ++ if (vlan_flag) { ++ u16 vlan; ++ ++ EMAC_TAG_TO_VLAN(vlan_tag, vlan); ++ __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan); ++ } ++ ++ napi_gro_receive(&rx_q->napi, skb); ++} ++ ++/* Process receive event */ ++void emac_mac_rx_process(struct emac_adapter *adpt, struct emac_rx_queue *rx_q, ++ int *num_pkts, int max_pkts) ++{ ++ u32 proc_idx, hw_consume_idx, num_consume_pkts; ++ struct net_device *netdev = adpt->netdev; ++ struct emac_buffer *rfbuf; ++ unsigned int count = 0; ++ struct emac_rrd rrd; ++ struct sk_buff *skb; ++ u32 reg; ++ ++ reg = readl_relaxed(adpt->base + rx_q->consume_reg); ++ ++ hw_consume_idx = (reg & rx_q->consume_mask) >> rx_q->consume_shift; ++ num_consume_pkts = (hw_consume_idx >= rx_q->rrd.consume_idx) ? ++ (hw_consume_idx - rx_q->rrd.consume_idx) : ++ (hw_consume_idx + rx_q->rrd.count - rx_q->rrd.consume_idx); ++ ++ do { ++ if (!num_consume_pkts) ++ break; ++ ++ if (!emac_rx_process_rrd(adpt, rx_q, &rrd)) ++ break; ++ ++ if (likely(RRD_NOR(&rrd) == 1)) { ++ /* good receive */ ++ rfbuf = GET_RFD_BUFFER(rx_q, RRD_SI(&rrd)); ++ dma_unmap_single(adpt->netdev->dev.parent, ++ rfbuf->dma_addr, rfbuf->length, ++ DMA_FROM_DEVICE); ++ rfbuf->dma_addr = 0; ++ skb = rfbuf->skb; ++ } else { ++ netdev_err(adpt->netdev, ++ "error: multi-RFD not support yet!\n"); ++ break; ++ } ++ emac_rx_rfd_clean(rx_q, &rrd); ++ num_consume_pkts--; ++ count++; ++ ++ /* Due to a HW issue in L4 check sum detection (UDP/TCP frags ++ * with DF set are marked as error), drop packets based on the ++ * error mask rather than the summary bit (ignoring L4F errors) ++ */ ++ if (rrd.word[EMAC_RRD_STATS_DW_IDX] & EMAC_RRD_ERROR) { ++ netif_dbg(adpt, rx_status, adpt->netdev, ++ "Drop error packet[RRD: 0x%x:0x%x:0x%x:0x%x]\n", ++ rrd.word[0], rrd.word[1], ++ rrd.word[2], rrd.word[3]); ++ ++ dev_kfree_skb(skb); ++ continue; ++ } ++ ++ skb_put(skb, RRD_PKT_SIZE(&rrd) - ETH_FCS_LEN); ++ skb->dev = netdev; ++ skb->protocol = eth_type_trans(skb, skb->dev); ++ if (netdev->features & NETIF_F_RXCSUM) ++ skb->ip_summed = RRD_L4F(&rrd) ? ++ CHECKSUM_NONE : CHECKSUM_UNNECESSARY; ++ else ++ skb_checksum_none_assert(skb); ++ ++ emac_receive_skb(rx_q, skb, (u16)RRD_CVALN_TAG(&rrd), ++ (bool)RRD_CVTAG(&rrd)); ++ ++ netdev->last_rx = jiffies; ++ (*num_pkts)++; ++ } while (*num_pkts < max_pkts); ++ ++ if (count) { ++ proc_idx = (rx_q->rfd.process_idx << rx_q->process_shft) & ++ rx_q->process_mask; ++ emac_reg_update32(adpt->base + rx_q->process_reg, ++ rx_q->process_mask, proc_idx); ++ emac_mac_rx_descs_refill(adpt, rx_q); ++ } ++} ++ ++/* get the number of free transmit descriptors */ ++static unsigned int emac_tpd_num_free_descs(struct emac_tx_queue *tx_q) ++{ ++ u32 produce_idx = tx_q->tpd.produce_idx; ++ u32 consume_idx = tx_q->tpd.consume_idx; ++ ++ return (consume_idx > produce_idx) ? ++ (consume_idx - produce_idx - 1) : ++ (tx_q->tpd.count + consume_idx - produce_idx - 1); ++} ++ ++/* Process transmit event */ ++void emac_mac_tx_process(struct emac_adapter *adpt, struct emac_tx_queue *tx_q) ++{ ++ u32 reg = readl_relaxed(adpt->base + tx_q->consume_reg); ++ u32 hw_consume_idx, pkts_compl = 0, bytes_compl = 0; ++ struct emac_buffer *tpbuf; ++ ++ hw_consume_idx = (reg & tx_q->consume_mask) >> tx_q->consume_shift; ++ ++ while (tx_q->tpd.consume_idx != hw_consume_idx) { ++ tpbuf = GET_TPD_BUFFER(tx_q, tx_q->tpd.consume_idx); ++ if (tpbuf->dma_addr) { ++ dma_unmap_single(adpt->netdev->dev.parent, ++ tpbuf->dma_addr, tpbuf->length, ++ DMA_TO_DEVICE); ++ tpbuf->dma_addr = 0; ++ } ++ ++ if (tpbuf->skb) { ++ pkts_compl++; ++ bytes_compl += tpbuf->skb->len; ++ dev_kfree_skb_irq(tpbuf->skb); ++ tpbuf->skb = NULL; ++ } ++ ++ if (++tx_q->tpd.consume_idx == tx_q->tpd.count) ++ tx_q->tpd.consume_idx = 0; ++ } ++ ++ netdev_completed_queue(adpt->netdev, pkts_compl, bytes_compl); ++ ++ if (netif_queue_stopped(adpt->netdev)) ++ if (emac_tpd_num_free_descs(tx_q) > (MAX_SKB_FRAGS + 1)) ++ netif_wake_queue(adpt->netdev); ++} ++ ++/* Initialize all queue data structures */ ++void emac_mac_rx_tx_ring_init_all(struct platform_device *pdev, ++ struct emac_adapter *adpt) ++{ ++ adpt->rx_q.netdev = adpt->netdev; ++ ++ adpt->rx_q.produce_reg = EMAC_MAILBOX_0; ++ adpt->rx_q.produce_mask = RFD0_PROD_IDX_BMSK; ++ adpt->rx_q.produce_shift = RFD0_PROD_IDX_SHFT; ++ ++ adpt->rx_q.process_reg = EMAC_MAILBOX_0; ++ adpt->rx_q.process_mask = RFD0_PROC_IDX_BMSK; ++ adpt->rx_q.process_shft = RFD0_PROC_IDX_SHFT; ++ ++ adpt->rx_q.consume_reg = EMAC_MAILBOX_3; ++ adpt->rx_q.consume_mask = RFD0_CONS_IDX_BMSK; ++ adpt->rx_q.consume_shift = RFD0_CONS_IDX_SHFT; ++ ++ adpt->rx_q.irq = &adpt->irq; ++ adpt->rx_q.intr = adpt->irq.mask & ISR_RX_PKT; ++ ++ adpt->tx_q.produce_reg = EMAC_MAILBOX_15; ++ adpt->tx_q.produce_mask = NTPD_PROD_IDX_BMSK; ++ adpt->tx_q.produce_shift = NTPD_PROD_IDX_SHFT; ++ ++ adpt->tx_q.consume_reg = EMAC_MAILBOX_2; ++ adpt->tx_q.consume_mask = NTPD_CONS_IDX_BMSK; ++ adpt->tx_q.consume_shift = NTPD_CONS_IDX_SHFT; ++} ++ ++/* Fill up transmit descriptors with TSO and Checksum offload information */ ++static int emac_tso_csum(struct emac_adapter *adpt, ++ struct emac_tx_queue *tx_q, ++ struct sk_buff *skb, ++ struct emac_tpd *tpd) ++{ ++ unsigned int hdr_len; ++ int ret; ++ ++ if (skb_is_gso(skb)) { ++ if (skb_header_cloned(skb)) { ++ ret = pskb_expand_head(skb, 0, 0, GFP_ATOMIC); ++ if (unlikely(ret)) ++ return ret; ++ } ++ ++ if (skb->protocol == htons(ETH_P_IP)) { ++ u32 pkt_len = ((unsigned char *)ip_hdr(skb) - skb->data) ++ + ntohs(ip_hdr(skb)->tot_len); ++ if (skb->len > pkt_len) ++ pskb_trim(skb, pkt_len); ++ } ++ ++ hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); ++ if (unlikely(skb->len == hdr_len)) { ++ /* we only need to do csum */ ++ netif_warn(adpt, tx_err, adpt->netdev, ++ "tso not needed for packet with 0 data\n"); ++ goto do_csum; ++ } ++ ++ if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV4) { ++ ip_hdr(skb)->check = 0; ++ tcp_hdr(skb)->check = ++ ~csum_tcpudp_magic(ip_hdr(skb)->saddr, ++ ip_hdr(skb)->daddr, ++ 0, IPPROTO_TCP, 0); ++ TPD_IPV4_SET(tpd, 1); ++ } ++ ++ if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6) { ++ /* ipv6 tso need an extra tpd */ ++ struct emac_tpd extra_tpd; ++ ++ memset(tpd, 0, sizeof(*tpd)); ++ memset(&extra_tpd, 0, sizeof(extra_tpd)); ++ ++ ipv6_hdr(skb)->payload_len = 0; ++ tcp_hdr(skb)->check = ++ ~csum_ipv6_magic(&ipv6_hdr(skb)->saddr, ++ &ipv6_hdr(skb)->daddr, ++ 0, IPPROTO_TCP, 0); ++ TPD_PKT_LEN_SET(&extra_tpd, skb->len); ++ TPD_LSO_SET(&extra_tpd, 1); ++ TPD_LSOV_SET(&extra_tpd, 1); ++ emac_tx_tpd_create(adpt, tx_q, &extra_tpd); ++ TPD_LSOV_SET(tpd, 1); ++ } ++ ++ TPD_LSO_SET(tpd, 1); ++ TPD_TCPHDR_OFFSET_SET(tpd, skb_transport_offset(skb)); ++ TPD_MSS_SET(tpd, skb_shinfo(skb)->gso_size); ++ return 0; ++ } ++ ++do_csum: ++ if (likely(skb->ip_summed == CHECKSUM_PARTIAL)) { ++ unsigned int css, cso; ++ ++ cso = skb_transport_offset(skb); ++ if (unlikely(cso & 0x1)) { ++ netdev_err(adpt->netdev, ++ "error: payload offset should be even\n"); ++ return -EINVAL; ++ } ++ css = cso + skb->csum_offset; ++ ++ TPD_PAYLOAD_OFFSET_SET(tpd, cso >> 1); ++ TPD_CXSUM_OFFSET_SET(tpd, css >> 1); ++ TPD_CSX_SET(tpd, 1); ++ } ++ ++ return 0; ++} ++ ++/* Fill up transmit descriptors */ ++static void emac_tx_fill_tpd(struct emac_adapter *adpt, ++ struct emac_tx_queue *tx_q, struct sk_buff *skb, ++ struct emac_tpd *tpd) ++{ ++ unsigned int nr_frags = skb_shinfo(skb)->nr_frags; ++ unsigned int first = tx_q->tpd.produce_idx; ++ unsigned int len = skb_headlen(skb); ++ struct emac_buffer *tpbuf = NULL; ++ unsigned int mapped_len = 0; ++ unsigned int i; ++ int count = 0; ++ int ret; ++ ++ /* if Large Segment Offload is (in TCP Segmentation Offload struct) */ ++ if (TPD_LSO(tpd)) { ++ mapped_len = skb_transport_offset(skb) + tcp_hdrlen(skb); ++ ++ tpbuf = GET_TPD_BUFFER(tx_q, tx_q->tpd.produce_idx); ++ tpbuf->length = mapped_len; ++ tpbuf->dma_addr = dma_map_single(adpt->netdev->dev.parent, ++ skb->data, tpbuf->length, ++ DMA_TO_DEVICE); ++ ret = dma_mapping_error(adpt->netdev->dev.parent, ++ tpbuf->dma_addr); ++ if (ret) ++ goto error; ++ ++ TPD_BUFFER_ADDR_L_SET(tpd, lower_32_bits(tpbuf->dma_addr)); ++ TPD_BUFFER_ADDR_H_SET(tpd, upper_32_bits(tpbuf->dma_addr)); ++ TPD_BUF_LEN_SET(tpd, tpbuf->length); ++ emac_tx_tpd_create(adpt, tx_q, tpd); ++ count++; ++ } ++ ++ if (mapped_len < len) { ++ tpbuf = GET_TPD_BUFFER(tx_q, tx_q->tpd.produce_idx); ++ tpbuf->length = len - mapped_len; ++ tpbuf->dma_addr = dma_map_single(adpt->netdev->dev.parent, ++ skb->data + mapped_len, ++ tpbuf->length, DMA_TO_DEVICE); ++ ret = dma_mapping_error(adpt->netdev->dev.parent, ++ tpbuf->dma_addr); ++ if (ret) ++ goto error; ++ ++ TPD_BUFFER_ADDR_L_SET(tpd, lower_32_bits(tpbuf->dma_addr)); ++ TPD_BUFFER_ADDR_H_SET(tpd, upper_32_bits(tpbuf->dma_addr)); ++ TPD_BUF_LEN_SET(tpd, tpbuf->length); ++ emac_tx_tpd_create(adpt, tx_q, tpd); ++ count++; ++ } ++ ++ for (i = 0; i < nr_frags; i++) { ++ struct skb_frag_struct *frag; ++ ++ frag = &skb_shinfo(skb)->frags[i]; ++ ++ tpbuf = GET_TPD_BUFFER(tx_q, tx_q->tpd.produce_idx); ++ tpbuf->length = frag->size; ++ tpbuf->dma_addr = dma_map_page(adpt->netdev->dev.parent, ++ frag->page.p, frag->page_offset, ++ tpbuf->length, DMA_TO_DEVICE); ++ ret = dma_mapping_error(adpt->netdev->dev.parent, ++ tpbuf->dma_addr); ++ if (ret) ++ goto error; ++ ++ TPD_BUFFER_ADDR_L_SET(tpd, lower_32_bits(tpbuf->dma_addr)); ++ TPD_BUFFER_ADDR_H_SET(tpd, upper_32_bits(tpbuf->dma_addr)); ++ TPD_BUF_LEN_SET(tpd, tpbuf->length); ++ emac_tx_tpd_create(adpt, tx_q, tpd); ++ count++; ++ } ++ ++ /* The last tpd */ ++ wmb(); ++ emac_tx_tpd_mark_last(adpt, tx_q); ++ ++ /* The last buffer info contain the skb address, ++ * so it will be freed after unmap ++ */ ++ tpbuf->skb = skb; ++ ++ return; ++ ++error: ++ /* One of the memory mappings failed, so undo everything */ ++ tx_q->tpd.produce_idx = first; ++ ++ while (count--) { ++ tpbuf = GET_TPD_BUFFER(tx_q, first); ++ dma_unmap_page(adpt->netdev->dev.parent, tpbuf->dma_addr, ++ tpbuf->length, DMA_TO_DEVICE); ++ tpbuf->dma_addr = 0; ++ tpbuf->length = 0; ++ ++ if (++first == tx_q->tpd.count) ++ first = 0; ++ } ++ ++ dev_kfree_skb(skb); ++} ++ ++/* Transmit the packet using specified transmit queue */ ++int emac_mac_tx_buf_send(struct emac_adapter *adpt, struct emac_tx_queue *tx_q, ++ struct sk_buff *skb) ++{ ++ struct emac_tpd tpd; ++ u32 prod_idx; ++ ++ memset(&tpd, 0, sizeof(tpd)); ++ ++ if (emac_tso_csum(adpt, tx_q, skb, &tpd) != 0) { ++ dev_kfree_skb_any(skb); ++ return NETDEV_TX_OK; ++ } ++ ++ if (skb_vlan_tag_present(skb)) { ++ u16 tag; ++ ++ EMAC_VLAN_TO_TAG(skb_vlan_tag_get(skb), tag); ++ TPD_CVLAN_TAG_SET(&tpd, tag); ++ TPD_INSTC_SET(&tpd, 1); ++ } ++ ++ if (skb_network_offset(skb) != ETH_HLEN) ++ TPD_TYP_SET(&tpd, 1); ++ ++ emac_tx_fill_tpd(adpt, tx_q, skb, &tpd); ++ ++ netdev_sent_queue(adpt->netdev, skb->len); ++ ++ /* Make sure the are enough free descriptors to hold one ++ * maximum-sized SKB. We need one desc for each fragment, ++ * one for the checksum (emac_tso_csum), one for TSO, and ++ * and one for the SKB header. ++ */ ++ if (emac_tpd_num_free_descs(tx_q) < (MAX_SKB_FRAGS + 3)) ++ netif_stop_queue(adpt->netdev); ++ ++ /* update produce idx */ ++ prod_idx = (tx_q->tpd.produce_idx << tx_q->produce_shift) & ++ tx_q->produce_mask; ++ emac_reg_update32(adpt->base + tx_q->produce_reg, ++ tx_q->produce_mask, prod_idx); ++ ++ return NETDEV_TX_OK; ++} +diff --git a/drivers/net/ethernet/qualcomm/emac/emac-mac.h b/drivers/net/ethernet/qualcomm/emac/emac-mac.h +new file mode 100644 +index 0000000..f3aa24d +--- /dev/null ++++ b/drivers/net/ethernet/qualcomm/emac/emac-mac.h +@@ -0,0 +1,248 @@ ++/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that 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. ++ */ ++ ++/* EMAC DMA HW engine uses three rings: ++ * Tx: ++ * TPD: Transmit Packet Descriptor ring. ++ * Rx: ++ * RFD: Receive Free Descriptor ring. ++ * Ring of descriptors with empty buffers to be filled by Rx HW. ++ * RRD: Receive Return Descriptor ring. ++ * Ring of descriptors with buffers filled with received data. ++ */ ++ ++#ifndef _EMAC_HW_H_ ++#define _EMAC_HW_H_ ++ ++/* EMAC_CSR register offsets */ ++#define EMAC_EMAC_WRAPPER_CSR1 0x000000 ++#define EMAC_EMAC_WRAPPER_CSR2 0x000004 ++#define EMAC_EMAC_WRAPPER_TX_TS_LO 0x000104 ++#define EMAC_EMAC_WRAPPER_TX_TS_HI 0x000108 ++#define EMAC_EMAC_WRAPPER_TX_TS_INX 0x00010c ++ ++/* DMA Order Settings */ ++enum emac_dma_order { ++ emac_dma_ord_in = 1, ++ emac_dma_ord_enh = 2, ++ emac_dma_ord_out = 4 ++}; ++ ++enum emac_dma_req_block { ++ emac_dma_req_128 = 0, ++ emac_dma_req_256 = 1, ++ emac_dma_req_512 = 2, ++ emac_dma_req_1024 = 3, ++ emac_dma_req_2048 = 4, ++ emac_dma_req_4096 = 5 ++}; ++ ++/* Returns the value of bits idx...idx+n_bits */ ++#define BITS_GET(val, lo, hi) ((le32_to_cpu(val) & GENMASK((hi), (lo))) >> lo) ++#define BITS_SET(val, lo, hi, new_val) \ ++ val = cpu_to_le32((le32_to_cpu(val) & (~GENMASK((hi), (lo)))) | \ ++ (((new_val) << (lo)) & GENMASK((hi), (lo)))) ++ ++/* RRD (Receive Return Descriptor) */ ++struct emac_rrd { ++ u32 word[6]; ++ ++/* number of RFD */ ++#define RRD_NOR(rrd) BITS_GET((rrd)->word[0], 16, 19) ++/* start consumer index of rfd-ring */ ++#define RRD_SI(rrd) BITS_GET((rrd)->word[0], 20, 31) ++/* vlan-tag (CVID, CFI and PRI) */ ++#define RRD_CVALN_TAG(rrd) BITS_GET((rrd)->word[2], 0, 15) ++/* length of the packet */ ++#define RRD_PKT_SIZE(rrd) BITS_GET((rrd)->word[3], 0, 13) ++/* L4(TCP/UDP) checksum failed */ ++#define RRD_L4F(rrd) BITS_GET((rrd)->word[3], 14, 14) ++/* vlan tagged */ ++#define RRD_CVTAG(rrd) BITS_GET((rrd)->word[3], 16, 16) ++/* When set, indicates that the descriptor is updated by the IP core. ++ * When cleared, indicates that the descriptor is invalid. ++ */ ++#define RRD_UPDT(rrd) BITS_GET((rrd)->word[3], 31, 31) ++#define RRD_UPDT_SET(rrd, val) BITS_SET((rrd)->word[3], 31, 31, val) ++/* timestamp low */ ++#define RRD_TS_LOW(rrd) BITS_GET((rrd)->word[4], 0, 29) ++/* timestamp high */ ++#define RRD_TS_HI(rrd) le32_to_cpu((rrd)->word[5]) ++}; ++ ++/* TPD (Transmit Packet Descriptor) */ ++struct emac_tpd { ++ u32 word[4]; ++ ++/* Number of bytes of the transmit packet. (include 4-byte CRC) */ ++#define TPD_BUF_LEN_SET(tpd, val) BITS_SET((tpd)->word[0], 0, 15, val) ++/* Custom Checksum Offload: When set, ask IP core to offload custom checksum */ ++#define TPD_CSX_SET(tpd, val) BITS_SET((tpd)->word[1], 8, 8, val) ++/* TCP Large Send Offload: When set, ask IP core to do offload TCP Large Send */ ++#define TPD_LSO(tpd) BITS_GET((tpd)->word[1], 12, 12) ++#define TPD_LSO_SET(tpd, val) BITS_SET((tpd)->word[1], 12, 12, val) ++/* Large Send Offload Version: When set, indicates this is an LSOv2 ++ * (for both IPv4 and IPv6). When cleared, indicates this is an LSOv1 ++ * (only for IPv4). ++ */ ++#define TPD_LSOV_SET(tpd, val) BITS_SET((tpd)->word[1], 13, 13, val) ++/* IPv4 packet: When set, indicates this is an IPv4 packet, this bit is only ++ * for LSOV2 format. ++ */ ++#define TPD_IPV4_SET(tpd, val) BITS_SET((tpd)->word[1], 16, 16, val) ++/* 0: Ethernet frame (DA+SA+TYPE+DATA+CRC) ++ * 1: IEEE 802.3 frame (DA+SA+LEN+DSAP+SSAP+CTL+ORG+TYPE+DATA+CRC) ++ */ ++#define TPD_TYP_SET(tpd, val) BITS_SET((tpd)->word[1], 17, 17, val) ++/* Low-32bit Buffer Address */ ++#define TPD_BUFFER_ADDR_L_SET(tpd, val) ((tpd)->word[2] = cpu_to_le32(val)) ++/* CVLAN Tag to be inserted if INS_VLAN_TAG is set, CVLAN TPID based on global ++ * register configuration. ++ */ ++#define TPD_CVLAN_TAG_SET(tpd, val) BITS_SET((tpd)->word[3], 0, 15, val) ++/* Insert CVlan Tag: When set, ask MAC to insert CVLAN TAG to outgoing packet ++ */ ++#define TPD_INSTC_SET(tpd, val) BITS_SET((tpd)->word[3], 17, 17, val) ++/* High-14bit Buffer Address, So, the 64b-bit address is ++ * {DESC_CTRL_11_TX_DATA_HIADDR[17:0],(register) BUFFER_ADDR_H, BUFFER_ADDR_L} ++ */ ++#define TPD_BUFFER_ADDR_H_SET(tpd, val) BITS_SET((tpd)->word[3], 18, 30, val) ++/* Format D. Word offset from the 1st byte of this packet to start to calculate ++ * the custom checksum. ++ */ ++#define TPD_PAYLOAD_OFFSET_SET(tpd, val) BITS_SET((tpd)->word[1], 0, 7, val) ++/* Format D. Word offset from the 1st byte of this packet to fill the custom ++ * checksum to ++ */ ++#define TPD_CXSUM_OFFSET_SET(tpd, val) BITS_SET((tpd)->word[1], 18, 25, val) ++ ++/* Format C. TCP Header offset from the 1st byte of this packet. (byte unit) */ ++#define TPD_TCPHDR_OFFSET_SET(tpd, val) BITS_SET((tpd)->word[1], 0, 7, val) ++/* Format C. MSS (Maximum Segment Size) got from the protocol layer. (byte unit) ++ */ ++#define TPD_MSS_SET(tpd, val) BITS_SET((tpd)->word[1], 18, 30, val) ++/* packet length in ext tpd */ ++#define TPD_PKT_LEN_SET(tpd, val) ((tpd)->word[2] = cpu_to_le32(val)) ++}; ++ ++/* emac_ring_header represents a single, contiguous block of DMA space ++ * mapped for the three descriptor rings (tpd, rfd, rrd) ++ */ ++struct emac_ring_header { ++ void *v_addr; /* virtual address */ ++ dma_addr_t dma_addr; /* dma address */ ++ size_t size; /* length in bytes */ ++ size_t used; ++}; ++ ++/* emac_buffer is wrapper around a pointer to a socket buffer ++ * so a DMA handle can be stored along with the skb ++ */ ++struct emac_buffer { ++ struct sk_buff *skb; /* socket buffer */ ++ u16 length; /* rx buffer length */ ++ dma_addr_t dma_addr; /* dma address */ ++}; ++ ++/* receive free descriptor (rfd) ring */ ++struct emac_rfd_ring { ++ struct emac_buffer *rfbuff; ++ u32 *v_addr; /* virtual address */ ++ dma_addr_t dma_addr; /* dma address */ ++ size_t size; /* length in bytes */ ++ unsigned int count; /* number of desc in the ring */ ++ unsigned int produce_idx; ++ unsigned int process_idx; ++ unsigned int consume_idx; /* unused */ ++}; ++ ++/* Receive Return Desciptor (RRD) ring */ ++struct emac_rrd_ring { ++ u32 *v_addr; /* virtual address */ ++ dma_addr_t dma_addr; /* physical address */ ++ size_t size; /* length in bytes */ ++ unsigned int count; /* number of desc in the ring */ ++ unsigned int produce_idx; /* unused */ ++ unsigned int consume_idx; ++}; ++ ++/* Rx queue */ ++struct emac_rx_queue { ++ struct net_device *netdev; /* netdev ring belongs to */ ++ struct emac_rrd_ring rrd; ++ struct emac_rfd_ring rfd; ++ struct napi_struct napi; ++ struct emac_irq *irq; ++ ++ u32 intr; ++ u32 produce_mask; ++ u32 process_mask; ++ u32 consume_mask; ++ ++ u16 produce_reg; ++ u16 process_reg; ++ u16 consume_reg; ++ ++ u8 produce_shift; ++ u8 process_shft; ++ u8 consume_shift; ++}; ++ ++/* Transimit Packet Descriptor (tpd) ring */ ++struct emac_tpd_ring { ++ struct emac_buffer *tpbuff; ++ u32 *v_addr; /* virtual address */ ++ dma_addr_t dma_addr; /* dma address */ ++ ++ size_t size; /* length in bytes */ ++ unsigned int count; /* number of desc in the ring */ ++ unsigned int produce_idx; ++ unsigned int consume_idx; ++ unsigned int last_produce_idx; ++}; ++ ++/* Tx queue */ ++struct emac_tx_queue { ++ struct emac_tpd_ring tpd; ++ ++ u32 produce_mask; ++ u32 consume_mask; ++ ++ u16 max_packets; /* max packets per interrupt */ ++ u16 produce_reg; ++ u16 consume_reg; ++ ++ u8 produce_shift; ++ u8 consume_shift; ++}; ++ ++struct emac_adapter; ++ ++int emac_mac_up(struct emac_adapter *adpt); ++void emac_mac_down(struct emac_adapter *adpt); ++void emac_mac_reset(struct emac_adapter *adpt); ++void emac_mac_start(struct emac_adapter *adpt); ++void emac_mac_stop(struct emac_adapter *adpt); ++void emac_mac_mode_config(struct emac_adapter *adpt); ++void emac_mac_rx_process(struct emac_adapter *adpt, struct emac_rx_queue *rx_q, ++ int *num_pkts, int max_pkts); ++int emac_mac_tx_buf_send(struct emac_adapter *adpt, struct emac_tx_queue *tx_q, ++ struct sk_buff *skb); ++void emac_mac_tx_process(struct emac_adapter *adpt, struct emac_tx_queue *tx_q); ++void emac_mac_rx_tx_ring_init_all(struct platform_device *pdev, ++ struct emac_adapter *adpt); ++int emac_mac_rx_tx_rings_alloc_all(struct emac_adapter *adpt); ++void emac_mac_rx_tx_rings_free_all(struct emac_adapter *adpt); ++void emac_mac_multicast_addr_clear(struct emac_adapter *adpt); ++void emac_mac_multicast_addr_set(struct emac_adapter *adpt, u8 *addr); ++ ++#endif /*_EMAC_HW_H_*/ +diff --git a/drivers/net/ethernet/qualcomm/emac/emac-phy.c b/drivers/net/ethernet/qualcomm/emac/emac-phy.c +new file mode 100644 +index 0000000..c412ba9 +--- /dev/null ++++ b/drivers/net/ethernet/qualcomm/emac/emac-phy.c +@@ -0,0 +1,204 @@ ++/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that 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. ++ */ ++ ++/* Qualcomm Technologies, Inc. EMAC PHY Controller driver. ++ */ ++ ++#include <linux/module.h> ++#include <linux/of.h> ++#include <linux/of_net.h> ++#include <linux/of_mdio.h> ++#include <linux/phy.h> ++#include <linux/iopoll.h> ++#include "emac.h" ++#include "emac-mac.h" ++#include "emac-phy.h" ++#include "emac-sgmii.h" ++ ++/* EMAC base register offsets */ ++#define EMAC_MDIO_CTRL 0x001414 ++#define EMAC_PHY_STS 0x001418 ++#define EMAC_MDIO_EX_CTRL 0x001440 ++ ++/* EMAC_MDIO_CTRL */ ++#define MDIO_MODE BIT(30) ++#define MDIO_PR BIT(29) ++#define MDIO_AP_EN BIT(28) ++#define MDIO_BUSY BIT(27) ++#define MDIO_CLK_SEL_BMSK 0x7000000 ++#define MDIO_CLK_SEL_SHFT 24 ++#define MDIO_START BIT(23) ++#define SUP_PREAMBLE BIT(22) ++#define MDIO_RD_NWR BIT(21) ++#define MDIO_REG_ADDR_BMSK 0x1f0000 ++#define MDIO_REG_ADDR_SHFT 16 ++#define MDIO_DATA_BMSK 0xffff ++#define MDIO_DATA_SHFT 0 ++ ++/* EMAC_PHY_STS */ ++#define PHY_ADDR_BMSK 0x1f0000 ++#define PHY_ADDR_SHFT 16 ++ ++#define MDIO_CLK_25_4 0 ++#define MDIO_CLK_25_28 7 ++ ++#define MDIO_WAIT_TIMES 1000 ++ ++#define EMAC_LINK_SPEED_DEFAULT (\ ++ EMAC_LINK_SPEED_10_HALF |\ ++ EMAC_LINK_SPEED_10_FULL |\ ++ EMAC_LINK_SPEED_100_HALF |\ ++ EMAC_LINK_SPEED_100_FULL |\ ++ EMAC_LINK_SPEED_1GB_FULL) ++ ++/** ++ * emac_phy_mdio_autopoll_disable() - disable mdio autopoll ++ * @adpt: the emac adapter ++ * ++ * The autopoll feature takes over the MDIO bus. In order for ++ * the PHY driver to be able to talk to the PHY over the MDIO ++ * bus, we need to temporarily disable the autopoll feature. ++ */ ++static int emac_phy_mdio_autopoll_disable(struct emac_adapter *adpt) ++{ ++ u32 val; ++ ++ /* disable autopoll */ ++ emac_reg_update32(adpt->base + EMAC_MDIO_CTRL, MDIO_AP_EN, 0); ++ ++ /* wait for any mdio polling to complete */ ++ if (!readl_poll_timeout(adpt->base + EMAC_MDIO_CTRL, val, ++ !(val & MDIO_BUSY), 100, MDIO_WAIT_TIMES * 100)) ++ return 0; ++ ++ /* failed to disable; ensure it is enabled before returning */ ++ emac_reg_update32(adpt->base + EMAC_MDIO_CTRL, 0, MDIO_AP_EN); ++ ++ return -EBUSY; ++} ++ ++/** ++ * emac_phy_mdio_autopoll_disable() - disable mdio autopoll ++ * @adpt: the emac adapter ++ * ++ * The EMAC has the ability to poll the external PHY on the MDIO ++ * bus for link state changes. This eliminates the need for the ++ * driver to poll the phy. If if the link state does change, ++ * the EMAC issues an interrupt on behalf of the PHY. ++ */ ++static void emac_phy_mdio_autopoll_enable(struct emac_adapter *adpt) ++{ ++ emac_reg_update32(adpt->base + EMAC_MDIO_CTRL, 0, MDIO_AP_EN); ++} ++ ++static int emac_mdio_read(struct mii_bus *bus, int addr, int regnum) ++{ ++ struct emac_adapter *adpt = bus->priv; ++ u32 reg; ++ int ret; ++ ++ ret = emac_phy_mdio_autopoll_disable(adpt); ++ if (ret) ++ return ret; ++ ++ emac_reg_update32(adpt->base + EMAC_PHY_STS, PHY_ADDR_BMSK, ++ (addr << PHY_ADDR_SHFT)); ++ ++ reg = SUP_PREAMBLE | ++ ((MDIO_CLK_25_4 << MDIO_CLK_SEL_SHFT) & MDIO_CLK_SEL_BMSK) | ++ ((regnum << MDIO_REG_ADDR_SHFT) & MDIO_REG_ADDR_BMSK) | ++ MDIO_START | MDIO_RD_NWR; ++ ++ writel(reg, adpt->base + EMAC_MDIO_CTRL); ++ ++ if (readl_poll_timeout(adpt->base + EMAC_MDIO_CTRL, reg, ++ !(reg & (MDIO_START | MDIO_BUSY)), ++ 100, MDIO_WAIT_TIMES * 100)) ++ ret = -EIO; ++ else ++ ret = (reg >> MDIO_DATA_SHFT) & MDIO_DATA_BMSK; ++ ++ emac_phy_mdio_autopoll_enable(adpt); ++ ++ return ret; ++} ++ ++static int emac_mdio_write(struct mii_bus *bus, int addr, int regnum, u16 val) ++{ ++ struct emac_adapter *adpt = bus->priv; ++ u32 reg; ++ int ret; ++ ++ ret = emac_phy_mdio_autopoll_disable(adpt); ++ if (ret) ++ return ret; ++ ++ emac_reg_update32(adpt->base + EMAC_PHY_STS, PHY_ADDR_BMSK, ++ (addr << PHY_ADDR_SHFT)); ++ ++ reg = SUP_PREAMBLE | ++ ((MDIO_CLK_25_4 << MDIO_CLK_SEL_SHFT) & MDIO_CLK_SEL_BMSK) | ++ ((regnum << MDIO_REG_ADDR_SHFT) & MDIO_REG_ADDR_BMSK) | ++ ((val << MDIO_DATA_SHFT) & MDIO_DATA_BMSK) | ++ MDIO_START; ++ ++ writel(reg, adpt->base + EMAC_MDIO_CTRL); ++ ++ if (readl_poll_timeout(adpt->base + EMAC_MDIO_CTRL, reg, ++ !(reg & (MDIO_START | MDIO_BUSY)), 100, ++ MDIO_WAIT_TIMES * 100)) ++ ret = -EIO; ++ ++ emac_phy_mdio_autopoll_enable(adpt); ++ ++ return ret; ++} ++ ++/* Configure the MDIO bus and connect the external PHY */ ++int emac_phy_config(struct platform_device *pdev, struct emac_adapter *adpt) ++{ ++ struct device_node *np = pdev->dev.of_node; ++ struct device_node *phy_np; ++ struct mii_bus *mii_bus; ++ int ret; ++ ++ /* Create the mii_bus object for talking to the MDIO bus */ ++ adpt->mii_bus = mii_bus = devm_mdiobus_alloc(&pdev->dev); ++ if (!mii_bus) ++ return -ENOMEM; ++ ++ mii_bus->name = "emac-mdio"; ++ snprintf(mii_bus->id, MII_BUS_ID_SIZE, "%s", pdev->name); ++ mii_bus->read = emac_mdio_read; ++ mii_bus->write = emac_mdio_write; ++ mii_bus->parent = &pdev->dev; ++ mii_bus->priv = adpt; ++ ++ ret = of_mdiobus_register(mii_bus, np); ++ if (ret) { ++ dev_err(&pdev->dev, "could not register mdio bus\n"); ++ return ret; ++ } ++ ++ phy_np = of_parse_phandle(np, "phy-handle", 0); ++ adpt->phydev = of_phy_find_device(phy_np); ++ if (!adpt->phydev) { ++ dev_err(&pdev->dev, "could not find external phy\n"); ++ mdiobus_unregister(mii_bus); ++ return -ENODEV; ++ } ++ ++ if (adpt->phydev->drv) ++ phy_attached_print(adpt->phydev, NULL); ++ ++ return 0; ++} +diff --git a/drivers/net/ethernet/qualcomm/emac/emac-phy.h b/drivers/net/ethernet/qualcomm/emac/emac-phy.h +new file mode 100644 +index 0000000..49f3701 +--- /dev/null ++++ b/drivers/net/ethernet/qualcomm/emac/emac-phy.h +@@ -0,0 +1,33 @@ ++/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. ++* ++* This program is free software; you can redistribute it and/or modify ++* it under the terms of the GNU General Public License version 2 and ++* only version 2 as published by the Free Software Foundation. ++* ++* This program is distributed in the hope that 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. ++*/ ++ ++#ifndef _EMAC_PHY_H_ ++#define _EMAC_PHY_H_ ++ ++typedef int (*emac_sgmii_initialize)(struct emac_adapter *adpt); ++ ++/** emac_phy - internal emac phy ++ * @base base address ++ * @digital per-lane digital block ++ * @initialize initialization function ++ */ ++struct emac_phy { ++ void __iomem *base; ++ void __iomem *digital; ++ emac_sgmii_initialize initialize; ++}; ++ ++struct emac_adapter; ++ ++int emac_phy_config(struct platform_device *pdev, struct emac_adapter *adpt); ++ ++#endif /* _EMAC_PHY_H_ */ +diff --git a/drivers/net/ethernet/qualcomm/emac/emac-sgmii.c b/drivers/net/ethernet/qualcomm/emac/emac-sgmii.c +new file mode 100644 +index 0000000..6ab0a3c +--- /dev/null ++++ b/drivers/net/ethernet/qualcomm/emac/emac-sgmii.c +@@ -0,0 +1,721 @@ ++/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that 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. ++ */ ++ ++/* Qualcomm Technologies, Inc. EMAC SGMII Controller driver. ++ */ ++ ++#include <linux/iopoll.h> ++#include <linux/of_device.h> ++#include "emac.h" ++#include "emac-mac.h" ++#include "emac-sgmii.h" ++ ++/* EMAC_QSERDES register offsets */ ++#define EMAC_QSERDES_COM_SYS_CLK_CTRL 0x000000 ++#define EMAC_QSERDES_COM_PLL_CNTRL 0x000014 ++#define EMAC_QSERDES_COM_PLL_IP_SETI 0x000018 ++#define EMAC_QSERDES_COM_PLL_CP_SETI 0x000024 ++#define EMAC_QSERDES_COM_PLL_IP_SETP 0x000028 ++#define EMAC_QSERDES_COM_PLL_CP_SETP 0x00002c ++#define EMAC_QSERDES_COM_SYSCLK_EN_SEL 0x000038 ++#define EMAC_QSERDES_COM_RESETSM_CNTRL 0x000040 ++#define EMAC_QSERDES_COM_PLLLOCK_CMP1 0x000044 ++#define EMAC_QSERDES_COM_PLLLOCK_CMP2 0x000048 ++#define EMAC_QSERDES_COM_PLLLOCK_CMP3 0x00004c ++#define EMAC_QSERDES_COM_PLLLOCK_CMP_EN 0x000050 ++#define EMAC_QSERDES_COM_DEC_START1 0x000064 ++#define EMAC_QSERDES_COM_DIV_FRAC_START1 0x000098 ++#define EMAC_QSERDES_COM_DIV_FRAC_START2 0x00009c ++#define EMAC_QSERDES_COM_DIV_FRAC_START3 0x0000a0 ++#define EMAC_QSERDES_COM_DEC_START2 0x0000a4 ++#define EMAC_QSERDES_COM_PLL_CRCTRL 0x0000ac ++#define EMAC_QSERDES_COM_RESET_SM 0x0000bc ++#define EMAC_QSERDES_TX_BIST_MODE_LANENO 0x000100 ++#define EMAC_QSERDES_TX_TX_EMP_POST1_LVL 0x000108 ++#define EMAC_QSERDES_TX_TX_DRV_LVL 0x00010c ++#define EMAC_QSERDES_TX_LANE_MODE 0x000150 ++#define EMAC_QSERDES_TX_TRAN_DRVR_EMP_EN 0x000170 ++#define EMAC_QSERDES_RX_CDR_CONTROL 0x000200 ++#define EMAC_QSERDES_RX_CDR_CONTROL2 0x000210 ++#define EMAC_QSERDES_RX_RX_EQ_GAIN12 0x000230 ++ ++/* EMAC_SGMII register offsets */ ++#define EMAC_SGMII_PHY_SERDES_START 0x000000 ++#define EMAC_SGMII_PHY_CMN_PWR_CTRL 0x000004 ++#define EMAC_SGMII_PHY_RX_PWR_CTRL 0x000008 ++#define EMAC_SGMII_PHY_TX_PWR_CTRL 0x00000C ++#define EMAC_SGMII_PHY_LANE_CTRL1 0x000018 ++#define EMAC_SGMII_PHY_AUTONEG_CFG2 0x000048 ++#define EMAC_SGMII_PHY_CDR_CTRL0 0x000058 ++#define EMAC_SGMII_PHY_SPEED_CFG1 0x000074 ++#define EMAC_SGMII_PHY_POW_DWN_CTRL0 0x000080 ++#define EMAC_SGMII_PHY_RESET_CTRL 0x0000a8 ++#define EMAC_SGMII_PHY_IRQ_CMD 0x0000ac ++#define EMAC_SGMII_PHY_INTERRUPT_CLEAR 0x0000b0 ++#define EMAC_SGMII_PHY_INTERRUPT_MASK 0x0000b4 ++#define EMAC_SGMII_PHY_INTERRUPT_STATUS 0x0000b8 ++#define EMAC_SGMII_PHY_RX_CHK_STATUS 0x0000d4 ++#define EMAC_SGMII_PHY_AUTONEG0_STATUS 0x0000e0 ++#define EMAC_SGMII_PHY_AUTONEG1_STATUS 0x0000e4 ++ ++/* EMAC_QSERDES_COM_PLL_IP_SETI */ ++#define PLL_IPSETI(x) ((x) & 0x3f) ++ ++/* EMAC_QSERDES_COM_PLL_CP_SETI */ ++#define PLL_CPSETI(x) ((x) & 0xff) ++ ++/* EMAC_QSERDES_COM_PLL_IP_SETP */ ++#define PLL_IPSETP(x) ((x) & 0x3f) ++ ++/* EMAC_QSERDES_COM_PLL_CP_SETP */ ++#define PLL_CPSETP(x) ((x) & 0x1f) ++ ++/* EMAC_QSERDES_COM_PLL_CRCTRL */ ++#define PLL_RCTRL(x) (((x) & 0xf) << 4) ++#define PLL_CCTRL(x) ((x) & 0xf) ++ ++/* SGMII v2 PHY registers per lane */ ++#define EMAC_SGMII_PHY_LN_OFFSET 0x0400 ++ ++/* SGMII v2 digital lane registers */ ++#define EMAC_SGMII_LN_DRVR_CTRL0 0x00C ++#define EMAC_SGMII_LN_DRVR_TAP_EN 0x018 ++#define EMAC_SGMII_LN_TX_MARGINING 0x01C ++#define EMAC_SGMII_LN_TX_PRE 0x020 ++#define EMAC_SGMII_LN_TX_POST 0x024 ++#define EMAC_SGMII_LN_TX_BAND_MODE 0x060 ++#define EMAC_SGMII_LN_LANE_MODE 0x064 ++#define EMAC_SGMII_LN_PARALLEL_RATE 0x078 ++#define EMAC_SGMII_LN_CML_CTRL_MODE0 0x0B8 ++#define EMAC_SGMII_LN_MIXER_CTRL_MODE0 0x0D0 ++#define EMAC_SGMII_LN_VGA_INITVAL 0x134 ++#define EMAC_SGMII_LN_UCDR_FO_GAIN_MODE0 0x17C ++#define EMAC_SGMII_LN_UCDR_SO_GAIN_MODE0 0x188 ++#define EMAC_SGMII_LN_UCDR_SO_CONFIG 0x194 ++#define EMAC_SGMII_LN_RX_BAND 0x19C ++#define EMAC_SGMII_LN_RX_RCVR_PATH1_MODE0 0x1B8 ++#define EMAC_SGMII_LN_RSM_CONFIG 0x1F0 ++#define EMAC_SGMII_LN_SIGDET_ENABLES 0x224 ++#define EMAC_SGMII_LN_SIGDET_CNTRL 0x228 ++#define EMAC_SGMII_LN_SIGDET_DEGLITCH_CNTRL 0x22C ++#define EMAC_SGMII_LN_RX_EN_SIGNAL 0x2A0 ++#define EMAC_SGMII_LN_RX_MISC_CNTRL0 0x2AC ++#define EMAC_SGMII_LN_DRVR_LOGIC_CLKDIV 0x2BC ++ ++/* SGMII v2 digital lane register values */ ++#define UCDR_STEP_BY_TWO_MODE0 BIT(7) ++#define UCDR_xO_GAIN_MODE(x) ((x) & 0x7f) ++#define UCDR_ENABLE BIT(6) ++#define UCDR_SO_SATURATION(x) ((x) & 0x3f) ++#define SIGDET_LP_BYP_PS4 BIT(7) ++#define SIGDET_EN_PS0_TO_PS2 BIT(6) ++#define EN_ACCOUPLEVCM_SW_MUX BIT(5) ++#define EN_ACCOUPLEVCM_SW BIT(4) ++#define RX_SYNC_EN BIT(3) ++#define RXTERM_HIGHZ_PS5 BIT(2) ++#define SIGDET_EN_PS3 BIT(1) ++#define EN_ACCOUPLE_VCM_PS3 BIT(0) ++#define UFS_MODE BIT(5) ++#define TXVAL_VALID_INIT BIT(4) ++#define TXVAL_VALID_MUX BIT(3) ++#define TXVAL_VALID BIT(2) ++#define USB3P1_MODE BIT(1) ++#define KR_PCIGEN3_MODE BIT(0) ++#define PRE_EN BIT(3) ++#define POST_EN BIT(2) ++#define MAIN_EN_MUX BIT(1) ++#define MAIN_EN BIT(0) ++#define TX_MARGINING_MUX BIT(6) ++#define TX_MARGINING(x) ((x) & 0x3f) ++#define TX_PRE_MUX BIT(6) ++#define TX_PRE(x) ((x) & 0x3f) ++#define TX_POST_MUX BIT(6) ++#define TX_POST(x) ((x) & 0x3f) ++#define CML_GEAR_MODE(x) (((x) & 7) << 3) ++#define CML2CMOS_IBOOST_MODE(x) ((x) & 7) ++#define MIXER_LOADB_MODE(x) (((x) & 0xf) << 2) ++#define MIXER_DATARATE_MODE(x) ((x) & 3) ++#define VGA_THRESH_DFE(x) ((x) & 0x3f) ++#define SIGDET_LP_BYP_PS0_TO_PS2 BIT(5) ++#define SIGDET_LP_BYP_MUX BIT(4) ++#define SIGDET_LP_BYP BIT(3) ++#define SIGDET_EN_MUX BIT(2) ++#define SIGDET_EN BIT(1) ++#define SIGDET_FLT_BYP BIT(0) ++#define SIGDET_LVL(x) (((x) & 0xf) << 4) ++#define SIGDET_BW_CTRL(x) ((x) & 0xf) ++#define SIGDET_DEGLITCH_CTRL(x) (((x) & 0xf) << 1) ++#define SIGDET_DEGLITCH_BYP BIT(0) ++#define INVERT_PCS_RX_CLK BIT(7) ++#define PWM_EN BIT(6) ++#define RXBIAS_SEL(x) (((x) & 0x3) << 4) ++#define EBDAC_SIGN BIT(3) ++#define EDAC_SIGN BIT(2) ++#define EN_AUXTAP1SIGN_INVERT BIT(1) ++#define EN_DAC_CHOPPING BIT(0) ++#define DRVR_LOGIC_CLK_EN BIT(4) ++#define DRVR_LOGIC_CLK_DIV(x) ((x) & 0xf) ++#define PARALLEL_RATE_MODE2(x) (((x) & 0x3) << 4) ++#define PARALLEL_RATE_MODE1(x) (((x) & 0x3) << 2) ++#define PARALLEL_RATE_MODE0(x) ((x) & 0x3) ++#define BAND_MODE2(x) (((x) & 0x3) << 4) ++#define BAND_MODE1(x) (((x) & 0x3) << 2) ++#define BAND_MODE0(x) ((x) & 0x3) ++#define LANE_SYNC_MODE BIT(5) ++#define LANE_MODE(x) ((x) & 0x1f) ++#define CDR_PD_SEL_MODE0(x) (((x) & 0x3) << 5) ++#define EN_DLL_MODE0 BIT(4) ++#define EN_IQ_DCC_MODE0 BIT(3) ++#define EN_IQCAL_MODE0 BIT(2) ++#define EN_QPATH_MODE0 BIT(1) ++#define EN_EPATH_MODE0 BIT(0) ++#define FORCE_TSYNC_ACK BIT(7) ++#define FORCE_CMN_ACK BIT(6) ++#define FORCE_CMN_READY BIT(5) ++#define EN_RCLK_DEGLITCH BIT(4) ++#define BYPASS_RSM_CDR_RESET BIT(3) ++#define BYPASS_RSM_TSYNC BIT(2) ++#define BYPASS_RSM_SAMP_CAL BIT(1) ++#define BYPASS_RSM_DLL_CAL BIT(0) ++ ++/* EMAC_QSERDES_COM_SYS_CLK_CTRL */ ++#define SYSCLK_CM BIT(4) ++#define SYSCLK_AC_COUPLE BIT(3) ++ ++/* EMAC_QSERDES_COM_PLL_CNTRL */ ++#define OCP_EN BIT(5) ++#define PLL_DIV_FFEN BIT(2) ++#define PLL_DIV_ORD BIT(1) ++ ++/* EMAC_QSERDES_COM_SYSCLK_EN_SEL */ ++#define SYSCLK_SEL_CMOS BIT(3) ++ ++/* EMAC_QSERDES_COM_RESETSM_CNTRL */ ++#define FRQ_TUNE_MODE BIT(4) ++ ++/* EMAC_QSERDES_COM_PLLLOCK_CMP_EN */ ++#define PLLLOCK_CMP_EN BIT(0) ++ ++/* EMAC_QSERDES_COM_DEC_START1 */ ++#define DEC_START1_MUX BIT(7) ++#define DEC_START1(x) ((x) & 0x7f) ++ ++/* EMAC_QSERDES_COM_DIV_FRAC_START1 * EMAC_QSERDES_COM_DIV_FRAC_START2 */ ++#define DIV_FRAC_START_MUX BIT(7) ++#define DIV_FRAC_START(x) ((x) & 0x7f) ++ ++/* EMAC_QSERDES_COM_DIV_FRAC_START3 */ ++#define DIV_FRAC_START3_MUX BIT(4) ++#define DIV_FRAC_START3(x) ((x) & 0xf) ++ ++/* EMAC_QSERDES_COM_DEC_START2 */ ++#define DEC_START2_MUX BIT(1) ++#define DEC_START2 BIT(0) ++ ++/* EMAC_QSERDES_COM_RESET_SM */ ++#define READY BIT(5) ++ ++/* EMAC_QSERDES_TX_TX_EMP_POST1_LVL */ ++#define TX_EMP_POST1_LVL_MUX BIT(5) ++#define TX_EMP_POST1_LVL(x) ((x) & 0x1f) ++#define TX_EMP_POST1_LVL_BMSK 0x1f ++#define TX_EMP_POST1_LVL_SHFT 0 ++ ++/* EMAC_QSERDES_TX_TX_DRV_LVL */ ++#define TX_DRV_LVL_MUX BIT(4) ++#define TX_DRV_LVL(x) ((x) & 0xf) ++ ++/* EMAC_QSERDES_TX_TRAN_DRVR_EMP_EN */ ++#define EMP_EN_MUX BIT(1) ++#define EMP_EN BIT(0) ++ ++/* EMAC_QSERDES_RX_CDR_CONTROL & EMAC_QSERDES_RX_CDR_CONTROL2 */ ++#define HBW_PD_EN BIT(7) ++#define SECONDORDERENABLE BIT(6) ++#define FIRSTORDER_THRESH(x) (((x) & 0x7) << 3) ++#define SECONDORDERGAIN(x) ((x) & 0x7) ++ ++/* EMAC_QSERDES_RX_RX_EQ_GAIN12 */ ++#define RX_EQ_GAIN2(x) (((x) & 0xf) << 4) ++#define RX_EQ_GAIN1(x) ((x) & 0xf) ++ ++/* EMAC_SGMII_PHY_SERDES_START */ ++#define SERDES_START BIT(0) ++ ++/* EMAC_SGMII_PHY_CMN_PWR_CTRL */ ++#define BIAS_EN BIT(6) ++#define PLL_EN BIT(5) ++#define SYSCLK_EN BIT(4) ++#define CLKBUF_L_EN BIT(3) ++#define PLL_TXCLK_EN BIT(1) ++#define PLL_RXCLK_EN BIT(0) ++ ++/* EMAC_SGMII_PHY_RX_PWR_CTRL */ ++#define L0_RX_SIGDET_EN BIT(7) ++#define L0_RX_TERM_MODE(x) (((x) & 3) << 4) ++#define L0_RX_I_EN BIT(1) ++ ++/* EMAC_SGMII_PHY_TX_PWR_CTRL */ ++#define L0_TX_EN BIT(5) ++#define L0_CLKBUF_EN BIT(4) ++#define L0_TRAN_BIAS_EN BIT(1) ++ ++/* EMAC_SGMII_PHY_LANE_CTRL1 */ ++#define L0_RX_EQUALIZE_ENABLE BIT(6) ++#define L0_RESET_TSYNC_EN BIT(4) ++#define L0_DRV_LVL(x) ((x) & 0xf) ++ ++/* EMAC_SGMII_PHY_AUTONEG_CFG2 */ ++#define FORCE_AN_TX_CFG BIT(5) ++#define FORCE_AN_RX_CFG BIT(4) ++#define AN_ENABLE BIT(0) ++ ++/* EMAC_SGMII_PHY_SPEED_CFG1 */ ++#define DUPLEX_MODE BIT(4) ++#define SPDMODE_1000 BIT(1) ++#define SPDMODE_100 BIT(0) ++#define SPDMODE_10 0 ++#define SPDMODE_BMSK 3 ++#define SPDMODE_SHFT 0 ++ ++/* EMAC_SGMII_PHY_POW_DWN_CTRL0 */ ++#define PWRDN_B BIT(0) ++#define CDR_MAX_CNT(x) ((x) & 0xff) ++ ++/* EMAC_QSERDES_TX_BIST_MODE_LANENO */ ++#define BIST_LANE_NUMBER(x) (((x) & 3) << 5) ++#define BISTMODE(x) ((x) & 0x1f) ++ ++/* EMAC_QSERDES_COM_PLLLOCK_CMPx */ ++#define PLLLOCK_CMP(x) ((x) & 0xff) ++ ++/* EMAC_SGMII_PHY_RESET_CTRL */ ++#define PHY_SW_RESET BIT(0) ++ ++/* EMAC_SGMII_PHY_IRQ_CMD */ ++#define IRQ_GLOBAL_CLEAR BIT(0) ++ ++/* EMAC_SGMII_PHY_INTERRUPT_MASK */ ++#define DECODE_CODE_ERR BIT(7) ++#define DECODE_DISP_ERR BIT(6) ++#define PLL_UNLOCK BIT(5) ++#define AN_ILLEGAL_TERM BIT(4) ++#define SYNC_FAIL BIT(3) ++#define AN_START BIT(2) ++#define AN_END BIT(1) ++#define AN_REQUEST BIT(0) ++ ++#define SGMII_PHY_IRQ_CLR_WAIT_TIME 10 ++ ++#define SGMII_PHY_INTERRUPT_ERR (\ ++ DECODE_CODE_ERR |\ ++ DECODE_DISP_ERR) ++ ++#define SGMII_ISR_AN_MASK (\ ++ AN_REQUEST |\ ++ AN_START |\ ++ AN_END |\ ++ AN_ILLEGAL_TERM |\ ++ PLL_UNLOCK |\ ++ SYNC_FAIL) ++ ++#define SGMII_ISR_MASK (\ ++ SGMII_PHY_INTERRUPT_ERR |\ ++ SGMII_ISR_AN_MASK) ++ ++/* SGMII TX_CONFIG */ ++#define TXCFG_LINK 0x8000 ++#define TXCFG_MODE_BMSK 0x1c00 ++#define TXCFG_1000_FULL 0x1800 ++#define TXCFG_100_FULL 0x1400 ++#define TXCFG_100_HALF 0x0400 ++#define TXCFG_10_FULL 0x1000 ++#define TXCFG_10_HALF 0x0000 ++ ++#define SERDES_START_WAIT_TIMES 100 ++ ++struct emac_reg_write { ++ unsigned int offset; ++ u32 val; ++}; ++ ++static void emac_reg_write_all(void __iomem *base, ++ const struct emac_reg_write *itr, size_t size) ++{ ++ size_t i; ++ ++ for (i = 0; i < size; ++itr, ++i) ++ writel(itr->val, base + itr->offset); ++} ++ ++static const struct emac_reg_write physical_coding_sublayer_programming_v1[] = { ++ {EMAC_SGMII_PHY_CDR_CTRL0, CDR_MAX_CNT(15)}, ++ {EMAC_SGMII_PHY_POW_DWN_CTRL0, PWRDN_B}, ++ {EMAC_SGMII_PHY_CMN_PWR_CTRL, ++ BIAS_EN | SYSCLK_EN | CLKBUF_L_EN | PLL_TXCLK_EN | PLL_RXCLK_EN}, ++ {EMAC_SGMII_PHY_TX_PWR_CTRL, L0_TX_EN | L0_CLKBUF_EN | L0_TRAN_BIAS_EN}, ++ {EMAC_SGMII_PHY_RX_PWR_CTRL, ++ L0_RX_SIGDET_EN | L0_RX_TERM_MODE(1) | L0_RX_I_EN}, ++ {EMAC_SGMII_PHY_CMN_PWR_CTRL, ++ BIAS_EN | PLL_EN | SYSCLK_EN | CLKBUF_L_EN | PLL_TXCLK_EN | ++ PLL_RXCLK_EN}, ++ {EMAC_SGMII_PHY_LANE_CTRL1, ++ L0_RX_EQUALIZE_ENABLE | L0_RESET_TSYNC_EN | L0_DRV_LVL(15)}, ++}; ++ ++static const struct emac_reg_write sysclk_refclk_setting[] = { ++ {EMAC_QSERDES_COM_SYSCLK_EN_SEL, SYSCLK_SEL_CMOS}, ++ {EMAC_QSERDES_COM_SYS_CLK_CTRL, SYSCLK_CM | SYSCLK_AC_COUPLE}, ++}; ++ ++static const struct emac_reg_write pll_setting[] = { ++ {EMAC_QSERDES_COM_PLL_IP_SETI, PLL_IPSETI(1)}, ++ {EMAC_QSERDES_COM_PLL_CP_SETI, PLL_CPSETI(59)}, ++ {EMAC_QSERDES_COM_PLL_IP_SETP, PLL_IPSETP(10)}, ++ {EMAC_QSERDES_COM_PLL_CP_SETP, PLL_CPSETP(9)}, ++ {EMAC_QSERDES_COM_PLL_CRCTRL, PLL_RCTRL(15) | PLL_CCTRL(11)}, ++ {EMAC_QSERDES_COM_PLL_CNTRL, OCP_EN | PLL_DIV_FFEN | PLL_DIV_ORD}, ++ {EMAC_QSERDES_COM_DEC_START1, DEC_START1_MUX | DEC_START1(2)}, ++ {EMAC_QSERDES_COM_DEC_START2, DEC_START2_MUX | DEC_START2}, ++ {EMAC_QSERDES_COM_DIV_FRAC_START1, ++ DIV_FRAC_START_MUX | DIV_FRAC_START(85)}, ++ {EMAC_QSERDES_COM_DIV_FRAC_START2, ++ DIV_FRAC_START_MUX | DIV_FRAC_START(42)}, ++ {EMAC_QSERDES_COM_DIV_FRAC_START3, ++ DIV_FRAC_START3_MUX | DIV_FRAC_START3(3)}, ++ {EMAC_QSERDES_COM_PLLLOCK_CMP1, PLLLOCK_CMP(43)}, ++ {EMAC_QSERDES_COM_PLLLOCK_CMP2, PLLLOCK_CMP(104)}, ++ {EMAC_QSERDES_COM_PLLLOCK_CMP3, PLLLOCK_CMP(0)}, ++ {EMAC_QSERDES_COM_PLLLOCK_CMP_EN, PLLLOCK_CMP_EN}, ++ {EMAC_QSERDES_COM_RESETSM_CNTRL, FRQ_TUNE_MODE}, ++}; ++ ++static const struct emac_reg_write cdr_setting[] = { ++ {EMAC_QSERDES_RX_CDR_CONTROL, ++ SECONDORDERENABLE | FIRSTORDER_THRESH(3) | SECONDORDERGAIN(2)}, ++ {EMAC_QSERDES_RX_CDR_CONTROL2, ++ SECONDORDERENABLE | FIRSTORDER_THRESH(3) | SECONDORDERGAIN(4)}, ++}; ++ ++static const struct emac_reg_write tx_rx_setting[] = { ++ {EMAC_QSERDES_TX_BIST_MODE_LANENO, 0}, ++ {EMAC_QSERDES_TX_TX_DRV_LVL, TX_DRV_LVL_MUX | TX_DRV_LVL(15)}, ++ {EMAC_QSERDES_TX_TRAN_DRVR_EMP_EN, EMP_EN_MUX | EMP_EN}, ++ {EMAC_QSERDES_TX_TX_EMP_POST1_LVL, ++ TX_EMP_POST1_LVL_MUX | TX_EMP_POST1_LVL(1)}, ++ {EMAC_QSERDES_RX_RX_EQ_GAIN12, RX_EQ_GAIN2(15) | RX_EQ_GAIN1(15)}, ++ {EMAC_QSERDES_TX_LANE_MODE, LANE_MODE(8)}, ++}; ++ ++static const struct emac_reg_write sgmii_v2_laned[] = { ++ /* CDR Settings */ ++ {EMAC_SGMII_LN_UCDR_FO_GAIN_MODE0, ++ UCDR_STEP_BY_TWO_MODE0 | UCDR_xO_GAIN_MODE(10)}, ++ {EMAC_SGMII_LN_UCDR_SO_GAIN_MODE0, UCDR_xO_GAIN_MODE(6)}, ++ {EMAC_SGMII_LN_UCDR_SO_CONFIG, UCDR_ENABLE | UCDR_SO_SATURATION(12)}, ++ ++ /* TX/RX Settings */ ++ {EMAC_SGMII_LN_RX_EN_SIGNAL, SIGDET_LP_BYP_PS4 | SIGDET_EN_PS0_TO_PS2}, ++ ++ {EMAC_SGMII_LN_DRVR_CTRL0, TXVAL_VALID_INIT | KR_PCIGEN3_MODE}, ++ {EMAC_SGMII_LN_DRVR_TAP_EN, MAIN_EN}, ++ {EMAC_SGMII_LN_TX_MARGINING, TX_MARGINING_MUX | TX_MARGINING(25)}, ++ {EMAC_SGMII_LN_TX_PRE, TX_PRE_MUX}, ++ {EMAC_SGMII_LN_TX_POST, TX_POST_MUX}, ++ ++ {EMAC_SGMII_LN_CML_CTRL_MODE0, ++ CML_GEAR_MODE(1) | CML2CMOS_IBOOST_MODE(1)}, ++ {EMAC_SGMII_LN_MIXER_CTRL_MODE0, ++ MIXER_LOADB_MODE(12) | MIXER_DATARATE_MODE(1)}, ++ {EMAC_SGMII_LN_VGA_INITVAL, VGA_THRESH_DFE(31)}, ++ {EMAC_SGMII_LN_SIGDET_ENABLES, ++ SIGDET_LP_BYP_PS0_TO_PS2 | SIGDET_FLT_BYP}, ++ {EMAC_SGMII_LN_SIGDET_CNTRL, SIGDET_LVL(8)}, ++ ++ {EMAC_SGMII_LN_SIGDET_DEGLITCH_CNTRL, SIGDET_DEGLITCH_CTRL(4)}, ++ {EMAC_SGMII_LN_RX_MISC_CNTRL0, 0}, ++ {EMAC_SGMII_LN_DRVR_LOGIC_CLKDIV, ++ DRVR_LOGIC_CLK_EN | DRVR_LOGIC_CLK_DIV(4)}, ++ ++ {EMAC_SGMII_LN_PARALLEL_RATE, PARALLEL_RATE_MODE0(1)}, ++ {EMAC_SGMII_LN_TX_BAND_MODE, BAND_MODE0(2)}, ++ {EMAC_SGMII_LN_RX_BAND, BAND_MODE0(3)}, ++ {EMAC_SGMII_LN_LANE_MODE, LANE_MODE(26)}, ++ {EMAC_SGMII_LN_RX_RCVR_PATH1_MODE0, CDR_PD_SEL_MODE0(3)}, ++ {EMAC_SGMII_LN_RSM_CONFIG, BYPASS_RSM_SAMP_CAL | BYPASS_RSM_DLL_CAL}, ++}; ++ ++static const struct emac_reg_write physical_coding_sublayer_programming_v2[] = { ++ {EMAC_SGMII_PHY_POW_DWN_CTRL0, PWRDN_B}, ++ {EMAC_SGMII_PHY_CDR_CTRL0, CDR_MAX_CNT(15)}, ++ {EMAC_SGMII_PHY_TX_PWR_CTRL, 0}, ++ {EMAC_SGMII_PHY_LANE_CTRL1, L0_RX_EQUALIZE_ENABLE}, ++}; ++ ++static int emac_sgmii_link_init(struct emac_adapter *adpt) ++{ ++ struct phy_device *phydev = adpt->phydev; ++ struct emac_phy *phy = &adpt->phy; ++ u32 val; ++ ++ val = readl(phy->base + EMAC_SGMII_PHY_AUTONEG_CFG2); ++ ++ if (phydev->autoneg == AUTONEG_ENABLE) { ++ val &= ~(FORCE_AN_RX_CFG | FORCE_AN_TX_CFG); ++ val |= AN_ENABLE; ++ writel(val, phy->base + EMAC_SGMII_PHY_AUTONEG_CFG2); ++ } else { ++ u32 speed_cfg; ++ ++ switch (phydev->speed) { ++ case SPEED_10: ++ speed_cfg = SPDMODE_10; ++ break; ++ case SPEED_100: ++ speed_cfg = SPDMODE_100; ++ break; ++ case SPEED_1000: ++ speed_cfg = SPDMODE_1000; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ if (phydev->duplex == DUPLEX_FULL) ++ speed_cfg |= DUPLEX_MODE; ++ ++ val &= ~AN_ENABLE; ++ writel(speed_cfg, phy->base + EMAC_SGMII_PHY_SPEED_CFG1); ++ writel(val, phy->base + EMAC_SGMII_PHY_AUTONEG_CFG2); ++ } ++ ++ return 0; ++} ++ ++static int emac_sgmii_irq_clear(struct emac_adapter *adpt, u32 irq_bits) ++{ ++ struct emac_phy *phy = &adpt->phy; ++ u32 status; ++ ++ writel_relaxed(irq_bits, phy->base + EMAC_SGMII_PHY_INTERRUPT_CLEAR); ++ writel_relaxed(IRQ_GLOBAL_CLEAR, phy->base + EMAC_SGMII_PHY_IRQ_CMD); ++ /* Ensure interrupt clear command is written to HW */ ++ wmb(); ++ ++ /* After set the IRQ_GLOBAL_CLEAR bit, the status clearing must ++ * be confirmed before clearing the bits in other registers. ++ * It takes a few cycles for hw to clear the interrupt status. ++ */ ++ if (readl_poll_timeout_atomic(phy->base + ++ EMAC_SGMII_PHY_INTERRUPT_STATUS, ++ status, !(status & irq_bits), 1, ++ SGMII_PHY_IRQ_CLR_WAIT_TIME)) { ++ netdev_err(adpt->netdev, ++ "error: failed clear SGMII irq: status:0x%x bits:0x%x\n", ++ status, irq_bits); ++ return -EIO; ++ } ++ ++ /* Finalize clearing procedure */ ++ writel_relaxed(0, phy->base + EMAC_SGMII_PHY_IRQ_CMD); ++ writel_relaxed(0, phy->base + EMAC_SGMII_PHY_INTERRUPT_CLEAR); ++ ++ /* Ensure that clearing procedure finalization is written to HW */ ++ wmb(); ++ ++ return 0; ++} ++ ++int emac_sgmii_init_v1(struct emac_adapter *adpt) ++{ ++ struct emac_phy *phy = &adpt->phy; ++ unsigned int i; ++ int ret; ++ ++ ret = emac_sgmii_link_init(adpt); ++ if (ret) ++ return ret; ++ ++ emac_reg_write_all(phy->base, physical_coding_sublayer_programming_v1, ++ ARRAY_SIZE(physical_coding_sublayer_programming_v1)); ++ emac_reg_write_all(phy->base, sysclk_refclk_setting, ++ ARRAY_SIZE(sysclk_refclk_setting)); ++ emac_reg_write_all(phy->base, pll_setting, ARRAY_SIZE(pll_setting)); ++ emac_reg_write_all(phy->base, cdr_setting, ARRAY_SIZE(cdr_setting)); ++ emac_reg_write_all(phy->base, tx_rx_setting, ++ ARRAY_SIZE(tx_rx_setting)); ++ ++ /* Power up the Ser/Des engine */ ++ writel(SERDES_START, phy->base + EMAC_SGMII_PHY_SERDES_START); ++ ++ for (i = 0; i < SERDES_START_WAIT_TIMES; i++) { ++ if (readl(phy->base + EMAC_QSERDES_COM_RESET_SM) & READY) ++ break; ++ usleep_range(100, 200); ++ } ++ ++ if (i == SERDES_START_WAIT_TIMES) { ++ netdev_err(adpt->netdev, "error: ser/des failed to start\n"); ++ return -EIO; ++ } ++ /* Mask out all the SGMII Interrupt */ ++ writel(0, phy->base + EMAC_SGMII_PHY_INTERRUPT_MASK); ++ ++ emac_sgmii_irq_clear(adpt, SGMII_PHY_INTERRUPT_ERR); ++ ++ return 0; ++} ++ ++int emac_sgmii_init_v2(struct emac_adapter *adpt) ++{ ++ struct emac_phy *phy = &adpt->phy; ++ void __iomem *phy_regs = phy->base; ++ void __iomem *laned = phy->digital; ++ unsigned int i; ++ u32 lnstatus; ++ int ret; ++ ++ ret = emac_sgmii_link_init(adpt); ++ if (ret) ++ return ret; ++ ++ /* PCS lane-x init */ ++ emac_reg_write_all(phy->base, physical_coding_sublayer_programming_v2, ++ ARRAY_SIZE(physical_coding_sublayer_programming_v2)); ++ ++ /* SGMII lane-x init */ ++ emac_reg_write_all(phy->digital, ++ sgmii_v2_laned, ARRAY_SIZE(sgmii_v2_laned)); ++ ++ /* Power up PCS and start reset lane state machine */ ++ ++ writel(0, phy_regs + EMAC_SGMII_PHY_RESET_CTRL); ++ writel(1, laned + SGMII_LN_RSM_START); ++ ++ /* Wait for c_ready assertion */ ++ for (i = 0; i < SERDES_START_WAIT_TIMES; i++) { ++ lnstatus = readl(phy_regs + SGMII_PHY_LN_LANE_STATUS); ++ if (lnstatus & BIT(1)) ++ break; ++ usleep_range(100, 200); ++ } ++ ++ if (i == SERDES_START_WAIT_TIMES) { ++ netdev_err(adpt->netdev, "SGMII failed to start\n"); ++ return -EIO; ++ } ++ ++ /* Disable digital and SERDES loopback */ ++ writel(0, phy_regs + SGMII_PHY_LN_BIST_GEN0); ++ writel(0, phy_regs + SGMII_PHY_LN_BIST_GEN2); ++ writel(0, phy_regs + SGMII_PHY_LN_CDR_CTRL1); ++ ++ /* Mask out all the SGMII Interrupt */ ++ writel(0, phy_regs + EMAC_SGMII_PHY_INTERRUPT_MASK); ++ ++ emac_sgmii_irq_clear(adpt, SGMII_PHY_INTERRUPT_ERR); ++ ++ return 0; ++} ++ ++static void emac_sgmii_reset_prepare(struct emac_adapter *adpt) ++{ ++ struct emac_phy *phy = &adpt->phy; ++ u32 val; ++ ++ /* Reset PHY */ ++ val = readl(phy->base + EMAC_EMAC_WRAPPER_CSR2); ++ writel(((val & ~PHY_RESET) | PHY_RESET), phy->base + ++ EMAC_EMAC_WRAPPER_CSR2); ++ /* Ensure phy-reset command is written to HW before the release cmd */ ++ msleep(50); ++ val = readl(phy->base + EMAC_EMAC_WRAPPER_CSR2); ++ writel((val & ~PHY_RESET), phy->base + EMAC_EMAC_WRAPPER_CSR2); ++ /* Ensure phy-reset release command is written to HW before initializing ++ * SGMII ++ */ ++ msleep(50); ++} ++ ++void emac_sgmii_reset(struct emac_adapter *adpt) ++{ ++ int ret; ++ ++ clk_set_rate(adpt->clk[EMAC_CLK_HIGH_SPEED], 19200000); ++ emac_sgmii_reset_prepare(adpt); ++ ++ ret = adpt->phy.initialize(adpt); ++ if (ret) ++ netdev_err(adpt->netdev, ++ "could not reinitialize internal PHY (error=%i)\n", ++ ret); ++ ++ clk_set_rate(adpt->clk[EMAC_CLK_HIGH_SPEED], 125000000); ++} ++ ++static const struct of_device_id emac_sgmii_dt_match[] = { ++ { ++ .compatible = "qcom,fsm9900-emac-sgmii", ++ .data = emac_sgmii_init_v1, ++ }, ++ { ++ .compatible = "qcom,qdf2432-emac-sgmii", ++ .data = emac_sgmii_init_v2, ++ }, ++ {} ++}; ++ ++int emac_sgmii_config(struct platform_device *pdev, struct emac_adapter *adpt) ++{ ++ struct platform_device *sgmii_pdev = NULL; ++ struct emac_phy *phy = &adpt->phy; ++ struct resource *res; ++ const struct of_device_id *match; ++ struct device_node *np; ++ ++ np = of_parse_phandle(pdev->dev.of_node, "internal-phy", 0); ++ if (!np) { ++ dev_err(&pdev->dev, "missing internal-phy property\n"); ++ return -ENODEV; ++ } ++ ++ sgmii_pdev = of_find_device_by_node(np); ++ if (!sgmii_pdev) { ++ dev_err(&pdev->dev, "invalid internal-phy property\n"); ++ return -ENODEV; ++ } ++ ++ match = of_match_device(emac_sgmii_dt_match, &sgmii_pdev->dev); ++ if (!match) { ++ dev_err(&pdev->dev, "unrecognized internal phy node\n"); ++ return -ENODEV; ++ } ++ ++ phy->initialize = (emac_sgmii_initialize)match->data; ++ ++ /* Base address is the first address */ ++ res = platform_get_resource(sgmii_pdev, IORESOURCE_MEM, 0); ++ phy->base = devm_ioremap_resource(&sgmii_pdev->dev, res); ++ if (IS_ERR(phy->base)) ++ return PTR_ERR(phy->base); ++ ++ /* v2 SGMII has a per-lane digital digital, so parse it if it exists */ ++ res = platform_get_resource(sgmii_pdev, IORESOURCE_MEM, 1); ++ if (res) { ++ phy->digital = devm_ioremap_resource(&sgmii_pdev->dev, res); ++ if (IS_ERR(phy->base)) ++ return PTR_ERR(phy->base); ++ ++ } ++ ++ return phy->initialize(adpt); ++} +diff --git a/drivers/net/ethernet/qualcomm/emac/emac-sgmii.h b/drivers/net/ethernet/qualcomm/emac/emac-sgmii.h +new file mode 100644 +index 0000000..ce79212 +--- /dev/null ++++ b/drivers/net/ethernet/qualcomm/emac/emac-sgmii.h +@@ -0,0 +1,24 @@ ++/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that 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. ++ */ ++ ++#ifndef _EMAC_SGMII_H_ ++#define _EMAC_SGMII_H_ ++ ++struct emac_adapter; ++struct platform_device; ++ ++int emac_sgmii_init_v1(struct emac_adapter *adpt); ++int emac_sgmii_init_v2(struct emac_adapter *adpt); ++int emac_sgmii_config(struct platform_device *pdev, struct emac_adapter *adpt); ++void emac_sgmii_reset(struct emac_adapter *adpt); ++ ++#endif +diff --git a/drivers/net/ethernet/qualcomm/emac/emac.c b/drivers/net/ethernet/qualcomm/emac/emac.c +new file mode 100644 +index 0000000..56e0a9f +--- /dev/null ++++ b/drivers/net/ethernet/qualcomm/emac/emac.c +@@ -0,0 +1,743 @@ ++/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that 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. ++ */ ++ ++/* Qualcomm Technologies, Inc. EMAC Gigabit Ethernet Driver */ ++ ++#include <linux/if_ether.h> ++#include <linux/if_vlan.h> ++#include <linux/interrupt.h> ++#include <linux/io.h> ++#include <linux/module.h> ++#include <linux/of.h> ++#include <linux/of_net.h> ++#include <linux/of_device.h> ++#include <linux/phy.h> ++#include <linux/platform_device.h> ++#include "emac.h" ++#include "emac-mac.h" ++#include "emac-phy.h" ++#include "emac-sgmii.h" ++ ++#define EMAC_MSG_DEFAULT (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK | \ ++ NETIF_MSG_TIMER | NETIF_MSG_IFDOWN | NETIF_MSG_IFUP) ++ ++#define EMAC_RRD_SIZE 4 ++/* The RRD size if timestamping is enabled: */ ++#define EMAC_TS_RRD_SIZE 6 ++#define EMAC_TPD_SIZE 4 ++#define EMAC_RFD_SIZE 2 ++ ++#define REG_MAC_RX_STATUS_BIN EMAC_RXMAC_STATC_REG0 ++#define REG_MAC_RX_STATUS_END EMAC_RXMAC_STATC_REG22 ++#define REG_MAC_TX_STATUS_BIN EMAC_TXMAC_STATC_REG0 ++#define REG_MAC_TX_STATUS_END EMAC_TXMAC_STATC_REG24 ++ ++#define RXQ0_NUM_RFD_PREF_DEF 8 ++#define TXQ0_NUM_TPD_PREF_DEF 5 ++ ++#define EMAC_PREAMBLE_DEF 7 ++ ++#define DMAR_DLY_CNT_DEF 15 ++#define DMAW_DLY_CNT_DEF 4 ++ ++#define IMR_NORMAL_MASK (\ ++ ISR_ERROR |\ ++ ISR_GPHY_LINK |\ ++ ISR_TX_PKT |\ ++ GPHY_WAKEUP_INT) ++ ++#define IMR_EXTENDED_MASK (\ ++ SW_MAN_INT |\ ++ ISR_OVER |\ ++ ISR_ERROR |\ ++ ISR_GPHY_LINK |\ ++ ISR_TX_PKT |\ ++ GPHY_WAKEUP_INT) ++ ++#define ISR_TX_PKT (\ ++ TX_PKT_INT |\ ++ TX_PKT_INT1 |\ ++ TX_PKT_INT2 |\ ++ TX_PKT_INT3) ++ ++#define ISR_GPHY_LINK (\ ++ GPHY_LINK_UP_INT |\ ++ GPHY_LINK_DOWN_INT) ++ ++#define ISR_OVER (\ ++ RFD0_UR_INT |\ ++ RFD1_UR_INT |\ ++ RFD2_UR_INT |\ ++ RFD3_UR_INT |\ ++ RFD4_UR_INT |\ ++ RXF_OF_INT |\ ++ TXF_UR_INT) ++ ++#define ISR_ERROR (\ ++ DMAR_TO_INT |\ ++ DMAW_TO_INT |\ ++ TXQ_TO_INT) ++ ++/* in sync with enum emac_clk_id */ ++static const char * const emac_clk_name[] = { ++ "axi_clk", "cfg_ahb_clk", "high_speed_clk", "mdio_clk", "tx_clk", ++ "rx_clk", "sys_clk" ++}; ++ ++void emac_reg_update32(void __iomem *addr, u32 mask, u32 val) ++{ ++ u32 data = readl(addr); ++ ++ writel(((data & ~mask) | val), addr); ++} ++ ++/* reinitialize */ ++int emac_reinit_locked(struct emac_adapter *adpt) ++{ ++ int ret; ++ ++ mutex_lock(&adpt->reset_lock); ++ ++ emac_mac_down(adpt); ++ emac_sgmii_reset(adpt); ++ ret = emac_mac_up(adpt); ++ ++ mutex_unlock(&adpt->reset_lock); ++ ++ return ret; ++} ++ ++/* NAPI */ ++static int emac_napi_rtx(struct napi_struct *napi, int budget) ++{ ++ struct emac_rx_queue *rx_q = ++ container_of(napi, struct emac_rx_queue, napi); ++ struct emac_adapter *adpt = netdev_priv(rx_q->netdev); ++ struct emac_irq *irq = rx_q->irq; ++ int work_done = 0; ++ ++ emac_mac_rx_process(adpt, rx_q, &work_done, budget); ++ ++ if (work_done < budget) { ++ napi_complete(napi); ++ ++ irq->mask |= rx_q->intr; ++ writel(irq->mask, adpt->base + EMAC_INT_MASK); ++ } ++ ++ return work_done; ++} ++ ++/* Transmit the packet */ ++static int emac_start_xmit(struct sk_buff *skb, struct net_device *netdev) ++{ ++ struct emac_adapter *adpt = netdev_priv(netdev); ++ ++ return emac_mac_tx_buf_send(adpt, &adpt->tx_q, skb); ++} ++ ++irqreturn_t emac_isr(int _irq, void *data) ++{ ++ struct emac_irq *irq = data; ++ struct emac_adapter *adpt = ++ container_of(irq, struct emac_adapter, irq); ++ struct emac_rx_queue *rx_q = &adpt->rx_q; ++ u32 isr, status; ++ ++ /* disable the interrupt */ ++ writel(0, adpt->base + EMAC_INT_MASK); ++ ++ isr = readl_relaxed(adpt->base + EMAC_INT_STATUS); ++ ++ status = isr & irq->mask; ++ if (status == 0) ++ goto exit; ++ ++ if (status & ISR_ERROR) { ++ netif_warn(adpt, intr, adpt->netdev, ++ "warning: error irq status 0x%lx\n", ++ status & ISR_ERROR); ++ /* reset MAC */ ++ schedule_work(&adpt->work_thread); ++ } ++ ++ /* Schedule the napi for receive queue with interrupt ++ * status bit set ++ */ ++ if (status & rx_q->intr) { ++ if (napi_schedule_prep(&rx_q->napi)) { ++ irq->mask &= ~rx_q->intr; ++ __napi_schedule(&rx_q->napi); ++ } ++ } ++ ++ if (status & TX_PKT_INT) ++ emac_mac_tx_process(adpt, &adpt->tx_q); ++ ++ if (status & ISR_OVER) ++ net_warn_ratelimited("warning: TX/RX overflow\n"); ++ ++ /* link event */ ++ if (status & ISR_GPHY_LINK) ++ phy_mac_interrupt(adpt->phydev, !!(status & GPHY_LINK_UP_INT)); ++ ++exit: ++ /* enable the interrupt */ ++ writel(irq->mask, adpt->base + EMAC_INT_MASK); ++ ++ return IRQ_HANDLED; ++} ++ ++/* Configure VLAN tag strip/insert feature */ ++static int emac_set_features(struct net_device *netdev, ++ netdev_features_t features) ++{ ++ netdev_features_t changed = features ^ netdev->features; ++ struct emac_adapter *adpt = netdev_priv(netdev); ++ ++ /* We only need to reprogram the hardware if the VLAN tag features ++ * have changed, and if it's already running. ++ */ ++ if (!(changed & (NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX))) ++ return 0; ++ ++ if (!netif_running(netdev)) ++ return 0; ++ ++ /* emac_mac_mode_config() uses netdev->features to configure the EMAC, ++ * so make sure it's set first. ++ */ ++ netdev->features = features; ++ ++ return emac_reinit_locked(adpt); ++} ++ ++/* Configure Multicast and Promiscuous modes */ ++static void emac_rx_mode_set(struct net_device *netdev) ++{ ++ struct emac_adapter *adpt = netdev_priv(netdev); ++ struct netdev_hw_addr *ha; ++ ++ emac_mac_mode_config(adpt); ++ ++ /* update multicast address filtering */ ++ emac_mac_multicast_addr_clear(adpt); ++ netdev_for_each_mc_addr(ha, netdev) ++ emac_mac_multicast_addr_set(adpt, ha->addr); ++} ++ ++/* Change the Maximum Transfer Unit (MTU) */ ++static int emac_change_mtu(struct net_device *netdev, int new_mtu) ++{ ++ unsigned int max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN; ++ struct emac_adapter *adpt = netdev_priv(netdev); ++ ++ if ((max_frame < EMAC_MIN_ETH_FRAME_SIZE) || ++ (max_frame > EMAC_MAX_ETH_FRAME_SIZE)) { ++ netdev_err(adpt->netdev, "error: invalid MTU setting\n"); ++ return -EINVAL; ++ } ++ ++ netif_info(adpt, hw, adpt->netdev, ++ "changing MTU from %d to %d\n", netdev->mtu, ++ new_mtu); ++ netdev->mtu = new_mtu; ++ ++ if (netif_running(netdev)) ++ return emac_reinit_locked(adpt); ++ ++ return 0; ++} ++ ++/* Called when the network interface is made active */ ++static int emac_open(struct net_device *netdev) ++{ ++ struct emac_adapter *adpt = netdev_priv(netdev); ++ int ret; ++ ++ /* allocate rx/tx dma buffer & descriptors */ ++ ret = emac_mac_rx_tx_rings_alloc_all(adpt); ++ if (ret) { ++ netdev_err(adpt->netdev, "error allocating rx/tx rings\n"); ++ return ret; ++ } ++ ++ ret = emac_mac_up(adpt); ++ if (ret) { ++ emac_mac_rx_tx_rings_free_all(adpt); ++ return ret; ++ } ++ ++ emac_mac_start(adpt); ++ ++ return 0; ++} ++ ++/* Called when the network interface is disabled */ ++static int emac_close(struct net_device *netdev) ++{ ++ struct emac_adapter *adpt = netdev_priv(netdev); ++ ++ mutex_lock(&adpt->reset_lock); ++ ++ emac_mac_down(adpt); ++ emac_mac_rx_tx_rings_free_all(adpt); ++ ++ mutex_unlock(&adpt->reset_lock); ++ ++ return 0; ++} ++ ++/* Respond to a TX hang */ ++static void emac_tx_timeout(struct net_device *netdev) ++{ ++ struct emac_adapter *adpt = netdev_priv(netdev); ++ ++ schedule_work(&adpt->work_thread); ++} ++ ++/* IOCTL support for the interface */ ++static int emac_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) ++{ ++ if (!netif_running(netdev)) ++ return -EINVAL; ++ ++ if (!netdev->phydev) ++ return -ENODEV; ++ ++ return phy_mii_ioctl(netdev->phydev, ifr, cmd); ++} ++ ++/* Provide network statistics info for the interface */ ++static struct rtnl_link_stats64 *emac_get_stats64(struct net_device *netdev, ++ struct rtnl_link_stats64 *net_stats) ++{ ++ struct emac_adapter *adpt = netdev_priv(netdev); ++ unsigned int addr = REG_MAC_RX_STATUS_BIN; ++ struct emac_stats *stats = &adpt->stats; ++ u64 *stats_itr = &adpt->stats.rx_ok; ++ u32 val; ++ ++ spin_lock(&stats->lock); ++ ++ while (addr <= REG_MAC_RX_STATUS_END) { ++ val = readl_relaxed(adpt->base + addr); ++ *stats_itr += val; ++ stats_itr++; ++ addr += sizeof(u32); ++ } ++ ++ /* additional rx status */ ++ val = readl_relaxed(adpt->base + EMAC_RXMAC_STATC_REG23); ++ adpt->stats.rx_crc_align += val; ++ val = readl_relaxed(adpt->base + EMAC_RXMAC_STATC_REG24); ++ adpt->stats.rx_jabbers += val; ++ ++ /* update tx status */ ++ addr = REG_MAC_TX_STATUS_BIN; ++ stats_itr = &adpt->stats.tx_ok; ++ ++ while (addr <= REG_MAC_TX_STATUS_END) { ++ val = readl_relaxed(adpt->base + addr); ++ *stats_itr += val; ++ ++stats_itr; ++ addr += sizeof(u32); ++ } ++ ++ /* additional tx status */ ++ val = readl_relaxed(adpt->base + EMAC_TXMAC_STATC_REG25); ++ adpt->stats.tx_col += val; ++ ++ /* return parsed statistics */ ++ net_stats->rx_packets = stats->rx_ok; ++ net_stats->tx_packets = stats->tx_ok; ++ net_stats->rx_bytes = stats->rx_byte_cnt; ++ net_stats->tx_bytes = stats->tx_byte_cnt; ++ net_stats->multicast = stats->rx_mcast; ++ net_stats->collisions = stats->tx_1_col + stats->tx_2_col * 2 + ++ stats->tx_late_col + stats->tx_abort_col; ++ ++ net_stats->rx_errors = stats->rx_frag + stats->rx_fcs_err + ++ stats->rx_len_err + stats->rx_sz_ov + ++ stats->rx_align_err; ++ net_stats->rx_fifo_errors = stats->rx_rxf_ov; ++ net_stats->rx_length_errors = stats->rx_len_err; ++ net_stats->rx_crc_errors = stats->rx_fcs_err; ++ net_stats->rx_frame_errors = stats->rx_align_err; ++ net_stats->rx_over_errors = stats->rx_rxf_ov; ++ net_stats->rx_missed_errors = stats->rx_rxf_ov; ++ ++ net_stats->tx_errors = stats->tx_late_col + stats->tx_abort_col + ++ stats->tx_underrun + stats->tx_trunc; ++ net_stats->tx_fifo_errors = stats->tx_underrun; ++ net_stats->tx_aborted_errors = stats->tx_abort_col; ++ net_stats->tx_window_errors = stats->tx_late_col; ++ ++ spin_unlock(&stats->lock); ++ ++ return net_stats; ++} ++ ++static const struct net_device_ops emac_netdev_ops = { ++ .ndo_open = emac_open, ++ .ndo_stop = emac_close, ++ .ndo_validate_addr = eth_validate_addr, ++ .ndo_start_xmit = emac_start_xmit, ++ .ndo_set_mac_address = eth_mac_addr, ++ .ndo_change_mtu = emac_change_mtu, ++ .ndo_do_ioctl = emac_ioctl, ++ .ndo_tx_timeout = emac_tx_timeout, ++ .ndo_get_stats64 = emac_get_stats64, ++ .ndo_set_features = emac_set_features, ++ .ndo_set_rx_mode = emac_rx_mode_set, ++}; ++ ++/* Watchdog task routine, called to reinitialize the EMAC */ ++static void emac_work_thread(struct work_struct *work) ++{ ++ struct emac_adapter *adpt = ++ container_of(work, struct emac_adapter, work_thread); ++ ++ emac_reinit_locked(adpt); ++} ++ ++/* Initialize various data structures */ ++static void emac_init_adapter(struct emac_adapter *adpt) ++{ ++ u32 reg; ++ ++ /* descriptors */ ++ adpt->tx_desc_cnt = EMAC_DEF_TX_DESCS; ++ adpt->rx_desc_cnt = EMAC_DEF_RX_DESCS; ++ ++ /* dma */ ++ adpt->dma_order = emac_dma_ord_out; ++ adpt->dmar_block = emac_dma_req_4096; ++ adpt->dmaw_block = emac_dma_req_128; ++ adpt->dmar_dly_cnt = DMAR_DLY_CNT_DEF; ++ adpt->dmaw_dly_cnt = DMAW_DLY_CNT_DEF; ++ adpt->tpd_burst = TXQ0_NUM_TPD_PREF_DEF; ++ adpt->rfd_burst = RXQ0_NUM_RFD_PREF_DEF; ++ ++ /* irq moderator */ ++ reg = ((EMAC_DEF_RX_IRQ_MOD >> 1) << IRQ_MODERATOR2_INIT_SHFT) | ++ ((EMAC_DEF_TX_IRQ_MOD >> 1) << IRQ_MODERATOR_INIT_SHFT); ++ adpt->irq_mod = reg; ++ ++ /* others */ ++ adpt->preamble = EMAC_PREAMBLE_DEF; ++} ++ ++/* Get the clock */ ++static int emac_clks_get(struct platform_device *pdev, ++ struct emac_adapter *adpt) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < EMAC_CLK_CNT; i++) { ++ struct clk *clk = devm_clk_get(&pdev->dev, emac_clk_name[i]); ++ ++ if (IS_ERR(clk)) { ++ dev_err(&pdev->dev, ++ "could not claim clock %s (error=%li)\n", ++ emac_clk_name[i], PTR_ERR(clk)); ++ ++ return PTR_ERR(clk); ++ } ++ ++ adpt->clk[i] = clk; ++ } ++ ++ return 0; ++} ++ ++/* Initialize clocks */ ++static int emac_clks_phase1_init(struct platform_device *pdev, ++ struct emac_adapter *adpt) ++{ ++ int ret; ++ ++ ret = emac_clks_get(pdev, adpt); ++ if (ret) ++ return ret; ++ ++ ret = clk_prepare_enable(adpt->clk[EMAC_CLK_AXI]); ++ if (ret) ++ return ret; ++ ++ ret = clk_prepare_enable(adpt->clk[EMAC_CLK_CFG_AHB]); ++ if (ret) ++ return ret; ++ ++ ret = clk_set_rate(adpt->clk[EMAC_CLK_HIGH_SPEED], 19200000); ++ if (ret) ++ return ret; ++ ++ return clk_prepare_enable(adpt->clk[EMAC_CLK_HIGH_SPEED]); ++} ++ ++/* Enable clocks; needs emac_clks_phase1_init to be called before */ ++static int emac_clks_phase2_init(struct platform_device *pdev, ++ struct emac_adapter *adpt) ++{ ++ int ret; ++ ++ ret = clk_set_rate(adpt->clk[EMAC_CLK_TX], 125000000); ++ if (ret) ++ return ret; ++ ++ ret = clk_prepare_enable(adpt->clk[EMAC_CLK_TX]); ++ if (ret) ++ return ret; ++ ++ ret = clk_set_rate(adpt->clk[EMAC_CLK_HIGH_SPEED], 125000000); ++ if (ret) ++ return ret; ++ ++ ret = clk_set_rate(adpt->clk[EMAC_CLK_MDIO], 25000000); ++ if (ret) ++ return ret; ++ ++ ret = clk_prepare_enable(adpt->clk[EMAC_CLK_MDIO]); ++ if (ret) ++ return ret; ++ ++ ret = clk_prepare_enable(adpt->clk[EMAC_CLK_RX]); ++ if (ret) ++ return ret; ++ ++ return clk_prepare_enable(adpt->clk[EMAC_CLK_SYS]); ++} ++ ++static void emac_clks_teardown(struct emac_adapter *adpt) ++{ ++ ++ unsigned int i; ++ ++ for (i = 0; i < EMAC_CLK_CNT; i++) ++ clk_disable_unprepare(adpt->clk[i]); ++} ++ ++/* Get the resources */ ++static int emac_probe_resources(struct platform_device *pdev, ++ struct emac_adapter *adpt) ++{ ++ struct device_node *node = pdev->dev.of_node; ++ struct net_device *netdev = adpt->netdev; ++ struct resource *res; ++ const void *maddr; ++ int ret = 0; ++ ++ /* get mac address */ ++ maddr = of_get_mac_address(node); ++ if (!maddr) ++ eth_hw_addr_random(netdev); ++ else ++ ether_addr_copy(netdev->dev_addr, maddr); ++ ++ /* Core 0 interrupt */ ++ ret = platform_get_irq(pdev, 0); ++ if (ret < 0) { ++ dev_err(&pdev->dev, ++ "error: missing core0 irq resource (error=%i)\n", ret); ++ return ret; ++ } ++ adpt->irq.irq = ret; ++ ++ /* base register address */ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ adpt->base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(adpt->base)) ++ return PTR_ERR(adpt->base); ++ ++ /* CSR register address */ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 1); ++ adpt->csr = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(adpt->csr)) ++ return PTR_ERR(adpt->csr); ++ ++ netdev->base_addr = (unsigned long)adpt->base; ++ ++ return 0; ++} ++ ++static const struct of_device_id emac_dt_match[] = { ++ { ++ .compatible = "qcom,fsm9900-emac", ++ }, ++ {} ++}; ++ ++static int emac_probe(struct platform_device *pdev) ++{ ++ struct net_device *netdev; ++ struct emac_adapter *adpt; ++ struct emac_phy *phy; ++ u16 devid, revid; ++ u32 reg; ++ int ret; ++ ++ /* The EMAC itself is capable of 64-bit DMA, so try that first. */ ++ ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); ++ if (ret) { ++ /* Some platforms may restrict the EMAC's address bus to less ++ * then the size of DDR. In this case, we need to try a ++ * smaller mask. We could try every possible smaller mask, ++ * but that's overkill. Instead, just fall to 32-bit, which ++ * should always work. ++ */ ++ ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); ++ if (ret) { ++ dev_err(&pdev->dev, "could not set DMA mask\n"); ++ return ret; ++ } ++ } ++ ++ netdev = alloc_etherdev(sizeof(struct emac_adapter)); ++ if (!netdev) ++ return -ENOMEM; ++ ++ dev_set_drvdata(&pdev->dev, netdev); ++ SET_NETDEV_DEV(netdev, &pdev->dev); ++ ++ adpt = netdev_priv(netdev); ++ adpt->netdev = netdev; ++ adpt->msg_enable = EMAC_MSG_DEFAULT; ++ ++ phy = &adpt->phy; ++ ++ mutex_init(&adpt->reset_lock); ++ spin_lock_init(&adpt->stats.lock); ++ ++ adpt->irq.mask = RX_PKT_INT0 | IMR_NORMAL_MASK; ++ ++ ret = emac_probe_resources(pdev, adpt); ++ if (ret) ++ goto err_undo_netdev; ++ ++ /* initialize clocks */ ++ ret = emac_clks_phase1_init(pdev, adpt); ++ if (ret) { ++ dev_err(&pdev->dev, "could not initialize clocks\n"); ++ goto err_undo_netdev; ++ } ++ ++ netdev->watchdog_timeo = EMAC_WATCHDOG_TIME; ++ netdev->irq = adpt->irq.irq; ++ ++ adpt->rrd_size = EMAC_RRD_SIZE; ++ adpt->tpd_size = EMAC_TPD_SIZE; ++ adpt->rfd_size = EMAC_RFD_SIZE; ++ ++ netdev->netdev_ops = &emac_netdev_ops; ++ ++ emac_init_adapter(adpt); ++ ++ /* init external phy */ ++ ret = emac_phy_config(pdev, adpt); ++ if (ret) ++ goto err_undo_clocks; ++ ++ /* init internal sgmii phy */ ++ ret = emac_sgmii_config(pdev, adpt); ++ if (ret) ++ goto err_undo_mdiobus; ++ ++ /* enable clocks */ ++ ret = emac_clks_phase2_init(pdev, adpt); ++ if (ret) { ++ dev_err(&pdev->dev, "could not initialize clocks\n"); ++ goto err_undo_mdiobus; ++ } ++ ++ emac_mac_reset(adpt); ++ ++ /* set hw features */ ++ netdev->features = NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_RXCSUM | ++ NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_HW_VLAN_CTAG_RX | ++ NETIF_F_HW_VLAN_CTAG_TX; ++ netdev->hw_features = netdev->features; ++ ++ netdev->vlan_features |= NETIF_F_SG | NETIF_F_HW_CSUM | ++ NETIF_F_TSO | NETIF_F_TSO6; ++ ++ INIT_WORK(&adpt->work_thread, emac_work_thread); ++ ++ /* Initialize queues */ ++ emac_mac_rx_tx_ring_init_all(pdev, adpt); ++ ++ netif_napi_add(netdev, &adpt->rx_q.napi, emac_napi_rtx, ++ NAPI_POLL_WEIGHT); ++ ++ ret = register_netdev(netdev); ++ if (ret) { ++ dev_err(&pdev->dev, "could not register net device\n"); ++ goto err_undo_napi; ++ } ++ ++ reg = readl_relaxed(adpt->base + EMAC_DMA_MAS_CTRL); ++ devid = (reg & DEV_ID_NUM_BMSK) >> DEV_ID_NUM_SHFT; ++ revid = (reg & DEV_REV_NUM_BMSK) >> DEV_REV_NUM_SHFT; ++ reg = readl_relaxed(adpt->base + EMAC_CORE_HW_VERSION); ++ ++ netif_info(adpt, probe, netdev, ++ "hardware id %d.%d, hardware version %d.%d.%d\n", ++ devid, revid, ++ (reg & MAJOR_BMSK) >> MAJOR_SHFT, ++ (reg & MINOR_BMSK) >> MINOR_SHFT, ++ (reg & STEP_BMSK) >> STEP_SHFT); ++ ++ return 0; ++ ++err_undo_napi: ++ netif_napi_del(&adpt->rx_q.napi); ++err_undo_mdiobus: ++ mdiobus_unregister(adpt->mii_bus); ++err_undo_clocks: ++ emac_clks_teardown(adpt); ++err_undo_netdev: ++ free_netdev(netdev); ++ ++ return ret; ++} ++ ++static int emac_remove(struct platform_device *pdev) ++{ ++ struct net_device *netdev = dev_get_drvdata(&pdev->dev); ++ struct emac_adapter *adpt = netdev_priv(netdev); ++ ++ unregister_netdev(netdev); ++ netif_napi_del(&adpt->rx_q.napi); ++ ++ emac_clks_teardown(adpt); ++ ++ mdiobus_unregister(adpt->mii_bus); ++ free_netdev(netdev); ++ dev_set_drvdata(&pdev->dev, NULL); ++ ++ return 0; ++} ++ ++static struct platform_driver emac_platform_driver = { ++ .probe = emac_probe, ++ .remove = emac_remove, ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "qcom-emac", ++ .of_match_table = emac_dt_match, ++ }, ++}; ++ ++module_platform_driver(emac_platform_driver); ++ ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:qcom-emac"); +diff --git a/drivers/net/ethernet/qualcomm/emac/emac.h b/drivers/net/ethernet/qualcomm/emac/emac.h +new file mode 100644 +index 0000000..0c76e6c +--- /dev/null ++++ b/drivers/net/ethernet/qualcomm/emac/emac.h +@@ -0,0 +1,335 @@ ++/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that 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. ++ */ ++ ++#ifndef _EMAC_H_ ++#define _EMAC_H_ ++ ++#include <linux/irqreturn.h> ++#include <linux/netdevice.h> ++#include <linux/clk.h> ++#include <linux/platform_device.h> ++#include "emac-mac.h" ++#include "emac-phy.h" ++ ++/* EMAC base register offsets */ ++#define EMAC_DMA_MAS_CTRL 0x001400 ++#define EMAC_IRQ_MOD_TIM_INIT 0x001408 ++#define EMAC_BLK_IDLE_STS 0x00140c ++#define EMAC_PHY_LINK_DELAY 0x00141c ++#define EMAC_SYS_ALIV_CTRL 0x001434 ++#define EMAC_MAC_IPGIFG_CTRL 0x001484 ++#define EMAC_MAC_STA_ADDR0 0x001488 ++#define EMAC_MAC_STA_ADDR1 0x00148c ++#define EMAC_HASH_TAB_REG0 0x001490 ++#define EMAC_HASH_TAB_REG1 0x001494 ++#define EMAC_MAC_HALF_DPLX_CTRL 0x001498 ++#define EMAC_MAX_FRAM_LEN_CTRL 0x00149c ++#define EMAC_INT_STATUS 0x001600 ++#define EMAC_INT_MASK 0x001604 ++#define EMAC_RXMAC_STATC_REG0 0x001700 ++#define EMAC_RXMAC_STATC_REG22 0x001758 ++#define EMAC_TXMAC_STATC_REG0 0x001760 ++#define EMAC_TXMAC_STATC_REG24 0x0017c0 ++#define EMAC_CORE_HW_VERSION 0x001974 ++#define EMAC_IDT_TABLE0 0x001b00 ++#define EMAC_RXMAC_STATC_REG23 0x001bc8 ++#define EMAC_RXMAC_STATC_REG24 0x001bcc ++#define EMAC_TXMAC_STATC_REG25 0x001bd0 ++#define EMAC_INT1_MASK 0x001bf0 ++#define EMAC_INT1_STATUS 0x001bf4 ++#define EMAC_INT2_MASK 0x001bf8 ++#define EMAC_INT2_STATUS 0x001bfc ++#define EMAC_INT3_MASK 0x001c00 ++#define EMAC_INT3_STATUS 0x001c04 ++ ++/* EMAC_DMA_MAS_CTRL */ ++#define DEV_ID_NUM_BMSK 0x7f000000 ++#define DEV_ID_NUM_SHFT 24 ++#define DEV_REV_NUM_BMSK 0xff0000 ++#define DEV_REV_NUM_SHFT 16 ++#define INT_RD_CLR_EN 0x4000 ++#define IRQ_MODERATOR2_EN 0x800 ++#define IRQ_MODERATOR_EN 0x400 ++#define LPW_CLK_SEL 0x80 ++#define LPW_STATE 0x20 ++#define LPW_MODE 0x10 ++#define SOFT_RST 0x1 ++ ++/* EMAC_IRQ_MOD_TIM_INIT */ ++#define IRQ_MODERATOR2_INIT_BMSK 0xffff0000 ++#define IRQ_MODERATOR2_INIT_SHFT 16 ++#define IRQ_MODERATOR_INIT_BMSK 0xffff ++#define IRQ_MODERATOR_INIT_SHFT 0 ++ ++/* EMAC_INT_STATUS */ ++#define DIS_INT BIT(31) ++#define PTP_INT BIT(30) ++#define RFD4_UR_INT BIT(29) ++#define TX_PKT_INT3 BIT(26) ++#define TX_PKT_INT2 BIT(25) ++#define TX_PKT_INT1 BIT(24) ++#define RX_PKT_INT3 BIT(19) ++#define RX_PKT_INT2 BIT(18) ++#define RX_PKT_INT1 BIT(17) ++#define RX_PKT_INT0 BIT(16) ++#define TX_PKT_INT BIT(15) ++#define TXQ_TO_INT BIT(14) ++#define GPHY_WAKEUP_INT BIT(13) ++#define GPHY_LINK_DOWN_INT BIT(12) ++#define GPHY_LINK_UP_INT BIT(11) ++#define DMAW_TO_INT BIT(10) ++#define DMAR_TO_INT BIT(9) ++#define TXF_UR_INT BIT(8) ++#define RFD3_UR_INT BIT(7) ++#define RFD2_UR_INT BIT(6) ++#define RFD1_UR_INT BIT(5) ++#define RFD0_UR_INT BIT(4) ++#define RXF_OF_INT BIT(3) ++#define SW_MAN_INT BIT(2) ++ ++/* EMAC_MAILBOX_6 */ ++#define RFD2_PROC_IDX_BMSK 0xfff0000 ++#define RFD2_PROC_IDX_SHFT 16 ++#define RFD2_PROD_IDX_BMSK 0xfff ++#define RFD2_PROD_IDX_SHFT 0 ++ ++/* EMAC_CORE_HW_VERSION */ ++#define MAJOR_BMSK 0xf0000000 ++#define MAJOR_SHFT 28 ++#define MINOR_BMSK 0xfff0000 ++#define MINOR_SHFT 16 ++#define STEP_BMSK 0xffff ++#define STEP_SHFT 0 ++ ++/* EMAC_EMAC_WRAPPER_CSR1 */ ++#define TX_INDX_FIFO_SYNC_RST BIT(23) ++#define TX_TS_FIFO_SYNC_RST BIT(22) ++#define RX_TS_FIFO2_SYNC_RST BIT(21) ++#define RX_TS_FIFO1_SYNC_RST BIT(20) ++#define TX_TS_ENABLE BIT(16) ++#define DIS_1588_CLKS BIT(11) ++#define FREQ_MODE BIT(9) ++#define ENABLE_RRD_TIMESTAMP BIT(3) ++ ++/* EMAC_EMAC_WRAPPER_CSR2 */ ++#define HDRIVE_BMSK 0x3000 ++#define HDRIVE_SHFT 12 ++#define SLB_EN BIT(9) ++#define PLB_EN BIT(8) ++#define WOL_EN BIT(3) ++#define PHY_RESET BIT(0) ++ ++#define EMAC_DEV_ID 0x0040 ++ ++/* SGMII v2 per lane registers */ ++#define SGMII_LN_RSM_START 0x029C ++ ++/* SGMII v2 PHY common registers */ ++#define SGMII_PHY_CMN_CTRL 0x0408 ++#define SGMII_PHY_CMN_RESET_CTRL 0x0410 ++ ++/* SGMII v2 PHY registers per lane */ ++#define SGMII_PHY_LN_OFFSET 0x0400 ++#define SGMII_PHY_LN_LANE_STATUS 0x00DC ++#define SGMII_PHY_LN_BIST_GEN0 0x008C ++#define SGMII_PHY_LN_BIST_GEN1 0x0090 ++#define SGMII_PHY_LN_BIST_GEN2 0x0094 ++#define SGMII_PHY_LN_BIST_GEN3 0x0098 ++#define SGMII_PHY_LN_CDR_CTRL1 0x005C ++ ++enum emac_clk_id { ++ EMAC_CLK_AXI, ++ EMAC_CLK_CFG_AHB, ++ EMAC_CLK_HIGH_SPEED, ++ EMAC_CLK_MDIO, ++ EMAC_CLK_TX, ++ EMAC_CLK_RX, ++ EMAC_CLK_SYS, ++ EMAC_CLK_CNT ++}; ++ ++#define EMAC_LINK_SPEED_UNKNOWN 0x0 ++#define EMAC_LINK_SPEED_10_HALF BIT(0) ++#define EMAC_LINK_SPEED_10_FULL BIT(1) ++#define EMAC_LINK_SPEED_100_HALF BIT(2) ++#define EMAC_LINK_SPEED_100_FULL BIT(3) ++#define EMAC_LINK_SPEED_1GB_FULL BIT(5) ++ ++#define EMAC_MAX_SETUP_LNK_CYCLE 100 ++ ++/* Wake On Lan */ ++#define EMAC_WOL_PHY 0x00000001 /* PHY Status Change */ ++#define EMAC_WOL_MAGIC 0x00000002 /* Magic Packet */ ++ ++struct emac_stats { ++ /* rx */ ++ u64 rx_ok; /* good packets */ ++ u64 rx_bcast; /* good broadcast packets */ ++ u64 rx_mcast; /* good multicast packets */ ++ u64 rx_pause; /* pause packet */ ++ u64 rx_ctrl; /* control packets other than pause frame. */ ++ u64 rx_fcs_err; /* packets with bad FCS. */ ++ u64 rx_len_err; /* packets with length mismatch */ ++ u64 rx_byte_cnt; /* good bytes count (without FCS) */ ++ u64 rx_runt; /* runt packets */ ++ u64 rx_frag; /* fragment count */ ++ u64 rx_sz_64; /* packets that are 64 bytes */ ++ u64 rx_sz_65_127; /* packets that are 65-127 bytes */ ++ u64 rx_sz_128_255; /* packets that are 128-255 bytes */ ++ u64 rx_sz_256_511; /* packets that are 256-511 bytes */ ++ u64 rx_sz_512_1023; /* packets that are 512-1023 bytes */ ++ u64 rx_sz_1024_1518; /* packets that are 1024-1518 bytes */ ++ u64 rx_sz_1519_max; /* packets that are 1519-MTU bytes*/ ++ u64 rx_sz_ov; /* packets that are >MTU bytes (truncated) */ ++ u64 rx_rxf_ov; /* packets dropped due to RX FIFO overflow */ ++ u64 rx_align_err; /* alignment errors */ ++ u64 rx_bcast_byte_cnt; /* broadcast packets byte count (without FCS) */ ++ u64 rx_mcast_byte_cnt; /* multicast packets byte count (without FCS) */ ++ u64 rx_err_addr; /* packets dropped due to address filtering */ ++ u64 rx_crc_align; /* CRC align errors */ ++ u64 rx_jabbers; /* jabbers */ ++ ++ /* tx */ ++ u64 tx_ok; /* good packets */ ++ u64 tx_bcast; /* good broadcast packets */ ++ u64 tx_mcast; /* good multicast packets */ ++ u64 tx_pause; /* pause packets */ ++ u64 tx_exc_defer; /* packets with excessive deferral */ ++ u64 tx_ctrl; /* control packets other than pause frame */ ++ u64 tx_defer; /* packets that are deferred. */ ++ u64 tx_byte_cnt; /* good bytes count (without FCS) */ ++ u64 tx_sz_64; /* packets that are 64 bytes */ ++ u64 tx_sz_65_127; /* packets that are 65-127 bytes */ ++ u64 tx_sz_128_255; /* packets that are 128-255 bytes */ ++ u64 tx_sz_256_511; /* packets that are 256-511 bytes */ ++ u64 tx_sz_512_1023; /* packets that are 512-1023 bytes */ ++ u64 tx_sz_1024_1518; /* packets that are 1024-1518 bytes */ ++ u64 tx_sz_1519_max; /* packets that are 1519-MTU bytes */ ++ u64 tx_1_col; /* packets single prior collision */ ++ u64 tx_2_col; /* packets with multiple prior collisions */ ++ u64 tx_late_col; /* packets with late collisions */ ++ u64 tx_abort_col; /* packets aborted due to excess collisions */ ++ u64 tx_underrun; /* packets aborted due to FIFO underrun */ ++ u64 tx_rd_eop; /* count of reads beyond EOP */ ++ u64 tx_len_err; /* packets with length mismatch */ ++ u64 tx_trunc; /* packets truncated due to size >MTU */ ++ u64 tx_bcast_byte; /* broadcast packets byte count (without FCS) */ ++ u64 tx_mcast_byte; /* multicast packets byte count (without FCS) */ ++ u64 tx_col; /* collisions */ ++ ++ spinlock_t lock; /* prevent multiple simultaneous readers */ ++}; ++ ++/* RSS hstype Definitions */ ++#define EMAC_RSS_HSTYP_IPV4_EN 0x00000001 ++#define EMAC_RSS_HSTYP_TCP4_EN 0x00000002 ++#define EMAC_RSS_HSTYP_IPV6_EN 0x00000004 ++#define EMAC_RSS_HSTYP_TCP6_EN 0x00000008 ++#define EMAC_RSS_HSTYP_ALL_EN (\ ++ EMAC_RSS_HSTYP_IPV4_EN |\ ++ EMAC_RSS_HSTYP_TCP4_EN |\ ++ EMAC_RSS_HSTYP_IPV6_EN |\ ++ EMAC_RSS_HSTYP_TCP6_EN) ++ ++#define EMAC_VLAN_TO_TAG(_vlan, _tag) \ ++ (_tag = ((((_vlan) >> 8) & 0xFF) | (((_vlan) & 0xFF) << 8))) ++ ++#define EMAC_TAG_TO_VLAN(_tag, _vlan) \ ++ (_vlan = ((((_tag) >> 8) & 0xFF) | (((_tag) & 0xFF) << 8))) ++ ++#define EMAC_DEF_RX_BUF_SIZE 1536 ++#define EMAC_MAX_JUMBO_PKT_SIZE (9 * 1024) ++#define EMAC_MAX_TX_OFFLOAD_THRESH (9 * 1024) ++ ++#define EMAC_MAX_ETH_FRAME_SIZE EMAC_MAX_JUMBO_PKT_SIZE ++#define EMAC_MIN_ETH_FRAME_SIZE 68 ++ ++#define EMAC_DEF_TX_QUEUES 1 ++#define EMAC_DEF_RX_QUEUES 1 ++ ++#define EMAC_MIN_TX_DESCS 128 ++#define EMAC_MIN_RX_DESCS 128 ++ ++#define EMAC_MAX_TX_DESCS 16383 ++#define EMAC_MAX_RX_DESCS 2047 ++ ++#define EMAC_DEF_TX_DESCS 512 ++#define EMAC_DEF_RX_DESCS 256 ++ ++#define EMAC_DEF_RX_IRQ_MOD 250 ++#define EMAC_DEF_TX_IRQ_MOD 250 ++ ++#define EMAC_WATCHDOG_TIME (5 * HZ) ++ ++/* by default check link every 4 seconds */ ++#define EMAC_TRY_LINK_TIMEOUT (4 * HZ) ++ ++/* emac_irq per-device (per-adapter) irq properties. ++ * @irq: irq number. ++ * @mask mask to use over status register. ++ */ ++struct emac_irq { ++ unsigned int irq; ++ u32 mask; ++}; ++ ++/* The device's main data structure */ ++struct emac_adapter { ++ struct net_device *netdev; ++ struct mii_bus *mii_bus; ++ struct phy_device *phydev; ++ ++ void __iomem *base; ++ void __iomem *csr; ++ ++ struct emac_phy phy; ++ struct emac_stats stats; ++ ++ struct emac_irq irq; ++ struct clk *clk[EMAC_CLK_CNT]; ++ ++ /* All Descriptor memory */ ++ struct emac_ring_header ring_header; ++ struct emac_tx_queue tx_q; ++ struct emac_rx_queue rx_q; ++ unsigned int tx_desc_cnt; ++ unsigned int rx_desc_cnt; ++ unsigned int rrd_size; /* in quad words */ ++ unsigned int rfd_size; /* in quad words */ ++ unsigned int tpd_size; /* in quad words */ ++ ++ unsigned int rxbuf_size; ++ ++ /* Ring parameter */ ++ u8 tpd_burst; ++ u8 rfd_burst; ++ unsigned int dmaw_dly_cnt; ++ unsigned int dmar_dly_cnt; ++ enum emac_dma_req_block dmar_block; ++ enum emac_dma_req_block dmaw_block; ++ enum emac_dma_order dma_order; ++ ++ u32 irq_mod; ++ u32 preamble; ++ ++ struct work_struct work_thread; ++ ++ u16 msg_enable; ++ ++ struct mutex reset_lock; ++}; ++ ++int emac_reinit_locked(struct emac_adapter *adpt); ++void emac_reg_update32(void __iomem *addr, u32 mask, u32 val); ++irqreturn_t emac_isr(int irq, void *data); ++ ++#endif /* _EMAC_H_ */ +-- +Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm +Technologies, Inc. Qualcomm Technologies, Inc. is a member of the Code Aurora +Forum, a Linux Foundation Collaborative Project. + + +From 3f10d9127d616e07403dfee21b218fa065aa247a Mon Sep 17 00:00:00 2001 +From: Wei Yongjun <weiyongjun1 at huawei.com> +Date: Thu, 15 Sep 2016 02:25:52 +0000 +Subject: [PATCH 02/17] net: emac: remove unnecessary dev_set_drvdata() + +The driver core clears the driver data to NULL after device_release +or on probe failure. Thus, it is not needed to manually clear the +device driver data to NULL. + +Signed-off-by: Wei Yongjun <weiyongjun1 at huawei.com> +Signed-off-by: David S. Miller <davem at davemloft.net> +(cherry picked from commit 7e5eded5c189abaea77556da41af1195af841b0a) +--- + drivers/net/ethernet/qualcomm/emac/emac.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/drivers/net/ethernet/qualcomm/emac/emac.c b/drivers/net/ethernet/qualcomm/emac/emac.c +index 56e0a9f..42d2d23 100644 +--- a/drivers/net/ethernet/qualcomm/emac/emac.c ++++ b/drivers/net/ethernet/qualcomm/emac/emac.c +@@ -722,7 +722,6 @@ static int emac_remove(struct platform_device *pdev) + + mdiobus_unregister(adpt->mii_bus); + free_netdev(netdev); +- dev_set_drvdata(&pdev->dev, NULL); + + return 0; + } +-- +Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm +Technologies, Inc. Qualcomm Technologies, Inc. is a member of the Code Aurora +Forum, a Linux Foundation Collaborative Project. + + +From 47f8484bebe3724f24e20d13428029c4fba60dbb Mon Sep 17 00:00:00 2001 +From: Wei Yongjun <weiyongjun1 at huawei.com> +Date: Thu, 15 Sep 2016 02:26:10 +0000 +Subject: [PATCH 03/17] net: emac: remove .owner field for driver + +Remove .owner field if calls are used which set it automatically. + +Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci + +Signed-off-by: Wei Yongjun <weiyongjun1 at huawei.com> +Signed-off-by: David S. Miller <davem at davemloft.net> +(cherry picked from commit 1d7b47a3c78e0b5391a18246f9637752a4565e5b) +--- + drivers/net/ethernet/qualcomm/emac/emac.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/drivers/net/ethernet/qualcomm/emac/emac.c b/drivers/net/ethernet/qualcomm/emac/emac.c +index 42d2d23..e47d387 100644 +--- a/drivers/net/ethernet/qualcomm/emac/emac.c ++++ b/drivers/net/ethernet/qualcomm/emac/emac.c +@@ -730,7 +730,6 @@ static struct platform_driver emac_platform_driver = { + .probe = emac_probe, + .remove = emac_remove, + .driver = { +- .owner = THIS_MODULE, + .name = "qcom-emac", + .of_match_table = emac_dt_match, + }, +-- +Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm +Technologies, Inc. Qualcomm Technologies, Inc. is a member of the Code Aurora +Forum, a Linux Foundation Collaborative Project. + + +From 5b0e60e3ddf0584b4cc6aacea9b08067e66866f9 Mon Sep 17 00:00:00 2001 +From: Timur Tabi <timur at codeaurora.org> +Date: Wed, 28 Sep 2016 11:58:42 -0500 +Subject: [PATCH 04/17] net: qcom/emac: do not use devm on internal phy pdev + +The platform_device returned by of_find_device_by_node() is not +automatically released when the driver unprobes. Therefore, +managed calls like devm_ioremap_resource() should not be used. +Instead, we manually allocate the resources and then free them +on driver release. + +Signed-off-by: Timur Tabi <timur at codeaurora.org> +Signed-off-by: David S. Miller <davem at davemloft.net> +(cherry picked from commit 54e19bc74f3380d414681762ceed9f7245bc6a6e) +--- + drivers/net/ethernet/qualcomm/emac/emac-sgmii.c | 42 +++++++++++++++++++------ + drivers/net/ethernet/qualcomm/emac/emac.c | 4 +++ + 2 files changed, 37 insertions(+), 9 deletions(-) + +diff --git a/drivers/net/ethernet/qualcomm/emac/emac-sgmii.c b/drivers/net/ethernet/qualcomm/emac/emac-sgmii.c +index 6ab0a3c..ad0e420 100644 +--- a/drivers/net/ethernet/qualcomm/emac/emac-sgmii.c ++++ b/drivers/net/ethernet/qualcomm/emac/emac-sgmii.c +@@ -681,6 +681,7 @@ int emac_sgmii_config(struct platform_device *pdev, struct emac_adapter *adpt) + struct resource *res; + const struct of_device_id *match; + struct device_node *np; ++ int ret; + + np = of_parse_phandle(pdev->dev.of_node, "internal-phy", 0); + if (!np) { +@@ -697,25 +698,48 @@ int emac_sgmii_config(struct platform_device *pdev, struct emac_adapter *adpt) + match = of_match_device(emac_sgmii_dt_match, &sgmii_pdev->dev); + if (!match) { + dev_err(&pdev->dev, "unrecognized internal phy node\n"); +- return -ENODEV; ++ ret = -ENODEV; ++ goto error_put_device; + } + + phy->initialize = (emac_sgmii_initialize)match->data; + + /* Base address is the first address */ + res = platform_get_resource(sgmii_pdev, IORESOURCE_MEM, 0); +- phy->base = devm_ioremap_resource(&sgmii_pdev->dev, res); +- if (IS_ERR(phy->base)) +- return PTR_ERR(phy->base); ++ phy->base = ioremap(res->start, resource_size(res)); ++ if (IS_ERR(phy->base)) { ++ ret = PTR_ERR(phy->base); ++ goto error_put_device; ++ } + + /* v2 SGMII has a per-lane digital digital, so parse it if it exists */ + res = platform_get_resource(sgmii_pdev, IORESOURCE_MEM, 1); + if (res) { +- phy->digital = devm_ioremap_resource(&sgmii_pdev->dev, res); +- if (IS_ERR(phy->base)) +- return PTR_ERR(phy->base); +- ++ phy->digital = ioremap(res->start, resource_size(res)); ++ if (IS_ERR(phy->digital)) { ++ ret = PTR_ERR(phy->digital); ++ goto error_unmap_base; ++ } + } + +- return phy->initialize(adpt); ++ ret = phy->initialize(adpt); ++ if (ret) ++ goto error; ++ ++ /* We've remapped the addresses, so we don't need the device any ++ * more. of_find_device_by_node() says we should release it. ++ */ ++ put_device(&sgmii_pdev->dev); ++ ++ return 0; ++ ++error: ++ if (phy->digital) ++ iounmap(phy->digital); ++error_unmap_base: ++ iounmap(phy->base); ++error_put_device: ++ put_device(&sgmii_pdev->dev); ++ ++ return ret; + } +diff --git a/drivers/net/ethernet/qualcomm/emac/emac.c b/drivers/net/ethernet/qualcomm/emac/emac.c +index e47d387..429b4cb 100644 +--- a/drivers/net/ethernet/qualcomm/emac/emac.c ++++ b/drivers/net/ethernet/qualcomm/emac/emac.c +@@ -723,6 +723,10 @@ static int emac_remove(struct platform_device *pdev) + mdiobus_unregister(adpt->mii_bus); + free_netdev(netdev); + ++ if (adpt->phy.digital) ++ iounmap(adpt->phy.digital); ++ iounmap(adpt->phy.base); ++ + return 0; + } + +-- +Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm +Technologies, Inc. Qualcomm Technologies, Inc. is a member of the Code Aurora +Forum, a Linux Foundation Collaborative Project. + + +From 960704db65564fafaccf0fdf98147f7bef86c56d Mon Sep 17 00:00:00 2001 +From: Timur Tabi <timur at codeaurora.org> +Date: Wed, 28 Sep 2016 11:58:43 -0500 +Subject: [PATCH 05/17] net: qcom/emac: use device_get_mac_address + +Replace the DT-specific of_get_mac_address() function with +device_get_mac_address, which works on both DT and ACPI platforms. This +change makes it easier to add ACPI support. + +Signed-off-by: Timur Tabi <timur at codeaurora.org> +Signed-off-by: David S. Miller <davem at davemloft.net> +(cherry picked from commit 0de709acbc0d0001dc4b0953aebcfb0f030b9b0e) +--- + drivers/net/ethernet/qualcomm/emac/emac.c | 10 ++++------ + 1 file changed, 4 insertions(+), 6 deletions(-) + +diff --git a/drivers/net/ethernet/qualcomm/emac/emac.c b/drivers/net/ethernet/qualcomm/emac/emac.c +index 429b4cb..551df1c 100644 +--- a/drivers/net/ethernet/qualcomm/emac/emac.c ++++ b/drivers/net/ethernet/qualcomm/emac/emac.c +@@ -531,18 +531,16 @@ static void emac_clks_teardown(struct emac_adapter *adpt) + static int emac_probe_resources(struct platform_device *pdev, + struct emac_adapter *adpt) + { +- struct device_node *node = pdev->dev.of_node; + struct net_device *netdev = adpt->netdev; + struct resource *res; +- const void *maddr; ++ char maddr[ETH_ALEN]; + int ret = 0; + + /* get mac address */ +- maddr = of_get_mac_address(node); +- if (!maddr) +- eth_hw_addr_random(netdev); +- else ++ if (device_get_mac_address(&pdev->dev, maddr, ETH_ALEN)) + ether_addr_copy(netdev->dev_addr, maddr); ++ else ++ eth_hw_addr_random(netdev); + + /* Core 0 interrupt */ + ret = platform_get_irq(pdev, 0); +-- +Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm +Technologies, Inc. Qualcomm Technologies, Inc. is a member of the Code Aurora +Forum, a Linux Foundation Collaborative Project. + + +From ebc1a440fa4fefa3fe6bed50801e4fd8e61b0717 Mon Sep 17 00:00:00 2001 +From: Timur Tabi <timur at codeaurora.org> +Date: Wed, 28 Sep 2016 11:58:44 -0500 +Subject: [PATCH 06/17] net: qcom/emac: initial ACPI support + +Add support for reading addresses, interrupts, and _DSD properties +from ACPI tables, just like with device tree. The HID for the +EMAC device itself is QCOM8070. The internal PHY is represented +by a child node with a HID of QCOM8071. + +The EMAC also has some complex clock initialization requirements +that are not represented by this patch. This will be addressed +in a future patch. + +Signed-off-by: Timur Tabi <timur at codeaurora.org> +Signed-off-by: David S. Miller <davem at davemloft.net> +(cherry picked from commit 5f3d38078c84f7bc12739a3b79fc59797cf0262d) +--- + drivers/net/ethernet/qualcomm/emac/emac-phy.c | 37 ++++++++++--- + drivers/net/ethernet/qualcomm/emac/emac-sgmii.c | 72 ++++++++++++++++++------- + drivers/net/ethernet/qualcomm/emac/emac.c | 12 +++++ + 3 files changed, 95 insertions(+), 26 deletions(-) + +diff --git a/drivers/net/ethernet/qualcomm/emac/emac-phy.c b/drivers/net/ethernet/qualcomm/emac/emac-phy.c +index c412ba9..da4e90d 100644 +--- a/drivers/net/ethernet/qualcomm/emac/emac-phy.c ++++ b/drivers/net/ethernet/qualcomm/emac/emac-phy.c +@@ -19,6 +19,7 @@ + #include <linux/of_mdio.h> + #include <linux/phy.h> + #include <linux/iopoll.h> ++#include <linux/acpi.h> + #include "emac.h" + #include "emac-mac.h" + #include "emac-phy.h" +@@ -167,7 +168,6 @@ static int emac_mdio_write(struct mii_bus *bus, int addr, int regnum, u16 val) + int emac_phy_config(struct platform_device *pdev, struct emac_adapter *adpt) + { + struct device_node *np = pdev->dev.of_node; +- struct device_node *phy_np; + struct mii_bus *mii_bus; + int ret; + +@@ -183,14 +183,37 @@ int emac_phy_config(struct platform_device *pdev, struct emac_adapter *adpt) + mii_bus->parent = &pdev->dev; + mii_bus->priv = adpt; + +- ret = of_mdiobus_register(mii_bus, np); +- if (ret) { +- dev_err(&pdev->dev, "could not register mdio bus\n"); +- return ret; ++ if (has_acpi_companion(&pdev->dev)) { ++ u32 phy_addr; ++ ++ ret = mdiobus_register(mii_bus); ++ if (ret) { ++ dev_err(&pdev->dev, "could not register mdio bus\n"); ++ return ret; ++ } ++ ret = device_property_read_u32(&pdev->dev, "phy-channel", ++ &phy_addr); ++ if (ret) ++ /* If we can't read a valid phy address, then assume ++ * that there is only one phy on this mdio bus. ++ */ ++ adpt->phydev = phy_find_first(mii_bus); ++ else ++ adpt->phydev = mdiobus_get_phy(mii_bus, phy_addr); ++ ++ } else { ++ struct device_node *phy_np; ++ ++ ret = of_mdiobus_register(mii_bus, np); ++ if (ret) { ++ dev_err(&pdev->dev, "could not register mdio bus\n"); ++ return ret; ++ } ++ ++ phy_np = of_parse_phandle(np, "phy-handle", 0); ++ adpt->phydev = of_phy_find_device(phy_np); + } + +- phy_np = of_parse_phandle(np, "phy-handle", 0); +- adpt->phydev = of_phy_find_device(phy_np); + if (!adpt->phydev) { + dev_err(&pdev->dev, "could not find external phy\n"); + mdiobus_unregister(mii_bus); +diff --git a/drivers/net/ethernet/qualcomm/emac/emac-sgmii.c b/drivers/net/ethernet/qualcomm/emac/emac-sgmii.c +index ad0e420..3d2c05a 100644 +--- a/drivers/net/ethernet/qualcomm/emac/emac-sgmii.c ++++ b/drivers/net/ethernet/qualcomm/emac/emac-sgmii.c +@@ -14,6 +14,7 @@ + */ + + #include <linux/iopoll.h> ++#include <linux/acpi.h> + #include <linux/of_device.h> + #include "emac.h" + #include "emac-mac.h" +@@ -662,6 +663,24 @@ void emac_sgmii_reset(struct emac_adapter *adpt) + clk_set_rate(adpt->clk[EMAC_CLK_HIGH_SPEED], 125000000); + } + ++static int emac_sgmii_acpi_match(struct device *dev, void *data) ++{ ++ static const struct acpi_device_id match_table[] = { ++ { ++ .id = "QCOM8071", ++ .driver_data = (kernel_ulong_t)emac_sgmii_init_v2, ++ }, ++ {} ++ }; ++ const struct acpi_device_id *id = acpi_match_device(match_table, dev); ++ emac_sgmii_initialize *initialize = data; ++ ++ if (id) ++ *initialize = (emac_sgmii_initialize)id->driver_data; ++ ++ return !!id; ++} ++ + static const struct of_device_id emac_sgmii_dt_match[] = { + { + .compatible = "qcom,fsm9900-emac-sgmii", +@@ -679,30 +698,45 @@ int emac_sgmii_config(struct platform_device *pdev, struct emac_adapter *adpt) + struct platform_device *sgmii_pdev = NULL; + struct emac_phy *phy = &adpt->phy; + struct resource *res; +- const struct of_device_id *match; +- struct device_node *np; + int ret; + +- np = of_parse_phandle(pdev->dev.of_node, "internal-phy", 0); +- if (!np) { +- dev_err(&pdev->dev, "missing internal-phy property\n"); +- return -ENODEV; +- } ++ if (has_acpi_companion(&pdev->dev)) { ++ struct device *dev; + +- sgmii_pdev = of_find_device_by_node(np); +- if (!sgmii_pdev) { +- dev_err(&pdev->dev, "invalid internal-phy property\n"); +- return -ENODEV; +- } ++ dev = device_find_child(&pdev->dev, &phy->initialize, ++ emac_sgmii_acpi_match); + +- match = of_match_device(emac_sgmii_dt_match, &sgmii_pdev->dev); +- if (!match) { +- dev_err(&pdev->dev, "unrecognized internal phy node\n"); +- ret = -ENODEV; +- goto error_put_device; +- } ++ if (!dev) { ++ dev_err(&pdev->dev, "cannot find internal phy node\n"); ++ return -ENODEV; ++ } + +- phy->initialize = (emac_sgmii_initialize)match->data; ++ sgmii_pdev = to_platform_device(dev); ++ } else { ++ const struct of_device_id *match; ++ struct device_node *np; ++ ++ np = of_parse_phandle(pdev->dev.of_node, "internal-phy", 0); ++ if (!np) { ++ dev_err(&pdev->dev, "missing internal-phy property\n"); ++ return -ENODEV; ++ } ++ ++ sgmii_pdev = of_find_device_by_node(np); ++ if (!sgmii_pdev) { ++ dev_err(&pdev->dev, "invalid internal-phy property\n"); ++ return -ENODEV; ++ } ++ ++ match = of_match_device(emac_sgmii_dt_match, &sgmii_pdev->dev); ++ if (!match) { ++ dev_err(&pdev->dev, "unrecognized internal phy node\n"); ++ ret = -ENODEV; ++ goto error_put_device; ++ } ++ ++ phy->initialize = (emac_sgmii_initialize)match->data; ++ } + + /* Base address is the first address */ + res = platform_get_resource(sgmii_pdev, IORESOURCE_MEM, 0); +diff --git a/drivers/net/ethernet/qualcomm/emac/emac.c b/drivers/net/ethernet/qualcomm/emac/emac.c +index 551df1c..9bf3b2b 100644 +--- a/drivers/net/ethernet/qualcomm/emac/emac.c ++++ b/drivers/net/ethernet/qualcomm/emac/emac.c +@@ -22,6 +22,7 @@ + #include <linux/of_device.h> + #include <linux/phy.h> + #include <linux/platform_device.h> ++#include <linux/acpi.h> + #include "emac.h" + #include "emac-mac.h" + #include "emac-phy.h" +@@ -575,6 +576,16 @@ static const struct of_device_id emac_dt_match[] = { + {} + }; + ++#if IS_ENABLED(CONFIG_ACPI) ++static const struct acpi_device_id emac_acpi_match[] = { ++ { ++ .id = "QCOM8070", ++ }, ++ {} ++}; ++MODULE_DEVICE_TABLE(acpi, emac_acpi_match); ++#endif ++ + static int emac_probe(struct platform_device *pdev) + { + struct net_device *netdev; +@@ -734,6 +745,7 @@ static struct platform_driver emac_platform_driver = { + .driver = { + .name = "qcom-emac", + .of_match_table = emac_dt_match, ++ .acpi_match_table = ACPI_PTR(emac_acpi_match), + }, + }; + +-- +Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm +Technologies, Inc. Qualcomm Technologies, Inc. is a member of the Code Aurora +Forum, a Linux Foundation Collaborative Project. + + +From 70d19022105a6ed626e942d5f542293774555757 Mon Sep 17 00:00:00 2001 +From: Wei Yongjun <weiyongjun1 at huawei.com> +Date: Sat, 1 Oct 2016 09:12:29 +0000 +Subject: [PATCH 07/17] net: qcom/emac: fix return value check in + emac_sgmii_config() + +In case of error, the function ioremap() returns NULL pointer +not ERR_PTR(). The IS_ERR() test in the return value check +should be replaced with NULL test. + +Also add check for return value of platform_get_resource(). + +Fixes: 54e19bc74f33 ("net: qcom/emac: do not use devm on internal +phy pdev") +Signed-off-by: Wei Yongjun <weiyongjun1 at huawei.com> +Acked-by: Timur Tabi <timur at codeaurora.org> +Signed-off-by: David S. Miller <davem at davemloft.net> + +(cherry picked from commit 0fd7d43fbc4aaf358005231a6bed27eb1c2f60c3) +--- + drivers/net/ethernet/qualcomm/emac/emac-sgmii.c | 13 +++++++++---- + 1 file changed, 9 insertions(+), 4 deletions(-) + +diff --git a/drivers/net/ethernet/qualcomm/emac/emac-sgmii.c b/drivers/net/ethernet/qualcomm/emac/emac-sgmii.c +index 3d2c05a..75c1b53 100644 +--- a/drivers/net/ethernet/qualcomm/emac/emac-sgmii.c ++++ b/drivers/net/ethernet/qualcomm/emac/emac-sgmii.c +@@ -740,9 +740,14 @@ int emac_sgmii_config(struct platform_device *pdev, struct emac_adapter *adpt) + + /* Base address is the first address */ + res = platform_get_resource(sgmii_pdev, IORESOURCE_MEM, 0); ++ if (!res) { ++ ret = -EINVAL; ++ goto error_put_device; ++ } ++ + phy->base = ioremap(res->start, resource_size(res)); +- if (IS_ERR(phy->base)) { +- ret = PTR_ERR(phy->base); ++ if (!phy->base) { ++ ret = -ENOMEM; + goto error_put_device; + } + +@@ -750,8 +755,8 @@ int emac_sgmii_config(struct platform_device *pdev, struct emac_adapter *adpt) + res = platform_get_resource(sgmii_pdev, IORESOURCE_MEM, 1); + if (res) { + phy->digital = ioremap(res->start, resource_size(res)); +- if (IS_ERR(phy->digital)) { +- ret = PTR_ERR(phy->digital); ++ if (!phy->digital) { ++ ret = -ENOMEM; + goto error_unmap_base; + } + } +-- +Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm +Technologies, Inc. Qualcomm Technologies, Inc. is a member of the Code Aurora +Forum, a Linux Foundation Collaborative Project. + + +From 0b50446fca931002f3a1f3d9279185cefb413e43 Mon Sep 17 00:00:00 2001 +From: Geert Uytterhoeven <geert at linux-m68k.org> +Date: Thu, 6 Oct 2016 16:44:53 +0200 +Subject: [PATCH 08/17] ethernet: qualcomm: QCOM_EMAC should depend on HAS_DMA + and HAS_IOMEM +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +If NO_DMA=y: + + drivers/built-in.o: In function `emac_probe': + emac.c:(.text+0x3780b8): undefined reference to `bad_dma_ops' + emac.c:(.text+0x3780e2): undefined reference to `bad_dma_ops' + emac.c:(.text+0x378112): undefined reference to `bad_dma_ops' + emac.c:(.text+0x378146): undefined reference to `bad_dma_ops' + emac.c:(.text+0x37816e): undefined reference to `bad_dma_ops' + drivers/built-in.o:emac.c:(.text+0x37819a): more undefined references to `bad_dma_ops' follow + +If NO_IOMEM=y: + + drivers/net/ethernet/qualcomm/emac/emac.c: In function ?emac_remove?: + drivers/net/ethernet/qualcomm/emac/emac.c:736:3: error: implicit declaration of function ?iounmap? [-Werror=implicit-function-declaration] + iounmap(adpt->phy.digital); + ^ + +Add dependencies on HAS_DMA and HAS_IOMEM to fix this. + +Signed-off-by: Geert Uytterhoeven <geert at linux-m68k.org> +Acked-by: Timur Tabi <timur at codeaurora.org> +Signed-off-by: David S. Miller <davem at davemloft.net> +(cherry picked from commit e5e0fbfc4e3b343ff985dd800f1ee31564793563) +--- + drivers/net/ethernet/qualcomm/Kconfig | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/net/ethernet/qualcomm/Kconfig b/drivers/net/ethernet/qualcomm/Kconfig +index 9ba568d..d7720bf 100644 +--- a/drivers/net/ethernet/qualcomm/Kconfig ++++ b/drivers/net/ethernet/qualcomm/Kconfig +@@ -26,6 +26,7 @@ config QCA7000 + + config QCOM_EMAC + tristate "Qualcomm Technologies, Inc. EMAC Gigabit Ethernet support" ++ depends on HAS_DMA && HAS_IOMEM + select CRC32 + select PHYLIB + ---help--- +-- +Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm +Technologies, Inc. Qualcomm Technologies, Inc. is a member of the Code Aurora +Forum, a Linux Foundation Collaborative Project. + + +From b2b3ad120df7295ec3cf37b8a5253da40adbdfb7 Mon Sep 17 00:00:00 2001 +From: Timur Tabi <timur at codeaurora.org> +Date: Fri, 14 Oct 2016 14:14:35 -0500 +Subject: [PATCH 09/17] net: qcom/emac: disable interrupts before calling + phy_disconnect + +There is a race condition that can occur if EMAC interrupts are +enabled when phy_disconnect() is called. phy_disconnect() sets +adjust_link to NULL. When an interrupt occurs, the ISR might +call phy_mac_interrupt(), which wakes up the workqueue function +phy_state_machine(). This function might reference adjust_link, +thereby causing a null pointer exception. + +Signed-off-by: Timur Tabi <timur at codeaurora.org> +Signed-off-by: David S. Miller <davem at davemloft.net> +(cherry picked from commit 93966b715b32a783a1641f5a385901bbfab04733) +--- + drivers/net/ethernet/qualcomm/emac/emac-mac.c | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/drivers/net/ethernet/qualcomm/emac/emac-mac.c b/drivers/net/ethernet/qualcomm/emac/emac-mac.c +index e97968e..6fb3bee 100644 +--- a/drivers/net/ethernet/qualcomm/emac/emac-mac.c ++++ b/drivers/net/ethernet/qualcomm/emac/emac-mac.c +@@ -1021,14 +1021,18 @@ void emac_mac_down(struct emac_adapter *adpt) + napi_disable(&adpt->rx_q.napi); + + phy_stop(adpt->phydev); +- phy_disconnect(adpt->phydev); + +- /* disable mac irq */ ++ /* Interrupts must be disabled before the PHY is disconnected, to ++ * avoid a race condition where adjust_link is null when we get ++ * an interrupt. ++ */ + writel(DIS_INT, adpt->base + EMAC_INT_STATUS); + writel(0, adpt->base + EMAC_INT_MASK); + synchronize_irq(adpt->irq.irq); + free_irq(adpt->irq.irq, &adpt->irq); + ++ phy_disconnect(adpt->phydev); ++ + emac_mac_reset(adpt); + + emac_tx_q_descs_free(adpt); +-- +Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm +Technologies, Inc. Qualcomm Technologies, Inc. is a member of the Code Aurora +Forum, a Linux Foundation Collaborative Project. + + +From 365117a7d1a658ccafcb739ba95deda8ce554638 Mon Sep 17 00:00:00 2001 +From: Javier Martinez Canillas <javier at osg.samsung.com> +Date: Mon, 17 Oct 2016 11:05:43 -0300 +Subject: [PATCH 10/17] net: qcom/emac: Fix module autoload for OF registration + +If the driver is built as a module, autoload won't work because the module +alias information is not filled. So user-space can't match the registered +device with the corresponding module. + +Export the module alias information using the MODULE_DEVICE_TABLE() macro. + +Before this patch: + +$ modinfo drivers/net/ethernet/qualcomm/emac/qcom-emac.ko | grep alias +alias: platform:qcom-emac + +After this patch: + +$ modinfo drivers/net/ethernet/qualcomm/emac/qcom-emac.ko | grep alias +alias: platform:qcom-emac +alias: of:N*T*Cqcom,fsm9900-emacC* +alias: of:N*T*Cqcom,fsm9900-emac + +Signed-off-by: Javier Martinez Canillas <javier at osg.samsung.com> +Acked-by: Timur Tabi <timur at codeaurora.org> +Signed-off-by: David S. Miller <davem at davemloft.net> +(cherry picked from commit 7097268509a7d6756d14ab422af7446f9bf53221) +--- + drivers/net/ethernet/qualcomm/emac/emac.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/net/ethernet/qualcomm/emac/emac.c b/drivers/net/ethernet/qualcomm/emac/emac.c +index 9bf3b2b..4fede4b 100644 +--- a/drivers/net/ethernet/qualcomm/emac/emac.c ++++ b/drivers/net/ethernet/qualcomm/emac/emac.c +@@ -575,6 +575,7 @@ static const struct of_device_id emac_dt_match[] = { + }, + {} + }; ++MODULE_DEVICE_TABLE(of, emac_dt_match); + + #if IS_ENABLED(CONFIG_ACPI) + static const struct acpi_device_id emac_acpi_match[] = { +-- +Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm +Technologies, Inc. Qualcomm Technologies, Inc. is a member of the Code Aurora +Forum, a Linux Foundation Collaborative Project. + + +From 2f3f76b226b960952726968a05c2731bd8ee8805 Mon Sep 17 00:00:00 2001 +From: Timur Tabi <timur at codeaurora.org> +Date: Mon, 31 Oct 2016 18:18:42 -0500 +Subject: [PATCH 11/17] net: qcom/emac: use correct value for + SGMII_LN_UCDR_SO_GAIN_MODE0 + +The documentation says that SGMII_LN_UCDR_SO_GAIN_MODE0 should be +set to 0, not 6, on the Qualcomm Technologies QDF2432. + +Signed-off-by: Timur Tabi <timur at codeaurora.org> +Signed-off-by: David S. Miller <davem at davemloft.net> +(cherry picked from commit 7bb9f731d1026bd48b84cee7853cba7f5678193c) +--- + drivers/net/ethernet/qualcomm/emac/emac-sgmii.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/net/ethernet/qualcomm/emac/emac-sgmii.c b/drivers/net/ethernet/qualcomm/emac/emac-sgmii.c +index 75c1b53..72fe343 100644 +--- a/drivers/net/ethernet/qualcomm/emac/emac-sgmii.c ++++ b/drivers/net/ethernet/qualcomm/emac/emac-sgmii.c +@@ -421,7 +421,7 @@ static const struct emac_reg_write sgmii_v2_laned[] = { + /* CDR Settings */ + {EMAC_SGMII_LN_UCDR_FO_GAIN_MODE0, + UCDR_STEP_BY_TWO_MODE0 | UCDR_xO_GAIN_MODE(10)}, +- {EMAC_SGMII_LN_UCDR_SO_GAIN_MODE0, UCDR_xO_GAIN_MODE(6)}, ++ {EMAC_SGMII_LN_UCDR_SO_GAIN_MODE0, UCDR_xO_GAIN_MODE(0)}, + {EMAC_SGMII_LN_UCDR_SO_CONFIG, UCDR_ENABLE | UCDR_SO_SATURATION(12)}, + + /* TX/RX Settings */ +-- +Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm +Technologies, Inc. Qualcomm Technologies, Inc. is a member of the Code Aurora +Forum, a Linux Foundation Collaborative Project. + + +From 0d2e4c4b2403a876b7d3bae956221379d3b6bb53 Mon Sep 17 00:00:00 2001 +From: Timur Tabi <timur at codeaurora.org> +Date: Mon, 7 Nov 2016 10:51:40 -0600 +Subject: [PATCH 12/17] net: qcom/emac: configure the external phy to allow + pause frames + +Pause frames are used to enable flow control. A MAC can send and +receive pause frames in order to throttle traffic. However, the PHY +must be configured to allow those frames to pass through. + +Reviewed-by: Florian Fainelli <f.fainelli at gmail.com> +Signed-off-by: Timur Tabi <timur at codeaurora.org> +Signed-off-by: David S. Miller <davem at davemloft.net> +(cherry picked from commit 3e884493448131179a5b7cae1ddca1028ffaecc8) +--- + drivers/net/ethernet/qualcomm/emac/emac-mac.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/drivers/net/ethernet/qualcomm/emac/emac-mac.c b/drivers/net/ethernet/qualcomm/emac/emac-mac.c +index 6fb3bee..70a55dc 100644 +--- a/drivers/net/ethernet/qualcomm/emac/emac-mac.c ++++ b/drivers/net/ethernet/qualcomm/emac/emac-mac.c +@@ -1003,6 +1003,12 @@ int emac_mac_up(struct emac_adapter *adpt) + writel((u32)~DIS_INT, adpt->base + EMAC_INT_STATUS); + writel(adpt->irq.mask, adpt->base + EMAC_INT_MASK); + ++ /* Enable pause frames. Without this feature, the EMAC has been shown ++ * to receive (and drop) frames with FCS errors at gigabit connections. ++ */ ++ adpt->phydev->supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause; ++ adpt->phydev->advertising |= SUPPORTED_Pause | SUPPORTED_Asym_Pause; ++ + adpt->phydev->irq = PHY_IGNORE_INTERRUPT; + phy_start(adpt->phydev); + +-- +Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm +Technologies, Inc. Qualcomm Technologies, Inc. is a member of the Code Aurora +Forum, a Linux Foundation Collaborative Project. + + +From 23a2358a8a1bfbe9d6e532df96deab21045a3b62 Mon Sep 17 00:00:00 2001 +From: Timur Tabi <timur at codeaurora.org> +Date: Mon, 7 Nov 2016 10:51:41 -0600 +Subject: [PATCH 13/17] net: qcom/emac: enable flow control if requested + +If the PHY has been configured to allow pause frames, then the MAC +should be configured to generate and/or accept those frames. + +Signed-off-by: Timur Tabi <timur at codeaurora.org> +Signed-off-by: David S. Miller <davem at davemloft.net> +(cherry picked from commit df63022e182de4041b65ae22df1950d3416b577e) +--- + drivers/net/ethernet/qualcomm/emac/emac-mac.c | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +diff --git a/drivers/net/ethernet/qualcomm/emac/emac-mac.c b/drivers/net/ethernet/qualcomm/emac/emac-mac.c +index 70a55dc..0b4deb3 100644 +--- a/drivers/net/ethernet/qualcomm/emac/emac-mac.c ++++ b/drivers/net/ethernet/qualcomm/emac/emac-mac.c +@@ -575,10 +575,11 @@ void emac_mac_start(struct emac_adapter *adpt) + + mac |= TXEN | RXEN; /* enable RX/TX */ + +- /* We don't have ethtool support yet, so force flow-control mode +- * to 'full' always. +- */ +- mac |= TXFC | RXFC; ++ /* Configure MAC flow control to match the PHY's settings. */ ++ if (phydev->pause) ++ mac |= RXFC; ++ if (phydev->pause != phydev->asym_pause) ++ mac |= TXFC; + + /* setup link speed */ + mac &= ~SPEED_MASK; +-- +Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm +Technologies, Inc. Qualcomm Technologies, Inc. is a member of the Code Aurora +Forum, a Linux Foundation Collaborative Project. + + +From e9e22c94ccf95f89ef919aed51602d442ae343ea Mon Sep 17 00:00:00 2001 +From: Johan Hovold <johan at kernel.org> +Date: Thu, 24 Nov 2016 19:21:31 +0100 +Subject: [PATCH 14/17] net: qcom/emac: fix of_node and phydev leaks + +Make sure to drop the reference taken by of_phy_find_device() during +probe on probe errors and on driver unbind. + +Also drop the of_node reference taken by of_parse_phandle() in the same +path. + +Fixes: b9b17debc69d ("net: emac: emac gigabit ethernet controller driver") +Signed-off-by: Johan Hovold <johan at kernel.org> +Signed-off-by: David S. Miller <davem at davemloft.net> +(cherry picked from commit 6ffe1c4cd0a77f51d8d2985aa721d636b03ddf58) +--- + drivers/net/ethernet/qualcomm/emac/emac-phy.c | 1 + + drivers/net/ethernet/qualcomm/emac/emac.c | 4 ++++ + 2 files changed, 5 insertions(+) + +diff --git a/drivers/net/ethernet/qualcomm/emac/emac-phy.c b/drivers/net/ethernet/qualcomm/emac/emac-phy.c +index da4e90d..99a14df 100644 +--- a/drivers/net/ethernet/qualcomm/emac/emac-phy.c ++++ b/drivers/net/ethernet/qualcomm/emac/emac-phy.c +@@ -212,6 +212,7 @@ int emac_phy_config(struct platform_device *pdev, struct emac_adapter *adpt) + + phy_np = of_parse_phandle(np, "phy-handle", 0); + adpt->phydev = of_phy_find_device(phy_np); ++ of_node_put(phy_np); + } + + if (!adpt->phydev) { +diff --git a/drivers/net/ethernet/qualcomm/emac/emac.c b/drivers/net/ethernet/qualcomm/emac/emac.c +index 4fede4b..57b35ae 100644 +--- a/drivers/net/ethernet/qualcomm/emac/emac.c ++++ b/drivers/net/ethernet/qualcomm/emac/emac.c +@@ -711,6 +711,8 @@ static int emac_probe(struct platform_device *pdev) + err_undo_napi: + netif_napi_del(&adpt->rx_q.napi); + err_undo_mdiobus: ++ if (!has_acpi_companion(&pdev->dev)) ++ put_device(&adpt->phydev->mdio.dev); + mdiobus_unregister(adpt->mii_bus); + err_undo_clocks: + emac_clks_teardown(adpt); +@@ -730,6 +732,8 @@ static int emac_remove(struct platform_device *pdev) + + emac_clks_teardown(adpt); + ++ if (!has_acpi_companion(&pdev->dev)) ++ put_device(&adpt->phydev->mdio.dev); + mdiobus_unregister(adpt->mii_bus); + free_netdev(netdev); + +-- +Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm +Technologies, Inc. Qualcomm Technologies, Inc. is a member of the Code Aurora +Forum, a Linux Foundation Collaborative Project. + + +From a99c0896cc3efc5bc1b91fb1105c83aff0189e65 Mon Sep 17 00:00:00 2001 +From: Timur Tabi <timur at codeaurora.org> +Date: Thu, 8 Dec 2016 13:24:20 -0600 +Subject: [PATCH 15/17] net: qcom/emac: move phy init code to separate files + +The internal PHY of the EMAC differs on each SOC, and the list will +only continue to grow. By separating the code into individual files, +we can add support for more SOCs more cleanly. + +Note: The internal PHY is also sometimes called the SGMII device. + +We also stop referring to the various PHY variations by version number, +so no more "v2", "v3", etc. Instead, the devices are named after the +SOC they are, which is in sync with the device tree property names. + +Future patches will probably rearrange more code among the files. + +Signed-off-by: Timur Tabi <timur at codeaurora.org> +Signed-off-by: David S. Miller <davem at davemloft.net> +(cherry picked from commit 1e88ab6fbbaacbd79dd14ab9ceec7a595611248d) +--- + drivers/net/ethernet/qualcomm/emac/Makefile | 3 +- + .../ethernet/qualcomm/emac/emac-sgmii-fsm9900.c | 245 ++++++++++ + .../ethernet/qualcomm/emac/emac-sgmii-qdf2432.c | 210 ++++++++ + drivers/net/ethernet/qualcomm/emac/emac-sgmii.c | 538 +-------------------- + drivers/net/ethernet/qualcomm/emac/emac-sgmii.h | 5 +- + 5 files changed, 478 insertions(+), 523 deletions(-) + create mode 100644 drivers/net/ethernet/qualcomm/emac/emac-sgmii-fsm9900.c + create mode 100644 drivers/net/ethernet/qualcomm/emac/emac-sgmii-qdf2432.c + +diff --git a/drivers/net/ethernet/qualcomm/emac/Makefile b/drivers/net/ethernet/qualcomm/emac/Makefile +index 01ee144..204b787 100644 +--- a/drivers/net/ethernet/qualcomm/emac/Makefile ++++ b/drivers/net/ethernet/qualcomm/emac/Makefile +@@ -4,4 +4,5 @@ + + obj-$(CONFIG_QCOM_EMAC) += qcom-emac.o + +-qcom-emac-objs := emac.o emac-mac.o emac-phy.o emac-sgmii.o ++qcom-emac-objs := emac.o emac-mac.o emac-phy.o emac-sgmii.o \ ++ emac-sgmii-fsm9900.o emac-sgmii-qdf2432.o +diff --git a/drivers/net/ethernet/qualcomm/emac/emac-sgmii-fsm9900.c b/drivers/net/ethernet/qualcomm/emac/emac-sgmii-fsm9900.c +new file mode 100644 +index 0000000..af690e1 +--- /dev/null ++++ b/drivers/net/ethernet/qualcomm/emac/emac-sgmii-fsm9900.c +@@ -0,0 +1,245 @@ ++/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that 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. ++ */ ++ ++/* Qualcomm Technologies, Inc. FSM9900 EMAC SGMII Controller driver. ++ */ ++ ++#include <linux/iopoll.h> ++#include "emac.h" ++ ++/* EMAC_QSERDES register offsets */ ++#define EMAC_QSERDES_COM_SYS_CLK_CTRL 0x0000 ++#define EMAC_QSERDES_COM_PLL_CNTRL 0x0014 ++#define EMAC_QSERDES_COM_PLL_IP_SETI 0x0018 ++#define EMAC_QSERDES_COM_PLL_CP_SETI 0x0024 ++#define EMAC_QSERDES_COM_PLL_IP_SETP 0x0028 ++#define EMAC_QSERDES_COM_PLL_CP_SETP 0x002c ++#define EMAC_QSERDES_COM_SYSCLK_EN_SEL 0x0038 ++#define EMAC_QSERDES_COM_RESETSM_CNTRL 0x0040 ++#define EMAC_QSERDES_COM_PLLLOCK_CMP1 0x0044 ++#define EMAC_QSERDES_COM_PLLLOCK_CMP2 0x0048 ++#define EMAC_QSERDES_COM_PLLLOCK_CMP3 0x004c ++#define EMAC_QSERDES_COM_PLLLOCK_CMP_EN 0x0050 ++#define EMAC_QSERDES_COM_DEC_START1 0x0064 ++#define EMAC_QSERDES_COM_DIV_FRAC_START1 0x0098 ++#define EMAC_QSERDES_COM_DIV_FRAC_START2 0x009c ++#define EMAC_QSERDES_COM_DIV_FRAC_START3 0x00a0 ++#define EMAC_QSERDES_COM_DEC_START2 0x00a4 ++#define EMAC_QSERDES_COM_PLL_CRCTRL 0x00ac ++#define EMAC_QSERDES_COM_RESET_SM 0x00bc ++#define EMAC_QSERDES_TX_BIST_MODE_LANENO 0x0100 ++#define EMAC_QSERDES_TX_TX_EMP_POST1_LVL 0x0108 ++#define EMAC_QSERDES_TX_TX_DRV_LVL 0x010c ++#define EMAC_QSERDES_TX_LANE_MODE 0x0150 ++#define EMAC_QSERDES_TX_TRAN_DRVR_EMP_EN 0x0170 ++#define EMAC_QSERDES_RX_CDR_CONTROL 0x0200 ++#define EMAC_QSERDES_RX_CDR_CONTROL2 0x0210 ++#define EMAC_QSERDES_RX_RX_EQ_GAIN12 0x0230 ++ ++/* EMAC_SGMII register offsets */ ++#define EMAC_SGMII_PHY_SERDES_START 0x0000 ++#define EMAC_SGMII_PHY_CMN_PWR_CTRL 0x0004 ++#define EMAC_SGMII_PHY_RX_PWR_CTRL 0x0008 ++#define EMAC_SGMII_PHY_TX_PWR_CTRL 0x000C ++#define EMAC_SGMII_PHY_LANE_CTRL1 0x0018 ++#define EMAC_SGMII_PHY_CDR_CTRL0 0x0058 ++#define EMAC_SGMII_PHY_POW_DWN_CTRL0 0x0080 ++#define EMAC_SGMII_PHY_INTERRUPT_MASK 0x00b4 ++ ++#define PLL_IPSETI(x) ((x) & 0x3f) ++ ++#define PLL_CPSETI(x) ((x) & 0xff) ++ ++#define PLL_IPSETP(x) ((x) & 0x3f) ++ ++#define PLL_CPSETP(x) ((x) & 0x1f) ++ ++#define PLL_RCTRL(x) (((x) & 0xf) << 4) ++#define PLL_CCTRL(x) ((x) & 0xf) ++ ++#define LANE_MODE(x) ((x) & 0x1f) ++ ++#define SYSCLK_CM BIT(4) ++#define SYSCLK_AC_COUPLE BIT(3) ++ ++#define OCP_EN BIT(5) ++#define PLL_DIV_FFEN BIT(2) ++#define PLL_DIV_ORD BIT(1) ++ ++#define SYSCLK_SEL_CMOS BIT(3) ++ ++#define FRQ_TUNE_MODE BIT(4) ++ ++#define PLLLOCK_CMP_EN BIT(0) ++ ++#define DEC_START1_MUX BIT(7) ++#define DEC_START1(x) ((x) & 0x7f) ++ ++#define DIV_FRAC_START_MUX BIT(7) ++#define DIV_FRAC_START(x) ((x) & 0x7f) ++ ++#define DIV_FRAC_START3_MUX BIT(4) ++#define DIV_FRAC_START3(x) ((x) & 0xf) ++ ++#define DEC_START2_MUX BIT(1) ++#define DEC_START2 BIT(0) ++ ++#define READY BIT(5) ++ ++#define TX_EMP_POST1_LVL_MUX BIT(5) ++#define TX_EMP_POST1_LVL(x) ((x) & 0x1f) ++ ++#define TX_DRV_LVL_MUX BIT(4) ++#define TX_DRV_LVL(x) ((x) & 0xf) ++ ++#define EMP_EN_MUX BIT(1) ++#define EMP_EN BIT(0) ++ ++#define SECONDORDERENABLE BIT(6) ++#define FIRSTORDER_THRESH(x) (((x) & 0x7) << 3) ++#define SECONDORDERGAIN(x) ((x) & 0x7) ++ ++#define RX_EQ_GAIN2(x) (((x) & 0xf) << 4) ++#define RX_EQ_GAIN1(x) ((x) & 0xf) ++ ++#define SERDES_START BIT(0) ++ ++#define BIAS_EN BIT(6) ++#define PLL_EN BIT(5) ++#define SYSCLK_EN BIT(4) ++#define CLKBUF_L_EN BIT(3) ++#define PLL_TXCLK_EN BIT(1) ++#define PLL_RXCLK_EN BIT(0) ++ ++#define L0_RX_SIGDET_EN BIT(7) ++#define L0_RX_TERM_MODE(x) (((x) & 3) << 4) ++#define L0_RX_I_EN BIT(1) ++ ++#define L0_TX_EN BIT(5) ++#define L0_CLKBUF_EN BIT(4) ++#define L0_TRAN_BIAS_EN BIT(1) ++ ++#define L0_RX_EQUALIZE_ENABLE BIT(6) ++#define L0_RESET_TSYNC_EN BIT(4) ++#define L0_DRV_LVL(x) ((x) & 0xf) ++ ++#define PWRDN_B BIT(0) ++#define CDR_MAX_CNT(x) ((x) & 0xff) ++ ++#define PLLLOCK_CMP(x) ((x) & 0xff) ++ ++#define SERDES_START_WAIT_TIMES 100 ++ ++struct emac_reg_write { ++ unsigned int offset; ++ u32 val; ++}; ++ ++static void emac_reg_write_all(void __iomem *base, ++ const struct emac_reg_write *itr, size_t size) ++{ ++ size_t i; ++ ++ for (i = 0; i < size; ++itr, ++i) ++ writel(itr->val, base + itr->offset); ++} ++ ++static const struct emac_reg_write physical_coding_sublayer_programming[] = { ++ {EMAC_SGMII_PHY_CDR_CTRL0, CDR_MAX_CNT(15)}, ++ {EMAC_SGMII_PHY_POW_DWN_CTRL0, PWRDN_B}, ++ {EMAC_SGMII_PHY_CMN_PWR_CTRL, ++ BIAS_EN | SYSCLK_EN | CLKBUF_L_EN | PLL_TXCLK_EN | PLL_RXCLK_EN}, ++ {EMAC_SGMII_PHY_TX_PWR_CTRL, L0_TX_EN | L0_CLKBUF_EN | L0_TRAN_BIAS_EN}, ++ {EMAC_SGMII_PHY_RX_PWR_CTRL, ++ L0_RX_SIGDET_EN | L0_RX_TERM_MODE(1) | L0_RX_I_EN}, ++ {EMAC_SGMII_PHY_CMN_PWR_CTRL, ++ BIAS_EN | PLL_EN | SYSCLK_EN | CLKBUF_L_EN | PLL_TXCLK_EN | ++ PLL_RXCLK_EN}, ++ {EMAC_SGMII_PHY_LANE_CTRL1, ++ L0_RX_EQUALIZE_ENABLE | L0_RESET_TSYNC_EN | L0_DRV_LVL(15)}, ++}; ++ ++static const struct emac_reg_write sysclk_refclk_setting[] = { ++ {EMAC_QSERDES_COM_SYSCLK_EN_SEL, SYSCLK_SEL_CMOS}, ++ {EMAC_QSERDES_COM_SYS_CLK_CTRL, SYSCLK_CM | SYSCLK_AC_COUPLE}, ++}; ++ ++static const struct emac_reg_write pll_setting[] = { ++ {EMAC_QSERDES_COM_PLL_IP_SETI, PLL_IPSETI(1)}, ++ {EMAC_QSERDES_COM_PLL_CP_SETI, PLL_CPSETI(59)}, ++ {EMAC_QSERDES_COM_PLL_IP_SETP, PLL_IPSETP(10)}, ++ {EMAC_QSERDES_COM_PLL_CP_SETP, PLL_CPSETP(9)}, ++ {EMAC_QSERDES_COM_PLL_CRCTRL, PLL_RCTRL(15) | PLL_CCTRL(11)}, ++ {EMAC_QSERDES_COM_PLL_CNTRL, OCP_EN | PLL_DIV_FFEN | PLL_DIV_ORD}, ++ {EMAC_QSERDES_COM_DEC_START1, DEC_START1_MUX | DEC_START1(2)}, ++ {EMAC_QSERDES_COM_DEC_START2, DEC_START2_MUX | DEC_START2}, ++ {EMAC_QSERDES_COM_DIV_FRAC_START1, ++ DIV_FRAC_START_MUX | DIV_FRAC_START(85)}, ++ {EMAC_QSERDES_COM_DIV_FRAC_START2, ++ DIV_FRAC_START_MUX | DIV_FRAC_START(42)}, ++ {EMAC_QSERDES_COM_DIV_FRAC_START3, ++ DIV_FRAC_START3_MUX | DIV_FRAC_START3(3)}, ++ {EMAC_QSERDES_COM_PLLLOCK_CMP1, PLLLOCK_CMP(43)}, ++ {EMAC_QSERDES_COM_PLLLOCK_CMP2, PLLLOCK_CMP(104)}, ++ {EMAC_QSERDES_COM_PLLLOCK_CMP3, PLLLOCK_CMP(0)}, ++ {EMAC_QSERDES_COM_PLLLOCK_CMP_EN, PLLLOCK_CMP_EN}, ++ {EMAC_QSERDES_COM_RESETSM_CNTRL, FRQ_TUNE_MODE}, ++}; ++ ++static const struct emac_reg_write cdr_setting[] = { ++ {EMAC_QSERDES_RX_CDR_CONTROL, ++ SECONDORDERENABLE | FIRSTORDER_THRESH(3) | SECONDORDERGAIN(2)}, ++ {EMAC_QSERDES_RX_CDR_CONTROL2, ++ SECONDORDERENABLE | FIRSTORDER_THRESH(3) | SECONDORDERGAIN(4)}, ++}; ++ ++static const struct emac_reg_write tx_rx_setting[] = { ++ {EMAC_QSERDES_TX_BIST_MODE_LANENO, 0}, ++ {EMAC_QSERDES_TX_TX_DRV_LVL, TX_DRV_LVL_MUX | TX_DRV_LVL(15)}, ++ {EMAC_QSERDES_TX_TRAN_DRVR_EMP_EN, EMP_EN_MUX | EMP_EN}, ++ {EMAC_QSERDES_TX_TX_EMP_POST1_LVL, ++ TX_EMP_POST1_LVL_MUX | TX_EMP_POST1_LVL(1)}, ++ {EMAC_QSERDES_RX_RX_EQ_GAIN12, RX_EQ_GAIN2(15) | RX_EQ_GAIN1(15)}, ++ {EMAC_QSERDES_TX_LANE_MODE, LANE_MODE(8)}, ++}; ++ ++int emac_sgmii_init_fsm9900(struct emac_adapter *adpt) ++{ ++ struct emac_phy *phy = &adpt->phy; ++ unsigned int i; ++ ++ emac_reg_write_all(phy->base, physical_coding_sublayer_programming, ++ ARRAY_SIZE(physical_coding_sublayer_programming)); ++ emac_reg_write_all(phy->base, sysclk_refclk_setting, ++ ARRAY_SIZE(sysclk_refclk_setting)); ++ emac_reg_write_all(phy->base, pll_setting, ARRAY_SIZE(pll_setting)); ++ emac_reg_write_all(phy->base, cdr_setting, ARRAY_SIZE(cdr_setting)); ++ emac_reg_write_all(phy->base, tx_rx_setting, ARRAY_SIZE(tx_rx_setting)); ++ ++ /* Power up the Ser/Des engine */ ++ writel(SERDES_START, phy->base + EMAC_SGMII_PHY_SERDES_START); ++ ++ for (i = 0; i < SERDES_START_WAIT_TIMES; i++) { ++ if (readl(phy->base + EMAC_QSERDES_COM_RESET_SM) & READY) ++ break; ++ usleep_range(100, 200); ++ } ++ ++ if (i == SERDES_START_WAIT_TIMES) { ++ netdev_err(adpt->netdev, "error: ser/des failed to start\n"); ++ return -EIO; ++ } ++ /* Mask out all the SGMII Interrupt */ ++ writel(0, phy->base + EMAC_SGMII_PHY_INTERRUPT_MASK); ++ ++ return 0; ++} +diff --git a/drivers/net/ethernet/qualcomm/emac/emac-sgmii-qdf2432.c b/drivers/net/ethernet/qualcomm/emac/emac-sgmii-qdf2432.c +new file mode 100644 +index 0000000..6170200 +--- /dev/null ++++ b/drivers/net/ethernet/qualcomm/emac/emac-sgmii-qdf2432.c +@@ -0,0 +1,210 @@ ++/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that 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. ++ */ ++ ++/* Qualcomm Technologies, Inc. QDF2432 EMAC SGMII Controller driver. ++ */ ++ ++#include <linux/iopoll.h> ++#include "emac.h" ++ ++/* EMAC_SGMII register offsets */ ++#define EMAC_SGMII_PHY_TX_PWR_CTRL 0x000C ++#define EMAC_SGMII_PHY_LANE_CTRL1 0x0018 ++#define EMAC_SGMII_PHY_CDR_CTRL0 0x0058 ++#define EMAC_SGMII_PHY_POW_DWN_CTRL0 0x0080 ++#define EMAC_SGMII_PHY_RESET_CTRL 0x00a8 ++#define EMAC_SGMII_PHY_INTERRUPT_MASK 0x00b4 ++ ++/* SGMII digital lane registers */ ++#define EMAC_SGMII_LN_DRVR_CTRL0 0x000C ++#define EMAC_SGMII_LN_DRVR_TAP_EN 0x0018 ++#define EMAC_SGMII_LN_TX_MARGINING 0x001C ++#define EMAC_SGMII_LN_TX_PRE 0x0020 ++#define EMAC_SGMII_LN_TX_POST 0x0024 ++#define EMAC_SGMII_LN_TX_BAND_MODE 0x0060 ++#define EMAC_SGMII_LN_LANE_MODE 0x0064 ++#define EMAC_SGMII_LN_PARALLEL_RATE 0x0078 ++#define EMAC_SGMII_LN_CML_CTRL_MODE0 0x00B8 ++#define EMAC_SGMII_LN_MIXER_CTRL_MODE0 0x00D0 ++#define EMAC_SGMII_LN_VGA_INITVAL 0x0134 ++#define EMAC_SGMII_LN_UCDR_FO_GAIN_MODE0 0x017C ++#define EMAC_SGMII_LN_UCDR_SO_GAIN_MODE0 0x0188 ++#define EMAC_SGMII_LN_UCDR_SO_CONFIG 0x0194 ++#define EMAC_SGMII_LN_RX_BAND 0x019C ++#define EMAC_SGMII_LN_RX_RCVR_PATH1_MODE0 0x01B8 ++#define EMAC_SGMII_LN_RSM_CONFIG 0x01F0 ++#define EMAC_SGMII_LN_SIGDET_ENABLES 0x0224 ++#define EMAC_SGMII_LN_SIGDET_CNTRL 0x0228 ++#define EMAC_SGMII_LN_SIGDET_DEGLITCH_CNTRL 0x022C ++#define EMAC_SGMII_LN_RX_EN_SIGNAL 0x02A0 ++#define EMAC_SGMII_LN_RX_MISC_CNTRL0 0x02AC ++#define EMAC_SGMII_LN_DRVR_LOGIC_CLKDIV 0x02BC ++ ++/* SGMII digital lane register values */ ++#define UCDR_STEP_BY_TWO_MODE0 BIT(7) ++#define UCDR_xO_GAIN_MODE(x) ((x) & 0x7f) ++#define UCDR_ENABLE BIT(6) ++#define UCDR_SO_SATURATION(x) ((x) & 0x3f) ++ ++#define SIGDET_LP_BYP_PS4 BIT(7) ++#define SIGDET_EN_PS0_TO_PS2 BIT(6) ++ ++#define TXVAL_VALID_INIT BIT(4) ++#define KR_PCIGEN3_MODE BIT(0) ++ ++#define MAIN_EN BIT(0) ++ ++#define TX_MARGINING_MUX BIT(6) ++#define TX_MARGINING(x) ((x) & 0x3f) ++ ++#define TX_PRE_MUX BIT(6) ++ ++#define TX_POST_MUX BIT(6) ++ ++#define CML_GEAR_MODE(x) (((x) & 7) << 3) ++#define CML2CMOS_IBOOST_MODE(x) ((x) & 7) ++ ++#define MIXER_LOADB_MODE(x) (((x) & 0xf) << 2) ++#define MIXER_DATARATE_MODE(x) ((x) & 3) ++ ++#define VGA_THRESH_DFE(x) ((x) & 0x3f) ++ ++#define SIGDET_LP_BYP_PS0_TO_PS2 BIT(5) ++#define SIGDET_FLT_BYP BIT(0) ++ ++#define SIGDET_LVL(x) (((x) & 0xf) << 4) ++ ++#define SIGDET_DEGLITCH_CTRL(x) (((x) & 0xf) << 1) ++ ++#define DRVR_LOGIC_CLK_EN BIT(4) ++#define DRVR_LOGIC_CLK_DIV(x) ((x) & 0xf) ++ ++#define PARALLEL_RATE_MODE0(x) ((x) & 0x3) ++ ++#define BAND_MODE0(x) ((x) & 0x3) ++ ++#define LANE_MODE(x) ((x) & 0x1f) ++ ++#define CDR_PD_SEL_MODE0(x) (((x) & 0x3) << 5) ++#define BYPASS_RSM_SAMP_CAL BIT(1) ++#define BYPASS_RSM_DLL_CAL BIT(0) ++ ++#define L0_RX_EQUALIZE_ENABLE BIT(6) ++ ++#define PWRDN_B BIT(0) ++ ++#define CDR_MAX_CNT(x) ((x) & 0xff) ++ ++#define SERDES_START_WAIT_TIMES 100 ++ ++struct emac_reg_write { ++ unsigned int offset; ++ u32 val; ++}; ++ ++static void emac_reg_write_all(void __iomem *base, ++ const struct emac_reg_write *itr, size_t size) ++{ ++ size_t i; ++ ++ for (i = 0; i < size; ++itr, ++i) ++ writel(itr->val, base + itr->offset); ++} ++ ++static const struct emac_reg_write sgmii_laned[] = { ++ /* CDR Settings */ ++ {EMAC_SGMII_LN_UCDR_FO_GAIN_MODE0, ++ UCDR_STEP_BY_TWO_MODE0 | UCDR_xO_GAIN_MODE(10)}, ++ {EMAC_SGMII_LN_UCDR_SO_GAIN_MODE0, UCDR_xO_GAIN_MODE(0)}, ++ {EMAC_SGMII_LN_UCDR_SO_CONFIG, UCDR_ENABLE | UCDR_SO_SATURATION(12)}, ++ ++ /* TX/RX Settings */ ++ {EMAC_SGMII_LN_RX_EN_SIGNAL, SIGDET_LP_BYP_PS4 | SIGDET_EN_PS0_TO_PS2}, ++ ++ {EMAC_SGMII_LN_DRVR_CTRL0, TXVAL_VALID_INIT | KR_PCIGEN3_MODE}, ++ {EMAC_SGMII_LN_DRVR_TAP_EN, MAIN_EN}, ++ {EMAC_SGMII_LN_TX_MARGINING, TX_MARGINING_MUX | TX_MARGINING(25)}, ++ {EMAC_SGMII_LN_TX_PRE, TX_PRE_MUX}, ++ {EMAC_SGMII_LN_TX_POST, TX_POST_MUX}, ++ ++ {EMAC_SGMII_LN_CML_CTRL_MODE0, ++ CML_GEAR_MODE(1) | CML2CMOS_IBOOST_MODE(1)}, ++ {EMAC_SGMII_LN_MIXER_CTRL_MODE0, ++ MIXER_LOADB_MODE(12) | MIXER_DATARATE_MODE(1)}, ++ {EMAC_SGMII_LN_VGA_INITVAL, VGA_THRESH_DFE(31)}, ++ {EMAC_SGMII_LN_SIGDET_ENABLES, ++ SIGDET_LP_BYP_PS0_TO_PS2 | SIGDET_FLT_BYP}, ++ {EMAC_SGMII_LN_SIGDET_CNTRL, SIGDET_LVL(8)}, ++ ++ {EMAC_SGMII_LN_SIGDET_DEGLITCH_CNTRL, SIGDET_DEGLITCH_CTRL(4)}, ++ {EMAC_SGMII_LN_RX_MISC_CNTRL0, 0}, ++ {EMAC_SGMII_LN_DRVR_LOGIC_CLKDIV, ++ DRVR_LOGIC_CLK_EN | DRVR_LOGIC_CLK_DIV(4)}, ++ ++ {EMAC_SGMII_LN_PARALLEL_RATE, PARALLEL_RATE_MODE0(1)}, ++ {EMAC_SGMII_LN_TX_BAND_MODE, BAND_MODE0(2)}, ++ {EMAC_SGMII_LN_RX_BAND, BAND_MODE0(3)}, ++ {EMAC_SGMII_LN_LANE_MODE, LANE_MODE(26)}, ++ {EMAC_SGMII_LN_RX_RCVR_PATH1_MODE0, CDR_PD_SEL_MODE0(3)}, ++ {EMAC_SGMII_LN_RSM_CONFIG, BYPASS_RSM_SAMP_CAL | BYPASS_RSM_DLL_CAL}, ++}; ++ ++static const struct emac_reg_write physical_coding_sublayer_programming[] = { ++ {EMAC_SGMII_PHY_POW_DWN_CTRL0, PWRDN_B}, ++ {EMAC_SGMII_PHY_CDR_CTRL0, CDR_MAX_CNT(15)}, ++ {EMAC_SGMII_PHY_TX_PWR_CTRL, 0}, ++ {EMAC_SGMII_PHY_LANE_CTRL1, L0_RX_EQUALIZE_ENABLE}, ++}; ++ ++int emac_sgmii_init_qdf2432(struct emac_adapter *adpt) ++{ ++ struct emac_phy *phy = &adpt->phy; ++ void __iomem *phy_regs = phy->base; ++ void __iomem *laned = phy->digital; ++ unsigned int i; ++ u32 lnstatus; ++ ++ /* PCS lane-x init */ ++ emac_reg_write_all(phy->base, physical_coding_sublayer_programming, ++ ARRAY_SIZE(physical_coding_sublayer_programming)); ++ ++ /* SGMII lane-x init */ ++ emac_reg_write_all(phy->digital, sgmii_laned, ARRAY_SIZE(sgmii_laned)); ++ ++ /* Power up PCS and start reset lane state machine */ ++ ++ writel(0, phy_regs + EMAC_SGMII_PHY_RESET_CTRL); ++ writel(1, laned + SGMII_LN_RSM_START); ++ ++ /* Wait for c_ready assertion */ ++ for (i = 0; i < SERDES_START_WAIT_TIMES; i++) { ++ lnstatus = readl(phy_regs + SGMII_PHY_LN_LANE_STATUS); ++ if (lnstatus & BIT(1)) ++ break; ++ usleep_range(100, 200); ++ } ++ ++ if (i == SERDES_START_WAIT_TIMES) { ++ netdev_err(adpt->netdev, "SGMII failed to start\n"); ++ return -EIO; ++ } ++ ++ /* Disable digital and SERDES loopback */ ++ writel(0, phy_regs + SGMII_PHY_LN_BIST_GEN0); ++ writel(0, phy_regs + SGMII_PHY_LN_BIST_GEN2); ++ writel(0, phy_regs + SGMII_PHY_LN_CDR_CTRL1); ++ ++ /* Mask out all the SGMII Interrupt */ ++ writel(0, phy_regs + EMAC_SGMII_PHY_INTERRUPT_MASK); ++ ++ return 0; ++} +diff --git a/drivers/net/ethernet/qualcomm/emac/emac-sgmii.c b/drivers/net/ethernet/qualcomm/emac/emac-sgmii.c +index 72fe343..0b25c46 100644 +--- a/drivers/net/ethernet/qualcomm/emac/emac-sgmii.c ++++ b/drivers/net/ethernet/qualcomm/emac/emac-sgmii.c +@@ -20,448 +20,33 @@ + #include "emac-mac.h" + #include "emac-sgmii.h" + +-/* EMAC_QSERDES register offsets */ +-#define EMAC_QSERDES_COM_SYS_CLK_CTRL 0x000000 +-#define EMAC_QSERDES_COM_PLL_CNTRL 0x000014 +-#define EMAC_QSERDES_COM_PLL_IP_SETI 0x000018 +-#define EMAC_QSERDES_COM_PLL_CP_SETI 0x000024 +-#define EMAC_QSERDES_COM_PLL_IP_SETP 0x000028 +-#define EMAC_QSERDES_COM_PLL_CP_SETP 0x00002c +-#define EMAC_QSERDES_COM_SYSCLK_EN_SEL 0x000038 +-#define EMAC_QSERDES_COM_RESETSM_CNTRL 0x000040 +-#define EMAC_QSERDES_COM_PLLLOCK_CMP1 0x000044 +-#define EMAC_QSERDES_COM_PLLLOCK_CMP2 0x000048 +-#define EMAC_QSERDES_COM_PLLLOCK_CMP3 0x00004c +-#define EMAC_QSERDES_COM_PLLLOCK_CMP_EN 0x000050 +-#define EMAC_QSERDES_COM_DEC_START1 0x000064 +-#define EMAC_QSERDES_COM_DIV_FRAC_START1 0x000098 +-#define EMAC_QSERDES_COM_DIV_FRAC_START2 0x00009c +-#define EMAC_QSERDES_COM_DIV_FRAC_START3 0x0000a0 +-#define EMAC_QSERDES_COM_DEC_START2 0x0000a4 +-#define EMAC_QSERDES_COM_PLL_CRCTRL 0x0000ac +-#define EMAC_QSERDES_COM_RESET_SM 0x0000bc +-#define EMAC_QSERDES_TX_BIST_MODE_LANENO 0x000100 +-#define EMAC_QSERDES_TX_TX_EMP_POST1_LVL 0x000108 +-#define EMAC_QSERDES_TX_TX_DRV_LVL 0x00010c +-#define EMAC_QSERDES_TX_LANE_MODE 0x000150 +-#define EMAC_QSERDES_TX_TRAN_DRVR_EMP_EN 0x000170 +-#define EMAC_QSERDES_RX_CDR_CONTROL 0x000200 +-#define EMAC_QSERDES_RX_CDR_CONTROL2 0x000210 +-#define EMAC_QSERDES_RX_RX_EQ_GAIN12 0x000230 +- + /* EMAC_SGMII register offsets */ +-#define EMAC_SGMII_PHY_SERDES_START 0x000000 +-#define EMAC_SGMII_PHY_CMN_PWR_CTRL 0x000004 +-#define EMAC_SGMII_PHY_RX_PWR_CTRL 0x000008 +-#define EMAC_SGMII_PHY_TX_PWR_CTRL 0x00000C +-#define EMAC_SGMII_PHY_LANE_CTRL1 0x000018 +-#define EMAC_SGMII_PHY_AUTONEG_CFG2 0x000048 +-#define EMAC_SGMII_PHY_CDR_CTRL0 0x000058 +-#define EMAC_SGMII_PHY_SPEED_CFG1 0x000074 +-#define EMAC_SGMII_PHY_POW_DWN_CTRL0 0x000080 +-#define EMAC_SGMII_PHY_RESET_CTRL 0x0000a8 +-#define EMAC_SGMII_PHY_IRQ_CMD 0x0000ac +-#define EMAC_SGMII_PHY_INTERRUPT_CLEAR 0x0000b0 +-#define EMAC_SGMII_PHY_INTERRUPT_MASK 0x0000b4 +-#define EMAC_SGMII_PHY_INTERRUPT_STATUS 0x0000b8 +-#define EMAC_SGMII_PHY_RX_CHK_STATUS 0x0000d4 +-#define EMAC_SGMII_PHY_AUTONEG0_STATUS 0x0000e0 +-#define EMAC_SGMII_PHY_AUTONEG1_STATUS 0x0000e4 +- +-/* EMAC_QSERDES_COM_PLL_IP_SETI */ +-#define PLL_IPSETI(x) ((x) & 0x3f) +- +-/* EMAC_QSERDES_COM_PLL_CP_SETI */ +-#define PLL_CPSETI(x) ((x) & 0xff) +- +-/* EMAC_QSERDES_COM_PLL_IP_SETP */ +-#define PLL_IPSETP(x) ((x) & 0x3f) +- +-/* EMAC_QSERDES_COM_PLL_CP_SETP */ +-#define PLL_CPSETP(x) ((x) & 0x1f) +- +-/* EMAC_QSERDES_COM_PLL_CRCTRL */ +-#define PLL_RCTRL(x) (((x) & 0xf) << 4) +-#define PLL_CCTRL(x) ((x) & 0xf) +- +-/* SGMII v2 PHY registers per lane */ +-#define EMAC_SGMII_PHY_LN_OFFSET 0x0400 +- +-/* SGMII v2 digital lane registers */ +-#define EMAC_SGMII_LN_DRVR_CTRL0 0x00C +-#define EMAC_SGMII_LN_DRVR_TAP_EN 0x018 +-#define EMAC_SGMII_LN_TX_MARGINING 0x01C +-#define EMAC_SGMII_LN_TX_PRE 0x020 +-#define EMAC_SGMII_LN_TX_POST 0x024 +-#define EMAC_SGMII_LN_TX_BAND_MODE 0x060 +-#define EMAC_SGMII_LN_LANE_MODE 0x064 +-#define EMAC_SGMII_LN_PARALLEL_RATE 0x078 +-#define EMAC_SGMII_LN_CML_CTRL_MODE0 0x0B8 +-#define EMAC_SGMII_LN_MIXER_CTRL_MODE0 0x0D0 +-#define EMAC_SGMII_LN_VGA_INITVAL 0x134 +-#define EMAC_SGMII_LN_UCDR_FO_GAIN_MODE0 0x17C +-#define EMAC_SGMII_LN_UCDR_SO_GAIN_MODE0 0x188 +-#define EMAC_SGMII_LN_UCDR_SO_CONFIG 0x194 +-#define EMAC_SGMII_LN_RX_BAND 0x19C +-#define EMAC_SGMII_LN_RX_RCVR_PATH1_MODE0 0x1B8 +-#define EMAC_SGMII_LN_RSM_CONFIG 0x1F0 +-#define EMAC_SGMII_LN_SIGDET_ENABLES 0x224 +-#define EMAC_SGMII_LN_SIGDET_CNTRL 0x228 +-#define EMAC_SGMII_LN_SIGDET_DEGLITCH_CNTRL 0x22C +-#define EMAC_SGMII_LN_RX_EN_SIGNAL 0x2A0 +-#define EMAC_SGMII_LN_RX_MISC_CNTRL0 0x2AC +-#define EMAC_SGMII_LN_DRVR_LOGIC_CLKDIV 0x2BC +- +-/* SGMII v2 digital lane register values */ +-#define UCDR_STEP_BY_TWO_MODE0 BIT(7) +-#define UCDR_xO_GAIN_MODE(x) ((x) & 0x7f) +-#define UCDR_ENABLE BIT(6) +-#define UCDR_SO_SATURATION(x) ((x) & 0x3f) +-#define SIGDET_LP_BYP_PS4 BIT(7) +-#define SIGDET_EN_PS0_TO_PS2 BIT(6) +-#define EN_ACCOUPLEVCM_SW_MUX BIT(5) +-#define EN_ACCOUPLEVCM_SW BIT(4) +-#define RX_SYNC_EN BIT(3) +-#define RXTERM_HIGHZ_PS5 BIT(2) +-#define SIGDET_EN_PS3 BIT(1) +-#define EN_ACCOUPLE_VCM_PS3 BIT(0) +-#define UFS_MODE BIT(5) +-#define TXVAL_VALID_INIT BIT(4) +-#define TXVAL_VALID_MUX BIT(3) +-#define TXVAL_VALID BIT(2) +-#define USB3P1_MODE BIT(1) +-#define KR_PCIGEN3_MODE BIT(0) +-#define PRE_EN BIT(3) +-#define POST_EN BIT(2) +-#define MAIN_EN_MUX BIT(1) +-#define MAIN_EN BIT(0) +-#define TX_MARGINING_MUX BIT(6) +-#define TX_MARGINING(x) ((x) & 0x3f) +-#define TX_PRE_MUX BIT(6) +-#define TX_PRE(x) ((x) & 0x3f) +-#define TX_POST_MUX BIT(6) +-#define TX_POST(x) ((x) & 0x3f) +-#define CML_GEAR_MODE(x) (((x) & 7) << 3) +-#define CML2CMOS_IBOOST_MODE(x) ((x) & 7) +-#define MIXER_LOADB_MODE(x) (((x) & 0xf) << 2) +-#define MIXER_DATARATE_MODE(x) ((x) & 3) +-#define VGA_THRESH_DFE(x) ((x) & 0x3f) +-#define SIGDET_LP_BYP_PS0_TO_PS2 BIT(5) +-#define SIGDET_LP_BYP_MUX BIT(4) +-#define SIGDET_LP_BYP BIT(3) +-#define SIGDET_EN_MUX BIT(2) +-#define SIGDET_EN BIT(1) +-#define SIGDET_FLT_BYP BIT(0) +-#define SIGDET_LVL(x) (((x) & 0xf) << 4) +-#define SIGDET_BW_CTRL(x) ((x) & 0xf) +-#define SIGDET_DEGLITCH_CTRL(x) (((x) & 0xf) << 1) +-#define SIGDET_DEGLITCH_BYP BIT(0) +-#define INVERT_PCS_RX_CLK BIT(7) +-#define PWM_EN BIT(6) +-#define RXBIAS_SEL(x) (((x) & 0x3) << 4) +-#define EBDAC_SIGN BIT(3) +-#define EDAC_SIGN BIT(2) +-#define EN_AUXTAP1SIGN_INVERT BIT(1) +-#define EN_DAC_CHOPPING BIT(0) +-#define DRVR_LOGIC_CLK_EN BIT(4) +-#define DRVR_LOGIC_CLK_DIV(x) ((x) & 0xf) +-#define PARALLEL_RATE_MODE2(x) (((x) & 0x3) << 4) +-#define PARALLEL_RATE_MODE1(x) (((x) & 0x3) << 2) +-#define PARALLEL_RATE_MODE0(x) ((x) & 0x3) +-#define BAND_MODE2(x) (((x) & 0x3) << 4) +-#define BAND_MODE1(x) (((x) & 0x3) << 2) +-#define BAND_MODE0(x) ((x) & 0x3) +-#define LANE_SYNC_MODE BIT(5) +-#define LANE_MODE(x) ((x) & 0x1f) +-#define CDR_PD_SEL_MODE0(x) (((x) & 0x3) << 5) +-#define EN_DLL_MODE0 BIT(4) +-#define EN_IQ_DCC_MODE0 BIT(3) +-#define EN_IQCAL_MODE0 BIT(2) +-#define EN_QPATH_MODE0 BIT(1) +-#define EN_EPATH_MODE0 BIT(0) +-#define FORCE_TSYNC_ACK BIT(7) +-#define FORCE_CMN_ACK BIT(6) +-#define FORCE_CMN_READY BIT(5) +-#define EN_RCLK_DEGLITCH BIT(4) +-#define BYPASS_RSM_CDR_RESET BIT(3) +-#define BYPASS_RSM_TSYNC BIT(2) +-#define BYPASS_RSM_SAMP_CAL BIT(1) +-#define BYPASS_RSM_DLL_CAL BIT(0) +- +-/* EMAC_QSERDES_COM_SYS_CLK_CTRL */ +-#define SYSCLK_CM BIT(4) +-#define SYSCLK_AC_COUPLE BIT(3) +- +-/* EMAC_QSERDES_COM_PLL_CNTRL */ +-#define OCP_EN BIT(5) +-#define PLL_DIV_FFEN BIT(2) +-#define PLL_DIV_ORD BIT(1) +- +-/* EMAC_QSERDES_COM_SYSCLK_EN_SEL */ +-#define SYSCLK_SEL_CMOS BIT(3) +- +-/* EMAC_QSERDES_COM_RESETSM_CNTRL */ +-#define FRQ_TUNE_MODE BIT(4) +- +-/* EMAC_QSERDES_COM_PLLLOCK_CMP_EN */ +-#define PLLLOCK_CMP_EN BIT(0) +- +-/* EMAC_QSERDES_COM_DEC_START1 */ +-#define DEC_START1_MUX BIT(7) +-#define DEC_START1(x) ((x) & 0x7f) +- +-/* EMAC_QSERDES_COM_DIV_FRAC_START1 * EMAC_QSERDES_COM_DIV_FRAC_START2 */ +-#define DIV_FRAC_START_MUX BIT(7) +-#define DIV_FRAC_START(x) ((x) & 0x7f) +- +-/* EMAC_QSERDES_COM_DIV_FRAC_START3 */ +-#define DIV_FRAC_START3_MUX BIT(4) +-#define DIV_FRAC_START3(x) ((x) & 0xf) +- +-/* EMAC_QSERDES_COM_DEC_START2 */ +-#define DEC_START2_MUX BIT(1) +-#define DEC_START2 BIT(0) +- +-/* EMAC_QSERDES_COM_RESET_SM */ +-#define READY BIT(5) +- +-/* EMAC_QSERDES_TX_TX_EMP_POST1_LVL */ +-#define TX_EMP_POST1_LVL_MUX BIT(5) +-#define TX_EMP_POST1_LVL(x) ((x) & 0x1f) +-#define TX_EMP_POST1_LVL_BMSK 0x1f +-#define TX_EMP_POST1_LVL_SHFT 0 +- +-/* EMAC_QSERDES_TX_TX_DRV_LVL */ +-#define TX_DRV_LVL_MUX BIT(4) +-#define TX_DRV_LVL(x) ((x) & 0xf) +- +-/* EMAC_QSERDES_TX_TRAN_DRVR_EMP_EN */ +-#define EMP_EN_MUX BIT(1) +-#define EMP_EN BIT(0) +- +-/* EMAC_QSERDES_RX_CDR_CONTROL & EMAC_QSERDES_RX_CDR_CONTROL2 */ +-#define HBW_PD_EN BIT(7) +-#define SECONDORDERENABLE BIT(6) +-#define FIRSTORDER_THRESH(x) (((x) & 0x7) << 3) +-#define SECONDORDERGAIN(x) ((x) & 0x7) +- +-/* EMAC_QSERDES_RX_RX_EQ_GAIN12 */ +-#define RX_EQ_GAIN2(x) (((x) & 0xf) << 4) +-#define RX_EQ_GAIN1(x) ((x) & 0xf) +- +-/* EMAC_SGMII_PHY_SERDES_START */ +-#define SERDES_START BIT(0) +- +-/* EMAC_SGMII_PHY_CMN_PWR_CTRL */ +-#define BIAS_EN BIT(6) +-#define PLL_EN BIT(5) +-#define SYSCLK_EN BIT(4) +-#define CLKBUF_L_EN BIT(3) +-#define PLL_TXCLK_EN BIT(1) +-#define PLL_RXCLK_EN BIT(0) +- +-/* EMAC_SGMII_PHY_RX_PWR_CTRL */ +-#define L0_RX_SIGDET_EN BIT(7) +-#define L0_RX_TERM_MODE(x) (((x) & 3) << 4) +-#define L0_RX_I_EN BIT(1) +- +-/* EMAC_SGMII_PHY_TX_PWR_CTRL */ +-#define L0_TX_EN BIT(5) +-#define L0_CLKBUF_EN BIT(4) +-#define L0_TRAN_BIAS_EN BIT(1) +- +-/* EMAC_SGMII_PHY_LANE_CTRL1 */ +-#define L0_RX_EQUALIZE_ENABLE BIT(6) +-#define L0_RESET_TSYNC_EN BIT(4) +-#define L0_DRV_LVL(x) ((x) & 0xf) +- +-/* EMAC_SGMII_PHY_AUTONEG_CFG2 */ ++#define EMAC_SGMII_PHY_AUTONEG_CFG2 0x0048 ++#define EMAC_SGMII_PHY_SPEED_CFG1 0x0074 ++#define EMAC_SGMII_PHY_IRQ_CMD 0x00ac ++#define EMAC_SGMII_PHY_INTERRUPT_CLEAR 0x00b0 ++#define EMAC_SGMII_PHY_INTERRUPT_STATUS 0x00b8 ++ + #define FORCE_AN_TX_CFG BIT(5) + #define FORCE_AN_RX_CFG BIT(4) + #define AN_ENABLE BIT(0) + +-/* EMAC_SGMII_PHY_SPEED_CFG1 */ + #define DUPLEX_MODE BIT(4) + #define SPDMODE_1000 BIT(1) + #define SPDMODE_100 BIT(0) + #define SPDMODE_10 0 +-#define SPDMODE_BMSK 3 +-#define SPDMODE_SHFT 0 +- +-/* EMAC_SGMII_PHY_POW_DWN_CTRL0 */ +-#define PWRDN_B BIT(0) +-#define CDR_MAX_CNT(x) ((x) & 0xff) +- +-/* EMAC_QSERDES_TX_BIST_MODE_LANENO */ +-#define BIST_LANE_NUMBER(x) (((x) & 3) << 5) +-#define BISTMODE(x) ((x) & 0x1f) +- +-/* EMAC_QSERDES_COM_PLLLOCK_CMPx */ +-#define PLLLOCK_CMP(x) ((x) & 0xff) + +-/* EMAC_SGMII_PHY_RESET_CTRL */ +-#define PHY_SW_RESET BIT(0) +- +-/* EMAC_SGMII_PHY_IRQ_CMD */ + #define IRQ_GLOBAL_CLEAR BIT(0) + +-/* EMAC_SGMII_PHY_INTERRUPT_MASK */ + #define DECODE_CODE_ERR BIT(7) + #define DECODE_DISP_ERR BIT(6) +-#define PLL_UNLOCK BIT(5) +-#define AN_ILLEGAL_TERM BIT(4) +-#define SYNC_FAIL BIT(3) +-#define AN_START BIT(2) +-#define AN_END BIT(1) +-#define AN_REQUEST BIT(0) + + #define SGMII_PHY_IRQ_CLR_WAIT_TIME 10 + +-#define SGMII_PHY_INTERRUPT_ERR (\ +- DECODE_CODE_ERR |\ +- DECODE_DISP_ERR) +- +-#define SGMII_ISR_AN_MASK (\ +- AN_REQUEST |\ +- AN_START |\ +- AN_END |\ +- AN_ILLEGAL_TERM |\ +- PLL_UNLOCK |\ +- SYNC_FAIL) +- +-#define SGMII_ISR_MASK (\ +- SGMII_PHY_INTERRUPT_ERR |\ +- SGMII_ISR_AN_MASK) +- +-/* SGMII TX_CONFIG */ +-#define TXCFG_LINK 0x8000 +-#define TXCFG_MODE_BMSK 0x1c00 +-#define TXCFG_1000_FULL 0x1800 +-#define TXCFG_100_FULL 0x1400 +-#define TXCFG_100_HALF 0x0400 +-#define TXCFG_10_FULL 0x1000 +-#define TXCFG_10_HALF 0x0000 ++#define SGMII_PHY_INTERRUPT_ERR (DECODE_CODE_ERR | DECODE_DISP_ERR) + + #define SERDES_START_WAIT_TIMES 100 + +-struct emac_reg_write { +- unsigned int offset; +- u32 val; +-}; +- +-static void emac_reg_write_all(void __iomem *base, +- const struct emac_reg_write *itr, size_t size) +-{ +- size_t i; +- +- for (i = 0; i < size; ++itr, ++i) +- writel(itr->val, base + itr->offset); +-} +- +-static const struct emac_reg_write physical_coding_sublayer_programming_v1[] = { +- {EMAC_SGMII_PHY_CDR_CTRL0, CDR_MAX_CNT(15)}, +- {EMAC_SGMII_PHY_POW_DWN_CTRL0, PWRDN_B}, +- {EMAC_SGMII_PHY_CMN_PWR_CTRL, +- BIAS_EN | SYSCLK_EN | CLKBUF_L_EN | PLL_TXCLK_EN | PLL_RXCLK_EN}, +- {EMAC_SGMII_PHY_TX_PWR_CTRL, L0_TX_EN | L0_CLKBUF_EN | L0_TRAN_BIAS_EN}, +- {EMAC_SGMII_PHY_RX_PWR_CTRL, +- L0_RX_SIGDET_EN | L0_RX_TERM_MODE(1) | L0_RX_I_EN}, +- {EMAC_SGMII_PHY_CMN_PWR_CTRL, +- BIAS_EN | PLL_EN | SYSCLK_EN | CLKBUF_L_EN | PLL_TXCLK_EN | +- PLL_RXCLK_EN}, +- {EMAC_SGMII_PHY_LANE_CTRL1, +- L0_RX_EQUALIZE_ENABLE | L0_RESET_TSYNC_EN | L0_DRV_LVL(15)}, +-}; +- +-static const struct emac_reg_write sysclk_refclk_setting[] = { +- {EMAC_QSERDES_COM_SYSCLK_EN_SEL, SYSCLK_SEL_CMOS}, +- {EMAC_QSERDES_COM_SYS_CLK_CTRL, SYSCLK_CM | SYSCLK_AC_COUPLE}, +-}; +- +-static const struct emac_reg_write pll_setting[] = { +- {EMAC_QSERDES_COM_PLL_IP_SETI, PLL_IPSETI(1)}, +- {EMAC_QSERDES_COM_PLL_CP_SETI, PLL_CPSETI(59)}, +- {EMAC_QSERDES_COM_PLL_IP_SETP, PLL_IPSETP(10)}, +- {EMAC_QSERDES_COM_PLL_CP_SETP, PLL_CPSETP(9)}, +- {EMAC_QSERDES_COM_PLL_CRCTRL, PLL_RCTRL(15) | PLL_CCTRL(11)}, +- {EMAC_QSERDES_COM_PLL_CNTRL, OCP_EN | PLL_DIV_FFEN | PLL_DIV_ORD}, +- {EMAC_QSERDES_COM_DEC_START1, DEC_START1_MUX | DEC_START1(2)}, +- {EMAC_QSERDES_COM_DEC_START2, DEC_START2_MUX | DEC_START2}, +- {EMAC_QSERDES_COM_DIV_FRAC_START1, +- DIV_FRAC_START_MUX | DIV_FRAC_START(85)}, +- {EMAC_QSERDES_COM_DIV_FRAC_START2, +- DIV_FRAC_START_MUX | DIV_FRAC_START(42)}, +- {EMAC_QSERDES_COM_DIV_FRAC_START3, +- DIV_FRAC_START3_MUX | DIV_FRAC_START3(3)}, +- {EMAC_QSERDES_COM_PLLLOCK_CMP1, PLLLOCK_CMP(43)}, +- {EMAC_QSERDES_COM_PLLLOCK_CMP2, PLLLOCK_CMP(104)}, +- {EMAC_QSERDES_COM_PLLLOCK_CMP3, PLLLOCK_CMP(0)}, +- {EMAC_QSERDES_COM_PLLLOCK_CMP_EN, PLLLOCK_CMP_EN}, +- {EMAC_QSERDES_COM_RESETSM_CNTRL, FRQ_TUNE_MODE}, +-}; +- +-static const struct emac_reg_write cdr_setting[] = { +- {EMAC_QSERDES_RX_CDR_CONTROL, +- SECONDORDERENABLE | FIRSTORDER_THRESH(3) | SECONDORDERGAIN(2)}, +- {EMAC_QSERDES_RX_CDR_CONTROL2, +- SECONDORDERENABLE | FIRSTORDER_THRESH(3) | SECONDORDERGAIN(4)}, +-}; +- +-static const struct emac_reg_write tx_rx_setting[] = { +- {EMAC_QSERDES_TX_BIST_MODE_LANENO, 0}, +- {EMAC_QSERDES_TX_TX_DRV_LVL, TX_DRV_LVL_MUX | TX_DRV_LVL(15)}, +- {EMAC_QSERDES_TX_TRAN_DRVR_EMP_EN, EMP_EN_MUX | EMP_EN}, +- {EMAC_QSERDES_TX_TX_EMP_POST1_LVL, +- TX_EMP_POST1_LVL_MUX | TX_EMP_POST1_LVL(1)}, +- {EMAC_QSERDES_RX_RX_EQ_GAIN12, RX_EQ_GAIN2(15) | RX_EQ_GAIN1(15)}, +- {EMAC_QSERDES_TX_LANE_MODE, LANE_MODE(8)}, +-}; +- +-static const struct emac_reg_write sgmii_v2_laned[] = { +- /* CDR Settings */ +- {EMAC_SGMII_LN_UCDR_FO_GAIN_MODE0, +- UCDR_STEP_BY_TWO_MODE0 | UCDR_xO_GAIN_MODE(10)}, +- {EMAC_SGMII_LN_UCDR_SO_GAIN_MODE0, UCDR_xO_GAIN_MODE(0)}, +- {EMAC_SGMII_LN_UCDR_SO_CONFIG, UCDR_ENABLE | UCDR_SO_SATURATION(12)}, +- +- /* TX/RX Settings */ +- {EMAC_SGMII_LN_RX_EN_SIGNAL, SIGDET_LP_BYP_PS4 | SIGDET_EN_PS0_TO_PS2}, +- +- {EMAC_SGMII_LN_DRVR_CTRL0, TXVAL_VALID_INIT | KR_PCIGEN3_MODE}, +- {EMAC_SGMII_LN_DRVR_TAP_EN, MAIN_EN}, +- {EMAC_SGMII_LN_TX_MARGINING, TX_MARGINING_MUX | TX_MARGINING(25)}, +- {EMAC_SGMII_LN_TX_PRE, TX_PRE_MUX}, +- {EMAC_SGMII_LN_TX_POST, TX_POST_MUX}, +- +- {EMAC_SGMII_LN_CML_CTRL_MODE0, +- CML_GEAR_MODE(1) | CML2CMOS_IBOOST_MODE(1)}, +- {EMAC_SGMII_LN_MIXER_CTRL_MODE0, +- MIXER_LOADB_MODE(12) | MIXER_DATARATE_MODE(1)}, +- {EMAC_SGMII_LN_VGA_INITVAL, VGA_THRESH_DFE(31)}, +- {EMAC_SGMII_LN_SIGDET_ENABLES, +- SIGDET_LP_BYP_PS0_TO_PS2 | SIGDET_FLT_BYP}, +- {EMAC_SGMII_LN_SIGDET_CNTRL, SIGDET_LVL(8)}, +- +- {EMAC_SGMII_LN_SIGDET_DEGLITCH_CNTRL, SIGDET_DEGLITCH_CTRL(4)}, +- {EMAC_SGMII_LN_RX_MISC_CNTRL0, 0}, +- {EMAC_SGMII_LN_DRVR_LOGIC_CLKDIV, +- DRVR_LOGIC_CLK_EN | DRVR_LOGIC_CLK_DIV(4)}, +- +- {EMAC_SGMII_LN_PARALLEL_RATE, PARALLEL_RATE_MODE0(1)}, +- {EMAC_SGMII_LN_TX_BAND_MODE, BAND_MODE0(2)}, +- {EMAC_SGMII_LN_RX_BAND, BAND_MODE0(3)}, +- {EMAC_SGMII_LN_LANE_MODE, LANE_MODE(26)}, +- {EMAC_SGMII_LN_RX_RCVR_PATH1_MODE0, CDR_PD_SEL_MODE0(3)}, +- {EMAC_SGMII_LN_RSM_CONFIG, BYPASS_RSM_SAMP_CAL | BYPASS_RSM_DLL_CAL}, +-}; +- +-static const struct emac_reg_write physical_coding_sublayer_programming_v2[] = { +- {EMAC_SGMII_PHY_POW_DWN_CTRL0, PWRDN_B}, +- {EMAC_SGMII_PHY_CDR_CTRL0, CDR_MAX_CNT(15)}, +- {EMAC_SGMII_PHY_TX_PWR_CTRL, 0}, +- {EMAC_SGMII_PHY_LANE_CTRL1, L0_RX_EQUALIZE_ENABLE}, +-}; +- + static int emac_sgmii_link_init(struct emac_adapter *adpt) + { + struct phy_device *phydev = adpt->phydev; +@@ -536,98 +121,6 @@ static int emac_sgmii_irq_clear(struct emac_adapter *adpt, u32 irq_bits) + return 0; + } + +-int emac_sgmii_init_v1(struct emac_adapter *adpt) +-{ +- struct emac_phy *phy = &adpt->phy; +- unsigned int i; +- int ret; +- +- ret = emac_sgmii_link_init(adpt); +- if (ret) +- return ret; +- +- emac_reg_write_all(phy->base, physical_coding_sublayer_programming_v1, +- ARRAY_SIZE(physical_coding_sublayer_programming_v1)); +- emac_reg_write_all(phy->base, sysclk_refclk_setting, +- ARRAY_SIZE(sysclk_refclk_setting)); +- emac_reg_write_all(phy->base, pll_setting, ARRAY_SIZE(pll_setting)); +- emac_reg_write_all(phy->base, cdr_setting, ARRAY_SIZE(cdr_setting)); +- emac_reg_write_all(phy->base, tx_rx_setting, +- ARRAY_SIZE(tx_rx_setting)); +- +- /* Power up the Ser/Des engine */ +- writel(SERDES_START, phy->base + EMAC_SGMII_PHY_SERDES_START); +- +- for (i = 0; i < SERDES_START_WAIT_TIMES; i++) { +- if (readl(phy->base + EMAC_QSERDES_COM_RESET_SM) & READY) +- break; +- usleep_range(100, 200); +- } +- +- if (i == SERDES_START_WAIT_TIMES) { +- netdev_err(adpt->netdev, "error: ser/des failed to start\n"); +- return -EIO; +- } +- /* Mask out all the SGMII Interrupt */ +- writel(0, phy->base + EMAC_SGMII_PHY_INTERRUPT_MASK); +- +- emac_sgmii_irq_clear(adpt, SGMII_PHY_INTERRUPT_ERR); +- +- return 0; +-} +- +-int emac_sgmii_init_v2(struct emac_adapter *adpt) +-{ +- struct emac_phy *phy = &adpt->phy; +- void __iomem *phy_regs = phy->base; +- void __iomem *laned = phy->digital; +- unsigned int i; +- u32 lnstatus; +- int ret; +- +- ret = emac_sgmii_link_init(adpt); +- if (ret) +- return ret; +- +- /* PCS lane-x init */ +- emac_reg_write_all(phy->base, physical_coding_sublayer_programming_v2, +- ARRAY_SIZE(physical_coding_sublayer_programming_v2)); +- +- /* SGMII lane-x init */ +- emac_reg_write_all(phy->digital, +- sgmii_v2_laned, ARRAY_SIZE(sgmii_v2_laned)); +- +- /* Power up PCS and start reset lane state machine */ +- +- writel(0, phy_regs + EMAC_SGMII_PHY_RESET_CTRL); +- writel(1, laned + SGMII_LN_RSM_START); +- +- /* Wait for c_ready assertion */ +- for (i = 0; i < SERDES_START_WAIT_TIMES; i++) { +- lnstatus = readl(phy_regs + SGMII_PHY_LN_LANE_STATUS); +- if (lnstatus & BIT(1)) +- break; +- usleep_range(100, 200); +- } +- +- if (i == SERDES_START_WAIT_TIMES) { +- netdev_err(adpt->netdev, "SGMII failed to start\n"); +- return -EIO; +- } +- +- /* Disable digital and SERDES loopback */ +- writel(0, phy_regs + SGMII_PHY_LN_BIST_GEN0); +- writel(0, phy_regs + SGMII_PHY_LN_BIST_GEN2); +- writel(0, phy_regs + SGMII_PHY_LN_CDR_CTRL1); +- +- /* Mask out all the SGMII Interrupt */ +- writel(0, phy_regs + EMAC_SGMII_PHY_INTERRUPT_MASK); +- +- emac_sgmii_irq_clear(adpt, SGMII_PHY_INTERRUPT_ERR); +- +- return 0; +-} +- + static void emac_sgmii_reset_prepare(struct emac_adapter *adpt) + { + struct emac_phy *phy = &adpt->phy; +@@ -651,16 +144,19 @@ void emac_sgmii_reset(struct emac_adapter *adpt) + { + int ret; + +- clk_set_rate(adpt->clk[EMAC_CLK_HIGH_SPEED], 19200000); + emac_sgmii_reset_prepare(adpt); + ++ ret = emac_sgmii_link_init(adpt); ++ if (ret) { ++ netdev_err(adpt->netdev, "unsupported link speed\n"); ++ return; ++ } ++ + ret = adpt->phy.initialize(adpt); + if (ret) + netdev_err(adpt->netdev, + "could not reinitialize internal PHY (error=%i)\n", + ret); +- +- clk_set_rate(adpt->clk[EMAC_CLK_HIGH_SPEED], 125000000); + } + + static int emac_sgmii_acpi_match(struct device *dev, void *data) +@@ -668,7 +164,7 @@ static int emac_sgmii_acpi_match(struct device *dev, void *data) + static const struct acpi_device_id match_table[] = { + { + .id = "QCOM8071", +- .driver_data = (kernel_ulong_t)emac_sgmii_init_v2, ++ .driver_data = (kernel_ulong_t)emac_sgmii_init_qdf2432, + }, + {} + }; +@@ -684,11 +180,11 @@ static int emac_sgmii_acpi_match(struct device *dev, void *data) + static const struct of_device_id emac_sgmii_dt_match[] = { + { + .compatible = "qcom,fsm9900-emac-sgmii", +- .data = emac_sgmii_init_v1, ++ .data = emac_sgmii_init_fsm9900, + }, + { + .compatible = "qcom,qdf2432-emac-sgmii", +- .data = emac_sgmii_init_v2, ++ .data = emac_sgmii_init_qdf2432, + }, + {} + }; +@@ -765,6 +261,8 @@ int emac_sgmii_config(struct platform_device *pdev, struct emac_adapter *adpt) + if (ret) + goto error; + ++ emac_sgmii_irq_clear(adpt, SGMII_PHY_INTERRUPT_ERR); ++ + /* We've remapped the addresses, so we don't need the device any + * more. of_find_device_by_node() says we should release it. + */ +diff --git a/drivers/net/ethernet/qualcomm/emac/emac-sgmii.h b/drivers/net/ethernet/qualcomm/emac/emac-sgmii.h +index ce79212..e2bef14 100644 +--- a/drivers/net/ethernet/qualcomm/emac/emac-sgmii.h ++++ b/drivers/net/ethernet/qualcomm/emac/emac-sgmii.h +@@ -16,9 +16,10 @@ + struct emac_adapter; + struct platform_device; + +-int emac_sgmii_init_v1(struct emac_adapter *adpt); +-int emac_sgmii_init_v2(struct emac_adapter *adpt); + int emac_sgmii_config(struct platform_device *pdev, struct emac_adapter *adpt); + void emac_sgmii_reset(struct emac_adapter *adpt); + ++int emac_sgmii_init_fsm9900(struct emac_adapter *adpt); ++int emac_sgmii_init_qdf2432(struct emac_adapter *adpt); ++ + #endif +-- +Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm +Technologies, Inc. Qualcomm Technologies, Inc. is a member of the Code Aurora +Forum, a Linux Foundation Collaborative Project. + + +From 332c9480bd5c0007839889a22b38ba656406b4b0 Mon Sep 17 00:00:00 2001 +From: Timur Tabi <timur at codeaurora.org> +Date: Thu, 8 Dec 2016 13:24:21 -0600 +Subject: [PATCH 16/17] net: qcom/emac: add support for the Qualcomm + Technologies QDF2400 + +The QDF2432 and the QDF2400 have slightly different internal PHYs, +so there are some programming differences. Some of the registers in +the QDF2400 have moved, and some registers require different values +during initialization. + +Because of the differences, and because HIDs are a scare resource, +the ACPI tables specify the hardware version in an _HRV property. +Version 1 is the QDF2432, and version 2 is the QDF2400. Any future +SOC that has the same internal PHY but different programming +requirements will be assigned the next available version number. + +Signed-off-by: Timur Tabi <timur at codeaurora.org> +Signed-off-by: David S. Miller <davem at davemloft.net> +(cherry picked from commit a51f4047232aeb4aee85268b351c7a84b83b36c5) +--- + drivers/net/ethernet/qualcomm/emac/Makefile | 3 +- + .../ethernet/qualcomm/emac/emac-sgmii-qdf2400.c | 217 +++++++++++++++++++++ + drivers/net/ethernet/qualcomm/emac/emac-sgmii.c | 33 +++- + drivers/net/ethernet/qualcomm/emac/emac-sgmii.h | 1 + + 4 files changed, 249 insertions(+), 5 deletions(-) + create mode 100644 drivers/net/ethernet/qualcomm/emac/emac-sgmii-qdf2400.c + +diff --git a/drivers/net/ethernet/qualcomm/emac/Makefile b/drivers/net/ethernet/qualcomm/emac/Makefile +index 204b787..7a66879 100644 +--- a/drivers/net/ethernet/qualcomm/emac/Makefile ++++ b/drivers/net/ethernet/qualcomm/emac/Makefile +@@ -5,4 +5,5 @@ + obj-$(CONFIG_QCOM_EMAC) += qcom-emac.o + + qcom-emac-objs := emac.o emac-mac.o emac-phy.o emac-sgmii.o \ +- emac-sgmii-fsm9900.o emac-sgmii-qdf2432.o ++ emac-sgmii-fsm9900.o emac-sgmii-qdf2432.o \ ++ emac-sgmii-qdf2400.o +diff --git a/drivers/net/ethernet/qualcomm/emac/emac-sgmii-qdf2400.c b/drivers/net/ethernet/qualcomm/emac/emac-sgmii-qdf2400.c +new file mode 100644 +index 0000000..5b84194 +--- /dev/null ++++ b/drivers/net/ethernet/qualcomm/emac/emac-sgmii-qdf2400.c +@@ -0,0 +1,217 @@ ++/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 and ++ * only version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that 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. ++ */ ++ ++/* Qualcomm Technologies, Inc. QDF2400 EMAC SGMII Controller driver. ++ */ ++ ++#include <linux/iopoll.h> ++#include "emac.h" ++ ++/* EMAC_SGMII register offsets */ ++#define EMAC_SGMII_PHY_TX_PWR_CTRL 0x000C ++#define EMAC_SGMII_PHY_LANE_CTRL1 0x0018 ++#define EMAC_SGMII_PHY_CDR_CTRL0 0x0058 ++#define EMAC_SGMII_PHY_POW_DWN_CTRL0 0x0080 ++#define EMAC_SGMII_PHY_RESET_CTRL 0x00a8 ++#define EMAC_SGMII_PHY_INTERRUPT_MASK 0x00b4 ++ ++/* SGMII digital lane registers */ ++#define EMAC_SGMII_LN_DRVR_CTRL0 0x000C ++#define EMAC_SGMII_LN_DRVR_TAP_EN 0x0018 ++#define EMAC_SGMII_LN_TX_MARGINING 0x001C ++#define EMAC_SGMII_LN_TX_PRE 0x0020 ++#define EMAC_SGMII_LN_TX_POST 0x0024 ++#define EMAC_SGMII_LN_TX_BAND_MODE 0x0060 ++#define EMAC_SGMII_LN_LANE_MODE 0x0064 ++#define EMAC_SGMII_LN_PARALLEL_RATE 0x007C ++#define EMAC_SGMII_LN_CML_CTRL_MODE0 0x00C0 ++#define EMAC_SGMII_LN_MIXER_CTRL_MODE0 0x00D8 ++#define EMAC_SGMII_LN_VGA_INITVAL 0x013C ++#define EMAC_SGMII_LN_UCDR_FO_GAIN_MODE0 0x0184 ++#define EMAC_SGMII_LN_UCDR_SO_GAIN_MODE0 0x0190 ++#define EMAC_SGMII_LN_UCDR_SO_CONFIG 0x019C ++#define EMAC_SGMII_LN_RX_BAND 0x01A4 ++#define EMAC_SGMII_LN_RX_RCVR_PATH1_MODE0 0x01C0 ++#define EMAC_SGMII_LN_RSM_CONFIG 0x01F8 ++#define EMAC_SGMII_LN_SIGDET_ENABLES 0x0230 ++#define EMAC_SGMII_LN_SIGDET_CNTRL 0x0234 ++#define EMAC_SGMII_LN_SIGDET_DEGLITCH_CNTRL 0x0238 ++#define EMAC_SGMII_LN_RX_EN_SIGNAL 0x02AC ++#define EMAC_SGMII_LN_RX_MISC_CNTRL0 0x02B8 ++#define EMAC_SGMII_LN_DRVR_LOGIC_CLKDIV 0x02C8 ++ ++/* SGMII digital lane register values */ ++#define UCDR_STEP_BY_TWO_MODE0 BIT(7) ++#define UCDR_xO_GAIN_MODE(x) ((x) & 0x7f) ++#define UCDR_ENABLE BIT(6) ++#define UCDR_SO_SATURATION(x) ((x) & 0x3f) ++ ++#define SIGDET_LP_BYP_PS4 BIT(7) ++#define SIGDET_EN_PS0_TO_PS2 BIT(6) ++ ++#define TXVAL_VALID_INIT BIT(4) ++#define KR_PCIGEN3_MODE BIT(0) ++ ++#define MAIN_EN BIT(0) ++ ++#define TX_MARGINING_MUX BIT(6) ++#define TX_MARGINING(x) ((x) & 0x3f) ++ ++#define TX_PRE_MUX BIT(6) ++ ++#define TX_POST_MUX BIT(6) ++ ++#define CML_GEAR_MODE(x) (((x) & 7) << 3) ++#define CML2CMOS_IBOOST_MODE(x) ((x) & 7) ++ ++#define MIXER_LOADB_MODE(x) (((x) & 0xf) << 2) ++#define MIXER_DATARATE_MODE(x) ((x) & 3) ++ ++#define VGA_THRESH_DFE(x) ((x) & 0x3f) ++ ++#define SIGDET_LP_BYP_PS0_TO_PS2 BIT(5) ++#define SIGDET_FLT_BYP BIT(0) ++ ++#define SIGDET_LVL(x) (((x) & 0xf) << 4) ++ ++#define SIGDET_DEGLITCH_CTRL(x) (((x) & 0xf) << 1) ++ ++#define INVERT_PCS_RX_CLK BIT(7) ++ ++#define DRVR_LOGIC_CLK_EN BIT(4) ++#define DRVR_LOGIC_CLK_DIV(x) ((x) & 0xf) ++ ++#define PARALLEL_RATE_MODE0(x) ((x) & 0x3) ++ ++#define BAND_MODE0(x) ((x) & 0x3) ++ ++#define LANE_MODE(x) ((x) & 0x1f) ++ ++#define CDR_PD_SEL_MODE0(x) (((x) & 0x3) << 5) ++#define EN_DLL_MODE0 BIT(4) ++#define EN_IQ_DCC_MODE0 BIT(3) ++#define EN_IQCAL_MODE0 BIT(2) ++ ++#define BYPASS_RSM_SAMP_CAL BIT(1) ++#define BYPASS_RSM_DLL_CAL BIT(0) ++ ++#define L0_RX_EQUALIZE_ENABLE BIT(6) ++ ++#define PWRDN_B BIT(0) ++ ++#define CDR_MAX_CNT(x) ((x) & 0xff) ++ ++#define SERDES_START_WAIT_TIMES 100 ++ ++struct emac_reg_write { ++ unsigned int offset; ++ u32 val; ++}; ++ ++static void emac_reg_write_all(void __iomem *base, ++ const struct emac_reg_write *itr, size_t size) ++{ ++ size_t i; ++ ++ for (i = 0; i < size; ++itr, ++i) ++ writel(itr->val, base + itr->offset); ++} ++ ++static const struct emac_reg_write sgmii_laned[] = { ++ /* CDR Settings */ ++ {EMAC_SGMII_LN_UCDR_FO_GAIN_MODE0, ++ UCDR_STEP_BY_TWO_MODE0 | UCDR_xO_GAIN_MODE(10)}, ++ {EMAC_SGMII_LN_UCDR_SO_GAIN_MODE0, UCDR_xO_GAIN_MODE(0)}, ++ {EMAC_SGMII_LN_UCDR_SO_CONFIG, UCDR_ENABLE | UCDR_SO_SATURATION(12)}, ++ ++ /* TX/RX Settings */ ++ {EMAC_SGMII_LN_RX_EN_SIGNAL, SIGDET_LP_BYP_PS4 | SIGDET_EN_PS0_TO_PS2}, ++ ++ {EMAC_SGMII_LN_DRVR_CTRL0, TXVAL_VALID_INIT | KR_PCIGEN3_MODE}, ++ {EMAC_SGMII_LN_DRVR_TAP_EN, MAIN_EN}, ++ {EMAC_SGMII_LN_TX_MARGINING, TX_MARGINING_MUX | TX_MARGINING(25)}, ++ {EMAC_SGMII_LN_TX_PRE, TX_PRE_MUX}, ++ {EMAC_SGMII_LN_TX_POST, TX_POST_MUX}, ++ ++ {EMAC_SGMII_LN_CML_CTRL_MODE0, ++ CML_GEAR_MODE(1) | CML2CMOS_IBOOST_MODE(1)}, ++ {EMAC_SGMII_LN_MIXER_CTRL_MODE0, ++ MIXER_LOADB_MODE(12) | MIXER_DATARATE_MODE(1)}, ++ {EMAC_SGMII_LN_VGA_INITVAL, VGA_THRESH_DFE(31)}, ++ {EMAC_SGMII_LN_SIGDET_ENABLES, ++ SIGDET_LP_BYP_PS0_TO_PS2 | SIGDET_FLT_BYP}, ++ {EMAC_SGMII_LN_SIGDET_CNTRL, SIGDET_LVL(8)}, ++ ++ {EMAC_SGMII_LN_SIGDET_DEGLITCH_CNTRL, SIGDET_DEGLITCH_CTRL(4)}, ++ {EMAC_SGMII_LN_RX_MISC_CNTRL0, INVERT_PCS_RX_CLK}, ++ {EMAC_SGMII_LN_DRVR_LOGIC_CLKDIV, ++ DRVR_LOGIC_CLK_EN | DRVR_LOGIC_CLK_DIV(4)}, ++ ++ {EMAC_SGMII_LN_PARALLEL_RATE, PARALLEL_RATE_MODE0(1)}, ++ {EMAC_SGMII_LN_TX_BAND_MODE, BAND_MODE0(1)}, ++ {EMAC_SGMII_LN_RX_BAND, BAND_MODE0(2)}, ++ {EMAC_SGMII_LN_LANE_MODE, LANE_MODE(26)}, ++ {EMAC_SGMII_LN_RX_RCVR_PATH1_MODE0, CDR_PD_SEL_MODE0(2) | ++ EN_DLL_MODE0 | EN_IQ_DCC_MODE0 | EN_IQCAL_MODE0}, ++ {EMAC_SGMII_LN_RSM_CONFIG, BYPASS_RSM_SAMP_CAL | BYPASS_RSM_DLL_CAL}, ++}; ++ ++static const struct emac_reg_write physical_coding_sublayer_programming[] = { ++ {EMAC_SGMII_PHY_POW_DWN_CTRL0, PWRDN_B}, ++ {EMAC_SGMII_PHY_CDR_CTRL0, CDR_MAX_CNT(15)}, ++ {EMAC_SGMII_PHY_TX_PWR_CTRL, 0}, ++ {EMAC_SGMII_PHY_LANE_CTRL1, L0_RX_EQUALIZE_ENABLE}, ++}; ++ ++int emac_sgmii_init_qdf2400(struct emac_adapter *adpt) ++{ ++ struct emac_phy *phy = &adpt->phy; ++ void __iomem *phy_regs = phy->base; ++ void __iomem *laned = phy->digital; ++ unsigned int i; ++ u32 lnstatus; ++ ++ /* PCS lane-x init */ ++ emac_reg_write_all(phy->base, physical_coding_sublayer_programming, ++ ARRAY_SIZE(physical_coding_sublayer_programming)); ++ ++ /* SGMII lane-x init */ ++ emac_reg_write_all(phy->digital, sgmii_laned, ARRAY_SIZE(sgmii_laned)); ++ ++ /* Power up PCS and start reset lane state machine */ ++ ++ writel(0, phy_regs + EMAC_SGMII_PHY_RESET_CTRL); ++ writel(1, laned + SGMII_LN_RSM_START); ++ ++ /* Wait for c_ready assertion */ ++ for (i = 0; i < SERDES_START_WAIT_TIMES; i++) { ++ lnstatus = readl(phy_regs + SGMII_PHY_LN_LANE_STATUS); ++ if (lnstatus & BIT(1)) ++ break; ++ usleep_range(100, 200); ++ } ++ ++ if (i == SERDES_START_WAIT_TIMES) { ++ netdev_err(adpt->netdev, "SGMII failed to start\n"); ++ return -EIO; ++ } ++ ++ /* Disable digital and SERDES loopback */ ++ writel(0, phy_regs + SGMII_PHY_LN_BIST_GEN0); ++ writel(0, phy_regs + SGMII_PHY_LN_BIST_GEN2); ++ writel(0, phy_regs + SGMII_PHY_LN_CDR_CTRL1); ++ ++ /* Mask out all the SGMII Interrupt */ ++ writel(0, phy_regs + EMAC_SGMII_PHY_INTERRUPT_MASK); ++ ++ return 0; ++} +diff --git a/drivers/net/ethernet/qualcomm/emac/emac-sgmii.c b/drivers/net/ethernet/qualcomm/emac/emac-sgmii.c +index 0b25c46..bf722a9 100644 +--- a/drivers/net/ethernet/qualcomm/emac/emac-sgmii.c ++++ b/drivers/net/ethernet/qualcomm/emac/emac-sgmii.c +@@ -161,20 +161,45 @@ void emac_sgmii_reset(struct emac_adapter *adpt) + + static int emac_sgmii_acpi_match(struct device *dev, void *data) + { ++#ifdef CONFIG_ACPI + static const struct acpi_device_id match_table[] = { + { + .id = "QCOM8071", +- .driver_data = (kernel_ulong_t)emac_sgmii_init_qdf2432, + }, + {} + }; + const struct acpi_device_id *id = acpi_match_device(match_table, dev); + emac_sgmii_initialize *initialize = data; + +- if (id) +- *initialize = (emac_sgmii_initialize)id->driver_data; ++ if (id) { ++ acpi_handle handle = ACPI_HANDLE(dev); ++ unsigned long long hrv; ++ acpi_status status; ++ ++ status = acpi_evaluate_integer(handle, "_HRV", NULL, &hrv); ++ if (status) { ++ if (status == AE_NOT_FOUND) ++ /* Older versions of the QDF2432 ACPI tables do ++ * not have an _HRV property. ++ */ ++ hrv = 1; ++ else ++ /* Something is wrong with the tables */ ++ return 0; ++ } + +- return !!id; ++ switch (hrv) { ++ case 1: ++ *initialize = emac_sgmii_init_qdf2432; ++ return 1; ++ case 2: ++ *initialize = emac_sgmii_init_qdf2400; ++ return 1; ++ } ++ } ++#endif ++ ++ return 0; + } + + static const struct of_device_id emac_sgmii_dt_match[] = { +diff --git a/drivers/net/ethernet/qualcomm/emac/emac-sgmii.h b/drivers/net/ethernet/qualcomm/emac/emac-sgmii.h +index e2bef14..80ed3dc 100644 +--- a/drivers/net/ethernet/qualcomm/emac/emac-sgmii.h ++++ b/drivers/net/ethernet/qualcomm/emac/emac-sgmii.h +@@ -21,5 +21,6 @@ void emac_sgmii_reset(struct emac_adapter *adpt); + + int emac_sgmii_init_fsm9900(struct emac_adapter *adpt); + int emac_sgmii_init_qdf2432(struct emac_adapter *adpt); ++int emac_sgmii_init_qdf2400(struct emac_adapter *adpt); + + #endif +-- +Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm +Technologies, Inc. Qualcomm Technologies, Inc. is a member of the Code Aurora +Forum, a Linux Foundation Collaborative Project. + + +From 487f43557b866af3491ced8076ee32de12f33592 Mon Sep 17 00:00:00 2001 +From: Timur Tabi <timur at codeaurora.org> +Date: Tue, 13 Dec 2016 17:49:02 -0600 +Subject: [PATCH 17/17] net: qcom/emac: don't try to claim clocks on ACPI + systems + +On ACPI systems, clocks are not available to drivers directly. They are +handled exclusively by ACPI and/or firmware, so there is no clock driver. +Calls to clk_get() always fail, so we should not even attempt to claim +any clocks on ACPI systems. + +Signed-off-by: Timur Tabi <timur at codeaurora.org> +Reviewed-by: Florian Fainelli <f.fainelli at gmail.com> +Signed-off-by: David S. Miller <davem at davemloft.net> +(cherry picked from commit 026acd5f47340382844f0af73516cf7ae6cdc876) +--- + drivers/net/ethernet/qualcomm/emac/emac.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/drivers/net/ethernet/qualcomm/emac/emac.c b/drivers/net/ethernet/qualcomm/emac/emac.c +index 57b35ae..e2f6d1b 100644 +--- a/drivers/net/ethernet/qualcomm/emac/emac.c ++++ b/drivers/net/ethernet/qualcomm/emac/emac.c +@@ -467,6 +467,12 @@ static int emac_clks_phase1_init(struct platform_device *pdev, + { + int ret; + ++ /* On ACPI platforms, clocks are controlled by firmware and/or ++ * ACPI, not by drivers. ++ */ ++ if (has_acpi_companion(&pdev->dev)) ++ return 0; ++ + ret = emac_clks_get(pdev, adpt); + if (ret) + return ret; +@@ -492,6 +498,9 @@ static int emac_clks_phase2_init(struct platform_device *pdev, + { + int ret; + ++ if (has_acpi_companion(&pdev->dev)) ++ return 0; ++ + ret = clk_set_rate(adpt->clk[EMAC_CLK_TX], 125000000); + if (ret) + return ret; +-- +Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm +Technologies, Inc. Qualcomm Technologies, Inc. is a member of the Code Aurora +Forum, a Linux Foundation Collaborative Project. + diff --git a/SOURCES/config-centos-sig b/SOURCES/config-centos-sig index b284ad1..281aaa1 100644 --- a/SOURCES/config-centos-sig +++ b/SOURCES/config-centos-sig @@ -11,6 +11,9 @@ CONFIG_I2C_DESIGNWARE_PLATFORM=m CONFIG_I2C_DESIGNWARE_PCI=m CONFIG_THUNDER_NIC_RGX=m CONFIG_MLX5_CORE_EN=y +CONFIG_NET_VENDOR_QUALCOMM=y +CONFIG_QCOM_EMAC=m +# CONFIG_QCA7000 is not set CONFIG_LEDS_PCA955X=m CONFIG_LEDS_PCA963X=m CONFIG_GPIO_DWAPB=m diff --git a/SPECS/kernel-aarch64.spec b/SPECS/kernel-aarch64.spec index d3e15b1..605055e 100644 --- a/SPECS/kernel-aarch64.spec +++ b/SPECS/kernel-aarch64.spec @@ -12,7 +12,7 @@ Summary: The Linux kernel %define rpmversion 4.5.0 %define pkgrelease 15.2.1.el7 -%define centupdate 27.el7 +%define centupdate 28.el7 # allow pkg_release to have configurable %{?dist} tag %define specrelease %%SPECRELEASE%% @@ -375,6 +375,7 @@ Patch2003: 0001-thunderx-nic-updates.patch #Patch4000: 4000-arm64-Define-Qualcomm-Technologies-ARMv8-CPU.patch #Patch4001: 4001-arm64-Workaround-Falkor-E1003.patch #Patch4002: 4002-arm64-Workaround-Falkor-E1009.patch +Patch4003: 4003-net-qcom-emac.patch # empty final patch to facilitate testing of kernel patches Patch999999: linux-kernel-test.patch @@ -757,6 +758,7 @@ git am %{PATCH2003} #git am %{PATCH4000} #git am %{PATCH4001} #git am %{PATCH4002} +git am %{PATCH4003} # Any further pre-build tree manipulations happen here. @@ -1530,6 +1532,10 @@ fi %kernel_variant_files %{with_debug} kernel-debug debug %changelog +* Thu Jul 13 2017 Brian Stinson <brian at bstinson.com> [4.5.0-28.el7] +- Add QDF2400 EMAC on-board Ethernet support +- Thanks to Christopher Covington https://lists.centos.org/pipermail/arm-dev/2016-December/002476.html + * Wed Jul 05 2017 Jim Perrin <jperrin at centos.org> [4.5.0.27.el7] - Add kernel patches 1036-1039 for APM hardware support -- 1.8.3.1