linux/drivers/net/wireless/mediatek/mt76/eeprom.c
<<
>>
Prefs
   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
  12int mt76_get_of_eeprom(struct mt76_dev *dev, void *eep, int offset, int len)
  13{
  14#if defined(CONFIG_OF) && defined(CONFIG_MTD)
  15        struct device_node *np = dev->dev->of_node;
  16        struct mtd_info *mtd;
  17        const __be32 *list;
  18        const char *part;
  19        phandle phandle;
  20        int size;
  21        size_t retlen;
  22        int ret;
  23
  24        if (!np)
  25                return -ENOENT;
  26
  27        list = of_get_property(np, "mediatek,mtd-eeprom", &size);
  28        if (!list)
  29                return -ENOENT;
  30
  31        phandle = be32_to_cpup(list++);
  32        if (!phandle)
  33                return -ENOENT;
  34
  35        np = of_find_node_by_phandle(phandle);
  36        if (!np)
  37                return -EINVAL;
  38
  39        part = of_get_property(np, "label", NULL);
  40        if (!part)
  41                part = np->name;
  42
  43        mtd = get_mtd_device_nm(part);
  44        if (IS_ERR(mtd)) {
  45                ret =  PTR_ERR(mtd);
  46                goto out_put_node;
  47        }
  48
  49        if (size <= sizeof(*list)) {
  50                ret = -EINVAL;
  51                goto out_put_node;
  52        }
  53
  54        offset = be32_to_cpup(list);
  55        ret = mtd_read(mtd, offset, len, &retlen, eep);
  56        put_mtd_device(mtd);
  57        if (ret)
  58                goto out_put_node;
  59
  60        if (retlen < len) {
  61                ret = -EINVAL;
  62                goto out_put_node;
  63        }
  64
  65        if (of_property_read_bool(dev->dev->of_node, "big-endian")) {
  66                u8 *data = (u8 *)eep;
  67                int i;
  68
  69                /* convert eeprom data in Little Endian */
  70                for (i = 0; i < round_down(len, 2); i += 2)
  71                        put_unaligned_le16(get_unaligned_be16(&data[i]),
  72                                           &data[i]);
  73        }
  74
  75#ifdef CONFIG_NL80211_TESTMODE
  76        dev->test_mtd.name = devm_kstrdup(dev->dev, part, GFP_KERNEL);
  77        dev->test_mtd.offset = offset;
  78#endif
  79
  80out_put_node:
  81        of_node_put(np);
  82        return ret;
  83#else
  84        return -ENOENT;
  85#endif
  86}
  87EXPORT_SYMBOL_GPL(mt76_get_of_eeprom);
  88
  89void
  90mt76_eeprom_override(struct mt76_phy *phy)
  91{
  92        struct mt76_dev *dev = phy->dev;
  93        struct device_node *np = dev->dev->of_node;
  94
  95        of_get_mac_address(np, phy->macaddr);
  96
  97        if (!is_valid_ether_addr(phy->macaddr)) {
  98                eth_random_addr(phy->macaddr);
  99                dev_info(dev->dev,
 100                         "Invalid MAC address, using random address %pM\n",
 101                         phy->macaddr);
 102        }
 103}
 104EXPORT_SYMBOL_GPL(mt76_eeprom_override);
 105
 106static bool mt76_string_prop_find(struct property *prop, const char *str)
 107{
 108        const char *cp = NULL;
 109
 110        if (!prop || !str || !str[0])
 111                return false;
 112
 113        while ((cp = of_prop_next_string(prop, cp)) != NULL)
 114                if (!strcasecmp(cp, str))
 115                        return true;
 116
 117        return false;
 118}
 119
 120static struct device_node *
 121mt76_find_power_limits_node(struct mt76_dev *dev)
 122{
 123        struct device_node *np = dev->dev->of_node;
 124        const char *const region_names[] = {
 125                [NL80211_DFS_ETSI] = "etsi",
 126                [NL80211_DFS_FCC] = "fcc",
 127                [NL80211_DFS_JP] = "jp",
 128        };
 129        struct device_node *cur, *fallback = NULL;
 130        const char *region_name = NULL;
 131
 132        if (dev->region < ARRAY_SIZE(region_names))
 133                region_name = region_names[dev->region];
 134
 135        np = of_get_child_by_name(np, "power-limits");
 136        if (!np)
 137                return NULL;
 138
 139        for_each_child_of_node(np, cur) {
 140                struct property *country = of_find_property(cur, "country", NULL);
 141                struct property *regd = of_find_property(cur, "regdomain", NULL);
 142
 143                if (!country && !regd) {
 144                        fallback = cur;
 145                        continue;
 146                }
 147
 148                if (mt76_string_prop_find(country, dev->alpha2) ||
 149                    mt76_string_prop_find(regd, region_name))
 150                        return cur;
 151        }
 152
 153        return fallback;
 154}
 155
 156static const __be32 *
 157mt76_get_of_array(struct device_node *np, char *name, size_t *len, int min)
 158{
 159        struct property *prop = of_find_property(np, name, NULL);
 160
 161        if (!prop || !prop->value || prop->length < min * 4)
 162                return NULL;
 163
 164        *len = prop->length;
 165
 166        return prop->value;
 167}
 168
 169static struct device_node *
 170mt76_find_channel_node(struct device_node *np, struct ieee80211_channel *chan)
 171{
 172        struct device_node *cur;
 173        const __be32 *val;
 174        size_t len;
 175
 176        for_each_child_of_node(np, cur) {
 177                val = mt76_get_of_array(cur, "channels", &len, 2);
 178                if (!val)
 179                        continue;
 180
 181                while (len >= 2 * sizeof(*val)) {
 182                        if (chan->hw_value >= be32_to_cpu(val[0]) &&
 183                            chan->hw_value <= be32_to_cpu(val[1]))
 184                                return cur;
 185
 186                        val += 2;
 187                        len -= 2 * sizeof(*val);
 188                }
 189        }
 190
 191        return NULL;
 192}
 193
 194static s8
 195mt76_get_txs_delta(struct device_node *np, u8 nss)
 196{
 197        const __be32 *val;
 198        size_t len;
 199
 200        val = mt76_get_of_array(np, "txs-delta", &len, nss);
 201        if (!val)
 202                return 0;
 203
 204        return be32_to_cpu(val[nss - 1]);
 205}
 206
 207static void
 208mt76_apply_array_limit(s8 *pwr, size_t pwr_len, const __be32 *data,
 209                       s8 target_power, s8 nss_delta, s8 *max_power)
 210{
 211        int i;
 212
 213        if (!data)
 214                return;
 215
 216        for (i = 0; i < pwr_len; i++) {
 217                pwr[i] = min_t(s8, target_power,
 218                               be32_to_cpu(data[i]) + nss_delta);
 219                *max_power = max(*max_power, pwr[i]);
 220        }
 221}
 222
 223static void
 224mt76_apply_multi_array_limit(s8 *pwr, size_t pwr_len, s8 pwr_num,
 225                             const __be32 *data, size_t len, s8 target_power,
 226                             s8 nss_delta, s8 *max_power)
 227{
 228        int i, cur;
 229
 230        if (!data)
 231                return;
 232
 233        len /= 4;
 234        cur = be32_to_cpu(data[0]);
 235        for (i = 0; i < pwr_num; i++) {
 236                if (len < pwr_len + 1)
 237                        break;
 238
 239                mt76_apply_array_limit(pwr + pwr_len * i, pwr_len, data + 1,
 240                                       target_power, nss_delta, max_power);
 241                if (--cur > 0)
 242                        continue;
 243
 244                data += pwr_len + 1;
 245                len -= pwr_len + 1;
 246                if (!len)
 247                        break;
 248
 249                cur = be32_to_cpu(data[0]);
 250        }
 251}
 252
 253s8 mt76_get_rate_power_limits(struct mt76_phy *phy,
 254                              struct ieee80211_channel *chan,
 255                              struct mt76_power_limits *dest,
 256                              s8 target_power)
 257{
 258        struct mt76_dev *dev = phy->dev;
 259        struct device_node *np;
 260        const __be32 *val;
 261        char name[16];
 262        u32 mcs_rates = dev->drv->mcs_rates;
 263        u32 ru_rates = ARRAY_SIZE(dest->ru[0]);
 264        char band;
 265        size_t len;
 266        s8 max_power = 0;
 267        s8 txs_delta;
 268
 269        if (!mcs_rates)
 270                mcs_rates = 10;
 271
 272        memset(dest, target_power, sizeof(*dest));
 273
 274        if (!IS_ENABLED(CONFIG_OF))
 275                return target_power;
 276
 277        np = mt76_find_power_limits_node(dev);
 278        if (!np)
 279                return target_power;
 280
 281        switch (chan->band) {
 282        case NL80211_BAND_2GHZ:
 283                band = '2';
 284                break;
 285        case NL80211_BAND_5GHZ:
 286                band = '5';
 287                break;
 288        default:
 289                return target_power;
 290        }
 291
 292        snprintf(name, sizeof(name), "txpower-%cg", band);
 293        np = of_get_child_by_name(np, name);
 294        if (!np)
 295                return target_power;
 296
 297        np = mt76_find_channel_node(np, chan);
 298        if (!np)
 299                return target_power;
 300
 301        txs_delta = mt76_get_txs_delta(np, hweight8(phy->antenna_mask));
 302
 303        val = mt76_get_of_array(np, "rates-cck", &len, ARRAY_SIZE(dest->cck));
 304        mt76_apply_array_limit(dest->cck, ARRAY_SIZE(dest->cck), val,
 305                               target_power, txs_delta, &max_power);
 306
 307        val = mt76_get_of_array(np, "rates-ofdm",
 308                                &len, ARRAY_SIZE(dest->ofdm));
 309        mt76_apply_array_limit(dest->ofdm, ARRAY_SIZE(dest->ofdm), val,
 310                               target_power, txs_delta, &max_power);
 311
 312        val = mt76_get_of_array(np, "rates-mcs", &len, mcs_rates + 1);
 313        mt76_apply_multi_array_limit(dest->mcs[0], ARRAY_SIZE(dest->mcs[0]),
 314                                     ARRAY_SIZE(dest->mcs), val, len,
 315                                     target_power, txs_delta, &max_power);
 316
 317        val = mt76_get_of_array(np, "rates-ru", &len, ru_rates + 1);
 318        mt76_apply_multi_array_limit(dest->ru[0], ARRAY_SIZE(dest->ru[0]),
 319                                     ARRAY_SIZE(dest->ru), val, len,
 320                                     target_power, txs_delta, &max_power);
 321
 322        return max_power;
 323}
 324EXPORT_SYMBOL_GPL(mt76_get_rate_power_limits);
 325
 326int
 327mt76_eeprom_init(struct mt76_dev *dev, int len)
 328{
 329        dev->eeprom.size = len;
 330        dev->eeprom.data = devm_kzalloc(dev->dev, len, GFP_KERNEL);
 331        if (!dev->eeprom.data)
 332                return -ENOMEM;
 333
 334        return !mt76_get_of_eeprom(dev, dev->eeprom.data, 0, len);
 335}
 336EXPORT_SYMBOL_GPL(mt76_eeprom_init);
 337