linux/drivers/net/wireless/mediatek/mt76/mt7921/debugfs.c
<<
>>
Prefs
   1// SPDX-License-Identifier: ISC
   2/* Copyright (C) 2020 MediaTek Inc. */
   3
   4#include "mt7921.h"
   5#include "eeprom.h"
   6
   7static int
   8mt7921_fw_debug_set(void *data, u64 val)
   9{
  10        struct mt7921_dev *dev = data;
  11
  12        mt7921_mutex_acquire(dev);
  13
  14        dev->fw_debug = (u8)val;
  15        mt7921_mcu_fw_log_2_host(dev, dev->fw_debug);
  16
  17        mt7921_mutex_release(dev);
  18
  19        return 0;
  20}
  21
  22static int
  23mt7921_fw_debug_get(void *data, u64 *val)
  24{
  25        struct mt7921_dev *dev = data;
  26
  27        *val = dev->fw_debug;
  28
  29        return 0;
  30}
  31
  32DEFINE_DEBUGFS_ATTRIBUTE(fops_fw_debug, mt7921_fw_debug_get,
  33                         mt7921_fw_debug_set, "%lld\n");
  34
  35static void
  36mt7921_ampdu_stat_read_phy(struct mt7921_phy *phy,
  37                           struct seq_file *file)
  38{
  39        struct mt7921_dev *dev = file->private;
  40        int bound[15], range[4], i;
  41
  42        if (!phy)
  43                return;
  44
  45        /* Tx ampdu stat */
  46        for (i = 0; i < ARRAY_SIZE(range); i++)
  47                range[i] = mt76_rr(dev, MT_MIB_ARNG(0, i));
  48
  49        for (i = 0; i < ARRAY_SIZE(bound); i++)
  50                bound[i] = MT_MIB_ARNCR_RANGE(range[i / 4], i % 4) + 1;
  51
  52        seq_printf(file, "\nPhy0\n");
  53
  54        seq_printf(file, "Length: %8d | ", bound[0]);
  55        for (i = 0; i < ARRAY_SIZE(bound) - 1; i++)
  56                seq_printf(file, "%3d  %3d | ", bound[i] + 1, bound[i + 1]);
  57
  58        seq_puts(file, "\nCount:  ");
  59        for (i = 0; i < ARRAY_SIZE(bound); i++)
  60                seq_printf(file, "%8d | ", dev->mt76.aggr_stats[i]);
  61        seq_puts(file, "\n");
  62
  63        seq_printf(file, "BA miss count: %d\n", phy->mib.ba_miss_cnt);
  64}
  65
  66static int
  67mt7921_tx_stats_show(struct seq_file *file, void *data)
  68{
  69        struct mt7921_dev *dev = file->private;
  70        int stat[8], i, n;
  71
  72        mt7921_ampdu_stat_read_phy(&dev->phy, file);
  73
  74        /* Tx amsdu info */
  75        seq_puts(file, "Tx MSDU stat:\n");
  76        for (i = 0, n = 0; i < ARRAY_SIZE(stat); i++) {
  77                stat[i] = mt76_rr(dev,  MT_PLE_AMSDU_PACK_MSDU_CNT(i));
  78                n += stat[i];
  79        }
  80
  81        for (i = 0; i < ARRAY_SIZE(stat); i++) {
  82                seq_printf(file, "AMSDU pack count of %d MSDU in TXD: 0x%x ",
  83                           i + 1, stat[i]);
  84                if (n != 0)
  85                        seq_printf(file, "(%d%%)\n", stat[i] * 100 / n);
  86                else
  87                        seq_puts(file, "\n");
  88        }
  89
  90        return 0;
  91}
  92
  93DEFINE_SHOW_ATTRIBUTE(mt7921_tx_stats);
  94
  95static int
  96mt7921_queues_acq(struct seq_file *s, void *data)
  97{
  98        struct mt7921_dev *dev = dev_get_drvdata(s->private);
  99        int i;
 100
 101        for (i = 0; i < 16; i++) {
 102                int j, acs = i / 4, index = i % 4;
 103                u32 ctrl, val, qlen = 0;
 104
 105                val = mt76_rr(dev, MT_PLE_AC_QEMPTY(acs, index));
 106                ctrl = BIT(31) | BIT(15) | (acs << 8);
 107
 108                for (j = 0; j < 32; j++) {
 109                        if (val & BIT(j))
 110                                continue;
 111
 112                        mt76_wr(dev, MT_PLE_FL_Q0_CTRL,
 113                                ctrl | (j + (index << 5)));
 114                        qlen += mt76_get_field(dev, MT_PLE_FL_Q3_CTRL,
 115                                               GENMASK(11, 0));
 116                }
 117                seq_printf(s, "AC%d%d: queued=%d\n", acs, index, qlen);
 118        }
 119
 120        return 0;
 121}
 122
 123static int
 124mt7921_queues_read(struct seq_file *s, void *data)
 125{
 126        struct mt7921_dev *dev = dev_get_drvdata(s->private);
 127        struct {
 128                struct mt76_queue *q;
 129                char *queue;
 130        } queue_map[] = {
 131                { dev->mphy.q_tx[MT_TXQ_BE],     "WFDMA0" },
 132                { dev->mt76.q_mcu[MT_MCUQ_WM],   "MCUWM"  },
 133                { dev->mt76.q_mcu[MT_MCUQ_FWDL], "MCUFWQ" },
 134        };
 135        int i;
 136
 137        for (i = 0; i < ARRAY_SIZE(queue_map); i++) {
 138                struct mt76_queue *q = queue_map[i].q;
 139
 140                if (!q)
 141                        continue;
 142
 143                seq_printf(s,
 144                           "%s: queued=%d head=%d tail=%d\n",
 145                           queue_map[i].queue, q->queued, q->head,
 146                           q->tail);
 147        }
 148
 149        return 0;
 150}
 151
 152static void
 153mt7921_seq_puts_array(struct seq_file *file, const char *str,
 154                      s8 *val, int len)
 155{
 156        int i;
 157
 158        seq_printf(file, "%-16s:", str);
 159        for (i = 0; i < len; i++)
 160                if (val[i] == 127)
 161                        seq_printf(file, " %6s", "N.A");
 162                else
 163                        seq_printf(file, " %6d", val[i]);
 164        seq_puts(file, "\n");
 165}
 166
 167#define mt7921_print_txpwr_entry(prefix, rate)                          \
 168({                                                                      \
 169        mt7921_seq_puts_array(s, #prefix " (user)",                     \
 170                              txpwr.data[TXPWR_USER].rate,              \
 171                              ARRAY_SIZE(txpwr.data[TXPWR_USER].rate)); \
 172        mt7921_seq_puts_array(s, #prefix " (eeprom)",                   \
 173                              txpwr.data[TXPWR_EEPROM].rate,            \
 174                              ARRAY_SIZE(txpwr.data[TXPWR_EEPROM].rate)); \
 175        mt7921_seq_puts_array(s, #prefix " (tmac)",                     \
 176                              txpwr.data[TXPWR_MAC].rate,               \
 177                              ARRAY_SIZE(txpwr.data[TXPWR_MAC].rate));  \
 178})
 179
 180static int
 181mt7921_txpwr(struct seq_file *s, void *data)
 182{
 183        struct mt7921_dev *dev = dev_get_drvdata(s->private);
 184        struct mt7921_txpwr txpwr;
 185        int ret;
 186
 187        mt7921_mutex_acquire(dev);
 188        ret = mt7921_get_txpwr_info(dev, &txpwr);
 189        mt7921_mutex_release(dev);
 190
 191        if (ret)
 192                return ret;
 193
 194        seq_printf(s, "Tx power table (channel %d)\n", txpwr.ch);
 195        seq_printf(s, "%-16s  %6s %6s %6s %6s\n",
 196                   " ", "1m", "2m", "5m", "11m");
 197        mt7921_print_txpwr_entry(CCK, cck);
 198
 199        seq_printf(s, "%-16s  %6s %6s %6s %6s %6s %6s %6s %6s\n",
 200                   " ", "6m", "9m", "12m", "18m", "24m", "36m",
 201                   "48m", "54m");
 202        mt7921_print_txpwr_entry(OFDM, ofdm);
 203
 204        seq_printf(s, "%-16s  %6s %6s %6s %6s %6s %6s %6s %6s\n",
 205                   " ", "mcs0", "mcs1", "mcs2", "mcs3", "mcs4", "mcs5",
 206                   "mcs6", "mcs7");
 207        mt7921_print_txpwr_entry(HT20, ht20);
 208
 209        seq_printf(s, "%-16s  %6s %6s %6s %6s %6s %6s %6s %6s %6s\n",
 210                   " ", "mcs0", "mcs1", "mcs2", "mcs3", "mcs4", "mcs5",
 211                   "mcs6", "mcs7", "mcs32");
 212        mt7921_print_txpwr_entry(HT40, ht40);
 213
 214        seq_printf(s, "%-16s  %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s\n",
 215                   " ", "mcs0", "mcs1", "mcs2", "mcs3", "mcs4", "mcs5",
 216                   "mcs6", "mcs7", "mcs8", "mcs9", "mcs10", "mcs11");
 217        mt7921_print_txpwr_entry(VHT20, vht20);
 218        mt7921_print_txpwr_entry(VHT40, vht40);
 219        mt7921_print_txpwr_entry(VHT80, vht80);
 220        mt7921_print_txpwr_entry(VHT160, vht160);
 221        mt7921_print_txpwr_entry(HE26, he26);
 222        mt7921_print_txpwr_entry(HE52, he52);
 223        mt7921_print_txpwr_entry(HE106, he106);
 224        mt7921_print_txpwr_entry(HE242, he242);
 225        mt7921_print_txpwr_entry(HE484, he484);
 226        mt7921_print_txpwr_entry(HE996, he996);
 227        mt7921_print_txpwr_entry(HE996x2, he996x2);
 228
 229        return 0;
 230}
 231
 232static int
 233mt7921_pm_set(void *data, u64 val)
 234{
 235        struct mt7921_dev *dev = data;
 236        struct mt76_connac_pm *pm = &dev->pm;
 237        struct mt76_phy *mphy = dev->phy.mt76;
 238
 239        if (val == pm->enable)
 240                return 0;
 241
 242        mt7921_mutex_acquire(dev);
 243
 244        if (!pm->enable) {
 245                pm->stats.last_wake_event = jiffies;
 246                pm->stats.last_doze_event = jiffies;
 247        }
 248        pm->enable = val;
 249
 250        ieee80211_iterate_active_interfaces(mphy->hw,
 251                                            IEEE80211_IFACE_ITER_RESUME_ALL,
 252                                            mt7921_pm_interface_iter, mphy->priv);
 253
 254        mt76_connac_mcu_set_deep_sleep(&dev->mt76, pm->ds_enable);
 255
 256        mt7921_mutex_release(dev);
 257
 258        return 0;
 259}
 260
 261static int
 262mt7921_pm_get(void *data, u64 *val)
 263{
 264        struct mt7921_dev *dev = data;
 265
 266        *val = dev->pm.enable;
 267
 268        return 0;
 269}
 270
 271DEFINE_DEBUGFS_ATTRIBUTE(fops_pm, mt7921_pm_get, mt7921_pm_set, "%lld\n");
 272
 273static int
 274mt7921_deep_sleep_set(void *data, u64 val)
 275{
 276        struct mt7921_dev *dev = data;
 277        struct mt76_connac_pm *pm = &dev->pm;
 278        bool enable = !!val;
 279
 280        mt7921_mutex_acquire(dev);
 281        if (pm->ds_enable != enable) {
 282                mt76_connac_mcu_set_deep_sleep(&dev->mt76, enable);
 283                pm->ds_enable = enable;
 284        }
 285        mt7921_mutex_release(dev);
 286
 287        return 0;
 288}
 289
 290static int
 291mt7921_deep_sleep_get(void *data, u64 *val)
 292{
 293        struct mt7921_dev *dev = data;
 294
 295        *val = dev->pm.ds_enable;
 296
 297        return 0;
 298}
 299
 300DEFINE_DEBUGFS_ATTRIBUTE(fops_ds, mt7921_deep_sleep_get,
 301                         mt7921_deep_sleep_set, "%lld\n");
 302
 303static int
 304mt7921_pm_stats(struct seq_file *s, void *data)
 305{
 306        struct mt7921_dev *dev = dev_get_drvdata(s->private);
 307        struct mt76_connac_pm *pm = &dev->pm;
 308
 309        unsigned long awake_time = pm->stats.awake_time;
 310        unsigned long doze_time = pm->stats.doze_time;
 311
 312        if (!test_bit(MT76_STATE_PM, &dev->mphy.state))
 313                awake_time += jiffies - pm->stats.last_wake_event;
 314        else
 315                doze_time += jiffies - pm->stats.last_doze_event;
 316
 317        seq_printf(s, "awake time: %14u\ndoze time: %15u\n",
 318                   jiffies_to_msecs(awake_time),
 319                   jiffies_to_msecs(doze_time));
 320
 321        seq_printf(s, "low power wakes: %9d\n", pm->stats.lp_wake);
 322
 323        return 0;
 324}
 325
 326static int
 327mt7921_pm_idle_timeout_set(void *data, u64 val)
 328{
 329        struct mt7921_dev *dev = data;
 330
 331        dev->pm.idle_timeout = msecs_to_jiffies(val);
 332
 333        return 0;
 334}
 335
 336static int
 337mt7921_pm_idle_timeout_get(void *data, u64 *val)
 338{
 339        struct mt7921_dev *dev = data;
 340
 341        *val = jiffies_to_msecs(dev->pm.idle_timeout);
 342
 343        return 0;
 344}
 345
 346DEFINE_DEBUGFS_ATTRIBUTE(fops_pm_idle_timeout, mt7921_pm_idle_timeout_get,
 347                         mt7921_pm_idle_timeout_set, "%lld\n");
 348
 349static int mt7921_chip_reset(void *data, u64 val)
 350{
 351        struct mt7921_dev *dev = data;
 352        int ret = 0;
 353
 354        switch (val) {
 355        case 1:
 356                /* Reset wifisys directly. */
 357                mt7921_reset(&dev->mt76);
 358                break;
 359        default:
 360                /* Collect the core dump before reset wifisys. */
 361                mt7921_mutex_acquire(dev);
 362                ret = mt76_connac_mcu_chip_config(&dev->mt76);
 363                mt7921_mutex_release(dev);
 364                break;
 365        }
 366
 367        return ret;
 368}
 369
 370DEFINE_DEBUGFS_ATTRIBUTE(fops_reset, NULL, mt7921_chip_reset, "%lld\n");
 371
 372int mt7921_init_debugfs(struct mt7921_dev *dev)
 373{
 374        struct dentry *dir;
 375
 376        dir = mt76_register_debugfs(&dev->mt76);
 377        if (!dir)
 378                return -ENOMEM;
 379
 380        debugfs_create_devm_seqfile(dev->mt76.dev, "queues", dir,
 381                                    mt7921_queues_read);
 382        debugfs_create_devm_seqfile(dev->mt76.dev, "acq", dir,
 383                                    mt7921_queues_acq);
 384        debugfs_create_devm_seqfile(dev->mt76.dev, "txpower_sku", dir,
 385                                    mt7921_txpwr);
 386        debugfs_create_file("tx_stats", 0400, dir, dev, &mt7921_tx_stats_fops);
 387        debugfs_create_file("fw_debug", 0600, dir, dev, &fops_fw_debug);
 388        debugfs_create_file("runtime-pm", 0600, dir, dev, &fops_pm);
 389        debugfs_create_file("idle-timeout", 0600, dir, dev,
 390                            &fops_pm_idle_timeout);
 391        debugfs_create_file("chip_reset", 0600, dir, dev, &fops_reset);
 392        debugfs_create_devm_seqfile(dev->mt76.dev, "runtime_pm_stats", dir,
 393                                    mt7921_pm_stats);
 394        debugfs_create_file("deep-sleep", 0600, dir, dev, &fops_ds);
 395
 396        return 0;
 397}
 398