mail archive of the barebox mailing list
 help / color / mirror / Atom feed
* [PATCH v2 0/6] ARM: stm32mp: finalize second stage support
@ 2019-10-28 21:39 Ahmad Fatoum
  2019-10-28 21:39 ` [PATCH v2 1/6] mci: stm32_sdmmc2: parse generic MCI oftree properties Ahmad Fatoum
                   ` (6 more replies)
  0 siblings, 7 replies; 8+ messages in thread
From: Ahmad Fatoum @ 2019-10-28 21:39 UTC (permalink / raw)
  To: barebox

Add barebox update handler and Ethernet driver.

v1 -> v2:
 - Split off patch to override unsupported device tree specified
   bus-widths
 - Delete fixing the frequency to 208MHz. I can no longer reproduce
   120MHz to trigger timeouts
 - Last 4 patches are new

Ahmad Fatoum (6):
  mci: stm32_sdmmc2: parse generic MCI oftree properties
  ARM: stm32mp: dk2: add barebox SD-Card update handler
  net: add Designware Ethernet QoS for STM32MP
  net: eqos: extend Designware Ethernet QoS for Tegra 186 support
  ARM: stm32mp: include new drivers in defconfig
  Documentation: boards: stm32mp: remove done TODOs

 Documentation/boards/stm32mp.rst         |  11 -
 arch/arm/boards/stm32mp157c-dk2/board.c  |  13 +
 arch/arm/configs/stm32mp_defconfig       |  20 +-
 arch/arm/dts/stm32mp157a-dk1.dtsi        |   4 +
 arch/arm/mach-stm32mp/include/mach/bbu.h |  14 +
 drivers/mci/stm32_sdmmc2.c               |   7 +
 drivers/net/Kconfig                      |  27 +-
 drivers/net/Makefile                     |   3 +
 drivers/net/designware_eqos.c            | 876 +++++++++++++++++++++++
 drivers/net/designware_eqos.h            |  84 +++
 drivers/net/designware_stm32.c           | 245 +++++++
 drivers/net/designware_tegra186.c        | 347 +++++++++
 12 files changed, 1636 insertions(+), 15 deletions(-)
 create mode 100644 arch/arm/mach-stm32mp/include/mach/bbu.h
 create mode 100644 drivers/net/designware_eqos.c
 create mode 100644 drivers/net/designware_eqos.h
 create mode 100644 drivers/net/designware_stm32.c
 create mode 100644 drivers/net/designware_tegra186.c

-- 
2.20.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH v2 1/6] mci: stm32_sdmmc2: parse generic MCI oftree properties
  2019-10-28 21:39 [PATCH v2 0/6] ARM: stm32mp: finalize second stage support Ahmad Fatoum
@ 2019-10-28 21:39 ` Ahmad Fatoum
  2019-10-28 21:39 ` [PATCH v2 2/6] ARM: stm32mp: dk2: add barebox SD-Card update handler Ahmad Fatoum
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: Ahmad Fatoum @ 2019-10-28 21:39 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

From: Ahmad Fatoum <a.fatoum@pengutronix.de>

mci_of_parse takes care of the generic oftree bits, like removable flag,
aliases and max-frequency. Insert it, so we can start to refer to the
controller's block devices by a fixed name.

Sticking in the mci_of_parse shows that the SD/MMC2 driver in barebox
doesn't play nicely with the device tree specification of the SD/MMC2
for the dk2 board, however:

Specifying the bus-width = <4> property in the device tree now made mmc
usage fail when reading the environment:

  ERROR: error SDMMC_STA_DCRCFAIL (0x81042) for cmd 18
  ERROR: stm32_sdmmc2_end_cmd: error SDMMC_STA_CTIMEOUT (0x4) for cmd 12
  WARNING: stm32_sdmmc2_send_cmd: cmd 12 failed, retrying ...
  ERROR: stm32_sdmmc2_end_cmd: error SDMMC_STA_CTIMEOUT (0x4) for cmd 12
  WARNING: stm32_sdmmc2_send_cmd: cmd 12 failed, retrying ...
  ERROR: stm32_sdmmc2_end_cmd: error SDMMC_STA_CTIMEOUT (0x4) for cmd 12
  WARNING: stm32_sdmmc2_send_cmd: cmd 12 failed, retrying ...
  ERROR: stm32_sdmmc2_end_cmd: error SDMMC_STA_CTIMEOUT (0x4) for cmd 12

We'll want to fix this eventually, but for now force the bus width to 1.
So we don't forget about this, print a notice to the console that we've
done so.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/mci/stm32_sdmmc2.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/drivers/mci/stm32_sdmmc2.c b/drivers/mci/stm32_sdmmc2.c
index 7346c8a3f5d6..1a41c34d24e2 100644
--- a/drivers/mci/stm32_sdmmc2.c
+++ b/drivers/mci/stm32_sdmmc2.c
@@ -632,6 +632,13 @@ static int stm32_sdmmc2_probe(struct amba_device *adev,
 	mci->f_max = 208000000;
 	mci->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
 
+	mci_of_parse(&priv->mci);
+
+	if (mci->host_caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA)) {
+		dev_notice(dev, "Fixing bus-width to 1 due to driver limitation\n");
+		mci->host_caps &= ~(MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA);
+	}
+
 	return mci_register(&priv->mci);
 
 priv_free:
