diff --git a/drivers/net/ethernet/stmicro/stmmac/Kconfig b/drivers/net/ethernet/stmicro/stmmac/Kconfig index 2ad3a77410c1..c5c0c64f01e6 100644 --- a/drivers/net/ethernet/stmicro/stmmac/Kconfig +++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig @@ -168,11 +168,21 @@ config DWMAC_ROCKCHIP depends on OF && (ARCH_ROCKCHIP || COMPILE_TEST) select MFD_SYSCON help - Support for Ethernet controller on Rockchip RK3288 SoC. + Support for Ethernet controller on Rockchip SoC. - This selects the Rockchip RK3288 SoC glue layer support for + This selects the Rockchip SoC glue layer support for the stmmac device driver. +config DWMAC_ROCKCHIP_SERIAL_MAC + bool "Generate static ethernet mac address from system serial number" + depends on DWMAC_ROCKCHIP + default n + help + Generate consistent MAC address from system serial number. + + This selects uses the system serial number to generate a + fixed Ethernet Mac address instead of a random one. + config DWMAC_ROCKCHIP_TOOL bool "Rockchip dwmac tool support" depends on DWMAC_ROCKCHIP diff --git a/drivers/net/ethernet/stmicro/stmmac/Makefile b/drivers/net/ethernet/stmicro/stmmac/Makefile index 29284f924611..2a9e73c5cfc5 100644 --- a/drivers/net/ethernet/stmicro/stmmac/Makefile +++ b/drivers/net/ethernet/stmicro/stmmac/Makefile @@ -47,3 +47,5 @@ obj-$(CONFIG_STMMAC_PCI) += stmmac-pci.o obj-$(CONFIG_DWMAC_INTEL) += dwmac-intel.o obj-$(CONFIG_DWMAC_LOONGSON) += dwmac-loongson.o stmmac-pci-objs:= stmmac_pci.o + +ccflags-$(CONFIG_DWMAC_ROCKCHIP_SERIAL_MAC) += -DDWMAC_ROCKCHIP_SERIAL_MAC diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c index 729740e02e2d..5d36d81f40cc 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later /** - * DOC: dwmac-rk.c - Rockchip RK3288 DWMAC specific glue layer + * DOC: dwmac-rk.c - Rockchip DWMAC specific glue layer * * Copyright (C) 2014 Chen-Zhi (Roger Chen) * @@ -28,6 +28,9 @@ #include #include "stmmac_platform.h" #include "dwmac-rk-tool.h" +#ifdef DWMAC_ROCKCHIP_SERIAL_MAC +#include +#endif #define MAX_ETH 2 @@ -3254,45 +3257,184 @@ int dwmac_rk_get_phy_interface(struct stmmac_priv *priv) } EXPORT_SYMBOL(dwmac_rk_get_phy_interface); +/** + * rk_generate_fallback_mac - Generate a fallback MAC address + * @addr: Buffer to store the MAC address (6 bytes) + * @iface_id: Interface identifier + * + * Generate a random MAC address with a local-admin OUI as fallback. + * Always returns a valid MAC address. + */ +static void rk_generate_fallback_mac(u8 *addr, int iface_id) +{ + /* Generate a random MAC address */ + eth_random_addr(addr); + + /* Ensure the address is valid */ + if (!is_valid_ether_addr(addr)) { + eth_zero_addr(addr); + /* Set a fallback locally administered address */ + addr[0] = 0x02; /* Locally administered bit is set */ + addr[1] = 0x00; + addr[2] = 0xa4; + addr[3] = 0xff; + addr[4] = iface_id & 0xff; + addr[5] = 0xfe; + } +} + +#ifdef DWMAC_ROCKCHIP_SERIAL_MAC +/* + * rk_get_mac_from_serial - Generate MAC address from system serial number + * @addr: Buffer to store the MAC address (6 bytes) + * @iface_id: Interface identifier + * + * Return: 0 on success, negative error code on failure + */ +static int rk_get_mac_from_serial(u8 *addr, int iface_id) +{ + unsigned long long serial; + + /* Check the validity of the serial number */ + if (system_serial_low == 0 && system_serial_high == 0) + return -ENODEV; + + /* Combine the 64-bit serial number */ + serial = ((unsigned long long)system_serial_high << 32) | system_serial_low; + + /* Validate the serial number (optional but recommended) */ + if (serial == 0 || serial == 0xFFFFFFFFFFFFFFFFULL) + return -EINVAL; + + /* Generate OUI: 02:00:a4 (locally administered address) */ + addr[0] = 0x02; /* Locally administered bit is set */ + addr[1] = 0x00; + addr[2] = 0xa4; + + /* Use parts of the serial number */ + addr[3] = 0x10 + ((serial >> 40) & 0xff); + addr[4] = (serial >> 32) & 0xff; + addr[5] = ((serial >> 24) & 0xff) + iface_id; + + return 0; +} +#endif /* DWMAC_ROCKCHIP_SERIAL_MAC */ + +/* + * rk_set_mac_address - Set up MAC address for an interface + * @addr: Buffer to store the MAC address (6 bytes) + * @iface_id: Interface identifier + * + * This function attempts to generate a deterministic MAC address from the + * system serial number if DWMAC_ROCKCHIP_SERIAL_MAC is enabled. If that fails + * or the feature is disabled, it falls back to a random MAC address. + * + * Return: Always returns 0 (success), as we guarantee a valid address. + */ +static int rk_set_mac_address(u8 *addr, int iface_id) +{ +#ifdef DWMAC_ROCKCHIP_SERIAL_MAC + int ret; + + /* Attempt to generate MAC from serial number */ + ret = rk_get_mac_from_serial(addr, iface_id); + if (ret == 0 && is_valid_ether_addr(addr)) { + return 0; /* Success, return immediately */ + } + + /* Fall back to a random address on failure */ + printk_once(KERN_WARNING "dwc-rockchip: Serial MAC generation failed, using random for interface %d\n", + iface_id); +#endif + + /* Generate fallback MAC address */ + rk_generate_fallback_mac(addr, iface_id); + + return 0; +} + static void rk_get_eth_addr(void *priv, unsigned char *addr) { - struct rk_priv_data *bsp_priv = priv; - struct device *dev = &bsp_priv->pdev->dev; - unsigned char ethaddr[ETH_ALEN * MAX_ETH] = {0}; - int ret, id = bsp_priv->id; + struct rk_priv_data *bsp_priv = priv; + struct device *dev = &bsp_priv->pdev->dev; + unsigned char ethaddr[ETH_ALEN * MAX_ETH] = {0}; + int ret, id = bsp_priv->id; - if (is_valid_ether_addr(addr)) - goto out; + /* Check if the address is already valid */ + if (is_valid_ether_addr(addr)) { + dev_info(dev, "%s: MAC address already set: %pM\n", __func__, addr); + return; + } - if (id < 0 || id >= MAX_ETH) { - dev_err(dev, "%s: Invalid ethernet bus id %d\n", __func__, id); - return; - } + if (id < 0 || id >= MAX_ETH) { + dev_err(dev, "%s: Invalid ethernet bus id %d\n", __func__, id); + /* Still try to set a MAC address even with invalid ID */ + id = 0; + } - ret = rk_vendor_read(LAN_MAC_ID, ethaddr, ETH_ALEN * MAX_ETH); - if (ret <= 0 || - !is_valid_ether_addr(ðaddr[id * ETH_ALEN])) { - dev_err(dev, "%s: rk_vendor_read eth mac address failed (%d)\n", - __func__, ret); - eth_random_addr(ðaddr[id * ETH_ALEN]); - memcpy(addr, ðaddr[id * ETH_ALEN], ETH_ALEN); - dev_err(dev, "%s: generate random eth mac address: %pM\n", __func__, addr); +#ifdef DWMAC_ROCKCHIP_SERIAL_MAC + /* Try to generate MAC from system serial number first */ + dev_info(dev, "%s: Attempting to generate MAC from system serial number for interface %d\n", + __func__, id); + + if (rk_set_mac_address(addr, id) == 0 && is_valid_ether_addr(addr)) { + dev_info(dev, "%s: Generated MAC from serial: %pM\n", __func__, addr); + + /* Store the generated MAC address to vendor storage for persistence */ + ret = rk_vendor_read(LAN_MAC_ID, ethaddr, ETH_ALEN * MAX_ETH); + if (ret < 0) { + /* Initialize the buffer if read failed */ + memset(ethaddr, 0, ETH_ALEN * MAX_ETH); + } + + /* Copy our generated MAC to the appropriate position */ + memcpy(ðaddr[id * ETH_ALEN], addr, ETH_ALEN); + + /* Write back to vendor storage */ + ret = rk_vendor_write(LAN_MAC_ID, ethaddr, ETH_ALEN * MAX_ETH); + if (ret != 0) { + dev_warn(dev, "%s: Failed to store MAC to vendor storage (%d)\n", + __func__, ret); + } else { + dev_info(dev, "%s: Stored generated MAC to vendor storage\n", __func__); + } + return; + } + + dev_warn(dev, "%s: Serial-based MAC generation failed for interface %d, falling back to vendor storage\n", + __func__, id); +#endif /* DWMAC_ROCKCHIP_SERIAL_MAC */ - ret = rk_vendor_write(LAN_MAC_ID, ethaddr, ETH_ALEN * MAX_ETH); - if (ret != 0) - dev_err(dev, "%s: rk_vendor_write eth mac address failed (%d)\n", - __func__, ret); + /* Fallback: try to read from vendor storage */ + ret = rk_vendor_read(LAN_MAC_ID, ethaddr, ETH_ALEN * MAX_ETH); + if (ret > 0 && is_valid_ether_addr(ðaddr[id * ETH_ALEN])) { + memcpy(addr, ðaddr[id * ETH_ALEN], ETH_ALEN); + dev_info(dev, "%s: Retrieved MAC from vendor storage: %pM\n", + __func__, addr); + return; + } - ret = rk_vendor_read(LAN_MAC_ID, ethaddr, ETH_ALEN * MAX_ETH); - if (ret != ETH_ALEN * MAX_ETH) - dev_err(dev, "%s: id: %d rk_vendor_read eth mac address failed (%d)\n", - __func__, id, ret); - } else { - memcpy(addr, ðaddr[id * ETH_ALEN], ETH_ALEN); - } - -out: - dev_err(dev, "%s: mac address: %pM\n", __func__, addr); + /* Last resort: generate fallback MAC */ + dev_warn(dev, "%s: No valid MAC found, generating fallback for interface %d\n", + __func__, id); + + rk_generate_fallback_mac(addr, id); + + /* Store the fallback MAC to vendor storage */ + if (ret < 0) { + /* Initialize the buffer if read failed */ + memset(ethaddr, 0, ETH_ALEN * MAX_ETH); + } + memcpy(ðaddr[id * ETH_ALEN], addr, ETH_ALEN); + + ret = rk_vendor_write(LAN_MAC_ID, ethaddr, ETH_ALEN * MAX_ETH); + if (ret != 0) { + dev_err(dev, "%s: Failed to store fallback MAC to vendor storage (%d)\n", + __func__, ret); + } else { + dev_info(dev, "%s: Stored fallback MAC to vendor storage: %pM\n", + __func__, addr); + } } static int rk_gmac_probe(struct platform_device *pdev) @@ -3483,5 +3625,5 @@ static struct platform_driver rk_gmac_dwmac_driver = { module_platform_driver(rk_gmac_dwmac_driver); MODULE_AUTHOR("Chen-Zhi (Roger Chen) "); -MODULE_DESCRIPTION("Rockchip RK3288 DWMAC specific glue layer"); +MODULE_DESCRIPTION("Rockchip DWMAC specific glue layer"); MODULE_LICENSE("GPL");