| /* |
| * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name> |
| * |
| * Permission to use, copy, modify, and/or distribute this software for any |
| * purpose with or without fee is hereby granted, provided that the above |
| * copyright notice and this permission notice appear in all copies. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
| * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
| * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
| * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| */ |
| #include <linux/of.h> |
| #include <linux/of_net.h> |
| #include <linux/mtd/mtd.h> |
| #include <linux/mtd/partitions.h> |
| #include <linux/etherdevice.h> |
| #include "mt76.h" |
| |
| static int |
| mt76_get_of_eeprom(struct mt76_dev *dev, int len) |
| { |
| #if defined(CONFIG_OF) && defined(CONFIG_MTD) |
| struct device_node *np = dev->dev->of_node; |
| struct mtd_info *mtd; |
| const __be32 *list; |
| const char *part; |
| phandle phandle; |
| int offset = 0; |
| int size; |
| size_t retlen; |
| int ret; |
| |
| if (!np) |
| return -ENOENT; |
| |
| list = of_get_property(np, "mediatek,mtd-eeprom", &size); |
| if (!list) |
| return -ENOENT; |
| |
| phandle = be32_to_cpup(list++); |
| if (!phandle) |
| return -ENOENT; |
| |
| np = of_find_node_by_phandle(phandle); |
| if (!np) |
| return -EINVAL; |
| |
| part = of_get_property(np, "label", NULL); |
| if (!part) |
| part = np->name; |
| |
| mtd = get_mtd_device_nm(part); |
| if (IS_ERR(mtd)) { |
| ret = PTR_ERR(mtd); |
| goto out_put_node; |
| } |
| |
| if (size <= sizeof(*list)) { |
| ret = -EINVAL; |
| goto out_put_node; |
| } |
| |
| offset = be32_to_cpup(list); |
| ret = mtd_read(mtd, offset, len, &retlen, dev->eeprom.data); |
| put_mtd_device(mtd); |
| if (ret) |
| goto out_put_node; |
| |
| if (retlen < len) { |
| ret = -EINVAL; |
| goto out_put_node; |
| } |
| |
| out_put_node: |
| of_node_put(np); |
| return ret; |
| #else |
| return -ENOENT; |
| #endif |
| } |
| |
| void |
| mt76_eeprom_override(struct mt76_dev *dev) |
| { |
| #ifdef CONFIG_OF |
| struct device_node *np = dev->dev->of_node; |
| const u8 *mac; |
| |
| if (!np) |
| return; |
| |
| mac = of_get_mac_address(np); |
| if (mac) |
| memcpy(dev->macaddr, mac, ETH_ALEN); |
| #endif |
| |
| if (!is_valid_ether_addr(dev->macaddr)) { |
| eth_random_addr(dev->macaddr); |
| dev_info(dev->dev, |
| "Invalid MAC address, using random address %pM\n", |
| dev->macaddr); |
| } |
| } |
| EXPORT_SYMBOL_GPL(mt76_eeprom_override); |
| |
| int |
| mt76_eeprom_init(struct mt76_dev *dev, int len) |
| { |
| dev->eeprom.size = len; |
| dev->eeprom.data = devm_kzalloc(dev->dev, len, GFP_KERNEL); |
| if (!dev->eeprom.data) |
| return -ENOMEM; |
| |
| return !mt76_get_of_eeprom(dev, len); |
| } |
| EXPORT_SYMBOL_GPL(mt76_eeprom_init); |