-- 
2.20.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH v2 2/6] ARM: stm32mp: dk2: add barebox SD-Card update handler
  2019-10-28 21:39 [PATCH v2 0/6] ARM: stm32mp: finalize second stage support Ahmad Fatoum
  2019-10-28 21:39 ` [PATCH v2 1/6] mci: stm32_sdmmc2: parse generic MCI oftree properties Ahmad Fatoum
@ 2019-10-28 21:39 ` Ahmad Fatoum
  2019-10-28 21:39 ` [PATCH v2 3/6] net: add Designware Ethernet QoS for STM32MP Ahmad Fatoum
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: Ahmad Fatoum @ 2019-10-28 21:39 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

From: Ahmad Fatoum <a.fatoum@pengutronix.de>

Now with the SD/MMC controller supported, lets add a bbu handler, so we
can use it to update the second stage boot loader partition.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 arch/arm/boards/stm32mp157c-dk2/board.c  | 13 +++++++++++++
 arch/arm/dts/stm32mp157a-dk1.dtsi        |  4 ++++
 arch/arm/mach-stm32mp/include/mach/bbu.h | 14 ++++++++++++++
 3 files changed, 31 insertions(+)
 create mode 100644 arch/arm/mach-stm32mp/include/mach/bbu.h

diff --git a/arch/arm/boards/stm32mp157c-dk2/board.c b/arch/arm/boards/stm32mp157c-dk2/board.c
index 9cb861af85d8..f15ae0b4aff0 100644
--- a/arch/arm/boards/stm32mp157c-dk2/board.c
+++ b/arch/arm/boards/stm32mp157c-dk2/board.c
@@ -4,6 +4,7 @@
 #include <init.h>
 #include <asm/memory.h>
 #include <mach/stm32.h>
+#include <mach/bbu.h>
 
 static int dk2_mem_init(void)
 {
@@ -15,3 +16,15 @@ static int dk2_mem_init(void)
 	return 0;
 }
 mem_initcall(dk2_mem_init);
+
+static int dk2_postcore_init(void)
+{
+	if (!of_machine_is_compatible("st,stm32mp157c-dk2"))
+		return 0;
+
+	stm32mp_bbu_mmc_register_handler("sd", "/dev/mmc0.ssbl",
+					 BBU_HANDLER_FLAG_DEFAULT);
+
+	return 0;
+}
+postcore_initcall(dk2_postcore_init);
diff --git a/arch/arm/dts/stm32mp157a-dk1.dtsi b/arch/arm/dts/stm32mp157a-dk1.dtsi
index 7f3b6fcf55ae..05a39d32e2fa 100644
--- a/arch/arm/dts/stm32mp157a-dk1.dtsi
+++ b/arch/arm/dts/stm32mp157a-dk1.dtsi
@@ -8,6 +8,10 @@
 #include <dt-bindings/gpio/gpio.h>
 
 / {
+	aliases {
+		mmc0 = &sdmmc1;
+	};
+
 	chosen {
 		environment {
 			compatible = "barebox,environment";
diff --git a/arch/arm/mach-stm32mp/include/mach/bbu.h b/arch/arm/mach-stm32mp/include/mach/bbu.h
new file mode 100644
index 000000000000..8b9504400e9e
--- /dev/null
+++ b/arch/arm/mach-stm32mp/include/mach/bbu.h
@@ -0,0 +1,14 @@
+#ifndef MACH_STM32MP_BBU_H_
+#define MACH_STM32MP_BBU_H_
+
+#include <bbu.h>
+
+static inline int stm32mp_bbu_mmc_register_handler(const char *name,
+						   const char *devicefile,
+						   unsigned long flags)
+{
+	return bbu_register_std_file_update(name, flags, devicefile,
+					    filetype_stm32_image_v1);
+}
+
+#endif /* MACH_STM32MP_BBU_H_ */
-- 
2.20.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH v2 3/6] net: add Designware Ethernet QoS for STM32MP
  2019-10-28 21:39 [PATCH v2 0/6] ARM: stm32mp: finalize second stage support Ahmad Fatoum
  2019-10-28 21:39 ` [PATCH v2 1/6] mci: stm32_sdmmc2: parse generic MCI oftree properties Ahmad Fatoum
  2019-10-28 21:39 ` [PATCH v2 2/6] ARM: stm32mp: dk2: add barebox SD-Card update handler Ahmad Fatoum
@ 2019-10-28 21:39 ` Ahmad Fatoum
  2019-10-28 21:39 ` [PATCH v2 4/6] net: eqos: extend Designware Ethernet QoS for Tegra 186 support Ahmad Fatoum
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: Ahmad Fatoum @ 2019-10-28 21:39 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

From: Ahmad Fatoum <a.fatoum@pengutronix.de>

We already have Designware NIC support in barebox, but for the DWMAC1000,
the DWMAC4 (also called GMAC4), no support was mainline so far.

The DWMAC4 is different enough that sharing code with the DWMAC1000 is
not really that helpful, because even basics like MDIO registers have
different layout. Instead of coding bit masks and shifts into the driver
data, like Linux does, we'll keep both driver kinds separate.

Nevertheless, we collect functions that are not SoC-specific into a
separate 'library' file.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/net/Kconfig            |  21 +-
 drivers/net/Makefile           |   2 +
 drivers/net/designware_eqos.c  | 876 +++++++++++++++++++++++++++++++++
 drivers/net/designware_eqos.h  |  84 ++++
 drivers/net/designware_stm32.c | 245 +++++++++
 5 files changed, 1227 insertions(+), 1 deletion(-)
 create mode 100644 drivers/net/designware_eqos.c
 create mode 100644 drivers/net/designware_eqos.h
 create mode 100644 drivers/net/designware_stm32.c

diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 57f0b57d64b1..5b66fe84ebf6 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -61,7 +61,7 @@ config DRIVER_NET_DAVINCI_EMAC
 	select PHYLIB
 
 config DRIVER_NET_DESIGNWARE
-	bool "Designware Universal MAC ethernet platform support"
+	bool "Designware Universal MAC1000 ethernet platform support"
 	depends on HAS_DMA
 	select PHYLIB
 	help
@@ -87,6 +87,25 @@ config DRIVER_NET_DESIGNWARE_SOCFPGA
 
 endif
 
+config DRIVER_NET_DESIGNWARE_EQOS
+	bool "Designware Designware Ethernet QoS support"
+	depends on HAS_DMA
+	select PHYLIB
+	select OFTREE
+	help
+	  This option enables support for the Synopsys
+	  Designware Ethernet Quality-of-Service (GMAC4).
+
+if DRIVER_NET_DESIGNWARE_EQOS
+
+config DRIVER_NET_DESIGNWARE_STM32
+	bool "Designware EQOS STM32 driver"
+	select MFD_SYSCON
+	help
+	  This option enables support for the ethernet MAC on the STM32MP platforms.
+
+endif
+
 config DRIVER_NET_DM9K
 	bool "Davicom dm9k[E|A|B] ethernet driver"
 	depends on HAS_DM9000
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index f6a8213613ca..7ecf4e2851ba 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -11,6 +11,8 @@ obj-$(CONFIG_DRIVER_NET_DAVINCI_EMAC)	+= davinci_emac.o
 obj-$(CONFIG_DRIVER_NET_DESIGNWARE)	+= designware.o
 obj-$(CONFIG_DRIVER_NET_DESIGNWARE_GENERIC) += designware_generic.o
 obj-$(CONFIG_DRIVER_NET_DESIGNWARE_SOCFPGA) += designware_socfpga.o
+obj-$(CONFIG_DRIVER_NET_DESIGNWARE_EQOS) += designware_eqos.o
+obj-$(CONFIG_DRIVER_NET_DESIGNWARE_STM32) += designware_stm32.o
 obj-$(CONFIG_DRIVER_NET_DM9K)		+= dm9k.o
 obj-$(CONFIG_DRIVER_NET_E1000)		+= e1000/regio.o e1000/main.o e1000/eeprom.o
 obj-$(CONFIG_DRIVER_NET_ENC28J60)	+= enc28j60.o
diff --git a/drivers/net/designware_eqos.c b/drivers/net/designware_eqos.c
new file mode 100644
index 000000000000..a49239e0573e
--- /dev/null
+++ b/drivers/net/designware_eqos.c
@@ -0,0 +1,876 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2016, NVIDIA CORPORATION.
+ * Copyright (c) 2019, Ahmad Fatoum, Pengutronix
+ *
+ * Portions based on U-Boot's rtl8169.c and dwc_eth_qos.
+ */
+
+#include <common.h>
+#include <init.h>
+#include <dma.h>
+#include <net.h>
+#include <of_net.h>
+#include <linux/iopoll.h>
+#include <linux/time.h>
+#include <linux/sizes.h>
+
+#include "designware_eqos.h"
+
+/* Core registers */
+
+#define EQOS_MAC_REGS_BASE 0x000
+struct eqos_mac_regs {
+	u32 config;				/* 0x000 */
+	u32 ext_config;				/* 0x004 */
+	u32 unused_004[(0x070 - 0x008) / 4];	/* 0x008 */
+	u32 q0_tx_flow_ctrl;			/* 0x070 */
+	u32 unused_070[(0x090 - 0x074) / 4];	/* 0x074 */
+	u32 rx_flow_ctrl;			/* 0x090 */
+	u32 unused_094;				/* 0x094 */
+	u32 txq_prty_map0;			/* 0x098 */
+	u32 unused_09c;				/* 0x09c */
+	u32 rxq_ctrl0;				/* 0x0a0 */
+	u32 unused_0a4;				/* 0x0a4 */
+	u32 rxq_ctrl2;				/* 0x0a8 */
+	u32 unused_0ac[(0x0dc - 0x0ac) / 4];	/* 0x0ac */
+	u32 us_tic_counter;			/* 0x0dc */
+	u32 unused_0e0[(0x11c - 0x0e0) / 4];	/* 0x0e0 */
+	u32 hw_feature0;			/* 0x11c */
+	u32 hw_feature1;			/* 0x120 */
+	u32 hw_feature2;			/* 0x124 */
+	u32 unused_128[(0x200 - 0x128) / 4];	/* 0x128 */
+	u32 mdio_address;			/* 0x200 */
+	u32 mdio_data;				/* 0x204 */
+	u32 unused_208[(0x300 - 0x208) / 4];	/* 0x208 */
+	u32 macaddr0hi;				/* 0x300 */
+	u32 macaddr0lo;				/* 0x304 */
+};
+
+#define EQOS_MAC_CONFIGURATION_GPSLCE			BIT(23)
+#define EQOS_MAC_CONFIGURATION_CST			BIT(21)
+#define EQOS_MAC_CONFIGURATION_ACS			BIT(20)
+#define EQOS_MAC_CONFIGURATION_WD			BIT(19)
+#define EQOS_MAC_CONFIGURATION_JD			BIT(17)
+#define EQOS_MAC_CONFIGURATION_JE			BIT(16)
+#define EQOS_MAC_CONFIGURATION_PS			BIT(15)
+#define EQOS_MAC_CONFIGURATION_FES			BIT(14)
+#define EQOS_MAC_CONFIGURATION_DM			BIT(13)
+#define EQOS_MAC_CONFIGURATION_TE			BIT(1)
+#define EQOS_MAC_CONFIGURATION_RE			BIT(0)
+
+#define EQOS_MAC_Q0_TX_FLOW_CTRL_PT_SHIFT		16
+#define EQOS_MAC_Q0_TX_FLOW_CTRL_PT_MASK		0xffff
+#define EQOS_MAC_Q0_TX_FLOW_CTRL_TFE			BIT(1)
+
+#define EQOS_MAC_RX_FLOW_CTRL_RFE			BIT(0)
+
+#define EQOS_MAC_TXQ_PRTY_MAP0_PSTQ0_SHIFT		0
+#define EQOS_MAC_TXQ_PRTY_MAP0_PSTQ0_MASK		0xff
+
+#define EQOS_MAC_RXQ_CTRL2_PSRQ0_SHIFT			0
+#define EQOS_MAC_RXQ_CTRL2_PSRQ0_MASK			0xff
+
+#define EQOS_MAC_HW_FEATURE1_TXFIFOSIZE_SHIFT		6
+#define EQOS_MAC_HW_FEATURE1_TXFIFOSIZE_MASK		0x1f
+#define EQOS_MAC_HW_FEATURE1_RXFIFOSIZE_SHIFT		0
+#define EQOS_MAC_HW_FEATURE1_RXFIFOSIZE_MASK		0x1f
+
+#define EQOS_MTL_REGS_BASE 0xd00
+struct eqos_mtl_regs {
+	u32 txq0_operation_mode;			/* 0xd00 */
+	u32 unused_d04;				/* 0xd04 */
+	u32 txq0_debug;				/* 0xd08 */
+	u32 unused_d0c[(0xd18 - 0xd0c) / 4];	/* 0xd0c */
+	u32 txq0_quantum_weight;			/* 0xd18 */
+	u32 unused_d1c[(0xd30 - 0xd1c) / 4];	/* 0xd1c */
+	u32 rxq0_operation_mode;			/* 0xd30 */
+	u32 unused_d34;				/* 0xd34 */
+	u32 rxq0_debug;				/* 0xd38 */
+};
+
+#define EQOS_MTL_TXQ0_OPERATION_MODE_TQS_SHIFT		16
+#define EQOS_MTL_TXQ0_OPERATION_MODE_TQS_MASK		0x1ff
+#define EQOS_MTL_TXQ0_OPERATION_MODE_TXQEN_SHIFT	2
+#define EQOS_MTL_TXQ0_OPERATION_MODE_TXQEN_MASK		3
+#define EQOS_MTL_TXQ0_OPERATION_MODE_TXQEN_ENABLED	2
+#define EQOS_MTL_TXQ0_OPERATION_MODE_TSF		BIT(1)
+#define EQOS_MTL_TXQ0_OPERATION_MODE_FTQ		BIT(0)
+
+#define EQOS_MTL_TXQ0_DEBUG_TXQSTS			BIT(4)
+#define EQOS_MTL_TXQ0_DEBUG_TRCSTS_SHIFT		1
+#define EQOS_MTL_TXQ0_DEBUG_TRCSTS_MASK			3
+
+#define EQOS_MTL_RXQ0_OPERATION_MODE_RQS_SHIFT		20
+#define EQOS_MTL_RXQ0_OPERATION_MODE_RQS_MASK		0x3ff
+#define EQOS_MTL_RXQ0_OPERATION_MODE_RFD_SHIFT		14
+#define EQOS_MTL_RXQ0_OPERATION_MODE_RFD_MASK		0x3f
+#define EQOS_MTL_RXQ0_OPERATION_MODE_RFA_SHIFT		8
+#define EQOS_MTL_RXQ0_OPERATION_MODE_RFA_MASK		0x3f
+#define EQOS_MTL_RXQ0_OPERATION_MODE_EHFC		BIT(7)
+#define EQOS_MTL_RXQ0_OPERATION_MODE_RSF		BIT(5)
+
+#define EQOS_MTL_RXQ0_DEBUG_PRXQ_SHIFT			16
+#define EQOS_MTL_RXQ0_DEBUG_PRXQ_MASK			0x7fff
+#define EQOS_MTL_RXQ0_DEBUG_RXQSTS_SHIFT		4
+#define EQOS_MTL_RXQ0_DEBUG_RXQSTS_MASK			3
+
+#define EQOS_DMA_REGS_BASE 0x1000
+struct eqos_dma_regs {
+	u32 mode;				/* 0x1000 */
+	u32 sysbus_mode;			/* 0x1004 */
+	u32 unused_1008[(0x1100 - 0x1008) / 4];	/* 0x1008 */
+	u32 ch0_control;			/* 0x1100 */
+	u32 ch0_tx_control;			/* 0x1104 */
+	u32 ch0_rx_control;			/* 0x1108 */
+	u32 unused_110c;			/* 0x110c */
+	u32 ch0_txdesc_list_haddress;		/* 0x1110 */
+	u32 ch0_txdesc_list_address;		/* 0x1114 */
+	u32 ch0_rxdesc_list_haddress;		/* 0x1118 */
+	u32 ch0_rxdesc_list_address;		/* 0x111c */
+	u32 ch0_txdesc_tail_pointer;		/* 0x1120 */
+	u32 unused_1124;			/* 0x1124 */
+	u32 ch0_rxdesc_tail_pointer;		/* 0x1128 */
+	u32 ch0_txdesc_ring_length;		/* 0x112c */
+	u32 ch0_rxdesc_ring_length;		/* 0x1130 */
+};
+
+#define EQOS_DMA_MODE_SWR				BIT(0)
+
+#define EQOS_DMA_SYSBUS_MODE_RD_OSR_LMT_SHIFT		16
+#define EQOS_DMA_SYSBUS_MODE_RD_OSR_LMT_MASK		0xf
+#define EQOS_DMA_SYSBUS_MODE_EAME			BIT(11)
+#define EQOS_DMA_SYSBUS_MODE_BLEN16			BIT(3)
+#define EQOS_DMA_SYSBUS_MODE_BLEN8			BIT(2)
+#define EQOS_DMA_SYSBUS_MODE_BLEN4			BIT(1)
+
+#define EQOS_DMA_CH0_CONTROL_PBLX8			BIT(16)
+
+#define EQOS_DMA_CH0_TX_CONTROL_TXPBL_SHIFT		16
+#define EQOS_DMA_CH0_TX_CONTROL_TXPBL_MASK		0x3f
+#define EQOS_DMA_CH0_TX_CONTROL_OSP			BIT(4)
+#define EQOS_DMA_CH0_TX_CONTROL_ST			BIT(0)
+
+#define EQOS_DMA_CH0_RX_CONTROL_RXPBL_SHIFT		16
+#define EQOS_DMA_CH0_RX_CONTROL_RXPBL_MASK		0x3f
+#define EQOS_DMA_CH0_RX_CONTROL_RBSZ_SHIFT		1
+#define EQOS_DMA_CH0_RX_CONTROL_RBSZ_MASK		0x3fff
+#define EQOS_DMA_CH0_RX_CONTROL_SR			BIT(0)
+
+/* Descriptors */
+
+#define EQOS_DESCRIPTOR_WORDS	4
+#define EQOS_DESCRIPTOR_SIZE	(EQOS_DESCRIPTOR_WORDS * 4)
+/* We assume ARCH_DMA_MINALIGN >= 16; 16 is the EQOS HW minimum */
+#define EQOS_DESCRIPTOR_ALIGN	64
+#define EQOS_DESCRIPTORS_TX	4
+#define EQOS_DESCRIPTORS_RX	4
+#define EQOS_DESCRIPTORS_NUM	(EQOS_DESCRIPTORS_TX + EQOS_DESCRIPTORS_RX)
+#define EQOS_DESCRIPTORS_SIZE	ALIGN(EQOS_DESCRIPTORS_NUM * \
+				      EQOS_DESCRIPTOR_SIZE, EQOS_DESCRIPTOR_ALIGN)
+#define EQOS_BUFFER_ALIGN	EQOS_DESCRIPTOR_ALIGN
+#define EQOS_MAX_PACKET_SIZE	ALIGN(1568, EQOS_DESCRIPTOR_ALIGN)
+
+struct eqos_desc {
+	u32 des0; /* PA of buffer 1 or TSO header */
+	u32 des1; /* PA of buffer 2 with descriptor rings */
+	u32 des2; /* Length, VLAN, Timestamps, Interrupts */
+	u32 des3; /* All other flags */
+};
+
+#define EQOS_DESC3_OWN		BIT(31)
+#define EQOS_DESC3_FD		BIT(29)
+#define EQOS_DESC3_LD		BIT(28)
+#define EQOS_DESC3_BUF1V	BIT(24)
+
+#define EQOS_MDIO_ADDR(reg)		((addr << 21) & GENMASK(25, 21))
+#define EQOS_MDIO_REG(reg)		((reg << 16) & GENMASK(20, 16))
+#define EQOS_MDIO_CLK_CSR(clk_csr)	((clk_csr << 8) & GENMASK(11, 8))
+
+#define MII_BUSY		(1 << 0)
+
+static int eqos_mdio_wait_idle(struct eqos *eqos)
+{
+	u32 idle;
+	return readl_poll_timeout(&eqos->mac_regs->mdio_address, idle,
+				  !(idle & MII_BUSY), 10 * USEC_PER_MSEC);
+}
+
+static int eqos_mdio_read(struct mii_bus *bus, int addr, int reg)
+{
+	struct eqos *eqos = bus->priv;
+	u32 miiaddr;
+	int ret;
+
+	ret = eqos_mdio_wait_idle(eqos);
+	if (ret) {
+		eqos_err(eqos, "MDIO not idle at entry\n");
+		return ret;
+	}
+
+	miiaddr = readl(&eqos->mac_regs->mdio_address);
+	miiaddr &= EQOS_MDIO_ADDR_SKAP | EQOS_MDIO_ADDR_C45E;
+	miiaddr |= EQOS_MDIO_ADDR_GOC_READ << EQOS_MDIO_ADDR_GOC_SHIFT;
+
+	miiaddr |= EQOS_MDIO_CLK_CSR(eqos->ops->clk_csr);
+	miiaddr |= EQOS_MDIO_ADDR(addr) | EQOS_MDIO_REG(reg);
+	miiaddr |= MII_BUSY;
+
+	writel(miiaddr, &eqos->mac_regs->mdio_address);
+
+	udelay(eqos->ops->mdio_wait_us);
+
+	ret = eqos_mdio_wait_idle(eqos);
+	if (ret) {
+		eqos_err(eqos, "MDIO read didn't complete\n");
+		return ret;
+	}
+
+	return readl(&eqos->mac_regs->mdio_data) & 0xffff;
+}
+
+static int eqos_mdio_write(struct mii_bus *bus, int addr, int reg, u16 val)
+{
+	struct eqos *eqos = bus->priv;
+	u32 miiaddr = 0;
+	int ret;
+
+	ret = eqos_mdio_wait_idle(eqos);
+	if (ret) {
+		eqos_err(eqos, "MDIO not idle at entry\n");
+		return ret;
+	}
+
+	miiaddr = readl(&eqos->mac_regs->mdio_address);
+	miiaddr &= EQOS_MDIO_ADDR_SKAP | EQOS_MDIO_ADDR_C45E;
+	miiaddr |= EQOS_MDIO_ADDR_GOC_WRITE << EQOS_MDIO_ADDR_GOC_SHIFT;
+
+	miiaddr |= EQOS_MDIO_CLK_CSR(eqos->ops->clk_csr);
+	miiaddr |= EQOS_MDIO_ADDR(addr) | EQOS_MDIO_REG(reg);
+	miiaddr |= MII_BUSY;
+
+	writel(val, &eqos->mac_regs->mdio_data);
+	writel(addr, &eqos->mac_regs->mdio_address);
+
+	udelay(eqos->ops->mdio_wait_us);
+
+	ret = eqos_mdio_wait_idle(eqos);
+	if (ret) {
+		eqos_err(eqos, "MDIO read didn't complete\n");
+		return ret;
+	}
+
+	/* Needed as a fix for ST-Phy */
+	eqos_mdio_read(bus, addr, reg);
+	return 0;
+}
+
+
+static inline void eqos_set_full_duplex(struct eqos *eqos)
+{
+	setbits_le32(&eqos->mac_regs->config, EQOS_MAC_CONFIGURATION_DM);
+}
+
+static inline void eqos_set_half_duplex(struct eqos *eqos)
+{
+	clrbits_le32(&eqos->mac_regs->config, EQOS_MAC_CONFIGURATION_DM);
+
+	/* WAR: Flush TX queue when switching to half-duplex */
+	setbits_le32(&eqos->mtl_regs->txq0_operation_mode,
+		     EQOS_MTL_TXQ0_OPERATION_MODE_FTQ);
+}
+
+static inline void eqos_set_gmii_speed(struct eqos *eqos)
+{
+	clrbits_le32(&eqos->mac_regs->config,
+		     EQOS_MAC_CONFIGURATION_PS | EQOS_MAC_CONFIGURATION_FES);
+}
+
+static inline void eqos_set_mii_speed_100(struct eqos *eqos)
+{
+	setbits_le32(&eqos->mac_regs->config,
+		     EQOS_MAC_CONFIGURATION_PS | EQOS_MAC_CONFIGURATION_FES);
+}
+
+static inline void eqos_set_mii_speed_10(struct eqos *eqos)
+{
+	clrsetbits_le32(&eqos->mac_regs->config,
+			EQOS_MAC_CONFIGURATION_FES, EQOS_MAC_CONFIGURATION_PS);
+}
+
+void eqos_adjust_link(struct eth_device *edev)
+{
+	struct eqos *eqos = edev->priv;
+	unsigned speed = edev->phydev->speed;
+
+	if (edev->phydev->duplex)
+		eqos_set_full_duplex(eqos);
+	else
+		eqos_set_half_duplex(eqos);
+
+	switch (speed) {
+	case SPEED_1000:
+		eqos_set_gmii_speed(eqos);
+		break;
+	case SPEED_100:
+		eqos_set_mii_speed_100(eqos);
+		break;
+	case SPEED_10:
+		eqos_set_mii_speed_10(eqos);
+		break;
+	default:
+		eqos_warn(eqos, "invalid speed %d\n", speed);
+		return;
+	}
+}
+
+int eqos_get_ethaddr(struct eth_device *edev, unsigned char *mac)
+{
+	return -EOPNOTSUPP;
+}
+
+int eqos_set_ethaddr(struct eth_device *edev, const unsigned char *mac)
+{
+	struct eqos *eqos = edev->priv;
+	__le32 mac_hi, mac_lo;
+
+	memcpy(eqos->macaddr, mac, ETH_ALEN);
+
+	/* Update the MAC address */
+	memcpy(&mac_hi, &mac[4], 2);
+	memcpy(&mac_lo, &mac[0], 4);
+
+	__raw_writel(mac_hi, &eqos->mac_regs->macaddr0hi);
+	__raw_writel(mac_lo, &eqos->mac_regs->macaddr0lo);
+
+	return 0;
+}
+
+/* Get PHY out of power saving mode.  If this is needed elsewhere then
+ * consider making it part of phy-core and adding a resume method to
+ * the phy device ops.  */
+static int phy_resume(struct phy_device *phydev)
+{
+	int bmcr;
+
+	bmcr = phy_read(phydev, MII_BMCR);
+	if (bmcr < 0)
+		return bmcr;
+
+	if (bmcr & BMCR_PDOWN) {
+		bmcr &= ~BMCR_PDOWN;
+		return phy_write(phydev, MII_BMCR, bmcr);
+	}
+
+	return 0;
+}
+
+int eqos_start(struct eth_device *edev)
+{
+	struct eqos *eqos = edev->priv;
+	u32 val, tx_fifo_sz, rx_fifo_sz, tqs, rqs, pbl;
+	unsigned long last_rx_desc;
+	unsigned long rate;
+	u32 mode_set;
+	int ret;
+	int i;
+
+	setbits_le32(&eqos->dma_regs->mode, EQOS_DMA_MODE_SWR);
+
+	ret = readl_poll_timeout(&eqos->dma_regs->mode, mode_set,
+				 !(mode_set & EQOS_DMA_MODE_SWR),
+				 100 * USEC_PER_MSEC);
+	if (ret) {
+		eqos_err(eqos, "EQOS_DMA_MODE_SWR stuck: 0x%08x\n", mode_set);
+		return ret;
+	}
+
+	/* Reset above clears MAC address */
+	eqos_set_ethaddr(edev, eqos->macaddr);
+
+	/* Required for accurate time keeping with EEE counters */
+	rate = eqos->ops->get_csr_clk_rate(eqos);
+
+	val = (rate / USEC_PER_SEC) - 1; /* -1 because the data sheet says so */
+	writel(val, &eqos->mac_regs->us_tic_counter);
+
+	ret = phy_device_connect(edev, &eqos->miibus, eqos->phy_addr,
+				 eqos->ops->adjust_link, 0, eqos->interface);
+	if (ret)
+		return ret;
+
+	/* Before we reset the mac, we must insure the PHY is not powered down
+	 * as the dw controller needs all clock domains to be running, including
+	 * the PHY clock, to come out of a mac reset.  */
+	ret = phy_resume(edev->phydev);
+	if (ret)
+		return ret;
+
+	/* Configure MTL */
+
+	/* Enable Store and Forward mode for TX */
+	/* Program Tx operating mode */
+	setbits_le32(&eqos->mtl_regs->txq0_operation_mode,
+		     EQOS_MTL_TXQ0_OPERATION_MODE_TSF |
+		     (EQOS_MTL_TXQ0_OPERATION_MODE_TXQEN_ENABLED <<
+		      EQOS_MTL_TXQ0_OPERATION_MODE_TXQEN_SHIFT));
+
+	/* Transmit Queue weight */
+	writel(0x10, &eqos->mtl_regs->txq0_quantum_weight);
+
+	/* Enable Store and Forward mode for RX, since no jumbo frame */
+	setbits_le32(&eqos->mtl_regs->rxq0_operation_mode,
+		     EQOS_MTL_RXQ0_OPERATION_MODE_RSF);
+
+	/* Transmit/Receive queue fifo size; use all RAM for 1 queue */
+	val = readl(&eqos->mac_regs->hw_feature1);
+	tx_fifo_sz = (val >> EQOS_MAC_HW_FEATURE1_TXFIFOSIZE_SHIFT) &
+		EQOS_MAC_HW_FEATURE1_TXFIFOSIZE_MASK;
+	rx_fifo_sz = (val >> EQOS_MAC_HW_FEATURE1_RXFIFOSIZE_SHIFT) &
+		EQOS_MAC_HW_FEATURE1_RXFIFOSIZE_MASK;
+
+	/*
+	 * r/tx_fifo_sz is encoded as log2(n / 128). Undo that by shifting.
+	 * r/tqs is encoded as (n / 256) - 1.
+	 */
+	tqs = (128 << tx_fifo_sz) / 256 - 1;
+	rqs = (128 << rx_fifo_sz) / 256 - 1;
+
+	clrsetbits_le32(&eqos->mtl_regs->txq0_operation_mode,
+			EQOS_MTL_TXQ0_OPERATION_MODE_TQS_MASK <<
+			EQOS_MTL_TXQ0_OPERATION_MODE_TQS_SHIFT,
+			tqs << EQOS_MTL_TXQ0_OPERATION_MODE_TQS_SHIFT);
+
+	clrsetbits_le32(&eqos->mtl_regs->rxq0_operation_mode,
+			EQOS_MTL_RXQ0_OPERATION_MODE_RQS_MASK <<
+			EQOS_MTL_RXQ0_OPERATION_MODE_RQS_SHIFT,
+			rqs << EQOS_MTL_RXQ0_OPERATION_MODE_RQS_SHIFT);
+
+	/* Flow control used only if each channel gets 4KB or more FIFO */
+	if (rqs >= ((SZ_4K / 256) - 1)) {
+		u32 rfd, rfa;
+
+		setbits_le32(&eqos->mtl_regs->rxq0_operation_mode,
+			     EQOS_MTL_RXQ0_OPERATION_MODE_EHFC);
+
+		/*
+		 * Set Threshold for Activating Flow Contol space for min 2
+		 * frames ie, (1500 * 1) = 1500 bytes.
+		 *
+		 * Set Threshold for Deactivating Flow Contol for space of
+		 * min 1 frame (frame size 1500bytes) in receive fifo
+		 */
+		if (rqs == ((SZ_4K / 256) - 1)) {
+			/*
+			 * This violates the above formula because of FIFO size
+			 * limit therefore overflow may occur inspite of this.
+			 */
+			rfd = 0x3;	/* Full-3K */
+			rfa = 0x1;	/* Full-1.5K */
+		} else if (rqs == ((SZ_8K / 256) - 1)) {
+			rfd = 0x6;	/* Full-4K */
+			rfa = 0xa;	/* Full-6K */
+		} else if (rqs == ((16384 / 256) - 1)) {
+			rfd = 0x6;	/* Full-4K */
+			rfa = 0x12;	/* Full-10K */
+		} else {
+			rfd = 0x6;	/* Full-4K */
+			rfa = 0x1E;	/* Full-16K */
+		}
+
+		clrsetbits_le32(&eqos->mtl_regs->rxq0_operation_mode,
+				(EQOS_MTL_RXQ0_OPERATION_MODE_RFD_MASK <<
+				 EQOS_MTL_RXQ0_OPERATION_MODE_RFD_SHIFT) |
+				(EQOS_MTL_RXQ0_OPERATION_MODE_RFA_MASK <<
+				 EQOS_MTL_RXQ0_OPERATION_MODE_RFA_SHIFT),
+				(rfd <<
+				 EQOS_MTL_RXQ0_OPERATION_MODE_RFD_SHIFT) |
+				(rfa <<
+				 EQOS_MTL_RXQ0_OPERATION_MODE_RFA_SHIFT));
+	}
+
+	/* Configure MAC */
+
+	clrsetbits_le32(&eqos->mac_regs->rxq_ctrl0,
+			EQOS_MAC_RXQ_CTRL0_RXQ0EN_MASK <<
+			EQOS_MAC_RXQ_CTRL0_RXQ0EN_SHIFT,
+			eqos->ops->config_mac <<
+			EQOS_MAC_RXQ_CTRL0_RXQ0EN_SHIFT);
+
+	/* Set TX flow control parameters */
+	/* Set Pause Time */
+	setbits_le32(&eqos->mac_regs->q0_tx_flow_ctrl,
+		     0xffff << EQOS_MAC_Q0_TX_FLOW_CTRL_PT_SHIFT);
+	/* Assign priority for TX flow control */
+	clrbits_le32(&eqos->mac_regs->txq_prty_map0,
+		     EQOS_MAC_TXQ_PRTY_MAP0_PSTQ0_MASK <<
+		     EQOS_MAC_TXQ_PRTY_MAP0_PSTQ0_SHIFT);
+	/* Assign priority for RX flow control */
+	clrbits_le32(&eqos->mac_regs->rxq_ctrl2,
+		     EQOS_MAC_RXQ_CTRL2_PSRQ0_MASK <<
+		     EQOS_MAC_RXQ_CTRL2_PSRQ0_SHIFT);
+	/* Enable flow control */
+	setbits_le32(&eqos->mac_regs->q0_tx_flow_ctrl,
+		     EQOS_MAC_Q0_TX_FLOW_CTRL_TFE);
+	setbits_le32(&eqos->mac_regs->rx_flow_ctrl,
+		     EQOS_MAC_RX_FLOW_CTRL_RFE);
+
+	clrsetbits_le32(&eqos->mac_regs->config,
+			EQOS_MAC_CONFIGURATION_GPSLCE |
+			EQOS_MAC_CONFIGURATION_WD |
+			EQOS_MAC_CONFIGURATION_JD |
+			EQOS_MAC_CONFIGURATION_JE,
+			EQOS_MAC_CONFIGURATION_CST |
+			EQOS_MAC_CONFIGURATION_ACS);
+
+	/* Configure DMA */
+
+	/* Enable OSP mode */
+	setbits_le32(&eqos->dma_regs->ch0_tx_control,
+		     EQOS_DMA_CH0_TX_CONTROL_OSP);
+
+	/* RX buffer size. Must be a multiple of bus width */
+	clrsetbits_le32(&eqos->dma_regs->ch0_rx_control,
+			EQOS_DMA_CH0_RX_CONTROL_RBSZ_MASK <<
+			EQOS_DMA_CH0_RX_CONTROL_RBSZ_SHIFT,
+			EQOS_MAX_PACKET_SIZE <<
+			EQOS_DMA_CH0_RX_CONTROL_RBSZ_SHIFT);
+
+	setbits_le32(&eqos->dma_regs->ch0_control,
+		     EQOS_DMA_CH0_CONTROL_PBLX8);
+
+	/*
+	 * Burst length must be < 1/2 FIFO size.
+	 * FIFO size in tqs is encoded as (n / 256) - 1.
+	 * Each burst is n * 8 (PBLX8) * 16 (AXI width) == 128 bytes.
+	 * Half of n * 256 is n * 128, so pbl == tqs, modulo the -1.
+	 */
+	pbl = tqs + 1;
+	if (pbl > 32)
+		pbl = 32;
+	clrsetbits_le32(&eqos->dma_regs->ch0_tx_control,
+			EQOS_DMA_CH0_TX_CONTROL_TXPBL_MASK <<
+			EQOS_DMA_CH0_TX_CONTROL_TXPBL_SHIFT,
+			pbl << EQOS_DMA_CH0_TX_CONTROL_TXPBL_SHIFT);
+
+	clrsetbits_le32(&eqos->dma_regs->ch0_rx_control,
+			EQOS_DMA_CH0_RX_CONTROL_RXPBL_MASK <<
+			EQOS_DMA_CH0_RX_CONTROL_RXPBL_SHIFT,
+			8 << EQOS_DMA_CH0_RX_CONTROL_RXPBL_SHIFT);
+
+	/* DMA performance configuration */
+	val = (2 << EQOS_DMA_SYSBUS_MODE_RD_OSR_LMT_SHIFT) |
+		EQOS_DMA_SYSBUS_MODE_EAME | EQOS_DMA_SYSBUS_MODE_BLEN16 |
+		EQOS_DMA_SYSBUS_MODE_BLEN8 | EQOS_DMA_SYSBUS_MODE_BLEN4;
+	writel(val, &eqos->dma_regs->sysbus_mode);
+
+	/* Set up descriptors */
+
+	eqos->tx_currdescnum = eqos->rx_currdescnum = 0;
+
+	for (i = 0; i < EQOS_DESCRIPTORS_RX; i++) {
+		struct eqos_desc *rx_desc = &eqos->rx_descs[i];
+
+		writel(EQOS_DESC3_BUF1V | EQOS_DESC3_OWN, &rx_desc->des3);
+	}
+
+	writel(0, &eqos->dma_regs->ch0_txdesc_list_haddress);
+	writel((ulong)eqos->tx_descs, &eqos->dma_regs->ch0_txdesc_list_address);
+	writel(EQOS_DESCRIPTORS_TX - 1,
+	       &eqos->dma_regs->ch0_txdesc_ring_length);
+
+	writel(0, &eqos->dma_regs->ch0_rxdesc_list_haddress);
+	writel((ulong)eqos->rx_descs, &eqos->dma_regs->ch0_rxdesc_list_address);
+	writel(EQOS_DESCRIPTORS_RX - 1,
+	       &eqos->dma_regs->ch0_rxdesc_ring_length);
+
+	/* Enable everything */
+
+	setbits_le32(&eqos->mac_regs->config,
+		     EQOS_MAC_CONFIGURATION_TE | EQOS_MAC_CONFIGURATION_RE);
+
+	setbits_le32(&eqos->dma_regs->ch0_tx_control,
+		     EQOS_DMA_CH0_TX_CONTROL_ST);
+	setbits_le32(&eqos->dma_regs->ch0_rx_control,
+		     EQOS_DMA_CH0_RX_CONTROL_SR);
+
+	/* TX tail pointer not written until we need to TX a packet */
+	/*
+	 * Point RX tail pointer at last descriptor. Ideally, we'd point at the
+	 * first descriptor, implying all descriptors were available. However,
+	 * that's not distinguishable from none of the descriptors being
+	 * available.
+	 */
+	last_rx_desc = (ulong)&eqos->rx_descs[(EQOS_DESCRIPTORS_RX - 1)];
+	writel(last_rx_desc, &eqos->dma_regs->ch0_rxdesc_tail_pointer);
+
+	barrier();
+
+	eqos->started = true;
+
+	return 0;
+}
+
+void eqos_stop(struct eth_device *edev)
+{
+	struct eqos *eqos = edev->priv;
+	int i;
+
+	if (!eqos->started)
+		return;
+
+	eqos->started = false;
+
+	barrier();
+
+	/* Disable TX DMA */
+	clrbits_le32(&eqos->dma_regs->ch0_tx_control,
+		     EQOS_DMA_CH0_TX_CONTROL_ST);
+
+	/* Wait for TX all packets to drain out of MTL */
+	for (i = 0; i < 1000000; i++) {
+		u32 val = readl(&eqos->mtl_regs->txq0_debug);
+		u32 trcsts = (val >> EQOS_MTL_TXQ0_DEBUG_TRCSTS_SHIFT) &
+			EQOS_MTL_TXQ0_DEBUG_TRCSTS_MASK;
+		u32 txqsts = val & EQOS_MTL_TXQ0_DEBUG_TXQSTS;
+		if ((trcsts != 1) && (!txqsts))
+			break;
+	}
+
+	/* Turn off MAC TX and RX */
+	clrbits_le32(&eqos->mac_regs->config,
+		     EQOS_MAC_CONFIGURATION_TE | EQOS_MAC_CONFIGURATION_RE);
+
+	/* Wait for all RX packets to drain out of MTL */
+	for (i = 0; i < 1000000; i++) {
+		u32 val = readl(&eqos->mtl_regs->rxq0_debug);
+		u32 prxq = (val >> EQOS_MTL_RXQ0_DEBUG_PRXQ_SHIFT) &
+			EQOS_MTL_RXQ0_DEBUG_PRXQ_MASK;
+		u32 rxqsts = (val >> EQOS_MTL_RXQ0_DEBUG_RXQSTS_SHIFT) &
+			EQOS_MTL_RXQ0_DEBUG_RXQSTS_MASK;
+		if ((!prxq) && (!rxqsts))
+			break;
+	}
+
+	/* Turn off RX DMA */
+	clrbits_le32(&eqos->dma_regs->ch0_rx_control,
+		     EQOS_DMA_CH0_RX_CONTROL_SR);
+}
+
+static int eqos_send(struct eth_device *edev, void *packet, int length)
+{
+	struct eqos *eqos = edev->priv;
+	struct device_d *dev = &eqos->netdev.dev;
+	struct eqos_desc *tx_desc;
+	dma_addr_t dma;
+	u32 des3;
+	int ret;
+
+	tx_desc = &eqos->tx_descs[eqos->tx_currdescnum];
+	eqos->tx_currdescnum++;
+	eqos->tx_currdescnum %= EQOS_DESCRIPTORS_TX;
+
+	dma = dma_map_single(dev, packet, length, DMA_TO_DEVICE);
+	if (dma_mapping_error(dev, dma))
+		return -EFAULT;
+
+	tx_desc->des0 = (unsigned long)dma;
+	tx_desc->des1 = 0;
+	tx_desc->des2 = length;
+	/*
+	 * Make sure the compiler doesn't reorder the _OWN write below, before
+	 * the writes to the rest of the descriptor.
+	 */
+	barrier();
+
+	writel(EQOS_DESC3_OWN | EQOS_DESC3_FD | EQOS_DESC3_LD | length, &tx_desc->des3);
+	writel((ulong)(tx_desc + 1), &eqos->dma_regs->ch0_txdesc_tail_pointer);
+
+	ret = readl_poll_timeout(&tx_desc->des3, des3,
+				  !(des3 & EQOS_DESC3_OWN),
+				  100 * USEC_PER_MSEC);
+
+	dma_unmap_single(dev, dma, length, DMA_TO_DEVICE);
+
+	if (ret == -ETIMEDOUT)
+		eqos_dbg(eqos, "TX timeout\n");
+
+	return ret;
+}
+
+static int eqos_recv(struct eth_device *edev)
+{
+	struct eqos *eqos = edev->priv;
+	struct eqos_desc *rx_desc;
+	void *frame;
+	int length;
+
+	rx_desc = &eqos->rx_descs[eqos->rx_currdescnum];
+	if (readl(&rx_desc->des3) & EQOS_DESC3_OWN)
+		return 0;
+
+	frame = phys_to_virt(rx_desc->des0);
+	length = rx_desc->des3 & 0x7fff;
+
+	dma_sync_single_for_cpu((unsigned long)frame, length, DMA_FROM_DEVICE);
+	net_receive(edev, frame, length);
+	dma_sync_single_for_device((unsigned long)frame, length, DMA_FROM_DEVICE);
+
+	rx_desc->des0 = (unsigned long)frame;
+	rx_desc->des1 = 0;
+	rx_desc->des2 = 0;
+	/*
+	 * Make sure that if HW sees the _OWN write below, it will see all the
+	 * writes to the rest of the descriptor too.
+	 */
+	rx_desc->des3 |= EQOS_DESC3_BUF1V;
+	rx_desc->des3 |= EQOS_DESC3_OWN;
+	barrier();
+
+	writel((ulong)rx_desc, &eqos->dma_regs->ch0_rxdesc_tail_pointer);
+
+	eqos->rx_currdescnum++;
+	eqos->rx_currdescnum %= EQOS_DESCRIPTORS_RX;
+
+	return 0;
+}
+
+static int eqos_init_resources(struct eqos *eqos)
+{
+	struct device_d *dev = eqos->netdev.parent;
+	int ret = -ENOMEM;
+	void *descs;
+	void *p;
+	int i;
+
+	descs = dma_alloc_coherent(EQOS_DESCRIPTORS_SIZE, DMA_ADDRESS_BROKEN);
+	if (!descs)
+		goto err;
+
+	eqos->tx_descs = (struct eqos_desc *)descs;
+	eqos->rx_descs = (eqos->tx_descs + EQOS_DESCRIPTORS_TX);
+
+	p = dma_alloc(EQOS_DESCRIPTORS_RX * EQOS_MAX_PACKET_SIZE);
+	if (!p)
+		goto err_free_desc;
+
+	for (i = 0; i < EQOS_DESCRIPTORS_RX; i++) {
+		struct eqos_desc *rx_desc = &eqos->rx_descs[i];
+		dma_addr_t dma;
+
+		dma = dma_map_single(dev, p, EQOS_MAX_PACKET_SIZE, DMA_FROM_DEVICE);
+		if (dma_mapping_error(dev, dma)) {
+			ret = -EFAULT;
+			goto err_free_rx_bufs;
+		}
+
+		rx_desc->des0 = dma;
+
+		p += EQOS_MAX_PACKET_SIZE;
+	}
+
+	return 0;
+
+err_free_rx_bufs:
+	dma_free(phys_to_virt(eqos->rx_descs[0].des0));
+err_free_desc:
+	dma_free_coherent(descs, 0, EQOS_DESCRIPTORS_SIZE);
+err:
+
+	return ret;
+}
+
+static int eqos_init(struct device_d *dev, struct eqos *eqos)
+{
+	int ret;
+
+	ret = eqos_init_resources(eqos);
+	if (ret) {
+		dev_err(dev, "eqos_init_resources() failed: %s\n", strerror(-ret));
+		return ret;
+	}
+
+	if (eqos->ops->init)
+		ret = eqos->ops->init(dev, eqos);
+
+	return ret;
+}
+
+static void eqos_probe_dt(struct device_d *dev, struct eqos *eqos)
+{
+	struct device_node *child;
+
+	eqos->interface = of_get_phy_mode(dev->device_node);
+	eqos->phy_addr = -1;
+
+	/* Set MDIO bus device node, if present. */
+	for_each_child_of_node(dev->device_node, child) {
+		if (of_device_is_compatible(child, "snps,dwmac-mdio") ||
+		    (child->name && !of_node_cmp(child->name, "mdio"))) {
+			eqos->miibus.dev.device_node = child;
+			break;
+		}
+	}
+}
+
+int eqos_probe(struct device_d *dev, const struct eqos_ops *ops, void *priv)
+{
+	struct mii_bus *miibus;
+	struct resource *iores;
+	struct eqos *eqos;
+	struct eth_device *edev;
+	int ret;
+
+	eqos = xzalloc(sizeof(*eqos));
+
+	iores = dev_request_mem_resource(dev, 0);
+	if (IS_ERR(iores))
+		return PTR_ERR(iores);
+	eqos->regs = IOMEM(iores->start);
+
+	eqos->mac_regs = eqos->regs + EQOS_MAC_REGS_BASE;
+	eqos->mtl_regs = eqos->regs + EQOS_MTL_REGS_BASE;
+	eqos->dma_regs = eqos->regs + EQOS_DMA_REGS_BASE;
+	eqos->ops = ops;
+	eqos->priv = priv;
+
+	eqos_probe_dt(dev, eqos);
+
+	edev = &eqos->netdev;
+
+	dev->priv = edev->priv = eqos;
+
+	edev->parent = dev;
+	edev->open = ops->start;
+	edev->send = eqos_send;
+	edev->recv = eqos_recv;
+	edev->halt = ops->stop;
+	edev->get_ethaddr = ops->get_ethaddr;
+	edev->set_ethaddr = ops->set_ethaddr;
+
+	miibus = &eqos->miibus;
+	miibus->parent = edev->parent;
+	miibus->read = eqos_mdio_read;
+	miibus->write = eqos_mdio_write;
+	miibus->priv = eqos;
+
+	ret = eqos_init(dev, eqos);
+	if (ret)
+		return ret;
+
+	ret = mdiobus_register(miibus);
+	if (ret)
+		return ret;
+
+	return eth_register(edev);
+}
+
+void eqos_remove(struct device_d *dev)
+{
+	struct eqos *eqos = dev->priv;
+
+	mdiobus_unregister(&eqos->miibus);
+
+	dma_free(phys_to_virt(eqos->rx_descs[0].des0));
+	dma_free_coherent(eqos->tx_descs, 0, EQOS_DESCRIPTORS_SIZE);
+}
diff --git a/drivers/net/designware_eqos.h b/drivers/net/designware_eqos.h
new file mode 100644
index 000000000000..969a524c0a95
--- /dev/null
+++ b/drivers/net/designware_eqos.h
@@ -0,0 +1,84 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 Ahmad Fatoum, Pengutronix
+ */
+
+#ifndef __EQOS_H_
+#define __EQOS_H_
+
+struct eqos;
+struct eth_device;
+
+struct eqos_ops {
+	int (*init)(struct device_d *dev, struct eqos *priv);
+	int (*start)(struct eth_device *edev);
+	void (*stop)(struct eth_device *edev);
+	int (*get_ethaddr)(struct eth_device *dev, unsigned char *mac);
+	int (*set_ethaddr)(struct eth_device *edev, const unsigned char *mac);
+	void (*adjust_link)(struct eth_device *edev);
+	unsigned long (*get_csr_clk_rate)(struct eqos *);
+
+	bool enh_desc;
+	int mdio_wait_us;
+
+#define EQOS_MAC_RXQ_CTRL0_RXQ0EN_SHIFT		0
+#define EQOS_MAC_RXQ_CTRL0_RXQ0EN_MASK		3
+#define EQOS_MAC_RXQ_CTRL0_RXQ0EN_NOT_ENABLED	0
+#define EQOS_MAC_RXQ_CTRL0_RXQ0EN_ENABLED_DCB	2
+#define EQOS_MAC_RXQ_CTRL0_RXQ0EN_ENABLED_AV	1
+	unsigned clk_csr;
+
+#define EQOS_MDIO_ADDR_CR_20_35			2
+#define EQOS_MDIO_ADDR_CR_250_300		5
+#define EQOS_MDIO_ADDR_SKAP			BIT(4)
+#define EQOS_MDIO_ADDR_GOC_SHIFT		2
+#define EQOS_MDIO_ADDR_GOC_READ			3
+#define EQOS_MDIO_ADDR_GOC_WRITE		1
+#define EQOS_MDIO_ADDR_C45E			BIT(1)
+	unsigned config_mac;
+};
+
+struct eqos_desc;
+struct eqos_dma_regs;
+struct eqos_mac_regs;
+struct eqos_mtl_regs;
+
+struct eqos {
+	struct eth_device netdev;
+	struct mii_bus miibus;
+
+	u8 macaddr[6];
+
+	u32 tx_currdescnum, rx_currdescnum;
+
+	struct eqos_desc *tx_descs, *rx_descs;
+
+	void __iomem *regs;
+	struct eqos_mac_regs __iomem *mac_regs;
+	struct eqos_dma_regs __iomem *dma_regs;
+	struct eqos_mtl_regs __iomem *mtl_regs;
+
+	int phy_addr;
+	phy_interface_t interface;
+
+	const struct eqos_ops *ops;
+	void *priv;
+	bool started;
+};
+
+struct device_d;
+int eqos_probe(struct device_d *dev, const struct eqos_ops *ops, void *priv);
+void eqos_remove(struct device_d *dev);
+int eqos_reset(struct eqos *priv);
+
+int eqos_get_ethaddr(struct eth_device *edev, unsigned char *mac);
+int eqos_set_ethaddr(struct eth_device *edev, const unsigned char *mac);
+int eqos_start(struct eth_device *edev);
+void eqos_stop(struct eth_device *edev);
+void eqos_adjust_link(struct eth_device *edev);
+
+#define eqos_dbg(eqos, ...) dev_dbg(&eqos->netdev.dev, __VA_ARGS__)
+#define eqos_warn(eqos, ...) dev_warn(&eqos->netdev.dev, __VA_ARGS__)
+#define eqos_err(eqos, ...) dev_err(&eqos->netdev.dev, __VA_ARGS__)
+
+#endif
diff --git a/drivers/net/designware_stm32.c b/drivers/net/designware_stm32.c
new file mode 100644
index 000000000000..5b087ad5a371
--- /dev/null
+++ b/drivers/net/designware_stm32.c
@@ -0,0 +1,245 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2016, NVIDIA CORPORATION.
+ * Copyright (c) 2019, Ahmad Fatoum, Pengutronix
+ *
+ * Portions based on U-Boot's rtl8169.c and dwc_eth_qos.
+ */
+
+#include <common.h>
+#include <init.h>
+#include <net.h>
+#include <linux/clk.h>
+#include <mfd/syscon.h>
+
+#include "designware_eqos.h"
+
+#define SYSCFG_PMCR_ETH_CLK_SEL		BIT(16)
+#define SYSCFG_PMCR_ETH_REF_CLK_SEL	BIT(17)
+
+/*  Ethernet PHY interface selection in register SYSCFG Configuration
+ *------------------------------------------
+ * src	 |BIT(23)| BIT(22)| BIT(21)|BIT(20)|
+ *------------------------------------------
+ * MII   |   0	 |   0	  |   0    |   1   |
+ *------------------------------------------
+ * GMII  |   0	 |   0	  |   0    |   0   |
+ *------------------------------------------
+ * RGMII |   0	 |   0	  |   1	   |  n/a  |
+ *------------------------------------------
+ * RMII  |   1	 |   0	  |   0	   |  n/a  |
+ *------------------------------------------
+ */
+#define SYSCFG_PMCR_ETH_SEL_MII		BIT(20)
+#define SYSCFG_PMCR_ETH_SEL_RGMII	BIT(21)
+#define SYSCFG_PMCR_ETH_SEL_RMII	BIT(23)
+#define SYSCFG_PMCR_ETH_SEL_GMII	0
+#define SYSCFG_MCU_ETH_SEL_MII		0
+#define SYSCFG_MCU_ETH_SEL_RMII		1
+
+/* Descriptors */
+
+#define SYSCFG_MCU_ETH_MASK		BIT(23)
+#define SYSCFG_MP1_ETH_MASK		GENMASK(23, 16)
+#define SYSCFG_PMCCLRR_OFFSET		0x40
+
+struct eqos_stm32 {
+	struct clk_bulk_data *clks;
+	int num_clks;
+	struct regmap *regmap;
+	u32 mode_reg;
+	int eth_clk_sel_reg;
+	int eth_ref_clk_sel_reg;
+};
+
+static inline struct eqos_stm32 *to_stm32(struct eqos *eqos)
+{
+	return eqos->priv;
+}
+
+enum { CLK_STMMACETH, CLK_MAX_RX, CLK_MAX_TX, CLK_SYSCFG,  };
+static const struct clk_bulk_data stm32_clks[] = {
+	[CLK_STMMACETH] = { .id = "stmmaceth" },
+	[CLK_MAX_RX]    = { .id = "mac-clk-rx" },
+	[CLK_MAX_TX]    = { .id = "mac-clk-tx" },
+	[CLK_SYSCFG]    = { .id = "syscfg-clk" },
+};
+
+static unsigned long eqos_get_csr_clk_rate_stm32(struct eqos *eqos)
+{
+	return clk_get_rate(to_stm32(eqos)->clks[CLK_STMMACETH].clk);
+}
+
+static int eqos_set_mode_stm32(struct eqos_stm32 *priv, phy_interface_t interface)
+{
+	u32 val, reg = priv->mode_reg;
+	int ret;
+
+	switch (interface) {
+	case PHY_INTERFACE_MODE_MII:
+		val = SYSCFG_PMCR_ETH_SEL_MII;
+		break;
+	case PHY_INTERFACE_MODE_GMII:
+		val = SYSCFG_PMCR_ETH_SEL_GMII;
+		if (priv->eth_clk_sel_reg)
+			val |= SYSCFG_PMCR_ETH_CLK_SEL;
+		break;
+	case PHY_INTERFACE_MODE_RMII:
+		val = SYSCFG_PMCR_ETH_SEL_RMII;
+		if (priv->eth_ref_clk_sel_reg)
+			val |= SYSCFG_PMCR_ETH_REF_CLK_SEL;
+		break;
+	case PHY_INTERFACE_MODE_RGMII:
+	case PHY_INTERFACE_MODE_RGMII_ID:
+	case PHY_INTERFACE_MODE_RGMII_RXID:
+	case PHY_INTERFACE_MODE_RGMII_TXID:
+		val = SYSCFG_PMCR_ETH_SEL_RGMII;
+		if (priv->eth_clk_sel_reg)
+			val |= SYSCFG_PMCR_ETH_CLK_SEL;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* Need to update PMCCLRR (clear register) */
+	ret = regmap_write(priv->regmap, reg + SYSCFG_PMCCLRR_OFFSET,
+			   SYSCFG_MP1_ETH_MASK);
+	if (ret)
+		return -EIO;
+
+	/* Update PMCSETR (set register) */
+	regmap_update_bits(priv->regmap, reg, GENMASK(23, 16), val);
+
+	return 0;
+}
+
+static int eqos_init_stm32(struct device_d *dev, struct eqos *eqos)
+{
+	struct device_node *np = dev->device_node;
+	struct eqos_stm32 *priv = to_stm32(eqos);
+	struct clk_bulk_data *eth_ck;
+	int ret;
+
+	/* Gigabit Ethernet 125MHz clock selection. */
+	priv->eth_clk_sel_reg = of_property_read_bool(np, "st,eth-clk-sel");
+
+	/* Ethernet 50Mhz RMII clock selection */
+	priv->eth_ref_clk_sel_reg =
+		of_property_read_bool(np, "st,eth-ref-clk-sel");
+
+	priv->regmap = syscon_regmap_lookup_by_phandle(dev->device_node,
+						       "st,syscon");
+	if (IS_ERR(priv->regmap)) {
+		dev_err(dev, "Could not get st,syscon node\n");
+		return PTR_ERR(priv->regmap);
+	}
+
+	ret = of_property_read_u32_index(dev->device_node, "st,syscon",
+					 1, &priv->mode_reg);
+	if (ret) {
+		dev_err(dev, "Can't get sysconfig mode offset (%s)\n",
+			strerror(-ret));
+		return -EINVAL;
+	}
+
+	ret = eqos_set_mode_stm32(priv, eqos->interface);
+	if (ret)
+		dev_warn(dev, "Configuring syscfg failed: %s\n", strerror(-ret));
+
+	priv->num_clks = ARRAY_SIZE(stm32_clks) + 1;
+	priv->clks = xmalloc(priv->num_clks * sizeof(*priv->clks));
+	memcpy(priv->clks, stm32_clks, sizeof stm32_clks);
+
+	ret = clk_bulk_get(dev, ARRAY_SIZE(stm32_clks), priv->clks);
+	if (ret) {
+		dev_err(dev, "Failed to get clks: %s\n", strerror(-ret));
+		return ret;
+	}
+
+	eth_ck = &priv->clks[ARRAY_SIZE(stm32_clks)];
+	eth_ck->id = "eth-ck";
+	eth_ck->clk = clk_get(dev, eth_ck->id);
+	if (IS_ERR(eth_ck->clk)) {
+		priv->num_clks--;
+		dev_dbg(dev, "No phy clock provided. Continuing without.\n");
+	}
+
+	return 0;
+
+}
+
+static int eqos_start_stm32(struct eth_device *edev)
+{
+	struct eqos *eqos = edev->priv;
+	struct eqos_stm32 *priv = to_stm32(eqos);
+	int ret;
+
+	ret = clk_bulk_enable(priv->num_clks, priv->clks);
+	if (ret < 0) {
+		eqos_err(eqos, "clk_bulk_enable() failed: %s\n",
+			 strerror(-ret));
+		return ret;
+	}
+
+	udelay(10);
+
+	ret = eqos_start(edev);
+	if (ret)
+		goto err_stop_clks;
+
+	return 0;
+
+err_stop_clks:
+	clk_bulk_disable(priv->num_clks, priv->clks);
+
+	return ret;
+}
+
+static void eqos_stop_stm32(struct eth_device *edev)
+{
+	struct eqos_stm32 *priv = to_stm32(edev->priv);
+
+	clk_bulk_disable(priv->num_clks, priv->clks);
+}
+
+// todo split!
+static struct eqos_ops stm32_ops = {
+	.init = eqos_init_stm32,
+	.get_ethaddr = eqos_get_ethaddr,
+	.set_ethaddr = eqos_set_ethaddr,
+	.start = eqos_start_stm32,
+	.stop = eqos_stop_stm32,
+	.adjust_link = eqos_adjust_link,
+	.get_csr_clk_rate = eqos_get_csr_clk_rate_stm32,
+
+	.mdio_wait_us = 10 * USEC_PER_MSEC,
+	.clk_csr = EQOS_MDIO_ADDR_CR_250_300,
+	.config_mac = EQOS_MAC_RXQ_CTRL0_RXQ0EN_ENABLED_AV,
+};
+
+static int eqos_probe_stm32(struct device_d *dev)
+{
+	return eqos_probe(dev, &stm32_ops, xzalloc(sizeof(struct eqos_stm32)));
+}
+
+static void eqos_remove_stm32(struct device_d *dev)
+{
+	struct eqos_stm32 *priv = to_stm32(dev->priv);
+
+	eqos_remove(dev);
+
+	clk_bulk_put(priv->num_clks, priv->clks);
+}
+
+static const struct of_device_id eqos_stm32_ids[] = {
+	{ .compatible = "st,stm32mp1-dwmac" },
+	{ /* sentinel */ }
+};
+
+static struct driver_d eqos_stm32_driver = {
+	.name = "eqos-stm32",
+	.probe = eqos_probe_stm32,
+	.remove	= eqos_remove_stm32,
+	.of_compatible = DRV_OF_COMPAT(eqos_stm32_ids),
+};
+device_platform_driver(eqos_stm32_driver);
-- 
2.20.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH v2 4/6] net: eqos: extend Designware Ethernet QoS for Tegra 186 support
  2019-10-28 21:39 [PATCH v2 0/6] ARM: stm32mp: finalize second stage support Ahmad Fatoum
                   ` (2 preceding siblings ...)
  2019-10-28 21:39 ` [PATCH v2 3/6] net: add Designware Ethernet QoS for STM32MP Ahmad Fatoum
@ 2019-10-28 21:39 ` Ahmad Fatoum
  2019-10-28 21:39 ` [PATCH v2 5/6] ARM: stm32mp: include new drivers in defconfig Ahmad Fatoum
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: Ahmad Fatoum @ 2019-10-28 21:39 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

From: Ahmad Fatoum <a.fatoum@pengutronix.de>

The U-Boot dwc_eth_qos driver ported in the previous commit had support
for both the Tegra 186/194 and STM32MP variants of the EQOS IP.

The barebox Tegra supported doesn't include the 186, but as the driver
was nevertheless ported along with the rest, lets include it in the
source tree.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/net/Kconfig               |   6 +
 drivers/net/Makefile              |   1 +
 drivers/net/designware_tegra186.c | 347 ++++++++++++++++++++++++++++++
 3 files changed, 354 insertions(+)
 create mode 100644 drivers/net/designware_tegra186.c

diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 5b66fe84ebf6..62e522a302ad 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -104,6 +104,12 @@ config DRIVER_NET_DESIGNWARE_STM32
 	help
 	  This option enables support for the ethernet MAC on the STM32MP platforms.
 
+config DRIVER_NET_DESIGNWARE_TEGRA186
+	bool "Designware Universal MAC ethernet driver for Tegra 186 platforms"
+	select RESET_CONTROLLER
+	help
+	  This option enables support for the ethernet MAC on the Tegra186 & 194.
+
 endif
 
 config DRIVER_NET_DM9K
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 7ecf4e2851ba..656d45a868a5 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_DRIVER_NET_DESIGNWARE_GENERIC) += designware_generic.o
 obj-$(CONFIG_DRIVER_NET_DESIGNWARE_SOCFPGA) += designware_socfpga.o
 obj-$(CONFIG_DRIVER_NET_DESIGNWARE_EQOS) += designware_eqos.o
 obj-$(CONFIG_DRIVER_NET_DESIGNWARE_STM32) += designware_stm32.o
+obj-$(CONFIG_DRIVER_NET_DESIGNWARE_TEGRA186) += designware_tegra186.o
 obj-$(CONFIG_DRIVER_NET_DM9K)		+= dm9k.o
 obj-$(CONFIG_DRIVER_NET_E1000)		+= e1000/regio.o e1000/main.o e1000/eeprom.o
 obj-$(CONFIG_DRIVER_NET_ENC28J60)	+= enc28j60.o
diff --git a/drivers/net/designware_tegra186.c b/drivers/net/designware_tegra186.c
new file mode 100644
index 000000000000..58484d4095dc
--- /dev/null
+++ b/drivers/net/designware_tegra186.c
@@ -0,0 +1,347 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2016, NVIDIA CORPORATION.
+ * Copyright (c) 2019, Ahmad Fatoum, Pengutronix
+ *
+ * Portions based on U-Boot's rtl8169.c and dwc_eth_qos.
+ */
+
+#include <common.h>
+#include <init.h>
+#include <gpio.h>
+#include <of_gpio.h>
+#include <linux/clk.h>
+#include <net.h>
+#include <linux/reset.h>
+
+#include "designware_eqos.h"
+
+/* These registers are Tegra186-specific */
+#define EQOS_TEGRA186_REGS_BASE 0x8800
+struct eqos_tegra186_regs {
+	uint32_t sdmemcomppadctrl;			/* 0x8800 */
+	uint32_t auto_cal_config;			/* 0x8804 */
+	uint32_t unused_8808;				/* 0x8808 */
+	uint32_t auto_cal_status;			/* 0x880c */
+};
+
+#define EQOS_SDMEMCOMPPADCTRL_PAD_E_INPUT_OR_E_PWRD	BIT(31)
+
+#define EQOS_AUTO_CAL_CONFIG_START			BIT(31)
+#define EQOS_AUTO_CAL_CONFIG_ENABLE			BIT(29)
+
+#define EQOS_AUTO_CAL_STATUS_ACTIVE			BIT(31)
+
+struct eqos_tegra186 {
+	struct clk_bulk_data *clks;
+	int num_clks;
+	struct reset_control	*rst;
+	struct eqos_tegra186_regs __iomem *tegra186_regs;
+	int phy_reset_gpio;
+};
+
+static inline struct eqos_tegra186 *to_tegra186(struct eqos *eqos)
+{
+	return eqos->priv;
+}
+
+enum { CLK_SLAVE_BUS, CLK_MASTER_BUS, CLK_RX, CLK_PTP_REF, CLK_TX };
+static const struct clk_bulk_data tegra186_clks[] = {
+	[CLK_SLAVE_BUS]  = { .id = "slave_bus" },
+	[CLK_MASTER_BUS] = { .id = "master_bus" },
+	[CLK_RX]         = { .id = "rx" },
+	[CLK_PTP_REF]    = { .id = "ptp_ref" },
+	[CLK_TX]         = { .id = "tx" },
+};
+
+static int eqos_clks_set_rate_tegra186(struct eqos_tegra186 *priv)
+{
+	return clk_set_rate(priv->clks[CLK_PTP_REF].clk, 125 * 1000 * 1000);
+}
+
+static int eqos_reset_tegra186(struct eqos_tegra186 *priv, bool reset)
+{
+	int ret;
+
+	if (reset) {
+		reset_control_assert(priv->rst);
+		gpio_set_value(priv->phy_reset_gpio, 1);
+		return 0;
+	}
+
+	gpio_set_value(priv->phy_reset_gpio, 1);
+
+	udelay(2);
+
+	gpio_set_value(priv->phy_reset_gpio, 0);
+
+	ret = reset_control_assert(priv->rst);
+	if (ret < 0)
+		return ret;
+
+	udelay(2);
+
+	return reset_control_deassert(priv->rst);
+}
+
+static int eqos_calibrate_pads_tegra186(struct eqos *eqos)
+{
+	struct eqos_tegra186 *priv = to_tegra186(eqos);
+	u32 active;
+	int ret;
+
+	setbits_le32(&priv->tegra186_regs->sdmemcomppadctrl,
+		     EQOS_SDMEMCOMPPADCTRL_PAD_E_INPUT_OR_E_PWRD);
+
+	udelay(1);
+
+	setbits_le32(&priv->tegra186_regs->auto_cal_config,
+		     EQOS_AUTO_CAL_CONFIG_START | EQOS_AUTO_CAL_CONFIG_ENABLE);
+
+	ret = readl_poll_timeout(&priv->tegra186_regs->auto_cal_status, active,
+				 active & EQOS_AUTO_CAL_STATUS_ACTIVE,
+				 10000);
+	if (ret) {
+		eqos_err(eqos, "calibrate didn't start\n");
+		goto failed;
+	}
+
+	ret = readl_poll_timeout(&priv->tegra186_regs->auto_cal_status, active,
+				 !(active & EQOS_AUTO_CAL_STATUS_ACTIVE),
+				 10000);
+	if (ret) {
+		eqos_err(eqos, "calibrate didn't finish\n");
+		goto failed;
+	}
+
+failed:
+	clrbits_le32(&priv->tegra186_regs->sdmemcomppadctrl,
+		     EQOS_SDMEMCOMPPADCTRL_PAD_E_INPUT_OR_E_PWRD);
+
+	return ret;
+}
+
+static int eqos_calibrate_link_tegra186(struct eqos *eqos, unsigned speed)
+{
+	struct eqos_tegra186 *priv = to_tegra186(eqos);
+	int ret = 0;
+	unsigned long rate;
+	bool calibrate;
+
+	switch (speed) {
+	case SPEED_1000:
+		rate = 125 * 1000 * 1000;
+		calibrate = true;
+		break;
+	case SPEED_100:
+		rate = 25 * 1000 * 1000;
+		calibrate = true;
+		break;
+	case SPEED_10:
+		rate = 2.5 * 1000 * 1000;
+		calibrate = false;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (calibrate) {
+		ret = eqos_calibrate_pads_tegra186(eqos);
+		if (ret)
+			return ret;
+	} else {
+		clrbits_le32(&priv->tegra186_regs->auto_cal_config,
+			     EQOS_AUTO_CAL_CONFIG_ENABLE);
+	}
+
+	ret = clk_set_rate(priv->clks[CLK_TX].clk, rate);
+	if (ret < 0) {
+		eqos_err(eqos, "clk_set_rate(tx_clk, %lu) failed: %d\n", rate, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static unsigned long eqos_get_csr_clk_rate_tegra186(struct eqos *eqos)
+{
+	return clk_get_rate(to_tegra186(eqos)->clks[CLK_SLAVE_BUS].clk);
+}
+
+static int eqos_set_ethaddr_tegra186(struct eth_device *edev, const unsigned char *mac)
+{
+	struct eqos *eqos = edev->priv;
+
+	/*
+	 * This function may be called before start() or after stop(). At that
+	 * time, on at least some configurations of the EQoS HW, all clocks to
+	 * the EQoS HW block will be stopped, and a reset signal applied. If
+	 * any register access is attempted in this state, bus timeouts or CPU
+	 * hangs may occur. This check prevents that.
+	 *
+	 * A simple solution to this problem would be to not implement
+	 * write_hwaddr(), since start() always writes the MAC address into HW
+	 * anyway. However, it is desirable to implement write_hwaddr() to
+	 * support the case of SW that runs subsequent to U-Boot which expects
+	 * the MAC address to already be programmed into the EQoS registers,
+	 * which must happen irrespective of whether the U-Boot user (or
+	 * scripts) actually made use of the EQoS device, and hence
+	 * irrespective of whether start() was ever called.
+	 *
+	 * Note that this requirement by subsequent SW is not valid for
+	 * Tegra186, and is likely not valid for any non-PCI instantiation of
+	 * the EQoS HW block. This function is implemented solely as
+	 * future-proofing with the expectation the driver will eventually be
+	 * ported to some system where the expectation above is true.
+	 */
+
+	if (!eqos->started) {
+		memcpy(eqos->macaddr, mac, 6);
+		return 0;
+	}
+
+	return eqos_set_ethaddr(edev, mac);
+}
+
+static int eqos_init_tegra186(struct device_d *dev, struct eqos *eqos)
+{
+	struct eqos_tegra186 *priv = to_tegra186(eqos);
+	int phy_reset;
+	int ret;
+
+	priv->tegra186_regs = IOMEM(eqos->regs + EQOS_TEGRA186_REGS_BASE);
+
+	priv->rst = reset_control_get(dev, "eqos");
+	if (IS_ERR(priv->rst)) {
+		ret = PTR_ERR(priv->rst);
+		dev_err(dev, "reset_get_by_name(rst) failed: %s\n", strerror(-ret));
+		return ret;
+	}
+
+	phy_reset = of_get_named_gpio(dev->device_node, "phy-reset-gpios", 0);
+	if (gpio_is_valid(phy_reset)) {
+		ret = gpio_request(phy_reset, "phy-reset");
+		if (ret)
+			goto release_res;
+
+		priv->phy_reset_gpio = phy_reset;
+	}
+
+	priv->clks = xmemdup(tegra186_clks, sizeof(tegra186_clks));
+	priv->num_clks = ARRAY_SIZE(tegra186_clks);
+
+	return 0;
+
+release_res:
+	reset_control_put(priv->rst);
+	return ret;
+}
+
+static int eqos_start_tegra186(struct eth_device *edev)
+{
+	struct eqos *eqos = edev->priv;
+	struct eqos_tegra186 *priv = to_tegra186(eqos);
+	int ret;
+
+	ret = clk_bulk_enable(priv->num_clks, priv->clks);
+	if (ret < 0) {
+		eqos_err(eqos, "clk_bulk_enable() failed: %s\n", strerror(-ret));
+		return ret;
+	}
+
+	ret = eqos_clks_set_rate_tegra186(priv);
+	if (ret < 0) {
+		eqos_err(eqos, "clks_set_rate() failed: %s\n", strerror(-ret));
+		goto err;
+	}
+
+	eqos_reset_tegra186(priv, false);
+	if (ret < 0) {
+		eqos_err(eqos, "reset(0) failed: %s\n", strerror(-ret));
+		goto err_stop_clks;
+	}
+
+	udelay(10);
+
+	ret = eqos_start(edev);
+	if (ret)
+		goto err_stop_resets;
+
+	return 0;
+
+err_stop_resets:
+	eqos_reset_tegra186(priv, true);
+err_stop_clks:
+	clk_bulk_disable(priv->num_clks, priv->clks);
+err:
+	return ret;
+}
+
+
+static void eqos_stop_tegra186(struct eth_device *edev)
+{
+	struct eqos_tegra186 *priv = to_tegra186(edev->priv);
+
+	eqos_reset_tegra186(priv, true);
+
+	clk_bulk_disable(priv->num_clks, priv->clks);
+}
+
+static void eqos_adjust_link_tegra186(struct eth_device *edev)
+{
+	struct eqos *eqos = edev->priv;
+	unsigned speed = edev->phydev->speed;
+	int ret;
+
+	eqos_adjust_link(edev);
+
+	ret = eqos_calibrate_link_tegra186(eqos, speed);
+	if (ret < 0) {
+		eqos_err(eqos, "eqos_calibrate_link_tegra186() failed: %d\n", ret);
+		return;
+	}
+}
+
+static const struct eqos_ops tegra186_ops = {
+	.init = eqos_init_tegra186,
+	.get_ethaddr = eqos_get_ethaddr,
+	.set_ethaddr = eqos_set_ethaddr_tegra186,
+	.start = eqos_start_tegra186,
+	.stop = eqos_stop_tegra186,
+	.adjust_link = eqos_adjust_link_tegra186,
+	.get_csr_clk_rate = eqos_get_csr_clk_rate_tegra186,
+
+	.mdio_wait_us = 10,
+	.clk_csr = EQOS_MDIO_ADDR_CR_20_35,
+	.config_mac = EQOS_MAC_RXQ_CTRL0_RXQ0EN_ENABLED_DCB,
+};
+
+static int eqos_probe_tegra186(struct device_d *dev)
+{
+	return eqos_probe(dev, &tegra186_ops, xzalloc(sizeof(struct eqos_tegra186)));
+}
+
+static void eqos_remove_tegra186(struct device_d *dev)
+{
+	struct eqos_tegra186 *priv = to_tegra186(dev->priv);
+
+	eqos_remove(dev);
+
+	clk_bulk_put(priv->num_clks, priv->clks);
+
+	gpio_free(priv->phy_reset_gpio);
+	reset_control_put(priv->rst);
+}
+
+static const struct of_device_id eqos_tegra186_ids[] = {
+	{ .compatible = "nvidia,tegra186-eqos" },
+	{ /* sentinel */ }
+};
+
+static struct driver_d eqos_tegra186_driver = {
+	.name = "eqos-tegra186",
+	.probe = eqos_probe_tegra186,
+	.remove	= eqos_remove_tegra186,
+	.of_compatible = DRV_OF_COMPAT(eqos_tegra186_ids),
+};
+device_platform_driver(eqos_tegra186_driver);
-- 
2.20.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH v2 5/6] ARM: stm32mp: include new drivers in defconfig
  2019-10-28 21:39 [PATCH v2 0/6] ARM: stm32mp: finalize second stage support Ahmad Fatoum
                   ` (3 preceding siblings ...)
  2019-10-28 21:39 ` [PATCH v2 4/6] net: eqos: extend Designware Ethernet QoS for Tegra 186 support Ahmad Fatoum
@ 2019-10-28 21:39 ` Ahmad Fatoum
  2019-10-28 21:39 ` [PATCH v2 6/6] Documentation: boards: stm32mp: remove done TODOs Ahmad Fatoum
  2019-11-04  8:04 ` [PATCH v2 0/6] ARM: stm32mp: finalize second stage support Sascha Hauer
  6 siblings, 0 replies; 8+ messages in thread
