VisionFive2 Linux kernel

StarFive Tech Linux Kernel for VisionFive (JH7110) boards (mirror)

More than 9999 Commits   33 Branches   55 Tags
author: leyfoon.tan <leyfoon.tan@starfivetech.com> 2022-11-07 07:11:22 +0000 committer: leyfoon.tan <leyfoon.tan@starfivetech.com> 2022-11-07 07:11:22 +0000 commit: 0f9fd42752277456a1f324b773877ac28aa9290b parent: fce77eb3dae41d6bc32dee28e5a184674b1e425e
Commit Summary:
Merge branch 'leyfoon/starfive-5.15-dubhe-gmac-dma-v3' into 'starfive-5.15-dubhe'
Diffstat:
16 files changed, 521 insertions, 48 deletions
diff --git a/Documentation/devicetree/bindings/net/starfive,dwmac-plat.yaml b/Documentation/devicetree/bindings/net/starfive,dwmac-plat.yaml
new file mode 100644
index 000000000000..19c2cf72501a
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/starfive,dwmac-plat.yaml
@@ -0,0 +1,104 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/starfive,dwmac-plat.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: StarFive DWMAC glue layer Device Tree Bindings
+
+maintainers:
+  - Tan Chun Hau <chunhau.tan@starfivetech.com>
+
+select:
+  properties:
+    compatible:
+      contains:
+        enum:
+          - starfive,dwmac
+  required:
+    - compatible
+
+allOf:
+  - $ref: "snps,dwmac.yaml#"
+
+properties:
+  compatible:
+    oneOf:
+      - items:
+          - enum:
+              - starfive,dwmac
+          - const: snps,dwmac-5.10a
+
+  clocks:
+    items:
+      - description: Gtx clock
+      - description: Tx clock
+      - description: PTP reference clock
+      - description: GMAC main clock
+      - description: PTP reference clock
+      - description: Gtxc clock
+
+  clock-names:
+    items:
+      - const: gtx
+      - const: tx
+      - const: ptp_ref
+      - const: stmmaceth
+      - const: pclk
+      - const: gtxc
+
+required:
+  - compatible
+  - clocks
+  - clock-names
+
+unevaluatedProperties: false
+
+Example:
+  - |
+    #include <dt-bindings/reset/starfive-jh7110.h>
+    #include <dt-bindings/clock/starfive-jh7110-clkgen.h>
+
+    /* gmac device configuration */
+    stmmac_axi_setup: stmmac-axi-config {
+      snps,wr_osr_lmt = <0xf>;
+      snps,rd_osr_lmt = <0xf>;
+      snps,blen = <256 128 64 32 0 0 0>;
+    };
+
+    gmac0: gmac@17020000 {
+      compatible = "starfive,dwmac","snps,dwmac-5.10a";
+      reg = <0x0 0x17020000 0x0 0x10000>;
+      interrupts = <7>, <6>, <5>;
+      interrupt-names = "macirq", "eth_wake_irq", "eth_lpi";
+      phy-reset-gpios = <&gpio 63 0>;
+      clock-names = "gtx",
+        "tx",
+        "ptp_ref",
+        "stmmaceth",
+        "pclk",
+        "gtxc";
+      clocks = <&clkgen JH7110_GMAC0_GTXCLK>,
+         <&clkgen JH7110_U0_GMAC5_CLK_TX>,
+         <&clkgen JH7110_GMAC0_PTP>,
+         <&clkgen JH7110_U0_GMAC5_CLK_AHB>,
+         <&clkgen JH7110_U0_GMAC5_CLK_AXI>,
+         <&clkgen JH7110_GMAC0_GTXC>;
+      resets = <&rstgen RSTN_U0_DW_GMAC5_AXI64_AHB>,
+         <&rstgen RSTN_U0_DW_GMAC5_AXI64_AXI>;
+      reset-names = "ahb", "stmmaceth";
+      max-frame-size = <1500>;
+      phy-mode = "rgmii-id";
+      snps,multicast-filter-bins = <64>;
+      snps,perfect-filter-entries = <128>;
+      rx-fifo-depth = <2048>;
+      tx-fifo-depth = <2048>;
+      snps,fixed-burst;
+      snps,no-pbl-x8;
+      snps,force_thresh_dma_mode;
+      snps,axi-config = <&stmmac_axi_setup>;
+      snps,tso;
+      snps,en-tx-lpi-clockgating;
+      snps,txpbl = <4>;
+      snps,rxpbl = <4>;
+    };
diff --git a/arch/riscv/Kconfig.socs b/arch/riscv/Kconfig.socs
index 4ea8d3c28658..ba81328edb01 100644
--- a/arch/riscv/Kconfig.socs
+++ b/arch/riscv/Kconfig.socs
@@ -32,6 +32,7 @@ config SOC_STARFIVE
 
 config SOC_STARFIVE_DUBHE
 	bool "StarFive Dubhe SoC"
