summaryrefslogtreecommitdiff
path: root/drivers/net/ethernet/freescale/fec_fixup.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/ethernet/freescale/fec_fixup.c')
-rw-r--r--drivers/net/ethernet/freescale/fec_fixup.c277
1 files changed, 277 insertions, 0 deletions
diff --git a/drivers/net/ethernet/freescale/fec_fixup.c b/drivers/net/ethernet/freescale/fec_fixup.c
new file mode 100644
index 000000000000..1781019d77ae
--- /dev/null
+++ b/drivers/net/ethernet/freescale/fec_fixup.c
@@ -0,0 +1,277 @@
+/*
+ * Copyright 2017 NXP
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/netdevice.h>
+#include <linux/phy.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#ifdef CONFIG_ARCH_MXC_ARM64
+#include <soc/imx8/sc/sci.h>
+#endif
+#include "fec.h"
+
+#define PHY_ID_AR8031 0x004dd074
+
+#define IMX8QM_FUSE_MAC0_WORD0 452
+#define IMX8QM_FUSE_MAC0_WORD1 453
+#define IMX8QM_FUSE_MAC1_WORD0 454
+#define IMX8QM_FUSE_MAC1_WORD1 455
+#define IMX8QXP_FUSE_MAC0_WORD0 708
+#define IMX8QXP_FUSE_MAC0_WORD1 709
+#define IMX8QXP_FUSE_MAC1_WORD0 710
+#define IMX8QXP_FUSE_MAC1_WORD1 711
+#define IMX8M_OCOTP_MAC_ADDR0_OFF 0x640
+#define IMX8M_OCOTP_MAC_ADDR1_OFF 0x650
+
+enum imx_soc_type {
+ IMX8QM_FUSE = 0,
+ IMX8QXP_FUSE,
+};
+
+struct imx_fuse_mac_addr {
+ u32 fuse_mac0_word0;
+ u32 fuse_mac0_word1;
+ u32 fuse_mac1_word0;
+ u32 fuse_mac1_word1;
+};
+
+static struct imx_fuse_mac_addr imx8_fuse_mapping[] = {
+ {
+ .fuse_mac0_word0 = IMX8QM_FUSE_MAC0_WORD0,
+ .fuse_mac0_word1 = IMX8QM_FUSE_MAC0_WORD1,
+ .fuse_mac1_word0 = IMX8QM_FUSE_MAC1_WORD0,
+ .fuse_mac1_word1 = IMX8QM_FUSE_MAC1_WORD1,
+ }, {
+ .fuse_mac0_word0 = IMX8QXP_FUSE_MAC0_WORD0,
+ .fuse_mac0_word1 = IMX8QXP_FUSE_MAC0_WORD1,
+ .fuse_mac1_word0 = IMX8QXP_FUSE_MAC1_WORD0,
+ .fuse_mac1_word1 = IMX8QXP_FUSE_MAC1_WORD1,
+ }, {
+ /* sentinel */
+ }
+};
+
+static int ar8031_phy_fixup(struct phy_device *dev)
+{
+ u16 val;
+
+ /* Set RGMII IO voltage to 1.8V */
+ phy_write(dev, 0x1d, 0x1f);
+ phy_write(dev, 0x1e, 0x8);
+
+ /* Disable phy AR8031 SmartEEE function */
+ phy_write(dev, 0xd, 0x3);
+ phy_write(dev, 0xe, 0x805d);
+ phy_write(dev, 0xd, 0x4003);
+ val = phy_read(dev, 0xe);
+ val &= ~(0x1 << 8);
+ phy_write(dev, 0xe, val);
+
+ /* Introduce tx clock delay */
+ phy_write(dev, 0x1d, 0x5);
+ phy_write(dev, 0x1e, 0x100);
+
+ return 0;
+}
+
+void fec_enet_register_fixup(struct net_device *ndev)
+{
+ struct fec_enet_private *fep = netdev_priv(ndev);
+ int err;
+
+ if (!IS_BUILTIN(CONFIG_PHYLIB))
+ return;
+
+ if (fep->fixups & FEC_QUIRK_AR8031_FIXUP) {
+ static int ar8031_registered = 0;
+
+ if (ar8031_registered)
+ return;
+ err = phy_register_fixup_for_uid(PHY_ID_AR8031, 0xffffffef,
+ ar8031_phy_fixup);
+ if (err)
+ netdev_info(ndev, "Cannot register PHY board fixup\n");
+ ar8031_registered = 1;
+ }
+}
+
+int of_fec_enet_parse_fixup(struct device_node *np)
+{
+ int fixups = 0;
+
+ if (of_get_property(np, "fsl,ar8031-phy-fixup", NULL))
+ fixups |= FEC_QUIRK_AR8031_FIXUP;
+
+ return fixups;
+}
+
+static void imx8mq_get_mac_from_fuse(int dev_id, unsigned char *mac)
+{
+ struct device_node *ocotp_np;
+ void __iomem *base;
+ u32 value;
+
+ ocotp_np = of_find_compatible_node(NULL, NULL, "fsl,imx7d-ocotp");
+ if (!ocotp_np) {
+ pr_warn("failed to find ocotp node\n");
+ return;
+ }
+
+ base = of_iomap(ocotp_np, 0);
+ if (!base) {
+ pr_warn("failed to map ocotp\n");
+ goto put_ocotp_node;
+ }
+
+ value = readl_relaxed(base + IMX8M_OCOTP_MAC_ADDR1_OFF);
+ mac[0] = (value >> 8);
+ mac[1] = value;
+
+ value = readl_relaxed(base + IMX8M_OCOTP_MAC_ADDR0_OFF);
+ mac[2] = value >> 24;
+ mac[3] = value >> 16;
+ mac[4] = value >> 8;
+ mac[5] = value;
+
+put_ocotp_node:
+ of_node_put(ocotp_np);
+}
+
+#ifdef CONFIG_ARCH_MXC_ARM64
+static void imx8qm_get_mac_from_fuse(int dev_id, unsigned char *mac,
+ struct imx_fuse_mac_addr *fuse_mapping)
+{
+ uint32_t mu_id;
+ sc_ipc_t ipc_handle;
+ sc_err_t sc_err = SC_ERR_NONE;
+ uint32_t val1 = 0, val2 = 0;
+ uint32_t word1, word2;
+
+ sc_err = sc_ipc_getMuID(&mu_id);
+ if (sc_err != SC_ERR_NONE) {
+ pr_err("FEC MAC fuse: Get MU ID failed\n");
+ return;
+ }
+
+ sc_err = sc_ipc_open(&ipc_handle, mu_id);
+ if (sc_err != SC_ERR_NONE) {
+ pr_err("FEC MAC fuse: Open MU channel failed\n");
+ return;
+ }
+
+ if (dev_id == 0) {
+ word1 = fuse_mapping->fuse_mac0_word0;
+ word2 = fuse_mapping->fuse_mac0_word1;
+ } else {
+ word1 = fuse_mapping->fuse_mac1_word0;
+ word2 = fuse_mapping->fuse_mac1_word1;
+ }
+
+ sc_err = sc_misc_otp_fuse_read(ipc_handle, word1, &val1);
+ if (sc_err != SC_ERR_NONE) {
+ pr_err("FEC MAC fuse %d read error: %d\n", word1, sc_err);
+ sc_ipc_close(ipc_handle);
+ return;
+ }
+
+ sc_err = sc_misc_otp_fuse_read(ipc_handle, word2, &val2);
+ if (sc_err != SC_ERR_NONE) {
+ pr_err("FEC MAC fuse %d read error: %d\n", word2, sc_err);
+ sc_ipc_close(ipc_handle);
+ return;
+ }
+
+ mac[0] = val1;
+ mac[1] = val1 >> 8;
+ mac[2] = val1 >> 16;
+ mac[3] = val1 >> 24;
+ mac[4] = val2;
+ mac[5] = val2 >> 8;
+
+ sc_ipc_close(ipc_handle);
+}
+
+static void imx8qm_ipg_stop_enable(int dev_id, bool enabled)
+{
+ uint32_t mu_id;
+ sc_ipc_t ipc_handle;
+ sc_err_t sc_err = SC_ERR_NONE;
+ uint32_t rsrc_id, val;
+
+ sc_err = sc_ipc_getMuID(&mu_id);
+ if (sc_err != SC_ERR_NONE) {
+ pr_err("FEC ipg stop: Get MU ID failed\n");
+ return;
+ }
+
+ sc_err = sc_ipc_open(&ipc_handle, mu_id);
+ if (sc_err != SC_ERR_NONE) {
+ pr_err("FEC ipg stop: Open MU channel failed\n");
+ return;
+ }
+
+ if (dev_id == 0)
+ rsrc_id = SC_R_ENET_0;
+ else
+ rsrc_id = SC_R_ENET_1;
+
+ val = enabled ? 1 : 0;
+ sc_err = sc_misc_set_control(ipc_handle, rsrc_id, SC_C_IPG_STOP, val);
+ if (sc_err != SC_ERR_NONE)
+ pr_err("FEC ipg stop set error: %d\n", sc_err);
+
+ sc_ipc_close(ipc_handle);
+}
+#else
+static void imx8qm_get_mac_from_fuse(int dev_id, unsigned char *mac,
+ struct imx_fuse_mac_addr *fuse_mapping) {}
+static void imx8qm_ipg_stop_enable(int dev_id, bool enabled) {}
+#endif
+
+void fec_enet_get_mac_from_fuse(struct device_node *np, unsigned char *mac)
+{
+ int idx;
+
+ if (!np)
+ return;
+
+ idx = of_alias_get_id(np, "ethernet");
+ if (idx < 0)
+ idx = 0;
+
+ if (of_machine_is_compatible("fsl,imx8qm"))
+ imx8qm_get_mac_from_fuse(idx, mac,
+ &imx8_fuse_mapping[IMX8QM_FUSE]);
+ else if (of_machine_is_compatible("fsl,imx8qxp"))
+ imx8qm_get_mac_from_fuse(idx, mac,
+ &imx8_fuse_mapping[IMX8QXP_FUSE]);
+ else if (of_machine_is_compatible("fsl,imx8mq"))
+ imx8mq_get_mac_from_fuse(idx, mac);
+}
+
+void fec_enet_ipg_stop_misc_set(struct device_node *np, bool enabled)
+{
+ int idx;
+
+ if (!np)
+ return;
+
+ idx = of_alias_get_id(np, "ethernet");
+ if (idx < 0)
+ idx = 0;
+
+ if (of_machine_is_compatible("fsl,imx8qm") ||
+ of_machine_is_compatible("fsl,imx8qxp"))
+ imx8qm_ipg_stop_enable(idx, enabled);
+}