18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * OF helpers for network devices. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Initially copied out of arch/powerpc/kernel/prom_parse.c 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 88c2ecf20Sopenharmony_ci#include <linux/kernel.h> 98c2ecf20Sopenharmony_ci#include <linux/of_net.h> 108c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 118c2ecf20Sopenharmony_ci#include <linux/phy.h> 128c2ecf20Sopenharmony_ci#include <linux/export.h> 138c2ecf20Sopenharmony_ci#include <linux/device.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci/** 168c2ecf20Sopenharmony_ci * of_get_phy_mode - Get phy mode for given device_node 178c2ecf20Sopenharmony_ci * @np: Pointer to the given device_node 188c2ecf20Sopenharmony_ci * @interface: Pointer to the result 198c2ecf20Sopenharmony_ci * 208c2ecf20Sopenharmony_ci * The function gets phy interface string from property 'phy-mode' or 218c2ecf20Sopenharmony_ci * 'phy-connection-type'. The index in phy_modes table is set in 228c2ecf20Sopenharmony_ci * interface and 0 returned. In case of error interface is set to 238c2ecf20Sopenharmony_ci * PHY_INTERFACE_MODE_NA and an errno is returned, e.g. -ENODEV. 248c2ecf20Sopenharmony_ci */ 258c2ecf20Sopenharmony_ciint of_get_phy_mode(struct device_node *np, phy_interface_t *interface) 268c2ecf20Sopenharmony_ci{ 278c2ecf20Sopenharmony_ci const char *pm; 288c2ecf20Sopenharmony_ci int err, i; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci *interface = PHY_INTERFACE_MODE_NA; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci err = of_property_read_string(np, "phy-mode", &pm); 338c2ecf20Sopenharmony_ci if (err < 0) 348c2ecf20Sopenharmony_ci err = of_property_read_string(np, "phy-connection-type", &pm); 358c2ecf20Sopenharmony_ci if (err < 0) 368c2ecf20Sopenharmony_ci return err; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci for (i = 0; i < PHY_INTERFACE_MODE_MAX; i++) 398c2ecf20Sopenharmony_ci if (!strcasecmp(pm, phy_modes(i))) { 408c2ecf20Sopenharmony_ci *interface = i; 418c2ecf20Sopenharmony_ci return 0; 428c2ecf20Sopenharmony_ci } 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci return -ENODEV; 458c2ecf20Sopenharmony_ci} 468c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(of_get_phy_mode); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic const void *of_get_mac_addr(struct device_node *np, const char *name) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci struct property *pp = of_find_property(np, name, NULL); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci if (pp && pp->length == ETH_ALEN && is_valid_ether_addr(pp->value)) 538c2ecf20Sopenharmony_ci return pp->value; 548c2ecf20Sopenharmony_ci return NULL; 558c2ecf20Sopenharmony_ci} 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic const void *of_get_mac_addr_nvmem(struct device_node *np) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci int ret; 608c2ecf20Sopenharmony_ci const void *mac; 618c2ecf20Sopenharmony_ci u8 nvmem_mac[ETH_ALEN]; 628c2ecf20Sopenharmony_ci struct platform_device *pdev = of_find_device_by_node(np); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci if (!pdev) 658c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci ret = nvmem_get_mac_address(&pdev->dev, &nvmem_mac); 688c2ecf20Sopenharmony_ci if (ret) { 698c2ecf20Sopenharmony_ci put_device(&pdev->dev); 708c2ecf20Sopenharmony_ci return ERR_PTR(ret); 718c2ecf20Sopenharmony_ci } 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci mac = devm_kmemdup(&pdev->dev, nvmem_mac, ETH_ALEN, GFP_KERNEL); 748c2ecf20Sopenharmony_ci put_device(&pdev->dev); 758c2ecf20Sopenharmony_ci if (!mac) 768c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci return mac; 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci/** 828c2ecf20Sopenharmony_ci * Search the device tree for the best MAC address to use. 'mac-address' is 838c2ecf20Sopenharmony_ci * checked first, because that is supposed to contain to "most recent" MAC 848c2ecf20Sopenharmony_ci * address. If that isn't set, then 'local-mac-address' is checked next, 858c2ecf20Sopenharmony_ci * because that is the default address. If that isn't set, then the obsolete 868c2ecf20Sopenharmony_ci * 'address' is checked, just in case we're using an old device tree. If any 878c2ecf20Sopenharmony_ci * of the above isn't set, then try to get MAC address from nvmem cell named 888c2ecf20Sopenharmony_ci * 'mac-address'. 898c2ecf20Sopenharmony_ci * 908c2ecf20Sopenharmony_ci * Note that the 'address' property is supposed to contain a virtual address of 918c2ecf20Sopenharmony_ci * the register set, but some DTS files have redefined that property to be the 928c2ecf20Sopenharmony_ci * MAC address. 938c2ecf20Sopenharmony_ci * 948c2ecf20Sopenharmony_ci * All-zero MAC addresses are rejected, because those could be properties that 958c2ecf20Sopenharmony_ci * exist in the device tree, but were not set by U-Boot. For example, the 968c2ecf20Sopenharmony_ci * DTS could define 'mac-address' and 'local-mac-address', with zero MAC 978c2ecf20Sopenharmony_ci * addresses. Some older U-Boots only initialized 'local-mac-address'. In 988c2ecf20Sopenharmony_ci * this case, the real MAC is in 'local-mac-address', and 'mac-address' exists 998c2ecf20Sopenharmony_ci * but is all zeros. 1008c2ecf20Sopenharmony_ci * 1018c2ecf20Sopenharmony_ci * Return: Will be a valid pointer on success and ERR_PTR in case of error. 1028c2ecf20Sopenharmony_ci*/ 1038c2ecf20Sopenharmony_ciconst void *of_get_mac_address(struct device_node *np) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci const void *addr; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci addr = of_get_mac_addr(np, "mac-address"); 1088c2ecf20Sopenharmony_ci if (addr) 1098c2ecf20Sopenharmony_ci return addr; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci addr = of_get_mac_addr(np, "local-mac-address"); 1128c2ecf20Sopenharmony_ci if (addr) 1138c2ecf20Sopenharmony_ci return addr; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci addr = of_get_mac_addr(np, "address"); 1168c2ecf20Sopenharmony_ci if (addr) 1178c2ecf20Sopenharmony_ci return addr; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci return of_get_mac_addr_nvmem(np); 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ciEXPORT_SYMBOL(of_get_mac_address); 122