+	select RISCV_DMA_NONCOHERENT
 	select SERIAL_SIFIVE if TTY
 	select SERIAL_SIFIVE_CONSOLE if TTY
 	select SIFIVE_PLIC
diff --git a/arch/riscv/boot/dts/starfive/dubhe.dtsi b/arch/riscv/boot/dts/starfive/dubhe.dtsi
index 73e0090fb7b1..a0506d46134a 100644
--- a/arch/riscv/boot/dts/starfive/dubhe.dtsi
+++ b/arch/riscv/boot/dts/starfive/dubhe.dtsi
@@ -124,6 +124,40 @@
 			status = "disabled";
 		};
 
+		stmmac_axi_setup: stmmac-axi-config {
+			snps,wr_osr_lmt = <0xf>;
+			snps,rd_osr_lmt = <0xf>;
+			snps,blen = <256 128 64 32 0 0 0>;
+		};
+
+		gmac0: gmac@10100000 {
+			compatible = "starfive,dwmac","snps,dwmac-5.10a";
+			reg = <0x0 0x10100000 0x0 0x10000>;
+			clock-names = "gtx",
+				"tx",
+				"ptp_ref",
+				"stmmaceth",
+				"pclk",
+				"gtxc";
+			interrupt-parent = <&plic0>;
+			interrupts = <8>, <11>, <12>;
+			interrupt-names = "macirq", "eth_wake_irq", "eth_lpi";
+			max-frame-size = <1500>;
+			snps,multicast-filter-bins = <64>;
+			snps,perfect-filter-entries = <8>;
+			rx-fifo-depth = <2048>;
+			tx-fifo-depth = <2048>;
+			snps,fixed-burst;
+			snps,no-pbl-x8;
+			snps,force_thresh_dma_mode;
+			snps,axi-config = <&stmmac_axi_setup>;
+			snps,tso;
+			snps,en-tx-lpi-clockgating;
+			snps,txpbl = <4>;
+			snps,rxpbl = <4>;
+			status = "disabled";
+		};
+
 		pmu {
 			compatible = "riscv,pmu";
 			riscv,event-to-mhpmevent = <0x00005 0x0000 0x1B>,
diff --git a/arch/riscv/boot/dts/starfive/dubhe_fpga.dts b/arch/riscv/boot/dts/starfive/dubhe_fpga.dts
index dce02d6ccd96..4563628bd389 100644
--- a/arch/riscv/boot/dts/starfive/dubhe_fpga.dts
+++ b/arch/riscv/boot/dts/starfive/dubhe_fpga.dts
@@ -7,11 +7,12 @@
 	model = "StarFive Dubhe FPGA";
 
 	aliases {
+		ethernet0 = &gmac0;
 		serial0 = &uart0;
 	};
 
 	chosen {
-		bootargs = "console=ttySIF0,115200 earlycon=sbi";
+		bootargs = "console=ttySIF0,115200 earlycon=sbi root=/dev/nfs nfsroot=192.168.153.221:/home/nfsroot,rw,tcp,vers=3 ip=192.168.152.100:192.168.153.221:192.168.152.1:255.255.255.0::eth0:off";
 	};
 
 	cpus {
@@ -25,6 +26,40 @@
 	};
 
 	soc {
+
+		fpga_2p5mhz_clk: fpga_2p5mhz_clk {
+			compatible = "fixed-clock";
+			#clock-cells = <0>;
+			clock-frequency = <2500000>;
+		};
+
+		fpga_50mhz_clk: fpga_50mhz_clk {
+			compatible = "fixed-clock";
+			#clock-cells = <0>;
+			clock-frequency = <50000000>;
+		};
+	};
+};
+
+&gmac0 {
+	status = "okay";
+	phy-mode = "rgmii-id";
+	phy-handle = <&ethernet_phy0>;
+	clocks = <&fpga_2p5mhz_clk>,
+		 <&fpga_2p5mhz_clk>,
+		 <&fpga_2p5mhz_clk>,
+		 <&fpga_50mhz_clk>,
+		 <&fpga_50mhz_clk>,
+		 <&fpga_2p5mhz_clk>;
+
+	mdio0 {
+		compatible = "snps,dwmac-mdio";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		ethernet_phy0: ethernet-phy@0 {
+			reg = <0>;
+			max-speed = <10>;
+		};
 	};
 };
 
diff --git a/arch/riscv/configs/starfive_dubhe_defconfig b/arch/riscv/configs/starfive_dubhe_defconfig
index 524ca5ceb076..4b584aa243a6 100644
--- a/arch/riscv/configs/starfive_dubhe_defconfig
+++ b/arch/riscv/configs/starfive_dubhe_defconfig
@@ -13,11 +13,10 @@ CONFIG_CGROUP_BPF=y
 CONFIG_NAMESPACES=y
 CONFIG_USER_NS=y
 CONFIG_CHECKPOINT_RESTORE=y
