162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * OF helpers for network devices. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Initially copied out of arch/powerpc/kernel/prom_parse.c 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci#include <linux/etherdevice.h> 862306a36Sopenharmony_ci#include <linux/kernel.h> 962306a36Sopenharmony_ci#include <linux/of_net.h> 1062306a36Sopenharmony_ci#include <linux/of_platform.h> 1162306a36Sopenharmony_ci#include <linux/platform_device.h> 1262306a36Sopenharmony_ci#include <linux/phy.h> 1362306a36Sopenharmony_ci#include <linux/export.h> 1462306a36Sopenharmony_ci#include <linux/device.h> 1562306a36Sopenharmony_ci#include <linux/nvmem-consumer.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci/** 1862306a36Sopenharmony_ci * of_get_phy_mode - Get phy mode for given device_node 1962306a36Sopenharmony_ci * @np: Pointer to the given device_node 2062306a36Sopenharmony_ci * @interface: Pointer to the result 2162306a36Sopenharmony_ci * 2262306a36Sopenharmony_ci * The function gets phy interface string from property 'phy-mode' or 2362306a36Sopenharmony_ci * 'phy-connection-type'. The index in phy_modes table is set in 2462306a36Sopenharmony_ci * interface and 0 returned. In case of error interface is set to 2562306a36Sopenharmony_ci * PHY_INTERFACE_MODE_NA and an errno is returned, e.g. -ENODEV. 2662306a36Sopenharmony_ci */ 2762306a36Sopenharmony_ciint of_get_phy_mode(struct device_node *np, phy_interface_t *interface) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci const char *pm; 3062306a36Sopenharmony_ci int err, i; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci *interface = PHY_INTERFACE_MODE_NA; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci err = of_property_read_string(np, "phy-mode", &pm); 3562306a36Sopenharmony_ci if (err < 0) 3662306a36Sopenharmony_ci err = of_property_read_string(np, "phy-connection-type", &pm); 3762306a36Sopenharmony_ci if (err < 0) 3862306a36Sopenharmony_ci return err; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci for (i = 0; i < PHY_INTERFACE_MODE_MAX; i++) 4162306a36Sopenharmony_ci if (!strcasecmp(pm, phy_modes(i))) { 4262306a36Sopenharmony_ci *interface = i; 4362306a36Sopenharmony_ci return 0; 4462306a36Sopenharmony_ci } 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci return -ENODEV; 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_get_phy_mode); 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic int of_get_mac_addr(struct device_node *np, const char *name, u8 *addr) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci struct property *pp = of_find_property(np, name, NULL); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci if (pp && pp->length == ETH_ALEN && is_valid_ether_addr(pp->value)) { 5562306a36Sopenharmony_ci memcpy(addr, pp->value, ETH_ALEN); 5662306a36Sopenharmony_ci return 0; 5762306a36Sopenharmony_ci } 5862306a36Sopenharmony_ci return -ENODEV; 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ciint of_get_mac_address_nvmem(struct device_node *np, u8 *addr) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci struct platform_device *pdev = of_find_device_by_node(np); 6462306a36Sopenharmony_ci struct nvmem_cell *cell; 6562306a36Sopenharmony_ci const void *mac; 6662306a36Sopenharmony_ci size_t len; 6762306a36Sopenharmony_ci int ret; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci /* Try lookup by device first, there might be a nvmem_cell_lookup 7062306a36Sopenharmony_ci * associated with a given device. 7162306a36Sopenharmony_ci */ 7262306a36Sopenharmony_ci if (pdev) { 7362306a36Sopenharmony_ci ret = nvmem_get_mac_address(&pdev->dev, addr); 7462306a36Sopenharmony_ci put_device(&pdev->dev); 7562306a36Sopenharmony_ci return ret; 7662306a36Sopenharmony_ci } 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci cell = of_nvmem_cell_get(np, "mac-address"); 7962306a36Sopenharmony_ci if (IS_ERR(cell)) 8062306a36Sopenharmony_ci return PTR_ERR(cell); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci mac = nvmem_cell_read(cell, &len); 8362306a36Sopenharmony_ci nvmem_cell_put(cell); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci if (IS_ERR(mac)) 8662306a36Sopenharmony_ci return PTR_ERR(mac); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci if (len != ETH_ALEN || !is_valid_ether_addr(mac)) { 8962306a36Sopenharmony_ci kfree(mac); 9062306a36Sopenharmony_ci return -EINVAL; 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci memcpy(addr, mac, ETH_ALEN); 9462306a36Sopenharmony_ci kfree(mac); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci return 0; 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ciEXPORT_SYMBOL(of_get_mac_address_nvmem); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci/** 10162306a36Sopenharmony_ci * of_get_mac_address() 10262306a36Sopenharmony_ci * @np: Caller's Device Node 10362306a36Sopenharmony_ci * @addr: Pointer to a six-byte array for the result 10462306a36Sopenharmony_ci * 10562306a36Sopenharmony_ci * Search the device tree for the best MAC address to use. 'mac-address' is 10662306a36Sopenharmony_ci * checked first, because that is supposed to contain to "most recent" MAC 10762306a36Sopenharmony_ci * address. If that isn't set, then 'local-mac-address' is checked next, 10862306a36Sopenharmony_ci * because that is the default address. If that isn't set, then the obsolete 10962306a36Sopenharmony_ci * 'address' is checked, just in case we're using an old device tree. If any 11062306a36Sopenharmony_ci * of the above isn't set, then try to get MAC address from nvmem cell named 11162306a36Sopenharmony_ci * 'mac-address'. 11262306a36Sopenharmony_ci * 11362306a36Sopenharmony_ci * Note that the 'address' property is supposed to contain a virtual address of 11462306a36Sopenharmony_ci * the register set, but some DTS files have redefined that property to be the 11562306a36Sopenharmony_ci * MAC address. 11662306a36Sopenharmony_ci * 11762306a36Sopenharmony_ci * All-zero MAC addresses are rejected, because those could be properties that 11862306a36Sopenharmony_ci * exist in the device tree, but were not set by U-Boot. For example, the 11962306a36Sopenharmony_ci * DTS could define 'mac-address' and 'local-mac-address', with zero MAC 12062306a36Sopenharmony_ci * addresses. Some older U-Boots only initialized 'local-mac-address'. In 12162306a36Sopenharmony_ci * this case, the real MAC is in 'local-mac-address', and 'mac-address' exists 12262306a36Sopenharmony_ci * but is all zeros. 12362306a36Sopenharmony_ci * 12462306a36Sopenharmony_ci * Return: 0 on success and errno in case of error. 12562306a36Sopenharmony_ci*/ 12662306a36Sopenharmony_ciint of_get_mac_address(struct device_node *np, u8 *addr) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci int ret; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci if (!np) 13162306a36Sopenharmony_ci return -ENODEV; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci ret = of_get_mac_addr(np, "mac-address", addr); 13462306a36Sopenharmony_ci if (!ret) 13562306a36Sopenharmony_ci return 0; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci ret = of_get_mac_addr(np, "local-mac-address", addr); 13862306a36Sopenharmony_ci if (!ret) 13962306a36Sopenharmony_ci return 0; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci ret = of_get_mac_addr(np, "address", addr); 14262306a36Sopenharmony_ci if (!ret) 14362306a36Sopenharmony_ci return 0; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci return of_get_mac_address_nvmem(np, addr); 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ciEXPORT_SYMBOL(of_get_mac_address); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci/** 15062306a36Sopenharmony_ci * of_get_ethdev_address() 15162306a36Sopenharmony_ci * @np: Caller's Device Node 15262306a36Sopenharmony_ci * @dev: Pointer to netdevice which address will be updated 15362306a36Sopenharmony_ci * 15462306a36Sopenharmony_ci * Search the device tree for the best MAC address to use. 15562306a36Sopenharmony_ci * If found set @dev->dev_addr to that address. 15662306a36Sopenharmony_ci * 15762306a36Sopenharmony_ci * See documentation of of_get_mac_address() for more information on how 15862306a36Sopenharmony_ci * the best address is determined. 15962306a36Sopenharmony_ci * 16062306a36Sopenharmony_ci * Return: 0 on success and errno in case of error. 16162306a36Sopenharmony_ci */ 16262306a36Sopenharmony_ciint of_get_ethdev_address(struct device_node *np, struct net_device *dev) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci u8 addr[ETH_ALEN]; 16562306a36Sopenharmony_ci int ret; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci ret = of_get_mac_address(np, addr); 16862306a36Sopenharmony_ci if (!ret) 16962306a36Sopenharmony_ci eth_hw_addr_set(dev, addr); 17062306a36Sopenharmony_ci return ret; 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ciEXPORT_SYMBOL(of_get_ethdev_address); 173