/* * 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 #include #include #include #ifdef CONFIG_ARCH_MXC_ARM64 #include #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); }