-CONFIG_BLK_DEV_INITRD=y
-CONFIG_INITRAMFS_SOURCE="rootfs.cpio"
 CONFIG_EXPERT=y
 CONFIG_KALLSYMS_ALL=y
 CONFIG_PERF_EVENTS=y
+CONFIG_RISCV_UNCACHED_OFFSET=0x400000000
 CONFIG_SOC_STARFIVE_DUBHE=y
 CONFIG_SMP=y
 CONFIG_HOTPLUG_CPU=y
@@ -37,6 +36,33 @@ CONFIG_IP_PNP=y
 CONFIG_IP_PNP_DHCP=y
 CONFIG_IP_PNP_BOOTP=y
 CONFIG_IP_PNP_RARP=y
+CONFIG_NETFILTER=y
+CONFIG_NETFILTER_NETLINK_ACCT=y
+CONFIG_NETFILTER_NETLINK_QUEUE=y
+CONFIG_NF_CONNTRACK=y
+CONFIG_NF_TABLES=y
+CONFIG_NFT_CT=y
+CONFIG_NFT_COMPAT=y
+CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y
+CONFIG_NETFILTER_XT_MATCH_IPCOMP=y
+CONFIG_NETFILTER_XT_MATCH_IPRANGE=y
+CONFIG_NETFILTER_XT_MATCH_MAC=y
+CONFIG_NETFILTER_XT_MATCH_MARK=y
+CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y
+CONFIG_NETFILTER_XT_MATCH_SOCKET=y
+CONFIG_NETFILTER_XT_MATCH_STATE=y
+CONFIG_NETFILTER_XT_MATCH_STRING=y
+CONFIG_NETFILTER_XT_MATCH_U32=y
+CONFIG_NF_TABLES_IPV4=y
+CONFIG_NFT_DUP_IPV4=y
+CONFIG_NFT_FIB_IPV4=y
+CONFIG_IP_NF_IPTABLES=y
+CONFIG_IP_NF_FILTER=y
+CONFIG_IP_NF_TARGET_REJECT=y
+CONFIG_IP_NF_NAT=y
+CONFIG_IP_NF_TARGET_MASQUERADE=y
+CONFIG_IP_NF_TARGET_NETMAP=y
+CONFIG_IP_NF_TARGET_REDIRECT=y
 CONFIG_NETLINK_DIAG=y
 CONFIG_NET_9P=y
 CONFIG_NET_9P_VIRTIO=y
@@ -51,6 +77,10 @@ CONFIG_SCSI_VIRTIO=y
 CONFIG_NETDEVICES=y
 CONFIG_VIRTIO_NET=y
 CONFIG_MACB=y
+CONFIG_STMMAC_ETH=y
+CONFIG_STMMAC_SELFTESTS=y
+CONFIG_DWMAC_STARFIVE_PLAT=y
+CONFIG_MARVELL_PHY=y
 CONFIG_MICROSEMI_PHY=y
 CONFIG_REALTEK_PHY=y
 CONFIG_INPUT_MOUSEDEV=y
@@ -82,7 +112,6 @@ CONFIG_VIRTIO_MMIO=y
 CONFIG_GOLDFISH=y
 CONFIG_RPMSG_CHAR=y
 CONFIG_RPMSG_VIRTIO=y
-CONFIG_RESET_CONTROLLER=y
 CONFIG_AUTOFS4_FS=y
 CONFIG_MSDOS_FS=y
 CONFIG_VFAT_FS=y
@@ -109,8 +138,6 @@ CONFIG_NLS_CODEPAGE_1251=y
 CONFIG_NLS_ASCII=y
 CONFIG_NLS_ISO8859_1=y
 CONFIG_NLS_UTF8=y
-CONFIG_CRYPTO=y
-CONFIG_CRYPTO_CRC32C=y
 CONFIG_CRYPTO_USER_API_HASH=y
 CONFIG_CRYPTO_DEV_VIRTIO=y
 CONFIG_CRC16=y