From: Ahmad Fatoum @ 2019-10-28 21:39 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

From: Ahmad Fatoum <a.fatoum@pengutronix.de>

We've had some new drivers added for the STM32MP since the defconfig was
first added. Include the newcomers.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 arch/arm/configs/stm32mp_defconfig | 20 +++++++++++++++++---
 1 file changed, 17 insertions(+), 3 deletions(-)

diff --git a/arch/arm/configs/stm32mp_defconfig b/arch/arm/configs/stm32mp_defconfig
index 9f30bb1caa48..292d7751e112 100644
--- a/arch/arm/configs/stm32mp_defconfig
+++ b/arch/arm/configs/stm32mp_defconfig
@@ -4,7 +4,6 @@ CONFIG_THUMB2_BAREBOX=y
 CONFIG_ARM_BOARD_APPEND_ATAG=y
 CONFIG_ARM_OPTIMZED_STRING_FUNCTIONS=y
 CONFIG_ARM_UNWIND=y
-CONFIG_ARM_PSCI=y
 CONFIG_MMU=y
 CONFIG_MALLOC_SIZE=0x0
 CONFIG_MALLOC_TLSF=y
@@ -23,10 +22,12 @@ CONFIG_BLSPEC=y
 CONFIG_CONSOLE_ACTIVATE_NONE=y
 CONFIG_CONSOLE_ALLOW_COLOR=y
 CONFIG_PBL_CONSOLE=y
