diff options
Diffstat (limited to 'drivers/net/ethernet/stmicro')
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/Kconfig | 14 | ||||
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/Makefile | 3 | ||||
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/altr_tse_pcs.c | 274 | ||||
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/altr_tse_pcs.h | 36 | ||||
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/common.h | 19 | ||||
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c | 165 | ||||
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c | 147 | ||||
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/dwmac1000.h | 86 | ||||
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c | 147 | ||||
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/dwmac4.h | 43 | ||||
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c | 96 | ||||
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/stmmac.h | 1 | ||||
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c | 60 | ||||
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 51 | ||||
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.h | 159 | ||||
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c | 17 |
16 files changed, 1085 insertions, 233 deletions
diff --git a/drivers/net/ethernet/stmicro/stmmac/Kconfig b/drivers/net/ethernet/stmicro/stmmac/Kconfig index cec147d1d34f..8f06a6621ab1 100644 --- a/drivers/net/ethernet/stmicro/stmmac/Kconfig +++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig @@ -40,7 +40,7 @@ config DWMAC_GENERIC config DWMAC_IPQ806X tristate "QCA IPQ806x DWMAC support" default ARCH_QCOM - depends on OF + depends on OF && (ARCH_QCOM || COMPILE_TEST) select MFD_SYSCON help Support for QCA IPQ806X DWMAC Ethernet. @@ -53,7 +53,7 @@ config DWMAC_IPQ806X config DWMAC_LPC18XX tristate "NXP LPC18xx/43xx DWMAC support" default ARCH_LPC18XX - depends on OF + depends on OF && (ARCH_LPC18XX || COMPILE_TEST) select MFD_SYSCON ---help--- Support for NXP LPC18xx/43xx DWMAC Ethernet. @@ -61,7 +61,7 @@ config DWMAC_LPC18XX config DWMAC_MESON tristate "Amlogic Meson dwmac support" default ARCH_MESON - depends on OF + depends on OF && (ARCH_MESON || COMPILE_TEST) help Support for Ethernet controller on Amlogic Meson SoCs. @@ -72,7 +72,7 @@ config DWMAC_MESON config DWMAC_ROCKCHIP tristate "Rockchip dwmac support" default ARCH_ROCKCHIP - depends on OF + depends on OF && (ARCH_ROCKCHIP || COMPILE_TEST) select MFD_SYSCON help Support for Ethernet controller on Rockchip RK3288 SoC. @@ -83,7 +83,7 @@ config DWMAC_ROCKCHIP config DWMAC_SOCFPGA tristate "SOCFPGA dwmac support" default ARCH_SOCFPGA - depends on OF + depends on OF && (ARCH_SOCFPGA || COMPILE_TEST) select MFD_SYSCON help Support for ethernet controller on Altera SOCFPGA @@ -95,7 +95,7 @@ config DWMAC_SOCFPGA config DWMAC_STI tristate "STi GMAC support" default ARCH_STI - depends on OF + depends on OF && (ARCH_STI || COMPILE_TEST) select MFD_SYSCON ---help--- Support for ethernet controller on STi SOCs. @@ -107,7 +107,7 @@ config DWMAC_STI config DWMAC_SUNXI tristate "Allwinner GMAC support" default ARCH_SUNXI - depends on OF + depends on OF && (ARCH_SUNXI || COMPILE_TEST) ---help--- Support for Allwinner A20/A31 GMAC ethernet controllers. diff --git a/drivers/net/ethernet/stmicro/stmmac/Makefile b/drivers/net/ethernet/stmicro/stmmac/Makefile index 0fb362d5a722..44b630cd1755 100644 --- a/drivers/net/ethernet/stmicro/stmmac/Makefile +++ b/drivers/net/ethernet/stmicro/stmmac/Makefile @@ -11,11 +11,12 @@ obj-$(CONFIG_DWMAC_IPQ806X) += dwmac-ipq806x.o obj-$(CONFIG_DWMAC_LPC18XX) += dwmac-lpc18xx.o obj-$(CONFIG_DWMAC_MESON) += dwmac-meson.o obj-$(CONFIG_DWMAC_ROCKCHIP) += dwmac-rk.o -obj-$(CONFIG_DWMAC_SOCFPGA) += dwmac-socfpga.o +obj-$(CONFIG_DWMAC_SOCFPGA) += dwmac-altr-socfpga.o obj-$(CONFIG_DWMAC_STI) += dwmac-sti.o obj-$(CONFIG_DWMAC_SUNXI) += dwmac-sunxi.o obj-$(CONFIG_DWMAC_GENERIC) += dwmac-generic.o stmmac-platform-objs:= stmmac_platform.o +dwmac-altr-socfpga-objs := altr_tse_pcs.o dwmac-socfpga.o obj-$(CONFIG_STMMAC_PCI) += stmmac-pci.o stmmac-pci-objs:= stmmac_pci.o diff --git a/drivers/net/ethernet/stmicro/stmmac/altr_tse_pcs.c b/drivers/net/ethernet/stmicro/stmmac/altr_tse_pcs.c new file mode 100644 index 000000000000..2920e2ee3864 --- /dev/null +++ b/drivers/net/ethernet/stmicro/stmmac/altr_tse_pcs.c @@ -0,0 +1,274 @@ +/* Copyright Altera Corporation (C) 2016. 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, + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Author: Tien Hock Loh <thloh@altera.com> + */ + +#include <linux/mfd/syscon.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_net.h> +#include <linux/phy.h> +#include <linux/regmap.h> +#include <linux/reset.h> +#include <linux/stmmac.h> + +#include "stmmac.h" +#include "stmmac_platform.h" +#include "altr_tse_pcs.h" + +#define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_GMII_MII 0 +#define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RGMII BIT(1) +#define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RMII BIT(2) +#define SYSMGR_EMACGRP_CTRL_PHYSEL_WIDTH 2 +#define SYSMGR_EMACGRP_CTRL_PHYSEL_MASK GENMASK(1, 0) + +#define TSE_PCS_CONTROL_AN_EN_MASK BIT(12) +#define TSE_PCS_CONTROL_REG 0x00 +#define TSE_PCS_CONTROL_RESTART_AN_MASK BIT(9) +#define TSE_PCS_IF_MODE_REG 0x28 +#define TSE_PCS_LINK_TIMER_0_REG 0x24 +#define TSE_PCS_LINK_TIMER_1_REG 0x26 +#define TSE_PCS_SIZE 0x40 +#define TSE_PCS_STATUS_AN_COMPLETED_MASK BIT(5) +#define TSE_PCS_STATUS_LINK_MASK 0x0004 +#define TSE_PCS_STATUS_REG 0x02 +#define TSE_PCS_SGMII_SPEED_1000 BIT(3) +#define TSE_PCS_SGMII_SPEED_100 BIT(2) +#define TSE_PCS_SGMII_SPEED_10 0x0 +#define TSE_PCS_SW_RST_MASK 0x8000 +#define TSE_PCS_PARTNER_ABILITY_REG 0x0A +#define TSE_PCS_PARTNER_DUPLEX_FULL 0x1000 +#define TSE_PCS_PARTNER_DUPLEX_HALF 0x0000 +#define TSE_PCS_PARTNER_DUPLEX_MASK 0x1000 +#define TSE_PCS_PARTNER_SPEED_MASK GENMASK(11, 10) +#define TSE_PCS_PARTNER_SPEED_1000 BIT(11) +#define TSE_PCS_PARTNER_SPEED_100 BIT(10) +#define TSE_PCS_PARTNER_SPEED_10 0x0000 +#define TSE_PCS_PARTNER_SPEED_1000 BIT(11) +#define TSE_PCS_PARTNER_SPEED_100 BIT(10) +#define TSE_PCS_PARTNER_SPEED_10 0x0000 +#define TSE_PCS_SGMII_SPEED_MASK GENMASK(3, 2) +#define TSE_PCS_SGMII_LINK_TIMER_0 0x0D40 +#define TSE_PCS_SGMII_LINK_TIMER_1 0x0003 +#define TSE_PCS_SW_RESET_TIMEOUT 100 +#define TSE_PCS_USE_SGMII_AN_MASK BIT(2) +#define TSE_PCS_USE_SGMII_ENA BIT(1) + +#define SGMII_ADAPTER_CTRL_REG 0x00 +#define SGMII_ADAPTER_DISABLE 0x0001 +#define SGMII_ADAPTER_ENABLE 0x0000 + +#define AUTONEGO_LINK_TIMER 20 + +static int tse_pcs_reset(void __iomem *base, struct tse_pcs *pcs) +{ + int counter = 0; + u16 val; + + val = readw(base + TSE_PCS_CONTROL_REG); + val |= TSE_PCS_SW_RST_MASK; + writew(val, base + TSE_PCS_CONTROL_REG); + + while (counter < TSE_PCS_SW_RESET_TIMEOUT) { + val = readw(base + TSE_PCS_CONTROL_REG); + val &= TSE_PCS_SW_RST_MASK; + if (val == 0) + break; + counter++; + udelay(1); + } + if (counter >= TSE_PCS_SW_RESET_TIMEOUT) { + dev_err(pcs->dev, "PCS could not get out of sw reset\n"); + return -ETIMEDOUT; + } + + return 0; +} + +int tse_pcs_init(void __iomem *base, struct tse_pcs *pcs) +{ + int ret = 0; + + writew(TSE_PCS_USE_SGMII_ENA, base + TSE_PCS_IF_MODE_REG); + + writew(TSE_PCS_SGMII_LINK_TIMER_0, base + TSE_PCS_LINK_TIMER_0_REG); + writew(TSE_PCS_SGMII_LINK_TIMER_1, base + TSE_PCS_LINK_TIMER_1_REG); + + ret = tse_pcs_reset(base, pcs); + if (ret == 0) + writew(SGMII_ADAPTER_ENABLE, + pcs->sgmii_adapter_base + SGMII_ADAPTER_CTRL_REG); + + return ret; +} + +static void pcs_link_timer_callback(unsigned long data) +{ + u16 val = 0; + struct tse_pcs *pcs = (struct tse_pcs *)data; + void __iomem *tse_pcs_base = pcs->tse_pcs_base; + void __iomem *sgmii_adapter_base = pcs->sgmii_adapter_base; + + val = readw(tse_pcs_base + TSE_PCS_STATUS_REG); + val &= TSE_PCS_STATUS_LINK_MASK; + + if (val != 0) { + dev_dbg(pcs->dev, "Adapter: Link is established\n"); + writew(SGMII_ADAPTER_ENABLE, + sgmii_adapter_base + SGMII_ADAPTER_CTRL_REG); + } else { + mod_timer(&pcs->aneg_link_timer, jiffies + + msecs_to_jiffies(AUTONEGO_LINK_TIMER)); + } +} + +static void auto_nego_timer_callback(unsigned long data) +{ + u16 val = 0; + u16 speed = 0; + u16 duplex = 0; + struct tse_pcs *pcs = (struct tse_pcs *)data; + void __iomem *tse_pcs_base = pcs->tse_pcs_base; + void __iomem *sgmii_adapter_base = pcs->sgmii_adapter_base; + + val = readw(tse_pcs_base + TSE_PCS_STATUS_REG); + val &= TSE_PCS_STATUS_AN_COMPLETED_MASK; + + if (val != 0) { + dev_dbg(pcs->dev, "Adapter: Auto Negotiation is completed\n"); + val = readw(tse_pcs_base + TSE_PCS_PARTNER_ABILITY_REG); + speed = val & TSE_PCS_PARTNER_SPEED_MASK; + duplex = val & TSE_PCS_PARTNER_DUPLEX_MASK; + + if (speed == TSE_PCS_PARTNER_SPEED_10 && + duplex == TSE_PCS_PARTNER_DUPLEX_FULL) + dev_dbg(pcs->dev, + "Adapter: Link Partner is Up - 10/Full\n"); + else if (speed == TSE_PCS_PARTNER_SPEED_100 && + duplex == TSE_PCS_PARTNER_DUPLEX_FULL) + dev_dbg(pcs->dev, + "Adapter: Link Partner is Up - 100/Full\n"); + else if (speed == TSE_PCS_PARTNER_SPEED_1000 && + duplex == TSE_PCS_PARTNER_DUPLEX_FULL) + dev_dbg(pcs->dev, + "Adapter: Link Partner is Up - 1000/Full\n"); + else if (speed == TSE_PCS_PARTNER_SPEED_10 && + duplex == TSE_PCS_PARTNER_DUPLEX_HALF) + dev_err(pcs->dev, + "Adapter does not support Half Duplex\n"); + else if (speed == TSE_PCS_PARTNER_SPEED_100 && + duplex == TSE_PCS_PARTNER_DUPLEX_HALF) + dev_err(pcs->dev, + "Adapter does not support Half Duplex\n"); + else if (speed == TSE_PCS_PARTNER_SPEED_1000 && + duplex == TSE_PCS_PARTNER_DUPLEX_HALF) + dev_err(pcs->dev, + "Adapter does not support Half Duplex\n"); + else + dev_err(pcs->dev, + "Adapter: Invalid Partner Speed and Duplex\n"); + + if (duplex == TSE_PCS_PARTNER_DUPLEX_FULL && + (speed == TSE_PCS_PARTNER_SPEED_10 || + speed == TSE_PCS_PARTNER_SPEED_100 || + speed == TSE_PCS_PARTNER_SPEED_1000)) + writew(SGMII_ADAPTER_ENABLE, + sgmii_adapter_base + SGMII_ADAPTER_CTRL_REG); + } else { + val = readw(tse_pcs_base + TSE_PCS_CONTROL_REG); + val |= TSE_PCS_CONTROL_RESTART_AN_MASK; + writew(val, tse_pcs_base + TSE_PCS_CONTROL_REG); + + tse_pcs_reset(tse_pcs_base, pcs); + mod_timer(&pcs->aneg_link_timer, jiffies + + msecs_to_jiffies(AUTONEGO_LINK_TIMER)); + } +} + +static void aneg_link_timer_callback(unsigned long data) +{ + struct tse_pcs *pcs = (struct tse_pcs *)data; + + if (pcs->autoneg == AUTONEG_ENABLE) + auto_nego_timer_callback(data); + else if (pcs->autoneg == AUTONEG_DISABLE) + pcs_link_timer_callback(data); +} + +void tse_pcs_fix_mac_speed(struct tse_pcs *pcs, struct phy_device *phy_dev, + unsigned int speed) +{ + void __iomem *tse_pcs_base = pcs->tse_pcs_base; + void __iomem *sgmii_adapter_base = pcs->sgmii_adapter_base; + u32 val; + + writew(SGMII_ADAPTER_ENABLE, + sgmii_adapter_base + SGMII_ADAPTER_CTRL_REG); + + pcs->autoneg = phy_dev->autoneg; + + if (phy_dev->autoneg == AUTONEG_ENABLE) { + val = readw(tse_pcs_base + TSE_PCS_CONTROL_REG); + val |= TSE_PCS_CONTROL_AN_EN_MASK; + writew(val, tse_pcs_base + TSE_PCS_CONTROL_REG); + + val = readw(tse_pcs_base + TSE_PCS_IF_MODE_REG); + val |= TSE_PCS_USE_SGMII_AN_MASK; + writew(val, tse_pcs_base + TSE_PCS_IF_MODE_REG); + + val = readw(tse_pcs_base + TSE_PCS_CONTROL_REG); + val |= TSE_PCS_CONTROL_RESTART_AN_MASK; + + tse_pcs_reset(tse_pcs_base, pcs); + + setup_timer(&pcs->aneg_link_timer, + aneg_link_timer_callback, (unsigned long)pcs); + mod_timer(&pcs->aneg_link_timer, jiffies + + msecs_to_jiffies(AUTONEGO_LINK_TIMER)); + } else if (phy_dev->autoneg == AUTONEG_DISABLE) { + val = readw(tse_pcs_base + TSE_PCS_CONTROL_REG); + val &= ~TSE_PCS_CONTROL_AN_EN_MASK; + writew(val, tse_pcs_base + TSE_PCS_CONTROL_REG); + + val = readw(tse_pcs_base + TSE_PCS_IF_MODE_REG); + val &= ~TSE_PCS_USE_SGMII_AN_MASK; + writew(val, tse_pcs_base + TSE_PCS_IF_MODE_REG); + + val = readw(tse_pcs_base + TSE_PCS_IF_MODE_REG); + val &= ~TSE_PCS_SGMII_SPEED_MASK; + + switch (speed) { + case 1000: + val |= TSE_PCS_SGMII_SPEED_1000; + break; + case 100: + val |= TSE_PCS_SGMII_SPEED_100; + break; + case 10: + val |= TSE_PCS_SGMII_SPEED_10; + break; + default: + return; + } + writew(val, tse_pcs_base + TSE_PCS_IF_MODE_REG); + + tse_pcs_reset(tse_pcs_base, pcs); + + setup_timer(&pcs->aneg_link_timer, + aneg_link_timer_callback, (unsigned long)pcs); + mod_timer(&pcs->aneg_link_timer, jiffies + + msecs_to_jiffies(AUTONEGO_LINK_TIMER)); + } +} diff --git a/drivers/net/ethernet/stmicro/stmmac/altr_tse_pcs.h b/drivers/net/ethernet/stmicro/stmmac/altr_tse_pcs.h new file mode 100644 index 000000000000..2f5882450b06 --- /dev/null +++ b/drivers/net/ethernet/stmicro/stmmac/altr_tse_pcs.h @@ -0,0 +1,36 @@ +/* Copyright Altera Corporation (C) 2016. 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, + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Author: Tien Hock Loh <thloh@altera.com> + */ + +#ifndef __TSE_PCS_H__ +#define __TSE_PCS_H__ + +#include <linux/phy.h> +#include <linux/timer.h> + +struct tse_pcs { + struct device *dev; + void __iomem *tse_pcs_base; + void __iomem *sgmii_adapter_base; + struct timer_list aneg_link_timer; + int autoneg; +}; + +int tse_pcs_init(void __iomem *base, struct tse_pcs *pcs); +void tse_pcs_fix_mac_speed(struct tse_pcs *pcs, struct phy_device *phy_dev, + unsigned int speed); + +#endif /* __TSE_PCS_H__ */ diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index fc60368df2e7..2533b91f1421 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -232,6 +232,11 @@ struct stmmac_extra_stats { #define DMA_HW_FEAT_ACTPHYIF 0x70000000 /* Active/selected PHY iface */ #define DEFAULT_DMA_PBL 8 +/* PCS status and mask defines */ +#define PCS_ANE_IRQ BIT(2) /* PCS Auto-Negotiation */ +#define PCS_LINK_IRQ BIT(1) /* PCS Link */ +#define PCS_RGSMIIIS_IRQ BIT(0) /* RGMII or SMII Interrupt */ + /* Max/Min RI Watchdog Timer count value */ #define MAX_DMA_RIWT 0xff #define MIN_DMA_RIWT 0x20 @@ -272,9 +277,6 @@ enum dma_irq_status { #define CORE_IRQ_RX_PATH_IN_LPI_MODE (1 << 2) #define CORE_IRQ_RX_PATH_EXIT_LPI_MODE (1 << 3) -#define CORE_PCS_ANE_COMPLETE (1 << 5) -#define CORE_PCS_LINK_STATUS (1 << 6) -#define CORE_RGMII_IRQ (1 << 7) #define CORE_IRQ_MTL_RX_OVERFLOW BIT(8) /* Physical Coding Sublayer */ @@ -469,9 +471,12 @@ struct stmmac_ops { void (*reset_eee_mode)(struct mac_device_info *hw); void (*set_eee_timer)(struct mac_device_info *hw, int ls, int tw); void (*set_eee_pls)(struct mac_device_info *hw, int link); - void (*ctrl_ane)(struct mac_device_info *hw, bool restart); - void (*get_adv)(struct mac_device_info *hw, struct rgmii_adv *adv); void (*debug)(void __iomem *ioaddr, struct stmmac_extra_stats *x); + /* PCS calls */ + void (*pcs_ctrl_ane)(void __iomem *ioaddr, bool ane, bool srgmi_ral, + bool loopback); + void (*pcs_rane)(void __iomem *ioaddr, bool restart); + void (*pcs_get_adv_lp)(void __iomem *ioaddr, struct rgmii_adv *adv); }; /* PTP and HW Timer helpers */ @@ -524,6 +529,9 @@ struct mac_device_info { int unicast_filter_entries; int mcast_bits_log2; unsigned int rx_csum; + unsigned int pcs; + unsigned int pmt; + unsigned int ps; }; struct mac_device_info *dwmac1000_setup(void __iomem *ioaddr, int mcbins, @@ -546,6 +554,7 @@ void stmmac_dwmac4_get_mac_addr(void __iomem *ioaddr, unsigned char *addr, void stmmac_dwmac4_set_mac(void __iomem *ioaddr, bool enable); void dwmac_dma_flush_tx_fifo(void __iomem *ioaddr); + extern const struct stmmac_mode_ops ring_mode_ops; extern const struct stmmac_mode_ops chain_mode_ops; extern const struct stmmac_desc_ops dwmac4_desc_ops; diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c index 0cd3ecff768b..92105916ef40 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c @@ -46,6 +46,7 @@ struct rk_priv_data { struct platform_device *pdev; int phy_iface; struct regulator *regulator; + bool suspended; const struct rk_gmac_ops *ops; bool clk_enabled; @@ -72,6 +73,122 @@ struct rk_priv_data { #define GRF_BIT(nr) (BIT(nr) | BIT(nr+16)) #define GRF_CLR_BIT(nr) (BIT(nr+16)) +#define RK3228_GRF_MAC_CON0 0x0900 +#define RK3228_GRF_MAC_CON1 0x0904 + +/* RK3228_GRF_MAC_CON0 */ +#define RK3228_GMAC_CLK_RX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 7) +#define RK3228_GMAC_CLK_TX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 0) + +/* RK3228_GRF_MAC_CON1 */ +#define RK3228_GMAC_PHY_INTF_SEL_RGMII \ + (GRF_BIT(4) | GRF_CLR_BIT(5) | GRF_CLR_BIT(6)) +#define RK3228_GMAC_PHY_INTF_SEL_RMII \ + (GRF_CLR_BIT(4) | GRF_CLR_BIT(5) | GRF_BIT(6)) +#define RK3228_GMAC_FLOW_CTRL GRF_BIT(3) +#define RK3228_GMAC_FLOW_CTRL_CLR GRF_CLR_BIT(3) +#define RK3228_GMAC_SPEED_10M GRF_CLR_BIT(2) +#define RK3228_GMAC_SPEED_100M GRF_BIT(2) +#define RK3228_GMAC_RMII_CLK_25M GRF_BIT(7) +#define RK3228_GMAC_RMII_CLK_2_5M GRF_CLR_BIT(7) +#define RK3228_GMAC_CLK_125M (GRF_CLR_BIT(8) | GRF_CLR_BIT(9)) +#define RK3228_GMAC_CLK_25M (GRF_BIT(8) | GRF_BIT(9)) +#define RK3228_GMAC_CLK_2_5M (GRF_CLR_BIT(8) | GRF_BIT(9)) +#define RK3228_GMAC_RMII_MODE GRF_BIT(10) +#define RK3228_GMAC_RMII_MODE_CLR GRF_CLR_BIT(10) +#define RK3228_GMAC_TXCLK_DLY_ENABLE GRF_BIT(0) +#define RK3228_GMAC_TXCLK_DLY_DISABLE GRF_CLR_BIT(0) +#define RK3228_GMAC_RXCLK_DLY_ENABLE GRF_BIT(1) +#define RK3228_GMAC_RXCLK_DLY_DISABLE GRF_CLR_BIT(1) + +static void rk3228_set_to_rgmii(struct rk_priv_data *bsp_priv, + int tx_delay, int rx_delay) +{ + struct device *dev = &bsp_priv->pdev->dev; + + if (IS_ERR(bsp_priv->grf)) { + dev_err(dev, "Missing rockchip,grf property\n"); + return; + } + + regmap_write(bsp_priv->grf, RK3228_GRF_MAC_CON1, + RK3228_GMAC_PHY_INTF_SEL_RGMII | + RK3228_GMAC_RMII_MODE_CLR | + RK3228_GMAC_RXCLK_DLY_ENABLE | + RK3228_GMAC_TXCLK_DLY_ENABLE); + + regmap_write(bsp_priv->grf, RK3228_GRF_MAC_CON0, + RK3228_GMAC_CLK_RX_DL_CFG(rx_delay) | + RK3228_GMAC_CLK_TX_DL_CFG(tx_delay)); +} + +static void rk3228_set_to_rmii(struct rk_priv_data *bsp_priv) +{ + struct device *dev = &bsp_priv->pdev->dev; + + if (IS_ERR(bsp_priv->grf)) { + dev_err(dev, "Missing rockchip,grf property\n"); + return; + } + + regmap_write(bsp_priv->grf, RK3228_GRF_MAC_CON1, + RK3228_GMAC_PHY_INTF_SEL_RMII | + RK3228_GMAC_RMII_MODE); + + /* set MAC to RMII mode */ + regmap_write(bsp_priv->grf, RK3228_GRF_MAC_CON1, GRF_BIT(11)); +} + +static void rk3228_set_rgmii_speed(struct rk_priv_data *bsp_priv, int speed) +{ + struct device *dev = &bsp_priv->pdev->dev; + + if (IS_ERR(bsp_priv->grf)) { + dev_err(dev, "Missing rockchip,grf property\n"); + return; + } + + if (speed == 10) + regmap_write(bsp_priv->grf, RK3228_GRF_MAC_CON1, + RK3228_GMAC_CLK_2_5M); + else if (speed == 100) + regmap_write(bsp_priv->grf, RK3228_GRF_MAC_CON1, + RK3228_GMAC_CLK_25M); + else if (speed == 1000) + regmap_write(bsp_priv->grf, RK3228_GRF_MAC_CON1, + RK3228_GMAC_CLK_125M); + else + dev_err(dev, "unknown speed value for RGMII! speed=%d", speed); +} + +static void rk3228_set_rmii_speed(struct rk_priv_data *bsp_priv, int speed) +{ + struct device *dev = &bsp_priv->pdev->dev; + + if (IS_ERR(bsp_priv->grf)) { + dev_err(dev, "Missing rockchip,grf property\n"); + return; + } + + if (speed == 10) + regmap_write(bsp_priv->grf, RK3228_GRF_MAC_CON1, + RK3228_GMAC_RMII_CLK_2_5M | + RK3228_GMAC_SPEED_10M); + else if (speed == 100) + regmap_write(bsp_priv->grf, RK3228_GRF_MAC_CON1, + RK3228_GMAC_RMII_CLK_25M | + RK3228_GMAC_SPEED_100M); + else + dev_err(dev, "unknown speed value for RMII! speed=%d", speed); +} + +static const struct rk_gmac_ops rk3228_ops = { + .set_to_rgmii = rk3228_set_to_rgmii, + .set_to_rmii = rk3228_set_to_rmii, + .set_rgmii_speed = rk3228_set_rgmii_speed, + .set_rmii_speed = rk3228_set_rmii_speed, +}; + #define RK3288_GRF_SOC_CON1 0x0248 #define RK3288_GRF_SOC_CON3 0x0250 @@ -529,9 +646,8 @@ static struct rk_priv_data *rk_gmac_setup(struct platform_device *pdev, return bsp_priv; } -static int rk_gmac_init(struct platform_device *pdev, void *priv) +static int rk_gmac_powerup(struct rk_priv_data *bsp_priv) { - struct rk_priv_data *bsp_priv = priv; int ret; ret = phy_power_on(bsp_priv, true); @@ -545,14 +661,50 @@ static int rk_gmac_init(struct platform_device *pdev, void *priv) return 0; } -static void rk_gmac_exit(struct platform_device *pdev, void *priv) +static void rk_gmac_powerdown(struct rk_priv_data *gmac) { - struct rk_priv_data *gmac = priv; - phy_power_on(gmac, false); gmac_clk_enable(gmac, false); } +static int rk_gmac_init(struct platform_device *pdev, void *priv) +{ + struct rk_priv_data *bsp_priv = priv; + + return rk_gmac_powerup(bsp_priv); +} + +static void rk_gmac_exit(struct platform_device *pdev, void *priv) +{ + struct rk_priv_data *bsp_priv = priv; + + rk_gmac_powerdown(bsp_priv); +} + +static void rk_gmac_suspend(struct platform_device *pdev, void *priv) +{ + struct rk_priv_data *bsp_priv = priv; + + /* Keep the PHY up if we use Wake-on-Lan. */ + if (device_may_wakeup(&pdev->dev)) + return; + + rk_gmac_powerdown(bsp_priv); + bsp_priv->suspended = true; +} + +static void rk_gmac_resume(struct platform_device *pdev, void *priv) +{ + struct rk_priv_data *bsp_priv = priv; + + /* The PHY was up for Wake-on-Lan. */ + if (!bsp_priv->suspended) + return; + + rk_gmac_powerup(bsp_priv); + bsp_priv->suspended = false; +} + static void rk_fix_speed(void *priv, unsigned int speed) { struct rk_priv_data *bsp_priv = priv; @@ -591,6 +743,8 @@ static int rk_gmac_probe(struct platform_device *pdev) plat_dat->init = rk_gmac_init; plat_dat->exit = rk_gmac_exit; plat_dat->fix_mac_speed = rk_fix_speed; + plat_dat->suspend = rk_gmac_suspend; + plat_dat->resume = rk_gmac_resume; plat_dat->bsp_priv = rk_gmac_setup(pdev, data); if (IS_ERR(plat_dat->bsp_priv)) @@ -604,6 +758,7 @@ static int rk_gmac_probe(struct platform_device *pdev) } static const struct of_device_id rk_gmac_dwmac_match[] = { + { .compatible = "rockchip,rk3228-gmac", .data = &rk3228_ops }, { .compatible = "rockchip,rk3288-gmac", .data = &rk3288_ops }, { .compatible = "rockchip,rk3368-gmac", .data = &rk3368_ops }, { } diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c index f13499fa1f58..bec6963ac71e 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c @@ -27,6 +27,11 @@ #include "stmmac.h" #include "stmmac_platform.h" +#include "altr_tse_pcs.h" + +#define SGMII_ADAPTER_CTRL_REG 0x00 +#define SGMII_ADAPTER_DISABLE 0x0001 + #define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_GMII_MII 0x0 #define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RGMII 0x1 #define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RMII 0x2 @@ -52,35 +57,46 @@ struct socfpga_dwmac { struct reset_control *stmmac_rst; void __iomem *splitter_base; bool f2h_ptp_ref_clk; + struct tse_pcs pcs; }; static void socfpga_dwmac_fix_mac_speed(void *priv, unsigned int speed) { struct socfpga_dwmac *dwmac = (struct socfpga_dwmac *)priv; void __iomem *splitter_base = dwmac->splitter_base; + void __iomem *tse_pcs_base = dwmac->pcs.tse_pcs_base; + void __iomem *sgmii_adapter_base = dwmac->pcs.sgmii_adapter_base; + struct device *dev = dwmac->dev; + struct net_device *ndev = dev_get_drvdata(dev); + struct phy_device *phy_dev = ndev->phydev; u32 val; - if (!splitter_base) - return; - - val = readl(splitter_base + EMAC_SPLITTER_CTRL_REG); - val &= ~EMAC_SPLITTER_CTRL_SPEED_MASK; - - switch (speed) { - case 1000: - val |= EMAC_SPLITTER_CTRL_SPEED_1000; - break; - case 100: - val |= EMAC_SPLITTER_CTRL_SPEED_100; - break; - case 10: - val |= EMAC_SPLITTER_CTRL_SPEED_10; - break; - default: - return; + if ((tse_pcs_base) && (sgmii_adapter_base)) + writew(SGMII_ADAPTER_DISABLE, + sgmii_adapter_base + SGMII_ADAPTER_CTRL_REG); + + if (splitter_base) { + val = readl(splitter_base + EMAC_SPLITTER_CTRL_REG); + val &= ~EMAC_SPLITTER_CTRL_SPEED_MASK; + + switch (speed) { + case 1000: + val |= EMAC_SPLITTER_CTRL_SPEED_1000; + break; + case 100: + val |= EMAC_SPLITTER_CTRL_SPEED_100; + break; + case 10: + val |= EMAC_SPLITTER_CTRL_SPEED_10; + break; + default: + return; + } + writel(val, splitter_base + EMAC_SPLITTER_CTRL_REG); } - writel(val, splitter_base + EMAC_SPLITTER_CTRL_REG); + if (tse_pcs_base && sgmii_adapter_base) + tse_pcs_fix_mac_speed(&dwmac->pcs, phy_dev, speed); } static int socfpga_dwmac_parse_data(struct socfpga_dwmac *dwmac, struct device *dev) @@ -88,9 +104,12 @@ static int socfpga_dwmac_parse_data(struct socfpga_dwmac *dwmac, struct device * struct device_node *np = dev->of_node; struct regmap *sys_mgr_base_addr; u32 reg_offset, reg_shift; - int ret; - struct device_node *np_splitter; + int ret, index; + struct device_node *np_splitter = NULL; + struct device_node *np_sgmii_adapter = NULL; struct resource res_splitter; + struct resource res_tse_pcs; + struct resource res_sgmii_adapter; dwmac->interface = of_get_phy_mode(np); @@ -116,7 +135,9 @@ static int socfpga_dwmac_parse_data(struct socfpga_dwmac *dwmac, struct device * np_splitter = of_parse_phandle(np, "altr,emac-splitter", 0); if (np_splitter) { - if (of_address_to_resource(np_splitter, 0, &res_splitter)) { + ret = of_address_to_resource(np_splitter, 0, &res_splitter); + of_node_put(np_splitter); + if (ret) { dev_info(dev, "Missing emac splitter address\n"); return -EINVAL; } @@ -128,12 +149,86 @@ static int socfpga_dwmac_parse_data(struct socfpga_dwmac *dwmac, struct device * } } + np_sgmii_adapter = of_parse_phandle(np, + "altr,gmii-to-sgmii-converter", 0); + if (np_sgmii_adapter) { + index = of_property_match_string(np_sgmii_adapter, "reg-names", + "hps_emac_interface_splitter_avalon_slave"); + + if (index >= 0) { + if (of_address_to_resource(np_sgmii_adapter, index, + &res_splitter)) { + dev_err(dev, + "%s: ERROR: missing emac splitter address\n", + __func__); + ret = -EINVAL; + goto err_node_put; + } + + dwmac->splitter_base = + devm_ioremap_resource(dev, &res_splitter); + + if (IS_ERR(dwmac->splitter_base)) { + ret = PTR_ERR(dwmac->splitter_base); + goto err_node_put; + } + } + + index = of_property_match_string(np_sgmii_adapter, "reg-names", + "gmii_to_sgmii_adapter_avalon_slave"); + + if (index >= 0) { + if (of_address_to_resource(np_sgmii_adapter, index, + &res_sgmii_adapter)) { + dev_err(dev, + "%s: ERROR: failed mapping adapter\n", + __func__); + ret = -EINVAL; + goto err_node_put; + } + + dwmac->pcs.sgmii_adapter_base = + devm_ioremap_resource(dev, &res_sgmii_adapter); + + if (IS_ERR(dwmac->pcs.sgmii_adapter_base)) { + ret = PTR_ERR(dwmac->pcs.sgmii_adapter_base); + goto err_node_put; + } + } + + index = of_property_match_string(np_sgmii_adapter, "reg-names", + "eth_tse_control_port"); + + if (index >= 0) { + if (of_address_to_resource(np_sgmii_adapter, index, + &res_tse_pcs)) { + dev_err(dev, + "%s: ERROR: failed mapping tse control port\n", + __func__); + ret = -EINVAL; + goto err_node_put; + } + + dwmac->pcs.tse_pcs_base = + devm_ioremap_resource(dev, &res_tse_pcs); + + if (IS_ERR(dwmac->pcs.tse_pcs_base)) { + ret = PTR_ERR(dwmac->pcs.tse_pcs_base); + goto err_node_put; + } + } + } dwmac->reg_offset = reg_offset; dwmac->reg_shift = reg_shift; dwmac->sys_mgr_base_addr = sys_mgr_base_addr; dwmac->dev = dev; + of_node_put(np_sgmii_adapter); return 0; + +err_node_put: + of_node_put(np_sgmii_adapter); + return ret; } static int socfpga_dwmac_set_phy_mode(struct socfpga_dwmac *dwmac) @@ -151,6 +246,7 @@ static int socfpga_dwmac_set_phy_mode(struct socfpga_dwmac *dwmac) break; case PHY_INTERFACE_MODE_MII: case PHY_INTERFACE_MODE_GMII: + case PHY_INTERFACE_MODE_SGMII: val = SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_GMII_MII; break; default: @@ -191,6 +287,12 @@ static int socfpga_dwmac_set_phy_mode(struct socfpga_dwmac *dwmac) */ if (dwmac->stmmac_rst) reset_control_deassert(dwmac->stmmac_rst); + if (phymode == PHY_INTERFACE_MODE_SGMII) { + if (tse_pcs_init(dwmac->pcs.tse_pcs_base, &dwmac->pcs) != 0) { + dev_err(dwmac->dev, "Unable to initialize TSE PCS"); + return -EINVAL; + } + } return 0; } @@ -225,6 +327,7 @@ static int socfpga_dwmac_probe(struct platform_device *pdev) plat_dat->fix_mac_speed = socfpga_dwmac_fix_mac_speed; ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); + if (!ret) { struct net_device *ndev = platform_get_drvdata(pdev); struct stmmac_priv *stpriv = netdev_priv(ndev); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h b/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h index b0593a4268ee..ff3e5ab39bd0 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h @@ -38,19 +38,26 @@ #define GMAC_WAKEUP_FILTER 0x00000028 /* Wake-up Frame Filter */ #define GMAC_INT_STATUS 0x00000038 /* interrupt status register */ -enum dwmac1000_irq_status { - lpiis_irq = 0x400, - time_stamp_irq = 0x0200, - mmc_rx_csum_offload_irq = 0x0080, - mmc_tx_irq = 0x0040, - mmc_rx_irq = 0x0020, - mmc_irq = 0x0010, - pmt_irq = 0x0008, - pcs_ane_irq = 0x0004, - pcs_link_irq = 0x0002, - rgmii_irq = 0x0001, -}; -#define GMAC_INT_MASK 0x0000003c /* interrupt mask register */ +#define GMAC_INT_STATUS_PMT BIT(3) +#define GMAC_INT_STATUS_MMCIS BIT(4) +#define GMAC_INT_STATUS_MMCRIS BIT(5) +#define GMAC_INT_STATUS_MMCTIS BIT(6) +#define GMAC_INT_STATUS_MMCCSUM BIT(7) +#define GMAC_INT_STATUS_TSTAMP BIT(9) +#define GMAC_INT_STATUS_LPIIS BIT(10) + +/* interrupt mask register */ +#define GMAC_INT_MASK 0x0000003c +#define GMAC_INT_DISABLE_RGMII BIT(0) +#define GMAC_INT_DISABLE_PCSLINK BIT(1) +#define GMAC_INT_DISABLE_PCSAN BIT(2) +#define GMAC_INT_DISABLE_PMT BIT(3) +#define GMAC_INT_DISABLE_TIMESTAMP BIT(9) +#define GMAC_INT_DISABLE_PCS (GMAC_INT_DISABLE_RGMII | \ + GMAC_INT_DISABLE_PCSLINK | \ + GMAC_INT_DISABLE_PCSAN) +#define GMAC_INT_DEFAULT_MASK (GMAC_INT_DISABLE_TIMESTAMP | \ + GMAC_INT_DISABLE_PCS) /* PMT Control and Status */ #define GMAC_PMT 0x0000002c @@ -90,42 +97,23 @@ enum power_event { (reg * 8)) #define GMAC_MAX_PERFECT_ADDRESSES 1 -/* PCS registers (AN/TBI/SGMII/RGMII) offset */ -#define GMAC_AN_CTRL 0x000000c0 /* AN control */ -#define GMAC_AN_STATUS 0x000000c4 /* AN status */ -#define GMAC_ANE_ADV 0x000000c8 /* Auto-Neg. Advertisement */ -#define GMAC_ANE_LPA 0x000000cc /* Auto-Neg. link partener ability */ -#define GMAC_ANE_EXP 0x000000d0 /* ANE expansion */ -#define GMAC_TBI 0x000000d4 /* TBI extend status */ -#define GMAC_S_R_GMII 0x000000d8 /* SGMII RGMII status */ - -/* AN Configuration defines */ -#define GMAC_AN_CTRL_RAN 0x00000200 /* Restart Auto-Negotiation */ -#define GMAC_AN_CTRL_ANE 0x00001000 /* Auto-Negotiation Enable */ -#define GMAC_AN_CTRL_ELE 0x00004000 /* External Loopback Enable */ -#define GMAC_AN_CTRL_ECD 0x00010000 /* Enable Comma Detect */ -#define GMAC_AN_CTRL_LR 0x00020000 /* Lock to Reference */ -#define GMAC_AN_CTRL_SGMRAL 0x00040000 /* SGMII RAL Control */ - -/* AN Status defines */ -#define GMAC_AN_STATUS_LS 0x00000004 /* Link Status 0:down 1:up */ -#define GMAC_AN_STATUS_ANA 0x00000008 /* Auto-Negotiation Ability */ -#define GMAC_AN_STATUS_ANC 0x00000020 /* Auto-Negotiation Complete */ -#define GMAC_AN_STATUS_ES 0x00000100 /* Extended Status */ - -/* Register 54 (SGMII/RGMII status register) */ -#define GMAC_S_R_GMII_LINK 0x8 -#define GMAC_S_R_GMII_SPEED 0x5 -#define GMAC_S_R_GMII_SPEED_SHIFT 0x1 -#define GMAC_S_R_GMII_MODE 0x1 -#define GMAC_S_R_GMII_SPEED_125 2 -#define GMAC_S_R_GMII_SPEED_25 1 - -/* Common ADV and LPA defines */ -#define GMAC_ANE_FD (1 << 5) -#define GMAC_ANE_HD (1 << 6) -#define GMAC_ANE_PSE (3 << 7) -#define GMAC_ANE_PSE_SHIFT 7 +#define GMAC_PCS_BASE 0x000000c0 /* PCS register base */ +#define GMAC_RGSMIIIS 0x000000d8 /* RGMII/SMII status */ + +/* SGMII/RGMII status register */ +#define GMAC_RGSMIIIS_LNKMODE BIT(0) +#define GMAC_RGSMIIIS_SPEED GENMASK(2, 1) +#define GMAC_RGSMIIIS_SPEED_SHIFT 1 +#define GMAC_RGSMIIIS_LNKSTS BIT(3) +#define GMAC_RGSMIIIS_JABTO BIT(4) +#define GMAC_RGSMIIIS_FALSECARDET BIT(5) +#define GMAC_RGSMIIIS_SMIDRXS BIT(16) +/* LNKMOD */ +#define GMAC_RGSMIIIS_LNKMOD_MASK 0x1 +/* LNKSPEED */ +#define GMAC_RGSMIIIS_SPEED_125 0x2 +#define GMAC_RGSMIIIS_SPEED_25 0x1 +#define GMAC_RGSMIIIS_SPEED_2_5 0x0 /* GMAC Configuration defines */ #define GMAC_CONTROL_2K 0x08000000 /* IEEE 802.3as 2K packets */ diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c index fb1eb578e34e..cbefe9e2207c 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c @@ -30,22 +30,48 @@ #include <linux/slab.h> #include <linux/ethtool.h> #include <asm/io.h> +#include "stmmac_pcs.h" #include "dwmac1000.h" static void dwmac1000_core_init(struct mac_device_info *hw, int mtu) { void __iomem *ioaddr = hw->pcsr; u32 value = readl(ioaddr + GMAC_CONTROL); + + /* Configure GMAC core */ value |= GMAC_CORE_INIT; + if (mtu > 1500) value |= GMAC_CONTROL_2K; if (mtu > 2000) value |= GMAC_CONTROL_JE; + if (hw->ps) { + value |= GMAC_CONTROL_TE; + + if (hw->ps == SPEED_1000) { + value &= ~GMAC_CONTROL_PS; + } else { + value |= GMAC_CONTROL_PS; + + if (hw->ps == SPEED_10) + value &= ~GMAC_CONTROL_FES; + else + value |= GMAC_CONTROL_FES; + } + } + writel(value, ioaddr + GMAC_CONTROL); /* Mask GMAC interrupts */ - writel(0x207, ioaddr + GMAC_INT_MASK); + value = GMAC_INT_DEFAULT_MASK; + + if (hw->pmt) + value &= ~GMAC_INT_DISABLE_PMT; + if (hw->pcs) + value &= ~GMAC_INT_DISABLE_PCS; + + writel(value, ioaddr + GMAC_INT_MASK); #ifdef STMMAC_VLAN_TAG_USED /* Tag detection without filtering */ @@ -241,6 +267,39 @@ static void dwmac1000_pmt(struct mac_device_info *hw, unsigned long mode) writel(pmt, ioaddr + GMAC_PMT); } +/* RGMII or SMII interface */ +static void dwmac1000_rgsmii(void __iomem *ioaddr, struct stmmac_extra_stats *x) +{ + u32 status; + + status = readl(ioaddr + GMAC_RGSMIIIS); + x->irq_rgmii_n++; + + /* Check the link status */ + if (status & GMAC_RGSMIIIS_LNKSTS) { + int speed_value; + + x->pcs_link = 1; + + speed_value = ((status & GMAC_RGSMIIIS_SPEED) >> + GMAC_RGSMIIIS_SPEED_SHIFT); + if (speed_value == GMAC_RGSMIIIS_SPEED_125) + x->pcs_speed = SPEED_1000; + else if (speed_value == GMAC_RGSMIIIS_SPEED_25) + x->pcs_speed = SPEED_100; + else + x->pcs_speed = SPEED_10; + + x->pcs_duplex = (status & GMAC_RGSMIIIS_LNKMOD_MASK); + + pr_info("Link is Up - %d/%s\n", (int)x->pcs_speed, + x->pcs_duplex ? "Full" : "Half"); + } else { + x->pcs_link = 0; + pr_info("Link is Down\n"); + } +} + static int dwmac1000_irq_status(struct mac_device_info *hw, struct stmmac_extra_stats *x) { @@ -249,19 +308,20 @@ static int dwmac1000_irq_status(struct mac_device_info *hw, int ret = 0; /* Not used events (e.g. MMC interrupts) are not handled. */ - if ((intr_status & mmc_tx_irq)) + if ((intr_status & GMAC_INT_STATUS_MMCTIS)) x->mmc_tx_irq_n++; - if (unlikely(intr_status & mmc_rx_irq)) + if (unlikely(intr_status & GMAC_INT_STATUS_MMCRIS)) x->mmc_rx_irq_n++; - if (unlikely(intr_status & mmc_rx_csum_offload_irq)) + if (unlikely(intr_status & GMAC_INT_STATUS_MMCCSUM)) x->mmc_rx_csum_offload_irq_n++; - if (unlikely(intr_status & pmt_irq)) { + if (unlikely(intr_status & GMAC_INT_DISABLE_PMT)) { /* clear the PMT bits 5 and 6 by reading the PMT status reg */ readl(ioaddr + GMAC_PMT); x->irq_receive_pmt_irq_n++; } - /* MAC trx/rx EEE LPI entry/exit interrupts */ - if (intr_status & lpiis_irq) { + + /* MAC tx/rx EEE LPI entry/exit interrupts */ + if (intr_status & GMAC_INT_STATUS_LPIIS) { /* Clean LPI interrupt by reading the Reg 12 */ ret = readl(ioaddr + LPI_CTRL_STATUS); @@ -275,36 +335,10 @@ static int dwmac1000_irq_status(struct mac_device_info *hw, x->irq_rx_path_exit_lpi_mode_n++; } - if ((intr_status & pcs_ane_irq) || (intr_status & pcs_link_irq)) { - readl(ioaddr + GMAC_AN_STATUS); - x->irq_pcs_ane_n++; - } - if (intr_status & rgmii_irq) { - u32 status = readl(ioaddr + GMAC_S_R_GMII); - x->irq_rgmii_n++; - - /* Save and dump the link status. */ - if (status & GMAC_S_R_GMII_LINK) { - int speed_value = (status & GMAC_S_R_GMII_SPEED) >> - GMAC_S_R_GMII_SPEED_SHIFT; - x->pcs_duplex = (status & GMAC_S_R_GMII_MODE); - - if (speed_value == GMAC_S_R_GMII_SPEED_125) - x->pcs_speed = SPEED_1000; - else if (speed_value == GMAC_S_R_GMII_SPEED_25) - x->pcs_speed = SPEED_100; - else - x->pcs_speed = SPEED_10; + dwmac_pcs_isr(ioaddr, GMAC_PCS_BASE, intr_status, x); - x->pcs_link = 1; - pr_debug("%s: Link is Up - %d/%s\n", __func__, - (int)x->pcs_speed, - x->pcs_duplex ? "Full" : "Half"); - } else { - x->pcs_link = 0; - pr_debug("%s: Link is Down\n", __func__); - } - } + if (intr_status & PCS_RGSMIIIS_IRQ) + dwmac1000_rgsmii(ioaddr, x); return ret; } @@ -363,38 +397,20 @@ static void dwmac1000_set_eee_timer(struct mac_device_info *hw, int ls, int tw) writel(value, ioaddr + LPI_TIMER_CTRL); } -static void dwmac1000_ctrl_ane(struct mac_device_info *hw, bool restart) +static void dwmac1000_ctrl_ane(void __iomem *ioaddr, bool ane, bool srgmi_ral, + bool loopback) { - void __iomem *ioaddr = hw->pcsr; - /* auto negotiation enable and External Loopback enable */ - u32 value = GMAC_AN_CTRL_ANE | GMAC_AN_CTRL_ELE; - - if (restart) - value |= GMAC_AN_CTRL_RAN; - - writel(value, ioaddr + GMAC_AN_CTRL); + dwmac_ctrl_ane(ioaddr, GMAC_PCS_BASE, ane, srgmi_ral, loopback); } -static void dwmac1000_get_adv(struct mac_device_info *hw, struct rgmii_adv *adv) +static void dwmac1000_rane(void __iomem *ioaddr, bool restart) { - void __iomem *ioaddr = hw->pcsr; - u32 value = readl(ioaddr + GMAC_ANE_ADV); - - if (value & GMAC_ANE_FD) - adv->duplex = DUPLEX_FULL; - if (value & GMAC_ANE_HD) - adv->duplex |= DUPLEX_HALF; - - adv->pause = (value & GMAC_ANE_PSE) >> GMAC_ANE_PSE_SHIFT; - - value = readl(ioaddr + GMAC_ANE_LPA); - - if (value & GMAC_ANE_FD) - adv->lp_duplex = DUPLEX_FULL; - if (value & GMAC_ANE_HD) - adv->lp_duplex = DUPLEX_HALF; + dwmac_rane(ioaddr, GMAC_PCS_BASE, restart); +} - adv->lp_pause = (value & GMAC_ANE_PSE) >> GMAC_ANE_PSE_SHIFT; +static void dwmac1000_get_adv_lp(void __iomem *ioaddr, struct rgmii_adv *adv) +{ + dwmac_get_adv_lp(ioaddr, GMAC_PCS_BASE, adv); } static void dwmac1000_debug(void __iomem *ioaddr, struct stmmac_extra_stats *x) @@ -485,9 +501,10 @@ static const struct stmmac_ops dwmac1000_ops = { .reset_eee_mode = dwmac1000_reset_eee_mode, .set_eee_timer = dwmac1000_set_eee_timer, .set_eee_pls = dwmac1000_set_eee_pls, - .ctrl_ane = dwmac1000_ctrl_ane, - .get_adv = dwmac1000_get_adv, .debug = dwmac1000_debug, + .pcs_ctrl_ane = dwmac1000_ctrl_ane, + .pcs_rane = dwmac1000_rane, + .pcs_get_adv_lp = dwmac1000_get_adv_lp, }; struct mac_device_info *dwmac1000_setup(void __iomem *ioaddr, int mcbins, diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4.h b/drivers/net/ethernet/stmicro/stmmac/dwmac4.h index bc50952a18e7..6f4f5ce25114 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4.h @@ -24,10 +24,8 @@ #define GMAC_QX_TX_FLOW_CTRL(x) (0x70 + x * 4) #define GMAC_INT_STATUS 0x000000b0 #define GMAC_INT_EN 0x000000b4 -#define GMAC_AN_CTRL 0x000000e0 -#define GMAC_AN_STATUS 0x000000e4 -#define GMAC_AN_ADV 0x000000e8 -#define GMAC_AN_LPA 0x000000ec +#define GMAC_PCS_BASE 0x000000e0 +#define GMAC_PHYIF_CONTROL_STATUS 0x000000f8 #define GMAC_PMT 0x000000c0 #define GMAC_VERSION 0x00000110 #define GMAC_DEBUG 0x00000114 @@ -54,9 +52,18 @@ #define GMAC_TX_FLOW_CTRL_PT_SHIFT 16 /* MAC Interrupt bitmap*/ +#define GMAC_INT_RGSMIIS BIT(0) +#define GMAC_INT_PCS_LINK BIT(1) +#define GMAC_INT_PCS_ANE BIT(2) +#define GMAC_INT_PCS_PHYIS BIT(3) #define GMAC_INT_PMT_EN BIT(4) #define GMAC_INT_LPI_EN BIT(5) +#define GMAC_PCS_IRQ_DEFAULT (GMAC_INT_RGSMIIS | GMAC_INT_PCS_LINK | \ + GMAC_INT_PCS_ANE) + +#define GMAC_INT_DEFAULT_MASK GMAC_INT_PMT_EN + enum dwmac4_irq_status { time_stamp_irq = 0x00001000, mmc_rx_csum_offload_irq = 0x00000800, @@ -64,19 +71,8 @@ enum dwmac4_irq_status { mmc_rx_irq = 0x00000200, mmc_irq = 0x00000100, pmt_irq = 0x00000010, - pcs_ane_irq = 0x00000004, - pcs_link_irq = 0x00000002, }; -/* MAC Auto-Neg bitmap*/ -#define GMAC_AN_CTRL_RAN BIT(9) -#define GMAC_AN_CTRL_ANE BIT(12) -#define GMAC_AN_CTRL_ELE BIT(14) -#define GMAC_AN_FD BIT(5) -#define GMAC_AN_HD BIT(6) -#define GMAC_AN_PSE_MASK GENMASK(8, 7) -#define GMAC_AN_PSE_SHIFT 7 - /* MAC PMT bitmap */ enum power_event { pointer_reset = 0x80000000, @@ -250,6 +246,23 @@ enum power_event { #define MTL_DEBUG_RRCSTS_FLUSH 3 #define MTL_DEBUG_RWCSTS BIT(0) +/* SGMII/RGMII status register */ +#define GMAC_PHYIF_CTRLSTATUS_TC BIT(0) +#define GMAC_PHYIF_CTRLSTATUS_LUD BIT(1) +#define GMAC_PHYIF_CTRLSTATUS_SMIDRXS BIT(4) +#define GMAC_PHYIF_CTRLSTATUS_LNKMOD BIT(16) +#define GMAC_PHYIF_CTRLSTATUS_SPEED GENMASK(18, 17) +#define GMAC_PHYIF_CTRLSTATUS_SPEED_SHIFT 17 +#define GMAC_PHYIF_CTRLSTATUS_LNKSTS BIT(19) +#define GMAC_PHYIF_CTRLSTATUS_JABTO BIT(20) +#define GMAC_PHYIF_CTRLSTATUS_FALSECARDET BIT(21) +/* LNKMOD */ +#define GMAC_PHYIF_CTRLSTATUS_LNKMOD_MASK 0x1 +/* LNKSPEED */ +#define GMAC_PHYIF_CTRLSTATUS_SPEED_125 0x2 +#define GMAC_PHYIF_CTRLSTATUS_SPEED_25 0x1 +#define GMAC_PHYIF_CTRLSTATUS_SPEED_2_5 0x0 + extern const struct stmmac_dma_ops dwmac4_dma_ops; extern const struct stmmac_dma_ops dwmac410_dma_ops; #endif /* __DWMAC4_H__ */ diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c index 44da877d2483..df5580dcdfed 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c @@ -17,6 +17,7 @@ #include <linux/slab.h> #include <linux/ethtool.h> #include <linux/io.h> +#include "stmmac_pcs.h" #include "dwmac4.h" static void dwmac4_core_init(struct mac_device_info *hw, int mtu) @@ -31,10 +32,31 @@ static void dwmac4_core_init(struct mac_device_info *hw, int mtu) if (mtu > 2000) value |= GMAC_CONFIG_JE; + if (hw->ps) { + value |= GMAC_CONFIG_TE; + + if (hw->ps == SPEED_1000) { + value &= ~GMAC_CONFIG_PS; + } else { + value |= GMAC_CONFIG_PS; + + if (hw->ps == SPEED_10) + value &= ~GMAC_CONFIG_FES; + else + value |= GMAC_CONFIG_FES; + } + } + writel(value, ioaddr + GMAC_CONFIG); /* Mask GMAC interrupts */ - writel(GMAC_INT_PMT_EN, ioaddr + GMAC_INT_EN); + value = GMAC_INT_DEFAULT_MASK; + if (hw->pmt) + value |= GMAC_INT_PMT_EN; + if (hw->pcs) + value |= GMAC_PCS_IRQ_DEFAULT; + + writel(value, ioaddr + GMAC_INT_EN); } static void dwmac4_dump_regs(struct mac_device_info *hw) @@ -190,39 +212,53 @@ static void dwmac4_flow_ctrl(struct mac_device_info *hw, unsigned int duplex, } } -static void dwmac4_ctrl_ane(struct mac_device_info *hw, bool restart) +static void dwmac4_ctrl_ane(void __iomem *ioaddr, bool ane, bool srgmi_ral, + bool loopback) { - void __iomem *ioaddr = hw->pcsr; - - /* auto negotiation enable and External Loopback enable */ - u32 value = GMAC_AN_CTRL_ANE | GMAC_AN_CTRL_ELE; + dwmac_ctrl_ane(ioaddr, GMAC_PCS_BASE, ane, srgmi_ral, loopback); +} - if (restart) - value |= GMAC_AN_CTRL_RAN; +static void dwmac4_rane(void __iomem *ioaddr, bool restart) +{ + dwmac_rane(ioaddr, GMAC_PCS_BASE, restart); +} - writel(value, ioaddr + GMAC_AN_CTRL); +static void dwmac4_get_adv_lp(void __iomem *ioaddr, struct rgmii_adv *adv) +{ + dwmac_get_adv_lp(ioaddr, GMAC_PCS_BASE, adv); } -static void dwmac4_get_adv(struct mac_device_info *hw, struct rgmii_adv *adv) +/* RGMII or SMII interface */ +static void dwmac4_phystatus(void __iomem *ioaddr, struct stmmac_extra_stats *x) { - void __iomem *ioaddr = hw->pcsr; - u32 value = readl(ioaddr + GMAC_AN_ADV); + u32 status; - if (value & GMAC_AN_FD) - adv->duplex = DUPLEX_FULL; - if (value & GMAC_AN_HD) - adv->duplex |= DUPLEX_HALF; + status = readl(ioaddr + GMAC_PHYIF_CONTROL_STATUS); + x->irq_rgmii_n++; - adv->pause = (value & GMAC_AN_PSE_MASK) >> GMAC_AN_PSE_SHIFT; + /* Check the link status */ + if (status & GMAC_PHYIF_CTRLSTATUS_LNKSTS) { + int speed_value; - value = readl(ioaddr + GMAC_AN_LPA); + x->pcs_link = 1; + + speed_value = ((status & GMAC_PHYIF_CTRLSTATUS_SPEED) >> + GMAC_PHYIF_CTRLSTATUS_SPEED_SHIFT); + if (speed_value == GMAC_PHYIF_CTRLSTATUS_SPEED_125) + x->pcs_speed = SPEED_1000; + else if (speed_value == GMAC_PHYIF_CTRLSTATUS_SPEED_25) + x->pcs_speed = SPEED_100; + else + x->pcs_speed = SPEED_10; - if (value & GMAC_AN_FD) - adv->lp_duplex = DUPLEX_FULL; - if (value & GMAC_AN_HD) - adv->lp_duplex = DUPLEX_HALF; + x->pcs_duplex = (status & GMAC_PHYIF_CTRLSTATUS_LNKMOD_MASK); - adv->lp_pause = (value & GMAC_AN_PSE_MASK) >> GMAC_AN_PSE_SHIFT; + pr_info("Link is Up - %d/%s\n", (int)x->pcs_speed, + x->pcs_duplex ? "Full" : "Half"); + } else { + x->pcs_link = 0; + pr_info("Link is Down\n"); + } } static int dwmac4_irq_status(struct mac_device_info *hw, @@ -248,11 +284,6 @@ static int dwmac4_irq_status(struct mac_device_info *hw, x->irq_receive_pmt_irq_n++; } - if ((intr_status & pcs_ane_irq) || (intr_status & pcs_link_irq)) { - readl(ioaddr + GMAC_AN_STATUS); - x->irq_pcs_ane_n++; - } - mtl_int_qx_status = readl(ioaddr + MTL_INT_STATUS); /* Check MTL Interrupt: Currently only one queue is used: Q0. */ if (mtl_int_qx_status & MTL_INT_Q0) { @@ -267,6 +298,10 @@ static int dwmac4_irq_status(struct mac_device_info *hw, } } + dwmac_pcs_isr(ioaddr, GMAC_PCS_BASE, intr_status, x); + if (intr_status & PCS_RGSMIIIS_IRQ) + dwmac4_phystatus(ioaddr, x); + return ret; } @@ -363,8 +398,9 @@ static const struct stmmac_ops dwmac4_ops = { .pmt = dwmac4_pmt, .set_umac_addr = dwmac4_set_umac_addr, .get_umac_addr = dwmac4_get_umac_addr, - .ctrl_ane = dwmac4_ctrl_ane, - .get_adv = dwmac4_get_adv, + .pcs_ctrl_ane = dwmac4_ctrl_ane, + .pcs_rane = dwmac4_rane, + .pcs_get_adv_lp = dwmac4_get_adv_lp, .debug = dwmac4_debug, .set_filter = dwmac4_set_filter, }; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h index 59ae6088cd22..8dc9056c1001 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h @@ -117,7 +117,6 @@ struct stmmac_priv { int eee_enabled; int eee_active; int tx_lpi_timer; - int pcs; unsigned int mode; int extend_desc; struct ptp_clock *ptp_clock; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c index e2b98b01647e..1e06173fc9d7 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c @@ -276,7 +276,8 @@ static int stmmac_ethtool_getsettings(struct net_device *dev, struct phy_device *phy = priv->phydev; int rc; - if ((priv->pcs & STMMAC_PCS_RGMII) || (priv->pcs & STMMAC_PCS_SGMII)) { + if (priv->hw->pcs & STMMAC_PCS_RGMII || + priv->hw->pcs & STMMAC_PCS_SGMII) { struct rgmii_adv adv; if (!priv->xstats.pcs_link) { @@ -289,10 +290,10 @@ static int stmmac_ethtool_getsettings(struct net_device *dev, ethtool_cmd_speed_set(cmd, priv->xstats.pcs_speed); /* Get and convert ADV/LP_ADV from the HW AN registers */ - if (!priv->hw->mac->get_adv) + if (!priv->hw->mac->pcs_get_adv_lp) return -EOPNOTSUPP; /* should never happen indeed */ - priv->hw->mac->get_adv(priv->hw, &adv); + priv->hw->mac->pcs_get_adv_lp(priv->ioaddr, &adv); /* Encoding of PSE bits is defined in 802.3z, 37.2.1.4 */ @@ -361,7 +362,8 @@ static int stmmac_ethtool_setsettings(struct net_device *dev, struct phy_device *phy = priv->phydev; int rc; - if ((priv->pcs & STMMAC_PCS_RGMII) || (priv->pcs & STMMAC_PCS_SGMII)) { + if (priv->hw->pcs & STMMAC_PCS_RGMII || + priv->hw->pcs & STMMAC_PCS_SGMII) { u32 mask = ADVERTISED_Autoneg | ADVERTISED_Pause; /* Only support ANE */ @@ -376,8 +378,11 @@ static int stmmac_ethtool_setsettings(struct net_device *dev, ADVERTISED_10baseT_Full); spin_lock(&priv->lock); - if (priv->hw->mac->ctrl_ane) - priv->hw->mac->ctrl_ane(priv->hw, 1); + + if (priv->hw->mac->pcs_ctrl_ane) + priv->hw->mac->pcs_ctrl_ane(priv->ioaddr, 1, + priv->hw->ps, 0); + spin_unlock(&priv->lock); return 0; @@ -452,11 +457,22 @@ stmmac_get_pauseparam(struct net_device *netdev, { struct stmmac_priv *priv = netdev_priv(netdev); - if (priv->pcs) /* FIXME */ - return; - pause->rx_pause = 0; pause->tx_pause = 0; + + if (priv->hw->pcs && priv->hw->mac->pcs_get_adv_lp) { + struct rgmii_adv adv_lp; + + pause->autoneg = 1; + priv->hw->mac->pcs_get_adv_lp(priv->ioaddr, &adv_lp); + if (!adv_lp.pause) + return; + } else { + if (!(priv->phydev->supported & SUPPORTED_Pause) || + !(priv->phydev->supported & SUPPORTED_Asym_Pause)) + return; + } + pause->autoneg = priv->phydev->autoneg; if (priv->flow_ctrl & FLOW_RX) @@ -473,10 +489,19 @@ stmmac_set_pauseparam(struct net_device *netdev, struct stmmac_priv *priv = netdev_priv(netdev); struct phy_device *phy = priv->phydev; int new_pause = FLOW_OFF; - int ret = 0; - if (priv->pcs) /* FIXME */ - return -EOPNOTSUPP; + if (priv->hw->pcs && priv->hw->mac->pcs_get_adv_lp) { + struct rgmii_adv adv_lp; + + pause->autoneg = 1; + priv->hw->mac->pcs_get_adv_lp(priv->ioaddr, &adv_lp); + if (!adv_lp.pause) + return -EOPNOTSUPP; + } else { + if (!(phy->supported & SUPPORTED_Pause) || + !(phy->supported & SUPPORTED_Asym_Pause)) + return -EOPNOTSUPP; + } if (pause->rx_pause) new_pause |= FLOW_RX; @@ -488,11 +513,12 @@ stmmac_set_pauseparam(struct net_device *netdev, if (phy->autoneg) { if (netif_running(netdev)) - ret = phy_start_aneg(phy); - } else - priv->hw->mac->flow_ctrl(priv->hw, phy->duplex, - priv->flow_ctrl, priv->pause); - return ret; + return phy_start_aneg(phy); + } + + priv->hw->mac->flow_ctrl(priv->hw, phy->duplex, priv->flow_ctrl, + priv->pause); + return 0; } static void stmmac_get_ethtool_stats(struct net_device *dev, diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index e4071265be76..4c8c60af7985 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -285,8 +285,9 @@ bool stmmac_eee_init(struct stmmac_priv *priv) /* Using PCS we cannot dial with the phy registers at this stage * so we do not support extra feature like EEE. */ - if ((priv->pcs == STMMAC_PCS_RGMII) || (priv->pcs == STMMAC_PCS_TBI) || - (priv->pcs == STMMAC_PCS_RTBI)) + if ((priv->hw->pcs == STMMAC_PCS_RGMII) || + (priv->hw->pcs == STMMAC_PCS_TBI) || + (priv->hw->pcs == STMMAC_PCS_RTBI)) goto out; /* MAC core supports the EEE feature. */ @@ -799,10 +800,10 @@ static void stmmac_check_pcs_mode(struct stmmac_priv *priv) (interface == PHY_INTERFACE_MODE_RGMII_RXID) || (interface == PHY_INTERFACE_MODE_RGMII_TXID)) { pr_debug("STMMAC: PCS RGMII support enable\n"); - priv->pcs = STMMAC_PCS_RGMII; + priv->hw->pcs = STMMAC_PCS_RGMII; } else if (interface == PHY_INTERFACE_MODE_SGMII) { pr_debug("STMMAC: PCS SGMII support enable\n"); - priv->pcs = STMMAC_PCS_SGMII; + priv->hw->pcs = STMMAC_PCS_SGMII; } } } @@ -1665,6 +1666,19 @@ static int stmmac_hw_setup(struct net_device *dev, bool init_ptp) if (priv->plat->bus_setup) priv->plat->bus_setup(priv->ioaddr); + /* PS and related bits will be programmed according to the speed */ + if (priv->hw->pcs) { + int speed = priv->plat->mac_port_sel_speed; + + if ((speed == SPEED_10) || (speed == SPEED_100) || + (speed == SPEED_1000)) { + priv->hw->ps = speed; + } else { + dev_warn(priv->device, "invalid port speed\n"); + priv->hw->ps = 0; + } + } + /* Initialize the MAC Core */ priv->hw->mac->core_init(priv->hw, dev->mtu); @@ -1714,8 +1728,8 @@ static int stmmac_hw_setup(struct net_device *dev, bool init_ptp) priv->hw->dma->rx_watchdog(priv->ioaddr, MAX_DMA_RIWT); } - if (priv->pcs && priv->hw->mac->ctrl_ane) - priv->hw->mac->ctrl_ane(priv->hw, 0); + if (priv->hw->pcs && priv->hw->mac->pcs_ctrl_ane) + priv->hw->mac->pcs_ctrl_ane(priv->hw, 1, priv->hw->ps, 0); /* set TX ring length */ if (priv->hw->dma->set_tx_ring_len) @@ -1748,8 +1762,9 @@ static int stmmac_open(struct net_device *dev) stmmac_check_ether_addr(priv); - if (priv->pcs != STMMAC_PCS_RGMII && priv->pcs != STMMAC_PCS_TBI && - priv->pcs != STMMAC_PCS_RTBI) { + if (priv->hw->pcs != STMMAC_PCS_RGMII && + priv->hw->pcs != STMMAC_PCS_TBI && + priv->hw->pcs != STMMAC_PCS_RTBI) { ret = stmmac_init_phy(dev); if (ret) { pr_err("%s: Cannot attach to PHY (error: %d)\n", @@ -2809,6 +2824,14 @@ static irqreturn_t stmmac_interrupt(int irq, void *dev_id) priv->rx_tail_addr, STMMAC_CHAN0); } + + /* PCS link status */ + if (priv->hw->pcs) { + if (priv->xstats.pcs_link) + netif_carrier_on(dev); + else + netif_carrier_off(dev); + } } /* To handle DMA interrupts */ @@ -3130,6 +3153,7 @@ static int stmmac_hw_init(struct stmmac_priv *priv) */ priv->plat->enh_desc = priv->dma_cap.enh_desc; priv->plat->pmt = priv->dma_cap.pmt_remote_wake_up; + priv->hw->pmt = priv->plat->pmt; /* TXCOE doesn't work in thresh DMA mode */ if (priv->plat->force_thresh_dma_mode) @@ -3325,8 +3349,9 @@ int stmmac_dvr_probe(struct device *device, stmmac_check_pcs_mode(priv); - if (priv->pcs != STMMAC_PCS_RGMII && priv->pcs != STMMAC_PCS_TBI && - priv->pcs != STMMAC_PCS_RTBI) { + if (priv->hw->pcs != STMMAC_PCS_RGMII && + priv->hw->pcs != STMMAC_PCS_TBI && + priv->hw->pcs != STMMAC_PCS_RTBI) { /* MDIO bus Registration */ ret = stmmac_mdio_register(ndev); if (ret < 0) { @@ -3372,12 +3397,14 @@ int stmmac_dvr_remove(struct device *dev) stmmac_set_mac(priv->ioaddr, false); netif_carrier_off(ndev); unregister_netdev(ndev); + of_node_put(priv->plat->phy_node); if (priv->stmmac_rst) reset_control_assert(priv->stmmac_rst); clk_disable_unprepare(priv->pclk); clk_disable_unprepare(priv->stmmac_clk); - if (priv->pcs != STMMAC_PCS_RGMII && priv->pcs != STMMAC_PCS_TBI && - priv->pcs != STMMAC_PCS_RTBI) + if (priv->hw->pcs != STMMAC_PCS_RGMII && + priv->hw->pcs != STMMAC_PCS_TBI && + priv->hw->pcs != STMMAC_PCS_RTBI) stmmac_mdio_unregister(ndev); free_netdev(ndev); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.h b/drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.h new file mode 100644 index 000000000000..eba41c24b7a7 --- /dev/null +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_pcs.h @@ -0,0 +1,159 @@ +/* + * stmmac_pcs.h: Physical Coding Sublayer Header File + * + * Copyright (C) 2016 STMicroelectronics (R&D) Limited + * Author: Giuseppe Cavallaro <peppe.cavallaro@st.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __STMMAC_PCS_H__ +#define __STMMAC_PCS_H__ + +#include <linux/slab.h> +#include <linux/io.h> +#include "common.h" + +/* PCS registers (AN/TBI/SGMII/RGMII) offsets */ +#define GMAC_AN_CTRL(x) (x) /* AN control */ +#define GMAC_AN_STATUS(x) (x + 0x4) /* AN status */ +#define GMAC_ANE_ADV(x) (x + 0x8) /* ANE Advertisement */ +#define GMAC_ANE_LPA(x) (x + 0xc) /* ANE link partener ability */ +#define GMAC_ANE_EXP(x) (x + 0x10) /* ANE expansion */ +#define GMAC_TBI(x) (x + 0x14) /* TBI extend status */ + +/* AN Configuration defines */ +#define GMAC_AN_CTRL_RAN BIT(9) /* Restart Auto-Negotiation */ +#define GMAC_AN_CTRL_ANE BIT(12) /* Auto-Negotiation Enable */ +#define GMAC_AN_CTRL_ELE BIT(14) /* External Loopback Enable */ +#define GMAC_AN_CTRL_ECD BIT(16) /* Enable Comma Detect */ +#define GMAC_AN_CTRL_LR BIT(17) /* Lock to Reference */ +#define GMAC_AN_CTRL_SGMRAL BIT(18) /* SGMII RAL Control */ + +/* AN Status defines */ +#define GMAC_AN_STATUS_LS BIT(2) /* Link Status 0:down 1:up */ +#define GMAC_AN_STATUS_ANA BIT(3) /* Auto-Negotiation Ability */ +#define GMAC_AN_STATUS_ANC BIT(5) /* Auto-Negotiation Complete */ +#define GMAC_AN_STATUS_ES BIT(8) /* Extended Status */ + +/* ADV and LPA defines */ +#define GMAC_ANE_FD BIT(5) +#define GMAC_ANE_HD BIT(6) +#define GMAC_ANE_PSE GENMASK(8, 7) +#define GMAC_ANE_PSE_SHIFT 7 +#define GMAC_ANE_RFE GENMASK(13, 12) +#define GMAC_ANE_RFE_SHIFT 12 +#define GMAC_ANE_ACK BIT(14) + +/** + * dwmac_pcs_isr - TBI, RTBI, or SGMII PHY ISR + * @ioaddr: IO registers pointer + * @reg: Base address of the AN Control Register. + * @intr_status: GMAC core interrupt status + * @x: pointer to log these events as stats + * Description: it is the ISR for PCS events: Auto-Negotiation Completed and + * Link status. + */ +static inline void dwmac_pcs_isr(void __iomem *ioaddr, u32 reg, + unsigned int intr_status, + struct stmmac_extra_stats *x) +{ + u32 val = readl(ioaddr + GMAC_AN_STATUS(reg)); + + if (intr_status & PCS_ANE_IRQ) { + x->irq_pcs_ane_n++; + if (val & GMAC_AN_STATUS_ANC) + pr_info("stmmac_pcs: ANE process completed\n"); + } + + if (intr_status & PCS_LINK_IRQ) { + x->irq_pcs_link_n++; + if (val & GMAC_AN_STATUS_LS) + pr_info("stmmac_pcs: Link Up\n"); + else + pr_info("stmmac_pcs: Link Down\n"); + } +} + +/** + * dwmac_rane - To restart ANE + * @ioaddr: IO registers pointer + * @reg: Base address of the AN Control Register. + * @restart: to restart ANE + * Description: this is to just restart the Auto-Negotiation. + */ +static inline void dwmac_rane(void __iomem *ioaddr, u32 reg, bool restart) +{ + u32 value = readl(ioaddr + GMAC_AN_CTRL(reg)); + + if (restart) + value |= GMAC_AN_CTRL_RAN; + + writel(value, ioaddr + GMAC_AN_CTRL(reg)); +} + +/** + * dwmac_ctrl_ane - To program the AN Control Register. + * @ioaddr: IO registers pointer + * @reg: Base address of the AN Control Register. + * @ane: to enable the auto-negotiation + * @srgmi_ral: to manage MAC-2-MAC SGMII connections. + * @loopback: to cause the PHY to loopback tx data into rx path. + * Description: this is the main function to configure the AN control register + * and init the ANE, select loopback (usually for debugging purpose) and + * configure SGMII RAL. + */ +static inline void dwmac_ctrl_ane(void __iomem *ioaddr, u32 reg, bool ane, + bool srgmi_ral, bool loopback) +{ + u32 value = readl(ioaddr + GMAC_AN_CTRL(reg)); + + /* Enable and restart the Auto-Negotiation */ + if (ane) + value |= GMAC_AN_CTRL_ANE | GMAC_AN_CTRL_RAN; + + /* In case of MAC-2-MAC connection, block is configured to operate + * according to MAC conf register. + */ + if (srgmi_ral) + value |= GMAC_AN_CTRL_SGMRAL; + + if (loopback) + value |= GMAC_AN_CTRL_ELE; + + writel(value, ioaddr + GMAC_AN_CTRL(reg)); +} + +/** + * dwmac_get_adv_lp - Get ADV and LP cap + * @ioaddr: IO registers pointer + * @reg: Base address of the AN Control Register. + * @adv_lp: structure to store the adv,lp status + * Description: this is to expose the ANE advertisement and Link partner ability + * status to ethtool support. + */ +static inline void dwmac_get_adv_lp(void __iomem *ioaddr, u32 reg, + struct rgmii_adv *adv_lp) +{ + u32 value = readl(ioaddr + GMAC_ANE_ADV(reg)); + + if (value & GMAC_ANE_FD) + adv_lp->duplex = DUPLEX_FULL; + if (value & GMAC_ANE_HD) + adv_lp->duplex |= DUPLEX_HALF; + + adv_lp->pause = (value & GMAC_ANE_PSE) >> GMAC_ANE_PSE_SHIFT; + + value = readl(ioaddr + GMAC_ANE_LPA(reg)); + + if (value & GMAC_ANE_FD) + adv_lp->lp_duplex = DUPLEX_FULL; + if (value & GMAC_ANE_HD) + adv_lp->lp_duplex = DUPLEX_HALF; + + adv_lp->lp_pause = (value & GMAC_ANE_PSE) >> GMAC_ANE_PSE_SHIFT; +} +#endif /* __STMMAC_PCS_H__ */ diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c index 409db913b117..756bb548e81a 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c @@ -113,8 +113,10 @@ static struct stmmac_axi *stmmac_axi_setup(struct platform_device *pdev) return NULL; axi = kzalloc(sizeof(*axi), GFP_KERNEL); - if (!axi) + if (!axi) { + of_node_put(np); return ERR_PTR(-ENOMEM); + } axi->axi_lpi_en = of_property_read_bool(np, "snps,lpi_en"); axi->axi_xit_frm = of_property_read_bool(np, "snps,xit_frm"); @@ -127,6 +129,7 @@ static struct stmmac_axi *stmmac_axi_setup(struct platform_device *pdev) of_property_read_u32(np, "snps,wr_osr_lmt", &axi->axi_wr_osr_lmt); of_property_read_u32(np, "snps,rd_osr_lmt", &axi->axi_rd_osr_lmt); of_property_read_u32_array(np, "snps,blen", axi->axi_blen, AXI_BLEN); + of_node_put(np); return axi; } @@ -302,7 +305,7 @@ stmmac_probe_config_dt(struct platform_device *pdev, const char **mac) dma_cfg = devm_kzalloc(&pdev->dev, sizeof(*dma_cfg), GFP_KERNEL); if (!dma_cfg) { - of_node_put(np); + of_node_put(plat->phy_node); return ERR_PTR(-ENOMEM); } plat->dma_cfg = dma_cfg; @@ -319,6 +322,8 @@ stmmac_probe_config_dt(struct platform_device *pdev, const char **mac) pr_warn("force_sf_dma_mode is ignored if force_thresh_dma_mode is set."); } + of_property_read_u32(np, "snps,ps-speed", &plat->mac_port_sel_speed); + plat->axi = stmmac_axi_setup(pdev); return plat; @@ -411,7 +416,9 @@ static int stmmac_pltfr_suspend(struct device *dev) struct platform_device *pdev = to_platform_device(dev); ret = stmmac_suspend(dev); - if (priv->plat->exit) + if (priv->plat->suspend) + priv->plat->suspend(pdev, priv->plat->bsp_priv); + else if (priv->plat->exit) priv->plat->exit(pdev, priv->plat->bsp_priv); return ret; @@ -430,7 +437,9 @@ static int stmmac_pltfr_resume(struct device *dev) struct stmmac_priv *priv = netdev_priv(ndev); struct platform_device *pdev = to_platform_device(dev); - if (priv->plat->init) + if (priv->plat->resume) + priv->plat->resume(pdev, priv->plat->bsp_priv); + else if (priv->plat->init) priv->plat->init(pdev, priv->plat->bsp_priv); return stmmac_resume(dev); |