diff --git a/arch/riscv/include/asm/sbi.h b/arch/riscv/include/asm/sbi.h
index 5ddec9c4e204..ce93f6cd8c7d 100644
--- a/arch/riscv/include/asm/sbi.h
+++ b/arch/riscv/include/asm/sbi.h
@@ -29,6 +29,7 @@ enum sbi_ext_id {
 	SBI_EXT_HSM = 0x48534D,
 	SBI_EXT_SRST = 0x53525354,
 	SBI_EXT_PMU = 0x504D55,
+	SBI_EXT_CACHE = 0x09057485,
 };
 
 enum sbi_ext_base_fid {
@@ -96,6 +97,11 @@ enum sbi_ext_pmu_fid {
 	SBI_EXT_PMU_COUNTER_FW_READ,
 };
 
+enum sbi_ext_cache_fid {
+	SBI_EXT_BASE_L2_FLUSH = 0,
+	SBI_EXT_BASE_L2_INVALIDATE,
+};
+
 #define RISCV_PMU_RAW_EVENT_MASK GENMASK_ULL(55, 0)
 #define RISCV_PMU_RAW_EVENT_IDX 0x20000
 
@@ -240,6 +246,8 @@ int sbi_remote_hfence_vvma_asid(const unsigned long *hart_mask,
 				unsigned long size,
 				unsigned long asid);
 int sbi_probe_extension(int ext);
+int sbi_cache_invalidate(unsigned long start, unsigned long len);
+int sbi_cache_flush(unsigned long start, unsigned long len);
 
 /* Check if current SBI specification version is 0.1 or not */
 static inline int sbi_spec_is_0_1(void)
diff --git a/arch/riscv/kernel/sbi.c b/arch/riscv/kernel/sbi.c
index eac30382f79e..5cab4d24ea03 100644
--- a/arch/riscv/kernel/sbi.c
+++ b/arch/riscv/kernel/sbi.c
@@ -605,6 +605,35 @@ static const struct riscv_ipi_ops sbi_ipi_ops = {
 	.ipi_inject = sbi_send_cpumask_ipi
 };
 
+int sbi_cache_flush(unsigned long start, unsigned long len)
+{
+	struct sbiret ret;
+
+	ret = sbi_ecall(SBI_EXT_CACHE, SBI_EXT_BASE_L2_FLUSH,
+		start, len, 0, 0, 0, 0);
+	if (!ret.error)
+		if (ret.value)
+			return ret.value;
+
+	return -ENOTSUPP;
+}
+EXPORT_SYMBOL(sbi_cache_flush);
+
+int sbi_cache_invalidate(unsigned long start, unsigned long len)
+{
+
+	struct sbiret ret;
+
+	ret = sbi_ecall(SBI_EXT_CACHE, SBI_EXT_BASE_L2_INVALIDATE,
+		start, len, 0, 0, 0, 0);
+	if (!ret.error)
+		if (ret.value)
+			return ret.value;
+
+	return -ENOTSUPP;
+}
+EXPORT_SYMBOL(sbi_cache_invalidate);
+
 void __init sbi_init(void)
 {
 	int ret;
diff --git a/arch/riscv/mm/dma-noncoherent.c b/arch/riscv/mm/dma-noncoherent.c
index 868dd02945fc..c1f7c5c21095 100644
--- a/arch/riscv/mm/dma-noncoherent.c
+++ b/arch/riscv/mm/dma-noncoherent.c
@@ -14,6 +14,62 @@
 #include <asm/sbi.h>
 #include <asm/smp.h>
 
+#ifdef CONFIG_SOC_STARFIVE_DUBHE
+void arch_sync_dma_for_device(phys_addr_t paddr, size_t size, enum dma_data_direction dir)
+{
+	sbi_cache_flush(paddr, size);
+}
+
+void arch_sync_dma_for_cpu(phys_addr_t paddr, size_t size, enum dma_data_direction dir)
+{
+	switch (dir) {
+	case DMA_BIDIRECTIONAL:
+		sbi_cache_flush(paddr, size);
+		break;
+	case DMA_FROM_DEVICE:
+		sbi_cache_invalidate(paddr, size);
+		break;
+	case DMA_TO_DEVICE:
+		break;
+	default:
+		BUG();
+	}
+}
+
+void arch_setup_dma_ops(struct device *dev, u64 dma_base, u64 size,
+		const struct iommu_ops *iommu, bool coherent)
+{
+	dev_info(dev, "coherent device %d dev->dma_coherent %d\n", coherent, dev->dma_coherent);
+	dev->dma_coherent = coherent;
+}
+
+void arch_dma_prep_coherent(struct page *page, size_t size)
+{
+	void *flush_addr = page_address(page);
+
+	sbi_cache_flush(__pa(flush_addr), size);
+}
+
+void arch_dma_clear_uncached(void *addr, size_t size)
+{
+	memunmap(addr);
+}
+
+void *arch_dma_set_uncached(void *addr, size_t size)
+{
+	phys_addr_t phys_addr = __pa(addr) + CONFIG_RISCV_UNCACHED_OFFSET;
+	void *mem_base = NULL;
+
+	mem_base = memremap(phys_addr, size, MEMREMAP_WT);
+	if (!mem_base) {
+		pr_err("%s memremap failed for addr %px\n", __func__, addr);
+		return ERR_PTR(-EINVAL);
+	}
+
+	return mem_base;
+}
+
+#else
 //TODO Do it through SBI
 #include <soc/sifive/sifive_l2_cache.h>
 
@@ -61,3 +117,4 @@ void *arch_dma_set_uncached(void *addr, size_t size)
 
 	return mem_base;
 }
+#endif
diff --git a/drivers/net/ethernet/stmicro/stmmac/Kconfig b/drivers/net/ethernet/stmicro/stmmac/Kconfig
index 929cfc22cd0c..41bafd5cac80 100644
--- a/drivers/net/ethernet/stmicro/stmmac/Kconfig
+++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig
@@ -232,6 +232,17 @@ config DWMAC_INTEL_PLAT
 	  the stmmac device driver. This driver is used for the Intel Keem Bay
 	  SoC.
 
+config DWMAC_STARFIVE_PLAT
+	tristate "StarFive dwmac support"
+	depends on OF && COMMON_CLK
+	depends on STMMAC_ETH
+	help
+	  Support for ethernet controllers on StarFive SoCs
+
+	  This selects the StarFive platform specific glue layer support for
+	  the stmmac device driver. This driver is used for the StarFive JH8100
+	  SoC.
+
 config DWMAC_VISCONTI
 	tristate "Toshiba Visconti DWMAC support"
 	default ARCH_VISCONTI
diff --git a/drivers/net/ethernet/stmicro/stmmac/Makefile b/drivers/net/ethernet/stmicro/stmmac/Makefile
index d4e12e9ace4f..1df002c07f79 100644
--- a/drivers/net/ethernet/stmicro/stmmac/Makefile
+++ b/drivers/net/ethernet/stmicro/stmmac/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_DWMAC_SUNXI)	+= dwmac-sunxi.o
 obj-$(CONFIG_DWMAC_SUN8I)	+= dwmac-sun8i.o
 obj-$(CONFIG_DWMAC_DWC_QOS_ETH)	+= dwmac-dwc-qos-eth.o
 obj-$(CONFIG_DWMAC_INTEL_PLAT)	+= dwmac-intel-plat.o
