summaryrefslogtreecommitdiffstats
path: root/drivers/net/designware_stm32.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/designware_stm32.c')
-rw-r--r--drivers/net/designware_stm32.c245
1 files changed, 245 insertions, 0 deletions
diff --git a/drivers/net/designware_stm32.c b/drivers/net/designware_stm32.c
new file mode 100644
index 0000000000..5b087ad5a3
--- /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);