18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: ISC 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name> 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci#include <linux/of.h> 68c2ecf20Sopenharmony_ci#include <linux/of_net.h> 78c2ecf20Sopenharmony_ci#include <linux/mtd/mtd.h> 88c2ecf20Sopenharmony_ci#include <linux/mtd/partitions.h> 98c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 108c2ecf20Sopenharmony_ci#include "mt76.h" 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_cistatic int 138c2ecf20Sopenharmony_cimt76_get_of_eeprom(struct mt76_dev *dev, int len) 148c2ecf20Sopenharmony_ci{ 158c2ecf20Sopenharmony_ci#if defined(CONFIG_OF) && defined(CONFIG_MTD) 168c2ecf20Sopenharmony_ci struct device_node *np = dev->dev->of_node; 178c2ecf20Sopenharmony_ci struct mtd_info *mtd; 188c2ecf20Sopenharmony_ci const __be32 *list; 198c2ecf20Sopenharmony_ci const char *part; 208c2ecf20Sopenharmony_ci phandle phandle; 218c2ecf20Sopenharmony_ci int offset = 0; 228c2ecf20Sopenharmony_ci int size; 238c2ecf20Sopenharmony_ci size_t retlen; 248c2ecf20Sopenharmony_ci int ret; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci if (!np) 278c2ecf20Sopenharmony_ci return -ENOENT; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci list = of_get_property(np, "mediatek,mtd-eeprom", &size); 308c2ecf20Sopenharmony_ci if (!list) 318c2ecf20Sopenharmony_ci return -ENOENT; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci phandle = be32_to_cpup(list++); 348c2ecf20Sopenharmony_ci if (!phandle) 358c2ecf20Sopenharmony_ci return -ENOENT; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci np = of_find_node_by_phandle(phandle); 388c2ecf20Sopenharmony_ci if (!np) 398c2ecf20Sopenharmony_ci return -EINVAL; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci part = of_get_property(np, "label", NULL); 428c2ecf20Sopenharmony_ci if (!part) 438c2ecf20Sopenharmony_ci part = np->name; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci mtd = get_mtd_device_nm(part); 468c2ecf20Sopenharmony_ci if (IS_ERR(mtd)) { 478c2ecf20Sopenharmony_ci ret = PTR_ERR(mtd); 488c2ecf20Sopenharmony_ci goto out_put_node; 498c2ecf20Sopenharmony_ci } 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci if (size <= sizeof(*list)) { 528c2ecf20Sopenharmony_ci ret = -EINVAL; 538c2ecf20Sopenharmony_ci goto out_put_node; 548c2ecf20Sopenharmony_ci } 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci offset = be32_to_cpup(list); 578c2ecf20Sopenharmony_ci ret = mtd_read(mtd, offset, len, &retlen, dev->eeprom.data); 588c2ecf20Sopenharmony_ci put_mtd_device(mtd); 598c2ecf20Sopenharmony_ci if (ret) 608c2ecf20Sopenharmony_ci goto out_put_node; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci if (retlen < len) { 638c2ecf20Sopenharmony_ci ret = -EINVAL; 648c2ecf20Sopenharmony_ci goto out_put_node; 658c2ecf20Sopenharmony_ci } 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci if (of_property_read_bool(dev->dev->of_node, "big-endian")) { 688c2ecf20Sopenharmony_ci u8 *data = (u8 *)dev->eeprom.data; 698c2ecf20Sopenharmony_ci int i; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci /* convert eeprom data in Little Endian */ 728c2ecf20Sopenharmony_ci for (i = 0; i < round_down(len, 2); i += 2) 738c2ecf20Sopenharmony_ci put_unaligned_le16(get_unaligned_be16(&data[i]), 748c2ecf20Sopenharmony_ci &data[i]); 758c2ecf20Sopenharmony_ci } 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci#ifdef CONFIG_NL80211_TESTMODE 788c2ecf20Sopenharmony_ci dev->test.mtd_name = devm_kstrdup(dev->dev, part, GFP_KERNEL); 798c2ecf20Sopenharmony_ci dev->test.mtd_offset = offset; 808c2ecf20Sopenharmony_ci#endif 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ciout_put_node: 838c2ecf20Sopenharmony_ci of_node_put(np); 848c2ecf20Sopenharmony_ci return ret; 858c2ecf20Sopenharmony_ci#else 868c2ecf20Sopenharmony_ci return -ENOENT; 878c2ecf20Sopenharmony_ci#endif 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_civoid 918c2ecf20Sopenharmony_cimt76_eeprom_override(struct mt76_dev *dev) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 948c2ecf20Sopenharmony_ci struct device_node *np = dev->dev->of_node; 958c2ecf20Sopenharmony_ci const u8 *mac = NULL; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci if (np) 988c2ecf20Sopenharmony_ci mac = of_get_mac_address(np); 998c2ecf20Sopenharmony_ci if (!IS_ERR_OR_NULL(mac)) 1008c2ecf20Sopenharmony_ci ether_addr_copy(dev->macaddr, mac); 1018c2ecf20Sopenharmony_ci#endif 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci if (!is_valid_ether_addr(dev->macaddr)) { 1048c2ecf20Sopenharmony_ci eth_random_addr(dev->macaddr); 1058c2ecf20Sopenharmony_ci dev_info(dev->dev, 1068c2ecf20Sopenharmony_ci "Invalid MAC address, using random address %pM\n", 1078c2ecf20Sopenharmony_ci dev->macaddr); 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76_eeprom_override); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ciint 1138c2ecf20Sopenharmony_cimt76_eeprom_init(struct mt76_dev *dev, int len) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci dev->eeprom.size = len; 1168c2ecf20Sopenharmony_ci dev->eeprom.data = devm_kzalloc(dev->dev, len, GFP_KERNEL); 1178c2ecf20Sopenharmony_ci if (!dev->eeprom.data) 1188c2ecf20Sopenharmony_ci return -ENOMEM; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci return !mt76_get_of_eeprom(dev, len); 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76_eeprom_init); 123