162306a36Sopenharmony_ci// SPDX-License-Identifier: ISC 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name> 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci#include <linux/of.h> 662306a36Sopenharmony_ci#include <linux/of_net.h> 762306a36Sopenharmony_ci#include <linux/mtd/mtd.h> 862306a36Sopenharmony_ci#include <linux/mtd/partitions.h> 962306a36Sopenharmony_ci#include <linux/nvmem-consumer.h> 1062306a36Sopenharmony_ci#include <linux/etherdevice.h> 1162306a36Sopenharmony_ci#include "mt76.h" 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_cistatic int mt76_get_of_eeprom_data(struct mt76_dev *dev, void *eep, int len) 1462306a36Sopenharmony_ci{ 1562306a36Sopenharmony_ci struct device_node *np = dev->dev->of_node; 1662306a36Sopenharmony_ci const void *data; 1762306a36Sopenharmony_ci int size; 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci data = of_get_property(np, "mediatek,eeprom-data", &size); 2062306a36Sopenharmony_ci if (!data) 2162306a36Sopenharmony_ci return -ENOENT; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci if (size > len) 2462306a36Sopenharmony_ci return -EINVAL; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci memcpy(eep, data, size); 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci return 0; 2962306a36Sopenharmony_ci} 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic int mt76_get_of_epprom_from_mtd(struct mt76_dev *dev, void *eep, int offset, int len) 3262306a36Sopenharmony_ci{ 3362306a36Sopenharmony_ci#ifdef CONFIG_MTD 3462306a36Sopenharmony_ci struct device_node *np = dev->dev->of_node; 3562306a36Sopenharmony_ci struct mtd_info *mtd; 3662306a36Sopenharmony_ci const __be32 *list; 3762306a36Sopenharmony_ci const char *part; 3862306a36Sopenharmony_ci phandle phandle; 3962306a36Sopenharmony_ci size_t retlen; 4062306a36Sopenharmony_ci int size; 4162306a36Sopenharmony_ci int ret; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci list = of_get_property(np, "mediatek,mtd-eeprom", &size); 4462306a36Sopenharmony_ci if (!list) 4562306a36Sopenharmony_ci return -ENOENT; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci phandle = be32_to_cpup(list++); 4862306a36Sopenharmony_ci if (!phandle) 4962306a36Sopenharmony_ci return -ENOENT; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci np = of_find_node_by_phandle(phandle); 5262306a36Sopenharmony_ci if (!np) 5362306a36Sopenharmony_ci return -EINVAL; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci part = of_get_property(np, "label", NULL); 5662306a36Sopenharmony_ci if (!part) 5762306a36Sopenharmony_ci part = np->name; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci mtd = get_mtd_device_nm(part); 6062306a36Sopenharmony_ci if (IS_ERR(mtd)) { 6162306a36Sopenharmony_ci ret = PTR_ERR(mtd); 6262306a36Sopenharmony_ci goto out_put_node; 6362306a36Sopenharmony_ci } 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci if (size <= sizeof(*list)) { 6662306a36Sopenharmony_ci ret = -EINVAL; 6762306a36Sopenharmony_ci goto out_put_node; 6862306a36Sopenharmony_ci } 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci offset += be32_to_cpup(list); 7162306a36Sopenharmony_ci ret = mtd_read(mtd, offset, len, &retlen, eep); 7262306a36Sopenharmony_ci put_mtd_device(mtd); 7362306a36Sopenharmony_ci if (mtd_is_bitflip(ret)) 7462306a36Sopenharmony_ci ret = 0; 7562306a36Sopenharmony_ci if (ret) { 7662306a36Sopenharmony_ci dev_err(dev->dev, "reading EEPROM from mtd %s failed: %i\n", 7762306a36Sopenharmony_ci part, ret); 7862306a36Sopenharmony_ci goto out_put_node; 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci if (retlen < len) { 8262306a36Sopenharmony_ci ret = -EINVAL; 8362306a36Sopenharmony_ci goto out_put_node; 8462306a36Sopenharmony_ci } 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci if (of_property_read_bool(dev->dev->of_node, "big-endian")) { 8762306a36Sopenharmony_ci u8 *data = (u8 *)eep; 8862306a36Sopenharmony_ci int i; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci /* convert eeprom data in Little Endian */ 9162306a36Sopenharmony_ci for (i = 0; i < round_down(len, 2); i += 2) 9262306a36Sopenharmony_ci put_unaligned_le16(get_unaligned_be16(&data[i]), 9362306a36Sopenharmony_ci &data[i]); 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci#ifdef CONFIG_NL80211_TESTMODE 9762306a36Sopenharmony_ci dev->test_mtd.name = devm_kstrdup(dev->dev, part, GFP_KERNEL); 9862306a36Sopenharmony_ci dev->test_mtd.offset = offset; 9962306a36Sopenharmony_ci#endif 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ciout_put_node: 10262306a36Sopenharmony_ci of_node_put(np); 10362306a36Sopenharmony_ci return ret; 10462306a36Sopenharmony_ci#else 10562306a36Sopenharmony_ci return -ENOENT; 10662306a36Sopenharmony_ci#endif 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistatic int mt76_get_of_eeprom_from_nvmem(struct mt76_dev *dev, void *eep, int len) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci struct device_node *np = dev->dev->of_node; 11262306a36Sopenharmony_ci struct nvmem_cell *cell; 11362306a36Sopenharmony_ci const void *data; 11462306a36Sopenharmony_ci size_t retlen; 11562306a36Sopenharmony_ci int ret = 0; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci cell = of_nvmem_cell_get(np, "eeprom"); 11862306a36Sopenharmony_ci if (IS_ERR(cell)) 11962306a36Sopenharmony_ci return PTR_ERR(cell); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci data = nvmem_cell_read(cell, &retlen); 12262306a36Sopenharmony_ci nvmem_cell_put(cell); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci if (IS_ERR(data)) 12562306a36Sopenharmony_ci return PTR_ERR(data); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci if (retlen < len) { 12862306a36Sopenharmony_ci ret = -EINVAL; 12962306a36Sopenharmony_ci goto exit; 13062306a36Sopenharmony_ci } 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci memcpy(eep, data, len); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ciexit: 13562306a36Sopenharmony_ci kfree(data); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci return ret; 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ciint mt76_get_of_eeprom(struct mt76_dev *dev, void *eep, int offset, int len) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci struct device_node *np = dev->dev->of_node; 14362306a36Sopenharmony_ci int ret; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci if (!np) 14662306a36Sopenharmony_ci return -ENOENT; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci ret = mt76_get_of_eeprom_data(dev, eep, len); 14962306a36Sopenharmony_ci if (!ret) 15062306a36Sopenharmony_ci return 0; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci ret = mt76_get_of_epprom_from_mtd(dev, eep, offset, len); 15362306a36Sopenharmony_ci if (!ret) 15462306a36Sopenharmony_ci return 0; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci return mt76_get_of_eeprom_from_nvmem(dev, eep, len); 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76_get_of_eeprom); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_civoid 16162306a36Sopenharmony_cimt76_eeprom_override(struct mt76_phy *phy) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci struct mt76_dev *dev = phy->dev; 16462306a36Sopenharmony_ci struct device_node *np = dev->dev->of_node; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci of_get_mac_address(np, phy->macaddr); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci if (!is_valid_ether_addr(phy->macaddr)) { 16962306a36Sopenharmony_ci eth_random_addr(phy->macaddr); 17062306a36Sopenharmony_ci dev_info(dev->dev, 17162306a36Sopenharmony_ci "Invalid MAC address, using random address %pM\n", 17262306a36Sopenharmony_ci phy->macaddr); 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76_eeprom_override); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic bool mt76_string_prop_find(struct property *prop, const char *str) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci const char *cp = NULL; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci if (!prop || !str || !str[0]) 18262306a36Sopenharmony_ci return false; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci while ((cp = of_prop_next_string(prop, cp)) != NULL) 18562306a36Sopenharmony_ci if (!strcasecmp(cp, str)) 18662306a36Sopenharmony_ci return true; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci return false; 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cistatic struct device_node * 19262306a36Sopenharmony_cimt76_find_power_limits_node(struct mt76_dev *dev) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci struct device_node *np = dev->dev->of_node; 19562306a36Sopenharmony_ci const char *const region_names[] = { 19662306a36Sopenharmony_ci [NL80211_DFS_UNSET] = "ww", 19762306a36Sopenharmony_ci [NL80211_DFS_ETSI] = "etsi", 19862306a36Sopenharmony_ci [NL80211_DFS_FCC] = "fcc", 19962306a36Sopenharmony_ci [NL80211_DFS_JP] = "jp", 20062306a36Sopenharmony_ci }; 20162306a36Sopenharmony_ci struct device_node *cur, *fallback = NULL; 20262306a36Sopenharmony_ci const char *region_name = NULL; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci if (dev->region < ARRAY_SIZE(region_names)) 20562306a36Sopenharmony_ci region_name = region_names[dev->region]; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci np = of_get_child_by_name(np, "power-limits"); 20862306a36Sopenharmony_ci if (!np) 20962306a36Sopenharmony_ci return NULL; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci for_each_child_of_node(np, cur) { 21262306a36Sopenharmony_ci struct property *country = of_find_property(cur, "country", NULL); 21362306a36Sopenharmony_ci struct property *regd = of_find_property(cur, "regdomain", NULL); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci if (!country && !regd) { 21662306a36Sopenharmony_ci fallback = cur; 21762306a36Sopenharmony_ci continue; 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci if (mt76_string_prop_find(country, dev->alpha2) || 22162306a36Sopenharmony_ci mt76_string_prop_find(regd, region_name)) { 22262306a36Sopenharmony_ci of_node_put(np); 22362306a36Sopenharmony_ci return cur; 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci of_node_put(np); 22862306a36Sopenharmony_ci return fallback; 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_cistatic const __be32 * 23262306a36Sopenharmony_cimt76_get_of_array(struct device_node *np, char *name, size_t *len, int min) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci struct property *prop = of_find_property(np, name, NULL); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci if (!prop || !prop->value || prop->length < min * 4) 23762306a36Sopenharmony_ci return NULL; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci *len = prop->length; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci return prop->value; 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_cistatic struct device_node * 24562306a36Sopenharmony_cimt76_find_channel_node(struct device_node *np, struct ieee80211_channel *chan) 24662306a36Sopenharmony_ci{ 24762306a36Sopenharmony_ci struct device_node *cur; 24862306a36Sopenharmony_ci const __be32 *val; 24962306a36Sopenharmony_ci size_t len; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci for_each_child_of_node(np, cur) { 25262306a36Sopenharmony_ci val = mt76_get_of_array(cur, "channels", &len, 2); 25362306a36Sopenharmony_ci if (!val) 25462306a36Sopenharmony_ci continue; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci while (len >= 2 * sizeof(*val)) { 25762306a36Sopenharmony_ci if (chan->hw_value >= be32_to_cpu(val[0]) && 25862306a36Sopenharmony_ci chan->hw_value <= be32_to_cpu(val[1])) 25962306a36Sopenharmony_ci return cur; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci val += 2; 26262306a36Sopenharmony_ci len -= 2 * sizeof(*val); 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci return NULL; 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistatic s8 27062306a36Sopenharmony_cimt76_get_txs_delta(struct device_node *np, u8 nss) 27162306a36Sopenharmony_ci{ 27262306a36Sopenharmony_ci const __be32 *val; 27362306a36Sopenharmony_ci size_t len; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci val = mt76_get_of_array(np, "txs-delta", &len, nss); 27662306a36Sopenharmony_ci if (!val) 27762306a36Sopenharmony_ci return 0; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci return be32_to_cpu(val[nss - 1]); 28062306a36Sopenharmony_ci} 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_cistatic void 28362306a36Sopenharmony_cimt76_apply_array_limit(s8 *pwr, size_t pwr_len, const __be32 *data, 28462306a36Sopenharmony_ci s8 target_power, s8 nss_delta, s8 *max_power) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci int i; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci if (!data) 28962306a36Sopenharmony_ci return; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci for (i = 0; i < pwr_len; i++) { 29262306a36Sopenharmony_ci pwr[i] = min_t(s8, target_power, 29362306a36Sopenharmony_ci be32_to_cpu(data[i]) + nss_delta); 29462306a36Sopenharmony_ci *max_power = max(*max_power, pwr[i]); 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cistatic void 29962306a36Sopenharmony_cimt76_apply_multi_array_limit(s8 *pwr, size_t pwr_len, s8 pwr_num, 30062306a36Sopenharmony_ci const __be32 *data, size_t len, s8 target_power, 30162306a36Sopenharmony_ci s8 nss_delta, s8 *max_power) 30262306a36Sopenharmony_ci{ 30362306a36Sopenharmony_ci int i, cur; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci if (!data) 30662306a36Sopenharmony_ci return; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci len /= 4; 30962306a36Sopenharmony_ci cur = be32_to_cpu(data[0]); 31062306a36Sopenharmony_ci for (i = 0; i < pwr_num; i++) { 31162306a36Sopenharmony_ci if (len < pwr_len + 1) 31262306a36Sopenharmony_ci break; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci mt76_apply_array_limit(pwr + pwr_len * i, pwr_len, data + 1, 31562306a36Sopenharmony_ci target_power, nss_delta, max_power); 31662306a36Sopenharmony_ci if (--cur > 0) 31762306a36Sopenharmony_ci continue; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci data += pwr_len + 1; 32062306a36Sopenharmony_ci len -= pwr_len + 1; 32162306a36Sopenharmony_ci if (!len) 32262306a36Sopenharmony_ci break; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci cur = be32_to_cpu(data[0]); 32562306a36Sopenharmony_ci } 32662306a36Sopenharmony_ci} 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_cis8 mt76_get_rate_power_limits(struct mt76_phy *phy, 32962306a36Sopenharmony_ci struct ieee80211_channel *chan, 33062306a36Sopenharmony_ci struct mt76_power_limits *dest, 33162306a36Sopenharmony_ci s8 target_power) 33262306a36Sopenharmony_ci{ 33362306a36Sopenharmony_ci struct mt76_dev *dev = phy->dev; 33462306a36Sopenharmony_ci struct device_node *np; 33562306a36Sopenharmony_ci const __be32 *val; 33662306a36Sopenharmony_ci char name[16]; 33762306a36Sopenharmony_ci u32 mcs_rates = dev->drv->mcs_rates; 33862306a36Sopenharmony_ci u32 ru_rates = ARRAY_SIZE(dest->ru[0]); 33962306a36Sopenharmony_ci char band; 34062306a36Sopenharmony_ci size_t len; 34162306a36Sopenharmony_ci s8 max_power = 0; 34262306a36Sopenharmony_ci s8 txs_delta; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci if (!mcs_rates) 34562306a36Sopenharmony_ci mcs_rates = 10; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci memset(dest, target_power, sizeof(*dest)); 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_OF)) 35062306a36Sopenharmony_ci return target_power; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci np = mt76_find_power_limits_node(dev); 35362306a36Sopenharmony_ci if (!np) 35462306a36Sopenharmony_ci return target_power; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci switch (chan->band) { 35762306a36Sopenharmony_ci case NL80211_BAND_2GHZ: 35862306a36Sopenharmony_ci band = '2'; 35962306a36Sopenharmony_ci break; 36062306a36Sopenharmony_ci case NL80211_BAND_5GHZ: 36162306a36Sopenharmony_ci band = '5'; 36262306a36Sopenharmony_ci break; 36362306a36Sopenharmony_ci case NL80211_BAND_6GHZ: 36462306a36Sopenharmony_ci band = '6'; 36562306a36Sopenharmony_ci break; 36662306a36Sopenharmony_ci default: 36762306a36Sopenharmony_ci return target_power; 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci snprintf(name, sizeof(name), "txpower-%cg", band); 37162306a36Sopenharmony_ci np = of_get_child_by_name(np, name); 37262306a36Sopenharmony_ci if (!np) 37362306a36Sopenharmony_ci return target_power; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci np = mt76_find_channel_node(np, chan); 37662306a36Sopenharmony_ci if (!np) 37762306a36Sopenharmony_ci return target_power; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci txs_delta = mt76_get_txs_delta(np, hweight8(phy->antenna_mask)); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci val = mt76_get_of_array(np, "rates-cck", &len, ARRAY_SIZE(dest->cck)); 38262306a36Sopenharmony_ci mt76_apply_array_limit(dest->cck, ARRAY_SIZE(dest->cck), val, 38362306a36Sopenharmony_ci target_power, txs_delta, &max_power); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci val = mt76_get_of_array(np, "rates-ofdm", 38662306a36Sopenharmony_ci &len, ARRAY_SIZE(dest->ofdm)); 38762306a36Sopenharmony_ci mt76_apply_array_limit(dest->ofdm, ARRAY_SIZE(dest->ofdm), val, 38862306a36Sopenharmony_ci target_power, txs_delta, &max_power); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci val = mt76_get_of_array(np, "rates-mcs", &len, mcs_rates + 1); 39162306a36Sopenharmony_ci mt76_apply_multi_array_limit(dest->mcs[0], ARRAY_SIZE(dest->mcs[0]), 39262306a36Sopenharmony_ci ARRAY_SIZE(dest->mcs), val, len, 39362306a36Sopenharmony_ci target_power, txs_delta, &max_power); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci val = mt76_get_of_array(np, "rates-ru", &len, ru_rates + 1); 39662306a36Sopenharmony_ci mt76_apply_multi_array_limit(dest->ru[0], ARRAY_SIZE(dest->ru[0]), 39762306a36Sopenharmony_ci ARRAY_SIZE(dest->ru), val, len, 39862306a36Sopenharmony_ci target_power, txs_delta, &max_power); 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci return max_power; 40162306a36Sopenharmony_ci} 40262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76_get_rate_power_limits); 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ciint 40562306a36Sopenharmony_cimt76_eeprom_init(struct mt76_dev *dev, int len) 40662306a36Sopenharmony_ci{ 40762306a36Sopenharmony_ci dev->eeprom.size = len; 40862306a36Sopenharmony_ci dev->eeprom.data = devm_kzalloc(dev->dev, len, GFP_KERNEL); 40962306a36Sopenharmony_ci if (!dev->eeprom.data) 41062306a36Sopenharmony_ci return -ENOMEM; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci return !mt76_get_of_eeprom(dev, dev->eeprom.data, 0, len); 41362306a36Sopenharmony_ci} 41462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mt76_eeprom_init); 415