|
|
|
|
@@ -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 <soc/rockchip/rockchip_csu.h>
|
|
|
|
|
#include "stmmac_platform.h"
|
|
|
|
|
#include "dwmac-rk-tool.h"
|
|
|
|
|
#ifdef DWMAC_ROCKCHIP_SERIAL_MAC
|
|
|
|
|
#include <asm/system_info.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#define MAX_ETH 2
|
|
|
|
|
|
|
|
|
|
@@ -3254,6 +3257,102 @@ 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;
|
|
|
|
|
@@ -3261,38 +3360,81 @@ static void rk_get_eth_addr(void *priv, unsigned char *addr)
|
|
|
|
|
unsigned char ethaddr[ETH_ALEN * MAX_ETH] = {0};
|
|
|
|
|
int ret, id = bsp_priv->id;
|
|
|
|
|
|
|
|
|
|
if (is_valid_ether_addr(addr))
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
if (id < 0 || id >= MAX_ETH) {
|
|
|
|
|
dev_err(dev, "%s: Invalid ethernet bus id %d\n", __func__, id);
|
|
|
|
|
/* 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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
dev_err(dev, "%s: 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 */
|
|
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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) <roger.chen@rock-chips.com>");
|
|
|
|
|
MODULE_DESCRIPTION("Rockchip RK3288 DWMAC specific glue layer");
|
|
|
|
|
MODULE_DESCRIPTION("Rockchip DWMAC specific glue layer");
|
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
|
|