mirror of
https://github.com/openwrt/openwrt.git
synced 2024-12-25 08:21:14 +00:00
ed43a4d4ac
This adds support for the GMAC which is use in the A64 and other Allwinner chips by backporting the changes from the kernel versions 4.13. Some commits are not backported which are adding support for newly introduced APIs which are not available in kernel 4.9. Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
1925 lines
53 KiB
Diff
1925 lines
53 KiB
Diff
--- a/drivers/net/ethernet/stmicro/stmmac/Kconfig
|
|
+++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig
|
|
@@ -145,6 +145,17 @@ config DWMAC_SUNXI
|
|
This selects Allwinner SoC glue layer support for the
|
|
stmmac device driver. This driver is used for A20/A31
|
|
GMAC ethernet controller.
|
|
+
|
|
+config DWMAC_SUN8I
|
|
+ tristate "Allwinner sun8i GMAC support"
|
|
+ default ARCH_SUNXI
|
|
+ depends on OF && (ARCH_SUNXI || COMPILE_TEST)
|
|
+ ---help---
|
|
+ Support for Allwinner H3 A83T A64 EMAC ethernet controllers.
|
|
+
|
|
+ This selects Allwinner SoC glue layer support for the
|
|
+ stmmac device driver. This driver is used for H3/A83T/A64
|
|
+ EMAC ethernet controller.
|
|
endif
|
|
|
|
config STMMAC_PCI
|
|
--- a/drivers/net/ethernet/stmicro/stmmac/Makefile
|
|
+++ b/drivers/net/ethernet/stmicro/stmmac/Makefile
|
|
@@ -16,6 +16,7 @@ obj-$(CONFIG_DWMAC_SOCFPGA) += dwmac-alt
|
|
obj-$(CONFIG_DWMAC_STI) += dwmac-sti.o
|
|
obj-$(CONFIG_DWMAC_STM32) += dwmac-stm32.o
|
|
obj-$(CONFIG_DWMAC_SUNXI) += dwmac-sunxi.o
|
|
+obj-$(CONFIG_DWMAC_SUN8I) += dwmac-sun8i.o
|
|
obj-$(CONFIG_DWMAC_DWC_QOS_ETH) += dwmac-dwc-qos-eth.o
|
|
obj-$(CONFIG_DWMAC_GENERIC) += dwmac-generic.o
|
|
stmmac-platform-objs:= stmmac_platform.o
|
|
--- a/drivers/net/ethernet/stmicro/stmmac/common.h
|
|
+++ b/drivers/net/ethernet/stmicro/stmmac/common.h
|
|
@@ -549,9 +549,11 @@ extern const struct stmmac_hwtimestamp s
|
|
extern const struct stmmac_mode_ops dwmac4_ring_mode_ops;
|
|
|
|
struct mac_link {
|
|
- int port;
|
|
- int duplex;
|
|
- int speed;
|
|
+ u32 speed_mask;
|
|
+ u32 speed10;
|
|
+ u32 speed100;
|
|
+ u32 speed1000;
|
|
+ u32 duplex;
|
|
};
|
|
|
|
struct mii_regs {
|
|
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
|
|
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
|
|
@@ -269,7 +269,10 @@ static int socfpga_dwmac_set_phy_mode(st
|
|
ctrl &= ~(SYSMGR_EMACGRP_CTRL_PHYSEL_MASK << reg_shift);
|
|
ctrl |= val << reg_shift;
|
|
|
|
- if (dwmac->f2h_ptp_ref_clk) {
|
|
+ if (dwmac->f2h_ptp_ref_clk ||
|
|
+ phymode == PHY_INTERFACE_MODE_MII ||
|
|
+ phymode == PHY_INTERFACE_MODE_GMII ||
|
|
+ phymode == PHY_INTERFACE_MODE_SGMII) {
|
|
ctrl |= SYSMGR_EMACGRP_CTRL_PTP_REF_CLK_MASK << (reg_shift / 2);
|
|
regmap_read(sys_mgr_base_addr, SYSMGR_FPGAGRP_MODULE_REG,
|
|
&module);
|
|
--- /dev/null
|
|
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sun8i.c
|
|
@@ -0,0 +1,1007 @@
|
|
+/*
|
|
+ * dwmac-sun8i.c - Allwinner sun8i DWMAC specific glue layer
|
|
+ *
|
|
+ * Copyright (C) 2017 Corentin Labbe <clabbe.montjoie@gmail.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.
|
|
+ *
|
|
+ * 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.
|
|
+ */
|
|
+
|
|
+#include <linux/clk.h>
|
|
+#include <linux/io.h>
|
|
+#include <linux/iopoll.h>
|
|
+#include <linux/mfd/syscon.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/of_device.h>
|
|
+#include <linux/of_mdio.h>
|
|
+#include <linux/of_net.h>
|
|
+#include <linux/phy.h>
|
|
+#include <linux/platform_device.h>
|
|
+#include <linux/regulator/consumer.h>
|
|
+#include <linux/regmap.h>
|
|
+#include <linux/stmmac.h>
|
|
+
|
|
+#include "stmmac.h"
|
|
+#include "stmmac_platform.h"
|
|
+
|
|
+/* General notes on dwmac-sun8i:
|
|
+ * Locking: no locking is necessary in this file because all necessary locking
|
|
+ * is done in the "stmmac files"
|
|
+ */
|
|
+
|
|
+/* struct emac_variant - Descrive dwmac-sun8i hardware variant
|
|
+ * @default_syscon_value: The default value of the EMAC register in syscon
|
|
+ * This value is used for disabling properly EMAC
|
|
+ * and used as a good starting value in case of the
|
|
+ * boot process(uboot) leave some stuff.
|
|
+ * @internal_phy: Does the MAC embed an internal PHY
|
|
+ * @support_mii: Does the MAC handle MII
|
|
+ * @support_rmii: Does the MAC handle RMII
|
|
+ * @support_rgmii: Does the MAC handle RGMII
|
|
+ */
|
|
+struct emac_variant {
|
|
+ u32 default_syscon_value;
|
|
+ int internal_phy;
|
|
+ bool support_mii;
|
|
+ bool support_rmii;
|
|
+ bool support_rgmii;
|
|
+};
|
|
+
|
|
+/* struct sunxi_priv_data - hold all sunxi private data
|
|
+ * @tx_clk: reference to MAC TX clock
|
|
+ * @ephy_clk: reference to the optional EPHY clock for the internal PHY
|
|
+ * @regulator: reference to the optional regulator
|
|
+ * @rst_ephy: reference to the optional EPHY reset for the internal PHY
|
|
+ * @variant: reference to the current board variant
|
|
+ * @regmap: regmap for using the syscon
|
|
+ * @use_internal_phy: Does the current PHY choice imply using the internal PHY
|
|
+ */
|
|
+struct sunxi_priv_data {
|
|
+ struct clk *tx_clk;
|
|
+ struct clk *ephy_clk;
|
|
+ struct regulator *regulator;
|
|
+ struct reset_control *rst_ephy;
|
|
+ const struct emac_variant *variant;
|
|
+ struct regmap *regmap;
|
|
+ bool use_internal_phy;
|
|
+};
|
|
+
|
|
+static const struct emac_variant emac_variant_h3 = {
|
|
+ .default_syscon_value = 0x58000,
|
|
+ .internal_phy = PHY_INTERFACE_MODE_MII,
|
|
+ .support_mii = true,
|
|
+ .support_rmii = true,
|
|
+ .support_rgmii = true
|
|
+};
|
|
+
|
|
+static const struct emac_variant emac_variant_v3s = {
|
|
+ .default_syscon_value = 0x38000,
|
|
+ .internal_phy = PHY_INTERFACE_MODE_MII,
|
|
+ .support_mii = true
|
|
+};
|
|
+
|
|
+static const struct emac_variant emac_variant_a83t = {
|
|
+ .default_syscon_value = 0,
|
|
+ .internal_phy = 0,
|
|
+ .support_mii = true,
|
|
+ .support_rgmii = true
|
|
+};
|
|
+
|
|
+static const struct emac_variant emac_variant_a64 = {
|
|
+ .default_syscon_value = 0,
|
|
+ .internal_phy = 0,
|
|
+ .support_mii = true,
|
|
+ .support_rmii = true,
|
|
+ .support_rgmii = true
|
|
+};
|
|
+
|
|
+#define EMAC_BASIC_CTL0 0x00
|
|
+#define EMAC_BASIC_CTL1 0x04
|
|
+#define EMAC_INT_STA 0x08
|
|
+#define EMAC_INT_EN 0x0C
|
|
+#define EMAC_TX_CTL0 0x10
|
|
+#define EMAC_TX_CTL1 0x14
|
|
+#define EMAC_TX_FLOW_CTL 0x1C
|
|
+#define EMAC_TX_DESC_LIST 0x20
|
|
+#define EMAC_RX_CTL0 0x24
|
|
+#define EMAC_RX_CTL1 0x28
|
|
+#define EMAC_RX_DESC_LIST 0x34
|
|
+#define EMAC_RX_FRM_FLT 0x38
|
|
+#define EMAC_MDIO_CMD 0x48
|
|
+#define EMAC_MDIO_DATA 0x4C
|
|
+#define EMAC_MACADDR_HI(reg) (0x50 + (reg) * 8)
|
|
+#define EMAC_MACADDR_LO(reg) (0x54 + (reg) * 8)
|
|
+#define EMAC_TX_DMA_STA 0xB0
|
|
+#define EMAC_TX_CUR_DESC 0xB4
|
|
+#define EMAC_TX_CUR_BUF 0xB8
|
|
+#define EMAC_RX_DMA_STA 0xC0
|
|
+#define EMAC_RX_CUR_DESC 0xC4
|
|
+#define EMAC_RX_CUR_BUF 0xC8
|
|
+
|
|
+/* Use in EMAC_BASIC_CTL0 */
|
|
+#define EMAC_DUPLEX_FULL BIT(0)
|
|
+#define EMAC_LOOPBACK BIT(1)
|
|
+#define EMAC_SPEED_1000 0
|
|
+#define EMAC_SPEED_100 (0x03 << 2)
|
|
+#define EMAC_SPEED_10 (0x02 << 2)
|
|
+
|
|
+/* Use in EMAC_BASIC_CTL1 */
|
|
+#define EMAC_BURSTLEN_SHIFT 24
|
|
+
|
|
+/* Used in EMAC_RX_FRM_FLT */
|
|
+#define EMAC_FRM_FLT_RXALL BIT(0)
|
|
+#define EMAC_FRM_FLT_CTL BIT(13)
|
|
+#define EMAC_FRM_FLT_MULTICAST BIT(16)
|
|
+
|
|
+/* Used in RX_CTL1*/
|
|
+#define EMAC_RX_MD BIT(1)
|
|
+#define EMAC_RX_TH_MASK GENMASK(4, 5)
|
|
+#define EMAC_RX_TH_32 0
|
|
+#define EMAC_RX_TH_64 (0x1 << 4)
|
|
+#define EMAC_RX_TH_96 (0x2 << 4)
|
|
+#define EMAC_RX_TH_128 (0x3 << 4)
|
|
+#define EMAC_RX_DMA_EN BIT(30)
|
|
+#define EMAC_RX_DMA_START BIT(31)
|
|
+
|
|
+/* Used in TX_CTL1*/
|
|
+#define EMAC_TX_MD BIT(1)
|
|
+#define EMAC_TX_NEXT_FRM BIT(2)
|
|
+#define EMAC_TX_TH_MASK GENMASK(8, 10)
|
|
+#define EMAC_TX_TH_64 0
|
|
+#define EMAC_TX_TH_128 (0x1 << 8)
|
|
+#define EMAC_TX_TH_192 (0x2 << 8)
|
|
+#define EMAC_TX_TH_256 (0x3 << 8)
|
|
+#define EMAC_TX_DMA_EN BIT(30)
|
|
+#define EMAC_TX_DMA_START BIT(31)
|
|
+
|
|
+/* Used in RX_CTL0 */
|
|
+#define EMAC_RX_RECEIVER_EN BIT(31)
|
|
+#define EMAC_RX_DO_CRC BIT(27)
|
|
+#define EMAC_RX_FLOW_CTL_EN BIT(16)
|
|
+
|
|
+/* Used in TX_CTL0 */
|
|
+#define EMAC_TX_TRANSMITTER_EN BIT(31)
|
|
+
|
|
+/* Used in EMAC_TX_FLOW_CTL */
|
|
+#define EMAC_TX_FLOW_CTL_EN BIT(0)
|
|
+
|
|
+/* Used in EMAC_INT_STA */
|
|
+#define EMAC_TX_INT BIT(0)
|
|
+#define EMAC_TX_DMA_STOP_INT BIT(1)
|
|
+#define EMAC_TX_BUF_UA_INT BIT(2)
|
|
+#define EMAC_TX_TIMEOUT_INT BIT(3)
|
|
+#define EMAC_TX_UNDERFLOW_INT BIT(4)
|
|
+#define EMAC_TX_EARLY_INT BIT(5)
|
|
+#define EMAC_RX_INT BIT(8)
|
|
+#define EMAC_RX_BUF_UA_INT BIT(9)
|
|
+#define EMAC_RX_DMA_STOP_INT BIT(10)
|
|
+#define EMAC_RX_TIMEOUT_INT BIT(11)
|
|
+#define EMAC_RX_OVERFLOW_INT BIT(12)
|
|
+#define EMAC_RX_EARLY_INT BIT(13)
|
|
+#define EMAC_RGMII_STA_INT BIT(16)
|
|
+
|
|
+#define MAC_ADDR_TYPE_DST BIT(31)
|
|
+
|
|
+/* H3 specific bits for EPHY */
|
|
+#define H3_EPHY_ADDR_SHIFT 20
|
|
+#define H3_EPHY_CLK_SEL BIT(18) /* 1: 24MHz, 0: 25MHz */
|
|
+#define H3_EPHY_LED_POL BIT(17) /* 1: active low, 0: active high */
|
|
+#define H3_EPHY_SHUTDOWN BIT(16) /* 1: shutdown, 0: power up */
|
|
+#define H3_EPHY_SELECT BIT(15) /* 1: internal PHY, 0: external PHY */
|
|
+
|
|
+/* H3/A64 specific bits */
|
|
+#define SYSCON_RMII_EN BIT(13) /* 1: enable RMII (overrides EPIT) */
|
|
+
|
|
+/* Generic system control EMAC_CLK bits */
|
|
+#define SYSCON_ETXDC_MASK GENMASK(2, 0)
|
|
+#define SYSCON_ETXDC_SHIFT 10
|
|
+#define SYSCON_ERXDC_MASK GENMASK(4, 0)
|
|
+#define SYSCON_ERXDC_SHIFT 5
|
|
+/* EMAC PHY Interface Type */
|
|
+#define SYSCON_EPIT BIT(2) /* 1: RGMII, 0: MII */
|
|
+#define SYSCON_ETCS_MASK GENMASK(1, 0)
|
|
+#define SYSCON_ETCS_MII 0x0
|
|
+#define SYSCON_ETCS_EXT_GMII 0x1
|
|
+#define SYSCON_ETCS_INT_GMII 0x2
|
|
+#define SYSCON_EMAC_REG 0x30
|
|
+
|
|
+/* sun8i_dwmac_dma_reset() - reset the EMAC
|
|
+ * Called from stmmac via stmmac_dma_ops->reset
|
|
+ */
|
|
+static int sun8i_dwmac_dma_reset(void __iomem *ioaddr)
|
|
+{
|
|
+ writel(0, ioaddr + EMAC_RX_CTL1);
|
|
+ writel(0, ioaddr + EMAC_TX_CTL1);
|
|
+ writel(0, ioaddr + EMAC_RX_FRM_FLT);
|
|
+ writel(0, ioaddr + EMAC_RX_DESC_LIST);
|
|
+ writel(0, ioaddr + EMAC_TX_DESC_LIST);
|
|
+ writel(0, ioaddr + EMAC_INT_EN);
|
|
+ writel(0x1FFFFFF, ioaddr + EMAC_INT_STA);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* sun8i_dwmac_dma_init() - initialize the EMAC
|
|
+ * Called from stmmac via stmmac_dma_ops->init
|
|
+ */
|
|
+static void sun8i_dwmac_dma_init(void __iomem *ioaddr,
|
|
+ struct stmmac_dma_cfg *dma_cfg,
|
|
+ u32 dma_tx, u32 dma_rx, int atds)
|
|
+{
|
|
+ /* Write TX and RX descriptors address */
|
|
+ writel(dma_rx, ioaddr + EMAC_RX_DESC_LIST);
|
|
+ writel(dma_tx, ioaddr + EMAC_TX_DESC_LIST);
|
|
+
|
|
+ writel(EMAC_RX_INT | EMAC_TX_INT, ioaddr + EMAC_INT_EN);
|
|
+ writel(0x1FFFFFF, ioaddr + EMAC_INT_STA);
|
|
+}
|
|
+
|
|
+/* sun8i_dwmac_dump_regs() - Dump EMAC address space
|
|
+ * Called from stmmac_dma_ops->dump_regs
|
|
+ * Used for ethtool
|
|
+ */
|
|
+static void sun8i_dwmac_dump_regs(void __iomem *ioaddr, u32 *reg_space)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i < 0xC8; i += 4) {
|
|
+ if (i == 0x32 || i == 0x3C)
|
|
+ continue;
|
|
+ reg_space[i / 4] = readl(ioaddr + i);
|
|
+ }
|
|
+}
|
|
+
|
|
+/* sun8i_dwmac_dump_mac_regs() - Dump EMAC address space
|
|
+ * Called from stmmac_ops->dump_regs
|
|
+ * Used for ethtool
|
|
+ */
|
|
+static void sun8i_dwmac_dump_mac_regs(struct mac_device_info *hw,
|
|
+ u32 *reg_space)
|
|
+{
|
|
+ int i;
|
|
+ void __iomem *ioaddr = hw->pcsr;
|
|
+
|
|
+ for (i = 0; i < 0xC8; i += 4) {
|
|
+ if (i == 0x32 || i == 0x3C)
|
|
+ continue;
|
|
+ reg_space[i / 4] = readl(ioaddr + i);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void sun8i_dwmac_enable_dma_irq(void __iomem *ioaddr, u32 chan)
|
|
+{
|
|
+ writel(EMAC_RX_INT | EMAC_TX_INT, ioaddr + EMAC_INT_EN);
|
|
+}
|
|
+
|
|
+static void sun8i_dwmac_disable_dma_irq(void __iomem *ioaddr, u32 chan)
|
|
+{
|
|
+ writel(0, ioaddr + EMAC_INT_EN);
|
|
+}
|
|
+
|
|
+static void sun8i_dwmac_dma_start_tx(void __iomem *ioaddr, u32 chan)
|
|
+{
|
|
+ u32 v;
|
|
+
|
|
+ v = readl(ioaddr + EMAC_TX_CTL1);
|
|
+ v |= EMAC_TX_DMA_START;
|
|
+ v |= EMAC_TX_DMA_EN;
|
|
+ writel(v, ioaddr + EMAC_TX_CTL1);
|
|
+}
|
|
+
|
|
+static void sun8i_dwmac_enable_dma_transmission(void __iomem *ioaddr)
|
|
+{
|
|
+ u32 v;
|
|
+
|
|
+ v = readl(ioaddr + EMAC_TX_CTL1);
|
|
+ v |= EMAC_TX_DMA_START;
|
|
+ v |= EMAC_TX_DMA_EN;
|
|
+ writel(v, ioaddr + EMAC_TX_CTL1);
|
|
+}
|
|
+
|
|
+static void sun8i_dwmac_dma_stop_tx(void __iomem *ioaddr, u32 chan)
|
|
+{
|
|
+ u32 v;
|
|
+
|
|
+ v = readl(ioaddr + EMAC_TX_CTL1);
|
|
+ v &= ~EMAC_TX_DMA_EN;
|
|
+ writel(v, ioaddr + EMAC_TX_CTL1);
|
|
+}
|
|
+
|
|
+static void sun8i_dwmac_dma_start_rx(void __iomem *ioaddr, u32 chan)
|
|
+{
|
|
+ u32 v;
|
|
+
|
|
+ v = readl(ioaddr + EMAC_RX_CTL1);
|
|
+ v |= EMAC_RX_DMA_START;
|
|
+ v |= EMAC_RX_DMA_EN;
|
|
+ writel(v, ioaddr + EMAC_RX_CTL1);
|
|
+}
|
|
+
|
|
+static void sun8i_dwmac_dma_stop_rx(void __iomem *ioaddr, u32 chan)
|
|
+{
|
|
+ u32 v;
|
|
+
|
|
+ v = readl(ioaddr + EMAC_RX_CTL1);
|
|
+ v &= ~EMAC_RX_DMA_EN;
|
|
+ writel(v, ioaddr + EMAC_RX_CTL1);
|
|
+}
|
|
+
|
|
+static int sun8i_dwmac_dma_interrupt(void __iomem *ioaddr,
|
|
+ struct stmmac_extra_stats *x, u32 chan)
|
|
+{
|
|
+ u32 v;
|
|
+ int ret = 0;
|
|
+
|
|
+ v = readl(ioaddr + EMAC_INT_STA);
|
|
+
|
|
+ if (v & EMAC_TX_INT) {
|
|
+ ret |= handle_tx;
|
|
+ x->tx_normal_irq_n++;
|
|
+ }
|
|
+
|
|
+ if (v & EMAC_TX_DMA_STOP_INT)
|
|
+ x->tx_process_stopped_irq++;
|
|
+
|
|
+ if (v & EMAC_TX_BUF_UA_INT)
|
|
+ x->tx_process_stopped_irq++;
|
|
+
|
|
+ if (v & EMAC_TX_TIMEOUT_INT)
|
|
+ ret |= tx_hard_error;
|
|
+
|
|
+ if (v & EMAC_TX_UNDERFLOW_INT) {
|
|
+ ret |= tx_hard_error;
|
|
+ x->tx_undeflow_irq++;
|
|
+ }
|
|
+
|
|
+ if (v & EMAC_TX_EARLY_INT)
|
|
+ x->tx_early_irq++;
|
|
+
|
|
+ if (v & EMAC_RX_INT) {
|
|
+ ret |= handle_rx;
|
|
+ x->rx_normal_irq_n++;
|
|
+ }
|
|
+
|
|
+ if (v & EMAC_RX_BUF_UA_INT)
|
|
+ x->rx_buf_unav_irq++;
|
|
+
|
|
+ if (v & EMAC_RX_DMA_STOP_INT)
|
|
+ x->rx_process_stopped_irq++;
|
|
+
|
|
+ if (v & EMAC_RX_TIMEOUT_INT)
|
|
+ ret |= tx_hard_error;
|
|
+
|
|
+ if (v & EMAC_RX_OVERFLOW_INT) {
|
|
+ ret |= tx_hard_error;
|
|
+ x->rx_overflow_irq++;
|
|
+ }
|
|
+
|
|
+ if (v & EMAC_RX_EARLY_INT)
|
|
+ x->rx_early_irq++;
|
|
+
|
|
+ if (v & EMAC_RGMII_STA_INT)
|
|
+ x->irq_rgmii_n++;
|
|
+
|
|
+ writel(v, ioaddr + EMAC_INT_STA);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void sun8i_dwmac_dma_operation_mode(void __iomem *ioaddr, int txmode,
|
|
+ int rxmode, int rxfifosz)
|
|
+{
|
|
+ u32 v;
|
|
+
|
|
+ v = readl(ioaddr + EMAC_TX_CTL1);
|
|
+ if (txmode == SF_DMA_MODE) {
|
|
+ v |= EMAC_TX_MD;
|
|
+ /* Undocumented bit (called TX_NEXT_FRM in BSP), the original
|
|
+ * comment is
|
|
+ * "Operating on second frame increase the performance
|
|
+ * especially when transmit store-and-forward is used."
|
|
+ */
|
|
+ v |= EMAC_TX_NEXT_FRM;
|
|
+ } else {
|
|
+ v &= ~EMAC_TX_MD;
|
|
+ v &= ~EMAC_TX_TH_MASK;
|
|
+ if (txmode < 64)
|
|
+ v |= EMAC_TX_TH_64;
|
|
+ else if (txmode < 128)
|
|
+ v |= EMAC_TX_TH_128;
|
|
+ else if (txmode < 192)
|
|
+ v |= EMAC_TX_TH_192;
|
|
+ else if (txmode < 256)
|
|
+ v |= EMAC_TX_TH_256;
|
|
+ }
|
|
+ writel(v, ioaddr + EMAC_TX_CTL1);
|
|
+
|
|
+ v = readl(ioaddr + EMAC_RX_CTL1);
|
|
+ if (rxmode == SF_DMA_MODE) {
|
|
+ v |= EMAC_RX_MD;
|
|
+ } else {
|
|
+ v &= ~EMAC_RX_MD;
|
|
+ v &= ~EMAC_RX_TH_MASK;
|
|
+ if (rxmode < 32)
|
|
+ v |= EMAC_RX_TH_32;
|
|
+ else if (rxmode < 64)
|
|
+ v |= EMAC_RX_TH_64;
|
|
+ else if (rxmode < 96)
|
|
+ v |= EMAC_RX_TH_96;
|
|
+ else if (rxmode < 128)
|
|
+ v |= EMAC_RX_TH_128;
|
|
+ }
|
|
+ writel(v, ioaddr + EMAC_RX_CTL1);
|
|
+}
|
|
+
|
|
+static const struct stmmac_dma_ops sun8i_dwmac_dma_ops = {
|
|
+ .reset = sun8i_dwmac_dma_reset,
|
|
+ .init = sun8i_dwmac_dma_init,
|
|
+ .dump_regs = sun8i_dwmac_dump_regs,
|
|
+ .dma_mode = sun8i_dwmac_dma_operation_mode,
|
|
+ .enable_dma_transmission = sun8i_dwmac_enable_dma_transmission,
|
|
+ .enable_dma_irq = sun8i_dwmac_enable_dma_irq,
|
|
+ .disable_dma_irq = sun8i_dwmac_disable_dma_irq,
|
|
+ .start_tx = sun8i_dwmac_dma_start_tx,
|
|
+ .stop_tx = sun8i_dwmac_dma_stop_tx,
|
|
+ .start_rx = sun8i_dwmac_dma_start_rx,
|
|
+ .stop_rx = sun8i_dwmac_dma_stop_rx,
|
|
+ .dma_interrupt = sun8i_dwmac_dma_interrupt,
|
|
+};
|
|
+
|
|
+static int sun8i_dwmac_init(struct platform_device *pdev, void *priv)
|
|
+{
|
|
+ struct sunxi_priv_data *gmac = priv;
|
|
+ int ret;
|
|
+
|
|
+ if (gmac->regulator) {
|
|
+ ret = regulator_enable(gmac->regulator);
|
|
+ if (ret) {
|
|
+ dev_err(&pdev->dev, "Fail to enable regulator\n");
|
|
+ return ret;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ ret = clk_prepare_enable(gmac->tx_clk);
|
|
+ if (ret) {
|
|
+ if (gmac->regulator)
|
|
+ regulator_disable(gmac->regulator);
|
|
+ dev_err(&pdev->dev, "Could not enable AHB clock\n");
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void sun8i_dwmac_core_init(struct mac_device_info *hw, int mtu)
|
|
+{
|
|
+ void __iomem *ioaddr = hw->pcsr;
|
|
+ u32 v;
|
|
+
|
|
+ v = (8 << EMAC_BURSTLEN_SHIFT); /* burst len */
|
|
+ writel(v, ioaddr + EMAC_BASIC_CTL1);
|
|
+}
|
|
+
|
|
+static void sun8i_dwmac_set_mac(void __iomem *ioaddr, bool enable)
|
|
+{
|
|
+ u32 t, r;
|
|
+
|
|
+ t = readl(ioaddr + EMAC_TX_CTL0);
|
|
+ r = readl(ioaddr + EMAC_RX_CTL0);
|
|
+ if (enable) {
|
|
+ t |= EMAC_TX_TRANSMITTER_EN;
|
|
+ r |= EMAC_RX_RECEIVER_EN;
|
|
+ } else {
|
|
+ t &= ~EMAC_TX_TRANSMITTER_EN;
|
|
+ r &= ~EMAC_RX_RECEIVER_EN;
|
|
+ }
|
|
+ writel(t, ioaddr + EMAC_TX_CTL0);
|
|
+ writel(r, ioaddr + EMAC_RX_CTL0);
|
|
+}
|
|
+
|
|
+/* Set MAC address at slot reg_n
|
|
+ * All slot > 0 need to be enabled with MAC_ADDR_TYPE_DST
|
|
+ * If addr is NULL, clear the slot
|
|
+ */
|
|
+static void sun8i_dwmac_set_umac_addr(struct mac_device_info *hw,
|
|
+ unsigned char *addr,
|
|
+ unsigned int reg_n)
|
|
+{
|
|
+ void __iomem *ioaddr = hw->pcsr;
|
|
+ u32 v;
|
|
+
|
|
+ if (!addr) {
|
|
+ writel(0, ioaddr + EMAC_MACADDR_HI(reg_n));
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ stmmac_set_mac_addr(ioaddr, addr, EMAC_MACADDR_HI(reg_n),
|
|
+ EMAC_MACADDR_LO(reg_n));
|
|
+ if (reg_n > 0) {
|
|
+ v = readl(ioaddr + EMAC_MACADDR_HI(reg_n));
|
|
+ v |= MAC_ADDR_TYPE_DST;
|
|
+ writel(v, ioaddr + EMAC_MACADDR_HI(reg_n));
|
|
+ }
|
|
+}
|
|
+
|
|
+static void sun8i_dwmac_get_umac_addr(struct mac_device_info *hw,
|
|
+ unsigned char *addr,
|
|
+ unsigned int reg_n)
|
|
+{
|
|
+ void __iomem *ioaddr = hw->pcsr;
|
|
+
|
|
+ stmmac_get_mac_addr(ioaddr, addr, EMAC_MACADDR_HI(reg_n),
|
|
+ EMAC_MACADDR_LO(reg_n));
|
|
+}
|
|
+
|
|
+/* caution this function must return non 0 to work */
|
|
+static int sun8i_dwmac_rx_ipc_enable(struct mac_device_info *hw)
|
|
+{
|
|
+ void __iomem *ioaddr = hw->pcsr;
|
|
+ u32 v;
|
|
+
|
|
+ v = readl(ioaddr + EMAC_RX_CTL0);
|
|
+ v |= EMAC_RX_DO_CRC;
|
|
+ writel(v, ioaddr + EMAC_RX_CTL0);
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+static void sun8i_dwmac_set_filter(struct mac_device_info *hw,
|
|
+ struct net_device *dev)
|
|
+{
|
|
+ void __iomem *ioaddr = hw->pcsr;
|
|
+ u32 v;
|
|
+ int i = 1;
|
|
+ struct netdev_hw_addr *ha;
|
|
+ int macaddrs = netdev_uc_count(dev) + netdev_mc_count(dev) + 1;
|
|
+
|
|
+ v = EMAC_FRM_FLT_CTL;
|
|
+
|
|
+ if (dev->flags & IFF_PROMISC) {
|
|
+ v = EMAC_FRM_FLT_RXALL;
|
|
+ } else if (dev->flags & IFF_ALLMULTI) {
|
|
+ v |= EMAC_FRM_FLT_MULTICAST;
|
|
+ } else if (macaddrs <= hw->unicast_filter_entries) {
|
|
+ if (!netdev_mc_empty(dev)) {
|
|
+ netdev_for_each_mc_addr(ha, dev) {
|
|
+ sun8i_dwmac_set_umac_addr(hw, ha->addr, i);
|
|
+ i++;
|
|
+ }
|
|
+ }
|
|
+ if (!netdev_uc_empty(dev)) {
|
|
+ netdev_for_each_uc_addr(ha, dev) {
|
|
+ sun8i_dwmac_set_umac_addr(hw, ha->addr, i);
|
|
+ i++;
|
|
+ }
|
|
+ }
|
|
+ } else {
|
|
+ netdev_info(dev, "Too many address, switching to promiscuous\n");
|
|
+ v = EMAC_FRM_FLT_RXALL;
|
|
+ }
|
|
+
|
|
+ /* Disable unused address filter slots */
|
|
+ while (i < hw->unicast_filter_entries)
|
|
+ sun8i_dwmac_set_umac_addr(hw, NULL, i++);
|
|
+
|
|
+ writel(v, ioaddr + EMAC_RX_FRM_FLT);
|
|
+}
|
|
+
|
|
+static void sun8i_dwmac_flow_ctrl(struct mac_device_info *hw,
|
|
+ unsigned int duplex, unsigned int fc,
|
|
+ unsigned int pause_time, u32 tx_cnt)
|
|
+{
|
|
+ void __iomem *ioaddr = hw->pcsr;
|
|
+ u32 v;
|
|
+
|
|
+ v = readl(ioaddr + EMAC_RX_CTL0);
|
|
+ if (fc == FLOW_AUTO)
|
|
+ v |= EMAC_RX_FLOW_CTL_EN;
|
|
+ else
|
|
+ v &= ~EMAC_RX_FLOW_CTL_EN;
|
|
+ writel(v, ioaddr + EMAC_RX_CTL0);
|
|
+
|
|
+ v = readl(ioaddr + EMAC_TX_FLOW_CTL);
|
|
+ if (fc == FLOW_AUTO)
|
|
+ v |= EMAC_TX_FLOW_CTL_EN;
|
|
+ else
|
|
+ v &= ~EMAC_TX_FLOW_CTL_EN;
|
|
+ writel(v, ioaddr + EMAC_TX_FLOW_CTL);
|
|
+}
|
|
+
|
|
+static int sun8i_dwmac_reset(struct stmmac_priv *priv)
|
|
+{
|
|
+ u32 v;
|
|
+ int err;
|
|
+
|
|
+ v = readl(priv->ioaddr + EMAC_BASIC_CTL1);
|
|
+ writel(v | 0x01, priv->ioaddr + EMAC_BASIC_CTL1);
|
|
+
|
|
+ /* The timeout was previoulsy set to 10ms, but some board (OrangePI0)
|
|
+ * need more if no cable plugged. 100ms seems OK
|
|
+ */
|
|
+ err = readl_poll_timeout(priv->ioaddr + EMAC_BASIC_CTL1, v,
|
|
+ !(v & 0x01), 100, 100000);
|
|
+
|
|
+ if (err) {
|
|
+ dev_err(priv->device, "EMAC reset timeout\n");
|
|
+ return -EFAULT;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int sun8i_dwmac_set_syscon(struct stmmac_priv *priv)
|
|
+{
|
|
+ struct sunxi_priv_data *gmac = priv->plat->bsp_priv;
|
|
+ struct device_node *node = priv->device->of_node;
|
|
+ int ret;
|
|
+ u32 reg, val;
|
|
+
|
|
+ regmap_read(gmac->regmap, SYSCON_EMAC_REG, &val);
|
|
+ reg = gmac->variant->default_syscon_value;
|
|
+ if (reg != val)
|
|
+ dev_warn(priv->device,
|
|
+ "Current syscon value is not the default %x (expect %x)\n",
|
|
+ val, reg);
|
|
+
|
|
+ if (gmac->variant->internal_phy) {
|
|
+ if (!gmac->use_internal_phy) {
|
|
+ /* switch to external PHY interface */
|
|
+ reg &= ~H3_EPHY_SELECT;
|
|
+ } else {
|
|
+ reg |= H3_EPHY_SELECT;
|
|
+ reg &= ~H3_EPHY_SHUTDOWN;
|
|
+ dev_dbg(priv->device, "Select internal_phy %x\n", reg);
|
|
+
|
|
+ if (of_property_read_bool(priv->plat->phy_node,
|
|
+ "allwinner,leds-active-low"))
|
|
+ reg |= H3_EPHY_LED_POL;
|
|
+ else
|
|
+ reg &= ~H3_EPHY_LED_POL;
|
|
+
|
|
+ /* Force EPHY xtal frequency to 24MHz. */
|
|
+ reg |= H3_EPHY_CLK_SEL;
|
|
+
|
|
+ ret = of_mdio_parse_addr(priv->device,
|
|
+ priv->plat->phy_node);
|
|
+ if (ret < 0) {
|
|
+ dev_err(priv->device, "Could not parse MDIO addr\n");
|
|
+ return ret;
|
|
+ }
|
|
+ /* of_mdio_parse_addr returns a valid (0 ~ 31) PHY
|
|
+ * address. No need to mask it again.
|
|
+ */
|
|
+ reg |= ret << H3_EPHY_ADDR_SHIFT;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!of_property_read_u32(node, "allwinner,tx-delay-ps", &val)) {
|
|
+ if (val % 100) {
|
|
+ dev_err(priv->device, "tx-delay must be a multiple of 100\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ val /= 100;
|
|
+ dev_dbg(priv->device, "set tx-delay to %x\n", val);
|
|
+ if (val <= SYSCON_ETXDC_MASK) {
|
|
+ reg &= ~(SYSCON_ETXDC_MASK << SYSCON_ETXDC_SHIFT);
|
|
+ reg |= (val << SYSCON_ETXDC_SHIFT);
|
|
+ } else {
|
|
+ dev_err(priv->device, "Invalid TX clock delay: %d\n",
|
|
+ val);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!of_property_read_u32(node, "allwinner,rx-delay-ps", &val)) {
|
|
+ if (val % 100) {
|
|
+ dev_err(priv->device, "rx-delay must be a multiple of 100\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ val /= 100;
|
|
+ dev_dbg(priv->device, "set rx-delay to %x\n", val);
|
|
+ if (val <= SYSCON_ERXDC_MASK) {
|
|
+ reg &= ~(SYSCON_ERXDC_MASK << SYSCON_ERXDC_SHIFT);
|
|
+ reg |= (val << SYSCON_ERXDC_SHIFT);
|
|
+ } else {
|
|
+ dev_err(priv->device, "Invalid RX clock delay: %d\n",
|
|
+ val);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Clear interface mode bits */
|
|
+ reg &= ~(SYSCON_ETCS_MASK | SYSCON_EPIT);
|
|
+ if (gmac->variant->support_rmii)
|
|
+ reg &= ~SYSCON_RMII_EN;
|
|
+
|
|
+ switch (priv->plat->interface) {
|
|
+ case PHY_INTERFACE_MODE_MII:
|
|
+ /* default */
|
|
+ break;
|
|
+ case PHY_INTERFACE_MODE_RGMII:
|
|
+ reg |= SYSCON_EPIT | SYSCON_ETCS_INT_GMII;
|
|
+ break;
|
|
+ case PHY_INTERFACE_MODE_RMII:
|
|
+ reg |= SYSCON_RMII_EN | SYSCON_ETCS_EXT_GMII;
|
|
+ break;
|
|
+ default:
|
|
+ dev_err(priv->device, "Unsupported interface mode: %s",
|
|
+ phy_modes(priv->plat->interface));
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ regmap_write(gmac->regmap, SYSCON_EMAC_REG, reg);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void sun8i_dwmac_unset_syscon(struct sunxi_priv_data *gmac)
|
|
+{
|
|
+ u32 reg = gmac->variant->default_syscon_value;
|
|
+
|
|
+ regmap_write(gmac->regmap, SYSCON_EMAC_REG, reg);
|
|
+}
|
|
+
|
|
+static int sun8i_dwmac_power_internal_phy(struct stmmac_priv *priv)
|
|
+{
|
|
+ struct sunxi_priv_data *gmac = priv->plat->bsp_priv;
|
|
+ int ret;
|
|
+
|
|
+ if (!gmac->use_internal_phy)
|
|
+ return 0;
|
|
+
|
|
+ ret = clk_prepare_enable(gmac->ephy_clk);
|
|
+ if (ret) {
|
|
+ dev_err(priv->device, "Cannot enable ephy\n");
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ /* Make sure the EPHY is properly reseted, as U-Boot may leave
|
|
+ * it at deasserted state, and thus it may fail to reset EMAC.
|
|
+ */
|
|
+ reset_control_assert(gmac->rst_ephy);
|
|
+
|
|
+ ret = reset_control_deassert(gmac->rst_ephy);
|
|
+ if (ret) {
|
|
+ dev_err(priv->device, "Cannot deassert ephy\n");
|
|
+ clk_disable_unprepare(gmac->ephy_clk);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int sun8i_dwmac_unpower_internal_phy(struct sunxi_priv_data *gmac)
|
|
+{
|
|
+ if (!gmac->use_internal_phy)
|
|
+ return 0;
|
|
+
|
|
+ clk_disable_unprepare(gmac->ephy_clk);
|
|
+ reset_control_assert(gmac->rst_ephy);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* sun8i_power_phy() - Activate the PHY:
|
|
+ * In case of error, no need to call sun8i_unpower_phy(),
|
|
+ * it will be called anyway by sun8i_dwmac_exit()
|
|
+ */
|
|
+static int sun8i_power_phy(struct stmmac_priv *priv)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ ret = sun8i_dwmac_power_internal_phy(priv);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ ret = sun8i_dwmac_set_syscon(priv);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ /* After changing syscon value, the MAC need reset or it will use
|
|
+ * the last value (and so the last PHY set.
|
|
+ */
|
|
+ ret = sun8i_dwmac_reset(priv);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void sun8i_unpower_phy(struct sunxi_priv_data *gmac)
|
|
+{
|
|
+ sun8i_dwmac_unset_syscon(gmac);
|
|
+ sun8i_dwmac_unpower_internal_phy(gmac);
|
|
+}
|
|
+
|
|
+static void sun8i_dwmac_exit(struct platform_device *pdev, void *priv)
|
|
+{
|
|
+ struct sunxi_priv_data *gmac = priv;
|
|
+
|
|
+ sun8i_unpower_phy(gmac);
|
|
+
|
|
+ clk_disable_unprepare(gmac->tx_clk);
|
|
+
|
|
+ if (gmac->regulator)
|
|
+ regulator_disable(gmac->regulator);
|
|
+}
|
|
+
|
|
+static const struct stmmac_ops sun8i_dwmac_ops = {
|
|
+ .core_init = sun8i_dwmac_core_init,
|
|
+ .set_mac = sun8i_dwmac_set_mac,
|
|
+ .dump_regs = sun8i_dwmac_dump_mac_regs,
|
|
+ .rx_ipc = sun8i_dwmac_rx_ipc_enable,
|
|
+ .set_filter = sun8i_dwmac_set_filter,
|
|
+ .flow_ctrl = sun8i_dwmac_flow_ctrl,
|
|
+ .set_umac_addr = sun8i_dwmac_set_umac_addr,
|
|
+ .get_umac_addr = sun8i_dwmac_get_umac_addr,
|
|
+};
|
|
+
|
|
+static struct mac_device_info *sun8i_dwmac_setup(void *ppriv)
|
|
+{
|
|
+ struct mac_device_info *mac;
|
|
+ struct stmmac_priv *priv = ppriv;
|
|
+ int ret;
|
|
+
|
|
+ mac = devm_kzalloc(priv->device, sizeof(*mac), GFP_KERNEL);
|
|
+ if (!mac)
|
|
+ return NULL;
|
|
+
|
|
+ ret = sun8i_power_phy(priv);
|
|
+ if (ret)
|
|
+ return NULL;
|
|
+
|
|
+ mac->pcsr = priv->ioaddr;
|
|
+ mac->mac = &sun8i_dwmac_ops;
|
|
+ mac->dma = &sun8i_dwmac_dma_ops;
|
|
+
|
|
+ /* The loopback bit seems to be re-set when link change
|
|
+ * Simply mask it each time
|
|
+ * Speed 10/100/1000 are set in BIT(2)/BIT(3)
|
|
+ */
|
|
+ mac->link.speed_mask = GENMASK(3, 2) | EMAC_LOOPBACK;
|
|
+ mac->link.speed10 = EMAC_SPEED_10;
|
|
+ mac->link.speed100 = EMAC_SPEED_100;
|
|
+ mac->link.speed1000 = EMAC_SPEED_1000;
|
|
+ mac->link.duplex = EMAC_DUPLEX_FULL;
|
|
+ mac->mii.addr = EMAC_MDIO_CMD;
|
|
+ mac->mii.data = EMAC_MDIO_DATA;
|
|
+ mac->mii.reg_shift = 4;
|
|
+ mac->mii.reg_mask = GENMASK(8, 4);
|
|
+ mac->mii.addr_shift = 12;
|
|
+ mac->mii.addr_mask = GENMASK(16, 12);
|
|
+ mac->mii.clk_csr_shift = 20;
|
|
+ mac->mii.clk_csr_mask = GENMASK(22, 20);
|
|
+ mac->unicast_filter_entries = 8;
|
|
+
|
|
+ /* Synopsys Id is not available */
|
|
+ priv->synopsys_id = 0;
|
|
+
|
|
+ return mac;
|
|
+}
|
|
+
|
|
+static int sun8i_dwmac_probe(struct platform_device *pdev)
|
|
+{
|
|
+ struct plat_stmmacenet_data *plat_dat;
|
|
+ struct stmmac_resources stmmac_res;
|
|
+ struct sunxi_priv_data *gmac;
|
|
+ struct device *dev = &pdev->dev;
|
|
+ int ret;
|
|
+
|
|
+ ret = stmmac_get_platform_resources(pdev, &stmmac_res);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac);
|
|
+ if (IS_ERR(plat_dat))
|
|
+ return PTR_ERR(plat_dat);
|
|
+
|
|
+ gmac = devm_kzalloc(dev, sizeof(*gmac), GFP_KERNEL);
|
|
+ if (!gmac)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ gmac->variant = of_device_get_match_data(&pdev->dev);
|
|
+ if (!gmac->variant) {
|
|
+ dev_err(&pdev->dev, "Missing dwmac-sun8i variant\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ gmac->tx_clk = devm_clk_get(dev, "stmmaceth");
|
|
+ if (IS_ERR(gmac->tx_clk)) {
|
|
+ dev_err(dev, "Could not get TX clock\n");
|
|
+ return PTR_ERR(gmac->tx_clk);
|
|
+ }
|
|
+
|
|
+ /* Optional regulator for PHY */
|
|
+ gmac->regulator = devm_regulator_get_optional(dev, "phy");
|
|
+ if (IS_ERR(gmac->regulator)) {
|
|
+ if (PTR_ERR(gmac->regulator) == -EPROBE_DEFER)
|
|
+ return -EPROBE_DEFER;
|
|
+ dev_info(dev, "No regulator found\n");
|
|
+ gmac->regulator = NULL;
|
|
+ }
|
|
+
|
|
+ gmac->regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
|
|
+ "syscon");
|
|
+ if (IS_ERR(gmac->regmap)) {
|
|
+ ret = PTR_ERR(gmac->regmap);
|
|
+ dev_err(&pdev->dev, "Unable to map syscon: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ plat_dat->interface = of_get_phy_mode(dev->of_node);
|
|
+ if (plat_dat->interface == gmac->variant->internal_phy) {
|
|
+ dev_info(&pdev->dev, "Will use internal PHY\n");
|
|
+ gmac->use_internal_phy = true;
|
|
+ gmac->ephy_clk = of_clk_get(plat_dat->phy_node, 0);
|
|
+ if (IS_ERR(gmac->ephy_clk)) {
|
|
+ ret = PTR_ERR(gmac->ephy_clk);
|
|
+ dev_err(&pdev->dev, "Cannot get EPHY clock: %d\n", ret);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ gmac->rst_ephy = of_reset_control_get(plat_dat->phy_node, NULL);
|
|
+ if (IS_ERR(gmac->rst_ephy)) {
|
|
+ ret = PTR_ERR(gmac->rst_ephy);
|
|
+ if (ret == -EPROBE_DEFER)
|
|
+ return ret;
|
|
+ dev_err(&pdev->dev, "No EPHY reset control found %d\n",
|
|
+ ret);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ } else {
|
|
+ dev_info(&pdev->dev, "Will use external PHY\n");
|
|
+ gmac->use_internal_phy = false;
|
|
+ }
|
|
+
|
|
+ /* platform data specifying hardware features and callbacks.
|
|
+ * hardware features were copied from Allwinner drivers.
|
|
+ */
|
|
+ plat_dat->rx_coe = STMMAC_RX_COE_TYPE2;
|
|
+ plat_dat->tx_coe = 1;
|
|
+ plat_dat->has_sun8i = true;
|
|
+ plat_dat->bsp_priv = gmac;
|
|
+ plat_dat->init = sun8i_dwmac_init;
|
|
+ plat_dat->exit = sun8i_dwmac_exit;
|
|
+ plat_dat->setup = sun8i_dwmac_setup;
|
|
+
|
|
+ ret = sun8i_dwmac_init(pdev, plat_dat->bsp_priv);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
|
|
+ if (ret)
|
|
+ sun8i_dwmac_exit(pdev, plat_dat->bsp_priv);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static const struct of_device_id sun8i_dwmac_match[] = {
|
|
+ { .compatible = "allwinner,sun8i-h3-emac",
|
|
+ .data = &emac_variant_h3 },
|
|
+ { .compatible = "allwinner,sun8i-v3s-emac",
|
|
+ .data = &emac_variant_v3s },
|
|
+ { .compatible = "allwinner,sun8i-a83t-emac",
|
|
+ .data = &emac_variant_a83t },
|
|
+ { .compatible = "allwinner,sun50i-a64-emac",
|
|
+ .data = &emac_variant_a64 },
|
|
+ { }
|
|
+};
|
|
+MODULE_DEVICE_TABLE(of, sun8i_dwmac_match);
|
|
+
|
|
+static struct platform_driver sun8i_dwmac_driver = {
|
|
+ .probe = sun8i_dwmac_probe,
|
|
+ .remove = stmmac_pltfr_remove,
|
|
+ .driver = {
|
|
+ .name = "dwmac-sun8i",
|
|
+ .pm = &stmmac_pltfr_pm_ops,
|
|
+ .of_match_table = sun8i_dwmac_match,
|
|
+ },
|
|
+};
|
|
+module_platform_driver(sun8i_dwmac_driver);
|
|
+
|
|
+MODULE_AUTHOR("Corentin Labbe <clabbe.montjoie@gmail.com>");
|
|
+MODULE_DESCRIPTION("Allwinner sun8i DWMAC specific glue layer");
|
|
+MODULE_LICENSE("GPL");
|
|
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c
|
|
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c
|
|
@@ -45,15 +45,17 @@ static void dwmac1000_core_init(struct m
|
|
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;
|
|
+ value &= ~hw->link.speed_mask;
|
|
+ switch (hw->ps) {
|
|
+ case SPEED_1000:
|
|
+ value |= hw->link.speed1000;
|
|
+ break;
|
|
+ case SPEED_100:
|
|
+ value |= hw->link.speed100;
|
|
+ break;
|
|
+ case SPEED_10:
|
|
+ value |= hw->link.speed10;
|
|
+ break;
|
|
}
|
|
}
|
|
|
|
@@ -531,9 +533,11 @@ struct mac_device_info *dwmac1000_setup(
|
|
mac->mac = &dwmac1000_ops;
|
|
mac->dma = &dwmac1000_dma_ops;
|
|
|
|
- mac->link.port = GMAC_CONTROL_PS;
|
|
mac->link.duplex = GMAC_CONTROL_DM;
|
|
- mac->link.speed = GMAC_CONTROL_FES;
|
|
+ mac->link.speed10 = GMAC_CONTROL_PS;
|
|
+ mac->link.speed100 = GMAC_CONTROL_PS | GMAC_CONTROL_FES;
|
|
+ mac->link.speed1000 = 0;
|
|
+ mac->link.speed_mask = GMAC_CONTROL_PS | GMAC_CONTROL_FES;
|
|
mac->mii.addr = GMAC_MII_ADDR;
|
|
mac->mii.data = GMAC_MII_DATA;
|
|
mac->mii.addr_shift = 11;
|
|
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c
|
|
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c
|
|
@@ -205,8 +205,8 @@ static void dwmac1000_dump_dma_regs(void
|
|
{
|
|
int i;
|
|
|
|
- for (i = 0; i < 22; i++)
|
|
- if ((i < 9) || (i > 17))
|
|
+ for (i = 0; i < NUM_DWMAC1000_DMA_REGS; i++)
|
|
+ if ((i < 12) || (i > 17))
|
|
reg_space[DMA_BUS_MODE / 4 + i] =
|
|
readl(ioaddr + DMA_BUS_MODE + i * 4);
|
|
}
|
|
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c
|
|
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac100_core.c
|
|
@@ -175,9 +175,11 @@ struct mac_device_info *dwmac100_setup(v
|
|
mac->mac = &dwmac100_ops;
|
|
mac->dma = &dwmac100_dma_ops;
|
|
|
|
- mac->link.port = MAC_CONTROL_PS;
|
|
mac->link.duplex = MAC_CONTROL_F;
|
|
- mac->link.speed = 0;
|
|
+ mac->link.speed10 = 0;
|
|
+ mac->link.speed100 = 0;
|
|
+ mac->link.speed1000 = 0;
|
|
+ mac->link.speed_mask = MAC_CONTROL_PS;
|
|
mac->mii.addr = MAC_MII_ADDR;
|
|
mac->mii.data = MAC_MII_DATA;
|
|
mac->mii.addr_shift = 11;
|
|
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c
|
|
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c
|
|
@@ -70,7 +70,7 @@ static void dwmac100_dump_dma_regs(void
|
|
{
|
|
int i;
|
|
|
|
- for (i = 0; i < 9; i++)
|
|
+ for (i = 0; i < NUM_DWMAC100_DMA_REGS; i++)
|
|
reg_space[DMA_BUS_MODE / 4 + i] =
|
|
readl(ioaddr + DMA_BUS_MODE + i * 4);
|
|
|
|
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c
|
|
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c
|
|
@@ -35,15 +35,17 @@ static void dwmac4_core_init(struct mac_
|
|
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;
|
|
+ value &= hw->link.speed_mask;
|
|
+ switch (hw->ps) {
|
|
+ case SPEED_1000:
|
|
+ value |= hw->link.speed1000;
|
|
+ break;
|
|
+ case SPEED_100:
|
|
+ value |= hw->link.speed100;
|
|
+ break;
|
|
+ case SPEED_10:
|
|
+ value |= hw->link.speed10;
|
|
+ break;
|
|
}
|
|
}
|
|
|
|
@@ -115,7 +117,7 @@ static void dwmac4_tx_queue_routing(stru
|
|
void __iomem *ioaddr = hw->pcsr;
|
|
u32 value;
|
|
|
|
- const struct stmmac_rx_routing route_possibilities[] = {
|
|
+ static const struct stmmac_rx_routing route_possibilities[] = {
|
|
{ GMAC_RXQCTRL_AVCPQ_MASK, GMAC_RXQCTRL_AVCPQ_SHIFT },
|
|
{ GMAC_RXQCTRL_PTPQ_MASK, GMAC_RXQCTRL_PTPQ_SHIFT },
|
|
{ GMAC_RXQCTRL_DCBCPQ_MASK, GMAC_RXQCTRL_DCBCPQ_SHIFT },
|
|
@@ -747,9 +749,11 @@ struct mac_device_info *dwmac4_setup(voi
|
|
if (mac->multicast_filter_bins)
|
|
mac->mcast_bits_log2 = ilog2(mac->multicast_filter_bins);
|
|
|
|
- mac->link.port = GMAC_CONFIG_PS;
|
|
mac->link.duplex = GMAC_CONFIG_DM;
|
|
- mac->link.speed = GMAC_CONFIG_FES;
|
|
+ mac->link.speed10 = GMAC_CONFIG_PS;
|
|
+ mac->link.speed100 = GMAC_CONFIG_FES | GMAC_CONFIG_PS;
|
|
+ mac->link.speed1000 = 0;
|
|
+ mac->link.speed_mask = GMAC_CONFIG_FES | GMAC_CONFIG_PS;
|
|
mac->mii.addr = GMAC_MDIO_ADDR;
|
|
mac->mii.data = GMAC_MDIO_DATA;
|
|
mac->mii.addr_shift = 21;
|
|
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c
|
|
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_dma.c
|
|
@@ -71,9 +71,9 @@ static void dwmac4_dma_axi(void __iomem
|
|
writel(value, ioaddr + DMA_SYS_BUS_MODE);
|
|
}
|
|
|
|
-void dwmac4_dma_init_rx_chan(void __iomem *ioaddr,
|
|
- struct stmmac_dma_cfg *dma_cfg,
|
|
- u32 dma_rx_phy, u32 chan)
|
|
+static void dwmac4_dma_init_rx_chan(void __iomem *ioaddr,
|
|
+ struct stmmac_dma_cfg *dma_cfg,
|
|
+ u32 dma_rx_phy, u32 chan)
|
|
{
|
|
u32 value;
|
|
u32 rxpbl = dma_cfg->rxpbl ?: dma_cfg->pbl;
|
|
@@ -85,9 +85,9 @@ void dwmac4_dma_init_rx_chan(void __iome
|
|
writel(dma_rx_phy, ioaddr + DMA_CHAN_RX_BASE_ADDR(chan));
|
|
}
|
|
|
|
-void dwmac4_dma_init_tx_chan(void __iomem *ioaddr,
|
|
- struct stmmac_dma_cfg *dma_cfg,
|
|
- u32 dma_tx_phy, u32 chan)
|
|
+static void dwmac4_dma_init_tx_chan(void __iomem *ioaddr,
|
|
+ struct stmmac_dma_cfg *dma_cfg,
|
|
+ u32 dma_tx_phy, u32 chan)
|
|
{
|
|
u32 value;
|
|
u32 txpbl = dma_cfg->txpbl ?: dma_cfg->pbl;
|
|
@@ -99,8 +99,8 @@ void dwmac4_dma_init_tx_chan(void __iome
|
|
writel(dma_tx_phy, ioaddr + DMA_CHAN_TX_BASE_ADDR(chan));
|
|
}
|
|
|
|
-void dwmac4_dma_init_channel(void __iomem *ioaddr,
|
|
- struct stmmac_dma_cfg *dma_cfg, u32 chan)
|
|
+static void dwmac4_dma_init_channel(void __iomem *ioaddr,
|
|
+ struct stmmac_dma_cfg *dma_cfg, u32 chan)
|
|
{
|
|
u32 value;
|
|
|
|
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h
|
|
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h
|
|
@@ -136,6 +136,9 @@
|
|
#define DMA_STATUS_TI 0x00000001 /* Transmit Interrupt */
|
|
#define DMA_CONTROL_FTF 0x00100000 /* Flush transmit FIFO */
|
|
|
|
+#define NUM_DWMAC100_DMA_REGS 9
|
|
+#define NUM_DWMAC1000_DMA_REGS 23
|
|
+
|
|
void dwmac_enable_dma_transmission(void __iomem *ioaddr);
|
|
void dwmac_enable_dma_irq(void __iomem *ioaddr, u32 chan);
|
|
void dwmac_disable_dma_irq(void __iomem *ioaddr, u32 chan);
|
|
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c
|
|
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c
|
|
@@ -248,6 +248,7 @@ void stmmac_set_mac_addr(void __iomem *i
|
|
data = (addr[3] << 24) | (addr[2] << 16) | (addr[1] << 8) | addr[0];
|
|
writel(data, ioaddr + low);
|
|
}
|
|
+EXPORT_SYMBOL_GPL(stmmac_set_mac_addr);
|
|
|
|
/* Enable disable MAC RX/TX */
|
|
void stmmac_set_mac(void __iomem *ioaddr, bool enable)
|
|
@@ -279,4 +280,4 @@ void stmmac_get_mac_addr(void __iomem *i
|
|
addr[4] = hi_addr & 0xff;
|
|
addr[5] = (hi_addr >> 8) & 0xff;
|
|
}
|
|
-
|
|
+EXPORT_SYMBOL_GPL(stmmac_get_mac_addr);
|
|
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h
|
|
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
|
|
@@ -104,7 +104,7 @@ struct stmmac_priv {
|
|
/* TX Queue */
|
|
struct stmmac_tx_queue tx_queue[MTL_MAX_TX_QUEUES];
|
|
|
|
- int oldlink;
|
|
+ bool oldlink;
|
|
int speed;
|
|
int oldduplex;
|
|
unsigned int flow_ctrl;
|
|
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
|
|
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
|
|
@@ -29,10 +29,12 @@
|
|
#include "stmmac.h"
|
|
#include "dwmac_dma.h"
|
|
|
|
-#define REG_SPACE_SIZE 0x1054
|
|
+#define REG_SPACE_SIZE 0x1060
|
|
#define MAC100_ETHTOOL_NAME "st_mac100"
|
|
#define GMAC_ETHTOOL_NAME "st_gmac"
|
|
|
|
+#define ETHTOOL_DMA_OFFSET 55
|
|
+
|
|
struct stmmac_stats {
|
|
char stat_string[ETH_GSTRING_LEN];
|
|
int sizeof_stat;
|
|
@@ -273,7 +275,6 @@ static int stmmac_ethtool_get_link_ksett
|
|
{
|
|
struct stmmac_priv *priv = netdev_priv(dev);
|
|
struct phy_device *phy = dev->phydev;
|
|
- int rc;
|
|
|
|
if (priv->hw->pcs & STMMAC_PCS_RGMII ||
|
|
priv->hw->pcs & STMMAC_PCS_SGMII) {
|
|
@@ -364,8 +365,8 @@ static int stmmac_ethtool_get_link_ksett
|
|
"link speed / duplex setting\n", dev->name);
|
|
return -EBUSY;
|
|
}
|
|
- rc = phy_ethtool_ksettings_get(phy, cmd);
|
|
- return rc;
|
|
+ phy_ethtool_ksettings_get(phy, cmd);
|
|
+ return 0;
|
|
}
|
|
|
|
static int
|
|
@@ -443,6 +444,9 @@ static void stmmac_ethtool_gregs(struct
|
|
|
|
priv->hw->mac->dump_regs(priv->hw, reg_space);
|
|
priv->hw->dma->dump_regs(priv->ioaddr, reg_space);
|
|
+ /* Copy DMA registers to where ethtool expects them */
|
|
+ memcpy(®_space[ETHTOOL_DMA_OFFSET], ®_space[DMA_BUS_MODE / 4],
|
|
+ NUM_DWMAC1000_DMA_REGS * 4);
|
|
}
|
|
|
|
static void
|
|
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
|
|
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
|
|
@@ -235,6 +235,17 @@ static void stmmac_clk_csr_set(struct st
|
|
else if ((clk_rate >= CSR_F_250M) && (clk_rate < CSR_F_300M))
|
|
priv->clk_csr = STMMAC_CSR_250_300M;
|
|
}
|
|
+
|
|
+ if (priv->plat->has_sun8i) {
|
|
+ if (clk_rate > 160000000)
|
|
+ priv->clk_csr = 0x03;
|
|
+ else if (clk_rate > 80000000)
|
|
+ priv->clk_csr = 0x02;
|
|
+ else if (clk_rate > 40000000)
|
|
+ priv->clk_csr = 0x01;
|
|
+ else
|
|
+ priv->clk_csr = 0;
|
|
+ }
|
|
}
|
|
|
|
static void print_pkt(unsigned char *buf, int len)
|
|
@@ -783,7 +794,7 @@ static void stmmac_adjust_link(struct ne
|
|
struct stmmac_priv *priv = netdev_priv(dev);
|
|
struct phy_device *phydev = dev->phydev;
|
|
unsigned long flags;
|
|
- int new_state = 0;
|
|
+ bool new_state = false;
|
|
|
|
if (!phydev)
|
|
return;
|
|
@@ -796,8 +807,8 @@ static void stmmac_adjust_link(struct ne
|
|
/* Now we make sure that we can be in full duplex mode.
|
|
* If not, we operate in half-duplex mode. */
|
|
if (phydev->duplex != priv->oldduplex) {
|
|
- new_state = 1;
|
|
- if (!(phydev->duplex))
|
|
+ new_state = true;
|
|
+ if (!phydev->duplex)
|
|
ctrl &= ~priv->hw->link.duplex;
|
|
else
|
|
ctrl |= priv->hw->link.duplex;
|
|
@@ -808,30 +819,17 @@ static void stmmac_adjust_link(struct ne
|
|
stmmac_mac_flow_ctrl(priv, phydev->duplex);
|
|
|
|
if (phydev->speed != priv->speed) {
|
|
- new_state = 1;
|
|
+ new_state = true;
|
|
+ ctrl &= ~priv->hw->link.speed_mask;
|
|
switch (phydev->speed) {
|
|
- case 1000:
|
|
- if (priv->plat->has_gmac ||
|
|
- priv->plat->has_gmac4)
|
|
- ctrl &= ~priv->hw->link.port;
|
|
+ case SPEED_1000:
|
|
+ ctrl |= priv->hw->link.speed1000;
|
|
break;
|
|
- case 100:
|
|
- if (priv->plat->has_gmac ||
|
|
- priv->plat->has_gmac4) {
|
|
- ctrl |= priv->hw->link.port;
|
|
- ctrl |= priv->hw->link.speed;
|
|
- } else {
|
|
- ctrl &= ~priv->hw->link.port;
|
|
- }
|
|
+ case SPEED_100:
|
|
+ ctrl |= priv->hw->link.speed100;
|
|
break;
|
|
- case 10:
|
|
- if (priv->plat->has_gmac ||
|
|
- priv->plat->has_gmac4) {
|
|
- ctrl |= priv->hw->link.port;
|
|
- ctrl &= ~(priv->hw->link.speed);
|
|
- } else {
|
|
- ctrl &= ~priv->hw->link.port;
|
|
- }
|
|
+ case SPEED_10:
|
|
+ ctrl |= priv->hw->link.speed10;
|
|
break;
|
|
default:
|
|
netif_warn(priv, link, priv->dev,
|
|
@@ -847,12 +845,12 @@ static void stmmac_adjust_link(struct ne
|
|
writel(ctrl, priv->ioaddr + MAC_CTRL_REG);
|
|
|
|
if (!priv->oldlink) {
|
|
- new_state = 1;
|
|
- priv->oldlink = 1;
|
|
+ new_state = true;
|
|
+ priv->oldlink = true;
|
|
}
|
|
} else if (priv->oldlink) {
|
|
- new_state = 1;
|
|
- priv->oldlink = 0;
|
|
+ new_state = true;
|
|
+ priv->oldlink = false;
|
|
priv->speed = SPEED_UNKNOWN;
|
|
priv->oldduplex = DUPLEX_UNKNOWN;
|
|
}
|
|
@@ -915,7 +913,7 @@ static int stmmac_init_phy(struct net_de
|
|
char bus_id[MII_BUS_ID_SIZE];
|
|
int interface = priv->plat->interface;
|
|
int max_speed = priv->plat->max_speed;
|
|
- priv->oldlink = 0;
|
|
+ priv->oldlink = false;
|
|
priv->speed = SPEED_UNKNOWN;
|
|
priv->oldduplex = DUPLEX_UNKNOWN;
|
|
|
|
@@ -1450,7 +1448,7 @@ static void free_dma_rx_desc_resources(s
|
|
static void free_dma_tx_desc_resources(struct stmmac_priv *priv)
|
|
{
|
|
u32 tx_count = priv->plat->tx_queues_to_use;
|
|
- u32 queue = 0;
|
|
+ u32 queue;
|
|
|
|
/* Free TX queue resources */
|
|
for (queue = 0; queue < tx_count; queue++) {
|
|
@@ -1499,7 +1497,7 @@ static int alloc_dma_rx_desc_resources(s
|
|
sizeof(dma_addr_t),
|
|
GFP_KERNEL);
|
|
if (!rx_q->rx_skbuff_dma)
|
|
- return -ENOMEM;
|
|
+ goto err_dma;
|
|
|
|
rx_q->rx_skbuff = kmalloc_array(DMA_RX_SIZE,
|
|
sizeof(struct sk_buff *),
|
|
@@ -1562,13 +1560,13 @@ static int alloc_dma_tx_desc_resources(s
|
|
sizeof(*tx_q->tx_skbuff_dma),
|
|
GFP_KERNEL);
|
|
if (!tx_q->tx_skbuff_dma)
|
|
- return -ENOMEM;
|
|
+ goto err_dma;
|
|
|
|
tx_q->tx_skbuff = kmalloc_array(DMA_TX_SIZE,
|
|
sizeof(struct sk_buff *),
|
|
GFP_KERNEL);
|
|
if (!tx_q->tx_skbuff)
|
|
- goto err_dma_buffers;
|
|
+ goto err_dma;
|
|
|
|
if (priv->extend_desc) {
|
|
tx_q->dma_etx = dma_zalloc_coherent(priv->device,
|
|
@@ -1578,7 +1576,7 @@ static int alloc_dma_tx_desc_resources(s
|
|
&tx_q->dma_tx_phy,
|
|
GFP_KERNEL);
|
|
if (!tx_q->dma_etx)
|
|
- goto err_dma_buffers;
|
|
+ goto err_dma;
|
|
} else {
|
|
tx_q->dma_tx = dma_zalloc_coherent(priv->device,
|
|
DMA_TX_SIZE *
|
|
@@ -1587,13 +1585,13 @@ static int alloc_dma_tx_desc_resources(s
|
|
&tx_q->dma_tx_phy,
|
|
GFP_KERNEL);
|
|
if (!tx_q->dma_tx)
|
|
- goto err_dma_buffers;
|
|
+ goto err_dma;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
|
|
-err_dma_buffers:
|
|
+err_dma:
|
|
free_dma_tx_desc_resources(priv);
|
|
|
|
return ret;
|
|
@@ -2895,8 +2893,7 @@ static netdev_tx_t stmmac_tso_xmit(struc
|
|
priv->xstats.tx_set_ic_bit++;
|
|
}
|
|
|
|
- if (!priv->hwts_tx_en)
|
|
- skb_tx_timestamp(skb);
|
|
+ skb_tx_timestamp(skb);
|
|
|
|
if (unlikely((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) &&
|
|
priv->hwts_tx_en)) {
|
|
@@ -2974,7 +2971,7 @@ static netdev_tx_t stmmac_xmit(struct sk
|
|
|
|
/* Manage oversized TCP frames for GMAC4 device */
|
|
if (skb_is_gso(skb) && priv->tso) {
|
|
- if (ip_hdr(skb)->protocol == IPPROTO_TCP)
|
|
+ if (skb_shinfo(skb)->gso_type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6))
|
|
return stmmac_tso_xmit(skb, dev);
|
|
}
|
|
|
|
@@ -3105,8 +3102,7 @@ static netdev_tx_t stmmac_xmit(struct sk
|
|
priv->xstats.tx_set_ic_bit++;
|
|
}
|
|
|
|
- if (!priv->hwts_tx_en)
|
|
- skb_tx_timestamp(skb);
|
|
+ skb_tx_timestamp(skb);
|
|
|
|
/* Ready to fill the first descriptor and set the OWN bit w/o any
|
|
* problems because all the descriptors are actually ready to be
|
|
@@ -3983,7 +3979,9 @@ static int stmmac_hw_init(struct stmmac_
|
|
struct mac_device_info *mac;
|
|
|
|
/* Identify the MAC HW device */
|
|
- if (priv->plat->has_gmac) {
|
|
+ if (priv->plat->setup) {
|
|
+ mac = priv->plat->setup(priv);
|
|
+ } else if (priv->plat->has_gmac) {
|
|
priv->dev->priv_flags |= IFF_UNICAST_FLT;
|
|
mac = dwmac1000_setup(priv->ioaddr,
|
|
priv->plat->multicast_filter_bins,
|
|
@@ -4003,6 +4001,10 @@ static int stmmac_hw_init(struct stmmac_
|
|
|
|
priv->hw = mac;
|
|
|
|
+ /* dwmac-sun8i only work in chain mode */
|
|
+ if (priv->plat->has_sun8i)
|
|
+ chain_mode = 1;
|
|
+
|
|
/* To use the chained or ring mode */
|
|
if (priv->synopsys_id >= DWMAC_CORE_4_00) {
|
|
priv->hw->mode = &dwmac4_ring_mode_ops;
|
|
@@ -4131,8 +4133,15 @@ int stmmac_dvr_probe(struct device *devi
|
|
if ((phyaddr >= 0) && (phyaddr <= 31))
|
|
priv->plat->phy_addr = phyaddr;
|
|
|
|
- if (priv->plat->stmmac_rst)
|
|
+ if (priv->plat->stmmac_rst) {
|
|
+ ret = reset_control_assert(priv->plat->stmmac_rst);
|
|
reset_control_deassert(priv->plat->stmmac_rst);
|
|
+ /* Some reset controllers have only reset callback instead of
|
|
+ * assert + deassert callbacks pair.
|
|
+ */
|
|
+ if (ret == -ENOTSUPP)
|
|
+ reset_control_reset(priv->plat->stmmac_rst);
|
|
+ }
|
|
|
|
/* Init MAC and get the capabilities */
|
|
ret = stmmac_hw_init(priv);
|
|
@@ -4149,7 +4158,7 @@ int stmmac_dvr_probe(struct device *devi
|
|
NETIF_F_RXCSUM;
|
|
|
|
if ((priv->plat->tso_en) && (priv->dma_cap.tsoen)) {
|
|
- ndev->hw_features |= NETIF_F_TSO;
|
|
+ ndev->hw_features |= NETIF_F_TSO | NETIF_F_TSO6;
|
|
priv->tso = true;
|
|
dev_info(priv->device, "TSO feature enabled\n");
|
|
}
|
|
@@ -4311,7 +4320,7 @@ int stmmac_suspend(struct device *dev)
|
|
}
|
|
spin_unlock_irqrestore(&priv->lock, flags);
|
|
|
|
- priv->oldlink = 0;
|
|
+ priv->oldlink = false;
|
|
priv->speed = SPEED_UNKNOWN;
|
|
priv->oldduplex = DUPLEX_UNKNOWN;
|
|
return 0;
|
|
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c
|
|
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c
|
|
@@ -204,6 +204,7 @@ int stmmac_mdio_register(struct net_devi
|
|
struct stmmac_priv *priv = netdev_priv(ndev);
|
|
struct stmmac_mdio_bus_data *mdio_bus_data = priv->plat->mdio_bus_data;
|
|
struct device_node *mdio_node = priv->plat->mdio_node;
|
|
+ struct device *dev = ndev->dev.parent;
|
|
int addr, found;
|
|
|
|
if (!mdio_bus_data)
|
|
@@ -237,7 +238,7 @@ int stmmac_mdio_register(struct net_devi
|
|
else
|
|
err = mdiobus_register(new_bus);
|
|
if (err != 0) {
|
|
- netdev_err(ndev, "Cannot register the MDIO bus\n");
|
|
+ dev_err(dev, "Cannot register the MDIO bus\n");
|
|
goto bus_register_fail;
|
|
}
|
|
|
|
@@ -292,7 +293,7 @@ int stmmac_mdio_register(struct net_devi
|
|
}
|
|
|
|
if (!found && !mdio_node) {
|
|
- netdev_warn(ndev, "No PHY found\n");
|
|
+ dev_warn(dev, "No PHY found\n");
|
|
mdiobus_unregister(new_bus);
|
|
mdiobus_free(new_bus);
|
|
return -ENODEV;
|
|
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c
|
|
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c
|
|
@@ -30,42 +30,39 @@
|
|
* negative value of the address means that MAC controller is not connected
|
|
* with PHY.
|
|
*/
|
|
-struct stmmac_pci_dmi_data {
|
|
- const char *name;
|
|
- const char *asset_tag;
|
|
+struct stmmac_pci_func_data {
|
|
unsigned int func;
|
|
int phy_addr;
|
|
};
|
|
|
|
-struct stmmac_pci_info {
|
|
- struct pci_dev *pdev;
|
|
- int (*setup)(struct plat_stmmacenet_data *plat,
|
|
- struct stmmac_pci_info *info);
|
|
- struct stmmac_pci_dmi_data *dmi;
|
|
+struct stmmac_pci_dmi_data {
|
|
+ const struct stmmac_pci_func_data *func;
|
|
+ size_t nfuncs;
|
|
};
|
|
|
|
-static int stmmac_pci_find_phy_addr(struct stmmac_pci_info *info)
|
|
-{
|
|
- const char *name = dmi_get_system_info(DMI_BOARD_NAME);
|
|
- const char *asset_tag = dmi_get_system_info(DMI_BOARD_ASSET_TAG);
|
|
- unsigned int func = PCI_FUNC(info->pdev->devfn);
|
|
- struct stmmac_pci_dmi_data *dmi;
|
|
-
|
|
- /*
|
|
- * Galileo boards with old firmware don't support DMI. We always return
|
|
- * 1 here, so at least first found MAC controller would be probed.
|
|
- */
|
|
- if (!name)
|
|
- return 1;
|
|
+struct stmmac_pci_info {
|
|
+ int (*setup)(struct pci_dev *pdev, struct plat_stmmacenet_data *plat);
|
|
+};
|
|
|
|
- for (dmi = info->dmi; dmi->name && *dmi->name; dmi++) {
|
|
- if (!strcmp(dmi->name, name) && dmi->func == func) {
|
|
- /* If asset tag is provided, match on it as well. */
|
|
- if (dmi->asset_tag && strcmp(dmi->asset_tag, asset_tag))
|
|
- continue;
|
|
- return dmi->phy_addr;
|
|
- }
|
|
- }
|
|
+static int stmmac_pci_find_phy_addr(struct pci_dev *pdev,
|
|
+ const struct dmi_system_id *dmi_list)
|
|
+{
|
|
+ const struct stmmac_pci_func_data *func_data;
|
|
+ const struct stmmac_pci_dmi_data *dmi_data;
|
|
+ const struct dmi_system_id *dmi_id;
|
|
+ int func = PCI_FUNC(pdev->devfn);
|
|
+ size_t n;
|
|
+
|
|
+ dmi_id = dmi_first_match(dmi_list);
|
|
+ if (!dmi_id)
|
|
+ return -ENODEV;
|
|
+
|
|
+ dmi_data = dmi_id->driver_data;
|
|
+ func_data = dmi_data->func;
|
|
+
|
|
+ for (n = 0; n < dmi_data->nfuncs; n++, func_data++)
|
|
+ if (func_data->func == func)
|
|
+ return func_data->phy_addr;
|
|
|
|
return -ENODEV;
|
|
}
|
|
@@ -100,7 +97,8 @@ static void common_default_data(struct p
|
|
plat->rx_queues_cfg[0].pkt_route = 0x0;
|
|
}
|
|
|
|
-static void stmmac_default_data(struct plat_stmmacenet_data *plat)
|
|
+static int stmmac_default_data(struct pci_dev *pdev,
|
|
+ struct plat_stmmacenet_data *plat)
|
|
{
|
|
/* Set common default data first */
|
|
common_default_data(plat);
|
|
@@ -112,12 +110,77 @@ static void stmmac_default_data(struct p
|
|
plat->dma_cfg->pbl = 32;
|
|
plat->dma_cfg->pblx8 = true;
|
|
/* TODO: AXI */
|
|
+
|
|
+ return 0;
|
|
}
|
|
|
|
-static int quark_default_data(struct plat_stmmacenet_data *plat,
|
|
- struct stmmac_pci_info *info)
|
|
+static const struct stmmac_pci_info stmmac_pci_info = {
|
|
+ .setup = stmmac_default_data,
|
|
+};
|
|
+
|
|
+static const struct stmmac_pci_func_data galileo_stmmac_func_data[] = {
|
|
+ {
|
|
+ .func = 6,
|
|
+ .phy_addr = 1,
|
|
+ },
|
|
+};
|
|
+
|
|
+static const struct stmmac_pci_dmi_data galileo_stmmac_dmi_data = {
|
|
+ .func = galileo_stmmac_func_data,
|
|
+ .nfuncs = ARRAY_SIZE(galileo_stmmac_func_data),
|
|
+};
|
|
+
|
|
+static const struct stmmac_pci_func_data iot2040_stmmac_func_data[] = {
|
|
+ {
|
|
+ .func = 6,
|
|
+ .phy_addr = 1,
|
|
+ },
|
|
+ {
|
|
+ .func = 7,
|
|
+ .phy_addr = 1,
|
|
+ },
|
|
+};
|
|
+
|
|
+static const struct stmmac_pci_dmi_data iot2040_stmmac_dmi_data = {
|
|
+ .func = iot2040_stmmac_func_data,
|
|
+ .nfuncs = ARRAY_SIZE(iot2040_stmmac_func_data),
|
|
+};
|
|
+
|
|
+static const struct dmi_system_id quark_pci_dmi[] = {
|
|
+ {
|
|
+ .matches = {
|
|
+ DMI_EXACT_MATCH(DMI_BOARD_NAME, "Galileo"),
|
|
+ },
|
|
+ .driver_data = (void *)&galileo_stmmac_dmi_data,
|
|
+ },
|
|
+ {
|
|
+ .matches = {
|
|
+ DMI_EXACT_MATCH(DMI_BOARD_NAME, "GalileoGen2"),
|
|
+ },
|
|
+ .driver_data = (void *)&galileo_stmmac_dmi_data,
|
|
+ },
|
|
+ {
|
|
+ .matches = {
|
|
+ DMI_EXACT_MATCH(DMI_BOARD_NAME, "SIMATIC IOT2000"),
|
|
+ DMI_EXACT_MATCH(DMI_BOARD_ASSET_TAG,
|
|
+ "6ES7647-0AA00-0YA2"),
|
|
+ },
|
|
+ .driver_data = (void *)&galileo_stmmac_dmi_data,
|
|
+ },
|
|
+ {
|
|
+ .matches = {
|
|
+ DMI_EXACT_MATCH(DMI_BOARD_NAME, "SIMATIC IOT2000"),
|
|
+ DMI_EXACT_MATCH(DMI_BOARD_ASSET_TAG,
|
|
+ "6ES7647-0AA00-1YA2"),
|
|
+ },
|
|
+ .driver_data = (void *)&iot2040_stmmac_dmi_data,
|
|
+ },
|
|
+ {}
|
|
+};
|
|
+
|
|
+static int quark_default_data(struct pci_dev *pdev,
|
|
+ struct plat_stmmacenet_data *plat)
|
|
{
|
|
- struct pci_dev *pdev = info->pdev;
|
|
int ret;
|
|
|
|
/* Set common default data first */
|
|
@@ -127,9 +190,19 @@ static int quark_default_data(struct pla
|
|
* Refuse to load the driver and register net device if MAC controller
|
|
* does not connect to any PHY interface.
|
|
*/
|
|
- ret = stmmac_pci_find_phy_addr(info);
|
|
- if (ret < 0)
|
|
- return ret;
|
|
+ ret = stmmac_pci_find_phy_addr(pdev, quark_pci_dmi);
|
|
+ if (ret < 0) {
|
|
+ /* Return error to the caller on DMI enabled boards. */
|
|
+ if (dmi_get_system_info(DMI_BOARD_NAME))
|
|
+ return ret;
|
|
+
|
|
+ /*
|
|
+ * Galileo boards with old firmware don't support DMI. We always
|
|
+ * use 1 here as PHY address, so at least the first found MAC
|
|
+ * controller would be probed.
|
|
+ */
|
|
+ ret = 1;
|
|
+ }
|
|
|
|
plat->bus_id = PCI_DEVID(pdev->bus->number, pdev->devfn);
|
|
plat->phy_addr = ret;
|
|
@@ -143,41 +216,8 @@ static int quark_default_data(struct pla
|
|
return 0;
|
|
}
|
|
|
|
-static struct stmmac_pci_dmi_data quark_pci_dmi_data[] = {
|
|
- {
|
|
- .name = "Galileo",
|
|
- .func = 6,
|
|
- .phy_addr = 1,
|
|
- },
|
|
- {
|
|
- .name = "GalileoGen2",
|
|
- .func = 6,
|
|
- .phy_addr = 1,
|
|
- },
|
|
- {
|
|
- .name = "SIMATIC IOT2000",
|
|
- .asset_tag = "6ES7647-0AA00-0YA2",
|
|
- .func = 6,
|
|
- .phy_addr = 1,
|
|
- },
|
|
- {
|
|
- .name = "SIMATIC IOT2000",
|
|
- .asset_tag = "6ES7647-0AA00-1YA2",
|
|
- .func = 6,
|
|
- .phy_addr = 1,
|
|
- },
|
|
- {
|
|
- .name = "SIMATIC IOT2000",
|
|
- .asset_tag = "6ES7647-0AA00-1YA2",
|
|
- .func = 7,
|
|
- .phy_addr = 1,
|
|
- },
|
|
- {}
|
|
-};
|
|
-
|
|
-static struct stmmac_pci_info quark_pci_info = {
|
|
+static const struct stmmac_pci_info quark_pci_info = {
|
|
.setup = quark_default_data,
|
|
- .dmi = quark_pci_dmi_data,
|
|
};
|
|
|
|
/**
|
|
@@ -236,15 +276,9 @@ static int stmmac_pci_probe(struct pci_d
|
|
|
|
pci_set_master(pdev);
|
|
|
|
- if (info) {
|
|
- info->pdev = pdev;
|
|
- if (info->setup) {
|
|
- ret = info->setup(plat, info);
|
|
- if (ret)
|
|
- return ret;
|
|
- }
|
|
- } else
|
|
- stmmac_default_data(plat);
|
|
+ ret = info->setup(pdev, plat);
|
|
+ if (ret)
|
|
+ return ret;
|
|
|
|
pci_enable_msi(pdev);
|
|
|
|
@@ -270,14 +304,21 @@ static void stmmac_pci_remove(struct pci
|
|
|
|
static SIMPLE_DEV_PM_OPS(stmmac_pm_ops, stmmac_suspend, stmmac_resume);
|
|
|
|
-#define STMMAC_VENDOR_ID 0x700
|
|
+/* synthetic ID, no official vendor */
|
|
+#define PCI_VENDOR_ID_STMMAC 0x700
|
|
+
|
|
#define STMMAC_QUARK_ID 0x0937
|
|
#define STMMAC_DEVICE_ID 0x1108
|
|
|
|
+#define STMMAC_DEVICE(vendor_id, dev_id, info) { \
|
|
+ PCI_VDEVICE(vendor_id, dev_id), \
|
|
+ .driver_data = (kernel_ulong_t)&info \
|
|
+ }
|
|
+
|
|
static const struct pci_device_id stmmac_id_table[] = {
|
|
- {PCI_DEVICE(STMMAC_VENDOR_ID, STMMAC_DEVICE_ID)},
|
|
- {PCI_DEVICE(PCI_VENDOR_ID_STMICRO, PCI_DEVICE_ID_STMICRO_MAC)},
|
|
- {PCI_VDEVICE(INTEL, STMMAC_QUARK_ID), (kernel_ulong_t)&quark_pci_info},
|
|
+ STMMAC_DEVICE(STMMAC, STMMAC_DEVICE_ID, stmmac_pci_info),
|
|
+ STMMAC_DEVICE(STMICRO, PCI_DEVICE_ID_STMICRO_MAC, stmmac_pci_info),
|
|
+ STMMAC_DEVICE(INTEL, STMMAC_QUARK_ID, quark_pci_info),
|
|
{}
|
|
};
|
|
|
|
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
|
|
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
|
|
@@ -309,6 +309,13 @@ static int stmmac_dt_phy(struct plat_stm
|
|
struct device_node *np, struct device *dev)
|
|
{
|
|
bool mdio = true;
|
|
+ static const struct of_device_id need_mdio_ids[] = {
|
|
+ { .compatible = "snps,dwc-qos-ethernet-4.10" },
|
|
+ { .compatible = "allwinner,sun8i-a83t-emac" },
|
|
+ { .compatible = "allwinner,sun8i-h3-emac" },
|
|
+ { .compatible = "allwinner,sun8i-v3s-emac" },
|
|
+ { .compatible = "allwinner,sun50i-a64-emac" },
|
|
+ };
|
|
|
|
/* If phy-handle property is passed from DT, use it as the PHY */
|
|
plat->phy_node = of_parse_phandle(np, "phy-handle", 0);
|
|
@@ -325,8 +332,7 @@ static int stmmac_dt_phy(struct plat_stm
|
|
mdio = false;
|
|
}
|
|
|
|
- /* exception for dwmac-dwc-qos-eth glue logic */
|
|
- if (of_device_is_compatible(np, "snps,dwc-qos-ethernet-4.10")) {
|
|
+ if (of_match_node(need_mdio_ids, np)) {
|
|
plat->mdio_node = of_get_child_by_name(np, "mdio");
|
|
} else {
|
|
/**
|
|
--- a/include/linux/stmmac.h
|
|
+++ b/include/linux/stmmac.h
|
|
@@ -177,6 +177,7 @@ struct plat_stmmacenet_data {
|
|
void (*fix_mac_speed)(void *priv, unsigned int speed);
|
|
int (*init)(struct platform_device *pdev, void *priv);
|
|
void (*exit)(struct platform_device *pdev, void *priv);
|
|
+ struct mac_device_info *(*setup)(void *priv);
|
|
void *bsp_priv;
|
|
struct clk *stmmac_clk;
|
|
struct clk *pclk;
|
|
@@ -185,6 +186,7 @@ struct plat_stmmacenet_data {
|
|
struct reset_control *stmmac_rst;
|
|
struct stmmac_axi *axi;
|
|
int has_gmac4;
|
|
+ bool has_sun8i;
|
|
bool tso_en;
|
|
int mac_port_sel_speed;
|
|
bool en_tx_lpi_clockgating;
|