-CONFIG_PARTITION=y
+CONFIG_PARTITION_DISK_EFI=y
+# CONFIG_PARTITION_DISK_EFI_GPT_NO_FORCE is not set
+# CONFIG_PARTITION_DISK_EFI_GPT_COMPARE is not set
 CONFIG_DEFAULT_ENVIRONMENT_GENERIC_NEW=y
 CONFIG_RESET_SOURCE=y
-CONFIG_DEBUG_INITCALLS=y
+CONFIG_MACHINE_ID=y
 CONFIG_CMD_DMESG=y
 CONFIG_LONGHELP=y
 CONFIG_CMD_IOMEM=y
@@ -82,19 +83,31 @@ CONFIG_NET=y
 CONFIG_NET_NETCONSOLE=y
 CONFIG_OFDEVICE=y
 CONFIG_OF_BAREBOX_DRIVERS=y
+CONFIG_DRIVER_SERIAL_STM32=y
 CONFIG_DRIVER_NET_DESIGNWARE=y
 CONFIG_DRIVER_NET_DESIGNWARE_GENERIC=y
+CONFIG_DRIVER_NET_DESIGNWARE_EQOS=y
+CONFIG_DRIVER_NET_DESIGNWARE_STM32=y
 CONFIG_AT803X_PHY=y
 CONFIG_MICREL_PHY=y
