mirror of
https://github.com/openwrt/openwrt.git
synced 2024-12-20 22:23:27 +00:00
3789b1f5a0
Signed-off-by: Felix Fietkau <nbd@openwrt.org> SVN-Revision: 47593
4259 lines
127 KiB
Diff
4259 lines
127 KiB
Diff
--- a/include/linux/stmmac.h
|
|
+++ b/include/linux/stmmac.h
|
|
@@ -99,6 +99,7 @@ struct plat_stmmacenet_data {
|
|
int phy_addr;
|
|
int interface;
|
|
struct stmmac_mdio_bus_data *mdio_bus_data;
|
|
+ struct device_node *phy_node;
|
|
struct stmmac_dma_cfg *dma_cfg;
|
|
int clk_csr;
|
|
int has_gmac;
|
|
@@ -114,32 +115,12 @@ struct plat_stmmacenet_data {
|
|
int maxmtu;
|
|
int multicast_filter_bins;
|
|
int unicast_filter_entries;
|
|
+ int tx_fifo_size;
|
|
+ int rx_fifo_size;
|
|
void (*fix_mac_speed)(void *priv, unsigned int speed);
|
|
void (*bus_setup)(void __iomem *ioaddr);
|
|
- void *(*setup)(struct platform_device *pdev);
|
|
- void (*free)(struct platform_device *pdev, void *priv);
|
|
int (*init)(struct platform_device *pdev, void *priv);
|
|
void (*exit)(struct platform_device *pdev, void *priv);
|
|
- void *custom_cfg;
|
|
- void *custom_data;
|
|
void *bsp_priv;
|
|
};
|
|
-
|
|
-/* of_data for SoC glue layer device tree bindings */
|
|
-
|
|
-struct stmmac_of_data {
|
|
- int has_gmac;
|
|
- int enh_desc;
|
|
- int tx_coe;
|
|
- int rx_coe;
|
|
- int bugged_jumbo;
|
|
- int pmt;
|
|
- int riwt_off;
|
|
- void (*fix_mac_speed)(void *priv, unsigned int speed);
|
|
- void (*bus_setup)(void __iomem *ioaddr);
|
|
- void *(*setup)(struct platform_device *pdev);
|
|
- void (*free)(struct platform_device *pdev, void *priv);
|
|
- int (*init)(struct platform_device *pdev, void *priv);
|
|
- void (*exit)(struct platform_device *pdev, void *priv);
|
|
-};
|
|
#endif
|
|
--- a/drivers/net/ethernet/stmicro/Kconfig
|
|
+++ b/drivers/net/ethernet/stmicro/Kconfig
|
|
@@ -7,9 +7,7 @@ config NET_VENDOR_STMICRO
|
|
default y
|
|
depends on HAS_IOMEM
|
|
---help---
|
|
- If you have a network (Ethernet) card belonging to this class, say Y
|
|
- and read the Ethernet-HOWTO, available from
|
|
- <http://www.tldp.org/docs.html#howto>.
|
|
+ If you have a network (Ethernet) card belonging to this class, say Y.
|
|
|
|
Note that the answer to this question doesn't directly affect the
|
|
kernel: saying N will just cause the configurator to skip all
|
|
--- a/drivers/net/ethernet/stmicro/stmmac/Kconfig
|
|
+++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig
|
|
@@ -14,21 +14,54 @@ config STMMAC_ETH
|
|
if STMMAC_ETH
|
|
|
|
config STMMAC_PLATFORM
|
|
- bool "STMMAC Platform bus support"
|
|
+ tristate "STMMAC Platform bus support"
|
|
depends on STMMAC_ETH
|
|
+ select MFD_SYSCON
|
|
default y
|
|
---help---
|
|
- This selects the platform specific bus support for
|
|
- the stmmac device driver. This is the driver used
|
|
- on many embedded STM platforms based on ARM and SuperH
|
|
- processors.
|
|
+ This selects the platform specific bus support for the stmmac driver.
|
|
+ This is the driver used on several SoCs:
|
|
+ STi, Allwinner, Amlogic Meson, Altera SOCFPGA.
|
|
+
|
|
If you have a controller with this interface, say Y or M here.
|
|
|
|
If unsure, say N.
|
|
|
|
+if STMMAC_PLATFORM
|
|
+
|
|
+config DWMAC_GENERIC
|
|
+ tristate "Generic driver for DWMAC"
|
|
+ default STMMAC_PLATFORM
|
|
+ ---help---
|
|
+ Generic DWMAC driver for platforms that don't require any
|
|
+ platform specific code to function or is using platform
|
|
+ data for setup.
|
|
+
|
|
+config DWMAC_IPQ806X
|
|
+ tristate "QCA IPQ806x DWMAC support"
|
|
+ default ARCH_QCOM
|
|
+ depends on OF
|
|
+ select MFD_SYSCON
|
|
+ help
|
|
+ Support for QCA IPQ806X DWMAC Ethernet.
|
|
+
|
|
+ This selects the IPQ806x SoC glue layer support for the stmmac
|
|
+ device driver. This driver does not use any of the hardware
|
|
+ acceleration features available on this SoC. Network devices
|
|
+ will behave like standard non-accelerated ethernet interfaces.
|
|
+
|
|
+config DWMAC_LPC18XX
|
|
+ tristate "NXP LPC18xx/43xx DWMAC support"
|
|
+ default ARCH_LPC18XX
|
|
+ depends on OF
|
|
+ select MFD_SYSCON
|
|
+ ---help---
|
|
+ Support for NXP LPC18xx/43xx DWMAC Ethernet.
|
|
+
|
|
config DWMAC_MESON
|
|
- bool "Amlogic Meson dwmac support"
|
|
- depends on STMMAC_PLATFORM && ARCH_MESON
|
|
+ tristate "Amlogic Meson dwmac support"
|
|
+ default ARCH_MESON
|
|
+ depends on OF
|
|
help
|
|
Support for Ethernet controller on Amlogic Meson SoCs.
|
|
|
|
@@ -36,9 +69,22 @@ config DWMAC_MESON
|
|
the stmmac device driver. This driver is used for Meson6 and
|
|
Meson8 SoCs.
|
|
|
|
+config DWMAC_ROCKCHIP
|
|
+ tristate "Rockchip dwmac support"
|
|
+ default ARCH_ROCKCHIP
|
|
+ depends on OF
|
|
+ select MFD_SYSCON
|
|
+ help
|
|
+ Support for Ethernet controller on Rockchip RK3288 SoC.
|
|
+
|
|
+ This selects the Rockchip RK3288 SoC glue layer support for
|
|
+ the stmmac device driver.
|
|
+
|
|
config DWMAC_SOCFPGA
|
|
- bool "SOCFPGA dwmac support"
|
|
- depends on STMMAC_PLATFORM && MFD_SYSCON && (ARCH_SOCFPGA || COMPILE_TEST)
|
|
+ tristate "SOCFPGA dwmac support"
|
|
+ default ARCH_SOCFPGA
|
|
+ depends on OF
|
|
+ select MFD_SYSCON
|
|
help
|
|
Support for ethernet controller on Altera SOCFPGA
|
|
|
|
@@ -46,21 +92,11 @@ config DWMAC_SOCFPGA
|
|
for the stmmac device driver. This driver is used for
|
|
arria5 and cyclone5 FPGA SoCs.
|
|
|
|
-config DWMAC_SUNXI
|
|
- bool "Allwinner GMAC support"
|
|
- depends on STMMAC_PLATFORM && ARCH_SUNXI
|
|
- default y
|
|
- ---help---
|
|
- Support for Allwinner A20/A31 GMAC ethernet controllers.
|
|
-
|
|
- This selects Allwinner SoC glue layer support for the
|
|
- stmmac device driver. This driver is used for A20/A31
|
|
- GMAC ethernet controller.
|
|
-
|
|
config DWMAC_STI
|
|
- bool "STi GMAC support"
|
|
- depends on STMMAC_PLATFORM && ARCH_STI
|
|
- default y
|
|
+ tristate "STi GMAC support"
|
|
+ default ARCH_STI
|
|
+ depends on OF
|
|
+ select MFD_SYSCON
|
|
---help---
|
|
Support for ethernet controller on STi SOCs.
|
|
|
|
@@ -68,8 +104,20 @@ config DWMAC_STI
|
|
device driver. This driver is used on for the STi series
|
|
SOCs GMAC ethernet controller.
|
|
|
|
+config DWMAC_SUNXI
|
|
+ tristate "Allwinner GMAC support"
|
|
+ default ARCH_SUNXI
|
|
+ depends on OF
|
|
+ ---help---
|
|
+ Support for Allwinner A20/A31 GMAC ethernet controllers.
|
|
+
|
|
+ This selects Allwinner SoC glue layer support for the
|
|
+ stmmac device driver. This driver is used for A20/A31
|
|
+ GMAC ethernet controller.
|
|
+endif
|
|
+
|
|
config STMMAC_PCI
|
|
- bool "STMMAC PCI bus support"
|
|
+ tristate "STMMAC PCI bus support"
|
|
depends on STMMAC_ETH && PCI
|
|
---help---
|
|
This is to select the Synopsys DWMAC available on PCI devices,
|
|
@@ -79,22 +127,4 @@ config STMMAC_PCI
|
|
D1215994A VIRTEX FPGA board.
|
|
|
|
If unsure, say N.
|
|
-
|
|
-config STMMAC_DEBUG_FS
|
|
- bool "Enable monitoring via sysFS "
|
|
- default n
|
|
- depends on STMMAC_ETH && DEBUG_FS
|
|
- ---help---
|
|
- The stmmac entry in /sys reports DMA TX/RX rings
|
|
- or (if supported) the HW cap register.
|
|
-
|
|
-config STMMAC_DA
|
|
- bool "STMMAC DMA arbitration scheme"
|
|
- default n
|
|
- ---help---
|
|
- Selecting this option, rx has priority over Tx (only for Giga
|
|
- Ethernet device).
|
|
- By default, the DMA arbitration scheme is based on Round-robin
|
|
- (rx:tx priority is 1:1).
|
|
-
|
|
endif
|
|
--- a/drivers/net/ethernet/stmicro/stmmac/Makefile
|
|
+++ b/drivers/net/ethernet/stmicro/stmmac/Makefile
|
|
@@ -1,11 +1,20 @@
|
|
obj-$(CONFIG_STMMAC_ETH) += stmmac.o
|
|
-stmmac-$(CONFIG_STMMAC_PLATFORM) += stmmac_platform.o
|
|
-stmmac-$(CONFIG_STMMAC_PCI) += stmmac_pci.o
|
|
-stmmac-$(CONFIG_DWMAC_MESON) += dwmac-meson.o
|
|
-stmmac-$(CONFIG_DWMAC_SUNXI) += dwmac-sunxi.o
|
|
-stmmac-$(CONFIG_DWMAC_STI) += dwmac-sti.o
|
|
-stmmac-$(CONFIG_DWMAC_SOCFPGA) += dwmac-socfpga.o
|
|
stmmac-objs:= stmmac_main.o stmmac_ethtool.o stmmac_mdio.o ring_mode.o \
|
|
- chain_mode.o dwmac_lib.o dwmac1000_core.o dwmac1000_dma.o \
|
|
- dwmac100_core.o dwmac100_dma.o enh_desc.o norm_desc.o \
|
|
+ chain_mode.o dwmac_lib.o dwmac1000_core.o dwmac1000_dma.o \
|
|
+ dwmac100_core.o dwmac100_dma.o enh_desc.o norm_desc.o \
|
|
mmc_core.o stmmac_hwtstamp.o stmmac_ptp.o $(stmmac-y)
|
|
+
|
|
+# Ordering matters. Generic driver must be last.
|
|
+obj-$(CONFIG_STMMAC_PLATFORM) += stmmac-platform.o
|
|
+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_STI) += dwmac-sti.o
|
|
+obj-$(CONFIG_DWMAC_SUNXI) += dwmac-sunxi.o
|
|
+obj-$(CONFIG_DWMAC_GENERIC) += dwmac-generic.o
|
|
+stmmac-platform-objs:= stmmac_platform.o
|
|
+
|
|
+obj-$(CONFIG_STMMAC_PCI) += stmmac-pci.o
|
|
+stmmac-pci-objs:= stmmac_pci.o
|
|
--- a/drivers/net/ethernet/stmicro/stmmac/common.h
|
|
+++ b/drivers/net/ethernet/stmicro/stmmac/common.h
|
|
@@ -44,6 +44,7 @@
|
|
#undef FRAME_FILTER_DEBUG
|
|
/* #define FRAME_FILTER_DEBUG */
|
|
|
|
+/* Extra statistic and debug information exposed by ethtool */
|
|
struct stmmac_extra_stats {
|
|
/* Transmit errors */
|
|
unsigned long tx_underflow ____cacheline_aligned;
|
|
@@ -149,7 +150,7 @@ struct stmmac_extra_stats {
|
|
#define MAC_CSR_H_FRQ_MASK 0x20
|
|
|
|
#define HASH_TABLE_SIZE 64
|
|
-#define PAUSE_TIME 0x200
|
|
+#define PAUSE_TIME 0xffff
|
|
|
|
/* Flow Control defines */
|
|
#define FLOW_OFF 0
|
|
@@ -220,6 +221,7 @@ enum dma_irq_status {
|
|
handle_tx = 0x8,
|
|
};
|
|
|
|
+/* EEE and LPI defines */
|
|
#define CORE_IRQ_TX_PATH_IN_LPI_MODE (1 << 0)
|
|
#define CORE_IRQ_TX_PATH_EXIT_LPI_MODE (1 << 1)
|
|
#define CORE_IRQ_RX_PATH_IN_LPI_MODE (1 << 2)
|
|
@@ -229,6 +231,7 @@ enum dma_irq_status {
|
|
#define CORE_PCS_LINK_STATUS (1 << 6)
|
|
#define CORE_RGMII_IRQ (1 << 7)
|
|
|
|
+/* Physical Coding Sublayer */
|
|
struct rgmii_adv {
|
|
unsigned int pause;
|
|
unsigned int duplex;
|
|
@@ -294,6 +297,7 @@ struct dma_features {
|
|
|
|
#define JUMBO_LEN 9000
|
|
|
|
+/* Descriptors helpers */
|
|
struct stmmac_desc_ops {
|
|
/* DMA RX descriptor ring initialization */
|
|
void (*init_rx_desc) (struct dma_desc *p, int disable_rx_ic, int mode,
|
|
@@ -341,6 +345,10 @@ struct stmmac_desc_ops {
|
|
int (*get_rx_timestamp_status) (void *desc, u32 ats);
|
|
};
|
|
|
|
+extern const struct stmmac_desc_ops enh_desc_ops;
|
|
+extern const struct stmmac_desc_ops ndesc_ops;
|
|
+
|
|
+/* Specific DMA helpers */
|
|
struct stmmac_dma_ops {
|
|
/* DMA core initialization */
|
|
int (*init) (void __iomem *ioaddr, int pbl, int fb, int mb,
|
|
@@ -349,7 +357,8 @@ struct stmmac_dma_ops {
|
|
void (*dump_regs) (void __iomem *ioaddr);
|
|
/* Set tx/rx threshold in the csr6 register
|
|
* An invalid value enables the store-and-forward mode */
|
|
- void (*dma_mode) (void __iomem *ioaddr, int txmode, int rxmode);
|
|
+ void (*dma_mode)(void __iomem *ioaddr, int txmode, int rxmode,
|
|
+ int rxfifosz);
|
|
/* To track extra statistic (if supported) */
|
|
void (*dma_diagnostic_fr) (void *data, struct stmmac_extra_stats *x,
|
|
void __iomem *ioaddr);
|
|
@@ -370,6 +379,7 @@ struct stmmac_dma_ops {
|
|
|
|
struct mac_device_info;
|
|
|
|
+/* Helpers to program the MAC core */
|
|
struct stmmac_ops {
|
|
/* MAC core initialization */
|
|
void (*core_init)(struct mac_device_info *hw, int mtu);
|
|
@@ -400,6 +410,7 @@ struct stmmac_ops {
|
|
void (*get_adv)(struct mac_device_info *hw, struct rgmii_adv *adv);
|
|
};
|
|
|
|
+/* PTP and HW Timer helpers */
|
|
struct stmmac_hwtimestamp {
|
|
void (*config_hw_tstamping) (void __iomem *ioaddr, u32 data);
|
|
void (*config_sub_second_increment) (void __iomem *ioaddr);
|
|
@@ -410,6 +421,8 @@ struct stmmac_hwtimestamp {
|
|
u64(*get_systime) (void __iomem *ioaddr);
|
|
};
|
|
|
|
+extern const struct stmmac_hwtimestamp stmmac_ptp;
|
|
+
|
|
struct mac_link {
|
|
int port;
|
|
int duplex;
|
|
@@ -421,6 +434,7 @@ struct mii_regs {
|
|
unsigned int data; /* MII Data */
|
|
};
|
|
|
|
+/* Helpers to manage the descriptors for chain and ring modes */
|
|
struct stmmac_mode_ops {
|
|
void (*init) (void *des, dma_addr_t phy_addr, unsigned int size,
|
|
unsigned int extend_desc);
|
|
--- /dev/null
|
|
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-generic.c
|
|
@@ -0,0 +1,81 @@
|
|
+/*
|
|
+ * Generic DWMAC platform driver
|
|
+ *
|
|
+ * Copyright (C) 2007-2011 STMicroelectronics Ltd
|
|
+ * Copyright (C) 2015 Joachim Eastwood <manabian@gmail.com>
|
|
+ *
|
|
+ * This file is licensed under the terms of the GNU General Public
|
|
+ * License version 2. This program is licensed "as is" without any
|
|
+ * warranty of any kind, whether express or implied.
|
|
+ */
|
|
+
|
|
+#include <linux/module.h>
|
|
+#include <linux/of.h>
|
|
+#include <linux/platform_device.h>
|
|
+
|
|
+#include "stmmac.h"
|
|
+#include "stmmac_platform.h"
|
|
+
|
|
+static int dwmac_generic_probe(struct platform_device *pdev)
|
|
+{
|
|
+ struct plat_stmmacenet_data *plat_dat;
|
|
+ struct stmmac_resources stmmac_res;
|
|
+ int ret;
|
|
+
|
|
+ ret = stmmac_get_platform_resources(pdev, &stmmac_res);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ if (pdev->dev.of_node) {
|
|
+ plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac);
|
|
+ if (IS_ERR(plat_dat)) {
|
|
+ dev_err(&pdev->dev, "dt configuration failed\n");
|
|
+ return PTR_ERR(plat_dat);
|
|
+ }
|
|
+ } else {
|
|
+ plat_dat = dev_get_platdata(&pdev->dev);
|
|
+ if (!plat_dat) {
|
|
+ dev_err(&pdev->dev, "no platform data provided\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ /* Set default value for multicast hash bins */
|
|
+ plat_dat->multicast_filter_bins = HASH_TABLE_SIZE;
|
|
+
|
|
+ /* Set default value for unicast filter entries */
|
|
+ plat_dat->unicast_filter_entries = 1;
|
|
+ }
|
|
+
|
|
+ /* Custom initialisation (if needed) */
|
|
+ if (plat_dat->init) {
|
|
+ ret = plat_dat->init(pdev, plat_dat->bsp_priv);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ return stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
|
|
+}
|
|
+
|
|
+static const struct of_device_id dwmac_generic_match[] = {
|
|
+ { .compatible = "st,spear600-gmac"},
|
|
+ { .compatible = "snps,dwmac-3.610"},
|
|
+ { .compatible = "snps,dwmac-3.70a"},
|
|
+ { .compatible = "snps,dwmac-3.710"},
|
|
+ { .compatible = "snps,dwmac"},
|
|
+ { }
|
|
+};
|
|
+MODULE_DEVICE_TABLE(of, dwmac_generic_match);
|
|
+
|
|
+static struct platform_driver dwmac_generic_driver = {
|
|
+ .probe = dwmac_generic_probe,
|
|
+ .remove = stmmac_pltfr_remove,
|
|
+ .driver = {
|
|
+ .name = STMMAC_RESOURCE_NAME,
|
|
+ .pm = &stmmac_pltfr_pm_ops,
|
|
+ .of_match_table = of_match_ptr(dwmac_generic_match),
|
|
+ },
|
|
+};
|
|
+module_platform_driver(dwmac_generic_driver);
|
|
+
|
|
+MODULE_DESCRIPTION("Generic dwmac driver");
|
|
+MODULE_LICENSE("GPL v2");
|
|
--- /dev/null
|
|
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c
|
|
@@ -0,0 +1,373 @@
|
|
+/*
|
|
+ * Qualcomm Atheros IPQ806x GMAC glue layer
|
|
+ *
|
|
+ * Copyright (C) 2015 The Linux Foundation
|
|
+ *
|
|
+ * Permission to use, copy, modify, and/or distribute this software for any
|
|
+ * purpose with or without fee is hereby granted, provided that the above
|
|
+ * copyright notice and this permission notice appear in all copies.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
+ */
|
|
+
|
|
+#include <linux/device.h>
|
|
+#include <linux/platform_device.h>
|
|
+#include <linux/phy.h>
|
|
+#include <linux/regmap.h>
|
|
+#include <linux/clk.h>
|
|
+#include <linux/reset.h>
|
|
+#include <linux/of_net.h>
|
|
+#include <linux/mfd/syscon.h>
|
|
+#include <linux/stmmac.h>
|
|
+#include <linux/of_mdio.h>
|
|
+#include <linux/module.h>
|
|
+
|
|
+#include "stmmac_platform.h"
|
|
+
|
|
+#define NSS_COMMON_CLK_GATE 0x8
|
|
+#define NSS_COMMON_CLK_GATE_PTP_EN(x) BIT(0x10 + x)
|
|
+#define NSS_COMMON_CLK_GATE_RGMII_RX_EN(x) BIT(0x9 + (x * 2))
|
|
+#define NSS_COMMON_CLK_GATE_RGMII_TX_EN(x) BIT(0x8 + (x * 2))
|
|
+#define NSS_COMMON_CLK_GATE_GMII_RX_EN(x) BIT(0x4 + x)
|
|
+#define NSS_COMMON_CLK_GATE_GMII_TX_EN(x) BIT(0x0 + x)
|
|
+
|
|
+#define NSS_COMMON_CLK_DIV0 0xC
|
|
+#define NSS_COMMON_CLK_DIV_OFFSET(x) (x * 8)
|
|
+#define NSS_COMMON_CLK_DIV_MASK 0x7f
|
|
+
|
|
+#define NSS_COMMON_CLK_SRC_CTRL 0x14
|
|
+#define NSS_COMMON_CLK_SRC_CTRL_OFFSET(x) (x)
|
|
+/* Mode is coded on 1 bit but is different depending on the MAC ID:
|
|
+ * MAC0: QSGMII=0 RGMII=1
|
|
+ * MAC1: QSGMII=0 SGMII=0 RGMII=1
|
|
+ * MAC2 & MAC3: QSGMII=0 SGMII=1
|
|
+ */
|
|
+#define NSS_COMMON_CLK_SRC_CTRL_RGMII(x) 1
|
|
+#define NSS_COMMON_CLK_SRC_CTRL_SGMII(x) ((x >= 2) ? 1 : 0)
|
|
+
|
|
+#define NSS_COMMON_MACSEC_CTL 0x28
|
|
+#define NSS_COMMON_MACSEC_CTL_EXT_BYPASS_EN(x) (1 << x)
|
|
+
|
|
+#define NSS_COMMON_GMAC_CTL(x) (0x30 + (x * 4))
|
|
+#define NSS_COMMON_GMAC_CTL_CSYS_REQ BIT(19)
|
|
+#define NSS_COMMON_GMAC_CTL_PHY_IFACE_SEL BIT(16)
|
|
+#define NSS_COMMON_GMAC_CTL_IFG_LIMIT_OFFSET 8
|
|
+#define NSS_COMMON_GMAC_CTL_IFG_OFFSET 0
|
|
+#define NSS_COMMON_GMAC_CTL_IFG_MASK 0x3f
|
|
+
|
|
+#define NSS_COMMON_CLK_DIV_RGMII_1000 1
|
|
+#define NSS_COMMON_CLK_DIV_RGMII_100 9
|
|
+#define NSS_COMMON_CLK_DIV_RGMII_10 99
|
|
+#define NSS_COMMON_CLK_DIV_SGMII_1000 0
|
|
+#define NSS_COMMON_CLK_DIV_SGMII_100 4
|
|
+#define NSS_COMMON_CLK_DIV_SGMII_10 49
|
|
+
|
|
+#define QSGMII_PCS_MODE_CTL 0x68
|
|
+#define QSGMII_PCS_MODE_CTL_AUTONEG_EN(x) BIT((x * 8) + 7)
|
|
+
|
|
+#define QSGMII_PCS_CAL_LCKDT_CTL 0x120
|
|
+#define QSGMII_PCS_CAL_LCKDT_CTL_RST BIT(19)
|
|
+
|
|
+/* Only GMAC1/2/3 support SGMII and their CTL register are not contiguous */
|
|
+#define QSGMII_PHY_SGMII_CTL(x) ((x == 1) ? 0x134 : \
|
|
+ (0x13c + (4 * (x - 2))))
|
|
+#define QSGMII_PHY_CDR_EN BIT(0)
|
|
+#define QSGMII_PHY_RX_FRONT_EN BIT(1)
|
|
+#define QSGMII_PHY_RX_SIGNAL_DETECT_EN BIT(2)
|
|
+#define QSGMII_PHY_TX_DRIVER_EN BIT(3)
|
|
+#define QSGMII_PHY_QSGMII_EN BIT(7)
|
|
+#define QSGMII_PHY_PHASE_LOOP_GAIN_OFFSET 12
|
|
+#define QSGMII_PHY_PHASE_LOOP_GAIN_MASK 0x7
|
|
+#define QSGMII_PHY_RX_DC_BIAS_OFFSET 18
|
|
+#define QSGMII_PHY_RX_DC_BIAS_MASK 0x3
|
|
+#define QSGMII_PHY_RX_INPUT_EQU_OFFSET 20
|
|
+#define QSGMII_PHY_RX_INPUT_EQU_MASK 0x3
|
|
+#define QSGMII_PHY_CDR_PI_SLEW_OFFSET 22
|
|
+#define QSGMII_PHY_CDR_PI_SLEW_MASK 0x3
|
|
+#define QSGMII_PHY_TX_DRV_AMP_OFFSET 28
|
|
+#define QSGMII_PHY_TX_DRV_AMP_MASK 0xf
|
|
+
|
|
+struct ipq806x_gmac {
|
|
+ struct platform_device *pdev;
|
|
+ struct regmap *nss_common;
|
|
+ struct regmap *qsgmii_csr;
|
|
+ uint32_t id;
|
|
+ struct clk *core_clk;
|
|
+ phy_interface_t phy_mode;
|
|
+};
|
|
+
|
|
+static int get_clk_div_sgmii(struct ipq806x_gmac *gmac, unsigned int speed)
|
|
+{
|
|
+ struct device *dev = &gmac->pdev->dev;
|
|
+ int div;
|
|
+
|
|
+ switch (speed) {
|
|
+ case SPEED_1000:
|
|
+ div = NSS_COMMON_CLK_DIV_SGMII_1000;
|
|
+ break;
|
|
+
|
|
+ case SPEED_100:
|
|
+ div = NSS_COMMON_CLK_DIV_SGMII_100;
|
|
+ break;
|
|
+
|
|
+ case SPEED_10:
|
|
+ div = NSS_COMMON_CLK_DIV_SGMII_10;
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ dev_err(dev, "Speed %dMbps not supported in SGMII\n", speed);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ return div;
|
|
+}
|
|
+
|
|
+static int get_clk_div_rgmii(struct ipq806x_gmac *gmac, unsigned int speed)
|
|
+{
|
|
+ struct device *dev = &gmac->pdev->dev;
|
|
+ int div;
|
|
+
|
|
+ switch (speed) {
|
|
+ case SPEED_1000:
|
|
+ div = NSS_COMMON_CLK_DIV_RGMII_1000;
|
|
+ break;
|
|
+
|
|
+ case SPEED_100:
|
|
+ div = NSS_COMMON_CLK_DIV_RGMII_100;
|
|
+ break;
|
|
+
|
|
+ case SPEED_10:
|
|
+ div = NSS_COMMON_CLK_DIV_RGMII_10;
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ dev_err(dev, "Speed %dMbps not supported in RGMII\n", speed);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ return div;
|
|
+}
|
|
+
|
|
+static int ipq806x_gmac_set_speed(struct ipq806x_gmac *gmac, unsigned int speed)
|
|
+{
|
|
+ uint32_t clk_bits, val;
|
|
+ int div;
|
|
+
|
|
+ switch (gmac->phy_mode) {
|
|
+ case PHY_INTERFACE_MODE_RGMII:
|
|
+ div = get_clk_div_rgmii(gmac, speed);
|
|
+ clk_bits = NSS_COMMON_CLK_GATE_RGMII_RX_EN(gmac->id) |
|
|
+ NSS_COMMON_CLK_GATE_RGMII_TX_EN(gmac->id);
|
|
+ break;
|
|
+
|
|
+ case PHY_INTERFACE_MODE_SGMII:
|
|
+ div = get_clk_div_sgmii(gmac, speed);
|
|
+ clk_bits = NSS_COMMON_CLK_GATE_GMII_RX_EN(gmac->id) |
|
|
+ NSS_COMMON_CLK_GATE_GMII_TX_EN(gmac->id);
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ dev_err(&gmac->pdev->dev, "Unsupported PHY mode: \"%s\"\n",
|
|
+ phy_modes(gmac->phy_mode));
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ /* Disable the clocks */
|
|
+ regmap_read(gmac->nss_common, NSS_COMMON_CLK_GATE, &val);
|
|
+ val &= ~clk_bits;
|
|
+ regmap_write(gmac->nss_common, NSS_COMMON_CLK_GATE, val);
|
|
+
|
|
+ /* Set the divider */
|
|
+ regmap_read(gmac->nss_common, NSS_COMMON_CLK_DIV0, &val);
|
|
+ val &= ~(NSS_COMMON_CLK_DIV_MASK
|
|
+ << NSS_COMMON_CLK_DIV_OFFSET(gmac->id));
|
|
+ val |= div << NSS_COMMON_CLK_DIV_OFFSET(gmac->id);
|
|
+ regmap_write(gmac->nss_common, NSS_COMMON_CLK_DIV0, val);
|
|
+
|
|
+ /* Enable the clock back */
|
|
+ regmap_read(gmac->nss_common, NSS_COMMON_CLK_GATE, &val);
|
|
+ val |= clk_bits;
|
|
+ regmap_write(gmac->nss_common, NSS_COMMON_CLK_GATE, val);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void *ipq806x_gmac_of_parse(struct ipq806x_gmac *gmac)
|
|
+{
|
|
+ struct device *dev = &gmac->pdev->dev;
|
|
+
|
|
+ gmac->phy_mode = of_get_phy_mode(dev->of_node);
|
|
+ if (gmac->phy_mode < 0) {
|
|
+ dev_err(dev, "missing phy mode property\n");
|
|
+ return ERR_PTR(-EINVAL);
|
|
+ }
|
|
+
|
|
+ if (of_property_read_u32(dev->of_node, "qcom,id", &gmac->id) < 0) {
|
|
+ dev_err(dev, "missing qcom id property\n");
|
|
+ return ERR_PTR(-EINVAL);
|
|
+ }
|
|
+
|
|
+ /* The GMACs are called 1 to 4 in the documentation, but to simplify the
|
|
+ * code and keep it consistent with the Linux convention, we'll number
|
|
+ * them from 0 to 3 here.
|
|
+ */
|
|
+ if (gmac->id < 0 || gmac->id > 3) {
|
|
+ dev_err(dev, "invalid gmac id\n");
|
|
+ return ERR_PTR(-EINVAL);
|
|
+ }
|
|
+
|
|
+ gmac->core_clk = devm_clk_get(dev, "stmmaceth");
|
|
+ if (IS_ERR(gmac->core_clk)) {
|
|
+ dev_err(dev, "missing stmmaceth clk property\n");
|
|
+ return gmac->core_clk;
|
|
+ }
|
|
+ clk_set_rate(gmac->core_clk, 266000000);
|
|
+
|
|
+ /* Setup the register map for the nss common registers */
|
|
+ gmac->nss_common = syscon_regmap_lookup_by_phandle(dev->of_node,
|
|
+ "qcom,nss-common");
|
|
+ if (IS_ERR(gmac->nss_common)) {
|
|
+ dev_err(dev, "missing nss-common node\n");
|
|
+ return gmac->nss_common;
|
|
+ }
|
|
+
|
|
+ /* Setup the register map for the qsgmii csr registers */
|
|
+ gmac->qsgmii_csr = syscon_regmap_lookup_by_phandle(dev->of_node,
|
|
+ "qcom,qsgmii-csr");
|
|
+ if (IS_ERR(gmac->qsgmii_csr)) {
|
|
+ dev_err(dev, "missing qsgmii-csr node\n");
|
|
+ return gmac->qsgmii_csr;
|
|
+ }
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static void ipq806x_gmac_fix_mac_speed(void *priv, unsigned int speed)
|
|
+{
|
|
+ struct ipq806x_gmac *gmac = priv;
|
|
+
|
|
+ ipq806x_gmac_set_speed(gmac, speed);
|
|
+}
|
|
+
|
|
+static int ipq806x_gmac_probe(struct platform_device *pdev)
|
|
+{
|
|
+ struct plat_stmmacenet_data *plat_dat;
|
|
+ struct stmmac_resources stmmac_res;
|
|
+ struct device *dev = &pdev->dev;
|
|
+ struct ipq806x_gmac *gmac;
|
|
+ int val;
|
|
+ void *err;
|
|
+
|
|
+ val = stmmac_get_platform_resources(pdev, &stmmac_res);
|
|
+ if (val)
|
|
+ return val;
|
|
+
|
|
+ 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->pdev = pdev;
|
|
+
|
|
+ err = ipq806x_gmac_of_parse(gmac);
|
|
+ if (IS_ERR(err)) {
|
|
+ dev_err(dev, "device tree parsing error\n");
|
|
+ return PTR_ERR(err);
|
|
+ }
|
|
+
|
|
+ regmap_write(gmac->qsgmii_csr, QSGMII_PCS_CAL_LCKDT_CTL,
|
|
+ QSGMII_PCS_CAL_LCKDT_CTL_RST);
|
|
+
|
|
+ /* Inter frame gap is set to 12 */
|
|
+ val = 12 << NSS_COMMON_GMAC_CTL_IFG_OFFSET |
|
|
+ 12 << NSS_COMMON_GMAC_CTL_IFG_LIMIT_OFFSET;
|
|
+ /* We also initiate an AXI low power exit request */
|
|
+ val |= NSS_COMMON_GMAC_CTL_CSYS_REQ;
|
|
+ switch (gmac->phy_mode) {
|
|
+ case PHY_INTERFACE_MODE_RGMII:
|
|
+ val |= NSS_COMMON_GMAC_CTL_PHY_IFACE_SEL;
|
|
+ break;
|
|
+ case PHY_INTERFACE_MODE_SGMII:
|
|
+ val &= ~NSS_COMMON_GMAC_CTL_PHY_IFACE_SEL;
|
|
+ break;
|
|
+ default:
|
|
+ dev_err(&pdev->dev, "Unsupported PHY mode: \"%s\"\n",
|
|
+ phy_modes(gmac->phy_mode));
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ regmap_write(gmac->nss_common, NSS_COMMON_GMAC_CTL(gmac->id), val);
|
|
+
|
|
+ /* Configure the clock src according to the mode */
|
|
+ regmap_read(gmac->nss_common, NSS_COMMON_CLK_SRC_CTRL, &val);
|
|
+ val &= ~(1 << NSS_COMMON_CLK_SRC_CTRL_OFFSET(gmac->id));
|
|
+ switch (gmac->phy_mode) {
|
|
+ case PHY_INTERFACE_MODE_RGMII:
|
|
+ val |= NSS_COMMON_CLK_SRC_CTRL_RGMII(gmac->id) <<
|
|
+ NSS_COMMON_CLK_SRC_CTRL_OFFSET(gmac->id);
|
|
+ break;
|
|
+ case PHY_INTERFACE_MODE_SGMII:
|
|
+ val |= NSS_COMMON_CLK_SRC_CTRL_SGMII(gmac->id) <<
|
|
+ NSS_COMMON_CLK_SRC_CTRL_OFFSET(gmac->id);
|
|
+ break;
|
|
+ default:
|
|
+ dev_err(&pdev->dev, "Unsupported PHY mode: \"%s\"\n",
|
|
+ phy_modes(gmac->phy_mode));
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ regmap_write(gmac->nss_common, NSS_COMMON_CLK_SRC_CTRL, val);
|
|
+
|
|
+ /* Enable PTP clock */
|
|
+ regmap_read(gmac->nss_common, NSS_COMMON_CLK_GATE, &val);
|
|
+ val |= NSS_COMMON_CLK_GATE_PTP_EN(gmac->id);
|
|
+ regmap_write(gmac->nss_common, NSS_COMMON_CLK_GATE, val);
|
|
+
|
|
+ if (gmac->phy_mode == PHY_INTERFACE_MODE_SGMII) {
|
|
+ regmap_write(gmac->qsgmii_csr, QSGMII_PHY_SGMII_CTL(gmac->id),
|
|
+ QSGMII_PHY_CDR_EN |
|
|
+ QSGMII_PHY_RX_FRONT_EN |
|
|
+ QSGMII_PHY_RX_SIGNAL_DETECT_EN |
|
|
+ QSGMII_PHY_TX_DRIVER_EN |
|
|
+ QSGMII_PHY_QSGMII_EN |
|
|
+ 0x4 << QSGMII_PHY_PHASE_LOOP_GAIN_OFFSET |
|
|
+ 0x3 << QSGMII_PHY_RX_DC_BIAS_OFFSET |
|
|
+ 0x1 << QSGMII_PHY_RX_INPUT_EQU_OFFSET |
|
|
+ 0x2 << QSGMII_PHY_CDR_PI_SLEW_OFFSET |
|
|
+ 0xC << QSGMII_PHY_TX_DRV_AMP_OFFSET);
|
|
+ }
|
|
+
|
|
+ plat_dat->has_gmac = true;
|
|
+ plat_dat->bsp_priv = gmac;
|
|
+ plat_dat->fix_mac_speed = ipq806x_gmac_fix_mac_speed;
|
|
+
|
|
+ return stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
|
|
+}
|
|
+
|
|
+static const struct of_device_id ipq806x_gmac_dwmac_match[] = {
|
|
+ { .compatible = "qcom,ipq806x-gmac" },
|
|
+ { }
|
|
+};
|
|
+MODULE_DEVICE_TABLE(of, ipq806x_gmac_dwmac_match);
|
|
+
|
|
+static struct platform_driver ipq806x_gmac_dwmac_driver = {
|
|
+ .probe = ipq806x_gmac_probe,
|
|
+ .remove = stmmac_pltfr_remove,
|
|
+ .driver = {
|
|
+ .name = "ipq806x-gmac-dwmac",
|
|
+ .pm = &stmmac_pltfr_pm_ops,
|
|
+ .of_match_table = ipq806x_gmac_dwmac_match,
|
|
+ },
|
|
+};
|
|
+module_platform_driver(ipq806x_gmac_dwmac_driver);
|
|
+
|
|
+MODULE_AUTHOR("Mathieu Olivari <mathieu@codeaurora.org>");
|
|
+MODULE_DESCRIPTION("Qualcomm Atheros IPQ806x DWMAC specific glue layer");
|
|
+MODULE_LICENSE("Dual BSD/GPL");
|
|
--- /dev/null
|
|
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-lpc18xx.c
|
|
@@ -0,0 +1,86 @@
|
|
+/*
|
|
+ * DWMAC glue for NXP LPC18xx/LPC43xx Ethernet
|
|
+ *
|
|
+ * Copyright (C) 2015 Joachim Eastwood <manabian@gmail.com>
|
|
+ *
|
|
+ * This file is licensed under the terms of the GNU General Public
|
|
+ * License version 2. This program is licensed "as is" without any
|
|
+ * warranty of any kind, whether express or implied.
|
|
+ */
|
|
+
|
|
+#include <linux/mfd/syscon.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/of.h>
|
|
+#include <linux/of_net.h>
|
|
+#include <linux/phy.h>
|
|
+#include <linux/platform_device.h>
|
|
+#include <linux/regmap.h>
|
|
+#include <linux/stmmac.h>
|
|
+
|
|
+#include "stmmac_platform.h"
|
|
+
|
|
+/* Register defines for CREG syscon */
|
|
+#define LPC18XX_CREG_CREG6 0x12c
|
|
+# define LPC18XX_CREG_CREG6_ETHMODE_MASK 0x7
|
|
+# define LPC18XX_CREG_CREG6_ETHMODE_MII 0x0
|
|
+# define LPC18XX_CREG_CREG6_ETHMODE_RMII 0x4
|
|
+
|
|
+static int lpc18xx_dwmac_probe(struct platform_device *pdev)
|
|
+{
|
|
+ struct plat_stmmacenet_data *plat_dat;
|
|
+ struct stmmac_resources stmmac_res;
|
|
+ struct regmap *reg;
|
|
+ u8 ethmode;
|
|
+ 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);
|
|
+
|
|
+ plat_dat->has_gmac = true;
|
|
+
|
|
+ reg = syscon_regmap_lookup_by_compatible("nxp,lpc1850-creg");
|
|
+ if (IS_ERR(reg)) {
|
|
+ dev_err(&pdev->dev, "syscon lookup failed\n");
|
|
+ return PTR_ERR(reg);
|
|
+ }
|
|
+
|
|
+ if (plat_dat->interface == PHY_INTERFACE_MODE_MII) {
|
|
+ ethmode = LPC18XX_CREG_CREG6_ETHMODE_MII;
|
|
+ } else if (plat_dat->interface == PHY_INTERFACE_MODE_RMII) {
|
|
+ ethmode = LPC18XX_CREG_CREG6_ETHMODE_RMII;
|
|
+ } else {
|
|
+ dev_err(&pdev->dev, "Only MII and RMII mode supported\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ regmap_update_bits(reg, LPC18XX_CREG_CREG6,
|
|
+ LPC18XX_CREG_CREG6_ETHMODE_MASK, ethmode);
|
|
+
|
|
+ return stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
|
|
+}
|
|
+
|
|
+static const struct of_device_id lpc18xx_dwmac_match[] = {
|
|
+ { .compatible = "nxp,lpc1850-dwmac" },
|
|
+ { }
|
|
+};
|
|
+MODULE_DEVICE_TABLE(of, lpc18xx_dwmac_match);
|
|
+
|
|
+static struct platform_driver lpc18xx_dwmac_driver = {
|
|
+ .probe = lpc18xx_dwmac_probe,
|
|
+ .remove = stmmac_pltfr_remove,
|
|
+ .driver = {
|
|
+ .name = "lpc18xx-dwmac",
|
|
+ .pm = &stmmac_pltfr_pm_ops,
|
|
+ .of_match_table = lpc18xx_dwmac_match,
|
|
+ },
|
|
+};
|
|
+module_platform_driver(lpc18xx_dwmac_driver);
|
|
+
|
|
+MODULE_AUTHOR("Joachim Eastwood <manabian@gmail.com>");
|
|
+MODULE_DESCRIPTION("DWMAC glue for LPC18xx/43xx Ethernet");
|
|
+MODULE_LICENSE("GPL v2");
|
|
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-meson.c
|
|
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-meson.c
|
|
@@ -15,9 +15,12 @@
|
|
#include <linux/ethtool.h>
|
|
#include <linux/io.h>
|
|
#include <linux/ioport.h>
|
|
+#include <linux/module.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/stmmac.h>
|
|
|
|
+#include "stmmac_platform.h"
|
|
+
|
|
#define ETHMAC_SPEED_100 BIT(1)
|
|
|
|
struct meson_dwmac {
|
|
@@ -44,24 +47,54 @@ static void meson6_dwmac_fix_mac_speed(v
|
|
writel(val, dwmac->reg);
|
|
}
|
|
|
|
-static void *meson6_dwmac_setup(struct platform_device *pdev)
|
|
+static int meson6_dwmac_probe(struct platform_device *pdev)
|
|
{
|
|
+ struct plat_stmmacenet_data *plat_dat;
|
|
+ struct stmmac_resources stmmac_res;
|
|
struct meson_dwmac *dwmac;
|
|
struct resource *res;
|
|
+ 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);
|
|
|
|
dwmac = devm_kzalloc(&pdev->dev, sizeof(*dwmac), GFP_KERNEL);
|
|
if (!dwmac)
|
|
- return ERR_PTR(-ENOMEM);
|
|
+ return -ENOMEM;
|
|
|
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
|
dwmac->reg = devm_ioremap_resource(&pdev->dev, res);
|
|
if (IS_ERR(dwmac->reg))
|
|
- return dwmac->reg;
|
|
+ return PTR_ERR(dwmac->reg);
|
|
+
|
|
+ plat_dat->bsp_priv = dwmac;
|
|
+ plat_dat->fix_mac_speed = meson6_dwmac_fix_mac_speed;
|
|
|
|
- return dwmac;
|
|
+ return stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
|
|
}
|
|
|
|
-const struct stmmac_of_data meson6_dwmac_data = {
|
|
- .setup = meson6_dwmac_setup,
|
|
- .fix_mac_speed = meson6_dwmac_fix_mac_speed,
|
|
+static const struct of_device_id meson6_dwmac_match[] = {
|
|
+ { .compatible = "amlogic,meson6-dwmac" },
|
|
+ { }
|
|
};
|
|
+MODULE_DEVICE_TABLE(of, meson6_dwmac_match);
|
|
+
|
|
+static struct platform_driver meson6_dwmac_driver = {
|
|
+ .probe = meson6_dwmac_probe,
|
|
+ .remove = stmmac_pltfr_remove,
|
|
+ .driver = {
|
|
+ .name = "meson6-dwmac",
|
|
+ .pm = &stmmac_pltfr_pm_ops,
|
|
+ .of_match_table = meson6_dwmac_match,
|
|
+ },
|
|
+};
|
|
+module_platform_driver(meson6_dwmac_driver);
|
|
+
|
|
+MODULE_AUTHOR("Beniamino Galvani <b.galvani@gmail.com>");
|
|
+MODULE_DESCRIPTION("Amlogic Meson DWMAC glue layer");
|
|
+MODULE_LICENSE("GPL v2");
|
|
--- /dev/null
|
|
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c
|
|
@@ -0,0 +1,626 @@
|
|
+/**
|
|
+ * dwmac-rk.c - Rockchip RK3288 DWMAC specific glue layer
|
|
+ *
|
|
+ * Copyright (C) 2014 Chen-Zhi (Roger Chen)
|
|
+ *
|
|
+ * Chen-Zhi (Roger Chen) <roger.chen@rock-chips.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/stmmac.h>
|
|
+#include <linux/bitops.h>
|
|
+#include <linux/clk.h>
|
|
+#include <linux/phy.h>
|
|
+#include <linux/of_net.h>
|
|
+#include <linux/gpio.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/of_gpio.h>
|
|
+#include <linux/of_device.h>
|
|
+#include <linux/platform_device.h>
|
|
+#include <linux/regulator/consumer.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/mfd/syscon.h>
|
|
+#include <linux/regmap.h>
|
|
+
|
|
+#include "stmmac_platform.h"
|
|
+
|
|
+struct rk_priv_data;
|
|
+struct rk_gmac_ops {
|
|
+ void (*set_to_rgmii)(struct rk_priv_data *bsp_priv,
|
|
+ int tx_delay, int rx_delay);
|
|
+ void (*set_to_rmii)(struct rk_priv_data *bsp_priv);
|
|
+ void (*set_rgmii_speed)(struct rk_priv_data *bsp_priv, int speed);
|
|
+ void (*set_rmii_speed)(struct rk_priv_data *bsp_priv, int speed);
|
|
+};
|
|
+
|
|
+struct rk_priv_data {
|
|
+ struct platform_device *pdev;
|
|
+ int phy_iface;
|
|
+ struct regulator *regulator;
|
|
+ const struct rk_gmac_ops *ops;
|
|
+
|
|
+ bool clk_enabled;
|
|
+ bool clock_input;
|
|
+
|
|
+ struct clk *clk_mac;
|
|
+ struct clk *gmac_clkin;
|
|
+ struct clk *mac_clk_rx;
|
|
+ struct clk *mac_clk_tx;
|
|
+ struct clk *clk_mac_ref;
|
|
+ struct clk *clk_mac_refout;
|
|
+ struct clk *aclk_mac;
|
|
+ struct clk *pclk_mac;
|
|
+
|
|
+ int tx_delay;
|
|
+ int rx_delay;
|
|
+
|
|
+ struct regmap *grf;
|
|
+};
|
|
+
|
|
+#define HIWORD_UPDATE(val, mask, shift) \
|
|
+ ((val) << (shift) | (mask) << ((shift) + 16))
|
|
+
|
|
+#define GRF_BIT(nr) (BIT(nr) | BIT(nr+16))
|
|
+#define GRF_CLR_BIT(nr) (BIT(nr+16))
|
|
+
|
|
+#define RK3288_GRF_SOC_CON1 0x0248
|
|
+#define RK3288_GRF_SOC_CON3 0x0250
|
|
+
|
|
+/*RK3288_GRF_SOC_CON1*/
|
|
+#define RK3288_GMAC_PHY_INTF_SEL_RGMII (GRF_BIT(6) | GRF_CLR_BIT(7) | \
|
|
+ GRF_CLR_BIT(8))
|
|
+#define RK3288_GMAC_PHY_INTF_SEL_RMII (GRF_CLR_BIT(6) | GRF_CLR_BIT(7) | \
|
|
+ GRF_BIT(8))
|
|
+#define RK3288_GMAC_FLOW_CTRL GRF_BIT(9)
|
|
+#define RK3288_GMAC_FLOW_CTRL_CLR GRF_CLR_BIT(9)
|
|
+#define RK3288_GMAC_SPEED_10M GRF_CLR_BIT(10)
|
|
+#define RK3288_GMAC_SPEED_100M GRF_BIT(10)
|
|
+#define RK3288_GMAC_RMII_CLK_25M GRF_BIT(11)
|
|
+#define RK3288_GMAC_RMII_CLK_2_5M GRF_CLR_BIT(11)
|
|
+#define RK3288_GMAC_CLK_125M (GRF_CLR_BIT(12) | GRF_CLR_BIT(13))
|
|
+#define RK3288_GMAC_CLK_25M (GRF_BIT(12) | GRF_BIT(13))
|
|
+#define RK3288_GMAC_CLK_2_5M (GRF_CLR_BIT(12) | GRF_BIT(13))
|
|
+#define RK3288_GMAC_RMII_MODE GRF_BIT(14)
|
|
+#define RK3288_GMAC_RMII_MODE_CLR GRF_CLR_BIT(14)
|
|
+
|
|
+/*RK3288_GRF_SOC_CON3*/
|
|
+#define RK3288_GMAC_TXCLK_DLY_ENABLE GRF_BIT(14)
|
|
+#define RK3288_GMAC_TXCLK_DLY_DISABLE GRF_CLR_BIT(14)
|
|
+#define RK3288_GMAC_RXCLK_DLY_ENABLE GRF_BIT(15)
|
|
+#define RK3288_GMAC_RXCLK_DLY_DISABLE GRF_CLR_BIT(15)
|
|
+#define RK3288_GMAC_CLK_RX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 7)
|
|
+#define RK3288_GMAC_CLK_TX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 0)
|
|
+
|
|
+static void rk3288_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, RK3288_GRF_SOC_CON1,
|
|
+ RK3288_GMAC_PHY_INTF_SEL_RGMII |
|
|
+ RK3288_GMAC_RMII_MODE_CLR);
|
|
+ regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON3,
|
|
+ RK3288_GMAC_RXCLK_DLY_ENABLE |
|
|
+ RK3288_GMAC_TXCLK_DLY_ENABLE |
|
|
+ RK3288_GMAC_CLK_RX_DL_CFG(rx_delay) |
|
|
+ RK3288_GMAC_CLK_TX_DL_CFG(tx_delay));
|
|
+}
|
|
+
|
|
+static void rk3288_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, RK3288_GRF_SOC_CON1,
|
|
+ RK3288_GMAC_PHY_INTF_SEL_RMII | RK3288_GMAC_RMII_MODE);
|
|
+}
|
|
+
|
|
+static void rk3288_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, RK3288_GRF_SOC_CON1,
|
|
+ RK3288_GMAC_CLK_2_5M);
|
|
+ else if (speed == 100)
|
|
+ regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1,
|
|
+ RK3288_GMAC_CLK_25M);
|
|
+ else if (speed == 1000)
|
|
+ regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1,
|
|
+ RK3288_GMAC_CLK_125M);
|
|
+ else
|
|
+ dev_err(dev, "unknown speed value for RGMII! speed=%d", speed);
|
|
+}
|
|
+
|
|
+static void rk3288_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, RK3288_GRF_SOC_CON1,
|
|
+ RK3288_GMAC_RMII_CLK_2_5M |
|
|
+ RK3288_GMAC_SPEED_10M);
|
|
+ } else if (speed == 100) {
|
|
+ regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1,
|
|
+ RK3288_GMAC_RMII_CLK_25M |
|
|
+ RK3288_GMAC_SPEED_100M);
|
|
+ } else {
|
|
+ dev_err(dev, "unknown speed value for RMII! speed=%d", speed);
|
|
+ }
|
|
+}
|
|
+
|
|
+static const struct rk_gmac_ops rk3288_ops = {
|
|
+ .set_to_rgmii = rk3288_set_to_rgmii,
|
|
+ .set_to_rmii = rk3288_set_to_rmii,
|
|
+ .set_rgmii_speed = rk3288_set_rgmii_speed,
|
|
+ .set_rmii_speed = rk3288_set_rmii_speed,
|
|
+};
|
|
+
|
|
+#define RK3368_GRF_SOC_CON15 0x043c
|
|
+#define RK3368_GRF_SOC_CON16 0x0440
|
|
+
|
|
+/* RK3368_GRF_SOC_CON15 */
|
|
+#define RK3368_GMAC_PHY_INTF_SEL_RGMII (GRF_BIT(9) | GRF_CLR_BIT(10) | \
|
|
+ GRF_CLR_BIT(11))
|
|
+#define RK3368_GMAC_PHY_INTF_SEL_RMII (GRF_CLR_BIT(9) | GRF_CLR_BIT(10) | \
|
|
+ GRF_BIT(11))
|
|
+#define RK3368_GMAC_FLOW_CTRL GRF_BIT(8)
|
|
+#define RK3368_GMAC_FLOW_CTRL_CLR GRF_CLR_BIT(8)
|
|
+#define RK3368_GMAC_SPEED_10M GRF_CLR_BIT(7)
|
|
+#define RK3368_GMAC_SPEED_100M GRF_BIT(7)
|
|
+#define RK3368_GMAC_RMII_CLK_25M GRF_BIT(3)
|
|
+#define RK3368_GMAC_RMII_CLK_2_5M GRF_CLR_BIT(3)
|
|
+#define RK3368_GMAC_CLK_125M (GRF_CLR_BIT(4) | GRF_CLR_BIT(5))
|
|
+#define RK3368_GMAC_CLK_25M (GRF_BIT(4) | GRF_BIT(5))
|
|
+#define RK3368_GMAC_CLK_2_5M (GRF_CLR_BIT(4) | GRF_BIT(5))
|
|
+#define RK3368_GMAC_RMII_MODE GRF_BIT(6)
|
|
+#define RK3368_GMAC_RMII_MODE_CLR GRF_CLR_BIT(6)
|
|
+
|
|
+/* RK3368_GRF_SOC_CON16 */
|
|
+#define RK3368_GMAC_TXCLK_DLY_ENABLE GRF_BIT(7)
|
|
+#define RK3368_GMAC_TXCLK_DLY_DISABLE GRF_CLR_BIT(7)
|
|
+#define RK3368_GMAC_RXCLK_DLY_ENABLE GRF_BIT(15)
|
|
+#define RK3368_GMAC_RXCLK_DLY_DISABLE GRF_CLR_BIT(15)
|
|
+#define RK3368_GMAC_CLK_RX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 8)
|
|
+#define RK3368_GMAC_CLK_TX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 0)
|
|
+
|
|
+static void rk3368_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, "%s: Missing rockchip,grf property\n", __func__);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ regmap_write(bsp_priv->grf, RK3368_GRF_SOC_CON15,
|
|
+ RK3368_GMAC_PHY_INTF_SEL_RGMII |
|
|
+ RK3368_GMAC_RMII_MODE_CLR);
|
|
+ regmap_write(bsp_priv->grf, RK3368_GRF_SOC_CON16,
|
|
+ RK3368_GMAC_RXCLK_DLY_ENABLE |
|
|
+ RK3368_GMAC_TXCLK_DLY_ENABLE |
|
|
+ RK3368_GMAC_CLK_RX_DL_CFG(rx_delay) |
|
|
+ RK3368_GMAC_CLK_TX_DL_CFG(tx_delay));
|
|
+}
|
|
+
|
|
+static void rk3368_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, "%s: Missing rockchip,grf property\n", __func__);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ regmap_write(bsp_priv->grf, RK3368_GRF_SOC_CON15,
|
|
+ RK3368_GMAC_PHY_INTF_SEL_RMII | RK3368_GMAC_RMII_MODE);
|
|
+}
|
|
+
|
|
+static void rk3368_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, "%s: Missing rockchip,grf property\n", __func__);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (speed == 10)
|
|
+ regmap_write(bsp_priv->grf, RK3368_GRF_SOC_CON15,
|
|
+ RK3368_GMAC_CLK_2_5M);
|
|
+ else if (speed == 100)
|
|
+ regmap_write(bsp_priv->grf, RK3368_GRF_SOC_CON15,
|
|
+ RK3368_GMAC_CLK_25M);
|
|
+ else if (speed == 1000)
|
|
+ regmap_write(bsp_priv->grf, RK3368_GRF_SOC_CON15,
|
|
+ RK3368_GMAC_CLK_125M);
|
|
+ else
|
|
+ dev_err(dev, "unknown speed value for RGMII! speed=%d", speed);
|
|
+}
|
|
+
|
|
+static void rk3368_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, "%s: Missing rockchip,grf property\n", __func__);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (speed == 10) {
|
|
+ regmap_write(bsp_priv->grf, RK3368_GRF_SOC_CON15,
|
|
+ RK3368_GMAC_RMII_CLK_2_5M |
|
|
+ RK3368_GMAC_SPEED_10M);
|
|
+ } else if (speed == 100) {
|
|
+ regmap_write(bsp_priv->grf, RK3368_GRF_SOC_CON15,
|
|
+ RK3368_GMAC_RMII_CLK_25M |
|
|
+ RK3368_GMAC_SPEED_100M);
|
|
+ } else {
|
|
+ dev_err(dev, "unknown speed value for RMII! speed=%d", speed);
|
|
+ }
|
|
+}
|
|
+
|
|
+static const struct rk_gmac_ops rk3368_ops = {
|
|
+ .set_to_rgmii = rk3368_set_to_rgmii,
|
|
+ .set_to_rmii = rk3368_set_to_rmii,
|
|
+ .set_rgmii_speed = rk3368_set_rgmii_speed,
|
|
+ .set_rmii_speed = rk3368_set_rmii_speed,
|
|
+};
|
|
+
|
|
+static int gmac_clk_init(struct rk_priv_data *bsp_priv)
|
|
+{
|
|
+ struct device *dev = &bsp_priv->pdev->dev;
|
|
+
|
|
+ bsp_priv->clk_enabled = false;
|
|
+
|
|
+ bsp_priv->mac_clk_rx = devm_clk_get(dev, "mac_clk_rx");
|
|
+ if (IS_ERR(bsp_priv->mac_clk_rx))
|
|
+ dev_err(dev, "cannot get clock %s\n",
|
|
+ "mac_clk_rx");
|
|
+
|
|
+ bsp_priv->mac_clk_tx = devm_clk_get(dev, "mac_clk_tx");
|
|
+ if (IS_ERR(bsp_priv->mac_clk_tx))
|
|
+ dev_err(dev, "cannot get clock %s\n",
|
|
+ "mac_clk_tx");
|
|
+
|
|
+ bsp_priv->aclk_mac = devm_clk_get(dev, "aclk_mac");
|
|
+ if (IS_ERR(bsp_priv->aclk_mac))
|
|
+ dev_err(dev, "cannot get clock %s\n",
|
|
+ "aclk_mac");
|
|
+
|
|
+ bsp_priv->pclk_mac = devm_clk_get(dev, "pclk_mac");
|
|
+ if (IS_ERR(bsp_priv->pclk_mac))
|
|
+ dev_err(dev, "cannot get clock %s\n",
|
|
+ "pclk_mac");
|
|
+
|
|
+ bsp_priv->clk_mac = devm_clk_get(dev, "stmmaceth");
|
|
+ if (IS_ERR(bsp_priv->clk_mac))
|
|
+ dev_err(dev, "cannot get clock %s\n",
|
|
+ "stmmaceth");
|
|
+
|
|
+ if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RMII) {
|
|
+ bsp_priv->clk_mac_ref = devm_clk_get(dev, "clk_mac_ref");
|
|
+ if (IS_ERR(bsp_priv->clk_mac_ref))
|
|
+ dev_err(dev, "cannot get clock %s\n",
|
|
+ "clk_mac_ref");
|
|
+
|
|
+ if (!bsp_priv->clock_input) {
|
|
+ bsp_priv->clk_mac_refout =
|
|
+ devm_clk_get(dev, "clk_mac_refout");
|
|
+ if (IS_ERR(bsp_priv->clk_mac_refout))
|
|
+ dev_err(dev, "cannot get clock %s\n",
|
|
+ "clk_mac_refout");
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (bsp_priv->clock_input) {
|
|
+ dev_info(dev, "clock input from PHY\n");
|
|
+ } else {
|
|
+ if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RMII)
|
|
+ clk_set_rate(bsp_priv->clk_mac, 50000000);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int gmac_clk_enable(struct rk_priv_data *bsp_priv, bool enable)
|
|
+{
|
|
+ int phy_iface = phy_iface = bsp_priv->phy_iface;
|
|
+
|
|
+ if (enable) {
|
|
+ if (!bsp_priv->clk_enabled) {
|
|
+ if (phy_iface == PHY_INTERFACE_MODE_RMII) {
|
|
+ if (!IS_ERR(bsp_priv->mac_clk_rx))
|
|
+ clk_prepare_enable(
|
|
+ bsp_priv->mac_clk_rx);
|
|
+
|
|
+ if (!IS_ERR(bsp_priv->clk_mac_ref))
|
|
+ clk_prepare_enable(
|
|
+ bsp_priv->clk_mac_ref);
|
|
+
|
|
+ if (!IS_ERR(bsp_priv->clk_mac_refout))
|
|
+ clk_prepare_enable(
|
|
+ bsp_priv->clk_mac_refout);
|
|
+ }
|
|
+
|
|
+ if (!IS_ERR(bsp_priv->aclk_mac))
|
|
+ clk_prepare_enable(bsp_priv->aclk_mac);
|
|
+
|
|
+ if (!IS_ERR(bsp_priv->pclk_mac))
|
|
+ clk_prepare_enable(bsp_priv->pclk_mac);
|
|
+
|
|
+ if (!IS_ERR(bsp_priv->mac_clk_tx))
|
|
+ clk_prepare_enable(bsp_priv->mac_clk_tx);
|
|
+
|
|
+ /**
|
|
+ * if (!IS_ERR(bsp_priv->clk_mac))
|
|
+ * clk_prepare_enable(bsp_priv->clk_mac);
|
|
+ */
|
|
+ mdelay(5);
|
|
+ bsp_priv->clk_enabled = true;
|
|
+ }
|
|
+ } else {
|
|
+ if (bsp_priv->clk_enabled) {
|
|
+ if (phy_iface == PHY_INTERFACE_MODE_RMII) {
|
|
+ if (!IS_ERR(bsp_priv->mac_clk_rx))
|
|
+ clk_disable_unprepare(
|
|
+ bsp_priv->mac_clk_rx);
|
|
+
|
|
+ if (!IS_ERR(bsp_priv->clk_mac_ref))
|
|
+ clk_disable_unprepare(
|
|
+ bsp_priv->clk_mac_ref);
|
|
+
|
|
+ if (!IS_ERR(bsp_priv->clk_mac_refout))
|
|
+ clk_disable_unprepare(
|
|
+ bsp_priv->clk_mac_refout);
|
|
+ }
|
|
+
|
|
+ if (!IS_ERR(bsp_priv->aclk_mac))
|
|
+ clk_disable_unprepare(bsp_priv->aclk_mac);
|
|
+
|
|
+ if (!IS_ERR(bsp_priv->pclk_mac))
|
|
+ clk_disable_unprepare(bsp_priv->pclk_mac);
|
|
+
|
|
+ if (!IS_ERR(bsp_priv->mac_clk_tx))
|
|
+ clk_disable_unprepare(bsp_priv->mac_clk_tx);
|
|
+ /**
|
|
+ * if (!IS_ERR(bsp_priv->clk_mac))
|
|
+ * clk_disable_unprepare(bsp_priv->clk_mac);
|
|
+ */
|
|
+ bsp_priv->clk_enabled = false;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int phy_power_on(struct rk_priv_data *bsp_priv, bool enable)
|
|
+{
|
|
+ struct regulator *ldo = bsp_priv->regulator;
|
|
+ int ret;
|
|
+ struct device *dev = &bsp_priv->pdev->dev;
|
|
+
|
|
+ if (!ldo) {
|
|
+ dev_err(dev, "no regulator found\n");
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ if (enable) {
|
|
+ ret = regulator_enable(ldo);
|
|
+ if (ret)
|
|
+ dev_err(dev, "fail to enable phy-supply\n");
|
|
+ } else {
|
|
+ ret = regulator_disable(ldo);
|
|
+ if (ret)
|
|
+ dev_err(dev, "fail to disable phy-supply\n");
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static struct rk_priv_data *rk_gmac_setup(struct platform_device *pdev,
|
|
+ const struct rk_gmac_ops *ops)
|
|
+{
|
|
+ struct rk_priv_data *bsp_priv;
|
|
+ struct device *dev = &pdev->dev;
|
|
+ int ret;
|
|
+ const char *strings = NULL;
|
|
+ int value;
|
|
+
|
|
+ bsp_priv = devm_kzalloc(dev, sizeof(*bsp_priv), GFP_KERNEL);
|
|
+ if (!bsp_priv)
|
|
+ return ERR_PTR(-ENOMEM);
|
|
+
|
|
+ bsp_priv->phy_iface = of_get_phy_mode(dev->of_node);
|
|
+ bsp_priv->ops = ops;
|
|
+
|
|
+ bsp_priv->regulator = devm_regulator_get_optional(dev, "phy");
|
|
+ if (IS_ERR(bsp_priv->regulator)) {
|
|
+ if (PTR_ERR(bsp_priv->regulator) == -EPROBE_DEFER) {
|
|
+ dev_err(dev, "phy regulator is not available yet, deferred probing\n");
|
|
+ return ERR_PTR(-EPROBE_DEFER);
|
|
+ }
|
|
+ dev_err(dev, "no regulator found\n");
|
|
+ bsp_priv->regulator = NULL;
|
|
+ }
|
|
+
|
|
+ ret = of_property_read_string(dev->of_node, "clock_in_out", &strings);
|
|
+ if (ret) {
|
|
+ dev_err(dev, "Can not read property: clock_in_out.\n");
|
|
+ bsp_priv->clock_input = true;
|
|
+ } else {
|
|
+ dev_info(dev, "clock input or output? (%s).\n",
|
|
+ strings);
|
|
+ if (!strcmp(strings, "input"))
|
|
+ bsp_priv->clock_input = true;
|
|
+ else
|
|
+ bsp_priv->clock_input = false;
|
|
+ }
|
|
+
|
|
+ ret = of_property_read_u32(dev->of_node, "tx_delay", &value);
|
|
+ if (ret) {
|
|
+ bsp_priv->tx_delay = 0x30;
|
|
+ dev_err(dev, "Can not read property: tx_delay.");
|
|
+ dev_err(dev, "set tx_delay to 0x%x\n",
|
|
+ bsp_priv->tx_delay);
|
|
+ } else {
|
|
+ dev_info(dev, "TX delay(0x%x).\n", value);
|
|
+ bsp_priv->tx_delay = value;
|
|
+ }
|
|
+
|
|
+ ret = of_property_read_u32(dev->of_node, "rx_delay", &value);
|
|
+ if (ret) {
|
|
+ bsp_priv->rx_delay = 0x10;
|
|
+ dev_err(dev, "Can not read property: rx_delay.");
|
|
+ dev_err(dev, "set rx_delay to 0x%x\n",
|
|
+ bsp_priv->rx_delay);
|
|
+ } else {
|
|
+ dev_info(dev, "RX delay(0x%x).\n", value);
|
|
+ bsp_priv->rx_delay = value;
|
|
+ }
|
|
+
|
|
+ bsp_priv->grf = syscon_regmap_lookup_by_phandle(dev->of_node,
|
|
+ "rockchip,grf");
|
|
+ bsp_priv->pdev = pdev;
|
|
+
|
|
+ /*rmii or rgmii*/
|
|
+ if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RGMII) {
|
|
+ dev_info(dev, "init for RGMII\n");
|
|
+ bsp_priv->ops->set_to_rgmii(bsp_priv, bsp_priv->tx_delay,
|
|
+ bsp_priv->rx_delay);
|
|
+ } else if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RMII) {
|
|
+ dev_info(dev, "init for RMII\n");
|
|
+ bsp_priv->ops->set_to_rmii(bsp_priv);
|
|
+ } else {
|
|
+ dev_err(dev, "NO interface defined!\n");
|
|
+ }
|
|
+
|
|
+ gmac_clk_init(bsp_priv);
|
|
+
|
|
+ return bsp_priv;
|
|
+}
|
|
+
|
|
+static int rk_gmac_init(struct platform_device *pdev, void *priv)
|
|
+{
|
|
+ struct rk_priv_data *bsp_priv = priv;
|
|
+ int ret;
|
|
+
|
|
+ ret = phy_power_on(bsp_priv, true);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ ret = gmac_clk_enable(bsp_priv, true);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void rk_gmac_exit(struct platform_device *pdev, void *priv)
|
|
+{
|
|
+ struct rk_priv_data *gmac = priv;
|
|
+
|
|
+ phy_power_on(gmac, false);
|
|
+ gmac_clk_enable(gmac, false);
|
|
+}
|
|
+
|
|
+static void rk_fix_speed(void *priv, unsigned int speed)
|
|
+{
|
|
+ struct rk_priv_data *bsp_priv = priv;
|
|
+ struct device *dev = &bsp_priv->pdev->dev;
|
|
+
|
|
+ if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RGMII)
|
|
+ bsp_priv->ops->set_rgmii_speed(bsp_priv, speed);
|
|
+ else if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RMII)
|
|
+ bsp_priv->ops->set_rmii_speed(bsp_priv, speed);
|
|
+ else
|
|
+ dev_err(dev, "unsupported interface %d", bsp_priv->phy_iface);
|
|
+}
|
|
+
|
|
+static int rk_gmac_probe(struct platform_device *pdev)
|
|
+{
|
|
+ struct plat_stmmacenet_data *plat_dat;
|
|
+ struct stmmac_resources stmmac_res;
|
|
+ const struct rk_gmac_ops *data;
|
|
+ int ret;
|
|
+
|
|
+ data = of_device_get_match_data(&pdev->dev);
|
|
+ if (!data) {
|
|
+ dev_err(&pdev->dev, "no of match data provided\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ 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);
|
|
+
|
|
+ plat_dat->has_gmac = true;
|
|
+ plat_dat->init = rk_gmac_init;
|
|
+ plat_dat->exit = rk_gmac_exit;
|
|
+ plat_dat->fix_mac_speed = rk_fix_speed;
|
|
+
|
|
+ plat_dat->bsp_priv = rk_gmac_setup(pdev, data);
|
|
+ if (IS_ERR(plat_dat->bsp_priv))
|
|
+ return PTR_ERR(plat_dat->bsp_priv);
|
|
+
|
|
+ ret = rk_gmac_init(pdev, plat_dat->bsp_priv);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ return stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
|
|
+}
|
|
+
|
|
+static const struct of_device_id rk_gmac_dwmac_match[] = {
|
|
+ { .compatible = "rockchip,rk3288-gmac", .data = &rk3288_ops },
|
|
+ { .compatible = "rockchip,rk3368-gmac", .data = &rk3368_ops },
|
|
+ { }
|
|
+};
|
|
+MODULE_DEVICE_TABLE(of, rk_gmac_dwmac_match);
|
|
+
|
|
+static struct platform_driver rk_gmac_dwmac_driver = {
|
|
+ .probe = rk_gmac_probe,
|
|
+ .remove = stmmac_pltfr_remove,
|
|
+ .driver = {
|
|
+ .name = "rk_gmac-dwmac",
|
|
+ .pm = &stmmac_pltfr_pm_ops,
|
|
+ .of_match_table = rk_gmac_dwmac_match,
|
|
+ },
|
|
+};
|
|
+module_platform_driver(rk_gmac_dwmac_driver);
|
|
+
|
|
+MODULE_AUTHOR("Chen-Zhi (Roger Chen) <roger.chen@rock-chips.com>");
|
|
+MODULE_DESCRIPTION("Rockchip RK3288 DWMAC specific glue layer");
|
|
+MODULE_LICENSE("GPL");
|
|
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
|
|
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-socfpga.c
|
|
@@ -23,7 +23,9 @@
|
|
#include <linux/regmap.h>
|
|
#include <linux/reset.h>
|
|
#include <linux/stmmac.h>
|
|
+
|
|
#include "stmmac.h"
|
|
+#include "stmmac_platform.h"
|
|
|
|
#define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_GMII_MII 0x0
|
|
#define SYSMGR_EMACGRP_CTRL_PHYSEL_ENUM_RGMII 0x1
|
|
@@ -89,7 +91,9 @@ static int socfpga_dwmac_parse_data(stru
|
|
STMMAC_RESOURCE_NAME);
|
|
if (IS_ERR(dwmac->stmmac_rst)) {
|
|
dev_info(dev, "Could not get reset control!\n");
|
|
- return -EINVAL;
|
|
+ if (PTR_ERR(dwmac->stmmac_rst) == -EPROBE_DEFER)
|
|
+ return -EPROBE_DEFER;
|
|
+ dwmac->stmmac_rst = NULL;
|
|
}
|
|
|
|
dwmac->interface = of_get_phy_mode(np);
|
|
@@ -171,31 +175,6 @@ static int socfpga_dwmac_setup(struct so
|
|
return 0;
|
|
}
|
|
|
|
-static void *socfpga_dwmac_probe(struct platform_device *pdev)
|
|
-{
|
|
- struct device *dev = &pdev->dev;
|
|
- int ret;
|
|
- struct socfpga_dwmac *dwmac;
|
|
-
|
|
- dwmac = devm_kzalloc(dev, sizeof(*dwmac), GFP_KERNEL);
|
|
- if (!dwmac)
|
|
- return ERR_PTR(-ENOMEM);
|
|
-
|
|
- ret = socfpga_dwmac_parse_data(dwmac, dev);
|
|
- if (ret) {
|
|
- dev_err(dev, "Unable to parse OF data\n");
|
|
- return ERR_PTR(ret);
|
|
- }
|
|
-
|
|
- ret = socfpga_dwmac_setup(dwmac);
|
|
- if (ret) {
|
|
- dev_err(dev, "couldn't setup SoC glue (%d)\n", ret);
|
|
- return ERR_PTR(ret);
|
|
- }
|
|
-
|
|
- return dwmac;
|
|
-}
|
|
-
|
|
static void socfpga_dwmac_exit(struct platform_device *pdev, void *priv)
|
|
{
|
|
struct socfpga_dwmac *dwmac = priv;
|
|
@@ -253,9 +232,65 @@ static int socfpga_dwmac_init(struct pla
|
|
return ret;
|
|
}
|
|
|
|
-const struct stmmac_of_data socfpga_gmac_data = {
|
|
- .setup = socfpga_dwmac_probe,
|
|
- .init = socfpga_dwmac_init,
|
|
- .exit = socfpga_dwmac_exit,
|
|
- .fix_mac_speed = socfpga_dwmac_fix_mac_speed,
|
|
+static int socfpga_dwmac_probe(struct platform_device *pdev)
|
|
+{
|
|
+ struct plat_stmmacenet_data *plat_dat;
|
|
+ struct stmmac_resources stmmac_res;
|
|
+ struct device *dev = &pdev->dev;
|
|
+ int ret;
|
|
+ struct socfpga_dwmac *dwmac;
|
|
+
|
|
+ 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);
|
|
+
|
|
+ dwmac = devm_kzalloc(dev, sizeof(*dwmac), GFP_KERNEL);
|
|
+ if (!dwmac)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ ret = socfpga_dwmac_parse_data(dwmac, dev);
|
|
+ if (ret) {
|
|
+ dev_err(dev, "Unable to parse OF data\n");
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ret = socfpga_dwmac_setup(dwmac);
|
|
+ if (ret) {
|
|
+ dev_err(dev, "couldn't setup SoC glue (%d)\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ plat_dat->bsp_priv = dwmac;
|
|
+ plat_dat->init = socfpga_dwmac_init;
|
|
+ plat_dat->exit = socfpga_dwmac_exit;
|
|
+ plat_dat->fix_mac_speed = socfpga_dwmac_fix_mac_speed;
|
|
+
|
|
+ ret = socfpga_dwmac_init(pdev, plat_dat->bsp_priv);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ return stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
|
|
+}
|
|
+
|
|
+static const struct of_device_id socfpga_dwmac_match[] = {
|
|
+ { .compatible = "altr,socfpga-stmmac" },
|
|
+ { }
|
|
};
|
|
+MODULE_DEVICE_TABLE(of, socfpga_dwmac_match);
|
|
+
|
|
+static struct platform_driver socfpga_dwmac_driver = {
|
|
+ .probe = socfpga_dwmac_probe,
|
|
+ .remove = stmmac_pltfr_remove,
|
|
+ .driver = {
|
|
+ .name = "socfpga-dwmac",
|
|
+ .pm = &stmmac_pltfr_pm_ops,
|
|
+ .of_match_table = socfpga_dwmac_match,
|
|
+ },
|
|
+};
|
|
+module_platform_driver(socfpga_dwmac_driver);
|
|
+
|
|
+MODULE_LICENSE("GPL v2");
|
|
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c
|
|
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c
|
|
@@ -1,4 +1,4 @@
|
|
-/**
|
|
+/*
|
|
* dwmac-sti.c - STMicroelectronics DWMAC Specific Glue layer
|
|
*
|
|
* Copyright (C) 2003-2014 STMicroelectronics (R&D) Limited
|
|
@@ -17,11 +17,15 @@
|
|
#include <linux/stmmac.h>
|
|
#include <linux/phy.h>
|
|
#include <linux/mfd/syscon.h>
|
|
+#include <linux/module.h>
|
|
#include <linux/regmap.h>
|
|
#include <linux/clk.h>
|
|
#include <linux/of.h>
|
|
+#include <linux/of_device.h>
|
|
#include <linux/of_net.h>
|
|
|
|
+#include "stmmac_platform.h"
|
|
+
|
|
#define DWMAC_125MHZ 125000000
|
|
#define DWMAC_50MHZ 50000000
|
|
#define DWMAC_25MHZ 25000000
|
|
@@ -35,9 +39,8 @@
|
|
#define IS_PHY_IF_MODE_GBIT(iface) (IS_PHY_IF_MODE_RGMII(iface) || \
|
|
iface == PHY_INTERFACE_MODE_GMII)
|
|
|
|
-/* STiH4xx register definitions (STiH415/STiH416/STiH407/STiH410 families) */
|
|
-
|
|
-/**
|
|
+/* STiH4xx register definitions (STiH415/STiH416/STiH407/STiH410 families)
|
|
+ *
|
|
* Below table summarizes the clock requirement and clock sources for
|
|
* supported phy interface modes with link speeds.
|
|
* ________________________________________________
|
|
@@ -76,9 +79,7 @@
|
|
#define STIH4XX_ETH_SEL_INTERNAL_NOTEXT_PHYCLK BIT(7)
|
|
#define STIH4XX_ETH_SEL_TXCLK_NOT_CLK125 BIT(6)
|
|
|
|
-/* STiD127 register definitions */
|
|
-
|
|
-/**
|
|
+/* STiD127 register definitions
|
|
*-----------------------
|
|
* src |BIT(6)| BIT(7)|
|
|
*-----------------------
|
|
@@ -104,13 +105,13 @@
|
|
#define EN_MASK GENMASK(1, 1)
|
|
#define EN BIT(1)
|
|
|
|
-/**
|
|
+/*
|
|
* 3 bits [4:2]
|
|
* 000-GMII/MII
|
|
* 001-RGMII
|
|
* 010-SGMII
|
|
* 100-RMII
|
|
-*/
|
|
+ */
|
|
#define MII_PHY_SEL_MASK GENMASK(4, 2)
|
|
#define ETH_PHY_SEL_RMII BIT(4)
|
|
#define ETH_PHY_SEL_SGMII BIT(3)
|
|
@@ -123,11 +124,16 @@ struct sti_dwmac {
|
|
bool ext_phyclk; /* Clock from external PHY */
|
|
u32 tx_retime_src; /* TXCLK Retiming*/
|
|
struct clk *clk; /* PHY clock */
|
|
- int ctrl_reg; /* GMAC glue-logic control register */
|
|
+ u32 ctrl_reg; /* GMAC glue-logic control register */
|
|
int clk_sel_reg; /* GMAC ext clk selection register */
|
|
struct device *dev;
|
|
struct regmap *regmap;
|
|
u32 speed;
|
|
+ void (*fix_retime_src)(void *priv, unsigned int speed);
|
|
+};
|
|
+
|
|
+struct sti_dwmac_of_data {
|
|
+ void (*fix_retime_src)(void *priv, unsigned int speed);
|
|
};
|
|
|
|
static u32 phy_intf_sels[] = {
|
|
@@ -222,8 +228,9 @@ static void stid127_fix_retime_src(void
|
|
regmap_update_bits(dwmac->regmap, reg, STID127_RETIME_SRC_MASK, val);
|
|
}
|
|
|
|
-static void sti_dwmac_ctrl_init(struct sti_dwmac *dwmac)
|
|
+static int sti_dwmac_init(struct platform_device *pdev, void *priv)
|
|
{
|
|
+ struct sti_dwmac *dwmac = priv;
|
|
struct regmap *regmap = dwmac->regmap;
|
|
int iface = dwmac->interface;
|
|
struct device *dev = dwmac->dev;
|
|
@@ -241,28 +248,8 @@ static void sti_dwmac_ctrl_init(struct s
|
|
|
|
val = (iface == PHY_INTERFACE_MODE_REVMII) ? 0 : ENMII;
|
|
regmap_update_bits(regmap, reg, ENMII_MASK, val);
|
|
-}
|
|
|
|
-static int stix4xx_init(struct platform_device *pdev, void *priv)
|
|
-{
|
|
- struct sti_dwmac *dwmac = priv;
|
|
- u32 spd = dwmac->speed;
|
|
-
|
|
- sti_dwmac_ctrl_init(dwmac);
|
|
-
|
|
- stih4xx_fix_retime_src(priv, spd);
|
|
-
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static int stid127_init(struct platform_device *pdev, void *priv)
|
|
-{
|
|
- struct sti_dwmac *dwmac = priv;
|
|
- u32 spd = dwmac->speed;
|
|
-
|
|
- sti_dwmac_ctrl_init(dwmac);
|
|
-
|
|
- stid127_fix_retime_src(priv, spd);
|
|
+ dwmac->fix_retime_src(priv, dwmac->speed);
|
|
|
|
return 0;
|
|
}
|
|
@@ -286,11 +273,6 @@ static int sti_dwmac_parse_data(struct s
|
|
if (!np)
|
|
return -EINVAL;
|
|
|
|
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sti-ethconf");
|
|
- if (!res)
|
|
- return -ENODATA;
|
|
- dwmac->ctrl_reg = res->start;
|
|
-
|
|
/* clk selection from extra syscfg register */
|
|
dwmac->clk_sel_reg = -ENXIO;
|
|
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sti-clkconf");
|
|
@@ -301,6 +283,12 @@ static int sti_dwmac_parse_data(struct s
|
|
if (IS_ERR(regmap))
|
|
return PTR_ERR(regmap);
|
|
|
|
+ err = of_property_read_u32_index(np, "st,syscon", 1, &dwmac->ctrl_reg);
|
|
+ if (err) {
|
|
+ dev_err(dev, "Can't get sysconfig ctrl offset (%d)\n", err);
|
|
+ return err;
|
|
+ }
|
|
+
|
|
dwmac->dev = dev;
|
|
dwmac->interface = of_get_phy_mode(np);
|
|
dwmac->regmap = regmap;
|
|
@@ -310,16 +298,16 @@ static int sti_dwmac_parse_data(struct s
|
|
|
|
if (IS_PHY_IF_MODE_GBIT(dwmac->interface)) {
|
|
const char *rs;
|
|
- dwmac->tx_retime_src = TX_RETIME_SRC_CLKGEN;
|
|
|
|
err = of_property_read_string(np, "st,tx-retime-src", &rs);
|
|
- if (err < 0)
|
|
+ if (err < 0) {
|
|
dev_warn(dev, "Use internal clock source\n");
|
|
-
|
|
- if (!strcasecmp(rs, "clk_125"))
|
|
+ dwmac->tx_retime_src = TX_RETIME_SRC_CLKGEN;
|
|
+ } else if (!strcasecmp(rs, "clk_125")) {
|
|
dwmac->tx_retime_src = TX_RETIME_SRC_CLK_125;
|
|
- else if (!strcasecmp(rs, "txclk"))
|
|
+ } else if (!strcasecmp(rs, "txclk")) {
|
|
dwmac->tx_retime_src = TX_RETIME_SRC_TXCLK;
|
|
+ }
|
|
|
|
dwmac->speed = SPEED_1000;
|
|
}
|
|
@@ -333,34 +321,80 @@ static int sti_dwmac_parse_data(struct s
|
|
return 0;
|
|
}
|
|
|
|
-static void *sti_dwmac_setup(struct platform_device *pdev)
|
|
+static int sti_dwmac_probe(struct platform_device *pdev)
|
|
{
|
|
+ struct plat_stmmacenet_data *plat_dat;
|
|
+ const struct sti_dwmac_of_data *data;
|
|
+ struct stmmac_resources stmmac_res;
|
|
struct sti_dwmac *dwmac;
|
|
int ret;
|
|
|
|
+ data = of_device_get_match_data(&pdev->dev);
|
|
+ if (!data) {
|
|
+ dev_err(&pdev->dev, "No OF match data provided\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ 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);
|
|
+
|
|
dwmac = devm_kzalloc(&pdev->dev, sizeof(*dwmac), GFP_KERNEL);
|
|
if (!dwmac)
|
|
- return ERR_PTR(-ENOMEM);
|
|
+ return -ENOMEM;
|
|
|
|
ret = sti_dwmac_parse_data(dwmac, pdev);
|
|
if (ret) {
|
|
dev_err(&pdev->dev, "Unable to parse OF data\n");
|
|
- return ERR_PTR(ret);
|
|
+ return ret;
|
|
}
|
|
|
|
- return dwmac;
|
|
+ dwmac->fix_retime_src = data->fix_retime_src;
|
|
+
|
|
+ plat_dat->bsp_priv = dwmac;
|
|
+ plat_dat->init = sti_dwmac_init;
|
|
+ plat_dat->exit = sti_dwmac_exit;
|
|
+ plat_dat->fix_mac_speed = data->fix_retime_src;
|
|
+
|
|
+ ret = sti_dwmac_init(pdev, plat_dat->bsp_priv);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ return stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
|
|
}
|
|
|
|
-const struct stmmac_of_data stih4xx_dwmac_data = {
|
|
- .fix_mac_speed = stih4xx_fix_retime_src,
|
|
- .setup = sti_dwmac_setup,
|
|
- .init = stix4xx_init,
|
|
- .exit = sti_dwmac_exit,
|
|
+static const struct sti_dwmac_of_data stih4xx_dwmac_data = {
|
|
+ .fix_retime_src = stih4xx_fix_retime_src,
|
|
};
|
|
|
|
-const struct stmmac_of_data stid127_dwmac_data = {
|
|
- .fix_mac_speed = stid127_fix_retime_src,
|
|
- .setup = sti_dwmac_setup,
|
|
- .init = stid127_init,
|
|
- .exit = sti_dwmac_exit,
|
|
+static const struct sti_dwmac_of_data stid127_dwmac_data = {
|
|
+ .fix_retime_src = stid127_fix_retime_src,
|
|
};
|
|
+
|
|
+static const struct of_device_id sti_dwmac_match[] = {
|
|
+ { .compatible = "st,stih415-dwmac", .data = &stih4xx_dwmac_data},
|
|
+ { .compatible = "st,stih416-dwmac", .data = &stih4xx_dwmac_data},
|
|
+ { .compatible = "st,stid127-dwmac", .data = &stid127_dwmac_data},
|
|
+ { .compatible = "st,stih407-dwmac", .data = &stih4xx_dwmac_data},
|
|
+ { }
|
|
+};
|
|
+MODULE_DEVICE_TABLE(of, sti_dwmac_match);
|
|
+
|
|
+static struct platform_driver sti_dwmac_driver = {
|
|
+ .probe = sti_dwmac_probe,
|
|
+ .remove = stmmac_pltfr_remove,
|
|
+ .driver = {
|
|
+ .name = "sti-dwmac",
|
|
+ .pm = &stmmac_pltfr_pm_ops,
|
|
+ .of_match_table = sti_dwmac_match,
|
|
+ },
|
|
+};
|
|
+module_platform_driver(sti_dwmac_driver);
|
|
+
|
|
+MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@st.com>");
|
|
+MODULE_DESCRIPTION("STMicroelectronics DWMAC Specific Glue layer");
|
|
+MODULE_LICENSE("GPL");
|
|
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sunxi.c
|
|
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sunxi.c
|
|
@@ -1,4 +1,4 @@
|
|
-/**
|
|
+/*
|
|
* dwmac-sunxi.c - Allwinner sunxi DWMAC specific glue layer
|
|
*
|
|
* Copyright (C) 2013 Chen-Yu Tsai
|
|
@@ -18,10 +18,14 @@
|
|
|
|
#include <linux/stmmac.h>
|
|
#include <linux/clk.h>
|
|
+#include <linux/module.h>
|
|
#include <linux/phy.h>
|
|
+#include <linux/platform_device.h>
|
|
#include <linux/of_net.h>
|
|
#include <linux/regulator/consumer.h>
|
|
|
|
+#include "stmmac_platform.h"
|
|
+
|
|
struct sunxi_priv_data {
|
|
int interface;
|
|
int clk_enabled;
|
|
@@ -29,35 +33,6 @@ struct sunxi_priv_data {
|
|
struct regulator *regulator;
|
|
};
|
|
|
|
-static void *sun7i_gmac_setup(struct platform_device *pdev)
|
|
-{
|
|
- struct sunxi_priv_data *gmac;
|
|
- struct device *dev = &pdev->dev;
|
|
-
|
|
- gmac = devm_kzalloc(dev, sizeof(*gmac), GFP_KERNEL);
|
|
- if (!gmac)
|
|
- return ERR_PTR(-ENOMEM);
|
|
-
|
|
- gmac->interface = of_get_phy_mode(dev->of_node);
|
|
-
|
|
- gmac->tx_clk = devm_clk_get(dev, "allwinner_gmac_tx");
|
|
- if (IS_ERR(gmac->tx_clk)) {
|
|
- dev_err(dev, "could not get tx clock\n");
|
|
- return 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 ERR_PTR(-EPROBE_DEFER);
|
|
- dev_info(dev, "no regulator found\n");
|
|
- gmac->regulator = NULL;
|
|
- }
|
|
-
|
|
- return gmac;
|
|
-}
|
|
-
|
|
#define SUN7I_GMAC_GMII_RGMII_RATE 125000000
|
|
#define SUN7I_GMAC_MII_RATE 25000000
|
|
|
|
@@ -128,13 +103,76 @@ static void sun7i_fix_speed(void *priv,
|
|
}
|
|
}
|
|
|
|
-/* of_data specifying hardware features and callbacks.
|
|
- * hardware features were copied from Allwinner drivers. */
|
|
-const struct stmmac_of_data sun7i_gmac_data = {
|
|
- .has_gmac = 1,
|
|
- .tx_coe = 1,
|
|
- .fix_mac_speed = sun7i_fix_speed,
|
|
- .setup = sun7i_gmac_setup,
|
|
- .init = sun7i_gmac_init,
|
|
- .exit = sun7i_gmac_exit,
|
|
+static int sun7i_gmac_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->interface = of_get_phy_mode(dev->of_node);
|
|
+
|
|
+ gmac->tx_clk = devm_clk_get(dev, "allwinner_gmac_tx");
|
|
+ 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;
|
|
+ }
|
|
+
|
|
+ /* platform data specifying hardware features and callbacks.
|
|
+ * hardware features were copied from Allwinner drivers. */
|
|
+ plat_dat->tx_coe = 1;
|
|
+ plat_dat->has_gmac = true;
|
|
+ plat_dat->bsp_priv = gmac;
|
|
+ plat_dat->init = sun7i_gmac_init;
|
|
+ plat_dat->exit = sun7i_gmac_exit;
|
|
+ plat_dat->fix_mac_speed = sun7i_fix_speed;
|
|
+
|
|
+ ret = sun7i_gmac_init(pdev, plat_dat->bsp_priv);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ return stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
|
|
+}
|
|
+
|
|
+static const struct of_device_id sun7i_dwmac_match[] = {
|
|
+ { .compatible = "allwinner,sun7i-a20-gmac" },
|
|
+ { }
|
|
};
|
|
+MODULE_DEVICE_TABLE(of, sun7i_dwmac_match);
|
|
+
|
|
+static struct platform_driver sun7i_dwmac_driver = {
|
|
+ .probe = sun7i_gmac_probe,
|
|
+ .remove = stmmac_pltfr_remove,
|
|
+ .driver = {
|
|
+ .name = "sun7i-dwmac",
|
|
+ .pm = &stmmac_pltfr_pm_ops,
|
|
+ .of_match_table = sun7i_dwmac_match,
|
|
+ },
|
|
+};
|
|
+module_platform_driver(sun7i_dwmac_driver);
|
|
+
|
|
+MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>");
|
|
+MODULE_DESCRIPTION("Allwinner sunxi DWMAC specific glue layer");
|
|
+MODULE_LICENSE("GPL");
|
|
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h
|
|
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h
|
|
@@ -172,6 +172,7 @@ enum inter_frame_gap {
|
|
/* GMAC FLOW CTRL defines */
|
|
#define GMAC_FLOW_CTRL_PT_MASK 0xffff0000 /* Pause Time Mask */
|
|
#define GMAC_FLOW_CTRL_PT_SHIFT 16
|
|
+#define GMAC_FLOW_CTRL_UP 0x00000008 /* Unicast pause frame enable */
|
|
#define GMAC_FLOW_CTRL_RFE 0x00000004 /* Rx Flow Control Enable */
|
|
#define GMAC_FLOW_CTRL_TFE 0x00000002 /* Tx Flow Control Enable */
|
|
#define GMAC_FLOW_CTRL_FCB_BPA 0x00000001 /* Flow Control Busy ... */
|
|
@@ -246,6 +247,56 @@ enum ttc_control {
|
|
#define DMA_CONTROL_FEF 0x00000080
|
|
#define DMA_CONTROL_FUF 0x00000040
|
|
|
|
+/* Receive flow control activation field
|
|
+ * RFA field in DMA control register, bits 23,10:9
|
|
+ */
|
|
+#define DMA_CONTROL_RFA_MASK 0x00800600
|
|
+
|
|
+/* Receive flow control deactivation field
|
|
+ * RFD field in DMA control register, bits 22,12:11
|
|
+ */
|
|
+#define DMA_CONTROL_RFD_MASK 0x00401800
|
|
+
|
|
+/* RFD and RFA fields are encoded as follows
|
|
+ *
|
|
+ * Bit Field
|
|
+ * 0,00 - Full minus 1KB (only valid when rxfifo >= 4KB and EFC enabled)
|
|
+ * 0,01 - Full minus 2KB (only valid when rxfifo >= 4KB and EFC enabled)
|
|
+ * 0,10 - Full minus 3KB (only valid when rxfifo >= 4KB and EFC enabled)
|
|
+ * 0,11 - Full minus 4KB (only valid when rxfifo > 4KB and EFC enabled)
|
|
+ * 1,00 - Full minus 5KB (only valid when rxfifo > 8KB and EFC enabled)
|
|
+ * 1,01 - Full minus 6KB (only valid when rxfifo > 8KB and EFC enabled)
|
|
+ * 1,10 - Full minus 7KB (only valid when rxfifo > 8KB and EFC enabled)
|
|
+ * 1,11 - Reserved
|
|
+ *
|
|
+ * RFD should always be > RFA for a given FIFO size. RFD == RFA may work,
|
|
+ * but packet throughput performance may not be as expected.
|
|
+ *
|
|
+ * Be sure that bit 3 in GMAC Register 6 is set for Unicast Pause frame
|
|
+ * detection (IEEE Specification Requirement, Annex 31B, 31B.1, Pause
|
|
+ * Description).
|
|
+ *
|
|
+ * Be sure that DZPA (bit 7 in Flow Control Register, GMAC Register 6),
|
|
+ * is set to 0. This allows pause frames with a quanta of 0 to be sent
|
|
+ * as an XOFF message to the link peer.
|
|
+ */
|
|
+
|
|
+#define RFA_FULL_MINUS_1K 0x00000000
|
|
+#define RFA_FULL_MINUS_2K 0x00000200
|
|
+#define RFA_FULL_MINUS_3K 0x00000400
|
|
+#define RFA_FULL_MINUS_4K 0x00000600
|
|
+#define RFA_FULL_MINUS_5K 0x00800000
|
|
+#define RFA_FULL_MINUS_6K 0x00800200
|
|
+#define RFA_FULL_MINUS_7K 0x00800400
|
|
+
|
|
+#define RFD_FULL_MINUS_1K 0x00000000
|
|
+#define RFD_FULL_MINUS_2K 0x00000800
|
|
+#define RFD_FULL_MINUS_3K 0x00001000
|
|
+#define RFD_FULL_MINUS_4K 0x00001800
|
|
+#define RFD_FULL_MINUS_5K 0x00400000
|
|
+#define RFD_FULL_MINUS_6K 0x00400800
|
|
+#define RFD_FULL_MINUS_7K 0x00401000
|
|
+
|
|
enum rtc_control {
|
|
DMA_CONTROL_RTC_64 = 0x00000000,
|
|
DMA_CONTROL_RTC_32 = 0x00000008,
|
|
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c
|
|
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_core.c
|
|
@@ -201,7 +201,10 @@ static void dwmac1000_flow_ctrl(struct m
|
|
unsigned int fc, unsigned int pause_time)
|
|
{
|
|
void __iomem *ioaddr = hw->pcsr;
|
|
- unsigned int flow = 0;
|
|
+ /* Set flow such that DZPQ in Mac Register 6 is 0,
|
|
+ * and unicast pause detect is enabled.
|
|
+ */
|
|
+ unsigned int flow = GMAC_FLOW_CTRL_UP;
|
|
|
|
pr_debug("GMAC Flow-Control:\n");
|
|
if (fc & FLOW_RX) {
|
|
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c
|
|
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c
|
|
@@ -70,10 +70,6 @@ static int dwmac1000_dma_init(void __iom
|
|
if (mb)
|
|
value |= DMA_BUS_MODE_MB;
|
|
|
|
-#ifdef CONFIG_STMMAC_DA
|
|
- value |= DMA_BUS_MODE_DA; /* Rx has priority over tx */
|
|
-#endif
|
|
-
|
|
if (atds)
|
|
value |= DMA_BUS_MODE_ATDS;
|
|
|
|
@@ -110,8 +106,29 @@ static int dwmac1000_dma_init(void __iom
|
|
return 0;
|
|
}
|
|
|
|
+static u32 dwmac1000_configure_fc(u32 csr6, int rxfifosz)
|
|
+{
|
|
+ csr6 &= ~DMA_CONTROL_RFA_MASK;
|
|
+ csr6 &= ~DMA_CONTROL_RFD_MASK;
|
|
+
|
|
+ /* Leave flow control disabled if receive fifo size is less than
|
|
+ * 4K or 0. Otherwise, send XOFF when fifo is 1K less than full,
|
|
+ * and send XON when 2K less than full.
|
|
+ */
|
|
+ if (rxfifosz < 4096) {
|
|
+ csr6 &= ~DMA_CONTROL_EFC;
|
|
+ pr_debug("GMAC: disabling flow control, rxfifo too small(%d)\n",
|
|
+ rxfifosz);
|
|
+ } else {
|
|
+ csr6 |= DMA_CONTROL_EFC;
|
|
+ csr6 |= RFA_FULL_MINUS_1K;
|
|
+ csr6 |= RFD_FULL_MINUS_2K;
|
|
+ }
|
|
+ return csr6;
|
|
+}
|
|
+
|
|
static void dwmac1000_dma_operation_mode(void __iomem *ioaddr, int txmode,
|
|
- int rxmode)
|
|
+ int rxmode, int rxfifosz)
|
|
{
|
|
u32 csr6 = readl(ioaddr + DMA_CONTROL);
|
|
|
|
@@ -157,6 +174,9 @@ static void dwmac1000_dma_operation_mode
|
|
csr6 |= DMA_CONTROL_RTC_128;
|
|
}
|
|
|
|
+ /* Configure flow control based on rx fifo size */
|
|
+ csr6 = dwmac1000_configure_fc(csr6, rxfifosz);
|
|
+
|
|
writel(csr6, ioaddr + DMA_CONTROL);
|
|
}
|
|
|
|
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c
|
|
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c
|
|
@@ -72,7 +72,7 @@ static int dwmac100_dma_init(void __iome
|
|
* control register.
|
|
*/
|
|
static void dwmac100_dma_operation_mode(void __iomem *ioaddr, int txmode,
|
|
- int rxmode)
|
|
+ int rxmode, int rxfifosz)
|
|
{
|
|
u32 csr6 = readl(ioaddr + DMA_CONTROL);
|
|
|
|
--- a/drivers/net/ethernet/stmicro/stmmac/mmc_core.c
|
|
+++ b/drivers/net/ethernet/stmicro/stmmac/mmc_core.c
|
|
@@ -73,7 +73,7 @@
|
|
#define MMC_RX_OCTETCOUNT_G 0x00000188
|
|
#define MMC_RX_BROADCASTFRAME_G 0x0000018c
|
|
#define MMC_RX_MULTICASTFRAME_G 0x00000190
|
|
-#define MMC_RX_CRC_ERRROR 0x00000194
|
|
+#define MMC_RX_CRC_ERROR 0x00000194
|
|
#define MMC_RX_ALIGN_ERROR 0x00000198
|
|
#define MMC_RX_RUN_ERROR 0x0000019C
|
|
#define MMC_RX_JABBER_ERROR 0x000001A0
|
|
@@ -196,7 +196,7 @@ void dwmac_mmc_read(void __iomem *ioaddr
|
|
mmc->mmc_rx_octetcount_g += readl(ioaddr + MMC_RX_OCTETCOUNT_G);
|
|
mmc->mmc_rx_broadcastframe_g += readl(ioaddr + MMC_RX_BROADCASTFRAME_G);
|
|
mmc->mmc_rx_multicastframe_g += readl(ioaddr + MMC_RX_MULTICASTFRAME_G);
|
|
- mmc->mmc_rx_crc_error += readl(ioaddr + MMC_RX_CRC_ERRROR);
|
|
+ mmc->mmc_rx_crc_error += readl(ioaddr + MMC_RX_CRC_ERROR);
|
|
mmc->mmc_rx_align_error += readl(ioaddr + MMC_RX_ALIGN_ERROR);
|
|
mmc->mmc_rx_run_error += readl(ioaddr + MMC_RX_RUN_ERROR);
|
|
mmc->mmc_rx_jabber_error += readl(ioaddr + MMC_RX_JABBER_ERROR);
|
|
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h
|
|
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
|
|
@@ -34,6 +34,14 @@
|
|
#include <linux/ptp_clock_kernel.h>
|
|
#include <linux/reset.h>
|
|
|
|
+struct stmmac_resources {
|
|
+ void __iomem *addr;
|
|
+ const char *mac;
|
|
+ int wol_irq;
|
|
+ int lpi_irq;
|
|
+ int irq;
|
|
+};
|
|
+
|
|
struct stmmac_tx_info {
|
|
dma_addr_t buf;
|
|
bool map_as_page;
|
|
@@ -97,6 +105,7 @@ struct stmmac_priv {
|
|
int wolopts;
|
|
int wol_irq;
|
|
struct clk *stmmac_clk;
|
|
+ struct clk *pclk;
|
|
struct reset_control *stmmac_rst;
|
|
int clk_csr;
|
|
struct timer_list eee_ctrl_timer;
|
|
@@ -116,97 +125,28 @@ struct stmmac_priv {
|
|
int use_riwt;
|
|
int irq_wake;
|
|
spinlock_t ptp_lock;
|
|
+
|
|
+#ifdef CONFIG_DEBUG_FS
|
|
+ struct dentry *dbgfs_dir;
|
|
+ struct dentry *dbgfs_rings_status;
|
|
+ struct dentry *dbgfs_dma_cap;
|
|
+#endif
|
|
};
|
|
|
|
int stmmac_mdio_unregister(struct net_device *ndev);
|
|
int stmmac_mdio_register(struct net_device *ndev);
|
|
int stmmac_mdio_reset(struct mii_bus *mii);
|
|
void stmmac_set_ethtool_ops(struct net_device *netdev);
|
|
-extern const struct stmmac_desc_ops enh_desc_ops;
|
|
-extern const struct stmmac_desc_ops ndesc_ops;
|
|
-extern const struct stmmac_hwtimestamp stmmac_ptp;
|
|
+
|
|
int stmmac_ptp_register(struct stmmac_priv *priv);
|
|
void stmmac_ptp_unregister(struct stmmac_priv *priv);
|
|
int stmmac_resume(struct net_device *ndev);
|
|
int stmmac_suspend(struct net_device *ndev);
|
|
int stmmac_dvr_remove(struct net_device *ndev);
|
|
-struct stmmac_priv *stmmac_dvr_probe(struct device *device,
|
|
- struct plat_stmmacenet_data *plat_dat,
|
|
- void __iomem *addr);
|
|
+int stmmac_dvr_probe(struct device *device,
|
|
+ struct plat_stmmacenet_data *plat_dat,
|
|
+ struct stmmac_resources *res);
|
|
void stmmac_disable_eee_mode(struct stmmac_priv *priv);
|
|
bool stmmac_eee_init(struct stmmac_priv *priv);
|
|
|
|
-#ifdef CONFIG_STMMAC_PLATFORM
|
|
-#ifdef CONFIG_DWMAC_MESON
|
|
-extern const struct stmmac_of_data meson6_dwmac_data;
|
|
-#endif
|
|
-#ifdef CONFIG_DWMAC_SUNXI
|
|
-extern const struct stmmac_of_data sun7i_gmac_data;
|
|
-#endif
|
|
-#ifdef CONFIG_DWMAC_STI
|
|
-extern const struct stmmac_of_data stih4xx_dwmac_data;
|
|
-extern const struct stmmac_of_data stid127_dwmac_data;
|
|
-#endif
|
|
-#ifdef CONFIG_DWMAC_SOCFPGA
|
|
-extern const struct stmmac_of_data socfpga_gmac_data;
|
|
-#endif
|
|
-extern struct platform_driver stmmac_pltfr_driver;
|
|
-static inline int stmmac_register_platform(void)
|
|
-{
|
|
- int err;
|
|
-
|
|
- err = platform_driver_register(&stmmac_pltfr_driver);
|
|
- if (err)
|
|
- pr_err("stmmac: failed to register the platform driver\n");
|
|
-
|
|
- return err;
|
|
-}
|
|
-
|
|
-static inline void stmmac_unregister_platform(void)
|
|
-{
|
|
- platform_driver_unregister(&stmmac_pltfr_driver);
|
|
-}
|
|
-#else
|
|
-static inline int stmmac_register_platform(void)
|
|
-{
|
|
- pr_debug("stmmac: do not register the platf driver\n");
|
|
-
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static inline void stmmac_unregister_platform(void)
|
|
-{
|
|
-}
|
|
-#endif /* CONFIG_STMMAC_PLATFORM */
|
|
-
|
|
-#ifdef CONFIG_STMMAC_PCI
|
|
-extern struct pci_driver stmmac_pci_driver;
|
|
-static inline int stmmac_register_pci(void)
|
|
-{
|
|
- int err;
|
|
-
|
|
- err = pci_register_driver(&stmmac_pci_driver);
|
|
- if (err)
|
|
- pr_err("stmmac: failed to register the PCI driver\n");
|
|
-
|
|
- return err;
|
|
-}
|
|
-
|
|
-static inline void stmmac_unregister_pci(void)
|
|
-{
|
|
- pci_unregister_driver(&stmmac_pci_driver);
|
|
-}
|
|
-#else
|
|
-static inline int stmmac_register_pci(void)
|
|
-{
|
|
- pr_debug("stmmac: do not register the PCI driver\n");
|
|
-
|
|
- return 0;
|
|
-}
|
|
-
|
|
-static inline void stmmac_unregister_pci(void)
|
|
-{
|
|
-}
|
|
-#endif /* CONFIG_STMMAC_PCI */
|
|
-
|
|
#endif /* __STMMAC_H__ */
|
|
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
|
|
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
|
|
@@ -696,7 +696,7 @@ static int stmmac_set_coalesce(struct ne
|
|
(ec->tx_max_coalesced_frames == 0))
|
|
return -EINVAL;
|
|
|
|
- if ((ec->tx_coalesce_usecs > STMMAC_COAL_TX_TIMER) ||
|
|
+ if ((ec->tx_coalesce_usecs > STMMAC_MAX_COAL_TX_TICK) ||
|
|
(ec->tx_max_coalesced_frames > STMMAC_TX_MAX_FRAMES))
|
|
return -EINVAL;
|
|
|
|
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
|
|
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
|
|
@@ -44,14 +44,15 @@
|
|
#include <linux/slab.h>
|
|
#include <linux/prefetch.h>
|
|
#include <linux/pinctrl/consumer.h>
|
|
-#ifdef CONFIG_STMMAC_DEBUG_FS
|
|
+#ifdef CONFIG_DEBUG_FS
|
|
#include <linux/debugfs.h>
|
|
#include <linux/seq_file.h>
|
|
-#endif /* CONFIG_STMMAC_DEBUG_FS */
|
|
+#endif /* CONFIG_DEBUG_FS */
|
|
#include <linux/net_tstamp.h>
|
|
#include "stmmac_ptp.h"
|
|
#include "stmmac.h"
|
|
#include <linux/reset.h>
|
|
+#include <linux/of_mdio.h>
|
|
|
|
#define STMMAC_ALIGN(x) L1_CACHE_ALIGN(x)
|
|
|
|
@@ -116,17 +117,17 @@ MODULE_PARM_DESC(chain_mode, "To use cha
|
|
|
|
static irqreturn_t stmmac_interrupt(int irq, void *dev_id);
|
|
|
|
-#ifdef CONFIG_STMMAC_DEBUG_FS
|
|
+#ifdef CONFIG_DEBUG_FS
|
|
static int stmmac_init_fs(struct net_device *dev);
|
|
-static void stmmac_exit_fs(void);
|
|
+static void stmmac_exit_fs(struct net_device *dev);
|
|
#endif
|
|
|
|
#define STMMAC_COAL_TIMER(x) (jiffies + usecs_to_jiffies(x))
|
|
|
|
/**
|
|
* stmmac_verify_args - verify the driver parameters.
|
|
- * Description: it verifies if some wrong parameter is passed to the driver.
|
|
- * Note that wrong parameters are replaced with the default values.
|
|
+ * Description: it checks the driver parameters and set a default in case of
|
|
+ * errors.
|
|
*/
|
|
static void stmmac_verify_args(void)
|
|
{
|
|
@@ -191,14 +192,8 @@ static void stmmac_clk_csr_set(struct st
|
|
|
|
static void print_pkt(unsigned char *buf, int len)
|
|
{
|
|
- int j;
|
|
- pr_debug("len = %d byte, buf addr: 0x%p", len, buf);
|
|
- for (j = 0; j < len; j++) {
|
|
- if ((j % 16) == 0)
|
|
- pr_debug("\n %03x:", j);
|
|
- pr_debug(" %02x", buf[j]);
|
|
- }
|
|
- pr_debug("\n");
|
|
+ pr_debug("len = %d byte, buf addr: 0x%p\n", len, buf);
|
|
+ print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, buf, len);
|
|
}
|
|
|
|
/* minimum number of free TX descriptors required to wake up TX process */
|
|
@@ -210,7 +205,7 @@ static inline u32 stmmac_tx_avail(struct
|
|
}
|
|
|
|
/**
|
|
- * stmmac_hw_fix_mac_speed: callback for speed selection
|
|
+ * stmmac_hw_fix_mac_speed - callback for speed selection
|
|
* @priv: driver private structure
|
|
* Description: on some platforms (e.g. ST), some HW system configuraton
|
|
* registers have to be set according to the link speed negotiated.
|
|
@@ -224,9 +219,10 @@ static inline void stmmac_hw_fix_mac_spe
|
|
}
|
|
|
|
/**
|
|
- * stmmac_enable_eee_mode: Check and enter in LPI mode
|
|
+ * stmmac_enable_eee_mode - check and enter in LPI mode
|
|
* @priv: driver private structure
|
|
- * Description: this function is to verify and enter in LPI mode for EEE.
|
|
+ * Description: this function is to verify and enter in LPI mode in case of
|
|
+ * EEE.
|
|
*/
|
|
static void stmmac_enable_eee_mode(struct stmmac_priv *priv)
|
|
{
|
|
@@ -237,7 +233,7 @@ static void stmmac_enable_eee_mode(struc
|
|
}
|
|
|
|
/**
|
|
- * stmmac_disable_eee_mode: disable/exit from EEE
|
|
+ * stmmac_disable_eee_mode - disable and exit from LPI mode
|
|
* @priv: driver private structure
|
|
* Description: this function is to exit and disable EEE in case of
|
|
* LPI state is true. This is called by the xmit.
|
|
@@ -250,7 +246,7 @@ void stmmac_disable_eee_mode(struct stmm
|
|
}
|
|
|
|
/**
|
|
- * stmmac_eee_ctrl_timer: EEE TX SW timer.
|
|
+ * stmmac_eee_ctrl_timer - EEE TX SW timer.
|
|
* @arg : data hook
|
|
* Description:
|
|
* if there is no data transfer and if we are not in LPI state,
|
|
@@ -265,13 +261,12 @@ static void stmmac_eee_ctrl_timer(unsign
|
|
}
|
|
|
|
/**
|
|
- * stmmac_eee_init: init EEE
|
|
+ * stmmac_eee_init - init EEE
|
|
* @priv: driver private structure
|
|
* Description:
|
|
- * If the EEE support has been enabled while configuring the driver,
|
|
- * if the GMAC actually supports the EEE (from the HW cap reg) and the
|
|
- * phy can also manage EEE, so enable the LPI state and start the timer
|
|
- * to verify if the tx path can enter in LPI state.
|
|
+ * if the GMAC supports the EEE (from the HW cap reg) and the phy device
|
|
+ * can also manage EEE, this function enable the LPI state and start related
|
|
+ * timer.
|
|
*/
|
|
bool stmmac_eee_init(struct stmmac_priv *priv)
|
|
{
|
|
@@ -316,11 +311,11 @@ bool stmmac_eee_init(struct stmmac_priv
|
|
spin_lock_irqsave(&priv->lock, flags);
|
|
if (!priv->eee_active) {
|
|
priv->eee_active = 1;
|
|
- init_timer(&priv->eee_ctrl_timer);
|
|
- priv->eee_ctrl_timer.function = stmmac_eee_ctrl_timer;
|
|
- priv->eee_ctrl_timer.data = (unsigned long)priv;
|
|
- priv->eee_ctrl_timer.expires = STMMAC_LPI_T(eee_timer);
|
|
- add_timer(&priv->eee_ctrl_timer);
|
|
+ setup_timer(&priv->eee_ctrl_timer,
|
|
+ stmmac_eee_ctrl_timer,
|
|
+ (unsigned long)priv);
|
|
+ mod_timer(&priv->eee_ctrl_timer,
|
|
+ STMMAC_LPI_T(eee_timer));
|
|
|
|
priv->hw->mac->set_eee_timer(priv->hw,
|
|
STMMAC_DEFAULT_LIT_LS,
|
|
@@ -338,7 +333,7 @@ out:
|
|
return ret;
|
|
}
|
|
|
|
-/* stmmac_get_tx_hwtstamp: get HW TX timestamps
|
|
+/* stmmac_get_tx_hwtstamp - get HW TX timestamps
|
|
* @priv: driver private structure
|
|
* @entry : descriptor index to be used.
|
|
* @skb : the socket buffer
|
|
@@ -380,7 +375,7 @@ static void stmmac_get_tx_hwtstamp(struc
|
|
return;
|
|
}
|
|
|
|
-/* stmmac_get_rx_hwtstamp: get HW RX timestamps
|
|
+/* stmmac_get_rx_hwtstamp - get HW RX timestamps
|
|
* @priv: driver private structure
|
|
* @entry : descriptor index to be used.
|
|
* @skb : the socket buffer
|
|
@@ -615,7 +610,7 @@ static int stmmac_hwtstamp_ioctl(struct
|
|
* where, freq_div_ratio = clk_ptp_ref_i/50MHz
|
|
* hence, addend = ((2^32) * 50MHz)/clk_ptp_ref_i;
|
|
* NOTE: clk_ptp_ref_i should be >= 50MHz to
|
|
- * achive 20ns accuracy.
|
|
+ * achieve 20ns accuracy.
|
|
*
|
|
* 2^x * y == (y << x), hence
|
|
* 2^32 * 50000000 ==> (50000000 << 32)
|
|
@@ -636,11 +631,11 @@ static int stmmac_hwtstamp_ioctl(struct
|
|
}
|
|
|
|
/**
|
|
- * stmmac_init_ptp: init PTP
|
|
+ * stmmac_init_ptp - init PTP
|
|
* @priv: driver private structure
|
|
- * Description: this is to verify if the HW supports the PTPv1 or v2.
|
|
+ * Description: this is to verify if the HW supports the PTPv1 or PTPv2.
|
|
* This is done by looking at the HW cap. register.
|
|
- * Also it registers the ptp driver.
|
|
+ * This function also registers the ptp driver.
|
|
*/
|
|
static int stmmac_init_ptp(struct stmmac_priv *priv)
|
|
{
|
|
@@ -682,9 +677,13 @@ static void stmmac_release_ptp(struct st
|
|
}
|
|
|
|
/**
|
|
- * stmmac_adjust_link
|
|
+ * stmmac_adjust_link - adjusts the link parameters
|
|
* @dev: net device structure
|
|
- * Description: it adjusts the link parameters.
|
|
+ * Description: this is the helper called by the physical abstraction layer
|
|
+ * drivers to communicate the phy link status. According the speed and duplex
|
|
+ * this driver can invoke registered glue-logic as well.
|
|
+ * It also invoke the eee initialization because it could happen when switch
|
|
+ * on different networks (that are eee capable).
|
|
*/
|
|
static void stmmac_adjust_link(struct net_device *dev)
|
|
{
|
|
@@ -774,7 +773,7 @@ static void stmmac_adjust_link(struct ne
|
|
}
|
|
|
|
/**
|
|
- * stmmac_check_pcs_mode: verify if RGMII/SGMII is supported
|
|
+ * stmmac_check_pcs_mode - verify if RGMII/SGMII is supported
|
|
* @priv: driver private structure
|
|
* Description: this is to verify if the HW supports the PCS.
|
|
* Physical Coding Sublayer (PCS) interface that can be used when the MAC is
|
|
@@ -818,21 +817,31 @@ static int stmmac_init_phy(struct net_de
|
|
priv->speed = 0;
|
|
priv->oldduplex = -1;
|
|
|
|
- if (priv->plat->phy_bus_name)
|
|
- snprintf(bus_id, MII_BUS_ID_SIZE, "%s-%x",
|
|
- priv->plat->phy_bus_name, priv->plat->bus_id);
|
|
- else
|
|
- snprintf(bus_id, MII_BUS_ID_SIZE, "stmmac-%x",
|
|
- priv->plat->bus_id);
|
|
+ if (priv->plat->phy_node) {
|
|
+ phydev = of_phy_connect(dev, priv->plat->phy_node,
|
|
+ &stmmac_adjust_link, 0, interface);
|
|
+ } else {
|
|
+ if (priv->plat->phy_bus_name)
|
|
+ snprintf(bus_id, MII_BUS_ID_SIZE, "%s-%x",
|
|
+ priv->plat->phy_bus_name, priv->plat->bus_id);
|
|
+ else
|
|
+ snprintf(bus_id, MII_BUS_ID_SIZE, "stmmac-%x",
|
|
+ priv->plat->bus_id);
|
|
|
|
- snprintf(phy_id_fmt, MII_BUS_ID_SIZE + 3, PHY_ID_FMT, bus_id,
|
|
- priv->plat->phy_addr);
|
|
- pr_debug("stmmac_init_phy: trying to attach to %s\n", phy_id_fmt);
|
|
+ snprintf(phy_id_fmt, MII_BUS_ID_SIZE + 3, PHY_ID_FMT, bus_id,
|
|
+ priv->plat->phy_addr);
|
|
+ pr_debug("stmmac_init_phy: trying to attach to %s\n",
|
|
+ phy_id_fmt);
|
|
|
|
- phydev = phy_connect(dev, phy_id_fmt, &stmmac_adjust_link, interface);
|
|
+ phydev = phy_connect(dev, phy_id_fmt, &stmmac_adjust_link,
|
|
+ interface);
|
|
+ }
|
|
|
|
- if (IS_ERR(phydev)) {
|
|
+ if (IS_ERR_OR_NULL(phydev)) {
|
|
pr_err("%s: Could not attach to PHY\n", dev->name);
|
|
+ if (!phydev)
|
|
+ return -ENODEV;
|
|
+
|
|
return PTR_ERR(phydev);
|
|
}
|
|
|
|
@@ -850,7 +859,7 @@ static int stmmac_init_phy(struct net_de
|
|
* device as well.
|
|
* Note: phydev->phy_id is the result of reading the UID PHY registers.
|
|
*/
|
|
- if (phydev->phy_id == 0) {
|
|
+ if (!priv->plat->phy_node && phydev->phy_id == 0) {
|
|
phy_disconnect(phydev);
|
|
return -ENODEV;
|
|
}
|
|
@@ -863,7 +872,7 @@ static int stmmac_init_phy(struct net_de
|
|
}
|
|
|
|
/**
|
|
- * stmmac_display_ring: display ring
|
|
+ * stmmac_display_ring - display ring
|
|
* @head: pointer to the head of the ring passed.
|
|
* @size: size of the ring.
|
|
* @extend_desc: to verify if extended descriptors are used.
|
|
@@ -931,7 +940,7 @@ static int stmmac_set_bfsize(int mtu, in
|
|
}
|
|
|
|
/**
|
|
- * stmmac_clear_descriptors: clear descriptors
|
|
+ * stmmac_clear_descriptors - clear descriptors
|
|
* @priv: driver private structure
|
|
* Description: this function is called to clear the tx and rx descriptors
|
|
* in case of both basic and extended descriptors are used.
|
|
@@ -963,18 +972,25 @@ static void stmmac_clear_descriptors(str
|
|
(i == txsize - 1));
|
|
}
|
|
|
|
+/**
|
|
+ * stmmac_init_rx_buffers - init the RX descriptor buffer.
|
|
+ * @priv: driver private structure
|
|
+ * @p: descriptor pointer
|
|
+ * @i: descriptor index
|
|
+ * @flags: gfp flag.
|
|
+ * Description: this function is called to allocate a receive buffer, perform
|
|
+ * the DMA mapping and init the descriptor.
|
|
+ */
|
|
static int stmmac_init_rx_buffers(struct stmmac_priv *priv, struct dma_desc *p,
|
|
int i, gfp_t flags)
|
|
{
|
|
struct sk_buff *skb;
|
|
|
|
- skb = __netdev_alloc_skb(priv->dev, priv->dma_buf_sz + NET_IP_ALIGN,
|
|
- flags);
|
|
+ skb = __netdev_alloc_skb_ip_align(priv->dev, priv->dma_buf_sz, flags);
|
|
if (!skb) {
|
|
pr_err("%s: Rx init fails; skb is NULL\n", __func__);
|
|
return -ENOMEM;
|
|
}
|
|
- skb_reserve(skb, NET_IP_ALIGN);
|
|
priv->rx_skbuff[i] = skb;
|
|
priv->rx_skbuff_dma[i] = dma_map_single(priv->device, skb->data,
|
|
priv->dma_buf_sz,
|
|
@@ -1007,7 +1023,8 @@ static void stmmac_free_rx_buffers(struc
|
|
/**
|
|
* init_dma_desc_rings - init the RX/TX descriptor rings
|
|
* @dev: net device structure
|
|
- * Description: this function initializes the DMA RX/TX descriptors
|
|
+ * @flags: gfp flag.
|
|
+ * Description: this function initializes the DMA RX/TX descriptors
|
|
* and allocates the socket buffers. It suppors the chained and ring
|
|
* modes.
|
|
*/
|
|
@@ -1089,6 +1106,7 @@ static int init_dma_desc_rings(struct ne
|
|
|
|
priv->dirty_tx = 0;
|
|
priv->cur_tx = 0;
|
|
+ netdev_reset_queue(priv->dev);
|
|
|
|
stmmac_clear_descriptors(priv);
|
|
|
|
@@ -1144,6 +1162,14 @@ static void dma_free_tx_skbufs(struct st
|
|
}
|
|
}
|
|
|
|
+/**
|
|
+ * alloc_dma_desc_resources - alloc TX/RX resources.
|
|
+ * @priv: private structure
|
|
+ * Description: according to which descriptor can be used (extend or basic)
|
|
+ * this function allocates the resources for TX and RX paths. In case of
|
|
+ * reception, for example, it pre-allocated the RX socket buffer in order to
|
|
+ * allow zero-copy mechanism.
|
|
+ */
|
|
static int alloc_dma_desc_resources(struct stmmac_priv *priv)
|
|
{
|
|
unsigned int txsize = priv->dma_tx_size;
|
|
@@ -1255,13 +1281,15 @@ static void free_dma_desc_resources(stru
|
|
/**
|
|
* stmmac_dma_operation_mode - HW DMA operation mode
|
|
* @priv: driver private structure
|
|
- * Description: it sets the DMA operation mode: tx/rx DMA thresholds
|
|
- * or Store-And-Forward capability.
|
|
+ * Description: it is used for configuring the DMA operation mode register in
|
|
+ * order to program the tx/rx DMA thresholds or Store-And-Forward mode.
|
|
*/
|
|
static void stmmac_dma_operation_mode(struct stmmac_priv *priv)
|
|
{
|
|
+ int rxfifosz = priv->plat->rx_fifo_size;
|
|
+
|
|
if (priv->plat->force_thresh_dma_mode)
|
|
- priv->hw->dma->dma_mode(priv->ioaddr, tc, tc);
|
|
+ priv->hw->dma->dma_mode(priv->ioaddr, tc, tc, rxfifosz);
|
|
else if (priv->plat->force_sf_dma_mode || priv->plat->tx_coe) {
|
|
/*
|
|
* In case of GMAC, SF mode can be enabled
|
|
@@ -1270,20 +1298,23 @@ static void stmmac_dma_operation_mode(st
|
|
* 2) There is no bugged Jumbo frame support
|
|
* that needs to not insert csum in the TDES.
|
|
*/
|
|
- priv->hw->dma->dma_mode(priv->ioaddr, SF_DMA_MODE, SF_DMA_MODE);
|
|
- tc = SF_DMA_MODE;
|
|
+ priv->hw->dma->dma_mode(priv->ioaddr, SF_DMA_MODE, SF_DMA_MODE,
|
|
+ rxfifosz);
|
|
+ priv->xstats.threshold = SF_DMA_MODE;
|
|
} else
|
|
- priv->hw->dma->dma_mode(priv->ioaddr, tc, SF_DMA_MODE);
|
|
+ priv->hw->dma->dma_mode(priv->ioaddr, tc, SF_DMA_MODE,
|
|
+ rxfifosz);
|
|
}
|
|
|
|
/**
|
|
- * stmmac_tx_clean:
|
|
+ * stmmac_tx_clean - to manage the transmission completion
|
|
* @priv: driver private structure
|
|
- * Description: it reclaims resources after transmission completes.
|
|
+ * Description: it reclaims the transmit resources after transmission completes.
|
|
*/
|
|
static void stmmac_tx_clean(struct stmmac_priv *priv)
|
|
{
|
|
unsigned int txsize = priv->dma_tx_size;
|
|
+ unsigned int bytes_compl = 0, pkts_compl = 0;
|
|
|
|
spin_lock(&priv->tx_lock);
|
|
|
|
@@ -1340,6 +1371,8 @@ static void stmmac_tx_clean(struct stmma
|
|
priv->hw->mode->clean_desc3(priv, p);
|
|
|
|
if (likely(skb != NULL)) {
|
|
+ pkts_compl++;
|
|
+ bytes_compl += skb->len;
|
|
dev_consume_skb_any(skb);
|
|
priv->tx_skbuff[entry] = NULL;
|
|
}
|
|
@@ -1348,6 +1381,9 @@ static void stmmac_tx_clean(struct stmma
|
|
|
|
priv->dirty_tx++;
|
|
}
|
|
+
|
|
+ netdev_completed_queue(priv->dev, pkts_compl, bytes_compl);
|
|
+
|
|
if (unlikely(netif_queue_stopped(priv->dev) &&
|
|
stmmac_tx_avail(priv) > STMMAC_TX_THRESH(priv))) {
|
|
netif_tx_lock(priv->dev);
|
|
@@ -1378,10 +1414,10 @@ static inline void stmmac_disable_dma_ir
|
|
}
|
|
|
|
/**
|
|
- * stmmac_tx_err: irq tx error mng function
|
|
+ * stmmac_tx_err - to manage the tx error
|
|
* @priv: driver private structure
|
|
* Description: it cleans the descriptors and restarts the transmission
|
|
- * in case of errors.
|
|
+ * in case of transmission errors.
|
|
*/
|
|
static void stmmac_tx_err(struct stmmac_priv *priv)
|
|
{
|
|
@@ -1402,6 +1438,7 @@ static void stmmac_tx_err(struct stmmac_
|
|
(i == txsize - 1));
|
|
priv->dirty_tx = 0;
|
|
priv->cur_tx = 0;
|
|
+ netdev_reset_queue(priv->dev);
|
|
priv->hw->dma->start_tx(priv->ioaddr);
|
|
|
|
priv->dev->stats.tx_errors++;
|
|
@@ -1409,16 +1446,16 @@ static void stmmac_tx_err(struct stmmac_
|
|
}
|
|
|
|
/**
|
|
- * stmmac_dma_interrupt: DMA ISR
|
|
+ * stmmac_dma_interrupt - DMA ISR
|
|
* @priv: driver private structure
|
|
* Description: this is the DMA ISR. It is called by the main ISR.
|
|
- * It calls the dwmac dma routine to understand which type of interrupt
|
|
- * happened. In case of there is a Normal interrupt and either TX or RX
|
|
- * interrupt happened so the NAPI is scheduled.
|
|
+ * It calls the dwmac dma routine and schedule poll method in case of some
|
|
+ * work can be done.
|
|
*/
|
|
static void stmmac_dma_interrupt(struct stmmac_priv *priv)
|
|
{
|
|
int status;
|
|
+ int rxfifosz = priv->plat->rx_fifo_size;
|
|
|
|
status = priv->hw->dma->dma_interrupt(priv->ioaddr, &priv->xstats);
|
|
if (likely((status & handle_rx)) || (status & handle_tx)) {
|
|
@@ -1429,9 +1466,15 @@ static void stmmac_dma_interrupt(struct
|
|
}
|
|
if (unlikely(status & tx_hard_error_bump_tc)) {
|
|
/* Try to bump up the dma threshold on this failure */
|
|
- if (unlikely(tc != SF_DMA_MODE) && (tc <= 256)) {
|
|
+ if (unlikely(priv->xstats.threshold != SF_DMA_MODE) &&
|
|
+ (tc <= 256)) {
|
|
tc += 64;
|
|
- priv->hw->dma->dma_mode(priv->ioaddr, tc, SF_DMA_MODE);
|
|
+ if (priv->plat->force_thresh_dma_mode)
|
|
+ priv->hw->dma->dma_mode(priv->ioaddr, tc, tc,
|
|
+ rxfifosz);
|
|
+ else
|
|
+ priv->hw->dma->dma_mode(priv->ioaddr, tc,
|
|
+ SF_DMA_MODE, rxfifosz);
|
|
priv->xstats.threshold = tc;
|
|
}
|
|
} else if (unlikely(status == tx_hard_error))
|
|
@@ -1457,6 +1500,12 @@ static void stmmac_mmc_setup(struct stmm
|
|
pr_info(" No MAC Management Counters available\n");
|
|
}
|
|
|
|
+/**
|
|
+ * stmmac_get_synopsys_id - return the SYINID.
|
|
+ * @priv: driver private structure
|
|
+ * Description: this simple function is to decode and return the SYINID
|
|
+ * starting from the HW core register.
|
|
+ */
|
|
static u32 stmmac_get_synopsys_id(struct stmmac_priv *priv)
|
|
{
|
|
u32 hwid = priv->hw->synopsys_uid;
|
|
@@ -1475,11 +1524,11 @@ static u32 stmmac_get_synopsys_id(struct
|
|
}
|
|
|
|
/**
|
|
- * stmmac_selec_desc_mode: to select among: normal/alternate/extend descriptors
|
|
+ * stmmac_selec_desc_mode - to select among: normal/alternate/extend descriptors
|
|
* @priv: driver private structure
|
|
* Description: select the Enhanced/Alternate or Normal descriptors.
|
|
- * In case of Enhanced/Alternate, it looks at the extended descriptors are
|
|
- * supported by the HW cap. register.
|
|
+ * In case of Enhanced/Alternate, it checks if the extended descriptors are
|
|
+ * supported by the HW capability register.
|
|
*/
|
|
static void stmmac_selec_desc_mode(struct stmmac_priv *priv)
|
|
{
|
|
@@ -1501,7 +1550,7 @@ static void stmmac_selec_desc_mode(struc
|
|
}
|
|
|
|
/**
|
|
- * stmmac_get_hw_features: get MAC capabilities from the HW cap. register.
|
|
+ * stmmac_get_hw_features - get MAC capabilities from the HW cap. register.
|
|
* @priv: driver private structure
|
|
* Description:
|
|
* new GMAC chip generations have a new register to indicate the
|
|
@@ -1559,7 +1608,7 @@ static int stmmac_get_hw_features(struct
|
|
}
|
|
|
|
/**
|
|
- * stmmac_check_ether_addr: check if the MAC addr is valid
|
|
+ * stmmac_check_ether_addr - check if the MAC addr is valid
|
|
* @priv: driver private structure
|
|
* Description:
|
|
* it is to verify if the MAC address is valid, in case of failures it
|
|
@@ -1578,7 +1627,7 @@ static void stmmac_check_ether_addr(stru
|
|
}
|
|
|
|
/**
|
|
- * stmmac_init_dma_engine: DMA init.
|
|
+ * stmmac_init_dma_engine - DMA init.
|
|
* @priv: driver private structure
|
|
* Description:
|
|
* It inits the DMA invoking the specific MAC/GMAC callback.
|
|
@@ -1607,7 +1656,7 @@ static int stmmac_init_dma_engine(struct
|
|
}
|
|
|
|
/**
|
|
- * stmmac_tx_timer: mitigation sw timer for tx.
|
|
+ * stmmac_tx_timer - mitigation sw timer for tx.
|
|
* @data: data pointer
|
|
* Description:
|
|
* This is the timer handler to directly invoke the stmmac_tx_clean.
|
|
@@ -1620,7 +1669,7 @@ static void stmmac_tx_timer(unsigned lon
|
|
}
|
|
|
|
/**
|
|
- * stmmac_init_tx_coalesce: init tx mitigation options.
|
|
+ * stmmac_init_tx_coalesce - init tx mitigation options.
|
|
* @priv: driver private structure
|
|
* Description:
|
|
* This inits the transmit coalesce parameters: i.e. timer rate,
|
|
@@ -1639,15 +1688,18 @@ static void stmmac_init_tx_coalesce(stru
|
|
}
|
|
|
|
/**
|
|
- * stmmac_hw_setup: setup mac in a usable state.
|
|
+ * stmmac_hw_setup - setup mac in a usable state.
|
|
* @dev : pointer to the device structure.
|
|
* Description:
|
|
- * This function sets up the ip in a usable state.
|
|
+ * this is the main function to setup the HW in a usable state because the
|
|
+ * dma engine is reset, the core registers are configured (e.g. AXI,
|
|
+ * Checksum features, timers). The DMA is ready to start receiving and
|
|
+ * transmitting.
|
|
* Return value:
|
|
* 0 on success and an appropriate (-)ve integer as defined in errno.h
|
|
* file on failure.
|
|
*/
|
|
-static int stmmac_hw_setup(struct net_device *dev)
|
|
+static int stmmac_hw_setup(struct net_device *dev, bool init_ptp)
|
|
{
|
|
struct stmmac_priv *priv = netdev_priv(dev);
|
|
int ret;
|
|
@@ -1684,11 +1736,13 @@ static int stmmac_hw_setup(struct net_de
|
|
|
|
stmmac_mmc_setup(priv);
|
|
|
|
- ret = stmmac_init_ptp(priv);
|
|
- if (ret && ret != -EOPNOTSUPP)
|
|
- pr_warn("%s: failed PTP initialisation\n", __func__);
|
|
+ if (init_ptp) {
|
|
+ ret = stmmac_init_ptp(priv);
|
|
+ if (ret && ret != -EOPNOTSUPP)
|
|
+ pr_warn("%s: failed PTP initialisation\n", __func__);
|
|
+ }
|
|
|
|
-#ifdef CONFIG_STMMAC_DEBUG_FS
|
|
+#ifdef CONFIG_DEBUG_FS
|
|
ret = stmmac_init_fs(dev);
|
|
if (ret < 0)
|
|
pr_warn("%s: failed debugFS registration\n", __func__);
|
|
@@ -1763,7 +1817,7 @@ static int stmmac_open(struct net_device
|
|
goto init_error;
|
|
}
|
|
|
|
- ret = stmmac_hw_setup(dev);
|
|
+ ret = stmmac_hw_setup(dev, true);
|
|
if (ret < 0) {
|
|
pr_err("%s: Hw setup failed\n", __func__);
|
|
goto init_error;
|
|
@@ -1870,8 +1924,8 @@ static int stmmac_release(struct net_dev
|
|
|
|
netif_carrier_off(dev);
|
|
|
|
-#ifdef CONFIG_STMMAC_DEBUG_FS
|
|
- stmmac_exit_fs();
|
|
+#ifdef CONFIG_DEBUG_FS
|
|
+ stmmac_exit_fs(dev);
|
|
#endif
|
|
|
|
stmmac_release_ptp(priv);
|
|
@@ -1880,7 +1934,7 @@ static int stmmac_release(struct net_dev
|
|
}
|
|
|
|
/**
|
|
- * stmmac_xmit: Tx entry point of the driver
|
|
+ * stmmac_xmit - Tx entry point of the driver
|
|
* @skb : the socket buffer
|
|
* @dev : device pointer
|
|
* Description : this is the tx entry point of the driver.
|
|
@@ -2024,6 +2078,7 @@ static netdev_tx_t stmmac_xmit(struct sk
|
|
if (!priv->hwts_tx_en)
|
|
skb_tx_timestamp(skb);
|
|
|
|
+ netdev_sent_queue(dev, skb->len);
|
|
priv->hw->dma->enable_dma_transmission(priv->ioaddr);
|
|
|
|
spin_unlock(&priv->tx_lock);
|
|
@@ -2055,7 +2110,7 @@ static void stmmac_rx_vlan(struct net_de
|
|
|
|
|
|
/**
|
|
- * stmmac_rx_refill: refill used skb preallocated buffers
|
|
+ * stmmac_rx_refill - refill used skb preallocated buffers
|
|
* @priv: driver private structure
|
|
* Description : this is to reallocate the skb for the reception process
|
|
* that is based on zero-copy.
|
|
@@ -2106,7 +2161,7 @@ static inline void stmmac_rx_refill(stru
|
|
}
|
|
|
|
/**
|
|
- * stmmac_rx_refill: refill used skb preallocated buffers
|
|
+ * stmmac_rx - manage the receive process
|
|
* @priv: driver private structure
|
|
* @limit: napi bugget.
|
|
* Description : this the function called by the napi poll method.
|
|
@@ -2375,8 +2430,11 @@ static int stmmac_set_features(struct ne
|
|
* @irq: interrupt number.
|
|
* @dev_id: to pass the net device pointer.
|
|
* Description: this is the main driver interrupt service routine.
|
|
- * It calls the DMA ISR and also the core ISR to manage PMT, MMC, LPI
|
|
- * interrupts.
|
|
+ * It can call:
|
|
+ * o DMA service routine (to manage incoming frame reception and transmission
|
|
+ * status)
|
|
+ * o Core interrupts to manage: remote wake-up, management counter, LPI
|
|
+ * interrupts.
|
|
*/
|
|
static irqreturn_t stmmac_interrupt(int irq, void *dev_id)
|
|
{
|
|
@@ -2457,10 +2515,8 @@ static int stmmac_ioctl(struct net_devic
|
|
return ret;
|
|
}
|
|
|
|
-#ifdef CONFIG_STMMAC_DEBUG_FS
|
|
+#ifdef CONFIG_DEBUG_FS
|
|
static struct dentry *stmmac_fs_dir;
|
|
-static struct dentry *stmmac_rings_status;
|
|
-static struct dentry *stmmac_dma_cap;
|
|
|
|
static void sysfs_display_ring(void *head, int size, int extend_desc,
|
|
struct seq_file *seq)
|
|
@@ -2599,36 +2655,39 @@ static const struct file_operations stmm
|
|
|
|
static int stmmac_init_fs(struct net_device *dev)
|
|
{
|
|
- /* Create debugfs entries */
|
|
- stmmac_fs_dir = debugfs_create_dir(STMMAC_RESOURCE_NAME, NULL);
|
|
+ struct stmmac_priv *priv = netdev_priv(dev);
|
|
|
|
- if (!stmmac_fs_dir || IS_ERR(stmmac_fs_dir)) {
|
|
- pr_err("ERROR %s, debugfs create directory failed\n",
|
|
- STMMAC_RESOURCE_NAME);
|
|
+ /* Create per netdev entries */
|
|
+ priv->dbgfs_dir = debugfs_create_dir(dev->name, stmmac_fs_dir);
|
|
+
|
|
+ if (!priv->dbgfs_dir || IS_ERR(priv->dbgfs_dir)) {
|
|
+ pr_err("ERROR %s/%s, debugfs create directory failed\n",
|
|
+ STMMAC_RESOURCE_NAME, dev->name);
|
|
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/* Entry to report DMA RX/TX rings */
|
|
- stmmac_rings_status = debugfs_create_file("descriptors_status",
|
|
- S_IRUGO, stmmac_fs_dir, dev,
|
|
- &stmmac_rings_status_fops);
|
|
+ priv->dbgfs_rings_status =
|
|
+ debugfs_create_file("descriptors_status", S_IRUGO,
|
|
+ priv->dbgfs_dir, dev,
|
|
+ &stmmac_rings_status_fops);
|
|
|
|
- if (!stmmac_rings_status || IS_ERR(stmmac_rings_status)) {
|
|
+ if (!priv->dbgfs_rings_status || IS_ERR(priv->dbgfs_rings_status)) {
|
|
pr_info("ERROR creating stmmac ring debugfs file\n");
|
|
- debugfs_remove(stmmac_fs_dir);
|
|
+ debugfs_remove_recursive(priv->dbgfs_dir);
|
|
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/* Entry to report the DMA HW features */
|
|
- stmmac_dma_cap = debugfs_create_file("dma_cap", S_IRUGO, stmmac_fs_dir,
|
|
- dev, &stmmac_dma_cap_fops);
|
|
+ priv->dbgfs_dma_cap = debugfs_create_file("dma_cap", S_IRUGO,
|
|
+ priv->dbgfs_dir,
|
|
+ dev, &stmmac_dma_cap_fops);
|
|
|
|
- if (!stmmac_dma_cap || IS_ERR(stmmac_dma_cap)) {
|
|
+ if (!priv->dbgfs_dma_cap || IS_ERR(priv->dbgfs_dma_cap)) {
|
|
pr_info("ERROR creating stmmac MMC debugfs file\n");
|
|
- debugfs_remove(stmmac_rings_status);
|
|
- debugfs_remove(stmmac_fs_dir);
|
|
+ debugfs_remove_recursive(priv->dbgfs_dir);
|
|
|
|
return -ENOMEM;
|
|
}
|
|
@@ -2636,13 +2695,13 @@ static int stmmac_init_fs(struct net_dev
|
|
return 0;
|
|
}
|
|
|
|
-static void stmmac_exit_fs(void)
|
|
+static void stmmac_exit_fs(struct net_device *dev)
|
|
{
|
|
- debugfs_remove(stmmac_rings_status);
|
|
- debugfs_remove(stmmac_dma_cap);
|
|
- debugfs_remove(stmmac_fs_dir);
|
|
+ struct stmmac_priv *priv = netdev_priv(dev);
|
|
+
|
|
+ debugfs_remove_recursive(priv->dbgfs_dir);
|
|
}
|
|
-#endif /* CONFIG_STMMAC_DEBUG_FS */
|
|
+#endif /* CONFIG_DEBUG_FS */
|
|
|
|
static const struct net_device_ops stmmac_netdev_ops = {
|
|
.ndo_open = stmmac_open,
|
|
@@ -2663,11 +2722,10 @@ static const struct net_device_ops stmma
|
|
/**
|
|
* stmmac_hw_init - Init the MAC device
|
|
* @priv: driver private structure
|
|
- * Description: this function detects which MAC device
|
|
- * (GMAC/MAC10-100) has to attached, checks the HW capability
|
|
- * (if supported) and sets the driver's features (for example
|
|
- * to use the ring or chaine mode or support the normal/enh
|
|
- * descriptor structure).
|
|
+ * Description: this function is to configure the MAC device according to
|
|
+ * some platform parameters or the HW capability register. It prepares the
|
|
+ * driver to use either ring or chain modes and to setup either enhanced or
|
|
+ * normal descriptors.
|
|
*/
|
|
static int stmmac_hw_init(struct stmmac_priv *priv)
|
|
{
|
|
@@ -2714,7 +2772,11 @@ static int stmmac_hw_init(struct stmmac_
|
|
priv->plat->enh_desc = priv->dma_cap.enh_desc;
|
|
priv->plat->pmt = priv->dma_cap.pmt_remote_wake_up;
|
|
|
|
- priv->plat->tx_coe = priv->dma_cap.tx_coe;
|
|
+ /* TXCOE doesn't work in thresh DMA mode */
|
|
+ if (priv->plat->force_thresh_dma_mode)
|
|
+ priv->plat->tx_coe = 0;
|
|
+ else
|
|
+ priv->plat->tx_coe = priv->dma_cap.tx_coe;
|
|
|
|
if (priv->dma_cap.rx_coe_type2)
|
|
priv->plat->rx_coe = STMMAC_RX_COE_TYPE2;
|
|
@@ -2747,13 +2809,15 @@ static int stmmac_hw_init(struct stmmac_
|
|
* stmmac_dvr_probe
|
|
* @device: device pointer
|
|
* @plat_dat: platform data pointer
|
|
- * @addr: iobase memory address
|
|
+ * @res: stmmac resource pointer
|
|
* Description: this is the main probe function used to
|
|
* call the alloc_etherdev, allocate the priv structure.
|
|
+ * Return:
|
|
+ * returns 0 on success, otherwise errno.
|
|
*/
|
|
-struct stmmac_priv *stmmac_dvr_probe(struct device *device,
|
|
- struct plat_stmmacenet_data *plat_dat,
|
|
- void __iomem *addr)
|
|
+int stmmac_dvr_probe(struct device *device,
|
|
+ struct plat_stmmacenet_data *plat_dat,
|
|
+ struct stmmac_resources *res)
|
|
{
|
|
int ret = 0;
|
|
struct net_device *ndev = NULL;
|
|
@@ -2761,7 +2825,7 @@ struct stmmac_priv *stmmac_dvr_probe(str
|
|
|
|
ndev = alloc_etherdev(sizeof(struct stmmac_priv));
|
|
if (!ndev)
|
|
- return NULL;
|
|
+ return -ENOMEM;
|
|
|
|
SET_NETDEV_DEV(ndev, device);
|
|
|
|
@@ -2772,8 +2836,17 @@ struct stmmac_priv *stmmac_dvr_probe(str
|
|
stmmac_set_ethtool_ops(ndev);
|
|
priv->pause = pause;
|
|
priv->plat = plat_dat;
|
|
- priv->ioaddr = addr;
|
|
- priv->dev->base_addr = (unsigned long)addr;
|
|
+ priv->ioaddr = res->addr;
|
|
+ priv->dev->base_addr = (unsigned long)res->addr;
|
|
+
|
|
+ priv->dev->irq = res->irq;
|
|
+ priv->wol_irq = res->wol_irq;
|
|
+ priv->lpi_irq = res->lpi_irq;
|
|
+
|
|
+ if (res->mac)
|
|
+ memcpy(priv->dev->dev_addr, res->mac, ETH_ALEN);
|
|
+
|
|
+ dev_set_drvdata(device, priv->dev);
|
|
|
|
/* Verify driver arguments */
|
|
stmmac_verify_args();
|
|
@@ -2800,6 +2873,16 @@ struct stmmac_priv *stmmac_dvr_probe(str
|
|
}
|
|
clk_prepare_enable(priv->stmmac_clk);
|
|
|
|
+ priv->pclk = devm_clk_get(priv->device, "pclk");
|
|
+ if (IS_ERR(priv->pclk)) {
|
|
+ if (PTR_ERR(priv->pclk) == -EPROBE_DEFER) {
|
|
+ ret = -EPROBE_DEFER;
|
|
+ goto error_pclk_get;
|
|
+ }
|
|
+ priv->pclk = NULL;
|
|
+ }
|
|
+ clk_prepare_enable(priv->pclk);
|
|
+
|
|
priv->stmmac_rst = devm_reset_control_get(priv->device,
|
|
STMMAC_RESOURCE_NAME);
|
|
if (IS_ERR(priv->stmmac_rst)) {
|
|
@@ -2878,19 +2961,22 @@ struct stmmac_priv *stmmac_dvr_probe(str
|
|
}
|
|
}
|
|
|
|
- return priv;
|
|
+ return 0;
|
|
|
|
error_mdio_register:
|
|
unregister_netdev(ndev);
|
|
error_netdev_register:
|
|
netif_napi_del(&priv->napi);
|
|
error_hw_init:
|
|
+ clk_disable_unprepare(priv->pclk);
|
|
+error_pclk_get:
|
|
clk_disable_unprepare(priv->stmmac_clk);
|
|
error_clk_get:
|
|
free_netdev(ndev);
|
|
|
|
- return ERR_PTR(ret);
|
|
+ return ret;
|
|
}
|
|
+EXPORT_SYMBOL_GPL(stmmac_dvr_probe);
|
|
|
|
/**
|
|
* stmmac_dvr_remove
|
|
@@ -2908,20 +2994,28 @@ int stmmac_dvr_remove(struct net_device
|
|
priv->hw->dma->stop_tx(priv->ioaddr);
|
|
|
|
stmmac_set_mac(priv->ioaddr, false);
|
|
- if (priv->pcs != STMMAC_PCS_RGMII && priv->pcs != STMMAC_PCS_TBI &&
|
|
- priv->pcs != STMMAC_PCS_RTBI)
|
|
- stmmac_mdio_unregister(ndev);
|
|
netif_carrier_off(ndev);
|
|
unregister_netdev(ndev);
|
|
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)
|
|
+ stmmac_mdio_unregister(ndev);
|
|
free_netdev(ndev);
|
|
|
|
return 0;
|
|
}
|
|
+EXPORT_SYMBOL_GPL(stmmac_dvr_remove);
|
|
|
|
-#ifdef CONFIG_PM
|
|
+/**
|
|
+ * stmmac_suspend - suspend callback
|
|
+ * @ndev: net device pointer
|
|
+ * Description: this is the function to suspend the device and it is called
|
|
+ * by the platform driver to stop the network queue, release the resources,
|
|
+ * program the PMT register (for WoL), clean and release driver resources.
|
|
+ */
|
|
int stmmac_suspend(struct net_device *ndev)
|
|
{
|
|
struct stmmac_priv *priv = netdev_priv(ndev);
|
|
@@ -2954,6 +3048,7 @@ int stmmac_suspend(struct net_device *nd
|
|
stmmac_set_mac(priv->ioaddr, false);
|
|
pinctrl_pm_select_sleep_state(priv->device);
|
|
/* Disable clock in case of PWM is off */
|
|
+ clk_disable(priv->pclk);
|
|
clk_disable(priv->stmmac_clk);
|
|
}
|
|
spin_unlock_irqrestore(&priv->lock, flags);
|
|
@@ -2963,7 +3058,14 @@ int stmmac_suspend(struct net_device *nd
|
|
priv->oldduplex = -1;
|
|
return 0;
|
|
}
|
|
+EXPORT_SYMBOL_GPL(stmmac_suspend);
|
|
|
|
+/**
|
|
+ * stmmac_resume - resume callback
|
|
+ * @ndev: net device pointer
|
|
+ * Description: when resume this function is invoked to setup the DMA and CORE
|
|
+ * in a usable state.
|
|
+ */
|
|
int stmmac_resume(struct net_device *ndev)
|
|
{
|
|
struct stmmac_priv *priv = netdev_priv(ndev);
|
|
@@ -2987,6 +3089,7 @@ int stmmac_resume(struct net_device *nde
|
|
pinctrl_pm_select_default_state(priv->device);
|
|
/* enable the clk prevously disabled */
|
|
clk_enable(priv->stmmac_clk);
|
|
+ clk_enable(priv->pclk);
|
|
/* reset the phy so that it's ready */
|
|
if (priv->mii)
|
|
stmmac_mdio_reset(priv->mii);
|
|
@@ -2995,7 +3098,7 @@ int stmmac_resume(struct net_device *nde
|
|
netif_device_attach(ndev);
|
|
|
|
init_dma_desc_rings(ndev, GFP_ATOMIC);
|
|
- stmmac_hw_setup(ndev);
|
|
+ stmmac_hw_setup(ndev, false);
|
|
stmmac_init_tx_coalesce(priv);
|
|
|
|
napi_enable(&priv->napi);
|
|
@@ -3009,37 +3112,7 @@ int stmmac_resume(struct net_device *nde
|
|
|
|
return 0;
|
|
}
|
|
-#endif /* CONFIG_PM */
|
|
-
|
|
-/* Driver can be configured w/ and w/ both PCI and Platf drivers
|
|
- * depending on the configuration selected.
|
|
- */
|
|
-static int __init stmmac_init(void)
|
|
-{
|
|
- int ret;
|
|
-
|
|
- ret = stmmac_register_platform();
|
|
- if (ret)
|
|
- goto err;
|
|
- ret = stmmac_register_pci();
|
|
- if (ret)
|
|
- goto err_pci;
|
|
- return 0;
|
|
-err_pci:
|
|
- stmmac_unregister_platform();
|
|
-err:
|
|
- pr_err("stmmac: driver registration failed\n");
|
|
- return ret;
|
|
-}
|
|
-
|
|
-static void __exit stmmac_exit(void)
|
|
-{
|
|
- stmmac_unregister_platform();
|
|
- stmmac_unregister_pci();
|
|
-}
|
|
-
|
|
-module_init(stmmac_init);
|
|
-module_exit(stmmac_exit);
|
|
+EXPORT_SYMBOL_GPL(stmmac_resume);
|
|
|
|
#ifndef MODULE
|
|
static int __init stmmac_cmdline_opt(char *str)
|
|
@@ -3094,6 +3167,35 @@ err:
|
|
__setup("stmmaceth=", stmmac_cmdline_opt);
|
|
#endif /* MODULE */
|
|
|
|
+static int __init stmmac_init(void)
|
|
+{
|
|
+#ifdef CONFIG_DEBUG_FS
|
|
+ /* Create debugfs main directory if it doesn't exist yet */
|
|
+ if (!stmmac_fs_dir) {
|
|
+ stmmac_fs_dir = debugfs_create_dir(STMMAC_RESOURCE_NAME, NULL);
|
|
+
|
|
+ if (!stmmac_fs_dir || IS_ERR(stmmac_fs_dir)) {
|
|
+ pr_err("ERROR %s, debugfs create directory failed\n",
|
|
+ STMMAC_RESOURCE_NAME);
|
|
+
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void __exit stmmac_exit(void)
|
|
+{
|
|
+#ifdef CONFIG_DEBUG_FS
|
|
+ debugfs_remove_recursive(stmmac_fs_dir);
|
|
+#endif
|
|
+}
|
|
+
|
|
+module_init(stmmac_init)
|
|
+module_exit(stmmac_exit)
|
|
+
|
|
MODULE_DESCRIPTION("STMMAC 10/100/1000 Ethernet device driver");
|
|
MODULE_AUTHOR("Giuseppe Cavallaro <peppe.cavallaro@st.com>");
|
|
MODULE_LICENSE("GPL");
|
|
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c
|
|
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c
|
|
@@ -161,11 +161,16 @@ int stmmac_mdio_reset(struct mii_bus *bu
|
|
|
|
if (!gpio_request(reset_gpio, "mdio-reset")) {
|
|
gpio_direction_output(reset_gpio, active_low ? 1 : 0);
|
|
- udelay(data->delays[0]);
|
|
+ if (data->delays[0])
|
|
+ msleep(DIV_ROUND_UP(data->delays[0], 1000));
|
|
+
|
|
gpio_set_value(reset_gpio, active_low ? 0 : 1);
|
|
- udelay(data->delays[1]);
|
|
+ if (data->delays[1])
|
|
+ msleep(DIV_ROUND_UP(data->delays[1], 1000));
|
|
+
|
|
gpio_set_value(reset_gpio, active_low ? 1 : 0);
|
|
- udelay(data->delays[2]);
|
|
+ if (data->delays[2])
|
|
+ msleep(DIV_ROUND_UP(data->delays[2], 1000));
|
|
}
|
|
}
|
|
#endif
|
|
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c
|
|
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c
|
|
@@ -24,38 +24,128 @@
|
|
*******************************************************************************/
|
|
|
|
#include <linux/pci.h>
|
|
+#include <linux/dmi.h>
|
|
+
|
|
#include "stmmac.h"
|
|
|
|
-static struct plat_stmmacenet_data plat_dat;
|
|
-static struct stmmac_mdio_bus_data mdio_data;
|
|
-static struct stmmac_dma_cfg dma_cfg;
|
|
-
|
|
-static void stmmac_default_data(void)
|
|
-{
|
|
- memset(&plat_dat, 0, sizeof(struct plat_stmmacenet_data));
|
|
-
|
|
- plat_dat.bus_id = 1;
|
|
- plat_dat.phy_addr = 0;
|
|
- plat_dat.interface = PHY_INTERFACE_MODE_GMII;
|
|
- plat_dat.clk_csr = 2; /* clk_csr_i = 20-35MHz & MDC = clk_csr_i/16 */
|
|
- plat_dat.has_gmac = 1;
|
|
- plat_dat.force_sf_dma_mode = 1;
|
|
-
|
|
- mdio_data.phy_reset = NULL;
|
|
- mdio_data.phy_mask = 0;
|
|
- plat_dat.mdio_bus_data = &mdio_data;
|
|
-
|
|
- dma_cfg.pbl = 32;
|
|
- dma_cfg.burst_len = DMA_AXI_BLEN_256;
|
|
- plat_dat.dma_cfg = &dma_cfg;
|
|
+/*
|
|
+ * This struct is used to associate PCI Function of MAC controller on a board,
|
|
+ * discovered via DMI, with the address of PHY connected to the MAC. The
|
|
+ * negative value of the address means that MAC controller is not connected
|
|
+ * with PHY.
|
|
+ */
|
|
+struct stmmac_pci_dmi_data {
|
|
+ const char *name;
|
|
+ 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;
|
|
+};
|
|
+
|
|
+static int stmmac_pci_find_phy_addr(struct stmmac_pci_info *info)
|
|
+{
|
|
+ const char *name = dmi_get_system_info(DMI_BOARD_NAME);
|
|
+ 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;
|
|
+
|
|
+ for (dmi = info->dmi; dmi->name && *dmi->name; dmi++) {
|
|
+ if (!strcmp(dmi->name, name) && dmi->func == func)
|
|
+ return dmi->phy_addr;
|
|
+ }
|
|
+
|
|
+ return -ENODEV;
|
|
+}
|
|
+
|
|
+static void stmmac_default_data(struct plat_stmmacenet_data *plat)
|
|
+{
|
|
+ plat->bus_id = 1;
|
|
+ plat->phy_addr = 0;
|
|
+ plat->interface = PHY_INTERFACE_MODE_GMII;
|
|
+ plat->clk_csr = 2; /* clk_csr_i = 20-35MHz & MDC = clk_csr_i/16 */
|
|
+ plat->has_gmac = 1;
|
|
+ plat->force_sf_dma_mode = 1;
|
|
+
|
|
+ plat->mdio_bus_data->phy_reset = NULL;
|
|
+ plat->mdio_bus_data->phy_mask = 0;
|
|
+
|
|
+ plat->dma_cfg->pbl = 32;
|
|
+ plat->dma_cfg->burst_len = DMA_AXI_BLEN_256;
|
|
+
|
|
+ /* Set default value for multicast hash bins */
|
|
+ plat->multicast_filter_bins = HASH_TABLE_SIZE;
|
|
+
|
|
+ /* Set default value for unicast filter entries */
|
|
+ plat->unicast_filter_entries = 1;
|
|
+}
|
|
+
|
|
+static int quark_default_data(struct plat_stmmacenet_data *plat,
|
|
+ struct stmmac_pci_info *info)
|
|
+{
|
|
+ struct pci_dev *pdev = info->pdev;
|
|
+ int ret;
|
|
+
|
|
+ /*
|
|
+ * 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;
|
|
+
|
|
+ plat->bus_id = PCI_DEVID(pdev->bus->number, pdev->devfn);
|
|
+ plat->phy_addr = ret;
|
|
+ plat->interface = PHY_INTERFACE_MODE_RMII;
|
|
+ plat->clk_csr = 2;
|
|
+ plat->has_gmac = 1;
|
|
+ plat->force_sf_dma_mode = 1;
|
|
+
|
|
+ plat->mdio_bus_data->phy_reset = NULL;
|
|
+ plat->mdio_bus_data->phy_mask = 0;
|
|
+
|
|
+ plat->dma_cfg->pbl = 16;
|
|
+ plat->dma_cfg->burst_len = DMA_AXI_BLEN_256;
|
|
+ plat->dma_cfg->fixed_burst = 1;
|
|
|
|
/* Set default value for multicast hash bins */
|
|
- plat_dat.multicast_filter_bins = HASH_TABLE_SIZE;
|
|
+ plat->multicast_filter_bins = HASH_TABLE_SIZE;
|
|
|
|
/* Set default value for unicast filter entries */
|
|
- plat_dat.unicast_filter_entries = 1;
|
|
+ plat->unicast_filter_entries = 1;
|
|
+
|
|
+ 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,
|
|
+ },
|
|
+ {}
|
|
+};
|
|
+
|
|
+static struct stmmac_pci_info quark_pci_info = {
|
|
+ .setup = quark_default_data,
|
|
+ .dmi = quark_pci_dmi_data,
|
|
+};
|
|
+
|
|
/**
|
|
* stmmac_pci_probe
|
|
*
|
|
@@ -71,64 +161,65 @@ static void stmmac_default_data(void)
|
|
static int stmmac_pci_probe(struct pci_dev *pdev,
|
|
const struct pci_device_id *id)
|
|
{
|
|
- int ret = 0;
|
|
- void __iomem *addr = NULL;
|
|
- struct stmmac_priv *priv = NULL;
|
|
+ struct stmmac_pci_info *info = (struct stmmac_pci_info *)id->driver_data;
|
|
+ struct plat_stmmacenet_data *plat;
|
|
+ struct stmmac_resources res;
|
|
int i;
|
|
+ int ret;
|
|
+
|
|
+ plat = devm_kzalloc(&pdev->dev, sizeof(*plat), GFP_KERNEL);
|
|
+ if (!plat)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ plat->mdio_bus_data = devm_kzalloc(&pdev->dev,
|
|
+ sizeof(*plat->mdio_bus_data),
|
|
+ GFP_KERNEL);
|
|
+ if (!plat->mdio_bus_data)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ plat->dma_cfg = devm_kzalloc(&pdev->dev, sizeof(*plat->dma_cfg),
|
|
+ GFP_KERNEL);
|
|
+ if (!plat->dma_cfg)
|
|
+ return -ENOMEM;
|
|
|
|
/* Enable pci device */
|
|
- ret = pci_enable_device(pdev);
|
|
+ ret = pcim_enable_device(pdev);
|
|
if (ret) {
|
|
- pr_err("%s : ERROR: failed to enable %s device\n", __func__,
|
|
- pci_name(pdev));
|
|
+ dev_err(&pdev->dev, "%s: ERROR: failed to enable device\n",
|
|
+ __func__);
|
|
return ret;
|
|
}
|
|
- if (pci_request_regions(pdev, STMMAC_RESOURCE_NAME)) {
|
|
- pr_err("%s: ERROR: failed to get PCI region\n", __func__);
|
|
- ret = -ENODEV;
|
|
- goto err_out_req_reg_failed;
|
|
- }
|
|
|
|
/* Get the base address of device */
|
|
- for (i = 0; i <= 5; i++) {
|
|
+ for (i = 0; i <= PCI_STD_RESOURCE_END; i++) {
|
|
if (pci_resource_len(pdev, i) == 0)
|
|
continue;
|
|
- addr = pci_iomap(pdev, i, 0);
|
|
- if (addr == NULL) {
|
|
- pr_err("%s: ERROR: cannot map register memory aborting",
|
|
- __func__);
|
|
- ret = -EIO;
|
|
- goto err_out_map_failed;
|
|
- }
|
|
+ ret = pcim_iomap_regions(pdev, BIT(i), pci_name(pdev));
|
|
+ if (ret)
|
|
+ return ret;
|
|
break;
|
|
}
|
|
- pci_set_master(pdev);
|
|
-
|
|
- stmmac_default_data();
|
|
|
|
- priv = stmmac_dvr_probe(&(pdev->dev), &plat_dat, addr);
|
|
- if (IS_ERR(priv)) {
|
|
- pr_err("%s: main driver probe failed", __func__);
|
|
- ret = PTR_ERR(priv);
|
|
- goto err_out;
|
|
- }
|
|
- priv->dev->irq = pdev->irq;
|
|
- priv->wol_irq = pdev->irq;
|
|
-
|
|
- pci_set_drvdata(pdev, priv->dev);
|
|
+ pci_set_master(pdev);
|
|
|
|
- pr_debug("STMMAC platform driver registration completed");
|
|
+ if (info) {
|
|
+ info->pdev = pdev;
|
|
+ if (info->setup) {
|
|
+ ret = info->setup(plat, info);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ }
|
|
+ } else
|
|
+ stmmac_default_data(plat);
|
|
|
|
- return 0;
|
|
+ pci_enable_msi(pdev);
|
|
|
|
-err_out:
|
|
- pci_clear_master(pdev);
|
|
-err_out_map_failed:
|
|
- pci_release_regions(pdev);
|
|
-err_out_req_reg_failed:
|
|
- pci_disable_device(pdev);
|
|
+ memset(&res, 0, sizeof(res));
|
|
+ res.addr = pcim_iomap_table(pdev)[i];
|
|
+ res.wol_irq = pdev->irq;
|
|
+ res.irq = pdev->irq;
|
|
|
|
- return ret;
|
|
+ return stmmac_dvr_probe(&pdev->dev, plat, &res);
|
|
}
|
|
|
|
/**
|
|
@@ -141,61 +232,55 @@ err_out_req_reg_failed:
|
|
static void stmmac_pci_remove(struct pci_dev *pdev)
|
|
{
|
|
struct net_device *ndev = pci_get_drvdata(pdev);
|
|
- struct stmmac_priv *priv = netdev_priv(ndev);
|
|
|
|
stmmac_dvr_remove(ndev);
|
|
-
|
|
- pci_iounmap(pdev, priv->ioaddr);
|
|
- pci_release_regions(pdev);
|
|
- pci_disable_device(pdev);
|
|
}
|
|
|
|
-#ifdef CONFIG_PM
|
|
-static int stmmac_pci_suspend(struct pci_dev *pdev, pm_message_t state)
|
|
+#ifdef CONFIG_PM_SLEEP
|
|
+static int stmmac_pci_suspend(struct device *dev)
|
|
{
|
|
+ struct pci_dev *pdev = to_pci_dev(dev);
|
|
struct net_device *ndev = pci_get_drvdata(pdev);
|
|
- int ret;
|
|
-
|
|
- ret = stmmac_suspend(ndev);
|
|
- pci_save_state(pdev);
|
|
- pci_set_power_state(pdev, pci_choose_state(pdev, state));
|
|
|
|
- return ret;
|
|
+ return stmmac_suspend(ndev);
|
|
}
|
|
|
|
-static int stmmac_pci_resume(struct pci_dev *pdev)
|
|
+static int stmmac_pci_resume(struct device *dev)
|
|
{
|
|
+ struct pci_dev *pdev = to_pci_dev(dev);
|
|
struct net_device *ndev = pci_get_drvdata(pdev);
|
|
|
|
- pci_set_power_state(pdev, PCI_D0);
|
|
- pci_restore_state(pdev);
|
|
-
|
|
return stmmac_resume(ndev);
|
|
}
|
|
#endif
|
|
|
|
+static SIMPLE_DEV_PM_OPS(stmmac_pm_ops, stmmac_pci_suspend, stmmac_pci_resume);
|
|
+
|
|
#define STMMAC_VENDOR_ID 0x700
|
|
+#define STMMAC_QUARK_ID 0x0937
|
|
#define STMMAC_DEVICE_ID 0x1108
|
|
|
|
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},
|
|
{}
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(pci, stmmac_id_table);
|
|
|
|
-struct pci_driver stmmac_pci_driver = {
|
|
+static struct pci_driver stmmac_pci_driver = {
|
|
.name = STMMAC_RESOURCE_NAME,
|
|
.id_table = stmmac_id_table,
|
|
.probe = stmmac_pci_probe,
|
|
.remove = stmmac_pci_remove,
|
|
-#ifdef CONFIG_PM
|
|
- .suspend = stmmac_pci_suspend,
|
|
- .resume = stmmac_pci_resume,
|
|
-#endif
|
|
+ .driver = {
|
|
+ .pm = &stmmac_pm_ops,
|
|
+ },
|
|
};
|
|
|
|
+module_pci_driver(stmmac_pci_driver);
|
|
+
|
|
MODULE_DESCRIPTION("STMMAC 10/100/1000 Ethernet PCI driver");
|
|
MODULE_AUTHOR("Rayagond Kokatanur <rayagond.kokatanur@vayavyalabs.com>");
|
|
MODULE_AUTHOR("Giuseppe Cavallaro <peppe.cavallaro@st.com>");
|
|
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
|
|
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
|
|
@@ -23,41 +23,23 @@
|
|
*******************************************************************************/
|
|
|
|
#include <linux/platform_device.h>
|
|
+#include <linux/module.h>
|
|
#include <linux/io.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_net.h>
|
|
#include <linux/of_device.h>
|
|
-#include "stmmac.h"
|
|
+#include <linux/of_mdio.h>
|
|
|
|
-static const struct of_device_id stmmac_dt_ids[] = {
|
|
-#ifdef CONFIG_DWMAC_MESON
|
|
- { .compatible = "amlogic,meson6-dwmac", .data = &meson6_dwmac_data},
|
|
-#endif
|
|
-#ifdef CONFIG_DWMAC_SUNXI
|
|
- { .compatible = "allwinner,sun7i-a20-gmac", .data = &sun7i_gmac_data},
|
|
-#endif
|
|
-#ifdef CONFIG_DWMAC_STI
|
|
- { .compatible = "st,stih415-dwmac", .data = &stih4xx_dwmac_data},
|
|
- { .compatible = "st,stih416-dwmac", .data = &stih4xx_dwmac_data},
|
|
- { .compatible = "st,stid127-dwmac", .data = &stid127_dwmac_data},
|
|
- { .compatible = "st,stih407-dwmac", .data = &stih4xx_dwmac_data},
|
|
-#endif
|
|
-#ifdef CONFIG_DWMAC_SOCFPGA
|
|
- { .compatible = "altr,socfpga-stmmac", .data = &socfpga_gmac_data },
|
|
-#endif
|
|
- /* SoC specific glue layers should come before generic bindings */
|
|
- { .compatible = "st,spear600-gmac"},
|
|
- { .compatible = "snps,dwmac-3.610"},
|
|
- { .compatible = "snps,dwmac-3.70a"},
|
|
- { .compatible = "snps,dwmac-3.710"},
|
|
- { .compatible = "snps,dwmac"},
|
|
- { /* sentinel */ }
|
|
-};
|
|
-MODULE_DEVICE_TABLE(of, stmmac_dt_ids);
|
|
+#include "stmmac.h"
|
|
+#include "stmmac_platform.h"
|
|
|
|
#ifdef CONFIG_OF
|
|
|
|
-/* This function validates the number of Multicast filtering bins specified
|
|
+/**
|
|
+ * dwmac1000_validate_mcast_bins - validates the number of Multicast filter bins
|
|
+ * @mcast_bins: Multicast filtering bins
|
|
+ * Description:
|
|
+ * this function validates the number of Multicast filtering bins specified
|
|
* by the configuration through the device tree. The Synopsys GMAC supports
|
|
* 64 bins, 128 bins, or 256 bins. "bins" refer to the division of CRC
|
|
* number space. 64 bins correspond to 6 bits of the CRC, 128 corresponds
|
|
@@ -83,7 +65,11 @@ static int dwmac1000_validate_mcast_bins
|
|
return x;
|
|
}
|
|
|
|
-/* This function validates the number of Unicast address entries supported
|
|
+/**
|
|
+ * dwmac1000_validate_ucast_entries - validate the Unicast address entries
|
|
+ * @ucast_entries: number of Unicast address entries
|
|
+ * Description:
|
|
+ * This function validates the number of Unicast address entries supported
|
|
* by a particular Synopsys 10/100/1000 controller. The Synopsys controller
|
|
* supports 1, 32, 64, or 128 Unicast filter entries for it's Unicast filter
|
|
* logic. This function validates a valid, supported configuration is
|
|
@@ -109,37 +95,25 @@ static int dwmac1000_validate_ucast_entr
|
|
return x;
|
|
}
|
|
|
|
-static int stmmac_probe_config_dt(struct platform_device *pdev,
|
|
- struct plat_stmmacenet_data *plat,
|
|
- const char **mac)
|
|
+/**
|
|
+ * stmmac_probe_config_dt - parse device-tree driver parameters
|
|
+ * @pdev: platform_device structure
|
|
+ * @plat: driver data platform structure
|
|
+ * @mac: MAC address to use
|
|
+ * Description:
|
|
+ * this function is to read the driver parameters from device-tree and
|
|
+ * set some private fields that will be used by the main at runtime.
|
|
+ */
|
|
+struct plat_stmmacenet_data *
|
|
+stmmac_probe_config_dt(struct platform_device *pdev, const char **mac)
|
|
{
|
|
struct device_node *np = pdev->dev.of_node;
|
|
+ struct plat_stmmacenet_data *plat;
|
|
struct stmmac_dma_cfg *dma_cfg;
|
|
- const struct of_device_id *device;
|
|
|
|
- if (!np)
|
|
- return -ENODEV;
|
|
-
|
|
- device = of_match_device(stmmac_dt_ids, &pdev->dev);
|
|
- if (!device)
|
|
- return -ENODEV;
|
|
-
|
|
- if (device->data) {
|
|
- const struct stmmac_of_data *data = device->data;
|
|
- plat->has_gmac = data->has_gmac;
|
|
- plat->enh_desc = data->enh_desc;
|
|
- plat->tx_coe = data->tx_coe;
|
|
- plat->rx_coe = data->rx_coe;
|
|
- plat->bugged_jumbo = data->bugged_jumbo;
|
|
- plat->pmt = data->pmt;
|
|
- plat->riwt_off = data->riwt_off;
|
|
- plat->fix_mac_speed = data->fix_mac_speed;
|
|
- plat->bus_setup = data->bus_setup;
|
|
- plat->setup = data->setup;
|
|
- plat->free = data->free;
|
|
- plat->init = data->init;
|
|
- plat->exit = data->exit;
|
|
- }
|
|
+ plat = devm_kzalloc(&pdev->dev, sizeof(*plat), GFP_KERNEL);
|
|
+ if (!plat)
|
|
+ return ERR_PTR(-ENOMEM);
|
|
|
|
*mac = of_get_mac_address(np);
|
|
plat->interface = of_get_phy_mode(np);
|
|
@@ -155,13 +129,24 @@ static int stmmac_probe_config_dt(struct
|
|
/* Default to phy auto-detection */
|
|
plat->phy_addr = -1;
|
|
|
|
+ /* If we find a phy-handle property, use it as the PHY */
|
|
+ plat->phy_node = of_parse_phandle(np, "phy-handle", 0);
|
|
+
|
|
+ /* If phy-handle is not specified, check if we have a fixed-phy */
|
|
+ if (!plat->phy_node && of_phy_is_fixed_link(np)) {
|
|
+ if ((of_phy_register_fixed_link(np) < 0))
|
|
+ return ERR_PTR(-ENODEV);
|
|
+
|
|
+ plat->phy_node = of_node_get(np);
|
|
+ }
|
|
+
|
|
/* "snps,phy-addr" is not a standard property. Mark it as deprecated
|
|
* and warn of its use. Remove this when phy node support is added.
|
|
*/
|
|
if (of_property_read_u32(np, "snps,phy-addr", &plat->phy_addr) == 0)
|
|
dev_warn(&pdev->dev, "snps,phy-addr property is deprecated\n");
|
|
|
|
- if (plat->phy_bus_name)
|
|
+ if (plat->phy_node || plat->phy_bus_name)
|
|
plat->mdio_bus_data = NULL;
|
|
else
|
|
plat->mdio_bus_data =
|
|
@@ -169,6 +154,10 @@ static int stmmac_probe_config_dt(struct
|
|
sizeof(struct stmmac_mdio_bus_data),
|
|
GFP_KERNEL);
|
|
|
|
+ of_property_read_u32(np, "tx-fifo-depth", &plat->tx_fifo_size);
|
|
+
|
|
+ of_property_read_u32(np, "rx-fifo-depth", &plat->rx_fifo_size);
|
|
+
|
|
plat->force_sf_dma_mode =
|
|
of_property_read_bool(np, "snps,force_sf_dma_mode");
|
|
|
|
@@ -177,6 +166,12 @@ static int stmmac_probe_config_dt(struct
|
|
*/
|
|
plat->maxmtu = JUMBO_LEN;
|
|
|
|
+ /* Set default value for multicast hash bins */
|
|
+ plat->multicast_filter_bins = HASH_TABLE_SIZE;
|
|
+
|
|
+ /* Set default value for unicast filter entries */
|
|
+ plat->unicast_filter_entries = 1;
|
|
+
|
|
/*
|
|
* Currently only the properties needed on SPEAr600
|
|
* are provided. All other properties should be added
|
|
@@ -215,14 +210,19 @@ static int stmmac_probe_config_dt(struct
|
|
if (of_find_property(np, "snps,pbl", NULL)) {
|
|
dma_cfg = devm_kzalloc(&pdev->dev, sizeof(*dma_cfg),
|
|
GFP_KERNEL);
|
|
- if (!dma_cfg)
|
|
- return -ENOMEM;
|
|
+ if (!dma_cfg) {
|
|
+ of_node_put(np);
|
|
+ return ERR_PTR(-ENOMEM);
|
|
+ }
|
|
plat->dma_cfg = dma_cfg;
|
|
of_property_read_u32(np, "snps,pbl", &dma_cfg->pbl);
|
|
dma_cfg->fixed_burst =
|
|
of_property_read_bool(np, "snps,fixed-burst");
|
|
dma_cfg->mixed_burst =
|
|
of_property_read_bool(np, "snps,mixed-burst");
|
|
+ of_property_read_u32(np, "snps,burst_len", &dma_cfg->burst_len);
|
|
+ if (dma_cfg->burst_len < 0 || dma_cfg->burst_len > 256)
|
|
+ dma_cfg->burst_len = 0;
|
|
}
|
|
plat->force_thresh_dma_mode = of_property_read_bool(np, "snps,force_thresh_dma_mode");
|
|
if (plat->force_thresh_dma_mode) {
|
|
@@ -230,123 +230,60 @@ static int stmmac_probe_config_dt(struct
|
|
pr_warn("force_sf_dma_mode is ignored if force_thresh_dma_mode is set.");
|
|
}
|
|
|
|
- return 0;
|
|
+ return plat;
|
|
}
|
|
#else
|
|
-static int stmmac_probe_config_dt(struct platform_device *pdev,
|
|
- struct plat_stmmacenet_data *plat,
|
|
- const char **mac)
|
|
+struct plat_stmmacenet_data *
|
|
+stmmac_probe_config_dt(struct platform_device *pdev, const char **mac)
|
|
{
|
|
- return -ENOSYS;
|
|
+ return ERR_PTR(-ENOSYS);
|
|
}
|
|
#endif /* CONFIG_OF */
|
|
+EXPORT_SYMBOL_GPL(stmmac_probe_config_dt);
|
|
|
|
-/**
|
|
- * stmmac_pltfr_probe
|
|
- * @pdev: platform device pointer
|
|
- * Description: platform_device probe function. It allocates
|
|
- * the necessary resources and invokes the main to init
|
|
- * the net device, register the mdio bus etc.
|
|
- */
|
|
-static int stmmac_pltfr_probe(struct platform_device *pdev)
|
|
+int stmmac_get_platform_resources(struct platform_device *pdev,
|
|
+ struct stmmac_resources *stmmac_res)
|
|
{
|
|
- int ret = 0;
|
|
struct resource *res;
|
|
- struct device *dev = &pdev->dev;
|
|
- void __iomem *addr = NULL;
|
|
- struct stmmac_priv *priv = NULL;
|
|
- struct plat_stmmacenet_data *plat_dat = NULL;
|
|
- const char *mac = NULL;
|
|
-
|
|
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
- addr = devm_ioremap_resource(dev, res);
|
|
- if (IS_ERR(addr))
|
|
- return PTR_ERR(addr);
|
|
-
|
|
- plat_dat = dev_get_platdata(&pdev->dev);
|
|
-
|
|
- if (!plat_dat)
|
|
- plat_dat = devm_kzalloc(&pdev->dev,
|
|
- sizeof(struct plat_stmmacenet_data),
|
|
- GFP_KERNEL);
|
|
- if (!plat_dat) {
|
|
- pr_err("%s: ERROR: no memory", __func__);
|
|
- return -ENOMEM;
|
|
- }
|
|
-
|
|
- /* Set default value for multicast hash bins */
|
|
- plat_dat->multicast_filter_bins = HASH_TABLE_SIZE;
|
|
-
|
|
- /* Set default value for unicast filter entries */
|
|
- plat_dat->unicast_filter_entries = 1;
|
|
-
|
|
- if (pdev->dev.of_node) {
|
|
- ret = stmmac_probe_config_dt(pdev, plat_dat, &mac);
|
|
- if (ret) {
|
|
- pr_err("%s: main dt probe failed", __func__);
|
|
- return ret;
|
|
- }
|
|
- }
|
|
-
|
|
- /* Custom setup (if needed) */
|
|
- if (plat_dat->setup) {
|
|
- plat_dat->bsp_priv = plat_dat->setup(pdev);
|
|
- if (IS_ERR(plat_dat->bsp_priv))
|
|
- return PTR_ERR(plat_dat->bsp_priv);
|
|
- }
|
|
-
|
|
- /* Custom initialisation (if needed)*/
|
|
- if (plat_dat->init) {
|
|
- ret = plat_dat->init(pdev, plat_dat->bsp_priv);
|
|
- if (unlikely(ret))
|
|
- return ret;
|
|
- }
|
|
|
|
- priv = stmmac_dvr_probe(&(pdev->dev), plat_dat, addr);
|
|
- if (IS_ERR(priv)) {
|
|
- pr_err("%s: main driver probe failed", __func__);
|
|
- return PTR_ERR(priv);
|
|
- }
|
|
+ memset(stmmac_res, 0, sizeof(*stmmac_res));
|
|
|
|
- /* Get MAC address if available (DT) */
|
|
- if (mac)
|
|
- memcpy(priv->dev->dev_addr, mac, ETH_ALEN);
|
|
-
|
|
- /* Get the MAC information */
|
|
- priv->dev->irq = platform_get_irq_byname(pdev, "macirq");
|
|
- if (priv->dev->irq < 0) {
|
|
- if (priv->dev->irq != -EPROBE_DEFER) {
|
|
- netdev_err(priv->dev,
|
|
- "MAC IRQ configuration information not found\n");
|
|
+ /* Get IRQ information early to have an ability to ask for deferred
|
|
+ * probe if needed before we went too far with resource allocation.
|
|
+ */
|
|
+ stmmac_res->irq = platform_get_irq_byname(pdev, "macirq");
|
|
+ if (stmmac_res->irq < 0) {
|
|
+ if (stmmac_res->irq != -EPROBE_DEFER) {
|
|
+ dev_err(&pdev->dev,
|
|
+ "MAC IRQ configuration information not found\n");
|
|
}
|
|
- return priv->dev->irq;
|
|
+ return stmmac_res->irq;
|
|
}
|
|
|
|
- /*
|
|
- * On some platforms e.g. SPEAr the wake up irq differs from the mac irq
|
|
+ /* On some platforms e.g. SPEAr the wake up irq differs from the mac irq
|
|
* The external wake up irq can be passed through the platform code
|
|
* named as "eth_wake_irq"
|
|
*
|
|
* In case the wake up interrupt is not passed from the platform
|
|
* so the driver will continue to use the mac irq (ndev->irq)
|
|
*/
|
|
- priv->wol_irq = platform_get_irq_byname(pdev, "eth_wake_irq");
|
|
- if (priv->wol_irq < 0) {
|
|
- if (priv->wol_irq == -EPROBE_DEFER)
|
|
+ stmmac_res->wol_irq = platform_get_irq_byname(pdev, "eth_wake_irq");
|
|
+ if (stmmac_res->wol_irq < 0) {
|
|
+ if (stmmac_res->wol_irq == -EPROBE_DEFER)
|
|
return -EPROBE_DEFER;
|
|
- priv->wol_irq = priv->dev->irq;
|
|
+ stmmac_res->wol_irq = stmmac_res->irq;
|
|
}
|
|
|
|
- priv->lpi_irq = platform_get_irq_byname(pdev, "eth_lpi");
|
|
- if (priv->lpi_irq == -EPROBE_DEFER)
|
|
+ stmmac_res->lpi_irq = platform_get_irq_byname(pdev, "eth_lpi");
|
|
+ if (stmmac_res->lpi_irq == -EPROBE_DEFER)
|
|
return -EPROBE_DEFER;
|
|
|
|
- platform_set_drvdata(pdev, priv->dev);
|
|
-
|
|
- pr_debug("STMMAC platform driver registration completed");
|
|
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
+ stmmac_res->addr = devm_ioremap_resource(&pdev->dev, res);
|
|
|
|
- return 0;
|
|
+ return PTR_ERR_OR_ZERO(stmmac_res->addr);
|
|
}
|
|
+EXPORT_SYMBOL_GPL(stmmac_get_platform_resources);
|
|
|
|
/**
|
|
* stmmac_pltfr_remove
|
|
@@ -354,7 +291,7 @@ static int stmmac_pltfr_probe(struct pla
|
|
* Description: this function calls the main to free the net resources
|
|
* and calls the platforms hook and release the resources (e.g. mem).
|
|
*/
|
|
-static int stmmac_pltfr_remove(struct platform_device *pdev)
|
|
+int stmmac_pltfr_remove(struct platform_device *pdev)
|
|
{
|
|
struct net_device *ndev = platform_get_drvdata(pdev);
|
|
struct stmmac_priv *priv = netdev_priv(ndev);
|
|
@@ -363,13 +300,18 @@ static int stmmac_pltfr_remove(struct pl
|
|
if (priv->plat->exit)
|
|
priv->plat->exit(pdev, priv->plat->bsp_priv);
|
|
|
|
- if (priv->plat->free)
|
|
- priv->plat->free(pdev, priv->plat->bsp_priv);
|
|
-
|
|
return ret;
|
|
}
|
|
+EXPORT_SYMBOL_GPL(stmmac_pltfr_remove);
|
|
|
|
-#ifdef CONFIG_PM
|
|
+#ifdef CONFIG_PM_SLEEP
|
|
+/**
|
|
+ * stmmac_pltfr_suspend
|
|
+ * @dev: device pointer
|
|
+ * Description: this function is invoked when suspend the driver and it direcly
|
|
+ * call the main suspend function and then, if required, on some platform, it
|
|
+ * can call an exit helper.
|
|
+ */
|
|
static int stmmac_pltfr_suspend(struct device *dev)
|
|
{
|
|
int ret;
|
|
@@ -384,6 +326,13 @@ static int stmmac_pltfr_suspend(struct d
|
|
return ret;
|
|
}
|
|
|
|
+/**
|
|
+ * stmmac_pltfr_resume
|
|
+ * @dev: device pointer
|
|
+ * Description: this function is invoked when resume the driver before calling
|
|
+ * the main resume function, on some platforms, it can call own init helper
|
|
+ * if required.
|
|
+ */
|
|
static int stmmac_pltfr_resume(struct device *dev)
|
|
{
|
|
struct net_device *ndev = dev_get_drvdata(dev);
|
|
@@ -395,23 +344,12 @@ static int stmmac_pltfr_resume(struct de
|
|
|
|
return stmmac_resume(ndev);
|
|
}
|
|
+#endif /* CONFIG_PM_SLEEP */
|
|
|
|
-#endif /* CONFIG_PM */
|
|
-
|
|
-static SIMPLE_DEV_PM_OPS(stmmac_pltfr_pm_ops,
|
|
- stmmac_pltfr_suspend, stmmac_pltfr_resume);
|
|
-
|
|
-struct platform_driver stmmac_pltfr_driver = {
|
|
- .probe = stmmac_pltfr_probe,
|
|
- .remove = stmmac_pltfr_remove,
|
|
- .driver = {
|
|
- .name = STMMAC_RESOURCE_NAME,
|
|
- .owner = THIS_MODULE,
|
|
- .pm = &stmmac_pltfr_pm_ops,
|
|
- .of_match_table = of_match_ptr(stmmac_dt_ids),
|
|
- },
|
|
-};
|
|
+SIMPLE_DEV_PM_OPS(stmmac_pltfr_pm_ops, stmmac_pltfr_suspend,
|
|
+ stmmac_pltfr_resume);
|
|
+EXPORT_SYMBOL_GPL(stmmac_pltfr_pm_ops);
|
|
|
|
-MODULE_DESCRIPTION("STMMAC 10/100/1000 Ethernet PLATFORM driver");
|
|
+MODULE_DESCRIPTION("STMMAC 10/100/1000 Ethernet platform support");
|
|
MODULE_AUTHOR("Giuseppe Cavallaro <peppe.cavallaro@st.com>");
|
|
MODULE_LICENSE("GPL");
|
|
--- /dev/null
|
|
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.h
|
|
@@ -0,0 +1,33 @@
|
|
+/*******************************************************************************
|
|
+ Copyright (C) 2007-2009 STMicroelectronics Ltd
|
|
+
|
|
+ This program is free software; you can redistribute it and/or modify it
|
|
+ under the terms and conditions of the GNU General Public License,
|
|
+ version 2, as published by the Free Software Foundation.
|
|
+
|
|
+ This program is distributed in the hope it will be useful, but WITHOUT
|
|
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
+ more details.
|
|
+
|
|
+ The full GNU General Public License is included in this distribution in
|
|
+ the file called "COPYING".
|
|
+
|
|
+ Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
|
|
+*******************************************************************************/
|
|
+
|
|
+#ifndef __STMMAC_PLATFORM_H__
|
|
+#define __STMMAC_PLATFORM_H__
|
|
+
|
|
+#include "stmmac.h"
|
|
+
|
|
+struct plat_stmmacenet_data *
|
|
+stmmac_probe_config_dt(struct platform_device *pdev, const char **mac);
|
|
+
|
|
+int stmmac_get_platform_resources(struct platform_device *pdev,
|
|
+ struct stmmac_resources *stmmac_res);
|
|
+
|
|
+int stmmac_pltfr_remove(struct platform_device *pdev);
|
|
+extern const struct dev_pm_ops stmmac_pltfr_pm_ops;
|
|
+
|
|
+#endif /* __STMMAC_PLATFORM_H__ */
|