+obj-$(CONFIG_DWMAC_STARFIVE_PLAT)	+= dwmac-starfive-plat.o
 obj-$(CONFIG_DWMAC_GENERIC)	+= dwmac-generic.o
 obj-$(CONFIG_DWMAC_IMX8)	+= dwmac-imx.o
 obj-$(CONFIG_DWMAC_VISCONTI)	+= dwmac-visconti.o
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-starfive-plat.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-starfive-plat.c
new file mode 100644
index 000000000000..a6248cd0bde9
--- /dev/null
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-starfive-plat.c
@@ -0,0 +1,150 @@
+// SPDX-License-Identifier: GPL-2.0
+/* StarFive DWMAC platform driver
+ *
+ * Copyright(C) 2022 StarFive Technology Co., Ltd.
+ */
+
+#include <linux/of_device.h>
+#include "stmmac_platform.h"
+
+struct starfive_dwmac {
+	struct device *dev;
+	void __iomem *regs;
+	struct clk *clk_tx;
+	struct clk *clk_gtx;
+	struct clk *clk_gtxc;
+};
+
+static void starfive_eth_fix_mac_speed(void *priv, unsigned int speed)
+{
+	struct starfive_dwmac *dwmac = priv;
+	unsigned long rate;
+	int err;
+
+	switch (speed) {
+	case SPEED_1000:
+		rate = 125000000;
+		break;
+	case SPEED_100:
+		rate = 25000000;
+		break;
+	case SPEED_10:
+		rate = 2500000;
+		break;
+	default:
+		dev_err(dwmac->dev, "invalid speed %u\n", speed);
+		return;
+	}
+
+	err = clk_set_rate(dwmac->clk_gtx, rate);
+	if (err < 0)
+		dev_err(dwmac->dev, "failed to set tx rate %lu\n", rate);
+}
+
+static const struct of_device_id starfive_eth_plat_match[] = {
+	{ .compatible = "starfive,dwmac"},
+	{ }
+};
+
+static int starfive_eth_plat_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct plat_stmmacenet_data *plat_dat;
+	struct stmmac_resources stmmac_res;
+	struct starfive_dwmac *dwmac;
+	int err;
+
+	err = stmmac_get_platform_resources(pdev, &stmmac_res);
+	if (err)
+		return err;
+
+	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);
+	}
+
+	dwmac = devm_kzalloc(&pdev->dev, sizeof(*dwmac), GFP_KERNEL);
+	if (!dwmac)
+		return -ENOMEM;
+
+	dwmac->dev = &pdev->dev;
+	dwmac->regs = stmmac_res.addr;
+
+	if (!is_of_node(dev->fwnode))
+		goto bypass_clk_reset_gpio;
+
+	dwmac->clk_tx = devm_clk_get(&pdev->dev, "tx");
+	if (IS_ERR(dwmac->clk_tx)) {
+		err = PTR_ERR(dwmac->clk_tx);
+		goto err;
+	}
+
+	err = clk_prepare_enable(dwmac->clk_tx);
+	if (err < 0)
+		goto err;
+
+	dwmac->clk_gtx = devm_clk_get(&pdev->dev, "gtx");
+	if (IS_ERR(dwmac->clk_gtx)) {
+		err = PTR_ERR(dwmac->clk_gtx);
+		goto disable_tx;
+	}
+
+	err = clk_prepare_enable(dwmac->clk_gtx);
+	if (err < 0)
+		goto disable_tx;
+
+	dwmac->clk_gtxc = devm_clk_get(&pdev->dev, "gtxc");
+	if (IS_ERR(dwmac->clk_gtxc)) {
+		err = PTR_ERR(dwmac->clk_gtxc);
+		goto disable_gtx;
+	}
+
+	err = clk_prepare_enable(dwmac->clk_gtxc);
+	if (err < 0)
+		goto disable_gtx;
+
+	err = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
+	if (err)
+		goto err;
+
+bypass_clk_reset_gpio:
+	plat_dat->fix_mac_speed = starfive_eth_fix_mac_speed;
+	plat_dat->init = NULL;
+	plat_dat->bsp_priv = dwmac;
+	return 0;
+
+disable_gtx:
+	clk_disable_unprepare(dwmac->clk_gtx);
+disable_tx:
+	clk_disable_unprepare(dwmac->clk_tx);
+err:
+	stmmac_remove_config_dt(pdev, plat_dat);
+	return err;
+}
+
+static int starfive_eth_plat_remove(struct platform_device *pdev)
+{
+	struct starfive_dwmac *dwmac = get_stmmac_bsp_priv(&pdev->dev);
+
+	clk_disable_unprepare(dwmac->clk_gtxc);
+	clk_disable_unprepare(dwmac->clk_gtx);
+	clk_disable_unprepare(dwmac->clk_tx);
+
+	return 0;
+}
+
+static struct platform_driver starfive_eth_plat_driver = {
+	.probe  = starfive_eth_plat_probe,
+	.remove = starfive_eth_plat_remove,
+	.driver = {
+		.name			= "starfive-eth-plat",
+		.pm				= &stmmac_pltfr_pm_ops,
+		.of_match_table = starfive_eth_plat_match,
+	},
+};
+
+module_platform_driver(starfive_eth_plat_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("StarFive DWMAC platform driver");
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index d6916bee1694..3422f0746d82 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -1463,16 +1463,14 @@ static int stmmac_init_rx_buffers(struct stmmac_priv *priv, struct dma_desc *p,
 	struct stmmac_rx_buffer *buf = &rx_q->buf_pool[i];
 
 	if (!buf->page) {
-		buf->page = page_pool_alloc_pages(rx_q->page_pool,
-						  GFP_ATOMIC | __GFP_NOWARN | GFP_DMA32);
+		buf->page = page_pool_dev_alloc_pages(rx_q->page_pool);
 		if (!buf->page)
 			return -ENOMEM;
 		buf->page_offset = stmmac_rx_offset(priv);
 	}
 
 	if (priv->sph && !buf->sec_page) {
-		buf->sec_page = page_pool_alloc_pages(rx_q->page_pool,
-						      GFP_ATOMIC | __GFP_NOWARN | GFP_DMA32);
+		buf->sec_page = page_pool_dev_alloc_pages(rx_q->page_pool);
 		if (!buf->sec_page)
 			return -ENOMEM;
 
@@ -4488,15 +4486,13 @@ static inline void stmmac_rx_refill(struct stmmac_priv *priv, u32 queue)
 			p = rx_q->dma_rx + entry;
 
 		if (!buf->page) {
-			buf->page = page_pool_alloc_pages(rx_q->page_pool,
-							  GFP_ATOMIC | __GFP_NOWARN | GFP_DMA32);
+			buf->page = page_pool_dev_alloc_pages(rx_q->page_pool);
 			if (!buf->page)
 				break;
 		}
 
 		if (priv->sph && !buf->sec_page) {
-			buf->sec_page = page_pool_alloc_pages(rx_q->page_pool,
-							      GFP_ATOMIC | __GFP_NOWARN | GFP_DMA32);
+			buf->sec_page = page_pool_dev_alloc_pages(rx_q->page_pool);
 			if (!buf->sec_page)
 				break;
 
diff --git a/drivers/net/pcs/pcs-xpcs.c b/drivers/net/pcs/pcs-xpcs.c
index 7de631f5356f..f94161637ea8 100644
--- a/drivers/net/pcs/pcs-xpcs.c
+++ b/drivers/net/pcs/pcs-xpcs.c
@@ -955,23 +955,7 @@ static void xpcs_link_up_sgmii(struct dw_xpcs *xpcs, unsigned int mode,
 	if (phylink_autoneg_inband(mode))
 		return;
 
-	switch (speed) {
-	case SPEED_1000:
-		val = BMCR_SPEED1000;
-		break;
-	case SPEED_100:
-		val = BMCR_SPEED100;
-		break;
-	case SPEED_10:
-		val = BMCR_SPEED10;
-		break;
-	default:
-		return;
-	}
-
-	if (duplex == DUPLEX_FULL)
-		val |= BMCR_FULLDPLX;
-
+	val = mii_bmcr_encode_fixed(speed, duplex);
 	ret = xpcs_write(xpcs, MDIO_MMD_VEND2, MDIO_CTRL1, val);
 	if (ret)
 		pr_err("%s: xpcs_write returned %pe\n", __func__, ERR_PTR(ret));
diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c
index 4fcfca4e1702..541f784981a1 100644
--- a/drivers/net/phy/marvell.c
+++ b/drivers/net/phy/marvell.c
@@ -189,6 +189,8 @@
 #define MII_88E1510_GEN_CTRL_REG_1_MODE_RGMII_SGMII	0x4
 #define MII_88E1510_GEN_CTRL_REG_1_RESET	0x8000	/* Soft reset */
 
+#define MII_88E1510_MSCR_2		0x15
+
 #define MII_VCT5_TX_RX_MDI0_COUPLING	0x10
 #define MII_VCT5_TX_RX_MDI1_COUPLING	0x11
 #define MII_VCT5_TX_RX_MDI2_COUPLING	0x12
@@ -551,9 +553,9 @@ static int m88e1121_config_aneg_rgmii_delays(struct phy_device *phydev)
 	else
 		mscr = 0;
 
-	return phy_modify_paged(phydev, MII_MARVELL_MSCR_PAGE,
-				MII_88E1121_PHY_MSCR_REG,
-				MII_88E1121_PHY_MSCR_DELAY_MASK, mscr);
+	return phy_modify_paged_changed(phydev, MII_MARVELL_MSCR_PAGE,
+					MII_88E1121_PHY_MSCR_REG,
+					MII_88E1121_PHY_MSCR_DELAY_MASK, mscr);
 }
 
 static int m88e1121_config_aneg(struct phy_device *phydev)
@@ -567,11 +569,13 @@ static int m88e1121_config_aneg(struct phy_device *phydev)
 			return err;
 	}
 
+	changed = err;
+
 	err = marvell_set_polarity(phydev, phydev->mdix_ctrl);
 	if (err < 0)
 		return err;
 
-	changed = err;
+	changed |= err;
 
 	err = genphy_config_aneg(phydev);
 	if (err < 0)
@@ -1173,7 +1177,44 @@ static int m88e1318_config_init(struct phy_device *phydev)
 
 static int m88e1510_config_init(struct phy_device *phydev)
 {
+	static const struct {
+		u16 reg17, reg16;
+	} errata_vals[] = {
+		{ 0x214b, 0x2144 },
+		{ 0x0c28, 0x2146 },
+		{ 0xb233, 0x214d },
+		{ 0xcc0c, 0x2159 },
+	};
 	int err;
+	int i;
+
+	/* As per Marvell Release Notes - Alaska 88E1510/88E1518/88E1512/
+	 * 88E1514 Rev A0, Errata Section 5.1:
+	 * If EEE is intended to be used, the following register writes
+	 * must be done once after every hardware reset.
+	 */
+	err = marvell_set_page(phydev, 0x00FF);
+	if (err < 0)
+		return err;
+
+	for (i = 0; i < ARRAY_SIZE(errata_vals); ++i) {
+		err = phy_write(phydev, 17, errata_vals[i].reg17);
+		if (err)
+			return err;
+		err = phy_write(phydev, 16, errata_vals[i].reg16);
+		if (err)
+			return err;
+	}
+
+	err = marvell_set_page(phydev, 0x00FB);
+	if (err < 0)
+		return err;
+	err = phy_write(phydev, 07, 0xC00D);
+	if (err < 0)
+		return err;
+	err = marvell_set_page(phydev, MII_MARVELL_COPPER_PAGE);
+	if (err < 0)
+		return err;
 
 	/* SGMII-to-Copper mode initialization */
 	if (phydev->interface == PHY_INTERFACE_MODE_SGMII) {
@@ -1932,6 +1973,52 @@ static void marvell_get_stats(struct phy_device *phydev,
 		data[i] = marvell_get_stat(phydev, i);
 }
 
+static int m88e1510_loopback(struct phy_device *phydev, bool enable)
+{
+	int err;
+
+	if (enable) {
+		u16 bmcr_ctl, mscr2_ctl = 0;
+
+		bmcr_ctl = mii_bmcr_encode_fixed(phydev->speed, phydev->duplex);
+
+		err = phy_write(phydev, MII_BMCR, bmcr_ctl);
+		if (err < 0)
+			return err;
+
+		if (phydev->speed == SPEED_1000)
+			mscr2_ctl = BMCR_SPEED1000;
+		else if (phydev->speed == SPEED_100)
+			mscr2_ctl = BMCR_SPEED100;
+
+		err = phy_modify_paged(phydev, MII_MARVELL_MSCR_PAGE,
+				       MII_88E1510_MSCR_2, BMCR_SPEED1000 |
+				       BMCR_SPEED100, mscr2_ctl);
+		if (err < 0)
+			return err;
+
+		/* Need soft reset to have speed configuration takes effect */
+		err = genphy_soft_reset(phydev);
+		if (err < 0)
+			return err;
+
+		/* FIXME: Based on trial and error test, it seem 1G need to have
+		 * delay between soft reset and loopback enablement.
+		 */
+		if (phydev->speed == SPEED_1000)
+			msleep(1000);
+
+		return phy_modify(phydev, MII_BMCR, BMCR_LOOPBACK,
+				  BMCR_LOOPBACK);
+	} else {
+		err = phy_modify(phydev, MII_BMCR, BMCR_LOOPBACK, 0);
+		if (err < 0)
+			return err;
+
+		return phy_config_aneg(phydev);
+	}
+}
+
 static int marvell_vct5_wait_complete(struct phy_device *phydev)
 {
 	int i;
@@ -3078,7 +3165,7 @@ static struct phy_driver marvell_drivers[] = {
 		.get_sset_count = marvell_get_sset_count,
 		.get_strings = marvell_get_strings,
 		.get_stats = marvell_get_stats,
-		.set_loopback = genphy_loopback,
+		.set_loopback = m88e1510_loopback,
 		.get_tunable = m88e1011_get_tunable,
 		.set_tunable = m88e1011_set_tunable,
 		.cable_test_start = marvell_vct7_cable_test_start,
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 4f9990b47a37..5544d3ee3aba 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -2001,18 +2001,12 @@ EXPORT_SYMBOL(genphy_config_eee_advert);
  */
 int genphy_setup_forced(struct phy_device *phydev)
 {
-	u16 ctl = 0;
+	u16 ctl;
 
 	phydev->pause = 0;
 	phydev->asym_pause = 0;
 
-	if (SPEED_1000 == phydev->speed)
-		ctl |= BMCR_SPEED1000;
-	else if (SPEED_100 == phydev->speed)
-		ctl |= BMCR_SPEED100;
-
-	if (DUPLEX_FULL == phydev->duplex)
-		ctl |= BMCR_FULLDPLX;
+	ctl = mii_bmcr_encode_fixed(phydev->speed, phydev->duplex);
 
 	return phy_modify(phydev, MII_BMCR,
 			  ~(BMCR_LOOPBACK | BMCR_ISOLATE | BMCR_PDOWN), ctl);
@@ -2615,13 +2609,7 @@ int genphy_loopback(struct phy_device *phydev, bool enable)
 		u16 val, ctl = BMCR_LOOPBACK;
 		int ret;
 
-		if (phydev->speed == SPEED_1000)
-			ctl |= BMCR_SPEED1000;
-		else if (phydev->speed == SPEED_100)
-			ctl |= BMCR_SPEED100;
-
-		if (phydev->duplex == DUPLEX_FULL)
-			ctl |= BMCR_FULLDPLX;
+		ctl |= mii_bmcr_encode_fixed(phydev->speed, phydev->duplex);
 
 		phy_modify(phydev, MII_BMCR, ~0, ctl);
 
diff --git a/include/linux/mii.h b/include/linux/mii.h
index 12ea29e04293..599b56766a51 100644
--- a/include/linux/mii.h
+++ b/include/linux/mii.h
@@ -595,4 +595,39 @@ static inline u8 mii_resolve_flowctrl_fdx(u16 lcladv, u16 rmtadv)
 	return cap;
 }
 
+/**
+ * mii_bmcr_encode_fixed - encode fixed speed/duplex settings to a BMCR value
+ * @speed: a SPEED_* value
+ * @duplex: a DUPLEX_* value
+ *
+ * Encode the speed and duplex to a BMCR value. 2500, 1000, 100 and 10 Mbps are
+ * supported. 2500Mbps is encoded to 1000Mbps. Other speeds are encoded as 10
+ * Mbps. Unknown duplex values are encoded to half-duplex.
+ */
+static inline u16 mii_bmcr_encode_fixed(int speed, int duplex)
+{
+	u16 bmcr;
+
+	switch (speed) {
+	case SPEED_2500:
+	case SPEED_1000:
+		bmcr = BMCR_SPEED1000;
+		break;
+
+	case SPEED_100:
+		bmcr = BMCR_SPEED100;
+		break;
+
+	case SPEED_10:
+	default:
+		bmcr = BMCR_SPEED10;
+		break;
+	}
+
+	if (duplex == DUPLEX_FULL)
+		bmcr |= BMCR_FULLDPLX;
+
+	return bmcr;
+}
+
 #endif /* __LINUX_MII_H__ */