+CONFIG_REALTEK_PHY=y
 # CONFIG_SPI is not set
+CONFIG_I2C=y
+CONFIG_I2C_STM32=y
+CONFIG_MCI=y
+CONFIG_MCI_STARTUP=y
+CONFIG_MCI_STM32_SDMMC2=y
 CONFIG_LED=y
 CONFIG_LED_GPIO=y
 CONFIG_LED_GPIO_OF=y
 CONFIG_WATCHDOG=y
 CONFIG_WATCHDOG_POLLER=y
 CONFIG_STM32_IWDG_WATCHDOG=y
+CONFIG_NVMEM=y
+CONFIG_STM32_BSEC=y
 CONFIG_REGULATOR=y
 CONFIG_REGULATOR_FIXED=y
+CONFIG_RESET_STM32=y
 CONFIG_FS_EXT4=y
 CONFIG_FS_TFTP=y
 CONFIG_FS_NFS=y
@@ -103,3 +116,4 @@ CONFIG_FS_FAT_WRITE=y
 CONFIG_FS_FAT_LFN=y
 CONFIG_ZLIB=y
 CONFIG_CRC8=y
+CONFIG_DIGEST_SHA1_ARM=y
-- 
2.20.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH v2 6/6] Documentation: boards: stm32mp: remove done TODOs
  2019-10-28 21:39 [PATCH v2 0/6] ARM: stm32mp: finalize second stage support Ahmad Fatoum
                   ` (4 preceding siblings ...)
  2019-10-28 21:39 ` [PATCH v2 5/6] ARM: stm32mp: include new drivers in defconfig Ahmad Fatoum
