1 // SPDX-License-Identifier: ISC 2 /* 3 * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name> 4 */ 5 #include <linux/of.h> 6 #include <linux/of_net.h> 7 #include <linux/mtd/mtd.h> 8 #include <linux/mtd/partitions.h> 9 #include <linux/etherdevice.h> 10 #include "mt76.h" 11 12 static int 13 mt76_get_of_eeprom(struct mt76_dev *dev, int len) 14 { 15 #if defined(CONFIG_OF) && defined(CONFIG_MTD) 16 struct device_node *np = dev->dev->of_node; 17 struct mtd_info *mtd; 18 const __be32 *list; 19 const char *part; 20 phandle phandle; 21 int offset = 0; 22 int size; 23 size_t retlen; 24 int ret; 25 26 if (!np) 27 return -ENOENT; 28 29 list = of_get_property(np, "mediatek,mtd-eeprom", &size); 30 if (!list) 31 return -ENOENT; 32 33 phandle = be32_to_cpup(list++); 34 if (!phandle) 35 return -ENOENT; 36 37 np = of_find_node_by_phandle(phandle); 38 if (!np) 39 return -EINVAL; 40 41 part = of_get_property(np, "label", NULL); 42 if (!part) 43 part = np->name; 44 45 mtd = get_mtd_device_nm(part); 46 if (IS_ERR(mtd)) { 47 ret = PTR_ERR(mtd); 48 goto out_put_node; 49 } 50 51 if (size <= sizeof(*list)) { 52 ret = -EINVAL; 53 goto out_put_node; 54 } 55 56 offset = be32_to_cpup(list); 57 ret = mtd_read(mtd, offset, len, &retlen, dev->eeprom.data); 58 put_mtd_device(mtd); 59 if (ret) 60 goto out_put_node; 61 62 if (retlen < len) { 63 ret = -EINVAL; 64 goto out_put_node; 65 } 66 67 if (of_property_read_bool(dev->dev->of_node, "big-endian")) { 68 u8 *data = (u8 *)dev->eeprom.data; 69 int i; 70 71 /* convert eeprom data in Little Endian */ 72 for (i = 0; i < round_down(len, 2); i += 2) 73 put_unaligned_le16(get_unaligned_be16(&data[i]), 74 &data[i]); 75 } 76 77 out_put_node: 78 of_node_put(np); 79 return ret; 80 #else 81 return -ENOENT; 82 #endif 83 } 84 85 void 86 mt76_eeprom_override(struct mt76_dev *dev) 87 { 88 #ifdef CONFIG_OF 89 struct device_node *np = dev->dev->of_node; 90 const u8 *mac = NULL; 91 92 if (np) 93 mac = of_get_mac_address(np); 94 if (!IS_ERR_OR_NULL(mac)) 95 ether_addr_copy(dev->macaddr, mac); 96 #endif 97 98 if (!is_valid_ether_addr(dev->macaddr)) { 99 eth_random_addr(dev->macaddr); 100 dev_info(dev->dev, 101 "Invalid MAC address, using random address %pM\n", 102 dev->macaddr); 103 } 104 } 105 EXPORT_SYMBOL_GPL(mt76_eeprom_override); 106 107 int 108 mt76_eeprom_init(struct mt76_dev *dev, int len) 109 { 110 dev->eeprom.size = len; 111 dev->eeprom.data = devm_kzalloc(dev->dev, len, GFP_KERNEL); 112 if (!dev->eeprom.data) 113 return -ENOMEM; 114 115 return !mt76_get_of_eeprom(dev, len); 116 } 117 EXPORT_SYMBOL_GPL(mt76_eeprom_init); 118