1 /*
2  * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
3  *
4  * Permission to use, copy, modify, and/or distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16 #include <linux/of.h>
17 #include <linux/of_net.h>
18 #include <linux/mtd/mtd.h>
19 #include <linux/mtd/partitions.h>
20 #include <linux/etherdevice.h>
21 #include "mt76.h"
22 
23 static int
24 mt76_get_of_eeprom(struct mt76_dev *dev, int len)
25 {
26 #if defined(CONFIG_OF) && defined(CONFIG_MTD)
27 	struct device_node *np = dev->dev->of_node;
28 	struct mtd_info *mtd;
29 	const __be32 *list;
30 	const char *part;
31 	phandle phandle;
32 	int offset = 0;
33 	int size;
34 	size_t retlen;
35 	int ret;
36 
37 	if (!np)
38 		return -ENOENT;
39 
40 	list = of_get_property(np, "mediatek,mtd-eeprom", &size);
41 	if (!list)
42 		return -ENOENT;
43 
44 	phandle = be32_to_cpup(list++);
45 	if (!phandle)
46 		return -ENOENT;
47 
48 	np = of_find_node_by_phandle(phandle);
49 	if (!np)
50 		return -EINVAL;
51 
52 	part = of_get_property(np, "label", NULL);
53 	if (!part)
54 		part = np->name;
55 
56 	mtd = get_mtd_device_nm(part);
57 	if (IS_ERR(mtd))
58 		return PTR_ERR(mtd);
59 
60 	if (size <= sizeof(*list))
61 		return -EINVAL;
62 
63 	offset = be32_to_cpup(list);
64 	ret = mtd_read(mtd, offset, len, &retlen, dev->eeprom.data);
65 	put_mtd_device(mtd);
66 	if (ret)
67 		return ret;
68 
69 	if (retlen < len)
70 		return -EINVAL;
71 
72 	return 0;
73 #else
74 	return -ENOENT;
75 #endif
76 }
77 
78 void
79 mt76_eeprom_override(struct mt76_dev *dev)
80 {
81 #ifdef CONFIG_OF
82 	struct device_node *np = dev->dev->of_node;
83 	const u8 *mac;
84 
85 	if (!np)
86 		return;
87 
88 	mac = of_get_mac_address(np);
89 	if (mac)
90 		memcpy(dev->macaddr, mac, ETH_ALEN);
91 #endif
92 
93 	if (!is_valid_ether_addr(dev->macaddr)) {
94 		eth_random_addr(dev->macaddr);
95 		dev_info(dev->dev,
96 			 "Invalid MAC address, using random address %pM\n",
97 			 dev->macaddr);
98 	}
99 }
100 EXPORT_SYMBOL_GPL(mt76_eeprom_override);
101 
102 int
103 mt76_eeprom_init(struct mt76_dev *dev, int len)
104 {
105 	dev->eeprom.size = len;
106 	dev->eeprom.data = devm_kzalloc(dev->dev, len, GFP_KERNEL);
107 	if (!dev->eeprom.data)
108 		return -ENOMEM;
109 
110 	return !mt76_get_of_eeprom(dev, len);
111 }
112 EXPORT_SYMBOL_GPL(mt76_eeprom_init);
113