@ 2019-10-28 21:39 ` Ahmad Fatoum
  2019-11-04  8:04 ` [PATCH v2 0/6] ARM: stm32mp: finalize second stage support Sascha Hauer
  6 siblings, 0 replies; 8+ messages in thread
From: Ahmad Fatoum @ 2019-10-28 21:39 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

From: Ahmad Fatoum <a.fatoum@pengutronix.de>

With SD/MMC controller and Ethernet MAC support merged, we now have
usable STM32MP support. Remove the text that suggested otherwise.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 Documentation/boards/stm32mp.rst | 11 -----------
 1 file changed, 11 deletions(-)

diff --git a/Documentation/boards/stm32mp.rst b/Documentation/boards/stm32mp.rst
index 774ede6e560f..d4429aae2765 100644
--- a/Documentation/boards/stm32mp.rst
+++ b/Documentation/boards/stm32mp.rst
@@ -1,11 +1,6 @@
 STMicroelectronics STM32MP
 ==========================
 
-.. note::
-
-  Support for the STM32MP architecure in barebox is still in progress.
-  Bootstrapping an OS from mainline barebox is not yet supported.
-
 The STM32MP is a line of 32-bit ARM SoCs. They reuse peripherals of the
 STM32 line of microcontrollers and can have a STM32 MCU embedded as co-processor
 as well.
@@ -67,9 +62,3 @@ An appropriate image for the boot media can be generated with following
   }
 
 Image can then be flashed on e.g. a SD-Card.
-
-TODO
-----
-
-* Extend barebox MMCI support to support the SDMMC2
-* Extend barebox DesignWare MAC support to support the stmmac
-- 
2.20.1


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v2 0/6] ARM: stm32mp: finalize second stage support
  2019-10-28 21:39 [PATCH v2 0/6] ARM: stm32mp: finalize second stage support Ahmad Fatoum
                   ` (5 preceding siblings ...)
  2019-10-28 21:39 ` [PATCH v2 6/6] Documentation: boards: stm32mp: remove done TODOs Ahmad Fatoum
@ 2019-11-04  8:04 ` Sascha Hauer
  6 siblings, 0 replies; 8+ messages in thread
From: Sascha Hauer @ 2019-11-04  8:04 UTC (permalink / raw)
  To: Ahmad Fatoum; +Cc: barebox

On Mon, Oct 28, 2019 at 10:39:37PM +0100, Ahmad Fatoum wrote:
> Add barebox update handler and Ethernet driver.
> 
> v1 -> v2:
>  - Split off patch to override unsupported device tree specified
>    bus-widths
>  - Delete fixing the frequency to 208MHz. I can no longer reproduce
>    120MHz to trigger timeouts
>  - Last 4 patches are new
> 
> Ahmad Fatoum (6):
>   mci: stm32_sdmmc2: parse generic MCI oftree properties
>   ARM: stm32mp: dk2: add barebox SD-Card update handler
>   net: add Designware Ethernet QoS for STM32MP
>   net: eqos: extend Designware Ethernet QoS for Tegra 186 support
>   ARM: stm32mp: include new drivers in defconfig
>   Documentation: boards: stm32mp: remove done TODOs

Applied, thanks

Sascha

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2019-11-04  8:04 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-10-28 21:39 [PATCH v2 0/6] ARM: stm32mp: finalize second stage support Ahmad Fatoum
2019-10-28 21:39 ` [PATCH v2 1/6] mci: stm32_sdmmc2: parse generic MCI oftree properties Ahmad Fatoum
2019-10-28 21:39 ` [PATCH v2 2/6] ARM: stm32mp: dk2: add barebox SD-Card update handler Ahmad Fatoum
2019-10-28 21:39 ` [PATCH v2 3/6] net: add Designware Ethernet QoS for STM32MP Ahmad Fatoum
2019-10-28 21:39 ` [PATCH v2 4/6] net: eqos: extend Designware Ethernet QoS for Tegra 186 support Ahmad Fatoum
2019-10-28 21:39 ` [PATCH v2 5/6] ARM: stm32mp: include new drivers in defconfig Ahmad Fatoum
2019-10-28 21:39 ` [PATCH v2 6/6] Documentation: boards: stm32mp: remove done TODOs Ahmad Fatoum
2019-11-04  8:04 ` [PATCH v2 0/6] ARM: stm32mp: finalize second stage support Sascha Hauer

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox