From 034dd340b08be1f2f0477ad16131d609f9dbd53c Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 30 Aug 2026 09:22:00 -0700 Subject: Merge tag 'trace-v7.3-2' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace Pull tracing fixes from Steven Rostedt: - Fix error output of boot instance creation failure Currently if a boot instance creation fails, instead of printing out the name of the instance that failed, it prints "(null)". That is because it prints "cur_str" that had already been processed by strsep(). Print the saved name instead. While at it, print the error code of the failure. - Fix use-after-free for same named historgrams Histograms can be named so that they can be used in multiple events. But if the named histogram has a variable attached, the second event that uses the named histogram which duplicates it and needs to free the original after duplication leaves the old variable in place and still visible. If another histogram uses than variable, it will use the stale one which will try to reference the freed duplicate histogram and crash the kernel. Free the duplicate variables along with the duplicated histogram data. - Check return value of kthread_run() in event self test The events self tests uses a kthread for testing but does not check if it succeeded in creating a kthread. If the kthread creation were to fail, the code will still try to call kthread_stop() on the error returned. - Fix race between reading trace_pipe and updating subbuffer size If a user is reading the trace_pipe file at the same time they update the ring buffer sub-buffer size, can cause the trace_pipe read to read stale data. Add trace_access_lock() around updating the ring buffer sub-buffer size. - Fix eventfs_inode on failure path in creation of the events directory In the creation of the "events" directory, if after allocating the eventfs_inode a failure is detected, it calls cleanup_ei() which calls free_ei(). The free_ei() will test if eventfs_inode being freed has no children. It is a bug if it does. But on the failure case of the creation of the "events" directory, the children lists have not yet been initialized and the free will trigger a warning because list_empty() on an uninitialized list returns false. Move the initialization into init_ei() where it makes more sense and makes sure that a created eventfs_inode has its lists initialized upon creation. - Check return value of kthread_run() in ftrace direct sample code The sample code that shows how to use the ftrace direct calls does not test the return of kthread_run() to see if it succeeds. Return a failure if the kthread_run() doesn't succeed. - Clear user events state on fork in case of alloc failure On fork, the child gets a pointer to the parent's user events state. It makes a copy of it then updates the child's pointer to it. But if the allocation fails, the duplication function leaves the child with a pointer to its parent's descriptor. When the child cleans up its data, it will free the parent's descriptor while the parent is still using it. In the duplication function, set the child's user_event_mm to NULL before testing if the allocation succeeded, and when it exits it will not free the parent's descriptor. - Fix retry exhaustion in simple ring buffer reader swap simple_ring_buffer_swap_reader_page() starts with retry set to 8 and post-decrements it only after a failed link replacement. On the final attempt, a successful replacement leaves retry at zero, while a failed replacement leaves it at -1. But the check for success expects the retry value to be non-zero and exits with an error on zero. This is the opposite result. Fix it. - Fail nicely when the remote swap_reader_page() returns an error Currently, if the swap_reader_page() of a remote buffer fails, it triggers a WARN_ON_ONCE() and continues normally. Instead, have it exit with an error and a pr_warn() print instead of a full WARNING. * tag 'trace-v7.3-2' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace: ring-buffer: Stop remote reader update when page swap fails tracing: Fix retry exhaustion in simple ring buffer reader swap tracing/user_events: Clear copied tracing state before fork duplication samples/ftrace: Fix kthread_stop() on ERR_PTR in ftrace-direct-multi-modify samples/ftrace: Fix kthread_stop() on ERR_PTR in ftrace-direct-modify eventfs: Initialize ei->children and ei->list in init_ei() tracing: Fix use-after-free in trace_pipe read on sub-buffer order change tracing: Fix crash passing ERR_PTR to kthread_stop() tracing: Fix use-after-free with same-name named triggers tracing: Fix logged instance name on creation failure --- drivers/net/wireless/mediatek/Kconfig | 16 + drivers/net/wireless/mediatek/Makefile | 3 + drivers/net/wireless/mediatek/mt76/Kconfig | 51 + drivers/net/wireless/mediatek/mt76/Makefile | 52 + drivers/net/wireless/mediatek/mt76/agg-rx.c | 303 ++ drivers/net/wireless/mediatek/mt76/channel.c | 433 ++ drivers/net/wireless/mediatek/mt76/debugfs.c | 126 + drivers/net/wireless/mediatek/mt76/dma.c | 1237 +++++ drivers/net/wireless/mediatek/mt76/dma.h | 220 + drivers/net/wireless/mediatek/mt76/eeprom.c | 531 ++ drivers/net/wireless/mediatek/mt76/mac80211.c | 2376 +++++++++ drivers/net/wireless/mediatek/mt76/mcu.c | 165 + drivers/net/wireless/mediatek/mt76/mmio.c | 128 + drivers/net/wireless/mediatek/mt76/mt76.h | 2167 ++++++++ drivers/net/wireless/mediatek/mt76/mt7603/Kconfig | 12 + drivers/net/wireless/mediatek/mt76/mt7603/Makefile | 7 + drivers/net/wireless/mediatek/mt76/mt7603/beacon.c | 227 + drivers/net/wireless/mediatek/mt76/mt7603/core.c | 67 + .../net/wireless/mediatek/mt76/mt7603/debugfs.c | 118 + drivers/net/wireless/mediatek/mt76/mt7603/dma.c | 260 + drivers/net/wireless/mediatek/mt76/mt7603/eeprom.c | 188 + drivers/net/wireless/mediatek/mt76/mt7603/eeprom.h | 91 + drivers/net/wireless/mediatek/mt76/mt7603/init.c | 571 ++ drivers/net/wireless/mediatek/mt76/mt7603/mac.c | 1946 +++++++ drivers/net/wireless/mediatek/mt76/mt7603/mac.h | 242 + drivers/net/wireless/mediatek/mt76/mt7603/main.c | 818 +++ drivers/net/wireless/mediatek/mt76/mt7603/mcu.c | 432 ++ drivers/net/wireless/mediatek/mt76/mt7603/mcu.h | 103 + drivers/net/wireless/mediatek/mt76/mt7603/mt7603.h | 266 + drivers/net/wireless/mediatek/mt76/mt7603/pci.c | 83 + drivers/net/wireless/mediatek/mt76/mt7603/regs.h | 780 +++ drivers/net/wireless/mediatek/mt76/mt7603/soc.c | 79 + drivers/net/wireless/mediatek/mt76/mt7615/Kconfig | 56 + drivers/net/wireless/mediatek/mt76/mt7615/Makefile | 20 + .../net/wireless/mediatek/mt76/mt7615/debugfs.c | 607 +++ drivers/net/wireless/mediatek/mt76/mt7615/dma.c | 316 ++ drivers/net/wireless/mediatek/mt76/mt7615/eeprom.c | 356 ++ drivers/net/wireless/mediatek/mt76/mt7615/eeprom.h | 116 + drivers/net/wireless/mediatek/mt76/mt7615/init.c | 649 +++ drivers/net/wireless/mediatek/mt76/mt7615/mac.c | 2351 +++++++++ drivers/net/wireless/mediatek/mt76/mt7615/mac.h | 325 ++ drivers/net/wireless/mediatek/mt76/mt7615/main.c | 1374 +++++ drivers/net/wireless/mediatek/mt76/mt7615/mcu.c | 2613 +++++++++ drivers/net/wireless/mediatek/mt76/mt7615/mcu.h | 243 + drivers/net/wireless/mediatek/mt76/mt7615/mmio.c | 275 + drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h | 557 ++ .../wireless/mediatek/mt76/mt7615/mt7615_trace.h | 56 + drivers/net/wireless/mediatek/mt76/mt7615/pci.c | 206 + .../net/wireless/mediatek/mt76/mt7615/pci_init.c | 128 + .../net/wireless/mediatek/mt76/mt7615/pci_mac.c | 296 ++ drivers/net/wireless/mediatek/mt76/mt7615/regs.h | 619 +++ drivers/net/wireless/mediatek/mt76/mt7615/sdio.c | 258 + .../net/wireless/mediatek/mt76/mt7615/sdio_mcu.c | 180 + drivers/net/wireless/mediatek/mt76/mt7615/soc.c | 70 + .../net/wireless/mediatek/mt76/mt7615/testmode.c | 376 ++ drivers/net/wireless/mediatek/mt76/mt7615/trace.c | 12 + drivers/net/wireless/mediatek/mt76/mt7615/usb.c | 283 + .../net/wireless/mediatek/mt76/mt7615/usb_mcu.c | 100 + .../net/wireless/mediatek/mt76/mt7615/usb_sdio.c | 354 ++ drivers/net/wireless/mediatek/mt76/mt76_connac.h | 499 ++ .../net/wireless/mediatek/mt76/mt76_connac2_mac.h | 365 ++ .../net/wireless/mediatek/mt76/mt76_connac3_mac.c | 268 + .../net/wireless/mediatek/mt76/mt76_connac3_mac.h | 442 ++ .../net/wireless/mediatek/mt76/mt76_connac_mac.c | 1230 +++++ .../net/wireless/mediatek/mt76/mt76_connac_mcu.c | 3568 +++++++++++++ .../net/wireless/mediatek/mt76/mt76_connac_mcu.h | 2155 ++++++++ drivers/net/wireless/mediatek/mt76/mt76x0/Kconfig | 29 + drivers/net/wireless/mediatek/mt76/mt76x0/Makefile | 11 + drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.c | 350 ++ drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.h | 41 + drivers/net/wireless/mediatek/mt76/mt76x0/init.c | 266 + .../net/wireless/mediatek/mt76/mt76x0/initvals.h | 85 + .../wireless/mediatek/mt76/mt76x0/initvals_init.h | 159 + .../wireless/mediatek/mt76/mt76x0/initvals_phy.h | 633 +++ drivers/net/wireless/mediatek/mt76/mt76x0/main.c | 93 + drivers/net/wireless/mediatek/mt76/mt76x0/mcu.h | 46 + drivers/net/wireless/mediatek/mt76/mt76x0/mt76x0.h | 63 + drivers/net/wireless/mediatek/mt76/mt76x0/pci.c | 327 ++ .../net/wireless/mediatek/mt76/mt76x0/pci_mcu.c | 133 + drivers/net/wireless/mediatek/mt76/mt76x0/phy.c | 1215 +++++ drivers/net/wireless/mediatek/mt76/mt76x0/phy.h | 90 + drivers/net/wireless/mediatek/mt76/mt76x0/usb.c | 356 ++ .../net/wireless/mediatek/mt76/mt76x0/usb_mcu.c | 175 + drivers/net/wireless/mediatek/mt76/mt76x02.h | 284 + .../net/wireless/mediatek/mt76/mt76x02_beacon.c | 215 + .../net/wireless/mediatek/mt76/mt76x02_debugfs.c | 157 + drivers/net/wireless/mediatek/mt76/mt76x02_dfs.c | 892 ++++ drivers/net/wireless/mediatek/mt76/mt76x02_dfs.h | 132 + drivers/net/wireless/mediatek/mt76/mt76x02_dma.h | 65 + .../net/wireless/mediatek/mt76/mt76x02_eeprom.c | 147 + .../net/wireless/mediatek/mt76/mt76x02_eeprom.h | 185 + drivers/net/wireless/mediatek/mt76/mt76x02_mac.c | 1241 +++++ drivers/net/wireless/mediatek/mt76/mt76x02_mac.h | 208 + drivers/net/wireless/mediatek/mt76/mt76x02_mcu.c | 171 + drivers/net/wireless/mediatek/mt76/mt76x02_mcu.h | 100 + drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c | 566 ++ drivers/net/wireless/mediatek/mt76/mt76x02_phy.c | 204 + drivers/net/wireless/mediatek/mt76/mt76x02_phy.h | 49 + drivers/net/wireless/mediatek/mt76/mt76x02_regs.h | 708 +++ drivers/net/wireless/mediatek/mt76/mt76x02_trace.c | 12 + drivers/net/wireless/mediatek/mt76/mt76x02_trace.h | 87 + drivers/net/wireless/mediatek/mt76/mt76x02_txrx.c | 183 + drivers/net/wireless/mediatek/mt76/mt76x02_usb.h | 25 + .../net/wireless/mediatek/mt76/mt76x02_usb_core.c | 286 + .../net/wireless/mediatek/mt76/mt76x02_usb_mcu.c | 297 ++ drivers/net/wireless/mediatek/mt76/mt76x02_util.c | 699 +++ drivers/net/wireless/mediatek/mt76/mt76x2/Kconfig | 29 + drivers/net/wireless/mediatek/mt76/mt76x2/Makefile | 15 + drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.c | 512 ++ drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.h | 83 + drivers/net/wireless/mediatek/mt76/mt76x2/init.c | 204 + drivers/net/wireless/mediatek/mt76/mt76x2/mac.c | 46 + drivers/net/wireless/mediatek/mt76/mt76x2/mac.h | 24 + drivers/net/wireless/mediatek/mt76/mt76x2/mcu.c | 108 + drivers/net/wireless/mediatek/mt76/mt76x2/mcu.h | 68 + drivers/net/wireless/mediatek/mt76/mt76x2/mt76x2.h | 82 + .../net/wireless/mediatek/mt76/mt76x2/mt76x2u.h | 43 + drivers/net/wireless/mediatek/mt76/mt76x2/pci.c | 187 + .../net/wireless/mediatek/mt76/mt76x2/pci_init.c | 320 ++ .../net/wireless/mediatek/mt76/mt76x2/pci_main.c | 157 + .../net/wireless/mediatek/mt76/mt76x2/pci_mcu.c | 198 + .../net/wireless/mediatek/mt76/mt76x2/pci_phy.c | 311 ++ drivers/net/wireless/mediatek/mt76/mt76x2/phy.c | 349 ++ drivers/net/wireless/mediatek/mt76/mt76x2/usb.c | 154 + .../net/wireless/mediatek/mt76/mt76x2/usb_init.c | 261 + .../net/wireless/mediatek/mt76/mt76x2/usb_mac.c | 174 + .../net/wireless/mediatek/mt76/mt76x2/usb_main.c | 118 + .../net/wireless/mediatek/mt76/mt76x2/usb_mcu.c | 255 + .../net/wireless/mediatek/mt76/mt76x2/usb_phy.c | 201 + drivers/net/wireless/mediatek/mt76/mt7915/Kconfig | 25 + drivers/net/wireless/mediatek/mt76/mt7915/Makefile | 10 + .../net/wireless/mediatek/mt76/mt7915/coredump.c | 411 ++ .../net/wireless/mediatek/mt76/mt7915/coredump.h | 136 + .../net/wireless/mediatek/mt76/mt7915/debugfs.c | 1475 ++++++ drivers/net/wireless/mediatek/mt76/mt7915/dma.c | 655 +++ drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c | 409 ++ drivers/net/wireless/mediatek/mt76/mt7915/eeprom.h | 203 + drivers/net/wireless/mediatek/mt76/mt7915/init.c | 1342 +++++ drivers/net/wireless/mediatek/mt76/mt7915/mac.c | 2411 +++++++++ drivers/net/wireless/mediatek/mt76/mt7915/mac.h | 75 + drivers/net/wireless/mediatek/mt76/mt7915/main.c | 1835 +++++++ drivers/net/wireless/mediatek/mt76/mt7915/mcu.c | 4231 +++++++++++++++ drivers/net/wireless/mediatek/mt76/mt7915/mcu.h | 544 ++ drivers/net/wireless/mediatek/mt76/mt7915/mmio.c | 1005 ++++ drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h | 645 +++ drivers/net/wireless/mediatek/mt76/mt7915/pci.c | 245 + drivers/net/wireless/mediatek/mt76/mt7915/regs.h | 1230 +++++ drivers/net/wireless/mediatek/mt76/mt7915/soc.c | 1312 +++++ .../net/wireless/mediatek/mt76/mt7915/testmode.c | 787 +++ .../net/wireless/mediatek/mt76/mt7915/testmode.h | 105 + drivers/net/wireless/mediatek/mt76/mt7921/Kconfig | 37 + drivers/net/wireless/mediatek/mt76/mt7921/Makefile | 12 + .../net/wireless/mediatek/mt76/mt7921/debugfs.c | 294 ++ drivers/net/wireless/mediatek/mt76/mt7921/init.c | 256 + drivers/net/wireless/mediatek/mt76/mt7921/mac.c | 881 ++++ drivers/net/wireless/mediatek/mt76/mt7921/main.c | 1618 ++++++ drivers/net/wireless/mediatek/mt76/mt7921/mcu.c | 1518 ++++++ drivers/net/wireless/mediatek/mt76/mt7921/mcu.h | 120 + drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h | 353 ++ drivers/net/wireless/mediatek/mt76/mt7921/pci.c | 743 +++ .../net/wireless/mediatek/mt76/mt7921/pci_mac.c | 130 + .../net/wireless/mediatek/mt76/mt7921/pci_mcu.c | 63 + drivers/net/wireless/mediatek/mt76/mt7921/regd.c | 399 ++ drivers/net/wireless/mediatek/mt76/mt7921/regd.h | 70 + drivers/net/wireless/mediatek/mt76/mt7921/regs.h | 86 + drivers/net/wireless/mediatek/mt76/mt7921/sdio.c | 337 ++ .../net/wireless/mediatek/mt76/mt7921/sdio_mac.c | 199 + .../net/wireless/mediatek/mt76/mt7921/sdio_mcu.c | 175 + .../net/wireless/mediatek/mt76/mt7921/testmode.c | 196 + drivers/net/wireless/mediatek/mt76/mt7921/usb.c | 355 ++ drivers/net/wireless/mediatek/mt76/mt7925/Kconfig | 30 + drivers/net/wireless/mediatek/mt76/mt7925/Makefile | 10 + .../net/wireless/mediatek/mt76/mt7925/debugfs.c | 317 ++ drivers/net/wireless/mediatek/mt76/mt7925/init.c | 330 ++ drivers/net/wireless/mediatek/mt76/mt7925/mac.c | 1737 ++++++ drivers/net/wireless/mediatek/mt76/mt7925/mac.h | 30 + drivers/net/wireless/mediatek/mt76/mt7925/main.c | 2794 ++++++++++ drivers/net/wireless/mediatek/mt76/mt7925/mcu.c | 4174 +++++++++++++++ drivers/net/wireless/mediatek/mt76/mt7925/mcu.h | 765 +++ drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h | 417 ++ drivers/net/wireless/mediatek/mt76/mt7925/nan.c | 1098 ++++ drivers/net/wireless/mediatek/mt76/mt7925/nan.h | 421 ++ drivers/net/wireless/mediatek/mt76/mt7925/pci.c | 937 ++++ .../net/wireless/mediatek/mt76/mt7925/pci_mac.c | 159 + .../net/wireless/mediatek/mt76/mt7925/pci_mcu.c | 53 + drivers/net/wireless/mediatek/mt76/mt7925/regd.c | 480 ++ drivers/net/wireless/mediatek/mt76/mt7925/regd.h | 76 + drivers/net/wireless/mediatek/mt76/mt7925/regs.h | 139 + .../net/wireless/mediatek/mt76/mt7925/testmode.c | 206 + drivers/net/wireless/mediatek/mt76/mt7925/usb.c | 372 ++ drivers/net/wireless/mediatek/mt76/mt792x.h | 660 +++ .../net/wireless/mediatek/mt76/mt792x_acpi_sar.c | 486 ++ .../net/wireless/mediatek/mt76/mt792x_acpi_sar.h | 123 + drivers/net/wireless/mediatek/mt76/mt792x_core.c | 1172 +++++ .../net/wireless/mediatek/mt76/mt792x_debugfs.c | 168 + drivers/net/wireless/mediatek/mt76/mt792x_dma.c | 626 +++ drivers/net/wireless/mediatek/mt76/mt792x_mac.c | 384 ++ drivers/net/wireless/mediatek/mt76/mt792x_regs.h | 529 ++ drivers/net/wireless/mediatek/mt76/mt792x_trace.c | 14 + drivers/net/wireless/mediatek/mt76/mt792x_trace.h | 51 + drivers/net/wireless/mediatek/mt76/mt792x_usb.c | 531 ++ drivers/net/wireless/mediatek/mt76/mt7996/Kconfig | 21 + drivers/net/wireless/mediatek/mt76/mt7996/Makefile | 9 + .../net/wireless/mediatek/mt76/mt7996/coredump.c | 268 + .../net/wireless/mediatek/mt76/mt7996/coredump.h | 97 + .../net/wireless/mediatek/mt76/mt7996/debugfs.c | 1078 ++++ drivers/net/wireless/mediatek/mt76/mt7996/dma.c | 1057 ++++ drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c | 421 ++ drivers/net/wireless/mediatek/mt76/mt7996/eeprom.h | 79 + drivers/net/wireless/mediatek/mt76/mt7996/init.c | 1831 +++++++ drivers/net/wireless/mediatek/mt76/mt7996/mac.c | 3328 ++++++++++++ drivers/net/wireless/mediatek/mt76/mt7996/mac.h | 40 + drivers/net/wireless/mediatek/mt76/mt7996/main.c | 2607 +++++++++ drivers/net/wireless/mediatek/mt76/mt7996/mcu.c | 5560 ++++++++++++++++++++ drivers/net/wireless/mediatek/mt76/mt7996/mcu.h | 1108 ++++ drivers/net/wireless/mediatek/mt76/mt7996/mmio.c | 909 ++++ drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h | 979 ++++ drivers/net/wireless/mediatek/mt76/mt7996/npu.c | 631 +++ drivers/net/wireless/mediatek/mt76/mt7996/pci.c | 269 + drivers/net/wireless/mediatek/mt76/mt7996/regs.h | 810 +++ drivers/net/wireless/mediatek/mt76/npu.c | 528 ++ drivers/net/wireless/mediatek/mt76/pci.c | 76 + drivers/net/wireless/mediatek/mt76/scan.c | 244 + drivers/net/wireless/mediatek/mt76/sdio.c | 690 +++ drivers/net/wireless/mediatek/mt76/sdio.h | 140 + drivers/net/wireless/mediatek/mt76/sdio_txrx.c | 390 ++ drivers/net/wireless/mediatek/mt76/testmode.c | 676 +++ drivers/net/wireless/mediatek/mt76/testmode.h | 200 + drivers/net/wireless/mediatek/mt76/trace.c | 15 + drivers/net/wireless/mediatek/mt76/trace.h | 111 + drivers/net/wireless/mediatek/mt76/tx.c | 1027 ++++ drivers/net/wireless/mediatek/mt76/usb.c | 1202 +++++ drivers/net/wireless/mediatek/mt76/usb_trace.c | 12 + drivers/net/wireless/mediatek/mt76/usb_trace.h | 86 + drivers/net/wireless/mediatek/mt76/util.c | 138 + drivers/net/wireless/mediatek/mt76/util.h | 115 + drivers/net/wireless/mediatek/mt76/wed.c | 214 + drivers/net/wireless/mediatek/mt7601u/Kconfig | 7 + drivers/net/wireless/mediatek/mt7601u/Makefile | 8 + drivers/net/wireless/mediatek/mt7601u/core.c | 70 + drivers/net/wireless/mediatek/mt7601u/debugfs.c | 138 + drivers/net/wireless/mediatek/mt7601u/dma.c | 551 ++ drivers/net/wireless/mediatek/mt7601u/dma.h | 117 + drivers/net/wireless/mediatek/mt7601u/eeprom.c | 390 ++ drivers/net/wireless/mediatek/mt7601u/eeprom.h | 143 + drivers/net/wireless/mediatek/mt7601u/init.c | 631 +++ drivers/net/wireless/mediatek/mt7601u/initvals.h | 157 + .../net/wireless/mediatek/mt7601u/initvals_phy.h | 283 + drivers/net/wireless/mediatek/mt7601u/mac.c | 593 +++ drivers/net/wireless/mediatek/mt7601u/mac.h | 171 + drivers/net/wireless/mediatek/mt7601u/main.c | 432 ++ drivers/net/wireless/mediatek/mt7601u/mcu.c | 548 ++ drivers/net/wireless/mediatek/mt7601u/mcu.h | 86 + drivers/net/wireless/mediatek/mt7601u/mt7601u.h | 393 ++ drivers/net/wireless/mediatek/mt7601u/phy.c | 1258 +++++ drivers/net/wireless/mediatek/mt7601u/regs.h | 627 +++ drivers/net/wireless/mediatek/mt7601u/trace.c | 13 + drivers/net/wireless/mediatek/mt7601u/trace.h | 392 ++ drivers/net/wireless/mediatek/mt7601u/tx.c | 316 ++ drivers/net/wireless/mediatek/mt7601u/usb.c | 379 ++ drivers/net/wireless/mediatek/mt7601u/usb.h | 72 + drivers/net/wireless/mediatek/mt7601u/util.c | 34 + 262 files changed, 131302 insertions(+) create mode 100644 drivers/net/wireless/mediatek/Kconfig create mode 100644 drivers/net/wireless/mediatek/Makefile create mode 100644 drivers/net/wireless/mediatek/mt76/Kconfig create mode 100644 drivers/net/wireless/mediatek/mt76/Makefile create mode 100644 drivers/net/wireless/mediatek/mt76/agg-rx.c create mode 100644 drivers/net/wireless/mediatek/mt76/channel.c create mode 100644 drivers/net/wireless/mediatek/mt76/debugfs.c create mode 100644 drivers/net/wireless/mediatek/mt76/dma.c create mode 100644 drivers/net/wireless/mediatek/mt76/dma.h create mode 100644 drivers/net/wireless/mediatek/mt76/eeprom.c create mode 100644 drivers/net/wireless/mediatek/mt76/mac80211.c create mode 100644 drivers/net/wireless/mediatek/mt76/mcu.c create mode 100644 drivers/net/wireless/mediatek/mt76/mmio.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt76.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt7603/Kconfig create mode 100644 drivers/net/wireless/mediatek/mt76/mt7603/Makefile create mode 100644 drivers/net/wireless/mediatek/mt76/mt7603/beacon.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7603/core.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7603/debugfs.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7603/dma.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7603/eeprom.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7603/eeprom.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt7603/init.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7603/mac.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7603/mac.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt7603/main.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7603/mcu.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7603/mcu.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt7603/mt7603.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt7603/pci.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7603/regs.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt7603/soc.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7615/Kconfig create mode 100644 drivers/net/wireless/mediatek/mt76/mt7615/Makefile create mode 100644 drivers/net/wireless/mediatek/mt76/mt7615/debugfs.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7615/dma.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7615/eeprom.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7615/eeprom.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt7615/init.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7615/mac.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7615/mac.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt7615/main.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7615/mcu.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7615/mcu.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt7615/mmio.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt7615/mt7615_trace.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt7615/pci.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7615/pci_init.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7615/pci_mac.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7615/regs.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt7615/sdio.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7615/sdio_mcu.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7615/soc.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7615/testmode.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7615/trace.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7615/usb.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7615/usb_mcu.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7615/usb_sdio.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt76_connac.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt76_connac2_mac.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt76_connac3_mac.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt76_connac3_mac.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x0/Kconfig create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x0/Makefile create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x0/init.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x0/initvals.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x0/initvals_init.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x0/initvals_phy.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x0/main.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x0/mcu.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x0/mt76x0.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x0/pci.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x0/pci_mcu.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x0/phy.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x0/phy.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x0/usb.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x0/usb_mcu.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x02.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x02_beacon.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x02_debugfs.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x02_dfs.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x02_dfs.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x02_dma.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x02_eeprom.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x02_eeprom.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x02_mac.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x02_mac.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x02_mcu.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x02_mcu.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x02_phy.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x02_phy.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x02_regs.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x02_trace.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x02_trace.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x02_txrx.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x02_usb.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x02_usb_core.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x02_usb_mcu.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x02_util.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x2/Kconfig create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x2/Makefile create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x2/init.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x2/mac.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x2/mac.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x2/mcu.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x2/mcu.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x2/mt76x2.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x2/mt76x2u.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x2/pci.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x2/pci_init.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x2/pci_main.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x2/pci_mcu.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x2/pci_phy.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x2/phy.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x2/usb.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x2/usb_init.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x2/usb_mac.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x2/usb_main.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x2/usb_mcu.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt76x2/usb_phy.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7915/Kconfig create mode 100644 drivers/net/wireless/mediatek/mt76/mt7915/Makefile create mode 100644 drivers/net/wireless/mediatek/mt76/mt7915/coredump.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7915/coredump.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7915/dma.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7915/eeprom.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt7915/init.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7915/mac.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7915/mac.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt7915/main.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7915/mcu.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7915/mcu.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt7915/mmio.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt7915/pci.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7915/regs.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt7915/soc.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7915/testmode.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7915/testmode.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt7921/Kconfig create mode 100644 drivers/net/wireless/mediatek/mt76/mt7921/Makefile create mode 100644 drivers/net/wireless/mediatek/mt76/mt7921/debugfs.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7921/init.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7921/mac.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7921/main.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7921/mcu.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7921/mcu.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt7921/pci.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7921/pci_mac.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7921/pci_mcu.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7921/regd.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7921/regd.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt7921/regs.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt7921/sdio.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7921/sdio_mac.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7921/sdio_mcu.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7921/testmode.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7921/usb.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7925/Kconfig create mode 100644 drivers/net/wireless/mediatek/mt76/mt7925/Makefile create mode 100644 drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7925/init.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7925/mac.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7925/mac.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt7925/main.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7925/mcu.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7925/mcu.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt7925/nan.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7925/nan.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt7925/pci.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7925/pci_mcu.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7925/regd.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7925/regd.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt7925/regs.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt7925/testmode.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7925/usb.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt792x.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt792x_core.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt792x_debugfs.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt792x_dma.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt792x_mac.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt792x_regs.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt792x_trace.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt792x_trace.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt792x_usb.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7996/Kconfig create mode 100644 drivers/net/wireless/mediatek/mt76/mt7996/Makefile create mode 100644 drivers/net/wireless/mediatek/mt76/mt7996/coredump.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7996/coredump.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7996/dma.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7996/eeprom.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt7996/init.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7996/mac.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7996/mac.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt7996/main.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7996/mcu.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7996/mcu.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt7996/mmio.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h create mode 100644 drivers/net/wireless/mediatek/mt76/mt7996/npu.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7996/pci.c create mode 100644 drivers/net/wireless/mediatek/mt76/mt7996/regs.h create mode 100644 drivers/net/wireless/mediatek/mt76/npu.c create mode 100644 drivers/net/wireless/mediatek/mt76/pci.c create mode 100644 drivers/net/wireless/mediatek/mt76/scan.c create mode 100644 drivers/net/wireless/mediatek/mt76/sdio.c create mode 100644 drivers/net/wireless/mediatek/mt76/sdio.h create mode 100644 drivers/net/wireless/mediatek/mt76/sdio_txrx.c create mode 100644 drivers/net/wireless/mediatek/mt76/testmode.c create mode 100644 drivers/net/wireless/mediatek/mt76/testmode.h create mode 100644 drivers/net/wireless/mediatek/mt76/trace.c create mode 100644 drivers/net/wireless/mediatek/mt76/trace.h create mode 100644 drivers/net/wireless/mediatek/mt76/tx.c create mode 100644 drivers/net/wireless/mediatek/mt76/usb.c create mode 100644 drivers/net/wireless/mediatek/mt76/usb_trace.c create mode 100644 drivers/net/wireless/mediatek/mt76/usb_trace.h create mode 100644 drivers/net/wireless/mediatek/mt76/util.c create mode 100644 drivers/net/wireless/mediatek/mt76/util.h create mode 100644 drivers/net/wireless/mediatek/mt76/wed.c create mode 100644 drivers/net/wireless/mediatek/mt7601u/Kconfig create mode 100644 drivers/net/wireless/mediatek/mt7601u/Makefile create mode 100644 drivers/net/wireless/mediatek/mt7601u/core.c create mode 100644 drivers/net/wireless/mediatek/mt7601u/debugfs.c create mode 100644 drivers/net/wireless/mediatek/mt7601u/dma.c create mode 100644 drivers/net/wireless/mediatek/mt7601u/dma.h create mode 100644 drivers/net/wireless/mediatek/mt7601u/eeprom.c create mode 100644 drivers/net/wireless/mediatek/mt7601u/eeprom.h create mode 100644 drivers/net/wireless/mediatek/mt7601u/init.c create mode 100644 drivers/net/wireless/mediatek/mt7601u/initvals.h create mode 100644 drivers/net/wireless/mediatek/mt7601u/initvals_phy.h create mode 100644 drivers/net/wireless/mediatek/mt7601u/mac.c create mode 100644 drivers/net/wireless/mediatek/mt7601u/mac.h create mode 100644 drivers/net/wireless/mediatek/mt7601u/main.c create mode 100644 drivers/net/wireless/mediatek/mt7601u/mcu.c create mode 100644 drivers/net/wireless/mediatek/mt7601u/mcu.h create mode 100644 drivers/net/wireless/mediatek/mt7601u/mt7601u.h create mode 100644 drivers/net/wireless/mediatek/mt7601u/phy.c create mode 100644 drivers/net/wireless/mediatek/mt7601u/regs.h create mode 100644 drivers/net/wireless/mediatek/mt7601u/trace.c create mode 100644 drivers/net/wireless/mediatek/mt7601u/trace.h create mode 100644 drivers/net/wireless/mediatek/mt7601u/tx.c create mode 100644 drivers/net/wireless/mediatek/mt7601u/usb.c create mode 100644 drivers/net/wireless/mediatek/mt7601u/usb.h create mode 100644 drivers/net/wireless/mediatek/mt7601u/util.c (limited to 'drivers/net/wireless/mediatek') diff --git a/drivers/net/wireless/mediatek/Kconfig b/drivers/net/wireless/mediatek/Kconfig new file mode 100644 index 000000000..bd4db1282 --- /dev/null +++ b/drivers/net/wireless/mediatek/Kconfig @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0-only +config WLAN_VENDOR_MEDIATEK + bool "MediaTek devices" + default y + help + If you have a wireless card belonging to this class, say Y. + + Note that the answer to this question doesn't directly affect the + kernel: saying N will just cause the configurator to skip all the + questions about these cards. If you say Y, you will be asked for + your specific card in the following questions. + +if WLAN_VENDOR_MEDIATEK +source "drivers/net/wireless/mediatek/mt7601u/Kconfig" +source "drivers/net/wireless/mediatek/mt76/Kconfig" +endif # WLAN_VENDOR_MEDIATEK diff --git a/drivers/net/wireless/mediatek/Makefile b/drivers/net/wireless/mediatek/Makefile new file mode 100644 index 000000000..806172659 --- /dev/null +++ b/drivers/net/wireless/mediatek/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_MT7601U) += mt7601u/ +obj-$(CONFIG_MT76_CORE) += mt76/ diff --git a/drivers/net/wireless/mediatek/mt76/Kconfig b/drivers/net/wireless/mediatek/mt76/Kconfig new file mode 100644 index 000000000..2ca96e052 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/Kconfig @@ -0,0 +1,51 @@ +# SPDX-License-Identifier: BSD-3-Clause-Clear +config MT76_CORE + tristate + select PAGE_POOL + +config MT76_LEDS + bool + depends on MT76_CORE + depends on LEDS_CLASS=y || MT76_CORE=LEDS_CLASS + default y + +config MT76_USB + tristate + depends on MT76_CORE + +config MT76_SDIO + tristate + depends on MT76_CORE + +config MT76x02_LIB + tristate + select MT76_CORE + +config MT76x02_USB + tristate + select MT76_USB + +config MT76_CONNAC_LIB + tristate + select MT76_CORE + +config MT792x_LIB + tristate + select MT76_CONNAC_LIB + +config MT792x_USB + tristate + select MT76_USB + +config MT76_NPU + tristate + depends on NET_AIROHA_NPU=y || MT76_CORE=NET_AIROHA_NPU + +source "drivers/net/wireless/mediatek/mt76/mt76x0/Kconfig" +source "drivers/net/wireless/mediatek/mt76/mt76x2/Kconfig" +source "drivers/net/wireless/mediatek/mt76/mt7603/Kconfig" +source "drivers/net/wireless/mediatek/mt76/mt7615/Kconfig" +source "drivers/net/wireless/mediatek/mt76/mt7915/Kconfig" +source "drivers/net/wireless/mediatek/mt76/mt7921/Kconfig" +source "drivers/net/wireless/mediatek/mt76/mt7996/Kconfig" +source "drivers/net/wireless/mediatek/mt76/mt7925/Kconfig" diff --git a/drivers/net/wireless/mediatek/mt76/Makefile b/drivers/net/wireless/mediatek/mt76/Makefile new file mode 100644 index 000000000..cacdd2b13 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/Makefile @@ -0,0 +1,52 @@ +# SPDX-License-Identifier: BSD-3-Clause-Clear +obj-$(CONFIG_MT76_CORE) += mt76.o +obj-$(CONFIG_MT76_USB) += mt76-usb.o +obj-$(CONFIG_MT76_SDIO) += mt76-sdio.o +obj-$(CONFIG_MT76x02_LIB) += mt76x02-lib.o +obj-$(CONFIG_MT76x02_USB) += mt76x02-usb.o +obj-$(CONFIG_MT76_CONNAC_LIB) += mt76-connac-lib.o +obj-$(CONFIG_MT792x_LIB) += mt792x-lib.o +obj-$(CONFIG_MT792x_USB) += mt792x-usb.o + +mt76-y := \ + mmio.o util.o trace.o dma.o mac80211.o debugfs.o eeprom.o \ + tx.o agg-rx.o mcu.o wed.o scan.o channel.o + +ifdef CONFIG_MT76_NPU +# CONFIG_MT76_NPU is tristate to simplify dependency tracking, +# but it behaves as a bool symbol here. +mt76-y += npu.o +endif +mt76-$(CONFIG_PCI) += pci.o +mt76-$(CONFIG_NL80211_TESTMODE) += testmode.o + +mt76-usb-y := usb.o usb_trace.o +mt76-sdio-y := sdio.o sdio_txrx.o + +CFLAGS_trace.o := -I$(src) +CFLAGS_usb_trace.o := -I$(src) +CFLAGS_mt76x02_trace.o := -I$(src) +CFLAGS_mt792x_trace.o := -I$(src) + +mt76x02-lib-y := mt76x02_util.o mt76x02_mac.o mt76x02_mcu.o \ + mt76x02_eeprom.o mt76x02_phy.o mt76x02_mmio.o \ + mt76x02_txrx.o mt76x02_trace.o mt76x02_debugfs.o \ + mt76x02_dfs.o mt76x02_beacon.o + +mt76x02-usb-y := mt76x02_usb_mcu.o mt76x02_usb_core.o + +mt76-connac-lib-y := mt76_connac_mcu.o mt76_connac_mac.o mt76_connac3_mac.o + +mt792x-lib-y := mt792x_core.o mt792x_mac.o mt792x_trace.o \ + mt792x_debugfs.o mt792x_dma.o +mt792x-lib-$(CONFIG_ACPI) += mt792x_acpi_sar.o +mt792x-usb-y := mt792x_usb.o + +obj-$(CONFIG_MT76x0_COMMON) += mt76x0/ +obj-$(CONFIG_MT76x2_COMMON) += mt76x2/ +obj-$(CONFIG_MT7603E) += mt7603/ +obj-$(CONFIG_MT7615_COMMON) += mt7615/ +obj-$(CONFIG_MT7915E) += mt7915/ +obj-$(CONFIG_MT7921_COMMON) += mt7921/ +obj-$(CONFIG_MT7996E) += mt7996/ +obj-$(CONFIG_MT7925_COMMON) += mt7925/ diff --git a/drivers/net/wireless/mediatek/mt76/agg-rx.c b/drivers/net/wireless/mediatek/mt76/agg-rx.c new file mode 100644 index 000000000..bf1babac3 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/agg-rx.c @@ -0,0 +1,303 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (C) 2018 Felix Fietkau + */ +#include "mt76.h" + +static unsigned long mt76_aggr_tid_to_timeo(u8 tidno) +{ + /* Currently voice traffic (AC_VO) always runs without aggregation, + * no special handling is needed. AC_BE/AC_BK use tids 0-3. Just check + * for non AC_BK/AC_BE and set smaller timeout for it. */ + return HZ / (tidno >= 4 ? 25 : 10); +} + +static void +mt76_aggr_release(struct mt76_rx_tid *tid, struct sk_buff_head *frames, int idx) +{ + struct sk_buff *skb; + + tid->head = ieee80211_sn_inc(tid->head); + + skb = tid->reorder_buf[idx]; + if (!skb) + return; + + tid->reorder_buf[idx] = NULL; + tid->nframes--; + __skb_queue_tail(frames, skb); +} + +static void +mt76_rx_aggr_release_frames(struct mt76_rx_tid *tid, + struct sk_buff_head *frames, + u16 head) +{ + int idx; + + while (ieee80211_sn_less(tid->head, head)) { + idx = tid->head % tid->size; + mt76_aggr_release(tid, frames, idx); + } +} + +static void +mt76_rx_aggr_release_head(struct mt76_rx_tid *tid, struct sk_buff_head *frames) +{ + int idx = tid->head % tid->size; + + while (tid->reorder_buf[idx]) { + mt76_aggr_release(tid, frames, idx); + idx = tid->head % tid->size; + } +} + +static void +mt76_rx_aggr_check_release(struct mt76_rx_tid *tid, struct sk_buff_head *frames) +{ + struct mt76_rx_status *status; + struct sk_buff *skb; + int start, idx, nframes; + + if (!tid->nframes) + return; + + mt76_rx_aggr_release_head(tid, frames); + + start = tid->head % tid->size; + nframes = tid->nframes; + + for (idx = (tid->head + 1) % tid->size; + idx != start && nframes; + idx = (idx + 1) % tid->size) { + skb = tid->reorder_buf[idx]; + if (!skb) + continue; + + nframes--; + status = (struct mt76_rx_status *)skb->cb; + if (!time_after32(jiffies, + status->reorder_time + + mt76_aggr_tid_to_timeo(tid->num))) + continue; + + mt76_rx_aggr_release_frames(tid, frames, status->seqno); + } + + mt76_rx_aggr_release_head(tid, frames); +} + +static void +mt76_rx_aggr_reorder_work(struct work_struct *work) +{ + struct mt76_rx_tid *tid = container_of(work, struct mt76_rx_tid, + reorder_work.work); + struct mt76_dev *dev = tid->dev; + struct sk_buff_head frames; + int nframes; + + __skb_queue_head_init(&frames); + + local_bh_disable(); + rcu_read_lock(); + + spin_lock(&tid->lock); + mt76_rx_aggr_check_release(tid, &frames); + nframes = tid->nframes; + spin_unlock(&tid->lock); + + if (nframes) + ieee80211_queue_delayed_work(tid->dev->hw, &tid->reorder_work, + mt76_aggr_tid_to_timeo(tid->num)); + mt76_rx_complete(dev, &frames, NULL); + + rcu_read_unlock(); + local_bh_enable(); +} + +static void +mt76_rx_aggr_check_ctl(struct sk_buff *skb, struct sk_buff_head *frames) +{ + struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb; + struct ieee80211_bar *bar = mt76_skb_get_hdr(skb); + struct mt76_wcid *wcid = status->wcid; + struct mt76_rx_tid *tid; + u8 tidno; + u16 seqno; + + if (!ieee80211_is_ctl(bar->frame_control)) + return; + + if (!ieee80211_is_back_req(bar->frame_control)) + return; + + status->qos_ctl = tidno = le16_to_cpu(bar->control) >> 12; + seqno = IEEE80211_SEQ_TO_SN(le16_to_cpu(bar->start_seq_num)); + tid = rcu_dereference(wcid->aggr[tidno]); + if (!tid) + return; + + spin_lock_bh(&tid->lock); + if (!tid->stopped) { + mt76_rx_aggr_release_frames(tid, frames, seqno); + mt76_rx_aggr_release_head(tid, frames); + } + spin_unlock_bh(&tid->lock); +} + +void mt76_rx_aggr_reorder(struct sk_buff *skb, struct sk_buff_head *frames) +{ + struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb; + struct mt76_wcid *wcid = status->wcid; + struct ieee80211_sta *sta; + struct mt76_rx_tid *tid; + bool sn_less; + u16 seqno, head, size, idx; + u8 tidno = status->qos_ctl & IEEE80211_QOS_CTL_TID_MASK; + u8 ackp; + + __skb_queue_tail(frames, skb); + + sta = wcid_to_sta(wcid); + if (!sta) + return; + + if (!status->aggr) { + if (!(status->flag & RX_FLAG_8023)) + mt76_rx_aggr_check_ctl(skb, frames); + return; + } + + /* not part of a BA session */ + ackp = status->qos_ctl & IEEE80211_QOS_CTL_ACK_POLICY_MASK; + if (ackp == IEEE80211_QOS_CTL_ACK_POLICY_NOACK) + return; + + if (wcid->def_wcid) + wcid = wcid->def_wcid; + tid = rcu_dereference(wcid->aggr[tidno]); + if (!tid) + return; + + status->flag |= RX_FLAG_DUP_VALIDATED; + spin_lock_bh(&tid->lock); + + if (tid->stopped) + goto out; + + head = tid->head; + seqno = status->seqno; + size = tid->size; + sn_less = ieee80211_sn_less(seqno, head); + + if (!tid->started) { + if (sn_less) + goto out; + + tid->started = true; + } + + if (sn_less) { + __skb_unlink(skb, frames); + dev_kfree_skb(skb); + goto out; + } + + if (seqno == head) { + tid->head = ieee80211_sn_inc(head); + if (tid->nframes) + mt76_rx_aggr_release_head(tid, frames); + goto out; + } + + __skb_unlink(skb, frames); + + /* + * Frame sequence number exceeds buffering window, free up some space + * by releasing previous frames + */ + if (!ieee80211_sn_less(seqno, head + size)) { + head = ieee80211_sn_inc(ieee80211_sn_sub(seqno, size)); + mt76_rx_aggr_release_frames(tid, frames, head); + } + + idx = seqno % size; + + /* Discard if the current slot is already in use */ + if (tid->reorder_buf[idx]) { + dev_kfree_skb(skb); + goto out; + } + + status->reorder_time = jiffies; + tid->reorder_buf[idx] = skb; + tid->nframes++; + mt76_rx_aggr_release_head(tid, frames); + + ieee80211_queue_delayed_work(tid->dev->hw, &tid->reorder_work, + mt76_aggr_tid_to_timeo(tid->num)); + +out: + spin_unlock_bh(&tid->lock); +} + +int mt76_rx_aggr_start(struct mt76_dev *dev, struct mt76_wcid *wcid, u8 tidno, + u16 ssn, u16 size) +{ + struct mt76_rx_tid *tid; + + mt76_rx_aggr_stop(dev, wcid, tidno); + + tid = kzalloc_flex(*tid, reorder_buf, size); + if (!tid) + return -ENOMEM; + + tid->dev = dev; + tid->head = ssn; + tid->size = size; + tid->num = tidno; + INIT_DELAYED_WORK(&tid->reorder_work, mt76_rx_aggr_reorder_work); + spin_lock_init(&tid->lock); + + rcu_assign_pointer(wcid->aggr[tidno], tid); + + return 0; +} +EXPORT_SYMBOL_GPL(mt76_rx_aggr_start); + +static void mt76_rx_aggr_shutdown(struct mt76_dev *dev, struct mt76_rx_tid *tid) +{ + u16 size = tid->size; + int i; + + spin_lock_bh(&tid->lock); + + tid->stopped = true; + for (i = 0; tid->nframes && i < size; i++) { + struct sk_buff *skb = tid->reorder_buf[i]; + + if (!skb) + continue; + + tid->reorder_buf[i] = NULL; + tid->nframes--; + dev_kfree_skb(skb); + } + + spin_unlock_bh(&tid->lock); + + cancel_delayed_work_sync(&tid->reorder_work); +} + +void mt76_rx_aggr_stop(struct mt76_dev *dev, struct mt76_wcid *wcid, u8 tidno) +{ + struct mt76_rx_tid *tid = NULL; + + tid = rcu_replace_pointer(wcid->aggr[tidno], tid, + lockdep_is_held(&dev->mutex)); + if (tid) { + mt76_rx_aggr_shutdown(dev, tid); + kfree_rcu(tid, rcu_head); + } +} +EXPORT_SYMBOL_GPL(mt76_rx_aggr_stop); diff --git a/drivers/net/wireless/mediatek/mt76/channel.c b/drivers/net/wireless/mediatek/mt76/channel.c new file mode 100644 index 000000000..28ad7bcaf --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/channel.c @@ -0,0 +1,433 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (C) 2024 Felix Fietkau + */ +#include "mt76.h" + +static struct mt76_vif_link * +mt76_alloc_mlink(struct mt76_dev *dev, struct mt76_vif_data *mvif) +{ + struct mt76_vif_link *mlink; + + mlink = kzalloc(dev->drv->link_data_size, GFP_KERNEL); + if (!mlink) + return NULL; + + mlink->mvif = mvif; + + return mlink; +} + +static int +mt76_phy_update_channel(struct mt76_phy *phy, + struct ieee80211_chanctx_conf *conf) +{ + phy->radar_enabled = conf->radar_enabled; + phy->main_chandef = conf->def; + phy->chanctx = (struct mt76_chanctx *)conf->drv_priv; + + return __mt76_set_channel(phy, &phy->main_chandef, false); +} + +int mt76_add_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *conf) +{ + struct mt76_chanctx *ctx = (struct mt76_chanctx *)conf->drv_priv; + struct mt76_phy *phy = hw->priv; + struct mt76_dev *dev = phy->dev; + int ret = -EINVAL; + + phy = ctx->phy = dev->band_phys[conf->def.chan->band]; + if (WARN_ON_ONCE(!phy)) + return ret; + + if (dev->scan.phy == phy) + mt76_abort_scan(dev); + + mutex_lock(&dev->mutex); + if (!phy->chanctx) + ret = mt76_phy_update_channel(phy, conf); + else + ret = 0; + mutex_unlock(&dev->mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(mt76_add_chanctx); + +void mt76_remove_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *conf) +{ + struct mt76_chanctx *ctx = (struct mt76_chanctx *)conf->drv_priv; + struct mt76_phy *phy = hw->priv; + struct mt76_dev *dev = phy->dev; + + phy = ctx->phy; + if (WARN_ON_ONCE(!phy)) + return; + + if (dev->scan.phy == phy) + mt76_abort_scan(dev); + + mutex_lock(&dev->mutex); + if (phy->chanctx == ctx) + phy->chanctx = NULL; + mutex_unlock(&dev->mutex); +} +EXPORT_SYMBOL_GPL(mt76_remove_chanctx); + +void mt76_change_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *conf, + u32 changed) +{ + struct mt76_chanctx *ctx = (struct mt76_chanctx *)conf->drv_priv; + struct mt76_phy *phy = ctx->phy; + struct mt76_dev *dev = phy->dev; + + if (!(changed & (IEEE80211_CHANCTX_CHANGE_WIDTH | + IEEE80211_CHANCTX_CHANGE_RADAR))) + return; + + if (phy->roc_vif) + mt76_abort_roc(phy); + + cancel_delayed_work_sync(&phy->mac_work); + + mutex_lock(&dev->mutex); + mt76_phy_update_channel(phy, conf); + mutex_unlock(&dev->mutex); +} +EXPORT_SYMBOL_GPL(mt76_change_chanctx); + + +int mt76_assign_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_chanctx_conf *conf) +{ + struct mt76_chanctx *ctx = (struct mt76_chanctx *)conf->drv_priv; + struct mt76_vif_link *mlink = (struct mt76_vif_link *)vif->drv_priv; + struct mt76_vif_data *mvif = mlink->mvif; + int link_id = link_conf->link_id; + struct mt76_phy *phy = ctx->phy; + struct mt76_dev *dev = phy->dev; + bool mlink_alloc = false; + int ret = 0; + + if (dev->scan.vif == vif) + mt76_abort_scan(dev); + + mutex_lock(&dev->mutex); + + if (vif->type == NL80211_IFTYPE_MONITOR && + is_zero_ether_addr(vif->addr)) + goto out; + + mlink = mt76_vif_conf_link(dev, vif, link_conf); + if (!mlink) { + mlink = mt76_alloc_mlink(dev, mvif); + if (!mlink) { + ret = -ENOMEM; + goto out; + } + mlink_alloc = true; + } + + mlink->ctx = conf; + ret = dev->drv->vif_link_add(phy, vif, link_conf, mlink); + if (ret) { + if (mlink_alloc) + kfree(mlink); + goto out; + } + + if (link_conf != &vif->bss_conf) + rcu_assign_pointer(mvif->link[link_id], mlink); + +out: + mutex_unlock(&dev->mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(mt76_assign_vif_chanctx); + +void mt76_unassign_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_chanctx_conf *conf) +{ + struct mt76_chanctx *ctx = (struct mt76_chanctx *)conf->drv_priv; + struct mt76_vif_link *mlink = (struct mt76_vif_link *)vif->drv_priv; + struct mt76_phy *phy = ctx->phy; + struct mt76_dev *dev = phy->dev; + + if (dev->scan.vif == vif) + mt76_abort_scan(dev); + + mutex_lock(&dev->mutex); + + if (vif->type == NL80211_IFTYPE_MONITOR && + is_zero_ether_addr(vif->addr)) + goto out; + + mlink = mt76_vif_conf_link(dev, vif, link_conf); + if (!mlink) + goto out; + + dev->drv->vif_link_remove(phy, vif, link_conf, mlink); + mlink->ctx = NULL; +out: + mutex_unlock(&dev->mutex); +} +EXPORT_SYMBOL_GPL(mt76_unassign_vif_chanctx); + +int mt76_switch_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif_chanctx_switch *vifs, + int n_vifs, + enum ieee80211_chanctx_switch_mode mode) +{ + struct ieee80211_vif_chanctx_switch *v; + struct mt76_chanctx *old_ctx, *new_ctx; + struct mt76_phy *old_phy, *phy = hw->priv; + struct mt76_dev *dev = phy->dev; + struct mt76_vif_link *mlink; + bool need_update[__MT_MAX_BAND] = {}; + int i, ret = 0; + + for (i = 0; i < n_vifs; i++) { + v = &vifs[i]; + new_ctx = (struct mt76_chanctx *)v->new_ctx->drv_priv; + if (mode == CHANCTX_SWMODE_SWAP_CONTEXTS) + phy = new_ctx->phy = dev->band_phys[v->new_ctx->def.chan->band]; + else + phy = new_ctx->phy; + + if (!phy) + return -EINVAL; + + if (need_update[phy->band_idx]) + continue; + + if (phy->chanctx != new_ctx) { + if (dev->scan.phy == phy) + mt76_abort_scan(dev); + + cancel_delayed_work_sync(&phy->mac_work); + need_update[phy->band_idx] = true; + } + } + + mutex_lock(&dev->mutex); + + for (i = 0; i < n_vifs; i++) { + v = &vifs[i]; + old_ctx = (struct mt76_chanctx *)v->old_ctx->drv_priv; + old_phy = old_ctx->phy; + + new_ctx = (struct mt76_chanctx *)v->new_ctx->drv_priv; + phy = new_ctx->phy; + + if (mode == CHANCTX_SWMODE_SWAP_CONTEXTS && old_phy->chanctx && + old_phy->chanctx == old_ctx && phy != old_phy) + old_phy->chanctx = NULL; + + if (need_update[phy->band_idx]) { + ret = mt76_phy_update_channel(phy, v->new_ctx); + if (ret) + goto out; + + need_update[phy->band_idx] = false; + } + + mlink = mt76_vif_conf_link(dev, v->vif, v->link_conf); + if (!mlink) + continue; + + if (old_phy != phy) { + dev->drv->vif_link_remove(old_phy, v->vif, v->link_conf, + mlink); + + ret = dev->drv->vif_link_add(phy, v->vif, v->link_conf, + mlink); + if (ret) + goto out; + } + + mlink->ctx = v->new_ctx; + if (mlink->beacon_mon_interval) + WRITE_ONCE(mlink->beacon_mon_last, jiffies); + } + +out: + mutex_unlock(&dev->mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(mt76_switch_vif_chanctx); + +struct mt76_vif_link *mt76_get_vif_phy_link(struct mt76_phy *phy, + struct ieee80211_vif *vif) +{ + struct mt76_vif_link *mlink = (struct mt76_vif_link *)vif->drv_priv; + struct mt76_vif_data *mvif = mlink->mvif; + struct mt76_dev *dev = phy->dev; + int i, ret; + + for (i = 0; i < ARRAY_SIZE(mvif->link); i++) { + mlink = mt76_dereference(mvif->link[i], dev); + if (!mlink) + continue; + + if (mt76_vif_link_phy(mlink) == phy) + return mlink; + } + + if (!dev->drv->vif_link_add) + return ERR_PTR(-EINVAL); + + mlink = mt76_alloc_mlink(dev, mvif); + if (!mlink) + return ERR_PTR(-ENOMEM); + + mlink->offchannel = true; + ret = dev->drv->vif_link_add(phy, vif, &vif->bss_conf, mlink); + if (ret) { + kfree(mlink); + return ERR_PTR(ret); + } + rcu_assign_pointer(mvif->offchannel_link, mlink); + + return mlink; +} + +void mt76_put_vif_phy_link(struct mt76_phy *phy, struct ieee80211_vif *vif, + struct mt76_vif_link *mlink) +{ + struct mt76_dev *dev = phy->dev; + struct mt76_vif_data *mvif; + + if (IS_ERR_OR_NULL(mlink) || !mlink->offchannel) + return; + + mvif = mlink->mvif; + + rcu_assign_pointer(mvif->offchannel_link, NULL); + dev->drv->vif_link_remove(phy, vif, &vif->bss_conf, mlink); + kfree_rcu(mlink, rcu_head); +} + +void mt76_roc_complete(struct mt76_phy *phy) +{ + struct mt76_vif_link *mlink = phy->roc_link; + struct mt76_dev *dev = phy->dev; + + if (!phy->roc_vif) + return; + + if (mlink) + mlink->mvif->roc_phy = NULL; + if (phy->chanctx && phy->main_chandef.chan && phy->offchannel && + !test_bit(MT76_MCU_RESET, &dev->phy.state)) { + __mt76_set_channel(phy, &phy->main_chandef, false); + mt76_offchannel_notify(phy, false); + } + mt76_put_vif_phy_link(phy, phy->roc_vif, phy->roc_link); + phy->roc_vif = NULL; + phy->roc_link = NULL; + if (!test_bit(MT76_MCU_RESET, &dev->phy.state)) + ieee80211_remain_on_channel_expired(phy->hw); +} + +void mt76_roc_complete_work(struct work_struct *work) +{ + struct mt76_phy *phy = container_of(work, struct mt76_phy, roc_work.work); + struct mt76_dev *dev = phy->dev; + + mutex_lock(&dev->mutex); + mt76_roc_complete(phy); + mutex_unlock(&dev->mutex); +} + +void mt76_abort_roc(struct mt76_phy *phy) +{ + struct mt76_dev *dev = phy->dev; + + cancel_delayed_work_sync(&phy->roc_work); + + mutex_lock(&dev->mutex); + mt76_roc_complete(phy); + mutex_unlock(&dev->mutex); +} +EXPORT_SYMBOL_GPL(mt76_abort_roc); + +int mt76_remain_on_channel(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_channel *chan, int duration, + enum ieee80211_roc_type type) +{ + struct cfg80211_chan_def chandef = {}; + struct mt76_phy *phy = hw->priv; + struct mt76_dev *dev = phy->dev; + struct mt76_vif_link *mlink; + bool offchannel; + int ret = 0; + + phy = dev->band_phys[chan->band]; + if (!phy) + return -EINVAL; + + cancel_delayed_work_sync(&phy->mac_work); + + mutex_lock(&dev->mutex); + + if (phy->roc_vif || dev->scan.phy == phy || + test_bit(MT76_MCU_RESET, &dev->phy.state)) { + ret = -EBUSY; + goto out; + } + + mlink = mt76_get_vif_phy_link(phy, vif); + if (IS_ERR(mlink)) { + ret = PTR_ERR(mlink); + goto out; + } + + mlink->mvif->roc_phy = phy; + phy->roc_vif = vif; + phy->roc_link = mlink; + + offchannel = mt76_offchannel_chandef(phy, chan, &chandef); + if (offchannel) + mt76_offchannel_notify(phy, true); + ret = __mt76_set_channel(phy, &chandef, offchannel); + if (ret) { + mlink->mvif->roc_phy = NULL; + phy->roc_vif = NULL; + phy->roc_link = NULL; + mt76_put_vif_phy_link(phy, vif, mlink); + goto out; + } + ieee80211_ready_on_channel(hw); + ieee80211_queue_delayed_work(phy->hw, &phy->roc_work, + msecs_to_jiffies(duration)); + +out: + mutex_unlock(&dev->mutex); + return ret; +} +EXPORT_SYMBOL_GPL(mt76_remain_on_channel); + +int mt76_cancel_remain_on_channel(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct mt76_vif_link *mlink = (struct mt76_vif_link *)vif->drv_priv; + struct mt76_vif_data *mvif = mlink->mvif; + struct mt76_phy *phy = mvif->roc_phy; + + if (!phy) + return 0; + + mt76_abort_roc(phy); + + return 0; +} +EXPORT_SYMBOL_GPL(mt76_cancel_remain_on_channel); diff --git a/drivers/net/wireless/mediatek/mt76/debugfs.c b/drivers/net/wireless/mediatek/mt76/debugfs.c new file mode 100644 index 000000000..a5ac6ca86 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/debugfs.c @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (C) 2016 Felix Fietkau + */ +#include "mt76.h" + +static int +mt76_reg_set(void *data, u64 val) +{ + struct mt76_dev *dev = data; + + __mt76_wr(dev, dev->debugfs_reg, val); + return 0; +} + +static int +mt76_reg_get(void *data, u64 *val) +{ + struct mt76_dev *dev = data; + + *val = __mt76_rr(dev, dev->debugfs_reg); + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_regval, mt76_reg_get, mt76_reg_set, + "0x%08llx\n"); + +static int +mt76_napi_threaded_set(void *data, u64 val) +{ + struct mt76_dev *dev = data; + + if (!mt76_is_mmio(dev)) + return -EOPNOTSUPP; + + if (dev->napi_dev->threaded != val) + return dev_set_threaded(dev->napi_dev, val); + + return 0; +} + +static int +mt76_napi_threaded_get(void *data, u64 *val) +{ + struct mt76_dev *dev = data; + + *val = dev->napi_dev->threaded; + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_napi_threaded, mt76_napi_threaded_get, + mt76_napi_threaded_set, "%llu\n"); + +int mt76_queues_read(struct seq_file *s, void *data) +{ + struct mt76_dev *dev = dev_get_drvdata(s->private); + int i; + + seq_puts(s, " queue | hw-queued | head | tail |\n"); + for (i = 0; i < ARRAY_SIZE(dev->phy.q_tx); i++) { + struct mt76_queue *q = dev->phy.q_tx[i]; + + if (!q) + continue; + + seq_printf(s, " %9d | %9d | %9d | %9d |\n", + i, q->queued, q->head, q->tail); + } + + return 0; +} +EXPORT_SYMBOL_GPL(mt76_queues_read); + +static int mt76_rx_queues_read(struct seq_file *s, void *data) +{ + struct mt76_dev *dev = dev_get_drvdata(s->private); + int i, queued; + + seq_puts(s, " queue | hw-queued | head | tail |\n"); + mt76_for_each_q_rx(dev, i) { + struct mt76_queue *q = &dev->q_rx[i]; + + queued = mt76_is_usb(dev) ? q->ndesc - q->queued : q->queued; + seq_printf(s, " %9d | %9d | %9d | %9d |\n", + i, queued, q->head, q->tail); + } + + return 0; +} + +void mt76_seq_puts_array(struct seq_file *file, const char *str, + s8 *val, int len) +{ + int i; + + seq_printf(file, "%16s:", str); + for (i = 0; i < len; i++) + seq_printf(file, " %4d", val[i]); + seq_puts(file, "\n"); +} +EXPORT_SYMBOL_GPL(mt76_seq_puts_array); + +struct dentry * +mt76_register_debugfs_fops(struct mt76_phy *phy, + const struct file_operations *ops) +{ + const struct file_operations *fops = ops ? ops : &fops_regval; + struct mt76_dev *dev = phy->dev; + struct dentry *dir; + + dir = debugfs_create_dir("mt76", phy->hw->wiphy->debugfsdir); + + debugfs_create_u8("led_pin", 0600, dir, &phy->leds.pin); + debugfs_create_u32("regidx", 0600, dir, &dev->debugfs_reg); + debugfs_create_file_unsafe("regval", 0600, dir, dev, fops); + debugfs_create_file_unsafe("napi_threaded", 0600, dir, dev, + &fops_napi_threaded); + debugfs_create_blob("eeprom", 0400, dir, &dev->eeprom); + if (dev->otp.data) + debugfs_create_blob("otp", 0400, dir, &dev->otp); + debugfs_create_devm_seqfile(dev->dev, "rx-queues", dir, + mt76_rx_queues_read); + + return dir; +} +EXPORT_SYMBOL_GPL(mt76_register_debugfs_fops); diff --git a/drivers/net/wireless/mediatek/mt76/dma.c b/drivers/net/wireless/mediatek/mt76/dma.c new file mode 100644 index 000000000..a67880a92 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/dma.c @@ -0,0 +1,1237 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (C) 2016 Felix Fietkau + */ + +#include +#include "mt76.h" +#include "dma.h" +#include "mt76_connac.h" + +static struct mt76_txwi_cache * +mt76_alloc_txwi(struct mt76_dev *dev) +{ + struct mt76_txwi_cache *t; + dma_addr_t addr; + u8 *txwi; + int size; + + size = L1_CACHE_ALIGN(dev->drv->txwi_size + sizeof(*t)); + txwi = kzalloc(size, GFP_ATOMIC); + if (!txwi) + return NULL; + + addr = dma_map_single(dev->dma_dev, txwi, dev->drv->txwi_size, + DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(dev->dma_dev, addr))) { + kfree(txwi); + return NULL; + } + + t = (struct mt76_txwi_cache *)(txwi + dev->drv->txwi_size); + t->dma_addr = addr; + + return t; +} + +static struct mt76_txwi_cache * +mt76_alloc_rxwi(struct mt76_dev *dev) +{ + struct mt76_txwi_cache *t; + + t = kzalloc(L1_CACHE_ALIGN(sizeof(*t)), GFP_ATOMIC); + if (!t) + return NULL; + + t->ptr = NULL; + return t; +} + +static struct mt76_txwi_cache * +__mt76_get_txwi(struct mt76_dev *dev) +{ + struct mt76_txwi_cache *t = NULL; + + spin_lock(&dev->lock); + if (!list_empty(&dev->txwi_cache)) { + t = list_first_entry(&dev->txwi_cache, struct mt76_txwi_cache, + list); + list_del(&t->list); + } + spin_unlock(&dev->lock); + + return t; +} + +static struct mt76_txwi_cache * +__mt76_get_rxwi(struct mt76_dev *dev) +{ + struct mt76_txwi_cache *t = NULL; + + spin_lock_bh(&dev->wed_lock); + if (!list_empty(&dev->rxwi_cache)) { + t = list_first_entry(&dev->rxwi_cache, struct mt76_txwi_cache, + list); + list_del(&t->list); + } + spin_unlock_bh(&dev->wed_lock); + + return t; +} + +static struct mt76_txwi_cache * +mt76_get_txwi(struct mt76_dev *dev) +{ + struct mt76_txwi_cache *t = __mt76_get_txwi(dev); + + if (t) + return t; + + return mt76_alloc_txwi(dev); +} + +struct mt76_txwi_cache * +mt76_get_rxwi(struct mt76_dev *dev) +{ + struct mt76_txwi_cache *t = __mt76_get_rxwi(dev); + + if (t) + return t; + + return mt76_alloc_rxwi(dev); +} +EXPORT_SYMBOL_GPL(mt76_get_rxwi); + +void +mt76_put_txwi(struct mt76_dev *dev, struct mt76_txwi_cache *t) +{ + if (!t) + return; + + spin_lock(&dev->lock); + list_add(&t->list, &dev->txwi_cache); + spin_unlock(&dev->lock); +} +EXPORT_SYMBOL_GPL(mt76_put_txwi); + +void +mt76_put_rxwi(struct mt76_dev *dev, struct mt76_txwi_cache *t) +{ + if (!t) + return; + + spin_lock_bh(&dev->wed_lock); + list_add(&t->list, &dev->rxwi_cache); + spin_unlock_bh(&dev->wed_lock); +} +EXPORT_SYMBOL_GPL(mt76_put_rxwi); + +static void +mt76_free_pending_txwi(struct mt76_dev *dev) +{ + struct mt76_txwi_cache *t; + + local_bh_disable(); + while ((t = __mt76_get_txwi(dev)) != NULL) { + dma_unmap_single(dev->dma_dev, t->dma_addr, dev->drv->txwi_size, + DMA_TO_DEVICE); + kfree(mt76_get_txwi_ptr(dev, t)); + } + local_bh_enable(); +} + +void +mt76_free_pending_rxwi(struct mt76_dev *dev) +{ + struct mt76_txwi_cache *t; + + local_bh_disable(); + while ((t = __mt76_get_rxwi(dev)) != NULL) { + if (t->ptr) + mt76_put_page_pool_buf(t->ptr, false); + kfree(t); + } + local_bh_enable(); +} +EXPORT_SYMBOL_GPL(mt76_free_pending_rxwi); + +static void +mt76_dma_queue_magic_cnt_init(struct mt76_dev *dev, struct mt76_queue *q) +{ + if (!mt76_queue_is_wed_rro(q)) + return; + + q->magic_cnt = 0; + if (mt76_queue_is_wed_rro_ind(q)) { + struct mt76_wed_rro_desc *rro_desc; + u32 data1 = FIELD_PREP(RRO_IND_DATA1_MAGIC_CNT_MASK, + MT_DMA_WED_IND_CMD_CNT - 1); + int i; + + rro_desc = (struct mt76_wed_rro_desc *)q->desc; + for (i = 0; i < q->ndesc; i++) { + struct mt76_wed_rro_ind *cmd; + + cmd = (struct mt76_wed_rro_ind *)&rro_desc[i]; + cmd->data1 = cpu_to_le32(data1); + } + } else if (mt76_queue_is_wed_rro_rxdmad_c(q)) { + struct mt76_rro_rxdmad_c *dmad = (void *)q->desc; + u32 data3 = FIELD_PREP(RRO_RXDMAD_DATA3_MAGIC_CNT_MASK, + MT_DMA_MAGIC_CNT - 1); + int i; + + for (i = 0; i < q->ndesc; i++) + dmad[i].data3 = cpu_to_le32(data3); + } +} + +/* A hung bus (e.g. after a PCIe AER error) reads 0xffffffff from every + * register, so clamp an out-of-range index to the fallback to keep it from + * corrupting q->head/q->tail. + */ +static int +mt76_dma_read_dma_idx(struct mt76_queue *q, int fallback) +{ + u32 idx = Q_READ(q, dma_idx); + + return idx < q->ndesc ? idx : fallback; +} + +static void +mt76_dma_sync_idx(struct mt76_dev *dev, struct mt76_queue *q) +{ + if ((q->flags & MT_QFLAG_WED_RRO_EN) && + (!is_mt7992(dev) || !mt76_npu_device_active(dev))) + Q_WRITE(q, ring_size, MT_DMA_RRO_EN | q->ndesc); + else + Q_WRITE(q, ring_size, q->ndesc); + + if (mt76_queue_is_npu_tx(q)) { + writel(q->ndesc, &q->regs->ring_size); + writel(q->desc_dma, &q->regs->desc_base); + } + + Q_WRITE(q, desc_base, q->desc_dma); + + q->head = mt76_dma_read_dma_idx(q, 0); + q->tail = q->head; +} + +void mt76_dma_queue_reset(struct mt76_dev *dev, struct mt76_queue *q, + bool reset_idx) +{ + if (!q || !q->ndesc) + return; + + if (!mt76_queue_is_wed_rro_ind(q) && + !mt76_queue_is_wed_rro_rxdmad_c(q) && !mt76_queue_is_npu(q)) { + int i; + + /* clear descriptors */ + for (i = 0; i < q->ndesc; i++) + q->desc[i].ctrl = cpu_to_le32(MT_DMA_CTL_DMA_DONE); + } + + mt76_dma_queue_magic_cnt_init(dev, q); + if (reset_idx) { + if (mt76_queue_is_emi(q)) + *q->emi_cpu_idx = 0; + else + Q_WRITE(q, cpu_idx, 0); + Q_WRITE(q, dma_idx, 0); + } + mt76_dma_sync_idx(dev, q); +} + +static int +mt76_dma_add_rx_buf(struct mt76_dev *dev, struct mt76_queue *q, + struct mt76_queue_buf *buf, void *data) +{ + struct mt76_queue_entry *entry = &q->entry[q->head]; + struct mt76_txwi_cache *txwi = NULL; + u32 buf1 = 0, ctrl, info = 0; + struct mt76_desc *desc; + int idx = q->head; + int rx_token; + + if (mt76_queue_is_wed_rro_ind(q)) { + struct mt76_wed_rro_desc *rro_desc; + + rro_desc = (struct mt76_wed_rro_desc *)q->desc; + data = &rro_desc[q->head]; + goto done; + } else if (mt76_queue_is_wed_rro_rxdmad_c(q)) { + data = &q->desc[q->head]; + goto done; + } + + desc = &q->desc[q->head]; + ctrl = FIELD_PREP(MT_DMA_CTL_SD_LEN0, buf[0].len); +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + buf1 = FIELD_PREP(MT_DMA_CTL_SDP0_H, buf->addr >> 32); +#endif + + if (mt76_queue_is_wed_rx(q) || mt76_queue_is_wed_rro_data(q)) { + txwi = mt76_get_rxwi(dev); + if (!txwi) + return -ENOMEM; + + rx_token = mt76_rx_token_consume(dev, data, txwi, buf->addr); + if (rx_token < 0) { + mt76_put_rxwi(dev, txwi); + return -ENOMEM; + } + + buf1 |= FIELD_PREP(MT_DMA_CTL_TOKEN, rx_token); + ctrl |= MT_DMA_CTL_TO_HOST; + + txwi->qid = q - dev->q_rx; + } + + if (mt76_queue_is_wed_rro_msdu_pg(q) && + dev->drv->rx_rro_add_msdu_page) { + if (dev->drv->rx_rro_add_msdu_page(dev, q, buf->addr, data)) + return -ENOMEM; + } + + if (q->flags & MT_QFLAG_WED_RRO_EN) { + info |= FIELD_PREP(MT_DMA_MAGIC_MASK, q->magic_cnt); + if ((q->head + 1) == q->ndesc) + q->magic_cnt = (q->magic_cnt + 1) % MT_DMA_MAGIC_CNT; + } + + WRITE_ONCE(desc->buf0, cpu_to_le32(buf->addr)); + WRITE_ONCE(desc->buf1, cpu_to_le32(buf1)); + WRITE_ONCE(desc->ctrl, cpu_to_le32(ctrl)); + WRITE_ONCE(desc->info, cpu_to_le32(info)); + +done: + entry->dma_addr[0] = buf->addr; + entry->dma_len[0] = buf->len; + entry->txwi = txwi; + entry->buf = data; + entry->wcid = 0xffff; + entry->skip_buf1 = true; + q->head = (q->head + 1) % q->ndesc; + q->queued++; + + return idx; +} + +static int +mt76_dma_add_buf(struct mt76_dev *dev, struct mt76_queue *q, + struct mt76_queue_buf *buf, int nbufs, u32 info, + struct sk_buff *skb, void *txwi) +{ + struct mt76_queue_entry *entry; + struct mt76_desc *desc; + int i, idx = -1; + u32 ctrl, next; + + if (txwi) { + q->entry[q->head].txwi = DMA_DUMMY_DATA; + q->entry[q->head].skip_buf0 = true; + } + + for (i = 0; i < nbufs; i += 2, buf += 2) { + u32 buf0 = buf[0].addr, buf1 = 0; + + idx = q->head; + next = (q->head + 1) % q->ndesc; + + desc = &q->desc[idx]; + entry = &q->entry[idx]; + + if (buf[0].skip_unmap) + entry->skip_buf0 = true; + entry->skip_buf1 = i == nbufs - 1; + + entry->dma_addr[0] = buf[0].addr; + entry->dma_len[0] = buf[0].len; + + ctrl = FIELD_PREP(MT_DMA_CTL_SD_LEN0, buf[0].len); +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + info |= FIELD_PREP(MT_DMA_CTL_SDP0_H, buf[0].addr >> 32); +#endif + if (i < nbufs - 1) { + entry->dma_addr[1] = buf[1].addr; + entry->dma_len[1] = buf[1].len; + buf1 = buf[1].addr; + ctrl |= FIELD_PREP(MT_DMA_CTL_SD_LEN1, buf[1].len); +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + info |= FIELD_PREP(MT_DMA_CTL_SDP1_H, + buf[1].addr >> 32); +#endif + if (buf[1].skip_unmap) + entry->skip_buf1 = true; + } + + if (i == nbufs - 1) + ctrl |= MT_DMA_CTL_LAST_SEC0; + else if (i == nbufs - 2) + ctrl |= MT_DMA_CTL_LAST_SEC1; + + WRITE_ONCE(desc->buf0, cpu_to_le32(buf0)); + WRITE_ONCE(desc->buf1, cpu_to_le32(buf1)); + WRITE_ONCE(desc->info, cpu_to_le32(info)); + WRITE_ONCE(desc->ctrl, cpu_to_le32(ctrl)); + + q->head = next; + q->queued++; + } + + q->entry[idx].txwi = txwi; + q->entry[idx].skb = skb; + q->entry[idx].wcid = 0xffff; + + return idx; +} + +static void +mt76_dma_tx_cleanup_idx(struct mt76_dev *dev, struct mt76_queue *q, int idx, + struct mt76_queue_entry *prev_e) +{ + struct mt76_queue_entry *e = &q->entry[idx]; + + if (!e->skip_buf0) + dma_unmap_single(dev->dma_dev, e->dma_addr[0], e->dma_len[0], + DMA_TO_DEVICE); + + if (!e->skip_buf1) + dma_unmap_single(dev->dma_dev, e->dma_addr[1], e->dma_len[1], + DMA_TO_DEVICE); + + if (e->txwi == DMA_DUMMY_DATA) + e->txwi = NULL; + + *prev_e = *e; + memset(e, 0, sizeof(*e)); +} + +static void +mt76_dma_kick_queue(struct mt76_dev *dev, struct mt76_queue *q) +{ + wmb(); + if (mt76_queue_is_emi(q)) + *q->emi_cpu_idx = cpu_to_le16(q->head); + else + Q_WRITE(q, cpu_idx, q->head); +} + +static void +mt76_dma_tx_cleanup(struct mt76_dev *dev, struct mt76_queue *q, bool flush) +{ + struct mt76_queue_entry entry; + int last; + + if (!q || !q->ndesc) + return; + + spin_lock_bh(&q->cleanup_lock); + if (flush) + last = -1; + else + last = mt76_dma_read_dma_idx(q, -1); + + while (q->queued > 0 && q->tail != last) { + mt76_dma_tx_cleanup_idx(dev, q, q->tail, &entry); + mt76_npu_txdesc_cleanup(q, q->tail); + mt76_queue_tx_complete(dev, q, &entry); + + if (entry.txwi) { + if (!(dev->drv->drv_flags & MT_DRV_TXWI_NO_FREE)) + mt76_put_txwi(dev, entry.txwi); + } + + if (!flush && q->tail == last) + last = mt76_dma_read_dma_idx(q, -1); + } + spin_unlock_bh(&q->cleanup_lock); + + if (flush) { + spin_lock_bh(&q->lock); + mt76_dma_sync_idx(dev, q); + mt76_dma_kick_queue(dev, q); + spin_unlock_bh(&q->lock); + } + + if (!q->queued) + wake_up(&dev->tx_wait); +} + +static void * +mt76_dma_get_rxdmad_c_buf(struct mt76_dev *dev, struct mt76_queue *q, + int idx, int *len, bool *more) +{ + struct mt76_queue_entry *e = &q->entry[idx]; + struct mt76_rro_rxdmad_c *dmad = e->buf; + u32 data1 = le32_to_cpu(dmad->data1); + u32 data2 = le32_to_cpu(dmad->data2); + struct mt76_txwi_cache *t; + u16 rx_token_id; + u8 ind_reason; + void *buf; + + rx_token_id = FIELD_GET(RRO_RXDMAD_DATA2_RX_TOKEN_ID_MASK, data2); + t = mt76_rx_token_release(dev, rx_token_id); + if (!t) + return ERR_PTR(-EAGAIN); + + q = &dev->q_rx[t->qid]; + dma_sync_single_for_cpu(dev->dma_dev, t->dma_addr, + SKB_WITH_OVERHEAD(q->buf_size), + page_pool_get_dma_dir(q->page_pool)); + + if (len) + *len = FIELD_GET(RRO_RXDMAD_DATA1_SDL0_MASK, data1); + if (more) + *more = !FIELD_GET(RRO_RXDMAD_DATA1_LS_MASK, data1); + + buf = t->ptr; + ind_reason = FIELD_GET(RRO_RXDMAD_DATA2_IND_REASON_MASK, data2); + if (ind_reason == MT_DMA_WED_IND_REASON_REPEAT || + ind_reason == MT_DMA_WED_IND_REASON_OLDPKT) { + mt76_put_page_pool_buf(buf, false); + buf = ERR_PTR(-EAGAIN); + } + t->ptr = NULL; + t->dma_addr = 0; + + mt76_put_rxwi(dev, t); + + return buf; +} + +static void * +mt76_dma_get_buf(struct mt76_dev *dev, struct mt76_queue *q, int idx, + int *len, u32 *info, bool *more, bool *drop, bool flush) +{ + struct mt76_queue_entry *e = &q->entry[idx]; + struct mt76_desc *desc = &q->desc[idx]; + u32 ctrl, desc_info, buf1; + void *buf = e->buf; + + if (mt76_queue_is_wed_rro_rxdmad_c(q) && !flush) + buf = mt76_dma_get_rxdmad_c_buf(dev, q, idx, len, more); + + if (mt76_queue_is_wed_rro(q)) + goto done; + + ctrl = le32_to_cpu(READ_ONCE(desc->ctrl)); + if (len) { + *len = FIELD_GET(MT_DMA_CTL_SD_LEN0, ctrl); + *more = !(ctrl & MT_DMA_CTL_LAST_SEC0); + } + + desc_info = le32_to_cpu(desc->info); + if (info) + *info = desc_info; + + buf1 = le32_to_cpu(desc->buf1); + mt76_dma_should_drop_buf(drop, ctrl, buf1, desc_info); + + if (mt76_queue_is_wed_rx(q)) { + u32 token = FIELD_GET(MT_DMA_CTL_TOKEN, buf1); + struct mt76_txwi_cache *t = mt76_rx_token_release(dev, token); + + if (!t) + return NULL; + + dma_sync_single_for_cpu(dev->dma_dev, t->dma_addr, + SKB_WITH_OVERHEAD(q->buf_size), + page_pool_get_dma_dir(q->page_pool)); + + buf = t->ptr; + t->dma_addr = 0; + t->ptr = NULL; + + mt76_put_rxwi(dev, t); +#ifdef CONFIG_NET_MEDIATEK_SOC_WED + /* the WO MCU owns the RX path only on WED v2, on newer + * versions this buf1 bit carries no drop information + */ + if (drop && dev->mmio.wed.version == 2) + *drop |= !!(buf1 & MT_DMA_CTL_WO_DROP); +#endif + } else { + dma_sync_single_for_cpu(dev->dma_dev, e->dma_addr[0], + SKB_WITH_OVERHEAD(q->buf_size), + page_pool_get_dma_dir(q->page_pool)); + } + +done: + e->buf = NULL; + return buf; +} + +static void * +mt76_dma_dequeue(struct mt76_dev *dev, struct mt76_queue *q, bool flush, + int *len, u32 *info, bool *more, bool *drop) +{ + int idx = q->tail; + + *more = false; + if (!q->queued) + return NULL; + + if (mt76_queue_is_wed_rro_data(q) || mt76_queue_is_wed_rro_msdu_pg(q)) + goto done; + + if (mt76_queue_is_wed_rro_ind(q)) { + struct mt76_wed_rro_ind *cmd; + u8 magic_cnt; + + if (flush) + goto done; + + cmd = q->entry[idx].buf; + magic_cnt = FIELD_GET(RRO_IND_DATA1_MAGIC_CNT_MASK, + le32_to_cpu(cmd->data1)); + if (magic_cnt != q->magic_cnt) + return NULL; + + if (q->tail == q->ndesc - 1) + q->magic_cnt = (q->magic_cnt + 1) % MT_DMA_WED_IND_CMD_CNT; + } else if (mt76_queue_is_wed_rro_rxdmad_c(q)) { + struct mt76_rro_rxdmad_c *dmad; + u16 magic_cnt; + + if (flush) + goto done; + + dmad = q->entry[idx].buf; + magic_cnt = FIELD_GET(RRO_RXDMAD_DATA3_MAGIC_CNT_MASK, + le32_to_cpu(dmad->data3)); + if (magic_cnt != q->magic_cnt) + return NULL; + + if (q->tail == q->ndesc - 1) + q->magic_cnt = (q->magic_cnt + 1) % MT_DMA_MAGIC_CNT; + } else { + if (flush) + q->desc[idx].ctrl |= cpu_to_le32(MT_DMA_CTL_DMA_DONE); + else if (!(q->desc[idx].ctrl & cpu_to_le32(MT_DMA_CTL_DMA_DONE))) + return NULL; +#ifdef CONFIG_NET_MEDIATEK_SOC_WED + /* on WED v3 the M_DONE bit signals that WED is done reading + * the txfree descriptor; WED v2 does not set it + */ + else if (dev->mmio.wed.version > 2 && + mt76_queue_is_wed_tx_free(q) && + !(q->desc[idx].ctrl & cpu_to_le32(MT_DMA_CTL_M_DONE))) + return NULL; +#endif + } +done: + q->tail = (q->tail + 1) % q->ndesc; + q->queued--; + + return mt76_dma_get_buf(dev, q, idx, len, info, more, drop, flush); +} + +static int +mt76_dma_tx_queue_skb_raw(struct mt76_dev *dev, struct mt76_queue *q, + struct sk_buff *skb, u32 tx_info) +{ + struct mt76_queue_buf buf = {}; + dma_addr_t addr; + + if (test_bit(MT76_MCU_RESET, &dev->phy.state)) + goto error; + + if (q->queued + 1 >= q->ndesc - 1) + goto error; + + addr = dma_map_single(dev->dma_dev, skb->data, skb->len, + DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(dev->dma_dev, addr))) + goto error; + + buf.addr = addr; + buf.len = skb->len; + + spin_lock_bh(&q->lock); + if (mt76_dma_add_buf(dev, q, &buf, 1, tx_info, skb, NULL) >= 0) + mt76_dma_kick_queue(dev, q); + spin_unlock_bh(&q->lock); + + return 0; + +error: + dev_kfree_skb(skb); + return -ENOMEM; +} + +static int +mt76_dma_tx_queue_skb(struct mt76_phy *phy, struct mt76_queue *q, + enum mt76_txq_id qid, struct sk_buff *skb, + struct mt76_wcid *wcid, struct ieee80211_sta *sta) +{ + struct ieee80211_tx_status status = { + .sta = sta, + }; + struct mt76_tx_info tx_info = { + .skb = skb, + }; + struct mt76_dev *dev = phy->dev; + struct ieee80211_hw *hw; + int len, n = 0, ret = -ENOMEM; + struct mt76_txwi_cache *t; + struct sk_buff *iter; + dma_addr_t addr; + u8 *txwi; + + if (test_bit(MT76_RESET, &phy->state)) + goto free_skb; + + /* TODO: Take into account unlinear skbs */ + if (mt76_npu_device_active(dev) && skb_linearize(skb)) + goto free_skb; + + t = mt76_get_txwi(dev); + if (!t) + goto free_skb; + + t->phy_idx = phy->band_idx; + t->qid = qid; + txwi = mt76_get_txwi_ptr(dev, t); + + skb->prev = skb->next = NULL; + if (dev->drv->drv_flags & MT_DRV_TX_ALIGNED4_SKBS) + mt76_insert_hdr_pad(skb); + + len = skb_headlen(skb); + addr = dma_map_single(dev->dma_dev, skb->data, len, DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(dev->dma_dev, addr))) + goto free; + + tx_info.buf[n].addr = t->dma_addr; + tx_info.buf[n++].len = dev->drv->txwi_size; + tx_info.buf[n].addr = addr; + tx_info.buf[n++].len = len; + + skb_walk_frags(skb, iter) { + if (n == ARRAY_SIZE(tx_info.buf)) + goto unmap; + + addr = dma_map_single(dev->dma_dev, iter->data, iter->len, + DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(dev->dma_dev, addr))) + goto unmap; + + tx_info.buf[n].addr = addr; + tx_info.buf[n++].len = iter->len; + } + tx_info.nbuf = n; + + if (q->queued + (tx_info.nbuf + 1) / 2 >= q->ndesc - 1) { + ret = -ENOMEM; + goto unmap; + } + + dma_sync_single_for_cpu(dev->dma_dev, t->dma_addr, dev->drv->txwi_size, + DMA_TO_DEVICE); + ret = dev->drv->tx_prepare_skb(dev, txwi, qid, wcid, sta, &tx_info); + dma_sync_single_for_device(dev->dma_dev, t->dma_addr, dev->drv->txwi_size, + DMA_TO_DEVICE); + if (ret < 0) + goto unmap; + + if (mt76_npu_device_active(dev)) + return mt76_npu_dma_add_buf(phy, q, skb, &tx_info.buf[1], txwi); + + return mt76_dma_add_buf(dev, q, tx_info.buf, tx_info.nbuf, + tx_info.info, tx_info.skb, t); + +unmap: + for (n--; n > 0; n--) + dma_unmap_single(dev->dma_dev, tx_info.buf[n].addr, + tx_info.buf[n].len, DMA_TO_DEVICE); + +free: +#ifdef CONFIG_NL80211_TESTMODE + /* fix tx_done accounting on queue overflow */ + if (mt76_is_testmode_skb(dev, skb, &hw)) { + struct mt76_phy *phy = hw->priv; + + if (tx_info.skb == phy->test.tx_skb) + phy->test.tx_done--; + } +#endif + + mt76_put_txwi(dev, t); + +free_skb: + status.skb = tx_info.skb; + hw = mt76_tx_status_get_hw(dev, tx_info.skb); + spin_lock_bh(&dev->rx_lock); + ieee80211_tx_status_ext(hw, &status); + spin_unlock_bh(&dev->rx_lock); + + return ret; +} + +static int +mt76_dma_rx_fill_buf(struct mt76_dev *dev, struct mt76_queue *q, + bool allow_direct) +{ + int len = SKB_WITH_OVERHEAD(q->buf_size); + int frames = 0; + + if (!q->ndesc) + return 0; + + while (q->queued < q->ndesc - 1) { + struct mt76_queue_buf qbuf = {}; + void *buf = NULL; + int offset; + + if (mt76_queue_is_wed_rro_ind(q) || + mt76_queue_is_wed_rro_rxdmad_c(q)) + goto done; + + buf = mt76_get_page_pool_buf(q, &offset, q->buf_size); + if (!buf) + break; + + qbuf.addr = page_pool_get_dma_addr(virt_to_head_page(buf)) + + offset + q->buf_offset; +done: + qbuf.len = len - q->buf_offset; + qbuf.skip_unmap = false; + if (mt76_dma_add_rx_buf(dev, q, &qbuf, buf) < 0) { + mt76_put_page_pool_buf(buf, allow_direct); + break; + } + frames++; + } + + if (frames || mt76_queue_is_wed_rx(q)) + mt76_dma_kick_queue(dev, q); + + return frames; +} + +int mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q, + bool allow_direct) +{ + int frames; + + spin_lock_bh(&q->lock); + frames = mt76_dma_rx_fill_buf(dev, q, allow_direct); + spin_unlock_bh(&q->lock); + + return frames; +} + +static int +mt76_dma_alloc_queue(struct mt76_dev *dev, struct mt76_queue *q, + int idx, int n_desc, int bufsize, + u32 ring_base) +{ + int ret, size; + + spin_lock_init(&q->lock); + spin_lock_init(&q->cleanup_lock); + + q->regs = dev->mmio.regs + ring_base + idx * MT_RING_SIZE; + q->ndesc = n_desc; + q->buf_size = bufsize; + q->hw_idx = idx; + q->dev = dev; + + if (mt76_queue_is_wed_rro_ind(q)) + size = sizeof(struct mt76_wed_rro_desc); + else if (mt76_queue_is_npu_tx(q)) + size = sizeof(struct airoha_npu_tx_dma_desc); + else if (mt76_queue_is_npu_rx(q)) + size = sizeof(struct airoha_npu_rx_dma_desc); + else + size = sizeof(struct mt76_desc); + + q->desc = dmam_alloc_coherent(dev->dma_dev, q->ndesc * size, + &q->desc_dma, GFP_KERNEL); + if (!q->desc) + return -ENOMEM; + + mt76_dma_queue_magic_cnt_init(dev, q); + size = q->ndesc * sizeof(*q->entry); + q->entry = devm_kzalloc(dev->dev, size, GFP_KERNEL); + if (!q->entry) + return -ENOMEM; + + ret = mt76_create_page_pool(dev, q); + if (ret) + return ret; + + mt76_npu_queue_setup(dev, q); + ret = mt76_wed_dma_setup(dev, q, false); + if (ret) + return ret; + + if (mtk_wed_device_active(&dev->mmio.wed)) { + if ((mtk_wed_get_rx_capa(&dev->mmio.wed) && mt76_queue_is_wed_rro(q)) || + mt76_queue_is_wed_tx_free(q)) + return 0; + } + + /* HW specific driver is supposed to reset brand-new EMI queues since + * it needs to set cpu index pointer. + */ + mt76_dma_queue_reset(dev, q, !mt76_queue_is_emi(q)); + + return 0; +} + +static void +mt76_dma_rx_cleanup(struct mt76_dev *dev, struct mt76_queue *q) +{ + void *buf; + bool more; + + if (!q->ndesc) + return; + + if (mt76_queue_is_npu(q)) { + mt76_npu_queue_cleanup(dev, q); + return; + } + + do { + spin_lock_bh(&q->lock); + buf = mt76_dma_dequeue(dev, q, true, NULL, NULL, &more, NULL); + spin_unlock_bh(&q->lock); + + if (!buf) + break; + + if (mtk_wed_device_active(&dev->mmio.wed) && + mt76_queue_is_wed_rro(q)) + continue; + + if (mt76_npu_device_active(dev) && + mt76_queue_is_wed_rro(q)) + continue; + + if (!mt76_queue_is_wed_rro_rxdmad_c(q) && + !mt76_queue_is_wed_rro_ind(q)) + mt76_put_page_pool_buf(buf, false); + } while (1); + + spin_lock_bh(&q->lock); + if (q->rx_head) { + dev_kfree_skb(q->rx_head); + q->rx_head = NULL; + } + + spin_unlock_bh(&q->lock); +} + +static void +mt76_dma_rx_reset(struct mt76_dev *dev, enum mt76_rxq_id qid) +{ + struct mt76_queue *q = &dev->q_rx[qid]; + + if (!q->ndesc) + return; + + if (!mt76_queue_is_wed_rro_ind(q) && + !mt76_queue_is_wed_rro_rxdmad_c(q) && !mt76_queue_is_npu(q)) { + int i; + + for (i = 0; i < q->ndesc; i++) + q->desc[i].ctrl = cpu_to_le32(MT_DMA_CTL_DMA_DONE); + } + + mt76_dma_rx_cleanup(dev, q); + + /* reset WED rx queues */ + mt76_wed_dma_setup(dev, q, true); + + if (mt76_queue_is_wed_tx_free(q)) + return; + + if (mtk_wed_device_active(&dev->mmio.wed) && + mt76_queue_is_wed_rro(q)) + return; + + if (mt76_npu_device_active(dev) && + mt76_queue_is_wed_rro(q)) + return; + + if (mt76_queue_is_npu_txfree(q)) + return; + + mt76_dma_sync_idx(dev, q); + if (mt76_queue_is_npu(q)) + mt76_npu_fill_rx_queue(dev, q); + else + mt76_dma_rx_fill(dev, q, false); +} + +static void +mt76_add_fragment(struct mt76_dev *dev, struct mt76_queue *q, void *data, + int len, bool more, u32 info, bool allow_direct) +{ + struct sk_buff *skb = q->rx_head; + struct skb_shared_info *shinfo = skb_shinfo(skb); + int nr_frags = shinfo->nr_frags; + + if (nr_frags < ARRAY_SIZE(shinfo->frags)) { + struct page *page = virt_to_head_page(data); + int offset = data - page_address(page) + q->buf_offset; + + skb_add_rx_frag(skb, nr_frags, page, offset, len, q->buf_size); + } else { + mt76_put_page_pool_buf(data, allow_direct); + } + + if (more) + return; + + q->rx_head = NULL; + if (nr_frags < ARRAY_SIZE(shinfo->frags)) + dev->drv->rx_skb(dev, q - dev->q_rx, skb, &info); + else + dev_kfree_skb(skb); +} + +static int +mt76_dma_rx_process(struct mt76_dev *dev, struct mt76_queue *q, int budget) +{ + int len, data_len, done = 0, dma_idx; + struct sk_buff *skb; + unsigned char *data; + bool check_ddone = false; + bool allow_direct = !mt76_queue_is_wed_rx(q) && + !mt76_queue_is_wed_rro_rxdmad_c(q); + bool more; + + if ((q->flags & MT_QFLAG_WED_RRO_EN) || + (IS_ENABLED(CONFIG_NET_MEDIATEK_SOC_WED) && + mt76_queue_is_wed_tx_free(q))) { + dma_idx = mt76_dma_read_dma_idx(q, q->tail); + check_ddone = true; + } + + while (done < budget) { + bool drop = false; + u32 info = 0; + + if (check_ddone) { + if (q->tail == dma_idx) + dma_idx = mt76_dma_read_dma_idx(q, q->tail); + + if (q->tail == dma_idx) + break; + } + + data = mt76_dma_dequeue(dev, q, false, &len, &info, &more, + &drop); + if (!data) + break; + + if (PTR_ERR(data) == -EAGAIN) { + done++; + continue; + } + + if (mt76_queue_is_wed_rro_ind(q) && dev->drv->rx_rro_ind_process) + dev->drv->rx_rro_ind_process(dev, data); + + if (mt76_queue_is_wed_rro(q) && + !mt76_queue_is_wed_rro_rxdmad_c(q)) { + done++; + continue; + } + + if (drop) + goto free_frag; + + if (q->rx_head) + data_len = q->buf_size; + else + data_len = SKB_WITH_OVERHEAD(q->buf_size); + + if (data_len < len + q->buf_offset) { + dev_kfree_skb(q->rx_head); + q->rx_head = NULL; + goto free_frag; + } + + if (q->rx_head) { + mt76_add_fragment(dev, q, data, len, more, info, + allow_direct); + continue; + } + + if (!more && dev->drv->rx_check && + !(dev->drv->rx_check(dev, data, len))) + goto free_frag; + + skb = napi_build_skb(data, q->buf_size); + if (!skb) + goto free_frag; + + skb_reserve(skb, q->buf_offset); + skb_mark_for_recycle(skb); + + *(u32 *)skb->cb = info; + + __skb_put(skb, len); + done++; + + if (more) { + q->rx_head = skb; + continue; + } + + dev->drv->rx_skb(dev, q - dev->q_rx, skb, &info); + continue; + +free_frag: + mt76_put_page_pool_buf(data, allow_direct); + } + + mt76_dma_rx_fill(dev, q, true); + return done; +} + +int mt76_dma_rx_poll(struct napi_struct *napi, int budget) +{ + struct mt76_dev *dev; + int qid, done = 0, cur; + + dev = mt76_priv(napi->dev); + qid = napi - dev->napi; + + rcu_read_lock(); + + do { + cur = mt76_dma_rx_process(dev, &dev->q_rx[qid], budget - done); + mt76_rx_poll_complete(dev, qid, napi); + done += cur; + } while (cur && done < budget); + + rcu_read_unlock(); + + if (done < budget && napi_complete(napi)) + dev->drv->rx_poll_complete(dev, qid); + + return done; +} +EXPORT_SYMBOL_GPL(mt76_dma_rx_poll); + +static void +mt76_dma_rx_queue_init(struct mt76_dev *dev, enum mt76_rxq_id qid, + int (*poll)(struct napi_struct *napi, int budget)) +{ + netif_napi_add(dev->napi_dev, &dev->napi[qid], poll); + mt76_dma_rx_fill_buf(dev, &dev->q_rx[qid], false); + napi_enable(&dev->napi[qid]); +} + +static int +mt76_dma_init(struct mt76_dev *dev, + int (*poll)(struct napi_struct *napi, int budget)) +{ + struct mt76_dev **priv; + int i; + + dev->napi_dev = alloc_netdev_dummy(sizeof(struct mt76_dev *)); + if (!dev->napi_dev) + return -ENOMEM; + + /* napi_dev private data points to mt76_dev parent, so, mt76_dev + * can be retrieved given napi_dev + */ + priv = netdev_priv(dev->napi_dev); + *priv = dev; + + dev->tx_napi_dev = alloc_netdev_dummy(sizeof(struct mt76_dev *)); + if (!dev->tx_napi_dev) { + free_netdev(dev->napi_dev); + return -ENOMEM; + } + priv = netdev_priv(dev->tx_napi_dev); + *priv = dev; + + snprintf(dev->napi_dev->name, sizeof(dev->napi_dev->name), "%s", + wiphy_name(dev->hw->wiphy)); + dev->napi_dev->threaded = 1; + init_completion(&dev->mmio.wed_reset); + init_completion(&dev->mmio.wed_reset_complete); + + mt76_for_each_q_rx(dev, i) { + if (mt76_queue_is_wed_rro(&dev->q_rx[i])) + continue; + + mt76_dma_rx_queue_init(dev, i, poll); + } + + return 0; +} + +static const struct mt76_queue_ops mt76_dma_ops = { + .init = mt76_dma_init, + .alloc = mt76_dma_alloc_queue, + .reset_q = mt76_dma_queue_reset, + .tx_queue_skb_raw = mt76_dma_tx_queue_skb_raw, + .tx_queue_skb = mt76_dma_tx_queue_skb, + .tx_cleanup = mt76_dma_tx_cleanup, + .rx_queue_init = mt76_dma_rx_queue_init, + .rx_cleanup = mt76_dma_rx_cleanup, + .rx_reset = mt76_dma_rx_reset, + .kick = mt76_dma_kick_queue, +}; + +void mt76_dma_attach(struct mt76_dev *dev) +{ + dev->queue_ops = &mt76_dma_ops; +} +EXPORT_SYMBOL_GPL(mt76_dma_attach); + +void mt76_dma_cleanup(struct mt76_dev *dev) +{ + int i; + + mt76_worker_disable(&dev->tx_worker); + napi_disable(&dev->tx_napi); + netif_napi_del(&dev->tx_napi); + + for (i = 0; i < ARRAY_SIZE(dev->phys); i++) { + struct mt76_phy *phy = dev->phys[i]; + int j; + + if (!phy) + continue; + + for (j = 0; j < ARRAY_SIZE(phy->q_tx); j++) + mt76_dma_tx_cleanup(dev, phy->q_tx[j], true); + } + + for (i = 0; i < ARRAY_SIZE(dev->q_mcu); i++) + mt76_dma_tx_cleanup(dev, dev->q_mcu[i], true); + + mt76_for_each_q_rx(dev, i) { + struct mt76_queue *q = &dev->q_rx[i]; + + netif_napi_del(&dev->napi[i]); + mt76_dma_rx_cleanup(dev, q); + + page_pool_destroy(q->page_pool); + } + + if (mtk_wed_device_active(&dev->mmio.wed)) + mtk_wed_device_detach(&dev->mmio.wed); + + if (mtk_wed_device_active(&dev->mmio.wed_hif2)) + mtk_wed_device_detach(&dev->mmio.wed_hif2); + + mt76_free_pending_txwi(dev); + mt76_free_pending_rxwi(dev); + free_netdev(dev->napi_dev); + free_netdev(dev->tx_napi_dev); +} +EXPORT_SYMBOL_GPL(mt76_dma_cleanup); diff --git a/drivers/net/wireless/mediatek/mt76/dma.h b/drivers/net/wireless/mediatek/mt76/dma.h new file mode 100644 index 000000000..7694764fe --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/dma.h @@ -0,0 +1,220 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (C) 2016 Felix Fietkau + */ +#ifndef __MT76_DMA_H +#define __MT76_DMA_H + +#include + +#define DMA_DUMMY_DATA ((void *)~0) + +#define MT_RING_SIZE 0x10 + +#define MT_DMA_CTL_SD_LEN1 GENMASK(13, 0) +#define MT_DMA_CTL_LAST_SEC1 BIT(14) +#define MT_DMA_CTL_M_DONE BIT(15) +#define MT_DMA_CTL_SD_LEN0 GENMASK(29, 16) +#define MT_DMA_CTL_LAST_SEC0 BIT(30) +#define MT_DMA_CTL_DMA_DONE BIT(31) +#define MT_DMA_CTL_TO_HOST BIT(8) +#define MT_DMA_CTL_TO_HOST_A BIT(12) +#define MT_DMA_CTL_DROP BIT(14) +#define MT_DMA_CTL_TOKEN GENMASK(31, 16) +#define MT_DMA_CTL_SDP1_H GENMASK(19, 16) +#define MT_DMA_CTL_SDP0_H GENMASK(3, 0) +#define MT_DMA_CTL_WO_DROP BIT(8) + +#define MT_DMA_PPE_CPU_REASON GENMASK(15, 11) +#define MT_DMA_PPE_ENTRY GENMASK(30, 16) +#define MT_DMA_INFO_DMA_FRAG BIT(9) +#define MT_DMA_INFO_PPE_VLD BIT(31) + +#define MT_DMA_CTL_PN_CHK_FAIL BIT(13) +#define MT_DMA_CTL_VER_MASK BIT(7) + +#define MT_DMA_SDP0 GENMASK(15, 0) +#define MT_DMA_TOKEN_ID GENMASK(31, 16) +#define MT_DMA_MAGIC_MASK GENMASK(31, 28) +#define MT_DMA_RRO_EN BIT(13) + +#define MT_DMA_MAGIC_CNT 16 + +#define MT_DMA_WED_IND_CMD_CNT 8 +#define MT_DMA_WED_IND_REASON GENMASK(15, 12) + +#define MT_DMA_HDR_LEN 4 +#define MT_RX_INFO_LEN 4 +#define MT_FCE_INFO_LEN 4 +#define MT_RX_RXWI_LEN 32 + +static inline bool +mt76_dma_handle_read(struct mt76_queue *q, u32 offset, u32 *val) +{ +#if IS_ENABLED(CONFIG_NET_MEDIATEK_SOC_WED) + if (q->flags & MT_QFLAG_WED) { + *val = mtk_wed_device_reg_read(q->wed, q->wed_regs + offset); + + return true; + } +#endif +#if IS_ENABLED(CONFIG_MT76_NPU) + if (q->flags & MT_QFLAG_NPU) { + struct airoha_npu *npu; + + *val = 0; + rcu_read_lock(); + npu = rcu_dereference(q->dev->mmio.npu); + if (npu) + regmap_read(npu->regmap, q->wed_regs + offset, val); + rcu_read_unlock(); + + return true; + } +#endif + + return false; +} + +static inline bool +mt76_dma_handle_write(struct mt76_queue *q, u32 offset, u32 val) +{ +#if IS_ENABLED(CONFIG_NET_MEDIATEK_SOC_WED) + if (q->flags & MT_QFLAG_WED) { + mtk_wed_device_reg_write(q->wed, q->wed_regs + offset, val); + + return true; + } +#endif +#if IS_ENABLED(CONFIG_MT76_NPU) + if (q->flags & MT_QFLAG_NPU) { + struct airoha_npu *npu; + + rcu_read_lock(); + npu = rcu_dereference(q->dev->mmio.npu); + if (npu) + regmap_write(npu->regmap, q->wed_regs + offset, val); + rcu_read_unlock(); + + return true; + } +#endif + + return false; +} + +#define Q_READ(_q, _field) ({ \ + u32 _offset = offsetof(struct mt76_queue_regs, _field); \ + u32 _val; \ + if (!mt76_dma_handle_read(_q, _offset, &_val)) \ + _val = readl(&(_q)->regs->_field); \ + _val; \ +}) + +#define Q_WRITE(_q, _field, _val) do { \ + u32 _offset = offsetof(struct mt76_queue_regs, _field); \ + if (!mt76_dma_handle_write(_q, _offset, _val)) \ + writel(_val, &(_q)->regs->_field); \ +} while (0) + +struct mt76_desc { + __le32 buf0; + __le32 ctrl; + __le32 buf1; + __le32 info; +} __packed __aligned(4); + +struct mt76_wed_rro_desc { + __le32 buf0; + __le32 buf1; +} __packed __aligned(4); + +/* data1 */ +#define RRO_RXDMAD_DATA1_LS_MASK BIT(30) +#define RRO_RXDMAD_DATA1_SDL0_MASK GENMASK(29, 16) +/* data2 */ +#define RRO_RXDMAD_DATA2_RX_TOKEN_ID_MASK GENMASK(31, 16) +#define RRO_RXDMAD_DATA2_IND_REASON_MASK GENMASK(15, 12) +/* data3 */ +#define RRO_RXDMAD_DATA3_MAGIC_CNT_MASK GENMASK(31, 28) +struct mt76_rro_rxdmad_c { + __le32 data0; + __le32 data1; + __le32 data2; + __le32 data3; +}; + +enum mt76_qsel { + MT_QSEL_MGMT, + MT_QSEL_HCCA, + MT_QSEL_EDCA, + MT_QSEL_EDCA_2, +}; + +enum mt76_mcu_evt_type { + EVT_CMD_DONE, + EVT_CMD_ERROR, + EVT_CMD_RETRY, + EVT_EVENT_PWR_RSP, + EVT_EVENT_WOW_RSP, + EVT_EVENT_CARRIER_DETECT_RSP, + EVT_EVENT_DFS_DETECT_RSP, +}; + +enum mt76_dma_wed_ind_reason { + MT_DMA_WED_IND_REASON_NORMAL, + MT_DMA_WED_IND_REASON_REPEAT, + MT_DMA_WED_IND_REASON_OLDPKT, +}; + +int mt76_dma_rx_poll(struct napi_struct *napi, int budget); +void mt76_dma_attach(struct mt76_dev *dev); +void mt76_dma_cleanup(struct mt76_dev *dev); +int mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q, + bool allow_direct); +void mt76_dma_queue_reset(struct mt76_dev *dev, struct mt76_queue *q, + bool reset_idx); + +static inline void +mt76_dma_reset_tx_queue(struct mt76_dev *dev, struct mt76_queue *q) +{ + bool reset_idx = q && !mt76_queue_is_npu_tx(q); + + dev->queue_ops->reset_q(dev, q, reset_idx); + if (mtk_wed_device_active(&dev->mmio.wed)) + mt76_wed_dma_setup(dev, q, true); +} + +static inline void +mt76_dma_should_drop_buf(bool *drop, u32 ctrl, u32 buf1, u32 info) +{ + if (!drop) + return; + + *drop = !!(ctrl & (MT_DMA_CTL_TO_HOST_A | MT_DMA_CTL_DROP)); + if (!(ctrl & MT_DMA_CTL_VER_MASK)) + return; + + switch (FIELD_GET(MT_DMA_WED_IND_REASON, buf1)) { + case MT_DMA_WED_IND_REASON_REPEAT: + *drop = true; + break; + case MT_DMA_WED_IND_REASON_OLDPKT: + *drop = !(info & MT_DMA_INFO_DMA_FRAG); + break; + default: + *drop = !!(ctrl & MT_DMA_CTL_PN_CHK_FAIL); + break; + } +} + +static inline void *mt76_priv(struct net_device *dev) +{ + struct mt76_dev **priv; + + priv = netdev_priv(dev); + + return *priv; +} + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/eeprom.c b/drivers/net/wireless/mediatek/mt76/eeprom.c new file mode 100644 index 000000000..afdb73661 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/eeprom.c @@ -0,0 +1,531 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (C) 2016 Felix Fietkau + */ +#include +#include +#include +#include +#include +#include +#include "mt76.h" +#include "mt76_connac.h" + +enum mt76_sku_type { + MT76_SKU_RATE, + MT76_SKU_BACKOFF, + MT76_SKU_BACKOFF_BF_OFFSET, +}; + +static int mt76_get_of_eeprom_data(struct mt76_dev *dev, void *eep, int len) +{ + struct device_node *np = dev->dev->of_node; + const void *data; + int size; + + data = of_get_property(np, "mediatek,eeprom-data", &size); + if (!data) + return -ENOENT; + + if (size > len) + return -EINVAL; + + memcpy(eep, data, size); + + return 0; +} + +int mt76_get_of_data_from_mtd(struct mt76_dev *dev, void *eep, int offset, int len) +{ +#ifdef CONFIG_MTD + struct device_node *np = dev->dev->of_node; + struct mtd_info *mtd; + const __be32 *list; + const char *part; + phandle phandle; + size_t retlen; + int size; + int ret; + + 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, eep); + put_mtd_device(mtd); + if (mtd_is_bitflip(ret)) + ret = 0; + if (ret) { + dev_err(dev->dev, "reading EEPROM from mtd %s failed: %i\n", + part, ret); + goto out_put_node; + } + + if (retlen < len) { + ret = -EINVAL; + goto out_put_node; + } + + if (of_property_read_bool(dev->dev->of_node, "big-endian")) { + u8 *data = (u8 *)eep; + int i; + + /* convert eeprom data in Little Endian */ + for (i = 0; i < round_down(len, 2); i += 2) + put_unaligned_le16(get_unaligned_be16(&data[i]), + &data[i]); + } + +#ifdef CONFIG_NL80211_TESTMODE + dev->test_mtd.name = devm_kstrdup(dev->dev, part, GFP_KERNEL); + if (!dev->test_mtd.name) { + ret = -ENOMEM; + goto out_put_node; + } + dev->test_mtd.offset = offset; +#endif + +out_put_node: + of_node_put(np); + return ret; +#else + return -ENOENT; +#endif +} +EXPORT_SYMBOL_GPL(mt76_get_of_data_from_mtd); + +int mt76_get_of_data_from_nvmem(struct mt76_dev *dev, void *eep, + const char *cell_name, int len) +{ + struct device_node *np = dev->dev->of_node; + struct nvmem_cell *cell; + const void *data; + size_t retlen; + int ret = 0; + + cell = of_nvmem_cell_get(np, cell_name); + if (IS_ERR(cell)) + return PTR_ERR(cell); + + data = nvmem_cell_read(cell, &retlen); + nvmem_cell_put(cell); + + if (IS_ERR(data)) + return PTR_ERR(data); + + if (retlen < len) { + ret = -EINVAL; + goto exit; + } + + memcpy(eep, data, len); + +exit: + kfree(data); + + return ret; +} +EXPORT_SYMBOL_GPL(mt76_get_of_data_from_nvmem); + +static int mt76_get_of_eeprom(struct mt76_dev *dev, void *eep, int len) +{ + struct device_node *np = dev->dev->of_node; + int ret; + + if (!np) + return -ENOENT; + + ret = mt76_get_of_eeprom_data(dev, eep, len); + if (!ret) + return 0; + + ret = mt76_get_of_data_from_mtd(dev, eep, 0, len); + if (!ret) + return 0; + + return mt76_get_of_data_from_nvmem(dev, eep, "eeprom", len); +} + +int +mt76_eeprom_override(struct mt76_phy *phy) +{ + struct mt76_dev *dev = phy->dev; + struct device_node *np = dev->dev->of_node; + int err; + + err = of_get_mac_address(np, phy->macaddr); + if (err == -EPROBE_DEFER) + return err; + + if (!is_valid_ether_addr(phy->macaddr)) { + eth_random_addr(phy->macaddr); + dev_info(dev->dev, + "Invalid MAC address, using random address %pM\n", + phy->macaddr); + } + + return 0; +} +EXPORT_SYMBOL_GPL(mt76_eeprom_override); + +static bool mt76_string_prop_find(struct property *prop, const char *str) +{ + const char *cp = NULL; + + if (!prop || !str || !str[0]) + return false; + + while ((cp = of_prop_next_string(prop, cp)) != NULL) + if (!strcasecmp(cp, str)) + return true; + + return false; +} + +struct device_node * +mt76_find_power_limits_node(struct mt76_dev *dev) +{ + struct device_node *np = dev->dev->of_node; + const char *const region_names[] = { + [NL80211_DFS_UNSET] = "ww", + [NL80211_DFS_ETSI] = "etsi", + [NL80211_DFS_FCC] = "fcc", + [NL80211_DFS_JP] = "jp", + }; + struct device_node *cur, *fallback = NULL; + const char *region_name = NULL; + + if (dev->region < ARRAY_SIZE(region_names)) + region_name = region_names[dev->region]; + + np = of_get_child_by_name(np, "power-limits"); + if (!np) + return NULL; + + for_each_child_of_node(np, cur) { + struct property *country = of_find_property(cur, "country", NULL); + struct property *regd = of_find_property(cur, "regdomain", NULL); + + if (!country && !regd) { + fallback = cur; + continue; + } + + if (mt76_string_prop_find(country, dev->alpha2) || + mt76_string_prop_find(regd, region_name)) { + of_node_put(np); + return cur; + } + } + + of_node_put(np); + return fallback; +} +EXPORT_SYMBOL_GPL(mt76_find_power_limits_node); + +static const __be32 * +mt76_get_of_array(struct device_node *np, char *name, size_t *len, int min) +{ + struct property *prop = of_find_property(np, name, NULL); + + if (!prop || !prop->value || prop->length < min * 4) + return NULL; + + *len = prop->length; + + return prop->value; +} + +static const s8 * +mt76_get_of_array_s8(struct device_node *np, char *name, size_t *len, int min) +{ + struct property *prop = of_find_property(np, name, NULL); + + if (!prop || !prop->value || prop->length < min) + return NULL; + + *len = prop->length; + + return prop->value; +} + +struct device_node * +mt76_find_channel_node(struct device_node *np, struct ieee80211_channel *chan) +{ + struct device_node *cur; + const __be32 *val; + size_t len; + + for_each_child_of_node(np, cur) { + val = mt76_get_of_array(cur, "channels", &len, 2); + if (!val) + continue; + + while (len >= 2 * sizeof(*val)) { + if (chan->hw_value >= be32_to_cpu(val[0]) && + chan->hw_value <= be32_to_cpu(val[1])) + return cur; + + val += 2; + len -= 2 * sizeof(*val); + } + } + + return NULL; +} +EXPORT_SYMBOL_GPL(mt76_find_channel_node); + +static s8 +mt76_get_txs_delta(struct device_node *np, u8 nss) +{ + const __be32 *val; + size_t len; + + val = mt76_get_of_array(np, "txs-delta", &len, nss); + if (!val) + return 0; + + return be32_to_cpu(val[nss - 1]); +} + +static inline u8 mt76_backoff_n_chains(struct mt76_dev *dev, u8 idx) +{ + /* 0:1T1ss, 1:2T1ss, ..., 14:5T5ss */ + static const u8 connac3_table[] = { + 1, 2, 3, 4, 5, 2, 3, 4, 5, 3, 4, 5, 4, 5, 5}; + static const u8 connac2_table[] = { + 1, 2, 3, 4, 2, 3, 4, 3, 4, 4, 0, 0, 0, 0, 0}; + + if (idx >= ARRAY_SIZE(connac3_table)) + return 0; + + return is_mt799x(dev) ? connac3_table[idx] : connac2_table[idx]; +} + +static void +mt76_apply_array_limit(struct mt76_dev *dev, s8 *pwr, size_t pwr_len, + const s8 *data, s8 target_power, s8 nss_delta, + s8 *max_power, int n_chains, enum mt76_sku_type type) +{ + int i; + + if (!data) + return; + + for (i = 0; i < pwr_len; i++) { + u8 backoff_chain_idx = i; + int backoff_n_chains; + s8 backoff_delta; + s8 delta; + + switch (type) { + case MT76_SKU_RATE: + delta = 0; + backoff_delta = 0; + backoff_n_chains = 0; + break; + case MT76_SKU_BACKOFF_BF_OFFSET: + backoff_chain_idx += 1; + fallthrough; + case MT76_SKU_BACKOFF: + delta = mt76_tx_power_path_delta(n_chains); + backoff_n_chains = mt76_backoff_n_chains(dev, backoff_chain_idx); + backoff_delta = mt76_tx_power_path_delta(backoff_n_chains); + break; + default: + return; + } + + pwr[i] = min_t(s8, target_power + delta - backoff_delta, data[i] + nss_delta); + + /* used for padding, doesn't need to be considered */ + if (data[i] >= S8_MAX - 1) + continue; + + /* only consider backoff value for the configured chain number */ + if (type != MT76_SKU_RATE && n_chains != backoff_n_chains) + continue; + + *max_power = max(*max_power, pwr[i]); + } +} + +static void +mt76_apply_multi_array_limit(struct mt76_dev *dev, s8 *pwr, size_t pwr_len, + s8 pwr_num, const s8 *data, size_t len, + s8 target_power, s8 nss_delta, s8 *max_power, + int n_chains, enum mt76_sku_type type) +{ + static const int connac2_backoff_ru_idx = 2; + int i, cur; + + if (!data) + return; + + cur = data[0]; + for (i = 0; i < pwr_num; i++) { + if (len < pwr_len + 1) + break; + + /* Each RU entry (RU26, RU52, RU106, BW20, ...) in the DTS + * corresponds to 10 stream combinations (1T1ss, 2T1ss, 3T1ss, + * 4T1ss, 2T2ss, 3T2ss, 4T2ss, 3T3ss, 4T3ss, 4T4ss). + * + * For beamforming tables: + * - In connac2, beamforming entries for BW20~BW160 and OFDM + * do not include 1T1ss. + * - In connac3, beamforming entries for BW20~BW160 and RU + * include 1T1ss, but OFDM beamforming does not include 1T1ss. + * + * Non-beamforming and RU entries for both connac2 and connac3 + * include 1T1ss. + */ + if (!is_mt799x(dev) && type == MT76_SKU_BACKOFF && + i > connac2_backoff_ru_idx) + type = MT76_SKU_BACKOFF_BF_OFFSET; + + mt76_apply_array_limit(dev, pwr + pwr_len * i, pwr_len, data + 1, + target_power, nss_delta, max_power, + n_chains, type); + if (--cur > 0) + continue; + + data += pwr_len + 1; + len -= pwr_len + 1; + if (!len) + break; + + cur = data[0]; + } +} + +s8 mt76_get_rate_power_limits(struct mt76_phy *phy, + struct ieee80211_channel *chan, + struct mt76_power_limits *dest, + s8 target_power) +{ + struct mt76_dev *dev = phy->dev; + struct device_node *np; + const s8 *val; + char name[16]; + char band; + size_t len; + s8 max_power = -127; + s8 txs_delta; + int n_chains = hweight16(phy->chainmask); + + memset(dest, target_power, sizeof(*dest) - sizeof(dest->path)); + memset(&dest->path, 0, sizeof(dest->path)); + + if (!IS_ENABLED(CONFIG_OF)) + return target_power; + + np = mt76_find_power_limits_node(dev); + if (!np) + return target_power; + + switch (chan->band) { + case NL80211_BAND_2GHZ: + band = '2'; + break; + case NL80211_BAND_5GHZ: + band = '5'; + break; + case NL80211_BAND_6GHZ: + band = '6'; + break; + default: + return target_power; + } + + snprintf(name, sizeof(name), "txpower-%cg", band); + np = of_get_child_by_name(np, name); + if (!np) + return target_power; + + np = mt76_find_channel_node(np, chan); + if (!np) + return target_power; + + txs_delta = mt76_get_txs_delta(np, hweight16(phy->chainmask)); + + val = mt76_get_of_array_s8(np, "rates-cck", &len, ARRAY_SIZE(dest->cck)); + mt76_apply_array_limit(dev, dest->cck, ARRAY_SIZE(dest->cck), val, + target_power, txs_delta, &max_power, n_chains, MT76_SKU_RATE); + + val = mt76_get_of_array_s8(np, "rates-ofdm", &len, ARRAY_SIZE(dest->ofdm)); + mt76_apply_array_limit(dev, dest->ofdm, ARRAY_SIZE(dest->ofdm), val, + target_power, txs_delta, &max_power, n_chains, MT76_SKU_RATE); + + val = mt76_get_of_array_s8(np, "rates-mcs", &len, ARRAY_SIZE(dest->mcs[0]) + 1); + mt76_apply_multi_array_limit(dev, dest->mcs[0], ARRAY_SIZE(dest->mcs[0]), + ARRAY_SIZE(dest->mcs), val, len, target_power, + txs_delta, &max_power, n_chains, MT76_SKU_RATE); + + val = mt76_get_of_array_s8(np, "rates-ru", &len, ARRAY_SIZE(dest->ru[0]) + 1); + mt76_apply_multi_array_limit(dev, dest->ru[0], ARRAY_SIZE(dest->ru[0]), + ARRAY_SIZE(dest->ru), val, len, target_power, + txs_delta, &max_power, n_chains, MT76_SKU_RATE); + + val = mt76_get_of_array_s8(np, "paths-cck", &len, ARRAY_SIZE(dest->path.cck)); + mt76_apply_array_limit(dev, dest->path.cck, ARRAY_SIZE(dest->path.cck), val, + target_power, txs_delta, &max_power, n_chains, MT76_SKU_BACKOFF); + + val = mt76_get_of_array_s8(np, "paths-ofdm", &len, ARRAY_SIZE(dest->path.ofdm)); + mt76_apply_array_limit(dev, dest->path.ofdm, ARRAY_SIZE(dest->path.ofdm), val, + target_power, txs_delta, &max_power, n_chains, MT76_SKU_BACKOFF); + + val = mt76_get_of_array_s8(np, "paths-ofdm-bf", &len, ARRAY_SIZE(dest->path.ofdm_bf)); + mt76_apply_array_limit(dev, dest->path.ofdm_bf, ARRAY_SIZE(dest->path.ofdm_bf), val, + target_power, txs_delta, &max_power, n_chains, + MT76_SKU_BACKOFF_BF_OFFSET); + + val = mt76_get_of_array_s8(np, "paths-ru", &len, ARRAY_SIZE(dest->path.ru[0]) + 1); + mt76_apply_multi_array_limit(dev, dest->path.ru[0], ARRAY_SIZE(dest->path.ru[0]), + ARRAY_SIZE(dest->path.ru), val, len, target_power, + txs_delta, &max_power, n_chains, MT76_SKU_BACKOFF); + + val = mt76_get_of_array_s8(np, "paths-ru-bf", &len, ARRAY_SIZE(dest->path.ru_bf[0]) + 1); + mt76_apply_multi_array_limit(dev, dest->path.ru_bf[0], ARRAY_SIZE(dest->path.ru_bf[0]), + ARRAY_SIZE(dest->path.ru_bf), val, len, target_power, + txs_delta, &max_power, n_chains, MT76_SKU_BACKOFF); + + return max_power; +} +EXPORT_SYMBOL_GPL(mt76_get_rate_power_limits); + +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, dev->eeprom.data, len); +} +EXPORT_SYMBOL_GPL(mt76_eeprom_init); diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c new file mode 100644 index 000000000..abbe65cbc --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mac80211.c @@ -0,0 +1,2376 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (C) 2016 Felix Fietkau + */ +#include +#include +#include "mt76.h" + +#define CHAN2G(_idx, _freq) { \ + .band = NL80211_BAND_2GHZ, \ + .center_freq = (_freq), \ + .hw_value = (_idx), \ + .max_power = 30, \ +} + +#define CHAN5G(_idx, _freq) { \ + .band = NL80211_BAND_5GHZ, \ + .center_freq = (_freq), \ + .hw_value = (_idx), \ + .max_power = 30, \ +} + +#define CHAN6G(_idx, _freq) { \ + .band = NL80211_BAND_6GHZ, \ + .center_freq = (_freq), \ + .hw_value = (_idx), \ + .max_power = 30, \ +} + +static const struct ieee80211_channel mt76_channels_2ghz[] = { + CHAN2G(1, 2412), + CHAN2G(2, 2417), + CHAN2G(3, 2422), + CHAN2G(4, 2427), + CHAN2G(5, 2432), + CHAN2G(6, 2437), + CHAN2G(7, 2442), + CHAN2G(8, 2447), + CHAN2G(9, 2452), + CHAN2G(10, 2457), + CHAN2G(11, 2462), + CHAN2G(12, 2467), + CHAN2G(13, 2472), + CHAN2G(14, 2484), +}; + +static const struct ieee80211_channel mt76_channels_5ghz[] = { + CHAN5G(36, 5180), + CHAN5G(40, 5200), + CHAN5G(44, 5220), + CHAN5G(48, 5240), + + CHAN5G(52, 5260), + CHAN5G(56, 5280), + CHAN5G(60, 5300), + CHAN5G(64, 5320), + + CHAN5G(100, 5500), + CHAN5G(104, 5520), + CHAN5G(108, 5540), + CHAN5G(112, 5560), + CHAN5G(116, 5580), + CHAN5G(120, 5600), + CHAN5G(124, 5620), + CHAN5G(128, 5640), + CHAN5G(132, 5660), + CHAN5G(136, 5680), + CHAN5G(140, 5700), + CHAN5G(144, 5720), + + CHAN5G(149, 5745), + CHAN5G(153, 5765), + CHAN5G(157, 5785), + CHAN5G(161, 5805), + CHAN5G(165, 5825), + CHAN5G(169, 5845), + CHAN5G(173, 5865), + CHAN5G(177, 5885), +}; + +static const struct ieee80211_channel mt76_channels_6ghz[] = { + /* UNII-5 */ + CHAN6G(1, 5955), + CHAN6G(5, 5975), + CHAN6G(9, 5995), + CHAN6G(13, 6015), + CHAN6G(17, 6035), + CHAN6G(21, 6055), + CHAN6G(25, 6075), + CHAN6G(29, 6095), + CHAN6G(33, 6115), + CHAN6G(37, 6135), + CHAN6G(41, 6155), + CHAN6G(45, 6175), + CHAN6G(49, 6195), + CHAN6G(53, 6215), + CHAN6G(57, 6235), + CHAN6G(61, 6255), + CHAN6G(65, 6275), + CHAN6G(69, 6295), + CHAN6G(73, 6315), + CHAN6G(77, 6335), + CHAN6G(81, 6355), + CHAN6G(85, 6375), + CHAN6G(89, 6395), + CHAN6G(93, 6415), + /* UNII-6 */ + CHAN6G(97, 6435), + CHAN6G(101, 6455), + CHAN6G(105, 6475), + CHAN6G(109, 6495), + CHAN6G(113, 6515), + CHAN6G(117, 6535), + /* UNII-7 */ + CHAN6G(121, 6555), + CHAN6G(125, 6575), + CHAN6G(129, 6595), + CHAN6G(133, 6615), + CHAN6G(137, 6635), + CHAN6G(141, 6655), + CHAN6G(145, 6675), + CHAN6G(149, 6695), + CHAN6G(153, 6715), + CHAN6G(157, 6735), + CHAN6G(161, 6755), + CHAN6G(165, 6775), + CHAN6G(169, 6795), + CHAN6G(173, 6815), + CHAN6G(177, 6835), + CHAN6G(181, 6855), + CHAN6G(185, 6875), + /* UNII-8 */ + CHAN6G(189, 6895), + CHAN6G(193, 6915), + CHAN6G(197, 6935), + CHAN6G(201, 6955), + CHAN6G(205, 6975), + CHAN6G(209, 6995), + CHAN6G(213, 7015), + CHAN6G(217, 7035), + CHAN6G(221, 7055), + CHAN6G(225, 7075), + CHAN6G(229, 7095), + CHAN6G(233, 7115), +}; + +static const struct ieee80211_tpt_blink mt76_tpt_blink[] = { + { .throughput = 0 * 1024, .blink_time = 334 }, + { .throughput = 1 * 1024, .blink_time = 260 }, + { .throughput = 5 * 1024, .blink_time = 220 }, + { .throughput = 10 * 1024, .blink_time = 190 }, + { .throughput = 20 * 1024, .blink_time = 170 }, + { .throughput = 50 * 1024, .blink_time = 150 }, + { .throughput = 70 * 1024, .blink_time = 130 }, + { .throughput = 100 * 1024, .blink_time = 110 }, + { .throughput = 200 * 1024, .blink_time = 80 }, + { .throughput = 300 * 1024, .blink_time = 50 }, +}; + +struct ieee80211_rate mt76_rates[] = { + CCK_RATE(0, 10), + CCK_RATE(1, 20), + CCK_RATE(2, 55), + CCK_RATE(3, 110), + OFDM_RATE(11, 60), + OFDM_RATE(15, 90), + OFDM_RATE(10, 120), + OFDM_RATE(14, 180), + OFDM_RATE(9, 240), + OFDM_RATE(13, 360), + OFDM_RATE(8, 480), + OFDM_RATE(12, 540), +}; +EXPORT_SYMBOL_GPL(mt76_rates); + +static const struct cfg80211_sar_freq_ranges mt76_sar_freq_ranges[] = { + { .start_freq = 2402, .end_freq = 2494, }, + { .start_freq = 5150, .end_freq = 5350, }, + { .start_freq = 5350, .end_freq = 5470, }, + { .start_freq = 5470, .end_freq = 5725, }, + { .start_freq = 5725, .end_freq = 5950, }, + { .start_freq = 5945, .end_freq = 6165, }, + { .start_freq = 6165, .end_freq = 6405, }, + { .start_freq = 6405, .end_freq = 6525, }, + { .start_freq = 6525, .end_freq = 6705, }, + { .start_freq = 6705, .end_freq = 6865, }, + { .start_freq = 6865, .end_freq = 7125, }, +}; + +static const struct cfg80211_sar_capa mt76_sar_capa = { + .type = NL80211_SAR_TYPE_POWER, + .num_freq_ranges = ARRAY_SIZE(mt76_sar_freq_ranges), + .freq_ranges = &mt76_sar_freq_ranges[0], +}; + +static int mt76_led_init(struct mt76_phy *phy) +{ + struct mt76_dev *dev = phy->dev; + struct ieee80211_hw *hw = phy->hw; + struct device_node *np = dev->dev->of_node; + + if (!phy->leds.cdev.brightness_set && !phy->leds.cdev.blink_set) + return 0; + + np = of_get_child_by_name(np, "led"); + if (np) { + if (!of_device_is_available(np)) { + of_node_put(np); + dev_info(dev->dev, + "led registration was explicitly disabled by dts\n"); + return 0; + } + + if (phy == &dev->phy) { + int led_pin; + + if (!of_property_read_u32(np, "led-sources", &led_pin)) + phy->leds.pin = led_pin; + + phy->leds.al = + of_property_read_bool(np, "led-active-low"); + } + + of_node_put(np); + } + + snprintf(phy->leds.name, sizeof(phy->leds.name), "mt76-%s", + wiphy_name(hw->wiphy)); + + phy->leds.cdev.name = phy->leds.name; + phy->leds.cdev.default_trigger = + ieee80211_create_tpt_led_trigger(hw, + IEEE80211_TPT_LEDTRIG_FL_RADIO, + mt76_tpt_blink, + ARRAY_SIZE(mt76_tpt_blink)); + + dev_info(dev->dev, + "registering led '%s'\n", phy->leds.name); + + return led_classdev_register(dev->dev, &phy->leds.cdev); +} + +static void mt76_led_cleanup(struct mt76_phy *phy) +{ + if (!phy->leds.cdev.brightness_set && !phy->leds.cdev.blink_set) + return; + + led_classdev_unregister(&phy->leds.cdev); +} + +static void mt76_init_stream_cap(struct mt76_phy *phy, + struct ieee80211_supported_band *sband, + bool vht) +{ + struct ieee80211_sta_ht_cap *ht_cap = &sband->ht_cap; + int i, nstream = hweight8(phy->antenna_mask); + struct ieee80211_sta_vht_cap *vht_cap; + u16 mcs_map = 0; + + if (nstream > 1) + ht_cap->cap |= IEEE80211_HT_CAP_TX_STBC; + else + ht_cap->cap &= ~IEEE80211_HT_CAP_TX_STBC; + + for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) + ht_cap->mcs.rx_mask[i] = i < nstream ? 0xff : 0; + + if (!vht) + return; + + vht_cap = &sband->vht_cap; + if (nstream > 1) + vht_cap->cap |= IEEE80211_VHT_CAP_TXSTBC; + else + vht_cap->cap &= ~IEEE80211_VHT_CAP_TXSTBC; + vht_cap->cap |= IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN | + IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN; + + for (i = 0; i < 8; i++) { + if (i < nstream) + mcs_map |= (IEEE80211_VHT_MCS_SUPPORT_0_9 << (i * 2)); + else + mcs_map |= + (IEEE80211_VHT_MCS_NOT_SUPPORTED << (i * 2)); + } + vht_cap->vht_mcs.rx_mcs_map = cpu_to_le16(mcs_map); + vht_cap->vht_mcs.tx_mcs_map = cpu_to_le16(mcs_map); + if (ieee80211_hw_check(phy->hw, SUPPORTS_VHT_EXT_NSS_BW)) + vht_cap->vht_mcs.tx_highest |= + cpu_to_le16(IEEE80211_VHT_EXT_NSS_BW_CAPABLE); +} + +void mt76_set_stream_caps(struct mt76_phy *phy, bool vht) +{ + if (phy->cap.has_2ghz) + mt76_init_stream_cap(phy, &phy->sband_2g.sband, false); + if (phy->cap.has_5ghz) + mt76_init_stream_cap(phy, &phy->sband_5g.sband, vht); + if (phy->cap.has_6ghz) + mt76_init_stream_cap(phy, &phy->sband_6g.sband, vht); +} +EXPORT_SYMBOL_GPL(mt76_set_stream_caps); + +static int +mt76_init_sband(struct mt76_phy *phy, struct mt76_sband *msband, + const struct ieee80211_channel *chan, int n_chan, + struct ieee80211_rate *rates, int n_rates, + bool ht, bool vht) +{ + struct ieee80211_supported_band *sband = &msband->sband; + struct ieee80211_sta_vht_cap *vht_cap; + struct ieee80211_sta_ht_cap *ht_cap; + struct mt76_dev *dev = phy->dev; + void *chanlist; + int size; + + size = n_chan * sizeof(*chan); + chanlist = devm_kmemdup(dev->dev, chan, size, GFP_KERNEL); + if (!chanlist) + return -ENOMEM; + + msband->chan = devm_kcalloc(dev->dev, n_chan, sizeof(*msband->chan), + GFP_KERNEL); + if (!msband->chan) + return -ENOMEM; + + sband->channels = chanlist; + sband->n_channels = n_chan; + sband->bitrates = rates; + sband->n_bitrates = n_rates; + + if (!ht) + return 0; + + ht_cap = &sband->ht_cap; + ht_cap->ht_supported = true; + ht_cap->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40 | + IEEE80211_HT_CAP_GRN_FLD | + IEEE80211_HT_CAP_SGI_20 | + IEEE80211_HT_CAP_SGI_40 | + (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT); + + ht_cap->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; + ht_cap->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; + + mt76_init_stream_cap(phy, sband, vht); + + if (!vht) + return 0; + + vht_cap = &sband->vht_cap; + vht_cap->vht_supported = true; + vht_cap->cap |= IEEE80211_VHT_CAP_RXLDPC | + IEEE80211_VHT_CAP_RXSTBC_1 | + IEEE80211_VHT_CAP_SHORT_GI_80 | + (3 << IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT); + + return 0; +} + +static int +mt76_init_sband_2g(struct mt76_phy *phy, struct ieee80211_rate *rates, + int n_rates) +{ + phy->hw->wiphy->bands[NL80211_BAND_2GHZ] = &phy->sband_2g.sband; + + return mt76_init_sband(phy, &phy->sband_2g, mt76_channels_2ghz, + ARRAY_SIZE(mt76_channels_2ghz), rates, + n_rates, true, false); +} + +static int +mt76_init_sband_5g(struct mt76_phy *phy, struct ieee80211_rate *rates, + int n_rates, bool vht) +{ + phy->hw->wiphy->bands[NL80211_BAND_5GHZ] = &phy->sband_5g.sband; + + return mt76_init_sband(phy, &phy->sband_5g, mt76_channels_5ghz, + ARRAY_SIZE(mt76_channels_5ghz), rates, + n_rates, true, vht); +} + +static int +mt76_init_sband_6g(struct mt76_phy *phy, struct ieee80211_rate *rates, + int n_rates) +{ + phy->hw->wiphy->bands[NL80211_BAND_6GHZ] = &phy->sband_6g.sband; + + return mt76_init_sband(phy, &phy->sband_6g, mt76_channels_6ghz, + ARRAY_SIZE(mt76_channels_6ghz), rates, + n_rates, false, false); +} + +static void +mt76_check_sband(struct mt76_phy *phy, struct mt76_sband *msband, + enum nl80211_band band) +{ + struct ieee80211_supported_band *sband = &msband->sband; + bool found = false; + int i; + + if (!sband) + return; + + for (i = 0; i < sband->n_channels; i++) { + if (sband->channels[i].flags & IEEE80211_CHAN_DISABLED) + continue; + + found = true; + break; + } + + if (found) { + cfg80211_chandef_create(&phy->chandef, &sband->channels[0], + NL80211_CHAN_HT20); + phy->chan_state = &msband->chan[0]; + phy->dev->band_phys[band] = phy; + return; + } + + sband->n_channels = 0; + if (phy->hw->wiphy->bands[band] == sband) + phy->hw->wiphy->bands[band] = NULL; +} + +static int +mt76_phy_init(struct mt76_phy *phy, struct ieee80211_hw *hw) +{ + struct mt76_dev *dev = phy->dev; + struct wiphy *wiphy = hw->wiphy; + + INIT_LIST_HEAD(&phy->tx_list); + spin_lock_init(&phy->tx_lock); + INIT_DELAYED_WORK(&phy->roc_work, mt76_roc_complete_work); + + if ((void *)phy != hw->priv) + return 0; + + SET_IEEE80211_DEV(hw, dev->dev); + SET_IEEE80211_PERM_ADDR(hw, phy->macaddr); + + wiphy->features |= NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE; + if (!phy->no_active_monitor) + wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR; + wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH | + WIPHY_FLAG_SUPPORTS_TDLS | + WIPHY_FLAG_AP_UAPSD; + + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_AIRTIME_FAIRNESS); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_AQL); + + if (!wiphy->max_remain_on_channel_duration) + wiphy->max_remain_on_channel_duration = 5000; + if (!wiphy->available_antennas_tx) + wiphy->available_antennas_tx = phy->antenna_mask; + if (!wiphy->available_antennas_rx) + wiphy->available_antennas_rx = phy->antenna_mask; + + wiphy->sar_capa = &mt76_sar_capa; + phy->frp = devm_kcalloc(dev->dev, wiphy->sar_capa->num_freq_ranges, + sizeof(struct mt76_freq_range_power), + GFP_KERNEL); + if (!phy->frp) + return -ENOMEM; + + hw->txq_data_size = sizeof(struct mt76_txq); + hw->uapsd_max_sp_len = IEEE80211_WMM_IE_STA_QOSINFO_SP_ALL; + + if (!hw->max_tx_fragments) + hw->max_tx_fragments = 16; + + ieee80211_hw_set(hw, SIGNAL_DBM); + ieee80211_hw_set(hw, AMPDU_AGGREGATION); + ieee80211_hw_set(hw, SUPPORTS_RC_TABLE); + ieee80211_hw_set(hw, SUPPORT_FAST_XMIT); + ieee80211_hw_set(hw, SUPPORTS_CLONED_SKBS); + ieee80211_hw_set(hw, SUPPORTS_AMSDU_IN_AMPDU); + ieee80211_hw_set(hw, SUPPORTS_REORDERING_BUFFER); + ieee80211_hw_set(hw, SPECTRUM_MGMT); + + if (!(dev->drv->drv_flags & MT_DRV_AMSDU_OFFLOAD) && + hw->max_tx_fragments > 1) { + ieee80211_hw_set(hw, TX_AMSDU); + ieee80211_hw_set(hw, TX_FRAG_LIST); + } + + ieee80211_hw_set(hw, MFP_CAPABLE); + ieee80211_hw_set(hw, AP_LINK_PS); + ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS); + + return 0; +} + +struct mt76_phy * +mt76_alloc_radio_phy(struct mt76_dev *dev, unsigned int size, + u8 band_idx) +{ + struct ieee80211_hw *hw = dev->phy.hw; + unsigned int phy_size; + struct mt76_phy *phy; + + phy_size = ALIGN(sizeof(*phy), 8); + phy = devm_kzalloc(dev->dev, size + phy_size, GFP_KERNEL); + if (!phy) + return NULL; + + phy->dev = dev; + phy->hw = hw; + phy->priv = (void *)phy + phy_size; + phy->band_idx = band_idx; + + return phy; +} +EXPORT_SYMBOL_GPL(mt76_alloc_radio_phy); + +struct mt76_phy * +mt76_alloc_phy(struct mt76_dev *dev, unsigned int size, + const struct ieee80211_ops *ops, u8 band_idx) +{ + struct ieee80211_hw *hw; + unsigned int phy_size; + struct mt76_phy *phy; + + phy_size = ALIGN(sizeof(*phy), 8); + hw = ieee80211_alloc_hw(size + phy_size, ops); + if (!hw) + return NULL; + + phy = hw->priv; + phy->dev = dev; + phy->hw = hw; + phy->priv = hw->priv + phy_size; + phy->band_idx = band_idx; + + hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; + hw->wiphy->interface_modes = + BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_AP) | +#ifdef CONFIG_MAC80211_MESH + BIT(NL80211_IFTYPE_MESH_POINT) | +#endif + BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_P2P_GO) | + BIT(NL80211_IFTYPE_ADHOC); + + return phy; +} +EXPORT_SYMBOL_GPL(mt76_alloc_phy); + +int mt76_register_phy(struct mt76_phy *phy, bool vht, + struct ieee80211_rate *rates, int n_rates) +{ + int ret; + + ret = mt76_phy_init(phy, phy->hw); + if (ret) + return ret; + + if (phy->cap.has_2ghz) { + ret = mt76_init_sband_2g(phy, rates, n_rates); + if (ret) + return ret; + } + + if (phy->cap.has_5ghz) { + ret = mt76_init_sband_5g(phy, rates + 4, n_rates - 4, vht); + if (ret) + return ret; + } + + if (phy->cap.has_6ghz) { + ret = mt76_init_sband_6g(phy, rates + 4, n_rates - 4); + if (ret) + return ret; + } + + if (IS_ENABLED(CONFIG_MT76_LEDS)) { + ret = mt76_led_init(phy); + if (ret) + return ret; + } + + wiphy_read_of_freq_limits(phy->hw->wiphy); + mt76_check_sband(phy, &phy->sband_2g, NL80211_BAND_2GHZ); + mt76_check_sband(phy, &phy->sband_5g, NL80211_BAND_5GHZ); + mt76_check_sband(phy, &phy->sband_6g, NL80211_BAND_6GHZ); + + if ((void *)phy == phy->hw->priv) { + ret = ieee80211_register_hw(phy->hw); + if (ret) + return ret; + } + + set_bit(MT76_STATE_REGISTERED, &phy->state); + phy->dev->phys[phy->band_idx] = phy; + + return 0; +} +EXPORT_SYMBOL_GPL(mt76_register_phy); + +void mt76_unregister_phy(struct mt76_phy *phy) +{ + struct mt76_dev *dev = phy->dev; + + if (!test_bit(MT76_STATE_REGISTERED, &phy->state)) + return; + + if (IS_ENABLED(CONFIG_MT76_LEDS)) + mt76_led_cleanup(phy); + mt76_tx_status_check(dev, true); + ieee80211_unregister_hw(phy->hw); + dev->phys[phy->band_idx] = NULL; +} +EXPORT_SYMBOL_GPL(mt76_unregister_phy); + +int mt76_create_page_pool(struct mt76_dev *dev, struct mt76_queue *q) +{ + bool is_qrx = mt76_queue_is_rx(dev, q); + struct page_pool_params pp_params = { + .order = 0, + .flags = 0, + .nid = NUMA_NO_NODE, + .dev = dev->dma_dev, + }; + int idx = is_qrx ? q - dev->q_rx : -1; + + /* Allocate page_pools just for rx/wed_tx_free queues */ + if (!is_qrx && !mt76_queue_is_wed_tx_free(q)) + return 0; + + switch (idx) { + case MT_RXQ_MAIN: + case MT_RXQ_BAND1: + case MT_RXQ_BAND2: + case MT_RXQ_NPU0: + case MT_RXQ_NPU1: + pp_params.pool_size = 256; + break; + default: + pp_params.pool_size = 16; + break; + } + + if (mt76_is_mmio(dev)) { + /* rely on page_pool for DMA mapping */ + pp_params.flags |= PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV; + pp_params.dma_dir = DMA_FROM_DEVICE; + pp_params.max_len = PAGE_SIZE; + pp_params.offset = 0; + /* NAPI is available just for rx queues */ + if (idx >= 0 && idx < ARRAY_SIZE(dev->napi)) + pp_params.napi = &dev->napi[idx]; + } + + q->page_pool = page_pool_create(&pp_params); + if (IS_ERR(q->page_pool)) { + int err = PTR_ERR(q->page_pool); + + q->page_pool = NULL; + return err; + } + + return 0; +} +EXPORT_SYMBOL_GPL(mt76_create_page_pool); + +struct mt76_dev * +mt76_alloc_device(struct device *pdev, unsigned int size, + const struct ieee80211_ops *ops, + const struct mt76_driver_ops *drv_ops) +{ + struct ieee80211_hw *hw; + struct mt76_phy *phy; + struct mt76_dev *dev; + int i; + + hw = ieee80211_alloc_hw(size, ops); + if (!hw) + return NULL; + + dev = hw->priv; + dev->hw = hw; + dev->dev = pdev; + dev->init_wiphy = NULL; + dev->drv = drv_ops; + dev->dma_dev = pdev; + + phy = &dev->phy; + phy->dev = dev; + phy->hw = hw; + phy->band_idx = MT_BAND0; + dev->phys[phy->band_idx] = phy; + + spin_lock_init(&dev->rx_lock); + spin_lock_init(&dev->lock); + spin_lock_init(&dev->cc_lock); + spin_lock_init(&dev->status_lock); + spin_lock_init(&dev->wed_lock); + mutex_init(&dev->mutex); + init_waitqueue_head(&dev->tx_wait); + + skb_queue_head_init(&dev->mcu.res_q); + init_waitqueue_head(&dev->mcu.wait); + mutex_init(&dev->mcu.mutex); + dev->tx_worker.fn = mt76_tx_worker; + + hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; + hw->wiphy->interface_modes = + BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_AP) | +#ifdef CONFIG_MAC80211_MESH + BIT(NL80211_IFTYPE_MESH_POINT) | +#endif + BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_P2P_GO) | + BIT(NL80211_IFTYPE_ADHOC); + + spin_lock_init(&dev->token_lock); + idr_init(&dev->token); + + spin_lock_init(&dev->rx_token_lock); + idr_init(&dev->rx_token); + + INIT_LIST_HEAD(&dev->wcid_list); + INIT_LIST_HEAD(&dev->sta_poll_list); + spin_lock_init(&dev->sta_poll_lock); + + INIT_LIST_HEAD(&dev->txwi_cache); + INIT_LIST_HEAD(&dev->rxwi_cache); + dev->token_size = dev->drv->token_size; + INIT_DELAYED_WORK(&dev->scan_work, mt76_scan_work); + spin_lock_init(&dev->scan_lock); + + for (i = 0; i < ARRAY_SIZE(dev->q_rx); i++) + skb_queue_head_init(&dev->rx_skb[i]); + + dev->wq = alloc_ordered_workqueue("mt76", 0); + if (!dev->wq) { + ieee80211_free_hw(hw); + return NULL; + } + + return dev; +} +EXPORT_SYMBOL_GPL(mt76_alloc_device); + +int mt76_register_device(struct mt76_dev *dev, bool vht, + struct ieee80211_rate *rates, int n_rates) +{ + struct ieee80211_hw *hw = dev->hw; + struct mt76_phy *phy = &dev->phy; + int ret; + + dev_set_drvdata(dev->dev, dev); + mt76_wcid_init(&dev->global_wcid, phy->band_idx); + ret = mt76_phy_init(phy, hw); + if (ret) + return ret; + + if (phy->cap.has_2ghz) { + ret = mt76_init_sband_2g(phy, rates, n_rates); + if (ret) + return ret; + } + + if (phy->cap.has_5ghz) { + ret = mt76_init_sband_5g(phy, rates + 4, n_rates - 4, vht); + if (ret) + return ret; + } + + if (phy->cap.has_6ghz) { + ret = mt76_init_sband_6g(phy, rates + 4, n_rates - 4); + if (ret) + return ret; + } + + wiphy_read_of_freq_limits(hw->wiphy); + mt76_check_sband(&dev->phy, &phy->sband_2g, NL80211_BAND_2GHZ); + mt76_check_sband(&dev->phy, &phy->sband_5g, NL80211_BAND_5GHZ); + mt76_check_sband(&dev->phy, &phy->sband_6g, NL80211_BAND_6GHZ); + + if (dev->init_wiphy) { + ret = dev->init_wiphy(dev); + if (ret) + return ret; + } + + if (IS_ENABLED(CONFIG_MT76_LEDS)) { + ret = mt76_led_init(phy); + if (ret) + return ret; + } + + ret = ieee80211_register_hw(hw); + if (ret) + return ret; + + WARN_ON(mt76_worker_setup(hw, &dev->tx_worker, NULL, "tx")); + set_bit(MT76_STATE_REGISTERED, &phy->state); + sched_set_fifo_low(dev->tx_worker.task); + + return 0; +} +EXPORT_SYMBOL_GPL(mt76_register_device); + +void mt76_unregister_device(struct mt76_dev *dev) +{ + struct ieee80211_hw *hw = dev->hw; + + if (!test_bit(MT76_STATE_REGISTERED, &dev->phy.state)) + return; + + if (IS_ENABLED(CONFIG_MT76_LEDS)) + mt76_led_cleanup(&dev->phy); + mt76_tx_status_check(dev, true); + mt76_wcid_cleanup(dev, &dev->global_wcid); + ieee80211_unregister_hw(hw); +} +EXPORT_SYMBOL_GPL(mt76_unregister_device); + +void mt76_free_device(struct mt76_dev *dev) +{ + mt76_worker_teardown(&dev->tx_worker); + if (dev->wq) { + destroy_workqueue(dev->wq); + dev->wq = NULL; + } + mt76_npu_deinit(dev); + ieee80211_free_hw(dev->hw); +} +EXPORT_SYMBOL_GPL(mt76_free_device); + +static void mt76_reset_phy(struct mt76_phy *phy) +{ + if (!phy) + return; + + INIT_LIST_HEAD(&phy->tx_list); + phy->num_sta = 0; + phy->chanctx = NULL; + mt76_roc_complete(phy); +} + +void mt76_reset_device(struct mt76_dev *dev) +{ + int i; + + rcu_read_lock(); + for (i = 0; i < ARRAY_SIZE(dev->wcid); i++) { + struct mt76_wcid *wcid; + + wcid = rcu_dereference(dev->wcid[i]); + if (!wcid) + continue; + + wcid->sta = 0; + mt76_wcid_cleanup(dev, wcid); + rcu_assign_pointer(dev->wcid[i], NULL); + } + rcu_read_unlock(); + + INIT_LIST_HEAD(&dev->wcid_list); + INIT_LIST_HEAD(&dev->sta_poll_list); + dev->vif_mask = 0; + memset(dev->wcid_mask, 0, sizeof(dev->wcid_mask)); + + mt76_reset_phy(&dev->phy); + for (i = 0; i < ARRAY_SIZE(dev->phys); i++) + mt76_reset_phy(dev->phys[i]); +} +EXPORT_SYMBOL_GPL(mt76_reset_device); + +struct mt76_phy *mt76_vif_phy(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct mt76_vif_link *mlink = (struct mt76_vif_link *)vif->drv_priv; + struct mt76_chanctx *ctx; + + if (!hw->wiphy->n_radio) + return hw->priv; + + if (!mlink->ctx) + return NULL; + + ctx = (struct mt76_chanctx *)mlink->ctx->drv_priv; + return ctx->phy; +} +EXPORT_SYMBOL_GPL(mt76_vif_phy); + +static void mt76_rx_release_amsdu(struct mt76_phy *phy, enum mt76_rxq_id q) +{ + struct sk_buff *skb = phy->rx_amsdu[q].head; + struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb; + struct mt76_dev *dev = phy->dev; + struct mt76_queue *rxq = &dev->q_rx[q]; + + phy->rx_amsdu[q].head = NULL; + phy->rx_amsdu[q].tail = NULL; + + /* + * Validate if the amsdu has a proper first subframe. + * A single MSDU can be parsed as A-MSDU when the unauthenticated A-MSDU + * flag of the QoS header gets flipped. In such cases, the first + * subframe has a LLC/SNAP header in the location of the destination + * address. + */ + if (skb_shinfo(skb)->frag_list) { + int offset = 0; + + if (!(status->flag & RX_FLAG_8023)) { + offset = ieee80211_get_hdrlen_from_skb(skb); + + if ((status->flag & + (RX_FLAG_DECRYPTED | RX_FLAG_IV_STRIPPED)) == + RX_FLAG_DECRYPTED) + offset += 8; + } + + if (ether_addr_equal(skb->data + offset, rfc1042_header)) { + dev_kfree_skb(skb); + return; + } + } + + /* RRO 3.0 data queue skbs are processed and completed in the context + * of the indicator queue NAPI, which only polls its own skb list + */ + if (mt76_queue_is_wed_rro_data(rxq) && dev->hwrro_mode == MT76_HWRRO_V3) + q = MT_RXQ_RRO_IND; + + __skb_queue_tail(&dev->rx_skb[q], skb); +} + +static void mt76_rx_release_burst(struct mt76_phy *phy, enum mt76_rxq_id q, + struct sk_buff *skb) +{ + struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb; + + if (phy->rx_amsdu[q].head && + (!status->amsdu || status->first_amsdu || + status->seqno != phy->rx_amsdu[q].seqno)) + mt76_rx_release_amsdu(phy, q); + + if (!phy->rx_amsdu[q].head) { + phy->rx_amsdu[q].tail = &skb_shinfo(skb)->frag_list; + phy->rx_amsdu[q].seqno = status->seqno; + phy->rx_amsdu[q].head = skb; + } else { + *phy->rx_amsdu[q].tail = skb; + phy->rx_amsdu[q].tail = &skb->next; + } + + if (!status->amsdu || status->last_amsdu) + mt76_rx_release_amsdu(phy, q); +} + +void mt76_rx(struct mt76_dev *dev, enum mt76_rxq_id q, struct sk_buff *skb) +{ + struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb; + struct mt76_phy *phy = mt76_dev_phy(dev, status->phy_idx); + + if (!test_bit(MT76_STATE_RUNNING, &phy->state)) { + dev_kfree_skb(skb); + return; + } + +#ifdef CONFIG_NL80211_TESTMODE + if (phy->test.state == MT76_TM_STATE_RX_FRAMES) { + phy->test.rx_stats.packets[q]++; + if (status->flag & RX_FLAG_FAILED_FCS_CRC) + phy->test.rx_stats.fcs_error[q]++; + } +#endif + + mt76_rx_release_burst(phy, q, skb); +} +EXPORT_SYMBOL_GPL(mt76_rx); + +bool mt76_has_tx_pending(struct mt76_phy *phy) +{ + struct mt76_queue *q; + int i; + + for (i = 0; i < __MT_TXQ_MAX; i++) { + q = phy->q_tx[i]; + if (q && q->queued) + return true; + } + + if (atomic_read(&phy->mgmt_tx_pending)) + return true; + + return false; +} +EXPORT_SYMBOL_GPL(mt76_has_tx_pending); + +static struct mt76_channel_state * +mt76_channel_state(struct mt76_phy *phy, struct ieee80211_channel *c) +{ + struct mt76_sband *msband; + int idx; + + if (c->band == NL80211_BAND_2GHZ) + msband = &phy->sband_2g; + else if (c->band == NL80211_BAND_6GHZ) + msband = &phy->sband_6g; + else + msband = &phy->sband_5g; + + idx = c - &msband->sband.channels[0]; + return &msband->chan[idx]; +} + +void mt76_update_survey_active_time(struct mt76_phy *phy, ktime_t time) +{ + struct mt76_channel_state *state = phy->chan_state; + + state->cc_active += ktime_to_us(ktime_sub(time, + phy->survey_time)); + phy->survey_time = time; +} +EXPORT_SYMBOL_GPL(mt76_update_survey_active_time); + +void mt76_update_survey(struct mt76_phy *phy) +{ + struct mt76_dev *dev = phy->dev; + ktime_t cur_time; + + if (dev->drv->update_survey) + dev->drv->update_survey(phy); + + cur_time = ktime_get_boottime(); + mt76_update_survey_active_time(phy, cur_time); + + if (dev->drv->drv_flags & MT_DRV_SW_RX_AIRTIME) { + struct mt76_channel_state *state = phy->chan_state; + + spin_lock_bh(&dev->cc_lock); + state->cc_bss_rx += dev->cur_cc_bss_rx; + dev->cur_cc_bss_rx = 0; + spin_unlock_bh(&dev->cc_lock); + } +} +EXPORT_SYMBOL_GPL(mt76_update_survey); + +int __mt76_set_channel(struct mt76_phy *phy, struct cfg80211_chan_def *chandef, + bool offchannel) +{ + struct mt76_dev *dev = phy->dev; + int timeout = HZ / 5; + int ret; + + mt76_worker_disable(&dev->tx_worker); + mt76_txq_schedule_pending(phy); + + set_bit(MT76_RESET, &phy->state); + wait_event_timeout(dev->tx_wait, !mt76_has_tx_pending(phy), timeout); + mt76_update_survey(phy); + + if (phy->chandef.chan->center_freq != chandef->chan->center_freq || + phy->chandef.width != chandef->width) + phy->dfs_state = MT_DFS_STATE_UNKNOWN; + + phy->chandef = *chandef; + phy->chan_state = mt76_channel_state(phy, chandef->chan); + phy->offchannel = offchannel; + + if (!offchannel) + phy->main_chandef = *chandef; + + if (chandef->chan != phy->main_chandef.chan) + memset(phy->chan_state, 0, sizeof(*phy->chan_state)); + + ret = dev->drv->set_channel(phy); + + clear_bit(MT76_RESET, &phy->state); + mt76_worker_enable(&dev->tx_worker); + mt76_worker_schedule(&dev->tx_worker); + + return ret; +} + +int mt76_set_channel(struct mt76_phy *phy, struct cfg80211_chan_def *chandef, + bool offchannel) +{ + struct mt76_dev *dev = phy->dev; + int ret; + + cancel_delayed_work_sync(&phy->mac_work); + + mutex_lock(&dev->mutex); + ret = __mt76_set_channel(phy, chandef, offchannel); + mutex_unlock(&dev->mutex); + + return ret; +} + +int mt76_update_channel(struct mt76_phy *phy) +{ + struct ieee80211_hw *hw = phy->hw; + struct cfg80211_chan_def *chandef = &hw->conf.chandef; + bool offchannel = hw->conf.flags & IEEE80211_CONF_OFFCHANNEL; + + phy->radar_enabled = hw->conf.radar_enabled; + + return mt76_set_channel(phy, chandef, offchannel); +} +EXPORT_SYMBOL_GPL(mt76_update_channel); + +static struct mt76_sband * +mt76_get_survey_sband(struct mt76_phy *phy, int *idx) +{ + if (*idx < phy->sband_2g.sband.n_channels) + return &phy->sband_2g; + + *idx -= phy->sband_2g.sband.n_channels; + if (*idx < phy->sband_5g.sband.n_channels) + return &phy->sband_5g; + + *idx -= phy->sband_5g.sband.n_channels; + if (*idx < phy->sband_6g.sband.n_channels) + return &phy->sband_6g; + + *idx -= phy->sband_6g.sband.n_channels; + return NULL; +} + +int mt76_get_survey(struct ieee80211_hw *hw, int idx, + struct survey_info *survey) +{ + struct mt76_phy *phy = hw->priv; + struct mt76_dev *dev = phy->dev; + struct mt76_sband *sband = NULL; + struct ieee80211_channel *chan; + struct mt76_channel_state *state; + int phy_idx = 0; + int ret = 0; + + mutex_lock(&dev->mutex); + + for (phy_idx = 0; phy_idx < ARRAY_SIZE(dev->phys); phy_idx++) { + sband = NULL; + phy = dev->phys[phy_idx]; + if (!phy || phy->hw != hw) + continue; + + sband = mt76_get_survey_sband(phy, &idx); + + if (idx == 0 && phy->dev->drv->update_survey) + mt76_update_survey(phy); + + if (sband || !hw->wiphy->n_radio) + break; + } + + if (!sband) { + ret = -ENOENT; + goto out; + } + + chan = &sband->sband.channels[idx]; + state = mt76_channel_state(phy, chan); + + memset(survey, 0, sizeof(*survey)); + survey->channel = chan; + survey->filled = SURVEY_INFO_TIME | SURVEY_INFO_TIME_BUSY; + survey->filled |= dev->drv->survey_flags; + if (state->noise) + survey->filled |= SURVEY_INFO_NOISE_DBM; + + if (chan == phy->main_chandef.chan) { + survey->filled |= SURVEY_INFO_IN_USE; + + if (dev->drv->drv_flags & MT_DRV_SW_RX_AIRTIME) + survey->filled |= SURVEY_INFO_TIME_BSS_RX; + } + + survey->time_busy = div_u64(state->cc_busy, 1000); + survey->time_rx = div_u64(state->cc_rx, 1000); + survey->time = div_u64(state->cc_active, 1000); + survey->noise = state->noise; + + spin_lock_bh(&dev->cc_lock); + survey->time_bss_rx = div_u64(state->cc_bss_rx, 1000); + survey->time_tx = div_u64(state->cc_tx, 1000); + spin_unlock_bh(&dev->cc_lock); + +out: + mutex_unlock(&dev->mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(mt76_get_survey); + +void mt76_wcid_key_setup(struct mt76_dev *dev, struct mt76_wcid *wcid, + struct ieee80211_key_conf *key) +{ + struct ieee80211_key_seq seq; + int i; + + wcid->rx_check_pn = false; + + if (!key) + return; + + if (key->cipher != WLAN_CIPHER_SUITE_CCMP) + return; + + wcid->rx_check_pn = true; + + /* data frame */ + for (i = 0; i < IEEE80211_NUM_TIDS; i++) { + ieee80211_get_key_rx_seq(key, i, &seq); + memcpy(wcid->rx_key_pn[i], seq.ccmp.pn, sizeof(seq.ccmp.pn)); + } + + /* robust management frame */ + ieee80211_get_key_rx_seq(key, -1, &seq); + memcpy(wcid->rx_key_pn[i], seq.ccmp.pn, sizeof(seq.ccmp.pn)); + +} +EXPORT_SYMBOL(mt76_wcid_key_setup); + +int mt76_rx_signal(u8 chain_mask, s8 *chain_signal) +{ + int signal = -128; + u8 chains; + + for (chains = chain_mask; chains; chains >>= 1, chain_signal++) { + int cur, diff; + + cur = *chain_signal; + if (!(chains & BIT(0)) || + cur > 0) + continue; + + if (cur > signal) + swap(cur, signal); + + diff = signal - cur; + if (diff == 0) + signal += 3; + else if (diff <= 2) + signal += 2; + else if (diff <= 6) + signal += 1; + } + + return signal; +} +EXPORT_SYMBOL(mt76_rx_signal); + +static void +mt76_rx_convert(struct mt76_dev *dev, struct sk_buff *skb, + struct ieee80211_hw **hw, + struct ieee80211_sta **sta) +{ + struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); + struct ieee80211_hdr *hdr = mt76_skb_get_hdr(skb); + struct mt76_rx_status mstat; + + mstat = *((struct mt76_rx_status *)skb->cb); + memset(status, 0, sizeof(*status)); + + skb->priority = mstat.qos_ctl & IEEE80211_QOS_CTL_TID_MASK; + + status->flag = mstat.flag; + status->freq = mstat.freq; + status->enc_flags = mstat.enc_flags; + status->encoding = mstat.encoding; + status->bw = mstat.bw; + if (status->encoding == RX_ENC_EHT) { + status->eht.ru = mstat.eht.ru; + status->eht.gi = mstat.eht.gi; + } else { + status->he_ru = mstat.he_ru; + status->he_gi = mstat.he_gi; + status->he_dcm = mstat.he_dcm; + } + status->rate_idx = mstat.rate_idx; + status->nss = mstat.nss; + status->band = mstat.band; + status->signal = mstat.signal; + status->chains = mstat.chains; + status->ampdu_reference = mstat.ampdu_ref; + status->device_timestamp = mstat.timestamp; + status->mactime = mstat.timestamp; + status->signal = mt76_rx_signal(mstat.chains, mstat.chain_signal); + if (status->signal <= -128) + status->flag |= RX_FLAG_NO_SIGNAL_VAL; + + if (ieee80211_is_beacon(hdr->frame_control) || + ieee80211_is_probe_resp(hdr->frame_control)) + status->boottime_ns = ktime_get_boottime_ns(); + + BUILD_BUG_ON(sizeof(mstat) > sizeof(skb->cb)); + BUILD_BUG_ON(sizeof(status->chain_signal) != + sizeof(mstat.chain_signal)); + memcpy(status->chain_signal, mstat.chain_signal, + sizeof(mstat.chain_signal)); + + if (mstat.wcid) { + status->link_valid = mstat.wcid->link_valid; + status->link_id = mstat.wcid->link_id; + } + + *sta = wcid_to_sta(mstat.wcid); + *hw = mt76_phy_hw(dev, mstat.phy_idx); +} + +static void +mt76_check_ccmp_pn(struct sk_buff *skb) +{ + struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb; + struct mt76_wcid *wcid = status->wcid; + struct ieee80211_hdr *hdr; + int security_idx; + int ret; + + if (!(status->flag & RX_FLAG_DECRYPTED)) + return; + + if (status->flag & RX_FLAG_ONLY_MONITOR) + return; + + if (!wcid || !wcid->rx_check_pn) + return; + + security_idx = status->qos_ctl & IEEE80211_QOS_CTL_TID_MASK; + if (status->flag & RX_FLAG_8023) + goto skip_hdr_check; + + hdr = mt76_skb_get_hdr(skb); + if (!(status->flag & RX_FLAG_IV_STRIPPED)) { + /* + * Validate the first fragment both here and in mac80211 + * All further fragments will be validated by mac80211 only. + */ + if (ieee80211_is_frag(hdr) && + !ieee80211_is_first_frag(hdr->seq_ctrl)) + return; + } + + /* IEEE 802.11-2020, 12.5.3.4.4 "PN and replay detection" c): + * + * the recipient shall maintain a single replay counter for received + * individually addressed robust Management frames that are received + * with the To DS subfield equal to 0, [...] + */ + if (ieee80211_is_mgmt(hdr->frame_control) && + !ieee80211_has_tods(hdr->frame_control)) + security_idx = IEEE80211_NUM_TIDS; + +skip_hdr_check: + BUILD_BUG_ON(sizeof(status->iv) != sizeof(wcid->rx_key_pn[0])); + ret = memcmp(status->iv, wcid->rx_key_pn[security_idx], + sizeof(status->iv)); + if (ret <= 0) { + status->flag |= RX_FLAG_ONLY_MONITOR; + return; + } + + memcpy(wcid->rx_key_pn[security_idx], status->iv, sizeof(status->iv)); + + if (status->flag & RX_FLAG_IV_STRIPPED) + status->flag |= RX_FLAG_PN_VALIDATED; +} + +static void +mt76_airtime_report(struct mt76_dev *dev, struct mt76_rx_status *status, + int len) +{ + struct mt76_wcid *wcid = status->wcid; + struct ieee80211_rx_status info = { + .enc_flags = status->enc_flags, + .rate_idx = status->rate_idx, + .encoding = status->encoding, + .band = status->band, + .nss = status->nss, + .bw = status->bw, + }; + struct ieee80211_sta *sta; + u32 airtime; + u8 tidno = status->qos_ctl & IEEE80211_QOS_CTL_TID_MASK; + + airtime = ieee80211_calc_rx_airtime(dev->hw, &info, len); + spin_lock(&dev->cc_lock); + dev->cur_cc_bss_rx += airtime; + spin_unlock(&dev->cc_lock); + + if (!wcid || !wcid->sta) + return; + + sta = container_of((void *)wcid, struct ieee80211_sta, drv_priv); + ieee80211_sta_register_airtime(sta, tidno, 0, airtime); +} + +static void +mt76_airtime_flush_ampdu(struct mt76_dev *dev) +{ + struct mt76_wcid *wcid; + int wcid_idx; + + if (!dev->rx_ampdu_len) + return; + + wcid_idx = dev->rx_ampdu_status.wcid_idx; + if (wcid_idx < ARRAY_SIZE(dev->wcid)) + wcid = rcu_dereference(dev->wcid[wcid_idx]); + else + wcid = NULL; + dev->rx_ampdu_status.wcid = wcid; + + mt76_airtime_report(dev, &dev->rx_ampdu_status, dev->rx_ampdu_len); + + dev->rx_ampdu_len = 0; + dev->rx_ampdu_ref = 0; +} + +static void +mt76_airtime_check(struct mt76_dev *dev, struct sk_buff *skb) +{ + struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb; + struct mt76_wcid *wcid = status->wcid; + + if (!(dev->drv->drv_flags & MT_DRV_SW_RX_AIRTIME)) + return; + + if (!wcid || !wcid->sta) { + struct ieee80211_hdr *hdr = mt76_skb_get_hdr(skb); + + if (status->flag & RX_FLAG_8023) + return; + + if (!ether_addr_equal(hdr->addr1, dev->phy.macaddr)) + return; + + wcid = NULL; + } + + if (!(status->flag & RX_FLAG_AMPDU_DETAILS) || + status->ampdu_ref != dev->rx_ampdu_ref) + mt76_airtime_flush_ampdu(dev); + + if (status->flag & RX_FLAG_AMPDU_DETAILS) { + if (!dev->rx_ampdu_len || + status->ampdu_ref != dev->rx_ampdu_ref) { + dev->rx_ampdu_status = *status; + dev->rx_ampdu_status.wcid_idx = wcid ? wcid->idx : 0xff; + dev->rx_ampdu_ref = status->ampdu_ref; + } + + dev->rx_ampdu_len += skb->len; + return; + } + + mt76_airtime_report(dev, status, skb->len); +} + +static void +mt76_check_sta(struct mt76_dev *dev, struct sk_buff *skb) +{ + struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb; + struct ieee80211_hdr *hdr = mt76_skb_get_hdr(skb); + struct ieee80211_sta *sta; + struct ieee80211_hw *hw; + struct mt76_wcid *wcid = status->wcid; + u8 tidno = status->qos_ctl & IEEE80211_QOS_CTL_TID_MASK; + bool ps; + + hw = mt76_phy_hw(dev, status->phy_idx); + if (ieee80211_is_pspoll(hdr->frame_control) && !wcid && + !(status->flag & RX_FLAG_8023)) { + sta = ieee80211_find_sta_by_ifaddr(hw, hdr->addr2, NULL); + if (sta) + wcid = status->wcid = (struct mt76_wcid *)sta->drv_priv; + } + + mt76_airtime_check(dev, skb); + + if (!wcid || !wcid->sta) + return; + + sta = container_of((void *)wcid, struct ieee80211_sta, drv_priv); + + if (status->signal <= 0) + ewma_signal_add(&wcid->rssi, -status->signal); + + wcid->inactive_count = 0; + + if (status->flag & RX_FLAG_8023) + return; + + if (!test_bit(MT_WCID_FLAG_CHECK_PS, &wcid->flags)) + return; + + if (ieee80211_is_pspoll(hdr->frame_control)) { + ieee80211_sta_pspoll(sta); + return; + } + + if (ieee80211_has_morefrags(hdr->frame_control) || + !(ieee80211_is_mgmt(hdr->frame_control) || + ieee80211_is_data(hdr->frame_control))) + return; + + ps = ieee80211_has_pm(hdr->frame_control); + + if (ps && (ieee80211_is_data_qos(hdr->frame_control) || + ieee80211_is_qos_nullfunc(hdr->frame_control))) + ieee80211_sta_uapsd_trigger(sta, tidno); + + if (!!test_bit(MT_WCID_FLAG_PS, &wcid->flags) == ps) + return; + + if (ps) + set_bit(MT_WCID_FLAG_PS, &wcid->flags); + + if (dev->drv->sta_ps) + dev->drv->sta_ps(dev, sta, ps); + + if (!ps) + clear_bit(MT_WCID_FLAG_PS, &wcid->flags); + + ieee80211_sta_ps_transition(sta, ps); +} + +void mt76_rx_complete(struct mt76_dev *dev, struct sk_buff_head *frames, + struct napi_struct *napi) +{ + struct ieee80211_sta *sta; + struct ieee80211_hw *hw; + struct sk_buff *skb, *tmp; + LIST_HEAD(list); + + spin_lock(&dev->rx_lock); + while ((skb = __skb_dequeue(frames)) != NULL) { + struct sk_buff *nskb = skb_shinfo(skb)->frag_list; + + mt76_check_ccmp_pn(skb); + skb_shinfo(skb)->frag_list = NULL; + mt76_rx_convert(dev, skb, &hw, &sta); + ieee80211_rx_list(hw, sta, skb, &list); + + /* subsequent amsdu frames */ + while (nskb) { + skb = nskb; + nskb = nskb->next; + skb->next = NULL; + + mt76_rx_convert(dev, skb, &hw, &sta); + ieee80211_rx_list(hw, sta, skb, &list); + } + } + spin_unlock(&dev->rx_lock); + + if (!napi) { + netif_receive_skb_list(&list); + return; + } + + list_for_each_entry_safe(skb, tmp, &list, list) { + skb_list_del_init(skb); + napi_gro_receive(napi, skb); + } +} + +void mt76_rx_poll_complete(struct mt76_dev *dev, enum mt76_rxq_id q, + struct napi_struct *napi) +{ + struct sk_buff_head frames; + struct sk_buff *skb; + + __skb_queue_head_init(&frames); + + while ((skb = __skb_dequeue(&dev->rx_skb[q])) != NULL) { + mt76_check_sta(dev, skb); + if (mtk_wed_device_active(&dev->mmio.wed) || + mt76_npu_device_active(dev)) + __skb_queue_tail(&frames, skb); + else + mt76_rx_aggr_reorder(skb, &frames); + } + + mt76_rx_complete(dev, &frames, napi); +} +EXPORT_SYMBOL_GPL(mt76_rx_poll_complete); + +static int +mt76_sta_add(struct mt76_phy *phy, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mt76_wcid *wcid = (struct mt76_wcid *)sta->drv_priv; + struct mt76_dev *dev = phy->dev; + struct mt76_wcid *published; + int ret; + int i; + + mutex_lock(&dev->mutex); + + ret = dev->drv->sta_add(dev, vif, sta); + if (ret) + goto out; + + for (i = 0; i < ARRAY_SIZE(sta->txq); i++) { + struct mt76_txq *mtxq; + + if (!sta->txq[i]) + continue; + + mtxq = (struct mt76_txq *)sta->txq[i]->drv_priv; + mtxq->wcid = wcid->idx; + } + + published = rcu_dereference_protected(dev->wcid[wcid->idx], + lockdep_is_held(&dev->mutex)); + if (published != wcid) { + WARN_ON_ONCE(published); + ewma_signal_init(&wcid->rssi); + rcu_assign_pointer(dev->wcid[wcid->idx], wcid); + mt76_wcid_init(wcid, phy->band_idx); + } else { + wcid->phy_idx = phy->band_idx; + } + + phy->num_sta++; + +out: + mutex_unlock(&dev->mutex); + + return ret; +} + +void __mt76_sta_remove(struct mt76_phy *phy, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mt76_dev *dev = phy->dev; + struct mt76_wcid *wcid = (struct mt76_wcid *)sta->drv_priv; + int i, idx = wcid->idx; + + for (i = 0; i < ARRAY_SIZE(wcid->aggr); i++) + mt76_rx_aggr_stop(dev, wcid, i); + + if (dev->drv->sta_remove) + dev->drv->sta_remove(dev, vif, sta); + + mt76_wcid_cleanup(dev, wcid); + + mt76_wcid_mask_clear(dev->wcid_mask, idx); + phy->num_sta--; +} +EXPORT_SYMBOL_GPL(__mt76_sta_remove); + +static void +mt76_sta_remove(struct mt76_phy *phy, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mt76_dev *dev = phy->dev; + + mutex_lock(&dev->mutex); + __mt76_sta_remove(phy, vif, sta); + mutex_unlock(&dev->mutex); +} + +int mt76_sta_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + enum ieee80211_sta_state old_state, + enum ieee80211_sta_state new_state) +{ + struct mt76_phy *phy = hw->priv; + struct mt76_dev *dev = phy->dev; + enum mt76_sta_event ev; + + phy = mt76_vif_phy(hw, vif); + if (!phy) + return -EINVAL; + + if (old_state == IEEE80211_STA_NOTEXIST && + new_state == IEEE80211_STA_NONE) + return mt76_sta_add(phy, vif, sta); + + if (old_state == IEEE80211_STA_NONE && + new_state == IEEE80211_STA_NOTEXIST) + mt76_sta_remove(phy, vif, sta); + + if (!dev->drv->sta_event) + return 0; + + if (old_state == IEEE80211_STA_AUTH && + new_state == IEEE80211_STA_ASSOC) + ev = MT76_STA_EVENT_ASSOC; + else if (old_state == IEEE80211_STA_ASSOC && + new_state == IEEE80211_STA_AUTHORIZED) + ev = MT76_STA_EVENT_AUTHORIZE; + else if (old_state == IEEE80211_STA_ASSOC && + new_state == IEEE80211_STA_AUTH) + ev = MT76_STA_EVENT_DISASSOC; + else + return 0; + + return dev->drv->sta_event(dev, vif, sta, ev); +} +EXPORT_SYMBOL_GPL(mt76_sta_state); + +void mt76_sta_pre_rcu_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mt76_phy *phy = hw->priv; + struct mt76_dev *dev = phy->dev; + struct mt76_wcid *wcid = (struct mt76_wcid *)sta->drv_priv; + + mutex_lock(&dev->mutex); + spin_lock_bh(&dev->status_lock); + rcu_assign_pointer(dev->wcid[wcid->idx], NULL); + spin_unlock_bh(&dev->status_lock); + mutex_unlock(&dev->mutex); +} +EXPORT_SYMBOL_GPL(mt76_sta_pre_rcu_remove); + +void mt76_wcid_init(struct mt76_wcid *wcid, u8 band_idx) +{ + wcid->hw_key_idx = -1; + wcid->phy_idx = band_idx; + + INIT_LIST_HEAD(&wcid->tx_list); + skb_queue_head_init(&wcid->tx_pending); + skb_queue_head_init(&wcid->tx_offchannel); + + INIT_LIST_HEAD(&wcid->list); + idr_init(&wcid->pktid); + + INIT_LIST_HEAD(&wcid->poll_list); +} +EXPORT_SYMBOL_GPL(mt76_wcid_init); + +void mt76_wcid_cleanup(struct mt76_dev *dev, struct mt76_wcid *wcid) +{ + struct mt76_phy *phy = mt76_dev_phy(dev, wcid->phy_idx); + struct ieee80211_hw *hw; + struct sk_buff_head list; + struct sk_buff *skb; + + mt76_tx_status_lock(dev, &list); + mt76_tx_status_skb_get(dev, wcid, -1, &list); + mt76_tx_status_unlock(dev, &list); + + idr_destroy(&wcid->pktid); + + /* Remove from sta_poll_list to prevent list corruption after reset. + * Without this, mt76_reset_device() reinitializes sta_poll_list but + * leaves wcid->poll_list with stale pointers, causing list corruption + * when mt76_wcid_add_poll() checks list_empty(). + */ + spin_lock_bh(&dev->sta_poll_lock); + if (!list_empty(&wcid->poll_list)) + list_del_init(&wcid->poll_list); + spin_unlock_bh(&dev->sta_poll_lock); + + spin_lock_bh(&phy->tx_lock); + + if (!list_empty(&wcid->tx_list)) + list_del_init(&wcid->tx_list); + + spin_lock(&wcid->tx_pending.lock); + skb_queue_splice_tail_init(&wcid->tx_pending, &list); + spin_unlock(&wcid->tx_pending.lock); + + spin_lock(&wcid->tx_offchannel.lock); + skb_queue_splice_tail_init(&wcid->tx_offchannel, &list); + spin_unlock(&wcid->tx_offchannel.lock); + + spin_unlock_bh(&phy->tx_lock); + + while ((skb = __skb_dequeue(&list)) != NULL) { + hw = mt76_tx_status_get_hw(dev, skb); + ieee80211_free_txskb(hw, skb); + } +} +EXPORT_SYMBOL_GPL(mt76_wcid_cleanup); + +void mt76_wcid_add_poll(struct mt76_dev *dev, struct mt76_wcid *wcid) +{ + if (test_bit(MT76_MCU_RESET, &dev->phy.state) || !wcid->sta) + return; + + spin_lock_bh(&dev->sta_poll_lock); + if (list_empty(&wcid->poll_list)) + list_add_tail(&wcid->poll_list, &dev->sta_poll_list); + spin_unlock_bh(&dev->sta_poll_lock); +} +EXPORT_SYMBOL_GPL(mt76_wcid_add_poll); + +s8 mt76_get_power_bound(struct mt76_phy *phy, s8 txpower) +{ + int n_chains = hweight16(phy->chainmask); + + txpower = mt76_get_sar_power(phy, phy->chandef.chan, txpower * 2); + txpower -= mt76_tx_power_path_delta(n_chains); + + return txpower; +} +EXPORT_SYMBOL_GPL(mt76_get_power_bound); + +int mt76_get_txpower(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + unsigned int link_id, int *dbm) +{ + struct mt76_phy *phy = mt76_vif_phy(hw, vif); + int n_chains, delta; + + if (!phy) + return -EINVAL; + + n_chains = hweight16(phy->chainmask); + delta = mt76_tx_power_path_delta(n_chains); + *dbm = DIV_ROUND_UP(phy->txpower_cur + delta, 2); + + return 0; +} +EXPORT_SYMBOL_GPL(mt76_get_txpower); + +int mt76_init_sar_power(struct ieee80211_hw *hw, + const struct cfg80211_sar_specs *sar) +{ + struct mt76_phy *phy = hw->priv; + const struct cfg80211_sar_capa *capa = hw->wiphy->sar_capa; + int i; + + if (sar->type != NL80211_SAR_TYPE_POWER || !sar->num_sub_specs) + return -EINVAL; + + for (i = 0; i < sar->num_sub_specs; i++) { + u32 index = sar->sub_specs[i].freq_range_index; + /* SAR specifies power limitaton in 0.25dbm */ + s32 power = sar->sub_specs[i].power >> 1; + + if (power > 127 || power < -127) + power = 127; + + phy->frp[index].range = &capa->freq_ranges[index]; + phy->frp[index].power = power; + } + + return 0; +} +EXPORT_SYMBOL_GPL(mt76_init_sar_power); + +int mt76_get_sar_power(struct mt76_phy *phy, + struct ieee80211_channel *chan, + int power) +{ + const struct cfg80211_sar_capa *capa = phy->hw->wiphy->sar_capa; + int freq, i; + + if (!capa || !phy->frp) + return power; + + if (power > 127 || power < -127) + power = 127; + + freq = ieee80211_channel_to_frequency(chan->hw_value, chan->band); + for (i = 0 ; i < capa->num_freq_ranges; i++) { + if (phy->frp[i].range && + freq >= phy->frp[i].range->start_freq && + freq < phy->frp[i].range->end_freq) { + power = min_t(int, phy->frp[i].power, power); + break; + } + } + + return power; +} +EXPORT_SYMBOL_GPL(mt76_get_sar_power); + +static void +__mt76_csa_finish(void *priv, u8 *mac, struct ieee80211_vif *vif) +{ + if (vif->bss_conf.csa_active && ieee80211_beacon_cntdwn_is_complete(vif, 0)) + ieee80211_csa_finish(vif, 0); +} + +void mt76_csa_finish(struct mt76_dev *dev) +{ + if (!dev->csa_complete) + return; + + ieee80211_iterate_active_interfaces_atomic(dev->hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + __mt76_csa_finish, dev); + + dev->csa_complete = 0; +} +EXPORT_SYMBOL_GPL(mt76_csa_finish); + +static void +__mt76_csa_check(void *priv, u8 *mac, struct ieee80211_vif *vif) +{ + struct mt76_dev *dev = priv; + + if (!vif->bss_conf.csa_active) + return; + + dev->csa_complete |= ieee80211_beacon_cntdwn_is_complete(vif, 0); +} + +void mt76_csa_check(struct mt76_dev *dev) +{ + ieee80211_iterate_active_interfaces_atomic(dev->hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + __mt76_csa_check, dev); +} +EXPORT_SYMBOL_GPL(mt76_csa_check); + +int +mt76_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, bool set) +{ + return 0; +} +EXPORT_SYMBOL_GPL(mt76_set_tim); + +void mt76_insert_ccmp_hdr(struct sk_buff *skb, u8 key_id) +{ + struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb; + int hdr_len = ieee80211_get_hdrlen_from_skb(skb); + u8 *hdr, *pn = status->iv; + + __skb_push(skb, 8); + memmove(skb->data, skb->data + 8, hdr_len); + hdr = skb->data + hdr_len; + + hdr[0] = pn[5]; + hdr[1] = pn[4]; + hdr[2] = 0; + hdr[3] = 0x20 | (key_id << 6); + hdr[4] = pn[3]; + hdr[5] = pn[2]; + hdr[6] = pn[1]; + hdr[7] = pn[0]; + + status->flag &= ~RX_FLAG_IV_STRIPPED; +} +EXPORT_SYMBOL_GPL(mt76_insert_ccmp_hdr); + +int mt76_get_rate(struct mt76_dev *dev, + struct ieee80211_supported_band *sband, + int idx, bool cck) +{ + bool is_2g = sband->band == NL80211_BAND_2GHZ; + int i, offset = 0, len = sband->n_bitrates; + + if (cck) { + if (!is_2g) + return 0; + + idx &= ~BIT(2); /* short preamble */ + } else if (is_2g) { + offset = 4; + } + + for (i = offset; i < len; i++) { + if ((sband->bitrates[i].hw_value & GENMASK(7, 0)) == idx) + return i; + } + + return 0; +} +EXPORT_SYMBOL_GPL(mt76_get_rate); + +void mt76_sw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + const u8 *mac) +{ + struct mt76_phy *phy = hw->priv; + + set_bit(MT76_SCANNING, &phy->state); +} +EXPORT_SYMBOL_GPL(mt76_sw_scan); + +void mt76_sw_scan_complete(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + struct mt76_phy *phy = hw->priv; + + clear_bit(MT76_SCANNING, &phy->state); +} +EXPORT_SYMBOL_GPL(mt76_sw_scan_complete); + +int mt76_get_antenna(struct ieee80211_hw *hw, int radio_idx, u32 *tx_ant, + u32 *rx_ant) +{ + struct mt76_phy *phy = hw->priv; + struct mt76_dev *dev = phy->dev; + int i; + + mutex_lock(&dev->mutex); + *tx_ant = 0; + for (i = 0; i < ARRAY_SIZE(dev->phys); i++) + if (dev->phys[i] && dev->phys[i]->hw == hw) + *tx_ant |= dev->phys[i]->chainmask; + *rx_ant = *tx_ant; + mutex_unlock(&dev->mutex); + + return 0; +} +EXPORT_SYMBOL_GPL(mt76_get_antenna); + +struct mt76_queue * +mt76_init_queue(struct mt76_dev *dev, int qid, int idx, int n_desc, + int ring_base, void *wed, u32 flags) +{ + struct mt76_queue *hwq; + int err; + + hwq = devm_kzalloc(dev->dev, sizeof(*hwq), GFP_KERNEL); + if (!hwq) + return ERR_PTR(-ENOMEM); + + hwq->flags = flags; + hwq->wed = wed; + + err = dev->queue_ops->alloc(dev, hwq, idx, n_desc, 0, ring_base); + if (err < 0) + return ERR_PTR(err); + + return hwq; +} +EXPORT_SYMBOL_GPL(mt76_init_queue); + +void mt76_ethtool_worker(struct mt76_ethtool_worker_info *wi, + struct mt76_sta_stats *stats, bool eht) +{ + int i, ei = wi->initial_stat_idx; + u64 *data = wi->data; + + wi->sta_count++; + + data[ei++] += stats->tx_mode[MT_PHY_TYPE_CCK]; + data[ei++] += stats->tx_mode[MT_PHY_TYPE_OFDM]; + data[ei++] += stats->tx_mode[MT_PHY_TYPE_HT]; + data[ei++] += stats->tx_mode[MT_PHY_TYPE_HT_GF]; + data[ei++] += stats->tx_mode[MT_PHY_TYPE_VHT]; + data[ei++] += stats->tx_mode[MT_PHY_TYPE_HE_SU]; + data[ei++] += stats->tx_mode[MT_PHY_TYPE_HE_EXT_SU]; + data[ei++] += stats->tx_mode[MT_PHY_TYPE_HE_TB]; + data[ei++] += stats->tx_mode[MT_PHY_TYPE_HE_MU]; + if (eht) { + data[ei++] += stats->tx_mode[MT_PHY_TYPE_EHT_SU]; + data[ei++] += stats->tx_mode[MT_PHY_TYPE_EHT_TRIG]; + data[ei++] += stats->tx_mode[MT_PHY_TYPE_EHT_MU]; + } + + for (i = 0; i < (ARRAY_SIZE(stats->tx_bw) - !eht); i++) + data[ei++] += stats->tx_bw[i]; + + for (i = 0; i < (eht ? 14 : 12); i++) + data[ei++] += stats->tx_mcs[i]; + + for (i = 0; i < 4; i++) + data[ei++] += stats->tx_nss[i]; + + wi->worker_stat_count = ei - wi->initial_stat_idx; +} +EXPORT_SYMBOL_GPL(mt76_ethtool_worker); + +void mt76_ethtool_page_pool_stats(struct mt76_dev *dev, u64 *data, int *index) +{ +#ifdef CONFIG_PAGE_POOL_STATS + struct page_pool_stats stats = {}; + int i; + + mt76_for_each_q_rx(dev, i) + page_pool_get_stats(dev->q_rx[i].page_pool, &stats); + + page_pool_ethtool_stats_get(data, &stats); + *index += page_pool_ethtool_stats_get_count(); +#endif +} +EXPORT_SYMBOL_GPL(mt76_ethtool_page_pool_stats); + +enum mt76_dfs_state mt76_phy_dfs_state(struct mt76_phy *phy) +{ + struct ieee80211_hw *hw = phy->hw; + struct mt76_dev *dev = phy->dev; + + if (dev->region == NL80211_DFS_UNSET || + test_bit(MT76_SCANNING, &phy->state)) + return MT_DFS_STATE_DISABLED; + + if (!phy->radar_enabled) { + if ((hw->conf.flags & IEEE80211_CONF_MONITOR) && + (phy->chandef.chan->flags & IEEE80211_CHAN_RADAR)) + return MT_DFS_STATE_ACTIVE; + + return MT_DFS_STATE_DISABLED; + } + + if (!cfg80211_reg_can_beacon(hw->wiphy, &phy->chandef, NL80211_IFTYPE_AP)) + return MT_DFS_STATE_CAC; + + return MT_DFS_STATE_ACTIVE; +} +EXPORT_SYMBOL_GPL(mt76_phy_dfs_state); + +void mt76_vif_cleanup(struct mt76_dev *dev, struct ieee80211_vif *vif) +{ + struct mt76_vif_link *mlink = (struct mt76_vif_link *)vif->drv_priv; + struct mt76_vif_data *mvif = mlink->mvif; + + rcu_assign_pointer(mvif->link[0], NULL); + mt76_abort_scan(dev); + if (mvif->roc_phy) + mt76_abort_roc(mvif->roc_phy); +} +EXPORT_SYMBOL_GPL(mt76_vif_cleanup); + +u16 mt76_select_links(struct ieee80211_vif *vif, int max_active_links) +{ + unsigned long usable_links = ieee80211_vif_usable_links(vif); + struct { + u8 link_id; + enum nl80211_band band; + } data[IEEE80211_MLD_MAX_NUM_LINKS]; + unsigned int link_id; + int i, n_data = 0; + u16 sel_links = 0; + + if (!ieee80211_vif_is_mld(vif)) + return 0; + + if (vif->active_links == usable_links) + return vif->active_links; + + rcu_read_lock(); + for_each_set_bit(link_id, &usable_links, IEEE80211_MLD_MAX_NUM_LINKS) { + struct ieee80211_bss_conf *link_conf; + + link_conf = rcu_dereference(vif->link_conf[link_id]); + if (WARN_ON_ONCE(!link_conf)) + continue; + + data[n_data].link_id = link_id; + data[n_data].band = link_conf->chanreq.oper.chan->band; + n_data++; + } + rcu_read_unlock(); + + for (i = 0; i < n_data; i++) { + int j; + + if (!(BIT(data[i].link_id) & vif->active_links)) + continue; + + sel_links = BIT(data[i].link_id); + for (j = 0; j < n_data; j++) { + if (data[i].band != data[j].band) { + sel_links |= BIT(data[j].link_id); + if (hweight16(sel_links) == max_active_links) + break; + } + } + break; + } + + return sel_links; +} +EXPORT_SYMBOL_GPL(mt76_select_links); + +struct mt76_offchannel_cb_data { + struct mt76_phy *phy; + bool offchannel; +}; + +static void +mt76_offchannel_send_nullfunc(struct mt76_offchannel_cb_data *data, + struct ieee80211_vif *vif, int link_id) +{ + struct mt76_phy *phy = data->phy; + struct ieee80211_tx_info *info; + struct ieee80211_sta *sta = NULL; + struct ieee80211_hdr *hdr; + struct mt76_wcid *wcid; + struct sk_buff *skb; + + skb = ieee80211_nullfunc_get(phy->hw, vif, link_id, true); + if (!skb) + return; + + hdr = (struct ieee80211_hdr *)skb->data; + if (data->offchannel) + hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM); + + skb->priority = 7; + skb_set_queue_mapping(skb, IEEE80211_AC_VO); + + if (!ieee80211_tx_prepare_skb(phy->hw, vif, skb, + phy->main_chandef.chan->band, + &sta)) + return; + + if (sta) + wcid = (struct mt76_wcid *)sta->drv_priv; + else + wcid = ((struct mt76_vif_link *)vif->drv_priv)->wcid; + + if (link_id >= 0) { + info = IEEE80211_SKB_CB(skb); + info->control.flags &= ~IEEE80211_TX_CTRL_MLO_LINK; + info->control.flags |= + u32_encode_bits(link_id, IEEE80211_TX_CTRL_MLO_LINK); + } + + mt76_tx(phy, sta, wcid, skb); +} + +static void +mt76_offchannel_notify_iter(void *_data, u8 *mac, struct ieee80211_vif *vif) +{ + struct mt76_offchannel_cb_data *data = _data; + struct mt76_vif_link *mlink; + struct mt76_vif_data *mvif; + int link_id; + + if (vif->type != NL80211_IFTYPE_STATION || !vif->cfg.assoc) + return; + + mlink = (struct mt76_vif_link *)vif->drv_priv; + mvif = mlink->mvif; + + if (!ieee80211_vif_is_mld(vif)) { + if (mt76_vif_link_phy(mlink) == data->phy) { + if (!data->offchannel && mlink->beacon_mon_interval) + WRITE_ONCE(mlink->beacon_mon_last, jiffies); + mt76_offchannel_send_nullfunc(data, vif, -1); + } + return; + } + + for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) { + if (link_id == mvif->deflink_id) + mlink = (struct mt76_vif_link *)vif->drv_priv; + else + mlink = rcu_dereference(mvif->link[link_id]); + if (!mlink) + continue; + if (mt76_vif_link_phy(mlink) != data->phy) + continue; + + if (!data->offchannel && mlink->beacon_mon_interval) + WRITE_ONCE(mlink->beacon_mon_last, jiffies); + + mt76_offchannel_send_nullfunc(data, vif, link_id); + } +} + +void mt76_offchannel_notify(struct mt76_phy *phy, bool offchannel) +{ + struct mt76_offchannel_cb_data data = { + .phy = phy, + .offchannel = offchannel, + }; + + if (!phy->num_sta) + return; + + local_bh_disable(); + ieee80211_iterate_active_interfaces_atomic(phy->hw, + IEEE80211_IFACE_ITER_NORMAL, + mt76_offchannel_notify_iter, &data); + local_bh_enable(); +} +EXPORT_SYMBOL_GPL(mt76_offchannel_notify); + +struct mt76_rx_beacon_data { + struct mt76_phy *phy; + const u8 *bssid; +}; + +static void mt76_rx_beacon_iter(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct mt76_rx_beacon_data *data = _data; + struct mt76_vif_link *mlink = (struct mt76_vif_link *)vif->drv_priv; + struct mt76_vif_data *mvif = mlink->mvif; + int link_id; + + if (vif->type != NL80211_IFTYPE_STATION || !vif->cfg.assoc) + return; + + for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) { + struct ieee80211_bss_conf *link_conf; + + if (link_id == mvif->deflink_id) + mlink = (struct mt76_vif_link *)vif->drv_priv; + else + mlink = rcu_dereference(mvif->link[link_id]); + if (!mlink || !mlink->beacon_mon_interval) + continue; + + if (mt76_vif_link_phy(mlink) != data->phy) + continue; + + link_conf = rcu_dereference(vif->link_conf[link_id]); + if (!link_conf) + continue; + + if (!ether_addr_equal(link_conf->bssid, data->bssid) && + (!link_conf->nontransmitted || + !ether_addr_equal(link_conf->transmitter_bssid, + data->bssid))) + continue; + + WRITE_ONCE(mlink->beacon_mon_last, jiffies); + } +} + +void mt76_rx_beacon(struct mt76_phy *phy, struct sk_buff *skb) +{ + struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb; + struct ieee80211_hdr *hdr = mt76_skb_get_hdr(skb); + struct mt76_rx_beacon_data data = { + .phy = phy, + .bssid = hdr->addr3, + }; + + mt76_scan_rx_beacon(phy->dev, phy->chandef.chan); + + if (!phy->num_sta) + return; + + if (status->flag & (RX_FLAG_FAILED_FCS_CRC | RX_FLAG_ONLY_MONITOR)) + return; + + ieee80211_iterate_active_interfaces_atomic(phy->hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + mt76_rx_beacon_iter, &data); +} +EXPORT_SYMBOL_GPL(mt76_rx_beacon); + +static void mt76_beacon_mon_iter(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct mt76_phy *phy = data; + struct mt76_vif_link *mlink = (struct mt76_vif_link *)vif->drv_priv; + struct mt76_vif_data *mvif = mlink->mvif; + int link_id; + + if (vif->type != NL80211_IFTYPE_STATION || !vif->cfg.assoc) + return; + + for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; link_id++) { + if (link_id == mvif->deflink_id) + mlink = (struct mt76_vif_link *)vif->drv_priv; + else + mlink = rcu_dereference(mvif->link[link_id]); + if (!mlink || !mlink->beacon_mon_interval) + continue; + + if (mt76_vif_link_phy(mlink) != phy) + continue; + + if (time_after(jiffies, + READ_ONCE(mlink->beacon_mon_last) + + MT76_BEACON_MON_MAX_MISS * mlink->beacon_mon_interval)) + ieee80211_beacon_loss(vif); + } +} + +void mt76_beacon_mon_check(struct mt76_phy *phy) +{ + if (phy->offchannel) + return; + + ieee80211_iterate_active_interfaces_atomic(phy->hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + mt76_beacon_mon_iter, phy); +} +EXPORT_SYMBOL_GPL(mt76_beacon_mon_check); diff --git a/drivers/net/wireless/mediatek/mt76/mcu.c b/drivers/net/wireless/mediatek/mt76/mcu.c new file mode 100644 index 000000000..7149b2f7a --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mcu.c @@ -0,0 +1,165 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (C) 2019 Lorenzo Bianconi + */ + +#include "mt76.h" + +struct sk_buff * +__mt76_mcu_msg_alloc(struct mt76_dev *dev, const void *data, + int len, int data_len, gfp_t gfp) +{ + const struct mt76_mcu_ops *ops = dev->mcu_ops; + struct sk_buff *skb; + + len = max_t(int, len, data_len); + len = ops->headroom + len + ops->tailroom; + + skb = alloc_skb(len, gfp); + if (!skb) + return NULL; + + memset(skb->head, 0, len); + skb_reserve(skb, ops->headroom); + + if (data && data_len) + skb_put_data(skb, data, data_len); + + return skb; +} +EXPORT_SYMBOL_GPL(__mt76_mcu_msg_alloc); + +struct sk_buff *mt76_mcu_get_response(struct mt76_dev *dev, + unsigned long expires) +{ + unsigned long timeout; + + if (!time_is_after_jiffies(expires)) + return NULL; + + timeout = expires - jiffies; + wait_event_timeout(dev->mcu.wait, + (!skb_queue_empty(&dev->mcu.res_q) || + test_bit(MT76_MCU_RESET, &dev->phy.state)), + timeout); + return skb_dequeue(&dev->mcu.res_q); +} +EXPORT_SYMBOL_GPL(mt76_mcu_get_response); + +void mt76_mcu_rx_event(struct mt76_dev *dev, struct sk_buff *skb) +{ + skb_queue_tail(&dev->mcu.res_q, skb); + wake_up(&dev->mcu.wait); +} +EXPORT_SYMBOL_GPL(mt76_mcu_rx_event); + +int mt76_mcu_send_and_get_msg(struct mt76_dev *dev, int cmd, const void *data, + int len, bool wait_resp, struct sk_buff **ret_skb) +{ + struct sk_buff *skb; + + if (dev->mcu_ops->mcu_send_msg) + return dev->mcu_ops->mcu_send_msg(dev, cmd, data, len, wait_resp); + + skb = mt76_mcu_msg_alloc(dev, data, len); + if (!skb) + return -ENOMEM; + + return mt76_mcu_skb_send_and_get_msg(dev, skb, cmd, wait_resp, ret_skb); +} +EXPORT_SYMBOL_GPL(mt76_mcu_send_and_get_msg); + +int mt76_mcu_skb_send_and_get_msg(struct mt76_dev *dev, struct sk_buff *skb, + int cmd, bool wait_resp, + struct sk_buff **ret_skb) +{ + unsigned int retry = 0; + struct sk_buff *orig_skb = NULL; + unsigned long expires; + int ret, seq; + + if (ret_skb) + *ret_skb = NULL; + + mutex_lock(&dev->mcu.mutex); + + if ((mt76_is_mmio(dev) && atomic_read(&dev->bus_hung)) || + (mt76_is_sdio(dev) && test_bit(MT76_RESET, &dev->phy.state) && + atomic_read(&dev->bus_hung))) { + orig_skb = skb; + ret = -EIO; + goto out; + } + + if (dev->mcu_ops->mcu_skb_prepare_msg) { + orig_skb = skb; + ret = dev->mcu_ops->mcu_skb_prepare_msg(dev, skb, cmd, &seq); + if (ret < 0) + goto out; + } + +retry: + /* orig skb might be needed for retry, mcu_skb_send_msg consumes it */ + if (orig_skb) + skb_get(orig_skb); + ret = dev->mcu_ops->mcu_skb_send_msg(dev, skb, cmd, wait_resp ? &seq : NULL); + if (ret < 0) + goto out; + + if (!wait_resp) { + ret = 0; + goto out; + } + + expires = jiffies + dev->mcu.timeout; + + do { + skb = mt76_mcu_get_response(dev, expires); + if (!skb && !test_bit(MT76_MCU_RESET, &dev->phy.state) && + orig_skb && retry++ < dev->mcu_ops->max_retry) { + dev_err(dev->dev, "Retry message %08x (seq %d)\n", + cmd, seq); + skb = orig_skb; + goto retry; + } + + ret = dev->mcu_ops->mcu_parse_response(dev, cmd, skb, seq); + if (!ret && ret_skb) + *ret_skb = skb; + else + dev_kfree_skb(skb); + } while (ret == -EAGAIN); + + +out: + dev_kfree_skb(orig_skb); + mutex_unlock(&dev->mcu.mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(mt76_mcu_skb_send_and_get_msg); + +int __mt76_mcu_send_firmware(struct mt76_dev *dev, int cmd, const void *data, + int len, int max_len) +{ + int err, cur_len; + + while (len > 0) { + cur_len = min_t(int, max_len, len); + + err = mt76_mcu_send_msg(dev, cmd, data, cur_len, false); + if (err) + return err; + + data += cur_len; + len -= cur_len; + + if (dev->queue_ops->tx_cleanup) + dev->queue_ops->tx_cleanup(dev, + dev->q_mcu[MT_MCUQ_FWDL], + false); + } + + return 0; +} +EXPORT_SYMBOL_GPL(__mt76_mcu_send_firmware); diff --git a/drivers/net/wireless/mediatek/mt76/mmio.c b/drivers/net/wireless/mediatek/mt76/mmio.c new file mode 100644 index 000000000..73d47608b --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mmio.c @@ -0,0 +1,128 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (C) 2016 Felix Fietkau + */ + +#include "mt76.h" +#include "dma.h" +#include "trace.h" + +static u32 mt76_mmio_rr(struct mt76_dev *dev, u32 offset) +{ + u32 val; + + val = readl(dev->mmio.regs + offset); + trace_reg_rr(dev, offset, val); + + return val; +} + +static void mt76_mmio_wr(struct mt76_dev *dev, u32 offset, u32 val) +{ + trace_reg_wr(dev, offset, val); + writel(val, dev->mmio.regs + offset); +} + +static u32 mt76_mmio_rmw(struct mt76_dev *dev, u32 offset, u32 mask, u32 val) +{ + val |= mt76_mmio_rr(dev, offset) & ~mask; + mt76_mmio_wr(dev, offset, val); + return val; +} + +static void mt76_mmio_write_copy(struct mt76_dev *dev, u32 offset, + const void *data, int len) +{ + int i; + + for (i = 0; i + 4 <= len; i += 4) + writel(get_unaligned_le32(data + i), + dev->mmio.regs + offset + i); + + if (i < len) { + u8 tmp[4] = {}; + + memcpy(tmp, data + i, len - i); + writel(get_unaligned_le32(tmp), dev->mmio.regs + offset + i); + } +} + +static void mt76_mmio_read_copy(struct mt76_dev *dev, u32 offset, + void *data, int len) +{ + int i; + + for (i = 0; i + 4 <= len; i += 4) + put_unaligned_le32(readl(dev->mmio.regs + offset + i), + data + i); + + if (i < len) { + u8 tmp[4]; + + put_unaligned_le32(readl(dev->mmio.regs + offset + i), tmp); + memcpy(data + i, tmp, len - i); + } +} + +static int mt76_mmio_wr_rp(struct mt76_dev *dev, u32 base, + const struct mt76_reg_pair *data, int len) +{ + while (len > 0) { + mt76_mmio_wr(dev, data->reg, data->value); + data++; + len--; + } + + return 0; +} + +static int mt76_mmio_rd_rp(struct mt76_dev *dev, u32 base, + struct mt76_reg_pair *data, int len) +{ + while (len > 0) { + data->value = mt76_mmio_rr(dev, data->reg); + data++; + len--; + } + + return 0; +} + +void mt76_set_irq_mask(struct mt76_dev *dev, u32 addr, + u32 clear, u32 set) +{ + unsigned long flags; + + spin_lock_irqsave(&dev->mmio.irq_lock, flags); + dev->mmio.irqmask &= ~clear; + dev->mmio.irqmask |= set; + if (addr) { + if (mtk_wed_device_active(&dev->mmio.wed)) + mtk_wed_device_irq_set_mask(&dev->mmio.wed, + dev->mmio.irqmask); + else + mt76_mmio_wr(dev, addr, dev->mmio.irqmask); + } + spin_unlock_irqrestore(&dev->mmio.irq_lock, flags); +} +EXPORT_SYMBOL_GPL(mt76_set_irq_mask); + +void mt76_mmio_init(struct mt76_dev *dev, void __iomem *regs) +{ + static const struct mt76_bus_ops mt76_mmio_ops = { + .rr = mt76_mmio_rr, + .rmw = mt76_mmio_rmw, + .wr = mt76_mmio_wr, + .write_copy = mt76_mmio_write_copy, + .read_copy = mt76_mmio_read_copy, + .wr_rp = mt76_mmio_wr_rp, + .rd_rp = mt76_mmio_rd_rp, + .type = MT76_BUS_MMIO, + }; + + dev->bus = &mt76_mmio_ops; + dev->mmio.regs = regs; + + spin_lock_init(&dev->mmio.irq_lock); +} +EXPORT_SYMBOL_GPL(mt76_mmio_init); diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h new file mode 100644 index 000000000..62b41c8bb --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76.h @@ -0,0 +1,2167 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (C) 2016 Felix Fietkau + */ + +#ifndef __MT76_H +#define __MT76_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "util.h" +#include "testmode.h" + +#define MT_MCU_RING_SIZE 32 +#define MT_RX_BUF_SIZE 2048 +#define MT_SKB_HEAD_LEN 256 + +#define MT_MAX_NON_AQL_PKT 16 +#define MT_TXQ_FREE_THR 32 + +#define MT76_TOKEN_FREE_THR 64 + +#define MT_QFLAG_WED_RING GENMASK(1, 0) +#define MT_QFLAG_WED_TYPE GENMASK(4, 2) +#define MT_QFLAG_WED BIT(5) +#define MT_QFLAG_WED_RRO BIT(6) +#define MT_QFLAG_WED_RRO_EN BIT(7) +#define MT_QFLAG_EMI_EN BIT(8) +#define MT_QFLAG_NPU BIT(9) + +#define __MT_WED_Q(_type, _n) (MT_QFLAG_WED | \ + FIELD_PREP(MT_QFLAG_WED_TYPE, _type) | \ + FIELD_PREP(MT_QFLAG_WED_RING, _n)) +#define __MT_WED_RRO_Q(_type, _n) (MT_QFLAG_WED_RRO | __MT_WED_Q(_type, _n)) + +#define MT_WED_Q_TX(_n) __MT_WED_Q(MT76_WED_Q_TX, _n) +#define MT_WED_Q_RX(_n) __MT_WED_Q(MT76_WED_Q_RX, _n) +#define MT_WED_Q_TXFREE __MT_WED_Q(MT76_WED_Q_TXFREE, 0) +#define MT_WED_RRO_Q_DATA(_n) __MT_WED_RRO_Q(MT76_WED_RRO_Q_DATA, _n) +#define MT_WED_RRO_Q_MSDU_PG(_n) __MT_WED_RRO_Q(MT76_WED_RRO_Q_MSDU_PG, _n) +#define MT_WED_RRO_Q_IND __MT_WED_RRO_Q(MT76_WED_RRO_Q_IND, 0) +#define MT_WED_RRO_Q_RXDMAD_C __MT_WED_RRO_Q(MT76_WED_RRO_Q_RXDMAD_C, 0) + +#define __MT_NPU_Q(_type, _n) (MT_QFLAG_NPU | \ + FIELD_PREP(MT_QFLAG_WED_TYPE, _type) | \ + FIELD_PREP(MT_QFLAG_WED_RING, _n)) +#define MT_NPU_Q_TX(_n) __MT_NPU_Q(MT76_WED_Q_TX, _n) +#define MT_NPU_Q_RX(_n) __MT_NPU_Q(MT76_WED_Q_RX, _n) +#define MT_NPU_Q_TXFREE(_n) (FIELD_PREP(MT_QFLAG_WED_TYPE, MT76_WED_Q_TXFREE) | \ + FIELD_PREP(MT_QFLAG_WED_RING, _n)) + +struct mt76_dev; +struct mt76_phy; +struct mt76_wcid; +struct mt76s_intr; +struct mt76_chanctx; +struct mt76_vif_link; + +struct mt76_reg_pair { + u32 reg; + u32 value; +}; + +enum mt76_bus_type { + MT76_BUS_MMIO, + MT76_BUS_USB, + MT76_BUS_SDIO, +}; + +enum mt76_wed_type { + MT76_WED_Q_TX, + MT76_WED_Q_TXFREE, + MT76_WED_Q_RX, + MT76_WED_RRO_Q_DATA, + MT76_WED_RRO_Q_MSDU_PG, + MT76_WED_RRO_Q_IND, + MT76_WED_RRO_Q_RXDMAD_C, +}; + +enum mt76_hwrro_mode { + MT76_HWRRO_OFF, + MT76_HWRRO_V3, + MT76_HWRRO_V3_1, +}; + +struct mt76_bus_ops { + u32 (*rr)(struct mt76_dev *dev, u32 offset); + void (*wr)(struct mt76_dev *dev, u32 offset, u32 val); + u32 (*rmw)(struct mt76_dev *dev, u32 offset, u32 mask, u32 val); + void (*write_copy)(struct mt76_dev *dev, u32 offset, const void *data, + int len); + void (*read_copy)(struct mt76_dev *dev, u32 offset, void *data, + int len); + int (*wr_rp)(struct mt76_dev *dev, u32 base, + const struct mt76_reg_pair *rp, int len); + int (*rd_rp)(struct mt76_dev *dev, u32 base, + struct mt76_reg_pair *rp, int len); + enum mt76_bus_type type; +}; + +#define mt76_is_usb(dev) ((dev)->bus->type == MT76_BUS_USB) +#define mt76_is_mmio(dev) ((dev)->bus->type == MT76_BUS_MMIO) +#define mt76_is_sdio(dev) ((dev)->bus->type == MT76_BUS_SDIO) + +enum mt76_txq_id { + MT_TXQ_VO = IEEE80211_AC_VO, + MT_TXQ_VI = IEEE80211_AC_VI, + MT_TXQ_BE = IEEE80211_AC_BE, + MT_TXQ_BK = IEEE80211_AC_BK, + MT_TXQ_PSD, + MT_TXQ_BEACON, + MT_TXQ_CAB, + __MT_TXQ_MAX +}; + +enum mt76_mcuq_id { + MT_MCUQ_WM, + MT_MCUQ_WA, + MT_MCUQ_FWDL, + __MT_MCUQ_MAX +}; + +enum mt76_rxq_id { + MT_RXQ_MAIN, + MT_RXQ_MCU, + MT_RXQ_MCU_WA, + MT_RXQ_BAND1, + MT_RXQ_BAND1_WA, + MT_RXQ_MAIN_WA, + MT_RXQ_BAND2, + MT_RXQ_BAND2_WA, + MT_RXQ_RRO_BAND0, + MT_RXQ_RRO_BAND1, + MT_RXQ_RRO_BAND2, + MT_RXQ_MSDU_PAGE_BAND0, + MT_RXQ_MSDU_PAGE_BAND1, + MT_RXQ_MSDU_PAGE_BAND2, + MT_RXQ_TXFREE_BAND0, + MT_RXQ_TXFREE_BAND1, + MT_RXQ_TXFREE_BAND2, + MT_RXQ_RRO_IND, + MT_RXQ_RRO_RXDMAD_C, + MT_RXQ_NPU0, + MT_RXQ_NPU1, + __MT_RXQ_MAX +}; + +enum mt76_band_id { + MT_BAND0, + MT_BAND1, + MT_BAND2, + __MT_MAX_BAND +}; + +enum mt76_cipher_type { + MT_CIPHER_NONE, + MT_CIPHER_WEP40, + MT_CIPHER_TKIP, + MT_CIPHER_TKIP_NO_MIC, + MT_CIPHER_AES_CCMP, + MT_CIPHER_WEP104, + MT_CIPHER_BIP_CMAC_128, + MT_CIPHER_WEP128, + MT_CIPHER_WAPI, + MT_CIPHER_CCMP_CCX, + MT_CIPHER_CCMP_256, + MT_CIPHER_GCMP, + MT_CIPHER_GCMP_256, +}; + +enum mt76_dfs_state { + MT_DFS_STATE_UNKNOWN, + MT_DFS_STATE_DISABLED, + MT_DFS_STATE_CAC, + MT_DFS_STATE_ACTIVE, +}; + +#define MT76_RNR_SCAN_MAX_BSSIDS 16 +struct mt76_scan_rnr_param { + u8 bssid[MT76_RNR_SCAN_MAX_BSSIDS][ETH_ALEN]; + u8 channel[MT76_RNR_SCAN_MAX_BSSIDS]; + u8 random_mac[ETH_ALEN]; + u8 seq_num; + u8 bssid_num; + u32 sreq_flag; +}; + +struct mt76_queue_buf { + dma_addr_t addr; + u16 len:15, + skip_unmap:1; +}; + +struct mt76_tx_info { + struct mt76_queue_buf buf[32]; + struct sk_buff *skb; + int nbuf; + u32 info; +}; + +struct mt76_queue_entry { + union { + void *buf; + struct sk_buff *skb; + }; + union { + struct mt76_txwi_cache *txwi; + struct urb *urb; + int buf_sz; + }; + dma_addr_t dma_addr[2]; + u16 dma_len[2]; + u16 wcid; + bool skip_buf0:1; + bool skip_buf1:1; + bool done:1; +}; + +struct mt76_queue_regs { + u32 desc_base; + u32 ring_size; + u32 cpu_idx; + u32 dma_idx; +} __packed __aligned(4); + +struct mt76_queue { + struct mt76_queue_regs __iomem *regs; + + spinlock_t lock; + spinlock_t cleanup_lock; + struct mt76_queue_entry *entry; + struct mt76_rro_desc *rro_desc; + struct mt76_desc *desc; + + u16 first; + u16 head; + u16 tail; + u8 hw_idx; + u8 ep; + int ndesc; + int queued; + int buf_size; + bool stopped; + bool blocked; + + u8 buf_offset; + u16 flags; + u8 magic_cnt; + + __le16 *emi_cpu_idx; + + struct mtk_wed_device *wed; + struct mt76_dev *dev; + u32 wed_regs; + + dma_addr_t desc_dma; + struct sk_buff *rx_head; + struct page_pool *page_pool; +}; + +struct mt76_mcu_ops { + unsigned int max_retry; + u32 headroom; + u32 tailroom; + + int (*mcu_send_msg)(struct mt76_dev *dev, int cmd, const void *data, + int len, bool wait_resp); + int (*mcu_skb_prepare_msg)(struct mt76_dev *dev, struct sk_buff *skb, + int cmd, int *seq); + int (*mcu_skb_send_msg)(struct mt76_dev *dev, struct sk_buff *skb, + int cmd, int *seq); + int (*mcu_parse_response)(struct mt76_dev *dev, int cmd, + struct sk_buff *skb, int seq); + u32 (*mcu_rr)(struct mt76_dev *dev, u32 offset); + void (*mcu_wr)(struct mt76_dev *dev, u32 offset, u32 val); + int (*mcu_wr_rp)(struct mt76_dev *dev, u32 base, + const struct mt76_reg_pair *rp, int len); + int (*mcu_rd_rp)(struct mt76_dev *dev, u32 base, + struct mt76_reg_pair *rp, int len); + int (*mcu_restart)(struct mt76_dev *dev); +}; + +struct mt76_queue_ops { + int (*init)(struct mt76_dev *dev, + int (*poll)(struct napi_struct *napi, int budget)); + + int (*alloc)(struct mt76_dev *dev, struct mt76_queue *q, + int idx, int n_desc, int bufsize, + u32 ring_base); + + int (*tx_queue_skb)(struct mt76_phy *phy, struct mt76_queue *q, + enum mt76_txq_id qid, struct sk_buff *skb, + struct mt76_wcid *wcid, struct ieee80211_sta *sta); + + int (*tx_queue_skb_raw)(struct mt76_dev *dev, struct mt76_queue *q, + struct sk_buff *skb, u32 tx_info); + + void *(*dequeue)(struct mt76_dev *dev, struct mt76_queue *q, bool flush, + int *len, u32 *info, bool *more); + + void (*rx_reset)(struct mt76_dev *dev, enum mt76_rxq_id qid); + + void (*tx_cleanup)(struct mt76_dev *dev, struct mt76_queue *q, + bool flush); + + void (*rx_queue_init)(struct mt76_dev *dev, enum mt76_rxq_id qid, + int (*poll)(struct napi_struct *napi, int budget)); + + void (*rx_cleanup)(struct mt76_dev *dev, struct mt76_queue *q); + + void (*kick)(struct mt76_dev *dev, struct mt76_queue *q); + + void (*reset_q)(struct mt76_dev *dev, struct mt76_queue *q, + bool reset_idx); +}; + +enum mt76_phy_type { + MT_PHY_TYPE_CCK, + MT_PHY_TYPE_OFDM, + MT_PHY_TYPE_HT, + MT_PHY_TYPE_HT_GF, + MT_PHY_TYPE_VHT, + MT_PHY_TYPE_HE_SU = 8, + MT_PHY_TYPE_HE_EXT_SU, + MT_PHY_TYPE_HE_TB, + MT_PHY_TYPE_HE_MU, + MT_PHY_TYPE_EHT_SU = 13, + MT_PHY_TYPE_EHT_TRIG, + MT_PHY_TYPE_EHT_MU, + __MT_PHY_TYPE_MAX, +}; + +struct mt76_sta_stats { + u64 tx_mode[__MT_PHY_TYPE_MAX]; + u64 tx_bw[5]; /* 20, 40, 80, 160, 320 */ + u64 tx_nss[4]; /* 1, 2, 3, 4 */ + u64 tx_mcs[16]; /* mcs idx */ + u64 tx_bytes; + /* WED TX */ + u32 tx_packets; /* unit: MSDU */ + u32 tx_retries; + u32 tx_failed; + /* WED RX */ + u64 rx_bytes; + u32 rx_packets; + u32 rx_errors; + u32 rx_drops; +}; + +enum mt76_wcid_flags { + MT_WCID_FLAG_CHECK_PS, + MT_WCID_FLAG_PS, + MT_WCID_FLAG_4ADDR, + MT_WCID_FLAG_HDR_TRANS, + MT_WCID_FLAG_TDLS_PEER, +}; + +#define MT76_N_WCIDS 1088 +#define MT76_BEACON_MON_MAX_MISS 7 + +/* stored in ieee80211_tx_info::hw_queue */ +#define MT_TX_HW_QUEUE_PHY GENMASK(3, 2) + +DECLARE_EWMA(signal, 10, 8); + +#define MT_WCID_TX_INFO_RATE GENMASK(15, 0) +#define MT_WCID_TX_INFO_NSS GENMASK(17, 16) +#define MT_WCID_TX_INFO_TXPWR_ADJ GENMASK(25, 18) +#define MT_WCID_TX_INFO_SET BIT(31) + +struct mt76_wcid { + struct mt76_rx_tid __rcu *aggr[IEEE80211_NUM_TIDS]; + + atomic_t non_aql_packets; + unsigned long flags; + + struct ewma_signal rssi; + int inactive_count; + + struct rate_info rate; + unsigned long ampdu_state; + + u16 idx; + u8 hw_key_idx; + u8 hw_key_idx2; + + u8 offchannel:1; + u8 sta:1; + u8 sta_disabled:1; + u8 amsdu:1; + u8 phy_idx:2; + u8 link_id:4; + bool link_valid; + + u8 rx_check_pn; + u8 rx_key_pn[IEEE80211_NUM_TIDS + 1][6]; + u16 cipher; + + u32 tx_info; + bool sw_iv; + + struct list_head tx_list; + struct sk_buff_head tx_pending; + struct sk_buff_head tx_offchannel; + + struct list_head list; + struct idr pktid; + + struct mt76_sta_stats stats; + + struct list_head poll_list; + + struct mt76_wcid *def_wcid; +}; + +struct mt76_txq { + u16 wcid; + + u16 agg_ssn; + bool send_bar; + bool aggr; +}; + +/* data0 */ +#define RRO_IND_DATA0_IND_REASON_MASK GENMASK(31, 28) +#define RRO_IND_DATA0_START_SEQ_MASK GENMASK(27, 16) +#define RRO_IND_DATA0_SEQ_ID_MASK GENMASK(11, 0) +/* data1 */ +#define RRO_IND_DATA1_MAGIC_CNT_MASK GENMASK(31, 29) +#define RRO_IND_DATA1_IND_COUNT_MASK GENMASK(12, 0) +struct mt76_wed_rro_ind { + __le32 data0; + __le32 data1; +}; + +struct mt76_txwi_cache { + struct list_head list; + dma_addr_t dma_addr; + + union { + struct sk_buff *skb; + void *ptr; + }; + + u8 qid; + u8 phy_idx; +}; + +struct mt76_rx_tid { + struct rcu_head rcu_head; + + struct mt76_dev *dev; + + spinlock_t lock; + struct delayed_work reorder_work; + + u16 id; + u16 head; + u16 size; + u16 nframes; + + u8 num; + + u8 started:1, stopped:1, timer_pending:1; + + struct sk_buff *reorder_buf[] __counted_by(size); +}; + +#define MT_TX_CB_DMA_DONE BIT(0) +#define MT_TX_CB_TXS_DONE BIT(1) +#define MT_TX_CB_TXS_FAILED BIT(2) + +#define MT_PACKET_ID_MASK GENMASK(6, 0) +#define MT_PACKET_ID_NO_ACK 0 +#define MT_PACKET_ID_NO_SKB 1 +#define MT_PACKET_ID_WED 2 +#define MT_PACKET_ID_FIRST 3 +#define MT_PACKET_ID_HAS_RATE BIT(7) +/* This is timer for when to give up when waiting for TXS callback, + * with starting time being the time at which the DMA_DONE callback + * was seen (so, we know packet was processed then, it should not take + * long after that for firmware to send the TXS callback if it is going + * to do so.) + */ +#define MT_TX_STATUS_SKB_TIMEOUT (HZ / 4) + +struct mt76_tx_cb { + unsigned long jiffies; + u16 wcid; + u8 pktid; + u8 flags; +}; + +enum { + MT76_STATE_INITIALIZED, + MT76_STATE_REGISTERED, + MT76_STATE_RUNNING, + MT76_STATE_MCU_RUNNING, + MT76_SCANNING, + MT76_HW_SCANNING, + MT76_HW_SCHED_SCANNING, + MT76_RESTART, + MT76_RESET, + MT76_MCU_RESET, + MT76_REMOVED, + MT76_READING_STATS, + MT76_STATE_POWER_OFF, + MT76_STATE_SUSPEND, + MT76_STATE_ROC, + MT76_STATE_PM, + MT76_STATE_WED_RESET, +}; + +enum mt76_sta_event { + MT76_STA_EVENT_ASSOC, + MT76_STA_EVENT_AUTHORIZE, + MT76_STA_EVENT_DISASSOC, +}; + +struct mt76_hw_cap { + bool has_2ghz; + bool has_5ghz; + bool has_6ghz; +}; + +#define MT_DRV_TXWI_NO_FREE BIT(0) +#define MT_DRV_TX_ALIGNED4_SKBS BIT(1) +#define MT_DRV_SW_RX_AIRTIME BIT(2) +#define MT_DRV_RX_DMA_HDR BIT(3) +#define MT_DRV_HW_MGMT_TXQ BIT(4) +#define MT_DRV_AMSDU_OFFLOAD BIT(5) +#define MT_DRV_IGNORE_TXS_FAILED BIT(6) +#define MT_DRV_HW_PS_BUFFERING BIT(7) + +struct mt76_driver_ops { + u32 drv_flags; + u32 survey_flags; + u16 txwi_size; + u16 token_size; + + unsigned int link_data_size; + + void (*update_survey)(struct mt76_phy *phy); + int (*set_channel)(struct mt76_phy *phy); + + int (*tx_prepare_skb)(struct mt76_dev *dev, void *txwi_ptr, + enum mt76_txq_id qid, struct mt76_wcid *wcid, + struct ieee80211_sta *sta, + struct mt76_tx_info *tx_info); + + void (*tx_complete_skb)(struct mt76_dev *dev, + struct mt76_queue_entry *e); + + bool (*tx_status_data)(struct mt76_dev *dev, u8 *update); + + bool (*rx_check)(struct mt76_dev *dev, void *data, int len); + + void (*rx_skb)(struct mt76_dev *dev, enum mt76_rxq_id q, + struct sk_buff *skb, u32 *info); + + void (*rx_poll_complete)(struct mt76_dev *dev, enum mt76_rxq_id q); + + void (*rx_rro_ind_process)(struct mt76_dev *dev, void *data); + int (*rx_rro_add_msdu_page)(struct mt76_dev *dev, struct mt76_queue *q, + dma_addr_t p, void *data); + + void (*sta_ps)(struct mt76_dev *dev, struct ieee80211_sta *sta, + bool ps); + + int (*sta_add)(struct mt76_dev *dev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); + + int (*sta_event)(struct mt76_dev *dev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, enum mt76_sta_event ev); + + void (*sta_remove)(struct mt76_dev *dev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); + + int (*vif_link_add)(struct mt76_phy *phy, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, + struct mt76_vif_link *mlink); + + void (*vif_link_remove)(struct mt76_phy *phy, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, + struct mt76_vif_link *mlink); +}; + +struct mt76_channel_state { + u64 cc_active; + u64 cc_busy; + u64 cc_rx; + u64 cc_bss_rx; + u64 cc_tx; + + s8 noise; +}; + +struct mt76_sband { + struct ieee80211_supported_band sband; + struct mt76_channel_state *chan; +}; + +/* addr req mask */ +#define MT_VEND_TYPE_EEPROM BIT(31) +#define MT_VEND_TYPE_CFG BIT(30) +#define MT_VEND_TYPE_MASK (MT_VEND_TYPE_EEPROM | MT_VEND_TYPE_CFG) + +#define MT_VEND_ADDR(type, n) (MT_VEND_TYPE_##type | (n)) +enum mt_vendor_req { + MT_VEND_DEV_MODE = 0x1, + MT_VEND_WRITE = 0x2, + MT_VEND_POWER_ON = 0x4, + MT_VEND_MULTI_WRITE = 0x6, + MT_VEND_MULTI_READ = 0x7, + MT_VEND_READ_EEPROM = 0x9, + MT_VEND_WRITE_FCE = 0x42, + MT_VEND_WRITE_CFG = 0x46, + MT_VEND_READ_CFG = 0x47, + MT_VEND_READ_EXT = 0x63, + MT_VEND_WRITE_EXT = 0x66, + MT_VEND_FEATURE_SET = 0x91, +}; + +enum mt76u_in_ep { + MT_EP_IN_PKT_RX, + MT_EP_IN_CMD_RESP, + __MT_EP_IN_MAX, +}; + +enum mt76u_out_ep { + MT_EP_OUT_INBAND_CMD, + MT_EP_OUT_AC_BE, + MT_EP_OUT_AC_BK, + MT_EP_OUT_AC_VI, + MT_EP_OUT_AC_VO, + MT_EP_OUT_HCCA, + __MT_EP_OUT_MAX, +}; + +struct mt76_mcu { + struct mutex mutex; + u32 msg_seq; + int timeout; + + struct sk_buff_head res_q; + wait_queue_head_t wait; +}; + +#define MT_TX_SG_MAX_SIZE 8 +#define MT_RX_SG_MAX_SIZE 4 +#define MT_NUM_TX_ENTRIES 256 +#define MT_NUM_RX_ENTRIES 128 +#define MCU_RESP_URB_SIZE 1024 +struct mt76_usb { + struct mutex usb_ctrl_mtx; + u8 *data; + u16 data_len; + + struct mt76_worker status_worker; + struct mt76_worker rx_worker; + + struct work_struct stat_work; + + u8 out_ep[__MT_EP_OUT_MAX]; + u8 in_ep[__MT_EP_IN_MAX]; + void (*ctrl_timeout)(struct mt76_dev *dev, int err); + bool sg_en; + + struct mt76u_mcu { + u8 *data; + /* multiple reads */ + struct mt76_reg_pair *rp; + int rp_len; + u32 base; + } mcu; +}; + +#define MT76S_XMIT_BUF_SZ 0x3fe00 +#define MT76S_NUM_TX_ENTRIES 256 +#define MT76S_NUM_RX_ENTRIES 512 +struct mt76_sdio { + struct mt76_worker txrx_worker; + struct mt76_worker status_worker; + struct mt76_worker net_worker; + struct mt76_worker stat_worker; + + u8 *xmit_buf; + u32 xmit_buf_sz; + + struct sdio_func *func; + void *intr_data; + u8 hw_ver; + wait_queue_head_t wait; + + int pse_mcu_quota_max; + struct { + int pse_data_quota; + int ple_data_quota; + int pse_mcu_quota; + int pse_page_size; + int deficit; + } sched; + + int (*parse_irq)(struct mt76_dev *dev, struct mt76s_intr *intr); +}; + +struct mt76_mmio { + void __iomem *regs; + spinlock_t irq_lock; + u32 irqmask; + + struct mtk_wed_device wed; + struct mtk_wed_device wed_hif2; + struct completion wed_reset; + struct completion wed_reset_complete; + + struct airoha_ppe_dev __rcu *ppe_dev; + struct airoha_npu __rcu *npu; + phys_addr_t phy_addr; + int npu_type; +}; + +struct mt76_rx_status { + union { + struct mt76_wcid *wcid; + u16 wcid_idx; + }; + + u32 reorder_time; + + u32 ampdu_ref; + u32 timestamp; + + u8 iv[6]; + + u8 phy_idx:2; + u8 aggr:1; + u8 qos_ctl; + u16 seqno; + + u16 freq; + u32 flag; + u8 enc_flags; + u8 encoding:3, bw:4; + union { + struct { + u8 he_ru:3; + u8 he_gi:2; + u8 he_dcm:1; + }; + struct { + u8 ru:4; + u8 gi:2; + } eht; + }; + + u8 amsdu:1, first_amsdu:1, last_amsdu:1; + u8 rate_idx; + u8 nss:5, band:3; + s8 signal; + u8 chains; + s8 chain_signal[IEEE80211_MAX_CHAINS]; +}; + +struct mt76_freq_range_power { + const struct cfg80211_sar_freq_ranges *range; + s8 power; +}; + +struct mt76_testmode_ops { + int (*set_state)(struct mt76_phy *phy, enum mt76_testmode_state state); + int (*set_params)(struct mt76_phy *phy, struct nlattr **tb, + enum mt76_testmode_state new_state); + int (*dump_stats)(struct mt76_phy *phy, struct sk_buff *msg); +}; + +struct mt76_testmode_data { + enum mt76_testmode_state state; + + u32 param_set[DIV_ROUND_UP(NUM_MT76_TM_ATTRS, 32)]; + struct sk_buff *tx_skb; + + u32 tx_count; + u16 tx_mpdu_len; + + u8 tx_rate_mode; + u8 tx_rate_idx; + u8 tx_rate_nss; + u8 tx_rate_sgi; + u8 tx_rate_ldpc; + u8 tx_rate_stbc; + u8 tx_ltf; + + u8 tx_antenna_mask; + u8 tx_spe_idx; + + u8 tx_duty_cycle; + u32 tx_time; + u32 tx_ipg; + + u32 freq_offset; + + u8 tx_power[4]; + u8 tx_power_control; + + u8 addr[3][ETH_ALEN]; + + u32 tx_pending; + u32 tx_queued; + u16 tx_queued_limit; + u32 tx_done; + struct { + u64 packets[__MT_RXQ_MAX]; + u64 fcs_error[__MT_RXQ_MAX]; + } rx_stats; +}; + +struct mt76_vif_link { + u8 idx; + u8 link_idx; + u8 omac_idx; + u8 band_idx; + u8 wmm_idx; + u8 scan_seq_num; + u8 cipher; + u8 basic_rates_idx; + u8 mcast_rates_idx; + u8 beacon_rates_idx; + bool offchannel; + unsigned long beacon_mon_last; + u16 beacon_mon_interval; + struct ieee80211_chanctx_conf *ctx; + struct mt76_wcid *wcid; + struct mt76_vif_data *mvif; + struct rcu_head rcu_head; +}; + +struct mt76_vif_data { + struct mt76_vif_link __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS]; + struct mt76_vif_link __rcu *offchannel_link; + + struct mt76_phy *roc_phy; + u16 valid_links; + u8 deflink_id; +}; + +struct mt76_phy { + struct ieee80211_hw *hw; + struct mt76_dev *dev; + void *priv; + + unsigned long state; + unsigned int num_sta; + u8 band_idx; + + spinlock_t tx_lock; + struct list_head tx_list; + struct mt76_queue *q_tx[__MT_TXQ_MAX]; + + atomic_t mgmt_tx_pending; + + struct cfg80211_chan_def chandef; + struct cfg80211_chan_def main_chandef; + bool offchannel; + bool radar_enabled; + bool no_active_monitor; + + struct delayed_work roc_work; + struct ieee80211_vif *roc_vif; + struct mt76_vif_link *roc_link; + + struct mt76_chanctx *chanctx; + + struct mt76_channel_state *chan_state; + enum mt76_dfs_state dfs_state; + ktime_t survey_time; + + u32 aggr_stats[32]; + + struct mt76_hw_cap cap; + struct mt76_sband sband_2g; + struct mt76_sband sband_5g; + struct mt76_sband sband_6g; + + u8 macaddr[ETH_ALEN]; + + int txpower_cur; + u8 antenna_mask; + u16 chainmask; + +#ifdef CONFIG_NL80211_TESTMODE + struct mt76_testmode_data test; +#endif + + struct delayed_work mac_work; + u8 mac_work_count; + + struct { + struct sk_buff *head; + struct sk_buff **tail; + u16 seqno; + } rx_amsdu[__MT_RXQ_MAX]; + + struct mt76_freq_range_power *frp; + + struct { + struct led_classdev cdev; + char name[32]; + bool al; + u8 pin; + } leds; +}; + +struct mt76_dev { + struct mt76_phy phy; /* must be first */ + struct mt76_phy *phys[__MT_MAX_BAND]; + struct mt76_phy *band_phys[NUM_NL80211_BANDS]; + + struct ieee80211_hw *hw; + + spinlock_t wed_lock; + spinlock_t lock; + spinlock_t cc_lock; + + u32 cur_cc_bss_rx; + + struct mt76_rx_status rx_ampdu_status; + u32 rx_ampdu_len; + u32 rx_ampdu_ref; + + struct mutex mutex; + + const struct mt76_bus_ops *bus; + const struct mt76_driver_ops *drv; + const struct mt76_mcu_ops *mcu_ops; + + /* Optional callback to finalize wiphy state before registration. */ + int (*init_wiphy)(struct mt76_dev *dev); + struct device *dev; + struct device *dma_dev; + + struct mt76_mcu mcu; + + struct net_device *napi_dev; + struct net_device *tx_napi_dev; + spinlock_t rx_lock; + struct napi_struct napi[__MT_RXQ_MAX]; + struct sk_buff_head rx_skb[__MT_RXQ_MAX]; + struct tasklet_struct irq_tasklet; + + struct list_head txwi_cache; + struct list_head rxwi_cache; + struct mt76_queue *q_mcu[__MT_MCUQ_MAX]; + struct mt76_queue q_rx[__MT_RXQ_MAX]; + const struct mt76_queue_ops *queue_ops; + int tx_dma_idx[4]; + enum mt76_hwrro_mode hwrro_mode; + + struct mt76_worker tx_worker; + struct napi_struct tx_napi; + + spinlock_t token_lock; + struct idr token; + u16 wed_token_count; + u16 token_count; + u16 token_start; + u16 token_size; + + spinlock_t rx_token_lock; + struct idr rx_token; + u16 rx_token_size; + + wait_queue_head_t tx_wait; + /* spinclock used to protect wcid pktid linked list */ + spinlock_t status_lock; + + u32 wcid_mask[DIV_ROUND_UP(MT76_N_WCIDS, 32)]; + + u64 vif_mask; + + struct mt76_wcid global_wcid; + struct mt76_wcid __rcu *wcid[MT76_N_WCIDS]; + struct list_head wcid_list; + + struct list_head sta_poll_list; + spinlock_t sta_poll_lock; + + u32 rev; + + struct tasklet_struct pre_tbtt_tasklet; + int beacon_int; + u8 beacon_mask; + + struct debugfs_blob_wrapper eeprom; + struct debugfs_blob_wrapper otp; + + char alpha2[3]; + enum nl80211_dfs_regions region; + + struct mt76_scan_rnr_param rnr; + + u32 debugfs_reg; + + u8 csa_complete; + + u32 rxfilter; + + struct delayed_work scan_work; + spinlock_t scan_lock; + struct { + struct cfg80211_scan_request *req; + struct ieee80211_channel *chan; + struct ieee80211_vif *vif; + struct mt76_vif_link *mlink; + struct mt76_phy *phy; + int chan_idx; + bool beacon_wait; + bool beacon_received; + } scan; + +#ifdef CONFIG_NL80211_TESTMODE + const struct mt76_testmode_ops *test_ops; + struct { + const char *name; + u32 offset; + } test_mtd; +#endif + struct workqueue_struct *wq; + + union { + struct mt76_mmio mmio; + struct mt76_usb usb; + struct mt76_sdio sdio; + }; + + atomic_t bus_hung; +}; + +/* per-phy stats. */ +struct mt76_mib_stats { + u32 ack_fail_cnt; + u32 fcs_err_cnt; + u32 rts_cnt; + u32 rts_retries_cnt; + u32 ba_miss_cnt; + u32 tx_bf_cnt; + u32 tx_mu_bf_cnt; + u32 tx_mu_mpdu_cnt; + u32 tx_mu_acked_mpdu_cnt; + u32 tx_su_acked_mpdu_cnt; + u32 tx_bf_ibf_ppdu_cnt; + u32 tx_bf_ebf_ppdu_cnt; + + u32 tx_bf_rx_fb_all_cnt; + u32 tx_bf_rx_fb_eht_cnt; + u32 tx_bf_rx_fb_he_cnt; + u32 tx_bf_rx_fb_vht_cnt; + u32 tx_bf_rx_fb_ht_cnt; + + u32 tx_bf_rx_fb_bw; /* value of last sample, not cumulative */ + u32 tx_bf_rx_fb_nc_cnt; + u32 tx_bf_rx_fb_nr_cnt; + u32 tx_bf_fb_cpl_cnt; + u32 tx_bf_fb_trig_cnt; + + u32 tx_ampdu_cnt; + u32 tx_stop_q_empty_cnt; + u32 tx_mpdu_attempts_cnt; + u32 tx_mpdu_success_cnt; + u32 tx_pkt_ebf_cnt; + u32 tx_pkt_ibf_cnt; + + u32 tx_rwp_fail_cnt; + u32 tx_rwp_need_cnt; + + /* rx stats */ + u32 rx_fifo_full_cnt; + u32 channel_idle_cnt; + u32 primary_cca_busy_time; + u32 secondary_cca_busy_time; + u32 primary_energy_detect_time; + u32 cck_mdrdy_time; + u32 ofdm_mdrdy_time; + u32 green_mdrdy_time; + u32 rx_vector_mismatch_cnt; + u32 rx_delimiter_fail_cnt; + u32 rx_mrdy_cnt; + u32 rx_len_mismatch_cnt; + u32 rx_mpdu_cnt; + u32 rx_ampdu_cnt; + u32 rx_ampdu_bytes_cnt; + u32 rx_ampdu_valid_subframe_cnt; + u32 rx_ampdu_valid_subframe_bytes_cnt; + u32 rx_pfdrop_cnt; + u32 rx_vec_queue_overflow_drop_cnt; + u32 rx_ba_cnt; + + u32 tx_amsdu[8]; + u32 tx_amsdu_cnt; + + /* mcu_muru_stats */ + u32 dl_cck_cnt; + u32 dl_ofdm_cnt; + u32 dl_htmix_cnt; + u32 dl_htgf_cnt; + u32 dl_vht_su_cnt; + u32 dl_vht_2mu_cnt; + u32 dl_vht_3mu_cnt; + u32 dl_vht_4mu_cnt; + u32 dl_he_su_cnt; + u32 dl_he_ext_su_cnt; + u32 dl_he_2ru_cnt; + u32 dl_he_2mu_cnt; + u32 dl_he_3ru_cnt; + u32 dl_he_3mu_cnt; + u32 dl_he_4ru_cnt; + u32 dl_he_4mu_cnt; + u32 dl_he_5to8ru_cnt; + u32 dl_he_9to16ru_cnt; + u32 dl_he_gtr16ru_cnt; + + u32 ul_hetrig_su_cnt; + u32 ul_hetrig_2ru_cnt; + u32 ul_hetrig_3ru_cnt; + u32 ul_hetrig_4ru_cnt; + u32 ul_hetrig_5to8ru_cnt; + u32 ul_hetrig_9to16ru_cnt; + u32 ul_hetrig_gtr16ru_cnt; + u32 ul_hetrig_2mu_cnt; + u32 ul_hetrig_3mu_cnt; + u32 ul_hetrig_4mu_cnt; +}; + +struct mt76_power_limits { + s8 cck[4]; + s8 ofdm[8]; + s8 mcs[4][10]; + s8 ru[7][12]; + s8 eht[16][16]; + + struct { + s8 cck[4]; + s8 ofdm[4]; + s8 ofdm_bf[4]; + s8 ru[7][10]; + s8 ru_bf[7][10]; + } path; +}; + +struct mt76_ethtool_worker_info { + u64 *data; + int idx; + int initial_stat_idx; + int worker_stat_count; + int sta_count; +}; + +struct mt76_chanctx { + struct mt76_phy *phy; +}; + +#define CCK_RATE(_idx, _rate) { \ + .bitrate = _rate, \ + .flags = IEEE80211_RATE_SHORT_PREAMBLE, \ + .hw_value = (MT_PHY_TYPE_CCK << 8) | (_idx), \ + .hw_value_short = (MT_PHY_TYPE_CCK << 8) | (4 + _idx), \ +} + +#define OFDM_RATE(_idx, _rate) { \ + .bitrate = _rate, \ + .hw_value = (MT_PHY_TYPE_OFDM << 8) | (_idx), \ + .hw_value_short = (MT_PHY_TYPE_OFDM << 8) | (_idx), \ +} + +extern struct ieee80211_rate mt76_rates[12]; + +#define __mt76_rr(dev, ...) (dev)->bus->rr((dev), __VA_ARGS__) +#define __mt76_wr(dev, ...) (dev)->bus->wr((dev), __VA_ARGS__) +#define __mt76_rmw(dev, ...) (dev)->bus->rmw((dev), __VA_ARGS__) +#define __mt76_wr_copy(dev, ...) (dev)->bus->write_copy((dev), __VA_ARGS__) +#define __mt76_rr_copy(dev, ...) (dev)->bus->read_copy((dev), __VA_ARGS__) + +#define __mt76_set(dev, offset, val) __mt76_rmw(dev, offset, 0, val) +#define __mt76_clear(dev, offset, val) __mt76_rmw(dev, offset, val, 0) + +#define mt76_rr(dev, ...) (dev)->mt76.bus->rr(&((dev)->mt76), __VA_ARGS__) +#define mt76_wr(dev, ...) (dev)->mt76.bus->wr(&((dev)->mt76), __VA_ARGS__) +#define mt76_rmw(dev, ...) (dev)->mt76.bus->rmw(&((dev)->mt76), __VA_ARGS__) +#define mt76_wr_copy(dev, ...) (dev)->mt76.bus->write_copy(&((dev)->mt76), __VA_ARGS__) +#define mt76_rr_copy(dev, ...) (dev)->mt76.bus->read_copy(&((dev)->mt76), __VA_ARGS__) +#define mt76_wr_rp(dev, ...) (dev)->mt76.bus->wr_rp(&((dev)->mt76), __VA_ARGS__) +#define mt76_rd_rp(dev, ...) (dev)->mt76.bus->rd_rp(&((dev)->mt76), __VA_ARGS__) + + +#define mt76_mcu_restart(dev, ...) (dev)->mt76.mcu_ops->mcu_restart(&((dev)->mt76)) + +#define mt76_set(dev, offset, val) mt76_rmw(dev, offset, 0, val) +#define mt76_clear(dev, offset, val) mt76_rmw(dev, offset, val, 0) + +#define mt76_get_field(_dev, _reg, _field) \ + FIELD_GET(_field, mt76_rr(dev, _reg)) + +#define mt76_rmw_field(_dev, _reg, _field, _val) \ + mt76_rmw(_dev, _reg, _field, FIELD_PREP(_field, _val)) + +#define __mt76_rmw_field(_dev, _reg, _field, _val) \ + __mt76_rmw(_dev, _reg, _field, FIELD_PREP(_field, _val)) + +#define mt76_hw(dev) (dev)->mphy.hw + +bool __mt76_poll(struct mt76_dev *dev, u32 offset, u32 mask, u32 val, + int timeout); + +#define mt76_poll(dev, ...) __mt76_poll(&((dev)->mt76), __VA_ARGS__) + +bool ____mt76_poll_msec(struct mt76_dev *dev, u32 offset, u32 mask, u32 val, + int timeout, int kick); +#define __mt76_poll_msec(...) ____mt76_poll_msec(__VA_ARGS__, 10) +#define mt76_poll_msec(dev, ...) ____mt76_poll_msec(&((dev)->mt76), __VA_ARGS__, 10) +#define mt76_poll_msec_tick(dev, ...) ____mt76_poll_msec(&((dev)->mt76), __VA_ARGS__) + +void mt76_mmio_init(struct mt76_dev *dev, void __iomem *regs); +void mt76_pci_disable_aspm(struct pci_dev *pdev); +bool mt76_pci_aspm_supported(struct pci_dev *pdev); + +static inline u16 mt76_chip(struct mt76_dev *dev) +{ + return dev->rev >> 16; +} + +static inline u16 mt76_rev(struct mt76_dev *dev) +{ + return dev->rev & 0xffff; +} + +void mt76_wed_release_rx_buf(struct mtk_wed_device *wed); +void mt76_wed_offload_disable(struct mtk_wed_device *wed); +void mt76_wed_reset_complete(struct mtk_wed_device *wed); +void mt76_wed_dma_reset(struct mt76_dev *dev); +int mt76_wed_net_setup_tc(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct net_device *netdev, enum tc_setup_type type, + void *type_data); +#ifdef CONFIG_NET_MEDIATEK_SOC_WED +u32 mt76_wed_init_rx_buf(struct mtk_wed_device *wed, int size); +int mt76_wed_offload_enable(struct mtk_wed_device *wed); +int mt76_wed_dma_setup(struct mt76_dev *dev, struct mt76_queue *q, bool reset); +#else +static inline u32 mt76_wed_init_rx_buf(struct mtk_wed_device *wed, int size) +{ + return 0; +} + +static inline int mt76_wed_offload_enable(struct mtk_wed_device *wed) +{ + return 0; +} + +static inline int mt76_wed_dma_setup(struct mt76_dev *dev, struct mt76_queue *q, + bool reset) +{ + return 0; +} +#endif /* CONFIG_NET_MEDIATEK_SOC_WED */ + +#define mt76xx_chip(dev) mt76_chip(&((dev)->mt76)) +#define mt76xx_rev(dev) mt76_rev(&((dev)->mt76)) + +#define mt76_init_queues(dev, ...) (dev)->mt76.queue_ops->init(&((dev)->mt76), __VA_ARGS__) +#define mt76_queue_alloc(dev, ...) (dev)->mt76.queue_ops->alloc(&((dev)->mt76), __VA_ARGS__) +#define mt76_tx_queue_skb_raw(dev, ...) (dev)->mt76.queue_ops->tx_queue_skb_raw(&((dev)->mt76), __VA_ARGS__) +#define mt76_tx_queue_skb(dev, ...) (dev)->mt76.queue_ops->tx_queue_skb(&((dev)->mphy), __VA_ARGS__) +#define mt76_queue_rx_reset(dev, ...) (dev)->mt76.queue_ops->rx_reset(&((dev)->mt76), __VA_ARGS__) +#define mt76_queue_tx_cleanup(dev, ...) (dev)->mt76.queue_ops->tx_cleanup(&((dev)->mt76), __VA_ARGS__) +#define mt76_queue_rx_init(dev, ...) (dev)->mt76.queue_ops->rx_queue_init(&((dev)->mt76), __VA_ARGS__) +#define mt76_queue_rx_cleanup(dev, ...) (dev)->mt76.queue_ops->rx_cleanup(&((dev)->mt76), __VA_ARGS__) +#define mt76_queue_kick(dev, ...) (dev)->mt76.queue_ops->kick(&((dev)->mt76), __VA_ARGS__) +#define mt76_queue_reset(dev, ...) (dev)->mt76.queue_ops->reset_q(&((dev)->mt76), __VA_ARGS__) + +#define mt76_for_each_q_rx(dev, i) \ + for (i = 0; i < ARRAY_SIZE((dev)->q_rx); i++) \ + if ((dev)->q_rx[i].ndesc) + + +#define mt76_dereference(p, dev) \ + rcu_dereference_protected(p, lockdep_is_held(&(dev)->mutex)) + +static inline struct mt76_dev *mt76_wed_to_dev(struct mtk_wed_device *wed) +{ +#ifdef CONFIG_NET_MEDIATEK_SOC_WED + if (wed->wlan.hif2) + return container_of(wed, struct mt76_dev, mmio.wed_hif2); +#endif /* CONFIG_NET_MEDIATEK_SOC_WED */ + return container_of(wed, struct mt76_dev, mmio.wed); +} + +static inline struct mt76_wcid * +__mt76_wcid_ptr(struct mt76_dev *dev, u16 idx) +{ + if (idx >= ARRAY_SIZE(dev->wcid)) + return NULL; + return rcu_dereference(dev->wcid[idx]); +} + +#define mt76_wcid_ptr(dev, idx) __mt76_wcid_ptr(&(dev)->mt76, idx) + +struct mt76_dev *mt76_alloc_device(struct device *pdev, unsigned int size, + const struct ieee80211_ops *ops, + const struct mt76_driver_ops *drv_ops); +int mt76_register_device(struct mt76_dev *dev, bool vht, + struct ieee80211_rate *rates, int n_rates); +void mt76_unregister_device(struct mt76_dev *dev); +void mt76_free_device(struct mt76_dev *dev); +void mt76_reset_device(struct mt76_dev *dev); +void mt76_unregister_phy(struct mt76_phy *phy); + +struct mt76_phy *mt76_alloc_radio_phy(struct mt76_dev *dev, unsigned int size, + u8 band_idx); +struct mt76_phy *mt76_alloc_phy(struct mt76_dev *dev, unsigned int size, + const struct ieee80211_ops *ops, + u8 band_idx); +int mt76_register_phy(struct mt76_phy *phy, bool vht, + struct ieee80211_rate *rates, int n_rates); +struct mt76_phy *mt76_vif_phy(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); + +struct dentry *mt76_register_debugfs_fops(struct mt76_phy *phy, + const struct file_operations *ops); +static inline struct dentry *mt76_register_debugfs(struct mt76_dev *dev) +{ + return mt76_register_debugfs_fops(&dev->phy, NULL); +} + +int mt76_queues_read(struct seq_file *s, void *data); +void mt76_seq_puts_array(struct seq_file *file, const char *str, + s8 *val, int len); + +int mt76_eeprom_init(struct mt76_dev *dev, int len); +int mt76_eeprom_override(struct mt76_phy *phy); +int mt76_get_of_data_from_mtd(struct mt76_dev *dev, void *eep, int offset, int len); +int mt76_get_of_data_from_nvmem(struct mt76_dev *dev, void *eep, + const char *cell_name, int len); + +struct mt76_queue * +mt76_init_queue(struct mt76_dev *dev, int qid, int idx, int n_desc, + int ring_base, void *wed, u32 flags); +static inline int mt76_init_tx_queue(struct mt76_phy *phy, int qid, int idx, + int n_desc, int ring_base, void *wed, + u32 flags) +{ + struct mt76_queue *q; + + q = mt76_init_queue(phy->dev, qid, idx, n_desc, ring_base, wed, flags); + if (IS_ERR(q)) + return PTR_ERR(q); + + phy->q_tx[qid] = q; + + return 0; +} + +static inline int mt76_init_mcu_queue(struct mt76_dev *dev, int qid, int idx, + int n_desc, int ring_base) +{ + struct mt76_queue *q; + + q = mt76_init_queue(dev, qid, idx, n_desc, ring_base, NULL, 0); + if (IS_ERR(q)) + return PTR_ERR(q); + + dev->q_mcu[qid] = q; + + return 0; +} + +static inline struct mt76_phy * +mt76_dev_phy(struct mt76_dev *dev, u8 phy_idx) +{ + if ((phy_idx == MT_BAND1 && dev->phys[phy_idx]) || + (phy_idx == MT_BAND2 && dev->phys[phy_idx])) + return dev->phys[phy_idx]; + + return &dev->phy; +} + +static inline struct ieee80211_hw * +mt76_phy_hw(struct mt76_dev *dev, u8 phy_idx) +{ + return mt76_dev_phy(dev, phy_idx)->hw; +} + +static inline u8 * +mt76_get_txwi_ptr(struct mt76_dev *dev, struct mt76_txwi_cache *t) +{ + return (u8 *)t - dev->drv->txwi_size; +} + +/* increment with wrap-around */ +static inline int mt76_incr(int val, int size) +{ + return (val + 1) & (size - 1); +} + +/* decrement with wrap-around */ +static inline int mt76_decr(int val, int size) +{ + return (val - 1) & (size - 1); +} + +u8 mt76_ac_to_hwq(u8 ac); + +static inline struct ieee80211_txq * +mtxq_to_txq(struct mt76_txq *mtxq) +{ + void *ptr = mtxq; + + return container_of(ptr, struct ieee80211_txq, drv_priv); +} + +static inline struct ieee80211_sta * +wcid_to_sta(struct mt76_wcid *wcid) +{ + void *ptr = wcid; + + if (!wcid || !wcid->sta) + return NULL; + + if (wcid->def_wcid) + ptr = wcid->def_wcid; + + return container_of(ptr, struct ieee80211_sta, drv_priv); +} + +static inline struct mt76_tx_cb *mt76_tx_skb_cb(struct sk_buff *skb) +{ + BUILD_BUG_ON(sizeof(struct mt76_tx_cb) > + sizeof(IEEE80211_SKB_CB(skb)->status.status_driver_data)); + return ((void *)IEEE80211_SKB_CB(skb)->status.status_driver_data); +} + +static inline void *mt76_skb_get_hdr(struct sk_buff *skb) +{ + struct mt76_rx_status mstat; + u8 *data = skb->data; + + /* Alignment concerns */ + BUILD_BUG_ON(sizeof(struct ieee80211_radiotap_he) % 4); + BUILD_BUG_ON(sizeof(struct ieee80211_radiotap_he_mu) % 4); + + mstat = *((struct mt76_rx_status *)skb->cb); + + if (mstat.flag & RX_FLAG_RADIOTAP_HE) + data += sizeof(struct ieee80211_radiotap_he); + if (mstat.flag & RX_FLAG_RADIOTAP_HE_MU) + data += sizeof(struct ieee80211_radiotap_he_mu); + + return data; +} + +static inline void mt76_insert_hdr_pad(struct sk_buff *skb) +{ + int len = ieee80211_get_hdrlen_from_skb(skb); + + if (len % 4 == 0) + return; + + skb_push(skb, 2); + memmove(skb->data, skb->data + 2, len); + + skb->data[len] = 0; + skb->data[len + 1] = 0; +} + +static inline bool mt76_is_skb_pktid(u8 pktid) +{ + if (pktid & MT_PACKET_ID_HAS_RATE) + return false; + + return pktid >= MT_PACKET_ID_FIRST; +} + +static inline u8 mt76_tx_power_path_delta(u8 path) +{ + static const u8 path_delta[5] = { 0, 6, 9, 12, 14 }; + u8 idx = path - 1; + + return (idx < ARRAY_SIZE(path_delta)) ? path_delta[idx] : 0; +} + +static inline bool mt76_testmode_enabled(struct mt76_phy *phy) +{ +#ifdef CONFIG_NL80211_TESTMODE + return phy->test.state != MT76_TM_STATE_OFF; +#else + return false; +#endif +} + +static inline bool mt76_is_testmode_skb(struct mt76_dev *dev, + struct sk_buff *skb, + struct ieee80211_hw **hw) +{ +#ifdef CONFIG_NL80211_TESTMODE + int i; + + for (i = 0; i < ARRAY_SIZE(dev->phys); i++) { + struct mt76_phy *phy = dev->phys[i]; + + if (phy && skb == phy->test.tx_skb) { + *hw = dev->phys[i]->hw; + return true; + } + } + return false; +#else + return false; +#endif +} + +void mt76_rx(struct mt76_dev *dev, enum mt76_rxq_id q, struct sk_buff *skb); +void mt76_tx(struct mt76_phy *dev, struct ieee80211_sta *sta, + struct mt76_wcid *wcid, struct sk_buff *skb); +void mt76_wake_tx_queue(struct ieee80211_hw *hw, struct ieee80211_txq *txq); +void mt76_stop_tx_queues(struct mt76_phy *phy, struct ieee80211_sta *sta, + bool send_bar); +void mt76_tx_check_agg_ssn(struct ieee80211_sta *sta, struct sk_buff *skb); +void mt76_txq_schedule(struct mt76_phy *phy, enum mt76_txq_id qid); +void mt76_txq_schedule_all(struct mt76_phy *phy); +void mt76_txq_schedule_pending(struct mt76_phy *phy); +void mt76_tx_worker_run(struct mt76_dev *dev); +void mt76_tx_worker(struct mt76_worker *w); +void mt76_release_buffered_frames(struct ieee80211_hw *hw, + struct ieee80211_sta *sta, + u16 tids, int nframes, + enum ieee80211_frame_release_type reason, + bool more_data); +void mt76_sta_ps_transition(struct mt76_dev *dev, struct mt76_wcid *wcid, + bool ps); +bool mt76_has_tx_pending(struct mt76_phy *phy); +int mt76_update_channel(struct mt76_phy *phy); +void mt76_update_survey(struct mt76_phy *phy); +void mt76_update_survey_active_time(struct mt76_phy *phy, ktime_t time); +int mt76_get_survey(struct ieee80211_hw *hw, int idx, + struct survey_info *survey); +int mt76_rx_signal(u8 chain_mask, s8 *chain_signal); +void mt76_set_stream_caps(struct mt76_phy *phy, bool vht); + +int mt76_rx_aggr_start(struct mt76_dev *dev, struct mt76_wcid *wcid, u8 tid, + u16 ssn, u16 size); +void mt76_rx_aggr_stop(struct mt76_dev *dev, struct mt76_wcid *wcid, u8 tid); + +void mt76_wcid_key_setup(struct mt76_dev *dev, struct mt76_wcid *wcid, + struct ieee80211_key_conf *key); + +void mt76_tx_status_lock(struct mt76_dev *dev, struct sk_buff_head *list) + __acquires(&dev->status_lock); +void mt76_tx_status_unlock(struct mt76_dev *dev, struct sk_buff_head *list) + __releases(&dev->status_lock); + +int mt76_tx_status_skb_add(struct mt76_dev *dev, struct mt76_wcid *wcid, + struct sk_buff *skb); +struct sk_buff *mt76_tx_status_skb_get(struct mt76_dev *dev, + struct mt76_wcid *wcid, int pktid, + struct sk_buff_head *list); +void mt76_tx_status_skb_done(struct mt76_dev *dev, struct sk_buff *skb, + struct sk_buff_head *list); +void __mt76_tx_complete_skb(struct mt76_dev *dev, u16 wcid, struct sk_buff *skb, + struct list_head *free_list); +static inline void +mt76_tx_complete_skb(struct mt76_dev *dev, u16 wcid, struct sk_buff *skb) +{ + __mt76_tx_complete_skb(dev, wcid, skb, NULL); +} + +void mt76_tx_status_check(struct mt76_dev *dev, bool flush); +int mt76_sta_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + enum ieee80211_sta_state old_state, + enum ieee80211_sta_state new_state); +void __mt76_sta_remove(struct mt76_phy *phy, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); +void mt76_sta_pre_rcu_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); + +int mt76_get_min_avg_rssi(struct mt76_dev *dev, u8 phy_idx); + +s8 mt76_get_power_bound(struct mt76_phy *phy, s8 txpower); + +int mt76_get_txpower(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + unsigned int link_id, int *dbm); +int mt76_init_sar_power(struct ieee80211_hw *hw, + const struct cfg80211_sar_specs *sar); +int mt76_get_sar_power(struct mt76_phy *phy, + struct ieee80211_channel *chan, + int power); + +void mt76_csa_check(struct mt76_dev *dev); +void mt76_csa_finish(struct mt76_dev *dev); + +int mt76_get_antenna(struct ieee80211_hw *hw, int radio_idx, u32 *tx_ant, + u32 *rx_ant); +int mt76_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, bool set); +void mt76_insert_ccmp_hdr(struct sk_buff *skb, u8 key_id); +int mt76_get_rate(struct mt76_dev *dev, + struct ieee80211_supported_band *sband, + int idx, bool cck); +int mt76_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_scan_request *hw_req); +void mt76_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif); +void mt76_scan_rx_beacon(struct mt76_dev *dev, struct ieee80211_channel *chan); +void mt76_rx_beacon(struct mt76_phy *phy, struct sk_buff *skb); +void mt76_beacon_mon_check(struct mt76_phy *phy); +void mt76_sw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + const u8 *mac); +void mt76_sw_scan_complete(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); +enum mt76_dfs_state mt76_phy_dfs_state(struct mt76_phy *phy); +int mt76_add_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *conf); +void mt76_remove_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *conf); +void mt76_change_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *conf, + u32 changed); +int mt76_assign_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_chanctx_conf *conf); +void mt76_unassign_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_chanctx_conf *conf); +int mt76_switch_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif_chanctx_switch *vifs, + int n_vifs, + enum ieee80211_chanctx_switch_mode mode); +int mt76_remain_on_channel(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_channel *chan, int duration, + enum ieee80211_roc_type type); +int mt76_cancel_remain_on_channel(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); +int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + void *data, int len); +int mt76_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *skb, + struct netlink_callback *cb, void *data, int len); +int mt76_testmode_set_state(struct mt76_phy *phy, enum mt76_testmode_state state); +int mt76_testmode_alloc_skb(struct mt76_phy *phy, u32 len); + +#if IS_ENABLED(CONFIG_MT76_NPU) +void mt76_npu_check_ppe(struct mt76_dev *dev, struct sk_buff *skb, + u32 info); +int mt76_npu_dma_add_buf(struct mt76_phy *phy, struct mt76_queue *q, + struct sk_buff *skb, struct mt76_queue_buf *buf, + void *txwi_ptr); +int mt76_npu_rx_queue_init(struct mt76_dev *dev, struct mt76_queue *q); +int mt76_npu_fill_rx_queue(struct mt76_dev *dev, struct mt76_queue *q); +void mt76_npu_queue_cleanup(struct mt76_dev *dev, struct mt76_queue *q); +void mt76_npu_disable_irqs(struct mt76_dev *dev); +int mt76_npu_init(struct mt76_dev *dev, phys_addr_t phy_addr, int type); +void mt76_npu_deinit(struct mt76_dev *dev); +void mt76_npu_queue_setup(struct mt76_dev *dev, struct mt76_queue *q); +void mt76_npu_txdesc_cleanup(struct mt76_queue *q, int index); +int mt76_npu_net_setup_tc(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct net_device *dev, enum tc_setup_type type, + void *type_data); +int mt76_npu_send_txrx_addr(struct mt76_dev *dev, int ifindex, + u32 direction, u32 i_count_addr, + u32 o_status_addr, u32 o_count_addr); +#else +static inline void mt76_npu_check_ppe(struct mt76_dev *dev, + struct sk_buff *skb, u32 info) +{ +} + +static inline int mt76_npu_dma_add_buf(struct mt76_phy *phy, + struct mt76_queue *q, + struct sk_buff *skb, + struct mt76_queue_buf *buf, + void *txwi_ptr) +{ + return -EOPNOTSUPP; +} + +static inline int mt76_npu_fill_rx_queue(struct mt76_dev *dev, + struct mt76_queue *q) +{ + return 0; +} + +static inline void mt76_npu_queue_cleanup(struct mt76_dev *dev, + struct mt76_queue *q) +{ +} + +static inline void mt76_npu_disable_irqs(struct mt76_dev *dev) +{ +} + +static inline int mt76_npu_init(struct mt76_dev *dev, phys_addr_t phy_addr, + int type) +{ + return 0; +} + +static inline void mt76_npu_deinit(struct mt76_dev *dev) +{ +} + +static inline void mt76_npu_queue_setup(struct mt76_dev *dev, + struct mt76_queue *q) +{ +} + +static inline void mt76_npu_txdesc_cleanup(struct mt76_queue *q, + int index) +{ +} + +static inline int mt76_npu_net_setup_tc(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct net_device *dev, + enum tc_setup_type type, + void *type_data) +{ + return -EOPNOTSUPP; +} + +static inline int mt76_npu_send_txrx_addr(struct mt76_dev *dev, int ifindex, + u32 direction, u32 i_count_addr, + u32 o_status_addr, u32 o_count_addr) +{ + return -EOPNOTSUPP; +} +#endif /* CONFIG_MT76_NPU */ + +static inline bool mt76_npu_device_active(struct mt76_dev *dev) +{ + return mt76_is_mmio(dev) && !!rcu_access_pointer(dev->mmio.npu); +} + +static inline bool mt76_ppe_device_active(struct mt76_dev *dev) +{ + return mt76_is_mmio(dev) && !!rcu_access_pointer(dev->mmio.ppe_dev); +} + +static inline int mt76_npu_send_msg(struct airoha_npu *npu, int ifindex, + enum airoha_npu_wlan_set_cmd cmd, + u32 val, gfp_t gfp) +{ + return airoha_npu_wlan_send_msg(npu, ifindex, cmd, &val, sizeof(val), + gfp); +} + +static inline int mt76_npu_get_msg(struct airoha_npu *npu, int ifindex, + enum airoha_npu_wlan_get_cmd cmd, + u32 *val, gfp_t gfp) +{ + return airoha_npu_wlan_get_msg(npu, ifindex, cmd, val, sizeof(*val), + gfp); +} + +static inline void mt76_testmode_reset(struct mt76_phy *phy, bool disable) +{ +#ifdef CONFIG_NL80211_TESTMODE + enum mt76_testmode_state state = MT76_TM_STATE_IDLE; + + if (disable || phy->test.state == MT76_TM_STATE_OFF) + state = MT76_TM_STATE_OFF; + + mt76_testmode_set_state(phy, state); +#endif +} + + +/* internal */ +static inline struct ieee80211_hw * +mt76_tx_status_get_hw(struct mt76_dev *dev, struct sk_buff *skb) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + u8 phy_idx = (info->hw_queue & MT_TX_HW_QUEUE_PHY) >> 2; + struct ieee80211_hw *hw = mt76_phy_hw(dev, phy_idx); + + info->hw_queue &= ~MT_TX_HW_QUEUE_PHY; + + return hw; +} + +void mt76_put_txwi(struct mt76_dev *dev, struct mt76_txwi_cache *t); +void mt76_put_rxwi(struct mt76_dev *dev, struct mt76_txwi_cache *t); +struct mt76_txwi_cache *mt76_get_rxwi(struct mt76_dev *dev); +void mt76_free_pending_rxwi(struct mt76_dev *dev); +void mt76_rx_complete(struct mt76_dev *dev, struct sk_buff_head *frames, + struct napi_struct *napi); +void mt76_rx_poll_complete(struct mt76_dev *dev, enum mt76_rxq_id q, + struct napi_struct *napi); +void mt76_rx_aggr_reorder(struct sk_buff *skb, struct sk_buff_head *frames); +void mt76_testmode_tx_pending(struct mt76_phy *phy); +void mt76_queue_tx_complete(struct mt76_dev *dev, struct mt76_queue *q, + struct mt76_queue_entry *e); +int __mt76_set_channel(struct mt76_phy *phy, struct cfg80211_chan_def *chandef, + bool offchannel); + +static inline bool +mt76_offchannel_chandef(struct mt76_phy *phy, struct ieee80211_channel *chan, + struct cfg80211_chan_def *chandef) +{ + cfg80211_chandef_create(chandef, chan, NL80211_CHAN_HT20); + if (phy->main_chandef.chan != chan) + return true; + + *chandef = phy->main_chandef; + return false; +} +int mt76_set_channel(struct mt76_phy *phy, struct cfg80211_chan_def *chandef, + bool offchannel); +void mt76_scan_work(struct work_struct *work); +void mt76_abort_scan(struct mt76_dev *dev); +void mt76_roc_complete_work(struct work_struct *work); +void mt76_roc_complete(struct mt76_phy *phy); +void mt76_abort_roc(struct mt76_phy *phy); +struct mt76_vif_link *mt76_get_vif_phy_link(struct mt76_phy *phy, + struct ieee80211_vif *vif); +void mt76_put_vif_phy_link(struct mt76_phy *phy, struct ieee80211_vif *vif, + struct mt76_vif_link *mlink); +void mt76_offchannel_notify(struct mt76_phy *phy, bool offchannel); + +/* usb */ +static inline bool mt76u_urb_error(struct urb *urb) +{ + return urb->status && + urb->status != -ECONNRESET && + urb->status != -ESHUTDOWN && + urb->status != -ENOENT; +} + +static inline int +mt76u_bulk_msg(struct mt76_dev *dev, void *data, int len, int *actual_len, + int timeout, int ep) +{ + struct usb_interface *uintf = to_usb_interface(dev->dev); + struct usb_device *udev = interface_to_usbdev(uintf); + struct mt76_usb *usb = &dev->usb; + unsigned int pipe; + + if (actual_len) + pipe = usb_rcvbulkpipe(udev, usb->in_ep[ep]); + else + pipe = usb_sndbulkpipe(udev, usb->out_ep[ep]); + + return usb_bulk_msg(udev, pipe, data, len, actual_len, timeout); +} + +void mt76_ethtool_page_pool_stats(struct mt76_dev *dev, u64 *data, int *index); +void mt76_ethtool_worker(struct mt76_ethtool_worker_info *wi, + struct mt76_sta_stats *stats, bool eht); +int mt76_skb_adjust_pad(struct sk_buff *skb, int pad); +int __mt76u_vendor_request(struct mt76_dev *dev, u8 req, u8 req_type, + u16 val, u16 offset, void *buf, size_t len); +int mt76u_vendor_request(struct mt76_dev *dev, u8 req, + u8 req_type, u16 val, u16 offset, + void *buf, size_t len); +void mt76u_single_wr(struct mt76_dev *dev, const u8 req, + const u16 offset, const u32 val); +void mt76u_read_copy(struct mt76_dev *dev, u32 offset, + void *data, int len); +u32 ___mt76u_rr(struct mt76_dev *dev, u8 req, u8 req_type, u32 addr); +void ___mt76u_wr(struct mt76_dev *dev, u8 req, u8 req_type, + u32 addr, u32 val); +int __mt76u_init(struct mt76_dev *dev, struct usb_interface *intf, + struct mt76_bus_ops *ops); +int mt76u_init(struct mt76_dev *dev, struct usb_interface *intf); +int mt76u_alloc_mcu_queue(struct mt76_dev *dev); +int mt76u_alloc_queues(struct mt76_dev *dev); +void mt76u_stop_tx(struct mt76_dev *dev); +void mt76u_stop_rx(struct mt76_dev *dev); +int mt76u_resume_rx(struct mt76_dev *dev); +void mt76u_queues_deinit(struct mt76_dev *dev); + +int mt76s_init(struct mt76_dev *dev, struct sdio_func *func, + const struct mt76_bus_ops *bus_ops); +int mt76s_alloc_rx_queue(struct mt76_dev *dev, enum mt76_rxq_id qid); +int mt76s_alloc_tx(struct mt76_dev *dev); +void mt76s_deinit(struct mt76_dev *dev); +void mt76s_sdio_irq(struct sdio_func *func); +void mt76s_txrx_worker(struct mt76_sdio *sdio); +bool mt76s_txqs_empty(struct mt76_dev *dev); +int mt76s_hw_init(struct mt76_dev *dev, struct sdio_func *func, + int hw_ver); +u32 mt76s_rr(struct mt76_dev *dev, u32 offset); +void mt76s_wr(struct mt76_dev *dev, u32 offset, u32 val); +u32 mt76s_rmw(struct mt76_dev *dev, u32 offset, u32 mask, u32 val); +u32 mt76s_read_pcr(struct mt76_dev *dev); +void mt76s_write_copy(struct mt76_dev *dev, u32 offset, + const void *data, int len); +void mt76s_read_copy(struct mt76_dev *dev, u32 offset, + void *data, int len); +int mt76s_wr_rp(struct mt76_dev *dev, u32 base, + const struct mt76_reg_pair *data, + int len); +int mt76s_rd_rp(struct mt76_dev *dev, u32 base, + struct mt76_reg_pair *data, int len); + +struct sk_buff * +__mt76_mcu_msg_alloc(struct mt76_dev *dev, const void *data, + int len, int data_len, gfp_t gfp); +static inline struct sk_buff * +mt76_mcu_msg_alloc(struct mt76_dev *dev, const void *data, + int data_len) +{ + return __mt76_mcu_msg_alloc(dev, data, data_len, data_len, GFP_KERNEL); +} + +void mt76_mcu_rx_event(struct mt76_dev *dev, struct sk_buff *skb); +struct sk_buff *mt76_mcu_get_response(struct mt76_dev *dev, + unsigned long expires); +int mt76_mcu_send_and_get_msg(struct mt76_dev *dev, int cmd, const void *data, + int len, bool wait_resp, struct sk_buff **ret); +int mt76_mcu_skb_send_and_get_msg(struct mt76_dev *dev, struct sk_buff *skb, + int cmd, bool wait_resp, struct sk_buff **ret); +int __mt76_mcu_send_firmware(struct mt76_dev *dev, int cmd, const void *data, + int len, int max_len); +static inline int +mt76_mcu_send_firmware(struct mt76_dev *dev, int cmd, const void *data, + int len) +{ + int max_len = 4096 - dev->mcu_ops->headroom; + + return __mt76_mcu_send_firmware(dev, cmd, data, len, max_len); +} + +static inline int +mt76_mcu_send_msg(struct mt76_dev *dev, int cmd, const void *data, int len, + bool wait_resp) +{ + return mt76_mcu_send_and_get_msg(dev, cmd, data, len, wait_resp, NULL); +} + +static inline int +mt76_mcu_skb_send_msg(struct mt76_dev *dev, struct sk_buff *skb, int cmd, + bool wait_resp) +{ + return mt76_mcu_skb_send_and_get_msg(dev, skb, cmd, wait_resp, NULL); +} + +void mt76_set_irq_mask(struct mt76_dev *dev, u32 addr, u32 clear, u32 set); + +struct device_node * +mt76_find_power_limits_node(struct mt76_dev *dev); +struct device_node * +mt76_find_channel_node(struct device_node *np, struct ieee80211_channel *chan); + +s8 mt76_get_rate_power_limits(struct mt76_phy *phy, + struct ieee80211_channel *chan, + struct mt76_power_limits *dest, + s8 target_power); + +static inline bool mt76_queue_is_rx(struct mt76_dev *dev, struct mt76_queue *q) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(dev->q_rx); i++) { + if (q == &dev->q_rx[i]) + return true; + } + + return false; +} + +static inline bool mt76_queue_is_wed_tx_free(struct mt76_queue *q) +{ + return (q->flags & MT_QFLAG_WED) && + FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_Q_TXFREE; +} + +static inline bool mt76_queue_is_wed_rro(struct mt76_queue *q) +{ + return q->flags & MT_QFLAG_WED_RRO; +} + +static inline bool mt76_queue_is_wed_rro_ind(struct mt76_queue *q) +{ + return mt76_queue_is_wed_rro(q) && + FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_RRO_Q_IND; +} + +static inline bool mt76_queue_is_wed_rro_rxdmad_c(struct mt76_queue *q) +{ + return mt76_queue_is_wed_rro(q) && + FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_RRO_Q_RXDMAD_C; +} + +static inline bool mt76_queue_is_wed_rro_data(struct mt76_queue *q) +{ + return mt76_queue_is_wed_rro(q) && + FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_RRO_Q_DATA; +} + +static inline bool mt76_queue_is_wed_rro_msdu_pg(struct mt76_queue *q) +{ + return mt76_queue_is_wed_rro(q) && + FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == + MT76_WED_RRO_Q_MSDU_PG; +} + +static inline bool mt76_queue_is_wed_rx(struct mt76_queue *q) +{ + return (q->flags & MT_QFLAG_WED) && + FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_Q_RX; +} + +static inline bool mt76_queue_is_emi(struct mt76_queue *q) +{ + return q->flags & MT_QFLAG_EMI_EN; +} + +static inline bool mt76_queue_is_npu(struct mt76_queue *q) +{ + return q->flags & MT_QFLAG_NPU; +} + +static inline bool mt76_queue_is_npu_tx(struct mt76_queue *q) +{ + return mt76_queue_is_npu(q) && + FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_Q_TX; +} + +static inline bool mt76_queue_is_npu_rx(struct mt76_queue *q) +{ + return mt76_queue_is_npu(q) && + FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_Q_RX; +} + +static inline bool mt76_queue_is_npu_txfree(struct mt76_queue *q) +{ + if (q->flags & MT_QFLAG_WED) + return false; + + return FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_Q_TXFREE; +} + +struct mt76_txwi_cache * +mt76_token_release(struct mt76_dev *dev, int token, bool *wake); +int mt76_token_consume(struct mt76_dev *dev, struct mt76_txwi_cache **ptxwi); +void __mt76_set_tx_blocked(struct mt76_dev *dev, bool blocked); +struct mt76_txwi_cache *mt76_rx_token_release(struct mt76_dev *dev, int token); +int mt76_rx_token_consume(struct mt76_dev *dev, void *ptr, + struct mt76_txwi_cache *r, dma_addr_t phys); +int mt76_create_page_pool(struct mt76_dev *dev, struct mt76_queue *q); +static inline void mt76_put_page_pool_buf(void *buf, bool allow_direct) +{ + struct page *page = virt_to_head_page(buf); + + page_pool_put_full_page(pp_page_to_nmdesc(page)->pp, page, + allow_direct); +} + +static inline void * +mt76_get_page_pool_buf(struct mt76_queue *q, u32 *offset, u32 size) +{ + struct page *page; + + page = page_pool_alloc_frag(q->page_pool, offset, size, + GFP_ATOMIC | __GFP_NOWARN | GFP_DMA32); + if (!page) + return NULL; + + return page_address(page) + *offset; +} + +static inline void mt76_set_tx_blocked(struct mt76_dev *dev, bool blocked) +{ + spin_lock_bh(&dev->token_lock); + __mt76_set_tx_blocked(dev, blocked); + spin_unlock_bh(&dev->token_lock); +} + +static inline int +mt76_token_get(struct mt76_dev *dev, struct mt76_txwi_cache **ptxwi) +{ + int token; + + spin_lock_bh(&dev->token_lock); + token = idr_alloc(&dev->token, *ptxwi, 0, dev->token_size, GFP_ATOMIC); + spin_unlock_bh(&dev->token_lock); + + return token; +} + +static inline struct mt76_txwi_cache * +mt76_token_put(struct mt76_dev *dev, int token) +{ + struct mt76_txwi_cache *txwi; + + spin_lock_bh(&dev->token_lock); + txwi = idr_remove(&dev->token, token); + spin_unlock_bh(&dev->token_lock); + + return txwi; +} + +void mt76_wcid_init(struct mt76_wcid *wcid, u8 band_idx); +void mt76_wcid_cleanup(struct mt76_dev *dev, struct mt76_wcid *wcid); +void mt76_wcid_add_poll(struct mt76_dev *dev, struct mt76_wcid *wcid); + +static inline void +mt76_vif_init(struct ieee80211_vif *vif, struct mt76_vif_data *mvif) +{ + struct mt76_vif_link *mlink = (struct mt76_vif_link *)vif->drv_priv; + + mlink->mvif = mvif; + rcu_assign_pointer(mvif->link[0], mlink); +} + +void mt76_vif_cleanup(struct mt76_dev *dev, struct ieee80211_vif *vif); +u16 mt76_select_links(struct ieee80211_vif *vif, int max_active_links); + +static inline struct mt76_vif_link * +mt76_vif_link(struct mt76_dev *dev, struct ieee80211_vif *vif, int link_id) +{ + struct mt76_vif_link *mlink = (struct mt76_vif_link *)vif->drv_priv; + struct mt76_vif_data *mvif = mlink->mvif; + + if (!link_id) + return mlink; + + if (link_id >= IEEE80211_MLD_MAX_NUM_LINKS) + return NULL; + + return mt76_dereference(mvif->link[link_id], dev); +} + +static inline struct mt76_vif_link * +mt76_vif_conf_link(struct mt76_dev *dev, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf) +{ + struct mt76_vif_link *mlink = (struct mt76_vif_link *)vif->drv_priv; + struct mt76_vif_data *mvif = mlink->mvif; + + if (link_conf == &vif->bss_conf || !link_conf->link_id) + return mlink; + + return mt76_dereference(mvif->link[link_conf->link_id], dev); +} + +static inline struct mt76_phy * +mt76_vif_link_phy(struct mt76_vif_link *mlink) +{ + struct mt76_chanctx *ctx; + + if (!mlink->ctx) + return NULL; + + ctx = (struct mt76_chanctx *)mlink->ctx->drv_priv; + + return ctx->phy; +} + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/Kconfig b/drivers/net/wireless/mediatek/mt76/mt7603/Kconfig new file mode 100644 index 000000000..ae40a596e --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7603/Kconfig @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: BSD-3-Clause-Clear +config MT7603E + tristate "MediaTek MT7603E (PCIe) and MT76x8 WLAN support" + select MT76_CORE + depends on MAC80211 + depends on PCI + help + This adds support for MT7603E PCIe wireless devices and the WLAN core + on MT7628/MT7688 SoC devices. This family supports IEEE 802.11n 2x2 + to 300Mbps PHY rate + + To compile this driver as a module, choose M here. diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/Makefile b/drivers/net/wireless/mediatek/mt76/mt7603/Makefile new file mode 100644 index 000000000..e954165ee --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7603/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: BSD-3-Clause-Clear +obj-$(CONFIG_MT7603E) += mt7603e.o + +mt7603e-y := \ + pci.o soc.o main.o init.o mcu.o \ + core.o dma.o mac.o eeprom.o \ + beacon.o debugfs.o diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/beacon.c b/drivers/net/wireless/mediatek/mt76/mt7603/beacon.c new file mode 100644 index 000000000..acca98139 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7603/beacon.c @@ -0,0 +1,227 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear + +#include "mt7603.h" + +struct beacon_bc_data { + struct mt7603_dev *dev; + struct sk_buff_head q; + struct sk_buff *tail[MT7603_MAX_INTERFACES]; + int count[MT7603_MAX_INTERFACES]; +}; + +static void +mt7603_mac_stuck_beacon_recovery(struct mt7603_dev *dev) +{ + if (dev->beacon_check % 5 != 4) + return; + + mt76_clear(dev, MT_WPDMA_GLO_CFG, MT_WPDMA_GLO_CFG_TX_DMA_EN); + mt76_set(dev, MT_SCH_4, MT_SCH_4_RESET); + mt76_clear(dev, MT_SCH_4, MT_SCH_4_RESET); + mt76_set(dev, MT_WPDMA_GLO_CFG, MT_WPDMA_GLO_CFG_TX_DMA_EN); + + mt76_set(dev, MT_WF_CFG_OFF_WOCCR, MT_WF_CFG_OFF_WOCCR_TMAC_GC_DIS); + mt76_set(dev, MT_ARB_SCR, MT_ARB_SCR_TX_DISABLE); + mt76_clear(dev, MT_ARB_SCR, MT_ARB_SCR_TX_DISABLE); + mt76_clear(dev, MT_WF_CFG_OFF_WOCCR, MT_WF_CFG_OFF_WOCCR_TMAC_GC_DIS); +} + +static void +mt7603_update_beacon_iter(void *priv, u8 *mac, struct ieee80211_vif *vif) +{ + struct mt7603_dev *dev = (struct mt7603_dev *)priv; + struct mt76_dev *mdev = &dev->mt76; + struct mt7603_vif *mvif = (struct mt7603_vif *)vif->drv_priv; + struct sk_buff *skb = NULL; + u32 om_idx = mvif->idx; + u32 val; + + if (!(mdev->beacon_mask & BIT(mvif->idx))) + return; + + skb = ieee80211_beacon_get(mt76_hw(dev), vif, 0); + if (!skb) + return; + + if (om_idx) + om_idx |= 0x10; + val = MT_DMA_FQCR0_BUSY | MT_DMA_FQCR0_MODE | + FIELD_PREP(MT_DMA_FQCR0_TARGET_BSS, om_idx) | + FIELD_PREP(MT_DMA_FQCR0_DEST_PORT_ID, 3) | + FIELD_PREP(MT_DMA_FQCR0_DEST_QUEUE_ID, 8); + + spin_lock_bh(&dev->ps_lock); + + mt76_wr(dev, MT_DMA_FQCR0, val | + FIELD_PREP(MT_DMA_FQCR0_TARGET_QID, MT_TX_HW_QUEUE_BCN)); + if (!mt76_poll(dev, MT_DMA_FQCR0, MT_DMA_FQCR0_BUSY, 0, 5000)) { + dev->beacon_check = MT7603_WATCHDOG_TIMEOUT; + dev_kfree_skb(skb); + goto out; + } + + mt76_wr(dev, MT_DMA_FQCR0, val | + FIELD_PREP(MT_DMA_FQCR0_TARGET_QID, MT_TX_HW_QUEUE_BMC)); + if (!mt76_poll(dev, MT_DMA_FQCR0, MT_DMA_FQCR0_BUSY, 0, 5000)) { + dev->beacon_check = MT7603_WATCHDOG_TIMEOUT; + dev_kfree_skb(skb); + goto out; + } + + mt76_tx_queue_skb(dev, dev->mphy.q_tx[MT_TXQ_BEACON], + MT_TXQ_BEACON, skb, &mvif->sta.wcid, NULL); + +out: + spin_unlock_bh(&dev->ps_lock); +} + +static void +mt7603_add_buffered_bc(void *priv, u8 *mac, struct ieee80211_vif *vif) +{ + struct beacon_bc_data *data = priv; + struct mt7603_dev *dev = data->dev; + struct mt7603_vif *mvif = (struct mt7603_vif *)vif->drv_priv; + struct ieee80211_tx_info *info; + struct sk_buff *skb; + + if (!(dev->mt76.beacon_mask & BIT(mvif->idx))) + return; + + skb = ieee80211_get_buffered_bc(mt76_hw(dev), vif); + if (!skb) + return; + + info = IEEE80211_SKB_CB(skb); + info->control.vif = vif; + info->flags |= IEEE80211_TX_CTL_ASSIGN_SEQ; + mt76_skb_set_moredata(skb, true); + __skb_queue_tail(&data->q, skb); + data->tail[mvif->idx] = skb; + data->count[mvif->idx]++; +} + +void mt7603_pre_tbtt_tasklet(struct tasklet_struct *t) +{ + struct mt7603_dev *dev = from_tasklet(dev, t, mt76.pre_tbtt_tasklet); + struct mt76_dev *mdev = &dev->mt76; + struct mt76_queue *q; + struct beacon_bc_data data = {}; + struct sk_buff *skb; + int i, nframes; + + if (dev->mphy.offchannel) + return; + + data.dev = dev; + __skb_queue_head_init(&data.q); + + /* Flush all previous CAB queue packets and beacons */ + mt76_wr(dev, MT_WF_ARB_CAB_FLUSH, GENMASK(30, 16) | BIT(0)); + + mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[MT_TXQ_CAB], false); + mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[MT_TXQ_BEACON], false); + + if (dev->mphy.q_tx[MT_TXQ_BEACON]->queued > 0) + dev->beacon_check++; + else + dev->beacon_check = 0; + mt7603_mac_stuck_beacon_recovery(dev); + + q = dev->mphy.q_tx[MT_TXQ_BEACON]; + spin_lock(&q->lock); + ieee80211_iterate_active_interfaces_atomic(mt76_hw(dev), + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7603_update_beacon_iter, dev); + mt76_queue_kick(dev, q); + spin_unlock(&q->lock); + + mt76_csa_check(mdev); + if (mdev->csa_complete) + return; + + q = dev->mphy.q_tx[MT_TXQ_CAB]; + do { + nframes = skb_queue_len(&data.q); + ieee80211_iterate_active_interfaces_atomic(mt76_hw(dev), + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7603_add_buffered_bc, &data); + } while (nframes != skb_queue_len(&data.q) && + skb_queue_len(&data.q) < 8); + + if (skb_queue_empty(&data.q)) + return; + + for (i = 0; i < ARRAY_SIZE(data.tail); i++) { + if (!data.tail[i]) + continue; + + mt76_skb_set_moredata(data.tail[i], false); + } + + spin_lock(&q->lock); + while ((skb = __skb_dequeue(&data.q)) != NULL) { + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_vif *vif = info->control.vif; + struct mt7603_vif *mvif = (struct mt7603_vif *)vif->drv_priv; + + mt76_tx_queue_skb(dev, q, MT_TXQ_CAB, skb, &mvif->sta.wcid, NULL); + } + mt76_queue_kick(dev, q); + spin_unlock(&q->lock); + + for (i = 0; i < ARRAY_SIZE(data.count); i++) + mt76_wr(dev, MT_WF_ARB_CAB_COUNT_B0_REG(i), + data.count[i] << MT_WF_ARB_CAB_COUNT_B0_SHIFT(i)); + + mt76_wr(dev, MT_WF_ARB_CAB_START, + MT_WF_ARB_CAB_START_BSSn(0) | + (MT_WF_ARB_CAB_START_BSS0n(1) * + ((1 << (MT7603_MAX_INTERFACES - 1)) - 1))); +} + +void mt7603_beacon_set_timer(struct mt7603_dev *dev, int idx, int intval) +{ + u32 pre_tbtt = MT7603_PRE_TBTT_TIME / 64; + + if (idx >= 0) { + if (intval) + dev->mt76.beacon_mask |= BIT(idx); + else + dev->mt76.beacon_mask &= ~BIT(idx); + } + + if (!dev->mt76.beacon_mask || (!intval && idx < 0)) { + mt7603_irq_disable(dev, MT_INT_MAC_IRQ3); + mt76_clear(dev, MT_ARB_SCR, MT_ARB_SCR_BCNQ_OPMODE_MASK); + mt76_wr(dev, MT_HW_INT_MASK(3), 0); + return; + } + + if (intval) + dev->mt76.beacon_int = intval; + mt76_wr(dev, MT_TBTT, + FIELD_PREP(MT_TBTT_PERIOD, intval) | MT_TBTT_CAL_ENABLE); + + mt76_wr(dev, MT_TBTT_TIMER_CFG, 0x99); /* start timer */ + + mt76_rmw_field(dev, MT_ARB_SCR, MT_ARB_SCR_BCNQ_OPMODE_MASK, + MT_BCNQ_OPMODE_AP); + mt76_clear(dev, MT_ARB_SCR, MT_ARB_SCR_TBTT_BCN_PRIO); + mt76_set(dev, MT_ARB_SCR, MT_ARB_SCR_TBTT_BCAST_PRIO); + + mt76_wr(dev, MT_PRE_TBTT, pre_tbtt); + + mt76_set(dev, MT_HW_INT_MASK(3), + MT_HW_INT3_PRE_TBTT0 | MT_HW_INT3_TBTT0); + + mt76_set(dev, MT_WF_ARB_BCN_START, + MT_WF_ARB_BCN_START_BSSn(0) | + ((dev->mt76.beacon_mask >> 1) * + MT_WF_ARB_BCN_START_BSS0n(1))); + mt7603_irq_enable(dev, MT_INT_MAC_IRQ3); + + if (dev->mt76.beacon_mask & ~BIT(0)) + mt76_set(dev, MT_LPON_SBTOR(0), MT_LPON_SBTOR_SUB_BSS_EN); + else + mt76_clear(dev, MT_LPON_SBTOR(0), MT_LPON_SBTOR_SUB_BSS_EN); +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/core.c b/drivers/net/wireless/mediatek/mt76/mt7603/core.c new file mode 100644 index 000000000..9c2943fd9 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7603/core.c @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear + +#include "mt7603.h" +#include "../trace.h" + +void mt7603_rx_poll_complete(struct mt76_dev *mdev, enum mt76_rxq_id q) +{ + struct mt7603_dev *dev = container_of(mdev, struct mt7603_dev, mt76); + + mt7603_irq_enable(dev, MT_INT_RX_DONE(q)); +} + +irqreturn_t mt7603_irq_handler(int irq, void *dev_instance) +{ + struct mt7603_dev *dev = dev_instance; + u32 intr; + + intr = mt76_rr(dev, MT_INT_SOURCE_CSR); + mt76_wr(dev, MT_INT_SOURCE_CSR, intr); + + if (!test_bit(MT76_STATE_INITIALIZED, &dev->mphy.state)) + return IRQ_NONE; + + trace_dev_irq(&dev->mt76, intr, dev->mt76.mmio.irqmask); + + intr &= dev->mt76.mmio.irqmask; + + if (intr & MT_INT_MAC_IRQ3) { + u32 hwintr = mt76_rr(dev, MT_HW_INT_STATUS(3)); + + mt76_wr(dev, MT_HW_INT_STATUS(3), hwintr); + if (hwintr & MT_HW_INT3_PRE_TBTT0) + tasklet_schedule(&dev->mt76.pre_tbtt_tasklet); + + if ((hwintr & MT_HW_INT3_TBTT0) && dev->mt76.csa_complete) + mt76_csa_finish(&dev->mt76); + } + + if (intr & MT_INT_TX_DONE_ALL) { + mt7603_irq_disable(dev, MT_INT_TX_DONE_ALL); + napi_schedule(&dev->mt76.tx_napi); + } + + if (intr & MT_INT_RX_DONE(0)) { + dev->rx_pse_check = 0; + mt7603_irq_disable(dev, MT_INT_RX_DONE(0)); + napi_schedule(&dev->mt76.napi[0]); + } + + if (intr & MT_INT_RX_DONE(1)) { + dev->rx_pse_check = 0; + mt7603_irq_disable(dev, MT_INT_RX_DONE(1)); + napi_schedule(&dev->mt76.napi[1]); + } + + return IRQ_HANDLED; +} + +u32 mt7603_reg_map(struct mt7603_dev *dev, u32 addr) +{ + u32 base = addr & MT_MCU_PCIE_REMAP_2_BASE; + u32 offset = addr & MT_MCU_PCIE_REMAP_2_OFFSET; + + dev->bus_ops->wr(&dev->mt76, MT_MCU_PCIE_REMAP_2, base); + + return MT_PCIE_REMAP_BASE_2 + offset; +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7603/debugfs.c new file mode 100644 index 000000000..c891ad549 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7603/debugfs.c @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear + +#include "mt7603.h" + +static int +mt7603_reset_read(struct seq_file *s, void *data) +{ + struct mt7603_dev *dev = dev_get_drvdata(s->private); + static const char * const reset_cause_str[] = { + [RESET_CAUSE_TX_HANG] = "TX hang", + [RESET_CAUSE_TX_BUSY] = "TX DMA busy stuck", + [RESET_CAUSE_RX_BUSY] = "RX DMA busy stuck", + [RESET_CAUSE_RX_PSE_BUSY] = "RX PSE busy stuck", + [RESET_CAUSE_BEACON_STUCK] = "Beacon stuck", + [RESET_CAUSE_MCU_HANG] = "MCU hang", + [RESET_CAUSE_RESET_FAILED] = "PSE reset failed", + }; + int i; + + for (i = 0; i < ARRAY_SIZE(reset_cause_str); i++) { + if (!reset_cause_str[i]) + continue; + + seq_printf(s, "%20s: %u\n", reset_cause_str[i], + dev->reset_cause[i]); + } + + return 0; +} + +static int +mt7603_radio_read(struct seq_file *s, void *data) +{ + struct mt7603_dev *dev = dev_get_drvdata(s->private); + + seq_printf(s, "Sensitivity: %d\n", dev->sensitivity); + seq_printf(s, "False CCA: ofdm=%d cck=%d\n", + dev->false_cca_ofdm, dev->false_cca_cck); + + return 0; +} + +static int +mt7603_edcca_set(void *data, u64 val) +{ + struct mt7603_dev *dev = data; + + mutex_lock(&dev->mt76.mutex); + + dev->ed_monitor_enabled = !!val; + dev->ed_monitor = dev->ed_monitor_enabled && + dev->mt76.region == NL80211_DFS_ETSI; + mt7603_init_edcca(dev); + + mutex_unlock(&dev->mt76.mutex); + + return 0; +} + +static int +mt7603_edcca_get(void *data, u64 *val) +{ + struct mt7603_dev *dev = data; + + *val = dev->ed_monitor_enabled; + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_edcca, mt7603_edcca_get, + mt7603_edcca_set, "%lld\n"); + +static int +mt7603_ampdu_stat_show(struct seq_file *file, void *data) +{ + struct mt7603_dev *dev = file->private; + int bound[3], i, range; + + range = mt76_rr(dev, MT_AGG_ASRCR); + for (i = 0; i < ARRAY_SIZE(bound); i++) + bound[i] = MT_AGG_ASRCR_RANGE(range, i) + 1; + + seq_printf(file, "Length: %8d | ", bound[0]); + for (i = 0; i < ARRAY_SIZE(bound) - 1; i++) + seq_printf(file, "%3d -%3d | ", + bound[i], bound[i + 1]); + seq_puts(file, "\nCount: "); + for (i = 0; i < ARRAY_SIZE(bound); i++) + seq_printf(file, "%8d | ", dev->mphy.aggr_stats[i]); + seq_puts(file, "\n"); + + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(mt7603_ampdu_stat); + +void mt7603_init_debugfs(struct mt7603_dev *dev) +{ + struct dentry *dir; + + dir = mt76_register_debugfs(&dev->mt76); + if (!dir) + return; + + debugfs_create_file("ampdu_stat", 0400, dir, dev, + &mt7603_ampdu_stat_fops); + debugfs_create_devm_seqfile(dev->mt76.dev, "xmit-queues", dir, + mt76_queues_read); + debugfs_create_file("edcca", 0600, dir, dev, &fops_edcca); + debugfs_create_u32("reset_test", 0600, dir, &dev->reset_test); + debugfs_create_devm_seqfile(dev->mt76.dev, "reset", dir, + mt7603_reset_read); + debugfs_create_devm_seqfile(dev->mt76.dev, "radio", dir, + mt7603_radio_read); + debugfs_create_u8("sensitivity_limit", 0600, dir, + &dev->sensitivity_limit); + debugfs_create_bool("dynamic_sensitivity", 0600, dir, + &dev->dynamic_sensitivity); +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/dma.c b/drivers/net/wireless/mediatek/mt76/mt7603/dma.c new file mode 100644 index 000000000..477a9b7a8 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7603/dma.c @@ -0,0 +1,260 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear + +#include "mt7603.h" +#include "mac.h" +#include "../dma.h" + +static const u8 wmm_queue_map[] = { + [IEEE80211_AC_BK] = 0, + [IEEE80211_AC_BE] = 1, + [IEEE80211_AC_VI] = 2, + [IEEE80211_AC_VO] = 3, +}; + +static void +mt7603_rx_loopback_skb(struct mt7603_dev *dev, struct sk_buff *skb) +{ + static const u8 tid_to_ac[8] = { + IEEE80211_AC_BE, + IEEE80211_AC_BK, + IEEE80211_AC_BK, + IEEE80211_AC_BE, + IEEE80211_AC_VI, + IEEE80211_AC_VI, + IEEE80211_AC_VO, + IEEE80211_AC_VO + }; + __le32 *txd = (__le32 *)skb->data; + struct ieee80211_hdr *hdr; + struct ieee80211_sta *sta; + struct mt7603_sta *msta; + struct mt76_wcid *wcid; + u8 qid, tid = 0, hwq = 0; + void *priv; + int idx; + u32 val; + + if (skb->len < MT_TXD_SIZE + sizeof(struct ieee80211_hdr)) + goto free; + + val = le32_to_cpu(txd[1]); + idx = FIELD_GET(MT_TXD1_WLAN_IDX, val); + + if (idx >= MT7603_WTBL_STA - 1) + goto free; + + wcid = mt76_wcid_ptr(dev, idx); + if (!wcid) + goto free; + + priv = msta = container_of(wcid, struct mt7603_sta, wcid); + + sta = container_of(priv, struct ieee80211_sta, drv_priv); + hdr = (struct ieee80211_hdr *)&skb->data[MT_TXD_SIZE]; + + hwq = wmm_queue_map[IEEE80211_AC_BE]; + if (ieee80211_is_data_qos(hdr->frame_control)) { + tid = *ieee80211_get_qos_ctl(hdr) & + IEEE80211_QOS_CTL_TAG1D_MASK; + qid = tid_to_ac[tid]; + hwq = wmm_queue_map[qid]; + skb_set_queue_mapping(skb, qid); + } else if (ieee80211_is_data(hdr->frame_control)) { + skb_set_queue_mapping(skb, IEEE80211_AC_BE); + hwq = wmm_queue_map[IEEE80211_AC_BE]; + } else { + skb_pull(skb, MT_TXD_SIZE); + if (!ieee80211_is_bufferable_mmpdu(skb)) + goto free; + skb_push(skb, MT_TXD_SIZE); + skb_set_queue_mapping(skb, MT_TXQ_PSD); + hwq = MT_TX_HW_QUEUE_MGMT; + } + + skb->priority = tid; + ieee80211_sta_set_buffered(sta, tid, true); + + val = le32_to_cpu(txd[0]); + val &= ~(MT_TXD0_P_IDX | MT_TXD0_Q_IDX); + val |= FIELD_PREP(MT_TXD0_Q_IDX, hwq); + txd[0] = cpu_to_le32(val); + + spin_lock_bh(&dev->ps_lock); + __skb_queue_tail(&msta->psq, skb); + if (skb_queue_len(&msta->psq) >= 64) { + skb = __skb_dequeue(&msta->psq); + dev_kfree_skb(skb); + } + spin_unlock_bh(&dev->ps_lock); + return; + +free: + dev_kfree_skb(skb); +} + +void mt7603_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, + struct sk_buff *skb, u32 *info) +{ + struct mt7603_dev *dev = container_of(mdev, struct mt7603_dev, mt76); + __le32 *rxd = (__le32 *)skb->data; + __le32 *end = (__le32 *)&skb->data[skb->len]; + enum rx_pkt_type type; + + type = le32_get_bits(rxd[0], MT_RXD0_PKT_TYPE); + + if (q == MT_RXQ_MCU) { + if (type == PKT_TYPE_RX_EVENT) + mt76_mcu_rx_event(&dev->mt76, skb); + else + mt7603_rx_loopback_skb(dev, skb); + return; + } + + switch (type) { + case PKT_TYPE_TXS: + for (rxd++; rxd + 5 <= end; rxd += 5) + mt7603_mac_add_txs(dev, rxd); + dev_kfree_skb(skb); + break; + case PKT_TYPE_RX_EVENT: + mt76_mcu_rx_event(&dev->mt76, skb); + return; + case PKT_TYPE_NORMAL: + if (mt7603_mac_fill_rx(dev, skb) == 0) { + mt76_rx(&dev->mt76, q, skb); + return; + } + fallthrough; + default: + dev_kfree_skb(skb); + break; + } +} + +static int +mt7603_init_rx_queue(struct mt7603_dev *dev, struct mt76_queue *q, + int idx, int n_desc, int bufsize) +{ + int err; + + err = mt76_queue_alloc(dev, q, idx, n_desc, bufsize, + MT_RX_RING_BASE); + if (err < 0) + return err; + + mt7603_irq_enable(dev, MT_INT_RX_DONE(idx)); + + return 0; +} + +static int mt7603_poll_tx(struct napi_struct *napi, int budget) +{ + struct mt7603_dev *dev; + int i; + + dev = container_of(napi, struct mt7603_dev, mt76.tx_napi); + dev->tx_dma_check = 0; + + mt76_queue_tx_cleanup(dev, dev->mt76.q_mcu[MT_MCUQ_WM], false); + for (i = MT_TXQ_PSD; i >= 0; i--) + mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[i], false); + + if (napi_complete_done(napi, 0)) + mt7603_irq_enable(dev, MT_INT_TX_DONE_ALL); + + mt76_queue_tx_cleanup(dev, dev->mt76.q_mcu[MT_MCUQ_WM], false); + for (i = MT_TXQ_PSD; i >= 0; i--) + mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[i], false); + + mt7603_mac_sta_poll(dev); + + mt76_worker_schedule(&dev->mt76.tx_worker); + + return 0; +} + +int mt7603_dma_init(struct mt7603_dev *dev) +{ + int ret; + int i; + + mt76_dma_attach(&dev->mt76); + + mt76_clear(dev, MT_WPDMA_GLO_CFG, + MT_WPDMA_GLO_CFG_TX_DMA_EN | + MT_WPDMA_GLO_CFG_RX_DMA_EN | + MT_WPDMA_GLO_CFG_DMA_BURST_SIZE | + MT_WPDMA_GLO_CFG_TX_WRITEBACK_DONE); + + mt76_wr(dev, MT_WPDMA_RST_IDX, ~0); + mt7603_pse_client_reset(dev); + + for (i = 0; i < ARRAY_SIZE(wmm_queue_map); i++) { + ret = mt76_init_tx_queue(&dev->mphy, i, wmm_queue_map[i], + MT7603_TX_RING_SIZE, MT_TX_RING_BASE, + NULL, 0); + if (ret) + return ret; + } + + ret = mt76_init_tx_queue(&dev->mphy, MT_TXQ_PSD, MT_TX_HW_QUEUE_MGMT, + MT7603_PSD_RING_SIZE, MT_TX_RING_BASE, NULL, 0); + if (ret) + return ret; + + ret = mt76_init_mcu_queue(&dev->mt76, MT_MCUQ_WM, MT_TX_HW_QUEUE_MCU, + MT_MCU_RING_SIZE, MT_TX_RING_BASE); + if (ret) + return ret; + + ret = mt76_init_tx_queue(&dev->mphy, MT_TXQ_BEACON, MT_TX_HW_QUEUE_BCN, + MT_MCU_RING_SIZE, MT_TX_RING_BASE, NULL, 0); + if (ret) + return ret; + + ret = mt76_init_tx_queue(&dev->mphy, MT_TXQ_CAB, MT_TX_HW_QUEUE_BMC, + MT_MCU_RING_SIZE, MT_TX_RING_BASE, NULL, 0); + if (ret) + return ret; + + mt7603_irq_enable(dev, + MT_INT_TX_DONE(IEEE80211_AC_VO) | + MT_INT_TX_DONE(IEEE80211_AC_VI) | + MT_INT_TX_DONE(IEEE80211_AC_BE) | + MT_INT_TX_DONE(IEEE80211_AC_BK) | + MT_INT_TX_DONE(MT_TX_HW_QUEUE_MGMT) | + MT_INT_TX_DONE(MT_TX_HW_QUEUE_MCU) | + MT_INT_TX_DONE(MT_TX_HW_QUEUE_BCN) | + MT_INT_TX_DONE(MT_TX_HW_QUEUE_BMC)); + + ret = mt7603_init_rx_queue(dev, &dev->mt76.q_rx[MT_RXQ_MCU], 1, + MT7603_MCU_RX_RING_SIZE, MT_RX_BUF_SIZE); + if (ret) + return ret; + + ret = mt7603_init_rx_queue(dev, &dev->mt76.q_rx[MT_RXQ_MAIN], 0, + MT7603_RX_RING_SIZE, MT_RX_BUF_SIZE); + if (ret) + return ret; + + mt76_wr(dev, MT_DELAY_INT_CFG, 0); + ret = mt76_init_queues(dev, mt76_dma_rx_poll); + if (ret) + return ret; + + netif_napi_add_tx(dev->mt76.tx_napi_dev, &dev->mt76.tx_napi, + mt7603_poll_tx); + napi_enable(&dev->mt76.tx_napi); + + return 0; +} + +void mt7603_dma_cleanup(struct mt7603_dev *dev) +{ + mt76_clear(dev, MT_WPDMA_GLO_CFG, + MT_WPDMA_GLO_CFG_TX_DMA_EN | + MT_WPDMA_GLO_CFG_RX_DMA_EN | + MT_WPDMA_GLO_CFG_TX_WRITEBACK_DONE); + + mt76_dma_cleanup(&dev->mt76); +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt7603/eeprom.c new file mode 100644 index 000000000..05f7aaefa --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7603/eeprom.c @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear + +#include +#include "mt7603.h" +#include "eeprom.h" + +static int +mt7603_efuse_read(struct mt7603_dev *dev, u32 base, u16 addr, u8 *data) +{ + u32 val; + int i; + + val = mt76_rr(dev, base + MT_EFUSE_CTRL); + val &= ~(MT_EFUSE_CTRL_AIN | + MT_EFUSE_CTRL_MODE); + val |= FIELD_PREP(MT_EFUSE_CTRL_AIN, addr & ~0xf); + val |= MT_EFUSE_CTRL_KICK; + mt76_wr(dev, base + MT_EFUSE_CTRL, val); + + if (!mt76_poll(dev, base + MT_EFUSE_CTRL, MT_EFUSE_CTRL_KICK, 0, 1000)) + return -ETIMEDOUT; + + udelay(2); + + val = mt76_rr(dev, base + MT_EFUSE_CTRL); + if ((val & MT_EFUSE_CTRL_AOUT) == MT_EFUSE_CTRL_AOUT || + WARN_ON_ONCE(!(val & MT_EFUSE_CTRL_VALID))) { + memset(data, 0xff, 16); + return 0; + } + + for (i = 0; i < 4; i++) { + val = mt76_rr(dev, base + MT_EFUSE_RDATA(i)); + put_unaligned_le32(val, data + 4 * i); + } + + return 0; +} + +static int +mt7603_efuse_init(struct mt7603_dev *dev) +{ + u32 base = mt7603_reg_map(dev, MT_EFUSE_BASE); + int len = MT7603_EEPROM_SIZE; + void *buf; + int ret, i; + + if (mt76_rr(dev, base + MT_EFUSE_BASE_CTRL) & MT_EFUSE_BASE_CTRL_EMPTY) + return 0; + + dev->mt76.otp.data = devm_kzalloc(dev->mt76.dev, len, GFP_KERNEL); + dev->mt76.otp.size = len; + if (!dev->mt76.otp.data) + return -ENOMEM; + + buf = dev->mt76.otp.data; + for (i = 0; i + 16 <= len; i += 16) { + ret = mt7603_efuse_read(dev, base, i, buf + i); + if (ret) + return ret; + } + + return 0; +} + +static bool +mt7603_has_cal_free_data(struct mt7603_dev *dev, u8 *efuse) +{ + if (!efuse[MT_EE_TEMP_SENSOR_CAL]) + return false; + + if (get_unaligned_le16(efuse + MT_EE_TX_POWER_0_START_2G) == 0) + return false; + + if (get_unaligned_le16(efuse + MT_EE_TX_POWER_1_START_2G) == 0) + return false; + + if (!efuse[MT_EE_CP_FT_VERSION]) + return false; + + if (!efuse[MT_EE_XTAL_FREQ_OFFSET]) + return false; + + if (!efuse[MT_EE_XTAL_WF_RFCAL]) + return false; + + return true; +} + +static void +mt7603_apply_cal_free_data(struct mt7603_dev *dev, u8 *efuse) +{ + static const u8 cal_free_bytes[] = { + MT_EE_TEMP_SENSOR_CAL, + MT_EE_CP_FT_VERSION, + MT_EE_XTAL_FREQ_OFFSET, + MT_EE_XTAL_WF_RFCAL, + /* Skip for MT7628 */ + MT_EE_TX_POWER_0_START_2G, + MT_EE_TX_POWER_0_START_2G + 1, + MT_EE_TX_POWER_1_START_2G, + MT_EE_TX_POWER_1_START_2G + 1, + }; + struct device_node *np = dev->mt76.dev->of_node; + u8 *eeprom = dev->mt76.eeprom.data; + int n = ARRAY_SIZE(cal_free_bytes); + int i; + + if (!np || !of_property_read_bool(np, "mediatek,eeprom-merge-otp")) + return; + + if (!mt7603_has_cal_free_data(dev, efuse)) + return; + + if (is_mt7628(dev)) + n -= 4; + + for (i = 0; i < n; i++) { + int offset = cal_free_bytes[i]; + + eeprom[offset] = efuse[offset]; + } +} + +static int +mt7603_eeprom_load(struct mt7603_dev *dev) +{ + int ret; + + ret = mt76_eeprom_init(&dev->mt76, MT7603_EEPROM_SIZE); + if (ret < 0) + return ret; + + return mt7603_efuse_init(dev); +} + +static int mt7603_check_eeprom(struct mt76_dev *dev) +{ + u16 val = get_unaligned_le16(dev->eeprom.data); + + switch (val) { + case 0x7628: + case 0x7603: + case 0x7600: + case 0x7592: + return 0; + default: + return -EINVAL; + } +} + +static inline bool is_mt7688(struct mt7603_dev *dev) +{ + return mt76_rr(dev, MT_EFUSE_BASE + 0x64) & BIT(4); +} + +int mt7603_eeprom_init(struct mt7603_dev *dev) +{ + u8 *eeprom; + int ret; + + ret = mt7603_eeprom_load(dev); + if (ret < 0) + return ret; + + if (dev->mt76.otp.data) { + if (mt7603_check_eeprom(&dev->mt76) == 0) + mt7603_apply_cal_free_data(dev, dev->mt76.otp.data); + else + memcpy(dev->mt76.eeprom.data, dev->mt76.otp.data, + MT7603_EEPROM_SIZE); + } + + eeprom = (u8 *)dev->mt76.eeprom.data; + dev->mphy.cap.has_2ghz = true; + memcpy(dev->mphy.macaddr, eeprom + MT_EE_MAC_ADDR, ETH_ALEN); + + /* Check for 1SS devices */ + dev->mphy.antenna_mask = 3; + if (FIELD_GET(MT_EE_NIC_CONF_0_RX_PATH, eeprom[MT_EE_NIC_CONF_0]) == 1 || + FIELD_GET(MT_EE_NIC_CONF_0_TX_PATH, eeprom[MT_EE_NIC_CONF_0]) == 1 || + is_mt7688(dev)) + dev->mphy.antenna_mask = 1; + + dev->mphy.chainmask = dev->mphy.antenna_mask; + + return mt76_eeprom_override(&dev->mphy); +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/eeprom.h b/drivers/net/wireless/mediatek/mt76/mt7603/eeprom.h new file mode 100644 index 000000000..b6b746d1e --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7603/eeprom.h @@ -0,0 +1,91 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ + +#ifndef __MT7603_EEPROM_H +#define __MT7603_EEPROM_H + +#include "mt7603.h" + +enum mt7603_eeprom_field { + MT_EE_CHIP_ID = 0x000, + MT_EE_VERSION = 0x002, + MT_EE_MAC_ADDR = 0x004, + MT_EE_NIC_CONF_0 = 0x034, + MT_EE_NIC_CONF_1 = 0x036, + MT_EE_NIC_CONF_2 = 0x042, + + MT_EE_XTAL_TRIM_1 = 0x03a, + + MT_EE_RSSI_OFFSET_2G = 0x046, + MT_EE_WIFI_RF_SETTING = 0x048, + MT_EE_RSSI_OFFSET_5G = 0x04a, + + MT_EE_TX_POWER_DELTA_BW40 = 0x050, + MT_EE_TX_POWER_DELTA_BW80 = 0x052, + + MT_EE_TX_POWER_EXT_PA_5G = 0x054, + + MT_EE_TEMP_SENSOR_CAL = 0x055, + + MT_EE_TX_POWER_0_START_2G = 0x056, + MT_EE_TX_POWER_1_START_2G = 0x05c, + + /* used as byte arrays */ +#define MT_TX_POWER_GROUP_SIZE_5G 5 +#define MT_TX_POWER_GROUPS_5G 6 + MT_EE_TX_POWER_0_START_5G = 0x062, + + MT_EE_TX_POWER_0_GRP3_TX_POWER_DELTA = 0x074, + MT_EE_TX_POWER_0_GRP4_TSSI_SLOPE = 0x076, + + MT_EE_TX_POWER_1_START_5G = 0x080, + + MT_EE_TX_POWER_CCK = 0x0a0, + MT_EE_TX_POWER_OFDM_2G_6M = 0x0a2, + MT_EE_TX_POWER_OFDM_2G_24M = 0x0a4, + MT_EE_TX_POWER_OFDM_2G_54M = 0x0a6, + MT_EE_TX_POWER_HT_BPSK_QPSK = 0x0a8, + MT_EE_TX_POWER_HT_16_64_QAM = 0x0aa, + MT_EE_TX_POWER_HT_64_QAM = 0x0ac, + + MT_EE_ELAN_RX_MODE_GAIN = 0x0c0, + MT_EE_ELAN_RX_MODE_NF = 0x0c1, + MT_EE_ELAN_RX_MODE_P1DB = 0x0c2, + + MT_EE_ELAN_BYPASS_MODE_GAIN = 0x0c3, + MT_EE_ELAN_BYPASS_MODE_NF = 0x0c4, + MT_EE_ELAN_BYPASS_MODE_P1DB = 0x0c5, + + MT_EE_STEP_NUM_NEG_6_7 = 0x0c6, + MT_EE_STEP_NUM_NEG_4_5 = 0x0c8, + MT_EE_STEP_NUM_NEG_2_3 = 0x0ca, + MT_EE_STEP_NUM_NEG_0_1 = 0x0cc, + + MT_EE_REF_STEP_24G = 0x0ce, + + MT_EE_STEP_NUM_PLUS_1_2 = 0x0d0, + MT_EE_STEP_NUM_PLUS_3_4 = 0x0d2, + MT_EE_STEP_NUM_PLUS_5_6 = 0x0d4, + MT_EE_STEP_NUM_PLUS_7 = 0x0d6, + + MT_EE_CP_FT_VERSION = 0x0f0, + + MT_EE_TX_POWER_TSSI_OFF = 0x0f2, + + MT_EE_XTAL_FREQ_OFFSET = 0x0f4, + MT_EE_XTAL_TRIM_2_COMP = 0x0f5, + MT_EE_XTAL_TRIM_3_COMP = 0x0f6, + MT_EE_XTAL_WF_RFCAL = 0x0f7, + + __MT_EE_MAX +}; + +enum mt7603_eeprom_source { + MT_EE_SRC_PROM, + MT_EE_SRC_EFUSE, + MT_EE_SRC_FLASH, +}; + +#define MT_EE_NIC_CONF_0_RX_PATH GENMASK(3, 0) +#define MT_EE_NIC_CONF_0_TX_PATH GENMASK(7, 4) + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/init.c b/drivers/net/wireless/mediatek/mt76/mt7603/init.c new file mode 100644 index 000000000..10f2ec70c --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7603/init.c @@ -0,0 +1,571 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear + +#include +#include "mt7603.h" +#include "mac.h" +#include "eeprom.h" + +const struct mt76_driver_ops mt7603_drv_ops = { + .txwi_size = MT_TXD_SIZE, + .drv_flags = MT_DRV_SW_RX_AIRTIME, + .survey_flags = SURVEY_INFO_TIME_TX, + .tx_prepare_skb = mt7603_tx_prepare_skb, + .tx_complete_skb = mt7603_tx_complete_skb, + .rx_skb = mt7603_queue_rx_skb, + .rx_poll_complete = mt7603_rx_poll_complete, + .sta_ps = mt7603_sta_ps, + .sta_add = mt7603_sta_add, + .sta_event = mt7603_sta_event, + .sta_remove = mt7603_sta_remove, + .update_survey = mt7603_update_channel, + .set_channel = mt7603_set_channel, +}; + +static void +mt7603_set_tmac_template(struct mt7603_dev *dev) +{ + u32 desc[5] = { + [1] = FIELD_PREP(MT_TXD3_REM_TX_COUNT, 0xf), + [3] = MT_TXD5_SW_POWER_MGMT + }; + u32 addr; + int i; + + addr = mt7603_reg_map(dev, MT_CLIENT_BASE_PHYS_ADDR); + addr += MT_CLIENT_TMAC_INFO_TEMPLATE; + for (i = 0; i < ARRAY_SIZE(desc); i++) + mt76_wr(dev, addr + 4 * i, desc[i]); +} + +static void +mt7603_dma_sched_init(struct mt7603_dev *dev) +{ + int page_size = 128; + int page_count; + int max_len = 1792; + int max_amsdu_pages = 4096 / page_size; + int max_mcu_len = 4096; + int max_beacon_len = 512 * 4 + max_len; + int max_mcast_pages = 4 * max_len / page_size; + int reserved_count = 0; + int beacon_pages; + int mcu_pages; + int i; + + page_count = mt76_get_field(dev, MT_PSE_FC_P0, + MT_PSE_FC_P0_MAX_QUOTA); + beacon_pages = 4 * (max_beacon_len / page_size); + mcu_pages = max_mcu_len / page_size; + + mt76_wr(dev, MT_PSE_FRP, + FIELD_PREP(MT_PSE_FRP_P0, 7) | + FIELD_PREP(MT_PSE_FRP_P1, 6) | + FIELD_PREP(MT_PSE_FRP_P2_RQ2, 4)); + + mt76_wr(dev, MT_HIGH_PRIORITY_1, 0x55555553); + mt76_wr(dev, MT_HIGH_PRIORITY_2, 0x78555555); + + mt76_wr(dev, MT_QUEUE_PRIORITY_1, 0x2b1a096e); + mt76_wr(dev, MT_QUEUE_PRIORITY_2, 0x785f4d3c); + + mt76_wr(dev, MT_PRIORITY_MASK, 0xffffffff); + + mt76_wr(dev, MT_SCH_1, page_count | (2 << 28)); + mt76_wr(dev, MT_SCH_2, max_amsdu_pages); + + for (i = 0; i <= 4; i++) + mt76_wr(dev, MT_PAGE_COUNT(i), max_amsdu_pages); + reserved_count += 5 * max_amsdu_pages; + + mt76_wr(dev, MT_PAGE_COUNT(5), mcu_pages); + reserved_count += mcu_pages; + + mt76_wr(dev, MT_PAGE_COUNT(7), beacon_pages); + reserved_count += beacon_pages; + + mt76_wr(dev, MT_PAGE_COUNT(8), max_mcast_pages); + reserved_count += max_mcast_pages; + + if (is_mt7603(dev)) + reserved_count = 0; + + mt76_wr(dev, MT_RSV_MAX_THRESH, page_count - reserved_count); + + if (is_mt7603(dev) && mt76xx_rev(dev) >= MT7603_REV_E2) { + mt76_wr(dev, MT_GROUP_THRESH(0), + page_count - beacon_pages - mcu_pages); + mt76_wr(dev, MT_GROUP_THRESH(1), beacon_pages); + mt76_wr(dev, MT_BMAP_0, 0x0080ff5f); + mt76_wr(dev, MT_GROUP_THRESH(2), mcu_pages); + mt76_wr(dev, MT_BMAP_1, 0x00000020); + } else { + mt76_wr(dev, MT_GROUP_THRESH(0), page_count); + mt76_wr(dev, MT_BMAP_0, 0xffff); + } + + mt76_wr(dev, MT_SCH_4, 0); + + for (i = 0; i <= 15; i++) + mt76_wr(dev, MT_TXTIME_THRESH(i), 0xfffff); + + mt76_set(dev, MT_SCH_4, BIT(6)); +} + +static void +mt7603_phy_init(struct mt7603_dev *dev) +{ + int rx_chains = dev->mphy.antenna_mask; + int tx_chains = hweight8(rx_chains) - 1; + + mt76_rmw(dev, MT_WF_RMAC_RMCR, + (MT_WF_RMAC_RMCR_SMPS_MODE | + MT_WF_RMAC_RMCR_RX_STREAMS), + (FIELD_PREP(MT_WF_RMAC_RMCR_SMPS_MODE, 3) | + FIELD_PREP(MT_WF_RMAC_RMCR_RX_STREAMS, rx_chains))); + + mt76_rmw_field(dev, MT_TMAC_TCR, MT_TMAC_TCR_TX_STREAMS, + tx_chains); + + dev->agc0 = mt76_rr(dev, MT_AGC(0)); + dev->agc3 = mt76_rr(dev, MT_AGC(3)); +} + +static void +mt7603_mac_init(struct mt7603_dev *dev) +{ + u8 bc_addr[ETH_ALEN]; + u32 addr; + int i; + + mt76_wr(dev, MT_AGG_BA_SIZE_LIMIT_0, + (MT_AGG_SIZE_LIMIT(0) << 0 * MT_AGG_BA_SIZE_LIMIT_SHIFT) | + (MT_AGG_SIZE_LIMIT(1) << 1 * MT_AGG_BA_SIZE_LIMIT_SHIFT) | + (MT_AGG_SIZE_LIMIT(2) << 2 * MT_AGG_BA_SIZE_LIMIT_SHIFT) | + (MT_AGG_SIZE_LIMIT(3) << 3 * MT_AGG_BA_SIZE_LIMIT_SHIFT)); + + mt76_wr(dev, MT_AGG_BA_SIZE_LIMIT_1, + (MT_AGG_SIZE_LIMIT(4) << 0 * MT_AGG_BA_SIZE_LIMIT_SHIFT) | + (MT_AGG_SIZE_LIMIT(5) << 1 * MT_AGG_BA_SIZE_LIMIT_SHIFT) | + (MT_AGG_SIZE_LIMIT(6) << 2 * MT_AGG_BA_SIZE_LIMIT_SHIFT) | + (MT_AGG_SIZE_LIMIT(7) << 3 * MT_AGG_BA_SIZE_LIMIT_SHIFT)); + + mt76_wr(dev, MT_AGG_LIMIT, + FIELD_PREP(MT_AGG_LIMIT_AC(0), 24) | + FIELD_PREP(MT_AGG_LIMIT_AC(1), 24) | + FIELD_PREP(MT_AGG_LIMIT_AC(2), 24) | + FIELD_PREP(MT_AGG_LIMIT_AC(3), 24)); + + mt76_wr(dev, MT_AGG_LIMIT_1, + FIELD_PREP(MT_AGG_LIMIT_AC(0), 24) | + FIELD_PREP(MT_AGG_LIMIT_AC(1), 24) | + FIELD_PREP(MT_AGG_LIMIT_AC(2), 24) | + FIELD_PREP(MT_AGG_LIMIT_AC(3), 24)); + + mt76_wr(dev, MT_AGG_CONTROL, + FIELD_PREP(MT_AGG_CONTROL_BAR_RATE, 0x4b) | + FIELD_PREP(MT_AGG_CONTROL_CFEND_RATE, 0x69) | + MT_AGG_CONTROL_NO_BA_AR_RULE); + + mt76_wr(dev, MT_AGG_RETRY_CONTROL, + FIELD_PREP(MT_AGG_RETRY_CONTROL_BAR_LIMIT, 1) | + FIELD_PREP(MT_AGG_RETRY_CONTROL_RTS_LIMIT, 15)); + + mt76_wr(dev, MT_DMA_DCR0, MT_DMA_DCR0_RX_VEC_DROP | + FIELD_PREP(MT_DMA_DCR0_MAX_RX_LEN, 4096)); + + mt76_rmw(dev, MT_DMA_VCFR0, BIT(0), BIT(13)); + mt76_rmw(dev, MT_DMA_TMCFR0, BIT(0) | BIT(1), BIT(13)); + + mt76_clear(dev, MT_WF_RMAC_TMR_PA, BIT(31)); + + mt76_set(dev, MT_WF_RMACDR, MT_WF_RMACDR_MAXLEN_20BIT); + mt76_rmw(dev, MT_WF_RMAC_MAXMINLEN, 0xffffff, 0x19000); + + mt76_wr(dev, MT_WF_RFCR1, 0); + + mt76_set(dev, MT_TMAC_TCR, MT_TMAC_TCR_RX_RIFS_MODE); + + if (is_mt7628(dev)) { + mt76_set(dev, MT_TMAC_TCR, + MT_TMAC_TCR_TXOP_BURST_STOP | BIT(1) | BIT(0)); + mt76_set(dev, MT_TXREQ, BIT(27)); + mt76_set(dev, MT_AGG_TMP, GENMASK(4, 2)); + } + + mt7603_set_tmac_template(dev); + + /* Enable RX group to HIF */ + addr = mt7603_reg_map(dev, MT_CLIENT_BASE_PHYS_ADDR); + mt76_set(dev, addr + MT_CLIENT_RXINF, MT_CLIENT_RXINF_RXSH_GROUPS); + + /* Enable RX group to MCU */ + mt76_set(dev, MT_DMA_DCR1, GENMASK(13, 11)); + + mt76_rmw_field(dev, MT_AGG_PCR_RTS, MT_AGG_PCR_RTS_PKT_THR, 3); + mt76_set(dev, MT_TMAC_PCR, MT_TMAC_PCR_SPE_EN); + + /* include preamble detection in CCA trigger signal */ + mt76_rmw_field(dev, MT_TXREQ, MT_TXREQ_CCA_SRC_SEL, 2); + + mt76_wr(dev, MT_RXREQ, 4); + + /* Configure all rx packets to HIF */ + mt76_wr(dev, MT_DMA_RCFR0, 0xc0000000); + + /* Configure MCU txs selection with aggregation */ + mt76_wr(dev, MT_DMA_TCFR0, + FIELD_PREP(MT_DMA_TCFR_TXS_AGGR_TIMEOUT, 1) | /* 32 us */ + MT_DMA_TCFR_TXS_AGGR_COUNT); + + /* Configure HIF txs selection with aggregation */ + mt76_wr(dev, MT_DMA_TCFR1, + FIELD_PREP(MT_DMA_TCFR_TXS_AGGR_TIMEOUT, 1) | /* 32 us */ + MT_DMA_TCFR_TXS_AGGR_COUNT | /* Maximum count */ + MT_DMA_TCFR_TXS_BIT_MAP); + + mt76_wr(dev, MT_MCU_PCIE_REMAP_1, MT_PSE_WTBL_2_PHYS_ADDR); + + for (i = 0; i < MT7603_WTBL_SIZE; i++) + mt7603_wtbl_clear(dev, i); + + eth_broadcast_addr(bc_addr); + mt7603_wtbl_init(dev, MT7603_WTBL_RESERVED, -1, bc_addr); + dev->global_sta.wcid.idx = MT7603_WTBL_RESERVED; + rcu_assign_pointer(dev->mt76.wcid[MT7603_WTBL_RESERVED], + &dev->global_sta.wcid); + + mt76_rmw_field(dev, MT_LPON_BTEIR, MT_LPON_BTEIR_MBSS_MODE, 2); + mt76_rmw_field(dev, MT_WF_RMACDR, MT_WF_RMACDR_MBSSID_MASK, 2); + + mt76_wr(dev, MT_AGG_ARUCR, + FIELD_PREP(MT_AGG_ARxCR_LIMIT(0), 7) | + FIELD_PREP(MT_AGG_ARxCR_LIMIT(1), 2) | + FIELD_PREP(MT_AGG_ARxCR_LIMIT(2), 2) | + FIELD_PREP(MT_AGG_ARxCR_LIMIT(3), 2) | + FIELD_PREP(MT_AGG_ARxCR_LIMIT(4), 1) | + FIELD_PREP(MT_AGG_ARxCR_LIMIT(5), 1) | + FIELD_PREP(MT_AGG_ARxCR_LIMIT(6), 1) | + FIELD_PREP(MT_AGG_ARxCR_LIMIT(7), 1)); + + mt76_wr(dev, MT_AGG_ARDCR, + FIELD_PREP(MT_AGG_ARxCR_LIMIT(0), MT7603_RATE_RETRY - 1) | + FIELD_PREP(MT_AGG_ARxCR_LIMIT(1), MT7603_RATE_RETRY - 1) | + FIELD_PREP(MT_AGG_ARxCR_LIMIT(2), MT7603_RATE_RETRY - 1) | + FIELD_PREP(MT_AGG_ARxCR_LIMIT(3), MT7603_RATE_RETRY - 1) | + FIELD_PREP(MT_AGG_ARxCR_LIMIT(4), MT7603_RATE_RETRY - 1) | + FIELD_PREP(MT_AGG_ARxCR_LIMIT(5), MT7603_RATE_RETRY - 1) | + FIELD_PREP(MT_AGG_ARxCR_LIMIT(6), MT7603_RATE_RETRY - 1) | + FIELD_PREP(MT_AGG_ARxCR_LIMIT(7), MT7603_RATE_RETRY - 1)); + + mt76_wr(dev, MT_AGG_ARCR, + (FIELD_PREP(MT_AGG_ARCR_RTS_RATE_THR, 2) | + MT_AGG_ARCR_RATE_DOWN_RATIO_EN | + FIELD_PREP(MT_AGG_ARCR_RATE_DOWN_RATIO, 1) | + FIELD_PREP(MT_AGG_ARCR_RATE_UP_EXTRA_TH, 4))); + + mt76_set(dev, MT_WTBL_RMVTCR, MT_WTBL_RMVTCR_RX_MV_MODE); + + mt76_clear(dev, MT_SEC_SCR, MT_SEC_SCR_MASK_ORDER); + mt76_clear(dev, MT_SEC_SCR, BIT(18)); + + /* Set secondary beacon time offsets */ + for (i = 0; i <= 4; i++) + mt76_rmw_field(dev, MT_LPON_SBTOR(i), MT_LPON_SBTOR_TIME_OFFSET, + (i + 1) * (20 + 4096)); +} + +static int +mt7603_init_hardware(struct mt7603_dev *dev) +{ + int i, ret; + + mt76_wr(dev, MT_INT_SOURCE_CSR, ~0); + + ret = mt7603_eeprom_init(dev); + if (ret < 0) + return ret; + + ret = mt7603_dma_init(dev); + if (ret) + return ret; + + mt76_wr(dev, MT_WPDMA_GLO_CFG, 0x52000850); + mt7603_mac_dma_start(dev); + dev->rxfilter = mt76_rr(dev, MT_WF_RFCR); + set_bit(MT76_STATE_INITIALIZED, &dev->mphy.state); + + for (i = 0; i < MT7603_WTBL_SIZE; i++) { + mt76_wr(dev, MT_PSE_RTA, MT_PSE_RTA_BUSY | MT_PSE_RTA_WRITE | + FIELD_PREP(MT_PSE_RTA_TAG_ID, i)); + mt76_poll(dev, MT_PSE_RTA, MT_PSE_RTA_BUSY, 0, 5000); + } + + ret = mt7603_mcu_init(dev); + if (ret) + return ret; + + mt7603_dma_sched_init(dev); + mt7603_mcu_set_eeprom(dev); + mt7603_phy_init(dev); + mt7603_mac_init(dev); + + return 0; +} + +static const struct ieee80211_iface_limit if_limits[] = { + { + .max = 1, + .types = BIT(NL80211_IFTYPE_ADHOC) + }, { + .max = MT7603_MAX_INTERFACES, + .types = BIT(NL80211_IFTYPE_STATION) | +#ifdef CONFIG_MAC80211_MESH + BIT(NL80211_IFTYPE_MESH_POINT) | +#endif + BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_P2P_GO) | + BIT(NL80211_IFTYPE_AP) + }, +}; + +static const struct ieee80211_iface_combination if_comb[] = { + { + .limits = if_limits, + .n_limits = ARRAY_SIZE(if_limits), + .max_interfaces = 4, + .num_different_channels = 1, + .beacon_int_infra_match = true, + } +}; + +static void mt7603_led_set_config(struct mt76_phy *mphy, u8 delay_on, + u8 delay_off) +{ + struct mt7603_dev *dev = container_of(mphy->dev, struct mt7603_dev, + mt76); + u32 val, addr; + + val = FIELD_PREP(MT_LED_STATUS_DURATION, 0xffff) | + FIELD_PREP(MT_LED_STATUS_OFF, delay_off) | + FIELD_PREP(MT_LED_STATUS_ON, delay_on); + + addr = mt7603_reg_map(dev, MT_LED_STATUS_0(mphy->leds.pin)); + mt76_wr(dev, addr, val); + addr = mt7603_reg_map(dev, MT_LED_STATUS_1(mphy->leds.pin)); + mt76_wr(dev, addr, val); + + val = MT_LED_CTRL_REPLAY(mphy->leds.pin) | + MT_LED_CTRL_KICK(mphy->leds.pin); + if (mphy->leds.al) + val |= MT_LED_CTRL_POLARITY(mphy->leds.pin); + addr = mt7603_reg_map(dev, MT_LED_CTRL); + mt76_wr(dev, addr, val); +} + +static int mt7603_led_set_blink(struct led_classdev *led_cdev, + unsigned long *delay_on, + unsigned long *delay_off) +{ + struct mt76_phy *mphy = container_of(led_cdev, struct mt76_phy, + leds.cdev); + u8 delta_on, delta_off; + + delta_off = max_t(u8, *delay_off / 10, 1); + delta_on = max_t(u8, *delay_on / 10, 1); + + mt7603_led_set_config(mphy, delta_on, delta_off); + return 0; +} + +static void mt7603_led_set_brightness(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct mt76_phy *mphy = container_of(led_cdev, struct mt76_phy, + leds.cdev); + + if (!brightness) + mt7603_led_set_config(mphy, 0, 0xff); + else + mt7603_led_set_config(mphy, 0xff, 0); +} + +static u32 __mt7603_reg_addr(struct mt7603_dev *dev, u32 addr) +{ + if (addr < 0x100000) + return addr; + + return mt7603_reg_map(dev, addr); +} + +static u32 mt7603_rr(struct mt76_dev *mdev, u32 offset) +{ + struct mt7603_dev *dev = container_of(mdev, struct mt7603_dev, mt76); + u32 addr = __mt7603_reg_addr(dev, offset); + + return dev->bus_ops->rr(mdev, addr); +} + +static void mt7603_wr(struct mt76_dev *mdev, u32 offset, u32 val) +{ + struct mt7603_dev *dev = container_of(mdev, struct mt7603_dev, mt76); + u32 addr = __mt7603_reg_addr(dev, offset); + + dev->bus_ops->wr(mdev, addr, val); +} + +static u32 mt7603_rmw(struct mt76_dev *mdev, u32 offset, u32 mask, u32 val) +{ + struct mt7603_dev *dev = container_of(mdev, struct mt7603_dev, mt76); + u32 addr = __mt7603_reg_addr(dev, offset); + + return dev->bus_ops->rmw(mdev, addr, mask, val); +} + +static void +mt7603_regd_notifier(struct wiphy *wiphy, + struct regulatory_request *request) +{ + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct mt7603_dev *dev = hw->priv; + + dev->mt76.region = request->dfs_region; + dev->ed_monitor = dev->ed_monitor_enabled && + dev->mt76.region == NL80211_DFS_ETSI; +} + +static int +mt7603_txpower_signed(int val) +{ + bool sign = val & BIT(6); + + if (!(val & BIT(7))) + return 0; + + val &= GENMASK(5, 0); + if (!sign) + val = -val; + + return val; +} + +static void +mt7603_init_txpower(struct mt7603_dev *dev, + struct ieee80211_supported_band *sband) +{ + struct ieee80211_channel *chan; + u8 *eeprom = (u8 *)dev->mt76.eeprom.data; + int target_power = eeprom[MT_EE_TX_POWER_0_START_2G + 2] & ~BIT(7); + u8 *rate_power = &eeprom[MT_EE_TX_POWER_CCK]; + bool ext_pa = eeprom[MT_EE_NIC_CONF_0 + 1] & BIT(1); + u8 ext_pa_pwr; + int max_offset, cur_offset; + int i; + + ext_pa_pwr = eeprom[MT_EE_TX_POWER_TSSI_OFF]; + if (ext_pa && is_mt7603(dev) && ext_pa_pwr != 0 && ext_pa_pwr != 0xff) + target_power = ext_pa_pwr & ~BIT(7); + + if (target_power & BIT(6)) + target_power = -(target_power & GENMASK(5, 0)); + + max_offset = 0; + for (i = 0; i < 14; i++) { + cur_offset = mt7603_txpower_signed(rate_power[i]); + max_offset = max(max_offset, cur_offset); + } + + target_power += max_offset; + + dev->tx_power_limit = target_power; + dev->mphy.txpower_cur = target_power; + + target_power = DIV_ROUND_UP(target_power, 2); + + /* add 3 dBm for 2SS devices (combined output) */ + if (dev->mphy.antenna_mask & BIT(1)) + target_power += 3; + + for (i = 0; i < sband->n_channels; i++) { + chan = &sband->channels[i]; + chan->max_power = min_t(int, chan->max_reg_power, target_power); + chan->orig_mpwr = target_power; + } +} + +int mt7603_register_device(struct mt7603_dev *dev) +{ + struct mt76_bus_ops *bus_ops; + struct ieee80211_hw *hw = mt76_hw(dev); + struct wiphy *wiphy = hw->wiphy; + int ret; + + dev->bus_ops = dev->mt76.bus; + bus_ops = devm_kmemdup(dev->mt76.dev, dev->bus_ops, sizeof(*bus_ops), + GFP_KERNEL); + if (!bus_ops) + return -ENOMEM; + + bus_ops->rr = mt7603_rr; + bus_ops->wr = mt7603_wr; + bus_ops->rmw = mt7603_rmw; + dev->mt76.bus = bus_ops; + + spin_lock_init(&dev->ps_lock); + + INIT_DELAYED_WORK(&dev->mphy.mac_work, mt7603_mac_work); + tasklet_setup(&dev->mt76.pre_tbtt_tasklet, mt7603_pre_tbtt_tasklet); + + dev->slottime = 9; + dev->sensitivity_limit = 28; + dev->dynamic_sensitivity = true; + + ret = mt7603_init_hardware(dev); + if (ret) + return ret; + + hw->queues = 4; + hw->max_rates = 3; + hw->max_report_rates = 7; + hw->max_rate_tries = 11; + hw->max_tx_fragments = 1; + + hw->radiotap_timestamp.units_pos = + IEEE80211_RADIOTAP_TIMESTAMP_UNIT_US; + + hw->sta_data_size = sizeof(struct mt7603_sta); + hw->vif_data_size = sizeof(struct mt7603_vif); + + wiphy->iface_combinations = if_comb; + wiphy->n_iface_combinations = ARRAY_SIZE(if_comb); + + ieee80211_hw_set(hw, TX_STATUS_NO_AMPDU_LEN); + ieee80211_hw_set(hw, HOST_BROADCAST_PS_BUFFERING); + ieee80211_hw_set(hw, NEEDS_UNIQUE_STA_ADDR); + + /* init led callbacks */ + if (IS_ENABLED(CONFIG_MT76_LEDS)) { + dev->mphy.leds.cdev.brightness_set = mt7603_led_set_brightness; + dev->mphy.leds.cdev.blink_set = mt7603_led_set_blink; + } + + wiphy->reg_notifier = mt7603_regd_notifier; + + ret = mt76_register_device(&dev->mt76, true, mt76_rates, + ARRAY_SIZE(mt76_rates)); + if (ret) + return ret; + + mt7603_init_debugfs(dev); + mt7603_init_txpower(dev, &dev->mphy.sband_2g.sband); + + return 0; +} + +void mt7603_unregister_device(struct mt7603_dev *dev) +{ + tasklet_disable(&dev->mt76.pre_tbtt_tasklet); + mt76_unregister_device(&dev->mt76); + mt7603_mcu_exit(dev); + mt7603_dma_cleanup(dev); + mt76_free_device(&dev->mt76); +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/mac.c b/drivers/net/wireless/mediatek/mt76/mt7603/mac.c new file mode 100644 index 000000000..fa0bf9a20 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7603/mac.c @@ -0,0 +1,1946 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear + +#include +#include +#include "mt7603.h" +#include "mac.h" +#include "../trace.h" + +#define MT_PSE_PAGE_SIZE 128 + +static u32 +mt7603_ac_queue_mask0(u32 mask) +{ + u32 ret = 0; + + ret |= GENMASK(3, 0) * !!(mask & BIT(0)); + ret |= GENMASK(8, 5) * !!(mask & BIT(1)); + ret |= GENMASK(13, 10) * !!(mask & BIT(2)); + ret |= GENMASK(19, 16) * !!(mask & BIT(3)); + return ret; +} + +static void +mt76_stop_tx_ac(struct mt7603_dev *dev, u32 mask) +{ + mt76_set(dev, MT_WF_ARB_TX_STOP_0, mt7603_ac_queue_mask0(mask)); +} + +static void +mt76_start_tx_ac(struct mt7603_dev *dev, u32 mask) +{ + mt76_set(dev, MT_WF_ARB_TX_START_0, mt7603_ac_queue_mask0(mask)); +} + +void mt7603_mac_reset_counters(struct mt7603_dev *dev) +{ + int i; + + for (i = 0; i < 2; i++) + mt76_rr(dev, MT_TX_AGG_CNT(i)); + + memset(dev->mphy.aggr_stats, 0, sizeof(dev->mphy.aggr_stats)); +} + +void mt7603_mac_set_timing(struct mt7603_dev *dev) +{ + u32 cck = FIELD_PREP(MT_TIMEOUT_VAL_PLCP, 231) | + FIELD_PREP(MT_TIMEOUT_VAL_CCA, 48); + u32 ofdm = FIELD_PREP(MT_TIMEOUT_VAL_PLCP, 60) | + FIELD_PREP(MT_TIMEOUT_VAL_CCA, 24); + int offset = 3 * dev->coverage_class; + u32 reg_offset = FIELD_PREP(MT_TIMEOUT_VAL_PLCP, offset) | + FIELD_PREP(MT_TIMEOUT_VAL_CCA, offset); + bool is_5ghz = dev->mphy.chandef.chan->band == NL80211_BAND_5GHZ; + int sifs; + u32 val; + + if (is_5ghz) + sifs = 16; + else + sifs = 10; + + mt76_set(dev, MT_ARB_SCR, + MT_ARB_SCR_TX_DISABLE | MT_ARB_SCR_RX_DISABLE); + udelay(1); + + mt76_wr(dev, MT_TIMEOUT_CCK, cck + reg_offset); + mt76_wr(dev, MT_TIMEOUT_OFDM, ofdm + reg_offset); + mt76_wr(dev, MT_IFS, + FIELD_PREP(MT_IFS_EIFS, 360) | + FIELD_PREP(MT_IFS_RIFS, 2) | + FIELD_PREP(MT_IFS_SIFS, sifs) | + FIELD_PREP(MT_IFS_SLOT, dev->slottime)); + + if (dev->slottime < 20 || is_5ghz) + val = MT7603_CFEND_RATE_DEFAULT; + else + val = MT7603_CFEND_RATE_11B; + + mt76_rmw_field(dev, MT_AGG_CONTROL, MT_AGG_CONTROL_CFEND_RATE, val); + + mt76_clear(dev, MT_ARB_SCR, + MT_ARB_SCR_TX_DISABLE | MT_ARB_SCR_RX_DISABLE); +} + +static void +mt7603_wtbl_update(struct mt7603_dev *dev, int idx, u32 mask) +{ + mt76_rmw(dev, MT_WTBL_UPDATE, MT_WTBL_UPDATE_WLAN_IDX, + FIELD_PREP(MT_WTBL_UPDATE_WLAN_IDX, idx) | mask); + + mt76_poll(dev, MT_WTBL_UPDATE, MT_WTBL_UPDATE_BUSY, 0, 5000); +} + +static u32 +mt7603_wtbl1_addr(int idx) +{ + return MT_WTBL1_BASE + idx * MT_WTBL1_SIZE; +} + +static u32 +mt7603_wtbl2_addr(int idx) +{ + /* Mapped to WTBL2 */ + return MT_PCIE_REMAP_BASE_1 + idx * MT_WTBL2_SIZE; +} + +static u32 +mt7603_wtbl3_addr(int idx) +{ + u32 base = mt7603_wtbl2_addr(MT7603_WTBL_SIZE); + + return base + idx * MT_WTBL3_SIZE; +} + +static u32 +mt7603_wtbl4_addr(int idx) +{ + u32 base = mt7603_wtbl3_addr(MT7603_WTBL_SIZE); + + return base + idx * MT_WTBL4_SIZE; +} + +void mt7603_wtbl_init(struct mt7603_dev *dev, int idx, int vif, + const u8 *mac_addr) +{ + const void *_mac = mac_addr; + u32 addr = mt7603_wtbl1_addr(idx); + u32 w0 = 0, w1 = 0; + int i; + + if (_mac) { + w0 = FIELD_PREP(MT_WTBL1_W0_ADDR_HI, + get_unaligned_le16(_mac + 4)); + w1 = FIELD_PREP(MT_WTBL1_W1_ADDR_LO, + get_unaligned_le32(_mac)); + } + + if (vif < 0) + vif = 0; + else + w0 |= MT_WTBL1_W0_RX_CHECK_A1; + w0 |= FIELD_PREP(MT_WTBL1_W0_MUAR_IDX, vif); + + mt76_poll(dev, MT_WTBL_UPDATE, MT_WTBL_UPDATE_BUSY, 0, 5000); + + mt76_set(dev, addr + 0 * 4, w0); + mt76_set(dev, addr + 1 * 4, w1); + mt76_set(dev, addr + 2 * 4, MT_WTBL1_W2_ADMISSION_CONTROL); + + mt76_stop_tx_ac(dev, GENMASK(3, 0)); + addr = mt7603_wtbl2_addr(idx); + for (i = 0; i < MT_WTBL2_SIZE; i += 4) + mt76_wr(dev, addr + i, 0); + mt7603_wtbl_update(dev, idx, MT_WTBL_UPDATE_WTBL2); + mt76_start_tx_ac(dev, GENMASK(3, 0)); + + addr = mt7603_wtbl3_addr(idx); + for (i = 0; i < MT_WTBL3_SIZE; i += 4) + mt76_wr(dev, addr + i, 0); + + addr = mt7603_wtbl4_addr(idx); + for (i = 0; i < MT_WTBL4_SIZE; i += 4) + mt76_wr(dev, addr + i, 0); + + mt7603_wtbl_update(dev, idx, MT_WTBL_UPDATE_ADM_COUNT_CLEAR); +} + +static void +mt7603_wtbl_set_skip_tx(struct mt7603_dev *dev, int idx, bool enabled) +{ + u32 addr = mt7603_wtbl1_addr(idx); + u32 val = mt76_rr(dev, addr + 3 * 4); + + val &= ~MT_WTBL1_W3_SKIP_TX; + val |= enabled * MT_WTBL1_W3_SKIP_TX; + + mt76_wr(dev, addr + 3 * 4, val); +} + +void mt7603_filter_tx(struct mt7603_dev *dev, int mac_idx, int idx, bool abort) +{ + u32 flush_mask; + int i, port, queue; + + if (abort) { + port = 3; /* PSE */ + queue = 8; /* free queue */ + } else { + port = 0; /* HIF */ + queue = 1; /* MCU queue */ + } + + mt7603_wtbl_set_skip_tx(dev, idx, true); + + mt76_wr(dev, MT_TX_ABORT, MT_TX_ABORT_EN | + FIELD_PREP(MT_TX_ABORT_WCID, idx)); + + flush_mask = MT_WF_ARB_TX_FLUSH_AC0 | + MT_WF_ARB_TX_FLUSH_AC1 | + MT_WF_ARB_TX_FLUSH_AC2 | + MT_WF_ARB_TX_FLUSH_AC3; + flush_mask <<= mac_idx; + + mt76_wr(dev, MT_WF_ARB_TX_FLUSH_0, flush_mask); + mt76_poll(dev, MT_WF_ARB_TX_FLUSH_0, flush_mask, 0, 20000); + mt76_wr(dev, MT_WF_ARB_TX_START_0, flush_mask); + + mt76_wr(dev, MT_TX_ABORT, 0); + + for (i = 0; i < 4; i++) { + mt76_wr(dev, MT_DMA_FQCR0, MT_DMA_FQCR0_BUSY | + FIELD_PREP(MT_DMA_FQCR0_TARGET_WCID, idx) | + FIELD_PREP(MT_DMA_FQCR0_TARGET_QID, i) | + FIELD_PREP(MT_DMA_FQCR0_DEST_PORT_ID, port) | + FIELD_PREP(MT_DMA_FQCR0_DEST_QUEUE_ID, queue)); + + mt76_poll(dev, MT_DMA_FQCR0, MT_DMA_FQCR0_BUSY, 0, 5000); + } + + WARN_ON_ONCE(mt76_rr(dev, MT_DMA_FQCR0) & MT_DMA_FQCR0_BUSY); + + mt7603_wtbl_set_skip_tx(dev, idx, false); +} + +void mt7603_wtbl_set_smps(struct mt7603_dev *dev, struct mt7603_sta *sta, + bool enabled) +{ + u32 addr = mt7603_wtbl1_addr(sta->wcid.idx); + + if (sta->smps == enabled) + return; + + mt76_rmw_field(dev, addr + 2 * 4, MT_WTBL1_W2_SMPS, enabled); + sta->smps = enabled; +} + +static void +__mt7603_wtbl_set_ps(struct mt7603_dev *dev, struct mt7603_sta *sta, + bool enabled, bool filter) +{ + int idx = sta->wcid.idx; + u32 addr; + + lockdep_assert_held(&dev->ps_lock); + + if (sta->ps == enabled) + return; + + mt76_wr(dev, MT_PSE_RTA, + FIELD_PREP(MT_PSE_RTA_TAG_ID, idx) | + FIELD_PREP(MT_PSE_RTA_PORT_ID, 0) | + FIELD_PREP(MT_PSE_RTA_QUEUE_ID, 1) | + FIELD_PREP(MT_PSE_RTA_REDIRECT_EN, enabled) | + MT_PSE_RTA_WRITE | MT_PSE_RTA_BUSY); + + mt76_poll(dev, MT_PSE_RTA, MT_PSE_RTA_BUSY, 0, 5000); + + if (enabled && filter) + mt7603_filter_tx(dev, sta->vif->idx, idx, false); + + addr = mt7603_wtbl1_addr(idx); + mt76_set(dev, MT_WTBL1_OR, MT_WTBL1_OR_PSM_WRITE); + mt76_rmw(dev, addr + 3 * 4, MT_WTBL1_W3_POWER_SAVE, + enabled * MT_WTBL1_W3_POWER_SAVE); + mt76_clear(dev, MT_WTBL1_OR, MT_WTBL1_OR_PSM_WRITE); + sta->ps = enabled; +} + +void mt7603_wtbl_set_ps(struct mt7603_dev *dev, struct mt7603_sta *sta, + bool enabled) +{ + spin_lock_bh(&dev->ps_lock); + __mt7603_wtbl_set_ps(dev, sta, enabled, enabled); + spin_unlock_bh(&dev->ps_lock); +} + +void mt7603_wtbl_sta_ps(struct mt7603_dev *dev, struct mt7603_sta *sta, bool ps) +{ + spin_lock_bh(&dev->ps_lock); + sta->ps_sleeping = ps; + __mt7603_wtbl_set_ps(dev, sta, ps, ps); + spin_unlock_bh(&dev->ps_lock); +} + +void mt7603_wtbl_restore_ps(struct mt7603_dev *dev, struct mt7603_sta *sta) +{ + spin_lock_bh(&dev->ps_lock); + /* + * Frames that are already queued for the station belong to the service + * period that has just been served, so unlike on a sleep transition + * they must not be pulled back into the PS queue. + */ + if (sta->ps_sleeping) + __mt7603_wtbl_set_ps(dev, sta, true, false); + spin_unlock_bh(&dev->ps_lock); +} + +void mt7603_wtbl_clear(struct mt7603_dev *dev, int idx) +{ + int wtbl2_frame_size = MT_PSE_PAGE_SIZE / MT_WTBL2_SIZE; + int wtbl2_frame = idx / wtbl2_frame_size; + int wtbl2_entry = idx % wtbl2_frame_size; + + int wtbl3_base_frame = MT_WTBL3_OFFSET / MT_PSE_PAGE_SIZE; + int wtbl3_frame_size = MT_PSE_PAGE_SIZE / MT_WTBL3_SIZE; + int wtbl3_frame = wtbl3_base_frame + idx / wtbl3_frame_size; + int wtbl3_entry = (idx % wtbl3_frame_size) * 2; + + int wtbl4_base_frame = MT_WTBL4_OFFSET / MT_PSE_PAGE_SIZE; + int wtbl4_frame_size = MT_PSE_PAGE_SIZE / MT_WTBL4_SIZE; + int wtbl4_frame = wtbl4_base_frame + idx / wtbl4_frame_size; + int wtbl4_entry = idx % wtbl4_frame_size; + + u32 addr = MT_WTBL1_BASE + idx * MT_WTBL1_SIZE; + int i; + + mt76_poll(dev, MT_WTBL_UPDATE, MT_WTBL_UPDATE_BUSY, 0, 5000); + + mt76_wr(dev, addr + 0 * 4, + MT_WTBL1_W0_RX_CHECK_A1 | + MT_WTBL1_W0_RX_CHECK_A2 | + MT_WTBL1_W0_RX_VALID); + mt76_wr(dev, addr + 1 * 4, 0); + mt76_wr(dev, addr + 2 * 4, 0); + + mt76_set(dev, MT_WTBL1_OR, MT_WTBL1_OR_PSM_WRITE); + + mt76_wr(dev, addr + 3 * 4, + FIELD_PREP(MT_WTBL1_W3_WTBL2_FRAME_ID, wtbl2_frame) | + FIELD_PREP(MT_WTBL1_W3_WTBL2_ENTRY_ID, wtbl2_entry) | + FIELD_PREP(MT_WTBL1_W3_WTBL4_FRAME_ID, wtbl4_frame) | + MT_WTBL1_W3_I_PSM | MT_WTBL1_W3_KEEP_I_PSM); + mt76_wr(dev, addr + 4 * 4, + FIELD_PREP(MT_WTBL1_W4_WTBL3_FRAME_ID, wtbl3_frame) | + FIELD_PREP(MT_WTBL1_W4_WTBL3_ENTRY_ID, wtbl3_entry) | + FIELD_PREP(MT_WTBL1_W4_WTBL4_ENTRY_ID, wtbl4_entry)); + + mt76_clear(dev, MT_WTBL1_OR, MT_WTBL1_OR_PSM_WRITE); + + addr = mt7603_wtbl2_addr(idx); + + /* Clear BA information */ + mt76_wr(dev, addr + (15 * 4), 0); + + mt76_stop_tx_ac(dev, GENMASK(3, 0)); + for (i = 2; i <= 4; i++) + mt76_wr(dev, addr + (i * 4), 0); + mt7603_wtbl_update(dev, idx, MT_WTBL_UPDATE_WTBL2); + mt76_start_tx_ac(dev, GENMASK(3, 0)); + + mt7603_wtbl_update(dev, idx, MT_WTBL_UPDATE_RX_COUNT_CLEAR); + mt7603_wtbl_update(dev, idx, MT_WTBL_UPDATE_TX_COUNT_CLEAR); + mt7603_wtbl_update(dev, idx, MT_WTBL_UPDATE_ADM_COUNT_CLEAR); +} + +void mt7603_wtbl_update_cap(struct mt7603_dev *dev, struct ieee80211_sta *sta) +{ + struct mt7603_sta *msta = (struct mt7603_sta *)sta->drv_priv; + int idx = msta->wcid.idx; + u8 ampdu_density; + u32 addr; + u32 val; + + addr = mt7603_wtbl1_addr(idx); + + ampdu_density = sta->deflink.ht_cap.ampdu_density; + if (ampdu_density < IEEE80211_HT_MPDU_DENSITY_4) + ampdu_density = IEEE80211_HT_MPDU_DENSITY_4; + + val = mt76_rr(dev, addr + 2 * 4); + val &= MT_WTBL1_W2_KEY_TYPE | MT_WTBL1_W2_ADMISSION_CONTROL; + val |= FIELD_PREP(MT_WTBL1_W2_AMPDU_FACTOR, + sta->deflink.ht_cap.ampdu_factor) | + FIELD_PREP(MT_WTBL1_W2_MPDU_DENSITY, + sta->deflink.ht_cap.ampdu_density) | + MT_WTBL1_W2_TXS_BAF_REPORT; + + if (sta->deflink.ht_cap.cap) + val |= MT_WTBL1_W2_HT; + if (sta->deflink.vht_cap.cap) + val |= MT_WTBL1_W2_VHT; + + mt76_wr(dev, addr + 2 * 4, val); + + addr = mt7603_wtbl2_addr(idx); + val = mt76_rr(dev, addr + 9 * 4); + val &= ~(MT_WTBL2_W9_SHORT_GI_20 | MT_WTBL2_W9_SHORT_GI_40 | + MT_WTBL2_W9_SHORT_GI_80); + if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_SGI_20) + val |= MT_WTBL2_W9_SHORT_GI_20; + if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_SGI_40) + val |= MT_WTBL2_W9_SHORT_GI_40; + mt76_wr(dev, addr + 9 * 4, val); +} + +void mt7603_mac_rx_ba_reset(struct mt7603_dev *dev, void *addr, u8 tid) +{ + mt76_wr(dev, MT_BA_CONTROL_0, get_unaligned_le32(addr)); + mt76_wr(dev, MT_BA_CONTROL_1, + (get_unaligned_le16(addr + 4) | + FIELD_PREP(MT_BA_CONTROL_1_TID, tid) | + MT_BA_CONTROL_1_RESET)); +} + +void mt7603_mac_tx_ba_reset(struct mt7603_dev *dev, int wcid, int tid, + int ba_size) +{ + u32 addr = mt7603_wtbl2_addr(wcid); + u32 tid_mask = FIELD_PREP(MT_WTBL2_W15_BA_EN_TIDS, BIT(tid)) | + (MT_WTBL2_W15_BA_WIN_SIZE << + (tid * MT_WTBL2_W15_BA_WIN_SIZE_SHIFT)); + u32 tid_val; + int i; + + if (ba_size < 0) { + /* disable */ + mt76_clear(dev, addr + (15 * 4), tid_mask); + return; + } + + for (i = 7; i > 0; i--) { + if (ba_size >= MT_AGG_SIZE_LIMIT(i)) + break; + } + + tid_val = FIELD_PREP(MT_WTBL2_W15_BA_EN_TIDS, BIT(tid)) | + i << (tid * MT_WTBL2_W15_BA_WIN_SIZE_SHIFT); + + mt76_rmw(dev, addr + (15 * 4), tid_mask, tid_val); +} + +void mt7603_mac_sta_poll(struct mt7603_dev *dev) +{ + static const u8 ac_to_tid[4] = { + [IEEE80211_AC_BE] = 0, + [IEEE80211_AC_BK] = 1, + [IEEE80211_AC_VI] = 4, + [IEEE80211_AC_VO] = 6 + }; + struct ieee80211_sta *sta; + struct mt7603_sta *msta; + u32 total_airtime = 0; + u32 airtime[4]; + u32 addr; + int i; + + rcu_read_lock(); + + while (1) { + bool clear = false; + + spin_lock_bh(&dev->mt76.sta_poll_lock); + if (list_empty(&dev->mt76.sta_poll_list)) { + spin_unlock_bh(&dev->mt76.sta_poll_lock); + break; + } + + msta = list_first_entry(&dev->mt76.sta_poll_list, + struct mt7603_sta, wcid.poll_list); + list_del_init(&msta->wcid.poll_list); + spin_unlock_bh(&dev->mt76.sta_poll_lock); + + addr = mt7603_wtbl4_addr(msta->wcid.idx); + for (i = 0; i < 4; i++) { + u32 airtime_last = msta->tx_airtime_ac[i]; + + msta->tx_airtime_ac[i] = mt76_rr(dev, addr + i * 8); + airtime[i] = msta->tx_airtime_ac[i] - airtime_last; + airtime[i] *= 32; + total_airtime += airtime[i]; + + if (msta->tx_airtime_ac[i] & BIT(22)) + clear = true; + } + + if (clear) { + mt7603_wtbl_update(dev, msta->wcid.idx, + MT_WTBL_UPDATE_ADM_COUNT_CLEAR); + memset(msta->tx_airtime_ac, 0, + sizeof(msta->tx_airtime_ac)); + } + + if (!msta->wcid.sta) + continue; + + sta = container_of((void *)msta, struct ieee80211_sta, drv_priv); + for (i = 0; i < 4; i++) { + struct mt76_queue *q = dev->mphy.q_tx[i]; + u8 qidx = q->hw_idx; + u8 tid = ac_to_tid[i]; + u32 txtime = airtime[qidx]; + + if (!txtime) + continue; + + ieee80211_sta_register_airtime(sta, tid, txtime, 0); + } + } + + rcu_read_unlock(); + + if (!total_airtime) + return; + + spin_lock_bh(&dev->mt76.cc_lock); + dev->mphy.chan_state->cc_tx += total_airtime; + spin_unlock_bh(&dev->mt76.cc_lock); +} + +static struct mt76_wcid * +mt7603_rx_get_wcid(struct mt7603_dev *dev, u8 idx, bool unicast) +{ + struct mt7603_sta *sta; + struct mt76_wcid *wcid; + + wcid = mt76_wcid_ptr(dev, idx); + if (unicast || !wcid) + return wcid; + + if (!wcid->sta) + return NULL; + + sta = container_of(wcid, struct mt7603_sta, wcid); + if (!sta->vif) + return NULL; + + return &sta->vif->sta.wcid; +} + +int +mt7603_mac_fill_rx(struct mt7603_dev *dev, struct sk_buff *skb) +{ + struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb; + struct ieee80211_supported_band *sband; + struct ieee80211_hdr *hdr; + __le32 *rxd = (__le32 *)skb->data; + u32 rxd0 = le32_to_cpu(rxd[0]); + u32 rxd1 = le32_to_cpu(rxd[1]); + u32 rxd2 = le32_to_cpu(rxd[2]); + bool unicast = rxd1 & MT_RXD1_NORMAL_U2M; + bool insert_ccmp_hdr = false; + bool remove_pad; + int idx; + int i; + + memset(status, 0, sizeof(*status)); + + i = FIELD_GET(MT_RXD1_NORMAL_CH_FREQ, rxd1); + sband = (i & 1) ? &dev->mphy.sband_5g.sband : &dev->mphy.sband_2g.sband; + i >>= 1; + + idx = FIELD_GET(MT_RXD2_NORMAL_WLAN_IDX, rxd2); + status->wcid = mt7603_rx_get_wcid(dev, idx, unicast); + + status->band = sband->band; + if (i < sband->n_channels) + status->freq = sband->channels[i].center_freq; + + if (rxd2 & MT_RXD2_NORMAL_FCS_ERR) + status->flag |= RX_FLAG_FAILED_FCS_CRC; + + if (rxd2 & MT_RXD2_NORMAL_TKIP_MIC_ERR) + status->flag |= RX_FLAG_MMIC_ERROR; + + /* ICV error or CCMP/BIP/WPI MIC error */ + if (rxd2 & MT_RXD2_NORMAL_ICV_ERR) + status->flag |= RX_FLAG_ONLY_MONITOR; + + if (FIELD_GET(MT_RXD2_NORMAL_SEC_MODE, rxd2) != 0 && + !(rxd2 & (MT_RXD2_NORMAL_CLM | MT_RXD2_NORMAL_CM))) { + status->flag |= RX_FLAG_DECRYPTED; + status->flag |= RX_FLAG_IV_STRIPPED; + status->flag |= RX_FLAG_MMIC_STRIPPED | RX_FLAG_MIC_STRIPPED; + } + + remove_pad = rxd1 & MT_RXD1_NORMAL_HDR_OFFSET; + + if (rxd2 & MT_RXD2_NORMAL_MAX_LEN_ERROR) + return -EINVAL; + + if (!sband->channels) + return -EINVAL; + + rxd += 4; + if (rxd0 & MT_RXD0_NORMAL_GROUP_4) { + rxd += 4; + if ((u8 *)rxd - skb->data >= skb->len) + return -EINVAL; + } + if (rxd0 & MT_RXD0_NORMAL_GROUP_1) { + u8 *data = (u8 *)rxd; + + if (status->flag & RX_FLAG_DECRYPTED) { + switch (FIELD_GET(MT_RXD2_NORMAL_SEC_MODE, rxd2)) { + case MT_CIPHER_AES_CCMP: + case MT_CIPHER_CCMP_CCX: + case MT_CIPHER_CCMP_256: + insert_ccmp_hdr = + FIELD_GET(MT_RXD2_NORMAL_FRAG, rxd2); + fallthrough; + case MT_CIPHER_TKIP: + case MT_CIPHER_TKIP_NO_MIC: + case MT_CIPHER_GCMP: + case MT_CIPHER_GCMP_256: + status->iv[0] = data[5]; + status->iv[1] = data[4]; + status->iv[2] = data[3]; + status->iv[3] = data[2]; + status->iv[4] = data[1]; + status->iv[5] = data[0]; + break; + default: + break; + } + } + + rxd += 4; + if ((u8 *)rxd - skb->data >= skb->len) + return -EINVAL; + } + if (rxd0 & MT_RXD0_NORMAL_GROUP_2) { + status->timestamp = le32_to_cpu(rxd[0]); + status->flag |= RX_FLAG_MACTIME_START; + + if (!(rxd2 & (MT_RXD2_NORMAL_NON_AMPDU_SUB | + MT_RXD2_NORMAL_NON_AMPDU))) { + status->flag |= RX_FLAG_AMPDU_DETAILS; + + /* all subframes of an A-MPDU have the same timestamp */ + if (dev->rx_ampdu_ts != status->timestamp) { + if (!++dev->ampdu_ref) + dev->ampdu_ref++; + } + dev->rx_ampdu_ts = status->timestamp; + + status->ampdu_ref = dev->ampdu_ref; + } + + rxd += 2; + if ((u8 *)rxd - skb->data >= skb->len) + return -EINVAL; + } + if (rxd0 & MT_RXD0_NORMAL_GROUP_3) { + u32 rxdg0 = le32_to_cpu(rxd[0]); + u32 rxdg3 = le32_to_cpu(rxd[3]); + bool cck = false; + + i = FIELD_GET(MT_RXV1_TX_RATE, rxdg0); + switch (FIELD_GET(MT_RXV1_TX_MODE, rxdg0)) { + case MT_PHY_TYPE_CCK: + cck = true; + fallthrough; + case MT_PHY_TYPE_OFDM: + i = mt76_get_rate(&dev->mt76, sband, i, cck); + break; + case MT_PHY_TYPE_HT_GF: + case MT_PHY_TYPE_HT: + status->encoding = RX_ENC_HT; + if (i > 15) + return -EINVAL; + break; + default: + return -EINVAL; + } + + if (rxdg0 & MT_RXV1_HT_SHORT_GI) + status->enc_flags |= RX_ENC_FLAG_SHORT_GI; + if (rxdg0 & MT_RXV1_HT_AD_CODE) + status->enc_flags |= RX_ENC_FLAG_LDPC; + + status->enc_flags |= RX_ENC_FLAG_STBC_MASK * + FIELD_GET(MT_RXV1_HT_STBC, rxdg0); + + status->rate_idx = i; + + status->chains = dev->mphy.antenna_mask; + status->chain_signal[0] = FIELD_GET(MT_RXV4_IB_RSSI0, rxdg3) + + dev->rssi_offset[0]; + status->chain_signal[1] = FIELD_GET(MT_RXV4_IB_RSSI1, rxdg3) + + dev->rssi_offset[1]; + + if (FIELD_GET(MT_RXV1_FRAME_MODE, rxdg0) == 1) + status->bw = RATE_INFO_BW_40; + + rxd += 6; + if ((u8 *)rxd - skb->data >= skb->len) + return -EINVAL; + } else { + return -EINVAL; + } + + skb_pull(skb, (u8 *)rxd - skb->data + 2 * remove_pad); + + if (insert_ccmp_hdr) { + u8 key_id = FIELD_GET(MT_RXD1_NORMAL_KEY_ID, rxd1); + + mt76_insert_ccmp_hdr(skb, key_id); + } + + hdr = (struct ieee80211_hdr *)skb->data; + if (!status->wcid || !ieee80211_is_data_qos(hdr->frame_control)) + return 0; + + status->aggr = unicast && + !ieee80211_is_qos_nullfunc(hdr->frame_control); + status->qos_ctl = *ieee80211_get_qos_ctl(hdr); + status->seqno = IEEE80211_SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl)); + + return 0; +} + +static u16 +mt7603_mac_tx_rate_val(struct mt7603_dev *dev, + const struct ieee80211_tx_rate *rate, bool stbc, u8 *bw) +{ + u8 phy, nss, rate_idx; + u16 rateval; + + *bw = 0; + if (rate->flags & IEEE80211_TX_RC_MCS) { + rate_idx = rate->idx; + nss = 1 + (rate->idx >> 3); + phy = MT_PHY_TYPE_HT; + if (rate->flags & IEEE80211_TX_RC_GREEN_FIELD) + phy = MT_PHY_TYPE_HT_GF; + if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) + *bw = 1; + } else { + const struct ieee80211_rate *r; + int band = dev->mphy.chandef.chan->band; + u16 val; + + nss = 1; + r = &mt76_hw(dev)->wiphy->bands[band]->bitrates[rate->idx]; + if (rate->flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) + val = r->hw_value_short; + else + val = r->hw_value; + + phy = val >> 8; + rate_idx = val & 0xff; + } + + rateval = (FIELD_PREP(MT_TX_RATE_IDX, rate_idx) | + FIELD_PREP(MT_TX_RATE_MODE, phy)); + + if (stbc && nss == 1) + rateval |= MT_TX_RATE_STBC; + + return rateval; +} + +void mt7603_wtbl_set_rates(struct mt7603_dev *dev, struct mt7603_sta *sta, + struct ieee80211_tx_rate *probe_rate, + struct ieee80211_tx_rate *rates) +{ + struct ieee80211_tx_rate *ref; + int wcid = sta->wcid.idx; + u32 addr = mt7603_wtbl2_addr(wcid); + bool stbc = false; + int n_rates = sta->n_rates; + u8 bw, bw_prev, bw_idx = 0; + u16 val[4]; + u16 probe_val; + u32 w9 = mt76_rr(dev, addr + 9 * 4); + bool rateset; + int i, k; + + if (!mt76_poll(dev, MT_WTBL_UPDATE, MT_WTBL_UPDATE_BUSY, 0, 5000)) + return; + + for (i = n_rates; i < 4; i++) + rates[i] = rates[n_rates - 1]; + + rateset = !(sta->rate_set_tsf & BIT(0)); + memcpy(sta->rateset[rateset].rates, rates, + sizeof(sta->rateset[rateset].rates)); + if (probe_rate) { + sta->rateset[rateset].probe_rate = *probe_rate; + ref = &sta->rateset[rateset].probe_rate; + } else { + sta->rateset[rateset].probe_rate.idx = -1; + ref = &sta->rateset[rateset].rates[0]; + } + + rates = sta->rateset[rateset].rates; + for (i = 0; i < ARRAY_SIZE(sta->rateset[rateset].rates); i++) { + /* + * We don't support switching between short and long GI + * within the rate set. For accurate tx status reporting, we + * need to make sure that flags match. + * For improved performance, avoid duplicate entries by + * decrementing the MCS index if necessary + */ + if ((ref->flags ^ rates[i].flags) & IEEE80211_TX_RC_SHORT_GI) + rates[i].flags ^= IEEE80211_TX_RC_SHORT_GI; + + for (k = 0; k < i; k++) { + if (rates[i].idx != rates[k].idx) + continue; + if ((rates[i].flags ^ rates[k].flags) & + IEEE80211_TX_RC_40_MHZ_WIDTH) + continue; + + if (!rates[i].idx) + continue; + + rates[i].idx--; + } + } + + w9 &= MT_WTBL2_W9_SHORT_GI_20 | MT_WTBL2_W9_SHORT_GI_40 | + MT_WTBL2_W9_SHORT_GI_80; + + val[0] = mt7603_mac_tx_rate_val(dev, &rates[0], stbc, &bw); + bw_prev = bw; + + if (probe_rate) { + probe_val = mt7603_mac_tx_rate_val(dev, probe_rate, stbc, &bw); + if (bw) + bw_idx = 1; + else + bw_prev = 0; + } else { + probe_val = val[0]; + } + + w9 |= FIELD_PREP(MT_WTBL2_W9_CC_BW_SEL, bw); + w9 |= FIELD_PREP(MT_WTBL2_W9_BW_CAP, bw); + + val[1] = mt7603_mac_tx_rate_val(dev, &rates[1], stbc, &bw); + if (bw_prev) { + bw_idx = 3; + bw_prev = bw; + } + + val[2] = mt7603_mac_tx_rate_val(dev, &rates[2], stbc, &bw); + if (bw_prev) { + bw_idx = 5; + bw_prev = bw; + } + + val[3] = mt7603_mac_tx_rate_val(dev, &rates[3], stbc, &bw); + if (bw_prev) + bw_idx = 7; + + w9 |= FIELD_PREP(MT_WTBL2_W9_CHANGE_BW_RATE, + bw_idx ? bw_idx - 1 : 7); + + mt76_wr(dev, MT_WTBL_RIUCR0, w9); + + mt76_wr(dev, MT_WTBL_RIUCR1, + FIELD_PREP(MT_WTBL_RIUCR1_RATE0, probe_val) | + FIELD_PREP(MT_WTBL_RIUCR1_RATE1, val[0]) | + FIELD_PREP(MT_WTBL_RIUCR1_RATE2_LO, val[1])); + + mt76_wr(dev, MT_WTBL_RIUCR2, + FIELD_PREP(MT_WTBL_RIUCR2_RATE2_HI, val[1] >> 8) | + FIELD_PREP(MT_WTBL_RIUCR2_RATE3, val[1]) | + FIELD_PREP(MT_WTBL_RIUCR2_RATE4, val[2]) | + FIELD_PREP(MT_WTBL_RIUCR2_RATE5_LO, val[2])); + + mt76_wr(dev, MT_WTBL_RIUCR3, + FIELD_PREP(MT_WTBL_RIUCR3_RATE5_HI, val[2] >> 4) | + FIELD_PREP(MT_WTBL_RIUCR3_RATE6, val[3]) | + FIELD_PREP(MT_WTBL_RIUCR3_RATE7, val[3])); + + mt76_set(dev, MT_LPON_T0CR, MT_LPON_T0CR_MODE); /* TSF read */ + sta->rate_set_tsf = (mt76_rr(dev, MT_LPON_UTTR0) & ~BIT(0)) | rateset; + + mt76_wr(dev, MT_WTBL_UPDATE, + FIELD_PREP(MT_WTBL_UPDATE_WLAN_IDX, wcid) | + MT_WTBL_UPDATE_RATE_UPDATE | + MT_WTBL_UPDATE_TX_COUNT_CLEAR); + + if (!(sta->wcid.tx_info & MT_WCID_TX_INFO_SET)) + mt76_poll(dev, MT_WTBL_UPDATE, MT_WTBL_UPDATE_BUSY, 0, 5000); + + sta->rate_count = 2 * MT7603_RATE_RETRY * n_rates; + sta->wcid.tx_info |= MT_WCID_TX_INFO_SET; +} + +static enum mt76_cipher_type +mt7603_mac_get_key_info(struct ieee80211_key_conf *key, u8 *key_data) +{ + memset(key_data, 0, 32); + if (!key) + return MT_CIPHER_NONE; + + if (key->keylen > 32) + return MT_CIPHER_NONE; + + memcpy(key_data, key->key, key->keylen); + + switch (key->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + return MT_CIPHER_WEP40; + case WLAN_CIPHER_SUITE_WEP104: + return MT_CIPHER_WEP104; + case WLAN_CIPHER_SUITE_TKIP: + /* Rx/Tx MIC keys are swapped */ + memcpy(key_data + 16, key->key + 24, 8); + memcpy(key_data + 24, key->key + 16, 8); + return MT_CIPHER_TKIP; + case WLAN_CIPHER_SUITE_CCMP: + return MT_CIPHER_AES_CCMP; + default: + return MT_CIPHER_NONE; + } +} + +int mt7603_wtbl_set_key(struct mt7603_dev *dev, int wcid, + struct ieee80211_key_conf *key) +{ + enum mt76_cipher_type cipher; + u32 addr = mt7603_wtbl3_addr(wcid); + u8 key_data[32]; + int key_len = sizeof(key_data); + + cipher = mt7603_mac_get_key_info(key, key_data); + if (cipher == MT_CIPHER_NONE && key) + return -EOPNOTSUPP; + + if (key && (cipher == MT_CIPHER_WEP40 || cipher == MT_CIPHER_WEP104)) { + addr += key->keyidx * 16; + key_len = 16; + } + + mt76_wr_copy(dev, addr, key_data, key_len); + + addr = mt7603_wtbl1_addr(wcid); + mt76_rmw_field(dev, addr + 2 * 4, MT_WTBL1_W2_KEY_TYPE, cipher); + if (key) + mt76_rmw_field(dev, addr, MT_WTBL1_W0_KEY_IDX, key->keyidx); + mt76_rmw_field(dev, addr, MT_WTBL1_W0_RX_KEY_VALID, !!key); + + return 0; +} + +static int +mt7603_mac_write_txwi(struct mt7603_dev *dev, __le32 *txwi, + struct sk_buff *skb, enum mt76_txq_id qid, + struct mt76_wcid *wcid, struct ieee80211_sta *sta, + int pid, struct ieee80211_key_conf *key) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_tx_rate *rate = &info->control.rates[0]; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ieee80211_bar *bar = (struct ieee80211_bar *)skb->data; + struct ieee80211_vif *vif = info->control.vif; + struct mt76_queue *q = dev->mphy.q_tx[qid]; + struct mt7603_vif *mvif; + int wlan_idx; + int hdr_len = ieee80211_get_hdrlen_from_skb(skb); + int tx_count = 8; + u8 frame_type, frame_subtype; + u16 fc = le16_to_cpu(hdr->frame_control); + u16 seqno = 0; + u8 vif_idx = 0; + u32 val; + u8 bw; + + if (vif) { + mvif = (struct mt7603_vif *)vif->drv_priv; + vif_idx = mvif->idx; + if (vif_idx && qid >= MT_TXQ_BEACON) + vif_idx += 0x10; + } + + if (sta) { + struct mt7603_sta *msta = (struct mt7603_sta *)sta->drv_priv; + + tx_count = msta->rate_count; + } + + if (wcid) + wlan_idx = wcid->idx; + else + wlan_idx = MT7603_WTBL_RESERVED; + + frame_type = (fc & IEEE80211_FCTL_FTYPE) >> 2; + frame_subtype = (fc & IEEE80211_FCTL_STYPE) >> 4; + + val = FIELD_PREP(MT_TXD0_TX_BYTES, skb->len + MT_TXD_SIZE) | + FIELD_PREP(MT_TXD0_Q_IDX, q->hw_idx); + txwi[0] = cpu_to_le32(val); + + val = MT_TXD1_LONG_FORMAT | + FIELD_PREP(MT_TXD1_OWN_MAC, vif_idx) | + FIELD_PREP(MT_TXD1_TID, + skb->priority & IEEE80211_QOS_CTL_TID_MASK) | + FIELD_PREP(MT_TXD1_HDR_FORMAT, MT_HDR_FORMAT_802_11) | + FIELD_PREP(MT_TXD1_HDR_INFO, hdr_len / 2) | + FIELD_PREP(MT_TXD1_WLAN_IDX, wlan_idx) | + FIELD_PREP(MT_TXD1_PROTECTED, !!key); + txwi[1] = cpu_to_le32(val); + + if (info->flags & IEEE80211_TX_CTL_NO_ACK) + txwi[1] |= cpu_to_le32(MT_TXD1_NO_ACK); + + val = FIELD_PREP(MT_TXD2_FRAME_TYPE, frame_type) | + FIELD_PREP(MT_TXD2_SUB_TYPE, frame_subtype) | + FIELD_PREP(MT_TXD2_MULTICAST, + is_multicast_ether_addr(hdr->addr1)); + txwi[2] = cpu_to_le32(val); + + if (!(info->flags & IEEE80211_TX_CTL_AMPDU)) + txwi[2] |= cpu_to_le32(MT_TXD2_BA_DISABLE); + + txwi[4] = 0; + + val = MT_TXD5_TX_STATUS_HOST | MT_TXD5_SW_POWER_MGMT | + FIELD_PREP(MT_TXD5_PID, pid); + txwi[5] = cpu_to_le32(val); + + txwi[6] = 0; + + if (rate->idx >= 0 && rate->count && + !(info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE)) { + bool stbc = info->flags & IEEE80211_TX_CTL_STBC; + u16 rateval = mt7603_mac_tx_rate_val(dev, rate, stbc, &bw); + + txwi[2] |= cpu_to_le32(MT_TXD2_FIX_RATE); + + val = MT_TXD6_FIXED_BW | + FIELD_PREP(MT_TXD6_BW, bw) | + FIELD_PREP(MT_TXD6_TX_RATE, rateval); + txwi[6] |= cpu_to_le32(val); + + if (rate->flags & IEEE80211_TX_RC_SHORT_GI) + txwi[6] |= cpu_to_le32(MT_TXD6_SGI); + + if (!(rate->flags & IEEE80211_TX_RC_MCS)) + txwi[2] |= cpu_to_le32(MT_TXD2_BA_DISABLE); + + tx_count = rate->count; + } + + /* use maximum tx count for beacons and buffered multicast */ + if (qid >= MT_TXQ_BEACON) + tx_count = 0x1f; + + val = FIELD_PREP(MT_TXD3_REM_TX_COUNT, tx_count) | + MT_TXD3_SN_VALID; + + if (ieee80211_is_data_qos(hdr->frame_control)) + seqno = le16_to_cpu(hdr->seq_ctrl); + else if (ieee80211_is_back_req(hdr->frame_control)) + seqno = le16_to_cpu(bar->start_seq_num); + else + val &= ~MT_TXD3_SN_VALID; + + val |= FIELD_PREP(MT_TXD3_SEQ, seqno >> 4); + + txwi[3] = cpu_to_le32(val); + + if (key) { + u64 pn = atomic64_inc_return(&key->tx_pn); + + txwi[3] |= cpu_to_le32(MT_TXD3_PN_VALID); + txwi[4] = cpu_to_le32(pn & GENMASK(31, 0)); + txwi[5] |= cpu_to_le32(FIELD_PREP(MT_TXD5_PN_HIGH, pn >> 32)); + } + + txwi[7] = 0; + + return 0; +} + +int mt7603_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, + enum mt76_txq_id qid, struct mt76_wcid *wcid, + struct ieee80211_sta *sta, + struct mt76_tx_info *tx_info) +{ + struct mt7603_dev *dev = container_of(mdev, struct mt7603_dev, mt76); + struct mt7603_sta *msta = container_of(wcid, struct mt7603_sta, wcid); + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx_info->skb); + struct ieee80211_key_conf *key = info->control.hw_key; + int pid; + + if (!wcid) + wcid = &dev->global_sta.wcid; + + if (sta) { + msta = (struct mt7603_sta *)sta->drv_priv; + + if ((info->flags & (IEEE80211_TX_CTL_NO_PS_BUFFER | + IEEE80211_TX_CTL_CLEAR_PS_FILT)) || + (info->control.flags & IEEE80211_TX_CTRL_PS_RESPONSE)) + mt7603_wtbl_set_ps(dev, msta, false); + + mt76_tx_check_agg_ssn(sta, tx_info->skb); + } + + pid = mt76_tx_status_skb_add(mdev, wcid, tx_info->skb); + + if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) { + spin_lock_bh(&dev->mt76.lock); + mt7603_wtbl_set_rates(dev, msta, &info->control.rates[0], + msta->rates); + msta->rate_probe = true; + spin_unlock_bh(&dev->mt76.lock); + } + + mt7603_mac_write_txwi(dev, txwi_ptr, tx_info->skb, qid, wcid, + sta, pid, key); + + return 0; +} + +static bool +mt7603_fill_txs(struct mt7603_dev *dev, struct mt7603_sta *sta, + struct ieee80211_tx_info *info, __le32 *txs_data) +{ + struct ieee80211_supported_band *sband; + struct mt7603_rate_set *rs; + int first_idx = 0, last_idx; + u32 rate_set_tsf; + u32 final_rate; + u32 final_rate_flags; + bool rs_idx; + bool ack_timeout; + bool fixed_rate; + bool probe; + bool ampdu; + bool cck = false; + int count; + u32 txs; + int idx; + int i; + + fixed_rate = info->status.rates[0].count; + probe = !!(info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE); + + txs = le32_to_cpu(txs_data[4]); + ampdu = !fixed_rate && (txs & MT_TXS4_AMPDU); + count = FIELD_GET(MT_TXS4_TX_COUNT, txs); + last_idx = FIELD_GET(MT_TXS4_LAST_TX_RATE, txs); + + txs = le32_to_cpu(txs_data[0]); + final_rate = FIELD_GET(MT_TXS0_TX_RATE, txs); + ack_timeout = txs & MT_TXS0_ACK_TIMEOUT; + + if (!ampdu && (txs & MT_TXS0_RTS_TIMEOUT)) + return false; + + if (txs & MT_TXS0_QUEUE_TIMEOUT) + return false; + + if (!ack_timeout) + info->flags |= IEEE80211_TX_STAT_ACK; + + info->status.ampdu_len = 1; + info->status.ampdu_ack_len = !!(info->flags & + IEEE80211_TX_STAT_ACK); + + if (ampdu || (info->flags & IEEE80211_TX_CTL_AMPDU)) + info->flags |= IEEE80211_TX_STAT_AMPDU | IEEE80211_TX_CTL_AMPDU; + + first_idx = max_t(int, 0, last_idx - (count - 1) / MT7603_RATE_RETRY); + + if (fixed_rate && !probe) { + info->status.rates[0].count = count; + i = 0; + goto out; + } + + rate_set_tsf = READ_ONCE(sta->rate_set_tsf); + rs_idx = !((u32)(le32_get_bits(txs_data[1], MT_TXS1_F0_TIMESTAMP) - + rate_set_tsf) < 1000000); + rs_idx ^= rate_set_tsf & BIT(0); + rs = &sta->rateset[rs_idx]; + + if (!first_idx && rs->probe_rate.idx >= 0) { + info->status.rates[0] = rs->probe_rate; + + spin_lock_bh(&dev->mt76.lock); + if (sta->rate_probe) { + mt7603_wtbl_set_rates(dev, sta, NULL, + sta->rates); + sta->rate_probe = false; + } + spin_unlock_bh(&dev->mt76.lock); + } else { + info->status.rates[0] = rs->rates[first_idx / 2]; + } + info->status.rates[0].count = 0; + + for (i = 0, idx = first_idx; count && idx <= last_idx; idx++) { + struct ieee80211_tx_rate *cur_rate; + int cur_count; + + cur_rate = &rs->rates[idx / 2]; + cur_count = min_t(int, MT7603_RATE_RETRY, count); + count -= cur_count; + + if (idx && (cur_rate->idx != info->status.rates[i].idx || + cur_rate->flags != info->status.rates[i].flags)) { + i++; + if (i == ARRAY_SIZE(info->status.rates)) { + i--; + break; + } + + info->status.rates[i] = *cur_rate; + info->status.rates[i].count = 0; + } + + info->status.rates[i].count += cur_count; + } + +out: + final_rate_flags = info->status.rates[i].flags; + + switch (FIELD_GET(MT_TX_RATE_MODE, final_rate)) { + case MT_PHY_TYPE_CCK: + cck = true; + fallthrough; + case MT_PHY_TYPE_OFDM: + if (dev->mphy.chandef.chan->band == NL80211_BAND_5GHZ) + sband = &dev->mphy.sband_5g.sband; + else + sband = &dev->mphy.sband_2g.sband; + final_rate &= GENMASK(5, 0); + final_rate = mt76_get_rate(&dev->mt76, sband, final_rate, + cck); + final_rate_flags = 0; + break; + case MT_PHY_TYPE_HT_GF: + case MT_PHY_TYPE_HT: + final_rate_flags |= IEEE80211_TX_RC_MCS; + final_rate &= GENMASK(5, 0); + if (final_rate > 15) + return false; + break; + default: + return false; + } + + info->status.rates[i].idx = final_rate; + info->status.rates[i].flags = final_rate_flags; + + return true; +} + +static bool +mt7603_mac_add_txs_skb(struct mt7603_dev *dev, struct mt7603_sta *sta, int pid, + __le32 *txs_data) +{ + struct mt76_dev *mdev = &dev->mt76; + struct sk_buff_head list; + struct sk_buff *skb; + + if (pid < MT_PACKET_ID_FIRST) + return false; + + trace_mac_txdone(mdev, sta->wcid.idx, pid); + + mt76_tx_status_lock(mdev, &list); + skb = mt76_tx_status_skb_get(mdev, &sta->wcid, pid, &list); + if (skb) { + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + + if (!mt7603_fill_txs(dev, sta, info, txs_data)) { + info->status.rates[0].count = 0; + info->status.rates[0].idx = -1; + } + + mt76_tx_status_skb_done(mdev, skb, &list); + } + mt76_tx_status_unlock(mdev, &list); + + return !!skb; +} + +void mt7603_mac_add_txs(struct mt7603_dev *dev, void *data) +{ + struct ieee80211_tx_info info = {}; + struct ieee80211_sta *sta = NULL; + struct mt7603_sta *msta = NULL; + struct mt76_wcid *wcid; + __le32 *txs_data = data; + u8 wcidx; + u8 pid; + + pid = le32_get_bits(txs_data[4], MT_TXS4_PID); + wcidx = le32_get_bits(txs_data[3], MT_TXS3_WCID); + + if (pid == MT_PACKET_ID_NO_ACK) + return; + + rcu_read_lock(); + + wcid = mt76_wcid_ptr(dev, wcidx); + if (!wcid) + goto out; + + msta = container_of(wcid, struct mt7603_sta, wcid); + sta = wcid_to_sta(wcid); + mt76_wcid_add_poll(&dev->mt76, &msta->wcid); + + if (mt7603_mac_add_txs_skb(dev, msta, pid, txs_data)) + goto out; + + if (wcidx >= MT7603_WTBL_STA || !sta) + goto out; + + if (mt7603_fill_txs(dev, msta, &info, txs_data)) { + spin_lock_bh(&dev->mt76.rx_lock); + ieee80211_tx_status_noskb(mt76_hw(dev), sta, &info); + spin_unlock_bh(&dev->mt76.rx_lock); + } + +out: + rcu_read_unlock(); +} + +void mt7603_tx_complete_skb(struct mt76_dev *mdev, struct mt76_queue_entry *e) +{ + struct mt7603_dev *dev = container_of(mdev, struct mt7603_dev, mt76); + struct sk_buff *skb = e->skb; + + if (!e->txwi) { + dev_kfree_skb_any(skb); + return; + } + + dev->tx_hang_check = 0; + mt76_tx_complete_skb(mdev, e->wcid, skb); +} + +static bool +wait_for_wpdma(struct mt7603_dev *dev) +{ + return mt76_poll(dev, MT_WPDMA_GLO_CFG, + MT_WPDMA_GLO_CFG_TX_DMA_BUSY | + MT_WPDMA_GLO_CFG_RX_DMA_BUSY, + 0, 1000); +} + +static void mt7603_pse_reset(struct mt7603_dev *dev) +{ + /* Clear previous reset result */ + if (!dev->reset_cause[RESET_CAUSE_RESET_FAILED]) + mt76_clear(dev, MT_MCU_DEBUG_RESET, MT_MCU_DEBUG_RESET_PSE_S); + + /* Reset PSE */ + mt76_set(dev, MT_MCU_DEBUG_RESET, MT_MCU_DEBUG_RESET_PSE); + + if (!mt76_poll_msec(dev, MT_MCU_DEBUG_RESET, + MT_MCU_DEBUG_RESET_PSE_S, + MT_MCU_DEBUG_RESET_PSE_S, 500)) { + dev->reset_cause[RESET_CAUSE_RESET_FAILED]++; + mt76_clear(dev, MT_MCU_DEBUG_RESET, MT_MCU_DEBUG_RESET_PSE); + } else { + dev->reset_cause[RESET_CAUSE_RESET_FAILED] = 0; + mt76_clear(dev, MT_MCU_DEBUG_RESET, MT_MCU_DEBUG_RESET_QUEUES); + } + + if (dev->reset_cause[RESET_CAUSE_RESET_FAILED] >= 3) + dev->reset_cause[RESET_CAUSE_RESET_FAILED] = 0; +} + +void mt7603_mac_dma_start(struct mt7603_dev *dev) +{ + mt7603_mac_start(dev); + + wait_for_wpdma(dev); + usleep_range(50, 100); + + mt76_set(dev, MT_WPDMA_GLO_CFG, + (MT_WPDMA_GLO_CFG_TX_DMA_EN | + MT_WPDMA_GLO_CFG_RX_DMA_EN | + FIELD_PREP(MT_WPDMA_GLO_CFG_DMA_BURST_SIZE, 3) | + MT_WPDMA_GLO_CFG_TX_WRITEBACK_DONE)); + + mt7603_irq_enable(dev, MT_INT_RX_DONE_ALL | MT_INT_TX_DONE_ALL); +} + +void mt7603_mac_start(struct mt7603_dev *dev) +{ + mt76_clear(dev, MT_ARB_SCR, + MT_ARB_SCR_TX_DISABLE | MT_ARB_SCR_RX_DISABLE); + mt76_wr(dev, MT_WF_ARB_TX_START_0, ~0); + mt76_set(dev, MT_WF_ARB_RQCR, MT_WF_ARB_RQCR_RX_START); +} + +void mt7603_mac_stop(struct mt7603_dev *dev) +{ + mt76_set(dev, MT_ARB_SCR, + MT_ARB_SCR_TX_DISABLE | MT_ARB_SCR_RX_DISABLE); + mt76_wr(dev, MT_WF_ARB_TX_START_0, 0); + mt76_clear(dev, MT_WF_ARB_RQCR, MT_WF_ARB_RQCR_RX_START); +} + +void mt7603_pse_client_reset(struct mt7603_dev *dev) +{ + u32 addr; + + addr = mt7603_reg_map(dev, MT_CLIENT_BASE_PHYS_ADDR + + MT_CLIENT_RESET_TX); + + /* Clear previous reset state */ + mt76_clear(dev, addr, + MT_CLIENT_RESET_TX_R_E_1 | + MT_CLIENT_RESET_TX_R_E_2 | + MT_CLIENT_RESET_TX_R_E_1_S | + MT_CLIENT_RESET_TX_R_E_2_S); + + /* Start PSE client TX abort */ + mt76_set(dev, MT_WPDMA_GLO_CFG, MT_WPDMA_GLO_CFG_FORCE_TX_EOF); + mt76_set(dev, addr, MT_CLIENT_RESET_TX_R_E_1); + mt76_poll_msec(dev, addr, MT_CLIENT_RESET_TX_R_E_1_S, + MT_CLIENT_RESET_TX_R_E_1_S, 500); + + mt76_set(dev, addr, MT_CLIENT_RESET_TX_R_E_2); + mt76_set(dev, MT_WPDMA_GLO_CFG, MT_WPDMA_GLO_CFG_SW_RESET); + + /* Wait for PSE client to clear TX FIFO */ + mt76_poll_msec(dev, addr, MT_CLIENT_RESET_TX_R_E_2_S, + MT_CLIENT_RESET_TX_R_E_2_S, 500); + + /* Clear PSE client TX abort state */ + mt76_clear(dev, addr, + MT_CLIENT_RESET_TX_R_E_1 | + MT_CLIENT_RESET_TX_R_E_2); +} + +static void mt7603_dma_sched_reset(struct mt7603_dev *dev) +{ + if (!is_mt7628(dev)) + return; + + mt76_set(dev, MT_SCH_4, MT_SCH_4_RESET); + mt76_clear(dev, MT_SCH_4, MT_SCH_4_RESET); +} + +static void mt7603_mac_watchdog_reset(struct mt7603_dev *dev) +{ + int beacon_int = dev->mt76.beacon_int; + u32 mask = dev->mt76.mmio.irqmask; + int i; + + ieee80211_stop_queues(dev->mt76.hw); + set_bit(MT76_RESET, &dev->mphy.state); + + /* lock/unlock all queues to ensure that no tx is pending */ + mt76_txq_schedule_all(&dev->mphy); + + mt76_worker_disable(&dev->mt76.tx_worker); + tasklet_disable(&dev->mt76.pre_tbtt_tasklet); + napi_disable(&dev->mt76.napi[0]); + napi_disable(&dev->mt76.napi[1]); + napi_disable(&dev->mt76.tx_napi); + + mutex_lock(&dev->mt76.mutex); + + mt7603_beacon_set_timer(dev, -1, 0); + + mt7603_mac_stop(dev); + + mt76_clear(dev, MT_WPDMA_GLO_CFG, + MT_WPDMA_GLO_CFG_RX_DMA_EN | MT_WPDMA_GLO_CFG_TX_DMA_EN | + MT_WPDMA_GLO_CFG_TX_WRITEBACK_DONE); + usleep_range(1000, 2000); + + mt7603_irq_disable(dev, mask); + + mt7603_pse_client_reset(dev); + + mt76_queue_tx_cleanup(dev, dev->mt76.q_mcu[MT_MCUQ_WM], true); + for (i = 0; i < __MT_TXQ_MAX; i++) + mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[i], true); + + mt7603_dma_sched_reset(dev); + + mt76_tx_status_check(&dev->mt76, true); + + mt76_for_each_q_rx(&dev->mt76, i) { + mt76_queue_rx_reset(dev, i); + } + + if (dev->reset_cause[RESET_CAUSE_RESET_FAILED] || + dev->cur_reset_cause == RESET_CAUSE_RX_PSE_BUSY) + mt7603_pse_reset(dev); + + if (!dev->reset_cause[RESET_CAUSE_RESET_FAILED]) { + mt7603_mac_dma_start(dev); + + mt7603_irq_enable(dev, mask); + + clear_bit(MT76_RESET, &dev->mphy.state); + } + + mutex_unlock(&dev->mt76.mutex); + + mt76_worker_enable(&dev->mt76.tx_worker); + + tasklet_enable(&dev->mt76.pre_tbtt_tasklet); + mt7603_beacon_set_timer(dev, -1, beacon_int); + + napi_enable(&dev->mt76.tx_napi); + napi_enable(&dev->mt76.napi[0]); + napi_enable(&dev->mt76.napi[1]); + + local_bh_disable(); + napi_schedule(&dev->mt76.tx_napi); + napi_schedule(&dev->mt76.napi[0]); + napi_schedule(&dev->mt76.napi[1]); + local_bh_enable(); + + ieee80211_wake_queues(dev->mt76.hw); + mt76_txq_schedule_all(&dev->mphy); +} + +static u32 mt7603_dma_debug(struct mt7603_dev *dev, u8 index) +{ + u32 val; + + mt76_wr(dev, MT_WPDMA_DEBUG, + FIELD_PREP(MT_WPDMA_DEBUG_IDX, index) | + MT_WPDMA_DEBUG_SEL); + + val = mt76_rr(dev, MT_WPDMA_DEBUG); + return FIELD_GET(MT_WPDMA_DEBUG_VALUE, val); +} + +static bool mt7603_rx_fifo_busy(struct mt7603_dev *dev) +{ + if (is_mt7628(dev)) + return mt7603_dma_debug(dev, 9) & BIT(9); + + return mt7603_dma_debug(dev, 2) & BIT(8); +} + +static bool mt7603_rx_dma_busy(struct mt7603_dev *dev) +{ + if (!(mt76_rr(dev, MT_WPDMA_GLO_CFG) & MT_WPDMA_GLO_CFG_RX_DMA_BUSY)) + return false; + + return mt7603_rx_fifo_busy(dev); +} + +static bool mt7603_tx_dma_busy(struct mt7603_dev *dev) +{ + u32 val; + + if (!(mt76_rr(dev, MT_WPDMA_GLO_CFG) & MT_WPDMA_GLO_CFG_TX_DMA_BUSY)) + return false; + + val = mt7603_dma_debug(dev, 9); + return (val & BIT(8)) && (val & 0xf) != 0xf; +} + +static bool mt7603_tx_hang(struct mt7603_dev *dev) +{ + struct mt76_queue *q; + u32 dma_idx, prev_dma_idx; + int i; + + for (i = 0; i < 4; i++) { + q = dev->mphy.q_tx[i]; + + if (!q->queued) + continue; + + prev_dma_idx = dev->tx_dma_idx[i]; + dma_idx = readl(&q->regs->dma_idx); + dev->tx_dma_idx[i] = dma_idx; + + if (dma_idx == prev_dma_idx && + dma_idx != readl(&q->regs->cpu_idx)) + break; + } + + return i < 4; +} + +static bool mt7603_rx_pse_busy(struct mt7603_dev *dev) +{ + u32 addr, val; + + if (mt7603_rx_fifo_busy(dev)) + goto out; + + addr = mt7603_reg_map(dev, MT_CLIENT_BASE_PHYS_ADDR + MT_CLIENT_STATUS); + mt76_wr(dev, addr, 3); + val = mt76_rr(dev, addr) >> 16; + + if (!(val & BIT(0))) + return false; + + if (is_mt7628(dev)) + val &= 0xa000; + else + val &= 0x8000; + if (!val) + return false; + +out: + if (mt76_rr(dev, MT_INT_SOURCE_CSR) & + (MT_INT_RX_DONE(0) | MT_INT_RX_DONE(1))) + return false; + + return true; +} + +static bool +mt7603_watchdog_check(struct mt7603_dev *dev, u8 *counter, + enum mt7603_reset_cause cause, + bool (*check)(struct mt7603_dev *dev)) +{ + if (dev->reset_test == cause + 1) { + dev->reset_test = 0; + goto trigger; + } + + if (check) { + if (!check(dev) && *counter < MT7603_WATCHDOG_TIMEOUT) { + *counter = 0; + return false; + } + + (*counter)++; + } + + if (*counter < MT7603_WATCHDOG_TIMEOUT) + return false; +trigger: + dev->cur_reset_cause = cause; + dev->reset_cause[cause]++; + return true; +} + +void mt7603_update_channel(struct mt76_phy *mphy) +{ + struct mt7603_dev *dev = container_of(mphy->dev, struct mt7603_dev, mt76); + struct mt76_channel_state *state; + + state = mphy->chan_state; + state->cc_busy += mt76_rr(dev, MT_MIB_STAT_CCA); +} + +void +mt7603_edcca_set_strict(struct mt7603_dev *dev, bool val) +{ + u32 rxtd_6 = 0xd7c80000; + + if (val == dev->ed_strict_mode) + return; + + dev->ed_strict_mode = val; + + /* Ensure that ED/CCA does not trigger if disabled */ + if (!dev->ed_monitor) + rxtd_6 |= FIELD_PREP(MT_RXTD_6_CCAED_TH, 0x34); + else + rxtd_6 |= FIELD_PREP(MT_RXTD_6_CCAED_TH, 0x7d); + + if (dev->ed_monitor && !dev->ed_strict_mode) + rxtd_6 |= FIELD_PREP(MT_RXTD_6_ACI_TH, 0x0f); + else + rxtd_6 |= FIELD_PREP(MT_RXTD_6_ACI_TH, 0x10); + + mt76_wr(dev, MT_RXTD(6), rxtd_6); + + mt76_rmw_field(dev, MT_RXTD(13), MT_RXTD_13_ACI_TH_EN, + dev->ed_monitor && !dev->ed_strict_mode); +} + +static void +mt7603_edcca_check(struct mt7603_dev *dev) +{ + u32 val = mt76_rr(dev, MT_AGC(41)); + ktime_t cur_time; + int rssi0, rssi1; + u32 active; + u32 ed_busy; + + if (!dev->ed_monitor) + return; + + rssi0 = FIELD_GET(MT_AGC_41_RSSI_0, val); + if (rssi0 > 128) + rssi0 -= 256; + + if (dev->mphy.antenna_mask & BIT(1)) { + rssi1 = FIELD_GET(MT_AGC_41_RSSI_1, val); + if (rssi1 > 128) + rssi1 -= 256; + } else { + rssi1 = rssi0; + } + + if (max(rssi0, rssi1) >= -40 && + dev->ed_strong_signal < MT7603_EDCCA_BLOCK_TH) + dev->ed_strong_signal++; + else if (dev->ed_strong_signal > 0) + dev->ed_strong_signal--; + + cur_time = ktime_get_boottime(); + ed_busy = mt76_rr(dev, MT_MIB_STAT_ED) & MT_MIB_STAT_ED_MASK; + + active = ktime_to_us(ktime_sub(cur_time, dev->ed_time)); + dev->ed_time = cur_time; + + if (!active) + return; + + if (100 * ed_busy / active > 90) { + if (dev->ed_trigger < 0) + dev->ed_trigger = 0; + dev->ed_trigger++; + } else { + if (dev->ed_trigger > 0) + dev->ed_trigger = 0; + dev->ed_trigger--; + } + + if (dev->ed_trigger > MT7603_EDCCA_BLOCK_TH || + dev->ed_strong_signal < MT7603_EDCCA_BLOCK_TH / 2) { + mt7603_edcca_set_strict(dev, true); + } else if (dev->ed_trigger < -MT7603_EDCCA_BLOCK_TH) { + mt7603_edcca_set_strict(dev, false); + } + + if (dev->ed_trigger > MT7603_EDCCA_BLOCK_TH) + dev->ed_trigger = MT7603_EDCCA_BLOCK_TH; + else if (dev->ed_trigger < -MT7603_EDCCA_BLOCK_TH) + dev->ed_trigger = -MT7603_EDCCA_BLOCK_TH; +} + +void mt7603_cca_stats_reset(struct mt7603_dev *dev) +{ + mt76_set(dev, MT_PHYCTRL(2), MT_PHYCTRL_2_STATUS_RESET); + mt76_clear(dev, MT_PHYCTRL(2), MT_PHYCTRL_2_STATUS_RESET); + mt76_set(dev, MT_PHYCTRL(2), MT_PHYCTRL_2_STATUS_EN); +} + +static void +mt7603_adjust_sensitivity(struct mt7603_dev *dev) +{ + u32 agc0 = dev->agc0, agc3 = dev->agc3; + u32 adj; + + if (!dev->sensitivity || dev->sensitivity < -100) { + dev->sensitivity = 0; + } else if (dev->sensitivity <= -84) { + adj = 7 + (dev->sensitivity + 92) / 2; + + agc0 = 0x56f0076f; + agc0 |= adj << 12; + agc0 |= adj << 16; + agc3 = 0x81d0d5e3; + } else if (dev->sensitivity <= -72) { + adj = 7 + (dev->sensitivity + 80) / 2; + + agc0 = 0x6af0006f; + agc0 |= adj << 8; + agc0 |= adj << 12; + agc0 |= adj << 16; + + agc3 = 0x8181d5e3; + } else { + if (dev->sensitivity > -54) + dev->sensitivity = -54; + + adj = 7 + (dev->sensitivity + 80) / 2; + + agc0 = 0x7ff0000f; + agc0 |= adj << 4; + agc0 |= adj << 8; + agc0 |= adj << 12; + agc0 |= adj << 16; + + agc3 = 0x818181e3; + } + + mt76_wr(dev, MT_AGC(0), agc0); + mt76_wr(dev, MT_AGC1(0), agc0); + + mt76_wr(dev, MT_AGC(3), agc3); + mt76_wr(dev, MT_AGC1(3), agc3); +} + +static void +mt7603_false_cca_check(struct mt7603_dev *dev) +{ + int pd_cck, pd_ofdm, mdrdy_cck, mdrdy_ofdm; + int false_cca; + int min_signal; + u32 val; + + if (!dev->dynamic_sensitivity) + return; + + val = mt76_rr(dev, MT_PHYCTRL_STAT_PD); + pd_cck = FIELD_GET(MT_PHYCTRL_STAT_PD_CCK, val); + pd_ofdm = FIELD_GET(MT_PHYCTRL_STAT_PD_OFDM, val); + + val = mt76_rr(dev, MT_PHYCTRL_STAT_MDRDY); + mdrdy_cck = FIELD_GET(MT_PHYCTRL_STAT_MDRDY_CCK, val); + mdrdy_ofdm = FIELD_GET(MT_PHYCTRL_STAT_MDRDY_OFDM, val); + + dev->false_cca_ofdm = pd_ofdm - mdrdy_ofdm; + dev->false_cca_cck = pd_cck - mdrdy_cck; + + mt7603_cca_stats_reset(dev); + + min_signal = mt76_get_min_avg_rssi(&dev->mt76, 0); + if (!min_signal) { + dev->sensitivity = 0; + dev->last_cca_adj = jiffies; + goto out; + } + + min_signal -= 15; + + false_cca = dev->false_cca_ofdm + dev->false_cca_cck; + if (false_cca > 600 && + dev->sensitivity < -100 + dev->sensitivity_limit) { + if (!dev->sensitivity) + dev->sensitivity = -92; + else + dev->sensitivity += 2; + dev->last_cca_adj = jiffies; + } else if (false_cca < 100 || + time_after(jiffies, dev->last_cca_adj + 10 * HZ)) { + dev->last_cca_adj = jiffies; + if (!dev->sensitivity) + goto out; + + dev->sensitivity -= 2; + } + + if (dev->sensitivity && dev->sensitivity > min_signal) { + dev->sensitivity = min_signal; + dev->last_cca_adj = jiffies; + } + +out: + mt7603_adjust_sensitivity(dev); +} + +/* + * Releasing buffered frames turns off the PSE redirect for a station, since + * the released frames would otherwise be looped back into the driver PS queue + * again. mac80211 never tells us when the service period is over, so hardware + * buffering has to be re-armed here for every station that is still known to + * be asleep. Waiting for the PSD queue to drain makes sure that the released + * frames have already passed the redirect stage. + */ +static void +mt7603_mac_ps_check(struct mt7603_dev *dev) +{ + int i; + + if (dev->mphy.q_tx[MT_TXQ_PSD]->queued) + return; + + rcu_read_lock(); + for (i = 0; i < MT7603_WTBL_STA; i++) { + struct mt76_wcid *wcid = mt76_wcid_ptr(dev, i); + struct mt7603_sta *msta; + + if (!wcid || !wcid->sta) + continue; + + msta = container_of(wcid, struct mt7603_sta, wcid); + if (msta->ps || !msta->ps_sleeping) + continue; + + mt7603_wtbl_restore_ps(dev, msta); + } + rcu_read_unlock(); +} + +void mt7603_mac_work(struct work_struct *work) +{ + struct mt7603_dev *dev = container_of(work, struct mt7603_dev, + mphy.mac_work.work); + bool reset = false; + int i, idx; + + mt76_tx_status_check(&dev->mt76, false); + + mutex_lock(&dev->mt76.mutex); + + dev->mphy.mac_work_count++; + mt76_update_survey(&dev->mphy); + mt7603_edcca_check(dev); + mt7603_mac_ps_check(dev); + + for (i = 0, idx = 0; i < 2; i++) { + u32 val = mt76_rr(dev, MT_TX_AGG_CNT(i)); + + dev->mphy.aggr_stats[idx++] += val & 0xffff; + dev->mphy.aggr_stats[idx++] += val >> 16; + } + + if (dev->mphy.mac_work_count == 10) + mt7603_false_cca_check(dev); + + if (mt7603_watchdog_check(dev, &dev->rx_pse_check, + RESET_CAUSE_RX_PSE_BUSY, + mt7603_rx_pse_busy) || + mt7603_watchdog_check(dev, &dev->beacon_check, + RESET_CAUSE_BEACON_STUCK, + NULL) || + mt7603_watchdog_check(dev, &dev->tx_hang_check, + RESET_CAUSE_TX_HANG, + mt7603_tx_hang) || + mt7603_watchdog_check(dev, &dev->tx_dma_check, + RESET_CAUSE_TX_BUSY, + mt7603_tx_dma_busy) || + mt7603_watchdog_check(dev, &dev->rx_dma_check, + RESET_CAUSE_RX_BUSY, + mt7603_rx_dma_busy) || + mt7603_watchdog_check(dev, &dev->mcu_hang, + RESET_CAUSE_MCU_HANG, + NULL) || + dev->reset_cause[RESET_CAUSE_RESET_FAILED]) { + dev->beacon_check = 0; + dev->tx_dma_check = 0; + dev->tx_hang_check = 0; + dev->rx_dma_check = 0; + dev->rx_pse_check = 0; + dev->mcu_hang = 0; + dev->rx_dma_idx = ~0; + memset(dev->tx_dma_idx, 0xff, sizeof(dev->tx_dma_idx)); + reset = true; + dev->mphy.mac_work_count = 0; + } + + if (dev->mphy.mac_work_count >= 10) + dev->mphy.mac_work_count = 0; + + mutex_unlock(&dev->mt76.mutex); + + if (reset) + mt7603_mac_watchdog_reset(dev); + + ieee80211_queue_delayed_work(mt76_hw(dev), &dev->mphy.mac_work, + msecs_to_jiffies(MT7603_WATCHDOG_TIME)); +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/mac.h b/drivers/net/wireless/mediatek/mt76/mt7603/mac.h new file mode 100644 index 000000000..9f5fab51f --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7603/mac.h @@ -0,0 +1,242 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ + +#ifndef __MT7603_MAC_H +#define __MT7603_MAC_H + +#define MT_RXD0_LENGTH GENMASK(15, 0) +#define MT_RXD0_PKT_TYPE GENMASK(31, 29) + +#define MT_RXD0_NORMAL_ETH_TYPE_OFS GENMASK(22, 16) +#define MT_RXD0_NORMAL_IP_SUM BIT(23) +#define MT_RXD0_NORMAL_UDP_TCP_SUM BIT(24) +#define MT_RXD0_NORMAL_GROUP_1 BIT(25) +#define MT_RXD0_NORMAL_GROUP_2 BIT(26) +#define MT_RXD0_NORMAL_GROUP_3 BIT(27) +#define MT_RXD0_NORMAL_GROUP_4 BIT(28) + +enum rx_pkt_type { + PKT_TYPE_TXS = 0, + PKT_TYPE_TXRXV = 1, + PKT_TYPE_NORMAL = 2, + PKT_TYPE_RX_DUP_RFB = 3, + PKT_TYPE_RX_TMR = 4, + PKT_TYPE_RETRIEVE = 5, + PKT_TYPE_RX_EVENT = 7, +}; + +#define MT_RXD1_NORMAL_BSSID GENMASK(31, 26) +#define MT_RXD1_NORMAL_PAYLOAD_FORMAT GENMASK(25, 24) +#define MT_RXD1_NORMAL_HDR_TRANS BIT(23) +#define MT_RXD1_NORMAL_HDR_OFFSET BIT(22) +#define MT_RXD1_NORMAL_MAC_HDR_LEN GENMASK(21, 16) +#define MT_RXD1_NORMAL_CH_FREQ GENMASK(15, 8) +#define MT_RXD1_NORMAL_KEY_ID GENMASK(7, 6) +#define MT_RXD1_NORMAL_BEACON_UC BIT(5) +#define MT_RXD1_NORMAL_BEACON_MC BIT(4) +#define MT_RXD1_NORMAL_BCAST BIT(3) +#define MT_RXD1_NORMAL_MCAST BIT(2) +#define MT_RXD1_NORMAL_U2M BIT(1) +#define MT_RXD1_NORMAL_HTC_VLD BIT(0) + +#define MT_RXD2_NORMAL_NON_AMPDU BIT(31) +#define MT_RXD2_NORMAL_NON_AMPDU_SUB BIT(30) +#define MT_RXD2_NORMAL_NDATA BIT(29) +#define MT_RXD2_NORMAL_NULL_FRAME BIT(28) +#define MT_RXD2_NORMAL_FRAG BIT(27) +#define MT_RXD2_NORMAL_UDF_VALID BIT(26) +#define MT_RXD2_NORMAL_LLC_MIS BIT(25) +#define MT_RXD2_NORMAL_MAX_LEN_ERROR BIT(24) +#define MT_RXD2_NORMAL_AMSDU_ERR BIT(23) +#define MT_RXD2_NORMAL_LEN_MISMATCH BIT(22) +#define MT_RXD2_NORMAL_TKIP_MIC_ERR BIT(21) +#define MT_RXD2_NORMAL_ICV_ERR BIT(20) +#define MT_RXD2_NORMAL_CLM BIT(19) +#define MT_RXD2_NORMAL_CM BIT(18) +#define MT_RXD2_NORMAL_FCS_ERR BIT(17) +#define MT_RXD2_NORMAL_SW_BIT BIT(16) +#define MT_RXD2_NORMAL_SEC_MODE GENMASK(15, 12) +#define MT_RXD2_NORMAL_TID GENMASK(11, 8) +#define MT_RXD2_NORMAL_WLAN_IDX GENMASK(7, 0) + +#define MT_RXD3_NORMAL_PF_STS GENMASK(31, 30) +#define MT_RXD3_NORMAL_PF_MODE BIT(29) +#define MT_RXD3_NORMAL_CLS_BITMAP GENMASK(28, 19) +#define MT_RXD3_NORMAL_WOL GENMASK(18, 14) +#define MT_RXD3_NORMAL_MAGIC_PKT BIT(13) +#define MT_RXD3_NORMAL_OFLD GENMASK(12, 11) +#define MT_RXD3_NORMAL_CLS BIT(10) +#define MT_RXD3_NORMAL_PATTERN_DROP BIT(9) +#define MT_RXD3_NORMAL_TSF_COMPARE_LOSS BIT(8) +#define MT_RXD3_NORMAL_RXV_SEQ GENMASK(7, 0) + +#define MT_RXV1_VHTA1_B5_B4 GENMASK(31, 30) +#define MT_RXV1_VHTA2_B8_B1 GENMASK(29, 22) +#define MT_RXV1_HT_NO_SOUND BIT(21) +#define MT_RXV1_HT_SMOOTH BIT(20) +#define MT_RXV1_HT_SHORT_GI BIT(19) +#define MT_RXV1_HT_AGGR BIT(18) +#define MT_RXV1_VHTA1_B22 BIT(17) +#define MT_RXV1_FRAME_MODE GENMASK(16, 15) +#define MT_RXV1_TX_MODE GENMASK(14, 12) +#define MT_RXV1_HT_EXT_LTF GENMASK(11, 10) +#define MT_RXV1_HT_AD_CODE BIT(9) +#define MT_RXV1_HT_STBC GENMASK(8, 7) +#define MT_RXV1_TX_RATE GENMASK(6, 0) + +#define MT_RXV2_VHTA1_B16_B6 GENMASK(31, 21) +#define MT_RXV2_LENGTH GENMASK(20, 0) + +#define MT_RXV3_F_AGC1_CAL_GAIN GENMASK(31, 29) +#define MT_RXV3_F_AGC1_EQ_CAL BIT(28) +#define MT_RXV3_RCPI1 GENMASK(27, 20) +#define MT_RXV3_F_AGC0_CAL_GAIN GENMASK(19, 17) +#define MT_RXV3_F_AGC0_EQ_CAL BIT(16) +#define MT_RXV3_RCPI0 GENMASK(15, 8) +#define MT_RXV3_SEL_ANT BIT(7) +#define MT_RXV3_ACI_DET_X BIT(6) +#define MT_RXV3_OFDM_FREQ_TRANS_DETECT BIT(5) +#define MT_RXV3_VHTA1_B21_B17 GENMASK(4, 0) + +#define MT_RXV4_F_AGC_CAL_GAIN GENMASK(31, 29) +#define MT_RXV4_F_AGC2_EQ_CAL BIT(28) +#define MT_RXV4_IB_RSSI1 GENMASK(27, 20) +#define MT_RXV4_F_AGC_LPF_GAIN_X GENMASK(19, 16) +#define MT_RXV4_WB_RSSI_X GENMASK(15, 8) +#define MT_RXV4_IB_RSSI0 GENMASK(7, 0) + +#define MT_RXV5_LTF_SNR0 GENMASK(31, 26) +#define MT_RXV5_LTF_PROC_TIME GENMASK(25, 19) +#define MT_RXV5_FOE GENMASK(18, 7) +#define MT_RXV5_C_AGC_SATE GENMASK(6, 4) +#define MT_RXV5_F_AGC_LNA_GAIN_0 GENMASK(3, 2) +#define MT_RXV5_F_AGC_LNA_GAIN_1 GENMASK(1, 0) + +#define MT_RXV6_C_AGC_STATE GENMASK(30, 28) +#define MT_RXV6_NS_TS_FIELD GENMASK(27, 25) +#define MT_RXV6_RX_VALID BIT(24) +#define MT_RXV6_NF2 GENMASK(23, 16) +#define MT_RXV6_NF1 GENMASK(15, 8) +#define MT_RXV6_NF0 GENMASK(7, 0) + +enum mt7603_tx_header_format { + MT_HDR_FORMAT_802_3, + MT_HDR_FORMAT_CMD, + MT_HDR_FORMAT_802_11, + MT_HDR_FORMAT_802_11_EXT, +}; + +#define MT_TXD_SIZE (8 * 4) + +#define MT_TXD0_P_IDX BIT(31) +#define MT_TXD0_Q_IDX GENMASK(30, 27) +#define MT_TXD0_UTXB BIT(26) +#define MT_TXD0_UNXV BIT(25) +#define MT_TXD0_UDP_TCP_SUM BIT(24) +#define MT_TXD0_IP_SUM BIT(23) +#define MT_TXD0_ETH_TYPE_OFFSET GENMASK(22, 16) +#define MT_TXD0_TX_BYTES GENMASK(15, 0) + +#define MT_TXD1_OWN_MAC GENMASK(31, 26) +#define MT_TXD1_PROTECTED BIT(23) +#define MT_TXD1_TID GENMASK(22, 20) +#define MT_TXD1_NO_ACK BIT(19) +#define MT_TXD1_HDR_PAD GENMASK(18, 16) +#define MT_TXD1_LONG_FORMAT BIT(15) +#define MT_TXD1_HDR_FORMAT GENMASK(14, 13) +#define MT_TXD1_HDR_INFO GENMASK(12, 8) +#define MT_TXD1_WLAN_IDX GENMASK(7, 0) + +#define MT_TXD2_FIX_RATE BIT(31) +#define MT_TXD2_TIMING_MEASURE BIT(30) +#define MT_TXD2_BA_DISABLE BIT(29) +#define MT_TXD2_POWER_OFFSET GENMASK(28, 24) +#define MT_TXD2_MAX_TX_TIME GENMASK(23, 16) +#define MT_TXD2_FRAG GENMASK(15, 14) +#define MT_TXD2_HTC_VLD BIT(13) +#define MT_TXD2_DURATION BIT(12) +#define MT_TXD2_BIP BIT(11) +#define MT_TXD2_MULTICAST BIT(10) +#define MT_TXD2_RTS BIT(9) +#define MT_TXD2_SOUNDING BIT(8) +#define MT_TXD2_NDPA BIT(7) +#define MT_TXD2_NDP BIT(6) +#define MT_TXD2_FRAME_TYPE GENMASK(5, 4) +#define MT_TXD2_SUB_TYPE GENMASK(3, 0) + +#define MT_TXD3_SN_VALID BIT(31) +#define MT_TXD3_PN_VALID BIT(30) +#define MT_TXD3_SEQ GENMASK(27, 16) +#define MT_TXD3_REM_TX_COUNT GENMASK(15, 11) +#define MT_TXD3_TX_COUNT GENMASK(10, 6) + +#define MT_TXD4_PN_LOW GENMASK(31, 0) + +#define MT_TXD5_PN_HIGH GENMASK(31, 16) +#define MT_TXD5_SW_POWER_MGMT BIT(13) +#define MT_TXD5_BA_SEQ_CTRL BIT(12) +#define MT_TXD5_DA_SELECT BIT(11) +#define MT_TXD5_TX_STATUS_HOST BIT(10) +#define MT_TXD5_TX_STATUS_MCU BIT(9) +#define MT_TXD5_TX_STATUS_FMT BIT(8) +#define MT_TXD5_PID GENMASK(7, 0) + +#define MT_TXD6_SGI BIT(31) +#define MT_TXD6_LDPC BIT(30) +#define MT_TXD6_TX_RATE GENMASK(29, 18) +#define MT_TXD6_I_TXBF BIT(17) +#define MT_TXD6_E_TXBF BIT(16) +#define MT_TXD6_DYN_BW BIT(15) +#define MT_TXD6_ANT_PRI GENMASK(14, 12) +#define MT_TXD6_SPE_EN BIT(11) +#define MT_TXD6_FIXED_BW BIT(10) +#define MT_TXD6_BW GENMASK(9, 8) +#define MT_TXD6_ANT_ID GENMASK(7, 2) +#define MT_TXD6_FIXED_RATE BIT(0) + +#define MT_TX_RATE_STBC BIT(11) +#define MT_TX_RATE_NSS GENMASK(10, 9) +#define MT_TX_RATE_MODE GENMASK(8, 6) +#define MT_TX_RATE_IDX GENMASK(5, 0) + +#define MT_TXS0_ANTENNA GENMASK(31, 26) +#define MT_TXS0_TID GENMASK(25, 22) +#define MT_TXS0_BA_ERROR BIT(22) +#define MT_TXS0_PS_FLAG BIT(21) +#define MT_TXS0_TXOP_TIMEOUT BIT(20) +#define MT_TXS0_BIP_ERROR BIT(19) + +#define MT_TXS0_QUEUE_TIMEOUT BIT(18) +#define MT_TXS0_RTS_TIMEOUT BIT(17) +#define MT_TXS0_ACK_TIMEOUT BIT(16) +#define MT_TXS0_ACK_ERROR_MASK GENMASK(18, 16) + +#define MT_TXS0_TX_STATUS_HOST BIT(15) +#define MT_TXS0_TX_STATUS_MCU BIT(14) +#define MT_TXS0_TXS_FORMAT BIT(13) +#define MT_TXS0_FIXED_RATE BIT(12) +#define MT_TXS0_TX_RATE GENMASK(11, 0) + +#define MT_TXS1_F0_TIMESTAMP GENMASK(31, 0) +#define MT_TXS1_F1_NOISE_2 GENMASK(23, 16) +#define MT_TXS1_F1_NOISE_1 GENMASK(15, 8) +#define MT_TXS1_F1_NOISE_0 GENMASK(7, 0) + +#define MT_TXS2_F0_FRONT_TIME GENMASK(24, 0) +#define MT_TXS2_F1_RCPI_2 GENMASK(23, 16) +#define MT_TXS2_F1_RCPI_1 GENMASK(15, 8) +#define MT_TXS2_F1_RCPI_0 GENMASK(7, 0) + +#define MT_TXS3_WCID GENMASK(31, 24) +#define MT_TXS3_RXV_SEQNO GENMASK(23, 16) +#define MT_TXS3_TX_DELAY GENMASK(15, 0) + +#define MT_TXS4_LAST_TX_RATE GENMASK(31, 29) +#define MT_TXS4_TX_COUNT GENMASK(28, 24) +#define MT_TXS4_AMPDU BIT(23) +#define MT_TXS4_ACKED_MPDU BIT(22) +#define MT_TXS4_PID GENMASK(21, 14) +#define MT_TXS4_BW GENMASK(13, 12) +#define MT_TXS4_F0_SEQNO GENMASK(11, 0) +#define MT_TXS4_F1_TSSI GENMASK(11, 0) + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/main.c b/drivers/net/wireless/mediatek/mt76/mt7603/main.c new file mode 100644 index 000000000..757664e9e --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7603/main.c @@ -0,0 +1,818 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear + +#include +#include +#include +#include +#include "mt7603.h" +#include "mac.h" +#include "eeprom.h" + +static int +mt7603_start(struct ieee80211_hw *hw) +{ + struct mt7603_dev *dev = hw->priv; + + mt7603_mac_reset_counters(dev); + mt7603_mac_start(dev); + dev->mphy.survey_time = ktime_get_boottime(); + set_bit(MT76_STATE_RUNNING, &dev->mphy.state); + mt7603_mac_work(&dev->mphy.mac_work.work); + + return 0; +} + +static void +mt7603_stop(struct ieee80211_hw *hw, bool suspend) +{ + struct mt7603_dev *dev = hw->priv; + + clear_bit(MT76_STATE_RUNNING, &dev->mphy.state); + cancel_delayed_work_sync(&dev->mphy.mac_work); + mt7603_mac_stop(dev); +} + +static int +mt7603_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + struct mt7603_vif *mvif = (struct mt7603_vif *)vif->drv_priv; + struct mt7603_dev *dev = hw->priv; + struct mt76_txq *mtxq; + u8 bc_addr[ETH_ALEN]; + int idx; + int ret = 0; + + mutex_lock(&dev->mt76.mutex); + + mvif->idx = __ffs64(~dev->mt76.vif_mask); + if (mvif->idx >= MT7603_MAX_INTERFACES) { + ret = -ENOSPC; + goto out; + } + + mt76_wr(dev, MT_MAC_ADDR0(mvif->idx), + get_unaligned_le32(vif->addr)); + mt76_wr(dev, MT_MAC_ADDR1(mvif->idx), + (get_unaligned_le16(vif->addr + 4) | + MT_MAC_ADDR1_VALID)); + + if (vif->type == NL80211_IFTYPE_AP) { + mt76_wr(dev, MT_BSSID0(mvif->idx), + get_unaligned_le32(vif->addr)); + mt76_wr(dev, MT_BSSID1(mvif->idx), + (get_unaligned_le16(vif->addr + 4) | + MT_BSSID1_VALID)); + } + + idx = MT7603_WTBL_RESERVED - 1 - mvif->idx; + dev->mt76.vif_mask |= BIT_ULL(mvif->idx); + mvif->sta.wcid.idx = idx; + mvif->sta.vif = mvif; + mt76_wcid_init(&mvif->sta.wcid, 0); + + eth_broadcast_addr(bc_addr); + mt7603_wtbl_init(dev, idx, mvif->idx, bc_addr); + + mtxq = (struct mt76_txq *)vif->txq->drv_priv; + mtxq->wcid = idx; + rcu_assign_pointer(dev->mt76.wcid[idx], &mvif->sta.wcid); + +out: + mutex_unlock(&dev->mt76.mutex); + + return ret; +} + +static void +mt7603_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + struct mt7603_vif *mvif = (struct mt7603_vif *)vif->drv_priv; + struct mt7603_sta *msta = &mvif->sta; + struct mt7603_dev *dev = hw->priv; + int idx = msta->wcid.idx; + + mt76_wr(dev, MT_MAC_ADDR0(mvif->idx), 0); + mt76_wr(dev, MT_MAC_ADDR1(mvif->idx), 0); + mt76_wr(dev, MT_BSSID0(mvif->idx), 0); + mt76_wr(dev, MT_BSSID1(mvif->idx), 0); + mt7603_beacon_set_timer(dev, mvif->idx, 0); + + rcu_assign_pointer(dev->mt76.wcid[idx], NULL); + + spin_lock_bh(&dev->mt76.sta_poll_lock); + if (!list_empty(&msta->wcid.poll_list)) + list_del_init(&msta->wcid.poll_list); + spin_unlock_bh(&dev->mt76.sta_poll_lock); + + mutex_lock(&dev->mt76.mutex); + dev->mt76.vif_mask &= ~BIT_ULL(mvif->idx); + mutex_unlock(&dev->mt76.mutex); + + mt76_wcid_cleanup(&dev->mt76, &mvif->sta.wcid); +} + +void mt7603_init_edcca(struct mt7603_dev *dev) +{ + /* Set lower signal level to -65dBm */ + mt76_rmw_field(dev, MT_RXTD(8), MT_RXTD_8_LOWER_SIGNAL, 0x23); + + /* clear previous energy detect monitor results */ + mt76_rr(dev, MT_MIB_STAT_ED); + + if (dev->ed_monitor) + mt76_set(dev, MT_MIB_CTL, MT_MIB_CTL_ED_TIME); + else + mt76_clear(dev, MT_MIB_CTL, MT_MIB_CTL_ED_TIME); + + dev->ed_strict_mode = 0xff; + dev->ed_strong_signal = 0; + dev->ed_time = ktime_get_boottime(); + + mt7603_edcca_set_strict(dev, false); +} + +int mt7603_set_channel(struct mt76_phy *mphy) +{ + struct mt7603_dev *dev = container_of(mphy->dev, struct mt7603_dev, mt76); + struct cfg80211_chan_def *def = &mphy->chandef; + + u8 *rssi_data = (u8 *)dev->mt76.eeprom.data; + int idx, ret; + u8 bw = MT_BW_20; + bool failed = false; + + tasklet_disable(&dev->mt76.pre_tbtt_tasklet); + + mt7603_beacon_set_timer(dev, -1, 0); + mt7603_mac_stop(dev); + + if (def->width == NL80211_CHAN_WIDTH_40) + bw = MT_BW_40; + + mt76_rmw_field(dev, MT_AGG_BWCR, MT_AGG_BWCR_BW, bw); + ret = mt7603_mcu_set_channel(dev); + if (ret) { + failed = true; + goto out; + } + + if (def->chan->band == NL80211_BAND_5GHZ) { + idx = 1; + rssi_data += MT_EE_RSSI_OFFSET_5G; + } else { + idx = 0; + rssi_data += MT_EE_RSSI_OFFSET_2G; + } + + memcpy(dev->rssi_offset, rssi_data, sizeof(dev->rssi_offset)); + + idx |= (def->chan - + mt76_hw(dev)->wiphy->bands[def->chan->band]->channels) << 1; + mt76_wr(dev, MT_WF_RMAC_CH_FREQ, idx); + mt7603_mac_set_timing(dev); + mt7603_mac_start(dev); + + ieee80211_queue_delayed_work(mt76_hw(dev), &dev->mphy.mac_work, + msecs_to_jiffies(MT7603_WATCHDOG_TIME)); + + /* reset channel stats */ + mt76_clear(dev, MT_MIB_CTL, MT_MIB_CTL_READ_CLR_DIS); + mt76_set(dev, MT_MIB_CTL, + MT_MIB_CTL_CCA_NAV_TX | MT_MIB_CTL_PSCCA_TIME); + mt76_rr(dev, MT_MIB_STAT_CCA); + mt7603_cca_stats_reset(dev); + + dev->mphy.survey_time = ktime_get_boottime(); + + mt7603_init_edcca(dev); + +out: + if (!mphy->offchannel) + mt7603_beacon_set_timer(dev, -1, dev->mt76.beacon_int); + + tasklet_enable(&dev->mt76.pre_tbtt_tasklet); + + if (failed) + mt7603_mac_work(&dev->mphy.mac_work.work); + + return ret; +} + +static int mt7603_set_sar_specs(struct ieee80211_hw *hw, + const struct cfg80211_sar_specs *sar) +{ + struct mt7603_dev *dev = hw->priv; + struct mt76_phy *mphy = &dev->mphy; + int err; + + if (!cfg80211_chandef_valid(&mphy->chandef)) + return -EINVAL; + + err = mt76_init_sar_power(hw, sar); + if (err) + return err; + + return mt76_update_channel(mphy); +} + +static int +mt7603_config(struct ieee80211_hw *hw, int radio_idx, u32 changed) +{ + struct mt7603_dev *dev = hw->priv; + int ret = 0; + + if (changed & (IEEE80211_CONF_CHANGE_CHANNEL | + IEEE80211_CONF_CHANGE_POWER)) + ret = mt76_update_channel(&dev->mphy); + + if (changed & IEEE80211_CONF_CHANGE_MONITOR) { + mutex_lock(&dev->mt76.mutex); + + if (!(hw->conf.flags & IEEE80211_CONF_MONITOR)) + dev->rxfilter |= MT_WF_RFCR_DROP_OTHER_UC; + else + dev->rxfilter &= ~MT_WF_RFCR_DROP_OTHER_UC; + + mt76_wr(dev, MT_WF_RFCR, dev->rxfilter); + + mutex_unlock(&dev->mt76.mutex); + } + + return ret; +} + +static void +mt7603_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, + unsigned int *total_flags, u64 multicast) +{ + struct mt7603_dev *dev = hw->priv; + u32 flags = 0; + +#define MT76_FILTER(_flag, _hw) do { \ + flags |= *total_flags & FIF_##_flag; \ + dev->rxfilter &= ~(_hw); \ + dev->rxfilter |= !(flags & FIF_##_flag) * (_hw); \ + } while (0) + + dev->rxfilter &= ~(MT_WF_RFCR_DROP_OTHER_BSS | + MT_WF_RFCR_DROP_OTHER_BEACON | + MT_WF_RFCR_DROP_FRAME_REPORT | + MT_WF_RFCR_DROP_PROBEREQ | + MT_WF_RFCR_DROP_MCAST_FILTERED | + MT_WF_RFCR_DROP_MCAST | + MT_WF_RFCR_DROP_BCAST | + MT_WF_RFCR_DROP_DUPLICATE | + MT_WF_RFCR_DROP_A2_BSSID | + MT_WF_RFCR_DROP_UNWANTED_CTL | + MT_WF_RFCR_DROP_STBC_MULTI); + + MT76_FILTER(OTHER_BSS, MT_WF_RFCR_DROP_OTHER_TIM | + MT_WF_RFCR_DROP_A3_MAC | + MT_WF_RFCR_DROP_A3_BSSID); + + MT76_FILTER(FCSFAIL, MT_WF_RFCR_DROP_FCSFAIL); + + MT76_FILTER(CONTROL, MT_WF_RFCR_DROP_CTS | + MT_WF_RFCR_DROP_RTS | + MT_WF_RFCR_DROP_CTL_RSV | + MT_WF_RFCR_DROP_NDPA); + + *total_flags = flags; + mt76_wr(dev, MT_WF_RFCR, dev->rxfilter); +} + +static void +mt7603_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, u64 changed) +{ + struct mt7603_dev *dev = hw->priv; + struct mt7603_vif *mvif = (struct mt7603_vif *)vif->drv_priv; + + mutex_lock(&dev->mt76.mutex); + + if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_BSSID)) { + if (vif->cfg.assoc || vif->cfg.ibss_joined) { + mt76_wr(dev, MT_BSSID0(mvif->idx), + get_unaligned_le32(info->bssid)); + mt76_wr(dev, MT_BSSID1(mvif->idx), + (get_unaligned_le16(info->bssid + 4) | + MT_BSSID1_VALID)); + } else { + mt76_wr(dev, MT_BSSID0(mvif->idx), 0); + mt76_wr(dev, MT_BSSID1(mvif->idx), 0); + } + } + + if (changed & BSS_CHANGED_ERP_SLOT) { + int slottime = info->use_short_slot ? 9 : 20; + + if (slottime != dev->slottime) { + dev->slottime = slottime; + mt7603_mac_set_timing(dev); + } + } + + if (changed & (BSS_CHANGED_BEACON_ENABLED | BSS_CHANGED_BEACON_INT)) { + int beacon_int = !!info->enable_beacon * info->beacon_int; + + tasklet_disable(&dev->mt76.pre_tbtt_tasklet); + mt7603_beacon_set_timer(dev, mvif->idx, beacon_int); + tasklet_enable(&dev->mt76.pre_tbtt_tasklet); + } + + mutex_unlock(&dev->mt76.mutex); +} + +int +mt7603_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mt7603_dev *dev = container_of(mdev, struct mt7603_dev, mt76); + struct mt7603_sta *msta = (struct mt7603_sta *)sta->drv_priv; + struct mt7603_vif *mvif = (struct mt7603_vif *)vif->drv_priv; + int idx; + int ret = 0; + + idx = mt76_wcid_alloc(dev->mt76.wcid_mask, MT7603_WTBL_STA - 1); + if (idx < 0) + return -ENOSPC; + + INIT_LIST_HEAD(&msta->wcid.poll_list); + __skb_queue_head_init(&msta->psq); + msta->ps = ~0; + msta->smps = ~0; + msta->wcid.sta = 1; + msta->wcid.idx = idx; + msta->vif = mvif; + mt7603_wtbl_init(dev, idx, mvif->idx, sta->addr); + mt7603_wtbl_set_ps(dev, msta, false); + + if (vif->type == NL80211_IFTYPE_AP) + set_bit(MT_WCID_FLAG_CHECK_PS, &msta->wcid.flags); + + return ret; +} + +int +mt7603_sta_event(struct mt76_dev *mdev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, enum mt76_sta_event ev) +{ + struct mt7603_dev *dev = container_of(mdev, struct mt7603_dev, mt76); + + if (ev == MT76_STA_EVENT_ASSOC) { + mutex_lock(&dev->mt76.mutex); + mt7603_wtbl_update_cap(dev, sta); + mutex_unlock(&dev->mt76.mutex); + } + + return 0; +} + +void +mt7603_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mt7603_dev *dev = container_of(mdev, struct mt7603_dev, mt76); + struct mt7603_vif *mvif = (struct mt7603_vif *)vif->drv_priv; + struct mt7603_sta *msta = (struct mt7603_sta *)sta->drv_priv; + struct mt76_wcid *wcid = (struct mt76_wcid *)sta->drv_priv; + + spin_lock_bh(&dev->ps_lock); + __skb_queue_purge(&msta->psq); + mt7603_filter_tx(dev, mvif->idx, wcid->idx, true); + spin_unlock_bh(&dev->ps_lock); + + spin_lock_bh(&mdev->sta_poll_lock); + if (!list_empty(&msta->wcid.poll_list)) + list_del_init(&msta->wcid.poll_list); + spin_unlock_bh(&mdev->sta_poll_lock); + + mt7603_wtbl_clear(dev, wcid->idx); +} + +static void +mt7603_ps_tx_list(struct mt7603_dev *dev, struct sk_buff_head *list) +{ + struct sk_buff *skb; + + while ((skb = __skb_dequeue(list)) != NULL) { + int qid = skb_get_queue_mapping(skb); + + mt76_tx_queue_skb_raw(dev, dev->mphy.q_tx[qid], skb, 0); + } +} + +void +mt7603_sta_ps(struct mt76_dev *mdev, struct ieee80211_sta *sta, bool ps) +{ + struct mt7603_dev *dev = container_of(mdev, struct mt7603_dev, mt76); + struct mt7603_sta *msta = (struct mt7603_sta *)sta->drv_priv; + struct sk_buff_head list; + + mt76_stop_tx_queues(&dev->mphy, sta, true); + mt7603_wtbl_sta_ps(dev, msta, ps); + if (ps) + return; + + __skb_queue_head_init(&list); + + spin_lock_bh(&dev->ps_lock); + skb_queue_splice_tail_init(&msta->psq, &list); + spin_unlock_bh(&dev->ps_lock); + + mt7603_ps_tx_list(dev, &list); +} + +static struct ieee80211_hdr * +mt7603_ps_skb_hdr(struct sk_buff *skb) +{ + return (struct ieee80211_hdr *)&skb->data[MT_TXD_SIZE]; +} + +/* + * Buffered frames can be recycled into the PS queue by mt7603_filter_tx(), so + * both bits have to be assigned, not just set. + */ +static void +mt7603_ps_set_flags(struct sk_buff *skb, bool more_data, bool eosp) +{ + struct ieee80211_hdr *hdr = mt7603_ps_skb_hdr(skb); + + if (more_data) + hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA); + else + hdr->frame_control &= ~cpu_to_le16(IEEE80211_FCTL_MOREDATA); + + if (!ieee80211_is_data_qos(hdr->frame_control)) + return; + + if (eosp) + *ieee80211_get_qos_ctl(hdr) |= IEEE80211_QOS_CTL_EOSP; + else + *ieee80211_get_qos_ctl(hdr) &= ~IEEE80211_QOS_CTL_EOSP; +} + +static void +mt7603_release_buffered_frames(struct ieee80211_hw *hw, + struct ieee80211_sta *sta, + u16 tids, int nframes, + enum ieee80211_frame_release_type reason, + bool more_data) +{ + struct mt7603_dev *dev = hw->priv; + struct mt7603_sta *msta = (struct mt7603_sta *)sta->drv_priv; + struct sk_buff_head list; + struct sk_buff *skb, *tmp, *last; + bool eosp_null, uapsd; + unsigned long drained; + u16 pending = 0; + u8 last_tid; + int i; + + __skb_queue_head_init(&list); + + mt7603_wtbl_set_ps(dev, msta, false); + + spin_lock_bh(&dev->ps_lock); + skb_queue_walk_safe(&msta->psq, skb, tmp) { + if (!nframes) + break; + + if (!(tids & BIT(skb->priority))) + continue; + + skb_set_queue_mapping(skb, MT_TXQ_PSD); + __skb_unlink(skb, &msta->psq); + __skb_queue_tail(&list, skb); + nframes--; + } + + skb_queue_walk(&msta->psq, skb) + pending |= BIT(skb->priority); + spin_unlock_bh(&dev->ps_lock); + + /* + * Without this, mac80211 keeps the TIM bit set for the station and + * keeps routing every service period to the driver, even though there + * is nothing left to release. + */ + drained = tids & ~pending; + for_each_set_bit(i, &drained, IEEE80211_NUM_TIDS) + ieee80211_sta_set_buffered(sta, i, false); + + last = skb_peek_tail(&list); + if (!last) { + mt76_release_buffered_frames(hw, sta, tids, nframes, reason, + more_data); + return; + } + + /* + * End the service period here instead of passing the remaining frame + * budget on to mt76_release_buffered_frames(), which would signal the + * end of the same service period a second time. + */ + uapsd = reason == IEEE80211_FRAME_RELEASE_UAPSD; + more_data |= !!(pending & tids); + + skb_queue_walk(&list, skb) + mt7603_ps_set_flags(skb, skb != last || more_data, + skb == last && uapsd); + + /* + * EOSP lives in the QoS control field, so a bufferable MMPDU cannot + * terminate a U-APSD service period on its own. In that case mac80211 + * has to append a QoS-Null frame, which ends the SP through its tx + * status. + */ + eosp_null = uapsd && + !ieee80211_is_data_qos(mt7603_ps_skb_hdr(last)->frame_control); + last_tid = __fls(tids); + + mt7603_ps_tx_list(dev, &list); + + if (eosp_null) + ieee80211_send_eosp_nullfunc(sta, last_tid); + else + ieee80211_sta_eosp(sta); +} + +static int +mt7603_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct mt7603_dev *dev = hw->priv; + struct mt7603_vif *mvif = (struct mt7603_vif *)vif->drv_priv; + struct mt7603_sta *msta = sta ? (struct mt7603_sta *)sta->drv_priv : + &mvif->sta; + struct mt76_wcid *wcid = &msta->wcid; + int idx = key->keyidx; + + /* fall back to sw encryption for unsupported ciphers */ + switch (key->cipher) { + case WLAN_CIPHER_SUITE_TKIP: + case WLAN_CIPHER_SUITE_CCMP: + break; + default: + return -EOPNOTSUPP; + } + + /* + * The hardware does not support per-STA RX GTK, fall back + * to software mode for these. + */ + if ((vif->type == NL80211_IFTYPE_ADHOC || + vif->type == NL80211_IFTYPE_MESH_POINT) && + (key->cipher == WLAN_CIPHER_SUITE_TKIP || + key->cipher == WLAN_CIPHER_SUITE_CCMP) && + !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) + return -EOPNOTSUPP; + + if (cmd != SET_KEY) { + if (idx == wcid->hw_key_idx) + wcid->hw_key_idx = -1; + + return 0; + } + + key->hw_key_idx = wcid->idx; + wcid->hw_key_idx = idx; + mt76_wcid_key_setup(&dev->mt76, wcid, key); + + return mt7603_wtbl_set_key(dev, wcid->idx, key); +} + +static int +mt7603_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + unsigned int link_id, u16 queue, + const struct ieee80211_tx_queue_params *params) +{ + struct mt7603_dev *dev = hw->priv; + u16 cw_min = (1 << 5) - 1; + u16 cw_max = (1 << 10) - 1; + u32 val; + + queue = dev->mphy.q_tx[queue]->hw_idx; + + if (params->cw_min) + cw_min = params->cw_min; + if (params->cw_max) + cw_max = params->cw_max; + + mutex_lock(&dev->mt76.mutex); + mt7603_mac_stop(dev); + + val = mt76_rr(dev, MT_WMM_TXOP(queue)); + val &= ~(MT_WMM_TXOP_MASK << MT_WMM_TXOP_SHIFT(queue)); + val |= params->txop << MT_WMM_TXOP_SHIFT(queue); + mt76_wr(dev, MT_WMM_TXOP(queue), val); + + val = mt76_rr(dev, MT_WMM_AIFSN); + val &= ~(MT_WMM_AIFSN_MASK << MT_WMM_AIFSN_SHIFT(queue)); + val |= params->aifs << MT_WMM_AIFSN_SHIFT(queue); + mt76_wr(dev, MT_WMM_AIFSN, val); + + val = mt76_rr(dev, MT_WMM_CWMIN); + val &= ~(MT_WMM_CWMIN_MASK << MT_WMM_CWMIN_SHIFT(queue)); + val |= cw_min << MT_WMM_CWMIN_SHIFT(queue); + mt76_wr(dev, MT_WMM_CWMIN, val); + + val = mt76_rr(dev, MT_WMM_CWMAX(queue)); + val &= ~(MT_WMM_CWMAX_MASK << MT_WMM_CWMAX_SHIFT(queue)); + val |= cw_max << MT_WMM_CWMAX_SHIFT(queue); + mt76_wr(dev, MT_WMM_CWMAX(queue), val); + + mt7603_mac_start(dev); + mutex_unlock(&dev->mt76.mutex); + + return 0; +} + +static void +mt7603_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 queues, bool drop) +{ +} + +static int +mt7603_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_ampdu_params *params) +{ + enum ieee80211_ampdu_mlme_action action = params->action; + struct mt7603_dev *dev = hw->priv; + struct ieee80211_sta *sta = params->sta; + struct ieee80211_txq *txq = sta->txq[params->tid]; + struct mt7603_sta *msta = (struct mt7603_sta *)sta->drv_priv; + u16 tid = params->tid; + u16 ssn = params->ssn; + u8 ba_size = params->buf_size; + struct mt76_txq *mtxq; + int ret = 0; + + if (!txq) + return -EINVAL; + + mtxq = (struct mt76_txq *)txq->drv_priv; + + mutex_lock(&dev->mt76.mutex); + switch (action) { + case IEEE80211_AMPDU_RX_START: + mt76_rx_aggr_start(&dev->mt76, &msta->wcid, tid, ssn, + params->buf_size); + mt7603_mac_rx_ba_reset(dev, sta->addr, tid); + break; + case IEEE80211_AMPDU_RX_STOP: + mt76_rx_aggr_stop(&dev->mt76, &msta->wcid, tid); + break; + case IEEE80211_AMPDU_TX_OPERATIONAL: + mtxq->aggr = true; + mtxq->send_bar = false; + mt7603_mac_tx_ba_reset(dev, msta->wcid.idx, tid, ba_size); + break; + case IEEE80211_AMPDU_TX_STOP_FLUSH: + case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: + mtxq->aggr = false; + mt7603_mac_tx_ba_reset(dev, msta->wcid.idx, tid, -1); + break; + case IEEE80211_AMPDU_TX_START: + mtxq->agg_ssn = IEEE80211_SN_TO_SEQ(ssn); + ret = IEEE80211_AMPDU_TX_START_IMMEDIATE; + break; + case IEEE80211_AMPDU_TX_STOP_CONT: + mtxq->aggr = false; + mt7603_mac_tx_ba_reset(dev, msta->wcid.idx, tid, -1); + ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); + break; + } + mutex_unlock(&dev->mt76.mutex); + + return ret; +} + +static void +mt7603_sta_rate_tbl_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mt7603_dev *dev = hw->priv; + struct mt7603_sta *msta = (struct mt7603_sta *)sta->drv_priv; + struct ieee80211_sta_rates *sta_rates = rcu_dereference(sta->rates); + int i; + + if (!sta_rates) + return; + + spin_lock_bh(&dev->mt76.lock); + for (i = 0; i < ARRAY_SIZE(msta->rates); i++) { + msta->rates[i].idx = sta_rates->rate[i].idx; + msta->rates[i].count = sta_rates->rate[i].count; + msta->rates[i].flags = sta_rates->rate[i].flags; + + if (msta->rates[i].idx < 0 || !msta->rates[i].count) + break; + } + msta->n_rates = i; + mt7603_wtbl_set_rates(dev, msta, NULL, msta->rates); + msta->rate_probe = false; + mt7603_wtbl_set_smps(dev, msta, + sta->deflink.smps_mode == IEEE80211_SMPS_DYNAMIC); + spin_unlock_bh(&dev->mt76.lock); +} + +static void +mt7603_set_coverage_class(struct ieee80211_hw *hw, int radio_idx, + s16 coverage_class) +{ + struct mt7603_dev *dev = hw->priv; + + mutex_lock(&dev->mt76.mutex); + dev->coverage_class = max_t(s16, coverage_class, 0); + mt7603_mac_set_timing(dev); + mutex_unlock(&dev->mt76.mutex); +} + +static void mt7603_tx(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, + struct sk_buff *skb) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_vif *vif = info->control.vif; + struct mt7603_dev *dev = hw->priv; + struct mt76_wcid *wcid = &dev->global_sta.wcid; + + if (control->sta) { + struct mt7603_sta *msta; + + msta = (struct mt7603_sta *)control->sta->drv_priv; + wcid = &msta->wcid; + } else if (vif) { + struct mt7603_vif *mvif; + + mvif = (struct mt7603_vif *)vif->drv_priv; + wcid = &mvif->sta.wcid; + } + + mt76_tx(&dev->mphy, control->sta, wcid, skb); +} + +const struct ieee80211_ops mt7603_ops = { + .add_chanctx = ieee80211_emulate_add_chanctx, + .remove_chanctx = ieee80211_emulate_remove_chanctx, + .change_chanctx = ieee80211_emulate_change_chanctx, + .switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx, + .tx = mt7603_tx, + .start = mt7603_start, + .stop = mt7603_stop, + .add_interface = mt7603_add_interface, + .remove_interface = mt7603_remove_interface, + .config = mt7603_config, + .configure_filter = mt7603_configure_filter, + .bss_info_changed = mt7603_bss_info_changed, + .sta_state = mt76_sta_state, + .sta_pre_rcu_remove = mt76_sta_pre_rcu_remove, + .set_key = mt7603_set_key, + .conf_tx = mt7603_conf_tx, + .sw_scan_start = mt76_sw_scan, + .sw_scan_complete = mt76_sw_scan_complete, + .flush = mt7603_flush, + .ampdu_action = mt7603_ampdu_action, + .get_txpower = mt76_get_txpower, + .wake_tx_queue = mt76_wake_tx_queue, + .sta_rate_tbl_update = mt7603_sta_rate_tbl_update, + .release_buffered_frames = mt7603_release_buffered_frames, + .set_coverage_class = mt7603_set_coverage_class, + .set_tim = mt76_set_tim, + .get_survey = mt76_get_survey, + .get_antenna = mt76_get_antenna, + .set_sar_specs = mt7603_set_sar_specs, +}; + +MODULE_DESCRIPTION("MediaTek MT7603E and MT76x8 wireless driver"); +MODULE_LICENSE("Dual BSD/GPL"); + +static int __init mt7603_init(void) +{ + int ret; + + ret = platform_driver_register(&mt76_wmac_driver); + if (ret) + return ret; + +#ifdef CONFIG_PCI + ret = pci_register_driver(&mt7603_pci_driver); + if (ret) + platform_driver_unregister(&mt76_wmac_driver); +#endif + return ret; +} + +static void __exit mt7603_exit(void) +{ +#ifdef CONFIG_PCI + pci_unregister_driver(&mt7603_pci_driver); +#endif + platform_driver_unregister(&mt76_wmac_driver); +} + +module_init(mt7603_init); +module_exit(mt7603_exit); diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7603/mcu.c new file mode 100644 index 000000000..e432cce97 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7603/mcu.c @@ -0,0 +1,432 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear + +#include +#include "mt7603.h" +#include "mcu.h" +#include "eeprom.h" + +#define MCU_SKB_RESERVE 8 + +struct mt7603_fw_trailer { + char fw_ver[10]; + char build_date[15]; + __le32 dl_len; +} __packed; + +static int +mt7603_mcu_parse_response(struct mt76_dev *mdev, int cmd, + struct sk_buff *skb, int seq) +{ + struct mt7603_dev *dev = container_of(mdev, struct mt7603_dev, mt76); + struct mt7603_mcu_rxd *rxd; + + if (!skb) { + dev_err(mdev->dev, "MCU message %02x (seq %d) timed out\n", + abs(cmd), seq); + dev->mcu_hang = MT7603_WATCHDOG_TIMEOUT; + return -ETIMEDOUT; + } + + rxd = (struct mt7603_mcu_rxd *)skb->data; + if (seq != rxd->seq) + return -EAGAIN; + + return 0; +} + +static int +mt7603_mcu_skb_send_msg(struct mt76_dev *mdev, struct sk_buff *skb, + int cmd, int *wait_seq) +{ + struct mt7603_dev *dev = container_of(mdev, struct mt7603_dev, mt76); + int hdrlen = dev->mcu_running ? sizeof(struct mt7603_mcu_txd) : 12; + struct mt7603_mcu_txd *txd; + u8 seq; + + mdev->mcu.timeout = 3 * HZ; + + seq = ++mdev->mcu.msg_seq & 0xf; + if (!seq) + seq = ++mdev->mcu.msg_seq & 0xf; + + txd = (struct mt7603_mcu_txd *)skb_push(skb, hdrlen); + + txd->len = cpu_to_le16(skb->len); + if (cmd == -MCU_CMD_FW_SCATTER) + txd->pq_id = cpu_to_le16(MCU_PORT_QUEUE_FW); + else + txd->pq_id = cpu_to_le16(MCU_PORT_QUEUE); + txd->pkt_type = MCU_PKT_ID; + txd->seq = seq; + + if (cmd < 0) { + txd->cid = -cmd; + txd->set_query = MCU_Q_NA; + } else { + txd->cid = MCU_CMD_EXT_CID; + txd->ext_cid = cmd; + txd->set_query = MCU_Q_SET; + txd->ext_cid_ack = 1; + } + + if (wait_seq) + *wait_seq = seq; + + return mt76_tx_queue_skb_raw(dev, mdev->q_mcu[MT_MCUQ_WM], skb, 0); +} + +static int +mt7603_mcu_init_download(struct mt7603_dev *dev, u32 addr, u32 len) +{ + struct { + __le32 addr; + __le32 len; + __le32 mode; + } req = { + .addr = cpu_to_le32(addr), + .len = cpu_to_le32(len), + .mode = cpu_to_le32(BIT(31)), + }; + + return mt76_mcu_send_msg(&dev->mt76, -MCU_CMD_TARGET_ADDRESS_LEN_REQ, + &req, sizeof(req), true); +} + +static int +mt7603_mcu_start_firmware(struct mt7603_dev *dev, u32 addr) +{ + struct { + __le32 override; + __le32 addr; + } req = { + .override = cpu_to_le32(addr ? 1 : 0), + .addr = cpu_to_le32(addr), + }; + + return mt76_mcu_send_msg(&dev->mt76, -MCU_CMD_FW_START_REQ, &req, + sizeof(req), true); +} + +static int +mt7603_mcu_restart(struct mt76_dev *dev) +{ + return mt76_mcu_send_msg(dev, -MCU_CMD_RESTART_DL_REQ, NULL, 0, true); +} + +static int mt7603_load_firmware(struct mt7603_dev *dev) +{ + const struct firmware *fw; + const struct mt7603_fw_trailer *hdr; + const char *firmware; + int dl_len; + u32 addr, val; + int ret; + + if (is_mt7628(dev)) { + if (mt76xx_rev(dev) == MT7628_REV_E1) + firmware = MT7628_FIRMWARE_E1; + else + firmware = MT7628_FIRMWARE_E2; + } else { + if (mt76xx_rev(dev) < MT7603_REV_E2) + firmware = MT7603_FIRMWARE_E1; + else + firmware = MT7603_FIRMWARE_E2; + } + + ret = request_firmware(&fw, firmware, dev->mt76.dev); + if (ret) + return ret; + + if (!fw || !fw->data || fw->size < sizeof(*hdr)) { + dev_err(dev->mt76.dev, "Invalid firmware\n"); + ret = -EINVAL; + goto out; + } + + hdr = (const struct mt7603_fw_trailer *)(fw->data + fw->size - + sizeof(*hdr)); + + dev_info(dev->mt76.dev, "Firmware Version: %.10s\n", hdr->fw_ver); + dev_info(dev->mt76.dev, "Build Time: %.15s\n", hdr->build_date); + + addr = mt7603_reg_map(dev, 0x50012498); + mt76_wr(dev, addr, 0x5); + mt76_wr(dev, addr, 0x5); + udelay(1); + + /* switch to bypass mode */ + mt76_rmw(dev, MT_SCH_4, MT_SCH_4_FORCE_QID, + MT_SCH_4_BYPASS | FIELD_PREP(MT_SCH_4_FORCE_QID, 5)); + + val = mt76_rr(dev, MT_TOP_MISC2); + if (val & BIT(1)) { + dev_info(dev->mt76.dev, "Firmware already running...\n"); + goto running; + } + + if (!mt76_poll_msec(dev, MT_TOP_MISC2, BIT(0) | BIT(1), BIT(0), 500)) { + dev_err(dev->mt76.dev, "Timeout waiting for ROM code to become ready\n"); + ret = -EIO; + goto out; + } + + dl_len = le32_to_cpu(hdr->dl_len) + 4; + ret = mt7603_mcu_init_download(dev, MCU_FIRMWARE_ADDRESS, dl_len); + if (ret) { + dev_err(dev->mt76.dev, "Download request failed\n"); + goto out; + } + + ret = mt76_mcu_send_firmware(&dev->mt76, -MCU_CMD_FW_SCATTER, + fw->data, dl_len); + if (ret) { + dev_err(dev->mt76.dev, "Failed to send firmware to device\n"); + goto out; + } + + ret = mt7603_mcu_start_firmware(dev, MCU_FIRMWARE_ADDRESS); + if (ret) { + dev_err(dev->mt76.dev, "Failed to start firmware\n"); + goto out; + } + + if (!mt76_poll_msec(dev, MT_TOP_MISC2, BIT(1), BIT(1), 500)) { + dev_err(dev->mt76.dev, "Timeout waiting for firmware to initialize\n"); + ret = -EIO; + goto out; + } + +running: + mt76_clear(dev, MT_SCH_4, MT_SCH_4_FORCE_QID | MT_SCH_4_BYPASS); + + mt76_set(dev, MT_SCH_4, BIT(8)); + mt76_clear(dev, MT_SCH_4, BIT(8)); + + dev->mcu_running = true; + snprintf(dev->mt76.hw->wiphy->fw_version, + sizeof(dev->mt76.hw->wiphy->fw_version), + "%.10s-%.15s", hdr->fw_ver, hdr->build_date); + dev_info(dev->mt76.dev, "firmware init done\n"); + +out: + release_firmware(fw); + + return ret; +} + +int mt7603_mcu_init(struct mt7603_dev *dev) +{ + static const struct mt76_mcu_ops mt7603_mcu_ops = { + .headroom = sizeof(struct mt7603_mcu_txd), + .mcu_skb_send_msg = mt7603_mcu_skb_send_msg, + .mcu_parse_response = mt7603_mcu_parse_response, + }; + + dev->mt76.mcu_ops = &mt7603_mcu_ops; + return mt7603_load_firmware(dev); +} + +void mt7603_mcu_exit(struct mt7603_dev *dev) +{ + mt7603_mcu_restart(&dev->mt76); + skb_queue_purge(&dev->mt76.mcu.res_q); +} + +int mt7603_mcu_set_eeprom(struct mt7603_dev *dev) +{ + static const u16 req_fields[] = { +#define WORD(_start) \ + _start, \ + _start + 1 +#define GROUP_2G(_start) \ + WORD(_start), \ + WORD(_start + 2), \ + WORD(_start + 4) + + MT_EE_NIC_CONF_0 + 1, + WORD(MT_EE_NIC_CONF_1), + MT_EE_WIFI_RF_SETTING, + MT_EE_TX_POWER_DELTA_BW40, + MT_EE_TX_POWER_DELTA_BW80 + 1, + MT_EE_TX_POWER_EXT_PA_5G, + MT_EE_TEMP_SENSOR_CAL, + GROUP_2G(MT_EE_TX_POWER_0_START_2G), + GROUP_2G(MT_EE_TX_POWER_1_START_2G), + WORD(MT_EE_TX_POWER_CCK), + WORD(MT_EE_TX_POWER_OFDM_2G_6M), + WORD(MT_EE_TX_POWER_OFDM_2G_24M), + WORD(MT_EE_TX_POWER_OFDM_2G_54M), + WORD(MT_EE_TX_POWER_HT_BPSK_QPSK), + WORD(MT_EE_TX_POWER_HT_16_64_QAM), + WORD(MT_EE_TX_POWER_HT_64_QAM), + MT_EE_ELAN_RX_MODE_GAIN, + MT_EE_ELAN_RX_MODE_NF, + MT_EE_ELAN_RX_MODE_P1DB, + MT_EE_ELAN_BYPASS_MODE_GAIN, + MT_EE_ELAN_BYPASS_MODE_NF, + MT_EE_ELAN_BYPASS_MODE_P1DB, + WORD(MT_EE_STEP_NUM_NEG_6_7), + WORD(MT_EE_STEP_NUM_NEG_4_5), + WORD(MT_EE_STEP_NUM_NEG_2_3), + WORD(MT_EE_STEP_NUM_NEG_0_1), + WORD(MT_EE_REF_STEP_24G), + WORD(MT_EE_STEP_NUM_PLUS_1_2), + WORD(MT_EE_STEP_NUM_PLUS_3_4), + WORD(MT_EE_STEP_NUM_PLUS_5_6), + MT_EE_STEP_NUM_PLUS_7, + MT_EE_XTAL_FREQ_OFFSET, + MT_EE_XTAL_TRIM_2_COMP, + MT_EE_XTAL_TRIM_3_COMP, + MT_EE_XTAL_WF_RFCAL, + + /* unknown fields below */ + WORD(0x24), + 0x34, + 0x39, + 0x3b, + WORD(0x42), + WORD(0x9e), + 0xf2, + WORD(0xf8), + 0xfa, + 0x12e, + WORD(0x130), WORD(0x132), WORD(0x134), WORD(0x136), + WORD(0x138), WORD(0x13a), WORD(0x13c), WORD(0x13e), + +#undef GROUP_2G +#undef WORD + + }; + struct req_data { + __le16 addr; + u8 val; + u8 pad; + } __packed; + struct { + u8 buffer_mode; + u8 len; + u8 pad[2]; + } req_hdr = { + .buffer_mode = 1, + .len = ARRAY_SIZE(req_fields) - 1, + }; + const int size = 0xff * sizeof(struct req_data); + u8 *req, *eep = (u8 *)dev->mt76.eeprom.data; + int i, ret, len = sizeof(req_hdr) + size; + struct req_data *data; + + BUILD_BUG_ON(ARRAY_SIZE(req_fields) * sizeof(*data) > size); + + req = kmalloc(len, GFP_KERNEL); + if (!req) + return -ENOMEM; + + memcpy(req, &req_hdr, sizeof(req_hdr)); + data = (struct req_data *)(req + sizeof(req_hdr)); + memset(data, 0, size); + for (i = 0; i < ARRAY_SIZE(req_fields); i++) { + data[i].addr = cpu_to_le16(req_fields[i]); + data[i].val = eep[req_fields[i]]; + } + + ret = mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD_EFUSE_BUFFER_MODE, + req, len, true); + kfree(req); + + return ret; +} + +static int mt7603_mcu_set_tx_power(struct mt7603_dev *dev) +{ + struct { + u8 center_channel; + u8 tssi; + u8 temp_comp; + u8 target_power[2]; + u8 rate_power_delta[14]; + u8 bw_power_delta; + u8 ch_power_delta[6]; + u8 temp_comp_power[17]; + u8 reserved; + } req = { + .center_channel = dev->mphy.chandef.chan->hw_value, +#define EEP_VAL(n) ((u8 *)dev->mt76.eeprom.data)[n] + .tssi = EEP_VAL(MT_EE_NIC_CONF_1 + 1), + .temp_comp = EEP_VAL(MT_EE_NIC_CONF_1), + .target_power = { + EEP_VAL(MT_EE_TX_POWER_0_START_2G + 2), + EEP_VAL(MT_EE_TX_POWER_1_START_2G + 2) + }, + .bw_power_delta = EEP_VAL(MT_EE_TX_POWER_DELTA_BW40), + .ch_power_delta = { + EEP_VAL(MT_EE_TX_POWER_0_START_2G + 3), + EEP_VAL(MT_EE_TX_POWER_0_START_2G + 4), + EEP_VAL(MT_EE_TX_POWER_0_START_2G + 5), + EEP_VAL(MT_EE_TX_POWER_1_START_2G + 3), + EEP_VAL(MT_EE_TX_POWER_1_START_2G + 4), + EEP_VAL(MT_EE_TX_POWER_1_START_2G + 5) + }, +#undef EEP_VAL + }; + u8 *eep = (u8 *)dev->mt76.eeprom.data; + + memcpy(req.rate_power_delta, eep + MT_EE_TX_POWER_CCK, + sizeof(req.rate_power_delta)); + + memcpy(req.temp_comp_power, eep + MT_EE_STEP_NUM_NEG_6_7, + sizeof(req.temp_comp_power)); + + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD_SET_TX_POWER_CTRL, + &req, sizeof(req), true); +} + +int mt7603_mcu_set_channel(struct mt7603_dev *dev) +{ + struct cfg80211_chan_def *chandef = &dev->mphy.chandef; + struct ieee80211_hw *hw = mt76_hw(dev); + int n_chains = hweight8(dev->mphy.antenna_mask); + struct { + u8 control_chan; + u8 center_chan; + u8 bw; + u8 tx_streams; + u8 rx_streams; + u8 _res0[7]; + u8 txpower[21]; + u8 _res1[3]; + } req = { + .control_chan = chandef->chan->hw_value, + .center_chan = chandef->chan->hw_value, + .bw = MT_BW_20, + .tx_streams = n_chains, + .rx_streams = n_chains, + }; + s8 tx_power = hw->conf.power_level * 2; + int i, ret; + + if (dev->mphy.chandef.width == NL80211_CHAN_WIDTH_40) { + req.bw = MT_BW_40; + if (chandef->center_freq1 > chandef->chan->center_freq) + req.center_chan += 2; + else + req.center_chan -= 2; + } + + tx_power = mt76_get_sar_power(&dev->mphy, chandef->chan, tx_power); + if (dev->mphy.antenna_mask == 3) + tx_power -= 6; + tx_power = min(tx_power, dev->tx_power_limit); + + dev->mphy.txpower_cur = tx_power; + + for (i = 0; i < ARRAY_SIZE(req.txpower); i++) + req.txpower[i] = tx_power; + + ret = mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD_CHANNEL_SWITCH, &req, + sizeof(req), true); + if (ret) + return ret; + + return mt7603_mcu_set_tx_power(dev); +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7603/mcu.h new file mode 100644 index 000000000..7debe76cd --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7603/mcu.h @@ -0,0 +1,103 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ + +#ifndef __MT7603_MCU_H +#define __MT7603_MCU_H + +struct mt7603_mcu_txd { + __le16 len; + __le16 pq_id; + + u8 cid; + u8 pkt_type; + u8 set_query; + u8 seq; + + u8 uc_d2b0_rev; + u8 ext_cid; + u8 uc_d2b2_rev; + u8 ext_cid_ack; + + u32 au4_d3_to_d7_rev[5]; +} __packed __aligned(4); + +struct mt7603_mcu_rxd { + __le16 len; + __le16 pkt_type_id; + + u8 eid; + u8 seq; + __le16 __rsv; + + u8 ext_eid; + u8 __rsv1[3]; +}; + +#define MCU_PKT_ID 0xa0 +#define MCU_PORT_QUEUE 0x8000 +#define MCU_PORT_QUEUE_FW 0xc000 + +#define MCU_FIRMWARE_ADDRESS 0x100000 + +enum { + MCU_Q_QUERY, + MCU_Q_SET, + MCU_Q_RESERVED, + MCU_Q_NA +}; + +enum { + MCU_CMD_TARGET_ADDRESS_LEN_REQ = 0x01, + MCU_CMD_FW_START_REQ = 0x02, + MCU_CMD_INIT_ACCESS_REG = 0x3, + MCU_CMD_PATCH_START_REQ = 0x05, + MCU_CMD_PATCH_FINISH_REQ = 0x07, + MCU_CMD_PATCH_SEM_CONTROL = 0x10, + MCU_CMD_HIF_LOOPBACK = 0x20, + MCU_CMD_CH_PRIVILEGE = 0x20, + MCU_CMD_ACCESS_REG = 0xC2, + MCU_CMD_EXT_CID = 0xED, + MCU_CMD_FW_SCATTER = 0xEE, + MCU_CMD_RESTART_DL_REQ = 0xEF, +}; + +enum { + MCU_EXT_CMD_RF_REG_ACCESS = 0x02, + MCU_EXT_CMD_RF_TEST = 0x04, + MCU_EXT_CMD_RADIO_ON_OFF_CTRL = 0x05, + MCU_EXT_CMD_WIFI_RX_DISABLE = 0x06, + MCU_EXT_CMD_PM_STATE_CTRL = 0x07, + MCU_EXT_CMD_CHANNEL_SWITCH = 0x08, + MCU_EXT_CMD_NIC_CAPABILITY = 0x09, + MCU_EXT_CMD_PWR_SAVING = 0x0A, + MCU_EXT_CMD_MULTIPLE_REG_ACCESS = 0x0E, + MCU_EXT_CMD_AP_PWR_SAVING_CAPABILITY = 0xF, + MCU_EXT_CMD_SEC_ADDREMOVE_KEY = 0x10, + MCU_EXT_CMD_SET_TX_POWER_CTRL = 0x11, + MCU_EXT_CMD_FW_LOG_2_HOST = 0x13, + MCU_EXT_CMD_PS_RETRIEVE_START = 0x14, + MCU_EXT_CMD_LED_CTRL = 0x17, + MCU_EXT_CMD_PACKET_FILTER = 0x18, + MCU_EXT_CMD_PWR_MGT_BIT_WIFI = 0x1B, + MCU_EXT_CMD_EFUSE_BUFFER_MODE = 0x21, + MCU_EXT_CMD_THERMAL_PROTECT = 0x23, + MCU_EXT_CMD_EDCA_SET = 0x27, + MCU_EXT_CMD_SLOT_TIME_SET = 0x28, + MCU_EXT_CMD_CONFIG_INTERNAL_SETTING = 0x29, + MCU_EXT_CMD_NOA_OFFLOAD_CTRL = 0x2B, + MCU_EXT_CMD_GET_THEMAL_SENSOR = 0x2C, + MCU_EXT_CMD_WAKEUP_OPTION = 0x2E, + MCU_EXT_CMD_AC_QUEUE_CONTROL = 0x31, + MCU_EXT_CMD_BCN_UPDATE = 0x33 +}; + +enum { + MCU_EXT_EVENT_CMD_RESULT = 0x0, + MCU_EXT_EVENT_RF_REG_ACCESS = 0x2, + MCU_EXT_EVENT_MULTI_CR_ACCESS = 0x0E, + MCU_EXT_EVENT_FW_LOG_2_HOST = 0x13, + MCU_EXT_EVENT_BEACON_LOSS = 0x1A, + MCU_EXT_EVENT_THERMAL_PROTECT = 0x22, + MCU_EXT_EVENT_BCN_UPDATE = 0x31, +}; + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/mt7603.h b/drivers/net/wireless/mediatek/mt76/mt7603/mt7603.h new file mode 100644 index 000000000..5b0ecd01a --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7603/mt7603.h @@ -0,0 +1,266 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ + +#ifndef __MT7603_H +#define __MT7603_H + +#include +#include +#include "../mt76.h" +#include "regs.h" + +#define MT7603_MAX_INTERFACES 4 +#define MT7603_WTBL_SIZE 128 +#define MT7603_WTBL_RESERVED (MT7603_WTBL_SIZE - 1) +#define MT7603_WTBL_STA (MT7603_WTBL_RESERVED - MT7603_MAX_INTERFACES) + +#define MT7603_RATE_RETRY 2 + +#define MT7603_MCU_RX_RING_SIZE 64 +#define MT7603_RX_RING_SIZE 128 +#define MT7603_TX_RING_SIZE 256 +#define MT7603_PSD_RING_SIZE 128 + +#define MT7603_FIRMWARE_E1 "mt7603_e1.bin" +#define MT7603_FIRMWARE_E2 "mt7603_e2.bin" +#define MT7628_FIRMWARE_E1 "mt7628_e1.bin" +#define MT7628_FIRMWARE_E2 "mt7628_e2.bin" + +#define MT7603_EEPROM_SIZE 1024 + +#define MT_AGG_SIZE_LIMIT(_n) (((_n) + 1) * 4) + +#define MT7603_PRE_TBTT_TIME 5000 /* ms */ + +#define MT7603_WATCHDOG_TIME 100 /* ms */ +#define MT7603_WATCHDOG_TIMEOUT 10 /* number of checks */ + +#define MT7603_EDCCA_BLOCK_TH 10 + +#define MT7603_CFEND_RATE_DEFAULT 0x69 /* chip default (24M) */ +#define MT7603_CFEND_RATE_11B 0x03 /* 11B LP, 11M */ + +struct mt7603_vif; +struct mt7603_sta; + +enum { + MT7603_REV_E1 = 0x00, + MT7603_REV_E2 = 0x10, + MT7628_REV_E1 = 0x8a00, +}; + +enum mt7603_bw { + MT_BW_20, + MT_BW_40, + MT_BW_80, +}; + +struct mt7603_rate_set { + struct ieee80211_tx_rate probe_rate; + struct ieee80211_tx_rate rates[4]; +}; + +struct mt7603_sta { + struct mt76_wcid wcid; /* must be first */ + + struct mt7603_vif *vif; + + u32 tx_airtime_ac[4]; + + struct sk_buff_head psq; + + struct ieee80211_tx_rate rates[4]; + + struct mt7603_rate_set rateset[2]; + u32 rate_set_tsf; + + u8 rate_count; + u8 n_rates; + + u8 rate_probe; + u8 smps; + + u8 ps; + u8 ps_sleeping; +}; + +struct mt7603_vif { + struct mt7603_sta sta; /* must be first */ + + u8 idx; +}; + +enum mt7603_reset_cause { + RESET_CAUSE_TX_HANG, + RESET_CAUSE_TX_BUSY, + RESET_CAUSE_RX_BUSY, + RESET_CAUSE_BEACON_STUCK, + RESET_CAUSE_RX_PSE_BUSY, + RESET_CAUSE_MCU_HANG, + RESET_CAUSE_RESET_FAILED, + __RESET_CAUSE_MAX +}; + +struct mt7603_dev { + union { /* must be first */ + struct mt76_dev mt76; + struct mt76_phy mphy; + }; + + const struct mt76_bus_ops *bus_ops; + + u32 rxfilter; + + struct mt7603_sta global_sta; + + u32 agc0, agc3; + u32 false_cca_ofdm, false_cca_cck; + unsigned long last_cca_adj; + + u32 ampdu_ref; + u32 rx_ampdu_ts; + u8 rssi_offset[3]; + + u8 slottime; + s16 coverage_class; + + s8 tx_power_limit; + + ktime_t ed_time; + + spinlock_t ps_lock; + + u8 mcu_running; + + u8 ed_monitor_enabled; + u8 ed_monitor; + s8 ed_trigger; + u8 ed_strict_mode; + u8 ed_strong_signal; + + bool dynamic_sensitivity; + s8 sensitivity; + u8 sensitivity_limit; + + u8 beacon_check; + u8 tx_hang_check; + u8 tx_dma_check; + u8 rx_dma_check; + u8 rx_pse_check; + u8 mcu_hang; + + enum mt7603_reset_cause cur_reset_cause; + + u16 tx_dma_idx[4]; + u16 rx_dma_idx; + + u32 reset_test; + + unsigned int reset_cause[__RESET_CAUSE_MAX]; +}; + +extern const struct mt76_driver_ops mt7603_drv_ops; +extern const struct ieee80211_ops mt7603_ops; +extern struct pci_driver mt7603_pci_driver; +extern struct platform_driver mt76_wmac_driver; + +static inline bool is_mt7603(struct mt7603_dev *dev) +{ + return mt76xx_chip(dev) == 0x7603; +} + +static inline bool is_mt7628(struct mt7603_dev *dev) +{ + return mt76xx_chip(dev) == 0x7628; +} + +/* need offset to prevent conflict with ampdu_ack_len */ +#define MT_RATE_DRIVER_DATA_OFFSET 4 + +u32 mt7603_reg_map(struct mt7603_dev *dev, u32 addr); + +irqreturn_t mt7603_irq_handler(int irq, void *dev_instance); + +int mt7603_register_device(struct mt7603_dev *dev); +void mt7603_unregister_device(struct mt7603_dev *dev); +int mt7603_eeprom_init(struct mt7603_dev *dev); +int mt7603_dma_init(struct mt7603_dev *dev); +void mt7603_dma_cleanup(struct mt7603_dev *dev); +int mt7603_mcu_init(struct mt7603_dev *dev); +void mt7603_init_debugfs(struct mt7603_dev *dev); + +static inline void mt7603_irq_enable(struct mt7603_dev *dev, u32 mask) +{ + mt76_set_irq_mask(&dev->mt76, MT_INT_MASK_CSR, 0, mask); +} + +static inline void mt7603_irq_disable(struct mt7603_dev *dev, u32 mask) +{ + mt76_set_irq_mask(&dev->mt76, MT_INT_MASK_CSR, mask, 0); +} + +void mt7603_mac_reset_counters(struct mt7603_dev *dev); +void mt7603_mac_dma_start(struct mt7603_dev *dev); +void mt7603_mac_start(struct mt7603_dev *dev); +void mt7603_mac_stop(struct mt7603_dev *dev); +void mt7603_mac_work(struct work_struct *work); +void mt7603_mac_set_timing(struct mt7603_dev *dev); +void mt7603_beacon_set_timer(struct mt7603_dev *dev, int idx, int intval); +int mt7603_mac_fill_rx(struct mt7603_dev *dev, struct sk_buff *skb); +void mt7603_mac_add_txs(struct mt7603_dev *dev, void *data); +void mt7603_mac_rx_ba_reset(struct mt7603_dev *dev, void *addr, u8 tid); +void mt7603_mac_tx_ba_reset(struct mt7603_dev *dev, int wcid, int tid, + int ba_size); +void mt7603_mac_sta_poll(struct mt7603_dev *dev); + +void mt7603_pse_client_reset(struct mt7603_dev *dev); + +int mt7603_set_channel(struct mt76_phy *mphy); +int mt7603_mcu_set_channel(struct mt7603_dev *dev); +int mt7603_mcu_set_eeprom(struct mt7603_dev *dev); +void mt7603_mcu_exit(struct mt7603_dev *dev); + +void mt7603_wtbl_init(struct mt7603_dev *dev, int idx, int vif, + const u8 *mac_addr); +void mt7603_wtbl_clear(struct mt7603_dev *dev, int idx); +void mt7603_wtbl_update_cap(struct mt7603_dev *dev, struct ieee80211_sta *sta); +void mt7603_wtbl_set_rates(struct mt7603_dev *dev, struct mt7603_sta *sta, + struct ieee80211_tx_rate *probe_rate, + struct ieee80211_tx_rate *rates); +int mt7603_wtbl_set_key(struct mt7603_dev *dev, int wcid, + struct ieee80211_key_conf *key); +void mt7603_wtbl_set_ps(struct mt7603_dev *dev, struct mt7603_sta *sta, + bool enabled); +void mt7603_wtbl_sta_ps(struct mt7603_dev *dev, struct mt7603_sta *sta, + bool ps); +void mt7603_wtbl_restore_ps(struct mt7603_dev *dev, struct mt7603_sta *sta); +void mt7603_wtbl_set_smps(struct mt7603_dev *dev, struct mt7603_sta *sta, + bool enabled); +void mt7603_filter_tx(struct mt7603_dev *dev, int mac_idx, int idx, bool abort); + +int mt7603_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, + enum mt76_txq_id qid, struct mt76_wcid *wcid, + struct ieee80211_sta *sta, + struct mt76_tx_info *tx_info); + +void mt7603_tx_complete_skb(struct mt76_dev *mdev, struct mt76_queue_entry *e); + +void mt7603_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, + struct sk_buff *skb, u32 *info); +void mt7603_rx_poll_complete(struct mt76_dev *mdev, enum mt76_rxq_id q); +void mt7603_sta_ps(struct mt76_dev *mdev, struct ieee80211_sta *sta, bool ps); +int mt7603_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); +int mt7603_sta_event(struct mt76_dev *mdev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, enum mt76_sta_event ev); +void mt7603_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); + +void mt7603_pre_tbtt_tasklet(struct tasklet_struct *t); + +void mt7603_update_channel(struct mt76_phy *mphy); + +void mt7603_edcca_set_strict(struct mt7603_dev *dev, bool val); +void mt7603_cca_stats_reset(struct mt7603_dev *dev); + +void mt7603_init_edcca(struct mt7603_dev *dev); +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/pci.c b/drivers/net/wireless/mediatek/mt76/mt7603/pci.c new file mode 100644 index 000000000..5fee61059 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7603/pci.c @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear + +#include +#include +#include + +#include "mt7603.h" + +static const struct pci_device_id mt76pci_device_table[] = { + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7603) }, + { }, +}; + +static int +mt76pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct mt7603_dev *dev; + struct mt76_dev *mdev; + int ret; + + ret = pcim_enable_device(pdev); + if (ret) + return ret; + + ret = pcim_iomap_regions(pdev, BIT(0), pci_name(pdev)); + if (ret) + return ret; + + pci_set_master(pdev); + + ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) + return ret; + + mdev = mt76_alloc_device(&pdev->dev, sizeof(*dev), &mt7603_ops, + &mt7603_drv_ops); + if (!mdev) + return -ENOMEM; + + dev = container_of(mdev, struct mt7603_dev, mt76); + mt76_mmio_init(mdev, pcim_iomap_table(pdev)[0]); + + mdev->rev = (mt76_rr(dev, MT_HW_CHIPID) << 16) | + (mt76_rr(dev, MT_HW_REV) & 0xff); + dev_info(mdev->dev, "ASIC revision: %04x\n", mdev->rev); + + mt76_wr(dev, MT_INT_MASK_CSR, 0); + + ret = devm_request_irq(mdev->dev, pdev->irq, mt7603_irq_handler, + IRQF_SHARED, KBUILD_MODNAME, dev); + if (ret) + goto error; + + ret = mt7603_register_device(dev); + if (ret) + goto error; + + return 0; +error: + mt76_free_device(&dev->mt76); + + return ret; +} + +static void +mt76pci_remove(struct pci_dev *pdev) +{ + struct mt76_dev *mdev = pci_get_drvdata(pdev); + struct mt7603_dev *dev = container_of(mdev, struct mt7603_dev, mt76); + + mt7603_unregister_device(dev); +} + +MODULE_DEVICE_TABLE(pci, mt76pci_device_table); +MODULE_FIRMWARE(MT7603_FIRMWARE_E1); +MODULE_FIRMWARE(MT7603_FIRMWARE_E2); + +struct pci_driver mt7603_pci_driver = { + .name = KBUILD_MODNAME, + .id_table = mt76pci_device_table, + .probe = mt76pci_probe, + .remove = mt76pci_remove, +}; diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/regs.h b/drivers/net/wireless/mediatek/mt76/mt7603/regs.h new file mode 100644 index 000000000..97942f5eb --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7603/regs.h @@ -0,0 +1,780 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ + +#ifndef __MT7603_REGS_H +#define __MT7603_REGS_H + +#define MT_HW_REV 0x1000 +#define MT_HW_CHIPID 0x1008 +#define MT_TOP_MISC2 0x1134 + +#define MT_MCU_BASE 0x2000 +#define MT_MCU(ofs) (MT_MCU_BASE + (ofs)) + +#define MT_MCU_PCIE_REMAP_1 MT_MCU(0x500) +#define MT_MCU_PCIE_REMAP_1_OFFSET GENMASK(17, 0) +#define MT_MCU_PCIE_REMAP_1_BASE GENMASK(31, 18) + +#define MT_MCU_PCIE_REMAP_2 MT_MCU(0x504) +#define MT_MCU_PCIE_REMAP_2_OFFSET GENMASK(18, 0) +#define MT_MCU_PCIE_REMAP_2_BASE GENMASK(31, 19) + +#define MT_HIF_BASE 0x4000 +#define MT_HIF(ofs) (MT_HIF_BASE + (ofs)) + +#define MT_INT_SOURCE_CSR MT_HIF(0x200) +#define MT_INT_MASK_CSR MT_HIF(0x204) +#define MT_DELAY_INT_CFG MT_HIF(0x210) + +#define MT_INT_RX_DONE(_n) BIT(_n) +#define MT_INT_RX_DONE_ALL GENMASK(1, 0) +#define MT_INT_TX_DONE_ALL GENMASK(19, 4) +#define MT_INT_TX_DONE(_n) BIT((_n) + 4) + +#define MT_INT_RX_COHERENT BIT(20) +#define MT_INT_TX_COHERENT BIT(21) +#define MT_INT_MAC_IRQ3 BIT(27) + +#define MT_INT_MCU_CMD BIT(30) + +#define MT_WPDMA_GLO_CFG MT_HIF(0x208) +#define MT_WPDMA_GLO_CFG_TX_DMA_EN BIT(0) +#define MT_WPDMA_GLO_CFG_TX_DMA_BUSY BIT(1) +#define MT_WPDMA_GLO_CFG_RX_DMA_EN BIT(2) +#define MT_WPDMA_GLO_CFG_RX_DMA_BUSY BIT(3) +#define MT_WPDMA_GLO_CFG_DMA_BURST_SIZE GENMASK(5, 4) +#define MT_WPDMA_GLO_CFG_TX_WRITEBACK_DONE BIT(6) +#define MT_WPDMA_GLO_CFG_BIG_ENDIAN BIT(7) +#define MT_WPDMA_GLO_CFG_HDR_SEG_LEN GENMASK(15, 8) +#define MT_WPDMA_GLO_CFG_SW_RESET BIT(24) +#define MT_WPDMA_GLO_CFG_FORCE_TX_EOF BIT(25) +#define MT_WPDMA_GLO_CFG_CLK_GATE_DIS BIT(30) +#define MT_WPDMA_GLO_CFG_RX_2B_OFFSET BIT(31) + +#define MT_WPDMA_RST_IDX MT_HIF(0x20c) + +#define MT_WPDMA_DEBUG MT_HIF(0x244) +#define MT_WPDMA_DEBUG_VALUE GENMASK(17, 0) +#define MT_WPDMA_DEBUG_SEL BIT(27) +#define MT_WPDMA_DEBUG_IDX GENMASK(31, 28) + +#define MT_TX_RING_BASE MT_HIF(0x300) +#define MT_RX_RING_BASE MT_HIF(0x400) + +#define MT_TXTIME_THRESH_BASE MT_HIF(0x500) +#define MT_TXTIME_THRESH(n) (MT_TXTIME_THRESH_BASE + ((n) * 4)) + +#define MT_PAGE_COUNT_BASE MT_HIF(0x540) +#define MT_PAGE_COUNT(n) (MT_PAGE_COUNT_BASE + ((n) * 4)) + +#define MT_SCH_1 MT_HIF(0x588) +#define MT_SCH_2 MT_HIF(0x58c) +#define MT_SCH_3 MT_HIF(0x590) + +#define MT_SCH_4 MT_HIF(0x594) +#define MT_SCH_4_FORCE_QID GENMASK(4, 0) +#define MT_SCH_4_BYPASS BIT(5) +#define MT_SCH_4_RESET BIT(8) + +#define MT_GROUP_THRESH_BASE MT_HIF(0x598) +#define MT_GROUP_THRESH(n) (MT_GROUP_THRESH_BASE + ((n) * 4)) + +#define MT_QUEUE_PRIORITY_1 MT_HIF(0x580) +#define MT_QUEUE_PRIORITY_2 MT_HIF(0x584) + +#define MT_BMAP_0 MT_HIF(0x5b0) +#define MT_BMAP_1 MT_HIF(0x5b4) +#define MT_BMAP_2 MT_HIF(0x5b8) + +#define MT_HIGH_PRIORITY_1 MT_HIF(0x5bc) +#define MT_HIGH_PRIORITY_2 MT_HIF(0x5c0) + +#define MT_PRIORITY_MASK MT_HIF(0x5c4) + +#define MT_RSV_MAX_THRESH MT_HIF(0x5c8) + +#define MT_PSE_BASE 0x8000 +#define MT_PSE(ofs) (MT_PSE_BASE + (ofs)) + +#define MT_MCU_DEBUG_RESET MT_PSE(0x16c) +#define MT_MCU_DEBUG_RESET_PSE BIT(0) +#define MT_MCU_DEBUG_RESET_PSE_S BIT(1) +#define MT_MCU_DEBUG_RESET_QUEUES GENMASK(6, 2) + +#define MT_PSE_FC_P0 MT_PSE(0x120) +#define MT_PSE_FC_P0_MIN_RESERVE GENMASK(11, 0) +#define MT_PSE_FC_P0_MAX_QUOTA GENMASK(27, 16) + +#define MT_PSE_FRP MT_PSE(0x138) +#define MT_PSE_FRP_P0 GENMASK(2, 0) +#define MT_PSE_FRP_P1 GENMASK(5, 3) +#define MT_PSE_FRP_P2_RQ0 GENMASK(8, 6) +#define MT_PSE_FRP_P2_RQ1 GENMASK(11, 9) +#define MT_PSE_FRP_P2_RQ2 GENMASK(14, 12) + +#define MT_FC_RSV_COUNT_0 MT_PSE(0x13c) +#define MT_FC_RSV_COUNT_0_P0 GENMASK(11, 0) +#define MT_FC_RSV_COUNT_0_P1 GENMASK(27, 16) + +#define MT_FC_SP2_Q0Q1 MT_PSE(0x14c) +#define MT_FC_SP2_Q0Q1_SRC_COUNT_Q0 GENMASK(11, 0) +#define MT_FC_SP2_Q0Q1_SRC_COUNT_Q1 GENMASK(27, 16) + +#define MT_PSE_FW_SHARED MT_PSE(0x17c) + +#define MT_PSE_RTA MT_PSE(0x194) +#define MT_PSE_RTA_QUEUE_ID GENMASK(4, 0) +#define MT_PSE_RTA_PORT_ID GENMASK(6, 5) +#define MT_PSE_RTA_REDIRECT_EN BIT(7) +#define MT_PSE_RTA_TAG_ID GENMASK(15, 8) +#define MT_PSE_RTA_WRITE BIT(16) +#define MT_PSE_RTA_BUSY BIT(31) + +#define MT_WF_PHY_BASE 0x10000 +#define MT_WF_PHY_OFFSET 0x1000 +#define MT_WF_PHY(ofs) (MT_WF_PHY_BASE + (ofs)) + +#define MT_AGC_BASE MT_WF_PHY(0x500) +#define MT_AGC(n) (MT_AGC_BASE + ((n) * 4)) + +#define MT_AGC1_BASE MT_WF_PHY(0x1500) +#define MT_AGC1(n) (MT_AGC1_BASE + ((n) * 4)) + +#define MT_AGC_41_RSSI_0 GENMASK(23, 16) +#define MT_AGC_41_RSSI_1 GENMASK(7, 0) + +#define MT_RXTD_BASE MT_WF_PHY(0x600) +#define MT_RXTD(n) (MT_RXTD_BASE + ((n) * 4)) + +#define MT_RXTD_6_ACI_TH GENMASK(4, 0) +#define MT_RXTD_6_CCAED_TH GENMASK(14, 8) + +#define MT_RXTD_8_LOWER_SIGNAL GENMASK(5, 0) + +#define MT_RXTD_13_ACI_TH_EN BIT(0) + +#define MT_WF_PHY_CR_TSSI_BASE MT_WF_PHY(0xd00) +#define MT_WF_PHY_CR_TSSI(phy, n) (MT_WF_PHY_CR_TSSI_BASE + \ + ((phy) * MT_WF_PHY_OFFSET) + \ + ((n) * 4)) + +#define MT_PHYCTRL_BASE MT_WF_PHY(0x4100) +#define MT_PHYCTRL(n) (MT_PHYCTRL_BASE + ((n) * 4)) + +#define MT_PHYCTRL_2_STATUS_RESET BIT(6) +#define MT_PHYCTRL_2_STATUS_EN BIT(7) + +#define MT_PHYCTRL_STAT_PD MT_PHYCTRL(3) +#define MT_PHYCTRL_STAT_PD_OFDM GENMASK(31, 16) +#define MT_PHYCTRL_STAT_PD_CCK GENMASK(15, 0) + +#define MT_PHYCTRL_STAT_MDRDY MT_PHYCTRL(8) +#define MT_PHYCTRL_STAT_MDRDY_OFDM GENMASK(31, 16) +#define MT_PHYCTRL_STAT_MDRDY_CCK GENMASK(15, 0) + +#define MT_WF_AGG_BASE 0x21200 +#define MT_WF_AGG(ofs) (MT_WF_AGG_BASE + (ofs)) + +#define MT_AGG_ARCR MT_WF_AGG(0x010) +#define MT_AGG_ARCR_INIT_RATE1 BIT(0) +#define MT_AGG_ARCR_FB_SGI_DISABLE BIT(1) +#define MT_AGG_ARCR_RATE8_DOWN_WRAP BIT(2) +#define MT_AGG_ARCR_RTS_RATE_THR GENMASK(12, 8) +#define MT_AGG_ARCR_RATE_DOWN_RATIO GENMASK(17, 16) +#define MT_AGG_ARCR_RATE_DOWN_RATIO_EN BIT(19) +#define MT_AGG_ARCR_RATE_UP_EXTRA_TH GENMASK(22, 20) +#define MT_AGG_ARCR_SPE_DIS_TH GENMASK(27, 24) + +#define MT_AGG_ARUCR MT_WF_AGG(0x014) +#define MT_AGG_ARDCR MT_WF_AGG(0x018) +#define MT_AGG_ARxCR_LIMIT_SHIFT(_n) (4 * (_n)) +#define MT_AGG_ARxCR_LIMIT(_n) GENMASK(2 + \ + MT_AGG_ARxCR_LIMIT_SHIFT(_n), \ + MT_AGG_ARxCR_LIMIT_SHIFT(_n)) + +#define MT_AGG_LIMIT MT_WF_AGG(0x040) +#define MT_AGG_LIMIT_1 MT_WF_AGG(0x044) +#define MT_AGG_LIMIT_AC(_n) GENMASK(((_n) + 1) * 8 - 1, (_n) * 8) + +#define MT_AGG_BA_SIZE_LIMIT_0 MT_WF_AGG(0x048) +#define MT_AGG_BA_SIZE_LIMIT_1 MT_WF_AGG(0x04c) +#define MT_AGG_BA_SIZE_LIMIT_SHIFT 8 + +#define MT_AGG_PCR MT_WF_AGG(0x050) +#define MT_AGG_PCR_MM BIT(16) +#define MT_AGG_PCR_GF BIT(17) +#define MT_AGG_PCR_BW40 BIT(18) +#define MT_AGG_PCR_RIFS BIT(19) +#define MT_AGG_PCR_BW80 BIT(20) +#define MT_AGG_PCR_BW160 BIT(21) +#define MT_AGG_PCR_ERP BIT(22) + +#define MT_AGG_PCR_RTS MT_WF_AGG(0x054) +#define MT_AGG_PCR_RTS_THR GENMASK(19, 0) +#define MT_AGG_PCR_RTS_PKT_THR GENMASK(31, 25) + +#define MT_AGG_ASRCR MT_WF_AGG(0x060) +#define MT_AGG_ASRCR_RANGE(val, n) (((val) >> ((n) << 3)) & GENMASK(5, 0)) + +#define MT_AGG_CONTROL MT_WF_AGG(0x070) +#define MT_AGG_CONTROL_NO_BA_RULE BIT(0) +#define MT_AGG_CONTROL_NO_BA_AR_RULE BIT(1) +#define MT_AGG_CONTROL_CFEND_SPE_EN BIT(3) +#define MT_AGG_CONTROL_CFEND_RATE GENMASK(15, 4) +#define MT_AGG_CONTROL_BAR_SPE_EN BIT(19) +#define MT_AGG_CONTROL_BAR_RATE GENMASK(31, 20) + +#define MT_AGG_TMP MT_WF_AGG(0x0d8) + +#define MT_AGG_BWCR MT_WF_AGG(0x0ec) +#define MT_AGG_BWCR_BW GENMASK(3, 2) + +#define MT_AGG_RETRY_CONTROL MT_WF_AGG(0x0f4) +#define MT_AGG_RETRY_CONTROL_RTS_LIMIT GENMASK(11, 7) +#define MT_AGG_RETRY_CONTROL_BAR_LIMIT GENMASK(15, 12) + +#define MT_WF_DMA_BASE 0x21c00 +#define MT_WF_DMA(ofs) (MT_WF_DMA_BASE + (ofs)) + +#define MT_DMA_DCR0 MT_WF_DMA(0x000) +#define MT_DMA_DCR0_MAX_RX_LEN GENMASK(15, 0) +#define MT_DMA_DCR0_DAMSDU BIT(16) +#define MT_DMA_DCR0_RX_VEC_DROP BIT(17) + +#define MT_DMA_DCR1 MT_WF_DMA(0x004) + +#define MT_DMA_FQCR0 MT_WF_DMA(0x008) +#define MT_DMA_FQCR0_TARGET_WCID GENMASK(7, 0) +#define MT_DMA_FQCR0_TARGET_BSS GENMASK(13, 8) +#define MT_DMA_FQCR0_TARGET_QID GENMASK(20, 16) +#define MT_DMA_FQCR0_DEST_PORT_ID GENMASK(23, 22) +#define MT_DMA_FQCR0_DEST_QUEUE_ID GENMASK(28, 24) +#define MT_DMA_FQCR0_MODE BIT(29) +#define MT_DMA_FQCR0_STATUS BIT(30) +#define MT_DMA_FQCR0_BUSY BIT(31) + +#define MT_DMA_RCFR0 MT_WF_DMA(0x070) +#define MT_DMA_VCFR0 MT_WF_DMA(0x07c) + +#define MT_DMA_TCFR0 MT_WF_DMA(0x080) +#define MT_DMA_TCFR1 MT_WF_DMA(0x084) +#define MT_DMA_TCFR_TXS_AGGR_TIMEOUT GENMASK(27, 16) +#define MT_DMA_TCFR_TXS_QUEUE BIT(14) +#define MT_DMA_TCFR_TXS_AGGR_COUNT GENMASK(12, 8) +#define MT_DMA_TCFR_TXS_BIT_MAP GENMASK(6, 0) + +#define MT_DMA_TMCFR0 MT_WF_DMA(0x088) + +#define MT_WF_ARB_BASE 0x21400 +#define MT_WF_ARB(ofs) (MT_WF_ARB_BASE + (ofs)) + +#define MT_WMM_AIFSN MT_WF_ARB(0x020) +#define MT_WMM_AIFSN_MASK GENMASK(3, 0) +#define MT_WMM_AIFSN_SHIFT(_n) ((_n) * 4) + +#define MT_WMM_CWMAX_BASE MT_WF_ARB(0x028) +#define MT_WMM_CWMAX(_n) (MT_WMM_CWMAX_BASE + (((_n) / 2) << 2)) +#define MT_WMM_CWMAX_SHIFT(_n) (((_n) & 1) * 16) +#define MT_WMM_CWMAX_MASK GENMASK(15, 0) + +#define MT_WMM_CWMIN MT_WF_ARB(0x040) +#define MT_WMM_CWMIN_MASK GENMASK(7, 0) +#define MT_WMM_CWMIN_SHIFT(_n) ((_n) * 8) + +#define MT_WF_ARB_RQCR MT_WF_ARB(0x070) +#define MT_WF_ARB_RQCR_RX_START BIT(0) +#define MT_WF_ARB_RQCR_RXV_START BIT(4) +#define MT_WF_ARB_RQCR_RXV_R_EN BIT(7) +#define MT_WF_ARB_RQCR_RXV_T_EN BIT(8) + +#define MT_ARB_SCR MT_WF_ARB(0x080) +#define MT_ARB_SCR_BCNQ_OPMODE_MASK GENMASK(1, 0) +#define MT_ARB_SCR_BCNQ_OPMODE_SHIFT(n) ((n) * 2) +#define MT_ARB_SCR_TX_DISABLE BIT(8) +#define MT_ARB_SCR_RX_DISABLE BIT(9) +#define MT_ARB_SCR_BCNQ_EMPTY_SKIP BIT(28) +#define MT_ARB_SCR_TTTT_BTIM_PRIO BIT(29) +#define MT_ARB_SCR_TBTT_BCN_PRIO BIT(30) +#define MT_ARB_SCR_TBTT_BCAST_PRIO BIT(31) + +enum { + MT_BCNQ_OPMODE_STA = 0, + MT_BCNQ_OPMODE_AP = 1, + MT_BCNQ_OPMODE_ADHOC = 2, +}; + +#define MT_WF_ARB_TX_START_0 MT_WF_ARB(0x100) +#define MT_WF_ARB_TX_START_1 MT_WF_ARB(0x104) +#define MT_WF_ARB_TX_FLUSH_0 MT_WF_ARB(0x108) +#define MT_WF_ARB_TX_FLUSH_1 MT_WF_ARB(0x10c) +#define MT_WF_ARB_TX_STOP_0 MT_WF_ARB(0x110) +#define MT_WF_ARB_TX_STOP_1 MT_WF_ARB(0x114) + +#define MT_WF_ARB_TX_FLUSH_AC0 BIT(0) +#define MT_WF_ARB_TX_FLUSH_AC1 BIT(5) +#define MT_WF_ARB_TX_FLUSH_AC2 BIT(10) +#define MT_WF_ARB_TX_FLUSH_AC3 BIT(16) +#define MT_WF_ARB_TX_FLUSH_AC4 BIT(21) +#define MT_WF_ARB_TX_FLUSH_AC5 BIT(26) + +#define MT_WF_ARB_BCN_START MT_WF_ARB(0x118) +#define MT_WF_ARB_BCN_START_BSSn(n) BIT(0 + (n)) +#define MT_WF_ARB_BCN_START_T_PRE_TTTT BIT(10) +#define MT_WF_ARB_BCN_START_T_TTTT BIT(11) +#define MT_WF_ARB_BCN_START_T_PRE_TBTT BIT(12) +#define MT_WF_ARB_BCN_START_T_TBTT BIT(13) +#define MT_WF_ARB_BCN_START_T_SLOT_IDLE BIT(14) +#define MT_WF_ARB_BCN_START_T_TX_START BIT(15) +#define MT_WF_ARB_BCN_START_BSS0n(n) BIT((n) ? 16 + ((n) - 1) : 0) + +#define MT_WF_ARB_BCN_FLUSH MT_WF_ARB(0x11c) +#define MT_WF_ARB_BCN_FLUSH_BSSn(n) BIT(0 + (n)) +#define MT_WF_ARB_BCN_FLUSH_BSS0n(n) BIT((n) ? 16 + ((n) - 1) : 0) + +#define MT_WF_ARB_CAB_START MT_WF_ARB(0x120) +#define MT_WF_ARB_CAB_START_BSSn(n) BIT(0 + (n)) +#define MT_WF_ARB_CAB_START_BSS0n(n) BIT((n) ? 16 + ((n) - 1) : 0) + +#define MT_WF_ARB_CAB_FLUSH MT_WF_ARB(0x124) +#define MT_WF_ARB_CAB_FLUSH_BSSn(n) BIT(0 + (n)) +#define MT_WF_ARB_CAB_FLUSH_BSS0n(n) BIT((n) ? 16 + ((n) - 1) : 0) + +#define MT_WF_ARB_CAB_COUNT(n) MT_WF_ARB(0x128 + (n) * 4) +#define MT_WF_ARB_CAB_COUNT_SHIFT 4 +#define MT_WF_ARB_CAB_COUNT_MASK GENMASK(3, 0) +#define MT_WF_ARB_CAB_COUNT_B0_REG(n) MT_WF_ARB_CAB_COUNT(((n) > 12 ? 2 : \ + ((n) > 4 ? 1 : 0))) +#define MT_WF_ARB_CAB_COUNT_B0_SHIFT(n) (((n) > 12 ? (n) - 12 : \ + ((n) > 4 ? (n) - 4 : \ + (n) ? (n) + 3 : 0)) * 4) + +#define MT_TX_ABORT MT_WF_ARB(0x134) +#define MT_TX_ABORT_EN BIT(0) +#define MT_TX_ABORT_WCID GENMASK(15, 8) + +#define MT_WF_TMAC_BASE 0x21600 +#define MT_WF_TMAC(ofs) (MT_WF_TMAC_BASE + (ofs)) + +#define MT_TMAC_TCR MT_WF_TMAC(0x000) +#define MT_TMAC_TCR_BLINK_SEL GENMASK(7, 6) +#define MT_TMAC_TCR_PRE_RTS_GUARD GENMASK(11, 8) +#define MT_TMAC_TCR_PRE_RTS_SEC_IDLE GENMASK(13, 12) +#define MT_TMAC_TCR_RTS_SIGTA BIT(14) +#define MT_TMAC_TCR_LDPC_OFS BIT(15) +#define MT_TMAC_TCR_TX_STREAMS GENMASK(17, 16) +#define MT_TMAC_TCR_SCH_IDLE_SEL GENMASK(19, 18) +#define MT_TMAC_TCR_SCH_DET_PER_IOD BIT(20) +#define MT_TMAC_TCR_DCH_DET_DISABLE BIT(21) +#define MT_TMAC_TCR_TX_RIFS BIT(22) +#define MT_TMAC_TCR_RX_RIFS_MODE BIT(23) +#define MT_TMAC_TCR_TXOP_TBTT_CTL BIT(24) +#define MT_TMAC_TCR_TBTT_TX_STOP_CTL BIT(25) +#define MT_TMAC_TCR_TXOP_BURST_STOP BIT(26) +#define MT_TMAC_TCR_RDG_RA_MODE BIT(27) +#define MT_TMAC_TCR_RDG_RESP BIT(29) +#define MT_TMAC_TCR_RDG_NO_PENDING BIT(30) +#define MT_TMAC_TCR_SMOOTHING BIT(31) + +#define MT_WMM_TXOP_BASE MT_WF_TMAC(0x010) +#define MT_WMM_TXOP(_n) (MT_WMM_TXOP_BASE + \ + ((((_n) / 2) ^ 0x1) << 2)) +#define MT_WMM_TXOP_SHIFT(_n) (((_n) & 1) * 16) +#define MT_WMM_TXOP_MASK GENMASK(15, 0) + +#define MT_TIMEOUT_CCK MT_WF_TMAC(0x090) +#define MT_TIMEOUT_OFDM MT_WF_TMAC(0x094) +#define MT_TIMEOUT_VAL_PLCP GENMASK(15, 0) +#define MT_TIMEOUT_VAL_CCA GENMASK(31, 16) + +#define MT_TXREQ MT_WF_TMAC(0x09c) +#define MT_TXREQ_CCA_SRC_SEL GENMASK(31, 30) + +#define MT_RXREQ MT_WF_TMAC(0x0a0) +#define MT_RXREQ_DELAY GENMASK(8, 0) + +#define MT_IFS MT_WF_TMAC(0x0a4) +#define MT_IFS_EIFS GENMASK(8, 0) +#define MT_IFS_RIFS GENMASK(14, 10) +#define MT_IFS_SIFS GENMASK(22, 16) +#define MT_IFS_SLOT GENMASK(30, 24) + +#define MT_TMAC_PCR MT_WF_TMAC(0x0b4) +#define MT_TMAC_PCR_RATE GENMASK(8, 0) +#define MT_TMAC_PCR_RATE_FIXED BIT(15) +#define MT_TMAC_PCR_ANT_ID GENMASK(21, 16) +#define MT_TMAC_PCR_ANT_ID_SEL BIT(22) +#define MT_TMAC_PCR_SPE_EN BIT(23) +#define MT_TMAC_PCR_ANT_PRI GENMASK(26, 24) +#define MT_TMAC_PCR_ANT_PRI_SEL GENMASK(27) + +#define MT_WF_RMAC_BASE 0x21800 +#define MT_WF_RMAC(ofs) (MT_WF_RMAC_BASE + (ofs)) + +#define MT_WF_RFCR MT_WF_RMAC(0x000) +#define MT_WF_RFCR_DROP_STBC_MULTI BIT(0) +#define MT_WF_RFCR_DROP_FCSFAIL BIT(1) +#define MT_WF_RFCR_DROP_VERSION BIT(3) +#define MT_WF_RFCR_DROP_PROBEREQ BIT(4) +#define MT_WF_RFCR_DROP_MCAST BIT(5) +#define MT_WF_RFCR_DROP_BCAST BIT(6) +#define MT_WF_RFCR_DROP_MCAST_FILTERED BIT(7) +#define MT_WF_RFCR_DROP_A3_MAC BIT(8) +#define MT_WF_RFCR_DROP_A3_BSSID BIT(9) +#define MT_WF_RFCR_DROP_A2_BSSID BIT(10) +#define MT_WF_RFCR_DROP_OTHER_BEACON BIT(11) +#define MT_WF_RFCR_DROP_FRAME_REPORT BIT(12) +#define MT_WF_RFCR_DROP_CTL_RSV BIT(13) +#define MT_WF_RFCR_DROP_CTS BIT(14) +#define MT_WF_RFCR_DROP_RTS BIT(15) +#define MT_WF_RFCR_DROP_DUPLICATE BIT(16) +#define MT_WF_RFCR_DROP_OTHER_BSS BIT(17) +#define MT_WF_RFCR_DROP_OTHER_UC BIT(18) +#define MT_WF_RFCR_DROP_OTHER_TIM BIT(19) +#define MT_WF_RFCR_DROP_NDPA BIT(20) +#define MT_WF_RFCR_DROP_UNWANTED_CTL BIT(21) + +#define MT_BSSID0(idx) MT_WF_RMAC(0x004 + (idx) * 8) +#define MT_BSSID1(idx) MT_WF_RMAC(0x008 + (idx) * 8) +#define MT_BSSID1_VALID BIT(16) + +#define MT_MAC_ADDR0(idx) MT_WF_RMAC(0x024 + (idx) * 8) +#define MT_MAC_ADDR1(idx) MT_WF_RMAC(0x028 + (idx) * 8) +#define MT_MAC_ADDR1_ADDR GENMASK(15, 0) +#define MT_MAC_ADDR1_VALID BIT(16) + +#define MT_BA_CONTROL_0 MT_WF_RMAC(0x068) +#define MT_BA_CONTROL_1 MT_WF_RMAC(0x06c) +#define MT_BA_CONTROL_1_ADDR GENMASK(15, 0) +#define MT_BA_CONTROL_1_TID GENMASK(19, 16) +#define MT_BA_CONTROL_1_IGNORE_TID BIT(20) +#define MT_BA_CONTROL_1_IGNORE_ALL BIT(21) +#define MT_BA_CONTROL_1_RESET BIT(22) + +#define MT_WF_RMACDR MT_WF_RMAC(0x078) +#define MT_WF_RMACDR_TSF_PROBERSP_DIS BIT(0) +#define MT_WF_RMACDR_TSF_TIM BIT(4) +#define MT_WF_RMACDR_MBSSID_MASK GENMASK(25, 24) +#define MT_WF_RMACDR_CHECK_HTC_BY_RATE BIT(26) +#define MT_WF_RMACDR_MAXLEN_20BIT BIT(30) + +#define MT_WF_RMAC_RMCR MT_WF_RMAC(0x080) +#define MT_WF_RMAC_RMCR_SMPS_MODE GENMASK(21, 20) +#define MT_WF_RMAC_RMCR_RX_STREAMS GENMASK(24, 22) +#define MT_WF_RMAC_RMCR_SMPS_RTS BIT(25) + +#define MT_WF_RMAC_CH_FREQ MT_WF_RMAC(0x090) +#define MT_WF_RMAC_MAXMINLEN MT_WF_RMAC(0x098) +#define MT_WF_RFCR1 MT_WF_RMAC(0x0a4) +#define MT_WF_RMAC_TMR_PA MT_WF_RMAC(0x0e0) + +#define MT_WF_SEC_BASE 0x21a00 +#define MT_WF_SEC(ofs) (MT_WF_SEC_BASE + (ofs)) + +#define MT_WF_CFG_OFF_BASE 0x21e00 +#define MT_WF_CFG_OFF(ofs) (MT_WF_CFG_OFF_BASE + (ofs)) +#define MT_WF_CFG_OFF_WOCCR MT_WF_CFG_OFF(0x004) +#define MT_WF_CFG_OFF_WOCCR_TMAC_GC_DIS BIT(4) + +#define MT_SEC_SCR MT_WF_SEC(0x004) +#define MT_SEC_SCR_MASK_ORDER GENMASK(1, 0) + +#define MT_WTBL_OFF_BASE 0x23000 +#define MT_WTBL_OFF(n) (MT_WTBL_OFF_BASE + (n)) + +#define MT_WTBL_UPDATE MT_WTBL_OFF(0x000) +#define MT_WTBL_UPDATE_WLAN_IDX GENMASK(7, 0) +#define MT_WTBL_UPDATE_WTBL2 BIT(11) +#define MT_WTBL_UPDATE_ADM_COUNT_CLEAR BIT(12) +#define MT_WTBL_UPDATE_RATE_UPDATE BIT(13) +#define MT_WTBL_UPDATE_TX_COUNT_CLEAR BIT(14) +#define MT_WTBL_UPDATE_RX_COUNT_CLEAR BIT(15) +#define MT_WTBL_UPDATE_BUSY BIT(16) + +#define MT_WTBL_RMVTCR MT_WTBL_OFF(0x008) +#define MT_WTBL_RMVTCR_RX_MV_MODE BIT(23) + +#define MT_LPON_BASE 0x24000 +#define MT_LPON(n) (MT_LPON_BASE + (n)) + +#define MT_LPON_T0CR MT_LPON(0x010) +#define MT_LPON_T0CR_MODE GENMASK(1, 0) + +#define MT_LPON_UTTR0 MT_LPON(0x018) +#define MT_LPON_UTTR1 MT_LPON(0x01c) + +#define MT_LPON_BTEIR MT_LPON(0x020) +#define MT_LPON_BTEIR_MBSS_MODE GENMASK(31, 29) + +#define MT_PRE_TBTT MT_LPON(0x030) +#define MT_PRE_TBTT_MASK GENMASK(7, 0) +#define MT_PRE_TBTT_SHIFT 8 + +#define MT_TBTT MT_LPON(0x034) +#define MT_TBTT_PERIOD GENMASK(15, 0) +#define MT_TBTT_DTIM_PERIOD GENMASK(23, 16) +#define MT_TBTT_TBTT_WAKE_PERIOD GENMASK(27, 24) +#define MT_TBTT_DTIM_WAKE_PERIOD GENMASK(30, 28) +#define MT_TBTT_CAL_ENABLE BIT(31) + +#define MT_TBTT_TIMER_CFG MT_LPON(0x05c) + +#define MT_LPON_SBTOR(n) MT_LPON(0x0a0) +#define MT_LPON_SBTOR_SUB_BSS_EN BIT(29) +#define MT_LPON_SBTOR_TIME_OFFSET GENMASK(19, 0) + +#define MT_INT_WAKEUP_BASE 0x24400 +#define MT_INT_WAKEUP(n) (MT_INT_WAKEUP_BASE + (n)) + +#define MT_HW_INT_STATUS(n) MT_INT_WAKEUP(0x3c + (n) * 8) +#define MT_HW_INT_MASK(n) MT_INT_WAKEUP(0x40 + (n) * 8) + +#define MT_HW_INT3_TBTT0 BIT(15) +#define MT_HW_INT3_PRE_TBTT0 BIT(31) + +#define MT_WTBL1_BASE 0x28000 + +#define MT_WTBL_ON_BASE (MT_WTBL1_BASE + 0x2000) +#define MT_WTBL_ON(_n) (MT_WTBL_ON_BASE + (_n)) + +#define MT_WTBL_RIUCR0 MT_WTBL_ON(0x200) + +#define MT_WTBL_RIUCR1 MT_WTBL_ON(0x204) +#define MT_WTBL_RIUCR1_RATE0 GENMASK(11, 0) +#define MT_WTBL_RIUCR1_RATE1 GENMASK(23, 12) +#define MT_WTBL_RIUCR1_RATE2_LO GENMASK(31, 24) + +#define MT_WTBL_RIUCR2 MT_WTBL_ON(0x208) +#define MT_WTBL_RIUCR2_RATE2_HI GENMASK(3, 0) +#define MT_WTBL_RIUCR2_RATE3 GENMASK(15, 4) +#define MT_WTBL_RIUCR2_RATE4 GENMASK(27, 16) +#define MT_WTBL_RIUCR2_RATE5_LO GENMASK(31, 28) + +#define MT_WTBL_RIUCR3 MT_WTBL_ON(0x20c) +#define MT_WTBL_RIUCR3_RATE5_HI GENMASK(7, 0) +#define MT_WTBL_RIUCR3_RATE6 GENMASK(19, 8) +#define MT_WTBL_RIUCR3_RATE7 GENMASK(31, 20) + +#define MT_MIB_BASE 0x2c000 +#define MT_MIB(_n) (MT_MIB_BASE + (_n)) + +#define MT_MIB_CTL MT_MIB(0x00) +#define MT_MIB_CTL_PSCCA_TIME GENMASK(13, 11) +#define MT_MIB_CTL_CCA_NAV_TX GENMASK(16, 14) +#define MT_MIB_CTL_ED_TIME GENMASK(30, 28) +#define MT_MIB_CTL_READ_CLR_DIS BIT(31) + +#define MT_MIB_STAT(_n) MT_MIB(0x08 + (_n) * 4) + +#define MT_MIB_STAT_CCA MT_MIB_STAT(9) +#define MT_MIB_STAT_CCA_MASK GENMASK(23, 0) + +#define MT_MIB_STAT_PSCCA MT_MIB_STAT(16) +#define MT_MIB_STAT_PSCCA_MASK GENMASK(23, 0) + +#define MT_TX_AGG_CNT(n) MT_MIB(0xa8 + ((n) << 2)) + +#define MT_MIB_STAT_ED MT_MIB_STAT(18) +#define MT_MIB_STAT_ED_MASK GENMASK(23, 0) + +#define MT_PCIE_REMAP_BASE_1 0x40000 +#define MT_PCIE_REMAP_BASE_2 0x80000 + +#define MT_TX_HW_QUEUE_MGMT 4 +#define MT_TX_HW_QUEUE_MCU 5 +#define MT_TX_HW_QUEUE_BCN 7 +#define MT_TX_HW_QUEUE_BMC 8 + +#define MT_LED_BASE_PHYS 0x80024000 +#define MT_LED_PHYS(_n) (MT_LED_BASE_PHYS + (_n)) + +#define MT_LED_CTRL MT_LED_PHYS(0x00) + +#define MT_LED_CTRL_REPLAY(_n) BIT(0 + (8 * (_n))) +#define MT_LED_CTRL_POLARITY(_n) BIT(1 + (8 * (_n))) +#define MT_LED_CTRL_TX_BLINK_MODE(_n) BIT(2 + (8 * (_n))) +#define MT_LED_CTRL_TX_MANUAL_BLINK(_n) BIT(3 + (8 * (_n))) +#define MT_LED_CTRL_TX_OVER_BLINK(_n) BIT(5 + (8 * (_n))) +#define MT_LED_CTRL_KICK(_n) BIT(7 + (8 * (_n))) + +#define MT_LED_STATUS_0(_n) MT_LED_PHYS(0x10 + ((_n) * 8)) +#define MT_LED_STATUS_1(_n) MT_LED_PHYS(0x14 + ((_n) * 8)) +#define MT_LED_STATUS_OFF GENMASK(31, 24) +#define MT_LED_STATUS_ON GENMASK(23, 16) +#define MT_LED_STATUS_DURATION GENMASK(15, 0) + +#define MT_CLIENT_BASE_PHYS_ADDR 0x800c0000 + +#define MT_CLIENT_TMAC_INFO_TEMPLATE 0x040 + +#define MT_CLIENT_STATUS 0x06c + +#define MT_CLIENT_RESET_TX 0x070 +#define MT_CLIENT_RESET_TX_R_E_1 BIT(16) +#define MT_CLIENT_RESET_TX_R_E_2 BIT(17) +#define MT_CLIENT_RESET_TX_R_E_1_S BIT(20) +#define MT_CLIENT_RESET_TX_R_E_2_S BIT(21) + +#define MT_EFUSE_BASE 0x81070000 + +#define MT_EFUSE_BASE_CTRL 0x000 +#define MT_EFUSE_BASE_CTRL_EMPTY BIT(30) + +#define MT_EFUSE_CTRL 0x008 +#define MT_EFUSE_CTRL_AOUT GENMASK(5, 0) +#define MT_EFUSE_CTRL_MODE GENMASK(7, 6) +#define MT_EFUSE_CTRL_LDO_OFF_TIME GENMASK(13, 8) +#define MT_EFUSE_CTRL_LDO_ON_TIME GENMASK(15, 14) +#define MT_EFUSE_CTRL_AIN GENMASK(25, 16) +#define MT_EFUSE_CTRL_VALID BIT(29) +#define MT_EFUSE_CTRL_KICK BIT(30) +#define MT_EFUSE_CTRL_SEL BIT(31) + +#define MT_EFUSE_WDATA(_i) (0x010 + ((_i) * 4)) +#define MT_EFUSE_RDATA(_i) (0x030 + ((_i) * 4)) + +#define MT_CLIENT_RXINF 0x068 +#define MT_CLIENT_RXINF_RXSH_GROUPS GENMASK(2, 0) + +#define MT_PSE_BASE_PHYS_ADDR 0xa0000000 + +#define MT_PSE_WTBL_2_PHYS_ADDR 0xa5000000 + +#define MT_WTBL1_SIZE (8 * 4) +#define MT_WTBL2_SIZE (16 * 4) +#define MT_WTBL3_OFFSET (MT7603_WTBL_SIZE * MT_WTBL2_SIZE) +#define MT_WTBL3_SIZE (16 * 4) +#define MT_WTBL4_OFFSET (MT7603_WTBL_SIZE * MT_WTBL3_SIZE + \ + MT_WTBL3_OFFSET) +#define MT_WTBL4_SIZE (8 * 4) + +#define MT_WTBL1_W0_ADDR_HI GENMASK(15, 0) +#define MT_WTBL1_W0_MUAR_IDX GENMASK(21, 16) +#define MT_WTBL1_W0_RX_CHECK_A1 BIT(22) +#define MT_WTBL1_W0_KEY_IDX GENMASK(24, 23) +#define MT_WTBL1_W0_RX_CHECK_KEY_IDX BIT(25) +#define MT_WTBL1_W0_RX_KEY_VALID BIT(26) +#define MT_WTBL1_W0_RX_IK_VALID BIT(27) +#define MT_WTBL1_W0_RX_VALID BIT(28) +#define MT_WTBL1_W0_RX_CHECK_A2 BIT(29) +#define MT_WTBL1_W0_RX_DATA_VALID BIT(30) +#define MT_WTBL1_W0_WRITE_BURST BIT(31) + +#define MT_WTBL1_W1_ADDR_LO GENMASK(31, 0) + +#define MT_WTBL1_W2_MPDU_DENSITY GENMASK(2, 0) +#define MT_WTBL1_W2_KEY_TYPE GENMASK(6, 3) +#define MT_WTBL1_W2_EVEN_PN BIT(7) +#define MT_WTBL1_W2_TO_DS BIT(8) +#define MT_WTBL1_W2_FROM_DS BIT(9) +#define MT_WTBL1_W2_HEADER_TRANS BIT(10) +#define MT_WTBL1_W2_AMPDU_FACTOR GENMASK(13, 11) +#define MT_WTBL1_W2_PWR_MGMT BIT(14) +#define MT_WTBL1_W2_RDG BIT(15) +#define MT_WTBL1_W2_RTS BIT(16) +#define MT_WTBL1_W2_CFACK BIT(17) +#define MT_WTBL1_W2_RDG_BA BIT(18) +#define MT_WTBL1_W2_SMPS BIT(19) +#define MT_WTBL1_W2_TXS_BAF_REPORT BIT(20) +#define MT_WTBL1_W2_DYN_BW BIT(21) +#define MT_WTBL1_W2_LDPC BIT(22) +#define MT_WTBL1_W2_ITXBF BIT(23) +#define MT_WTBL1_W2_ETXBF BIT(24) +#define MT_WTBL1_W2_TXOP_PS BIT(25) +#define MT_WTBL1_W2_MESH BIT(26) +#define MT_WTBL1_W2_QOS BIT(27) +#define MT_WTBL1_W2_HT BIT(28) +#define MT_WTBL1_W2_VHT BIT(29) +#define MT_WTBL1_W2_ADMISSION_CONTROL BIT(30) +#define MT_WTBL1_W2_GROUP_ID BIT(31) + +#define MT_WTBL1_W3_WTBL2_FRAME_ID GENMASK(10, 0) +#define MT_WTBL1_W3_WTBL2_ENTRY_ID GENMASK(15, 11) +#define MT_WTBL1_W3_WTBL4_FRAME_ID GENMASK(26, 16) +#define MT_WTBL1_W3_CHECK_PER BIT(27) +#define MT_WTBL1_W3_KEEP_I_PSM BIT(28) +#define MT_WTBL1_W3_I_PSM BIT(29) +#define MT_WTBL1_W3_POWER_SAVE BIT(30) +#define MT_WTBL1_W3_SKIP_TX BIT(31) + +#define MT_WTBL1_W4_WTBL3_FRAME_ID GENMASK(10, 0) +#define MT_WTBL1_W4_WTBL3_ENTRY_ID GENMASK(16, 11) +#define MT_WTBL1_W4_WTBL4_ENTRY_ID GENMASK(22, 17) +#define MT_WTBL1_W4_PARTIAL_AID GENMASK(31, 23) + +#define MT_WTBL2_W0_PN_LO GENMASK(31, 0) + +#define MT_WTBL2_W1_PN_HI GENMASK(15, 0) +#define MT_WTBL2_W1_NON_QOS_SEQNO GENMASK(27, 16) + +#define MT_WTBL2_W2_TID0_SN GENMASK(11, 0) +#define MT_WTBL2_W2_TID1_SN GENMASK(23, 12) +#define MT_WTBL2_W2_TID2_SN_LO GENMASK(31, 24) + +#define MT_WTBL2_W3_TID2_SN_HI GENMASK(3, 0) +#define MT_WTBL2_W3_TID3_SN GENMASK(15, 4) +#define MT_WTBL2_W3_TID4_SN GENMASK(27, 16) +#define MT_WTBL2_W3_TID5_SN_LO GENMASK(31, 28) + +#define MT_WTBL2_W4_TID5_SN_HI GENMASK(7, 0) +#define MT_WTBL2_W4_TID6_SN GENMASK(19, 8) +#define MT_WTBL2_W4_TID7_SN GENMASK(31, 20) + +#define MT_WTBL2_W5_TX_COUNT_RATE1 GENMASK(15, 0) +#define MT_WTBL2_W5_FAIL_COUNT_RATE1 GENAMSK(31, 16) + +#define MT_WTBL2_W6_TX_COUNT_RATE2 GENMASK(7, 0) +#define MT_WTBL2_W6_TX_COUNT_RATE3 GENMASK(15, 8) +#define MT_WTBL2_W6_TX_COUNT_RATE4 GENMASK(23, 16) +#define MT_WTBL2_W6_TX_COUNT_RATE5 GENMASK(31, 24) + +#define MT_WTBL2_W7_TX_COUNT_CUR_BW GENMASK(15, 0) +#define MT_WTBL2_W7_FAIL_COUNT_CUR_BW GENMASK(31, 16) + +#define MT_WTBL2_W8_TX_COUNT_OTHER_BW GENMASK(15, 0) +#define MT_WTBL2_W8_FAIL_COUNT_OTHER_BW GENMASK(31, 16) + +#define MT_WTBL2_W9_POWER_OFFSET GENMASK(4, 0) +#define MT_WTBL2_W9_SPATIAL_EXT BIT(5) +#define MT_WTBL2_W9_ANT_PRIORITY GENMASK(8, 6) +#define MT_WTBL2_W9_CC_BW_SEL GENMASK(10, 9) +#define MT_WTBL2_W9_CHANGE_BW_RATE GENMASK(13, 11) +#define MT_WTBL2_W9_BW_CAP GENMASK(15, 14) +#define MT_WTBL2_W9_SHORT_GI_20 BIT(16) +#define MT_WTBL2_W9_SHORT_GI_40 BIT(17) +#define MT_WTBL2_W9_SHORT_GI_80 BIT(18) +#define MT_WTBL2_W9_SHORT_GI_160 BIT(19) +#define MT_WTBL2_W9_MPDU_FAIL_COUNT GENMASK(25, 23) +#define MT_WTBL2_W9_MPDU_OK_COUNT GENMASK(28, 26) +#define MT_WTBL2_W9_RATE_IDX GENMASK(31, 29) + +#define MT_WTBL2_W10_RATE1 GENMASK(11, 0) +#define MT_WTBL2_W10_RATE2 GENMASK(23, 12) +#define MT_WTBL2_W10_RATE3_LO GENMASK(31, 24) + +#define MT_WTBL2_W11_RATE3_HI GENMASK(3, 0) +#define MT_WTBL2_W11_RATE4 GENMASK(15, 4) +#define MT_WTBL2_W11_RATE5 GENMASK(27, 16) +#define MT_WTBL2_W11_RATE6_LO GENMASK(31, 28) + +#define MT_WTBL2_W12_RATE6_HI GENMASK(7, 0) +#define MT_WTBL2_W12_RATE7 GENMASK(19, 8) +#define MT_WTBL2_W12_RATE8 GENMASK(31, 20) + +#define MT_WTBL2_W13_AVG_RCPI0 GENMASK(7, 0) +#define MT_WTBL2_W13_AVG_RCPI1 GENMASK(15, 8) +#define MT_WTBL2_W13_AVG_RCPI2 GENAMSK(23, 16) + +#define MT_WTBL2_W14_CC_NOISE_1S GENMASK(6, 0) +#define MT_WTBL2_W14_CC_NOISE_2S GENMASK(13, 7) +#define MT_WTBL2_W14_CC_NOISE_3S GENMASK(20, 14) +#define MT_WTBL2_W14_CHAN_EST_RMS GENMASK(24, 21) +#define MT_WTBL2_W14_CC_NOISE_SEL BIT(15) +#define MT_WTBL2_W14_ANT_SEL GENMASK(31, 26) + +#define MT_WTBL2_W15_BA_WIN_SIZE GENMASK(2, 0) +#define MT_WTBL2_W15_BA_WIN_SIZE_SHIFT 3 +#define MT_WTBL2_W15_BA_EN_TIDS GENMASK(31, 24) + +#define MT_WTBL1_OR (MT_WTBL1_BASE + 0x2300) +#define MT_WTBL1_OR_PSM_WRITE BIT(31) + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/soc.c b/drivers/net/wireless/mediatek/mt76/mt7603/soc.c new file mode 100644 index 000000000..b74256efb --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7603/soc.c @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear + +#include +#include +#include + +#include "mt7603.h" + +static int +mt76_wmac_probe(struct platform_device *pdev) +{ + struct mt7603_dev *dev; + void __iomem *mem_base; + struct mt76_dev *mdev; + int irq; + int ret; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + mem_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(mem_base)) + return PTR_ERR(mem_base); + + mdev = mt76_alloc_device(&pdev->dev, sizeof(*dev), &mt7603_ops, + &mt7603_drv_ops); + if (!mdev) + return -ENOMEM; + + dev = container_of(mdev, struct mt7603_dev, mt76); + mt76_mmio_init(mdev, mem_base); + + mdev->rev = (mt76_rr(dev, MT_HW_CHIPID) << 16) | + (mt76_rr(dev, MT_HW_REV) & 0xff); + dev_info(mdev->dev, "ASIC revision: %04x\n", mdev->rev); + + mt76_wr(dev, MT_INT_MASK_CSR, 0); + + ret = devm_request_irq(mdev->dev, irq, mt7603_irq_handler, + IRQF_SHARED, KBUILD_MODNAME, dev); + if (ret) + goto error; + + ret = mt7603_register_device(dev); + if (ret) + goto error; + + return 0; +error: + mt76_free_device(mdev); + return ret; +} + +static void mt76_wmac_remove(struct platform_device *pdev) +{ + struct mt76_dev *mdev = platform_get_drvdata(pdev); + struct mt7603_dev *dev = container_of(mdev, struct mt7603_dev, mt76); + + mt7603_unregister_device(dev); +} + +static const struct of_device_id of_wmac_match[] = { + { .compatible = "mediatek,mt7628-wmac" }, + {}, +}; + +MODULE_DEVICE_TABLE(of, of_wmac_match); +MODULE_FIRMWARE(MT7628_FIRMWARE_E1); +MODULE_FIRMWARE(MT7628_FIRMWARE_E2); + +struct platform_driver mt76_wmac_driver = { + .probe = mt76_wmac_probe, + .remove = mt76_wmac_remove, + .driver = { + .name = "mt76_wmac", + .of_match_table = of_wmac_match, + }, +}; diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/Kconfig b/drivers/net/wireless/mediatek/mt76/mt7615/Kconfig new file mode 100644 index 000000000..8cc0c1b5c --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7615/Kconfig @@ -0,0 +1,56 @@ +# SPDX-License-Identifier: BSD-3-Clause-Clear + +config MT7615_COMMON + tristate + select WANT_DEV_COREDUMP + select MT76_CONNAC_LIB + +config MT7615E + tristate "MediaTek MT7615E and MT7663E (PCIe) support" + select MT7615_COMMON + depends on MAC80211 + depends on PCI + help + This adds support for MT7615-based PCIe wireless devices, + which support concurrent dual-band operation at both 5GHz + and 2.4GHz, IEEE 802.11ac 4x4:4SS 1733Mbps PHY rate, wave2 + MU-MIMO up to 4 users/group and 160MHz channels. + + To compile this driver as a module, choose M here. + +config MT7622_WMAC + bool "MT7622 (SoC) WMAC support" + depends on MT7615E + depends on ARCH_MEDIATEK || COMPILE_TEST + select REGMAP + default y + help + This adds support for the built-in WMAC on MT7622 SoC devices + which has the same feature set as a MT7615, but limited to + 2.4 GHz only. + +config MT7663_USB_SDIO_COMMON + tristate + select MT7615_COMMON + +config MT7663U + tristate "MediaTek MT7663U (USB) support" + select MT76_USB + select MT7663_USB_SDIO_COMMON + depends on MAC80211 + depends on USB + help + This adds support for MT7663U 802.11ac 2x2:2 wireless devices. + + To compile this driver as a module, choose M here. + +config MT7663S + tristate "MediaTek MT7663S (SDIO) support" + select MT76_SDIO + select MT7663_USB_SDIO_COMMON + depends on MAC80211 + depends on MMC + help + This adds support for MT7663S 802.11ac 2x2:2 wireless devices. + + To compile this driver as a module, choose M here. diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/Makefile b/drivers/net/wireless/mediatek/mt76/mt7615/Makefile new file mode 100644 index 000000000..4def3b13e --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7615/Makefile @@ -0,0 +1,20 @@ +# SPDX-License-Identifier: BSD-3-Clause-Clear + +obj-$(CONFIG_MT7615_COMMON) += mt7615-common.o +obj-$(CONFIG_MT7615E) += mt7615e.o +obj-$(CONFIG_MT7663_USB_SDIO_COMMON) += mt7663-usb-sdio-common.o +obj-$(CONFIG_MT7663U) += mt7663u.o +obj-$(CONFIG_MT7663S) += mt7663s.o + +CFLAGS_trace.o := -I$(src) + +mt7615-common-y := main.o init.o mcu.o eeprom.o mac.o \ + debugfs.o trace.o +mt7615-common-$(CONFIG_NL80211_TESTMODE) += testmode.o + +mt7615e-y := pci.o pci_init.o dma.o pci_mac.o mmio.o +mt7615e-$(CONFIG_MT7622_WMAC) += soc.o + +mt7663-usb-sdio-common-y := usb_sdio.o +mt7663u-y := usb.o usb_mcu.o +mt7663s-y := sdio.o sdio_mcu.o diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7615/debugfs.c new file mode 100644 index 000000000..6a1475e3c --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7615/debugfs.c @@ -0,0 +1,607 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear + +#include "mt7615.h" + +static int +mt7615_reg_set(void *data, u64 val) +{ + struct mt7615_dev *dev = data; + + mt7615_mutex_acquire(dev); + mt76_wr(dev, dev->mt76.debugfs_reg, val); + mt7615_mutex_release(dev); + + return 0; +} + +static int +mt7615_reg_get(void *data, u64 *val) +{ + struct mt7615_dev *dev = data; + + mt7615_mutex_acquire(dev); + *val = mt76_rr(dev, dev->mt76.debugfs_reg); + mt7615_mutex_release(dev); + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_regval, mt7615_reg_get, mt7615_reg_set, + "0x%08llx\n"); + +static int +mt7615_radar_pattern_set(void *data, u64 val) +{ + struct mt7615_dev *dev = data; + int err; + + if (!mt7615_wait_for_mcu_init(dev)) + return 0; + + mt7615_mutex_acquire(dev); + err = mt7615_mcu_rdd_send_pattern(dev); + mt7615_mutex_release(dev); + + return err; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_radar_pattern, NULL, + mt7615_radar_pattern_set, "%lld\n"); + +static int mt7615_config(void *data, u64 val) +{ + struct mt7615_dev *dev = data; + int ret; + + mt7615_mutex_acquire(dev); + ret = mt76_connac_mcu_chip_config(&dev->mt76); + mt7615_mutex_release(dev); + + return ret; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_config, NULL, mt7615_config, "%lld\n"); + +static int +mt7615_scs_set(void *data, u64 val) +{ + struct mt7615_dev *dev = data; + struct mt7615_phy *ext_phy; + + if (!mt7615_wait_for_mcu_init(dev)) + return 0; + + mt7615_mac_set_scs(&dev->phy, val); + ext_phy = mt7615_ext_phy(dev); + if (ext_phy) + mt7615_mac_set_scs(ext_phy, val); + + return 0; +} + +static int +mt7615_scs_get(void *data, u64 *val) +{ + struct mt7615_dev *dev = data; + + *val = dev->phy.scs_en; + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_scs, mt7615_scs_get, + mt7615_scs_set, "%lld\n"); + +static int +mt7615_pm_set(void *data, u64 val) +{ + struct mt7615_dev *dev = data; + struct mt76_connac_pm *pm = &dev->pm; + int ret = 0; + + if (!mt7615_wait_for_mcu_init(dev)) + return 0; + + if (!mt7615_firmware_offload(dev) || mt76_is_usb(&dev->mt76)) + return -EOPNOTSUPP; + + mutex_lock(&dev->mt76.mutex); + + if (val == pm->enable) + goto out; + + if (dev->phy.n_beacon_vif) { + ret = -EBUSY; + goto out; + } + + if (!pm->enable) { + pm->stats.last_wake_event = jiffies; + pm->stats.last_doze_event = jiffies; + } + /* make sure the chip is awake here and ps_work is scheduled + * just at end of the this routine. + */ + pm->enable = false; + mt76_connac_pm_wake(&dev->mphy, pm); + + pm->enable = val; + mt76_connac_power_save_sched(&dev->mphy, pm); +out: + mutex_unlock(&dev->mt76.mutex); + + return ret; +} + +static int +mt7615_pm_get(void *data, u64 *val) +{ + struct mt7615_dev *dev = data; + + *val = dev->pm.enable; + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_pm, mt7615_pm_get, mt7615_pm_set, "%lld\n"); + +static int +mt7615_pm_stats(struct seq_file *s, void *data) +{ + struct mt7615_dev *dev = dev_get_drvdata(s->private); + struct mt76_connac_pm *pm = &dev->pm; + unsigned long awake_time = pm->stats.awake_time; + unsigned long doze_time = pm->stats.doze_time; + + if (!test_bit(MT76_STATE_PM, &dev->mphy.state)) + awake_time += jiffies - pm->stats.last_wake_event; + else + doze_time += jiffies - pm->stats.last_doze_event; + + seq_printf(s, "awake time: %14u\ndoze time: %15u\n", + jiffies_to_msecs(awake_time), + jiffies_to_msecs(doze_time)); + + return 0; +} + +static int +mt7615_pm_idle_timeout_set(void *data, u64 val) +{ + struct mt7615_dev *dev = data; + + dev->pm.idle_timeout = msecs_to_jiffies(val); + + return 0; +} + +static int +mt7615_pm_idle_timeout_get(void *data, u64 *val) +{ + struct mt7615_dev *dev = data; + + *val = jiffies_to_msecs(dev->pm.idle_timeout); + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_pm_idle_timeout, mt7615_pm_idle_timeout_get, + mt7615_pm_idle_timeout_set, "%lld\n"); + +static int +mt7615_dbdc_set(void *data, u64 val) +{ + struct mt7615_dev *dev = data; + + if (!mt7615_wait_for_mcu_init(dev)) + return 0; + + if (val) + mt7615_register_ext_phy(dev); + else + mt7615_unregister_ext_phy(dev); + + return 0; +} + +static int +mt7615_dbdc_get(void *data, u64 *val) +{ + struct mt7615_dev *dev = data; + + *val = !!mt7615_ext_phy(dev); + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_dbdc, mt7615_dbdc_get, + mt7615_dbdc_set, "%lld\n"); + +static int +mt7615_fw_debug_set(void *data, u64 val) +{ + struct mt7615_dev *dev = data; + + if (!mt7615_wait_for_mcu_init(dev)) + return 0; + + dev->fw_debug = val; + + mt7615_mutex_acquire(dev); + mt7615_mcu_fw_log_2_host(dev, dev->fw_debug ? 2 : 0); + mt7615_mutex_release(dev); + + return 0; +} + +static int +mt7615_fw_debug_get(void *data, u64 *val) +{ + struct mt7615_dev *dev = data; + + *val = dev->fw_debug; + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_fw_debug, mt7615_fw_debug_get, + mt7615_fw_debug_set, "%lld\n"); + +static int +mt7615_reset_test_set(void *data, u64 val) +{ + struct mt7615_dev *dev = data; + struct sk_buff *skb; + + if (!mt7615_wait_for_mcu_init(dev)) + return 0; + + skb = alloc_skb(1, GFP_KERNEL); + if (!skb) + return -ENOMEM; + + skb_put(skb, 1); + + mt7615_mutex_acquire(dev); + mt76_tx_queue_skb_raw(dev, dev->mphy.q_tx[0], skb, 0); + mt7615_mutex_release(dev); + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_reset_test, NULL, + mt7615_reset_test_set, "%lld\n"); + +static void +mt7615_ampdu_stat_read_phy(struct mt7615_phy *phy, + struct seq_file *file) +{ + struct mt7615_dev *dev = file->private; + u32 reg = is_mt7663(&dev->mt76) ? MT_MIB_ARNG(0) : MT_AGG_ASRCR0; + int bound[7], i, range; + + if (!phy) + return; + + range = mt76_rr(dev, reg); + for (i = 0; i < 4; i++) + bound[i] = MT_AGG_ASRCR_RANGE(range, i) + 1; + + range = mt76_rr(dev, reg + 4); + for (i = 0; i < 3; i++) + bound[i + 4] = MT_AGG_ASRCR_RANGE(range, i) + 1; + + seq_printf(file, "\nPhy %d\n", phy != &dev->phy); + + seq_printf(file, "Length: %8d | ", bound[0]); + for (i = 0; i < ARRAY_SIZE(bound) - 1; i++) + seq_printf(file, "%3d -%3d | ", + bound[i], bound[i + 1]); + seq_puts(file, "\nCount: "); + + for (i = 0; i < ARRAY_SIZE(bound); i++) + seq_printf(file, "%8d | ", phy->mt76->aggr_stats[i]); + seq_puts(file, "\n"); + + seq_printf(file, "BA miss count: %d\n", phy->mib.ba_miss_cnt); + seq_printf(file, "PER: %ld.%1ld%%\n", + phy->mib.aggr_per / 10, phy->mib.aggr_per % 10); +} + +static int +mt7615_ampdu_stat_show(struct seq_file *file, void *data) +{ + struct mt7615_dev *dev = file->private; + + mt7615_mutex_acquire(dev); + + mt7615_ampdu_stat_read_phy(&dev->phy, file); + mt7615_ampdu_stat_read_phy(mt7615_ext_phy(dev), file); + + mt7615_mutex_release(dev); + + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(mt7615_ampdu_stat); + +static void +mt7615_radio_read_phy(struct mt7615_phy *phy, struct seq_file *s) +{ + struct mt7615_dev *dev = dev_get_drvdata(s->private); + bool ext_phy = phy != &dev->phy; + + if (!phy) + return; + + seq_printf(s, "Radio %d sensitivity: ofdm=%d cck=%d\n", ext_phy, + phy->ofdm_sensitivity, phy->cck_sensitivity); + seq_printf(s, "Radio %d false CCA: ofdm=%d cck=%d\n", ext_phy, + phy->false_cca_ofdm, phy->false_cca_cck); +} + +static int +mt7615_radio_read(struct seq_file *s, void *data) +{ + struct mt7615_dev *dev = dev_get_drvdata(s->private); + + mt7615_radio_read_phy(&dev->phy, s); + mt7615_radio_read_phy(mt7615_ext_phy(dev), s); + + return 0; +} + +static int +mt7615_queues_acq(struct seq_file *s, void *data) +{ + struct mt7615_dev *dev = dev_get_drvdata(s->private); + int i; + + mt7615_mutex_acquire(dev); + + for (i = 0; i < 16; i++) { + int j, wmm_idx = i % MT7615_MAX_WMM_SETS; + int acs = i / MT7615_MAX_WMM_SETS; + u32 ctrl, val, qlen = 0; + + if (wmm_idx == 3 && is_mt7663(&dev->mt76)) + continue; + + val = mt76_rr(dev, MT_PLE_AC_QEMPTY(acs, wmm_idx)); + ctrl = BIT(31) | BIT(15) | (acs << 8); + + for (j = 0; j < 32; j++) { + if (val & BIT(j)) + continue; + + mt76_wr(dev, MT_PLE_FL_Q0_CTRL, + ctrl | (j + (wmm_idx << 5))); + qlen += mt76_get_field(dev, MT_PLE_FL_Q3_CTRL, + GENMASK(11, 0)); + } + seq_printf(s, "AC%d%d: queued=%d\n", wmm_idx, acs, qlen); + } + + mt7615_mutex_release(dev); + + return 0; +} + +static int +mt7615_queues_read(struct seq_file *s, void *data) +{ + struct mt7615_dev *dev = dev_get_drvdata(s->private); + struct { + struct mt76_queue *q; + char *queue; + } queue_map[] = { + { dev->mphy.q_tx[MT_TXQ_BE], "PDMA0" }, + { dev->mt76.q_mcu[MT_MCUQ_WM], "MCUQ" }, + { dev->mt76.q_mcu[MT_MCUQ_FWDL], "MCUFWQ" }, + }; + int i; + + for (i = 0; i < ARRAY_SIZE(queue_map); i++) { + struct mt76_queue *q = queue_map[i].q; + + seq_printf(s, + "%s: queued=%d head=%d tail=%d\n", + queue_map[i].queue, q->queued, q->head, + q->tail); + } + + return 0; +} + +static int +mt7615_rf_reg_set(void *data, u64 val) +{ + struct mt7615_dev *dev = data; + + mt7615_rf_wr(dev, dev->debugfs_rf_wf, dev->debugfs_rf_reg, val); + + return 0; +} + +static int +mt7615_rf_reg_get(void *data, u64 *val) +{ + struct mt7615_dev *dev = data; + + *val = mt7615_rf_rr(dev, dev->debugfs_rf_wf, dev->debugfs_rf_reg); + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_rf_reg, mt7615_rf_reg_get, mt7615_rf_reg_set, + "0x%08llx\n"); + +static ssize_t +mt7615_ext_mac_addr_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct mt7615_dev *dev = file->private_data; + u32 len = 32 * ((ETH_ALEN * 3) + 4) + 1; + u8 addr[ETH_ALEN]; + char *buf; + int ofs = 0; + int i; + + buf = kzalloc(len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + for (i = 0; i < 32; i++) { + if (!(dev->muar_mask & BIT(i))) + continue; + + mt76_wr(dev, MT_WF_RMAC_MAR1, + FIELD_PREP(MT_WF_RMAC_MAR1_IDX, i * 2) | + MT_WF_RMAC_MAR1_START); + put_unaligned_le32(mt76_rr(dev, MT_WF_RMAC_MAR0), addr); + put_unaligned_le16((mt76_rr(dev, MT_WF_RMAC_MAR1) & + MT_WF_RMAC_MAR1_ADDR), addr + 4); + ofs += snprintf(buf + ofs, len - ofs, "%d=%pM\n", i, addr); + } + + ofs = simple_read_from_buffer(userbuf, count, ppos, buf, ofs); + + kfree(buf); + return ofs; +} + +static ssize_t +mt7615_ext_mac_addr_write(struct file *file, const char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct mt7615_dev *dev = file->private_data; + unsigned long idx = 0; + u8 addr[ETH_ALEN]; + char buf[32]; + char *p; + + if (count > sizeof(buf)) + return -EINVAL; + + if (copy_from_user(buf, userbuf, count)) + return -EFAULT; + + buf[sizeof(buf) - 1] = '\0'; + + p = strchr(buf, '='); + if (p) { + *p = 0; + p++; + + if (kstrtoul(buf, 0, &idx) || idx > 31) + return -EINVAL; + } else { + idx = 0; + p = buf; + } + + if (!mac_pton(p, addr)) + return -EINVAL; + + if (is_valid_ether_addr(addr)) { + dev->muar_mask |= BIT(idx); + } else { + memset(addr, 0, sizeof(addr)); + dev->muar_mask &= ~BIT(idx); + } + + mt76_rmw_field(dev, MT_WF_RMAC_MORE(0), MT_WF_RMAC_MORE_MUAR_MODE, 1); + mt76_wr(dev, MT_WF_RMAC_MAR0, get_unaligned_le32(addr)); + mt76_wr(dev, MT_WF_RMAC_MAR1, + get_unaligned_le16(addr + 4) | + FIELD_PREP(MT_WF_RMAC_MAR1_IDX, idx * 2) | + MT_WF_RMAC_MAR1_START | + MT_WF_RMAC_MAR1_WRITE); + + mt76_rmw_field(dev, MT_WF_RMAC_MORE(0), MT_WF_RMAC_MORE_MUAR_MODE, !!dev->muar_mask); + + return count; +} + +static const struct file_operations fops_ext_mac_addr = { + .open = simple_open, + .llseek = generic_file_llseek, + .read = mt7615_ext_mac_addr_read, + .write = mt7615_ext_mac_addr_write, + .owner = THIS_MODULE, +}; + +static int +mt7663s_sched_quota_read(struct seq_file *s, void *data) +{ + struct mt7615_dev *dev = dev_get_drvdata(s->private); + struct mt76_sdio *sdio = &dev->mt76.sdio; + + seq_printf(s, "pse_data_quota\t%d\n", sdio->sched.pse_data_quota); + seq_printf(s, "ple_data_quota\t%d\n", sdio->sched.ple_data_quota); + seq_printf(s, "pse_mcu_quota\t%d\n", sdio->sched.pse_mcu_quota); + seq_printf(s, "sched_deficit\t%d\n", sdio->sched.deficit); + + return 0; +} + +int mt7615_init_debugfs(struct mt7615_dev *dev) +{ + struct dentry *dir; + + dir = mt76_register_debugfs_fops(&dev->mphy, &fops_regval); + + if (is_mt7615(&dev->mt76)) + debugfs_create_devm_seqfile(dev->mt76.dev, "xmit-queues", dir, + mt7615_queues_read); + else + debugfs_create_devm_seqfile(dev->mt76.dev, "xmit-queues", dir, + mt76_queues_read); + debugfs_create_devm_seqfile(dev->mt76.dev, "acq", dir, + mt7615_queues_acq); + debugfs_create_file("ampdu_stat", 0400, dir, dev, &mt7615_ampdu_stat_fops); + debugfs_create_file("scs", 0600, dir, dev, &fops_scs); + debugfs_create_file("dbdc", 0600, dir, dev, &fops_dbdc); + debugfs_create_file("fw_debug", 0600, dir, dev, &fops_fw_debug); + debugfs_create_file("runtime-pm", 0600, dir, dev, &fops_pm); + debugfs_create_file("idle-timeout", 0600, dir, dev, + &fops_pm_idle_timeout); + debugfs_create_devm_seqfile(dev->mt76.dev, "runtime_pm_stats", dir, + mt7615_pm_stats); + debugfs_create_devm_seqfile(dev->mt76.dev, "radio", dir, + mt7615_radio_read); + + if (is_mt7615(&dev->mt76)) { + debugfs_create_u32("dfs_hw_pattern", 0400, dir, + &dev->hw_pattern); + /* test pattern knobs */ + debugfs_create_u8("pattern_len", 0600, dir, + &dev->radar_pattern.n_pulses); + debugfs_create_u32("pulse_period", 0600, dir, + &dev->radar_pattern.period); + debugfs_create_u16("pulse_width", 0600, dir, + &dev->radar_pattern.width); + debugfs_create_u16("pulse_power", 0600, dir, + &dev->radar_pattern.power); + debugfs_create_file("radar_trigger", 0200, dir, dev, + &fops_radar_pattern); + } + + debugfs_create_file("reset_test", 0200, dir, dev, + &fops_reset_test); + debugfs_create_file("ext_mac_addr", 0600, dir, dev, &fops_ext_mac_addr); + + debugfs_create_u32("rf_wfidx", 0600, dir, &dev->debugfs_rf_wf); + debugfs_create_u32("rf_regidx", 0600, dir, &dev->debugfs_rf_reg); + debugfs_create_file_unsafe("rf_regval", 0600, dir, dev, + &fops_rf_reg); + if (is_mt7663(&dev->mt76)) + debugfs_create_file("chip_config", 0600, dir, dev, + &fops_config); + if (mt76_is_sdio(&dev->mt76)) + debugfs_create_devm_seqfile(dev->mt76.dev, "sched-quota", dir, + mt7663s_sched_quota_read); + + return 0; +} +EXPORT_SYMBOL_GPL(mt7615_init_debugfs); diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/dma.c b/drivers/net/wireless/mediatek/mt76/mt7615/dma.c new file mode 100644 index 000000000..59d2b3e86 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7615/dma.c @@ -0,0 +1,316 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2019 MediaTek Inc. + * + * Author: Ryder Lee + * Roy Luo + * Lorenzo Bianconi + * Felix Fietkau + */ + +#include "mt7615.h" +#include "../dma.h" +#include "mac.h" + +static int +mt7622_init_tx_queues_multi(struct mt7615_dev *dev) +{ + static const u8 wmm_queue_map[] = { + [IEEE80211_AC_BK] = MT7622_TXQ_AC0, + [IEEE80211_AC_BE] = MT7622_TXQ_AC1, + [IEEE80211_AC_VI] = MT7622_TXQ_AC2, + [IEEE80211_AC_VO] = MT7622_TXQ_AC3, + }; + int ret; + int i; + + for (i = 0; i < ARRAY_SIZE(wmm_queue_map); i++) { + ret = mt76_init_tx_queue(&dev->mphy, i, wmm_queue_map[i], + MT7615_TX_RING_SIZE / 2, + MT_TX_RING_BASE, NULL, 0); + if (ret) + return ret; + } + + ret = mt76_init_tx_queue(&dev->mphy, MT_TXQ_PSD, MT7622_TXQ_MGMT, + MT7615_TX_MGMT_RING_SIZE, + MT_TX_RING_BASE, NULL, 0); + if (ret) + return ret; + + return mt76_init_mcu_queue(&dev->mt76, MT_MCUQ_WM, MT7622_TXQ_MCU, + MT7615_TX_MCU_RING_SIZE, MT_TX_RING_BASE); +} + +static int +mt7615_init_tx_queues(struct mt7615_dev *dev) +{ + int ret; + + ret = mt76_init_mcu_queue(&dev->mt76, MT_MCUQ_FWDL, MT7615_TXQ_FWDL, + MT7615_TX_FWDL_RING_SIZE, MT_TX_RING_BASE); + if (ret) + return ret; + + if (!is_mt7615(&dev->mt76)) + return mt7622_init_tx_queues_multi(dev); + + ret = mt76_connac_init_tx_queues(&dev->mphy, 0, MT7615_TX_RING_SIZE, + MT_TX_RING_BASE, NULL, 0); + if (ret) + return ret; + + return mt76_init_mcu_queue(&dev->mt76, MT_MCUQ_WM, MT7615_TXQ_MCU, + MT7615_TX_MCU_RING_SIZE, MT_TX_RING_BASE); +} + +static int mt7615_poll_tx(struct napi_struct *napi, int budget) +{ + struct mt7615_dev *dev; + + dev = mt76_priv(napi->dev); + if (!mt76_connac_pm_ref(&dev->mphy, &dev->pm)) { + napi_complete(napi); + queue_work(dev->mt76.wq, &dev->pm.wake_work); + return 0; + } + + mt76_queue_tx_cleanup(dev, dev->mt76.q_mcu[MT_MCUQ_WM], false); + if (napi_complete(napi)) + mt76_connac_irq_enable(&dev->mt76, + mt7615_tx_mcu_int_mask(dev)); + + mt76_connac_pm_unref(&dev->mphy, &dev->pm); + + return 0; +} + +static int mt7615_poll_rx(struct napi_struct *napi, int budget) +{ + struct mt7615_dev *dev; + int done; + + dev = mt76_priv(napi->dev); + + if (!mt76_connac_pm_ref(&dev->mphy, &dev->pm)) { + napi_complete(napi); + queue_work(dev->mt76.wq, &dev->pm.wake_work); + return 0; + } + done = mt76_dma_rx_poll(napi, budget); + mt76_connac_pm_unref(&dev->mphy, &dev->pm); + + return done; +} + +int mt7615_wait_pdma_busy(struct mt7615_dev *dev) +{ + struct mt76_dev *mdev = &dev->mt76; + + if (!is_mt7663(mdev)) { + u32 mask = MT_PDMA_TX_BUSY | MT_PDMA_RX_BUSY; + u32 reg = mt7615_reg_map(dev, MT_PDMA_BUSY); + + if (!mt76_poll_msec(dev, reg, mask, 0, 1000)) { + dev_err(mdev->dev, "PDMA engine busy\n"); + return -EIO; + } + + return 0; + } + + if (!mt76_poll_msec(dev, MT_PDMA_BUSY_STATUS, + MT_PDMA_TX_IDX_BUSY, 0, 1000)) { + dev_err(mdev->dev, "PDMA engine tx busy\n"); + return -EIO; + } + + if (!mt76_poll_msec(dev, MT_PSE_PG_INFO, + MT_PSE_SRC_CNT, 0, 1000)) { + dev_err(mdev->dev, "PSE engine busy\n"); + return -EIO; + } + + if (!mt76_poll_msec(dev, MT_PDMA_BUSY_STATUS, + MT_PDMA_BUSY_IDX, 0, 1000)) { + dev_err(mdev->dev, "PDMA engine busy\n"); + return -EIO; + } + + return 0; +} + +static void mt7622_dma_sched_init(struct mt7615_dev *dev) +{ + u32 reg = mt7615_reg_map(dev, MT_DMASHDL_BASE); + int i; + + mt76_rmw(dev, reg + MT_DMASHDL_PKT_MAX_SIZE, + MT_DMASHDL_PKT_MAX_SIZE_PLE | MT_DMASHDL_PKT_MAX_SIZE_PSE, + FIELD_PREP(MT_DMASHDL_PKT_MAX_SIZE_PLE, 1) | + FIELD_PREP(MT_DMASHDL_PKT_MAX_SIZE_PSE, 8)); + + for (i = 0; i <= 5; i++) + mt76_wr(dev, reg + MT_DMASHDL_GROUP_QUOTA(i), + FIELD_PREP(MT_DMASHDL_GROUP_QUOTA_MIN, 0x10) | + FIELD_PREP(MT_DMASHDL_GROUP_QUOTA_MAX, 0x800)); + + mt76_wr(dev, reg + MT_DMASHDL_Q_MAP(0), 0x42104210); + mt76_wr(dev, reg + MT_DMASHDL_Q_MAP(1), 0x42104210); + mt76_wr(dev, reg + MT_DMASHDL_Q_MAP(2), 0x5); + mt76_wr(dev, reg + MT_DMASHDL_Q_MAP(3), 0); + + mt76_wr(dev, reg + MT_DMASHDL_SCHED_SET0, 0x6012345f); + mt76_wr(dev, reg + MT_DMASHDL_SCHED_SET1, 0xedcba987); +} + +static void mt7663_dma_sched_init(struct mt7615_dev *dev) +{ + int i; + + mt76_rmw(dev, MT_DMA_SHDL(MT_DMASHDL_PKT_MAX_SIZE), + MT_DMASHDL_PKT_MAX_SIZE_PLE | MT_DMASHDL_PKT_MAX_SIZE_PSE, + FIELD_PREP(MT_DMASHDL_PKT_MAX_SIZE_PLE, 1) | + FIELD_PREP(MT_DMASHDL_PKT_MAX_SIZE_PSE, 8)); + + /* enable refill control group 0, 1, 2, 4, 5 */ + mt76_wr(dev, MT_DMA_SHDL(MT_DMASHDL_REFILL), 0xffc80000); + /* enable group 0, 1, 2, 4, 5, 15 */ + mt76_wr(dev, MT_DMA_SHDL(MT_DMASHDL_OPTIONAL), 0x70068037); + + /* each group min quota must larger then PLE_PKT_MAX_SIZE_NUM */ + for (i = 0; i < 5; i++) + mt76_wr(dev, MT_DMA_SHDL(MT_DMASHDL_GROUP_QUOTA(i)), + FIELD_PREP(MT_DMASHDL_GROUP_QUOTA_MIN, 0x40) | + FIELD_PREP(MT_DMASHDL_GROUP_QUOTA_MAX, 0x800)); + mt76_wr(dev, MT_DMA_SHDL(MT_DMASHDL_GROUP_QUOTA(5)), + FIELD_PREP(MT_DMASHDL_GROUP_QUOTA_MIN, 0x40) | + FIELD_PREP(MT_DMASHDL_GROUP_QUOTA_MAX, 0x40)); + mt76_wr(dev, MT_DMA_SHDL(MT_DMASHDL_GROUP_QUOTA(15)), + FIELD_PREP(MT_DMASHDL_GROUP_QUOTA_MIN, 0x20) | + FIELD_PREP(MT_DMASHDL_GROUP_QUOTA_MAX, 0x20)); + + mt76_wr(dev, MT_DMA_SHDL(MT_DMASHDL_Q_MAP(0)), 0x42104210); + mt76_wr(dev, MT_DMA_SHDL(MT_DMASHDL_Q_MAP(1)), 0x42104210); + mt76_wr(dev, MT_DMA_SHDL(MT_DMASHDL_Q_MAP(2)), 0x00050005); + mt76_wr(dev, MT_DMA_SHDL(MT_DMASHDL_Q_MAP(3)), 0); + /* ALTX0 and ALTX1 QID mapping to group 5 */ + mt76_wr(dev, MT_DMA_SHDL(MT_DMASHDL_SCHED_SET0), 0x6012345f); + mt76_wr(dev, MT_DMA_SHDL(MT_DMASHDL_SCHED_SET1), 0xedcba987); +} + +void mt7615_dma_start(struct mt7615_dev *dev) +{ + /* start dma engine */ + mt76_set(dev, MT_WPDMA_GLO_CFG, + MT_WPDMA_GLO_CFG_TX_DMA_EN | + MT_WPDMA_GLO_CFG_RX_DMA_EN | + MT_WPDMA_GLO_CFG_TX_WRITEBACK_DONE); + + if (is_mt7622(&dev->mt76)) + mt7622_dma_sched_init(dev); + + if (is_mt7663(&dev->mt76)) { + mt7663_dma_sched_init(dev); + + mt76_wr(dev, MT_MCU2HOST_INT_ENABLE, MT7663_MCU_CMD_ERROR_MASK); + } + +} + +int mt7615_dma_init(struct mt7615_dev *dev) +{ + int rx_ring_size = MT7615_RX_RING_SIZE; + u32 mask; + int ret; + + mt76_dma_attach(&dev->mt76); + + mt76_wr(dev, MT_WPDMA_GLO_CFG, + MT_WPDMA_GLO_CFG_TX_WRITEBACK_DONE | + MT_WPDMA_GLO_CFG_FIFO_LITTLE_ENDIAN | + MT_WPDMA_GLO_CFG_OMIT_TX_INFO); + + mt76_rmw_field(dev, MT_WPDMA_GLO_CFG, + MT_WPDMA_GLO_CFG_TX_BT_SIZE_BIT0, 0x1); + + mt76_rmw_field(dev, MT_WPDMA_GLO_CFG, + MT_WPDMA_GLO_CFG_TX_BT_SIZE_BIT21, 0x1); + + mt76_rmw_field(dev, MT_WPDMA_GLO_CFG, + MT_WPDMA_GLO_CFG_DMA_BURST_SIZE, 0x3); + + mt76_rmw_field(dev, MT_WPDMA_GLO_CFG, + MT_WPDMA_GLO_CFG_MULTI_DMA_EN, 0x3); + + if (is_mt7615(&dev->mt76)) { + mt76_set(dev, MT_WPDMA_GLO_CFG, + MT_WPDMA_GLO_CFG_FIRST_TOKEN_ONLY); + + mt76_wr(dev, MT_WPDMA_GLO_CFG1, 0x1); + mt76_wr(dev, MT_WPDMA_TX_PRE_CFG, 0xf0000); + mt76_wr(dev, MT_WPDMA_RX_PRE_CFG, 0xf7f0000); + mt76_wr(dev, MT_WPDMA_ABT_CFG, 0x4000026); + mt76_wr(dev, MT_WPDMA_ABT_CFG1, 0x18811881); + mt76_set(dev, 0x7158, BIT(16)); + mt76_clear(dev, 0x7000, BIT(23)); + } + + mt76_wr(dev, MT_WPDMA_RST_IDX, ~0); + + ret = mt7615_init_tx_queues(dev); + if (ret) + return ret; + + /* init rx queues */ + ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MCU], 1, + MT7615_RX_MCU_RING_SIZE, MT_RX_BUF_SIZE, + MT_RX_RING_BASE); + if (ret) + return ret; + + if (!is_mt7615(&dev->mt76)) + rx_ring_size /= 2; + + ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MAIN], 0, + rx_ring_size, MT_RX_BUF_SIZE, MT_RX_RING_BASE); + if (ret) + return ret; + + mt76_wr(dev, MT_DELAY_INT_CFG, 0); + + ret = mt76_init_queues(dev, mt7615_poll_rx); + if (ret < 0) + return ret; + + netif_napi_add_tx(dev->mt76.tx_napi_dev, &dev->mt76.tx_napi, + mt7615_poll_tx); + napi_enable(&dev->mt76.tx_napi); + + mt76_poll(dev, MT_WPDMA_GLO_CFG, + MT_WPDMA_GLO_CFG_TX_DMA_BUSY | + MT_WPDMA_GLO_CFG_RX_DMA_BUSY, 0, 1000); + + /* enable interrupts for TX/RX rings */ + + mask = MT_INT_RX_DONE_ALL | mt7615_tx_mcu_int_mask(dev); + if (is_mt7663(&dev->mt76)) + mask |= MT7663_INT_MCU_CMD; + else + mask |= MT_INT_MCU_CMD; + + mt76_connac_irq_enable(&dev->mt76, mask); + + mt7615_dma_start(dev); + + return 0; +} + +void mt7615_dma_cleanup(struct mt7615_dev *dev) +{ + mt76_clear(dev, MT_WPDMA_GLO_CFG, + MT_WPDMA_GLO_CFG_TX_DMA_EN | + MT_WPDMA_GLO_CFG_RX_DMA_EN); + mt76_set(dev, MT_WPDMA_GLO_CFG, MT_WPDMA_GLO_CFG_SW_RESET); + + mt76_dma_cleanup(&dev->mt76); +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt7615/eeprom.c new file mode 100644 index 000000000..d6828e1cd --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7615/eeprom.c @@ -0,0 +1,356 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2019 MediaTek Inc. + * + * Author: Ryder Lee + * Felix Fietkau + */ + +#include +#include "mt7615.h" +#include "eeprom.h" + +static int mt7615_efuse_read(struct mt7615_dev *dev, u32 base, + u16 addr, u8 *data) +{ + u32 val; + int i; + + val = mt76_rr(dev, base + MT_EFUSE_CTRL); + val &= ~(MT_EFUSE_CTRL_AIN | MT_EFUSE_CTRL_MODE); + val |= FIELD_PREP(MT_EFUSE_CTRL_AIN, addr & ~0xf); + val |= MT_EFUSE_CTRL_KICK; + mt76_wr(dev, base + MT_EFUSE_CTRL, val); + + if (!mt76_poll(dev, base + MT_EFUSE_CTRL, MT_EFUSE_CTRL_KICK, 0, 1000)) + return -ETIMEDOUT; + + udelay(2); + + val = mt76_rr(dev, base + MT_EFUSE_CTRL); + if ((val & MT_EFUSE_CTRL_AOUT) == MT_EFUSE_CTRL_AOUT || + WARN_ON_ONCE(!(val & MT_EFUSE_CTRL_VALID))) { + memset(data, 0x0, 16); + return 0; + } + + for (i = 0; i < 4; i++) { + val = mt76_rr(dev, base + MT_EFUSE_RDATA(i)); + put_unaligned_le32(val, data + 4 * i); + } + + return 0; +} + +static int mt7615_efuse_init(struct mt7615_dev *dev, u32 base) +{ + int i, len = MT7615_EEPROM_SIZE; + void *buf; + u32 val; + + if (is_mt7663(&dev->mt76)) + len = MT7663_EEPROM_SIZE; + + val = mt76_rr(dev, base + MT_EFUSE_BASE_CTRL); + if (val & MT_EFUSE_BASE_CTRL_EMPTY) + return 0; + + dev->mt76.otp.data = devm_kzalloc(dev->mt76.dev, len, GFP_KERNEL); + dev->mt76.otp.size = len; + if (!dev->mt76.otp.data) + return -ENOMEM; + + buf = dev->mt76.otp.data; + for (i = 0; i + 16 <= len; i += 16) { + int ret; + + ret = mt7615_efuse_read(dev, base, i, buf + i); + if (ret) + return ret; + } + + return 0; +} + +static int mt7615_eeprom_load(struct mt7615_dev *dev, u32 addr) +{ + int ret; + + BUILD_BUG_ON(MT7615_EEPROM_FULL_SIZE < MT7663_EEPROM_SIZE); + + ret = mt76_eeprom_init(&dev->mt76, MT7615_EEPROM_FULL_SIZE); + if (ret < 0) + return ret; + + return mt7615_efuse_init(dev, addr); +} + +static int mt7615_check_eeprom(struct mt76_dev *dev) +{ + u16 val = get_unaligned_le16(dev->eeprom.data); + + switch (val) { + case 0x7615: + case 0x7622: + case 0x7663: + return 0; + default: + return -EINVAL; + } +} + +static void +mt7615_eeprom_parse_hw_band_cap(struct mt7615_dev *dev) +{ + u8 val, *eeprom = dev->mt76.eeprom.data; + + if (is_mt7663(&dev->mt76)) { + /* dual band */ + dev->mphy.cap.has_2ghz = true; + dev->mphy.cap.has_5ghz = true; + return; + } + + if (is_mt7622(&dev->mt76)) { + /* 2GHz only */ + dev->mphy.cap.has_2ghz = true; + return; + } + + if (is_mt7611(&dev->mt76)) { + /* 5GHz only */ + dev->mphy.cap.has_5ghz = true; + return; + } + + val = FIELD_GET(MT_EE_NIC_WIFI_CONF_BAND_SEL, + eeprom[MT_EE_WIFI_CONF]); + switch (val) { + case MT_EE_5GHZ: + dev->mphy.cap.has_5ghz = true; + break; + case MT_EE_DBDC: + dev->dbdc_support = true; + fallthrough; + case MT_EE_2GHZ: + dev->mphy.cap.has_2ghz = true; + break; + default: + dev->mphy.cap.has_2ghz = true; + dev->mphy.cap.has_5ghz = true; + break; + } +} + +static void mt7615_eeprom_parse_hw_cap(struct mt7615_dev *dev) +{ + u8 *eeprom = dev->mt76.eeprom.data; + u8 tx_mask, max_nss; + + mt7615_eeprom_parse_hw_band_cap(dev); + + if (is_mt7663(&dev->mt76)) { + max_nss = 2; + tx_mask = FIELD_GET(MT_EE_HW_CONF1_TX_MASK, + eeprom[MT7663_EE_HW_CONF1]); + } else { + u32 val; + + /* read tx-rx mask from eeprom */ + val = mt76_rr(dev, MT_TOP_STRAP_STA); + max_nss = val & MT_TOP_3NSS ? 3 : 4; + + tx_mask = FIELD_GET(MT_EE_NIC_CONF_TX_MASK, + eeprom[MT_EE_NIC_CONF_0]); + } + if (!tx_mask || tx_mask > max_nss) + tx_mask = max_nss; + + dev->chainmask = BIT(tx_mask) - 1; + dev->mphy.antenna_mask = dev->chainmask; + dev->mphy.chainmask = dev->chainmask; +} + +static int mt7663_eeprom_get_target_power_index(struct mt7615_dev *dev, + struct ieee80211_channel *chan, + u8 chain_idx) +{ + int index, group; + + if (chain_idx > 1) + return -EINVAL; + + if (chan->band == NL80211_BAND_2GHZ) + return MT7663_EE_TX0_2G_TARGET_POWER + (chain_idx << 4); + + group = mt7615_get_channel_group(chan->hw_value); + if (chain_idx == 1) + index = MT7663_EE_TX1_5G_G0_TARGET_POWER; + else + index = MT7663_EE_TX0_5G_G0_TARGET_POWER; + + return index + group * 3; +} + +int mt7615_eeprom_get_target_power_index(struct mt7615_dev *dev, + struct ieee80211_channel *chan, + u8 chain_idx) +{ + int index; + + if (is_mt7663(&dev->mt76)) + return mt7663_eeprom_get_target_power_index(dev, chan, + chain_idx); + + if (chain_idx > 3) + return -EINVAL; + + /* TSSI disabled */ + if (mt7615_ext_pa_enabled(dev, chan->band)) { + if (chan->band == NL80211_BAND_2GHZ) + return MT_EE_EXT_PA_2G_TARGET_POWER; + else + return MT_EE_EXT_PA_5G_TARGET_POWER; + } + + /* TSSI enabled */ + if (chan->band == NL80211_BAND_2GHZ) { + index = MT_EE_TX0_2G_TARGET_POWER + chain_idx * 6; + } else { + int group = mt7615_get_channel_group(chan->hw_value); + + switch (chain_idx) { + case 1: + index = MT_EE_TX1_5G_G0_TARGET_POWER; + break; + case 2: + index = MT_EE_TX2_5G_G0_TARGET_POWER; + break; + case 3: + index = MT_EE_TX3_5G_G0_TARGET_POWER; + break; + case 0: + default: + index = MT_EE_TX0_5G_G0_TARGET_POWER; + break; + } + index += 5 * group; + } + + return index; +} + +int mt7615_eeprom_get_power_delta_index(struct mt7615_dev *dev, + enum nl80211_band band) +{ + /* assume the first rate has the highest power offset */ + if (is_mt7663(&dev->mt76)) { + if (band == NL80211_BAND_2GHZ) + return MT_EE_TX0_5G_G0_TARGET_POWER; + else + return MT7663_EE_5G_RATE_POWER; + } + + if (band == NL80211_BAND_2GHZ) + return MT_EE_2G_RATE_POWER; + else + return MT_EE_5G_RATE_POWER; +} + +static void mt7615_apply_cal_free_data(struct mt7615_dev *dev) +{ + static const u16 ical[] = { + 0x53, 0x54, 0x55, 0x56, 0x57, 0x5c, 0x5d, 0x62, 0x63, 0x68, + 0x69, 0x6e, 0x6f, 0x73, 0x74, 0x78, 0x79, 0x82, 0x83, 0x87, + 0x88, 0x8c, 0x8d, 0x91, 0x92, 0x96, 0x97, 0x9b, 0x9c, 0xa0, + 0xa1, 0xaa, 0xab, 0xaf, 0xb0, 0xb4, 0xb5, 0xb9, 0xba, 0xf4, + 0xf7, 0xff, + 0x140, 0x141, 0x145, 0x146, 0x14a, 0x14b, 0x154, 0x155, 0x159, + 0x15a, 0x15e, 0x15f, 0x163, 0x164, 0x168, 0x169, 0x16d, 0x16e, + 0x172, 0x173, 0x17c, 0x17d, 0x181, 0x182, 0x186, 0x187, 0x18b, + 0x18c + }; + static const u16 ical_nocheck[] = { + 0x110, 0x111, 0x112, 0x113, 0x114, 0x115, 0x116, 0x117, 0x118, + 0x1b5, 0x1b6, 0x1b7, 0x3ac, 0x3ad, 0x3ae, 0x3af, 0x3b0, 0x3b1, + 0x3b2 + }; + u8 *eeprom = dev->mt76.eeprom.data; + u8 *otp = dev->mt76.otp.data; + int i; + + if (!otp) + return; + + for (i = 0; i < ARRAY_SIZE(ical); i++) + if (!otp[ical[i]]) + return; + + for (i = 0; i < ARRAY_SIZE(ical); i++) + eeprom[ical[i]] = otp[ical[i]]; + + for (i = 0; i < ARRAY_SIZE(ical_nocheck); i++) + eeprom[ical_nocheck[i]] = otp[ical_nocheck[i]]; +} + +static void mt7622_apply_cal_free_data(struct mt7615_dev *dev) +{ + static const u16 ical[] = { + 0x53, 0x54, 0x55, 0x56, 0xf4, 0xf7, 0x144, 0x156, 0x15b + }; + u8 *eeprom = dev->mt76.eeprom.data; + u8 *otp = dev->mt76.otp.data; + int i; + + if (!otp) + return; + + for (i = 0; i < ARRAY_SIZE(ical); i++) { + if (!otp[ical[i]]) + continue; + + eeprom[ical[i]] = otp[ical[i]]; + } +} + +static void mt7615_cal_free_data(struct mt7615_dev *dev) +{ + struct device_node *np = dev->mt76.dev->of_node; + + if (!np || !of_property_read_bool(np, "mediatek,eeprom-merge-otp")) + return; + + switch (mt76_chip(&dev->mt76)) { + case 0x7622: + mt7622_apply_cal_free_data(dev); + break; + case 0x7615: + case 0x7611: + mt7615_apply_cal_free_data(dev); + break; + } +} + +int mt7615_eeprom_init(struct mt7615_dev *dev, u32 addr) +{ + int ret; + + ret = mt7615_eeprom_load(dev, addr); + if (ret < 0) + return ret; + + ret = mt7615_check_eeprom(&dev->mt76); + if (ret && dev->mt76.otp.data) { + memcpy(dev->mt76.eeprom.data, dev->mt76.otp.data, + dev->mt76.otp.size); + } else { + dev->flash_eeprom = true; + mt7615_cal_free_data(dev); + } + + mt7615_eeprom_parse_hw_cap(dev); + memcpy(dev->mphy.macaddr, dev->mt76.eeprom.data + MT_EE_MAC_ADDR, + ETH_ALEN); + + return mt76_eeprom_override(&dev->mphy); +} +EXPORT_SYMBOL_GPL(mt7615_eeprom_init); diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/eeprom.h b/drivers/net/wireless/mediatek/mt76/mt7615/eeprom.h new file mode 100644 index 000000000..6aed52e14 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7615/eeprom.h @@ -0,0 +1,116 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* Copyright (C) 2019 MediaTek Inc. */ + +#ifndef __MT7615_EEPROM_H +#define __MT7615_EEPROM_H + +#include "mt7615.h" + + +#define MT7615_EEPROM_DCOC_OFFSET MT7615_EEPROM_SIZE +#define MT7615_EEPROM_DCOC_SIZE 256 +#define MT7615_EEPROM_DCOC_COUNT 34 + +#define MT7615_EEPROM_TXDPD_OFFSET (MT7615_EEPROM_SIZE + \ + MT7615_EEPROM_DCOC_COUNT * \ + MT7615_EEPROM_DCOC_SIZE) +#define MT7615_EEPROM_TXDPD_SIZE 216 +#define MT7615_EEPROM_TXDPD_COUNT (44 + 3) + +#define MT7615_EEPROM_FULL_SIZE (MT7615_EEPROM_TXDPD_OFFSET + \ + MT7615_EEPROM_TXDPD_COUNT * \ + MT7615_EEPROM_TXDPD_SIZE) + +enum mt7615_eeprom_field { + MT_EE_CHIP_ID = 0x000, + MT_EE_VERSION = 0x002, + MT_EE_MAC_ADDR = 0x004, + MT_EE_NIC_CONF_0 = 0x034, + MT_EE_NIC_CONF_1 = 0x036, + MT_EE_WIFI_CONF = 0x03e, + MT_EE_CALDATA_FLASH = 0x052, + MT_EE_TX0_2G_TARGET_POWER = 0x058, + MT_EE_TX0_5G_G0_TARGET_POWER = 0x070, + MT7663_EE_5G_RATE_POWER = 0x089, + MT_EE_TX1_5G_G0_TARGET_POWER = 0x098, + MT_EE_2G_RATE_POWER = 0x0be, + MT_EE_5G_RATE_POWER = 0x0d5, + MT7663_EE_TX0_2G_TARGET_POWER = 0x0e3, + MT_EE_EXT_PA_2G_TARGET_POWER = 0x0f2, + MT_EE_EXT_PA_5G_TARGET_POWER = 0x0f3, + MT_EE_TX2_5G_G0_TARGET_POWER = 0x142, + MT_EE_TX3_5G_G0_TARGET_POWER = 0x16a, + MT7663_EE_HW_CONF1 = 0x1b0, + MT7663_EE_TX0_5G_G0_TARGET_POWER = 0x245, + MT7663_EE_TX1_5G_G0_TARGET_POWER = 0x2b5, + + MT7615_EE_MAX = 0x3bf, + MT7622_EE_MAX = 0x3db, + MT7663_EE_MAX = 0x600, +}; + +#define MT_EE_RATE_POWER_MASK GENMASK(5, 0) +#define MT_EE_RATE_POWER_SIGN BIT(6) +#define MT_EE_RATE_POWER_EN BIT(7) + +#define MT_EE_CALDATA_FLASH_TX_DPD BIT(0) +#define MT_EE_CALDATA_FLASH_RX_CAL BIT(1) + +#define MT_EE_NIC_CONF_TX_MASK GENMASK(7, 4) +#define MT_EE_NIC_CONF_RX_MASK GENMASK(3, 0) + +#define MT_EE_HW_CONF1_TX_MASK GENMASK(2, 0) + +#define MT_EE_NIC_CONF_TSSI_2G BIT(5) +#define MT_EE_NIC_CONF_TSSI_5G BIT(6) + +#define MT_EE_NIC_WIFI_CONF_BAND_SEL GENMASK(5, 4) +enum mt7615_eeprom_band { + MT_EE_DUAL_BAND, + MT_EE_5GHZ, + MT_EE_2GHZ, + MT_EE_DBDC, +}; + +enum mt7615_channel_group { + MT_CH_5G_JAPAN, + MT_CH_5G_UNII_1, + MT_CH_5G_UNII_2A, + MT_CH_5G_UNII_2B, + MT_CH_5G_UNII_2E_1, + MT_CH_5G_UNII_2E_2, + MT_CH_5G_UNII_2E_3, + MT_CH_5G_UNII_3, + __MT_CH_MAX +}; + +static inline enum mt7615_channel_group +mt7615_get_channel_group(int channel) +{ + if (channel >= 184 && channel <= 196) + return MT_CH_5G_JAPAN; + if (channel <= 48) + return MT_CH_5G_UNII_1; + if (channel <= 64) + return MT_CH_5G_UNII_2A; + if (channel <= 114) + return MT_CH_5G_UNII_2E_1; + if (channel <= 144) + return MT_CH_5G_UNII_2E_2; + if (channel <= 161) + return MT_CH_5G_UNII_2E_3; + return MT_CH_5G_UNII_3; +} + +static inline bool +mt7615_ext_pa_enabled(struct mt7615_dev *dev, enum nl80211_band band) +{ + u8 *eep = dev->mt76.eeprom.data; + + if (band == NL80211_BAND_5GHZ) + return !(eep[MT_EE_NIC_CONF_1 + 1] & MT_EE_NIC_CONF_TSSI_5G); + else + return !(eep[MT_EE_NIC_CONF_1 + 1] & MT_EE_NIC_CONF_TSSI_2G); +} + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/init.c b/drivers/net/wireless/mediatek/mt76/mt7615/init.c new file mode 100644 index 000000000..e437e088b --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7615/init.c @@ -0,0 +1,649 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2019 MediaTek Inc. + * + * Author: Roy Luo + * Ryder Lee + * Felix Fietkau + * Lorenzo Bianconi + */ + +#include +#include +#include +#include "mt7615.h" +#include "mac.h" +#include "mcu.h" +#include "eeprom.h" + +static ssize_t mt7615_thermal_show_temp(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct mt7615_dev *mdev = dev_get_drvdata(dev); + int temperature; + + if (!mt7615_wait_for_mcu_init(mdev)) + return 0; + + mt7615_mutex_acquire(mdev); + temperature = mt7615_mcu_get_temperature(mdev); + mt7615_mutex_release(mdev); + + if (temperature < 0) + return temperature; + + /* display in millidegree celcius */ + return sprintf(buf, "%u\n", temperature * 1000); +} + +static SENSOR_DEVICE_ATTR(temp1_input, 0444, mt7615_thermal_show_temp, + NULL, 0); + +static struct attribute *mt7615_hwmon_attrs[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + NULL, +}; +ATTRIBUTE_GROUPS(mt7615_hwmon); + +int mt7615_thermal_init(struct mt7615_dev *dev) +{ + struct wiphy *wiphy = mt76_hw(dev)->wiphy; + struct device *hwmon; + const char *name; + + if (!IS_REACHABLE(CONFIG_HWMON)) + return 0; + + name = devm_kasprintf(&wiphy->dev, GFP_KERNEL, "mt7615_%s", + wiphy_name(wiphy)); + if (!name) + return -ENOMEM; + + hwmon = devm_hwmon_device_register_with_groups(&wiphy->dev, name, dev, + mt7615_hwmon_groups); + return PTR_ERR_OR_ZERO(hwmon); +} +EXPORT_SYMBOL_GPL(mt7615_thermal_init); + +static void +mt7615_phy_init(struct mt7615_dev *dev) +{ + /* disable rf low power beacon mode */ + mt76_set(dev, MT_WF_PHY_WF2_RFCTRL0(0), MT_WF_PHY_WF2_RFCTRL0_LPBCN_EN); + mt76_set(dev, MT_WF_PHY_WF2_RFCTRL0(1), MT_WF_PHY_WF2_RFCTRL0_LPBCN_EN); +} + +static void +mt7615_init_mac_chain(struct mt7615_dev *dev, int chain) +{ + u32 val; + + if (!chain) + val = MT_CFG_CCR_MAC_D0_1X_GC_EN | MT_CFG_CCR_MAC_D0_2X_GC_EN; + else + val = MT_CFG_CCR_MAC_D1_1X_GC_EN | MT_CFG_CCR_MAC_D1_2X_GC_EN; + + /* enable band 0/1 clk */ + mt76_set(dev, MT_CFG_CCR, val); + + mt76_rmw(dev, MT_TMAC_TRCR(chain), + MT_TMAC_TRCR_CCA_SEL | MT_TMAC_TRCR_SEC_CCA_SEL, + FIELD_PREP(MT_TMAC_TRCR_CCA_SEL, 2) | + FIELD_PREP(MT_TMAC_TRCR_SEC_CCA_SEL, 0)); + + mt76_wr(dev, MT_AGG_ACR(chain), + MT_AGG_ACR_PKT_TIME_EN | MT_AGG_ACR_NO_BA_AR_RULE | + FIELD_PREP(MT_AGG_ACR_CFEND_RATE, MT7615_CFEND_RATE_DEFAULT) | + FIELD_PREP(MT_AGG_ACR_BAR_RATE, MT7615_BAR_RATE_DEFAULT)); + + mt76_wr(dev, MT_AGG_ARUCR(chain), + FIELD_PREP(MT_AGG_ARxCR_LIMIT(0), 7) | + FIELD_PREP(MT_AGG_ARxCR_LIMIT(1), 2) | + FIELD_PREP(MT_AGG_ARxCR_LIMIT(2), 2) | + FIELD_PREP(MT_AGG_ARxCR_LIMIT(3), 2) | + FIELD_PREP(MT_AGG_ARxCR_LIMIT(4), 1) | + FIELD_PREP(MT_AGG_ARxCR_LIMIT(5), 1) | + FIELD_PREP(MT_AGG_ARxCR_LIMIT(6), 1) | + FIELD_PREP(MT_AGG_ARxCR_LIMIT(7), 1)); + + mt76_wr(dev, MT_AGG_ARDCR(chain), + FIELD_PREP(MT_AGG_ARxCR_LIMIT(0), MT7615_RATE_RETRY - 1) | + FIELD_PREP(MT_AGG_ARxCR_LIMIT(1), MT7615_RATE_RETRY - 1) | + FIELD_PREP(MT_AGG_ARxCR_LIMIT(2), MT7615_RATE_RETRY - 1) | + FIELD_PREP(MT_AGG_ARxCR_LIMIT(3), MT7615_RATE_RETRY - 1) | + FIELD_PREP(MT_AGG_ARxCR_LIMIT(4), MT7615_RATE_RETRY - 1) | + FIELD_PREP(MT_AGG_ARxCR_LIMIT(5), MT7615_RATE_RETRY - 1) | + FIELD_PREP(MT_AGG_ARxCR_LIMIT(6), MT7615_RATE_RETRY - 1) | + FIELD_PREP(MT_AGG_ARxCR_LIMIT(7), MT7615_RATE_RETRY - 1)); + + mt76_clear(dev, MT_DMA_RCFR0(chain), MT_DMA_RCFR0_MCU_RX_TDLS); + if (!mt7615_firmware_offload(dev)) { + u32 mask, set; + + mask = MT_DMA_RCFR0_MCU_RX_MGMT | + MT_DMA_RCFR0_MCU_RX_CTL_NON_BAR | + MT_DMA_RCFR0_MCU_RX_CTL_BAR | + MT_DMA_RCFR0_MCU_RX_BYPASS | + MT_DMA_RCFR0_RX_DROPPED_UCAST | + MT_DMA_RCFR0_RX_DROPPED_MCAST; + set = FIELD_PREP(MT_DMA_RCFR0_RX_DROPPED_UCAST, 2) | + FIELD_PREP(MT_DMA_RCFR0_RX_DROPPED_MCAST, 2); + mt76_rmw(dev, MT_DMA_RCFR0(chain), mask, set); + } +} + +static void +mt7615_mac_init(struct mt7615_dev *dev) +{ + int i; + + mt7615_init_mac_chain(dev, 0); + + mt76_rmw_field(dev, MT_TMAC_CTCR0, + MT_TMAC_CTCR0_INS_DDLMT_REFTIME, 0x3f); + mt76_rmw_field(dev, MT_TMAC_CTCR0, + MT_TMAC_CTCR0_INS_DDLMT_DENSITY, 0x3); + mt76_rmw(dev, MT_TMAC_CTCR0, + MT_TMAC_CTCR0_INS_DDLMT_VHT_SMPDU_EN | + MT_TMAC_CTCR0_INS_DDLMT_EN, + MT_TMAC_CTCR0_INS_DDLMT_VHT_SMPDU_EN | + MT_TMAC_CTCR0_INS_DDLMT_EN); + + mt76_connac_mcu_set_rts_thresh(&dev->mt76, 0x92b, 0); + mt7615_mac_set_scs(&dev->phy, true); + + mt76_rmw(dev, MT_AGG_SCR, MT_AGG_SCR_NLNAV_MID_PTEC_DIS, + MT_AGG_SCR_NLNAV_MID_PTEC_DIS); + + mt76_wr(dev, MT_AGG_ARCR, + FIELD_PREP(MT_AGG_ARCR_RTS_RATE_THR, 2) | + MT_AGG_ARCR_RATE_DOWN_RATIO_EN | + FIELD_PREP(MT_AGG_ARCR_RATE_DOWN_RATIO, 1) | + FIELD_PREP(MT_AGG_ARCR_RATE_UP_EXTRA_TH, 4)); + + for (i = 0; i < MT7615_WTBL_SIZE; i++) + mt7615_mac_wtbl_update(dev, i, + MT_WTBL_UPDATE_ADM_COUNT_CLEAR); + + mt76_set(dev, MT_WF_RMAC_MIB_TIME0, MT_WF_RMAC_MIB_RXTIME_EN); + mt76_set(dev, MT_WF_RMAC_MIB_AIRTIME0, MT_WF_RMAC_MIB_RXTIME_EN); + + mt76_wr(dev, MT_DMA_DCR0, + FIELD_PREP(MT_DMA_DCR0_MAX_RX_LEN, 3072) | + MT_DMA_DCR0_RX_VEC_DROP | MT_DMA_DCR0_DAMSDU_EN | + MT_DMA_DCR0_RX_HDR_TRANS_EN); + /* disable TDLS filtering */ + mt76_clear(dev, MT_WF_PFCR, MT_WF_PFCR_TDLS_EN); + mt76_set(dev, MT_WF_MIB_SCR0, MT_MIB_SCR0_AGG_CNT_RANGE_EN); + if (is_mt7663(&dev->mt76)) { + mt76_wr(dev, MT_WF_AGG(0x160), 0x5c341c02); + mt76_wr(dev, MT_WF_AGG(0x164), 0x70708040); + } else { + mt7615_init_mac_chain(dev, 1); + } + mt7615_mcu_set_rx_hdr_trans_blacklist(dev); +} + +static void +mt7615_check_offload_capability(struct mt7615_dev *dev) +{ + struct ieee80211_hw *hw = mt76_hw(dev); + struct wiphy *wiphy = hw->wiphy; + + if (mt7615_firmware_offload(dev)) { + ieee80211_hw_set(hw, SUPPORTS_PS); + ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS); + + wiphy->flags &= ~WIPHY_FLAG_4ADDR_STATION; + wiphy->features |= NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR | + NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR | + WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | + NL80211_FEATURE_P2P_GO_CTWIN | + NL80211_FEATURE_P2P_GO_OPPPS; + } else { + dev->ops->hw_scan = NULL; + dev->ops->cancel_hw_scan = NULL; + dev->ops->sched_scan_start = NULL; + dev->ops->sched_scan_stop = NULL; + dev->ops->set_rekey_data = NULL; + dev->ops->remain_on_channel = NULL; + dev->ops->cancel_remain_on_channel = NULL; + + wiphy->max_sched_scan_plan_interval = 0; + wiphy->max_sched_scan_ie_len = 0; + wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN; + wiphy->max_sched_scan_ssids = 0; + wiphy->max_match_sets = 0; + wiphy->max_sched_scan_reqs = 0; + } +} + +bool mt7615_wait_for_mcu_init(struct mt7615_dev *dev) +{ + flush_work(&dev->mcu_work); + + return test_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state); +} +EXPORT_SYMBOL_GPL(mt7615_wait_for_mcu_init); + +static const struct ieee80211_iface_limit if_limits[] = { + { + .max = 1, + .types = BIT(NL80211_IFTYPE_ADHOC) + }, { + .max = MT7615_MAX_INTERFACES, + .types = BIT(NL80211_IFTYPE_AP) | +#ifdef CONFIG_MAC80211_MESH + BIT(NL80211_IFTYPE_MESH_POINT) | +#endif + BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_P2P_GO) | + BIT(NL80211_IFTYPE_STATION) + } +}; + +static const struct ieee80211_iface_combination if_comb_radar[] = { + { + .limits = if_limits, + .n_limits = ARRAY_SIZE(if_limits), + .max_interfaces = MT7615_MAX_INTERFACES, + .num_different_channels = 1, + .beacon_int_infra_match = true, + .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) | + BIT(NL80211_CHAN_WIDTH_20) | + BIT(NL80211_CHAN_WIDTH_40) | + BIT(NL80211_CHAN_WIDTH_80) | + BIT(NL80211_CHAN_WIDTH_160) | + BIT(NL80211_CHAN_WIDTH_80P80), + } +}; + +static const struct ieee80211_iface_combination if_comb[] = { + { + .limits = if_limits, + .n_limits = ARRAY_SIZE(if_limits), + .max_interfaces = MT7615_MAX_INTERFACES, + .num_different_channels = 1, + .beacon_int_infra_match = true, + } +}; + +void mt7615_init_txpower(struct mt7615_dev *dev, + struct ieee80211_supported_band *sband) +{ + int i, n_chains = hweight8(dev->mphy.antenna_mask), target_chains; + int delta_idx, delta = mt76_tx_power_path_delta(n_chains); + u8 *eep = (u8 *)dev->mt76.eeprom.data; + enum nl80211_band band = sband->band; + struct mt76_power_limits limits; + u8 rate_val; + + delta_idx = mt7615_eeprom_get_power_delta_index(dev, band); + rate_val = eep[delta_idx]; + if ((rate_val & ~MT_EE_RATE_POWER_MASK) == + (MT_EE_RATE_POWER_EN | MT_EE_RATE_POWER_SIGN)) + delta += rate_val & MT_EE_RATE_POWER_MASK; + + if (!is_mt7663(&dev->mt76) && mt7615_ext_pa_enabled(dev, band)) + target_chains = 1; + else + target_chains = n_chains; + + for (i = 0; i < sband->n_channels; i++) { + struct ieee80211_channel *chan = &sband->channels[i]; + u8 target_power = 0; + int j; + + for (j = 0; j < target_chains; j++) { + int index; + + index = mt7615_eeprom_get_target_power_index(dev, chan, j); + if (index < 0) + continue; + + target_power = max(target_power, eep[index]); + } + + target_power = mt76_get_rate_power_limits(&dev->mphy, chan, + &limits, + target_power); + target_power += delta; + target_power = DIV_ROUND_UP(target_power, 2); + chan->max_power = min_t(int, chan->max_reg_power, + target_power); + chan->orig_mpwr = target_power; + } +} +EXPORT_SYMBOL_GPL(mt7615_init_txpower); + +void mt7615_init_work(struct mt7615_dev *dev) +{ + mt7615_mcu_set_eeprom(dev); + mt7615_mac_init(dev); + mt7615_phy_init(dev); + mt76_connac_mcu_del_wtbl_all(&dev->mt76); + mt7615_check_offload_capability(dev); +} +EXPORT_SYMBOL_GPL(mt7615_init_work); + +static void +mt7615_regd_notifier(struct wiphy *wiphy, + struct regulatory_request *request) +{ + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct mt7615_dev *dev = mt7615_hw_dev(hw); + struct mt76_phy *mphy = hw->priv; + struct mt7615_phy *phy = mphy->priv; + struct cfg80211_chan_def *chandef = &mphy->chandef; + + memcpy(dev->mt76.alpha2, request->alpha2, sizeof(dev->mt76.alpha2)); + dev->mt76.region = request->dfs_region; + + mt7615_init_txpower(dev, &mphy->sband_2g.sband); + mt7615_init_txpower(dev, &mphy->sband_5g.sband); + + mt7615_mutex_acquire(dev); + + if (chandef->chan->flags & IEEE80211_CHAN_RADAR) + mt7615_dfs_init_radar_detector(phy); + + if (mt7615_firmware_offload(phy->dev)) { + mt76_connac_mcu_set_channel_domain(mphy); + mt76_connac_mcu_set_rate_txpower(mphy); + } + + mt7615_mutex_release(dev); +} + +static void +mt7615_init_wiphy(struct ieee80211_hw *hw) +{ + struct mt7615_phy *phy = mt7615_hw_phy(hw); + struct wiphy *wiphy = hw->wiphy; + + hw->queues = 4; + hw->max_rates = 3; + hw->max_report_rates = 7; + hw->max_rate_tries = 11; + hw->netdev_features = NETIF_F_RXCSUM; + + hw->radiotap_timestamp.units_pos = + IEEE80211_RADIOTAP_TIMESTAMP_UNIT_US; + + phy->slottime = 9; + + hw->sta_data_size = sizeof(struct mt7615_sta); + hw->vif_data_size = sizeof(struct mt7615_vif); + + if (is_mt7663(&phy->dev->mt76)) { + wiphy->iface_combinations = if_comb; + wiphy->n_iface_combinations = ARRAY_SIZE(if_comb); + } else { + wiphy->iface_combinations = if_comb_radar; + wiphy->n_iface_combinations = ARRAY_SIZE(if_comb_radar); + } + wiphy->reg_notifier = mt7615_regd_notifier; + + wiphy->max_sched_scan_plan_interval = + MT76_CONNAC_MAX_TIME_SCHED_SCAN_INTERVAL; + wiphy->max_sched_scan_ie_len = IEEE80211_MAX_DATA_LEN; + wiphy->max_scan_ie_len = MT76_CONNAC_SCAN_IE_LEN; + wiphy->max_sched_scan_ssids = MT76_CONNAC_MAX_SCHED_SCAN_SSID; + wiphy->max_match_sets = MT76_CONNAC_MAX_SCAN_MATCH; + wiphy->max_sched_scan_reqs = 1; + wiphy->max_scan_ssids = 4; + + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_SET_SCAN_DWELL); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_VHT_IBSS); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_CAN_REPLACE_PTK0); + if (!is_mt7622(&phy->dev->mt76)) + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER); + + ieee80211_hw_set(hw, SINGLE_SCAN_ON_ALL_BANDS); + ieee80211_hw_set(hw, TX_STATUS_NO_AMPDU_LEN); + ieee80211_hw_set(hw, WANT_MONITOR_VIF); + ieee80211_hw_set(hw, SUPPORTS_RX_DECAP_OFFLOAD); + ieee80211_hw_set(hw, SUPPORTS_VHT_EXT_NSS_BW); + + if (is_mt7615(&phy->dev->mt76)) + hw->max_tx_fragments = MT_TXP_MAX_BUF_NUM; + else + hw->max_tx_fragments = MT_HW_TXP_MAX_BUF_NUM; + + phy->mt76->sband_2g.sband.ht_cap.cap |= IEEE80211_HT_CAP_LDPC_CODING; + phy->mt76->sband_5g.sband.ht_cap.cap |= IEEE80211_HT_CAP_LDPC_CODING; + phy->mt76->sband_5g.sband.vht_cap.cap |= + IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK; +} + +static void +mt7615_cap_dbdc_enable(struct mt7615_dev *dev) +{ + dev->mphy.sband_5g.sband.vht_cap.cap &= + ~(IEEE80211_VHT_CAP_SHORT_GI_160 | + IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ); + if (dev->chainmask == 0xf) + dev->mphy.antenna_mask = dev->chainmask >> 2; + else + dev->mphy.antenna_mask = dev->chainmask >> 1; + dev->mphy.chainmask = dev->mphy.antenna_mask; + dev->mphy.hw->wiphy->available_antennas_rx = dev->mphy.chainmask; + dev->mphy.hw->wiphy->available_antennas_tx = dev->mphy.chainmask; + mt76_set_stream_caps(&dev->mphy, true); +} + +static void +mt7615_cap_dbdc_disable(struct mt7615_dev *dev) +{ + dev->mphy.sband_5g.sband.vht_cap.cap |= + IEEE80211_VHT_CAP_SHORT_GI_160 | + IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ; + dev->mphy.antenna_mask = dev->chainmask; + dev->mphy.chainmask = dev->chainmask; + dev->mphy.hw->wiphy->available_antennas_rx = dev->chainmask; + dev->mphy.hw->wiphy->available_antennas_tx = dev->chainmask; + mt76_set_stream_caps(&dev->mphy, true); +} + +u32 mt7615_reg_map(struct mt7615_dev *dev, u32 addr) +{ + u32 base, offset; + + if (is_mt7663(&dev->mt76)) { + base = addr & MT7663_MCU_PCIE_REMAP_2_BASE; + offset = addr & MT7663_MCU_PCIE_REMAP_2_OFFSET; + } else { + base = addr & MT_MCU_PCIE_REMAP_2_BASE; + offset = addr & MT_MCU_PCIE_REMAP_2_OFFSET; + } + mt76_wr(dev, MT_MCU_PCIE_REMAP_2, base); + + return MT_PCIE_REMAP_BASE_2 + offset; +} +EXPORT_SYMBOL_GPL(mt7615_reg_map); + +static void +mt7615_led_set_config(struct led_classdev *led_cdev, + u8 delay_on, u8 delay_off) +{ + struct mt7615_dev *dev; + struct mt76_phy *mphy; + u32 val, addr; + u8 index; + + mphy = container_of(led_cdev, struct mt76_phy, leds.cdev); + dev = container_of(mphy->dev, struct mt7615_dev, mt76); + + if (!mt76_connac_pm_ref(mphy, &dev->pm)) + return; + + val = FIELD_PREP(MT_LED_STATUS_DURATION, 0xffff) | + FIELD_PREP(MT_LED_STATUS_OFF, delay_off) | + FIELD_PREP(MT_LED_STATUS_ON, delay_on); + + index = dev->dbdc_support ? mphy->band_idx : mphy->leds.pin; + addr = mt7615_reg_map(dev, MT_LED_STATUS_0(index)); + mt76_wr(dev, addr, val); + addr = mt7615_reg_map(dev, MT_LED_STATUS_1(index)); + mt76_wr(dev, addr, val); + + val = MT_LED_CTRL_REPLAY(index) | MT_LED_CTRL_KICK(index); + if (dev->mphy.leds.al) + val |= MT_LED_CTRL_POLARITY(index); + if (mphy->band_idx) + val |= MT_LED_CTRL_BAND(index); + + addr = mt7615_reg_map(dev, MT_LED_CTRL); + mt76_wr(dev, addr, val); + + mt76_connac_pm_unref(mphy, &dev->pm); +} + +int mt7615_led_set_blink(struct led_classdev *led_cdev, + unsigned long *delay_on, + unsigned long *delay_off) +{ + u8 delta_on, delta_off; + + delta_off = max_t(u8, *delay_off / 10, 1); + delta_on = max_t(u8, *delay_on / 10, 1); + + mt7615_led_set_config(led_cdev, delta_on, delta_off); + + return 0; +} +EXPORT_SYMBOL_GPL(mt7615_led_set_blink); + +void mt7615_led_set_brightness(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + if (!brightness) + mt7615_led_set_config(led_cdev, 0, 0xff); + else + mt7615_led_set_config(led_cdev, 0xff, 0); +} +EXPORT_SYMBOL_GPL(mt7615_led_set_brightness); + +int mt7615_register_ext_phy(struct mt7615_dev *dev) +{ + struct mt7615_phy *phy = mt7615_ext_phy(dev); + struct mt76_phy *mphy; + int i, ret; + + if (!is_mt7615(&dev->mt76)) + return -EOPNOTSUPP; + + if (test_bit(MT76_STATE_RUNNING, &dev->mphy.state)) + return -EINVAL; + + if (phy) + return 0; + + mt7615_cap_dbdc_enable(dev); + mphy = mt76_alloc_phy(&dev->mt76, sizeof(*phy), &mt7615_ops, MT_BAND1); + if (!mphy) + return -ENOMEM; + + phy = mphy->priv; + phy->dev = dev; + phy->mt76 = mphy; + mphy->chainmask = dev->chainmask & ~dev->mphy.chainmask; + mphy->antenna_mask = BIT(hweight8(mphy->chainmask)) - 1; + mt7615_init_wiphy(mphy->hw); + + INIT_DELAYED_WORK(&mphy->mac_work, mt7615_mac_work); + INIT_DELAYED_WORK(&phy->scan_work, mt7615_scan_work); + skb_queue_head_init(&phy->scan_event_list); + + INIT_WORK(&phy->roc_work, mt7615_roc_work); + timer_setup(&phy->roc_timer, mt7615_roc_timer, 0); + init_waitqueue_head(&phy->roc_wait); + + mt7615_mac_set_scs(phy, true); + + /* + * Make the secondary PHY MAC address local without overlapping with + * the usual MAC address allocation scheme on multiple virtual interfaces + */ + memcpy(mphy->macaddr, dev->mt76.eeprom.data + MT_EE_MAC_ADDR, + ETH_ALEN); + mphy->macaddr[0] |= 2; + mphy->macaddr[0] ^= BIT(7); + + ret = mt76_eeprom_override(mphy); + if (ret) + return ret; + + /* second phy can only handle 5 GHz */ + mphy->cap.has_5ghz = true; + + /* mt7615 second phy shares the same hw queues with the primary one */ + for (i = 0; i <= MT_TXQ_PSD ; i++) + mphy->q_tx[i] = dev->mphy.q_tx[i]; + + /* init led callbacks */ + if (IS_ENABLED(CONFIG_MT76_LEDS)) { + mphy->leds.cdev.brightness_set = mt7615_led_set_brightness; + mphy->leds.cdev.blink_set = mt7615_led_set_blink; + } + + ret = mt76_register_phy(mphy, true, mt76_rates, + ARRAY_SIZE(mt76_rates)); + if (ret) + ieee80211_free_hw(mphy->hw); + + return ret; +} +EXPORT_SYMBOL_GPL(mt7615_register_ext_phy); + +void mt7615_unregister_ext_phy(struct mt7615_dev *dev) +{ + struct mt7615_phy *phy = mt7615_ext_phy(dev); + struct mt76_phy *mphy = dev->mt76.phys[MT_BAND1]; + + if (!phy) + return; + + mt7615_cap_dbdc_disable(dev); + mt76_unregister_phy(mphy); + ieee80211_free_hw(mphy->hw); +} +EXPORT_SYMBOL_GPL(mt7615_unregister_ext_phy); + +void mt7615_init_device(struct mt7615_dev *dev) +{ + struct ieee80211_hw *hw = mt76_hw(dev); + + dev->phy.dev = dev; + dev->phy.mt76 = &dev->mt76.phy; + dev->mt76.phy.priv = &dev->phy; + dev->mt76.tx_worker.fn = mt7615_tx_worker; + + INIT_DELAYED_WORK(&dev->pm.ps_work, mt7615_pm_power_save_work); + INIT_WORK(&dev->pm.wake_work, mt7615_pm_wake_work); + spin_lock_init(&dev->pm.wake.lock); + mutex_init(&dev->pm.mutex); + init_waitqueue_head(&dev->pm.wait); + spin_lock_init(&dev->pm.txq_lock); + INIT_DELAYED_WORK(&dev->mphy.mac_work, mt7615_mac_work); + INIT_DELAYED_WORK(&dev->phy.scan_work, mt7615_scan_work); + INIT_DELAYED_WORK(&dev->coredump.work, mt7615_coredump_work); + skb_queue_head_init(&dev->phy.scan_event_list); + skb_queue_head_init(&dev->coredump.msg_list); + init_waitqueue_head(&dev->reset_wait); + init_waitqueue_head(&dev->phy.roc_wait); + + INIT_WORK(&dev->phy.roc_work, mt7615_roc_work); + timer_setup(&dev->phy.roc_timer, mt7615_roc_timer, 0); + + mt7615_init_wiphy(hw); + dev->pm.idle_timeout = MT7615_PM_TIMEOUT; + dev->pm.stats.last_wake_event = jiffies; + dev->pm.stats.last_doze_event = jiffies; + mt7615_cap_dbdc_disable(dev); + +#ifdef CONFIG_NL80211_TESTMODE + dev->mt76.test_ops = &mt7615_testmode_ops; +#endif +} +EXPORT_SYMBOL_GPL(mt7615_init_device); diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mac.c b/drivers/net/wireless/mediatek/mt76/mt7615/mac.c new file mode 100644 index 000000000..aad232c5a --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7615/mac.c @@ -0,0 +1,2351 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2019 MediaTek Inc. + * + * Author: Ryder Lee + * Roy Luo + * Felix Fietkau + * Lorenzo Bianconi + */ + +#include +#include +#include +#include "mt7615.h" +#include "../trace.h" +#include "../dma.h" +#include "mt7615_trace.h" +#include "mac.h" +#include "mcu.h" + +#define to_rssi(field, rxv) ((FIELD_GET(field, rxv) - 220) / 2) + +static const struct mt7615_dfs_radar_spec etsi_radar_specs = { + .pulse_th = { 110, -10, -80, 40, 5200, 128, 5200 }, + .radar_pattern = { + [5] = { 1, 0, 6, 32, 28, 0, 17, 990, 5010, 1, 1 }, + [6] = { 1, 0, 9, 32, 28, 0, 27, 615, 5010, 1, 1 }, + [7] = { 1, 0, 15, 32, 28, 0, 27, 240, 445, 1, 1 }, + [8] = { 1, 0, 12, 32, 28, 0, 42, 240, 510, 1, 1 }, + [9] = { 1, 1, 0, 0, 0, 0, 14, 2490, 3343, 0, 0, 12, 32, 28 }, + [10] = { 1, 1, 0, 0, 0, 0, 14, 2490, 3343, 0, 0, 15, 32, 24 }, + [11] = { 1, 1, 0, 0, 0, 0, 14, 823, 2510, 0, 0, 18, 32, 28 }, + [12] = { 1, 1, 0, 0, 0, 0, 14, 823, 2510, 0, 0, 27, 32, 24 }, + }, +}; + +static const struct mt7615_dfs_radar_spec fcc_radar_specs = { + .pulse_th = { 110, -10, -80, 40, 5200, 128, 5200 }, + .radar_pattern = { + [0] = { 1, 0, 9, 32, 28, 0, 13, 508, 3076, 1, 1 }, + [1] = { 1, 0, 12, 32, 28, 0, 17, 140, 240, 1, 1 }, + [2] = { 1, 0, 8, 32, 28, 0, 22, 190, 510, 1, 1 }, + [3] = { 1, 0, 6, 32, 28, 0, 32, 190, 510, 1, 1 }, + [4] = { 1, 0, 9, 255, 28, 0, 13, 323, 343, 1, 32 }, + }, +}; + +static const struct mt7615_dfs_radar_spec jp_radar_specs = { + .pulse_th = { 110, -10, -80, 40, 5200, 128, 5200 }, + .radar_pattern = { + [0] = { 1, 0, 8, 32, 28, 0, 13, 508, 3076, 1, 1 }, + [1] = { 1, 0, 12, 32, 28, 0, 17, 140, 240, 1, 1 }, + [2] = { 1, 0, 8, 32, 28, 0, 22, 190, 510, 1, 1 }, + [3] = { 1, 0, 6, 32, 28, 0, 32, 190, 510, 1, 1 }, + [4] = { 1, 0, 9, 32, 28, 0, 13, 323, 343, 1, 32 }, + [13] = { 1, 0, 8, 32, 28, 0, 14, 3836, 3856, 1, 1 }, + [14] = { 1, 0, 8, 32, 28, 0, 14, 3990, 4010, 1, 1 }, + }, +}; + +static enum mt76_cipher_type +mt7615_mac_get_cipher(int cipher) +{ + switch (cipher) { + case WLAN_CIPHER_SUITE_WEP40: + return MT_CIPHER_WEP40; + case WLAN_CIPHER_SUITE_WEP104: + return MT_CIPHER_WEP104; + case WLAN_CIPHER_SUITE_TKIP: + return MT_CIPHER_TKIP; + case WLAN_CIPHER_SUITE_AES_CMAC: + return MT_CIPHER_BIP_CMAC_128; + case WLAN_CIPHER_SUITE_CCMP: + return MT_CIPHER_AES_CCMP; + case WLAN_CIPHER_SUITE_CCMP_256: + return MT_CIPHER_CCMP_256; + case WLAN_CIPHER_SUITE_GCMP: + return MT_CIPHER_GCMP; + case WLAN_CIPHER_SUITE_GCMP_256: + return MT_CIPHER_GCMP_256; + case WLAN_CIPHER_SUITE_SMS4: + return MT_CIPHER_WAPI; + default: + return MT_CIPHER_NONE; + } +} + +static struct mt76_wcid *mt7615_rx_get_wcid(struct mt7615_dev *dev, + u8 idx, bool unicast) +{ + struct mt7615_sta *sta; + struct mt76_wcid *wcid; + + wcid = mt76_wcid_ptr(dev, idx); + if (unicast || !wcid) + return wcid; + + if (!wcid->sta) + return NULL; + + sta = container_of(wcid, struct mt7615_sta, wcid); + if (!sta->vif) + return NULL; + + return &sta->vif->sta.wcid; +} + +void mt7615_mac_reset_counters(struct mt7615_phy *phy) +{ + struct mt7615_dev *dev = phy->dev; + int i; + + for (i = 0; i < 4; i++) { + mt76_rr(dev, MT_TX_AGG_CNT(0, i)); + mt76_rr(dev, MT_TX_AGG_CNT(1, i)); + } + + memset(phy->mt76->aggr_stats, 0, sizeof(phy->mt76->aggr_stats)); + phy->mt76->survey_time = ktime_get_boottime(); + + /* reset airtime counters */ + mt76_rr(dev, MT_MIB_SDR9(0)); + mt76_rr(dev, MT_MIB_SDR9(1)); + + mt76_rr(dev, MT_MIB_SDR36(0)); + mt76_rr(dev, MT_MIB_SDR36(1)); + + mt76_rr(dev, MT_MIB_SDR37(0)); + mt76_rr(dev, MT_MIB_SDR37(1)); + + mt76_set(dev, MT_WF_RMAC_MIB_TIME0, MT_WF_RMAC_MIB_RXTIME_CLR); + mt76_set(dev, MT_WF_RMAC_MIB_AIRTIME0, MT_WF_RMAC_MIB_RXTIME_CLR); +} + +void mt7615_mac_set_timing(struct mt7615_phy *phy) +{ + s16 coverage_class = phy->coverage_class; + struct mt7615_dev *dev = phy->dev; + bool ext_phy = phy != &dev->phy; + u32 val, reg_offset; + u32 cck = FIELD_PREP(MT_TIMEOUT_VAL_PLCP, 231) | + FIELD_PREP(MT_TIMEOUT_VAL_CCA, 48); + u32 ofdm = FIELD_PREP(MT_TIMEOUT_VAL_PLCP, 60) | + FIELD_PREP(MT_TIMEOUT_VAL_CCA, 28); + int sifs, offset; + bool is_5ghz = phy->mt76->chandef.chan->band == NL80211_BAND_5GHZ; + + if (!test_bit(MT76_STATE_RUNNING, &phy->mt76->state)) + return; + + if (is_5ghz) + sifs = 16; + else + sifs = 10; + + if (ext_phy) { + coverage_class = max_t(s16, dev->phy.coverage_class, + coverage_class); + mt76_set(dev, MT_ARB_SCR, + MT_ARB_SCR_TX1_DISABLE | MT_ARB_SCR_RX1_DISABLE); + } else { + struct mt7615_phy *phy_ext = mt7615_ext_phy(dev); + + if (phy_ext) + coverage_class = max_t(s16, phy_ext->coverage_class, + coverage_class); + mt76_set(dev, MT_ARB_SCR, + MT_ARB_SCR_TX0_DISABLE | MT_ARB_SCR_RX0_DISABLE); + } + udelay(1); + + offset = 3 * coverage_class; + reg_offset = FIELD_PREP(MT_TIMEOUT_VAL_PLCP, offset) | + FIELD_PREP(MT_TIMEOUT_VAL_CCA, offset); + mt76_wr(dev, MT_TMAC_CDTR, cck + reg_offset); + mt76_wr(dev, MT_TMAC_ODTR, ofdm + reg_offset); + + mt76_wr(dev, MT_TMAC_ICR(ext_phy), + FIELD_PREP(MT_IFS_EIFS, 360) | + FIELD_PREP(MT_IFS_RIFS, 2) | + FIELD_PREP(MT_IFS_SIFS, sifs) | + FIELD_PREP(MT_IFS_SLOT, phy->slottime)); + + if (phy->slottime < 20 || is_5ghz) + val = MT7615_CFEND_RATE_DEFAULT; + else + val = MT7615_CFEND_RATE_11B; + + mt76_rmw_field(dev, MT_AGG_ACR(ext_phy), MT_AGG_ACR_CFEND_RATE, val); + if (ext_phy) + mt76_clear(dev, MT_ARB_SCR, + MT_ARB_SCR_TX1_DISABLE | MT_ARB_SCR_RX1_DISABLE); + else + mt76_clear(dev, MT_ARB_SCR, + MT_ARB_SCR_TX0_DISABLE | MT_ARB_SCR_RX0_DISABLE); + +} + +static void +mt7615_get_status_freq_info(struct mt7615_dev *dev, struct mt76_phy *mphy, + struct mt76_rx_status *status, u8 chfreq) +{ + if (!test_bit(MT76_HW_SCANNING, &mphy->state) && + !test_bit(MT76_HW_SCHED_SCANNING, &mphy->state) && + !test_bit(MT76_STATE_ROC, &mphy->state)) { + status->freq = mphy->chandef.chan->center_freq; + status->band = mphy->chandef.chan->band; + return; + } + + status->band = chfreq <= 14 ? NL80211_BAND_2GHZ : NL80211_BAND_5GHZ; + status->freq = ieee80211_channel_to_frequency(chfreq, status->band); +} + +static void mt7615_mac_fill_tm_rx(struct mt7615_phy *phy, __le32 *rxv) +{ +#ifdef CONFIG_NL80211_TESTMODE + u32 rxv1 = le32_to_cpu(rxv[0]); + u32 rxv3 = le32_to_cpu(rxv[2]); + u32 rxv4 = le32_to_cpu(rxv[3]); + u32 rxv5 = le32_to_cpu(rxv[4]); + u8 cbw = FIELD_GET(MT_RXV1_FRAME_MODE, rxv1); + u8 mode = FIELD_GET(MT_RXV1_TX_MODE, rxv1); + s16 foe = FIELD_GET(MT_RXV5_FOE, rxv5); + u32 foe_const = (BIT(cbw + 1) & 0xf) * 10000; + + if (!mode) { + /* CCK */ + foe &= ~BIT(11); + foe *= 1000; + foe >>= 11; + } else { + if (foe > 2048) + foe -= 4096; + + foe = (foe * foe_const) >> 15; + } + + phy->test.last_freq_offset = foe; + phy->test.last_rcpi[0] = FIELD_GET(MT_RXV4_RCPI0, rxv4); + phy->test.last_rcpi[1] = FIELD_GET(MT_RXV4_RCPI1, rxv4); + phy->test.last_rcpi[2] = FIELD_GET(MT_RXV4_RCPI2, rxv4); + phy->test.last_rcpi[3] = FIELD_GET(MT_RXV4_RCPI3, rxv4); + phy->test.last_ib_rssi[0] = FIELD_GET(MT_RXV3_IB_RSSI, rxv3); + phy->test.last_wb_rssi[0] = FIELD_GET(MT_RXV3_WB_RSSI, rxv3); +#endif +} + +/* The HW does not translate the mac header to 802.3 for mesh point */ +static int mt7615_reverse_frag0_hdr_trans(struct sk_buff *skb, u16 hdr_gap) +{ + struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb; + struct ethhdr *eth_hdr = (struct ethhdr *)(skb->data + hdr_gap); + struct mt7615_sta *msta = (struct mt7615_sta *)status->wcid; + __le32 *rxd = (__le32 *)skb->data; + struct ieee80211_sta *sta; + struct ieee80211_vif *vif; + struct ieee80211_hdr hdr; + u16 frame_control; + + if (le32_get_bits(rxd[1], MT_RXD1_NORMAL_ADDR_TYPE) != + MT_RXD1_NORMAL_U2M) + return -EINVAL; + + if (!(le32_to_cpu(rxd[0]) & MT_RXD0_NORMAL_GROUP_4)) + return -EINVAL; + + if (!msta || !msta->vif) + return -EINVAL; + + sta = container_of((void *)msta, struct ieee80211_sta, drv_priv); + vif = container_of((void *)msta->vif, struct ieee80211_vif, drv_priv); + + /* store the info from RXD and ethhdr to avoid being overridden */ + frame_control = le32_get_bits(rxd[4], MT_RXD4_FRAME_CONTROL); + hdr.frame_control = cpu_to_le16(frame_control); + hdr.seq_ctrl = cpu_to_le16(le32_get_bits(rxd[6], MT_RXD6_SEQ_CTRL)); + hdr.duration_id = 0; + + ether_addr_copy(hdr.addr1, vif->addr); + ether_addr_copy(hdr.addr2, sta->addr); + switch (frame_control & (IEEE80211_FCTL_TODS | + IEEE80211_FCTL_FROMDS)) { + case 0: + ether_addr_copy(hdr.addr3, vif->bss_conf.bssid); + break; + case IEEE80211_FCTL_FROMDS: + ether_addr_copy(hdr.addr3, eth_hdr->h_source); + break; + case IEEE80211_FCTL_TODS: + ether_addr_copy(hdr.addr3, eth_hdr->h_dest); + break; + case IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS: + ether_addr_copy(hdr.addr3, eth_hdr->h_dest); + ether_addr_copy(hdr.addr4, eth_hdr->h_source); + break; + default: + break; + } + + skb_pull(skb, hdr_gap + sizeof(struct ethhdr) - 2); + if (eth_hdr->h_proto == cpu_to_be16(ETH_P_AARP) || + eth_hdr->h_proto == cpu_to_be16(ETH_P_IPX)) + ether_addr_copy(skb_push(skb, ETH_ALEN), bridge_tunnel_header); + else if (be16_to_cpu(eth_hdr->h_proto) >= ETH_P_802_3_MIN) + ether_addr_copy(skb_push(skb, ETH_ALEN), rfc1042_header); + else + skb_pull(skb, 2); + + if (ieee80211_has_order(hdr.frame_control)) + memcpy(skb_push(skb, IEEE80211_HT_CTL_LEN), &rxd[7], + IEEE80211_HT_CTL_LEN); + + if (ieee80211_is_data_qos(hdr.frame_control)) { + __le16 qos_ctrl; + + qos_ctrl = cpu_to_le16(le32_get_bits(rxd[6], MT_RXD6_QOS_CTL)); + memcpy(skb_push(skb, IEEE80211_QOS_CTL_LEN), &qos_ctrl, + IEEE80211_QOS_CTL_LEN); + } + + if (ieee80211_has_a4(hdr.frame_control)) + memcpy(skb_push(skb, sizeof(hdr)), &hdr, sizeof(hdr)); + else + memcpy(skb_push(skb, sizeof(hdr) - 6), &hdr, sizeof(hdr) - 6); + + status->flag &= ~(RX_FLAG_RADIOTAP_HE | RX_FLAG_RADIOTAP_HE_MU); + return 0; +} + +static int mt7615_mac_fill_rx(struct mt7615_dev *dev, struct sk_buff *skb) +{ + struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb; + struct mt76_phy *mphy = &dev->mt76.phy; + struct mt7615_phy *phy = &dev->phy; + struct ieee80211_supported_band *sband; + struct ieee80211_hdr *hdr; + struct mt7615_phy *phy2; + __le32 *rxd = (__le32 *)skb->data; + u32 rxd0 = le32_to_cpu(rxd[0]); + u32 rxd1 = le32_to_cpu(rxd[1]); + u32 rxd2 = le32_to_cpu(rxd[2]); + u32 csum_mask = MT_RXD0_NORMAL_IP_SUM | MT_RXD0_NORMAL_UDP_TCP_SUM; + u32 csum_status = *(u32 *)skb->cb; + bool unicast, hdr_trans, remove_pad, insert_ccmp_hdr = false; + u16 hdr_gap; + int phy_idx; + int i, idx; + u8 chfreq, amsdu_info, qos_ctl = 0; + u16 seq_ctrl = 0; + __le16 fc = 0; + + memset(status, 0, sizeof(*status)); + + chfreq = FIELD_GET(MT_RXD1_NORMAL_CH_FREQ, rxd1); + + phy2 = dev->mt76.phys[MT_BAND1] ? dev->mt76.phys[MT_BAND1]->priv : NULL; + if (!phy2) + phy_idx = 0; + else if (phy2->chfreq == phy->chfreq) + phy_idx = -1; + else if (phy->chfreq == chfreq) + phy_idx = 0; + else if (phy2->chfreq == chfreq) + phy_idx = 1; + else + phy_idx = -1; + + if (rxd2 & MT_RXD2_NORMAL_AMSDU_ERR) + return -EINVAL; + + hdr_trans = rxd1 & MT_RXD1_NORMAL_HDR_TRANS; + if (hdr_trans && (rxd2 & MT_RXD2_NORMAL_CM)) + return -EINVAL; + + /* ICV error or CCMP/BIP/WPI MIC error */ + if (rxd2 & MT_RXD2_NORMAL_ICV_ERR) + status->flag |= RX_FLAG_ONLY_MONITOR; + + unicast = (rxd1 & MT_RXD1_NORMAL_ADDR_TYPE) == MT_RXD1_NORMAL_U2M; + idx = FIELD_GET(MT_RXD2_NORMAL_WLAN_IDX, rxd2); + status->wcid = mt7615_rx_get_wcid(dev, idx, unicast); + + if (status->wcid) { + struct mt7615_sta *msta; + + msta = container_of(status->wcid, struct mt7615_sta, wcid); + mt76_wcid_add_poll(&dev->mt76, &msta->wcid); + } + + if (mt76_is_mmio(&dev->mt76) && (rxd0 & csum_mask) == csum_mask && + !(csum_status & (BIT(0) | BIT(2) | BIT(3)))) + skb->ip_summed = CHECKSUM_UNNECESSARY; + + if (rxd2 & MT_RXD2_NORMAL_FCS_ERR) + status->flag |= RX_FLAG_FAILED_FCS_CRC; + + if (rxd2 & MT_RXD2_NORMAL_TKIP_MIC_ERR) + status->flag |= RX_FLAG_MMIC_ERROR; + + if (FIELD_GET(MT_RXD2_NORMAL_SEC_MODE, rxd2) != 0 && + !(rxd2 & (MT_RXD2_NORMAL_CLM | MT_RXD2_NORMAL_CM))) { + status->flag |= RX_FLAG_DECRYPTED; + status->flag |= RX_FLAG_IV_STRIPPED; + status->flag |= RX_FLAG_MMIC_STRIPPED | RX_FLAG_MIC_STRIPPED; + } + + remove_pad = rxd1 & MT_RXD1_NORMAL_HDR_OFFSET; + + if (rxd2 & MT_RXD2_NORMAL_MAX_LEN_ERROR) + return -EINVAL; + + rxd += 4; + if (rxd0 & MT_RXD0_NORMAL_GROUP_4) { + u32 v0 = le32_to_cpu(rxd[0]); + u32 v2 = le32_to_cpu(rxd[2]); + + fc = cpu_to_le16(FIELD_GET(MT_RXD4_FRAME_CONTROL, v0)); + qos_ctl = FIELD_GET(MT_RXD6_QOS_CTL, v2); + seq_ctrl = FIELD_GET(MT_RXD6_SEQ_CTRL, v2); + + rxd += 4; + if ((u8 *)rxd - skb->data >= skb->len) + return -EINVAL; + } + + if (rxd0 & MT_RXD0_NORMAL_GROUP_1) { + u8 *data = (u8 *)rxd; + + if (status->flag & RX_FLAG_DECRYPTED) { + switch (FIELD_GET(MT_RXD2_NORMAL_SEC_MODE, rxd2)) { + case MT_CIPHER_AES_CCMP: + case MT_CIPHER_CCMP_CCX: + case MT_CIPHER_CCMP_256: + insert_ccmp_hdr = + FIELD_GET(MT_RXD2_NORMAL_FRAG, rxd2); + fallthrough; + case MT_CIPHER_TKIP: + case MT_CIPHER_TKIP_NO_MIC: + case MT_CIPHER_GCMP: + case MT_CIPHER_GCMP_256: + status->iv[0] = data[5]; + status->iv[1] = data[4]; + status->iv[2] = data[3]; + status->iv[3] = data[2]; + status->iv[4] = data[1]; + status->iv[5] = data[0]; + break; + default: + break; + } + } + rxd += 4; + if ((u8 *)rxd - skb->data >= skb->len) + return -EINVAL; + } + + if (rxd0 & MT_RXD0_NORMAL_GROUP_2) { + status->timestamp = le32_to_cpu(rxd[0]); + status->flag |= RX_FLAG_MACTIME_START; + + if (!(rxd2 & (MT_RXD2_NORMAL_NON_AMPDU_SUB | + MT_RXD2_NORMAL_NON_AMPDU))) { + status->flag |= RX_FLAG_AMPDU_DETAILS; + + /* all subframes of an A-MPDU have the same timestamp */ + if (phy->rx_ampdu_ts != status->timestamp) { + if (!++phy->ampdu_ref) + phy->ampdu_ref++; + } + phy->rx_ampdu_ts = status->timestamp; + + status->ampdu_ref = phy->ampdu_ref; + } + + rxd += 2; + if ((u8 *)rxd - skb->data >= skb->len) + return -EINVAL; + } + + if (rxd0 & MT_RXD0_NORMAL_GROUP_3) { + u32 rxdg5 = le32_to_cpu(rxd[5]); + + /* + * If both PHYs are on the same channel and we don't have a WCID, + * we need to figure out which PHY this packet was received on. + * On the primary PHY, the noise value for the chains belonging to the + * second PHY will be set to the noise value of the last packet from + * that PHY. + */ + if (phy_idx < 0) { + int first_chain = ffs(phy2->mt76->chainmask) - 1; + + phy_idx = ((rxdg5 >> (first_chain * 8)) & 0xff) == 0; + } + } + + if (phy_idx == 1 && phy2) { + mphy = dev->mt76.phys[MT_BAND1]; + phy = phy2; + status->phy_idx = phy_idx; + } + + if (!mt7615_firmware_offload(dev) && chfreq != phy->chfreq) + return -EINVAL; + + mt7615_get_status_freq_info(dev, mphy, status, chfreq); + if (status->band == NL80211_BAND_5GHZ) + sband = &mphy->sband_5g.sband; + else + sband = &mphy->sband_2g.sband; + + if (!test_bit(MT76_STATE_RUNNING, &mphy->state)) + return -EINVAL; + + if (!sband->channels) + return -EINVAL; + + if (rxd0 & MT_RXD0_NORMAL_GROUP_3) { + u32 rxdg0 = le32_to_cpu(rxd[0]); + u32 rxdg1 = le32_to_cpu(rxd[1]); + u32 rxdg3 = le32_to_cpu(rxd[3]); + u8 stbc = FIELD_GET(MT_RXV1_HT_STBC, rxdg0); + bool cck = false; + + i = FIELD_GET(MT_RXV1_TX_RATE, rxdg0); + switch (FIELD_GET(MT_RXV1_TX_MODE, rxdg0)) { + case MT_PHY_TYPE_CCK: + cck = true; + fallthrough; + case MT_PHY_TYPE_OFDM: + i = mt76_get_rate(&dev->mt76, sband, i, cck); + break; + case MT_PHY_TYPE_HT_GF: + case MT_PHY_TYPE_HT: + status->encoding = RX_ENC_HT; + if (i > 31) + return -EINVAL; + break; + case MT_PHY_TYPE_VHT: + status->nss = FIELD_GET(MT_RXV2_NSTS, rxdg1) + 1; + status->encoding = RX_ENC_VHT; + break; + default: + return -EINVAL; + } + status->rate_idx = i; + + switch (FIELD_GET(MT_RXV1_FRAME_MODE, rxdg0)) { + case MT_PHY_BW_20: + break; + case MT_PHY_BW_40: + status->bw = RATE_INFO_BW_40; + break; + case MT_PHY_BW_80: + status->bw = RATE_INFO_BW_80; + break; + case MT_PHY_BW_160: + status->bw = RATE_INFO_BW_160; + break; + default: + return -EINVAL; + } + + if (rxdg0 & MT_RXV1_HT_SHORT_GI) + status->enc_flags |= RX_ENC_FLAG_SHORT_GI; + if (rxdg0 & MT_RXV1_HT_AD_CODE) + status->enc_flags |= RX_ENC_FLAG_LDPC; + + status->enc_flags |= RX_ENC_FLAG_STBC_MASK * stbc; + + status->chains = mphy->antenna_mask; + status->chain_signal[0] = to_rssi(MT_RXV4_RCPI0, rxdg3); + status->chain_signal[1] = to_rssi(MT_RXV4_RCPI1, rxdg3); + status->chain_signal[2] = to_rssi(MT_RXV4_RCPI2, rxdg3); + status->chain_signal[3] = to_rssi(MT_RXV4_RCPI3, rxdg3); + + mt7615_mac_fill_tm_rx(mphy->priv, rxd); + + rxd += 6; + if ((u8 *)rxd - skb->data >= skb->len) + return -EINVAL; + } + + amsdu_info = FIELD_GET(MT_RXD1_NORMAL_PAYLOAD_FORMAT, rxd1); + status->amsdu = !!amsdu_info; + if (status->amsdu) { + status->first_amsdu = amsdu_info == MT_RXD1_FIRST_AMSDU_FRAME; + status->last_amsdu = amsdu_info == MT_RXD1_LAST_AMSDU_FRAME; + } + + hdr_gap = (u8 *)rxd - skb->data + 2 * remove_pad; + if (hdr_trans && ieee80211_has_morefrags(fc)) { + if (mt7615_reverse_frag0_hdr_trans(skb, hdr_gap)) + return -EINVAL; + hdr_trans = false; + } else { + int pad_start = 0; + + skb_pull(skb, hdr_gap); + if (!hdr_trans && status->amsdu) { + pad_start = ieee80211_get_hdrlen_from_skb(skb); + } else if (hdr_trans && (rxd2 & MT_RXD2_NORMAL_HDR_TRANS_ERROR)) { + /* + * When header translation failure is indicated, + * the hardware will insert an extra 2-byte field + * containing the data length after the protocol + * type field. This happens either when the LLC-SNAP + * pattern did not match, or if a VLAN header was + * detected. + */ + pad_start = 12; + if (get_unaligned_be16(skb->data + pad_start) == ETH_P_8021Q) + pad_start += 4; + else + pad_start = 0; + } + + if (pad_start) { + memmove(skb->data + 2, skb->data, pad_start); + skb_pull(skb, 2); + } + } + + if (insert_ccmp_hdr && !hdr_trans) { + u8 key_id = FIELD_GET(MT_RXD1_NORMAL_KEY_ID, rxd1); + + mt76_insert_ccmp_hdr(skb, key_id); + } + + if (!hdr_trans) { + hdr = (struct ieee80211_hdr *)skb->data; + fc = hdr->frame_control; + if (ieee80211_is_data_qos(fc)) { + seq_ctrl = le16_to_cpu(hdr->seq_ctrl); + qos_ctl = *ieee80211_get_qos_ctl(hdr); + } + } else { + status->flag |= RX_FLAG_8023; + } + + if (!status->wcid || !ieee80211_is_data_qos(fc)) + return 0; + + status->aggr = unicast && + !ieee80211_is_qos_nullfunc(fc); + status->qos_ctl = qos_ctl; + status->seqno = IEEE80211_SEQ_TO_SN(seq_ctrl); + + return 0; +} + +static u16 +mt7615_mac_tx_rate_val(struct mt7615_dev *dev, + struct mt76_phy *mphy, + const struct ieee80211_tx_rate *rate, + bool stbc, u8 *bw) +{ + u8 phy, nss, rate_idx; + u16 rateval = 0; + + *bw = 0; + + if (rate->flags & IEEE80211_TX_RC_VHT_MCS) { + rate_idx = ieee80211_rate_get_vht_mcs(rate); + nss = ieee80211_rate_get_vht_nss(rate); + phy = MT_PHY_TYPE_VHT; + if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) + *bw = 1; + else if (rate->flags & IEEE80211_TX_RC_80_MHZ_WIDTH) + *bw = 2; + else if (rate->flags & IEEE80211_TX_RC_160_MHZ_WIDTH) + *bw = 3; + } else if (rate->flags & IEEE80211_TX_RC_MCS) { + rate_idx = rate->idx; + nss = 1 + (rate->idx >> 3); + phy = MT_PHY_TYPE_HT; + if (rate->flags & IEEE80211_TX_RC_GREEN_FIELD) + phy = MT_PHY_TYPE_HT_GF; + if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) + *bw = 1; + } else { + const struct ieee80211_rate *r; + int band = mphy->chandef.chan->band; + u16 val; + + nss = 1; + r = &mphy->hw->wiphy->bands[band]->bitrates[rate->idx]; + if (rate->flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) + val = r->hw_value_short; + else + val = r->hw_value; + + phy = val >> 8; + rate_idx = val & 0xff; + } + + if (stbc && nss == 1) { + nss++; + rateval |= MT_TX_RATE_STBC; + } + + rateval |= (FIELD_PREP(MT_TX_RATE_IDX, rate_idx) | + FIELD_PREP(MT_TX_RATE_MODE, phy) | + FIELD_PREP(MT_TX_RATE_NSS, nss - 1)); + + return rateval; +} + +int mt7615_mac_write_txwi(struct mt7615_dev *dev, __le32 *txwi, + struct sk_buff *skb, struct mt76_wcid *wcid, + struct ieee80211_sta *sta, int pid, + struct ieee80211_key_conf *key, + enum mt76_txq_id qid, bool beacon) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + u8 fc_type, fc_stype, p_fmt, q_idx, omac_idx = 0, wmm_idx = 0; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_tx_rate *rate = &info->control.rates[0]; + u8 phy_idx = (info->hw_queue & MT_TX_HW_QUEUE_PHY) >> 2; + bool multicast = is_multicast_ether_addr(hdr->addr1); + struct ieee80211_vif *vif = info->control.vif; + bool is_mmio = mt76_is_mmio(&dev->mt76); + u32 val, sz_txd = is_mmio ? MT_TXD_SIZE : MT_USB_TXD_SIZE; + struct mt76_phy *mphy = &dev->mphy; + __le16 fc = hdr->frame_control; + int tx_count = 8; + u16 seqno = 0; + + if (vif) { + struct mt76_vif_link *mvif = (struct mt76_vif_link *)vif->drv_priv; + + omac_idx = mvif->omac_idx; + wmm_idx = mvif->wmm_idx; + } + + if (sta) { + struct mt7615_sta *msta = (struct mt7615_sta *)sta->drv_priv; + + tx_count = msta->rate_count; + } + + if (phy_idx && dev->mt76.phys[MT_BAND1]) + mphy = dev->mt76.phys[MT_BAND1]; + + fc_type = (le16_to_cpu(fc) & IEEE80211_FCTL_FTYPE) >> 2; + fc_stype = (le16_to_cpu(fc) & IEEE80211_FCTL_STYPE) >> 4; + + if (beacon) { + p_fmt = MT_TX_TYPE_FW; + q_idx = phy_idx ? MT_LMAC_BCN1 : MT_LMAC_BCN0; + } else if (qid >= MT_TXQ_PSD) { + p_fmt = is_mmio ? MT_TX_TYPE_CT : MT_TX_TYPE_SF; + q_idx = phy_idx ? MT_LMAC_ALTX1 : MT_LMAC_ALTX0; + } else { + p_fmt = is_mmio ? MT_TX_TYPE_CT : MT_TX_TYPE_SF; + q_idx = wmm_idx * MT7615_MAX_WMM_SETS + + mt7615_lmac_mapping(dev, skb_get_queue_mapping(skb)); + } + + val = FIELD_PREP(MT_TXD0_TX_BYTES, skb->len + sz_txd) | + FIELD_PREP(MT_TXD0_P_IDX, MT_TX_PORT_IDX_LMAC) | + FIELD_PREP(MT_TXD0_Q_IDX, q_idx); + txwi[0] = cpu_to_le32(val); + + val = MT_TXD1_LONG_FORMAT | + FIELD_PREP(MT_TXD1_WLAN_IDX, wcid->idx) | + FIELD_PREP(MT_TXD1_HDR_FORMAT, MT_HDR_FORMAT_802_11) | + FIELD_PREP(MT_TXD1_HDR_INFO, + ieee80211_get_hdrlen_from_skb(skb) / 2) | + FIELD_PREP(MT_TXD1_TID, + skb->priority & IEEE80211_QOS_CTL_TID_MASK) | + FIELD_PREP(MT_TXD1_PKT_FMT, p_fmt) | + FIELD_PREP(MT_TXD1_OWN_MAC, omac_idx); + txwi[1] = cpu_to_le32(val); + + val = FIELD_PREP(MT_TXD2_FRAME_TYPE, fc_type) | + FIELD_PREP(MT_TXD2_SUB_TYPE, fc_stype) | + FIELD_PREP(MT_TXD2_MULTICAST, multicast); + if (key) { + if (multicast && ieee80211_is_robust_mgmt_frame(skb) && + key->cipher == WLAN_CIPHER_SUITE_AES_CMAC) { + val |= MT_TXD2_BIP; + txwi[3] = 0; + } else { + txwi[3] = cpu_to_le32(MT_TXD3_PROTECT_FRAME); + } + } else { + txwi[3] = 0; + } + txwi[2] = cpu_to_le32(val); + + if (!(info->flags & IEEE80211_TX_CTL_AMPDU)) + txwi[2] |= cpu_to_le32(MT_TXD2_BA_DISABLE); + + txwi[4] = 0; + txwi[6] = 0; + + if (rate->idx >= 0 && rate->count && + !(info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE)) { + bool stbc = info->flags & IEEE80211_TX_CTL_STBC; + u8 bw; + u16 rateval = mt7615_mac_tx_rate_val(dev, mphy, rate, stbc, + &bw); + + txwi[2] |= cpu_to_le32(MT_TXD2_FIX_RATE); + + val = MT_TXD6_FIXED_BW | + FIELD_PREP(MT_TXD6_BW, bw) | + FIELD_PREP(MT_TXD6_TX_RATE, rateval); + txwi[6] |= cpu_to_le32(val); + + if (rate->flags & IEEE80211_TX_RC_SHORT_GI) + txwi[6] |= cpu_to_le32(MT_TXD6_SGI); + + if (info->flags & IEEE80211_TX_CTL_LDPC) + txwi[6] |= cpu_to_le32(MT_TXD6_LDPC); + + if (!(rate->flags & (IEEE80211_TX_RC_MCS | + IEEE80211_TX_RC_VHT_MCS))) + txwi[2] |= cpu_to_le32(MT_TXD2_BA_DISABLE); + + tx_count = rate->count; + } + + if (!ieee80211_is_beacon(fc)) { + struct ieee80211_hw *hw = mt76_hw(dev); + + val = MT_TXD5_TX_STATUS_HOST | FIELD_PREP(MT_TXD5_PID, pid); + if (!ieee80211_hw_check(hw, SUPPORTS_PS)) + val |= MT_TXD5_SW_POWER_MGMT; + txwi[5] = cpu_to_le32(val); + } else { + txwi[5] = 0; + /* use maximum tx count for beacons */ + tx_count = 0x1f; + } + + val = FIELD_PREP(MT_TXD3_REM_TX_COUNT, tx_count); + if (info->flags & IEEE80211_TX_CTL_INJECTED) { + seqno = le16_to_cpu(hdr->seq_ctrl); + + if (ieee80211_is_back_req(hdr->frame_control)) { + struct ieee80211_bar *bar; + + bar = (struct ieee80211_bar *)skb->data; + seqno = le16_to_cpu(bar->start_seq_num); + } + + val |= MT_TXD3_SN_VALID | + FIELD_PREP(MT_TXD3_SEQ, IEEE80211_SEQ_TO_SN(seqno)); + } + + txwi[3] |= cpu_to_le32(val); + + if (info->flags & IEEE80211_TX_CTL_NO_ACK) + txwi[3] |= cpu_to_le32(MT_TXD3_NO_ACK); + + val = FIELD_PREP(MT_TXD7_TYPE, fc_type) | + FIELD_PREP(MT_TXD7_SUB_TYPE, fc_stype) | + FIELD_PREP(MT_TXD7_SPE_IDX, 0x18); + txwi[7] = cpu_to_le32(val); + if (!is_mmio) { + val = FIELD_PREP(MT_TXD8_L_TYPE, fc_type) | + FIELD_PREP(MT_TXD8_L_SUB_TYPE, fc_stype); + txwi[8] = cpu_to_le32(val); + } + + return 0; +} +EXPORT_SYMBOL_GPL(mt7615_mac_write_txwi); + +bool mt7615_mac_wtbl_update(struct mt7615_dev *dev, int idx, u32 mask) +{ + mt76_rmw(dev, MT_WTBL_UPDATE, MT_WTBL_UPDATE_WLAN_IDX, + FIELD_PREP(MT_WTBL_UPDATE_WLAN_IDX, idx) | mask); + + return mt76_poll(dev, MT_WTBL_UPDATE, MT_WTBL_UPDATE_BUSY, + 0, 5000); +} + +void mt7615_mac_sta_poll(struct mt7615_dev *dev) +{ + static const u8 ac_to_tid[4] = { + [IEEE80211_AC_BE] = 0, + [IEEE80211_AC_BK] = 1, + [IEEE80211_AC_VI] = 4, + [IEEE80211_AC_VO] = 6 + }; + static const u8 hw_queue_map[] = { + [IEEE80211_AC_BK] = 0, + [IEEE80211_AC_BE] = 1, + [IEEE80211_AC_VI] = 2, + [IEEE80211_AC_VO] = 3, + }; + struct ieee80211_sta *sta; + struct mt7615_sta *msta; + u32 addr, tx_time[4], rx_time[4]; + struct list_head sta_poll_list; + int i; + + INIT_LIST_HEAD(&sta_poll_list); + spin_lock_bh(&dev->mt76.sta_poll_lock); + list_splice_init(&dev->mt76.sta_poll_list, &sta_poll_list); + spin_unlock_bh(&dev->mt76.sta_poll_lock); + + while (!list_empty(&sta_poll_list)) { + bool clear = false; + + msta = list_first_entry(&sta_poll_list, struct mt7615_sta, + wcid.poll_list); + + spin_lock_bh(&dev->mt76.sta_poll_lock); + list_del_init(&msta->wcid.poll_list); + spin_unlock_bh(&dev->mt76.sta_poll_lock); + + addr = mt7615_mac_wtbl_addr(dev, msta->wcid.idx) + 19 * 4; + + for (i = 0; i < 4; i++, addr += 8) { + u32 tx_last = msta->airtime_ac[i]; + u32 rx_last = msta->airtime_ac[i + 4]; + + msta->airtime_ac[i] = mt76_rr(dev, addr); + msta->airtime_ac[i + 4] = mt76_rr(dev, addr + 4); + tx_time[i] = msta->airtime_ac[i] - tx_last; + rx_time[i] = msta->airtime_ac[i + 4] - rx_last; + + if ((tx_last | rx_last) & BIT(30)) + clear = true; + } + + if (clear) { + mt7615_mac_wtbl_update(dev, msta->wcid.idx, + MT_WTBL_UPDATE_ADM_COUNT_CLEAR); + memset(msta->airtime_ac, 0, sizeof(msta->airtime_ac)); + } + + if (!msta->wcid.sta) + continue; + + sta = container_of((void *)msta, struct ieee80211_sta, + drv_priv); + for (i = 0; i < 4; i++) { + u32 tx_cur = tx_time[i]; + u32 rx_cur = rx_time[hw_queue_map[i]]; + u8 tid = ac_to_tid[i]; + + if (!tx_cur && !rx_cur) + continue; + + ieee80211_sta_register_airtime(sta, tid, tx_cur, + rx_cur); + } + } +} +EXPORT_SYMBOL_GPL(mt7615_mac_sta_poll); + +static void +mt7615_mac_update_rate_desc(struct mt7615_phy *phy, struct mt7615_sta *sta, + struct ieee80211_tx_rate *probe_rate, + struct ieee80211_tx_rate *rates, + struct mt7615_rate_desc *rd) +{ + struct mt7615_dev *dev = phy->dev; + struct mt76_phy *mphy = phy->mt76; + struct ieee80211_tx_rate *ref; + bool rateset, stbc = false; + int n_rates = sta->n_rates; + u8 bw, bw_prev; + int i, j; + + for (i = n_rates; i < 4; i++) + rates[i] = rates[n_rates - 1]; + + rateset = !(sta->rate_set_tsf & BIT(0)); + memcpy(sta->rateset[rateset].rates, rates, + sizeof(sta->rateset[rateset].rates)); + if (probe_rate) { + sta->rateset[rateset].probe_rate = *probe_rate; + ref = &sta->rateset[rateset].probe_rate; + } else { + sta->rateset[rateset].probe_rate.idx = -1; + ref = &sta->rateset[rateset].rates[0]; + } + + rates = sta->rateset[rateset].rates; + for (i = 0; i < ARRAY_SIZE(sta->rateset[rateset].rates); i++) { + /* + * We don't support switching between short and long GI + * within the rate set. For accurate tx status reporting, we + * need to make sure that flags match. + * For improved performance, avoid duplicate entries by + * decrementing the MCS index if necessary + */ + if ((ref->flags ^ rates[i].flags) & IEEE80211_TX_RC_SHORT_GI) + rates[i].flags ^= IEEE80211_TX_RC_SHORT_GI; + + for (j = 0; j < i; j++) { + if (rates[i].idx != rates[j].idx) + continue; + if ((rates[i].flags ^ rates[j].flags) & + (IEEE80211_TX_RC_40_MHZ_WIDTH | + IEEE80211_TX_RC_80_MHZ_WIDTH | + IEEE80211_TX_RC_160_MHZ_WIDTH)) + continue; + + if (!rates[i].idx) + continue; + + rates[i].idx--; + } + } + + rd->val[0] = mt7615_mac_tx_rate_val(dev, mphy, &rates[0], stbc, &bw); + bw_prev = bw; + + if (probe_rate) { + rd->probe_val = mt7615_mac_tx_rate_val(dev, mphy, probe_rate, + stbc, &bw); + if (bw) + rd->bw_idx = 1; + else + bw_prev = 0; + } else { + rd->probe_val = rd->val[0]; + } + + rd->val[1] = mt7615_mac_tx_rate_val(dev, mphy, &rates[1], stbc, &bw); + if (bw_prev) { + rd->bw_idx = 3; + bw_prev = bw; + } + + rd->val[2] = mt7615_mac_tx_rate_val(dev, mphy, &rates[2], stbc, &bw); + if (bw_prev) { + rd->bw_idx = 5; + bw_prev = bw; + } + + rd->val[3] = mt7615_mac_tx_rate_val(dev, mphy, &rates[3], stbc, &bw); + if (bw_prev) + rd->bw_idx = 7; + + rd->rateset = rateset; + rd->bw = bw; +} + +static int +mt7615_mac_queue_rate_update(struct mt7615_phy *phy, struct mt7615_sta *sta, + struct ieee80211_tx_rate *probe_rate, + struct ieee80211_tx_rate *rates) +{ + struct mt7615_dev *dev = phy->dev; + struct mt7615_wtbl_rate_desc *wrd; + + if (work_pending(&dev->rate_work)) + return -EBUSY; + + wrd = kzalloc_obj(*wrd, GFP_ATOMIC); + if (!wrd) + return -ENOMEM; + + wrd->sta = sta; + mt7615_mac_update_rate_desc(phy, sta, probe_rate, rates, + &wrd->rate); + list_add_tail(&wrd->node, &dev->wrd_head); + queue_work(dev->mt76.wq, &dev->rate_work); + + return 0; +} + +u32 mt7615_mac_get_sta_tid_sn(struct mt7615_dev *dev, int wcid, u8 tid) +{ + u32 addr, val, val2; + u8 offset; + + addr = mt7615_mac_wtbl_addr(dev, wcid) + 11 * 4; + + offset = tid * 12; + addr += 4 * (offset / 32); + offset %= 32; + + val = mt76_rr(dev, addr); + val >>= offset; + + if (offset > 20) { + addr += 4; + val2 = mt76_rr(dev, addr); + val |= val2 << (32 - offset); + } + + return val & GENMASK(11, 0); +} + +void mt7615_mac_set_rates(struct mt7615_phy *phy, struct mt7615_sta *sta, + struct ieee80211_tx_rate *probe_rate, + struct ieee80211_tx_rate *rates) +{ + int wcid = sta->wcid.idx, n_rates = sta->n_rates; + struct mt7615_dev *dev = phy->dev; + struct mt7615_rate_desc rd; + u32 w5, w27, addr; + u16 idx = sta->vif->mt76.omac_idx; + + if (!mt76_is_mmio(&dev->mt76)) { + mt7615_mac_queue_rate_update(phy, sta, probe_rate, rates); + return; + } + + if (!mt76_poll(dev, MT_WTBL_UPDATE, MT_WTBL_UPDATE_BUSY, 0, 5000)) + return; + + memset(&rd, 0, sizeof(struct mt7615_rate_desc)); + mt7615_mac_update_rate_desc(phy, sta, probe_rate, rates, &rd); + + addr = mt7615_mac_wtbl_addr(dev, wcid); + w27 = mt76_rr(dev, addr + 27 * 4); + w27 &= ~MT_WTBL_W27_CC_BW_SEL; + w27 |= FIELD_PREP(MT_WTBL_W27_CC_BW_SEL, rd.bw); + + w5 = mt76_rr(dev, addr + 5 * 4); + w5 &= ~(MT_WTBL_W5_BW_CAP | MT_WTBL_W5_CHANGE_BW_RATE | + MT_WTBL_W5_MPDU_OK_COUNT | + MT_WTBL_W5_MPDU_FAIL_COUNT | + MT_WTBL_W5_RATE_IDX); + w5 |= FIELD_PREP(MT_WTBL_W5_BW_CAP, rd.bw) | + FIELD_PREP(MT_WTBL_W5_CHANGE_BW_RATE, + rd.bw_idx ? rd.bw_idx - 1 : 7); + + mt76_wr(dev, MT_WTBL_RIUCR0, w5); + + mt76_wr(dev, MT_WTBL_RIUCR1, + FIELD_PREP(MT_WTBL_RIUCR1_RATE0, rd.probe_val) | + FIELD_PREP(MT_WTBL_RIUCR1_RATE1, rd.val[0]) | + FIELD_PREP(MT_WTBL_RIUCR1_RATE2_LO, rd.val[1])); + + mt76_wr(dev, MT_WTBL_RIUCR2, + FIELD_PREP(MT_WTBL_RIUCR2_RATE2_HI, rd.val[1] >> 8) | + FIELD_PREP(MT_WTBL_RIUCR2_RATE3, rd.val[1]) | + FIELD_PREP(MT_WTBL_RIUCR2_RATE4, rd.val[2]) | + FIELD_PREP(MT_WTBL_RIUCR2_RATE5_LO, rd.val[2])); + + mt76_wr(dev, MT_WTBL_RIUCR3, + FIELD_PREP(MT_WTBL_RIUCR3_RATE5_HI, rd.val[2] >> 4) | + FIELD_PREP(MT_WTBL_RIUCR3_RATE6, rd.val[3]) | + FIELD_PREP(MT_WTBL_RIUCR3_RATE7, rd.val[3])); + + mt76_wr(dev, MT_WTBL_UPDATE, + FIELD_PREP(MT_WTBL_UPDATE_WLAN_IDX, wcid) | + MT_WTBL_UPDATE_RATE_UPDATE | + MT_WTBL_UPDATE_TX_COUNT_CLEAR); + + mt76_wr(dev, addr + 27 * 4, w27); + + idx = idx > HW_BSSID_MAX ? HW_BSSID_0 : idx; + addr = idx > 1 ? MT_LPON_TCR2(idx): MT_LPON_TCR0(idx); + + mt76_rmw(dev, addr, MT_LPON_TCR_MODE, MT_LPON_TCR_READ); /* TSF read */ + sta->rate_set_tsf = mt76_rr(dev, MT_LPON_UTTR0) & ~BIT(0); + sta->rate_set_tsf |= rd.rateset; + + if (!(sta->wcid.tx_info & MT_WCID_TX_INFO_SET)) + mt76_poll(dev, MT_WTBL_UPDATE, MT_WTBL_UPDATE_BUSY, 0, 5000); + + sta->rate_count = 2 * MT7615_RATE_RETRY * n_rates; + sta->wcid.tx_info |= MT_WCID_TX_INFO_SET; + sta->rate_probe = !!probe_rate; +} +EXPORT_SYMBOL_GPL(mt7615_mac_set_rates); + +static int +mt7615_mac_wtbl_update_key(struct mt7615_dev *dev, struct mt76_wcid *wcid, + struct ieee80211_key_conf *key, + enum mt76_cipher_type cipher, u16 cipher_mask) +{ + u32 addr = mt7615_mac_wtbl_addr(dev, wcid->idx) + 30 * 4; + u8 data[32] = {}; + + if (key->keylen > sizeof(data)) + return -EINVAL; + + mt76_rr_copy(dev, addr, data, sizeof(data)); + if (cipher == MT_CIPHER_TKIP) { + /* Rx/Tx MIC keys are swapped */ + memcpy(data, key->key, 16); + memcpy(data + 16, key->key + 24, 8); + memcpy(data + 24, key->key + 16, 8); + } else { + if (cipher_mask == BIT(cipher)) + memcpy(data, key->key, key->keylen); + else if (cipher != MT_CIPHER_BIP_CMAC_128) + memcpy(data, key->key, 16); + if (cipher == MT_CIPHER_BIP_CMAC_128) + memcpy(data + 16, key->key, 16); + } + + mt76_wr_copy(dev, addr, data, sizeof(data)); + + return 0; +} + +static int +mt7615_mac_wtbl_update_pk(struct mt7615_dev *dev, struct mt76_wcid *wcid, + enum mt76_cipher_type cipher, u16 cipher_mask, + int keyidx) +{ + u32 addr = mt7615_mac_wtbl_addr(dev, wcid->idx), w0, w1; + + if (!mt76_poll(dev, MT_WTBL_UPDATE, MT_WTBL_UPDATE_BUSY, 0, 5000)) + return -ETIMEDOUT; + + w0 = mt76_rr(dev, addr); + w1 = mt76_rr(dev, addr + 4); + + if (cipher_mask) + w0 |= MT_WTBL_W0_RX_KEY_VALID; + else + w0 &= ~(MT_WTBL_W0_RX_KEY_VALID | MT_WTBL_W0_KEY_IDX); + if (cipher_mask & BIT(MT_CIPHER_BIP_CMAC_128)) + w0 |= MT_WTBL_W0_RX_IK_VALID; + else + w0 &= ~MT_WTBL_W0_RX_IK_VALID; + + if (cipher != MT_CIPHER_BIP_CMAC_128 || cipher_mask == BIT(cipher)) { + w0 &= ~MT_WTBL_W0_KEY_IDX; + w0 |= FIELD_PREP(MT_WTBL_W0_KEY_IDX, keyidx); + } + + mt76_wr(dev, MT_WTBL_RICR0, w0); + mt76_wr(dev, MT_WTBL_RICR1, w1); + + if (!mt7615_mac_wtbl_update(dev, wcid->idx, + MT_WTBL_UPDATE_RXINFO_UPDATE)) + return -ETIMEDOUT; + + return 0; +} + +static void +mt7615_mac_wtbl_update_cipher(struct mt7615_dev *dev, struct mt76_wcid *wcid, + enum mt76_cipher_type cipher, u16 cipher_mask) +{ + u32 addr = mt7615_mac_wtbl_addr(dev, wcid->idx); + + if (cipher == MT_CIPHER_BIP_CMAC_128 && + cipher_mask & ~BIT(MT_CIPHER_BIP_CMAC_128)) + return; + + mt76_rmw(dev, addr + 2 * 4, MT_WTBL_W2_KEY_TYPE, + FIELD_PREP(MT_WTBL_W2_KEY_TYPE, cipher)); +} + +int __mt7615_mac_wtbl_set_key(struct mt7615_dev *dev, + struct mt76_wcid *wcid, + struct ieee80211_key_conf *key) +{ + enum mt76_cipher_type cipher; + u16 cipher_mask = wcid->cipher; + int err; + + cipher = mt7615_mac_get_cipher(key->cipher); + if (cipher == MT_CIPHER_NONE) + return -EOPNOTSUPP; + + cipher_mask |= BIT(cipher); + mt7615_mac_wtbl_update_cipher(dev, wcid, cipher, cipher_mask); + err = mt7615_mac_wtbl_update_key(dev, wcid, key, cipher, cipher_mask); + if (err < 0) + return err; + + err = mt7615_mac_wtbl_update_pk(dev, wcid, cipher, cipher_mask, + key->keyidx); + if (err < 0) + return err; + + wcid->cipher = cipher_mask; + + return 0; +} + +int mt7615_mac_wtbl_set_key(struct mt7615_dev *dev, + struct mt76_wcid *wcid, + struct ieee80211_key_conf *key) +{ + int err; + + spin_lock_bh(&dev->mt76.lock); + err = __mt7615_mac_wtbl_set_key(dev, wcid, key); + spin_unlock_bh(&dev->mt76.lock); + + return err; +} + +static bool mt7615_fill_txs(struct mt7615_dev *dev, struct mt7615_sta *sta, + struct ieee80211_tx_info *info, __le32 *txs_data) +{ + struct ieee80211_supported_band *sband; + struct mt7615_rate_set *rs; + struct mt76_phy *mphy; + int first_idx = 0, last_idx; + int i, idx, count; + bool fixed_rate, ack_timeout; + bool ampdu, cck = false; + bool rs_idx; + u32 rate_set_tsf; + u32 final_rate, final_rate_flags, final_nss, txs; + + txs = le32_to_cpu(txs_data[1]); + ampdu = txs & MT_TXS1_AMPDU; + + txs = le32_to_cpu(txs_data[3]); + count = FIELD_GET(MT_TXS3_TX_COUNT, txs); + last_idx = FIELD_GET(MT_TXS3_LAST_TX_RATE, txs); + + txs = le32_to_cpu(txs_data[0]); + fixed_rate = txs & MT_TXS0_FIXED_RATE; + final_rate = FIELD_GET(MT_TXS0_TX_RATE, txs); + ack_timeout = txs & MT_TXS0_ACK_TIMEOUT; + + if (!ampdu && (txs & MT_TXS0_RTS_TIMEOUT)) + return false; + + if (txs & MT_TXS0_QUEUE_TIMEOUT) + return false; + + if (!ack_timeout) + info->flags |= IEEE80211_TX_STAT_ACK; + + info->status.ampdu_len = 1; + info->status.ampdu_ack_len = !!(info->flags & + IEEE80211_TX_STAT_ACK); + + if (ampdu || (info->flags & IEEE80211_TX_CTL_AMPDU)) + info->flags |= IEEE80211_TX_STAT_AMPDU | IEEE80211_TX_CTL_AMPDU; + + first_idx = max_t(int, 0, last_idx - (count - 1) / MT7615_RATE_RETRY); + + if (fixed_rate) { + info->status.rates[0].count = count; + i = 0; + goto out; + } + + rate_set_tsf = READ_ONCE(sta->rate_set_tsf); + rs_idx = !((u32)(le32_get_bits(txs_data[4], MT_TXS4_F0_TIMESTAMP) - + rate_set_tsf) < 1000000); + rs_idx ^= rate_set_tsf & BIT(0); + rs = &sta->rateset[rs_idx]; + + if (!first_idx && rs->probe_rate.idx >= 0) { + info->status.rates[0] = rs->probe_rate; + + spin_lock_bh(&dev->mt76.lock); + if (sta->rate_probe) { + struct mt7615_phy *phy = &dev->phy; + + if (sta->wcid.phy_idx && dev->mt76.phys[MT_BAND1]) + phy = dev->mt76.phys[MT_BAND1]->priv; + + mt7615_mac_set_rates(phy, sta, NULL, sta->rates); + } + spin_unlock_bh(&dev->mt76.lock); + } else { + info->status.rates[0] = rs->rates[first_idx / 2]; + } + info->status.rates[0].count = 0; + + for (i = 0, idx = first_idx; count && idx <= last_idx; idx++) { + struct ieee80211_tx_rate *cur_rate; + int cur_count; + + cur_rate = &rs->rates[idx / 2]; + cur_count = min_t(int, MT7615_RATE_RETRY, count); + count -= cur_count; + + if (idx && (cur_rate->idx != info->status.rates[i].idx || + cur_rate->flags != info->status.rates[i].flags)) { + i++; + if (i == ARRAY_SIZE(info->status.rates)) { + i--; + break; + } + + info->status.rates[i] = *cur_rate; + info->status.rates[i].count = 0; + } + + info->status.rates[i].count += cur_count; + } + +out: + final_rate_flags = info->status.rates[i].flags; + + switch (FIELD_GET(MT_TX_RATE_MODE, final_rate)) { + case MT_PHY_TYPE_CCK: + cck = true; + fallthrough; + case MT_PHY_TYPE_OFDM: + mphy = &dev->mphy; + if (sta->wcid.phy_idx && dev->mt76.phys[MT_BAND1]) + mphy = dev->mt76.phys[MT_BAND1]; + + if (mphy->chandef.chan->band == NL80211_BAND_5GHZ) + sband = &mphy->sband_5g.sband; + else + sband = &mphy->sband_2g.sband; + final_rate &= MT_TX_RATE_IDX; + final_rate = mt76_get_rate(&dev->mt76, sband, final_rate, + cck); + final_rate_flags = 0; + break; + case MT_PHY_TYPE_HT_GF: + case MT_PHY_TYPE_HT: + final_rate_flags |= IEEE80211_TX_RC_MCS; + final_rate &= MT_TX_RATE_IDX; + if (final_rate > 31) + return false; + break; + case MT_PHY_TYPE_VHT: + final_nss = FIELD_GET(MT_TX_RATE_NSS, final_rate); + + if ((final_rate & MT_TX_RATE_STBC) && final_nss) + final_nss--; + + final_rate_flags |= IEEE80211_TX_RC_VHT_MCS; + final_rate = (final_rate & MT_TX_RATE_IDX) | (final_nss << 4); + break; + default: + return false; + } + + info->status.rates[i].idx = final_rate; + info->status.rates[i].flags = final_rate_flags; + + return true; +} + +static bool mt7615_mac_add_txs_skb(struct mt7615_dev *dev, + struct mt7615_sta *sta, int pid, + __le32 *txs_data) +{ + struct mt76_dev *mdev = &dev->mt76; + struct sk_buff_head list; + struct sk_buff *skb; + + if (pid < MT_PACKET_ID_FIRST) + return false; + + trace_mac_txdone(mdev, sta->wcid.idx, pid); + + mt76_tx_status_lock(mdev, &list); + skb = mt76_tx_status_skb_get(mdev, &sta->wcid, pid, &list); + if (skb) { + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + + if (!mt7615_fill_txs(dev, sta, info, txs_data)) { + info->status.rates[0].count = 0; + info->status.rates[0].idx = -1; + } + + mt76_tx_status_skb_done(mdev, skb, &list); + } + mt76_tx_status_unlock(mdev, &list); + + return !!skb; +} + +static void mt7615_mac_add_txs(struct mt7615_dev *dev, void *data) +{ + struct ieee80211_tx_info info = {}; + struct ieee80211_sta *sta = NULL; + struct mt7615_sta *msta = NULL; + struct mt76_wcid *wcid; + struct mt76_phy *mphy = &dev->mt76.phy; + __le32 *txs_data = data; + u8 wcidx; + u8 pid; + + pid = le32_get_bits(txs_data[0], MT_TXS0_PID); + wcidx = le32_get_bits(txs_data[2], MT_TXS2_WCID); + + if (pid == MT_PACKET_ID_NO_ACK) + return; + + if (wcidx >= MT7615_WTBL_SIZE) + return; + + rcu_read_lock(); + + wcid = mt76_wcid_ptr(dev, wcidx); + if (!wcid) + goto out; + + msta = container_of(wcid, struct mt7615_sta, wcid); + sta = wcid_to_sta(wcid); + mt76_wcid_add_poll(&dev->mt76, &msta->wcid); + + if (mt7615_mac_add_txs_skb(dev, msta, pid, txs_data)) + goto out; + + if (wcidx >= MT7615_WTBL_STA || !sta) + goto out; + + if (wcid->phy_idx && dev->mt76.phys[MT_BAND1]) + mphy = dev->mt76.phys[MT_BAND1]; + + if (mt7615_fill_txs(dev, msta, &info, txs_data)) { + spin_lock_bh(&dev->mt76.rx_lock); + ieee80211_tx_status_noskb(mphy->hw, sta, &info); + spin_unlock_bh(&dev->mt76.rx_lock); + } + +out: + rcu_read_unlock(); +} + +static void +mt7615_txwi_free(struct mt7615_dev *dev, struct mt76_txwi_cache *txwi) +{ + struct mt76_dev *mdev = &dev->mt76; + __le32 *txwi_data; + u32 val; + u8 wcid; + + mt76_connac_txp_skb_unmap(mdev, txwi); + if (!txwi->skb) + goto out; + + txwi_data = (__le32 *)mt76_get_txwi_ptr(mdev, txwi); + val = le32_to_cpu(txwi_data[1]); + wcid = FIELD_GET(MT_TXD1_WLAN_IDX, val); + mt76_tx_complete_skb(mdev, wcid, txwi->skb); + +out: + txwi->skb = NULL; + mt76_put_txwi(mdev, txwi); +} + +static void +mt7615_mac_tx_free_token(struct mt7615_dev *dev, u16 token) +{ + struct mt76_dev *mdev = &dev->mt76; + struct mt76_txwi_cache *txwi; + + trace_mac_tx_free(dev, token); + txwi = mt76_token_put(mdev, token); + if (!txwi) + return; + + mt7615_txwi_free(dev, txwi); +} + +static void mt7615_mac_tx_free(struct mt7615_dev *dev, void *data, int len) +{ + struct mt76_connac_tx_free *free = data; + void *tx_token = data + sizeof(*free); + void *end = data + len; + u8 i, count; + + mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[MT_TXQ_PSD], false); + if (is_mt7615(&dev->mt76)) { + mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[MT_TXQ_BE], false); + } else { + for (i = 0; i < IEEE80211_NUM_ACS; i++) + mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[i], false); + } + + count = le16_get_bits(free->ctrl, MT_TX_FREE_MSDU_ID_CNT); + if (is_mt7615(&dev->mt76)) { + __le16 *token = tx_token; + + if (WARN_ON_ONCE((void *)&token[count] > end)) + return; + + for (i = 0; i < count; i++) + mt7615_mac_tx_free_token(dev, le16_to_cpu(token[i])); + } else { + __le32 *token = tx_token; + + if (WARN_ON_ONCE((void *)&token[count] > end)) + return; + + for (i = 0; i < count; i++) + mt7615_mac_tx_free_token(dev, le32_to_cpu(token[i])); + } + + rcu_read_lock(); + mt7615_mac_sta_poll(dev); + rcu_read_unlock(); + + mt76_worker_schedule(&dev->mt76.tx_worker); +} + +bool mt7615_rx_check(struct mt76_dev *mdev, void *data, int len) +{ + struct mt7615_dev *dev = container_of(mdev, struct mt7615_dev, mt76); + __le32 *rxd = (__le32 *)data; + __le32 *end = (__le32 *)&rxd[len / 4]; + enum rx_pkt_type type; + + type = le32_get_bits(rxd[0], MT_RXD0_PKT_TYPE); + + switch (type) { + case PKT_TYPE_TXRX_NOTIFY: + if (!mt76_is_mmio(mdev)) + return false; + mt7615_mac_tx_free(dev, data, len); + return false; + case PKT_TYPE_TXS: + for (rxd++; rxd + 7 <= end; rxd += 7) + mt7615_mac_add_txs(dev, rxd); + return false; + default: + return true; + } +} +EXPORT_SYMBOL_GPL(mt7615_rx_check); + +void mt7615_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, + struct sk_buff *skb, u32 *info) +{ + struct mt7615_dev *dev = container_of(mdev, struct mt7615_dev, mt76); + __le32 *rxd = (__le32 *)skb->data; + __le32 *end = (__le32 *)&skb->data[skb->len]; + enum rx_pkt_type type; + u16 flag; + + type = le32_get_bits(rxd[0], MT_RXD0_PKT_TYPE); + flag = le32_get_bits(rxd[0], MT_RXD0_PKT_FLAG); + if (type == PKT_TYPE_RX_EVENT && flag == 0x1) + type = PKT_TYPE_NORMAL_MCU; + + switch (type) { + case PKT_TYPE_TXS: + for (rxd++; rxd + 7 <= end; rxd += 7) + mt7615_mac_add_txs(dev, rxd); + dev_kfree_skb(skb); + break; + case PKT_TYPE_TXRX_NOTIFY: + if (!mt76_is_mmio(mdev)) { + dev_kfree_skb(skb); + break; + } + mt7615_mac_tx_free(dev, skb->data, skb->len); + dev_kfree_skb(skb); + break; + case PKT_TYPE_RX_EVENT: + mt7615_mcu_rx_event(dev, skb); + break; + case PKT_TYPE_NORMAL_MCU: + case PKT_TYPE_NORMAL: + if (!mt7615_mac_fill_rx(dev, skb)) { + mt76_rx(&dev->mt76, q, skb); + return; + } + fallthrough; + default: + dev_kfree_skb(skb); + break; + } +} +EXPORT_SYMBOL_GPL(mt7615_queue_rx_skb); + +static void +mt7615_mac_set_sensitivity(struct mt7615_phy *phy, int val, bool ofdm) +{ + struct mt7615_dev *dev = phy->dev; + bool ext_phy = phy != &dev->phy; + + if (is_mt7663(&dev->mt76)) { + if (ofdm) + mt76_rmw(dev, MT7663_WF_PHY_MIN_PRI_PWR(ext_phy), + MT_WF_PHY_PD_OFDM_MASK(0), + MT_WF_PHY_PD_OFDM(0, val)); + else + mt76_rmw(dev, MT7663_WF_PHY_RXTD_CCK_PD(ext_phy), + MT_WF_PHY_PD_CCK_MASK(ext_phy), + MT_WF_PHY_PD_CCK(ext_phy, val)); + return; + } + + if (ofdm) + mt76_rmw(dev, MT_WF_PHY_MIN_PRI_PWR(ext_phy), + MT_WF_PHY_PD_OFDM_MASK(ext_phy), + MT_WF_PHY_PD_OFDM(ext_phy, val)); + else + mt76_rmw(dev, MT_WF_PHY_RXTD_CCK_PD(ext_phy), + MT_WF_PHY_PD_CCK_MASK(ext_phy), + MT_WF_PHY_PD_CCK(ext_phy, val)); +} + +static void +mt7615_mac_set_default_sensitivity(struct mt7615_phy *phy) +{ + /* ofdm */ + mt7615_mac_set_sensitivity(phy, 0x13c, true); + /* cck */ + mt7615_mac_set_sensitivity(phy, 0x92, false); + + phy->ofdm_sensitivity = -98; + phy->cck_sensitivity = -110; + phy->last_cca_adj = jiffies; +} + +void mt7615_mac_set_scs(struct mt7615_phy *phy, bool enable) +{ + struct mt7615_dev *dev = phy->dev; + bool ext_phy = phy != &dev->phy; + u32 reg, mask; + + mt7615_mutex_acquire(dev); + + if (phy->scs_en == enable) + goto out; + + if (is_mt7663(&dev->mt76)) { + reg = MT7663_WF_PHY_MIN_PRI_PWR(ext_phy); + mask = MT_WF_PHY_PD_BLK(0); + } else { + reg = MT_WF_PHY_MIN_PRI_PWR(ext_phy); + mask = MT_WF_PHY_PD_BLK(ext_phy); + } + + if (enable) { + mt76_set(dev, reg, mask); + if (is_mt7622(&dev->mt76)) { + mt76_set(dev, MT_MIB_M0_MISC_CR(0), 0x7 << 8); + mt76_set(dev, MT_MIB_M0_MISC_CR(0), 0x7); + } + } else { + mt76_clear(dev, reg, mask); + } + + mt7615_mac_set_default_sensitivity(phy); + phy->scs_en = enable; + +out: + mt7615_mutex_release(dev); +} + +void mt7615_mac_enable_nf(struct mt7615_dev *dev, bool ext_phy) +{ + u32 rxtd, reg; + + if (is_mt7663(&dev->mt76)) + reg = MT7663_WF_PHY_R0_PHYMUX_5; + else + reg = MT_WF_PHY_R0_PHYMUX_5(ext_phy); + + if (ext_phy) + rxtd = MT_WF_PHY_RXTD2(10); + else + rxtd = MT_WF_PHY_RXTD(12); + + mt76_set(dev, rxtd, BIT(18) | BIT(29)); + mt76_set(dev, reg, 0x5 << 12); +} + +void mt7615_mac_cca_stats_reset(struct mt7615_phy *phy) +{ + struct mt7615_dev *dev = phy->dev; + bool ext_phy = phy != &dev->phy; + u32 reg; + + if (is_mt7663(&dev->mt76)) + reg = MT7663_WF_PHY_R0_PHYMUX_5; + else + reg = MT_WF_PHY_R0_PHYMUX_5(ext_phy); + + /* reset PD and MDRDY counters */ + mt76_clear(dev, reg, GENMASK(22, 20)); + mt76_set(dev, reg, BIT(22) | BIT(20)); +} + +static void +mt7615_mac_adjust_sensitivity(struct mt7615_phy *phy, + u32 rts_err_rate, bool ofdm) +{ + struct mt7615_dev *dev = phy->dev; + int false_cca = ofdm ? phy->false_cca_ofdm : phy->false_cca_cck; + bool ext_phy = phy != &dev->phy; + s16 def_th = ofdm ? -98 : -110; + bool update = false; + s8 *sensitivity; + int signal; + + sensitivity = ofdm ? &phy->ofdm_sensitivity : &phy->cck_sensitivity; + signal = mt76_get_min_avg_rssi(&dev->mt76, ext_phy); + if (!signal) { + mt7615_mac_set_default_sensitivity(phy); + return; + } + + signal = min(signal, -72); + if (false_cca > 500) { + if (rts_err_rate > MT_FRAC(40, 100)) + return; + + /* decrease coverage */ + if (*sensitivity == def_th && signal > -90) { + *sensitivity = -90; + update = true; + } else if (*sensitivity + 2 < signal) { + *sensitivity += 2; + update = true; + } + } else if ((false_cca > 0 && false_cca < 50) || + rts_err_rate > MT_FRAC(60, 100)) { + /* increase coverage */ + if (*sensitivity - 2 >= def_th) { + *sensitivity -= 2; + update = true; + } + } + + if (*sensitivity > signal) { + *sensitivity = signal; + update = true; + } + + if (update) { + u16 val = ofdm ? *sensitivity * 2 + 512 : *sensitivity + 256; + + mt7615_mac_set_sensitivity(phy, val, ofdm); + phy->last_cca_adj = jiffies; + } +} + +static void +mt7615_mac_scs_check(struct mt7615_phy *phy) +{ + struct mt7615_dev *dev = phy->dev; + struct mib_stats *mib = &phy->mib; + u32 val, rts_err_rate = 0; + u32 mdrdy_cck, mdrdy_ofdm, pd_cck, pd_ofdm; + bool ext_phy = phy != &dev->phy; + + if (!phy->scs_en) + return; + + if (is_mt7663(&dev->mt76)) + val = mt76_rr(dev, MT7663_WF_PHY_R0_PHYCTRL_STS0(ext_phy)); + else + val = mt76_rr(dev, MT_WF_PHY_R0_PHYCTRL_STS0(ext_phy)); + pd_cck = FIELD_GET(MT_WF_PHYCTRL_STAT_PD_CCK, val); + pd_ofdm = FIELD_GET(MT_WF_PHYCTRL_STAT_PD_OFDM, val); + + if (is_mt7663(&dev->mt76)) + val = mt76_rr(dev, MT7663_WF_PHY_R0_PHYCTRL_STS5(ext_phy)); + else + val = mt76_rr(dev, MT_WF_PHY_R0_PHYCTRL_STS5(ext_phy)); + mdrdy_cck = FIELD_GET(MT_WF_PHYCTRL_STAT_MDRDY_CCK, val); + mdrdy_ofdm = FIELD_GET(MT_WF_PHYCTRL_STAT_MDRDY_OFDM, val); + + phy->false_cca_ofdm = pd_ofdm - mdrdy_ofdm; + phy->false_cca_cck = pd_cck - mdrdy_cck; + mt7615_mac_cca_stats_reset(phy); + + if (mib->rts_cnt + mib->rts_retries_cnt) + rts_err_rate = MT_FRAC(mib->rts_retries_cnt, + mib->rts_cnt + mib->rts_retries_cnt); + + /* cck */ + mt7615_mac_adjust_sensitivity(phy, rts_err_rate, false); + /* ofdm */ + mt7615_mac_adjust_sensitivity(phy, rts_err_rate, true); + + if (time_after(jiffies, phy->last_cca_adj + 10 * HZ)) + mt7615_mac_set_default_sensitivity(phy); +} + +static u8 +mt7615_phy_get_nf(struct mt7615_dev *dev, int idx) +{ + static const u8 nf_power[] = { 92, 89, 86, 83, 80, 75, 70, 65, 60, 55, 52 }; + u32 reg, val, sum = 0, n = 0; + int i; + + if (is_mt7663(&dev->mt76)) + reg = MT7663_WF_PHY_RXTD(20); + else + reg = idx ? MT_WF_PHY_RXTD2(17) : MT_WF_PHY_RXTD(20); + + for (i = 0; i < ARRAY_SIZE(nf_power); i++, reg += 4) { + val = mt76_rr(dev, reg); + sum += val * nf_power[i]; + n += val; + } + + if (!n) + return 0; + + return sum / n; +} + +static void +mt7615_phy_update_channel(struct mt76_phy *mphy, int idx) +{ + struct mt7615_dev *dev = container_of(mphy->dev, struct mt7615_dev, mt76); + struct mt7615_phy *phy = mphy->priv; + struct mt76_channel_state *state; + u64 busy_time, tx_time, rx_time, obss_time; + u32 obss_reg = idx ? MT_WF_RMAC_MIB_TIME6 : MT_WF_RMAC_MIB_TIME5; + int nf; + + busy_time = mt76_get_field(dev, MT_MIB_SDR9(idx), + MT_MIB_SDR9_BUSY_MASK); + tx_time = mt76_get_field(dev, MT_MIB_SDR36(idx), + MT_MIB_SDR36_TXTIME_MASK); + rx_time = mt76_get_field(dev, MT_MIB_SDR37(idx), + MT_MIB_SDR37_RXTIME_MASK); + obss_time = mt76_get_field(dev, obss_reg, MT_MIB_OBSSTIME_MASK); + + nf = mt7615_phy_get_nf(dev, idx); + if (!phy->noise) + phy->noise = nf << 4; + else if (nf) + phy->noise += nf - (phy->noise >> 4); + + state = mphy->chan_state; + state->cc_busy += busy_time; + state->cc_tx += tx_time; + state->cc_rx += rx_time + obss_time; + state->cc_bss_rx += rx_time; + state->noise = -(phy->noise >> 4); +} + +static void mt7615_update_survey(struct mt7615_dev *dev) +{ + struct mt76_dev *mdev = &dev->mt76; + struct mt76_phy *mphy_ext = mdev->phys[MT_BAND1]; + ktime_t cur_time; + + /* MT7615 can only update both phys simultaneously + * since some reisters are shared across bands. + */ + + mt7615_phy_update_channel(&mdev->phy, 0); + if (mphy_ext) + mt7615_phy_update_channel(mphy_ext, 1); + + cur_time = ktime_get_boottime(); + + mt76_update_survey_active_time(&mdev->phy, cur_time); + if (mphy_ext) + mt76_update_survey_active_time(mphy_ext, cur_time); + + /* reset obss airtime */ + mt76_set(dev, MT_WF_RMAC_MIB_TIME0, MT_WF_RMAC_MIB_RXTIME_CLR); +} + +void mt7615_update_channel(struct mt76_phy *mphy) +{ + struct mt7615_dev *dev = container_of(mphy->dev, struct mt7615_dev, mt76); + + if (mt76_connac_pm_wake(&dev->mphy, &dev->pm)) + return; + + mt7615_update_survey(dev); + mt76_connac_power_save_sched(&dev->mphy, &dev->pm); +} +EXPORT_SYMBOL_GPL(mt7615_update_channel); + +static void +mt7615_mac_update_mib_stats(struct mt7615_phy *phy) +{ + struct mt7615_dev *dev = phy->dev; + struct mib_stats *mib = &phy->mib; + bool ext_phy = phy != &dev->phy; + int i, aggr = 0; + u32 val, val2; + + mib->fcs_err_cnt += mt76_get_field(dev, MT_MIB_SDR3(ext_phy), + MT_MIB_SDR3_FCS_ERR_MASK); + + val = mt76_get_field(dev, MT_MIB_SDR14(ext_phy), + MT_MIB_AMPDU_MPDU_COUNT); + if (val) { + val2 = mt76_get_field(dev, MT_MIB_SDR15(ext_phy), + MT_MIB_AMPDU_ACK_COUNT); + mib->aggr_per = 1000 * (val - val2) / val; + } + + for (i = 0; i < 4; i++) { + val = mt76_rr(dev, MT_MIB_MB_SDR1(ext_phy, i)); + mib->ba_miss_cnt += FIELD_GET(MT_MIB_BA_MISS_COUNT_MASK, val); + mib->ack_fail_cnt += FIELD_GET(MT_MIB_ACK_FAIL_COUNT_MASK, + val); + + val = mt76_rr(dev, MT_MIB_MB_SDR0(ext_phy, i)); + mib->rts_cnt += FIELD_GET(MT_MIB_RTS_COUNT_MASK, val); + mib->rts_retries_cnt += FIELD_GET(MT_MIB_RTS_RETRIES_COUNT_MASK, + val); + + val = mt76_rr(dev, MT_TX_AGG_CNT(ext_phy, i)); + phy->mt76->aggr_stats[aggr++] += val & 0xffff; + phy->mt76->aggr_stats[aggr++] += val >> 16; + } +} + +void mt7615_pm_wake_work(struct work_struct *work) +{ + struct mt7615_dev *dev; + struct mt76_phy *mphy; + + dev = (struct mt7615_dev *)container_of(work, struct mt7615_dev, + pm.wake_work); + mphy = dev->phy.mt76; + + if (!mt7615_mcu_set_drv_ctrl(dev)) { + struct mt76_dev *mdev = &dev->mt76; + int i; + + if (mt76_is_sdio(mdev)) { + mt76_connac_pm_dequeue_skbs(mphy, &dev->pm); + mt76_worker_schedule(&mdev->sdio.txrx_worker); + } else { + local_bh_disable(); + mt76_for_each_q_rx(mdev, i) + napi_schedule(&mdev->napi[i]); + local_bh_enable(); + mt76_connac_pm_dequeue_skbs(mphy, &dev->pm); + mt76_queue_tx_cleanup(dev, mdev->q_mcu[MT_MCUQ_WM], + false); + } + + if (test_bit(MT76_STATE_RUNNING, &mphy->state)) { + unsigned long timeout; + + timeout = mt7615_get_macwork_timeout(dev); + ieee80211_queue_delayed_work(mphy->hw, &mphy->mac_work, + timeout); + } + } + + ieee80211_wake_queues(mphy->hw); + wake_up(&dev->pm.wait); +} + +void mt7615_pm_power_save_work(struct work_struct *work) +{ + struct mt7615_dev *dev; + unsigned long delta; + + dev = (struct mt7615_dev *)container_of(work, struct mt7615_dev, + pm.ps_work.work); + + delta = dev->pm.idle_timeout; + if (test_bit(MT76_HW_SCANNING, &dev->mphy.state) || + test_bit(MT76_HW_SCHED_SCANNING, &dev->mphy.state)) + goto out; + + if (mutex_is_locked(&dev->mt76.mutex)) + /* if mt76 mutex is held we should not put the device + * to sleep since we are currently accessing device + * register map. We need to wait for the next power_save + * trigger. + */ + goto out; + + if (time_is_after_jiffies(dev->pm.last_activity + delta)) { + delta = dev->pm.last_activity + delta - jiffies; + goto out; + } + + if (!mt7615_mcu_set_fw_ctrl(dev)) + return; +out: + queue_delayed_work(dev->mt76.wq, &dev->pm.ps_work, delta); +} + +void mt7615_mac_work(struct work_struct *work) +{ + struct mt7615_phy *phy; + struct mt76_phy *mphy; + unsigned long timeout; + + mphy = (struct mt76_phy *)container_of(work, struct mt76_phy, + mac_work.work); + phy = mphy->priv; + + mt7615_mutex_acquire(phy->dev); + + mt7615_update_survey(phy->dev); + if (++mphy->mac_work_count == 5) { + mphy->mac_work_count = 0; + + mt7615_mac_update_mib_stats(phy); + mt7615_mac_scs_check(phy); + } + + mt7615_mutex_release(phy->dev); + + mt76_tx_status_check(mphy->dev, false); + + timeout = mt7615_get_macwork_timeout(phy->dev); + ieee80211_queue_delayed_work(mphy->hw, &mphy->mac_work, timeout); +} + +void mt7615_tx_token_put(struct mt7615_dev *dev) +{ + struct mt76_txwi_cache *txwi; + int id; + + spin_lock_bh(&dev->mt76.token_lock); + idr_for_each_entry(&dev->mt76.token, txwi, id) + mt7615_txwi_free(dev, txwi); + spin_unlock_bh(&dev->mt76.token_lock); + idr_destroy(&dev->mt76.token); +} +EXPORT_SYMBOL_GPL(mt7615_tx_token_put); + +static void mt7615_dfs_stop_radar_detector(struct mt7615_phy *phy) +{ + struct mt7615_dev *dev = phy->dev; + + if (phy->rdd_state & BIT(0)) + mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_STOP, 0, + MT_RX_SEL0, 0); + if (phy->rdd_state & BIT(1)) + mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_STOP, 1, + MT_RX_SEL0, 0); +} + +static int mt7615_dfs_start_rdd(struct mt7615_dev *dev, int chain) +{ + int err; + + err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_START, chain, + MT_RX_SEL0, 0); + if (err < 0) + return err; + + return mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_DET_MODE, chain, + MT_RX_SEL0, 1); +} + +static int mt7615_dfs_start_radar_detector(struct mt7615_phy *phy) +{ + struct cfg80211_chan_def *chandef = &phy->mt76->chandef; + struct mt7615_dev *dev = phy->dev; + bool ext_phy = phy != &dev->phy; + int err; + + /* start CAC */ + err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_CAC_START, ext_phy, + MT_RX_SEL0, 0); + if (err < 0) + return err; + + err = mt7615_dfs_start_rdd(dev, ext_phy); + if (err < 0) + return err; + + phy->rdd_state |= BIT(ext_phy); + + if (chandef->width == NL80211_CHAN_WIDTH_160 || + chandef->width == NL80211_CHAN_WIDTH_80P80) { + err = mt7615_dfs_start_rdd(dev, 1); + if (err < 0) + return err; + + phy->rdd_state |= BIT(1); + } + + return 0; +} + +static int +mt7615_dfs_init_radar_specs(struct mt7615_phy *phy) +{ + const struct mt7615_dfs_radar_spec *radar_specs; + struct mt7615_dev *dev = phy->dev; + int err, i, lpn = 500; + + switch (dev->mt76.region) { + case NL80211_DFS_FCC: + radar_specs = &fcc_radar_specs; + lpn = 8; + break; + case NL80211_DFS_ETSI: + radar_specs = &etsi_radar_specs; + break; + case NL80211_DFS_JP: + radar_specs = &jp_radar_specs; + break; + default: + return -EINVAL; + } + + /* avoid FCC radar detection in non-FCC region */ + err = mt7615_mcu_set_fcc5_lpn(dev, lpn); + if (err < 0) + return err; + + for (i = 0; i < ARRAY_SIZE(radar_specs->radar_pattern); i++) { + err = mt7615_mcu_set_radar_th(dev, i, + &radar_specs->radar_pattern[i]); + if (err < 0) + return err; + } + + return mt7615_mcu_set_pulse_th(dev, &radar_specs->pulse_th); +} + +int mt7615_dfs_init_radar_detector(struct mt7615_phy *phy) +{ + struct cfg80211_chan_def *chandef = &phy->mt76->chandef; + struct mt7615_dev *dev = phy->dev; + bool ext_phy = phy != &dev->phy; + enum mt76_dfs_state dfs_state, prev_state; + int err; + + if (is_mt7663(&dev->mt76)) + return 0; + + prev_state = phy->mt76->dfs_state; + dfs_state = mt76_phy_dfs_state(phy->mt76); + if ((chandef->chan->flags & IEEE80211_CHAN_RADAR) && + dfs_state < MT_DFS_STATE_CAC) + dfs_state = MT_DFS_STATE_ACTIVE; + + if (prev_state == dfs_state) + return 0; + + if (dfs_state == MT_DFS_STATE_DISABLED) + goto stop; + + if (prev_state <= MT_DFS_STATE_DISABLED) { + err = mt7615_dfs_init_radar_specs(phy); + if (err < 0) + return err; + + err = mt7615_dfs_start_radar_detector(phy); + if (err < 0) + return err; + + phy->mt76->dfs_state = MT_DFS_STATE_CAC; + } + + if (dfs_state == MT_DFS_STATE_CAC) + return 0; + + err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_CAC_END, + ext_phy, MT_RX_SEL0, 0); + if (err < 0) { + phy->mt76->dfs_state = MT_DFS_STATE_UNKNOWN; + return err; + } + + phy->mt76->dfs_state = MT_DFS_STATE_ACTIVE; + return 0; + +stop: + err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_NORMAL_START, ext_phy, + MT_RX_SEL0, 0); + if (err < 0) + return err; + + mt7615_dfs_stop_radar_detector(phy); + phy->mt76->dfs_state = MT_DFS_STATE_DISABLED; + + return 0; +} + +int mt7615_mac_set_beacon_filter(struct mt7615_phy *phy, + struct ieee80211_vif *vif, + bool enable) +{ + struct mt7615_dev *dev = phy->dev; + bool ext_phy = phy != &dev->phy; + int err; + + if (!mt7615_firmware_offload(dev)) + return -EOPNOTSUPP; + + switch (vif->type) { + case NL80211_IFTYPE_MONITOR: + return 0; + case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_AP: + if (enable) + phy->n_beacon_vif++; + else + phy->n_beacon_vif--; + fallthrough; + default: + break; + } + + err = mt7615_mcu_set_bss_pm(dev, vif, !phy->n_beacon_vif); + if (err) + return err; + + if (phy->n_beacon_vif) { + vif->driver_flags &= ~IEEE80211_VIF_BEACON_FILTER; + mt76_clear(dev, MT_WF_RFCR(ext_phy), + MT_WF_RFCR_DROP_OTHER_BEACON); + } else { + vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER; + mt76_set(dev, MT_WF_RFCR(ext_phy), + MT_WF_RFCR_DROP_OTHER_BEACON); + } + + return 0; +} + +void mt7615_coredump_work(struct work_struct *work) +{ + struct mt7615_dev *dev; + char *dump, *data; + + dev = (struct mt7615_dev *)container_of(work, struct mt7615_dev, + coredump.work.work); + + if (time_is_after_jiffies(dev->coredump.last_activity + + 4 * MT76_CONNAC_COREDUMP_TIMEOUT)) { + queue_delayed_work(dev->mt76.wq, &dev->coredump.work, + MT76_CONNAC_COREDUMP_TIMEOUT); + return; + } + + dump = vzalloc(MT76_CONNAC_COREDUMP_SZ); + data = dump; + + while (true) { + struct sk_buff *skb; + + spin_lock_bh(&dev->mt76.lock); + skb = __skb_dequeue(&dev->coredump.msg_list); + spin_unlock_bh(&dev->mt76.lock); + + if (!skb) + break; + + skb_pull(skb, sizeof(struct mt7615_mcu_rxd)); + if (!dump || data + skb->len - dump > MT76_CONNAC_COREDUMP_SZ) { + dev_kfree_skb(skb); + continue; + } + + memcpy(data, skb->data, skb->len); + data += skb->len; + + dev_kfree_skb(skb); + } + + if (dump) + dev_coredumpv(dev->mt76.dev, dump, MT76_CONNAC_COREDUMP_SZ, + GFP_KERNEL); +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mac.h b/drivers/net/wireless/mediatek/mt76/mt7615/mac.h new file mode 100644 index 000000000..336ebce5d --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7615/mac.h @@ -0,0 +1,325 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* Copyright (C) 2019 MediaTek Inc. */ + +#ifndef __MT7615_MAC_H +#define __MT7615_MAC_H + +#define MT_CT_PARSE_LEN 72 +#define MT_CT_DMA_BUF_NUM 2 + +#define MT_RXD0_LENGTH GENMASK(15, 0) +#define MT_RXD0_PKT_FLAG GENMASK(19, 16) +#define MT_RXD0_PKT_TYPE GENMASK(31, 29) + +#define MT_RXD0_NORMAL_ETH_TYPE_OFS GENMASK(22, 16) +#define MT_RXD0_NORMAL_IP_SUM BIT(23) +#define MT_RXD0_NORMAL_UDP_TCP_SUM BIT(24) +#define MT_RXD0_NORMAL_GROUP_1 BIT(25) +#define MT_RXD0_NORMAL_GROUP_2 BIT(26) +#define MT_RXD0_NORMAL_GROUP_3 BIT(27) +#define MT_RXD0_NORMAL_GROUP_4 BIT(28) + +#define MT_RXD1_NORMAL_BSSID GENMASK(31, 26) +#define MT_RXD1_NORMAL_PAYLOAD_FORMAT GENMASK(25, 24) +#define MT_RXD1_FIRST_AMSDU_FRAME GENMASK(1, 0) +#define MT_RXD1_MID_AMSDU_FRAME BIT(1) +#define MT_RXD1_LAST_AMSDU_FRAME BIT(0) +#define MT_RXD1_NORMAL_HDR_TRANS BIT(23) +#define MT_RXD1_NORMAL_HDR_OFFSET BIT(22) +#define MT_RXD1_NORMAL_MAC_HDR_LEN GENMASK(21, 16) +#define MT_RXD1_NORMAL_CH_FREQ GENMASK(15, 8) +#define MT_RXD1_NORMAL_KEY_ID GENMASK(7, 6) +#define MT_RXD1_NORMAL_BEACON_UC BIT(5) +#define MT_RXD1_NORMAL_BEACON_MC BIT(4) +#define MT_RXD1_NORMAL_BF_REPORT BIT(3) +#define MT_RXD1_NORMAL_ADDR_TYPE GENMASK(2, 1) +#define MT_RXD1_NORMAL_BCAST GENMASK(2, 1) +#define MT_RXD1_NORMAL_MCAST BIT(2) +#define MT_RXD1_NORMAL_U2M BIT(1) +#define MT_RXD1_NORMAL_HTC_VLD BIT(0) + +#define MT_RXD2_NORMAL_NON_AMPDU BIT(31) +#define MT_RXD2_NORMAL_NON_AMPDU_SUB BIT(30) +#define MT_RXD2_NORMAL_NDATA BIT(29) +#define MT_RXD2_NORMAL_NULL_FRAME BIT(28) +#define MT_RXD2_NORMAL_FRAG BIT(27) +#define MT_RXD2_NORMAL_INT_FRAME BIT(26) +#define MT_RXD2_NORMAL_HDR_TRANS_ERROR BIT(25) +#define MT_RXD2_NORMAL_MAX_LEN_ERROR BIT(24) +#define MT_RXD2_NORMAL_AMSDU_ERR BIT(23) +#define MT_RXD2_NORMAL_LEN_MISMATCH BIT(22) +#define MT_RXD2_NORMAL_TKIP_MIC_ERR BIT(21) +#define MT_RXD2_NORMAL_ICV_ERR BIT(20) +#define MT_RXD2_NORMAL_CLM BIT(19) +#define MT_RXD2_NORMAL_CM BIT(18) +#define MT_RXD2_NORMAL_FCS_ERR BIT(17) +#define MT_RXD2_NORMAL_SW_BIT BIT(16) +#define MT_RXD2_NORMAL_SEC_MODE GENMASK(15, 12) +#define MT_RXD2_NORMAL_TID GENMASK(11, 8) +#define MT_RXD2_NORMAL_WLAN_IDX GENMASK(7, 0) + +#define MT_RXD3_NORMAL_PF_STS GENMASK(31, 30) +#define MT_RXD3_NORMAL_PF_MODE BIT(29) +#define MT_RXD3_NORMAL_CLS_BITMAP GENMASK(28, 19) +#define MT_RXD3_NORMAL_WOL GENMASK(18, 14) +#define MT_RXD3_NORMAL_MAGIC_PKT BIT(13) +#define MT_RXD3_NORMAL_OFLD GENMASK(12, 11) +#define MT_RXD3_NORMAL_CLS BIT(10) +#define MT_RXD3_NORMAL_PATTERN_DROP BIT(9) +#define MT_RXD3_NORMAL_TSF_COMPARE_LOSS BIT(8) +#define MT_RXD3_NORMAL_RXV_SEQ GENMASK(7, 0) + +#define MT_RXD4_FRAME_CONTROL GENMASK(15, 0) + +#define MT_RXD6_SEQ_CTRL GENMASK(15, 0) +#define MT_RXD6_QOS_CTL GENMASK(31, 16) + +#define MT_RXD7_HT_CONTROL GENMASK(31, 0) + +#define MT_RXV1_ACID_DET_H BIT(31) +#define MT_RXV1_ACID_DET_L BIT(30) +#define MT_RXV1_VHTA2_B8_B3 GENMASK(29, 24) +#define MT_RXV1_NUM_RX GENMASK(23, 22) +#define MT_RXV1_HT_NO_SOUND BIT(21) +#define MT_RXV1_HT_SMOOTH BIT(20) +#define MT_RXV1_HT_SHORT_GI BIT(19) +#define MT_RXV1_HT_AGGR BIT(18) +#define MT_RXV1_VHTA1_B22 BIT(17) +#define MT_RXV1_FRAME_MODE GENMASK(16, 15) +#define MT_RXV1_TX_MODE GENMASK(14, 12) +#define MT_RXV1_HT_EXT_LTF GENMASK(11, 10) +#define MT_RXV1_HT_AD_CODE BIT(9) +#define MT_RXV1_HT_STBC GENMASK(8, 7) +#define MT_RXV1_TX_RATE GENMASK(6, 0) + +#define MT_RXV2_SEL_ANT BIT(31) +#define MT_RXV2_VALID_BIT BIT(30) +#define MT_RXV2_NSTS GENMASK(29, 27) +#define MT_RXV2_GROUP_ID GENMASK(26, 21) +#define MT_RXV2_LENGTH GENMASK(20, 0) + +#define MT_RXV3_WB_RSSI GENMASK(31, 24) +#define MT_RXV3_IB_RSSI GENMASK(23, 16) + +#define MT_RXV4_RCPI3 GENMASK(31, 24) +#define MT_RXV4_RCPI2 GENMASK(23, 16) +#define MT_RXV4_RCPI1 GENMASK(15, 8) +#define MT_RXV4_RCPI0 GENMASK(7, 0) + +#define MT_RXV5_FOE GENMASK(11, 0) + +#define MT_RXV6_NF3 GENMASK(31, 24) +#define MT_RXV6_NF2 GENMASK(23, 16) +#define MT_RXV6_NF1 GENMASK(15, 8) +#define MT_RXV6_NF0 GENMASK(7, 0) + +enum tx_header_format { + MT_HDR_FORMAT_802_3, + MT_HDR_FORMAT_CMD, + MT_HDR_FORMAT_802_11, + MT_HDR_FORMAT_802_11_EXT, +}; + +enum tx_pkt_type { + MT_TX_TYPE_CT, + MT_TX_TYPE_SF, + MT_TX_TYPE_CMD, + MT_TX_TYPE_FW, +}; + +enum tx_port_idx { + MT_TX_PORT_IDX_LMAC, + MT_TX_PORT_IDX_MCU +}; + +enum tx_mcu_port_q_idx { + MT_TX_MCU_PORT_RX_Q0 = 0, + MT_TX_MCU_PORT_RX_Q1, + MT_TX_MCU_PORT_RX_Q2, + MT_TX_MCU_PORT_RX_Q3, + MT_TX_MCU_PORT_RX_FWDL = 0x1e +}; + +enum tx_phy_bandwidth { + MT_PHY_BW_20, + MT_PHY_BW_40, + MT_PHY_BW_80, + MT_PHY_BW_160, +}; + +#define MT_CT_INFO_APPLY_TXD BIT(0) +#define MT_CT_INFO_COPY_HOST_TXD_ALL BIT(1) +#define MT_CT_INFO_MGMT_FRAME BIT(2) +#define MT_CT_INFO_NONE_CIPHER_FRAME BIT(3) +#define MT_CT_INFO_HSR2_TX BIT(4) + +#define MT_TXD0_P_IDX BIT(31) +#define MT_TXD0_Q_IDX GENMASK(30, 26) +#define MT_TXD0_UDP_TCP_SUM BIT(24) +#define MT_TXD0_IP_SUM BIT(23) +#define MT_TXD0_ETH_TYPE_OFFSET GENMASK(22, 16) +#define MT_TXD0_TX_BYTES GENMASK(15, 0) + +#define MT_TXD1_OWN_MAC GENMASK(31, 26) +#define MT_TXD1_PKT_FMT GENMASK(25, 24) +#define MT_TXD1_TID GENMASK(23, 21) +#define MT_TXD1_AMSDU BIT(20) +#define MT_TXD1_UNXV BIT(19) +#define MT_TXD1_HDR_PAD GENMASK(18, 17) +#define MT_TXD1_TXD_LEN BIT(16) +#define MT_TXD1_LONG_FORMAT BIT(15) +#define MT_TXD1_HDR_FORMAT GENMASK(14, 13) +#define MT_TXD1_HDR_INFO GENMASK(12, 8) +#define MT_TXD1_WLAN_IDX GENMASK(7, 0) + +#define MT_TXD2_FIX_RATE BIT(31) +#define MT_TXD2_TIMING_MEASURE BIT(30) +#define MT_TXD2_BA_DISABLE BIT(29) +#define MT_TXD2_POWER_OFFSET GENMASK(28, 24) +#define MT_TXD2_MAX_TX_TIME GENMASK(23, 16) +#define MT_TXD2_FRAG GENMASK(15, 14) +#define MT_TXD2_HTC_VLD BIT(13) +#define MT_TXD2_DURATION BIT(12) +#define MT_TXD2_BIP BIT(11) +#define MT_TXD2_MULTICAST BIT(10) +#define MT_TXD2_RTS BIT(9) +#define MT_TXD2_SOUNDING BIT(8) +#define MT_TXD2_NDPA BIT(7) +#define MT_TXD2_NDP BIT(6) +#define MT_TXD2_FRAME_TYPE GENMASK(5, 4) +#define MT_TXD2_SUB_TYPE GENMASK(3, 0) + +#define MT_TXD3_SN_VALID BIT(31) +#define MT_TXD3_PN_VALID BIT(30) +#define MT_TXD3_SEQ GENMASK(27, 16) +#define MT_TXD3_REM_TX_COUNT GENMASK(15, 11) +#define MT_TXD3_TX_COUNT GENMASK(10, 6) +#define MT_TXD3_PROTECT_FRAME BIT(1) +#define MT_TXD3_NO_ACK BIT(0) + +#define MT_TXD4_PN_LOW GENMASK(31, 0) + +#define MT_TXD5_PN_HIGH GENMASK(31, 16) +#define MT_TXD5_SW_POWER_MGMT BIT(13) +#define MT_TXD5_DA_SELECT BIT(11) +#define MT_TXD5_TX_STATUS_HOST BIT(10) +#define MT_TXD5_TX_STATUS_MCU BIT(9) +#define MT_TXD5_TX_STATUS_FMT BIT(8) +#define MT_TXD5_PID GENMASK(7, 0) + +#define MT_TXD6_FIXED_RATE BIT(31) +#define MT_TXD6_SGI BIT(30) +#define MT_TXD6_LDPC BIT(29) +#define MT_TXD6_TX_BF BIT(28) +#define MT_TXD6_TX_RATE GENMASK(27, 16) +#define MT_TXD6_ANT_ID GENMASK(15, 4) +#define MT_TXD6_DYN_BW BIT(3) +#define MT_TXD6_FIXED_BW BIT(2) +#define MT_TXD6_BW GENMASK(1, 0) + +/* MT7663 DW7 HW-AMSDU */ +#define MT_TXD7_HW_AMSDU_CAP BIT(30) +#define MT_TXD7_TYPE GENMASK(21, 20) +#define MT_TXD7_SUB_TYPE GENMASK(19, 16) +#define MT_TXD7_SPE_IDX GENMASK(15, 11) +#define MT_TXD7_SPE_IDX_SLE BIT(10) + +#define MT_TXD8_L_TYPE GENMASK(5, 4) +#define MT_TXD8_L_SUB_TYPE GENMASK(3, 0) + +#define MT_TX_RATE_STBC BIT(11) +#define MT_TX_RATE_NSS GENMASK(10, 9) +#define MT_TX_RATE_MODE GENMASK(8, 6) +#define MT_TX_RATE_IDX GENMASK(5, 0) + +#define MT_TX_FREE_MSDU_ID_CNT GENMASK(6, 0) + +#define MT_TXS0_PID GENMASK(31, 24) +#define MT_TXS0_BA_ERROR BIT(22) +#define MT_TXS0_PS_FLAG BIT(21) +#define MT_TXS0_TXOP_TIMEOUT BIT(20) +#define MT_TXS0_BIP_ERROR BIT(19) + +#define MT_TXS0_QUEUE_TIMEOUT BIT(18) +#define MT_TXS0_RTS_TIMEOUT BIT(17) +#define MT_TXS0_ACK_TIMEOUT BIT(16) +#define MT_TXS0_ACK_ERROR_MASK GENMASK(18, 16) + +#define MT_TXS0_TX_STATUS_HOST BIT(15) +#define MT_TXS0_TX_STATUS_MCU BIT(14) +#define MT_TXS0_TXS_FORMAT BIT(13) +#define MT_TXS0_FIXED_RATE BIT(12) +#define MT_TXS0_TX_RATE GENMASK(11, 0) + +#define MT_TXS1_ANT_ID GENMASK(31, 20) +#define MT_TXS1_RESP_RATE GENMASK(19, 16) +#define MT_TXS1_BW GENMASK(15, 14) +#define MT_TXS1_I_TXBF BIT(13) +#define MT_TXS1_E_TXBF BIT(12) +#define MT_TXS1_TID GENMASK(11, 9) +#define MT_TXS1_AMPDU BIT(8) +#define MT_TXS1_ACKED_MPDU BIT(7) +#define MT_TXS1_TX_POWER_DBM GENMASK(6, 0) + +#define MT_TXS2_WCID GENMASK(31, 24) +#define MT_TXS2_RXV_SEQNO GENMASK(23, 16) +#define MT_TXS2_TX_DELAY GENMASK(15, 0) + +#define MT_TXS3_LAST_TX_RATE GENMASK(31, 29) +#define MT_TXS3_TX_COUNT GENMASK(28, 24) +#define MT_TXS3_F1_TSSI1 GENMASK(23, 12) +#define MT_TXS3_F1_TSSI0 GENMASK(11, 0) +#define MT_TXS3_F0_SEQNO GENMASK(11, 0) + +#define MT_TXS4_F0_TIMESTAMP GENMASK(31, 0) +#define MT_TXS4_F1_TSSI3 GENMASK(23, 12) +#define MT_TXS4_F1_TSSI2 GENMASK(11, 0) + +#define MT_TXS5_F0_FRONT_TIME GENMASK(24, 0) +#define MT_TXS5_F1_NOISE_2 GENMASK(23, 16) +#define MT_TXS5_F1_NOISE_1 GENMASK(15, 8) +#define MT_TXS5_F1_NOISE_0 GENMASK(7, 0) + +#define MT_TXS6_F1_RCPI_3 GENMASK(31, 24) +#define MT_TXS6_F1_RCPI_2 GENMASK(23, 16) +#define MT_TXS6_F1_RCPI_1 GENMASK(15, 8) +#define MT_TXS6_F1_RCPI_0 GENMASK(7, 0) + +struct mt7615_dfs_pulse { + u32 max_width; /* us */ + int max_pwr; /* dbm */ + int min_pwr; /* dbm */ + u32 min_stgr_pri; /* us */ + u32 max_stgr_pri; /* us */ + u32 min_cr_pri; /* us */ + u32 max_cr_pri; /* us */ +}; + +struct mt7615_dfs_pattern { + u8 enb; + u8 stgr; + u8 min_crpn; + u8 max_crpn; + u8 min_crpr; + u8 min_pw; + u8 max_pw; + u32 min_pri; + u32 max_pri; + u8 min_crbn; + u8 max_crbn; + u8 min_stgpn; + u8 max_stgpn; + u8 min_stgpr; +}; + +struct mt7615_dfs_radar_spec { + struct mt7615_dfs_pulse pulse_th; + struct mt7615_dfs_pattern radar_pattern[16]; +}; + +static inline u32 mt7615_mac_wtbl_addr(struct mt7615_dev *dev, int wcid) +{ + return MT_WTBL_BASE(dev) + wcid * MT_WTBL_ENTRY_SIZE; +} + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/main.c b/drivers/net/wireless/mediatek/mt76/mt7615/main.c new file mode 100644 index 000000000..67f56e428 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7615/main.c @@ -0,0 +1,1374 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2019 MediaTek Inc. + * + * Author: Roy Luo + * Ryder Lee + * Felix Fietkau + * Lorenzo Bianconi + */ + +#include +#include +#include "mt7615.h" +#include "mcu.h" + +static bool mt7615_dev_running(struct mt7615_dev *dev) +{ + struct mt7615_phy *phy; + + if (test_bit(MT76_STATE_RUNNING, &dev->mphy.state)) + return true; + + phy = mt7615_ext_phy(dev); + + return phy && test_bit(MT76_STATE_RUNNING, &phy->mt76->state); +} + +static int mt7615_start(struct ieee80211_hw *hw) +{ + struct mt7615_dev *dev = mt7615_hw_dev(hw); + struct mt7615_phy *phy = mt7615_hw_phy(hw); + unsigned long timeout; + bool running; + int ret; + + if (!mt7615_wait_for_mcu_init(dev)) + return -EIO; + + mt7615_mutex_acquire(dev); + + running = mt7615_dev_running(dev); + + if (!running) { + ret = mt7615_mcu_set_pm(dev, 0, 0); + if (ret) + goto out; + + ret = mt76_connac_mcu_set_mac_enable(&dev->mt76, 0, true, false); + if (ret) + goto out; + + mt7615_mac_enable_nf(dev, 0); + } + + if (phy != &dev->phy) { + ret = mt7615_mcu_set_pm(dev, 1, 0); + if (ret) + goto out; + + ret = mt76_connac_mcu_set_mac_enable(&dev->mt76, 1, true, false); + if (ret) + goto out; + + mt7615_mac_enable_nf(dev, 1); + } + + if (mt7615_firmware_offload(dev)) { + ret = mt76_connac_mcu_set_channel_domain(phy->mt76); + if (ret) + goto out; + + ret = mt76_connac_mcu_set_rate_txpower(phy->mt76); + if (ret) + goto out; + } + + ret = mt7615_mcu_set_chan_info(phy, MCU_EXT_CMD(SET_RX_PATH)); + if (ret) + goto out; + + set_bit(MT76_STATE_RUNNING, &phy->mt76->state); + + timeout = mt7615_get_macwork_timeout(dev); + ieee80211_queue_delayed_work(hw, &phy->mt76->mac_work, timeout); + + if (!running) + mt7615_mac_reset_counters(phy); + +out: + mt7615_mutex_release(dev); + + return ret; +} + +static void mt7615_stop(struct ieee80211_hw *hw, bool suspend) +{ + struct mt7615_dev *dev = mt7615_hw_dev(hw); + struct mt7615_phy *phy = mt7615_hw_phy(hw); + + cancel_delayed_work_sync(&phy->mt76->mac_work); + timer_delete_sync(&phy->roc_timer); + cancel_work_sync(&phy->roc_work); + + cancel_delayed_work_sync(&dev->pm.ps_work); + cancel_work_sync(&dev->pm.wake_work); + + mt76_connac_free_pending_tx_skbs(&dev->pm, NULL); + + mt7615_mutex_acquire(dev); + + mt76_testmode_reset(phy->mt76, true); + + clear_bit(MT76_STATE_RUNNING, &phy->mt76->state); + cancel_delayed_work_sync(&phy->scan_work); + + if (phy != &dev->phy) { + mt7615_mcu_set_pm(dev, 1, 1); + mt76_connac_mcu_set_mac_enable(&dev->mt76, 1, false, false); + } + + if (!mt7615_dev_running(dev)) { + mt7615_mcu_set_pm(dev, 0, 1); + mt76_connac_mcu_set_mac_enable(&dev->mt76, 0, false, false); + } + + mt7615_mutex_release(dev); +} + +static inline int get_free_idx(u32 mask, u8 start, u8 end) +{ + return ffs(~mask & GENMASK(end, start)); +} + +static int get_omac_idx(enum nl80211_iftype type, u64 mask) +{ + int i; + + switch (type) { + case NL80211_IFTYPE_STATION: + /* prefer hw bssid slot 1-3 */ + i = get_free_idx(mask, HW_BSSID_1, HW_BSSID_3); + if (i) + return i - 1; + + /* next, try to find a free repeater entry for the sta */ + i = get_free_idx(mask >> REPEATER_BSSID_START, 0, + REPEATER_BSSID_MAX - REPEATER_BSSID_START); + if (i) + return i + 32 - 1; + + i = get_free_idx(mask, EXT_BSSID_1, EXT_BSSID_MAX); + if (i) + return i - 1; + + if (~mask & BIT(HW_BSSID_0)) + return HW_BSSID_0; + + break; + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_MONITOR: + case NL80211_IFTYPE_AP: + /* ap uses hw bssid 0 and ext bssid */ + if (~mask & BIT(HW_BSSID_0)) + return HW_BSSID_0; + + i = get_free_idx(mask, EXT_BSSID_1, EXT_BSSID_MAX); + if (i) + return i - 1; + + break; + default: + WARN_ON(1); + break; + } + + return -1; +} + +static int mt7615_add_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv; + struct mt7615_dev *dev = mt7615_hw_dev(hw); + struct mt7615_phy *phy = mt7615_hw_phy(hw); + struct mt76_txq *mtxq; + bool ext_phy = phy != &dev->phy; + int idx, ret = 0; + + mt7615_mutex_acquire(dev); + + mt76_testmode_reset(phy->mt76, true); + + if (vif->type == NL80211_IFTYPE_MONITOR && + is_zero_ether_addr(vif->addr)) + phy->monitor_vif = vif; + + mvif->mt76.idx = __ffs64(~dev->mt76.vif_mask); + if (mvif->mt76.idx >= MT7615_MAX_INTERFACES) { + ret = -ENOSPC; + goto out; + } + + idx = get_omac_idx(vif->type, dev->omac_mask); + if (idx < 0) { + ret = -ENOSPC; + goto out; + } + mvif->mt76.omac_idx = idx; + + mvif->mt76.band_idx = ext_phy; + mvif->mt76.wmm_idx = vif->type != NL80211_IFTYPE_AP; + mvif->mt76.wcid = &mvif->sta.wcid; + if (ext_phy) + mvif->mt76.wmm_idx += 2; + + dev->mt76.vif_mask |= BIT_ULL(mvif->mt76.idx); + dev->omac_mask |= BIT_ULL(mvif->mt76.omac_idx); + phy->omac_mask |= BIT_ULL(mvif->mt76.omac_idx); + + ret = mt7615_mcu_set_dbdc(dev); + if (ret) + goto out; + + idx = MT7615_WTBL_RESERVED - mvif->mt76.idx; + + INIT_LIST_HEAD(&mvif->sta.wcid.poll_list); + mvif->sta.wcid.idx = idx; + mt76_wcid_init(&mvif->sta.wcid, mvif->mt76.band_idx); + + mt7615_mac_wtbl_update(dev, idx, + MT_WTBL_UPDATE_ADM_COUNT_CLEAR); + + rcu_assign_pointer(dev->mt76.wcid[idx], &mvif->sta.wcid); + if (vif->txq) { + mtxq = (struct mt76_txq *)vif->txq->drv_priv; + mtxq->wcid = idx; + } + + ret = mt7615_mcu_add_dev_info(phy, vif, true); +out: + mt7615_mutex_release(dev); + + return ret; +} + +static void mt7615_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv; + struct mt7615_sta *msta = &mvif->sta; + struct mt7615_dev *dev = mt7615_hw_dev(hw); + struct mt7615_phy *phy = mt7615_hw_phy(hw); + int idx = msta->wcid.idx; + + mt7615_mutex_acquire(dev); + + mt7615_mcu_add_bss_info(phy, vif, NULL, false); + mt7615_mcu_sta_add(phy, vif, NULL, false); + + mt76_testmode_reset(phy->mt76, true); + if (vif == phy->monitor_vif) + phy->monitor_vif = NULL; + + mt76_connac_free_pending_tx_skbs(&dev->pm, &msta->wcid); + + mt7615_mcu_add_dev_info(phy, vif, false); + + rcu_assign_pointer(dev->mt76.wcid[idx], NULL); + + dev->mt76.vif_mask &= ~BIT_ULL(mvif->mt76.idx); + dev->omac_mask &= ~BIT_ULL(mvif->mt76.omac_idx); + phy->omac_mask &= ~BIT_ULL(mvif->mt76.omac_idx); + + mt7615_mutex_release(dev); + + spin_lock_bh(&dev->mt76.sta_poll_lock); + if (!list_empty(&msta->wcid.poll_list)) + list_del_init(&msta->wcid.poll_list); + spin_unlock_bh(&dev->mt76.sta_poll_lock); + + mt76_wcid_cleanup(&dev->mt76, &mvif->sta.wcid); +} + +int mt7615_set_channel(struct mt76_phy *mphy) +{ + struct mt7615_phy *phy = mphy->priv; + struct mt7615_dev *dev = phy->dev; + bool ext_phy = phy != &dev->phy; + int ret; + + mt76_connac_pm_wake(mphy, &dev->pm); + + if (is_mt7615(&dev->mt76) && dev->flash_eeprom) { + ret = mt7615_mcu_apply_rx_dcoc(phy); + if (ret) + goto out; + + ret = mt7615_mcu_apply_tx_dpd(phy); + if (ret) + goto out; + } + + ret = mt7615_mcu_set_chan_info(phy, MCU_EXT_CMD(CHANNEL_SWITCH)); + if (ret) + goto out; + + mt7615_mac_set_timing(phy); + ret = mt7615_dfs_init_radar_detector(phy); + if (ret) + goto out; + + mt7615_mac_cca_stats_reset(phy); + ret = mt7615_mcu_set_sku_en(phy, true); + if (ret) + goto out; + + mt7615_mac_reset_counters(phy); + phy->noise = 0; + phy->chfreq = mt76_rr(dev, MT_CHFREQ(ext_phy)); + +out: + mt76_connac_power_save_sched(mphy, &dev->pm); + + if (!mt76_testmode_enabled(phy->mt76)) { + unsigned long timeout = mt7615_get_macwork_timeout(dev); + + ieee80211_queue_delayed_work(phy->mt76->hw, + &phy->mt76->mac_work, timeout); + } + + return ret; +} +EXPORT_SYMBOL_GPL(mt7615_set_channel); + +static int mt7615_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct mt7615_dev *dev = mt7615_hw_dev(hw); + struct mt7615_phy *phy = mt7615_hw_phy(hw); + struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv; + struct mt7615_sta *msta = sta ? (struct mt7615_sta *)sta->drv_priv : + &mvif->sta; + struct mt76_wcid *wcid = &msta->wcid; + int idx = key->keyidx, err = 0; + u8 *wcid_keyidx = &wcid->hw_key_idx; + + /* The hardware does not support per-STA RX GTK, fallback + * to software mode for these. + */ + if ((vif->type == NL80211_IFTYPE_ADHOC || + vif->type == NL80211_IFTYPE_MESH_POINT) && + (key->cipher == WLAN_CIPHER_SUITE_TKIP || + key->cipher == WLAN_CIPHER_SUITE_CCMP) && + !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) + return -EOPNOTSUPP; + + /* fall back to sw encryption for unsupported ciphers */ + switch (key->cipher) { + case WLAN_CIPHER_SUITE_AES_CMAC: + wcid_keyidx = &wcid->hw_key_idx2; + key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIE; + break; + case WLAN_CIPHER_SUITE_TKIP: + case WLAN_CIPHER_SUITE_CCMP: + case WLAN_CIPHER_SUITE_CCMP_256: + case WLAN_CIPHER_SUITE_GCMP: + case WLAN_CIPHER_SUITE_GCMP_256: + case WLAN_CIPHER_SUITE_SMS4: + break; + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + default: + return -EOPNOTSUPP; + } + + mt7615_mutex_acquire(dev); + + if (cmd == SET_KEY && !sta && !mvif->mt76.cipher) { + mvif->mt76.cipher = mt76_connac_mcu_get_cipher(key->cipher); + mt7615_mcu_add_bss_info(phy, vif, NULL, true); + } + + if (cmd == SET_KEY) + *wcid_keyidx = idx; + else { + if (idx == *wcid_keyidx) + *wcid_keyidx = -1; + goto out; + } + + mt76_wcid_key_setup(&dev->mt76, wcid, key); + if (mt76_is_mmio(&dev->mt76)) + err = mt7615_mac_wtbl_set_key(dev, wcid, key); + else + err = __mt7615_mac_wtbl_set_key(dev, wcid, key); + +out: + mt7615_mutex_release(dev); + + return err; +} + +static int mt7615_set_sar_specs(struct ieee80211_hw *hw, + const struct cfg80211_sar_specs *sar) +{ + struct mt7615_phy *phy = mt7615_hw_phy(hw); + int err; + + if (!cfg80211_chandef_valid(&phy->mt76->chandef)) + return -EINVAL; + + err = mt76_init_sar_power(hw, sar); + if (err) + return err; + + if (mt7615_firmware_offload(phy->dev)) + return mt76_connac_mcu_set_rate_txpower(phy->mt76); + + return mt76_update_channel(phy->mt76); +} + +static int mt7615_config(struct ieee80211_hw *hw, int radio_idx, u32 changed) +{ + struct mt7615_dev *dev = mt7615_hw_dev(hw); + struct mt7615_phy *phy = mt7615_hw_phy(hw); + bool band = phy != &dev->phy; + int ret = 0; + + if (changed & (IEEE80211_CONF_CHANGE_CHANNEL | + IEEE80211_CONF_CHANGE_POWER)) { +#ifdef CONFIG_NL80211_TESTMODE + if (phy->mt76->test.state != MT76_TM_STATE_OFF) { + mt7615_mutex_acquire(dev); + mt76_testmode_reset(phy->mt76, false); + mt7615_mutex_release(dev); + } +#endif + ret = mt76_update_channel(phy->mt76); + } + + mt7615_mutex_acquire(dev); + + if (changed & IEEE80211_CONF_CHANGE_MONITOR) { + mt76_testmode_reset(phy->mt76, true); + + if (!(hw->conf.flags & IEEE80211_CONF_MONITOR)) + phy->rxfilter |= MT_WF_RFCR_DROP_OTHER_UC; + else + phy->rxfilter &= ~MT_WF_RFCR_DROP_OTHER_UC; + + mt76_wr(dev, MT_WF_RFCR(band), phy->rxfilter); + } + + mt7615_mutex_release(dev); + + return ret; +} + +static int +mt7615_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + unsigned int link_id, u16 queue, + const struct ieee80211_tx_queue_params *params) +{ + struct mt76_vif_link *mvif = (struct mt76_vif_link *)vif->drv_priv; + struct mt7615_dev *dev = mt7615_hw_dev(hw); + int err; + + mt7615_mutex_acquire(dev); + + queue = mt7615_lmac_mapping(dev, queue); + queue += mvif->wmm_idx * MT7615_MAX_WMM_SETS; + err = mt7615_mcu_set_wmm(dev, queue, params); + + mt7615_mutex_release(dev); + + return err; +} + +static void mt7615_configure_filter(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *total_flags, + u64 multicast) +{ + struct mt7615_dev *dev = mt7615_hw_dev(hw); + struct mt7615_phy *phy = mt7615_hw_phy(hw); + bool band = phy != &dev->phy; + + u32 ctl_flags = MT_WF_RFCR1_DROP_ACK | + MT_WF_RFCR1_DROP_BF_POLL | + MT_WF_RFCR1_DROP_BA | + MT_WF_RFCR1_DROP_CFEND | + MT_WF_RFCR1_DROP_CFACK; + u32 flags = 0; + + mt7615_mutex_acquire(dev); + +#define MT76_FILTER(_flag, _hw) do { \ + flags |= *total_flags & FIF_##_flag; \ + phy->rxfilter &= ~(_hw); \ + if (!mt76_testmode_enabled(phy->mt76)) \ + phy->rxfilter |= !(flags & FIF_##_flag) * (_hw);\ + } while (0) + + phy->rxfilter &= ~(MT_WF_RFCR_DROP_OTHER_BSS | + MT_WF_RFCR_DROP_FRAME_REPORT | + MT_WF_RFCR_DROP_PROBEREQ | + MT_WF_RFCR_DROP_MCAST_FILTERED | + MT_WF_RFCR_DROP_MCAST | + MT_WF_RFCR_DROP_BCAST | + MT_WF_RFCR_DROP_DUPLICATE | + MT_WF_RFCR_DROP_A2_BSSID | + MT_WF_RFCR_DROP_UNWANTED_CTL | + MT_WF_RFCR_DROP_STBC_MULTI); + + if (phy->n_beacon_vif || !mt7615_firmware_offload(dev)) + phy->rxfilter &= ~MT_WF_RFCR_DROP_OTHER_BEACON; + + MT76_FILTER(OTHER_BSS, MT_WF_RFCR_DROP_OTHER_TIM | + MT_WF_RFCR_DROP_A3_MAC | + MT_WF_RFCR_DROP_A3_BSSID); + + MT76_FILTER(FCSFAIL, MT_WF_RFCR_DROP_FCSFAIL); + + MT76_FILTER(CONTROL, MT_WF_RFCR_DROP_CTS | + MT_WF_RFCR_DROP_RTS | + MT_WF_RFCR_DROP_CTL_RSV | + MT_WF_RFCR_DROP_NDPA); + + *total_flags = flags; + mt76_wr(dev, MT_WF_RFCR(band), phy->rxfilter); + + if (*total_flags & FIF_CONTROL) + mt76_clear(dev, MT_WF_RFCR1(band), ctl_flags); + else + mt76_set(dev, MT_WF_RFCR1(band), ctl_flags); + + mt7615_mutex_release(dev); +} + +static void +mt7615_update_mu_group(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info) +{ + struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv; + struct mt7615_dev *dev = mt7615_hw_dev(hw); + u8 i, band = mvif->mt76.band_idx; + u32 *mu; + + mu = (u32 *)info->mu_group.membership; + for (i = 0; i < WLAN_MEMBERSHIP_LEN / sizeof(*mu); i++) { + if (is_mt7663(&dev->mt76)) + mt76_wr(dev, MT7663_WF_PHY_GID_TAB_VLD(band, i), mu[i]); + else + mt76_wr(dev, MT_WF_PHY_GID_TAB_VLD(band, i), mu[i]); + } + + mu = (u32 *)info->mu_group.position; + for (i = 0; i < WLAN_USER_POSITION_LEN / sizeof(*mu); i++) { + if (is_mt7663(&dev->mt76)) + mt76_wr(dev, MT7663_WF_PHY_GID_TAB_POS(band, i), mu[i]); + else + mt76_wr(dev, MT_WF_PHY_GID_TAB_POS(band, i), mu[i]); + } +} + +static void mt7615_bss_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, + u64 changed) +{ + struct mt7615_dev *dev = mt7615_hw_dev(hw); + struct mt7615_phy *phy = mt7615_hw_phy(hw); + + mt7615_mutex_acquire(dev); + + if (changed & BSS_CHANGED_ERP_SLOT) { + int slottime = info->use_short_slot ? 9 : 20; + + if (slottime != phy->slottime) { + phy->slottime = slottime; + mt7615_mac_set_timing(phy); + } + } + + if (changed & BSS_CHANGED_BEACON_ENABLED && info->enable_beacon) { + mt7615_mcu_add_bss_info(phy, vif, NULL, true); + mt7615_mcu_sta_add(phy, vif, NULL, true); + + if (mt7615_firmware_offload(dev) && vif->p2p) + mt76_connac_mcu_set_p2p_oppps(hw, vif); + } + + if (changed & (BSS_CHANGED_BEACON | + BSS_CHANGED_BEACON_ENABLED)) + mt7615_mcu_add_beacon(dev, hw, vif, info->enable_beacon); + + if (changed & BSS_CHANGED_HT || changed & BSS_CHANGED_ERP_CTS_PROT) + mt7615_mcu_set_protection(phy, vif, info->ht_operation_mode, + info->use_cts_prot); + + if (changed & BSS_CHANGED_PS) + mt76_connac_mcu_set_vif_ps(&dev->mt76, vif); + + if ((changed & BSS_CHANGED_ARP_FILTER) && + mt7615_firmware_offload(dev)) { + struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv; + + mt76_connac_mcu_update_arp_filter(&dev->mt76, &mvif->mt76, + info); + } + + if (changed & BSS_CHANGED_ASSOC) + mt7615_mac_set_beacon_filter(phy, vif, vif->cfg.assoc); + + if (changed & BSS_CHANGED_MU_GROUPS) + mt7615_update_mu_group(hw, vif, info); + + mt7615_mutex_release(dev); +} + +static void +mt7615_channel_switch_beacon(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_chan_def *chandef) +{ + struct mt7615_dev *dev = mt7615_hw_dev(hw); + + mt7615_mutex_acquire(dev); + mt7615_mcu_add_beacon(dev, hw, vif, true); + mt7615_mutex_release(dev); +} + +int mt7615_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mt7615_dev *dev = container_of(mdev, struct mt7615_dev, mt76); + struct mt7615_sta *msta = (struct mt7615_sta *)sta->drv_priv; + struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv; + struct mt7615_phy *phy; + int idx, err; + + idx = mt76_wcid_alloc(dev->mt76.wcid_mask, MT7615_WTBL_STA - 1); + if (idx < 0) + return -ENOSPC; + + INIT_LIST_HEAD(&msta->wcid.poll_list); + msta->vif = mvif; + msta->wcid.sta = 1; + msta->wcid.idx = idx; + msta->wcid.phy_idx = mvif->mt76.band_idx; + + phy = mvif->mt76.band_idx ? mt7615_ext_phy(dev) : &dev->phy; + err = mt76_connac_pm_wake(phy->mt76, &dev->pm); + if (err) + return err; + + if (vif->type == NL80211_IFTYPE_STATION && !sta->tdls) { + err = mt7615_mcu_add_bss_info(phy, vif, sta, true); + if (err) + return err; + } + + mt7615_mac_wtbl_update(dev, idx, + MT_WTBL_UPDATE_ADM_COUNT_CLEAR); + err = mt7615_mcu_sta_add(&dev->phy, vif, sta, true); + if (err) + return err; + + mt76_connac_power_save_sched(phy->mt76, &dev->pm); + + return err; +} +EXPORT_SYMBOL_GPL(mt7615_mac_sta_add); + +void mt7615_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mt7615_dev *dev = container_of(mdev, struct mt7615_dev, mt76); + struct mt7615_sta *msta = (struct mt7615_sta *)sta->drv_priv; + struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv; + struct mt7615_phy *phy; + + mt76_connac_free_pending_tx_skbs(&dev->pm, &msta->wcid); + + phy = mvif->mt76.band_idx ? mt7615_ext_phy(dev) : &dev->phy; + mt76_connac_pm_wake(phy->mt76, &dev->pm); + + mt7615_mcu_sta_add(&dev->phy, vif, sta, false); + mt7615_mac_wtbl_update(dev, msta->wcid.idx, + MT_WTBL_UPDATE_ADM_COUNT_CLEAR); + if (vif->type == NL80211_IFTYPE_STATION && !sta->tdls) + mt7615_mcu_add_bss_info(phy, vif, sta, false); + + spin_lock_bh(&mdev->sta_poll_lock); + if (!list_empty(&msta->wcid.poll_list)) + list_del_init(&msta->wcid.poll_list); + spin_unlock_bh(&mdev->sta_poll_lock); + + mt76_connac_power_save_sched(phy->mt76, &dev->pm); +} +EXPORT_SYMBOL_GPL(mt7615_mac_sta_remove); + +static void mt7615_sta_rate_tbl_update(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mt7615_dev *dev = mt7615_hw_dev(hw); + struct mt7615_phy *phy = mt7615_hw_phy(hw); + struct mt7615_sta *msta = (struct mt7615_sta *)sta->drv_priv; + struct ieee80211_sta_rates *sta_rates = rcu_dereference(sta->rates); + int i; + + if (!sta_rates) + return; + + spin_lock_bh(&dev->mt76.lock); + for (i = 0; i < ARRAY_SIZE(msta->rates); i++) { + msta->rates[i].idx = sta_rates->rate[i].idx; + msta->rates[i].count = sta_rates->rate[i].count; + msta->rates[i].flags = sta_rates->rate[i].flags; + + if (msta->rates[i].idx < 0 || !msta->rates[i].count) + break; + } + msta->n_rates = i; + if (mt76_connac_pm_ref(phy->mt76, &dev->pm)) { + mt7615_mac_set_rates(phy, msta, NULL, msta->rates); + mt76_connac_pm_unref(phy->mt76, &dev->pm); + } + spin_unlock_bh(&dev->mt76.lock); +} + +void mt7615_tx_worker(struct mt76_worker *w) +{ + struct mt7615_dev *dev = container_of(w, struct mt7615_dev, + mt76.tx_worker); + + if (!mt76_connac_pm_ref(&dev->mphy, &dev->pm)) { + queue_work(dev->mt76.wq, &dev->pm.wake_work); + return; + } + + mt76_tx_worker_run(&dev->mt76); + mt76_connac_pm_unref(&dev->mphy, &dev->pm); +} + +static void mt7615_tx(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, + struct sk_buff *skb) +{ + struct mt7615_dev *dev = mt7615_hw_dev(hw); + struct mt76_phy *mphy = hw->priv; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_vif *vif = info->control.vif; + struct mt76_wcid *wcid = &dev->mt76.global_wcid; + struct mt7615_sta *msta = NULL; + int qid; + + if (control->sta) { + msta = (struct mt7615_sta *)control->sta->drv_priv; + wcid = &msta->wcid; + } + + if (vif && !control->sta) { + struct mt7615_vif *mvif; + + mvif = (struct mt7615_vif *)vif->drv_priv; + msta = &mvif->sta; + wcid = &msta->wcid; + } + + if (mt76_connac_pm_ref(mphy, &dev->pm)) { + mt76_tx(mphy, control->sta, wcid, skb); + mt76_connac_pm_unref(mphy, &dev->pm); + return; + } + + qid = skb_get_queue_mapping(skb); + if (qid >= MT_TXQ_PSD) { + qid = IEEE80211_AC_BE; + skb_set_queue_mapping(skb, qid); + } + + mt76_connac_pm_queue_skb(hw, &dev->pm, wcid, skb); +} + +static int mt7615_set_rts_threshold(struct ieee80211_hw *hw, int radio_idx, + u32 val) +{ + struct mt7615_dev *dev = mt7615_hw_dev(hw); + struct mt7615_phy *phy = mt7615_hw_phy(hw); + int err, band = phy != &dev->phy; + + mt7615_mutex_acquire(dev); + err = mt76_connac_mcu_set_rts_thresh(&dev->mt76, val, band); + mt7615_mutex_release(dev); + + return err; +} + +static int +mt7615_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_ampdu_params *params) +{ + enum ieee80211_ampdu_mlme_action action = params->action; + struct mt7615_dev *dev = mt7615_hw_dev(hw); + struct ieee80211_sta *sta = params->sta; + struct ieee80211_txq *txq = sta->txq[params->tid]; + struct mt7615_sta *msta = (struct mt7615_sta *)sta->drv_priv; + u16 tid = params->tid; + u16 ssn = params->ssn; + struct mt76_txq *mtxq; + int ret = 0; + + if (!txq) + return -EINVAL; + + mtxq = (struct mt76_txq *)txq->drv_priv; + + mt7615_mutex_acquire(dev); + + switch (action) { + case IEEE80211_AMPDU_RX_START: + mt76_rx_aggr_start(&dev->mt76, &msta->wcid, tid, ssn, + params->buf_size); + ret = mt7615_mcu_add_rx_ba(dev, params, true); + break; + case IEEE80211_AMPDU_RX_STOP: + mt76_rx_aggr_stop(&dev->mt76, &msta->wcid, tid); + ret = mt7615_mcu_add_rx_ba(dev, params, false); + break; + case IEEE80211_AMPDU_TX_OPERATIONAL: + mtxq->aggr = true; + mtxq->send_bar = false; + ret = mt7615_mcu_add_tx_ba(dev, params, true); + ssn = mt7615_mac_get_sta_tid_sn(dev, msta->wcid.idx, tid); + ieee80211_send_bar(vif, sta->addr, tid, + IEEE80211_SN_TO_SEQ(ssn)); + break; + case IEEE80211_AMPDU_TX_STOP_FLUSH: + case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: + mtxq->aggr = false; + ret = mt7615_mcu_add_tx_ba(dev, params, false); + break; + case IEEE80211_AMPDU_TX_START: + ssn = mt7615_mac_get_sta_tid_sn(dev, msta->wcid.idx, tid); + params->ssn = ssn; + ret = IEEE80211_AMPDU_TX_START_IMMEDIATE; + break; + case IEEE80211_AMPDU_TX_STOP_CONT: + mtxq->aggr = false; + ret = mt7615_mcu_add_tx_ba(dev, params, false); + ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); + break; + } + mt7615_mutex_release(dev); + + return ret; +} + +static int +mt7615_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + return mt76_sta_state(hw, vif, sta, IEEE80211_STA_NOTEXIST, + IEEE80211_STA_NONE); +} + +static int +mt7615_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + return mt76_sta_state(hw, vif, sta, IEEE80211_STA_NONE, + IEEE80211_STA_NOTEXIST); +} + +static int +mt7615_get_stats(struct ieee80211_hw *hw, + struct ieee80211_low_level_stats *stats) +{ + struct mt7615_phy *phy = mt7615_hw_phy(hw); + struct mib_stats *mib = &phy->mib; + + mt7615_mutex_acquire(phy->dev); + + stats->dot11RTSSuccessCount = mib->rts_cnt; + stats->dot11RTSFailureCount = mib->rts_retries_cnt; + stats->dot11FCSErrorCount = mib->fcs_err_cnt; + stats->dot11ACKFailureCount = mib->ack_fail_cnt; + + mt7615_mutex_release(phy->dev); + + return 0; +} + +static u64 +mt7615_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv; + struct mt7615_dev *dev = mt7615_hw_dev(hw); + union { + u64 t64; + u32 t32[2]; + } tsf; + u16 idx = mvif->mt76.omac_idx; + u32 reg; + + idx = idx > HW_BSSID_MAX ? HW_BSSID_0 : idx; + reg = idx > 1 ? MT_LPON_TCR2(idx): MT_LPON_TCR0(idx); + + mt7615_mutex_acquire(dev); + + /* TSF read */ + mt76_rmw(dev, reg, MT_LPON_TCR_MODE, MT_LPON_TCR_READ); + tsf.t32[0] = mt76_rr(dev, MT_LPON_UTTR0); + tsf.t32[1] = mt76_rr(dev, MT_LPON_UTTR1); + + mt7615_mutex_release(dev); + + return tsf.t64; +} + +static void +mt7615_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u64 timestamp) +{ + struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv; + struct mt7615_dev *dev = mt7615_hw_dev(hw); + union { + u64 t64; + u32 t32[2]; + } tsf = { .t64 = timestamp, }; + u16 idx = mvif->mt76.omac_idx; + u32 reg; + + idx = idx > HW_BSSID_MAX ? HW_BSSID_0 : idx; + reg = idx > 1 ? MT_LPON_TCR2(idx): MT_LPON_TCR0(idx); + + mt7615_mutex_acquire(dev); + + mt76_wr(dev, MT_LPON_UTTR0, tsf.t32[0]); + mt76_wr(dev, MT_LPON_UTTR1, tsf.t32[1]); + /* TSF software overwrite */ + mt76_rmw(dev, reg, MT_LPON_TCR_MODE, MT_LPON_TCR_WRITE); + + mt7615_mutex_release(dev); +} + +static void +mt7615_offset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + s64 timestamp) +{ + struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv; + struct mt7615_dev *dev = mt7615_hw_dev(hw); + union { + u64 t64; + u32 t32[2]; + } tsf = { .t64 = timestamp, }; + u16 idx = mvif->mt76.omac_idx; + u32 reg; + + idx = idx > HW_BSSID_MAX ? HW_BSSID_0 : idx; + reg = idx > 1 ? MT_LPON_TCR2(idx): MT_LPON_TCR0(idx); + + mt7615_mutex_acquire(dev); + + mt76_wr(dev, MT_LPON_UTTR0, tsf.t32[0]); + mt76_wr(dev, MT_LPON_UTTR1, tsf.t32[1]); + /* TSF software adjust*/ + mt76_rmw(dev, reg, MT_LPON_TCR_MODE, MT_LPON_TCR_ADJUST); + + mt7615_mutex_release(dev); +} + +static void +mt7615_set_coverage_class(struct ieee80211_hw *hw, int radio_idx, + s16 coverage_class) +{ + struct mt7615_phy *phy = mt7615_hw_phy(hw); + struct mt7615_dev *dev = phy->dev; + + mt7615_mutex_acquire(dev); + phy->coverage_class = max_t(s16, coverage_class, 0); + mt7615_mac_set_timing(phy); + mt7615_mutex_release(dev); +} + +static int +mt7615_set_antenna(struct ieee80211_hw *hw, int radio_idx, + u32 tx_ant, u32 rx_ant) +{ + struct mt7615_dev *dev = mt7615_hw_dev(hw); + struct mt7615_phy *phy = mt7615_hw_phy(hw); + int max_nss = hweight8(hw->wiphy->available_antennas_tx); + bool ext_phy = phy != &dev->phy; + + if (!tx_ant || tx_ant != rx_ant || ffs(tx_ant) > max_nss) + return -EINVAL; + + if ((BIT(hweight8(tx_ant)) - 1) != tx_ant) + tx_ant = BIT(ffs(tx_ant) - 1) - 1; + + mt7615_mutex_acquire(dev); + + phy->mt76->antenna_mask = tx_ant; + if (ext_phy) { + if (dev->chainmask == 0xf) + tx_ant <<= 2; + else + tx_ant <<= 1; + } + phy->mt76->chainmask = tx_ant; + + mt76_set_stream_caps(phy->mt76, true); + + mt7615_mutex_release(dev); + + return 0; +} + +static void mt7615_roc_iter(void *priv, u8 *mac, + struct ieee80211_vif *vif) +{ + struct mt7615_phy *phy = priv; + + mt7615_mcu_set_roc(phy, vif, NULL, 0); +} + +void mt7615_roc_work(struct work_struct *work) +{ + struct mt7615_phy *phy; + + phy = (struct mt7615_phy *)container_of(work, struct mt7615_phy, + roc_work); + + if (!test_and_clear_bit(MT76_STATE_ROC, &phy->mt76->state)) + return; + + mt7615_mutex_acquire(phy->dev); + ieee80211_iterate_active_interfaces(phy->mt76->hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7615_roc_iter, phy); + mt7615_mutex_release(phy->dev); + ieee80211_remain_on_channel_expired(phy->mt76->hw); +} + +void mt7615_roc_timer(struct timer_list *timer) +{ + struct mt7615_phy *phy = timer_container_of(phy, timer, roc_timer); + + ieee80211_queue_work(phy->mt76->hw, &phy->roc_work); +} + +void mt7615_scan_work(struct work_struct *work) +{ + struct mt7615_phy *phy; + + phy = (struct mt7615_phy *)container_of(work, struct mt7615_phy, + scan_work.work); + + while (true) { + struct mt7615_mcu_rxd *rxd; + struct sk_buff *skb; + + spin_lock_bh(&phy->dev->mt76.lock); + skb = __skb_dequeue(&phy->scan_event_list); + spin_unlock_bh(&phy->dev->mt76.lock); + + if (!skb) + break; + + rxd = (struct mt7615_mcu_rxd *)skb->data; + if (rxd->eid == MCU_EVENT_SCHED_SCAN_DONE) { + ieee80211_sched_scan_results(phy->mt76->hw); + } else if (test_and_clear_bit(MT76_HW_SCANNING, + &phy->mt76->state)) { + struct cfg80211_scan_info info = { + .aborted = false, + }; + + ieee80211_scan_completed(phy->mt76->hw, &info); + } + dev_kfree_skb(skb); + } +} + +static int +mt7615_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_scan_request *req) +{ + struct mt7615_dev *dev = mt7615_hw_dev(hw); + struct mt76_phy *mphy = hw->priv; + int err; + + /* fall-back to sw-scan */ + if (!mt7615_firmware_offload(dev)) + return 1; + + mt7615_mutex_acquire(dev); + err = mt76_connac_mcu_hw_scan(mphy, vif, req); + mt7615_mutex_release(dev); + + return err; +} + +static void +mt7615_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + struct mt7615_dev *dev = mt7615_hw_dev(hw); + struct mt76_phy *mphy = hw->priv; + + mt7615_mutex_acquire(dev); + mt76_connac_mcu_cancel_hw_scan(mphy, vif); + mt7615_mutex_release(dev); +} + +static int +mt7615_start_sched_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct cfg80211_sched_scan_request *req, + struct ieee80211_scan_ies *ies) +{ + struct mt7615_dev *dev = mt7615_hw_dev(hw); + struct mt76_phy *mphy = hw->priv; + int err; + + if (!mt7615_firmware_offload(dev)) + return -EOPNOTSUPP; + + mt7615_mutex_acquire(dev); + + err = mt76_connac_mcu_sched_scan_req(mphy, vif, req); + if (err < 0) + goto out; + + err = mt76_connac_mcu_sched_scan_enable(mphy, vif, true); +out: + mt7615_mutex_release(dev); + + return err; +} + +static int +mt7615_stop_sched_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + struct mt7615_dev *dev = mt7615_hw_dev(hw); + struct mt76_phy *mphy = hw->priv; + int err; + + if (!mt7615_firmware_offload(dev)) + return -EOPNOTSUPP; + + mt7615_mutex_acquire(dev); + err = mt76_connac_mcu_sched_scan_enable(mphy, vif, false); + mt7615_mutex_release(dev); + + return err; +} + +static int mt7615_remain_on_channel(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_channel *chan, + int duration, + enum ieee80211_roc_type type) +{ + struct mt7615_phy *phy = mt7615_hw_phy(hw); + int err; + + if (test_and_set_bit(MT76_STATE_ROC, &phy->mt76->state)) + return 0; + + mt7615_mutex_acquire(phy->dev); + + err = mt7615_mcu_set_roc(phy, vif, chan, duration); + if (err < 0) { + clear_bit(MT76_STATE_ROC, &phy->mt76->state); + goto out; + } + + if (!wait_event_timeout(phy->roc_wait, phy->roc_grant, HZ)) { + mt7615_mcu_set_roc(phy, vif, NULL, 0); + clear_bit(MT76_STATE_ROC, &phy->mt76->state); + err = -ETIMEDOUT; + } + +out: + mt7615_mutex_release(phy->dev); + + return err; +} + +static int mt7615_cancel_remain_on_channel(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct mt7615_phy *phy = mt7615_hw_phy(hw); + int err; + + if (!test_and_clear_bit(MT76_STATE_ROC, &phy->mt76->state)) + return 0; + + timer_delete_sync(&phy->roc_timer); + cancel_work_sync(&phy->roc_work); + + mt7615_mutex_acquire(phy->dev); + err = mt7615_mcu_set_roc(phy, vif, NULL, 0); + mt7615_mutex_release(phy->dev); + + return err; +} + +static void mt7615_sta_set_decap_offload(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + bool enabled) +{ + struct mt7615_dev *dev = mt7615_hw_dev(hw); + struct mt7615_sta *msta = (struct mt7615_sta *)sta->drv_priv; + + mt7615_mutex_acquire(dev); + + if (enabled) + set_bit(MT_WCID_FLAG_HDR_TRANS, &msta->wcid.flags); + else + clear_bit(MT_WCID_FLAG_HDR_TRANS, &msta->wcid.flags); + + mt7615_mcu_set_sta_decap_offload(dev, vif, sta); + + mt7615_mutex_release(dev); +} + +#ifdef CONFIG_PM +static int mt7615_suspend(struct ieee80211_hw *hw, + struct cfg80211_wowlan *wowlan) +{ + struct mt7615_phy *phy = mt7615_hw_phy(hw); + struct mt7615_dev *dev = mt7615_hw_dev(hw); + int err = 0; + + cancel_delayed_work_sync(&dev->pm.ps_work); + mt76_connac_free_pending_tx_skbs(&dev->pm, NULL); + + cancel_delayed_work_sync(&phy->scan_work); + cancel_delayed_work_sync(&phy->mt76->mac_work); + + mt7615_mutex_acquire(dev); + + clear_bit(MT76_STATE_RUNNING, &phy->mt76->state); + + set_bit(MT76_STATE_SUSPEND, &phy->mt76->state); + ieee80211_iterate_active_interfaces(hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + mt76_connac_mcu_set_suspend_iter, + phy->mt76); + + if (!mt7615_dev_running(dev)) + err = mt76_connac_mcu_set_hif_suspend(&dev->mt76, true, true); + + mt7615_mutex_release(dev); + + return err; +} + +static int mt7615_resume(struct ieee80211_hw *hw) +{ + struct mt7615_phy *phy = mt7615_hw_phy(hw); + struct mt7615_dev *dev = mt7615_hw_dev(hw); + unsigned long timeout; + bool running; + + mt7615_mutex_acquire(dev); + + running = mt7615_dev_running(dev); + set_bit(MT76_STATE_RUNNING, &phy->mt76->state); + + if (!running) { + int err; + + err = mt76_connac_mcu_set_hif_suspend(&dev->mt76, false, true); + if (err < 0) { + mt7615_mutex_release(dev); + return err; + } + } + + clear_bit(MT76_STATE_SUSPEND, &phy->mt76->state); + ieee80211_iterate_active_interfaces(hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + mt76_connac_mcu_set_suspend_iter, + phy->mt76); + + timeout = mt7615_get_macwork_timeout(dev); + ieee80211_queue_delayed_work(hw, &phy->mt76->mac_work, timeout); + + mt7615_mutex_release(dev); + + return 0; +} + +static void mt7615_set_wakeup(struct ieee80211_hw *hw, bool enabled) +{ + struct mt7615_dev *dev = mt7615_hw_dev(hw); + struct mt76_dev *mdev = &dev->mt76; + + device_set_wakeup_enable(mdev->dev, enabled); +} + +static void mt7615_set_rekey_data(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_gtk_rekey_data *data) +{ + struct mt7615_dev *dev = mt7615_hw_dev(hw); + + mt7615_mutex_acquire(dev); + mt76_connac_mcu_update_gtk_rekey(hw, vif, data); + mt7615_mutex_release(dev); +} +#endif /* CONFIG_PM */ + +const struct ieee80211_ops mt7615_ops = { + .add_chanctx = ieee80211_emulate_add_chanctx, + .remove_chanctx = ieee80211_emulate_remove_chanctx, + .change_chanctx = ieee80211_emulate_change_chanctx, + .switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx, + .tx = mt7615_tx, + .start = mt7615_start, + .stop = mt7615_stop, + .add_interface = mt7615_add_interface, + .remove_interface = mt7615_remove_interface, + .config = mt7615_config, + .conf_tx = mt7615_conf_tx, + .configure_filter = mt7615_configure_filter, + .bss_info_changed = mt7615_bss_info_changed, + .sta_add = mt7615_sta_add, + .sta_remove = mt7615_sta_remove, + .sta_pre_rcu_remove = mt76_sta_pre_rcu_remove, + .set_key = mt7615_set_key, + .sta_set_decap_offload = mt7615_sta_set_decap_offload, + .ampdu_action = mt7615_ampdu_action, + .set_rts_threshold = mt7615_set_rts_threshold, + .wake_tx_queue = mt76_wake_tx_queue, + .sta_rate_tbl_update = mt7615_sta_rate_tbl_update, + .sw_scan_start = mt76_sw_scan, + .sw_scan_complete = mt76_sw_scan_complete, + .release_buffered_frames = mt76_release_buffered_frames, + .get_txpower = mt76_get_txpower, + .channel_switch_beacon = mt7615_channel_switch_beacon, + .get_stats = mt7615_get_stats, + .get_tsf = mt7615_get_tsf, + .set_tsf = mt7615_set_tsf, + .offset_tsf = mt7615_offset_tsf, + .get_survey = mt76_get_survey, + .get_antenna = mt76_get_antenna, + .set_antenna = mt7615_set_antenna, + .set_coverage_class = mt7615_set_coverage_class, + .hw_scan = mt7615_hw_scan, + .cancel_hw_scan = mt7615_cancel_hw_scan, + .sched_scan_start = mt7615_start_sched_scan, + .sched_scan_stop = mt7615_stop_sched_scan, + .remain_on_channel = mt7615_remain_on_channel, + .cancel_remain_on_channel = mt7615_cancel_remain_on_channel, + CFG80211_TESTMODE_CMD(mt76_testmode_cmd) + CFG80211_TESTMODE_DUMP(mt76_testmode_dump) +#ifdef CONFIG_PM + .suspend = mt7615_suspend, + .resume = mt7615_resume, + .set_wakeup = mt7615_set_wakeup, + .set_rekey_data = mt7615_set_rekey_data, +#endif /* CONFIG_PM */ + .set_sar_specs = mt7615_set_sar_specs, +}; +EXPORT_SYMBOL_GPL(mt7615_ops); + +MODULE_DESCRIPTION("MediaTek MT7615E and MT7663E wireless driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c new file mode 100644 index 000000000..ff57ede87 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c @@ -0,0 +1,2613 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2019 MediaTek Inc. + * + * Author: Roy Luo + * Ryder Lee + */ + +#include +#include "mt7615.h" +#include "mcu.h" +#include "mac.h" +#include "eeprom.h" + +static bool prefer_offload_fw = true; +module_param(prefer_offload_fw, bool, 0644); +MODULE_PARM_DESC(prefer_offload_fw, + "Prefer client mode offload firmware (MT7663)"); + +struct mt7615_patch_hdr { + char build_date[16]; + char platform[4]; + __be32 hw_sw_ver; + __be32 patch_ver; + __be16 checksum; +} __packed; + +struct mt7615_fw_trailer { + __le32 addr; + u8 chip_id; + u8 feature_set; + u8 eco_code; + char fw_ver[10]; + char build_date[15]; + __le32 len; +} __packed; + +#define FW_V3_COMMON_TAILER_SIZE 36 +#define FW_V3_REGION_TAILER_SIZE 40 +#define FW_START_OVERRIDE BIT(0) +#define FW_START_DLYCAL BIT(1) +#define FW_START_WORKING_PDA_CR4 BIT(2) + +struct mt7663_fw_buf { + __le32 crc; + __le32 d_img_size; + __le32 block_size; + u8 rsv[4]; + __le32 img_dest_addr; + __le32 img_size; + u8 feature_set; +}; + +#define MT7615_PATCH_ADDRESS 0x80000 +#define MT7622_PATCH_ADDRESS 0x9c000 +#define MT7663_PATCH_ADDRESS 0xdc000 + +#define N9_REGION_NUM 2 +#define CR4_REGION_NUM 1 + +#define IMG_CRC_LEN 4 + +void mt7615_mcu_fill_msg(struct mt7615_dev *dev, struct sk_buff *skb, + int cmd, int *wait_seq) +{ + int txd_len, mcu_cmd = FIELD_GET(__MCU_CMD_FIELD_ID, cmd); + struct mt7615_uni_txd *uni_txd; + struct mt7615_mcu_txd *mcu_txd; + u8 seq, q_idx, pkt_fmt; + __le32 *txd; + u32 val; + + /* TODO: make dynamic based on msg type */ + dev->mt76.mcu.timeout = 20 * HZ; + + seq = ++dev->mt76.mcu.msg_seq & 0xf; + if (!seq) + seq = ++dev->mt76.mcu.msg_seq & 0xf; + if (wait_seq) + *wait_seq = seq; + + txd_len = cmd & __MCU_CMD_FIELD_UNI ? sizeof(*uni_txd) : sizeof(*mcu_txd); + txd = (__le32 *)skb_push(skb, txd_len); + + if (cmd != MCU_CMD(FW_SCATTER)) { + q_idx = MT_TX_MCU_PORT_RX_Q0; + pkt_fmt = MT_TX_TYPE_CMD; + } else { + q_idx = MT_TX_MCU_PORT_RX_FWDL; + pkt_fmt = MT_TX_TYPE_FW; + } + + val = FIELD_PREP(MT_TXD0_TX_BYTES, skb->len) | + FIELD_PREP(MT_TXD0_P_IDX, MT_TX_PORT_IDX_MCU) | + FIELD_PREP(MT_TXD0_Q_IDX, q_idx); + txd[0] = cpu_to_le32(val); + + val = MT_TXD1_LONG_FORMAT | + FIELD_PREP(MT_TXD1_HDR_FORMAT, MT_HDR_FORMAT_CMD) | + FIELD_PREP(MT_TXD1_PKT_FMT, pkt_fmt); + txd[1] = cpu_to_le32(val); + + if (cmd & __MCU_CMD_FIELD_UNI) { + uni_txd = (struct mt7615_uni_txd *)txd; + uni_txd->len = cpu_to_le16(skb->len - sizeof(uni_txd->txd)); + uni_txd->option = MCU_CMD_UNI_EXT_ACK; + uni_txd->cid = cpu_to_le16(mcu_cmd); + uni_txd->s2d_index = MCU_S2D_H2N; + uni_txd->pkt_type = MCU_PKT_ID; + uni_txd->seq = seq; + + return; + } + + mcu_txd = (struct mt7615_mcu_txd *)txd; + mcu_txd->len = cpu_to_le16(skb->len - sizeof(mcu_txd->txd)); + mcu_txd->pq_id = cpu_to_le16(MCU_PQ_ID(MT_TX_PORT_IDX_MCU, q_idx)); + mcu_txd->s2d_index = MCU_S2D_H2N; + mcu_txd->pkt_type = MCU_PKT_ID; + mcu_txd->seq = seq; + mcu_txd->cid = mcu_cmd; + mcu_txd->ext_cid = FIELD_GET(__MCU_CMD_FIELD_EXT_ID, cmd); + + if (mcu_txd->ext_cid || (cmd & __MCU_CMD_FIELD_CE)) { + if (cmd & __MCU_CMD_FIELD_QUERY) + mcu_txd->set_query = MCU_Q_QUERY; + else + mcu_txd->set_query = MCU_Q_SET; + mcu_txd->ext_cid_ack = !!mcu_txd->ext_cid; + } else { + mcu_txd->set_query = MCU_Q_NA; + } +} +EXPORT_SYMBOL_GPL(mt7615_mcu_fill_msg); + +int mt7615_mcu_parse_response(struct mt76_dev *mdev, int cmd, + struct sk_buff *skb, int seq) +{ + struct mt7615_mcu_rxd *rxd; + int ret = 0; + + if (!skb) { + dev_err(mdev->dev, "Message %08x (seq %d) timeout\n", + cmd, seq); + return -ETIMEDOUT; + } + + rxd = (struct mt7615_mcu_rxd *)skb->data; + if (seq != rxd->seq) + return -EAGAIN; + + if (cmd == MCU_CMD(PATCH_SEM_CONTROL)) { + skb_pull(skb, sizeof(*rxd) - 4); + ret = *skb->data; + } else if (cmd == MCU_EXT_CMD(THERMAL_CTRL)) { + skb_pull(skb, sizeof(*rxd)); + ret = le32_to_cpu(*(__le32 *)skb->data); + } else if (cmd == MCU_EXT_QUERY(RF_REG_ACCESS)) { + skb_pull(skb, sizeof(*rxd)); + ret = le32_to_cpu(*(__le32 *)&skb->data[8]); + } else if (cmd == MCU_UNI_CMD(DEV_INFO_UPDATE) || + cmd == MCU_UNI_CMD(BSS_INFO_UPDATE) || + cmd == MCU_UNI_CMD(STA_REC_UPDATE) || + cmd == MCU_UNI_CMD(HIF_CTRL) || + cmd == MCU_UNI_CMD(OFFLOAD) || + cmd == MCU_UNI_CMD(SUSPEND)) { + struct mt76_connac_mcu_uni_event *event; + + skb_pull(skb, sizeof(*rxd)); + event = (struct mt76_connac_mcu_uni_event *)skb->data; + ret = le32_to_cpu(event->status); + } else if (cmd == MCU_CE_QUERY(REG_READ)) { + struct mt76_connac_mcu_reg_event *event; + + skb_pull(skb, sizeof(*rxd)); + event = (struct mt76_connac_mcu_reg_event *)skb->data; + ret = (int)le32_to_cpu(event->val); + } + + return ret; +} +EXPORT_SYMBOL_GPL(mt7615_mcu_parse_response); + +static int +mt7615_mcu_send_message(struct mt76_dev *mdev, struct sk_buff *skb, + int cmd, int *seq) +{ + struct mt7615_dev *dev = container_of(mdev, struct mt7615_dev, mt76); + enum mt76_mcuq_id qid; + + mt7615_mcu_fill_msg(dev, skb, cmd, seq); + if (test_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state)) + qid = MT_MCUQ_WM; + else + qid = MT_MCUQ_FWDL; + + return mt76_tx_queue_skb_raw(dev, dev->mt76.q_mcu[qid], skb, 0); +} + +u32 mt7615_rf_rr(struct mt7615_dev *dev, u32 wf, u32 reg) +{ + struct { + __le32 wifi_stream; + __le32 address; + __le32 data; + } req = { + .wifi_stream = cpu_to_le32(wf), + .address = cpu_to_le32(reg), + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_QUERY(RF_REG_ACCESS), + &req, sizeof(req), true); +} + +int mt7615_rf_wr(struct mt7615_dev *dev, u32 wf, u32 reg, u32 val) +{ + struct { + __le32 wifi_stream; + __le32 address; + __le32 data; + } req = { + .wifi_stream = cpu_to_le32(wf), + .address = cpu_to_le32(reg), + .data = cpu_to_le32(val), + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(RF_REG_ACCESS), + &req, sizeof(req), false); +} + +void mt7622_trigger_hif_int(struct mt7615_dev *dev, bool en) +{ + if (!is_mt7622(&dev->mt76)) + return; + + regmap_update_bits(dev->infracfg, MT_INFRACFG_MISC, + MT_INFRACFG_MISC_AP2CONN_WAKE, + !en * MT_INFRACFG_MISC_AP2CONN_WAKE); +} +EXPORT_SYMBOL_GPL(mt7622_trigger_hif_int); + +static int mt7615_mcu_drv_pmctrl(struct mt7615_dev *dev) +{ + struct mt76_phy *mphy = &dev->mt76.phy; + struct mt76_connac_pm *pm = &dev->pm; + struct mt76_dev *mdev = &dev->mt76; + u32 addr; + int err; + + if (is_mt7663(mdev)) { + /* Clear firmware own via N9 eint */ + mt76_wr(dev, MT_PCIE_DOORBELL_PUSH, MT_CFG_LPCR_HOST_DRV_OWN); + mt76_poll(dev, MT_CONN_ON_MISC, MT_CFG_LPCR_HOST_FW_OWN, 0, 3000); + + addr = MT_CONN_HIF_ON_LPCTL; + } else { + addr = MT_CFG_LPCR_HOST; + } + + mt76_wr(dev, addr, MT_CFG_LPCR_HOST_DRV_OWN); + + mt7622_trigger_hif_int(dev, true); + + err = !mt76_poll_msec(dev, addr, MT_CFG_LPCR_HOST_FW_OWN, 0, 3000); + + mt7622_trigger_hif_int(dev, false); + + if (err) { + dev_err(mdev->dev, "driver own failed\n"); + return -ETIMEDOUT; + } + + clear_bit(MT76_STATE_PM, &mphy->state); + + pm->stats.last_wake_event = jiffies; + pm->stats.doze_time += pm->stats.last_wake_event - + pm->stats.last_doze_event; + + return 0; +} + +static int mt7615_mcu_lp_drv_pmctrl(struct mt7615_dev *dev) +{ + struct mt76_phy *mphy = &dev->mt76.phy; + struct mt76_connac_pm *pm = &dev->pm; + int i, err = 0; + + mutex_lock(&pm->mutex); + + if (!test_bit(MT76_STATE_PM, &mphy->state)) + goto out; + + for (i = 0; i < MT7615_DRV_OWN_RETRY_COUNT; i++) { + mt76_wr(dev, MT_PCIE_DOORBELL_PUSH, MT_CFG_LPCR_HOST_DRV_OWN); + if (mt76_poll_msec(dev, MT_CONN_HIF_ON_LPCTL, + MT_CFG_LPCR_HOST_FW_OWN, 0, 50)) + break; + } + + if (i == MT7615_DRV_OWN_RETRY_COUNT) { + dev_err(dev->mt76.dev, "driver own failed\n"); + err = -EIO; + goto out; + } + clear_bit(MT76_STATE_PM, &mphy->state); + + pm->stats.last_wake_event = jiffies; + pm->stats.doze_time += pm->stats.last_wake_event - + pm->stats.last_doze_event; +out: + mutex_unlock(&pm->mutex); + + return err; +} + +static int mt7615_mcu_fw_pmctrl(struct mt7615_dev *dev) +{ + struct mt76_phy *mphy = &dev->mt76.phy; + struct mt76_connac_pm *pm = &dev->pm; + int err = 0; + u32 addr; + + mutex_lock(&pm->mutex); + + if (mt76_connac_skip_fw_pmctrl(mphy, pm)) + goto out; + + mt7622_trigger_hif_int(dev, true); + + addr = is_mt7663(&dev->mt76) ? MT_CONN_HIF_ON_LPCTL : MT_CFG_LPCR_HOST; + mt76_wr(dev, addr, MT_CFG_LPCR_HOST_FW_OWN); + + if (is_mt7622(&dev->mt76) && + !mt76_poll_msec(dev, addr, MT_CFG_LPCR_HOST_FW_OWN, + MT_CFG_LPCR_HOST_FW_OWN, 3000)) { + dev_err(dev->mt76.dev, "Timeout for firmware own\n"); + clear_bit(MT76_STATE_PM, &mphy->state); + err = -EIO; + } + + mt7622_trigger_hif_int(dev, false); + if (!err) { + pm->stats.last_doze_event = jiffies; + pm->stats.awake_time += pm->stats.last_doze_event - + pm->stats.last_wake_event; + } +out: + mutex_unlock(&pm->mutex); + + return err; +} + +static void +mt7615_mcu_csa_finish(void *priv, u8 *mac, struct ieee80211_vif *vif) +{ + if (vif->bss_conf.csa_active) + ieee80211_csa_finish(vif, 0); +} + +static void +mt7615_mcu_rx_csa_notify(struct mt7615_dev *dev, struct sk_buff *skb) +{ + struct mt7615_phy *ext_phy = mt7615_ext_phy(dev); + struct mt76_phy *mphy = &dev->mt76.phy; + struct mt7615_mcu_csa_notify *c; + + c = (struct mt7615_mcu_csa_notify *)skb->data; + + if (c->omac_idx > EXT_BSSID_MAX) + return; + + if (ext_phy && ext_phy->omac_mask & BIT_ULL(c->omac_idx)) + mphy = dev->mt76.phys[MT_BAND1]; + + ieee80211_iterate_active_interfaces_atomic(mphy->hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7615_mcu_csa_finish, mphy->hw); +} + +static void +mt7615_mcu_rx_radar_detected(struct mt7615_dev *dev, struct sk_buff *skb) +{ + struct mt76_phy *mphy = &dev->mt76.phy; + struct mt7615_mcu_rdd_report *r; + + r = (struct mt7615_mcu_rdd_report *)skb->data; + + if (!dev->radar_pattern.n_pulses && !r->long_detected && + !r->constant_prf_detected && !r->staggered_prf_detected) + return; + + if (r->band_idx && dev->mt76.phys[MT_BAND1]) + mphy = dev->mt76.phys[MT_BAND1]; + + if (mt76_phy_dfs_state(mphy) < MT_DFS_STATE_CAC) + return; + + ieee80211_radar_detected(mphy->hw, NULL); + dev->hw_pattern++; +} + +static void +mt7615_mcu_rx_log_message(struct mt7615_dev *dev, struct sk_buff *skb) +{ + struct mt7615_mcu_rxd *rxd = (struct mt7615_mcu_rxd *)skb->data; + const char *data = (char *)&rxd[1]; + const char *type; + + switch (rxd->s2d_index) { + case 0: + type = "N9"; + break; + case 2: + type = "CR4"; + break; + default: + type = "unknown"; + break; + } + + wiphy_info(mt76_hw(dev)->wiphy, "%s: %.*s", type, + (int)(skb->len - sizeof(*rxd)), data); +} + +static void +mt7615_mcu_rx_ext_event(struct mt7615_dev *dev, struct sk_buff *skb) +{ + struct mt7615_mcu_rxd *rxd = (struct mt7615_mcu_rxd *)skb->data; + + switch (rxd->ext_eid) { + case MCU_EXT_EVENT_RDD_REPORT: + mt7615_mcu_rx_radar_detected(dev, skb); + break; + case MCU_EXT_EVENT_CSA_NOTIFY: + mt7615_mcu_rx_csa_notify(dev, skb); + break; + case MCU_EXT_EVENT_FW_LOG_2_HOST: + mt7615_mcu_rx_log_message(dev, skb); + break; + default: + break; + } +} + +static void +mt7615_mcu_scan_event(struct mt7615_dev *dev, struct sk_buff *skb) +{ + u8 *seq_num = skb->data + sizeof(struct mt7615_mcu_rxd); + struct mt7615_phy *phy; + struct mt76_phy *mphy; + + if (*seq_num & BIT(7) && dev->mt76.phys[MT_BAND1]) + mphy = dev->mt76.phys[MT_BAND1]; + else + mphy = &dev->mt76.phy; + + phy = mphy->priv; + + spin_lock_bh(&dev->mt76.lock); + __skb_queue_tail(&phy->scan_event_list, skb); + spin_unlock_bh(&dev->mt76.lock); + + ieee80211_queue_delayed_work(mphy->hw, &phy->scan_work, + MT7615_HW_SCAN_TIMEOUT); +} + +static void +mt7615_mcu_roc_event(struct mt7615_dev *dev, struct sk_buff *skb) +{ + struct mt7615_roc_tlv *event; + struct mt7615_phy *phy; + struct mt76_phy *mphy; + int duration; + + skb_pull(skb, sizeof(struct mt7615_mcu_rxd)); + event = (struct mt7615_roc_tlv *)skb->data; + + if (event->dbdc_band && dev->mt76.phys[MT_BAND1]) + mphy = dev->mt76.phys[MT_BAND1]; + else + mphy = &dev->mt76.phy; + + ieee80211_ready_on_channel(mphy->hw); + + phy = mphy->priv; + phy->roc_grant = true; + wake_up(&phy->roc_wait); + + duration = le32_to_cpu(event->max_interval); + mod_timer(&phy->roc_timer, + round_jiffies_up(jiffies + msecs_to_jiffies(duration))); +} + +static void +mt7615_mcu_beacon_loss_event(struct mt7615_dev *dev, struct sk_buff *skb) +{ + struct mt76_connac_beacon_loss_event *event; + struct mt76_phy *mphy; + u8 band_idx = 0; /* DBDC support */ + + skb_pull(skb, sizeof(struct mt7615_mcu_rxd)); + event = (struct mt76_connac_beacon_loss_event *)skb->data; + if (band_idx && dev->mt76.phys[MT_BAND1]) + mphy = dev->mt76.phys[MT_BAND1]; + else + mphy = &dev->mt76.phy; + + ieee80211_iterate_active_interfaces_atomic(mphy->hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + mt76_connac_mcu_beacon_loss_iter, + event); +} + +static void +mt7615_mcu_bss_event(struct mt7615_dev *dev, struct sk_buff *skb) +{ + struct mt76_connac_mcu_bss_event *event; + struct mt76_phy *mphy; + u8 band_idx = 0; /* DBDC support */ + + skb_pull(skb, sizeof(struct mt7615_mcu_rxd)); + event = (struct mt76_connac_mcu_bss_event *)skb->data; + + if (band_idx && dev->mt76.phys[MT_BAND1]) + mphy = dev->mt76.phys[MT_BAND1]; + else + mphy = &dev->mt76.phy; + + if (event->is_absent) + ieee80211_stop_queues(mphy->hw); + else + ieee80211_wake_queues(mphy->hw); +} + +static void +mt7615_mcu_rx_unsolicited_event(struct mt7615_dev *dev, struct sk_buff *skb) +{ + struct mt7615_mcu_rxd *rxd = (struct mt7615_mcu_rxd *)skb->data; + + switch (rxd->eid) { + case MCU_EVENT_EXT: + mt7615_mcu_rx_ext_event(dev, skb); + break; + case MCU_EVENT_BSS_BEACON_LOSS: + mt7615_mcu_beacon_loss_event(dev, skb); + break; + case MCU_EVENT_ROC: + mt7615_mcu_roc_event(dev, skb); + break; + case MCU_EVENT_SCHED_SCAN_DONE: + case MCU_EVENT_SCAN_DONE: + mt7615_mcu_scan_event(dev, skb); + return; + case MCU_EVENT_BSS_ABSENCE: + mt7615_mcu_bss_event(dev, skb); + break; + case MCU_EVENT_COREDUMP: + mt76_connac_mcu_coredump_event(&dev->mt76, skb, + &dev->coredump); + return; + default: + break; + } + dev_kfree_skb(skb); +} + +void mt7615_mcu_rx_event(struct mt7615_dev *dev, struct sk_buff *skb) +{ + struct mt7615_mcu_rxd *rxd = (struct mt7615_mcu_rxd *)skb->data; + + if (rxd->ext_eid == MCU_EXT_EVENT_THERMAL_PROTECT || + rxd->ext_eid == MCU_EXT_EVENT_FW_LOG_2_HOST || + rxd->ext_eid == MCU_EXT_EVENT_ASSERT_DUMP || + rxd->ext_eid == MCU_EXT_EVENT_PS_SYNC || + rxd->eid == MCU_EVENT_BSS_BEACON_LOSS || + rxd->eid == MCU_EVENT_SCHED_SCAN_DONE || + rxd->eid == MCU_EVENT_BSS_ABSENCE || + rxd->eid == MCU_EVENT_SCAN_DONE || + rxd->eid == MCU_EVENT_COREDUMP || + rxd->eid == MCU_EVENT_ROC || + !rxd->seq) + mt7615_mcu_rx_unsolicited_event(dev, skb); + else + mt76_mcu_rx_event(&dev->mt76, skb); +} + +static int +mt7615_mcu_muar_config(struct mt7615_dev *dev, struct ieee80211_vif *vif, + bool bssid, bool enable) +{ + struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv; + u32 idx = mvif->mt76.omac_idx - REPEATER_BSSID_START; + u32 mask = dev->omac_mask >> 32 & ~BIT(idx); + const u8 *addr = vif->addr; + struct { + u8 mode; + u8 force_clear; + u8 clear_bitmap[8]; + u8 entry_count; + u8 write; + + u8 index; + u8 bssid; + u8 addr[ETH_ALEN]; + } __packed req = { + .mode = !!mask || enable, + .entry_count = 1, + .write = 1, + + .index = idx * 2 + bssid, + }; + + if (bssid) + addr = vif->bss_conf.bssid; + + if (enable) + ether_addr_copy(req.addr, addr); + + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(MUAR_UPDATE), + &req, sizeof(req), true); +} + +static int +mt7615_mcu_add_dev(struct mt7615_phy *phy, struct ieee80211_vif *vif, + bool enable) +{ + struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv; + struct mt7615_dev *dev = phy->dev; + struct { + struct req_hdr { + u8 omac_idx; + u8 band_idx; + __le16 tlv_num; + u8 is_tlv_append; + u8 rsv[3]; + } __packed hdr; + struct req_tlv { + __le16 tag; + __le16 len; + u8 active; + u8 band_idx; + u8 omac_addr[ETH_ALEN]; + } __packed tlv; + } data = { + .hdr = { + .omac_idx = mvif->mt76.omac_idx, + .band_idx = mvif->mt76.band_idx, + .tlv_num = cpu_to_le16(1), + .is_tlv_append = 1, + }, + .tlv = { + .tag = cpu_to_le16(DEV_INFO_ACTIVE), + .len = cpu_to_le16(sizeof(struct req_tlv)), + .active = enable, + .band_idx = mvif->mt76.band_idx, + }, + }; + + if (mvif->mt76.omac_idx >= REPEATER_BSSID_START) + return mt7615_mcu_muar_config(dev, vif, false, enable); + + memcpy(data.tlv.omac_addr, vif->addr, ETH_ALEN); + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(DEV_INFO_UPDATE), + &data, sizeof(data), true); +} + +static int +mt7615_mcu_add_beacon_offload(struct mt7615_dev *dev, + struct ieee80211_hw *hw, + struct ieee80211_vif *vif, bool enable) +{ + struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv; + struct mt76_wcid *wcid = &dev->mt76.global_wcid; + struct ieee80211_mutable_offsets offs; + struct ieee80211_tx_info *info; + struct req { + u8 omac_idx; + u8 enable; + u8 wlan_idx; + u8 band_idx; + u8 pkt_type; + u8 need_pre_tbtt_int; + __le16 csa_ie_pos; + __le16 pkt_len; + __le16 tim_ie_pos; + u8 pkt[512]; + u8 csa_cnt; + /* bss color change */ + u8 bcc_cnt; + __le16 bcc_ie_pos; + } __packed req = { + .omac_idx = mvif->mt76.omac_idx, + .enable = enable, + .wlan_idx = wcid->idx, + .band_idx = mvif->mt76.band_idx, + }; + struct sk_buff *skb; + + if (!enable) + goto out; + + skb = ieee80211_beacon_get_template(hw, vif, &offs, 0); + if (!skb) + return -EINVAL; + + if (skb->len > 512 - MT_TXD_SIZE) { + dev_err(dev->mt76.dev, "Bcn size limit exceed\n"); + dev_kfree_skb(skb); + return -EINVAL; + } + + info = IEEE80211_SKB_CB(skb); + info->hw_queue |= FIELD_PREP(MT_TX_HW_QUEUE_PHY, mvif->mt76.band_idx); + + mt7615_mac_write_txwi(dev, (__le32 *)(req.pkt), skb, wcid, NULL, + 0, NULL, 0, true); + memcpy(req.pkt + MT_TXD_SIZE, skb->data, skb->len); + req.pkt_len = cpu_to_le16(MT_TXD_SIZE + skb->len); + req.tim_ie_pos = cpu_to_le16(MT_TXD_SIZE + offs.tim_offset); + if (offs.cntdwn_counter_offs[0]) { + u16 csa_offs; + + csa_offs = MT_TXD_SIZE + offs.cntdwn_counter_offs[0] - 4; + req.csa_ie_pos = cpu_to_le16(csa_offs); + req.csa_cnt = skb->data[offs.cntdwn_counter_offs[0]]; + } + dev_kfree_skb(skb); + +out: + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(BCN_OFFLOAD), &req, + sizeof(req), true); +} + +static int +mt7615_mcu_ctrl_pm_state(struct mt7615_dev *dev, int band, int state) +{ + return mt76_connac_mcu_set_pm(&dev->mt76, band, state); +} + +static int +mt7615_mcu_add_bss(struct mt7615_phy *phy, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, bool enable) +{ + struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv; + struct mt7615_dev *dev = phy->dev; + struct sk_buff *skb; + + if (mvif->mt76.omac_idx >= REPEATER_BSSID_START) + mt7615_mcu_muar_config(dev, vif, true, enable); + + skb = mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76, NULL); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + if (enable) + mt76_connac_mcu_bss_omac_tlv(skb, vif); + + mt76_connac_mcu_bss_basic_tlv(skb, vif, sta, phy->mt76, + mvif->sta.wcid.idx, enable); + + if (enable && mvif->mt76.omac_idx >= EXT_BSSID_START && + mvif->mt76.omac_idx < REPEATER_BSSID_START) + mt76_connac_mcu_bss_ext_tlv(skb, &mvif->mt76); + + return mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_EXT_CMD(BSS_INFO_UPDATE), true); +} + +static int +mt7615_mcu_wtbl_tx_ba(struct mt7615_dev *dev, + struct ieee80211_ampdu_params *params, + bool enable) +{ + struct mt7615_sta *msta = (struct mt7615_sta *)params->sta->drv_priv; + struct mt7615_vif *mvif = msta->vif; + struct wtbl_req_hdr *wtbl_hdr; + struct sk_buff *skb = NULL; + int err; + + wtbl_hdr = mt76_connac_mcu_alloc_wtbl_req(&dev->mt76, &msta->wcid, + WTBL_SET, NULL, &skb); + if (IS_ERR(wtbl_hdr)) + return PTR_ERR(wtbl_hdr); + + mt76_connac_mcu_wtbl_ba_tlv(&dev->mt76, skb, params, enable, true, + NULL, wtbl_hdr); + + err = mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_EXT_CMD(WTBL_UPDATE), true); + if (err < 0) + return err; + + skb = mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76, + &msta->wcid); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + mt76_connac_mcu_sta_ba_tlv(skb, params, enable, true); + + return mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_EXT_CMD(STA_REC_UPDATE), true); +} + +static int +mt7615_mcu_wtbl_rx_ba(struct mt7615_dev *dev, + struct ieee80211_ampdu_params *params, + bool enable) +{ + struct mt7615_sta *msta = (struct mt7615_sta *)params->sta->drv_priv; + struct mt7615_vif *mvif = msta->vif; + struct wtbl_req_hdr *wtbl_hdr; + struct sk_buff *skb; + int err; + + skb = mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76, + &msta->wcid); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + mt76_connac_mcu_sta_ba_tlv(skb, params, enable, false); + + err = mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_EXT_CMD(STA_REC_UPDATE), true); + if (err < 0 || !enable) + return err; + + skb = NULL; + wtbl_hdr = mt76_connac_mcu_alloc_wtbl_req(&dev->mt76, &msta->wcid, + WTBL_SET, NULL, &skb); + if (IS_ERR(wtbl_hdr)) + return PTR_ERR(wtbl_hdr); + + mt76_connac_mcu_wtbl_ba_tlv(&dev->mt76, skb, params, enable, false, + NULL, wtbl_hdr); + + return mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_EXT_CMD(WTBL_UPDATE), true); +} + +static int +mt7615_mcu_wtbl_sta_add(struct mt7615_phy *phy, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, bool enable) +{ + struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv; + struct sk_buff *skb, *sskb, *wskb = NULL; + struct ieee80211_link_sta *link_sta; + struct mt7615_dev *dev = phy->dev; + struct wtbl_req_hdr *wtbl_hdr; + struct mt7615_sta *msta; + bool new_entry = true; + int conn_state; + int cmd, err; + + msta = sta ? (struct mt7615_sta *)sta->drv_priv : &mvif->sta; + link_sta = sta ? &sta->deflink : NULL; + + sskb = mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76, + &msta->wcid); + if (IS_ERR(sskb)) + return PTR_ERR(sskb); + + if (!sta) { + if (mvif->sta_added) + new_entry = false; + else + mvif->sta_added = true; + } + conn_state = enable ? CONN_STATE_PORT_SECURE : CONN_STATE_DISCONNECT; + mt76_connac_mcu_sta_basic_tlv(&dev->mt76, sskb, &vif->bss_conf, + link_sta, conn_state, new_entry); + if (enable && sta) + mt76_connac_mcu_sta_tlv(phy->mt76, sskb, sta, vif, 0, + MT76_STA_INFO_STATE_ASSOC); + + wtbl_hdr = mt76_connac_mcu_alloc_wtbl_req(&dev->mt76, &msta->wcid, + WTBL_RESET_AND_SET, NULL, + &wskb); + if (IS_ERR(wtbl_hdr)) { + dev_kfree_skb(sskb); + return PTR_ERR(wtbl_hdr); + } + + if (enable) { + mt76_connac_mcu_wtbl_generic_tlv(&dev->mt76, wskb, vif, sta, + NULL, wtbl_hdr); + if (sta) + mt76_connac_mcu_wtbl_ht_tlv(&dev->mt76, wskb, sta, + NULL, wtbl_hdr, true, true); + mt76_connac_mcu_wtbl_hdr_trans_tlv(wskb, vif, &msta->wcid, + NULL, wtbl_hdr); + } + + cmd = enable ? MCU_EXT_CMD(WTBL_UPDATE) : MCU_EXT_CMD(STA_REC_UPDATE); + skb = enable ? wskb : sskb; + + err = mt76_mcu_skb_send_msg(&dev->mt76, skb, cmd, true); + if (err < 0) { + skb = enable ? sskb : wskb; + dev_kfree_skb(skb); + + return err; + } + + cmd = enable ? MCU_EXT_CMD(STA_REC_UPDATE) : MCU_EXT_CMD(WTBL_UPDATE); + skb = enable ? sskb : wskb; + + return mt76_mcu_skb_send_msg(&dev->mt76, skb, cmd, true); +} + +static int +mt7615_mcu_wtbl_update_hdr_trans(struct mt7615_dev *dev, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + return mt76_connac_mcu_wtbl_update_hdr_trans(&dev->mt76, vif, sta); +} + +static const struct mt7615_mcu_ops wtbl_update_ops = { + .add_beacon_offload = mt7615_mcu_add_beacon_offload, + .set_pm_state = mt7615_mcu_ctrl_pm_state, + .add_dev_info = mt7615_mcu_add_dev, + .add_bss_info = mt7615_mcu_add_bss, + .add_tx_ba = mt7615_mcu_wtbl_tx_ba, + .add_rx_ba = mt7615_mcu_wtbl_rx_ba, + .sta_add = mt7615_mcu_wtbl_sta_add, + .set_drv_ctrl = mt7615_mcu_drv_pmctrl, + .set_fw_ctrl = mt7615_mcu_fw_pmctrl, + .set_sta_decap_offload = mt7615_mcu_wtbl_update_hdr_trans, +}; + +static int +mt7615_mcu_sta_ba(struct mt7615_dev *dev, + struct ieee80211_ampdu_params *params, + bool enable, bool tx) +{ + struct mt7615_sta *msta = (struct mt7615_sta *)params->sta->drv_priv; + struct mt7615_vif *mvif = msta->vif; + struct wtbl_req_hdr *wtbl_hdr; + struct tlv *sta_wtbl; + struct sk_buff *skb; + + skb = mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76, + &msta->wcid); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + mt76_connac_mcu_sta_ba_tlv(skb, params, enable, tx); + + sta_wtbl = mt76_connac_mcu_add_tlv(skb, STA_REC_WTBL, sizeof(struct tlv)); + + wtbl_hdr = mt76_connac_mcu_alloc_wtbl_req(&dev->mt76, &msta->wcid, + WTBL_SET, sta_wtbl, &skb); + if (IS_ERR(wtbl_hdr)) + return PTR_ERR(wtbl_hdr); + + mt76_connac_mcu_wtbl_ba_tlv(&dev->mt76, skb, params, enable, tx, + sta_wtbl, wtbl_hdr); + + return mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_EXT_CMD(STA_REC_UPDATE), true); +} + +static int +mt7615_mcu_sta_tx_ba(struct mt7615_dev *dev, + struct ieee80211_ampdu_params *params, + bool enable) +{ + return mt7615_mcu_sta_ba(dev, params, enable, true); +} + +static int +mt7615_mcu_sta_rx_ba(struct mt7615_dev *dev, + struct ieee80211_ampdu_params *params, + bool enable) +{ + return mt7615_mcu_sta_ba(dev, params, enable, false); +} + +static int +__mt7615_mcu_add_sta(struct mt76_phy *phy, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, bool enable, int cmd, + bool offload_fw) +{ + struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv; + struct mt76_sta_cmd_info info = { + .sta = sta, + .vif = vif, + .offload_fw = offload_fw, + .enable = enable, + .newly = true, + .cmd = cmd, + }; + + info.wcid = sta ? (struct mt76_wcid *)sta->drv_priv : &mvif->sta.wcid; + return mt76_connac_mcu_sta_cmd(phy, &info); +} + +static int +mt7615_mcu_add_sta(struct mt7615_phy *phy, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, bool enable) +{ + return __mt7615_mcu_add_sta(phy->mt76, vif, sta, enable, + MCU_EXT_CMD(STA_REC_UPDATE), false); +} + +static int +mt7615_mcu_sta_update_hdr_trans(struct mt7615_dev *dev, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mt7615_sta *msta = (struct mt7615_sta *)sta->drv_priv; + + return mt76_connac_mcu_sta_update_hdr_trans(&dev->mt76, + vif, &msta->wcid, + MCU_EXT_CMD(STA_REC_UPDATE)); +} + +static const struct mt7615_mcu_ops sta_update_ops = { + .add_beacon_offload = mt7615_mcu_add_beacon_offload, + .set_pm_state = mt7615_mcu_ctrl_pm_state, + .add_dev_info = mt7615_mcu_add_dev, + .add_bss_info = mt7615_mcu_add_bss, + .add_tx_ba = mt7615_mcu_sta_tx_ba, + .add_rx_ba = mt7615_mcu_sta_rx_ba, + .sta_add = mt7615_mcu_add_sta, + .set_drv_ctrl = mt7615_mcu_drv_pmctrl, + .set_fw_ctrl = mt7615_mcu_fw_pmctrl, + .set_sta_decap_offload = mt7615_mcu_sta_update_hdr_trans, +}; + +static int +mt7615_mcu_uni_ctrl_pm_state(struct mt7615_dev *dev, int band, int state) +{ + return 0; +} + +static int +mt7615_mcu_uni_add_beacon_offload(struct mt7615_dev *dev, + struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + bool enable) +{ + struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv; + struct mt76_wcid *wcid = &dev->mt76.global_wcid; + struct ieee80211_mutable_offsets offs; + struct { + struct req_hdr { + u8 bss_idx; + u8 pad[3]; + } __packed hdr; + struct bcn_content_tlv { + __le16 tag; + __le16 len; + __le16 tim_ie_pos; + __le16 csa_ie_pos; + __le16 bcc_ie_pos; + /* 0: disable beacon offload + * 1: enable beacon offload + * 2: update probe respond offload + */ + u8 enable; + /* 0: legacy format (TXD + payload) + * 1: only cap field IE + */ + u8 type; + __le16 pkt_len; + u8 pkt[512]; + } __packed beacon_tlv; + } req = { + .hdr = { + .bss_idx = mvif->mt76.idx, + }, + .beacon_tlv = { + .tag = cpu_to_le16(UNI_BSS_INFO_BCN_CONTENT), + .len = cpu_to_le16(sizeof(struct bcn_content_tlv)), + .enable = enable, + }, + }; + struct sk_buff *skb; + + if (!enable) + goto out; + + skb = ieee80211_beacon_get_template(mt76_hw(dev), vif, &offs, 0); + if (!skb) + return -EINVAL; + + if (skb->len > 512 - MT_TXD_SIZE) { + dev_err(dev->mt76.dev, "beacon size limit exceed\n"); + dev_kfree_skb(skb); + return -EINVAL; + } + + mt7615_mac_write_txwi(dev, (__le32 *)(req.beacon_tlv.pkt), skb, + wcid, NULL, 0, NULL, 0, true); + memcpy(req.beacon_tlv.pkt + MT_TXD_SIZE, skb->data, skb->len); + req.beacon_tlv.pkt_len = cpu_to_le16(MT_TXD_SIZE + skb->len); + req.beacon_tlv.tim_ie_pos = cpu_to_le16(MT_TXD_SIZE + offs.tim_offset); + + if (offs.cntdwn_counter_offs[0]) { + u16 csa_offs; + + csa_offs = MT_TXD_SIZE + offs.cntdwn_counter_offs[0] - 4; + req.beacon_tlv.csa_ie_pos = cpu_to_le16(csa_offs); + } + dev_kfree_skb(skb); + +out: + return mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(BSS_INFO_UPDATE), + &req, sizeof(req), true); +} + +static int +mt7615_mcu_uni_add_dev(struct mt7615_phy *phy, struct ieee80211_vif *vif, + bool enable) +{ + struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv; + + return mt76_connac_mcu_uni_add_dev(phy->mt76, &vif->bss_conf, &mvif->mt76, + &mvif->sta.wcid, enable); +} + +static int +mt7615_mcu_uni_add_bss(struct mt7615_phy *phy, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, bool enable) +{ + struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv; + + return mt76_connac_mcu_uni_add_bss(phy->mt76, vif, &mvif->sta.wcid, + enable, NULL); +} + +static inline int +mt7615_mcu_uni_add_sta(struct mt7615_phy *phy, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, bool enable) +{ + return __mt7615_mcu_add_sta(phy->mt76, vif, sta, enable, + MCU_UNI_CMD(STA_REC_UPDATE), true); +} + +static int +mt7615_mcu_uni_tx_ba(struct mt7615_dev *dev, + struct ieee80211_ampdu_params *params, + bool enable) +{ + struct mt7615_sta *sta = (struct mt7615_sta *)params->sta->drv_priv; + + return mt76_connac_mcu_sta_ba(&dev->mt76, &sta->vif->mt76, params, + MCU_UNI_CMD(STA_REC_UPDATE), enable, + true); +} + +static int +mt7615_mcu_uni_rx_ba(struct mt7615_dev *dev, + struct ieee80211_ampdu_params *params, + bool enable) +{ + struct mt7615_sta *msta = (struct mt7615_sta *)params->sta->drv_priv; + struct mt7615_vif *mvif = msta->vif; + struct wtbl_req_hdr *wtbl_hdr; + struct tlv *sta_wtbl; + struct sk_buff *skb; + int err; + + skb = mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76, + &msta->wcid); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + mt76_connac_mcu_sta_ba_tlv(skb, params, enable, false); + + err = mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_UNI_CMD(STA_REC_UPDATE), true); + if (err < 0 || !enable) + return err; + + skb = mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76, + &msta->wcid); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + sta_wtbl = mt76_connac_mcu_add_tlv(skb, STA_REC_WTBL, + sizeof(struct tlv)); + + wtbl_hdr = mt76_connac_mcu_alloc_wtbl_req(&dev->mt76, &msta->wcid, + WTBL_SET, sta_wtbl, &skb); + if (IS_ERR(wtbl_hdr)) + return PTR_ERR(wtbl_hdr); + + mt76_connac_mcu_wtbl_ba_tlv(&dev->mt76, skb, params, enable, false, + sta_wtbl, wtbl_hdr); + + return mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_UNI_CMD(STA_REC_UPDATE), true); +} + +static int +mt7615_mcu_sta_uni_update_hdr_trans(struct mt7615_dev *dev, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mt7615_sta *msta = (struct mt7615_sta *)sta->drv_priv; + + return mt76_connac_mcu_sta_update_hdr_trans(&dev->mt76, + vif, &msta->wcid, + MCU_UNI_CMD(STA_REC_UPDATE)); +} + +static const struct mt7615_mcu_ops uni_update_ops = { + .add_beacon_offload = mt7615_mcu_uni_add_beacon_offload, + .set_pm_state = mt7615_mcu_uni_ctrl_pm_state, + .add_dev_info = mt7615_mcu_uni_add_dev, + .add_bss_info = mt7615_mcu_uni_add_bss, + .add_tx_ba = mt7615_mcu_uni_tx_ba, + .add_rx_ba = mt7615_mcu_uni_rx_ba, + .sta_add = mt7615_mcu_uni_add_sta, + .set_drv_ctrl = mt7615_mcu_lp_drv_pmctrl, + .set_fw_ctrl = mt7615_mcu_fw_pmctrl, + .set_sta_decap_offload = mt7615_mcu_sta_uni_update_hdr_trans, +}; + +int mt7615_mcu_restart(struct mt76_dev *dev) +{ + return mt76_mcu_send_msg(dev, MCU_CMD(RESTART_DL_REQ), NULL, 0, true); +} +EXPORT_SYMBOL_GPL(mt7615_mcu_restart); + +static int mt7615_load_patch(struct mt7615_dev *dev, u32 addr, const char *name) +{ + const struct mt7615_patch_hdr *hdr; + const struct firmware *fw = NULL; + int len, ret, sem; + + ret = firmware_request_nowarn(&fw, name, dev->mt76.dev); + if (ret) + return ret; + + if (!fw || !fw->data || fw->size < sizeof(*hdr)) { + dev_err(dev->mt76.dev, "Invalid firmware\n"); + ret = -EINVAL; + goto release_fw; + } + + sem = mt76_connac_mcu_patch_sem_ctrl(&dev->mt76, true); + switch (sem) { + case PATCH_IS_DL: + goto release_fw; + case PATCH_NOT_DL_SEM_SUCCESS: + break; + default: + dev_err(dev->mt76.dev, "Failed to get patch semaphore\n"); + ret = -EAGAIN; + goto release_fw; + } + + hdr = (const struct mt7615_patch_hdr *)(fw->data); + + dev_info(dev->mt76.dev, "HW/SW Version: 0x%x, Build Time: %.16s\n", + be32_to_cpu(hdr->hw_sw_ver), hdr->build_date); + + len = fw->size - sizeof(*hdr); + + ret = mt76_connac_mcu_init_download(&dev->mt76, addr, len, + DL_MODE_NEED_RSP); + if (ret) { + dev_err(dev->mt76.dev, "Download request failed\n"); + goto out; + } + + ret = mt76_mcu_send_firmware(&dev->mt76, MCU_CMD(FW_SCATTER), + fw->data + sizeof(*hdr), len); + if (ret) { + dev_err(dev->mt76.dev, "Failed to send firmware to device\n"); + goto out; + } + + ret = mt76_connac_mcu_start_patch(&dev->mt76); + if (ret) + dev_err(dev->mt76.dev, "Failed to start patch\n"); + +out: + sem = mt76_connac_mcu_patch_sem_ctrl(&dev->mt76, false); + switch (sem) { + case PATCH_REL_SEM_SUCCESS: + break; + default: + ret = -EAGAIN; + dev_err(dev->mt76.dev, "Failed to release patch semaphore\n"); + break; + } + +release_fw: + release_firmware(fw); + + return ret; +} + +static int +mt7615_mcu_send_ram_firmware(struct mt7615_dev *dev, + const struct mt7615_fw_trailer *hdr, + const u8 *data, bool is_cr4) +{ + int n_region = is_cr4 ? CR4_REGION_NUM : N9_REGION_NUM; + int err, i, offset = 0; + u32 len, addr, mode; + + for (i = 0; i < n_region; i++) { + mode = mt76_connac_mcu_gen_dl_mode(&dev->mt76, + hdr[i].feature_set, is_cr4); + len = le32_to_cpu(hdr[i].len) + IMG_CRC_LEN; + addr = le32_to_cpu(hdr[i].addr); + + err = mt76_connac_mcu_init_download(&dev->mt76, addr, len, + mode); + if (err) { + dev_err(dev->mt76.dev, "Download request failed\n"); + return err; + } + + err = mt76_mcu_send_firmware(&dev->mt76, MCU_CMD(FW_SCATTER), + data + offset, len); + if (err) { + dev_err(dev->mt76.dev, "Failed to send firmware to device\n"); + return err; + } + + offset += len; + } + + return 0; +} + +static int mt7615_load_n9(struct mt7615_dev *dev, const char *name) +{ + const struct mt7615_fw_trailer *hdr; + const struct firmware *fw; + int ret; + + ret = request_firmware(&fw, name, dev->mt76.dev); + if (ret) + return ret; + + if (!fw || !fw->data || fw->size < N9_REGION_NUM * sizeof(*hdr)) { + dev_err(dev->mt76.dev, "Invalid firmware\n"); + ret = -EINVAL; + goto out; + } + + hdr = (const struct mt7615_fw_trailer *)(fw->data + fw->size - + N9_REGION_NUM * sizeof(*hdr)); + + dev_info(dev->mt76.dev, "N9 Firmware Version: %.10s, Build Time: %.15s\n", + hdr->fw_ver, hdr->build_date); + + ret = mt7615_mcu_send_ram_firmware(dev, hdr, fw->data, false); + if (ret) + goto out; + + ret = mt76_connac_mcu_start_firmware(&dev->mt76, + le32_to_cpu(hdr->addr), + FW_START_OVERRIDE); + if (ret) { + dev_err(dev->mt76.dev, "Failed to start N9 firmware\n"); + goto out; + } + + snprintf(dev->mt76.hw->wiphy->fw_version, + sizeof(dev->mt76.hw->wiphy->fw_version), + "%.10s-%.15s", hdr->fw_ver, hdr->build_date); + + if (!is_mt7615(&dev->mt76)) { + dev->fw_ver = MT7615_FIRMWARE_V2; + dev->mcu_ops = &sta_update_ops; + } else { + dev->fw_ver = MT7615_FIRMWARE_V1; + dev->mcu_ops = &wtbl_update_ops; + } + +out: + release_firmware(fw); + return ret; +} + +static int mt7615_load_cr4(struct mt7615_dev *dev, const char *name) +{ + const struct mt7615_fw_trailer *hdr; + const struct firmware *fw; + int ret; + + ret = request_firmware(&fw, name, dev->mt76.dev); + if (ret) + return ret; + + if (!fw || !fw->data || fw->size < CR4_REGION_NUM * sizeof(*hdr)) { + dev_err(dev->mt76.dev, "Invalid firmware\n"); + ret = -EINVAL; + goto out; + } + + hdr = (const struct mt7615_fw_trailer *)(fw->data + fw->size - + CR4_REGION_NUM * sizeof(*hdr)); + + dev_info(dev->mt76.dev, "CR4 Firmware Version: %.10s, Build Time: %.15s\n", + hdr->fw_ver, hdr->build_date); + + ret = mt7615_mcu_send_ram_firmware(dev, hdr, fw->data, true); + if (ret) + goto out; + + ret = mt76_connac_mcu_start_firmware(&dev->mt76, 0, + FW_START_WORKING_PDA_CR4); + if (ret) { + dev_err(dev->mt76.dev, "Failed to start CR4 firmware\n"); + goto out; + } + +out: + release_firmware(fw); + + return ret; +} + +static int mt7615_load_ram(struct mt7615_dev *dev) +{ + int ret; + + ret = mt7615_load_n9(dev, MT7615_FIRMWARE_N9); + if (ret) + return ret; + + return mt7615_load_cr4(dev, MT7615_FIRMWARE_CR4); +} + +static int mt7615_load_firmware(struct mt7615_dev *dev) +{ + int ret; + u32 val; + + val = mt76_get_field(dev, MT_TOP_MISC2, MT_TOP_MISC2_FW_STATE); + + if (val != FW_STATE_FW_DOWNLOAD) { + dev_err(dev->mt76.dev, "Firmware is not ready for download\n"); + return -EIO; + } + + ret = mt7615_load_patch(dev, MT7615_PATCH_ADDRESS, MT7615_ROM_PATCH); + if (ret) + return ret; + + ret = mt7615_load_ram(dev); + if (ret) + return ret; + + if (!mt76_poll_msec(dev, MT_TOP_MISC2, MT_TOP_MISC2_FW_STATE, + FIELD_PREP(MT_TOP_MISC2_FW_STATE, + FW_STATE_RDY), 500)) { + dev_err(dev->mt76.dev, "Timeout for initializing firmware\n"); + return -EIO; + } + + return 0; +} + +static int mt7622_load_firmware(struct mt7615_dev *dev) +{ + int ret; + u32 val; + + mt76_set(dev, MT_WPDMA_GLO_CFG, MT_WPDMA_GLO_CFG_BYPASS_TX_SCH); + + val = mt76_get_field(dev, MT_TOP_OFF_RSV, MT_TOP_OFF_RSV_FW_STATE); + if (val != FW_STATE_FW_DOWNLOAD) { + dev_err(dev->mt76.dev, "Firmware is not ready for download\n"); + return -EIO; + } + + ret = mt7615_load_patch(dev, MT7622_PATCH_ADDRESS, MT7622_ROM_PATCH); + if (ret) + return ret; + + ret = mt7615_load_n9(dev, MT7622_FIRMWARE_N9); + if (ret) + return ret; + + if (!mt76_poll_msec(dev, MT_TOP_OFF_RSV, MT_TOP_OFF_RSV_FW_STATE, + FIELD_PREP(MT_TOP_OFF_RSV_FW_STATE, + FW_STATE_NORMAL_TRX), 1500)) { + dev_err(dev->mt76.dev, "Timeout for initializing firmware\n"); + return -EIO; + } + + mt76_clear(dev, MT_WPDMA_GLO_CFG, MT_WPDMA_GLO_CFG_BYPASS_TX_SCH); + + return 0; +} + +int mt7615_mcu_fw_log_2_host(struct mt7615_dev *dev, u8 ctrl) +{ + struct { + u8 ctrl_val; + u8 pad[3]; + } data = { + .ctrl_val = ctrl + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(FW_LOG_2_HOST), + &data, sizeof(data), true); +} + +static int mt7615_mcu_cal_cache_apply(struct mt7615_dev *dev) +{ + struct { + bool cache_enable; + u8 pad[3]; + } data = { + .cache_enable = true + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(CAL_CACHE), &data, + sizeof(data), false); +} + +static int mt7663_load_n9(struct mt7615_dev *dev, const char *name) +{ + u32 offset = 0, override_addr = 0, flag = FW_START_DLYCAL; + const struct mt76_connac2_fw_trailer *hdr; + const struct mt7663_fw_buf *buf; + const struct firmware *fw; + const u8 *base_addr; + int i, ret; + + ret = request_firmware(&fw, name, dev->mt76.dev); + if (ret) + return ret; + + if (!fw || !fw->data || fw->size < FW_V3_COMMON_TAILER_SIZE) { + dev_err(dev->mt76.dev, "Invalid firmware\n"); + ret = -EINVAL; + goto out; + } + + hdr = (const void *)(fw->data + fw->size - FW_V3_COMMON_TAILER_SIZE); + dev_info(dev->mt76.dev, "N9 Firmware Version: %.10s, Build Time: %.15s\n", + hdr->fw_ver, hdr->build_date); + dev_info(dev->mt76.dev, "Region number: 0x%x\n", hdr->n_region); + + base_addr = fw->data + fw->size - FW_V3_COMMON_TAILER_SIZE; + for (i = 0; i < hdr->n_region; i++) { + u32 shift = (hdr->n_region - i) * FW_V3_REGION_TAILER_SIZE; + u32 len, addr, mode; + + dev_info(dev->mt76.dev, "Parsing tailer Region: %d\n", i); + + buf = (const struct mt7663_fw_buf *)(base_addr - shift); + mode = mt76_connac_mcu_gen_dl_mode(&dev->mt76, + buf->feature_set, false); + addr = le32_to_cpu(buf->img_dest_addr); + len = le32_to_cpu(buf->img_size); + + ret = mt76_connac_mcu_init_download(&dev->mt76, addr, len, + mode); + if (ret) { + dev_err(dev->mt76.dev, "Download request failed\n"); + goto out; + } + + ret = mt76_mcu_send_firmware(&dev->mt76, MCU_CMD(FW_SCATTER), + fw->data + offset, len); + if (ret) { + dev_err(dev->mt76.dev, "Failed to send firmware\n"); + goto out; + } + + offset += le32_to_cpu(buf->img_size); + if (buf->feature_set & DL_MODE_VALID_RAM_ENTRY) { + override_addr = le32_to_cpu(buf->img_dest_addr); + dev_info(dev->mt76.dev, "Region %d, override_addr = 0x%08x\n", + i, override_addr); + } + } + + if (override_addr) + flag |= FW_START_OVERRIDE; + + dev_info(dev->mt76.dev, "override_addr = 0x%08x, option = %d\n", + override_addr, flag); + + ret = mt76_connac_mcu_start_firmware(&dev->mt76, override_addr, flag); + if (ret) { + dev_err(dev->mt76.dev, "Failed to start N9 firmware\n"); + goto out; + } + + snprintf(dev->mt76.hw->wiphy->fw_version, + sizeof(dev->mt76.hw->wiphy->fw_version), + "%.10s-%.15s", hdr->fw_ver, hdr->build_date); + +out: + release_firmware(fw); + + return ret; +} + +static int +mt7663_load_rom_patch(struct mt7615_dev *dev, const char **n9_firmware) +{ + const char *selected_rom, *secondary_rom = MT7663_ROM_PATCH; + const char *primary_rom = MT7663_OFFLOAD_ROM_PATCH; + int ret; + + if (!prefer_offload_fw) { + secondary_rom = MT7663_OFFLOAD_ROM_PATCH; + primary_rom = MT7663_ROM_PATCH; + } + selected_rom = primary_rom; + + ret = mt7615_load_patch(dev, MT7663_PATCH_ADDRESS, primary_rom); + if (ret) { + dev_info(dev->mt76.dev, "%s not found, switching to %s", + primary_rom, secondary_rom); + ret = mt7615_load_patch(dev, MT7663_PATCH_ADDRESS, + secondary_rom); + if (ret) { + dev_err(dev->mt76.dev, "failed to load %s", + secondary_rom); + return ret; + } + selected_rom = secondary_rom; + } + + if (!strcmp(selected_rom, MT7663_OFFLOAD_ROM_PATCH)) { + *n9_firmware = MT7663_OFFLOAD_FIRMWARE_N9; + dev->fw_ver = MT7615_FIRMWARE_V3; + dev->mcu_ops = &uni_update_ops; + } else { + *n9_firmware = MT7663_FIRMWARE_N9; + dev->fw_ver = MT7615_FIRMWARE_V2; + dev->mcu_ops = &sta_update_ops; + } + + return 0; +} + +int __mt7663_load_firmware(struct mt7615_dev *dev) +{ + const char *n9_firmware; + int ret; + + ret = mt76_get_field(dev, MT_CONN_ON_MISC, MT_TOP_MISC2_FW_N9_RDY); + if (ret) { + dev_dbg(dev->mt76.dev, "Firmware is already download\n"); + return -EIO; + } + + ret = mt7663_load_rom_patch(dev, &n9_firmware); + if (ret) + return ret; + + ret = mt7663_load_n9(dev, n9_firmware); + if (ret) + return ret; + + if (!mt76_poll_msec(dev, MT_CONN_ON_MISC, MT_TOP_MISC2_FW_N9_RDY, + MT_TOP_MISC2_FW_N9_RDY, 1500)) { + ret = mt76_get_field(dev, MT_CONN_ON_MISC, + MT7663_TOP_MISC2_FW_STATE); + dev_err(dev->mt76.dev, "Timeout for initializing firmware\n"); + return -EIO; + } + +#ifdef CONFIG_PM + if (mt7615_firmware_offload(dev)) + dev->mt76.hw->wiphy->wowlan = &mt76_connac_wowlan_support; +#endif /* CONFIG_PM */ + + dev_dbg(dev->mt76.dev, "Firmware init done\n"); + + return 0; +} +EXPORT_SYMBOL_GPL(__mt7663_load_firmware); + +static int mt7663_load_firmware(struct mt7615_dev *dev) +{ + int ret; + + mt76_set(dev, MT_WPDMA_GLO_CFG, MT_WPDMA_GLO_CFG_BYPASS_TX_SCH); + + ret = __mt7663_load_firmware(dev); + if (ret) + return ret; + + mt76_clear(dev, MT_WPDMA_GLO_CFG, MT_WPDMA_GLO_CFG_BYPASS_TX_SCH); + + return 0; +} + +int mt7615_mcu_init(struct mt7615_dev *dev) +{ + static const struct mt76_mcu_ops mt7615_mcu_ops = { + .headroom = sizeof(struct mt7615_mcu_txd), + .mcu_skb_send_msg = mt7615_mcu_send_message, + .mcu_parse_response = mt7615_mcu_parse_response, + }; + int ret; + + dev->mt76.mcu_ops = &mt7615_mcu_ops; + + ret = mt7615_mcu_drv_pmctrl(dev); + if (ret) + return ret; + + switch (mt76_chip(&dev->mt76)) { + case 0x7622: + ret = mt7622_load_firmware(dev); + break; + case 0x7663: + ret = mt7663_load_firmware(dev); + break; + default: + ret = mt7615_load_firmware(dev); + break; + } + if (ret) + return ret; + + mt76_queue_tx_cleanup(dev, dev->mt76.q_mcu[MT_MCUQ_FWDL], false); + dev_dbg(dev->mt76.dev, "Firmware init done\n"); + set_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state); + + if (dev->dbdc_support) { + ret = mt7615_mcu_cal_cache_apply(dev); + if (ret) + return ret; + } + + return mt7615_mcu_fw_log_2_host(dev, 0); +} +EXPORT_SYMBOL_GPL(mt7615_mcu_init); + +void mt7615_mcu_exit(struct mt7615_dev *dev) +{ + mt7615_mcu_restart(&dev->mt76); + mt7615_mcu_set_fw_ctrl(dev); + skb_queue_purge(&dev->mt76.mcu.res_q); +} +EXPORT_SYMBOL_GPL(mt7615_mcu_exit); + +int mt7615_mcu_set_eeprom(struct mt7615_dev *dev) +{ + struct { + u8 buffer_mode; + u8 content_format; + __le16 len; + } __packed req_hdr = { + .buffer_mode = 1, + }; + u8 *eep = (u8 *)dev->mt76.eeprom.data; + struct sk_buff *skb; + int eep_len, offset; + + switch (mt76_chip(&dev->mt76)) { + case 0x7622: + eep_len = MT7622_EE_MAX - MT_EE_NIC_CONF_0; + offset = MT_EE_NIC_CONF_0; + break; + case 0x7663: + eep_len = MT7663_EE_MAX - MT_EE_CHIP_ID; + req_hdr.content_format = 1; + offset = MT_EE_CHIP_ID; + break; + default: + eep_len = MT7615_EE_MAX - MT_EE_NIC_CONF_0; + offset = MT_EE_NIC_CONF_0; + break; + } + + req_hdr.len = cpu_to_le16(eep_len); + + skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, sizeof(req_hdr) + eep_len); + if (!skb) + return -ENOMEM; + + skb_put_data(skb, &req_hdr, sizeof(req_hdr)); + skb_put_data(skb, eep + offset, eep_len); + + return mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_EXT_CMD(EFUSE_BUFFER_MODE), true); +} + +int mt7615_mcu_set_wmm(struct mt7615_dev *dev, u8 queue, + const struct ieee80211_tx_queue_params *params) +{ +#define WMM_AIFS_SET BIT(0) +#define WMM_CW_MIN_SET BIT(1) +#define WMM_CW_MAX_SET BIT(2) +#define WMM_TXOP_SET BIT(3) +#define WMM_PARAM_SET (WMM_AIFS_SET | WMM_CW_MIN_SET | \ + WMM_CW_MAX_SET | WMM_TXOP_SET) + struct req_data { + u8 number; + u8 rsv[3]; + u8 queue; + u8 valid; + u8 aifs; + u8 cw_min; + __le16 cw_max; + __le16 txop; + } __packed req = { + .number = 1, + .queue = queue, + .valid = WMM_PARAM_SET, + .aifs = params->aifs, + .cw_min = 5, + .cw_max = cpu_to_le16(10), + .txop = cpu_to_le16(params->txop), + }; + + if (params->cw_min) + req.cw_min = fls(params->cw_min); + if (params->cw_max) + req.cw_max = cpu_to_le16(fls(params->cw_max)); + + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(EDCA_UPDATE), + &req, sizeof(req), true); +} + +int mt7615_mcu_set_dbdc(struct mt7615_dev *dev) +{ + struct mt7615_phy *ext_phy = mt7615_ext_phy(dev); + struct dbdc_entry { + u8 type; + u8 index; + u8 band; + u8 _rsv; + }; + struct { + u8 enable; + u8 num; + u8 _rsv[2]; + struct dbdc_entry entry[64]; + } req = { + .enable = !!ext_phy, + }; + int i; + + if (!ext_phy) + goto out; + +#define ADD_DBDC_ENTRY(_type, _idx, _band) \ + do { \ + req.entry[req.num].type = _type; \ + req.entry[req.num].index = _idx; \ + req.entry[req.num++].band = _band; \ + } while (0) + + for (i = 0; i < 4; i++) { + bool band = !!(ext_phy->omac_mask & BIT_ULL(i)); + + ADD_DBDC_ENTRY(DBDC_TYPE_BSS, i, band); + } + + for (i = 0; i < 14; i++) { + bool band = !!(ext_phy->omac_mask & BIT_ULL(0x11 + i)); + + ADD_DBDC_ENTRY(DBDC_TYPE_MBSS, i, band); + } + + ADD_DBDC_ENTRY(DBDC_TYPE_MU, 0, 1); + + for (i = 0; i < 3; i++) + ADD_DBDC_ENTRY(DBDC_TYPE_BF, i, 1); + + ADD_DBDC_ENTRY(DBDC_TYPE_WMM, 0, 0); + ADD_DBDC_ENTRY(DBDC_TYPE_WMM, 1, 0); + ADD_DBDC_ENTRY(DBDC_TYPE_WMM, 2, 1); + ADD_DBDC_ENTRY(DBDC_TYPE_WMM, 3, 1); + + ADD_DBDC_ENTRY(DBDC_TYPE_MGMT, 0, 0); + ADD_DBDC_ENTRY(DBDC_TYPE_MGMT, 1, 1); + +out: + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(DBDC_CTRL), &req, + sizeof(req), true); +} + +int mt7615_mcu_set_fcc5_lpn(struct mt7615_dev *dev, int val) +{ + struct { + __le16 tag; + __le16 min_lpn; + } req = { + .tag = cpu_to_le16(0x1), + .min_lpn = cpu_to_le16(val), + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(SET_RADAR_TH), + &req, sizeof(req), true); +} + +int mt7615_mcu_set_pulse_th(struct mt7615_dev *dev, + const struct mt7615_dfs_pulse *pulse) +{ + struct { + __le16 tag; + __le32 max_width; /* us */ + __le32 max_pwr; /* dbm */ + __le32 min_pwr; /* dbm */ + __le32 min_stgr_pri; /* us */ + __le32 max_stgr_pri; /* us */ + __le32 min_cr_pri; /* us */ + __le32 max_cr_pri; /* us */ + } req = { + .tag = cpu_to_le16(0x3), +#define __req_field(field) .field = cpu_to_le32(pulse->field) + __req_field(max_width), + __req_field(max_pwr), + __req_field(min_pwr), + __req_field(min_stgr_pri), + __req_field(max_stgr_pri), + __req_field(min_cr_pri), + __req_field(max_cr_pri), +#undef __req_field + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(SET_RADAR_TH), + &req, sizeof(req), true); +} + +int mt7615_mcu_set_radar_th(struct mt7615_dev *dev, int index, + const struct mt7615_dfs_pattern *pattern) +{ + struct { + __le16 tag; + __le16 radar_type; + u8 enb; + u8 stgr; + u8 min_crpn; + u8 max_crpn; + u8 min_crpr; + u8 min_pw; + u8 max_pw; + __le32 min_pri; + __le32 max_pri; + u8 min_crbn; + u8 max_crbn; + u8 min_stgpn; + u8 max_stgpn; + u8 min_stgpr; + } req = { + .tag = cpu_to_le16(0x2), + .radar_type = cpu_to_le16(index), +#define __req_field_u8(field) .field = pattern->field +#define __req_field_u32(field) .field = cpu_to_le32(pattern->field) + __req_field_u8(enb), + __req_field_u8(stgr), + __req_field_u8(min_crpn), + __req_field_u8(max_crpn), + __req_field_u8(min_crpr), + __req_field_u8(min_pw), + __req_field_u8(max_pw), + __req_field_u32(min_pri), + __req_field_u32(max_pri), + __req_field_u8(min_crbn), + __req_field_u8(max_crbn), + __req_field_u8(min_stgpn), + __req_field_u8(max_stgpn), + __req_field_u8(min_stgpr), +#undef __req_field_u8 +#undef __req_field_u32 + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(SET_RADAR_TH), + &req, sizeof(req), true); +} + +int mt7615_mcu_rdd_send_pattern(struct mt7615_dev *dev) +{ + struct { + u8 pulse_num; + u8 rsv[3]; + struct { + __le32 start_time; + __le16 width; + __le16 power; + } pattern[32]; + } req = { + .pulse_num = dev->radar_pattern.n_pulses, + }; + u32 start_time = ktime_to_ms(ktime_get_boottime()); + int i; + + if (dev->radar_pattern.n_pulses > ARRAY_SIZE(req.pattern)) + return -EINVAL; + + /* TODO: add some noise here */ + for (i = 0; i < dev->radar_pattern.n_pulses; i++) { + u32 ts = start_time + i * dev->radar_pattern.period; + + req.pattern[i].width = cpu_to_le16(dev->radar_pattern.width); + req.pattern[i].power = cpu_to_le16(dev->radar_pattern.power); + req.pattern[i].start_time = cpu_to_le32(ts); + } + + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(SET_RDD_PATTERN), + &req, sizeof(req), false); +} + +static void mt7615_mcu_set_txpower_sku(struct mt7615_phy *phy, u8 *sku) +{ + struct mt76_phy *mphy = phy->mt76; + struct ieee80211_hw *hw = mphy->hw; + struct mt76_power_limits limits; + s8 *limits_array = (s8 *)&limits; + int n_chains = hweight8(mphy->antenna_mask); + int tx_power = hw->conf.power_level * 2; + int i; + static const u8 sku_mapping[] = { +#define SKU_FIELD(_type, _field) \ + [MT_SKU_##_type] = offsetof(struct mt76_power_limits, _field) + SKU_FIELD(CCK_1_2, cck[0]), + SKU_FIELD(CCK_55_11, cck[2]), + SKU_FIELD(OFDM_6_9, ofdm[0]), + SKU_FIELD(OFDM_12_18, ofdm[2]), + SKU_FIELD(OFDM_24_36, ofdm[4]), + SKU_FIELD(OFDM_48, ofdm[6]), + SKU_FIELD(OFDM_54, ofdm[7]), + SKU_FIELD(HT20_0_8, mcs[0][0]), + SKU_FIELD(HT20_32, ofdm[0]), + SKU_FIELD(HT20_1_2_9_10, mcs[0][1]), + SKU_FIELD(HT20_3_4_11_12, mcs[0][3]), + SKU_FIELD(HT20_5_13, mcs[0][5]), + SKU_FIELD(HT20_6_14, mcs[0][6]), + SKU_FIELD(HT20_7_15, mcs[0][7]), + SKU_FIELD(HT40_0_8, mcs[1][0]), + SKU_FIELD(HT40_32, ofdm[0]), + SKU_FIELD(HT40_1_2_9_10, mcs[1][1]), + SKU_FIELD(HT40_3_4_11_12, mcs[1][3]), + SKU_FIELD(HT40_5_13, mcs[1][5]), + SKU_FIELD(HT40_6_14, mcs[1][6]), + SKU_FIELD(HT40_7_15, mcs[1][7]), + SKU_FIELD(VHT20_0, mcs[0][0]), + SKU_FIELD(VHT20_1_2, mcs[0][1]), + SKU_FIELD(VHT20_3_4, mcs[0][3]), + SKU_FIELD(VHT20_5_6, mcs[0][5]), + SKU_FIELD(VHT20_7, mcs[0][7]), + SKU_FIELD(VHT20_8, mcs[0][8]), + SKU_FIELD(VHT20_9, mcs[0][9]), + SKU_FIELD(VHT40_0, mcs[1][0]), + SKU_FIELD(VHT40_1_2, mcs[1][1]), + SKU_FIELD(VHT40_3_4, mcs[1][3]), + SKU_FIELD(VHT40_5_6, mcs[1][5]), + SKU_FIELD(VHT40_7, mcs[1][7]), + SKU_FIELD(VHT40_8, mcs[1][8]), + SKU_FIELD(VHT40_9, mcs[1][9]), + SKU_FIELD(VHT80_0, mcs[2][0]), + SKU_FIELD(VHT80_1_2, mcs[2][1]), + SKU_FIELD(VHT80_3_4, mcs[2][3]), + SKU_FIELD(VHT80_5_6, mcs[2][5]), + SKU_FIELD(VHT80_7, mcs[2][7]), + SKU_FIELD(VHT80_8, mcs[2][8]), + SKU_FIELD(VHT80_9, mcs[2][9]), + SKU_FIELD(VHT160_0, mcs[3][0]), + SKU_FIELD(VHT160_1_2, mcs[3][1]), + SKU_FIELD(VHT160_3_4, mcs[3][3]), + SKU_FIELD(VHT160_5_6, mcs[3][5]), + SKU_FIELD(VHT160_7, mcs[3][7]), + SKU_FIELD(VHT160_8, mcs[3][8]), + SKU_FIELD(VHT160_9, mcs[3][9]), +#undef SKU_FIELD + }; + + tx_power = mt76_get_sar_power(mphy, mphy->chandef.chan, tx_power); + tx_power -= mt76_tx_power_path_delta(n_chains); + tx_power = mt76_get_rate_power_limits(mphy, mphy->chandef.chan, + &limits, tx_power); + mphy->txpower_cur = tx_power; + + if (is_mt7663(mphy->dev)) { + memset(sku, tx_power, MT_SKU_4SS_DELTA + 1); + return; + } + + for (i = 0; i < MT_SKU_1SS_DELTA; i++) + sku[i] = limits_array[sku_mapping[i]]; + + for (i = 0; i < 4; i++) { + int delta = 0; + + if (i < n_chains - 1) + delta = mt76_tx_power_path_delta(n_chains) - + mt76_tx_power_path_delta(i + 1); + sku[MT_SKU_1SS_DELTA + i] = delta; + } +} + +static u8 mt7615_mcu_chan_bw(struct cfg80211_chan_def *chandef) +{ + static const u8 width_to_bw[] = { + [NL80211_CHAN_WIDTH_40] = CMD_CBW_40MHZ, + [NL80211_CHAN_WIDTH_80] = CMD_CBW_80MHZ, + [NL80211_CHAN_WIDTH_80P80] = CMD_CBW_8080MHZ, + [NL80211_CHAN_WIDTH_160] = CMD_CBW_160MHZ, + [NL80211_CHAN_WIDTH_5] = CMD_CBW_5MHZ, + [NL80211_CHAN_WIDTH_10] = CMD_CBW_10MHZ, + [NL80211_CHAN_WIDTH_20] = CMD_CBW_20MHZ, + [NL80211_CHAN_WIDTH_20_NOHT] = CMD_CBW_20MHZ, + }; + + if (chandef->width >= ARRAY_SIZE(width_to_bw)) + return 0; + + return width_to_bw[chandef->width]; +} + +int mt7615_mcu_set_chan_info(struct mt7615_phy *phy, int cmd) +{ + struct mt7615_dev *dev = phy->dev; + struct cfg80211_chan_def *chandef = &phy->mt76->chandef; + int freq1 = chandef->center_freq1, freq2 = chandef->center_freq2; + struct { + u8 control_chan; + u8 center_chan; + u8 bw; + u8 tx_streams; + u8 rx_streams_mask; + u8 switch_reason; + u8 band_idx; + /* for 80+80 only */ + u8 center_chan2; + __le16 cac_case; + u8 channel_band; + u8 rsv0; + __le32 outband_freq; + u8 txpower_drop; + u8 rsv1[3]; + u8 txpower_sku[53]; + u8 rsv2[3]; + } req = { + .control_chan = chandef->chan->hw_value, + .center_chan = ieee80211_frequency_to_channel(freq1), + .tx_streams = hweight8(phy->mt76->antenna_mask), + .rx_streams_mask = phy->mt76->chainmask, + .center_chan2 = ieee80211_frequency_to_channel(freq2), + }; + + if (cmd == MCU_EXT_CMD(SET_RX_PATH) || + phy->mt76->hw->conf.flags & IEEE80211_CONF_MONITOR) + req.switch_reason = CH_SWITCH_NORMAL; + else if (phy->mt76->offchannel) + req.switch_reason = CH_SWITCH_SCAN_BYPASS_DPD; + else if (!cfg80211_reg_can_beacon(phy->mt76->hw->wiphy, chandef, + NL80211_IFTYPE_AP)) + req.switch_reason = CH_SWITCH_DFS; + else + req.switch_reason = CH_SWITCH_NORMAL; + + req.band_idx = phy != &dev->phy; + req.bw = mt7615_mcu_chan_bw(chandef); + + if (mt76_testmode_enabled(phy->mt76)) + memset(req.txpower_sku, 0x3f, 49); + else + mt7615_mcu_set_txpower_sku(phy, req.txpower_sku); + + return mt76_mcu_send_msg(&dev->mt76, cmd, &req, sizeof(req), true); +} + +int mt7615_mcu_get_temperature(struct mt7615_dev *dev) +{ + struct { + u8 action; + u8 rsv[3]; + } req = {}; + + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(THERMAL_CTRL), + &req, sizeof(req), true); +} + +int mt7615_mcu_set_test_param(struct mt7615_dev *dev, u8 param, bool test_mode, + u32 val) +{ + struct { + u8 test_mode_en; + u8 param_idx; + u8 _rsv[2]; + + __le32 value; + + u8 pad[8]; + } req = { + .test_mode_en = test_mode, + .param_idx = param, + .value = cpu_to_le32(val), + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(ATE_CTRL), + &req, sizeof(req), false); +} + +int mt7615_mcu_set_sku_en(struct mt7615_phy *phy, bool enable) +{ + struct mt7615_dev *dev = phy->dev; + struct { + u8 format_id; + u8 sku_enable; + u8 band_idx; + u8 rsv; + } req = { + .format_id = 0, + .band_idx = phy != &dev->phy, + .sku_enable = enable, + }; + + return mt76_mcu_send_msg(&dev->mt76, + MCU_EXT_CMD(TX_POWER_FEATURE_CTRL), + &req, sizeof(req), true); +} + +static int mt7615_find_freq_idx(const u16 *freqs, int n_freqs, u16 cur) +{ + int i; + + for (i = 0; i < n_freqs; i++) + if (cur == freqs[i]) + return i; + + return -1; +} + +static int mt7615_dcoc_freq_idx(u16 freq, u8 bw) +{ + static const u16 freq_list[] = { + 4980, 5805, 5905, 5190, + 5230, 5270, 5310, 5350, + 5390, 5430, 5470, 5510, + 5550, 5590, 5630, 5670, + 5710, 5755, 5795, 5835, + 5875, 5210, 5290, 5370, + 5450, 5530, 5610, 5690, + 5775, 5855 + }; + static const u16 freq_bw40[] = { + 5190, 5230, 5270, 5310, + 5350, 5390, 5430, 5470, + 5510, 5550, 5590, 5630, + 5670, 5710, 5755, 5795, + 5835, 5875 + }; + int offset_2g = ARRAY_SIZE(freq_list); + int idx; + + if (freq < 4000) { + if (freq < 2427) + return offset_2g; + if (freq < 2442) + return offset_2g + 1; + if (freq < 2457) + return offset_2g + 2; + + return offset_2g + 3; + } + + switch (bw) { + case NL80211_CHAN_WIDTH_80: + case NL80211_CHAN_WIDTH_80P80: + case NL80211_CHAN_WIDTH_160: + break; + default: + idx = mt7615_find_freq_idx(freq_bw40, ARRAY_SIZE(freq_bw40), + freq + 10); + if (idx >= 0) { + freq = freq_bw40[idx]; + break; + } + + idx = mt7615_find_freq_idx(freq_bw40, ARRAY_SIZE(freq_bw40), + freq - 10); + if (idx >= 0) { + freq = freq_bw40[idx]; + break; + } + fallthrough; + case NL80211_CHAN_WIDTH_40: + idx = mt7615_find_freq_idx(freq_bw40, ARRAY_SIZE(freq_bw40), + freq); + if (idx >= 0) + break; + + return -1; + + } + + return mt7615_find_freq_idx(freq_list, ARRAY_SIZE(freq_list), freq); +} + +int mt7615_mcu_apply_rx_dcoc(struct mt7615_phy *phy) +{ + struct mt7615_dev *dev = phy->dev; + struct cfg80211_chan_def *chandef = &phy->mt76->chandef; + int freq2 = chandef->center_freq2; + int ret; + struct { + u8 direction; + u8 runtime_calibration; + u8 _rsv[2]; + + __le16 center_freq; + u8 bw; + u8 band; + u8 is_freq2; + u8 success; + u8 dbdc_en; + + u8 _rsv2; + + struct { + __le32 sx0_i_lna[4]; + __le32 sx0_q_lna[4]; + + __le32 sx2_i_lna[4]; + __le32 sx2_q_lna[4]; + } dcoc_data[4]; + } req = { + .direction = 1, + + .bw = mt7615_mcu_chan_bw(chandef), + .band = chandef->center_freq1 > 4000, + .dbdc_en = !!dev->mt76.phys[MT_BAND1], + }; + u16 center_freq = chandef->center_freq1; + int freq_idx; + u8 *eep = dev->mt76.eeprom.data; + + if (!(eep[MT_EE_CALDATA_FLASH] & MT_EE_CALDATA_FLASH_RX_CAL)) + return 0; + + if (chandef->width == NL80211_CHAN_WIDTH_160) { + freq2 = center_freq + 40; + center_freq -= 40; + } + +again: + req.runtime_calibration = 1; + freq_idx = mt7615_dcoc_freq_idx(center_freq, chandef->width); + if (freq_idx < 0) + goto out; + + memcpy(req.dcoc_data, eep + MT7615_EEPROM_DCOC_OFFSET + + freq_idx * MT7615_EEPROM_DCOC_SIZE, + sizeof(req.dcoc_data)); + req.runtime_calibration = 0; + +out: + req.center_freq = cpu_to_le16(center_freq); + ret = mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(RXDCOC_CAL), &req, + sizeof(req), true); + + if ((chandef->width == NL80211_CHAN_WIDTH_80P80 || + chandef->width == NL80211_CHAN_WIDTH_160) && !req.is_freq2) { + req.is_freq2 = true; + center_freq = freq2; + goto again; + } + + return ret; +} + +static int mt7615_dpd_freq_idx(u16 freq, u8 bw) +{ + static const u16 freq_list[] = { + 4920, 4940, 4960, 4980, + 5040, 5060, 5080, 5180, + 5200, 5220, 5240, 5260, + 5280, 5300, 5320, 5340, + 5360, 5380, 5400, 5420, + 5440, 5460, 5480, 5500, + 5520, 5540, 5560, 5580, + 5600, 5620, 5640, 5660, + 5680, 5700, 5720, 5745, + 5765, 5785, 5805, 5825, + 5845, 5865, 5885, 5905 + }; + int offset_2g = ARRAY_SIZE(freq_list); + int idx; + + if (freq < 4000) { + if (freq < 2432) + return offset_2g; + if (freq < 2457) + return offset_2g + 1; + + return offset_2g + 2; + } + + if (bw != NL80211_CHAN_WIDTH_20) { + idx = mt7615_find_freq_idx(freq_list, ARRAY_SIZE(freq_list), + freq + 10); + if (idx >= 0) + return idx; + + idx = mt7615_find_freq_idx(freq_list, ARRAY_SIZE(freq_list), + freq - 10); + if (idx >= 0) + return idx; + } + + return mt7615_find_freq_idx(freq_list, ARRAY_SIZE(freq_list), freq); +} + + +int mt7615_mcu_apply_tx_dpd(struct mt7615_phy *phy) +{ + struct mt7615_dev *dev = phy->dev; + struct cfg80211_chan_def *chandef = &phy->mt76->chandef; + int freq2 = chandef->center_freq2; + int ret; + struct { + u8 direction; + u8 runtime_calibration; + u8 _rsv[2]; + + __le16 center_freq; + u8 bw; + u8 band; + u8 is_freq2; + u8 success; + u8 dbdc_en; + + u8 _rsv2; + + struct { + struct { + u32 dpd_g0; + u8 data[32]; + } wf0, wf1; + + struct { + u32 dpd_g0_prim; + u32 dpd_g0_sec; + u8 data_prim[32]; + u8 data_sec[32]; + } wf2, wf3; + } dpd_data; + } req = { + .direction = 1, + + .bw = mt7615_mcu_chan_bw(chandef), + .band = chandef->center_freq1 > 4000, + .dbdc_en = !!dev->mt76.phys[MT_BAND1], + }; + u16 center_freq = chandef->center_freq1; + int freq_idx; + u8 *eep = dev->mt76.eeprom.data; + + if (!(eep[MT_EE_CALDATA_FLASH] & MT_EE_CALDATA_FLASH_TX_DPD)) + return 0; + + if (chandef->width == NL80211_CHAN_WIDTH_160) { + freq2 = center_freq + 40; + center_freq -= 40; + } + +again: + req.runtime_calibration = 1; + freq_idx = mt7615_dpd_freq_idx(center_freq, chandef->width); + if (freq_idx < 0) + goto out; + + memcpy(&req.dpd_data, eep + MT7615_EEPROM_TXDPD_OFFSET + + freq_idx * MT7615_EEPROM_TXDPD_SIZE, + sizeof(req.dpd_data)); + req.runtime_calibration = 0; + +out: + req.center_freq = cpu_to_le16(center_freq); + ret = mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(TXDPD_CAL), + &req, sizeof(req), true); + + if ((chandef->width == NL80211_CHAN_WIDTH_80P80 || + chandef->width == NL80211_CHAN_WIDTH_160) && !req.is_freq2) { + req.is_freq2 = true; + center_freq = freq2; + goto again; + } + + return ret; +} + +int mt7615_mcu_set_rx_hdr_trans_blacklist(struct mt7615_dev *dev) +{ + struct { + u8 operation; + u8 count; + u8 _rsv[2]; + u8 index; + u8 enable; + __le16 etype; + } req = { + .operation = 1, + .count = 1, + .enable = 1, + .etype = cpu_to_le16(ETH_P_PAE), + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(RX_HDR_TRANS), + &req, sizeof(req), false); +} + +int mt7615_mcu_set_bss_pm(struct mt7615_dev *dev, struct ieee80211_vif *vif, + bool enable) +{ + struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv; + struct { + u8 bss_idx; + u8 dtim_period; + __le16 aid; + __le16 bcn_interval; + __le16 atim_window; + u8 uapsd; + u8 bmc_delivered_ac; + u8 bmc_triggered_ac; + u8 pad; + } req = { + .bss_idx = mvif->mt76.idx, + .aid = cpu_to_le16(vif->cfg.aid), + .dtim_period = vif->bss_conf.dtim_period, + .bcn_interval = cpu_to_le16(vif->bss_conf.beacon_int), + }; + struct { + u8 bss_idx; + u8 pad[3]; + } req_hdr = { + .bss_idx = mvif->mt76.idx, + }; + int err; + + if (vif->type != NL80211_IFTYPE_STATION) + return 0; + + err = mt76_mcu_send_msg(&dev->mt76, MCU_CE_CMD(SET_BSS_ABORT), + &req_hdr, sizeof(req_hdr), false); + if (err < 0 || !enable) + return err; + + return mt76_mcu_send_msg(&dev->mt76, MCU_CE_CMD(SET_BSS_CONNECTED), + &req, sizeof(req), false); +} + +int mt7615_mcu_set_roc(struct mt7615_phy *phy, struct ieee80211_vif *vif, + struct ieee80211_channel *chan, int duration) +{ + struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv; + struct mt7615_dev *dev = phy->dev; + struct mt7615_roc_tlv req = { + .bss_idx = mvif->mt76.idx, + .active = !chan, + .max_interval = cpu_to_le32(duration), + .primary_chan = chan ? chan->hw_value : 0, + .band = chan ? chan->band : 0, + .req_type = 2, + }; + + phy->roc_grant = false; + + return mt76_mcu_send_msg(&dev->mt76, MCU_CE_CMD(SET_ROC), + &req, sizeof(req), false); +} + +int mt7615_mcu_set_protection(struct mt7615_phy *phy, struct ieee80211_vif *vif, + u8 ht_mode, bool use_cts_prot) +{ + struct mt7615_dev *dev = phy->dev; + struct { + u8 prot_idx; + u8 band; + u8 rsv[2]; + + bool long_nav; + bool prot_mm; + bool prot_gf; + bool prot_bw40; + bool prot_rifs; + bool prot_bw80; + bool prot_bw160; + u8 prot_erp_mask; + } __packed req = { + .prot_idx = 0x2, + .band = phy != &dev->phy, + }; + + switch (ht_mode & IEEE80211_HT_OP_MODE_PROTECTION) { + case IEEE80211_HT_OP_MODE_PROTECTION_NONMEMBER: + case IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED: + req.prot_mm = true; + req.prot_gf = true; + fallthrough; + case IEEE80211_HT_OP_MODE_PROTECTION_20MHZ: + req.prot_bw40 = true; + break; + } + + if (ht_mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT) + req.prot_gf = true; + + if (use_cts_prot) { + struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv; + u8 i = mvif->mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0 : mvif->mt76.omac_idx; + + req.prot_erp_mask = BIT(i); + } + + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(PROTECT_CTRL), &req, + sizeof(req), true); +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7615/mcu.h new file mode 100644 index 000000000..851b0e483 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7615/mcu.h @@ -0,0 +1,243 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* Copyright (C) 2019 MediaTek Inc. */ + +#ifndef __MT7615_MCU_H +#define __MT7615_MCU_H + +#include "../mt76_connac_mcu.h" + +struct mt7615_mcu_txd { + __le32 txd[8]; + + __le16 len; + __le16 pq_id; + + u8 cid; + u8 pkt_type; + u8 set_query; /* FW don't care */ + u8 seq; + + u8 uc_d2b0_rev; + u8 ext_cid; + u8 s2d_index; + u8 ext_cid_ack; + + u32 reserved[5]; +} __packed __aligned(4); + +/** + * struct mt7615_uni_txd - mcu command descriptor for firmware v3 + * @txd: hardware descriptor + * @len: total length not including txd + * @cid: command identifier + * @pkt_type: must be 0xa0 (cmd packet by long format) + * @frag_n: fragment number + * @seq: sequence number + * @checksum: 0 mean there is no checksum + * @s2d_index: index for command source and destination + * Definition | value | note + * CMD_S2D_IDX_H2N | 0x00 | command from HOST to WM + * CMD_S2D_IDX_C2N | 0x01 | command from WA to WM + * CMD_S2D_IDX_H2C | 0x02 | command from HOST to WA + * CMD_S2D_IDX_H2N_AND_H2C | 0x03 | command from HOST to WA and WM + * + * @option: command option + * BIT[0]: UNI_CMD_OPT_BIT_ACK + * set to 1 to request a fw reply + * if UNI_CMD_OPT_BIT_0_ACK is set and UNI_CMD_OPT_BIT_2_SET_QUERY + * is set, mcu firmware will send response event EID = 0x01 + * (UNI_EVENT_ID_CMD_RESULT) to the host. + * BIT[1]: UNI_CMD_OPT_BIT_UNI_CMD + * 0: original command + * 1: unified command + * BIT[2]: UNI_CMD_OPT_BIT_SET_QUERY + * 0: QUERY command + * 1: SET command + */ +struct mt7615_uni_txd { + __le32 txd[8]; + + /* DW1 */ + __le16 len; + __le16 cid; + + /* DW2 */ + u8 reserved; + u8 pkt_type; + u8 frag_n; + u8 seq; + + /* DW3 */ + __le16 checksum; + u8 s2d_index; + u8 option; + + /* DW4 */ + u8 reserved2[4]; +} __packed __aligned(4); + +enum { + MT_SKU_CCK_1_2 = 0, + MT_SKU_CCK_55_11, + MT_SKU_OFDM_6_9, + MT_SKU_OFDM_12_18, + MT_SKU_OFDM_24_36, + MT_SKU_OFDM_48, + MT_SKU_OFDM_54, + MT_SKU_HT20_0_8, + MT_SKU_HT20_32, + MT_SKU_HT20_1_2_9_10, + MT_SKU_HT20_3_4_11_12, + MT_SKU_HT20_5_13, + MT_SKU_HT20_6_14, + MT_SKU_HT20_7_15, + MT_SKU_HT40_0_8, + MT_SKU_HT40_32, + MT_SKU_HT40_1_2_9_10, + MT_SKU_HT40_3_4_11_12, + MT_SKU_HT40_5_13, + MT_SKU_HT40_6_14, + MT_SKU_HT40_7_15, + MT_SKU_VHT20_0, + MT_SKU_VHT20_1_2, + MT_SKU_VHT20_3_4, + MT_SKU_VHT20_5_6, + MT_SKU_VHT20_7, + MT_SKU_VHT20_8, + MT_SKU_VHT20_9, + MT_SKU_VHT40_0, + MT_SKU_VHT40_1_2, + MT_SKU_VHT40_3_4, + MT_SKU_VHT40_5_6, + MT_SKU_VHT40_7, + MT_SKU_VHT40_8, + MT_SKU_VHT40_9, + MT_SKU_VHT80_0, + MT_SKU_VHT80_1_2, + MT_SKU_VHT80_3_4, + MT_SKU_VHT80_5_6, + MT_SKU_VHT80_7, + MT_SKU_VHT80_8, + MT_SKU_VHT80_9, + MT_SKU_VHT160_0, + MT_SKU_VHT160_1_2, + MT_SKU_VHT160_3_4, + MT_SKU_VHT160_5_6, + MT_SKU_VHT160_7, + MT_SKU_VHT160_8, + MT_SKU_VHT160_9, + MT_SKU_1SS_DELTA, + MT_SKU_2SS_DELTA, + MT_SKU_3SS_DELTA, + MT_SKU_4SS_DELTA, +}; + +struct mt7615_mcu_rxd { + __le32 rxd[4]; + + __le16 len; + __le16 pkt_type_id; + + u8 eid; + u8 seq; + __le16 __rsv; + + u8 ext_eid; + u8 __rsv1[2]; + u8 s2d_index; +}; + +struct mt7615_mcu_csa_notify { + struct mt7615_mcu_rxd rxd; + + u8 omac_idx; + u8 csa_count; + u8 rsv[2]; +} __packed; + +struct mt7615_mcu_rdd_report { + struct mt7615_mcu_rxd rxd; + + u8 band_idx; + u8 long_detected; + u8 constant_prf_detected; + u8 staggered_prf_detected; + u8 radar_type_idx; + u8 periodic_pulse_num; + u8 long_pulse_num; + u8 hw_pulse_num; + + u8 out_lpn; + u8 out_spn; + u8 out_crpn; + u8 out_crpw; + u8 out_crbn; + u8 out_stgpn; + u8 out_stgpw; + + u8 _rsv[2]; + + __le32 out_pri_const; + __le32 out_pri_stg[3]; + + struct { + __le32 start; + __le16 pulse_width; + __le16 pulse_power; + } long_pulse[32]; + + struct { + __le32 start; + __le16 pulse_width; + __le16 pulse_power; + } periodic_pulse[32]; + + struct { + __le32 start; + __le16 pulse_width; + __le16 pulse_power; + u8 sc_pass; + u8 sw_reset; + } hw_pulse[32]; +}; + +enum { + MCU_ATE_SET_FREQ_OFFSET = 0xa, + MCU_ATE_SET_TX_POWER_CONTROL = 0x15, +}; + +struct mt7615_roc_tlv { + u8 bss_idx; + u8 token; + u8 active; + u8 primary_chan; + u8 sco; + u8 band; + u8 width; /* To support 80/160MHz bandwidth */ + u8 freq_seg1; /* To support 80/160MHz bandwidth */ + u8 freq_seg2; /* To support 80/160MHz bandwidth */ + u8 req_type; + u8 dbdc_band; + u8 rsv0; + __le32 max_interval; /* ms */ + u8 rsv1[8]; +} __packed; + +enum { + FW_STATE_PWR_ON = 1, + FW_STATE_N9_RDY = 2, +}; + +enum { + DBDC_TYPE_WMM, + DBDC_TYPE_MGMT, + DBDC_TYPE_BSS, + DBDC_TYPE_MBSS, + DBDC_TYPE_REPEATER, + DBDC_TYPE_MU, + DBDC_TYPE_BF, + DBDC_TYPE_PTA, + __DBDC_TYPE_MAX, +}; + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7615/mmio.c new file mode 100644 index 000000000..da7edd48c --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7615/mmio.c @@ -0,0 +1,275 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2020 MediaTek Inc. */ + +#include +#include +#include +#include + +#include "mt7615.h" +#include "regs.h" +#include "mac.h" +#include "../trace.h" + +const u32 mt7615e_reg_map[] = { + [MT_TOP_CFG_BASE] = 0x01000, + [MT_HW_BASE] = 0x01000, + [MT_PCIE_REMAP_2] = 0x02504, + [MT_ARB_BASE] = 0x20c00, + [MT_HIF_BASE] = 0x04000, + [MT_CSR_BASE] = 0x07000, + [MT_PLE_BASE] = 0x08000, + [MT_PSE_BASE] = 0x0c000, + [MT_CFG_BASE] = 0x20200, + [MT_AGG_BASE] = 0x20a00, + [MT_TMAC_BASE] = 0x21000, + [MT_RMAC_BASE] = 0x21200, + [MT_DMA_BASE] = 0x21800, + [MT_PF_BASE] = 0x22000, + [MT_WTBL_BASE_ON] = 0x23000, + [MT_WTBL_BASE_OFF] = 0x23400, + [MT_LPON_BASE] = 0x24200, + [MT_MIB_BASE] = 0x24800, + [MT_WTBL_BASE_ADDR] = 0x30000, + [MT_PCIE_REMAP_BASE2] = 0x80000, + [MT_TOP_MISC_BASE] = 0xc0000, + [MT_EFUSE_ADDR_BASE] = 0x81070000, +}; + +const u32 mt7663e_reg_map[] = { + [MT_TOP_CFG_BASE] = 0x01000, + [MT_HW_BASE] = 0x02000, + [MT_DMA_SHDL_BASE] = 0x06000, + [MT_PCIE_REMAP_2] = 0x0700c, + [MT_ARB_BASE] = 0x20c00, + [MT_HIF_BASE] = 0x04000, + [MT_CSR_BASE] = 0x07000, + [MT_PLE_BASE] = 0x08000, + [MT_PSE_BASE] = 0x0c000, + [MT_PP_BASE] = 0x0e000, + [MT_CFG_BASE] = 0x20000, + [MT_AGG_BASE] = 0x22000, + [MT_TMAC_BASE] = 0x24000, + [MT_RMAC_BASE] = 0x25000, + [MT_DMA_BASE] = 0x27000, + [MT_PF_BASE] = 0x28000, + [MT_WTBL_BASE_ON] = 0x29000, + [MT_WTBL_BASE_OFF] = 0x29800, + [MT_LPON_BASE] = 0x2b000, + [MT_MIB_BASE] = 0x2d000, + [MT_WTBL_BASE_ADDR] = 0x30000, + [MT_PCIE_REMAP_BASE2] = 0x90000, + [MT_TOP_MISC_BASE] = 0xc0000, + [MT_EFUSE_ADDR_BASE] = 0x78011000, +}; + +static void +mt7615_rx_poll_complete(struct mt76_dev *mdev, enum mt76_rxq_id q) +{ + mt76_connac_irq_enable(mdev, MT_INT_RX_DONE(q)); +} + +static irqreturn_t mt7615_irq_handler(int irq, void *dev_instance) +{ + struct mt7615_dev *dev = dev_instance; + + mt76_wr(dev, MT_INT_MASK_CSR, 0); + + if (!test_bit(MT76_STATE_INITIALIZED, &dev->mphy.state)) + return IRQ_NONE; + + tasklet_schedule(&dev->mt76.irq_tasklet); + + return IRQ_HANDLED; +} + +static void mt7615_irq_tasklet(struct tasklet_struct *t) +{ + struct mt7615_dev *dev = from_tasklet(dev, t, mt76.irq_tasklet); + u32 intr, mask = 0, tx_mcu_mask = mt7615_tx_mcu_int_mask(dev); + u32 mcu_int; + + mt76_wr(dev, MT_INT_MASK_CSR, 0); + + intr = mt76_rr(dev, MT_INT_SOURCE_CSR); + intr &= dev->mt76.mmio.irqmask; + mt76_wr(dev, MT_INT_SOURCE_CSR, intr); + + trace_dev_irq(&dev->mt76, intr, dev->mt76.mmio.irqmask); + + mask |= intr & MT_INT_RX_DONE_ALL; + if (intr & tx_mcu_mask) + mask |= tx_mcu_mask; + mt76_set_irq_mask(&dev->mt76, MT_INT_MASK_CSR, mask, 0); + + if (intr & tx_mcu_mask) + napi_schedule(&dev->mt76.tx_napi); + + if (intr & MT_INT_RX_DONE(0)) + napi_schedule(&dev->mt76.napi[0]); + + if (intr & MT_INT_RX_DONE(1)) + napi_schedule(&dev->mt76.napi[1]); + + if (!(intr & (MT_INT_MCU_CMD | MT7663_INT_MCU_CMD))) + return; + + if (is_mt7663(&dev->mt76)) { + mcu_int = mt76_rr(dev, MT_MCU2HOST_INT_STATUS); + mcu_int &= MT7663_MCU_CMD_ERROR_MASK; + mt76_wr(dev, MT_MCU2HOST_INT_STATUS, mcu_int); + } else { + mcu_int = mt76_rr(dev, MT_MCU_CMD); + mcu_int &= MT_MCU_CMD_ERROR_MASK; + } + + if (!mcu_int) + return; + + dev->reset_state = mcu_int; + queue_work(dev->mt76.wq, &dev->reset_work); + wake_up(&dev->reset_wait); +} + +static u32 __mt7615_reg_addr(struct mt7615_dev *dev, u32 addr) +{ + if (addr < 0x100000) + return addr; + + return mt7615_reg_map(dev, addr); +} + +static u32 mt7615_rr(struct mt76_dev *mdev, u32 offset) +{ + struct mt7615_dev *dev = container_of(mdev, struct mt7615_dev, mt76); + u32 addr = __mt7615_reg_addr(dev, offset); + + return dev->bus_ops->rr(mdev, addr); +} + +static void mt7615_wr(struct mt76_dev *mdev, u32 offset, u32 val) +{ + struct mt7615_dev *dev = container_of(mdev, struct mt7615_dev, mt76); + u32 addr = __mt7615_reg_addr(dev, offset); + + dev->bus_ops->wr(mdev, addr, val); +} + +static u32 mt7615_rmw(struct mt76_dev *mdev, u32 offset, u32 mask, u32 val) +{ + struct mt7615_dev *dev = container_of(mdev, struct mt7615_dev, mt76); + u32 addr = __mt7615_reg_addr(dev, offset); + + return dev->bus_ops->rmw(mdev, addr, mask, val); +} + +int mt7615_mmio_probe(struct device *pdev, void __iomem *mem_base, + int irq, const u32 *map) +{ + static const struct mt76_driver_ops drv_ops = { + /* txwi_size = txd size + txp size */ + .txwi_size = MT_TXD_SIZE + sizeof(struct mt76_connac_txp_common), + .drv_flags = MT_DRV_TXWI_NO_FREE | MT_DRV_HW_MGMT_TXQ, + .survey_flags = SURVEY_INFO_TIME_TX | + SURVEY_INFO_TIME_RX | + SURVEY_INFO_TIME_BSS_RX, + .token_size = MT7615_TOKEN_SIZE, + .tx_prepare_skb = mt7615_tx_prepare_skb, + .tx_complete_skb = mt76_connac_tx_complete_skb, + .rx_check = mt7615_rx_check, + .rx_skb = mt7615_queue_rx_skb, + .rx_poll_complete = mt7615_rx_poll_complete, + .sta_add = mt7615_mac_sta_add, + .sta_remove = mt7615_mac_sta_remove, + .update_survey = mt7615_update_channel, + .set_channel = mt7615_set_channel, + }; + struct mt76_bus_ops *bus_ops; + struct ieee80211_ops *ops; + struct mt7615_dev *dev; + struct mt76_dev *mdev; + int ret; + + ops = devm_kmemdup(pdev, &mt7615_ops, sizeof(mt7615_ops), GFP_KERNEL); + if (!ops) + return -ENOMEM; + + mdev = mt76_alloc_device(pdev, sizeof(*dev), ops, &drv_ops); + if (!mdev) + return -ENOMEM; + + dev = container_of(mdev, struct mt7615_dev, mt76); + mt76_mmio_init(&dev->mt76, mem_base); + tasklet_setup(&mdev->irq_tasklet, mt7615_irq_tasklet); + + dev->reg_map = map; + dev->ops = ops; + mdev->rev = (mt76_rr(dev, MT_HW_CHIPID) << 16) | + (mt76_rr(dev, MT_HW_REV) & 0xff); + dev_dbg(mdev->dev, "ASIC revision: %04x\n", mdev->rev); + + dev->bus_ops = dev->mt76.bus; + bus_ops = devm_kmemdup(dev->mt76.dev, dev->bus_ops, sizeof(*bus_ops), + GFP_KERNEL); + if (!bus_ops) { + ret = -ENOMEM; + goto err_free_dev; + } + + bus_ops->rr = mt7615_rr; + bus_ops->wr = mt7615_wr; + bus_ops->rmw = mt7615_rmw; + dev->mt76.bus = bus_ops; + + mt76_wr(dev, MT_INT_MASK_CSR, 0); + + ret = devm_request_irq(mdev->dev, irq, mt7615_irq_handler, + IRQF_SHARED, KBUILD_MODNAME, dev); + if (ret) + goto err_free_dev; + + if (is_mt7663(mdev)) + mt76_wr(dev, MT_PCIE_IRQ_ENABLE, 1); + + ret = mt7615_register_device(dev); + if (ret) + goto err_free_irq; + + return 0; + +err_free_irq: + devm_free_irq(pdev, irq, dev); +err_free_dev: + mt76_free_device(&dev->mt76); + + return ret; +} + +static int __init mt7615_init(void) +{ + int ret; + + ret = pci_register_driver(&mt7615_pci_driver); + if (ret) + return ret; + + if (IS_ENABLED(CONFIG_MT7622_WMAC)) { + ret = platform_driver_register(&mt7622_wmac_driver); + if (ret) + pci_unregister_driver(&mt7615_pci_driver); + } + + return ret; +} + +static void __exit mt7615_exit(void) +{ + if (IS_ENABLED(CONFIG_MT7622_WMAC)) + platform_driver_unregister(&mt7622_wmac_driver); + pci_unregister_driver(&mt7615_pci_driver); +} + +module_init(mt7615_init); +module_exit(mt7615_exit); +MODULE_DESCRIPTION("MediaTek MT7615E MMIO helpers"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h b/drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h new file mode 100644 index 000000000..391928405 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7615/mt7615.h @@ -0,0 +1,557 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* Copyright (C) 2019 MediaTek Inc. */ + +#ifndef __MT7615_H +#define __MT7615_H + +#include +#include +#include +#include +#include +#include "../mt76_connac_mcu.h" +#include "regs.h" + +#define MT7615_MAX_INTERFACES 16 +#define MT7615_MAX_WMM_SETS 4 +#define MT7663_WTBL_SIZE 32 +#define MT7615_WTBL_SIZE 128 +#define MT7615_WTBL_RESERVED (mt7615_wtbl_size(dev) - 1) +#define MT7615_WTBL_STA (MT7615_WTBL_RESERVED - \ + MT7615_MAX_INTERFACES) + +#define MT7615_PM_TIMEOUT (HZ / 12) +#define MT7615_HW_SCAN_TIMEOUT (HZ / 10) +#define MT7615_RESET_TIMEOUT (30 * HZ) +#define MT7615_RATE_RETRY 2 + +#define MT7615_TX_RING_SIZE 1024 +#define MT7615_TX_MGMT_RING_SIZE 128 +#define MT7615_TX_MCU_RING_SIZE 128 +#define MT7615_TX_FWDL_RING_SIZE 128 + +#define MT7615_RX_RING_SIZE 1024 +#define MT7615_RX_MCU_RING_SIZE 512 + +#define MT7615_DRV_OWN_RETRY_COUNT 10 + +#define MT7615_FIRMWARE_CR4 "mediatek/mt7615_cr4.bin" +#define MT7615_FIRMWARE_N9 "mediatek/mt7615_n9.bin" +#define MT7615_ROM_PATCH "mediatek/mt7615_rom_patch.bin" + +#define MT7622_FIRMWARE_N9 "mediatek/mt7622_n9.bin" +#define MT7622_ROM_PATCH "mediatek/mt7622_rom_patch.bin" + +#define MT7615_FIRMWARE_V1 1 +#define MT7615_FIRMWARE_V2 2 +#define MT7615_FIRMWARE_V3 3 + +#define MT7663_OFFLOAD_ROM_PATCH "mediatek/mt7663pr2h.bin" +#define MT7663_OFFLOAD_FIRMWARE_N9 "mediatek/mt7663_n9_v3.bin" +#define MT7663_ROM_PATCH "mediatek/mt7663pr2h_rebb.bin" +#define MT7663_FIRMWARE_N9 "mediatek/mt7663_n9_rebb.bin" + +#define MT7615_EEPROM_SIZE 1024 +#define MT7663_EEPROM_SIZE 1536 +#define MT7615_TOKEN_SIZE 4096 + +#define MT_FRAC_SCALE 12 +#define MT_FRAC(val, div) (((val) << MT_FRAC_SCALE) / (div)) + +#define MT_CHFREQ_VALID BIT(7) +#define MT_CHFREQ_DBDC_IDX BIT(6) +#define MT_CHFREQ_SEQ GENMASK(5, 0) + +#define MT7615_BAR_RATE_DEFAULT 0x4b /* OFDM 6M */ +#define MT7615_CFEND_RATE_DEFAULT 0x49 /* OFDM 24M */ +#define MT7615_CFEND_RATE_11B 0x03 /* 11B LP, 11M */ + +struct mt7615_vif; +struct mt7615_sta; +struct mt7615_dfs_pulse; +struct mt7615_dfs_pattern; +enum mt7615_cipher_type; + +enum mt7615_hw_txq_id { + MT7615_TXQ_MAIN, + MT7615_TXQ_EXT, + MT7615_TXQ_MCU, + MT7615_TXQ_FWDL, +}; + +enum mt7622_hw_txq_id { + MT7622_TXQ_AC0, + MT7622_TXQ_AC1, + MT7622_TXQ_AC2, + MT7622_TXQ_FWDL = MT7615_TXQ_FWDL, + MT7622_TXQ_AC3, + MT7622_TXQ_MGMT, + MT7622_TXQ_MCU = 15, +}; + +struct mt7615_rate_set { + struct ieee80211_tx_rate probe_rate; + struct ieee80211_tx_rate rates[4]; +}; + +struct mt7615_rate_desc { + bool rateset; + u16 probe_val; + u16 val[4]; + u8 bw_idx; + u8 bw; +}; + +struct mt7615_wtbl_rate_desc { + struct list_head node; + + struct mt7615_rate_desc rate; + struct mt7615_sta *sta; +}; + +struct mt7663s_intr { + u32 isr; + struct { + u32 wtqcr[8]; + } tx; + struct { + u16 num[2]; + u16 len[2][16]; + } rx; + u32 rec_mb[2]; +} __packed; + +struct mt7615_sta { + struct mt76_wcid wcid; /* must be first */ + + struct mt7615_vif *vif; + + u32 airtime_ac[8]; + + struct ieee80211_tx_rate rates[4]; + + struct mt7615_rate_set rateset[2]; + u32 rate_set_tsf; + + u8 rate_count; + u8 n_rates; + + u8 rate_probe; +}; + +struct mt7615_vif { + struct mt76_vif_link mt76; /* must be first */ + struct mt7615_sta sta; + bool sta_added; +}; + +struct mib_stats { + u32 ack_fail_cnt; + u32 fcs_err_cnt; + u32 rts_cnt; + u32 rts_retries_cnt; + u32 ba_miss_cnt; + unsigned long aggr_per; +}; + +struct mt7615_phy { + struct mt76_phy *mt76; + struct mt7615_dev *dev; + + struct ieee80211_vif *monitor_vif; + + u8 n_beacon_vif; + + u32 rxfilter; + u64 omac_mask; + + u16 noise; + + bool scs_en; + + unsigned long last_cca_adj; + int false_cca_ofdm, false_cca_cck; + s8 ofdm_sensitivity; + s8 cck_sensitivity; + + s16 coverage_class; + u8 slottime; + + u8 chfreq; + u8 rdd_state; + + u32 rx_ampdu_ts; + u32 ampdu_ref; + + struct mib_stats mib; + + struct sk_buff_head scan_event_list; + struct delayed_work scan_work; + + struct work_struct roc_work; + struct timer_list roc_timer; + wait_queue_head_t roc_wait; + bool roc_grant; + +#ifdef CONFIG_NL80211_TESTMODE + struct { + u32 *reg_backup; + + s16 last_freq_offset; + u8 last_rcpi[4]; + s8 last_ib_rssi[4]; + s8 last_wb_rssi[4]; + } test; +#endif +}; + +#define mt7615_mcu_add_tx_ba(dev, ...) (dev)->mcu_ops->add_tx_ba((dev), __VA_ARGS__) +#define mt7615_mcu_add_rx_ba(dev, ...) (dev)->mcu_ops->add_rx_ba((dev), __VA_ARGS__) +#define mt7615_mcu_sta_add(phy, ...) ((phy)->dev)->mcu_ops->sta_add((phy), __VA_ARGS__) +#define mt7615_mcu_add_dev_info(phy, ...) ((phy)->dev)->mcu_ops->add_dev_info((phy), __VA_ARGS__) +#define mt7615_mcu_add_bss_info(phy, ...) ((phy)->dev)->mcu_ops->add_bss_info((phy), __VA_ARGS__) +#define mt7615_mcu_add_beacon(dev, ...) (dev)->mcu_ops->add_beacon_offload((dev), __VA_ARGS__) +#define mt7615_mcu_set_pm(dev, ...) (dev)->mcu_ops->set_pm_state((dev), __VA_ARGS__) +#define mt7615_mcu_set_drv_ctrl(dev) (dev)->mcu_ops->set_drv_ctrl((dev)) +#define mt7615_mcu_set_fw_ctrl(dev) (dev)->mcu_ops->set_fw_ctrl((dev)) +#define mt7615_mcu_set_sta_decap_offload(dev, ...) (dev)->mcu_ops->set_sta_decap_offload((dev), __VA_ARGS__) +struct mt7615_mcu_ops { + int (*add_tx_ba)(struct mt7615_dev *dev, + struct ieee80211_ampdu_params *params, + bool enable); + int (*add_rx_ba)(struct mt7615_dev *dev, + struct ieee80211_ampdu_params *params, + bool enable); + int (*sta_add)(struct mt7615_phy *phy, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, bool enable); + int (*add_dev_info)(struct mt7615_phy *phy, struct ieee80211_vif *vif, + bool enable); + int (*add_bss_info)(struct mt7615_phy *phy, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, bool enable); + int (*add_beacon_offload)(struct mt7615_dev *dev, + struct ieee80211_hw *hw, + struct ieee80211_vif *vif, bool enable); + int (*set_pm_state)(struct mt7615_dev *dev, int band, int state); + int (*set_drv_ctrl)(struct mt7615_dev *dev); + int (*set_fw_ctrl)(struct mt7615_dev *dev); + int (*set_sta_decap_offload)(struct mt7615_dev *dev, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta); +}; + +struct mt7615_dev { + union { /* must be first */ + struct mt76_dev mt76; + struct mt76_phy mphy; + }; + + const struct mt76_bus_ops *bus_ops; + struct mt7615_phy phy; + u64 omac_mask; + + u16 chainmask; + + struct ieee80211_ops *ops; + const struct mt7615_mcu_ops *mcu_ops; + struct regmap *infracfg; + const u32 *reg_map; + + struct work_struct mcu_work; + + struct work_struct reset_work; + wait_queue_head_t reset_wait; + u32 reset_state; + + struct { + u8 n_pulses; + u32 period; + u16 width; + s16 power; + } radar_pattern; + u32 hw_pattern; + + bool fw_debug; + bool flash_eeprom; + bool dbdc_support; + + u8 fw_ver; + + struct work_struct rate_work; + struct list_head wrd_head; + + u32 debugfs_rf_wf; + u32 debugfs_rf_reg; + + u32 muar_mask; + + struct mt76_connac_pm pm; + struct mt76_connac_coredump coredump; +}; + +enum tx_pkt_queue_idx { + MT_LMAC_AC00, + MT_LMAC_AC01, + MT_LMAC_AC02, + MT_LMAC_AC03, + MT_LMAC_ALTX0 = 0x10, + MT_LMAC_BMC0, + MT_LMAC_BCN0, + MT_LMAC_PSMP0, + MT_LMAC_ALTX1, + MT_LMAC_BMC1, + MT_LMAC_BCN1, + MT_LMAC_PSMP1, +}; + +enum { + MT_RX_SEL0, + MT_RX_SEL1, +}; + +enum mt7615_rdd_cmd { + RDD_STOP, + RDD_START, + RDD_DET_MODE, + RDD_DET_STOP, + RDD_CAC_START, + RDD_CAC_END, + RDD_NORMAL_START, + RDD_DISABLE_DFS_CAL, + RDD_PULSE_DBG, + RDD_READ_PULSE, + RDD_RESUME_BF, +}; + +static inline struct mt7615_phy * +mt7615_hw_phy(struct ieee80211_hw *hw) +{ + struct mt76_phy *phy = hw->priv; + + return phy->priv; +} + +static inline struct mt7615_dev * +mt7615_hw_dev(struct ieee80211_hw *hw) +{ + struct mt76_phy *phy = hw->priv; + + return container_of(phy->dev, struct mt7615_dev, mt76); +} + +static inline struct mt7615_phy * +mt7615_ext_phy(struct mt7615_dev *dev) +{ + struct mt76_phy *phy = dev->mt76.phys[MT_BAND1]; + + if (!phy) + return NULL; + + return phy->priv; +} + +extern struct ieee80211_rate mt7615_rates[12]; +extern const struct ieee80211_ops mt7615_ops; +extern const u32 mt7615e_reg_map[__MT_BASE_MAX]; +extern const u32 mt7663e_reg_map[__MT_BASE_MAX]; +extern const u32 mt7663_usb_sdio_reg_map[__MT_BASE_MAX]; +extern struct pci_driver mt7615_pci_driver; +extern struct platform_driver mt7622_wmac_driver; +extern const struct mt76_testmode_ops mt7615_testmode_ops; + +#ifdef CONFIG_MT7622_WMAC +int mt7622_wmac_init(struct mt7615_dev *dev); +#else +static inline int mt7622_wmac_init(struct mt7615_dev *dev) +{ + return 0; +} +#endif + +int mt7615_thermal_init(struct mt7615_dev *dev); +int mt7615_mmio_probe(struct device *pdev, void __iomem *mem_base, + int irq, const u32 *map); +u32 mt7615_reg_map(struct mt7615_dev *dev, u32 addr); + +u32 mt7615_reg_map(struct mt7615_dev *dev, u32 addr); +int mt7615_led_set_blink(struct led_classdev *led_cdev, + unsigned long *delay_on, + unsigned long *delay_off); +void mt7615_led_set_brightness(struct led_classdev *led_cdev, + enum led_brightness brightness); +void mt7615_init_device(struct mt7615_dev *dev); +int mt7615_register_device(struct mt7615_dev *dev); +void mt7615_unregister_device(struct mt7615_dev *dev); +int mt7615_register_ext_phy(struct mt7615_dev *dev); +void mt7615_unregister_ext_phy(struct mt7615_dev *dev); +int mt7615_eeprom_init(struct mt7615_dev *dev, u32 addr); +int mt7615_eeprom_get_target_power_index(struct mt7615_dev *dev, + struct ieee80211_channel *chan, + u8 chain_idx); +int mt7615_eeprom_get_power_delta_index(struct mt7615_dev *dev, + enum nl80211_band band); +int mt7615_wait_pdma_busy(struct mt7615_dev *dev); +int mt7615_dma_init(struct mt7615_dev *dev); +void mt7615_dma_start(struct mt7615_dev *dev); +void mt7615_dma_cleanup(struct mt7615_dev *dev); +int mt7615_mcu_init(struct mt7615_dev *dev); +bool mt7615_wait_for_mcu_init(struct mt7615_dev *dev); +void mt7615_mac_set_rates(struct mt7615_phy *phy, struct mt7615_sta *sta, + struct ieee80211_tx_rate *probe_rate, + struct ieee80211_tx_rate *rates); +void mt7615_pm_wake_work(struct work_struct *work); +void mt7615_pm_power_save_work(struct work_struct *work); +int mt7615_mcu_set_chan_info(struct mt7615_phy *phy, int cmd); +int mt7615_mcu_set_wmm(struct mt7615_dev *dev, u8 queue, + const struct ieee80211_tx_queue_params *params); +void mt7615_mcu_rx_event(struct mt7615_dev *dev, struct sk_buff *skb); +int mt7615_mcu_rdd_send_pattern(struct mt7615_dev *dev); +int mt7615_mcu_fw_log_2_host(struct mt7615_dev *dev, u8 ctrl); + +static inline bool mt7615_firmware_offload(struct mt7615_dev *dev) +{ + return dev->fw_ver > MT7615_FIRMWARE_V2; +} + +static inline u16 mt7615_wtbl_size(struct mt7615_dev *dev) +{ + if (is_mt7663(&dev->mt76) && mt7615_firmware_offload(dev)) + return MT7663_WTBL_SIZE; + else + return MT7615_WTBL_SIZE; +} + +#define mt7615_mutex_acquire(dev) \ + mt76_connac_mutex_acquire(&(dev)->mt76, &(dev)->pm) +#define mt7615_mutex_release(dev) \ + mt76_connac_mutex_release(&(dev)->mt76, &(dev)->pm) + +static inline u8 mt7615_lmac_mapping(struct mt7615_dev *dev, u8 ac) +{ + static const u8 lmac_queue_map[] = { + [IEEE80211_AC_BK] = MT_LMAC_AC00, + [IEEE80211_AC_BE] = MT_LMAC_AC01, + [IEEE80211_AC_VI] = MT_LMAC_AC02, + [IEEE80211_AC_VO] = MT_LMAC_AC03, + }; + + if (WARN_ON_ONCE(ac >= ARRAY_SIZE(lmac_queue_map))) + return MT_LMAC_AC01; /* BE */ + + return lmac_queue_map[ac]; +} + +static inline u32 mt7615_tx_mcu_int_mask(struct mt7615_dev *dev) +{ + return MT_INT_TX_DONE(dev->mt76.q_mcu[MT_MCUQ_WM]->hw_idx); +} + +static inline unsigned long +mt7615_get_macwork_timeout(struct mt7615_dev *dev) +{ + return dev->pm.enable ? HZ / 3 : HZ / 10; +} + +void mt7615_dma_reset(struct mt7615_dev *dev); +void mt7615_scan_work(struct work_struct *work); +void mt7615_roc_work(struct work_struct *work); +void mt7615_roc_timer(struct timer_list *timer); +void mt7615_init_txpower(struct mt7615_dev *dev, + struct ieee80211_supported_band *sband); +int mt7615_set_channel(struct mt76_phy *mphy); +void mt7615_init_work(struct mt7615_dev *dev); + +int mt7615_mcu_restart(struct mt76_dev *dev); +void mt7615_update_channel(struct mt76_phy *mphy); +bool mt7615_mac_wtbl_update(struct mt7615_dev *dev, int idx, u32 mask); +void mt7615_mac_reset_counters(struct mt7615_phy *phy); +void mt7615_mac_cca_stats_reset(struct mt7615_phy *phy); +void mt7615_mac_set_scs(struct mt7615_phy *phy, bool enable); +void mt7615_mac_enable_nf(struct mt7615_dev *dev, bool ext_phy); +void mt7615_mac_sta_poll(struct mt7615_dev *dev); +int mt7615_mac_write_txwi(struct mt7615_dev *dev, __le32 *txwi, + struct sk_buff *skb, struct mt76_wcid *wcid, + struct ieee80211_sta *sta, int pid, + struct ieee80211_key_conf *key, + enum mt76_txq_id qid, bool beacon); +void mt7615_mac_set_timing(struct mt7615_phy *phy); +int __mt7615_mac_wtbl_set_key(struct mt7615_dev *dev, + struct mt76_wcid *wcid, + struct ieee80211_key_conf *key); +int mt7615_mac_wtbl_set_key(struct mt7615_dev *dev, struct mt76_wcid *wcid, + struct ieee80211_key_conf *key); +void mt7615_mac_reset_work(struct work_struct *work); +u32 mt7615_mac_get_sta_tid_sn(struct mt7615_dev *dev, int wcid, u8 tid); + +int mt7615_mcu_parse_response(struct mt76_dev *mdev, int cmd, + struct sk_buff *skb, int seq); +u32 mt7615_rf_rr(struct mt7615_dev *dev, u32 wf, u32 reg); +int mt7615_rf_wr(struct mt7615_dev *dev, u32 wf, u32 reg, u32 val); +int mt7615_mcu_set_dbdc(struct mt7615_dev *dev); +int mt7615_mcu_set_eeprom(struct mt7615_dev *dev); +int mt7615_mcu_get_temperature(struct mt7615_dev *dev); +int mt7615_mcu_set_tx_power(struct mt7615_phy *phy); +void mt7615_mcu_exit(struct mt7615_dev *dev); +void mt7615_mcu_fill_msg(struct mt7615_dev *dev, struct sk_buff *skb, + int cmd, int *wait_seq); + +int mt7615_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, + enum mt76_txq_id qid, struct mt76_wcid *wcid, + struct ieee80211_sta *sta, + struct mt76_tx_info *tx_info); + +void mt7615_tx_worker(struct mt76_worker *w); +void mt7615_tx_token_put(struct mt7615_dev *dev); +bool mt7615_rx_check(struct mt76_dev *mdev, void *data, int len); +void mt7615_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, + struct sk_buff *skb, u32 *info); +int mt7615_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); +void mt7615_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); +void mt7615_mac_work(struct work_struct *work); +int mt7615_mcu_set_rx_hdr_trans_blacklist(struct mt7615_dev *dev); +int mt7615_mcu_set_fcc5_lpn(struct mt7615_dev *dev, int val); +int mt7615_mcu_set_pulse_th(struct mt7615_dev *dev, + const struct mt7615_dfs_pulse *pulse); +int mt7615_mcu_set_radar_th(struct mt7615_dev *dev, int index, + const struct mt7615_dfs_pattern *pattern); +int mt7615_mcu_set_test_param(struct mt7615_dev *dev, u8 param, bool test_mode, + u32 val); +int mt7615_mcu_set_sku_en(struct mt7615_phy *phy, bool enable); +int mt7615_mcu_apply_rx_dcoc(struct mt7615_phy *phy); +int mt7615_mcu_apply_tx_dpd(struct mt7615_phy *phy); +int mt7615_dfs_init_radar_detector(struct mt7615_phy *phy); +int mt7615_mcu_set_protection(struct mt7615_phy *phy, struct ieee80211_vif *vif, + u8 ht_mode, bool use_cts_prot); +int mt7615_mcu_set_roc(struct mt7615_phy *phy, struct ieee80211_vif *vif, + struct ieee80211_channel *chan, int duration); + +int mt7615_init_debugfs(struct mt7615_dev *dev); +int mt7615_mcu_wait_response(struct mt7615_dev *dev, int cmd, int seq); + +int mt7615_mac_set_beacon_filter(struct mt7615_phy *phy, + struct ieee80211_vif *vif, + bool enable); +int mt7615_mcu_set_bss_pm(struct mt7615_dev *dev, struct ieee80211_vif *vif, + bool enable); +int __mt7663_load_firmware(struct mt7615_dev *dev); +void mt7615_coredump_work(struct work_struct *work); + +void mt7622_trigger_hif_int(struct mt7615_dev *dev, bool en); + +/* usb */ +int mt7663_usb_sdio_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, + enum mt76_txq_id qid, struct mt76_wcid *wcid, + struct ieee80211_sta *sta, + struct mt76_tx_info *tx_info); +bool mt7663_usb_sdio_tx_status_data(struct mt76_dev *mdev, u8 *update); +void mt7663_usb_sdio_tx_complete_skb(struct mt76_dev *mdev, + struct mt76_queue_entry *e); +int mt7663_usb_sdio_register_device(struct mt7615_dev *dev); +int mt7663u_mcu_init(struct mt7615_dev *dev); +int mt7663u_mcu_power_on(struct mt7615_dev *dev); + +/* sdio */ +int mt7663s_mcu_init(struct mt7615_dev *dev); + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mt7615_trace.h b/drivers/net/wireless/mediatek/mt76/mt7615/mt7615_trace.h new file mode 100644 index 000000000..697fc5d22 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7615/mt7615_trace.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (C) 2019 Lorenzo Bianconi + */ + +#if !defined(__MT7615_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define __MT7615_TRACE_H + +#include +#include "mt7615.h" + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM mt7615 + +#define MAXNAME 32 +#define DEV_ENTRY __array(char, wiphy_name, 32) +#define DEV_ASSIGN strscpy(__entry->wiphy_name, \ + wiphy_name(mt76_hw(dev)->wiphy), MAXNAME) +#define DEV_PR_FMT "%s" +#define DEV_PR_ARG __entry->wiphy_name + +#define TOKEN_ENTRY __field(u16, token) +#define TOKEN_ASSIGN __entry->token = token +#define TOKEN_PR_FMT " %d" +#define TOKEN_PR_ARG __entry->token + +DECLARE_EVENT_CLASS(dev_token, + TP_PROTO(struct mt7615_dev *dev, u16 token), + TP_ARGS(dev, token), + TP_STRUCT__entry( + DEV_ENTRY + TOKEN_ENTRY + ), + TP_fast_assign( + DEV_ASSIGN; + TOKEN_ASSIGN; + ), + TP_printk( + DEV_PR_FMT TOKEN_PR_FMT, + DEV_PR_ARG, TOKEN_PR_ARG + ) +); + +DEFINE_EVENT(dev_token, mac_tx_free, + TP_PROTO(struct mt7615_dev *dev, u16 token), + TP_ARGS(dev, token) +); + +#endif + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE mt7615_trace + +#include diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/pci.c b/drivers/net/wireless/mediatek/mt76/mt7615/pci.c new file mode 100644 index 000000000..f5018bfa3 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7615/pci.c @@ -0,0 +1,206 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2019 MediaTek Inc. + * + * Author: Ryder Lee + * Felix Fietkau + */ + +#include +#include +#include + +#include "mt7615.h" +#include "mcu.h" + +static const struct pci_device_id mt7615_pci_device_table[] = { + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7615) }, + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7663) }, + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7611) }, + { }, +}; + +static int mt7615_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + const u32 *map; + int ret; + + ret = pcim_enable_device(pdev); + if (ret) + return ret; + + ret = pcim_iomap_regions(pdev, BIT(0), pci_name(pdev)); + if (ret) + return ret; + + pci_set_master(pdev); + + ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES); + if (ret < 0) + return ret; + + ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) + goto error; + + mt76_pci_disable_aspm(pdev); + + map = id->device == 0x7663 ? mt7663e_reg_map : mt7615e_reg_map; + ret = mt7615_mmio_probe(&pdev->dev, pcim_iomap_table(pdev)[0], + pdev->irq, map); + if (ret) + goto error; + + return 0; +error: + pci_free_irq_vectors(pdev); + + return ret; +} + +static void mt7615_pci_remove(struct pci_dev *pdev) +{ + struct mt76_dev *mdev = pci_get_drvdata(pdev); + struct mt7615_dev *dev = container_of(mdev, struct mt7615_dev, mt76); + + mt7615_unregister_device(dev); + devm_free_irq(&pdev->dev, pdev->irq, dev); + pci_free_irq_vectors(pdev); +} + +#ifdef CONFIG_PM +static int mt7615_pci_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct mt76_dev *mdev = pci_get_drvdata(pdev); + struct mt7615_dev *dev = container_of(mdev, struct mt7615_dev, mt76); + bool hif_suspend; + int i, err; + + err = mt76_connac_pm_wake(&dev->mphy, &dev->pm); + if (err < 0) + return err; + + hif_suspend = !test_bit(MT76_STATE_SUSPEND, &dev->mphy.state) && + mt7615_firmware_offload(dev); + if (hif_suspend) { + err = mt76_connac_mcu_set_hif_suspend(mdev, true, true); + if (err) + return err; + } + + napi_disable(&mdev->tx_napi); + mt76_worker_disable(&mdev->tx_worker); + + mt76_for_each_q_rx(mdev, i) { + napi_disable(&mdev->napi[i]); + } + tasklet_kill(&mdev->irq_tasklet); + + mt7615_dma_reset(dev); + + err = mt7615_wait_pdma_busy(dev); + if (err) + goto restore; + + if (is_mt7663(mdev)) { + mt76_set(dev, MT_PDMA_SLP_PROT, MT_PDMA_AXI_SLPPROT_ENABLE); + if (!mt76_poll_msec(dev, MT_PDMA_SLP_PROT, + MT_PDMA_AXI_SLPPROT_RDY, + MT_PDMA_AXI_SLPPROT_RDY, 1000)) { + dev_err(mdev->dev, "PDMA sleep protection failed\n"); + err = -EIO; + goto restore; + } + } + + pci_enable_wake(pdev, pci_choose_state(pdev, state), true); + pci_save_state(pdev); + err = pci_set_power_state(pdev, pci_choose_state(pdev, state)); + if (err) + goto restore; + + err = mt7615_mcu_set_fw_ctrl(dev); + if (err) + goto restore; + + return 0; + +restore: + mt76_for_each_q_rx(mdev, i) { + napi_enable(&mdev->napi[i]); + } + napi_enable(&mdev->tx_napi); + if (hif_suspend) + mt76_connac_mcu_set_hif_suspend(mdev, false, true); + + return err; +} + +static int mt7615_pci_resume(struct pci_dev *pdev) +{ + struct mt76_dev *mdev = pci_get_drvdata(pdev); + struct mt7615_dev *dev = container_of(mdev, struct mt7615_dev, mt76); + bool pdma_reset; + int i, err; + + err = mt7615_mcu_set_drv_ctrl(dev); + if (err < 0) + return err; + + err = pci_set_power_state(pdev, PCI_D0); + if (err) + return err; + + pci_restore_state(pdev); + + if (is_mt7663(&dev->mt76)) { + mt76_clear(dev, MT_PDMA_SLP_PROT, MT_PDMA_AXI_SLPPROT_ENABLE); + mt76_wr(dev, MT_PCIE_IRQ_ENABLE, 1); + } + + pdma_reset = !mt76_rr(dev, MT_WPDMA_TX_RING0_CTRL0) && + !mt76_rr(dev, MT_WPDMA_TX_RING0_CTRL1); + if (pdma_reset) + dev_err(mdev->dev, "PDMA engine must be reinitialized\n"); + + mt76_worker_enable(&mdev->tx_worker); + + mt76_for_each_q_rx(mdev, i) { + napi_enable(&mdev->napi[i]); + } + napi_enable(&mdev->tx_napi); + + local_bh_disable(); + mt76_for_each_q_rx(mdev, i) { + napi_schedule(&mdev->napi[i]); + } + napi_schedule(&mdev->tx_napi); + local_bh_enable(); + + if (!test_bit(MT76_STATE_SUSPEND, &dev->mphy.state) && + mt7615_firmware_offload(dev)) + err = mt76_connac_mcu_set_hif_suspend(mdev, false, true); + + return err; +} +#endif /* CONFIG_PM */ + +struct pci_driver mt7615_pci_driver = { + .name = KBUILD_MODNAME, + .id_table = mt7615_pci_device_table, + .probe = mt7615_pci_probe, + .remove = mt7615_pci_remove, +#ifdef CONFIG_PM + .suspend = mt7615_pci_suspend, + .resume = mt7615_pci_resume, +#endif /* CONFIG_PM */ +}; + +MODULE_DEVICE_TABLE(pci, mt7615_pci_device_table); +MODULE_FIRMWARE(MT7615_FIRMWARE_CR4); +MODULE_FIRMWARE(MT7615_FIRMWARE_N9); +MODULE_FIRMWARE(MT7615_ROM_PATCH); +MODULE_FIRMWARE(MT7663_OFFLOAD_FIRMWARE_N9); +MODULE_FIRMWARE(MT7663_OFFLOAD_ROM_PATCH); +MODULE_FIRMWARE(MT7663_FIRMWARE_N9); +MODULE_FIRMWARE(MT7663_ROM_PATCH); diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/pci_init.c b/drivers/net/wireless/mediatek/mt76/mt7615/pci_init.c new file mode 100644 index 000000000..a9178e077 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7615/pci_init.c @@ -0,0 +1,128 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2019 MediaTek Inc. + * + * Author: Roy Luo + * Ryder Lee + * Felix Fietkau + * Lorenzo Bianconi + */ + +#include +#include "mt7615.h" +#include "mac.h" +#include "eeprom.h" + +static void mt7615_pci_init_work(struct work_struct *work) +{ + struct mt7615_dev *dev = container_of(work, struct mt7615_dev, + mcu_work); + int i, ret; + + ret = mt7615_mcu_init(dev); + for (i = 0; (ret == -EAGAIN) && (i < 10); i++) { + msleep(200); + ret = mt7615_mcu_init(dev); + } + + if (ret) + return; + + mt7615_init_work(dev); +} + +static int mt7615_init_hardware(struct mt7615_dev *dev) +{ + u32 addr = mt7615_reg_map(dev, MT_EFUSE_BASE); + int ret, idx; + + mt76_wr(dev, MT_INT_SOURCE_CSR, ~0); + + INIT_WORK(&dev->mcu_work, mt7615_pci_init_work); + ret = mt7615_eeprom_init(dev, addr); + if (ret < 0) + return ret; + + if (is_mt7663(&dev->mt76)) { + /* Reset RGU */ + mt76_clear(dev, MT_MCU_CIRQ_IRQ_SEL(4), BIT(1)); + mt76_set(dev, MT_MCU_CIRQ_IRQ_SEL(4), BIT(1)); + } + + ret = mt7615_dma_init(dev); + if (ret) + return ret; + + set_bit(MT76_STATE_INITIALIZED, &dev->mphy.state); + + /* Beacon and mgmt frames should occupy wcid 0 */ + idx = mt76_wcid_alloc(dev->mt76.wcid_mask, MT7615_WTBL_STA - 1); + if (idx) + return -ENOSPC; + + dev->mt76.global_wcid.idx = idx; + dev->mt76.global_wcid.hw_key_idx = -1; + rcu_assign_pointer(dev->mt76.wcid[idx], &dev->mt76.global_wcid); + + return 0; +} + +int mt7615_register_device(struct mt7615_dev *dev) +{ + int ret; + + mt7615_init_device(dev); + INIT_WORK(&dev->reset_work, mt7615_mac_reset_work); + + /* init led callbacks */ + if (IS_ENABLED(CONFIG_MT76_LEDS)) { + dev->mphy.leds.cdev.brightness_set = mt7615_led_set_brightness; + dev->mphy.leds.cdev.blink_set = mt7615_led_set_blink; + } + + ret = mt7622_wmac_init(dev); + if (ret) + return ret; + + ret = mt7615_init_hardware(dev); + if (ret) + return ret; + + ret = mt76_register_device(&dev->mt76, true, mt76_rates, + ARRAY_SIZE(mt76_rates)); + if (ret) + return ret; + + ret = mt7615_thermal_init(dev); + if (ret) + return ret; + + ieee80211_queue_work(mt76_hw(dev), &dev->mcu_work); + mt7615_init_txpower(dev, &dev->mphy.sband_2g.sband); + mt7615_init_txpower(dev, &dev->mphy.sband_5g.sband); + + if (dev->dbdc_support) { + ret = mt7615_register_ext_phy(dev); + if (ret) + return ret; + } + + return mt7615_init_debugfs(dev); +} + +void mt7615_unregister_device(struct mt7615_dev *dev) +{ + bool mcu_running; + + mcu_running = mt7615_wait_for_mcu_init(dev); + + mt7615_unregister_ext_phy(dev); + mt76_unregister_device(&dev->mt76); + if (mcu_running) + mt7615_mcu_exit(dev); + + mt7615_tx_token_put(dev); + mt7615_dma_cleanup(dev); + tasklet_disable(&dev->mt76.irq_tasklet); + + mt76_free_device(&dev->mt76); +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/pci_mac.c b/drivers/net/wireless/mediatek/mt76/mt7615/pci_mac.c new file mode 100644 index 000000000..0aca590f2 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7615/pci_mac.c @@ -0,0 +1,296 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2020 MediaTek Inc. + * + * Author: Ryder Lee + * Roy Luo + * Felix Fietkau + * Lorenzo Bianconi + */ + +#include +#include + +#include "mt7615.h" +#include "../dma.h" +#include "mac.h" + +static void +mt7615_write_fw_txp(struct mt7615_dev *dev, struct mt76_tx_info *tx_info, + void *txp_ptr, u32 id) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx_info->skb->data; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx_info->skb); + struct ieee80211_key_conf *key = info->control.hw_key; + struct ieee80211_vif *vif = info->control.vif; + struct mt76_connac_fw_txp *txp = txp_ptr; + u8 *rept_wds_wcid = (u8 *)&txp->rept_wds_wcid; + int nbuf = tx_info->nbuf - 1; + int i; + + for (i = 0; i < nbuf; i++) { + txp->buf[i] = cpu_to_le32(tx_info->buf[i + 1].addr); + txp->len[i] = cpu_to_le16(tx_info->buf[i + 1].len); + } + txp->nbuf = nbuf; + + /* pass partial skb header to fw */ + tx_info->buf[0].len = MT_TXD_SIZE + sizeof(*txp); + tx_info->buf[1].len = MT_CT_PARSE_LEN; + tx_info->buf[1].skip_unmap = true; + tx_info->nbuf = MT_CT_DMA_BUF_NUM; + + txp->flags = cpu_to_le16(MT_CT_INFO_APPLY_TXD); + + if (!key) + txp->flags |= cpu_to_le16(MT_CT_INFO_NONE_CIPHER_FRAME); + + if (ieee80211_is_mgmt(hdr->frame_control)) + txp->flags |= cpu_to_le16(MT_CT_INFO_MGMT_FRAME); + + if (vif) { + struct mt76_vif_link *mvif = (struct mt76_vif_link *)vif->drv_priv; + + txp->bss_idx = mvif->idx; + } + + txp->token = cpu_to_le16(id); + *rept_wds_wcid = 0xff; +} + +int mt7615_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, + enum mt76_txq_id qid, struct mt76_wcid *wcid, + struct ieee80211_sta *sta, + struct mt76_tx_info *tx_info) +{ + struct mt7615_dev *dev = container_of(mdev, struct mt7615_dev, mt76); + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx_info->skb); + struct ieee80211_key_conf *key = info->control.hw_key; + int pid, id; + u8 *txwi = (u8 *)txwi_ptr; + struct mt76_txwi_cache *t; + struct mt7615_sta *msta = NULL; + void *txp; + + if (wcid && wcid->sta) + msta = container_of(wcid, struct mt7615_sta, wcid); + if (!wcid) + wcid = &dev->mt76.global_wcid; + + if ((info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) && msta) { + struct mt7615_phy *phy = &dev->phy; + u8 phy_idx = (info->hw_queue & MT_TX_HW_QUEUE_PHY) >> 2; + + if (phy_idx && mdev->phys[MT_BAND1]) + phy = mdev->phys[MT_BAND1]->priv; + + spin_lock_bh(&dev->mt76.lock); + mt7615_mac_set_rates(phy, msta, &info->control.rates[0], + msta->rates); + spin_unlock_bh(&dev->mt76.lock); + } + + t = (struct mt76_txwi_cache *)(txwi + mdev->drv->txwi_size); + t->skb = tx_info->skb; + + id = mt76_token_get(mdev, &t); + if (id < 0) + return id; + + pid = mt76_tx_status_skb_add(mdev, wcid, tx_info->skb); + mt7615_mac_write_txwi(dev, txwi_ptr, tx_info->skb, wcid, sta, + pid, key, qid, false); + + txp = txwi + MT_TXD_SIZE; + memset(txp, 0, sizeof(struct mt76_connac_txp_common)); + if (is_mt7615(&dev->mt76)) + mt7615_write_fw_txp(dev, tx_info, txp, id); + else + mt76_connac_write_hw_txp(mdev, tx_info, txp, id); + + tx_info->skb = NULL; + + return 0; +} + +void mt7615_dma_reset(struct mt7615_dev *dev) +{ + int i; + + mt76_clear(dev, MT_WPDMA_GLO_CFG, + MT_WPDMA_GLO_CFG_RX_DMA_EN | MT_WPDMA_GLO_CFG_TX_DMA_EN | + MT_WPDMA_GLO_CFG_TX_WRITEBACK_DONE); + + usleep_range(1000, 2000); + + for (i = 0; i < __MT_TXQ_MAX; i++) + mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[i], true); + + for (i = 0; i < __MT_MCUQ_MAX; i++) + mt76_queue_tx_cleanup(dev, dev->mt76.q_mcu[i], true); + + mt76_for_each_q_rx(&dev->mt76, i) + mt76_queue_rx_reset(dev, i); + + mt76_tx_status_check(&dev->mt76, true); + + mt7615_dma_start(dev); +} +EXPORT_SYMBOL_GPL(mt7615_dma_reset); + +static void +mt7615_hif_int_event_trigger(struct mt7615_dev *dev, u8 event) +{ + u32 reg = MT_MCU_INT_EVENT; + + if (is_mt7663(&dev->mt76)) + reg = MT7663_MCU_INT_EVENT; + + mt76_wr(dev, reg, event); + + mt7622_trigger_hif_int(dev, true); + mt7622_trigger_hif_int(dev, false); +} + +static bool +mt7615_wait_reset_state(struct mt7615_dev *dev, u32 state) +{ + bool ret; + + ret = wait_event_timeout(dev->reset_wait, + (READ_ONCE(dev->reset_state) & state), + MT7615_RESET_TIMEOUT); + WARN(!ret, "Timeout waiting for MCU reset state %x\n", state); + return ret; +} + +static void +mt7615_update_vif_beacon(void *priv, u8 *mac, struct ieee80211_vif *vif) +{ + struct ieee80211_hw *hw = priv; + struct mt7615_dev *dev = mt7615_hw_dev(hw); + + switch (vif->type) { + case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_AP: + mt7615_mcu_add_beacon(dev, hw, vif, + vif->bss_conf.enable_beacon); + break; + default: + break; + } +} + +static void +mt7615_update_beacons(struct mt7615_dev *dev) +{ + struct mt76_phy *mphy_ext = dev->mt76.phys[MT_BAND1]; + + ieee80211_iterate_active_interfaces(dev->mt76.hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7615_update_vif_beacon, dev->mt76.hw); + + if (!mphy_ext) + return; + + ieee80211_iterate_active_interfaces(mphy_ext->hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7615_update_vif_beacon, mphy_ext->hw); +} + +void mt7615_mac_reset_work(struct work_struct *work) +{ + struct mt7615_phy *phy2; + struct mt76_phy *ext_phy; + struct mt7615_dev *dev; + unsigned long timeout; + int i; + + dev = container_of(work, struct mt7615_dev, reset_work); + ext_phy = dev->mt76.phys[MT_BAND1]; + phy2 = ext_phy ? ext_phy->priv : NULL; + + if (!(READ_ONCE(dev->reset_state) & MT_MCU_CMD_STOP_PDMA)) + return; + + ieee80211_stop_queues(mt76_hw(dev)); + if (ext_phy) + ieee80211_stop_queues(ext_phy->hw); + + set_bit(MT76_RESET, &dev->mphy.state); + set_bit(MT76_MCU_RESET, &dev->mphy.state); + wake_up(&dev->mt76.mcu.wait); + cancel_delayed_work_sync(&dev->mphy.mac_work); + timer_delete_sync(&dev->phy.roc_timer); + cancel_work_sync(&dev->phy.roc_work); + if (phy2) { + set_bit(MT76_RESET, &phy2->mt76->state); + cancel_delayed_work_sync(&phy2->mt76->mac_work); + timer_delete_sync(&phy2->roc_timer); + cancel_work_sync(&phy2->roc_work); + } + + /* lock/unlock all queues to ensure that no tx is pending */ + mt76_txq_schedule_all(&dev->mphy); + if (ext_phy) + mt76_txq_schedule_all(ext_phy); + + mt76_worker_disable(&dev->mt76.tx_worker); + mt76_for_each_q_rx(&dev->mt76, i) + napi_disable(&dev->mt76.napi[i]); + napi_disable(&dev->mt76.tx_napi); + + mt7615_mutex_acquire(dev); + + mt7615_hif_int_event_trigger(dev, MT_MCU_INT_EVENT_PDMA_STOPPED); + + if (mt7615_wait_reset_state(dev, MT_MCU_CMD_RESET_DONE)) { + mt7615_dma_reset(dev); + + mt7615_tx_token_put(dev); + idr_init(&dev->mt76.token); + + mt76_wr(dev, MT_WPDMA_MEM_RNG_ERR, 0); + + mt7615_hif_int_event_trigger(dev, MT_MCU_INT_EVENT_PDMA_INIT); + mt7615_wait_reset_state(dev, MT_MCU_CMD_RECOVERY_DONE); + } + + clear_bit(MT76_MCU_RESET, &dev->mphy.state); + clear_bit(MT76_RESET, &dev->mphy.state); + if (phy2) + clear_bit(MT76_RESET, &phy2->mt76->state); + + mt76_worker_enable(&dev->mt76.tx_worker); + + napi_enable(&dev->mt76.tx_napi); + mt76_for_each_q_rx(&dev->mt76, i) { + napi_enable(&dev->mt76.napi[i]); + } + + local_bh_disable(); + napi_schedule(&dev->mt76.tx_napi); + mt76_for_each_q_rx(&dev->mt76, i) { + napi_schedule(&dev->mt76.napi[i]); + } + local_bh_enable(); + + ieee80211_wake_queues(mt76_hw(dev)); + if (ext_phy) + ieee80211_wake_queues(ext_phy->hw); + + mt7615_hif_int_event_trigger(dev, MT_MCU_INT_EVENT_RESET_DONE); + mt7615_wait_reset_state(dev, MT_MCU_CMD_NORMAL_STATE); + + mt7615_update_beacons(dev); + + mt7615_mutex_release(dev); + + timeout = mt7615_get_macwork_timeout(dev); + ieee80211_queue_delayed_work(mt76_hw(dev), &dev->mphy.mac_work, + timeout); + if (phy2) + ieee80211_queue_delayed_work(ext_phy->hw, + &phy2->mt76->mac_work, timeout); + +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/regs.h b/drivers/net/wireless/mediatek/mt76/mt7615/regs.h new file mode 100644 index 000000000..e4133e918 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7615/regs.h @@ -0,0 +1,619 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* Copyright (C) 2019 MediaTek Inc. */ + +#ifndef __MT7615_REGS_H +#define __MT7615_REGS_H + +enum mt7615_reg_base { + MT_TOP_CFG_BASE, + MT_HW_BASE, + MT_DMA_SHDL_BASE, + MT_PCIE_REMAP_2, + MT_ARB_BASE, + MT_HIF_BASE, + MT_CSR_BASE, + MT_PLE_BASE, + MT_PSE_BASE, + MT_CFG_BASE, + MT_AGG_BASE, + MT_TMAC_BASE, + MT_RMAC_BASE, + MT_DMA_BASE, + MT_PF_BASE, + MT_WTBL_BASE_ON, + MT_WTBL_BASE_OFF, + MT_LPON_BASE, + MT_MIB_BASE, + MT_WTBL_BASE_ADDR, + MT_PCIE_REMAP_BASE2, + MT_TOP_MISC_BASE, + MT_EFUSE_ADDR_BASE, + MT_PP_BASE, + __MT_BASE_MAX, +}; + +#define MT_HW_INFO_BASE ((dev)->reg_map[MT_HW_BASE]) +#define MT_HW_INFO(ofs) (MT_HW_INFO_BASE + (ofs)) +#define MT_HW_REV MT_HW_INFO(0x000) +#define MT_HW_CHIPID MT_HW_INFO(0x008) +#define MT_TOP_STRAP_STA MT_HW_INFO(0x010) +#define MT_TOP_3NSS BIT(24) + +#define MT_TOP_OFF_RSV 0x1128 +#define MT_TOP_OFF_RSV_FW_STATE GENMASK(18, 16) + +#define MT_TOP_MISC2 ((dev)->reg_map[MT_TOP_CFG_BASE] + 0x134) +#define MT_TOP_MISC2_FW_STATE GENMASK(2, 0) + +#define MT7663_TOP_MISC2_FW_STATE GENMASK(3, 1) +#define MT_TOP_MISC2_FW_PWR_ON BIT(1) + +#define MT_MCU_BASE 0x2000 +#define MT_MCU(ofs) (MT_MCU_BASE + (ofs)) + +#define MT_MCU_PCIE_REMAP_1 MT_MCU(0x500) +#define MT_MCU_PCIE_REMAP_1_OFFSET GENMASK(17, 0) +#define MT_MCU_PCIE_REMAP_1_BASE GENMASK(31, 18) +#define MT_PCIE_REMAP_BASE_1 0x40000 + +#define MT_MCU_PCIE_REMAP_2 ((dev)->reg_map[MT_PCIE_REMAP_2]) +#define MT_MCU_PCIE_REMAP_2_OFFSET GENMASK(18, 0) +#define MT_MCU_PCIE_REMAP_2_BASE GENMASK(31, 19) +#define MT_PCIE_REMAP_BASE_2 ((dev)->reg_map[MT_PCIE_REMAP_BASE2]) + +#define MT_MCU_CIRQ_BASE 0xc0000 +#define MT_MCU_CIRQ(ofs) (MT_MCU_CIRQ_BASE + (ofs)) + +#define MT_MCU_CIRQ_IRQ_SEL(n) MT_MCU_CIRQ((n) << 2) + +#define MT_HIF(ofs) ((dev)->reg_map[MT_HIF_BASE] + (ofs)) +#define MT_HIF_RST MT_HIF(0x100) +#define MT_HIF_LOGIC_RST_N BIT(4) + +#define MT_PDMA_SLP_PROT MT_HIF(0x154) +#define MT_PDMA_AXI_SLPPROT_ENABLE BIT(0) +#define MT_PDMA_AXI_SLPPROT_RDY BIT(16) + +#define MT_PDMA_BUSY_STATUS MT_HIF(0x168) +#define MT_PDMA_TX_IDX_BUSY BIT(2) +#define MT_PDMA_BUSY_IDX BIT(31) + +#define MT_WPDMA_TX_RING0_CTRL0 MT_HIF(0x300) +#define MT_WPDMA_TX_RING0_CTRL1 MT_HIF(0x304) + +#define MT7663_MCU_PCIE_REMAP_2_OFFSET GENMASK(15, 0) +#define MT7663_MCU_PCIE_REMAP_2_BASE GENMASK(31, 16) + +#define MT_HIF2_BASE 0xf0000 +#define MT_HIF2(ofs) (MT_HIF2_BASE + (ofs)) +#define MT_PCIE_IRQ_ENABLE MT_HIF2(0x188) +#define MT_PCIE_DOORBELL_PUSH MT_HIF2(0x1484) + +#define MT_CFG_LPCR_HOST MT_HIF(0x1f0) +#define MT_CFG_LPCR_HOST_FW_OWN BIT(0) +#define MT_CFG_LPCR_HOST_DRV_OWN BIT(1) + +#define MT_MCU2HOST_INT_STATUS MT_HIF(0x1f0) +#define MT_MCU2HOST_INT_ENABLE MT_HIF(0x1f4) + +#define MT7663_MCU_INT_EVENT MT_HIF(0x108) +#define MT_MCU_INT_EVENT MT_HIF(0x1f8) +#define MT_MCU_INT_EVENT_PDMA_STOPPED BIT(0) +#define MT_MCU_INT_EVENT_PDMA_INIT BIT(1) +#define MT_MCU_INT_EVENT_SER_TRIGGER BIT(2) +#define MT_MCU_INT_EVENT_RESET_DONE BIT(3) + +#define MT_INT_SOURCE_CSR MT_HIF(0x200) +#define MT_INT_MASK_CSR MT_HIF(0x204) +#define MT_DELAY_INT_CFG MT_HIF(0x210) + +#define MT_INT_RX_DONE(_n) BIT(_n) +#define MT_INT_RX_DONE_ALL GENMASK(1, 0) +#define MT_INT_TX_DONE_ALL GENMASK(19, 4) +#define MT_INT_TX_DONE(_n) BIT((_n) + 4) +#define MT7663_INT_MCU_CMD BIT(29) +#define MT_INT_MCU_CMD BIT(30) + +#define MT_WPDMA_GLO_CFG MT_HIF(0x208) +#define MT_WPDMA_GLO_CFG_TX_DMA_EN BIT(0) +#define MT_WPDMA_GLO_CFG_TX_DMA_BUSY BIT(1) +#define MT_WPDMA_GLO_CFG_RX_DMA_EN BIT(2) +#define MT_WPDMA_GLO_CFG_RX_DMA_BUSY BIT(3) +#define MT_WPDMA_GLO_CFG_DMA_BURST_SIZE GENMASK(5, 4) +#define MT_WPDMA_GLO_CFG_TX_WRITEBACK_DONE BIT(6) +#define MT_WPDMA_GLO_CFG_BIG_ENDIAN BIT(7) +#define MT_WPDMA_GLO_CFG_TX_BT_SIZE_BIT0 BIT(9) +#define MT_WPDMA_GLO_CFG_BYPASS_TX_SCH BIT(9) /* MT7622 */ +#define MT_WPDMA_GLO_CFG_MULTI_DMA_EN GENMASK(11, 10) +#define MT_WPDMA_GLO_CFG_FIFO_LITTLE_ENDIAN BIT(12) +#define MT_WPDMA_GLO_CFG_TX_BT_SIZE_BIT21 GENMASK(23, 22) +#define MT_WPDMA_GLO_CFG_SW_RESET BIT(24) +#define MT_WPDMA_GLO_CFG_FIRST_TOKEN_ONLY BIT(26) +#define MT_WPDMA_GLO_CFG_OMIT_TX_INFO BIT(28) + +#define MT_WPDMA_RST_IDX MT_HIF(0x20c) + +#define MT_WPDMA_MEM_RNG_ERR MT_HIF(0x224) + +#define MT_MCU_CMD MT_HIF(0x234) +#define MT_MCU_CMD_CLEAR_FW_OWN BIT(0) +#define MT_MCU_CMD_STOP_PDMA_FW_RELOAD BIT(1) +#define MT_MCU_CMD_STOP_PDMA BIT(2) +#define MT_MCU_CMD_RESET_DONE BIT(3) +#define MT_MCU_CMD_RECOVERY_DONE BIT(4) +#define MT_MCU_CMD_NORMAL_STATE BIT(5) +#define MT_MCU_CMD_LMAC_ERROR BIT(24) +#define MT_MCU_CMD_PSE_ERROR BIT(25) +#define MT_MCU_CMD_PLE_ERROR BIT(26) +#define MT_MCU_CMD_PDMA_ERROR BIT(27) +#define MT_MCU_CMD_PCIE_ERROR BIT(28) +#define MT_MCU_CMD_ERROR_MASK (GENMASK(5, 1) | GENMASK(28, 24)) +#define MT7663_MCU_CMD_ERROR_MASK GENMASK(5, 2) + +#define MT_TX_RING_BASE MT_HIF(0x300) +#define MT_RX_RING_BASE MT_HIF(0x400) + +#define MT_WPDMA_GLO_CFG1 MT_HIF(0x500) +#define MT_WPDMA_TX_PRE_CFG MT_HIF(0x510) +#define MT_WPDMA_RX_PRE_CFG MT_HIF(0x520) +#define MT_WPDMA_ABT_CFG MT_HIF(0x530) +#define MT_WPDMA_ABT_CFG1 MT_HIF(0x534) + +#define MT_CSR(ofs) ((dev)->reg_map[MT_CSR_BASE] + (ofs)) +#define MT_CONN_HIF_ON_LPCTL MT_CSR(0x000) + +#define MT_PLE(ofs) ((dev)->reg_map[MT_PLE_BASE] + (ofs)) + +#define MT_PLE_PG_HIF0_GROUP MT_PLE(0x110) +#define MT_HIF0_MIN_QUOTA GENMASK(11, 0) +#define MT_PLE_FL_Q0_CTRL MT_PLE(0x1b0) +#define MT_PLE_FL_Q1_CTRL MT_PLE(0x1b4) +#define MT_PLE_FL_Q2_CTRL MT_PLE(0x1b8) +#define MT_PLE_FL_Q3_CTRL MT_PLE(0x1bc) + +#define MT_PLE_AC_QEMPTY(ac, n) MT_PLE(0x300 + 0x10 * (ac) + \ + ((n) << 2)) + +#define MT_PSE(ofs) ((dev)->reg_map[MT_PSE_BASE] + (ofs)) +#define MT_PSE_PG_HIF0_GROUP MT_PSE(0x110) +#define MT_HIF0_MIN_QUOTA GENMASK(11, 0) +#define MT_PSE_PG_HIF1_GROUP MT_PSE(0x118) +#define MT_HIF1_MIN_QUOTA GENMASK(11, 0) +#define MT_PSE_QUEUE_EMPTY MT_PSE(0x0b4) +#define MT_HIF_0_EMPTY_MASK BIT(16) +#define MT_HIF_1_EMPTY_MASK BIT(17) +#define MT_HIF_ALL_EMPTY_MASK GENMASK(17, 16) +#define MT_PSE_PG_INFO MT_PSE(0x194) +#define MT_PSE_SRC_CNT GENMASK(27, 16) + +#define MT_PP(ofs) ((dev)->reg_map[MT_PP_BASE] + (ofs)) +#define MT_PP_TXDWCNT MT_PP(0x0) +#define MT_PP_TXDWCNT_TX0_ADD_DW_CNT GENMASK(7, 0) +#define MT_PP_TXDWCNT_TX1_ADD_DW_CNT GENMASK(15, 8) + +#define MT_WF_PHY_BASE 0x82070000 +#define MT_WF_PHY(ofs) (MT_WF_PHY_BASE + (ofs)) + +#define MT_WF_PHY_WF2_RFCTRL0(n) MT_WF_PHY(0x1900 + (n) * 0x400) +#define MT_WF_PHY_WF2_RFCTRL0_LPBCN_EN BIT(9) + +#define MT_WF_PHY_R0_PHYMUX_5(_phy) MT_WF_PHY(0x0614 + ((_phy) << 9)) +#define MT7663_WF_PHY_R0_PHYMUX_5 MT_WF_PHY(0x0414) + +#define MT_WF_PHY_R0_PHYCTRL_STS0(_phy) MT_WF_PHY(0x020c + ((_phy) << 9)) +#define MT_WF_PHYCTRL_STAT_PD_OFDM GENMASK(31, 16) +#define MT_WF_PHYCTRL_STAT_PD_CCK GENMASK(15, 0) + +#define MT7663_WF_PHY_R0_PHYCTRL_STS0(_phy) MT_WF_PHY(0x0210 + ((_phy) << 12)) + +#define MT_WF_PHY_R0_PHYCTRL_STS5(_phy) MT_WF_PHY(0x0220 + ((_phy) << 9)) +#define MT_WF_PHYCTRL_STAT_MDRDY_OFDM GENMASK(31, 16) +#define MT_WF_PHYCTRL_STAT_MDRDY_CCK GENMASK(15, 0) + +#define MT7663_WF_PHY_R0_PHYCTRL_STS5(_phy) MT_WF_PHY(0x0224 + ((_phy) << 12)) + +#define MT_WF_PHY_GID_TAB_VLD(_phy, i) MT_WF_PHY(0x0254 + (i) * 4 + \ + ((_phy) << 9)) +#define MT7663_WF_PHY_GID_TAB_VLD(_phy, i) MT_WF_PHY(0x0254 + (i) * 4 + \ + ((_phy) << 12)) +#define MT_WF_PHY_GID_TAB_POS(_phy, i) MT_WF_PHY(0x025c + (i) * 4 + \ + ((_phy) << 9)) +#define MT7663_WF_PHY_GID_TAB_POS(_phy, i) MT_WF_PHY(0x025c + (i) * 4 + \ + ((_phy) << 12)) + +#define MT_WF_PHY_MIN_PRI_PWR(_phy) MT_WF_PHY((_phy) ? 0x084 : 0x229c) +#define MT_WF_PHY_PD_OFDM_MASK(_phy) ((_phy) ? GENMASK(24, 16) : \ + GENMASK(28, 20)) +#define MT_WF_PHY_PD_OFDM(_phy, v) ((v) << ((_phy) ? 16 : 20)) +#define MT_WF_PHY_PD_BLK(_phy) ((_phy) ? BIT(25) : BIT(19)) + +#define MT7663_WF_PHY_MIN_PRI_PWR(_phy) MT_WF_PHY((_phy) ? 0x2aec : 0x22f0) + +#define MT_WF_PHY_RXTD_BASE MT_WF_PHY(0x2200) +#define MT_WF_PHY_RXTD(_n) (MT_WF_PHY_RXTD_BASE + ((_n) << 2)) + +#define MT7663_WF_PHY_RXTD(_n) (MT_WF_PHY(0x25b0) + ((_n) << 2)) + +#define MT_WF_PHY_RXTD_CCK_PD(_phy) MT_WF_PHY((_phy) ? 0x2314 : 0x2310) +#define MT_WF_PHY_PD_CCK_MASK(_phy) (_phy) ? GENMASK(31, 24) : \ + GENMASK(8, 1) +#define MT_WF_PHY_PD_CCK(_phy, v) ((v) << ((_phy) ? 24 : 1)) + +#define MT7663_WF_PHY_RXTD_CCK_PD(_phy) MT_WF_PHY((_phy) ? 0x2350 : 0x234c) + +#define MT_WF_PHY_RXTD2_BASE MT_WF_PHY(0x2a00) +#define MT_WF_PHY_RXTD2(_n) (MT_WF_PHY_RXTD2_BASE + ((_n) << 2)) + +#define MT_WF_PHY_RFINTF3_0(_n) MT_WF_PHY(0x1100 + (_n) * 0x400) +#define MT_WF_PHY_RFINTF3_0_ANT GENMASK(7, 4) + +#define MT_WF_CFG_BASE ((dev)->reg_map[MT_CFG_BASE]) +#define MT_WF_CFG(ofs) (MT_WF_CFG_BASE + (ofs)) + +#define MT_CFG_CCR MT_WF_CFG(0x000) +#define MT_CFG_CCR_MAC_D1_1X_GC_EN BIT(24) +#define MT_CFG_CCR_MAC_D0_1X_GC_EN BIT(25) +#define MT_CFG_CCR_MAC_D1_2X_GC_EN BIT(30) +#define MT_CFG_CCR_MAC_D0_2X_GC_EN BIT(31) + +#define MT_WF_AGG_BASE ((dev)->reg_map[MT_AGG_BASE]) +#define MT_WF_AGG(ofs) (MT_WF_AGG_BASE + (ofs)) + +#define MT_AGG_ARCR MT_WF_AGG(0x010) +#define MT_AGG_ARCR_INIT_RATE1 BIT(0) +#define MT_AGG_ARCR_RTS_RATE_THR GENMASK(12, 8) +#define MT_AGG_ARCR_RATE_DOWN_RATIO GENMASK(17, 16) +#define MT_AGG_ARCR_RATE_DOWN_RATIO_EN BIT(19) +#define MT_AGG_ARCR_RATE_UP_EXTRA_TH GENMASK(22, 20) + +#define MT_AGG_ARUCR(_band) MT_WF_AGG(0x018 + (_band) * 0x100) +#define MT_AGG_ARDCR(_band) MT_WF_AGG(0x01c + (_band) * 0x100) +#define MT_AGG_ARxCR_LIMIT_SHIFT(_n) (4 * (_n)) +#define MT_AGG_ARxCR_LIMIT(_n) GENMASK(2 + \ + MT_AGG_ARxCR_LIMIT_SHIFT(_n), \ + MT_AGG_ARxCR_LIMIT_SHIFT(_n)) + +#define MT_AGG_ASRCR0 MT_WF_AGG(0x060) +#define MT_AGG_ASRCR1 MT_WF_AGG(0x064) +#define MT_AGG_ASRCR_RANGE(val, n) (((val) >> ((n) << 3)) & GENMASK(5, 0)) + +#define MT_AGG_ACR(_band) MT_WF_AGG(0x070 + (_band) * 0x100) +#define MT_AGG_ACR_NO_BA_RULE BIT(0) +#define MT_AGG_ACR_NO_BA_AR_RULE BIT(1) +#define MT_AGG_ACR_PKT_TIME_EN BIT(2) +#define MT_AGG_ACR_CFEND_RATE GENMASK(15, 4) +#define MT_AGG_ACR_BAR_RATE GENMASK(31, 20) + +#define MT_AGG_SCR MT_WF_AGG(0x0fc) +#define MT_AGG_SCR_NLNAV_MID_PTEC_DIS BIT(3) + +#define MT_WF_ARB_BASE ((dev)->reg_map[MT_ARB_BASE]) +#define MT_WF_ARB(ofs) (MT_WF_ARB_BASE + (ofs)) + +#define MT_ARB_RQCR MT_WF_ARB(0x070) +#define MT_ARB_RQCR_RX_START BIT(0) +#define MT_ARB_RQCR_RXV_START BIT(4) +#define MT_ARB_RQCR_RXV_R_EN BIT(7) +#define MT_ARB_RQCR_RXV_T_EN BIT(8) +#define MT_ARB_RQCR_BAND_SHIFT 16 + +#define MT_ARB_SCR MT_WF_ARB(0x080) +#define MT_ARB_SCR_TX0_DISABLE BIT(8) +#define MT_ARB_SCR_RX0_DISABLE BIT(9) +#define MT_ARB_SCR_TX1_DISABLE BIT(10) +#define MT_ARB_SCR_RX1_DISABLE BIT(11) + +#define MT_WF_TMAC_BASE ((dev)->reg_map[MT_TMAC_BASE]) +#define MT_WF_TMAC(ofs) (MT_WF_TMAC_BASE + (ofs)) + +#define MT_TMAC_CDTR MT_WF_TMAC(0x090) +#define MT_TMAC_ODTR MT_WF_TMAC(0x094) +#define MT_TIMEOUT_VAL_PLCP GENMASK(15, 0) +#define MT_TIMEOUT_VAL_CCA GENMASK(31, 16) + +#define MT_TMAC_TRCR(_band) MT_WF_TMAC((_band) ? 0x070 : 0x09c) +#define MT_TMAC_TRCR_CCA_SEL GENMASK(31, 30) +#define MT_TMAC_TRCR_SEC_CCA_SEL GENMASK(29, 28) + +#define MT_TMAC_ICR(_band) MT_WF_TMAC((_band) ? 0x074 : 0x0a4) +#define MT_IFS_EIFS GENMASK(8, 0) +#define MT_IFS_RIFS GENMASK(14, 10) +#define MT_IFS_SIFS GENMASK(22, 16) +#define MT_IFS_SLOT GENMASK(30, 24) + +#define MT_TMAC_CTCR0 MT_WF_TMAC(0x0f4) +#define MT_TMAC_CTCR0_INS_DDLMT_REFTIME GENMASK(5, 0) +#define MT_TMAC_CTCR0_INS_DDLMT_DENSITY GENMASK(15, 12) +#define MT_TMAC_CTCR0_INS_DDLMT_EN BIT(17) +#define MT_TMAC_CTCR0_INS_DDLMT_VHT_SMPDU_EN BIT(18) + +#define MT_WF_RMAC_BASE ((dev)->reg_map[MT_RMAC_BASE]) +#define MT_WF_RMAC(ofs) (MT_WF_RMAC_BASE + (ofs)) + +#define MT_WF_RFCR(_band) MT_WF_RMAC((_band) ? 0x100 : 0x000) +#define MT_WF_RFCR_DROP_STBC_MULTI BIT(0) +#define MT_WF_RFCR_DROP_FCSFAIL BIT(1) +#define MT_WF_RFCR_DROP_VERSION BIT(3) +#define MT_WF_RFCR_DROP_PROBEREQ BIT(4) +#define MT_WF_RFCR_DROP_MCAST BIT(5) +#define MT_WF_RFCR_DROP_BCAST BIT(6) +#define MT_WF_RFCR_DROP_MCAST_FILTERED BIT(7) +#define MT_WF_RFCR_DROP_A3_MAC BIT(8) +#define MT_WF_RFCR_DROP_A3_BSSID BIT(9) +#define MT_WF_RFCR_DROP_A2_BSSID BIT(10) +#define MT_WF_RFCR_DROP_OTHER_BEACON BIT(11) +#define MT_WF_RFCR_DROP_FRAME_REPORT BIT(12) +#define MT_WF_RFCR_DROP_CTL_RSV BIT(13) +#define MT_WF_RFCR_DROP_CTS BIT(14) +#define MT_WF_RFCR_DROP_RTS BIT(15) +#define MT_WF_RFCR_DROP_DUPLICATE BIT(16) +#define MT_WF_RFCR_DROP_OTHER_BSS BIT(17) +#define MT_WF_RFCR_DROP_OTHER_UC BIT(18) +#define MT_WF_RFCR_DROP_OTHER_TIM BIT(19) +#define MT_WF_RFCR_DROP_NDPA BIT(20) +#define MT_WF_RFCR_DROP_UNWANTED_CTL BIT(21) + +#define MT_WF_RMAC_MORE(_band) MT_WF_RMAC((_band) ? 0x124 : 0x024) +#define MT_WF_RMAC_MORE_MUAR_MODE GENMASK(31, 30) + +#define MT_WF_RFCR1(_band) MT_WF_RMAC((_band) ? 0x104 : 0x004) +#define MT_WF_RFCR1_DROP_ACK BIT(4) +#define MT_WF_RFCR1_DROP_BF_POLL BIT(5) +#define MT_WF_RFCR1_DROP_BA BIT(6) +#define MT_WF_RFCR1_DROP_CFEND BIT(7) +#define MT_WF_RFCR1_DROP_CFACK BIT(8) + +#define MT_CHFREQ(_band) MT_WF_RMAC((_band) ? 0x130 : 0x030) + +#define MT_WF_RMAC_MAR0 MT_WF_RMAC(0x025c) +#define MT_WF_RMAC_MAR1 MT_WF_RMAC(0x0260) +#define MT_WF_RMAC_MAR1_ADDR GENMASK(15, 0) +#define MT_WF_RMAC_MAR1_START BIT(16) +#define MT_WF_RMAC_MAR1_WRITE BIT(17) +#define MT_WF_RMAC_MAR1_IDX GENMASK(29, 24) +#define MT_WF_RMAC_MAR1_GROUP GENMASK(31, 30) + +#define MT_WF_RMAC_MIB_TIME0 MT_WF_RMAC(0x03c4) +#define MT_WF_RMAC_MIB_RXTIME_CLR BIT(31) +#define MT_WF_RMAC_MIB_RXTIME_EN BIT(30) + +#define MT_WF_RMAC_MIB_AIRTIME0 MT_WF_RMAC(0x0380) + +#define MT_WF_RMAC_MIB_TIME5 MT_WF_RMAC(0x03d8) +#define MT_WF_RMAC_MIB_TIME6 MT_WF_RMAC(0x03dc) +#define MT_MIB_OBSSTIME_MASK GENMASK(23, 0) + +#define MT_WF_DMA_BASE ((dev)->reg_map[MT_DMA_BASE]) +#define MT_WF_DMA(ofs) (MT_WF_DMA_BASE + (ofs)) + +#define MT_DMA_DCR0 MT_WF_DMA(0x000) +#define MT_DMA_DCR0_MAX_RX_LEN GENMASK(15, 2) +#define MT_DMA_DCR0_DAMSDU_EN BIT(16) +#define MT_DMA_DCR0_RX_VEC_DROP BIT(17) +#define MT_DMA_DCR0_RX_HDR_TRANS_EN BIT(19) + +#define MT_DMA_RCFR0(_band) MT_WF_DMA(0x070 + (_band) * 0x40) +#define MT_DMA_RCFR0_MCU_RX_MGMT BIT(2) +#define MT_DMA_RCFR0_MCU_RX_CTL_NON_BAR BIT(3) +#define MT_DMA_RCFR0_MCU_RX_CTL_BAR BIT(4) +#define MT_DMA_RCFR0_MCU_RX_TDLS BIT(19) +#define MT_DMA_RCFR0_MCU_RX_BYPASS BIT(21) +#define MT_DMA_RCFR0_RX_DROPPED_UCAST GENMASK(25, 24) +#define MT_DMA_RCFR0_RX_DROPPED_MCAST GENMASK(27, 26) + +#define MT_WF_PF_BASE ((dev)->reg_map[MT_PF_BASE]) +#define MT_WF_PF(ofs) (MT_WF_PF_BASE + (ofs)) + +#define MT_WF_PFCR MT_WF_PF(0x000) +#define MT_WF_PFCR_TDLS_EN BIT(9) + +#define MT_WTBL_BASE(dev) ((dev)->reg_map[MT_WTBL_BASE_ADDR]) +#define MT_WTBL_ENTRY_SIZE 256 + +#define MT_WTBL_OFF_BASE ((dev)->reg_map[MT_WTBL_BASE_OFF]) +#define MT_WTBL_OFF(n) (MT_WTBL_OFF_BASE + (n)) + +#define MT_WTBL_W0_KEY_IDX GENMASK(24, 23) +#define MT_WTBL_W0_RX_KEY_VALID BIT(26) +#define MT_WTBL_W0_RX_IK_VALID BIT(27) + +#define MT_WTBL_W2_KEY_TYPE GENMASK(7, 4) + +#define MT_WTBL_UPDATE MT_WTBL_OFF(0x030) +#define MT_WTBL_UPDATE_WLAN_IDX GENMASK(7, 0) +#define MT_WTBL_UPDATE_RXINFO_UPDATE BIT(11) +#define MT_WTBL_UPDATE_ADM_COUNT_CLEAR BIT(12) +#define MT_WTBL_UPDATE_RATE_UPDATE BIT(13) +#define MT_WTBL_UPDATE_TX_COUNT_CLEAR BIT(14) +#define MT_WTBL_UPDATE_BUSY BIT(31) + +#define MT_TOP_MISC(ofs) ((dev)->reg_map[MT_TOP_MISC_BASE] + (ofs)) +#define MT_CONN_ON_MISC MT_TOP_MISC(0x1140) +#define MT_TOP_MISC2_FW_N9_RDY BIT(2) + +#define MT_WTBL_ON_BASE ((dev)->reg_map[MT_WTBL_BASE_ON]) +#define MT_WTBL_ON(_n) (MT_WTBL_ON_BASE + (_n)) + +#define MT_WTBL_RICR0 MT_WTBL_ON(0x010) +#define MT_WTBL_RICR1 MT_WTBL_ON(0x014) + +#define MT_WTBL_RIUCR0 MT_WTBL_ON(0x020) + +#define MT_WTBL_RIUCR1 MT_WTBL_ON(0x024) +#define MT_WTBL_RIUCR1_RATE0 GENMASK(11, 0) +#define MT_WTBL_RIUCR1_RATE1 GENMASK(23, 12) +#define MT_WTBL_RIUCR1_RATE2_LO GENMASK(31, 24) + +#define MT_WTBL_RIUCR2 MT_WTBL_ON(0x028) +#define MT_WTBL_RIUCR2_RATE2_HI GENMASK(3, 0) +#define MT_WTBL_RIUCR2_RATE3 GENMASK(15, 4) +#define MT_WTBL_RIUCR2_RATE4 GENMASK(27, 16) +#define MT_WTBL_RIUCR2_RATE5_LO GENMASK(31, 28) + +#define MT_WTBL_RIUCR3 MT_WTBL_ON(0x02c) +#define MT_WTBL_RIUCR3_RATE5_HI GENMASK(7, 0) +#define MT_WTBL_RIUCR3_RATE6 GENMASK(19, 8) +#define MT_WTBL_RIUCR3_RATE7 GENMASK(31, 20) + +#define MT_WTBL_W5_CHANGE_BW_RATE GENMASK(7, 5) +#define MT_WTBL_W5_SHORT_GI_20 BIT(8) +#define MT_WTBL_W5_SHORT_GI_40 BIT(9) +#define MT_WTBL_W5_SHORT_GI_80 BIT(10) +#define MT_WTBL_W5_SHORT_GI_160 BIT(11) +#define MT_WTBL_W5_BW_CAP GENMASK(13, 12) +#define MT_WTBL_W5_MPDU_FAIL_COUNT GENMASK(25, 23) +#define MT_WTBL_W5_MPDU_OK_COUNT GENMASK(28, 26) +#define MT_WTBL_W5_RATE_IDX GENMASK(31, 29) + +#define MT_WTBL_W27_CC_BW_SEL GENMASK(6, 5) + +#define MT_LPON(_n) ((dev)->reg_map[MT_LPON_BASE] + (_n)) + +#define MT_LPON_TCR0(_n) MT_LPON(0x010 + ((_n) * 4)) +#define MT_LPON_TCR2(_n) MT_LPON(0x0f8 + ((_n) - 2) * 4) +#define MT_LPON_TCR_MODE GENMASK(1, 0) +#define MT_LPON_TCR_READ GENMASK(1, 0) +#define MT_LPON_TCR_WRITE BIT(0) +#define MT_LPON_TCR_ADJUST BIT(1) + +#define MT_LPON_UTTR0 MT_LPON(0x018) +#define MT_LPON_UTTR1 MT_LPON(0x01c) + +#define MT_WF_MIB_BASE (dev->reg_map[MT_MIB_BASE]) +#define MT_WF_MIB(_band, ofs) (MT_WF_MIB_BASE + (ofs) + (_band) * 0x200) + +#define MT_WF_MIB_SCR0 MT_WF_MIB(0, 0) +#define MT_MIB_SCR0_AGG_CNT_RANGE_EN BIT(21) + +#define MT_MIB_M0_MISC_CR(_band) MT_WF_MIB(_band, 0x00c) + +#define MT_MIB_SDR3(_band) MT_WF_MIB(_band, 0x014) +#define MT_MIB_SDR3_FCS_ERR_MASK GENMASK(15, 0) + +#define MT_MIB_SDR9(_band) MT_WF_MIB(_band, 0x02c) +#define MT_MIB_SDR9_BUSY_MASK GENMASK(23, 0) + +#define MT_MIB_SDR14(_band) MT_WF_MIB(_band, 0x040) +#define MT_MIB_AMPDU_MPDU_COUNT GENMASK(23, 0) + +#define MT_MIB_SDR15(_band) MT_WF_MIB(_band, 0x044) +#define MT_MIB_AMPDU_ACK_COUNT GENMASK(23, 0) + +#define MT_MIB_SDR16(_band) MT_WF_MIB(_band, 0x048) +#define MT_MIB_SDR16_BUSY_MASK GENMASK(23, 0) + +#define MT_MIB_SDR36(_band) MT_WF_MIB(_band, 0x098) +#define MT_MIB_SDR36_TXTIME_MASK GENMASK(23, 0) +#define MT_MIB_SDR37(_band) MT_WF_MIB(_band, 0x09c) +#define MT_MIB_SDR37_RXTIME_MASK GENMASK(23, 0) + +#define MT_MIB_MB_SDR0(_band, n) MT_WF_MIB(_band, 0x100 + ((n) << 4)) +#define MT_MIB_RTS_RETRIES_COUNT_MASK GENMASK(31, 16) +#define MT_MIB_RTS_COUNT_MASK GENMASK(15, 0) + +#define MT_MIB_MB_SDR1(_band, n) MT_WF_MIB(_band, 0x104 + ((n) << 4)) +#define MT_MIB_BA_MISS_COUNT_MASK GENMASK(15, 0) +#define MT_MIB_ACK_FAIL_COUNT_MASK GENMASK(31, 16) + +#define MT_MIB_ARNG(n) MT_WF_MIB(0, 0x4b8 + ((n) << 2)) + +#define MT_TX_AGG_CNT(_band, n) MT_WF_MIB(_band, 0xa8 + ((n) << 2)) + +#define MT_DMA_SHDL(ofs) (dev->reg_map[MT_DMA_SHDL_BASE] + (ofs)) + +#define MT_DMASHDL_BASE 0x5000a000 +#define MT_DMASHDL_OPTIONAL 0x008 +#define MT_DMASHDL_PAGE 0x00c + +#define MT_DMASHDL_REFILL 0x010 + +#define MT_DMASHDL_PKT_MAX_SIZE 0x01c +#define MT_DMASHDL_PKT_MAX_SIZE_PLE GENMASK(11, 0) +#define MT_DMASHDL_PKT_MAX_SIZE_PSE GENMASK(27, 16) + +#define MT_DMASHDL_GROUP_QUOTA(_n) (0x020 + ((_n) << 2)) +#define MT_DMASHDL_GROUP_QUOTA_MIN GENMASK(11, 0) +#define MT_DMASHDL_GROUP_QUOTA_MAX GENMASK(27, 16) + +#define MT_DMASHDL_SCHED_SET0 0x0b0 +#define MT_DMASHDL_SCHED_SET1 0x0b4 + +#define MT_DMASHDL_Q_MAP(_n) (0x0d0 + ((_n) << 2)) +#define MT_DMASHDL_Q_MAP_MASK GENMASK(3, 0) +#define MT_DMASHDL_Q_MAP_SHIFT(_n) (4 * ((_n) % 8)) + +#define MT_LED_BASE_PHYS 0x80024000 +#define MT_LED_PHYS(_n) (MT_LED_BASE_PHYS + (_n)) + +#define MT_LED_CTRL MT_LED_PHYS(0x00) + +#define MT_LED_CTRL_REPLAY(_n) BIT(0 + (8 * (_n))) +#define MT_LED_CTRL_POLARITY(_n) BIT(1 + (8 * (_n))) +#define MT_LED_CTRL_TX_BLINK_MODE(_n) BIT(2 + (8 * (_n))) +#define MT_LED_CTRL_TX_MANUAL_BLINK(_n) BIT(3 + (8 * (_n))) +#define MT_LED_CTRL_BAND(_n) BIT(4 + (8 * (_n))) +#define MT_LED_CTRL_TX_OVER_BLINK(_n) BIT(5 + (8 * (_n))) +#define MT_LED_CTRL_KICK(_n) BIT(7 + (8 * (_n))) + +#define MT_LED_STATUS_0(_n) MT_LED_PHYS(0x10 + ((_n) * 8)) +#define MT_LED_STATUS_1(_n) MT_LED_PHYS(0x14 + ((_n) * 8)) +#define MT_LED_STATUS_OFF GENMASK(31, 24) +#define MT_LED_STATUS_ON GENMASK(23, 16) +#define MT_LED_STATUS_DURATION GENMASK(15, 0) + +#define MT_PDMA_BUSY 0x82000504 +#define MT_PDMA_TX_BUSY BIT(0) +#define MT_PDMA_RX_BUSY BIT(1) + +#define MT_EFUSE_BASE ((dev)->reg_map[MT_EFUSE_ADDR_BASE]) +#define MT_EFUSE_BASE_CTRL 0x000 +#define MT_EFUSE_BASE_CTRL_EMPTY BIT(30) + +#define MT_EFUSE_CTRL 0x008 +#define MT_EFUSE_CTRL_AOUT GENMASK(5, 0) +#define MT_EFUSE_CTRL_MODE GENMASK(7, 6) +#define MT_EFUSE_CTRL_LDO_OFF_TIME GENMASK(13, 8) +#define MT_EFUSE_CTRL_LDO_ON_TIME GENMASK(15, 14) +#define MT_EFUSE_CTRL_AIN GENMASK(25, 16) +#define MT_EFUSE_CTRL_VALID BIT(29) +#define MT_EFUSE_CTRL_KICK BIT(30) +#define MT_EFUSE_CTRL_SEL BIT(31) + +#define MT_EFUSE_WDATA(_i) (0x010 + ((_i) * 4)) +#define MT_EFUSE_RDATA(_i) (0x030 + ((_i) * 4)) + +/* INFRACFG host register range on MT7622 */ +#define MT_INFRACFG_MISC 0x700 +#define MT_INFRACFG_MISC_AP2CONN_WAKE BIT(1) + +#define MT_UMAC_BASE 0x7c000000 +#define MT_UMAC(ofs) (MT_UMAC_BASE + (ofs)) +#define MT_UDMA_TX_QSEL MT_UMAC(0x008) +#define MT_FW_DL_EN BIT(3) + +#define MT_UDMA_WLCFG_1 MT_UMAC(0x00c) +#define MT_WL_RX_AGG_PKT_LMT GENMASK(7, 0) +#define MT_WL_TX_TMOUT_LMT GENMASK(27, 8) + +#define MT_UDMA_WLCFG_0 MT_UMAC(0x18) +#define MT_WL_RX_AGG_TO GENMASK(7, 0) +#define MT_WL_RX_AGG_LMT GENMASK(15, 8) +#define MT_WL_TX_TMOUT_FUNC_EN BIT(16) +#define MT_WL_TX_DPH_CHK_EN BIT(17) +#define MT_WL_RX_MPSZ_PAD0 BIT(18) +#define MT_WL_RX_FLUSH BIT(19) +#define MT_TICK_1US_EN BIT(20) +#define MT_WL_RX_AGG_EN BIT(21) +#define MT_WL_RX_EN BIT(22) +#define MT_WL_TX_EN BIT(23) +#define MT_WL_RX_BUSY BIT(30) +#define MT_WL_TX_BUSY BIT(31) + +#define MT_MCU_PTA_BASE 0x81060000 +#define MT_MCU_PTA(_n) (MT_MCU_PTA_BASE + (_n)) + +#define MT_ANT_SWITCH_CON(_n) MT_MCU_PTA(0x0c8 + ((_n) - 1) * 4) +#define MT_ANT_SWITCH_CON_MODE(_n) (GENMASK(4, 0) << (_n * 8)) +#define MT_ANT_SWITCH_CON_MODE1(_n) (GENMASK(3, 0) << (_n * 8)) + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/sdio.c b/drivers/net/wireless/mediatek/mt76/mt7615/sdio.c new file mode 100644 index 000000000..46188951a --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7615/sdio.c @@ -0,0 +1,258 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2020 MediaTek Inc. + * + * Author: Felix Fietkau + * Lorenzo Bianconi + * Sean Wang + */ + +#include +#include +#include + +#include +#include +#include + +#include "../sdio.h" +#include "mt7615.h" +#include "mac.h" +#include "mcu.h" + +static const struct sdio_device_id mt7663s_table[] = { + { SDIO_DEVICE(SDIO_VENDOR_ID_MEDIATEK, 0x7603) }, + { } /* Terminating entry */ +}; + +static void mt7663s_txrx_worker(struct mt76_worker *w) +{ + struct mt76_sdio *sdio = container_of(w, struct mt76_sdio, + txrx_worker); + struct mt76_dev *mdev = container_of(sdio, struct mt76_dev, sdio); + struct mt7615_dev *dev = container_of(mdev, struct mt7615_dev, mt76); + + if (!mt76_connac_pm_ref(&dev->mphy, &dev->pm)) { + queue_work(mdev->wq, &dev->pm.wake_work); + return; + } + mt76s_txrx_worker(sdio); + mt76_connac_pm_unref(&dev->mphy, &dev->pm); +} + +static void mt7663s_init_work(struct work_struct *work) +{ + struct mt7615_dev *dev; + + dev = container_of(work, struct mt7615_dev, mcu_work); + if (mt7663s_mcu_init(dev)) + return; + + mt7615_init_work(dev); +} + +static int mt7663s_parse_intr(struct mt76_dev *dev, struct mt76s_intr *intr) +{ + struct mt76_sdio *sdio = &dev->sdio; + struct mt7663s_intr *irq_data = sdio->intr_data; + int i, err; + + sdio_claim_host(sdio->func); + err = sdio_readsb(sdio->func, irq_data, MCR_WHISR, sizeof(*irq_data)); + sdio_release_host(sdio->func); + + if (err) + return err; + + intr->isr = irq_data->isr; + intr->rec_mb = irq_data->rec_mb; + intr->tx.wtqcr = irq_data->tx.wtqcr; + intr->rx.num = irq_data->rx.num; + for (i = 0; i < 2 ; i++) + intr->rx.len[i] = irq_data->rx.len[i]; + + return 0; +} + +static int mt7663s_probe(struct sdio_func *func, + const struct sdio_device_id *id) +{ + static const struct mt76_driver_ops drv_ops = { + .txwi_size = MT_USB_TXD_SIZE, + .drv_flags = MT_DRV_RX_DMA_HDR, + .tx_prepare_skb = mt7663_usb_sdio_tx_prepare_skb, + .tx_complete_skb = mt7663_usb_sdio_tx_complete_skb, + .tx_status_data = mt7663_usb_sdio_tx_status_data, + .rx_skb = mt7615_queue_rx_skb, + .rx_check = mt7615_rx_check, + .sta_add = mt7615_mac_sta_add, + .sta_remove = mt7615_mac_sta_remove, + .update_survey = mt7615_update_channel, + .set_channel = mt7615_set_channel, + }; + static const struct mt76_bus_ops mt7663s_ops = { + .rr = mt76s_rr, + .rmw = mt76s_rmw, + .wr = mt76s_wr, + .write_copy = mt76s_write_copy, + .read_copy = mt76s_read_copy, + .wr_rp = mt76s_wr_rp, + .rd_rp = mt76s_rd_rp, + .type = MT76_BUS_SDIO, + }; + struct ieee80211_ops *ops; + struct mt7615_dev *dev; + struct mt76_dev *mdev; + int ret; + + ops = devm_kmemdup(&func->dev, &mt7615_ops, sizeof(mt7615_ops), + GFP_KERNEL); + if (!ops) + return -ENOMEM; + + mdev = mt76_alloc_device(&func->dev, sizeof(*dev), ops, &drv_ops); + if (!mdev) + return -ENOMEM; + + dev = container_of(mdev, struct mt7615_dev, mt76); + + INIT_WORK(&dev->mcu_work, mt7663s_init_work); + dev->reg_map = mt7663_usb_sdio_reg_map; + dev->ops = ops; + sdio_set_drvdata(func, dev); + + ret = mt76s_init(mdev, func, &mt7663s_ops); + if (ret < 0) + goto error; + + ret = mt76s_hw_init(mdev, func, MT76_CONNAC_SDIO); + if (ret) + goto error; + + mdev->rev = (mt76_rr(dev, MT_HW_CHIPID) << 16) | + (mt76_rr(dev, MT_HW_REV) & 0xff); + dev_dbg(mdev->dev, "ASIC revision: %04x\n", mdev->rev); + + mdev->sdio.parse_irq = mt7663s_parse_intr; + mdev->sdio.intr_data = devm_kmalloc(mdev->dev, + sizeof(struct mt7663s_intr), + GFP_KERNEL); + if (!mdev->sdio.intr_data) { + ret = -ENOMEM; + goto error; + } + + ret = mt76s_alloc_rx_queue(mdev, MT_RXQ_MAIN); + if (ret) + goto error; + + ret = mt76s_alloc_tx(mdev); + if (ret) + goto error; + + ret = mt76_worker_setup(mt76_hw(dev), &mdev->sdio.txrx_worker, + mt7663s_txrx_worker, "sdio-txrx"); + if (ret) + goto error; + + sched_set_fifo_low(mdev->sdio.txrx_worker.task); + + ret = mt7663_usb_sdio_register_device(dev); + if (ret) + goto error; + + return 0; + +error: + mt76s_deinit(&dev->mt76); + mt76_free_device(&dev->mt76); + + return ret; +} + +static void mt7663s_remove(struct sdio_func *func) +{ + struct mt7615_dev *dev = sdio_get_drvdata(func); + + if (!test_and_clear_bit(MT76_STATE_INITIALIZED, &dev->mphy.state)) + return; + + ieee80211_unregister_hw(dev->mt76.hw); + mt76s_deinit(&dev->mt76); + mt76_free_device(&dev->mt76); +} + +static int mt7663s_suspend(struct device *dev) +{ + struct sdio_func *func = dev_to_sdio_func(dev); + struct mt7615_dev *mdev = sdio_get_drvdata(func); + int err; + + if (!test_bit(MT76_STATE_SUSPEND, &mdev->mphy.state) && + mt7615_firmware_offload(mdev)) { + int err; + + err = mt76_connac_mcu_set_hif_suspend(&mdev->mt76, true, true); + if (err < 0) + return err; + } + + sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); + + err = mt7615_mcu_set_fw_ctrl(mdev); + if (err) + return err; + + mt76_worker_disable(&mdev->mt76.sdio.txrx_worker); + mt76_worker_disable(&mdev->mt76.sdio.status_worker); + mt76_worker_disable(&mdev->mt76.sdio.net_worker); + mt76_worker_disable(&mdev->mt76.sdio.stat_worker); + + clear_bit(MT76_READING_STATS, &mdev->mphy.state); + + mt76_tx_status_check(&mdev->mt76, true); + + return 0; +} + +static int mt7663s_resume(struct device *dev) +{ + struct sdio_func *func = dev_to_sdio_func(dev); + struct mt7615_dev *mdev = sdio_get_drvdata(func); + int err; + + mt76_worker_enable(&mdev->mt76.sdio.txrx_worker); + mt76_worker_enable(&mdev->mt76.sdio.status_worker); + mt76_worker_enable(&mdev->mt76.sdio.net_worker); + + err = mt7615_mcu_set_drv_ctrl(mdev); + if (err) + return err; + + if (!test_bit(MT76_STATE_SUSPEND, &mdev->mphy.state) && + mt7615_firmware_offload(mdev)) + err = mt76_connac_mcu_set_hif_suspend(&mdev->mt76, false, true); + + return err; +} + +MODULE_DEVICE_TABLE(sdio, mt7663s_table); +MODULE_FIRMWARE(MT7663_OFFLOAD_FIRMWARE_N9); +MODULE_FIRMWARE(MT7663_OFFLOAD_ROM_PATCH); +MODULE_FIRMWARE(MT7663_FIRMWARE_N9); +MODULE_FIRMWARE(MT7663_ROM_PATCH); + +static DEFINE_SIMPLE_DEV_PM_OPS(mt7663s_pm_ops, mt7663s_suspend, mt7663s_resume); + +static struct sdio_driver mt7663s_driver = { + .name = KBUILD_MODNAME, + .probe = mt7663s_probe, + .remove = mt7663s_remove, + .id_table = mt7663s_table, + .drv.pm = pm_sleep_ptr(&mt7663s_pm_ops), +}; +module_sdio_driver(mt7663s_driver); + +MODULE_AUTHOR("Sean Wang "); +MODULE_AUTHOR("Lorenzo Bianconi "); +MODULE_DESCRIPTION("MediaTek MT7663S (SDIO) wireless driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/sdio_mcu.c b/drivers/net/wireless/mediatek/mt76/mt7615/sdio_mcu.c new file mode 100644 index 000000000..a7b8acb2d --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7615/sdio_mcu.c @@ -0,0 +1,180 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2020 MediaTek Inc. + * + * Author: Felix Fietkau + * Lorenzo Bianconi + * Sean Wang + */ +#include +#include +#include +#include + +#include "../sdio.h" +#include "mt7615.h" +#include "mac.h" +#include "mcu.h" +#include "regs.h" + +static int mt7663s_mcu_init_sched(struct mt7615_dev *dev) +{ + struct mt76_sdio *sdio = &dev->mt76.sdio; + u32 txdwcnt; + + sdio->sched.pse_data_quota = mt76_get_field(dev, MT_PSE_PG_HIF0_GROUP, + MT_HIF0_MIN_QUOTA); + sdio->sched.pse_mcu_quota = mt76_get_field(dev, MT_PSE_PG_HIF1_GROUP, + MT_HIF1_MIN_QUOTA); + sdio->sched.ple_data_quota = mt76_get_field(dev, MT_PLE_PG_HIF0_GROUP, + MT_HIF0_MIN_QUOTA); + sdio->sched.pse_page_size = MT_PSE_PAGE_SZ; + txdwcnt = mt76_get_field(dev, MT_PP_TXDWCNT, + MT_PP_TXDWCNT_TX1_ADD_DW_CNT); + sdio->sched.deficit = txdwcnt << 2; + + return 0; +} + +static int +mt7663s_mcu_send_message(struct mt76_dev *mdev, struct sk_buff *skb, + int cmd, int *seq) +{ + struct mt7615_dev *dev = container_of(mdev, struct mt7615_dev, mt76); + int ret; + + mt7615_mcu_fill_msg(dev, skb, cmd, seq); + ret = mt76_tx_queue_skb_raw(dev, mdev->q_mcu[MT_MCUQ_WM], skb, 0); + if (ret) + return ret; + + mt76_queue_kick(dev, mdev->q_mcu[MT_MCUQ_WM]); + + return ret; +} + +static int __mt7663s_mcu_drv_pmctrl(struct mt7615_dev *dev) +{ + struct sdio_func *func = dev->mt76.sdio.func; + struct mt76_phy *mphy = &dev->mt76.phy; + struct mt76_connac_pm *pm = &dev->pm; + u32 status; + int ret; + + sdio_claim_host(func); + + sdio_writel(func, WHLPCR_FW_OWN_REQ_CLR, MCR_WHLPCR, NULL); + + ret = readx_poll_timeout(mt76s_read_pcr, &dev->mt76, status, + status & WHLPCR_IS_DRIVER_OWN, 2000, 1000000); + if (ret < 0) { + dev_err(dev->mt76.dev, "Cannot get ownership from device"); + } else { + clear_bit(MT76_STATE_PM, &mphy->state); + + pm->stats.last_wake_event = jiffies; + pm->stats.doze_time += pm->stats.last_wake_event - + pm->stats.last_doze_event; + } + sdio_release_host(func); + + return ret; +} + +static int mt7663s_mcu_drv_pmctrl(struct mt7615_dev *dev) +{ + struct mt76_phy *mphy = &dev->mt76.phy; + int ret = 0; + + mutex_lock(&dev->pm.mutex); + + if (test_bit(MT76_STATE_PM, &mphy->state)) + ret = __mt7663s_mcu_drv_pmctrl(dev); + + mutex_unlock(&dev->pm.mutex); + + return ret; +} + +static int mt7663s_mcu_fw_pmctrl(struct mt7615_dev *dev) +{ + struct sdio_func *func = dev->mt76.sdio.func; + struct mt76_phy *mphy = &dev->mt76.phy; + struct mt76_connac_pm *pm = &dev->pm; + int ret = 0; + u32 status; + + mutex_lock(&pm->mutex); + + if (mt76_connac_skip_fw_pmctrl(mphy, pm)) + goto out; + + sdio_claim_host(func); + + sdio_writel(func, WHLPCR_FW_OWN_REQ_SET, MCR_WHLPCR, NULL); + + ret = readx_poll_timeout(mt76s_read_pcr, &dev->mt76, status, + !(status & WHLPCR_IS_DRIVER_OWN), 2000, 1000000); + if (ret < 0) { + dev_err(dev->mt76.dev, "Cannot set ownership to device"); + clear_bit(MT76_STATE_PM, &mphy->state); + } else { + pm->stats.last_doze_event = jiffies; + pm->stats.awake_time += pm->stats.last_doze_event - + pm->stats.last_wake_event; + } + + sdio_release_host(func); +out: + mutex_unlock(&pm->mutex); + + return ret; +} + +int mt7663s_mcu_init(struct mt7615_dev *dev) +{ + static const struct mt76_mcu_ops mt7663s_mcu_ops = { + .headroom = sizeof(struct mt7615_mcu_txd), + .tailroom = MT_USB_TAIL_SIZE, + .mcu_skb_send_msg = mt7663s_mcu_send_message, + .mcu_parse_response = mt7615_mcu_parse_response, + .mcu_rr = mt76_connac_mcu_reg_rr, + .mcu_wr = mt76_connac_mcu_reg_wr, + }; + struct mt7615_mcu_ops *mcu_ops; + int ret; + + ret = __mt7663s_mcu_drv_pmctrl(dev); + if (ret) + return ret; + + dev->mt76.mcu_ops = &mt7663s_mcu_ops; + + ret = mt76_get_field(dev, MT_CONN_ON_MISC, MT_TOP_MISC2_FW_N9_RDY); + if (ret) { + mt7615_mcu_restart(&dev->mt76); + if (!mt76_poll_msec(dev, MT_CONN_ON_MISC, + MT_TOP_MISC2_FW_N9_RDY, 0, 500)) + return -EIO; + } + + ret = __mt7663_load_firmware(dev); + if (ret) + return ret; + + mcu_ops = devm_kmemdup(dev->mt76.dev, dev->mcu_ops, sizeof(*mcu_ops), + GFP_KERNEL); + if (!mcu_ops) + return -ENOMEM; + + mcu_ops->set_drv_ctrl = mt7663s_mcu_drv_pmctrl; + mcu_ops->set_fw_ctrl = mt7663s_mcu_fw_pmctrl; + dev->mcu_ops = mcu_ops; + + ret = mt7663s_mcu_init_sched(dev); + if (ret) + return ret; + + set_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state); + + return 0; +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/soc.c b/drivers/net/wireless/mediatek/mt76/mt7615/soc.c new file mode 100644 index 000000000..4bd189dd6 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7615/soc.c @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2019 MediaTek Inc. + * + * Author: Ryder Lee + * Felix Fietkau + */ + +#include +#include +#include +#include +#include +#include +#include "mt7615.h" + +int mt7622_wmac_init(struct mt7615_dev *dev) +{ + struct device_node *np = dev->mt76.dev->of_node; + + if (!is_mt7622(&dev->mt76)) + return 0; + + dev->infracfg = syscon_regmap_lookup_by_phandle(np, "mediatek,infracfg"); + if (IS_ERR(dev->infracfg)) { + dev_err(dev->mt76.dev, "Cannot find infracfg controller\n"); + return PTR_ERR(dev->infracfg); + } + + return 0; +} + +static int mt7622_wmac_probe(struct platform_device *pdev) +{ + void __iomem *mem_base; + int irq; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + mem_base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); + if (IS_ERR(mem_base)) + return PTR_ERR(mem_base); + + return mt7615_mmio_probe(&pdev->dev, mem_base, irq, mt7615e_reg_map); +} + +static void mt7622_wmac_remove(struct platform_device *pdev) +{ + struct mt7615_dev *dev = platform_get_drvdata(pdev); + + mt7615_unregister_device(dev); +} + +static const struct of_device_id mt7622_wmac_of_match[] = { + { .compatible = "mediatek,mt7622-wmac" }, + {}, +}; + +struct platform_driver mt7622_wmac_driver = { + .driver = { + .name = "mt7622-wmac", + .of_match_table = mt7622_wmac_of_match, + }, + .probe = mt7622_wmac_probe, + .remove = mt7622_wmac_remove, +}; + +MODULE_FIRMWARE(MT7622_FIRMWARE_N9); +MODULE_FIRMWARE(MT7622_ROM_PATCH); diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/testmode.c b/drivers/net/wireless/mediatek/mt76/mt7615/testmode.c new file mode 100644 index 000000000..6eb97b7eb --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7615/testmode.c @@ -0,0 +1,376 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2020 Felix Fietkau */ + +#include "mt7615.h" +#include "eeprom.h" +#include "mcu.h" + +enum { + TM_CHANGED_TXPOWER_CTRL, + TM_CHANGED_TXPOWER, + TM_CHANGED_FREQ_OFFSET, + + /* must be last */ + NUM_TM_CHANGED +}; + + +static const u8 tm_change_map[] = { + [TM_CHANGED_TXPOWER_CTRL] = MT76_TM_ATTR_TX_POWER_CONTROL, + [TM_CHANGED_TXPOWER] = MT76_TM_ATTR_TX_POWER, + [TM_CHANGED_FREQ_OFFSET] = MT76_TM_ATTR_FREQ_OFFSET, +}; + +static const u32 reg_backup_list[] = { + MT_WF_PHY_RFINTF3_0(0), + MT_WF_PHY_RFINTF3_0(1), + MT_WF_PHY_RFINTF3_0(2), + MT_WF_PHY_RFINTF3_0(3), + MT_ANT_SWITCH_CON(2), + MT_ANT_SWITCH_CON(3), + MT_ANT_SWITCH_CON(4), + MT_ANT_SWITCH_CON(6), + MT_ANT_SWITCH_CON(7), + MT_ANT_SWITCH_CON(8), +}; + +static const struct { + u16 wf; + u16 reg; +} rf_backup_list[] = { + { 0, 0x48 }, + { 1, 0x48 }, + { 2, 0x48 }, + { 3, 0x48 }, +}; + +static int +mt7615_tm_set_tx_power(struct mt7615_phy *phy) +{ + struct mt7615_dev *dev = phy->dev; + struct mt76_phy *mphy = phy->mt76; + int i, ret, n_chains = hweight8(mphy->antenna_mask); + struct cfg80211_chan_def *chandef = &mphy->chandef; + int freq = chandef->center_freq1, len, target_chains; + u8 *data, *eep = (u8 *)dev->mt76.eeprom.data; + enum nl80211_band band = chandef->chan->band; + struct sk_buff *skb; + struct { + u8 center_chan; + u8 dbdc_idx; + u8 band; + u8 rsv; + } __packed req_hdr = { + .center_chan = ieee80211_frequency_to_channel(freq), + .band = band, + .dbdc_idx = phy != &dev->phy, + }; + u8 *tx_power = NULL; + + if (mphy->test.state != MT76_TM_STATE_OFF) + tx_power = mphy->test.tx_power; + + len = MT7615_EE_MAX - MT_EE_NIC_CONF_0; + skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, sizeof(req_hdr) + len); + if (!skb) + return -ENOMEM; + + skb_put_data(skb, &req_hdr, sizeof(req_hdr)); + data = skb_put_data(skb, eep + MT_EE_NIC_CONF_0, len); + + target_chains = mt7615_ext_pa_enabled(dev, band) ? 1 : n_chains; + for (i = 0; i < target_chains; i++) { + ret = mt7615_eeprom_get_target_power_index(dev, chandef->chan, i); + if (ret < 0) { + dev_kfree_skb(skb); + return -EINVAL; + } + + if (tx_power && tx_power[i]) + data[ret - MT_EE_NIC_CONF_0] = tx_power[i]; + } + + return mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_EXT_CMD(SET_TX_POWER_CTRL), false); +} + +static void +mt7615_tm_reg_backup_restore(struct mt7615_phy *phy) +{ + struct mt7615_dev *dev = phy->dev; + u32 *b = phy->test.reg_backup; + int n_regs = ARRAY_SIZE(reg_backup_list); + int n_rf_regs = ARRAY_SIZE(rf_backup_list); + int i; + + if (phy->mt76->test.state == MT76_TM_STATE_OFF) { + for (i = 0; i < n_regs; i++) + mt76_wr(dev, reg_backup_list[i], b[i]); + + for (i = 0; i < n_rf_regs; i++) + mt7615_rf_wr(dev, rf_backup_list[i].wf, + rf_backup_list[i].reg, b[n_regs + i]); + return; + } + + if (b) + return; + + b = devm_kzalloc(dev->mt76.dev, 4 * (n_regs + n_rf_regs), + GFP_KERNEL); + if (!b) + return; + + phy->test.reg_backup = b; + for (i = 0; i < n_regs; i++) + b[i] = mt76_rr(dev, reg_backup_list[i]); + for (i = 0; i < n_rf_regs; i++) + b[n_regs + i] = mt7615_rf_rr(dev, rf_backup_list[i].wf, + rf_backup_list[i].reg); +} + +static void +mt7615_tm_init(struct mt7615_phy *phy) +{ + struct mt7615_dev *dev = phy->dev; + unsigned int total_flags = ~0; + + if (!test_bit(MT76_STATE_RUNNING, &phy->mt76->state)) + return; + + mt7615_mcu_set_sku_en(phy, phy->mt76->test.state == MT76_TM_STATE_OFF); + + mutex_unlock(&dev->mt76.mutex); + mt76_update_channel(phy->mt76); + mt7615_ops.configure_filter(phy->mt76->hw, 0, &total_flags, 0); + mutex_lock(&dev->mt76.mutex); + + mt7615_tm_reg_backup_restore(phy); +} + +static void +mt7615_tm_set_rx_enable(struct mt7615_dev *dev, bool en) +{ + u32 rqcr_mask = (MT_ARB_RQCR_RX_START | + MT_ARB_RQCR_RXV_START | + MT_ARB_RQCR_RXV_R_EN | + MT_ARB_RQCR_RXV_T_EN) * + (BIT(0) | BIT(MT_ARB_RQCR_BAND_SHIFT)); + + if (en) { + mt76_clear(dev, MT_ARB_SCR, + MT_ARB_SCR_RX0_DISABLE | MT_ARB_SCR_RX1_DISABLE); + mt76_set(dev, MT_ARB_RQCR, rqcr_mask); + } else { + mt76_set(dev, MT_ARB_SCR, + MT_ARB_SCR_RX0_DISABLE | MT_ARB_SCR_RX1_DISABLE); + mt76_clear(dev, MT_ARB_RQCR, rqcr_mask); + } +} + +static void +mt7615_tm_set_tx_antenna(struct mt7615_phy *phy, bool en) +{ + struct mt7615_dev *dev = phy->dev; + struct mt76_testmode_data *td = &phy->mt76->test; + u8 mask = td->tx_antenna_mask; + int i; + + if (!mask) + return; + + if (!en) + mask = phy->mt76->chainmask; + + for (i = 0; i < 4; i++) { + mt76_rmw_field(dev, MT_WF_PHY_RFINTF3_0(i), + MT_WF_PHY_RFINTF3_0_ANT, + (mask & BIT(i)) ? 0 : 0xa); + } + + /* 2.4 GHz band */ + mt76_rmw_field(dev, MT_ANT_SWITCH_CON(3), MT_ANT_SWITCH_CON_MODE(0), + (mask & BIT(0)) ? 0x8 : 0x1b); + mt76_rmw_field(dev, MT_ANT_SWITCH_CON(4), MT_ANT_SWITCH_CON_MODE(2), + (mask & BIT(1)) ? 0xe : 0x1b); + mt76_rmw_field(dev, MT_ANT_SWITCH_CON(6), MT_ANT_SWITCH_CON_MODE1(0), + (mask & BIT(2)) ? 0x0 : 0xf); + mt76_rmw_field(dev, MT_ANT_SWITCH_CON(7), MT_ANT_SWITCH_CON_MODE1(2), + (mask & BIT(3)) ? 0x6 : 0xf); + + /* 5 GHz band */ + mt76_rmw_field(dev, MT_ANT_SWITCH_CON(4), MT_ANT_SWITCH_CON_MODE(1), + (mask & BIT(0)) ? 0xd : 0x1b); + mt76_rmw_field(dev, MT_ANT_SWITCH_CON(2), MT_ANT_SWITCH_CON_MODE(3), + (mask & BIT(1)) ? 0x13 : 0x1b); + mt76_rmw_field(dev, MT_ANT_SWITCH_CON(7), MT_ANT_SWITCH_CON_MODE1(1), + (mask & BIT(2)) ? 0x5 : 0xf); + mt76_rmw_field(dev, MT_ANT_SWITCH_CON(8), MT_ANT_SWITCH_CON_MODE1(3), + (mask & BIT(3)) ? 0xb : 0xf); + + for (i = 0; i < 4; i++) { + u32 val; + + val = mt7615_rf_rr(dev, i, 0x48); + val &= ~(0x3ff << 20); + if (mask & BIT(i)) + val |= 3 << 20; + else + val |= (2 << 28) | (2 << 26) | (8 << 20); + mt7615_rf_wr(dev, i, 0x48, val); + } +} + +static void +mt7615_tm_set_tx_frames(struct mt7615_phy *phy, bool en) +{ + struct mt7615_dev *dev = phy->dev; + struct ieee80211_tx_info *info; + struct sk_buff *skb = phy->mt76->test.tx_skb; + + mt7615_mcu_set_chan_info(phy, MCU_EXT_CMD(SET_RX_PATH)); + mt7615_tm_set_tx_antenna(phy, en); + mt7615_tm_set_rx_enable(dev, !en); + if (!en || !skb) + return; + + info = IEEE80211_SKB_CB(skb); + info->control.vif = phy->monitor_vif; +} + +static void +mt7615_tm_update_params(struct mt7615_phy *phy, u32 changed) +{ + struct mt7615_dev *dev = phy->dev; + struct mt76_testmode_data *td = &phy->mt76->test; + bool en = phy->mt76->test.state != MT76_TM_STATE_OFF; + + if (changed & BIT(TM_CHANGED_TXPOWER_CTRL)) + mt7615_mcu_set_test_param(dev, MCU_ATE_SET_TX_POWER_CONTROL, + en, en && td->tx_power_control); + if (changed & BIT(TM_CHANGED_FREQ_OFFSET)) + mt7615_mcu_set_test_param(dev, MCU_ATE_SET_FREQ_OFFSET, + en, en ? td->freq_offset : 0); + if (changed & BIT(TM_CHANGED_TXPOWER)) + mt7615_tm_set_tx_power(phy); +} + +static int +mt7615_tm_set_state(struct mt76_phy *mphy, enum mt76_testmode_state state) +{ + struct mt7615_phy *phy = mphy->priv; + struct mt76_testmode_data *td = &mphy->test; + enum mt76_testmode_state prev_state = td->state; + + mphy->test.state = state; + + if (prev_state == MT76_TM_STATE_TX_FRAMES) + mt7615_tm_set_tx_frames(phy, false); + else if (state == MT76_TM_STATE_TX_FRAMES) + mt7615_tm_set_tx_frames(phy, true); + + if (state <= MT76_TM_STATE_IDLE) + mt7615_tm_init(phy); + + if ((state == MT76_TM_STATE_IDLE && + prev_state == MT76_TM_STATE_OFF) || + (state == MT76_TM_STATE_OFF && + prev_state == MT76_TM_STATE_IDLE)) { + u32 changed = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(tm_change_map); i++) { + u16 cur = tm_change_map[i]; + + if (td->param_set[cur / 32] & BIT(cur % 32)) + changed |= BIT(i); + } + + mt7615_tm_update_params(phy, changed); + } + + return 0; +} + +static int +mt7615_tm_set_params(struct mt76_phy *mphy, struct nlattr **tb, + enum mt76_testmode_state new_state) +{ + struct mt76_testmode_data *td = &mphy->test; + struct mt7615_phy *phy = mphy->priv; + u32 changed = 0; + int i; + + BUILD_BUG_ON(NUM_TM_CHANGED >= 32); + + if (new_state == MT76_TM_STATE_OFF || + td->state == MT76_TM_STATE_OFF) + return 0; + + if (td->tx_antenna_mask & ~mphy->chainmask) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(tm_change_map); i++) { + if (tb[tm_change_map[i]]) + changed |= BIT(i); + } + + mt7615_tm_update_params(phy, changed); + + return 0; +} + +static int +mt7615_tm_dump_stats(struct mt76_phy *mphy, struct sk_buff *msg) +{ + struct mt7615_phy *phy = mphy->priv; + void *rx, *rssi; + int i; + + rx = nla_nest_start(msg, MT76_TM_STATS_ATTR_LAST_RX); + if (!rx) + return -ENOMEM; + + if (nla_put_s32(msg, MT76_TM_RX_ATTR_FREQ_OFFSET, phy->test.last_freq_offset)) + return -ENOMEM; + + rssi = nla_nest_start(msg, MT76_TM_RX_ATTR_RCPI); + if (!rssi) + return -ENOMEM; + + for (i = 0; i < ARRAY_SIZE(phy->test.last_rcpi); i++) + if (nla_put_u8(msg, i, phy->test.last_rcpi[i])) + return -ENOMEM; + + nla_nest_end(msg, rssi); + + rssi = nla_nest_start(msg, MT76_TM_RX_ATTR_IB_RSSI); + if (!rssi) + return -ENOMEM; + + for (i = 0; i < ARRAY_SIZE(phy->test.last_ib_rssi); i++) + if (nla_put_s8(msg, i, phy->test.last_ib_rssi[i])) + return -ENOMEM; + + nla_nest_end(msg, rssi); + + rssi = nla_nest_start(msg, MT76_TM_RX_ATTR_WB_RSSI); + if (!rssi) + return -ENOMEM; + + for (i = 0; i < ARRAY_SIZE(phy->test.last_wb_rssi); i++) + if (nla_put_s8(msg, i, phy->test.last_wb_rssi[i])) + return -ENOMEM; + + nla_nest_end(msg, rssi); + + nla_nest_end(msg, rx); + + return 0; +} + +const struct mt76_testmode_ops mt7615_testmode_ops = { + .set_state = mt7615_tm_set_state, + .set_params = mt7615_tm_set_params, + .dump_stats = mt7615_tm_dump_stats, +}; diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/trace.c b/drivers/net/wireless/mediatek/mt76/mt7615/trace.c new file mode 100644 index 000000000..7ec39e0b3 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7615/trace.c @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (C) 2019 Lorenzo Bianconi + */ + +#include + +#ifndef __CHECKER__ +#define CREATE_TRACE_POINTS +#include "mt7615_trace.h" + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/usb.c b/drivers/net/wireless/mediatek/mt76/mt7615/usb.c new file mode 100644 index 000000000..bab7b91f1 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7615/usb.c @@ -0,0 +1,283 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2019 MediaTek Inc. + * + * Author: Felix Fietkau + * Lorenzo Bianconi + * Sean Wang + */ + +#include +#include +#include + +#include "mt7615.h" +#include "mac.h" +#include "mcu.h" +#include "regs.h" + +static const struct usb_device_id mt7615_device_table[] = { + { USB_DEVICE_AND_INTERFACE_INFO(0x0e8d, 0x7663, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(0x043e, 0x310c, 0xff, 0xff, 0xff) }, + { }, +}; + +static u32 mt7663u_rr(struct mt76_dev *dev, u32 addr) +{ + u32 ret; + + mutex_lock(&dev->usb.usb_ctrl_mtx); + ret = ___mt76u_rr(dev, MT_VEND_READ_EXT, + USB_DIR_IN | USB_TYPE_VENDOR, addr); + mutex_unlock(&dev->usb.usb_ctrl_mtx); + + return ret; +} + +static void mt7663u_wr(struct mt76_dev *dev, u32 addr, u32 val) +{ + mutex_lock(&dev->usb.usb_ctrl_mtx); + ___mt76u_wr(dev, MT_VEND_WRITE_EXT, + USB_DIR_OUT | USB_TYPE_VENDOR, addr, val); + mutex_unlock(&dev->usb.usb_ctrl_mtx); +} + +static u32 mt7663u_rmw(struct mt76_dev *dev, u32 addr, + u32 mask, u32 val) +{ + mutex_lock(&dev->usb.usb_ctrl_mtx); + val |= ___mt76u_rr(dev, MT_VEND_READ_EXT, + USB_DIR_IN | USB_TYPE_VENDOR, addr) & ~mask; + ___mt76u_wr(dev, MT_VEND_WRITE_EXT, + USB_DIR_OUT | USB_TYPE_VENDOR, addr, val); + mutex_unlock(&dev->usb.usb_ctrl_mtx); + + return val; +} + +static void mt7663u_copy(struct mt76_dev *dev, u32 offset, + const void *data, int len) +{ + struct mt76_usb *usb = &dev->usb; + int ret, i = 0, batch_len; + const u8 *val = data; + + len = round_up(len, 4); + + mutex_lock(&usb->usb_ctrl_mtx); + while (i < len) { + batch_len = min_t(int, usb->data_len, len - i); + memcpy(usb->data, val + i, batch_len); + ret = __mt76u_vendor_request(dev, MT_VEND_WRITE_EXT, + USB_DIR_OUT | USB_TYPE_VENDOR, + (offset + i) >> 16, offset + i, + usb->data, batch_len); + if (ret < 0) + break; + + i += batch_len; + } + mutex_unlock(&usb->usb_ctrl_mtx); +} + +static void mt7663u_stop(struct ieee80211_hw *hw, bool suspend) +{ + struct mt7615_phy *phy = mt7615_hw_phy(hw); + struct mt7615_dev *dev = hw->priv; + + clear_bit(MT76_STATE_RUNNING, &dev->mphy.state); + timer_delete_sync(&phy->roc_timer); + cancel_work_sync(&phy->roc_work); + cancel_delayed_work_sync(&phy->scan_work); + cancel_delayed_work_sync(&phy->mt76->mac_work); + mt76u_stop_tx(&dev->mt76); +} + +static void mt7663u_cleanup(struct mt7615_dev *dev) +{ + clear_bit(MT76_STATE_INITIALIZED, &dev->mphy.state); + mt76u_queues_deinit(&dev->mt76); +} + +static void mt7663u_init_work(struct work_struct *work) +{ + struct mt7615_dev *dev; + + dev = container_of(work, struct mt7615_dev, mcu_work); + if (mt7663u_mcu_init(dev)) + return; + + mt7615_init_work(dev); +} + +static int mt7663u_probe(struct usb_interface *usb_intf, + const struct usb_device_id *id) +{ + static const struct mt76_driver_ops drv_ops = { + .txwi_size = MT_USB_TXD_SIZE, + .drv_flags = MT_DRV_RX_DMA_HDR | MT_DRV_HW_MGMT_TXQ, + .tx_prepare_skb = mt7663_usb_sdio_tx_prepare_skb, + .tx_complete_skb = mt7663_usb_sdio_tx_complete_skb, + .tx_status_data = mt7663_usb_sdio_tx_status_data, + .rx_skb = mt7615_queue_rx_skb, + .rx_check = mt7615_rx_check, + .sta_add = mt7615_mac_sta_add, + .sta_remove = mt7615_mac_sta_remove, + .update_survey = mt7615_update_channel, + .set_channel = mt7615_set_channel, + }; + static struct mt76_bus_ops bus_ops = { + .rr = mt7663u_rr, + .wr = mt7663u_wr, + .rmw = mt7663u_rmw, + .read_copy = mt76u_read_copy, + .write_copy = mt7663u_copy, + .type = MT76_BUS_USB, + }; + struct usb_device *udev = interface_to_usbdev(usb_intf); + struct ieee80211_ops *ops; + struct mt7615_dev *dev; + struct mt76_dev *mdev; + int ret; + + ops = devm_kmemdup(&usb_intf->dev, &mt7615_ops, sizeof(mt7615_ops), + GFP_KERNEL); + if (!ops) + return -ENOMEM; + + ops->stop = mt7663u_stop; + + mdev = mt76_alloc_device(&usb_intf->dev, sizeof(*dev), ops, &drv_ops); + if (!mdev) + return -ENOMEM; + + dev = container_of(mdev, struct mt7615_dev, mt76); + usb_reset_device(udev); + + usb_set_intfdata(usb_intf, dev); + + INIT_WORK(&dev->mcu_work, mt7663u_init_work); + dev->reg_map = mt7663_usb_sdio_reg_map; + dev->ops = ops; + ret = __mt76u_init(mdev, usb_intf, &bus_ops); + if (ret < 0) + goto error; + + mdev->rev = (mt76_rr(dev, MT_HW_CHIPID) << 16) | + (mt76_rr(dev, MT_HW_REV) & 0xff); + dev_dbg(mdev->dev, "ASIC revision: %04x\n", mdev->rev); + + if (!mt76_poll_msec(dev, MT_CONN_ON_MISC, MT_TOP_MISC2_FW_PWR_ON, + FW_STATE_PWR_ON << 1, 500)) { + ret = mt7663u_mcu_power_on(dev); + if (ret) + goto error; + } else { + set_bit(MT76_STATE_POWER_OFF, &dev->mphy.state); + } + + ret = mt76u_alloc_mcu_queue(&dev->mt76); + if (ret) + goto error; + + ret = mt76u_alloc_queues(&dev->mt76); + if (ret) + goto error; + + ret = mt7663_usb_sdio_register_device(dev); + if (ret) + goto error; + + return 0; + +error: + mt76u_queues_deinit(&dev->mt76); + usb_set_intfdata(usb_intf, NULL); + + mt76_free_device(&dev->mt76); + + return ret; +} + +static void mt7663u_disconnect(struct usb_interface *usb_intf) +{ + struct mt7615_dev *dev = usb_get_intfdata(usb_intf); + + if (!test_bit(MT76_STATE_INITIALIZED, &dev->mphy.state)) + return; + + ieee80211_unregister_hw(dev->mt76.hw); + mt7663u_cleanup(dev); + + usb_set_intfdata(usb_intf, NULL); + + mt76_free_device(&dev->mt76); +} + +#ifdef CONFIG_PM +static int mt7663u_suspend(struct usb_interface *intf, pm_message_t state) +{ + struct mt7615_dev *dev = usb_get_intfdata(intf); + + if (!test_bit(MT76_STATE_SUSPEND, &dev->mphy.state) && + mt7615_firmware_offload(dev)) { + int err; + + err = mt76_connac_mcu_set_hif_suspend(&dev->mt76, true, true); + if (err < 0) + return err; + } + + mt76u_stop_rx(&dev->mt76); + mt76u_stop_tx(&dev->mt76); + + return 0; +} + +static int mt7663u_resume(struct usb_interface *intf) +{ + struct mt7615_dev *dev = usb_get_intfdata(intf); + int err; + + err = mt76u_vendor_request(&dev->mt76, MT_VEND_FEATURE_SET, + USB_DIR_OUT | USB_TYPE_VENDOR, + 0x5, 0x0, NULL, 0); + if (err) + return err; + + err = mt76u_resume_rx(&dev->mt76); + if (err < 0) + return err; + + if (!test_bit(MT76_STATE_SUSPEND, &dev->mphy.state) && + mt7615_firmware_offload(dev)) + err = mt76_connac_mcu_set_hif_suspend(&dev->mt76, false, true); + + return err; +} +#endif /* CONFIG_PM */ + +MODULE_DEVICE_TABLE(usb, mt7615_device_table); +MODULE_FIRMWARE(MT7663_OFFLOAD_FIRMWARE_N9); +MODULE_FIRMWARE(MT7663_OFFLOAD_ROM_PATCH); +MODULE_FIRMWARE(MT7663_FIRMWARE_N9); +MODULE_FIRMWARE(MT7663_ROM_PATCH); + +static struct usb_driver mt7663u_driver = { + .name = KBUILD_MODNAME, + .id_table = mt7615_device_table, + .probe = mt7663u_probe, + .disconnect = mt7663u_disconnect, +#ifdef CONFIG_PM + .suspend = mt7663u_suspend, + .resume = mt7663u_resume, + .reset_resume = mt7663u_resume, +#endif /* CONFIG_PM */ + .soft_unbind = 1, + .disable_hub_initiated_lpm = 1, +}; +module_usb_driver(mt7663u_driver); + +MODULE_AUTHOR("Sean Wang "); +MODULE_AUTHOR("Lorenzo Bianconi "); +MODULE_DESCRIPTION("MediaTek MT7663U (USB) wireless driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/usb_mcu.c b/drivers/net/wireless/mediatek/mt76/mt7615/usb_mcu.c new file mode 100644 index 000000000..33c01f8ce --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7615/usb_mcu.c @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2019 MediaTek Inc. + * + * Author: Felix Fietkau + * Lorenzo Bianconi + * Sean Wang + */ +#include +#include + +#include "mt7615.h" +#include "mac.h" +#include "mcu.h" +#include "regs.h" + +static int +mt7663u_mcu_send_message(struct mt76_dev *mdev, struct sk_buff *skb, + int cmd, int *seq) +{ + struct mt7615_dev *dev = container_of(mdev, struct mt7615_dev, mt76); + int ret, ep, len, pad; + + mt7615_mcu_fill_msg(dev, skb, cmd, seq); + if (cmd != MCU_CMD(FW_SCATTER)) + ep = MT_EP_OUT_INBAND_CMD; + else + ep = MT_EP_OUT_AC_BE; + + len = skb->len; + put_unaligned_le32(len, skb_push(skb, sizeof(len))); + pad = round_up(skb->len, 4) + 4 - skb->len; + ret = mt76_skb_adjust_pad(skb, pad); + if (ret < 0) + goto out; + + ret = mt76u_bulk_msg(&dev->mt76, skb->data, skb->len, NULL, + 1000, ep); + +out: + dev_kfree_skb(skb); + + return ret; +} + +int mt7663u_mcu_power_on(struct mt7615_dev *dev) +{ + int ret; + + ret = mt76u_vendor_request(&dev->mt76, MT_VEND_POWER_ON, + USB_DIR_OUT | USB_TYPE_VENDOR, + 0x0, 0x1, NULL, 0); + if (ret) + return ret; + + if (!mt76_poll_msec(dev, MT_CONN_ON_MISC, + MT_TOP_MISC2_FW_PWR_ON, + FW_STATE_PWR_ON << 1, 500)) { + dev_err(dev->mt76.dev, "Timeout for power on\n"); + ret = -EIO; + } + + return 0; +} + +int mt7663u_mcu_init(struct mt7615_dev *dev) +{ + static const struct mt76_mcu_ops mt7663u_mcu_ops = { + .headroom = MT_USB_HDR_SIZE + sizeof(struct mt7615_mcu_txd), + .tailroom = MT_USB_TAIL_SIZE, + .mcu_skb_send_msg = mt7663u_mcu_send_message, + .mcu_parse_response = mt7615_mcu_parse_response, + }; + int ret; + + dev->mt76.mcu_ops = &mt7663u_mcu_ops; + + mt76_set(dev, MT_UDMA_TX_QSEL, MT_FW_DL_EN); + if (test_and_clear_bit(MT76_STATE_POWER_OFF, &dev->mphy.state)) { + ret = mt7615_mcu_restart(&dev->mt76); + if (ret) + return ret; + + if (!mt76_poll_msec(dev, MT_CONN_ON_MISC, + MT_TOP_MISC2_FW_PWR_ON, 0, 500)) + return -EIO; + + ret = mt7663u_mcu_power_on(dev); + if (ret) + return ret; + } + + ret = __mt7663_load_firmware(dev); + if (ret) + return ret; + + mt76_clear(dev, MT_UDMA_TX_QSEL, MT_FW_DL_EN); + set_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state); + + return 0; +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/usb_sdio.c b/drivers/net/wireless/mediatek/mt76/mt7615/usb_sdio.c new file mode 100644 index 000000000..9525d8062 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7615/usb_sdio.c @@ -0,0 +1,354 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2020 MediaTek Inc. + * + * Author: Lorenzo Bianconi + * Sean Wang + */ + +#include +#include +#include + +#include "mt7615.h" +#include "mac.h" +#include "mcu.h" +#include "regs.h" + +const u32 mt7663_usb_sdio_reg_map[] = { + [MT_TOP_CFG_BASE] = 0x80020000, + [MT_HW_BASE] = 0x80000000, + [MT_DMA_SHDL_BASE] = 0x5000a000, + [MT_HIF_BASE] = 0x50000000, + [MT_CSR_BASE] = 0x40000000, + [MT_EFUSE_ADDR_BASE] = 0x78011000, + [MT_TOP_MISC_BASE] = 0x81020000, + [MT_PLE_BASE] = 0x82060000, + [MT_PSE_BASE] = 0x82068000, + [MT_PP_BASE] = 0x8206c000, + [MT_WTBL_BASE_ADDR] = 0x820e0000, + [MT_CFG_BASE] = 0x820f0000, + [MT_AGG_BASE] = 0x820f2000, + [MT_ARB_BASE] = 0x820f3000, + [MT_TMAC_BASE] = 0x820f4000, + [MT_RMAC_BASE] = 0x820f5000, + [MT_DMA_BASE] = 0x820f7000, + [MT_PF_BASE] = 0x820f8000, + [MT_WTBL_BASE_ON] = 0x820f9000, + [MT_WTBL_BASE_OFF] = 0x820f9800, + [MT_LPON_BASE] = 0x820fb000, + [MT_MIB_BASE] = 0x820fd000, +}; +EXPORT_SYMBOL_GPL(mt7663_usb_sdio_reg_map); + +static void +mt7663_usb_sdio_write_txwi(struct mt7615_dev *dev, struct mt76_wcid *wcid, + enum mt76_txq_id qid, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key, int pid, + struct sk_buff *skb) +{ + __le32 *txwi = (__le32 *)(skb->data - MT_USB_TXD_SIZE); + + memset(txwi, 0, MT_USB_TXD_SIZE); + mt7615_mac_write_txwi(dev, txwi, skb, wcid, sta, pid, key, qid, false); + skb_push(skb, MT_USB_TXD_SIZE); +} + +static int mt7663_usb_sdio_set_rates(struct mt7615_dev *dev, + struct mt7615_wtbl_rate_desc *wrd) +{ + struct mt7615_rate_desc *rate = &wrd->rate; + struct mt7615_sta *sta = wrd->sta; + u32 w5, w27, addr, val; + u16 idx; + + lockdep_assert_held(&dev->mt76.mutex); + + if (!sta) + return -EINVAL; + + if (!mt76_poll(dev, MT_WTBL_UPDATE, MT_WTBL_UPDATE_BUSY, 0, 5000)) + return -ETIMEDOUT; + + addr = mt7615_mac_wtbl_addr(dev, sta->wcid.idx); + + w27 = mt76_rr(dev, addr + 27 * 4); + w27 &= ~MT_WTBL_W27_CC_BW_SEL; + w27 |= FIELD_PREP(MT_WTBL_W27_CC_BW_SEL, rate->bw); + + w5 = mt76_rr(dev, addr + 5 * 4); + w5 &= ~(MT_WTBL_W5_BW_CAP | MT_WTBL_W5_CHANGE_BW_RATE | + MT_WTBL_W5_MPDU_OK_COUNT | + MT_WTBL_W5_MPDU_FAIL_COUNT | + MT_WTBL_W5_RATE_IDX); + w5 |= FIELD_PREP(MT_WTBL_W5_BW_CAP, rate->bw) | + FIELD_PREP(MT_WTBL_W5_CHANGE_BW_RATE, + rate->bw_idx ? rate->bw_idx - 1 : 7); + + mt76_wr(dev, MT_WTBL_RIUCR0, w5); + + mt76_wr(dev, MT_WTBL_RIUCR1, + FIELD_PREP(MT_WTBL_RIUCR1_RATE0, rate->probe_val) | + FIELD_PREP(MT_WTBL_RIUCR1_RATE1, rate->val[0]) | + FIELD_PREP(MT_WTBL_RIUCR1_RATE2_LO, rate->val[1])); + + mt76_wr(dev, MT_WTBL_RIUCR2, + FIELD_PREP(MT_WTBL_RIUCR2_RATE2_HI, rate->val[1] >> 8) | + FIELD_PREP(MT_WTBL_RIUCR2_RATE3, rate->val[1]) | + FIELD_PREP(MT_WTBL_RIUCR2_RATE4, rate->val[2]) | + FIELD_PREP(MT_WTBL_RIUCR2_RATE5_LO, rate->val[2])); + + mt76_wr(dev, MT_WTBL_RIUCR3, + FIELD_PREP(MT_WTBL_RIUCR3_RATE5_HI, rate->val[2] >> 4) | + FIELD_PREP(MT_WTBL_RIUCR3_RATE6, rate->val[3]) | + FIELD_PREP(MT_WTBL_RIUCR3_RATE7, rate->val[3])); + + mt76_wr(dev, MT_WTBL_UPDATE, + FIELD_PREP(MT_WTBL_UPDATE_WLAN_IDX, sta->wcid.idx) | + MT_WTBL_UPDATE_RATE_UPDATE | + MT_WTBL_UPDATE_TX_COUNT_CLEAR); + + mt76_wr(dev, addr + 27 * 4, w27); + + sta->rate_probe = sta->rateset[rate->rateset].probe_rate.idx != -1; + + idx = sta->vif->mt76.omac_idx; + idx = idx > HW_BSSID_MAX ? HW_BSSID_0 : idx; + addr = idx > 1 ? MT_LPON_TCR2(idx): MT_LPON_TCR0(idx); + + mt76_rmw(dev, addr, MT_LPON_TCR_MODE, MT_LPON_TCR_READ); /* TSF read */ + val = mt76_rr(dev, MT_LPON_UTTR0); + sta->rate_set_tsf = (val & ~BIT(0)) | rate->rateset; + + if (!(sta->wcid.tx_info & MT_WCID_TX_INFO_SET)) + mt76_poll(dev, MT_WTBL_UPDATE, MT_WTBL_UPDATE_BUSY, 0, 5000); + + sta->rate_count = 2 * MT7615_RATE_RETRY * sta->n_rates; + sta->wcid.tx_info |= MT_WCID_TX_INFO_SET; + + return 0; +} + +static void mt7663_usb_sdio_rate_work(struct work_struct *work) +{ + struct mt7615_wtbl_rate_desc *wrd, *wrd_next; + struct list_head wrd_list; + struct mt7615_dev *dev; + + dev = (struct mt7615_dev *)container_of(work, struct mt7615_dev, + rate_work); + + INIT_LIST_HEAD(&wrd_list); + spin_lock_bh(&dev->mt76.lock); + list_splice_init(&dev->wrd_head, &wrd_list); + spin_unlock_bh(&dev->mt76.lock); + + list_for_each_entry_safe(wrd, wrd_next, &wrd_list, node) { + list_del(&wrd->node); + + mt7615_mutex_acquire(dev); + mt7663_usb_sdio_set_rates(dev, wrd); + mt7615_mutex_release(dev); + + kfree(wrd); + } +} + +bool mt7663_usb_sdio_tx_status_data(struct mt76_dev *mdev, u8 *update) +{ + struct mt7615_dev *dev = container_of(mdev, struct mt7615_dev, mt76); + + mt7615_mutex_acquire(dev); + mt7615_mac_sta_poll(dev); + mt7615_mutex_release(dev); + + return false; +} +EXPORT_SYMBOL_GPL(mt7663_usb_sdio_tx_status_data); + +void mt7663_usb_sdio_tx_complete_skb(struct mt76_dev *mdev, + struct mt76_queue_entry *e) +{ + unsigned int headroom = MT_USB_TXD_SIZE; + + if (mt76_is_usb(mdev)) + headroom += MT_USB_HDR_SIZE; + skb_pull(e->skb, headroom); + + mt76_tx_complete_skb(mdev, e->wcid, e->skb); +} +EXPORT_SYMBOL_GPL(mt7663_usb_sdio_tx_complete_skb); + +int mt7663_usb_sdio_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, + enum mt76_txq_id qid, struct mt76_wcid *wcid, + struct ieee80211_sta *sta, + struct mt76_tx_info *tx_info) +{ + struct mt7615_dev *dev = container_of(mdev, struct mt7615_dev, mt76); + struct sk_buff *skb = tx_info->skb; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_key_conf *key = info->control.hw_key; + struct mt7615_sta *msta = NULL; + int pad, err, pktid; + + if (wcid && wcid->sta) + msta = container_of(wcid, struct mt7615_sta, wcid); + if (!wcid) + wcid = &dev->mt76.global_wcid; + + if ((info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) && + msta && !msta->rate_probe) { + /* request to configure sampling rate */ + spin_lock_bh(&dev->mt76.lock); + mt7615_mac_set_rates(&dev->phy, msta, &info->control.rates[0], + msta->rates); + spin_unlock_bh(&dev->mt76.lock); + } + + pktid = mt76_tx_status_skb_add(&dev->mt76, wcid, skb); + mt7663_usb_sdio_write_txwi(dev, wcid, qid, sta, key, pktid, skb); + if (mt76_is_usb(mdev)) { + u32 len = skb->len; + + put_unaligned_le32(len, skb_push(skb, sizeof(len))); + pad = round_up(skb->len, 4) + 4 - skb->len; + } else { + pad = round_up(skb->len, 4) - skb->len; + } + + err = mt76_skb_adjust_pad(skb, pad); + if (err) + /* Release pktid in case of error. */ + idr_remove(&wcid->pktid, pktid); + + return err; +} +EXPORT_SYMBOL_GPL(mt7663_usb_sdio_tx_prepare_skb); + +static int mt7663u_dma_sched_init(struct mt7615_dev *dev) +{ + int i; + + mt76_rmw(dev, MT_DMA_SHDL(MT_DMASHDL_PKT_MAX_SIZE), + MT_DMASHDL_PKT_MAX_SIZE_PLE | MT_DMASHDL_PKT_MAX_SIZE_PSE, + FIELD_PREP(MT_DMASHDL_PKT_MAX_SIZE_PLE, 1) | + FIELD_PREP(MT_DMASHDL_PKT_MAX_SIZE_PSE, 8)); + + /* disable refill group 5 - group 15 and raise group 2 + * and 3 as high priority. + */ + mt76_wr(dev, MT_DMA_SHDL(MT_DMASHDL_REFILL), 0xffe00006); + mt76_clear(dev, MT_DMA_SHDL(MT_DMASHDL_PAGE), BIT(16)); + + for (i = 0; i < 5; i++) + mt76_wr(dev, MT_DMA_SHDL(MT_DMASHDL_GROUP_QUOTA(i)), + FIELD_PREP(MT_DMASHDL_GROUP_QUOTA_MIN, 0x3) | + FIELD_PREP(MT_DMASHDL_GROUP_QUOTA_MAX, 0x1ff)); + + mt76_wr(dev, MT_DMA_SHDL(MT_DMASHDL_Q_MAP(0)), 0x42104210); + mt76_wr(dev, MT_DMA_SHDL(MT_DMASHDL_Q_MAP(1)), 0x42104210); + + mt76_wr(dev, MT_DMA_SHDL(MT_DMASHDL_Q_MAP(2)), 0x4444); + + /* group pririority from high to low: + * 15 (cmd groups) > 4 > 3 > 2 > 1 > 0. + */ + mt76_wr(dev, MT_DMA_SHDL(MT_DMASHDL_SCHED_SET0), 0x6501234f); + mt76_wr(dev, MT_DMA_SHDL(MT_DMASHDL_SCHED_SET1), 0xedcba987); + mt76_wr(dev, MT_DMA_SHDL(MT_DMASHDL_OPTIONAL), 0x7004801c); + + mt76_wr(dev, MT_UDMA_WLCFG_1, + FIELD_PREP(MT_WL_TX_TMOUT_LMT, 80000) | + FIELD_PREP(MT_WL_RX_AGG_PKT_LMT, 1)); + + /* setup UDMA Rx Flush */ + mt76_clear(dev, MT_UDMA_WLCFG_0, MT_WL_RX_FLUSH); + /* hif reset */ + mt76_set(dev, MT_HIF_RST, MT_HIF_LOGIC_RST_N); + + mt76_set(dev, MT_UDMA_WLCFG_0, + MT_WL_RX_AGG_EN | MT_WL_RX_EN | MT_WL_TX_EN | + MT_WL_RX_MPSZ_PAD0 | MT_TICK_1US_EN | + MT_WL_TX_TMOUT_FUNC_EN); + mt76_rmw(dev, MT_UDMA_WLCFG_0, MT_WL_RX_AGG_LMT | MT_WL_RX_AGG_TO, + FIELD_PREP(MT_WL_RX_AGG_LMT, 32) | + FIELD_PREP(MT_WL_RX_AGG_TO, 100)); + + return 0; +} + +static int mt7663_usb_sdio_init_hardware(struct mt7615_dev *dev) +{ + int ret, idx; + + ret = mt7615_eeprom_init(dev, MT_EFUSE_BASE); + if (ret < 0) + return ret; + + if (mt76_is_usb(&dev->mt76)) { + ret = mt7663u_dma_sched_init(dev); + if (ret) + return ret; + } + + set_bit(MT76_STATE_INITIALIZED, &dev->mphy.state); + + /* Beacon and mgmt frames should occupy wcid 0 */ + idx = mt76_wcid_alloc(dev->mt76.wcid_mask, MT7615_WTBL_STA - 1); + if (idx) + return -ENOSPC; + + dev->mt76.global_wcid.idx = idx; + dev->mt76.global_wcid.hw_key_idx = -1; + rcu_assign_pointer(dev->mt76.wcid[idx], &dev->mt76.global_wcid); + + return 0; +} + +int mt7663_usb_sdio_register_device(struct mt7615_dev *dev) +{ + struct ieee80211_hw *hw = mt76_hw(dev); + int err; + + INIT_WORK(&dev->rate_work, mt7663_usb_sdio_rate_work); + INIT_LIST_HEAD(&dev->wrd_head); + mt7615_init_device(dev); + + err = mt7663_usb_sdio_init_hardware(dev); + if (err) + return err; + + hw->extra_tx_headroom += MT_USB_TXD_SIZE; + if (mt76_is_usb(&dev->mt76)) { + hw->extra_tx_headroom += MT_USB_HDR_SIZE; + /* check hw sg support in order to enable AMSDU */ + if (dev->mt76.usb.sg_en) + hw->max_tx_fragments = MT_HW_TXP_MAX_BUF_NUM; + else + hw->max_tx_fragments = 1; + } + + err = mt76_register_device(&dev->mt76, true, mt76_rates, + ARRAY_SIZE(mt76_rates)); + if (err < 0) + return err; + + if (!dev->mt76.usb.sg_en) { + struct ieee80211_sta_vht_cap *vht_cap; + + /* decrease max A-MSDU size if SG is not supported */ + vht_cap = &dev->mphy.sband_5g.sband.vht_cap; + vht_cap->cap &= ~IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454; + } + + ieee80211_queue_work(hw, &dev->mcu_work); + mt7615_init_txpower(dev, &dev->mphy.sband_2g.sband); + mt7615_init_txpower(dev, &dev->mphy.sband_5g.sband); + + return mt7615_init_debugfs(dev); +} +EXPORT_SYMBOL_GPL(mt7663_usb_sdio_register_device); + +MODULE_AUTHOR("Lorenzo Bianconi "); +MODULE_AUTHOR("Sean Wang "); +MODULE_DESCRIPTION("MediaTek MT7663 SDIO/USB helpers"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac.h b/drivers/net/wireless/mediatek/mt76/mt76_connac.h new file mode 100644 index 000000000..b1677ca24 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac.h @@ -0,0 +1,499 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* Copyright (C) 2020 MediaTek Inc. */ + +#ifndef __MT76_CONNAC_H +#define __MT76_CONNAC_H + +#include "mt76.h" + +enum rx_pkt_type { + PKT_TYPE_TXS, + PKT_TYPE_TXRXV, + PKT_TYPE_NORMAL, + PKT_TYPE_RX_DUP_RFB, + PKT_TYPE_RX_TMR, + PKT_TYPE_RETRIEVE, + PKT_TYPE_TXRX_NOTIFY, + PKT_TYPE_RX_EVENT, + PKT_TYPE_NORMAL_MCU, + PKT_TYPE_RX_FW_MONITOR = 0x0c, + PKT_TYPE_TXRX_NOTIFY_V0 = 0x18, +}; + +#define MT76_CONNAC_SCAN_IE_LEN 600 +#define MT76_CONNAC_MAX_NUM_SCHED_SCAN_INTERVAL 10 +#define MT76_CONNAC_MAX_TIME_SCHED_SCAN_INTERVAL U16_MAX +#define MT76_CONNAC_MAX_SCHED_SCAN_SSID 10 +#define MT76_CONNAC_MAX_SCAN_MATCH 16 + +#define MT76_CONNAC_MAX_WMM_SETS 4 + +#define MT76_CONNAC_COREDUMP_TIMEOUT (HZ / 20) +#define MT76_CONNAC_COREDUMP_SZ (1300 * 1024) + +#define MT_TXD_SIZE (8 * 4) + +#define MT_USB_TXD_SIZE (MT_TXD_SIZE + 8 * 4) +#define MT_USB_HDR_SIZE 4 +#define MT_USB_TAIL_SIZE 4 + +#define MT_SDIO_TXD_SIZE (MT_TXD_SIZE + 8 * 4) +#define MT_SDIO_TAIL_SIZE 8 +#define MT_SDIO_HDR_SIZE 4 + +#define MT_MSDU_ID_VALID BIT(15) + +#define MT_TXD_LEN_LAST BIT(15) +#define MT_TXD_LEN_MASK GENMASK(11, 0) +#define MT_TXD_LEN_MSDU_LAST BIT(14) +#define MT_TXD_LEN_AMSDU_LAST BIT(15) + +/* PCIE part */ +#define PCIE_AER_UNC_STATUS_OFFSET 0x204 +#define PCIE_AER_UNC_MASK_OFFSET 0x208 +#define PCIE_AER_CO_STATUS_OFFSET 0x210 + +enum { + CMD_CBW_20MHZ = IEEE80211_STA_RX_BW_20, + CMD_CBW_40MHZ = IEEE80211_STA_RX_BW_40, + CMD_CBW_80MHZ = IEEE80211_STA_RX_BW_80, + CMD_CBW_160MHZ = IEEE80211_STA_RX_BW_160, + CMD_CBW_10MHZ, + CMD_CBW_5MHZ, + CMD_CBW_8080MHZ, + CMD_CBW_320MHZ, + + CMD_HE_MCS_BW80 = 0, + CMD_HE_MCS_BW160, + CMD_HE_MCS_BW8080, + CMD_HE_MCS_BW_NUM +}; + +enum { + HW_BSSID_0 = 0x0, + HW_BSSID_1, + HW_BSSID_2, + HW_BSSID_3, + HW_BSSID_MAX = HW_BSSID_3, + EXT_BSSID_START = 0x10, + EXT_BSSID_1, + EXT_BSSID_15 = 0x1f, + EXT_BSSID_MAX = EXT_BSSID_15, + REPEATER_BSSID_START = 0x20, + REPEATER_BSSID_MAX = 0x3f, +}; + +struct mt76_connac_reg_map { + u32 phys; + u32 maps; + u32 size; +}; + +struct mt76_connac_pm { + bool enable:1; + bool enable_user:1; + bool ds_enable:1; + bool ds_enable_user:1; + bool suspended:1; + + spinlock_t txq_lock; + struct { + struct mt76_wcid *wcid; + struct sk_buff *skb; + } tx_q[IEEE80211_NUM_ACS]; + + struct work_struct wake_work; + wait_queue_head_t wait; + + struct { + spinlock_t lock; + u32 count; + } wake; + struct mutex mutex; + + struct delayed_work ps_work; + unsigned long last_activity; + unsigned long idle_timeout; + + struct { + unsigned long last_wake_event; + unsigned long awake_time; + unsigned long last_doze_event; + unsigned long doze_time; + unsigned int lp_wake; + } stats; +}; + +struct mt76_connac_coredump { + struct sk_buff_head msg_list; + struct delayed_work work; + unsigned long last_activity; +}; + +struct mt76_connac_sta_key_conf { + s8 keyidx; + u8 key[16]; +}; + +#define MT_TXP_MAX_BUF_NUM 6 + +struct mt76_connac_fw_txp { + __le16 flags; + __le16 token; + u8 bss_idx; + __le16 rept_wds_wcid; + u8 nbuf; + __le32 buf[MT_TXP_MAX_BUF_NUM]; + __le16 len[MT_TXP_MAX_BUF_NUM]; +} __packed __aligned(4); + +#define MT_HW_TXP_MAX_MSDU_NUM 4 +#define MT_HW_TXP_MAX_BUF_NUM 4 + +struct mt76_connac_txp_ptr { + __le32 buf0; + __le16 len0; + __le16 len1; + __le32 buf1; +} __packed __aligned(4); + +struct mt76_connac_hw_txp { + __le16 msdu_id[MT_HW_TXP_MAX_MSDU_NUM]; + struct mt76_connac_txp_ptr ptr[MT_HW_TXP_MAX_BUF_NUM / 2]; +} __packed __aligned(4); + +struct mt76_connac_txp_common { + union { + struct mt76_connac_fw_txp fw; + struct mt76_connac_hw_txp hw; + }; +}; + +struct mt76_connac_tx_free { + __le16 rx_byte_cnt; + __le16 ctrl; + __le32 txd; +} __packed __aligned(4); + +extern const struct wiphy_wowlan_support mt76_connac_wowlan_support; + +static inline bool is_connac3(struct mt76_dev *dev) +{ + return mt76_chip(dev) == 0x7925 || mt76_chip(dev) == 0x7927 || mt76_chip(dev) == 0x7928; +} + +static inline bool is_mt7925(struct mt76_dev *dev) +{ + return mt76_chip(dev) == 0x7925; +} + +static inline bool is_mt7927(struct mt76_dev *dev) +{ + return mt76_chip(dev) == 0x7927; +} + +static inline bool is_mt7928(struct mt76_dev *dev) +{ + return mt76_chip(dev) == 0x7928; +} + +static inline bool is_320mhz_supported(struct mt76_dev *dev) +{ + return mt76_chip(dev) == 0x7927; +} + +static inline bool is_mt7920(struct mt76_dev *dev) +{ + return mt76_chip(dev) == 0x7920; +} + +static inline bool is_mt7902(struct mt76_dev *dev) +{ + return mt76_chip(dev) == 0x7902; +} + +static inline bool is_mt7922(struct mt76_dev *dev) +{ + return mt76_chip(dev) == 0x7922; +} + +static inline bool is_connac2(struct mt76_dev *dev) +{ + return mt76_chip(dev) == 0x7961 || is_mt7922(dev) || is_mt7920(dev) || + is_mt7902(dev); +} + +static inline bool is_mt7663(struct mt76_dev *dev) +{ + return mt76_chip(dev) == 0x7663; +} + +static inline bool is_mt7915(struct mt76_dev *dev) +{ + return mt76_chip(dev) == 0x7915; +} + +static inline bool is_mt7916(struct mt76_dev *dev) +{ + return mt76_chip(dev) == 0x7906; +} + +static inline bool is_mt7981(struct mt76_dev *dev) +{ + return mt76_chip(dev) == 0x7981; +} + +static inline bool is_mt7986(struct mt76_dev *dev) +{ + return mt76_chip(dev) == 0x7986; +} + +static inline bool is_mt798x(struct mt76_dev *dev) +{ + return is_mt7981(dev) || is_mt7986(dev); +} + +static inline bool is_mt7996(struct mt76_dev *dev) +{ + u16 chip = mt76_chip(dev); + return chip == 0x7990 || chip == 0x7991; +} + +static inline bool is_mt7992(struct mt76_dev *dev) +{ + return mt76_chip(dev) == 0x7992; +} + +static inline bool is_mt7990(struct mt76_dev *dev) +{ + return mt76_chip(dev) == 0x7993; +} + +static inline bool is_mt799x(struct mt76_dev *dev) +{ + return is_mt7996(dev) || is_mt7992(dev) || is_mt7990(dev); +} + +static inline bool is_mt7622(struct mt76_dev *dev) +{ + if (!IS_ENABLED(CONFIG_MT7622_WMAC)) + return false; + + return mt76_chip(dev) == 0x7622; +} + +static inline bool is_mt7615(struct mt76_dev *dev) +{ + return mt76_chip(dev) == 0x7615 || mt76_chip(dev) == 0x7611; +} + +static inline bool is_mt7611(struct mt76_dev *dev) +{ + return mt76_chip(dev) == 0x7611; +} + +static inline bool is_connac_v1(struct mt76_dev *dev) +{ + return is_mt7615(dev) || is_mt7663(dev) || is_mt7622(dev); +} + +static inline bool is_mt76_fw_txp(struct mt76_dev *dev) +{ + switch (mt76_chip(dev)) { + case 0x7961: + case 0x7920: + case 0x7922: + case 0x7902: + case 0x7925: + case 0x7927: + case 0x7928: + case 0x7663: + case 0x7622: + return false; + default: + return true; + } +} + +static inline u8 mt76_connac_chan_bw(struct cfg80211_chan_def *chandef) +{ + static const u8 width_to_bw[] = { + [NL80211_CHAN_WIDTH_40] = CMD_CBW_40MHZ, + [NL80211_CHAN_WIDTH_80] = CMD_CBW_80MHZ, + [NL80211_CHAN_WIDTH_80P80] = CMD_CBW_8080MHZ, + [NL80211_CHAN_WIDTH_160] = CMD_CBW_160MHZ, + [NL80211_CHAN_WIDTH_5] = CMD_CBW_5MHZ, + [NL80211_CHAN_WIDTH_10] = CMD_CBW_10MHZ, + [NL80211_CHAN_WIDTH_20] = CMD_CBW_20MHZ, + [NL80211_CHAN_WIDTH_20_NOHT] = CMD_CBW_20MHZ, + [NL80211_CHAN_WIDTH_320] = CMD_CBW_320MHZ, + }; + + if (chandef->width >= ARRAY_SIZE(width_to_bw)) + return 0; + + return width_to_bw[chandef->width]; +} + +static inline u8 mt76_connac_lmac_mapping(u8 ac) +{ + /* LMAC uses the reverse order of mac80211 AC indexes */ + return 3 - ac; +} + +static inline void * +mt76_connac_txwi_to_txp(struct mt76_dev *dev, struct mt76_txwi_cache *t) +{ + u8 *txwi; + + if (!t) + return NULL; + + txwi = mt76_get_txwi_ptr(dev, t); + + return (void *)(txwi + MT_TXD_SIZE); +} + +static inline u8 mt76_connac_spe_idx(u8 antenna_mask) +{ + static const u8 ant_to_spe[] = {0, 0, 1, 0, 3, 2, 4, 0, + 9, 8, 6, 10, 16, 12, 18, 0}; + + if (antenna_mask >= sizeof(ant_to_spe)) + return 0; + + return ant_to_spe[antenna_mask]; +} + +static inline void mt76_connac_irq_enable(struct mt76_dev *dev, u32 mask) +{ + mt76_set_irq_mask(dev, 0, 0, mask); + tasklet_schedule(&dev->irq_tasklet); +} + +int mt76_connac_pm_wake(struct mt76_phy *phy, struct mt76_connac_pm *pm); +void mt76_connac_power_save_sched(struct mt76_phy *phy, + struct mt76_connac_pm *pm); +void mt76_connac_free_pending_tx_skbs(struct mt76_connac_pm *pm, + struct mt76_wcid *wcid); + +static inline void mt76_connac_tx_cleanup(struct mt76_dev *dev) +{ + dev->queue_ops->tx_cleanup(dev, dev->q_mcu[MT_MCUQ_WM], false); + dev->queue_ops->tx_cleanup(dev, dev->q_mcu[MT_MCUQ_WA], false); +} + +static inline bool +mt76_connac_pm_ref(struct mt76_phy *phy, struct mt76_connac_pm *pm) +{ + bool ret = false; + + spin_lock_bh(&pm->wake.lock); + if (test_bit(MT76_STATE_PM, &phy->state)) + goto out; + + pm->wake.count++; + ret = true; +out: + spin_unlock_bh(&pm->wake.lock); + + return ret; +} + +static inline void +mt76_connac_pm_unref(struct mt76_phy *phy, struct mt76_connac_pm *pm) +{ + spin_lock_bh(&pm->wake.lock); + + pm->last_activity = jiffies; + if (--pm->wake.count == 0 && + test_bit(MT76_STATE_MCU_RUNNING, &phy->state)) + mt76_connac_power_save_sched(phy, pm); + + spin_unlock_bh(&pm->wake.lock); +} + +static inline bool +mt76_connac_skip_fw_pmctrl(struct mt76_phy *phy, struct mt76_connac_pm *pm) +{ + struct mt76_dev *dev = phy->dev; + bool ret; + + if (dev->token_count) + return true; + + spin_lock_bh(&pm->wake.lock); + ret = pm->wake.count || test_and_set_bit(MT76_STATE_PM, &phy->state); + spin_unlock_bh(&pm->wake.lock); + + return ret; +} + +static inline void +mt76_connac_mutex_acquire(struct mt76_dev *dev, struct mt76_connac_pm *pm) + __acquires(&dev->mutex) +{ + mutex_lock(&dev->mutex); + mt76_connac_pm_wake(&dev->phy, pm); +} + +static inline void +mt76_connac_mutex_release(struct mt76_dev *dev, struct mt76_connac_pm *pm) + __releases(&dev->mutex) +{ + mt76_connac_power_save_sched(&dev->phy, pm); + mutex_unlock(&dev->mutex); +} + +void mt76_connac_gen_ppe_thresh(u8 *he_ppet, int nss, enum nl80211_band band); +int mt76_connac_init_tx_queues(struct mt76_phy *phy, int idx, int n_desc, + int ring_base, void *wed, u32 flags); +void mt76_connac_set_txpower_cur(struct mt76_phy *phy, s8 max_power); +s8 mt76_connac_get_rate_power_limit(struct mt76_phy *phy, + struct ieee80211_channel *chan, + struct mt76_power_limits *limits); +void mt76_connac_write_hw_txp(struct mt76_dev *dev, + struct mt76_tx_info *tx_info, + void *txp_ptr, u32 id); +void mt76_connac_txp_skb_unmap(struct mt76_dev *dev, + struct mt76_txwi_cache *txwi); +void mt76_connac_tx_complete_skb(struct mt76_dev *mdev, + struct mt76_queue_entry *e); +void mt76_connac_pm_queue_skb(struct ieee80211_hw *hw, + struct mt76_connac_pm *pm, + struct mt76_wcid *wcid, + struct sk_buff *skb); +void mt76_connac_pm_dequeue_skbs(struct mt76_phy *phy, + struct mt76_connac_pm *pm); +void mt76_connac2_mac_write_txwi(struct mt76_dev *dev, __le32 *txwi, + struct sk_buff *skb, struct mt76_wcid *wcid, + struct ieee80211_key_conf *key, int pid, + enum mt76_txq_id qid, u32 changed); +u16 mt76_connac2_mac_tx_rate_val(struct mt76_phy *mphy, + struct ieee80211_bss_conf *conf, + bool beacon, bool mcast); +bool mt76_connac2_mac_fill_txs(struct mt76_dev *dev, struct mt76_wcid *wcid, + __le32 *txs_data); +bool mt76_connac2_mac_add_txs_skb(struct mt76_dev *dev, struct mt76_wcid *wcid, + int pid, __le32 *txs_data); +void mt76_connac2_mac_decode_he_radiotap(struct mt76_dev *dev, + struct sk_buff *skb, + __le32 *rxv, u32 mode); +int mt76_connac2_reverse_frag0_hdr_trans(struct ieee80211_vif *vif, + struct sk_buff *skb, u16 hdr_offset); +int mt76_connac2_mac_fill_rx_rate(struct mt76_dev *dev, + struct mt76_rx_status *status, + struct ieee80211_supported_band *sband, + __le32 *rxv, u8 *mode); +void mt76_connac2_tx_check_aggr(struct ieee80211_sta *sta, __le32 *txwi); +void mt76_connac2_txwi_free(struct mt76_dev *dev, struct mt76_txwi_cache *t, + struct ieee80211_sta *sta, + struct list_head *free_list); +void mt76_connac2_tx_token_put(struct mt76_dev *dev); + +/* connac3 */ +void mt76_connac3_mac_decode_he_radiotap(struct sk_buff *skb, __le32 *rxv, + u8 mode); +void mt76_connac3_mac_decode_eht_radiotap(struct sk_buff *skb, __le32 *rxv, + u8 mode); +#endif /* __MT76_CONNAC_H */ diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac2_mac.h b/drivers/net/wireless/mediatek/mt76/mt76_connac2_mac.h new file mode 100644 index 000000000..d4e2c3140 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac2_mac.h @@ -0,0 +1,365 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* Copyright (C) 2022 MediaTek Inc. */ + +#ifndef __MT76_CONNAC2_MAC_H +#define __MT76_CONNAC2_MAC_H + +enum tx_header_format { + MT_HDR_FORMAT_802_3, + MT_HDR_FORMAT_CMD, + MT_HDR_FORMAT_802_11, + MT_HDR_FORMAT_802_11_EXT, +}; + +enum tx_pkt_type { + MT_TX_TYPE_CT, + MT_TX_TYPE_SF, + MT_TX_TYPE_CMD, + MT_TX_TYPE_FW, +}; + +enum { + MT_CTX0, + MT_HIF0 = 0x0, + + MT_LMAC_AC00 = 0x0, + MT_LMAC_AC01, + MT_LMAC_AC02, + MT_LMAC_AC03, + MT_LMAC_ALTX0 = 0x10, + MT_LMAC_BMC0, + MT_LMAC_BCN0, + MT_LMAC_PSMP0, +}; + +enum { + MT_TXS_MPDU_FMT = 0, + MT_TXS_PPDU_FMT = 2, +}; + +#define MT_TX_FREE_MSDU_CNT GENMASK(9, 0) +#define MT_TX_FREE_WLAN_ID GENMASK(23, 14) +#define MT_TX_FREE_COUNT GENMASK(12, 0) +/* 0: success, others: dropped */ +#define MT_TX_FREE_STATUS GENMASK(14, 13) +#define MT_TX_FREE_MSDU_ID GENMASK(30, 16) +#define MT_TX_FREE_PAIR BIT(31) +/* will support this field in further revision */ +#define MT_TX_FREE_RATE GENMASK(13, 0) + +#define MT_TXD0_Q_IDX GENMASK(31, 25) +#define MT_TXD0_PKT_FMT GENMASK(24, 23) +#define MT_TXD0_ETH_TYPE_OFFSET GENMASK(22, 16) +#define MT_TXD0_TX_BYTES GENMASK(15, 0) + +#define MT_TXD1_LONG_FORMAT BIT(31) +#define MT_TXD1_TGID BIT(30) +#define MT_TXD1_OWN_MAC GENMASK(29, 24) +#define MT_TXD1_AMSDU BIT(23) +#define MT_TXD1_TID GENMASK(22, 20) +#define MT_TXD1_HDR_PAD GENMASK(19, 18) +#define MT_TXD1_HDR_FORMAT GENMASK(17, 16) +#define MT_TXD1_HDR_INFO GENMASK(15, 11) +#define MT_TXD1_ETH_802_3 BIT(15) +#define MT_TXD1_VTA BIT(10) +#define MT_TXD1_WLAN_IDX GENMASK(9, 0) + +#define MT_TXD2_FIX_RATE BIT(31) +#define MT_TXD2_FIXED_RATE BIT(30) +#define MT_TXD2_POWER_OFFSET GENMASK(29, 24) +#define MT_TXD2_MAX_TX_TIME GENMASK(23, 16) +#define MT_TXD2_FRAG GENMASK(15, 14) +#define MT_TXD2_HTC_VLD BIT(13) +#define MT_TXD2_DURATION BIT(12) +#define MT_TXD2_BIP BIT(11) +#define MT_TXD2_MULTICAST BIT(10) +#define MT_TXD2_RTS BIT(9) +#define MT_TXD2_SOUNDING BIT(8) +#define MT_TXD2_NDPA BIT(7) +#define MT_TXD2_NDP BIT(6) +#define MT_TXD2_FRAME_TYPE GENMASK(5, 4) +#define MT_TXD2_SUB_TYPE GENMASK(3, 0) + +#define MT_TXD3_SN_VALID BIT(31) +#define MT_TXD3_PN_VALID BIT(30) +#define MT_TXD3_SW_POWER_MGMT BIT(29) +#define MT_TXD3_BA_DISABLE BIT(28) +#define MT_TXD3_SEQ GENMASK(27, 16) +#define MT_TXD3_REM_TX_COUNT GENMASK(15, 11) +#define MT_TXD3_TX_COUNT GENMASK(10, 6) +#define MT_TXD3_TIMING_MEASURE BIT(5) +#define MT_TXD3_DAS BIT(4) +#define MT_TXD3_EEOSP BIT(3) +#define MT_TXD3_EMRD BIT(2) +#define MT_TXD3_PROTECT_FRAME BIT(1) +#define MT_TXD3_NO_ACK BIT(0) + +#define MT_TXD4_PN_LOW GENMASK(31, 0) + +#define MT_TXD5_PN_HIGH GENMASK(31, 16) +#define MT_TXD5_MD BIT(15) +#define MT_TXD5_ADD_BA BIT(14) +#define MT_TXD5_TX_STATUS_HOST BIT(10) +#define MT_TXD5_TX_STATUS_MCU BIT(9) +#define MT_TXD5_TX_STATUS_FMT BIT(8) +#define MT_TXD5_PID GENMASK(7, 0) + +#define MT_TXD6_TX_IBF BIT(31) +#define MT_TXD6_TX_EBF BIT(30) +#define MT_TXD6_TX_RATE GENMASK(29, 16) +#define MT_TXD6_SGI GENMASK(15, 14) +#define MT_TXD6_HELTF GENMASK(13, 12) +#define MT_TXD6_LDPC BIT(11) +#define MT_TXD6_SPE_ID_IDX BIT(10) +#define MT_TXD6_ANT_ID GENMASK(7, 4) +#define MT_TXD6_DYN_BW BIT(3) +#define MT_TXD6_FIXED_BW BIT(2) +#define MT_TXD6_BW GENMASK(1, 0) + +#define MT_TXD7_TXD_LEN GENMASK(31, 30) +#define MT_TXD7_UDP_TCP_SUM BIT(29) +#define MT_TXD7_IP_SUM BIT(28) +#define MT_TXD7_TYPE GENMASK(21, 20) +#define MT_TXD7_SUB_TYPE GENMASK(19, 16) + +#define MT_TXD7_PSE_FID GENMASK(27, 16) +#define MT_TXD7_SPE_IDX GENMASK(15, 11) +#define MT_TXD7_HW_AMSDU BIT(10) +#define MT_TXD7_TX_TIME GENMASK(9, 0) + +#define MT_TXD8_L_TYPE GENMASK(5, 4) +#define MT_TXD8_L_SUB_TYPE GENMASK(3, 0) + +#define MT_TX_RATE_STBC BIT(13) +#define MT_TX_RATE_NSS GENMASK(12, 10) +#define MT_TX_RATE_MODE GENMASK(9, 6) +#define MT_TX_RATE_SU_EXT_TONE BIT(5) +#define MT_TX_RATE_DCM BIT(4) +/* VHT/HE only use bits 0-3 */ +#define MT_TX_RATE_IDX GENMASK(5, 0) + +#define MT_TXS0_FIXED_RATE BIT(31) +#define MT_TXS0_BW GENMASK(30, 29) +#define MT_TXS0_TID GENMASK(28, 26) +#define MT_TXS0_AMPDU BIT(25) +#define MT_TXS0_TXS_FORMAT GENMASK(24, 23) +#define MT_TXS0_BA_ERROR BIT(22) +#define MT_TXS0_PS_FLAG BIT(21) +#define MT_TXS0_TXOP_TIMEOUT BIT(20) +#define MT_TXS0_BIP_ERROR BIT(19) + +#define MT_TXS0_QUEUE_TIMEOUT BIT(18) +#define MT_TXS0_RTS_TIMEOUT BIT(17) +#define MT_TXS0_ACK_TIMEOUT BIT(16) +#define MT_TXS0_ACK_ERROR_MASK GENMASK(18, 16) + +#define MT_TXS0_TX_STATUS_HOST BIT(15) +#define MT_TXS0_TX_STATUS_MCU BIT(14) +#define MT_TXS0_TX_RATE GENMASK(13, 0) + +#define MT_TXS1_SEQNO GENMASK(31, 20) +#define MT_TXS1_RESP_RATE GENMASK(19, 16) +#define MT_TXS1_RXV_SEQNO GENMASK(15, 8) +#define MT_TXS1_TX_POWER_DBM GENMASK(7, 0) + +#define MT_TXS2_BF_STATUS GENMASK(31, 30) +#define MT_TXS2_LAST_TX_RATE GENMASK(29, 27) +#define MT_TXS2_SHARED_ANTENNA BIT(26) +#define MT_TXS2_WCID GENMASK(25, 16) +#define MT_TXS2_TX_DELAY GENMASK(15, 0) + +#define MT_TXS3_PID GENMASK(31, 24) +#define MT_TXS3_ANT_ID GENMASK(23, 0) + +#define MT_TXS4_TIMESTAMP GENMASK(31, 0) + +/* PPDU based TXS */ +#define MT_TXS5_MPDU_TX_BYTE GENMASK(22, 0) +#define MT_TXS5_MPDU_TX_CNT GENMASK(31, 23) + +#define MT_TXS6_MPDU_FAIL_CNT GENMASK(31, 23) +#define MT_TXS7_MPDU_RETRY_BYTE GENMASK(22, 0) +#define MT_TXS7_MPDU_RETRY_CNT GENMASK(31, 23) + +/* RXD DW0 */ +#define MT_RXD0_LENGTH GENMASK(15, 0) +#define MT_RXD0_PKT_FLAG GENMASK(19, 16) +#define MT_RXD0_PKT_TYPE GENMASK(31, 27) + +#define MT_RXD0_NORMAL_ETH_TYPE_OFS GENMASK(22, 16) +#define MT_RXD0_NORMAL_IP_SUM BIT(23) +#define MT_RXD0_NORMAL_UDP_TCP_SUM BIT(24) + +/* RXD DW1 */ +#define MT_RXD1_NORMAL_WLAN_IDX GENMASK(9, 0) +#define MT_RXD1_NORMAL_GROUP_1 BIT(11) +#define MT_RXD1_NORMAL_GROUP_2 BIT(12) +#define MT_RXD1_NORMAL_GROUP_3 BIT(13) +#define MT_RXD1_NORMAL_GROUP_4 BIT(14) +#define MT_RXD1_NORMAL_GROUP_5 BIT(15) +#define MT_RXD1_NORMAL_SEC_MODE GENMASK(20, 16) +#define MT_RXD1_NORMAL_KEY_ID GENMASK(22, 21) +#define MT_RXD1_NORMAL_CM BIT(23) +#define MT_RXD1_NORMAL_CLM BIT(24) +#define MT_RXD1_NORMAL_ICV_ERR BIT(25) +#define MT_RXD1_NORMAL_TKIP_MIC_ERR BIT(26) +#define MT_RXD1_NORMAL_FCS_ERR BIT(27) +#define MT_RXD1_NORMAL_BAND_IDX BIT(28) +#define MT_RXD1_NORMAL_SPP_EN BIT(29) +#define MT_RXD1_NORMAL_ADD_OM BIT(30) +#define MT_RXD1_NORMAL_SEC_DONE BIT(31) + +/* RXD DW2 */ +#define MT_RXD2_NORMAL_BSSID GENMASK(5, 0) +#define MT_RXD2_NORMAL_CO_ANT BIT(6) +#define MT_RXD2_NORMAL_BF_CQI BIT(7) +#define MT_RXD2_NORMAL_MAC_HDR_LEN GENMASK(12, 8) +#define MT_RXD2_NORMAL_HDR_TRANS BIT(13) +#define MT_RXD2_NORMAL_HDR_OFFSET GENMASK(15, 14) +#define MT_RXD2_NORMAL_TID GENMASK(19, 16) +#define MT_RXD2_NORMAL_MU_BAR BIT(21) +#define MT_RXD2_NORMAL_SW_BIT BIT(22) +#define MT_RXD2_NORMAL_AMSDU_ERR BIT(23) +#define MT_RXD2_NORMAL_MAX_LEN_ERROR BIT(24) +#define MT_RXD2_NORMAL_HDR_TRANS_ERROR BIT(25) +#define MT_RXD2_NORMAL_INT_FRAME BIT(26) +#define MT_RXD2_NORMAL_FRAG BIT(27) +#define MT_RXD2_NORMAL_NULL_FRAME BIT(28) +#define MT_RXD2_NORMAL_NDATA BIT(29) +#define MT_RXD2_NORMAL_NON_AMPDU BIT(30) +#define MT_RXD2_NORMAL_BF_REPORT BIT(31) + +/* RXD DW4 */ +#define MT_RXD4_NORMAL_PAYLOAD_FORMAT GENMASK(1, 0) +#define MT_RXD4_FIRST_AMSDU_FRAME GENMASK(1, 0) +#define MT_RXD4_MID_AMSDU_FRAME BIT(1) +#define MT_RXD4_LAST_AMSDU_FRAME BIT(0) +#define MT_RXD4_NORMAL_PATTERN_DROP BIT(9) +#define MT_RXD4_NORMAL_CLS BIT(10) +#define MT_RXD4_NORMAL_OFLD GENMASK(12, 11) +#define MT_RXD4_NORMAL_MAGIC_PKT BIT(13) +#define MT_RXD4_NORMAL_WOL GENMASK(18, 14) +#define MT_RXD4_NORMAL_CLS_BITMAP GENMASK(28, 19) +#define MT_RXD3_NORMAL_PF_MODE BIT(29) +#define MT_RXD3_NORMAL_PF_STS GENMASK(31, 30) + +#define MT_RXV_HDR_BAND_IDX BIT(24) + +/* RXD DW3 */ +#define MT_RXD3_NORMAL_RXV_SEQ GENMASK(7, 0) +#define MT_RXD3_NORMAL_CH_FREQ GENMASK(15, 8) +#define MT_RXD3_NORMAL_ADDR_TYPE GENMASK(17, 16) +#define MT_RXD3_NORMAL_U2M BIT(0) +#define MT_RXD3_NORMAL_HTC_VLD BIT(0) +#define MT_RXD3_NORMAL_TSF_COMPARE_LOSS BIT(19) +#define MT_RXD3_NORMAL_BEACON_MC BIT(20) +#define MT_RXD3_NORMAL_BEACON_UC BIT(21) +#define MT_RXD3_NORMAL_AMSDU BIT(22) +#define MT_RXD3_NORMAL_MESH BIT(23) +#define MT_RXD3_NORMAL_MHCP BIT(24) +#define MT_RXD3_NORMAL_NO_INFO_WB BIT(25) +#define MT_RXD3_NORMAL_DISABLE_RX_HDR_TRANS BIT(26) +#define MT_RXD3_NORMAL_POWER_SAVE_STAT BIT(27) +#define MT_RXD3_NORMAL_MORE BIT(28) +#define MT_RXD3_NORMAL_UNWANT BIT(29) +#define MT_RXD3_NORMAL_RX_DROP BIT(30) +#define MT_RXD3_NORMAL_VLAN2ETH BIT(31) + +/* RXD GROUP4 */ +#define MT_RXD6_FRAME_CONTROL GENMASK(15, 0) +#define MT_RXD6_TA_LO GENMASK(31, 16) + +#define MT_RXD7_TA_HI GENMASK(31, 0) + +#define MT_RXD8_SEQ_CTRL GENMASK(15, 0) +#define MT_RXD8_QOS_CTL GENMASK(31, 16) + +#define MT_RXD9_HT_CONTROL GENMASK(31, 0) + +/* P-RXV DW0 */ +#define MT_PRXV_TX_RATE GENMASK(6, 0) +#define MT_PRXV_TX_DCM BIT(4) +#define MT_PRXV_TX_ER_SU_106T BIT(5) +#define MT_PRXV_NSTS GENMASK(9, 7) +#define MT_PRXV_TXBF BIT(10) +#define MT_PRXV_HT_AD_CODE BIT(11) +#define MT_PRXV_HE_RU_ALLOC_L GENMASK(31, 28) + +#define MT_PRXV_FRAME_MODE GENMASK(14, 12) +#define MT_PRXV_HT_SGI GENMASK(16, 15) +#define MT_PRXV_HT_STBC GENMASK(23, 22) +#define MT_PRXV_TX_MODE GENMASK(27, 24) +#define MT_PRXV_DCM BIT(17) +#define MT_PRXV_NUM_RX BIT(20, 18) + +/* P-RXV DW1 */ +#define MT_PRXV_RCPI3 GENMASK(31, 24) +#define MT_PRXV_RCPI2 GENMASK(23, 16) +#define MT_PRXV_RCPI1 GENMASK(15, 8) +#define MT_PRXV_RCPI0 GENMASK(7, 0) +#define MT_PRXV_HE_RU_ALLOC_H GENMASK(3, 0) + +/* C-RXV */ +#define MT_CRXV_HT_STBC GENMASK(1, 0) +#define MT_CRXV_TX_MODE GENMASK(7, 4) +#define MT_CRXV_FRAME_MODE GENMASK(10, 8) +#define MT_CRXV_HT_SHORT_GI GENMASK(14, 13) +#define MT_CRXV_HE_LTF_SIZE GENMASK(18, 17) +#define MT_CRXV_HE_LDPC_EXT_SYM BIT(20) +#define MT_CRXV_HE_PE_DISAMBIG BIT(23) +#define MT_CRXV_HE_NUM_USER GENMASK(30, 24) +#define MT_CRXV_HE_UPLINK BIT(31) + +#define MT_CRXV_HE_RU0 GENMASK(7, 0) +#define MT_CRXV_HE_RU1 GENMASK(15, 8) +#define MT_CRXV_HE_RU2 GENMASK(23, 16) +#define MT_CRXV_HE_RU3 GENMASK(31, 24) + +#define MT_CRXV_HE_MU_AID GENMASK(30, 20) + +#define MT_CRXV_HE_SR_MASK GENMASK(11, 8) +#define MT_CRXV_HE_SR1_MASK GENMASK(16, 12) +#define MT_CRXV_HE_SR2_MASK GENMASK(20, 17) +#define MT_CRXV_HE_SR3_MASK GENMASK(24, 21) + +#define MT_CRXV_HE_BSS_COLOR GENMASK(5, 0) +#define MT_CRXV_HE_TXOP_DUR GENMASK(12, 6) +#define MT_CRXV_HE_BEAM_CHNG BIT(13) +#define MT_CRXV_HE_DOPPLER BIT(16) + +#define MT_CRXV_SNR GENMASK(18, 13) +#define MT_CRXV_FOE_LO GENMASK(31, 19) +#define MT_CRXV_FOE_HI GENMASK(6, 0) +#define MT_CRXV_FOE_SHIFT 13 + +#define MT_CT_PARSE_LEN 72 +#define MT_CT_DMA_BUF_NUM 2 + +#define MT_CT_INFO_APPLY_TXD BIT(0) +#define MT_CT_INFO_COPY_HOST_TXD_ALL BIT(1) +#define MT_CT_INFO_MGMT_FRAME BIT(2) +#define MT_CT_INFO_NONE_CIPHER_FRAME BIT(3) +#define MT_CT_INFO_HSR2_TX BIT(4) +#define MT_CT_INFO_FROM_HOST BIT(7) + +enum tx_mcu_port_q_idx { + MT_TX_MCU_PORT_RX_Q0 = 0x20, + MT_TX_MCU_PORT_RX_Q1, + MT_TX_MCU_PORT_RX_Q2, + MT_TX_MCU_PORT_RX_Q3, + MT_TX_MCU_PORT_RX_FWDL = 0x3e +}; + +enum tx_port_idx { + MT_TX_PORT_IDX_LMAC, + MT_TX_PORT_IDX_MCU +}; + +enum tx_frag_idx { + MT_TX_FRAG_NONE, + MT_TX_FRAG_FIRST, + MT_TX_FRAG_MID, + MT_TX_FRAG_LAST +}; + +#endif /* __MT76_CONNAC2_MAC_H */ diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac3_mac.c b/drivers/net/wireless/mediatek/mt76/mt76_connac3_mac.c new file mode 100644 index 000000000..651fcd416 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac3_mac.c @@ -0,0 +1,268 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2023 MediaTek Inc. */ + +#include "mt76_connac.h" +#include "mt76_connac3_mac.h" +#include "dma.h" + +#define HE_BITS(f) cpu_to_le16(IEEE80211_RADIOTAP_HE_##f) +#define EHT_BITS(f) cpu_to_le32(IEEE80211_RADIOTAP_EHT_##f) +#define HE_PREP(f, m, v) le16_encode_bits(le32_get_bits(v, MT_CRXV_HE_##m),\ + IEEE80211_RADIOTAP_HE_##f) +#define EHT_PREP(f, m, v) le32_encode_bits(le32_get_bits(v, MT_CRXV_EHT_##m),\ + IEEE80211_RADIOTAP_EHT_##f) + +static void +mt76_connac3_mac_decode_he_radiotap_ru(struct mt76_rx_status *status, + struct ieee80211_radiotap_he *he, + __le32 *rxv) +{ + u32 ru = le32_get_bits(rxv[0], MT_PRXV_HE_RU_ALLOC), offs = 0; + + status->bw = RATE_INFO_BW_HE_RU; + + switch (ru) { + case 0 ... 36: + status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_26; + offs = ru; + break; + case 37 ... 52: + status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_52; + offs = ru - 37; + break; + case 53 ... 60: + status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_106; + offs = ru - 53; + break; + case 61 ... 64: + status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_242; + offs = ru - 61; + break; + case 65 ... 66: + status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_484; + offs = ru - 65; + break; + case 67: + status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_996; + break; + case 68: + status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_2x996; + break; + } + + he->data1 |= HE_BITS(DATA1_BW_RU_ALLOC_KNOWN); + he->data2 |= HE_BITS(DATA2_RU_OFFSET_KNOWN) | + le16_encode_bits(offs, + IEEE80211_RADIOTAP_HE_DATA2_RU_OFFSET); +} + +#define MU_PREP(f, v) le16_encode_bits(v, IEEE80211_RADIOTAP_HE_MU_##f) +static void +mt76_connac3_mac_decode_he_mu_radiotap(struct sk_buff *skb, __le32 *rxv) +{ + struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb; + static const struct ieee80211_radiotap_he_mu mu_known = { + .flags1 = HE_BITS(MU_FLAGS1_SIG_B_MCS_KNOWN) | + HE_BITS(MU_FLAGS1_SIG_B_DCM_KNOWN) | + HE_BITS(MU_FLAGS1_CH1_RU_KNOWN) | + HE_BITS(MU_FLAGS1_SIG_B_SYMS_USERS_KNOWN), + .flags2 = HE_BITS(MU_FLAGS2_BW_FROM_SIG_A_BW_KNOWN), + }; + struct ieee80211_radiotap_he_mu *he_mu; + + status->flag |= RX_FLAG_RADIOTAP_HE_MU; + + he_mu = skb_push(skb, sizeof(mu_known)); + memcpy(he_mu, &mu_known, sizeof(mu_known)); + + he_mu->flags1 |= MU_PREP(FLAGS1_SIG_B_MCS, status->rate_idx); + if (status->he_dcm) + he_mu->flags1 |= MU_PREP(FLAGS1_SIG_B_DCM, status->he_dcm); + + he_mu->flags2 |= MU_PREP(FLAGS2_BW_FROM_SIG_A_BW, status->bw) | + MU_PREP(FLAGS2_SIG_B_SYMS_USERS, + le32_get_bits(rxv[4], MT_CRXV_HE_NUM_USER)); + + he_mu->ru_ch1[0] = le32_get_bits(rxv[16], MT_CRXV_HE_RU0) & 0xff; + + if (status->bw >= RATE_INFO_BW_40) { + he_mu->flags1 |= HE_BITS(MU_FLAGS1_CH2_RU_KNOWN); + he_mu->ru_ch2[0] = le32_get_bits(rxv[16], MT_CRXV_HE_RU1) & 0xff; + } + + if (status->bw >= RATE_INFO_BW_80) { + u32 ru_h, ru_l; + + he_mu->ru_ch1[1] = le32_get_bits(rxv[16], MT_CRXV_HE_RU2) & 0xff; + + ru_l = le32_get_bits(rxv[16], MT_CRXV_HE_RU3_L); + ru_h = le32_get_bits(rxv[17], MT_CRXV_HE_RU3_H) & 0x7; + he_mu->ru_ch2[1] = (u8)(ru_l | ru_h << 4); + } +} + +void mt76_connac3_mac_decode_he_radiotap(struct sk_buff *skb, __le32 *rxv, + u8 mode) +{ + struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb; + static const struct ieee80211_radiotap_he known = { + .data1 = HE_BITS(DATA1_DATA_MCS_KNOWN) | + HE_BITS(DATA1_DATA_DCM_KNOWN) | + HE_BITS(DATA1_STBC_KNOWN) | + HE_BITS(DATA1_CODING_KNOWN) | + HE_BITS(DATA1_LDPC_XSYMSEG_KNOWN) | + HE_BITS(DATA1_DOPPLER_KNOWN) | + HE_BITS(DATA1_SPTL_REUSE_KNOWN) | + HE_BITS(DATA1_BSS_COLOR_KNOWN), + .data2 = HE_BITS(DATA2_GI_KNOWN) | + HE_BITS(DATA2_TXBF_KNOWN) | + HE_BITS(DATA2_PE_DISAMBIG_KNOWN) | + HE_BITS(DATA2_TXOP_KNOWN), + }; + u32 ltf_size = le32_get_bits(rxv[4], MT_CRXV_HE_LTF_SIZE) + 1; + struct ieee80211_radiotap_he *he; + + status->flag |= RX_FLAG_RADIOTAP_HE; + + he = skb_push(skb, sizeof(known)); + memcpy(he, &known, sizeof(known)); + + he->data3 = HE_PREP(DATA3_BSS_COLOR, BSS_COLOR, rxv[9]) | + HE_PREP(DATA3_LDPC_XSYMSEG, LDPC_EXT_SYM, rxv[4]); + he->data4 = HE_PREP(DATA4_SU_MU_SPTL_REUSE, SR_MASK, rxv[13]); + he->data5 = HE_PREP(DATA5_PE_DISAMBIG, PE_DISAMBIG, rxv[5]) | + le16_encode_bits(ltf_size, + IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE); + if (le32_to_cpu(rxv[0]) & MT_PRXV_TXBF) + he->data5 |= HE_BITS(DATA5_TXBF); + he->data6 = HE_PREP(DATA6_TXOP, TXOP_DUR, rxv[9]) | + HE_PREP(DATA6_DOPPLER, DOPPLER, rxv[9]); + + switch (mode) { + case MT_PHY_TYPE_HE_SU: + he->data1 |= HE_BITS(DATA1_FORMAT_SU) | + HE_BITS(DATA1_UL_DL_KNOWN) | + HE_BITS(DATA1_BEAM_CHANGE_KNOWN) | + HE_BITS(DATA1_BW_RU_ALLOC_KNOWN); + + he->data3 |= HE_PREP(DATA3_BEAM_CHANGE, BEAM_CHNG, rxv[8]) | + HE_PREP(DATA3_UL_DL, UPLINK, rxv[5]); + break; + case MT_PHY_TYPE_HE_EXT_SU: + he->data1 |= HE_BITS(DATA1_FORMAT_EXT_SU) | + HE_BITS(DATA1_UL_DL_KNOWN) | + HE_BITS(DATA1_BW_RU_ALLOC_KNOWN); + + he->data3 |= HE_PREP(DATA3_UL_DL, UPLINK, rxv[5]); + break; + case MT_PHY_TYPE_HE_MU: + he->data1 |= HE_BITS(DATA1_FORMAT_MU) | + HE_BITS(DATA1_UL_DL_KNOWN); + + he->data3 |= HE_PREP(DATA3_UL_DL, UPLINK, rxv[5]); + he->data4 |= HE_PREP(DATA4_MU_STA_ID, MU_AID, rxv[8]); + + mt76_connac3_mac_decode_he_radiotap_ru(status, he, rxv); + mt76_connac3_mac_decode_he_mu_radiotap(skb, rxv); + break; + case MT_PHY_TYPE_HE_TB: + he->data1 |= HE_BITS(DATA1_FORMAT_TRIG) | + HE_BITS(DATA1_SPTL_REUSE2_KNOWN) | + HE_BITS(DATA1_SPTL_REUSE3_KNOWN) | + HE_BITS(DATA1_SPTL_REUSE4_KNOWN); + + he->data4 |= HE_PREP(DATA4_TB_SPTL_REUSE1, SR_MASK, rxv[13]) | + HE_PREP(DATA4_TB_SPTL_REUSE2, SR1_MASK, rxv[13]) | + HE_PREP(DATA4_TB_SPTL_REUSE3, SR2_MASK, rxv[13]) | + HE_PREP(DATA4_TB_SPTL_REUSE4, SR3_MASK, rxv[13]); + + mt76_connac3_mac_decode_he_radiotap_ru(status, he, rxv); + break; + default: + break; + } +} +EXPORT_SYMBOL_GPL(mt76_connac3_mac_decode_he_radiotap); + +static void * +mt76_connac3_mac_radiotap_push_tlv(struct sk_buff *skb, u16 type, u16 len) +{ + struct ieee80211_radiotap_tlv *tlv; + + tlv = skb_push(skb, sizeof(*tlv) + len); + tlv->type = cpu_to_le16(type); + tlv->len = cpu_to_le16(len); + memset(tlv->data, 0, len); + + return tlv->data; +} + +void mt76_connac3_mac_decode_eht_radiotap(struct sk_buff *skb, __le32 *rxv, + u8 mode) +{ + struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb; + struct ieee80211_radiotap_eht_usig *usig; + struct ieee80211_radiotap_eht *eht; + u32 ltf_size = le32_get_bits(rxv[4], MT_CRXV_HE_LTF_SIZE) + 1; + u8 bw = FIELD_GET(MT_PRXV_FRAME_MODE, le32_to_cpu(rxv[2])); + + if (WARN_ONCE(skb_mac_header(skb) != skb->data, + "Should push tlv at the top of mac hdr")) + return; + + eht = mt76_connac3_mac_radiotap_push_tlv(skb, IEEE80211_RADIOTAP_EHT, + sizeof(*eht) + sizeof(u32)); + usig = mt76_connac3_mac_radiotap_push_tlv(skb, IEEE80211_RADIOTAP_EHT_USIG, + sizeof(*usig)); + + status->flag |= RX_FLAG_RADIOTAP_TLV_AT_END; + + eht->known |= EHT_BITS(KNOWN_SPATIAL_REUSE) | + EHT_BITS(KNOWN_GI) | + EHT_BITS(KNOWN_EHT_LTF) | + EHT_BITS(KNOWN_LDPC_EXTRA_SYM_OM) | + EHT_BITS(KNOWN_PE_DISAMBIGUITY_OM) | + EHT_BITS(KNOWN_NSS_S); + + eht->data[0] |= + EHT_PREP(DATA0_SPATIAL_REUSE, SR_MASK, rxv[13]) | + cpu_to_le32(FIELD_PREP(IEEE80211_RADIOTAP_EHT_DATA0_GI, status->eht.gi) | + FIELD_PREP(IEEE80211_RADIOTAP_EHT_DATA0_LTF, ltf_size)) | + EHT_PREP(DATA0_PE_DISAMBIGUITY_OM, PE_DISAMBIG, rxv[5]) | + EHT_PREP(DATA0_LDPC_EXTRA_SYM_OM, LDPC_EXT_SYM, rxv[4]); + + /* iwlwifi and wireshark expect radiotap to report zero-based NSS, so subtract 1. */ + eht->data[7] |= le32_encode_bits(status->nss - 1, IEEE80211_RADIOTAP_EHT_DATA7_NSS_S); + + eht->user_info[0] |= + EHT_BITS(USER_INFO_MCS_KNOWN) | + EHT_BITS(USER_INFO_CODING_KNOWN) | + EHT_BITS(USER_INFO_NSS_KNOWN_O) | + EHT_BITS(USER_INFO_BEAMFORMING_KNOWN_O) | + EHT_BITS(USER_INFO_DATA_FOR_USER) | + le32_encode_bits(status->rate_idx, IEEE80211_RADIOTAP_EHT_USER_INFO_MCS) | + le32_encode_bits(status->nss - 1, IEEE80211_RADIOTAP_EHT_USER_INFO_NSS_O); + + if (le32_to_cpu(rxv[0]) & MT_PRXV_TXBF) + eht->user_info[0] |= EHT_BITS(USER_INFO_BEAMFORMING_O); + + if (le32_to_cpu(rxv[0]) & MT_PRXV_HT_AD_CODE) + eht->user_info[0] |= EHT_BITS(USER_INFO_CODING); + + if (mode == MT_PHY_TYPE_EHT_MU) + eht->user_info[0] |= EHT_BITS(USER_INFO_STA_ID_KNOWN) | + EHT_PREP(USER_INFO_STA_ID, MU_AID, rxv[8]); + + usig->common |= + EHT_BITS(USIG_COMMON_PHY_VER_KNOWN) | + EHT_BITS(USIG_COMMON_BW_KNOWN) | + EHT_BITS(USIG_COMMON_UL_DL_KNOWN) | + EHT_BITS(USIG_COMMON_BSS_COLOR_KNOWN) | + EHT_BITS(USIG_COMMON_TXOP_KNOWN) | + le32_encode_bits(0, IEEE80211_RADIOTAP_EHT_USIG_COMMON_PHY_VER) | + le32_encode_bits(bw, IEEE80211_RADIOTAP_EHT_USIG_COMMON_BW) | + EHT_PREP(USIG_COMMON_UL_DL, UPLINK, rxv[5]) | + EHT_PREP(USIG_COMMON_BSS_COLOR, BSS_COLOR, rxv[9]) | + EHT_PREP(USIG_COMMON_TXOP, TXOP_DUR, rxv[9]); +} +EXPORT_SYMBOL_GPL(mt76_connac3_mac_decode_eht_radiotap); diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac3_mac.h b/drivers/net/wireless/mediatek/mt76/mt76_connac3_mac.h new file mode 100644 index 000000000..d2d63767b --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac3_mac.h @@ -0,0 +1,442 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* Copyright (C) 2023 MediaTek Inc. */ + +#ifndef __MT76_CONNAC3_MAC_H +#define __MT76_CONNAC3_MAC_H + +enum { + MT_CTX0, + MT_HIF0 = 0x0, + + MT_LMAC_AC00 = 0x0, + MT_LMAC_AC01, + MT_LMAC_AC02, + MT_LMAC_AC03, + MT_LMAC_ALTX0 = 0x10, + MT_LMAC_BMC0, + MT_LMAC_BCN0, + MT_LMAC_PSMP0, +}; + +enum { + TXS_FM_MPDU = 0, + TXS_FM_PPDU = 2, +}; + +#define MT_CT_PARSE_LEN 72 +#define MT_CT_DMA_BUF_NUM 2 + +#define MT_RXD0_LENGTH GENMASK(15, 0) +#define MT_RXD0_PKT_FLAG GENMASK(19, 16) +#define MT_RXD0_PKT_TYPE GENMASK(31, 27) + +#define MT_RXD0_MESH BIT(18) +#define MT_RXD0_MHCP BIT(19) +#define MT_RXD0_NORMAL_ETH_TYPE_OFS GENMASK(22, 16) + +#define MT_RXD0_SW_PKT_TYPE_MASK GENMASK(31, 16) +#define MT_RXD0_SW_PKT_TYPE_MAP 0x380F +#define MT_RXD0_SW_PKT_TYPE_FRAME 0x3801 + +/* RXD DW1 */ +#define MT_RXD1_NORMAL_WLAN_IDX GENMASK(11, 0) +#define MT_RXD1_NORMAL_GROUP_1 BIT(16) +#define MT_RXD1_NORMAL_GROUP_2 BIT(17) +#define MT_RXD1_NORMAL_GROUP_3 BIT(18) +#define MT_RXD1_NORMAL_GROUP_4 BIT(19) +#define MT_RXD1_NORMAL_GROUP_5 BIT(20) +#define MT_RXD1_NORMAL_KEY_ID GENMASK(22, 21) +#define MT_RXD1_NORMAL_CM BIT(23) +#define MT_RXD1_NORMAL_CLM BIT(24) +#define MT_RXD1_NORMAL_ICV_ERR BIT(25) +#define MT_RXD1_NORMAL_TKIP_MIC_ERR BIT(26) +#define MT_RXD1_NORMAL_BAND_IDX GENMASK(28, 27) +#define MT_RXD1_NORMAL_SPP_EN BIT(29) +#define MT_RXD1_NORMAL_ADD_OM BIT(30) +#define MT_RXD1_NORMAL_SEC_DONE BIT(31) + +/* RXD DW2 */ +#define MT_RXD2_NORMAL_BSSID GENMASK(5, 0) +#define MT_RXD2_NORMAL_MAC_HDR_LEN GENMASK(12, 8) +#define MT_RXD2_NORMAL_HDR_TRANS BIT(7) +#define MT_RXD2_NORMAL_HDR_OFFSET GENMASK(15, 13) +#define MT_RXD2_NORMAL_SEC_MODE GENMASK(20, 16) +#define MT_RXD2_NORMAL_MU_BAR BIT(21) +#define MT_RXD2_NORMAL_SW_BIT BIT(22) +#define MT_RXD2_NORMAL_AMSDU_ERR BIT(23) +#define MT_RXD2_NORMAL_MAX_LEN_ERROR BIT(24) +#define MT_RXD2_NORMAL_HDR_TRANS_ERROR BIT(25) +#define MT_RXD2_NORMAL_INT_FRAME BIT(26) +#define MT_RXD2_NORMAL_FRAG BIT(27) +#define MT_RXD2_NORMAL_NULL_FRAME BIT(28) +#define MT_RXD2_NORMAL_NDATA BIT(29) +#define MT_RXD2_NORMAL_NON_AMPDU BIT(30) +#define MT_RXD2_NORMAL_BF_REPORT BIT(31) + +/* RXD DW3 */ +#define MT_RXD3_NORMAL_RXV_SEQ GENMASK(7, 0) +#define MT_RXD3_NORMAL_CH_FREQ GENMASK(15, 8) +#define MT_RXD3_NORMAL_ADDR_TYPE GENMASK(17, 16) +#define MT_RXD3_NORMAL_U2M BIT(0) +#define MT_RXD3_NORMAL_HTC_VLD BIT(18) +#define MT_RXD3_NORMAL_BEACON_MC BIT(20) +#define MT_RXD3_NORMAL_BEACON_UC BIT(21) +#define MT_RXD3_NORMAL_CO_ANT BIT(22) +#define MT_RXD3_NORMAL_FCS_ERR BIT(24) +#define MT_RXD3_NORMAL_IP_SUM BIT(26) +#define MT_RXD3_NORMAL_UDP_TCP_SUM BIT(27) +#define MT_RXD3_NORMAL_VLAN2ETH BIT(31) + +/* RXD DW4 */ +#define MT_RXD4_NORMAL_PAYLOAD_FORMAT GENMASK(1, 0) +#define MT_RXD4_FIRST_AMSDU_FRAME GENMASK(1, 0) +#define MT_RXD4_MID_AMSDU_FRAME BIT(1) +#define MT_RXD4_LAST_AMSDU_FRAME BIT(0) + +#define MT_RXV_HDR_BAND_IDX BIT(24) + +/* RXD GROUP4 */ +#define MT_RXD8_FRAME_CONTROL GENMASK(15, 0) + +#define MT_RXD10_SEQ_CTRL GENMASK(15, 0) +#define MT_RXD10_QOS_CTL GENMASK(31, 16) + +#define MT_RXD11_HT_CONTROL GENMASK(31, 0) + +/* P-RXV */ +#define MT_PRXV_TX_RATE GENMASK(6, 0) +#define MT_PRXV_TX_DCM BIT(4) +#define MT_PRXV_TX_ER_SU_106T BIT(5) +#define MT_PRXV_NSTS GENMASK(10, 7) +#define MT_PRXV_TXBF BIT(11) +#define MT_PRXV_HT_AD_CODE BIT(12) +#define MT_PRXV_HE_RU_ALLOC GENMASK(30, 22) +#define MT_PRXV_RCPI3 GENMASK(31, 24) +#define MT_PRXV_RCPI2 GENMASK(23, 16) +#define MT_PRXV_RCPI1 GENMASK(15, 8) +#define MT_PRXV_RCPI0 GENMASK(7, 0) +#define MT_PRXV_HT_SHORT_GI GENMASK(4, 3) +#define MT_PRXV_HT_STBC GENMASK(10, 9) +#define MT_PRXV_TX_MODE GENMASK(14, 11) +#define MT_PRXV_FRAME_MODE GENMASK(2, 0) +#define MT_PRXV_DCM BIT(5) + +/* C-RXV */ +#define MT_CRXV_HE_NUM_USER GENMASK(26, 20) +#define MT_CRXV_HE_LTF_SIZE GENMASK(28, 27) +#define MT_CRXV_HE_LDPC_EXT_SYM BIT(30) + +#define MT_CRXV_HE_PE_DISAMBIG BIT(1) +#define MT_CRXV_HE_UPLINK BIT(2) + +#define MT_CRXV_HE_MU_AID GENMASK(27, 17) +#define MT_CRXV_HE_BEAM_CHNG BIT(29) + +#define MT_CRXV_HE_DOPPLER BIT(0) +#define MT_CRXV_HE_BSS_COLOR GENMASK(15, 10) +#define MT_CRXV_HE_TXOP_DUR GENMASK(19, 17) + +#define MT_CRXV_HE_SR_MASK GENMASK(11, 8) +#define MT_CRXV_HE_SR1_MASK GENMASK(16, 12) +#define MT_CRXV_HE_SR2_MASK GENMASK(20, 17) +#define MT_CRXV_HE_SR3_MASK GENMASK(24, 21) + +#define MT_CRXV_HE_RU0 GENMASK(8, 0) +#define MT_CRXV_HE_RU1 GENMASK(17, 9) +#define MT_CRXV_HE_RU2 GENMASK(26, 18) +#define MT_CRXV_HE_RU3_L GENMASK(31, 27) +#define MT_CRXV_HE_RU3_H GENMASK(3, 0) + +#define MT_CRXV_EHT_NUM_USER GENMASK(26, 20) +#define MT_CRXV_EHT_LTF_SIZE GENMASK(28, 27) +#define MT_CRXV_EHT_LDPC_EXT_SYM BIT(30) +#define MT_CRXV_EHT_PE_DISAMBIG BIT(1) +#define MT_CRXV_EHT_UPLINK BIT(2) +#define MT_CRXV_EHT_MU_AID GENMASK(27, 17) +#define MT_CRXV_EHT_BEAM_CHNG BIT(29) +#define MT_CRXV_EHT_DOPPLER BIT(0) +#define MT_CRXV_EHT_BSS_COLOR GENMASK(15, 10) +#define MT_CRXV_EHT_TXOP_DUR GENMASK(23, 17) +#define MT_CRXV_EHT_SR_MASK GENMASK(11, 8) +#define MT_CRXV_EHT_SR1_MASK GENMASK(15, 12) +#define MT_CRXV_EHT_SR2_MASK GENMASK(19, 16) +#define MT_CRXV_EHT_SR3_MASK GENMASK(23, 20) +#define MT_CRXV_EHT_RU0 GENMASK(8, 0) +#define MT_CRXV_EHT_RU1 GENMASK(17, 9) +#define MT_CRXV_EHT_RU2 GENMASK(26, 18) +#define MT_CRXV_EHT_RU3_L GENMASK(31, 27) +#define MT_CRXV_EHT_RU3_H GENMASK(3, 0) +#define MT_CRXV_EHT_SIG_MCS GENMASK(19, 18) +#define MT_CRXV_EHT_LTF_SYM GENMASK(22, 20) + +enum tx_header_format { + MT_HDR_FORMAT_802_3, + MT_HDR_FORMAT_CMD, + MT_HDR_FORMAT_802_11, + MT_HDR_FORMAT_802_11_EXT, +}; + +enum tx_pkt_type { + MT_TX_TYPE_CT, + MT_TX_TYPE_SF, + MT_TX_TYPE_CMD, + MT_TX_TYPE_FW, +}; + +enum tx_port_idx { + MT_TX_PORT_IDX_LMAC, + MT_TX_PORT_IDX_MCU +}; + +enum tx_mcu_port_q_idx { + MT_TX_MCU_PORT_RX_Q0 = 0x20, + MT_TX_MCU_PORT_RX_Q1, + MT_TX_MCU_PORT_RX_Q2, + MT_TX_MCU_PORT_RX_Q3, + MT_TX_MCU_PORT_RX_FWDL = 0x3e +}; + +enum tx_mgnt_type { + MT_TX_NORMAL, + MT_TX_TIMING, + MT_TX_ADDBA, +}; + +enum tx_frag_idx { + MT_TX_FRAG_NONE, + MT_TX_FRAG_FIRST, + MT_TX_FRAG_MID, + MT_TX_FRAG_LAST +}; + +#define MT_CT_INFO_APPLY_TXD BIT(0) +#define MT_CT_INFO_COPY_HOST_TXD_ALL BIT(1) +#define MT_CT_INFO_MGMT_FRAME BIT(2) +#define MT_CT_INFO_NONE_CIPHER_FRAME BIT(3) +#define MT_CT_INFO_HSR2_TX BIT(4) +#define MT_CT_INFO_FROM_HOST BIT(7) + +#define MT_TXD_SIZE (8 * 4) + +#define MT_TXD0_Q_IDX GENMASK(31, 25) +#define MT_TXD0_PKT_FMT GENMASK(24, 23) +#define MT_TXD0_ETH_TYPE_OFFSET GENMASK(22, 16) +#define MT_TXD0_TX_BYTES GENMASK(15, 0) + +#define MT_TXD1_FIXED_RATE BIT(31) +#define MT_TXD1_OWN_MAC GENMASK(30, 25) +#define MT_TXD1_TID GENMASK(24, 21) +#define MT_TXD1_BIP BIT(24) +#define MT_TXD1_ETH_802_3 BIT(20) +#define MT_TXD1_HDR_INFO GENMASK(20, 16) +#define MT_TXD1_HDR_FORMAT GENMASK(15, 14) +#define MT_TXD1_TGID GENMASK(13, 12) +#define MT_TXD1_WLAN_IDX GENMASK(11, 0) + +#define MT_TXD2_POWER_OFFSET GENMASK(31, 26) +#define MT_TXD2_MAX_TX_TIME GENMASK(25, 16) +#define MT_TXD2_FRAG GENMASK(15, 14) +#define MT_TXD2_HTC_VLD BIT(13) +#define MT_TXD2_DURATION BIT(12) +#define MT_TXD2_HDR_PAD GENMASK(11, 10) +#define MT_TXD2_RTS BIT(9) +#define MT_TXD2_OWN_MAC_MAP BIT(8) +#define MT_TXD2_OWN_MAC_MAP_V2 BIT(9) +#define MT_TXD2_BF_TYPE GENMASK(6, 7) +#define MT_TXD2_BF_TYPE_V2 GENMASK(6, 8) +#define MT_TXD2_FRAME_TYPE GENMASK(5, 4) +#define MT_TXD2_SUB_TYPE GENMASK(3, 0) + +#define MT_TXD3_SN_VALID BIT(31) +#define MT_TXD3_PN_VALID BIT(30) +#define MT_TXD3_SW_POWER_MGMT BIT(29) +#define MT_TXD3_BA_DISABLE BIT(28) +#define MT_TXD3_SEQ GENMASK(27, 16) +#define MT_TXD3_REM_TX_COUNT GENMASK(15, 11) +#define MT_TXD3_TX_COUNT GENMASK(10, 6) +#define MT_TXD3_HW_AMSDU BIT(5) +#define MT_TXD3_BCM BIT(4) +#define MT_TXD3_EEOSP BIT(3) +#define MT_TXD3_EMRD BIT(2) +#define MT_TXD3_PROTECT_FRAME BIT(1) +#define MT_TXD3_NO_ACK BIT(0) + +#define MT_TXD4_PN_LOW GENMASK(31, 0) + +#define MT_TXD5_PN_HIGH GENMASK(31, 16) +#define MT_TXD5_FL BIT(15) +#define MT_TXD5_BYPASS_TBB BIT(14) +#define MT_TXD5_BYPASS_RBB BIT(13) +#define MT_TXD5_BSS_COLOR_ZERO BIT(12) +#define MT_TXD5_OCUP_BY_OTHER_LNK BIT(11) +#define MT_TXD5_TX_STATUS_HOST BIT(10) +#define MT_TXD5_TX_STATUS_MCU BIT(9) +#define MT_TXD5_TX_STATUS_FMT BIT(8) +#define MT_TXD5_PID GENMASK(7, 0) + +#define MT_TXD6_TX_SRC GENMASK(31, 30) +#define MT_TXD6_VTA BIT(28) +#define MT_TXD6_FIXED_BW BIT(25) +#define MT_TXD6_BW GENMASK(24, 22) +#define MT_TXD6_BW_V2 GENMASK(25, 22) +#define MT_TXD6_TX_RATE GENMASK(21, 16) +#define MT_TXD6_TIMESTAMP_OFS_EN BIT(15) +#define MT_TXD6_TIMESTAMP_OFS_EN_V2 GENMASK(15, 13) +#define MT_TXD6_TIMESTAMP_OFS_IDX GENMASK(14, 10) +#define MT_TXD6_TIMESTAMP_OFS_IDX_V2 GENMASK(12, 8) +#define MT_TXD6_TID_ADDBA GENMASK(10, 8) +#define MT_TXD6_MSDU_CNT GENMASK(9, 4) +#define MT_TXD6_MSDU_CNT_V2 GENMASK(15, 10) +#define MT_TXD6_DIS_MAT BIT(3) +#define MT_TXD6_DAS BIT(2) +#define MT_TXD6_AMSDU_CAP BIT(1) +#define MT_TXD6_MLD BIT(0) + +#define MT_TXD7_TXD_LEN GENMASK(31, 30) +#define MT_TXD7_IP_SUM BIT(29) +#define MT_TXD7_DROP_BY_SDO BIT(28) +#define MT_TXD7_MAC_TXD BIT(27) +#define MT_TXD7_CTXD BIT(26) +#define MT_TXD7_CTXD_CNT GENMASK(25, 22) +#define MT_TXD7_UDP_TCP_SUM BIT(15) +#define MT_TXD7_IMMEDIATE_TX BIT(14) +#define MT_TXD7_FORCE_RTS_CTS BIT(13) +#define MT_TXD7_ENABLE_ICI BIT(12) +#define MT_TXD7_TX_TIME GENMASK(9, 0) + +#define MT_TXD9_WLAN_IDX GENMASK(23, 8) + +#define MT_TXP_BUF_LEN GENMASK(11, 0) +#define MT_TXP_DMA_ADDR_H GENMASK(15, 12) + +#define MT_TXP0_TOKEN_ID0 GENMASK(14, 0) +#define MT_TXP0_TOKEN_ID0_VALID_MASK BIT(15) + +#define MT_TXP1_TID_ADDBA GENMASK(14, 12) +#define MT_TXP3_ML0_MASK BIT(15) +#define MT_TXP3_DMA_ADDR_H GENMASK(13, 12) + +#define MT_TX_RATE_STBC BIT(14) +#define MT_TX_RATE_NSS GENMASK(13, 10) +#define MT_TX_RATE_MODE GENMASK(9, 6) +#define MT_TX_RATE_SU_EXT_TONE BIT(5) +#define MT_TX_RATE_DCM BIT(4) +/* VHT/HE only use bits 0-3 */ +#define MT_TX_RATE_IDX GENMASK(5, 0) + +#define MT_TXFREE0_PKT_TYPE GENMASK(31, 27) +#define MT_TXFREE0_MSDU_CNT GENMASK(25, 16) +#define MT_TXFREE0_RX_BYTE GENMASK(15, 0) + +#define MT_TXFREE1_VER GENMASK(19, 16) + +#define MT_TXFREE_INFO_PAIR BIT(31) +#define MT_TXFREE_INFO_HEADER BIT(30) +#define MT_TXFREE_INFO_WLAN_ID GENMASK(23, 12) +#define MT_TXFREE_INFO_MSDU_ID GENMASK(14, 0) +#define MT_TXFREE_INFO_COUNT GENMASK(27, 24) +#define MT_TXFREE_INFO_STAT GENMASK(29, 28) + +#define MT_TXS_HDR_SIZE 4 /* Unit: DW */ +#define MT_TXS_SIZE 12 /* Unit: DW */ + +#define MT_TXS0_BW GENMASK(31, 29) +#define MT_TXS0_TID GENMASK(28, 26) +#define MT_TXS0_AMPDU BIT(25) +#define MT_TXS0_TXS_FORMAT GENMASK(24, 23) +#define MT_TXS0_BA_ERROR BIT(22) +#define MT_TXS0_PS_FLAG BIT(21) +#define MT_TXS0_TXOP_TIMEOUT BIT(20) +#define MT_TXS0_BIP_ERROR BIT(19) + +#define MT_TXS0_QUEUE_TIMEOUT BIT(18) +#define MT_TXS0_RTS_TIMEOUT BIT(17) +#define MT_TXS0_ACK_TIMEOUT BIT(16) +#define MT_TXS0_ACK_ERROR_MASK GENMASK(18, 16) + +#define MT_TXS0_TX_STATUS_HOST BIT(15) +#define MT_TXS0_TX_STATUS_MCU BIT(14) +#define MT_TXS0_TX_RATE GENMASK(13, 0) + +#define MT_TXS1_SEQNO GENMASK(31, 20) +#define MT_TXS1_RESP_RATE GENMASK(19, 16) +#define MT_TXS1_RXV_SEQNO GENMASK(15, 8) +#define MT_TXS1_TX_POWER_DBM GENMASK(7, 0) + +#define MT_TXS2_BF_STATUS GENMASK(31, 30) +#define MT_TXS2_BAND GENMASK(29, 28) +#define MT_TXS2_WCID GENMASK(27, 16) +#define MT_TXS2_TX_DELAY GENMASK(15, 0) + +#define MT_TXS3_PID GENMASK(31, 24) +#define MT_TXS3_RATE_STBC BIT(7) +#define MT_TXS3_FIXED_RATE BIT(6) +#define MT_TXS3_SRC GENMASK(5, 4) +#define MT_TXS3_SHARED_ANTENNA BIT(3) +#define MT_TXS3_LAST_TX_RATE GENMASK(2, 0) + +#define MT_TXS4_TIMESTAMP GENMASK(31, 0) + +/* MPDU based TXS */ +#define MT_TXS5_F0_FINAL_MPDU BIT(31) +#define MT_TXS5_F0_QOS BIT(30) +#define MT_TXS5_F0_TX_COUNT GENMASK(29, 25) +#define MT_TXS5_F0_FRONT_TIME GENMASK(24, 0) +#define MT_TXS5_F1_MPDU_TX_COUNT GENMASK(31, 24) +#define MT_TXS5_F1_MPDU_TX_BYTES GENMASK(23, 0) + +#define MT_TXS6_F0_NOISE_3 GENMASK(31, 24) +#define MT_TXS6_F0_NOISE_2 GENMASK(23, 16) +#define MT_TXS6_F0_NOISE_1 GENMASK(15, 8) +#define MT_TXS6_F0_NOISE_0 GENMASK(7, 0) +#define MT_TXS6_F1_MPDU_FAIL_COUNT GENMASK(31, 24) +#define MT_TXS6_F1_MPDU_FAIL_BYTES GENMASK(23, 0) + +#define MT_TXS7_F0_RCPI_3 GENMASK(31, 24) +#define MT_TXS7_F0_RCPI_2 GENMASK(23, 16) +#define MT_TXS7_F0_RCPI_1 GENMASK(15, 8) +#define MT_TXS7_F0_RCPI_0 GENMASK(7, 0) +#define MT_TXS7_F1_MPDU_RETRY_COUNT GENMASK(31, 24) +#define MT_TXS7_F1_MPDU_RETRY_BYTES GENMASK(23, 0) + +/* PPDU based TXS */ +#define MT_TXS5_MPDU_TX_CNT GENMASK(30, 20) +#define MT_TXS5_MPDU_TX_BYTE_SCALE BIT(15) +#define MT_TXS5_MPDU_TX_BYTE GENMASK(14, 0) + +#define MT_TXS6_MPDU_FAIL_CNT GENMASK(30, 20) +#define MT_TXS6_MPDU_FAIL_BYTE_SCALE BIT(15) +#define MT_TXS6_MPDU_FAIL_BYTE GENMASK(14, 0) + +#define MT_TXS7_MPDU_RETRY_CNT GENMASK(30, 20) +#define MT_TXS7_MPDU_RETRY_BYTE_SCALE BIT(15) +#define MT_TXS7_MPDU_RETRY_BYTE GENMASK(14, 0) + +struct mt7928_uni_txdone_event { + __le16 tag; + __le16 len; + + u8 pid; /* HW packet ID */ + u8 status; /* TX_RESULT_xx */ + __le16 seq; /* packet sequence number */ + + u8 wcid; /* WLAN index (WTBL) */ + u8 tx_count; /* TX attempts including retries */ + __le16 tx_rate; + + u8 flag; /* TXS_WITH_ADVANCED_INFO or TXS_IS_EXIST */ + u8 tid; + u8 rsp_rate; + u8 rate_tbl_idx; /* last TX rate index from WLAN table */ + + u8 bw; /* bandwidth used for this PPDU */ + u8 tx_pwr; /* dBm */ + u8 flush_reason; + u8 rsv[1]; + + __le32 tx_delay; /* unit: 32us, UMAC TX to TX status */ + __le32 timestamp; /* local TSF at first bit of MAC header */ + __le32 applied_flags; +} __packed; + +#endif /* __MT76_CONNAC3_MAC_H */ diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c new file mode 100644 index 000000000..8a8d608dd --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mac.c @@ -0,0 +1,1230 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2020 MediaTek Inc. */ + +#include "mt76_connac.h" +#include "mt76_connac2_mac.h" +#include "dma.h" + +#define HE_BITS(f) cpu_to_le16(IEEE80211_RADIOTAP_HE_##f) +#define HE_PREP(f, m, v) le16_encode_bits(le32_get_bits(v, MT_CRXV_HE_##m),\ + IEEE80211_RADIOTAP_HE_##f) + +void mt76_connac_gen_ppe_thresh(u8 *he_ppet, int nss, enum nl80211_band band) +{ + static const u8 ppet16_ppet8_ru3_ru0[] = { 0x1c, 0xc7, 0x71 }; + u8 i, ppet_bits, ppet_size, ru_bit_mask = 0xf; + + if (band == NL80211_BAND_2GHZ) + ru_bit_mask = 0x3; + + he_ppet[0] = FIELD_PREP(IEEE80211_PPE_THRES_NSS_MASK, nss - 1) | + FIELD_PREP(IEEE80211_PPE_THRES_RU_INDEX_BITMASK_MASK, + ru_bit_mask); + + ppet_bits = IEEE80211_PPE_THRES_INFO_PPET_SIZE * + nss * hweight8(ru_bit_mask) * 2; + ppet_size = DIV_ROUND_UP(ppet_bits, 8); + + for (i = 0; i < ppet_size - 1; i++) + he_ppet[i + 1] = ppet16_ppet8_ru3_ru0[i % 3]; + + he_ppet[i + 1] = ppet16_ppet8_ru3_ru0[i % 3] & + (0xff >> (8 - (ppet_bits - 1) % 8)); +} +EXPORT_SYMBOL_GPL(mt76_connac_gen_ppe_thresh); + +int mt76_connac_pm_wake(struct mt76_phy *phy, struct mt76_connac_pm *pm) +{ + struct mt76_dev *dev = phy->dev; + + if (mt76_is_usb(dev)) + return 0; + + cancel_delayed_work_sync(&pm->ps_work); + if (!test_bit(MT76_STATE_PM, &phy->state)) + return 0; + + if (pm->suspended) + return 0; + + queue_work(dev->wq, &pm->wake_work); + if (!wait_event_timeout(pm->wait, + !test_bit(MT76_STATE_PM, &phy->state), + 3 * HZ)) { + ieee80211_wake_queues(phy->hw); + return -ETIMEDOUT; + } + + return 0; +} +EXPORT_SYMBOL_GPL(mt76_connac_pm_wake); + +void mt76_connac_power_save_sched(struct mt76_phy *phy, + struct mt76_connac_pm *pm) +{ + struct mt76_dev *dev = phy->dev; + + if (mt76_is_usb(dev)) + return; + + if (!pm->enable) + return; + + if (pm->suspended) + return; + + pm->last_activity = jiffies; + + if (!test_bit(MT76_STATE_PM, &phy->state)) { + cancel_delayed_work(&phy->mac_work); + queue_delayed_work(dev->wq, &pm->ps_work, pm->idle_timeout); + } +} +EXPORT_SYMBOL_GPL(mt76_connac_power_save_sched); + +void mt76_connac_free_pending_tx_skbs(struct mt76_connac_pm *pm, + struct mt76_wcid *wcid) +{ + int i; + + spin_lock_bh(&pm->txq_lock); + for (i = 0; i < IEEE80211_NUM_ACS; i++) { + if (wcid && pm->tx_q[i].wcid != wcid) + continue; + + dev_kfree_skb(pm->tx_q[i].skb); + pm->tx_q[i].skb = NULL; + } + spin_unlock_bh(&pm->txq_lock); +} +EXPORT_SYMBOL_GPL(mt76_connac_free_pending_tx_skbs); + +void mt76_connac_pm_queue_skb(struct ieee80211_hw *hw, + struct mt76_connac_pm *pm, + struct mt76_wcid *wcid, + struct sk_buff *skb) +{ + int qid = skb_get_queue_mapping(skb); + struct mt76_phy *phy = hw->priv; + + spin_lock_bh(&pm->txq_lock); + if (!pm->tx_q[qid].skb) { + ieee80211_stop_queues(hw); + pm->tx_q[qid].wcid = wcid; + pm->tx_q[qid].skb = skb; + queue_work(phy->dev->wq, &pm->wake_work); + } else { + dev_kfree_skb(skb); + } + spin_unlock_bh(&pm->txq_lock); +} +EXPORT_SYMBOL_GPL(mt76_connac_pm_queue_skb); + +void mt76_connac_pm_dequeue_skbs(struct mt76_phy *phy, + struct mt76_connac_pm *pm) +{ + int i; + + spin_lock_bh(&pm->txq_lock); + for (i = 0; i < IEEE80211_NUM_ACS; i++) { + struct mt76_wcid *wcid = pm->tx_q[i].wcid; + struct ieee80211_sta *sta = NULL; + + if (!pm->tx_q[i].skb) + continue; + + if (wcid && wcid->sta) + sta = container_of((void *)wcid, struct ieee80211_sta, + drv_priv); + + mt76_tx(phy, sta, wcid, pm->tx_q[i].skb); + pm->tx_q[i].skb = NULL; + } + spin_unlock_bh(&pm->txq_lock); + + mt76_worker_schedule(&phy->dev->tx_worker); +} +EXPORT_SYMBOL_GPL(mt76_connac_pm_dequeue_skbs); + +void mt76_connac_tx_complete_skb(struct mt76_dev *mdev, + struct mt76_queue_entry *e) +{ + if (!e->txwi) { + dev_kfree_skb_any(e->skb); + return; + } + + if (e->skb) + mt76_tx_complete_skb(mdev, e->wcid, e->skb); +} +EXPORT_SYMBOL_GPL(mt76_connac_tx_complete_skb); + +void mt76_connac_write_hw_txp(struct mt76_dev *dev, + struct mt76_tx_info *tx_info, + void *txp_ptr, u32 id) +{ + struct mt76_connac_hw_txp *txp = txp_ptr; + struct mt76_connac_txp_ptr *ptr = &txp->ptr[0]; + int i, nbuf = tx_info->nbuf - 1; + u32 last_mask; + + tx_info->buf[0].len = MT_TXD_SIZE + sizeof(*txp); + tx_info->nbuf = 1; + + txp->msdu_id[0] = cpu_to_le16(id | MT_MSDU_ID_VALID); + + if (is_mt7663(dev) || is_connac2(dev) || is_connac3(dev)) + last_mask = MT_TXD_LEN_LAST; + else + last_mask = MT_TXD_LEN_AMSDU_LAST | + MT_TXD_LEN_MSDU_LAST; + + for (i = 0; i < nbuf; i++) { + u16 len = tx_info->buf[i + 1].len & MT_TXD_LEN_MASK; + u32 addr = tx_info->buf[i + 1].addr; + + if (i == nbuf - 1) + len |= last_mask; + + if (i & 1) { + ptr->buf1 = cpu_to_le32(addr); + ptr->len1 = cpu_to_le16(len); + ptr++; + } else { + ptr->buf0 = cpu_to_le32(addr); + ptr->len0 = cpu_to_le16(len); + } + } +} +EXPORT_SYMBOL_GPL(mt76_connac_write_hw_txp); + +static void +mt76_connac_txp_skb_unmap_fw(struct mt76_dev *mdev, + struct mt76_connac_fw_txp *txp) +{ + struct device *dev = is_connac_v1(mdev) ? mdev->dev : mdev->dma_dev; + int i; + + for (i = 0; i < txp->nbuf; i++) + dma_unmap_single(dev, le32_to_cpu(txp->buf[i]), + le16_to_cpu(txp->len[i]), DMA_TO_DEVICE); +} + +static void +mt76_connac_txp_skb_unmap_hw(struct mt76_dev *dev, + struct mt76_connac_hw_txp *txp) +{ + u32 last_mask; + int i; + + if (is_mt7663(dev) || is_connac2(dev) || is_connac3(dev)) + last_mask = MT_TXD_LEN_LAST; + else + last_mask = MT_TXD_LEN_MSDU_LAST; + + for (i = 0; i < ARRAY_SIZE(txp->ptr); i++) { + struct mt76_connac_txp_ptr *ptr = &txp->ptr[i]; + bool last; + u16 len; + + len = le16_to_cpu(ptr->len0); + last = len & last_mask; + len &= MT_TXD_LEN_MASK; + dma_unmap_single(dev->dev, le32_to_cpu(ptr->buf0), len, + DMA_TO_DEVICE); + if (last) + break; + + len = le16_to_cpu(ptr->len1); + last = len & last_mask; + len &= MT_TXD_LEN_MASK; + dma_unmap_single(dev->dev, le32_to_cpu(ptr->buf1), len, + DMA_TO_DEVICE); + if (last) + break; + } +} + +void mt76_connac_txp_skb_unmap(struct mt76_dev *dev, + struct mt76_txwi_cache *t) +{ + struct mt76_connac_txp_common *txp; + + txp = mt76_connac_txwi_to_txp(dev, t); + if (is_mt76_fw_txp(dev)) + mt76_connac_txp_skb_unmap_fw(dev, &txp->fw); + else + mt76_connac_txp_skb_unmap_hw(dev, &txp->hw); +} +EXPORT_SYMBOL_GPL(mt76_connac_txp_skb_unmap); + +int mt76_connac_init_tx_queues(struct mt76_phy *phy, int idx, int n_desc, + int ring_base, void *wed, u32 flags) +{ + int i, err; + + err = mt76_init_tx_queue(phy, 0, idx, n_desc, ring_base, + wed, flags); + if (err < 0) + return err; + + for (i = 1; i <= MT_TXQ_PSD; i++) + phy->q_tx[i] = phy->q_tx[0]; + + return 0; +} +EXPORT_SYMBOL_GPL(mt76_connac_init_tx_queues); + +void mt76_connac_set_txpower_cur(struct mt76_phy *phy, s8 max_power) +{ + int delta; + + delta = mt76_tx_power_path_delta(hweight16(phy->chainmask)); + phy->txpower_cur = max_power - delta; +} +EXPORT_SYMBOL_GPL(mt76_connac_set_txpower_cur); + +#define __bitrate_mask_check(_mcs, _mode) \ +({ \ + u8 i = 0; \ + for (nss = 0; i < ARRAY_SIZE(mask->control[band]._mcs); i++) { \ + if (!mask->control[band]._mcs[i]) \ + continue; \ + if (hweight16(mask->control[band]._mcs[i]) == 1) { \ + mode = MT_PHY_TYPE_##_mode; \ + rateidx = ffs(mask->control[band]._mcs[i]) - 1; \ + if (mode == MT_PHY_TYPE_HT) \ + rateidx += 8 * i; \ + else \ + nss = i + 1; \ + goto out; \ + } \ + } \ +}) + +u16 mt76_connac2_mac_tx_rate_val(struct mt76_phy *mphy, + struct ieee80211_bss_conf *conf, + bool beacon, bool mcast) +{ + u8 nss = 0, mode = 0, band = NL80211_BAND_2GHZ; + int rateidx = 0, offset = 0, mcast_rate; + struct cfg80211_chan_def *chandef; + struct mt76_vif_link *mvif; + + if (!conf) + goto legacy; + + mvif = mt76_vif_conf_link(mphy->dev, conf->vif, conf); + chandef = mvif->ctx ? &mvif->ctx->def : &mphy->chandef; + band = chandef->chan->band; + + if (is_connac2(mphy->dev)) { + rateidx = ffs(conf->basic_rates) - 1; + goto legacy; + } + + if (beacon) { + struct cfg80211_bitrate_mask *mask; + + mask = &conf->beacon_tx_rate; + + __bitrate_mask_check(he_mcs, HE_SU); + __bitrate_mask_check(vht_mcs, VHT); + __bitrate_mask_check(ht_mcs, HT); + + if (hweight32(mask->control[band].legacy) == 1) { + rateidx = ffs(mask->control[band].legacy) - 1; + goto legacy; + } + } + + mcast_rate = conf->mcast_rate[band]; + if (mcast && mcast_rate > 0) + rateidx = mcast_rate - 1; + else + rateidx = ffs(conf->basic_rates) - 1; + +legacy: + if (band != NL80211_BAND_2GHZ) + offset = 4; + + /* pick the lowest rate for hidden nodes */ + if (rateidx < 0) + rateidx = 0; + + rateidx += offset; + if (rateidx >= ARRAY_SIZE(mt76_rates)) + rateidx = offset; + + rateidx = mt76_rates[rateidx].hw_value; + mode = rateidx >> 8; + rateidx &= GENMASK(7, 0); +out: + return FIELD_PREP(MT_TX_RATE_NSS, nss) | + FIELD_PREP(MT_TX_RATE_IDX, rateidx) | + FIELD_PREP(MT_TX_RATE_MODE, mode); +} +EXPORT_SYMBOL_GPL(mt76_connac2_mac_tx_rate_val); + +static void +mt76_connac2_mac_write_txwi_8023(__le32 *txwi, struct sk_buff *skb, + struct mt76_wcid *wcid) +{ + u8 tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK; + u8 fc_type, fc_stype; + u16 ethertype; + bool wmm = false; + u32 val; + + if (wcid->sta) { + struct ieee80211_sta *sta; + + sta = container_of((void *)wcid, struct ieee80211_sta, drv_priv); + wmm = sta->wme; + } + + val = FIELD_PREP(MT_TXD1_HDR_FORMAT, MT_HDR_FORMAT_802_3) | + FIELD_PREP(MT_TXD1_TID, tid); + + ethertype = get_unaligned_be16(&skb->data[12]); + if (ethertype >= ETH_P_802_3_MIN) + val |= MT_TXD1_ETH_802_3; + + txwi[1] |= cpu_to_le32(val); + + fc_type = IEEE80211_FTYPE_DATA >> 2; + fc_stype = wmm ? IEEE80211_STYPE_QOS_DATA >> 4 : 0; + + val = FIELD_PREP(MT_TXD2_FRAME_TYPE, fc_type) | + FIELD_PREP(MT_TXD2_SUB_TYPE, fc_stype); + + txwi[2] |= cpu_to_le32(val); + + val = FIELD_PREP(MT_TXD7_TYPE, fc_type) | + FIELD_PREP(MT_TXD7_SUB_TYPE, fc_stype); + + txwi[7] |= cpu_to_le32(val); +} + +static void +mt76_connac2_mac_write_txwi_80211(struct mt76_dev *dev, __le32 *txwi, + struct sk_buff *skb, + struct ieee80211_key_conf *key) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + bool multicast = is_multicast_ether_addr(hdr->addr1); + u8 tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK; + __le16 fc = hdr->frame_control; + __le16 sc = hdr->seq_ctrl; + u8 fc_type, fc_stype; + u32 val; + + if (ieee80211_is_action(fc) && + skb->len >= IEEE80211_MIN_ACTION_SIZE(addba_req.capab) && + mgmt->u.action.category == WLAN_CATEGORY_BACK && + mgmt->u.action.action_code == WLAN_ACTION_ADDBA_REQ) { + u16 capab = le16_to_cpu(mgmt->u.action.addba_req.capab); + + txwi[5] |= cpu_to_le32(MT_TXD5_ADD_BA); + tid = (capab >> 2) & IEEE80211_QOS_CTL_TID_MASK; + } else if (ieee80211_is_back_req(hdr->frame_control)) { + struct ieee80211_bar *bar = (struct ieee80211_bar *)hdr; + u16 control = le16_to_cpu(bar->control); + + tid = FIELD_GET(IEEE80211_BAR_CTRL_TID_INFO_MASK, control); + } + + val = FIELD_PREP(MT_TXD1_HDR_FORMAT, MT_HDR_FORMAT_802_11) | + FIELD_PREP(MT_TXD1_HDR_INFO, + ieee80211_get_hdrlen_from_skb(skb) / 2) | + FIELD_PREP(MT_TXD1_TID, tid); + + txwi[1] |= cpu_to_le32(val); + + fc_type = (le16_to_cpu(fc) & IEEE80211_FCTL_FTYPE) >> 2; + fc_stype = (le16_to_cpu(fc) & IEEE80211_FCTL_STYPE) >> 4; + + val = FIELD_PREP(MT_TXD2_FRAME_TYPE, fc_type) | + FIELD_PREP(MT_TXD2_SUB_TYPE, fc_stype) | + FIELD_PREP(MT_TXD2_MULTICAST, multicast); + + if (key && multicast && ieee80211_is_robust_mgmt_frame(skb) && + key->cipher == WLAN_CIPHER_SUITE_AES_CMAC) { + val |= MT_TXD2_BIP; + txwi[3] &= ~cpu_to_le32(MT_TXD3_PROTECT_FRAME); + } + + if (!ieee80211_is_data(fc) || multicast || + info->flags & IEEE80211_TX_CTL_USE_MINRATE) + val |= MT_TXD2_FIX_RATE; + + if (ieee80211_has_morefrags(fc) && ieee80211_is_first_frag(sc)) + val |= FIELD_PREP(MT_TXD2_FRAG, MT_TX_FRAG_FIRST); + else if (ieee80211_has_morefrags(fc) && !ieee80211_is_first_frag(sc)) + val |= FIELD_PREP(MT_TXD2_FRAG, MT_TX_FRAG_MID); + else if (!ieee80211_has_morefrags(fc) && !ieee80211_is_first_frag(sc)) + val |= FIELD_PREP(MT_TXD2_FRAG, MT_TX_FRAG_LAST); + + txwi[2] |= cpu_to_le32(val); + + if (ieee80211_is_beacon(fc)) { + txwi[3] &= ~cpu_to_le32(MT_TXD3_SW_POWER_MGMT); + txwi[3] |= cpu_to_le32(MT_TXD3_REM_TX_COUNT); + } + + if (info->flags & IEEE80211_TX_CTL_INJECTED) { + u16 seqno = le16_to_cpu(sc); + + if (ieee80211_is_back_req(hdr->frame_control)) { + struct ieee80211_bar *bar; + + bar = (struct ieee80211_bar *)skb->data; + seqno = le16_to_cpu(bar->start_seq_num); + } + + val = MT_TXD3_SN_VALID | + FIELD_PREP(MT_TXD3_SEQ, IEEE80211_SEQ_TO_SN(seqno)); + txwi[3] |= cpu_to_le32(val); + txwi[7] &= ~cpu_to_le32(MT_TXD7_HW_AMSDU); + } + + if (mt76_is_mmio(dev)) { + val = FIELD_PREP(MT_TXD7_TYPE, fc_type) | + FIELD_PREP(MT_TXD7_SUB_TYPE, fc_stype); + txwi[7] |= cpu_to_le32(val); + } else { + val = FIELD_PREP(MT_TXD8_L_TYPE, fc_type) | + FIELD_PREP(MT_TXD8_L_SUB_TYPE, fc_stype); + txwi[8] |= cpu_to_le32(val); + } +} + +void mt76_connac2_mac_write_txwi(struct mt76_dev *dev, __le32 *txwi, + struct sk_buff *skb, struct mt76_wcid *wcid, + struct ieee80211_key_conf *key, int pid, + enum mt76_txq_id qid, u32 changed) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + u8 phy_idx = (info->hw_queue & MT_TX_HW_QUEUE_PHY) >> 2; + struct ieee80211_vif *vif = info->control.vif; + struct mt76_phy *mphy = &dev->phy; + u8 p_fmt, q_idx, omac_idx = 0, wmm_idx = 0, band_idx = 0; + u32 val, sz_txd = mt76_is_mmio(dev) ? MT_TXD_SIZE : MT_SDIO_TXD_SIZE; + bool is_8023 = info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP; + bool beacon = !!(changed & (BSS_CHANGED_BEACON | + BSS_CHANGED_BEACON_ENABLED)); + bool inband_disc = !!(changed & (BSS_CHANGED_UNSOL_BCAST_PROBE_RESP | + BSS_CHANGED_FILS_DISCOVERY)); + bool amsdu_en = wcid->amsdu; + + if (vif) { + struct mt76_vif_link *mvif = (struct mt76_vif_link *)vif->drv_priv; + + omac_idx = mvif->omac_idx; + wmm_idx = mvif->wmm_idx; + band_idx = mvif->band_idx; + } + + if (phy_idx && dev->phys[MT_BAND1]) + mphy = dev->phys[MT_BAND1]; + + if (inband_disc) { + p_fmt = MT_TX_TYPE_FW; + q_idx = MT_LMAC_ALTX0; + } else if (beacon) { + p_fmt = MT_TX_TYPE_FW; + q_idx = MT_LMAC_BCN0; + } else if (qid >= MT_TXQ_PSD) { + p_fmt = mt76_is_mmio(dev) ? MT_TX_TYPE_CT : MT_TX_TYPE_SF; + q_idx = MT_LMAC_ALTX0; + } else { + p_fmt = mt76_is_mmio(dev) ? MT_TX_TYPE_CT : MT_TX_TYPE_SF; + q_idx = wmm_idx * MT76_CONNAC_MAX_WMM_SETS + + mt76_connac_lmac_mapping(skb_get_queue_mapping(skb)); + + /* mt7915 WA only counts WED path */ + if (is_mt7915(dev) && mtk_wed_device_active(&dev->mmio.wed)) + wcid->stats.tx_packets++; + } + + val = FIELD_PREP(MT_TXD0_TX_BYTES, skb->len + sz_txd) | + FIELD_PREP(MT_TXD0_PKT_FMT, p_fmt) | + FIELD_PREP(MT_TXD0_Q_IDX, q_idx); + txwi[0] = cpu_to_le32(val); + + val = MT_TXD1_LONG_FORMAT | + FIELD_PREP(MT_TXD1_WLAN_IDX, wcid->idx) | + FIELD_PREP(MT_TXD1_OWN_MAC, omac_idx); + if (!is_connac2(dev)) + val |= MT_TXD1_VTA; + if (phy_idx || band_idx) + val |= MT_TXD1_TGID; + + txwi[1] = cpu_to_le32(val); + txwi[2] = 0; + + val = FIELD_PREP(MT_TXD3_REM_TX_COUNT, 15); + if (!is_connac2(dev)) + val |= MT_TXD3_SW_POWER_MGMT; + if (key) + val |= MT_TXD3_PROTECT_FRAME; + if (info->flags & IEEE80211_TX_CTL_NO_ACK) + val |= MT_TXD3_NO_ACK; + + txwi[3] = cpu_to_le32(val); + txwi[4] = 0; + + val = FIELD_PREP(MT_TXD5_PID, pid); + if (pid >= MT_PACKET_ID_FIRST) { + val |= MT_TXD5_TX_STATUS_HOST; + amsdu_en = 0; + } + + txwi[5] = cpu_to_le32(val); + txwi[6] = 0; + txwi[7] = amsdu_en ? cpu_to_le32(MT_TXD7_HW_AMSDU) : 0; + + if (is_8023) + mt76_connac2_mac_write_txwi_8023(txwi, skb, wcid); + else + mt76_connac2_mac_write_txwi_80211(dev, txwi, skb, key); + + if (txwi[2] & cpu_to_le32(MT_TXD2_FIX_RATE)) { + /* Fixed rata is available just for 802.11 txd */ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + bool multicast = ieee80211_is_data(hdr->frame_control) && + is_multicast_ether_addr(hdr->addr1); + u16 rate = mt76_connac2_mac_tx_rate_val(mphy, + vif ? &vif->bss_conf : NULL, + beacon, multicast); + u32 val = MT_TXD6_FIXED_BW; + + /* hardware won't add HTC for mgmt/ctrl frame */ + txwi[2] |= cpu_to_le32(MT_TXD2_HTC_VLD); + + val |= FIELD_PREP(MT_TXD6_TX_RATE, rate); + txwi[6] |= cpu_to_le32(val); + txwi[3] |= cpu_to_le32(MT_TXD3_BA_DISABLE); + + if (!is_connac2(dev)) { + u8 spe_idx = mt76_connac_spe_idx(mphy->antenna_mask); + + if (!spe_idx) + spe_idx = 24 + phy_idx; + txwi[7] |= cpu_to_le32(FIELD_PREP(MT_TXD7_SPE_IDX, spe_idx)); + } + + txwi[7] &= ~cpu_to_le32(MT_TXD7_HW_AMSDU); + } +} +EXPORT_SYMBOL_GPL(mt76_connac2_mac_write_txwi); + +bool mt76_connac2_mac_fill_txs(struct mt76_dev *dev, struct mt76_wcid *wcid, + __le32 *txs_data) +{ + struct mt76_sta_stats *stats = &wcid->stats; + struct ieee80211_supported_band *sband; + struct mt76_phy *mphy; + struct rate_info rate = {}; + bool cck = false; + u32 txrate, txs, mode, stbc; + + txs = le32_to_cpu(txs_data[0]); + + /* PPDU based reporting */ + if (mtk_wed_device_active(&dev->mmio.wed) && + FIELD_GET(MT_TXS0_TXS_FORMAT, txs) > 1) { + stats->tx_bytes += + le32_get_bits(txs_data[5], MT_TXS5_MPDU_TX_BYTE) - + le32_get_bits(txs_data[7], MT_TXS7_MPDU_RETRY_BYTE); + stats->tx_failed += + le32_get_bits(txs_data[6], MT_TXS6_MPDU_FAIL_CNT); + stats->tx_retries += + le32_get_bits(txs_data[7], MT_TXS7_MPDU_RETRY_CNT); + + if (wcid->sta) { + struct ieee80211_sta *sta; + u8 tid; + + sta = container_of((void *)wcid, struct ieee80211_sta, + drv_priv); + tid = FIELD_GET(MT_TXS0_TID, txs); + + ieee80211_refresh_tx_agg_session_timer(sta, tid); + } + } + + txrate = FIELD_GET(MT_TXS0_TX_RATE, txs); + + rate.mcs = FIELD_GET(MT_TX_RATE_IDX, txrate); + rate.nss = FIELD_GET(MT_TX_RATE_NSS, txrate) + 1; + stbc = FIELD_GET(MT_TX_RATE_STBC, txrate); + + if (stbc && rate.nss > 1) + rate.nss >>= 1; + + if (rate.nss - 1 < ARRAY_SIZE(stats->tx_nss)) + stats->tx_nss[rate.nss - 1]++; + if (rate.mcs < ARRAY_SIZE(stats->tx_mcs)) + stats->tx_mcs[rate.mcs]++; + + mode = FIELD_GET(MT_TX_RATE_MODE, txrate); + switch (mode) { + case MT_PHY_TYPE_CCK: + cck = true; + fallthrough; + case MT_PHY_TYPE_OFDM: + mphy = &dev->phy; + if (wcid->phy_idx == MT_BAND1 && dev->phys[MT_BAND1]) + mphy = dev->phys[MT_BAND1]; + + if (mphy->chandef.chan->band == NL80211_BAND_5GHZ) + sband = &mphy->sband_5g.sband; + else if (mphy->chandef.chan->band == NL80211_BAND_6GHZ) + sband = &mphy->sband_6g.sband; + else + sband = &mphy->sband_2g.sband; + + rate.mcs = mt76_get_rate(mphy->dev, sband, rate.mcs, cck); + rate.legacy = sband->bitrates[rate.mcs].bitrate; + break; + case MT_PHY_TYPE_HT: + case MT_PHY_TYPE_HT_GF: + if (rate.mcs > 31) + return false; + + rate.flags = RATE_INFO_FLAGS_MCS; + if (wcid->rate.flags & RATE_INFO_FLAGS_SHORT_GI) + rate.flags |= RATE_INFO_FLAGS_SHORT_GI; + break; + case MT_PHY_TYPE_VHT: + if (rate.mcs > 9) + return false; + + rate.flags = RATE_INFO_FLAGS_VHT_MCS; + break; + case MT_PHY_TYPE_HE_SU: + case MT_PHY_TYPE_HE_EXT_SU: + case MT_PHY_TYPE_HE_TB: + case MT_PHY_TYPE_HE_MU: + if (rate.mcs > 11) + return false; + + rate.he_gi = wcid->rate.he_gi; + rate.he_dcm = FIELD_GET(MT_TX_RATE_DCM, txrate); + rate.flags = RATE_INFO_FLAGS_HE_MCS; + break; + default: + return false; + } + + stats->tx_mode[mode]++; + + switch (FIELD_GET(MT_TXS0_BW, txs)) { + case IEEE80211_STA_RX_BW_160: + rate.bw = RATE_INFO_BW_160; + stats->tx_bw[3]++; + break; + case IEEE80211_STA_RX_BW_80: + rate.bw = RATE_INFO_BW_80; + stats->tx_bw[2]++; + break; + case IEEE80211_STA_RX_BW_40: + rate.bw = RATE_INFO_BW_40; + stats->tx_bw[1]++; + break; + default: + rate.bw = RATE_INFO_BW_20; + stats->tx_bw[0]++; + break; + } + wcid->rate = rate; + + return true; +} +EXPORT_SYMBOL_GPL(mt76_connac2_mac_fill_txs); + +bool mt76_connac2_mac_add_txs_skb(struct mt76_dev *dev, struct mt76_wcid *wcid, + int pid, __le32 *txs_data) +{ + struct sk_buff_head list; + struct sk_buff *skb; + + if (le32_get_bits(txs_data[0], MT_TXS0_TXS_FORMAT) == MT_TXS_PPDU_FMT) + return false; + + mt76_tx_status_lock(dev, &list); + skb = mt76_tx_status_skb_get(dev, wcid, pid, &list); + if (skb) { + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + + if (!(le32_to_cpu(txs_data[0]) & MT_TXS0_ACK_ERROR_MASK)) + info->flags |= IEEE80211_TX_STAT_ACK; + + info->status.ampdu_len = 1; + info->status.ampdu_ack_len = + !!(info->flags & IEEE80211_TX_STAT_ACK); + info->status.rates[0].idx = -1; + + mt76_connac2_mac_fill_txs(dev, wcid, txs_data); + mt76_tx_status_skb_done(dev, skb, &list); + } + mt76_tx_status_unlock(dev, &list); + + return !!skb; +} +EXPORT_SYMBOL_GPL(mt76_connac2_mac_add_txs_skb); + +static void +mt76_connac2_mac_decode_he_radiotap_ru(struct mt76_rx_status *status, + struct ieee80211_radiotap_he *he, + __le32 *rxv) +{ + u32 ru_h, ru_l; + u8 ru, offs = 0; + + ru_l = le32_get_bits(rxv[0], MT_PRXV_HE_RU_ALLOC_L); + ru_h = le32_get_bits(rxv[1], MT_PRXV_HE_RU_ALLOC_H); + ru = (u8)(ru_l | ru_h << 4); + + status->bw = RATE_INFO_BW_HE_RU; + + switch (ru) { + case 0 ... 36: + status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_26; + offs = ru; + break; + case 37 ... 52: + status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_52; + offs = ru - 37; + break; + case 53 ... 60: + status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_106; + offs = ru - 53; + break; + case 61 ... 64: + status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_242; + offs = ru - 61; + break; + case 65 ... 66: + status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_484; + offs = ru - 65; + break; + case 67: + status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_996; + break; + case 68: + status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_2x996; + break; + } + + he->data1 |= HE_BITS(DATA1_BW_RU_ALLOC_KNOWN); + he->data2 |= HE_BITS(DATA2_RU_OFFSET_KNOWN) | + le16_encode_bits(offs, + IEEE80211_RADIOTAP_HE_DATA2_RU_OFFSET); +} + +static void +mt76_connac2_mac_decode_he_mu_radiotap(struct mt76_dev *dev, struct sk_buff *skb, + __le32 *rxv) +{ + struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb; + static struct ieee80211_radiotap_he_mu mu_known = { + .flags1 = HE_BITS(MU_FLAGS1_SIG_B_MCS_KNOWN) | + HE_BITS(MU_FLAGS1_SIG_B_DCM_KNOWN) | + HE_BITS(MU_FLAGS1_CH1_RU_KNOWN) | + HE_BITS(MU_FLAGS1_SIG_B_SYMS_USERS_KNOWN), + .flags2 = HE_BITS(MU_FLAGS2_BW_FROM_SIG_A_BW_KNOWN), + }; + struct ieee80211_radiotap_he_mu *he_mu; + + if (is_connac2(dev)) { + mu_known.flags1 |= HE_BITS(MU_FLAGS1_SIG_B_COMP_KNOWN); + mu_known.flags2 |= HE_BITS(MU_FLAGS2_PUNC_FROM_SIG_A_BW_KNOWN); + } + + status->flag |= RX_FLAG_RADIOTAP_HE_MU; + + he_mu = skb_push(skb, sizeof(mu_known)); + memcpy(he_mu, &mu_known, sizeof(mu_known)); + +#define MU_PREP(f, v) le16_encode_bits(v, IEEE80211_RADIOTAP_HE_MU_##f) + + he_mu->flags1 |= MU_PREP(FLAGS1_SIG_B_MCS, status->rate_idx); + if (status->he_dcm) + he_mu->flags1 |= MU_PREP(FLAGS1_SIG_B_DCM, status->he_dcm); + + he_mu->flags2 |= MU_PREP(FLAGS2_BW_FROM_SIG_A_BW, status->bw) | + MU_PREP(FLAGS2_SIG_B_SYMS_USERS, + le32_get_bits(rxv[2], MT_CRXV_HE_NUM_USER)); + + he_mu->ru_ch1[0] = le32_get_bits(rxv[3], MT_CRXV_HE_RU0); + + if (status->bw >= RATE_INFO_BW_40) { + he_mu->flags1 |= HE_BITS(MU_FLAGS1_CH2_RU_KNOWN); + he_mu->ru_ch2[0] = + le32_get_bits(rxv[3], MT_CRXV_HE_RU1); + } + + if (status->bw >= RATE_INFO_BW_80) { + he_mu->ru_ch1[1] = + le32_get_bits(rxv[3], MT_CRXV_HE_RU2); + he_mu->ru_ch2[1] = + le32_get_bits(rxv[3], MT_CRXV_HE_RU3); + } +} + +void mt76_connac2_mac_decode_he_radiotap(struct mt76_dev *dev, + struct sk_buff *skb, + __le32 *rxv, u32 mode) +{ + struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb; + static const struct ieee80211_radiotap_he known = { + .data1 = HE_BITS(DATA1_DATA_MCS_KNOWN) | + HE_BITS(DATA1_DATA_DCM_KNOWN) | + HE_BITS(DATA1_STBC_KNOWN) | + HE_BITS(DATA1_CODING_KNOWN) | + HE_BITS(DATA1_LDPC_XSYMSEG_KNOWN) | + HE_BITS(DATA1_DOPPLER_KNOWN) | + HE_BITS(DATA1_SPTL_REUSE_KNOWN) | + HE_BITS(DATA1_BSS_COLOR_KNOWN), + .data2 = HE_BITS(DATA2_GI_KNOWN) | + HE_BITS(DATA2_TXBF_KNOWN) | + HE_BITS(DATA2_PE_DISAMBIG_KNOWN) | + HE_BITS(DATA2_TXOP_KNOWN), + }; + u32 ltf_size = le32_get_bits(rxv[2], MT_CRXV_HE_LTF_SIZE) + 1; + struct ieee80211_radiotap_he *he; + + status->flag |= RX_FLAG_RADIOTAP_HE; + + he = skb_push(skb, sizeof(known)); + memcpy(he, &known, sizeof(known)); + + he->data3 = HE_PREP(DATA3_BSS_COLOR, BSS_COLOR, rxv[14]) | + HE_PREP(DATA3_LDPC_XSYMSEG, LDPC_EXT_SYM, rxv[2]); + he->data4 = HE_PREP(DATA4_SU_MU_SPTL_REUSE, SR_MASK, rxv[11]); + he->data5 = HE_PREP(DATA5_PE_DISAMBIG, PE_DISAMBIG, rxv[2]) | + le16_encode_bits(ltf_size, + IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE); + if (le32_to_cpu(rxv[0]) & MT_PRXV_TXBF) + he->data5 |= HE_BITS(DATA5_TXBF); + he->data6 = HE_PREP(DATA6_TXOP, TXOP_DUR, rxv[14]) | + HE_PREP(DATA6_DOPPLER, DOPPLER, rxv[14]); + + switch (mode) { + case MT_PHY_TYPE_HE_SU: + he->data1 |= HE_BITS(DATA1_FORMAT_SU) | + HE_BITS(DATA1_UL_DL_KNOWN) | + HE_BITS(DATA1_BEAM_CHANGE_KNOWN) | + HE_BITS(DATA1_BW_RU_ALLOC_KNOWN); + + he->data3 |= HE_PREP(DATA3_BEAM_CHANGE, BEAM_CHNG, rxv[14]) | + HE_PREP(DATA3_UL_DL, UPLINK, rxv[2]); + break; + case MT_PHY_TYPE_HE_EXT_SU: + he->data1 |= HE_BITS(DATA1_FORMAT_EXT_SU) | + HE_BITS(DATA1_UL_DL_KNOWN) | + HE_BITS(DATA1_BW_RU_ALLOC_KNOWN); + + he->data3 |= HE_PREP(DATA3_UL_DL, UPLINK, rxv[2]); + break; + case MT_PHY_TYPE_HE_MU: + he->data1 |= HE_BITS(DATA1_FORMAT_MU) | + HE_BITS(DATA1_UL_DL_KNOWN); + + he->data3 |= HE_PREP(DATA3_UL_DL, UPLINK, rxv[2]); + he->data4 |= HE_PREP(DATA4_MU_STA_ID, MU_AID, rxv[7]); + + mt76_connac2_mac_decode_he_radiotap_ru(status, he, rxv); + mt76_connac2_mac_decode_he_mu_radiotap(dev, skb, rxv); + break; + case MT_PHY_TYPE_HE_TB: + he->data1 |= HE_BITS(DATA1_FORMAT_TRIG) | + HE_BITS(DATA1_SPTL_REUSE2_KNOWN) | + HE_BITS(DATA1_SPTL_REUSE3_KNOWN) | + HE_BITS(DATA1_SPTL_REUSE4_KNOWN); + + he->data4 |= HE_PREP(DATA4_TB_SPTL_REUSE1, SR_MASK, rxv[11]) | + HE_PREP(DATA4_TB_SPTL_REUSE2, SR1_MASK, rxv[11]) | + HE_PREP(DATA4_TB_SPTL_REUSE3, SR2_MASK, rxv[11]) | + HE_PREP(DATA4_TB_SPTL_REUSE4, SR3_MASK, rxv[11]); + + mt76_connac2_mac_decode_he_radiotap_ru(status, he, rxv); + break; + default: + break; + } +} +EXPORT_SYMBOL_GPL(mt76_connac2_mac_decode_he_radiotap); + +/* The HW does not translate the mac header to 802.3 for mesh point */ +int mt76_connac2_reverse_frag0_hdr_trans(struct ieee80211_vif *vif, + struct sk_buff *skb, u16 hdr_offset) +{ + struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb; + struct ethhdr *eth_hdr = (struct ethhdr *)(skb->data + hdr_offset); + __le32 *rxd = (__le32 *)skb->data; + struct ieee80211_sta *sta; + struct ieee80211_hdr hdr; + u16 frame_control; + + if (le32_get_bits(rxd[3], MT_RXD3_NORMAL_ADDR_TYPE) != + MT_RXD3_NORMAL_U2M) + return -EINVAL; + + if (!(le32_to_cpu(rxd[1]) & MT_RXD1_NORMAL_GROUP_4)) + return -EINVAL; + + sta = container_of((void *)status->wcid, struct ieee80211_sta, drv_priv); + + /* store the info from RXD and ethhdr to avoid being overridden */ + frame_control = le32_get_bits(rxd[6], MT_RXD6_FRAME_CONTROL); + hdr.frame_control = cpu_to_le16(frame_control); + hdr.seq_ctrl = cpu_to_le16(le32_get_bits(rxd[8], MT_RXD8_SEQ_CTRL)); + hdr.duration_id = 0; + + ether_addr_copy(hdr.addr1, vif->addr); + ether_addr_copy(hdr.addr2, sta->addr); + switch (frame_control & (IEEE80211_FCTL_TODS | + IEEE80211_FCTL_FROMDS)) { + case 0: + ether_addr_copy(hdr.addr3, vif->bss_conf.bssid); + break; + case IEEE80211_FCTL_FROMDS: + ether_addr_copy(hdr.addr3, eth_hdr->h_source); + break; + case IEEE80211_FCTL_TODS: + ether_addr_copy(hdr.addr3, eth_hdr->h_dest); + break; + case IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS: + ether_addr_copy(hdr.addr3, eth_hdr->h_dest); + ether_addr_copy(hdr.addr4, eth_hdr->h_source); + break; + default: + return -EINVAL; + } + + skb_pull(skb, hdr_offset + sizeof(struct ethhdr) - 2); + if (eth_hdr->h_proto == cpu_to_be16(ETH_P_AARP) || + eth_hdr->h_proto == cpu_to_be16(ETH_P_IPX)) + ether_addr_copy(skb_push(skb, ETH_ALEN), bridge_tunnel_header); + else if (be16_to_cpu(eth_hdr->h_proto) >= ETH_P_802_3_MIN) + ether_addr_copy(skb_push(skb, ETH_ALEN), rfc1042_header); + else + skb_pull(skb, 2); + + if (ieee80211_has_order(hdr.frame_control)) + memcpy(skb_push(skb, IEEE80211_HT_CTL_LEN), &rxd[9], + IEEE80211_HT_CTL_LEN); + if (ieee80211_is_data_qos(hdr.frame_control)) { + __le16 qos_ctrl; + + qos_ctrl = cpu_to_le16(le32_get_bits(rxd[8], MT_RXD8_QOS_CTL)); + memcpy(skb_push(skb, IEEE80211_QOS_CTL_LEN), &qos_ctrl, + IEEE80211_QOS_CTL_LEN); + } + + if (ieee80211_has_a4(hdr.frame_control)) + memcpy(skb_push(skb, sizeof(hdr)), &hdr, sizeof(hdr)); + else + memcpy(skb_push(skb, sizeof(hdr) - 6), &hdr, sizeof(hdr) - 6); + + return 0; +} +EXPORT_SYMBOL_GPL(mt76_connac2_reverse_frag0_hdr_trans); + +int mt76_connac2_mac_fill_rx_rate(struct mt76_dev *dev, + struct mt76_rx_status *status, + struct ieee80211_supported_band *sband, + __le32 *rxv, u8 *mode) +{ + u32 v0, v2; + u8 stbc, gi, bw, dcm, nss; + int i, idx; + bool cck = false; + + v0 = le32_to_cpu(rxv[0]); + v2 = le32_to_cpu(rxv[2]); + + idx = i = FIELD_GET(MT_PRXV_TX_RATE, v0); + nss = FIELD_GET(MT_PRXV_NSTS, v0) + 1; + + if (!is_mt7915(dev)) { + stbc = FIELD_GET(MT_PRXV_HT_STBC, v0); + gi = FIELD_GET(MT_PRXV_HT_SGI, v0); + *mode = FIELD_GET(MT_PRXV_TX_MODE, v0); + if (is_connac2(dev)) + dcm = !!(idx & MT_PRXV_TX_DCM); + else + dcm = FIELD_GET(MT_PRXV_DCM, v0); + bw = FIELD_GET(MT_PRXV_FRAME_MODE, v0); + } else { + stbc = FIELD_GET(MT_CRXV_HT_STBC, v2); + gi = FIELD_GET(MT_CRXV_HT_SHORT_GI, v2); + *mode = FIELD_GET(MT_CRXV_TX_MODE, v2); + dcm = !!(idx & GENMASK(3, 0) & MT_PRXV_TX_DCM); + bw = FIELD_GET(MT_CRXV_FRAME_MODE, v2); + } + + /* the hardware reports NSTS; report the data NSS for STBC frames */ + if (stbc && nss > 1) + nss >>= 1; + + switch (*mode) { + case MT_PHY_TYPE_CCK: + cck = true; + fallthrough; + case MT_PHY_TYPE_OFDM: + i = mt76_get_rate(dev, sband, i, cck); + break; + case MT_PHY_TYPE_HT_GF: + case MT_PHY_TYPE_HT: + status->encoding = RX_ENC_HT; + if (gi) + status->enc_flags |= RX_ENC_FLAG_SHORT_GI; + if (i > 31) + return -EINVAL; + break; + case MT_PHY_TYPE_VHT: + status->nss = nss; + status->encoding = RX_ENC_VHT; + if (gi) + status->enc_flags |= RX_ENC_FLAG_SHORT_GI; + if (i > 11) + return -EINVAL; + break; + case MT_PHY_TYPE_HE_MU: + case MT_PHY_TYPE_HE_SU: + case MT_PHY_TYPE_HE_EXT_SU: + case MT_PHY_TYPE_HE_TB: + status->nss = nss; + status->encoding = RX_ENC_HE; + i &= GENMASK(3, 0); + + if (gi <= NL80211_RATE_INFO_HE_GI_3_2) + status->he_gi = gi; + + status->he_dcm = dcm; + break; + default: + return -EINVAL; + } + status->rate_idx = i; + + switch (bw) { + case IEEE80211_STA_RX_BW_20: + break; + case IEEE80211_STA_RX_BW_40: + if (*mode == MT_PHY_TYPE_HE_EXT_SU && + (idx & MT_PRXV_TX_ER_SU_106T)) { + status->bw = RATE_INFO_BW_HE_RU; + status->he_ru = + NL80211_RATE_INFO_HE_RU_ALLOC_106; + } else { + status->bw = RATE_INFO_BW_40; + } + break; + case IEEE80211_STA_RX_BW_80: + status->bw = RATE_INFO_BW_80; + break; + case IEEE80211_STA_RX_BW_160: + status->bw = RATE_INFO_BW_160; + break; + default: + return -EINVAL; + } + + status->enc_flags |= RX_ENC_FLAG_STBC_MASK * stbc; + if (*mode < MT_PHY_TYPE_HE_SU && gi) + status->enc_flags |= RX_ENC_FLAG_SHORT_GI; + + return 0; +} +EXPORT_SYMBOL_GPL(mt76_connac2_mac_fill_rx_rate); + +void mt76_connac2_tx_check_aggr(struct ieee80211_sta *sta, __le32 *txwi) +{ + struct mt76_wcid *wcid; + u16 fc, tid; + u32 val; + + if (!sta || + !(sta->deflink.ht_cap.ht_supported || sta->deflink.he_cap.has_he)) + return; + + tid = le32_get_bits(txwi[1], MT_TXD1_TID); + + val = le32_to_cpu(txwi[2]); + fc = FIELD_GET(MT_TXD2_FRAME_TYPE, val) << 2 | + FIELD_GET(MT_TXD2_SUB_TYPE, val) << 4; + if (unlikely(fc != (IEEE80211_FTYPE_DATA | IEEE80211_STYPE_QOS_DATA))) + return; + + wcid = (struct mt76_wcid *)sta->drv_priv; + if (!test_and_set_bit(tid, &wcid->ampdu_state)) { + if (ieee80211_start_tx_ba_session(sta, tid, 0)) + clear_bit(tid, &wcid->ampdu_state); + } +} +EXPORT_SYMBOL_GPL(mt76_connac2_tx_check_aggr); + +void mt76_connac2_txwi_free(struct mt76_dev *dev, struct mt76_txwi_cache *t, + struct ieee80211_sta *sta, + struct list_head *free_list) +{ + struct mt76_wcid *wcid; + __le32 *txwi; + u16 wcid_idx; + + mt76_connac_txp_skb_unmap(dev, t); + if (!t->skb) + goto out; + + txwi = (__le32 *)mt76_get_txwi_ptr(dev, t); + if (sta) { + wcid = (struct mt76_wcid *)sta->drv_priv; + wcid_idx = wcid->idx; + } else { + wcid_idx = le32_get_bits(txwi[1], MT_TXD1_WLAN_IDX); + wcid = __mt76_wcid_ptr(dev, wcid_idx); + + if (wcid && wcid->sta) { + sta = container_of((void *)wcid, struct ieee80211_sta, + drv_priv); + mt76_wcid_add_poll(dev, wcid); + } + } + + if (sta && likely(t->skb->protocol != cpu_to_be16(ETH_P_PAE))) + mt76_connac2_tx_check_aggr(sta, txwi); + + __mt76_tx_complete_skb(dev, wcid_idx, t->skb, free_list); +out: + t->skb = NULL; + mt76_put_txwi(dev, t); +} +EXPORT_SYMBOL_GPL(mt76_connac2_txwi_free); + +void mt76_connac2_tx_token_put(struct mt76_dev *dev) +{ + struct mt76_txwi_cache *txwi; + int id; + + spin_lock_bh(&dev->token_lock); + idr_for_each_entry(&dev->token, txwi, id) { + mt76_connac2_txwi_free(dev, txwi, NULL, NULL); + dev->token_count--; + } + spin_unlock_bh(&dev->token_lock); + idr_destroy(&dev->token); + + for (id = 0; id < __MT_MAX_BAND; id++) { + struct mt76_phy *phy = dev->phys[id]; + if (phy) + atomic_set(&phy->mgmt_tx_pending, 0); + } +} +EXPORT_SYMBOL_GPL(mt76_connac2_tx_token_put); diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c new file mode 100644 index 000000000..2f925d22b --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c @@ -0,0 +1,3568 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2020 MediaTek Inc. */ + +#include +#include "mt76_connac2_mac.h" +#include "mt76_connac_mcu.h" +#include "mt792x_regs.h" + +int mt76_connac_mcu_start_firmware(struct mt76_dev *dev, u32 addr, u32 option) +{ + struct { + __le32 option; + __le32 addr; + } req = { + .option = cpu_to_le32(option), + .addr = cpu_to_le32(addr), + }; + + return mt76_mcu_send_msg(dev, MCU_CMD(FW_START_REQ), &req, + sizeof(req), true); +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_start_firmware); + +int mt76_connac_mcu_patch_sem_ctrl(struct mt76_dev *dev, bool get) +{ + u32 op = get ? PATCH_SEM_GET : PATCH_SEM_RELEASE; + struct { + __le32 op; + } req = { + .op = cpu_to_le32(op), + }; + + return mt76_mcu_send_msg(dev, MCU_CMD(PATCH_SEM_CONTROL), + &req, sizeof(req), true); +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_patch_sem_ctrl); + +int mt76_connac_mcu_start_patch(struct mt76_dev *dev) +{ + struct { + u8 check_crc; + u8 reserved[3]; + } req = { + .check_crc = 0, + }; + + return mt76_mcu_send_msg(dev, MCU_CMD(PATCH_FINISH_REQ), + &req, sizeof(req), true); +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_start_patch); + +#define MCU_PATCH_ADDRESS 0x200000 + +int mt76_connac_mcu_init_download(struct mt76_dev *dev, u32 addr, u32 len, + u32 mode) +{ + struct { + __le32 addr; + __le32 len; + __le32 mode; + } req = { + .addr = cpu_to_le32(addr), + .len = cpu_to_le32(len), + .mode = cpu_to_le32(mode), + }; + int cmd; + + if ((!is_connac_v1(dev) && addr == MCU_PATCH_ADDRESS) || + (is_connac2(dev) && addr == 0x900000) || + ((is_mt7925(dev) || is_mt7927(dev)) && (addr == 0x900000 || addr == 0xe0002800)) || + (is_mt7928(dev) && (addr == 0x900000 || addr == 0xe0002000)) || + (is_mt799x(dev) && addr == 0x900000)) + cmd = MCU_CMD(PATCH_START_REQ); + else + cmd = MCU_CMD(TARGET_ADDRESS_LEN_REQ); + + return mt76_mcu_send_msg(dev, cmd, &req, sizeof(req), true); +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_init_download); + +int mt76_connac_cb_mcu_patch_sem_ctrl(struct mt76_dev *dev, bool get) +{ + u8 op = get ? PATCH_SEM_GET : PATCH_SEM_RELEASE; + struct { + u8 op; + u8 reserved[3]; + } req = { + .op = op, + }; + + return mt76_mcu_send_msg(dev, MCU_CMD(CB_PATCH_SEM_CONTROL), + &req, sizeof(req), true); +} +EXPORT_SYMBOL_GPL(mt76_connac_cb_mcu_patch_sem_ctrl); + +int mt76_connac_cb_mcu_start_patch(struct mt76_dev *dev) +{ + struct { + __le32 reserved; + } req = { + .reserved = 0, + }; + + return mt76_mcu_send_msg(dev, MCU_CMD(CB_PATCH_FINISH_REQ), + &req, sizeof(req), true); +} +EXPORT_SYMBOL_GPL(mt76_connac_cb_mcu_start_patch); + +int mt76_connac_cb_mcu_init_download(struct mt76_dev *dev, u32 len) +{ + struct { + __le32 addr; + __le32 len; + } req = { + .addr = 0, /* addr is meaningless for cbmcu fwdl */ + .len = cpu_to_le32(len), + }; + + return mt76_mcu_send_msg(dev, MCU_CMD(CB_PATCH_START_REQ), &req, sizeof(req), true); +} +EXPORT_SYMBOL_GPL(mt76_connac_cb_mcu_init_download); + +int mt76_connac_mcu_set_channel_domain(struct mt76_phy *phy) +{ + int len, i, n_max_channels, n_2ch = 0, n_5ch = 0, n_6ch = 0; + struct mt76_connac_mcu_channel_domain { + u8 alpha2[4]; /* regulatory_request.alpha2 */ + u8 bw_2g; /* BW_20_40M 0 + * BW_20M 1 + * BW_20_40_80M 2 + * BW_20_40_80_160M 3 + * BW_20_40_80_8080M 4 + */ + u8 bw_5g; + u8 bw_6g; + u8 pad; + u8 n_2ch; + u8 n_5ch; + u8 n_6ch; + u8 pad2; + } __packed hdr = { + .bw_2g = 0, + .bw_5g = 3, /* BW_20_40_80_160M */ + .bw_6g = 3, + }; + struct mt76_connac_mcu_chan { + __le16 hw_value; + __le16 pad; + __le32 flags; + } __packed channel; + struct mt76_dev *dev = phy->dev; + struct ieee80211_channel *chan; + struct sk_buff *skb; + + n_max_channels = phy->sband_2g.sband.n_channels + + phy->sband_5g.sband.n_channels + + phy->sband_6g.sband.n_channels; + len = sizeof(hdr) + n_max_channels * sizeof(channel); + + skb = mt76_mcu_msg_alloc(dev, NULL, len); + if (!skb) + return -ENOMEM; + + skb_reserve(skb, sizeof(hdr)); + + for (i = 0; i < phy->sband_2g.sband.n_channels; i++) { + chan = &phy->sband_2g.sband.channels[i]; + if (chan->flags & IEEE80211_CHAN_DISABLED) + continue; + + channel.hw_value = cpu_to_le16(chan->hw_value); + channel.flags = cpu_to_le32(chan->flags); + channel.pad = 0; + + skb_put_data(skb, &channel, sizeof(channel)); + n_2ch++; + } + for (i = 0; i < phy->sband_5g.sband.n_channels; i++) { + chan = &phy->sband_5g.sband.channels[i]; + if (chan->flags & IEEE80211_CHAN_DISABLED) + continue; + + channel.hw_value = cpu_to_le16(chan->hw_value); + channel.flags = cpu_to_le32(chan->flags); + channel.pad = 0; + + skb_put_data(skb, &channel, sizeof(channel)); + n_5ch++; + } + for (i = 0; i < phy->sband_6g.sband.n_channels; i++) { + chan = &phy->sband_6g.sband.channels[i]; + if (chan->flags & IEEE80211_CHAN_DISABLED) + continue; + + channel.hw_value = cpu_to_le16(chan->hw_value); + channel.flags = cpu_to_le32(chan->flags); + channel.pad = 0; + + skb_put_data(skb, &channel, sizeof(channel)); + n_6ch++; + } + + BUILD_BUG_ON(sizeof(dev->alpha2) > sizeof(hdr.alpha2)); + memcpy(hdr.alpha2, dev->alpha2, sizeof(dev->alpha2)); + hdr.n_2ch = n_2ch; + hdr.n_5ch = n_5ch; + hdr.n_6ch = n_6ch; + + memcpy(__skb_push(skb, sizeof(hdr)), &hdr, sizeof(hdr)); + + return mt76_mcu_skb_send_msg(dev, skb, MCU_CE_CMD(SET_CHAN_DOMAIN), + false); +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_set_channel_domain); + +int mt76_connac_mcu_set_mac_enable(struct mt76_dev *dev, int band, bool enable, + bool hdr_trans) +{ + struct { + u8 enable; + u8 band; + u8 rsv[2]; + } __packed req_mac = { + .enable = enable, + .band = band, + }; + + return mt76_mcu_send_msg(dev, MCU_EXT_CMD(MAC_INIT_CTRL), &req_mac, + sizeof(req_mac), true); +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_set_mac_enable); + +int mt76_connac_mcu_set_vif_ps(struct mt76_dev *dev, struct ieee80211_vif *vif) +{ + struct mt76_vif_link *mvif = (struct mt76_vif_link *)vif->drv_priv; + struct { + u8 bss_idx; + u8 ps_state; /* 0: device awake + * 1: static power save + * 2: dynamic power saving + */ + } req = { + .bss_idx = mvif->idx, + .ps_state = vif->cfg.ps ? 2 : 0, + }; + + if (vif->type != NL80211_IFTYPE_STATION) + return -EOPNOTSUPP; + + return mt76_mcu_send_msg(dev, MCU_CE_CMD(SET_PS_PROFILE), + &req, sizeof(req), false); +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_set_vif_ps); + +int mt76_connac_mcu_set_rts_thresh(struct mt76_dev *dev, u32 val, u8 band) +{ + struct { + u8 prot_idx; + u8 band; + u8 rsv[2]; + __le32 len_thresh; + __le32 pkt_thresh; + } __packed req = { + .prot_idx = 1, + .band = band, + .len_thresh = cpu_to_le32(val), + .pkt_thresh = cpu_to_le32(0x2), + }; + + return mt76_mcu_send_msg(dev, MCU_EXT_CMD(PROTECT_CTRL), &req, + sizeof(req), true); +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_set_rts_thresh); + +void mt76_connac_mcu_beacon_loss_iter(void *priv, u8 *mac, + struct ieee80211_vif *vif) +{ + struct mt76_vif_link *mvif = (struct mt76_vif_link *)vif->drv_priv; + struct mt76_connac_beacon_loss_event *event = priv; + + if (mvif->idx != event->bss_idx) + return; + + if (!(vif->driver_flags & IEEE80211_VIF_BEACON_FILTER)) + return; + + ieee80211_beacon_loss(vif); +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_beacon_loss_iter); + +struct tlv * +mt76_connac_mcu_add_nested_tlv(struct sk_buff *skb, int tag, int len, + void *sta_ntlv, void *sta_wtbl) +{ + struct sta_ntlv_hdr *ntlv_hdr = sta_ntlv; + struct tlv *sta_hdr = sta_wtbl; + struct tlv *ptlv, tlv = { + .tag = cpu_to_le16(tag), + .len = cpu_to_le16(len), + }; + u16 ntlv; + + ptlv = skb_put_zero(skb, len); + memcpy(ptlv, &tlv, sizeof(tlv)); + + ntlv = le16_to_cpu(ntlv_hdr->tlv_num); + ntlv_hdr->tlv_num = cpu_to_le16(ntlv + 1); + + if (sta_hdr) { + len += le16_to_cpu(sta_hdr->len); + sta_hdr->len = cpu_to_le16(len); + } + + return ptlv; +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_add_nested_tlv); + +struct sk_buff * +__mt76_connac_mcu_alloc_sta_req(struct mt76_dev *dev, struct mt76_vif_link *mvif, + struct mt76_wcid *wcid, int len) +{ + struct sta_req_hdr hdr = { + .bss_idx = mvif->idx, + .muar_idx = wcid ? mvif->omac_idx : 0, + .is_tlv_append = 1, + }; + struct sk_buff *skb; + + if (wcid && !wcid->sta && !wcid->sta_disabled) + hdr.muar_idx = 0xe; + + mt76_connac_mcu_get_wlan_idx(dev, wcid, &hdr.wlan_idx_lo, + &hdr.wlan_idx_hi); + skb = __mt76_mcu_msg_alloc(dev, NULL, len, len, GFP_ATOMIC); + if (!skb) + return ERR_PTR(-ENOMEM); + + skb_put_data(skb, &hdr, sizeof(hdr)); + + return skb; +} +EXPORT_SYMBOL_GPL(__mt76_connac_mcu_alloc_sta_req); + +struct wtbl_req_hdr * +mt76_connac_mcu_alloc_wtbl_req(struct mt76_dev *dev, struct mt76_wcid *wcid, + int cmd, void *sta_wtbl, struct sk_buff **skb) +{ + struct tlv *sta_hdr = sta_wtbl; + struct wtbl_req_hdr hdr = { + .operation = cmd, + }; + struct sk_buff *nskb = *skb; + + mt76_connac_mcu_get_wlan_idx(dev, wcid, &hdr.wlan_idx_lo, + &hdr.wlan_idx_hi); + if (!nskb) { + nskb = mt76_mcu_msg_alloc(dev, NULL, + MT76_CONNAC_WTBL_UPDATE_MAX_SIZE); + if (!nskb) + return ERR_PTR(-ENOMEM); + + *skb = nskb; + } + + if (sta_hdr) + le16_add_cpu(&sta_hdr->len, sizeof(hdr)); + + return skb_put_data(nskb, &hdr, sizeof(hdr)); +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_alloc_wtbl_req); + +void mt76_connac_mcu_bss_omac_tlv(struct sk_buff *skb, + struct ieee80211_vif *vif) +{ + struct mt76_vif_link *mvif = (struct mt76_vif_link *)vif->drv_priv; + u8 omac_idx = mvif->omac_idx; + struct bss_info_omac *omac; + struct tlv *tlv; + u32 type = 0; + + switch (vif->type) { + case NL80211_IFTYPE_MONITOR: + case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_AP: + if (vif->p2p) + type = CONNECTION_P2P_GO; + else + type = CONNECTION_INFRA_AP; + break; + case NL80211_IFTYPE_STATION: + if (vif->p2p) + type = CONNECTION_P2P_GC; + else + type = CONNECTION_INFRA_STA; + break; + case NL80211_IFTYPE_ADHOC: + type = CONNECTION_IBSS_ADHOC; + break; + default: + WARN_ON(1); + break; + } + + tlv = mt76_connac_mcu_add_tlv(skb, BSS_INFO_OMAC, sizeof(*omac)); + + omac = (struct bss_info_omac *)tlv; + omac->conn_type = cpu_to_le32(type); + omac->omac_idx = mvif->omac_idx; + omac->band_idx = mvif->band_idx; + omac->hw_bss_idx = omac_idx > EXT_BSSID_START ? HW_BSSID_0 : omac_idx; +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_bss_omac_tlv); + +void mt76_connac_mcu_sta_basic_tlv(struct mt76_dev *dev, struct sk_buff *skb, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_link_sta *link_sta, + int conn_state, bool newly) +{ + struct ieee80211_vif *vif = link_conf->vif; + struct sta_rec_basic *basic; + struct tlv *tlv; + int conn_type; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_BASIC, sizeof(*basic)); + + basic = (struct sta_rec_basic *)tlv; + basic->extra_info = cpu_to_le16(EXTRA_INFO_VER); + + if (newly && conn_state != CONN_STATE_DISCONNECT) + basic->extra_info |= cpu_to_le16(EXTRA_INFO_NEW); + basic->conn_state = conn_state; + + if (!link_sta) { + basic->conn_type = cpu_to_le32(CONNECTION_INFRA_BC); + + if (vif->type == NL80211_IFTYPE_STATION && + !is_zero_ether_addr(link_conf->bssid)) { + memcpy(basic->peer_addr, link_conf->bssid, ETH_ALEN); + basic->aid = cpu_to_le16(vif->cfg.aid); + } else { + eth_broadcast_addr(basic->peer_addr); + } + return; + } + + switch (vif->type) { + case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_AP: + if (vif->p2p && !is_connac2(dev)) + conn_type = CONNECTION_P2P_GC; + else + conn_type = CONNECTION_INFRA_STA; + basic->conn_type = cpu_to_le32(conn_type); + basic->aid = cpu_to_le16(link_sta->sta->aid); + break; + case NL80211_IFTYPE_STATION: + if (vif->p2p && !is_connac2(dev)) + conn_type = CONNECTION_P2P_GO; + else + conn_type = CONNECTION_INFRA_AP; + basic->conn_type = cpu_to_le32(conn_type); + basic->aid = cpu_to_le16(vif->cfg.aid); + break; + case NL80211_IFTYPE_ADHOC: + basic->conn_type = cpu_to_le32(CONNECTION_IBSS_ADHOC); + basic->aid = cpu_to_le16(link_sta->sta->aid); + break; + case NL80211_IFTYPE_NAN: + case NL80211_IFTYPE_NAN_DATA: + basic->conn_type = cpu_to_le32(CONNECTION_NAN); + break; + default: + WARN_ON(1); + break; + } + + memcpy(basic->peer_addr, link_sta->addr, ETH_ALEN); + basic->qos = link_sta->sta->wme; +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_sta_basic_tlv); + +void mt76_connac_mcu_sta_uapsd(struct sk_buff *skb, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct sta_rec_uapsd *uapsd; + struct tlv *tlv; + + if (vif->type != NL80211_IFTYPE_AP || !sta->wme) + return; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_APPS, sizeof(*uapsd)); + uapsd = (struct sta_rec_uapsd *)tlv; + + if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO) { + uapsd->dac_map |= BIT(3); + uapsd->tac_map |= BIT(3); + } + if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VI) { + uapsd->dac_map |= BIT(2); + uapsd->tac_map |= BIT(2); + } + if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BE) { + uapsd->dac_map |= BIT(1); + uapsd->tac_map |= BIT(1); + } + if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BK) { + uapsd->dac_map |= BIT(0); + uapsd->tac_map |= BIT(0); + } + uapsd->max_sp = sta->max_sp; +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_sta_uapsd); + +void mt76_connac_mcu_wtbl_hdr_trans_tlv(struct sk_buff *skb, + struct ieee80211_vif *vif, + struct mt76_wcid *wcid, + void *sta_wtbl, void *wtbl_tlv) +{ + struct wtbl_hdr_trans *htr; + struct tlv *tlv; + + tlv = mt76_connac_mcu_add_nested_tlv(skb, WTBL_HDR_TRANS, + sizeof(*htr), + wtbl_tlv, sta_wtbl); + htr = (struct wtbl_hdr_trans *)tlv; + htr->no_rx_trans = true; + + if (vif->type == NL80211_IFTYPE_STATION) + htr->to_ds = true; + else + htr->from_ds = true; + + if (!wcid) + return; + + htr->no_rx_trans = !test_bit(MT_WCID_FLAG_HDR_TRANS, &wcid->flags); + if (test_bit(MT_WCID_FLAG_4ADDR, &wcid->flags)) { + htr->to_ds = true; + htr->from_ds = true; + } + + if (test_bit(MT_WCID_FLAG_TDLS_PEER, &wcid->flags)) { + htr->to_ds = false; + htr->from_ds = false; + } +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_wtbl_hdr_trans_tlv); + +int mt76_connac_mcu_sta_update_hdr_trans(struct mt76_dev *dev, + struct ieee80211_vif *vif, + struct mt76_wcid *wcid, int cmd) +{ + struct mt76_vif_link *mvif = (struct mt76_vif_link *)vif->drv_priv; + struct wtbl_req_hdr *wtbl_hdr; + struct tlv *sta_wtbl; + struct sk_buff *skb; + + skb = mt76_connac_mcu_alloc_sta_req(dev, mvif, wcid); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + sta_wtbl = mt76_connac_mcu_add_tlv(skb, STA_REC_WTBL, + sizeof(struct tlv)); + + wtbl_hdr = mt76_connac_mcu_alloc_wtbl_req(dev, wcid, WTBL_SET, + sta_wtbl, &skb); + if (IS_ERR(wtbl_hdr)) + return PTR_ERR(wtbl_hdr); + + mt76_connac_mcu_wtbl_hdr_trans_tlv(skb, vif, wcid, sta_wtbl, wtbl_hdr); + + return mt76_mcu_skb_send_msg(dev, skb, cmd, true); +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_sta_update_hdr_trans); + +int mt76_connac_mcu_wtbl_update_hdr_trans(struct mt76_dev *dev, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mt76_wcid *wcid = (struct mt76_wcid *)sta->drv_priv; + struct wtbl_req_hdr *wtbl_hdr; + struct sk_buff *skb = NULL; + + wtbl_hdr = mt76_connac_mcu_alloc_wtbl_req(dev, wcid, WTBL_SET, NULL, + &skb); + if (IS_ERR(wtbl_hdr)) + return PTR_ERR(wtbl_hdr); + + mt76_connac_mcu_wtbl_hdr_trans_tlv(skb, vif, wcid, NULL, wtbl_hdr); + + return mt76_mcu_skb_send_msg(dev, skb, MCU_EXT_CMD(WTBL_UPDATE), true); +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_wtbl_update_hdr_trans); + +void mt76_connac_mcu_wtbl_generic_tlv(struct mt76_dev *dev, + struct sk_buff *skb, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + void *sta_wtbl, void *wtbl_tlv) +{ + struct mt76_vif_link *mvif = (struct mt76_vif_link *)vif->drv_priv; + struct wtbl_generic *generic; + struct wtbl_rx *rx; + struct wtbl_spe *spe; + struct tlv *tlv; + + tlv = mt76_connac_mcu_add_nested_tlv(skb, WTBL_GENERIC, + sizeof(*generic), + wtbl_tlv, sta_wtbl); + + generic = (struct wtbl_generic *)tlv; + + if (sta) { + if (vif->type == NL80211_IFTYPE_STATION) + generic->partial_aid = cpu_to_le16(vif->cfg.aid); + else + generic->partial_aid = cpu_to_le16(sta->aid); + memcpy(generic->peer_addr, sta->addr, ETH_ALEN); + generic->muar_idx = mvif->omac_idx; + generic->qos = sta->wme; + } else { + if (!is_connac_v1(dev) && vif->type == NL80211_IFTYPE_STATION) + memcpy(generic->peer_addr, vif->bss_conf.bssid, + ETH_ALEN); + else + eth_broadcast_addr(generic->peer_addr); + + generic->muar_idx = 0xe; + } + + tlv = mt76_connac_mcu_add_nested_tlv(skb, WTBL_RX, sizeof(*rx), + wtbl_tlv, sta_wtbl); + + rx = (struct wtbl_rx *)tlv; + rx->rca1 = sta ? vif->type != NL80211_IFTYPE_AP : 1; + rx->rca2 = 1; + rx->rv = 1; + + if (!is_connac_v1(dev)) + return; + + tlv = mt76_connac_mcu_add_nested_tlv(skb, WTBL_SPE, sizeof(*spe), + wtbl_tlv, sta_wtbl); + spe = (struct wtbl_spe *)tlv; + spe->spe_idx = 24; +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_wtbl_generic_tlv); + +static void +mt76_connac_mcu_sta_amsdu_tlv(struct sk_buff *skb, struct ieee80211_sta *sta, + struct ieee80211_vif *vif) +{ + struct mt76_wcid *wcid = (struct mt76_wcid *)sta->drv_priv; + struct sta_rec_amsdu *amsdu; + struct tlv *tlv; + + if (vif->type != NL80211_IFTYPE_AP && + vif->type != NL80211_IFTYPE_STATION) + return; + + if (!sta->deflink.agg.max_amsdu_len) + return; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HW_AMSDU, sizeof(*amsdu)); + amsdu = (struct sta_rec_amsdu *)tlv; + amsdu->max_amsdu_num = 8; + amsdu->amsdu_en = true; + amsdu->max_mpdu_size = sta->deflink.agg.max_amsdu_len >= + IEEE80211_MAX_MPDU_LEN_VHT_7991; + + wcid->amsdu = true; +} + +#define HE_PHY(p, c) u8_get_bits(c, IEEE80211_HE_PHY_##p) +#define HE_MAC(m, c) u8_get_bits(c, IEEE80211_HE_MAC_##m) +static void +mt76_connac_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_sta *sta) +{ + struct ieee80211_sta_he_cap *he_cap = &sta->deflink.he_cap; + struct ieee80211_he_cap_elem *elem = &he_cap->he_cap_elem; + struct sta_rec_he *he; + struct tlv *tlv; + u32 cap = 0; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HE, sizeof(*he)); + + he = (struct sta_rec_he *)tlv; + + if (elem->mac_cap_info[0] & IEEE80211_HE_MAC_CAP0_HTC_HE) + cap |= STA_REC_HE_CAP_HTC; + + if (elem->mac_cap_info[2] & IEEE80211_HE_MAC_CAP2_BSR) + cap |= STA_REC_HE_CAP_BSR; + + if (elem->mac_cap_info[3] & IEEE80211_HE_MAC_CAP3_OMI_CONTROL) + cap |= STA_REC_HE_CAP_OM; + + if (elem->mac_cap_info[4] & IEEE80211_HE_MAC_CAP4_AMSDU_IN_AMPDU) + cap |= STA_REC_HE_CAP_AMSDU_IN_AMPDU; + + if (elem->mac_cap_info[4] & IEEE80211_HE_MAC_CAP4_BQR) + cap |= STA_REC_HE_CAP_BQR; + + if (elem->phy_cap_info[0] & + (IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_RU_MAPPING_IN_2G | + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_RU_MAPPING_IN_5G)) + cap |= STA_REC_HE_CAP_BW20_RU242_SUPPORT; + + if (elem->phy_cap_info[1] & + IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD) + cap |= STA_REC_HE_CAP_LDPC; + + if (elem->phy_cap_info[1] & + IEEE80211_HE_PHY_CAP1_HE_LTF_AND_GI_FOR_HE_PPDUS_0_8US) + cap |= STA_REC_HE_CAP_SU_PPDU_1LTF_8US_GI; + + if (elem->phy_cap_info[2] & + IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US) + cap |= STA_REC_HE_CAP_NDP_4LTF_3DOT2MS_GI; + + if (elem->phy_cap_info[2] & + IEEE80211_HE_PHY_CAP2_STBC_TX_UNDER_80MHZ) + cap |= STA_REC_HE_CAP_LE_EQ_80M_TX_STBC; + + if (elem->phy_cap_info[2] & + IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ) + cap |= STA_REC_HE_CAP_LE_EQ_80M_RX_STBC; + + if (elem->phy_cap_info[6] & + IEEE80211_HE_PHY_CAP6_PARTIAL_BW_EXT_RANGE) + cap |= STA_REC_HE_CAP_PARTIAL_BW_EXT_RANGE; + + if (elem->phy_cap_info[7] & + IEEE80211_HE_PHY_CAP7_HE_SU_MU_PPDU_4XLTF_AND_08_US_GI) + cap |= STA_REC_HE_CAP_SU_MU_PPDU_4LTF_8US_GI; + + if (elem->phy_cap_info[7] & + IEEE80211_HE_PHY_CAP7_STBC_TX_ABOVE_80MHZ) + cap |= STA_REC_HE_CAP_GT_80M_TX_STBC; + + if (elem->phy_cap_info[7] & + IEEE80211_HE_PHY_CAP7_STBC_RX_ABOVE_80MHZ) + cap |= STA_REC_HE_CAP_GT_80M_RX_STBC; + + if (elem->phy_cap_info[8] & + IEEE80211_HE_PHY_CAP8_HE_ER_SU_PPDU_4XLTF_AND_08_US_GI) + cap |= STA_REC_HE_CAP_ER_SU_PPDU_4LTF_8US_GI; + + if (elem->phy_cap_info[8] & + IEEE80211_HE_PHY_CAP8_HE_ER_SU_1XLTF_AND_08_US_GI) + cap |= STA_REC_HE_CAP_ER_SU_PPDU_1LTF_8US_GI; + + if (elem->phy_cap_info[9] & + IEEE80211_HE_PHY_CAP9_NON_TRIGGERED_CQI_FEEDBACK) + cap |= STA_REC_HE_CAP_TRIG_CQI_FK; + + if (elem->phy_cap_info[9] & + IEEE80211_HE_PHY_CAP9_TX_1024_QAM_LESS_THAN_242_TONE_RU) + cap |= STA_REC_HE_CAP_TX_1024QAM_UNDER_RU242; + + if (elem->phy_cap_info[9] & + IEEE80211_HE_PHY_CAP9_RX_1024_QAM_LESS_THAN_242_TONE_RU) + cap |= STA_REC_HE_CAP_RX_1024QAM_UNDER_RU242; + + he->he_cap = cpu_to_le32(cap); + + switch (sta->deflink.bandwidth) { + case IEEE80211_STA_RX_BW_160: + if (elem->phy_cap_info[0] & + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G) + he->max_nss_mcs[CMD_HE_MCS_BW8080] = + he_cap->he_mcs_nss_supp.rx_mcs_80p80; + + he->max_nss_mcs[CMD_HE_MCS_BW160] = + he_cap->he_mcs_nss_supp.rx_mcs_160; + fallthrough; + default: + he->max_nss_mcs[CMD_HE_MCS_BW80] = + he_cap->he_mcs_nss_supp.rx_mcs_80; + break; + } + + he->t_frame_dur = + HE_MAC(CAP1_TF_MAC_PAD_DUR_MASK, elem->mac_cap_info[1]); + he->max_ampdu_exp = + HE_MAC(CAP3_MAX_AMPDU_LEN_EXP_MASK, elem->mac_cap_info[3]); + + he->bw_set = + HE_PHY(CAP0_CHANNEL_WIDTH_SET_MASK, elem->phy_cap_info[0]); + he->device_class = + HE_PHY(CAP1_DEVICE_CLASS_A, elem->phy_cap_info[1]); + he->punc_pream_rx = + HE_PHY(CAP1_PREAMBLE_PUNC_RX_MASK, elem->phy_cap_info[1]); + + he->dcm_tx_mode = + HE_PHY(CAP3_DCM_MAX_CONST_TX_MASK, elem->phy_cap_info[3]); + he->dcm_tx_max_nss = + HE_PHY(CAP3_DCM_MAX_TX_NSS_2, elem->phy_cap_info[3]); + he->dcm_rx_mode = + HE_PHY(CAP3_DCM_MAX_CONST_RX_MASK, elem->phy_cap_info[3]); + he->dcm_rx_max_nss = + HE_PHY(CAP3_DCM_MAX_RX_NSS_2, elem->phy_cap_info[3]); + he->dcm_max_ru = + HE_PHY(CAP8_DCM_MAX_RU_MASK, elem->phy_cap_info[8]); + + he->pkt_ext = 2; +} + +void +mt76_connac_mcu_sta_he_tlv_v2(struct sk_buff *skb, struct ieee80211_sta *sta) +{ + struct ieee80211_sta_he_cap *he_cap = &sta->deflink.he_cap; + struct ieee80211_he_cap_elem *elem = &he_cap->he_cap_elem; + struct sta_rec_he_v2 *he; + struct tlv *tlv; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HE_V2, sizeof(*he)); + + he = (struct sta_rec_he_v2 *)tlv; + memcpy(he->he_phy_cap, elem->phy_cap_info, sizeof(he->he_phy_cap)); + memcpy(he->he_mac_cap, elem->mac_cap_info, sizeof(he->he_mac_cap)); + + switch (sta->deflink.bandwidth) { + case IEEE80211_STA_RX_BW_160: + if (elem->phy_cap_info[0] & + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G) + he->max_nss_mcs[CMD_HE_MCS_BW8080] = + he_cap->he_mcs_nss_supp.rx_mcs_80p80; + + he->max_nss_mcs[CMD_HE_MCS_BW160] = + he_cap->he_mcs_nss_supp.rx_mcs_160; + fallthrough; + default: + he->max_nss_mcs[CMD_HE_MCS_BW80] = + he_cap->he_mcs_nss_supp.rx_mcs_80; + break; + } + + he->pkt_ext = IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_16US; +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_sta_he_tlv_v2); + +u8 +mt76_connac_get_phy_mode_v2(struct mt76_phy *mphy, struct ieee80211_vif *vif, + enum nl80211_band band, + struct ieee80211_link_sta *link_sta) +{ + struct ieee80211_sta_ht_cap *ht_cap; + struct ieee80211_sta_vht_cap *vht_cap; + const struct ieee80211_sta_he_cap *he_cap; + const struct ieee80211_sta_eht_cap *eht_cap; + u8 mode = 0; + + if (link_sta) { + ht_cap = &link_sta->ht_cap; + vht_cap = &link_sta->vht_cap; + he_cap = &link_sta->he_cap; + eht_cap = &link_sta->eht_cap; + } else { + struct ieee80211_supported_band *sband; + + sband = mphy->hw->wiphy->bands[band]; + ht_cap = &sband->ht_cap; + vht_cap = &sband->vht_cap; + he_cap = ieee80211_get_he_iftype_cap(sband, vif->type); + eht_cap = ieee80211_get_eht_iftype_cap(sband, vif->type); + } + + if (band == NL80211_BAND_2GHZ) { + mode |= PHY_TYPE_BIT_HR_DSSS | PHY_TYPE_BIT_ERP; + + if (ht_cap->ht_supported) + mode |= PHY_TYPE_BIT_HT; + + if (he_cap && he_cap->has_he) + mode |= PHY_TYPE_BIT_HE; + + if (eht_cap && eht_cap->has_eht) + mode |= PHY_TYPE_BIT_BE; + } else if (band == NL80211_BAND_5GHZ || band == NL80211_BAND_6GHZ) { + mode |= PHY_TYPE_BIT_OFDM; + + if (ht_cap->ht_supported) + mode |= PHY_TYPE_BIT_HT; + + if (vht_cap->vht_supported) + mode |= PHY_TYPE_BIT_VHT; + + if (he_cap && he_cap->has_he) + mode |= PHY_TYPE_BIT_HE; + + if (eht_cap && eht_cap->has_eht) + mode |= PHY_TYPE_BIT_BE; + } + + return mode; +} +EXPORT_SYMBOL_GPL(mt76_connac_get_phy_mode_v2); + +void mt76_connac_mcu_sta_tlv(struct mt76_phy *mphy, struct sk_buff *skb, + struct ieee80211_sta *sta, + struct ieee80211_vif *vif, + u8 rcpi, u8 sta_state) +{ + struct mt76_vif_link *mvif = (struct mt76_vif_link *)vif->drv_priv; + struct cfg80211_chan_def *chandef = mvif->ctx ? + &mvif->ctx->def : &mphy->chandef; + enum nl80211_band band = chandef->chan->band; + struct mt76_dev *dev = mphy->dev; + struct sta_rec_ra_info *ra_info; + struct sta_rec_state *state; + struct sta_rec_phy *phy; + struct tlv *tlv; + u16 supp_rates; + + /* starec ht */ + if (sta->deflink.ht_cap.ht_supported) { + struct sta_rec_ht *ht; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HT, sizeof(*ht)); + ht = (struct sta_rec_ht *)tlv; + ht->ht_cap = cpu_to_le16(sta->deflink.ht_cap.cap); + } + + /* starec vht */ + if (sta->deflink.vht_cap.vht_supported) { + struct sta_rec_vht *vht; + int len; + + len = is_connac2(dev) ? sizeof(*vht) : sizeof(*vht) - 4; + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_VHT, len); + vht = (struct sta_rec_vht *)tlv; + vht->vht_cap = cpu_to_le32(sta->deflink.vht_cap.cap); + vht->vht_rx_mcs_map = sta->deflink.vht_cap.vht_mcs.rx_mcs_map; + vht->vht_tx_mcs_map = sta->deflink.vht_cap.vht_mcs.tx_mcs_map; + } + + /* starec uapsd */ + mt76_connac_mcu_sta_uapsd(skb, vif, sta); + + if (!is_connac2(dev)) + return; + + if (sta->deflink.ht_cap.ht_supported || sta->deflink.he_cap.has_he) + mt76_connac_mcu_sta_amsdu_tlv(skb, sta, vif); + + /* starec he */ + if (sta->deflink.he_cap.has_he) { + mt76_connac_mcu_sta_he_tlv(skb, sta); + mt76_connac_mcu_sta_he_tlv_v2(skb, sta); + if (band == NL80211_BAND_6GHZ && + sta_state == MT76_STA_INFO_STATE_ASSOC) { + struct sta_rec_he_6g_capa *he_6g_capa; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HE_6G, + sizeof(*he_6g_capa)); + he_6g_capa = (struct sta_rec_he_6g_capa *)tlv; + he_6g_capa->capa = sta->deflink.he_6ghz_capa.capa; + } + } + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_PHY, sizeof(*phy)); + phy = (struct sta_rec_phy *)tlv; + phy->phy_type = mt76_connac_get_phy_mode_v2(mphy, vif, band, + &sta->deflink); + phy->basic_rate = cpu_to_le16((u16)vif->bss_conf.basic_rates); + phy->rcpi = rcpi; + phy->ampdu = FIELD_PREP(IEEE80211_HT_AMPDU_PARM_FACTOR, + sta->deflink.ht_cap.ampdu_factor) | + FIELD_PREP(IEEE80211_HT_AMPDU_PARM_DENSITY, + sta->deflink.ht_cap.ampdu_density); + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_RA, sizeof(*ra_info)); + ra_info = (struct sta_rec_ra_info *)tlv; + + supp_rates = sta->deflink.supp_rates[band]; + if (band == NL80211_BAND_2GHZ) + supp_rates = FIELD_PREP(RA_LEGACY_OFDM, supp_rates >> 4) | + FIELD_PREP(RA_LEGACY_CCK, supp_rates & 0xf); + else + supp_rates = FIELD_PREP(RA_LEGACY_OFDM, supp_rates); + + ra_info->legacy = cpu_to_le16(supp_rates); + + if (sta->deflink.ht_cap.ht_supported) + memcpy(ra_info->rx_mcs_bitmask, + sta->deflink.ht_cap.mcs.rx_mask, + HT_MCS_MASK_NUM); + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_STATE, sizeof(*state)); + state = (struct sta_rec_state *)tlv; + state->state = sta_state; + + if (sta->deflink.vht_cap.vht_supported) { + state->vht_opmode = sta->deflink.bandwidth; + state->vht_opmode |= (sta->deflink.rx_nss - 1) << + IEEE80211_OPMODE_NOTIF_RX_NSS_SHIFT; + } +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_sta_tlv); + +void mt76_connac_mcu_wtbl_smps_tlv(struct sk_buff *skb, + struct ieee80211_sta *sta, + void *sta_wtbl, void *wtbl_tlv) +{ + struct wtbl_smps *smps; + struct tlv *tlv; + + tlv = mt76_connac_mcu_add_nested_tlv(skb, WTBL_SMPS, sizeof(*smps), + wtbl_tlv, sta_wtbl); + smps = (struct wtbl_smps *)tlv; + smps->smps = (sta->deflink.smps_mode == IEEE80211_SMPS_DYNAMIC); +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_wtbl_smps_tlv); + +void mt76_connac_mcu_wtbl_ht_tlv(struct mt76_dev *dev, struct sk_buff *skb, + struct ieee80211_sta *sta, void *sta_wtbl, + void *wtbl_tlv, bool ht_ldpc, bool vht_ldpc) +{ + struct wtbl_ht *ht = NULL; + struct tlv *tlv; + u32 flags = 0; + + if (sta->deflink.ht_cap.ht_supported || sta->deflink.he_6ghz_capa.capa) { + tlv = mt76_connac_mcu_add_nested_tlv(skb, WTBL_HT, sizeof(*ht), + wtbl_tlv, sta_wtbl); + ht = (struct wtbl_ht *)tlv; + ht->ldpc = ht_ldpc && + !!(sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_LDPC_CODING); + + if (sta->deflink.ht_cap.ht_supported) { + ht->af = sta->deflink.ht_cap.ampdu_factor; + ht->mm = sta->deflink.ht_cap.ampdu_density; + } else { + ht->af = le16_get_bits(sta->deflink.he_6ghz_capa.capa, + IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP); + ht->mm = le16_get_bits(sta->deflink.he_6ghz_capa.capa, + IEEE80211_HE_6GHZ_CAP_MIN_MPDU_START); + } + + ht->ht = true; + } + + if (sta->deflink.vht_cap.vht_supported || sta->deflink.he_6ghz_capa.capa) { + struct wtbl_vht *vht; + u8 af; + + tlv = mt76_connac_mcu_add_nested_tlv(skb, WTBL_VHT, + sizeof(*vht), wtbl_tlv, + sta_wtbl); + vht = (struct wtbl_vht *)tlv; + vht->ldpc = vht_ldpc && + !!(sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_RXLDPC); + vht->vht = true; + + af = FIELD_GET(IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK, + sta->deflink.vht_cap.cap); + if (ht) + ht->af = max(ht->af, af); + } + + mt76_connac_mcu_wtbl_smps_tlv(skb, sta, sta_wtbl, wtbl_tlv); + + if (is_connac_v1(dev) && sta->deflink.ht_cap.ht_supported) { + /* sgi */ + u32 msk = MT_WTBL_W5_SHORT_GI_20 | MT_WTBL_W5_SHORT_GI_40 | + MT_WTBL_W5_SHORT_GI_80 | MT_WTBL_W5_SHORT_GI_160; + struct wtbl_raw *raw; + + tlv = mt76_connac_mcu_add_nested_tlv(skb, WTBL_RAW_DATA, + sizeof(*raw), wtbl_tlv, + sta_wtbl); + + if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_SGI_20) + flags |= MT_WTBL_W5_SHORT_GI_20; + if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_SGI_40) + flags |= MT_WTBL_W5_SHORT_GI_40; + + if (sta->deflink.vht_cap.vht_supported) { + if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_SHORT_GI_80) + flags |= MT_WTBL_W5_SHORT_GI_80; + if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_SHORT_GI_160) + flags |= MT_WTBL_W5_SHORT_GI_160; + } + raw = (struct wtbl_raw *)tlv; + raw->val = cpu_to_le32(flags); + raw->msk = cpu_to_le32(~msk); + raw->wtbl_idx = 1; + raw->dw = 5; + } +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_wtbl_ht_tlv); + +int mt76_connac_mcu_sta_cmd(struct mt76_phy *phy, + struct mt76_sta_cmd_info *info) +{ + struct mt76_vif_link *mvif = (struct mt76_vif_link *)info->vif->drv_priv; + struct ieee80211_link_sta *link_sta; + struct mt76_dev *dev = phy->dev; + struct wtbl_req_hdr *wtbl_hdr; + struct tlv *sta_wtbl; + struct sk_buff *skb; + int conn_state; + + if (!info->link_conf) + info->link_conf = &info->vif->bss_conf; + + skb = mt76_connac_mcu_alloc_sta_req(dev, mvif, info->wcid); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + conn_state = info->enable ? CONN_STATE_PORT_SECURE : + CONN_STATE_DISCONNECT; + link_sta = info->sta ? &info->sta->deflink : NULL; + if (info->sta || !info->offload_fw) + mt76_connac_mcu_sta_basic_tlv(dev, skb, info->link_conf, + link_sta, conn_state, + info->newly); + if (info->sta && info->enable) + mt76_connac_mcu_sta_tlv(phy, skb, info->sta, + info->vif, info->rcpi, + info->state); + + sta_wtbl = mt76_connac_mcu_add_tlv(skb, STA_REC_WTBL, + sizeof(struct tlv)); + + wtbl_hdr = mt76_connac_mcu_alloc_wtbl_req(dev, info->wcid, + WTBL_RESET_AND_SET, + sta_wtbl, &skb); + if (IS_ERR(wtbl_hdr)) + return PTR_ERR(wtbl_hdr); + + if (info->enable) { + mt76_connac_mcu_wtbl_generic_tlv(dev, skb, info->vif, + info->sta, sta_wtbl, + wtbl_hdr); + mt76_connac_mcu_wtbl_hdr_trans_tlv(skb, info->vif, info->wcid, + sta_wtbl, wtbl_hdr); + if (info->sta) + mt76_connac_mcu_wtbl_ht_tlv(dev, skb, info->sta, + sta_wtbl, wtbl_hdr, + true, true); + } + + return mt76_mcu_skb_send_msg(dev, skb, info->cmd, true); +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_sta_cmd); + +void mt76_connac_mcu_wtbl_ba_tlv(struct mt76_dev *dev, struct sk_buff *skb, + struct ieee80211_ampdu_params *params, + bool enable, bool tx, void *sta_wtbl, + void *wtbl_tlv) +{ + struct wtbl_ba *ba; + struct tlv *tlv; + + tlv = mt76_connac_mcu_add_nested_tlv(skb, WTBL_BA, sizeof(*ba), + wtbl_tlv, sta_wtbl); + + ba = (struct wtbl_ba *)tlv; + ba->tid = params->tid; + + if (tx) { + ba->ba_type = MT_BA_TYPE_ORIGINATOR; + ba->sn = enable ? cpu_to_le16(params->ssn) : 0; + ba->ba_winsize = enable ? cpu_to_le16(params->buf_size) : 0; + ba->ba_en = enable; + } else { + memcpy(ba->peer_addr, params->sta->addr, ETH_ALEN); + ba->ba_type = MT_BA_TYPE_RECIPIENT; + ba->rst_ba_tid = params->tid; + ba->rst_ba_sel = RST_BA_MAC_TID_MATCH; + ba->rst_ba_sb = 1; + } + + if (!is_connac_v1(dev)) { + ba->ba_winsize = enable ? cpu_to_le16(params->buf_size) : 0; + return; + } + + if (enable && tx) { + static const u8 ba_range[] = { 4, 8, 12, 24, 36, 48, 54, 64 }; + int i; + + for (i = 7; i > 0; i--) { + if (params->buf_size >= ba_range[i]) + break; + } + ba->ba_winsize_idx = i; + } +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_wtbl_ba_tlv); + +int mt76_connac_mcu_uni_add_dev(struct mt76_phy *phy, + struct ieee80211_bss_conf *bss_conf, + struct mt76_vif_link *mvif, + struct mt76_wcid *wcid, + bool enable) +{ + struct mt76_dev *dev = phy->dev; + struct { + struct { + u8 omac_idx; + u8 band_idx; + __le16 pad; + } __packed hdr; + struct req_tlv { + __le16 tag; + __le16 len; + u8 active; + u8 link_idx; /* not link_id */ + u8 omac_addr[ETH_ALEN]; + } __packed tlv; + } dev_req = { + .hdr = { + .omac_idx = mvif->omac_idx, + .band_idx = mvif->band_idx, + }, + .tlv = { + .tag = cpu_to_le16(DEV_INFO_ACTIVE), + .len = cpu_to_le16(sizeof(struct req_tlv)), + .active = enable, + .link_idx = mvif->link_idx, + }, + }; + struct { + struct { + u8 bss_idx; + u8 pad[3]; + } __packed hdr; + struct mt76_connac_bss_basic_tlv basic; + } basic_req = { + .hdr = { + .bss_idx = mvif->idx, + }, + .basic = { + .tag = cpu_to_le16(UNI_BSS_INFO_BASIC), + .len = cpu_to_le16(sizeof(struct mt76_connac_bss_basic_tlv)), + .omac_idx = mvif->omac_idx, + .band_idx = mvif->band_idx, + .wmm_idx = mvif->wmm_idx, + .active = enable, + .bmc_tx_wlan_idx = cpu_to_le16(wcid->idx), + .sta_idx = cpu_to_le16(wcid->idx), + .conn_state = 1, + .link_idx = mvif->link_idx, + }, + }; + int err, idx, cmd, len; + void *data; + + switch (bss_conf->vif->type) { + case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_MONITOR: + case NL80211_IFTYPE_AP: + basic_req.basic.conn_type = cpu_to_le32(CONNECTION_INFRA_AP); + break; + case NL80211_IFTYPE_STATION: + basic_req.basic.conn_type = cpu_to_le32(CONNECTION_INFRA_STA); + break; + case NL80211_IFTYPE_P2P_DEVICE: + basic_req.basic.conn_type = cpu_to_le32(CONNECTION_P2P_GO); + break; + case NL80211_IFTYPE_ADHOC: + basic_req.basic.conn_type = cpu_to_le32(CONNECTION_IBSS_ADHOC); + break; + case NL80211_IFTYPE_NAN: + case NL80211_IFTYPE_NAN_DATA: + basic_req.basic.conn_type = cpu_to_le32(CONNECTION_NAN); + basic_req.basic.conn_state = !enable; + break; + default: + WARN_ON(1); + break; + } + + idx = mvif->omac_idx > EXT_BSSID_START ? HW_BSSID_0 : mvif->omac_idx; + basic_req.basic.hw_bss_idx = idx; + + memcpy(dev_req.tlv.omac_addr, bss_conf->addr, ETH_ALEN); + + cmd = enable ? MCU_UNI_CMD(DEV_INFO_UPDATE) : MCU_UNI_CMD(BSS_INFO_UPDATE); + data = enable ? (void *)&dev_req : (void *)&basic_req; + len = enable ? sizeof(dev_req) : sizeof(basic_req); + + err = mt76_mcu_send_msg(dev, cmd, data, len, true); + if (err && cmd == MCU_UNI_CMD(BSS_INFO_UPDATE)) + err = mt76_connac_mcu_bss_deact_err(dev, err, enable); + + if (err < 0) + return err; + + cmd = enable ? MCU_UNI_CMD(BSS_INFO_UPDATE) : MCU_UNI_CMD(DEV_INFO_UPDATE); + data = enable ? (void *)&basic_req : (void *)&dev_req; + len = enable ? sizeof(basic_req) : sizeof(dev_req); + + err = mt76_mcu_send_msg(dev, cmd, data, len, true); + if (err && cmd == MCU_UNI_CMD(BSS_INFO_UPDATE)) + err = mt76_connac_mcu_bss_deact_err(dev, err, enable); + + return err; +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_uni_add_dev); + +void mt76_connac_mcu_sta_ba_tlv(struct sk_buff *skb, + struct ieee80211_ampdu_params *params, + bool enable, bool tx) +{ + struct sta_rec_ba *ba; + struct tlv *tlv; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_BA, sizeof(*ba)); + + ba = (struct sta_rec_ba *)tlv; + ba->ba_type = tx ? MT_BA_TYPE_ORIGINATOR : MT_BA_TYPE_RECIPIENT; + ba->winsize = cpu_to_le16(params->buf_size); + ba->ssn = cpu_to_le16(params->ssn); + ba->ba_en = enable << params->tid; + ba->amsdu = params->amsdu; + ba->tid = params->tid; +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_sta_ba_tlv); + +int mt76_connac_mcu_sta_wed_update(struct mt76_dev *dev, struct sk_buff *skb) +{ + if (!mt76_is_mmio(dev)) + return 0; + + if (!mtk_wed_device_active(&dev->mmio.wed)) + return 0; + + return mtk_wed_device_update_msg(&dev->mmio.wed, WED_WO_STA_REC, + skb->data, skb->len); +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_sta_wed_update); + +int mt76_connac_mcu_sta_ba(struct mt76_dev *dev, struct mt76_vif_link *mvif, + struct ieee80211_ampdu_params *params, + int cmd, bool enable, bool tx) +{ + struct mt76_wcid *wcid = (struct mt76_wcid *)params->sta->drv_priv; + struct wtbl_req_hdr *wtbl_hdr; + struct tlv *sta_wtbl; + struct sk_buff *skb; + int ret; + + skb = mt76_connac_mcu_alloc_sta_req(dev, mvif, wcid); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + sta_wtbl = mt76_connac_mcu_add_tlv(skb, STA_REC_WTBL, + sizeof(struct tlv)); + + wtbl_hdr = mt76_connac_mcu_alloc_wtbl_req(dev, wcid, WTBL_SET, + sta_wtbl, &skb); + if (IS_ERR(wtbl_hdr)) + return PTR_ERR(wtbl_hdr); + + mt76_connac_mcu_wtbl_ba_tlv(dev, skb, params, enable, tx, sta_wtbl, + wtbl_hdr); + + ret = mt76_connac_mcu_sta_wed_update(dev, skb); + if (ret) { + dev_kfree_skb(skb); + return ret; + } + + ret = mt76_mcu_skb_send_msg(dev, skb, cmd, true); + if (ret) + return ret; + + skb = mt76_connac_mcu_alloc_sta_req(dev, mvif, wcid); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + mt76_connac_mcu_sta_ba_tlv(skb, params, enable, tx); + + ret = mt76_connac_mcu_sta_wed_update(dev, skb); + if (ret) { + dev_kfree_skb(skb); + return ret; + } + + return mt76_mcu_skb_send_msg(dev, skb, cmd, true); +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_sta_ba); + +u8 mt76_connac_get_phy_mode(struct mt76_phy *phy, struct ieee80211_vif *vif, + enum nl80211_band band, + struct ieee80211_link_sta *link_sta) +{ + struct mt76_dev *dev = phy->dev; + const struct ieee80211_sta_he_cap *he_cap; + struct ieee80211_sta_vht_cap *vht_cap; + struct ieee80211_sta_ht_cap *ht_cap; + u8 mode = 0; + + if (is_connac_v1(dev)) + return 0x38; + + if (link_sta) { + ht_cap = &link_sta->ht_cap; + vht_cap = &link_sta->vht_cap; + he_cap = &link_sta->he_cap; + } else { + struct ieee80211_supported_band *sband; + + sband = phy->hw->wiphy->bands[band]; + ht_cap = &sband->ht_cap; + vht_cap = &sband->vht_cap; + he_cap = ieee80211_get_he_iftype_cap(sband, vif->type); + } + + if (band == NL80211_BAND_2GHZ) { + mode |= PHY_MODE_B | PHY_MODE_G; + + if (ht_cap->ht_supported) + mode |= PHY_MODE_GN; + + if (he_cap && he_cap->has_he) + mode |= PHY_MODE_AX_24G; + } else if (band == NL80211_BAND_5GHZ) { + mode |= PHY_MODE_A; + + if (ht_cap->ht_supported) + mode |= PHY_MODE_AN; + + if (vht_cap->vht_supported) + mode |= PHY_MODE_AC; + + if (he_cap && he_cap->has_he) + mode |= PHY_MODE_AX_5G; + } else if (band == NL80211_BAND_6GHZ) { + mode |= PHY_MODE_A | PHY_MODE_AN | + PHY_MODE_AC | PHY_MODE_AX_5G; + } + + return mode; +} +EXPORT_SYMBOL_GPL(mt76_connac_get_phy_mode); + +u8 mt76_connac_get_phy_mode_ext(struct mt76_phy *phy, struct ieee80211_bss_conf *conf, + enum nl80211_band band) +{ + const struct ieee80211_sta_eht_cap *eht_cap; + struct ieee80211_supported_band *sband; + u8 mode = 0; + + if (band == NL80211_BAND_6GHZ) + mode |= PHY_MODE_AX_6G; + + sband = phy->hw->wiphy->bands[band]; + eht_cap = ieee80211_get_eht_iftype_cap(sband, conf->vif->type); + + if (!eht_cap || !eht_cap->has_eht || !conf->eht_support) + return mode; + + switch (band) { + case NL80211_BAND_6GHZ: + mode |= PHY_MODE_BE_6G; + break; + case NL80211_BAND_5GHZ: + mode |= PHY_MODE_BE_5G; + break; + case NL80211_BAND_2GHZ: + mode |= PHY_MODE_BE_24G; + break; + default: + break; + } + + return mode; +} +EXPORT_SYMBOL_GPL(mt76_connac_get_phy_mode_ext); + +const struct ieee80211_sta_he_cap * +mt76_connac_get_he_phy_cap(struct mt76_phy *phy, struct ieee80211_vif *vif) +{ + struct mt76_vif_link *mvif = (struct mt76_vif_link *)vif->drv_priv; + struct cfg80211_chan_def *chandef = mvif->ctx ? + &mvif->ctx->def : &phy->chandef; + enum nl80211_band band = chandef->chan->band; + struct ieee80211_supported_band *sband; + + sband = phy->hw->wiphy->bands[band]; + + return ieee80211_get_he_iftype_cap(sband, vif->type); +} +EXPORT_SYMBOL_GPL(mt76_connac_get_he_phy_cap); + +const struct ieee80211_sta_eht_cap * +mt76_connac_get_eht_phy_cap(struct mt76_phy *phy, struct ieee80211_vif *vif) +{ + enum nl80211_band band = phy->chandef.chan->band; + struct ieee80211_supported_band *sband; + + sband = phy->hw->wiphy->bands[band]; + + return ieee80211_get_eht_iftype_cap(sband, vif->type); +} +EXPORT_SYMBOL_GPL(mt76_connac_get_eht_phy_cap); + +#define DEFAULT_HE_PE_DURATION 4 +#define DEFAULT_HE_DURATION_RTS_THRES 1023 +static void +mt76_connac_mcu_uni_bss_he_tlv(struct mt76_phy *phy, struct ieee80211_vif *vif, + struct tlv *tlv) +{ + const struct ieee80211_sta_he_cap *cap; + struct bss_info_uni_he *he; + + cap = mt76_connac_get_he_phy_cap(phy, vif); + if (!cap) + return; + + he = (struct bss_info_uni_he *)tlv; + he->he_pe_duration = vif->bss_conf.htc_trig_based_pkt_ext; + if (!he->he_pe_duration) + he->he_pe_duration = DEFAULT_HE_PE_DURATION; + + he->he_rts_thres = cpu_to_le16(vif->bss_conf.frame_time_rts_th); + if (!he->he_rts_thres) + he->he_rts_thres = cpu_to_le16(DEFAULT_HE_DURATION_RTS_THRES); + + he->max_nss_mcs[CMD_HE_MCS_BW80] = cap->he_mcs_nss_supp.tx_mcs_80; + he->max_nss_mcs[CMD_HE_MCS_BW160] = cap->he_mcs_nss_supp.tx_mcs_160; + he->max_nss_mcs[CMD_HE_MCS_BW8080] = cap->he_mcs_nss_supp.tx_mcs_80p80; +} + +int mt76_connac_mcu_uni_set_chctx(struct mt76_phy *phy, struct mt76_vif_link *mvif, + struct ieee80211_chanctx_conf *ctx) +{ + struct cfg80211_chan_def *chandef = ctx ? &ctx->def : &phy->chandef; + int freq1 = chandef->center_freq1, freq2 = chandef->center_freq2; + enum nl80211_band band = chandef->chan->band; + struct mt76_dev *mdev = phy->dev; + struct { + struct { + u8 bss_idx; + u8 pad[3]; + } __packed hdr; + struct rlm_tlv { + __le16 tag; + __le16 len; + u8 control_channel; + u8 center_chan; + u8 center_chan2; + u8 bw; + u8 tx_streams; + u8 rx_streams; + u8 short_st; + u8 ht_op_info; + u8 sco; + u8 band; + u8 pad[2]; + } __packed rlm; + } __packed rlm_req = { + .hdr = { + .bss_idx = mvif->idx, + }, + .rlm = { + .tag = cpu_to_le16(UNI_BSS_INFO_RLM), + .len = cpu_to_le16(sizeof(struct rlm_tlv)), + .control_channel = chandef->chan->hw_value, + .center_chan = ieee80211_frequency_to_channel(freq1), + .center_chan2 = ieee80211_frequency_to_channel(freq2), + .tx_streams = hweight8(phy->antenna_mask), + .ht_op_info = 4, /* set HT 40M allowed */ + .rx_streams = phy->chainmask, + .short_st = true, + .band = band, + }, + }; + + switch (chandef->width) { + case NL80211_CHAN_WIDTH_40: + rlm_req.rlm.bw = CMD_CBW_40MHZ; + break; + case NL80211_CHAN_WIDTH_80: + rlm_req.rlm.bw = CMD_CBW_80MHZ; + break; + case NL80211_CHAN_WIDTH_80P80: + rlm_req.rlm.bw = CMD_CBW_8080MHZ; + break; + case NL80211_CHAN_WIDTH_160: + rlm_req.rlm.bw = CMD_CBW_160MHZ; + break; + case NL80211_CHAN_WIDTH_5: + rlm_req.rlm.bw = CMD_CBW_5MHZ; + break; + case NL80211_CHAN_WIDTH_10: + rlm_req.rlm.bw = CMD_CBW_10MHZ; + break; + case NL80211_CHAN_WIDTH_20_NOHT: + case NL80211_CHAN_WIDTH_20: + default: + rlm_req.rlm.bw = CMD_CBW_20MHZ; + rlm_req.rlm.ht_op_info = 0; + break; + } + + if (rlm_req.rlm.control_channel < rlm_req.rlm.center_chan) + rlm_req.rlm.sco = 1; /* SCA */ + else if (rlm_req.rlm.control_channel > rlm_req.rlm.center_chan) + rlm_req.rlm.sco = 3; /* SCB */ + + return mt76_mcu_send_msg(mdev, MCU_UNI_CMD(BSS_INFO_UPDATE), &rlm_req, + sizeof(rlm_req), true); +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_uni_set_chctx); + +int mt76_connac_mcu_uni_add_bss(struct mt76_phy *phy, + struct ieee80211_vif *vif, + struct mt76_wcid *wcid, + bool enable, + struct ieee80211_chanctx_conf *ctx) +{ + struct mt76_vif_link *mvif = (struct mt76_vif_link *)vif->drv_priv; + struct cfg80211_chan_def *chandef = ctx ? &ctx->def : &phy->chandef; + enum nl80211_band band = chandef->chan->band; + struct mt76_dev *mdev = phy->dev; + struct { + struct { + u8 bss_idx; + u8 pad[3]; + } __packed hdr; + struct mt76_connac_bss_basic_tlv basic; + struct mt76_connac_bss_qos_tlv qos; + } basic_req = { + .hdr = { + .bss_idx = mvif->idx, + }, + .basic = { + .tag = cpu_to_le16(UNI_BSS_INFO_BASIC), + .len = cpu_to_le16(sizeof(struct mt76_connac_bss_basic_tlv)), + .bcn_interval = cpu_to_le16(vif->bss_conf.beacon_int), + .dtim_period = vif->bss_conf.dtim_period, + .omac_idx = mvif->omac_idx, + .band_idx = mvif->band_idx, + .wmm_idx = mvif->wmm_idx, + .active = true, /* keep bss deactivated */ + .phymode = mt76_connac_get_phy_mode(phy, vif, band, NULL), + }, + .qos = { + .tag = cpu_to_le16(UNI_BSS_INFO_QBSS), + .len = cpu_to_le16(sizeof(struct mt76_connac_bss_qos_tlv)), + .qos = vif->bss_conf.qos, + }, + }; + int err, conn_type; + u8 idx, basic_phy; + + idx = mvif->omac_idx > EXT_BSSID_START ? HW_BSSID_0 : mvif->omac_idx; + basic_req.basic.hw_bss_idx = idx; + if (band == NL80211_BAND_6GHZ) + basic_req.basic.phymode_ext = PHY_MODE_AX_6G; + + basic_phy = mt76_connac_get_phy_mode_v2(phy, vif, band, NULL); + basic_req.basic.nonht_basic_phy = cpu_to_le16(basic_phy); + + switch (vif->type) { + case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_AP: + if (vif->p2p) + conn_type = CONNECTION_P2P_GO; + else + conn_type = CONNECTION_INFRA_AP; + basic_req.basic.conn_type = cpu_to_le32(conn_type); + /* Fully active/deactivate BSS network in AP mode only */ + basic_req.basic.active = enable; + break; + case NL80211_IFTYPE_STATION: + if (vif->p2p) + conn_type = CONNECTION_P2P_GC; + else + conn_type = CONNECTION_INFRA_STA; + basic_req.basic.conn_type = cpu_to_le32(conn_type); + break; + case NL80211_IFTYPE_ADHOC: + basic_req.basic.conn_type = cpu_to_le32(CONNECTION_IBSS_ADHOC); + break; + case NL80211_IFTYPE_NAN: + case NL80211_IFTYPE_NAN_DATA: + basic_req.basic.conn_type = cpu_to_le32(CONNECTION_NAN); + basic_req.basic.active = enable; + break; + default: + WARN_ON(1); + break; + } + + memcpy(basic_req.basic.bssid, vif->bss_conf.bssid, ETH_ALEN); + basic_req.basic.bmc_tx_wlan_idx = cpu_to_le16(wcid->idx); + basic_req.basic.sta_idx = cpu_to_le16(wcid->idx); + basic_req.basic.conn_state = !enable; + + err = mt76_mcu_send_msg(mdev, MCU_UNI_CMD(BSS_INFO_UPDATE), &basic_req, + sizeof(basic_req), true); + if (err < 0) + return err; + + if (vif->bss_conf.he_support) { + struct { + struct { + u8 bss_idx; + u8 pad[3]; + } __packed hdr; + struct bss_info_uni_he he; + struct bss_info_uni_bss_color bss_color; + } he_req = { + .hdr = { + .bss_idx = mvif->idx, + }, + .he = { + .tag = cpu_to_le16(UNI_BSS_INFO_HE_BASIC), + .len = cpu_to_le16(sizeof(struct bss_info_uni_he)), + }, + .bss_color = { + .tag = cpu_to_le16(UNI_BSS_INFO_BSS_COLOR), + .len = cpu_to_le16(sizeof(struct bss_info_uni_bss_color)), + .enable = 0, + .bss_color = 0, + }, + }; + + if (enable) { + he_req.bss_color.enable = + vif->bss_conf.he_bss_color.enabled; + he_req.bss_color.bss_color = + vif->bss_conf.he_bss_color.color; + } + + mt76_connac_mcu_uni_bss_he_tlv(phy, vif, + (struct tlv *)&he_req.he); + err = mt76_mcu_send_msg(mdev, MCU_UNI_CMD(BSS_INFO_UPDATE), + &he_req, sizeof(he_req), true); + if (err < 0) + return err; + } + + if (enable && vif->bss_conf.bssid_indicator) { + struct { + struct { + u8 bss_idx; + u8 pad[3]; + } __packed hdr; + struct bss_info_uni_mbssid mbssid; + } mbssid_req = { + .hdr = { + .bss_idx = mvif->idx, + }, + .mbssid = { + .tag = cpu_to_le16(UNI_BSS_INFO_11V_MBSSID), + .len = cpu_to_le16(sizeof(struct bss_info_uni_mbssid)), + .max_indicator = vif->bss_conf.bssid_indicator, + .mbss_idx = vif->bss_conf.bssid_index, + }, + }; + + err = mt76_mcu_send_msg(mdev, MCU_UNI_CMD(BSS_INFO_UPDATE), + &mbssid_req, sizeof(mbssid_req), true); + if (err < 0) + return err; + } + + return mt76_connac_mcu_uni_set_chctx(phy, mvif, ctx); +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_uni_add_bss); + +void mt76_connac_mcu_build_rnr_scan_param(struct mt76_dev *mdev, + struct cfg80211_scan_request *sreq) +{ + struct ieee80211_channel **scan_list = sreq->channels; + int i, bssid_index = 0; + + /* clear 6G active Scan BSSID table */ + memset(&mdev->rnr, 0, sizeof(mdev->rnr)); + + for (i = 0; i < sreq->n_6ghz_params; i++) { + u8 ch_idx = sreq->scan_6ghz_params[i].channel_idx; + int k = 0; + + /* Remove the duplicated BSSID */ + for (k = 0; k < bssid_index; k++) { + if (!memcmp(&mdev->rnr.bssid[k], + sreq->scan_6ghz_params[i].bssid, + ETH_ALEN)) + break; + } + + if (k == bssid_index && + bssid_index < MT76_RNR_SCAN_MAX_BSSIDS) { + memcpy(&mdev->rnr.bssid[bssid_index++], + sreq->scan_6ghz_params[i].bssid, ETH_ALEN); + mdev->rnr.channel[k] = scan_list[ch_idx]->hw_value; + } + } + + mdev->rnr.bssid_num = bssid_index; + + if (sreq->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) { + memcpy(mdev->rnr.random_mac, sreq->mac_addr, ETH_ALEN); + mdev->rnr.sreq_flag = sreq->flags; + } +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_build_rnr_scan_param); + +#define MT76_CONNAC_SCAN_CHANNEL_TIME 60 +int mt76_connac_mcu_hw_scan(struct mt76_phy *phy, struct ieee80211_vif *vif, + struct ieee80211_scan_request *scan_req) +{ + struct mt76_vif_link *mvif = (struct mt76_vif_link *)vif->drv_priv; + struct cfg80211_scan_request *sreq = &scan_req->req; + int n_ssids = 0, err, i, duration; + int ext_channels_num = max_t(int, sreq->n_channels - 32, 0); + struct ieee80211_channel **scan_list = sreq->channels; + struct mt76_dev *mdev = phy->dev; + struct mt76_connac_mcu_scan_channel *chan; + struct mt76_connac_hw_scan_req *req; + struct sk_buff *skb; + + if (test_bit(MT76_HW_SCANNING, &phy->state)) + return -EBUSY; + + skb = mt76_mcu_msg_alloc(mdev, NULL, sizeof(*req)); + if (!skb) + return -ENOMEM; + + set_bit(MT76_HW_SCANNING, &phy->state); + mvif->scan_seq_num = (mvif->scan_seq_num + 1) & 0x7f; + + req = (struct mt76_connac_hw_scan_req *)skb_put_zero(skb, sizeof(*req)); + + req->seq_num = mvif->scan_seq_num | mvif->band_idx << 7; + req->bss_idx = mvif->idx; + req->scan_type = sreq->n_ssids ? 1 : 0; + req->probe_req_num = sreq->n_ssids ? 2 : 0; + req->version = 1; + + for (i = 0; i < sreq->n_ssids; i++) { + if (!sreq->ssids[i].ssid_len) + continue; + + req->ssids[n_ssids].ssid_len = cpu_to_le32(sreq->ssids[i].ssid_len); + memcpy(req->ssids[n_ssids].ssid, sreq->ssids[i].ssid, + sreq->ssids[i].ssid_len); + n_ssids++; + } + req->ssid_type = n_ssids ? BIT(2) : BIT(0); + req->ssid_type_ext = n_ssids ? BIT(0) : 0; + req->ssids_num = n_ssids; + + duration = is_connac2(phy->dev) ? 0 : MT76_CONNAC_SCAN_CHANNEL_TIME; + /* increase channel time for passive scan */ + if (!sreq->n_ssids) + duration *= 2; + req->timeout_value = cpu_to_le16(sreq->n_channels * duration); + req->channel_min_dwell_time = cpu_to_le16(duration); + req->channel_dwell_time = cpu_to_le16(duration); + + if (sreq->n_channels == 0 || sreq->n_channels > 64) { + req->channel_type = 0; + req->channels_num = 0; + req->ext_channels_num = 0; + } else { + req->channel_type = 4; + req->channels_num = min_t(u8, sreq->n_channels, 32); + req->ext_channels_num = min_t(u8, ext_channels_num, 32); + } + + for (i = 0; i < req->channels_num + req->ext_channels_num; i++) { + if (i >= 32) + chan = &req->ext_channels[i - 32]; + else + chan = &req->channels[i]; + + switch (scan_list[i]->band) { + case NL80211_BAND_2GHZ: + chan->band = 1; + break; + case NL80211_BAND_6GHZ: + chan->band = 3; + break; + default: + chan->band = 2; + break; + } + chan->channel_num = scan_list[i]->hw_value; + } + + if (sreq->ie_len > 0) { + memcpy(req->ies, sreq->ie, sreq->ie_len); + req->ies_len = cpu_to_le16(sreq->ie_len); + } + + if (is_connac2(phy->dev)) + req->scan_func |= SCAN_FUNC_SPLIT_SCAN; + + memcpy(req->bssid, sreq->bssid, ETH_ALEN); + if (sreq->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) { + get_random_mask_addr(req->random_mac, sreq->mac_addr, + sreq->mac_addr_mask); + req->scan_func |= SCAN_FUNC_RANDOM_MAC; + } + + err = mt76_mcu_skb_send_msg(mdev, skb, MCU_CE_CMD(START_HW_SCAN), + false); + if (err < 0) + clear_bit(MT76_HW_SCANNING, &phy->state); + + return err; +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_hw_scan); + +int mt76_connac_mcu_cancel_hw_scan(struct mt76_phy *phy, + struct ieee80211_vif *vif) +{ + struct mt76_vif_link *mvif = (struct mt76_vif_link *)vif->drv_priv; + struct { + u8 seq_num; + u8 is_ext_channel; + u8 rsv[2]; + } __packed req = { + .seq_num = mvif->scan_seq_num, + }; + + if (test_and_clear_bit(MT76_HW_SCANNING, &phy->state)) { + struct cfg80211_scan_info info = { + .aborted = true, + }; + + ieee80211_scan_completed(phy->hw, &info); + } + + return mt76_mcu_send_msg(phy->dev, MCU_CE_CMD(CANCEL_HW_SCAN), + &req, sizeof(req), false); +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_cancel_hw_scan); + +int mt76_connac_mcu_sched_scan_req(struct mt76_phy *phy, + struct ieee80211_vif *vif, + struct cfg80211_sched_scan_request *sreq) +{ + struct mt76_vif_link *mvif = (struct mt76_vif_link *)vif->drv_priv; + struct ieee80211_channel **scan_list = sreq->channels; + struct mt76_connac_mcu_scan_channel *chan; + struct mt76_connac_sched_scan_req *req; + struct mt76_dev *mdev = phy->dev; + struct cfg80211_match_set *match; + struct cfg80211_ssid *ssid; + struct sk_buff *skb; + int i; + + skb = mt76_mcu_msg_alloc(mdev, NULL, sizeof(*req) + sreq->ie_len); + if (!skb) + return -ENOMEM; + + mvif->scan_seq_num = (mvif->scan_seq_num + 1) & 0x7f; + + req = (struct mt76_connac_sched_scan_req *)skb_put_zero(skb, sizeof(*req)); + req->version = 1; + req->seq_num = mvif->scan_seq_num | mvif->band_idx << 7; + + if (sreq->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) { + u8 *addr = is_mt7663(phy->dev) ? req->mt7663.random_mac + : req->mt7921.random_mac; + + req->scan_func = 1; + get_random_mask_addr(addr, sreq->mac_addr, + sreq->mac_addr_mask); + } + if (is_connac2(phy->dev)) { + req->mt7921.bss_idx = mvif->idx; + req->mt7921.delay = cpu_to_le32(sreq->delay); + } + + req->ssids_num = sreq->n_ssids; + for (i = 0; i < req->ssids_num; i++) { + ssid = &sreq->ssids[i]; + memcpy(req->ssids[i].ssid, ssid->ssid, ssid->ssid_len); + req->ssids[i].ssid_len = cpu_to_le32(ssid->ssid_len); + } + + req->match_num = sreq->n_match_sets; + for (i = 0; i < req->match_num; i++) { + match = &sreq->match_sets[i]; + memcpy(req->match[i].ssid, match->ssid.ssid, + match->ssid.ssid_len); + req->match[i].rssi_th = cpu_to_le32(match->rssi_thold); + req->match[i].ssid_len = match->ssid.ssid_len; + } + + req->channel_type = sreq->n_channels ? 4 : 0; + req->channels_num = min_t(u8, sreq->n_channels, 64); + for (i = 0; i < req->channels_num; i++) { + chan = &req->channels[i]; + + switch (scan_list[i]->band) { + case NL80211_BAND_2GHZ: + chan->band = 1; + break; + case NL80211_BAND_6GHZ: + chan->band = 3; + break; + default: + chan->band = 2; + break; + } + chan->channel_num = scan_list[i]->hw_value; + } + + req->intervals_num = sreq->n_scan_plans; + for (i = 0; i < req->intervals_num; i++) + req->intervals[i] = cpu_to_le16(sreq->scan_plans[i].interval); + + if (sreq->ie_len > 0) { + req->ie_len = cpu_to_le16(sreq->ie_len); + memcpy(skb_put(skb, sreq->ie_len), sreq->ie, sreq->ie_len); + } + + return mt76_mcu_skb_send_msg(mdev, skb, MCU_CE_CMD(SCHED_SCAN_REQ), + false); +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_sched_scan_req); + +int mt76_connac_mcu_sched_scan_enable(struct mt76_phy *phy, + struct ieee80211_vif *vif, + bool enable) +{ + struct { + u8 active; /* 0: enabled 1: disabled */ + u8 rsv[3]; + } __packed req = { + .active = !enable, + }; + + if (enable) + set_bit(MT76_HW_SCHED_SCANNING, &phy->state); + else + clear_bit(MT76_HW_SCHED_SCANNING, &phy->state); + + return mt76_mcu_send_msg(phy->dev, MCU_CE_CMD(SCHED_SCAN_ENABLE), + &req, sizeof(req), false); +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_sched_scan_enable); + +int mt76_connac_mcu_chip_config(struct mt76_dev *dev) +{ + struct mt76_connac_config req = { + .resp_type = 0, + }; + + strscpy(req.data, "assert"); + + return mt76_mcu_send_msg(dev, MCU_CE_CMD(CHIP_CONFIG), + &req, sizeof(req), false); +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_chip_config); + +int mt76_connac_mcu_set_deep_sleep(struct mt76_dev *dev, bool enable) +{ + struct mt76_connac_config req = { + .resp_type = 0, + }; + + snprintf(req.data, sizeof(req.data), "KeepFullPwr %d", !enable); + + return mt76_mcu_send_msg(dev, MCU_CE_CMD(CHIP_CONFIG), + &req, sizeof(req), false); +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_set_deep_sleep); + +int mt76_connac_sta_state_dp(struct mt76_dev *dev, + enum ieee80211_sta_state old_state, + enum ieee80211_sta_state new_state) +{ + if ((old_state == IEEE80211_STA_ASSOC && + new_state == IEEE80211_STA_AUTHORIZED) || + (old_state == IEEE80211_STA_NONE && + new_state == IEEE80211_STA_NOTEXIST)) + mt76_connac_mcu_set_deep_sleep(dev, true); + + if ((old_state == IEEE80211_STA_NOTEXIST && + new_state == IEEE80211_STA_NONE) || + (old_state == IEEE80211_STA_AUTHORIZED && + new_state == IEEE80211_STA_ASSOC)) + mt76_connac_mcu_set_deep_sleep(dev, false); + + return 0; +} +EXPORT_SYMBOL_GPL(mt76_connac_sta_state_dp); + +void mt76_connac_mcu_coredump_event(struct mt76_dev *dev, struct sk_buff *skb, + struct mt76_connac_coredump *coredump) +{ + spin_lock_bh(&dev->lock); + __skb_queue_tail(&coredump->msg_list, skb); + spin_unlock_bh(&dev->lock); + + coredump->last_activity = jiffies; + + queue_delayed_work(dev->wq, &coredump->work, + MT76_CONNAC_COREDUMP_TIMEOUT); +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_coredump_event); + +static void +mt76_connac_mcu_build_sku(struct mt76_dev *dev, s8 *sku, + struct mt76_power_limits *limits, + enum nl80211_band band) +{ + int max_power = is_connac2(dev) ? 127 : 63; + int i, offset = sizeof(limits->cck); + + memset(sku, max_power, MT_SKU_POWER_LIMIT); + + if (band == NL80211_BAND_2GHZ) { + /* cck */ + memcpy(sku, limits->cck, sizeof(limits->cck)); + } + + /* ofdm */ + memcpy(&sku[offset], limits->ofdm, sizeof(limits->ofdm)); + offset += sizeof(limits->ofdm); + + /* ht */ + for (i = 0; i < 2; i++) { + memcpy(&sku[offset], limits->mcs[i], 8); + offset += 8; + } + sku[offset++] = limits->mcs[0][0]; + + /* vht */ + for (i = 0; i < ARRAY_SIZE(limits->mcs); i++) { + memcpy(&sku[offset], limits->mcs[i], + ARRAY_SIZE(limits->mcs[i])); + offset += 12; + } + + if (!is_connac2(dev)) + return; + + /* he */ + for (i = 0; i < ARRAY_SIZE(limits->ru); i++) { + memcpy(&sku[offset], limits->ru[i], ARRAY_SIZE(limits->ru[i])); + offset += ARRAY_SIZE(limits->ru[i]); + } +} + +s8 mt76_connac_get_ch_power(struct mt76_phy *phy, + struct ieee80211_channel *chan, + s8 target_power) +{ + struct mt76_dev *dev = phy->dev; + struct ieee80211_supported_band *sband; + int i; + + switch (chan->band) { + case NL80211_BAND_2GHZ: + sband = &phy->sband_2g.sband; + break; + case NL80211_BAND_5GHZ: + sband = &phy->sband_5g.sband; + break; + case NL80211_BAND_6GHZ: + sband = &phy->sband_6g.sband; + break; + default: + return target_power; + } + + for (i = 0; i < sband->n_channels; i++) { + struct ieee80211_channel *ch = &sband->channels[i]; + + if (ch->hw_value == chan->hw_value) { + if (!(ch->flags & IEEE80211_CHAN_DISABLED)) { + int power = 2 * ch->max_reg_power; + + if (is_mt7663(dev) && (power > 63 || power < -64)) + power = 63; + target_power = min_t(s8, power, target_power); + } + break; + } + } + + return target_power; +} +EXPORT_SYMBOL_GPL(mt76_connac_get_ch_power); + +static int +mt76_connac_mcu_rate_txpower_band(struct mt76_phy *phy, + enum nl80211_band band) +{ + struct mt76_dev *dev = phy->dev; + int sku_len, batch_len = is_connac2(dev) ? 8 : 16; + static const u8 chan_list_2ghz[] = { + 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14 + }; + static const u8 chan_list_5ghz[] = { + 36, 38, 40, 42, 44, 46, 48, + 50, 52, 54, 56, 58, 60, 62, + 64, 100, 102, 104, 106, 108, 110, + 112, 114, 116, 118, 120, 122, 124, + 126, 128, 132, 134, 136, 138, 140, + 142, 144, 149, 151, 153, 155, 157, + 159, 161, 165, 169, 173, 177 + }; + static const u8 chan_list_6ghz[] = { + 1, 3, 5, 7, 9, 11, 13, + 15, 17, 19, 21, 23, 25, 27, + 29, 33, 35, 37, 39, 41, 43, + 45, 47, 49, 51, 53, 55, 57, + 59, 61, 65, 67, 69, 71, 73, + 75, 77, 79, 81, 83, 85, 87, + 89, 91, 93, 97, 99, 101, 103, + 105, 107, 109, 111, 113, 115, 117, + 119, 121, 123, 125, 129, 131, 133, + 135, 137, 139, 141, 143, 145, 147, + 149, 151, 153, 155, 157, 161, 163, + 165, 167, 169, 171, 173, 175, 177, + 179, 181, 183, 185, 187, 189, 193, + 195, 197, 199, 201, 203, 205, 207, + 209, 211, 213, 215, 217, 219, 221, + 225, 227, 229, 233 + }; + int i, n_chan, batch_size, idx = 0, tx_power, last_ch, err = 0; + struct mt76_connac_sku_tlv sku_tlbv; + struct mt76_power_limits *limits; + const u8 *ch_list; + + limits = devm_kmalloc(dev->dev, sizeof(*limits), GFP_KERNEL); + if (!limits) + return -ENOMEM; + + sku_len = is_connac2(dev) ? sizeof(sku_tlbv) : sizeof(sku_tlbv) - 92; + tx_power = 2 * phy->hw->conf.power_level; + if (!tx_power) + tx_power = 127; + + if (band == NL80211_BAND_2GHZ) { + n_chan = ARRAY_SIZE(chan_list_2ghz); + ch_list = chan_list_2ghz; + } else if (band == NL80211_BAND_6GHZ) { + n_chan = ARRAY_SIZE(chan_list_6ghz); + ch_list = chan_list_6ghz; + } else { + n_chan = ARRAY_SIZE(chan_list_5ghz); + ch_list = chan_list_5ghz; + } + batch_size = DIV_ROUND_UP(n_chan, batch_len); + + if (phy->cap.has_6ghz) + last_ch = chan_list_6ghz[ARRAY_SIZE(chan_list_6ghz) - 1]; + else if (phy->cap.has_5ghz) + last_ch = chan_list_5ghz[ARRAY_SIZE(chan_list_5ghz) - 1]; + else + last_ch = chan_list_2ghz[ARRAY_SIZE(chan_list_2ghz) - 1]; + + for (i = 0; i < batch_size; i++) { + struct mt76_connac_tx_power_limit_tlv tx_power_tlv = {}; + int j, msg_len, num_ch; + struct sk_buff *skb; + + num_ch = i == batch_size - 1 ? n_chan - i * batch_len : batch_len; + msg_len = sizeof(tx_power_tlv) + num_ch * sizeof(sku_tlbv); + skb = mt76_mcu_msg_alloc(dev, NULL, msg_len); + if (!skb) { + err = -ENOMEM; + goto out; + } + + skb_reserve(skb, sizeof(tx_power_tlv)); + + BUILD_BUG_ON(sizeof(dev->alpha2) > sizeof(tx_power_tlv.alpha2)); + memcpy(tx_power_tlv.alpha2, dev->alpha2, sizeof(dev->alpha2)); + tx_power_tlv.n_chan = num_ch; + + switch (band) { + case NL80211_BAND_2GHZ: + tx_power_tlv.band = 1; + break; + case NL80211_BAND_6GHZ: + tx_power_tlv.band = 3; + break; + default: + tx_power_tlv.band = 2; + break; + } + + for (j = 0; j < num_ch; j++, idx++) { + struct ieee80211_channel chan = { + .hw_value = ch_list[idx], + .band = band, + }; + s8 max_power; + + max_power = mt76_connac_get_rate_power_limit(phy, &chan, + limits); + if (phy->chandef.chan && + phy->chandef.chan->hw_value == ch_list[idx] && + phy->chandef.chan->band == band) + mt76_connac_set_txpower_cur(phy, max_power); + + tx_power_tlv.last_msg = ch_list[idx] == last_ch; + sku_tlbv.channel = ch_list[idx]; + + mt76_connac_mcu_build_sku(dev, sku_tlbv.pwr_limit, + limits, band); + skb_put_data(skb, &sku_tlbv, sku_len); + } + __skb_push(skb, sizeof(tx_power_tlv)); + memcpy(skb->data, &tx_power_tlv, sizeof(tx_power_tlv)); + + err = mt76_mcu_skb_send_msg(dev, skb, + MCU_CE_CMD(SET_RATE_TX_POWER), + false); + if (err < 0) + goto out; + + /* read a CR to avoid PSE buffer underflow */ + mt76_connac_mcu_reg_rr(dev, MT_PSE_BASE); + } + +out: + devm_kfree(dev->dev, limits); + return err; +} + +int mt76_connac_mcu_set_rate_txpower(struct mt76_phy *phy) +{ + int err; + + if (phy->cap.has_2ghz) { + err = mt76_connac_mcu_rate_txpower_band(phy, + NL80211_BAND_2GHZ); + if (err < 0) + return err; + } + if (phy->cap.has_5ghz) { + err = mt76_connac_mcu_rate_txpower_band(phy, + NL80211_BAND_5GHZ); + if (err < 0) + return err; + } + if (phy->cap.has_6ghz) { + err = mt76_connac_mcu_rate_txpower_band(phy, + NL80211_BAND_6GHZ); + if (err < 0) + return err; + } + + return 0; +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_set_rate_txpower); + +int mt76_connac_mcu_update_arp_filter(struct mt76_dev *dev, + struct mt76_vif_link *vif, + struct ieee80211_bss_conf *info) +{ + struct ieee80211_vif *mvif = container_of(info, struct ieee80211_vif, + bss_conf); + struct sk_buff *skb; + int i, len = min_t(int, mvif->cfg.arp_addr_cnt, + IEEE80211_BSS_ARP_ADDR_LIST_LEN); + struct { + struct { + u8 bss_idx; + u8 pad[3]; + } __packed hdr; + struct mt76_connac_arpns_tlv arp; + } req_hdr = { + .hdr = { + .bss_idx = vif->idx, + }, + .arp = { + .tag = cpu_to_le16(UNI_OFFLOAD_OFFLOAD_ARP), + .len = cpu_to_le16(sizeof(struct mt76_connac_arpns_tlv)), + .ips_num = len, + .mode = 2, /* update */ + .option = 1, + }, + }; + + skb = mt76_mcu_msg_alloc(dev, NULL, + sizeof(req_hdr) + len * sizeof(__be32)); + if (!skb) + return -ENOMEM; + + skb_put_data(skb, &req_hdr, sizeof(req_hdr)); + for (i = 0; i < len; i++) + skb_put_data(skb, &mvif->cfg.arp_addr_list[i], sizeof(__be32)); + + return mt76_mcu_skb_send_msg(dev, skb, MCU_UNI_CMD(OFFLOAD), true); +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_update_arp_filter); + +int mt76_connac_mcu_set_p2p_oppps(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct mt76_vif_link *mvif = (struct mt76_vif_link *)vif->drv_priv; + int ct_window = vif->bss_conf.p2p_noa_attr.oppps_ctwindow; + struct mt76_phy *phy = hw->priv; + struct { + __le32 ct_win; + u8 bss_idx; + u8 rsv[3]; + } __packed req = { + .ct_win = cpu_to_le32(ct_window), + .bss_idx = mvif->idx, + }; + + return mt76_mcu_send_msg(phy->dev, MCU_CE_CMD(SET_P2P_OPPPS), + &req, sizeof(req), false); +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_set_p2p_oppps); + +#ifdef CONFIG_PM + +const struct wiphy_wowlan_support mt76_connac_wowlan_support = { + .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT | + WIPHY_WOWLAN_SUPPORTS_GTK_REKEY | WIPHY_WOWLAN_NET_DETECT, + .n_patterns = 1, + .pattern_min_len = 1, + .pattern_max_len = MT76_CONNAC_WOW_PATTEN_MAX_LEN, + .max_nd_match_sets = 10, +}; +EXPORT_SYMBOL_GPL(mt76_connac_wowlan_support); + +static void +mt76_connac_mcu_key_iter(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key, + void *data) +{ + struct mt76_connac_gtk_rekey_tlv *gtk_tlv = data; + u32 cipher; + + if (key->cipher != WLAN_CIPHER_SUITE_AES_CMAC && + key->cipher != WLAN_CIPHER_SUITE_CCMP && + key->cipher != WLAN_CIPHER_SUITE_TKIP) + return; + + if (key->cipher == WLAN_CIPHER_SUITE_TKIP) + cipher = BIT(3); + else + cipher = BIT(4); + + /* we are assuming here to have a single pairwise key */ + if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) { + if (key->cipher == WLAN_CIPHER_SUITE_TKIP) + gtk_tlv->proto = cpu_to_le32(NL80211_WPA_VERSION_1); + else + gtk_tlv->proto = cpu_to_le32(NL80211_WPA_VERSION_2); + + gtk_tlv->pairwise_cipher = cpu_to_le32(cipher); + gtk_tlv->keyid = key->keyidx; + } else { + gtk_tlv->group_cipher = cpu_to_le32(cipher); + } +} + +int mt76_connac_mcu_update_gtk_rekey(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_gtk_rekey_data *key) +{ + struct mt76_vif_link *mvif = (struct mt76_vif_link *)vif->drv_priv; + struct mt76_connac_gtk_rekey_tlv *gtk_tlv; + struct mt76_phy *phy = hw->priv; + struct sk_buff *skb; + struct { + u8 bss_idx; + u8 pad[3]; + } __packed hdr = { + .bss_idx = mvif->idx, + }; + + skb = mt76_mcu_msg_alloc(phy->dev, NULL, + sizeof(hdr) + sizeof(*gtk_tlv)); + if (!skb) + return -ENOMEM; + + skb_put_data(skb, &hdr, sizeof(hdr)); + gtk_tlv = (struct mt76_connac_gtk_rekey_tlv *)skb_put_zero(skb, + sizeof(*gtk_tlv)); + gtk_tlv->tag = cpu_to_le16(UNI_OFFLOAD_OFFLOAD_GTK_REKEY); + gtk_tlv->len = cpu_to_le16(sizeof(*gtk_tlv)); + gtk_tlv->rekey_mode = 2; + gtk_tlv->option = 1; + + rcu_read_lock(); + ieee80211_iter_keys_rcu(hw, vif, mt76_connac_mcu_key_iter, gtk_tlv); + rcu_read_unlock(); + + memcpy(gtk_tlv->kek, key->kek, NL80211_KEK_LEN); + memcpy(gtk_tlv->kck, key->kck, NL80211_KCK_LEN); + memcpy(gtk_tlv->replay_ctr, key->replay_ctr, NL80211_REPLAY_CTR_LEN); + + return mt76_mcu_skb_send_msg(phy->dev, skb, + MCU_UNI_CMD(OFFLOAD), true); +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_update_gtk_rekey); + +static int +mt76_connac_mcu_set_arp_filter(struct mt76_dev *dev, struct ieee80211_vif *vif, + bool suspend) +{ + struct mt76_vif_link *mvif = (struct mt76_vif_link *)vif->drv_priv; + struct { + struct { + u8 bss_idx; + u8 pad[3]; + } __packed hdr; + struct mt76_connac_arpns_tlv arpns; + } req = { + .hdr = { + .bss_idx = mvif->idx, + }, + .arpns = { + .tag = cpu_to_le16(UNI_OFFLOAD_OFFLOAD_ARP), + .len = cpu_to_le16(sizeof(struct mt76_connac_arpns_tlv)), + .mode = suspend, + }, + }; + + return mt76_mcu_send_msg(dev, MCU_UNI_CMD(OFFLOAD), &req, + sizeof(req), true); +} + +int +mt76_connac_mcu_set_gtk_rekey(struct mt76_dev *dev, struct ieee80211_vif *vif, + bool suspend) +{ + struct mt76_vif_link *mvif = (struct mt76_vif_link *)vif->drv_priv; + struct { + struct { + u8 bss_idx; + u8 pad[3]; + } __packed hdr; + struct mt76_connac_gtk_rekey_tlv gtk_tlv; + } __packed req = { + .hdr = { + .bss_idx = mvif->idx, + }, + .gtk_tlv = { + .tag = cpu_to_le16(UNI_OFFLOAD_OFFLOAD_GTK_REKEY), + .len = cpu_to_le16(sizeof(struct mt76_connac_gtk_rekey_tlv)), + .rekey_mode = !suspend, + }, + }; + + return mt76_mcu_send_msg(dev, MCU_UNI_CMD(OFFLOAD), &req, + sizeof(req), true); +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_set_gtk_rekey); + +int +mt76_connac_mcu_set_suspend_mode(struct mt76_dev *dev, + struct ieee80211_vif *vif, + bool enable, u8 mdtim, + bool wow_suspend) +{ + struct mt76_vif_link *mvif = (struct mt76_vif_link *)vif->drv_priv; + struct { + struct { + u8 bss_idx; + u8 pad[3]; + } __packed hdr; + struct mt76_connac_suspend_tlv suspend_tlv; + } req = { + .hdr = { + .bss_idx = mvif->idx, + }, + .suspend_tlv = { + .tag = cpu_to_le16(UNI_SUSPEND_MODE_SETTING), + .len = cpu_to_le16(sizeof(struct mt76_connac_suspend_tlv)), + .enable = enable, + .mdtim = mdtim, + .wow_suspend = wow_suspend, + }, + }; + + return mt76_mcu_send_msg(dev, MCU_UNI_CMD(SUSPEND), &req, + sizeof(req), true); +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_set_suspend_mode); + +static int +mt76_connac_mcu_set_wow_pattern(struct mt76_dev *dev, + struct ieee80211_vif *vif, + u8 index, bool enable, + struct cfg80211_pkt_pattern *pattern) +{ + struct mt76_vif_link *mvif = (struct mt76_vif_link *)vif->drv_priv; + struct mt76_connac_wow_pattern_tlv *ptlv; + struct sk_buff *skb; + struct req_hdr { + u8 bss_idx; + u8 pad[3]; + } __packed hdr = { + .bss_idx = mvif->idx, + }; + + skb = mt76_mcu_msg_alloc(dev, NULL, sizeof(hdr) + sizeof(*ptlv)); + if (!skb) + return -ENOMEM; + + skb_put_data(skb, &hdr, sizeof(hdr)); + ptlv = (struct mt76_connac_wow_pattern_tlv *)skb_put_zero(skb, sizeof(*ptlv)); + ptlv->tag = cpu_to_le16(UNI_SUSPEND_WOW_PATTERN); + ptlv->len = cpu_to_le16(sizeof(*ptlv)); + ptlv->data_len = pattern->pattern_len; + ptlv->enable = enable; + ptlv->index = index; + + memcpy(ptlv->pattern, pattern->pattern, pattern->pattern_len); + memcpy(ptlv->mask, pattern->mask, DIV_ROUND_UP(pattern->pattern_len, 8)); + + return mt76_mcu_skb_send_msg(dev, skb, MCU_UNI_CMD(SUSPEND), true); +} + +int +mt76_connac_mcu_set_wow_ctrl(struct mt76_phy *phy, struct ieee80211_vif *vif, + bool suspend, struct cfg80211_wowlan *wowlan) +{ + struct mt76_vif_link *mvif = (struct mt76_vif_link *)vif->drv_priv; + struct mt76_dev *dev = phy->dev; + struct { + struct { + u8 bss_idx; + u8 pad[3]; + } __packed hdr; + struct mt76_connac_wow_ctrl_tlv wow_ctrl_tlv; + struct mt76_connac_wow_gpio_param_tlv gpio_tlv; + } req = { + .hdr = { + .bss_idx = mvif->idx, + }, + .wow_ctrl_tlv = { + .tag = cpu_to_le16(UNI_SUSPEND_WOW_CTRL), + .len = cpu_to_le16(sizeof(struct mt76_connac_wow_ctrl_tlv)), + .cmd = suspend ? 1 : 2, + }, + .gpio_tlv = { + .tag = cpu_to_le16(UNI_SUSPEND_WOW_GPIO_PARAM), + .len = cpu_to_le16(sizeof(struct mt76_connac_wow_gpio_param_tlv)), + .gpio_pin = 0xff, /* follow fw about GPIO pin */ + }, + }; + + if (wowlan->magic_pkt) + req.wow_ctrl_tlv.trigger |= UNI_WOW_DETECT_TYPE_MAGIC; + if (wowlan->disconnect) + req.wow_ctrl_tlv.trigger |= (UNI_WOW_DETECT_TYPE_DISCONNECT | + UNI_WOW_DETECT_TYPE_BCN_LOST); + if (wowlan->nd_config) { + mt76_connac_mcu_sched_scan_req(phy, vif, wowlan->nd_config); + req.wow_ctrl_tlv.trigger |= UNI_WOW_DETECT_TYPE_SCH_SCAN_HIT; + mt76_connac_mcu_sched_scan_enable(phy, vif, suspend); + } + if (wowlan->n_patterns) + req.wow_ctrl_tlv.trigger |= UNI_WOW_DETECT_TYPE_BITMAP; + + if (mt76_is_mmio(dev)) + req.wow_ctrl_tlv.wakeup_hif = WOW_PCIE; + else if (mt76_is_usb(dev)) + req.wow_ctrl_tlv.wakeup_hif = WOW_USB; + else if (mt76_is_sdio(dev)) + req.wow_ctrl_tlv.wakeup_hif = WOW_GPIO; + + return mt76_mcu_send_msg(dev, MCU_UNI_CMD(SUSPEND), &req, + sizeof(req), true); +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_set_wow_ctrl); + +int mt76_connac_mcu_set_hif_suspend(struct mt76_dev *dev, bool suspend, bool wait_resp) +{ + struct { + struct { + u8 hif_type; /* 0x0: HIF_SDIO + * 0x1: HIF_USB + * 0x2: HIF_PCIE + */ + u8 pad[3]; + } __packed hdr; + struct hif_suspend_tlv { + __le16 tag; + __le16 len; + u8 suspend; + u8 pad[7]; + } __packed hif_suspend; + } req = { + .hif_suspend = { + .tag = cpu_to_le16(0), /* 0: UNI_HIF_CTRL_BASIC */ + .len = cpu_to_le16(sizeof(struct hif_suspend_tlv)), + .suspend = suspend, + }, + }; + + if (mt76_is_mmio(dev)) + req.hdr.hif_type = 2; + else if (mt76_is_usb(dev)) + req.hdr.hif_type = 1; + else if (mt76_is_sdio(dev)) + req.hdr.hif_type = 0; + + return mt76_mcu_send_msg(dev, MCU_UNI_CMD(HIF_CTRL), &req, + sizeof(req), wait_resp); +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_set_hif_suspend); + +void mt76_connac_mcu_set_suspend_iter(void *priv, u8 *mac, + struct ieee80211_vif *vif) +{ + struct mt76_phy *phy = priv; + bool suspend = !test_bit(MT76_STATE_RUNNING, &phy->state); + struct ieee80211_hw *hw = phy->hw; + struct cfg80211_wowlan *wowlan = hw->wiphy->wowlan_config; + int i; + + mt76_connac_mcu_set_gtk_rekey(phy->dev, vif, suspend); + mt76_connac_mcu_set_arp_filter(phy->dev, vif, suspend); + + mt76_connac_mcu_set_suspend_mode(phy->dev, vif, suspend, 1, true); + + for (i = 0; i < wowlan->n_patterns; i++) + mt76_connac_mcu_set_wow_pattern(phy->dev, vif, i, suspend, + &wowlan->patterns[i]); + mt76_connac_mcu_set_wow_ctrl(phy, vif, suspend, wowlan); +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_set_suspend_iter); +#endif /* CONFIG_PM */ + +u32 mt76_connac_mcu_reg_rr(struct mt76_dev *dev, u32 offset) +{ + struct { + __le32 addr; + __le32 val; + } __packed req = { + .addr = cpu_to_le32(offset), + }; + + return mt76_mcu_send_msg(dev, MCU_CE_QUERY(REG_READ), &req, + sizeof(req), true); +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_reg_rr); + +void mt76_connac_mcu_reg_wr(struct mt76_dev *dev, u32 offset, u32 val) +{ + struct { + __le32 addr; + __le32 val; + } __packed req = { + .addr = cpu_to_le32(offset), + .val = cpu_to_le32(val), + }; + + mt76_mcu_send_msg(dev, MCU_CE_CMD(REG_WRITE), &req, + sizeof(req), false); +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_reg_wr); + +static int +mt76_connac_mcu_sta_key_tlv(struct mt76_connac_sta_key_conf *sta_key_conf, + struct sk_buff *skb, + struct ieee80211_key_conf *key, + enum set_key_cmd cmd) +{ + struct sta_rec_sec *sec; + u32 len = sizeof(*sec); + struct tlv *tlv; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_KEY_V2, sizeof(*sec)); + sec = (struct sta_rec_sec *)tlv; + sec->add = cmd; + + if (cmd == SET_KEY) { + struct sec_key *sec_key; + u8 cipher; + + cipher = mt76_connac_mcu_get_cipher(key->cipher); + if (cipher == MCU_CIPHER_NONE) + return -EOPNOTSUPP; + + sec_key = &sec->key[0]; + sec_key->cipher_len = sizeof(*sec_key); + + if (cipher == MCU_CIPHER_BIP_CMAC_128) { + sec_key->cipher_id = MCU_CIPHER_AES_CCMP; + sec_key->key_id = sta_key_conf->keyidx; + sec_key->key_len = 16; + memcpy(sec_key->key, sta_key_conf->key, 16); + + sec_key = &sec->key[1]; + sec_key->cipher_id = MCU_CIPHER_BIP_CMAC_128; + sec_key->cipher_len = sizeof(*sec_key); + sec_key->key_len = 16; + memcpy(sec_key->key, key->key, 16); + sec->n_cipher = 2; + } else { + sec_key->cipher_id = cipher; + sec_key->key_id = key->keyidx; + sec_key->key_len = key->keylen; + memcpy(sec_key->key, key->key, key->keylen); + + if (cipher == MCU_CIPHER_TKIP) { + /* Rx/Tx MIC keys are swapped */ + memcpy(sec_key->key + 16, key->key + 24, 8); + memcpy(sec_key->key + 24, key->key + 16, 8); + } + + /* store key_conf for BIP batch update */ + if (cipher == MCU_CIPHER_AES_CCMP) { + memcpy(sta_key_conf->key, key->key, key->keylen); + sta_key_conf->keyidx = key->keyidx; + } + + len -= sizeof(*sec_key); + sec->n_cipher = 1; + } + } else { + len -= sizeof(sec->key); + sec->n_cipher = 0; + } + sec->len = cpu_to_le16(len); + + return 0; +} + +int mt76_connac_mcu_add_key(struct mt76_dev *dev, struct ieee80211_vif *vif, + struct mt76_connac_sta_key_conf *sta_key_conf, + struct ieee80211_key_conf *key, int mcu_cmd, + struct mt76_wcid *wcid, enum set_key_cmd cmd) +{ + struct mt76_vif_link *mvif = (struct mt76_vif_link *)vif->drv_priv; + struct sk_buff *skb; + int ret; + + skb = mt76_connac_mcu_alloc_sta_req(dev, mvif, wcid); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + ret = mt76_connac_mcu_sta_key_tlv(sta_key_conf, skb, key, cmd); + if (ret) { + dev_kfree_skb(skb); + return ret; + } + + ret = mt76_connac_mcu_sta_wed_update(dev, skb); + if (ret) { + dev_kfree_skb(skb); + return ret; + } + + return mt76_mcu_skb_send_msg(dev, skb, mcu_cmd, true); +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_add_key); + +/* SIFS 20us + 512 byte beacon transmitted by 1Mbps (3906us) */ +#define BCN_TX_ESTIMATE_TIME (4096 + 20) +void mt76_connac_mcu_bss_ext_tlv(struct sk_buff *skb, struct mt76_vif_link *mvif) +{ + struct bss_info_ext_bss *ext; + int ext_bss_idx, tsf_offset; + struct tlv *tlv; + + ext_bss_idx = mvif->omac_idx - EXT_BSSID_START; + if (ext_bss_idx < 0) + return; + + tlv = mt76_connac_mcu_add_tlv(skb, BSS_INFO_EXT_BSS, sizeof(*ext)); + + ext = (struct bss_info_ext_bss *)tlv; + tsf_offset = ext_bss_idx * BCN_TX_ESTIMATE_TIME; + ext->mbss_tsf_offset = cpu_to_le32(tsf_offset); +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_bss_ext_tlv); + +int mt76_connac_mcu_bss_basic_tlv(struct sk_buff *skb, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct mt76_phy *phy, u16 wlan_idx, + bool enable) +{ + struct mt76_vif_link *mvif = (struct mt76_vif_link *)vif->drv_priv; + u32 type = vif->p2p ? NETWORK_P2P : NETWORK_INFRA; + struct bss_info_basic *bss; + struct tlv *tlv; + + tlv = mt76_connac_mcu_add_tlv(skb, BSS_INFO_BASIC, sizeof(*bss)); + bss = (struct bss_info_basic *)tlv; + + switch (vif->type) { + case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_MONITOR: + break; + case NL80211_IFTYPE_AP: + if (ieee80211_hw_check(phy->hw, SUPPORTS_MULTI_BSSID)) { + u8 bssid_id = vif->bss_conf.bssid_indicator; + struct wiphy *wiphy = phy->hw->wiphy; + + if (bssid_id > ilog2(wiphy->mbssid_max_interfaces)) + return -EINVAL; + + bss->non_tx_bssid = vif->bss_conf.bssid_index; + bss->max_bssid = bssid_id; + } + break; + case NL80211_IFTYPE_STATION: + if (enable) { + rcu_read_lock(); + if (!sta) + sta = ieee80211_find_sta(vif, + vif->bss_conf.bssid); + /* TODO: enable BSS_INFO_UAPSD & BSS_INFO_PM */ + if (sta) { + struct mt76_wcid *wcid; + + wcid = (struct mt76_wcid *)sta->drv_priv; + wlan_idx = wcid->idx; + } + rcu_read_unlock(); + } + break; + case NL80211_IFTYPE_ADHOC: + type = NETWORK_IBSS; + break; + default: + WARN_ON(1); + break; + } + + bss->network_type = cpu_to_le32(type); + bss->bmc_wcid_lo = to_wcid_lo(wlan_idx); + bss->bmc_wcid_hi = to_wcid_hi(wlan_idx); + bss->wmm_idx = mvif->wmm_idx; + bss->active = enable; + bss->cipher = mvif->cipher; + + if (vif->type != NL80211_IFTYPE_MONITOR) { + struct cfg80211_chan_def *chandef = &phy->chandef; + + memcpy(bss->bssid, vif->bss_conf.bssid, ETH_ALEN); + bss->bcn_interval = cpu_to_le16(vif->bss_conf.beacon_int); + bss->dtim_period = vif->bss_conf.dtim_period; + bss->phy_mode = mt76_connac_get_phy_mode(phy, vif, + chandef->chan->band, NULL); + } else { + memcpy(bss->bssid, phy->macaddr, ETH_ALEN); + } + + return 0; +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_bss_basic_tlv); + +#define ENTER_PM_STATE 1 +#define EXIT_PM_STATE 2 +int mt76_connac_mcu_set_pm(struct mt76_dev *dev, int band, int enter) +{ + struct { + u8 pm_number; + u8 pm_state; + u8 bssid[ETH_ALEN]; + u8 dtim_period; + u8 wlan_idx_lo; + __le16 bcn_interval; + __le32 aid; + __le32 rx_filter; + u8 band_idx; + u8 wlan_idx_hi; + u8 rsv[2]; + __le32 feature; + u8 omac_idx; + u8 wmm_idx; + u8 bcn_loss_cnt; + u8 bcn_sp_duration; + } __packed req = { + .pm_number = 5, + .pm_state = enter ? ENTER_PM_STATE : EXIT_PM_STATE, + .band_idx = band, + }; + + return mt76_mcu_send_msg(dev, MCU_EXT_CMD(PM_STATE_CTRL), &req, + sizeof(req), true); +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_set_pm); + +int mt76_connac_mcu_restart(struct mt76_dev *dev) +{ + struct { + u8 power_mode; + u8 rsv[3]; + } req = { + .power_mode = 1, + }; + + return mt76_mcu_send_msg(dev, MCU_CMD(NIC_POWER_CTRL), &req, + sizeof(req), false); +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_restart); + +int mt76_connac_mcu_del_wtbl_all(struct mt76_dev *dev) +{ + struct wtbl_req_hdr req = { + .operation = WTBL_RESET_ALL, + }; + + return mt76_mcu_send_msg(dev, MCU_EXT_CMD(WTBL_UPDATE), + &req, sizeof(req), true); +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_del_wtbl_all); + +int mt76_connac_mcu_rdd_cmd(struct mt76_dev *dev, int cmd, u8 index, + u8 rx_sel, u8 val) +{ + struct { + u8 ctrl; + u8 rdd_idx; + u8 rdd_rx_sel; + u8 val; + u8 rsv[4]; + } __packed req = { + .ctrl = cmd, + .rdd_idx = index, + .rdd_rx_sel = rx_sel, + .val = val, + }; + + return mt76_mcu_send_msg(dev, MCU_EXT_CMD(SET_RDD_CTRL), &req, + sizeof(req), true); +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_rdd_cmd); + +s8 mt76_connac_get_rate_power_limit(struct mt76_phy *phy, + struct ieee80211_channel *chan, + struct mt76_power_limits *limits) +{ + s8 reg_power, sar_power; + int tx_power; + + tx_power = 2 * phy->hw->conf.power_level; + if (!tx_power) + tx_power = 127; + + reg_power = mt76_connac_get_ch_power(phy, chan, tx_power); + sar_power = mt76_get_sar_power(phy, chan, reg_power); + return mt76_get_rate_power_limits(phy, chan, limits, sar_power); +} +EXPORT_SYMBOL_GPL(mt76_connac_get_rate_power_limit); + +static int +mt76_connac_mcu_send_ram_firmware(struct mt76_dev *dev, + const struct mt76_connac2_fw_trailer *hdr, + const u8 *data, bool is_wa) +{ + int i, offset = 0, max_len = mt76_is_sdio(dev) ? 2048 : 4096; + u32 override = 0, option = 0; + + for (i = 0; i < hdr->n_region; i++) { + const struct mt76_connac2_fw_region *region; + u32 len, addr, mode; + int err; + + region = (const void *)((const u8 *)hdr - + (hdr->n_region - i) * sizeof(*region)); + mode = mt76_connac_mcu_gen_dl_mode(dev, region->feature_set, + is_wa); + len = le32_to_cpu(region->len); + addr = le32_to_cpu(region->addr); + + if (region->feature_set & FW_FEATURE_NON_DL) + goto next; + + if (region->feature_set & FW_FEATURE_OVERRIDE_ADDR) + override = addr; + + err = mt76_connac_mcu_init_download(dev, addr, len, mode); + if (err) { + dev_err(dev->dev, "Download request failed\n"); + return err; + } + + err = __mt76_mcu_send_firmware(dev, MCU_CMD(FW_SCATTER), + data + offset, len, max_len); + if (err) { + dev_err(dev->dev, "Failed to send firmware.\n"); + return err; + } + +next: + offset += len; + } + + if (override) + option |= FW_START_OVERRIDE; + if (is_wa) + option |= FW_START_WORKING_PDA_CR4; + + return mt76_connac_mcu_start_firmware(dev, override, option); +} + +static int +mt76_connac_mcu_send_phy_ram_firmware(struct mt76_dev *dev, + const struct mt76_connac2_fw_trailer *hdr, + const u8 *data) +{ + int i, offset = 0, max_len = mt76_is_sdio(dev) ? 2048 : 4096; + u32 override = 0, option = 0; + + for (i = 0; i < hdr->n_region; i++) { + const struct mt76_connac2_fw_region *region; + u32 len, addr, mode; + int err; + + region = (const void *)((const u8 *)hdr - + (hdr->n_region - i) * sizeof(*region)); + mode = mt76_connac_mcu_gen_dl_mode(dev, region->feature_set, + true); + len = le32_to_cpu(region->len); + addr = le32_to_cpu(region->addr); + + if (region->feature_set & FW_FEATURE_NON_DL) + goto next; + + if (region->feature_set & FW_FEATURE_OVERRIDE_ADDR) + override = addr; + + err = mt76_connac_mcu_init_download(dev, addr, len, mode); + if (err) { + dev_err(dev->dev, + "The request to dowload PHY firmware failed.\n"); + return err; + } + + err = __mt76_mcu_send_firmware(dev, MCU_CMD(FW_SCATTER), + data + offset, len, max_len); + if (err) { + dev_err(dev->dev, "Failed to send PHY firmware.\n"); + return err; + } + +next: + offset += len; + } + + if (override) + option |= FW_START_OVERRIDE; + option |= FW_START_WORKING_PDA_DSP; + + return mt76_connac_mcu_start_firmware(dev, override, option); +} + +int mt76_connac2_load_ram(struct mt76_dev *dev, const char *fw_wm, + const char *fw_wa) +{ + const struct mt76_connac2_fw_trailer *hdr; + const struct firmware *fw; + int ret; + + ret = request_firmware(&fw, fw_wm, dev->dev); + if (ret) + return ret; + + if (!fw || !fw->data || fw->size < sizeof(*hdr)) { + dev_err(dev->dev, "Invalid firmware\n"); + ret = -EINVAL; + goto out; + } + + hdr = (const void *)(fw->data + fw->size - sizeof(*hdr)); + dev_info(dev->dev, "WM Firmware Version: %.10s, Build Time: %.15s", + hdr->fw_ver, hdr->build_date); + + ret = mt76_connac_mcu_send_ram_firmware(dev, hdr, fw->data, false); + if (ret) { + dev_err(dev->dev, "Failed to start WM firmware\n"); + goto out; + } + + snprintf(dev->hw->wiphy->fw_version, + sizeof(dev->hw->wiphy->fw_version), + "%.10s-%.15s", hdr->fw_ver, hdr->build_date); + + release_firmware(fw); + + if (!fw_wa) + return 0; + + ret = request_firmware(&fw, fw_wa, dev->dev); + if (ret) + return ret; + + if (!fw || !fw->data || fw->size < sizeof(*hdr)) { + dev_err(dev->dev, "Invalid firmware\n"); + ret = -EINVAL; + goto out; + } + + hdr = (const void *)(fw->data + fw->size - sizeof(*hdr)); + dev_info(dev->dev, "WA Firmware Version: %.10s, Build Time: %.15s", + hdr->fw_ver, hdr->build_date); + + ret = mt76_connac_mcu_send_ram_firmware(dev, hdr, fw->data, true); + if (ret) { + dev_err(dev->dev, "Failed to start WA firmware\n"); + goto out; + } + + snprintf(dev->hw->wiphy->fw_version, + sizeof(dev->hw->wiphy->fw_version), + "%.10s-%.15s", hdr->fw_ver, hdr->build_date); + +out: + release_firmware(fw); + + return ret; +} +EXPORT_SYMBOL_GPL(mt76_connac2_load_ram); + +int mt76_connac3_load_phy_ram(struct mt76_dev *dev, const char *fw_name) +{ + const struct mt76_connac2_fw_trailer *hdr; + const struct firmware *fw; + int ret; + + ret = request_firmware(&fw, fw_name, dev->dev); + if (ret) + return ret; + + if (!fw || !fw->data || fw->size < sizeof(*hdr)) { + dev_err(dev->dev, "Invalid PHY firmware\n"); + ret = -EINVAL; + goto out; + } + + hdr = (const void *)(fw->data + fw->size - sizeof(*hdr)); + dev_info(dev->dev, "PHY Firmware Version: %.10s, Build Time: %.15s\n", + hdr->fw_ver, hdr->build_date); + + ret = mt76_connac_mcu_send_phy_ram_firmware(dev, hdr, fw->data); + if (ret) { + dev_err(dev->dev, "Failed to start PHY firmware\n"); + goto out; + } + +out: + release_firmware(fw); + + return ret; +} +EXPORT_SYMBOL_GPL(mt76_connac3_load_phy_ram); + +static u32 mt76_connac2_get_data_mode(struct mt76_dev *dev, u32 info) +{ + u32 mode = DL_MODE_NEED_RSP; + + if ((!is_connac2(dev) && !is_connac3(dev)) || info == PATCH_SEC_NOT_SUPPORT) + return mode; + + switch (FIELD_GET(PATCH_SEC_ENC_TYPE_MASK, info)) { + case PATCH_SEC_ENC_TYPE_PLAIN: + break; + case PATCH_SEC_ENC_TYPE_AES: + mode |= DL_MODE_ENCRYPT; + mode |= FIELD_PREP(DL_MODE_KEY_IDX, + (info & PATCH_SEC_ENC_AES_KEY_MASK)) & DL_MODE_KEY_IDX; + mode |= DL_MODE_RESET_SEC_IV; + break; + case PATCH_SEC_ENC_TYPE_SCRAMBLE: + mode |= DL_MODE_ENCRYPT; + mode |= DL_CONFIG_ENCRY_MODE_SEL; + mode |= DL_MODE_RESET_SEC_IV; + break; + default: + dev_err(dev->dev, "Encryption type not support!\n"); + } + + return mode; +} + +int mt76_connac2_load_patch(struct mt76_dev *dev, const char *fw_name) +{ + int i, ret, sem, max_len = mt76_is_sdio(dev) ? 2048 : 4096; + const struct mt76_connac2_patch_hdr *hdr; + const struct firmware *fw = NULL; + + sem = mt76_connac_mcu_patch_sem_ctrl(dev, true); + switch (sem) { + case PATCH_IS_DL: + return 0; + case PATCH_NOT_DL_SEM_SUCCESS: + break; + default: + dev_err(dev->dev, "Failed to get patch semaphore\n"); + return -EAGAIN; + } + + ret = request_firmware(&fw, fw_name, dev->dev); + if (ret) + goto out; + + if (!fw || !fw->data || fw->size < sizeof(*hdr)) { + dev_err(dev->dev, "Invalid firmware\n"); + ret = -EINVAL; + goto out; + } + + hdr = (const void *)fw->data; + dev_info(dev->dev, "HW/SW Version: 0x%x, Build Time: %.16s", + be32_to_cpu(hdr->hw_sw_ver), hdr->build_date); + + for (i = 0; i < be32_to_cpu(hdr->desc.n_region); i++) { + struct mt76_connac2_patch_sec *sec; + u32 len, addr, mode; + const u8 *dl; + u32 sec_info; + + sec = (void *)(fw->data + sizeof(*hdr) + i * sizeof(*sec)); + if ((be32_to_cpu(sec->type) & PATCH_SEC_TYPE_MASK) != + PATCH_SEC_TYPE_INFO) { + ret = -EINVAL; + goto out; + } + + addr = be32_to_cpu(sec->info.addr); + len = be32_to_cpu(sec->info.len); + dl = fw->data + be32_to_cpu(sec->offs); + sec_info = be32_to_cpu(sec->info.sec_key_idx); + mode = mt76_connac2_get_data_mode(dev, sec_info); + + ret = mt76_connac_mcu_init_download(dev, addr, len, mode); + if (ret) { + dev_err(dev->dev, "Download request failed\n"); + goto out; + } + + ret = __mt76_mcu_send_firmware(dev, MCU_CMD(FW_SCATTER), + dl, len, max_len); + if (ret) { + dev_err(dev->dev, "Failed to send patch\n"); + goto out; + } + } + + ret = mt76_connac_mcu_start_patch(dev); + if (ret) + dev_err(dev->dev, "Failed to start patch\n"); + +out: + sem = mt76_connac_mcu_patch_sem_ctrl(dev, false); + switch (sem) { + case PATCH_REL_SEM_SUCCESS: + break; + default: + ret = -EAGAIN; + dev_err(dev->dev, "Failed to release patch semaphore\n"); + break; + } + + release_firmware(fw); + + return ret; +} +EXPORT_SYMBOL_GPL(mt76_connac2_load_patch); + +int mt76_connac3_load_cb_patch(struct mt76_dev *dev, const char *fw_name) +{ + const struct mt76_connac3_multi_header_v2_sec_raw_format *sect = NULL; + const struct mt76_connac3_multi_header_v2_raw_format *hdr = NULL; + int i, ret, sem, max_len = mt76_is_sdio(dev) ? 2048 : 4096; + const struct firmware *fw = NULL; + const u8 *dl; + u32 len; + + sem = mt76_connac_cb_mcu_patch_sem_ctrl(dev, true); + switch (sem) { + case CB_PATCH_IS_DL: + return 0; + case CB_PATCH_GET_SEM_NEED_PATCH: + break; + default: + dev_err(dev->dev, "Failed to get cb patch semaphore %u\n", sem); + return -EAGAIN; + } + + ret = request_firmware(&fw, fw_name, dev->dev); + if (ret) + goto out; + + if (!fw || !fw->data || fw->size < sizeof(*hdr)) { + dev_err(dev->dev, "Invalid firmware\n"); + ret = -EINVAL; + goto out; + } + + hdr = (const void *)fw->data; + dev_info(dev->dev, "HW/SW Version: 0x%x, Pack Time: %.21s\n", + le32_to_cpu(hdr->patch_ver), hdr->pack_time); + + if (le32_to_cpu(hdr->sec_num) < 2) { + dev_err(dev->dev, "Invalid section numbers %u\n", + le32_to_cpu(hdr->sec_num)); + ret = -EINVAL; + goto out; + } + + sect = &hdr->sects[0]; + /* check the fw bin basic info */ + if (((le32_to_cpu(sect->type) & SEC_TYPE_SUBSYS_MASK) + != SEC_TYPE_SUBSYS_CBMCU) || + ((le32_to_cpu(sect->type) & SEC_TYPE_SUBSYS_SEC_MASK) + != SEC_TYPE_SUBSYS_SEC_IMG_SIGN)) { + dev_err(dev->dev, "Invalid CBMCU firmware\n"); + ret = -EINVAL; + goto out; + } + + /* download global desc + sect map + cert section */ + len = sizeof(struct mt76_connac3_multi_header_v2_raw_format) - + offsetof(struct mt76_connac3_multi_header_v2_raw_format, + global_desc_head) + + sizeof(struct mt76_connac3_multi_header_v2_sec_raw_format) * + le32_to_cpu(hdr->sec_num) + + le32_to_cpu(hdr->sects[0].size); + dl = &hdr->global_desc_head[0]; + + ret = mt76_connac_cb_mcu_init_download(dev, len); + if (ret) { + dev_err(dev->dev, "Download cb cert section request failed\n"); + goto out; + } + + ret = __mt76_mcu_send_firmware(dev, MCU_CMD(FW_SCATTER), + dl, len, max_len); + if (ret) { + dev_err(dev->dev, "Failed to send cb patch cert section\n"); + goto out; + } + + ret = mt76_connac_cb_mcu_start_patch(dev); + if (ret) { + dev_err(dev->dev, "CBMCU cert check fail\n"); + goto out; + } + + /* download cbmcu idlm */ + for (i = 1; i < le32_to_cpu(hdr->sec_num); i++) { + dl = (uint8_t *)hdr + le32_to_cpu(hdr->sects[i].offset); + len = le32_to_cpu(hdr->sects[i].size); + + ret = mt76_connac_cb_mcu_init_download(dev, len); + if (ret) { + dev_err(dev->dev, + "Download cb section %u request failed\n", i); + goto out; + } + + ret = __mt76_mcu_send_firmware(dev, MCU_CMD(FW_SCATTER), + dl, len, max_len); + if (ret) { + dev_err(dev->dev, + "Failed to send cb patch section %u\n", i); + goto out; + } + } + + ret = mt76_connac_cb_mcu_start_patch(dev); + if (ret) + dev_err(dev->dev, "Failed to start cb patch\n"); + +out: + sem = mt76_connac_cb_mcu_patch_sem_ctrl(dev, false); + switch (sem) { + case CB_PATCH_REL_SEM_SUCCESS: + break; + default: + ret = -EAGAIN; + dev_err(dev->dev, "Failed to release cb patch semaphore\n"); + break; + } + + release_firmware(fw); + + return ret; +} +EXPORT_SYMBOL_GPL(mt76_connac3_load_cb_patch); + +int mt76_connac2_mcu_fill_message(struct mt76_dev *dev, struct sk_buff *skb, + int cmd, int *wait_seq) +{ + int txd_len, mcu_cmd = FIELD_GET(__MCU_CMD_FIELD_ID, cmd); + struct mt76_connac2_mcu_uni_txd *uni_txd; + struct mt76_connac2_mcu_txd *mcu_txd; + __le32 *txd; + u32 val; + u8 seq; + + /* TODO: make dynamic based on msg type */ + dev->mcu.timeout = 20 * HZ; + + seq = ++dev->mcu.msg_seq & 0xf; + if (!seq) + seq = ++dev->mcu.msg_seq & 0xf; + + if (cmd == MCU_CMD(FW_SCATTER)) + goto exit; + + txd_len = cmd & __MCU_CMD_FIELD_UNI ? sizeof(*uni_txd) : sizeof(*mcu_txd); + txd = (__le32 *)skb_push(skb, txd_len); + + val = FIELD_PREP(MT_TXD0_TX_BYTES, skb->len) | + FIELD_PREP(MT_TXD0_PKT_FMT, MT_TX_TYPE_CMD) | + FIELD_PREP(MT_TXD0_Q_IDX, MT_TX_MCU_PORT_RX_Q0); + txd[0] = cpu_to_le32(val); + + val = MT_TXD1_LONG_FORMAT | + FIELD_PREP(MT_TXD1_HDR_FORMAT, MT_HDR_FORMAT_CMD); + txd[1] = cpu_to_le32(val); + + if (cmd & __MCU_CMD_FIELD_UNI) { + uni_txd = (struct mt76_connac2_mcu_uni_txd *)txd; + uni_txd->len = cpu_to_le16(skb->len - sizeof(uni_txd->txd)); + uni_txd->option = MCU_CMD_UNI_EXT_ACK; + uni_txd->cid = cpu_to_le16(mcu_cmd); + uni_txd->s2d_index = MCU_S2D_H2N; + uni_txd->pkt_type = MCU_PKT_ID; + uni_txd->seq = seq; + + goto exit; + } + + mcu_txd = (struct mt76_connac2_mcu_txd *)txd; + mcu_txd->len = cpu_to_le16(skb->len - sizeof(mcu_txd->txd)); + mcu_txd->pq_id = cpu_to_le16(MCU_PQ_ID(MT_TX_PORT_IDX_MCU, + MT_TX_MCU_PORT_RX_Q0)); + mcu_txd->pkt_type = MCU_PKT_ID; + mcu_txd->seq = seq; + mcu_txd->cid = mcu_cmd; + mcu_txd->ext_cid = FIELD_GET(__MCU_CMD_FIELD_EXT_ID, cmd); + + if (mcu_txd->ext_cid || (cmd & __MCU_CMD_FIELD_CE)) { + if (cmd & __MCU_CMD_FIELD_QUERY) + mcu_txd->set_query = MCU_Q_QUERY; + else + mcu_txd->set_query = MCU_Q_SET; + mcu_txd->ext_cid_ack = !!mcu_txd->ext_cid; + } else { + mcu_txd->set_query = MCU_Q_NA; + } + + if (cmd & __MCU_CMD_FIELD_WA) + mcu_txd->s2d_index = MCU_S2D_H2C; + else + mcu_txd->s2d_index = MCU_S2D_H2N; + +exit: + if (wait_seq) + *wait_seq = seq; + + return 0; +} +EXPORT_SYMBOL_GPL(mt76_connac2_mcu_fill_message); + +MODULE_AUTHOR("Lorenzo Bianconi "); +MODULE_DESCRIPTION("MediaTek MT76x connac layer helpers"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h new file mode 100644 index 000000000..0ededa569 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h @@ -0,0 +1,2155 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* Copyright (C) 2020 MediaTek Inc. */ + +#ifndef __MT76_CONNAC_MCU_H +#define __MT76_CONNAC_MCU_H + +#include "mt76_connac.h" + +#define FW_FEATURE_SET_ENCRYPT BIT(0) +#define FW_FEATURE_SET_KEY_IDX GENMASK(2, 1) +#define FW_FEATURE_ENCRY_MODE BIT(4) +#define FW_FEATURE_OVERRIDE_ADDR BIT(5) +#define FW_FEATURE_NON_DL BIT(6) + +#define DL_MODE_ENCRYPT BIT(0) +#define DL_MODE_KEY_IDX GENMASK(2, 1) +#define DL_MODE_RESET_SEC_IV BIT(3) +#define DL_MODE_WORKING_PDA_CR4 BIT(4) +#define DL_MODE_VALID_RAM_ENTRY BIT(5) +#define DL_CONFIG_ENCRY_MODE_SEL BIT(6) +#define DL_MODE_NEED_RSP BIT(31) + +#define FW_START_OVERRIDE BIT(0) +#define FW_START_WORKING_PDA_CR4 BIT(2) +#define FW_START_WORKING_PDA_DSP BIT(3) + +#define PATCH_SEC_NOT_SUPPORT GENMASK(31, 0) +#define PATCH_SEC_TYPE_MASK GENMASK(15, 0) +#define PATCH_SEC_TYPE_INFO 0x2 + +#define PATCH_SEC_ENC_TYPE_MASK GENMASK(31, 24) +#define PATCH_SEC_ENC_TYPE_PLAIN 0x00 +#define PATCH_SEC_ENC_TYPE_AES 0x01 +#define PATCH_SEC_ENC_TYPE_SCRAMBLE 0x02 +#define PATCH_SEC_ENC_SCRAMBLE_INFO_MASK GENMASK(15, 0) +#define PATCH_SEC_ENC_AES_KEY_MASK GENMASK(7, 0) + +#define SEC_TYPE_SUBSYS_MASK GENMASK(23, 16) +#define SEC_TYPE_SUBSYS_CBMCU BIT(16) + +#define SEC_TYPE_SUBSYS_SEC_MASK GENMASK(15, 0) +#define SEC_TYPE_SUBSYS_SEC_IMG_SIGN 0x05 + +enum { + FW_TYPE_DEFAULT = 0, + FW_TYPE_CLC = 2, + FW_TYPE_MAX_NUM = 255 +}; + +#define MCU_PQ_ID(p, q) (((p) << 15) | ((q) << 10)) +#define MCU_PKT_ID 0xa0 + +struct mt76_connac2_mcu_txd { + __le32 txd[8]; + + __le16 len; + __le16 pq_id; + + u8 cid; + u8 pkt_type; + u8 set_query; /* FW don't care */ + u8 seq; + + u8 uc_d2b0_rev; + u8 ext_cid; + u8 s2d_index; + u8 ext_cid_ack; + + u32 rsv[5]; +} __packed __aligned(4); + +/** + * struct mt76_connac2_mcu_uni_txd - mcu command descriptor for connac2 and connac3 + * @txd: hardware descriptor + * @len: total length not including txd + * @cid: command identifier + * @pkt_type: must be 0xa0 (cmd packet by long format) + * @frag_n: fragment number + * @seq: sequence number + * @checksum: 0 mean there is no checksum + * @s2d_index: index for command source and destination + * Definition | value | note + * CMD_S2D_IDX_H2N | 0x00 | command from HOST to WM + * CMD_S2D_IDX_C2N | 0x01 | command from WA to WM + * CMD_S2D_IDX_H2C | 0x02 | command from HOST to WA + * CMD_S2D_IDX_H2N_AND_H2C | 0x03 | command from HOST to WA and WM + * + * @option: command option + * BIT[0]: UNI_CMD_OPT_BIT_ACK + * set to 1 to request a fw reply + * if UNI_CMD_OPT_BIT_0_ACK is set and UNI_CMD_OPT_BIT_2_SET_QUERY + * is set, mcu firmware will send response event EID = 0x01 + * (UNI_EVENT_ID_CMD_RESULT) to the host. + * BIT[1]: UNI_CMD_OPT_BIT_UNI_CMD + * 0: original command + * 1: unified command + * BIT[2]: UNI_CMD_OPT_BIT_SET_QUERY + * 0: QUERY command + * 1: SET command + */ +struct mt76_connac2_mcu_uni_txd { + __le32 txd[8]; + + /* DW1 */ + __le16 len; + __le16 cid; + + /* DW2 */ + u8 rsv; + u8 pkt_type; + u8 frag_n; + u8 seq; + + /* DW3 */ + __le16 checksum; + u8 s2d_index; + u8 option; + + /* DW4 */ + u8 rsv1[4]; +} __packed __aligned(4); + +struct mt76_connac2_mcu_rxd { + /* New members MUST be added within the struct_group() macro below. */ + struct_group_tagged(mt76_connac2_mcu_rxd_hdr, hdr, + __le32 rxd[6]; + + __le16 len; + __le16 pkt_type_id; + + u8 eid; + u8 seq; + u8 option; + u8 rsv; + u8 ext_eid; + u8 rsv1[2]; + u8 s2d_index; + ); + + u8 tlv[]; +}; +static_assert(offsetof(struct mt76_connac2_mcu_rxd, tlv) == sizeof(struct mt76_connac2_mcu_rxd_hdr), + "struct member likely outside of struct_group_tagged()"); + +struct mt76_connac2_patch_hdr { + char build_date[16]; + char platform[4]; + __be32 hw_sw_ver; + __be32 patch_ver; + __be16 checksum; + u16 rsv; + struct { + __be32 patch_ver; + __be32 subsys; + __be32 feature; + __be32 n_region; + __be32 crc; + u32 rsv[11]; + } desc; +} __packed; + +struct mt76_connac2_patch_sec { + __be32 type; + __be32 offs; + __be32 size; + union { + __be32 spec[13]; + struct { + __be32 addr; + __be32 len; + __be32 sec_key_idx; + __be32 align_len; + u32 rsv[9]; + } info; + }; +} __packed; + +struct mt76_connac2_fw_trailer { + u8 chip_id; + u8 eco_code; + u8 n_region; + u8 format_ver; + u8 format_flag; + u8 rsv[2]; + char fw_ver[10]; + char build_date[15]; + __le32 crc; +} __packed; + +struct mt76_connac2_fw_region { + __le32 decomp_crc; + __le32 decomp_len; + __le32 decomp_blk_sz; + u8 rsv[4]; + __le32 addr; + __le32 len; + u8 feature_set; + u8 type; + u8 rsv1[14]; +} __packed; + +struct mt76_connac3_multi_header_v2_sec_raw_format { + __le32 type; + __le32 offset; + __le32 size; + u8 spec[52]; +} __packed; + +struct mt76_connac3_multi_header_v2_raw_format { + u8 pack_time[20]; + u8 chip_id_eco_ver[8]; + __le32 patch_ver; + u8 global_desc_head[4]; + __le32 global_subsys; + u8 rsv2[4]; + __le32 sec_num; + u8 rsv3[48]; + struct mt76_connac3_multi_header_v2_sec_raw_format sects[]; +} __packed; + +struct tlv { + __le16 tag; + __le16 len; + u8 data[]; +} __packed; + +struct bss_info_omac { + __le16 tag; + __le16 len; + u8 hw_bss_idx; + u8 omac_idx; + u8 band_idx; + u8 rsv0; + __le32 conn_type; + u32 rsv1; +} __packed; + +struct bss_info_basic { + __le16 tag; + __le16 len; + __le32 network_type; + u8 active; + u8 rsv0; + __le16 bcn_interval; + u8 bssid[ETH_ALEN]; + u8 wmm_idx; + u8 dtim_period; + u8 bmc_wcid_lo; + u8 cipher; + u8 phy_mode; + u8 max_bssid; /* max BSSID. range: 1 ~ 8, 0: MBSSID disabled */ + u8 non_tx_bssid;/* non-transmitted BSSID, 0: transmitted BSSID */ + u8 bmc_wcid_hi; /* high Byte and version */ + u8 rsv[2]; +} __packed; + +struct bss_info_rf_ch { + __le16 tag; + __le16 len; + u8 pri_ch; + u8 center_ch0; + u8 center_ch1; + u8 bw; + u8 he_ru26_block; /* 1: don't send HETB in RU26, 0: allow */ + u8 he_all_disable; /* 1: disallow all HETB, 0: allow */ + u8 rsv[2]; +} __packed; + +struct bss_info_ext_bss { + __le16 tag; + __le16 len; + __le32 mbss_tsf_offset; /* in unit of us */ + u8 rsv[8]; +} __packed; + +enum { + BSS_INFO_OMAC, + BSS_INFO_BASIC, + BSS_INFO_RF_CH, /* optional, for BT/LTE coex */ + BSS_INFO_PM, /* sta only */ + BSS_INFO_UAPSD, /* sta only */ + BSS_INFO_ROAM_DETECT, /* obsoleted */ + BSS_INFO_LQ_RM, /* obsoleted */ + BSS_INFO_EXT_BSS, + BSS_INFO_BMC_RATE, /* for bmc rate control in CR4 */ + BSS_INFO_SYNC_MODE, /* obsoleted */ + BSS_INFO_RA, + BSS_INFO_HW_AMSDU, + BSS_INFO_BSS_COLOR, + BSS_INFO_HE_BASIC, + BSS_INFO_PROTECT_INFO, + BSS_INFO_OFFLOAD, + BSS_INFO_11V_MBSSID, + BSS_INFO_MAX_NUM +}; + +/* sta_rec */ + +struct sta_ntlv_hdr { + u8 rsv[2]; + __le16 tlv_num; +} __packed; + +struct sta_req_hdr { + u8 bss_idx; + u8 wlan_idx_lo; + __le16 tlv_num; + u8 is_tlv_append; + u8 muar_idx; + u8 wlan_idx_hi; + u8 rsv; +} __packed; + +struct sta_rec_basic { + __le16 tag; + __le16 len; + __le32 conn_type; + u8 conn_state; + u8 qos; + __le16 aid; + u8 peer_addr[ETH_ALEN]; +#define EXTRA_INFO_VER BIT(0) +#define EXTRA_INFO_NEW BIT(1) + __le16 extra_info; +} __packed; + +struct sta_rec_ht { + __le16 tag; + __le16 len; + __le16 ht_cap; + u16 rsv; +} __packed; + +struct sta_rec_vht { + __le16 tag; + __le16 len; + __le32 vht_cap; + __le16 vht_rx_mcs_map; + __le16 vht_tx_mcs_map; + /* mt7915 - mt7921 */ + u8 rts_bw_sig; + u8 rsv[3]; +} __packed; + +struct sta_rec_uapsd { + __le16 tag; + __le16 len; + u8 dac_map; + u8 tac_map; + u8 max_sp; + u8 rsv0; + __le16 listen_interval; + u8 rsv1[2]; +} __packed; + +struct sta_rec_ba { + __le16 tag; + __le16 len; + u8 tid; + u8 ba_type; + u8 amsdu; + u8 ba_en; + __le16 ssn; + __le16 winsize; +} __packed; + +struct sta_rec_he { + __le16 tag; + __le16 len; + + __le32 he_cap; + + u8 t_frame_dur; + u8 max_ampdu_exp; + u8 bw_set; + u8 device_class; + u8 dcm_tx_mode; + u8 dcm_tx_max_nss; + u8 dcm_rx_mode; + u8 dcm_rx_max_nss; + u8 dcm_max_ru; + u8 punc_pream_rx; + u8 pkt_ext; + u8 rsv1; + + __le16 max_nss_mcs[CMD_HE_MCS_BW_NUM]; + + u8 rsv2[2]; +} __packed; + +struct sta_rec_he_v2 { + __le16 tag; + __le16 len; + u8 he_mac_cap[6]; + u8 he_phy_cap[11]; + u8 pkt_ext; + /* 0: BW80, 1: BW160, 2: BW8080 */ + __le16 max_nss_mcs[CMD_HE_MCS_BW_NUM]; +} __packed; + +struct sta_rec_amsdu { + __le16 tag; + __le16 len; + u8 max_amsdu_num; + u8 max_mpdu_size; + u8 amsdu_en; + u8 rsv; +} __packed; + +struct sta_rec_state { + __le16 tag; + __le16 len; + __le32 flags; + u8 state; + u8 vht_opmode; + u8 action; + u8 rsv[1]; +} __packed; + +#define RA_LEGACY_OFDM GENMASK(13, 6) +#define RA_LEGACY_CCK GENMASK(3, 0) +#define HT_MCS_MASK_NUM 10 +struct sta_rec_ra_info { + __le16 tag; + __le16 len; + __le16 legacy; + u8 rx_mcs_bitmask[HT_MCS_MASK_NUM]; +} __packed; + +struct sta_rec_phy { + __le16 tag; + __le16 len; + __le16 basic_rate; + u8 phy_type; + u8 ampdu; + u8 rts_policy; + u8 rcpi; + u8 max_ampdu_len; /* connac3 */ + u8 rsv[1]; +} __packed; + +struct sta_rec_he_6g_capa { + __le16 tag; + __le16 len; + __le16 capa; + u8 rsv[2]; +} __packed; + +struct sta_rec_pn_info { + __le16 tag; + __le16 len; + u8 pn[6]; + u8 tsc_type; + u8 rsv; +} __packed; + +struct sec_key { + u8 cipher_id; + u8 cipher_len; + u8 key_id; + u8 key_len; + u8 key[32]; +} __packed; + +struct sta_rec_sec { + __le16 tag; + __le16 len; + u8 add; + u8 n_cipher; + u8 rsv[2]; + + struct sec_key key[2]; +} __packed; + +struct sta_rec_bf { + __le16 tag; + __le16 len; + + __le16 pfmu; /* 0xffff: no access right for PFMU */ + bool su_mu; /* 0: SU, 1: MU */ + u8 bf_cap; /* 0: iBF, 1: eBF */ + u8 sounding_phy; /* 0: legacy, 1: OFDM, 2: HT, 4: VHT */ + u8 ndpa_rate; + u8 ndp_rate; + u8 rept_poll_rate; + u8 tx_mode; /* 0: legacy, 1: OFDM, 2: HT, 4: VHT ... */ + u8 ncol; + u8 nrow; + u8 bw; /* 0: 20M, 1: 40M, 2: 80M, 3: 160M */ + + u8 mem_total; + u8 mem_20m; + struct { + u8 row; + u8 col: 6, row_msb: 2; + } mem[4]; + + __le16 smart_ant; + u8 se_idx; + u8 auto_sounding; /* b7: low traffic indicator + * b6: Stop sounding for this entry + * b5 ~ b0: postpone sounding + */ + u8 ibf_timeout; + u8 ibf_dbw; + u8 ibf_ncol; + u8 ibf_nrow; + u8 nrow_gt_bw80; + u8 ncol_gt_bw80; + u8 ru_start_idx; + u8 ru_end_idx; + + bool trigger_su; + bool trigger_mu; + bool ng16_su; + bool ng16_mu; + bool codebook42_su; + bool codebook75_mu; + + u8 he_ltf; + u8 rsv[3]; +} __packed; + +struct sta_rec_bfee { + __le16 tag; + __le16 len; + bool fb_identity_matrix; /* 1: feedback identity matrix */ + bool ignore_feedback; /* 1: ignore */ + u8 rsv[2]; +} __packed; + +struct sta_rec_muru { + __le16 tag; + __le16 len; + + struct { + bool ofdma_dl_en; + bool ofdma_ul_en; + bool mimo_dl_en; + bool mimo_ul_en; + u8 rsv[4]; + } cfg; + + struct { + u8 punc_pream_rx; + bool he_20m_in_40m_2g; + bool he_20m_in_160m; + bool he_80m_in_160m; + bool lt16_sigb; + bool rx_su_comp_sigb; + bool rx_su_non_comp_sigb; + u8 rsv; + } ofdma_dl; + + struct { + u8 t_frame_dur; + u8 mu_cascading; + u8 uo_ra; + u8 he_2x996_tone; + u8 rx_t_frame_11ac; + u8 rx_ctrl_frame_to_mbss; + u8 rsv[2]; + } ofdma_ul; + + struct { + bool vht_mu_bfee; + bool partial_bw_dl_mimo; + u8 rsv[2]; + } mimo_dl; + + struct { + bool full_ul_mimo; + bool partial_ul_mimo; + u8 rsv[2]; + } mimo_ul; +} __packed; + +struct sta_rec_remove { + __le16 tag; + __le16 len; + u8 action; + u8 pad[3]; +} __packed; + +struct sta_phy { + u8 type; + u8 flag; + u8 stbc; + u8 sgi; + u8 bw; + u8 ldpc; + u8 mcs; + u8 nss; + u8 he_ltf; +}; + +struct sta_rec_ra { + __le16 tag; + __le16 len; + + u8 valid; + u8 auto_rate; + u8 phy_mode; + u8 channel; + u8 bw; + u8 disable_cck; + u8 ht_mcs32; + u8 ht_gf; + u8 ht_mcs[4]; + u8 mmps_mode; + u8 gband_256; + u8 af; + u8 auth_wapi_mode; + u8 rate_len; + + u8 supp_mode; + u8 supp_cck_rate; + u8 supp_ofdm_rate; + __le32 supp_ht_mcs; + __le16 supp_vht_mcs[4]; + + u8 op_mode; + u8 op_vht_chan_width; + u8 op_vht_rx_nss; + u8 op_vht_rx_nss_type; + + __le32 sta_cap; + + struct sta_phy phy; +} __packed; + +struct sta_rec_ra_fixed { + __le16 tag; + __le16 len; + + __le32 field; + u8 op_mode; + u8 op_vht_chan_width; + u8 op_vht_rx_nss; + u8 op_vht_rx_nss_type; + + struct sta_phy phy; + + u8 spe_idx; + u8 short_preamble; + u8 is_5g; + u8 mmps_mode; +} __packed; + +struct sta_rec_tx_proc { + __le16 tag; + __le16 len; + __le32 flag; +} __packed; + +struct sta_rec_eml_op { + __le16 tag; + __le16 len; + u8 link_bitmap; + u8 link_ant_num[3]; +} __packed; + +/* wtbl_rec */ + +struct wtbl_req_hdr { + u8 wlan_idx_lo; + u8 operation; + __le16 tlv_num; + u8 wlan_idx_hi; + u8 rsv[3]; +} __packed; + +struct wtbl_generic { + __le16 tag; + __le16 len; + u8 peer_addr[ETH_ALEN]; + u8 muar_idx; + u8 skip_tx; + u8 cf_ack; + u8 qos; + u8 mesh; + u8 adm; + __le16 partial_aid; + u8 baf_en; + u8 aad_om; +} __packed; + +struct wtbl_rx { + __le16 tag; + __le16 len; + u8 rcid; + u8 rca1; + u8 rca2; + u8 rv; + u8 rsv[4]; +} __packed; + +struct wtbl_ht { + __le16 tag; + __le16 len; + u8 ht; + u8 ldpc; + u8 af; + u8 mm; + u8 rsv[4]; +} __packed; + +struct wtbl_vht { + __le16 tag; + __le16 len; + u8 ldpc; + u8 dyn_bw; + u8 vht; + u8 txop_ps; + u8 rsv[4]; +} __packed; + +struct wtbl_tx_ps { + __le16 tag; + __le16 len; + u8 txps; + u8 rsv[3]; +} __packed; + +struct wtbl_hdr_trans { + __le16 tag; + __le16 len; + u8 to_ds; + u8 from_ds; + u8 no_rx_trans; + u8 rsv; +} __packed; + +struct wtbl_ba { + __le16 tag; + __le16 len; + /* common */ + u8 tid; + u8 ba_type; + u8 rsv0[2]; + /* originator only */ + __le16 sn; + u8 ba_en; + u8 ba_winsize_idx; + /* originator & recipient */ + __le16 ba_winsize; + /* recipient only */ + u8 peer_addr[ETH_ALEN]; + u8 rst_ba_tid; + u8 rst_ba_sel; + u8 rst_ba_sb; + u8 band_idx; + u8 rsv1[4]; +} __packed; + +struct wtbl_smps { + __le16 tag; + __le16 len; + u8 smps; + u8 rsv[3]; +} __packed; + +/* mt7615 only */ + +struct wtbl_bf { + __le16 tag; + __le16 len; + u8 ibf; + u8 ebf; + u8 ibf_vht; + u8 ebf_vht; + u8 gid; + u8 pfmu_idx; + u8 rsv[2]; +} __packed; + +struct wtbl_pn { + __le16 tag; + __le16 len; + u8 pn[6]; + u8 rsv[2]; +} __packed; + +struct wtbl_spe { + __le16 tag; + __le16 len; + u8 spe_idx; + u8 rsv[3]; +} __packed; + +struct wtbl_raw { + __le16 tag; + __le16 len; + u8 wtbl_idx; + u8 dw; + u8 rsv[2]; + __le32 msk; + __le32 val; +} __packed; + +#define MT76_CONNAC_WTBL_UPDATE_MAX_SIZE (sizeof(struct wtbl_req_hdr) + \ + sizeof(struct wtbl_generic) + \ + sizeof(struct wtbl_rx) + \ + sizeof(struct wtbl_ht) + \ + sizeof(struct wtbl_vht) + \ + sizeof(struct wtbl_tx_ps) + \ + sizeof(struct wtbl_hdr_trans) +\ + sizeof(struct wtbl_ba) + \ + sizeof(struct wtbl_bf) + \ + sizeof(struct wtbl_smps) + \ + sizeof(struct wtbl_pn) + \ + sizeof(struct wtbl_spe)) + +#define MT76_CONNAC_STA_UPDATE_MAX_SIZE (sizeof(struct sta_req_hdr) + \ + sizeof(struct sta_rec_basic) + \ + sizeof(struct sta_rec_bf) + \ + sizeof(struct sta_rec_ht) + \ + sizeof(struct sta_rec_he) + \ + sizeof(struct sta_rec_ba) + \ + sizeof(struct sta_rec_vht) + \ + sizeof(struct sta_rec_uapsd) + \ + sizeof(struct sta_rec_amsdu) + \ + sizeof(struct sta_rec_muru) + \ + sizeof(struct sta_rec_bfee) + \ + sizeof(struct sta_rec_ra) + \ + sizeof(struct sta_rec_sec) + \ + sizeof(struct sta_rec_ra_fixed) + \ + sizeof(struct sta_rec_he_6g_capa) + \ + sizeof(struct sta_rec_pn_info) + \ + sizeof(struct sta_rec_tx_proc) + \ + sizeof(struct sta_rec_eml_op) + \ + sizeof(struct tlv) + \ + MT76_CONNAC_WTBL_UPDATE_MAX_SIZE) + +enum { + STA_REC_BASIC, + STA_REC_RA, + STA_REC_RA_CMM_INFO, + STA_REC_RA_UPDATE, + STA_REC_BF, + STA_REC_AMSDU, + STA_REC_BA, + STA_REC_STATE, + STA_REC_TX_PROC, /* for hdr trans and CSO in CR4 */ + STA_REC_HT, + STA_REC_VHT, + STA_REC_APPS, + STA_REC_KEY, + STA_REC_WTBL, + STA_REC_HE, + STA_REC_HW_AMSDU, + STA_REC_WTBL_AADOM, + STA_REC_KEY_V2, + STA_REC_MURU, + STA_REC_MUEDCA, + STA_REC_BFEE, + STA_REC_PHY = 0x15, + STA_REC_HE_6G = 0x17, + STA_REC_HE_V2 = 0x19, + STA_REC_MLD = 0x20, + STA_REC_EHT_MLD = 0x21, + STA_REC_EHT = 0x22, + STA_REC_MLD_OFF = 0x23, + STA_REC_REMOVE = 0x25, + STA_REC_PN_INFO = 0x26, + STA_REC_KEY_V3 = 0x27, + STA_REC_HDRT = 0x28, + STA_REC_EML_OP = 0x29, + STA_REC_HDR_TRANS = 0x2B, + STA_REC_PS_LEAVE = 0x45, + STA_REC_MAX_NUM +}; + +enum { + WTBL_GENERIC, + WTBL_RX, + WTBL_HT, + WTBL_VHT, + WTBL_PEER_PS, /* not used */ + WTBL_TX_PS, + WTBL_HDR_TRANS, + WTBL_SEC_KEY, + WTBL_BA, + WTBL_RDG, /* obsoleted */ + WTBL_PROTECT, /* not used */ + WTBL_CLEAR, /* not used */ + WTBL_BF, + WTBL_SMPS, + WTBL_RAW_DATA, /* debug only */ + WTBL_PN, + WTBL_SPE, + WTBL_MAX_NUM +}; + +#define STA_TYPE_STA BIT(0) +#define STA_TYPE_AP BIT(1) +#define STA_TYPE_ADHOC BIT(2) +#define STA_TYPE_WDS BIT(4) +#define STA_TYPE_BC BIT(5) + +#define NETWORK_INFRA BIT(16) +#define NETWORK_P2P BIT(17) +#define NETWORK_IBSS BIT(18) +#define NETWORK_WDS BIT(21) +#define NETWORK_NAN BIT(22) + +#define SCAN_FUNC_RANDOM_MAC BIT(0) +#define SCAN_FUNC_RNR_SCAN BIT(3) +#define SCAN_FUNC_SPLIT_SCAN BIT(5) + +#define CONNECTION_INFRA_STA (STA_TYPE_STA | NETWORK_INFRA) +#define CONNECTION_INFRA_AP (STA_TYPE_AP | NETWORK_INFRA) +#define CONNECTION_P2P_GC (STA_TYPE_STA | NETWORK_P2P) +#define CONNECTION_P2P_GO (STA_TYPE_AP | NETWORK_P2P) +#define CONNECTION_IBSS_ADHOC (STA_TYPE_ADHOC | NETWORK_IBSS) +#define CONNECTION_WDS (STA_TYPE_WDS | NETWORK_WDS) +#define CONNECTION_INFRA_BC (STA_TYPE_BC | NETWORK_INFRA) +#define CONNECTION_NAN (NETWORK_NAN) + +#define CONN_STATE_DISCONNECT 0 +#define CONN_STATE_CONNECT 1 +#define CONN_STATE_PORT_SECURE 2 + +/* HE MAC */ +#define STA_REC_HE_CAP_HTC BIT(0) +#define STA_REC_HE_CAP_BQR BIT(1) +#define STA_REC_HE_CAP_BSR BIT(2) +#define STA_REC_HE_CAP_OM BIT(3) +#define STA_REC_HE_CAP_AMSDU_IN_AMPDU BIT(4) +/* HE PHY */ +#define STA_REC_HE_CAP_DUAL_BAND BIT(5) +#define STA_REC_HE_CAP_LDPC BIT(6) +#define STA_REC_HE_CAP_TRIG_CQI_FK BIT(7) +#define STA_REC_HE_CAP_PARTIAL_BW_EXT_RANGE BIT(8) +/* STBC */ +#define STA_REC_HE_CAP_LE_EQ_80M_TX_STBC BIT(9) +#define STA_REC_HE_CAP_LE_EQ_80M_RX_STBC BIT(10) +#define STA_REC_HE_CAP_GT_80M_TX_STBC BIT(11) +#define STA_REC_HE_CAP_GT_80M_RX_STBC BIT(12) +/* GI */ +#define STA_REC_HE_CAP_SU_PPDU_1LTF_8US_GI BIT(13) +#define STA_REC_HE_CAP_SU_MU_PPDU_4LTF_8US_GI BIT(14) +#define STA_REC_HE_CAP_ER_SU_PPDU_1LTF_8US_GI BIT(15) +#define STA_REC_HE_CAP_ER_SU_PPDU_4LTF_8US_GI BIT(16) +#define STA_REC_HE_CAP_NDP_4LTF_3DOT2MS_GI BIT(17) +/* 242 TONE */ +#define STA_REC_HE_CAP_BW20_RU242_SUPPORT BIT(18) +#define STA_REC_HE_CAP_TX_1024QAM_UNDER_RU242 BIT(19) +#define STA_REC_HE_CAP_RX_1024QAM_UNDER_RU242 BIT(20) + +#define PHY_MODE_A BIT(0) +#define PHY_MODE_B BIT(1) +#define PHY_MODE_G BIT(2) +#define PHY_MODE_GN BIT(3) +#define PHY_MODE_AN BIT(4) +#define PHY_MODE_AC BIT(5) +#define PHY_MODE_AX_24G BIT(6) +#define PHY_MODE_AX_5G BIT(7) + +#define PHY_MODE_AX_6G BIT(0) /* phymode_ext */ +#define PHY_MODE_BE_24G BIT(1) +#define PHY_MODE_BE_5G BIT(2) +#define PHY_MODE_BE_6G BIT(3) + +#define MODE_CCK BIT(0) +#define MODE_OFDM BIT(1) +#define MODE_HT BIT(2) +#define MODE_VHT BIT(3) +#define MODE_HE BIT(4) +#define MODE_EHT BIT(5) + +#define STA_CAP_WMM BIT(0) +#define STA_CAP_SGI_20 BIT(4) +#define STA_CAP_SGI_40 BIT(5) +#define STA_CAP_TX_STBC BIT(6) +#define STA_CAP_RX_STBC BIT(7) +#define STA_CAP_VHT_SGI_80 BIT(16) +#define STA_CAP_VHT_SGI_160 BIT(17) +#define STA_CAP_VHT_TX_STBC BIT(18) +#define STA_CAP_VHT_RX_STBC BIT(19) +#define STA_CAP_VHT_LDPC BIT(23) +#define STA_CAP_LDPC BIT(24) +#define STA_CAP_HT BIT(26) +#define STA_CAP_VHT BIT(27) +#define STA_CAP_HE BIT(28) + +enum { + PHY_TYPE_HR_DSSS_INDEX = 0, + PHY_TYPE_ERP_INDEX, + PHY_TYPE_ERP_P2P_INDEX, + PHY_TYPE_OFDM_INDEX, + PHY_TYPE_HT_INDEX, + PHY_TYPE_VHT_INDEX, + PHY_TYPE_HE_INDEX, + PHY_TYPE_BE_INDEX, + PHY_TYPE_INDEX_NUM +}; + +#define HR_DSSS_ERP_BASIC_RATE GENMASK(3, 0) +#define OFDM_BASIC_RATE (BIT(6) | BIT(8) | BIT(10)) + +#define PHY_TYPE_BIT_HR_DSSS BIT(PHY_TYPE_HR_DSSS_INDEX) +#define PHY_TYPE_BIT_ERP BIT(PHY_TYPE_ERP_INDEX) +#define PHY_TYPE_BIT_OFDM BIT(PHY_TYPE_OFDM_INDEX) +#define PHY_TYPE_BIT_HT BIT(PHY_TYPE_HT_INDEX) +#define PHY_TYPE_BIT_VHT BIT(PHY_TYPE_VHT_INDEX) +#define PHY_TYPE_BIT_HE BIT(PHY_TYPE_HE_INDEX) +#define PHY_TYPE_BIT_BE BIT(PHY_TYPE_BE_INDEX) + +#define MT_WTBL_RATE_TX_MODE GENMASK(9, 6) +#define MT_WTBL_RATE_MCS GENMASK(5, 0) +#define MT_WTBL_RATE_NSS GENMASK(12, 10) +#define MT_WTBL_RATE_HE_GI GENMASK(7, 4) +#define MT_WTBL_RATE_GI GENMASK(3, 0) + +#define MT_WTBL_W5_CHANGE_BW_RATE GENMASK(7, 5) +#define MT_WTBL_W5_SHORT_GI_20 BIT(8) +#define MT_WTBL_W5_SHORT_GI_40 BIT(9) +#define MT_WTBL_W5_SHORT_GI_80 BIT(10) +#define MT_WTBL_W5_SHORT_GI_160 BIT(11) +#define MT_WTBL_W5_BW_CAP GENMASK(13, 12) +#define MT_WTBL_W5_MPDU_FAIL_COUNT GENMASK(25, 23) +#define MT_WTBL_W5_MPDU_OK_COUNT GENMASK(28, 26) +#define MT_WTBL_W5_RATE_IDX GENMASK(31, 29) + +enum { + WTBL_RESET_AND_SET = 1, + WTBL_SET, + WTBL_QUERY, + WTBL_RESET_ALL +}; + +enum { + MT_BA_TYPE_INVALID, + MT_BA_TYPE_ORIGINATOR, + MT_BA_TYPE_RECIPIENT +}; + +enum { + RST_BA_MAC_TID_MATCH, + RST_BA_MAC_MATCH, + RST_BA_NO_MATCH +}; + +enum { + DEV_INFO_ACTIVE, + DEV_INFO_MAX_NUM +}; + +/* event table */ +enum { + MCU_EVENT_TARGET_ADDRESS_LEN = 0x01, + MCU_EVENT_FW_START = 0x01, + MCU_EVENT_GENERIC = 0x01, + MCU_EVENT_ACCESS_REG = 0x02, + MCU_EVENT_MT_PATCH_SEM = 0x04, + MCU_EVENT_REG_ACCESS = 0x05, + MCU_EVENT_LP_INFO = 0x07, + MCU_EVENT_SCAN_DONE = 0x0d, + MCU_EVENT_TX_DONE = 0x0f, + MCU_EVENT_ROC = 0x10, + MCU_EVENT_BSS_ABSENCE = 0x11, + MCU_EVENT_BSS_BEACON_LOSS = 0x13, + MCU_EVENT_CH_PRIVILEGE = 0x18, + MCU_EVENT_SCHED_SCAN_DONE = 0x23, + MCU_EVENT_DBG_MSG = 0x27, + MCU_EVENT_RSSI_NOTIFY = 0x96, + MCU_EVENT_TXPWR = 0xd0, + MCU_EVENT_EXT = 0xed, + MCU_EVENT_RESTART_DL = 0xef, + MCU_EVENT_COREDUMP = 0xf0, +}; + +/* ext event table */ +enum { + MCU_EXT_EVENT_PS_SYNC = 0x5, + MCU_EXT_EVENT_FW_LOG_2_HOST = 0x13, + MCU_EXT_EVENT_THERMAL_PROTECT = 0x22, + MCU_EXT_EVENT_ASSERT_DUMP = 0x23, + MCU_EXT_EVENT_RDD_REPORT = 0x3a, + MCU_EXT_EVENT_CSA_NOTIFY = 0x4f, + MCU_EXT_EVENT_WA_TX_STAT = 0x74, + MCU_EXT_EVENT_BCC_NOTIFY = 0x75, + MCU_EXT_EVENT_WF_RF_PIN_CTRL = 0x9a, + MCU_EXT_EVENT_MURU_CTRL = 0x9f, +}; + +/* unified event table */ +enum { + MCU_UNI_EVENT_RESULT = 0x01, + MCU_UNI_EVENT_HIF_CTRL = 0x03, + MCU_UNI_EVENT_FW_LOG_2_HOST = 0x04, + MCU_UNI_EVENT_ACCESS_REG = 0x6, + MCU_UNI_EVENT_IE_COUNTDOWN = 0x09, + MCU_UNI_EVENT_COREDUMP = 0x0a, + MCU_UNI_EVENT_BSS_BEACON_LOSS = 0x0c, + MCU_UNI_EVENT_PS_SYNC = 0x0d, + MCU_UNI_EVENT_SCAN_DONE = 0x0e, + MCU_UNI_EVENT_RDD_REPORT = 0x11, + MCU_UNI_EVENT_ROC = 0x27, + MCU_UNI_EVENT_MBMC = 0x28, + MCU_UNI_EVENT_TX_DONE = 0x2d, + MCU_UNI_EVENT_THERMAL = 0x35, + MCU_UNI_EVENT_RSSI_MONITOR = 0x41, + MCU_UNI_EVENT_NIC_CAPAB = 0x43, + MCU_UNI_EVENT_NAN = 0x56, + MCU_UNI_EVENT_WED_RRO = 0x57, + MCU_UNI_EVENT_PER_STA_INFO = 0x6d, + MCU_UNI_EVENT_ALL_STA_INFO = 0x6e, + MCU_UNI_EVENT_SDO = 0x83, +}; + +#define MCU_UNI_CMD_EVENT BIT(1) +#define MCU_UNI_CMD_UNSOLICITED_EVENT BIT(2) + +enum { + MCU_Q_QUERY, + MCU_Q_SET, + MCU_Q_RESERVED, + MCU_Q_NA +}; + +enum { + MCU_S2D_H2N, + MCU_S2D_C2N, + MCU_S2D_H2C, + MCU_S2D_H2CN +}; + +enum { + PATCH_NOT_DL_SEM_FAIL, + PATCH_IS_DL, + PATCH_NOT_DL_SEM_SUCCESS, + PATCH_REL_SEM_SUCCESS +}; + +enum { + CB_PATCH_GET_SEM_NEED_PATCH, + CB_PATCH_IS_DL, + CB_PATCH_NO_SEM_NEED_PATCH, + CB_PATCH_REL_SEM_SUCCESS +}; + +enum { + FW_STATE_INITIAL, + FW_STATE_FW_DOWNLOAD, + FW_STATE_NORMAL_OPERATION, + FW_STATE_NORMAL_TRX, + FW_STATE_RDY = 7 +}; + +enum { + CH_SWITCH_NORMAL = 0, + CH_SWITCH_SCAN = 3, + CH_SWITCH_MCC = 4, + CH_SWITCH_DFS = 5, + CH_SWITCH_BACKGROUND_SCAN_START = 6, + CH_SWITCH_BACKGROUND_SCAN_RUNNING = 7, + CH_SWITCH_BACKGROUND_SCAN_STOP = 8, + CH_SWITCH_SCAN_BYPASS_DPD = 9 +}; + +enum { + THERMAL_SENSOR_TEMP_QUERY, + THERMAL_SENSOR_MANUAL_CTRL, + THERMAL_SENSOR_INFO_QUERY, + THERMAL_SENSOR_TASK_CTRL, +}; + +enum mcu_cipher_type { + MCU_CIPHER_NONE = 0, + MCU_CIPHER_WEP40, + MCU_CIPHER_WEP104, + MCU_CIPHER_WEP128, + MCU_CIPHER_TKIP, + MCU_CIPHER_AES_CCMP, + MCU_CIPHER_CCMP_256, + MCU_CIPHER_GCMP, + MCU_CIPHER_GCMP_256, + MCU_CIPHER_WAPI, + MCU_CIPHER_BIP_CMAC_128, + MCU_CIPHER_BIP_CMAC_256, + MCU_CIPHER_BCN_PROT_CMAC_128, + MCU_CIPHER_BCN_PROT_CMAC_256, + MCU_CIPHER_BCN_PROT_GMAC_128, + MCU_CIPHER_BCN_PROT_GMAC_256, + MCU_CIPHER_BIP_GMAC_128, + MCU_CIPHER_BIP_GMAC_256, +}; + +enum { + EE_MODE_EFUSE, + EE_MODE_BUFFER, +}; + +enum { + EE_FORMAT_BIN, + EE_FORMAT_WHOLE, + EE_FORMAT_MULTIPLE, +}; + +enum { + MCU_PHY_STATE_TX_RATE, + MCU_PHY_STATE_RX_RATE, + MCU_PHY_STATE_RSSI, + MCU_PHY_STATE_CONTENTION_RX_RATE, + MCU_PHY_STATE_OFDMLQ_CNINFO, +}; + +#define MCU_CMD_ACK BIT(0) +#define MCU_CMD_UNI BIT(1) +#define MCU_CMD_SET BIT(2) + +#define MCU_CMD_UNI_EXT_ACK (MCU_CMD_ACK | MCU_CMD_UNI | \ + MCU_CMD_SET) +#define MCU_CMD_UNI_QUERY_ACK (MCU_CMD_ACK | MCU_CMD_UNI) + +#define __MCU_CMD_FIELD_ID GENMASK(7, 0) +#define __MCU_CMD_FIELD_EXT_ID GENMASK(15, 8) +#define __MCU_CMD_FIELD_QUERY BIT(16) +#define __MCU_CMD_FIELD_UNI BIT(17) +#define __MCU_CMD_FIELD_CE BIT(18) +#define __MCU_CMD_FIELD_WA BIT(19) +#define __MCU_CMD_FIELD_WM BIT(20) + +#define MCU_CMD(_t) FIELD_PREP(__MCU_CMD_FIELD_ID, \ + MCU_CMD_##_t) +#define MCU_EXT_CMD(_t) (MCU_CMD(EXT_CID) | \ + FIELD_PREP(__MCU_CMD_FIELD_EXT_ID, \ + MCU_EXT_CMD_##_t)) +#define MCU_EXT_QUERY(_t) (MCU_EXT_CMD(_t) | __MCU_CMD_FIELD_QUERY) +#define MCU_UNI_CMD(_t) (__MCU_CMD_FIELD_UNI | \ + FIELD_PREP(__MCU_CMD_FIELD_ID, \ + MCU_UNI_CMD_##_t)) + +#define MCU_UNI_QUERY(_t) (__MCU_CMD_FIELD_UNI | __MCU_CMD_FIELD_QUERY | \ + FIELD_PREP(__MCU_CMD_FIELD_ID, \ + MCU_UNI_CMD_##_t)) + +#define MCU_CE_CMD(_t) (__MCU_CMD_FIELD_CE | \ + FIELD_PREP(__MCU_CMD_FIELD_ID, \ + MCU_CE_CMD_##_t)) +#define MCU_CE_QUERY(_t) (MCU_CE_CMD(_t) | __MCU_CMD_FIELD_QUERY) + +#define MCU_WA_CMD(_t) (MCU_CMD(_t) | __MCU_CMD_FIELD_WA) +#define MCU_WA_EXT_CMD(_t) (MCU_EXT_CMD(_t) | __MCU_CMD_FIELD_WA) +#define MCU_WA_PARAM_CMD(_t) (MCU_WA_CMD(WA_PARAM) | \ + FIELD_PREP(__MCU_CMD_FIELD_EXT_ID, \ + MCU_WA_PARAM_CMD_##_t)) + +#define MCU_WM_UNI_CMD(_t) (MCU_UNI_CMD(_t) | \ + __MCU_CMD_FIELD_WM) +#define MCU_WM_UNI_CMD_QUERY(_t) (MCU_UNI_CMD(_t) | \ + __MCU_CMD_FIELD_QUERY | \ + __MCU_CMD_FIELD_WM) +#define MCU_WA_UNI_CMD(_t) (MCU_UNI_CMD(_t) | \ + __MCU_CMD_FIELD_WA) +#define MCU_WMWA_UNI_CMD(_t) (MCU_WM_UNI_CMD(_t) | \ + __MCU_CMD_FIELD_WA) + +enum { + MCU_EXT_CMD_EFUSE_ACCESS = 0x01, + MCU_EXT_CMD_RF_REG_ACCESS = 0x02, + MCU_EXT_CMD_RF_TEST = 0x04, + MCU_EXT_CMD_ID_RADIO_ON_OFF_CTRL = 0x05, + MCU_EXT_CMD_PM_STATE_CTRL = 0x07, + MCU_EXT_CMD_CHANNEL_SWITCH = 0x08, + MCU_EXT_CMD_SET_TX_POWER_CTRL = 0x11, + MCU_EXT_CMD_FW_LOG_2_HOST = 0x13, + MCU_EXT_CMD_TXBF_ACTION = 0x1e, + MCU_EXT_CMD_EFUSE_BUFFER_MODE = 0x21, + MCU_EXT_CMD_THERMAL_PROT = 0x23, + MCU_EXT_CMD_STA_REC_UPDATE = 0x25, + MCU_EXT_CMD_BSS_INFO_UPDATE = 0x26, + MCU_EXT_CMD_EDCA_UPDATE = 0x27, + MCU_EXT_CMD_DEV_INFO_UPDATE = 0x2A, + MCU_EXT_CMD_THERMAL_CTRL = 0x2c, + MCU_EXT_CMD_WTBL_UPDATE = 0x32, + MCU_EXT_CMD_SET_DRR_CTRL = 0x36, + MCU_EXT_CMD_SET_RDD_CTRL = 0x3a, + MCU_EXT_CMD_ATE_CTRL = 0x3d, + MCU_EXT_CMD_PROTECT_CTRL = 0x3e, + MCU_EXT_CMD_DBDC_CTRL = 0x45, + MCU_EXT_CMD_MAC_INIT_CTRL = 0x46, + MCU_EXT_CMD_RX_HDR_TRANS = 0x47, + MCU_EXT_CMD_MUAR_UPDATE = 0x48, + MCU_EXT_CMD_BCN_OFFLOAD = 0x49, + MCU_EXT_CMD_RX_AIRTIME_CTRL = 0x4a, + MCU_EXT_CMD_SET_RX_PATH = 0x4e, + MCU_EXT_CMD_EFUSE_FREE_BLOCK = 0x4f, + MCU_EXT_CMD_TX_POWER_FEATURE_CTRL = 0x58, + MCU_EXT_CMD_RXDCOC_CAL = 0x59, + MCU_EXT_CMD_GET_MIB_INFO = 0x5a, + MCU_EXT_CMD_TXDPD_CAL = 0x60, + MCU_EXT_CMD_CAL_CACHE = 0x67, + MCU_EXT_CMD_RED_ENABLE = 0x68, + MCU_EXT_CMD_CP_SUPPORT = 0x75, + MCU_EXT_CMD_SET_RADAR_TH = 0x7c, + MCU_EXT_CMD_SET_RDD_PATTERN = 0x7d, + MCU_EXT_CMD_MWDS_SUPPORT = 0x80, + MCU_EXT_CMD_SET_SER_TRIGGER = 0x81, + MCU_EXT_CMD_TWT_AGRT_UPDATE = 0x94, + MCU_EXT_CMD_FW_DBG_CTRL = 0x95, + MCU_EXT_CMD_OFFCH_SCAN_CTRL = 0x9a, + MCU_EXT_CMD_SET_RDD_TH = 0x9d, + MCU_EXT_CMD_MURU_CTRL = 0x9f, + MCU_EXT_CMD_SET_SPR = 0xa8, + MCU_EXT_CMD_GROUP_PRE_CAL_INFO = 0xab, + MCU_EXT_CMD_DPD_PRE_CAL_INFO = 0xac, + MCU_EXT_CMD_PHY_STAT_INFO = 0xad, + MCU_EXT_CMD_WF_RF_PIN_CTRL = 0xbd, +}; + +enum { + MCU_UNI_CMD_DEV_INFO_UPDATE = 0x01, + MCU_UNI_CMD_BSS_INFO_UPDATE = 0x02, + MCU_UNI_CMD_STA_REC_UPDATE = 0x03, + MCU_UNI_CMD_EDCA_UPDATE = 0x04, + MCU_UNI_CMD_SUSPEND = 0x05, + MCU_UNI_CMD_OFFLOAD = 0x06, + MCU_UNI_CMD_HIF_CTRL = 0x07, + MCU_UNI_CMD_BAND_CONFIG = 0x08, + MCU_UNI_CMD_REPT_MUAR = 0x09, + MCU_UNI_CMD_WSYS_CONFIG = 0x0b, + MCU_UNI_CMD_REG_ACCESS = 0x0d, + MCU_UNI_CMD_CHIP_CONFIG = 0x0e, + MCU_UNI_CMD_POWER_CTRL = 0x0f, + MCU_UNI_CMD_RX_HDR_TRANS = 0x12, + MCU_UNI_CMD_SER = 0x13, + MCU_UNI_CMD_TWT = 0x14, + MCU_UNI_CMD_SET_DOMAIN_INFO = 0x15, + MCU_UNI_CMD_SCAN_REQ = 0x16, + MCU_UNI_CMD_RDD_CTRL = 0x19, + MCU_UNI_CMD_GET_MIB_INFO = 0x22, + MCU_UNI_CMD_GET_STAT_INFO = 0x23, + MCU_UNI_CMD_SNIFFER = 0x24, + MCU_UNI_CMD_SR = 0x25, + MCU_UNI_CMD_ROC = 0x27, + MCU_UNI_CMD_SET_DBDC_PARMS = 0x28, + MCU_UNI_CMD_TXPOWER = 0x2b, + MCU_UNI_CMD_SET_POWER_LIMIT = 0x2c, + MCU_UNI_CMD_EFUSE_CTRL = 0x2d, + MCU_UNI_CMD_RA = 0x2f, + MCU_UNI_CMD_MURU = 0x31, + MCU_UNI_CMD_TESTMODE_RX_STAT = 0x32, + MCU_UNI_CMD_BF = 0x33, + MCU_UNI_CMD_CHANNEL_SWITCH = 0x34, + MCU_UNI_CMD_THERMAL = 0x35, + MCU_UNI_CMD_VOW = 0x37, + MCU_UNI_CMD_FIXED_RATE_TABLE = 0x40, + MCU_UNI_CMD_RSSI_MONITOR = 0x41, + MCU_UNI_CMD_TESTMODE_CTRL = 0x46, + MCU_UNI_CMD_NAN = 0x56, + MCU_UNI_CMD_RRO = 0x57, + MCU_UNI_CMD_OFFCH_SCAN_CTRL = 0x58, + MCU_UNI_CMD_PER_STA_INFO = 0x6d, + MCU_UNI_CMD_ALL_STA_INFO = 0x6e, + MCU_UNI_CMD_ASSERT_DUMP = 0x6f, + MCU_UNI_CMD_EXT_EEPROM_CTRL = 0x74, + MCU_UNI_CMD_RADIO_STATUS = 0x80, + MCU_UNI_CMD_MLD = 0x82, + MCU_UNI_CMD_SDO = 0x88, +}; + +enum { + MCU_CMD_TARGET_ADDRESS_LEN_REQ = 0x01, + MCU_CMD_FW_START_REQ = 0x02, + MCU_CMD_INIT_ACCESS_REG = 0x3, + MCU_CMD_NIC_POWER_CTRL = 0x4, + MCU_CMD_PATCH_START_REQ = 0x05, + MCU_CMD_PATCH_FINISH_REQ = 0x07, + MCU_CMD_PATCH_SEM_CONTROL = 0x10, + MCU_CMD_CB_PATCH_SEM_CONTROL = 0x30, + MCU_CMD_CB_PATCH_START_REQ = 0x31, + MCU_CMD_CB_PATCH_FINISH_REQ = 0x32, + MCU_CMD_WA_PARAM = 0xc4, + MCU_CMD_EXT_CID = 0xed, + MCU_CMD_FW_SCATTER = 0xee, + MCU_CMD_RESTART_DL_REQ = 0xef, +}; + +/* offload mcu commands */ +enum { + MCU_CE_CMD_TEST_CTRL = 0x01, + MCU_CE_CMD_START_HW_SCAN = 0x03, + MCU_CE_CMD_SET_PS_PROFILE = 0x05, + MCU_CE_CMD_SET_RX_FILTER = 0x0a, + MCU_CE_CMD_SET_CHAN_DOMAIN = 0x0f, + MCU_CE_CMD_SET_BSS_CONNECTED = 0x16, + MCU_CE_CMD_SET_BSS_ABORT = 0x17, + MCU_CE_CMD_CANCEL_HW_SCAN = 0x1b, + MCU_CE_CMD_SET_ROC = 0x1c, + MCU_CE_CMD_SET_EDCA_PARMS = 0x1d, + MCU_CE_CMD_SET_P2P_OPPPS = 0x33, + MCU_CE_CMD_SET_CLC = 0x5c, + MCU_CE_CMD_SET_RATE_TX_POWER = 0x5d, + MCU_CE_CMD_SCHED_SCAN_ENABLE = 0x61, + MCU_CE_CMD_SCHED_SCAN_REQ = 0x62, + MCU_CE_CMD_GET_NIC_CAPAB = 0x8a, + MCU_CE_CMD_RSSI_MONITOR = 0xa1, + MCU_CE_CMD_SET_MU_EDCA_PARMS = 0xb0, + MCU_CE_CMD_REG_WRITE = 0xc0, + MCU_CE_CMD_REG_READ = 0xc0, + MCU_CE_CMD_CHIP_CONFIG = 0xca, + MCU_CE_CMD_FWLOG_2_HOST = 0xc5, + MCU_CE_CMD_GET_WTBL = 0xcd, + MCU_CE_CMD_GET_TXPWR = 0xd0, + MCU_CE_CMD_SET_REGD_CH = 0xd1, +}; + +enum { + PATCH_SEM_RELEASE, + PATCH_SEM_GET +}; + +enum { + UNI_BSS_INFO_BASIC = 0, + UNI_BSS_INFO_RA = 1, + UNI_BSS_INFO_RLM = 2, + UNI_BSS_INFO_PROTECT_INFO = 3, + UNI_BSS_INFO_BSS_COLOR = 4, + UNI_BSS_INFO_HE_BASIC = 5, + UNI_BSS_INFO_11V_MBSSID = 6, + UNI_BSS_INFO_BCN_CONTENT = 7, + UNI_BSS_INFO_BCN_CSA = 8, + UNI_BSS_INFO_BCN_BCC = 9, + UNI_BSS_INFO_BCN_MBSSID = 10, + UNI_BSS_INFO_RATE = 11, + UNI_BSS_INFO_QBSS = 15, + UNI_BSS_INFO_SEC = 16, + UNI_BSS_INFO_BCN_PROT = 17, + UNI_BSS_INFO_TXCMD = 18, + UNI_BSS_INFO_UAPSD = 19, + UNI_BSS_INFO_PS = 21, + UNI_BSS_INFO_BCNFT = 22, + UNI_BSS_INFO_IFS_TIME = 23, + UNI_BSS_INFO_OFFLOAD = 25, + UNI_BSS_INFO_MLD = 26, + UNI_BSS_INFO_PM_DISABLE = 27, + UNI_BSS_INFO_EHT = 30, + UNI_BSS_INFO_MLD_LINK_OP = 36, +}; + +enum { + UNI_OFFLOAD_OFFLOAD_ARP, + UNI_OFFLOAD_OFFLOAD_ND, + UNI_OFFLOAD_OFFLOAD_GTK_REKEY, + UNI_OFFLOAD_OFFLOAD_BMC_RPY_DETECT, +}; + +enum UNI_ALL_STA_INFO_TAG { + UNI_ALL_STA_TXRX_RATE, + UNI_ALL_STA_TX_STAT, + UNI_ALL_STA_TXRX_ADM_STAT, + UNI_ALL_STA_TXRX_AIR_TIME, + UNI_ALL_STA_DATA_TX_RETRY_COUNT, + UNI_ALL_STA_GI_MODE, + UNI_ALL_STA_TXRX_MSDU_COUNT, + UNI_ALL_STA_MAX_NUM +}; + +enum { + MT_NIC_CAP_TX_RESOURCE, + MT_NIC_CAP_TX_EFUSE_ADDR, + MT_NIC_CAP_COEX, + MT_NIC_CAP_SINGLE_SKU, + MT_NIC_CAP_CSUM_OFFLOAD, + MT_NIC_CAP_HW_VER, + MT_NIC_CAP_SW_VER, + MT_NIC_CAP_MAC_ADDR, + MT_NIC_CAP_PHY, + MT_NIC_CAP_MAC, + MT_NIC_CAP_FRAME_BUF, + MT_NIC_CAP_BEAM_FORM, + MT_NIC_CAP_LOCATION, + MT_NIC_CAP_MUMIMO, + MT_NIC_CAP_BUFFER_MODE_INFO, + MT_NIC_CAP_HW_ADIE_VERSION = 0x14, + MT_NIC_CAP_ANTSWP = 0x16, + MT_NIC_CAP_WFDMA_REALLOC, + MT_NIC_CAP_6G, + MT_NIC_CAP_CHIP_CAP = 0x20, + MT_NIC_CAP_EML_CAP = 0x22, +}; + +#define UNI_WOW_DETECT_TYPE_MAGIC BIT(0) +#define UNI_WOW_DETECT_TYPE_ANY BIT(1) +#define UNI_WOW_DETECT_TYPE_DISCONNECT BIT(2) +#define UNI_WOW_DETECT_TYPE_GTK_REKEY_FAIL BIT(3) +#define UNI_WOW_DETECT_TYPE_BCN_LOST BIT(4) +#define UNI_WOW_DETECT_TYPE_SCH_SCAN_HIT BIT(5) +#define UNI_WOW_DETECT_TYPE_BITMAP BIT(6) + +enum { + UNI_SUSPEND_MODE_SETTING, + UNI_SUSPEND_WOW_CTRL, + UNI_SUSPEND_WOW_GPIO_PARAM, + UNI_SUSPEND_WOW_WAKEUP_PORT, + UNI_SUSPEND_WOW_PATTERN, +}; + +enum { + WOW_USB = 1, + WOW_PCIE = 2, + WOW_GPIO = 3, +}; + +struct mt76_connac_bss_basic_tlv { + __le16 tag; + __le16 len; + u8 active; + u8 omac_idx; + u8 hw_bss_idx; + u8 band_idx; + __le32 conn_type; + u8 conn_state; + u8 wmm_idx; + u8 bssid[ETH_ALEN]; + __le16 bmc_tx_wlan_idx; + __le16 bcn_interval; + u8 dtim_period; + u8 phymode; /* bit(0): A + * bit(1): B + * bit(2): G + * bit(3): GN + * bit(4): AN + * bit(5): AC + * bit(6): AX2 + * bit(7): AX5 + * bit(8): AX6 + */ + __le16 sta_idx; + __le16 nonht_basic_phy; + u8 phymode_ext; /* bit(0) AX_6G */ + u8 link_idx; +} __packed; + +struct mt76_connac_bss_qos_tlv { + __le16 tag; + __le16 len; + u8 qos; + u8 pad[3]; +} __packed; + +struct mt76_connac_beacon_loss_event { + u8 bss_idx; + u8 reason; + u8 pad[2]; +} __packed; + +struct mt76_connac_rssi_notify_event { + __le32 rssi[4]; +} __packed; + +struct mt76_connac_mcu_bss_event { + u8 bss_idx; + u8 is_absent; + u8 free_quota; + u8 pad; +} __packed; + +struct mt76_connac_mcu_scan_ssid { + __le32 ssid_len; + u8 ssid[IEEE80211_MAX_SSID_LEN]; +} __packed; + +struct mt76_connac_mcu_scan_channel { + u8 band; /* 1: 2.4GHz + * 2: 5.0GHz + * Others: Reserved + */ + u8 channel_num; +} __packed; + +struct mt76_connac_mcu_scan_match { + __le32 rssi_th; + u8 ssid[IEEE80211_MAX_SSID_LEN]; + u8 ssid_len; + u8 rsv[3]; +} __packed; + +struct mt76_connac_hw_scan_req { + u8 seq_num; + u8 bss_idx; + u8 scan_type; /* 0: PASSIVE SCAN + * 1: ACTIVE SCAN + */ + u8 ssid_type; /* BIT(0) wildcard SSID + * BIT(1) P2P wildcard SSID + * BIT(2) specified SSID + wildcard SSID + * BIT(2) + ssid_type_ext BIT(0) specified SSID only + */ + u8 ssids_num; + u8 probe_req_num; /* Number of probe request for each SSID */ + u8 scan_func; /* BIT(0) Enable random MAC scan + * BIT(1) Disable DBDC scan type 1~3. + * BIT(2) Use DBDC scan type 3 (dedicated one RF to scan). + */ + u8 version; /* 0: Not support fields after ies. + * 1: Support fields after ies. + */ + struct mt76_connac_mcu_scan_ssid ssids[4]; + __le16 probe_delay_time; + __le16 channel_dwell_time; /* channel Dwell interval */ + __le16 timeout_value; + u8 channel_type; /* 0: Full channels + * 1: Only 2.4GHz channels + * 2: Only 5GHz channels + * 3: P2P social channel only (channel #1, #6 and #11) + * 4: Specified channels + * Others: Reserved + */ + u8 channels_num; /* valid when channel_type is 4 */ + /* valid when channels_num is set */ + struct mt76_connac_mcu_scan_channel channels[32]; + __le16 ies_len; + u8 ies[MT76_CONNAC_SCAN_IE_LEN]; + /* following fields are valid if version > 0 */ + u8 ext_channels_num; + u8 ext_ssids_num; + __le16 channel_min_dwell_time; + struct mt76_connac_mcu_scan_channel ext_channels[32]; + struct mt76_connac_mcu_scan_ssid ext_ssids[6]; + u8 bssid[ETH_ALEN]; + u8 random_mac[ETH_ALEN]; /* valid when BIT(1) in scan_func is set. */ + u8 pad[63]; + u8 ssid_type_ext; +} __packed; + +#define MT76_CONNAC_SCAN_DONE_EVENT_MAX_CHANNEL_NUM 64 + +struct mt76_connac_hw_scan_done { + u8 seq_num; + u8 sparse_channel_num; + struct mt76_connac_mcu_scan_channel sparse_channel; + u8 complete_channel_num; + u8 current_state; + u8 version; + u8 pad; + __le32 beacon_scan_num; + u8 pno_enabled; + u8 pad2[3]; + u8 sparse_channel_valid_num; + u8 alpha2[3]; + u8 channel_num[MT76_CONNAC_SCAN_DONE_EVENT_MAX_CHANNEL_NUM]; + /* idle format for channel_idle_time + * 0: first bytes: idle time(ms) 2nd byte: dwell time(ms) + * 1: first bytes: idle time(8ms) 2nd byte: dwell time(8ms) + * 2: dwell time (16us) + */ + __le16 channel_idle_time[MT76_CONNAC_SCAN_DONE_EVENT_MAX_CHANNEL_NUM]; + /* beacon and probe response count */ + u8 beacon_probe_num[MT76_CONNAC_SCAN_DONE_EVENT_MAX_CHANNEL_NUM]; + u8 mdrdy_count[MT76_CONNAC_SCAN_DONE_EVENT_MAX_CHANNEL_NUM]; + __le32 beacon_2g_num; + __le32 beacon_5g_num; + __le16 channel_scan_time[MT76_CONNAC_SCAN_DONE_EVENT_MAX_CHANNEL_NUM]; +} __packed; + +struct mt76_connac_sched_scan_req { + u8 version; + u8 seq_num; + u8 stop_on_match; + u8 ssids_num; + u8 match_num; + u8 pad; + __le16 ie_len; + struct mt76_connac_mcu_scan_ssid ssids[MT76_CONNAC_MAX_SCHED_SCAN_SSID]; + struct mt76_connac_mcu_scan_match match[MT76_CONNAC_MAX_SCAN_MATCH]; + u8 channel_type; + u8 channels_num; + u8 intervals_num; + u8 scan_func; /* MT7663: BIT(0) eable random mac address */ + struct mt76_connac_mcu_scan_channel channels[64]; + __le16 intervals[MT76_CONNAC_MAX_NUM_SCHED_SCAN_INTERVAL]; + union { + struct { + u8 random_mac[ETH_ALEN]; + u8 pad2[58]; + } mt7663; + struct { + u8 bss_idx; + u8 pad1[3]; + __le32 delay; + u8 pad2[12]; + u8 random_mac[ETH_ALEN]; + u8 pad3[38]; + } mt7921; + }; +} __packed; + +struct mt76_connac_sched_scan_done { + u8 seq_num; + u8 status; /* 0: ssid found */ + __le16 pad; +} __packed; + +struct bss_info_uni_bss_color { + __le16 tag; + __le16 len; + u8 enable; + u8 bss_color; + u8 rsv[2]; +} __packed; + +struct bss_info_uni_he { + __le16 tag; + __le16 len; + __le16 he_rts_thres; + u8 he_pe_duration; + u8 su_disable; + __le16 max_nss_mcs[CMD_HE_MCS_BW_NUM]; + u8 rsv[2]; +} __packed; + +struct bss_info_uni_mbssid { + __le16 tag; + __le16 len; + u8 max_indicator; + u8 mbss_idx; + u8 tx_bss_omac_idx; + u8 rsv; +} __packed; + +struct mt76_connac_gtk_rekey_tlv { + __le16 tag; + __le16 len; + u8 kek[NL80211_KEK_LEN]; + u8 kck[NL80211_KCK_LEN]; + u8 replay_ctr[NL80211_REPLAY_CTR_LEN]; + u8 rekey_mode; /* 0: rekey offload enable + * 1: rekey offload disable + * 2: rekey update + */ + u8 keyid; + u8 option; /* 1: rekey data update without enabling offload */ + u8 pad[1]; + __le32 proto; /* WPA-RSN-WAPI-OPSN */ + __le32 pairwise_cipher; + __le32 group_cipher; + __le32 key_mgmt; /* NONE-PSK-IEEE802.1X */ + __le32 mgmt_group_cipher; + u8 reserverd[4]; +} __packed; + +#define MT76_CONNAC_WOW_MASK_MAX_LEN 16 +#define MT76_CONNAC_WOW_PATTEN_MAX_LEN 128 + +struct mt76_connac_wow_pattern_tlv { + __le16 tag; + __le16 len; + u8 index; /* pattern index */ + u8 enable; /* 0: disable + * 1: enable + */ + u8 data_len; /* pattern length */ + u8 pad; + u8 mask[MT76_CONNAC_WOW_MASK_MAX_LEN]; + u8 pattern[MT76_CONNAC_WOW_PATTEN_MAX_LEN]; + u8 rsv[4]; +} __packed; + +struct mt76_connac_wow_ctrl_tlv { + __le16 tag; + __le16 len; + u8 cmd; /* 0x1: PM_WOWLAN_REQ_START + * 0x2: PM_WOWLAN_REQ_STOP + * 0x3: PM_WOWLAN_PARAM_CLEAR + */ + u8 trigger; /* 0: NONE + * BIT(0): NL80211_WOWLAN_TRIG_MAGIC_PKT + * BIT(1): NL80211_WOWLAN_TRIG_ANY + * BIT(2): NL80211_WOWLAN_TRIG_DISCONNECT + * BIT(3): NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE + * BIT(4): BEACON_LOST + * BIT(5): NL80211_WOWLAN_TRIG_NET_DETECT + */ + u8 wakeup_hif; /* 0x0: HIF_SDIO + * 0x1: HIF_USB + * 0x2: HIF_PCIE + * 0x3: HIF_GPIO + */ + u8 pad; + u8 rsv[4]; +} __packed; + +struct mt76_connac_wow_gpio_param_tlv { + __le16 tag; + __le16 len; + u8 gpio_pin; + u8 trigger_lvl; + u8 pad[2]; + __le32 gpio_interval; + u8 rsv[4]; +} __packed; + +struct mt76_connac_arpns_tlv { + __le16 tag; + __le16 len; + u8 mode; + u8 ips_num; + u8 option; + u8 pad[1]; +} __packed; + +struct mt76_connac_suspend_tlv { + __le16 tag; + __le16 len; + u8 enable; /* 0: suspend mode disabled + * 1: suspend mode enabled + */ + u8 mdtim; /* LP parameter */ + u8 wow_suspend; /* 0: update by origin policy + * 1: update by wow dtim + */ + u8 pad[5]; +} __packed; + +enum mt76_sta_info_state { + MT76_STA_INFO_STATE_NONE, + MT76_STA_INFO_STATE_AUTH, + MT76_STA_INFO_STATE_ASSOC +}; + +struct mt76_sta_cmd_info { + union { + struct ieee80211_sta *sta; + struct ieee80211_link_sta *link_sta; + }; + struct mt76_wcid *wcid; + + struct ieee80211_vif *vif; + struct ieee80211_bss_conf *link_conf; + + bool offload_fw; + bool enable; + bool newly; + int cmd; + u8 rcpi; + u8 state; +}; + +#define MT_SKU_POWER_LIMIT 161 + +struct mt76_connac_sku_tlv { + u8 channel; + s8 pwr_limit[MT_SKU_POWER_LIMIT]; +} __packed; + +struct mt76_connac_tx_power_limit_tlv { + /* DW0 - common info*/ + u8 ver; + u8 pad0; + __le16 len; + /* DW1 - cmd hint */ + u8 n_chan; /* # channel */ + u8 band; /* 2.4GHz - 5GHz - 6GHz */ + u8 last_msg; + u8 pad1; + /* DW3 */ + u8 alpha2[4]; /* regulatory_request.alpha2 */ + u8 pad2[32]; +} __packed; + +struct mt76_connac_config { + __le16 id; + u8 type; + u8 resp_type; + __le16 data_size; + __le16 resv; + u8 data[320]; +} __packed; + +struct mt76_connac_mcu_uni_event { + u8 cid; + u8 pad[3]; + __le32 status; /* 0: success, others: fail */ +} __packed; + +struct mt76_connac_mcu_reg_event { + __le32 reg; + __le32 val; +} __packed; + +static inline enum mcu_cipher_type +mt76_connac_mcu_get_cipher(int cipher) +{ + switch (cipher) { + case WLAN_CIPHER_SUITE_WEP40: + return MCU_CIPHER_WEP40; + case WLAN_CIPHER_SUITE_WEP104: + return MCU_CIPHER_WEP104; + case WLAN_CIPHER_SUITE_TKIP: + return MCU_CIPHER_TKIP; + case WLAN_CIPHER_SUITE_AES_CMAC: + return MCU_CIPHER_BIP_CMAC_128; + case WLAN_CIPHER_SUITE_CCMP: + return MCU_CIPHER_AES_CCMP; + case WLAN_CIPHER_SUITE_CCMP_256: + return MCU_CIPHER_CCMP_256; + case WLAN_CIPHER_SUITE_GCMP: + return MCU_CIPHER_GCMP; + case WLAN_CIPHER_SUITE_GCMP_256: + return MCU_CIPHER_GCMP_256; + case WLAN_CIPHER_SUITE_BIP_GMAC_128: + return MCU_CIPHER_BIP_GMAC_128; + case WLAN_CIPHER_SUITE_BIP_GMAC_256: + return MCU_CIPHER_BIP_GMAC_256; + case WLAN_CIPHER_SUITE_BIP_CMAC_256: + return MCU_CIPHER_BIP_CMAC_256; + case WLAN_CIPHER_SUITE_SMS4: + return MCU_CIPHER_WAPI; + default: + return MCU_CIPHER_NONE; + } +} + +static inline u32 +mt76_connac_mcu_gen_dl_mode(struct mt76_dev *dev, u8 feature_set, bool is_wa) +{ + u32 ret = 0; + + ret |= feature_set & FW_FEATURE_SET_ENCRYPT ? + DL_MODE_ENCRYPT | DL_MODE_RESET_SEC_IV : 0; + if (is_connac2(dev) || is_connac3(dev)) + ret |= feature_set & FW_FEATURE_ENCRY_MODE ? + DL_CONFIG_ENCRY_MODE_SEL : 0; + ret |= FIELD_PREP(DL_MODE_KEY_IDX, + FIELD_GET(FW_FEATURE_SET_KEY_IDX, feature_set)); + ret |= DL_MODE_NEED_RSP; + ret |= is_wa ? DL_MODE_WORKING_PDA_CR4 : 0; + + return ret; +} + +#define to_wcid_lo(id) FIELD_GET(GENMASK(7, 0), (u16)id) +#define to_wcid_hi(id) FIELD_GET(GENMASK(10, 8), (u16)id) + +static inline void +mt76_connac_mcu_get_wlan_idx(struct mt76_dev *dev, struct mt76_wcid *wcid, + u8 *wlan_idx_lo, u8 *wlan_idx_hi) +{ + *wlan_idx_hi = 0; + + if (!is_connac_v1(dev)) { + *wlan_idx_lo = wcid ? to_wcid_lo(wcid->idx) : 0; + *wlan_idx_hi = wcid ? to_wcid_hi(wcid->idx) : 0; + } else { + *wlan_idx_lo = wcid ? wcid->idx : 0; + } +} + +#define MT76_CONNAC_MCU_STATUS_WLAN_FAILURE 0xc0000001 + +static inline int +mt76_connac_mcu_bss_deact_err(struct mt76_dev *mdev, int err, bool enable) +{ + if (err != (int)MT76_CONNAC_MCU_STATUS_WLAN_FAILURE) + return err; + + /* Ignore wlan_failure state false alarm when deactivating an + * inactive network. It does not harm the firmware state. + */ + if (!enable) { + dev_dbg(mdev->dev, + "ignore wlan_failure when bss is deactivated\n"); + return 0; + } + + dev_warn(mdev->dev, + "wlan_failure when bss is activated\n"); + + return err; +} + +struct sk_buff * +__mt76_connac_mcu_alloc_sta_req(struct mt76_dev *dev, struct mt76_vif_link *mvif, + struct mt76_wcid *wcid, int len); +static inline struct sk_buff * +mt76_connac_mcu_alloc_sta_req(struct mt76_dev *dev, struct mt76_vif_link *mvif, + struct mt76_wcid *wcid) +{ + return __mt76_connac_mcu_alloc_sta_req(dev, mvif, wcid, + MT76_CONNAC_STA_UPDATE_MAX_SIZE); +} + +struct wtbl_req_hdr * +mt76_connac_mcu_alloc_wtbl_req(struct mt76_dev *dev, struct mt76_wcid *wcid, + int cmd, void *sta_wtbl, struct sk_buff **skb); +struct tlv *mt76_connac_mcu_add_nested_tlv(struct sk_buff *skb, int tag, + int len, void *sta_ntlv, + void *sta_wtbl); +static inline struct tlv * +mt76_connac_mcu_add_tlv(struct sk_buff *skb, int tag, int len) +{ + return mt76_connac_mcu_add_nested_tlv(skb, tag, len, skb->data, NULL); +} + +int mt76_connac_mcu_set_channel_domain(struct mt76_phy *phy); +int mt76_connac_mcu_set_vif_ps(struct mt76_dev *dev, struct ieee80211_vif *vif); +void mt76_connac_mcu_sta_basic_tlv(struct mt76_dev *dev, struct sk_buff *skb, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_link_sta *link_sta, + int state, bool newly); +void mt76_connac_mcu_wtbl_generic_tlv(struct mt76_dev *dev, struct sk_buff *skb, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, void *sta_wtbl, + void *wtbl_tlv); +void mt76_connac_mcu_wtbl_hdr_trans_tlv(struct sk_buff *skb, + struct ieee80211_vif *vif, + struct mt76_wcid *wcid, + void *sta_wtbl, void *wtbl_tlv); +int mt76_connac_mcu_sta_update_hdr_trans(struct mt76_dev *dev, + struct ieee80211_vif *vif, + struct mt76_wcid *wcid, int cmd); +void mt76_connac_mcu_sta_he_tlv_v2(struct sk_buff *skb, struct ieee80211_sta *sta); +u8 mt76_connac_get_phy_mode_v2(struct mt76_phy *mphy, struct ieee80211_vif *vif, + enum nl80211_band band, + struct ieee80211_link_sta *link_sta); +int mt76_connac_mcu_wtbl_update_hdr_trans(struct mt76_dev *dev, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta); +void mt76_connac_mcu_sta_tlv(struct mt76_phy *mphy, struct sk_buff *skb, + struct ieee80211_sta *sta, + struct ieee80211_vif *vif, + u8 rcpi, u8 state); +void mt76_connac_mcu_wtbl_ht_tlv(struct mt76_dev *dev, struct sk_buff *skb, + struct ieee80211_sta *sta, void *sta_wtbl, + void *wtbl_tlv, bool ht_ldpc, bool vht_ldpc); +void mt76_connac_mcu_wtbl_ba_tlv(struct mt76_dev *dev, struct sk_buff *skb, + struct ieee80211_ampdu_params *params, + bool enable, bool tx, void *sta_wtbl, + void *wtbl_tlv); +void mt76_connac_mcu_sta_ba_tlv(struct sk_buff *skb, + struct ieee80211_ampdu_params *params, + bool enable, bool tx); +int mt76_connac_mcu_uni_add_dev(struct mt76_phy *phy, + struct ieee80211_bss_conf *bss_conf, + struct mt76_vif_link *mvif, + struct mt76_wcid *wcid, + bool enable); +int mt76_connac_mcu_sta_ba(struct mt76_dev *dev, struct mt76_vif_link *mvif, + struct ieee80211_ampdu_params *params, + int cmd, bool enable, bool tx); +int mt76_connac_mcu_uni_set_chctx(struct mt76_phy *phy, + struct mt76_vif_link *vif, + struct ieee80211_chanctx_conf *ctx); +int mt76_connac_mcu_uni_add_bss(struct mt76_phy *phy, + struct ieee80211_vif *vif, + struct mt76_wcid *wcid, + bool enable, + struct ieee80211_chanctx_conf *ctx); +int mt76_connac_mcu_sta_cmd(struct mt76_phy *phy, + struct mt76_sta_cmd_info *info); +void mt76_connac_mcu_beacon_loss_iter(void *priv, u8 *mac, + struct ieee80211_vif *vif); +int mt76_connac_mcu_set_rts_thresh(struct mt76_dev *dev, u32 val, u8 band); +int mt76_connac_mcu_set_mac_enable(struct mt76_dev *dev, int band, bool enable, + bool hdr_trans); +int mt76_connac_mcu_init_download(struct mt76_dev *dev, u32 addr, u32 len, + u32 mode); +int mt76_connac_mcu_start_patch(struct mt76_dev *dev); +int mt76_connac_mcu_patch_sem_ctrl(struct mt76_dev *dev, bool get); +int mt76_connac_cb_mcu_init_download(struct mt76_dev *dev, u32 len); +int mt76_connac_cb_mcu_start_patch(struct mt76_dev *dev); +int mt76_connac_cb_mcu_patch_sem_ctrl(struct mt76_dev *dev, bool get); +int mt76_connac_mcu_start_firmware(struct mt76_dev *dev, u32 addr, u32 option); + +void mt76_connac_mcu_build_rnr_scan_param(struct mt76_dev *mdev, + struct cfg80211_scan_request *sreq); +int mt76_connac_mcu_hw_scan(struct mt76_phy *phy, struct ieee80211_vif *vif, + struct ieee80211_scan_request *scan_req); +int mt76_connac_mcu_cancel_hw_scan(struct mt76_phy *phy, + struct ieee80211_vif *vif); +int mt76_connac_mcu_sched_scan_req(struct mt76_phy *phy, + struct ieee80211_vif *vif, + struct cfg80211_sched_scan_request *sreq); +int mt76_connac_mcu_sched_scan_enable(struct mt76_phy *phy, + struct ieee80211_vif *vif, + bool enable); +int mt76_connac_mcu_update_arp_filter(struct mt76_dev *dev, + struct mt76_vif_link *vif, + struct ieee80211_bss_conf *info); +int mt76_connac_mcu_set_gtk_rekey(struct mt76_dev *dev, struct ieee80211_vif *vif, + bool suspend); +int mt76_connac_mcu_set_wow_ctrl(struct mt76_phy *phy, struct ieee80211_vif *vif, + bool suspend, struct cfg80211_wowlan *wowlan); +int mt76_connac_mcu_update_gtk_rekey(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_gtk_rekey_data *key); +int mt76_connac_mcu_set_suspend_mode(struct mt76_dev *dev, + struct ieee80211_vif *vif, + bool enable, u8 mdtim, + bool wow_suspend); +int mt76_connac_mcu_set_hif_suspend(struct mt76_dev *dev, bool suspend, bool wait_resp); +void mt76_connac_mcu_set_suspend_iter(void *priv, u8 *mac, + struct ieee80211_vif *vif); +int mt76_connac_sta_state_dp(struct mt76_dev *dev, + enum ieee80211_sta_state old_state, + enum ieee80211_sta_state new_state); +int mt76_connac_mcu_chip_config(struct mt76_dev *dev); +int mt76_connac_mcu_set_deep_sleep(struct mt76_dev *dev, bool enable); +void mt76_connac_mcu_coredump_event(struct mt76_dev *dev, struct sk_buff *skb, + struct mt76_connac_coredump *coredump); +s8 mt76_connac_get_ch_power(struct mt76_phy *phy, + struct ieee80211_channel *chan, + s8 target_power); +int mt76_connac_mcu_set_rate_txpower(struct mt76_phy *phy); +int mt76_connac_mcu_set_p2p_oppps(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); +u32 mt76_connac_mcu_reg_rr(struct mt76_dev *dev, u32 offset); +void mt76_connac_mcu_reg_wr(struct mt76_dev *dev, u32 offset, u32 val); + +const struct ieee80211_sta_he_cap * +mt76_connac_get_he_phy_cap(struct mt76_phy *phy, struct ieee80211_vif *vif); +const struct ieee80211_sta_eht_cap * +mt76_connac_get_eht_phy_cap(struct mt76_phy *phy, struct ieee80211_vif *vif); +u8 mt76_connac_get_phy_mode(struct mt76_phy *phy, struct ieee80211_vif *vif, + enum nl80211_band band, + struct ieee80211_link_sta *sta); +u8 mt76_connac_get_phy_mode_ext(struct mt76_phy *phy, struct ieee80211_bss_conf *conf, + enum nl80211_band band); + +int mt76_connac_mcu_add_key(struct mt76_dev *dev, struct ieee80211_vif *vif, + struct mt76_connac_sta_key_conf *sta_key_conf, + struct ieee80211_key_conf *key, int mcu_cmd, + struct mt76_wcid *wcid, enum set_key_cmd cmd); + +void mt76_connac_mcu_bss_ext_tlv(struct sk_buff *skb, struct mt76_vif_link *mvif); +void mt76_connac_mcu_bss_omac_tlv(struct sk_buff *skb, + struct ieee80211_vif *vif); +int mt76_connac_mcu_bss_basic_tlv(struct sk_buff *skb, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct mt76_phy *phy, u16 wlan_idx, + bool enable); +void mt76_connac_mcu_sta_uapsd(struct sk_buff *skb, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); +void mt76_connac_mcu_wtbl_smps_tlv(struct sk_buff *skb, + struct ieee80211_sta *sta, + void *sta_wtbl, void *wtbl_tlv); +int mt76_connac_mcu_set_pm(struct mt76_dev *dev, int band, int enter); +int mt76_connac_mcu_restart(struct mt76_dev *dev); +int mt76_connac_mcu_del_wtbl_all(struct mt76_dev *dev); +int mt76_connac_mcu_rdd_cmd(struct mt76_dev *dev, int cmd, u8 index, + u8 rx_sel, u8 val); +int mt76_connac_mcu_sta_wed_update(struct mt76_dev *dev, struct sk_buff *skb); +int mt76_connac2_load_ram(struct mt76_dev *dev, const char *fw_wm, + const char *fw_wa); +int mt76_connac3_load_phy_ram(struct mt76_dev *dev, const char *fw_name); +int mt76_connac2_load_patch(struct mt76_dev *dev, const char *fw_name); +int mt76_connac3_load_cb_patch(struct mt76_dev *dev, const char *fw_name); +int mt76_connac2_mcu_fill_message(struct mt76_dev *mdev, struct sk_buff *skb, + int cmd, int *wait_seq); +#endif /* __MT76_CONNAC_MCU_H */ diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/Kconfig b/drivers/net/wireless/mediatek/mt76/mt76x0/Kconfig new file mode 100644 index 000000000..3ed888782 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/Kconfig @@ -0,0 +1,29 @@ +# SPDX-License-Identifier: GPL-2.0-only +config MT76x0_COMMON + tristate + select MT76x02_LIB + +config MT76x0U + tristate "MediaTek MT76x0U (USB) support" + select MT76x0_COMMON + select MT76x02_USB + depends on MAC80211 + depends on USB + help + This adds support for MT7610U-based USB 2.0 wireless dongles, + which comply with IEEE 802.11ac standards and support 1x1 + 433Mbps PHY rate. + + To compile this driver as a module, choose M here. + +config MT76x0E + tristate "MediaTek MT76x0E (PCIe) support" + select MT76x0_COMMON + depends on MAC80211 + depends on PCI + help + This adds support for MT7610/MT7630-based PCIe wireless devices, + which comply with IEEE 802.11ac standards and support 1x1 + 433Mbps PHY rate. + + To compile this driver as a module, choose M here. diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/Makefile b/drivers/net/wireless/mediatek/mt76/mt76x0/Makefile new file mode 100644 index 000000000..8dcfb4cb4 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/Makefile @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_MT76x0U) += mt76x0u.o +obj-$(CONFIG_MT76x0E) += mt76x0e.o +obj-$(CONFIG_MT76x0_COMMON) += mt76x0-common.o + +mt76x0-common-y := init.o main.o eeprom.o phy.o + +mt76x0u-y := usb.o usb_mcu.o +mt76x0e-y := pci.o pci_mcu.o + +# ccflags-y := -DDEBUG diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.c new file mode 100644 index 000000000..d4506b8b4 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.c @@ -0,0 +1,350 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2014 Felix Fietkau + * Copyright (C) 2015 Jakub Kicinski + * Copyright (C) 2018 Stanislaw Gruszka + */ + +#include +#include +#include +#include +#include +#include +#include "mt76x0.h" +#include "eeprom.h" +#include "../mt76x02_phy.h" + +#define MT_MAP_READS DIV_ROUND_UP(MT_EFUSE_USAGE_MAP_SIZE, 16) +static int +mt76x0_efuse_physical_size_check(struct mt76x02_dev *dev) +{ + u8 data[MT_MAP_READS * 16]; + int ret, i; + u32 start = 0, end = 0, cnt_free; + + ret = mt76x02_get_efuse_data(dev, MT_EE_USAGE_MAP_START, data, + sizeof(data), MT_EE_PHYSICAL_READ); + if (ret) + return ret; + + for (i = 0; i < MT_EFUSE_USAGE_MAP_SIZE; i++) + if (!data[i]) { + if (!start) + start = MT_EE_USAGE_MAP_START + i; + end = MT_EE_USAGE_MAP_START + i; + } + cnt_free = end - start + 1; + + if (MT_EFUSE_USAGE_MAP_SIZE - cnt_free < 5) { + dev_err(dev->mt76.dev, + "driver does not support default EEPROM\n"); + return -EINVAL; + } + + return 0; +} + +static void mt76x0_set_chip_cap(struct mt76x02_dev *dev) +{ + u16 nic_conf0 = mt76x02_eeprom_get(dev, MT_EE_NIC_CONF_0); + u16 nic_conf1 = mt76x02_eeprom_get(dev, MT_EE_NIC_CONF_1); + + mt76x02_eeprom_parse_hw_cap(dev); + dev_dbg(dev->mt76.dev, "2GHz %d 5GHz %d\n", + dev->mphy.cap.has_2ghz, dev->mphy.cap.has_5ghz); + + if (dev->no_2ghz) { + dev->mphy.cap.has_2ghz = false; + dev_dbg(dev->mt76.dev, "mask out 2GHz support\n"); + } + + if (is_mt7630(dev)) { + dev->mphy.cap.has_5ghz = false; + dev_dbg(dev->mt76.dev, "mask out 5GHz support\n"); + } + + if (!mt76x02_field_valid(nic_conf1 & 0xff)) + nic_conf1 &= 0xff00; + + if (nic_conf1 & MT_EE_NIC_CONF_1_HW_RF_CTRL) + dev_dbg(dev->mt76.dev, + "driver does not support HW RF ctrl\n"); + + if (!mt76x02_field_valid(nic_conf0 >> 8)) + return; + + if (FIELD_GET(MT_EE_NIC_CONF_0_RX_PATH, nic_conf0) > 1 || + FIELD_GET(MT_EE_NIC_CONF_0_TX_PATH, nic_conf0) > 1) + dev_err(dev->mt76.dev, "invalid tx-rx stream\n"); +} + +static void mt76x0_set_temp_offset(struct mt76x02_dev *dev) +{ + u8 val; + + val = mt76x02_eeprom_get(dev, MT_EE_2G_TARGET_POWER) >> 8; + if (mt76x02_field_valid(val)) + dev->cal.rx.temp_offset = mt76x02_sign_extend(val, 8); + else + dev->cal.rx.temp_offset = -10; +} + +static void mt76x0_set_freq_offset(struct mt76x02_dev *dev) +{ + struct mt76x02_rx_freq_cal *caldata = &dev->cal.rx; + u8 val; + + val = mt76x02_eeprom_get(dev, MT_EE_FREQ_OFFSET); + if (!mt76x02_field_valid(val)) + val = 0; + caldata->freq_offset = val; + + val = mt76x02_eeprom_get(dev, MT_EE_TSSI_BOUND4) >> 8; + if (!mt76x02_field_valid(val)) + val = 0; + + caldata->freq_offset -= mt76x02_sign_extend(val, 8); +} + +void mt76x0_read_rx_gain(struct mt76x02_dev *dev) +{ + struct ieee80211_channel *chan = dev->mphy.chandef.chan; + struct mt76x02_rx_freq_cal *caldata = &dev->cal.rx; + s8 val, lna_5g[3], lna_2g; + u16 rssi_offset; + int i; + + mt76x02_get_rx_gain(dev, chan->band, &rssi_offset, &lna_2g, lna_5g); + caldata->lna_gain = mt76x02_get_lna_gain(dev, &lna_2g, lna_5g, chan); + + for (i = 0; i < ARRAY_SIZE(caldata->rssi_offset); i++) { + val = rssi_offset >> (8 * i); + if (val < -10 || val > 10) + val = 0; + + caldata->rssi_offset[i] = val; + } +} + +static s8 mt76x0_get_delta(struct mt76x02_dev *dev) +{ + struct cfg80211_chan_def *chandef = &dev->mphy.chandef; + u8 val; + + if (chandef->width == NL80211_CHAN_WIDTH_80) { + val = mt76x02_eeprom_get(dev, MT_EE_5G_TARGET_POWER) >> 8; + } else if (chandef->width == NL80211_CHAN_WIDTH_40) { + u16 data; + + data = mt76x02_eeprom_get(dev, MT_EE_TX_POWER_DELTA_BW40); + if (chandef->chan->band == NL80211_BAND_5GHZ) + val = data >> 8; + else + val = data; + } else { + return 0; + } + + return mt76x02_rate_power_val(val); +} + +void mt76x0_get_tx_power_per_rate(struct mt76x02_dev *dev, + struct ieee80211_channel *chan, + struct mt76x02_rate_power *t) +{ + bool is_2ghz = chan->band == NL80211_BAND_2GHZ; + u16 val, addr; + s8 delta; + + memset(t, 0, sizeof(*t)); + + /* cck 1M, 2M, 5.5M, 11M */ + val = mt76x02_eeprom_get(dev, MT_EE_TX_POWER_BYRATE_BASE); + t->cck[0] = t->cck[1] = s6_to_s8(val); + t->cck[2] = t->cck[3] = s6_to_s8(val >> 8); + + /* ofdm 6M, 9M, 12M, 18M */ + addr = is_2ghz ? MT_EE_TX_POWER_BYRATE_BASE + 2 : 0x120; + val = mt76x02_eeprom_get(dev, addr); + t->ofdm[0] = t->ofdm[1] = s6_to_s8(val); + t->ofdm[2] = t->ofdm[3] = s6_to_s8(val >> 8); + + /* ofdm 24M, 36M, 48M, 54M */ + addr = is_2ghz ? MT_EE_TX_POWER_BYRATE_BASE + 4 : 0x122; + val = mt76x02_eeprom_get(dev, addr); + t->ofdm[4] = t->ofdm[5] = s6_to_s8(val); + t->ofdm[6] = t->ofdm[7] = s6_to_s8(val >> 8); + + /* ht-vht mcs 1ss 0, 1, 2, 3 */ + addr = is_2ghz ? MT_EE_TX_POWER_BYRATE_BASE + 6 : 0x124; + val = mt76x02_eeprom_get(dev, addr); + t->ht[0] = t->ht[1] = s6_to_s8(val); + t->ht[2] = t->ht[3] = s6_to_s8(val >> 8); + + /* ht-vht mcs 1ss 4, 5, 6 */ + addr = is_2ghz ? MT_EE_TX_POWER_BYRATE_BASE + 8 : 0x126; + val = mt76x02_eeprom_get(dev, addr); + t->ht[4] = t->ht[5] = s6_to_s8(val); + t->ht[6] = t->ht[7] = s6_to_s8(val >> 8); + + /* vht mcs 8, 9 5GHz */ + val = mt76x02_eeprom_get(dev, 0x12c); + t->vht[0] = s6_to_s8(val); + t->vht[1] = s6_to_s8(val >> 8); + + delta = mt76x0_tssi_enabled(dev) ? 0 : mt76x0_get_delta(dev); + mt76x02_add_rate_power_offset(t, delta); +} + +void mt76x0_get_power_info(struct mt76x02_dev *dev, + struct ieee80211_channel *chan, s8 *tp) +{ + static const struct mt76x0_chan_map { + u8 chan; + u8 offset; + } chan_map[] = { + { 2, 0 }, { 4, 2 }, { 6, 4 }, { 8, 6 }, + { 10, 8 }, { 12, 10 }, { 14, 12 }, { 38, 0 }, + { 44, 2 }, { 48, 4 }, { 54, 6 }, { 60, 8 }, + { 64, 10 }, { 102, 12 }, { 108, 14 }, { 112, 16 }, + { 118, 18 }, { 124, 20 }, { 128, 22 }, { 134, 24 }, + { 140, 26 }, { 151, 28 }, { 157, 30 }, { 161, 32 }, + { 167, 34 }, { 171, 36 }, { 175, 38 }, + }; + u8 offset, addr; + int i, idx = 0; + u16 data; + + if (mt76x0_tssi_enabled(dev)) { + s8 target_power; + + if (chan->band == NL80211_BAND_5GHZ) + data = mt76x02_eeprom_get(dev, MT_EE_5G_TARGET_POWER); + else + data = mt76x02_eeprom_get(dev, MT_EE_2G_TARGET_POWER); + target_power = (data & 0xff) - dev->rate_power.ofdm[7]; + *tp = target_power + mt76x0_get_delta(dev); + + return; + } + + for (i = 0; i < ARRAY_SIZE(chan_map); i++) { + if (chan->hw_value <= chan_map[i].chan) { + idx = (chan->hw_value == chan_map[i].chan); + offset = chan_map[i].offset; + break; + } + } + if (i == ARRAY_SIZE(chan_map)) + offset = chan_map[0].offset; + + if (chan->band == NL80211_BAND_2GHZ) { + addr = MT_EE_TX_POWER_DELTA_BW80 + offset; + } else { + switch (chan->hw_value) { + case 42: + offset = 2; + break; + case 58: + offset = 8; + break; + case 106: + offset = 14; + break; + case 122: + offset = 20; + break; + case 155: + offset = 30; + break; + default: + break; + } + addr = MT_EE_TX_POWER_0_GRP4_TSSI_SLOPE + 2 + offset; + } + + data = mt76x02_eeprom_get(dev, addr); + *tp = data >> (8 * idx); + if (*tp < 0 || *tp > 0x3f) + *tp = 5; +} + +static int mt76x0_check_eeprom(struct mt76x02_dev *dev) +{ + u16 val; + + val = get_unaligned_le16(dev->mt76.eeprom.data); + if (!val) + val = get_unaligned_le16(dev->mt76.eeprom.data + + MT_EE_PCI_ID); + + switch (val) { + case 0x7650: + case 0x7610: + return 0; + default: + dev_err(dev->mt76.dev, "EEPROM data check failed: %04x\n", + val); + return -EINVAL; + } +} + +static int mt76x0_load_eeprom(struct mt76x02_dev *dev) +{ + int found; + + found = mt76_eeprom_init(&dev->mt76, MT76X0_EEPROM_SIZE); + if (found < 0) + return found; + + if (found && !mt76x0_check_eeprom(dev)) + return 0; + + found = mt76x0_efuse_physical_size_check(dev); + if (found < 0) + return found; + + return mt76x02_get_efuse_data(dev, 0, dev->mt76.eeprom.data, + MT76X0_EEPROM_SIZE, MT_EE_READ); +} + +int mt76x0_eeprom_init(struct mt76x02_dev *dev) +{ + u8 version, fae; + u16 data; + int err; + + err = mt76x0_load_eeprom(dev); + if (err < 0) + return err; + + data = mt76x02_eeprom_get(dev, MT_EE_VERSION); + version = data >> 8; + fae = data; + + if (version > MT76X0U_EE_MAX_VER) + dev_warn(dev->mt76.dev, + "Warning: unsupported EEPROM version %02hhx\n", + version); + dev_info(dev->mt76.dev, "EEPROM ver:%02hhx fae:%02hhx\n", + version, fae); + + memcpy(dev->mphy.macaddr, (u8 *)dev->mt76.eeprom.data + MT_EE_MAC_ADDR, + ETH_ALEN); + + err = mt76_eeprom_override(&dev->mphy); + if (err) + return err; + + mt76x02_mac_setaddr(dev, dev->mphy.macaddr); + + mt76x0_set_chip_cap(dev); + mt76x0_set_freq_offset(dev); + mt76x0_set_temp_offset(dev); + + return 0; +} + +MODULE_DESCRIPTION("MediaTek MT76x EEPROM helpers"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.h b/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.h new file mode 100644 index 000000000..08f1b10bf --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/eeprom.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2014 Felix Fietkau + * Copyright (C) 2015 Jakub Kicinski + * Copyright (C) 2018 Stanislaw Gruszka + */ + +#ifndef __MT76X0U_EEPROM_H +#define __MT76X0U_EEPROM_H + +#include "../mt76x02_eeprom.h" + +struct mt76x02_dev; + +#define MT76X0U_EE_MAX_VER 0x0c +#define MT76X0_EEPROM_SIZE 512 + +int mt76x0_eeprom_init(struct mt76x02_dev *dev); +void mt76x0_read_rx_gain(struct mt76x02_dev *dev); +void mt76x0_get_tx_power_per_rate(struct mt76x02_dev *dev, + struct ieee80211_channel *chan, + struct mt76x02_rate_power *t); +void mt76x0_get_power_info(struct mt76x02_dev *dev, + struct ieee80211_channel *chan, s8 *tp); + +static inline s8 s6_to_s8(u32 val) +{ + s8 ret = val & GENMASK(5, 0); + + if (ret & BIT(5)) + ret -= BIT(6); + return ret; +} + +static inline bool mt76x0_tssi_enabled(struct mt76x02_dev *dev) +{ + return (mt76x02_eeprom_get(dev, MT_EE_NIC_CONF_1) & + MT_EE_NIC_CONF_1_TX_ALC_EN); +} + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/init.c b/drivers/net/wireless/mediatek/mt76/mt76x0/init.c new file mode 100644 index 000000000..6257460f8 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/init.c @@ -0,0 +1,266 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * (c) Copyright 2002-2010, Ralink Technology, Inc. + * Copyright (C) 2014 Felix Fietkau + * Copyright (C) 2015 Jakub Kicinski + * Copyright (C) 2018 Stanislaw Gruszka + */ + +#include "mt76x0.h" +#include "eeprom.h" +#include "mcu.h" +#include "initvals.h" +#include "initvals_init.h" +#include "../mt76x02_phy.h" + +static void +mt76x0_set_wlan_state(struct mt76x02_dev *dev, u32 val, bool enable) +{ + u32 mask = MT_CMB_CTRL_XTAL_RDY | MT_CMB_CTRL_PLL_LD; + + /* Note: we don't turn off WLAN_CLK because that makes the device + * not respond properly on the probe path. + * In case anyone (PSM?) wants to use this function we can + * bring the clock stuff back and fixup the probe path. + */ + + if (enable) + val |= (MT_WLAN_FUN_CTRL_WLAN_EN | + MT_WLAN_FUN_CTRL_WLAN_CLK_EN); + else + val &= ~(MT_WLAN_FUN_CTRL_WLAN_EN); + + mt76_wr(dev, MT_WLAN_FUN_CTRL, val); + udelay(20); + + /* Note: vendor driver tries to disable/enable wlan here and retry + * but the code which does it is so buggy it must have never + * triggered, so don't bother. + */ + if (enable && !mt76_poll(dev, MT_CMB_CTRL, mask, mask, 2000)) + dev_err(dev->mt76.dev, "PLL and XTAL check failed\n"); +} + +void mt76x0_chip_onoff(struct mt76x02_dev *dev, bool enable, bool reset) +{ + u32 val; + + val = mt76_rr(dev, MT_WLAN_FUN_CTRL); + + if (reset) { + val |= MT_WLAN_FUN_CTRL_GPIO_OUT_EN; + val &= ~MT_WLAN_FUN_CTRL_FRC_WL_ANT_SEL; + + if (val & MT_WLAN_FUN_CTRL_WLAN_EN) { + val |= (MT_WLAN_FUN_CTRL_WLAN_RESET | + MT_WLAN_FUN_CTRL_WLAN_RESET_RF); + mt76_wr(dev, MT_WLAN_FUN_CTRL, val); + udelay(20); + + val &= ~(MT_WLAN_FUN_CTRL_WLAN_RESET | + MT_WLAN_FUN_CTRL_WLAN_RESET_RF); + } + } + + mt76_wr(dev, MT_WLAN_FUN_CTRL, val); + udelay(20); + + mt76x0_set_wlan_state(dev, val, enable); +} +EXPORT_SYMBOL_GPL(mt76x0_chip_onoff); + +static void mt76x0_reset_csr_bbp(struct mt76x02_dev *dev) +{ + mt76_wr(dev, MT_MAC_SYS_CTRL, + MT_MAC_SYS_CTRL_RESET_CSR | + MT_MAC_SYS_CTRL_RESET_BBP); + msleep(200); + mt76_clear(dev, MT_MAC_SYS_CTRL, + MT_MAC_SYS_CTRL_RESET_CSR | + MT_MAC_SYS_CTRL_RESET_BBP); +} + +#define RANDOM_WRITE(dev, tab) \ + mt76_wr_rp(dev, MT_MCU_MEMMAP_WLAN, \ + tab, ARRAY_SIZE(tab)) + +static int mt76x0_init_bbp(struct mt76x02_dev *dev) +{ + int ret, i; + + ret = mt76x0_phy_wait_bbp_ready(dev); + if (ret) + return ret; + + RANDOM_WRITE(dev, mt76x0_bbp_init_tab); + + for (i = 0; i < ARRAY_SIZE(mt76x0_bbp_switch_tab); i++) { + const struct mt76x0_bbp_switch_item *item = &mt76x0_bbp_switch_tab[i]; + const struct mt76_reg_pair *pair = &item->reg_pair; + + if (((RF_G_BAND | RF_BW_20) & item->bw_band) == (RF_G_BAND | RF_BW_20)) + mt76_wr(dev, pair->reg, pair->value); + } + + RANDOM_WRITE(dev, mt76x0_dcoc_tab); + + return 0; +} + +static void mt76x0_init_mac_registers(struct mt76x02_dev *dev) +{ + RANDOM_WRITE(dev, common_mac_reg_table); + + /* Enable PBF and MAC clock SYS_CTRL[11:10] = 0x3 */ + RANDOM_WRITE(dev, mt76x0_mac_reg_table); + + /* Release BBP and MAC reset MAC_SYS_CTRL[1:0] = 0x0 */ + mt76_clear(dev, MT_MAC_SYS_CTRL, 0x3); + + /* Set 0x141C[15:12]=0xF */ + mt76_set(dev, MT_EXT_CCA_CFG, 0xf000); + + mt76_clear(dev, MT_FCE_L2_STUFF, MT_FCE_L2_STUFF_WR_MPDU_LEN_EN); + + /* + * tx_ring 9 is for mgmt frame + * tx_ring 8 is for in-band command frame. + * WMM_RG0_TXQMA: this register setting is for FCE to + * define the rule of tx_ring 9 + * WMM_RG1_TXQMA: this register setting is for FCE to + * define the rule of tx_ring 8 + */ + mt76_rmw(dev, MT_WMM_CTRL, 0x3ff, 0x201); +} + +void mt76x0_mac_stop(struct mt76x02_dev *dev) +{ + int i = 200, ok = 0; + + mt76_clear(dev, MT_TXOP_CTRL_CFG, MT_TXOP_ED_CCA_EN); + + /* Page count on TxQ */ + while (i-- && ((mt76_rr(dev, 0x0438) & 0xffffffff) || + (mt76_rr(dev, 0x0a30) & 0x000000ff) || + (mt76_rr(dev, 0x0a34) & 0x00ff00ff))) + msleep(10); + + if (!mt76_poll(dev, MT_MAC_STATUS, MT_MAC_STATUS_TX, 0, 1000)) + dev_warn(dev->mt76.dev, "Warning: MAC TX did not stop!\n"); + + mt76_clear(dev, MT_MAC_SYS_CTRL, MT_MAC_SYS_CTRL_ENABLE_RX | + MT_MAC_SYS_CTRL_ENABLE_TX); + + /* Page count on RxQ */ + for (i = 0; i < 200; i++) { + if (!(mt76_rr(dev, MT_RXQ_STA) & 0x00ff0000) && + !mt76_rr(dev, 0x0a30) && + !mt76_rr(dev, 0x0a34)) { + if (ok++ > 5) + break; + continue; + } + msleep(1); + } + + if (!mt76_poll(dev, MT_MAC_STATUS, MT_MAC_STATUS_RX, 0, 1000)) + dev_warn(dev->mt76.dev, "Warning: MAC RX did not stop!\n"); +} +EXPORT_SYMBOL_GPL(mt76x0_mac_stop); + +int mt76x0_init_hardware(struct mt76x02_dev *dev) +{ + int ret, i, k; + + if (!mt76x02_wait_for_wpdma(&dev->mt76, 1000)) + return -EIO; + + /* Wait for ASIC ready after FW load. */ + if (!mt76x02_wait_for_mac(&dev->mt76)) + return -ETIMEDOUT; + + mt76x0_reset_csr_bbp(dev); + ret = mt76x02_mcu_function_select(dev, Q_SELECT, 1); + if (ret) + return ret; + + mt76x0_init_mac_registers(dev); + + if (!mt76x02_wait_for_txrx_idle(&dev->mt76)) + return -EIO; + + ret = mt76x0_init_bbp(dev); + if (ret) + return ret; + + dev->mt76.rxfilter = mt76_rr(dev, MT_RX_FILTR_CFG); + + for (i = 0; i < 16; i++) + for (k = 0; k < 4; k++) + mt76x02_mac_shared_key_setup(dev, i, k, NULL); + + for (i = 0; i < 256; i++) + mt76x02_mac_wcid_setup(dev, i, 0, NULL); + + ret = mt76x0_eeprom_init(dev); + if (ret) + return ret; + + mt76x0_phy_init(dev); + + return 0; +} +EXPORT_SYMBOL_GPL(mt76x0_init_hardware); + +static void +mt76x0_init_txpower(struct mt76x02_dev *dev, + struct ieee80211_supported_band *sband) +{ + struct ieee80211_channel *chan; + struct mt76x02_rate_power t; + s8 tp; + int i; + + for (i = 0; i < sband->n_channels; i++) { + chan = &sband->channels[i]; + + mt76x0_get_tx_power_per_rate(dev, chan, &t); + mt76x0_get_power_info(dev, chan, &tp); + + chan->orig_mpwr = (mt76x02_get_max_rate_power(&t) + tp) / 2; + chan->max_power = min_t(int, chan->max_reg_power, + chan->orig_mpwr); + } +} + +int mt76x0_register_device(struct mt76x02_dev *dev) +{ + int ret; + + ret = mt76x02_init_device(dev); + if (ret) + return ret; + + mt76x02_config_mac_addr_list(dev); + + ret = mt76_register_device(&dev->mt76, true, mt76x02_rates, + ARRAY_SIZE(mt76x02_rates)); + if (ret) + return ret; + + if (dev->mphy.cap.has_5ghz) { + struct ieee80211_supported_band *sband; + + sband = &dev->mphy.sband_5g.sband; + sband->vht_cap.cap &= ~IEEE80211_VHT_CAP_RXLDPC; + mt76x0_init_txpower(dev, sband); + } + + if (dev->mphy.cap.has_2ghz) + mt76x0_init_txpower(dev, &dev->mphy.sband_2g.sband); + + mt76x02_init_debugfs(dev); + + return 0; +} +EXPORT_SYMBOL_GPL(mt76x0_register_device); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/initvals.h b/drivers/net/wireless/mediatek/mt76/mt76x0/initvals.h new file mode 100644 index 000000000..99808ed0c --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/initvals.h @@ -0,0 +1,85 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * (c) Copyright 2002-2010, Ralink Technology, Inc. + * Copyright (C) 2015 Jakub Kicinski + * Copyright (C) 2018 Stanislaw Gruszka + * Copyright (C) 2018 Lorenzo Bianconi + */ + +#ifndef __MT76X0U_INITVALS_H +#define __MT76X0U_INITVALS_H + +#include "phy.h" + +static const struct mt76x0_bbp_switch_item mt76x0_bbp_switch_tab[] = { + { RF_G_BAND | RF_BW_20 | RF_BW_40, { MT_BBP(AGC, 4), 0x1FEDA049 } }, + { RF_A_BAND | RF_BW_20 | RF_BW_40 | RF_BW_80, { MT_BBP(AGC, 4), 0x1FECA054 } }, + + { RF_G_BAND | RF_BW_20 | RF_BW_40, { MT_BBP(AGC, 6), 0x00000045 } }, + { RF_A_BAND | RF_BW_20 | RF_BW_40 | RF_BW_80, { MT_BBP(AGC, 6), 0x0000000A } }, + + { RF_G_BAND | RF_BW_20 | RF_BW_40, { MT_BBP(AGC, 8), 0x16344EF0 } }, + { RF_A_BAND | RF_BW_20 | RF_BW_40 | RF_BW_80, { MT_BBP(AGC, 8), 0x122C54F2 } }, + + { RF_G_BAND | RF_BW_20, { MT_BBP(AGC, 12), 0x05052879 } }, + { RF_G_BAND | RF_BW_40, { MT_BBP(AGC, 12), 0x050528F9 } }, + { RF_A_BAND | RF_BW_20 | RF_BW_40 | RF_BW_80, { MT_BBP(AGC, 12), 0x050528F9 } }, + + { RF_G_BAND | RF_BW_20 | RF_BW_40, { MT_BBP(AGC, 13), 0x35050004 } }, + { RF_A_BAND | RF_BW_20 | RF_BW_40 | RF_BW_80, { MT_BBP(AGC, 13), 0x2C3A0406 } }, + + { RF_G_BAND | RF_BW_20 | RF_BW_40, { MT_BBP(AGC, 14), 0x310F2E3C } }, + { RF_A_BAND | RF_BW_20 | RF_BW_40 | RF_BW_80, { MT_BBP(AGC, 14), 0x310F2A3F } }, + + { RF_G_BAND | RF_BW_20 | RF_BW_40, { MT_BBP(AGC, 26), 0x007C2005 } }, + { RF_A_BAND | RF_BW_20 | RF_BW_40 | RF_BW_80, { MT_BBP(AGC, 26), 0x007C2005 } }, + + { RF_G_BAND | RF_BW_20 | RF_BW_40, { MT_BBP(AGC, 27), 0x000000E1 } }, + { RF_A_BAND | RF_BW_20 | RF_BW_40 | RF_BW_80, { MT_BBP(AGC, 27), 0x000000EC } }, + + { RF_G_BAND | RF_BW_20, { MT_BBP(AGC, 28), 0x00060806 } }, + { RF_G_BAND | RF_BW_40, { MT_BBP(AGC, 28), 0x00050806 } }, + { RF_A_BAND | RF_BW_40, { MT_BBP(AGC, 28), 0x00060801 } }, + { RF_A_BAND | RF_BW_20 | RF_BW_80, { MT_BBP(AGC, 28), 0x00060806 } }, + + { RF_A_BAND | RF_BW_20 | RF_BW_40 | RF_BW_80, { MT_BBP(RXO, 28), 0x0000008A } }, + + { RF_G_BAND | RF_BW_20 | RF_BW_40, { MT_BBP(AGC, 31), 0x00000E23 } }, + { RF_A_BAND | RF_BW_20 | RF_BW_40 | RF_BW_80, { MT_BBP(AGC, 31), 0x00000E13 } }, + + { RF_G_BAND | RF_BW_20 | RF_BW_40, { MT_BBP(AGC, 32), 0x00003218 } }, + { RF_A_BAND | RF_BW_20 | RF_BW_40 | RF_BW_80, { MT_BBP(AGC, 32), 0x0000181C } }, + + { RF_G_BAND | RF_BW_20 | RF_BW_40, { MT_BBP(AGC, 33), 0x00003240 } }, + { RF_A_BAND | RF_BW_20 | RF_BW_40 | RF_BW_80, { MT_BBP(AGC, 33), 0x00003218 } }, + + { RF_G_BAND | RF_BW_20, { MT_BBP(AGC, 35), 0x11111616 } }, + { RF_G_BAND | RF_BW_40, { MT_BBP(AGC, 35), 0x11111516 } }, + { RF_A_BAND | RF_BW_20 | RF_BW_40 | RF_BW_80, { MT_BBP(AGC, 35), 0x11111111 } }, + + { RF_G_BAND | RF_BW_20, { MT_BBP(AGC, 39), 0x2A2A3036 } }, + { RF_G_BAND | RF_BW_40, { MT_BBP(AGC, 39), 0x2A2A2C36 } }, + { RF_A_BAND | RF_BW_20 | RF_BW_40 | RF_BW_80, { MT_BBP(AGC, 39), 0x2A2A2A2A } }, + + { RF_G_BAND | RF_BW_20, { MT_BBP(AGC, 43), 0x27273438 } }, + { RF_G_BAND | RF_BW_40, { MT_BBP(AGC, 43), 0x27272D38 } }, + { RF_A_BAND | RF_BW_20 | RF_BW_40 | RF_BW_80, { MT_BBP(AGC, 43), 0x27271A1A } }, + + { RF_G_BAND | RF_BW_20 | RF_BW_40, { MT_BBP(AGC, 51), 0x17171C1C } }, + { RF_A_BAND | RF_BW_20 | RF_BW_40 | RF_BW_80, { MT_BBP(AGC, 51), 0xFFFFFFFF } }, + + { RF_G_BAND | RF_BW_20, { MT_BBP(AGC, 53), 0x26262A2F } }, + { RF_G_BAND | RF_BW_40, { MT_BBP(AGC, 53), 0x2626322F } }, + { RF_A_BAND | RF_BW_20 | RF_BW_40 | RF_BW_80, { MT_BBP(AGC, 53), 0xFFFFFFFF } }, + + { RF_G_BAND | RF_BW_20 | RF_BW_40, { MT_BBP(AGC, 55), 0x40404040 } }, + { RF_A_BAND | RF_BW_20 | RF_BW_40 | RF_BW_80, { MT_BBP(AGC, 55), 0xFFFFFFFF } }, + + { RF_G_BAND | RF_BW_20 | RF_BW_40, { MT_BBP(AGC, 58), 0x00001010 } }, + { RF_A_BAND | RF_BW_20 | RF_BW_40 | RF_BW_80, { MT_BBP(AGC, 58), 0x00000000 } }, + + { RF_G_BAND | RF_BW_20 | RF_BW_40, { MT_BBP(RXFE, 0), 0x3D5000E0 } }, + { RF_A_BAND | RF_BW_20 | RF_BW_40 | RF_BW_80, { MT_BBP(RXFE, 0), 0x895000E0 } }, +}; + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/initvals_init.h b/drivers/net/wireless/mediatek/mt76/mt76x0/initvals_init.h new file mode 100644 index 000000000..9e99ba75f --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/initvals_init.h @@ -0,0 +1,159 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * (c) Copyright 2002-2010, Ralink Technology, Inc. + * Copyright (C) 2015 Jakub Kicinski + * Copyright (C) 2018 Stanislaw Gruszka + * Copyright (C) 2018 Lorenzo Bianconi + */ + +#ifndef __MT76X0U_INITVALS_INIT_H +#define __MT76X0U_INITVALS_INIT_H + +#include "phy.h" + +static const struct mt76_reg_pair common_mac_reg_table[] = { + { MT_BCN_OFFSET(0), 0xf8f0e8e0 }, + { MT_BCN_OFFSET(1), 0x6f77d0c8 }, + { MT_LEGACY_BASIC_RATE, 0x0000013f }, + { MT_HT_BASIC_RATE, 0x00008003 }, + { MT_MAC_SYS_CTRL, 0x00000000 }, + { MT_RX_FILTR_CFG, 0x00017f97 }, + { MT_BKOFF_SLOT_CFG, 0x00000209 }, + { MT_TX_SW_CFG0, 0x00000000 }, + { MT_TX_SW_CFG1, 0x00080606 }, + { MT_TX_LINK_CFG, 0x00001020 }, + { MT_TX_TIMEOUT_CFG, 0x000a2090 }, + { MT_MAX_LEN_CFG, 0xa0fff | 0x00001000 }, + { MT_LED_CFG, 0x7f031e46 }, + { MT_PBF_TX_MAX_PCNT, 0x1fbf1f1f }, + { MT_PBF_RX_MAX_PCNT, 0x0000fe9f }, + { MT_TX_RETRY_CFG, 0x47d01f0f }, + { MT_AUTO_RSP_CFG, 0x00000013 }, + { MT_CCK_PROT_CFG, 0x07f40003 }, + { MT_OFDM_PROT_CFG, 0x07f42004 }, + { MT_PBF_CFG, 0x00f40006 }, + { MT_WPDMA_GLO_CFG, 0x00000030 }, + { MT_GF20_PROT_CFG, 0x01742004 }, + { MT_GF40_PROT_CFG, 0x03f42084 }, + { MT_MM20_PROT_CFG, 0x01742004 }, + { MT_MM40_PROT_CFG, 0x03f42084 }, + { MT_TXOP_CTRL_CFG, 0x0000583f }, + { MT_TX_RTS_CFG, 0x00ffff20 }, + { MT_EXP_ACK_TIME, 0x002400ca }, + { MT_TXOP_HLDR_ET, 0x00000002 }, + { MT_XIFS_TIME_CFG, 0x33a41010 }, + { MT_PWR_PIN_CFG, 0x00000000 }, +}; + +static const struct mt76_reg_pair mt76x0_mac_reg_table[] = { + { MT_IOCFG_6, 0xa0040080 }, + { MT_PBF_SYS_CTRL, 0x00080c00 }, + { MT_PBF_CFG, 0x77723c1f }, + { MT_FCE_PSE_CTRL, 0x00000001 }, + { MT_AMPDU_MAX_LEN_20M1S, 0xAAA99887 }, + { MT_TX_SW_CFG0, 0x00000601 }, + { MT_TX_SW_CFG1, 0x00040000 }, + { MT_TX_SW_CFG2, 0x00000000 }, + { 0xa44, 0x00000000 }, + { MT_HEADER_TRANS_CTRL_REG, 0x00000000 }, + { MT_TSO_CTRL, 0x00000000 }, + { MT_BB_PA_MODE_CFG1, 0x00500055 }, + { MT_RF_PA_MODE_CFG1, 0x00500055 }, + { MT_TX_ALC_CFG_0, 0x2F2F000C }, + { MT_TX0_BB_GAIN_ATTEN, 0x00000000 }, + { MT_TX_PWR_CFG_0, 0x3A3A3A3A }, + { MT_TX_PWR_CFG_1, 0x3A3A3A3A }, + { MT_TX_PWR_CFG_2, 0x3A3A3A3A }, + { MT_TX_PWR_CFG_3, 0x3A3A3A3A }, + { MT_TX_PWR_CFG_4, 0x3A3A3A3A }, + { MT_TX_PWR_CFG_7, 0x3A3A3A3A }, + { MT_TX_PWR_CFG_8, 0x0000003A }, + { MT_TX_PWR_CFG_9, 0x0000003A }, + { 0x150C, 0x00000002 }, + { 0x1238, 0x001700C8 }, + { MT_LDO_CTRL_0, 0x00A647B6 }, + { MT_LDO_CTRL_1, 0x6B006464 }, + { MT_HT_BASIC_RATE, 0x00004003 }, + { MT_HT_CTRL_CFG, 0x000001FF }, + { MT_TXOP_HLDR_ET, 0x00000000 }, + { MT_PN_PAD_MODE, 0x00000003 }, + { MT_TX_PROT_CFG6, 0xe3f42004 }, + { MT_TX_PROT_CFG7, 0xe3f42084 }, + { MT_TX_PROT_CFG8, 0xe3f42104 }, + { MT_VHT_HT_FBK_CFG1, 0xedcba980 }, +}; + +static const struct mt76_reg_pair mt76x0_bbp_init_tab[] = { + { MT_BBP(CORE, 1), 0x00000002 }, + { MT_BBP(CORE, 4), 0x00000000 }, + { MT_BBP(CORE, 24), 0x00000000 }, + { MT_BBP(CORE, 32), 0x4003000a }, + { MT_BBP(CORE, 42), 0x00000000 }, + { MT_BBP(CORE, 44), 0x00000000 }, + { MT_BBP(IBI, 11), 0x0FDE8081 }, + { MT_BBP(AGC, 0), 0x00021400 }, + { MT_BBP(AGC, 1), 0x00000003 }, + { MT_BBP(AGC, 2), 0x003A6464 }, + { MT_BBP(AGC, 15), 0x88A28CB8 }, + { MT_BBP(AGC, 22), 0x00001E21 }, + { MT_BBP(AGC, 23), 0x0000272C }, + { MT_BBP(AGC, 24), 0x00002F3A }, + { MT_BBP(AGC, 25), 0x8000005A }, + { MT_BBP(AGC, 26), 0x007C2005 }, + { MT_BBP(AGC, 33), 0x00003238 }, + { MT_BBP(AGC, 34), 0x000A0C0C }, + { MT_BBP(AGC, 37), 0x2121262C }, + { MT_BBP(AGC, 41), 0x38383E45 }, + { MT_BBP(AGC, 57), 0x00001010 }, + { MT_BBP(AGC, 59), 0xBAA20E96 }, + { MT_BBP(AGC, 63), 0x00000001 }, + { MT_BBP(TXC, 0), 0x00280403 }, + { MT_BBP(TXC, 1), 0x00000000 }, + { MT_BBP(RXC, 1), 0x00000012 }, + { MT_BBP(RXC, 2), 0x00000011 }, + { MT_BBP(RXC, 3), 0x00000005 }, + { MT_BBP(RXC, 4), 0x00000000 }, + { MT_BBP(RXC, 5), 0xF977C4EC }, + { MT_BBP(RXC, 7), 0x00000090 }, + { MT_BBP(TXO, 8), 0x00000000 }, + { MT_BBP(TXBE, 0), 0x00000000 }, + { MT_BBP(TXBE, 4), 0x00000004 }, + { MT_BBP(TXBE, 6), 0x00000000 }, + { MT_BBP(TXBE, 8), 0x00000014 }, + { MT_BBP(TXBE, 9), 0x20000000 }, + { MT_BBP(TXBE, 10), 0x00000000 }, + { MT_BBP(TXBE, 12), 0x00000000 }, + { MT_BBP(TXBE, 13), 0x00000000 }, + { MT_BBP(TXBE, 14), 0x00000000 }, + { MT_BBP(TXBE, 15), 0x00000000 }, + { MT_BBP(TXBE, 16), 0x00000000 }, + { MT_BBP(TXBE, 17), 0x00000000 }, + { MT_BBP(RXFE, 1), 0x00008800 }, + { MT_BBP(RXFE, 3), 0x00000000 }, + { MT_BBP(RXFE, 4), 0x00000000 }, + { MT_BBP(RXO, 13), 0x00000192 }, + { MT_BBP(RXO, 14), 0x00060612 }, + { MT_BBP(RXO, 15), 0xC8321B18 }, + { MT_BBP(RXO, 16), 0x0000001E }, + { MT_BBP(RXO, 17), 0x00000000 }, + { MT_BBP(RXO, 18), 0xCC00A993 }, + { MT_BBP(RXO, 19), 0xB9CB9CB9 }, + { MT_BBP(RXO, 20), 0x26c00057 }, + { MT_BBP(RXO, 21), 0x00000001 }, + { MT_BBP(RXO, 24), 0x00000006 }, + { MT_BBP(RXO, 28), 0x0000003F }, +}; + +static const struct mt76_reg_pair mt76x0_dcoc_tab[] = { + { MT_BBP(CAL, 47), 0x000010F0 }, + { MT_BBP(CAL, 48), 0x00008080 }, + { MT_BBP(CAL, 49), 0x00000F07 }, + { MT_BBP(CAL, 50), 0x00000040 }, + { MT_BBP(CAL, 51), 0x00000404 }, + { MT_BBP(CAL, 52), 0x00080803 }, + { MT_BBP(CAL, 53), 0x00000704 }, + { MT_BBP(CAL, 54), 0x00002828 }, + { MT_BBP(CAL, 55), 0x00005050 }, +}; + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/initvals_phy.h b/drivers/net/wireless/mediatek/mt76/mt76x0/initvals_phy.h new file mode 100644 index 000000000..42a79887b --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/initvals_phy.h @@ -0,0 +1,633 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * (c) Copyright 2002-2010, Ralink Technology, Inc. + * Copyright (C) 2015 Jakub Kicinski + * Copyright (C) 2018 Stanislaw Gruszka + */ + +#ifndef __MT76X0U_PHY_INITVALS_H +#define __MT76X0U_PHY_INITVALS_H + +static const struct mt76_reg_pair mt76x0_rf_central_tab[] = { + { MT_RF(0, 1), 0x01 }, + { MT_RF(0, 2), 0x11 }, + /* R3 ~ R7: VCO Cal */ + { MT_RF(0, 3), 0x73 }, /* VCO Freq Cal */ + { MT_RF(0, 4), 0x30 }, /* R4 b<7>=1, VCO cal */ + { MT_RF(0, 5), 0x00 }, + { MT_RF(0, 6), 0x41 }, + { MT_RF(0, 7), 0x00 }, + { MT_RF(0, 8), 0x00 }, + { MT_RF(0, 9), 0x00 }, + { MT_RF(0, 10), 0x0C }, + { MT_RF(0, 11), 0x00 }, + { MT_RF(0, 12), 0x00 }, + /* BG */ + { MT_RF(0, 13), 0x00 }, + { MT_RF(0, 14), 0x00 }, + { MT_RF(0, 15), 0x00 }, + /* LDO */ + { MT_RF(0, 19), 0x20 }, + { MT_RF(0, 20), 0x22 }, + { MT_RF(0, 21), 0x12 }, + { MT_RF(0, 23), 0x00 }, + { MT_RF(0, 24), 0x33 }, + { MT_RF(0, 25), 0x00 }, + /* PLL */ + { MT_RF(0, 26), 0x00 }, + { MT_RF(0, 27), 0x00 }, + { MT_RF(0, 28), 0x00 }, + { MT_RF(0, 29), 0x00 }, + { MT_RF(0, 30), 0x00 }, + { MT_RF(0, 31), 0x00 }, + { MT_RF(0, 32), 0x00 }, + { MT_RF(0, 33), 0x00 }, + { MT_RF(0, 34), 0x00 }, + { MT_RF(0, 35), 0x00 }, + { MT_RF(0, 36), 0x00 }, + { MT_RF(0, 37), 0x00 }, + /* LO Buffer */ + { MT_RF(0, 38), 0x2F }, + /* Test Ports */ + { MT_RF(0, 64), 0x00 }, + { MT_RF(0, 65), 0x80 }, + { MT_RF(0, 66), 0x01 }, + { MT_RF(0, 67), 0x04 }, + /* ADC-DAC */ + { MT_RF(0, 68), 0x00 }, + { MT_RF(0, 69), 0x08 }, + { MT_RF(0, 70), 0x08 }, + { MT_RF(0, 71), 0x40 }, + { MT_RF(0, 72), 0xD0 }, + { MT_RF(0, 73), 0x93 }, +}; + +static const struct mt76_reg_pair mt76x0_rf_2g_channel_0_tab[] = { + /* RX logic operation */ + { MT_RF(5, 2), 0x0C }, /* 5G+2G */ + { MT_RF(5, 3), 0x00 }, + /* TX logic operation */ + { MT_RF(5, 4), 0x00 }, + { MT_RF(5, 5), 0x84 }, + { MT_RF(5, 6), 0x02 }, + /* LDO */ + { MT_RF(5, 7), 0x00 }, + { MT_RF(5, 8), 0x00 }, + { MT_RF(5, 9), 0x00 }, + /* RX */ + { MT_RF(5, 10), 0x51 }, + { MT_RF(5, 11), 0x22 }, + { MT_RF(5, 12), 0x22 }, + { MT_RF(5, 13), 0x0F }, + { MT_RF(5, 14), 0x47 }, + { MT_RF(5, 15), 0x25 }, + { MT_RF(5, 16), 0xC7 }, + { MT_RF(5, 17), 0x00 }, + { MT_RF(5, 18), 0x00 }, + { MT_RF(5, 19), 0x30 }, + { MT_RF(5, 20), 0x33 }, + { MT_RF(5, 21), 0x02 }, + { MT_RF(5, 22), 0x32 }, + { MT_RF(5, 23), 0x00 }, + { MT_RF(5, 24), 0x25 }, + { MT_RF(5, 26), 0x00 }, + { MT_RF(5, 27), 0x12 }, + { MT_RF(5, 28), 0x0F }, + { MT_RF(5, 29), 0x00 }, + /* LOGEN */ + { MT_RF(5, 30), 0x51 }, + { MT_RF(5, 31), 0x35 }, + { MT_RF(5, 32), 0x31 }, + { MT_RF(5, 33), 0x31 }, + { MT_RF(5, 34), 0x34 }, + { MT_RF(5, 35), 0x03 }, + { MT_RF(5, 36), 0x00 }, + /* TX */ + { MT_RF(5, 37), 0xDD }, + { MT_RF(5, 38), 0xB3 }, + { MT_RF(5, 39), 0x33 }, + { MT_RF(5, 40), 0xB1 }, + { MT_RF(5, 41), 0x71 }, + { MT_RF(5, 42), 0xF2 }, + { MT_RF(5, 43), 0x47 }, + { MT_RF(5, 44), 0x77 }, + { MT_RF(5, 45), 0x0E }, + { MT_RF(5, 46), 0x10 }, + { MT_RF(5, 47), 0x00 }, + { MT_RF(5, 48), 0x53 }, + { MT_RF(5, 49), 0x03 }, + { MT_RF(5, 50), 0xEF }, + { MT_RF(5, 51), 0xC7 }, + { MT_RF(5, 52), 0x62 }, + { MT_RF(5, 53), 0x62 }, + { MT_RF(5, 54), 0x00 }, + { MT_RF(5, 55), 0x00 }, + { MT_RF(5, 56), 0x0F }, + { MT_RF(5, 57), 0x0F }, + { MT_RF(5, 58), 0x16 }, + { MT_RF(5, 59), 0x16 }, + { MT_RF(5, 60), 0x10 }, + { MT_RF(5, 61), 0x10 }, + { MT_RF(5, 62), 0xD0 }, + { MT_RF(5, 63), 0x6C }, + { MT_RF(5, 64), 0x58 }, + { MT_RF(5, 65), 0x58 }, + { MT_RF(5, 66), 0xF2 }, + { MT_RF(5, 67), 0xE8 }, + { MT_RF(5, 68), 0xF0 }, + { MT_RF(5, 69), 0xF0 }, + { MT_RF(5, 127), 0x04 }, +}; + +static const struct mt76_reg_pair mt76x0_rf_5g_channel_0_tab[] = { + /* RX logic operation */ + { MT_RF(6, 2), 0x0C }, + { MT_RF(6, 3), 0x00 }, + /* TX logic operation */ + { MT_RF(6, 4), 0x00 }, + { MT_RF(6, 5), 0x84 }, + { MT_RF(6, 6), 0x02 }, + /* LDO */ + { MT_RF(6, 7), 0x00 }, + { MT_RF(6, 8), 0x00 }, + { MT_RF(6, 9), 0x00 }, + /* RX */ + { MT_RF(6, 10), 0x00 }, + { MT_RF(6, 11), 0x01 }, + { MT_RF(6, 13), 0x23 }, + { MT_RF(6, 14), 0x00 }, + { MT_RF(6, 15), 0x04 }, + { MT_RF(6, 16), 0x22 }, + { MT_RF(6, 18), 0x08 }, + { MT_RF(6, 19), 0x00 }, + { MT_RF(6, 20), 0x00 }, + { MT_RF(6, 21), 0x00 }, + { MT_RF(6, 22), 0xFB }, + /* LOGEN5G */ + { MT_RF(6, 25), 0x76 }, + { MT_RF(6, 26), 0x24 }, + { MT_RF(6, 27), 0x04 }, + { MT_RF(6, 28), 0x00 }, + { MT_RF(6, 29), 0x00 }, + /* TX */ + { MT_RF(6, 37), 0xBB }, + { MT_RF(6, 38), 0xB3 }, + { MT_RF(6, 40), 0x33 }, + { MT_RF(6, 41), 0x33 }, + { MT_RF(6, 43), 0x03 }, + { MT_RF(6, 44), 0xB3 }, + { MT_RF(6, 46), 0x17 }, + { MT_RF(6, 47), 0x0E }, + { MT_RF(6, 48), 0x10 }, + { MT_RF(6, 49), 0x07 }, + { MT_RF(6, 62), 0x00 }, + { MT_RF(6, 63), 0x00 }, + { MT_RF(6, 64), 0xF1 }, + { MT_RF(6, 65), 0x0F }, +}; + +static const struct mt76_reg_pair mt76x0_rf_vga_channel_0_tab[] = { + /* E3 CR */ + { MT_RF(7, 0), 0x47 }, + { MT_RF(7, 1), 0x00 }, + { MT_RF(7, 2), 0x00 }, + { MT_RF(7, 3), 0x00 }, + { MT_RF(7, 4), 0x00 }, + { MT_RF(7, 10), 0x13 }, + { MT_RF(7, 11), 0x0F }, + { MT_RF(7, 12), 0x13 }, + { MT_RF(7, 13), 0x13 }, + { MT_RF(7, 14), 0x13 }, + { MT_RF(7, 15), 0x20 }, + { MT_RF(7, 16), 0x22 }, + { MT_RF(7, 17), 0x7C }, + { MT_RF(7, 18), 0x00 }, + { MT_RF(7, 19), 0x00 }, + { MT_RF(7, 20), 0x00 }, + { MT_RF(7, 21), 0xF1 }, + { MT_RF(7, 22), 0x11 }, + { MT_RF(7, 23), 0xC2 }, + { MT_RF(7, 24), 0x41 }, + { MT_RF(7, 25), 0x20 }, + { MT_RF(7, 26), 0x40 }, + { MT_RF(7, 27), 0xD7 }, + { MT_RF(7, 28), 0xA2 }, + { MT_RF(7, 29), 0x60 }, + { MT_RF(7, 30), 0x49 }, + { MT_RF(7, 31), 0x20 }, + { MT_RF(7, 32), 0x44 }, + { MT_RF(7, 33), 0xC1 }, + { MT_RF(7, 34), 0x60 }, + { MT_RF(7, 35), 0xC0 }, + { MT_RF(7, 61), 0x01 }, + { MT_RF(7, 72), 0x3C }, + { MT_RF(7, 73), 0x34 }, + { MT_RF(7, 74), 0x00 }, +}; + +static const struct mt76x0_rf_switch_item mt76x0_rf_bw_switch_tab[] = { + /* bank, reg bw/band value */ + { MT_RF(0, 17), RF_G_BAND | RF_BW_20, 0x00 }, + { MT_RF(0, 17), RF_G_BAND | RF_BW_40, 0x00 }, + { MT_RF(0, 17), RF_A_BAND | RF_BW_20, 0x00 }, + { MT_RF(0, 17), RF_A_BAND | RF_BW_40, 0x00 }, + { MT_RF(0, 17), RF_A_BAND | RF_BW_80, 0x00 }, + { MT_RF(7, 6), RF_G_BAND | RF_BW_20, 0x40 }, + { MT_RF(7, 6), RF_G_BAND | RF_BW_40, 0x1C }, + { MT_RF(7, 6), RF_A_BAND | RF_BW_20, 0x40 }, + { MT_RF(7, 6), RF_A_BAND | RF_BW_40, 0x20 }, + { MT_RF(7, 6), RF_A_BAND | RF_BW_80, 0x10 }, + { MT_RF(7, 7), RF_G_BAND | RF_BW_20, 0x40 }, + { MT_RF(7, 7), RF_G_BAND | RF_BW_40, 0x20 }, + { MT_RF(7, 7), RF_A_BAND | RF_BW_20, 0x40 }, + { MT_RF(7, 7), RF_A_BAND | RF_BW_40, 0x20 }, + { MT_RF(7, 7), RF_A_BAND | RF_BW_80, 0x10 }, + { MT_RF(7, 8), RF_G_BAND | RF_BW_20, 0x03 }, + { MT_RF(7, 8), RF_G_BAND | RF_BW_40, 0x01 }, + { MT_RF(7, 8), RF_A_BAND | RF_BW_20, 0x03 }, + { MT_RF(7, 8), RF_A_BAND | RF_BW_40, 0x01 }, + { MT_RF(7, 8), RF_A_BAND | RF_BW_80, 0x00 }, + { MT_RF(7, 58), RF_G_BAND | RF_BW_20, 0x40 }, + { MT_RF(7, 58), RF_G_BAND | RF_BW_40, 0x40 }, + { MT_RF(7, 58), RF_A_BAND | RF_BW_20, 0x40 }, + { MT_RF(7, 58), RF_A_BAND | RF_BW_40, 0x40 }, + { MT_RF(7, 58), RF_A_BAND | RF_BW_80, 0x10 }, + { MT_RF(7, 59), RF_G_BAND | RF_BW_20, 0x40 }, + { MT_RF(7, 59), RF_G_BAND | RF_BW_40, 0x40 }, + { MT_RF(7, 59), RF_A_BAND | RF_BW_20, 0x40 }, + { MT_RF(7, 59), RF_A_BAND | RF_BW_40, 0x40 }, + { MT_RF(7, 59), RF_A_BAND | RF_BW_80, 0x10 }, + { MT_RF(7, 60), RF_G_BAND | RF_BW_20, 0xAA }, + { MT_RF(7, 60), RF_G_BAND | RF_BW_40, 0xAA }, + { MT_RF(7, 60), RF_A_BAND | RF_BW_20, 0xAA }, + { MT_RF(7, 60), RF_A_BAND | RF_BW_40, 0xAA }, + { MT_RF(7, 60), RF_A_BAND | RF_BW_80, 0xAA }, + { MT_RF(7, 76), RF_BW_20, 0x40 }, + { MT_RF(7, 76), RF_BW_40, 0x40 }, + { MT_RF(7, 76), RF_BW_80, 0x10 }, + { MT_RF(7, 77), RF_BW_20, 0x40 }, + { MT_RF(7, 77), RF_BW_40, 0x40 }, + { MT_RF(7, 77), RF_BW_80, 0x10 }, +}; + +static const struct mt76x0_rf_switch_item mt76x0_rf_band_switch_tab[] = { + /* bank, reg bw/band value */ + { MT_RF(0, 16), RF_G_BAND, 0x20 }, + { MT_RF(0, 16), RF_A_BAND, 0x20 }, + { MT_RF(0, 18), RF_G_BAND, 0x00 }, + { MT_RF(0, 18), RF_A_BAND, 0x00 }, + { MT_RF(0, 39), RF_G_BAND, 0x36 }, + { MT_RF(0, 39), RF_A_BAND_LB, 0x34 }, + { MT_RF(0, 39), RF_A_BAND_MB, 0x33 }, + { MT_RF(0, 39), RF_A_BAND_HB, 0x31 }, + { MT_RF(0, 39), RF_A_BAND_11J, 0x36 }, + { MT_RF(6, 12), RF_A_BAND_LB, 0x44 }, + { MT_RF(6, 12), RF_A_BAND_MB, 0x44 }, + { MT_RF(6, 12), RF_A_BAND_HB, 0x55 }, + { MT_RF(6, 12), RF_A_BAND_11J, 0x44 }, + { MT_RF(6, 17), RF_A_BAND_LB, 0x02 }, + { MT_RF(6, 17), RF_A_BAND_MB, 0x00 }, + { MT_RF(6, 17), RF_A_BAND_HB, 0x00 }, + { MT_RF(6, 17), RF_A_BAND_11J, 0x05 }, + { MT_RF(6, 24), RF_A_BAND_LB, 0xA1 }, + { MT_RF(6, 24), RF_A_BAND_MB, 0x41 }, + { MT_RF(6, 24), RF_A_BAND_HB, 0x21 }, + { MT_RF(6, 24), RF_A_BAND_11J, 0xE1 }, + { MT_RF(6, 39), RF_A_BAND_LB, 0x36 }, + { MT_RF(6, 39), RF_A_BAND_MB, 0x34 }, + { MT_RF(6, 39), RF_A_BAND_HB, 0x32 }, + { MT_RF(6, 39), RF_A_BAND_11J, 0x37 }, + { MT_RF(6, 42), RF_A_BAND_LB, 0xFB }, + { MT_RF(6, 42), RF_A_BAND_MB, 0xF3 }, + { MT_RF(6, 42), RF_A_BAND_HB, 0xEB }, + { MT_RF(6, 42), RF_A_BAND_11J, 0xEB }, + { MT_RF(6, 127), RF_G_BAND, 0x84 }, + { MT_RF(6, 127), RF_A_BAND, 0x04 }, + { MT_RF(7, 5), RF_G_BAND, 0x40 }, + { MT_RF(7, 5), RF_A_BAND, 0x00 }, + { MT_RF(7, 9), RF_G_BAND, 0x00 }, + { MT_RF(7, 9), RF_A_BAND, 0x00 }, + { MT_RF(7, 70), RF_G_BAND, 0x00 }, + { MT_RF(7, 70), RF_A_BAND, 0x6D }, + { MT_RF(7, 71), RF_G_BAND, 0x00 }, + { MT_RF(7, 71), RF_A_BAND, 0xB0 }, + { MT_RF(7, 78), RF_G_BAND, 0x00 }, + { MT_RF(7, 78), RF_A_BAND, 0x55 }, + { MT_RF(7, 79), RF_G_BAND, 0x00 }, + { MT_RF(7, 79), RF_A_BAND, 0x55 }, +}; + +static const struct mt76x0_freq_item mt76x0_frequency_plan[] = { + { 1, RF_G_BAND, 0x02, 0x3F, 0x28, 0xDD, 0xE2, 0x40, 0x02, 0x40, 0x02, 0, 0, 1, 0x28, 0, 0x30, 0, 0, 0x3 }, /* Freq 2412 */ + { 2, RF_G_BAND, 0x02, 0x3F, 0x3C, 0xDD, 0xE4, 0x40, 0x07, 0x40, 0x02, 0, 0, 1, 0xA1, 0, 0x30, 0, 0, 0x1 }, /* Freq 2417 */ + { 3, RF_G_BAND, 0x02, 0x3F, 0x3C, 0xDD, 0xE2, 0x40, 0x07, 0x40, 0x0B, 0, 0, 1, 0x50, 0, 0x30, 0, 0, 0x0 }, /* Freq 2422 */ + { 4, RF_G_BAND, 0x02, 0x3F, 0x28, 0xDD, 0xD4, 0x40, 0x02, 0x40, 0x09, 0, 0, 1, 0x50, 0, 0x30, 0, 0, 0x0 }, /* Freq 2427 */ + { 5, RF_G_BAND, 0x02, 0x3F, 0x3C, 0xDD, 0xD4, 0x40, 0x07, 0x40, 0x02, 0, 0, 1, 0xA2, 0, 0x30, 0, 0, 0x1 }, /* Freq 2432 */ + { 6, RF_G_BAND, 0x02, 0x3F, 0x3C, 0xDD, 0xD4, 0x40, 0x07, 0x40, 0x07, 0, 0, 1, 0xA2, 0, 0x30, 0, 0, 0x1 }, /* Freq 2437 */ + { 7, RF_G_BAND, 0x02, 0x3F, 0x28, 0xDD, 0xE2, 0x40, 0x02, 0x40, 0x07, 0, 0, 1, 0x28, 0, 0x30, 0, 0, 0x3 }, /* Freq 2442 */ + { 8, RF_G_BAND, 0x02, 0x3F, 0x3C, 0xDD, 0xD4, 0x40, 0x07, 0x40, 0x02, 0, 0, 1, 0xA3, 0, 0x30, 0, 0, 0x1 }, /* Freq 2447 */ + { 9, RF_G_BAND, 0x02, 0x3F, 0x3C, 0xDD, 0xF2, 0x40, 0x07, 0x40, 0x0D, 0, 0, 1, 0x28, 0, 0x30, 0, 0, 0x3 }, /* Freq 2452 */ + { 10, RF_G_BAND, 0x02, 0x3F, 0x28, 0xDD, 0xD4, 0x40, 0x02, 0x40, 0x09, 0, 0, 1, 0x51, 0, 0x30, 0, 0, 0x0 }, /* Freq 2457 */ + { 11, RF_G_BAND, 0x02, 0x3F, 0x3C, 0xDD, 0xD4, 0x40, 0x07, 0x40, 0x02, 0, 0, 1, 0xA4, 0, 0x30, 0, 0, 0x1 }, /* Freq 2462 */ + { 12, RF_G_BAND, 0x02, 0x3F, 0x3C, 0xDD, 0xD4, 0x40, 0x07, 0x40, 0x07, 0, 0, 1, 0xA4, 0, 0x30, 0, 0, 0x1 }, /* Freq 2467 */ + { 13, RF_G_BAND, 0x02, 0x3F, 0x28, 0xDD, 0xF2, 0x40, 0x02, 0x40, 0x02, 0, 0, 1, 0x29, 0, 0x30, 0, 0, 0x3 }, /* Freq 2472 */ + { 14, RF_G_BAND, 0x02, 0x3F, 0x28, 0xDD, 0xF2, 0x40, 0x02, 0x40, 0x04, 0, 0, 1, 0x29, 0, 0x30, 0, 0, 0x3 }, /* Freq 2484 */ + { 183, (RF_A_BAND | RF_A_BAND_11J), 0x02, 0x3F, 0x70, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x17, 0, 0, 1, 0x28, 0, 0x30, 0, 0, 0x3 }, /* Freq 4915 */ + { 184, (RF_A_BAND | RF_A_BAND_11J), 0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x00, 0, 0, 1, 0x29, 0, 0x30, 0, 0, 0x3 }, /* Freq 4920 */ + { 185, (RF_A_BAND | RF_A_BAND_11J), 0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x01, 0, 0, 1, 0x29, 0, 0x30, 0, 0, 0x3 }, /* Freq 4925 */ + { 187, (RF_A_BAND | RF_A_BAND_11J), 0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x03, 0, 0, 1, 0x29, 0, 0x30, 0, 0, 0x3 }, /* Freq 4935 */ + { 188, (RF_A_BAND | RF_A_BAND_11J), 0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x02, 0, 0, 1, 0x29, 0, 0x30, 0, 0, 0x3 }, /* Freq 4940 */ + { 189, (RF_A_BAND | RF_A_BAND_11J), 0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x05, 0, 0, 1, 0x29, 0, 0x30, 0, 0, 0x3 }, /* Freq 4945 */ + { 192, (RF_A_BAND | RF_A_BAND_11J), 0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x04, 0, 0, 1, 0x29, 0, 0x30, 0, 0, 0x3 }, /* Freq 4960 */ + { 196, (RF_A_BAND | RF_A_BAND_11J), 0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x06, 0, 0, 1, 0x29, 0, 0x30, 0, 0, 0x3 }, /* Freq 4980 */ + { 36, (RF_A_BAND | RF_A_BAND_LB), 0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x02, 0, 0, 1, 0x2B, 0, 0x30, 0, 0, 0x3 }, /* Freq 5180 */ + { 37, (RF_A_BAND | RF_A_BAND_LB), 0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x05, 0, 0, 1, 0x2B, 0, 0x30, 0, 0, 0x3 }, /* Freq 5185 */ + { 38, (RF_A_BAND | RF_A_BAND_LB), 0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x03, 0, 0, 1, 0x2B, 0, 0x30, 0, 0, 0x3 }, /* Freq 5190 */ + { 39, (RF_A_BAND | RF_A_BAND_LB), 0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x07, 0, 0, 1, 0x2B, 0, 0x30, 0, 0, 0x3 }, /* Freq 5195 */ + { 40, (RF_A_BAND | RF_A_BAND_LB), 0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x04, 0, 0, 1, 0x2B, 0, 0x30, 0, 0, 0x3 }, /* Freq 5200 */ + { 41, (RF_A_BAND | RF_A_BAND_LB), 0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x09, 0, 0, 1, 0x2B, 0, 0x30, 0, 0, 0x3 }, /* Freq 5205 */ + { 42, (RF_A_BAND | RF_A_BAND_LB), 0x02, 0x3F, 0x30, 0xDD, 0xD2, 0x40, 0x04, 0x40, 0x05, 0, 0, 1, 0x2B, 0, 0x30, 0, 0, 0x3 }, /* Freq 5210 */ + { 43, (RF_A_BAND | RF_A_BAND_LB), 0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x0B, 0, 0, 1, 0x2B, 0, 0x30, 0, 0, 0x3 }, /* Freq 5215 */ + { 44, (RF_A_BAND | RF_A_BAND_LB), 0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x06, 0, 0, 1, 0x2B, 0, 0x30, 0, 0, 0x3 }, /* Freq 5220 */ + { 45, (RF_A_BAND | RF_A_BAND_LB), 0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x0D, 0, 0, 1, 0x2B, 0, 0x30, 0, 0, 0x3 }, /* Freq 5225 */ + { 46, (RF_A_BAND | RF_A_BAND_LB), 0x02, 0x3F, 0x30, 0xDD, 0xD2, 0x40, 0x04, 0x40, 0x07, 0, 0, 1, 0x2B, 0, 0x30, 0, 0, 0x3 }, /* Freq 5230 */ + { 47, (RF_A_BAND | RF_A_BAND_LB), 0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x0F, 0, 0, 1, 0x2B, 0, 0x30, 0, 0, 0x3 }, /* Freq 5235 */ + { 48, (RF_A_BAND | RF_A_BAND_LB), 0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x08, 0, 0, 1, 0x2B, 0, 0x30, 0, 0, 0x3 }, /* Freq 5240 */ + { 49, (RF_A_BAND | RF_A_BAND_LB), 0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x11, 0, 0, 1, 0x2B, 0, 0x30, 0, 0, 0x3 }, /* Freq 5245 */ + { 50, (RF_A_BAND | RF_A_BAND_LB), 0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x09, 0, 0, 1, 0x2B, 0, 0x30, 0, 0, 0x3 }, /* Freq 5250 */ + { 51, (RF_A_BAND | RF_A_BAND_LB), 0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x13, 0, 0, 1, 0x2B, 0, 0x30, 0, 0, 0x3 }, /* Freq 5255 */ + { 52, (RF_A_BAND | RF_A_BAND_LB), 0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x0A, 0, 0, 1, 0x2B, 0, 0x30, 0, 0, 0x3 }, /* Freq 5260 */ + { 53, (RF_A_BAND | RF_A_BAND_LB), 0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x15, 0, 0, 1, 0x2B, 0, 0x30, 0, 0, 0x3 }, /* Freq 5265 */ + { 54, (RF_A_BAND | RF_A_BAND_LB), 0x02, 0x3F, 0x30, 0xDD, 0xD2, 0x40, 0x04, 0x40, 0x0B, 0, 0, 1, 0x2B, 0, 0x30, 0, 0, 0x3 }, /* Freq 5270 */ + { 55, (RF_A_BAND | RF_A_BAND_LB), 0x02, 0x3F, 0x70, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x17, 0, 0, 1, 0x2B, 0, 0x30, 0, 0, 0x3 }, /* Freq 5275 */ + { 56, (RF_A_BAND | RF_A_BAND_LB), 0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x00, 0, 0, 1, 0x2C, 0, 0x30, 0, 0, 0x3 }, /* Freq 5280 */ + { 57, (RF_A_BAND | RF_A_BAND_LB), 0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x01, 0, 0, 1, 0x2C, 0, 0x30, 0, 0, 0x3 }, /* Freq 5285 */ + { 58, (RF_A_BAND | RF_A_BAND_LB), 0x02, 0x3F, 0x30, 0xDD, 0xD2, 0x40, 0x04, 0x40, 0x01, 0, 0, 1, 0x2C, 0, 0x30, 0, 0, 0x3 }, /* Freq 5290 */ + { 59, (RF_A_BAND | RF_A_BAND_LB), 0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x03, 0, 0, 1, 0x2C, 0, 0x30, 0, 0, 0x3 }, /* Freq 5295 */ + { 60, (RF_A_BAND | RF_A_BAND_LB), 0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x02, 0, 0, 1, 0x2C, 0, 0x30, 0, 0, 0x3 }, /* Freq 5300 */ + { 61, (RF_A_BAND | RF_A_BAND_LB), 0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x05, 0, 0, 1, 0x2C, 0, 0x30, 0, 0, 0x3 }, /* Freq 5305 */ + { 62, (RF_A_BAND | RF_A_BAND_LB), 0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x03, 0, 0, 1, 0x2C, 0, 0x30, 0, 0, 0x3 }, /* Freq 5310 */ + { 63, (RF_A_BAND | RF_A_BAND_LB), 0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x07, 0, 0, 1, 0x2C, 0, 0x30, 0, 0, 0x3 }, /* Freq 5315 */ + { 64, (RF_A_BAND | RF_A_BAND_LB), 0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x04, 0, 0, 1, 0x2C, 0, 0x30, 0, 0, 0x3 }, /* Freq 5320 */ + { 100, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x0A, 0, 0, 1, 0x2D, 0, 0x30, 0, 0, 0x3 }, /* Freq 5500 */ + { 101, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x15, 0, 0, 1, 0x2D, 0, 0x30, 0, 0, 0x3 }, /* Freq 5505 */ + { 102, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x30, 0xDD, 0xD2, 0x40, 0x04, 0x40, 0x0B, 0, 0, 1, 0x2D, 0, 0x30, 0, 0, 0x3 }, /* Freq 5510 */ + { 103, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x70, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x17, 0, 0, 1, 0x2D, 0, 0x30, 0, 0, 0x3 }, /* Freq 5515 */ + { 104, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x00, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3 }, /* Freq 5520 */ + { 105, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x01, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3 }, /* Freq 5525 */ + { 106, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x30, 0xDD, 0xD2, 0x40, 0x04, 0x40, 0x01, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3 }, /* Freq 5530 */ + { 107, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x03, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3 }, /* Freq 5535 */ + { 108, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x02, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3 }, /* Freq 5540 */ + { 109, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x05, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3 }, /* Freq 5545 */ + { 110, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x03, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3 }, /* Freq 5550 */ + { 111, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x07, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3 }, /* Freq 5555 */ + { 112, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x04, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3 }, /* Freq 5560 */ + { 113, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x09, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3 }, /* Freq 5565 */ + { 114, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x30, 0xDD, 0xD2, 0x40, 0x04, 0x40, 0x05, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3 }, /* Freq 5570 */ + { 115, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x0B, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3 }, /* Freq 5575 */ + { 116, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x06, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3 }, /* Freq 5580 */ + { 117, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x0D, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3 }, /* Freq 5585 */ + { 118, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x30, 0xDD, 0xD2, 0x40, 0x04, 0x40, 0x07, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3 }, /* Freq 5590 */ + { 119, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x0F, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3 }, /* Freq 5595 */ + { 120, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x08, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3 }, /* Freq 5600 */ + { 121, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x11, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3 }, /* Freq 5605 */ + { 122, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x09, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3 }, /* Freq 5610 */ + { 123, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x13, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3 }, /* Freq 5615 */ + { 124, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x0A, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3 }, /* Freq 5620 */ + { 125, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x15, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3 }, /* Freq 5625 */ + { 126, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x30, 0xDD, 0xD2, 0x40, 0x04, 0x40, 0x0B, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3 }, /* Freq 5630 */ + { 127, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x70, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x17, 0, 0, 1, 0x2E, 0, 0x30, 0, 0, 0x3 }, /* Freq 5635 */ + { 128, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x00, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3 }, /* Freq 5640 */ + { 129, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x01, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3 }, /* Freq 5645 */ + { 130, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x30, 0xDD, 0xD2, 0x40, 0x04, 0x40, 0x01, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3 }, /* Freq 5650 */ + { 131, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x03, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3 }, /* Freq 5655 */ + { 132, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x02, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3 }, /* Freq 5660 */ + { 133, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x05, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3 }, /* Freq 5665 */ + { 134, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x03, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3 }, /* Freq 5670 */ + { 135, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x07, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3 }, /* Freq 5675 */ + { 136, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x04, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3 }, /* Freq 5680 */ + { 137, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x09, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3 }, /* Freq 5685 */ + { 138, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x30, 0xDD, 0xD2, 0x40, 0x04, 0x40, 0x05, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3 }, /* Freq 5690 */ + { 139, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x0B, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3 }, /* Freq 5695 */ + { 140, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x06, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3 }, /* Freq 5700 */ + { 141, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x0D, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3 }, /* Freq 5705 */ + { 142, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x30, 0xDD, 0xD2, 0x40, 0x04, 0x40, 0x07, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3 }, /* Freq 5710 */ + { 143, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x0F, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3 }, /* Freq 5715 */ + { 144, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x08, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3 }, /* Freq 5720 */ + { 145, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x11, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3 }, /* Freq 5725 */ + { 146, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x09, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3 }, /* Freq 5730 */ + { 147, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x13, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3 }, /* Freq 5735 */ + { 148, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x0A, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3 }, /* Freq 5740 */ + { 149, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x15, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3 }, /* Freq 5745 */ + { 150, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x30, 0xDD, 0xD2, 0x40, 0x04, 0x40, 0x0B, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3 }, /* Freq 5750 */ + { 151, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x70, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x17, 0, 0, 1, 0x2F, 0, 0x30, 0, 0, 0x3 }, /* Freq 5755 */ + { 152, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x00, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3 }, /* Freq 5760 */ + { 153, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x01, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3 }, /* Freq 5765 */ + { 154, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x30, 0xDD, 0xD2, 0x40, 0x04, 0x40, 0x01, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3 }, /* Freq 5770 */ + { 155, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x03, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3 }, /* Freq 5775 */ + { 156, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x02, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3 }, /* Freq 5780 */ + { 157, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x05, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3 }, /* Freq 5785 */ + { 158, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x03, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3 }, /* Freq 5790 */ + { 159, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x07, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3 }, /* Freq 5795 */ + { 160, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x04, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3 }, /* Freq 5800 */ + { 161, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x09, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3 }, /* Freq 5805 */ + { 162, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x30, 0xDD, 0xD2, 0x40, 0x04, 0x40, 0x05, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3 }, /* Freq 5810 */ + { 163, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x0B, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3 }, /* Freq 5815 */ + { 164, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x06, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3 }, /* Freq 5820 */ + { 165, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x0D, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3 }, /* Freq 5825 */ + { 166, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x30, 0xDD, 0xD2, 0x40, 0x04, 0x40, 0x07, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3 }, /* Freq 5830 */ + { 167, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x0F, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3 }, /* Freq 5835 */ + { 168, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x08, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3 }, /* Freq 5840 */ + { 169, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x11, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3 }, /* Freq 5845 */ + { 170, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x09, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3 }, /* Freq 5850 */ + { 171, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x13, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3 }, /* Freq 5855 */ + { 172, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x30, 0x97, 0xD2, 0x40, 0x04, 0x40, 0x0A, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3 }, /* Freq 5860 */ + { 173, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x68, 0xDD, 0xD2, 0x40, 0x10, 0x40, 0x15, 0, 0, 1, 0x30, 0, 0x30, 0, 0, 0x3 }, /* Freq 5865 */ +}; + +static const struct mt76x0_freq_item mt76x0_sdm_frequency_plan[] = { + { 1, RF_G_BAND, 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x28, 0, 0x0, 0x8, 0x0CCCC, 0x3 }, /* Freq 2412 */ + { 2, RF_G_BAND, 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x28, 0, 0x0, 0x8, 0x12222, 0x3 }, /* Freq 2417 */ + { 3, RF_G_BAND, 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x28, 0, 0x0, 0x8, 0x17777, 0x3 }, /* Freq 2422 */ + { 4, RF_G_BAND, 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x28, 0, 0x0, 0x8, 0x1CCCC, 0x3 }, /* Freq 2427 */ + { 5, RF_G_BAND, 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x28, 0, 0x0, 0x8, 0x22222, 0x3 }, /* Freq 2432 */ + { 6, RF_G_BAND, 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x28, 0, 0x0, 0x8, 0x27777, 0x3 }, /* Freq 2437 */ + { 7, RF_G_BAND, 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x28, 0, 0x0, 0x8, 0x2CCCC, 0x3 }, /* Freq 2442 */ + { 8, RF_G_BAND, 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x28, 0, 0x0, 0x8, 0x32222, 0x3 }, /* Freq 2447 */ + { 9, RF_G_BAND, 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x28, 0, 0x0, 0x8, 0x37777, 0x3 }, /* Freq 2452 */ + { 10, RF_G_BAND, 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x28, 0, 0x0, 0x8, 0x3CCCC, 0x3 }, /* Freq 2457 */ + { 11, RF_G_BAND, 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x29, 0, 0x0, 0x8, 0x02222, 0x3 }, /* Freq 2462 */ + { 12, RF_G_BAND, 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x29, 0, 0x0, 0x8, 0x07777, 0x3 }, /* Freq 2467 */ + { 13, RF_G_BAND, 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x29, 0, 0x0, 0x8, 0x0CCCC, 0x3 }, /* Freq 2472 */ + { 14, RF_G_BAND, 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x29, 0, 0x0, 0x8, 0x19999, 0x3 }, /* Freq 2484 */ + { 183, (RF_A_BAND | RF_A_BAND_11J), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x28, 0, 0x0, 0x8, 0x3D555, 0x3 }, /* Freq 4915 */ + { 184, (RF_A_BAND | RF_A_BAND_11J), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x29, 0, 0x0, 0x8, 0x00000, 0x3 }, /* Freq 4920 */ + { 185, (RF_A_BAND | RF_A_BAND_11J), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x29, 0, 0x0, 0x8, 0x02AAA, 0x3 }, /* Freq 4925 */ + { 187, (RF_A_BAND | RF_A_BAND_11J), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x29, 0, 0x0, 0x8, 0x08000, 0x3 }, /* Freq 4935 */ + { 188, (RF_A_BAND | RF_A_BAND_11J), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x29, 0, 0x0, 0x8, 0x0AAAA, 0x3 }, /* Freq 4940 */ + { 189, (RF_A_BAND | RF_A_BAND_11J), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x29, 0, 0x0, 0x8, 0x0D555, 0x3 }, /* Freq 4945 */ + { 192, (RF_A_BAND | RF_A_BAND_11J), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x29, 0, 0x0, 0x8, 0x15555, 0x3 }, /* Freq 4960 */ + { 196, (RF_A_BAND | RF_A_BAND_11J), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x29, 0, 0x0, 0x8, 0x20000, 0x3 }, /* Freq 4980 */ + { 36, (RF_A_BAND | RF_A_BAND_LB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2B, 0, 0x0, 0x8, 0x0AAAA, 0x3 }, /* Freq 5180 */ + { 37, (RF_A_BAND | RF_A_BAND_LB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2B, 0, 0x0, 0x8, 0x0D555, 0x3 }, /* Freq 5185 */ + { 38, (RF_A_BAND | RF_A_BAND_LB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2B, 0, 0x0, 0x8, 0x10000, 0x3 }, /* Freq 5190 */ + { 39, (RF_A_BAND | RF_A_BAND_LB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2B, 0, 0x0, 0x8, 0x12AAA, 0x3 }, /* Freq 5195 */ + { 40, (RF_A_BAND | RF_A_BAND_LB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2B, 0, 0x0, 0x8, 0x15555, 0x3 }, /* Freq 5200 */ + { 41, (RF_A_BAND | RF_A_BAND_LB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2B, 0, 0x0, 0x8, 0x18000, 0x3 }, /* Freq 5205 */ + { 42, (RF_A_BAND | RF_A_BAND_LB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2B, 0, 0x0, 0x8, 0x1AAAA, 0x3 }, /* Freq 5210 */ + { 43, (RF_A_BAND | RF_A_BAND_LB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2B, 0, 0x0, 0x8, 0x1D555, 0x3 }, /* Freq 5215 */ + { 44, (RF_A_BAND | RF_A_BAND_LB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2B, 0, 0x0, 0x8, 0x20000, 0x3 }, /* Freq 5220 */ + { 45, (RF_A_BAND | RF_A_BAND_LB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2B, 0, 0x0, 0x8, 0x22AAA, 0x3 }, /* Freq 5225 */ + { 46, (RF_A_BAND | RF_A_BAND_LB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2B, 0, 0x0, 0x8, 0x25555, 0x3 }, /* Freq 5230 */ + { 47, (RF_A_BAND | RF_A_BAND_LB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2B, 0, 0x0, 0x8, 0x28000, 0x3 }, /* Freq 5235 */ + { 48, (RF_A_BAND | RF_A_BAND_LB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2B, 0, 0x0, 0x8, 0x2AAAA, 0x3 }, /* Freq 5240 */ + { 49, (RF_A_BAND | RF_A_BAND_LB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2B, 0, 0x0, 0x8, 0x2D555, 0x3 }, /* Freq 5245 */ + { 50, (RF_A_BAND | RF_A_BAND_LB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2B, 0, 0x0, 0x8, 0x30000, 0x3 }, /* Freq 5250 */ + { 51, (RF_A_BAND | RF_A_BAND_LB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2B, 0, 0x0, 0x8, 0x32AAA, 0x3 }, /* Freq 5255 */ + { 52, (RF_A_BAND | RF_A_BAND_LB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2B, 0, 0x0, 0x8, 0x35555, 0x3 }, /* Freq 5260 */ + { 53, (RF_A_BAND | RF_A_BAND_LB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2B, 0, 0x0, 0x8, 0x38000, 0x3 }, /* Freq 5265 */ + { 54, (RF_A_BAND | RF_A_BAND_LB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2B, 0, 0x0, 0x8, 0x3AAAA, 0x3 }, /* Freq 5270 */ + { 55, (RF_A_BAND | RF_A_BAND_LB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2B, 0, 0x0, 0x8, 0x3D555, 0x3 }, /* Freq 5275 */ + { 56, (RF_A_BAND | RF_A_BAND_LB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2C, 0, 0x0, 0x8, 0x00000, 0x3 }, /* Freq 5280 */ + { 57, (RF_A_BAND | RF_A_BAND_LB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2C, 0, 0x0, 0x8, 0x02AAA, 0x3 }, /* Freq 5285 */ + { 58, (RF_A_BAND | RF_A_BAND_LB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2C, 0, 0x0, 0x8, 0x05555, 0x3 }, /* Freq 5290 */ + { 59, (RF_A_BAND | RF_A_BAND_LB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2C, 0, 0x0, 0x8, 0x08000, 0x3 }, /* Freq 5295 */ + { 60, (RF_A_BAND | RF_A_BAND_LB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2C, 0, 0x0, 0x8, 0x0AAAA, 0x3 }, /* Freq 5300 */ + { 61, (RF_A_BAND | RF_A_BAND_LB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2C, 0, 0x0, 0x8, 0x0D555, 0x3 }, /* Freq 5305 */ + { 62, (RF_A_BAND | RF_A_BAND_LB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2C, 0, 0x0, 0x8, 0x10000, 0x3 }, /* Freq 5310 */ + { 63, (RF_A_BAND | RF_A_BAND_LB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2C, 0, 0x0, 0x8, 0x12AAA, 0x3 }, /* Freq 5315 */ + { 64, (RF_A_BAND | RF_A_BAND_LB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2C, 0, 0x0, 0x8, 0x15555, 0x3 }, /* Freq 5320 */ + { 100, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2D, 0, 0x0, 0x8, 0x35555, 0x3 }, /* Freq 5500 */ + { 101, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2D, 0, 0x0, 0x8, 0x38000, 0x3 }, /* Freq 5505 */ + { 102, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2D, 0, 0x0, 0x8, 0x3AAAA, 0x3 }, /* Freq 5510 */ + { 103, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2D, 0, 0x0, 0x8, 0x3D555, 0x3 }, /* Freq 5515 */ + { 104, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2E, 0, 0x0, 0x8, 0x00000, 0x3 }, /* Freq 5520 */ + { 105, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2E, 0, 0x0, 0x8, 0x02AAA, 0x3 }, /* Freq 5525 */ + { 106, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2E, 0, 0x0, 0x8, 0x05555, 0x3 }, /* Freq 5530 */ + { 107, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2E, 0, 0x0, 0x8, 0x08000, 0x3 }, /* Freq 5535 */ + { 108, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2E, 0, 0x0, 0x8, 0x0AAAA, 0x3 }, /* Freq 5540 */ + { 109, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2E, 0, 0x0, 0x8, 0x0D555, 0x3 }, /* Freq 5545 */ + { 110, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2E, 0, 0x0, 0x8, 0x10000, 0x3 }, /* Freq 5550 */ + { 111, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2E, 0, 0x0, 0x8, 0x12AAA, 0x3 }, /* Freq 5555 */ + { 112, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2E, 0, 0x0, 0x8, 0x15555, 0x3 }, /* Freq 5560 */ + { 113, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2E, 0, 0x0, 0x8, 0x18000, 0x3 }, /* Freq 5565 */ + { 114, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2E, 0, 0x0, 0x8, 0x1AAAA, 0x3 }, /* Freq 5570 */ + { 115, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2E, 0, 0x0, 0x8, 0x1D555, 0x3 }, /* Freq 5575 */ + { 116, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2E, 0, 0x0, 0x8, 0x20000, 0x3 }, /* Freq 5580 */ + { 117, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2E, 0, 0x0, 0x8, 0x22AAA, 0x3 }, /* Freq 5585 */ + { 118, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2E, 0, 0x0, 0x8, 0x25555, 0x3 }, /* Freq 5590 */ + { 119, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2E, 0, 0x0, 0x8, 0x28000, 0x3 }, /* Freq 5595 */ + { 120, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2E, 0, 0x0, 0x8, 0x2AAAA, 0x3 }, /* Freq 5600 */ + { 121, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2E, 0, 0x0, 0x8, 0x2D555, 0x3 }, /* Freq 5605 */ + { 122, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2E, 0, 0x0, 0x8, 0x30000, 0x3 }, /* Freq 5610 */ + { 123, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2E, 0, 0x0, 0x8, 0x32AAA, 0x3 }, /* Freq 5615 */ + { 124, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2E, 0, 0x0, 0x8, 0x35555, 0x3 }, /* Freq 5620 */ + { 125, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2E, 0, 0x0, 0x8, 0x38000, 0x3 }, /* Freq 5625 */ + { 126, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2E, 0, 0x0, 0x8, 0x3AAAA, 0x3 }, /* Freq 5630 */ + { 127, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2E, 0, 0x0, 0x8, 0x3D555, 0x3 }, /* Freq 5635 */ + { 128, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2F, 0, 0x0, 0x8, 0x00000, 0x3 }, /* Freq 5640 */ + { 129, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2F, 0, 0x0, 0x8, 0x02AAA, 0x3 }, /* Freq 5645 */ + { 130, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2F, 0, 0x0, 0x8, 0x05555, 0x3 }, /* Freq 5650 */ + { 131, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2F, 0, 0x0, 0x8, 0x08000, 0x3 }, /* Freq 5655 */ + { 132, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2F, 0, 0x0, 0x8, 0x0AAAA, 0x3 }, /* Freq 5660 */ + { 133, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2F, 0, 0x0, 0x8, 0x0D555, 0x3 }, /* Freq 5665 */ + { 134, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2F, 0, 0x0, 0x8, 0x10000, 0x3 }, /* Freq 5670 */ + { 135, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2F, 0, 0x0, 0x8, 0x12AAA, 0x3 }, /* Freq 5675 */ + { 136, (RF_A_BAND | RF_A_BAND_MB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2F, 0, 0x0, 0x8, 0x15555, 0x3 }, /* Freq 5680 */ + { 137, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2F, 0, 0x0, 0x8, 0x18000, 0x3 }, /* Freq 5685 */ + { 138, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2F, 0, 0x0, 0x8, 0x1AAAA, 0x3 }, /* Freq 5690 */ + { 139, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2F, 0, 0x0, 0x8, 0x1D555, 0x3 }, /* Freq 5695 */ + { 140, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2F, 0, 0x0, 0x8, 0x20000, 0x3 }, /* Freq 5700 */ + { 141, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2F, 0, 0x0, 0x8, 0x22AAA, 0x3 }, /* Freq 5705 */ + { 142, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2F, 0, 0x0, 0x8, 0x25555, 0x3 }, /* Freq 5710 */ + { 143, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2F, 0, 0x0, 0x8, 0x28000, 0x3 }, /* Freq 5715 */ + { 144, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2F, 0, 0x0, 0x8, 0x2AAAA, 0x3 }, /* Freq 5720 */ + { 145, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2F, 0, 0x0, 0x8, 0x2D555, 0x3 }, /* Freq 5725 */ + { 146, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2F, 0, 0x0, 0x8, 0x30000, 0x3 }, /* Freq 5730 */ + { 147, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2F, 0, 0x0, 0x8, 0x32AAA, 0x3 }, /* Freq 5735 */ + { 148, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2F, 0, 0x0, 0x8, 0x35555, 0x3 }, /* Freq 5740 */ + { 149, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2F, 0, 0x0, 0x8, 0x38000, 0x3 }, /* Freq 5745 */ + { 150, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2F, 0, 0x0, 0x8, 0x3AAAA, 0x3 }, /* Freq 5750 */ + { 151, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x2F, 0, 0x0, 0x8, 0x3D555, 0x3 }, /* Freq 5755 */ + { 152, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x30, 0, 0x0, 0x8, 0x00000, 0x3 }, /* Freq 5760 */ + { 153, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x30, 0, 0x0, 0x8, 0x02AAA, 0x3 }, /* Freq 5765 */ + { 154, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x30, 0, 0x0, 0x8, 0x05555, 0x3 }, /* Freq 5770 */ + { 155, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x30, 0, 0x0, 0x8, 0x08000, 0x3 }, /* Freq 5775 */ + { 156, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x30, 0, 0x0, 0x8, 0x0AAAA, 0x3 }, /* Freq 5780 */ + { 157, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x30, 0, 0x0, 0x8, 0x0D555, 0x3 }, /* Freq 5785 */ + { 158, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x30, 0, 0x0, 0x8, 0x10000, 0x3 }, /* Freq 5790 */ + { 159, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x30, 0, 0x0, 0x8, 0x12AAA, 0x3 }, /* Freq 5795 */ + { 160, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x30, 0, 0x0, 0x8, 0x15555, 0x3 }, /* Freq 5800 */ + { 161, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x30, 0, 0x0, 0x8, 0x18000, 0x3 }, /* Freq 5805 */ + { 162, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x30, 0, 0x0, 0x8, 0x1AAAA, 0x3 }, /* Freq 5810 */ + { 163, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x30, 0, 0x0, 0x8, 0x1D555, 0x3 }, /* Freq 5815 */ + { 164, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x30, 0, 0x0, 0x8, 0x20000, 0x3 }, /* Freq 5820 */ + { 165, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x30, 0, 0x0, 0x8, 0x22AAA, 0x3 }, /* Freq 5825 */ + { 166, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x30, 0, 0x0, 0x8, 0x25555, 0x3 }, /* Freq 5830 */ + { 167, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x30, 0, 0x0, 0x8, 0x28000, 0x3 }, /* Freq 5835 */ + { 168, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x30, 0, 0x0, 0x8, 0x2AAAA, 0x3 }, /* Freq 5840 */ + { 169, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x30, 0, 0x0, 0x8, 0x2D555, 0x3 }, /* Freq 5845 */ + { 170, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x30, 0, 0x0, 0x8, 0x30000, 0x3 }, /* Freq 5850 */ + { 171, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x30, 0, 0x0, 0x8, 0x32AAA, 0x3 }, /* Freq 5855 */ + { 172, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x30, 0, 0x0, 0x8, 0x35555, 0x3 }, /* Freq 5860 */ + { 173, (RF_A_BAND | RF_A_BAND_HB), 0x02, 0x3F, 0x7F, 0xDD, 0xC3, 0x40, 0x0, 0x80, 0x0, 0, 0, 0, 0x30, 0, 0x0, 0x8, 0x38000, 0x3 }, /* Freq 5865 */ +}; + +static const u8 mt76x0_sdm_channel[] = { + 183, 185, 43, 45, + 54, 55, 57, 58, + 102, 103, 105, 106, + 115, 117, 126, 127, + 129, 130, 139, 141, + 150, 151, 153, 154, + 163, 165 +}; + +static const struct mt76x0_rf_switch_item mt76x0_rf_ext_pa_tab[] = { + { MT_RF(6, 45), RF_A_BAND_LB, 0x63 }, + { MT_RF(6, 45), RF_A_BAND_MB, 0x43 }, + { MT_RF(6, 45), RF_A_BAND_HB, 0x33 }, + { MT_RF(6, 45), RF_A_BAND_11J, 0x73 }, + { MT_RF(6, 50), RF_A_BAND_LB, 0x02 }, + { MT_RF(6, 50), RF_A_BAND_MB, 0x02 }, + { MT_RF(6, 50), RF_A_BAND_HB, 0x02 }, + { MT_RF(6, 50), RF_A_BAND_11J, 0x02 }, + { MT_RF(6, 51), RF_A_BAND_LB, 0x02 }, + { MT_RF(6, 51), RF_A_BAND_MB, 0x02 }, + { MT_RF(6, 51), RF_A_BAND_HB, 0x02 }, + { MT_RF(6, 51), RF_A_BAND_11J, 0x02 }, + { MT_RF(6, 52), RF_A_BAND_LB, 0x08 }, + { MT_RF(6, 52), RF_A_BAND_MB, 0x08 }, + { MT_RF(6, 52), RF_A_BAND_HB, 0x08 }, + { MT_RF(6, 52), RF_A_BAND_11J, 0x08 }, + { MT_RF(6, 53), RF_A_BAND_LB, 0x08 }, + { MT_RF(6, 53), RF_A_BAND_MB, 0x08 }, + { MT_RF(6, 53), RF_A_BAND_HB, 0x08 }, + { MT_RF(6, 53), RF_A_BAND_11J, 0x08 }, + { MT_RF(6, 54), RF_A_BAND_LB, 0x0A }, + { MT_RF(6, 54), RF_A_BAND_MB, 0x0A }, + { MT_RF(6, 54), RF_A_BAND_HB, 0x0A }, + { MT_RF(6, 54), RF_A_BAND_11J, 0x0A }, + { MT_RF(6, 55), RF_A_BAND_LB, 0x0A }, + { MT_RF(6, 55), RF_A_BAND_MB, 0x0A }, + { MT_RF(6, 55), RF_A_BAND_HB, 0x0A }, + { MT_RF(6, 55), RF_A_BAND_11J, 0x0A }, + { MT_RF(6, 56), RF_A_BAND_LB, 0x05 }, + { MT_RF(6, 56), RF_A_BAND_MB, 0x05 }, + { MT_RF(6, 56), RF_A_BAND_HB, 0x05 }, + { MT_RF(6, 56), RF_A_BAND_11J, 0x05 }, + { MT_RF(6, 57), RF_A_BAND_LB, 0x05 }, + { MT_RF(6, 57), RF_A_BAND_MB, 0x05 }, + { MT_RF(6, 57), RF_A_BAND_HB, 0x05 }, + { MT_RF(6, 57), RF_A_BAND_11J, 0x05 }, + { MT_RF(6, 58), RF_A_BAND_LB, 0x05 }, + { MT_RF(6, 58), RF_A_BAND_MB, 0x03 }, + { MT_RF(6, 58), RF_A_BAND_HB, 0x02 }, + { MT_RF(6, 58), RF_A_BAND_11J, 0x07 }, + { MT_RF(6, 59), RF_A_BAND_LB, 0x05 }, + { MT_RF(6, 59), RF_A_BAND_MB, 0x03 }, + { MT_RF(6, 59), RF_A_BAND_HB, 0x02 }, + { MT_RF(6, 59), RF_A_BAND_11J, 0x07 }, +}; + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/main.c b/drivers/net/wireless/mediatek/mt76/mt76x0/main.c new file mode 100644 index 000000000..a5c40d350 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/main.c @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2014 Felix Fietkau + * Copyright (C) 2015 Jakub Kicinski + * Copyright (C) 2018 Stanislaw Gruszka + */ + +#include +#include "mt76x0.h" + +int mt76x0_set_channel(struct mt76_phy *mphy) +{ + struct mt76x02_dev *dev = container_of(mphy->dev, struct mt76x02_dev, mt76); + + mt76x02_pre_tbtt_enable(dev, false); + if (mt76_is_mmio(&dev->mt76)) + tasklet_disable(&dev->dfs_pd.dfs_tasklet); + + mt76x0_phy_set_channel(dev, &mphy->chandef); + + mt76x02_mac_cc_reset(dev); + mt76x02_edcca_init(dev); + + if (mt76_is_mmio(&dev->mt76)) { + mt76x02_dfs_init_params(dev); + tasklet_enable(&dev->dfs_pd.dfs_tasklet); + } + mt76x02_pre_tbtt_enable(dev, true); + + return 0; +} +EXPORT_SYMBOL_GPL(mt76x0_set_channel); + +int mt76x0_set_sar_specs(struct ieee80211_hw *hw, + const struct cfg80211_sar_specs *sar) +{ + int err = -EINVAL, power = hw->conf.power_level * 2; + struct mt76x02_dev *dev = hw->priv; + struct mt76_phy *mphy = &dev->mphy; + + mutex_lock(&dev->mt76.mutex); + if (!cfg80211_chandef_valid(&mphy->chandef)) + goto out; + + err = mt76_init_sar_power(hw, sar); + if (err) + goto out; + + dev->txpower_conf = mt76_get_sar_power(mphy, mphy->chandef.chan, + power); + if (test_bit(MT76_STATE_RUNNING, &mphy->state)) + mt76x0_phy_set_txpower(dev); +out: + mutex_unlock(&dev->mt76.mutex); + + return err; +} +EXPORT_SYMBOL_GPL(mt76x0_set_sar_specs); + +int mt76x0_config(struct ieee80211_hw *hw, int radio_idx, u32 changed) +{ + struct mt76x02_dev *dev = hw->priv; + + if (changed & IEEE80211_CONF_CHANGE_CHANNEL) + mt76_update_channel(&dev->mphy); + + mutex_lock(&dev->mt76.mutex); + + if (changed & IEEE80211_CONF_CHANGE_POWER) { + struct mt76_phy *mphy = &dev->mphy; + + dev->txpower_conf = hw->conf.power_level * 2; + dev->txpower_conf = mt76_get_sar_power(mphy, + mphy->chandef.chan, + dev->txpower_conf); + if (test_bit(MT76_STATE_RUNNING, &mphy->state)) + mt76x0_phy_set_txpower(dev); + } + + if (changed & IEEE80211_CONF_CHANGE_MONITOR) { + if (!(hw->conf.flags & IEEE80211_CONF_MONITOR)) + dev->mt76.rxfilter |= MT_RX_FILTR_CFG_PROMISC; + else + dev->mt76.rxfilter &= ~MT_RX_FILTR_CFG_PROMISC; + + mt76_wr(dev, MT_RX_FILTR_CFG, dev->mt76.rxfilter); + } + + mutex_unlock(&dev->mt76.mutex); + + return 0; +} +EXPORT_SYMBOL_GPL(mt76x0_config); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/mcu.h b/drivers/net/wireless/mediatek/mt76/mt76x0/mcu.h new file mode 100644 index 000000000..0ef29f15f --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/mcu.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2014 Felix Fietkau + * Copyright (C) 2015 Jakub Kicinski + */ + +#ifndef __MT76X0U_MCU_H +#define __MT76X0U_MCU_H + +#include "../mt76x02_mcu.h" + +struct mt76x02_dev; + +#define MT_MCU_IVB_SIZE 0x40 +#define MT_MCU_DLM_OFFSET 0x80000 + +/* We use same space for BBP as for MAC regs + * #define MT_MCU_MEMMAP_BBP 0x40000000 + */ +#define MT_MCU_MEMMAP_RF 0x80000000 + +enum mcu_calibrate { + MCU_CAL_R = 1, + MCU_CAL_RXDCOC, + MCU_CAL_LC, + MCU_CAL_LOFT, + MCU_CAL_TXIQ, + MCU_CAL_BW, + MCU_CAL_DPD, + MCU_CAL_RXIQ, + MCU_CAL_TXDCOC, + MCU_CAL_RX_GROUP_DELAY, + MCU_CAL_TX_GROUP_DELAY, + MCU_CAL_VCO, + MCU_CAL_NO_SIGNAL = 0xfe, + MCU_CAL_FULL = 0xff, +}; + +int mt76x0e_mcu_init(struct mt76x02_dev *dev); +int mt76x0u_mcu_init(struct mt76x02_dev *dev); +static inline int mt76x0_firmware_running(struct mt76x02_dev *dev) +{ + return mt76_rr(dev, MT_MCU_COM_REG0) == 1; +} + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/mt76x0.h b/drivers/net/wireless/mediatek/mt76/mt76x0/mt76x0.h new file mode 100644 index 000000000..e5bc14d4c --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/mt76x0.h @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2014 Felix Fietkau + * Copyright (C) 2015 Jakub Kicinski + * Copyright (C) 2018 Stanislaw Gruszka + */ + +#ifndef MT76X0U_H +#define MT76X0U_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../mt76x02.h" +#include "eeprom.h" + +#define MT7610E_FIRMWARE "mediatek/mt7610e.bin" +#define MT7650E_FIRMWARE "mediatek/mt7650e.bin" + +#define MT7610U_FIRMWARE "mediatek/mt7610u.bin" + +#define MT_USB_AGGR_SIZE_LIMIT 21 /* * 1024B */ +#define MT_USB_AGGR_TIMEOUT 0x80 /* * 33ns */ + +static inline bool is_mt7610e(struct mt76x02_dev *dev) +{ + if (!mt76_is_mmio(&dev->mt76)) + return false; + + return mt76_chip(&dev->mt76) == 0x7610; +} + +static inline bool is_mt7630(struct mt76x02_dev *dev) +{ + return mt76_chip(&dev->mt76) == 0x7630; +} + +/* Init */ +int mt76x0_init_hardware(struct mt76x02_dev *dev); +int mt76x0_register_device(struct mt76x02_dev *dev); +void mt76x0_chip_onoff(struct mt76x02_dev *dev, bool enable, bool reset); + +void mt76x0_mac_stop(struct mt76x02_dev *dev); + +int mt76x0_config(struct ieee80211_hw *hw, int radio_idx, u32 changed); +int mt76x0_set_channel(struct mt76_phy *mphy); +int mt76x0_set_sar_specs(struct ieee80211_hw *hw, + const struct cfg80211_sar_specs *sar); + +/* PHY */ +void mt76x0_phy_init(struct mt76x02_dev *dev); +int mt76x0_phy_wait_bbp_ready(struct mt76x02_dev *dev); +void mt76x0_phy_set_channel(struct mt76x02_dev *dev, + struct cfg80211_chan_def *chandef); +void mt76x0_phy_set_txpower(struct mt76x02_dev *dev); +void mt76x0_phy_calibrate(struct mt76x02_dev *dev, bool power_on); +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/pci.c b/drivers/net/wireless/mediatek/mt76/mt76x0/pci.c new file mode 100644 index 000000000..f8d206a07 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/pci.c @@ -0,0 +1,327 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (C) 2016 Felix Fietkau + */ + +#include +#include +#include + +#include "mt76x0.h" +#include "mcu.h" + +static int mt76x0e_start(struct ieee80211_hw *hw) +{ + struct mt76x02_dev *dev = hw->priv; + + mt76x02_mac_start(dev); + mt76x0_phy_calibrate(dev, true); + ieee80211_queue_delayed_work(dev->mt76.hw, &dev->mphy.mac_work, + MT_MAC_WORK_INTERVAL); + ieee80211_queue_delayed_work(dev->mt76.hw, &dev->cal_work, + MT_CALIBRATE_INTERVAL); + set_bit(MT76_STATE_RUNNING, &dev->mphy.state); + + return 0; +} + +static void mt76x0e_stop_hw(struct mt76x02_dev *dev) +{ + cancel_delayed_work_sync(&dev->cal_work); + cancel_delayed_work_sync(&dev->mphy.mac_work); + clear_bit(MT76_RESTART, &dev->mphy.state); + + if (!mt76_poll(dev, MT_WPDMA_GLO_CFG, MT_WPDMA_GLO_CFG_TX_DMA_BUSY, + 0, 1000)) + dev_warn(dev->mt76.dev, "TX DMA did not stop\n"); + mt76_clear(dev, MT_WPDMA_GLO_CFG, MT_WPDMA_GLO_CFG_TX_DMA_EN); + + mt76x0_mac_stop(dev); + + if (!mt76_poll(dev, MT_WPDMA_GLO_CFG, MT_WPDMA_GLO_CFG_RX_DMA_BUSY, + 0, 1000)) + dev_warn(dev->mt76.dev, "TX DMA did not stop\n"); + mt76_clear(dev, MT_WPDMA_GLO_CFG, MT_WPDMA_GLO_CFG_RX_DMA_EN); +} + +static void mt76x0e_stop(struct ieee80211_hw *hw, bool suspend) +{ + struct mt76x02_dev *dev = hw->priv; + + clear_bit(MT76_STATE_RUNNING, &dev->mphy.state); + mt76x0e_stop_hw(dev); +} + +static void +mt76x0e_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 queues, bool drop) +{ +} + +static const struct ieee80211_ops mt76x0e_ops = { + .add_chanctx = ieee80211_emulate_add_chanctx, + .remove_chanctx = ieee80211_emulate_remove_chanctx, + .change_chanctx = ieee80211_emulate_change_chanctx, + .switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx, + .tx = mt76x02_tx, + .start = mt76x0e_start, + .stop = mt76x0e_stop, + .add_interface = mt76x02_add_interface, + .remove_interface = mt76x02_remove_interface, + .config = mt76x0_config, + .configure_filter = mt76x02_configure_filter, + .bss_info_changed = mt76x02_bss_info_changed, + .sta_state = mt76_sta_state, + .sta_pre_rcu_remove = mt76_sta_pre_rcu_remove, + .set_key = mt76x02_set_key, + .conf_tx = mt76x02_conf_tx, + .sw_scan_start = mt76_sw_scan, + .sw_scan_complete = mt76x02_sw_scan_complete, + .ampdu_action = mt76x02_ampdu_action, + .sta_rate_tbl_update = mt76x02_sta_rate_tbl_update, + .wake_tx_queue = mt76_wake_tx_queue, + .get_survey = mt76_get_survey, + .get_txpower = mt76_get_txpower, + .flush = mt76x0e_flush, + .set_tim = mt76_set_tim, + .release_buffered_frames = mt76_release_buffered_frames, + .set_coverage_class = mt76x02_set_coverage_class, + .set_rts_threshold = mt76x02_set_rts_threshold, + .get_antenna = mt76_get_antenna, + .reconfig_complete = mt76x02_reconfig_complete, + .set_sar_specs = mt76x0_set_sar_specs, +}; + +static int mt76x0e_init_hardware(struct mt76x02_dev *dev, bool resume) +{ + int err; + + mt76x0_chip_onoff(dev, true, false); + if (!mt76x02_wait_for_mac(&dev->mt76)) + return -ETIMEDOUT; + + mt76x02_dma_disable(dev); + err = mt76x0e_mcu_init(dev); + if (err < 0) + return err; + + if (!resume) { + err = mt76x02_dma_init(dev); + if (err < 0) + return err; + } + + err = mt76x0_init_hardware(dev); + if (err < 0) + return err; + + mt76x02e_init_beacon_config(dev); + + if (mt76_chip(&dev->mt76) == 0x7610) { + u16 val; + + mt76_clear(dev, MT_COEXCFG0, BIT(0)); + + val = mt76x02_eeprom_get(dev, MT_EE_NIC_CONF_0); + if (!(val & MT_EE_NIC_CONF_0_PA_IO_CURRENT)) + mt76_set(dev, MT_XO_CTRL7, 0xc03); + } + + mt76_clear(dev, 0x110, BIT(9)); + mt76_set(dev, MT_MAX_LEN_CFG, BIT(13)); + + return 0; +} + +static int mt76x0e_register_device(struct mt76x02_dev *dev) +{ + int err; + + err = mt76x0e_init_hardware(dev, false); + if (err < 0) + return err; + + err = mt76x0_register_device(dev); + if (err < 0) + return err; + + set_bit(MT76_STATE_INITIALIZED, &dev->mphy.state); + + return 0; +} + +static int +mt76x0e_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + static const struct mt76_driver_ops drv_ops = { + .txwi_size = sizeof(struct mt76x02_txwi), + .drv_flags = MT_DRV_TX_ALIGNED4_SKBS | + MT_DRV_SW_RX_AIRTIME | + MT_DRV_IGNORE_TXS_FAILED, + .survey_flags = SURVEY_INFO_TIME_TX, + .update_survey = mt76x02_update_channel, + .set_channel = mt76x0_set_channel, + .tx_prepare_skb = mt76x02_tx_prepare_skb, + .tx_complete_skb = mt76x02_tx_complete_skb, + .rx_skb = mt76x02_queue_rx_skb, + .rx_poll_complete = mt76x02_rx_poll_complete, + .sta_ps = mt76x02_sta_ps, + .sta_add = mt76x02_sta_add, + .sta_remove = mt76x02_sta_remove, + }; + struct mt76x02_dev *dev; + struct mt76_dev *mdev; + int ret; + + ret = pcim_enable_device(pdev); + if (ret) + return ret; + + ret = pcim_iomap_regions(pdev, BIT(0), pci_name(pdev)); + if (ret) + return ret; + + pci_set_master(pdev); + + ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) + return ret; + + mt76_pci_disable_aspm(pdev); + + mdev = mt76_alloc_device(&pdev->dev, sizeof(*dev), &mt76x0e_ops, + &drv_ops); + if (!mdev) + return -ENOMEM; + + dev = container_of(mdev, struct mt76x02_dev, mt76); + mutex_init(&dev->phy_mutex); + + mt76_mmio_init(mdev, pcim_iomap_table(pdev)[0]); + + mdev->rev = mt76_rr(dev, MT_ASIC_VERSION); + dev_info(mdev->dev, "ASIC revision: %08x\n", mdev->rev); + + mt76_wr(dev, MT_INT_MASK_CSR, 0); + + ret = devm_request_irq(mdev->dev, pdev->irq, mt76x02_irq_handler, + IRQF_SHARED, KBUILD_MODNAME, dev); + if (ret) + goto error; + + ret = mt76x0e_register_device(dev); + if (ret < 0) + goto error; + + return 0; + +error: + mt76_free_device(&dev->mt76); + + return ret; +} + +static void mt76x0e_cleanup(struct mt76x02_dev *dev) +{ + clear_bit(MT76_STATE_INITIALIZED, &dev->mphy.state); + tasklet_disable(&dev->mt76.pre_tbtt_tasklet); + mt76x0_chip_onoff(dev, false, false); + mt76x0e_stop_hw(dev); + mt76_dma_cleanup(&dev->mt76); + mt76x02_mcu_cleanup(dev); +} + +static void +mt76x0e_remove(struct pci_dev *pdev) +{ + struct mt76_dev *mdev = pci_get_drvdata(pdev); + struct mt76x02_dev *dev = container_of(mdev, struct mt76x02_dev, mt76); + + mt76_unregister_device(mdev); + mt76x0e_cleanup(dev); + mt76_free_device(mdev); +} + +#ifdef CONFIG_PM +static int mt76x0e_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct mt76_dev *mdev = pci_get_drvdata(pdev); + struct mt76x02_dev *dev = container_of(mdev, struct mt76x02_dev, mt76); + int i; + + mt76_worker_disable(&mdev->tx_worker); + for (i = 0; i < ARRAY_SIZE(mdev->phy.q_tx); i++) + mt76_queue_tx_cleanup(dev, mdev->phy.q_tx[i], true); + for (i = 0; i < ARRAY_SIZE(mdev->q_mcu); i++) + mt76_queue_tx_cleanup(dev, mdev->q_mcu[i], true); + napi_disable(&mdev->tx_napi); + + mt76_for_each_q_rx(mdev, i) + napi_disable(&mdev->napi[i]); + + mt76x02_dma_disable(dev); + mt76x02_mcu_cleanup(dev); + mt76x0_chip_onoff(dev, false, false); + + pci_enable_wake(pdev, pci_choose_state(pdev, state), true); + pci_save_state(pdev); + + return pci_set_power_state(pdev, pci_choose_state(pdev, state)); +} + +static int mt76x0e_resume(struct pci_dev *pdev) +{ + struct mt76_dev *mdev = pci_get_drvdata(pdev); + struct mt76x02_dev *dev = container_of(mdev, struct mt76x02_dev, mt76); + int err, i; + + err = pci_set_power_state(pdev, PCI_D0); + if (err) + return err; + + pci_restore_state(pdev); + + mt76_worker_enable(&mdev->tx_worker); + + mt76_for_each_q_rx(mdev, i) { + mt76_queue_rx_reset(dev, i); + napi_enable(&mdev->napi[i]); + } + napi_enable(&mdev->tx_napi); + + local_bh_disable(); + mt76_for_each_q_rx(mdev, i) { + napi_schedule(&mdev->napi[i]); + } + napi_schedule(&mdev->tx_napi); + local_bh_enable(); + + return mt76x0e_init_hardware(dev, true); +} +#endif /* CONFIG_PM */ + +static const struct pci_device_id mt76x0e_device_table[] = { + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7610) }, + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7630) }, + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7650) }, + { }, +}; + +MODULE_DEVICE_TABLE(pci, mt76x0e_device_table); +MODULE_FIRMWARE(MT7610E_FIRMWARE); +MODULE_FIRMWARE(MT7650E_FIRMWARE); +MODULE_DESCRIPTION("MediaTek MT76x0E (PCIe) wireless driver"); +MODULE_LICENSE("Dual BSD/GPL"); + +static struct pci_driver mt76x0e_driver = { + .name = KBUILD_MODNAME, + .id_table = mt76x0e_device_table, + .probe = mt76x0e_probe, + .remove = mt76x0e_remove, +#ifdef CONFIG_PM + .suspend = mt76x0e_suspend, + .resume = mt76x0e_resume, +#endif /* CONFIG_PM */ +}; + +module_pci_driver(mt76x0e_driver); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/pci_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76x0/pci_mcu.c new file mode 100644 index 000000000..efa549dc6 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/pci_mcu.c @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (C) 2018 Lorenzo Bianconi + */ +#include +#include + +#include "mt76x0.h" +#include "mcu.h" + +#define MT_MCU_IVB_ADDR (MT_MCU_ILM_ADDR + 0x54000 - MT_MCU_IVB_SIZE) + +static int mt76x0e_load_firmware(struct mt76x02_dev *dev) +{ + bool is_combo_chip = mt76_chip(&dev->mt76) != 0x7610; + u32 val, ilm_len, dlm_len, offset = 0; + const struct mt76x02_fw_header *hdr; + const struct firmware *fw; + const char *firmware; + const u8 *fw_payload; + int len, err; + + if (is_combo_chip) + firmware = MT7650E_FIRMWARE; + else + firmware = MT7610E_FIRMWARE; + + err = request_firmware(&fw, firmware, dev->mt76.dev); + if (err) + return err; + + if (!fw || !fw->data || fw->size < sizeof(*hdr)) { + err = -EIO; + goto out; + } + + hdr = (const struct mt76x02_fw_header *)fw->data; + + len = sizeof(*hdr); + len += le32_to_cpu(hdr->ilm_len); + len += le32_to_cpu(hdr->dlm_len); + + if (fw->size != len) { + err = -EIO; + goto out; + } + + fw_payload = fw->data + sizeof(*hdr); + + val = le16_to_cpu(hdr->fw_ver); + dev_info(dev->mt76.dev, "Firmware Version: %d.%d.%02d\n", + (val >> 12) & 0xf, (val >> 8) & 0xf, val & 0xf); + + val = le16_to_cpu(hdr->fw_ver); + dev_dbg(dev->mt76.dev, + "Firmware Version: %d.%d.%02d Build: %x Build time: %.16s\n", + (val >> 12) & 0xf, (val >> 8) & 0xf, val & 0xf, + le16_to_cpu(hdr->build_ver), hdr->build_time); + + if (is_combo_chip && !mt76_poll(dev, MT_MCU_SEMAPHORE_00, 1, 1, 600)) { + dev_err(dev->mt76.dev, + "Could not get hardware semaphore for loading fw\n"); + err = -ETIMEDOUT; + goto out; + } + + /* upload ILM. */ + mt76_wr(dev, MT_MCU_PCIE_REMAP_BASE4, 0); + ilm_len = le32_to_cpu(hdr->ilm_len); + if (is_combo_chip) { + ilm_len -= MT_MCU_IVB_SIZE; + offset = MT_MCU_IVB_SIZE; + } + dev_dbg(dev->mt76.dev, "loading FW - ILM %u\n", ilm_len); + mt76_wr_copy(dev, MT_MCU_ILM_ADDR + offset, fw_payload + offset, + ilm_len); + + /* upload IVB. */ + if (is_combo_chip) { + dev_dbg(dev->mt76.dev, "loading FW - IVB %u\n", + MT_MCU_IVB_SIZE); + mt76_wr_copy(dev, MT_MCU_IVB_ADDR, fw_payload, MT_MCU_IVB_SIZE); + } + + /* upload DLM. */ + mt76_wr(dev, MT_MCU_PCIE_REMAP_BASE4, MT_MCU_DLM_OFFSET); + dlm_len = le32_to_cpu(hdr->dlm_len); + dev_dbg(dev->mt76.dev, "loading FW - DLM %u\n", dlm_len); + mt76_wr_copy(dev, MT_MCU_ILM_ADDR, + fw_payload + le32_to_cpu(hdr->ilm_len), dlm_len); + + /* trigger firmware */ + mt76_wr(dev, MT_MCU_PCIE_REMAP_BASE4, 0); + if (is_combo_chip) + mt76_wr(dev, MT_MCU_INT_LEVEL, 0x3); + else + mt76_wr(dev, MT_MCU_RESET_CTL, 0x300); + + if (!mt76_poll_msec(dev, MT_MCU_COM_REG0, 1, 1, 1000)) { + dev_err(dev->mt76.dev, "Firmware failed to start\n"); + err = -ETIMEDOUT; + goto out; + } + + mt76x02_set_ethtool_fwver(dev, hdr); + dev_dbg(dev->mt76.dev, "Firmware running!\n"); + +out: + if (is_combo_chip) + mt76_wr(dev, MT_MCU_SEMAPHORE_00, 0x1); + release_firmware(fw); + + return err; +} + +int mt76x0e_mcu_init(struct mt76x02_dev *dev) +{ + static const struct mt76_mcu_ops mt76x0e_mcu_ops = { + .mcu_send_msg = mt76x02_mcu_msg_send, + .mcu_parse_response = mt76x02_mcu_parse_response, + }; + int err; + + dev->mt76.mcu_ops = &mt76x0e_mcu_ops; + + err = mt76x0e_load_firmware(dev); + if (err < 0) + return err; + + set_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state); + + return 0; +} diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/phy.c b/drivers/net/wireless/mediatek/mt76/mt76x0/phy.c new file mode 100644 index 000000000..ec554a059 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/phy.c @@ -0,0 +1,1215 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * (c) Copyright 2002-2010, Ralink Technology, Inc. + * Copyright (C) 2014 Felix Fietkau + * Copyright (C) 2015 Jakub Kicinski + * Copyright (C) 2018 Stanislaw Gruszka + */ + +#include +#include + +#include "mt76x0.h" +#include "mcu.h" +#include "eeprom.h" +#include "phy.h" +#include "initvals.h" +#include "initvals_phy.h" +#include "../mt76x02_phy.h" + +static int +mt76x0_rf_csr_wr(struct mt76x02_dev *dev, u32 offset, u8 value) +{ + int ret = 0; + u8 bank, reg; + + if (test_bit(MT76_REMOVED, &dev->mphy.state)) + return -ENODEV; + + bank = MT_RF_BANK(offset); + reg = MT_RF_REG(offset); + + if (WARN_ON_ONCE(reg > 127) || WARN_ON_ONCE(bank > 8)) + return -EINVAL; + + mutex_lock(&dev->phy_mutex); + + if (!mt76_poll(dev, MT_RF_CSR_CFG, MT_RF_CSR_CFG_KICK, 0, 100)) { + ret = -ETIMEDOUT; + goto out; + } + + mt76_wr(dev, MT_RF_CSR_CFG, + FIELD_PREP(MT_RF_CSR_CFG_DATA, value) | + FIELD_PREP(MT_RF_CSR_CFG_REG_BANK, bank) | + FIELD_PREP(MT_RF_CSR_CFG_REG_ID, reg) | + MT_RF_CSR_CFG_WR | + MT_RF_CSR_CFG_KICK); + +out: + mutex_unlock(&dev->phy_mutex); + + if (ret < 0) + dev_err(dev->mt76.dev, "Error: RF write %d:%d failed:%d!!\n", + bank, reg, ret); + + return ret; +} + +static int mt76x0_rf_csr_rr(struct mt76x02_dev *dev, u32 offset) +{ + int ret = -ETIMEDOUT; + u32 val; + u8 bank, reg; + + if (test_bit(MT76_REMOVED, &dev->mphy.state)) + return -ENODEV; + + bank = MT_RF_BANK(offset); + reg = MT_RF_REG(offset); + + if (WARN_ON_ONCE(reg > 127) || WARN_ON_ONCE(bank > 8)) + return -EINVAL; + + mutex_lock(&dev->phy_mutex); + + if (!mt76_poll(dev, MT_RF_CSR_CFG, MT_RF_CSR_CFG_KICK, 0, 100)) + goto out; + + mt76_wr(dev, MT_RF_CSR_CFG, + FIELD_PREP(MT_RF_CSR_CFG_REG_BANK, bank) | + FIELD_PREP(MT_RF_CSR_CFG_REG_ID, reg) | + MT_RF_CSR_CFG_KICK); + + if (!mt76_poll(dev, MT_RF_CSR_CFG, MT_RF_CSR_CFG_KICK, 0, 100)) + goto out; + + val = mt76_rr(dev, MT_RF_CSR_CFG); + if (FIELD_GET(MT_RF_CSR_CFG_REG_ID, val) == reg && + FIELD_GET(MT_RF_CSR_CFG_REG_BANK, val) == bank) + ret = FIELD_GET(MT_RF_CSR_CFG_DATA, val); + +out: + mutex_unlock(&dev->phy_mutex); + + if (ret < 0) + dev_err(dev->mt76.dev, "Error: RF read %d:%d failed:%d!!\n", + bank, reg, ret); + + return ret; +} + +static int +mt76x0_rf_wr(struct mt76x02_dev *dev, u32 offset, u8 val) +{ + if (mt76_is_usb(&dev->mt76)) { + struct mt76_reg_pair pair = { + .reg = offset, + .value = val, + }; + + WARN_ON_ONCE(!test_bit(MT76_STATE_MCU_RUNNING, + &dev->mphy.state)); + return mt76_wr_rp(dev, MT_MCU_MEMMAP_RF, &pair, 1); + } else { + return mt76x0_rf_csr_wr(dev, offset, val); + } +} + +static int mt76x0_rf_rr(struct mt76x02_dev *dev, u32 offset) +{ + int ret; + u32 val; + + if (mt76_is_usb(&dev->mt76)) { + struct mt76_reg_pair pair = { + .reg = offset, + }; + + WARN_ON_ONCE(!test_bit(MT76_STATE_MCU_RUNNING, + &dev->mphy.state)); + ret = mt76_rd_rp(dev, MT_MCU_MEMMAP_RF, &pair, 1); + val = pair.value; + } else { + ret = val = mt76x0_rf_csr_rr(dev, offset); + } + + return (ret < 0) ? ret : val; +} + +static int +mt76x0_rf_rmw(struct mt76x02_dev *dev, u32 offset, u8 mask, u8 val) +{ + int ret; + + ret = mt76x0_rf_rr(dev, offset); + if (ret < 0) + return ret; + + val |= ret & ~mask; + + ret = mt76x0_rf_wr(dev, offset, val); + return ret ? ret : val; +} + +static int +mt76x0_rf_set(struct mt76x02_dev *dev, u32 offset, u8 val) +{ + return mt76x0_rf_rmw(dev, offset, 0, val); +} + +static int +mt76x0_rf_clear(struct mt76x02_dev *dev, u32 offset, u8 mask) +{ + return mt76x0_rf_rmw(dev, offset, mask, 0); +} + +static void +mt76x0_phy_rf_csr_wr_rp(struct mt76x02_dev *dev, + const struct mt76_reg_pair *data, + int n) +{ + while (n-- > 0) { + mt76x0_rf_csr_wr(dev, data->reg, data->value); + data++; + } +} + +#define RF_RANDOM_WRITE(dev, tab) do { \ + if (mt76_is_mmio(&dev->mt76)) \ + mt76x0_phy_rf_csr_wr_rp(dev, tab, ARRAY_SIZE(tab)); \ + else \ + mt76_wr_rp(dev, MT_MCU_MEMMAP_RF, tab, ARRAY_SIZE(tab));\ +} while (0) + +int mt76x0_phy_wait_bbp_ready(struct mt76x02_dev *dev) +{ + int i = 20; + u32 val; + + do { + val = mt76_rr(dev, MT_BBP(CORE, 0)); + if (val && ~val) + break; + } while (--i); + + if (!i) { + dev_err(dev->mt76.dev, "Error: BBP is not ready\n"); + return -EIO; + } + + dev_dbg(dev->mt76.dev, "BBP version %08x\n", val); + return 0; +} + +static void +mt76x0_phy_set_band(struct mt76x02_dev *dev, enum nl80211_band band) +{ + switch (band) { + case NL80211_BAND_2GHZ: + RF_RANDOM_WRITE(dev, mt76x0_rf_2g_channel_0_tab); + + mt76x0_rf_wr(dev, MT_RF(5, 0), 0x45); + mt76x0_rf_wr(dev, MT_RF(6, 0), 0x44); + + mt76_wr(dev, MT_TX_ALC_VGA3, 0x00050007); + mt76_wr(dev, MT_TX0_RF_GAIN_CORR, 0x003E0002); + break; + case NL80211_BAND_5GHZ: + RF_RANDOM_WRITE(dev, mt76x0_rf_5g_channel_0_tab); + + mt76x0_rf_wr(dev, MT_RF(5, 0), 0x44); + mt76x0_rf_wr(dev, MT_RF(6, 0), 0x45); + + mt76_wr(dev, MT_TX_ALC_VGA3, 0x00000005); + mt76_wr(dev, MT_TX0_RF_GAIN_CORR, 0x01010102); + break; + default: + break; + } +} + +static void +mt76x0_phy_set_chan_rf_params(struct mt76x02_dev *dev, u8 channel, + u16 rf_bw_band) +{ + const struct mt76x0_freq_item *freq_item; + u16 rf_band = rf_bw_band & 0xff00; + u16 rf_bw = rf_bw_band & 0x00ff; + enum nl80211_band band; + bool b_sdm = false; + u32 mac_reg; + int i; + + for (i = 0; i < ARRAY_SIZE(mt76x0_sdm_channel); i++) { + if (channel == mt76x0_sdm_channel[i]) { + b_sdm = true; + break; + } + } + + for (i = 0; i < ARRAY_SIZE(mt76x0_frequency_plan); i++) { + if (channel == mt76x0_frequency_plan[i].channel) { + rf_band = mt76x0_frequency_plan[i].band; + + if (b_sdm) + freq_item = &mt76x0_sdm_frequency_plan[i]; + else + freq_item = &mt76x0_frequency_plan[i]; + + mt76x0_rf_wr(dev, MT_RF(0, 37), freq_item->pllR37); + mt76x0_rf_wr(dev, MT_RF(0, 36), freq_item->pllR36); + mt76x0_rf_wr(dev, MT_RF(0, 35), freq_item->pllR35); + mt76x0_rf_wr(dev, MT_RF(0, 34), freq_item->pllR34); + mt76x0_rf_wr(dev, MT_RF(0, 33), freq_item->pllR33); + + mt76x0_rf_rmw(dev, MT_RF(0, 32), 0xe0, + freq_item->pllR32_b7b5); + + /* R32<4:0> pll_den: (Denomina - 8) */ + mt76x0_rf_rmw(dev, MT_RF(0, 32), MT_RF_PLL_DEN_MASK, + freq_item->pllR32_b4b0); + + /* R31<7:5> */ + mt76x0_rf_rmw(dev, MT_RF(0, 31), 0xe0, + freq_item->pllR31_b7b5); + + /* R31<4:0> pll_k(Nominator) */ + mt76x0_rf_rmw(dev, MT_RF(0, 31), MT_RF_PLL_K_MASK, + freq_item->pllR31_b4b0); + + /* R30<7> sdm_reset_n */ + if (b_sdm) { + mt76x0_rf_clear(dev, MT_RF(0, 30), + MT_RF_SDM_RESET_MASK); + mt76x0_rf_set(dev, MT_RF(0, 30), + MT_RF_SDM_RESET_MASK); + } else { + mt76x0_rf_rmw(dev, MT_RF(0, 30), + MT_RF_SDM_RESET_MASK, + freq_item->pllR30_b7); + } + + /* R30<6:2> sdmmash_prbs,sin */ + mt76x0_rf_rmw(dev, MT_RF(0, 30), + MT_RF_SDM_MASH_PRBS_MASK, + freq_item->pllR30_b6b2); + + /* R30<1> sdm_bp */ + mt76x0_rf_rmw(dev, MT_RF(0, 30), MT_RF_SDM_BP_MASK, + freq_item->pllR30_b1 << 1); + + /* R30<0> R29<7:0> (hex) pll_n */ + mt76x0_rf_wr(dev, MT_RF(0, 29), + freq_item->pll_n & 0xff); + + mt76x0_rf_rmw(dev, MT_RF(0, 30), 0x1, + (freq_item->pll_n >> 8) & 0x1); + + /* R28<7:6> isi_iso */ + mt76x0_rf_rmw(dev, MT_RF(0, 28), MT_RF_ISI_ISO_MASK, + freq_item->pllR28_b7b6); + + /* R28<5:4> pfd_dly */ + mt76x0_rf_rmw(dev, MT_RF(0, 28), MT_RF_PFD_DLY_MASK, + freq_item->pllR28_b5b4); + + /* R28<3:2> clksel option */ + mt76x0_rf_rmw(dev, MT_RF(0, 28), MT_RF_CLK_SEL_MASK, + freq_item->pllR28_b3b2); + + /* R28<1:0> R27<7:0> R26<7:0> (hex) sdm_k */ + mt76x0_rf_wr(dev, MT_RF(0, 26), + freq_item->pll_sdm_k & 0xff); + mt76x0_rf_wr(dev, MT_RF(0, 27), + (freq_item->pll_sdm_k >> 8) & 0xff); + + mt76x0_rf_rmw(dev, MT_RF(0, 28), 0x3, + (freq_item->pll_sdm_k >> 16) & 0x3); + + /* R24<1:0> xo_div */ + mt76x0_rf_rmw(dev, MT_RF(0, 24), MT_RF_XO_DIV_MASK, + freq_item->pllR24_b1b0); + + break; + } + } + + for (i = 0; i < ARRAY_SIZE(mt76x0_rf_bw_switch_tab); i++) { + if (rf_bw == mt76x0_rf_bw_switch_tab[i].bw_band) { + mt76x0_rf_wr(dev, + mt76x0_rf_bw_switch_tab[i].rf_bank_reg, + mt76x0_rf_bw_switch_tab[i].value); + } else if ((rf_bw == (mt76x0_rf_bw_switch_tab[i].bw_band & 0xFF)) && + (rf_band & mt76x0_rf_bw_switch_tab[i].bw_band)) { + mt76x0_rf_wr(dev, + mt76x0_rf_bw_switch_tab[i].rf_bank_reg, + mt76x0_rf_bw_switch_tab[i].value); + } + } + + for (i = 0; i < ARRAY_SIZE(mt76x0_rf_band_switch_tab); i++) { + if (mt76x0_rf_band_switch_tab[i].bw_band & rf_band) { + mt76x0_rf_wr(dev, + mt76x0_rf_band_switch_tab[i].rf_bank_reg, + mt76x0_rf_band_switch_tab[i].value); + } + } + + mt76_clear(dev, MT_RF_MISC, 0xc); + + band = (rf_band & RF_G_BAND) ? NL80211_BAND_2GHZ : NL80211_BAND_5GHZ; + if (mt76x02_ext_pa_enabled(dev, band)) { + /* MT_RF_MISC (offset: 0x0518) + * [2]1'b1: enable external A band PA + * 1'b0: disable external A band PA + * [3]1'b1: enable external G band PA + * 1'b0: disable external G band PA + */ + if (rf_band & RF_A_BAND) + mt76_set(dev, MT_RF_MISC, BIT(2)); + else + mt76_set(dev, MT_RF_MISC, BIT(3)); + + /* External PA */ + for (i = 0; i < ARRAY_SIZE(mt76x0_rf_ext_pa_tab); i++) + if (mt76x0_rf_ext_pa_tab[i].bw_band & rf_band) + mt76x0_rf_wr(dev, + mt76x0_rf_ext_pa_tab[i].rf_bank_reg, + mt76x0_rf_ext_pa_tab[i].value); + } + + if (rf_band & RF_G_BAND) { + mt76_wr(dev, MT_TX0_RF_GAIN_ATTEN, 0x63707400); + /* Set Atten mode = 2 For G band, Disable Tx Inc dcoc. */ + mac_reg = mt76_rr(dev, MT_TX_ALC_CFG_1); + mac_reg &= 0x896400FF; + mt76_wr(dev, MT_TX_ALC_CFG_1, mac_reg); + } else { + mt76_wr(dev, MT_TX0_RF_GAIN_ATTEN, 0x686A7800); + /* Set Atten mode = 0 + * For Ext A band, Disable Tx Inc dcoc Cal. + */ + mac_reg = mt76_rr(dev, MT_TX_ALC_CFG_1); + mac_reg &= 0x890400FF; + mt76_wr(dev, MT_TX_ALC_CFG_1, mac_reg); + } +} + +static void +mt76x0_phy_set_chan_bbp_params(struct mt76x02_dev *dev, u16 rf_bw_band) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(mt76x0_bbp_switch_tab); i++) { + const struct mt76x0_bbp_switch_item *item = &mt76x0_bbp_switch_tab[i]; + const struct mt76_reg_pair *pair = &item->reg_pair; + + if ((rf_bw_band & item->bw_band) != rf_bw_band) + continue; + + if (pair->reg == MT_BBP(AGC, 8)) { + u32 val = pair->value; + u8 gain; + + gain = FIELD_GET(MT_BBP_AGC_GAIN, val); + gain -= dev->cal.rx.lna_gain * 2; + val &= ~MT_BBP_AGC_GAIN; + val |= FIELD_PREP(MT_BBP_AGC_GAIN, gain); + mt76_wr(dev, pair->reg, val); + } else { + mt76_wr(dev, pair->reg, pair->value); + } + } +} + +static void mt76x0_phy_ant_select(struct mt76x02_dev *dev) +{ + u16 ee_ant = mt76x02_eeprom_get(dev, MT_EE_ANTENNA); + u16 ee_cfg1 = mt76x02_eeprom_get(dev, MT_EE_CFG1_INIT); + u16 nic_conf2 = mt76x02_eeprom_get(dev, MT_EE_NIC_CONF_2); + u32 wlan, coex3; + bool ant_div; + + wlan = mt76_rr(dev, MT_WLAN_FUN_CTRL); + coex3 = mt76_rr(dev, MT_COEXCFG3); + + ee_ant &= ~(BIT(14) | BIT(12)); + wlan &= ~(BIT(6) | BIT(5)); + coex3 &= ~GENMASK(5, 2); + + if (ee_ant & MT_EE_ANTENNA_DUAL) { + /* dual antenna mode */ + ant_div = !(nic_conf2 & MT_EE_NIC_CONF_2_ANT_OPT) && + (nic_conf2 & MT_EE_NIC_CONF_2_ANT_DIV); + if (ant_div) + ee_ant |= BIT(12); + else + coex3 |= BIT(4); + coex3 |= BIT(3); + if (dev->mphy.cap.has_2ghz) + wlan |= BIT(6); + } else { + /* sigle antenna mode */ + if (dev->mphy.cap.has_5ghz) { + coex3 |= BIT(3) | BIT(4); + } else { + wlan |= BIT(6); + coex3 |= BIT(1); + } + } + + if (is_mt7630(dev)) + ee_ant |= BIT(14) | BIT(11); + + mt76_wr(dev, MT_WLAN_FUN_CTRL, wlan); + mt76_rmw(dev, MT_CMB_CTRL, GENMASK(15, 0), ee_ant); + mt76_rmw(dev, MT_CSR_EE_CFG1, GENMASK(15, 0), ee_cfg1); + mt76_clear(dev, MT_COEXCFG0, BIT(2)); + mt76_wr(dev, MT_COEXCFG3, coex3); +} + +static void +mt76x0_phy_bbp_set_bw(struct mt76x02_dev *dev, enum nl80211_chan_width width) +{ + enum { BW_20 = 0, BW_40 = 1, BW_80 = 2, BW_10 = 4}; + int bw; + + switch (width) { + default: + case NL80211_CHAN_WIDTH_20_NOHT: + case NL80211_CHAN_WIDTH_20: + bw = BW_20; + break; + case NL80211_CHAN_WIDTH_40: + bw = BW_40; + break; + case NL80211_CHAN_WIDTH_80: + bw = BW_80; + break; + case NL80211_CHAN_WIDTH_10: + bw = BW_10; + break; + case NL80211_CHAN_WIDTH_80P80: + case NL80211_CHAN_WIDTH_160: + case NL80211_CHAN_WIDTH_5: + /* TODO error */ + return; + } + + mt76x02_mcu_function_select(dev, BW_SETTING, bw); +} + +static void mt76x0_phy_tssi_dc_calibrate(struct mt76x02_dev *dev) +{ + struct ieee80211_channel *chan = dev->mphy.chandef.chan; + u32 val; + + if (chan->band == NL80211_BAND_5GHZ) + mt76x0_rf_clear(dev, MT_RF(0, 67), 0xf); + + /* bypass ADDA control */ + mt76_wr(dev, MT_RF_SETTING_0, 0x60002237); + mt76_wr(dev, MT_RF_BYPASS_0, 0xffffffff); + + /* bbp sw reset */ + mt76_set(dev, MT_BBP(CORE, 4), BIT(0)); + usleep_range(500, 1000); + mt76_clear(dev, MT_BBP(CORE, 4), BIT(0)); + + val = (chan->band == NL80211_BAND_5GHZ) ? 0x80055 : 0x80050; + mt76_wr(dev, MT_BBP(CORE, 34), val); + + /* enable TX with DAC0 input */ + mt76_wr(dev, MT_BBP(TXBE, 6), BIT(31)); + + mt76_poll_msec(dev, MT_BBP(CORE, 34), BIT(4), 0, 200); + dev->cal.tssi_dc = mt76_rr(dev, MT_BBP(CORE, 35)) & 0xff; + + /* stop bypass ADDA */ + mt76_wr(dev, MT_RF_BYPASS_0, 0); + /* stop TX */ + mt76_wr(dev, MT_BBP(TXBE, 6), 0); + /* bbp sw reset */ + mt76_set(dev, MT_BBP(CORE, 4), BIT(0)); + usleep_range(500, 1000); + mt76_clear(dev, MT_BBP(CORE, 4), BIT(0)); + + if (chan->band == NL80211_BAND_5GHZ) + mt76x0_rf_rmw(dev, MT_RF(0, 67), 0xf, 0x4); +} + +static int +mt76x0_phy_tssi_adc_calibrate(struct mt76x02_dev *dev, s16 *ltssi, + u8 *info) +{ + struct ieee80211_channel *chan = dev->mphy.chandef.chan; + u32 val; + + val = (chan->band == NL80211_BAND_5GHZ) ? 0x80055 : 0x80050; + mt76_wr(dev, MT_BBP(CORE, 34), val); + + if (!mt76_poll_msec(dev, MT_BBP(CORE, 34), BIT(4), 0, 200)) { + mt76_clear(dev, MT_BBP(CORE, 34), BIT(4)); + return -ETIMEDOUT; + } + + *ltssi = mt76_rr(dev, MT_BBP(CORE, 35)) & 0xff; + if (chan->band == NL80211_BAND_5GHZ) + *ltssi += 128; + + /* set packet info#1 mode */ + mt76_wr(dev, MT_BBP(CORE, 34), 0x80041); + info[0] = mt76_rr(dev, MT_BBP(CORE, 35)) & 0xff; + + /* set packet info#2 mode */ + mt76_wr(dev, MT_BBP(CORE, 34), 0x80042); + info[1] = mt76_rr(dev, MT_BBP(CORE, 35)) & 0xff; + + /* set packet info#3 mode */ + mt76_wr(dev, MT_BBP(CORE, 34), 0x80043); + info[2] = mt76_rr(dev, MT_BBP(CORE, 35)) & 0xff; + + return 0; +} + +static u8 mt76x0_phy_get_rf_pa_mode(struct mt76x02_dev *dev, + int index, u8 tx_rate) +{ + u32 val, reg; + + reg = (index == 1) ? MT_RF_PA_MODE_CFG1 : MT_RF_PA_MODE_CFG0; + val = mt76_rr(dev, reg); + return (val & (3 << (tx_rate * 2))) >> (tx_rate * 2); +} + +static int +mt76x0_phy_get_target_power(struct mt76x02_dev *dev, u8 tx_mode, + u8 *info, s8 *target_power, + s8 *target_pa_power) +{ + u8 tx_rate, cur_power; + + cur_power = mt76_rr(dev, MT_TX_ALC_CFG_0) & MT_TX_ALC_CFG_0_CH_INIT_0; + switch (tx_mode) { + case 0: + /* cck rates */ + tx_rate = (info[0] & 0x60) >> 5; + *target_power = cur_power + dev->rate_power.cck[tx_rate]; + *target_pa_power = mt76x0_phy_get_rf_pa_mode(dev, 0, tx_rate); + break; + case 1: { + u8 index; + + /* ofdm rates */ + tx_rate = (info[0] & 0xf0) >> 4; + switch (tx_rate) { + case 0xb: + index = 0; + break; + case 0xf: + index = 1; + break; + case 0xa: + index = 2; + break; + case 0xe: + index = 3; + break; + case 0x9: + index = 4; + break; + case 0xd: + index = 5; + break; + case 0x8: + index = 6; + break; + case 0xc: + index = 7; + break; + default: + return -EINVAL; + } + + *target_power = cur_power + dev->rate_power.ofdm[index]; + *target_pa_power = mt76x0_phy_get_rf_pa_mode(dev, 0, index + 4); + break; + } + case 4: + /* vht rates */ + tx_rate = info[1] & 0xf; + if (tx_rate > 9) + return -EINVAL; + + *target_power = cur_power; + if (tx_rate > 7) + *target_power += dev->rate_power.vht[tx_rate - 8]; + else + *target_power += dev->rate_power.ht[tx_rate]; + + *target_pa_power = mt76x0_phy_get_rf_pa_mode(dev, 1, tx_rate); + break; + default: + /* ht rates */ + tx_rate = info[1] & 0x7f; + if (tx_rate > 9) + return -EINVAL; + + *target_power = cur_power + dev->rate_power.ht[tx_rate]; + *target_pa_power = mt76x0_phy_get_rf_pa_mode(dev, 1, tx_rate); + break; + } + + return 0; +} + +static s16 mt76x0_phy_lin2db(u16 val) +{ + u32 mantissa = val << 4; + int ret, data; + s16 exp = -4; + + while (mantissa < BIT(15)) { + mantissa <<= 1; + if (--exp < -20) + return -10000; + } + while (mantissa > 0xffff) { + mantissa >>= 1; + if (++exp > 20) + return -10000; + } + + /* s(15,0) */ + if (mantissa <= 47104) + data = mantissa + (mantissa >> 3) + (mantissa >> 4) - 38400; + else + data = mantissa - (mantissa >> 3) - (mantissa >> 6) - 23040; + data = max_t(int, 0, data); + + ret = ((15 + exp) << 15) + data; + ret = (ret << 2) + (ret << 1) + (ret >> 6) + (ret >> 7); + return ret >> 10; +} + +static int +mt76x0_phy_get_delta_power(struct mt76x02_dev *dev, u8 tx_mode, + s8 target_power, s8 target_pa_power, + s16 ltssi) +{ + struct ieee80211_channel *chan = dev->mphy.chandef.chan; + int tssi_target = target_power << 12, tssi_slope; + int tssi_offset, tssi_db, ret; + u32 data; + u16 val; + + if (chan->band == NL80211_BAND_5GHZ) { + u8 bound[7]; + int i, err; + + err = mt76x02_eeprom_copy(dev, MT_EE_TSSI_BOUND1, bound, + sizeof(bound)); + if (err < 0) + return err; + + for (i = 0; i < ARRAY_SIZE(bound); i++) { + if (chan->hw_value <= bound[i] || !bound[i]) + break; + } + val = mt76x02_eeprom_get(dev, MT_EE_TSSI_SLOPE_5G + i * 2); + + tssi_offset = val >> 8; + if ((tssi_offset >= 64 && tssi_offset <= 127) || + (tssi_offset & BIT(7))) + tssi_offset -= BIT(8); + } else { + val = mt76x02_eeprom_get(dev, MT_EE_TSSI_SLOPE_2G); + + tssi_offset = val >> 8; + if (tssi_offset & BIT(7)) + tssi_offset -= BIT(8); + } + tssi_slope = val & 0xff; + + switch (target_pa_power) { + case 1: + if (chan->band == NL80211_BAND_2GHZ) + tssi_target += 29491; /* 3.6 * 8192 */ + fallthrough; + case 0: + break; + default: + tssi_target += 4424; /* 0.54 * 8192 */ + break; + } + + if (!tx_mode) { + data = mt76_rr(dev, MT_BBP(CORE, 1)); + if (is_mt7630(dev) && mt76_is_mmio(&dev->mt76)) { + int offset; + + /* 2.3 * 8192 or 1.5 * 8192 */ + offset = (data & BIT(5)) ? 18841 : 12288; + tssi_target += offset; + } else if (data & BIT(5)) { + /* 0.8 * 8192 */ + tssi_target += 6554; + } + } + + data = mt76_rr(dev, MT_BBP(TXBE, 4)); + switch (data & 0x3) { + case 1: + tssi_target -= 49152; /* -6db * 8192 */ + break; + case 2: + tssi_target -= 98304; /* -12db * 8192 */ + break; + case 3: + tssi_target += 49152; /* 6db * 8192 */ + break; + default: + break; + } + + tssi_db = mt76x0_phy_lin2db(ltssi - dev->cal.tssi_dc) * tssi_slope; + if (chan->band == NL80211_BAND_5GHZ) { + tssi_db += ((tssi_offset - 50) << 10); /* offset s4.3 */ + tssi_target -= tssi_db; + if (ltssi > 254 && tssi_target > 0) { + /* upper saturate */ + tssi_target = 0; + } + } else { + tssi_db += (tssi_offset << 9); /* offset s3.4 */ + tssi_target -= tssi_db; + /* upper-lower saturate */ + if ((ltssi > 126 && tssi_target > 0) || + ((ltssi - dev->cal.tssi_dc) < 1 && tssi_target < 0)) { + tssi_target = 0; + } + } + + if ((dev->cal.tssi_target ^ tssi_target) < 0 && + dev->cal.tssi_target > -4096 && dev->cal.tssi_target < 4096 && + tssi_target > -4096 && tssi_target < 4096) { + if ((tssi_target < 0 && + tssi_target + dev->cal.tssi_target > 0) || + (tssi_target > 0 && + tssi_target + dev->cal.tssi_target <= 0)) + tssi_target = 0; + else + dev->cal.tssi_target = tssi_target; + } else { + dev->cal.tssi_target = tssi_target; + } + + /* make the compensate value to the nearest compensate code */ + if (tssi_target > 0) + tssi_target += 2048; + else + tssi_target -= 2048; + tssi_target >>= 12; + + ret = mt76_get_field(dev, MT_TX_ALC_CFG_1, MT_TX_ALC_CFG_1_TEMP_COMP); + if (ret & BIT(5)) + ret -= BIT(6); + ret += tssi_target; + + ret = min_t(int, 31, ret); + return max_t(int, -32, ret); +} + +static void mt76x0_phy_tssi_calibrate(struct mt76x02_dev *dev) +{ + s8 target_power, target_pa_power; + u8 tssi_info[3], tx_mode; + s16 ltssi; + s8 val; + + if (mt76x0_phy_tssi_adc_calibrate(dev, <ssi, tssi_info) < 0) + return; + + tx_mode = tssi_info[0] & 0x7; + if (mt76x0_phy_get_target_power(dev, tx_mode, tssi_info, + &target_power, &target_pa_power) < 0) + return; + + val = mt76x0_phy_get_delta_power(dev, tx_mode, target_power, + target_pa_power, ltssi); + mt76_rmw_field(dev, MT_TX_ALC_CFG_1, MT_TX_ALC_CFG_1_TEMP_COMP, val); +} + +void mt76x0_phy_set_txpower(struct mt76x02_dev *dev) +{ + struct mt76x02_rate_power *t = &dev->rate_power; + s8 info; + + mt76x0_get_tx_power_per_rate(dev, dev->mphy.chandef.chan, t); + mt76x0_get_power_info(dev, dev->mphy.chandef.chan, &info); + + mt76x02_add_rate_power_offset(t, info); + mt76x02_limit_rate_power(t, dev->txpower_conf); + dev->mphy.txpower_cur = mt76x02_get_max_rate_power(t); + mt76x02_add_rate_power_offset(t, -info); + + dev->target_power = info; + mt76x02_phy_set_txpower(dev, info, info); +} + +void mt76x0_phy_calibrate(struct mt76x02_dev *dev, bool power_on) +{ + struct ieee80211_channel *chan = dev->mphy.chandef.chan; + int is_5ghz = (chan->band == NL80211_BAND_5GHZ) ? 1 : 0; + u32 val, tx_alc, reg_val; + + if (is_mt7630(dev)) + return; + + if (power_on) { + mt76x02_mcu_calibrate(dev, MCU_CAL_R, 0); + mt76x02_mcu_calibrate(dev, MCU_CAL_VCO, chan->hw_value); + usleep_range(10, 20); + + if (mt76x0_tssi_enabled(dev)) { + mt76_wr(dev, MT_MAC_SYS_CTRL, + MT_MAC_SYS_CTRL_ENABLE_RX); + mt76x0_phy_tssi_dc_calibrate(dev); + mt76_wr(dev, MT_MAC_SYS_CTRL, + MT_MAC_SYS_CTRL_ENABLE_TX | + MT_MAC_SYS_CTRL_ENABLE_RX); + } + } + + tx_alc = mt76_rr(dev, MT_TX_ALC_CFG_0); + mt76_wr(dev, MT_TX_ALC_CFG_0, 0); + usleep_range(500, 700); + + reg_val = mt76_rr(dev, MT_BBP(IBI, 9)); + mt76_wr(dev, MT_BBP(IBI, 9), 0xffffff7e); + + if (is_5ghz) { + if (chan->hw_value < 100) + val = 0x701; + else if (chan->hw_value < 140) + val = 0x801; + else + val = 0x901; + } else { + val = 0x600; + } + + mt76x02_mcu_calibrate(dev, MCU_CAL_FULL, val); + mt76x02_mcu_calibrate(dev, MCU_CAL_LC, is_5ghz); + usleep_range(15000, 20000); + + mt76_wr(dev, MT_BBP(IBI, 9), reg_val); + mt76_wr(dev, MT_TX_ALC_CFG_0, tx_alc); + mt76x02_mcu_calibrate(dev, MCU_CAL_RXDCOC, 1); +} +EXPORT_SYMBOL_GPL(mt76x0_phy_calibrate); + +void mt76x0_phy_set_channel(struct mt76x02_dev *dev, + struct cfg80211_chan_def *chandef) +{ + u32 ext_cca_chan[4] = { + [0] = FIELD_PREP(MT_EXT_CCA_CFG_CCA0, 0) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA1, 1) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA2, 2) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA3, 3) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA_MASK, BIT(0)), + [1] = FIELD_PREP(MT_EXT_CCA_CFG_CCA0, 1) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA1, 0) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA2, 2) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA3, 3) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA_MASK, BIT(1)), + [2] = FIELD_PREP(MT_EXT_CCA_CFG_CCA0, 2) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA1, 3) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA2, 1) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA3, 0) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA_MASK, BIT(2)), + [3] = FIELD_PREP(MT_EXT_CCA_CFG_CCA0, 3) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA1, 2) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA2, 1) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA3, 0) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA_MASK, BIT(3)), + }; + bool scan = test_bit(MT76_SCANNING, &dev->mphy.state); + int ch_group_index, freq, freq1; + u8 channel; + u32 val; + u16 rf_bw_band; + + freq = chandef->chan->center_freq; + freq1 = chandef->center_freq1; + channel = chandef->chan->hw_value; + rf_bw_band = (channel <= 14) ? RF_G_BAND : RF_A_BAND; + + switch (chandef->width) { + case NL80211_CHAN_WIDTH_40: + if (freq1 > freq) + ch_group_index = 0; + else + ch_group_index = 1; + channel += 2 - ch_group_index * 4; + rf_bw_band |= RF_BW_40; + break; + case NL80211_CHAN_WIDTH_80: + ch_group_index = (freq - freq1 + 30) / 20; + if (WARN_ON(ch_group_index < 0 || ch_group_index > 3)) + ch_group_index = 0; + channel += 6 - ch_group_index * 4; + rf_bw_band |= RF_BW_80; + break; + default: + ch_group_index = 0; + rf_bw_band |= RF_BW_20; + break; + } + + if (mt76_is_usb(&dev->mt76)) { + mt76x0_phy_bbp_set_bw(dev, chandef->width); + } else { + if (chandef->width == NL80211_CHAN_WIDTH_80 || + chandef->width == NL80211_CHAN_WIDTH_40) + val = 0x201; + else + val = 0x601; + mt76_wr(dev, MT_TX_SW_CFG0, val); + } + mt76x02_phy_set_bw(dev, chandef->width, ch_group_index); + mt76x02_phy_set_band(dev, chandef->chan->band, + ch_group_index & 1); + + mt76_rmw(dev, MT_EXT_CCA_CFG, + (MT_EXT_CCA_CFG_CCA0 | + MT_EXT_CCA_CFG_CCA1 | + MT_EXT_CCA_CFG_CCA2 | + MT_EXT_CCA_CFG_CCA3 | + MT_EXT_CCA_CFG_CCA_MASK), + ext_cca_chan[ch_group_index]); + + mt76x0_phy_set_band(dev, chandef->chan->band); + mt76x0_phy_set_chan_rf_params(dev, channel, rf_bw_band); + + /* set Japan Tx filter at channel 14 */ + if (channel == 14) + mt76_set(dev, MT_BBP(CORE, 1), 0x20); + else + mt76_clear(dev, MT_BBP(CORE, 1), 0x20); + + mt76x0_read_rx_gain(dev); + mt76x0_phy_set_chan_bbp_params(dev, rf_bw_band); + + /* enable vco */ + mt76x0_rf_set(dev, MT_RF(0, 4), BIT(7)); + if (scan) + return; + + mt76x02_init_agc_gain(dev); + mt76x0_phy_calibrate(dev, false); + mt76x0_phy_set_txpower(dev); + + ieee80211_queue_delayed_work(dev->mt76.hw, &dev->cal_work, + MT_CALIBRATE_INTERVAL); +} + +static void mt76x0_phy_temp_sensor(struct mt76x02_dev *dev) +{ + u8 rf_b7_73, rf_b0_66, rf_b0_67; + s8 val; + + rf_b7_73 = mt76x0_rf_rr(dev, MT_RF(7, 73)); + rf_b0_66 = mt76x0_rf_rr(dev, MT_RF(0, 66)); + rf_b0_67 = mt76x0_rf_rr(dev, MT_RF(0, 67)); + + mt76x0_rf_wr(dev, MT_RF(7, 73), 0x02); + mt76x0_rf_wr(dev, MT_RF(0, 66), 0x23); + mt76x0_rf_wr(dev, MT_RF(0, 67), 0x01); + + mt76_wr(dev, MT_BBP(CORE, 34), 0x00080055); + if (!mt76_poll_msec(dev, MT_BBP(CORE, 34), BIT(4), 0, 200)) { + mt76_clear(dev, MT_BBP(CORE, 34), BIT(4)); + goto done; + } + + val = mt76_rr(dev, MT_BBP(CORE, 35)); + val = (35 * (val - dev->cal.rx.temp_offset)) / 10 + 25; + + if (abs(val - dev->cal.temp_vco) > 20) { + mt76x02_mcu_calibrate(dev, MCU_CAL_VCO, + dev->mphy.chandef.chan->hw_value); + dev->cal.temp_vco = val; + } + if (abs(val - dev->cal.temp) > 30) { + mt76x0_phy_calibrate(dev, false); + dev->cal.temp = val; + } + +done: + mt76x0_rf_wr(dev, MT_RF(7, 73), rf_b7_73); + mt76x0_rf_wr(dev, MT_RF(0, 66), rf_b0_66); + mt76x0_rf_wr(dev, MT_RF(0, 67), rf_b0_67); +} + +static void mt76x0_phy_set_gain_val(struct mt76x02_dev *dev) +{ + u8 gain = dev->cal.agc_gain_cur[0] - dev->cal.agc_gain_adjust; + + mt76_rmw_field(dev, MT_BBP(AGC, 8), MT_BBP_AGC_GAIN, gain); + + if ((dev->mphy.chandef.chan->flags & IEEE80211_CHAN_RADAR) && + !is_mt7630(dev)) + mt76x02_phy_dfs_adjust_agc(dev); +} + +static void +mt76x0_phy_update_channel_gain(struct mt76x02_dev *dev) +{ + bool gain_change; + u8 gain_delta; + int low_gain; + + dev->cal.avg_rssi_all = mt76_get_min_avg_rssi(&dev->mt76, 0); + if (!dev->cal.avg_rssi_all) + dev->cal.avg_rssi_all = -75; + + low_gain = (dev->cal.avg_rssi_all > mt76x02_get_rssi_gain_thresh(dev)) + + (dev->cal.avg_rssi_all > mt76x02_get_low_rssi_gain_thresh(dev)); + + gain_change = dev->cal.low_gain < 0 || + (dev->cal.low_gain & 2) ^ (low_gain & 2); + dev->cal.low_gain = low_gain; + + if (!gain_change) { + if (mt76x02_phy_adjust_vga_gain(dev)) + mt76x0_phy_set_gain_val(dev); + return; + } + + dev->cal.agc_gain_adjust = (low_gain == 2) ? 0 : 10; + gain_delta = (low_gain == 2) ? 10 : 0; + + dev->cal.agc_gain_cur[0] = dev->cal.agc_gain_init[0] - gain_delta; + mt76x0_phy_set_gain_val(dev); + + /* clear false CCA counters */ + mt76_rr(dev, MT_RX_STAT_1); +} + +static void mt76x0_phy_calibration_work(struct work_struct *work) +{ + struct mt76x02_dev *dev = container_of(work, struct mt76x02_dev, + cal_work.work); + + mt76x0_phy_update_channel_gain(dev); + if (mt76x0_tssi_enabled(dev)) + mt76x0_phy_tssi_calibrate(dev); + else + mt76x0_phy_temp_sensor(dev); + + ieee80211_queue_delayed_work(dev->mt76.hw, &dev->cal_work, + 4 * MT_CALIBRATE_INTERVAL); +} + +static void mt76x0_rf_patch_reg_array(struct mt76x02_dev *dev, + const struct mt76_reg_pair *rp, int len) +{ + int i; + + for (i = 0; i < len; i++) { + u32 reg = rp[i].reg; + u8 val = rp[i].value; + + switch (reg) { + case MT_RF(0, 3): + if (mt76_is_mmio(&dev->mt76)) { + if (is_mt7630(dev)) + val = 0x70; + else + val = 0x63; + } else { + val = 0x73; + } + break; + case MT_RF(0, 21): + if (is_mt7610e(dev)) + val = 0x10; + else + val = 0x12; + break; + case MT_RF(5, 2): + if (is_mt7630(dev)) + val = 0x1d; + else if (is_mt7610e(dev)) + val = 0x00; + else + val = 0x0c; + break; + default: + break; + } + mt76x0_rf_wr(dev, reg, val); + } +} + +static void mt76x0_phy_rf_init(struct mt76x02_dev *dev) +{ + int i; + + mt76x0_rf_patch_reg_array(dev, mt76x0_rf_central_tab, + ARRAY_SIZE(mt76x0_rf_central_tab)); + mt76x0_rf_patch_reg_array(dev, mt76x0_rf_2g_channel_0_tab, + ARRAY_SIZE(mt76x0_rf_2g_channel_0_tab)); + RF_RANDOM_WRITE(dev, mt76x0_rf_5g_channel_0_tab); + RF_RANDOM_WRITE(dev, mt76x0_rf_vga_channel_0_tab); + + for (i = 0; i < ARRAY_SIZE(mt76x0_rf_bw_switch_tab); i++) { + const struct mt76x0_rf_switch_item *item = &mt76x0_rf_bw_switch_tab[i]; + + if (item->bw_band == RF_BW_20) + mt76x0_rf_wr(dev, item->rf_bank_reg, item->value); + else if (((RF_G_BAND | RF_BW_20) & item->bw_band) == + (RF_G_BAND | RF_BW_20)) + mt76x0_rf_wr(dev, item->rf_bank_reg, item->value); + } + + for (i = 0; i < ARRAY_SIZE(mt76x0_rf_band_switch_tab); i++) { + if (mt76x0_rf_band_switch_tab[i].bw_band & RF_G_BAND) { + mt76x0_rf_wr(dev, + mt76x0_rf_band_switch_tab[i].rf_bank_reg, + mt76x0_rf_band_switch_tab[i].value); + } + } + + /* Frequency calibration + * E1: B0.R22<6:0>: xo_cxo<6:0> + * E2: B0.R21<0>: xo_cxo<0>, B0.R22<7:0>: xo_cxo<8:1> + */ + mt76x0_rf_wr(dev, MT_RF(0, 22), + min_t(u8, dev->cal.rx.freq_offset, 0xbf)); + mt76x0_rf_rr(dev, MT_RF(0, 22)); + + /* Reset procedure DAC during power-up: + * - set B0.R73<7> + * - clear B0.R73<7> + * - set B0.R73<7> + */ + mt76x0_rf_set(dev, MT_RF(0, 73), BIT(7)); + mt76x0_rf_clear(dev, MT_RF(0, 73), BIT(7)); + mt76x0_rf_set(dev, MT_RF(0, 73), BIT(7)); + + /* vcocal_en: initiate VCO calibration (reset after completion)) */ + mt76x0_rf_set(dev, MT_RF(0, 4), 0x80); +} + +void mt76x0_phy_init(struct mt76x02_dev *dev) +{ + INIT_DELAYED_WORK(&dev->cal_work, mt76x0_phy_calibration_work); + + mt76x0_phy_ant_select(dev); + mt76x0_phy_rf_init(dev); + mt76x02_phy_set_rxpath(dev); + mt76x02_phy_set_txdac(dev); +} diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/phy.h b/drivers/net/wireless/mediatek/mt76/mt76x0/phy.h new file mode 100644 index 000000000..441d6559d --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/phy.h @@ -0,0 +1,90 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * (c) Copyright 2002-2010, Ralink Technology, Inc. + * Copyright (C) 2018 Stanislaw Gruszka + */ +#ifndef _MT76X0_PHY_H_ +#define _MT76X0_PHY_H_ + +#define RF_G_BAND 0x0100 +#define RF_A_BAND 0x0200 +#define RF_A_BAND_LB 0x0400 +#define RF_A_BAND_MB 0x0800 +#define RF_A_BAND_HB 0x1000 +#define RF_A_BAND_11J 0x2000 + +#define RF_BW_20 1 +#define RF_BW_40 2 +#define RF_BW_10 4 +#define RF_BW_80 8 + +#define MT_RF(bank, reg) ((bank) << 16 | (reg)) +#define MT_RF_BANK(offset) ((offset) >> 16) +#define MT_RF_REG(offset) ((offset) & 0xff) + +#define MT_RF_VCO_BP_CLOSE_LOOP BIT(3) +#define MT_RF_VCO_BP_CLOSE_LOOP_MASK GENMASK(3, 0) +#define MT_RF_VCO_CAL_MASK GENMASK(2, 0) +#define MT_RF_START_TIME 0x3 +#define MT_RF_START_TIME_MASK GENMASK(2, 0) +#define MT_RF_SETTLE_TIME_MASK GENMASK(6, 4) + +#define MT_RF_PLL_DEN_MASK GENMASK(4, 0) +#define MT_RF_PLL_K_MASK GENMASK(4, 0) +#define MT_RF_SDM_RESET_MASK BIT(7) +#define MT_RF_SDM_MASH_PRBS_MASK GENMASK(6, 2) +#define MT_RF_SDM_BP_MASK BIT(1) +#define MT_RF_ISI_ISO_MASK GENMASK(7, 6) +#define MT_RF_PFD_DLY_MASK GENMASK(5, 4) +#define MT_RF_CLK_SEL_MASK GENMASK(3, 2) +#define MT_RF_XO_DIV_MASK GENMASK(1, 0) + +struct mt76x0_bbp_switch_item { + u16 bw_band; + struct mt76_reg_pair reg_pair; +}; + +struct mt76x0_rf_switch_item { + u32 rf_bank_reg; + u16 bw_band; + u8 value; +}; + +struct mt76x0_freq_item { + u8 channel; + u32 band; + u8 pllR37; + u8 pllR36; + u8 pllR35; + u8 pllR34; + u8 pllR33; + u8 pllR32_b7b5; + u8 pllR32_b4b0; /* PLL_DEN (Denomina - 8) */ + u8 pllR31_b7b5; + u8 pllR31_b4b0; /* PLL_K (Nominator *)*/ + u8 pllR30_b7; /* sdm_reset_n */ + u8 pllR30_b6b2; /* sdmmash_prbs,sin */ + u8 pllR30_b1; /* sdm_bp */ + u16 pll_n; /* R30<0>, R29<7:0> (hex) */ + u8 pllR28_b7b6; /* isi,iso */ + u8 pllR28_b5b4; /* pfd_dly */ + u8 pllR28_b3b2; /* clksel option */ + u32 pll_sdm_k; /* R28<1:0>, R27<7:0>, R26<7:0> (hex) SDM_k */ + u8 pllR24_b1b0; /* xo_div */ +}; + +struct mt76x0_rate_pwr_item { + s8 mcs_power; + u8 rf_pa_mode; +}; + +struct mt76x0_rate_pwr_tab { + struct mt76x0_rate_pwr_item cck[4]; + struct mt76x0_rate_pwr_item ofdm[8]; + struct mt76x0_rate_pwr_item ht[8]; + struct mt76x0_rate_pwr_item vht[10]; + struct mt76x0_rate_pwr_item stbc[8]; + struct mt76x0_rate_pwr_item mcs32; +}; + +#endif /* _MT76X0_PHY_H_ */ diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c b/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c new file mode 100644 index 000000000..2acce121f --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c @@ -0,0 +1,356 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2015 Jakub Kicinski + */ + +#include +#include +#include + +#include "mt76x0.h" +#include "mcu.h" +#include "../mt76x02_usb.h" + +static struct usb_device_id mt76x0_device_table[] = { + { USB_DEVICE(0x148F, 0x7610) }, /* MT7610U */ + { USB_DEVICE(0x13B1, 0x003E) }, /* Linksys AE6000 */ + { USB_DEVICE(0x0E8D, 0x7610) }, /* Sabrent NTWLAC */ + { USB_DEVICE(0x7392, 0xa711) }, /* Edimax 7711mac */ + { USB_DEVICE(0x7392, 0xb711) }, /* Edimax / Elecom */ + { USB_DEVICE(0x148f, 0x761a) }, /* TP-Link TL-WDN5200 */ + { USB_DEVICE(0x148f, 0x760a) }, /* TP-Link unknown */ + { USB_DEVICE(0x0b05, 0x17d1) }, /* Asus USB-AC51 */ + { USB_DEVICE(0x0b05, 0x17db) }, /* Asus USB-AC50 */ + { USB_DEVICE(0x0df6, 0x0075) }, /* Sitecom WLA-3100 */ + { USB_DEVICE(0x2019, 0xab31) }, /* Planex GW-450D */ + { USB_DEVICE(0x2001, 0x3d02) }, /* D-LINK DWA-171 rev B1 */ + { USB_DEVICE(0x0586, 0x3425) }, /* Zyxel NWD6505 */ + { USB_DEVICE(0x07b8, 0x7610) }, /* AboCom AU7212 */ + { USB_DEVICE(0x04bb, 0x0951) }, /* I-O DATA WN-AC433UK */ + { USB_DEVICE(0x057c, 0x8502) }, /* AVM FRITZ!WLAN USB Stick AC 430 */ + { USB_DEVICE(0x293c, 0x5702) }, /* Comcast Xfinity KXW02AAA */ + { USB_DEVICE(0x20f4, 0x806b) }, /* TRENDnet TEW-806UBH */ + { USB_DEVICE(0x7392, 0xc711) }, /* Devolo Wifi ac Stick */ + { USB_DEVICE(0x0df6, 0x0079) }, /* Sitecom Europe B.V. ac Stick */ + { USB_DEVICE(0x2357, 0x0123) }, /* TP-LINK T2UHP_US_v1 */ + { USB_DEVICE(0x2357, 0x010b) }, /* TP-LINK T2UHP_UN_v1 */ + /* TP-LINK Archer T1U */ + { USB_DEVICE(0x2357, 0x0105), .driver_info = 1, }, + /* MT7630U */ + { USB_DEVICE_AND_INTERFACE_INFO(0x0E8D, 0x7630, 0xff, 0x2, 0xff)}, + /* MT7650U */ + { USB_DEVICE_AND_INTERFACE_INFO(0x0E8D, 0x7650, 0xff, 0x2, 0xff)}, + { 0, } +}; + +static void mt76x0_init_usb_dma(struct mt76x02_dev *dev) +{ + u32 val; + + val = mt76_rr(dev, MT_USB_DMA_CFG); + + val |= MT_USB_DMA_CFG_RX_BULK_EN | + MT_USB_DMA_CFG_TX_BULK_EN; + + /* disable AGGR_BULK_RX in order to receive one + * frame in each rx urb and avoid copies + */ + val &= ~MT_USB_DMA_CFG_RX_BULK_AGG_EN; + mt76_wr(dev, MT_USB_DMA_CFG, val); + + val = mt76_rr(dev, MT_COM_REG0); + if (val & 1) + dev_dbg(dev->mt76.dev, "MCU not ready\n"); + + val = mt76_rr(dev, MT_USB_DMA_CFG); + + val |= MT_USB_DMA_CFG_RX_DROP_OR_PAD; + mt76_wr(dev, MT_USB_DMA_CFG, val); + val &= ~MT_USB_DMA_CFG_RX_DROP_OR_PAD; + mt76_wr(dev, MT_USB_DMA_CFG, val); +} + +static void mt76x0u_cleanup(struct mt76x02_dev *dev) +{ + clear_bit(MT76_STATE_INITIALIZED, &dev->mphy.state); + mt76x0_chip_onoff(dev, false, false); + mt76u_queues_deinit(&dev->mt76); +} + +static void mt76x0u_stop(struct ieee80211_hw *hw, bool suspend) +{ + struct mt76x02_dev *dev = hw->priv; + + clear_bit(MT76_STATE_RUNNING, &dev->mphy.state); + cancel_delayed_work_sync(&dev->cal_work); + cancel_delayed_work_sync(&dev->mphy.mac_work); + mt76u_stop_tx(&dev->mt76); + mt76x02u_exit_beacon_config(dev); + + if (test_bit(MT76_REMOVED, &dev->mphy.state)) + return; + + if (!mt76_poll(dev, MT_USB_DMA_CFG, MT_USB_DMA_CFG_TX_BUSY, 0, 1000)) + dev_warn(dev->mt76.dev, "TX DMA did not stop\n"); + + mt76x0_mac_stop(dev); + + if (!mt76_poll(dev, MT_USB_DMA_CFG, MT_USB_DMA_CFG_RX_BUSY, 0, 1000)) + dev_warn(dev->mt76.dev, "RX DMA did not stop\n"); +} + +static int mt76x0u_start(struct ieee80211_hw *hw) +{ + struct mt76x02_dev *dev = hw->priv; + int ret; + + ret = mt76x02u_mac_start(dev); + if (ret) + return ret; + + mt76x0_phy_calibrate(dev, true); + ieee80211_queue_delayed_work(dev->mt76.hw, &dev->mphy.mac_work, + MT_MAC_WORK_INTERVAL); + ieee80211_queue_delayed_work(dev->mt76.hw, &dev->cal_work, + MT_CALIBRATE_INTERVAL); + set_bit(MT76_STATE_RUNNING, &dev->mphy.state); + return 0; +} + +static const struct ieee80211_ops mt76x0u_ops = { + .add_chanctx = ieee80211_emulate_add_chanctx, + .remove_chanctx = ieee80211_emulate_remove_chanctx, + .change_chanctx = ieee80211_emulate_change_chanctx, + .switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx, + .tx = mt76x02_tx, + .start = mt76x0u_start, + .stop = mt76x0u_stop, + .add_interface = mt76x02_add_interface, + .remove_interface = mt76x02_remove_interface, + .config = mt76x0_config, + .configure_filter = mt76x02_configure_filter, + .bss_info_changed = mt76x02_bss_info_changed, + .sta_state = mt76_sta_state, + .sta_pre_rcu_remove = mt76_sta_pre_rcu_remove, + .set_key = mt76x02_set_key, + .conf_tx = mt76x02_conf_tx, + .sw_scan_start = mt76_sw_scan, + .sw_scan_complete = mt76x02_sw_scan_complete, + .ampdu_action = mt76x02_ampdu_action, + .sta_rate_tbl_update = mt76x02_sta_rate_tbl_update, + .set_rts_threshold = mt76x02_set_rts_threshold, + .wake_tx_queue = mt76_wake_tx_queue, + .get_txpower = mt76_get_txpower, + .get_survey = mt76_get_survey, + .set_tim = mt76_set_tim, + .release_buffered_frames = mt76_release_buffered_frames, + .get_antenna = mt76_get_antenna, + .set_sar_specs = mt76x0_set_sar_specs, +}; + +static int mt76x0u_init_hardware(struct mt76x02_dev *dev, bool reset) +{ + int err; + + mt76x0_chip_onoff(dev, true, reset); + + if (!mt76x02_wait_for_mac(&dev->mt76)) + return -ETIMEDOUT; + + err = mt76x0u_mcu_init(dev); + if (err < 0) + return err; + + mt76x0_init_usb_dma(dev); + err = mt76x0_init_hardware(dev); + if (err < 0) + return err; + + mt76x02u_init_beacon_config(dev); + + mt76_rmw(dev, MT_US_CYC_CFG, MT_US_CYC_CNT, 0x1e); + mt76_wr(dev, MT_TXOP_CTRL_CFG, + FIELD_PREP(MT_TXOP_TRUN_EN, 0x3f) | + FIELD_PREP(MT_TXOP_EXT_CCA_DLY, 0x58)); + + return 0; +} + +static int mt76x0u_register_device(struct mt76x02_dev *dev) +{ + struct ieee80211_hw *hw = dev->mt76.hw; + struct mt76_usb *usb = &dev->mt76.usb; + int err; + + usb->mcu.data = devm_kmalloc(dev->mt76.dev, MCU_RESP_URB_SIZE, + GFP_KERNEL); + if (!usb->mcu.data) + return -ENOMEM; + + err = mt76u_alloc_queues(&dev->mt76); + if (err < 0) + goto out_err; + + err = mt76x0u_init_hardware(dev, true); + if (err < 0) + goto out_err; + + /* check hw sg support in order to enable AMSDU */ + hw->max_tx_fragments = dev->mt76.usb.sg_en ? MT_TX_SG_MAX_SIZE : 1; + err = mt76x0_register_device(dev); + if (err < 0) + goto out_err; + + set_bit(MT76_STATE_INITIALIZED, &dev->mphy.state); + + return 0; + +out_err: + mt76x0u_cleanup(dev); + return err; +} + +static int mt76x0u_probe(struct usb_interface *usb_intf, + const struct usb_device_id *id) +{ + static const struct mt76_driver_ops drv_ops = { + .drv_flags = MT_DRV_SW_RX_AIRTIME | + MT_DRV_IGNORE_TXS_FAILED, + .survey_flags = SURVEY_INFO_TIME_TX, + .update_survey = mt76x02_update_channel, + .set_channel = mt76x0_set_channel, + .tx_prepare_skb = mt76x02u_tx_prepare_skb, + .tx_complete_skb = mt76x02u_tx_complete_skb, + .tx_status_data = mt76x02_tx_status_data, + .rx_skb = mt76x02_queue_rx_skb, + .sta_ps = mt76x02_sta_ps, + .sta_add = mt76x02_sta_add, + .sta_remove = mt76x02_sta_remove, + }; + struct usb_device *usb_dev = interface_to_usbdev(usb_intf); + struct mt76x02_dev *dev; + struct mt76_dev *mdev; + u32 mac_rev; + int ret; + + mdev = mt76_alloc_device(&usb_intf->dev, sizeof(*dev), &mt76x0u_ops, + &drv_ops); + if (!mdev) + return -ENOMEM; + + dev = container_of(mdev, struct mt76x02_dev, mt76); + mutex_init(&dev->phy_mutex); + + /* Quirk for Archer T1U */ + if (id->driver_info) + dev->no_2ghz = true; + + usb_reset_device(usb_dev); + + usb_set_intfdata(usb_intf, dev); + + mt76x02u_init_mcu(mdev); + ret = mt76u_init(mdev, usb_intf); + if (ret) + goto err; + + /* Disable the HW, otherwise MCU fail to initialize on hot reboot */ + mt76x0_chip_onoff(dev, false, false); + + if (!mt76x02_wait_for_mac(mdev)) { + ret = -ETIMEDOUT; + goto err; + } + + mdev->rev = mt76_rr(dev, MT_ASIC_VERSION); + mac_rev = mt76_rr(dev, MT_MAC_CSR0); + dev_info(mdev->dev, "ASIC revision: %08x MAC revision: %08x\n", + mdev->rev, mac_rev); + if (!is_mt76x0(dev)) { + ret = -ENODEV; + goto err; + } + + /* Note: vendor driver skips this check for MT76X0U */ + if (!(mt76_rr(dev, MT_EFUSE_CTRL) & MT_EFUSE_CTRL_SEL)) + dev_warn(mdev->dev, "Warning: eFUSE not present\n"); + + ret = mt76x0u_register_device(dev); + if (ret < 0) + goto err; + + return 0; + +err: + usb_set_intfdata(usb_intf, NULL); + mt76u_queues_deinit(&dev->mt76); + mt76_free_device(&dev->mt76); + + return ret; +} + +static void mt76x0_disconnect(struct usb_interface *usb_intf) +{ + struct mt76x02_dev *dev = usb_get_intfdata(usb_intf); + bool initialized = test_bit(MT76_STATE_INITIALIZED, &dev->mphy.state); + + if (!initialized) + return; + + ieee80211_unregister_hw(dev->mt76.hw); + mt76x0u_cleanup(dev); + + usb_set_intfdata(usb_intf, NULL); + + mt76_free_device(&dev->mt76); +} + +static int __maybe_unused mt76x0_suspend(struct usb_interface *usb_intf, + pm_message_t state) +{ + struct mt76x02_dev *dev = usb_get_intfdata(usb_intf); + + mt76u_stop_rx(&dev->mt76); + clear_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state); + mt76x0_chip_onoff(dev, false, false); + + return 0; +} + +static int __maybe_unused mt76x0_resume(struct usb_interface *usb_intf) +{ + struct mt76x02_dev *dev = usb_get_intfdata(usb_intf); + int ret; + + ret = mt76u_resume_rx(&dev->mt76); + if (ret < 0) + goto err; + + ret = mt76x0u_init_hardware(dev, false); + if (ret) + goto err; + + return 0; +err: + mt76x0u_cleanup(dev); + return ret; +} + +MODULE_DEVICE_TABLE(usb, mt76x0_device_table); +MODULE_FIRMWARE(MT7610E_FIRMWARE); +MODULE_FIRMWARE(MT7610U_FIRMWARE); +MODULE_DESCRIPTION("MediaTek MT76x0U (USB) wireless driver"); +MODULE_LICENSE("GPL"); + +static struct usb_driver mt76x0_driver = { + .name = KBUILD_MODNAME, + .id_table = mt76x0_device_table, + .probe = mt76x0u_probe, + .disconnect = mt76x0_disconnect, + .suspend = mt76x0_suspend, + .resume = mt76x0_resume, + .reset_resume = mt76x0_resume, + .soft_unbind = 1, + .disable_hub_initiated_lpm = 1, +}; +module_usb_driver(mt76x0_driver); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/usb_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76x0/usb_mcu.c new file mode 100644 index 000000000..20a8f3659 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/usb_mcu.c @@ -0,0 +1,175 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (C) 2018 Lorenzo Bianconi + */ +#include +#include +#include + +#include "mt76x0.h" +#include "mcu.h" +#include "../mt76x02_usb.h" + +#define MCU_FW_URB_MAX_PAYLOAD 0x38f8 +#define MCU_FW_URB_SIZE (MCU_FW_URB_MAX_PAYLOAD + 12) + +static int +mt76x0u_upload_firmware(struct mt76x02_dev *dev, + const struct mt76x02_fw_header *hdr) +{ + u8 *fw_payload = (u8 *)(hdr + 1); + u32 ilm_len, dlm_len; + void *ivb; + int err; + + ivb = kmemdup(fw_payload, MT_MCU_IVB_SIZE, GFP_KERNEL); + if (!ivb) + return -ENOMEM; + + ilm_len = le32_to_cpu(hdr->ilm_len) - MT_MCU_IVB_SIZE; + dev_dbg(dev->mt76.dev, "loading FW - ILM %u + IVB %u\n", + ilm_len, MT_MCU_IVB_SIZE); + err = mt76x02u_mcu_fw_send_data(dev, fw_payload + MT_MCU_IVB_SIZE, + ilm_len, MCU_FW_URB_MAX_PAYLOAD, + MT_MCU_IVB_SIZE); + if (err) + goto out; + + dlm_len = le32_to_cpu(hdr->dlm_len); + dev_dbg(dev->mt76.dev, "loading FW - DLM %u\n", dlm_len); + err = mt76x02u_mcu_fw_send_data(dev, + fw_payload + le32_to_cpu(hdr->ilm_len), + dlm_len, MCU_FW_URB_MAX_PAYLOAD, + MT_MCU_DLM_OFFSET); + if (err) + goto out; + + err = mt76u_vendor_request(&dev->mt76, MT_VEND_DEV_MODE, + USB_DIR_OUT | USB_TYPE_VENDOR, + 0x12, 0, ivb, MT_MCU_IVB_SIZE); + if (err < 0) + goto out; + + if (!mt76_poll_msec(dev, MT_MCU_COM_REG0, 1, 1, 1000)) { + dev_err(dev->mt76.dev, "Firmware failed to start\n"); + err = -ETIMEDOUT; + goto out; + } + + dev_dbg(dev->mt76.dev, "Firmware running!\n"); + +out: + kfree(ivb); + + return err; +} + +static int mt76x0_get_firmware(struct mt76x02_dev *dev, + const struct firmware **fw) +{ + int err; + + /* try to load mt7610e fw if available + * otherwise fall back to mt7610u one + */ + err = firmware_request_nowarn(fw, MT7610E_FIRMWARE, dev->mt76.dev); + if (err) { + dev_info(dev->mt76.dev, "%s not found, switching to %s", + MT7610E_FIRMWARE, MT7610U_FIRMWARE); + return request_firmware(fw, MT7610U_FIRMWARE, + dev->mt76.dev); + } + return 0; +} + +static int mt76x0u_load_firmware(struct mt76x02_dev *dev) +{ + const struct firmware *fw; + const struct mt76x02_fw_header *hdr; + int len, ret; + u32 val; + + mt76_wr(dev, MT_USB_DMA_CFG, (MT_USB_DMA_CFG_RX_BULK_EN | + MT_USB_DMA_CFG_TX_BULK_EN)); + + if (mt76x0_firmware_running(dev)) + return 0; + + ret = mt76x0_get_firmware(dev, &fw); + if (ret) + return ret; + + if (!fw || !fw->data || fw->size < sizeof(*hdr)) + goto err_inv_fw; + + hdr = (const struct mt76x02_fw_header *)fw->data; + + if (le32_to_cpu(hdr->ilm_len) <= MT_MCU_IVB_SIZE) + goto err_inv_fw; + + len = sizeof(*hdr); + len += le32_to_cpu(hdr->ilm_len); + len += le32_to_cpu(hdr->dlm_len); + + if (fw->size != len) + goto err_inv_fw; + + val = le16_to_cpu(hdr->fw_ver); + dev_dbg(dev->mt76.dev, + "Firmware Version: %d.%d.%02d Build: %x Build time: %.16s\n", + (val >> 12) & 0xf, (val >> 8) & 0xf, val & 0xf, + le16_to_cpu(hdr->build_ver), hdr->build_time); + + len = le32_to_cpu(hdr->ilm_len); + + mt76_wr(dev, 0x1004, 0x2c); + + mt76_set(dev, MT_USB_DMA_CFG, + (MT_USB_DMA_CFG_RX_BULK_EN | MT_USB_DMA_CFG_TX_BULK_EN) | + FIELD_PREP(MT_USB_DMA_CFG_RX_BULK_AGG_TOUT, 0x20)); + mt76x02u_mcu_fw_reset(dev); + usleep_range(5000, 6000); + + mt76_wr(dev, MT_FCE_PSE_CTRL, 1); + + /* FCE tx_fs_base_ptr */ + mt76_wr(dev, MT_TX_CPU_FROM_FCE_BASE_PTR, 0x400230); + /* FCE tx_fs_max_cnt */ + mt76_wr(dev, MT_TX_CPU_FROM_FCE_MAX_COUNT, 1); + /* FCE pdma enable */ + mt76_wr(dev, MT_FCE_PDMA_GLOBAL_CONF, 0x44); + /* FCE skip_fs_en */ + mt76_wr(dev, MT_FCE_SKIP_FS, 3); + + val = mt76_rr(dev, MT_USB_DMA_CFG); + val |= MT_USB_DMA_CFG_UDMA_TX_WL_DROP; + mt76_wr(dev, MT_USB_DMA_CFG, val); + val &= ~MT_USB_DMA_CFG_UDMA_TX_WL_DROP; + mt76_wr(dev, MT_USB_DMA_CFG, val); + + ret = mt76x0u_upload_firmware(dev, hdr); + mt76x02_set_ethtool_fwver(dev, hdr); + release_firmware(fw); + + mt76_wr(dev, MT_FCE_PSE_CTRL, 1); + + return ret; + +err_inv_fw: + dev_err(dev->mt76.dev, "Invalid firmware image\n"); + release_firmware(fw); + return -ENOENT; +} + +int mt76x0u_mcu_init(struct mt76x02_dev *dev) +{ + int ret; + + ret = mt76x0u_load_firmware(dev); + if (ret < 0) + return ret; + + set_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state); + + return 0; +} diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02.h b/drivers/net/wireless/mediatek/mt76/mt76x02.h new file mode 100644 index 000000000..3c98808cc --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x02.h @@ -0,0 +1,284 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (C) 2016 Felix Fietkau + * Copyright (C) 2018 Stanislaw Gruszka + */ + +#ifndef __MT76x02_H +#define __MT76x02_H + +#include + +#include "mt76.h" +#include "mt76x02_regs.h" +#include "mt76x02_mac.h" +#include "mt76x02_dfs.h" +#include "mt76x02_dma.h" + +#define MT76x02_TX_RING_SIZE 512 +#define MT76x02_PSD_RING_SIZE 128 +#define MT76x02_N_WCIDS 128 +#define MT_CALIBRATE_INTERVAL HZ +#define MT_MAC_WORK_INTERVAL (HZ / 10) + +#define MT_WATCHDOG_TIME (HZ / 10) +#define MT_TX_HANG_TH 10 + +#define MT_MAX_CHAINS 2 +struct mt76x02_rx_freq_cal { + s8 high_gain[MT_MAX_CHAINS]; + s8 rssi_offset[MT_MAX_CHAINS]; + s8 lna_gain; + u32 mcu_gain; + s16 temp_offset; + u8 freq_offset; +}; + +struct mt76x02_calibration { + struct mt76x02_rx_freq_cal rx; + + u8 agc_gain_init[MT_MAX_CHAINS]; + u8 agc_gain_cur[MT_MAX_CHAINS]; + + u16 false_cca; + s8 avg_rssi_all; + s8 agc_gain_adjust; + s8 agc_lowest_gain; + s8 low_gain; + + s8 temp_vco; + s8 temp; + + bool init_cal_done; + bool tssi_cal_done; + bool tssi_comp_pending; + bool dpd_cal_done; + bool channel_cal_done; + bool gain_init_done; + + int tssi_target; + s8 tssi_dc; +}; + +struct mt76x02_beacon_ops { + unsigned int nslots; + unsigned int slot_size; + void (*pre_tbtt_enable)(struct mt76x02_dev *dev, bool en); + void (*beacon_enable)(struct mt76x02_dev *dev, bool en); +}; + +#define mt76x02_beacon_enable(dev, enable) \ + (dev)->beacon_ops->beacon_enable(dev, enable) +#define mt76x02_pre_tbtt_enable(dev, enable) \ + (dev)->beacon_ops->pre_tbtt_enable(dev, enable) + +struct mt76x02_rate_power { + union { + struct { + s8 cck[4]; + s8 ofdm[8]; + s8 ht[16]; + s8 vht[2]; + }; + s8 all[30]; + }; +}; + +struct mt76x02_dev { + union { /* must be first */ + struct mt76_dev mt76; + struct mt76_phy mphy; + }; + + struct mac_address macaddr_list[8]; + + struct mutex phy_mutex; + + u8 txdone_seq; + DECLARE_KFIFO_PTR(txstatus_fifo, struct mt76x02_tx_status); + spinlock_t txstatus_fifo_lock; + u32 tx_airtime; + u32 ampdu_ref; + + struct sk_buff *rx_head; + + struct delayed_work cal_work; + struct delayed_work wdt_work; + + struct hrtimer pre_tbtt_timer; + struct work_struct pre_tbtt_work; + + const struct mt76x02_beacon_ops *beacon_ops; + + u8 beacon_data_count; + + u8 tbtt_count; + + u32 tx_hang_reset; + u8 tx_hang_check[4]; + u8 beacon_hang_check; + u8 mcu_timeout; + + struct mt76x02_rate_power rate_power; + + struct mt76x02_calibration cal; + + int txpower_conf; + s8 target_power; + s8 target_power_delta[2]; + bool enable_tpc; + + bool no_2ghz; + + s16 coverage_class; + u8 slottime; + + struct mt76x02_dfs_pattern_detector dfs_pd; + + /* edcca monitor */ + unsigned long ed_trigger_timeout; + bool ed_tx_blocked; + bool ed_monitor; + u8 ed_monitor_enabled; + u8 ed_monitor_learning; + u8 ed_trigger; + u8 ed_silent; + ktime_t ed_time; +}; + +extern struct ieee80211_rate mt76x02_rates[12]; + +int mt76x02_init_device(struct mt76x02_dev *dev); +void mt76x02_configure_filter(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *total_flags, u64 multicast); +int mt76x02_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); +void mt76x02_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); + +void mt76x02_config_mac_addr_list(struct mt76x02_dev *dev); + +int mt76x02_add_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); +void mt76x02_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); + +int mt76x02_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_ampdu_params *params); +int mt76x02_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key); +int mt76x02_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + unsigned int link_id, u16 queue, + const struct ieee80211_tx_queue_params *params); +void mt76x02_sta_rate_tbl_update(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta); +s8 mt76x02_tx_get_max_txpwr_adj(struct mt76x02_dev *dev, + const struct ieee80211_tx_rate *rate); +s8 mt76x02_tx_get_txpwr_adj(struct mt76x02_dev *dev, s8 txpwr, + s8 max_txpwr_adj); +void mt76x02_wdt_work(struct work_struct *work); +void mt76x02_tx_set_txpwr_auto(struct mt76x02_dev *dev, s8 txpwr); +void mt76x02_set_tx_ackto(struct mt76x02_dev *dev); +void mt76x02_set_coverage_class(struct ieee80211_hw *hw, + int radio_idx, s16 coverage_class); +int mt76x02_set_rts_threshold(struct ieee80211_hw *hw, int radio_idx, u32 val); +void mt76x02_remove_hdr_pad(struct sk_buff *skb, int len); +bool mt76x02_tx_status_data(struct mt76_dev *mdev, u8 *update); +void mt76x02_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, + struct sk_buff *skb, u32 *info); +void mt76x02_rx_poll_complete(struct mt76_dev *mdev, enum mt76_rxq_id q); +irqreturn_t mt76x02_irq_handler(int irq, void *dev_instance); +void mt76x02_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, + struct sk_buff *skb); +int mt76x02_tx_prepare_skb(struct mt76_dev *mdev, void *txwi, + enum mt76_txq_id qid, struct mt76_wcid *wcid, + struct ieee80211_sta *sta, + struct mt76_tx_info *tx_info); +void mt76x02_sw_scan_complete(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); +void mt76x02_sta_ps(struct mt76_dev *dev, struct ieee80211_sta *sta, bool ps); +void mt76x02_bss_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, u64 changed); +void mt76x02_reconfig_complete(struct ieee80211_hw *hw, + enum ieee80211_reconfig_type reconfig_type); + +struct beacon_bc_data { + struct mt76x02_dev *dev; + struct sk_buff_head q; + struct sk_buff *tail[8]; +}; + +void mt76x02_init_beacon_config(struct mt76x02_dev *dev); +void mt76x02e_init_beacon_config(struct mt76x02_dev *dev); +void mt76x02_resync_beacon_timer(struct mt76x02_dev *dev); +void mt76x02_update_beacon_iter(void *priv, u8 *mac, struct ieee80211_vif *vif); +void mt76x02_enqueue_buffered_bc(struct mt76x02_dev *dev, + struct beacon_bc_data *data, + int max_nframes); + +void mt76x02_mac_start(struct mt76x02_dev *dev); + +void mt76x02_init_debugfs(struct mt76x02_dev *dev); + +static inline bool is_mt76x0(struct mt76x02_dev *dev) +{ + return mt76_chip(&dev->mt76) == 0x7610 || + mt76_chip(&dev->mt76) == 0x7630 || + mt76_chip(&dev->mt76) == 0x7650; +} + +static inline bool is_mt76x2(struct mt76x02_dev *dev) +{ + return mt76_chip(&dev->mt76) == 0x7612 || + mt76_chip(&dev->mt76) == 0x7632 || + mt76_chip(&dev->mt76) == 0x7662 || + mt76_chip(&dev->mt76) == 0x7602; +} + +static inline void mt76x02_irq_enable(struct mt76x02_dev *dev, u32 mask) +{ + mt76_set_irq_mask(&dev->mt76, MT_INT_MASK_CSR, 0, mask); +} + +static inline void mt76x02_irq_disable(struct mt76x02_dev *dev, u32 mask) +{ + mt76_set_irq_mask(&dev->mt76, MT_INT_MASK_CSR, mask, 0); +} + +static inline bool +mt76x02_wait_for_txrx_idle(struct mt76_dev *dev) +{ + return __mt76_poll_msec(dev, MT_MAC_STATUS, + MT_MAC_STATUS_TX | MT_MAC_STATUS_RX, + 0, 100); +} + +static inline struct mt76x02_sta * +mt76x02_rx_get_sta(struct mt76_dev *dev, u8 idx) +{ + struct mt76_wcid *wcid; + + wcid = __mt76_wcid_ptr(dev, idx); + if (!wcid) + return NULL; + + return container_of(wcid, struct mt76x02_sta, wcid); +} + +static inline struct mt76_wcid * +mt76x02_rx_get_sta_wcid(struct mt76x02_sta *sta, bool unicast) +{ + if (!sta) + return NULL; + + if (unicast) + return &sta->wcid; + else + return &sta->vif->group_wcid; +} + +#endif /* __MT76x02_H */ diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_beacon.c b/drivers/net/wireless/mediatek/mt76/mt76x02_beacon.c new file mode 100644 index 000000000..7c9b16942 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_beacon.c @@ -0,0 +1,215 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (C) 2016 Felix Fietkau + * Copyright (C) 2018 Lorenzo Bianconi + * Copyright (C) 2018 Stanislaw Gruszka + */ + +#include "mt76x02.h" + +static void mt76x02_set_beacon_offsets(struct mt76x02_dev *dev) +{ + u32 regs[4] = {}; + u16 val; + int i; + + for (i = 0; i < dev->beacon_ops->nslots; i++) { + val = i * dev->beacon_ops->slot_size; + regs[i / 4] |= (val / 64) << (8 * (i % 4)); + } + + for (i = 0; i < 4; i++) + mt76_wr(dev, MT_BCN_OFFSET(i), regs[i]); +} + +static int +mt76x02_write_beacon(struct mt76x02_dev *dev, int offset, struct sk_buff *skb) +{ + int beacon_len = dev->beacon_ops->slot_size; + + if (WARN_ON_ONCE(beacon_len < skb->len + sizeof(struct mt76x02_txwi))) + return -ENOSPC; + + /* USB devices already reserve enough skb headroom for txwi's. This + * helps to save slow copies over USB. + */ + if (mt76_is_usb(&dev->mt76)) { + struct mt76x02_txwi *txwi; + + txwi = (struct mt76x02_txwi *)(skb->data - sizeof(*txwi)); + mt76x02_mac_write_txwi(dev, txwi, skb, NULL, NULL, skb->len); + skb_push(skb, sizeof(*txwi)); + } else { + struct mt76x02_txwi txwi; + + mt76x02_mac_write_txwi(dev, &txwi, skb, NULL, NULL, skb->len); + mt76_wr_copy(dev, offset, &txwi, sizeof(txwi)); + offset += sizeof(txwi); + } + + mt76_wr_copy(dev, offset, skb->data, skb->len); + return 0; +} + +void mt76x02_mac_set_beacon(struct mt76x02_dev *dev, + struct sk_buff *skb) +{ + int bcn_len = dev->beacon_ops->slot_size; + int bcn_addr = MT_BEACON_BASE + (bcn_len * dev->beacon_data_count); + + if (!mt76x02_write_beacon(dev, bcn_addr, skb)) { + if (!dev->beacon_data_count) + dev->beacon_hang_check++; + dev->beacon_data_count++; + } + dev_kfree_skb(skb); +} +EXPORT_SYMBOL_GPL(mt76x02_mac_set_beacon); + +void mt76x02_mac_set_beacon_enable(struct mt76x02_dev *dev, + struct ieee80211_vif *vif, bool enable) +{ + struct mt76x02_vif *mvif = (struct mt76x02_vif *)vif->drv_priv; + u8 old_mask = dev->mt76.beacon_mask; + + mt76x02_pre_tbtt_enable(dev, false); + + if (!dev->mt76.beacon_mask) + dev->tbtt_count = 0; + + dev->beacon_hang_check = 0; + if (enable) { + dev->mt76.beacon_mask |= BIT(mvif->idx); + } else { + dev->mt76.beacon_mask &= ~BIT(mvif->idx); + } + + if (!!old_mask == !!dev->mt76.beacon_mask) + goto out; + + if (dev->mt76.beacon_mask) + mt76_set(dev, MT_BEACON_TIME_CFG, + MT_BEACON_TIME_CFG_BEACON_TX | + MT_BEACON_TIME_CFG_TBTT_EN | + MT_BEACON_TIME_CFG_TIMER_EN); + else + mt76_clear(dev, MT_BEACON_TIME_CFG, + MT_BEACON_TIME_CFG_BEACON_TX | + MT_BEACON_TIME_CFG_TBTT_EN | + MT_BEACON_TIME_CFG_TIMER_EN); + mt76x02_beacon_enable(dev, !!dev->mt76.beacon_mask); + +out: + mt76x02_pre_tbtt_enable(dev, true); +} + +void +mt76x02_resync_beacon_timer(struct mt76x02_dev *dev) +{ + u32 timer_val = dev->mt76.beacon_int << 4; + + dev->tbtt_count++; + + /* + * Beacon timer drifts by 1us every tick, the timer is configured + * in 1/16 TU (64us) units. + */ + if (dev->tbtt_count < 63) + return; + + /* + * The updated beacon interval takes effect after two TBTT, because + * at this point the original interval has already been loaded into + * the next TBTT_TIMER value + */ + if (dev->tbtt_count == 63) + timer_val -= 1; + + mt76_rmw_field(dev, MT_BEACON_TIME_CFG, + MT_BEACON_TIME_CFG_INTVAL, timer_val); + + if (dev->tbtt_count >= 64) + dev->tbtt_count = 0; +} +EXPORT_SYMBOL_GPL(mt76x02_resync_beacon_timer); + +void +mt76x02_update_beacon_iter(void *priv, u8 *mac, struct ieee80211_vif *vif) +{ + struct beacon_bc_data *data = priv; + struct mt76x02_dev *dev = data->dev; + struct mt76x02_vif *mvif = (struct mt76x02_vif *)vif->drv_priv; + struct sk_buff *skb = NULL; + + if (!(dev->mt76.beacon_mask & BIT(mvif->idx))) + return; + + skb = ieee80211_beacon_get(mt76_hw(dev), vif, 0); + if (!skb) + return; + + __skb_queue_tail(&data->q, skb); +} +EXPORT_SYMBOL_GPL(mt76x02_update_beacon_iter); + +static void +mt76x02_add_buffered_bc(void *priv, u8 *mac, struct ieee80211_vif *vif) +{ + struct beacon_bc_data *data = priv; + struct mt76x02_dev *dev = data->dev; + struct mt76x02_vif *mvif = (struct mt76x02_vif *)vif->drv_priv; + struct ieee80211_tx_info *info; + struct sk_buff *skb; + + if (!(dev->mt76.beacon_mask & BIT(mvif->idx))) + return; + + skb = ieee80211_get_buffered_bc(mt76_hw(dev), vif); + if (!skb) + return; + + info = IEEE80211_SKB_CB(skb); + info->control.vif = vif; + info->flags |= IEEE80211_TX_CTL_ASSIGN_SEQ; + mt76_skb_set_moredata(skb, true); + __skb_queue_tail(&data->q, skb); + data->tail[mvif->idx] = skb; +} + +void +mt76x02_enqueue_buffered_bc(struct mt76x02_dev *dev, + struct beacon_bc_data *data, + int max_nframes) +{ + int i, nframes; + + do { + nframes = skb_queue_len(&data->q); + ieee80211_iterate_active_interfaces_atomic(mt76_hw(dev), + IEEE80211_IFACE_ITER_RESUME_ALL, + mt76x02_add_buffered_bc, data); + } while (nframes != skb_queue_len(&data->q) && + skb_queue_len(&data->q) < max_nframes); + + if (!skb_queue_len(&data->q)) + return; + + for (i = 0; i < ARRAY_SIZE(data->tail); i++) { + if (!data->tail[i]) + continue; + mt76_skb_set_moredata(data->tail[i], false); + } +} +EXPORT_SYMBOL_GPL(mt76x02_enqueue_buffered_bc); + +void mt76x02_init_beacon_config(struct mt76x02_dev *dev) +{ + mt76_clear(dev, MT_BEACON_TIME_CFG, (MT_BEACON_TIME_CFG_TIMER_EN | + MT_BEACON_TIME_CFG_TBTT_EN | + MT_BEACON_TIME_CFG_BEACON_TX)); + mt76_set(dev, MT_BEACON_TIME_CFG, MT_BEACON_TIME_CFG_SYNC_MODE); + mt76_wr(dev, MT_BCN_BYPASS_MASK, 0xffff); + mt76x02_set_beacon_offsets(dev); +} +EXPORT_SYMBOL_GPL(mt76x02_init_beacon_config); + diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_debugfs.c b/drivers/net/wireless/mediatek/mt76/mt76x02_debugfs.c new file mode 100644 index 000000000..d81f696b3 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_debugfs.c @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (C) 2016 Felix Fietkau + */ + +#include +#include "mt76x02.h" + +static int +mt76x02_ampdu_stat_show(struct seq_file *file, void *data) +{ + struct mt76x02_dev *dev = file->private; + int i, j; + + for (i = 0; i < 4; i++) { + seq_puts(file, "Length: "); + for (j = 0; j < 8; j++) + seq_printf(file, "%8d | ", i * 8 + j + 1); + seq_puts(file, "\n"); + seq_puts(file, "Count: "); + for (j = 0; j < 8; j++) + seq_printf(file, "%8d | ", + dev->mphy.aggr_stats[i * 8 + j]); + seq_puts(file, "\n"); + seq_puts(file, "--------"); + for (j = 0; j < 8; j++) + seq_puts(file, "-----------"); + seq_puts(file, "\n"); + } + + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(mt76x02_ampdu_stat); + +static int read_txpower(struct seq_file *file, void *data) +{ + struct mt76x02_dev *dev = dev_get_drvdata(file->private); + + seq_printf(file, "Target power: %d\n", dev->target_power); + + mt76_seq_puts_array(file, "Delta", dev->target_power_delta, + ARRAY_SIZE(dev->target_power_delta)); + return 0; +} + +static int +mt76x02_dfs_stat_show(struct seq_file *file, void *data) +{ + struct mt76x02_dev *dev = file->private; + struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd; + int i; + + seq_printf(file, "allocated sequences:\t%d\n", + dfs_pd->seq_stats.seq_pool_len); + seq_printf(file, "used sequences:\t\t%d\n", + dfs_pd->seq_stats.seq_len); + seq_puts(file, "\n"); + + for (i = 0; i < MT_DFS_NUM_ENGINES; i++) { + seq_printf(file, "engine: %d\n", i); + seq_printf(file, " hw pattern detected:\t%d\n", + dfs_pd->stats[i].hw_pattern); + seq_printf(file, " hw pulse discarded:\t%d\n", + dfs_pd->stats[i].hw_pulse_discarded); + seq_printf(file, " sw pattern detected:\t%d\n", + dfs_pd->stats[i].sw_pattern); + } + + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(mt76x02_dfs_stat); + +static int read_agc(struct seq_file *file, void *data) +{ + struct mt76x02_dev *dev = dev_get_drvdata(file->private); + + seq_printf(file, "avg_rssi: %d\n", dev->cal.avg_rssi_all); + seq_printf(file, "low_gain: %d\n", dev->cal.low_gain); + seq_printf(file, "false_cca: %d\n", dev->cal.false_cca); + seq_printf(file, "agc_gain_adjust: %d\n", dev->cal.agc_gain_adjust); + + return 0; +} + +static int +mt76_edcca_set(void *data, u64 val) +{ + struct mt76x02_dev *dev = data; + enum nl80211_dfs_regions region = dev->mt76.region; + + mutex_lock(&dev->mt76.mutex); + + dev->ed_monitor_enabled = !!val; + dev->ed_monitor = dev->ed_monitor_enabled && + region == NL80211_DFS_ETSI; + mt76x02_edcca_init(dev); + + mutex_unlock(&dev->mt76.mutex); + + return 0; +} + +static int +mt76_edcca_get(void *data, u64 *val) +{ + struct mt76x02_dev *dev = data; + + *val = dev->ed_monitor_enabled; + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_edcca, mt76_edcca_get, mt76_edcca_set, + "%lld\n"); + +static int mt76x02_read_rate_txpower(struct seq_file *s, void *data) +{ + struct mt76x02_dev *dev = dev_get_drvdata(s->private); + + mt76_seq_puts_array(s, "CCK", dev->rate_power.cck, + ARRAY_SIZE(dev->rate_power.cck)); + mt76_seq_puts_array(s, "OFDM", dev->rate_power.ofdm, + ARRAY_SIZE(dev->rate_power.ofdm)); + mt76_seq_puts_array(s, "HT", dev->rate_power.ht, + ARRAY_SIZE(dev->rate_power.ht)); + mt76_seq_puts_array(s, "VHT", dev->rate_power.vht, + ARRAY_SIZE(dev->rate_power.vht)); + return 0; +} + +void mt76x02_init_debugfs(struct mt76x02_dev *dev) +{ + struct dentry *dir; + + dir = mt76_register_debugfs(&dev->mt76); + if (!dir) + return; + + debugfs_create_devm_seqfile(dev->mt76.dev, "xmit-queues", dir, + mt76_queues_read); + debugfs_create_u8("temperature", 0400, dir, &dev->cal.temp); + debugfs_create_bool("tpc", 0600, dir, &dev->enable_tpc); + + debugfs_create_file("edcca", 0600, dir, dev, &fops_edcca); + debugfs_create_file("ampdu_stat", 0400, dir, dev, &mt76x02_ampdu_stat_fops); + debugfs_create_file("dfs_stats", 0400, dir, dev, &mt76x02_dfs_stat_fops); + debugfs_create_devm_seqfile(dev->mt76.dev, "txpower", dir, + read_txpower); + + debugfs_create_devm_seqfile(dev->mt76.dev, "rate_txpower", dir, + mt76x02_read_rate_txpower); + debugfs_create_devm_seqfile(dev->mt76.dev, "agc", dir, read_agc); + + debugfs_create_u32("tx_hang_reset", 0400, dir, &dev->tx_hang_reset); +} +EXPORT_SYMBOL_GPL(mt76x02_init_debugfs); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_dfs.c b/drivers/net/wireless/mediatek/mt76/mt76x02_dfs.c new file mode 100644 index 000000000..21c99ad7e --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_dfs.c @@ -0,0 +1,892 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (C) 2016 Lorenzo Bianconi + */ + +#include "mt76x02.h" + +#define RADAR_SPEC(m, len, el, eh, wl, wh, \ + w_tolerance, tl, th, t_tolerance, \ + bl, bh, event_exp, power_jmp) \ +{ \ + .mode = m, \ + .avg_len = len, \ + .e_low = el, \ + .e_high = eh, \ + .w_low = wl, \ + .w_high = wh, \ + .w_margin = w_tolerance, \ + .t_low = tl, \ + .t_high = th, \ + .t_margin = t_tolerance, \ + .b_low = bl, \ + .b_high = bh, \ + .event_expiration = event_exp, \ + .pwr_jmp = power_jmp \ +} + +static const struct mt76x02_radar_specs etsi_radar_specs[] = { + /* 20MHz */ + RADAR_SPEC(0, 8, 2, 15, 106, 150, 10, 4900, 100096, 10, 0, + 0x7fffffff, 0x155cc0, 0x19cc), + RADAR_SPEC(0, 40, 4, 59, 96, 380, 150, 4900, 100096, 40, 0, + 0x7fffffff, 0x155cc0, 0x19cc), + RADAR_SPEC(3, 60, 20, 46, 300, 640, 80, 4900, 10100, 80, 0, + 0x7fffffff, 0x155cc0, 0x19dd), + RADAR_SPEC(8, 8, 2, 9, 106, 150, 32, 4900, 296704, 32, 0, + 0x7fffffff, 0x2191c0, 0x15cc), + /* 40MHz */ + RADAR_SPEC(0, 8, 2, 15, 106, 150, 10, 4900, 100096, 10, 0, + 0x7fffffff, 0x155cc0, 0x19cc), + RADAR_SPEC(0, 40, 4, 59, 96, 380, 150, 4900, 100096, 40, 0, + 0x7fffffff, 0x155cc0, 0x19cc), + RADAR_SPEC(3, 60, 20, 46, 300, 640, 80, 4900, 10100, 80, 0, + 0x7fffffff, 0x155cc0, 0x19dd), + RADAR_SPEC(8, 8, 2, 9, 106, 150, 32, 4900, 296704, 32, 0, + 0x7fffffff, 0x2191c0, 0x15cc), + /* 80MHz */ + RADAR_SPEC(0, 8, 2, 15, 106, 150, 10, 4900, 100096, 10, 0, + 0x7fffffff, 0x155cc0, 0x19cc), + RADAR_SPEC(0, 40, 4, 59, 96, 380, 150, 4900, 100096, 40, 0, + 0x7fffffff, 0x155cc0, 0x19cc), + RADAR_SPEC(3, 60, 20, 46, 300, 640, 80, 4900, 10100, 80, 0, + 0x7fffffff, 0x155cc0, 0x19dd), + RADAR_SPEC(8, 8, 2, 9, 106, 150, 32, 4900, 296704, 32, 0, + 0x7fffffff, 0x2191c0, 0x15cc) +}; + +static const struct mt76x02_radar_specs fcc_radar_specs[] = { + /* 20MHz */ + RADAR_SPEC(0, 8, 2, 12, 106, 150, 5, 2900, 80100, 5, 0, + 0x7fffffff, 0xfe808, 0x13dc), + RADAR_SPEC(0, 8, 2, 7, 106, 140, 5, 27600, 27900, 5, 0, + 0x7fffffff, 0xfe808, 0x19dd), + RADAR_SPEC(0, 40, 4, 54, 96, 480, 150, 2900, 80100, 40, 0, + 0x7fffffff, 0xfe808, 0x12cc), + RADAR_SPEC(2, 60, 15, 63, 640, 2080, 32, 19600, 40200, 32, 0, + 0x3938700, 0x57bcf00, 0x1289), + /* 40MHz */ + RADAR_SPEC(0, 8, 2, 12, 106, 150, 5, 2900, 80100, 5, 0, + 0x7fffffff, 0xfe808, 0x13dc), + RADAR_SPEC(0, 8, 2, 7, 106, 140, 5, 27600, 27900, 5, 0, + 0x7fffffff, 0xfe808, 0x19dd), + RADAR_SPEC(0, 40, 4, 54, 96, 480, 150, 2900, 80100, 40, 0, + 0x7fffffff, 0xfe808, 0x12cc), + RADAR_SPEC(2, 60, 15, 63, 640, 2080, 32, 19600, 40200, 32, 0, + 0x3938700, 0x57bcf00, 0x1289), + /* 80MHz */ + RADAR_SPEC(0, 8, 2, 14, 106, 150, 15, 2900, 80100, 15, 0, + 0x7fffffff, 0xfe808, 0x16cc), + RADAR_SPEC(0, 8, 2, 7, 106, 140, 5, 27600, 27900, 5, 0, + 0x7fffffff, 0xfe808, 0x19dd), + RADAR_SPEC(0, 40, 4, 54, 96, 480, 150, 2900, 80100, 40, 0, + 0x7fffffff, 0xfe808, 0x12cc), + RADAR_SPEC(2, 60, 15, 63, 640, 2080, 32, 19600, 40200, 32, 0, + 0x3938700, 0x57bcf00, 0x1289) +}; + +static const struct mt76x02_radar_specs jp_w56_radar_specs[] = { + /* 20MHz */ + RADAR_SPEC(0, 8, 2, 7, 106, 150, 5, 2900, 80100, 5, 0, + 0x7fffffff, 0x14c080, 0x13dc), + RADAR_SPEC(0, 8, 2, 7, 106, 140, 5, 27600, 27900, 5, 0, + 0x7fffffff, 0x14c080, 0x19dd), + RADAR_SPEC(0, 40, 4, 44, 96, 480, 150, 2900, 80100, 40, 0, + 0x7fffffff, 0x14c080, 0x12cc), + RADAR_SPEC(2, 60, 15, 48, 940, 2080, 32, 19600, 40200, 32, 0, + 0x3938700, 0X57bcf00, 0x1289), + /* 40MHz */ + RADAR_SPEC(0, 8, 2, 7, 106, 150, 5, 2900, 80100, 5, 0, + 0x7fffffff, 0x14c080, 0x13dc), + RADAR_SPEC(0, 8, 2, 7, 106, 140, 5, 27600, 27900, 5, 0, + 0x7fffffff, 0x14c080, 0x19dd), + RADAR_SPEC(0, 40, 4, 44, 96, 480, 150, 2900, 80100, 40, 0, + 0x7fffffff, 0x14c080, 0x12cc), + RADAR_SPEC(2, 60, 15, 48, 940, 2080, 32, 19600, 40200, 32, 0, + 0x3938700, 0X57bcf00, 0x1289), + /* 80MHz */ + RADAR_SPEC(0, 8, 2, 9, 106, 150, 15, 2900, 80100, 15, 0, + 0x7fffffff, 0x14c080, 0x16cc), + RADAR_SPEC(0, 8, 2, 7, 106, 140, 5, 27600, 27900, 5, 0, + 0x7fffffff, 0x14c080, 0x19dd), + RADAR_SPEC(0, 40, 4, 44, 96, 480, 150, 2900, 80100, 40, 0, + 0x7fffffff, 0x14c080, 0x12cc), + RADAR_SPEC(2, 60, 15, 48, 940, 2080, 32, 19600, 40200, 32, 0, + 0x3938700, 0X57bcf00, 0x1289) +}; + +static const struct mt76x02_radar_specs jp_w53_radar_specs[] = { + /* 20MHz */ + RADAR_SPEC(0, 8, 2, 9, 106, 150, 20, 28400, 77000, 20, 0, + 0x7fffffff, 0x14c080, 0x16cc), + { 0 }, + RADAR_SPEC(0, 40, 4, 44, 96, 200, 150, 28400, 77000, 60, 0, + 0x7fffffff, 0x14c080, 0x16cc), + { 0 }, + /* 40MHz */ + RADAR_SPEC(0, 8, 2, 9, 106, 150, 20, 28400, 77000, 20, 0, + 0x7fffffff, 0x14c080, 0x16cc), + { 0 }, + RADAR_SPEC(0, 40, 4, 44, 96, 200, 150, 28400, 77000, 60, 0, + 0x7fffffff, 0x14c080, 0x16cc), + { 0 }, + /* 80MHz */ + RADAR_SPEC(0, 8, 2, 9, 106, 150, 20, 28400, 77000, 20, 0, + 0x7fffffff, 0x14c080, 0x16cc), + { 0 }, + RADAR_SPEC(0, 40, 4, 44, 96, 200, 150, 28400, 77000, 60, 0, + 0x7fffffff, 0x14c080, 0x16cc), + { 0 } +}; + +static void +mt76x02_dfs_set_capture_mode_ctrl(struct mt76x02_dev *dev, u8 enable) +{ + u32 data; + + data = (1 << 1) | enable; + mt76_wr(dev, MT_BBP(DFS, 36), data); +} + +static void mt76x02_dfs_seq_pool_put(struct mt76x02_dev *dev, + struct mt76x02_dfs_sequence *seq) +{ + struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd; + + list_add(&seq->head, &dfs_pd->seq_pool); + + dfs_pd->seq_stats.seq_pool_len++; + dfs_pd->seq_stats.seq_len--; +} + +static struct mt76x02_dfs_sequence * +mt76x02_dfs_seq_pool_get(struct mt76x02_dev *dev) +{ + struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd; + struct mt76x02_dfs_sequence *seq; + + if (list_empty(&dfs_pd->seq_pool)) { + seq = devm_kzalloc(dev->mt76.dev, sizeof(*seq), GFP_ATOMIC); + } else { + seq = list_first_entry(&dfs_pd->seq_pool, + struct mt76x02_dfs_sequence, + head); + list_del(&seq->head); + dfs_pd->seq_stats.seq_pool_len--; + } + if (seq) + dfs_pd->seq_stats.seq_len++; + + return seq; +} + +static int mt76x02_dfs_get_multiple(int val, int frac, int margin) +{ + int remainder, factor; + + if (!frac) + return 0; + + if (abs(val - frac) <= margin) + return 1; + + factor = val / frac; + remainder = val % frac; + + if (remainder > margin) { + if ((frac - remainder) <= margin) + factor++; + else + factor = 0; + } + return factor; +} + +static void mt76x02_dfs_detector_reset(struct mt76x02_dev *dev) +{ + struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd; + struct mt76x02_dfs_sequence *seq, *tmp_seq; + int i; + + /* reset hw detector */ + mt76_wr(dev, MT_BBP(DFS, 1), 0xf); + + /* reset sw detector */ + for (i = 0; i < ARRAY_SIZE(dfs_pd->event_rb); i++) { + dfs_pd->event_rb[i].h_rb = 0; + dfs_pd->event_rb[i].t_rb = 0; + } + + list_for_each_entry_safe(seq, tmp_seq, &dfs_pd->sequences, head) { + list_del_init(&seq->head); + mt76x02_dfs_seq_pool_put(dev, seq); + } +} + +static bool mt76x02_dfs_check_chirp(struct mt76x02_dev *dev) +{ + bool ret = false; + u32 current_ts, delta_ts; + struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd; + + current_ts = mt76_rr(dev, MT_PBF_LIFE_TIMER); + delta_ts = current_ts - dfs_pd->chirp_pulse_ts; + dfs_pd->chirp_pulse_ts = current_ts; + + /* 12 sec */ + if (delta_ts <= (12 * (1 << 20))) { + if (++dfs_pd->chirp_pulse_cnt > 8) + ret = true; + } else { + dfs_pd->chirp_pulse_cnt = 1; + } + + return ret; +} + +static void mt76x02_dfs_get_hw_pulse(struct mt76x02_dev *dev, + struct mt76x02_dfs_hw_pulse *pulse) +{ + u32 data; + + /* select channel */ + data = (MT_DFS_CH_EN << 16) | pulse->engine; + mt76_wr(dev, MT_BBP(DFS, 0), data); + + /* reported period */ + pulse->period = mt76_rr(dev, MT_BBP(DFS, 19)); + + /* reported width */ + pulse->w1 = mt76_rr(dev, MT_BBP(DFS, 20)); + pulse->w2 = mt76_rr(dev, MT_BBP(DFS, 23)); + + /* reported burst number */ + pulse->burst = mt76_rr(dev, MT_BBP(DFS, 22)); +} + +static bool mt76x02_dfs_check_hw_pulse(struct mt76x02_dev *dev, + struct mt76x02_dfs_hw_pulse *pulse) +{ + bool ret = false; + + if (!pulse->period || !pulse->w1) + return false; + + switch (dev->mt76.region) { + case NL80211_DFS_FCC: + if (pulse->engine > 3) + break; + + if (pulse->engine == 3) { + ret = mt76x02_dfs_check_chirp(dev); + break; + } + + /* check short pulse*/ + if (pulse->w1 < 120) + ret = (pulse->period >= 2900 && + (pulse->period <= 4700 || + pulse->period >= 6400) && + (pulse->period <= 6800 || + pulse->period >= 10200) && + pulse->period <= 61600); + else if (pulse->w1 < 130) /* 120 - 130 */ + ret = (pulse->period >= 2900 && + pulse->period <= 61600); + else + ret = (pulse->period >= 3500 && + pulse->period <= 10100); + break; + case NL80211_DFS_ETSI: + if (pulse->engine >= 3) + break; + + ret = (pulse->period >= 4900 && + (pulse->period <= 10200 || + pulse->period >= 12400) && + pulse->period <= 100100); + break; + case NL80211_DFS_JP: + if (dev->mphy.chandef.chan->center_freq >= 5250 && + dev->mphy.chandef.chan->center_freq <= 5350) { + /* JPW53 */ + if (pulse->w1 <= 130) + ret = (pulse->period >= 28360 && + (pulse->period <= 28700 || + pulse->period >= 76900) && + pulse->period <= 76940); + break; + } + + if (pulse->engine > 3) + break; + + if (pulse->engine == 3) { + ret = mt76x02_dfs_check_chirp(dev); + break; + } + + /* check short pulse*/ + if (pulse->w1 < 120) + ret = (pulse->period >= 2900 && + (pulse->period <= 4700 || + pulse->period >= 6400) && + (pulse->period <= 6800 || + pulse->period >= 27560) && + (pulse->period <= 27960 || + pulse->period >= 28360) && + (pulse->period <= 28700 || + pulse->period >= 79900) && + pulse->period <= 80100); + else if (pulse->w1 < 130) /* 120 - 130 */ + ret = (pulse->period >= 2900 && + (pulse->period <= 10100 || + pulse->period >= 27560) && + (pulse->period <= 27960 || + pulse->period >= 28360) && + (pulse->period <= 28700 || + pulse->period >= 79900) && + pulse->period <= 80100); + else + ret = (pulse->period >= 3900 && + pulse->period <= 10100); + break; + case NL80211_DFS_UNSET: + default: + return false; + } + + return ret; +} + +static bool mt76x02_dfs_fetch_event(struct mt76x02_dev *dev, + struct mt76x02_dfs_event *event) +{ + u32 data; + + /* 1st: DFS_R37[31]: 0 (engine 0) - 1 (engine 2) + * 2nd: DFS_R37[21:0]: pulse time + * 3rd: DFS_R37[11:0]: pulse width + * 3rd: DFS_R37[25:16]: phase + * 4th: DFS_R37[12:0]: current pwr + * 4th: DFS_R37[21:16]: pwr stable counter + * + * 1st: DFS_R37[31:0] set to 0xffffffff means no event detected + */ + data = mt76_rr(dev, MT_BBP(DFS, 37)); + if (!MT_DFS_CHECK_EVENT(data)) + return false; + + event->engine = MT_DFS_EVENT_ENGINE(data); + data = mt76_rr(dev, MT_BBP(DFS, 37)); + event->ts = MT_DFS_EVENT_TIMESTAMP(data); + data = mt76_rr(dev, MT_BBP(DFS, 37)); + event->width = MT_DFS_EVENT_WIDTH(data); + + return true; +} + +static bool mt76x02_dfs_check_event(struct mt76x02_dev *dev, + struct mt76x02_dfs_event *event) +{ + if (event->engine == 2) { + struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd; + struct mt76x02_dfs_event_rb *event_buff = &dfs_pd->event_rb[1]; + u16 last_event_idx; + u32 delta_ts; + + last_event_idx = mt76_decr(event_buff->t_rb, + MT_DFS_EVENT_BUFLEN); + delta_ts = event->ts - event_buff->data[last_event_idx].ts; + if (delta_ts < MT_DFS_EVENT_TIME_MARGIN && + event_buff->data[last_event_idx].width >= 200) + return false; + } + return true; +} + +static void mt76x02_dfs_queue_event(struct mt76x02_dev *dev, + struct mt76x02_dfs_event *event) +{ + struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd; + struct mt76x02_dfs_event_rb *event_buff; + + /* add radar event to ring buffer */ + event_buff = event->engine == 2 ? &dfs_pd->event_rb[1] + : &dfs_pd->event_rb[0]; + event_buff->data[event_buff->t_rb] = *event; + event_buff->data[event_buff->t_rb].fetch_ts = jiffies; + + event_buff->t_rb = mt76_incr(event_buff->t_rb, MT_DFS_EVENT_BUFLEN); + if (event_buff->t_rb == event_buff->h_rb) + event_buff->h_rb = mt76_incr(event_buff->h_rb, + MT_DFS_EVENT_BUFLEN); +} + +static int mt76x02_dfs_create_sequence(struct mt76x02_dev *dev, + struct mt76x02_dfs_event *event, + u16 cur_len) +{ + struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd; + struct mt76x02_dfs_sw_detector_params *sw_params; + u32 width_delta, with_sum; + struct mt76x02_dfs_sequence seq, *seq_p; + struct mt76x02_dfs_event_rb *event_rb; + struct mt76x02_dfs_event *cur_event; + int i, j, end, pri, factor, cur_pri; + + event_rb = event->engine == 2 ? &dfs_pd->event_rb[1] + : &dfs_pd->event_rb[0]; + + i = mt76_decr(event_rb->t_rb, MT_DFS_EVENT_BUFLEN); + end = mt76_decr(event_rb->h_rb, MT_DFS_EVENT_BUFLEN); + + while (i != end) { + cur_event = &event_rb->data[i]; + with_sum = event->width + cur_event->width; + + sw_params = &dfs_pd->sw_dpd_params; + switch (dev->mt76.region) { + case NL80211_DFS_FCC: + case NL80211_DFS_JP: + if (with_sum < 600) + width_delta = 8; + else + width_delta = with_sum >> 3; + break; + case NL80211_DFS_ETSI: + if (event->engine == 2) + width_delta = with_sum >> 6; + else if (with_sum < 620) + width_delta = 24; + else + width_delta = 8; + break; + case NL80211_DFS_UNSET: + default: + return -EINVAL; + } + + pri = event->ts - cur_event->ts; + if (abs(event->width - cur_event->width) > width_delta || + pri < sw_params->min_pri) + goto next; + + if (pri > sw_params->max_pri) + break; + + seq.pri = event->ts - cur_event->ts; + seq.first_ts = cur_event->ts; + seq.last_ts = event->ts; + seq.engine = event->engine; + seq.count = 2; + + j = mt76_decr(i, MT_DFS_EVENT_BUFLEN); + while (j != end) { + cur_event = &event_rb->data[j]; + cur_pri = event->ts - cur_event->ts; + factor = mt76x02_dfs_get_multiple(cur_pri, seq.pri, + sw_params->pri_margin); + if (factor > 0) { + seq.first_ts = cur_event->ts; + seq.count++; + } + + j = mt76_decr(j, MT_DFS_EVENT_BUFLEN); + } + if (seq.count <= cur_len) + goto next; + + seq_p = mt76x02_dfs_seq_pool_get(dev); + if (!seq_p) + return -ENOMEM; + + *seq_p = seq; + INIT_LIST_HEAD(&seq_p->head); + list_add(&seq_p->head, &dfs_pd->sequences); +next: + i = mt76_decr(i, MT_DFS_EVENT_BUFLEN); + } + return 0; +} + +static u16 mt76x02_dfs_add_event_to_sequence(struct mt76x02_dev *dev, + struct mt76x02_dfs_event *event) +{ + struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd; + struct mt76x02_dfs_sw_detector_params *sw_params; + struct mt76x02_dfs_sequence *seq, *tmp_seq; + u16 max_seq_len = 0; + int factor, pri; + + sw_params = &dfs_pd->sw_dpd_params; + list_for_each_entry_safe(seq, tmp_seq, &dfs_pd->sequences, head) { + if (event->ts > seq->first_ts + MT_DFS_SEQUENCE_WINDOW) { + list_del_init(&seq->head); + mt76x02_dfs_seq_pool_put(dev, seq); + continue; + } + + if (event->engine != seq->engine) + continue; + + pri = event->ts - seq->last_ts; + factor = mt76x02_dfs_get_multiple(pri, seq->pri, + sw_params->pri_margin); + if (factor > 0) { + seq->last_ts = event->ts; + seq->count++; + max_seq_len = max_t(u16, max_seq_len, seq->count); + } + } + return max_seq_len; +} + +static bool mt76x02_dfs_check_detection(struct mt76x02_dev *dev) +{ + struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd; + struct mt76x02_dfs_sequence *seq; + + if (list_empty(&dfs_pd->sequences)) + return false; + + list_for_each_entry(seq, &dfs_pd->sequences, head) { + if (seq->count > MT_DFS_SEQUENCE_TH) { + dfs_pd->stats[seq->engine].sw_pattern++; + return true; + } + } + return false; +} + +static void mt76x02_dfs_add_events(struct mt76x02_dev *dev) +{ + struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd; + struct mt76x02_dfs_event event; + int i, seq_len; + + /* disable debug mode */ + mt76x02_dfs_set_capture_mode_ctrl(dev, false); + for (i = 0; i < MT_DFS_EVENT_LOOP; i++) { + if (!mt76x02_dfs_fetch_event(dev, &event)) + break; + + if (dfs_pd->last_event_ts > event.ts) + mt76x02_dfs_detector_reset(dev); + dfs_pd->last_event_ts = event.ts; + + if (!mt76x02_dfs_check_event(dev, &event)) + continue; + + seq_len = mt76x02_dfs_add_event_to_sequence(dev, &event); + mt76x02_dfs_create_sequence(dev, &event, seq_len); + + mt76x02_dfs_queue_event(dev, &event); + } + mt76x02_dfs_set_capture_mode_ctrl(dev, true); +} + +static void mt76x02_dfs_check_event_window(struct mt76x02_dev *dev) +{ + struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd; + struct mt76x02_dfs_event_rb *event_buff; + struct mt76x02_dfs_event *event; + int i; + + for (i = 0; i < ARRAY_SIZE(dfs_pd->event_rb); i++) { + event_buff = &dfs_pd->event_rb[i]; + + while (event_buff->h_rb != event_buff->t_rb) { + event = &event_buff->data[event_buff->h_rb]; + + /* sorted list */ + if (time_is_after_jiffies(event->fetch_ts + + MT_DFS_EVENT_WINDOW)) + break; + event_buff->h_rb = mt76_incr(event_buff->h_rb, + MT_DFS_EVENT_BUFLEN); + } + } +} + +static void mt76x02_dfs_tasklet(struct tasklet_struct *t) +{ + struct mt76x02_dfs_pattern_detector *dfs_pd = from_tasklet(dfs_pd, t, + dfs_tasklet); + struct mt76x02_dev *dev = container_of(dfs_pd, typeof(*dev), dfs_pd); + u32 engine_mask; + int i; + + if (test_bit(MT76_SCANNING, &dev->mphy.state)) + goto out; + + if (time_is_before_jiffies(dfs_pd->last_sw_check + + MT_DFS_SW_TIMEOUT)) { + bool radar_detected; + + dfs_pd->last_sw_check = jiffies; + + mt76x02_dfs_add_events(dev); + radar_detected = mt76x02_dfs_check_detection(dev); + if (radar_detected) { + /* sw detector rx radar pattern */ + ieee80211_radar_detected(dev->mt76.hw, NULL); + mt76x02_dfs_detector_reset(dev); + + return; + } + mt76x02_dfs_check_event_window(dev); + } + + engine_mask = mt76_rr(dev, MT_BBP(DFS, 1)); + if (!(engine_mask & 0xf)) + goto out; + + for (i = 0; i < MT_DFS_NUM_ENGINES; i++) { + struct mt76x02_dfs_hw_pulse pulse; + + if (!(engine_mask & (1 << i))) + continue; + + pulse.engine = i; + mt76x02_dfs_get_hw_pulse(dev, &pulse); + + if (!mt76x02_dfs_check_hw_pulse(dev, &pulse)) { + dfs_pd->stats[i].hw_pulse_discarded++; + continue; + } + + /* hw detector rx radar pattern */ + dfs_pd->stats[i].hw_pattern++; + ieee80211_radar_detected(dev->mt76.hw, NULL); + mt76x02_dfs_detector_reset(dev); + + return; + } + + /* reset hw detector */ + mt76_wr(dev, MT_BBP(DFS, 1), 0xf); + +out: + mt76x02_irq_enable(dev, MT_INT_GPTIMER); +} + +static void mt76x02_dfs_init_sw_detector(struct mt76x02_dev *dev) +{ + struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd; + + switch (dev->mt76.region) { + case NL80211_DFS_FCC: + dfs_pd->sw_dpd_params.max_pri = MT_DFS_FCC_MAX_PRI; + dfs_pd->sw_dpd_params.min_pri = MT_DFS_FCC_MIN_PRI; + dfs_pd->sw_dpd_params.pri_margin = MT_DFS_PRI_MARGIN; + break; + case NL80211_DFS_ETSI: + dfs_pd->sw_dpd_params.max_pri = MT_DFS_ETSI_MAX_PRI; + dfs_pd->sw_dpd_params.min_pri = MT_DFS_ETSI_MIN_PRI; + dfs_pd->sw_dpd_params.pri_margin = MT_DFS_PRI_MARGIN << 2; + break; + case NL80211_DFS_JP: + dfs_pd->sw_dpd_params.max_pri = MT_DFS_JP_MAX_PRI; + dfs_pd->sw_dpd_params.min_pri = MT_DFS_JP_MIN_PRI; + dfs_pd->sw_dpd_params.pri_margin = MT_DFS_PRI_MARGIN; + break; + case NL80211_DFS_UNSET: + default: + break; + } +} + +static void mt76x02_dfs_set_bbp_params(struct mt76x02_dev *dev) +{ + const struct mt76x02_radar_specs *radar_specs; + u8 i, shift; + u32 data; + + switch (dev->mphy.chandef.width) { + case NL80211_CHAN_WIDTH_40: + shift = MT_DFS_NUM_ENGINES; + break; + case NL80211_CHAN_WIDTH_80: + shift = 2 * MT_DFS_NUM_ENGINES; + break; + default: + shift = 0; + break; + } + + switch (dev->mt76.region) { + case NL80211_DFS_FCC: + radar_specs = &fcc_radar_specs[shift]; + break; + case NL80211_DFS_ETSI: + radar_specs = &etsi_radar_specs[shift]; + break; + case NL80211_DFS_JP: + if (dev->mphy.chandef.chan->center_freq >= 5250 && + dev->mphy.chandef.chan->center_freq <= 5350) + radar_specs = &jp_w53_radar_specs[shift]; + else + radar_specs = &jp_w56_radar_specs[shift]; + break; + case NL80211_DFS_UNSET: + default: + return; + } + + data = (MT_DFS_VGA_MASK << 16) | + (MT_DFS_PWR_GAIN_OFFSET << 12) | + (MT_DFS_PWR_DOWN_TIME << 8) | + (MT_DFS_SYM_ROUND << 4) | + (MT_DFS_DELTA_DELAY & 0xf); + mt76_wr(dev, MT_BBP(DFS, 2), data); + + data = (MT_DFS_RX_PE_MASK << 16) | MT_DFS_PKT_END_MASK; + mt76_wr(dev, MT_BBP(DFS, 3), data); + + for (i = 0; i < MT_DFS_NUM_ENGINES; i++) { + /* configure engine */ + mt76_wr(dev, MT_BBP(DFS, 0), i); + + /* detection mode + avg_len */ + data = ((radar_specs[i].avg_len & 0x1ff) << 16) | + (radar_specs[i].mode & 0xf); + mt76_wr(dev, MT_BBP(DFS, 4), data); + + /* dfs energy */ + data = ((radar_specs[i].e_high & 0x0fff) << 16) | + (radar_specs[i].e_low & 0x0fff); + mt76_wr(dev, MT_BBP(DFS, 5), data); + + /* dfs period */ + mt76_wr(dev, MT_BBP(DFS, 7), radar_specs[i].t_low); + mt76_wr(dev, MT_BBP(DFS, 9), radar_specs[i].t_high); + + /* dfs burst */ + mt76_wr(dev, MT_BBP(DFS, 11), radar_specs[i].b_low); + mt76_wr(dev, MT_BBP(DFS, 13), radar_specs[i].b_high); + + /* dfs width */ + data = ((radar_specs[i].w_high & 0x0fff) << 16) | + (radar_specs[i].w_low & 0x0fff); + mt76_wr(dev, MT_BBP(DFS, 14), data); + + /* dfs margins */ + data = (radar_specs[i].w_margin << 16) | + radar_specs[i].t_margin; + mt76_wr(dev, MT_BBP(DFS, 15), data); + + /* dfs event expiration */ + mt76_wr(dev, MT_BBP(DFS, 17), radar_specs[i].event_expiration); + + /* dfs pwr adj */ + mt76_wr(dev, MT_BBP(DFS, 30), radar_specs[i].pwr_jmp); + } + + /* reset status */ + mt76_wr(dev, MT_BBP(DFS, 1), 0xf); + mt76_wr(dev, MT_BBP(DFS, 36), 0x3); + + /* enable detection*/ + mt76_wr(dev, MT_BBP(DFS, 0), MT_DFS_CH_EN << 16); + mt76_wr(dev, MT_BBP(IBI, 11), 0x0c350001); +} + +void mt76x02_phy_dfs_adjust_agc(struct mt76x02_dev *dev) +{ + u32 agc_r8, agc_r4, val_r8, val_r4, dfs_r31; + + agc_r8 = mt76_rr(dev, MT_BBP(AGC, 8)); + agc_r4 = mt76_rr(dev, MT_BBP(AGC, 4)); + + val_r8 = (agc_r8 & 0x00007e00) >> 9; + val_r4 = agc_r4 & ~0x1f000000; + val_r4 += (((val_r8 + 1) >> 1) << 24); + mt76_wr(dev, MT_BBP(AGC, 4), val_r4); + + dfs_r31 = FIELD_GET(MT_BBP_AGC_LNA_HIGH_GAIN, val_r4); + dfs_r31 += val_r8; + dfs_r31 -= (agc_r8 & 0x00000038) >> 3; + dfs_r31 = (dfs_r31 << 16) | 0x00000307; + mt76_wr(dev, MT_BBP(DFS, 31), dfs_r31); + + if (is_mt76x2(dev)) { + mt76_wr(dev, MT_BBP(DFS, 32), 0x00040071); + } else { + /* disable hw detector */ + mt76_wr(dev, MT_BBP(DFS, 0), 0); + /* enable hw detector */ + mt76_wr(dev, MT_BBP(DFS, 0), MT_DFS_CH_EN << 16); + } +} +EXPORT_SYMBOL_GPL(mt76x02_phy_dfs_adjust_agc); + +void mt76x02_dfs_init_params(struct mt76x02_dev *dev) +{ + if (mt76_phy_dfs_state(&dev->mphy) > MT_DFS_STATE_DISABLED) { + mt76x02_dfs_init_sw_detector(dev); + mt76x02_dfs_set_bbp_params(dev); + /* enable debug mode */ + mt76x02_dfs_set_capture_mode_ctrl(dev, true); + + mt76x02_irq_enable(dev, MT_INT_GPTIMER); + mt76_rmw_field(dev, MT_INT_TIMER_EN, + MT_INT_TIMER_EN_GP_TIMER_EN, 1); + } else { + /* disable hw detector */ + mt76_wr(dev, MT_BBP(DFS, 0), 0); + /* clear detector status */ + mt76_wr(dev, MT_BBP(DFS, 1), 0xf); + if (mt76_chip(&dev->mt76) == 0x7610 || + mt76_chip(&dev->mt76) == 0x7630) + mt76_wr(dev, MT_BBP(IBI, 11), 0xfde8081); + else + mt76_wr(dev, MT_BBP(IBI, 11), 0); + + mt76x02_irq_disable(dev, MT_INT_GPTIMER); + mt76_rmw_field(dev, MT_INT_TIMER_EN, + MT_INT_TIMER_EN_GP_TIMER_EN, 0); + } +} +EXPORT_SYMBOL_GPL(mt76x02_dfs_init_params); + +void mt76x02_dfs_init_detector(struct mt76x02_dev *dev) +{ + struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd; + + INIT_LIST_HEAD(&dfs_pd->sequences); + INIT_LIST_HEAD(&dfs_pd->seq_pool); + dev->mt76.region = NL80211_DFS_UNSET; + dfs_pd->last_sw_check = jiffies; + tasklet_setup(&dfs_pd->dfs_tasklet, mt76x02_dfs_tasklet); +} + +static void +mt76x02_dfs_set_domain(struct mt76x02_dev *dev, + enum nl80211_dfs_regions region) +{ + struct mt76x02_dfs_pattern_detector *dfs_pd = &dev->dfs_pd; + + mutex_lock(&dev->mt76.mutex); + if (dev->mt76.region != region) { + tasklet_disable(&dfs_pd->dfs_tasklet); + + dev->ed_monitor = dev->ed_monitor_enabled && + region == NL80211_DFS_ETSI; + mt76x02_edcca_init(dev); + + dev->mt76.region = region; + mt76x02_dfs_init_params(dev); + tasklet_enable(&dfs_pd->dfs_tasklet); + } + mutex_unlock(&dev->mt76.mutex); +} + +void mt76x02_regd_notifier(struct wiphy *wiphy, + struct regulatory_request *request) +{ + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct mt76x02_dev *dev = hw->priv; + + mt76x02_dfs_set_domain(dev, request->dfs_region); +} diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_dfs.h b/drivers/net/wireless/mediatek/mt76/mt76x02_dfs.h new file mode 100644 index 000000000..d40051992 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_dfs.h @@ -0,0 +1,132 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (C) 2016 Lorenzo Bianconi + */ + +#ifndef __MT76x02_DFS_H +#define __MT76x02_DFS_H + +#include +#include + +#define MT_DFS_GP_INTERVAL (10 << 4) /* 64 us unit */ +#define MT_DFS_NUM_ENGINES 4 + +/* bbp params */ +#define MT_DFS_SYM_ROUND 0 +#define MT_DFS_DELTA_DELAY 2 +#define MT_DFS_VGA_MASK 0 +#define MT_DFS_PWR_GAIN_OFFSET 3 +#define MT_DFS_PWR_DOWN_TIME 0xf +#define MT_DFS_RX_PE_MASK 0xff +#define MT_DFS_PKT_END_MASK 0 +#define MT_DFS_CH_EN 0xf + +/* sw detector params */ +#define MT_DFS_EVENT_LOOP 64 +#define MT_DFS_SW_TIMEOUT (HZ / 20) +#define MT_DFS_EVENT_WINDOW (HZ / 5) +#define MT_DFS_SEQUENCE_WINDOW (200 * (1 << 20)) +#define MT_DFS_EVENT_TIME_MARGIN 2000 +#define MT_DFS_PRI_MARGIN 4 +#define MT_DFS_SEQUENCE_TH 6 + +#define MT_DFS_FCC_MAX_PRI ((28570 << 1) + 1000) +#define MT_DFS_FCC_MIN_PRI (3000 - 2) +#define MT_DFS_JP_MAX_PRI ((80000 << 1) + 1000) +#define MT_DFS_JP_MIN_PRI (28500 - 2) +#define MT_DFS_ETSI_MAX_PRI (133333 + 125000 + 117647 + 1000) +#define MT_DFS_ETSI_MIN_PRI (4500 - 20) + +struct mt76x02_radar_specs { + u8 mode; + u16 avg_len; + u16 e_low; + u16 e_high; + u16 w_low; + u16 w_high; + u16 w_margin; + u32 t_low; + u32 t_high; + u16 t_margin; + u32 b_low; + u32 b_high; + u32 event_expiration; + u16 pwr_jmp; +}; + +#define MT_DFS_CHECK_EVENT(x) ((x) != GENMASK(31, 0)) +#define MT_DFS_EVENT_ENGINE(x) (((x) & BIT(31)) ? 2 : 0) +#define MT_DFS_EVENT_TIMESTAMP(x) ((x) & GENMASK(21, 0)) +#define MT_DFS_EVENT_WIDTH(x) ((x) & GENMASK(11, 0)) +struct mt76x02_dfs_event { + unsigned long fetch_ts; + u32 ts; + u16 width; + u8 engine; +}; + +#define MT_DFS_EVENT_BUFLEN 256 +struct mt76x02_dfs_event_rb { + struct mt76x02_dfs_event data[MT_DFS_EVENT_BUFLEN]; + int h_rb, t_rb; +}; + +struct mt76x02_dfs_sequence { + struct list_head head; + u32 first_ts; + u32 last_ts; + u32 pri; + u16 count; + u8 engine; +}; + +struct mt76x02_dfs_hw_pulse { + u8 engine; + u32 period; + u32 w1; + u32 w2; + u32 burst; +}; + +struct mt76x02_dfs_sw_detector_params { + u32 min_pri; + u32 max_pri; + u32 pri_margin; +}; + +struct mt76x02_dfs_engine_stats { + u32 hw_pattern; + u32 hw_pulse_discarded; + u32 sw_pattern; +}; + +struct mt76x02_dfs_seq_stats { + u32 seq_pool_len; + u32 seq_len; +}; + +struct mt76x02_dfs_pattern_detector { + u8 chirp_pulse_cnt; + u32 chirp_pulse_ts; + + struct mt76x02_dfs_sw_detector_params sw_dpd_params; + struct mt76x02_dfs_event_rb event_rb[2]; + + struct list_head sequences; + struct list_head seq_pool; + struct mt76x02_dfs_seq_stats seq_stats; + + unsigned long last_sw_check; + u32 last_event_ts; + + struct mt76x02_dfs_engine_stats stats[MT_DFS_NUM_ENGINES]; + struct tasklet_struct dfs_tasklet; +}; + +void mt76x02_dfs_init_params(struct mt76x02_dev *dev); +void mt76x02_dfs_init_detector(struct mt76x02_dev *dev); +void mt76x02_regd_notifier(struct wiphy *wiphy, + struct regulatory_request *request); +void mt76x02_phy_dfs_adjust_agc(struct mt76x02_dev *dev); +#endif /* __MT76x02_DFS_H */ diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_dma.h b/drivers/net/wireless/mediatek/mt76/mt76x02_dma.h new file mode 100644 index 000000000..2f6ba8cf5 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_dma.h @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (C) 2018 Lorenzo Bianconi + */ + +#ifndef __MT76x02_DMA_H +#define __MT76x02_DMA_H + +#include "mt76x02.h" +#include "dma.h" + +#define MT_TXD_INFO_LEN GENMASK(15, 0) +#define MT_TXD_INFO_NEXT_VLD BIT(16) +#define MT_TXD_INFO_TX_BURST BIT(17) +#define MT_TXD_INFO_80211 BIT(19) +#define MT_TXD_INFO_TSO BIT(20) +#define MT_TXD_INFO_CSO BIT(21) +#define MT_TXD_INFO_WIV BIT(24) +#define MT_TXD_INFO_QSEL GENMASK(26, 25) +#define MT_TXD_INFO_DPORT GENMASK(29, 27) +#define MT_TXD_INFO_TYPE GENMASK(31, 30) + +#define MT_RX_FCE_INFO_LEN GENMASK(13, 0) +#define MT_RX_FCE_INFO_SELF_GEN BIT(15) +#define MT_RX_FCE_INFO_CMD_SEQ GENMASK(19, 16) +#define MT_RX_FCE_INFO_EVT_TYPE GENMASK(23, 20) +#define MT_RX_FCE_INFO_PCIE_INTR BIT(24) +#define MT_RX_FCE_INFO_QSEL GENMASK(26, 25) +#define MT_RX_FCE_INFO_D_PORT GENMASK(29, 27) +#define MT_RX_FCE_INFO_TYPE GENMASK(31, 30) + +/* MCU request message header */ +#define MT_MCU_MSG_LEN GENMASK(15, 0) +#define MT_MCU_MSG_CMD_SEQ GENMASK(19, 16) +#define MT_MCU_MSG_CMD_TYPE GENMASK(26, 20) +#define MT_MCU_MSG_PORT GENMASK(29, 27) +#define MT_MCU_MSG_TYPE GENMASK(31, 30) +#define MT_MCU_MSG_TYPE_CMD BIT(30) + +#define MT_RX_HEADROOM 32 +#define MT76X02_RX_RING_SIZE 256 + +enum dma_msg_port { + WLAN_PORT, + CPU_RX_PORT, + CPU_TX_PORT, + HOST_PORT, + VIRTUAL_CPU_RX_PORT, + VIRTUAL_CPU_TX_PORT, + DISCARD, +}; + +static inline bool +mt76x02_wait_for_wpdma(struct mt76_dev *dev, int timeout) +{ + return __mt76_poll(dev, MT_WPDMA_GLO_CFG, + MT_WPDMA_GLO_CFG_TX_DMA_BUSY | + MT_WPDMA_GLO_CFG_RX_DMA_BUSY, + 0, timeout); +} + +int mt76x02_dma_init(struct mt76x02_dev *dev); +void mt76x02_dma_disable(struct mt76x02_dev *dev); + +#endif /* __MT76x02_DMA_H */ diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_eeprom.c b/drivers/net/wireless/mediatek/mt76/mt76x02_eeprom.c new file mode 100644 index 000000000..d16be0cb0 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_eeprom.c @@ -0,0 +1,147 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (C) 2016 Felix Fietkau + * Copyright (C) 2018 Lorenzo Bianconi + */ + +#include + +#include "mt76x02_eeprom.h" + +static int +mt76x02_efuse_read(struct mt76x02_dev *dev, u16 addr, u8 *data, + enum mt76x02_eeprom_modes mode) +{ + u32 val; + int i; + + val = mt76_rr(dev, MT_EFUSE_CTRL); + val &= ~(MT_EFUSE_CTRL_AIN | + MT_EFUSE_CTRL_MODE); + val |= FIELD_PREP(MT_EFUSE_CTRL_AIN, addr & ~0xf); + val |= FIELD_PREP(MT_EFUSE_CTRL_MODE, mode); + val |= MT_EFUSE_CTRL_KICK; + mt76_wr(dev, MT_EFUSE_CTRL, val); + + if (!mt76_poll_msec(dev, MT_EFUSE_CTRL, MT_EFUSE_CTRL_KICK, 0, 1000)) + return -ETIMEDOUT; + + udelay(2); + + val = mt76_rr(dev, MT_EFUSE_CTRL); + if ((val & MT_EFUSE_CTRL_AOUT) == MT_EFUSE_CTRL_AOUT) { + memset(data, 0xff, 16); + return 0; + } + + for (i = 0; i < 4; i++) { + val = mt76_rr(dev, MT_EFUSE_DATA(i)); + put_unaligned_le32(val, data + 4 * i); + } + + return 0; +} + +int mt76x02_eeprom_copy(struct mt76x02_dev *dev, + enum mt76x02_eeprom_field field, + void *dest, int len) +{ + if (field + len > dev->mt76.eeprom.size) + return -1; + + memcpy(dest, dev->mt76.eeprom.data + field, len); + return 0; +} +EXPORT_SYMBOL_GPL(mt76x02_eeprom_copy); + +int mt76x02_get_efuse_data(struct mt76x02_dev *dev, u16 base, void *buf, + int len, enum mt76x02_eeprom_modes mode) +{ + int ret, i; + + for (i = 0; i + 16 <= len; i += 16) { + ret = mt76x02_efuse_read(dev, base + i, buf + i, mode); + if (ret) + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(mt76x02_get_efuse_data); + +void mt76x02_eeprom_parse_hw_cap(struct mt76x02_dev *dev) +{ + u16 val = mt76x02_eeprom_get(dev, MT_EE_NIC_CONF_0); + + switch (FIELD_GET(MT_EE_NIC_CONF_0_BOARD_TYPE, val)) { + case BOARD_TYPE_5GHZ: + dev->mphy.cap.has_5ghz = true; + break; + case BOARD_TYPE_2GHZ: + dev->mphy.cap.has_2ghz = true; + break; + default: + dev->mphy.cap.has_2ghz = true; + dev->mphy.cap.has_5ghz = true; + break; + } +} +EXPORT_SYMBOL_GPL(mt76x02_eeprom_parse_hw_cap); + +bool mt76x02_ext_pa_enabled(struct mt76x02_dev *dev, enum nl80211_band band) +{ + u16 conf0 = mt76x02_eeprom_get(dev, MT_EE_NIC_CONF_0); + + if (band == NL80211_BAND_5GHZ) + return !(conf0 & MT_EE_NIC_CONF_0_PA_INT_5G); + else + return !(conf0 & MT_EE_NIC_CONF_0_PA_INT_2G); +} +EXPORT_SYMBOL_GPL(mt76x02_ext_pa_enabled); + +void mt76x02_get_rx_gain(struct mt76x02_dev *dev, enum nl80211_band band, + u16 *rssi_offset, s8 *lna_2g, s8 *lna_5g) +{ + u16 val; + + val = mt76x02_eeprom_get(dev, MT_EE_LNA_GAIN); + *lna_2g = val & 0xff; + lna_5g[0] = val >> 8; + + val = mt76x02_eeprom_get(dev, MT_EE_RSSI_OFFSET_2G_1); + lna_5g[1] = val >> 8; + + val = mt76x02_eeprom_get(dev, MT_EE_RSSI_OFFSET_5G_1); + lna_5g[2] = val >> 8; + + if (!mt76x02_field_valid(lna_5g[1])) + lna_5g[1] = lna_5g[0]; + + if (!mt76x02_field_valid(lna_5g[2])) + lna_5g[2] = lna_5g[0]; + + if (band == NL80211_BAND_2GHZ) + *rssi_offset = mt76x02_eeprom_get(dev, MT_EE_RSSI_OFFSET_2G_0); + else + *rssi_offset = mt76x02_eeprom_get(dev, MT_EE_RSSI_OFFSET_5G_0); +} +EXPORT_SYMBOL_GPL(mt76x02_get_rx_gain); + +u8 mt76x02_get_lna_gain(struct mt76x02_dev *dev, + s8 *lna_2g, s8 *lna_5g, + struct ieee80211_channel *chan) +{ + u8 lna; + + if (chan->band == NL80211_BAND_2GHZ) + lna = *lna_2g; + else if (chan->hw_value <= 64) + lna = lna_5g[0]; + else if (chan->hw_value <= 128) + lna = lna_5g[1]; + else + lna = lna_5g[2]; + + return lna != 0xff ? lna : 0; +} +EXPORT_SYMBOL_GPL(mt76x02_get_lna_gain); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_eeprom.h b/drivers/net/wireless/mediatek/mt76/mt76x02_eeprom.h new file mode 100644 index 000000000..3cbb2977f --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_eeprom.h @@ -0,0 +1,185 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (C) 2016 Felix Fietkau + * Copyright (C) 2018 Lorenzo Bianconi + */ + +#ifndef __MT76x02_EEPROM_H +#define __MT76x02_EEPROM_H + +#include "mt76x02.h" + +enum mt76x02_eeprom_field { + MT_EE_CHIP_ID = 0x000, + MT_EE_VERSION = 0x002, + MT_EE_MAC_ADDR = 0x004, + MT_EE_PCI_ID = 0x00A, + MT_EE_ANTENNA = 0x022, + MT_EE_CFG1_INIT = 0x024, + MT_EE_NIC_CONF_0 = 0x034, + MT_EE_NIC_CONF_1 = 0x036, + MT_EE_COUNTRY_REGION_5GHZ = 0x038, + MT_EE_COUNTRY_REGION_2GHZ = 0x039, + MT_EE_FREQ_OFFSET = 0x03a, + MT_EE_NIC_CONF_2 = 0x042, + + MT_EE_XTAL_TRIM_1 = 0x03a, + MT_EE_XTAL_TRIM_2 = 0x09e, + + MT_EE_LNA_GAIN = 0x044, + MT_EE_RSSI_OFFSET_2G_0 = 0x046, + MT_EE_RSSI_OFFSET_2G_1 = 0x048, + MT_EE_LNA_GAIN_5GHZ_1 = 0x049, + MT_EE_RSSI_OFFSET_5G_0 = 0x04a, + MT_EE_RSSI_OFFSET_5G_1 = 0x04c, + MT_EE_LNA_GAIN_5GHZ_2 = 0x04d, + + MT_EE_TX_POWER_DELTA_BW40 = 0x050, + MT_EE_TX_POWER_DELTA_BW80 = 0x052, + + MT_EE_TX_POWER_EXT_PA_5G = 0x054, + + MT_EE_TX_POWER_0_START_2G = 0x056, + MT_EE_TX_POWER_1_START_2G = 0x05c, + + /* used as byte arrays */ +#define MT_TX_POWER_GROUP_SIZE_5G 5 +#define MT_TX_POWER_GROUPS_5G 6 + MT_EE_TX_POWER_0_START_5G = 0x062, + MT_EE_TSSI_SLOPE_2G = 0x06e, + + MT_EE_TX_POWER_0_GRP3_TX_POWER_DELTA = 0x074, + MT_EE_TX_POWER_0_GRP4_TSSI_SLOPE = 0x076, + + MT_EE_TX_POWER_1_START_5G = 0x080, + + MT_EE_TX_POWER_CCK = 0x0a0, + MT_EE_TX_POWER_OFDM_2G_6M = 0x0a2, + MT_EE_TX_POWER_OFDM_2G_24M = 0x0a4, + MT_EE_TX_POWER_OFDM_5G_6M = 0x0b2, + MT_EE_TX_POWER_OFDM_5G_24M = 0x0b4, + MT_EE_TX_POWER_HT_MCS0 = 0x0a6, + MT_EE_TX_POWER_HT_MCS4 = 0x0a8, + MT_EE_TX_POWER_HT_MCS8 = 0x0aa, + MT_EE_TX_POWER_HT_MCS12 = 0x0ac, + MT_EE_TX_POWER_VHT_MCS8 = 0x0be, + + MT_EE_2G_TARGET_POWER = 0x0d0, + MT_EE_TEMP_OFFSET = 0x0d1, + MT_EE_5G_TARGET_POWER = 0x0d2, + MT_EE_TSSI_BOUND1 = 0x0d4, + MT_EE_TSSI_BOUND2 = 0x0d6, + MT_EE_TSSI_BOUND3 = 0x0d8, + MT_EE_TSSI_BOUND4 = 0x0da, + MT_EE_FREQ_OFFSET_COMPENSATION = 0x0db, + MT_EE_TSSI_BOUND5 = 0x0dc, + MT_EE_TX_POWER_BYRATE_BASE = 0x0de, + + MT_EE_TSSI_SLOPE_5G = 0x0f0, + MT_EE_RF_TEMP_COMP_SLOPE_5G = 0x0f2, + MT_EE_RF_TEMP_COMP_SLOPE_2G = 0x0f4, + + MT_EE_RF_2G_TSSI_OFF_TXPOWER = 0x0f6, + MT_EE_RF_2G_RX_HIGH_GAIN = 0x0f8, + MT_EE_RF_5G_GRP0_1_RX_HIGH_GAIN = 0x0fa, + MT_EE_RF_5G_GRP2_3_RX_HIGH_GAIN = 0x0fc, + MT_EE_RF_5G_GRP4_5_RX_HIGH_GAIN = 0x0fe, + + MT_EE_BT_RCAL_RESULT = 0x138, + MT_EE_BT_VCDL_CALIBRATION = 0x13c, + MT_EE_BT_PMUCFG = 0x13e, + + MT_EE_USAGE_MAP_START = 0x1e0, + MT_EE_USAGE_MAP_END = 0x1fc, + + __MT_EE_MAX +}; + +#define MT_EE_ANTENNA_DUAL BIT(15) + +#define MT_EE_NIC_CONF_0_RX_PATH GENMASK(3, 0) +#define MT_EE_NIC_CONF_0_TX_PATH GENMASK(7, 4) +#define MT_EE_NIC_CONF_0_PA_TYPE GENMASK(9, 8) +#define MT_EE_NIC_CONF_0_PA_INT_2G BIT(8) +#define MT_EE_NIC_CONF_0_PA_INT_5G BIT(9) +#define MT_EE_NIC_CONF_0_PA_IO_CURRENT BIT(10) +#define MT_EE_NIC_CONF_0_BOARD_TYPE GENMASK(13, 12) + +#define MT_EE_NIC_CONF_1_HW_RF_CTRL BIT(0) +#define MT_EE_NIC_CONF_1_TEMP_TX_ALC BIT(1) +#define MT_EE_NIC_CONF_1_LNA_EXT_2G BIT(2) +#define MT_EE_NIC_CONF_1_LNA_EXT_5G BIT(3) +#define MT_EE_NIC_CONF_1_TX_ALC_EN BIT(13) + +#define MT_EE_NIC_CONF_2_ANT_OPT BIT(3) +#define MT_EE_NIC_CONF_2_ANT_DIV BIT(4) +#define MT_EE_NIC_CONF_2_XTAL_OPTION GENMASK(10, 9) + +#define MT_EFUSE_USAGE_MAP_SIZE (MT_EE_USAGE_MAP_END - \ + MT_EE_USAGE_MAP_START + 1) + +enum mt76x02_eeprom_modes { + MT_EE_READ, + MT_EE_PHYSICAL_READ, +}; + +enum mt76x02_board_type { + BOARD_TYPE_2GHZ = 1, + BOARD_TYPE_5GHZ = 2, +}; + +static inline bool mt76x02_field_valid(u8 val) +{ + return val != 0 && val != 0xff; +} + +static inline int +mt76x02_sign_extend(u32 val, unsigned int size) +{ + bool sign = val & BIT(size - 1); + + val &= BIT(size - 1) - 1; + + return sign ? val : -val; +} + +static inline int +mt76x02_sign_extend_optional(u32 val, unsigned int size) +{ + bool enable = val & BIT(size); + + return enable ? mt76x02_sign_extend(val, size) : 0; +} + +static inline s8 mt76x02_rate_power_val(u8 val) +{ + if (!mt76x02_field_valid(val)) + return 0; + + return mt76x02_sign_extend_optional(val, 7); +} + +static inline int +mt76x02_eeprom_get(struct mt76x02_dev *dev, + enum mt76x02_eeprom_field field) +{ + if ((field & 1) || field >= __MT_EE_MAX) + return -1; + + return get_unaligned_le16(dev->mt76.eeprom.data + field); +} + +bool mt76x02_ext_pa_enabled(struct mt76x02_dev *dev, enum nl80211_band band); +int mt76x02_get_efuse_data(struct mt76x02_dev *dev, u16 base, void *buf, + int len, enum mt76x02_eeprom_modes mode); +void mt76x02_get_rx_gain(struct mt76x02_dev *dev, enum nl80211_band band, + u16 *rssi_offset, s8 *lna_2g, s8 *lna_5g); +u8 mt76x02_get_lna_gain(struct mt76x02_dev *dev, + s8 *lna_2g, s8 *lna_5g, + struct ieee80211_channel *chan); +void mt76x02_eeprom_parse_hw_cap(struct mt76x02_dev *dev); +int mt76x02_eeprom_copy(struct mt76x02_dev *dev, + enum mt76x02_eeprom_field field, + void *dest, int len); + +#endif /* __MT76x02_EEPROM_H */ diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c b/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c new file mode 100644 index 000000000..21f8b1e64 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_mac.c @@ -0,0 +1,1241 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (C) 2016 Felix Fietkau + * Copyright (C) 2018 Stanislaw Gruszka + */ + +#include "mt76x02.h" +#include "mt76x02_trace.h" +#include "trace.h" + +void mt76x02_mac_reset_counters(struct mt76x02_dev *dev) +{ + int i; + + mt76_rr(dev, MT_RX_STAT_0); + mt76_rr(dev, MT_RX_STAT_1); + mt76_rr(dev, MT_RX_STAT_2); + mt76_rr(dev, MT_TX_STA_0); + mt76_rr(dev, MT_TX_STA_1); + mt76_rr(dev, MT_TX_STA_2); + + for (i = 0; i < 16; i++) + mt76_rr(dev, MT_TX_AGG_CNT(i)); + + for (i = 0; i < 16; i++) + mt76_rr(dev, MT_TX_STAT_FIFO); + + memset(dev->mphy.aggr_stats, 0, sizeof(dev->mphy.aggr_stats)); +} +EXPORT_SYMBOL_GPL(mt76x02_mac_reset_counters); + +static enum mt76x02_cipher_type +mt76x02_mac_get_key_info(struct ieee80211_key_conf *key, u8 *key_data) +{ + memset(key_data, 0, 32); + if (!key) + return MT76X02_CIPHER_NONE; + + if (key->keylen > 32) + return MT76X02_CIPHER_NONE; + + memcpy(key_data, key->key, key->keylen); + + switch (key->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + return MT76X02_CIPHER_WEP40; + case WLAN_CIPHER_SUITE_WEP104: + return MT76X02_CIPHER_WEP104; + case WLAN_CIPHER_SUITE_TKIP: + return MT76X02_CIPHER_TKIP; + case WLAN_CIPHER_SUITE_CCMP: + return MT76X02_CIPHER_AES_CCMP; + default: + return MT76X02_CIPHER_NONE; + } +} + +int mt76x02_mac_shared_key_setup(struct mt76x02_dev *dev, u8 vif_idx, + u8 key_idx, struct ieee80211_key_conf *key) +{ + enum mt76x02_cipher_type cipher; + u8 key_data[32]; + u32 val; + + cipher = mt76x02_mac_get_key_info(key, key_data); + if (cipher == MT76X02_CIPHER_NONE && key) + return -EOPNOTSUPP; + + val = mt76_rr(dev, MT_SKEY_MODE(vif_idx)); + val &= ~(MT_SKEY_MODE_MASK << MT_SKEY_MODE_SHIFT(vif_idx, key_idx)); + val |= cipher << MT_SKEY_MODE_SHIFT(vif_idx, key_idx); + mt76_wr(dev, MT_SKEY_MODE(vif_idx), val); + + mt76_wr_copy(dev, MT_SKEY(vif_idx, key_idx), key_data, + sizeof(key_data)); + + return 0; +} +EXPORT_SYMBOL_GPL(mt76x02_mac_shared_key_setup); + +void mt76x02_mac_wcid_sync_pn(struct mt76x02_dev *dev, u8 idx, + struct ieee80211_key_conf *key) +{ + enum mt76x02_cipher_type cipher; + u8 key_data[32]; + u32 iv, eiv; + u64 pn; + + cipher = mt76x02_mac_get_key_info(key, key_data); + iv = mt76_rr(dev, MT_WCID_IV(idx)); + eiv = mt76_rr(dev, MT_WCID_IV(idx) + 4); + + pn = (u64)eiv << 16; + if (cipher == MT76X02_CIPHER_TKIP) { + pn |= (iv >> 16) & 0xff; + pn |= (iv & 0xff) << 8; + } else if (cipher >= MT76X02_CIPHER_AES_CCMP) { + pn |= iv & 0xffff; + } else { + return; + } + + atomic64_set(&key->tx_pn, pn); +} + +int mt76x02_mac_wcid_set_key(struct mt76x02_dev *dev, u8 idx, + struct ieee80211_key_conf *key) +{ + enum mt76x02_cipher_type cipher; + u8 key_data[32]; + u8 iv_data[8]; + u64 pn; + + cipher = mt76x02_mac_get_key_info(key, key_data); + if (cipher == MT76X02_CIPHER_NONE && key) + return -EOPNOTSUPP; + + mt76_wr_copy(dev, MT_WCID_KEY(idx), key_data, sizeof(key_data)); + mt76_rmw_field(dev, MT_WCID_ATTR(idx), MT_WCID_ATTR_PKEY_MODE, cipher); + + memset(iv_data, 0, sizeof(iv_data)); + if (key) { + mt76_rmw_field(dev, MT_WCID_ATTR(idx), MT_WCID_ATTR_PAIRWISE, + !!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)); + + pn = atomic64_read(&key->tx_pn); + + iv_data[3] = key->keyidx << 6; + if (cipher >= MT76X02_CIPHER_TKIP) { + iv_data[3] |= 0x20; + put_unaligned_le32(pn >> 16, &iv_data[4]); + } + + if (cipher == MT76X02_CIPHER_TKIP) { + iv_data[0] = (pn >> 8) & 0xff; + iv_data[1] = (iv_data[0] | 0x20) & 0x7f; + iv_data[2] = pn & 0xff; + } else if (cipher >= MT76X02_CIPHER_AES_CCMP) { + put_unaligned_le16((pn & 0xffff), &iv_data[0]); + } + } + + mt76_wr_copy(dev, MT_WCID_IV(idx), iv_data, sizeof(iv_data)); + + return 0; +} + +void mt76x02_mac_wcid_setup(struct mt76x02_dev *dev, u8 idx, + u8 vif_idx, u8 *mac) +{ + struct mt76_wcid_addr addr = {}; + u32 attr; + + attr = FIELD_PREP(MT_WCID_ATTR_BSS_IDX, vif_idx & 7) | + FIELD_PREP(MT_WCID_ATTR_BSS_IDX_EXT, !!(vif_idx & 8)); + + mt76_wr(dev, MT_WCID_ATTR(idx), attr); + + if (idx >= 128) + return; + + if (mac) + memcpy(addr.macaddr, mac, ETH_ALEN); + + mt76_wr_copy(dev, MT_WCID_ADDR(idx), &addr, sizeof(addr)); +} +EXPORT_SYMBOL_GPL(mt76x02_mac_wcid_setup); + +void mt76x02_mac_wcid_set_drop(struct mt76x02_dev *dev, u8 idx, bool drop) +{ + u32 val = mt76_rr(dev, MT_WCID_DROP(idx)); + u32 bit = MT_WCID_DROP_MASK(idx); + + /* prevent unnecessary writes */ + if ((val & bit) != (bit * drop)) + mt76_wr(dev, MT_WCID_DROP(idx), (val & ~bit) | (bit * drop)); +} + +static u16 +mt76x02_mac_tx_rate_val(struct mt76x02_dev *dev, + const struct ieee80211_tx_rate *rate, u8 *nss_val) +{ + u8 phy, rate_idx, nss, bw = 0; + u16 rateval; + + if (rate->flags & IEEE80211_TX_RC_VHT_MCS) { + rate_idx = rate->idx; + nss = 1 + (rate->idx >> 4); + phy = MT_PHY_TYPE_VHT; + if (rate->flags & IEEE80211_TX_RC_80_MHZ_WIDTH) + bw = 2; + else if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) + bw = 1; + } else if (rate->flags & IEEE80211_TX_RC_MCS) { + rate_idx = rate->idx; + nss = 1 + (rate->idx >> 3); + phy = MT_PHY_TYPE_HT; + if (rate->flags & IEEE80211_TX_RC_GREEN_FIELD) + phy = MT_PHY_TYPE_HT_GF; + if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) + bw = 1; + } else { + const struct ieee80211_rate *r; + int band = dev->mphy.chandef.chan->band; + u16 val; + + r = &dev->mt76.hw->wiphy->bands[band]->bitrates[rate->idx]; + if (rate->flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) + val = r->hw_value_short; + else + val = r->hw_value; + + phy = val >> 8; + rate_idx = val & 0xff; + nss = 1; + } + + rateval = FIELD_PREP(MT_RXWI_RATE_INDEX, rate_idx); + rateval |= FIELD_PREP(MT_RXWI_RATE_PHY, phy); + rateval |= FIELD_PREP(MT_RXWI_RATE_BW, bw); + if (rate->flags & IEEE80211_TX_RC_SHORT_GI) + rateval |= MT_RXWI_RATE_SGI; + + *nss_val = nss; + return rateval; +} + +void mt76x02_mac_wcid_set_rate(struct mt76x02_dev *dev, struct mt76_wcid *wcid, + const struct ieee80211_tx_rate *rate) +{ + s8 max_txpwr_adj = mt76x02_tx_get_max_txpwr_adj(dev, rate); + u16 rateval; + u32 tx_info; + s8 nss; + + rateval = mt76x02_mac_tx_rate_val(dev, rate, &nss); + tx_info = FIELD_PREP(MT_WCID_TX_INFO_RATE, rateval) | + FIELD_PREP(MT_WCID_TX_INFO_NSS, nss) | + FIELD_PREP(MT_WCID_TX_INFO_TXPWR_ADJ, max_txpwr_adj) | + MT_WCID_TX_INFO_SET; + wcid->tx_info = tx_info; +} + +void mt76x02_mac_set_short_preamble(struct mt76x02_dev *dev, bool enable) +{ + if (enable) + mt76_set(dev, MT_AUTO_RSP_CFG, MT_AUTO_RSP_PREAMB_SHORT); + else + mt76_clear(dev, MT_AUTO_RSP_CFG, MT_AUTO_RSP_PREAMB_SHORT); +} + +bool mt76x02_mac_load_tx_status(struct mt76x02_dev *dev, + struct mt76x02_tx_status *stat) +{ + u32 stat1, stat2; + + stat2 = mt76_rr(dev, MT_TX_STAT_FIFO_EXT); + stat1 = mt76_rr(dev, MT_TX_STAT_FIFO); + + stat->valid = !!(stat1 & MT_TX_STAT_FIFO_VALID); + if (!stat->valid) + return false; + + stat->success = !!(stat1 & MT_TX_STAT_FIFO_SUCCESS); + stat->aggr = !!(stat1 & MT_TX_STAT_FIFO_AGGR); + stat->ack_req = !!(stat1 & MT_TX_STAT_FIFO_ACKREQ); + stat->wcid = FIELD_GET(MT_TX_STAT_FIFO_WCID, stat1); + stat->rate = FIELD_GET(MT_TX_STAT_FIFO_RATE, stat1); + + stat->retry = FIELD_GET(MT_TX_STAT_FIFO_EXT_RETRY, stat2); + stat->pktid = FIELD_GET(MT_TX_STAT_FIFO_EXT_PKTID, stat2); + + trace_mac_txstat_fetch(dev, stat); + + return true; +} + +static int +mt76x02_mac_process_tx_rate(struct ieee80211_tx_rate *txrate, u16 rate, + enum nl80211_band band) +{ + u8 idx = FIELD_GET(MT_RXWI_RATE_INDEX, rate); + + txrate->idx = 0; + txrate->flags = 0; + txrate->count = 1; + + switch (FIELD_GET(MT_RXWI_RATE_PHY, rate)) { + case MT_PHY_TYPE_OFDM: + if (band == NL80211_BAND_2GHZ) + idx += 4; + + txrate->idx = idx; + return 0; + case MT_PHY_TYPE_CCK: + if (idx >= 8) + idx -= 8; + + txrate->idx = idx; + return 0; + case MT_PHY_TYPE_HT_GF: + txrate->flags |= IEEE80211_TX_RC_GREEN_FIELD; + fallthrough; + case MT_PHY_TYPE_HT: + txrate->flags |= IEEE80211_TX_RC_MCS; + txrate->idx = idx; + break; + case MT_PHY_TYPE_VHT: + txrate->flags |= IEEE80211_TX_RC_VHT_MCS; + txrate->idx = idx; + break; + default: + return -EINVAL; + } + + switch (FIELD_GET(MT_RXWI_RATE_BW, rate)) { + case MT_PHY_BW_20: + break; + case MT_PHY_BW_40: + txrate->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; + break; + case MT_PHY_BW_80: + txrate->flags |= IEEE80211_TX_RC_80_MHZ_WIDTH; + break; + default: + return -EINVAL; + } + + if (rate & MT_RXWI_RATE_SGI) + txrate->flags |= IEEE80211_TX_RC_SHORT_GI; + + return 0; +} + +void mt76x02_mac_write_txwi(struct mt76x02_dev *dev, struct mt76x02_txwi *txwi, + struct sk_buff *skb, struct mt76_wcid *wcid, + struct ieee80211_sta *sta, int len) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_tx_rate *rate = &info->control.rates[0]; + struct ieee80211_key_conf *key = info->control.hw_key; + u32 wcid_tx_info; + u16 rate_ht_mask = FIELD_PREP(MT_RXWI_RATE_PHY, BIT(1) | BIT(2)); + u16 txwi_flags = 0, rateval; + u8 nss; + s8 txpwr_adj, max_txpwr_adj; + u8 ccmp_pn[8], nstreams = dev->mphy.chainmask & 0xf; + + memset(txwi, 0, sizeof(*txwi)); + + mt76_tx_check_agg_ssn(sta, skb); + + if (!info->control.hw_key && wcid && wcid->hw_key_idx != 0xff && + ieee80211_has_protected(hdr->frame_control)) { + wcid = NULL; + ieee80211_get_tx_rates(info->control.vif, sta, skb, + info->control.rates, 1); + } + + if (wcid) + txwi->wcid = wcid->idx; + else + txwi->wcid = 0xff; + + if (wcid && wcid->sw_iv && key) { + u64 pn = atomic64_inc_return(&key->tx_pn); + + ccmp_pn[0] = pn; + ccmp_pn[1] = pn >> 8; + ccmp_pn[2] = 0; + ccmp_pn[3] = 0x20 | (key->keyidx << 6); + ccmp_pn[4] = pn >> 16; + ccmp_pn[5] = pn >> 24; + ccmp_pn[6] = pn >> 32; + ccmp_pn[7] = pn >> 40; + txwi->iv = *((__le32 *)&ccmp_pn[0]); + txwi->eiv = *((__le32 *)&ccmp_pn[4]); + } + + if (wcid && (rate->idx < 0 || !rate->count)) { + wcid_tx_info = wcid->tx_info; + rateval = FIELD_GET(MT_WCID_TX_INFO_RATE, wcid_tx_info); + max_txpwr_adj = FIELD_GET(MT_WCID_TX_INFO_TXPWR_ADJ, + wcid_tx_info); + nss = FIELD_GET(MT_WCID_TX_INFO_NSS, wcid_tx_info); + } else { + rateval = mt76x02_mac_tx_rate_val(dev, rate, &nss); + max_txpwr_adj = mt76x02_tx_get_max_txpwr_adj(dev, rate); + } + txwi->rate = cpu_to_le16(rateval); + + txpwr_adj = mt76x02_tx_get_txpwr_adj(dev, dev->txpower_conf, + max_txpwr_adj); + txwi->ctl2 = FIELD_PREP(MT_TX_PWR_ADJ, txpwr_adj); + + if (nstreams > 1 && mt76_rev(&dev->mt76) >= MT76XX_REV_E4) + txwi->txstream = 0x13; + else if (nstreams > 1 && mt76_rev(&dev->mt76) >= MT76XX_REV_E3 && + !(txwi->rate & cpu_to_le16(rate_ht_mask))) + txwi->txstream = 0x93; + + if (is_mt76x2(dev) && (info->flags & IEEE80211_TX_CTL_LDPC)) + txwi->rate |= cpu_to_le16(MT_RXWI_RATE_LDPC); + if ((info->flags & IEEE80211_TX_CTL_STBC) && nss == 1) + txwi->rate |= cpu_to_le16(MT_RXWI_RATE_STBC); + if (nss > 1 && sta && sta->deflink.smps_mode == IEEE80211_SMPS_DYNAMIC) + txwi_flags |= MT_TXWI_FLAGS_MMPS; + if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) + txwi->ack_ctl |= MT_TXWI_ACK_CTL_REQ; + if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) + txwi->ack_ctl |= MT_TXWI_ACK_CTL_NSEQ; + if ((info->flags & IEEE80211_TX_CTL_AMPDU) && sta) { + u8 ba_size = IEEE80211_MIN_AMPDU_BUF; + u8 ampdu_density = sta->deflink.ht_cap.ampdu_density; + + ba_size <<= sta->deflink.ht_cap.ampdu_factor; + ba_size = min_t(int, 63, ba_size - 1); + if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) + ba_size = 0; + txwi->ack_ctl |= FIELD_PREP(MT_TXWI_ACK_CTL_BA_WINDOW, ba_size); + + if (ampdu_density < IEEE80211_HT_MPDU_DENSITY_4) + ampdu_density = IEEE80211_HT_MPDU_DENSITY_4; + + txwi_flags |= MT_TXWI_FLAGS_AMPDU | + FIELD_PREP(MT_TXWI_FLAGS_MPDU_DENSITY, ampdu_density); + } + + if (ieee80211_is_probe_resp(hdr->frame_control) || + ieee80211_is_beacon(hdr->frame_control)) + txwi_flags |= MT_TXWI_FLAGS_TS; + + txwi->flags |= cpu_to_le16(txwi_flags); + txwi->len_ctl = cpu_to_le16(len); +} +EXPORT_SYMBOL_GPL(mt76x02_mac_write_txwi); + +static void +mt76x02_tx_rate_fallback(struct ieee80211_tx_rate *rates, int idx, int phy) +{ + u8 mcs, nss; + + if (!idx) + return; + + rates += idx - 1; + rates[1] = rates[0]; + switch (phy) { + case MT_PHY_TYPE_VHT: + mcs = ieee80211_rate_get_vht_mcs(rates); + nss = ieee80211_rate_get_vht_nss(rates); + + if (mcs == 0) + nss = max_t(int, nss - 1, 1); + else + mcs--; + + ieee80211_rate_set_vht(rates + 1, mcs, nss); + break; + case MT_PHY_TYPE_HT_GF: + case MT_PHY_TYPE_HT: + /* MCS 8 falls back to MCS 0 */ + if (rates[0].idx == 8) { + rates[1].idx = 0; + break; + } + fallthrough; + default: + rates[1].idx = max_t(int, rates[0].idx - 1, 0); + break; + } +} + +static void +mt76x02_mac_fill_tx_status(struct mt76x02_dev *dev, struct mt76x02_sta *msta, + struct ieee80211_tx_info *info, + struct mt76x02_tx_status *st, int n_frames) +{ + struct ieee80211_tx_rate *rate = info->status.rates; + struct ieee80211_tx_rate last_rate; + u16 first_rate; + int retry = st->retry; + int phy; + int i; + + if (!n_frames) + return; + + phy = FIELD_GET(MT_RXWI_RATE_PHY, st->rate); + + if (st->pktid & MT_PACKET_ID_HAS_RATE) { + first_rate = st->rate & ~MT_PKTID_RATE; + first_rate |= st->pktid & MT_PKTID_RATE; + + mt76x02_mac_process_tx_rate(&rate[0], first_rate, + dev->mphy.chandef.chan->band); + } else if (rate[0].idx < 0) { + if (!msta) + return; + + mt76x02_mac_process_tx_rate(&rate[0], msta->wcid.tx_info, + dev->mphy.chandef.chan->band); + } + + mt76x02_mac_process_tx_rate(&last_rate, st->rate, + dev->mphy.chandef.chan->band); + + for (i = 0; i < ARRAY_SIZE(info->status.rates); i++) { + retry--; + if (i + 1 == ARRAY_SIZE(info->status.rates)) { + info->status.rates[i] = last_rate; + info->status.rates[i].count = max_t(int, retry, 1); + break; + } + + mt76x02_tx_rate_fallback(info->status.rates, i, phy); + if (info->status.rates[i].idx == last_rate.idx) + break; + } + + if (i + 1 < ARRAY_SIZE(info->status.rates)) { + info->status.rates[i + 1].idx = -1; + info->status.rates[i + 1].count = 0; + } + + info->status.ampdu_len = n_frames; + info->status.ampdu_ack_len = st->success ? n_frames : 0; + + if (st->aggr) + info->flags |= IEEE80211_TX_CTL_AMPDU | + IEEE80211_TX_STAT_AMPDU; + + if (!st->ack_req) + info->flags |= IEEE80211_TX_CTL_NO_ACK; + else if (st->success) + info->flags |= IEEE80211_TX_STAT_ACK; +} + +void mt76x02_send_tx_status(struct mt76x02_dev *dev, + struct mt76x02_tx_status *stat, u8 *update) +{ + struct ieee80211_tx_info info = {}; + struct ieee80211_tx_status status = { + .info = &info + }; + static const u8 ac_to_tid[4] = { + [IEEE80211_AC_BE] = 0, + [IEEE80211_AC_BK] = 1, + [IEEE80211_AC_VI] = 4, + [IEEE80211_AC_VO] = 6 + }; + struct mt76_wcid *wcid = NULL; + struct mt76x02_sta *msta = NULL; + struct mt76_dev *mdev = &dev->mt76; + struct sk_buff_head list; + u32 duration = 0; + u8 cur_pktid; + u32 ac = 0; + int len = 0; + + if (stat->pktid == MT_PACKET_ID_NO_ACK) + return; + + rcu_read_lock(); + + wcid = mt76_wcid_ptr(dev, stat->wcid); + if (wcid && wcid->sta) { + void *priv; + + priv = msta = container_of(wcid, struct mt76x02_sta, wcid); + status.sta = container_of(priv, struct ieee80211_sta, + drv_priv); + } + + mt76_tx_status_lock(mdev, &list); + + if (wcid) { + if (mt76_is_skb_pktid(stat->pktid)) + status.skb = mt76_tx_status_skb_get(mdev, wcid, + stat->pktid, &list); + if (status.skb) + status.info = IEEE80211_SKB_CB(status.skb); + } + + if (!status.skb && !(stat->pktid & MT_PACKET_ID_HAS_RATE)) { + mt76_tx_status_unlock(mdev, &list); + goto out; + } + + + if (msta && stat->aggr && !status.skb) { + u32 stat_val, stat_cache; + + stat_val = stat->rate; + stat_val |= ((u32)stat->retry) << 16; + stat_cache = msta->status.rate; + stat_cache |= ((u32)msta->status.retry) << 16; + + if (*update == 0 && stat_val == stat_cache && + stat->wcid == msta->status.wcid && msta->n_frames < 32) { + msta->n_frames++; + mt76_tx_status_unlock(mdev, &list); + goto out; + } + + cur_pktid = msta->status.pktid; + mt76x02_mac_fill_tx_status(dev, msta, status.info, + &msta->status, msta->n_frames); + + msta->status = *stat; + msta->n_frames = 1; + *update = 0; + } else { + cur_pktid = stat->pktid; + mt76x02_mac_fill_tx_status(dev, msta, status.info, stat, 1); + *update = 1; + } + + if (status.skb) { + info = *status.info; + len = status.skb->len; + ac = skb_get_queue_mapping(status.skb); + mt76_tx_status_skb_done(mdev, status.skb, &list); + } else if (msta) { + len = status.info->status.ampdu_len * ewma_pktlen_read(&msta->pktlen); + ac = FIELD_GET(MT_PKTID_AC, cur_pktid); + } + + mt76_tx_status_unlock(mdev, &list); + + if (!status.skb) { + spin_lock_bh(&dev->mt76.rx_lock); + ieee80211_tx_status_ext(mt76_hw(dev), &status); + spin_unlock_bh(&dev->mt76.rx_lock); + } + + if (!len) + goto out; + + duration = ieee80211_calc_tx_airtime(mt76_hw(dev), &info, len); + + spin_lock_bh(&dev->mt76.cc_lock); + dev->tx_airtime += duration; + spin_unlock_bh(&dev->mt76.cc_lock); + + if (msta) + ieee80211_sta_register_airtime(status.sta, ac_to_tid[ac], duration, 0); + +out: + rcu_read_unlock(); +} + +static int +mt76x02_mac_process_rate(struct mt76x02_dev *dev, + struct mt76_rx_status *status, + u16 rate) +{ + u8 idx = FIELD_GET(MT_RXWI_RATE_INDEX, rate); + + switch (FIELD_GET(MT_RXWI_RATE_PHY, rate)) { + case MT_PHY_TYPE_OFDM: + if (idx >= 8) + idx = 0; + + if (status->band == NL80211_BAND_2GHZ) + idx += 4; + + status->rate_idx = idx; + return 0; + case MT_PHY_TYPE_CCK: + if (idx >= 8) { + idx -= 8; + status->enc_flags |= RX_ENC_FLAG_SHORTPRE; + } + + if (idx >= 4) + idx = 0; + + status->rate_idx = idx; + return 0; + case MT_PHY_TYPE_HT_GF: + status->enc_flags |= RX_ENC_FLAG_HT_GF; + fallthrough; + case MT_PHY_TYPE_HT: + status->encoding = RX_ENC_HT; + status->rate_idx = idx; + break; + case MT_PHY_TYPE_VHT: { + u8 n_rxstream = dev->mphy.chainmask & 0xf; + + status->encoding = RX_ENC_VHT; + status->rate_idx = FIELD_GET(MT_RATE_INDEX_VHT_IDX, idx); + status->nss = min_t(u8, n_rxstream, + FIELD_GET(MT_RATE_INDEX_VHT_NSS, idx) + 1); + break; + } + default: + return -EINVAL; + } + + if (rate & MT_RXWI_RATE_LDPC) + status->enc_flags |= RX_ENC_FLAG_LDPC; + + if (rate & MT_RXWI_RATE_SGI) + status->enc_flags |= RX_ENC_FLAG_SHORT_GI; + + if (rate & MT_RXWI_RATE_STBC) + status->enc_flags |= 1 << RX_ENC_FLAG_STBC_SHIFT; + + switch (FIELD_GET(MT_RXWI_RATE_BW, rate)) { + case MT_PHY_BW_20: + break; + case MT_PHY_BW_40: + status->bw = RATE_INFO_BW_40; + break; + case MT_PHY_BW_80: + status->bw = RATE_INFO_BW_80; + break; + default: + break; + } + + return 0; +} + +void mt76x02_mac_setaddr(struct mt76x02_dev *dev, const u8 *addr) +{ + static const u8 null_addr[ETH_ALEN] = {}; + int i; + + ether_addr_copy(dev->mphy.macaddr, addr); + + if (!is_valid_ether_addr(dev->mphy.macaddr)) { + eth_random_addr(dev->mphy.macaddr); + dev_info(dev->mt76.dev, + "Invalid MAC address, using random address %pM\n", + dev->mphy.macaddr); + } + + mt76_wr(dev, MT_MAC_ADDR_DW0, get_unaligned_le32(dev->mphy.macaddr)); + mt76_wr(dev, MT_MAC_ADDR_DW1, + get_unaligned_le16(dev->mphy.macaddr + 4) | + FIELD_PREP(MT_MAC_ADDR_DW1_U2ME_MASK, 0xff)); + + mt76_wr(dev, MT_MAC_BSSID_DW0, + get_unaligned_le32(dev->mphy.macaddr)); + mt76_wr(dev, MT_MAC_BSSID_DW1, + get_unaligned_le16(dev->mphy.macaddr + 4) | + FIELD_PREP(MT_MAC_BSSID_DW1_MBSS_MODE, 3) | /* 8 APs + 8 STAs */ + MT_MAC_BSSID_DW1_MBSS_LOCAL_BIT); + /* enable 7 additional beacon slots and control them with bypass mask */ + mt76_rmw_field(dev, MT_MAC_BSSID_DW1, MT_MAC_BSSID_DW1_MBEACON_N, 7); + + for (i = 0; i < 16; i++) + mt76x02_mac_set_bssid(dev, i, null_addr); +} +EXPORT_SYMBOL_GPL(mt76x02_mac_setaddr); + +static int +mt76x02_mac_get_rssi(struct mt76x02_dev *dev, s8 rssi, int chain) +{ + struct mt76x02_rx_freq_cal *cal = &dev->cal.rx; + + rssi += cal->rssi_offset[chain]; + rssi -= cal->lna_gain; + + return rssi; +} + +int mt76x02_mac_process_rx(struct mt76x02_dev *dev, struct sk_buff *skb, + void *rxi) +{ + struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb; + struct ieee80211_hdr *hdr; + struct mt76x02_rxwi *rxwi = rxi; + struct mt76x02_sta *sta; + u32 rxinfo = le32_to_cpu(rxwi->rxinfo); + u32 ctl = le32_to_cpu(rxwi->ctl); + u16 rate = le16_to_cpu(rxwi->rate); + u16 tid_sn = le16_to_cpu(rxwi->tid_sn); + bool unicast = rxwi->rxinfo & cpu_to_le32(MT_RXINFO_UNICAST); + int pad_len = 0, nstreams = dev->mphy.chainmask & 0xf; + s8 signal; + u8 pn_len; + u8 wcid; + int len; + + if (!test_bit(MT76_STATE_RUNNING, &dev->mphy.state)) + return -EINVAL; + + if (rxinfo & MT_RXINFO_L2PAD) + pad_len += 2; + + if (rxinfo & MT_RXINFO_CRCERR) + status->flag |= RX_FLAG_FAILED_FCS_CRC; + + if (rxinfo & MT_RXINFO_DECRYPT) { + status->flag |= RX_FLAG_DECRYPTED; + status->flag |= RX_FLAG_MMIC_STRIPPED; + status->flag |= RX_FLAG_MIC_STRIPPED; + status->flag |= RX_FLAG_IV_STRIPPED; + } + + wcid = FIELD_GET(MT_RXWI_CTL_WCID, ctl); + sta = mt76x02_rx_get_sta(&dev->mt76, wcid); + status->wcid = mt76x02_rx_get_sta_wcid(sta, unicast); + + len = FIELD_GET(MT_RXWI_CTL_MPDU_LEN, ctl); + pn_len = FIELD_GET(MT_RXINFO_PN_LEN, rxinfo); + if (pn_len) { + int offset = ieee80211_get_hdrlen_from_skb(skb) + pad_len; + u8 *data = skb->data + offset; + + status->iv[0] = data[7]; + status->iv[1] = data[6]; + status->iv[2] = data[5]; + status->iv[3] = data[4]; + status->iv[4] = data[1]; + status->iv[5] = data[0]; + + /* + * Driver CCMP validation can't deal with fragments. + * Let mac80211 take care of it. + */ + if (rxinfo & MT_RXINFO_FRAG) { + status->flag &= ~RX_FLAG_IV_STRIPPED; + } else { + pad_len += pn_len << 2; + len -= pn_len << 2; + } + } + + mt76x02_remove_hdr_pad(skb, pad_len); + + if ((rxinfo & MT_RXINFO_BA) && !(rxinfo & MT_RXINFO_NULL)) + status->aggr = true; + + if (rxinfo & MT_RXINFO_AMPDU) { + status->flag |= RX_FLAG_AMPDU_DETAILS; + status->ampdu_ref = dev->ampdu_ref; + + /* + * When receiving an A-MPDU subframe and RSSI info is not valid, + * we can assume that more subframes belonging to the same A-MPDU + * are coming. The last one will have valid RSSI info + */ + if (rxinfo & MT_RXINFO_RSSI) { + if (!++dev->ampdu_ref) + dev->ampdu_ref++; + } + } + + if (len > skb->len) + return -EINVAL; + + if (pskb_trim(skb, len)) + return -EINVAL; + + status->chains = BIT(0); + signal = mt76x02_mac_get_rssi(dev, rxwi->rssi[0], 0); + status->chain_signal[0] = signal; + if (nstreams > 1) { + status->chains |= BIT(1); + status->chain_signal[1] = mt76x02_mac_get_rssi(dev, + rxwi->rssi[1], + 1); + } + status->freq = dev->mphy.chandef.chan->center_freq; + status->band = dev->mphy.chandef.chan->band; + + hdr = (struct ieee80211_hdr *)skb->data; + status->qos_ctl = *ieee80211_get_qos_ctl(hdr); + status->seqno = FIELD_GET(MT_RXWI_SN, tid_sn); + + return mt76x02_mac_process_rate(dev, status, rate); +} + +void mt76x02_mac_poll_tx_status(struct mt76x02_dev *dev, bool irq) +{ + struct mt76x02_tx_status stat = {}; + u8 update = 1; + bool ret; + + if (!test_bit(MT76_STATE_RUNNING, &dev->mphy.state)) + return; + + trace_mac_txstat_poll(dev); + + while (!irq || !kfifo_is_full(&dev->txstatus_fifo)) { + if (!spin_trylock(&dev->txstatus_fifo_lock)) + break; + + ret = mt76x02_mac_load_tx_status(dev, &stat); + spin_unlock(&dev->txstatus_fifo_lock); + + if (!ret) + break; + + if (!irq) { + mt76x02_send_tx_status(dev, &stat, &update); + continue; + } + + kfifo_put(&dev->txstatus_fifo, stat); + } +} + +void mt76x02_tx_complete_skb(struct mt76_dev *mdev, struct mt76_queue_entry *e) +{ + struct mt76x02_dev *dev = container_of(mdev, struct mt76x02_dev, mt76); + struct mt76x02_txwi *txwi; + u8 *txwi_ptr; + + if (!e->txwi) { + dev_kfree_skb_any(e->skb); + return; + } + + mt76x02_mac_poll_tx_status(dev, false); + + txwi_ptr = mt76_get_txwi_ptr(mdev, e->txwi); + txwi = (struct mt76x02_txwi *)txwi_ptr; + trace_mac_txdone(mdev, txwi->wcid, txwi->pktid); + + mt76_tx_complete_skb(mdev, e->wcid, e->skb); +} +EXPORT_SYMBOL_GPL(mt76x02_tx_complete_skb); + +void mt76x02_mac_set_rts_thresh(struct mt76x02_dev *dev, u32 val) +{ + u32 data = 0; + + if (val != ~0) + data = FIELD_PREP(MT_PROT_CFG_CTRL, 1) | + MT_PROT_CFG_RTS_THRESH; + + mt76_rmw_field(dev, MT_TX_RTS_CFG, MT_TX_RTS_CFG_THRESH, val); + + mt76_rmw(dev, MT_CCK_PROT_CFG, + MT_PROT_CFG_CTRL | MT_PROT_CFG_RTS_THRESH, data); + mt76_rmw(dev, MT_OFDM_PROT_CFG, + MT_PROT_CFG_CTRL | MT_PROT_CFG_RTS_THRESH, data); +} + +void mt76x02_mac_set_tx_protection(struct mt76x02_dev *dev, bool legacy_prot, + int ht_mode) +{ + int mode = ht_mode & IEEE80211_HT_OP_MODE_PROTECTION; + bool non_gf = !!(ht_mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT); + u32 prot[6]; + u32 vht_prot[3]; + int i; + u16 rts_thr; + + for (i = 0; i < ARRAY_SIZE(prot); i++) { + prot[i] = mt76_rr(dev, MT_CCK_PROT_CFG + i * 4); + prot[i] &= ~MT_PROT_CFG_CTRL; + if (i >= 2) + prot[i] &= ~MT_PROT_CFG_RATE; + } + + for (i = 0; i < ARRAY_SIZE(vht_prot); i++) { + vht_prot[i] = mt76_rr(dev, MT_TX_PROT_CFG6 + i * 4); + vht_prot[i] &= ~(MT_PROT_CFG_CTRL | MT_PROT_CFG_RATE); + } + + rts_thr = mt76_get_field(dev, MT_TX_RTS_CFG, MT_TX_RTS_CFG_THRESH); + + if (rts_thr != 0xffff) + prot[0] |= MT_PROT_CTRL_RTS_CTS; + + if (legacy_prot) { + prot[1] |= MT_PROT_CTRL_CTS2SELF; + + prot[2] |= MT_PROT_RATE_CCK_11; + prot[3] |= MT_PROT_RATE_CCK_11; + prot[4] |= MT_PROT_RATE_CCK_11; + prot[5] |= MT_PROT_RATE_CCK_11; + + vht_prot[0] |= MT_PROT_RATE_CCK_11; + vht_prot[1] |= MT_PROT_RATE_CCK_11; + vht_prot[2] |= MT_PROT_RATE_CCK_11; + } else { + if (rts_thr != 0xffff) + prot[1] |= MT_PROT_CTRL_RTS_CTS; + + prot[2] |= MT_PROT_RATE_OFDM_24; + prot[3] |= MT_PROT_RATE_DUP_OFDM_24; + prot[4] |= MT_PROT_RATE_OFDM_24; + prot[5] |= MT_PROT_RATE_DUP_OFDM_24; + + vht_prot[0] |= MT_PROT_RATE_OFDM_24; + vht_prot[1] |= MT_PROT_RATE_DUP_OFDM_24; + vht_prot[2] |= MT_PROT_RATE_SGI_OFDM_24; + } + + switch (mode) { + case IEEE80211_HT_OP_MODE_PROTECTION_NONMEMBER: + case IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED: + prot[2] |= MT_PROT_CTRL_RTS_CTS; + prot[3] |= MT_PROT_CTRL_RTS_CTS; + prot[4] |= MT_PROT_CTRL_RTS_CTS; + prot[5] |= MT_PROT_CTRL_RTS_CTS; + vht_prot[0] |= MT_PROT_CTRL_RTS_CTS; + vht_prot[1] |= MT_PROT_CTRL_RTS_CTS; + vht_prot[2] |= MT_PROT_CTRL_RTS_CTS; + break; + case IEEE80211_HT_OP_MODE_PROTECTION_20MHZ: + prot[3] |= MT_PROT_CTRL_RTS_CTS; + prot[5] |= MT_PROT_CTRL_RTS_CTS; + vht_prot[1] |= MT_PROT_CTRL_RTS_CTS; + vht_prot[2] |= MT_PROT_CTRL_RTS_CTS; + break; + } + + if (non_gf) { + prot[4] |= MT_PROT_CTRL_RTS_CTS; + prot[5] |= MT_PROT_CTRL_RTS_CTS; + } + + for (i = 0; i < ARRAY_SIZE(prot); i++) + mt76_wr(dev, MT_CCK_PROT_CFG + i * 4, prot[i]); + + for (i = 0; i < ARRAY_SIZE(vht_prot); i++) + mt76_wr(dev, MT_TX_PROT_CFG6 + i * 4, vht_prot[i]); +} + +void mt76x02_update_channel(struct mt76_phy *mphy) +{ + struct mt76x02_dev *dev = container_of(mphy->dev, struct mt76x02_dev, mt76); + struct mt76_channel_state *state; + + state = mphy->chan_state; + state->cc_busy += mt76_rr(dev, MT_CH_BUSY); + + spin_lock_bh(&dev->mt76.cc_lock); + state->cc_tx += dev->tx_airtime; + dev->tx_airtime = 0; + spin_unlock_bh(&dev->mt76.cc_lock); +} +EXPORT_SYMBOL_GPL(mt76x02_update_channel); + +static void mt76x02_check_mac_err(struct mt76x02_dev *dev) +{ + if (dev->mt76.beacon_mask) { + if (mt76_rr(dev, MT_TX_STA_0) & MT_TX_STA_0_BEACONS) { + dev->beacon_hang_check = 0; + return; + } + + if (dev->beacon_hang_check < 10) + return; + + } else { + u32 val = mt76_rr(dev, 0x10f4); + if (!(val & BIT(29)) || !(val & (BIT(7) | BIT(5)))) + return; + } + + dev_err(dev->mt76.dev, "MAC error detected\n"); + + mt76_wr(dev, MT_MAC_SYS_CTRL, 0); + if (!mt76x02_wait_for_txrx_idle(&dev->mt76)) { + dev_err(dev->mt76.dev, "MAC stop failed\n"); + goto out; + } + + dev->beacon_hang_check = 0; + mt76_set(dev, MT_MAC_SYS_CTRL, MT_MAC_SYS_CTRL_RESET_CSR); + udelay(10); + +out: + mt76_wr(dev, MT_MAC_SYS_CTRL, + MT_MAC_SYS_CTRL_ENABLE_TX | MT_MAC_SYS_CTRL_ENABLE_RX); +} + +static void +mt76x02_edcca_tx_enable(struct mt76x02_dev *dev, bool enable) +{ + if (enable) { + u32 data; + + mt76_set(dev, MT_MAC_SYS_CTRL, MT_MAC_SYS_CTRL_ENABLE_TX); + mt76_set(dev, MT_AUTO_RSP_CFG, MT_AUTO_RSP_EN); + /* enable pa-lna */ + data = mt76_rr(dev, MT_TX_PIN_CFG); + data |= MT_TX_PIN_CFG_TXANT | + MT_TX_PIN_CFG_RXANT | + MT_TX_PIN_RFTR_EN | + MT_TX_PIN_TRSW_EN; + mt76_wr(dev, MT_TX_PIN_CFG, data); + } else { + mt76_clear(dev, MT_MAC_SYS_CTRL, MT_MAC_SYS_CTRL_ENABLE_TX); + mt76_clear(dev, MT_AUTO_RSP_CFG, MT_AUTO_RSP_EN); + /* disable pa-lna */ + mt76_clear(dev, MT_TX_PIN_CFG, MT_TX_PIN_CFG_TXANT); + mt76_clear(dev, MT_TX_PIN_CFG, MT_TX_PIN_CFG_RXANT); + } + dev->ed_tx_blocked = !enable; +} + +void mt76x02_edcca_init(struct mt76x02_dev *dev) +{ + dev->ed_trigger = 0; + dev->ed_silent = 0; + + if (dev->ed_monitor) { + struct ieee80211_channel *chan = dev->mphy.chandef.chan; + u8 ed_th = chan->band == NL80211_BAND_5GHZ ? 0x0e : 0x20; + + mt76_clear(dev, MT_TX_LINK_CFG, MT_TX_CFACK_EN); + mt76_set(dev, MT_TXOP_CTRL_CFG, MT_TXOP_ED_CCA_EN); + mt76_rmw(dev, MT_BBP(AGC, 2), GENMASK(15, 0), + ed_th << 8 | ed_th); + mt76_set(dev, MT_TXOP_HLDR_ET, MT_TXOP_HLDR_TX40M_BLK_EN); + } else { + mt76_set(dev, MT_TX_LINK_CFG, MT_TX_CFACK_EN); + mt76_clear(dev, MT_TXOP_CTRL_CFG, MT_TXOP_ED_CCA_EN); + if (is_mt76x2(dev)) { + mt76_wr(dev, MT_BBP(AGC, 2), 0x00007070); + mt76_set(dev, MT_TXOP_HLDR_ET, + MT_TXOP_HLDR_TX40M_BLK_EN); + } else { + mt76_wr(dev, MT_BBP(AGC, 2), 0x003a6464); + mt76_clear(dev, MT_TXOP_HLDR_ET, + MT_TXOP_HLDR_TX40M_BLK_EN); + } + } + mt76x02_edcca_tx_enable(dev, true); + dev->ed_monitor_learning = true; + + /* clear previous CCA timer value */ + mt76_rr(dev, MT_ED_CCA_TIMER); + dev->ed_time = ktime_get_boottime(); +} +EXPORT_SYMBOL_GPL(mt76x02_edcca_init); + +#define MT_EDCCA_TH 92 +#define MT_EDCCA_BLOCK_TH 2 +#define MT_EDCCA_LEARN_TH 50 +#define MT_EDCCA_LEARN_CCA 180 +#define MT_EDCCA_LEARN_TIMEOUT (20 * HZ) + +static void mt76x02_edcca_check(struct mt76x02_dev *dev) +{ + ktime_t cur_time; + u32 active, val, busy; + + cur_time = ktime_get_boottime(); + val = mt76_rr(dev, MT_ED_CCA_TIMER); + + active = ktime_to_us(ktime_sub(cur_time, dev->ed_time)); + dev->ed_time = cur_time; + + busy = (val * 100) / active; + busy = min_t(u32, busy, 100); + + if (busy > MT_EDCCA_TH) { + dev->ed_trigger++; + dev->ed_silent = 0; + } else { + dev->ed_silent++; + dev->ed_trigger = 0; + } + + if (dev->cal.agc_lowest_gain && + dev->cal.false_cca > MT_EDCCA_LEARN_CCA && + dev->ed_trigger > MT_EDCCA_LEARN_TH) { + dev->ed_monitor_learning = false; + dev->ed_trigger_timeout = jiffies + 20 * HZ; + } else if (!dev->ed_monitor_learning && + time_is_after_jiffies(dev->ed_trigger_timeout)) { + dev->ed_monitor_learning = true; + mt76x02_edcca_tx_enable(dev, true); + } + + if (dev->ed_monitor_learning) + return; + + if (dev->ed_trigger > MT_EDCCA_BLOCK_TH && !dev->ed_tx_blocked) + mt76x02_edcca_tx_enable(dev, false); + else if (dev->ed_silent > MT_EDCCA_BLOCK_TH && dev->ed_tx_blocked) + mt76x02_edcca_tx_enable(dev, true); +} + +void mt76x02_mac_work(struct work_struct *work) +{ + struct mt76x02_dev *dev = container_of(work, struct mt76x02_dev, + mphy.mac_work.work); + int i, idx; + + mutex_lock(&dev->mt76.mutex); + + mt76_update_survey(&dev->mphy); + for (i = 0, idx = 0; i < 16; i++) { + u32 val = mt76_rr(dev, MT_TX_AGG_CNT(i)); + + dev->mphy.aggr_stats[idx++] += val & 0xffff; + dev->mphy.aggr_stats[idx++] += val >> 16; + } + + mt76x02_check_mac_err(dev); + + if (dev->ed_monitor) + mt76x02_edcca_check(dev); + + mutex_unlock(&dev->mt76.mutex); + + mt76_tx_status_check(&dev->mt76, false); + + ieee80211_queue_delayed_work(mt76_hw(dev), &dev->mphy.mac_work, + MT_MAC_WORK_INTERVAL); +} + +void mt76x02_mac_cc_reset(struct mt76x02_dev *dev) +{ + dev->mphy.survey_time = ktime_get_boottime(); + + mt76_wr(dev, MT_CH_TIME_CFG, + MT_CH_TIME_CFG_TIMER_EN | + MT_CH_TIME_CFG_TX_AS_BUSY | + MT_CH_TIME_CFG_RX_AS_BUSY | + MT_CH_TIME_CFG_NAV_AS_BUSY | + MT_CH_TIME_CFG_EIFS_AS_BUSY | + MT_CH_CCA_RC_EN | + FIELD_PREP(MT_CH_TIME_CFG_CH_TIMER_CLR, 1)); + + /* channel cycle counters read-and-clear */ + mt76_rr(dev, MT_CH_BUSY); + mt76_rr(dev, MT_CH_IDLE); +} +EXPORT_SYMBOL_GPL(mt76x02_mac_cc_reset); + +void mt76x02_mac_set_bssid(struct mt76x02_dev *dev, u8 idx, const u8 *addr) +{ + idx &= 7; + mt76_wr(dev, MT_MAC_APC_BSSID_L(idx), get_unaligned_le32(addr)); + mt76_rmw_field(dev, MT_MAC_APC_BSSID_H(idx), MT_MAC_APC_BSSID_H_ADDR, + get_unaligned_le16(addr + 4)); +} diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_mac.h b/drivers/net/wireless/mediatek/mt76/mt76x02_mac.h new file mode 100644 index 000000000..778454ac8 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_mac.h @@ -0,0 +1,208 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (C) 2016 Felix Fietkau + * Copyright (C) 2018 Stanislaw Gruszka + */ + +#ifndef __MT76X02_MAC_H +#define __MT76X02_MAC_H + +struct mt76x02_dev; + +struct mt76x02_tx_status { + u8 valid:1; + u8 success:1; + u8 aggr:1; + u8 ack_req:1; + u8 wcid; + u8 pktid; + u8 retry; + u16 rate; +} __packed __aligned(2); + +#define MT_VIF_WCID(_n) (254 - ((_n) & 7)) +#define MT_MAX_VIFS 8 + +#define MT_PKTID_RATE GENMASK(4, 0) +#define MT_PKTID_AC GENMASK(6, 5) + +struct mt76x02_vif { + struct mt76_wcid group_wcid; /* must be first */ + u8 idx; +}; + +DECLARE_EWMA(pktlen, 8, 8); + +struct mt76x02_sta { + struct mt76_wcid wcid; /* must be first */ + + struct mt76x02_vif *vif; + struct mt76x02_tx_status status; + int n_frames; + + struct ewma_pktlen pktlen; +}; + +#define MT_RXINFO_BA BIT(0) +#define MT_RXINFO_DATA BIT(1) +#define MT_RXINFO_NULL BIT(2) +#define MT_RXINFO_FRAG BIT(3) +#define MT_RXINFO_UNICAST BIT(4) +#define MT_RXINFO_MULTICAST BIT(5) +#define MT_RXINFO_BROADCAST BIT(6) +#define MT_RXINFO_MYBSS BIT(7) +#define MT_RXINFO_CRCERR BIT(8) +#define MT_RXINFO_ICVERR BIT(9) +#define MT_RXINFO_MICERR BIT(10) +#define MT_RXINFO_AMSDU BIT(11) +#define MT_RXINFO_HTC BIT(12) +#define MT_RXINFO_RSSI BIT(13) +#define MT_RXINFO_L2PAD BIT(14) +#define MT_RXINFO_AMPDU BIT(15) +#define MT_RXINFO_DECRYPT BIT(16) +#define MT_RXINFO_BSSIDX3 BIT(17) +#define MT_RXINFO_WAPI_KEY BIT(18) +#define MT_RXINFO_PN_LEN GENMASK(21, 19) +#define MT_RXINFO_SW_FTYPE0 BIT(22) +#define MT_RXINFO_SW_FTYPE1 BIT(23) +#define MT_RXINFO_PROBE_RESP BIT(24) +#define MT_RXINFO_BEACON BIT(25) +#define MT_RXINFO_DISASSOC BIT(26) +#define MT_RXINFO_DEAUTH BIT(27) +#define MT_RXINFO_ACTION BIT(28) +#define MT_RXINFO_TCP_SUM_ERR BIT(30) +#define MT_RXINFO_IP_SUM_ERR BIT(31) + +#define MT_RXWI_CTL_WCID GENMASK(7, 0) +#define MT_RXWI_CTL_KEY_IDX GENMASK(9, 8) +#define MT_RXWI_CTL_BSS_IDX GENMASK(12, 10) +#define MT_RXWI_CTL_UDF GENMASK(15, 13) +#define MT_RXWI_CTL_MPDU_LEN GENMASK(29, 16) +#define MT_RXWI_CTL_EOF BIT(31) + +#define MT_RXWI_TID GENMASK(3, 0) +#define MT_RXWI_SN GENMASK(15, 4) + +#define MT_RXWI_RATE_INDEX GENMASK(5, 0) +#define MT_RXWI_RATE_LDPC BIT(6) +#define MT_RXWI_RATE_BW GENMASK(8, 7) +#define MT_RXWI_RATE_SGI BIT(9) +#define MT_RXWI_RATE_STBC BIT(10) +#define MT_RXWI_RATE_LDPC_EXSYM BIT(11) +#define MT_RXWI_RATE_PHY GENMASK(15, 13) + +#define MT_RATE_INDEX_VHT_IDX GENMASK(3, 0) +#define MT_RATE_INDEX_VHT_NSS GENMASK(5, 4) + +struct mt76x02_rxwi { + __le32 rxinfo; + + __le32 ctl; + + __le16 tid_sn; + __le16 rate; + + u8 rssi[4]; + + __le32 bbp_rxinfo[4]; +}; + +#define MT_TX_PWR_ADJ GENMASK(3, 0) + +enum mt76x2_phy_bandwidth { + MT_PHY_BW_20, + MT_PHY_BW_40, + MT_PHY_BW_80, +}; + +#define MT_TXWI_FLAGS_FRAG BIT(0) +#define MT_TXWI_FLAGS_MMPS BIT(1) +#define MT_TXWI_FLAGS_CFACK BIT(2) +#define MT_TXWI_FLAGS_TS BIT(3) +#define MT_TXWI_FLAGS_AMPDU BIT(4) +#define MT_TXWI_FLAGS_MPDU_DENSITY GENMASK(7, 5) +#define MT_TXWI_FLAGS_TXOP GENMASK(9, 8) +#define MT_TXWI_FLAGS_NDPS BIT(10) +#define MT_TXWI_FLAGS_RTSBWSIG BIT(11) +#define MT_TXWI_FLAGS_NDP_BW GENMASK(13, 12) +#define MT_TXWI_FLAGS_SOUND BIT(14) +#define MT_TXWI_FLAGS_TX_RATE_LUT BIT(15) + +#define MT_TXWI_ACK_CTL_REQ BIT(0) +#define MT_TXWI_ACK_CTL_NSEQ BIT(1) +#define MT_TXWI_ACK_CTL_BA_WINDOW GENMASK(7, 2) + +struct mt76x02_txwi { + __le16 flags; + __le16 rate; + u8 ack_ctl; + u8 wcid; + __le16 len_ctl; + __le32 iv; + __le32 eiv; + u8 aid; + u8 txstream; + u8 ctl2; + u8 pktid; +} __packed __aligned(4); + +static inline bool mt76x02_wait_for_mac(struct mt76_dev *dev) +{ + const u32 MAC_CSR0 = 0x1000; + int i; + + for (i = 0; i < 500; i++) { + if (test_bit(MT76_REMOVED, &dev->phy.state)) + return false; + + switch (dev->bus->rr(dev, MAC_CSR0)) { + case 0: + case ~0: + break; + default: + return true; + } + usleep_range(5000, 10000); + } + return false; +} + +void mt76x02_mac_reset_counters(struct mt76x02_dev *dev); +void mt76x02_mac_set_short_preamble(struct mt76x02_dev *dev, bool enable); +int mt76x02_mac_shared_key_setup(struct mt76x02_dev *dev, u8 vif_idx, + u8 key_idx, struct ieee80211_key_conf *key); +int mt76x02_mac_wcid_set_key(struct mt76x02_dev *dev, u8 idx, + struct ieee80211_key_conf *key); +void mt76x02_mac_wcid_sync_pn(struct mt76x02_dev *dev, u8 idx, + struct ieee80211_key_conf *key); +void mt76x02_mac_wcid_setup(struct mt76x02_dev *dev, u8 idx, u8 vif_idx, + u8 *mac); +void mt76x02_mac_wcid_set_drop(struct mt76x02_dev *dev, u8 idx, bool drop); +void mt76x02_mac_wcid_set_rate(struct mt76x02_dev *dev, struct mt76_wcid *wcid, + const struct ieee80211_tx_rate *rate); +bool mt76x02_mac_load_tx_status(struct mt76x02_dev *dev, + struct mt76x02_tx_status *stat); +void mt76x02_send_tx_status(struct mt76x02_dev *dev, + struct mt76x02_tx_status *stat, u8 *update); +int mt76x02_mac_process_rx(struct mt76x02_dev *dev, struct sk_buff *skb, + void *rxi); +void mt76x02_mac_set_tx_protection(struct mt76x02_dev *dev, bool legacy_prot, + int ht_mode); +void mt76x02_mac_set_rts_thresh(struct mt76x02_dev *dev, u32 val); +void mt76x02_mac_setaddr(struct mt76x02_dev *dev, const u8 *addr); +void mt76x02_mac_write_txwi(struct mt76x02_dev *dev, struct mt76x02_txwi *txwi, + struct sk_buff *skb, struct mt76_wcid *wcid, + struct ieee80211_sta *sta, int len); +void mt76x02_mac_poll_tx_status(struct mt76x02_dev *dev, bool irq); +void mt76x02_tx_complete_skb(struct mt76_dev *mdev, struct mt76_queue_entry *e); +void mt76x02_update_channel(struct mt76_phy *mphy); +void mt76x02_mac_work(struct work_struct *work); + +void mt76x02_mac_cc_reset(struct mt76x02_dev *dev); +void mt76x02_mac_set_bssid(struct mt76x02_dev *dev, u8 idx, const u8 *addr); +void mt76x02_mac_set_beacon(struct mt76x02_dev *dev, struct sk_buff *skb); +void mt76x02_mac_set_beacon_enable(struct mt76x02_dev *dev, + struct ieee80211_vif *vif, bool enable); + +void mt76x02_edcca_init(struct mt76x02_dev *dev); +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76x02_mcu.c new file mode 100644 index 000000000..e16f06a28 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_mcu.c @@ -0,0 +1,171 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (C) 2016 Felix Fietkau + * Copyright (C) 2018 Lorenzo Bianconi + */ + +#include +#include +#include + +#include "mt76x02_mcu.h" + +int mt76x02_mcu_parse_response(struct mt76_dev *mdev, int cmd, + struct sk_buff *skb, int seq) +{ + struct mt76x02_dev *dev = container_of(mdev, struct mt76x02_dev, mt76); + u32 *rxfce; + + if (!skb) { + dev_err(mdev->dev, "MCU message %02x (seq %d) timed out\n", + abs(cmd), seq); + dev->mcu_timeout = 1; + return -ETIMEDOUT; + } + + rxfce = (u32 *)skb->cb; + if (seq != FIELD_GET(MT_RX_FCE_INFO_CMD_SEQ, *rxfce)) + return -EAGAIN; + + return 0; +} +EXPORT_SYMBOL_GPL(mt76x02_mcu_parse_response); + +int mt76x02_mcu_msg_send(struct mt76_dev *mdev, int cmd, const void *data, + int len, bool wait_resp) +{ + struct mt76x02_dev *dev = container_of(mdev, struct mt76x02_dev, mt76); + unsigned long expires = jiffies + HZ; + struct sk_buff *skb; + u32 tx_info; + int ret; + u8 seq; + + if (dev->mcu_timeout) + return -EIO; + + skb = mt76_mcu_msg_alloc(mdev, data, len); + if (!skb) + return -ENOMEM; + + mutex_lock(&mdev->mcu.mutex); + + seq = ++mdev->mcu.msg_seq & 0xf; + if (!seq) + seq = ++mdev->mcu.msg_seq & 0xf; + + tx_info = MT_MCU_MSG_TYPE_CMD | + FIELD_PREP(MT_MCU_MSG_CMD_TYPE, cmd) | + FIELD_PREP(MT_MCU_MSG_CMD_SEQ, seq) | + FIELD_PREP(MT_MCU_MSG_PORT, CPU_TX_PORT) | + FIELD_PREP(MT_MCU_MSG_LEN, skb->len); + + ret = mt76_tx_queue_skb_raw(dev, mdev->q_mcu[MT_MCUQ_WM], skb, tx_info); + if (ret) + goto out; + + while (wait_resp) { + skb = mt76_mcu_get_response(&dev->mt76, expires); + ret = mt76x02_mcu_parse_response(mdev, cmd, skb, seq); + dev_kfree_skb(skb); + if (ret != -EAGAIN) + break; + } + +out: + mutex_unlock(&mdev->mcu.mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(mt76x02_mcu_msg_send); + +int mt76x02_mcu_function_select(struct mt76x02_dev *dev, enum mcu_function func, + u32 val) +{ + struct { + __le32 id; + __le32 value; + } __packed __aligned(4) msg = { + .id = cpu_to_le32(func), + .value = cpu_to_le32(val), + }; + bool wait = false; + + if (func != Q_SELECT) + wait = true; + + return mt76_mcu_send_msg(&dev->mt76, CMD_FUN_SET_OP, &msg, + sizeof(msg), wait); +} +EXPORT_SYMBOL_GPL(mt76x02_mcu_function_select); + +int mt76x02_mcu_set_radio_state(struct mt76x02_dev *dev, bool on) +{ + struct { + __le32 mode; + __le32 level; + } __packed __aligned(4) msg = { + .mode = cpu_to_le32(on ? RADIO_ON : RADIO_OFF), + .level = cpu_to_le32(0), + }; + + return mt76_mcu_send_msg(&dev->mt76, CMD_POWER_SAVING_OP, &msg, + sizeof(msg), false); +} +EXPORT_SYMBOL_GPL(mt76x02_mcu_set_radio_state); + +int mt76x02_mcu_calibrate(struct mt76x02_dev *dev, int type, u32 param) +{ + struct { + __le32 id; + __le32 value; + } __packed __aligned(4) msg = { + .id = cpu_to_le32(type), + .value = cpu_to_le32(param), + }; + bool is_mt76x2e = mt76_is_mmio(&dev->mt76) && is_mt76x2(dev); + int ret; + + if (is_mt76x2e) + mt76_rmw(dev, MT_MCU_COM_REG0, BIT(31), 0); + + ret = mt76_mcu_send_msg(&dev->mt76, CMD_CALIBRATION_OP, &msg, + sizeof(msg), true); + if (ret) + return ret; + + if (is_mt76x2e && + WARN_ON(!mt76_poll_msec(dev, MT_MCU_COM_REG0, + BIT(31), BIT(31), 100))) + return -ETIMEDOUT; + + return 0; +} +EXPORT_SYMBOL_GPL(mt76x02_mcu_calibrate); + +int mt76x02_mcu_cleanup(struct mt76x02_dev *dev) +{ + struct sk_buff *skb; + + mt76_wr(dev, MT_MCU_INT_LEVEL, 1); + usleep_range(20000, 30000); + + while ((skb = skb_dequeue(&dev->mt76.mcu.res_q)) != NULL) + dev_kfree_skb(skb); + + return 0; +} +EXPORT_SYMBOL_GPL(mt76x02_mcu_cleanup); + +void mt76x02_set_ethtool_fwver(struct mt76x02_dev *dev, + const struct mt76x02_fw_header *h) +{ + u16 bld = le16_to_cpu(h->build_ver); + u16 ver = le16_to_cpu(h->fw_ver); + + snprintf(dev->mt76.hw->wiphy->fw_version, + sizeof(dev->mt76.hw->wiphy->fw_version), + "%d.%d.%02d-b%x", + (ver >> 12) & 0xf, (ver >> 8) & 0xf, ver & 0xf, bld); +} +EXPORT_SYMBOL_GPL(mt76x02_set_ethtool_fwver); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76x02_mcu.h new file mode 100644 index 000000000..a422cdc52 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_mcu.h @@ -0,0 +1,100 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (C) 2018 Lorenzo Bianconi + */ + +#ifndef __MT76x02_MCU_H +#define __MT76x02_MCU_H + +#include "mt76x02.h" + +#define MT_MCU_RESET_CTL 0x070C +#define MT_MCU_INT_LEVEL 0x0718 +#define MT_MCU_COM_REG0 0x0730 +#define MT_MCU_COM_REG1 0x0734 +#define MT_MCU_COM_REG2 0x0738 +#define MT_MCU_COM_REG3 0x073C + +#define MT_INBAND_PACKET_MAX_LEN 192 +#define MT_MCU_MEMMAP_WLAN 0x410000 + +#define MT_MCU_PCIE_REMAP_BASE4 0x074C + +#define MT_MCU_SEMAPHORE_00 0x07B0 +#define MT_MCU_SEMAPHORE_01 0x07B4 +#define MT_MCU_SEMAPHORE_02 0x07B8 +#define MT_MCU_SEMAPHORE_03 0x07BC + +#define MT_MCU_ILM_ADDR 0x80000 + +enum mcu_cmd { + CMD_FUN_SET_OP = 1, + CMD_LOAD_CR = 2, + CMD_INIT_GAIN_OP = 3, + CMD_DYNC_VGA_OP = 6, + CMD_TDLS_CH_SW = 7, + CMD_BURST_WRITE = 8, + CMD_READ_MODIFY_WRITE = 9, + CMD_RANDOM_READ = 10, + CMD_BURST_READ = 11, + CMD_RANDOM_WRITE = 12, + CMD_LED_MODE_OP = 16, + CMD_POWER_SAVING_OP = 20, + CMD_WOW_CONFIG = 21, + CMD_WOW_QUERY = 22, + CMD_WOW_FEATURE = 24, + CMD_CARRIER_DETECT_OP = 28, + CMD_RADOR_DETECT_OP = 29, + CMD_SWITCH_CHANNEL_OP = 30, + CMD_CALIBRATION_OP = 31, + CMD_BEACON_OP = 32, + CMD_ANTENNA_OP = 33, +}; + +enum mcu_power_mode { + RADIO_OFF = 0x30, + RADIO_ON = 0x31, + RADIO_OFF_AUTO_WAKEUP = 0x32, + RADIO_OFF_ADVANCE = 0x33, + RADIO_ON_ADVANCE = 0x34, +}; + +enum mcu_function { + Q_SELECT = 1, + BW_SETTING = 2, + USB2_SW_DISCONNECT = 2, + USB3_SW_DISCONNECT = 3, + LOG_FW_DEBUG_MSG = 4, + GET_FW_VERSION = 5, +}; + +struct mt76x02_fw_header { + __le32 ilm_len; + __le32 dlm_len; + __le16 build_ver; + __le16 fw_ver; + u8 pad[4]; + char build_time[16]; +}; + +struct mt76x02_patch_header { + char build_time[16]; + char platform[4]; + char hw_version[4]; + char patch_version[4]; + u8 pad[2]; +}; + +int mt76x02_mcu_cleanup(struct mt76x02_dev *dev); +int mt76x02_mcu_calibrate(struct mt76x02_dev *dev, int type, u32 param); +int mt76x02_mcu_msg_send(struct mt76_dev *mdev, int cmd, const void *data, + int len, bool wait_resp); +int mt76x02_mcu_parse_response(struct mt76_dev *mdev, int cmd, + struct sk_buff *skb, int seq); +int mt76x02_mcu_function_select(struct mt76x02_dev *dev, enum mcu_function func, + u32 val); +int mt76x02_mcu_set_radio_state(struct mt76x02_dev *dev, bool on); +void mt76x02_set_ethtool_fwver(struct mt76x02_dev *dev, + const struct mt76x02_fw_header *h); + +#endif /* __MT76x02_MCU_H */ diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c b/drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c new file mode 100644 index 000000000..dc7c03d23 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c @@ -0,0 +1,566 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (C) 2016 Felix Fietkau + * Copyright (C) 2018 Lorenzo Bianconi + */ + +#include +#include + +#include "mt76x02.h" +#include "mt76x02_mcu.h" +#include "trace.h" + +static void mt76x02_pre_tbtt_tasklet(struct tasklet_struct *t) +{ + struct mt76x02_dev *dev = from_tasklet(dev, t, mt76.pre_tbtt_tasklet); + struct mt76_dev *mdev = &dev->mt76; + struct mt76_queue *q = dev->mphy.q_tx[MT_TXQ_PSD]; + struct beacon_bc_data data = { + .dev = dev, + }; + struct sk_buff *skb; + int i; + + if (dev->mphy.offchannel) + return; + + __skb_queue_head_init(&data.q); + + mt76x02_resync_beacon_timer(dev); + + /* Prevent corrupt transmissions during update */ + mt76_set(dev, MT_BCN_BYPASS_MASK, 0xffff); + dev->beacon_data_count = 0; + + ieee80211_iterate_active_interfaces_atomic(mt76_hw(dev), + IEEE80211_IFACE_ITER_RESUME_ALL, + mt76x02_update_beacon_iter, &data); + + while ((skb = __skb_dequeue(&data.q)) != NULL) + mt76x02_mac_set_beacon(dev, skb); + + mt76_wr(dev, MT_BCN_BYPASS_MASK, + 0xff00 | ~(0xff00 >> dev->beacon_data_count)); + + mt76_csa_check(mdev); + + if (mdev->csa_complete) + return; + + mt76x02_enqueue_buffered_bc(dev, &data, 8); + + if (!skb_queue_len(&data.q)) + return; + + for (i = 0; i < ARRAY_SIZE(data.tail); i++) { + if (!data.tail[i]) + continue; + + mt76_skb_set_moredata(data.tail[i], false); + } + + spin_lock(&q->lock); + while ((skb = __skb_dequeue(&data.q)) != NULL) { + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_vif *vif = info->control.vif; + struct mt76x02_vif *mvif = (struct mt76x02_vif *)vif->drv_priv; + + mt76_tx_queue_skb(dev, q, MT_TXQ_PSD, skb, &mvif->group_wcid, + NULL); + } + spin_unlock(&q->lock); +} + +static void mt76x02e_pre_tbtt_enable(struct mt76x02_dev *dev, bool en) +{ + if (en) + tasklet_enable(&dev->mt76.pre_tbtt_tasklet); + else + tasklet_disable(&dev->mt76.pre_tbtt_tasklet); +} + +static void mt76x02e_beacon_enable(struct mt76x02_dev *dev, bool en) +{ + mt76_rmw_field(dev, MT_INT_TIMER_EN, MT_INT_TIMER_EN_PRE_TBTT_EN, en); + if (en) + mt76x02_irq_enable(dev, MT_INT_PRE_TBTT | MT_INT_TBTT); + else + mt76x02_irq_disable(dev, MT_INT_PRE_TBTT | MT_INT_TBTT); +} + +void mt76x02e_init_beacon_config(struct mt76x02_dev *dev) +{ + static const struct mt76x02_beacon_ops beacon_ops = { + .nslots = 8, + .slot_size = 1024, + .pre_tbtt_enable = mt76x02e_pre_tbtt_enable, + .beacon_enable = mt76x02e_beacon_enable, + }; + + dev->beacon_ops = &beacon_ops; + + /* Fire a pre-TBTT interrupt 8 ms before TBTT */ + mt76_rmw_field(dev, MT_INT_TIMER_CFG, MT_INT_TIMER_CFG_PRE_TBTT, + 8 << 4); + mt76_rmw_field(dev, MT_INT_TIMER_CFG, MT_INT_TIMER_CFG_GP_TIMER, + MT_DFS_GP_INTERVAL); + mt76_wr(dev, MT_INT_TIMER_EN, 0); + + mt76x02_init_beacon_config(dev); +} +EXPORT_SYMBOL_GPL(mt76x02e_init_beacon_config); + +static int +mt76x02_init_rx_queue(struct mt76x02_dev *dev, struct mt76_queue *q, + int idx, int n_desc, int bufsize) +{ + int err; + + err = mt76_queue_alloc(dev, q, idx, n_desc, bufsize, + MT_RX_RING_BASE); + if (err < 0) + return err; + + mt76x02_irq_enable(dev, MT_INT_RX_DONE(idx)); + + return 0; +} + +static void mt76x02_process_tx_status_fifo(struct mt76x02_dev *dev) +{ + struct mt76x02_tx_status stat; + u8 update = 1; + + while (kfifo_get(&dev->txstatus_fifo, &stat)) + mt76x02_send_tx_status(dev, &stat, &update); +} + +static void mt76x02_tx_worker(struct mt76_worker *w) +{ + struct mt76x02_dev *dev; + + dev = container_of(w, struct mt76x02_dev, mt76.tx_worker); + + mt76x02_mac_poll_tx_status(dev, false); + mt76x02_process_tx_status_fifo(dev); + + mt76_txq_schedule_all(&dev->mphy); +} + +static int mt76x02_poll_tx(struct napi_struct *napi, int budget) +{ + struct mt76x02_dev *dev = container_of(napi, struct mt76x02_dev, + mt76.tx_napi); + int i; + + mt76x02_mac_poll_tx_status(dev, false); + + mt76_queue_tx_cleanup(dev, dev->mt76.q_mcu[MT_MCUQ_WM], false); + for (i = MT_TXQ_PSD; i >= 0; i--) + mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[i], false); + + if (napi_complete_done(napi, 0)) + mt76x02_irq_enable(dev, MT_INT_TX_DONE_ALL); + + mt76_queue_tx_cleanup(dev, dev->mt76.q_mcu[MT_MCUQ_WM], false); + for (i = MT_TXQ_PSD; i >= 0; i--) + mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[i], false); + + mt76_worker_schedule(&dev->mt76.tx_worker); + + return 0; +} + +int mt76x02_dma_init(struct mt76x02_dev *dev) +{ + int i, ret, fifo_size; + struct mt76_queue *q; + void *status_fifo; + + BUILD_BUG_ON(sizeof(struct mt76x02_rxwi) > MT_RX_HEADROOM); + + fifo_size = roundup_pow_of_two(32 * sizeof(struct mt76x02_tx_status)); + status_fifo = devm_kzalloc(dev->mt76.dev, fifo_size, GFP_KERNEL); + if (!status_fifo) + return -ENOMEM; + + dev->mt76.tx_worker.fn = mt76x02_tx_worker; + tasklet_setup(&dev->mt76.pre_tbtt_tasklet, mt76x02_pre_tbtt_tasklet); + + spin_lock_init(&dev->txstatus_fifo_lock); + kfifo_init(&dev->txstatus_fifo, status_fifo, fifo_size); + + mt76_dma_attach(&dev->mt76); + + mt76_wr(dev, MT_WPDMA_RST_IDX, ~0); + + for (i = 0; i < IEEE80211_NUM_ACS; i++) { + ret = mt76_init_tx_queue(&dev->mphy, i, mt76_ac_to_hwq(i), + MT76x02_TX_RING_SIZE, + MT_TX_RING_BASE, NULL, 0); + if (ret) + return ret; + } + + ret = mt76_init_tx_queue(&dev->mphy, MT_TXQ_PSD, MT_TX_HW_QUEUE_MGMT, + MT76x02_PSD_RING_SIZE, MT_TX_RING_BASE, + NULL, 0); + if (ret) + return ret; + + ret = mt76_init_mcu_queue(&dev->mt76, MT_MCUQ_WM, MT_TX_HW_QUEUE_MCU, + MT_MCU_RING_SIZE, MT_TX_RING_BASE); + if (ret) + return ret; + + mt76x02_irq_enable(dev, + MT_INT_TX_DONE(IEEE80211_AC_VO) | + MT_INT_TX_DONE(IEEE80211_AC_VI) | + MT_INT_TX_DONE(IEEE80211_AC_BE) | + MT_INT_TX_DONE(IEEE80211_AC_BK) | + MT_INT_TX_DONE(MT_TX_HW_QUEUE_MGMT) | + MT_INT_TX_DONE(MT_TX_HW_QUEUE_MCU)); + + ret = mt76x02_init_rx_queue(dev, &dev->mt76.q_rx[MT_RXQ_MCU], 1, + MT_MCU_RING_SIZE, MT_RX_BUF_SIZE); + if (ret) + return ret; + + q = &dev->mt76.q_rx[MT_RXQ_MAIN]; + q->buf_offset = MT_RX_HEADROOM - sizeof(struct mt76x02_rxwi); + ret = mt76x02_init_rx_queue(dev, q, 0, MT76X02_RX_RING_SIZE, + MT_RX_BUF_SIZE); + if (ret) + return ret; + + ret = mt76_init_queues(dev, mt76_dma_rx_poll); + if (ret) + return ret; + + netif_napi_add_tx(dev->mt76.tx_napi_dev, &dev->mt76.tx_napi, + mt76x02_poll_tx); + napi_enable(&dev->mt76.tx_napi); + + return 0; +} +EXPORT_SYMBOL_GPL(mt76x02_dma_init); + +void mt76x02_rx_poll_complete(struct mt76_dev *mdev, enum mt76_rxq_id q) +{ + struct mt76x02_dev *dev; + + dev = container_of(mdev, struct mt76x02_dev, mt76); + mt76x02_irq_enable(dev, MT_INT_RX_DONE(q)); +} +EXPORT_SYMBOL_GPL(mt76x02_rx_poll_complete); + +irqreturn_t mt76x02_irq_handler(int irq, void *dev_instance) +{ + struct mt76x02_dev *dev = dev_instance; + u32 intr, mask; + + intr = mt76_rr(dev, MT_INT_SOURCE_CSR); + intr &= dev->mt76.mmio.irqmask; + mt76_wr(dev, MT_INT_SOURCE_CSR, intr); + + if (!test_bit(MT76_STATE_INITIALIZED, &dev->mphy.state)) + return IRQ_NONE; + + trace_dev_irq(&dev->mt76, intr, dev->mt76.mmio.irqmask); + + mask = intr & (MT_INT_RX_DONE_ALL | MT_INT_GPTIMER); + if (intr & (MT_INT_TX_DONE_ALL | MT_INT_TX_STAT)) + mask |= MT_INT_TX_DONE_ALL; + + mt76x02_irq_disable(dev, mask); + + if (intr & MT_INT_RX_DONE(0)) + napi_schedule(&dev->mt76.napi[0]); + + if (intr & MT_INT_RX_DONE(1)) + napi_schedule(&dev->mt76.napi[1]); + + if (intr & MT_INT_PRE_TBTT) + tasklet_schedule(&dev->mt76.pre_tbtt_tasklet); + + /* send buffered multicast frames now */ + if (intr & MT_INT_TBTT) { + if (dev->mt76.csa_complete) + mt76_csa_finish(&dev->mt76); + else + mt76_queue_kick(dev, dev->mphy.q_tx[MT_TXQ_PSD]); + } + + if (intr & MT_INT_TX_STAT) + mt76x02_mac_poll_tx_status(dev, true); + + if (intr & (MT_INT_TX_STAT | MT_INT_TX_DONE_ALL)) + napi_schedule(&dev->mt76.tx_napi); + + if (intr & MT_INT_GPTIMER) + tasklet_schedule(&dev->dfs_pd.dfs_tasklet); + + return IRQ_HANDLED; +} +EXPORT_SYMBOL_GPL(mt76x02_irq_handler); + +static void mt76x02_dma_enable(struct mt76x02_dev *dev) +{ + u32 val; + + mt76_wr(dev, MT_MAC_SYS_CTRL, MT_MAC_SYS_CTRL_ENABLE_TX); + mt76x02_wait_for_wpdma(&dev->mt76, 1000); + usleep_range(50, 100); + + val = FIELD_PREP(MT_WPDMA_GLO_CFG_DMA_BURST_SIZE, 3) | + MT_WPDMA_GLO_CFG_TX_DMA_EN | + MT_WPDMA_GLO_CFG_RX_DMA_EN; + mt76_set(dev, MT_WPDMA_GLO_CFG, val); + mt76_clear(dev, MT_WPDMA_GLO_CFG, + MT_WPDMA_GLO_CFG_TX_WRITEBACK_DONE); +} + +void mt76x02_dma_disable(struct mt76x02_dev *dev) +{ + u32 val = mt76_rr(dev, MT_WPDMA_GLO_CFG); + + val &= MT_WPDMA_GLO_CFG_DMA_BURST_SIZE | + MT_WPDMA_GLO_CFG_BIG_ENDIAN | + MT_WPDMA_GLO_CFG_HDR_SEG_LEN; + val |= MT_WPDMA_GLO_CFG_TX_WRITEBACK_DONE; + mt76_wr(dev, MT_WPDMA_GLO_CFG, val); +} +EXPORT_SYMBOL_GPL(mt76x02_dma_disable); + +void mt76x02_mac_start(struct mt76x02_dev *dev) +{ + mt76x02_mac_reset_counters(dev); + mt76x02_dma_enable(dev); + mt76_wr(dev, MT_RX_FILTR_CFG, dev->mt76.rxfilter); + mt76_wr(dev, MT_MAC_SYS_CTRL, + MT_MAC_SYS_CTRL_ENABLE_TX | + MT_MAC_SYS_CTRL_ENABLE_RX); + mt76x02_irq_enable(dev, + MT_INT_RX_DONE_ALL | MT_INT_TX_DONE_ALL | + MT_INT_TX_STAT); +} +EXPORT_SYMBOL_GPL(mt76x02_mac_start); + +static bool mt76x02_tx_hang(struct mt76x02_dev *dev) +{ + u32 dma_idx, prev_dma_idx; + struct mt76_queue *q; + int i; + + for (i = 0; i < 4; i++) { + q = dev->mphy.q_tx[i]; + + prev_dma_idx = dev->mt76.tx_dma_idx[i]; + dma_idx = readl(&q->regs->dma_idx); + dev->mt76.tx_dma_idx[i] = dma_idx; + + if (!q->queued || prev_dma_idx != dma_idx) { + dev->tx_hang_check[i] = 0; + continue; + } + + if (++dev->tx_hang_check[i] >= MT_TX_HANG_TH) + return true; + } + + return false; +} + +static void mt76x02_key_sync(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key, void *data) +{ + struct mt76x02_dev *dev = hw->priv; + struct mt76_wcid *wcid; + + if (!sta) + return; + + wcid = (struct mt76_wcid *)sta->drv_priv; + + if (wcid->hw_key_idx != key->keyidx || wcid->sw_iv) + return; + + mt76x02_mac_wcid_sync_pn(dev, wcid->idx, key); +} + +static void mt76x02_reset_state(struct mt76x02_dev *dev) +{ + int i; + + lockdep_assert_held(&dev->mt76.mutex); + + clear_bit(MT76_STATE_RUNNING, &dev->mphy.state); + + rcu_read_lock(); + ieee80211_iter_keys_rcu(dev->mt76.hw, NULL, mt76x02_key_sync, NULL); + rcu_read_unlock(); + + for (i = 0; i < MT76x02_N_WCIDS; i++) { + struct ieee80211_sta *sta; + struct ieee80211_vif *vif; + struct mt76x02_sta *msta; + struct mt76_wcid *wcid; + void *priv; + + wcid = rcu_dereference_protected(dev->mt76.wcid[i], + lockdep_is_held(&dev->mt76.mutex)); + if (!wcid) + continue; + + rcu_assign_pointer(dev->mt76.wcid[i], NULL); + + priv = msta = container_of(wcid, struct mt76x02_sta, wcid); + sta = container_of(priv, struct ieee80211_sta, drv_priv); + + priv = msta->vif; + vif = container_of(priv, struct ieee80211_vif, drv_priv); + + __mt76_sta_remove(&dev->mphy, vif, sta); + memset(msta, 0, sizeof(*msta)); + } + + dev->mt76.vif_mask = 0; + dev->mt76.beacon_mask = 0; +} + +static void mt76x02_watchdog_reset(struct mt76x02_dev *dev) +{ + u32 mask = dev->mt76.mmio.irqmask; + bool restart = dev->mt76.mcu_ops->mcu_restart; + int i; + + ieee80211_stop_queues(dev->mt76.hw); + set_bit(MT76_RESET, &dev->mphy.state); + + tasklet_disable(&dev->mt76.pre_tbtt_tasklet); + mt76_worker_disable(&dev->mt76.tx_worker); + napi_disable(&dev->mt76.tx_napi); + + mt76_for_each_q_rx(&dev->mt76, i) { + napi_disable(&dev->mt76.napi[i]); + } + + mutex_lock(&dev->mt76.mutex); + + dev->mcu_timeout = 0; + if (restart) + mt76x02_reset_state(dev); + + if (dev->mt76.beacon_mask) + mt76_clear(dev, MT_BEACON_TIME_CFG, + MT_BEACON_TIME_CFG_BEACON_TX | + MT_BEACON_TIME_CFG_TBTT_EN); + + mt76x02_irq_disable(dev, mask); + + /* perform device reset */ + mt76_clear(dev, MT_TXOP_CTRL_CFG, MT_TXOP_ED_CCA_EN); + mt76_wr(dev, MT_MAC_SYS_CTRL, 0); + mt76_clear(dev, MT_WPDMA_GLO_CFG, + MT_WPDMA_GLO_CFG_TX_DMA_EN | MT_WPDMA_GLO_CFG_RX_DMA_EN); + usleep_range(5000, 10000); + mt76_wr(dev, MT_INT_SOURCE_CSR, 0xffffffff); + + /* let fw reset DMA */ + mt76_set(dev, 0x734, 0x3); + + if (restart) + mt76_mcu_restart(dev); + + mt76_queue_tx_cleanup(dev, dev->mt76.q_mcu[MT_MCUQ_WM], true); + for (i = 0; i < __MT_TXQ_MAX; i++) + mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[i], true); + + mt76_for_each_q_rx(&dev->mt76, i) { + mt76_queue_rx_reset(dev, i); + } + + mt76_tx_status_check(&dev->mt76, true); + + mt76x02_mac_start(dev); + + if (dev->ed_monitor) + mt76_set(dev, MT_TXOP_CTRL_CFG, MT_TXOP_ED_CCA_EN); + + if (dev->mt76.beacon_mask && !restart) + mt76_set(dev, MT_BEACON_TIME_CFG, + MT_BEACON_TIME_CFG_BEACON_TX | + MT_BEACON_TIME_CFG_TBTT_EN); + + mt76x02_irq_enable(dev, mask); + + mutex_unlock(&dev->mt76.mutex); + + clear_bit(MT76_RESET, &dev->mphy.state); + + mt76_worker_enable(&dev->mt76.tx_worker); + tasklet_enable(&dev->mt76.pre_tbtt_tasklet); + + napi_enable(&dev->mt76.tx_napi); + mt76_for_each_q_rx(&dev->mt76, i) { + napi_enable(&dev->mt76.napi[i]); + } + + local_bh_disable(); + napi_schedule(&dev->mt76.tx_napi); + mt76_for_each_q_rx(&dev->mt76, i) { + napi_schedule(&dev->mt76.napi[i]); + } + local_bh_enable(); + + if (restart) { + set_bit(MT76_RESTART, &dev->mphy.state); + mt76x02_mcu_function_select(dev, Q_SELECT, 1); + ieee80211_restart_hw(dev->mt76.hw); + } else { + ieee80211_wake_queues(dev->mt76.hw); + mt76_txq_schedule_all(&dev->mphy); + } +} + +void mt76x02_reconfig_complete(struct ieee80211_hw *hw, + enum ieee80211_reconfig_type reconfig_type) +{ + struct mt76x02_dev *dev = hw->priv; + + if (reconfig_type != IEEE80211_RECONFIG_TYPE_RESTART) + return; + + clear_bit(MT76_RESTART, &dev->mphy.state); + ieee80211_wake_queues(hw); +} +EXPORT_SYMBOL_GPL(mt76x02_reconfig_complete); + +static void mt76x02_check_tx_hang(struct mt76x02_dev *dev) +{ + if (test_bit(MT76_RESTART, &dev->mphy.state)) + return; + + if (!mt76x02_tx_hang(dev) && !dev->mcu_timeout) + return; + + mt76x02_watchdog_reset(dev); + + dev->tx_hang_reset++; + memset(dev->tx_hang_check, 0, sizeof(dev->tx_hang_check)); + memset(dev->mt76.tx_dma_idx, 0xff, + sizeof(dev->mt76.tx_dma_idx)); +} + +void mt76x02_wdt_work(struct work_struct *work) +{ + struct mt76x02_dev *dev = container_of(work, struct mt76x02_dev, + wdt_work.work); + + mt76x02_check_tx_hang(dev); + + ieee80211_queue_delayed_work(mt76_hw(dev), &dev->wdt_work, + MT_WATCHDOG_TIME); +} diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_phy.c b/drivers/net/wireless/mediatek/mt76/mt76x02_phy.c new file mode 100644 index 000000000..557380c9b --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_phy.c @@ -0,0 +1,204 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (C) 2016 Felix Fietkau + * Copyright (C) 2018 Lorenzo Bianconi + */ + +#include + +#include "mt76x02.h" +#include "mt76x02_phy.h" + +void mt76x02_phy_set_rxpath(struct mt76x02_dev *dev) +{ + u32 val; + + val = mt76_rr(dev, MT_BBP(AGC, 0)); + val &= ~BIT(4); + + switch (dev->mphy.chainmask & 0xf) { + case 2: + val |= BIT(3); + break; + default: + val &= ~BIT(3); + break; + } + + mt76_wr(dev, MT_BBP(AGC, 0), val); + mb(); + val = mt76_rr(dev, MT_BBP(AGC, 0)); +} +EXPORT_SYMBOL_GPL(mt76x02_phy_set_rxpath); + +void mt76x02_phy_set_txdac(struct mt76x02_dev *dev) +{ + int txpath; + + txpath = (dev->mphy.chainmask >> 8) & 0xf; + switch (txpath) { + case 2: + mt76_set(dev, MT_BBP(TXBE, 5), 0x3); + break; + default: + mt76_clear(dev, MT_BBP(TXBE, 5), 0x3); + break; + } +} +EXPORT_SYMBOL_GPL(mt76x02_phy_set_txdac); + +static u32 +mt76x02_tx_power_mask(u8 v1, u8 v2, u8 v3, u8 v4) +{ + u32 val = 0; + + val |= (v1 & (BIT(6) - 1)) << 0; + val |= (v2 & (BIT(6) - 1)) << 8; + val |= (v3 & (BIT(6) - 1)) << 16; + val |= (v4 & (BIT(6) - 1)) << 24; + return val; +} + +int mt76x02_get_max_rate_power(struct mt76x02_rate_power *r) +{ + s8 ret = 0; + int i; + + for (i = 0; i < sizeof(r->all); i++) + ret = max(ret, r->all[i]); + + return ret; +} +EXPORT_SYMBOL_GPL(mt76x02_get_max_rate_power); + +void mt76x02_limit_rate_power(struct mt76x02_rate_power *r, int limit) +{ + int i; + + for (i = 0; i < sizeof(r->all); i++) + if (r->all[i] > limit) + r->all[i] = limit; +} +EXPORT_SYMBOL_GPL(mt76x02_limit_rate_power); + +void mt76x02_add_rate_power_offset(struct mt76x02_rate_power *r, int offset) +{ + int i; + + for (i = 0; i < sizeof(r->all); i++) + r->all[i] += offset; +} +EXPORT_SYMBOL_GPL(mt76x02_add_rate_power_offset); + +void mt76x02_phy_set_txpower(struct mt76x02_dev *dev, int txp_0, int txp_1) +{ + struct mt76x02_rate_power *t = &dev->rate_power; + + mt76_rmw_field(dev, MT_TX_ALC_CFG_0, MT_TX_ALC_CFG_0_CH_INIT_0, txp_0); + mt76_rmw_field(dev, MT_TX_ALC_CFG_0, MT_TX_ALC_CFG_0_CH_INIT_1, txp_1); + + mt76_wr(dev, MT_TX_PWR_CFG_0, + mt76x02_tx_power_mask(t->cck[0], t->cck[2], t->ofdm[0], + t->ofdm[2])); + mt76_wr(dev, MT_TX_PWR_CFG_1, + mt76x02_tx_power_mask(t->ofdm[4], t->ofdm[6], t->ht[0], + t->ht[2])); + mt76_wr(dev, MT_TX_PWR_CFG_2, + mt76x02_tx_power_mask(t->ht[4], t->ht[6], t->ht[8], + t->ht[10])); + mt76_wr(dev, MT_TX_PWR_CFG_3, + mt76x02_tx_power_mask(t->ht[12], t->ht[14], t->ht[0], + t->ht[2])); + mt76_wr(dev, MT_TX_PWR_CFG_4, + mt76x02_tx_power_mask(t->ht[4], t->ht[6], 0, 0)); + mt76_wr(dev, MT_TX_PWR_CFG_7, + mt76x02_tx_power_mask(t->ofdm[7], t->vht[0], t->ht[7], + t->vht[1])); + mt76_wr(dev, MT_TX_PWR_CFG_8, + mt76x02_tx_power_mask(t->ht[14], 0, t->vht[0], t->vht[1])); + mt76_wr(dev, MT_TX_PWR_CFG_9, + mt76x02_tx_power_mask(t->ht[7], 0, t->vht[0], t->vht[1])); +} +EXPORT_SYMBOL_GPL(mt76x02_phy_set_txpower); + +void mt76x02_phy_set_bw(struct mt76x02_dev *dev, int width, u8 ctrl) +{ + int core_val, agc_val; + + switch (width) { + case NL80211_CHAN_WIDTH_80: + core_val = 3; + agc_val = 7; + break; + case NL80211_CHAN_WIDTH_40: + core_val = 2; + agc_val = 3; + break; + default: + core_val = 0; + agc_val = 1; + break; + } + + mt76_rmw_field(dev, MT_BBP(CORE, 1), MT_BBP_CORE_R1_BW, core_val); + mt76_rmw_field(dev, MT_BBP(AGC, 0), MT_BBP_AGC_R0_BW, agc_val); + mt76_rmw_field(dev, MT_BBP(AGC, 0), MT_BBP_AGC_R0_CTRL_CHAN, ctrl); + mt76_rmw_field(dev, MT_BBP(TXBE, 0), MT_BBP_TXBE_R0_CTRL_CHAN, ctrl); +} +EXPORT_SYMBOL_GPL(mt76x02_phy_set_bw); + +void mt76x02_phy_set_band(struct mt76x02_dev *dev, int band, + bool primary_upper) +{ + switch (band) { + case NL80211_BAND_2GHZ: + mt76_set(dev, MT_TX_BAND_CFG, MT_TX_BAND_CFG_2G); + mt76_clear(dev, MT_TX_BAND_CFG, MT_TX_BAND_CFG_5G); + break; + case NL80211_BAND_5GHZ: + mt76_clear(dev, MT_TX_BAND_CFG, MT_TX_BAND_CFG_2G); + mt76_set(dev, MT_TX_BAND_CFG, MT_TX_BAND_CFG_5G); + break; + } + + mt76_rmw_field(dev, MT_TX_BAND_CFG, MT_TX_BAND_CFG_UPPER_40M, + primary_upper); +} +EXPORT_SYMBOL_GPL(mt76x02_phy_set_band); + +bool mt76x02_phy_adjust_vga_gain(struct mt76x02_dev *dev) +{ + u8 limit = dev->cal.low_gain > 0 ? 16 : 4; + bool ret = false; + u32 false_cca; + + false_cca = FIELD_GET(MT_RX_STAT_1_CCA_ERRORS, + mt76_rr(dev, MT_RX_STAT_1)); + dev->cal.false_cca = false_cca; + if (false_cca > 800 && dev->cal.agc_gain_adjust < limit) { + dev->cal.agc_gain_adjust += 2; + ret = true; + } else if ((false_cca < 10 && dev->cal.agc_gain_adjust > 0) || + (dev->cal.agc_gain_adjust >= limit && false_cca < 500)) { + dev->cal.agc_gain_adjust -= 2; + ret = true; + } + + dev->cal.agc_lowest_gain = dev->cal.agc_gain_adjust >= limit; + + return ret; +} +EXPORT_SYMBOL_GPL(mt76x02_phy_adjust_vga_gain); + +void mt76x02_init_agc_gain(struct mt76x02_dev *dev) +{ + dev->cal.agc_gain_init[0] = mt76_get_field(dev, MT_BBP(AGC, 8), + MT_BBP_AGC_GAIN); + dev->cal.agc_gain_init[1] = mt76_get_field(dev, MT_BBP(AGC, 9), + MT_BBP_AGC_GAIN); + memcpy(dev->cal.agc_gain_cur, dev->cal.agc_gain_init, + sizeof(dev->cal.agc_gain_cur)); + dev->cal.low_gain = -1; + dev->cal.gain_init_done = true; +} +EXPORT_SYMBOL_GPL(mt76x02_init_agc_gain); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_phy.h b/drivers/net/wireless/mediatek/mt76/mt76x02_phy.h new file mode 100644 index 000000000..09e8edee2 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_phy.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (C) 2018 Lorenzo Bianconi + */ + +#ifndef __MT76x02_PHY_H +#define __MT76x02_PHY_H + +#include "mt76x02_regs.h" + +static inline int +mt76x02_get_rssi_gain_thresh(struct mt76x02_dev *dev) +{ + switch (dev->mphy.chandef.width) { + case NL80211_CHAN_WIDTH_80: + return -62; + case NL80211_CHAN_WIDTH_40: + return -65; + default: + return -68; + } +} + +static inline int +mt76x02_get_low_rssi_gain_thresh(struct mt76x02_dev *dev) +{ + switch (dev->mphy.chandef.width) { + case NL80211_CHAN_WIDTH_80: + return -76; + case NL80211_CHAN_WIDTH_40: + return -79; + default: + return -82; + } +} + +void mt76x02_add_rate_power_offset(struct mt76x02_rate_power *r, int offset); +void mt76x02_phy_set_txpower(struct mt76x02_dev *dev, int txp_0, int txp_2); +void mt76x02_limit_rate_power(struct mt76x02_rate_power *r, int limit); +int mt76x02_get_max_rate_power(struct mt76x02_rate_power *r); +void mt76x02_phy_set_rxpath(struct mt76x02_dev *dev); +void mt76x02_phy_set_txdac(struct mt76x02_dev *dev); +void mt76x02_phy_set_bw(struct mt76x02_dev *dev, int width, u8 ctrl); +void mt76x02_phy_set_band(struct mt76x02_dev *dev, int band, + bool primary_upper); +bool mt76x02_phy_adjust_vga_gain(struct mt76x02_dev *dev); +void mt76x02_init_agc_gain(struct mt76x02_dev *dev); + +#endif /* __MT76x02_PHY_H */ diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_regs.h b/drivers/net/wireless/mediatek/mt76/mt76x02_regs.h new file mode 100644 index 000000000..e87d3f8a1 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_regs.h @@ -0,0 +1,708 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (C) 2016 Felix Fietkau + */ + +#ifndef __MT76X02_REGS_H +#define __MT76X02_REGS_H + +#define MT_ASIC_VERSION 0x0000 + +#define MT76XX_REV_E3 0x22 +#define MT76XX_REV_E4 0x33 + +#define MT_CMB_CTRL 0x0020 +#define MT_CMB_CTRL_XTAL_RDY BIT(22) +#define MT_CMB_CTRL_PLL_LD BIT(23) + +#define MT_EFUSE_CTRL 0x0024 +#define MT_EFUSE_CTRL_AOUT GENMASK(5, 0) +#define MT_EFUSE_CTRL_MODE GENMASK(7, 6) +#define MT_EFUSE_CTRL_LDO_OFF_TIME GENMASK(13, 8) +#define MT_EFUSE_CTRL_LDO_ON_TIME GENMASK(15, 14) +#define MT_EFUSE_CTRL_AIN GENMASK(25, 16) +#define MT_EFUSE_CTRL_KICK BIT(30) +#define MT_EFUSE_CTRL_SEL BIT(31) + +#define MT_EFUSE_DATA_BASE 0x0028 +#define MT_EFUSE_DATA(_n) (MT_EFUSE_DATA_BASE + ((_n) << 2)) + +#define MT_COEXCFG0 0x0040 +#define MT_COEXCFG0_COEX_EN BIT(0) + +#define MT_WLAN_FUN_CTRL 0x0080 +#define MT_WLAN_FUN_CTRL_WLAN_EN BIT(0) +#define MT_WLAN_FUN_CTRL_WLAN_CLK_EN BIT(1) +#define MT_WLAN_FUN_CTRL_WLAN_RESET_RF BIT(2) + +#define MT_COEXCFG3 0x004c + +#define MT_LDO_CTRL_0 0x006c +#define MT_LDO_CTRL_1 0x0070 + +#define MT_WLAN_FUN_CTRL_WLAN_RESET BIT(3) /* MT76x0 */ +#define MT_WLAN_FUN_CTRL_CSR_F20M_CKEN BIT(3) /* MT76x2 */ + +#define MT_WLAN_FUN_CTRL_PCIE_CLK_REQ BIT(4) +#define MT_WLAN_FUN_CTRL_FRC_WL_ANT_SEL BIT(5) +#define MT_WLAN_FUN_CTRL_INV_ANT_SEL BIT(6) +#define MT_WLAN_FUN_CTRL_WAKE_HOST BIT(7) + +#define MT_WLAN_FUN_CTRL_THERM_RST BIT(8) /* MT76x2 */ +#define MT_WLAN_FUN_CTRL_THERM_CKEN BIT(9) /* MT76x2 */ + +#define MT_WLAN_FUN_CTRL_GPIO_IN GENMASK(15, 8) /* MT76x0 */ +#define MT_WLAN_FUN_CTRL_GPIO_OUT GENMASK(23, 16) /* MT76x0 */ +#define MT_WLAN_FUN_CTRL_GPIO_OUT_EN GENMASK(31, 24) /* MT76x0 */ + +/* MT76x0 */ +#define MT_CSR_EE_CFG1 0x0104 + +#define MT_XO_CTRL0 0x0100 +#define MT_XO_CTRL1 0x0104 +#define MT_XO_CTRL2 0x0108 +#define MT_XO_CTRL3 0x010c +#define MT_XO_CTRL4 0x0110 + +#define MT_XO_CTRL5 0x0114 +#define MT_XO_CTRL5_C2_VAL GENMASK(14, 8) + +#define MT_XO_CTRL6 0x0118 +#define MT_XO_CTRL6_C2_CTRL GENMASK(14, 8) + +#define MT_XO_CTRL7 0x011c + +#define MT_IOCFG_6 0x0124 + +#define MT_USB_U3DMA_CFG 0x9018 +#define MT_USB_DMA_CFG_RX_BULK_AGG_TOUT GENMASK(7, 0) +#define MT_USB_DMA_CFG_RX_BULK_AGG_LMT GENMASK(15, 8) +#define MT_USB_DMA_CFG_UDMA_TX_WL_DROP BIT(16) +#define MT_USB_DMA_CFG_WAKE_UP_EN BIT(17) +#define MT_USB_DMA_CFG_RX_DROP_OR_PAD BIT(18) +#define MT_USB_DMA_CFG_TX_CLR BIT(19) +#define MT_USB_DMA_CFG_TXOP_HALT BIT(20) +#define MT_USB_DMA_CFG_RX_BULK_AGG_EN BIT(21) +#define MT_USB_DMA_CFG_RX_BULK_EN BIT(22) +#define MT_USB_DMA_CFG_TX_BULK_EN BIT(23) +#define MT_USB_DMA_CFG_EP_OUT_VALID GENMASK(29, 24) +#define MT_USB_DMA_CFG_RX_BUSY BIT(30) +#define MT_USB_DMA_CFG_TX_BUSY BIT(31) + +#define MT_WLAN_MTC_CTRL 0x10148 +#define MT_WLAN_MTC_CTRL_MTCMOS_PWR_UP BIT(0) +#define MT_WLAN_MTC_CTRL_PWR_ACK BIT(12) +#define MT_WLAN_MTC_CTRL_PWR_ACK_S BIT(13) +#define MT_WLAN_MTC_CTRL_BBP_MEM_PD GENMASK(19, 16) +#define MT_WLAN_MTC_CTRL_PBF_MEM_PD BIT(20) +#define MT_WLAN_MTC_CTRL_FCE_MEM_PD BIT(21) +#define MT_WLAN_MTC_CTRL_TSO_MEM_PD BIT(22) +#define MT_WLAN_MTC_CTRL_BBP_MEM_RB BIT(24) +#define MT_WLAN_MTC_CTRL_PBF_MEM_RB BIT(25) +#define MT_WLAN_MTC_CTRL_FCE_MEM_RB BIT(26) +#define MT_WLAN_MTC_CTRL_TSO_MEM_RB BIT(27) +#define MT_WLAN_MTC_CTRL_STATE_UP BIT(28) + +#define MT_INT_SOURCE_CSR 0x0200 +#define MT_INT_MASK_CSR 0x0204 + +#define MT_INT_RX_DONE(_n) BIT(_n) +#define MT_INT_RX_DONE_ALL GENMASK(1, 0) +#define MT_INT_TX_DONE_ALL GENMASK(13, 4) +#define MT_INT_TX_DONE(_n) BIT((_n) + 4) +#define MT_INT_RX_COHERENT BIT(16) +#define MT_INT_TX_COHERENT BIT(17) +#define MT_INT_ANY_COHERENT BIT(18) +#define MT_INT_MCU_CMD BIT(19) +#define MT_INT_TBTT BIT(20) +#define MT_INT_PRE_TBTT BIT(21) +#define MT_INT_TX_STAT BIT(22) +#define MT_INT_AUTO_WAKEUP BIT(23) +#define MT_INT_GPTIMER BIT(24) +#define MT_INT_RXDELAYINT BIT(26) +#define MT_INT_TXDELAYINT BIT(27) + +#define MT_WPDMA_GLO_CFG 0x0208 +#define MT_WPDMA_GLO_CFG_TX_DMA_EN BIT(0) +#define MT_WPDMA_GLO_CFG_TX_DMA_BUSY BIT(1) +#define MT_WPDMA_GLO_CFG_RX_DMA_EN BIT(2) +#define MT_WPDMA_GLO_CFG_RX_DMA_BUSY BIT(3) +#define MT_WPDMA_GLO_CFG_DMA_BURST_SIZE GENMASK(5, 4) +#define MT_WPDMA_GLO_CFG_TX_WRITEBACK_DONE BIT(6) +#define MT_WPDMA_GLO_CFG_BIG_ENDIAN BIT(7) +#define MT_WPDMA_GLO_CFG_HDR_SEG_LEN GENMASK(15, 8) +#define MT_WPDMA_GLO_CFG_CLK_GATE_DIS BIT(30) +#define MT_WPDMA_GLO_CFG_RX_2B_OFFSET BIT(31) + +#define MT_WPDMA_RST_IDX 0x020c + +#define MT_WPDMA_DELAY_INT_CFG 0x0210 + +#define MT_WMM_AIFSN 0x0214 +#define MT_WMM_AIFSN_MASK GENMASK(3, 0) +#define MT_WMM_AIFSN_SHIFT(_n) ((_n) * 4) + +#define MT_WMM_CWMIN 0x0218 +#define MT_WMM_CWMIN_MASK GENMASK(3, 0) +#define MT_WMM_CWMIN_SHIFT(_n) ((_n) * 4) + +#define MT_WMM_CWMAX 0x021c +#define MT_WMM_CWMAX_MASK GENMASK(3, 0) +#define MT_WMM_CWMAX_SHIFT(_n) ((_n) * 4) + +#define MT_WMM_TXOP_BASE 0x0220 +#define MT_WMM_TXOP(_n) (MT_WMM_TXOP_BASE + (((_n) / 2) << 2)) +#define MT_WMM_TXOP_SHIFT(_n) (((_n) & 1) * 16) +#define MT_WMM_TXOP_MASK GENMASK(15, 0) + +#define MT_WMM_CTRL 0x0230 /* MT76x0 */ +#define MT_FCE_DMA_ADDR 0x0230 +#define MT_FCE_DMA_LEN 0x0234 +#define MT_USB_DMA_CFG 0x0238 + +#define MT_TSO_CTRL 0x0250 +#define MT_HEADER_TRANS_CTRL_REG 0x0260 + +#define MT_US_CYC_CFG 0x02a4 +#define MT_US_CYC_CNT GENMASK(7, 0) + +#define MT_TX_RING_BASE 0x0300 +#define MT_RX_RING_BASE 0x03c0 + +#define MT_TX_HW_QUEUE_MCU 8 +#define MT_TX_HW_QUEUE_MGMT 9 + +#define MT_PBF_SYS_CTRL 0x0400 +#define MT_PBF_SYS_CTRL_MCU_RESET BIT(0) +#define MT_PBF_SYS_CTRL_DMA_RESET BIT(1) +#define MT_PBF_SYS_CTRL_MAC_RESET BIT(2) +#define MT_PBF_SYS_CTRL_PBF_RESET BIT(3) +#define MT_PBF_SYS_CTRL_ASY_RESET BIT(4) + +#define MT_PBF_CFG 0x0404 +#define MT_PBF_CFG_TX0Q_EN BIT(0) +#define MT_PBF_CFG_TX1Q_EN BIT(1) +#define MT_PBF_CFG_TX2Q_EN BIT(2) +#define MT_PBF_CFG_TX3Q_EN BIT(3) +#define MT_PBF_CFG_RX0Q_EN BIT(4) +#define MT_PBF_CFG_RX_DROP_EN BIT(8) + +#define MT_PBF_TX_MAX_PCNT 0x0408 +#define MT_PBF_RX_MAX_PCNT 0x040c + +#define MT_BCN_OFFSET_BASE 0x041c +#define MT_BCN_OFFSET(_n) (MT_BCN_OFFSET_BASE + ((_n) << 2)) + +#define MT_RXQ_STA 0x0430 +#define MT_TXQ_STA 0x0434 +#define MT_RF_CSR_CFG 0x0500 +#define MT_RF_CSR_CFG_DATA GENMASK(7, 0) +#define MT_RF_CSR_CFG_REG_ID GENMASK(14, 8) +#define MT_RF_CSR_CFG_REG_BANK GENMASK(17, 15) +#define MT_RF_CSR_CFG_WR BIT(30) +#define MT_RF_CSR_CFG_KICK BIT(31) + +#define MT_RF_BYPASS_0 0x0504 +#define MT_RF_BYPASS_1 0x0508 +#define MT_RF_SETTING_0 0x050c + +#define MT_RF_MISC 0x0518 +#define MT_RF_DATA_WRITE 0x0524 + +#define MT_RF_CTRL 0x0528 +#define MT_RF_CTRL_ADDR GENMASK(11, 0) +#define MT_RF_CTRL_WRITE BIT(12) +#define MT_RF_CTRL_BUSY BIT(13) +#define MT_RF_CTRL_IDX BIT(16) + +#define MT_RF_DATA_READ 0x052c + +#define MT_COM_REG0 0x0730 +#define MT_COM_REG1 0x0734 +#define MT_COM_REG2 0x0738 +#define MT_COM_REG3 0x073C + +#define MT_LED_CTRL 0x0770 +#define MT_LED_CTRL_REPLAY(_n) BIT(0 + (8 * (_n))) +#define MT_LED_CTRL_POLARITY(_n) BIT(1 + (8 * (_n))) +#define MT_LED_CTRL_TX_BLINK_MODE(_n) BIT(2 + (8 * (_n))) +#define MT_LED_CTRL_KICK(_n) BIT(7 + (8 * (_n))) + +#define MT_LED_TX_BLINK_0 0x0774 +#define MT_LED_TX_BLINK_1 0x0778 + +#define MT_LED_S0_BASE 0x077C +#define MT_LED_S0(_n) (MT_LED_S0_BASE + 8 * (_n)) +#define MT_LED_S1_BASE 0x0780 +#define MT_LED_S1(_n) (MT_LED_S1_BASE + 8 * (_n)) +#define MT_LED_STATUS_OFF GENMASK(31, 24) +#define MT_LED_STATUS_ON GENMASK(23, 16) +#define MT_LED_STATUS_DURATION GENMASK(15, 8) + +#define MT_FCE_PSE_CTRL 0x0800 +#define MT_FCE_PARAMETERS 0x0804 +#define MT_FCE_CSO 0x0808 + +#define MT_FCE_L2_STUFF 0x080c +#define MT_FCE_L2_STUFF_HT_L2_EN BIT(0) +#define MT_FCE_L2_STUFF_QOS_L2_EN BIT(1) +#define MT_FCE_L2_STUFF_RX_STUFF_EN BIT(2) +#define MT_FCE_L2_STUFF_TX_STUFF_EN BIT(3) +#define MT_FCE_L2_STUFF_WR_MPDU_LEN_EN BIT(4) +#define MT_FCE_L2_STUFF_MVINV_BSWAP BIT(5) +#define MT_FCE_L2_STUFF_TS_CMD_QSEL_EN GENMASK(15, 8) +#define MT_FCE_L2_STUFF_TS_LEN_EN GENMASK(23, 16) +#define MT_FCE_L2_STUFF_OTHER_PORT GENMASK(25, 24) + +#define MT_FCE_WLAN_FLOW_CONTROL1 0x0824 + +#define MT_TX_CPU_FROM_FCE_BASE_PTR 0x09a0 +#define MT_TX_CPU_FROM_FCE_MAX_COUNT 0x09a4 +#define MT_TX_CPU_FROM_FCE_CPU_DESC_IDX 0x09a8 +#define MT_FCE_PDMA_GLOBAL_CONF 0x09c4 +#define MT_FCE_SKIP_FS 0x0a6c + +#define MT_PAUSE_ENABLE_CONTROL1 0x0a38 + +#define MT_MAC_CSR0 0x1000 + +#define MT_MAC_SYS_CTRL 0x1004 +#define MT_MAC_SYS_CTRL_RESET_CSR BIT(0) +#define MT_MAC_SYS_CTRL_RESET_BBP BIT(1) +#define MT_MAC_SYS_CTRL_ENABLE_TX BIT(2) +#define MT_MAC_SYS_CTRL_ENABLE_RX BIT(3) + +#define MT_MAC_ADDR_DW0 0x1008 +#define MT_MAC_ADDR_DW1 0x100c +#define MT_MAC_ADDR_DW1_U2ME_MASK GENMASK(23, 16) + +#define MT_MAC_BSSID_DW0 0x1010 +#define MT_MAC_BSSID_DW1 0x1014 +#define MT_MAC_BSSID_DW1_ADDR GENMASK(15, 0) +#define MT_MAC_BSSID_DW1_MBSS_MODE GENMASK(17, 16) +#define MT_MAC_BSSID_DW1_MBEACON_N GENMASK(20, 18) +#define MT_MAC_BSSID_DW1_MBSS_LOCAL_BIT BIT(21) +#define MT_MAC_BSSID_DW1_MBSS_MODE_B2 BIT(22) +#define MT_MAC_BSSID_DW1_MBEACON_N_B3 BIT(23) +#define MT_MAC_BSSID_DW1_MBSS_IDX_BYTE GENMASK(26, 24) + +#define MT_MAX_LEN_CFG 0x1018 +#define MT_MAX_LEN_CFG_AMPDU GENMASK(13, 12) + +#define MT_LED_CFG 0x102c + +#define MT_AMPDU_MAX_LEN_20M1S 0x1030 +#define MT_AMPDU_MAX_LEN_20M2S 0x1034 +#define MT_AMPDU_MAX_LEN_40M1S 0x1038 +#define MT_AMPDU_MAX_LEN_40M2S 0x103c +#define MT_AMPDU_MAX_LEN 0x1040 + +#define MT_WCID_DROP_BASE 0x106c +#define MT_WCID_DROP(_n) (MT_WCID_DROP_BASE + ((_n) >> 5) * 4) +#define MT_WCID_DROP_MASK(_n) BIT((_n) % 32) + +#define MT_BCN_BYPASS_MASK 0x108c + +#define MT_MAC_APC_BSSID_BASE 0x1090 +#define MT_MAC_APC_BSSID_L(_n) (MT_MAC_APC_BSSID_BASE + ((_n) * 8)) +#define MT_MAC_APC_BSSID_H(_n) (MT_MAC_APC_BSSID_BASE + ((_n) * 8 + 4)) +#define MT_MAC_APC_BSSID_H_ADDR GENMASK(15, 0) +#define MT_MAC_APC_BSSID0_H_EN BIT(16) + +#define MT_XIFS_TIME_CFG 0x1100 +#define MT_XIFS_TIME_CFG_CCK_SIFS GENMASK(7, 0) +#define MT_XIFS_TIME_CFG_OFDM_SIFS GENMASK(15, 8) +#define MT_XIFS_TIME_CFG_OFDM_XIFS GENMASK(19, 16) +#define MT_XIFS_TIME_CFG_EIFS GENMASK(28, 20) +#define MT_XIFS_TIME_CFG_BB_RXEND_EN BIT(29) + +#define MT_BKOFF_SLOT_CFG 0x1104 +#define MT_BKOFF_SLOT_CFG_SLOTTIME GENMASK(7, 0) +#define MT_BKOFF_SLOT_CFG_CC_DELAY GENMASK(11, 8) + +#define MT_CH_TIME_CFG 0x110c +#define MT_CH_TIME_CFG_TIMER_EN BIT(0) +#define MT_CH_TIME_CFG_TX_AS_BUSY BIT(1) +#define MT_CH_TIME_CFG_RX_AS_BUSY BIT(2) +#define MT_CH_TIME_CFG_NAV_AS_BUSY BIT(3) +#define MT_CH_TIME_CFG_EIFS_AS_BUSY BIT(4) +#define MT_CH_TIME_CFG_MDRDY_CNT_EN BIT(5) +#define MT_CH_CCA_RC_EN BIT(6) +#define MT_CH_TIME_CFG_CH_TIMER_CLR GENMASK(9, 8) +#define MT_CH_TIME_CFG_MDRDY_CLR GENMASK(11, 10) + +#define MT_PBF_LIFE_TIMER 0x1110 + +#define MT_BEACON_TIME_CFG 0x1114 +#define MT_BEACON_TIME_CFG_INTVAL GENMASK(15, 0) +#define MT_BEACON_TIME_CFG_TIMER_EN BIT(16) +#define MT_BEACON_TIME_CFG_SYNC_MODE GENMASK(18, 17) +#define MT_BEACON_TIME_CFG_TBTT_EN BIT(19) +#define MT_BEACON_TIME_CFG_BEACON_TX BIT(20) +#define MT_BEACON_TIME_CFG_TSF_COMP GENMASK(31, 24) + +#define MT_TBTT_SYNC_CFG 0x1118 +#define MT_TSF_TIMER_DW0 0x111c +#define MT_TSF_TIMER_DW1 0x1120 +#define MT_TBTT_TIMER 0x1124 +#define MT_TBTT_TIMER_VAL GENMASK(16, 0) + +#define MT_INT_TIMER_CFG 0x1128 +#define MT_INT_TIMER_CFG_PRE_TBTT GENMASK(15, 0) +#define MT_INT_TIMER_CFG_GP_TIMER GENMASK(31, 16) + +#define MT_INT_TIMER_EN 0x112c +#define MT_INT_TIMER_EN_PRE_TBTT_EN BIT(0) +#define MT_INT_TIMER_EN_GP_TIMER_EN BIT(1) + +#define MT_CH_IDLE 0x1130 +#define MT_CH_BUSY 0x1134 +#define MT_EXT_CH_BUSY 0x1138 +#define MT_ED_CCA_TIMER 0x1140 + +#define MT_MAC_STATUS 0x1200 +#define MT_MAC_STATUS_TX BIT(0) +#define MT_MAC_STATUS_RX BIT(1) + +#define MT_PWR_PIN_CFG 0x1204 +#define MT_AUX_CLK_CFG 0x120c + +#define MT_BB_PA_MODE_CFG0 0x1214 +#define MT_BB_PA_MODE_CFG1 0x1218 +#define MT_RF_PA_MODE_CFG0 0x121c +#define MT_RF_PA_MODE_CFG1 0x1220 + +#define MT_RF_PA_MODE_ADJ0 0x1228 +#define MT_RF_PA_MODE_ADJ1 0x122c + +#define MT_DACCLK_EN_DLY_CFG 0x1264 + +#define MT_EDCA_CFG_BASE 0x1300 +#define MT_EDCA_CFG_AC(_n) (MT_EDCA_CFG_BASE + ((_n) << 2)) +#define MT_EDCA_CFG_TXOP GENMASK(7, 0) +#define MT_EDCA_CFG_AIFSN GENMASK(11, 8) +#define MT_EDCA_CFG_CWMIN GENMASK(15, 12) +#define MT_EDCA_CFG_CWMAX GENMASK(19, 16) + +#define MT_TX_PWR_CFG_0 0x1314 +#define MT_TX_PWR_CFG_1 0x1318 +#define MT_TX_PWR_CFG_2 0x131c +#define MT_TX_PWR_CFG_3 0x1320 +#define MT_TX_PWR_CFG_4 0x1324 +#define MT_TX_PIN_CFG 0x1328 +#define MT_TX_PIN_CFG_TXANT GENMASK(3, 0) +#define MT_TX_PIN_CFG_RXANT GENMASK(11, 8) +#define MT_TX_PIN_RFTR_EN BIT(16) +#define MT_TX_PIN_TRSW_EN BIT(18) + +#define MT_TX_BAND_CFG 0x132c +#define MT_TX_BAND_CFG_UPPER_40M BIT(0) +#define MT_TX_BAND_CFG_5G BIT(1) +#define MT_TX_BAND_CFG_2G BIT(2) + +#define MT_HT_FBK_TO_LEGACY 0x1384 +#define MT_TX_MPDU_ADJ_INT 0x1388 + +#define MT_TX_PWR_CFG_7 0x13d4 +#define MT_TX_PWR_CFG_8 0x13d8 +#define MT_TX_PWR_CFG_9 0x13dc + +#define MT_TX_SW_CFG0 0x1330 +#define MT_TX_SW_CFG1 0x1334 +#define MT_TX_SW_CFG2 0x1338 + +#define MT_TXOP_CTRL_CFG 0x1340 +#define MT_TXOP_TRUN_EN GENMASK(5, 0) +#define MT_TXOP_EXT_CCA_DLY GENMASK(15, 8) +#define MT_TXOP_ED_CCA_EN BIT(20) + +#define MT_TX_RTS_CFG 0x1344 +#define MT_TX_RTS_CFG_RETRY_LIMIT GENMASK(7, 0) +#define MT_TX_RTS_CFG_THRESH GENMASK(23, 8) +#define MT_TX_RTS_FALLBACK BIT(24) + +#define MT_TX_TIMEOUT_CFG 0x1348 +#define MT_TX_TIMEOUT_CFG_ACKTO GENMASK(15, 8) + +#define MT_TX_RETRY_CFG 0x134c +#define MT_TX_LINK_CFG 0x1350 +#define MT_TX_CFACK_EN BIT(12) +#define MT_VHT_HT_FBK_CFG0 0x1354 +#define MT_VHT_HT_FBK_CFG1 0x1358 +#define MT_LG_FBK_CFG0 0x135c +#define MT_LG_FBK_CFG1 0x1360 + +#define MT_PROT_CFG_RATE GENMASK(15, 0) +#define MT_PROT_CFG_CTRL GENMASK(17, 16) +#define MT_PROT_CFG_NAV GENMASK(19, 18) +#define MT_PROT_CFG_TXOP_ALLOW GENMASK(25, 20) +#define MT_PROT_CFG_RTS_THRESH BIT(26) + +#define MT_CCK_PROT_CFG 0x1364 +#define MT_OFDM_PROT_CFG 0x1368 +#define MT_MM20_PROT_CFG 0x136c +#define MT_MM40_PROT_CFG 0x1370 +#define MT_GF20_PROT_CFG 0x1374 +#define MT_GF40_PROT_CFG 0x1378 + +#define MT_PROT_RATE GENMASK(15, 0) +#define MT_PROT_CTRL_RTS_CTS BIT(16) +#define MT_PROT_CTRL_CTS2SELF BIT(17) +#define MT_PROT_NAV_SHORT BIT(18) +#define MT_PROT_NAV_LONG BIT(19) +#define MT_PROT_TXOP_ALLOW_CCK BIT(20) +#define MT_PROT_TXOP_ALLOW_OFDM BIT(21) +#define MT_PROT_TXOP_ALLOW_MM20 BIT(22) +#define MT_PROT_TXOP_ALLOW_MM40 BIT(23) +#define MT_PROT_TXOP_ALLOW_GF20 BIT(24) +#define MT_PROT_TXOP_ALLOW_GF40 BIT(25) +#define MT_PROT_RTS_THR_EN BIT(26) +#define MT_PROT_RATE_CCK_11 0x0003 +#define MT_PROT_RATE_OFDM_6 0x2000 +#define MT_PROT_RATE_OFDM_24 0x2004 +#define MT_PROT_RATE_DUP_OFDM_24 0x2084 +#define MT_PROT_RATE_SGI_OFDM_24 0x2104 +#define MT_PROT_TXOP_ALLOW_ALL GENMASK(25, 20) +#define MT_PROT_TXOP_ALLOW_BW20 (MT_PROT_TXOP_ALLOW_ALL & \ + ~MT_PROT_TXOP_ALLOW_MM40 & \ + ~MT_PROT_TXOP_ALLOW_GF40) + +#define MT_EXP_ACK_TIME 0x1380 + +#define MT_TX_PWR_CFG_0_EXT 0x1390 +#define MT_TX_PWR_CFG_1_EXT 0x1394 + +#define MT_TX_FBK_LIMIT 0x1398 +#define MT_TX_FBK_LIMIT_MPDU_FBK GENMASK(7, 0) +#define MT_TX_FBK_LIMIT_AMPDU_FBK GENMASK(15, 8) +#define MT_TX_FBK_LIMIT_MPDU_UP_CLEAR BIT(16) +#define MT_TX_FBK_LIMIT_AMPDU_UP_CLEAR BIT(17) +#define MT_TX_FBK_LIMIT_RATE_LUT BIT(18) + +#define MT_TX0_RF_GAIN_CORR 0x13a0 +#define MT_TX1_RF_GAIN_CORR 0x13a4 +#define MT_TX0_RF_GAIN_ATTEN 0x13a8 +#define MT_TX0_RF_GAIN_ATTEN 0x13a8 /* MT76x0 */ + +#define MT_TX_ALC_CFG_0 0x13b0 +#define MT_TX_ALC_CFG_0_CH_INIT_0 GENMASK(5, 0) +#define MT_TX_ALC_CFG_0_CH_INIT_1 GENMASK(13, 8) +#define MT_TX_ALC_CFG_0_LIMIT_0 GENMASK(21, 16) +#define MT_TX_ALC_CFG_0_LIMIT_1 GENMASK(29, 24) + +#define MT_TX_ALC_CFG_1 0x13b4 +#define MT_TX_ALC_CFG_1_TEMP_COMP GENMASK(5, 0) + +#define MT_TX_ALC_CFG_2 0x13a8 +#define MT_TX_ALC_CFG_2_TEMP_COMP GENMASK(5, 0) + +#define MT_TX_ALC_CFG_3 0x13ac +#define MT_TX_ALC_CFG_4 0x13c0 +#define MT_TX_ALC_CFG_4_LOWGAIN_CH_EN BIT(31) +#define MT_TX0_BB_GAIN_ATTEN 0x13c0 /* MT76x0 */ + +#define MT_TX_ALC_VGA3 0x13c8 + +#define MT_TX_PROT_CFG6 0x13e0 +#define MT_TX_PROT_CFG7 0x13e4 +#define MT_TX_PROT_CFG8 0x13e8 + +#define MT_PIFS_TX_CFG 0x13ec + +#define MT_RX_FILTR_CFG 0x1400 + +#define MT_RX_FILTR_CFG_CRC_ERR BIT(0) +#define MT_RX_FILTR_CFG_PHY_ERR BIT(1) +#define MT_RX_FILTR_CFG_PROMISC BIT(2) +#define MT_RX_FILTR_CFG_OTHER_BSS BIT(3) +#define MT_RX_FILTR_CFG_VER_ERR BIT(4) +#define MT_RX_FILTR_CFG_MCAST BIT(5) +#define MT_RX_FILTR_CFG_BCAST BIT(6) +#define MT_RX_FILTR_CFG_DUP BIT(7) +#define MT_RX_FILTR_CFG_CFACK BIT(8) +#define MT_RX_FILTR_CFG_CFEND BIT(9) +#define MT_RX_FILTR_CFG_ACK BIT(10) +#define MT_RX_FILTR_CFG_CTS BIT(11) +#define MT_RX_FILTR_CFG_RTS BIT(12) +#define MT_RX_FILTR_CFG_PSPOLL BIT(13) +#define MT_RX_FILTR_CFG_BA BIT(14) +#define MT_RX_FILTR_CFG_BAR BIT(15) +#define MT_RX_FILTR_CFG_CTRL_RSV BIT(16) + +#define MT_AUTO_RSP_CFG 0x1404 +#define MT_AUTO_RSP_EN BIT(0) +#define MT_AUTO_RSP_PREAMB_SHORT BIT(4) +#define MT_LEGACY_BASIC_RATE 0x1408 +#define MT_HT_BASIC_RATE 0x140c + +#define MT_HT_CTRL_CFG 0x1410 +#define MT_RX_PARSER_CFG 0x1418 +#define MT_RX_PARSER_RX_SET_NAV_ALL BIT(0) + +#define MT_EXT_CCA_CFG 0x141c +#define MT_EXT_CCA_CFG_CCA0 GENMASK(1, 0) +#define MT_EXT_CCA_CFG_CCA1 GENMASK(3, 2) +#define MT_EXT_CCA_CFG_CCA2 GENMASK(5, 4) +#define MT_EXT_CCA_CFG_CCA3 GENMASK(7, 6) +#define MT_EXT_CCA_CFG_CCA_MASK GENMASK(11, 8) +#define MT_EXT_CCA_CFG_ED_CCA_MASK GENMASK(15, 12) + +#define MT_TX_SW_CFG3 0x1478 + +#define MT_PN_PAD_MODE 0x150c + +#define MT_TXOP_HLDR_ET 0x1608 +#define MT_TXOP_HLDR_TX40M_BLK_EN BIT(1) + +#define MT_PROT_AUTO_TX_CFG 0x1648 +#define MT_PROT_AUTO_TX_CFG_PROT_PADJ GENMASK(11, 8) +#define MT_PROT_AUTO_TX_CFG_AUTO_PADJ GENMASK(27, 24) + +#define MT_RX_STAT_0 0x1700 +#define MT_RX_STAT_0_CRC_ERRORS GENMASK(15, 0) +#define MT_RX_STAT_0_PHY_ERRORS GENMASK(31, 16) + +#define MT_RX_STAT_1 0x1704 +#define MT_RX_STAT_1_CCA_ERRORS GENMASK(15, 0) +#define MT_RX_STAT_1_PLCP_ERRORS GENMASK(31, 16) + +#define MT_RX_STAT_2 0x1708 +#define MT_RX_STAT_2_DUP_ERRORS GENMASK(15, 0) +#define MT_RX_STAT_2_OVERFLOW_ERRORS GENMASK(31, 16) + +#define MT_TX_STA_0 0x170c +#define MT_TX_STA_0_BEACONS GENMASK(31, 16) + +#define MT_TX_STA_1 0x1710 +#define MT_TX_STA_2 0x1714 + +#define MT_TX_STAT_FIFO 0x1718 +#define MT_TX_STAT_FIFO_VALID BIT(0) +#define MT_TX_STAT_FIFO_SUCCESS BIT(5) +#define MT_TX_STAT_FIFO_AGGR BIT(6) +#define MT_TX_STAT_FIFO_ACKREQ BIT(7) +#define MT_TX_STAT_FIFO_WCID GENMASK(15, 8) +#define MT_TX_STAT_FIFO_RATE GENMASK(31, 16) + +#define MT_TX_AGG_STAT 0x171c + +#define MT_TX_AGG_CNT_BASE0 0x1720 +#define MT_MPDU_DENSITY_CNT 0x1740 +#define MT_TX_AGG_CNT_BASE1 0x174c + +#define MT_TX_AGG_CNT(_id) ((_id) < 8 ? \ + MT_TX_AGG_CNT_BASE0 + ((_id) << 2) : \ + MT_TX_AGG_CNT_BASE1 + (((_id) - 8) << 2)) + +#define MT_TX_STAT_FIFO_EXT 0x1798 +#define MT_TX_STAT_FIFO_EXT_RETRY GENMASK(7, 0) +#define MT_TX_STAT_FIFO_EXT_PKTID GENMASK(15, 8) + +#define MT_WCID_TX_RATE_BASE 0x1c00 +#define MT_WCID_TX_RATE(_i) (MT_WCID_TX_RATE_BASE + ((_i) << 3)) + +#define MT_BBP_CORE_BASE 0x2000 +#define MT_BBP_IBI_BASE 0x2100 +#define MT_BBP_AGC_BASE 0x2300 +#define MT_BBP_TXC_BASE 0x2400 +#define MT_BBP_RXC_BASE 0x2500 +#define MT_BBP_TXO_BASE 0x2600 +#define MT_BBP_TXBE_BASE 0x2700 +#define MT_BBP_RXFE_BASE 0x2800 +#define MT_BBP_RXO_BASE 0x2900 +#define MT_BBP_DFS_BASE 0x2a00 +#define MT_BBP_TR_BASE 0x2b00 +#define MT_BBP_CAL_BASE 0x2c00 +#define MT_BBP_DSC_BASE 0x2e00 +#define MT_BBP_PFMU_BASE 0x2f00 + +#define MT_BBP(_type, _n) (MT_BBP_##_type##_BASE + ((_n) << 2)) + +#define MT_BBP_CORE_R1_BW GENMASK(4, 3) + +#define MT_BBP_AGC_R0_CTRL_CHAN GENMASK(9, 8) +#define MT_BBP_AGC_R0_BW GENMASK(14, 12) + +/* AGC, R4/R5 */ +#define MT_BBP_AGC_LNA_HIGH_GAIN GENMASK(21, 16) +#define MT_BBP_AGC_LNA_MID_GAIN GENMASK(13, 8) +#define MT_BBP_AGC_LNA_LOW_GAIN GENMASK(5, 0) + +/* AGC, R6/R7 */ +#define MT_BBP_AGC_LNA_ULOW_GAIN GENMASK(5, 0) + +/* AGC, R8/R9 */ +#define MT_BBP_AGC_LNA_GAIN_MODE GENMASK(7, 6) +#define MT_BBP_AGC_GAIN GENMASK(14, 8) + +#define MT_BBP_AGC20_RSSI0 GENMASK(7, 0) +#define MT_BBP_AGC20_RSSI1 GENMASK(15, 8) + +#define MT_BBP_TXBE_R0_CTRL_CHAN GENMASK(1, 0) + +#define MT_WCID_ADDR_BASE 0x1800 +#define MT_WCID_ADDR(_n) (MT_WCID_ADDR_BASE + (_n) * 8) + +#define MT_SRAM_BASE 0x4000 + +#define MT_WCID_KEY_BASE 0x8000 +#define MT_WCID_KEY(_n) (MT_WCID_KEY_BASE + (_n) * 32) + +#define MT_WCID_IV_BASE 0xa000 +#define MT_WCID_IV(_n) (MT_WCID_IV_BASE + (_n) * 8) + +#define MT_WCID_ATTR_BASE 0xa800 +#define MT_WCID_ATTR(_n) (MT_WCID_ATTR_BASE + (_n) * 4) + +#define MT_WCID_ATTR_PAIRWISE BIT(0) +#define MT_WCID_ATTR_PKEY_MODE GENMASK(3, 1) +#define MT_WCID_ATTR_BSS_IDX GENMASK(6, 4) +#define MT_WCID_ATTR_RXWI_UDF GENMASK(9, 7) +#define MT_WCID_ATTR_PKEY_MODE_EXT BIT(10) +#define MT_WCID_ATTR_BSS_IDX_EXT BIT(11) +#define MT_WCID_ATTR_WAPI_MCBC BIT(15) +#define MT_WCID_ATTR_WAPI_KEYID GENMASK(31, 24) + +#define MT_SKEY_BASE_0 0xac00 +#define MT_SKEY_BASE_1 0xb400 +#define MT_SKEY_0(_bss, _idx) (MT_SKEY_BASE_0 + (4 * (_bss) + (_idx)) * 32) +#define MT_SKEY_1(_bss, _idx) (MT_SKEY_BASE_1 + (4 * ((_bss) & 7) + (_idx)) * 32) +#define MT_SKEY(_bss, _idx) (((_bss) & 8) ? MT_SKEY_1(_bss, _idx) : MT_SKEY_0(_bss, _idx)) + +#define MT_SKEY_MODE_BASE_0 0xb000 +#define MT_SKEY_MODE_BASE_1 0xb3f0 +#define MT_SKEY_MODE_0(_bss) (MT_SKEY_MODE_BASE_0 + (((_bss) / 2) << 2)) +#define MT_SKEY_MODE_1(_bss) (MT_SKEY_MODE_BASE_1 + ((((_bss) & 7) / 2) << 2)) +#define MT_SKEY_MODE(_bss) (((_bss) & 8) ? MT_SKEY_MODE_1(_bss) : MT_SKEY_MODE_0(_bss)) +#define MT_SKEY_MODE_MASK GENMASK(3, 0) +#define MT_SKEY_MODE_SHIFT(_bss, _idx) (4 * ((_idx) + 4 * ((_bss) & 1))) + +#define MT_BEACON_BASE 0xc000 + +#define MT_TEMP_SENSOR 0x1d000 +#define MT_TEMP_SENSOR_VAL GENMASK(6, 0) + +struct mt76_wcid_addr { + u8 macaddr[6]; + __le16 ba_mask; +} __packed __aligned(4); + +struct mt76_wcid_key { + u8 key[16]; + u8 tx_mic[8]; + u8 rx_mic[8]; +} __packed __aligned(4); + +enum mt76x02_cipher_type { + MT76X02_CIPHER_NONE, + MT76X02_CIPHER_WEP40, + MT76X02_CIPHER_WEP104, + MT76X02_CIPHER_TKIP, + MT76X02_CIPHER_AES_CCMP, + MT76X02_CIPHER_CKIP40, + MT76X02_CIPHER_CKIP104, + MT76X02_CIPHER_CKIP128, + MT76X02_CIPHER_WAPI, +}; + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_trace.c b/drivers/net/wireless/mediatek/mt76/mt76x02_trace.c new file mode 100644 index 000000000..a92b2b739 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_trace.c @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (C) 2016 Felix Fietkau + */ + +#include + +#ifndef __CHECKER__ +#define CREATE_TRACE_POINTS +#include "mt76x02_trace.h" + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_trace.h b/drivers/net/wireless/mediatek/mt76/mt76x02_trace.h new file mode 100644 index 000000000..56eea2f68 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_trace.h @@ -0,0 +1,87 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (C) 2016 Felix Fietkau + */ + +#if !defined(__MT76x02_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define __MT76x02_TRACE_H + +#include +#include "mt76x02.h" + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM mt76x02 + +#define MAXNAME 32 +#define DEV_ENTRY __array(char, wiphy_name, 32) +#define DEV_ASSIGN strscpy(__entry->wiphy_name, \ + wiphy_name(mt76_hw(dev)->wiphy), MAXNAME) +#define DEV_PR_FMT "%s" +#define DEV_PR_ARG __entry->wiphy_name + +#define TXID_ENTRY __field(u8, wcid) __field(u8, pktid) +#define TXID_PR_FMT " [%d:%d]" +#define TXID_PR_ARG __entry->wcid, __entry->pktid + +DECLARE_EVENT_CLASS(dev_evt, + TP_PROTO(struct mt76x02_dev *dev), + TP_ARGS(dev), + TP_STRUCT__entry( + DEV_ENTRY + ), + TP_fast_assign( + DEV_ASSIGN; + ), + TP_printk(DEV_PR_FMT, DEV_PR_ARG) +); + +DEFINE_EVENT(dev_evt, mac_txstat_poll, + TP_PROTO(struct mt76x02_dev *dev), + TP_ARGS(dev) +); + +TRACE_EVENT(mac_txstat_fetch, + TP_PROTO(struct mt76x02_dev *dev, + struct mt76x02_tx_status *stat), + + TP_ARGS(dev, stat), + + TP_STRUCT__entry( + DEV_ENTRY + TXID_ENTRY + __field(bool, success) + __field(bool, aggr) + __field(bool, ack_req) + __field(u16, rate) + __field(u8, retry) + ), + + TP_fast_assign( + DEV_ASSIGN; + __entry->success = stat->success; + __entry->aggr = stat->aggr; + __entry->ack_req = stat->ack_req; + __entry->wcid = stat->wcid; + __entry->pktid = stat->pktid; + __entry->rate = stat->rate; + __entry->retry = stat->retry; + ), + + TP_printk( + DEV_PR_FMT TXID_PR_FMT + " success:%d aggr:%d ack_req:%d" + " rate:%04x retry:%d", + DEV_PR_ARG, TXID_PR_ARG, + __entry->success, __entry->aggr, __entry->ack_req, + __entry->rate, __entry->retry + ) +); + +#endif + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE mt76x02_trace + +#include diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_txrx.c b/drivers/net/wireless/mediatek/mt76/mt76x02_txrx.c new file mode 100644 index 000000000..301b43180 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_txrx.c @@ -0,0 +1,183 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (C) 2016 Felix Fietkau + * Copyright (C) 2018 Lorenzo Bianconi + */ + +#include + +#include "mt76x02.h" + +void mt76x02_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, + struct sk_buff *skb) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct mt76x02_dev *dev = hw->priv; + struct ieee80211_vif *vif = info->control.vif; + struct mt76_wcid *wcid = &dev->mt76.global_wcid; + + if (control->sta) { + struct mt76x02_sta *msta; + + msta = (struct mt76x02_sta *)control->sta->drv_priv; + wcid = &msta->wcid; + } else if (vif) { + struct mt76x02_vif *mvif; + + mvif = (struct mt76x02_vif *)vif->drv_priv; + wcid = &mvif->group_wcid; + } + + mt76_tx(&dev->mphy, control->sta, wcid, skb); +} +EXPORT_SYMBOL_GPL(mt76x02_tx); + +void mt76x02_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, + struct sk_buff *skb, u32 *info) +{ + struct mt76x02_dev *dev = container_of(mdev, struct mt76x02_dev, mt76); + void *rxwi = skb->data; + + if (q == MT_RXQ_MCU) { + mt76_mcu_rx_event(&dev->mt76, skb); + return; + } + + skb_pull(skb, sizeof(struct mt76x02_rxwi)); + if (mt76x02_mac_process_rx(dev, skb, rxwi)) { + dev_kfree_skb(skb); + return; + } + + mt76_rx(mdev, q, skb); +} +EXPORT_SYMBOL_GPL(mt76x02_queue_rx_skb); + +s8 mt76x02_tx_get_max_txpwr_adj(struct mt76x02_dev *dev, + const struct ieee80211_tx_rate *rate) +{ + s8 max_txpwr; + + if (rate->flags & IEEE80211_TX_RC_VHT_MCS) { + u8 mcs = ieee80211_rate_get_vht_mcs(rate); + + if (mcs == 8 || mcs == 9) { + max_txpwr = dev->rate_power.vht[0]; + } else { + u8 nss, idx; + + nss = ieee80211_rate_get_vht_nss(rate); + idx = ((nss - 1) << 3) + mcs; + max_txpwr = dev->rate_power.ht[idx & 0xf]; + } + } else if (rate->flags & IEEE80211_TX_RC_MCS) { + max_txpwr = dev->rate_power.ht[rate->idx & 0xf]; + } else { + enum nl80211_band band = dev->mphy.chandef.chan->band; + + if (band == NL80211_BAND_2GHZ) { + const struct ieee80211_rate *r; + struct wiphy *wiphy = dev->mt76.hw->wiphy; + struct mt76x02_rate_power *rp = &dev->rate_power; + + r = &wiphy->bands[band]->bitrates[rate->idx]; + if (r->flags & IEEE80211_RATE_SHORT_PREAMBLE) + max_txpwr = rp->cck[r->hw_value & 0x3]; + else + max_txpwr = rp->ofdm[r->hw_value & 0x7]; + } else { + max_txpwr = dev->rate_power.ofdm[rate->idx & 0x7]; + } + } + + return max_txpwr; +} + +s8 mt76x02_tx_get_txpwr_adj(struct mt76x02_dev *dev, s8 txpwr, s8 max_txpwr_adj) +{ + txpwr = min_t(s8, txpwr, dev->txpower_conf); + txpwr -= (dev->target_power + dev->target_power_delta[0]); + txpwr = min_t(s8, txpwr, max_txpwr_adj); + + if (!dev->enable_tpc) + return 0; + else if (txpwr >= 0) + return min_t(s8, txpwr, 7); + else + return (txpwr < -16) ? 8 : (txpwr + 32) / 2; +} + +void mt76x02_tx_set_txpwr_auto(struct mt76x02_dev *dev, s8 txpwr) +{ + s8 txpwr_adj; + + txpwr_adj = mt76x02_tx_get_txpwr_adj(dev, txpwr, + dev->rate_power.ofdm[4]); + mt76_rmw_field(dev, MT_PROT_AUTO_TX_CFG, + MT_PROT_AUTO_TX_CFG_PROT_PADJ, txpwr_adj); + mt76_rmw_field(dev, MT_PROT_AUTO_TX_CFG, + MT_PROT_AUTO_TX_CFG_AUTO_PADJ, txpwr_adj); +} +EXPORT_SYMBOL_GPL(mt76x02_tx_set_txpwr_auto); + +bool mt76x02_tx_status_data(struct mt76_dev *mdev, u8 *update) +{ + struct mt76x02_dev *dev = container_of(mdev, struct mt76x02_dev, mt76); + struct mt76x02_tx_status stat; + + if (!mt76x02_mac_load_tx_status(dev, &stat)) + return false; + + mt76x02_send_tx_status(dev, &stat, update); + + return true; +} +EXPORT_SYMBOL_GPL(mt76x02_tx_status_data); + +int mt76x02_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, + enum mt76_txq_id qid, struct mt76_wcid *wcid, + struct ieee80211_sta *sta, + struct mt76_tx_info *tx_info) +{ + struct mt76x02_dev *dev = container_of(mdev, struct mt76x02_dev, mt76); + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx_info->skb->data; + struct mt76x02_txwi *txwi = txwi_ptr; + bool ampdu = IEEE80211_SKB_CB(tx_info->skb)->flags & IEEE80211_TX_CTL_AMPDU; + int hdrlen, len, pid, qsel = MT_QSEL_EDCA; + + if (qid == MT_TXQ_PSD && wcid && wcid->idx < 128) + mt76x02_mac_wcid_set_drop(dev, wcid->idx, false); + + hdrlen = ieee80211_hdrlen(hdr->frame_control); + len = tx_info->skb->len - (hdrlen & 2); + mt76x02_mac_write_txwi(dev, txwi, tx_info->skb, wcid, sta, len); + + pid = mt76_tx_status_skb_add(mdev, wcid, tx_info->skb); + + /* encode packet rate for no-skb packet id to fix up status reporting */ + if (pid == MT_PACKET_ID_NO_SKB) + pid = MT_PACKET_ID_HAS_RATE | + (le16_to_cpu(txwi->rate) & MT_RXWI_RATE_INDEX) | + FIELD_PREP(MT_PKTID_AC, + skb_get_queue_mapping(tx_info->skb)); + + txwi->pktid = pid; + + if (mt76_is_skb_pktid(pid) && ampdu) + qsel = MT_QSEL_MGMT; + + tx_info->info = FIELD_PREP(MT_TXD_INFO_QSEL, qsel) | + MT_TXD_INFO_80211; + + if (!wcid || wcid->hw_key_idx == 0xff || wcid->sw_iv) + tx_info->info |= MT_TXD_INFO_WIV; + + if (sta) { + struct mt76x02_sta *msta = (struct mt76x02_sta *)sta->drv_priv; + + ewma_pktlen_add(&msta->pktlen, tx_info->skb->len); + } + + return 0; +} +EXPORT_SYMBOL_GPL(mt76x02_tx_prepare_skb); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_usb.h b/drivers/net/wireless/mediatek/mt76/mt76x02_usb.h new file mode 100644 index 000000000..49ab05c1f --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_usb.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (C) 2018 Lorenzo Bianconi + */ + +#ifndef __MT76x02_USB_H +#define __MT76x02_USB_H + +#include "mt76x02.h" + +int mt76x02u_mac_start(struct mt76x02_dev *dev); +void mt76x02u_init_mcu(struct mt76_dev *dev); +void mt76x02u_mcu_fw_reset(struct mt76x02_dev *dev); +int mt76x02u_mcu_fw_send_data(struct mt76x02_dev *dev, const void *data, + int data_len, u32 max_payload, u32 offset); + +int mt76x02u_skb_dma_info(struct sk_buff *skb, int port, u32 flags); +int mt76x02u_tx_prepare_skb(struct mt76_dev *mdev, void *data, + enum mt76_txq_id qid, struct mt76_wcid *wcid, + struct ieee80211_sta *sta, + struct mt76_tx_info *tx_info); +void mt76x02u_tx_complete_skb(struct mt76_dev *mdev, struct mt76_queue_entry *e); +void mt76x02u_init_beacon_config(struct mt76x02_dev *dev); +void mt76x02u_exit_beacon_config(struct mt76x02_dev *dev); +#endif /* __MT76x02_USB_H */ diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_usb_core.c b/drivers/net/wireless/mediatek/mt76/mt76x02_usb_core.c new file mode 100644 index 000000000..3a28a8cc1 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_usb_core.c @@ -0,0 +1,286 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (C) 2018 Lorenzo Bianconi + */ + +#include "mt76x02_usb.h" + +static void mt76x02u_remove_dma_hdr(struct sk_buff *skb) +{ + int hdr_len; + + skb_pull(skb, sizeof(struct mt76x02_txwi) + MT_DMA_HDR_LEN); + hdr_len = ieee80211_get_hdrlen_from_skb(skb); + if (hdr_len % 4) + mt76x02_remove_hdr_pad(skb, 2); +} + +void mt76x02u_tx_complete_skb(struct mt76_dev *mdev, struct mt76_queue_entry *e) +{ + mt76x02u_remove_dma_hdr(e->skb); + mt76_tx_complete_skb(mdev, e->wcid, e->skb); +} +EXPORT_SYMBOL_GPL(mt76x02u_tx_complete_skb); + +int mt76x02u_mac_start(struct mt76x02_dev *dev) +{ + mt76x02_mac_reset_counters(dev); + + mt76_wr(dev, MT_MAC_SYS_CTRL, MT_MAC_SYS_CTRL_ENABLE_TX); + if (!mt76x02_wait_for_wpdma(&dev->mt76, 200000)) + return -ETIMEDOUT; + + mt76_wr(dev, MT_RX_FILTR_CFG, dev->mt76.rxfilter); + + mt76_wr(dev, MT_MAC_SYS_CTRL, + MT_MAC_SYS_CTRL_ENABLE_TX | + MT_MAC_SYS_CTRL_ENABLE_RX); + + if (!mt76x02_wait_for_wpdma(&dev->mt76, 50)) + return -ETIMEDOUT; + + return 0; +} +EXPORT_SYMBOL_GPL(mt76x02u_mac_start); + +int mt76x02u_skb_dma_info(struct sk_buff *skb, int port, u32 flags) +{ + u32 info, pad; + + /* Buffer layout: + * | 4B | xfer len | pad | 4B | + * | TXINFO | pkt/cmd | zero pad to 4B | zero | + * + * length field of TXINFO should be set to 'xfer len'. + */ + info = FIELD_PREP(MT_TXD_INFO_LEN, round_up(skb->len, 4)) | + FIELD_PREP(MT_TXD_INFO_DPORT, port) | flags; + put_unaligned_le32(info, skb_push(skb, sizeof(info))); + + pad = round_up(skb->len, 4) + 4 - skb->len; + return mt76_skb_adjust_pad(skb, pad); +} + +int mt76x02u_tx_prepare_skb(struct mt76_dev *mdev, void *data, + enum mt76_txq_id qid, struct mt76_wcid *wcid, + struct ieee80211_sta *sta, + struct mt76_tx_info *tx_info) +{ + struct mt76x02_dev *dev = container_of(mdev, struct mt76x02_dev, mt76); + int pid, len = tx_info->skb->len, ep = dev->mphy.q_tx[qid]->ep; + struct mt76x02_txwi *txwi; + bool ampdu = IEEE80211_SKB_CB(tx_info->skb)->flags & IEEE80211_TX_CTL_AMPDU; + enum mt76_qsel qsel; + u32 flags; + int err; + + mt76_insert_hdr_pad(tx_info->skb); + + txwi = (struct mt76x02_txwi *)(tx_info->skb->data - sizeof(*txwi)); + mt76x02_mac_write_txwi(dev, txwi, tx_info->skb, wcid, sta, len); + skb_push(tx_info->skb, sizeof(*txwi)); + + pid = mt76_tx_status_skb_add(mdev, wcid, tx_info->skb); + + /* encode packet rate for no-skb packet id to fix up status reporting */ + if (pid == MT_PACKET_ID_NO_SKB) + pid = MT_PACKET_ID_HAS_RATE | + (le16_to_cpu(txwi->rate) & MT_PKTID_RATE) | + FIELD_PREP(MT_PKTID_AC, + skb_get_queue_mapping(tx_info->skb)); + + txwi->pktid = pid; + + if ((mt76_is_skb_pktid(pid) && ampdu) || ep == MT_EP_OUT_HCCA) + qsel = MT_QSEL_MGMT; + else + qsel = MT_QSEL_EDCA; + + flags = FIELD_PREP(MT_TXD_INFO_QSEL, qsel) | + MT_TXD_INFO_80211; + if (!wcid || wcid->hw_key_idx == 0xff || wcid->sw_iv) + flags |= MT_TXD_INFO_WIV; + + if (sta) { + struct mt76x02_sta *msta = (struct mt76x02_sta *)sta->drv_priv; + + ewma_pktlen_add(&msta->pktlen, tx_info->skb->len); + } + + err = mt76x02u_skb_dma_info(tx_info->skb, WLAN_PORT, flags); + if (err && wcid) + /* Release pktid in case of error. */ + idr_remove(&wcid->pktid, pid); + + return err; +} +EXPORT_SYMBOL_GPL(mt76x02u_tx_prepare_skb); + +/* Trigger pre-TBTT event 8 ms before TBTT */ +#define PRE_TBTT_USEC 8000 + +/* Beacon SRAM memory is limited to 8kB. We need to send PS buffered frames + * (which can be 1500 bytes big) via beacon memory. That make limit of number + * of slots to 5. TODO: dynamically calculate offsets in beacon SRAM. + */ +#define N_BCN_SLOTS 5 + +static void mt76x02u_start_pre_tbtt_timer(struct mt76x02_dev *dev) +{ + u64 time; + u32 tbtt; + + /* Get remaining TBTT in usec */ + tbtt = mt76_get_field(dev, MT_TBTT_TIMER, MT_TBTT_TIMER_VAL); + tbtt *= 32; + + if (tbtt <= PRE_TBTT_USEC) { + queue_work(system_highpri_wq, &dev->pre_tbtt_work); + return; + } + + time = (tbtt - PRE_TBTT_USEC) * 1000ull; + hrtimer_start(&dev->pre_tbtt_timer, time, HRTIMER_MODE_REL); +} + +static void mt76x02u_restart_pre_tbtt_timer(struct mt76x02_dev *dev) +{ + u32 tbtt, dw0, dw1; + u64 tsf, time; + + /* Get remaining TBTT in usec */ + tbtt = mt76_get_field(dev, MT_TBTT_TIMER, MT_TBTT_TIMER_VAL); + tbtt *= 32; + + dw0 = mt76_rr(dev, MT_TSF_TIMER_DW0); + dw1 = mt76_rr(dev, MT_TSF_TIMER_DW1); + tsf = (u64)dw0 << 32 | dw1; + dev_dbg(dev->mt76.dev, "TSF: %llu us TBTT %u us\n", tsf, tbtt); + + /* Convert beacon interval in TU (1024 usec) to nsec */ + time = ((1000000000ull * dev->mt76.beacon_int) >> 10); + + /* Adjust time to trigger hrtimer 8ms before TBTT */ + if (tbtt < PRE_TBTT_USEC) + time -= (PRE_TBTT_USEC - tbtt) * 1000ull; + else + time += (tbtt - PRE_TBTT_USEC) * 1000ull; + + hrtimer_start(&dev->pre_tbtt_timer, time, HRTIMER_MODE_REL); +} + +static void mt76x02u_stop_pre_tbtt_timer(struct mt76x02_dev *dev) +{ + do { + hrtimer_cancel(&dev->pre_tbtt_timer); + cancel_work_sync(&dev->pre_tbtt_work); + /* Timer can be rearmed by work. */ + } while (hrtimer_active(&dev->pre_tbtt_timer)); +} + +static void mt76x02u_pre_tbtt_work(struct work_struct *work) +{ + struct mt76x02_dev *dev = + container_of(work, struct mt76x02_dev, pre_tbtt_work); + struct beacon_bc_data data = { + .dev = dev, + }; + struct sk_buff *skb; + int nbeacons; + + if (!dev->mt76.beacon_mask || dev->mphy.offchannel) + return; + + __skb_queue_head_init(&data.q); + + mt76x02_resync_beacon_timer(dev); + + /* Prevent corrupt transmissions during update */ + mt76_set(dev, MT_BCN_BYPASS_MASK, 0xffff); + dev->beacon_data_count = 0; + + ieee80211_iterate_active_interfaces_atomic(mt76_hw(dev), + IEEE80211_IFACE_ITER_RESUME_ALL, + mt76x02_update_beacon_iter, &data); + + while ((skb = __skb_dequeue(&data.q)) != NULL) + mt76x02_mac_set_beacon(dev, skb); + + mt76_csa_check(&dev->mt76); + + if (dev->mt76.csa_complete) { + mt76_csa_finish(&dev->mt76); + goto out; + } + + nbeacons = hweight8(dev->mt76.beacon_mask); + mt76x02_enqueue_buffered_bc(dev, &data, N_BCN_SLOTS - nbeacons); + + while ((skb = __skb_dequeue(&data.q)) != NULL) + mt76x02_mac_set_beacon(dev, skb); + +out: + mt76_wr(dev, MT_BCN_BYPASS_MASK, + 0xff00 | ~(0xff00 >> dev->beacon_data_count)); + + mt76x02u_restart_pre_tbtt_timer(dev); +} + +static enum hrtimer_restart mt76x02u_pre_tbtt_interrupt(struct hrtimer *timer) +{ + struct mt76x02_dev *dev = + container_of(timer, struct mt76x02_dev, pre_tbtt_timer); + + queue_work(system_highpri_wq, &dev->pre_tbtt_work); + + return HRTIMER_NORESTART; +} + +static void mt76x02u_pre_tbtt_enable(struct mt76x02_dev *dev, bool en) +{ + if (en && dev->mt76.beacon_mask && + !hrtimer_active(&dev->pre_tbtt_timer)) + mt76x02u_start_pre_tbtt_timer(dev); + if (!en) + mt76x02u_stop_pre_tbtt_timer(dev); +} + +static void mt76x02u_beacon_enable(struct mt76x02_dev *dev, bool en) +{ + if (WARN_ON_ONCE(!dev->mt76.beacon_int)) + return; + + if (en) + mt76x02u_start_pre_tbtt_timer(dev); +} + +void mt76x02u_init_beacon_config(struct mt76x02_dev *dev) +{ + static const struct mt76x02_beacon_ops beacon_ops = { + .nslots = N_BCN_SLOTS, + .slot_size = (8192 / N_BCN_SLOTS) & ~63, + .pre_tbtt_enable = mt76x02u_pre_tbtt_enable, + .beacon_enable = mt76x02u_beacon_enable, + }; + dev->beacon_ops = &beacon_ops; + + hrtimer_setup(&dev->pre_tbtt_timer, mt76x02u_pre_tbtt_interrupt, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); + INIT_WORK(&dev->pre_tbtt_work, mt76x02u_pre_tbtt_work); + + mt76x02_init_beacon_config(dev); +} +EXPORT_SYMBOL_GPL(mt76x02u_init_beacon_config); + +void mt76x02u_exit_beacon_config(struct mt76x02_dev *dev) +{ + if (!test_bit(MT76_REMOVED, &dev->mphy.state)) + mt76_clear(dev, MT_BEACON_TIME_CFG, + MT_BEACON_TIME_CFG_TIMER_EN | + MT_BEACON_TIME_CFG_SYNC_MODE | + MT_BEACON_TIME_CFG_TBTT_EN | + MT_BEACON_TIME_CFG_BEACON_TX); + + mt76x02u_stop_pre_tbtt_timer(dev); +} +EXPORT_SYMBOL_GPL(mt76x02u_exit_beacon_config); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_usb_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76x02_usb_mcu.c new file mode 100644 index 000000000..968c73e06 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_usb_mcu.c @@ -0,0 +1,297 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (C) 2018 Lorenzo Bianconi + */ + +#include +#include + +#include "mt76x02.h" +#include "mt76x02_mcu.h" +#include "mt76x02_usb.h" + +#define MT_CMD_HDR_LEN 4 + +#define MT_FCE_DMA_ADDR 0x0230 +#define MT_FCE_DMA_LEN 0x0234 + +#define MT_TX_CPU_FROM_FCE_CPU_DESC_IDX 0x09a8 + +static void +mt76x02u_multiple_mcu_reads(struct mt76_dev *dev, u8 *data, int len) +{ + struct mt76_usb *usb = &dev->usb; + int i; + + WARN_ON_ONCE(len / 8 != usb->mcu.rp_len); + + for (i = 0; i < usb->mcu.rp_len; i++) { + u32 reg = get_unaligned_le32(data + 8 * i) - usb->mcu.base; + u32 val = get_unaligned_le32(data + 8 * i + 4); + + WARN_ON_ONCE(usb->mcu.rp[i].reg != reg); + usb->mcu.rp[i].value = val; + } +} + +static int mt76x02u_mcu_wait_resp(struct mt76_dev *dev, u8 seq) +{ + struct mt76_usb *usb = &dev->usb; + u8 *data = usb->mcu.data; + int i, len, ret; + u32 rxfce; + + for (i = 0; i < 5; i++) { + ret = mt76u_bulk_msg(dev, data, MCU_RESP_URB_SIZE, &len, + 300, MT_EP_IN_CMD_RESP); + if (ret == -ETIMEDOUT) + continue; + if (ret) + goto out; + + if (usb->mcu.rp) + mt76x02u_multiple_mcu_reads(dev, data + 4, len - 8); + + rxfce = get_unaligned_le32(data); + if (seq == FIELD_GET(MT_RX_FCE_INFO_CMD_SEQ, rxfce) && + FIELD_GET(MT_RX_FCE_INFO_EVT_TYPE, rxfce) == EVT_CMD_DONE) + return 0; + + dev_err(dev->dev, "error: MCU resp evt:%lx seq:%hhx-%lx\n", + FIELD_GET(MT_RX_FCE_INFO_EVT_TYPE, rxfce), + seq, FIELD_GET(MT_RX_FCE_INFO_CMD_SEQ, rxfce)); + } +out: + dev_err(dev->dev, "error: %s failed with %d\n", __func__, ret); + return ret; +} + +static int +__mt76x02u_mcu_send_msg(struct mt76_dev *dev, struct sk_buff *skb, + int cmd, bool wait_resp) +{ + u8 seq = 0; + u32 info; + int ret; + + if (test_bit(MT76_REMOVED, &dev->phy.state)) { + ret = 0; + goto out; + } + + if (wait_resp) { + seq = ++dev->mcu.msg_seq & 0xf; + if (!seq) + seq = ++dev->mcu.msg_seq & 0xf; + } + + info = FIELD_PREP(MT_MCU_MSG_CMD_SEQ, seq) | + FIELD_PREP(MT_MCU_MSG_CMD_TYPE, cmd) | + MT_MCU_MSG_TYPE_CMD; + ret = mt76x02u_skb_dma_info(skb, CPU_TX_PORT, info); + if (ret) + return ret; + + ret = mt76u_bulk_msg(dev, skb->data, skb->len, NULL, 500, + MT_EP_OUT_INBAND_CMD); + if (ret) + goto out; + + if (wait_resp) + ret = mt76x02u_mcu_wait_resp(dev, seq); + +out: + consume_skb(skb); + + return ret; +} + +static int +mt76x02u_mcu_send_msg(struct mt76_dev *dev, int cmd, const void *data, + int len, bool wait_resp) +{ + struct sk_buff *skb; + int err; + + skb = mt76_mcu_msg_alloc(dev, data, len); + if (!skb) + return -ENOMEM; + + mutex_lock(&dev->mcu.mutex); + err = __mt76x02u_mcu_send_msg(dev, skb, cmd, wait_resp); + mutex_unlock(&dev->mcu.mutex); + + return err; +} + +static inline void skb_put_le32(struct sk_buff *skb, u32 val) +{ + put_unaligned_le32(val, skb_put(skb, 4)); +} + +static int +mt76x02u_mcu_wr_rp(struct mt76_dev *dev, u32 base, + const struct mt76_reg_pair *data, int n) +{ + const int max_vals_per_cmd = MT_INBAND_PACKET_MAX_LEN / 8; + const int CMD_RANDOM_WRITE = 12; + struct sk_buff *skb; + int cnt, i, ret; + + if (!n) + return 0; + + cnt = min(max_vals_per_cmd, n); + + skb = alloc_skb(cnt * 8 + MT_DMA_HDR_LEN + 4, GFP_KERNEL); + if (!skb) + return -ENOMEM; + skb_reserve(skb, MT_DMA_HDR_LEN); + + for (i = 0; i < cnt; i++) { + skb_put_le32(skb, base + data[i].reg); + skb_put_le32(skb, data[i].value); + } + + mutex_lock(&dev->mcu.mutex); + ret = __mt76x02u_mcu_send_msg(dev, skb, CMD_RANDOM_WRITE, cnt == n); + mutex_unlock(&dev->mcu.mutex); + if (ret) + return ret; + + return mt76x02u_mcu_wr_rp(dev, base, data + cnt, n - cnt); +} + +static int +mt76x02u_mcu_rd_rp(struct mt76_dev *dev, u32 base, + struct mt76_reg_pair *data, int n) +{ + const int CMD_RANDOM_READ = 10; + const int max_vals_per_cmd = MT_INBAND_PACKET_MAX_LEN / 8; + struct mt76_usb *usb = &dev->usb; + struct sk_buff *skb; + int cnt, i, ret; + + if (!n) + return 0; + + cnt = min(max_vals_per_cmd, n); + if (cnt != n) + return -EINVAL; + + skb = alloc_skb(cnt * 8 + MT_DMA_HDR_LEN + 4, GFP_KERNEL); + if (!skb) + return -ENOMEM; + skb_reserve(skb, MT_DMA_HDR_LEN); + + for (i = 0; i < cnt; i++) { + skb_put_le32(skb, base + data[i].reg); + skb_put_le32(skb, data[i].value); + } + + mutex_lock(&dev->mcu.mutex); + + usb->mcu.rp = data; + usb->mcu.rp_len = n; + usb->mcu.base = base; + + ret = __mt76x02u_mcu_send_msg(dev, skb, CMD_RANDOM_READ, true); + + usb->mcu.rp = NULL; + + mutex_unlock(&dev->mcu.mutex); + + return ret; +} + +void mt76x02u_mcu_fw_reset(struct mt76x02_dev *dev) +{ + mt76u_vendor_request(&dev->mt76, MT_VEND_DEV_MODE, + USB_DIR_OUT | USB_TYPE_VENDOR, + 0x1, 0, NULL, 0); +} +EXPORT_SYMBOL_GPL(mt76x02u_mcu_fw_reset); + +static int +__mt76x02u_mcu_fw_send_data(struct mt76x02_dev *dev, u8 *data, + const void *fw_data, int len, u32 dst_addr) +{ + __le32 info; + u32 val; + int err, data_len; + + info = cpu_to_le32(FIELD_PREP(MT_MCU_MSG_PORT, CPU_TX_PORT) | + FIELD_PREP(MT_MCU_MSG_LEN, len) | + MT_MCU_MSG_TYPE_CMD); + + memcpy(data, &info, sizeof(info)); + memcpy(data + sizeof(info), fw_data, len); + memset(data + sizeof(info) + len, 0, 4); + + mt76u_single_wr(&dev->mt76, MT_VEND_WRITE_FCE, + MT_FCE_DMA_ADDR, dst_addr); + len = roundup(len, 4); + mt76u_single_wr(&dev->mt76, MT_VEND_WRITE_FCE, + MT_FCE_DMA_LEN, len << 16); + + data_len = MT_CMD_HDR_LEN + len + sizeof(info); + + err = mt76u_bulk_msg(&dev->mt76, data, data_len, NULL, 1000, + MT_EP_OUT_INBAND_CMD); + if (err) { + dev_err(dev->mt76.dev, "firmware upload failed: %d\n", err); + return err; + } + + val = mt76_rr(dev, MT_TX_CPU_FROM_FCE_CPU_DESC_IDX); + val++; + mt76_wr(dev, MT_TX_CPU_FROM_FCE_CPU_DESC_IDX, val); + + return 0; +} + +int mt76x02u_mcu_fw_send_data(struct mt76x02_dev *dev, const void *data, + int data_len, u32 max_payload, u32 offset) +{ + int len, err = 0, pos = 0, max_len = max_payload - 8; + u8 *buf; + + buf = kmalloc(max_payload, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + while (data_len > 0) { + len = min_t(int, data_len, max_len); + err = __mt76x02u_mcu_fw_send_data(dev, buf, data + pos, + len, offset + pos); + if (err < 0) + break; + + data_len -= len; + pos += len; + usleep_range(5000, 10000); + } + kfree(buf); + + return err; +} +EXPORT_SYMBOL_GPL(mt76x02u_mcu_fw_send_data); + +void mt76x02u_init_mcu(struct mt76_dev *dev) +{ + static const struct mt76_mcu_ops mt76x02u_mcu_ops = { + .headroom = MT_CMD_HDR_LEN, + .tailroom = 8, + .mcu_send_msg = mt76x02u_mcu_send_msg, + .mcu_parse_response = mt76x02_mcu_parse_response, + .mcu_wr_rp = mt76x02u_mcu_wr_rp, + .mcu_rd_rp = mt76x02u_mcu_rd_rp, + }; + + dev->mcu_ops = &mt76x02u_mcu_ops; +} +EXPORT_SYMBOL_GPL(mt76x02u_init_mcu); + +MODULE_AUTHOR("Lorenzo Bianconi "); +MODULE_DESCRIPTION("MediaTek MT76x02 MCU helpers"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_util.c b/drivers/net/wireless/mediatek/mt76/mt76x02_util.c new file mode 100644 index 000000000..e5d9d1bc9 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_util.c @@ -0,0 +1,699 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (C) 2018 Stanislaw Gruszka + * Copyright (C) 2016 Felix Fietkau + */ + +#include +#include "mt76x02.h" + +#define MT76x02_CCK_RATE(_idx, _rate) { \ + .bitrate = _rate, \ + .flags = IEEE80211_RATE_SHORT_PREAMBLE, \ + .hw_value = (MT_PHY_TYPE_CCK << 8) | (_idx), \ + .hw_value_short = (MT_PHY_TYPE_CCK << 8) | (8 + (_idx)), \ +} + +struct ieee80211_rate mt76x02_rates[] = { + MT76x02_CCK_RATE(0, 10), + MT76x02_CCK_RATE(1, 20), + MT76x02_CCK_RATE(2, 55), + MT76x02_CCK_RATE(3, 110), + OFDM_RATE(0, 60), + OFDM_RATE(1, 90), + OFDM_RATE(2, 120), + OFDM_RATE(3, 180), + OFDM_RATE(4, 240), + OFDM_RATE(5, 360), + OFDM_RATE(6, 480), + OFDM_RATE(7, 540), +}; +EXPORT_SYMBOL_GPL(mt76x02_rates); + +static const struct ieee80211_iface_limit mt76x02_if_limits[] = { + { + .max = 1, + .types = BIT(NL80211_IFTYPE_ADHOC) + }, { + .max = 8, + .types = BIT(NL80211_IFTYPE_STATION) | +#ifdef CONFIG_MAC80211_MESH + BIT(NL80211_IFTYPE_MESH_POINT) | +#endif + BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_P2P_GO) | + BIT(NL80211_IFTYPE_AP) + }, +}; + +static const struct ieee80211_iface_limit mt76x02u_if_limits[] = { + { + .max = 1, + .types = BIT(NL80211_IFTYPE_ADHOC) + }, { + .max = 2, + .types = BIT(NL80211_IFTYPE_STATION) | +#ifdef CONFIG_MAC80211_MESH + BIT(NL80211_IFTYPE_MESH_POINT) | +#endif + BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_P2P_GO) | + BIT(NL80211_IFTYPE_AP) + }, +}; + +static const struct ieee80211_iface_combination mt76x02_if_comb[] = { + { + .limits = mt76x02_if_limits, + .n_limits = ARRAY_SIZE(mt76x02_if_limits), + .max_interfaces = 8, + .num_different_channels = 1, + .beacon_int_infra_match = true, + .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) | + BIT(NL80211_CHAN_WIDTH_20) | + BIT(NL80211_CHAN_WIDTH_40) | + BIT(NL80211_CHAN_WIDTH_80), + } +}; + +static const struct ieee80211_iface_combination mt76x02u_if_comb[] = { + { + .limits = mt76x02u_if_limits, + .n_limits = ARRAY_SIZE(mt76x02u_if_limits), + .max_interfaces = 2, + .num_different_channels = 1, + .beacon_int_infra_match = true, + } +}; + +static void +mt76x02_led_set_config(struct mt76_phy *mphy, u8 delay_on, u8 delay_off) +{ + struct mt76x02_dev *dev = container_of(mphy->dev, struct mt76x02_dev, + mt76); + u32 val; + + val = FIELD_PREP(MT_LED_STATUS_DURATION, 0xff) | + FIELD_PREP(MT_LED_STATUS_OFF, delay_off) | + FIELD_PREP(MT_LED_STATUS_ON, delay_on); + + mt76_wr(dev, MT_LED_S0(mphy->leds.pin), val); + mt76_wr(dev, MT_LED_S1(mphy->leds.pin), val); + + val = MT_LED_CTRL_REPLAY(mphy->leds.pin) | + MT_LED_CTRL_KICK(mphy->leds.pin); + if (mphy->leds.al) + val |= MT_LED_CTRL_POLARITY(mphy->leds.pin); + mt76_wr(dev, MT_LED_CTRL, val); +} + +static int +mt76x02_led_set_blink(struct led_classdev *led_cdev, + unsigned long *delay_on, + unsigned long *delay_off) +{ + struct mt76_phy *mphy = container_of(led_cdev, struct mt76_phy, + leds.cdev); + u8 delta_on, delta_off; + + delta_off = max_t(u8, *delay_off / 10, 1); + delta_on = max_t(u8, *delay_on / 10, 1); + + mt76x02_led_set_config(mphy, delta_on, delta_off); + + return 0; +} + +static void +mt76x02_led_set_brightness(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct mt76_phy *mphy = container_of(led_cdev, struct mt76_phy, + leds.cdev); + + if (!brightness) + mt76x02_led_set_config(mphy, 0, 0xff); + else + mt76x02_led_set_config(mphy, 0xff, 0); +} + +int mt76x02_init_device(struct mt76x02_dev *dev) +{ + struct ieee80211_hw *hw = mt76_hw(dev); + struct wiphy *wiphy = hw->wiphy; + + INIT_DELAYED_WORK(&dev->mphy.mac_work, mt76x02_mac_work); + + hw->queues = 4; + hw->max_rates = 1; + hw->max_report_rates = 7; + hw->max_rate_tries = 1; + hw->extra_tx_headroom = 2; + + if (mt76_is_usb(&dev->mt76)) { + hw->extra_tx_headroom += sizeof(struct mt76x02_txwi) + + MT_DMA_HDR_LEN; + wiphy->iface_combinations = mt76x02u_if_comb; + wiphy->n_iface_combinations = ARRAY_SIZE(mt76x02u_if_comb); + } else { + INIT_DELAYED_WORK(&dev->wdt_work, mt76x02_wdt_work); + + mt76x02_dfs_init_detector(dev); + + wiphy->reg_notifier = mt76x02_regd_notifier; + wiphy->iface_combinations = mt76x02_if_comb; + wiphy->n_iface_combinations = ARRAY_SIZE(mt76x02_if_comb); + + /* init led callbacks */ + if (IS_ENABLED(CONFIG_MT76_LEDS)) { + dev->mphy.leds.cdev.brightness_set = + mt76x02_led_set_brightness; + dev->mphy.leds.cdev.blink_set = mt76x02_led_set_blink; + } + } + + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_VHT_IBSS); + + hw->sta_data_size = sizeof(struct mt76x02_sta); + hw->vif_data_size = sizeof(struct mt76x02_vif); + + ieee80211_hw_set(hw, SUPPORTS_HT_CCK_RATES); + ieee80211_hw_set(hw, HOST_BROADCAST_PS_BUFFERING); + ieee80211_hw_set(hw, NEEDS_UNIQUE_STA_ADDR); + + dev->mt76.global_wcid.idx = 255; + dev->mt76.global_wcid.hw_key_idx = -1; + dev->slottime = 9; + + if (is_mt76x2(dev)) { + dev->mphy.sband_2g.sband.ht_cap.cap |= + IEEE80211_HT_CAP_LDPC_CODING; + dev->mphy.sband_5g.sband.ht_cap.cap |= + IEEE80211_HT_CAP_LDPC_CODING; + dev->mphy.chainmask = 0x202; + dev->mphy.antenna_mask = 3; + } else { + dev->mphy.chainmask = 0x101; + dev->mphy.antenna_mask = 1; + } + + return 0; +} +EXPORT_SYMBOL_GPL(mt76x02_init_device); + +void mt76x02_configure_filter(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *total_flags, u64 multicast) +{ + struct mt76x02_dev *dev = hw->priv; + u32 flags = 0; + +#define MT76_FILTER(_flag, _hw) do { \ + flags |= *total_flags & FIF_##_flag; \ + dev->mt76.rxfilter &= ~(_hw); \ + dev->mt76.rxfilter |= !(flags & FIF_##_flag) * (_hw); \ + } while (0) + + mutex_lock(&dev->mt76.mutex); + + dev->mt76.rxfilter &= ~MT_RX_FILTR_CFG_OTHER_BSS; + + MT76_FILTER(FCSFAIL, MT_RX_FILTR_CFG_CRC_ERR); + MT76_FILTER(PLCPFAIL, MT_RX_FILTR_CFG_PHY_ERR); + MT76_FILTER(CONTROL, MT_RX_FILTR_CFG_ACK | + MT_RX_FILTR_CFG_CTS | + MT_RX_FILTR_CFG_CFEND | + MT_RX_FILTR_CFG_CFACK | + MT_RX_FILTR_CFG_BA | + MT_RX_FILTR_CFG_CTRL_RSV); + MT76_FILTER(PSPOLL, MT_RX_FILTR_CFG_PSPOLL); + + *total_flags = flags; + mt76_wr(dev, MT_RX_FILTR_CFG, dev->mt76.rxfilter); + + mutex_unlock(&dev->mt76.mutex); +} +EXPORT_SYMBOL_GPL(mt76x02_configure_filter); + +int mt76x02_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mt76x02_dev *dev = container_of(mdev, struct mt76x02_dev, mt76); + struct mt76x02_sta *msta = (struct mt76x02_sta *)sta->drv_priv; + struct mt76x02_vif *mvif = (struct mt76x02_vif *)vif->drv_priv; + int idx = 0; + + memset(msta, 0, sizeof(*msta)); + + idx = mt76_wcid_alloc(dev->mt76.wcid_mask, MT76x02_N_WCIDS); + if (idx < 0) + return -ENOSPC; + + msta->vif = mvif; + msta->wcid.sta = 1; + msta->wcid.idx = idx; + msta->wcid.hw_key_idx = -1; + mt76x02_mac_wcid_setup(dev, idx, mvif->idx, sta->addr); + mt76x02_mac_wcid_set_drop(dev, idx, false); + ewma_pktlen_init(&msta->pktlen); + + if (vif->type == NL80211_IFTYPE_AP) + set_bit(MT_WCID_FLAG_CHECK_PS, &msta->wcid.flags); + + return 0; +} +EXPORT_SYMBOL_GPL(mt76x02_sta_add); + +void mt76x02_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mt76x02_dev *dev = container_of(mdev, struct mt76x02_dev, mt76); + struct mt76_wcid *wcid = (struct mt76_wcid *)sta->drv_priv; + int idx = wcid->idx; + + mt76x02_mac_wcid_set_drop(dev, idx, true); + mt76x02_mac_wcid_setup(dev, idx, 0, NULL); +} +EXPORT_SYMBOL_GPL(mt76x02_sta_remove); + +static void +mt76x02_vif_init(struct mt76x02_dev *dev, struct ieee80211_vif *vif, + unsigned int idx) +{ + struct mt76x02_vif *mvif = (struct mt76x02_vif *)vif->drv_priv; + struct mt76_txq *mtxq; + + memset(mvif, 0, sizeof(*mvif)); + + mvif->idx = idx; + mvif->group_wcid.idx = MT_VIF_WCID(idx); + mt76_wcid_init(&mvif->group_wcid, 0); + + mtxq = (struct mt76_txq *)vif->txq->drv_priv; + rcu_assign_pointer(dev->mt76.wcid[MT_VIF_WCID(idx)], &mvif->group_wcid); + mtxq->wcid = MT_VIF_WCID(idx); +} + +int +mt76x02_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + struct mt76x02_dev *dev = hw->priv; + unsigned int idx = 0; + + /* Allow to change address in HW if we create first interface. */ + if (!dev->mt76.vif_mask && + (((vif->addr[0] ^ dev->mphy.macaddr[0]) & ~GENMASK(4, 1)) || + memcmp(vif->addr + 1, dev->mphy.macaddr + 1, ETH_ALEN - 1))) + mt76x02_mac_setaddr(dev, vif->addr); + + if (vif->addr[0] & BIT(1)) + idx = 1 + (((dev->mphy.macaddr[0] ^ vif->addr[0]) >> 2) & 7); + + /* + * Client mode typically only has one configurable BSSID register, + * which is used for bssidx=0. This is linked to the MAC address. + * Since mac80211 allows changing interface types, and we cannot + * force the use of the primary MAC address for a station mode + * interface, we need some other way of configuring a per-interface + * remote BSSID. + * The hardware provides an AP-Client feature, where bssidx 0-7 are + * used for AP mode and bssidx 8-15 for client mode. + * We shift the station interface bss index by 8 to force the + * hardware to recognize the BSSID. + * The resulting bssidx mismatch for unicast frames is ignored by hw. + */ + if (vif->type == NL80211_IFTYPE_STATION) + idx += 8; + + /* vif is already set or idx is 8 for AP/Mesh/... */ + if (dev->mt76.vif_mask & BIT_ULL(idx) || + (vif->type != NL80211_IFTYPE_STATION && idx > 7)) + return -EBUSY; + + dev->mt76.vif_mask |= BIT_ULL(idx); + + mt76x02_vif_init(dev, vif, idx); + return 0; +} +EXPORT_SYMBOL_GPL(mt76x02_add_interface); + +void mt76x02_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct mt76x02_dev *dev = hw->priv; + struct mt76x02_vif *mvif = (struct mt76x02_vif *)vif->drv_priv; + + dev->mt76.vif_mask &= ~BIT_ULL(mvif->idx); + rcu_assign_pointer(dev->mt76.wcid[mvif->group_wcid.idx], NULL); + mt76_wcid_cleanup(&dev->mt76, &mvif->group_wcid); +} +EXPORT_SYMBOL_GPL(mt76x02_remove_interface); + +int mt76x02_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_ampdu_params *params) +{ + enum ieee80211_ampdu_mlme_action action = params->action; + struct ieee80211_sta *sta = params->sta; + struct mt76x02_dev *dev = hw->priv; + struct mt76x02_sta *msta = (struct mt76x02_sta *)sta->drv_priv; + struct ieee80211_txq *txq = sta->txq[params->tid]; + u16 tid = params->tid; + u16 ssn = params->ssn; + struct mt76_txq *mtxq; + int ret = 0; + + if (!txq) + return -EINVAL; + + mtxq = (struct mt76_txq *)txq->drv_priv; + + mutex_lock(&dev->mt76.mutex); + switch (action) { + case IEEE80211_AMPDU_RX_START: + mt76_rx_aggr_start(&dev->mt76, &msta->wcid, tid, + ssn, params->buf_size); + mt76_set(dev, MT_WCID_ADDR(msta->wcid.idx) + 4, BIT(16 + tid)); + break; + case IEEE80211_AMPDU_RX_STOP: + mt76_rx_aggr_stop(&dev->mt76, &msta->wcid, tid); + mt76_clear(dev, MT_WCID_ADDR(msta->wcid.idx) + 4, + BIT(16 + tid)); + break; + case IEEE80211_AMPDU_TX_OPERATIONAL: + mtxq->aggr = true; + mtxq->send_bar = false; + ieee80211_send_bar(vif, sta->addr, tid, mtxq->agg_ssn); + break; + case IEEE80211_AMPDU_TX_STOP_FLUSH: + case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: + mtxq->aggr = false; + break; + case IEEE80211_AMPDU_TX_START: + mtxq->agg_ssn = IEEE80211_SN_TO_SEQ(ssn); + ret = IEEE80211_AMPDU_TX_START_IMMEDIATE; + break; + case IEEE80211_AMPDU_TX_STOP_CONT: + mtxq->aggr = false; + ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); + break; + } + mutex_unlock(&dev->mt76.mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(mt76x02_ampdu_action); + +int mt76x02_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct mt76x02_dev *dev = hw->priv; + struct mt76x02_vif *mvif = (struct mt76x02_vif *)vif->drv_priv; + struct mt76x02_sta *msta; + struct mt76_wcid *wcid; + int idx = key->keyidx; + int ret; + + /* fall back to sw encryption for unsupported ciphers */ + switch (key->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + case WLAN_CIPHER_SUITE_TKIP: + case WLAN_CIPHER_SUITE_CCMP: + break; + default: + return -EOPNOTSUPP; + } + + /* + * The hardware does not support per-STA RX GTK, fall back + * to software mode for these. + */ + if ((vif->type == NL80211_IFTYPE_ADHOC || + vif->type == NL80211_IFTYPE_MESH_POINT) && + (key->cipher == WLAN_CIPHER_SUITE_TKIP || + key->cipher == WLAN_CIPHER_SUITE_CCMP) && + !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) + return -EOPNOTSUPP; + + /* + * In USB AP mode, broadcast/multicast frames are setup in beacon + * data registers and sent via HW beacons engine, they require to + * be already encrypted. + */ + if (mt76_is_usb(&dev->mt76) && + vif->type == NL80211_IFTYPE_AP && + !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) + return -EOPNOTSUPP; + + /* MT76x0 GTK offloading does not work with more than one VIF */ + if (is_mt76x0(dev) && !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) + return -EOPNOTSUPP; + + msta = sta ? (struct mt76x02_sta *)sta->drv_priv : NULL; + wcid = msta ? &msta->wcid : &mvif->group_wcid; + + if (cmd != SET_KEY) { + if (idx == wcid->hw_key_idx) { + wcid->hw_key_idx = -1; + wcid->sw_iv = false; + } + + return 0; + } + + key->hw_key_idx = wcid->idx; + wcid->hw_key_idx = idx; + if (key->flags & IEEE80211_KEY_FLAG_RX_MGMT) { + key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX; + wcid->sw_iv = true; + } + mt76_wcid_key_setup(&dev->mt76, wcid, key); + + if (!msta) { + if (key || wcid->hw_key_idx == idx) { + ret = mt76x02_mac_wcid_set_key(dev, wcid->idx, key); + if (ret) + return ret; + } + + return mt76x02_mac_shared_key_setup(dev, mvif->idx, idx, key); + } + + return mt76x02_mac_wcid_set_key(dev, msta->wcid.idx, key); +} +EXPORT_SYMBOL_GPL(mt76x02_set_key); + +int mt76x02_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + unsigned int link_id, u16 queue, + const struct ieee80211_tx_queue_params *params) +{ + struct mt76x02_dev *dev = hw->priv; + u8 cw_min = 5, cw_max = 10, qid; + u32 val; + + qid = dev->mphy.q_tx[queue]->hw_idx; + + if (params->cw_min) + cw_min = fls(params->cw_min); + if (params->cw_max) + cw_max = fls(params->cw_max); + + val = FIELD_PREP(MT_EDCA_CFG_TXOP, params->txop) | + FIELD_PREP(MT_EDCA_CFG_AIFSN, params->aifs) | + FIELD_PREP(MT_EDCA_CFG_CWMIN, cw_min) | + FIELD_PREP(MT_EDCA_CFG_CWMAX, cw_max); + mt76_wr(dev, MT_EDCA_CFG_AC(qid), val); + + val = mt76_rr(dev, MT_WMM_TXOP(qid)); + val &= ~(MT_WMM_TXOP_MASK << MT_WMM_TXOP_SHIFT(qid)); + val |= params->txop << MT_WMM_TXOP_SHIFT(qid); + mt76_wr(dev, MT_WMM_TXOP(qid), val); + + val = mt76_rr(dev, MT_WMM_AIFSN); + val &= ~(MT_WMM_AIFSN_MASK << MT_WMM_AIFSN_SHIFT(qid)); + val |= params->aifs << MT_WMM_AIFSN_SHIFT(qid); + mt76_wr(dev, MT_WMM_AIFSN, val); + + val = mt76_rr(dev, MT_WMM_CWMIN); + val &= ~(MT_WMM_CWMIN_MASK << MT_WMM_CWMIN_SHIFT(qid)); + val |= cw_min << MT_WMM_CWMIN_SHIFT(qid); + mt76_wr(dev, MT_WMM_CWMIN, val); + + val = mt76_rr(dev, MT_WMM_CWMAX); + val &= ~(MT_WMM_CWMAX_MASK << MT_WMM_CWMAX_SHIFT(qid)); + val |= cw_max << MT_WMM_CWMAX_SHIFT(qid); + mt76_wr(dev, MT_WMM_CWMAX, val); + + return 0; +} +EXPORT_SYMBOL_GPL(mt76x02_conf_tx); + +void mt76x02_set_tx_ackto(struct mt76x02_dev *dev) +{ + u8 ackto, sifs, slottime = dev->slottime; + + /* As defined by IEEE 802.11-2007 17.3.8.6 */ + slottime += 3 * dev->coverage_class; + mt76_rmw_field(dev, MT_BKOFF_SLOT_CFG, + MT_BKOFF_SLOT_CFG_SLOTTIME, slottime); + + sifs = mt76_get_field(dev, MT_XIFS_TIME_CFG, + MT_XIFS_TIME_CFG_OFDM_SIFS); + + ackto = slottime + sifs; + mt76_rmw_field(dev, MT_TX_TIMEOUT_CFG, + MT_TX_TIMEOUT_CFG_ACKTO, ackto); +} +EXPORT_SYMBOL_GPL(mt76x02_set_tx_ackto); + +void mt76x02_set_coverage_class(struct ieee80211_hw *hw, + int radio_idx, s16 coverage_class) +{ + struct mt76x02_dev *dev = hw->priv; + + mutex_lock(&dev->mt76.mutex); + dev->coverage_class = max_t(s16, coverage_class, 0); + mt76x02_set_tx_ackto(dev); + mutex_unlock(&dev->mt76.mutex); +} +EXPORT_SYMBOL_GPL(mt76x02_set_coverage_class); + +int mt76x02_set_rts_threshold(struct ieee80211_hw *hw, int radio_idx, u32 val) +{ + struct mt76x02_dev *dev = hw->priv; + + if (val != ~0 && val > 0xffff) + return -EINVAL; + + mutex_lock(&dev->mt76.mutex); + mt76x02_mac_set_rts_thresh(dev, val); + mutex_unlock(&dev->mt76.mutex); + + return 0; +} +EXPORT_SYMBOL_GPL(mt76x02_set_rts_threshold); + +void mt76x02_sta_rate_tbl_update(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mt76x02_dev *dev = hw->priv; + struct mt76x02_sta *msta = (struct mt76x02_sta *)sta->drv_priv; + struct ieee80211_sta_rates *rates = rcu_dereference(sta->rates); + struct ieee80211_tx_rate rate = {}; + + if (!rates) + return; + + rate.idx = rates->rate[0].idx; + rate.flags = rates->rate[0].flags; + mt76x02_mac_wcid_set_rate(dev, &msta->wcid, &rate); +} +EXPORT_SYMBOL_GPL(mt76x02_sta_rate_tbl_update); + +void mt76x02_remove_hdr_pad(struct sk_buff *skb, int len) +{ + int hdrlen; + + if (!len) + return; + + hdrlen = ieee80211_get_hdrlen_from_skb(skb); + memmove(skb->data + len, skb->data, hdrlen); + skb_pull(skb, len); +} +EXPORT_SYMBOL_GPL(mt76x02_remove_hdr_pad); + +void mt76x02_sw_scan_complete(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct mt76x02_dev *dev = hw->priv; + + clear_bit(MT76_SCANNING, &dev->mphy.state); + if (dev->cal.gain_init_done) { + /* Restore AGC gain and resume calibration after scanning. */ + dev->cal.low_gain = -1; + ieee80211_queue_delayed_work(hw, &dev->cal_work, 0); + } +} +EXPORT_SYMBOL_GPL(mt76x02_sw_scan_complete); + +void mt76x02_sta_ps(struct mt76_dev *mdev, struct ieee80211_sta *sta, + bool ps) +{ + struct mt76x02_dev *dev = container_of(mdev, struct mt76x02_dev, mt76); + struct mt76x02_sta *msta = (struct mt76x02_sta *)sta->drv_priv; + int idx = msta->wcid.idx; + + mt76_stop_tx_queues(&dev->mphy, sta, true); + if (mt76_is_mmio(mdev)) + mt76x02_mac_wcid_set_drop(dev, idx, ps); +} +EXPORT_SYMBOL_GPL(mt76x02_sta_ps); + +void mt76x02_bss_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, + u64 changed) +{ + struct mt76x02_vif *mvif = (struct mt76x02_vif *)vif->drv_priv; + struct mt76x02_dev *dev = hw->priv; + + mutex_lock(&dev->mt76.mutex); + + if (changed & BSS_CHANGED_BSSID) + mt76x02_mac_set_bssid(dev, mvif->idx, info->bssid); + + if (changed & BSS_CHANGED_HT || changed & BSS_CHANGED_ERP_CTS_PROT) + mt76x02_mac_set_tx_protection(dev, info->use_cts_prot, + info->ht_operation_mode); + + if (changed & BSS_CHANGED_BEACON_INT) { + mt76_rmw_field(dev, MT_BEACON_TIME_CFG, + MT_BEACON_TIME_CFG_INTVAL, + info->beacon_int << 4); + dev->mt76.beacon_int = info->beacon_int; + } + + if (changed & BSS_CHANGED_BEACON_ENABLED) + mt76x02_mac_set_beacon_enable(dev, vif, info->enable_beacon); + + if (changed & BSS_CHANGED_ERP_PREAMBLE) + mt76x02_mac_set_short_preamble(dev, info->use_short_preamble); + + if (changed & BSS_CHANGED_ERP_SLOT) { + int slottime = info->use_short_slot ? 9 : 20; + + dev->slottime = slottime; + mt76x02_set_tx_ackto(dev); + } + + mutex_unlock(&dev->mt76.mutex); +} +EXPORT_SYMBOL_GPL(mt76x02_bss_info_changed); + +void mt76x02_config_mac_addr_list(struct mt76x02_dev *dev) +{ + struct ieee80211_hw *hw = mt76_hw(dev); + struct wiphy *wiphy = hw->wiphy; + int i; + + for (i = 0; i < ARRAY_SIZE(dev->macaddr_list); i++) { + u8 *addr = dev->macaddr_list[i].addr; + + memcpy(addr, dev->mphy.macaddr, ETH_ALEN); + + if (!i) + continue; + + addr[0] |= BIT(1); + addr[0] ^= ((i - 1) << 2); + } + wiphy->addresses = dev->macaddr_list; + wiphy->n_addresses = ARRAY_SIZE(dev->macaddr_list); +} +EXPORT_SYMBOL_GPL(mt76x02_config_mac_addr_list); + +MODULE_DESCRIPTION("MediaTek MT76x02 helpers"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/Kconfig b/drivers/net/wireless/mediatek/mt76/mt76x2/Kconfig new file mode 100644 index 000000000..d820510cb --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/Kconfig @@ -0,0 +1,29 @@ +# SPDX-License-Identifier: BSD-3-Clause-Clear +config MT76x2_COMMON + tristate + select MT76x02_LIB + +config MT76x2E + tristate "MediaTek MT76x2E (PCIe) support" + select MT76x2_COMMON + depends on MAC80211 + depends on PCI + help + This adds support for MT7612/MT7602/MT7662-based PCIe wireless + devices, which comply with IEEE 802.11ac standards and support + 2SS to 866Mbit/s PHY rate. + + To compile this driver as a module, choose M here. + +config MT76x2U + tristate "MediaTek MT76x2U (USB) support" + select MT76x2_COMMON + select MT76x02_USB + depends on MAC80211 + depends on USB + help + This adds support for MT7612U-based USB 3.0 wireless dongles, + which comply with IEEE 802.11ac standards and support 2SS to + 866Mbit/s PHY rate. + + To compile this driver as a module, choose M here. diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/Makefile b/drivers/net/wireless/mediatek/mt76/mt76x2/Makefile new file mode 100644 index 000000000..cbc90a961 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/Makefile @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: BSD-3-Clause-Clear +obj-$(CONFIG_MT76x2_COMMON) += mt76x2-common.o +obj-$(CONFIG_MT76x2E) += mt76x2e.o +obj-$(CONFIG_MT76x2U) += mt76x2u.o + +mt76x2-common-y := \ + eeprom.o mac.o init.o phy.o mcu.o + +mt76x2e-y := \ + pci.o pci_main.o pci_init.o pci_mcu.o \ + pci_phy.o + +mt76x2u-y := \ + usb.o usb_init.o usb_main.o usb_mac.o usb_mcu.o \ + usb_phy.o diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.c new file mode 100644 index 000000000..782813aad --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.c @@ -0,0 +1,512 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (C) 2016 Felix Fietkau + */ + +#include +#include +#include +#include "mt76x2.h" +#include "eeprom.h" + +#define EE_FIELD(_name, _value) [MT_EE_##_name] = (_value) | 1 + +static int +mt76x2_eeprom_get_macaddr(struct mt76x02_dev *dev) +{ + void *src = dev->mt76.eeprom.data + MT_EE_MAC_ADDR; + + memcpy(dev->mphy.macaddr, src, ETH_ALEN); + return 0; +} + +static bool +mt76x2_has_cal_free_data(struct mt76x02_dev *dev, u8 *efuse) +{ + u16 *efuse_w = (u16 *)efuse; + + if (efuse_w[MT_EE_NIC_CONF_0] != 0) + return false; + + if (efuse_w[MT_EE_XTAL_TRIM_1] == 0xffff) + return false; + + if (efuse_w[MT_EE_TX_POWER_DELTA_BW40] != 0) + return false; + + if (efuse_w[MT_EE_TX_POWER_0_START_2G] == 0xffff) + return false; + + if (efuse_w[MT_EE_TX_POWER_0_GRP3_TX_POWER_DELTA] != 0) + return false; + + if (efuse_w[MT_EE_TX_POWER_0_GRP4_TSSI_SLOPE] == 0xffff) + return false; + + return true; +} + +static void +mt76x2_apply_cal_free_data(struct mt76x02_dev *dev, u8 *efuse) +{ +#define GROUP_5G(_id) \ + MT_EE_TX_POWER_0_START_5G + MT_TX_POWER_GROUP_SIZE_5G * (_id), \ + MT_EE_TX_POWER_0_START_5G + MT_TX_POWER_GROUP_SIZE_5G * (_id) + 1, \ + MT_EE_TX_POWER_1_START_5G + MT_TX_POWER_GROUP_SIZE_5G * (_id), \ + MT_EE_TX_POWER_1_START_5G + MT_TX_POWER_GROUP_SIZE_5G * (_id) + 1 + + static const u8 cal_free_bytes[] = { + MT_EE_XTAL_TRIM_1, + MT_EE_TX_POWER_EXT_PA_5G + 1, + MT_EE_TX_POWER_0_START_2G, + MT_EE_TX_POWER_0_START_2G + 1, + MT_EE_TX_POWER_1_START_2G, + MT_EE_TX_POWER_1_START_2G + 1, + GROUP_5G(0), + GROUP_5G(1), + GROUP_5G(2), + GROUP_5G(3), + GROUP_5G(4), + GROUP_5G(5), + MT_EE_RF_2G_TSSI_OFF_TXPOWER, + MT_EE_RF_2G_RX_HIGH_GAIN + 1, + MT_EE_RF_5G_GRP0_1_RX_HIGH_GAIN, + MT_EE_RF_5G_GRP0_1_RX_HIGH_GAIN + 1, + MT_EE_RF_5G_GRP2_3_RX_HIGH_GAIN, + MT_EE_RF_5G_GRP2_3_RX_HIGH_GAIN + 1, + MT_EE_RF_5G_GRP4_5_RX_HIGH_GAIN, + MT_EE_RF_5G_GRP4_5_RX_HIGH_GAIN + 1, + }; + struct device_node *np = dev->mt76.dev->of_node; + u8 *eeprom = dev->mt76.eeprom.data; + u8 prev_grp0[4] = { + eeprom[MT_EE_TX_POWER_0_START_5G], + eeprom[MT_EE_TX_POWER_0_START_5G + 1], + eeprom[MT_EE_TX_POWER_1_START_5G], + eeprom[MT_EE_TX_POWER_1_START_5G + 1] + }; + u16 val; + int i; + + if (!np || !of_property_read_bool(np, "mediatek,eeprom-merge-otp")) + return; + + if (!mt76x2_has_cal_free_data(dev, efuse)) + return; + + for (i = 0; i < ARRAY_SIZE(cal_free_bytes); i++) { + int offset = cal_free_bytes[i]; + + eeprom[offset] = efuse[offset]; + } + + if (!(efuse[MT_EE_TX_POWER_0_START_5G] | + efuse[MT_EE_TX_POWER_0_START_5G + 1])) + memcpy(eeprom + MT_EE_TX_POWER_0_START_5G, prev_grp0, 2); + if (!(efuse[MT_EE_TX_POWER_1_START_5G] | + efuse[MT_EE_TX_POWER_1_START_5G + 1])) + memcpy(eeprom + MT_EE_TX_POWER_1_START_5G, prev_grp0 + 2, 2); + + val = get_unaligned_le16(efuse + MT_EE_BT_RCAL_RESULT); + if (val != 0xffff) + eeprom[MT_EE_BT_RCAL_RESULT] = val & 0xff; + + val = get_unaligned_le16(efuse + MT_EE_BT_VCDL_CALIBRATION); + if (val != 0xffff) + eeprom[MT_EE_BT_VCDL_CALIBRATION + 1] = val >> 8; + + val = get_unaligned_le16(efuse + MT_EE_BT_PMUCFG); + if (val != 0xffff) + eeprom[MT_EE_BT_PMUCFG] = val & 0xff; +} + +static int mt76x2_check_eeprom(struct mt76x02_dev *dev) +{ + u16 val = get_unaligned_le16(dev->mt76.eeprom.data); + + if (!val) + val = get_unaligned_le16(dev->mt76.eeprom.data + MT_EE_PCI_ID); + + switch (val) { + case 0x7662: + case 0x7612: + return 0; + default: + dev_err(dev->mt76.dev, "EEPROM data check failed: %04x\n", val); + return -EINVAL; + } +} + +static int +mt76x2_eeprom_load(struct mt76x02_dev *dev) +{ + void *efuse; + bool found; + int ret; + + ret = mt76_eeprom_init(&dev->mt76, MT7662_EEPROM_SIZE); + if (ret < 0) + return ret; + + found = ret; + if (found) + found = !mt76x2_check_eeprom(dev); + + dev->mt76.otp.data = devm_kzalloc(dev->mt76.dev, MT7662_EEPROM_SIZE, + GFP_KERNEL); + dev->mt76.otp.size = MT7662_EEPROM_SIZE; + if (!dev->mt76.otp.data) + return -ENOMEM; + + efuse = dev->mt76.otp.data; + + if (mt76x02_get_efuse_data(dev, 0, efuse, MT7662_EEPROM_SIZE, + MT_EE_READ)) + goto out; + + if (found) { + mt76x2_apply_cal_free_data(dev, efuse); + } else { + /* FIXME: check if efuse data is complete */ + found = true; + memcpy(dev->mt76.eeprom.data, efuse, MT7662_EEPROM_SIZE); + } + +out: + if (!found) + return -ENOENT; + + return 0; +} + +static void +mt76x2_set_rx_gain_group(struct mt76x02_dev *dev, u8 val) +{ + s8 *dest = dev->cal.rx.high_gain; + + if (!mt76x02_field_valid(val)) { + dest[0] = 0; + dest[1] = 0; + return; + } + + dest[0] = mt76x02_sign_extend(val, 4); + dest[1] = mt76x02_sign_extend(val >> 4, 4); +} + +static void +mt76x2_set_rssi_offset(struct mt76x02_dev *dev, int chain, u8 val) +{ + s8 *dest = dev->cal.rx.rssi_offset; + + if (!mt76x02_field_valid(val)) { + dest[chain] = 0; + return; + } + + dest[chain] = mt76x02_sign_extend_optional(val, 7); +} + +static enum mt76x2_cal_channel_group +mt76x2_get_cal_channel_group(int channel) +{ + if (channel >= 184 && channel <= 196) + return MT_CH_5G_JAPAN; + if (channel <= 48) + return MT_CH_5G_UNII_1; + if (channel <= 64) + return MT_CH_5G_UNII_2; + if (channel <= 114) + return MT_CH_5G_UNII_2E_1; + if (channel <= 144) + return MT_CH_5G_UNII_2E_2; + return MT_CH_5G_UNII_3; +} + +static u8 +mt76x2_get_5g_rx_gain(struct mt76x02_dev *dev, u8 channel) +{ + enum mt76x2_cal_channel_group group; + + group = mt76x2_get_cal_channel_group(channel); + switch (group) { + case MT_CH_5G_JAPAN: + return mt76x02_eeprom_get(dev, + MT_EE_RF_5G_GRP0_1_RX_HIGH_GAIN); + case MT_CH_5G_UNII_1: + return mt76x02_eeprom_get(dev, + MT_EE_RF_5G_GRP0_1_RX_HIGH_GAIN) >> 8; + case MT_CH_5G_UNII_2: + return mt76x02_eeprom_get(dev, + MT_EE_RF_5G_GRP2_3_RX_HIGH_GAIN); + case MT_CH_5G_UNII_2E_1: + return mt76x02_eeprom_get(dev, + MT_EE_RF_5G_GRP2_3_RX_HIGH_GAIN) >> 8; + case MT_CH_5G_UNII_2E_2: + return mt76x02_eeprom_get(dev, + MT_EE_RF_5G_GRP4_5_RX_HIGH_GAIN); + default: + return mt76x02_eeprom_get(dev, + MT_EE_RF_5G_GRP4_5_RX_HIGH_GAIN) >> 8; + } +} + +void mt76x2_read_rx_gain(struct mt76x02_dev *dev) +{ + struct ieee80211_channel *chan = dev->mphy.chandef.chan; + int channel = chan->hw_value; + s8 lna_5g[3], lna_2g; + bool use_lna; + u8 lna = 0; + u16 val; + + if (chan->band == NL80211_BAND_2GHZ) + val = mt76x02_eeprom_get(dev, MT_EE_RF_2G_RX_HIGH_GAIN) >> 8; + else + val = mt76x2_get_5g_rx_gain(dev, channel); + + mt76x2_set_rx_gain_group(dev, val); + + mt76x02_get_rx_gain(dev, chan->band, &val, &lna_2g, lna_5g); + mt76x2_set_rssi_offset(dev, 0, val); + mt76x2_set_rssi_offset(dev, 1, val >> 8); + + dev->cal.rx.mcu_gain = (lna_2g & 0xff); + dev->cal.rx.mcu_gain |= (lna_5g[0] & 0xff) << 8; + dev->cal.rx.mcu_gain |= (lna_5g[1] & 0xff) << 16; + dev->cal.rx.mcu_gain |= (lna_5g[2] & 0xff) << 24; + + val = mt76x02_eeprom_get(dev, MT_EE_NIC_CONF_1); + if (chan->band == NL80211_BAND_2GHZ) + use_lna = !(val & MT_EE_NIC_CONF_1_LNA_EXT_2G); + else + use_lna = !(val & MT_EE_NIC_CONF_1_LNA_EXT_5G); + + if (use_lna) + lna = mt76x02_get_lna_gain(dev, &lna_2g, lna_5g, chan); + + dev->cal.rx.lna_gain = mt76x02_sign_extend(lna, 8); +} +EXPORT_SYMBOL_GPL(mt76x2_read_rx_gain); + +void mt76x2_get_rate_power(struct mt76x02_dev *dev, struct mt76x02_rate_power *t, + struct ieee80211_channel *chan) +{ + bool is_5ghz; + u16 val; + + is_5ghz = chan->band == NL80211_BAND_5GHZ; + + memset(t, 0, sizeof(*t)); + + val = mt76x02_eeprom_get(dev, MT_EE_TX_POWER_CCK); + t->cck[0] = t->cck[1] = mt76x02_rate_power_val(val); + t->cck[2] = t->cck[3] = mt76x02_rate_power_val(val >> 8); + + if (is_5ghz) + val = mt76x02_eeprom_get(dev, MT_EE_TX_POWER_OFDM_5G_6M); + else + val = mt76x02_eeprom_get(dev, MT_EE_TX_POWER_OFDM_2G_6M); + t->ofdm[0] = t->ofdm[1] = mt76x02_rate_power_val(val); + t->ofdm[2] = t->ofdm[3] = mt76x02_rate_power_val(val >> 8); + + if (is_5ghz) + val = mt76x02_eeprom_get(dev, MT_EE_TX_POWER_OFDM_5G_24M); + else + val = mt76x02_eeprom_get(dev, MT_EE_TX_POWER_OFDM_2G_24M); + t->ofdm[4] = t->ofdm[5] = mt76x02_rate_power_val(val); + t->ofdm[6] = t->ofdm[7] = mt76x02_rate_power_val(val >> 8); + + val = mt76x02_eeprom_get(dev, MT_EE_TX_POWER_HT_MCS0); + t->ht[0] = t->ht[1] = mt76x02_rate_power_val(val); + t->ht[2] = t->ht[3] = mt76x02_rate_power_val(val >> 8); + + val = mt76x02_eeprom_get(dev, MT_EE_TX_POWER_HT_MCS4); + t->ht[4] = t->ht[5] = mt76x02_rate_power_val(val); + t->ht[6] = t->ht[7] = mt76x02_rate_power_val(val >> 8); + + val = mt76x02_eeprom_get(dev, MT_EE_TX_POWER_HT_MCS8); + t->ht[8] = t->ht[9] = mt76x02_rate_power_val(val); + t->ht[10] = t->ht[11] = mt76x02_rate_power_val(val >> 8); + + val = mt76x02_eeprom_get(dev, MT_EE_TX_POWER_HT_MCS12); + t->ht[12] = t->ht[13] = mt76x02_rate_power_val(val); + t->ht[14] = t->ht[15] = mt76x02_rate_power_val(val >> 8); + + val = mt76x02_eeprom_get(dev, MT_EE_TX_POWER_VHT_MCS8); + if (!is_5ghz) + val >>= 8; + t->vht[0] = t->vht[1] = mt76x02_rate_power_val(val >> 8); +} +EXPORT_SYMBOL_GPL(mt76x2_get_rate_power); + +static void +mt76x2_get_power_info_2g(struct mt76x02_dev *dev, + struct mt76x2_tx_power_info *t, + struct ieee80211_channel *chan, + int chain, int offset) +{ + int channel = chan->hw_value; + int delta_idx; + u8 data[6]; + u16 val; + + if (channel < 6) + delta_idx = 3; + else if (channel < 11) + delta_idx = 4; + else + delta_idx = 5; + + mt76x02_eeprom_copy(dev, offset, data, sizeof(data)); + + t->chain[chain].tssi_slope = data[0]; + t->chain[chain].tssi_offset = data[1]; + t->chain[chain].target_power = data[2]; + t->chain[chain].delta = + mt76x02_sign_extend_optional(data[delta_idx], 7); + + val = mt76x02_eeprom_get(dev, MT_EE_RF_2G_TSSI_OFF_TXPOWER); + t->target_power = val >> 8; +} + +static void +mt76x2_get_power_info_5g(struct mt76x02_dev *dev, + struct mt76x2_tx_power_info *t, + struct ieee80211_channel *chan, + int chain, int offset) +{ + int channel = chan->hw_value; + enum mt76x2_cal_channel_group group; + int delta_idx; + u16 val; + u8 data[5]; + + group = mt76x2_get_cal_channel_group(channel); + offset += group * MT_TX_POWER_GROUP_SIZE_5G; + + if (channel >= 192) + delta_idx = 4; + else if (channel >= 184) + delta_idx = 3; + else if (channel < 44) + delta_idx = 3; + else if (channel < 52) + delta_idx = 4; + else if (channel < 58) + delta_idx = 3; + else if (channel < 98) + delta_idx = 4; + else if (channel < 106) + delta_idx = 3; + else if (channel < 116) + delta_idx = 4; + else if (channel < 130) + delta_idx = 3; + else if (channel < 149) + delta_idx = 4; + else if (channel < 157) + delta_idx = 3; + else + delta_idx = 4; + + mt76x02_eeprom_copy(dev, offset, data, sizeof(data)); + + t->chain[chain].tssi_slope = data[0]; + t->chain[chain].tssi_offset = data[1]; + t->chain[chain].target_power = data[2]; + t->chain[chain].delta = + mt76x02_sign_extend_optional(data[delta_idx], 7); + + val = mt76x02_eeprom_get(dev, MT_EE_RF_2G_RX_HIGH_GAIN); + t->target_power = val & 0xff; +} + +void mt76x2_get_power_info(struct mt76x02_dev *dev, + struct mt76x2_tx_power_info *t, + struct ieee80211_channel *chan) +{ + u16 bw40, bw80; + + memset(t, 0, sizeof(*t)); + + bw40 = mt76x02_eeprom_get(dev, MT_EE_TX_POWER_DELTA_BW40); + bw80 = mt76x02_eeprom_get(dev, MT_EE_TX_POWER_DELTA_BW80); + + if (chan->band == NL80211_BAND_5GHZ) { + bw40 >>= 8; + mt76x2_get_power_info_5g(dev, t, chan, 0, + MT_EE_TX_POWER_0_START_5G); + mt76x2_get_power_info_5g(dev, t, chan, 1, + MT_EE_TX_POWER_1_START_5G); + } else { + mt76x2_get_power_info_2g(dev, t, chan, 0, + MT_EE_TX_POWER_0_START_2G); + mt76x2_get_power_info_2g(dev, t, chan, 1, + MT_EE_TX_POWER_1_START_2G); + } + + if (mt76x2_tssi_enabled(dev) || + !mt76x02_field_valid(t->target_power)) + t->target_power = t->chain[0].target_power; + + t->delta_bw40 = mt76x02_rate_power_val(bw40); + t->delta_bw80 = mt76x02_rate_power_val(bw80); +} +EXPORT_SYMBOL_GPL(mt76x2_get_power_info); + +int mt76x2_get_temp_comp(struct mt76x02_dev *dev, struct mt76x2_temp_comp *t) +{ + enum nl80211_band band = dev->mphy.chandef.chan->band; + u16 val, slope; + u8 bounds; + + memset(t, 0, sizeof(*t)); + + if (!mt76x2_temp_tx_alc_enabled(dev)) + return -EINVAL; + + if (!mt76x02_ext_pa_enabled(dev, band)) + return -EINVAL; + + val = mt76x02_eeprom_get(dev, MT_EE_TX_POWER_EXT_PA_5G) >> 8; + t->temp_25_ref = val & 0x7f; + if (band == NL80211_BAND_5GHZ) { + slope = mt76x02_eeprom_get(dev, MT_EE_RF_TEMP_COMP_SLOPE_5G); + bounds = mt76x02_eeprom_get(dev, MT_EE_TX_POWER_EXT_PA_5G); + } else { + slope = mt76x02_eeprom_get(dev, MT_EE_RF_TEMP_COMP_SLOPE_2G); + bounds = mt76x02_eeprom_get(dev, + MT_EE_TX_POWER_DELTA_BW80) >> 8; + } + + t->high_slope = slope & 0xff; + t->low_slope = slope >> 8; + t->lower_bound = 0 - (bounds & 0xf); + t->upper_bound = (bounds >> 4) & 0xf; + + return 0; +} +EXPORT_SYMBOL_GPL(mt76x2_get_temp_comp); + +int mt76x2_eeprom_init(struct mt76x02_dev *dev) +{ + int ret; + + ret = mt76x2_eeprom_load(dev); + if (ret) + return ret; + + mt76x02_eeprom_parse_hw_cap(dev); + mt76x2_eeprom_get_macaddr(dev); + ret = mt76_eeprom_override(&dev->mphy); + if (ret) + return ret; + dev->mphy.macaddr[0] &= ~BIT(1); + + return 0; +} +EXPORT_SYMBOL_GPL(mt76x2_eeprom_init); + +MODULE_DESCRIPTION("MediaTek MT76x2 EEPROM helpers"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.h b/drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.h new file mode 100644 index 000000000..1ee8be389 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/eeprom.h @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (C) 2016 Felix Fietkau + */ + +#ifndef __MT76x2_EEPROM_H +#define __MT76x2_EEPROM_H + +#include "../mt76x02_eeprom.h" + +enum mt76x2_cal_channel_group { + MT_CH_5G_JAPAN, + MT_CH_5G_UNII_1, + MT_CH_5G_UNII_2, + MT_CH_5G_UNII_2E_1, + MT_CH_5G_UNII_2E_2, + MT_CH_5G_UNII_3, + __MT_CH_MAX +}; + +struct mt76x2_tx_power_info { + u8 target_power; + + s8 delta_bw40; + s8 delta_bw80; + + struct { + s8 tssi_slope; + s8 tssi_offset; + s8 target_power; + s8 delta; + } chain[MT_MAX_CHAINS]; +}; + +struct mt76x2_temp_comp { + u8 temp_25_ref; + int lower_bound; /* J */ + int upper_bound; /* J */ + unsigned int high_slope; /* J / dB */ + unsigned int low_slope; /* J / dB */ +}; + +void mt76x2_get_rate_power(struct mt76x02_dev *dev, struct mt76x02_rate_power *t, + struct ieee80211_channel *chan); +void mt76x2_get_power_info(struct mt76x02_dev *dev, + struct mt76x2_tx_power_info *t, + struct ieee80211_channel *chan); +int mt76x2_get_temp_comp(struct mt76x02_dev *dev, struct mt76x2_temp_comp *t); +void mt76x2_read_rx_gain(struct mt76x02_dev *dev); + +static inline bool +mt76x2_has_ext_lna(struct mt76x02_dev *dev) +{ + u32 val = mt76x02_eeprom_get(dev, MT_EE_NIC_CONF_1); + + if (dev->mphy.chandef.chan->band == NL80211_BAND_2GHZ) + return val & MT_EE_NIC_CONF_1_LNA_EXT_2G; + else + return val & MT_EE_NIC_CONF_1_LNA_EXT_5G; +} + +static inline bool +mt76x2_temp_tx_alc_enabled(struct mt76x02_dev *dev) +{ + u16 val; + + val = mt76x02_eeprom_get(dev, MT_EE_TX_POWER_EXT_PA_5G); + if (!(val & BIT(15))) + return false; + + return mt76x02_eeprom_get(dev, MT_EE_NIC_CONF_1) & + MT_EE_NIC_CONF_1_TEMP_TX_ALC; +} + +static inline bool +mt76x2_tssi_enabled(struct mt76x02_dev *dev) +{ + return !mt76x2_temp_tx_alc_enabled(dev) && + (mt76x02_eeprom_get(dev, MT_EE_NIC_CONF_1) & + MT_EE_NIC_CONF_1_TX_ALC_EN); +} + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/init.c b/drivers/net/wireless/mediatek/mt76/mt76x2/init.c new file mode 100644 index 000000000..408dc08b6 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/init.c @@ -0,0 +1,204 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (C) 2016 Felix Fietkau + * Copyright (C) 2018 Lorenzo Bianconi + */ + +#include "mt76x2.h" +#include "eeprom.h" +#include "../mt76x02_phy.h" + +int mt76x2_set_sar_specs(struct ieee80211_hw *hw, + const struct cfg80211_sar_specs *sar) +{ + int err = -EINVAL, power = hw->conf.power_level * 2; + struct mt76x02_dev *dev = hw->priv; + struct mt76_phy *mphy = &dev->mphy; + + mutex_lock(&dev->mt76.mutex); + if (!cfg80211_chandef_valid(&mphy->chandef)) + goto out; + + err = mt76_init_sar_power(hw, sar); + if (err) + goto out; + + dev->txpower_conf = mt76_get_sar_power(mphy, mphy->chandef.chan, + power); + /* convert to per-chain power for 2x2 devices */ + dev->txpower_conf -= 6; + + if (test_bit(MT76_STATE_RUNNING, &mphy->state)) + mt76x2_phy_set_txpower(dev); +out: + mutex_unlock(&dev->mt76.mutex); + + return err; +} +EXPORT_SYMBOL_GPL(mt76x2_set_sar_specs); + +static void +mt76x2_set_wlan_state(struct mt76x02_dev *dev, bool enable) +{ + u32 val = mt76_rr(dev, MT_WLAN_FUN_CTRL); + + if (enable) + val |= (MT_WLAN_FUN_CTRL_WLAN_EN | + MT_WLAN_FUN_CTRL_WLAN_CLK_EN); + else + val &= ~(MT_WLAN_FUN_CTRL_WLAN_EN | + MT_WLAN_FUN_CTRL_WLAN_CLK_EN); + + mt76_wr(dev, MT_WLAN_FUN_CTRL, val); + udelay(20); +} + +void mt76x2_reset_wlan(struct mt76x02_dev *dev, bool enable) +{ + u32 val; + + if (!enable) + goto out; + + val = mt76_rr(dev, MT_WLAN_FUN_CTRL); + + val &= ~MT_WLAN_FUN_CTRL_FRC_WL_ANT_SEL; + + if (val & MT_WLAN_FUN_CTRL_WLAN_EN) { + val |= MT_WLAN_FUN_CTRL_WLAN_RESET_RF; + mt76_wr(dev, MT_WLAN_FUN_CTRL, val); + udelay(20); + + val &= ~MT_WLAN_FUN_CTRL_WLAN_RESET_RF; + } + + mt76_wr(dev, MT_WLAN_FUN_CTRL, val); + udelay(20); + +out: + mt76x2_set_wlan_state(dev, enable); +} +EXPORT_SYMBOL_GPL(mt76x2_reset_wlan); + +void mt76_write_mac_initvals(struct mt76x02_dev *dev) +{ +#define DEFAULT_PROT_CFG_CCK \ + (FIELD_PREP(MT_PROT_CFG_RATE, 0x3) | \ + FIELD_PREP(MT_PROT_CFG_NAV, 1) | \ + FIELD_PREP(MT_PROT_CFG_TXOP_ALLOW, 0x3f) | \ + MT_PROT_CFG_RTS_THRESH) + +#define DEFAULT_PROT_CFG_OFDM \ + (FIELD_PREP(MT_PROT_CFG_RATE, 0x2004) | \ + FIELD_PREP(MT_PROT_CFG_NAV, 1) | \ + FIELD_PREP(MT_PROT_CFG_TXOP_ALLOW, 0x3f) | \ + MT_PROT_CFG_RTS_THRESH) + +#define DEFAULT_PROT_CFG_20 \ + (FIELD_PREP(MT_PROT_CFG_RATE, 0x2004) | \ + FIELD_PREP(MT_PROT_CFG_CTRL, 1) | \ + FIELD_PREP(MT_PROT_CFG_NAV, 1) | \ + FIELD_PREP(MT_PROT_CFG_TXOP_ALLOW, 0x17)) + +#define DEFAULT_PROT_CFG_40 \ + (FIELD_PREP(MT_PROT_CFG_RATE, 0x2084) | \ + FIELD_PREP(MT_PROT_CFG_CTRL, 1) | \ + FIELD_PREP(MT_PROT_CFG_NAV, 1) | \ + FIELD_PREP(MT_PROT_CFG_TXOP_ALLOW, 0x3f)) + + static const struct mt76_reg_pair vals[] = { + /* Copied from MediaTek reference source */ + { MT_PBF_SYS_CTRL, 0x00080c00 }, + { MT_PBF_CFG, 0x1efebcff }, + { MT_FCE_PSE_CTRL, 0x00000001 }, + { MT_MAC_SYS_CTRL, 0x00000000 }, + { MT_MAX_LEN_CFG, 0x003e3f00 }, + { MT_AMPDU_MAX_LEN_20M1S, 0xaaa99887 }, + { MT_AMPDU_MAX_LEN_20M2S, 0x000000aa }, + { MT_XIFS_TIME_CFG, 0x33a40d0a }, + { MT_BKOFF_SLOT_CFG, 0x00000209 }, + { MT_TBTT_SYNC_CFG, 0x00422010 }, + { MT_PWR_PIN_CFG, 0x00000000 }, + { 0x1238, 0x001700c8 }, + { MT_TX_SW_CFG0, 0x00101001 }, + { MT_TX_SW_CFG1, 0x00010000 }, + { MT_TX_SW_CFG2, 0x00000000 }, + { MT_TXOP_CTRL_CFG, 0x0400583f }, + { MT_TX_RTS_CFG, 0x00ffff20 }, + { MT_TX_TIMEOUT_CFG, 0x000a2290 }, + { MT_TX_RETRY_CFG, 0x47f01f0f }, + { MT_EXP_ACK_TIME, 0x002c00dc }, + { MT_TX_PROT_CFG6, 0xe3f42004 }, + { MT_TX_PROT_CFG7, 0xe3f42084 }, + { MT_TX_PROT_CFG8, 0xe3f42104 }, + { MT_PIFS_TX_CFG, 0x00060fff }, + { MT_RX_FILTR_CFG, 0x00015f97 }, + { MT_LEGACY_BASIC_RATE, 0x0000017f }, + { MT_HT_BASIC_RATE, 0x00004003 }, + { MT_PN_PAD_MODE, 0x00000003 }, + { MT_TXOP_HLDR_ET, 0x00000002 }, + { 0xa44, 0x00000000 }, + { MT_HEADER_TRANS_CTRL_REG, 0x00000000 }, + { MT_TSO_CTRL, 0x00000000 }, + { MT_AUX_CLK_CFG, 0x00000000 }, + { MT_DACCLK_EN_DLY_CFG, 0x00000000 }, + { MT_TX_ALC_CFG_4, 0x00000000 }, + { MT_TX_ALC_VGA3, 0x00000000 }, + { MT_TX_PWR_CFG_0, 0x3a3a3a3a }, + { MT_TX_PWR_CFG_1, 0x3a3a3a3a }, + { MT_TX_PWR_CFG_2, 0x3a3a3a3a }, + { MT_TX_PWR_CFG_3, 0x3a3a3a3a }, + { MT_TX_PWR_CFG_4, 0x3a3a3a3a }, + { MT_TX_PWR_CFG_7, 0x3a3a3a3a }, + { MT_TX_PWR_CFG_8, 0x0000003a }, + { MT_TX_PWR_CFG_9, 0x0000003a }, + { MT_EFUSE_CTRL, 0x0000d000 }, + { MT_PAUSE_ENABLE_CONTROL1, 0x0000000a }, + { MT_FCE_WLAN_FLOW_CONTROL1, 0x60401c18 }, + { MT_WPDMA_DELAY_INT_CFG, 0x94ff0000 }, + { MT_TX_SW_CFG3, 0x00000004 }, + { MT_HT_FBK_TO_LEGACY, 0x00001818 }, + { MT_VHT_HT_FBK_CFG1, 0xedcba980 }, + { MT_PROT_AUTO_TX_CFG, 0x00830083 }, + { MT_HT_CTRL_CFG, 0x000001ff }, + { MT_TX_LINK_CFG, 0x00001020 }, + }; + struct mt76_reg_pair prot_vals[] = { + { MT_CCK_PROT_CFG, DEFAULT_PROT_CFG_CCK }, + { MT_OFDM_PROT_CFG, DEFAULT_PROT_CFG_OFDM }, + { MT_MM20_PROT_CFG, DEFAULT_PROT_CFG_20 }, + { MT_MM40_PROT_CFG, DEFAULT_PROT_CFG_40 }, + { MT_GF20_PROT_CFG, DEFAULT_PROT_CFG_20 }, + { MT_GF40_PROT_CFG, DEFAULT_PROT_CFG_40 }, + }; + + mt76_wr_rp(dev, 0, vals, ARRAY_SIZE(vals)); + mt76_wr_rp(dev, 0, prot_vals, ARRAY_SIZE(prot_vals)); +} +EXPORT_SYMBOL_GPL(mt76_write_mac_initvals); + +void mt76x2_init_txpower(struct mt76x02_dev *dev, + struct ieee80211_supported_band *sband) +{ + struct ieee80211_channel *chan; + struct mt76x2_tx_power_info txp; + struct mt76x02_rate_power t = {}; + int i; + + for (i = 0; i < sband->n_channels; i++) { + chan = &sband->channels[i]; + + mt76x2_get_power_info(dev, &txp, chan); + mt76x2_get_rate_power(dev, &t, chan); + + chan->orig_mpwr = mt76x02_get_max_rate_power(&t) + + txp.target_power; + chan->orig_mpwr = DIV_ROUND_UP(chan->orig_mpwr, 2); + + /* convert to combined output power on 2x2 devices */ + chan->orig_mpwr += 3; + chan->max_power = min_t(int, chan->max_reg_power, + chan->orig_mpwr); + } +} +EXPORT_SYMBOL_GPL(mt76x2_init_txpower); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/mac.c b/drivers/net/wireless/mediatek/mt76/mt76x2/mac.c new file mode 100644 index 000000000..2fa34ca69 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/mac.c @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (C) 2016 Felix Fietkau + * Copyright (C) 2018 Lorenzo Bianconi + */ + +#include "mt76x2.h" + +void mt76x2_mac_stop(struct mt76x02_dev *dev, bool force) +{ + bool stopped = false; + u32 rts_cfg; + int i; + + mt76_clear(dev, MT_TXOP_CTRL_CFG, MT_TXOP_ED_CCA_EN); + mt76_clear(dev, MT_TXOP_HLDR_ET, MT_TXOP_HLDR_TX40M_BLK_EN); + + mt76_wr(dev, MT_MAC_SYS_CTRL, 0); + + rts_cfg = mt76_rr(dev, MT_TX_RTS_CFG); + mt76_wr(dev, MT_TX_RTS_CFG, rts_cfg & ~MT_TX_RTS_CFG_RETRY_LIMIT); + + /* Wait for MAC to become idle */ + for (i = 0; i < 300; i++) { + if ((mt76_rr(dev, MT_MAC_STATUS) & + (MT_MAC_STATUS_RX | MT_MAC_STATUS_TX)) || + mt76_rr(dev, MT_BBP(IBI, 12))) { + udelay(1); + continue; + } + + stopped = true; + break; + } + + if (force && !stopped) { + mt76_set(dev, MT_BBP(CORE, 4), BIT(1)); + mt76_clear(dev, MT_BBP(CORE, 4), BIT(1)); + + mt76_set(dev, MT_BBP(CORE, 4), BIT(0)); + mt76_clear(dev, MT_BBP(CORE, 4), BIT(0)); + } + + mt76_wr(dev, MT_TX_RTS_CFG, rts_cfg); +} +EXPORT_SYMBOL_GPL(mt76x2_mac_stop); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/mac.h b/drivers/net/wireless/mediatek/mt76/mt76x2/mac.h new file mode 100644 index 000000000..f8ea70074 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/mac.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (C) 2016 Felix Fietkau + */ + +#ifndef __MT76x2_MAC_H +#define __MT76x2_MAC_H + +#include "mt76x2.h" + +struct mt76x02_dev; +struct mt76x2_sta; +struct mt76x02_vif; + +void mt76x2_mac_stop(struct mt76x02_dev *dev, bool force); + +static inline void mt76x2_mac_resume(struct mt76x02_dev *dev) +{ + mt76_wr(dev, MT_MAC_SYS_CTRL, + MT_MAC_SYS_CTRL_ENABLE_TX | + MT_MAC_SYS_CTRL_ENABLE_RX); +} + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/mcu.c b/drivers/net/wireless/mediatek/mt76/mt76x2/mcu.c new file mode 100644 index 000000000..769d92422 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/mcu.c @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (C) 2016 Felix Fietkau + * Copyright (C) 2018 Lorenzo Bianconi + */ + +#include +#include +#include + +#include "mt76x2.h" +#include "mcu.h" +#include "eeprom.h" + +int mt76x2_mcu_set_channel(struct mt76x02_dev *dev, u8 channel, u8 bw, + u8 bw_index, bool scan) +{ + struct { + u8 idx; + u8 scan; + u8 bw; + u8 _pad0; + + __le16 chainmask; + u8 ext_chan; + u8 _pad1; + + } __packed __aligned(4) msg = { + .idx = channel, + .scan = scan, + .bw = bw, + .chainmask = cpu_to_le16(dev->mphy.chainmask), + }; + + /* first set the channel without the extension channel info */ + mt76_mcu_send_msg(&dev->mt76, CMD_SWITCH_CHANNEL_OP, &msg, + sizeof(msg), true); + + usleep_range(5000, 10000); + + msg.ext_chan = 0xe0 + bw_index; + return mt76_mcu_send_msg(&dev->mt76, CMD_SWITCH_CHANNEL_OP, &msg, + sizeof(msg), true); +} +EXPORT_SYMBOL_GPL(mt76x2_mcu_set_channel); + +int mt76x2_mcu_load_cr(struct mt76x02_dev *dev, u8 type, u8 temp_level, + u8 channel) +{ + struct { + u8 cr_mode; + u8 temp; + u8 ch; + u8 _pad0; + + __le32 cfg; + } __packed __aligned(4) msg = { + .cr_mode = type, + .temp = temp_level, + .ch = channel, + }; + u32 val; + + val = BIT(31); + val |= (mt76x02_eeprom_get(dev, MT_EE_NIC_CONF_0) >> 8) & 0x00ff; + val |= (mt76x02_eeprom_get(dev, MT_EE_NIC_CONF_1) << 8) & 0xff00; + msg.cfg = cpu_to_le32(val); + + /* first set the channel without the extension channel info */ + return mt76_mcu_send_msg(&dev->mt76, CMD_LOAD_CR, &msg, sizeof(msg), + true); +} +EXPORT_SYMBOL_GPL(mt76x2_mcu_load_cr); + +int mt76x2_mcu_init_gain(struct mt76x02_dev *dev, u8 channel, u32 gain, + bool force) +{ + struct { + __le32 channel; + __le32 gain_val; + } __packed __aligned(4) msg = { + .channel = cpu_to_le32(channel), + .gain_val = cpu_to_le32(gain), + }; + + if (force) + msg.channel |= cpu_to_le32(BIT(31)); + + return mt76_mcu_send_msg(&dev->mt76, CMD_INIT_GAIN_OP, &msg, + sizeof(msg), true); +} +EXPORT_SYMBOL_GPL(mt76x2_mcu_init_gain); + +int mt76x2_mcu_tssi_comp(struct mt76x02_dev *dev, + struct mt76x2_tssi_comp *tssi_data) +{ + struct { + __le32 id; + struct mt76x2_tssi_comp data; + } __packed __aligned(4) msg = { + .id = cpu_to_le32(MCU_CAL_TSSI_COMP), + .data = *tssi_data, + }; + + return mt76_mcu_send_msg(&dev->mt76, CMD_CALIBRATION_OP, &msg, + sizeof(msg), true); +} +EXPORT_SYMBOL_GPL(mt76x2_mcu_tssi_comp); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/mcu.h b/drivers/net/wireless/mediatek/mt76/mt76x2/mcu.h new file mode 100644 index 000000000..16a4386aa --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/mcu.h @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (C) 2016 Felix Fietkau + */ + +#ifndef __MT76x2_MCU_H +#define __MT76x2_MCU_H + +#include "../mt76x02_mcu.h" + +/* Register definitions */ +#define MT_MCU_CPU_CTL 0x0704 +#define MT_MCU_CLOCK_CTL 0x0708 +#define MT_MCU_PCIE_REMAP_BASE1 0x0740 +#define MT_MCU_PCIE_REMAP_BASE2 0x0744 +#define MT_MCU_PCIE_REMAP_BASE3 0x0748 + +#define MT_MCU_ROM_PATCH_OFFSET 0x80000 +#define MT_MCU_ROM_PATCH_ADDR 0x90000 + +#define MT_MCU_ILM_OFFSET 0x80000 + +#define MT_MCU_DLM_OFFSET 0x100000 +#define MT_MCU_DLM_ADDR 0x90000 +#define MT_MCU_DLM_ADDR_E3 0x90800 + +enum mcu_calibration { + MCU_CAL_R = 1, + MCU_CAL_TEMP_SENSOR, + MCU_CAL_RXDCOC, + MCU_CAL_RC, + MCU_CAL_SX_LOGEN, + MCU_CAL_LC, + MCU_CAL_TX_LOFT, + MCU_CAL_TXIQ, + MCU_CAL_TSSI, + MCU_CAL_TSSI_COMP, + MCU_CAL_DPD, + MCU_CAL_RXIQC_FI, + MCU_CAL_RXIQC_FD, + MCU_CAL_PWRON, + MCU_CAL_TX_SHAPING, +}; + +enum mt76x2_mcu_cr_mode { + MT_RF_CR, + MT_BBP_CR, + MT_RF_BBP_CR, + MT_HL_TEMP_CR_UPDATE, +}; + +struct mt76x2_tssi_comp { + u8 pa_mode; + u8 cal_mode; + u16 pad; + + u8 slope0; + u8 slope1; + u8 offset0; + u8 offset1; +} __packed __aligned(4); + +int mt76x2_mcu_tssi_comp(struct mt76x02_dev *dev, + struct mt76x2_tssi_comp *tssi_data); +int mt76x2_mcu_init_gain(struct mt76x02_dev *dev, u8 channel, u32 gain, + bool force); + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/mt76x2.h b/drivers/net/wireless/mediatek/mt76/mt76x2/mt76x2.h new file mode 100644 index 000000000..984756c81 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/mt76x2.h @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (C) 2016 Felix Fietkau + */ + +#ifndef __MT76x2_H +#define __MT76x2_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MT7662_FIRMWARE "mt7662.bin" +#define MT7662_ROM_PATCH "mt7662_rom_patch.bin" +#define MT7662_EEPROM_SIZE 512 + +#include "../mt76x02.h" +#include "mac.h" + +static inline bool is_mt7612(struct mt76x02_dev *dev) +{ + return mt76_chip(&dev->mt76) == 0x7612; +} + +static inline bool mt76x2_channel_silent(struct mt76x02_dev *dev) +{ + struct ieee80211_channel *chan = dev->mphy.chandef.chan; + + return ((chan->flags & IEEE80211_CHAN_RADAR) && + chan->dfs_state != NL80211_DFS_AVAILABLE); +} + +extern const struct ieee80211_ops mt76x2_ops; + +int mt76x2_register_device(struct mt76x02_dev *dev); +int mt76x2_resume_device(struct mt76x02_dev *dev); + +int mt76x2_set_sar_specs(struct ieee80211_hw *hw, + const struct cfg80211_sar_specs *sar); +void mt76x2_phy_power_on(struct mt76x02_dev *dev); +void mt76x2_stop_hardware(struct mt76x02_dev *dev); +int mt76x2_eeprom_init(struct mt76x02_dev *dev); +int mt76x2_apply_calibration_data(struct mt76x02_dev *dev, int channel); +int mt76x2e_set_channel(struct mt76_phy *phy); +int mt76x2u_set_channel(struct mt76_phy *phy); + +void mt76x2_phy_set_antenna(struct mt76x02_dev *dev); +int mt76x2_phy_start(struct mt76x02_dev *dev); +int mt76x2_phy_set_channel(struct mt76x02_dev *dev, + struct cfg80211_chan_def *chandef); +void mt76x2_phy_calibrate(struct work_struct *work); +void mt76x2_phy_set_txpower(struct mt76x02_dev *dev); + +int mt76x2_mcu_init(struct mt76x02_dev *dev); +int mt76x2_mcu_set_channel(struct mt76x02_dev *dev, u8 channel, u8 bw, + u8 bw_index, bool scan); +int mt76x2_mcu_load_cr(struct mt76x02_dev *dev, u8 type, u8 temp_level, + u8 channel); + +void mt76x2_cleanup(struct mt76x02_dev *dev); + +int mt76x2_mac_reset(struct mt76x02_dev *dev, bool hard); +void mt76x2_reset_wlan(struct mt76x02_dev *dev, bool enable); +void mt76x2_init_txpower(struct mt76x02_dev *dev, + struct ieee80211_supported_band *sband); +void mt76_write_mac_initvals(struct mt76x02_dev *dev); + +void mt76x2_phy_tssi_compensate(struct mt76x02_dev *dev); +void mt76x2_phy_set_txpower_regs(struct mt76x02_dev *dev, + enum nl80211_band band); +void mt76x2_configure_tx_delay(struct mt76x02_dev *dev, + enum nl80211_band band, u8 bw); +void mt76x2_apply_gain_adj(struct mt76x02_dev *dev); +void mt76x2_phy_update_channel_gain(struct mt76x02_dev *dev); + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/mt76x2u.h b/drivers/net/wireless/mediatek/mt76/mt76x2/mt76x2u.h new file mode 100644 index 000000000..27e478ab5 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/mt76x2u.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (C) 2018 Lorenzo Bianconi + */ + +#ifndef __MT76x2U_H +#define __MT76x2U_H + +#include + +#include "mt76x2.h" +#include "mcu.h" + +#define MT7612U_EEPROM_SIZE 512 + +#define MT_USB_AGGR_SIZE_LIMIT 21 /* 1024B unit */ +#define MT_USB_AGGR_TIMEOUT 0x80 /* 33ns unit */ + +extern const struct ieee80211_ops mt76x2u_ops; + +int mt76x2u_register_device(struct mt76x02_dev *dev); +int mt76x2u_init_hardware(struct mt76x02_dev *dev); +void mt76x2u_cleanup(struct mt76x02_dev *dev); +void mt76x2u_stop_hw(struct mt76x02_dev *dev); + +int mt76x2u_mac_reset(struct mt76x02_dev *dev); +int mt76x2u_mac_stop(struct mt76x02_dev *dev); + +int mt76x2u_phy_set_channel(struct mt76x02_dev *dev, + struct cfg80211_chan_def *chandef); +void mt76x2u_phy_calibrate(struct work_struct *work); + +void mt76x2u_mcu_complete_urb(struct urb *urb); +int mt76x2u_mcu_init(struct mt76x02_dev *dev); +int mt76x2u_mcu_fw_init(struct mt76x02_dev *dev); + +int mt76x2u_alloc_queues(struct mt76x02_dev *dev); +void mt76x2u_queues_deinit(struct mt76x02_dev *dev); +void mt76x2u_stop_queues(struct mt76x02_dev *dev); +int mt76x2u_skb_dma_info(struct sk_buff *skb, enum dma_msg_port port, + u32 flags); + +#endif /* __MT76x2U_H */ diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/pci.c b/drivers/net/wireless/mediatek/mt76/mt76x2/pci.c new file mode 100644 index 000000000..491a32921 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/pci.c @@ -0,0 +1,187 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (C) 2016 Felix Fietkau + */ + +#include +#include +#include + +#include "mt76x2.h" + +static const struct pci_device_id mt76x2e_device_table[] = { + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7662) }, + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7612) }, + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7602) }, + { }, +}; + +static int +mt76x2e_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + static const struct mt76_driver_ops drv_ops = { + .txwi_size = sizeof(struct mt76x02_txwi), + .drv_flags = MT_DRV_TX_ALIGNED4_SKBS | + MT_DRV_SW_RX_AIRTIME | + MT_DRV_IGNORE_TXS_FAILED, + .survey_flags = SURVEY_INFO_TIME_TX, + .update_survey = mt76x02_update_channel, + .set_channel = mt76x2e_set_channel, + .tx_prepare_skb = mt76x02_tx_prepare_skb, + .tx_complete_skb = mt76x02_tx_complete_skb, + .rx_skb = mt76x02_queue_rx_skb, + .rx_poll_complete = mt76x02_rx_poll_complete, + .sta_ps = mt76x02_sta_ps, + .sta_add = mt76x02_sta_add, + .sta_remove = mt76x02_sta_remove, + }; + struct mt76x02_dev *dev; + struct mt76_dev *mdev; + int ret; + + ret = pcim_enable_device(pdev); + if (ret) + return ret; + + ret = pcim_iomap_regions(pdev, BIT(0), pci_name(pdev)); + if (ret) + return ret; + + pci_set_master(pdev); + + ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) + return ret; + + mdev = mt76_alloc_device(&pdev->dev, sizeof(*dev), &mt76x2_ops, + &drv_ops); + if (!mdev) + return -ENOMEM; + + dev = container_of(mdev, struct mt76x02_dev, mt76); + mt76_mmio_init(mdev, pcim_iomap_table(pdev)[0]); + mt76x2_reset_wlan(dev, false); + + mdev->rev = mt76_rr(dev, MT_ASIC_VERSION); + dev_info(mdev->dev, "ASIC revision: %08x\n", mdev->rev); + + mt76_wr(dev, MT_INT_MASK_CSR, 0); + + ret = devm_request_irq(mdev->dev, pdev->irq, mt76x02_irq_handler, + IRQF_SHARED, KBUILD_MODNAME, dev); + if (ret) + goto error; + + ret = mt76x2_register_device(dev); + if (ret) + goto error; + + /* Fix up ASPM configuration */ + + /* RG_SSUSB_G1_CDR_BIR_LTR = 0x9 */ + mt76_rmw_field(dev, 0x15a10, 0x1f << 16, 0x9); + + /* RG_SSUSB_G1_CDR_BIC_LTR = 0xf */ + mt76_rmw_field(dev, 0x15a0c, 0xfU << 28, 0xf); + + /* RG_SSUSB_CDR_BR_PE1D = 0x3 */ + mt76_rmw_field(dev, 0x15c58, 0x3 << 6, 0x3); + + mt76_pci_disable_aspm(pdev); + + return 0; + +error: + mt76_free_device(&dev->mt76); + + return ret; +} + +static void +mt76x2e_remove(struct pci_dev *pdev) +{ + struct mt76_dev *mdev = pci_get_drvdata(pdev); + struct mt76x02_dev *dev = container_of(mdev, struct mt76x02_dev, mt76); + + mt76_unregister_device(mdev); + mt76x2_cleanup(dev); + mt76_free_device(mdev); +} + +static int __maybe_unused +mt76x2e_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct mt76_dev *mdev = pci_get_drvdata(pdev); + int i, err; + + napi_disable(&mdev->tx_napi); + tasklet_kill(&mdev->pre_tbtt_tasklet); + mt76_worker_disable(&mdev->tx_worker); + + mt76_for_each_q_rx(mdev, i) + napi_disable(&mdev->napi[i]); + + pci_enable_wake(pdev, pci_choose_state(pdev, state), true); + pci_save_state(pdev); + err = pci_set_power_state(pdev, pci_choose_state(pdev, state)); + if (err) + goto restore; + + return 0; + +restore: + mt76_for_each_q_rx(mdev, i) + napi_enable(&mdev->napi[i]); + napi_enable(&mdev->tx_napi); + + return err; +} + +static int __maybe_unused +mt76x2e_resume(struct pci_dev *pdev) +{ + struct mt76_dev *mdev = pci_get_drvdata(pdev); + struct mt76x02_dev *dev = container_of(mdev, struct mt76x02_dev, mt76); + int i, err; + + err = pci_set_power_state(pdev, PCI_D0); + if (err) + return err; + + pci_restore_state(pdev); + + mt76_worker_enable(&mdev->tx_worker); + + mt76_for_each_q_rx(mdev, i) { + napi_enable(&mdev->napi[i]); + } + napi_enable(&mdev->tx_napi); + + local_bh_disable(); + mt76_for_each_q_rx(mdev, i) { + napi_schedule(&mdev->napi[i]); + } + napi_schedule(&mdev->tx_napi); + local_bh_enable(); + + return mt76x2_resume_device(dev); +} + +MODULE_DEVICE_TABLE(pci, mt76x2e_device_table); +MODULE_FIRMWARE(MT7662_FIRMWARE); +MODULE_FIRMWARE(MT7662_ROM_PATCH); +MODULE_DESCRIPTION("MediaTek MT76x2E (PCIe) wireless driver"); +MODULE_LICENSE("Dual BSD/GPL"); + +static struct pci_driver mt76pci_driver = { + .name = KBUILD_MODNAME, + .id_table = mt76x2e_device_table, + .probe = mt76x2e_probe, + .remove = mt76x2e_remove, +#ifdef CONFIG_PM + .suspend = mt76x2e_suspend, + .resume = mt76x2e_resume, +#endif /* CONFIG_PM */ +}; + +module_pci_driver(mt76pci_driver); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/pci_init.c b/drivers/net/wireless/mediatek/mt76/mt76x2/pci_init.c new file mode 100644 index 000000000..bec84f932 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/pci_init.c @@ -0,0 +1,320 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (C) 2016 Felix Fietkau + */ + +#include +#include "mt76x2.h" +#include "eeprom.h" +#include "mcu.h" +#include "../mt76x02_mac.h" + +static void +mt76x2_mac_pbf_init(struct mt76x02_dev *dev) +{ + u32 val; + + val = MT_PBF_SYS_CTRL_MCU_RESET | + MT_PBF_SYS_CTRL_DMA_RESET | + MT_PBF_SYS_CTRL_MAC_RESET | + MT_PBF_SYS_CTRL_PBF_RESET | + MT_PBF_SYS_CTRL_ASY_RESET; + + mt76_set(dev, MT_PBF_SYS_CTRL, val); + mt76_clear(dev, MT_PBF_SYS_CTRL, val); + + mt76_wr(dev, MT_PBF_TX_MAX_PCNT, 0xefef3f1f); + mt76_wr(dev, MT_PBF_RX_MAX_PCNT, 0xfebf); +} + +static void +mt76x2_fixup_xtal(struct mt76x02_dev *dev) +{ + u16 eep_val; + s8 offset = 0; + + eep_val = mt76x02_eeprom_get(dev, MT_EE_XTAL_TRIM_2); + + offset = eep_val & 0x7f; + if ((eep_val & 0xff) == 0xff) + offset = 0; + else if (eep_val & 0x80) + offset = 0 - offset; + + eep_val >>= 8; + if (eep_val == 0x00 || eep_val == 0xff) { + eep_val = mt76x02_eeprom_get(dev, MT_EE_XTAL_TRIM_1); + eep_val &= 0xff; + + if (eep_val == 0x00 || eep_val == 0xff) + eep_val = 0x14; + } + + eep_val &= 0x7f; + mt76_rmw_field(dev, MT_XO_CTRL5, MT_XO_CTRL5_C2_VAL, eep_val + offset); + mt76_set(dev, MT_XO_CTRL6, MT_XO_CTRL6_C2_CTRL); + + eep_val = mt76x02_eeprom_get(dev, MT_EE_NIC_CONF_2); + switch (FIELD_GET(MT_EE_NIC_CONF_2_XTAL_OPTION, eep_val)) { + case 0: + mt76_wr(dev, MT_XO_CTRL7, 0x5c1fee80); + break; + case 1: + mt76_wr(dev, MT_XO_CTRL7, 0x5c1feed0); + break; + default: + break; + } +} + +int mt76x2_mac_reset(struct mt76x02_dev *dev, bool hard) +{ + const u8 *macaddr = dev->mphy.macaddr; + u32 val; + int i, k; + + if (!mt76x02_wait_for_mac(&dev->mt76)) + return -ETIMEDOUT; + + val = mt76_rr(dev, MT_WPDMA_GLO_CFG); + + val &= ~(MT_WPDMA_GLO_CFG_TX_DMA_EN | + MT_WPDMA_GLO_CFG_TX_DMA_BUSY | + MT_WPDMA_GLO_CFG_RX_DMA_EN | + MT_WPDMA_GLO_CFG_RX_DMA_BUSY | + MT_WPDMA_GLO_CFG_DMA_BURST_SIZE); + val |= FIELD_PREP(MT_WPDMA_GLO_CFG_DMA_BURST_SIZE, 3); + + mt76_wr(dev, MT_WPDMA_GLO_CFG, val); + + mt76x2_mac_pbf_init(dev); + mt76_write_mac_initvals(dev); + mt76x2_fixup_xtal(dev); + + mt76_clear(dev, MT_MAC_SYS_CTRL, + MT_MAC_SYS_CTRL_RESET_CSR | + MT_MAC_SYS_CTRL_RESET_BBP); + + if (is_mt7612(dev)) + mt76_clear(dev, MT_COEXCFG0, MT_COEXCFG0_COEX_EN); + + mt76_set(dev, MT_EXT_CCA_CFG, 0x0000f000); + mt76_clear(dev, MT_TX_ALC_CFG_4, BIT(31)); + + mt76_wr(dev, MT_RF_BYPASS_0, 0x06000000); + mt76_wr(dev, MT_RF_SETTING_0, 0x08800000); + usleep_range(5000, 10000); + mt76_wr(dev, MT_RF_BYPASS_0, 0x00000000); + + mt76_wr(dev, MT_MCU_CLOCK_CTL, 0x1401); + mt76_clear(dev, MT_FCE_L2_STUFF, MT_FCE_L2_STUFF_WR_MPDU_LEN_EN); + + mt76x02_mac_setaddr(dev, macaddr); + mt76x02e_init_beacon_config(dev); + if (!hard) + return 0; + + for (i = 0; i < 256 / 32; i++) + mt76_wr(dev, MT_WCID_DROP_BASE + i * 4, 0); + + for (i = 0; i < 256; i++) { + mt76x02_mac_wcid_setup(dev, i, 0, NULL); + mt76_wr(dev, MT_WCID_TX_RATE(i), 0); + mt76_wr(dev, MT_WCID_TX_RATE(i) + 4, 0); + } + + for (i = 0; i < MT_MAX_VIFS; i++) + mt76x02_mac_wcid_setup(dev, MT_VIF_WCID(i), i, NULL); + + for (i = 0; i < 16; i++) + for (k = 0; k < 4; k++) + mt76x02_mac_shared_key_setup(dev, i, k, NULL); + + for (i = 0; i < 16; i++) + mt76_rr(dev, MT_TX_STAT_FIFO); + + mt76x02_set_tx_ackto(dev); + + return 0; +} + +static void +mt76x2_power_on_rf_patch(struct mt76x02_dev *dev) +{ + mt76_set(dev, 0x10130, BIT(0) | BIT(16)); + udelay(1); + + mt76_clear(dev, 0x1001c, 0xff); + mt76_set(dev, 0x1001c, 0x30); + + mt76_wr(dev, 0x10014, 0x484f); + udelay(1); + + mt76_set(dev, 0x10130, BIT(17)); + udelay(125); + + mt76_clear(dev, 0x10130, BIT(16)); + udelay(50); + + mt76_set(dev, 0x1014c, BIT(19) | BIT(20)); +} + +static void +mt76x2_power_on_rf(struct mt76x02_dev *dev, int unit) +{ + int shift = unit ? 8 : 0; + + /* Enable RF BG */ + mt76_set(dev, 0x10130, BIT(0) << shift); + udelay(10); + + /* Enable RFDIG LDO/AFE/ABB/ADDA */ + mt76_set(dev, 0x10130, (BIT(1) | BIT(3) | BIT(4) | BIT(5)) << shift); + udelay(10); + + /* Switch RFDIG power to internal LDO */ + mt76_clear(dev, 0x10130, BIT(2) << shift); + udelay(10); + + mt76x2_power_on_rf_patch(dev); + + mt76_set(dev, 0x530, 0xf); +} + +static void +mt76x2_power_on(struct mt76x02_dev *dev) +{ + u32 val; + + /* Turn on WL MTCMOS */ + mt76_set(dev, MT_WLAN_MTC_CTRL, MT_WLAN_MTC_CTRL_MTCMOS_PWR_UP); + + val = MT_WLAN_MTC_CTRL_STATE_UP | + MT_WLAN_MTC_CTRL_PWR_ACK | + MT_WLAN_MTC_CTRL_PWR_ACK_S; + + mt76_poll(dev, MT_WLAN_MTC_CTRL, val, val, 1000); + + mt76_clear(dev, MT_WLAN_MTC_CTRL, 0x7f << 16); + udelay(10); + + mt76_clear(dev, MT_WLAN_MTC_CTRL, 0xf << 24); + udelay(10); + + mt76_set(dev, MT_WLAN_MTC_CTRL, 0xf << 24); + mt76_clear(dev, MT_WLAN_MTC_CTRL, 0xfff); + + /* Turn on AD/DA power down */ + mt76_clear(dev, 0x11204, BIT(3)); + + /* WLAN function enable */ + mt76_set(dev, 0x10080, BIT(0)); + + /* Release BBP software reset */ + mt76_clear(dev, 0x10064, BIT(18)); + + mt76x2_power_on_rf(dev, 0); + mt76x2_power_on_rf(dev, 1); +} + +int mt76x2_resume_device(struct mt76x02_dev *dev) +{ + int err; + + mt76x02_dma_disable(dev); + mt76x2_reset_wlan(dev, true); + mt76x2_power_on(dev); + + err = mt76x2_mac_reset(dev, true); + if (err) + return err; + + mt76x02_mac_start(dev); + + return mt76x2_mcu_init(dev); +} + +static int mt76x2_init_hardware(struct mt76x02_dev *dev) +{ + int ret; + + mt76x02_dma_disable(dev); + mt76x2_reset_wlan(dev, true); + mt76x2_power_on(dev); + + ret = mt76x2_eeprom_init(dev); + if (ret) + return ret; + + ret = mt76x2_mac_reset(dev, true); + if (ret) + return ret; + + dev->mt76.rxfilter = mt76_rr(dev, MT_RX_FILTR_CFG); + + ret = mt76x02_dma_init(dev); + if (ret) + return ret; + + set_bit(MT76_STATE_INITIALIZED, &dev->mphy.state); + mt76x02_mac_start(dev); + + ret = mt76x2_mcu_init(dev); + if (ret) + return ret; + + mt76x2_mac_stop(dev, false); + + return 0; +} + +void mt76x2_stop_hardware(struct mt76x02_dev *dev) +{ + cancel_delayed_work_sync(&dev->cal_work); + cancel_delayed_work_sync(&dev->mphy.mac_work); + cancel_delayed_work_sync(&dev->wdt_work); + clear_bit(MT76_RESTART, &dev->mphy.state); + mt76x02_mcu_set_radio_state(dev, false); + mt76x2_mac_stop(dev, false); +} + +void mt76x2_cleanup(struct mt76x02_dev *dev) +{ + tasklet_disable(&dev->dfs_pd.dfs_tasklet); + tasklet_disable(&dev->mt76.pre_tbtt_tasklet); + mt76x2_stop_hardware(dev); + mt76_dma_cleanup(&dev->mt76); + mt76x02_mcu_cleanup(dev); +} + +int mt76x2_register_device(struct mt76x02_dev *dev) +{ + int ret; + + INIT_DELAYED_WORK(&dev->cal_work, mt76x2_phy_calibrate); + ret = mt76x02_init_device(dev); + if (ret) + return ret; + + ret = mt76x2_init_hardware(dev); + if (ret) + return ret; + + mt76x02_config_mac_addr_list(dev); + + ret = mt76_register_device(&dev->mt76, true, mt76x02_rates, + ARRAY_SIZE(mt76x02_rates)); + if (ret) + goto fail; + + mt76x02_init_debugfs(dev); + mt76x2_init_txpower(dev, &dev->mphy.sband_2g.sband); + mt76x2_init_txpower(dev, &dev->mphy.sband_5g.sband); + + return 0; + +fail: + mt76x2_stop_hardware(dev); + return ret; +} + diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/pci_main.c b/drivers/net/wireless/mediatek/mt76/mt76x2/pci_main.c new file mode 100644 index 000000000..550644676 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/pci_main.c @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (C) 2016 Felix Fietkau + */ + +#include "mt76x2.h" +#include "../mt76x02_mac.h" + +static int +mt76x2_start(struct ieee80211_hw *hw) +{ + struct mt76x02_dev *dev = hw->priv; + + mt76x02_mac_start(dev); + mt76x2_phy_start(dev); + + ieee80211_queue_delayed_work(mt76_hw(dev), &dev->mphy.mac_work, + MT_MAC_WORK_INTERVAL); + ieee80211_queue_delayed_work(mt76_hw(dev), &dev->wdt_work, + MT_WATCHDOG_TIME); + + set_bit(MT76_STATE_RUNNING, &dev->mphy.state); + return 0; +} + +static void +mt76x2_stop(struct ieee80211_hw *hw, bool suspend) +{ + struct mt76x02_dev *dev = hw->priv; + + clear_bit(MT76_STATE_RUNNING, &dev->mphy.state); + mt76x2_stop_hardware(dev); +} + +int mt76x2e_set_channel(struct mt76_phy *phy) +{ + struct mt76x02_dev *dev = container_of(phy->dev, struct mt76x02_dev, mt76); + + tasklet_disable(&dev->mt76.pre_tbtt_tasklet); + tasklet_disable(&dev->dfs_pd.dfs_tasklet); + + mt76x2_mac_stop(dev, true); + mt76x2_phy_set_channel(dev, &phy->chandef); + + mt76x02_mac_cc_reset(dev); + mt76x02_dfs_init_params(dev); + + mt76x2_mac_resume(dev); + + tasklet_enable(&dev->dfs_pd.dfs_tasklet); + tasklet_enable(&dev->mt76.pre_tbtt_tasklet); + + return 0; +} + +static int +mt76x2_config(struct ieee80211_hw *hw, int radio_idx, u32 changed) +{ + struct mt76x02_dev *dev = hw->priv; + + mutex_lock(&dev->mt76.mutex); + + if (changed & IEEE80211_CONF_CHANGE_MONITOR) { + if (!(hw->conf.flags & IEEE80211_CONF_MONITOR)) + dev->mt76.rxfilter |= MT_RX_FILTR_CFG_PROMISC; + else + dev->mt76.rxfilter &= ~MT_RX_FILTR_CFG_PROMISC; + + mt76_wr(dev, MT_RX_FILTR_CFG, dev->mt76.rxfilter); + } + + if (changed & IEEE80211_CONF_CHANGE_POWER) { + struct mt76_phy *mphy = &dev->mphy; + + dev->txpower_conf = hw->conf.power_level * 2; + dev->txpower_conf = mt76_get_sar_power(mphy, + mphy->chandef.chan, + dev->txpower_conf); + /* convert to per-chain power for 2x2 devices */ + dev->txpower_conf -= 6; + + if (test_bit(MT76_STATE_RUNNING, &dev->mphy.state)) { + mt76x2_phy_set_txpower(dev); + mt76x02_tx_set_txpwr_auto(dev, dev->txpower_conf); + } + } + + mutex_unlock(&dev->mt76.mutex); + + if (changed & IEEE80211_CONF_CHANGE_CHANNEL) + mt76_update_channel(&dev->mphy); + + return 0; +} + +static void +mt76x2_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 queues, bool drop) +{ +} + +static int mt76x2_set_antenna(struct ieee80211_hw *hw, int radio_idx, + u32 tx_ant, u32 rx_ant) +{ + struct mt76x02_dev *dev = hw->priv; + + if (!tx_ant || tx_ant > 3 || tx_ant != rx_ant) + return -EINVAL; + + mutex_lock(&dev->mt76.mutex); + + dev->mphy.chainmask = (tx_ant == 3) ? 0x202 : 0x101; + dev->mphy.antenna_mask = tx_ant; + + mt76_set_stream_caps(&dev->mphy, true); + mt76x2_phy_set_antenna(dev); + + mutex_unlock(&dev->mt76.mutex); + + return 0; +} + +const struct ieee80211_ops mt76x2_ops = { + .add_chanctx = ieee80211_emulate_add_chanctx, + .remove_chanctx = ieee80211_emulate_remove_chanctx, + .change_chanctx = ieee80211_emulate_change_chanctx, + .switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx, + .tx = mt76x02_tx, + .start = mt76x2_start, + .stop = mt76x2_stop, + .add_interface = mt76x02_add_interface, + .remove_interface = mt76x02_remove_interface, + .config = mt76x2_config, + .configure_filter = mt76x02_configure_filter, + .bss_info_changed = mt76x02_bss_info_changed, + .sta_state = mt76_sta_state, + .sta_pre_rcu_remove = mt76_sta_pre_rcu_remove, + .set_key = mt76x02_set_key, + .conf_tx = mt76x02_conf_tx, + .sw_scan_start = mt76_sw_scan, + .sw_scan_complete = mt76x02_sw_scan_complete, + .flush = mt76x2_flush, + .ampdu_action = mt76x02_ampdu_action, + .get_txpower = mt76_get_txpower, + .wake_tx_queue = mt76_wake_tx_queue, + .sta_rate_tbl_update = mt76x02_sta_rate_tbl_update, + .release_buffered_frames = mt76_release_buffered_frames, + .set_coverage_class = mt76x02_set_coverage_class, + .get_survey = mt76_get_survey, + .set_tim = mt76_set_tim, + .set_antenna = mt76x2_set_antenna, + .get_antenna = mt76_get_antenna, + .set_rts_threshold = mt76x02_set_rts_threshold, + .reconfig_complete = mt76x02_reconfig_complete, + .set_sar_specs = mt76x2_set_sar_specs, +}; + diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/pci_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76x2/pci_mcu.c new file mode 100644 index 000000000..daba16380 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/pci_mcu.c @@ -0,0 +1,198 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (C) 2016 Felix Fietkau + */ + +#include +#include +#include + +#include "mt76x2.h" +#include "mcu.h" +#include "eeprom.h" + +static int +mt76pci_load_rom_patch(struct mt76x02_dev *dev) +{ + const struct firmware *fw = NULL; + struct mt76x02_patch_header *hdr; + bool rom_protect = !is_mt7612(dev); + int len, ret = 0; + __le32 *cur; + u32 patch_mask, patch_reg; + + if (rom_protect && !mt76_poll(dev, MT_MCU_SEMAPHORE_03, 1, 1, 600)) { + dev_err(dev->mt76.dev, + "Could not get hardware semaphore for ROM PATCH\n"); + return -ETIMEDOUT; + } + + if (mt76xx_rev(dev) >= MT76XX_REV_E3) { + patch_mask = BIT(0); + patch_reg = MT_MCU_CLOCK_CTL; + } else { + patch_mask = BIT(1); + patch_reg = MT_MCU_COM_REG0; + } + + if (rom_protect && (mt76_rr(dev, patch_reg) & patch_mask)) { + dev_info(dev->mt76.dev, "ROM patch already applied\n"); + goto out; + } + + ret = request_firmware(&fw, MT7662_ROM_PATCH, dev->mt76.dev); + if (ret) + goto out; + + if (!fw || !fw->data || fw->size <= sizeof(*hdr)) { + ret = -EIO; + dev_err(dev->mt76.dev, "Failed to load firmware\n"); + goto out; + } + + hdr = (struct mt76x02_patch_header *)fw->data; + dev_info(dev->mt76.dev, "ROM patch build: %.15s\n", hdr->build_time); + + mt76_wr(dev, MT_MCU_PCIE_REMAP_BASE4, MT_MCU_ROM_PATCH_OFFSET); + + cur = (__le32 *)(fw->data + sizeof(*hdr)); + len = fw->size - sizeof(*hdr); + mt76_wr_copy(dev, MT_MCU_ROM_PATCH_ADDR, cur, len); + + mt76_wr(dev, MT_MCU_PCIE_REMAP_BASE4, 0); + + /* Trigger ROM */ + mt76_wr(dev, MT_MCU_INT_LEVEL, 4); + + if (!mt76_poll_msec(dev, patch_reg, patch_mask, patch_mask, 2000)) { + dev_err(dev->mt76.dev, "Failed to load ROM patch\n"); + ret = -ETIMEDOUT; + } + +out: + /* release semaphore */ + if (rom_protect) + mt76_wr(dev, MT_MCU_SEMAPHORE_03, 1); + release_firmware(fw); + return ret; +} + +static int +mt76pci_load_firmware(struct mt76x02_dev *dev) +{ + const struct firmware *fw; + const struct mt76x02_fw_header *hdr; + int len, ret; + __le32 *cur; + u32 offset, val; + + ret = request_firmware(&fw, MT7662_FIRMWARE, dev->mt76.dev); + if (ret) + return ret; + + if (!fw || !fw->data || fw->size < sizeof(*hdr)) + goto error; + + hdr = (const struct mt76x02_fw_header *)fw->data; + + len = sizeof(*hdr); + len += le32_to_cpu(hdr->ilm_len); + len += le32_to_cpu(hdr->dlm_len); + + if (fw->size != len) + goto error; + + val = le16_to_cpu(hdr->fw_ver); + dev_info(dev->mt76.dev, "Firmware Version: %d.%d.%02d\n", + (val >> 12) & 0xf, (val >> 8) & 0xf, val & 0xf); + + val = le16_to_cpu(hdr->build_ver); + dev_info(dev->mt76.dev, "Build: %x\n", val); + dev_info(dev->mt76.dev, "Build Time: %.16s\n", hdr->build_time); + + cur = (__le32 *)(fw->data + sizeof(*hdr)); + len = le32_to_cpu(hdr->ilm_len); + + mt76_wr(dev, MT_MCU_PCIE_REMAP_BASE4, MT_MCU_ILM_OFFSET); + mt76_wr_copy(dev, MT_MCU_ILM_ADDR, cur, len); + + cur += len / sizeof(*cur); + len = le32_to_cpu(hdr->dlm_len); + + if (mt76xx_rev(dev) >= MT76XX_REV_E3) + offset = MT_MCU_DLM_ADDR_E3; + else + offset = MT_MCU_DLM_ADDR; + + mt76_wr(dev, MT_MCU_PCIE_REMAP_BASE4, MT_MCU_DLM_OFFSET); + mt76_wr_copy(dev, offset, cur, len); + + mt76_wr(dev, MT_MCU_PCIE_REMAP_BASE4, 0); + + val = mt76x02_eeprom_get(dev, MT_EE_NIC_CONF_2); + if (FIELD_GET(MT_EE_NIC_CONF_2_XTAL_OPTION, val) == 1) + mt76_set(dev, MT_MCU_COM_REG0, BIT(30)); + + /* trigger firmware */ + mt76_wr(dev, MT_MCU_INT_LEVEL, 2); + if (!mt76_poll_msec(dev, MT_MCU_COM_REG0, 1, 1, 200)) { + dev_err(dev->mt76.dev, "Firmware failed to start\n"); + release_firmware(fw); + return -ETIMEDOUT; + } + + mt76x02_set_ethtool_fwver(dev, hdr); + dev_info(dev->mt76.dev, "Firmware running!\n"); + + release_firmware(fw); + + return ret; + +error: + dev_err(dev->mt76.dev, "Invalid firmware\n"); + release_firmware(fw); + return -ENOENT; +} + +static int +mt76pci_mcu_restart(struct mt76_dev *mdev) +{ + struct mt76x02_dev *dev; + int ret; + + dev = container_of(mdev, struct mt76x02_dev, mt76); + + mt76x02_mcu_cleanup(dev); + mt76x2_mac_reset(dev, true); + + ret = mt76pci_load_firmware(dev); + if (ret) + return ret; + + mt76_wr(dev, MT_WPDMA_RST_IDX, ~0); + + return 0; +} + +int mt76x2_mcu_init(struct mt76x02_dev *dev) +{ + static const struct mt76_mcu_ops mt76x2_mcu_ops = { + .mcu_restart = mt76pci_mcu_restart, + .mcu_send_msg = mt76x02_mcu_msg_send, + .mcu_parse_response = mt76x02_mcu_parse_response, + }; + int ret; + + dev->mt76.mcu_ops = &mt76x2_mcu_ops; + + ret = mt76pci_load_rom_patch(dev); + if (ret) + return ret; + + ret = mt76pci_load_firmware(dev); + if (ret) + return ret; + + mt76x02_mcu_function_select(dev, Q_SELECT, 1); + return 0; +} diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/pci_phy.c b/drivers/net/wireless/mediatek/mt76/mt76x2/pci_phy.c new file mode 100644 index 000000000..dcf4328c1 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/pci_phy.c @@ -0,0 +1,311 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (C) 2016 Felix Fietkau + */ + +#include +#include "mt76x2.h" +#include "mcu.h" +#include "eeprom.h" +#include "../mt76x02_phy.h" + +static bool +mt76x2_phy_tssi_init_cal(struct mt76x02_dev *dev) +{ + struct ieee80211_channel *chan = dev->mphy.chandef.chan; + u32 flag = 0; + + if (!mt76x2_tssi_enabled(dev)) + return false; + + if (mt76x2_channel_silent(dev)) + return false; + + if (chan->band == NL80211_BAND_5GHZ) + flag |= BIT(0); + + if (mt76x02_ext_pa_enabled(dev, chan->band)) + flag |= BIT(8); + + mt76x02_mcu_calibrate(dev, MCU_CAL_TSSI, flag); + dev->cal.tssi_cal_done = true; + return true; +} + +static void +mt76x2_phy_channel_calibrate(struct mt76x02_dev *dev, bool mac_stopped) +{ + struct ieee80211_channel *chan = dev->mphy.chandef.chan; + bool is_5ghz = chan->band == NL80211_BAND_5GHZ; + + if (dev->cal.channel_cal_done) + return; + + if (mt76x2_channel_silent(dev)) + return; + + if (!dev->cal.tssi_cal_done) + mt76x2_phy_tssi_init_cal(dev); + + if (!mac_stopped) + mt76x2_mac_stop(dev, false); + + if (is_5ghz) + mt76x02_mcu_calibrate(dev, MCU_CAL_LC, 0); + + mt76x02_mcu_calibrate(dev, MCU_CAL_TX_LOFT, is_5ghz); + mt76x02_mcu_calibrate(dev, MCU_CAL_TXIQ, is_5ghz); + mt76x02_mcu_calibrate(dev, MCU_CAL_RXIQC_FI, is_5ghz); + mt76x02_mcu_calibrate(dev, MCU_CAL_TEMP_SENSOR, 0); + mt76x02_mcu_calibrate(dev, MCU_CAL_TX_SHAPING, 0); + + if (!mac_stopped) + mt76x2_mac_resume(dev); + + mt76x2_apply_gain_adj(dev); + mt76x02_edcca_init(dev); + + dev->cal.channel_cal_done = true; +} + +void mt76x2_phy_set_antenna(struct mt76x02_dev *dev) +{ + u32 val; + + val = mt76_rr(dev, MT_BBP(AGC, 0)); + val &= ~(BIT(4) | BIT(1)); + switch (dev->mphy.antenna_mask) { + case 1: + /* disable mac DAC control */ + mt76_clear(dev, MT_BBP(IBI, 9), BIT(11)); + mt76_clear(dev, MT_BBP(TXBE, 5), 3); + mt76_rmw_field(dev, MT_TX_PIN_CFG, MT_TX_PIN_CFG_TXANT, 0x3); + mt76_rmw_field(dev, MT_BBP(CORE, 32), GENMASK(21, 20), 2); + /* disable DAC 1 */ + mt76_rmw_field(dev, MT_BBP(CORE, 33), GENMASK(12, 9), 4); + + val &= ~(BIT(3) | BIT(0)); + break; + case 2: + /* disable mac DAC control */ + mt76_clear(dev, MT_BBP(IBI, 9), BIT(11)); + mt76_rmw_field(dev, MT_BBP(TXBE, 5), 3, 1); + mt76_rmw_field(dev, MT_TX_PIN_CFG, MT_TX_PIN_CFG_TXANT, 0xc); + mt76_rmw_field(dev, MT_BBP(CORE, 32), GENMASK(21, 20), 1); + /* disable DAC 0 */ + mt76_rmw_field(dev, MT_BBP(CORE, 33), GENMASK(12, 9), 1); + + val &= ~BIT(3); + val |= BIT(0); + break; + case 3: + default: + /* enable mac DAC control */ + mt76_set(dev, MT_BBP(IBI, 9), BIT(11)); + mt76_set(dev, MT_BBP(TXBE, 5), 3); + mt76_rmw_field(dev, MT_TX_PIN_CFG, MT_TX_PIN_CFG_TXANT, 0xf); + mt76_clear(dev, MT_BBP(CORE, 32), GENMASK(21, 20)); + mt76_clear(dev, MT_BBP(CORE, 33), GENMASK(12, 9)); + + val &= ~BIT(0); + val |= BIT(3); + break; + } + mt76_wr(dev, MT_BBP(AGC, 0), val); +} + +int mt76x2_phy_set_channel(struct mt76x02_dev *dev, + struct cfg80211_chan_def *chandef) +{ + struct ieee80211_channel *chan = chandef->chan; + bool scan = test_bit(MT76_SCANNING, &dev->mphy.state); + enum nl80211_band band = chan->band; + u8 channel; + + u32 ext_cca_chan[4] = { + [0] = FIELD_PREP(MT_EXT_CCA_CFG_CCA0, 0) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA1, 1) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA2, 2) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA3, 3) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA_MASK, BIT(0)), + [1] = FIELD_PREP(MT_EXT_CCA_CFG_CCA0, 1) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA1, 0) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA2, 2) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA3, 3) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA_MASK, BIT(1)), + [2] = FIELD_PREP(MT_EXT_CCA_CFG_CCA0, 2) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA1, 3) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA2, 1) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA3, 0) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA_MASK, BIT(2)), + [3] = FIELD_PREP(MT_EXT_CCA_CFG_CCA0, 3) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA1, 2) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA2, 1) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA3, 0) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA_MASK, BIT(3)), + }; + int ch_group_index; + u8 bw, bw_index; + int freq, freq1; + int ret; + + dev->cal.channel_cal_done = false; + freq = chandef->chan->center_freq; + freq1 = chandef->center_freq1; + channel = chan->hw_value; + + switch (chandef->width) { + case NL80211_CHAN_WIDTH_40: + bw = 1; + if (freq1 > freq) { + bw_index = 1; + ch_group_index = 0; + } else { + bw_index = 3; + ch_group_index = 1; + } + channel += 2 - ch_group_index * 4; + break; + case NL80211_CHAN_WIDTH_80: + ch_group_index = (freq - freq1 + 30) / 20; + if (WARN_ON(ch_group_index < 0 || ch_group_index > 3)) + ch_group_index = 0; + bw = 2; + bw_index = ch_group_index; + channel += 6 - ch_group_index * 4; + break; + default: + bw = 0; + bw_index = 0; + ch_group_index = 0; + break; + } + + mt76x2_read_rx_gain(dev); + mt76x2_phy_set_txpower_regs(dev, band); + mt76x2_configure_tx_delay(dev, band, bw); + mt76x2_phy_set_txpower(dev); + + mt76x02_phy_set_band(dev, chan->band, ch_group_index & 1); + mt76x02_phy_set_bw(dev, chandef->width, ch_group_index); + + mt76_rmw(dev, MT_EXT_CCA_CFG, + (MT_EXT_CCA_CFG_CCA0 | + MT_EXT_CCA_CFG_CCA1 | + MT_EXT_CCA_CFG_CCA2 | + MT_EXT_CCA_CFG_CCA3 | + MT_EXT_CCA_CFG_CCA_MASK), + ext_cca_chan[ch_group_index]); + + ret = mt76x2_mcu_set_channel(dev, channel, bw, bw_index, scan); + if (ret) + return ret; + + mt76x2_mcu_init_gain(dev, channel, dev->cal.rx.mcu_gain, true); + + mt76x2_phy_set_antenna(dev); + + /* Enable LDPC Rx */ + if (mt76xx_rev(dev) >= MT76XX_REV_E3) + mt76_set(dev, MT_BBP(RXO, 13), BIT(10)); + + if (!dev->cal.init_cal_done) { + u8 val = mt76x02_eeprom_get(dev, MT_EE_BT_RCAL_RESULT); + + if (val != 0xff) + mt76x02_mcu_calibrate(dev, MCU_CAL_R, 0); + } + + mt76x02_mcu_calibrate(dev, MCU_CAL_RXDCOC, channel); + + /* Rx LPF calibration */ + if (!dev->cal.init_cal_done) + mt76x02_mcu_calibrate(dev, MCU_CAL_RC, 0); + + dev->cal.init_cal_done = true; + + mt76_wr(dev, MT_BBP(AGC, 61), 0xFF64A4E2); + mt76_wr(dev, MT_BBP(AGC, 7), 0x08081010); + mt76_wr(dev, MT_BBP(AGC, 11), 0x00000404); + mt76_wr(dev, MT_BBP(AGC, 2), 0x00007070); + mt76_wr(dev, MT_TXOP_CTRL_CFG, 0x04101B3F); + + if (scan) + return 0; + + mt76x2_phy_channel_calibrate(dev, true); + mt76x02_init_agc_gain(dev); + + /* init default values for temp compensation */ + if (mt76x2_tssi_enabled(dev)) { + mt76_rmw_field(dev, MT_TX_ALC_CFG_1, MT_TX_ALC_CFG_1_TEMP_COMP, + 0x38); + mt76_rmw_field(dev, MT_TX_ALC_CFG_2, MT_TX_ALC_CFG_2_TEMP_COMP, + 0x38); + } + + ieee80211_queue_delayed_work(mt76_hw(dev), &dev->cal_work, + MT_CALIBRATE_INTERVAL); + + return 0; +} + +static void +mt76x2_phy_temp_compensate(struct mt76x02_dev *dev) +{ + struct mt76x2_temp_comp t; + int temp, db_diff; + + if (mt76x2_get_temp_comp(dev, &t)) + return; + + temp = mt76_get_field(dev, MT_TEMP_SENSOR, MT_TEMP_SENSOR_VAL); + temp -= t.temp_25_ref; + temp = (temp * 1789) / 1000 + 25; + dev->cal.temp = temp; + + if (temp > 25) + db_diff = (temp - 25) / t.high_slope; + else + db_diff = (25 - temp) / t.low_slope; + + db_diff = min(db_diff, t.upper_bound); + db_diff = max(db_diff, t.lower_bound); + + mt76_rmw_field(dev, MT_TX_ALC_CFG_1, MT_TX_ALC_CFG_1_TEMP_COMP, + db_diff * 2); + mt76_rmw_field(dev, MT_TX_ALC_CFG_2, MT_TX_ALC_CFG_2_TEMP_COMP, + db_diff * 2); +} + +void mt76x2_phy_calibrate(struct work_struct *work) +{ + struct mt76x02_dev *dev; + + dev = container_of(work, struct mt76x02_dev, cal_work.work); + + mutex_lock(&dev->mt76.mutex); + + mt76x2_phy_channel_calibrate(dev, false); + mt76x2_phy_tssi_compensate(dev); + mt76x2_phy_temp_compensate(dev); + mt76x2_phy_update_channel_gain(dev); + + mutex_unlock(&dev->mt76.mutex); + + ieee80211_queue_delayed_work(mt76_hw(dev), &dev->cal_work, + MT_CALIBRATE_INTERVAL); +} + +int mt76x2_phy_start(struct mt76x02_dev *dev) +{ + int ret; + + ret = mt76x02_mcu_set_radio_state(dev, true); + if (ret) + return ret; + + mt76x2_mcu_load_cr(dev, MT_RF_BBP_CR, 0, 0); + + return ret; +} diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/phy.c b/drivers/net/wireless/mediatek/mt76/mt76x2/phy.c new file mode 100644 index 000000000..a5efa13a8 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/phy.c @@ -0,0 +1,349 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (C) 2016 Felix Fietkau + * Copyright (C) 2018 Lorenzo Bianconi + */ + +#include "mt76x2.h" +#include "eeprom.h" +#include "mcu.h" +#include "../mt76x02_phy.h" + +static void +mt76x2_adjust_high_lna_gain(struct mt76x02_dev *dev, int reg, s8 offset) +{ + s8 gain; + + gain = FIELD_GET(MT_BBP_AGC_LNA_HIGH_GAIN, + mt76_rr(dev, MT_BBP(AGC, reg))); + gain -= offset / 2; + mt76_rmw_field(dev, MT_BBP(AGC, reg), MT_BBP_AGC_LNA_HIGH_GAIN, gain); +} + +static void +mt76x2_adjust_agc_gain(struct mt76x02_dev *dev, int reg, s8 offset) +{ + s8 gain; + + gain = FIELD_GET(MT_BBP_AGC_GAIN, mt76_rr(dev, MT_BBP(AGC, reg))); + gain += offset; + mt76_rmw_field(dev, MT_BBP(AGC, reg), MT_BBP_AGC_GAIN, gain); +} + +void mt76x2_apply_gain_adj(struct mt76x02_dev *dev) +{ + s8 *gain_adj = dev->cal.rx.high_gain; + + mt76x2_adjust_high_lna_gain(dev, 4, gain_adj[0]); + mt76x2_adjust_high_lna_gain(dev, 5, gain_adj[1]); + + mt76x2_adjust_agc_gain(dev, 8, gain_adj[0]); + mt76x2_adjust_agc_gain(dev, 9, gain_adj[1]); +} +EXPORT_SYMBOL_GPL(mt76x2_apply_gain_adj); + +void mt76x2_phy_set_txpower_regs(struct mt76x02_dev *dev, + enum nl80211_band band) +{ + u32 pa_mode[2]; + u32 pa_mode_adj; + + if (band == NL80211_BAND_2GHZ) { + pa_mode[0] = 0x010055ff; + pa_mode[1] = 0x00550055; + + mt76_wr(dev, MT_TX_ALC_CFG_2, 0x35160a00); + mt76_wr(dev, MT_TX_ALC_CFG_3, 0x35160a06); + + if (mt76x02_ext_pa_enabled(dev, band)) { + mt76_wr(dev, MT_RF_PA_MODE_ADJ0, 0x0000ec00); + mt76_wr(dev, MT_RF_PA_MODE_ADJ1, 0x0000ec00); + } else { + mt76_wr(dev, MT_RF_PA_MODE_ADJ0, 0xf4000200); + mt76_wr(dev, MT_RF_PA_MODE_ADJ1, 0xfa000200); + } + } else { + pa_mode[0] = 0x0000ffff; + pa_mode[1] = 0x00ff00ff; + + if (mt76x02_ext_pa_enabled(dev, band)) { + mt76_wr(dev, MT_TX_ALC_CFG_2, 0x2f0f0400); + mt76_wr(dev, MT_TX_ALC_CFG_3, 0x2f0f0476); + } else { + mt76_wr(dev, MT_TX_ALC_CFG_2, 0x1b0f0400); + mt76_wr(dev, MT_TX_ALC_CFG_3, 0x1b0f0476); + } + + if (mt76x02_ext_pa_enabled(dev, band)) + pa_mode_adj = 0x04000000; + else + pa_mode_adj = 0; + + mt76_wr(dev, MT_RF_PA_MODE_ADJ0, pa_mode_adj); + mt76_wr(dev, MT_RF_PA_MODE_ADJ1, pa_mode_adj); + } + + mt76_wr(dev, MT_BB_PA_MODE_CFG0, pa_mode[0]); + mt76_wr(dev, MT_BB_PA_MODE_CFG1, pa_mode[1]); + mt76_wr(dev, MT_RF_PA_MODE_CFG0, pa_mode[0]); + mt76_wr(dev, MT_RF_PA_MODE_CFG1, pa_mode[1]); + + if (mt76x02_ext_pa_enabled(dev, band)) { + u32 val; + + if (band == NL80211_BAND_2GHZ) + val = 0x3c3c023c; + else + val = 0x363c023c; + + mt76_wr(dev, MT_TX0_RF_GAIN_CORR, val); + mt76_wr(dev, MT_TX1_RF_GAIN_CORR, val); + mt76_wr(dev, MT_TX_ALC_CFG_4, 0x00001818); + } else { + if (band == NL80211_BAND_2GHZ) { + u32 val = 0x0f3c3c3c; + + mt76_wr(dev, MT_TX0_RF_GAIN_CORR, val); + mt76_wr(dev, MT_TX1_RF_GAIN_CORR, val); + mt76_wr(dev, MT_TX_ALC_CFG_4, 0x00000606); + } else { + mt76_wr(dev, MT_TX0_RF_GAIN_CORR, 0x383c023c); + mt76_wr(dev, MT_TX1_RF_GAIN_CORR, 0x24282e28); + mt76_wr(dev, MT_TX_ALC_CFG_4, 0); + } + } +} +EXPORT_SYMBOL_GPL(mt76x2_phy_set_txpower_regs); + +static int +mt76x2_get_min_rate_power(struct mt76x02_rate_power *r) +{ + int i; + s8 ret = 0; + + for (i = 0; i < sizeof(r->all); i++) { + if (!r->all[i]) + continue; + + if (ret) + ret = min(ret, r->all[i]); + else + ret = r->all[i]; + } + + return ret; +} + +void mt76x2_phy_set_txpower(struct mt76x02_dev *dev) +{ + enum nl80211_chan_width width = dev->mphy.chandef.width; + struct ieee80211_channel *chan = dev->mphy.chandef.chan; + struct mt76x2_tx_power_info txp; + int txp_0, txp_1, delta = 0; + struct mt76x02_rate_power t = {}; + int base_power, gain; + + mt76x2_get_power_info(dev, &txp, chan); + + if (width == NL80211_CHAN_WIDTH_40) + delta = txp.delta_bw40; + else if (width == NL80211_CHAN_WIDTH_80) + delta = txp.delta_bw80; + + mt76x2_get_rate_power(dev, &t, chan); + mt76x02_add_rate_power_offset(&t, txp.target_power + delta); + mt76x02_limit_rate_power(&t, dev->txpower_conf); + dev->mphy.txpower_cur = mt76x02_get_max_rate_power(&t); + + base_power = mt76x2_get_min_rate_power(&t); + delta = base_power - txp.target_power; + txp_0 = txp.chain[0].target_power + txp.chain[0].delta + delta; + txp_1 = txp.chain[1].target_power + txp.chain[1].delta + delta; + + gain = min(txp_0, txp_1); + if (gain < 0) { + base_power -= gain; + txp_0 -= gain; + txp_1 -= gain; + } else if (gain > 0x2f) { + base_power -= gain - 0x2f; + txp_0 = 0x2f; + txp_1 = 0x2f; + } + + mt76x02_add_rate_power_offset(&t, -base_power); + dev->target_power = txp.target_power; + dev->target_power_delta[0] = txp_0 - txp.chain[0].target_power; + dev->target_power_delta[1] = txp_1 - txp.chain[0].target_power; + dev->rate_power = t; + + mt76x02_phy_set_txpower(dev, txp_0, txp_1); +} +EXPORT_SYMBOL_GPL(mt76x2_phy_set_txpower); + +void mt76x2_configure_tx_delay(struct mt76x02_dev *dev, + enum nl80211_band band, u8 bw) +{ + u32 cfg0, cfg1; + + if (mt76x02_ext_pa_enabled(dev, band)) { + cfg0 = bw ? 0x000b0c01 : 0x00101101; + cfg1 = 0x00011414; + } else { + cfg0 = bw ? 0x000b0b01 : 0x00101001; + cfg1 = 0x00021414; + } + mt76_wr(dev, MT_TX_SW_CFG0, cfg0); + mt76_wr(dev, MT_TX_SW_CFG1, cfg1); + + mt76_rmw_field(dev, MT_XIFS_TIME_CFG, MT_XIFS_TIME_CFG_OFDM_SIFS, 15); +} +EXPORT_SYMBOL_GPL(mt76x2_configure_tx_delay); + +void mt76x2_phy_tssi_compensate(struct mt76x02_dev *dev) +{ + struct ieee80211_channel *chan = dev->mphy.chandef.chan; + struct mt76x2_tx_power_info txp; + struct mt76x2_tssi_comp t = {}; + + if (!dev->cal.tssi_cal_done) + return; + + if (!dev->cal.tssi_comp_pending) { + /* TSSI trigger */ + t.cal_mode = BIT(0); + mt76x2_mcu_tssi_comp(dev, &t); + dev->cal.tssi_comp_pending = true; + } else { + if (mt76_rr(dev, MT_BBP(CORE, 34)) & BIT(4)) + return; + + dev->cal.tssi_comp_pending = false; + mt76x2_get_power_info(dev, &txp, chan); + + if (mt76x02_ext_pa_enabled(dev, chan->band)) + t.pa_mode = 1; + + t.cal_mode = BIT(1); + t.slope0 = txp.chain[0].tssi_slope; + t.offset0 = txp.chain[0].tssi_offset; + t.slope1 = txp.chain[1].tssi_slope; + t.offset1 = txp.chain[1].tssi_offset; + mt76x2_mcu_tssi_comp(dev, &t); + + if (t.pa_mode || dev->cal.dpd_cal_done || dev->ed_tx_blocked) + return; + + usleep_range(10000, 20000); + mt76x02_mcu_calibrate(dev, MCU_CAL_DPD, chan->hw_value); + dev->cal.dpd_cal_done = true; + } +} +EXPORT_SYMBOL_GPL(mt76x2_phy_tssi_compensate); + +static void +mt76x2_phy_set_gain_val(struct mt76x02_dev *dev) +{ + u32 val; + u8 gain_val[2]; + + gain_val[0] = dev->cal.agc_gain_cur[0] - dev->cal.agc_gain_adjust; + gain_val[1] = dev->cal.agc_gain_cur[1] - dev->cal.agc_gain_adjust; + + val = 0x1836 << 16; + if (!mt76x2_has_ext_lna(dev) && + dev->mphy.chandef.width >= NL80211_CHAN_WIDTH_40) + val = 0x1e42 << 16; + + if (mt76x2_has_ext_lna(dev) && + dev->mphy.chandef.chan->band == NL80211_BAND_2GHZ && + dev->mphy.chandef.width < NL80211_CHAN_WIDTH_40) + val = 0x0f36 << 16; + + val |= 0xf8; + + mt76_wr(dev, MT_BBP(AGC, 8), + val | FIELD_PREP(MT_BBP_AGC_GAIN, gain_val[0])); + mt76_wr(dev, MT_BBP(AGC, 9), + val | FIELD_PREP(MT_BBP_AGC_GAIN, gain_val[1])); + + if (dev->mphy.chandef.chan->flags & IEEE80211_CHAN_RADAR) + mt76x02_phy_dfs_adjust_agc(dev); +} + +void mt76x2_phy_update_channel_gain(struct mt76x02_dev *dev) +{ + u8 *gain = dev->cal.agc_gain_init; + u8 low_gain_delta, gain_delta; + u32 agc_35, agc_37; + bool gain_change; + int low_gain; + u32 val; + + dev->cal.avg_rssi_all = mt76_get_min_avg_rssi(&dev->mt76, 0); + if (!dev->cal.avg_rssi_all) + dev->cal.avg_rssi_all = -75; + + low_gain = (dev->cal.avg_rssi_all > mt76x02_get_rssi_gain_thresh(dev)) + + (dev->cal.avg_rssi_all > mt76x02_get_low_rssi_gain_thresh(dev)); + + gain_change = dev->cal.low_gain < 0 || + (dev->cal.low_gain & 2) ^ (low_gain & 2); + dev->cal.low_gain = low_gain; + + if (!gain_change) { + if (mt76x02_phy_adjust_vga_gain(dev)) + mt76x2_phy_set_gain_val(dev); + return; + } + + if (dev->mphy.chandef.width == NL80211_CHAN_WIDTH_80) { + mt76_wr(dev, MT_BBP(RXO, 14), 0x00560211); + val = mt76_rr(dev, MT_BBP(AGC, 26)) & ~0xf; + if (low_gain == 2) + val |= 0x3; + else + val |= 0x5; + mt76_wr(dev, MT_BBP(AGC, 26), val); + } else { + mt76_wr(dev, MT_BBP(RXO, 14), 0x00560423); + } + + if (mt76x2_has_ext_lna(dev)) + low_gain_delta = 10; + else + low_gain_delta = 14; + + agc_37 = 0x2121262c; + if (dev->mphy.chandef.chan->band == NL80211_BAND_2GHZ) + agc_35 = 0x11111516; + else if (low_gain == 2) + agc_35 = agc_37 = 0x08080808; + else if (dev->mphy.chandef.width == NL80211_CHAN_WIDTH_80) + agc_35 = 0x10101014; + else + agc_35 = 0x11111116; + + if (low_gain == 2) { + mt76_wr(dev, MT_BBP(RXO, 18), 0xf000a990); + mt76_wr(dev, MT_BBP(AGC, 35), 0x08080808); + mt76_wr(dev, MT_BBP(AGC, 37), 0x08080808); + gain_delta = low_gain_delta; + dev->cal.agc_gain_adjust = 0; + } else { + mt76_wr(dev, MT_BBP(RXO, 18), 0xf000a991); + gain_delta = 0; + dev->cal.agc_gain_adjust = low_gain_delta; + } + + mt76_wr(dev, MT_BBP(AGC, 35), agc_35); + mt76_wr(dev, MT_BBP(AGC, 37), agc_37); + + dev->cal.agc_gain_cur[0] = gain[0] - gain_delta; + dev->cal.agc_gain_cur[1] = gain[1] - gain_delta; + mt76x2_phy_set_gain_val(dev); + + /* clear false CCA counters */ + mt76_rr(dev, MT_RX_STAT_1); +} +EXPORT_SYMBOL_GPL(mt76x2_phy_update_channel_gain); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c b/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c new file mode 100644 index 000000000..ade15bdde --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (C) 2018 Lorenzo Bianconi + */ + +#include +#include + +#include "../mt76x02_usb.h" +#include "mt76x2u.h" + +static const struct usb_device_id mt76x2u_device_table[] = { + { USB_DEVICE(0x0b05, 0x1833) }, /* Asus USB-AC54 */ + { USB_DEVICE(0x0b05, 0x17eb) }, /* Asus USB-AC55 */ + { USB_DEVICE(0x0b05, 0x180b) }, /* Asus USB-N53 B1 */ + { USB_DEVICE(0x0e8d, 0x7612) }, /* Aukey USBAC1200 - Alfa AWUS036ACM */ + { USB_DEVICE(0x057c, 0x8503) }, /* Avm FRITZ!WLAN AC860 */ + { USB_DEVICE(0x7392, 0xb711) }, /* Edimax EW 7722 UAC */ + { USB_DEVICE(0x056e, 0x400a) }, /* ELECOM WDC-867SU3S */ + { USB_DEVICE(0x0e8d, 0x7632) }, /* HC-M7662BU1 */ + { USB_DEVICE(0x0471, 0x2126) }, /* LiteOn WN4516R module, nonstandard USB connector */ + { USB_DEVICE(0x0471, 0x7600) }, /* LiteOn WN4519R module, nonstandard USB connector */ + { USB_DEVICE(0x2c4e, 0x0103) }, /* Mercury UD13 */ + { USB_DEVICE(0x0846, 0x9014) }, /* Netgear WNDA3100v3 */ + { USB_DEVICE(0x0846, 0x9053) }, /* Netgear A6210 */ + { USB_DEVICE(0x045e, 0x02e6) }, /* XBox One Wireless Adapter */ + { USB_DEVICE(0x045e, 0x02fe) }, /* XBox One Wireless Adapter */ + { USB_DEVICE(0x2357, 0x0137) }, /* TP-Link TL-WDN6200 */ + { }, +}; + +static int mt76x2u_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + static const struct mt76_driver_ops drv_ops = { + .drv_flags = MT_DRV_SW_RX_AIRTIME | + MT_DRV_IGNORE_TXS_FAILED, + .survey_flags = SURVEY_INFO_TIME_TX, + .update_survey = mt76x02_update_channel, + .set_channel = mt76x2u_set_channel, + .tx_prepare_skb = mt76x02u_tx_prepare_skb, + .tx_complete_skb = mt76x02u_tx_complete_skb, + .tx_status_data = mt76x02_tx_status_data, + .rx_skb = mt76x02_queue_rx_skb, + .sta_ps = mt76x02_sta_ps, + .sta_add = mt76x02_sta_add, + .sta_remove = mt76x02_sta_remove, + }; + struct usb_device *udev = interface_to_usbdev(intf); + struct mt76x02_dev *dev; + struct mt76_dev *mdev; + int err; + + mdev = mt76_alloc_device(&intf->dev, sizeof(*dev), &mt76x2u_ops, + &drv_ops); + if (!mdev) + return -ENOMEM; + + dev = container_of(mdev, struct mt76x02_dev, mt76); + + usb_reset_device(udev); + + usb_set_intfdata(intf, dev); + + mt76x02u_init_mcu(mdev); + err = mt76u_init(mdev, intf); + if (err < 0) + goto err; + + mdev->rev = mt76_rr(dev, MT_ASIC_VERSION); + dev_info(mdev->dev, "ASIC revision: %08x\n", mdev->rev); + if (!is_mt76x2(dev)) { + err = -ENODEV; + goto err; + } + + err = mt76x2u_register_device(dev); + if (err < 0) + goto err; + + return 0; + +err: + mt76u_queues_deinit(&dev->mt76); + mt76_free_device(&dev->mt76); + usb_set_intfdata(intf, NULL); + + return err; +} + +static void mt76x2u_disconnect(struct usb_interface *intf) +{ + struct mt76x02_dev *dev = usb_get_intfdata(intf); + struct ieee80211_hw *hw = mt76_hw(dev); + + set_bit(MT76_REMOVED, &dev->mphy.state); + ieee80211_unregister_hw(hw); + mt76x2u_cleanup(dev); + mt76_free_device(&dev->mt76); + usb_set_intfdata(intf, NULL); +} + +static int __maybe_unused mt76x2u_suspend(struct usb_interface *intf, + pm_message_t state) +{ + struct mt76x02_dev *dev = usb_get_intfdata(intf); + + mt76u_stop_rx(&dev->mt76); + + return 0; +} + +static int __maybe_unused mt76x2u_resume(struct usb_interface *intf) +{ + struct mt76x02_dev *dev = usb_get_intfdata(intf); + int err; + + err = mt76u_resume_rx(&dev->mt76); + if (err < 0) + goto err; + + err = mt76x2u_init_hardware(dev); + if (err < 0) + goto err; + + return 0; + +err: + mt76x2u_cleanup(dev); + return err; +} + +MODULE_DEVICE_TABLE(usb, mt76x2u_device_table); +MODULE_FIRMWARE(MT7662_FIRMWARE); +MODULE_FIRMWARE(MT7662_ROM_PATCH); + +static struct usb_driver mt76x2u_driver = { + .name = KBUILD_MODNAME, + .id_table = mt76x2u_device_table, + .probe = mt76x2u_probe, + .disconnect = mt76x2u_disconnect, +#ifdef CONFIG_PM + .suspend = mt76x2u_suspend, + .resume = mt76x2u_resume, + .reset_resume = mt76x2u_resume, +#endif /* CONFIG_PM */ + .soft_unbind = 1, + .disable_hub_initiated_lpm = 1, +}; +module_usb_driver(mt76x2u_driver); + +MODULE_AUTHOR("Lorenzo Bianconi "); +MODULE_DESCRIPTION("MediaTek MT76x2U (USB) wireless driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/usb_init.c b/drivers/net/wireless/mediatek/mt76/mt76x2/usb_init.c new file mode 100644 index 000000000..41778a8ef --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/usb_init.c @@ -0,0 +1,261 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (C) 2018 Lorenzo Bianconi + */ + +#include + +#include "mt76x2u.h" +#include "eeprom.h" +#include "../mt76x02_phy.h" +#include "../mt76x02_usb.h" + +static void mt76x2u_init_dma(struct mt76x02_dev *dev) +{ + u32 val = mt76_rr(dev, MT_VEND_ADDR(CFG, MT_USB_U3DMA_CFG)); + + val |= MT_USB_DMA_CFG_RX_DROP_OR_PAD | + MT_USB_DMA_CFG_RX_BULK_EN | + MT_USB_DMA_CFG_TX_BULK_EN; + + /* disable AGGR_BULK_RX in order to receive one + * frame in each rx urb and avoid copies + */ + val &= ~MT_USB_DMA_CFG_RX_BULK_AGG_EN; + mt76_wr(dev, MT_VEND_ADDR(CFG, MT_USB_U3DMA_CFG), val); +} + +static void mt76x2u_power_on_rf_patch(struct mt76x02_dev *dev) +{ + mt76_set(dev, MT_VEND_ADDR(CFG, 0x130), BIT(0) | BIT(16)); + udelay(1); + + mt76_clear(dev, MT_VEND_ADDR(CFG, 0x1c), 0xff); + mt76_set(dev, MT_VEND_ADDR(CFG, 0x1c), 0x30); + + mt76_wr(dev, MT_VEND_ADDR(CFG, 0x14), 0x484f); + udelay(1); + + mt76_set(dev, MT_VEND_ADDR(CFG, 0x130), BIT(17)); + usleep_range(150, 200); + + mt76_clear(dev, MT_VEND_ADDR(CFG, 0x130), BIT(16)); + usleep_range(50, 100); + + mt76_set(dev, MT_VEND_ADDR(CFG, 0x14c), BIT(19) | BIT(20)); +} + +static void mt76x2u_power_on_rf(struct mt76x02_dev *dev, int unit) +{ + int shift = unit ? 8 : 0; + u32 val = (BIT(1) | BIT(3) | BIT(4) | BIT(5)) << shift; + + /* Enable RF BG */ + mt76_set(dev, MT_VEND_ADDR(CFG, 0x130), BIT(0) << shift); + usleep_range(10, 20); + + /* Enable RFDIG LDO/AFE/ABB/ADDA */ + mt76_set(dev, MT_VEND_ADDR(CFG, 0x130), val); + usleep_range(10, 20); + + /* Switch RFDIG power to internal LDO */ + mt76_clear(dev, MT_VEND_ADDR(CFG, 0x130), BIT(2) << shift); + usleep_range(10, 20); + + mt76x2u_power_on_rf_patch(dev); + + mt76_set(dev, 0x530, 0xf); +} + +static void mt76x2u_power_on(struct mt76x02_dev *dev) +{ + u32 val; + + /* Turn on WL MTCMOS */ + mt76_set(dev, MT_VEND_ADDR(CFG, 0x148), + MT_WLAN_MTC_CTRL_MTCMOS_PWR_UP); + + val = MT_WLAN_MTC_CTRL_STATE_UP | + MT_WLAN_MTC_CTRL_PWR_ACK | + MT_WLAN_MTC_CTRL_PWR_ACK_S; + + mt76_poll(dev, MT_VEND_ADDR(CFG, 0x148), val, val, 1000); + + mt76_clear(dev, MT_VEND_ADDR(CFG, 0x148), 0x7f << 16); + usleep_range(10, 20); + + mt76_clear(dev, MT_VEND_ADDR(CFG, 0x148), 0xf << 24); + usleep_range(10, 20); + + mt76_set(dev, MT_VEND_ADDR(CFG, 0x148), 0xf << 24); + mt76_clear(dev, MT_VEND_ADDR(CFG, 0x148), 0xfff); + + /* Turn on AD/DA power down */ + mt76_clear(dev, MT_VEND_ADDR(CFG, 0x1204), BIT(3)); + + /* WLAN function enable */ + mt76_set(dev, MT_VEND_ADDR(CFG, 0x80), BIT(0)); + + /* Release BBP software reset */ + mt76_clear(dev, MT_VEND_ADDR(CFG, 0x64), BIT(18)); + + mt76x2u_power_on_rf(dev, 0); + mt76x2u_power_on_rf(dev, 1); +} + +static int mt76x2u_init_eeprom(struct mt76x02_dev *dev) +{ + u32 val, i; + + dev->mt76.eeprom.data = devm_kzalloc(dev->mt76.dev, + MT7612U_EEPROM_SIZE, + GFP_KERNEL); + dev->mt76.eeprom.size = MT7612U_EEPROM_SIZE; + if (!dev->mt76.eeprom.data) + return -ENOMEM; + + for (i = 0; i + 4 <= MT7612U_EEPROM_SIZE; i += 4) { + val = mt76_rr(dev, MT_VEND_ADDR(EEPROM, i)); + put_unaligned_le32(val, dev->mt76.eeprom.data + i); + } + + mt76x02_eeprom_parse_hw_cap(dev); + return 0; +} + +int mt76x2u_init_hardware(struct mt76x02_dev *dev) +{ + int i, k, err; + + mt76x2_reset_wlan(dev, true); + mt76x2u_power_on(dev); + + if (!mt76x02_wait_for_mac(&dev->mt76)) + return -ETIMEDOUT; + + err = mt76x2u_mcu_fw_init(dev); + if (err < 0) + return err; + + if (!mt76_poll_msec(dev, MT_WPDMA_GLO_CFG, + MT_WPDMA_GLO_CFG_TX_DMA_BUSY | + MT_WPDMA_GLO_CFG_RX_DMA_BUSY, 0, 100)) + return -EIO; + + /* wait for asic ready after fw load. */ + if (!mt76x02_wait_for_mac(&dev->mt76)) + return -ETIMEDOUT; + + mt76x2u_init_dma(dev); + + err = mt76x2u_mcu_init(dev); + if (err < 0) + return err; + + err = mt76x2u_mac_reset(dev); + if (err < 0) + return err; + + mt76x02_mac_setaddr(dev, dev->mt76.eeprom.data + MT_EE_MAC_ADDR); + dev->mt76.rxfilter = mt76_rr(dev, MT_RX_FILTR_CFG); + + if (!mt76x02_wait_for_txrx_idle(&dev->mt76)) + return -ETIMEDOUT; + + /* reset wcid table */ + for (i = 0; i < 256; i++) + mt76x02_mac_wcid_setup(dev, i, 0, NULL); + + /* reset shared key table and pairwise key table */ + for (i = 0; i < 16; i++) { + for (k = 0; k < 4; k++) + mt76x02_mac_shared_key_setup(dev, i, k, NULL); + } + + mt76x02u_init_beacon_config(dev); + + mt76_rmw(dev, MT_US_CYC_CFG, MT_US_CYC_CNT, 0x1e); + mt76_wr(dev, MT_TXOP_CTRL_CFG, 0x583f); + + err = mt76x2_mcu_load_cr(dev, MT_RF_BBP_CR, 0, 0); + if (err < 0) + return err; + + mt76x02_phy_set_rxpath(dev); + mt76x02_phy_set_txdac(dev); + + return mt76x2u_mac_stop(dev); +} + +int mt76x2u_register_device(struct mt76x02_dev *dev) +{ + struct ieee80211_hw *hw = mt76_hw(dev); + struct mt76_usb *usb = &dev->mt76.usb; + bool vht; + int err; + + INIT_DELAYED_WORK(&dev->cal_work, mt76x2u_phy_calibrate); + err = mt76x02_init_device(dev); + if (err) + return err; + + err = mt76x2u_init_eeprom(dev); + if (err < 0) + return err; + + usb->mcu.data = devm_kmalloc(dev->mt76.dev, MCU_RESP_URB_SIZE, + GFP_KERNEL); + if (!usb->mcu.data) + return -ENOMEM; + + err = mt76u_alloc_queues(&dev->mt76); + if (err < 0) + goto fail; + + err = mt76x2u_init_hardware(dev); + if (err < 0) + goto fail; + + /* check hw sg support in order to enable AMSDU */ + hw->max_tx_fragments = dev->mt76.usb.sg_en ? MT_TX_SG_MAX_SIZE : 1; + switch (dev->mt76.rev) { + case 0x76320044: + /* these ASIC revisions do not support VHT */ + vht = false; + break; + default: + vht = true; + break; + } + + err = mt76_register_device(&dev->mt76, vht, mt76x02_rates, + ARRAY_SIZE(mt76x02_rates)); + if (err) + goto fail; + + set_bit(MT76_STATE_INITIALIZED, &dev->mphy.state); + + mt76x02_init_debugfs(dev); + mt76x2_init_txpower(dev, &dev->mphy.sband_2g.sband); + mt76x2_init_txpower(dev, &dev->mphy.sband_5g.sband); + + return 0; + +fail: + mt76x2u_cleanup(dev); + return err; +} + +void mt76x2u_stop_hw(struct mt76x02_dev *dev) +{ + cancel_delayed_work_sync(&dev->cal_work); + cancel_delayed_work_sync(&dev->mphy.mac_work); + mt76x2u_mac_stop(dev); +} + +void mt76x2u_cleanup(struct mt76x02_dev *dev) +{ + mt76x02_mcu_set_radio_state(dev, false); + mt76x2u_stop_hw(dev); + mt76u_queues_deinit(&dev->mt76); +} diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/usb_mac.c b/drivers/net/wireless/mediatek/mt76/mt76x2/usb_mac.c new file mode 100644 index 000000000..d0cb511ac --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/usb_mac.c @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (C) 2018 Lorenzo Bianconi + */ + +#include "mt76x2u.h" +#include "eeprom.h" + +static void mt76x2u_mac_fixup_xtal(struct mt76x02_dev *dev) +{ + s8 offset = 0; + u16 eep_val; + + eep_val = mt76x02_eeprom_get(dev, MT_EE_XTAL_TRIM_2); + + offset = eep_val & 0x7f; + if ((eep_val & 0xff) == 0xff) + offset = 0; + else if (eep_val & 0x80) + offset = 0 - offset; + + eep_val >>= 8; + if (eep_val == 0x00 || eep_val == 0xff) { + eep_val = mt76x02_eeprom_get(dev, MT_EE_XTAL_TRIM_1); + eep_val &= 0xff; + + if (eep_val == 0x00 || eep_val == 0xff) + eep_val = 0x14; + } + + eep_val &= 0x7f; + mt76_rmw_field(dev, MT_VEND_ADDR(CFG, MT_XO_CTRL5), + MT_XO_CTRL5_C2_VAL, eep_val + offset); + mt76_set(dev, MT_VEND_ADDR(CFG, MT_XO_CTRL6), MT_XO_CTRL6_C2_CTRL); + + mt76_wr(dev, 0x504, 0x06000000); + mt76_wr(dev, 0x50c, 0x08800000); + mdelay(5); + mt76_wr(dev, 0x504, 0x0); + + /* decrease SIFS from 16us to 13us */ + mt76_rmw_field(dev, MT_XIFS_TIME_CFG, + MT_XIFS_TIME_CFG_OFDM_SIFS, 0xd); + mt76_rmw_field(dev, MT_BKOFF_SLOT_CFG, MT_BKOFF_SLOT_CFG_CC_DELAY, 1); + + /* init fce */ + mt76_clear(dev, MT_FCE_L2_STUFF, MT_FCE_L2_STUFF_WR_MPDU_LEN_EN); + + eep_val = mt76x02_eeprom_get(dev, MT_EE_NIC_CONF_2); + switch (FIELD_GET(MT_EE_NIC_CONF_2_XTAL_OPTION, eep_val)) { + case 0: + mt76_wr(dev, MT_XO_CTRL7, 0x5c1fee80); + break; + case 1: + mt76_wr(dev, MT_XO_CTRL7, 0x5c1feed0); + break; + default: + break; + } +} + +int mt76x2u_mac_reset(struct mt76x02_dev *dev) +{ + mt76_wr(dev, MT_WPDMA_GLO_CFG, BIT(4) | BIT(5)); + + /* init pbf regs */ + mt76_wr(dev, MT_PBF_TX_MAX_PCNT, 0xefef3f1f); + mt76_wr(dev, MT_PBF_RX_MAX_PCNT, 0xfebf); + + mt76_write_mac_initvals(dev); + + mt76_wr(dev, MT_TX_LINK_CFG, 0x1020); + mt76_wr(dev, MT_AUTO_RSP_CFG, 0x13); + mt76_wr(dev, MT_MAX_LEN_CFG, 0x2f00); + + mt76_wr(dev, MT_WMM_AIFSN, 0x2273); + mt76_wr(dev, MT_WMM_CWMIN, 0x2344); + mt76_wr(dev, MT_WMM_CWMAX, 0x34aa); + + mt76_clear(dev, MT_MAC_SYS_CTRL, + MT_MAC_SYS_CTRL_RESET_CSR | + MT_MAC_SYS_CTRL_RESET_BBP); + + if (is_mt7612(dev)) + mt76_clear(dev, MT_COEXCFG0, MT_COEXCFG0_COEX_EN); + + mt76_set(dev, MT_EXT_CCA_CFG, 0xf000); + mt76_clear(dev, MT_TX_ALC_CFG_4, BIT(31)); + + mt76x2u_mac_fixup_xtal(dev); + + return 0; +} + +int mt76x2u_mac_stop(struct mt76x02_dev *dev) +{ + int i, count = 0, val; + bool stopped = false; + u32 rts_cfg; + + if (test_bit(MT76_REMOVED, &dev->mphy.state)) + return -EIO; + + rts_cfg = mt76_rr(dev, MT_TX_RTS_CFG); + mt76_wr(dev, MT_TX_RTS_CFG, rts_cfg & ~MT_TX_RTS_CFG_RETRY_LIMIT); + + mt76_clear(dev, MT_TXOP_CTRL_CFG, MT_TXOP_ED_CCA_EN); + mt76_clear(dev, MT_TXOP_HLDR_ET, MT_TXOP_HLDR_TX40M_BLK_EN); + + /* wait tx dma to stop */ + for (i = 0; i < 2000; i++) { + val = mt76_rr(dev, MT_VEND_ADDR(CFG, MT_USB_U3DMA_CFG)); + if (!(val & MT_USB_DMA_CFG_TX_BUSY) && i > 10) + break; + usleep_range(50, 100); + } + + /* page count on TxQ */ + for (i = 0; i < 200; i++) { + if (!(mt76_rr(dev, 0x0438) & 0xffffffff) && + !(mt76_rr(dev, 0x0a30) & 0x000000ff) && + !(mt76_rr(dev, 0x0a34) & 0xff00ff00)) + break; + usleep_range(10, 20); + } + + /* disable tx-rx */ + mt76_clear(dev, MT_MAC_SYS_CTRL, + MT_MAC_SYS_CTRL_ENABLE_RX | + MT_MAC_SYS_CTRL_ENABLE_TX); + + /* Wait for MAC to become idle */ + for (i = 0; i < 1000; i++) { + if (!(mt76_rr(dev, MT_MAC_STATUS) & MT_MAC_STATUS_TX) && + !mt76_rr(dev, MT_BBP(IBI, 12))) { + stopped = true; + break; + } + usleep_range(10, 20); + } + + if (!stopped) { + mt76_set(dev, MT_BBP(CORE, 4), BIT(1)); + mt76_clear(dev, MT_BBP(CORE, 4), BIT(1)); + + mt76_set(dev, MT_BBP(CORE, 4), BIT(0)); + mt76_clear(dev, MT_BBP(CORE, 4), BIT(0)); + } + + /* page count on RxQ */ + for (i = 0; i < 200; i++) { + if (!(mt76_rr(dev, 0x0430) & 0x00ff0000) && + !(mt76_rr(dev, 0x0a30) & 0xffffffff) && + !(mt76_rr(dev, 0x0a34) & 0xffffffff) && + ++count > 10) + break; + msleep(50); + } + + if (!mt76_poll(dev, MT_MAC_STATUS, MT_MAC_STATUS_RX, 0, 2000)) + dev_warn(dev->mt76.dev, "MAC RX failed to stop\n"); + + /* wait rx dma to stop */ + for (i = 0; i < 2000; i++) { + val = mt76_rr(dev, MT_VEND_ADDR(CFG, MT_USB_U3DMA_CFG)); + if (!(val & MT_USB_DMA_CFG_RX_BUSY) && i > 10) + break; + usleep_range(50, 100); + } + + mt76_wr(dev, MT_TX_RTS_CFG, rts_cfg); + + return 0; +} diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/usb_main.c b/drivers/net/wireless/mediatek/mt76/mt76x2/usb_main.c new file mode 100644 index 000000000..66b06a493 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/usb_main.c @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (C) 2018 Lorenzo Bianconi + */ + +#include "mt76x2u.h" +#include "../mt76x02_usb.h" + +static int mt76x2u_start(struct ieee80211_hw *hw) +{ + struct mt76x02_dev *dev = hw->priv; + int ret; + + ret = mt76x02u_mac_start(dev); + if (ret) + return ret; + + ieee80211_queue_delayed_work(mt76_hw(dev), &dev->mphy.mac_work, + MT_MAC_WORK_INTERVAL); + set_bit(MT76_STATE_RUNNING, &dev->mphy.state); + + return 0; +} + +static void mt76x2u_stop(struct ieee80211_hw *hw, bool suspend) +{ + struct mt76x02_dev *dev = hw->priv; + + clear_bit(MT76_STATE_RUNNING, &dev->mphy.state); + mt76u_stop_tx(&dev->mt76); + mt76x2u_stop_hw(dev); +} + +int mt76x2u_set_channel(struct mt76_phy *mphy) +{ + struct mt76x02_dev *dev = container_of(mphy->dev, struct mt76x02_dev, mt76); + int err; + + mt76x02_pre_tbtt_enable(dev, false); + mt76x2_mac_stop(dev, false); + + err = mt76x2u_phy_set_channel(dev, &mphy->chandef); + + mt76x02_mac_cc_reset(dev); + mt76x2_mac_resume(dev); + + mt76x02_pre_tbtt_enable(dev, true); + + return err; +} + +static int +mt76x2u_config(struct ieee80211_hw *hw, int radio_idx, u32 changed) +{ + struct mt76x02_dev *dev = hw->priv; + int err = 0; + + mutex_lock(&dev->mt76.mutex); + + if (changed & IEEE80211_CONF_CHANGE_MONITOR) { + if (!(hw->conf.flags & IEEE80211_CONF_MONITOR)) + dev->mt76.rxfilter |= MT_RX_FILTR_CFG_PROMISC; + else + dev->mt76.rxfilter &= ~MT_RX_FILTR_CFG_PROMISC; + mt76_wr(dev, MT_RX_FILTR_CFG, dev->mt76.rxfilter); + } + + if (changed & IEEE80211_CONF_CHANGE_POWER) { + struct mt76_phy *mphy = &dev->mphy; + + dev->txpower_conf = hw->conf.power_level * 2; + dev->txpower_conf = mt76_get_sar_power(mphy, + mphy->chandef.chan, + dev->txpower_conf); + /* convert to per-chain power for 2x2 devices */ + dev->txpower_conf -= 6; + + if (test_bit(MT76_STATE_RUNNING, &mphy->state)) + mt76x2_phy_set_txpower(dev); + } + + mutex_unlock(&dev->mt76.mutex); + + if (changed & IEEE80211_CONF_CHANGE_CHANNEL) + mt76_update_channel(&dev->mphy); + + return err; +} + +const struct ieee80211_ops mt76x2u_ops = { + .add_chanctx = ieee80211_emulate_add_chanctx, + .remove_chanctx = ieee80211_emulate_remove_chanctx, + .change_chanctx = ieee80211_emulate_change_chanctx, + .switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx, + .tx = mt76x02_tx, + .start = mt76x2u_start, + .stop = mt76x2u_stop, + .add_interface = mt76x02_add_interface, + .remove_interface = mt76x02_remove_interface, + .sta_state = mt76_sta_state, + .sta_pre_rcu_remove = mt76_sta_pre_rcu_remove, + .set_key = mt76x02_set_key, + .ampdu_action = mt76x02_ampdu_action, + .config = mt76x2u_config, + .wake_tx_queue = mt76_wake_tx_queue, + .bss_info_changed = mt76x02_bss_info_changed, + .configure_filter = mt76x02_configure_filter, + .conf_tx = mt76x02_conf_tx, + .sw_scan_start = mt76_sw_scan, + .sw_scan_complete = mt76x02_sw_scan_complete, + .sta_rate_tbl_update = mt76x02_sta_rate_tbl_update, + .get_txpower = mt76_get_txpower, + .get_survey = mt76_get_survey, + .set_tim = mt76_set_tim, + .release_buffered_frames = mt76_release_buffered_frames, + .get_antenna = mt76_get_antenna, + .set_sar_specs = mt76x2_set_sar_specs, +}; diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/usb_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76x2/usb_mcu.c new file mode 100644 index 000000000..9102be180 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/usb_mcu.c @@ -0,0 +1,255 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (C) 2018 Lorenzo Bianconi + */ + +#include + +#include "mt76x2u.h" +#include "eeprom.h" +#include "../mt76x02_usb.h" + +#define MT_CMD_HDR_LEN 4 + +#define MCU_FW_URB_MAX_PAYLOAD 0x3900 +#define MCU_ROM_PATCH_MAX_PAYLOAD 2048 + +#define MT76U_MCU_ILM_OFFSET 0x80000 +#define MT76U_MCU_DLM_OFFSET 0x110000 +#define MT76U_MCU_ROM_PATCH_OFFSET 0x90000 + +static void mt76x2u_mcu_load_ivb(struct mt76x02_dev *dev) +{ + mt76u_vendor_request(&dev->mt76, MT_VEND_DEV_MODE, + USB_DIR_OUT | USB_TYPE_VENDOR, + 0x12, 0, NULL, 0); +} + +static void mt76x2u_mcu_enable_patch(struct mt76x02_dev *dev) +{ + struct mt76_usb *usb = &dev->mt76.usb; + static const u8 data[] = { + 0x6f, 0xfc, 0x08, 0x01, + 0x20, 0x04, 0x00, 0x00, + 0x00, 0x09, 0x00, + }; + + memcpy(usb->data, data, sizeof(data)); + mt76u_vendor_request(&dev->mt76, MT_VEND_DEV_MODE, + USB_DIR_OUT | USB_TYPE_CLASS, + 0x12, 0, usb->data, sizeof(data)); +} + +static void mt76x2u_mcu_reset_wmt(struct mt76x02_dev *dev) +{ + struct mt76_usb *usb = &dev->mt76.usb; + u8 data[] = { + 0x6f, 0xfc, 0x05, 0x01, + 0x07, 0x01, 0x00, 0x04 + }; + + memcpy(usb->data, data, sizeof(data)); + mt76u_vendor_request(&dev->mt76, MT_VEND_DEV_MODE, + USB_DIR_OUT | USB_TYPE_CLASS, + 0x12, 0, usb->data, sizeof(data)); +} + +static int mt76x2u_mcu_load_rom_patch(struct mt76x02_dev *dev) +{ + bool rom_protect = !is_mt7612(dev); + struct mt76x02_patch_header *hdr; + u32 val, patch_mask, patch_reg; + const struct firmware *fw; + int err; + + if (rom_protect && + !mt76_poll_msec(dev, MT_MCU_SEMAPHORE_03, 1, 1, 600)) { + dev_err(dev->mt76.dev, + "could not get hardware semaphore for ROM PATCH\n"); + return -ETIMEDOUT; + } + + if (mt76xx_rev(dev) >= MT76XX_REV_E3) { + patch_mask = BIT(0); + patch_reg = MT_MCU_CLOCK_CTL; + } else { + patch_mask = BIT(1); + patch_reg = MT_MCU_COM_REG0; + } + + if (rom_protect && (mt76_rr(dev, patch_reg) & patch_mask)) { + dev_info(dev->mt76.dev, "ROM patch already applied\n"); + return 0; + } + + err = request_firmware(&fw, MT7662_ROM_PATCH, dev->mt76.dev); + if (err < 0) + return err; + + if (!fw || !fw->data || fw->size <= sizeof(*hdr)) { + dev_err(dev->mt76.dev, "failed to load firmware\n"); + err = -EIO; + goto out; + } + + hdr = (struct mt76x02_patch_header *)fw->data; + dev_info(dev->mt76.dev, "ROM patch build: %.15s\n", hdr->build_time); + + /* enable USB_DMA_CFG */ + val = MT_USB_DMA_CFG_RX_BULK_EN | + MT_USB_DMA_CFG_TX_BULK_EN | + FIELD_PREP(MT_USB_DMA_CFG_RX_BULK_AGG_TOUT, 0x20); + mt76_wr(dev, MT_VEND_ADDR(CFG, MT_USB_U3DMA_CFG), val); + + /* vendor reset */ + mt76x02u_mcu_fw_reset(dev); + usleep_range(5000, 10000); + + /* enable FCE to send in-band cmd */ + mt76_wr(dev, MT_FCE_PSE_CTRL, 0x1); + /* FCE tx_fs_base_ptr */ + mt76_wr(dev, MT_TX_CPU_FROM_FCE_BASE_PTR, 0x400230); + /* FCE tx_fs_max_cnt */ + mt76_wr(dev, MT_TX_CPU_FROM_FCE_MAX_COUNT, 0x1); + /* FCE pdma enable */ + mt76_wr(dev, MT_FCE_PDMA_GLOBAL_CONF, 0x44); + /* FCE skip_fs_en */ + mt76_wr(dev, MT_FCE_SKIP_FS, 0x3); + + err = mt76x02u_mcu_fw_send_data(dev, fw->data + sizeof(*hdr), + fw->size - sizeof(*hdr), + MCU_ROM_PATCH_MAX_PAYLOAD, + MT76U_MCU_ROM_PATCH_OFFSET); + if (err < 0) { + err = -EIO; + goto out; + } + + mt76x2u_mcu_enable_patch(dev); + mt76x2u_mcu_reset_wmt(dev); + mdelay(20); + + if (!mt76_poll_msec(dev, patch_reg, patch_mask, patch_mask, 100)) { + dev_err(dev->mt76.dev, "failed to load ROM patch\n"); + err = -ETIMEDOUT; + } + +out: + if (rom_protect) + mt76_wr(dev, MT_MCU_SEMAPHORE_03, 1); + release_firmware(fw); + return err; +} + +static int mt76x2u_mcu_load_firmware(struct mt76x02_dev *dev) +{ + u32 val, dlm_offset = MT76U_MCU_DLM_OFFSET; + const struct mt76x02_fw_header *hdr; + int err, len, ilm_len, dlm_len; + const struct firmware *fw; + + err = request_firmware(&fw, MT7662_FIRMWARE, dev->mt76.dev); + if (err < 0) + return err; + + if (!fw || !fw->data || fw->size < sizeof(*hdr)) { + err = -EINVAL; + goto out; + } + + hdr = (const struct mt76x02_fw_header *)fw->data; + ilm_len = le32_to_cpu(hdr->ilm_len); + dlm_len = le32_to_cpu(hdr->dlm_len); + len = sizeof(*hdr) + ilm_len + dlm_len; + if (fw->size != len) { + err = -EINVAL; + goto out; + } + + val = le16_to_cpu(hdr->fw_ver); + dev_info(dev->mt76.dev, "Firmware Version: %d.%d.%02d\n", + (val >> 12) & 0xf, (val >> 8) & 0xf, val & 0xf); + + val = le16_to_cpu(hdr->build_ver); + dev_info(dev->mt76.dev, "Build: %x\n", val); + dev_info(dev->mt76.dev, "Build Time: %.16s\n", hdr->build_time); + + /* vendor reset */ + mt76x02u_mcu_fw_reset(dev); + usleep_range(5000, 10000); + + /* enable USB_DMA_CFG */ + val = MT_USB_DMA_CFG_RX_BULK_EN | + MT_USB_DMA_CFG_TX_BULK_EN | + FIELD_PREP(MT_USB_DMA_CFG_RX_BULK_AGG_TOUT, 0x20); + mt76_wr(dev, MT_VEND_ADDR(CFG, MT_USB_U3DMA_CFG), val); + /* enable FCE to send in-band cmd */ + mt76_wr(dev, MT_FCE_PSE_CTRL, 0x1); + /* FCE tx_fs_base_ptr */ + mt76_wr(dev, MT_TX_CPU_FROM_FCE_BASE_PTR, 0x400230); + /* FCE tx_fs_max_cnt */ + mt76_wr(dev, MT_TX_CPU_FROM_FCE_MAX_COUNT, 0x1); + /* FCE pdma enable */ + mt76_wr(dev, MT_FCE_PDMA_GLOBAL_CONF, 0x44); + /* FCE skip_fs_en */ + mt76_wr(dev, MT_FCE_SKIP_FS, 0x3); + + /* load ILM */ + err = mt76x02u_mcu_fw_send_data(dev, fw->data + sizeof(*hdr), + ilm_len, MCU_FW_URB_MAX_PAYLOAD, + MT76U_MCU_ILM_OFFSET); + if (err < 0) { + err = -EIO; + goto out; + } + + /* load DLM */ + if (mt76xx_rev(dev) >= MT76XX_REV_E3) + dlm_offset += 0x800; + err = mt76x02u_mcu_fw_send_data(dev, fw->data + sizeof(*hdr) + ilm_len, + dlm_len, MCU_FW_URB_MAX_PAYLOAD, + dlm_offset); + if (err < 0) { + err = -EIO; + goto out; + } + + mt76x2u_mcu_load_ivb(dev); + if (!mt76_poll_msec(dev, MT_MCU_COM_REG0, 1, 1, 100)) { + dev_err(dev->mt76.dev, "firmware failed to start\n"); + err = -ETIMEDOUT; + goto out; + } + + mt76_set(dev, MT_MCU_COM_REG0, BIT(1)); + /* enable FCE to send in-band cmd */ + mt76_wr(dev, MT_FCE_PSE_CTRL, 0x1); + mt76x02_set_ethtool_fwver(dev, hdr); + dev_dbg(dev->mt76.dev, "firmware running\n"); + +out: + release_firmware(fw); + return err; +} + +int mt76x2u_mcu_fw_init(struct mt76x02_dev *dev) +{ + int err; + + err = mt76x2u_mcu_load_rom_patch(dev); + if (err < 0) + return err; + + return mt76x2u_mcu_load_firmware(dev); +} + +int mt76x2u_mcu_init(struct mt76x02_dev *dev) +{ + int err; + + err = mt76x02_mcu_function_select(dev, Q_SELECT, 1); + if (err < 0) + return err; + + return mt76x02_mcu_set_radio_state(dev, true); +} diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/usb_phy.c b/drivers/net/wireless/mediatek/mt76/mt76x2/usb_phy.c new file mode 100644 index 000000000..b63dd7f3e --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/usb_phy.c @@ -0,0 +1,201 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (C) 2018 Lorenzo Bianconi + */ + +#include "mt76x2u.h" +#include "eeprom.h" +#include "../mt76x02_phy.h" + +static void +mt76x2u_phy_channel_calibrate(struct mt76x02_dev *dev, bool mac_stopped) +{ + struct ieee80211_channel *chan = dev->mphy.chandef.chan; + bool is_5ghz = chan->band == NL80211_BAND_5GHZ; + + if (dev->cal.channel_cal_done) + return; + + if (mt76x2_channel_silent(dev)) + return; + + if (!mac_stopped) + mt76x2u_mac_stop(dev); + + if (is_5ghz) + mt76x02_mcu_calibrate(dev, MCU_CAL_LC, 0); + + mt76x02_mcu_calibrate(dev, MCU_CAL_TX_LOFT, is_5ghz); + mt76x02_mcu_calibrate(dev, MCU_CAL_TXIQ, is_5ghz); + mt76x02_mcu_calibrate(dev, MCU_CAL_RXIQC_FI, is_5ghz); + mt76x02_mcu_calibrate(dev, MCU_CAL_TEMP_SENSOR, 0); + mt76x02_mcu_calibrate(dev, MCU_CAL_TX_SHAPING, 0); + + if (!mac_stopped) + mt76x2_mac_resume(dev); + mt76x2_apply_gain_adj(dev); + mt76x02_edcca_init(dev); + + dev->cal.channel_cal_done = true; +} + +void mt76x2u_phy_calibrate(struct work_struct *work) +{ + struct mt76x02_dev *dev; + + dev = container_of(work, struct mt76x02_dev, cal_work.work); + + mutex_lock(&dev->mt76.mutex); + + mt76x2u_phy_channel_calibrate(dev, false); + mt76x2_phy_tssi_compensate(dev); + mt76x2_phy_update_channel_gain(dev); + + mutex_unlock(&dev->mt76.mutex); + + ieee80211_queue_delayed_work(mt76_hw(dev), &dev->cal_work, + MT_CALIBRATE_INTERVAL); +} + +int mt76x2u_phy_set_channel(struct mt76x02_dev *dev, + struct cfg80211_chan_def *chandef) +{ + u32 ext_cca_chan[4] = { + [0] = FIELD_PREP(MT_EXT_CCA_CFG_CCA0, 0) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA1, 1) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA2, 2) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA3, 3) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA_MASK, BIT(0)), + [1] = FIELD_PREP(MT_EXT_CCA_CFG_CCA0, 1) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA1, 0) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA2, 2) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA3, 3) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA_MASK, BIT(1)), + [2] = FIELD_PREP(MT_EXT_CCA_CFG_CCA0, 2) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA1, 3) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA2, 1) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA3, 0) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA_MASK, BIT(2)), + [3] = FIELD_PREP(MT_EXT_CCA_CFG_CCA0, 3) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA1, 2) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA2, 1) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA3, 0) | + FIELD_PREP(MT_EXT_CCA_CFG_CCA_MASK, BIT(3)), + }; + bool scan = test_bit(MT76_SCANNING, &dev->mphy.state); + struct ieee80211_channel *chan = chandef->chan; + u8 channel = chan->hw_value, bw, bw_index; + int ch_group_index, freq, freq1, ret; + + dev->cal.channel_cal_done = false; + freq = chandef->chan->center_freq; + freq1 = chandef->center_freq1; + + switch (chandef->width) { + case NL80211_CHAN_WIDTH_40: + bw = 1; + if (freq1 > freq) { + bw_index = 1; + ch_group_index = 0; + } else { + bw_index = 3; + ch_group_index = 1; + } + channel += 2 - ch_group_index * 4; + break; + case NL80211_CHAN_WIDTH_80: + ch_group_index = (freq - freq1 + 30) / 20; + if (WARN_ON(ch_group_index < 0 || ch_group_index > 3)) + ch_group_index = 0; + bw = 2; + bw_index = ch_group_index; + channel += 6 - ch_group_index * 4; + break; + default: + bw = 0; + bw_index = 0; + ch_group_index = 0; + break; + } + + mt76x2_read_rx_gain(dev); + mt76x2_phy_set_txpower_regs(dev, chan->band); + mt76x2_configure_tx_delay(dev, chan->band, bw); + mt76x2_phy_set_txpower(dev); + + mt76x02_phy_set_band(dev, chan->band, ch_group_index & 1); + mt76x02_phy_set_bw(dev, chandef->width, ch_group_index); + + mt76_rmw(dev, MT_EXT_CCA_CFG, + (MT_EXT_CCA_CFG_CCA0 | + MT_EXT_CCA_CFG_CCA1 | + MT_EXT_CCA_CFG_CCA2 | + MT_EXT_CCA_CFG_CCA3 | + MT_EXT_CCA_CFG_CCA_MASK), + ext_cca_chan[ch_group_index]); + + ret = mt76x2_mcu_set_channel(dev, channel, bw, bw_index, scan); + if (ret) + return ret; + + mt76x2_mcu_init_gain(dev, channel, dev->cal.rx.mcu_gain, true); + + /* Enable LDPC Rx */ + if (mt76xx_rev(dev) >= MT76XX_REV_E3) + mt76_set(dev, MT_BBP(RXO, 13), BIT(10)); + + if (!dev->cal.init_cal_done) { + u8 val = mt76x02_eeprom_get(dev, MT_EE_BT_RCAL_RESULT); + + if (val != 0xff) + mt76x02_mcu_calibrate(dev, MCU_CAL_R, 0); + } + + mt76x02_mcu_calibrate(dev, MCU_CAL_RXDCOC, channel); + + /* Rx LPF calibration */ + if (!dev->cal.init_cal_done) + mt76x02_mcu_calibrate(dev, MCU_CAL_RC, 0); + dev->cal.init_cal_done = true; + + mt76_wr(dev, MT_BBP(AGC, 61), 0xff64a4e2); + mt76_wr(dev, MT_BBP(AGC, 7), 0x08081010); + mt76_wr(dev, MT_BBP(AGC, 11), 0x00000404); + mt76_wr(dev, MT_BBP(AGC, 2), 0x00007070); + mt76_wr(dev, MT_TXOP_CTRL_CFG, 0X04101b3f); + + mt76_set(dev, MT_BBP(TXO, 4), BIT(25)); + mt76_set(dev, MT_BBP(RXO, 13), BIT(8)); + + if (scan) + return 0; + + mt76x2u_phy_channel_calibrate(dev, true); + mt76x02_init_agc_gain(dev); + + if (mt76x2_tssi_enabled(dev)) { + /* init default values for temp compensation */ + mt76_rmw_field(dev, MT_TX_ALC_CFG_1, MT_TX_ALC_CFG_1_TEMP_COMP, + 0x38); + mt76_rmw_field(dev, MT_TX_ALC_CFG_2, MT_TX_ALC_CFG_2_TEMP_COMP, + 0x38); + + /* init tssi calibration */ + if (!mt76x2_channel_silent(dev)) { + struct ieee80211_channel *chan; + u32 flag = 0; + + chan = dev->mphy.chandef.chan; + if (chan->band == NL80211_BAND_5GHZ) + flag |= BIT(0); + if (mt76x02_ext_pa_enabled(dev, chan->band)) + flag |= BIT(8); + mt76x02_mcu_calibrate(dev, MCU_CAL_TSSI, flag); + dev->cal.tssi_cal_done = true; + } + } + + ieee80211_queue_delayed_work(mt76_hw(dev), &dev->cal_work, + MT_CALIBRATE_INTERVAL); + return 0; +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/Kconfig b/drivers/net/wireless/mediatek/mt76/mt7915/Kconfig new file mode 100644 index 000000000..c24be8227 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7915/Kconfig @@ -0,0 +1,25 @@ +# SPDX-License-Identifier: BSD-3-Clause-Clear +config MT7915E + tristate "MediaTek MT7915E (PCIe) support" + select MT76_CONNAC_LIB + select WANT_DEV_COREDUMP + depends on MAC80211 + depends on PCI + select RELAY + help + This adds support for MT7915-based PCIe wireless devices, + which support concurrent dual-band operation at both 5GHz + and 2.4GHz IEEE 802.11ax 4x4:4SS 1024-QAM, 160MHz channels, + OFDMA, spatial reuse and dual carrier modulation. + + To compile this driver as a module, choose M here. + +config MT798X_WMAC + bool "MT798x (SoC) WMAC support" + depends on MT7915E + depends on ARCH_MEDIATEK || COMPILE_TEST + select REGMAP + help + This adds support for the built-in WMAC on MT7981 and MT7986 SoC device + which has the same feature set as a MT7915, but enables 6E + support. diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/Makefile b/drivers/net/wireless/mediatek/mt76/mt7915/Makefile new file mode 100644 index 000000000..963fb3109 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7915/Makefile @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: BSD-3-Clause-Clear + +obj-$(CONFIG_MT7915E) += mt7915e.o + +mt7915e-y := pci.o init.o dma.o eeprom.o main.o mcu.o mac.o \ + debugfs.o mmio.o + +mt7915e-$(CONFIG_NL80211_TESTMODE) += testmode.o +mt7915e-$(CONFIG_MT798X_WMAC) += soc.o +mt7915e-$(CONFIG_DEV_COREDUMP) += coredump.o diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/coredump.c b/drivers/net/wireless/mediatek/mt76/mt7915/coredump.c new file mode 100644 index 000000000..6c7273e5b --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7915/coredump.c @@ -0,0 +1,411 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2022 MediaTek Inc. */ + +#include +#include +#include +#include +#include "coredump.h" + +static bool coredump_memdump; +module_param(coredump_memdump, bool, 0644); +MODULE_PARM_DESC(coredump_memdump, "Optional ability to dump firmware memory"); + +static const struct mt7915_mem_region mt7915_mem_regions[] = { + { + .start = 0xe003b400, + .len = 0x00003bff, + .name = "CRAM", + }, +}; + +static const struct mt7915_mem_region mt7916_mem_regions[] = { + { + .start = 0x00800000, + .len = 0x0005ffff, + .name = "ROM", + }, + { + .start = 0x00900000, + .len = 0x00013fff, + .name = "ULM1", + }, + { + .start = 0x02200000, + .len = 0x0004ffff, + .name = "ULM2", + }, + { + .start = 0x02300000, + .len = 0x0004ffff, + .name = "ULM3", + }, + { + .start = 0x00400000, + .len = 0x00027fff, + .name = "SRAM", + }, + { + .start = 0xe0000000, + .len = 0x00157fff, + .name = "CRAM", + }, +}; + +static const struct mt7915_mem_region mt798x_mem_regions[] = { + { + .start = 0x00800000, + .len = 0x0005ffff, + .name = "ROM", + }, + { + .start = 0x00900000, + .len = 0x0000ffff, + .name = "ULM1", + }, + { + .start = 0x02200000, + .len = 0x0004ffff, + .name = "ULM2", + }, + { + .start = 0x02300000, + .len = 0x0004ffff, + .name = "ULM3", + }, + { + .start = 0x00400000, + .len = 0x00017fff, + .name = "SRAM", + }, + { + .start = 0xe0000000, + .len = 0x00113fff, + .name = "CRAM", + }, +}; + +const struct mt7915_mem_region* +mt7915_coredump_get_mem_layout(struct mt7915_dev *dev, u32 *num) +{ + switch (mt76_chip(&dev->mt76)) { + case 0x7915: + *num = ARRAY_SIZE(mt7915_mem_regions); + return &mt7915_mem_regions[0]; + case 0x7981: + case 0x7986: + *num = ARRAY_SIZE(mt798x_mem_regions); + return &mt798x_mem_regions[0]; + case 0x7916: + *num = ARRAY_SIZE(mt7916_mem_regions); + return &mt7916_mem_regions[0]; + default: + return NULL; + } +} + +static int mt7915_coredump_get_mem_size(struct mt7915_dev *dev) +{ + const struct mt7915_mem_region *mem_region; + size_t size = 0; + u32 num; + int i; + + mem_region = mt7915_coredump_get_mem_layout(dev, &num); + if (!mem_region) + return 0; + + for (i = 0; i < num; i++) { + size += mem_region->len; + mem_region++; + } + + /* reserve space for the headers */ + size += num * sizeof(struct mt7915_mem_hdr); + /* make sure it is aligned 4 bytes for debug message print out */ + size = ALIGN(size, 4); + + return size; +} + +struct mt7915_crash_data *mt7915_coredump_new(struct mt7915_dev *dev) +{ + struct mt7915_crash_data *crash_data = dev->coredump.crash_data; + + lockdep_assert_held(&dev->dump_mutex); + + guid_gen(&crash_data->guid); + ktime_get_real_ts64(&crash_data->timestamp); + + return crash_data; +} + +static void +mt7915_coredump_fw_state(struct mt7915_dev *dev, struct mt7915_coredump *dump, + bool *exception) +{ + u32 state, count, type; + + type = (u32)mt76_get_field(dev, MT_FW_EXCEPT_TYPE, GENMASK(7, 0)); + state = (u32)mt76_get_field(dev, MT_FW_ASSERT_STAT, GENMASK(7, 0)); + count = is_mt7915(&dev->mt76) ? + (u32)mt76_get_field(dev, MT_FW_EXCEPT_COUNT, GENMASK(15, 8)) : + (u32)mt76_get_field(dev, MT_FW_EXCEPT_COUNT, GENMASK(7, 0)); + + /* normal mode: driver can manually trigger assert for detail info */ + if (!count) + strscpy(dump->fw_state, "normal", sizeof(dump->fw_state)); + else if (state > 1 && (count == 1) && type == 5) + strscpy(dump->fw_state, "assert", sizeof(dump->fw_state)); + else if ((state > 1 && count == 1) || count > 1) + strscpy(dump->fw_state, "exception", sizeof(dump->fw_state)); + + *exception = !!count; +} + +static void +mt7915_coredump_fw_trace(struct mt7915_dev *dev, struct mt7915_coredump *dump, + bool exception) +{ + u32 n, irq, sch, base = MT_FW_EINT_INFO; + + /* trap or run? */ + dump->last_msg_id = mt76_rr(dev, MT_FW_LAST_MSG_ID); + + n = is_mt7915(&dev->mt76) ? + (u32)mt76_get_field(dev, base, GENMASK(7, 0)) : + (u32)mt76_get_field(dev, base, GENMASK(15, 8)); + dump->eint_info_idx = n; + + irq = mt76_rr(dev, base + 0x8); + n = is_mt7915(&dev->mt76) ? + FIELD_GET(GENMASK(7, 0), irq) : FIELD_GET(GENMASK(23, 16), irq); + dump->irq_info_idx = n; + + sch = mt76_rr(dev, MT_FW_SCHED_INFO); + n = is_mt7915(&dev->mt76) ? + FIELD_GET(GENMASK(7, 0), sch) : FIELD_GET(GENMASK(15, 8), sch); + dump->sched_info_idx = n; + + if (exception) { + u32 i, y; + + /* sched trace */ + n = is_mt7915(&dev->mt76) ? + FIELD_GET(GENMASK(15, 8), sch) : FIELD_GET(GENMASK(7, 0), sch); + n = n > 60 ? 60 : n; + + strscpy(dump->trace_sched, "(sched_info) id, time", + sizeof(dump->trace_sched)); + + for (y = dump->sched_info_idx, i = 0; i < n; i++, y++) { + mt7915_memcpy_fromio(dev, dump->sched, base + 0xc + y * 12, + sizeof(dump->sched)); + y = y >= n ? 0 : y; + } + + /* irq trace */ + n = is_mt7915(&dev->mt76) ? + FIELD_GET(GENMASK(15, 8), irq) : FIELD_GET(GENMASK(7, 0), irq); + n = n > 60 ? 60 : n; + + strscpy(dump->trace_irq, "(irq_info) id, time", + sizeof(dump->trace_irq)); + + for (y = dump->irq_info_idx, i = 0; i < n; i++, y++) { + mt7915_memcpy_fromio(dev, dump->irq, base + 0x4 + y * 16, + sizeof(dump->irq)); + y = y >= n ? 0 : y; + } + } +} + +static void +mt7915_coredump_fw_stack(struct mt7915_dev *dev, struct mt7915_coredump *dump, + bool exception) +{ + u32 oldest, i, idx; + + /* stop call stack record */ + if (!exception) + mt76_clear(dev, 0x89050200, BIT(0)); + + oldest = (u32)mt76_get_field(dev, 0x89050200, GENMASK(20, 16)) + 2; + for (i = 0; i < 16; i++) { + idx = ((oldest + 2 * i + 1) % 32); + dump->call_stack[i] = mt76_rr(dev, 0x89050204 + idx * 4); + } + + /* start call stack record */ + if (!exception) + mt76_set(dev, 0x89050200, BIT(0)); +} + +static void +mt7915_coredump_fw_task(struct mt7915_dev *dev, struct mt7915_coredump *dump) +{ + u32 offs = is_mt7915(&dev->mt76) ? 0xe0 : 0x170; + + strscpy(dump->task_qid, "(task queue id) read, write", + sizeof(dump->task_qid)); + + dump->taskq[0].read = mt76_rr(dev, MT_FW_TASK_QID1); + dump->taskq[0].write = mt76_rr(dev, MT_FW_TASK_QID1 - 4); + dump->taskq[1].read = mt76_rr(dev, MT_FW_TASK_QID2); + dump->taskq[1].write = mt76_rr(dev, MT_FW_TASK_QID2 - 4); + + strscpy(dump->task_info, "(task stack) start, end, size", + sizeof(dump->task_info)); + + dump->taski[0].start = mt76_rr(dev, MT_FW_TASK_START); + dump->taski[0].end = mt76_rr(dev, MT_FW_TASK_END); + dump->taski[0].size = mt76_rr(dev, MT_FW_TASK_SIZE); + dump->taski[1].start = mt76_rr(dev, MT_FW_TASK_START + offs); + dump->taski[1].end = mt76_rr(dev, MT_FW_TASK_END + offs); + dump->taski[1].size = mt76_rr(dev, MT_FW_TASK_SIZE + offs); +} + +static void +mt7915_coredump_fw_context(struct mt7915_dev *dev, struct mt7915_coredump *dump) +{ + u32 count, idx, id; + + count = mt76_rr(dev, MT_FW_CIRQ_COUNT); + + /* current context */ + if (!count) { + strscpy(dump->fw_context, "(context) interrupt", + sizeof(dump->fw_context)); + + idx = is_mt7915(&dev->mt76) ? + (u32)mt76_get_field(dev, MT_FW_CIRQ_IDX, GENMASK(31, 16)) : + (u32)mt76_get_field(dev, MT_FW_CIRQ_IDX, GENMASK(15, 0)); + dump->context.idx = idx; + dump->context.handler = mt76_rr(dev, MT_FW_CIRQ_LISR); + } else { + idx = mt76_rr(dev, MT_FW_TASK_IDX); + id = mt76_rr(dev, MT_FW_TASK_ID); + + if (!id && idx == 3) { + strscpy(dump->fw_context, "(context) idle", + sizeof(dump->fw_context)); + } else if (id && idx != 3) { + strscpy(dump->fw_context, "(context) task", + sizeof(dump->fw_context)); + + dump->context.idx = idx; + dump->context.handler = id; + } + } +} + +static struct mt7915_coredump *mt7915_coredump_build(struct mt7915_dev *dev) +{ + struct mt7915_crash_data *crash_data = dev->coredump.crash_data; + struct mt7915_coredump *dump; + struct mt7915_coredump_mem *dump_mem; + size_t len, sofar = 0, hdr_len = sizeof(*dump); + unsigned char *buf; + bool exception; + + len = hdr_len; + + if (coredump_memdump && crash_data->memdump_buf_len) + len += sizeof(*dump_mem) + crash_data->memdump_buf_len; + + sofar += hdr_len; + + /* this is going to get big when we start dumping memory and such, + * so go ahead and use vmalloc. + */ + buf = vzalloc(len); + if (!buf) + return NULL; + + mutex_lock(&dev->dump_mutex); + + dump = (struct mt7915_coredump *)(buf); + dump->len = len; + + /* plain text */ + strscpy(dump->magic, "mt76-crash-dump", sizeof(dump->magic)); + strscpy(dump->kernel, init_utsname()->release, sizeof(dump->kernel)); + strscpy(dump->fw_ver, dev->mt76.hw->wiphy->fw_version, + sizeof(dump->fw_ver)); + + guid_copy(&dump->guid, &crash_data->guid); + dump->tv_sec = crash_data->timestamp.tv_sec; + dump->tv_nsec = crash_data->timestamp.tv_nsec; + dump->device_id = mt76_chip(&dev->mt76); + + mt7915_coredump_fw_state(dev, dump, &exception); + mt7915_coredump_fw_trace(dev, dump, exception); + mt7915_coredump_fw_task(dev, dump); + mt7915_coredump_fw_context(dev, dump); + mt7915_coredump_fw_stack(dev, dump, exception); + + /* gather memory content */ + dump_mem = (struct mt7915_coredump_mem *)(buf + sofar); + dump_mem->len = crash_data->memdump_buf_len; + if (coredump_memdump && crash_data->memdump_buf_len) + memcpy(dump_mem->data, crash_data->memdump_buf, + crash_data->memdump_buf_len); + + mutex_unlock(&dev->dump_mutex); + + return dump; +} + +int mt7915_coredump_submit(struct mt7915_dev *dev) +{ + struct mt7915_coredump *dump; + + dump = mt7915_coredump_build(dev); + if (!dump) { + dev_warn(dev->mt76.dev, "no crash dump data found\n"); + return -ENODATA; + } + + dev_coredumpv(dev->mt76.dev, dump, dump->len, GFP_KERNEL); + + return 0; +} + +int mt7915_coredump_register(struct mt7915_dev *dev) +{ + struct mt7915_crash_data *crash_data; + + crash_data = vzalloc(sizeof(*dev->coredump.crash_data)); + if (!crash_data) + return -ENOMEM; + + dev->coredump.crash_data = crash_data; + + if (coredump_memdump) { + crash_data->memdump_buf_len = mt7915_coredump_get_mem_size(dev); + if (!crash_data->memdump_buf_len) + /* no memory content */ + return 0; + + crash_data->memdump_buf = vzalloc(crash_data->memdump_buf_len); + if (!crash_data->memdump_buf) { + vfree(crash_data); + return -ENOMEM; + } + } + + return 0; +} + +void mt7915_coredump_unregister(struct mt7915_dev *dev) +{ + if (dev->coredump.crash_data->memdump_buf) { + vfree(dev->coredump.crash_data->memdump_buf); + dev->coredump.crash_data->memdump_buf = NULL; + dev->coredump.crash_data->memdump_buf_len = 0; + } + + vfree(dev->coredump.crash_data); + dev->coredump.crash_data = NULL; +} + diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/coredump.h b/drivers/net/wireless/mediatek/mt76/mt7915/coredump.h new file mode 100644 index 000000000..eb3991eda --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7915/coredump.h @@ -0,0 +1,136 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* Copyright (C) 2022 MediaTek Inc. */ + +#ifndef _COREDUMP_H_ +#define _COREDUMP_H_ + +#include "mt7915.h" + +struct trace { + u32 id; + u32 timestamp; +}; + +struct mt7915_coredump { + char magic[16]; + + u32 len; + + guid_t guid; + + /* time-of-day stamp */ + u64 tv_sec; + /* time-of-day stamp, nano-seconds */ + u64 tv_nsec; + /* kernel version */ + char kernel[64]; + /* firmware version */ + char fw_ver[ETHTOOL_FWVERS_LEN]; + + u32 device_id; + + /* exception state */ + char fw_state[12]; + + u32 last_msg_id; + u32 eint_info_idx; + u32 irq_info_idx; + u32 sched_info_idx; + + /* schedule info */ + char trace_sched[32]; + struct { + struct trace t; + u32 pc; + } sched[60]; + + /* irq info */ + char trace_irq[32]; + struct trace irq[60]; + + /* task queue status */ + char task_qid[32]; + struct { + u32 read; + u32 write; + } taskq[2]; + + /* task stack info */ + char task_info[32]; + struct { + u32 start; + u32 end; + u32 size; + } taski[2]; + + /* firmware context */ + char fw_context[24]; + struct { + u32 idx; + u32 handler; + } context; + + /* link registers calltrace */ + u32 call_stack[16]; + + /* memory content */ + u8 data[]; +} __packed; + +struct mt7915_coredump_mem { + u32 len; + u8 data[]; +} __packed; + +struct mt7915_mem_hdr { + u32 start; + u32 len; + u8 data[]; +}; + +struct mt7915_mem_region { + u32 start; + size_t len; + + const char *name; +}; + +#ifdef CONFIG_DEV_COREDUMP + +const struct mt7915_mem_region * +mt7915_coredump_get_mem_layout(struct mt7915_dev *dev, u32 *num); +struct mt7915_crash_data *mt7915_coredump_new(struct mt7915_dev *dev); +int mt7915_coredump_submit(struct mt7915_dev *dev); +int mt7915_coredump_register(struct mt7915_dev *dev); +void mt7915_coredump_unregister(struct mt7915_dev *dev); + +#else /* CONFIG_DEV_COREDUMP */ + +static inline const struct mt7915_mem_region * +mt7915_coredump_get_mem_layout(struct mt7915_dev *dev, u32 *num) +{ + return NULL; +} + +static inline int mt7915_coredump_submit(struct mt7915_dev *dev) +{ + return 0; +} + +static inline struct mt7915_crash_data *mt7915_coredump_new(struct mt7915_dev *dev) +{ + return NULL; +} + +static inline int mt7915_coredump_register(struct mt7915_dev *dev) +{ + return 0; +} + +static inline void mt7915_coredump_unregister(struct mt7915_dev *dev) +{ +} + +#endif /* CONFIG_DEV_COREDUMP */ + +#endif /* _COREDUMP_H_ */ diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c new file mode 100644 index 000000000..0413b4fd2 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c @@ -0,0 +1,1475 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2020 MediaTek Inc. */ + +#include +#include "mt7915.h" +#include "eeprom.h" +#include "mcu.h" +#include "mac.h" + +#define FW_BIN_LOG_MAGIC 0x44e98caf + +/** global debugfs **/ + +struct hw_queue_map { + const char *name; + u8 index; + u8 pid; + u8 qid; +}; + +static int +mt7915_implicit_txbf_set(void *data, u64 val) +{ + struct mt7915_dev *dev = data; + + /* The existing connected stations shall reconnect to apply + * new implicit txbf configuration. + */ + dev->ibf = !!val; + + return mt7915_mcu_set_txbf(dev, MT_BF_TYPE_UPDATE); +} + +static int +mt7915_implicit_txbf_get(void *data, u64 *val) +{ + struct mt7915_dev *dev = data; + + *val = dev->ibf; + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_implicit_txbf, mt7915_implicit_txbf_get, + mt7915_implicit_txbf_set, "%lld\n"); + +/* test knob of system error recovery */ +static ssize_t +mt7915_sys_recovery_set(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct mt7915_phy *phy = file->private_data; + struct mt7915_dev *dev = phy->dev; + bool band = phy->mt76->band_idx; + int ret = 0; + u16 val; + + ret = kstrtou16_from_user(user_buf, count, 0, &val); + if (ret) + return ret; + + switch (val) { + /* + * 0: grab firmware current SER state. + * 1: trigger & enable system error L1 recovery. + * 2: trigger & enable system error L2 recovery. + * 3: trigger & enable system error L3 rx abort. + * 4: trigger & enable system error L3 tx abort + * 5: trigger & enable system error L3 tx disable. + * 6: trigger & enable system error L3 bf recovery. + * 7: trigger & enable system error full recovery. + * 8: trigger firmware crash. + */ + case SER_QUERY: + ret = mt7915_mcu_set_ser(dev, 0, 0, band); + break; + case SER_SET_RECOVER_L1: + case SER_SET_RECOVER_L2: + case SER_SET_RECOVER_L3_RX_ABORT: + case SER_SET_RECOVER_L3_TX_ABORT: + case SER_SET_RECOVER_L3_TX_DISABLE: + case SER_SET_RECOVER_L3_BF: + ret = mt7915_mcu_set_ser(dev, SER_ENABLE, BIT(val), band); + if (ret) + return ret; + + ret = mt7915_mcu_set_ser(dev, SER_RECOVER, val, band); + break; + + /* enable full chip reset */ + case SER_SET_RECOVER_FULL: + mt76_set(dev, MT_WFDMA0_MCU_HOST_INT_ENA, MT_MCU_CMD_WDT_MASK); + ret = mt7915_mcu_set_ser(dev, 1, 3, band); + if (ret) + return ret; + + dev->recovery.state |= MT_MCU_CMD_WDT_MASK; + mt7915_reset(dev); + break; + + /* WARNING: trigger firmware crash */ + case SER_SET_SYSTEM_ASSERT: + mt76_wr(dev, MT_MCU_WM_CIRQ_EINT_MASK_CLR_ADDR, BIT(18)); + mt76_wr(dev, MT_MCU_WM_CIRQ_EINT_SOFT_ADDR, BIT(18)); + break; + default: + break; + } + + return ret ? ret : count; +} + +static ssize_t +mt7915_sys_recovery_get(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct mt7915_phy *phy = file->private_data; + struct mt7915_dev *dev = phy->dev; + char *buff; + int desc = 0; + ssize_t ret; + static const size_t bufsz = 1024; + + buff = kmalloc(bufsz, GFP_KERNEL); + if (!buff) + return -ENOMEM; + + /* HELP */ + desc += scnprintf(buff + desc, bufsz - desc, + "Please echo the correct value ...\n"); + desc += scnprintf(buff + desc, bufsz - desc, + "0: grab firmware transient SER state\n"); + desc += scnprintf(buff + desc, bufsz - desc, + "1: trigger system error L1 recovery\n"); + desc += scnprintf(buff + desc, bufsz - desc, + "2: trigger system error L2 recovery\n"); + desc += scnprintf(buff + desc, bufsz - desc, + "3: trigger system error L3 rx abort\n"); + desc += scnprintf(buff + desc, bufsz - desc, + "4: trigger system error L3 tx abort\n"); + desc += scnprintf(buff + desc, bufsz - desc, + "5: trigger system error L3 tx disable\n"); + desc += scnprintf(buff + desc, bufsz - desc, + "6: trigger system error L3 bf recovery\n"); + desc += scnprintf(buff + desc, bufsz - desc, + "7: trigger system error full recovery\n"); + desc += scnprintf(buff + desc, bufsz - desc, + "8: trigger firmware crash\n"); + + /* SER statistics */ + desc += scnprintf(buff + desc, bufsz - desc, + "\nlet's dump firmware SER statistics...\n"); + desc += scnprintf(buff + desc, bufsz - desc, + "::E R , SER_STATUS = 0x%08x\n", + mt76_rr(dev, MT_SWDEF_SER_STATS)); + desc += scnprintf(buff + desc, bufsz - desc, + "::E R , SER_PLE_ERR = 0x%08x\n", + mt76_rr(dev, MT_SWDEF_PLE_STATS)); + desc += scnprintf(buff + desc, bufsz - desc, + "::E R , SER_PLE_ERR_1 = 0x%08x\n", + mt76_rr(dev, MT_SWDEF_PLE1_STATS)); + desc += scnprintf(buff + desc, bufsz - desc, + "::E R , SER_PLE_ERR_AMSDU = 0x%08x\n", + mt76_rr(dev, MT_SWDEF_PLE_AMSDU_STATS)); + desc += scnprintf(buff + desc, bufsz - desc, + "::E R , SER_PSE_ERR = 0x%08x\n", + mt76_rr(dev, MT_SWDEF_PSE_STATS)); + desc += scnprintf(buff + desc, bufsz - desc, + "::E R , SER_PSE_ERR_1 = 0x%08x\n", + mt76_rr(dev, MT_SWDEF_PSE1_STATS)); + desc += scnprintf(buff + desc, bufsz - desc, + "::E R , SER_LMAC_WISR6_B0 = 0x%08x\n", + mt76_rr(dev, MT_SWDEF_LAMC_WISR6_BN0_STATS)); + desc += scnprintf(buff + desc, bufsz - desc, + "::E R , SER_LMAC_WISR6_B1 = 0x%08x\n", + mt76_rr(dev, MT_SWDEF_LAMC_WISR6_BN1_STATS)); + desc += scnprintf(buff + desc, bufsz - desc, + "::E R , SER_LMAC_WISR7_B0 = 0x%08x\n", + mt76_rr(dev, MT_SWDEF_LAMC_WISR7_BN0_STATS)); + desc += scnprintf(buff + desc, bufsz - desc, + "::E R , SER_LMAC_WISR7_B1 = 0x%08x\n", + mt76_rr(dev, MT_SWDEF_LAMC_WISR7_BN1_STATS)); + desc += scnprintf(buff + desc, bufsz - desc, + "\nSYS_RESET_COUNT: WM %d, WA %d\n", + dev->recovery.wm_reset_count, + dev->recovery.wa_reset_count); + + ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc); + kfree(buff); + return ret; +} + +static const struct file_operations mt7915_sys_recovery_ops = { + .write = mt7915_sys_recovery_set, + .read = mt7915_sys_recovery_get, + .open = simple_open, + .llseek = default_llseek, +}; + +static int +mt7915_radar_trigger(void *data, u64 val) +{ +#define RADAR_MAIN_CHAIN 1 +#define RADAR_BACKGROUND 2 + struct mt7915_phy *phy = data; + struct mt7915_dev *dev = phy->dev; + int rdd_idx; + + if (!val || val > RADAR_BACKGROUND) + return -EINVAL; + + if (val == RADAR_BACKGROUND && !dev->rdd2_phy) { + dev_err(dev->mt76.dev, "Background radar is not enabled\n"); + return -EINVAL; + } + + rdd_idx = mt7915_get_rdd_idx(phy, val == RADAR_BACKGROUND); + if (rdd_idx < 0) { + dev_err(dev->mt76.dev, "No RDD found\n"); + return -EINVAL; + } + + return mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_RADAR_EMULATE, + rdd_idx, 0, 0); +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_radar_trigger, NULL, + mt7915_radar_trigger, "%lld\n"); + +static int +mt7915_muru_debug_set(void *data, u64 val) +{ + struct mt7915_dev *dev = data; + + dev->muru_debug = val; + mt7915_mcu_muru_debug_set(dev, dev->muru_debug); + + return 0; +} + +static int +mt7915_muru_debug_get(void *data, u64 *val) +{ + struct mt7915_dev *dev = data; + + *val = dev->muru_debug; + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_muru_debug, mt7915_muru_debug_get, + mt7915_muru_debug_set, "%lld\n"); + +static int mt7915_muru_stats_show(struct seq_file *file, void *data) +{ + struct mt7915_phy *phy = file->private; + struct mt7915_dev *dev = phy->dev; + static const char * const dl_non_he_type[] = { + "CCK", "OFDM", "HT MIX", "HT GF", + "VHT SU", "VHT 2MU", "VHT 3MU", "VHT 4MU" + }; + static const char * const dl_he_type[] = { + "HE SU", "HE EXT", "HE 2MU", "HE 3MU", "HE 4MU", + "HE 2RU", "HE 3RU", "HE 4RU", "HE 5-8RU", "HE 9-16RU", + "HE >16RU" + }; + static const char * const ul_he_type[] = { + "HE 2MU", "HE 3MU", "HE 4MU", "HE SU", "HE 2RU", + "HE 3RU", "HE 4RU", "HE 5-8RU", "HE 9-16RU", "HE >16RU" + }; + int ret, i; + u64 total_ppdu_cnt, sub_total_cnt; + + if (!dev->muru_debug) { + seq_puts(file, "Please enable muru_debug first.\n"); + return 0; + } + + mutex_lock(&dev->mt76.mutex); + + ret = mt7915_mcu_muru_debug_get(phy); + if (ret) + goto exit; + + /* Non-HE Downlink*/ + seq_puts(file, "[Non-HE]\nDownlink\nData Type: "); + + for (i = 0; i < 5; i++) + seq_printf(file, "%8s | ", dl_non_he_type[i]); + + seq_puts(file, "\nTotal Count:"); + seq_printf(file, "%8u | %8u | %8u | %8u | %8u | ", + phy->mib.dl_cck_cnt, + phy->mib.dl_ofdm_cnt, + phy->mib.dl_htmix_cnt, + phy->mib.dl_htgf_cnt, + phy->mib.dl_vht_su_cnt); + + seq_puts(file, "\nDownlink MU-MIMO\nData Type: "); + + for (i = 5; i < 8; i++) + seq_printf(file, "%8s | ", dl_non_he_type[i]); + + seq_puts(file, "\nTotal Count:"); + seq_printf(file, "%8u | %8u | %8u | ", + phy->mib.dl_vht_2mu_cnt, + phy->mib.dl_vht_3mu_cnt, + phy->mib.dl_vht_4mu_cnt); + + sub_total_cnt = (u64)phy->mib.dl_vht_2mu_cnt + + phy->mib.dl_vht_3mu_cnt + + phy->mib.dl_vht_4mu_cnt; + + seq_printf(file, "\nTotal non-HE MU-MIMO DL PPDU count: %lld", + sub_total_cnt); + + total_ppdu_cnt = sub_total_cnt + + phy->mib.dl_cck_cnt + + phy->mib.dl_ofdm_cnt + + phy->mib.dl_htmix_cnt + + phy->mib.dl_htgf_cnt + + phy->mib.dl_vht_su_cnt; + + seq_printf(file, "\nAll non-HE DL PPDU count: %lld", total_ppdu_cnt); + + /* HE Downlink */ + seq_puts(file, "\n\n[HE]\nDownlink\nData Type: "); + + for (i = 0; i < 2; i++) + seq_printf(file, "%8s | ", dl_he_type[i]); + + seq_puts(file, "\nTotal Count:"); + seq_printf(file, "%8u | %8u | ", + phy->mib.dl_he_su_cnt, phy->mib.dl_he_ext_su_cnt); + + seq_puts(file, "\nDownlink MU-MIMO\nData Type: "); + + for (i = 2; i < 5; i++) + seq_printf(file, "%8s | ", dl_he_type[i]); + + seq_puts(file, "\nTotal Count:"); + seq_printf(file, "%8u | %8u | %8u | ", + phy->mib.dl_he_2mu_cnt, phy->mib.dl_he_3mu_cnt, + phy->mib.dl_he_4mu_cnt); + + seq_puts(file, "\nDownlink OFDMA\nData Type: "); + + for (i = 5; i < 11; i++) + seq_printf(file, "%8s | ", dl_he_type[i]); + + seq_puts(file, "\nTotal Count:"); + seq_printf(file, "%8u | %8u | %8u | %8u | %9u | %8u | ", + phy->mib.dl_he_2ru_cnt, + phy->mib.dl_he_3ru_cnt, + phy->mib.dl_he_4ru_cnt, + phy->mib.dl_he_5to8ru_cnt, + phy->mib.dl_he_9to16ru_cnt, + phy->mib.dl_he_gtr16ru_cnt); + + sub_total_cnt = (u64)phy->mib.dl_he_2mu_cnt + + phy->mib.dl_he_3mu_cnt + + phy->mib.dl_he_4mu_cnt; + total_ppdu_cnt = sub_total_cnt; + + seq_printf(file, "\nTotal HE MU-MIMO DL PPDU count: %lld", + sub_total_cnt); + + sub_total_cnt = (u64)phy->mib.dl_he_2ru_cnt + + phy->mib.dl_he_3ru_cnt + + phy->mib.dl_he_4ru_cnt + + phy->mib.dl_he_5to8ru_cnt + + phy->mib.dl_he_9to16ru_cnt + + phy->mib.dl_he_gtr16ru_cnt; + total_ppdu_cnt += sub_total_cnt; + + seq_printf(file, "\nTotal HE OFDMA DL PPDU count: %lld", + sub_total_cnt); + + total_ppdu_cnt += (u64)phy->mib.dl_he_su_cnt + + phy->mib.dl_he_ext_su_cnt; + + seq_printf(file, "\nAll HE DL PPDU count: %lld", total_ppdu_cnt); + + /* HE Uplink */ + seq_puts(file, "\n\nUplink"); + seq_puts(file, "\nTrigger-based Uplink MU-MIMO\nData Type: "); + + for (i = 0; i < 3; i++) + seq_printf(file, "%8s | ", ul_he_type[i]); + + seq_puts(file, "\nTotal Count:"); + seq_printf(file, "%8u | %8u | %8u | ", + phy->mib.ul_hetrig_2mu_cnt, + phy->mib.ul_hetrig_3mu_cnt, + phy->mib.ul_hetrig_4mu_cnt); + + seq_puts(file, "\nTrigger-based Uplink OFDMA\nData Type: "); + + for (i = 3; i < 10; i++) + seq_printf(file, "%8s | ", ul_he_type[i]); + + seq_puts(file, "\nTotal Count:"); + seq_printf(file, "%8u | %8u | %8u | %8u | %8u | %9u | %7u | ", + phy->mib.ul_hetrig_su_cnt, + phy->mib.ul_hetrig_2ru_cnt, + phy->mib.ul_hetrig_3ru_cnt, + phy->mib.ul_hetrig_4ru_cnt, + phy->mib.ul_hetrig_5to8ru_cnt, + phy->mib.ul_hetrig_9to16ru_cnt, + phy->mib.ul_hetrig_gtr16ru_cnt); + + sub_total_cnt = (u64)phy->mib.ul_hetrig_2mu_cnt + + phy->mib.ul_hetrig_3mu_cnt + + phy->mib.ul_hetrig_4mu_cnt; + total_ppdu_cnt = sub_total_cnt; + + seq_printf(file, "\nTotal HE MU-MIMO UL TB PPDU count: %lld", + sub_total_cnt); + + sub_total_cnt = (u64)phy->mib.ul_hetrig_2ru_cnt + + phy->mib.ul_hetrig_3ru_cnt + + phy->mib.ul_hetrig_4ru_cnt + + phy->mib.ul_hetrig_5to8ru_cnt + + phy->mib.ul_hetrig_9to16ru_cnt + + phy->mib.ul_hetrig_gtr16ru_cnt; + total_ppdu_cnt += sub_total_cnt; + + seq_printf(file, "\nTotal HE OFDMA UL TB PPDU count: %lld", + sub_total_cnt); + + total_ppdu_cnt += phy->mib.ul_hetrig_su_cnt; + + seq_printf(file, "\nAll HE UL TB PPDU count: %lld\n", total_ppdu_cnt); + +exit: + mutex_unlock(&dev->mt76.mutex); + + return ret; +} +DEFINE_SHOW_ATTRIBUTE(mt7915_muru_stats); + +static int +mt7915_rdd_monitor(struct seq_file *s, void *data) +{ + struct mt7915_dev *dev = dev_get_drvdata(s->private); + struct cfg80211_chan_def *chandef = &dev->rdd2_chandef; + const char *bw; + int ret = 0; + + mutex_lock(&dev->mt76.mutex); + + if (!mt7915_eeprom_has_background_radar(dev)) { + seq_puts(s, "no background radar capability\n"); + goto out; + } + + if (!cfg80211_chandef_valid(chandef)) { + ret = -EINVAL; + goto out; + } + + if (!dev->rdd2_phy) { + seq_puts(s, "not running\n"); + goto out; + } + + switch (chandef->width) { + case NL80211_CHAN_WIDTH_40: + bw = "40"; + break; + case NL80211_CHAN_WIDTH_80: + bw = "80"; + break; + case NL80211_CHAN_WIDTH_160: + bw = "160"; + break; + case NL80211_CHAN_WIDTH_80P80: + bw = "80P80"; + break; + default: + bw = "20"; + break; + } + + seq_printf(s, "channel %d (%d MHz) width %s MHz center1: %d MHz\n", + chandef->chan->hw_value, chandef->chan->center_freq, + bw, chandef->center_freq1); +out: + mutex_unlock(&dev->mt76.mutex); + + return ret; +} + +static int +mt7915_fw_debug_wm_set(void *data, u64 val) +{ + struct mt7915_dev *dev = data; + enum { + DEBUG_TXCMD = 62, + DEBUG_CMD_RPT_TX, + DEBUG_CMD_RPT_TRIG, + DEBUG_SPL, + DEBUG_RPT_RX, + } debug; + bool tx, rx, en; + int ret; + + dev->fw.debug_wm = val ? MCU_FW_LOG_TO_HOST : 0; + + if (dev->fw.debug_bin) + val = 16; + else + val = dev->fw.debug_wm; + + tx = dev->fw.debug_wm || (dev->fw.debug_bin & BIT(1)); + rx = dev->fw.debug_wm || (dev->fw.debug_bin & BIT(2)); + en = dev->fw.debug_wm || (dev->fw.debug_bin & BIT(0)); + + ret = mt7915_mcu_fw_log_2_host(dev, MCU_FW_LOG_WM, val); + if (ret) + goto out; + + for (debug = DEBUG_TXCMD; debug <= DEBUG_RPT_RX; debug++) { + if (debug == DEBUG_RPT_RX) + val = en && rx; + else + val = en && tx; + + ret = mt7915_mcu_fw_dbg_ctrl(dev, debug, val); + if (ret) + goto out; + } + + /* WM CPU info record control */ + mt76_clear(dev, MT_CPU_UTIL_CTRL, BIT(0)); + mt76_wr(dev, MT_DIC_CMD_REG_CMD, BIT(2) | BIT(13) | + (dev->fw.debug_wm ? 0 : BIT(0))); + mt76_wr(dev, MT_MCU_WM_CIRQ_IRQ_MASK_CLR_ADDR, BIT(5)); + mt76_wr(dev, MT_MCU_WM_CIRQ_IRQ_SOFT_ADDR, BIT(5)); + +out: + if (ret) + dev->fw.debug_wm = 0; + + return ret; +} + +static int +mt7915_fw_debug_wm_get(void *data, u64 *val) +{ + struct mt7915_dev *dev = data; + + *val = dev->fw.debug_wm; + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_fw_debug_wm, mt7915_fw_debug_wm_get, + mt7915_fw_debug_wm_set, "%lld\n"); + +static int +mt7915_fw_debug_wa_set(void *data, u64 val) +{ + struct mt7915_dev *dev = data; + int ret; + + dev->fw.debug_wa = val ? MCU_FW_LOG_TO_HOST : 0; + + ret = mt7915_mcu_fw_log_2_host(dev, MCU_FW_LOG_WA, dev->fw.debug_wa); + if (ret) + goto out; + + ret = mt7915_mcu_wa_cmd(dev, MCU_WA_PARAM_CMD(SET), + MCU_WA_PARAM_PDMA_RX, !!dev->fw.debug_wa, 0); +out: + if (ret) + dev->fw.debug_wa = 0; + + return ret; +} + +static int +mt7915_fw_debug_wa_get(void *data, u64 *val) +{ + struct mt7915_dev *dev = data; + + *val = dev->fw.debug_wa; + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_fw_debug_wa, mt7915_fw_debug_wa_get, + mt7915_fw_debug_wa_set, "%lld\n"); + +static struct dentry * +create_buf_file_cb(const char *filename, struct dentry *parent, umode_t mode, + struct rchan_buf *buf, int *is_global) +{ + struct dentry *f; + + f = debugfs_create_file("fwlog_data", mode, parent, buf, + &relay_file_operations); + if (IS_ERR(f)) + return NULL; + + *is_global = 1; + + return f; +} + +static int +remove_buf_file_cb(struct dentry *f) +{ + debugfs_remove(f); + + return 0; +} + +static int +mt7915_fw_debug_bin_set(void *data, u64 val) +{ + static struct rchan_callbacks relay_cb = { + .create_buf_file = create_buf_file_cb, + .remove_buf_file = remove_buf_file_cb, + }; + struct mt7915_dev *dev = data; + + if (!dev->relay_fwlog) + dev->relay_fwlog = relay_open("fwlog_data", dev->debugfs_dir, + 1500, 512, &relay_cb, NULL); + if (!dev->relay_fwlog) + return -ENOMEM; + + dev->fw.debug_bin = val; + + relay_reset(dev->relay_fwlog); + + return mt7915_fw_debug_wm_set(dev, dev->fw.debug_wm); +} + +static int +mt7915_fw_debug_bin_get(void *data, u64 *val) +{ + struct mt7915_dev *dev = data; + + *val = dev->fw.debug_bin; + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_fw_debug_bin, mt7915_fw_debug_bin_get, + mt7915_fw_debug_bin_set, "%lld\n"); + +static int +mt7915_fw_util_wm_show(struct seq_file *file, void *data) +{ + struct mt7915_dev *dev = file->private; + + seq_printf(file, "Program counter: 0x%x\n", mt76_rr(dev, MT_WM_MCU_PC)); + + if (dev->fw.debug_wm) { + seq_printf(file, "Busy: %u%% Peak busy: %u%%\n", + mt76_rr(dev, MT_CPU_UTIL_BUSY_PCT), + mt76_rr(dev, MT_CPU_UTIL_PEAK_BUSY_PCT)); + seq_printf(file, "Idle count: %u Peak idle count: %u\n", + mt76_rr(dev, MT_CPU_UTIL_IDLE_CNT), + mt76_rr(dev, MT_CPU_UTIL_PEAK_IDLE_CNT)); + } + + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(mt7915_fw_util_wm); + +static int +mt7915_fw_util_wa_show(struct seq_file *file, void *data) +{ + struct mt7915_dev *dev = file->private; + + seq_printf(file, "Program counter: 0x%x\n", mt76_rr(dev, MT_WA_MCU_PC)); + + if (dev->fw.debug_wa) + return mt7915_mcu_wa_cmd(dev, MCU_WA_PARAM_CMD(QUERY), + MCU_WA_PARAM_CPU_UTIL, 0, 0); + + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(mt7915_fw_util_wa); + +static void +mt7915_ampdu_stat_read_phy(struct mt7915_phy *phy, + struct seq_file *file) +{ + struct mt7915_dev *dev = phy->dev; + bool ext_phy = phy != &dev->phy; + int bound[15], range[4], i; + u8 band = phy->mt76->band_idx; + + /* Tx ampdu stat */ + for (i = 0; i < ARRAY_SIZE(range); i++) + range[i] = mt76_rr(dev, MT_MIB_ARNG(band, i)); + + for (i = 0; i < ARRAY_SIZE(bound); i++) + bound[i] = MT_MIB_ARNCR_RANGE(range[i / 4], i % 4) + 1; + + seq_printf(file, "\nPhy %d, Phy band %d\n", ext_phy, band); + + seq_printf(file, "Length: %8d | ", bound[0]); + for (i = 0; i < ARRAY_SIZE(bound) - 1; i++) + seq_printf(file, "%3d -%3d | ", + bound[i] + 1, bound[i + 1]); + + seq_puts(file, "\nCount: "); + for (i = 0; i < ARRAY_SIZE(bound); i++) + seq_printf(file, "%8d | ", phy->mt76->aggr_stats[i]); + seq_puts(file, "\n"); + + seq_printf(file, "BA miss count: %d\n", phy->mib.ba_miss_cnt); +} + +static void +mt7915_txbf_stat_read_phy(struct mt7915_phy *phy, struct seq_file *s) +{ + struct mt76_mib_stats *mib = &phy->mib; + static const char * const bw[] = { + "BW20", "BW40", "BW80", "BW160" + }; + + /* Tx Beamformer monitor */ + seq_puts(s, "\nTx Beamformer applied PPDU counts: "); + + seq_printf(s, "iBF: %d, eBF: %d\n", + mib->tx_bf_ibf_ppdu_cnt, + mib->tx_bf_ebf_ppdu_cnt); + + /* Tx Beamformer Rx feedback monitor */ + seq_puts(s, "Tx Beamformer Rx feedback statistics: "); + + seq_printf(s, "All: %d, HE: %d, VHT: %d, HT: %d, ", + mib->tx_bf_rx_fb_all_cnt, + mib->tx_bf_rx_fb_he_cnt, + mib->tx_bf_rx_fb_vht_cnt, + mib->tx_bf_rx_fb_ht_cnt); + + seq_printf(s, "%s, NC: %d, NR: %d\n", + bw[mib->tx_bf_rx_fb_bw], + mib->tx_bf_rx_fb_nc_cnt, + mib->tx_bf_rx_fb_nr_cnt); + + /* Tx Beamformee Rx NDPA & Tx feedback report */ + seq_printf(s, "Tx Beamformee successful feedback frames: %d\n", + mib->tx_bf_fb_cpl_cnt); + seq_printf(s, "Tx Beamformee feedback triggered counts: %d\n", + mib->tx_bf_fb_trig_cnt); + + /* Tx SU & MU counters */ + seq_printf(s, "Tx multi-user Beamforming counts: %d\n", + mib->tx_bf_cnt); + seq_printf(s, "Tx multi-user MPDU counts: %d\n", mib->tx_mu_mpdu_cnt); + seq_printf(s, "Tx multi-user successful MPDU counts: %d\n", + mib->tx_mu_acked_mpdu_cnt); + seq_printf(s, "Tx single-user successful MPDU counts: %d\n", + mib->tx_su_acked_mpdu_cnt); + + seq_puts(s, "\n"); +} + +static int +mt7915_tx_stats_show(struct seq_file *file, void *data) +{ + struct mt7915_phy *phy = file->private; + struct mt7915_dev *dev = phy->dev; + struct mt76_mib_stats *mib = &phy->mib; + int i; + + mutex_lock(&dev->mt76.mutex); + + mt7915_ampdu_stat_read_phy(phy, file); + mt7915_mac_update_stats(phy); + mt7915_txbf_stat_read_phy(phy, file); + + /* Tx amsdu info */ + seq_puts(file, "Tx MSDU statistics:\n"); + for (i = 0; i < ARRAY_SIZE(mib->tx_amsdu); i++) { + seq_printf(file, "AMSDU pack count of %d MSDU in TXD: %8d ", + i + 1, mib->tx_amsdu[i]); + if (mib->tx_amsdu_cnt) + seq_printf(file, "(%3d%%)\n", + mib->tx_amsdu[i] * 100 / mib->tx_amsdu_cnt); + else + seq_puts(file, "\n"); + } + + mutex_unlock(&dev->mt76.mutex); + + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(mt7915_tx_stats); + +static void +mt7915_hw_queue_read(struct seq_file *s, u32 size, + const struct hw_queue_map *map) +{ + struct mt7915_phy *phy = s->private; + struct mt7915_dev *dev = phy->dev; + u32 i, val; + + val = mt76_rr(dev, MT_FL_Q_EMPTY); + for (i = 0; i < size; i++) { + u32 ctrl, head, tail, queued; + + if (val & BIT(map[i].index)) + continue; + + ctrl = BIT(31) | (map[i].pid << 10) | ((u32)map[i].qid << 24); + mt76_wr(dev, MT_FL_Q0_CTRL, ctrl); + + head = mt76_get_field(dev, MT_FL_Q2_CTRL, + GENMASK(11, 0)); + tail = mt76_get_field(dev, MT_FL_Q2_CTRL, + GENMASK(27, 16)); + queued = mt76_get_field(dev, MT_FL_Q3_CTRL, + GENMASK(11, 0)); + + seq_printf(s, "\t%s: ", map[i].name); + seq_printf(s, "queued:0x%03x head:0x%03x tail:0x%03x\n", + queued, head, tail); + } +} + +static void +mt7915_sta_hw_queue_read(void *data, struct ieee80211_sta *sta) +{ + struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv; + struct mt7915_dev *dev = msta->vif->phy->dev; + struct seq_file *s = data; + u8 ac; + + for (ac = 0; ac < 4; ac++) { + u32 qlen, ctrl, val; + u32 idx = msta->wcid.idx >> 5; + u8 offs = msta->wcid.idx & GENMASK(4, 0); + + ctrl = BIT(31) | BIT(11) | (ac << 24); + val = mt76_rr(dev, MT_PLE_AC_QEMPTY(ac, idx)); + + if (val & BIT(offs)) + continue; + + mt76_wr(dev, MT_FL_Q0_CTRL, ctrl | msta->wcid.idx); + qlen = mt76_get_field(dev, MT_FL_Q3_CTRL, + GENMASK(11, 0)); + seq_printf(s, "\tSTA %pM wcid %d: AC%d%d queued:%d\n", + sta->addr, msta->wcid.idx, + msta->vif->mt76.wmm_idx, ac, qlen); + } +} + +static int +mt7915_hw_queues_show(struct seq_file *file, void *data) +{ + struct mt7915_phy *phy = file->private; + struct mt7915_dev *dev = phy->dev; + static const struct hw_queue_map ple_queue_map[] = { + { "CPU_Q0", 0, 1, MT_CTX0 }, + { "CPU_Q1", 1, 1, MT_CTX0 + 1 }, + { "CPU_Q2", 2, 1, MT_CTX0 + 2 }, + { "CPU_Q3", 3, 1, MT_CTX0 + 3 }, + { "ALTX_Q0", 8, 2, MT_LMAC_ALTX0 }, + { "BMC_Q0", 9, 2, MT_LMAC_BMC0 }, + { "BCN_Q0", 10, 2, MT_LMAC_BCN0 }, + { "PSMP_Q0", 11, 2, MT_LMAC_PSMP0 }, + { "ALTX_Q1", 12, 2, MT_LMAC_ALTX0 + 4 }, + { "BMC_Q1", 13, 2, MT_LMAC_BMC0 + 4 }, + { "BCN_Q1", 14, 2, MT_LMAC_BCN0 + 4 }, + { "PSMP_Q1", 15, 2, MT_LMAC_PSMP0 + 4 }, + }; + static const struct hw_queue_map pse_queue_map[] = { + { "CPU Q0", 0, 1, MT_CTX0 }, + { "CPU Q1", 1, 1, MT_CTX0 + 1 }, + { "CPU Q2", 2, 1, MT_CTX0 + 2 }, + { "CPU Q3", 3, 1, MT_CTX0 + 3 }, + { "HIF_Q0", 8, 0, MT_HIF0 }, + { "HIF_Q1", 9, 0, MT_HIF0 + 1 }, + { "HIF_Q2", 10, 0, MT_HIF0 + 2 }, + { "HIF_Q3", 11, 0, MT_HIF0 + 3 }, + { "HIF_Q4", 12, 0, MT_HIF0 + 4 }, + { "HIF_Q5", 13, 0, MT_HIF0 + 5 }, + { "LMAC_Q", 16, 2, 0 }, + { "MDP_TXQ", 17, 2, 1 }, + { "MDP_RXQ", 18, 2, 2 }, + { "SEC_TXQ", 19, 2, 3 }, + { "SEC_RXQ", 20, 2, 4 }, + }; + u32 val, head, tail; + + /* ple queue */ + val = mt76_rr(dev, MT_PLE_FREEPG_CNT); + head = mt76_get_field(dev, MT_PLE_FREEPG_HEAD_TAIL, GENMASK(11, 0)); + tail = mt76_get_field(dev, MT_PLE_FREEPG_HEAD_TAIL, GENMASK(27, 16)); + seq_puts(file, "PLE page info:\n"); + seq_printf(file, + "\tTotal free page: 0x%08x head: 0x%03x tail: 0x%03x\n", + val, head, tail); + + val = mt76_rr(dev, MT_PLE_PG_HIF_GROUP); + head = mt76_get_field(dev, MT_PLE_HIF_PG_INFO, GENMASK(11, 0)); + tail = mt76_get_field(dev, MT_PLE_HIF_PG_INFO, GENMASK(27, 16)); + seq_printf(file, "\tHIF free page: 0x%03x res: 0x%03x used: 0x%03x\n", + val, head, tail); + + seq_puts(file, "PLE non-empty queue info:\n"); + mt7915_hw_queue_read(file, ARRAY_SIZE(ple_queue_map), + &ple_queue_map[0]); + + /* iterate per-sta ple queue */ + ieee80211_iterate_stations_atomic(phy->mt76->hw, + mt7915_sta_hw_queue_read, file); + /* pse queue */ + seq_puts(file, "PSE non-empty queue info:\n"); + mt7915_hw_queue_read(file, ARRAY_SIZE(pse_queue_map), + &pse_queue_map[0]); + + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(mt7915_hw_queues); + +static int +mt7915_xmit_queues_show(struct seq_file *file, void *data) +{ + struct mt7915_phy *phy = file->private; + struct mt7915_dev *dev = phy->dev; + struct { + struct mt76_queue *q; + char *queue; + } queue_map[] = { + { phy->mt76->q_tx[MT_TXQ_BE], " MAIN" }, + { dev->mt76.q_mcu[MT_MCUQ_WM], " MCUWM" }, + { dev->mt76.q_mcu[MT_MCUQ_WA], " MCUWA" }, + { dev->mt76.q_mcu[MT_MCUQ_FWDL], "MCUFWDL" }, + }; + int i; + + seq_puts(file, " queue | hw-queued | head | tail |\n"); + for (i = 0; i < ARRAY_SIZE(queue_map); i++) { + struct mt76_queue *q = queue_map[i].q; + + if (!q) + continue; + + seq_printf(file, " %s | %9d | %9d | %9d |\n", + queue_map[i].queue, q->queued, q->head, + q->tail); + } + + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(mt7915_xmit_queues); + +#define mt7915_txpower_puts(rate) \ +({ \ + len += scnprintf(buf + len, sz - len, "%-16s:", #rate " (TMAC)"); \ + for (i = 0; i < mt7915_sku_group_len[SKU_##rate]; i++, offs++) \ + len += scnprintf(buf + len, sz - len, " %6d", txpwr[offs]); \ + len += scnprintf(buf + len, sz - len, "\n"); \ +}) + +#define mt7915_txpower_sets(rate, pwr, flag) \ +({ \ + offs += len; \ + len = mt7915_sku_group_len[rate]; \ + if (mode == flag) { \ + for (i = 0; i < len; i++) \ + req.txpower_sku[offs + i] = pwr; \ + } \ +}) + +static ssize_t +mt7915_rate_txpower_get(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct mt7915_phy *phy = file->private_data; + struct mt7915_dev *dev = phy->dev; + s8 txpwr[MT7915_SKU_RATE_NUM]; + static const size_t sz = 2048; + u8 band = phy->mt76->band_idx; + int i, offs = 0, len = 0; + ssize_t ret; + char *buf; + u32 reg; + + buf = kzalloc(sz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = mt7915_mcu_get_txpower_sku(phy, txpwr, sizeof(txpwr), TX_POWER_INFO_RATE); + if (ret) + goto out; + + /* Txpower propagation path: TMAC -> TXV -> BBP */ + len += scnprintf(buf + len, sz - len, + "\nPhy%d Tx power table (channel %d)\n", + phy != &dev->phy, phy->mt76->chandef.chan->hw_value); + len += scnprintf(buf + len, sz - len, "%-16s %6s %6s %6s %6s\n", + " ", "1m", "2m", "5m", "11m"); + mt7915_txpower_puts(CCK); + + len += scnprintf(buf + len, sz - len, + "%-16s %6s %6s %6s %6s %6s %6s %6s %6s\n", + " ", "6m", "9m", "12m", "18m", "24m", "36m", "48m", + "54m"); + mt7915_txpower_puts(OFDM); + + len += scnprintf(buf + len, sz - len, + "%-16s %6s %6s %6s %6s %6s %6s %6s %6s\n", + " ", "mcs0", "mcs1", "mcs2", "mcs3", "mcs4", + "mcs5", "mcs6", "mcs7"); + mt7915_txpower_puts(HT_BW20); + + len += scnprintf(buf + len, sz - len, + "%-16s %6s %6s %6s %6s %6s %6s %6s %6s %6s\n", + " ", "mcs0", "mcs1", "mcs2", "mcs3", "mcs4", "mcs5", + "mcs6", "mcs7", "mcs32"); + mt7915_txpower_puts(HT_BW40); + + len += scnprintf(buf + len, sz - len, + "%-16s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s\n", + " ", "mcs0", "mcs1", "mcs2", "mcs3", "mcs4", "mcs5", + "mcs6", "mcs7", "mcs8", "mcs9", "mcs10", "mcs11"); + mt7915_txpower_puts(VHT_BW20); + mt7915_txpower_puts(VHT_BW40); + mt7915_txpower_puts(VHT_BW80); + mt7915_txpower_puts(VHT_BW160); + mt7915_txpower_puts(HE_RU26); + mt7915_txpower_puts(HE_RU52); + mt7915_txpower_puts(HE_RU106); + mt7915_txpower_puts(HE_RU242); + mt7915_txpower_puts(HE_RU484); + mt7915_txpower_puts(HE_RU996); + mt7915_txpower_puts(HE_RU2x996); + + reg = is_mt7915(&dev->mt76) ? MT_WF_PHY_TPC_CTRL_STAT(band) : + MT_WF_PHY_TPC_CTRL_STAT_MT7916(band); + + len += scnprintf(buf + len, sz - len, "\nTx power (bbp) : %6ld\n", + mt76_get_field(dev, reg, MT_WF_PHY_TPC_POWER)); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, len); + +out: + kfree(buf); + return ret; +} + +static ssize_t +mt7915_rate_txpower_set(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + int i, ret, pwr, pwr160 = 0, pwr80 = 0, pwr40 = 0, pwr20 = 0; + struct mt7915_phy *phy = file->private_data; + struct mt7915_dev *dev = phy->dev; + struct mt76_phy *mphy = phy->mt76; + struct mt7915_mcu_txpower_sku req = { + .format_id = TX_POWER_LIMIT_TABLE, + .band_idx = phy->mt76->band_idx, + }; + char buf[100]; + enum mac80211_rx_encoding mode; + u32 offs = 0, len = 0; + + if (count >= sizeof(buf)) + return -EINVAL; + + if (copy_from_user(buf, user_buf, count)) + return -EFAULT; + + if (count && buf[count - 1] == '\n') + buf[count - 1] = '\0'; + else + buf[count] = '\0'; + + if (sscanf(buf, "%u %u %u %u %u", + &mode, &pwr160, &pwr80, &pwr40, &pwr20) != 5) { + dev_warn(dev->mt76.dev, + "per bandwidth power limit: Mode BW160 BW80 BW40 BW20"); + return -EINVAL; + } + + if (mode > RX_ENC_HE) + return -EINVAL; + + if (pwr160) + pwr160 = mt76_get_power_bound(mphy, pwr160); + if (pwr80) + pwr80 = mt76_get_power_bound(mphy, pwr80); + if (pwr40) + pwr40 = mt76_get_power_bound(mphy, pwr40); + if (pwr20) + pwr20 = mt76_get_power_bound(mphy, pwr20); + + if (pwr160 < 0 || pwr80 < 0 || pwr40 < 0 || pwr20 < 0) + return -EINVAL; + + mutex_lock(&dev->mt76.mutex); + ret = mt7915_mcu_get_txpower_sku(phy, req.txpower_sku, + sizeof(req.txpower_sku), TX_POWER_INFO_RATE); + if (ret) + goto out; + + mt7915_txpower_sets(SKU_CCK, pwr20, RX_ENC_LEGACY); + mt7915_txpower_sets(SKU_OFDM, pwr20, RX_ENC_LEGACY); + if (mode == RX_ENC_LEGACY) + goto skip; + + mt7915_txpower_sets(SKU_HT_BW20, pwr20, RX_ENC_HT); + mt7915_txpower_sets(SKU_HT_BW40, pwr40, RX_ENC_HT); + if (mode == RX_ENC_HT) + goto skip; + + mt7915_txpower_sets(SKU_VHT_BW20, pwr20, RX_ENC_VHT); + mt7915_txpower_sets(SKU_VHT_BW40, pwr40, RX_ENC_VHT); + mt7915_txpower_sets(SKU_VHT_BW80, pwr80, RX_ENC_VHT); + mt7915_txpower_sets(SKU_VHT_BW160, pwr160, RX_ENC_VHT); + if (mode == RX_ENC_VHT) + goto skip; + + mt7915_txpower_sets(SKU_HE_RU26, pwr20, RX_ENC_HE + 1); + mt7915_txpower_sets(SKU_HE_RU52, pwr20, RX_ENC_HE + 1); + mt7915_txpower_sets(SKU_HE_RU106, pwr20, RX_ENC_HE + 1); + mt7915_txpower_sets(SKU_HE_RU242, pwr20, RX_ENC_HE); + mt7915_txpower_sets(SKU_HE_RU484, pwr40, RX_ENC_HE); + mt7915_txpower_sets(SKU_HE_RU996, pwr80, RX_ENC_HE); + mt7915_txpower_sets(SKU_HE_RU2x996, pwr160, RX_ENC_HE); +skip: + ret = mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(TX_POWER_FEATURE_CTRL), + &req, sizeof(req), true); + if (ret) + goto out; + + pwr = max3(pwr80, pwr40, pwr20); + mphy->txpower_cur = max3(mphy->txpower_cur, pwr160, pwr); +out: + mutex_unlock(&dev->mt76.mutex); + + return ret ? ret : count; +} + +static const struct file_operations mt7915_txpower_fops = { + .write = mt7915_rate_txpower_set, + .read = mt7915_rate_txpower_get, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static int +mt7915_path_txpower_show(struct seq_file *file) +{ + struct mt7915_phy *phy = file->private; + s8 txpower[MT7915_SKU_PATH_NUM], *buf = txpower; + int ret; + +#define PATH_POWER_SHOW(_name, _len, _skip) do { \ + size_t __len = (_len); \ + if (_skip) { \ + buf -= 1; \ + *buf = 0; \ + } \ + mt76_seq_puts_array(file, _name, buf, __len); \ + buf += __len; \ + } while (0) + + seq_printf(file, "\n%*c", 18, ' '); + seq_puts(file, "1T1S/2T1S/3T1S/4T1S/2T2S/3T2S/4T2S/3T3S/4T3S/4T4S\n"); + ret = mt7915_mcu_get_txpower_sku(phy, txpower, sizeof(txpower), + TX_POWER_INFO_PATH); + if (ret) + return ret; + + PATH_POWER_SHOW("CCK", 4, 0); + PATH_POWER_SHOW("OFDM", 4, 0); + PATH_POWER_SHOW("BF-OFDM", 4, 1); + + PATH_POWER_SHOW("HT/VHT20", 10, 0); + PATH_POWER_SHOW("BF-HT/VHT20", 10, 1); + PATH_POWER_SHOW("HT/VHT40", 10, 0); + PATH_POWER_SHOW("BF-HT/VHT40", 10, 1); + + PATH_POWER_SHOW("BW20/RU242", 10, 0); + PATH_POWER_SHOW("BF-BW20/RU242", 10, 1); + PATH_POWER_SHOW("BW40/RU484", 10, 0); + PATH_POWER_SHOW("BF-BW40/RU484", 10, 1); + PATH_POWER_SHOW("BW80/RU996", 10, 0); + PATH_POWER_SHOW("BF-BW80/RU996", 10, 1); + PATH_POWER_SHOW("BW160/RU2x996", 10, 0); + PATH_POWER_SHOW("BF-BW160/RU2x996", 10, 1); + PATH_POWER_SHOW("RU26", 10, 0); + PATH_POWER_SHOW("BF-RU26", 10, 0); + PATH_POWER_SHOW("RU52", 10, 0); + PATH_POWER_SHOW("BF-RU52", 10, 0); + PATH_POWER_SHOW("RU106", 10, 0); + PATH_POWER_SHOW("BF-RU106", 10, 0); +#undef PATH_POWER_SHOW + + return 0; +} + +static int +mt7915_txpower_path_show(struct seq_file *file, void *data) +{ + struct mt7915_phy *phy = file->private; + + seq_printf(file, "\nBand %d\n", phy != &phy->dev->phy); + + return mt7915_path_txpower_show(file); +} + +DEFINE_SHOW_ATTRIBUTE(mt7915_txpower_path); + +static int +mt7915_twt_stats(struct seq_file *s, void *data) +{ + struct mt7915_dev *dev = dev_get_drvdata(s->private); + struct mt7915_twt_flow *iter; + + rcu_read_lock(); + + seq_puts(s, " wcid | id | flags | exp | mantissa"); + seq_puts(s, " | duration | tsf |\n"); + list_for_each_entry_rcu(iter, &dev->twt_list, list) + seq_printf(s, + "%9d | %8d | %5c%c%c%c | %8d | %8d | %8d | %14lld |\n", + iter->wcid, iter->id, + iter->sched ? 's' : 'u', + iter->protection ? 'p' : '-', + iter->trigger ? 't' : '-', + iter->flowtype ? '-' : 'a', + iter->exp, iter->mantissa, + iter->duration, iter->tsf); + + rcu_read_unlock(); + + return 0; +} + +/* The index of RF registers use the generic regidx, combined with two parts: + * WF selection [31:24] and offset [23:0]. + */ +static int +mt7915_rf_regval_get(void *data, u64 *val) +{ + struct mt7915_dev *dev = data; + u32 regval; + int ret; + + ret = mt7915_mcu_rf_regval(dev, dev->mt76.debugfs_reg, ®val, false); + if (ret) + return ret; + + *val = regval; + + return 0; +} + +static int +mt7915_rf_regval_set(void *data, u64 val) +{ + struct mt7915_dev *dev = data; + u32 val32 = val; + + return mt7915_mcu_rf_regval(dev, dev->mt76.debugfs_reg, &val32, true); +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_rf_regval, mt7915_rf_regval_get, + mt7915_rf_regval_set, "0x%08llx\n"); + +int mt7915_init_debugfs(struct mt7915_phy *phy) +{ + struct mt7915_dev *dev = phy->dev; + bool ext_phy = phy != &dev->phy; + struct dentry *dir; + + dir = mt76_register_debugfs_fops(phy->mt76, NULL); + + debugfs_create_file("muru_debug", 0600, dir, dev, &fops_muru_debug); + debugfs_create_file("muru_stats", 0400, dir, phy, + &mt7915_muru_stats_fops); + debugfs_create_file("hw-queues", 0400, dir, phy, + &mt7915_hw_queues_fops); + debugfs_create_file("xmit-queues", 0400, dir, phy, + &mt7915_xmit_queues_fops); + debugfs_create_file("tx_stats", 0400, dir, phy, &mt7915_tx_stats_fops); + debugfs_create_file("sys_recovery", 0600, dir, phy, + &mt7915_sys_recovery_ops); + debugfs_create_file("fw_debug_wm", 0600, dir, dev, &fops_fw_debug_wm); + debugfs_create_file("fw_debug_wa", 0600, dir, dev, &fops_fw_debug_wa); + debugfs_create_file("fw_debug_bin", 0600, dir, dev, &fops_fw_debug_bin); + debugfs_create_file("fw_util_wm", 0400, dir, dev, + &mt7915_fw_util_wm_fops); + debugfs_create_file("fw_util_wa", 0400, dir, dev, + &mt7915_fw_util_wa_fops); + debugfs_create_file("implicit_txbf", 0600, dir, dev, + &fops_implicit_txbf); + debugfs_create_file("txpower_sku", 0400, dir, phy, + &mt7915_txpower_fops); + debugfs_create_file("txpower_path", 0400, dir, phy, + &mt7915_txpower_path_fops); + debugfs_create_devm_seqfile(dev->mt76.dev, "twt_stats", dir, + mt7915_twt_stats); + debugfs_create_file("rf_regval", 0600, dir, dev, &fops_rf_regval); + + if (!dev->dbdc_support || phy->mt76->band_idx) { + debugfs_create_u32("dfs_hw_pattern", 0400, dir, + &dev->hw_pattern); + debugfs_create_file("radar_trigger", 0200, dir, phy, + &fops_radar_trigger); + debugfs_create_devm_seqfile(dev->mt76.dev, "rdd_monitor", dir, + mt7915_rdd_monitor); + } + + if (!ext_phy) + dev->debugfs_dir = dir; + + return 0; +} + +static void +mt7915_debugfs_write_fwlog(struct mt7915_dev *dev, const void *hdr, int hdrlen, + const void *data, int len) +{ + static DEFINE_SPINLOCK(lock); + unsigned long flags; + void *dest; + + spin_lock_irqsave(&lock, flags); + dest = relay_reserve(dev->relay_fwlog, hdrlen + len + 4); + if (dest) { + *(u32 *)dest = hdrlen + len; + dest += 4; + + if (hdrlen) { + memcpy(dest, hdr, hdrlen); + dest += hdrlen; + } + + memcpy(dest, data, len); + relay_flush(dev->relay_fwlog); + } + spin_unlock_irqrestore(&lock, flags); +} + +void mt7915_debugfs_rx_fw_monitor(struct mt7915_dev *dev, const void *data, int len) +{ + struct { + __le32 magic; + __le32 timestamp; + __le16 msg_type; + __le16 len; + } hdr = { + .magic = cpu_to_le32(FW_BIN_LOG_MAGIC), + .msg_type = cpu_to_le16(PKT_TYPE_RX_FW_MONITOR), + }; + + if (!dev->relay_fwlog) + return; + + hdr.timestamp = cpu_to_le32(mt76_rr(dev, MT_LPON_FRCR(0))); + hdr.len = *(__le16 *)data; + mt7915_debugfs_write_fwlog(dev, &hdr, sizeof(hdr), data, len); +} + +bool mt7915_debugfs_rx_log(struct mt7915_dev *dev, const void *data, int len) +{ + if (get_unaligned_le32(data) != FW_BIN_LOG_MAGIC) + return false; + + if (dev->relay_fwlog) + mt7915_debugfs_write_fwlog(dev, NULL, 0, data, len); + + return true; +} + +#ifdef CONFIG_MAC80211_DEBUGFS +/** per-station debugfs **/ + +static ssize_t mt7915_sta_fixed_rate_set(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_sta *sta = file->private_data; + struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv; + struct mt7915_dev *dev = msta->vif->phy->dev; + struct ieee80211_vif *vif; + struct sta_phy phy = {}; + char buf[100]; + int ret; + u32 field; + u8 i, gi, he_ltf; + + if (count >= sizeof(buf)) + return -EINVAL; + + if (copy_from_user(buf, user_buf, count)) + return -EFAULT; + + if (count && buf[count - 1] == '\n') + buf[count - 1] = '\0'; + else + buf[count] = '\0'; + + /* mode - cck: 0, ofdm: 1, ht: 2, gf: 3, vht: 4, he_su: 8, he_er: 9 + * bw - bw20: 0, bw40: 1, bw80: 2, bw160: 3 + * nss - vht: 1~4, he: 1~4, others: ignore + * mcs - cck: 0~4, ofdm: 0~7, ht: 0~32, vht: 0~9, he_su: 0~11, he_er: 0~2 + * gi - (ht/vht) lgi: 0, sgi: 1; (he) 0.8us: 0, 1.6us: 1, 3.2us: 2 + * ldpc - off: 0, on: 1 + * stbc - off: 0, on: 1 + * he_ltf - 1xltf: 0, 2xltf: 1, 4xltf: 2 + */ + if (sscanf(buf, "%hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu", + &phy.type, &phy.bw, &phy.nss, &phy.mcs, &gi, + &phy.ldpc, &phy.stbc, &he_ltf) != 8) { + dev_warn(dev->mt76.dev, + "format: Mode BW NSS MCS (HE)GI LDPC STBC HE_LTF\n"); + field = RATE_PARAM_AUTO; + goto out; + } + + phy.ldpc = (phy.bw || phy.ldpc) * GENMASK(2, 0); + for (i = 0; i <= phy.bw; i++) { + phy.sgi |= gi << (i << sta->deflink.he_cap.has_he); + phy.he_ltf |= he_ltf << (i << sta->deflink.he_cap.has_he); + } + field = RATE_PARAM_FIXED; + +out: + vif = container_of((void *)msta->vif, struct ieee80211_vif, drv_priv); + ret = mt7915_mcu_set_fixed_rate_ctrl(dev, vif, sta, &phy, field); + if (ret) + return -EFAULT; + + return count; +} + +static const struct file_operations fops_fixed_rate = { + .write = mt7915_sta_fixed_rate_set, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static int +mt7915_queues_show(struct seq_file *s, void *data) +{ + struct ieee80211_sta *sta = s->private; + + mt7915_sta_hw_queue_read(s, sta); + + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(mt7915_queues); + +void mt7915_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, struct dentry *dir) +{ + debugfs_create_file("fixed_rate", 0600, dir, sta, &fops_fixed_rate); + debugfs_create_file("hw-queues", 0400, dir, sta, &mt7915_queues_fops); +} + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/dma.c b/drivers/net/wireless/mediatek/mt76/mt7915/dma.c new file mode 100644 index 000000000..aabd37366 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7915/dma.c @@ -0,0 +1,655 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2020 MediaTek Inc. */ + +#include "mt7915.h" +#include "../dma.h" +#include "mac.h" + +static int +mt7915_init_tx_queues(struct mt7915_phy *phy, int idx, int n_desc, int ring_base) +{ + struct mt7915_dev *dev = phy->dev; + struct mtk_wed_device *wed = NULL; + + if (mtk_wed_device_active(&dev->mt76.mmio.wed)) { + if (is_mt798x(&dev->mt76)) + ring_base += MT_TXQ_ID(0) * MT_RING_SIZE; + else + ring_base = MT_WED_TX_RING_BASE; + + idx -= MT_TXQ_ID(0); + wed = &dev->mt76.mmio.wed; + } + + return mt76_connac_init_tx_queues(phy->mt76, idx, n_desc, ring_base, + wed, MT_WED_Q_TX(idx)); +} + +static int mt7915_poll_tx(struct napi_struct *napi, int budget) +{ + struct mt7915_dev *dev; + + dev = container_of(napi, struct mt7915_dev, mt76.tx_napi); + + mt76_connac_tx_cleanup(&dev->mt76); + if (napi_complete_done(napi, 0)) + mt7915_irq_enable(dev, MT_INT_TX_DONE_MCU); + + return 0; +} + +static void mt7915_dma_config(struct mt7915_dev *dev) +{ +#define Q_CONFIG(q, wfdma, int, id) do { \ + if (wfdma) \ + dev->wfdma_mask |= (1 << (q)); \ + dev->q_int_mask[(q)] = int; \ + dev->q_id[(q)] = id; \ + } while (0) + +#define MCUQ_CONFIG(q, wfdma, int, id) Q_CONFIG(q, (wfdma), (int), (id)) +#define RXQ_CONFIG(q, wfdma, int, id) Q_CONFIG(__RXQ(q), (wfdma), (int), (id)) +#define TXQ_CONFIG(q, wfdma, int, id) Q_CONFIG(__TXQ(q), (wfdma), (int), (id)) + + if (is_mt7915(&dev->mt76)) { + RXQ_CONFIG(MT_RXQ_MAIN, WFDMA0, MT_INT_RX_DONE_BAND0, + MT7915_RXQ_BAND0); + RXQ_CONFIG(MT_RXQ_MCU, WFDMA1, MT_INT_RX_DONE_WM, + MT7915_RXQ_MCU_WM); + RXQ_CONFIG(MT_RXQ_MCU_WA, WFDMA1, MT_INT_RX_DONE_WA, + MT7915_RXQ_MCU_WA); + RXQ_CONFIG(MT_RXQ_BAND1, WFDMA0, MT_INT_RX_DONE_BAND1, + MT7915_RXQ_BAND1); + RXQ_CONFIG(MT_RXQ_BAND1_WA, WFDMA1, MT_INT_RX_DONE_WA_EXT, + MT7915_RXQ_MCU_WA_EXT); + RXQ_CONFIG(MT_RXQ_MAIN_WA, WFDMA1, MT_INT_RX_DONE_WA_MAIN, + MT7915_RXQ_MCU_WA); + TXQ_CONFIG(0, WFDMA1, MT_INT_TX_DONE_BAND0, MT7915_TXQ_BAND0); + TXQ_CONFIG(1, WFDMA1, MT_INT_TX_DONE_BAND1, MT7915_TXQ_BAND1); + MCUQ_CONFIG(MT_MCUQ_WM, WFDMA1, MT_INT_TX_DONE_MCU_WM, + MT7915_TXQ_MCU_WM); + MCUQ_CONFIG(MT_MCUQ_WA, WFDMA1, MT_INT_TX_DONE_MCU_WA, + MT7915_TXQ_MCU_WA); + MCUQ_CONFIG(MT_MCUQ_FWDL, WFDMA1, MT_INT_TX_DONE_FWDL, + MT7915_TXQ_FWDL); + } else { + RXQ_CONFIG(MT_RXQ_MCU, WFDMA0, MT_INT_RX_DONE_WM, + MT7916_RXQ_MCU_WM); + RXQ_CONFIG(MT_RXQ_BAND1_WA, WFDMA0, MT_INT_RX_DONE_WA_EXT_MT7916, + MT7916_RXQ_MCU_WA_EXT); + MCUQ_CONFIG(MT_MCUQ_WM, WFDMA0, MT_INT_TX_DONE_MCU_WM, + MT7915_TXQ_MCU_WM); + MCUQ_CONFIG(MT_MCUQ_WA, WFDMA0, MT_INT_TX_DONE_MCU_WA_MT7916, + MT7915_TXQ_MCU_WA); + MCUQ_CONFIG(MT_MCUQ_FWDL, WFDMA0, MT_INT_TX_DONE_FWDL, + MT7915_TXQ_FWDL); + + if (is_mt7916(&dev->mt76) && mtk_wed_device_active(&dev->mt76.mmio.wed)) { + RXQ_CONFIG(MT_RXQ_MAIN, WFDMA0, MT_INT_WED_RX_DONE_BAND0_MT7916, + MT7916_RXQ_BAND0); + RXQ_CONFIG(MT_RXQ_MCU_WA, WFDMA0, MT_INT_WED_RX_DONE_WA_MT7916, + MT7916_RXQ_MCU_WA); + if (dev->hif2) + RXQ_CONFIG(MT_RXQ_BAND1, WFDMA0, + MT_INT_RX_DONE_BAND1_MT7916, + MT7916_RXQ_BAND1); + else + RXQ_CONFIG(MT_RXQ_BAND1, WFDMA0, + MT_INT_WED_RX_DONE_BAND1_MT7916, + MT7916_RXQ_BAND1); + RXQ_CONFIG(MT_RXQ_MAIN_WA, WFDMA0, MT_INT_WED_RX_DONE_WA_MAIN_MT7916, + MT7916_RXQ_MCU_WA_MAIN); + TXQ_CONFIG(0, WFDMA0, MT_INT_WED_TX_DONE_BAND0, + MT7915_TXQ_BAND0); + TXQ_CONFIG(1, WFDMA0, MT_INT_WED_TX_DONE_BAND1, + MT7915_TXQ_BAND1); + } else { + RXQ_CONFIG(MT_RXQ_MAIN, WFDMA0, MT_INT_RX_DONE_BAND0_MT7916, + MT7916_RXQ_BAND0); + RXQ_CONFIG(MT_RXQ_MCU_WA, WFDMA0, MT_INT_RX_DONE_WA, + MT7916_RXQ_MCU_WA); + RXQ_CONFIG(MT_RXQ_BAND1, WFDMA0, MT_INT_RX_DONE_BAND1_MT7916, + MT7916_RXQ_BAND1); + RXQ_CONFIG(MT_RXQ_MAIN_WA, WFDMA0, MT_INT_RX_DONE_WA_MAIN_MT7916, + MT7916_RXQ_MCU_WA_MAIN); + TXQ_CONFIG(0, WFDMA0, MT_INT_TX_DONE_BAND0, + MT7915_TXQ_BAND0); + TXQ_CONFIG(1, WFDMA0, MT_INT_TX_DONE_BAND1, + MT7915_TXQ_BAND1); + } + } +} + +static void __mt7915_dma_prefetch(struct mt7915_dev *dev, u32 ofs) +{ +#define PREFETCH(_base, _depth) ((_base) << 16 | (_depth)) + u32 base = 0; + + /* prefetch SRAM wrapping boundary for tx/rx ring. */ + mt76_wr(dev, MT_MCUQ_EXT_CTRL(MT_MCUQ_FWDL) + ofs, PREFETCH(0x0, 0x4)); + mt76_wr(dev, MT_MCUQ_EXT_CTRL(MT_MCUQ_WM) + ofs, PREFETCH(0x40, 0x4)); + mt76_wr(dev, MT_TXQ_EXT_CTRL(0) + ofs, PREFETCH(0x80, 0x4)); + mt76_wr(dev, MT_TXQ_EXT_CTRL(1) + ofs, PREFETCH(0xc0, 0x4)); + mt76_wr(dev, MT_MCUQ_EXT_CTRL(MT_MCUQ_WA) + ofs, PREFETCH(0x100, 0x4)); + + mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_MCU) + ofs, + PREFETCH(0x140, 0x4)); + mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_MCU_WA) + ofs, + PREFETCH(0x180, 0x4)); + if (!is_mt7915(&dev->mt76)) { + mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_MAIN_WA) + ofs, + PREFETCH(0x1c0, 0x4)); + base = 0x40; + } + mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_BAND1_WA) + ofs, + PREFETCH(0x1c0 + base, 0x4)); + mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_MAIN) + ofs, + PREFETCH(0x200 + base, 0x4)); + mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_BAND1) + ofs, + PREFETCH(0x240 + base, 0x4)); + + /* for mt7915, the ring which is next the last + * used ring must be initialized. + */ + if (is_mt7915(&dev->mt76)) { + ofs += 0x4; + mt76_wr(dev, MT_MCUQ_EXT_CTRL(MT_MCUQ_WA) + ofs, + PREFETCH(0x140, 0x0)); + mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_BAND1_WA) + ofs, + PREFETCH(0x200 + base, 0x0)); + mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_BAND1) + ofs, + PREFETCH(0x280 + base, 0x0)); + } +} + +void mt7915_dma_prefetch(struct mt7915_dev *dev) +{ + __mt7915_dma_prefetch(dev, 0); + if (dev->hif2) + __mt7915_dma_prefetch(dev, MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0)); +} + +static void mt7915_dma_disable(struct mt7915_dev *dev, bool rst) +{ + struct mt76_dev *mdev = &dev->mt76; + u32 hif1_ofs = 0; + + if (dev->hif2) + hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0); + + /* reset */ + if (rst) { + mt76_clear(dev, MT_WFDMA0_RST, + MT_WFDMA0_RST_DMASHDL_ALL_RST | + MT_WFDMA0_RST_LOGIC_RST); + + mt76_set(dev, MT_WFDMA0_RST, + MT_WFDMA0_RST_DMASHDL_ALL_RST | + MT_WFDMA0_RST_LOGIC_RST); + + if (is_mt7915(mdev)) { + mt76_clear(dev, MT_WFDMA1_RST, + MT_WFDMA1_RST_DMASHDL_ALL_RST | + MT_WFDMA1_RST_LOGIC_RST); + + mt76_set(dev, MT_WFDMA1_RST, + MT_WFDMA1_RST_DMASHDL_ALL_RST | + MT_WFDMA1_RST_LOGIC_RST); + } + + if (dev->hif2) { + mt76_clear(dev, MT_WFDMA0_RST + hif1_ofs, + MT_WFDMA0_RST_DMASHDL_ALL_RST | + MT_WFDMA0_RST_LOGIC_RST); + + mt76_set(dev, MT_WFDMA0_RST + hif1_ofs, + MT_WFDMA0_RST_DMASHDL_ALL_RST | + MT_WFDMA0_RST_LOGIC_RST); + + if (is_mt7915(mdev)) { + mt76_clear(dev, MT_WFDMA1_RST + hif1_ofs, + MT_WFDMA1_RST_DMASHDL_ALL_RST | + MT_WFDMA1_RST_LOGIC_RST); + + mt76_set(dev, MT_WFDMA1_RST + hif1_ofs, + MT_WFDMA1_RST_DMASHDL_ALL_RST | + MT_WFDMA1_RST_LOGIC_RST); + } + } + } + + /* disable */ + mt76_clear(dev, MT_WFDMA0_GLO_CFG, + MT_WFDMA0_GLO_CFG_TX_DMA_EN | + MT_WFDMA0_GLO_CFG_RX_DMA_EN | + MT_WFDMA0_GLO_CFG_OMIT_TX_INFO | + MT_WFDMA0_GLO_CFG_OMIT_RX_INFO | + MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2); + + if (is_mt7915(mdev)) + mt76_clear(dev, MT_WFDMA1_GLO_CFG, + MT_WFDMA1_GLO_CFG_TX_DMA_EN | + MT_WFDMA1_GLO_CFG_RX_DMA_EN | + MT_WFDMA1_GLO_CFG_OMIT_TX_INFO | + MT_WFDMA1_GLO_CFG_OMIT_RX_INFO | + MT_WFDMA1_GLO_CFG_OMIT_RX_INFO_PFET2); + + if (dev->hif2) { + mt76_clear(dev, MT_WFDMA0_GLO_CFG + hif1_ofs, + MT_WFDMA0_GLO_CFG_TX_DMA_EN | + MT_WFDMA0_GLO_CFG_RX_DMA_EN | + MT_WFDMA0_GLO_CFG_OMIT_TX_INFO | + MT_WFDMA0_GLO_CFG_OMIT_RX_INFO | + MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2); + + if (is_mt7915(mdev)) + mt76_clear(dev, MT_WFDMA1_GLO_CFG + hif1_ofs, + MT_WFDMA1_GLO_CFG_TX_DMA_EN | + MT_WFDMA1_GLO_CFG_RX_DMA_EN | + MT_WFDMA1_GLO_CFG_OMIT_TX_INFO | + MT_WFDMA1_GLO_CFG_OMIT_RX_INFO | + MT_WFDMA1_GLO_CFG_OMIT_RX_INFO_PFET2); + } +} + +int mt7915_dma_start(struct mt7915_dev *dev, bool reset, bool wed_reset) +{ + struct mt76_dev *mdev = &dev->mt76; + u32 hif1_ofs = 0; + u32 irq_mask; + + if (dev->hif2) + hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0); + + /* enable wpdma tx/rx */ + if (!reset) { + mt76_set(dev, MT_WFDMA0_GLO_CFG, + MT_WFDMA0_GLO_CFG_TX_DMA_EN | + MT_WFDMA0_GLO_CFG_RX_DMA_EN | + MT_WFDMA0_GLO_CFG_OMIT_TX_INFO | + MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2); + + if (is_mt7915(mdev)) + mt76_set(dev, MT_WFDMA1_GLO_CFG, + MT_WFDMA1_GLO_CFG_TX_DMA_EN | + MT_WFDMA1_GLO_CFG_RX_DMA_EN | + MT_WFDMA1_GLO_CFG_OMIT_TX_INFO | + MT_WFDMA1_GLO_CFG_OMIT_RX_INFO); + + if (dev->hif2) { + mt76_set(dev, MT_WFDMA0_GLO_CFG + hif1_ofs, + MT_WFDMA0_GLO_CFG_TX_DMA_EN | + MT_WFDMA0_GLO_CFG_RX_DMA_EN | + MT_WFDMA0_GLO_CFG_OMIT_TX_INFO | + MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2); + + if (is_mt7915(mdev)) + mt76_set(dev, MT_WFDMA1_GLO_CFG + hif1_ofs, + MT_WFDMA1_GLO_CFG_TX_DMA_EN | + MT_WFDMA1_GLO_CFG_RX_DMA_EN | + MT_WFDMA1_GLO_CFG_OMIT_TX_INFO | + MT_WFDMA1_GLO_CFG_OMIT_RX_INFO); + + mt76_set(dev, MT_WFDMA_HOST_CONFIG, + MT_WFDMA_HOST_CONFIG_PDMA_BAND); + } + } + + /* enable interrupts for TX/RX rings */ + irq_mask = MT_INT_RX_DONE_MCU | + MT_INT_TX_DONE_MCU | + MT_INT_MCU_CMD; + + if (!dev->phy.mt76->band_idx) + irq_mask |= MT_INT_BAND0_RX_DONE; + + if (dev->dbdc_support || dev->phy.mt76->band_idx) + irq_mask |= MT_INT_BAND1_RX_DONE; + + if (mtk_wed_device_active(&dev->mt76.mmio.wed) && wed_reset) { + u32 wed_irq_mask = irq_mask; + int ret; + + wed_irq_mask |= MT_INT_TX_DONE_BAND0 | MT_INT_TX_DONE_BAND1; + if (!is_mt798x(&dev->mt76)) + mt76_wr(dev, MT_INT_WED_MASK_CSR, wed_irq_mask); + else + mt76_wr(dev, MT_INT_MASK_CSR, wed_irq_mask); + + ret = mt7915_mcu_wed_enable_rx_stats(dev); + if (ret) + return ret; + + mtk_wed_device_start(&dev->mt76.mmio.wed, wed_irq_mask); + } + + irq_mask = reset ? MT_INT_MCU_CMD : irq_mask; + + mt7915_irq_enable(dev, irq_mask); + mt7915_irq_disable(dev, 0); + + return 0; +} + +static int mt7915_dma_enable(struct mt7915_dev *dev, bool reset) +{ + struct mt76_dev *mdev = &dev->mt76; + u32 hif1_ofs = 0; + + if (dev->hif2) + hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0); + + /* reset dma idx */ + mt76_wr(dev, MT_WFDMA0_RST_DTX_PTR, ~0); + if (is_mt7915(mdev)) + mt76_wr(dev, MT_WFDMA1_RST_DTX_PTR, ~0); + if (dev->hif2) { + mt76_wr(dev, MT_WFDMA0_RST_DTX_PTR + hif1_ofs, ~0); + if (is_mt7915(mdev)) + mt76_wr(dev, MT_WFDMA1_RST_DTX_PTR + hif1_ofs, ~0); + } + + /* configure delay interrupt off */ + mt76_wr(dev, MT_WFDMA0_PRI_DLY_INT_CFG0, 0); + if (is_mt7915(mdev)) { + mt76_wr(dev, MT_WFDMA1_PRI_DLY_INT_CFG0, 0); + } else { + mt76_wr(dev, MT_WFDMA0_PRI_DLY_INT_CFG1, 0); + mt76_wr(dev, MT_WFDMA0_PRI_DLY_INT_CFG2, 0); + } + + if (dev->hif2) { + mt76_wr(dev, MT_WFDMA0_PRI_DLY_INT_CFG0 + hif1_ofs, 0); + if (is_mt7915(mdev)) { + mt76_wr(dev, MT_WFDMA1_PRI_DLY_INT_CFG0 + + hif1_ofs, 0); + } else { + mt76_wr(dev, MT_WFDMA0_PRI_DLY_INT_CFG1 + + hif1_ofs, 0); + mt76_wr(dev, MT_WFDMA0_PRI_DLY_INT_CFG2 + + hif1_ofs, 0); + } + } + + /* configure perfetch settings */ + mt7915_dma_prefetch(dev); + + /* hif wait WFDMA idle */ + mt76_set(dev, MT_WFDMA0_BUSY_ENA, + MT_WFDMA0_BUSY_ENA_TX_FIFO0 | + MT_WFDMA0_BUSY_ENA_TX_FIFO1 | + MT_WFDMA0_BUSY_ENA_RX_FIFO); + + if (is_mt7915(mdev)) + mt76_set(dev, MT_WFDMA1_BUSY_ENA, + MT_WFDMA1_BUSY_ENA_TX_FIFO0 | + MT_WFDMA1_BUSY_ENA_TX_FIFO1 | + MT_WFDMA1_BUSY_ENA_RX_FIFO); + + if (dev->hif2) { + mt76_set(dev, MT_WFDMA0_BUSY_ENA + hif1_ofs, + MT_WFDMA0_PCIE1_BUSY_ENA_TX_FIFO0 | + MT_WFDMA0_PCIE1_BUSY_ENA_TX_FIFO1 | + MT_WFDMA0_PCIE1_BUSY_ENA_RX_FIFO); + + if (is_mt7915(mdev)) + mt76_set(dev, MT_WFDMA1_BUSY_ENA + hif1_ofs, + MT_WFDMA1_PCIE1_BUSY_ENA_TX_FIFO0 | + MT_WFDMA1_PCIE1_BUSY_ENA_TX_FIFO1 | + MT_WFDMA1_PCIE1_BUSY_ENA_RX_FIFO); + } + + mt76_poll(dev, MT_WFDMA_EXT_CSR_HIF_MISC, + MT_WFDMA_EXT_CSR_HIF_MISC_BUSY, 0, 1000); + + return mt7915_dma_start(dev, reset, true); +} + +int mt7915_dma_init(struct mt7915_dev *dev, struct mt7915_phy *phy2) +{ + struct mt76_dev *mdev = &dev->mt76; + u32 wa_rx_base, wa_rx_idx; + u32 hif1_ofs = 0; + int ret; + + mt7915_dma_config(dev); + + mt76_dma_attach(&dev->mt76); + + if (dev->hif2) + hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0); + + mt7915_dma_disable(dev, true); + + if (mtk_wed_device_active(&mdev->mmio.wed)) { + if (!is_mt798x(mdev)) { + u8 wed_control_rx1 = is_mt7915(mdev) ? 1 : 2; + + mt76_set(dev, MT_WFDMA_HOST_CONFIG, + MT_WFDMA_HOST_CONFIG_WED); + mt76_wr(dev, MT_WFDMA_WED_RING_CONTROL, + FIELD_PREP(MT_WFDMA_WED_RING_CONTROL_TX0, 18) | + FIELD_PREP(MT_WFDMA_WED_RING_CONTROL_TX1, 19) | + FIELD_PREP(MT_WFDMA_WED_RING_CONTROL_RX1, + wed_control_rx1)); + if (is_mt7915(mdev)) + mt76_rmw(dev, MT_WFDMA0_EXT0_CFG, MT_WFDMA0_EXT0_RXWB_KEEP, + MT_WFDMA0_EXT0_RXWB_KEEP); + } + } else { + mt76_clear(dev, MT_WFDMA_HOST_CONFIG, MT_WFDMA_HOST_CONFIG_WED); + } + + /* init tx queue */ + ret = mt7915_init_tx_queues(&dev->phy, + MT_TXQ_ID(dev->phy.mt76->band_idx), + MT7915_TX_RING_SIZE, + MT_TXQ_RING_BASE(0)); + if (ret) + return ret; + + if (phy2) { + ret = mt7915_init_tx_queues(phy2, + MT_TXQ_ID(phy2->mt76->band_idx), + MT7915_TX_RING_SIZE, + MT_TXQ_RING_BASE(1)); + if (ret) + return ret; + } + + /* command to WM */ + ret = mt76_init_mcu_queue(&dev->mt76, MT_MCUQ_WM, + MT_MCUQ_ID(MT_MCUQ_WM), + MT7915_TX_MCU_RING_SIZE, + MT_MCUQ_RING_BASE(MT_MCUQ_WM)); + if (ret) + return ret; + + /* command to WA */ + ret = mt76_init_mcu_queue(&dev->mt76, MT_MCUQ_WA, + MT_MCUQ_ID(MT_MCUQ_WA), + MT7915_TX_MCU_RING_SIZE, + MT_MCUQ_RING_BASE(MT_MCUQ_WA)); + if (ret) + return ret; + + /* firmware download */ + ret = mt76_init_mcu_queue(&dev->mt76, MT_MCUQ_FWDL, + MT_MCUQ_ID(MT_MCUQ_FWDL), + MT7915_TX_FWDL_RING_SIZE, + MT_MCUQ_RING_BASE(MT_MCUQ_FWDL)); + if (ret) + return ret; + + /* event from WM */ + ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MCU], + MT_RXQ_ID(MT_RXQ_MCU), + MT7915_RX_MCU_RING_SIZE, + MT_RX_BUF_SIZE, + MT_RXQ_RING_BASE(MT_RXQ_MCU)); + if (ret) + return ret; + + /* event from WA */ + if (mtk_wed_device_active(&mdev->mmio.wed) && is_mt7915(mdev)) { + wa_rx_base = MT_WED_RX_RING_BASE; + wa_rx_idx = MT7915_RXQ_MCU_WA; + mdev->q_rx[MT_RXQ_MCU_WA].flags = MT_WED_Q_TXFREE; + mdev->q_rx[MT_RXQ_MCU_WA].wed = &mdev->mmio.wed; + } else { + wa_rx_base = MT_RXQ_RING_BASE(MT_RXQ_MCU_WA); + wa_rx_idx = MT_RXQ_ID(MT_RXQ_MCU_WA); + } + ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MCU_WA], + wa_rx_idx, MT7915_RX_MCU_RING_SIZE, + MT_RX_BUF_SIZE, wa_rx_base); + if (ret) + return ret; + + /* rx data queue for band0 */ + if (!dev->phy.mt76->band_idx) { + if (mtk_wed_device_active(&mdev->mmio.wed) && + mtk_wed_get_rx_capa(&mdev->mmio.wed)) { + mdev->q_rx[MT_RXQ_MAIN].flags = + MT_WED_Q_RX(MT7915_RXQ_BAND0); + dev->mt76.rx_token_size += MT7915_RX_RING_SIZE; + mdev->q_rx[MT_RXQ_MAIN].wed = &mdev->mmio.wed; + } + + ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MAIN], + MT_RXQ_ID(MT_RXQ_MAIN), + MT7915_RX_RING_SIZE, + MT_RX_BUF_SIZE, + MT_RXQ_RING_BASE(MT_RXQ_MAIN)); + if (ret) + return ret; + } + + /* tx free notify event from WA for band0 */ + if (!is_mt7915(mdev)) { + wa_rx_base = MT_RXQ_RING_BASE(MT_RXQ_MAIN_WA); + wa_rx_idx = MT_RXQ_ID(MT_RXQ_MAIN_WA); + + if (mtk_wed_device_active(&mdev->mmio.wed)) { + mdev->q_rx[MT_RXQ_MAIN_WA].flags = MT_WED_Q_TXFREE; + mdev->q_rx[MT_RXQ_MAIN_WA].wed = &mdev->mmio.wed; + if (is_mt7916(mdev)) { + wa_rx_base = MT_WED_RX_RING_BASE; + wa_rx_idx = MT7915_RXQ_MCU_WA; + } + } + + ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MAIN_WA], + wa_rx_idx, MT7915_RX_MCU_RING_SIZE, + MT_RX_BUF_SIZE, wa_rx_base); + if (ret) + return ret; + } + + if (dev->dbdc_support || dev->phy.mt76->band_idx) { + if (mtk_wed_device_active(&mdev->mmio.wed) && + mtk_wed_get_rx_capa(&mdev->mmio.wed)) { + mdev->q_rx[MT_RXQ_BAND1].flags = + MT_WED_Q_RX(MT7915_RXQ_BAND1); + dev->mt76.rx_token_size += MT7915_RX_RING_SIZE; + mdev->q_rx[MT_RXQ_BAND1].wed = &mdev->mmio.wed; + } + + /* rx data queue for band1 */ + ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_BAND1], + MT_RXQ_ID(MT_RXQ_BAND1), + MT7915_RX_RING_SIZE, + MT_RX_BUF_SIZE, + MT_RXQ_RING_BASE(MT_RXQ_BAND1) + hif1_ofs); + if (ret) + return ret; + + /* tx free notify event from WA for band1 */ + ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_BAND1_WA], + MT_RXQ_ID(MT_RXQ_BAND1_WA), + MT7915_RX_MCU_RING_SIZE, + MT_RX_BUF_SIZE, + MT_RXQ_RING_BASE(MT_RXQ_BAND1_WA) + hif1_ofs); + if (ret) + return ret; + } + + ret = mt76_init_queues(dev, mt76_dma_rx_poll); + if (ret < 0) + return ret; + + netif_napi_add_tx(dev->mt76.tx_napi_dev, &dev->mt76.tx_napi, + mt7915_poll_tx); + napi_enable(&dev->mt76.tx_napi); + + mt7915_dma_enable(dev, false); + + return 0; +} + +int mt7915_dma_reset(struct mt7915_dev *dev, bool force) +{ + struct mt76_phy *mphy_ext = dev->mt76.phys[MT_BAND1]; + struct mtk_wed_device *wed = &dev->mt76.mmio.wed; + int i; + + /* clean up hw queues */ + for (i = 0; i < ARRAY_SIZE(dev->mt76.phy.q_tx); i++) { + mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[i], true); + if (mphy_ext) + mt76_queue_tx_cleanup(dev, mphy_ext->q_tx[i], true); + } + + for (i = 0; i < ARRAY_SIZE(dev->mt76.q_mcu); i++) + mt76_queue_tx_cleanup(dev, dev->mt76.q_mcu[i], true); + + mt76_for_each_q_rx(&dev->mt76, i) + mt76_queue_rx_cleanup(dev, &dev->mt76.q_rx[i]); + + /* reset wfsys */ + if (force) + mt7915_wfsys_reset(dev); + + if (mtk_wed_device_active(wed)) + mtk_wed_device_dma_reset(wed); + + mt7915_dma_disable(dev, force); + mt76_wed_dma_reset(&dev->mt76); + + /* reset hw queues */ + for (i = 0; i < __MT_TXQ_MAX; i++) { + mt76_dma_reset_tx_queue(&dev->mt76, dev->mphy.q_tx[i]); + if (mphy_ext) + mt76_dma_reset_tx_queue(&dev->mt76, mphy_ext->q_tx[i]); + } + + for (i = 0; i < __MT_MCUQ_MAX; i++) + mt76_queue_reset(dev, dev->mt76.q_mcu[i], true); + + mt76_for_each_q_rx(&dev->mt76, i) { + if (mt76_queue_is_wed_tx_free(&dev->mt76.q_rx[i])) + continue; + + mt76_queue_reset(dev, &dev->mt76.q_rx[i], true); + } + + mt76_tx_status_check(&dev->mt76, true); + + mt76_for_each_q_rx(&dev->mt76, i) + mt76_queue_rx_reset(dev, i); + + if (mtk_wed_device_active(wed) && is_mt7915(&dev->mt76)) + mt76_rmw(dev, MT_WFDMA0_EXT0_CFG, MT_WFDMA0_EXT0_RXWB_KEEP, + MT_WFDMA0_EXT0_RXWB_KEEP); + + mt7915_dma_enable(dev, !force); + + return 0; +} + +void mt7915_dma_cleanup(struct mt7915_dev *dev) +{ + mt7915_dma_disable(dev, true); + + mt76_dma_cleanup(&dev->mt76); +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c new file mode 100644 index 000000000..fe7b29ebc --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c @@ -0,0 +1,409 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2020 MediaTek Inc. */ + +#include +#include +#include "mt7915.h" +#include "eeprom.h" + +static bool enable_6ghz; +module_param(enable_6ghz, bool, 0644); +MODULE_PARM_DESC(enable_6ghz, "Enable 6 GHz instead of 5 GHz on hardware that supports both"); + +static int mt7915_eeprom_load_precal(struct mt7915_dev *dev) +{ + struct mt76_dev *mdev = &dev->mt76; + u8 *eeprom = mdev->eeprom.data; + u32 offs = is_mt7915(&dev->mt76) ? MT_EE_DO_PRE_CAL : MT_EE_DO_PRE_CAL_V2; + u32 size, val = eeprom[offs]; + int ret; + + if (!dev->flash_mode || !val) + return 0; + + size = mt7915_get_cal_group_size(dev) + mt7915_get_cal_dpd_size(dev); + + dev->cal = devm_kzalloc(mdev->dev, size, GFP_KERNEL); + if (!dev->cal) + return -ENOMEM; + + offs = is_mt7915(&dev->mt76) ? MT_EE_PRECAL : MT_EE_PRECAL_V2; + + ret = mt76_get_of_data_from_mtd(mdev, dev->cal, offs, size); + if (!ret) + return ret; + + ret = mt76_get_of_data_from_nvmem(mdev, dev->cal, "precal", size); + if (!ret) + return ret; + + dev_warn(mdev->dev, "missing precal data, size=%d\n", size); + devm_kfree(mdev->dev, dev->cal); + dev->cal = NULL; + + return ret; +} + +static int mt7915_check_eeprom(struct mt7915_dev *dev) +{ + u8 *eeprom = dev->mt76.eeprom.data; + u16 val = get_unaligned_le16(eeprom); + +#define CHECK_EEPROM_ERR(match) (match ? 0 : -EINVAL) + switch (val) { + case 0x7915: + return CHECK_EEPROM_ERR(is_mt7915(&dev->mt76)); + case 0x7916: + return CHECK_EEPROM_ERR(is_mt7916(&dev->mt76)); + case 0x7981: + return CHECK_EEPROM_ERR(is_mt7981(&dev->mt76)); + case 0x7986: + return CHECK_EEPROM_ERR(is_mt7986(&dev->mt76)); + default: + return -EINVAL; + } +} + +static char *mt7915_eeprom_name(struct mt7915_dev *dev) +{ + switch (mt76_chip(&dev->mt76)) { + case 0x7915: + return dev->dbdc_support ? + MT7915_EEPROM_DEFAULT_DBDC : MT7915_EEPROM_DEFAULT; + case 0x7981: + /* mt7981 only supports mt7976 and only in DBDC mode */ + return MT7981_EEPROM_MT7976_DEFAULT_DBDC; + case 0x7986: + switch (mt7915_check_adie(dev, true)) { + case MT7976_ONE_ADIE_DBDC: + return MT7986_EEPROM_MT7976_DEFAULT_DBDC; + case MT7975_ONE_ADIE: + return MT7986_EEPROM_MT7975_DEFAULT; + case MT7976_ONE_ADIE: + return MT7986_EEPROM_MT7976_DEFAULT; + case MT7975_DUAL_ADIE: + return MT7986_EEPROM_MT7975_DUAL_DEFAULT; + case MT7976_DUAL_ADIE: + return MT7986_EEPROM_MT7976_DUAL_DEFAULT; + default: + break; + } + return NULL; + default: + return MT7916_EEPROM_DEFAULT; + } +} + +static int +mt7915_eeprom_load_default(struct mt7915_dev *dev) +{ + u8 *eeprom = dev->mt76.eeprom.data; + const struct firmware *fw = NULL; + int ret; + + ret = request_firmware(&fw, mt7915_eeprom_name(dev), dev->mt76.dev); + if (ret) + return ret; + + if (!fw || !fw->data) { + dev_err(dev->mt76.dev, "Invalid default bin\n"); + ret = -EINVAL; + goto out; + } + + memcpy(eeprom, fw->data, mt7915_eeprom_size(dev)); + dev->flash_mode = true; + +out: + release_firmware(fw); + + return ret; +} + +static int mt7915_eeprom_load(struct mt7915_dev *dev) +{ + int ret; + u16 eeprom_size = mt7915_eeprom_size(dev); + + ret = mt76_eeprom_init(&dev->mt76, eeprom_size); + if (ret < 0) + return ret; + + if (ret) { + dev->flash_mode = true; + } else { + u8 free_block_num; + u32 block_num, i; + u32 eeprom_blk_size = MT7915_EEPROM_BLOCK_SIZE; + + ret = mt7915_mcu_get_eeprom_free_block(dev, &free_block_num); + if (ret < 0) + return ret; + + /* efuse info isn't enough */ + if (free_block_num >= 29) + return -EINVAL; + + /* read eeprom data from efuse */ + block_num = DIV_ROUND_UP(eeprom_size, eeprom_blk_size); + for (i = 0; i < block_num; i++) { + ret = mt7915_mcu_get_eeprom(dev, i * eeprom_blk_size, NULL); + if (ret < 0) + return ret; + } + } + + return mt7915_check_eeprom(dev); +} + +static void mt7915_eeprom_parse_band_config(struct mt7915_phy *phy) +{ + struct mt7915_dev *dev = phy->dev; + u8 *eeprom = dev->mt76.eeprom.data; + u8 band = phy->mt76->band_idx; + u32 val; + + val = eeprom[MT_EE_WIFI_CONF + band]; + val = FIELD_GET(MT_EE_WIFI_CONF0_BAND_SEL, val); + + if (!is_mt7915(&dev->mt76)) { + switch (val) { + case MT_EE_V2_BAND_SEL_5GHZ: + phy->mt76->cap.has_5ghz = true; + return; + case MT_EE_V2_BAND_SEL_6GHZ: + phy->mt76->cap.has_6ghz = true; + return; + case MT_EE_V2_BAND_SEL_5GHZ_6GHZ: + if (enable_6ghz) { + phy->mt76->cap.has_6ghz = true; + u8p_replace_bits(&eeprom[MT_EE_WIFI_CONF + band], + MT_EE_V2_BAND_SEL_6GHZ, + MT_EE_WIFI_CONF0_BAND_SEL); + } else { + phy->mt76->cap.has_5ghz = true; + u8p_replace_bits(&eeprom[MT_EE_WIFI_CONF + band], + MT_EE_V2_BAND_SEL_5GHZ, + MT_EE_WIFI_CONF0_BAND_SEL); + } + /* force to buffer mode */ + dev->flash_mode = true; + + return; + default: + phy->mt76->cap.has_2ghz = true; + return; + } + } else if (val == MT_EE_BAND_SEL_DEFAULT && dev->dbdc_support) { + val = band ? MT_EE_BAND_SEL_5GHZ : MT_EE_BAND_SEL_2GHZ; + } + + switch (val) { + case MT_EE_BAND_SEL_5GHZ: + phy->mt76->cap.has_5ghz = true; + break; + case MT_EE_BAND_SEL_2GHZ: + phy->mt76->cap.has_2ghz = true; + break; + default: + phy->mt76->cap.has_2ghz = true; + phy->mt76->cap.has_5ghz = true; + break; + } +} + +void mt7915_eeprom_parse_hw_cap(struct mt7915_dev *dev, + struct mt7915_phy *phy) +{ + u8 path, nss, nss_max = 4, *eeprom = dev->mt76.eeprom.data; + struct mt76_phy *mphy = phy->mt76; + u8 band = phy->mt76->band_idx; + + mt7915_eeprom_parse_band_config(phy); + + /* read tx/rx path from eeprom */ + if (is_mt7915(&dev->mt76)) { + path = FIELD_GET(MT_EE_WIFI_CONF0_TX_PATH, + eeprom[MT_EE_WIFI_CONF]); + } else { + path = FIELD_GET(MT_EE_WIFI_CONF0_TX_PATH, + eeprom[MT_EE_WIFI_CONF + band]); + } + + if (!path || path > 4) + path = 4; + + /* read tx/rx stream */ + nss = path; + if (dev->dbdc_support) { + if (is_mt7915(&dev->mt76)) { + path = min_t(u8, path, 2); + nss = FIELD_GET(MT_EE_WIFI_CONF3_TX_PATH_B0, + eeprom[MT_EE_WIFI_CONF + 3]); + if (band) + nss = FIELD_GET(MT_EE_WIFI_CONF3_TX_PATH_B1, + eeprom[MT_EE_WIFI_CONF + 3]); + } else { + nss = FIELD_GET(MT_EE_WIFI_CONF_STREAM_NUM, + eeprom[MT_EE_WIFI_CONF + 2 + band]); + } + + if (!is_mt798x(&dev->mt76)) + nss_max = 2; + } + + if (!nss) + nss = nss_max; + nss = min_t(u8, min_t(u8, nss_max, nss), path); + + mphy->chainmask = BIT(path) - 1; + if (band && dev->dbdc_support) + mphy->chainmask <<= dev->chainshift; + mphy->antenna_mask = BIT(nss) - 1; + dev->chainmask |= mphy->chainmask; + dev->chainshift = hweight8(dev->mphy.chainmask); +} + +int mt7915_eeprom_init(struct mt7915_dev *dev) +{ + int ret; + + ret = mt7915_eeprom_load(dev); + if (ret < 0) { + if (ret != -EINVAL) + return ret; + + dev_warn(dev->mt76.dev, "eeprom load fail, use default bin\n"); + ret = mt7915_eeprom_load_default(dev); + if (ret) + return ret; + } + + mt7915_eeprom_load_precal(dev); + mt7915_eeprom_parse_hw_cap(dev, &dev->phy); + memcpy(dev->mphy.macaddr, dev->mt76.eeprom.data + MT_EE_MAC_ADDR, + ETH_ALEN); + + return mt76_eeprom_override(&dev->mphy); +} + +int mt7915_eeprom_get_target_power(struct mt7915_dev *dev, + struct ieee80211_channel *chan, + u8 chain_idx) +{ + u8 *eeprom = dev->mt76.eeprom.data; + int index, target_power; + bool tssi_on, is_7976; + + if (chain_idx > 3) + return -EINVAL; + + tssi_on = mt7915_tssi_enabled(dev, chan->band); + is_7976 = mt7915_check_adie(dev, false) || is_mt7916(&dev->mt76); + + if (chan->band == NL80211_BAND_2GHZ) { + if (is_7976) { + index = MT_EE_TX0_POWER_2G_V2 + chain_idx; + target_power = eeprom[index]; + } else { + index = MT_EE_TX0_POWER_2G + chain_idx * 3; + target_power = eeprom[index]; + + if (!tssi_on) + target_power += eeprom[index + 1]; + } + } else if (chan->band == NL80211_BAND_5GHZ) { + int group = mt7915_get_channel_group_5g(chan->hw_value, is_7976); + + if (is_7976) { + index = MT_EE_TX0_POWER_5G_V2 + chain_idx * 5; + target_power = eeprom[index + group]; + } else { + index = MT_EE_TX0_POWER_5G + chain_idx * 12; + target_power = eeprom[index + group]; + + if (!tssi_on) + target_power += eeprom[index + 8]; + } + } else { + int group = mt7915_get_channel_group_6g(chan->hw_value); + + index = MT_EE_TX0_POWER_6G_V2 + chain_idx * 8; + target_power = is_7976 ? eeprom[index + group] : 0; + } + + return target_power; +} + +s8 mt7915_eeprom_get_power_delta(struct mt7915_dev *dev, int band) +{ + u8 *eeprom = dev->mt76.eeprom.data; + u32 val, offs; + s8 delta; + bool is_7976 = mt7915_check_adie(dev, false) || is_mt7916(&dev->mt76); + + if (band == NL80211_BAND_2GHZ) + offs = is_7976 ? MT_EE_RATE_DELTA_2G_V2 : MT_EE_RATE_DELTA_2G; + else if (band == NL80211_BAND_5GHZ) + offs = is_7976 ? MT_EE_RATE_DELTA_5G_V2 : MT_EE_RATE_DELTA_5G; + else + offs = is_7976 ? MT_EE_RATE_DELTA_6G_V2 : 0; + + val = eeprom[offs]; + + if (!offs || !(val & MT_EE_RATE_DELTA_EN)) + return 0; + + delta = FIELD_GET(MT_EE_RATE_DELTA_MASK, val); + + return val & MT_EE_RATE_DELTA_SIGN ? delta : -delta; +} + +bool +mt7915_eeprom_has_background_radar(struct mt7915_dev *dev) +{ + u8 val, buf[MT7915_EEPROM_BLOCK_SIZE]; + u8 band_sel, tx_path, rx_path; + int offs = MT_EE_WIFI_CONF + 1; + + switch (mt76_chip(&dev->mt76)) { + case 0x7915: + return true; + case 0x7906: + /* read efuse to check background radar capability */ + if (mt7915_mcu_get_eeprom(dev, offs, buf)) + break; + + val = buf[offs % MT7915_EEPROM_BLOCK_SIZE]; + band_sel = u8_get_bits(val, MT_EE_WIFI_CONF0_BAND_SEL); + tx_path = u8_get_bits(val, MT_EE_WIFI_CONF0_TX_PATH); + rx_path = u8_get_bits(val, MT_EE_WIFI_CONF0_RX_PATH); + + return (band_sel == MT_EE_V2_BAND_SEL_5GHZ && + tx_path == rx_path && rx_path == 2); + case 0x7981: + case 0x7986: + default: + break; + } + + return false; +} + +const u8 mt7915_sku_group_len[] = { + [SKU_CCK] = 4, + [SKU_OFDM] = 8, + [SKU_HT_BW20] = 8, + [SKU_HT_BW40] = 9, + [SKU_VHT_BW20] = 12, + [SKU_VHT_BW40] = 12, + [SKU_VHT_BW80] = 12, + [SKU_VHT_BW160] = 12, + [SKU_HE_RU26] = 12, + [SKU_HE_RU52] = 12, + [SKU_HE_RU106] = 12, + [SKU_HE_RU242] = 12, + [SKU_HE_RU484] = 12, + [SKU_HE_RU996] = 12, + [SKU_HE_RU2x996] = 12 +}; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.h b/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.h new file mode 100644 index 000000000..1dc285c72 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.h @@ -0,0 +1,203 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* Copyright (C) 2020 MediaTek Inc. */ + +#ifndef __MT7915_EEPROM_H +#define __MT7915_EEPROM_H + +#include "mt7915.h" + +struct cal_data { + u8 count; + u16 offset[60]; +}; + +enum mt7915_eeprom_field { + MT_EE_CHIP_ID = 0x000, + MT_EE_VERSION = 0x002, + MT_EE_MAC_ADDR = 0x004, + MT_EE_MAC_ADDR2 = 0x00a, + MT_EE_DDIE_FT_VERSION = 0x050, + MT_EE_DO_PRE_CAL = 0x062, + MT_EE_WIFI_CONF = 0x190, + MT_EE_DO_PRE_CAL_V2 = 0x19a, + MT_EE_RATE_DELTA_2G = 0x252, + MT_EE_RATE_DELTA_5G = 0x29d, + MT_EE_TX0_POWER_2G = 0x2fc, + MT_EE_TX0_POWER_5G = 0x34b, + MT_EE_RATE_DELTA_2G_V2 = 0x7d3, + MT_EE_RATE_DELTA_5G_V2 = 0x81e, + MT_EE_RATE_DELTA_6G_V2 = 0x884, /* 6g fields only appear in eeprom v2 */ + MT_EE_TX0_POWER_2G_V2 = 0x441, + MT_EE_TX0_POWER_5G_V2 = 0x445, + MT_EE_TX0_POWER_6G_V2 = 0x465, + MT_EE_ADIE_FT_VERSION = 0x9a0, + + __MT_EE_MAX = 0xe00, + __MT_EE_MAX_V2 = 0x1000, + /* 0xe10 ~ 0x5780 used to save group cal data */ + MT_EE_PRECAL = 0xe10, + MT_EE_PRECAL_V2 = 0x1010 +}; + +#define MT_EE_WIFI_CAL_GROUP BIT(0) +#define MT_EE_WIFI_CAL_DPD_2G BIT(2) +#define MT_EE_WIFI_CAL_DPD_5G BIT(1) +#define MT_EE_WIFI_CAL_DPD_6G BIT(3) +#define MT_EE_WIFI_CAL_DPD GENMASK(3, 1) +#define MT_EE_CAL_UNIT 1024 +#define MT_EE_CAL_GROUP_SIZE_7915 (49 * MT_EE_CAL_UNIT + 16) +#define MT_EE_CAL_GROUP_SIZE_7916 (54 * MT_EE_CAL_UNIT + 16) +#define MT_EE_CAL_GROUP_SIZE_7975 (54 * MT_EE_CAL_UNIT + 16) +#define MT_EE_CAL_GROUP_SIZE_7976 (94 * MT_EE_CAL_UNIT + 16) +#define MT_EE_CAL_GROUP_SIZE_7916_6G (94 * MT_EE_CAL_UNIT + 16) +#define MT_EE_CAL_GROUP_SIZE_7981 (144 * MT_EE_CAL_UNIT + 16) +#define MT_EE_CAL_DPD_SIZE_V1 (54 * MT_EE_CAL_UNIT) +#define MT_EE_CAL_DPD_SIZE_V2 (300 * MT_EE_CAL_UNIT) + +#define MT_EE_WIFI_CONF0_TX_PATH GENMASK(2, 0) +#define MT_EE_WIFI_CONF0_RX_PATH GENMASK(5, 3) +#define MT_EE_WIFI_CONF0_BAND_SEL GENMASK(7, 6) +#define MT_EE_WIFI_CONF1_BAND_SEL GENMASK(7, 6) +#define MT_EE_WIFI_CONF_STREAM_NUM GENMASK(7, 5) +#define MT_EE_WIFI_CONF3_TX_PATH_B0 GENMASK(1, 0) +#define MT_EE_WIFI_CONF3_TX_PATH_B1 GENMASK(5, 4) +#define MT_EE_WIFI_CONF7_TSSI0_2G BIT(0) +#define MT_EE_WIFI_CONF7_TSSI0_5G BIT(2) +#define MT_EE_WIFI_CONF7_TSSI1_5G BIT(4) + +#define MT_EE_RATE_DELTA_MASK GENMASK(5, 0) +#define MT_EE_RATE_DELTA_SIGN BIT(6) +#define MT_EE_RATE_DELTA_EN BIT(7) + +enum mt7915_adie_sku { + MT7976_ONE_ADIE_DBDC = 0x7, + MT7975_ONE_ADIE = 0x8, + MT7976_ONE_ADIE = 0xa, + MT7975_DUAL_ADIE = 0xd, + MT7976_DUAL_ADIE = 0xf, +}; + +enum mt7915_eeprom_band { + MT_EE_BAND_SEL_DEFAULT, + MT_EE_BAND_SEL_5GHZ, + MT_EE_BAND_SEL_2GHZ, + MT_EE_BAND_SEL_DUAL, +}; + +enum { + MT_EE_V2_BAND_SEL_2GHZ, + MT_EE_V2_BAND_SEL_5GHZ, + MT_EE_V2_BAND_SEL_6GHZ, + MT_EE_V2_BAND_SEL_5GHZ_6GHZ, +}; + +enum mt7915_sku_rate_group { + SKU_CCK, + SKU_OFDM, + SKU_HT_BW20, + SKU_HT_BW40, + SKU_VHT_BW20, + SKU_VHT_BW40, + SKU_VHT_BW80, + SKU_VHT_BW160, + SKU_HE_RU26, + SKU_HE_RU52, + SKU_HE_RU106, + SKU_HE_RU242, + SKU_HE_RU484, + SKU_HE_RU996, + SKU_HE_RU2x996, + MAX_SKU_RATE_GROUP_NUM, +}; + +static inline int +mt7915_get_channel_group_5g(int channel, bool is_7976) +{ + if (is_7976) { + if (channel <= 64) + return 0; + if (channel <= 96) + return 1; + if (channel <= 128) + return 2; + if (channel <= 144) + return 3; + return 4; + } + + if (channel >= 184 && channel <= 196) + return 0; + if (channel <= 48) + return 1; + if (channel <= 64) + return 2; + if (channel <= 96) + return 3; + if (channel <= 112) + return 4; + if (channel <= 128) + return 5; + if (channel <= 144) + return 6; + return 7; +} + +static inline int +mt7915_get_channel_group_6g(int channel) +{ + if (channel <= 29) + return 0; + + return DIV_ROUND_UP(channel - 29, 32); +} + +static inline bool +mt7915_tssi_enabled(struct mt7915_dev *dev, enum nl80211_band band) +{ + u8 *eep = dev->mt76.eeprom.data; + u8 offs = is_mt7981(&dev->mt76) ? 8 : 7; + u8 val = eep[MT_EE_WIFI_CONF + offs]; + + if (band == NL80211_BAND_2GHZ) + return val & MT_EE_WIFI_CONF7_TSSI0_2G; + + if (dev->dbdc_support) + return val & MT_EE_WIFI_CONF7_TSSI1_5G; + else + return val & MT_EE_WIFI_CONF7_TSSI0_5G; +} + +static inline u32 +mt7915_get_cal_group_size(struct mt7915_dev *dev) +{ + u8 *eep = dev->mt76.eeprom.data; + u32 val; + + if (is_mt7915(&dev->mt76)) { + return MT_EE_CAL_GROUP_SIZE_7915; + } else if (is_mt7916(&dev->mt76)) { + val = eep[MT_EE_WIFI_CONF + 1]; + val = FIELD_GET(MT_EE_WIFI_CONF0_BAND_SEL, val); + return (val == MT_EE_V2_BAND_SEL_6GHZ) ? MT_EE_CAL_GROUP_SIZE_7916_6G : + MT_EE_CAL_GROUP_SIZE_7916; + } else if (is_mt7981(&dev->mt76)) { + return MT_EE_CAL_GROUP_SIZE_7981; + } else if (mt7915_check_adie(dev, false)) { + return MT_EE_CAL_GROUP_SIZE_7976; + } else { + return MT_EE_CAL_GROUP_SIZE_7975; + } +} + +static inline u32 +mt7915_get_cal_dpd_size(struct mt7915_dev *dev) +{ + if (is_mt7915(&dev->mt76)) + return MT_EE_CAL_DPD_SIZE_V1; + else + return MT_EE_CAL_DPD_SIZE_V2; +} + +extern const u8 mt7915_sku_group_len[MAX_SKU_RATE_GROUP_NUM]; + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/init.c b/drivers/net/wireless/mediatek/mt76/mt7915/init.c new file mode 100644 index 000000000..ca46a203a --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7915/init.c @@ -0,0 +1,1342 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2020 MediaTek Inc. */ + +#include +#include +#include +#include +#include +#include "mt7915.h" +#include "mac.h" +#include "mcu.h" +#include "coredump.h" +#include "eeprom.h" + +static const struct ieee80211_iface_limit if_limits[] = { + { + .max = 1, + .types = BIT(NL80211_IFTYPE_ADHOC) + }, { + .max = 16, + .types = BIT(NL80211_IFTYPE_AP) +#ifdef CONFIG_MAC80211_MESH + | BIT(NL80211_IFTYPE_MESH_POINT) +#endif + }, { + .max = MT7915_MAX_INTERFACES, + .types = BIT(NL80211_IFTYPE_STATION) + } +}; + +static const struct ieee80211_iface_combination if_comb[] = { + { + .limits = if_limits, + .n_limits = ARRAY_SIZE(if_limits), + .max_interfaces = MT7915_MAX_INTERFACES, + .num_different_channels = 1, + .beacon_int_infra_match = true, + .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) | + BIT(NL80211_CHAN_WIDTH_20) | + BIT(NL80211_CHAN_WIDTH_40) | + BIT(NL80211_CHAN_WIDTH_80) | + BIT(NL80211_CHAN_WIDTH_160), + } +}; + +static ssize_t mt7915_thermal_temp_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct mt7915_phy *phy = dev_get_drvdata(dev); + int i = to_sensor_dev_attr(attr)->index; + int temperature; + + switch (i) { + case 0: + mutex_lock(&phy->dev->mt76.mutex); + temperature = mt7915_mcu_get_temperature(phy); + mutex_unlock(&phy->dev->mt76.mutex); + if (temperature < 0) + return temperature; + /* display in millidegree celcius */ + return sprintf(buf, "%u\n", temperature * 1000); + case 1: + case 2: + return sprintf(buf, "%u\n", + phy->throttle_temp[i - 1] * 1000); + case 3: + return sprintf(buf, "%hhu\n", phy->throttle_state); + default: + return -EINVAL; + } +} + +static ssize_t mt7915_thermal_temp_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct mt7915_phy *phy = dev_get_drvdata(dev); + int ret, i = to_sensor_dev_attr(attr)->index; + long val; + + ret = kstrtol(buf, 10, &val); + if (ret < 0) + return ret; + + mutex_lock(&phy->dev->mt76.mutex); + val = DIV_ROUND_CLOSEST(clamp_val(val, 60 * 1000, 130 * 1000), 1000); + + if ((i - 1 == MT7915_CRIT_TEMP_IDX && + val > phy->throttle_temp[MT7915_MAX_TEMP_IDX]) || + (i - 1 == MT7915_MAX_TEMP_IDX && + val < phy->throttle_temp[MT7915_CRIT_TEMP_IDX])) { + dev_err(phy->dev->mt76.dev, + "temp1_max shall be greater than temp1_crit."); + mutex_unlock(&phy->dev->mt76.mutex); + return -EINVAL; + } + + phy->throttle_temp[i - 1] = val; + ret = mt7915_mcu_set_thermal_protect(phy); + mutex_unlock(&phy->dev->mt76.mutex); + if (ret) + return ret; + + return count; +} + +static SENSOR_DEVICE_ATTR_RO(temp1_input, mt7915_thermal_temp, 0); +static SENSOR_DEVICE_ATTR_RW(temp1_crit, mt7915_thermal_temp, 1); +static SENSOR_DEVICE_ATTR_RW(temp1_max, mt7915_thermal_temp, 2); +static SENSOR_DEVICE_ATTR_RO(throttle1, mt7915_thermal_temp, 3); + +static struct attribute *mt7915_hwmon_attrs[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_crit.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_throttle1.dev_attr.attr, + NULL, +}; +ATTRIBUTE_GROUPS(mt7915_hwmon); + +static int +mt7915_thermal_get_max_throttle_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + *state = MT7915_CDEV_THROTTLE_MAX; + + return 0; +} + +static int +mt7915_thermal_get_cur_throttle_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + struct mt7915_phy *phy = cdev->devdata; + + *state = phy->cdev_state; + + return 0; +} + +static int +mt7915_thermal_set_cur_throttle_state(struct thermal_cooling_device *cdev, + unsigned long state) +{ + struct mt7915_phy *phy = cdev->devdata; + u8 throttling = MT7915_THERMAL_THROTTLE_MAX - state; + int ret; + + if (state > MT7915_CDEV_THROTTLE_MAX) { + dev_err(phy->dev->mt76.dev, + "please specify a valid throttling state\n"); + return -EINVAL; + } + + if (state == phy->cdev_state) + return 0; + + /* + * cooling_device convention: 0 = no cooling, more = more cooling + * mcu convention: 1 = max cooling, more = less cooling + */ + mutex_lock(&phy->dev->mt76.mutex); + ret = mt7915_mcu_set_thermal_throttling(phy, throttling); + mutex_unlock(&phy->dev->mt76.mutex); + if (ret) + return ret; + + phy->cdev_state = state; + + return 0; +} + +static const struct thermal_cooling_device_ops mt7915_thermal_ops = { + .get_max_state = mt7915_thermal_get_max_throttle_state, + .get_cur_state = mt7915_thermal_get_cur_throttle_state, + .set_cur_state = mt7915_thermal_set_cur_throttle_state, +}; + +static int mt7915_thermal_get_temp(struct thermal_zone_device *tz, int *temp) +{ + struct mt7915_phy *phy = thermal_zone_device_priv(tz); + int val; + + mutex_lock(&phy->dev->mt76.mutex); + val = mt7915_mcu_get_temperature(phy); + mutex_unlock(&phy->dev->mt76.mutex); + if (val < 0) + return val; + + *temp = val * 1000; + return 0; +} + +static const struct thermal_zone_device_ops mt7915_tz_ops = { + .get_temp = mt7915_thermal_get_temp, +}; + +static void mt7915_unregister_thermal(struct mt7915_phy *phy) +{ + struct wiphy *wiphy = phy->mt76->hw->wiphy; + + if (!phy->cdev) + return; + + sysfs_remove_link(&wiphy->dev.kobj, "cooling_device"); + thermal_cooling_device_unregister(phy->cdev); +} + +static int mt7915_thermal_init(struct mt7915_phy *phy) +{ + struct wiphy *wiphy = phy->mt76->hw->wiphy; + struct thermal_cooling_device *cdev; + struct device *hwmon; + const char *name; + + name = devm_kasprintf(&wiphy->dev, GFP_KERNEL, "mt7915_%s", + wiphy_name(wiphy)); + if (!name) + return -ENOMEM; + + cdev = thermal_cooling_device_register(name, phy, &mt7915_thermal_ops); + if (!IS_ERR(cdev)) { + if (sysfs_create_link(&wiphy->dev.kobj, &cdev->device.kobj, + "cooling_device") < 0) + thermal_cooling_device_unregister(cdev); + else + phy->cdev = cdev; + } + + /* initialize critical/maximum high temperature */ + phy->throttle_temp[MT7915_CRIT_TEMP_IDX] = MT7915_CRIT_TEMP; + phy->throttle_temp[MT7915_MAX_TEMP_IDX] = MT7915_MAX_TEMP; + + phy->tzone = devm_thermal_of_zone_register(phy->dev->mt76.dev, + phy->mt76->band_idx, phy, + &mt7915_tz_ops); + if (IS_ERR(phy->tzone)) { + if (PTR_ERR(phy->tzone) != -ENODEV) + dev_warn(phy->dev->mt76.dev, + "failed to register thermal zone: %ld\n", + PTR_ERR(phy->tzone)); + phy->tzone = NULL; + } + + if (!IS_REACHABLE(CONFIG_HWMON)) + return 0; + + hwmon = devm_hwmon_device_register_with_groups(&wiphy->dev, name, phy, + mt7915_hwmon_groups); + return PTR_ERR_OR_ZERO(hwmon); +} + +static void mt7915_led_set_config(struct led_classdev *led_cdev, + u8 delay_on, u8 delay_off) +{ + struct mt7915_dev *dev; + struct mt76_phy *mphy; + u32 val; + + mphy = container_of(led_cdev, struct mt76_phy, leds.cdev); + dev = container_of(mphy->dev, struct mt7915_dev, mt76); + + /* set PWM mode */ + val = FIELD_PREP(MT_LED_STATUS_DURATION, 0xffff) | + FIELD_PREP(MT_LED_STATUS_OFF, delay_off) | + FIELD_PREP(MT_LED_STATUS_ON, delay_on); + mt76_wr(dev, MT_LED_STATUS_0(mphy->band_idx), val); + mt76_wr(dev, MT_LED_STATUS_1(mphy->band_idx), val); + + /* enable LED */ + mt76_wr(dev, MT_LED_EN(mphy->band_idx), 1); + + /* control LED */ + val = MT_LED_CTRL_KICK; + if (dev->mphy.leds.al) + val |= MT_LED_CTRL_POLARITY; + if (mphy->band_idx) + val |= MT_LED_CTRL_BAND; + + mt76_wr(dev, MT_LED_CTRL(mphy->band_idx), val); + mt76_clear(dev, MT_LED_CTRL(mphy->band_idx), MT_LED_CTRL_KICK); +} + +static int mt7915_led_set_blink(struct led_classdev *led_cdev, + unsigned long *delay_on, + unsigned long *delay_off) +{ + u16 delta_on = 0, delta_off = 0; + +#define HW_TICK 10 +#define TO_HW_TICK(_t) (((_t) > HW_TICK) ? ((_t) / HW_TICK) : HW_TICK) + + if (*delay_on) + delta_on = TO_HW_TICK(*delay_on); + if (*delay_off) + delta_off = TO_HW_TICK(*delay_off); + + mt7915_led_set_config(led_cdev, delta_on, delta_off); + + return 0; +} + +static void mt7915_led_set_brightness(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + if (!brightness) + mt7915_led_set_config(led_cdev, 0, 0xff); + else + mt7915_led_set_config(led_cdev, 0xff, 0); +} + +static void __mt7915_init_txpower(struct mt7915_phy *phy, + struct ieee80211_supported_band *sband) +{ + struct mt7915_dev *dev = phy->dev; + int i, n_chains = hweight16(phy->mt76->chainmask); + int path_delta = mt76_tx_power_path_delta(n_chains); + int pwr_delta = mt7915_eeprom_get_power_delta(dev, sband->band); + struct mt76_power_limits limits; + + phy->sku_limit_en = true; + phy->sku_path_en = true; + for (i = 0; i < sband->n_channels; i++) { + struct ieee80211_channel *chan = &sband->channels[i]; + u32 target_power = 0; + int j; + + for (j = 0; j < n_chains; j++) { + u32 val; + + val = mt7915_eeprom_get_target_power(dev, chan, j); + target_power = max(target_power, val); + } + + target_power += pwr_delta; + target_power = mt76_get_rate_power_limits(phy->mt76, chan, + &limits, + target_power); + + /* MT7915N can not enable Backoff table without setting value in dts */ + if (!limits.path.ofdm[0]) + phy->sku_path_en = false; + + target_power += path_delta; + target_power = DIV_ROUND_UP(target_power, 2); + chan->max_power = min_t(int, chan->max_reg_power, + target_power); + chan->orig_mpwr = target_power; + } +} + +void mt7915_init_txpower(struct mt7915_phy *phy) +{ + if (!phy) + return; + + if (phy->mt76->cap.has_2ghz) + __mt7915_init_txpower(phy, &phy->mt76->sband_2g.sband); + if (phy->mt76->cap.has_5ghz) + __mt7915_init_txpower(phy, &phy->mt76->sband_5g.sband); + if (phy->mt76->cap.has_6ghz) + __mt7915_init_txpower(phy, &phy->mt76->sband_6g.sband); +} + +static void +mt7915_regd_notifier(struct wiphy *wiphy, + struct regulatory_request *request) +{ + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct mt7915_dev *dev = mt7915_hw_dev(hw); + struct mt76_phy *mphy = hw->priv; + struct mt7915_phy *phy = mphy->priv; + + memcpy(dev->mt76.alpha2, request->alpha2, sizeof(dev->mt76.alpha2)); + dev->mt76.region = request->dfs_region; + + if (dev->mt76.region == NL80211_DFS_UNSET) + mt7915_mcu_rdd_background_enable(phy, NULL); + + mt7915_init_txpower(phy); + + mphy->dfs_state = MT_DFS_STATE_UNKNOWN; + mt7915_dfs_init_radar_detector(phy); +} + +static void +mt7915_init_wiphy(struct mt7915_phy *phy) +{ + struct mt76_phy *mphy = phy->mt76; + struct ieee80211_hw *hw = mphy->hw; + struct mt76_dev *mdev = &phy->dev->mt76; + struct wiphy *wiphy = hw->wiphy; + struct mt7915_dev *dev = phy->dev; + + hw->queues = 4; + hw->max_rx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF_HE; + hw->max_tx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF_HE; + hw->netdev_features = NETIF_F_RXCSUM; + + if (mtk_wed_device_active(&mdev->mmio.wed)) + hw->netdev_features |= NETIF_F_HW_TC; + + hw->radiotap_timestamp.units_pos = + IEEE80211_RADIOTAP_TIMESTAMP_UNIT_US; + + phy->slottime = 9; + + hw->sta_data_size = sizeof(struct mt7915_sta); + hw->vif_data_size = sizeof(struct mt7915_vif); + + wiphy->iface_combinations = if_comb; + wiphy->n_iface_combinations = ARRAY_SIZE(if_comb); + wiphy->reg_notifier = mt7915_regd_notifier; + wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH; + wiphy->mbssid_max_interfaces = 16; + + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BSS_COLOR); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_VHT_IBSS); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_RATE_LEGACY); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_RATE_HT); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_RATE_VHT); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_RATE_HE); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_UNSOL_BCAST_PROBE_RESP); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_FILS_DISCOVERY); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_CAN_REPLACE_PTK0); + + if (!is_mt7915(&dev->mt76)) + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_STA_TX_PWR); + + if (mt7915_eeprom_has_background_radar(phy->dev) && + (!mdev->dev->of_node || + !of_property_read_bool(mdev->dev->of_node, + "mediatek,disable-radar-background"))) + wiphy_ext_feature_set(wiphy, + NL80211_EXT_FEATURE_RADAR_BACKGROUND); + + ieee80211_hw_set(hw, HAS_RATE_CONTROL); + ieee80211_hw_set(hw, SUPPORTS_TX_ENCAP_OFFLOAD); + ieee80211_hw_set(hw, SUPPORTS_RX_DECAP_OFFLOAD); + ieee80211_hw_set(hw, SUPPORTS_MULTI_BSSID); + ieee80211_hw_set(hw, WANT_MONITOR_VIF); + ieee80211_hw_set(hw, SUPPORTS_TX_FRAG); + + hw->max_tx_fragments = 4; + + if (phy->mt76->cap.has_2ghz) { + phy->mt76->sband_2g.sband.ht_cap.cap |= + IEEE80211_HT_CAP_LDPC_CODING | + IEEE80211_HT_CAP_MAX_AMSDU; + if (is_mt7915(&dev->mt76)) + phy->mt76->sband_2g.sband.ht_cap.ampdu_density = + IEEE80211_HT_MPDU_DENSITY_4; + else + phy->mt76->sband_2g.sband.ht_cap.ampdu_density = + IEEE80211_HT_MPDU_DENSITY_2; + } + + if (phy->mt76->cap.has_5ghz) { + struct ieee80211_sta_vht_cap *vht_cap; + + vht_cap = &phy->mt76->sband_5g.sband.vht_cap; + phy->mt76->sband_5g.sband.ht_cap.cap |= + IEEE80211_HT_CAP_LDPC_CODING | + IEEE80211_HT_CAP_MAX_AMSDU; + + if (is_mt7915(&dev->mt76)) { + phy->mt76->sband_5g.sband.ht_cap.ampdu_density = + IEEE80211_HT_MPDU_DENSITY_4; + + vht_cap->cap |= + IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991 | + IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK; + + if (!dev->dbdc_support) + vht_cap->cap |= + IEEE80211_VHT_CAP_SHORT_GI_160 | + FIELD_PREP(IEEE80211_VHT_CAP_EXT_NSS_BW_MASK, 1); + } else { + phy->mt76->sband_5g.sband.ht_cap.ampdu_density = + IEEE80211_HT_MPDU_DENSITY_2; + + vht_cap->cap |= + IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 | + IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK; + + /* mt7916 dbdc with 2g 2x2 bw40 and 5g 2x2 bw160c */ + vht_cap->cap |= + IEEE80211_VHT_CAP_SHORT_GI_160 | + IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; + } + + if (!is_mt7915(&dev->mt76) || !dev->dbdc_support) + ieee80211_hw_set(hw, SUPPORTS_VHT_EXT_NSS_BW); + } + + mt76_set_stream_caps(phy->mt76, true); + mt7915_set_stream_vht_txbf_caps(phy); + mt7915_set_stream_he_caps(phy); + mt7915_init_txpower(phy); + + wiphy->available_antennas_rx = phy->mt76->antenna_mask; + wiphy->available_antennas_tx = phy->mt76->antenna_mask; + + /* init led callbacks */ + if (IS_ENABLED(CONFIG_MT76_LEDS)) { + mphy->leds.cdev.brightness_set = mt7915_led_set_brightness; + mphy->leds.cdev.blink_set = mt7915_led_set_blink; + } +} + +static void +mt7915_mac_init_band(struct mt7915_dev *dev, u8 band) +{ + u32 mask, set; + + mt76_rmw_field(dev, MT_TMAC_CTCR0(band), + MT_TMAC_CTCR0_INS_DDLMT_REFTIME, 0x3f); + mt76_set(dev, MT_TMAC_CTCR0(band), + MT_TMAC_CTCR0_INS_DDLMT_VHT_SMPDU_EN | + MT_TMAC_CTCR0_INS_DDLMT_EN); + + mask = MT_MDP_RCFR0_MCU_RX_MGMT | + MT_MDP_RCFR0_MCU_RX_CTL_NON_BAR | + MT_MDP_RCFR0_MCU_RX_CTL_BAR; + set = FIELD_PREP(MT_MDP_RCFR0_MCU_RX_MGMT, MT_MDP_TO_HIF) | + FIELD_PREP(MT_MDP_RCFR0_MCU_RX_CTL_NON_BAR, MT_MDP_TO_HIF) | + FIELD_PREP(MT_MDP_RCFR0_MCU_RX_CTL_BAR, MT_MDP_TO_HIF); + mt76_rmw(dev, MT_MDP_BNRCFR0(band), mask, set); + + mask = MT_MDP_RCFR1_MCU_RX_BYPASS | + MT_MDP_RCFR1_RX_DROPPED_UCAST | + MT_MDP_RCFR1_RX_DROPPED_MCAST; + set = FIELD_PREP(MT_MDP_RCFR1_MCU_RX_BYPASS, MT_MDP_TO_HIF) | + FIELD_PREP(MT_MDP_RCFR1_RX_DROPPED_UCAST, MT_MDP_TO_HIF) | + FIELD_PREP(MT_MDP_RCFR1_RX_DROPPED_MCAST, MT_MDP_TO_HIF); + mt76_rmw(dev, MT_MDP_BNRCFR1(band), mask, set); + + mt76_rmw_field(dev, MT_DMA_DCR0(band), MT_DMA_DCR0_MAX_RX_LEN, 0x680); + + /* mt7915: disable rx rate report by default due to hw issues */ + mt76_clear(dev, MT_DMA_DCR0(band), MT_DMA_DCR0_RXD_G5_EN); + + /* clear estimated value of EIFS for Rx duration & OBSS time */ + mt76_wr(dev, MT_WF_RMAC_RSVD0(band), MT_WF_RMAC_RSVD0_EIFS_CLR); + + /* clear backoff time for Rx duration */ + mt76_clear(dev, MT_WF_RMAC_MIB_AIRTIME1(band), + MT_WF_RMAC_MIB_NONQOSD_BACKOFF); + mt76_clear(dev, MT_WF_RMAC_MIB_AIRTIME3(band), + MT_WF_RMAC_MIB_QOS01_BACKOFF); + mt76_clear(dev, MT_WF_RMAC_MIB_AIRTIME4(band), + MT_WF_RMAC_MIB_QOS23_BACKOFF); + + /* clear backoff time for Tx duration */ + mt76_clear(dev, MT_WTBLOFF_TOP_ACR(band), + MT_WTBLOFF_TOP_ADM_BACKOFFTIME); + + /* exclude estimated backoff time for Tx duration on MT7915 */ + if (is_mt7915(&dev->mt76)) + mt76_set(dev, MT_AGG_ATCR0(band), + MT_AGG_ATCR_MAC_BFF_TIME_EN); + + /* clear backoff time and set software compensation for OBSS time */ + mask = MT_WF_RMAC_MIB_OBSS_BACKOFF | MT_WF_RMAC_MIB_ED_OFFSET; + set = FIELD_PREP(MT_WF_RMAC_MIB_OBSS_BACKOFF, 0) | + FIELD_PREP(MT_WF_RMAC_MIB_ED_OFFSET, 4); + mt76_rmw(dev, MT_WF_RMAC_MIB_AIRTIME0(band), mask, set); + + /* filter out non-resp frames and get instanstaeous signal reporting */ + mask = MT_WTBLOFF_TOP_RSCR_RCPI_MODE | MT_WTBLOFF_TOP_RSCR_RCPI_PARAM; + set = FIELD_PREP(MT_WTBLOFF_TOP_RSCR_RCPI_MODE, 0) | + FIELD_PREP(MT_WTBLOFF_TOP_RSCR_RCPI_PARAM, 0x3); + mt76_rmw(dev, MT_WTBLOFF_TOP_RSCR(band), mask, set); + + /* MT_TXD5_TX_STATUS_HOST (MPDU format) has higher priority than + * MT_AGG_ACR_PPDU_TXS2H (PPDU format) even though ACR bit is set. + */ + if (mtk_wed_device_active(&dev->mt76.mmio.wed)) + mt76_set(dev, MT_AGG_ACR4(band), MT_AGG_ACR_PPDU_TXS2H); +} + +static void +mt7915_init_led_mux(struct mt7915_dev *dev) +{ + if (!IS_ENABLED(CONFIG_MT76_LEDS)) + return; + + if (dev->dbdc_support) { + switch (mt76_chip(&dev->mt76)) { + case 0x7915: + mt76_rmw_field(dev, MT_LED_GPIO_MUX2, + GENMASK(11, 8), 4); + mt76_rmw_field(dev, MT_LED_GPIO_MUX3, + GENMASK(11, 8), 4); + break; + case 0x7986: + mt76_rmw_field(dev, MT_LED_GPIO_MUX0, + GENMASK(7, 4), 1); + mt76_rmw_field(dev, MT_LED_GPIO_MUX0, + GENMASK(11, 8), 1); + break; + case 0x7916: + mt76_rmw_field(dev, MT_LED_GPIO_MUX1, + GENMASK(27, 24), 3); + mt76_rmw_field(dev, MT_LED_GPIO_MUX1, + GENMASK(31, 28), 3); + break; + default: + break; + } + } else if (dev->mphy.leds.pin) { + switch (mt76_chip(&dev->mt76)) { + case 0x7915: + mt76_rmw_field(dev, MT_LED_GPIO_MUX3, + GENMASK(11, 8), 4); + break; + case 0x7986: + mt76_rmw_field(dev, MT_LED_GPIO_MUX0, + GENMASK(11, 8), 1); + break; + case 0x7916: + mt76_rmw_field(dev, MT_LED_GPIO_MUX1, + GENMASK(31, 28), 3); + break; + default: + break; + } + } else { + switch (mt76_chip(&dev->mt76)) { + case 0x7915: + mt76_rmw_field(dev, MT_LED_GPIO_MUX2, + GENMASK(11, 8), 4); + break; + case 0x7986: + mt76_rmw_field(dev, MT_LED_GPIO_MUX0, + GENMASK(7, 4), 1); + break; + case 0x7916: + mt76_rmw_field(dev, MT_LED_GPIO_MUX1, + GENMASK(27, 24), 3); + break; + default: + break; + } + } +} + +void mt7915_mac_init(struct mt7915_dev *dev) +{ + int i; + u32 rx_len = is_mt7915(&dev->mt76) ? 0x400 : 0x680; + + /* config pse qid6 wfdma port selection */ + if (!is_mt7915(&dev->mt76) && dev->hif2) + mt76_rmw(dev, MT_WF_PP_TOP_RXQ_WFDMA_CF_5, 0, + MT_WF_PP_TOP_RXQ_QID6_WFDMA_HIF_SEL_MASK); + + mt76_rmw_field(dev, MT_MDP_DCR1, MT_MDP_DCR1_MAX_RX_LEN, rx_len); + + if (!is_mt7915(&dev->mt76)) + mt76_clear(dev, MT_MDP_DCR2, MT_MDP_DCR2_RX_TRANS_SHORT); + else + mt76_clear(dev, MT_PLE_HOST_RPT0, MT_PLE_HOST_RPT0_TX_LATENCY); + + /* enable hardware de-agg */ + mt76_set(dev, MT_MDP_DCR0, MT_MDP_DCR0_DAMSDU_EN); + + for (i = 0; i < mt7915_wtbl_size(dev); i++) + mt7915_mac_wtbl_update(dev, i, + MT_WTBL_UPDATE_ADM_COUNT_CLEAR); + for (i = 0; i < 2; i++) + mt7915_mac_init_band(dev, i); + + mt7915_init_led_mux(dev); +} + +int mt7915_txbf_init(struct mt7915_dev *dev) +{ + int ret; + + if (dev->dbdc_support) { + ret = mt7915_mcu_set_txbf(dev, MT_BF_MODULE_UPDATE); + if (ret) + return ret; + } + + /* trigger sounding packets */ + ret = mt7915_mcu_set_txbf(dev, MT_BF_SOUNDING_ON); + if (ret) + return ret; + + /* enable eBF */ + return mt7915_mcu_set_txbf(dev, MT_BF_TYPE_UPDATE); +} + +static struct mt7915_phy * +mt7915_alloc_ext_phy(struct mt7915_dev *dev) +{ + struct mt7915_phy *phy; + struct mt76_phy *mphy; + + if (!dev->dbdc_support) + return NULL; + + mphy = mt76_alloc_phy(&dev->mt76, sizeof(*phy), &mt7915_ops, MT_BAND1); + if (!mphy) + return ERR_PTR(-ENOMEM); + + phy = mphy->priv; + phy->dev = dev; + phy->mt76 = mphy; + + /* Bind main phy to band0 and ext_phy to band1 for dbdc case */ + phy->mt76->band_idx = 1; + + return phy; +} + +static int +mt7915_register_ext_phy(struct mt7915_dev *dev, struct mt7915_phy *phy) +{ + struct mt76_phy *mphy = phy->mt76; + int ret; + + INIT_DELAYED_WORK(&mphy->mac_work, mt7915_mac_work); + + mt7915_eeprom_parse_hw_cap(dev, phy); + + memcpy(mphy->macaddr, dev->mt76.eeprom.data + MT_EE_MAC_ADDR2, + ETH_ALEN); + /* Make the secondary PHY MAC address local without overlapping with + * the usual MAC address allocation scheme on multiple virtual interfaces + */ + if (!is_valid_ether_addr(mphy->macaddr)) { + memcpy(mphy->macaddr, dev->mt76.eeprom.data + MT_EE_MAC_ADDR, + ETH_ALEN); + mphy->macaddr[0] |= 2; + mphy->macaddr[0] ^= BIT(7); + } + ret = mt76_eeprom_override(mphy); + if (ret) + return ret; + + /* init wiphy according to mphy and phy */ + mt7915_init_wiphy(phy); + + ret = mt76_register_phy(mphy, true, mt76_rates, + ARRAY_SIZE(mt76_rates)); + if (ret) + return ret; + + ret = mt7915_thermal_init(phy); + if (ret) + goto unreg; + + mt7915_init_debugfs(phy); + + return 0; + +unreg: + mt76_unregister_phy(mphy); + return ret; +} + +static void mt7915_init_work(struct work_struct *work) +{ + struct mt7915_dev *dev = container_of(work, struct mt7915_dev, + init_work); + + mt7915_mcu_set_eeprom(dev); + mt7915_mac_init(dev); + mt7915_txbf_init(dev); +} + +void mt7915_wfsys_reset(struct mt7915_dev *dev) +{ +#define MT_MCU_DUMMY_RANDOM GENMASK(15, 0) +#define MT_MCU_DUMMY_DEFAULT GENMASK(31, 16) + + if (is_mt7915(&dev->mt76)) { + u32 val = MT_TOP_PWR_KEY | MT_TOP_PWR_SW_PWR_ON | MT_TOP_PWR_PWR_ON; + + mt76_wr(dev, MT_MCU_WFDMA0_DUMMY_CR, MT_MCU_DUMMY_RANDOM); + + /* change to software control */ + val |= MT_TOP_PWR_SW_RST; + mt76_wr(dev, MT_TOP_PWR_CTRL, val); + + /* reset wfsys */ + val &= ~MT_TOP_PWR_SW_RST; + mt76_wr(dev, MT_TOP_PWR_CTRL, val); + + /* release wfsys then mcu re-executes romcode */ + val |= MT_TOP_PWR_SW_RST; + mt76_wr(dev, MT_TOP_PWR_CTRL, val); + + /* switch to hw control */ + val &= ~MT_TOP_PWR_SW_RST; + val |= MT_TOP_PWR_HW_CTRL; + mt76_wr(dev, MT_TOP_PWR_CTRL, val); + + /* check whether mcu resets to default */ + if (!mt76_poll_msec(dev, MT_MCU_WFDMA0_DUMMY_CR, + MT_MCU_DUMMY_DEFAULT, MT_MCU_DUMMY_DEFAULT, + 1000)) { + dev_err(dev->mt76.dev, "wifi subsystem reset failure\n"); + return; + } + + /* wfsys reset won't clear host registers */ + mt76_clear(dev, MT_TOP_MISC, MT_TOP_MISC_FW_STATE); + + msleep(100); + } else if (is_mt798x(&dev->mt76)) { + mt7986_wmac_disable(dev); + msleep(20); + + mt7986_wmac_enable(dev); + msleep(20); + } else { + mt76_set(dev, MT_WF_SUBSYS_RST, 0x1); + msleep(20); + + mt76_clear(dev, MT_WF_SUBSYS_RST, 0x1); + msleep(20); + } +} + +static bool mt7915_band_config(struct mt7915_dev *dev) +{ + bool ret = true; + + dev->phy.mt76->band_idx = 0; + + if (is_mt798x(&dev->mt76)) { + u32 sku = mt7915_check_adie(dev, true); + + /* + * for mt7986, dbdc support is determined by the number + * of adie chips and the main phy is bound to band1 when + * dbdc is disabled. + */ + if (sku == MT7975_ONE_ADIE || sku == MT7976_ONE_ADIE) { + dev->phy.mt76->band_idx = 1; + ret = false; + } + } else { + ret = is_mt7915(&dev->mt76) ? + !!(mt76_rr(dev, MT_HW_BOUND) & BIT(5)) : true; + } + + return ret; +} + +static int +mt7915_init_hardware(struct mt7915_dev *dev, struct mt7915_phy *phy2) +{ + int ret, idx; + + mt76_wr(dev, MT_INT_MASK_CSR, 0); + mt76_wr(dev, MT_INT_SOURCE_CSR, ~0); + + INIT_WORK(&dev->init_work, mt7915_init_work); + + ret = mt7915_dma_init(dev, phy2); + if (ret) + return ret; + + set_bit(MT76_STATE_INITIALIZED, &dev->mphy.state); + + ret = mt7915_mcu_init(dev); + if (ret) + return ret; + + ret = mt7915_eeprom_init(dev); + if (ret < 0) + return ret; + + if (dev->cal) { + ret = mt7915_mcu_apply_group_cal(dev); + if (ret) + return ret; + } + + /* Beacon and mgmt frames should occupy wcid 0 */ + idx = mt76_wcid_alloc(dev->mt76.wcid_mask, MT7915_WTBL_STA); + if (idx) + return -ENOSPC; + + dev->mt76.global_wcid.idx = idx; + dev->mt76.global_wcid.hw_key_idx = -1; + dev->mt76.global_wcid.tx_info |= MT_WCID_TX_INFO_SET; + rcu_assign_pointer(dev->mt76.wcid[idx], &dev->mt76.global_wcid); + + return 0; +} + +void mt7915_set_stream_vht_txbf_caps(struct mt7915_phy *phy) +{ + int sts; + u32 *cap; + + if (!phy->mt76->cap.has_5ghz) + return; + + sts = hweight8(phy->mt76->chainmask); + cap = &phy->mt76->sband_5g.sband.vht_cap.cap; + + *cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE | + IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE | + FIELD_PREP(IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK, + sts - 1); + + *cap &= ~(IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK | + IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE | + IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE); + + if (sts < 2) + return; + + *cap |= IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE | + IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE | + FIELD_PREP(IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK, + sts - 1); +} + +static void +mt7915_set_stream_he_txbf_caps(struct mt7915_phy *phy, + struct ieee80211_sta_he_cap *he_cap, int vif) +{ + struct mt7915_dev *dev = phy->dev; + struct ieee80211_he_cap_elem *elem = &he_cap->he_cap_elem; + int sts = hweight8(phy->mt76->chainmask); + u8 c, sts_160 = sts; + + /* Can do 1/2 of STS in 160Mhz mode for mt7915 */ + if (is_mt7915(&dev->mt76)) { + if (!dev->dbdc_support) + sts_160 /= 2; + else + sts_160 = 0; + } + +#ifdef CONFIG_MAC80211_MESH + if (vif == NL80211_IFTYPE_MESH_POINT) + return; +#endif + + elem->phy_cap_info[3] &= ~IEEE80211_HE_PHY_CAP3_SU_BEAMFORMER; + elem->phy_cap_info[4] &= ~IEEE80211_HE_PHY_CAP4_MU_BEAMFORMER; + + c = IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_MASK; + if (sts_160) + c |= IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_MASK; + elem->phy_cap_info[5] &= ~c; + + c = IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMING_FB | + IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMING_PARTIAL_BW_FB; + elem->phy_cap_info[6] &= ~c; + + elem->phy_cap_info[7] &= ~IEEE80211_HE_PHY_CAP7_MAX_NC_MASK; + + c = IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US; + if (!is_mt7915(&dev->mt76)) + c |= IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO; + elem->phy_cap_info[2] |= c; + + c = IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE | + IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_4; + if (sts_160) + c |= IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_4; + elem->phy_cap_info[4] |= c; + + /* do not support NG16 due to spec D4.0 changes subcarrier idx */ + c = IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_42_SU | + IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_75_MU; + + if (vif == NL80211_IFTYPE_STATION) + c |= IEEE80211_HE_PHY_CAP6_PARTIAL_BANDWIDTH_DL_MUMIMO; + + elem->phy_cap_info[6] |= c; + + if (sts < 2) + return; + + /* the maximum cap is 4 x 3, (Nr, Nc) = (3, 2) */ + elem->phy_cap_info[7] |= min_t(int, sts - 1, 2) << 3; + + if (vif != NL80211_IFTYPE_AP && vif != NL80211_IFTYPE_STATION) + return; + + elem->phy_cap_info[3] |= IEEE80211_HE_PHY_CAP3_SU_BEAMFORMER; + + c = FIELD_PREP(IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_MASK, + sts - 1); + if (sts_160) + c |= FIELD_PREP(IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_MASK, + sts_160 - 1); + elem->phy_cap_info[5] |= c; + + if (vif != NL80211_IFTYPE_AP) + return; + + elem->phy_cap_info[4] |= IEEE80211_HE_PHY_CAP4_MU_BEAMFORMER; + + c = IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMING_FB | + IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMING_PARTIAL_BW_FB; + elem->phy_cap_info[6] |= c; + + if (!is_mt7915(&dev->mt76)) { + c = IEEE80211_HE_PHY_CAP7_STBC_TX_ABOVE_80MHZ | + IEEE80211_HE_PHY_CAP7_STBC_RX_ABOVE_80MHZ; + elem->phy_cap_info[7] |= c; + } +} + +static int +mt7915_init_he_caps(struct mt7915_phy *phy, enum nl80211_band band, + struct ieee80211_sband_iftype_data *data) +{ + struct mt7915_dev *dev = phy->dev; + int i, idx = 0, nss = hweight8(phy->mt76->antenna_mask); + u16 mcs_map = 0; + u16 mcs_map_160 = 0; + u8 nss_160; + + if (!is_mt7915(&dev->mt76)) + nss_160 = nss; + else if (!dev->dbdc_support) + /* Can do 1/2 of NSS streams in 160Mhz mode for mt7915 */ + nss_160 = nss / 2; + else + /* Can't do 160MHz with mt7915 dbdc */ + nss_160 = 0; + + for (i = 0; i < 8; i++) { + if (i < nss) + mcs_map |= (IEEE80211_HE_MCS_SUPPORT_0_11 << (i * 2)); + else + mcs_map |= (IEEE80211_HE_MCS_NOT_SUPPORTED << (i * 2)); + + if (i < nss_160) + mcs_map_160 |= (IEEE80211_HE_MCS_SUPPORT_0_11 << (i * 2)); + else + mcs_map_160 |= (IEEE80211_HE_MCS_NOT_SUPPORTED << (i * 2)); + } + + for (i = 0; i < NUM_NL80211_IFTYPES; i++) { + struct ieee80211_sta_he_cap *he_cap = &data[idx].he_cap; + struct ieee80211_he_cap_elem *he_cap_elem = + &he_cap->he_cap_elem; + struct ieee80211_he_mcs_nss_supp *he_mcs = + &he_cap->he_mcs_nss_supp; + + switch (i) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_AP: +#ifdef CONFIG_MAC80211_MESH + case NL80211_IFTYPE_MESH_POINT: +#endif + break; + default: + continue; + } + + data[idx].types_mask = BIT(i); + he_cap->has_he = true; + + he_cap_elem->mac_cap_info[0] = + IEEE80211_HE_MAC_CAP0_HTC_HE; + he_cap_elem->mac_cap_info[3] = + IEEE80211_HE_MAC_CAP3_OMI_CONTROL | + IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_3; + he_cap_elem->mac_cap_info[4] = + IEEE80211_HE_MAC_CAP4_AMSDU_IN_AMPDU; + + if (band == NL80211_BAND_2GHZ) + he_cap_elem->phy_cap_info[0] = + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G; + else if (nss_160) + he_cap_elem->phy_cap_info[0] = + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G | + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G; + else + he_cap_elem->phy_cap_info[0] = + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G; + + he_cap_elem->phy_cap_info[1] = + IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD; + he_cap_elem->phy_cap_info[2] = + IEEE80211_HE_PHY_CAP2_STBC_TX_UNDER_80MHZ | + IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ; + + switch (i) { + case NL80211_IFTYPE_AP: + he_cap_elem->mac_cap_info[0] |= + IEEE80211_HE_MAC_CAP0_TWT_RES; + he_cap_elem->mac_cap_info[2] |= + IEEE80211_HE_MAC_CAP2_BSR; + he_cap_elem->mac_cap_info[4] |= + IEEE80211_HE_MAC_CAP4_BQR; + he_cap_elem->mac_cap_info[5] |= + IEEE80211_HE_MAC_CAP5_OM_CTRL_UL_MU_DATA_DIS_RX; + he_cap_elem->phy_cap_info[3] |= + IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_QPSK | + IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_QPSK; + he_cap_elem->phy_cap_info[6] |= + IEEE80211_HE_PHY_CAP6_PARTIAL_BW_EXT_RANGE | + IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT; + he_cap_elem->phy_cap_info[9] |= + IEEE80211_HE_PHY_CAP9_TX_1024_QAM_LESS_THAN_242_TONE_RU | + IEEE80211_HE_PHY_CAP9_RX_1024_QAM_LESS_THAN_242_TONE_RU; + break; + case NL80211_IFTYPE_STATION: + he_cap_elem->mac_cap_info[1] |= + IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US; + + if (band == NL80211_BAND_2GHZ) + he_cap_elem->phy_cap_info[0] |= + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_RU_MAPPING_IN_2G; + else + he_cap_elem->phy_cap_info[0] |= + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_RU_MAPPING_IN_5G; + + he_cap_elem->phy_cap_info[1] |= + IEEE80211_HE_PHY_CAP1_DEVICE_CLASS_A | + IEEE80211_HE_PHY_CAP1_HE_LTF_AND_GI_FOR_HE_PPDUS_0_8US; + he_cap_elem->phy_cap_info[3] |= + IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_QPSK | + IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_QPSK; + he_cap_elem->phy_cap_info[6] |= + IEEE80211_HE_PHY_CAP6_TRIG_CQI_FB | + IEEE80211_HE_PHY_CAP6_PARTIAL_BW_EXT_RANGE | + IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT; + he_cap_elem->phy_cap_info[7] |= + IEEE80211_HE_PHY_CAP7_POWER_BOOST_FACTOR_SUPP | + IEEE80211_HE_PHY_CAP7_HE_SU_MU_PPDU_4XLTF_AND_08_US_GI; + he_cap_elem->phy_cap_info[8] |= + IEEE80211_HE_PHY_CAP8_20MHZ_IN_40MHZ_HE_PPDU_IN_2G | + IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_484; + if (nss_160) + he_cap_elem->phy_cap_info[8] |= + IEEE80211_HE_PHY_CAP8_20MHZ_IN_160MHZ_HE_PPDU | + IEEE80211_HE_PHY_CAP8_80MHZ_IN_160MHZ_HE_PPDU; + he_cap_elem->phy_cap_info[9] |= + IEEE80211_HE_PHY_CAP9_LONGER_THAN_16_SIGB_OFDM_SYM | + IEEE80211_HE_PHY_CAP9_NON_TRIGGERED_CQI_FEEDBACK | + IEEE80211_HE_PHY_CAP9_TX_1024_QAM_LESS_THAN_242_TONE_RU | + IEEE80211_HE_PHY_CAP9_RX_1024_QAM_LESS_THAN_242_TONE_RU | + IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_COMP_SIGB | + IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_NON_COMP_SIGB; + break; + } + + memset(he_mcs, 0, sizeof(*he_mcs)); + he_mcs->rx_mcs_80 = cpu_to_le16(mcs_map); + he_mcs->tx_mcs_80 = cpu_to_le16(mcs_map); + he_mcs->rx_mcs_160 = cpu_to_le16(mcs_map_160); + he_mcs->tx_mcs_160 = cpu_to_le16(mcs_map_160); + + mt7915_set_stream_he_txbf_caps(phy, he_cap, i); + + memset(he_cap->ppe_thres, 0, sizeof(he_cap->ppe_thres)); + if (he_cap_elem->phy_cap_info[6] & + IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT) { + mt76_connac_gen_ppe_thresh(he_cap->ppe_thres, nss, band); + } else { + he_cap_elem->phy_cap_info[9] |= + u8_encode_bits(IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_16US, + IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_MASK); + } + + if (band == NL80211_BAND_6GHZ) { + u16 cap = IEEE80211_HE_6GHZ_CAP_TX_ANTPAT_CONS | + IEEE80211_HE_6GHZ_CAP_RX_ANTPAT_CONS; + + cap |= u16_encode_bits(IEEE80211_HT_MPDU_DENSITY_2, + IEEE80211_HE_6GHZ_CAP_MIN_MPDU_START) | + u16_encode_bits(IEEE80211_VHT_MAX_AMPDU_1024K, + IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP) | + u16_encode_bits(IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454, + IEEE80211_HE_6GHZ_CAP_MAX_MPDU_LEN); + + data[idx].he_6ghz_capa.capa = cpu_to_le16(cap); + } + + idx++; + } + + return idx; +} + +void mt7915_set_stream_he_caps(struct mt7915_phy *phy) +{ + struct ieee80211_sband_iftype_data *data; + struct ieee80211_supported_band *band; + int n; + + if (phy->mt76->cap.has_2ghz) { + data = phy->iftype[NL80211_BAND_2GHZ]; + n = mt7915_init_he_caps(phy, NL80211_BAND_2GHZ, data); + + band = &phy->mt76->sband_2g.sband; + _ieee80211_set_sband_iftype_data(band, data, n); + } + + if (phy->mt76->cap.has_5ghz) { + data = phy->iftype[NL80211_BAND_5GHZ]; + n = mt7915_init_he_caps(phy, NL80211_BAND_5GHZ, data); + + band = &phy->mt76->sband_5g.sband; + _ieee80211_set_sband_iftype_data(band, data, n); + } + + if (phy->mt76->cap.has_6ghz) { + data = phy->iftype[NL80211_BAND_6GHZ]; + n = mt7915_init_he_caps(phy, NL80211_BAND_6GHZ, data); + + band = &phy->mt76->sband_6g.sband; + _ieee80211_set_sband_iftype_data(band, data, n); + } +} + +static void mt7915_unregister_ext_phy(struct mt7915_dev *dev) +{ + struct mt7915_phy *phy = mt7915_ext_phy(dev); + struct mt76_phy *mphy = dev->mt76.phys[MT_BAND1]; + + if (!phy) + return; + + mt7915_unregister_thermal(phy); + mt76_unregister_phy(mphy); + ieee80211_free_hw(mphy->hw); +} + +static void mt7915_stop_hardware(struct mt7915_dev *dev) +{ + mt7915_mcu_exit(dev); + mt76_connac2_tx_token_put(&dev->mt76); + mt7915_dma_cleanup(dev); + tasklet_disable(&dev->mt76.irq_tasklet); + + if (is_mt798x(&dev->mt76)) + mt7986_wmac_disable(dev); +} + +int mt7915_register_device(struct mt7915_dev *dev) +{ + struct mt7915_phy *phy2; + int ret; + + dev->phy.dev = dev; + dev->phy.mt76 = &dev->mt76.phy; + dev->mt76.phy.priv = &dev->phy; + INIT_WORK(&dev->rc_work, mt7915_mac_sta_rc_work); + INIT_DELAYED_WORK(&dev->mphy.mac_work, mt7915_mac_work); + INIT_LIST_HEAD(&dev->sta_rc_list); + INIT_LIST_HEAD(&dev->twt_list); + + init_waitqueue_head(&dev->reset_wait); + INIT_WORK(&dev->reset_work, mt7915_mac_reset_work); + INIT_WORK(&dev->dump_work, mt7915_mac_dump_work); + mutex_init(&dev->dump_mutex); + + dev->dbdc_support = mt7915_band_config(dev); + + phy2 = mt7915_alloc_ext_phy(dev); + if (IS_ERR(phy2)) + return PTR_ERR(phy2); + + ret = mt7915_init_hardware(dev, phy2); + if (ret) + goto free_phy2; + + mt7915_init_wiphy(&dev->phy); + +#ifdef CONFIG_NL80211_TESTMODE + dev->mt76.test_ops = &mt7915_testmode_ops; +#endif + + ret = mt76_register_device(&dev->mt76, true, mt76_rates, + ARRAY_SIZE(mt76_rates)); + if (ret) + goto stop_hw; + + ret = mt7915_thermal_init(&dev->phy); + if (ret) + goto unreg_dev; + + if (phy2) { + ret = mt7915_register_ext_phy(dev, phy2); + if (ret) + goto unreg_thermal; + } + + ieee80211_queue_work(mt76_hw(dev), &dev->init_work); + + dev->recovery.hw_init_done = true; + + ret = mt7915_init_debugfs(&dev->phy); + if (ret) + goto unreg_ext_phy; + + ret = mt7915_coredump_register(dev); + if (ret) + goto unreg_ext_phy; + + return 0; + +unreg_ext_phy: + if (phy2) { + mt7915_unregister_ext_phy(dev); + phy2 = NULL; + } +unreg_thermal: + mt7915_unregister_thermal(&dev->phy); +unreg_dev: + mt76_unregister_device(&dev->mt76); +stop_hw: + mt7915_stop_hardware(dev); +free_phy2: + if (phy2) + ieee80211_free_hw(phy2->mt76->hw); + return ret; +} + +void mt7915_unregister_device(struct mt7915_dev *dev) +{ + cancel_work_sync(&dev->dump_work); + cancel_work_sync(&dev->reset_work); + cancel_work_sync(&dev->rc_work); + mt7915_unregister_ext_phy(dev); + mt7915_coredump_unregister(dev); + mt7915_unregister_thermal(&dev->phy); + mt76_unregister_device(&dev->mt76); + mt7915_stop_hardware(dev); + + mt76_free_device(&dev->mt76); +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c new file mode 100644 index 000000000..b3d4abf6a --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c @@ -0,0 +1,2411 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2020 MediaTek Inc. */ + +#include +#include +#include "coredump.h" +#include "mt7915.h" +#include "../dma.h" +#include "mac.h" +#include "mcu.h" + +#define to_rssi(field, rcpi) ((FIELD_GET(field, rcpi) - 220) / 2) + +static const struct mt7915_dfs_radar_spec etsi_radar_specs = { + .pulse_th = { 110, -10, -80, 40, 5200, 128, 5200 }, + .radar_pattern = { + [5] = { 1, 0, 6, 32, 28, 0, 990, 5010, 17, 1, 1 }, + [6] = { 1, 0, 9, 32, 28, 0, 615, 5010, 27, 1, 1 }, + [7] = { 1, 0, 15, 32, 28, 0, 240, 445, 27, 1, 1 }, + [8] = { 1, 0, 12, 32, 28, 0, 240, 510, 42, 1, 1 }, + [9] = { 1, 1, 0, 0, 0, 0, 2490, 3343, 14, 0, 0, 12, 32, 28, { }, 126 }, + [10] = { 1, 1, 0, 0, 0, 0, 2490, 3343, 14, 0, 0, 15, 32, 24, { }, 126 }, + [11] = { 1, 1, 0, 0, 0, 0, 823, 2510, 14, 0, 0, 18, 32, 28, { }, 54 }, + [12] = { 1, 1, 0, 0, 0, 0, 823, 2510, 14, 0, 0, 27, 32, 24, { }, 54 }, + }, +}; + +static const struct mt7915_dfs_radar_spec fcc_radar_specs = { + .pulse_th = { 110, -10, -80, 40, 5200, 128, 5200 }, + .radar_pattern = { + [0] = { 1, 0, 8, 32, 28, 0, 508, 3076, 13, 1, 1 }, + [1] = { 1, 0, 12, 32, 28, 0, 140, 240, 17, 1, 1 }, + [2] = { 1, 0, 8, 32, 28, 0, 190, 510, 22, 1, 1 }, + [3] = { 1, 0, 6, 32, 28, 0, 190, 510, 32, 1, 1 }, + [4] = { 1, 0, 9, 255, 28, 0, 323, 343, 13, 1, 32 }, + }, +}; + +static const struct mt7915_dfs_radar_spec jp_radar_specs = { + .pulse_th = { 110, -10, -80, 40, 5200, 128, 5200 }, + .radar_pattern = { + [0] = { 1, 0, 8, 32, 28, 0, 508, 3076, 13, 1, 1 }, + [1] = { 1, 0, 12, 32, 28, 0, 140, 240, 17, 1, 1 }, + [2] = { 1, 0, 8, 32, 28, 0, 190, 510, 22, 1, 1 }, + [3] = { 1, 0, 6, 32, 28, 0, 190, 510, 32, 1, 1 }, + [4] = { 1, 0, 9, 255, 28, 0, 323, 343, 13, 1, 32 }, + [13] = { 1, 0, 7, 32, 28, 0, 3836, 3856, 14, 1, 1 }, + [14] = { 1, 0, 6, 32, 28, 0, 615, 5010, 110, 1, 1 }, + [15] = { 1, 1, 0, 0, 0, 0, 15, 5010, 110, 0, 0, 12, 32, 28 }, + }, +}; + +static struct mt76_wcid *mt7915_rx_get_wcid(struct mt7915_dev *dev, + u16 idx, bool unicast) +{ + struct mt7915_sta *sta; + struct mt76_wcid *wcid; + + wcid = mt76_wcid_ptr(dev, idx); + if (unicast || !wcid) + return wcid; + + if (!wcid->sta) + return NULL; + + sta = container_of(wcid, struct mt7915_sta, wcid); + if (!sta->vif) + return NULL; + + return &sta->vif->sta.wcid; +} + +bool mt7915_mac_wtbl_update(struct mt7915_dev *dev, int idx, u32 mask) +{ + mt76_rmw(dev, MT_WTBL_UPDATE, MT_WTBL_UPDATE_WLAN_IDX, + FIELD_PREP(MT_WTBL_UPDATE_WLAN_IDX, idx) | mask); + + return mt76_poll(dev, MT_WTBL_UPDATE, MT_WTBL_UPDATE_BUSY, + 0, 5000); +} + +u32 mt7915_mac_wtbl_lmac_addr(struct mt7915_dev *dev, u16 wcid, u8 dw) +{ + mt76_wr(dev, MT_WTBLON_TOP_WDUCR, + FIELD_PREP(MT_WTBLON_TOP_WDUCR_GROUP, (wcid >> 7))); + + return MT_WTBL_LMAC_OFFS(wcid, dw); +} + +static void mt7915_mac_sta_poll(struct mt7915_dev *dev) +{ + static const u8 ac_to_tid[] = { + [IEEE80211_AC_BE] = 0, + [IEEE80211_AC_BK] = 1, + [IEEE80211_AC_VI] = 4, + [IEEE80211_AC_VO] = 6 + }; + struct ieee80211_sta *sta; + struct mt7915_sta *msta; + struct rate_info *rate; + u32 tx_time[IEEE80211_NUM_ACS], rx_time[IEEE80211_NUM_ACS]; + LIST_HEAD(sta_poll_list); + int i; + + spin_lock_bh(&dev->mt76.sta_poll_lock); + list_splice_init(&dev->mt76.sta_poll_list, &sta_poll_list); + spin_unlock_bh(&dev->mt76.sta_poll_lock); + + rcu_read_lock(); + + while (true) { + bool clear = false; + u32 addr, val; + u16 idx; + s8 rssi[4]; + u8 bw; + + spin_lock_bh(&dev->mt76.sta_poll_lock); + if (list_empty(&sta_poll_list)) { + spin_unlock_bh(&dev->mt76.sta_poll_lock); + break; + } + msta = list_first_entry(&sta_poll_list, + struct mt7915_sta, wcid.poll_list); + list_del_init(&msta->wcid.poll_list); + spin_unlock_bh(&dev->mt76.sta_poll_lock); + + idx = msta->wcid.idx; + + /* refresh peer's airtime reporting */ + addr = mt7915_mac_wtbl_lmac_addr(dev, idx, 20); + + for (i = 0; i < IEEE80211_NUM_ACS; i++) { + u32 tx_last = msta->airtime_ac[i]; + u32 rx_last = msta->airtime_ac[i + 4]; + + msta->airtime_ac[i] = mt76_rr(dev, addr); + msta->airtime_ac[i + 4] = mt76_rr(dev, addr + 4); + + if (msta->airtime_ac[i] <= tx_last) + tx_time[i] = 0; + else + tx_time[i] = msta->airtime_ac[i] - tx_last; + + if (msta->airtime_ac[i + 4] <= rx_last) + rx_time[i] = 0; + else + rx_time[i] = msta->airtime_ac[i + 4] - rx_last; + + if ((tx_last | rx_last) & BIT(30)) + clear = true; + + addr += 8; + } + + if (clear) { + mt7915_mac_wtbl_update(dev, idx, + MT_WTBL_UPDATE_ADM_COUNT_CLEAR); + memset(msta->airtime_ac, 0, sizeof(msta->airtime_ac)); + } + + if (!msta->wcid.sta) + continue; + + sta = container_of((void *)msta, struct ieee80211_sta, + drv_priv); + for (i = 0; i < IEEE80211_NUM_ACS; i++) { + u8 queue = mt76_connac_lmac_mapping(i); + u32 tx_cur = tx_time[queue]; + u32 rx_cur = rx_time[queue]; + u8 tid = ac_to_tid[i]; + + if (!tx_cur && !rx_cur) + continue; + + ieee80211_sta_register_airtime(sta, tid, tx_cur, + rx_cur); + } + + /* + * We don't support reading GI info from txs packets. + * For accurate tx status reporting and AQL improvement, + * we need to make sure that flags match so polling GI + * from per-sta counters directly. + */ + rate = &msta->wcid.rate; + addr = mt7915_mac_wtbl_lmac_addr(dev, idx, 7); + val = mt76_rr(dev, addr); + + switch (rate->bw) { + case RATE_INFO_BW_160: + bw = IEEE80211_STA_RX_BW_160; + break; + case RATE_INFO_BW_80: + bw = IEEE80211_STA_RX_BW_80; + break; + case RATE_INFO_BW_40: + bw = IEEE80211_STA_RX_BW_40; + break; + default: + bw = IEEE80211_STA_RX_BW_20; + break; + } + + if (rate->flags & RATE_INFO_FLAGS_HE_MCS) { + u8 offs = 24 + 2 * bw; + + rate->he_gi = (val & (0x3 << offs)) >> offs; + } else if (rate->flags & + (RATE_INFO_FLAGS_VHT_MCS | RATE_INFO_FLAGS_MCS)) { + if (val & BIT(12 + bw)) + rate->flags |= RATE_INFO_FLAGS_SHORT_GI; + else + rate->flags &= ~RATE_INFO_FLAGS_SHORT_GI; + } + + /* get signal strength of resp frames (CTS/BA/ACK) */ + addr = mt7915_mac_wtbl_lmac_addr(dev, idx, 30); + val = mt76_rr(dev, addr); + + rssi[0] = to_rssi(GENMASK(7, 0), val); + rssi[1] = to_rssi(GENMASK(15, 8), val); + rssi[2] = to_rssi(GENMASK(23, 16), val); + rssi[3] = to_rssi(GENMASK(31, 24), val); + + msta->ack_signal = + mt76_rx_signal(msta->vif->phy->mt76->antenna_mask, rssi); + + ewma_avg_signal_add(&msta->avg_ack_signal, -msta->ack_signal); + } + + rcu_read_unlock(); +} + +static void +mt7915_wed_check_ppe(struct mt7915_dev *dev, struct mt76_queue *q, + struct mt7915_sta *msta, struct sk_buff *skb, + u32 info) +{ + struct ieee80211_vif *vif; + struct wireless_dev *wdev; + + if (!msta || !msta->vif) + return; + + if (!mt76_queue_is_wed_rx(q)) + return; + + if (!(info & MT_DMA_INFO_PPE_VLD)) + return; + + vif = container_of((void *)msta->vif, struct ieee80211_vif, + drv_priv); + wdev = ieee80211_vif_to_wdev(vif); + skb->dev = wdev->netdev; + + mtk_wed_device_ppe_check(&dev->mt76.mmio.wed, skb, + FIELD_GET(MT_DMA_PPE_CPU_REASON, info), + FIELD_GET(MT_DMA_PPE_ENTRY, info)); +} + +static int +mt7915_mac_fill_rx(struct mt7915_dev *dev, struct sk_buff *skb, + enum mt76_rxq_id q, u32 *info) +{ + struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb; + struct mt76_phy *mphy = &dev->mt76.phy; + struct mt7915_phy *phy = &dev->phy; + struct ieee80211_supported_band *sband; + __le32 *rxd = (__le32 *)skb->data; + __le32 *rxv = NULL; + u32 rxd0 = le32_to_cpu(rxd[0]); + u32 rxd1 = le32_to_cpu(rxd[1]); + u32 rxd2 = le32_to_cpu(rxd[2]); + u32 rxd3 = le32_to_cpu(rxd[3]); + u32 rxd4 = le32_to_cpu(rxd[4]); + u32 csum_mask = MT_RXD0_NORMAL_IP_SUM | MT_RXD0_NORMAL_UDP_TCP_SUM; + bool unicast, insert_ccmp_hdr = false; + u8 remove_pad, amsdu_info; + u8 mode = 0, qos_ctl = 0; + struct mt7915_sta *msta = NULL; + u32 csum_status = *(u32 *)skb->cb; + bool hdr_trans; + u16 hdr_gap; + u16 seq_ctrl = 0; + __le16 fc = 0; + int idx; + + memset(status, 0, sizeof(*status)); + + if ((rxd1 & MT_RXD1_NORMAL_BAND_IDX) && !phy->mt76->band_idx) { + mphy = dev->mt76.phys[MT_BAND1]; + if (!mphy) + return -EINVAL; + + phy = mphy->priv; + status->phy_idx = 1; + } + + if (!test_bit(MT76_STATE_RUNNING, &mphy->state)) + return -EINVAL; + + if (rxd2 & MT_RXD2_NORMAL_AMSDU_ERR) + return -EINVAL; + + hdr_trans = rxd2 & MT_RXD2_NORMAL_HDR_TRANS; + if (hdr_trans && (rxd1 & MT_RXD1_NORMAL_CM)) + return -EINVAL; + + /* ICV error or CCMP/BIP/WPI MIC error */ + if (rxd1 & MT_RXD1_NORMAL_ICV_ERR) + status->flag |= RX_FLAG_ONLY_MONITOR; + + unicast = FIELD_GET(MT_RXD3_NORMAL_ADDR_TYPE, rxd3) == MT_RXD3_NORMAL_U2M; + idx = FIELD_GET(MT_RXD1_NORMAL_WLAN_IDX, rxd1); + status->wcid = mt7915_rx_get_wcid(dev, idx, unicast); + + if (status->wcid) { + msta = container_of(status->wcid, struct mt7915_sta, wcid); + mt76_wcid_add_poll(&dev->mt76, &msta->wcid); + } + + status->freq = mphy->chandef.chan->center_freq; + status->band = mphy->chandef.chan->band; + if (status->band == NL80211_BAND_5GHZ) + sband = &mphy->sband_5g.sband; + else if (status->band == NL80211_BAND_6GHZ) + sband = &mphy->sband_6g.sband; + else + sband = &mphy->sband_2g.sband; + + if (!sband->channels) + return -EINVAL; + + if ((rxd0 & csum_mask) == csum_mask && + !(csum_status & (BIT(0) | BIT(2) | BIT(3)))) + skb->ip_summed = CHECKSUM_UNNECESSARY; + + if (rxd1 & MT_RXD1_NORMAL_FCS_ERR) + status->flag |= RX_FLAG_FAILED_FCS_CRC; + + if (rxd1 & MT_RXD1_NORMAL_TKIP_MIC_ERR) + status->flag |= RX_FLAG_MMIC_ERROR; + + if (FIELD_GET(MT_RXD1_NORMAL_SEC_MODE, rxd1) != 0 && + !(rxd1 & (MT_RXD1_NORMAL_CLM | MT_RXD1_NORMAL_CM))) { + status->flag |= RX_FLAG_DECRYPTED; + status->flag |= RX_FLAG_IV_STRIPPED; + status->flag |= RX_FLAG_MMIC_STRIPPED | RX_FLAG_MIC_STRIPPED; + } + + remove_pad = FIELD_GET(MT_RXD2_NORMAL_HDR_OFFSET, rxd2); + + if (rxd2 & MT_RXD2_NORMAL_MAX_LEN_ERROR) + return -EINVAL; + + rxd += 6; + if (rxd1 & MT_RXD1_NORMAL_GROUP_4) { + u32 v0 = le32_to_cpu(rxd[0]); + u32 v2 = le32_to_cpu(rxd[2]); + + fc = cpu_to_le16(FIELD_GET(MT_RXD6_FRAME_CONTROL, v0)); + qos_ctl = FIELD_GET(MT_RXD8_QOS_CTL, v2); + seq_ctrl = FIELD_GET(MT_RXD8_SEQ_CTRL, v2); + + rxd += 4; + if ((u8 *)rxd - skb->data >= skb->len) + return -EINVAL; + } + + if (rxd1 & MT_RXD1_NORMAL_GROUP_1) { + u8 *data = (u8 *)rxd; + + if (status->flag & RX_FLAG_DECRYPTED) { + switch (FIELD_GET(MT_RXD1_NORMAL_SEC_MODE, rxd1)) { + case MT_CIPHER_AES_CCMP: + case MT_CIPHER_CCMP_CCX: + case MT_CIPHER_CCMP_256: + insert_ccmp_hdr = + FIELD_GET(MT_RXD2_NORMAL_FRAG, rxd2); + fallthrough; + case MT_CIPHER_TKIP: + case MT_CIPHER_TKIP_NO_MIC: + case MT_CIPHER_GCMP: + case MT_CIPHER_GCMP_256: + status->iv[0] = data[5]; + status->iv[1] = data[4]; + status->iv[2] = data[3]; + status->iv[3] = data[2]; + status->iv[4] = data[1]; + status->iv[5] = data[0]; + break; + default: + break; + } + } + rxd += 4; + if ((u8 *)rxd - skb->data >= skb->len) + return -EINVAL; + } + + if (rxd1 & MT_RXD1_NORMAL_GROUP_2) { + status->timestamp = le32_to_cpu(rxd[0]); + status->flag |= RX_FLAG_MACTIME_START; + + if (!(rxd2 & MT_RXD2_NORMAL_NON_AMPDU)) { + status->flag |= RX_FLAG_AMPDU_DETAILS; + + /* all subframes of an A-MPDU have the same timestamp */ + if (phy->rx_ampdu_ts != status->timestamp) { + if (!++phy->ampdu_ref) + phy->ampdu_ref++; + } + phy->rx_ampdu_ts = status->timestamp; + + status->ampdu_ref = phy->ampdu_ref; + } + + rxd += 2; + if ((u8 *)rxd - skb->data >= skb->len) + return -EINVAL; + } + + /* RXD Group 3 - P-RXV */ + if (rxd1 & MT_RXD1_NORMAL_GROUP_3) { + u32 v0, v1; + int ret; + + rxv = rxd; + rxd += 2; + if ((u8 *)rxd - skb->data >= skb->len) + return -EINVAL; + + v0 = le32_to_cpu(rxv[0]); + v1 = le32_to_cpu(rxv[1]); + + if (v0 & MT_PRXV_HT_AD_CODE) + status->enc_flags |= RX_ENC_FLAG_LDPC; + + status->chains = mt7915_band_chainmask(phy); + status->chain_signal[0] = to_rssi(MT_PRXV_RCPI0, v1); + status->chain_signal[1] = to_rssi(MT_PRXV_RCPI1, v1); + status->chain_signal[2] = to_rssi(MT_PRXV_RCPI2, v1); + status->chain_signal[3] = to_rssi(MT_PRXV_RCPI3, v1); + + /* RXD Group 5 - C-RXV */ + if (rxd1 & MT_RXD1_NORMAL_GROUP_5) { + rxd += 18; + if ((u8 *)rxd - skb->data >= skb->len) + return -EINVAL; + } + + if (!is_mt7915(&dev->mt76) || (rxd1 & MT_RXD1_NORMAL_GROUP_5)) { + ret = mt76_connac2_mac_fill_rx_rate(&dev->mt76, status, + sband, rxv, &mode); + if (ret < 0) + return ret; + } + } + + amsdu_info = FIELD_GET(MT_RXD4_NORMAL_PAYLOAD_FORMAT, rxd4); + status->amsdu = !!amsdu_info; + if (status->amsdu) { + status->first_amsdu = amsdu_info == MT_RXD4_FIRST_AMSDU_FRAME; + status->last_amsdu = amsdu_info == MT_RXD4_LAST_AMSDU_FRAME; + } + + hdr_gap = (u8 *)rxd - skb->data + 2 * remove_pad; + if (hdr_trans && ieee80211_has_morefrags(fc)) { + struct ieee80211_vif *vif; + int err; + + if (!msta || !msta->vif) + return -EINVAL; + + vif = container_of((void *)msta->vif, struct ieee80211_vif, + drv_priv); + err = mt76_connac2_reverse_frag0_hdr_trans(vif, skb, hdr_gap); + if (err) + return err; + + hdr_trans = false; + } else { + int pad_start = 0; + + skb_pull(skb, hdr_gap); + if (!hdr_trans && status->amsdu) { + pad_start = ieee80211_get_hdrlen_from_skb(skb); + } else if (hdr_trans && (rxd2 & MT_RXD2_NORMAL_HDR_TRANS_ERROR)) { + /* + * When header translation failure is indicated, + * the hardware will insert an extra 2-byte field + * containing the data length after the protocol + * type field. This happens either when the LLC-SNAP + * pattern did not match, or if a VLAN header was + * detected. + */ + pad_start = 12; + if (get_unaligned_be16(skb->data + pad_start) == ETH_P_8021Q) + pad_start += 4; + else + pad_start = 0; + } + + if (pad_start) { + memmove(skb->data + 2, skb->data, pad_start); + skb_pull(skb, 2); + } + } + + if (!hdr_trans) { + struct ieee80211_hdr *hdr; + + if (insert_ccmp_hdr) { + u8 key_id = FIELD_GET(MT_RXD1_NORMAL_KEY_ID, rxd1); + + mt76_insert_ccmp_hdr(skb, key_id); + } + + hdr = mt76_skb_get_hdr(skb); + fc = hdr->frame_control; + if (ieee80211_is_data_qos(fc)) { + seq_ctrl = le16_to_cpu(hdr->seq_ctrl); + qos_ctl = *ieee80211_get_qos_ctl(hdr); + } + } else { + status->flag |= RX_FLAG_8023; + mt7915_wed_check_ppe(dev, &dev->mt76.q_rx[q], msta, skb, + *info); + } + + if (rxv && mode >= MT_PHY_TYPE_HE_SU && !(status->flag & RX_FLAG_8023)) + mt76_connac2_mac_decode_he_radiotap(&dev->mt76, skb, rxv, mode); + + if (!status->wcid || !ieee80211_is_data_qos(fc)) + return 0; + + status->aggr = unicast && + !ieee80211_is_qos_nullfunc(fc); + status->qos_ctl = qos_ctl; + status->seqno = IEEE80211_SEQ_TO_SN(seq_ctrl); + + return 0; +} + +static void +mt7915_mac_fill_rx_vector(struct mt7915_dev *dev, struct sk_buff *skb) +{ +#ifdef CONFIG_NL80211_TESTMODE + struct mt7915_phy *phy = &dev->phy; + __le32 *rxd = (__le32 *)skb->data; + __le32 *rxv_hdr = rxd + 2; + __le32 *rxv = rxd + 4; + u32 rcpi, ib_rssi, wb_rssi, v20, v21; + u8 band_idx; + s32 foe; + u8 snr; + int i; + + band_idx = le32_get_bits(rxv_hdr[1], MT_RXV_HDR_BAND_IDX); + if (band_idx && !phy->mt76->band_idx) { + phy = mt7915_ext_phy(dev); + if (!phy) + goto out; + } + + rcpi = le32_to_cpu(rxv[6]); + ib_rssi = le32_to_cpu(rxv[7]); + wb_rssi = le32_to_cpu(rxv[8]) >> 5; + + for (i = 0; i < 4; i++, rcpi >>= 8, ib_rssi >>= 8, wb_rssi >>= 9) { + if (i == 3) + wb_rssi = le32_to_cpu(rxv[9]); + + phy->test.last_rcpi[i] = rcpi & 0xff; + phy->test.last_ib_rssi[i] = ib_rssi & 0xff; + phy->test.last_wb_rssi[i] = wb_rssi & 0xff; + } + + v20 = le32_to_cpu(rxv[20]); + v21 = le32_to_cpu(rxv[21]); + + foe = FIELD_GET(MT_CRXV_FOE_LO, v20) | + (FIELD_GET(MT_CRXV_FOE_HI, v21) << MT_CRXV_FOE_SHIFT); + + snr = FIELD_GET(MT_CRXV_SNR, v20) - 16; + + phy->test.last_freq_offset = foe; + phy->test.last_snr = snr; +out: +#endif + dev_kfree_skb(skb); +} + +static void +mt7915_mac_write_txwi_tm(struct mt7915_phy *phy, __le32 *txwi, + struct sk_buff *skb) +{ +#ifdef CONFIG_NL80211_TESTMODE + struct mt76_testmode_data *td = &phy->mt76->test; + const struct ieee80211_rate *r; + u8 bw, mode, nss = td->tx_rate_nss; + u8 rate_idx = td->tx_rate_idx; + u16 rateval = 0; + u32 val; + bool cck = false; + int band; + + if (skb != phy->mt76->test.tx_skb) + return; + + switch (td->tx_rate_mode) { + case MT76_TM_TX_MODE_HT: + nss = 1 + (rate_idx >> 3); + mode = MT_PHY_TYPE_HT; + break; + case MT76_TM_TX_MODE_VHT: + mode = MT_PHY_TYPE_VHT; + break; + case MT76_TM_TX_MODE_HE_SU: + mode = MT_PHY_TYPE_HE_SU; + break; + case MT76_TM_TX_MODE_HE_EXT_SU: + mode = MT_PHY_TYPE_HE_EXT_SU; + break; + case MT76_TM_TX_MODE_HE_TB: + mode = MT_PHY_TYPE_HE_TB; + break; + case MT76_TM_TX_MODE_HE_MU: + mode = MT_PHY_TYPE_HE_MU; + break; + case MT76_TM_TX_MODE_CCK: + cck = true; + fallthrough; + case MT76_TM_TX_MODE_OFDM: + band = phy->mt76->chandef.chan->band; + if (band == NL80211_BAND_2GHZ && !cck) + rate_idx += 4; + + r = &phy->mt76->hw->wiphy->bands[band]->bitrates[rate_idx]; + val = cck ? r->hw_value_short : r->hw_value; + + mode = val >> 8; + rate_idx = val & 0xff; + break; + default: + mode = MT_PHY_TYPE_OFDM; + break; + } + + switch (phy->mt76->chandef.width) { + case NL80211_CHAN_WIDTH_40: + bw = 1; + break; + case NL80211_CHAN_WIDTH_80: + bw = 2; + break; + case NL80211_CHAN_WIDTH_80P80: + case NL80211_CHAN_WIDTH_160: + bw = 3; + break; + default: + bw = 0; + break; + } + + if (td->tx_rate_stbc && nss == 1) { + nss++; + rateval |= MT_TX_RATE_STBC; + } + + rateval |= FIELD_PREP(MT_TX_RATE_IDX, rate_idx) | + FIELD_PREP(MT_TX_RATE_MODE, mode) | + FIELD_PREP(MT_TX_RATE_NSS, nss - 1); + + txwi[2] |= cpu_to_le32(MT_TXD2_FIX_RATE); + + le32p_replace_bits(&txwi[3], 1, MT_TXD3_REM_TX_COUNT); + if (td->tx_rate_mode < MT76_TM_TX_MODE_HT) + txwi[3] |= cpu_to_le32(MT_TXD3_BA_DISABLE); + + val = MT_TXD6_FIXED_BW | + FIELD_PREP(MT_TXD6_BW, bw) | + FIELD_PREP(MT_TXD6_TX_RATE, rateval) | + FIELD_PREP(MT_TXD6_SGI, td->tx_rate_sgi); + + /* for HE_SU/HE_EXT_SU PPDU + * - 1x, 2x, 4x LTF + 0.8us GI + * - 2x LTF + 1.6us GI, 4x LTF + 3.2us GI + * for HE_MU PPDU + * - 2x, 4x LTF + 0.8us GI + * - 2x LTF + 1.6us GI, 4x LTF + 3.2us GI + * for HE_TB PPDU + * - 1x, 2x LTF + 1.6us GI + * - 4x LTF + 3.2us GI + */ + if (mode >= MT_PHY_TYPE_HE_SU) + val |= FIELD_PREP(MT_TXD6_HELTF, td->tx_ltf); + + if (td->tx_rate_ldpc || (bw > 0 && mode >= MT_PHY_TYPE_HE_SU)) + val |= MT_TXD6_LDPC; + + txwi[3] &= ~cpu_to_le32(MT_TXD3_SN_VALID); + txwi[6] |= cpu_to_le32(val); + txwi[7] |= cpu_to_le32(FIELD_PREP(MT_TXD7_SPE_IDX, + phy->test.spe_idx)); +#endif +} + +void mt7915_mac_write_txwi(struct mt76_dev *dev, __le32 *txwi, + struct sk_buff *skb, struct mt76_wcid *wcid, int pid, + struct ieee80211_key_conf *key, + enum mt76_txq_id qid, u32 changed) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + u8 phy_idx = (info->hw_queue & MT_TX_HW_QUEUE_PHY) >> 2; + struct mt76_phy *mphy = &dev->phy; + + if (phy_idx && dev->phys[MT_BAND1]) + mphy = dev->phys[MT_BAND1]; + + mt76_connac2_mac_write_txwi(dev, txwi, skb, wcid, key, pid, qid, changed); + + if (mt76_testmode_enabled(mphy)) + mt7915_mac_write_txwi_tm(mphy->priv, txwi, skb); +} + +int mt7915_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, + enum mt76_txq_id qid, struct mt76_wcid *wcid, + struct ieee80211_sta *sta, + struct mt76_tx_info *tx_info) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx_info->skb->data; + struct mt7915_dev *dev = container_of(mdev, struct mt7915_dev, mt76); + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx_info->skb); + struct ieee80211_key_conf *key = info->control.hw_key; + struct ieee80211_vif *vif = info->control.vif; + struct mt76_connac_fw_txp *txp; + struct mt76_txwi_cache *t; + int id, i, nbuf = tx_info->nbuf - 1; + u8 *txwi = (u8 *)txwi_ptr; + int pid; + + if (unlikely(tx_info->skb->len <= ETH_HLEN)) + return -EINVAL; + + if (!wcid) + wcid = &dev->mt76.global_wcid; + + if (sta) { + struct mt7915_sta *msta; + + msta = (struct mt7915_sta *)sta->drv_priv; + + if (time_after(jiffies, msta->jiffies + HZ / 4)) { + info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; + msta->jiffies = jiffies; + } + } + + t = (struct mt76_txwi_cache *)(txwi + mdev->drv->txwi_size); + t->skb = tx_info->skb; + + id = mt76_token_consume(mdev, &t); + if (id < 0) + return id; + + pid = mt76_tx_status_skb_add(mdev, wcid, tx_info->skb); + mt7915_mac_write_txwi(mdev, txwi_ptr, tx_info->skb, wcid, pid, key, + qid, 0); + + txp = (struct mt76_connac_fw_txp *)(txwi + MT_TXD_SIZE); + for (i = 0; i < nbuf; i++) { + txp->buf[i] = cpu_to_le32(tx_info->buf[i + 1].addr); + txp->len[i] = cpu_to_le16(tx_info->buf[i + 1].len); + } + txp->nbuf = nbuf; + + txp->flags = cpu_to_le16(MT_CT_INFO_APPLY_TXD | MT_CT_INFO_FROM_HOST); + + if (!key) + txp->flags |= cpu_to_le16(MT_CT_INFO_NONE_CIPHER_FRAME); + + if (!(info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP) && + ieee80211_is_mgmt(hdr->frame_control)) + txp->flags |= cpu_to_le16(MT_CT_INFO_MGMT_FRAME); + + if (vif) { + struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; + + txp->bss_idx = mvif->mt76.idx; + } + + txp->token = cpu_to_le16(id); + if (test_bit(MT_WCID_FLAG_4ADDR, &wcid->flags)) + txp->rept_wds_wcid = cpu_to_le16(wcid->idx); + else + txp->rept_wds_wcid = cpu_to_le16(0x3ff); + tx_info->skb = NULL; + + /* pass partial skb header to fw */ + tx_info->buf[1].len = MT_CT_PARSE_LEN; + tx_info->buf[1].skip_unmap = true; + tx_info->nbuf = MT_CT_DMA_BUF_NUM; + + return 0; +} + +u32 mt7915_wed_init_buf(void *ptr, dma_addr_t phys, int token_id) +{ + struct mt76_connac_fw_txp *txp = ptr + MT_TXD_SIZE; + __le32 *txwi = ptr; + u32 val; + + memset(ptr, 0, MT_TXD_SIZE + sizeof(*txp)); + + val = FIELD_PREP(MT_TXD0_TX_BYTES, MT_TXD_SIZE) | + FIELD_PREP(MT_TXD0_PKT_FMT, MT_TX_TYPE_CT); + txwi[0] = cpu_to_le32(val); + + val = MT_TXD1_LONG_FORMAT | + FIELD_PREP(MT_TXD1_HDR_FORMAT, MT_HDR_FORMAT_802_3); + txwi[1] = cpu_to_le32(val); + + txp->token = cpu_to_le16(token_id); + txp->nbuf = 1; + txp->buf[0] = cpu_to_le32(phys + MT_TXD_SIZE + sizeof(*txp)); + + return MT_TXD_SIZE + sizeof(*txp); +} + +static void +mt7915_mac_tx_free_prepare(struct mt7915_dev *dev) +{ + struct mt76_dev *mdev = &dev->mt76; + struct mt76_phy *mphy_ext = mdev->phys[MT_BAND1]; + + /* clean DMA queues and unmap buffers first */ + mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[MT_TXQ_PSD], false); + mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[MT_TXQ_BE], false); + if (mphy_ext) { + mt76_queue_tx_cleanup(dev, mphy_ext->q_tx[MT_TXQ_PSD], false); + mt76_queue_tx_cleanup(dev, mphy_ext->q_tx[MT_TXQ_BE], false); + } +} + +static void +mt7915_mac_tx_free_done(struct mt7915_dev *dev, + struct list_head *free_list, bool wake) +{ + struct sk_buff *skb, *tmp; + + mt7915_mac_sta_poll(dev); + + if (wake) + mt76_set_tx_blocked(&dev->mt76, false); + + mt76_worker_schedule(&dev->mt76.tx_worker); + + list_for_each_entry_safe(skb, tmp, free_list, list) { + skb_list_del_init(skb); + napi_consume_skb(skb, 1); + } +} + +static void +mt7915_mac_tx_free(struct mt7915_dev *dev, void *data, int len) +{ + struct mt76_connac_tx_free *free = data; + __le32 *tx_info = (__le32 *)(data + sizeof(*free)); + struct mt76_dev *mdev = &dev->mt76; + struct mt76_txwi_cache *txwi; + struct ieee80211_sta *sta = NULL; + struct mt76_wcid *wcid = NULL; + LIST_HEAD(free_list); + void *end = data + len; + bool v3, wake = false; + u16 total, count = 0; + u32 txd = le32_to_cpu(free->txd); + __le32 *cur_info; + + mt7915_mac_tx_free_prepare(dev); + + total = le16_get_bits(free->ctrl, MT_TX_FREE_MSDU_CNT); + v3 = (FIELD_GET(MT_TX_FREE_VER, txd) == 0x4); + + for (cur_info = tx_info; count < total; cur_info++) { + u32 msdu, info; + u8 i; + + if (WARN_ON_ONCE((void *)cur_info >= end)) + return; + + /* + * 1'b1: new wcid pair. + * 1'b0: msdu_id with the same 'wcid pair' as above. + */ + info = le32_to_cpu(*cur_info); + if (info & MT_TX_FREE_PAIR) { + struct mt7915_sta *msta; + u16 idx; + + idx = FIELD_GET(MT_TX_FREE_WLAN_ID, info); + wcid = mt76_wcid_ptr(dev, idx); + sta = wcid_to_sta(wcid); + if (!sta) + continue; + + msta = container_of(wcid, struct mt7915_sta, wcid); + mt76_wcid_add_poll(&dev->mt76, &msta->wcid); + continue; + } + + if (!mtk_wed_device_active(&mdev->mmio.wed) && wcid) { + u32 tx_retries = 0, tx_failed = 0, count; + + if (v3 && (info & MT_TX_FREE_MPDU_HEADER_V3)) { + count = FIELD_GET(MT_TX_FREE_COUNT_V3, info); + tx_retries = count ? count - 1 : 0; + tx_failed = tx_retries + + !!FIELD_GET(MT_TX_FREE_STAT_V3, info); + } else if (!v3 && (info & MT_TX_FREE_MPDU_HEADER)) { + count = FIELD_GET(MT_TX_FREE_COUNT, info); + tx_retries = count ? count - 1 : 0; + tx_failed = tx_retries + + !!FIELD_GET(MT_TX_FREE_STAT, info); + } + wcid->stats.tx_retries += tx_retries; + wcid->stats.tx_failed += tx_failed; + } + + if (v3 && (info & MT_TX_FREE_MPDU_HEADER_V3)) + continue; + + for (i = 0; i < 1 + v3; i++) { + if (v3) { + msdu = (info >> (15 * i)) & MT_TX_FREE_MSDU_ID_V3; + if (msdu == MT_TX_FREE_MSDU_ID_V3) + continue; + } else { + msdu = FIELD_GET(MT_TX_FREE_MSDU_ID, info); + } + count++; + txwi = mt76_token_release(mdev, msdu, &wake); + if (!txwi) + continue; + + mt76_connac2_txwi_free(mdev, txwi, sta, &free_list); + } + } + + mt7915_mac_tx_free_done(dev, &free_list, wake); +} + +static void +mt7915_mac_tx_free_v0(struct mt7915_dev *dev, void *data, int len) +{ + struct mt76_connac_tx_free *free = data; + __le16 *info = (__le16 *)(data + sizeof(*free)); + struct mt76_dev *mdev = &dev->mt76; + void *end = data + len; + LIST_HEAD(free_list); + bool wake = false; + u8 i, count; + + mt7915_mac_tx_free_prepare(dev); + + count = FIELD_GET(MT_TX_FREE_MSDU_CNT_V0, le16_to_cpu(free->ctrl)); + if (WARN_ON_ONCE((void *)&info[count] > end)) + return; + + for (i = 0; i < count; i++) { + struct mt76_txwi_cache *txwi; + u16 msdu = le16_to_cpu(info[i]); + + txwi = mt76_token_release(mdev, msdu, &wake); + if (!txwi) + continue; + + mt76_connac2_txwi_free(mdev, txwi, NULL, &free_list); + } + + mt7915_mac_tx_free_done(dev, &free_list, wake); +} + +static void mt7915_mac_add_txs(struct mt7915_dev *dev, void *data) +{ + struct mt7915_sta *msta = NULL; + struct mt76_wcid *wcid; + __le32 *txs_data = data; + u16 wcidx; + u8 pid; + + wcidx = le32_get_bits(txs_data[2], MT_TXS2_WCID); + pid = le32_get_bits(txs_data[3], MT_TXS3_PID); + + if (pid < MT_PACKET_ID_WED) + return; + + rcu_read_lock(); + + wcid = mt76_wcid_ptr(dev, wcidx); + if (!wcid) + goto out; + + msta = container_of(wcid, struct mt7915_sta, wcid); + + if (pid == MT_PACKET_ID_WED) + mt76_connac2_mac_fill_txs(&dev->mt76, wcid, txs_data); + else + mt76_connac2_mac_add_txs_skb(&dev->mt76, wcid, pid, txs_data); + + if (!wcid->sta) + goto out; + + mt76_wcid_add_poll(&dev->mt76, &msta->wcid); + +out: + rcu_read_unlock(); +} + +bool mt7915_rx_check(struct mt76_dev *mdev, void *data, int len) +{ + struct mt7915_dev *dev = container_of(mdev, struct mt7915_dev, mt76); + __le32 *rxd = (__le32 *)data; + __le32 *end = (__le32 *)&rxd[len / 4]; + enum rx_pkt_type type; + + type = le32_get_bits(rxd[0], MT_RXD0_PKT_TYPE); + + switch (type) { + case PKT_TYPE_TXRX_NOTIFY: + mt7915_mac_tx_free(dev, data, len); + return false; + case PKT_TYPE_TXRX_NOTIFY_V0: + mt7915_mac_tx_free_v0(dev, data, len); + return false; + case PKT_TYPE_TXS: + for (rxd += 2; rxd + 8 <= end; rxd += 8) + mt7915_mac_add_txs(dev, rxd); + return false; + case PKT_TYPE_RX_FW_MONITOR: + mt7915_debugfs_rx_fw_monitor(dev, data, len); + return false; + default: + return true; + } +} + +void mt7915_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, + struct sk_buff *skb, u32 *info) +{ + struct mt7915_dev *dev = container_of(mdev, struct mt7915_dev, mt76); + __le32 *rxd = (__le32 *)skb->data; + __le32 *end = (__le32 *)&skb->data[skb->len]; + enum rx_pkt_type type; + + type = le32_get_bits(rxd[0], MT_RXD0_PKT_TYPE); + + switch (type) { + case PKT_TYPE_TXRX_NOTIFY: + mt7915_mac_tx_free(dev, skb->data, skb->len); + napi_consume_skb(skb, 1); + break; + case PKT_TYPE_TXRX_NOTIFY_V0: + mt7915_mac_tx_free_v0(dev, skb->data, skb->len); + napi_consume_skb(skb, 1); + break; + case PKT_TYPE_RX_EVENT: + mt7915_mcu_rx_event(dev, skb); + break; + case PKT_TYPE_TXRXV: + mt7915_mac_fill_rx_vector(dev, skb); + break; + case PKT_TYPE_TXS: + for (rxd += 2; rxd + 8 <= end; rxd += 8) + mt7915_mac_add_txs(dev, rxd); + dev_kfree_skb(skb); + break; + case PKT_TYPE_RX_FW_MONITOR: + mt7915_debugfs_rx_fw_monitor(dev, skb->data, skb->len); + dev_kfree_skb(skb); + break; + case PKT_TYPE_NORMAL: + if (!mt7915_mac_fill_rx(dev, skb, q, info)) { + mt76_rx(&dev->mt76, q, skb); + return; + } + fallthrough; + default: + dev_kfree_skb(skb); + break; + } +} + +void mt7915_mac_cca_stats_reset(struct mt7915_phy *phy) +{ + struct mt7915_dev *dev = phy->dev; + u32 reg = MT_WF_PHY_RX_CTRL1(phy->mt76->band_idx); + + mt76_clear(dev, reg, MT_WF_PHY_RX_CTRL1_STSCNT_EN); + mt76_set(dev, reg, BIT(11) | BIT(9)); +} + +void mt7915_mac_reset_counters(struct mt7915_phy *phy) +{ + struct mt7915_dev *dev = phy->dev; + int i; + + for (i = 0; i < 4; i++) { + mt76_rr(dev, MT_TX_AGG_CNT(phy->mt76->band_idx, i)); + mt76_rr(dev, MT_TX_AGG_CNT2(phy->mt76->band_idx, i)); + } + + phy->mt76->survey_time = ktime_get_boottime(); + memset(phy->mt76->aggr_stats, 0, sizeof(phy->mt76->aggr_stats)); + + /* reset airtime counters */ + mt76_set(dev, MT_WF_RMAC_MIB_AIRTIME0(phy->mt76->band_idx), + MT_WF_RMAC_MIB_RXTIME_CLR); + + mt7915_mcu_get_chan_mib_info(phy, true); +} + +void mt7915_mac_set_timing(struct mt7915_phy *phy) +{ + s16 coverage_class = phy->coverage_class; + struct mt7915_dev *dev = phy->dev; + struct mt7915_phy *ext_phy = mt7915_ext_phy(dev); + u32 val, reg_offset; + u32 cck = FIELD_PREP(MT_TIMEOUT_VAL_PLCP, 231) | + FIELD_PREP(MT_TIMEOUT_VAL_CCA, 48); + u32 ofdm = FIELD_PREP(MT_TIMEOUT_VAL_PLCP, 60) | + FIELD_PREP(MT_TIMEOUT_VAL_CCA, 28); + u8 band = phy->mt76->band_idx; + int eifs_ofdm = 84, sifs = 10, offset; + bool a_band = !(phy->mt76->chandef.chan->band == NL80211_BAND_2GHZ); + + if (!test_bit(MT76_STATE_RUNNING, &phy->mt76->state)) + return; + + if (ext_phy) + coverage_class = max_t(s16, dev->phy.coverage_class, + ext_phy->coverage_class); + + mt76_set(dev, MT_ARB_SCR(band), + MT_ARB_SCR_TX_DISABLE | MT_ARB_SCR_RX_DISABLE); + udelay(1); + + offset = 3 * coverage_class; + reg_offset = FIELD_PREP(MT_TIMEOUT_VAL_PLCP, offset) | + FIELD_PREP(MT_TIMEOUT_VAL_CCA, offset); + + if (!is_mt7915(&dev->mt76)) { + if (!a_band) { + mt76_wr(dev, MT_TMAC_ICR1(band), + FIELD_PREP(MT_IFS_EIFS_CCK, 314)); + eifs_ofdm = 78; + } else { + eifs_ofdm = 84; + } + } else if (a_band) { + sifs = 16; + } + + mt76_wr(dev, MT_TMAC_CDTR(band), cck + reg_offset); + mt76_wr(dev, MT_TMAC_ODTR(band), ofdm + reg_offset); + mt76_wr(dev, MT_TMAC_ICR0(band), + FIELD_PREP(MT_IFS_EIFS_OFDM, eifs_ofdm) | + FIELD_PREP(MT_IFS_RIFS, 2) | + FIELD_PREP(MT_IFS_SIFS, sifs) | + FIELD_PREP(MT_IFS_SLOT, phy->slottime)); + + if (phy->slottime < 20 || a_band) + val = MT7915_CFEND_RATE_DEFAULT; + else + val = MT7915_CFEND_RATE_11B; + + mt76_rmw_field(dev, MT_AGG_ACR0(band), MT_AGG_ACR_CFEND_RATE, val); + mt76_clear(dev, MT_ARB_SCR(band), + MT_ARB_SCR_TX_DISABLE | MT_ARB_SCR_RX_DISABLE); +} + +void mt7915_mac_enable_nf(struct mt7915_dev *dev, bool band) +{ + u32 reg; + + reg = is_mt7915(&dev->mt76) ? MT_WF_PHY_RXTD12(band) : + MT_WF_PHY_RXTD12_MT7916(band); + mt76_set(dev, reg, + MT_WF_PHY_RXTD12_IRPI_SW_CLR_ONLY | + MT_WF_PHY_RXTD12_IRPI_SW_CLR); + + reg = is_mt7915(&dev->mt76) ? MT_WF_PHY_RX_CTRL1(band) : + MT_WF_PHY_RX_CTRL1_MT7916(band); + mt76_set(dev, reg, FIELD_PREP(MT_WF_PHY_RX_CTRL1_IPI_EN, 0x5)); +} + +static u8 +mt7915_phy_get_nf(struct mt7915_phy *phy, int idx) +{ + static const u8 nf_power[] = { 92, 89, 86, 83, 80, 75, 70, 65, 60, 55, 52 }; + struct mt7915_dev *dev = phy->dev; + u32 val, sum = 0, n = 0; + int nss, i; + + for (nss = 0; nss < hweight8(phy->mt76->chainmask); nss++) { + u32 reg = is_mt7915(&dev->mt76) ? + MT_WF_IRPI_NSS(0, nss + (idx << dev->dbdc_support)) : + MT_WF_IRPI_NSS_MT7916(idx, nss); + + for (i = 0; i < ARRAY_SIZE(nf_power); i++, reg += 4) { + val = mt76_rr(dev, reg); + sum += val * nf_power[i]; + n += val; + } + } + + if (!n) + return 0; + + return sum / n; +} + +void mt7915_update_channel(struct mt76_phy *mphy) +{ + struct mt7915_phy *phy = mphy->priv; + struct mt76_channel_state *state = mphy->chan_state; + int nf; + + mt7915_mcu_get_chan_mib_info(phy, false); + + nf = mt7915_phy_get_nf(phy, phy->mt76->band_idx); + if (!phy->noise) + phy->noise = nf << 4; + else if (nf) + phy->noise += nf - (phy->noise >> 4); + + state->noise = -(phy->noise >> 4); +} + +static bool +mt7915_wait_reset_state(struct mt7915_dev *dev, u32 state) +{ + bool ret; + + ret = wait_event_timeout(dev->reset_wait, + (READ_ONCE(dev->recovery.state) & state), + MT7915_RESET_TIMEOUT); + + WARN(!ret, "Timeout waiting for MCU reset state %x\n", state); + return ret; +} + +static void +mt7915_update_vif_beacon(void *priv, u8 *mac, struct ieee80211_vif *vif) +{ + struct ieee80211_hw *hw = priv; + + switch (vif->type) { + case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_AP: + mt7915_mcu_add_beacon(hw, vif, vif->bss_conf.enable_beacon, + BSS_CHANGED_BEACON_ENABLED); + break; + default: + break; + } +} + +static void +mt7915_update_beacons(struct mt7915_dev *dev) +{ + struct mt76_phy *mphy_ext = dev->mt76.phys[MT_BAND1]; + + ieee80211_iterate_active_interfaces(dev->mt76.hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7915_update_vif_beacon, dev->mt76.hw); + + if (!mphy_ext) + return; + + ieee80211_iterate_active_interfaces(mphy_ext->hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7915_update_vif_beacon, mphy_ext->hw); +} + +static int +mt7915_mac_restart(struct mt7915_dev *dev) +{ + struct mt7915_phy *phy2; + struct mt76_phy *ext_phy; + struct mt76_dev *mdev = &dev->mt76; + bool run_main, run_ext; + int i, ret; + + ext_phy = dev->mt76.phys[MT_BAND1]; + phy2 = ext_phy ? ext_phy->priv : NULL; + + if (dev->hif2) { + mt76_wr(dev, MT_INT1_MASK_CSR, 0x0); + mt76_wr(dev, MT_INT1_SOURCE_CSR, ~0); + } + + if (dev_is_pci(mdev->dev)) { + mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0x0); + if (dev->hif2) { + if (is_mt7915(mdev)) + mt76_wr(dev, MT_PCIE1_MAC_INT_ENABLE, 0x0); + else + mt76_wr(dev, MT_PCIE1_MAC_INT_ENABLE_MT7916, 0x0); + } + } + + set_bit(MT76_RESET, &dev->mphy.state); + set_bit(MT76_MCU_RESET, &dev->mphy.state); + wake_up(&dev->mt76.mcu.wait); + if (ext_phy) + set_bit(MT76_RESET, &ext_phy->state); + + /* lock/unlock all queues to ensure that no tx is pending */ + mt76_txq_schedule_all(&dev->mphy); + if (ext_phy) + mt76_txq_schedule_all(ext_phy); + + /* disable all tx/rx napi */ + mt76_worker_disable(&dev->mt76.tx_worker); + mt76_for_each_q_rx(mdev, i) { + if (mdev->q_rx[i].ndesc) + napi_disable(&dev->mt76.napi[i]); + } + napi_disable(&dev->mt76.tx_napi); + + /* token reinit */ + mt76_connac2_tx_token_put(&dev->mt76); + idr_init(&dev->mt76.token); + + mt7915_dma_reset(dev, true); + + mt76_for_each_q_rx(mdev, i) { + if (mdev->q_rx[i].ndesc) { + napi_enable(&dev->mt76.napi[i]); + } + } + + local_bh_disable(); + mt76_for_each_q_rx(mdev, i) { + if (mdev->q_rx[i].ndesc) { + napi_schedule(&dev->mt76.napi[i]); + } + } + local_bh_enable(); + clear_bit(MT76_MCU_RESET, &dev->mphy.state); + clear_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state); + + mt76_wr(dev, MT_INT_MASK_CSR, dev->mt76.mmio.irqmask); + mt76_wr(dev, MT_INT_SOURCE_CSR, ~0); + + if (dev->hif2) { + mt76_wr(dev, MT_INT1_MASK_CSR, dev->mt76.mmio.irqmask); + mt76_wr(dev, MT_INT1_SOURCE_CSR, ~0); + } + if (dev_is_pci(mdev->dev)) { + mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0xff); + if (dev->hif2) { + mt76_wr(dev, MT_PCIE_RECOG_ID, + dev->hif2->index | MT_PCIE_RECOG_ID_SEM); + if (is_mt7915(mdev)) + mt76_wr(dev, MT_PCIE1_MAC_INT_ENABLE, 0xff); + else + mt76_wr(dev, MT_PCIE1_MAC_INT_ENABLE_MT7916, 0xff); + } + } + + /* load firmware */ + ret = mt7915_mcu_init_firmware(dev); + if (ret) + goto out; + + /* set the necessary init items */ + ret = mt7915_mcu_set_eeprom(dev); + if (ret) + goto out; + + mt7915_mac_init(dev); + mt7915_init_txpower(&dev->phy); + mt7915_init_txpower(phy2); + ret = mt7915_txbf_init(dev); + + run_main = test_and_clear_bit(MT76_STATE_RUNNING, &dev->mphy.state); + run_ext = ext_phy && + test_and_clear_bit(MT76_STATE_RUNNING, &ext_phy->state); + + if (run_main) { + ret = mt7915_run(dev->mphy.hw); + if (ret) + goto out; + } + + if (run_ext) { + ret = mt7915_run(ext_phy->hw); + if (ret) + goto out; + } + +out: + /* reset done */ + clear_bit(MT76_RESET, &dev->mphy.state); + if (phy2) + clear_bit(MT76_RESET, &phy2->mt76->state); + + napi_enable(&dev->mt76.tx_napi); + + local_bh_disable(); + napi_schedule(&dev->mt76.tx_napi); + local_bh_enable(); + + mt76_worker_enable(&dev->mt76.tx_worker); + + return ret; +} + +static void +mt7915_mac_full_reset(struct mt7915_dev *dev) +{ + struct mt76_phy *ext_phy; + struct mt7915_phy *phy2; + int i; + + ext_phy = dev->mt76.phys[MT_BAND1]; + phy2 = ext_phy ? ext_phy->priv : NULL; + + dev->recovery.hw_full_reset = true; + + set_bit(MT76_MCU_RESET, &dev->mphy.state); + wake_up(&dev->mt76.mcu.wait); + ieee80211_stop_queues(mt76_hw(dev)); + if (ext_phy) + ieee80211_stop_queues(ext_phy->hw); + + cancel_delayed_work_sync(&dev->mphy.mac_work); + if (ext_phy) + cancel_delayed_work_sync(&ext_phy->mac_work); + + mt76_abort_scan(&dev->mt76); + + mutex_lock(&dev->mt76.mutex); + for (i = 0; i < 10; i++) { + if (!mt7915_mac_restart(dev)) + break; + } + + if (i == 10) + dev_err(dev->mt76.dev, "chip full reset failed\n"); + + dev->phy.omac_mask = 0; + if (phy2) + phy2->omac_mask = 0; + + mt76_reset_device(&dev->mt76); + + INIT_LIST_HEAD(&dev->sta_rc_list); + INIT_LIST_HEAD(&dev->twt_list); + + i = mt76_wcid_alloc(dev->mt76.wcid_mask, MT7915_WTBL_STA); + dev->mt76.global_wcid.idx = i; + dev->recovery.hw_full_reset = false; + + mutex_unlock(&dev->mt76.mutex); + + ieee80211_wake_queues(mt76_hw(dev)); + if (ext_phy) + ieee80211_wake_queues(ext_phy->hw); + + ieee80211_restart_hw(mt76_hw(dev)); + if (ext_phy) + ieee80211_restart_hw(ext_phy->hw); +} + +/* system error recovery */ +void mt7915_mac_reset_work(struct work_struct *work) +{ + struct mt7915_phy *phy2; + struct mt76_phy *ext_phy; + struct mt7915_dev *dev; + int i; + + dev = container_of(work, struct mt7915_dev, reset_work); + ext_phy = dev->mt76.phys[MT_BAND1]; + phy2 = ext_phy ? ext_phy->priv : NULL; + + /* chip full reset */ + if (dev->recovery.restart) { + /* disable WA/WM WDT */ + mt76_clear(dev, MT_WFDMA0_MCU_HOST_INT_ENA, + MT_MCU_CMD_WDT_MASK); + + if (READ_ONCE(dev->recovery.state) & MT_MCU_CMD_WA_WDT) + dev->recovery.wa_reset_count++; + else + dev->recovery.wm_reset_count++; + + mt7915_mac_full_reset(dev); + + /* enable mcu irq */ + mt7915_irq_enable(dev, MT_INT_MCU_CMD); + mt7915_irq_disable(dev, 0); + + /* enable WA/WM WDT */ + mt76_set(dev, MT_WFDMA0_MCU_HOST_INT_ENA, MT_MCU_CMD_WDT_MASK); + + dev->recovery.state = MT_MCU_CMD_NORMAL_STATE; + dev->recovery.restart = false; + return; + } + + /* chip partial reset */ + if (!(READ_ONCE(dev->recovery.state) & MT_MCU_CMD_STOP_DMA)) + return; + + ieee80211_stop_queues(mt76_hw(dev)); + if (ext_phy) + ieee80211_stop_queues(ext_phy->hw); + + set_bit(MT76_RESET, &dev->mphy.state); + set_bit(MT76_MCU_RESET, &dev->mphy.state); + wake_up(&dev->mt76.mcu.wait); + cancel_delayed_work_sync(&dev->mphy.mac_work); + if (phy2) { + set_bit(MT76_RESET, &phy2->mt76->state); + cancel_delayed_work_sync(&phy2->mt76->mac_work); + } + + mutex_lock(&dev->mt76.mutex); + + mt76_worker_disable(&dev->mt76.tx_worker); + mt76_for_each_q_rx(&dev->mt76, i) + napi_disable(&dev->mt76.napi[i]); + napi_disable(&dev->mt76.tx_napi); + + + if (mtk_wed_device_active(&dev->mt76.mmio.wed)) + mtk_wed_device_stop(&dev->mt76.mmio.wed); + + mt76_wr(dev, MT_MCU_INT_EVENT, MT_MCU_INT_EVENT_DMA_STOPPED); + + if (mt7915_wait_reset_state(dev, MT_MCU_CMD_RESET_DONE)) { + mt7915_dma_reset(dev, false); + + mt76_connac2_tx_token_put(&dev->mt76); + idr_init(&dev->mt76.token); + + mt76_wr(dev, MT_MCU_INT_EVENT, MT_MCU_INT_EVENT_DMA_INIT); + mt7915_wait_reset_state(dev, MT_MCU_CMD_RECOVERY_DONE); + } + + mt76_wr(dev, MT_MCU_INT_EVENT, MT_MCU_INT_EVENT_RESET_DONE); + mt7915_wait_reset_state(dev, MT_MCU_CMD_NORMAL_STATE); + + /* enable DMA Tx/Rx and interrupt */ + mt7915_dma_start(dev, false, false); + + clear_bit(MT76_MCU_RESET, &dev->mphy.state); + clear_bit(MT76_RESET, &dev->mphy.state); + if (phy2) + clear_bit(MT76_RESET, &phy2->mt76->state); + + mt76_for_each_q_rx(&dev->mt76, i) { + napi_enable(&dev->mt76.napi[i]); + } + + local_bh_disable(); + mt76_for_each_q_rx(&dev->mt76, i) { + napi_schedule(&dev->mt76.napi[i]); + } + local_bh_enable(); + + tasklet_schedule(&dev->mt76.irq_tasklet); + + mt76_worker_enable(&dev->mt76.tx_worker); + + napi_enable(&dev->mt76.tx_napi); + local_bh_disable(); + napi_schedule(&dev->mt76.tx_napi); + local_bh_enable(); + + ieee80211_wake_queues(mt76_hw(dev)); + if (ext_phy) + ieee80211_wake_queues(ext_phy->hw); + + mutex_unlock(&dev->mt76.mutex); + + mt7915_update_beacons(dev); + + ieee80211_queue_delayed_work(mt76_hw(dev), &dev->mphy.mac_work, + MT7915_WATCHDOG_TIME); + if (phy2) + ieee80211_queue_delayed_work(ext_phy->hw, + &phy2->mt76->mac_work, + MT7915_WATCHDOG_TIME); +} + +/* firmware coredump */ +void mt7915_mac_dump_work(struct work_struct *work) +{ + const struct mt7915_mem_region *mem_region; + struct mt7915_crash_data *crash_data; + struct mt7915_dev *dev; + struct mt7915_mem_hdr *hdr; + size_t buf_len; + int i; + u32 num; + u8 *buf; + + dev = container_of(work, struct mt7915_dev, dump_work); + + mutex_lock(&dev->dump_mutex); + + crash_data = mt7915_coredump_new(dev); + if (!crash_data) { + mutex_unlock(&dev->dump_mutex); + goto skip_coredump; + } + + mem_region = mt7915_coredump_get_mem_layout(dev, &num); + if (!mem_region || !crash_data->memdump_buf_len) { + mutex_unlock(&dev->dump_mutex); + goto skip_memdump; + } + + buf = crash_data->memdump_buf; + buf_len = crash_data->memdump_buf_len; + + /* dumping memory content... */ + memset(buf, 0, buf_len); + for (i = 0; i < num; i++) { + if (mem_region->len > buf_len) { + dev_warn(dev->mt76.dev, "%s len %lu is too large\n", + mem_region->name, + (unsigned long)mem_region->len); + break; + } + + /* reserve space for the header */ + hdr = (void *)buf; + buf += sizeof(*hdr); + buf_len -= sizeof(*hdr); + + mt7915_memcpy_fromio(dev, buf, mem_region->start, + mem_region->len); + + hdr->start = mem_region->start; + hdr->len = mem_region->len; + + if (!mem_region->len) + /* note: the header remains, just with zero length */ + break; + + buf += mem_region->len; + buf_len -= mem_region->len; + + mem_region++; + } + + mutex_unlock(&dev->dump_mutex); + +skip_memdump: + mt7915_coredump_submit(dev); +skip_coredump: + queue_work(dev->mt76.wq, &dev->reset_work); +} + +void mt7915_reset(struct mt7915_dev *dev) +{ + if (!dev->recovery.hw_init_done) + return; + + if (dev->recovery.hw_full_reset) + return; + + /* wm/wa exception: do full recovery */ + if (READ_ONCE(dev->recovery.state) & MT_MCU_CMD_WDT_MASK) { + dev->recovery.restart = true; + dev_info(dev->mt76.dev, + "%s indicated firmware crash, attempting recovery\n", + wiphy_name(dev->mt76.hw->wiphy)); + + mt7915_irq_disable(dev, MT_INT_MCU_CMD); + queue_work(dev->mt76.wq, &dev->dump_work); + return; + } + + if ((READ_ONCE(dev->recovery.state) & MT_MCU_CMD_STOP_DMA)) { + set_bit(MT76_MCU_RESET, &dev->mphy.state); + wake_up(&dev->mt76.mcu.wait); + } + + queue_work(dev->mt76.wq, &dev->reset_work); + wake_up(&dev->reset_wait); +} + +void mt7915_mac_update_stats(struct mt7915_phy *phy) +{ + struct mt76_mib_stats *mib = &phy->mib; + struct mt7915_dev *dev = phy->dev; + int i, aggr0 = 0, aggr1, cnt; + u8 band = phy->mt76->band_idx; + u32 val; + + cnt = mt76_rr(dev, MT_MIB_SDR3(band)); + mib->fcs_err_cnt += is_mt7915(&dev->mt76) ? + FIELD_GET(MT_MIB_SDR3_FCS_ERR_MASK, cnt) : + FIELD_GET(MT_MIB_SDR3_FCS_ERR_MASK_MT7916, cnt); + + cnt = mt76_rr(dev, MT_MIB_SDR4(band)); + mib->rx_fifo_full_cnt += FIELD_GET(MT_MIB_SDR4_RX_FIFO_FULL_MASK, cnt); + + cnt = mt76_rr(dev, MT_MIB_SDR5(band)); + mib->rx_mpdu_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_SDR6(band)); + mib->channel_idle_cnt += FIELD_GET(MT_MIB_SDR6_CHANNEL_IDL_CNT_MASK, cnt); + + cnt = mt76_rr(dev, MT_MIB_SDR7(band)); + mib->rx_vector_mismatch_cnt += + FIELD_GET(MT_MIB_SDR7_RX_VECTOR_MISMATCH_CNT_MASK, cnt); + + cnt = mt76_rr(dev, MT_MIB_SDR8(band)); + mib->rx_delimiter_fail_cnt += + FIELD_GET(MT_MIB_SDR8_RX_DELIMITER_FAIL_CNT_MASK, cnt); + + cnt = mt76_rr(dev, MT_MIB_SDR10(band)); + mib->rx_mrdy_cnt += is_mt7915(&dev->mt76) ? + FIELD_GET(MT_MIB_SDR10_MRDY_COUNT_MASK, cnt) : + FIELD_GET(MT_MIB_SDR10_MRDY_COUNT_MASK_MT7916, cnt); + + cnt = mt76_rr(dev, MT_MIB_SDR11(band)); + mib->rx_len_mismatch_cnt += + FIELD_GET(MT_MIB_SDR11_RX_LEN_MISMATCH_CNT_MASK, cnt); + + cnt = mt76_rr(dev, MT_MIB_SDR12(band)); + mib->tx_ampdu_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_SDR13(band)); + mib->tx_stop_q_empty_cnt += + FIELD_GET(MT_MIB_SDR13_TX_STOP_Q_EMPTY_CNT_MASK, cnt); + + cnt = mt76_rr(dev, MT_MIB_SDR14(band)); + mib->tx_mpdu_attempts_cnt += is_mt7915(&dev->mt76) ? + FIELD_GET(MT_MIB_SDR14_TX_MPDU_ATTEMPTS_CNT_MASK, cnt) : + FIELD_GET(MT_MIB_SDR14_TX_MPDU_ATTEMPTS_CNT_MASK_MT7916, cnt); + + cnt = mt76_rr(dev, MT_MIB_SDR15(band)); + mib->tx_mpdu_success_cnt += is_mt7915(&dev->mt76) ? + FIELD_GET(MT_MIB_SDR15_TX_MPDU_SUCCESS_CNT_MASK, cnt) : + FIELD_GET(MT_MIB_SDR15_TX_MPDU_SUCCESS_CNT_MASK_MT7916, cnt); + + cnt = mt76_rr(dev, MT_MIB_SDR16(band)); + mib->primary_cca_busy_time += + FIELD_GET(MT_MIB_SDR16_PRIMARY_CCA_BUSY_TIME_MASK, cnt); + + cnt = mt76_rr(dev, MT_MIB_SDR17(band)); + mib->secondary_cca_busy_time += + FIELD_GET(MT_MIB_SDR17_SECONDARY_CCA_BUSY_TIME_MASK, cnt); + + cnt = mt76_rr(dev, MT_MIB_SDR18(band)); + mib->primary_energy_detect_time += + FIELD_GET(MT_MIB_SDR18_PRIMARY_ENERGY_DETECT_TIME_MASK, cnt); + + cnt = mt76_rr(dev, MT_MIB_SDR19(band)); + mib->cck_mdrdy_time += FIELD_GET(MT_MIB_SDR19_CCK_MDRDY_TIME_MASK, cnt); + + cnt = mt76_rr(dev, MT_MIB_SDR20(band)); + mib->ofdm_mdrdy_time += + FIELD_GET(MT_MIB_SDR20_OFDM_VHT_MDRDY_TIME_MASK, cnt); + + cnt = mt76_rr(dev, MT_MIB_SDR21(band)); + mib->green_mdrdy_time += + FIELD_GET(MT_MIB_SDR21_GREEN_MDRDY_TIME_MASK, cnt); + + cnt = mt76_rr(dev, MT_MIB_SDR22(band)); + mib->rx_ampdu_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_SDR23(band)); + mib->rx_ampdu_bytes_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_SDR24(band)); + mib->rx_ampdu_valid_subframe_cnt += is_mt7915(&dev->mt76) ? + FIELD_GET(MT_MIB_SDR24_RX_AMPDU_SF_CNT_MASK, cnt) : + FIELD_GET(MT_MIB_SDR24_RX_AMPDU_SF_CNT_MASK_MT7916, cnt); + + cnt = mt76_rr(dev, MT_MIB_SDR25(band)); + mib->rx_ampdu_valid_subframe_bytes_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_SDR27(band)); + mib->tx_rwp_fail_cnt += + FIELD_GET(MT_MIB_SDR27_TX_RWP_FAIL_CNT_MASK, cnt); + + cnt = mt76_rr(dev, MT_MIB_SDR28(band)); + mib->tx_rwp_need_cnt += + FIELD_GET(MT_MIB_SDR28_TX_RWP_NEED_CNT_MASK, cnt); + + cnt = mt76_rr(dev, MT_MIB_SDR29(band)); + mib->rx_pfdrop_cnt += is_mt7915(&dev->mt76) ? + FIELD_GET(MT_MIB_SDR29_RX_PFDROP_CNT_MASK, cnt) : + FIELD_GET(MT_MIB_SDR29_RX_PFDROP_CNT_MASK_MT7916, cnt); + + cnt = mt76_rr(dev, MT_MIB_SDRVEC(band)); + mib->rx_vec_queue_overflow_drop_cnt += is_mt7915(&dev->mt76) ? + FIELD_GET(MT_MIB_SDR30_RX_VEC_QUEUE_OVERFLOW_DROP_CNT_MASK, cnt) : + FIELD_GET(MT_MIB_SDR30_RX_VEC_QUEUE_OVERFLOW_DROP_CNT_MASK_MT7916, cnt); + + cnt = mt76_rr(dev, MT_MIB_SDR31(band)); + mib->rx_ba_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_SDRMUBF(band)); + mib->tx_bf_cnt += FIELD_GET(MT_MIB_MU_BF_TX_CNT, cnt); + + cnt = mt76_rr(dev, MT_MIB_DR8(band)); + mib->tx_mu_mpdu_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_DR9(band)); + mib->tx_mu_acked_mpdu_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_DR11(band)); + mib->tx_su_acked_mpdu_cnt += cnt; + + cnt = mt76_rr(dev, MT_ETBF_PAR_RPT0(band)); + mib->tx_bf_rx_fb_bw = FIELD_GET(MT_ETBF_PAR_RPT0_FB_BW, cnt); + mib->tx_bf_rx_fb_nc_cnt += FIELD_GET(MT_ETBF_PAR_RPT0_FB_NC, cnt); + mib->tx_bf_rx_fb_nr_cnt += FIELD_GET(MT_ETBF_PAR_RPT0_FB_NR, cnt); + + for (i = 0; i < ARRAY_SIZE(mib->tx_amsdu); i++) { + cnt = mt76_rr(dev, MT_PLE_AMSDU_PACK_MSDU_CNT(i)); + mib->tx_amsdu[i] += cnt; + mib->tx_amsdu_cnt += cnt; + } + + if (is_mt7915(&dev->mt76)) { + for (i = 0, aggr1 = aggr0 + 8; i < 4; i++) { + val = mt76_rr(dev, MT_MIB_MB_SDR1(band, (i << 4))); + mib->ba_miss_cnt += + FIELD_GET(MT_MIB_BA_MISS_COUNT_MASK, val); + mib->ack_fail_cnt += + FIELD_GET(MT_MIB_ACK_FAIL_COUNT_MASK, val); + + val = mt76_rr(dev, MT_MIB_MB_SDR0(band, (i << 4))); + mib->rts_cnt += FIELD_GET(MT_MIB_RTS_COUNT_MASK, val); + mib->rts_retries_cnt += + FIELD_GET(MT_MIB_RTS_RETRIES_COUNT_MASK, val); + + val = mt76_rr(dev, MT_TX_AGG_CNT(band, i)); + phy->mt76->aggr_stats[aggr0++] += val & 0xffff; + phy->mt76->aggr_stats[aggr0++] += val >> 16; + + val = mt76_rr(dev, MT_TX_AGG_CNT2(band, i)); + phy->mt76->aggr_stats[aggr1++] += val & 0xffff; + phy->mt76->aggr_stats[aggr1++] += val >> 16; + } + + cnt = mt76_rr(dev, MT_MIB_SDR32(band)); + mib->tx_pkt_ebf_cnt += FIELD_GET(MT_MIB_SDR32_TX_PKT_EBF_CNT, cnt); + + cnt = mt76_rr(dev, MT_MIB_SDR33(band)); + mib->tx_pkt_ibf_cnt += FIELD_GET(MT_MIB_SDR33_TX_PKT_IBF_CNT, cnt); + + cnt = mt76_rr(dev, MT_ETBF_TX_APP_CNT(band)); + mib->tx_bf_ibf_ppdu_cnt += FIELD_GET(MT_ETBF_TX_IBF_CNT, cnt); + mib->tx_bf_ebf_ppdu_cnt += FIELD_GET(MT_ETBF_TX_EBF_CNT, cnt); + + cnt = mt76_rr(dev, MT_ETBF_TX_NDP_BFRP(band)); + mib->tx_bf_fb_cpl_cnt += FIELD_GET(MT_ETBF_TX_FB_CPL, cnt); + mib->tx_bf_fb_trig_cnt += FIELD_GET(MT_ETBF_TX_FB_TRI, cnt); + + cnt = mt76_rr(dev, MT_ETBF_RX_FB_CNT(band)); + mib->tx_bf_rx_fb_all_cnt += FIELD_GET(MT_ETBF_RX_FB_ALL, cnt); + mib->tx_bf_rx_fb_he_cnt += FIELD_GET(MT_ETBF_RX_FB_HE, cnt); + mib->tx_bf_rx_fb_vht_cnt += FIELD_GET(MT_ETBF_RX_FB_VHT, cnt); + mib->tx_bf_rx_fb_ht_cnt += FIELD_GET(MT_ETBF_RX_FB_HT, cnt); + } else { + for (i = 0; i < 2; i++) { + /* rts count */ + val = mt76_rr(dev, MT_MIB_MB_SDR0(band, (i << 2))); + mib->rts_cnt += FIELD_GET(GENMASK(15, 0), val); + mib->rts_cnt += FIELD_GET(GENMASK(31, 16), val); + + /* rts retry count */ + val = mt76_rr(dev, MT_MIB_MB_SDR1(band, (i << 2))); + mib->rts_retries_cnt += FIELD_GET(GENMASK(15, 0), val); + mib->rts_retries_cnt += FIELD_GET(GENMASK(31, 16), val); + + /* ba miss count */ + val = mt76_rr(dev, MT_MIB_MB_SDR2(band, (i << 2))); + mib->ba_miss_cnt += FIELD_GET(GENMASK(15, 0), val); + mib->ba_miss_cnt += FIELD_GET(GENMASK(31, 16), val); + + /* ack fail count */ + val = mt76_rr(dev, MT_MIB_MB_BFTF(band, (i << 2))); + mib->ack_fail_cnt += FIELD_GET(GENMASK(15, 0), val); + mib->ack_fail_cnt += FIELD_GET(GENMASK(31, 16), val); + } + + for (i = 0; i < 8; i++) { + val = mt76_rr(dev, MT_TX_AGG_CNT(band, i)); + phy->mt76->aggr_stats[aggr0++] += FIELD_GET(GENMASK(15, 0), val); + phy->mt76->aggr_stats[aggr0++] += FIELD_GET(GENMASK(31, 16), val); + } + + cnt = mt76_rr(dev, MT_MIB_SDR32(band)); + mib->tx_pkt_ibf_cnt += FIELD_GET(MT_MIB_SDR32_TX_PKT_IBF_CNT, cnt); + mib->tx_bf_ibf_ppdu_cnt += FIELD_GET(MT_MIB_SDR32_TX_PKT_IBF_CNT, cnt); + mib->tx_pkt_ebf_cnt += FIELD_GET(MT_MIB_SDR32_TX_PKT_EBF_CNT, cnt); + mib->tx_bf_ebf_ppdu_cnt += FIELD_GET(MT_MIB_SDR32_TX_PKT_EBF_CNT, cnt); + + cnt = mt76_rr(dev, MT_MIB_BFCR7(band)); + mib->tx_bf_fb_cpl_cnt += FIELD_GET(MT_MIB_BFCR7_BFEE_TX_FB_CPL, cnt); + + cnt = mt76_rr(dev, MT_MIB_BFCR2(band)); + mib->tx_bf_fb_trig_cnt += FIELD_GET(MT_MIB_BFCR2_BFEE_TX_FB_TRIG, cnt); + + cnt = mt76_rr(dev, MT_MIB_BFCR0(band)); + mib->tx_bf_rx_fb_vht_cnt += FIELD_GET(MT_MIB_BFCR0_RX_FB_VHT, cnt); + mib->tx_bf_rx_fb_all_cnt += FIELD_GET(MT_MIB_BFCR0_RX_FB_VHT, cnt); + mib->tx_bf_rx_fb_ht_cnt += FIELD_GET(MT_MIB_BFCR0_RX_FB_HT, cnt); + mib->tx_bf_rx_fb_all_cnt += FIELD_GET(MT_MIB_BFCR0_RX_FB_HT, cnt); + + cnt = mt76_rr(dev, MT_MIB_BFCR1(band)); + mib->tx_bf_rx_fb_he_cnt += FIELD_GET(MT_MIB_BFCR1_RX_FB_HE, cnt); + mib->tx_bf_rx_fb_all_cnt += FIELD_GET(MT_MIB_BFCR1_RX_FB_HE, cnt); + } +} + +static void mt7915_mac_severe_check(struct mt7915_phy *phy) +{ + struct mt7915_dev *dev = phy->dev; + u32 trb, ple_err; + + if (!phy->omac_mask) + return; + + /* In rare cases, TRB pointers might be out of sync leads to RMAC + * stopping Rx, so check status periodically to see if TRB hardware + * requires minimal recovery. + */ + trb = mt76_rr(dev, MT_TRB_RXPSR0(phy->mt76->band_idx)); + + if ((FIELD_GET(MT_TRB_RXPSR0_RX_RMAC_PTR, trb) != + FIELD_GET(MT_TRB_RXPSR0_RX_WTBL_PTR, trb)) && + (FIELD_GET(MT_TRB_RXPSR0_RX_RMAC_PTR, phy->trb_ts) != + FIELD_GET(MT_TRB_RXPSR0_RX_WTBL_PTR, phy->trb_ts)) && + trb == phy->trb_ts) + mt7915_mcu_set_ser(dev, SER_RECOVER, SER_SET_RECOVER_L3_RX_ABORT, + phy->mt76->band_idx); + + phy->trb_ts = trb; + + ple_err = mt76_rr(dev, MT_SWDEF_PLE1_STATS); + if ((ple_err & MT_SWDEF_PLE1_MDP_RIOC_HANG_ERR) && + !(dev->ple1_sts & MT_SWDEF_PLE1_MDP_RIOC_HANG_ERR)) { + dev_warn(dev->mt76.dev, + "band%d: PLE error 0x%x detected, triggering L1 SER\n", + phy->mt76->band_idx, ple_err); + mt7915_mcu_set_ser(dev, SER_RECOVER, SER_SET_RECOVER_L1, + phy->mt76->band_idx); + } + dev->ple1_sts = ple_err; +} + +void mt7915_mac_sta_rc_work(struct work_struct *work) +{ + struct mt7915_dev *dev = container_of(work, struct mt7915_dev, rc_work); + struct ieee80211_sta *sta; + struct ieee80211_vif *vif; + struct mt7915_sta *msta; + u32 changed; + LIST_HEAD(list); + + spin_lock_bh(&dev->mt76.sta_poll_lock); + list_splice_init(&dev->sta_rc_list, &list); + + while (!list_empty(&list)) { + msta = list_first_entry(&list, struct mt7915_sta, rc_list); + list_del_init(&msta->rc_list); + changed = msta->changed; + msta->changed = 0; + spin_unlock_bh(&dev->mt76.sta_poll_lock); + + sta = container_of((void *)msta, struct ieee80211_sta, drv_priv); + vif = container_of((void *)msta->vif, struct ieee80211_vif, drv_priv); + + if (changed & (IEEE80211_RC_SUPP_RATES_CHANGED | + IEEE80211_RC_NSS_CHANGED | + IEEE80211_RC_BW_CHANGED)) + mt7915_mcu_add_rate_ctrl(dev, vif, sta, true); + + if (changed & IEEE80211_RC_SMPS_CHANGED) + mt7915_mcu_add_smps(dev, vif, sta); + + spin_lock_bh(&dev->mt76.sta_poll_lock); + } + + spin_unlock_bh(&dev->mt76.sta_poll_lock); +} + +void mt7915_mac_work(struct work_struct *work) +{ + struct mt7915_phy *phy; + struct mt76_phy *mphy; + + mphy = (struct mt76_phy *)container_of(work, struct mt76_phy, + mac_work.work); + phy = mphy->priv; + + mutex_lock(&mphy->dev->mutex); + + mt76_update_survey(mphy); + if (++mphy->mac_work_count == 5) { + mphy->mac_work_count = 0; + + mt7915_mac_update_stats(phy); + mt7915_mac_severe_check(phy); + + if (phy->dev->muru_debug) + mt7915_mcu_muru_debug_get(phy); + } + + mutex_unlock(&mphy->dev->mutex); + + mt76_tx_status_check(mphy->dev, false); + + ieee80211_queue_delayed_work(mphy->hw, &mphy->mac_work, + MT7915_WATCHDOG_TIME); +} + +static void mt7915_dfs_stop_radar_detector(struct mt7915_phy *phy) +{ + struct mt7915_dev *dev = phy->dev; + int rdd_idx = mt7915_get_rdd_idx(phy, false); + + if (rdd_idx < 0) + return; + + mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_STOP, rdd_idx, 0, 0); +} + +static int mt7915_dfs_start_rdd(struct mt7915_dev *dev, int rdd_idx) +{ + int err, region; + + switch (dev->mt76.region) { + case NL80211_DFS_ETSI: + region = 0; + break; + case NL80211_DFS_JP: + region = 2; + break; + case NL80211_DFS_FCC: + default: + region = 1; + break; + } + + err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_START, rdd_idx, 0, region); + if (err < 0) + return err; + + if (is_mt7915(&dev->mt76)) { + err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_SET_WF_ANT, rdd_idx, + 0, dev->dbdc_support ? 2 : 0); + if (err < 0) + return err; + } + + return mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_DET_MODE, rdd_idx, 0, 1); +} + +static int mt7915_dfs_start_radar_detector(struct mt7915_phy *phy) +{ + struct mt7915_dev *dev = phy->dev; + int err, rdd_idx; + + rdd_idx = mt7915_get_rdd_idx(phy, false); + if (rdd_idx < 0) + return -EINVAL; + + /* start CAC */ + err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_CAC_START, rdd_idx, 0, 0); + if (err < 0) + return err; + + err = mt7915_dfs_start_rdd(dev, rdd_idx); + if (err < 0) + return err; + + return 0; +} + +static int +mt7915_dfs_init_radar_specs(struct mt7915_phy *phy) +{ + const struct mt7915_dfs_radar_spec *radar_specs; + struct mt7915_dev *dev = phy->dev; + int err, i; + + switch (dev->mt76.region) { + case NL80211_DFS_FCC: + radar_specs = &fcc_radar_specs; + err = mt7915_mcu_set_fcc5_lpn(dev, 8); + if (err < 0) + return err; + break; + case NL80211_DFS_ETSI: + radar_specs = &etsi_radar_specs; + break; + case NL80211_DFS_JP: + radar_specs = &jp_radar_specs; + break; + default: + return -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(radar_specs->radar_pattern); i++) { + err = mt7915_mcu_set_radar_th(dev, i, + &radar_specs->radar_pattern[i]); + if (err < 0) + return err; + } + + return mt7915_mcu_set_pulse_th(dev, &radar_specs->pulse_th); +} + +int mt7915_dfs_init_radar_detector(struct mt7915_phy *phy) +{ + struct mt7915_dev *dev = phy->dev; + enum mt76_dfs_state dfs_state, prev_state; + int err, rdd_idx = mt7915_get_rdd_idx(phy, false); + + prev_state = phy->mt76->dfs_state; + dfs_state = mt76_phy_dfs_state(phy->mt76); + + if (prev_state == dfs_state || rdd_idx < 0) + return 0; + + if (prev_state == MT_DFS_STATE_UNKNOWN) + mt7915_dfs_stop_radar_detector(phy); + + if (dfs_state == MT_DFS_STATE_DISABLED) + goto stop; + + if (prev_state <= MT_DFS_STATE_DISABLED) { + err = mt7915_dfs_init_radar_specs(phy); + if (err < 0) + return err; + + err = mt7915_dfs_start_radar_detector(phy); + if (err < 0) + return err; + + phy->mt76->dfs_state = MT_DFS_STATE_CAC; + } + + if (dfs_state == MT_DFS_STATE_CAC) + return 0; + + err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_CAC_END, rdd_idx, 0, 0); + if (err < 0) { + phy->mt76->dfs_state = MT_DFS_STATE_UNKNOWN; + return err; + } + + phy->mt76->dfs_state = MT_DFS_STATE_ACTIVE; + return 0; + +stop: + err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_NORMAL_START, rdd_idx, 0, 0); + if (err < 0) + return err; + + if (is_mt7915(&dev->mt76)) { + err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_SET_WF_ANT, + rdd_idx, 0, dev->dbdc_support ? 2 : 0); + if (err < 0) + return err; + } + + mt7915_dfs_stop_radar_detector(phy); + phy->mt76->dfs_state = MT_DFS_STATE_DISABLED; + + return 0; +} + +static int +mt7915_mac_twt_duration_align(int duration) +{ + return duration << 8; +} + +static u64 +mt7915_mac_twt_sched_list_add(struct mt7915_dev *dev, + struct mt7915_twt_flow *flow) +{ + struct mt7915_twt_flow *iter, *iter_next; + u32 duration = flow->duration << 8; + u64 start_tsf; + + iter = list_first_entry_or_null(&dev->twt_list, + struct mt7915_twt_flow, list); + if (!iter || !iter->sched || iter->start_tsf > duration) { + /* add flow as first entry in the list */ + list_add(&flow->list, &dev->twt_list); + return 0; + } + + list_for_each_entry_safe(iter, iter_next, &dev->twt_list, list) { + start_tsf = iter->start_tsf + + mt7915_mac_twt_duration_align(iter->duration); + if (list_is_last(&iter->list, &dev->twt_list)) + break; + + if (!iter_next->sched || + iter_next->start_tsf > start_tsf + duration) { + list_add(&flow->list, &iter->list); + goto out; + } + } + + /* add flow as last entry in the list */ + list_add_tail(&flow->list, &dev->twt_list); +out: + return start_tsf; +} + +static int mt7915_mac_check_twt_req(struct ieee80211_twt_setup *twt) +{ + struct ieee80211_twt_params *twt_agrt; + u64 interval, duration; + u16 mantissa; + u8 exp; + + /* only individual agreement supported */ + if (twt->control & IEEE80211_TWT_CONTROL_NEG_TYPE_BROADCAST) + return -EOPNOTSUPP; + + /* only 256us unit supported */ + if (twt->control & IEEE80211_TWT_CONTROL_WAKE_DUR_UNIT) + return -EOPNOTSUPP; + + twt_agrt = (struct ieee80211_twt_params *)twt->params; + + /* explicit agreement not supported */ + if (!(twt_agrt->req_type & cpu_to_le16(IEEE80211_TWT_REQTYPE_IMPLICIT))) + return -EOPNOTSUPP; + + exp = FIELD_GET(IEEE80211_TWT_REQTYPE_WAKE_INT_EXP, + le16_to_cpu(twt_agrt->req_type)); + mantissa = le16_to_cpu(twt_agrt->mantissa); + duration = twt_agrt->min_twt_dur << 8; + + interval = (u64)mantissa << exp; + if (interval < duration) + return -EOPNOTSUPP; + + return 0; +} + +static bool +mt7915_mac_twt_param_equal(struct mt7915_sta *msta, + struct ieee80211_twt_params *twt_agrt) +{ + u16 type = le16_to_cpu(twt_agrt->req_type); + u8 exp; + int i; + + exp = FIELD_GET(IEEE80211_TWT_REQTYPE_WAKE_INT_EXP, type); + for (i = 0; i < MT7915_MAX_STA_TWT_AGRT; i++) { + struct mt7915_twt_flow *f; + + if (!(msta->twt.flowid_mask & BIT(i))) + continue; + + f = &msta->twt.flow[i]; + if (f->duration == twt_agrt->min_twt_dur && + f->mantissa == twt_agrt->mantissa && + f->exp == exp && + f->protection == !!(type & IEEE80211_TWT_REQTYPE_PROTECTION) && + f->flowtype == !!(type & IEEE80211_TWT_REQTYPE_FLOWTYPE) && + f->trigger == !!(type & IEEE80211_TWT_REQTYPE_TRIGGER)) + return true; + } + + return false; +} + +void mt7915_mac_add_twt_setup(struct ieee80211_hw *hw, + struct ieee80211_sta *sta, + struct ieee80211_twt_setup *twt) +{ + enum ieee80211_twt_setup_cmd setup_cmd = TWT_SETUP_CMD_REJECT; + struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv; + struct ieee80211_twt_params *twt_agrt = (void *)twt->params; + u16 req_type = le16_to_cpu(twt_agrt->req_type); + enum ieee80211_twt_setup_cmd sta_setup_cmd; + struct mt7915_dev *dev = mt7915_hw_dev(hw); + struct mt7915_twt_flow *flow; + int flowid, table_id; + u8 exp; + + if (mt7915_mac_check_twt_req(twt)) + goto out; + + mutex_lock(&dev->mt76.mutex); + + if (dev->twt.n_agrt == MT7915_MAX_TWT_AGRT) + goto unlock; + + if (hweight8(msta->twt.flowid_mask) == ARRAY_SIZE(msta->twt.flow)) + goto unlock; + + if (twt_agrt->min_twt_dur < MT7915_MIN_TWT_DUR) { + setup_cmd = TWT_SETUP_CMD_DICTATE; + twt_agrt->min_twt_dur = MT7915_MIN_TWT_DUR; + goto unlock; + } + + flowid = ffs(~msta->twt.flowid_mask) - 1; + twt_agrt->req_type &= ~cpu_to_le16(IEEE80211_TWT_REQTYPE_FLOWID); + twt_agrt->req_type |= le16_encode_bits(flowid, + IEEE80211_TWT_REQTYPE_FLOWID); + + table_id = ffs(~dev->twt.table_mask) - 1; + exp = FIELD_GET(IEEE80211_TWT_REQTYPE_WAKE_INT_EXP, req_type); + sta_setup_cmd = FIELD_GET(IEEE80211_TWT_REQTYPE_SETUP_CMD, req_type); + + if (mt7915_mac_twt_param_equal(msta, twt_agrt)) + goto unlock; + + flow = &msta->twt.flow[flowid]; + memset(flow, 0, sizeof(*flow)); + INIT_LIST_HEAD(&flow->list); + flow->wcid = msta->wcid.idx; + flow->table_id = table_id; + flow->id = flowid; + flow->duration = twt_agrt->min_twt_dur; + flow->mantissa = twt_agrt->mantissa; + flow->exp = exp; + flow->protection = !!(req_type & IEEE80211_TWT_REQTYPE_PROTECTION); + flow->flowtype = !!(req_type & IEEE80211_TWT_REQTYPE_FLOWTYPE); + flow->trigger = !!(req_type & IEEE80211_TWT_REQTYPE_TRIGGER); + + if (sta_setup_cmd == TWT_SETUP_CMD_REQUEST || + sta_setup_cmd == TWT_SETUP_CMD_SUGGEST) { + u64 interval = (u64)le16_to_cpu(twt_agrt->mantissa) << exp; + u64 flow_tsf, curr_tsf; + u32 rem; + + flow->sched = true; + flow->start_tsf = mt7915_mac_twt_sched_list_add(dev, flow); + curr_tsf = __mt7915_get_tsf(hw, msta->vif); + div_u64_rem(curr_tsf - flow->start_tsf, interval, &rem); + flow_tsf = curr_tsf + interval - rem; + twt_agrt->twt = cpu_to_le64(flow_tsf); + } else { + list_add_tail(&flow->list, &dev->twt_list); + } + flow->tsf = le64_to_cpu(twt_agrt->twt); + + if (mt7915_mcu_twt_agrt_update(dev, msta->vif, flow, MCU_TWT_AGRT_ADD)) { + list_del(&flow->list); + goto unlock; + } + + setup_cmd = TWT_SETUP_CMD_ACCEPT; + dev->twt.table_mask |= BIT(table_id); + msta->twt.flowid_mask |= BIT(flowid); + dev->twt.n_agrt++; + +unlock: + mutex_unlock(&dev->mt76.mutex); +out: + twt_agrt->req_type &= ~cpu_to_le16(IEEE80211_TWT_REQTYPE_SETUP_CMD); + twt_agrt->req_type |= + le16_encode_bits(setup_cmd, IEEE80211_TWT_REQTYPE_SETUP_CMD); + twt->control = (twt->control & IEEE80211_TWT_CONTROL_WAKE_DUR_UNIT) | + (twt->control & IEEE80211_TWT_CONTROL_RX_DISABLED); +} + +void mt7915_mac_twt_teardown_flow(struct mt7915_dev *dev, + struct mt7915_sta *msta, + u8 flowid) +{ + struct mt7915_twt_flow *flow; + + lockdep_assert_held(&dev->mt76.mutex); + + if (flowid >= ARRAY_SIZE(msta->twt.flow)) + return; + + if (!(msta->twt.flowid_mask & BIT(flowid))) + return; + + flow = &msta->twt.flow[flowid]; + if (mt7915_mcu_twt_agrt_update(dev, msta->vif, flow, + MCU_TWT_AGRT_DELETE)) + return; + + list_del_init(&flow->list); + msta->twt.flowid_mask &= ~BIT(flowid); + dev->twt.table_mask &= ~BIT(flow->table_id); + dev->twt.n_agrt--; +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.h b/drivers/net/wireless/mediatek/mt76/mt7915/mac.h new file mode 100644 index 000000000..e39f96e00 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.h @@ -0,0 +1,75 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* Copyright (C) 2020 MediaTek Inc. */ + +#ifndef __MT7915_MAC_H +#define __MT7915_MAC_H + +#include "../mt76_connac2_mac.h" + +#define MT_TX_FREE_VER GENMASK(18, 16) +#define MT_TX_FREE_MSDU_CNT_V0 GENMASK(6, 0) +/* 0: success, others: dropped */ +#define MT_TX_FREE_COUNT GENMASK(12, 0) +#define MT_TX_FREE_COUNT_V3 GENMASK(27, 24) +#define MT_TX_FREE_STAT GENMASK(14, 13) +#define MT_TX_FREE_STAT_V3 GENMASK(29, 28) +#define MT_TX_FREE_MPDU_HEADER BIT(15) +#define MT_TX_FREE_MPDU_HEADER_V3 BIT(30) +#define MT_TX_FREE_MSDU_ID_V3 GENMASK(14, 0) + +#define MT_TXS5_F0_FINAL_MPDU BIT(31) +#define MT_TXS5_F0_QOS BIT(30) +#define MT_TXS5_F0_TX_COUNT GENMASK(29, 25) +#define MT_TXS5_F0_FRONT_TIME GENMASK(24, 0) +#define MT_TXS5_F1_MPDU_TX_COUNT GENMASK(31, 24) +#define MT_TXS5_F1_MPDU_TX_BYTES GENMASK(23, 0) + +#define MT_TXS6_F0_NOISE_3 GENMASK(31, 24) +#define MT_TXS6_F0_NOISE_2 GENMASK(23, 16) +#define MT_TXS6_F0_NOISE_1 GENMASK(15, 8) +#define MT_TXS6_F0_NOISE_0 GENMASK(7, 0) +#define MT_TXS6_F1_MPDU_FAIL_COUNT GENMASK(31, 24) +#define MT_TXS6_F1_MPDU_FAIL_BYTES GENMASK(23, 0) + +#define MT_TXS7_F0_RCPI_3 GENMASK(31, 24) +#define MT_TXS7_F0_RCPI_2 GENMASK(23, 16) +#define MT_TXS7_F0_RCPI_1 GENMASK(15, 8) +#define MT_TXS7_F0_RCPI_0 GENMASK(7, 0) +#define MT_TXS7_F1_MPDU_RETRY_COUNT GENMASK(31, 24) +#define MT_TXS7_F1_MPDU_RETRY_BYTES GENMASK(23, 0) + +struct mt7915_dfs_pulse { + u32 max_width; /* us */ + int max_pwr; /* dbm */ + int min_pwr; /* dbm */ + u32 min_stgr_pri; /* us */ + u32 max_stgr_pri; /* us */ + u32 min_cr_pri; /* us */ + u32 max_cr_pri; /* us */ +}; + +struct mt7915_dfs_pattern { + u8 enb; + u8 stgr; + u8 min_crpn; + u8 max_crpn; + u8 min_crpr; + u8 min_pw; + u32 min_pri; + u32 max_pri; + u8 max_pw; + u8 min_crbn; + u8 max_crbn; + u8 min_stgpn; + u8 max_stgpn; + u8 min_stgpr; + u8 rsv[2]; + u32 min_stgpr_diff; +} __packed; + +struct mt7915_dfs_radar_spec { + struct mt7915_dfs_pulse pulse_th; + struct mt7915_dfs_pattern radar_pattern[16]; +}; + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/main.c b/drivers/net/wireless/mediatek/mt76/mt7915/main.c new file mode 100644 index 000000000..a8286f8be --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7915/main.c @@ -0,0 +1,1835 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2020 MediaTek Inc. */ + +#include +#include +#include +#include +#include "mt7915.h" +#include "mcu.h" + +static bool mt7915_dev_running(struct mt7915_dev *dev) +{ + struct mt7915_phy *phy; + + if (test_bit(MT76_STATE_RUNNING, &dev->mphy.state)) + return true; + + phy = mt7915_ext_phy(dev); + + return phy && test_bit(MT76_STATE_RUNNING, &phy->mt76->state); +} + +int mt7915_run(struct ieee80211_hw *hw) +{ + struct mt7915_dev *dev = mt7915_hw_dev(hw); + struct mt7915_phy *phy = mt7915_hw_phy(hw); + bool running; + int ret; + + running = mt7915_dev_running(dev); + + if (!running) { + ret = mt76_connac_mcu_set_pm(&dev->mt76, + dev->phy.mt76->band_idx, 0); + if (ret) + goto out; + + ret = mt7915_mcu_set_mac(dev, dev->phy.mt76->band_idx, + true, true); + if (ret) + goto out; + + mt7915_mac_enable_nf(dev, dev->phy.mt76->band_idx); + } + + if (phy != &dev->phy) { + ret = mt76_connac_mcu_set_pm(&dev->mt76, + phy->mt76->band_idx, 0); + if (ret) + goto out; + + ret = mt7915_mcu_set_mac(dev, phy->mt76->band_idx, + true, true); + if (ret) + goto out; + + mt7915_mac_enable_nf(dev, phy->mt76->band_idx); + } + + ret = mt7915_mcu_set_thermal_throttling(phy, + MT7915_THERMAL_THROTTLE_MAX); + + if (ret) + goto out; + + ret = mt7915_mcu_set_thermal_protect(phy); + + if (ret) + goto out; + + ret = mt76_connac_mcu_set_rts_thresh(&dev->mt76, MT7915_RTS_LEN_THRES, + phy->mt76->band_idx); + if (ret) + goto out; + + ret = mt7915_mcu_set_sku_en(phy); + if (ret) + goto out; + + ret = mt7915_mcu_set_chan_info(phy, MCU_EXT_CMD(SET_RX_PATH)); + if (ret) + goto out; + + set_bit(MT76_STATE_RUNNING, &phy->mt76->state); + + if (!mt76_testmode_enabled(phy->mt76)) + ieee80211_queue_delayed_work(hw, &phy->mt76->mac_work, + MT7915_WATCHDOG_TIME); + + if (!running) + mt7915_mac_reset_counters(phy); + +out: + return ret; +} + +static int mt7915_start(struct ieee80211_hw *hw) +{ + struct mt7915_dev *dev = mt7915_hw_dev(hw); + int ret; + + flush_work(&dev->init_work); + + mutex_lock(&dev->mt76.mutex); + ret = mt7915_run(hw); + mutex_unlock(&dev->mt76.mutex); + + return ret; +} + +static void mt7915_stop(struct ieee80211_hw *hw, bool suspend) +{ + struct mt7915_dev *dev = mt7915_hw_dev(hw); + struct mt7915_phy *phy = mt7915_hw_phy(hw); + + cancel_delayed_work_sync(&phy->mt76->mac_work); + + mutex_lock(&dev->mt76.mutex); + + mt76_testmode_reset(phy->mt76, true); + + clear_bit(MT76_STATE_RUNNING, &phy->mt76->state); + + if (phy != &dev->phy) { + mt76_connac_mcu_set_pm(&dev->mt76, phy->mt76->band_idx, 1); + mt7915_mcu_set_mac(dev, phy->mt76->band_idx, false, false); + } + + if (!mt7915_dev_running(dev)) { + mt76_connac_mcu_set_pm(&dev->mt76, dev->phy.mt76->band_idx, 1); + mt7915_mcu_set_mac(dev, dev->phy.mt76->band_idx, false, false); + } + + mutex_unlock(&dev->mt76.mutex); +} + +static inline int get_free_idx(u32 mask, u8 start, u8 end) +{ + return ffs(~mask & GENMASK(end, start)); +} + +static int get_omac_idx(enum nl80211_iftype type, u64 mask) +{ + int i; + + switch (type) { + case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_STATION: + /* prefer hw bssid slot 1-3 */ + i = get_free_idx(mask, HW_BSSID_1, HW_BSSID_3); + if (i) + return i - 1; + + if (type != NL80211_IFTYPE_STATION) + break; + + i = get_free_idx(mask, EXT_BSSID_1, EXT_BSSID_MAX); + if (i) + return i - 1; + + if (~mask & BIT(HW_BSSID_0)) + return HW_BSSID_0; + + break; + case NL80211_IFTYPE_MONITOR: + case NL80211_IFTYPE_AP: + /* ap uses hw bssid 0 and ext bssid */ + if (~mask & BIT(HW_BSSID_0)) + return HW_BSSID_0; + + i = get_free_idx(mask, EXT_BSSID_1, EXT_BSSID_MAX); + if (i) + return i - 1; + + break; + default: + WARN_ON(1); + break; + } + + return -1; +} + +static void mt7915_init_bitrate_mask(struct ieee80211_vif *vif) +{ + struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; + int i; + + for (i = 0; i < ARRAY_SIZE(mvif->bitrate_mask.control); i++) { + mvif->bitrate_mask.control[i].gi = NL80211_TXRATE_DEFAULT_GI; + mvif->bitrate_mask.control[i].he_gi = 0xff; + mvif->bitrate_mask.control[i].he_ltf = 0xff; + mvif->bitrate_mask.control[i].legacy = GENMASK(31, 0); + memset(mvif->bitrate_mask.control[i].ht_mcs, 0xff, + sizeof(mvif->bitrate_mask.control[i].ht_mcs)); + memset(mvif->bitrate_mask.control[i].vht_mcs, 0xff, + sizeof(mvif->bitrate_mask.control[i].vht_mcs)); + memset(mvif->bitrate_mask.control[i].he_mcs, 0xff, + sizeof(mvif->bitrate_mask.control[i].he_mcs)); + } +} + +static int mt7915_add_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; + struct mt7915_dev *dev = mt7915_hw_dev(hw); + struct mt7915_phy *phy = mt7915_hw_phy(hw); + struct mt76_txq *mtxq; + bool ext_phy = phy != &dev->phy; + int idx, ret = 0; + + mutex_lock(&dev->mt76.mutex); + + mt76_testmode_reset(phy->mt76, true); + + if (vif->type == NL80211_IFTYPE_MONITOR && + is_zero_ether_addr(vif->addr)) + phy->monitor_vif = vif; + + mvif->mt76.idx = __ffs64(~dev->mt76.vif_mask); + if (mvif->mt76.idx >= (MT7915_MAX_INTERFACES << dev->dbdc_support)) { + ret = -ENOSPC; + goto out; + } + + idx = get_omac_idx(vif->type, phy->omac_mask); + if (idx < 0) { + ret = -ENOSPC; + goto out; + } + mvif->mt76.omac_idx = idx; + mvif->phy = phy; + mvif->mt76.band_idx = phy->mt76->band_idx; + mvif->mt76.wcid = &mvif->sta.wcid; + + mvif->mt76.wmm_idx = vif->type != NL80211_IFTYPE_AP; + if (ext_phy) + mvif->mt76.wmm_idx += 2; + + ret = mt7915_mcu_add_dev_info(phy, vif, true); + if (ret) + goto out; + + dev->mt76.vif_mask |= BIT_ULL(mvif->mt76.idx); + phy->omac_mask |= BIT_ULL(mvif->mt76.omac_idx); + + idx = mt76_wcid_alloc(dev->mt76.wcid_mask, mt7915_wtbl_size(dev)); + if (idx < 0) { + ret = -ENOSPC; + goto err; + } + + INIT_LIST_HEAD(&mvif->sta.rc_list); + mvif->sta.wcid.idx = idx; + mvif->sta.wcid.tx_info |= MT_WCID_TX_INFO_SET; + mt76_wcid_init(&mvif->sta.wcid, phy->mt76->band_idx); + + mt7915_mac_wtbl_update(dev, idx, + MT_WTBL_UPDATE_ADM_COUNT_CLEAR); + + if (vif->txq) { + mtxq = (struct mt76_txq *)vif->txq->drv_priv; + mtxq->wcid = idx; + } + + if (vif->type != NL80211_IFTYPE_AP && + (!mvif->mt76.omac_idx || mvif->mt76.omac_idx > 3)) + vif->offload_flags = 0; + vif->offload_flags |= IEEE80211_OFFLOAD_ENCAP_4ADDR; + + mt7915_init_bitrate_mask(vif); + memset(&mvif->cap, -1, sizeof(mvif->cap)); + + mt7915_mcu_add_bss_info(phy, vif, true); + mt7915_mcu_add_sta(dev, vif, NULL, CONN_STATE_PORT_SECURE, true); + rcu_assign_pointer(dev->mt76.wcid[idx], &mvif->sta.wcid); + + mutex_unlock(&dev->mt76.mutex); + + return 0; + +err: + dev->mt76.vif_mask &= ~BIT_ULL(mvif->mt76.idx); + phy->omac_mask &= ~BIT_ULL(mvif->mt76.omac_idx); + mt7915_mcu_add_dev_info(phy, vif, false); +out: + if (phy->monitor_vif == vif) + phy->monitor_vif = NULL; + mutex_unlock(&dev->mt76.mutex); + + return ret; +} + +static void mt7915_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; + struct mt7915_sta *msta = &mvif->sta; + struct mt7915_dev *dev = mt7915_hw_dev(hw); + struct mt7915_phy *phy = mt7915_hw_phy(hw); + int idx = msta->wcid.idx; + + mt7915_mcu_add_bss_info(phy, vif, false); + mt7915_mcu_add_sta(dev, vif, NULL, CONN_STATE_DISCONNECT, false); + + mutex_lock(&dev->mt76.mutex); + mt76_testmode_reset(phy->mt76, true); + mutex_unlock(&dev->mt76.mutex); + + if (vif == phy->monitor_vif) + phy->monitor_vif = NULL; + + mt7915_mcu_add_dev_info(phy, vif, false); + + rcu_assign_pointer(dev->mt76.wcid[idx], NULL); + + mutex_lock(&dev->mt76.mutex); + dev->mt76.vif_mask &= ~BIT_ULL(mvif->mt76.idx); + phy->omac_mask &= ~BIT_ULL(mvif->mt76.omac_idx); + mt76_wcid_mask_clear(dev->mt76.wcid_mask, mvif->sta.wcid.idx); + mutex_unlock(&dev->mt76.mutex); + + spin_lock_bh(&dev->mt76.sta_poll_lock); + if (!list_empty(&msta->wcid.poll_list)) + list_del_init(&msta->wcid.poll_list); + spin_unlock_bh(&dev->mt76.sta_poll_lock); + + mt76_wcid_cleanup(&dev->mt76, &msta->wcid); +} + +int mt7915_set_channel(struct mt76_phy *mphy) +{ + struct mt7915_phy *phy = mphy->priv; + struct mt7915_dev *dev = phy->dev; + int ret; + + if (dev->cal) { + ret = mt7915_mcu_apply_tx_dpd(phy); + if (ret) + goto out; + } + + ret = mt7915_mcu_set_chan_info(phy, MCU_EXT_CMD(CHANNEL_SWITCH)); + if (ret) + goto out; + + mt7915_mac_set_timing(phy); + ret = mt7915_dfs_init_radar_detector(phy); + mt7915_mac_cca_stats_reset(phy); + + mt7915_mac_reset_counters(phy); + phy->noise = 0; + +out: + if (!mt76_testmode_enabled(phy->mt76)) + ieee80211_queue_delayed_work(phy->mt76->hw, + &phy->mt76->mac_work, + MT7915_WATCHDOG_TIME); + + return ret; +} + +static int mt7915_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct mt7915_dev *dev = mt7915_hw_dev(hw); + struct mt7915_phy *phy = mt7915_hw_phy(hw); + struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; + struct mt7915_sta *msta = sta ? (struct mt7915_sta *)sta->drv_priv : + &mvif->sta; + struct mt76_wcid *wcid = &msta->wcid; + u8 *wcid_keyidx = &wcid->hw_key_idx; + int idx = key->keyidx; + int err = 0; + + if (sta && !wcid->sta) { + if (cmd != SET_KEY) + return 0; + + return -EOPNOTSUPP; + } + + /* The hardware does not support per-STA RX GTK, fallback + * to software mode for these. + */ + if ((vif->type == NL80211_IFTYPE_ADHOC || + vif->type == NL80211_IFTYPE_MESH_POINT) && + (key->cipher == WLAN_CIPHER_SUITE_TKIP || + key->cipher == WLAN_CIPHER_SUITE_CCMP) && + !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) + return -EOPNOTSUPP; + + /* fall back to sw encryption for unsupported ciphers */ + switch (key->cipher) { + case WLAN_CIPHER_SUITE_AES_CMAC: + wcid_keyidx = &wcid->hw_key_idx2; + key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIE; + break; + case WLAN_CIPHER_SUITE_TKIP: + case WLAN_CIPHER_SUITE_CCMP: + case WLAN_CIPHER_SUITE_CCMP_256: + case WLAN_CIPHER_SUITE_GCMP: + case WLAN_CIPHER_SUITE_GCMP_256: + case WLAN_CIPHER_SUITE_SMS4: + break; + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + default: + return -EOPNOTSUPP; + } + + mutex_lock(&dev->mt76.mutex); + + if (cmd == SET_KEY && !sta && !mvif->mt76.cipher) { + mvif->mt76.cipher = mt76_connac_mcu_get_cipher(key->cipher); + mt7915_mcu_add_bss_info(phy, vif, true); + } + + if (cmd == SET_KEY) { + *wcid_keyidx = idx; + } else { + if (idx == *wcid_keyidx) + *wcid_keyidx = -1; + goto out; + } + + mt76_wcid_key_setup(&dev->mt76, wcid, key); + err = mt76_connac_mcu_add_key(&dev->mt76, vif, &msta->bip, + key, MCU_EXT_CMD(STA_REC_UPDATE), + &msta->wcid, cmd); +out: + mutex_unlock(&dev->mt76.mutex); + + return err; +} + +static int mt7915_set_sar_specs(struct ieee80211_hw *hw, + const struct cfg80211_sar_specs *sar) +{ + struct mt7915_phy *phy = mt7915_hw_phy(hw); + struct mt7915_dev *dev = mt7915_hw_dev(hw); + int err = -EINVAL; + + mutex_lock(&dev->mt76.mutex); + if (!cfg80211_chandef_valid(&phy->mt76->chandef)) + goto out; + + err = mt76_init_sar_power(hw, sar); + if (err) + goto out; + + err = mt7915_mcu_set_txpower_sku(phy); +out: + mutex_unlock(&dev->mt76.mutex); + + return err; +} + +static int mt7915_config(struct ieee80211_hw *hw, int radio_idx, + u32 changed) +{ + struct mt7915_dev *dev = mt7915_hw_dev(hw); + struct mt7915_phy *phy = mt7915_hw_phy(hw); + int ret; + + if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { +#ifdef CONFIG_NL80211_TESTMODE + if (phy->mt76->test.state != MT76_TM_STATE_OFF) { + mutex_lock(&dev->mt76.mutex); + mt76_testmode_reset(phy->mt76, false); + mutex_unlock(&dev->mt76.mutex); + } +#endif + ret = mt76_update_channel(phy->mt76); + if (ret) + return ret; + } + + if (changed & (IEEE80211_CONF_CHANGE_POWER | + IEEE80211_CONF_CHANGE_CHANNEL)) { + ret = mt7915_mcu_set_txpower_sku(phy); + if (ret) + return ret; + } + + mutex_lock(&dev->mt76.mutex); + + if (changed & IEEE80211_CONF_CHANGE_MONITOR) { + bool enabled = !!(hw->conf.flags & IEEE80211_CONF_MONITOR); + bool band = phy->mt76->band_idx; + u32 rxfilter = phy->rxfilter; + + if (!enabled) { + rxfilter |= MT_WF_RFCR_DROP_OTHER_UC; + dev->monitor_mask &= ~BIT(band); + } else { + rxfilter &= ~MT_WF_RFCR_DROP_OTHER_UC; + dev->monitor_mask |= BIT(band); + } + + mt76_rmw_field(dev, MT_DMA_DCR0(band), MT_DMA_DCR0_RXD_G5_EN, + enabled); + mt76_rmw_field(dev, MT_MDP_DCR0, MT_MDP_DCR0_RX_HDR_TRANS_EN, + !dev->monitor_mask); + mt76_testmode_reset(phy->mt76, true); + mt76_wr(dev, MT_WF_RFCR(band), rxfilter); + } + + mutex_unlock(&dev->mt76.mutex); + + return 0; +} + +static int +mt7915_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + unsigned int link_id, u16 queue, + const struct ieee80211_tx_queue_params *params) +{ + struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; + + /* no need to update right away, we'll get BSS_CHANGED_QOS */ + queue = mt76_connac_lmac_mapping(queue); + mvif->queue_params[queue] = *params; + + return 0; +} + +static void mt7915_configure_filter(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *total_flags, + u64 multicast) +{ + struct mt7915_dev *dev = mt7915_hw_dev(hw); + struct mt7915_phy *phy = mt7915_hw_phy(hw); + bool band = phy->mt76->band_idx; + u32 ctl_flags = MT_WF_RFCR1_DROP_ACK | + MT_WF_RFCR1_DROP_BF_POLL | + MT_WF_RFCR1_DROP_BA | + MT_WF_RFCR1_DROP_CFEND | + MT_WF_RFCR1_DROP_CFACK; + u32 rxfilter; + u32 flags = 0; + +#define MT76_FILTER(_flag, _hw) do { \ + flags |= *total_flags & FIF_##_flag; \ + phy->rxfilter &= ~(_hw); \ + phy->rxfilter |= !(flags & FIF_##_flag) * (_hw); \ + } while (0) + + mutex_lock(&dev->mt76.mutex); + + phy->rxfilter &= ~(MT_WF_RFCR_DROP_OTHER_BSS | + MT_WF_RFCR_DROP_OTHER_BEACON | + MT_WF_RFCR_DROP_FRAME_REPORT | + MT_WF_RFCR_DROP_PROBEREQ | + MT_WF_RFCR_DROP_MCAST_FILTERED | + MT_WF_RFCR_DROP_MCAST | + MT_WF_RFCR_DROP_BCAST | + MT_WF_RFCR_DROP_DUPLICATE | + MT_WF_RFCR_DROP_A2_BSSID | + MT_WF_RFCR_DROP_UNWANTED_CTL | + MT_WF_RFCR_DROP_STBC_MULTI); + + MT76_FILTER(OTHER_BSS, MT_WF_RFCR_DROP_OTHER_TIM | + MT_WF_RFCR_DROP_A3_MAC | + MT_WF_RFCR_DROP_A3_BSSID); + + MT76_FILTER(FCSFAIL, MT_WF_RFCR_DROP_FCSFAIL); + + MT76_FILTER(CONTROL, MT_WF_RFCR_DROP_CTS | + MT_WF_RFCR_DROP_RTS | + MT_WF_RFCR_DROP_CTL_RSV); + + *total_flags = flags; + rxfilter = phy->rxfilter; + if (hw->conf.flags & IEEE80211_CONF_MONITOR) + rxfilter &= ~MT_WF_RFCR_DROP_OTHER_UC; + else + rxfilter |= MT_WF_RFCR_DROP_OTHER_UC; + mt76_wr(dev, MT_WF_RFCR(band), rxfilter); + + if (*total_flags & FIF_CONTROL) + mt76_clear(dev, MT_WF_RFCR1(band), ctl_flags); + else + mt76_set(dev, MT_WF_RFCR1(band), ctl_flags); + + mutex_unlock(&dev->mt76.mutex); +} + +static void +mt7915_update_bss_color(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_he_bss_color *bss_color) +{ + struct mt7915_dev *dev = mt7915_hw_dev(hw); + + switch (vif->type) { + case NL80211_IFTYPE_AP: { + struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; + + if (mvif->mt76.omac_idx > HW_BSSID_MAX) + return; + fallthrough; + } + case NL80211_IFTYPE_STATION: + mt7915_mcu_update_bss_color(dev, vif, bss_color); + break; + default: + break; + } +} + +static void mt7915_bss_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, + u64 changed) +{ + struct mt7915_phy *phy = mt7915_hw_phy(hw); + struct mt7915_dev *dev = mt7915_hw_dev(hw); + int set_bss_info = -1, set_sta = -1; + + mutex_lock(&dev->mt76.mutex); + + /* + * station mode uses BSSID to map the wlan entry to a peer, + * and then peer references bss_info_rfch to set bandwidth cap. + */ + if (changed & BSS_CHANGED_BSSID && + vif->type == NL80211_IFTYPE_STATION) + set_bss_info = set_sta = !is_zero_ether_addr(info->bssid); + if (changed & BSS_CHANGED_ASSOC) + set_bss_info = vif->cfg.assoc; + if (changed & BSS_CHANGED_BEACON_ENABLED && + info->enable_beacon && + vif->type != NL80211_IFTYPE_AP) + set_bss_info = set_sta = 1; + + if (set_bss_info == 1) + mt7915_mcu_add_bss_info(phy, vif, true); + if (set_sta == 1) + mt7915_mcu_add_sta(dev, vif, NULL, CONN_STATE_PORT_SECURE, false); + + if (changed & BSS_CHANGED_HT || changed & BSS_CHANGED_ERP_CTS_PROT) + mt7915_mcu_set_protection(phy, vif, info->ht_operation_mode, + info->use_cts_prot); + + if (changed & BSS_CHANGED_ERP_SLOT) { + int slottime = 9; + + if (phy->mt76->chandef.chan->band == NL80211_BAND_2GHZ && + !info->use_short_slot) + slottime = 20; + + if (slottime != phy->slottime) { + phy->slottime = slottime; + mt7915_mac_set_timing(phy); + } + } + + /* ensure that enable txcmd_mode after bss_info */ + if (changed & (BSS_CHANGED_QOS | BSS_CHANGED_BEACON_ENABLED)) + mt7915_mcu_set_tx(dev, vif); + + if (changed & BSS_CHANGED_HE_OBSS_PD) + mt7915_mcu_add_obss_spr(phy, vif, &info->he_obss_pd); + + if (changed & BSS_CHANGED_HE_BSS_COLOR) + mt7915_update_bss_color(hw, vif, &info->he_bss_color); + + if (changed & (BSS_CHANGED_BEACON | + BSS_CHANGED_BEACON_ENABLED)) + mt7915_mcu_add_beacon(hw, vif, info->enable_beacon, changed); + + if (changed & (BSS_CHANGED_UNSOL_BCAST_PROBE_RESP | + BSS_CHANGED_FILS_DISCOVERY)) + mt7915_mcu_add_inband_discov(dev, vif, changed); + + if (set_bss_info == 0) + mt7915_mcu_add_bss_info(phy, vif, false); + if (set_sta == 0) + mt7915_mcu_add_sta(dev, vif, NULL, CONN_STATE_DISCONNECT, false); + + mutex_unlock(&dev->mt76.mutex); +} + +static void +mt7915_vif_check_caps(struct mt7915_phy *phy, struct ieee80211_vif *vif) +{ + struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; + struct mt7915_vif_cap *vc = &mvif->cap; + + vc->ht_ldpc = vif->bss_conf.ht_ldpc; + vc->vht_ldpc = vif->bss_conf.vht_ldpc; + vc->vht_su_ebfer = vif->bss_conf.vht_su_beamformer; + vc->vht_su_ebfee = vif->bss_conf.vht_su_beamformee; + vc->vht_mu_ebfer = vif->bss_conf.vht_mu_beamformer; + vc->vht_mu_ebfee = vif->bss_conf.vht_mu_beamformee; + vc->he_ldpc = vif->bss_conf.he_ldpc; + vc->he_su_ebfer = vif->bss_conf.he_su_beamformer; + vc->he_su_ebfee = vif->bss_conf.he_su_beamformee; + vc->he_mu_ebfer = vif->bss_conf.he_mu_beamformer; +} + +static int +mt7915_start_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf) +{ + struct mt7915_phy *phy = mt7915_hw_phy(hw); + struct mt7915_dev *dev = mt7915_hw_dev(hw); + int err; + + mutex_lock(&dev->mt76.mutex); + + mt7915_vif_check_caps(phy, vif); + + err = mt7915_mcu_add_bss_info(phy, vif, true); + if (err) + goto out; + err = mt7915_mcu_add_sta(dev, vif, NULL, CONN_STATE_PORT_SECURE, false); +out: + mutex_unlock(&dev->mt76.mutex); + + return err; +} + +static void +mt7915_stop_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf) +{ + struct mt7915_dev *dev = mt7915_hw_dev(hw); + + mutex_lock(&dev->mt76.mutex); + mt7915_mcu_add_sta(dev, vif, NULL, CONN_STATE_DISCONNECT, false); + mutex_unlock(&dev->mt76.mutex); +} + +static void +mt7915_channel_switch_beacon(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_chan_def *chandef) +{ + struct mt7915_dev *dev = mt7915_hw_dev(hw); + + mutex_lock(&dev->mt76.mutex); + mt7915_mcu_add_beacon(hw, vif, true, BSS_CHANGED_BEACON); + mutex_unlock(&dev->mt76.mutex); +} + +int mt7915_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mt7915_dev *dev = container_of(mdev, struct mt7915_dev, mt76); + struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv; + struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; + bool ext_phy = mvif->phy != &dev->phy; + int idx; + + idx = mt76_wcid_alloc(dev->mt76.wcid_mask, MT7915_WTBL_STA); + if (idx < 0) + return -ENOSPC; + + INIT_LIST_HEAD(&msta->rc_list); + INIT_LIST_HEAD(&msta->wcid.poll_list); + msta->vif = mvif; + msta->wcid.sta_disabled = 1; + msta->wcid.idx = idx; + msta->wcid.phy_idx = ext_phy; + msta->jiffies = jiffies; + + if (sta->tdls) + set_bit(MT_WCID_FLAG_TDLS_PEER, &msta->wcid.flags); + + ewma_avg_signal_init(&msta->avg_ack_signal); + + mt7915_mac_wtbl_update(dev, idx, + MT_WTBL_UPDATE_ADM_COUNT_CLEAR); + mt7915_mcu_add_sta(dev, vif, sta, CONN_STATE_DISCONNECT, true); + + return 0; +} + +struct drop_sta_iter { + struct mt7915_dev *dev; + struct ieee80211_hw *hw; + struct ieee80211_vif *vif; + u8 sta_addr[ETH_ALEN]; +}; + +static void +__mt7915_drop_sta(void *ptr, u8 *mac, struct ieee80211_vif *vif) +{ + struct drop_sta_iter *data = ptr; + struct ieee80211_sta *sta; + struct mt7915_sta *msta; + + if (vif == data->vif || vif->type != NL80211_IFTYPE_AP) + return; + + sta = ieee80211_find_sta_by_ifaddr(data->hw, data->sta_addr, mac); + if (!sta) + return; + + msta = (struct mt7915_sta *)sta->drv_priv; + mt7915_mcu_add_sta(data->dev, vif, sta, CONN_STATE_DISCONNECT, false); + msta->wcid.sta_disabled = 1; + msta->wcid.sta = 0; +} + +static void +mt7915_drop_other_sta(struct mt7915_dev *dev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mt76_phy *ext_phy = dev->mt76.phys[MT_BAND1]; + struct drop_sta_iter data = { + .dev = dev, + .hw = dev->mphy.hw, + .vif = vif, + }; + + if (vif->type != NL80211_IFTYPE_AP) + return; + + memcpy(data.sta_addr, sta->addr, ETH_ALEN); + ieee80211_iterate_active_interfaces(data.hw, 0, __mt7915_drop_sta, &data); + + if (!ext_phy) + return; + + data.hw = ext_phy->hw; + ieee80211_iterate_active_interfaces(data.hw, 0, __mt7915_drop_sta, &data); +} + +int mt7915_mac_sta_event(struct mt76_dev *mdev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, enum mt76_sta_event ev) +{ + struct mt7915_dev *dev = container_of(mdev, struct mt7915_dev, mt76); + struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv; + int i, ret; + u32 addr; + + switch (ev) { + case MT76_STA_EVENT_ASSOC: + ret = mt7915_mcu_add_sta(dev, vif, sta, CONN_STATE_CONNECT, true); + if (ret) + return ret; + + addr = mt7915_mac_wtbl_lmac_addr(dev, msta->wcid.idx, 30); + mt76_rmw_field(dev, addr, GENMASK(7, 0), 0xa0); + + ret = mt7915_mcu_add_rate_ctrl(dev, vif, sta, false); + if (ret) + return ret; + + msta->wcid.tx_info |= MT_WCID_TX_INFO_SET; + msta->wcid.sta = 1; + msta->wcid.sta_disabled = 0; + + return 0; + + case MT76_STA_EVENT_AUTHORIZE: + mt7915_drop_other_sta(dev, vif, sta); + return mt7915_mcu_add_sta(dev, vif, sta, CONN_STATE_PORT_SECURE, false); + + case MT76_STA_EVENT_DISASSOC: + mutex_lock(&dev->mt76.mutex); + for (i = 0; i < ARRAY_SIZE(msta->twt.flow); i++) + mt7915_mac_twt_teardown_flow(dev, msta, i); + mutex_unlock(&dev->mt76.mutex); + + mt7915_mcu_add_sta(dev, vif, sta, CONN_STATE_DISCONNECT, false); + msta->wcid.sta_disabled = 1; + msta->wcid.sta = 0; + return 0; + } + + return 0; +} + +void mt7915_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mt7915_dev *dev = container_of(mdev, struct mt7915_dev, mt76); + struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv; + + mt7915_mac_wtbl_update(dev, msta->wcid.idx, + MT_WTBL_UPDATE_ADM_COUNT_CLEAR); + + spin_lock_bh(&mdev->sta_poll_lock); + if (!list_empty(&msta->wcid.poll_list)) + list_del_init(&msta->wcid.poll_list); + if (!list_empty(&msta->rc_list)) + list_del_init(&msta->rc_list); + spin_unlock_bh(&mdev->sta_poll_lock); +} + +static void mt7915_tx(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, + struct sk_buff *skb) +{ + struct mt7915_dev *dev = mt7915_hw_dev(hw); + struct mt76_phy *mphy = hw->priv; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_vif *vif = info->control.vif; + struct mt76_wcid *wcid = &dev->mt76.global_wcid; + + if (control->sta) { + struct mt7915_sta *sta; + + sta = (struct mt7915_sta *)control->sta->drv_priv; + wcid = &sta->wcid; + } + + if (vif && !control->sta) { + struct mt7915_vif *mvif; + + mvif = (struct mt7915_vif *)vif->drv_priv; + wcid = &mvif->sta.wcid; + } + + mt76_tx(mphy, control->sta, wcid, skb); +} + +static int mt7915_set_rts_threshold(struct ieee80211_hw *hw, int radio_idx, + u32 val) +{ + struct mt7915_dev *dev = mt7915_hw_dev(hw); + struct mt7915_phy *phy = mt7915_hw_phy(hw); + int ret; + + mutex_lock(&dev->mt76.mutex); + ret = mt76_connac_mcu_set_rts_thresh(&dev->mt76, val, + phy->mt76->band_idx); + mutex_unlock(&dev->mt76.mutex); + + return ret; +} + +static int +mt7915_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_ampdu_params *params) +{ + enum ieee80211_ampdu_mlme_action action = params->action; + struct mt7915_dev *dev = mt7915_hw_dev(hw); + struct ieee80211_sta *sta = params->sta; + struct ieee80211_txq *txq = sta->txq[params->tid]; + struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv; + u16 tid = params->tid; + u16 ssn = params->ssn; + struct mt76_txq *mtxq; + int ret = 0; + + if (!txq) + return -EINVAL; + + mtxq = (struct mt76_txq *)txq->drv_priv; + + mutex_lock(&dev->mt76.mutex); + switch (action) { + case IEEE80211_AMPDU_RX_START: + mt76_rx_aggr_start(&dev->mt76, &msta->wcid, tid, ssn, + params->buf_size); + ret = mt7915_mcu_add_rx_ba(dev, params, true); + break; + case IEEE80211_AMPDU_RX_STOP: + mt76_rx_aggr_stop(&dev->mt76, &msta->wcid, tid); + ret = mt7915_mcu_add_rx_ba(dev, params, false); + break; + case IEEE80211_AMPDU_TX_OPERATIONAL: + mtxq->aggr = true; + mtxq->send_bar = false; + ret = mt7915_mcu_add_tx_ba(dev, params, true); + break; + case IEEE80211_AMPDU_TX_STOP_FLUSH: + case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: + mtxq->aggr = false; + clear_bit(tid, &msta->wcid.ampdu_state); + ret = mt7915_mcu_add_tx_ba(dev, params, false); + break; + case IEEE80211_AMPDU_TX_START: + set_bit(tid, &msta->wcid.ampdu_state); + ret = IEEE80211_AMPDU_TX_START_IMMEDIATE; + break; + case IEEE80211_AMPDU_TX_STOP_CONT: + mtxq->aggr = false; + clear_bit(tid, &msta->wcid.ampdu_state); + ret = mt7915_mcu_add_tx_ba(dev, params, false); + ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); + break; + } + mutex_unlock(&dev->mt76.mutex); + + return ret; +} + +static int +mt7915_get_stats(struct ieee80211_hw *hw, + struct ieee80211_low_level_stats *stats) +{ + struct mt7915_phy *phy = mt7915_hw_phy(hw); + struct mt7915_dev *dev = mt7915_hw_dev(hw); + struct mt76_mib_stats *mib = &phy->mib; + + mutex_lock(&dev->mt76.mutex); + + stats->dot11RTSSuccessCount = mib->rts_cnt; + stats->dot11RTSFailureCount = mib->rts_retries_cnt; + stats->dot11FCSErrorCount = mib->fcs_err_cnt; + stats->dot11ACKFailureCount = mib->ack_fail_cnt; + + mutex_unlock(&dev->mt76.mutex); + + return 0; +} + +u64 __mt7915_get_tsf(struct ieee80211_hw *hw, struct mt7915_vif *mvif) +{ + struct mt7915_dev *dev = mt7915_hw_dev(hw); + struct mt7915_phy *phy = mt7915_hw_phy(hw); + bool band = phy->mt76->band_idx; + union { + u64 t64; + u32 t32[2]; + } tsf; + u16 n; + + lockdep_assert_held(&dev->mt76.mutex); + + n = mvif->mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0 + : mvif->mt76.omac_idx; + /* TSF software read */ + if (is_mt7915(&dev->mt76)) + mt76_rmw(dev, MT_LPON_TCR(band, n), MT_LPON_TCR_SW_MODE, + MT_LPON_TCR_SW_READ); + else + mt76_rmw(dev, MT_LPON_TCR_MT7916(band, n), MT_LPON_TCR_SW_MODE, + MT_LPON_TCR_SW_READ); + tsf.t32[0] = mt76_rr(dev, MT_LPON_UTTR0(band)); + tsf.t32[1] = mt76_rr(dev, MT_LPON_UTTR1(band)); + + return tsf.t64; +} + +static u64 +mt7915_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; + struct mt7915_dev *dev = mt7915_hw_dev(hw); + u64 ret; + + mutex_lock(&dev->mt76.mutex); + ret = __mt7915_get_tsf(hw, mvif); + mutex_unlock(&dev->mt76.mutex); + + return ret; +} + +static void +mt7915_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u64 timestamp) +{ + struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; + struct mt7915_dev *dev = mt7915_hw_dev(hw); + struct mt7915_phy *phy = mt7915_hw_phy(hw); + bool band = phy->mt76->band_idx; + union { + u64 t64; + u32 t32[2]; + } tsf = { .t64 = timestamp, }; + u16 n; + + mutex_lock(&dev->mt76.mutex); + + n = mvif->mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0 + : mvif->mt76.omac_idx; + mt76_wr(dev, MT_LPON_UTTR0(band), tsf.t32[0]); + mt76_wr(dev, MT_LPON_UTTR1(band), tsf.t32[1]); + /* TSF software overwrite */ + if (is_mt7915(&dev->mt76)) + mt76_rmw(dev, MT_LPON_TCR(band, n), MT_LPON_TCR_SW_MODE, + MT_LPON_TCR_SW_WRITE); + else + mt76_rmw(dev, MT_LPON_TCR_MT7916(band, n), MT_LPON_TCR_SW_MODE, + MT_LPON_TCR_SW_WRITE); + + mutex_unlock(&dev->mt76.mutex); +} + +static void +mt7915_offset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + s64 timestamp) +{ + struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; + struct mt7915_dev *dev = mt7915_hw_dev(hw); + struct mt7915_phy *phy = mt7915_hw_phy(hw); + bool band = phy->mt76->band_idx; + union { + u64 t64; + u32 t32[2]; + } tsf = { .t64 = timestamp, }; + u16 n; + + mutex_lock(&dev->mt76.mutex); + + n = mvif->mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0 + : mvif->mt76.omac_idx; + mt76_wr(dev, MT_LPON_UTTR0(band), tsf.t32[0]); + mt76_wr(dev, MT_LPON_UTTR1(band), tsf.t32[1]); + /* TSF software adjust*/ + if (is_mt7915(&dev->mt76)) + mt76_rmw(dev, MT_LPON_TCR(band, n), MT_LPON_TCR_SW_MODE, + MT_LPON_TCR_SW_ADJUST); + else + mt76_rmw(dev, MT_LPON_TCR_MT7916(band, n), MT_LPON_TCR_SW_MODE, + MT_LPON_TCR_SW_ADJUST); + + mutex_unlock(&dev->mt76.mutex); +} + +static void +mt7915_set_coverage_class(struct ieee80211_hw *hw, int radio_idx, + s16 coverage_class) +{ + struct mt7915_phy *phy = mt7915_hw_phy(hw); + struct mt7915_dev *dev = phy->dev; + + mutex_lock(&dev->mt76.mutex); + phy->coverage_class = max_t(s16, coverage_class, 0); + mt7915_mac_set_timing(phy); + mutex_unlock(&dev->mt76.mutex); +} + +static int +mt7915_set_antenna(struct ieee80211_hw *hw, int radio_idx, u32 tx_ant, u32 rx_ant) +{ + struct mt7915_dev *dev = mt7915_hw_dev(hw); + struct mt7915_phy *phy = mt7915_hw_phy(hw); + int max_nss = hweight8(hw->wiphy->available_antennas_tx); + u8 shift = mt7915_band_chainshift(phy); + u8 band = phy->mt76->band_idx; + + if (!tx_ant || tx_ant != rx_ant || ffs(tx_ant) > max_nss) + return -EINVAL; + + mutex_lock(&dev->mt76.mutex); + + phy->mt76->antenna_mask = tx_ant; + + /* handle a variant of mt7916/mt7981 which has 3T3R but nss2 on 5 GHz band */ + if ((is_mt7916(&dev->mt76) || is_mt7981(&dev->mt76)) && + band && hweight8(tx_ant) == max_nss) + phy->mt76->chainmask = (dev->chainmask >> shift) << shift; + else + phy->mt76->chainmask = tx_ant << shift; + + mt76_set_stream_caps(phy->mt76, true); + mt7915_set_stream_vht_txbf_caps(phy); + mt7915_set_stream_he_caps(phy); + mt7915_mcu_set_txpower_sku(phy); + + mutex_unlock(&dev->mt76.mutex); + + return 0; +} + +static void mt7915_sta_statistics(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct station_info *sinfo) +{ + struct mt7915_phy *phy = mt7915_hw_phy(hw); + struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv; + struct rate_info *txrate = &msta->wcid.rate; + struct rate_info rxrate = {}; + + if (!mt7915_mcu_get_rx_rate(phy, vif, sta, &rxrate)) { + sinfo->rxrate = rxrate; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BITRATE); + } + + if (txrate->legacy || txrate->flags) { + if (txrate->legacy) { + sinfo->txrate.legacy = txrate->legacy; + } else { + sinfo->txrate.mcs = txrate->mcs; + sinfo->txrate.nss = txrate->nss; + sinfo->txrate.bw = txrate->bw; + sinfo->txrate.he_gi = txrate->he_gi; + sinfo->txrate.he_dcm = txrate->he_dcm; + sinfo->txrate.he_ru_alloc = txrate->he_ru_alloc; + } + sinfo->txrate.flags = txrate->flags; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE); + } + + /* offloading flows bypass networking stack, so driver counts and + * reports sta statistics via NL80211_STA_INFO when WED is active. + */ + if (mtk_wed_device_active(&phy->dev->mt76.mmio.wed)) { + sinfo->tx_bytes = msta->wcid.stats.tx_bytes; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BYTES64); + + if (!mt7915_mcu_wed_wa_tx_stats(phy->dev, msta->wcid.idx)) { + sinfo->tx_packets = msta->wcid.stats.tx_packets; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_PACKETS); + } + + if (mtk_wed_get_rx_capa(&phy->dev->mt76.mmio.wed)) { + sinfo->rx_bytes = msta->wcid.stats.rx_bytes; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BYTES64); + + sinfo->rx_packets = msta->wcid.stats.rx_packets; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_PACKETS); + } + } + + sinfo->tx_failed = msta->wcid.stats.tx_failed; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_FAILED); + + sinfo->tx_retries = msta->wcid.stats.tx_retries; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_RETRIES); + + sinfo->ack_signal = (s8)msta->ack_signal; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_ACK_SIGNAL); + + sinfo->avg_ack_signal = -(s8)ewma_avg_signal_read(&msta->avg_ack_signal); + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_ACK_SIGNAL_AVG); +} + +static void mt7915_sta_rc_work(void *data, struct ieee80211_sta *sta) +{ + struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv; + struct mt7915_dev *dev = msta->vif->phy->dev; + u32 *changed = data; + + spin_lock_bh(&dev->mt76.sta_poll_lock); + msta->changed |= *changed; + if (list_empty(&msta->rc_list)) + list_add_tail(&msta->rc_list, &dev->sta_rc_list); + spin_unlock_bh(&dev->mt76.sta_poll_lock); +} + +static void mt7915_sta_rc_update(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_link_sta *link_sta, + u32 changed) +{ + struct ieee80211_sta *sta = link_sta->sta; + struct mt7915_phy *phy = mt7915_hw_phy(hw); + struct mt7915_dev *dev = phy->dev; + struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv; + + if (!msta->wcid.sta) + return; + + mt7915_sta_rc_work(&changed, sta); + ieee80211_queue_work(hw, &dev->rc_work); +} + +static int +mt7915_set_bitrate_mask(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + const struct cfg80211_bitrate_mask *mask) +{ + struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; + struct mt7915_phy *phy = mt7915_hw_phy(hw); + struct mt7915_dev *dev = phy->dev; + u32 changed = IEEE80211_RC_SUPP_RATES_CHANGED; + + mvif->bitrate_mask = *mask; + + /* if multiple rates across different preambles are given we can + * reconfigure this info with all peers using sta_rec command with + * the below exception cases. + * - single rate : if a rate is passed along with different preambles, + * we select the highest one as fixed rate. i.e VHT MCS for VHT peers. + * - multiple rates: if it's not in range format i.e 0-{7,8,9} for VHT + * then multiple MCS setting (MCS 4,5,6) is not supported. + */ + ieee80211_iterate_stations_atomic(hw, mt7915_sta_rc_work, &changed); + ieee80211_queue_work(hw, &dev->rc_work); + + return 0; +} + +static void mt7915_sta_set_4addr(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + bool enabled) +{ + struct mt7915_dev *dev = mt7915_hw_dev(hw); + struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv; + + if (enabled) + set_bit(MT_WCID_FLAG_4ADDR, &msta->wcid.flags); + else + clear_bit(MT_WCID_FLAG_4ADDR, &msta->wcid.flags); + + if (!msta->wcid.sta) + return; + + mt76_connac_mcu_wtbl_update_hdr_trans(&dev->mt76, vif, sta); +} + +static void mt7915_sta_set_decap_offload(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + bool enabled) +{ + struct mt7915_dev *dev = mt7915_hw_dev(hw); + struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv; + + if (enabled) + set_bit(MT_WCID_FLAG_HDR_TRANS, &msta->wcid.flags); + else + clear_bit(MT_WCID_FLAG_HDR_TRANS, &msta->wcid.flags); + + if (!msta->wcid.sta) + return; + + mt76_connac_mcu_wtbl_update_hdr_trans(&dev->mt76, vif, sta); +} + +static int mt7915_sta_set_txpwr(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mt7915_phy *phy = mt7915_hw_phy(hw); + struct mt7915_dev *dev = mt7915_hw_dev(hw); + s16 txpower = sta->deflink.txpwr.power; + int ret; + + if (sta->deflink.txpwr.type == NL80211_TX_POWER_AUTOMATIC) + txpower = 0; + + mutex_lock(&dev->mt76.mutex); + + /* NOTE: temporarily use 0 as minimum limit, which is a + * global setting and will be applied to all stations. + */ + ret = mt7915_mcu_set_txpower_frame_min(phy, 0); + if (ret) + goto out; + + /* This only applies to data frames while pushing traffic, + * whereas the management frames or other packets that are + * using fixed rate can be configured via TxD. + */ + ret = mt7915_mcu_set_txpower_frame(phy, vif, sta, txpower); + +out: + mutex_unlock(&dev->mt76.mutex); + + return ret; +} + +static const char mt7915_gstrings_stats[][ETH_GSTRING_LEN] = { + "tx_ampdu_cnt", + "tx_stop_q_empty_cnt", + "tx_mpdu_attempts", + "tx_mpdu_success", + "tx_rwp_fail_cnt", + "tx_rwp_need_cnt", + "tx_pkt_ebf_cnt", + "tx_pkt_ibf_cnt", + "tx_ampdu_len:0-1", + "tx_ampdu_len:2-10", + "tx_ampdu_len:11-19", + "tx_ampdu_len:20-28", + "tx_ampdu_len:29-37", + "tx_ampdu_len:38-46", + "tx_ampdu_len:47-55", + "tx_ampdu_len:56-79", + "tx_ampdu_len:80-103", + "tx_ampdu_len:104-127", + "tx_ampdu_len:128-151", + "tx_ampdu_len:152-175", + "tx_ampdu_len:176-199", + "tx_ampdu_len:200-223", + "tx_ampdu_len:224-247", + "ba_miss_count", + "tx_beamformer_ppdu_iBF", + "tx_beamformer_ppdu_eBF", + "tx_beamformer_rx_feedback_all", + "tx_beamformer_rx_feedback_he", + "tx_beamformer_rx_feedback_vht", + "tx_beamformer_rx_feedback_ht", + "tx_beamformer_rx_feedback_bw", /* zero based idx: 20, 40, 80, 160 */ + "tx_beamformer_rx_feedback_nc", + "tx_beamformer_rx_feedback_nr", + "tx_beamformee_ok_feedback_pkts", + "tx_beamformee_feedback_trig", + "tx_mu_beamforming", + "tx_mu_mpdu", + "tx_mu_successful_mpdu", + "tx_su_successful_mpdu", + "tx_msdu_pack_1", + "tx_msdu_pack_2", + "tx_msdu_pack_3", + "tx_msdu_pack_4", + "tx_msdu_pack_5", + "tx_msdu_pack_6", + "tx_msdu_pack_7", + "tx_msdu_pack_8", + + /* rx counters */ + "rx_fifo_full_cnt", + "rx_mpdu_cnt", + "channel_idle_cnt", + "primary_cca_busy_time", + "secondary_cca_busy_time", + "primary_energy_detect_time", + "cck_mdrdy_time", + "ofdm_mdrdy_time", + "green_mdrdy_time", + "rx_vector_mismatch_cnt", + "rx_delimiter_fail_cnt", + "rx_mrdy_cnt", + "rx_len_mismatch_cnt", + "rx_ampdu_cnt", + "rx_ampdu_bytes_cnt", + "rx_ampdu_valid_subframe_cnt", + "rx_ampdu_valid_subframe_b_cnt", + "rx_pfdrop_cnt", + "rx_vec_queue_overflow_drop_cnt", + "rx_ba_cnt", + + /* muru mu-mimo and ofdma related stats */ + "dl_cck_cnt", + "dl_ofdm_cnt", + "dl_htmix_cnt", + "dl_htgf_cnt", + "dl_vht_su_cnt", + "dl_vht_2mu_cnt", + "dl_vht_3mu_cnt", + "dl_vht_4mu_cnt", + "dl_he_su_cnt", + "dl_he_ext_su_cnt", + "dl_he_2ru_cnt", + "dl_he_2mu_cnt", + "dl_he_3ru_cnt", + "dl_he_3mu_cnt", + "dl_he_4ru_cnt", + "dl_he_4mu_cnt", + "dl_he_5to8ru_cnt", + "dl_he_9to16ru_cnt", + "dl_he_gtr16ru_cnt", + + "ul_hetrig_su_cnt", + "ul_hetrig_2ru_cnt", + "ul_hetrig_3ru_cnt", + "ul_hetrig_4ru_cnt", + "ul_hetrig_5to8ru_cnt", + "ul_hetrig_9to16ru_cnt", + "ul_hetrig_gtr16ru_cnt", + "ul_hetrig_2mu_cnt", + "ul_hetrig_3mu_cnt", + "ul_hetrig_4mu_cnt", + + /* per vif counters */ + "v_tx_mode_cck", + "v_tx_mode_ofdm", + "v_tx_mode_ht", + "v_tx_mode_ht_gf", + "v_tx_mode_vht", + "v_tx_mode_he_su", + "v_tx_mode_he_ext_su", + "v_tx_mode_he_tb", + "v_tx_mode_he_mu", + "v_tx_bw_20", + "v_tx_bw_40", + "v_tx_bw_80", + "v_tx_bw_160", + "v_tx_mcs_0", + "v_tx_mcs_1", + "v_tx_mcs_2", + "v_tx_mcs_3", + "v_tx_mcs_4", + "v_tx_mcs_5", + "v_tx_mcs_6", + "v_tx_mcs_7", + "v_tx_mcs_8", + "v_tx_mcs_9", + "v_tx_mcs_10", + "v_tx_mcs_11", + "v_tx_nss_1", + "v_tx_nss_2", + "v_tx_nss_3", + "v_tx_nss_4", +}; + +#define MT7915_SSTATS_LEN ARRAY_SIZE(mt7915_gstrings_stats) + +/* Ethtool related API */ +static +void mt7915_get_et_strings(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + u32 sset, u8 *data) +{ + if (sset != ETH_SS_STATS) + return; + + memcpy(data, mt7915_gstrings_stats, sizeof(mt7915_gstrings_stats)); + data += sizeof(mt7915_gstrings_stats); + page_pool_ethtool_stats_get_strings(data); +} + +static +int mt7915_get_et_sset_count(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, int sset) +{ + if (sset != ETH_SS_STATS) + return 0; + + return MT7915_SSTATS_LEN + page_pool_ethtool_stats_get_count(); +} + +static void mt7915_ethtool_worker(void *wi_data, struct ieee80211_sta *sta) +{ + struct mt76_ethtool_worker_info *wi = wi_data; + struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv; + + if (msta->vif->mt76.idx != wi->idx) + return; + + mt76_ethtool_worker(wi, &msta->wcid.stats, false); +} + +static +void mt7915_get_et_stats(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ethtool_stats *stats, u64 *data) +{ + struct mt7915_dev *dev = mt7915_hw_dev(hw); + struct mt7915_phy *phy = mt7915_hw_phy(hw); + struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; + struct mt76_mib_stats *mib = &phy->mib; + struct mt76_ethtool_worker_info wi = { + .data = data, + .idx = mvif->mt76.idx, + }; + /* See mt7915_ampdu_stat_read_phy, etc */ + int i, ei = 0, stats_size; + + mutex_lock(&dev->mt76.mutex); + + mt7915_mac_update_stats(phy); + + data[ei++] = mib->tx_ampdu_cnt; + data[ei++] = mib->tx_stop_q_empty_cnt; + data[ei++] = mib->tx_mpdu_attempts_cnt; + data[ei++] = mib->tx_mpdu_success_cnt; + data[ei++] = mib->tx_rwp_fail_cnt; + data[ei++] = mib->tx_rwp_need_cnt; + data[ei++] = mib->tx_pkt_ebf_cnt; + data[ei++] = mib->tx_pkt_ibf_cnt; + + /* Tx ampdu stat */ + for (i = 0; i < 15 /*ARRAY_SIZE(bound)*/; i++) + data[ei++] = phy->mt76->aggr_stats[i]; + + data[ei++] = phy->mib.ba_miss_cnt; + + /* Tx Beamformer monitor */ + data[ei++] = mib->tx_bf_ibf_ppdu_cnt; + data[ei++] = mib->tx_bf_ebf_ppdu_cnt; + + /* Tx Beamformer Rx feedback monitor */ + data[ei++] = mib->tx_bf_rx_fb_all_cnt; + data[ei++] = mib->tx_bf_rx_fb_he_cnt; + data[ei++] = mib->tx_bf_rx_fb_vht_cnt; + data[ei++] = mib->tx_bf_rx_fb_ht_cnt; + + data[ei++] = mib->tx_bf_rx_fb_bw; + data[ei++] = mib->tx_bf_rx_fb_nc_cnt; + data[ei++] = mib->tx_bf_rx_fb_nr_cnt; + + /* Tx Beamformee Rx NDPA & Tx feedback report */ + data[ei++] = mib->tx_bf_fb_cpl_cnt; + data[ei++] = mib->tx_bf_fb_trig_cnt; + + /* Tx SU & MU counters */ + data[ei++] = mib->tx_bf_cnt; + data[ei++] = mib->tx_mu_mpdu_cnt; + data[ei++] = mib->tx_mu_acked_mpdu_cnt; + data[ei++] = mib->tx_su_acked_mpdu_cnt; + + /* Tx amsdu info (pack-count histogram) */ + for (i = 0; i < ARRAY_SIZE(mib->tx_amsdu); i++) + data[ei++] = mib->tx_amsdu[i]; + + /* rx counters */ + data[ei++] = mib->rx_fifo_full_cnt; + data[ei++] = mib->rx_mpdu_cnt; + data[ei++] = mib->channel_idle_cnt; + data[ei++] = mib->primary_cca_busy_time; + data[ei++] = mib->secondary_cca_busy_time; + data[ei++] = mib->primary_energy_detect_time; + data[ei++] = mib->cck_mdrdy_time; + data[ei++] = mib->ofdm_mdrdy_time; + data[ei++] = mib->green_mdrdy_time; + data[ei++] = mib->rx_vector_mismatch_cnt; + data[ei++] = mib->rx_delimiter_fail_cnt; + data[ei++] = mib->rx_mrdy_cnt; + data[ei++] = mib->rx_len_mismatch_cnt; + data[ei++] = mib->rx_ampdu_cnt; + data[ei++] = mib->rx_ampdu_bytes_cnt; + data[ei++] = mib->rx_ampdu_valid_subframe_cnt; + data[ei++] = mib->rx_ampdu_valid_subframe_bytes_cnt; + data[ei++] = mib->rx_pfdrop_cnt; + data[ei++] = mib->rx_vec_queue_overflow_drop_cnt; + data[ei++] = mib->rx_ba_cnt; + + data[ei++] = mib->dl_cck_cnt; + data[ei++] = mib->dl_ofdm_cnt; + data[ei++] = mib->dl_htmix_cnt; + data[ei++] = mib->dl_htgf_cnt; + data[ei++] = mib->dl_vht_su_cnt; + data[ei++] = mib->dl_vht_2mu_cnt; + data[ei++] = mib->dl_vht_3mu_cnt; + data[ei++] = mib->dl_vht_4mu_cnt; + data[ei++] = mib->dl_he_su_cnt; + data[ei++] = mib->dl_he_ext_su_cnt; + data[ei++] = mib->dl_he_2ru_cnt; + data[ei++] = mib->dl_he_2mu_cnt; + data[ei++] = mib->dl_he_3ru_cnt; + data[ei++] = mib->dl_he_3mu_cnt; + data[ei++] = mib->dl_he_4ru_cnt; + data[ei++] = mib->dl_he_4mu_cnt; + data[ei++] = mib->dl_he_5to8ru_cnt; + data[ei++] = mib->dl_he_9to16ru_cnt; + data[ei++] = mib->dl_he_gtr16ru_cnt; + + data[ei++] = mib->ul_hetrig_su_cnt; + data[ei++] = mib->ul_hetrig_2ru_cnt; + data[ei++] = mib->ul_hetrig_3ru_cnt; + data[ei++] = mib->ul_hetrig_4ru_cnt; + data[ei++] = mib->ul_hetrig_5to8ru_cnt; + data[ei++] = mib->ul_hetrig_9to16ru_cnt; + data[ei++] = mib->ul_hetrig_gtr16ru_cnt; + data[ei++] = mib->ul_hetrig_2mu_cnt; + data[ei++] = mib->ul_hetrig_3mu_cnt; + data[ei++] = mib->ul_hetrig_4mu_cnt; + + /* Add values for all stations owned by this vif */ + wi.initial_stat_idx = ei; + ieee80211_iterate_stations_atomic(hw, mt7915_ethtool_worker, &wi); + + mutex_unlock(&dev->mt76.mutex); + + if (wi.sta_count == 0) + return; + + ei += wi.worker_stat_count; + + mt76_ethtool_page_pool_stats(&dev->mt76, &data[ei], &ei); + + stats_size = MT7915_SSTATS_LEN + page_pool_ethtool_stats_get_count(); + if (ei != stats_size) + dev_err(dev->mt76.dev, "ei: %d size: %d", ei, stats_size); +} + +static void +mt7915_twt_teardown_request(struct ieee80211_hw *hw, + struct ieee80211_sta *sta, + u8 flowid) +{ + struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv; + struct mt7915_dev *dev = mt7915_hw_dev(hw); + + mutex_lock(&dev->mt76.mutex); + mt7915_mac_twt_teardown_flow(dev, msta, flowid); + mutex_unlock(&dev->mt76.mutex); +} + +static int +mt7915_set_frag_threshold(struct ieee80211_hw *hw, int radio_idx, u32 val) +{ + return 0; +} + +static int +mt7915_set_radar_background(struct ieee80211_hw *hw, + struct cfg80211_chan_def *chandef) +{ + struct mt7915_phy *phy = mt7915_hw_phy(hw); + struct mt7915_dev *dev = phy->dev; + int ret = -EINVAL; + bool running; + + mutex_lock(&dev->mt76.mutex); + + if (dev->mt76.region == NL80211_DFS_UNSET) + goto out; + + if (dev->rdd2_phy && dev->rdd2_phy != phy) { + /* rdd2 is already locked */ + ret = -EBUSY; + goto out; + } + + /* rdd2 already configured on a radar channel */ + running = dev->rdd2_phy && + cfg80211_chandef_valid(&dev->rdd2_chandef) && + !!(dev->rdd2_chandef.chan->flags & IEEE80211_CHAN_RADAR); + + if (!chandef || running || + !(chandef->chan->flags & IEEE80211_CHAN_RADAR)) { + ret = mt7915_mcu_rdd_background_enable(phy, NULL); + if (ret) + goto out; + + if (!running) + goto update_phy; + } + + ret = mt7915_mcu_rdd_background_enable(phy, chandef); + if (ret) + goto out; + +update_phy: + dev->rdd2_phy = chandef ? phy : NULL; + if (chandef) + dev->rdd2_chandef = *chandef; +out: + mutex_unlock(&dev->mt76.mutex); + + return ret; +} + +#ifdef CONFIG_NET_MEDIATEK_SOC_WED +static int +mt7915_net_fill_forward_path(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct net_device_path_ctx *ctx, + struct net_device_path *path) +{ + struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; + struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv; + struct mt7915_dev *dev = mt7915_hw_dev(hw); + struct mt7915_phy *phy = mt7915_hw_phy(hw); + struct mtk_wed_device *wed = &dev->mt76.mmio.wed; + + if (!mtk_wed_device_active(wed)) + return -ENODEV; + + if (msta->wcid.idx > 0xff) + return -EIO; + + path->type = DEV_PATH_MTK_WDMA; + path->dev = ctx->dev; + path->mtk_wdma.wdma_idx = wed->wdma_idx; + path->mtk_wdma.bss = mvif->mt76.idx; + path->mtk_wdma.wcid = is_mt7915(&dev->mt76) ? msta->wcid.idx : 0x3ff; + path->mtk_wdma.queue = phy->mt76->band_idx; + + ctx->dev = NULL; + + return 0; +} +#endif + +static void +mt7915_reconfig_complete(struct ieee80211_hw *hw, + enum ieee80211_reconfig_type reconfig_type) +{ + struct mt7915_phy *phy = mt7915_hw_phy(hw); + + ieee80211_wake_queues(hw); + ieee80211_queue_delayed_work(hw, &phy->mt76->mac_work, + MT7915_WATCHDOG_TIME); +} + +const struct ieee80211_ops mt7915_ops = { + .add_chanctx = ieee80211_emulate_add_chanctx, + .remove_chanctx = ieee80211_emulate_remove_chanctx, + .change_chanctx = ieee80211_emulate_change_chanctx, + .switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx, + .tx = mt7915_tx, + .start = mt7915_start, + .stop = mt7915_stop, + .add_interface = mt7915_add_interface, + .remove_interface = mt7915_remove_interface, + .config = mt7915_config, + .conf_tx = mt7915_conf_tx, + .configure_filter = mt7915_configure_filter, + .bss_info_changed = mt7915_bss_info_changed, + .start_ap = mt7915_start_ap, + .stop_ap = mt7915_stop_ap, + .sta_state = mt76_sta_state, + .sta_pre_rcu_remove = mt76_sta_pre_rcu_remove, + .link_sta_rc_update = mt7915_sta_rc_update, + .set_key = mt7915_set_key, + .ampdu_action = mt7915_ampdu_action, + .set_rts_threshold = mt7915_set_rts_threshold, + .wake_tx_queue = mt76_wake_tx_queue, + .sw_scan_start = mt76_sw_scan, + .sw_scan_complete = mt76_sw_scan_complete, + .release_buffered_frames = mt76_release_buffered_frames, + .get_txpower = mt76_get_txpower, + .set_sar_specs = mt7915_set_sar_specs, + .channel_switch_beacon = mt7915_channel_switch_beacon, + .get_stats = mt7915_get_stats, + .get_et_sset_count = mt7915_get_et_sset_count, + .get_et_stats = mt7915_get_et_stats, + .get_et_strings = mt7915_get_et_strings, + .get_tsf = mt7915_get_tsf, + .set_tsf = mt7915_set_tsf, + .offset_tsf = mt7915_offset_tsf, + .get_survey = mt76_get_survey, + .get_antenna = mt76_get_antenna, + .set_antenna = mt7915_set_antenna, + .set_bitrate_mask = mt7915_set_bitrate_mask, + .set_coverage_class = mt7915_set_coverage_class, + .sta_statistics = mt7915_sta_statistics, + .sta_set_txpwr = mt7915_sta_set_txpwr, + .sta_set_4addr = mt7915_sta_set_4addr, + .sta_set_decap_offload = mt7915_sta_set_decap_offload, + .add_twt_setup = mt7915_mac_add_twt_setup, + .twt_teardown_request = mt7915_twt_teardown_request, + .set_frag_threshold = mt7915_set_frag_threshold, + CFG80211_TESTMODE_CMD(mt76_testmode_cmd) + CFG80211_TESTMODE_DUMP(mt76_testmode_dump) +#ifdef CONFIG_MAC80211_DEBUGFS + .sta_add_debugfs = mt7915_sta_add_debugfs, +#endif + .set_radar_background = mt7915_set_radar_background, +#ifdef CONFIG_NET_MEDIATEK_SOC_WED + .net_fill_forward_path = mt7915_net_fill_forward_path, + .net_setup_tc = mt76_wed_net_setup_tc, +#endif + .reconfig_complete = mt7915_reconfig_complete, +}; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c new file mode 100644 index 000000000..5bfe03665 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c @@ -0,0 +1,4231 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2020 MediaTek Inc. */ + +#include +#include "mt7915.h" +#include "mcu.h" +#include "mac.h" +#include "eeprom.h" + +#define fw_name(_dev, name, ...) ({ \ + char *_fw; \ + switch (mt76_chip(&(_dev)->mt76)) { \ + case 0x7915: \ + _fw = MT7915_##name; \ + break; \ + case 0x7981: \ + _fw = MT7981_##name; \ + break; \ + case 0x7986: \ + _fw = MT7986_##name##__VA_ARGS__; \ + break; \ + default: \ + _fw = MT7916_##name; \ + break; \ + } \ + _fw; \ +}) + +#define fw_name_var(_dev, name) (mt7915_check_adie(dev, false) ? \ + fw_name(_dev, name) : \ + fw_name(_dev, name, _MT7975)) + +#define MCU_PATCH_ADDRESS 0x200000 + +#define HE_PHY(p, c) u8_get_bits(c, IEEE80211_HE_PHY_##p) +#define HE_MAC(m, c) u8_get_bits(c, IEEE80211_HE_MAC_##m) + +static bool sr_scene_detect = true; +module_param(sr_scene_detect, bool, 0644); +MODULE_PARM_DESC(sr_scene_detect, "Enable firmware scene detection algorithm"); + +static u8 +mt7915_mcu_get_sta_nss(u16 mcs_map) +{ + u8 nss; + + for (nss = 8; nss > 0; nss--) { + u8 nss_mcs = (mcs_map >> (2 * (nss - 1))) & 3; + + if (nss_mcs != IEEE80211_VHT_MCS_NOT_SUPPORTED) + break; + } + + return nss ? nss - 1 : 0; +} + +static void +mt7915_mcu_set_sta_he_mcs(struct ieee80211_sta *sta, __le16 *he_mcs, + u16 mcs_map) +{ + struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv; + struct mt7915_dev *dev = msta->vif->phy->dev; + enum nl80211_band band = msta->vif->phy->mt76->chandef.chan->band; + const u16 *mask = msta->vif->bitrate_mask.control[band].he_mcs; + int nss, max_nss = sta->deflink.rx_nss > 3 ? 4 : sta->deflink.rx_nss; + + for (nss = 0; nss < max_nss; nss++) { + int mcs; + + switch ((mcs_map >> (2 * nss)) & 0x3) { + case IEEE80211_HE_MCS_SUPPORT_0_11: + mcs = GENMASK(11, 0); + break; + case IEEE80211_HE_MCS_SUPPORT_0_9: + mcs = GENMASK(9, 0); + break; + case IEEE80211_HE_MCS_SUPPORT_0_7: + mcs = GENMASK(7, 0); + break; + default: + mcs = 0; + } + + mcs = mcs ? fls(mcs & mask[nss]) - 1 : -1; + + switch (mcs) { + case 0 ... 7: + mcs = IEEE80211_HE_MCS_SUPPORT_0_7; + break; + case 8 ... 9: + mcs = IEEE80211_HE_MCS_SUPPORT_0_9; + break; + case 10 ... 11: + mcs = IEEE80211_HE_MCS_SUPPORT_0_11; + break; + default: + mcs = IEEE80211_HE_MCS_NOT_SUPPORTED; + break; + } + mcs_map &= ~(0x3 << (nss * 2)); + mcs_map |= mcs << (nss * 2); + + /* only support 2ss on 160MHz for mt7915 */ + if (is_mt7915(&dev->mt76) && nss > 1 && + sta->deflink.bandwidth == IEEE80211_STA_RX_BW_160) + break; + } + + *he_mcs = cpu_to_le16(mcs_map); +} + +static void +mt7915_mcu_set_sta_vht_mcs(struct ieee80211_sta *sta, __le16 *vht_mcs, + const u16 *mask) +{ + struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv; + struct mt7915_dev *dev = msta->vif->phy->dev; + u16 mcs_map = le16_to_cpu(sta->deflink.vht_cap.vht_mcs.rx_mcs_map); + int nss, max_nss = sta->deflink.rx_nss > 3 ? 4 : sta->deflink.rx_nss; + u16 mcs; + + for (nss = 0; nss < max_nss; nss++, mcs_map >>= 2) { + switch (mcs_map & 0x3) { + case IEEE80211_VHT_MCS_SUPPORT_0_9: + mcs = GENMASK(9, 0); + break; + case IEEE80211_VHT_MCS_SUPPORT_0_8: + mcs = GENMASK(8, 0); + break; + case IEEE80211_VHT_MCS_SUPPORT_0_7: + mcs = GENMASK(7, 0); + break; + default: + mcs = 0; + } + + vht_mcs[nss] = cpu_to_le16(mcs & mask[nss]); + + /* only support 2ss on 160MHz for mt7915 */ + if (is_mt7915(&dev->mt76) && nss > 1 && + sta->deflink.bandwidth == IEEE80211_STA_RX_BW_160) + break; + } +} + +static void +mt7915_mcu_set_sta_ht_mcs(struct ieee80211_sta *sta, u8 *ht_mcs, + const u8 *mask) +{ + int nss, max_nss = sta->deflink.rx_nss > 3 ? 4 : sta->deflink.rx_nss; + + for (nss = 0; nss < max_nss; nss++) + ht_mcs[nss] = sta->deflink.ht_cap.mcs.rx_mask[nss] & mask[nss]; +} + +static int +mt7915_mcu_parse_response(struct mt76_dev *mdev, int cmd, + struct sk_buff *skb, int seq) +{ + struct mt7915_dev *dev = container_of(mdev, struct mt7915_dev, mt76); + struct mt76_connac2_mcu_rxd *rxd; + int ret = 0; + + if (!skb) { + dev_err(mdev->dev, "Message %08x (seq %d) timeout\n", + cmd, seq); + + if (!test_and_set_bit(MT76_MCU_RESET, &dev->mphy.state)) { + dev->recovery.restart = true; + wake_up(&dev->mt76.mcu.wait); + queue_work(dev->mt76.wq, &dev->reset_work); + wake_up(&dev->reset_wait); + } + + return -ETIMEDOUT; + } + + rxd = (struct mt76_connac2_mcu_rxd *)skb->data; + if (seq != rxd->seq && + !(rxd->eid == MCU_CMD_EXT_CID && + rxd->ext_eid == MCU_EXT_EVENT_WA_TX_STAT)) + return -EAGAIN; + + if (cmd == MCU_CMD(PATCH_SEM_CONTROL)) { + skb_pull(skb, sizeof(*rxd) - 4); + ret = *skb->data; + } else if (cmd == MCU_EXT_CMD(THERMAL_CTRL)) { + skb_pull(skb, sizeof(*rxd) + 4); + ret = le32_to_cpu(*(__le32 *)skb->data); + } else { + skb_pull(skb, sizeof(struct mt76_connac2_mcu_rxd)); + } + + return ret; +} + +static void +mt7915_mcu_set_timeout(struct mt76_dev *mdev, int cmd) +{ + mdev->mcu.timeout = 5 * HZ; + + if ((cmd & __MCU_CMD_FIELD_ID) != MCU_CMD_EXT_CID) + return; + + switch (FIELD_GET(__MCU_CMD_FIELD_EXT_ID, cmd)) { + case MCU_EXT_CMD_THERMAL_CTRL: + case MCU_EXT_CMD_GET_MIB_INFO: + case MCU_EXT_CMD_PHY_STAT_INFO: + case MCU_EXT_CMD_STA_REC_UPDATE: + case MCU_EXT_CMD_BSS_INFO_UPDATE: + mdev->mcu.timeout = 2 * HZ; + return; + case MCU_EXT_CMD_EFUSE_BUFFER_MODE: + mdev->mcu.timeout = 10 * HZ; + return; + default: + break; + } +} + +static int +mt7915_mcu_send_message(struct mt76_dev *mdev, struct sk_buff *skb, + int cmd, int *wait_seq) +{ + struct mt7915_dev *dev = container_of(mdev, struct mt7915_dev, mt76); + enum mt76_mcuq_id qid; + + if (cmd == MCU_CMD(FW_SCATTER)) + qid = MT_MCUQ_FWDL; + else if (test_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state)) + qid = MT_MCUQ_WA; + else + qid = MT_MCUQ_WM; + + mt7915_mcu_set_timeout(mdev, cmd); + + return mt76_tx_queue_skb_raw(dev, mdev->q_mcu[qid], skb, 0); +} + +int mt7915_mcu_wa_cmd(struct mt7915_dev *dev, int cmd, u32 a1, u32 a2, u32 a3) +{ + struct { + __le32 args[3]; + } req = { + .args = { + cpu_to_le32(a1), + cpu_to_le32(a2), + cpu_to_le32(a3), + }, + }; + + return mt76_mcu_send_msg(&dev->mt76, cmd, &req, sizeof(req), false); +} + +static void +mt7915_mcu_csa_finish(void *priv, u8 *mac, struct ieee80211_vif *vif) +{ + if (!vif->bss_conf.csa_active || vif->type == NL80211_IFTYPE_STATION) + return; + + ieee80211_csa_finish(vif, 0); +} + +static void +mt7915_mcu_rx_csa_notify(struct mt7915_dev *dev, struct sk_buff *skb) +{ + struct mt76_phy *mphy = &dev->mt76.phy; + struct mt7915_mcu_csa_notify *c; + + c = (struct mt7915_mcu_csa_notify *)skb->data; + + if (c->band_idx > MT_BAND1) + return; + + if ((c->band_idx && !dev->phy.mt76->band_idx) && + dev->mt76.phys[MT_BAND1]) + mphy = dev->mt76.phys[MT_BAND1]; + + ieee80211_iterate_active_interfaces_atomic(mphy->hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7915_mcu_csa_finish, mphy->hw); +} + +static void +mt7915_mcu_rx_thermal_notify(struct mt7915_dev *dev, struct sk_buff *skb) +{ + struct mt76_phy *mphy = &dev->mt76.phy; + struct mt7915_mcu_thermal_notify *t; + struct mt7915_phy *phy; + + t = (struct mt7915_mcu_thermal_notify *)skb->data; + if (t->ctrl.ctrl_id != THERMAL_PROTECT_ENABLE) + return; + + if (t->ctrl.band_idx > MT_BAND1) + return; + + if ((t->ctrl.band_idx && !dev->phy.mt76->band_idx) && + dev->mt76.phys[MT_BAND1]) + mphy = dev->mt76.phys[MT_BAND1]; + + phy = mphy->priv; + phy->throttle_state = t->ctrl.duty.duty_cycle; +} + +static void +mt7915_mcu_rx_radar_detected(struct mt7915_dev *dev, struct sk_buff *skb) +{ + struct mt76_phy *mphy = &dev->mt76.phy; + struct mt7915_mcu_rdd_report *r; + u32 sku; + + r = (struct mt7915_mcu_rdd_report *)skb->data; + + switch (r->rdd_idx) { + case MT_RDD_IDX_BAND0: + break; + case MT_RDD_IDX_BAND1: + sku = mt7915_check_adie(dev, true); + /* the main phy is bound to band 1 for this sku */ + if (is_mt7986(&dev->mt76) && + (sku == MT7975_ONE_ADIE || sku == MT7976_ONE_ADIE)) + break; + mphy = dev->mt76.phys[MT_BAND1]; + break; + case MT_RDD_IDX_BACKGROUND: + if (!dev->rdd2_phy) + return; + mphy = dev->rdd2_phy->mt76; + break; + default: + dev_err(dev->mt76.dev, "Unknown RDD idx %d\n", r->rdd_idx); + return; + } + + if (!mphy) + return; + + if (r->rdd_idx == MT_RDD_IDX_BACKGROUND) + cfg80211_background_radar_event(mphy->hw->wiphy, + &dev->rdd2_chandef, + GFP_ATOMIC); + else + ieee80211_radar_detected(mphy->hw, NULL); + dev->hw_pattern++; +} + +static void +mt7915_mcu_rx_log_message(struct mt7915_dev *dev, struct sk_buff *skb) +{ + struct mt76_connac2_mcu_rxd *rxd; + int len = skb->len - sizeof(*rxd); + const char *data, *type; + + rxd = (struct mt76_connac2_mcu_rxd *)skb->data; + data = (char *)&rxd[1]; + + switch (rxd->s2d_index) { + case 0: + if (mt7915_debugfs_rx_log(dev, data, len)) + return; + + type = "WM"; + break; + case 2: + type = "WA"; + break; + default: + type = "unknown"; + break; + } + + wiphy_info(mt76_hw(dev)->wiphy, "%s: %.*s", type, len, data); +} + +static void +mt7915_mcu_cca_finish(void *priv, u8 *mac, struct ieee80211_vif *vif) +{ + if (!vif->bss_conf.color_change_active || vif->type == NL80211_IFTYPE_STATION) + return; + + ieee80211_color_change_finish(vif, 0); +} + +static void +mt7915_mcu_rx_bcc_notify(struct mt7915_dev *dev, struct sk_buff *skb) +{ + struct mt76_phy *mphy = &dev->mt76.phy; + struct mt7915_mcu_bcc_notify *b; + + b = (struct mt7915_mcu_bcc_notify *)skb->data; + + if (b->band_idx > MT_BAND1) + return; + + if ((b->band_idx && !dev->phy.mt76->band_idx) && + dev->mt76.phys[MT_BAND1]) + mphy = dev->mt76.phys[MT_BAND1]; + + ieee80211_iterate_active_interfaces_atomic(mphy->hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7915_mcu_cca_finish, mphy->hw); +} + +static void +mt7915_mcu_rx_ps_sync(struct mt7915_dev *dev, struct sk_buff *skb) +{ + struct mt7915_mcu_ps_notify *p = (void *)skb->data; + struct mt76_wcid *wcid; + u16 wcid_idx; + + wcid_idx = p->wtbl_lower | (p->wtbl_higher << 8); + wcid = mt76_wcid_ptr(dev, wcid_idx); + if (!wcid || !wcid_to_sta(wcid)) + return; + + mt76_sta_ps_transition(&dev->mt76, wcid, !!p->ps_bit); +} + +static void +mt7915_mcu_rx_ext_event(struct mt7915_dev *dev, struct sk_buff *skb) +{ + struct mt76_connac2_mcu_rxd *rxd; + + rxd = (struct mt76_connac2_mcu_rxd *)skb->data; + switch (rxd->ext_eid) { + case MCU_EXT_EVENT_THERMAL_PROTECT: + mt7915_mcu_rx_thermal_notify(dev, skb); + break; + case MCU_EXT_EVENT_RDD_REPORT: + mt7915_mcu_rx_radar_detected(dev, skb); + break; + case MCU_EXT_EVENT_CSA_NOTIFY: + mt7915_mcu_rx_csa_notify(dev, skb); + break; + case MCU_EXT_EVENT_FW_LOG_2_HOST: + mt7915_mcu_rx_log_message(dev, skb); + break; + case MCU_EXT_EVENT_BCC_NOTIFY: + mt7915_mcu_rx_bcc_notify(dev, skb); + break; + case MCU_EXT_EVENT_PS_SYNC: + mt7915_mcu_rx_ps_sync(dev, skb); + break; + default: + break; + } +} + +static void +mt7915_mcu_rx_unsolicited_event(struct mt7915_dev *dev, struct sk_buff *skb) +{ + struct mt76_connac2_mcu_rxd *rxd; + + rxd = (struct mt76_connac2_mcu_rxd *)skb->data; + switch (rxd->eid) { + case MCU_EVENT_EXT: + mt7915_mcu_rx_ext_event(dev, skb); + break; + default: + break; + } + dev_kfree_skb(skb); +} + +void mt7915_mcu_rx_event(struct mt7915_dev *dev, struct sk_buff *skb) +{ + struct mt76_connac2_mcu_rxd *rxd; + + rxd = (struct mt76_connac2_mcu_rxd *)skb->data; + if ((rxd->ext_eid == MCU_EXT_EVENT_THERMAL_PROTECT || + rxd->ext_eid == MCU_EXT_EVENT_FW_LOG_2_HOST || + rxd->ext_eid == MCU_EXT_EVENT_ASSERT_DUMP || + rxd->ext_eid == MCU_EXT_EVENT_PS_SYNC || + rxd->ext_eid == MCU_EXT_EVENT_BCC_NOTIFY || + !rxd->seq) && + !(rxd->eid == MCU_CMD_EXT_CID && + rxd->ext_eid == MCU_EXT_EVENT_WA_TX_STAT)) + mt7915_mcu_rx_unsolicited_event(dev, skb); + else + mt76_mcu_rx_event(&dev->mt76, skb); +} + +static struct tlv * +mt7915_mcu_add_nested_subtlv(struct sk_buff *skb, int sub_tag, int sub_len, + __le16 *sub_ntlv, __le16 *len) +{ + struct tlv *ptlv, tlv = { + .tag = cpu_to_le16(sub_tag), + .len = cpu_to_le16(sub_len), + }; + + ptlv = skb_put_zero(skb, sub_len); + memcpy(ptlv, &tlv, sizeof(tlv)); + + le16_add_cpu(sub_ntlv, 1); + le16_add_cpu(len, sub_len); + + return ptlv; +} + +/** bss info **/ +struct mt7915_he_obss_narrow_bw_ru_data { + bool tolerated; +}; + +static void mt7915_check_he_obss_narrow_bw_ru_iter(struct wiphy *wiphy, + struct cfg80211_bss *bss, + void *_data) +{ + struct mt7915_he_obss_narrow_bw_ru_data *data = _data; + const struct element *elem; + + rcu_read_lock(); + elem = ieee80211_bss_get_elem(bss, WLAN_EID_EXT_CAPABILITY); + + if (!elem || elem->datalen <= 10 || + !(elem->data[10] & + WLAN_EXT_CAPA10_OBSS_NARROW_BW_RU_TOLERANCE_SUPPORT)) + data->tolerated = false; + + rcu_read_unlock(); +} + +static bool mt7915_check_he_obss_narrow_bw_ru(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct mt7915_he_obss_narrow_bw_ru_data iter_data = { + .tolerated = true, + }; + + if (!(vif->bss_conf.chanreq.oper.chan->flags & IEEE80211_CHAN_RADAR)) + return false; + + cfg80211_bss_iter(hw->wiphy, &vif->bss_conf.chanreq.oper, + mt7915_check_he_obss_narrow_bw_ru_iter, + &iter_data); + + /* + * If there is at least one AP on radar channel that cannot + * tolerate 26-tone RU UL OFDMA transmissions using HE TB PPDU. + */ + return !iter_data.tolerated; +} + +static void +mt7915_mcu_bss_rfch_tlv(struct sk_buff *skb, struct ieee80211_vif *vif, + struct mt7915_phy *phy) +{ + struct cfg80211_chan_def *chandef = &phy->mt76->chandef; + struct bss_info_rf_ch *ch; + struct tlv *tlv; + int freq1 = chandef->center_freq1; + + tlv = mt76_connac_mcu_add_tlv(skb, BSS_INFO_RF_CH, sizeof(*ch)); + + ch = (struct bss_info_rf_ch *)tlv; + ch->pri_ch = chandef->chan->hw_value; + ch->center_ch0 = ieee80211_frequency_to_channel(freq1); + ch->bw = mt76_connac_chan_bw(chandef); + + if (chandef->width == NL80211_CHAN_WIDTH_80P80) { + int freq2 = chandef->center_freq2; + + ch->center_ch1 = ieee80211_frequency_to_channel(freq2); + } + + if (vif->bss_conf.he_support && vif->type == NL80211_IFTYPE_STATION) { + struct mt76_phy *mphy = phy->mt76; + + ch->he_ru26_block = + mt7915_check_he_obss_narrow_bw_ru(mphy->hw, vif); + ch->he_all_disable = false; + } else { + ch->he_all_disable = true; + } +} + +static void +mt7915_mcu_bss_ra_tlv(struct sk_buff *skb, struct ieee80211_vif *vif, + struct mt7915_phy *phy) +{ + int max_nss = hweight8(phy->mt76->antenna_mask); + struct bss_info_ra *ra; + struct tlv *tlv; + + tlv = mt76_connac_mcu_add_tlv(skb, BSS_INFO_RA, sizeof(*ra)); + + ra = (struct bss_info_ra *)tlv; + ra->op_mode = vif->type == NL80211_IFTYPE_AP; + ra->adhoc_en = vif->type == NL80211_IFTYPE_ADHOC; + ra->short_preamble = true; + ra->tx_streams = max_nss; + ra->rx_streams = max_nss; + ra->algo = 4; + ra->train_up_rule = 2; + ra->train_up_high_thres = cpu_to_le16(110); + ra->train_up_rule_rssi = cpu_to_le16(-70); + ra->low_traffic_thres = cpu_to_le16(2); + ra->phy_cap = cpu_to_le32(0xfdf); + ra->interval = cpu_to_le32(500); + ra->fast_interval = cpu_to_le32(100); +} + +static void +mt7915_mcu_bss_he_tlv(struct sk_buff *skb, struct ieee80211_vif *vif, + struct mt7915_phy *phy) +{ +#define DEFAULT_HE_PE_DURATION 4 +#define DEFAULT_HE_DURATION_RTS_THRES 1023 + const struct ieee80211_sta_he_cap *cap; + struct bss_info_he *he; + struct tlv *tlv; + + cap = mt76_connac_get_he_phy_cap(phy->mt76, vif); + if (!cap) + return; + + tlv = mt76_connac_mcu_add_tlv(skb, BSS_INFO_HE_BASIC, sizeof(*he)); + + he = (struct bss_info_he *)tlv; + he->he_pe_duration = vif->bss_conf.htc_trig_based_pkt_ext; + if (!he->he_pe_duration) + he->he_pe_duration = DEFAULT_HE_PE_DURATION; + + he->he_rts_thres = cpu_to_le16(vif->bss_conf.frame_time_rts_th); + if (!he->he_rts_thres) + he->he_rts_thres = cpu_to_le16(DEFAULT_HE_DURATION_RTS_THRES); + + he->max_nss_mcs[CMD_HE_MCS_BW80] = cap->he_mcs_nss_supp.tx_mcs_80; + he->max_nss_mcs[CMD_HE_MCS_BW160] = cap->he_mcs_nss_supp.tx_mcs_160; + he->max_nss_mcs[CMD_HE_MCS_BW8080] = cap->he_mcs_nss_supp.tx_mcs_80p80; +} + +static void +mt7915_mcu_bss_hw_amsdu_tlv(struct sk_buff *skb) +{ +#define TXD_CMP_MAP1 GENMASK(15, 0) +#define TXD_CMP_MAP2 (GENMASK(31, 0) & ~BIT(23)) + struct bss_info_hw_amsdu *amsdu; + struct tlv *tlv; + + tlv = mt76_connac_mcu_add_tlv(skb, BSS_INFO_HW_AMSDU, sizeof(*amsdu)); + + amsdu = (struct bss_info_hw_amsdu *)tlv; + amsdu->cmp_bitmap_0 = cpu_to_le32(TXD_CMP_MAP1); + amsdu->cmp_bitmap_1 = cpu_to_le32(TXD_CMP_MAP2); + amsdu->trig_thres = cpu_to_le16(2); + amsdu->enable = true; +} + +static void +mt7915_mcu_bss_bmc_tlv(struct sk_buff *skb, struct mt7915_phy *phy) +{ + struct bss_info_bmc_rate *bmc; + struct cfg80211_chan_def *chandef = &phy->mt76->chandef; + enum nl80211_band band = chandef->chan->band; + struct tlv *tlv; + + tlv = mt76_connac_mcu_add_tlv(skb, BSS_INFO_BMC_RATE, sizeof(*bmc)); + + bmc = (struct bss_info_bmc_rate *)tlv; + if (band == NL80211_BAND_2GHZ) { + bmc->short_preamble = true; + } else { + bmc->bc_trans = cpu_to_le16(0x2000); + bmc->mc_trans = cpu_to_le16(0x2080); + } +} + +static int +mt7915_mcu_muar_config(struct mt7915_phy *phy, struct ieee80211_vif *vif, + bool bssid, bool enable) +{ + struct mt7915_dev *dev = phy->dev; + struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; + u32 idx = mvif->mt76.omac_idx - REPEATER_BSSID_START; + u32 mask = phy->omac_mask >> 32 & ~BIT(idx); + const u8 *addr = vif->addr; + struct { + u8 mode; + u8 force_clear; + u8 clear_bitmap[8]; + u8 entry_count; + u8 write; + u8 band; + + u8 index; + u8 bssid; + u8 addr[ETH_ALEN]; + } __packed req = { + .mode = !!mask || enable, + .entry_count = 1, + .write = 1, + .band = phy->mt76->band_idx, + .index = idx * 2 + bssid, + }; + + if (bssid) + addr = vif->bss_conf.bssid; + + if (enable) + ether_addr_copy(req.addr, addr); + + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(MUAR_UPDATE), &req, + sizeof(req), true); +} + +int mt7915_mcu_add_bss_info(struct mt7915_phy *phy, + struct ieee80211_vif *vif, int enable) +{ + struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; + struct mt7915_dev *dev = phy->dev; + struct sk_buff *skb; + + if (mvif->mt76.omac_idx >= REPEATER_BSSID_START) { + mt7915_mcu_muar_config(phy, vif, false, enable); + mt7915_mcu_muar_config(phy, vif, true, enable); + } + + skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76, NULL, + MT7915_BSS_UPDATE_MAX_SIZE); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + /* bss_omac must be first */ + if (enable) + mt76_connac_mcu_bss_omac_tlv(skb, vif); + + mt76_connac_mcu_bss_basic_tlv(skb, vif, NULL, phy->mt76, + mvif->sta.wcid.idx, enable); + + if (vif->type == NL80211_IFTYPE_MONITOR) + goto out; + + if (enable) { + mt7915_mcu_bss_rfch_tlv(skb, vif, phy); + mt7915_mcu_bss_bmc_tlv(skb, phy); + mt7915_mcu_bss_ra_tlv(skb, vif, phy); + mt7915_mcu_bss_hw_amsdu_tlv(skb); + + if (vif->bss_conf.he_support) + mt7915_mcu_bss_he_tlv(skb, vif, phy); + + if (mvif->mt76.omac_idx >= EXT_BSSID_START && + mvif->mt76.omac_idx < REPEATER_BSSID_START) + mt76_connac_mcu_bss_ext_tlv(skb, &mvif->mt76); + } +out: + return mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_EXT_CMD(BSS_INFO_UPDATE), true); +} + +/** starec & wtbl **/ +int mt7915_mcu_add_tx_ba(struct mt7915_dev *dev, + struct ieee80211_ampdu_params *params, + bool enable) +{ + struct mt7915_sta *msta = (struct mt7915_sta *)params->sta->drv_priv; + struct mt7915_vif *mvif = msta->vif; + int ret; + + mt76_worker_disable(&dev->mt76.tx_worker); + if (enable && !params->amsdu) + msta->wcid.amsdu = false; + ret = mt76_connac_mcu_sta_ba(&dev->mt76, &mvif->mt76, params, + MCU_EXT_CMD(STA_REC_UPDATE), + enable, true); + mt76_worker_enable(&dev->mt76.tx_worker); + + return ret; +} + +int mt7915_mcu_add_rx_ba(struct mt7915_dev *dev, + struct ieee80211_ampdu_params *params, + bool enable) +{ + struct mt7915_sta *msta = (struct mt7915_sta *)params->sta->drv_priv; + struct mt7915_vif *mvif = msta->vif; + + return mt76_connac_mcu_sta_ba(&dev->mt76, &mvif->mt76, params, + MCU_EXT_CMD(STA_REC_UPDATE), + enable, false); +} + +static void +mt7915_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_sta *sta, + struct ieee80211_vif *vif) +{ + struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; + struct ieee80211_he_cap_elem *elem = &sta->deflink.he_cap.he_cap_elem; + struct ieee80211_he_mcs_nss_supp mcs_map; + struct sta_rec_he *he; + struct tlv *tlv; + u32 cap = 0; + + if (!sta->deflink.he_cap.has_he) + return; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HE, sizeof(*he)); + + he = (struct sta_rec_he *)tlv; + + if (elem->mac_cap_info[0] & IEEE80211_HE_MAC_CAP0_HTC_HE) + cap |= STA_REC_HE_CAP_HTC; + + if (elem->mac_cap_info[2] & IEEE80211_HE_MAC_CAP2_BSR) + cap |= STA_REC_HE_CAP_BSR; + + if (elem->mac_cap_info[3] & IEEE80211_HE_MAC_CAP3_OMI_CONTROL) + cap |= STA_REC_HE_CAP_OM; + + if (elem->mac_cap_info[4] & IEEE80211_HE_MAC_CAP4_AMSDU_IN_AMPDU) + cap |= STA_REC_HE_CAP_AMSDU_IN_AMPDU; + + if (elem->mac_cap_info[4] & IEEE80211_HE_MAC_CAP4_BQR) + cap |= STA_REC_HE_CAP_BQR; + + if (elem->phy_cap_info[0] & + (IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_RU_MAPPING_IN_2G | + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_RU_MAPPING_IN_5G)) + cap |= STA_REC_HE_CAP_BW20_RU242_SUPPORT; + + if (mvif->cap.he_ldpc && + (elem->phy_cap_info[1] & + IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD)) + cap |= STA_REC_HE_CAP_LDPC; + + if (elem->phy_cap_info[1] & + IEEE80211_HE_PHY_CAP1_HE_LTF_AND_GI_FOR_HE_PPDUS_0_8US) + cap |= STA_REC_HE_CAP_SU_PPDU_1LTF_8US_GI; + + if (elem->phy_cap_info[2] & + IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US) + cap |= STA_REC_HE_CAP_NDP_4LTF_3DOT2MS_GI; + + if (elem->phy_cap_info[2] & + IEEE80211_HE_PHY_CAP2_STBC_TX_UNDER_80MHZ) + cap |= STA_REC_HE_CAP_LE_EQ_80M_TX_STBC; + + if (elem->phy_cap_info[2] & + IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ) + cap |= STA_REC_HE_CAP_LE_EQ_80M_RX_STBC; + + if (elem->phy_cap_info[6] & + IEEE80211_HE_PHY_CAP6_TRIG_CQI_FB) + cap |= STA_REC_HE_CAP_TRIG_CQI_FK; + + if (elem->phy_cap_info[6] & + IEEE80211_HE_PHY_CAP6_PARTIAL_BW_EXT_RANGE) + cap |= STA_REC_HE_CAP_PARTIAL_BW_EXT_RANGE; + + if (elem->phy_cap_info[7] & + IEEE80211_HE_PHY_CAP7_HE_SU_MU_PPDU_4XLTF_AND_08_US_GI) + cap |= STA_REC_HE_CAP_SU_MU_PPDU_4LTF_8US_GI; + + if (elem->phy_cap_info[7] & + IEEE80211_HE_PHY_CAP7_STBC_TX_ABOVE_80MHZ) + cap |= STA_REC_HE_CAP_GT_80M_TX_STBC; + + if (elem->phy_cap_info[7] & + IEEE80211_HE_PHY_CAP7_STBC_RX_ABOVE_80MHZ) + cap |= STA_REC_HE_CAP_GT_80M_RX_STBC; + + if (elem->phy_cap_info[8] & + IEEE80211_HE_PHY_CAP8_HE_ER_SU_PPDU_4XLTF_AND_08_US_GI) + cap |= STA_REC_HE_CAP_ER_SU_PPDU_4LTF_8US_GI; + + if (elem->phy_cap_info[8] & + IEEE80211_HE_PHY_CAP8_HE_ER_SU_1XLTF_AND_08_US_GI) + cap |= STA_REC_HE_CAP_ER_SU_PPDU_1LTF_8US_GI; + + if (elem->phy_cap_info[9] & + IEEE80211_HE_PHY_CAP9_TX_1024_QAM_LESS_THAN_242_TONE_RU) + cap |= STA_REC_HE_CAP_TX_1024QAM_UNDER_RU242; + + if (elem->phy_cap_info[9] & + IEEE80211_HE_PHY_CAP9_RX_1024_QAM_LESS_THAN_242_TONE_RU) + cap |= STA_REC_HE_CAP_RX_1024QAM_UNDER_RU242; + + he->he_cap = cpu_to_le32(cap); + + mcs_map = sta->deflink.he_cap.he_mcs_nss_supp; + switch (sta->deflink.bandwidth) { + case IEEE80211_STA_RX_BW_160: + if (elem->phy_cap_info[0] & + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G) + mt7915_mcu_set_sta_he_mcs(sta, + &he->max_nss_mcs[CMD_HE_MCS_BW8080], + le16_to_cpu(mcs_map.rx_mcs_80p80)); + + mt7915_mcu_set_sta_he_mcs(sta, + &he->max_nss_mcs[CMD_HE_MCS_BW160], + le16_to_cpu(mcs_map.rx_mcs_160)); + fallthrough; + default: + mt7915_mcu_set_sta_he_mcs(sta, + &he->max_nss_mcs[CMD_HE_MCS_BW80], + le16_to_cpu(mcs_map.rx_mcs_80)); + break; + } + + he->t_frame_dur = + HE_MAC(CAP1_TF_MAC_PAD_DUR_MASK, elem->mac_cap_info[1]); + he->max_ampdu_exp = + HE_MAC(CAP3_MAX_AMPDU_LEN_EXP_MASK, elem->mac_cap_info[3]); + + he->bw_set = + HE_PHY(CAP0_CHANNEL_WIDTH_SET_MASK, elem->phy_cap_info[0]); + he->device_class = + HE_PHY(CAP1_DEVICE_CLASS_A, elem->phy_cap_info[1]); + he->punc_pream_rx = + HE_PHY(CAP1_PREAMBLE_PUNC_RX_MASK, elem->phy_cap_info[1]); + + he->dcm_tx_mode = + HE_PHY(CAP3_DCM_MAX_CONST_TX_MASK, elem->phy_cap_info[3]); + he->dcm_tx_max_nss = + HE_PHY(CAP3_DCM_MAX_TX_NSS_2, elem->phy_cap_info[3]); + he->dcm_rx_mode = + HE_PHY(CAP3_DCM_MAX_CONST_RX_MASK, elem->phy_cap_info[3]); + he->dcm_rx_max_nss = + HE_PHY(CAP3_DCM_MAX_RX_NSS_2, elem->phy_cap_info[3]); + he->dcm_max_ru = + HE_PHY(CAP8_DCM_MAX_RU_MASK, elem->phy_cap_info[8]); + + he->pkt_ext = 2; +} + +static void +mt7915_mcu_sta_muru_tlv(struct mt7915_dev *dev, struct sk_buff *skb, + struct ieee80211_sta *sta, struct ieee80211_vif *vif) +{ + struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; + struct ieee80211_he_cap_elem *elem = &sta->deflink.he_cap.he_cap_elem; + struct sta_rec_muru *muru; + struct tlv *tlv; + + if (vif->type != NL80211_IFTYPE_STATION && + vif->type != NL80211_IFTYPE_AP) + return; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_MURU, sizeof(*muru)); + + muru = (struct sta_rec_muru *)tlv; + + muru->cfg.mimo_dl_en = mvif->cap.he_mu_ebfer || + mvif->cap.vht_mu_ebfer || + mvif->cap.vht_mu_ebfee; + if (!is_mt7915(&dev->mt76)) + muru->cfg.mimo_ul_en = true; + muru->cfg.ofdma_dl_en = true; + + if (sta->deflink.vht_cap.vht_supported) + muru->mimo_dl.vht_mu_bfee = + !!(sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE); + + if (!sta->deflink.he_cap.has_he) + return; + + muru->mimo_dl.partial_bw_dl_mimo = + HE_PHY(CAP6_PARTIAL_BANDWIDTH_DL_MUMIMO, elem->phy_cap_info[6]); + + muru->mimo_ul.full_ul_mimo = + HE_PHY(CAP2_UL_MU_FULL_MU_MIMO, elem->phy_cap_info[2]); + muru->mimo_ul.partial_ul_mimo = + HE_PHY(CAP2_UL_MU_PARTIAL_MU_MIMO, elem->phy_cap_info[2]); + + muru->ofdma_dl.punc_pream_rx = + HE_PHY(CAP1_PREAMBLE_PUNC_RX_MASK, elem->phy_cap_info[1]); + muru->ofdma_dl.he_20m_in_40m_2g = + HE_PHY(CAP8_20MHZ_IN_40MHZ_HE_PPDU_IN_2G, elem->phy_cap_info[8]); + muru->ofdma_dl.he_20m_in_160m = + HE_PHY(CAP8_20MHZ_IN_160MHZ_HE_PPDU, elem->phy_cap_info[8]); + muru->ofdma_dl.he_80m_in_160m = + HE_PHY(CAP8_80MHZ_IN_160MHZ_HE_PPDU, elem->phy_cap_info[8]); + + muru->ofdma_ul.t_frame_dur = + HE_MAC(CAP1_TF_MAC_PAD_DUR_MASK, elem->mac_cap_info[1]); + muru->ofdma_ul.mu_cascading = + HE_MAC(CAP2_MU_CASCADING, elem->mac_cap_info[2]); + muru->ofdma_ul.uo_ra = + HE_MAC(CAP3_OFDMA_RA, elem->mac_cap_info[3]); + muru->ofdma_ul.rx_ctrl_frame_to_mbss = + HE_MAC(CAP3_RX_CTRL_FRAME_TO_MULTIBSS, elem->mac_cap_info[3]); +} + +static void +mt7915_mcu_sta_ht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta) +{ + struct sta_rec_ht *ht; + struct tlv *tlv; + + if (!sta->deflink.ht_cap.ht_supported) + return; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HT, sizeof(*ht)); + + ht = (struct sta_rec_ht *)tlv; + ht->ht_cap = cpu_to_le16(sta->deflink.ht_cap.cap); +} + +static void +mt7915_mcu_sta_vht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta) +{ + struct sta_rec_vht *vht; + struct tlv *tlv; + + if (!sta->deflink.vht_cap.vht_supported) + return; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_VHT, sizeof(*vht)); + + vht = (struct sta_rec_vht *)tlv; + vht->vht_cap = cpu_to_le32(sta->deflink.vht_cap.cap); + vht->vht_rx_mcs_map = sta->deflink.vht_cap.vht_mcs.rx_mcs_map; + vht->vht_tx_mcs_map = sta->deflink.vht_cap.vht_mcs.tx_mcs_map; +} + +static void +mt7915_mcu_sta_amsdu_tlv(struct mt7915_dev *dev, struct sk_buff *skb, + struct ieee80211_vif *vif, struct ieee80211_sta *sta) +{ + struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv; + struct sta_rec_amsdu *amsdu; + struct tlv *tlv; + + if (vif->type != NL80211_IFTYPE_STATION && + vif->type != NL80211_IFTYPE_AP) + return; + + if (!sta->deflink.agg.max_amsdu_len) + return; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HW_AMSDU, sizeof(*amsdu)); + amsdu = (struct sta_rec_amsdu *)tlv; + amsdu->max_amsdu_num = 8; + amsdu->amsdu_en = true; + msta->wcid.amsdu = true; + + switch (sta->deflink.agg.max_amsdu_len) { + case IEEE80211_MAX_MPDU_LEN_VHT_11454: + if (!is_mt7915(&dev->mt76)) { + amsdu->max_mpdu_size = + IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454; + return; + } + fallthrough; + case IEEE80211_MAX_MPDU_LEN_HT_7935: + case IEEE80211_MAX_MPDU_LEN_VHT_7991: + amsdu->max_mpdu_size = IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991; + return; + default: + amsdu->max_mpdu_size = IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895; + return; + } +} + +static int +mt7915_mcu_sta_wtbl_tlv(struct mt7915_dev *dev, struct sk_buff *skb, + struct ieee80211_vif *vif, struct ieee80211_sta *sta) +{ + struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; + struct mt7915_sta *msta; + struct wtbl_req_hdr *wtbl_hdr; + struct mt76_wcid *wcid; + struct tlv *tlv; + + msta = sta ? (struct mt7915_sta *)sta->drv_priv : &mvif->sta; + wcid = sta ? &msta->wcid : NULL; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_WTBL, sizeof(struct tlv)); + wtbl_hdr = mt76_connac_mcu_alloc_wtbl_req(&dev->mt76, &msta->wcid, + WTBL_RESET_AND_SET, tlv, + &skb); + if (IS_ERR(wtbl_hdr)) + return PTR_ERR(wtbl_hdr); + + mt76_connac_mcu_wtbl_generic_tlv(&dev->mt76, skb, vif, sta, tlv, + wtbl_hdr); + mt76_connac_mcu_wtbl_hdr_trans_tlv(skb, vif, wcid, tlv, wtbl_hdr); + if (sta) + mt76_connac_mcu_wtbl_ht_tlv(&dev->mt76, skb, sta, tlv, + wtbl_hdr, mvif->cap.ht_ldpc, + mvif->cap.vht_ldpc); + + return 0; +} + +static inline bool +mt7915_is_ebf_supported(struct mt7915_phy *phy, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, bool bfee) +{ + struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; + int sts = hweight16(phy->mt76->chainmask); + + if (vif->type != NL80211_IFTYPE_STATION && + vif->type != NL80211_IFTYPE_AP) + return false; + + if (!bfee && sts < 2) + return false; + + if (sta->deflink.he_cap.has_he) { + struct ieee80211_he_cap_elem *pe = &sta->deflink.he_cap.he_cap_elem; + + if (bfee) + return mvif->cap.he_su_ebfee && + HE_PHY(CAP3_SU_BEAMFORMER, pe->phy_cap_info[3]); + else + return mvif->cap.he_su_ebfer && + HE_PHY(CAP4_SU_BEAMFORMEE, pe->phy_cap_info[4]); + } + + if (sta->deflink.vht_cap.vht_supported) { + u32 cap = sta->deflink.vht_cap.cap; + + if (bfee) + return mvif->cap.vht_su_ebfee && + (cap & IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE); + else + return mvif->cap.vht_su_ebfer && + (cap & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE); + } + + return false; +} + +static void +mt7915_mcu_sta_sounding_rate(struct sta_rec_bf *bf) +{ + bf->sounding_phy = MT_PHY_TYPE_OFDM; + bf->ndp_rate = 0; /* mcs0 */ + bf->ndpa_rate = MT7915_CFEND_RATE_DEFAULT; /* ofdm 24m */ + bf->rept_poll_rate = MT7915_CFEND_RATE_DEFAULT; /* ofdm 24m */ +} + +static void +mt7915_mcu_sta_bfer_ht(struct ieee80211_sta *sta, struct mt7915_phy *phy, + struct sta_rec_bf *bf) +{ + struct ieee80211_mcs_info *mcs = &sta->deflink.ht_cap.mcs; + u8 n = 0; + + bf->tx_mode = MT_PHY_TYPE_HT; + + if ((mcs->tx_params & IEEE80211_HT_MCS_TX_RX_DIFF) && + (mcs->tx_params & IEEE80211_HT_MCS_TX_DEFINED)) + n = FIELD_GET(IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK, + mcs->tx_params); + else if (mcs->rx_mask[3]) + n = 3; + else if (mcs->rx_mask[2]) + n = 2; + else if (mcs->rx_mask[1]) + n = 1; + + bf->nrow = hweight8(phy->mt76->chainmask) - 1; + bf->ncol = min_t(u8, bf->nrow, n); + bf->ibf_ncol = n; +} + +static void +mt7915_mcu_sta_bfer_vht(struct ieee80211_sta *sta, struct mt7915_phy *phy, + struct sta_rec_bf *bf, bool explicit) +{ + struct ieee80211_sta_vht_cap *pc = &sta->deflink.vht_cap; + struct ieee80211_sta_vht_cap *vc = &phy->mt76->sband_5g.sband.vht_cap; + u16 mcs_map = le16_to_cpu(pc->vht_mcs.rx_mcs_map); + u8 nss_mcs = mt7915_mcu_get_sta_nss(mcs_map); + u8 tx_ant = hweight8(phy->mt76->chainmask) - 1; + + bf->tx_mode = MT_PHY_TYPE_VHT; + + if (explicit) { + u8 sts, snd_dim; + + mt7915_mcu_sta_sounding_rate(bf); + + sts = FIELD_GET(IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK, + pc->cap); + snd_dim = FIELD_GET(IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK, + vc->cap); + bf->nrow = min_t(u8, min_t(u8, snd_dim, sts), tx_ant); + bf->ncol = min_t(u8, nss_mcs, bf->nrow); + bf->ibf_ncol = bf->ncol; + + if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_160) + bf->nrow = 1; + } else { + bf->nrow = tx_ant; + bf->ncol = min_t(u8, nss_mcs, bf->nrow); + bf->ibf_ncol = nss_mcs; + + if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_160) + bf->ibf_nrow = 1; + } +} + +static void +mt7915_mcu_sta_bfer_he(struct ieee80211_sta *sta, + const struct ieee80211_sta_he_cap *vc, + struct sta_rec_bf *bf) +{ + struct ieee80211_sta_he_cap *pc = &sta->deflink.he_cap; + struct ieee80211_he_cap_elem *pe = &pc->he_cap_elem; + const struct ieee80211_he_cap_elem *ve = &vc->he_cap_elem; + u16 mcs_map = le16_to_cpu(pc->he_mcs_nss_supp.rx_mcs_80); + u8 nss_mcs = mt7915_mcu_get_sta_nss(mcs_map); + u8 snd_dim, sts; + + bf->tx_mode = MT_PHY_TYPE_HE_SU; + + mt7915_mcu_sta_sounding_rate(bf); + + bf->trigger_su = HE_PHY(CAP6_TRIG_SU_BEAMFORMING_FB, + pe->phy_cap_info[6]); + bf->trigger_mu = HE_PHY(CAP6_TRIG_MU_BEAMFORMING_PARTIAL_BW_FB, + pe->phy_cap_info[6]); + snd_dim = HE_PHY(CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_MASK, + ve->phy_cap_info[5]); + sts = HE_PHY(CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_MASK, + pe->phy_cap_info[4]); + bf->nrow = min_t(u8, snd_dim, sts); + bf->ncol = min_t(u8, nss_mcs, bf->nrow); + bf->ibf_ncol = bf->ncol; + + if (sta->deflink.bandwidth != IEEE80211_STA_RX_BW_160) + return; + + /* go over for 160MHz and 80p80 */ + if (pe->phy_cap_info[0] & + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G) { + mcs_map = le16_to_cpu(pc->he_mcs_nss_supp.rx_mcs_160); + nss_mcs = mt7915_mcu_get_sta_nss(mcs_map); + + bf->ncol_gt_bw80 = nss_mcs; + } + + if (pe->phy_cap_info[0] & + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G) { + mcs_map = le16_to_cpu(pc->he_mcs_nss_supp.rx_mcs_80p80); + nss_mcs = mt7915_mcu_get_sta_nss(mcs_map); + + if (bf->ncol_gt_bw80) + bf->ncol_gt_bw80 = min_t(u8, bf->ncol_gt_bw80, nss_mcs); + else + bf->ncol_gt_bw80 = nss_mcs; + } + + snd_dim = HE_PHY(CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_MASK, + ve->phy_cap_info[5]); + sts = HE_PHY(CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_MASK, + pe->phy_cap_info[4]); + + bf->nrow_gt_bw80 = min_t(int, snd_dim, sts); +} + +static void +mt7915_mcu_sta_bfer_tlv(struct mt7915_dev *dev, struct sk_buff *skb, + struct ieee80211_vif *vif, struct ieee80211_sta *sta) +{ + struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; + struct mt7915_phy *phy = mvif->phy; + const struct ieee80211_sta_he_cap *vc = NULL; + int tx_ant = hweight8(phy->mt76->chainmask) - 1; + struct sta_rec_bf *bf; + struct tlv *tlv; + static const u8 matrix[4][4] = { + {0, 0, 0, 0}, + {1, 1, 0, 0}, /* 2x1, 2x2, 2x3, 2x4 */ + {2, 4, 4, 0}, /* 3x1, 3x2, 3x3, 3x4 */ + {3, 5, 6, 0} /* 4x1, 4x2, 4x3, 4x4 */ + }; + bool ebf; + + if (!(sta->deflink.ht_cap.ht_supported || sta->deflink.he_cap.has_he)) + return; + + ebf = mt7915_is_ebf_supported(phy, vif, sta, false); + if (!ebf && !dev->ibf) + return; + + if (sta->deflink.he_cap.has_he && ebf) { + vc = mt76_connac_get_he_phy_cap(phy->mt76, vif); + if (!vc) + return; + } + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_BF, sizeof(*bf)); + bf = (struct sta_rec_bf *)tlv; + + /* he: eBF only, in accordance with spec + * vht: support eBF and iBF + * ht: iBF only, since mac80211 lacks of eBF support + */ + if (sta->deflink.he_cap.has_he && ebf) + mt7915_mcu_sta_bfer_he(sta, vc, bf); + else if (sta->deflink.vht_cap.vht_supported) + mt7915_mcu_sta_bfer_vht(sta, phy, bf, ebf); + else if (sta->deflink.ht_cap.ht_supported) + mt7915_mcu_sta_bfer_ht(sta, phy, bf); + else + return; + + bf->bf_cap = ebf ? ebf : dev->ibf << 1; + bf->bw = sta->deflink.bandwidth; + bf->ibf_dbw = sta->deflink.bandwidth; + bf->ibf_nrow = tx_ant; + + if (!ebf && sta->deflink.bandwidth <= IEEE80211_STA_RX_BW_40 && !bf->ncol) + bf->ibf_timeout = 0x48; + else + bf->ibf_timeout = 0x18; + + if (ebf && bf->nrow != tx_ant) + bf->mem_20m = matrix[tx_ant][bf->ncol]; + else + bf->mem_20m = matrix[bf->nrow][bf->ncol]; + + switch (sta->deflink.bandwidth) { + case IEEE80211_STA_RX_BW_160: + case IEEE80211_STA_RX_BW_80: + bf->mem_total = bf->mem_20m * 2; + break; + case IEEE80211_STA_RX_BW_40: + bf->mem_total = bf->mem_20m; + break; + case IEEE80211_STA_RX_BW_20: + default: + break; + } +} + +static void +mt7915_mcu_sta_bfee_tlv(struct mt7915_dev *dev, struct sk_buff *skb, + struct ieee80211_vif *vif, struct ieee80211_sta *sta) +{ + struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; + struct mt7915_phy *phy = mvif->phy; + int tx_ant = hweight8(phy->mt76->chainmask) - 1; + struct sta_rec_bfee *bfee; + struct tlv *tlv; + u8 nrow = 0; + + if (!(sta->deflink.vht_cap.vht_supported || sta->deflink.he_cap.has_he)) + return; + + if (!mt7915_is_ebf_supported(phy, vif, sta, true)) + return; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_BFEE, sizeof(*bfee)); + bfee = (struct sta_rec_bfee *)tlv; + + if (sta->deflink.he_cap.has_he) { + struct ieee80211_he_cap_elem *pe = &sta->deflink.he_cap.he_cap_elem; + + nrow = HE_PHY(CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_MASK, + pe->phy_cap_info[5]); + } else if (sta->deflink.vht_cap.vht_supported) { + struct ieee80211_sta_vht_cap *pc = &sta->deflink.vht_cap; + + nrow = FIELD_GET(IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK, + pc->cap); + } + + /* reply with identity matrix to avoid 2x2 BF negative gain */ + bfee->fb_identity_matrix = (nrow == 1 && tx_ant == 2); +} + +static enum mcu_mmps_mode +mt7915_mcu_get_mmps_mode(enum ieee80211_smps_mode smps) +{ + switch (smps) { + case IEEE80211_SMPS_OFF: + return MCU_MMPS_DISABLE; + case IEEE80211_SMPS_STATIC: + return MCU_MMPS_STATIC; + case IEEE80211_SMPS_DYNAMIC: + return MCU_MMPS_DYNAMIC; + default: + return MCU_MMPS_DISABLE; + } +} + +int mt7915_mcu_set_fixed_rate_ctrl(struct mt7915_dev *dev, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + void *data, u32 field) +{ + struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; + struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv; + struct sta_phy *phy = data; + struct sta_rec_ra_fixed *ra; + struct sk_buff *skb; + struct tlv *tlv; + + skb = mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76, + &msta->wcid); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_RA_UPDATE, sizeof(*ra)); + ra = (struct sta_rec_ra_fixed *)tlv; + + switch (field) { + case RATE_PARAM_AUTO: + break; + case RATE_PARAM_FIXED: + case RATE_PARAM_FIXED_MCS: + case RATE_PARAM_FIXED_GI: + case RATE_PARAM_FIXED_HE_LTF: + if (phy) + ra->phy = *phy; + break; + case RATE_PARAM_MMPS_UPDATE: + ra->mmps_mode = mt7915_mcu_get_mmps_mode(sta->deflink.smps_mode); + break; + case RATE_PARAM_SPE_UPDATE: + ra->spe_idx = *(u8 *)data; + break; + default: + break; + } + ra->field = cpu_to_le32(field); + + return mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_EXT_CMD(STA_REC_UPDATE), true); +} + +int mt7915_mcu_add_smps(struct mt7915_dev *dev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; + struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv; + struct wtbl_req_hdr *wtbl_hdr; + struct tlv *sta_wtbl; + struct sk_buff *skb; + int ret; + + skb = mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76, + &msta->wcid); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + sta_wtbl = mt76_connac_mcu_add_tlv(skb, STA_REC_WTBL, + sizeof(struct tlv)); + wtbl_hdr = mt76_connac_mcu_alloc_wtbl_req(&dev->mt76, &msta->wcid, + WTBL_SET, sta_wtbl, &skb); + if (IS_ERR(wtbl_hdr)) + return PTR_ERR(wtbl_hdr); + + mt76_connac_mcu_wtbl_smps_tlv(skb, sta, sta_wtbl, wtbl_hdr); + + ret = mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_EXT_CMD(STA_REC_UPDATE), true); + if (ret) + return ret; + + return mt7915_mcu_set_fixed_rate_ctrl(dev, vif, sta, NULL, + RATE_PARAM_MMPS_UPDATE); +} + +static int +mt7915_mcu_set_spe_idx(struct mt7915_dev *dev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; + struct mt76_phy *mphy = mvif->phy->mt76; + u8 spe_idx = mt76_connac_spe_idx(mphy->antenna_mask); + + return mt7915_mcu_set_fixed_rate_ctrl(dev, vif, sta, &spe_idx, + RATE_PARAM_SPE_UPDATE); +} + +static int +mt7915_mcu_add_rate_ctrl_fixed(struct mt7915_dev *dev, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; + struct cfg80211_chan_def *chandef = &mvif->phy->mt76->chandef; + struct cfg80211_bitrate_mask *mask = &mvif->bitrate_mask; + enum nl80211_band band = chandef->chan->band; + struct sta_phy phy = {}; + int ret, nrates = 0; + +#define __sta_phy_bitrate_mask_check(_mcs, _gi, _ht, _he) \ + do { \ + u8 i, gi = mask->control[band]._gi; \ + gi = (_he) ? gi : gi == NL80211_TXRATE_FORCE_SGI; \ + for (i = 0; i <= sta->deflink.bandwidth; i++) { \ + phy.sgi |= gi << (i << (_he)); \ + phy.he_ltf |= mask->control[band].he_ltf << (i << (_he));\ + } \ + for (i = 0; i < ARRAY_SIZE(mask->control[band]._mcs); i++) { \ + if (!mask->control[band]._mcs[i]) \ + continue; \ + nrates += hweight16(mask->control[band]._mcs[i]); \ + phy.mcs = ffs(mask->control[band]._mcs[i]) - 1; \ + if (_ht) \ + phy.mcs += 8 * i; \ + } \ + } while (0) + + if (sta->deflink.he_cap.has_he) { + __sta_phy_bitrate_mask_check(he_mcs, he_gi, 0, 1); + } else if (sta->deflink.vht_cap.vht_supported) { + __sta_phy_bitrate_mask_check(vht_mcs, gi, 0, 0); + } else if (sta->deflink.ht_cap.ht_supported) { + __sta_phy_bitrate_mask_check(ht_mcs, gi, 1, 0); + } else { + nrates = hweight32(mask->control[band].legacy); + phy.mcs = ffs(mask->control[band].legacy) - 1; + } +#undef __sta_phy_bitrate_mask_check + + /* fall back to auto rate control */ + if (mask->control[band].gi == NL80211_TXRATE_DEFAULT_GI && + mask->control[band].he_gi == GENMASK(7, 0) && + mask->control[band].he_ltf == GENMASK(7, 0) && + nrates != 1) + return 0; + + /* fixed single rate */ + if (nrates == 1) { + ret = mt7915_mcu_set_fixed_rate_ctrl(dev, vif, sta, &phy, + RATE_PARAM_FIXED_MCS); + if (ret) + return ret; + } + + /* fixed GI */ + if (mask->control[band].gi != NL80211_TXRATE_DEFAULT_GI || + mask->control[band].he_gi != GENMASK(7, 0)) { + struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv; + u32 addr; + + /* firmware updates only TXCMD but doesn't take WTBL into + * account, so driver should update here to reflect the + * actual txrate hardware sends out. + */ + addr = mt7915_mac_wtbl_lmac_addr(dev, msta->wcid.idx, 7); + if (sta->deflink.he_cap.has_he) + mt76_rmw_field(dev, addr, GENMASK(31, 24), phy.sgi); + else + mt76_rmw_field(dev, addr, GENMASK(15, 12), phy.sgi); + + ret = mt7915_mcu_set_fixed_rate_ctrl(dev, vif, sta, &phy, + RATE_PARAM_FIXED_GI); + if (ret) + return ret; + } + + /* fixed HE_LTF */ + if (mask->control[band].he_ltf != GENMASK(7, 0)) { + ret = mt7915_mcu_set_fixed_rate_ctrl(dev, vif, sta, &phy, + RATE_PARAM_FIXED_HE_LTF); + if (ret) + return ret; + } + + return mt7915_mcu_set_spe_idx(dev, vif, sta); +} + +static void +mt7915_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7915_dev *dev, + struct ieee80211_vif *vif, struct ieee80211_sta *sta) +{ + struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; + struct mt76_phy *mphy = mvif->phy->mt76; + struct cfg80211_chan_def *chandef = &mphy->chandef; + struct cfg80211_bitrate_mask *mask = &mvif->bitrate_mask; + enum nl80211_band band = chandef->chan->band; + struct sta_rec_ra *ra; + struct tlv *tlv; + u32 supp_rate = sta->deflink.supp_rates[band]; + u32 cap = sta->wme ? STA_CAP_WMM : 0; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_RA, sizeof(*ra)); + ra = (struct sta_rec_ra *)tlv; + + ra->valid = true; + ra->auto_rate = true; + ra->phy_mode = mt76_connac_get_phy_mode(mphy, vif, band, &sta->deflink); + ra->channel = chandef->chan->hw_value; + ra->bw = sta->deflink.bandwidth; + ra->phy.bw = sta->deflink.bandwidth; + ra->mmps_mode = mt7915_mcu_get_mmps_mode(sta->deflink.smps_mode); + + if (supp_rate) { + supp_rate &= mask->control[band].legacy; + ra->rate_len = hweight32(supp_rate); + + if (band == NL80211_BAND_2GHZ) { + ra->supp_mode = MODE_CCK; + ra->supp_cck_rate = supp_rate & GENMASK(3, 0); + + if (ra->rate_len > 4) { + ra->supp_mode |= MODE_OFDM; + ra->supp_ofdm_rate = supp_rate >> 4; + } + } else { + ra->supp_mode = MODE_OFDM; + ra->supp_ofdm_rate = supp_rate; + } + } + + if (sta->deflink.ht_cap.ht_supported) { + ra->supp_mode |= MODE_HT; + ra->af = sta->deflink.ht_cap.ampdu_factor; + ra->ht_gf = !!(sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD); + + cap |= STA_CAP_HT; + if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_SGI_20) + cap |= STA_CAP_SGI_20; + if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_SGI_40) + cap |= STA_CAP_SGI_40; + if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_TX_STBC) + cap |= STA_CAP_TX_STBC; + if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_RX_STBC) + cap |= STA_CAP_RX_STBC; + if (mvif->cap.ht_ldpc && + (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_LDPC_CODING)) + cap |= STA_CAP_LDPC; + + mt7915_mcu_set_sta_ht_mcs(sta, ra->ht_mcs, + mask->control[band].ht_mcs); + ra->supp_ht_mcs = *(__le32 *)ra->ht_mcs; + } + + if (sta->deflink.vht_cap.vht_supported) { + u8 af; + + ra->supp_mode |= MODE_VHT; + af = FIELD_GET(IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK, + sta->deflink.vht_cap.cap); + ra->af = max_t(u8, ra->af, af); + + cap |= STA_CAP_VHT; + if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_SHORT_GI_80) + cap |= STA_CAP_VHT_SGI_80; + if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_SHORT_GI_160) + cap |= STA_CAP_VHT_SGI_160; + if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_TXSTBC) + cap |= STA_CAP_VHT_TX_STBC; + if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_RXSTBC_MASK) + cap |= STA_CAP_VHT_RX_STBC; + if (mvif->cap.vht_ldpc && + (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_RXLDPC)) + cap |= STA_CAP_VHT_LDPC; + + mt7915_mcu_set_sta_vht_mcs(sta, ra->supp_vht_mcs, + mask->control[band].vht_mcs); + } + + if (sta->deflink.he_cap.has_he) { + ra->supp_mode |= MODE_HE; + cap |= STA_CAP_HE; + + if (sta->deflink.he_6ghz_capa.capa) + ra->af = le16_get_bits(sta->deflink.he_6ghz_capa.capa, + IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP); + } + + ra->sta_cap = cpu_to_le32(cap); +} + +int mt7915_mcu_add_rate_ctrl(struct mt7915_dev *dev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, bool changed) +{ + struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; + struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv; + struct sk_buff *skb; + int ret; + + skb = mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76, + &msta->wcid); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + /* firmware rc algorithm refers to sta_rec_he for HE control. + * once dev->rc_work changes the settings driver should also + * update sta_rec_he here. + */ + if (changed) + mt7915_mcu_sta_he_tlv(skb, sta, vif); + + /* sta_rec_ra accommodates BW, NSS and only MCS range format + * i.e 0-{7,8,9} for VHT. + */ + mt7915_mcu_sta_rate_ctrl_tlv(skb, dev, vif, sta); + + ret = mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_EXT_CMD(STA_REC_UPDATE), true); + if (ret) + return ret; + + /* sta_rec_ra_fixed accommodates single rate, (HE)GI and HE_LTE, + * and updates as peer fixed rate parameters, which overrides + * sta_rec_ra and firmware rate control algorithm. + */ + return mt7915_mcu_add_rate_ctrl_fixed(dev, vif, sta); +} + +static int +mt7915_mcu_add_group(struct mt7915_dev *dev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ +#define MT_STA_BSS_GROUP 1 + struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; + struct mt7915_sta *msta; + struct { + __le32 action; + u8 wlan_idx_lo; + u8 status; + u8 wlan_idx_hi; + u8 rsv0[5]; + __le32 val; + u8 rsv1[8]; + } __packed req = { + .action = cpu_to_le32(MT_STA_BSS_GROUP), + .val = cpu_to_le32(mvif->mt76.idx % 16), + }; + + msta = sta ? (struct mt7915_sta *)sta->drv_priv : &mvif->sta; + req.wlan_idx_lo = to_wcid_lo(msta->wcid.idx); + req.wlan_idx_hi = to_wcid_hi(msta->wcid.idx); + + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(SET_DRR_CTRL), &req, + sizeof(req), true); +} + +int mt7915_mcu_add_sta(struct mt7915_dev *dev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, int conn_state, bool newly) +{ + struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; + struct ieee80211_link_sta *link_sta; + struct mt7915_sta *msta; + struct sk_buff *skb; + int ret; + + msta = sta ? (struct mt7915_sta *)sta->drv_priv : &mvif->sta; + link_sta = sta ? &sta->deflink : NULL; + + skb = mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76, + &msta->wcid); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + /* starec basic */ + mt76_connac_mcu_sta_basic_tlv(&dev->mt76, skb, &vif->bss_conf, link_sta, + conn_state, newly); + /* tag order is in accordance with firmware dependency. */ + if (sta && conn_state != CONN_STATE_DISCONNECT) { + /* starec bfer */ + mt7915_mcu_sta_bfer_tlv(dev, skb, vif, sta); + /* starec ht */ + mt7915_mcu_sta_ht_tlv(skb, sta); + /* starec vht */ + mt7915_mcu_sta_vht_tlv(skb, sta); + /* starec uapsd */ + mt76_connac_mcu_sta_uapsd(skb, vif, sta); + } + + if (newly || conn_state != CONN_STATE_DISCONNECT) { + ret = mt7915_mcu_sta_wtbl_tlv(dev, skb, vif, sta); + if (ret) { + dev_kfree_skb(skb); + return ret; + } + } + + if (conn_state == CONN_STATE_DISCONNECT) + goto out; + + if (sta) { + /* starec amsdu */ + mt7915_mcu_sta_amsdu_tlv(dev, skb, vif, sta); + /* starec he */ + mt7915_mcu_sta_he_tlv(skb, sta, vif); + /* starec muru */ + mt7915_mcu_sta_muru_tlv(dev, skb, sta, vif); + /* starec bfee */ + mt7915_mcu_sta_bfee_tlv(dev, skb, vif, sta); + } + + ret = mt7915_mcu_add_group(dev, vif, sta); + if (ret) { + dev_kfree_skb(skb); + return ret; + } +out: + ret = mt76_connac_mcu_sta_wed_update(&dev->mt76, skb); + if (ret) { + dev_kfree_skb(skb); + return ret; + } + + return mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_EXT_CMD(STA_REC_UPDATE), true); +} + +int mt7915_mcu_wed_enable_rx_stats(struct mt7915_dev *dev) +{ +#ifdef CONFIG_NET_MEDIATEK_SOC_WED + struct mtk_wed_device *wed = &dev->mt76.mmio.wed; + struct { + __le32 args[2]; + } req = { + .args[0] = cpu_to_le32(1), + .args[1] = cpu_to_le32(6), + }; + + return mtk_wed_device_update_msg(wed, MTK_WED_WO_CMD_RXCNT_CTRL, + &req, sizeof(req)); +#else + return 0; +#endif +} + +int mt7915_mcu_add_dev_info(struct mt7915_phy *phy, + struct ieee80211_vif *vif, bool enable) +{ + struct mt7915_dev *dev = phy->dev; + struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; + struct { + struct req_hdr { + u8 omac_idx; + u8 band_idx; + __le16 tlv_num; + u8 is_tlv_append; + u8 rsv[3]; + } __packed hdr; + struct req_tlv { + __le16 tag; + __le16 len; + u8 active; + u8 band_idx; + u8 omac_addr[ETH_ALEN]; + } __packed tlv; + } data = { + .hdr = { + .omac_idx = mvif->mt76.omac_idx, + .band_idx = mvif->mt76.band_idx, + .tlv_num = cpu_to_le16(1), + .is_tlv_append = 1, + }, + .tlv = { + .tag = cpu_to_le16(DEV_INFO_ACTIVE), + .len = cpu_to_le16(sizeof(struct req_tlv)), + .active = enable, + .band_idx = mvif->mt76.band_idx, + }, + }; + + if (mvif->mt76.omac_idx >= REPEATER_BSSID_START) + return mt7915_mcu_muar_config(phy, vif, false, enable); + + memcpy(data.tlv.omac_addr, vif->addr, ETH_ALEN); + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(DEV_INFO_UPDATE), + &data, sizeof(data), true); +} + +static void +mt7915_mcu_beacon_cntdwn(struct ieee80211_vif *vif, struct sk_buff *rskb, + struct sk_buff *skb, struct bss_info_bcn *bcn, + struct ieee80211_mutable_offsets *offs) +{ + struct bss_info_bcn_cntdwn *info; + struct tlv *tlv; + int sub_tag; + + if (!offs->cntdwn_counter_offs[0]) + return; + + sub_tag = vif->bss_conf.csa_active ? BSS_INFO_BCN_CSA : BSS_INFO_BCN_BCC; + tlv = mt7915_mcu_add_nested_subtlv(rskb, sub_tag, sizeof(*info), + &bcn->sub_ntlv, &bcn->len); + info = (struct bss_info_bcn_cntdwn *)tlv; + info->cnt = skb->data[offs->cntdwn_counter_offs[0]]; +} + +static void +mt7915_mcu_beacon_mbss(struct sk_buff *rskb, struct sk_buff *skb, + struct ieee80211_vif *vif, struct bss_info_bcn *bcn, + struct ieee80211_mutable_offsets *offs) +{ + struct bss_info_bcn_mbss *mbss; + const struct element *elem; + struct tlv *tlv; + + if (!vif->bss_conf.bssid_indicator) + return; + + tlv = mt7915_mcu_add_nested_subtlv(rskb, BSS_INFO_BCN_MBSSID, + sizeof(*mbss), &bcn->sub_ntlv, + &bcn->len); + + mbss = (struct bss_info_bcn_mbss *)tlv; + mbss->offset[0] = cpu_to_le16(offs->tim_offset); + mbss->bitmap = cpu_to_le32(1); + + for_each_element_id(elem, WLAN_EID_MULTIPLE_BSSID, + &skb->data[offs->mbssid_off], + skb->len - offs->mbssid_off) { + const struct element *sub_elem; + + if (elem->datalen < 2) + continue; + + for_each_element(sub_elem, elem->data + 1, elem->datalen - 1) { + const struct ieee80211_bssid_index *idx; + const u8 *idx_ie; + + if (sub_elem->id || sub_elem->datalen < 4) + continue; /* not a valid BSS profile */ + + /* Find WLAN_EID_MULTI_BSSID_IDX + * in the merged nontransmitted profile + */ + idx_ie = cfg80211_find_ie(WLAN_EID_MULTI_BSSID_IDX, + sub_elem->data, + sub_elem->datalen); + if (!idx_ie || idx_ie[1] < sizeof(*idx)) + continue; + + idx = (void *)(idx_ie + 2); + if (!idx->bssid_index || idx->bssid_index > 31) + continue; + + mbss->offset[idx->bssid_index] = + cpu_to_le16(idx_ie - skb->data); + mbss->bitmap |= cpu_to_le32(BIT(idx->bssid_index)); + } + } +} + +static void +mt7915_mcu_beacon_cont(struct mt7915_dev *dev, struct ieee80211_vif *vif, + struct sk_buff *rskb, struct sk_buff *skb, + struct bss_info_bcn *bcn, + struct ieee80211_mutable_offsets *offs) +{ + struct mt76_wcid *wcid = &dev->mt76.global_wcid; + struct bss_info_bcn_cont *cont; + struct tlv *tlv; + u8 *buf; + int len = sizeof(*cont) + MT_TXD_SIZE + skb->len; + + len = (len & 0x3) ? ((len | 0x3) + 1) : len; + tlv = mt7915_mcu_add_nested_subtlv(rskb, BSS_INFO_BCN_CONTENT, + len, &bcn->sub_ntlv, &bcn->len); + + cont = (struct bss_info_bcn_cont *)tlv; + cont->pkt_len = cpu_to_le16(MT_TXD_SIZE + skb->len); + cont->tim_ofs = cpu_to_le16(offs->tim_offset); + + if (offs->cntdwn_counter_offs[0]) { + u16 offset = offs->cntdwn_counter_offs[0]; + + if (vif->bss_conf.csa_active) + cont->csa_ofs = cpu_to_le16(offset - 4); + if (vif->bss_conf.color_change_active) + cont->bcc_ofs = cpu_to_le16(offset - 3); + } + + buf = (u8 *)tlv + sizeof(*cont); + mt7915_mac_write_txwi(&dev->mt76, (__le32 *)buf, skb, wcid, 0, NULL, + 0, BSS_CHANGED_BEACON); + memcpy(buf + MT_TXD_SIZE, skb->data, skb->len); +} + +int +mt7915_mcu_add_inband_discov(struct mt7915_dev *dev, struct ieee80211_vif *vif, + u32 changed) +{ +#define OFFLOAD_TX_MODE_SU BIT(0) +#define OFFLOAD_TX_MODE_MU BIT(1) + struct ieee80211_hw *hw = mt76_hw(dev); + struct mt7915_phy *phy = mt7915_hw_phy(hw); + struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; + struct cfg80211_chan_def *chandef = &mvif->phy->mt76->chandef; + enum nl80211_band band = chandef->chan->band; + struct mt76_wcid *wcid = &dev->mt76.global_wcid; + struct bss_info_bcn *bcn; + struct bss_info_inband_discovery *discov; + struct ieee80211_tx_info *info; + struct sk_buff *rskb, *skb = NULL; + struct tlv *tlv, *sub_tlv; + bool ext_phy = phy != &dev->phy; + u8 *buf, interval; + int len; + + if (vif->bss_conf.nontransmitted) + return 0; + + rskb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76, NULL, + MT7915_MAX_BSS_OFFLOAD_SIZE); + if (IS_ERR(rskb)) + return PTR_ERR(rskb); + + tlv = mt76_connac_mcu_add_tlv(rskb, BSS_INFO_OFFLOAD, sizeof(*bcn)); + bcn = (struct bss_info_bcn *)tlv; + bcn->enable = true; + + if (changed & BSS_CHANGED_FILS_DISCOVERY) { + interval = vif->bss_conf.fils_discovery.max_interval; + skb = ieee80211_get_fils_discovery_tmpl(hw, vif, 0); + } else if (changed & BSS_CHANGED_UNSOL_BCAST_PROBE_RESP && + vif->bss_conf.unsol_bcast_probe_resp_interval) { + interval = vif->bss_conf.unsol_bcast_probe_resp_interval; + skb = ieee80211_get_unsol_bcast_probe_resp_tmpl(hw, vif, 0); + } + + if (!skb) { + dev_kfree_skb(rskb); + return -EINVAL; + } + + info = IEEE80211_SKB_CB(skb); + info->control.vif = vif; + info->band = band; + info->hw_queue |= FIELD_PREP(MT_TX_HW_QUEUE_PHY, ext_phy); + + len = sizeof(*discov) + MT_TXD_SIZE + skb->len; + len = (len & 0x3) ? ((len | 0x3) + 1) : len; + + if (skb->len > MT7915_MAX_BEACON_SIZE) { + dev_err(dev->mt76.dev, "inband discovery size limit exceed\n"); + dev_kfree_skb(rskb); + dev_kfree_skb(skb); + return -EINVAL; + } + + sub_tlv = mt7915_mcu_add_nested_subtlv(rskb, BSS_INFO_BCN_DISCOV, + len, &bcn->sub_ntlv, &bcn->len); + discov = (struct bss_info_inband_discovery *)sub_tlv; + discov->tx_mode = OFFLOAD_TX_MODE_SU; + /* 0: UNSOL PROBE RESP, 1: FILS DISCOV */ + discov->tx_type = !!(changed & BSS_CHANGED_FILS_DISCOVERY); + discov->tx_interval = interval; + discov->prob_rsp_len = cpu_to_le16(MT_TXD_SIZE + skb->len); + discov->enable = !!interval; + + buf = (u8 *)sub_tlv + sizeof(*discov); + + mt7915_mac_write_txwi(&dev->mt76, (__le32 *)buf, skb, wcid, 0, NULL, + 0, changed); + memcpy(buf + MT_TXD_SIZE, skb->data, skb->len); + + dev_kfree_skb(skb); + + return mt76_mcu_skb_send_msg(&phy->dev->mt76, rskb, + MCU_EXT_CMD(BSS_INFO_UPDATE), true); +} + +int mt7915_mcu_add_beacon(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + int en, u32 changed) +{ + struct mt7915_dev *dev = mt7915_hw_dev(hw); + struct mt7915_phy *phy = mt7915_hw_phy(hw); + struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; + struct ieee80211_mutable_offsets offs; + struct ieee80211_tx_info *info; + struct sk_buff *skb, *rskb; + struct tlv *tlv; + struct bss_info_bcn *bcn; + int len = MT7915_MAX_BSS_OFFLOAD_SIZE; + bool ext_phy = phy != &dev->phy; + + if (vif->bss_conf.nontransmitted) + return 0; + + rskb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76, + NULL, len); + if (IS_ERR(rskb)) + return PTR_ERR(rskb); + + tlv = mt76_connac_mcu_add_tlv(rskb, BSS_INFO_OFFLOAD, sizeof(*bcn)); + bcn = (struct bss_info_bcn *)tlv; + bcn->enable = en; + + if (!en) + goto out; + + skb = ieee80211_beacon_get_template(hw, vif, &offs, 0); + if (!skb) { + dev_kfree_skb(rskb); + return -EINVAL; + } + + if (skb->len > MT7915_MAX_BEACON_SIZE) { + dev_err(dev->mt76.dev, "Bcn size limit exceed\n"); + dev_kfree_skb(rskb); + dev_kfree_skb(skb); + return -EINVAL; + } + + info = IEEE80211_SKB_CB(skb); + info->hw_queue = FIELD_PREP(MT_TX_HW_QUEUE_PHY, ext_phy); + + mt7915_mcu_beacon_cntdwn(vif, rskb, skb, bcn, &offs); + mt7915_mcu_beacon_mbss(rskb, skb, vif, bcn, &offs); + mt7915_mcu_beacon_cont(dev, vif, rskb, skb, bcn, &offs); + dev_kfree_skb(skb); + +out: + return mt76_mcu_skb_send_msg(&phy->dev->mt76, rskb, + MCU_EXT_CMD(BSS_INFO_UPDATE), true); +} + +static int mt7915_driver_own(struct mt7915_dev *dev, u8 band) +{ + mt76_wr(dev, MT_TOP_LPCR_HOST_BAND(band), MT_TOP_LPCR_HOST_DRV_OWN); + if (!mt76_poll_msec(dev, MT_TOP_LPCR_HOST_BAND(band), + MT_TOP_LPCR_HOST_FW_OWN_STAT, 0, 500)) { + dev_err(dev->mt76.dev, "Timeout for driver own\n"); + return -EIO; + } + + /* clear irq when the driver own success */ + mt76_wr(dev, MT_TOP_LPCR_HOST_BAND_IRQ_STAT(band), + MT_TOP_LPCR_HOST_BAND_STAT); + + return 0; +} + +static int +mt7915_firmware_state(struct mt7915_dev *dev, bool wa) +{ + u32 state = FIELD_PREP(MT_TOP_MISC_FW_STATE, + wa ? FW_STATE_RDY : FW_STATE_FW_DOWNLOAD); + + if (!mt76_poll_msec(dev, MT_TOP_MISC, MT_TOP_MISC_FW_STATE, + state, 1000)) { + dev_err(dev->mt76.dev, "Timeout for initializing firmware\n"); + return -EIO; + } + return 0; +} + +static int mt7915_load_firmware(struct mt7915_dev *dev) +{ + int ret; + + /* Release Semaphore if taken by previous failed attempt */ + ret = mt76_connac_mcu_patch_sem_ctrl(&dev->mt76, false); + if (ret != PATCH_REL_SEM_SUCCESS) { + dev_err(dev->mt76.dev, "Could not release semaphore\n"); + /* Continue anyways */ + } + + /* Always restart MCU firmware */ + mt76_connac_mcu_restart(&dev->mt76); + + /* Check if MCU is ready */ + ret = mt7915_firmware_state(dev, false); + if (ret) { + dev_err(dev->mt76.dev, "Firmware did not enter download state\n"); + return ret; + } + + ret = mt76_connac2_load_patch(&dev->mt76, fw_name_var(dev, ROM_PATCH)); + if (ret) + return ret; + + ret = mt76_connac2_load_ram(&dev->mt76, fw_name_var(dev, FIRMWARE_WM), + fw_name(dev, FIRMWARE_WA)); + if (ret) + return ret; + + ret = mt7915_firmware_state(dev, true); + if (ret) + return ret; + + mt76_queue_tx_cleanup(dev, dev->mt76.q_mcu[MT_MCUQ_FWDL], false); + + dev_dbg(dev->mt76.dev, "Firmware init done\n"); + + return 0; +} + +int mt7915_mcu_fw_log_2_host(struct mt7915_dev *dev, u8 type, u8 ctrl) +{ + struct { + u8 ctrl_val; + u8 pad[3]; + } data = { + .ctrl_val = ctrl + }; + + if (type == MCU_FW_LOG_WA) + return mt76_mcu_send_msg(&dev->mt76, MCU_WA_EXT_CMD(FW_LOG_2_HOST), + &data, sizeof(data), true); + + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(FW_LOG_2_HOST), &data, + sizeof(data), true); +} + +int mt7915_mcu_fw_dbg_ctrl(struct mt7915_dev *dev, u32 module, u8 level) +{ + struct { + u8 ver; + u8 pad; + __le16 len; + u8 level; + u8 rsv[3]; + __le32 module_idx; + } data = { + .module_idx = cpu_to_le32(module), + .level = level, + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(FW_DBG_CTRL), &data, + sizeof(data), false); +} + +int mt7915_mcu_muru_debug_set(struct mt7915_dev *dev, bool enabled) +{ + struct { + __le32 cmd; + u8 enable; + } data = { + .cmd = cpu_to_le32(MURU_SET_TXC_TX_STATS_EN), + .enable = enabled, + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(MURU_CTRL), &data, + sizeof(data), false); +} + +int mt7915_mcu_muru_debug_get(struct mt7915_phy *phy) +{ + struct mt7915_dev *dev = phy->dev; + struct sk_buff *skb; + struct mt7915_mcu_muru_stats *mu_stats; + int ret; + + struct { + __le32 cmd; + u8 band_idx; + } req = { + .cmd = cpu_to_le32(MURU_GET_TXC_TX_STATS), + .band_idx = phy->mt76->band_idx, + }; + + ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_EXT_CMD(MURU_CTRL), + &req, sizeof(req), true, &skb); + if (ret) + return ret; + + mu_stats = (struct mt7915_mcu_muru_stats *)(skb->data); + + /* accumulate stats, these are clear-on-read */ +#define __dl_u32(s) phy->mib.dl_##s += le32_to_cpu(mu_stats->dl.s) +#define __ul_u32(s) phy->mib.ul_##s += le32_to_cpu(mu_stats->ul.s) + __dl_u32(cck_cnt); + __dl_u32(ofdm_cnt); + __dl_u32(htmix_cnt); + __dl_u32(htgf_cnt); + __dl_u32(vht_su_cnt); + __dl_u32(vht_2mu_cnt); + __dl_u32(vht_3mu_cnt); + __dl_u32(vht_4mu_cnt); + __dl_u32(he_su_cnt); + __dl_u32(he_2ru_cnt); + __dl_u32(he_2mu_cnt); + __dl_u32(he_3ru_cnt); + __dl_u32(he_3mu_cnt); + __dl_u32(he_4ru_cnt); + __dl_u32(he_4mu_cnt); + __dl_u32(he_5to8ru_cnt); + __dl_u32(he_9to16ru_cnt); + __dl_u32(he_gtr16ru_cnt); + + __ul_u32(hetrig_su_cnt); + __ul_u32(hetrig_2ru_cnt); + __ul_u32(hetrig_3ru_cnt); + __ul_u32(hetrig_4ru_cnt); + __ul_u32(hetrig_5to8ru_cnt); + __ul_u32(hetrig_9to16ru_cnt); + __ul_u32(hetrig_gtr16ru_cnt); + __ul_u32(hetrig_2mu_cnt); + __ul_u32(hetrig_3mu_cnt); + __ul_u32(hetrig_4mu_cnt); +#undef __dl_u32 +#undef __ul_u32 + + dev_kfree_skb(skb); + + return 0; +} + +static int mt7915_mcu_set_mwds(struct mt7915_dev *dev, bool enabled) +{ + struct { + u8 enable; + u8 _rsv[3]; + } __packed req = { + .enable = enabled + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_WA_EXT_CMD(MWDS_SUPPORT), &req, + sizeof(req), false); +} + +int mt7915_mcu_set_muru_ctrl(struct mt7915_dev *dev, u32 cmd, u32 val) +{ + struct { + __le32 cmd; + u8 val[4]; + } __packed req = { + .cmd = cpu_to_le32(cmd), + }; + + put_unaligned_le32(val, req.val); + + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(MURU_CTRL), &req, + sizeof(req), false); +} + +static int +mt7915_mcu_init_rx_airtime(struct mt7915_dev *dev) +{ +#define RX_AIRTIME_FEATURE_CTRL 1 +#define RX_AIRTIME_BITWISE_CTRL 2 +#define RX_AIRTIME_CLEAR_EN 1 + struct { + __le16 field; + __le16 sub_field; + __le32 set_status; + __le32 get_status; + u8 _rsv[12]; + + bool airtime_en; + bool mibtime_en; + bool earlyend_en; + u8 _rsv1[9]; + + bool airtime_clear; + bool mibtime_clear; + u8 _rsv2[98]; + } __packed req = { + .field = cpu_to_le16(RX_AIRTIME_BITWISE_CTRL), + .sub_field = cpu_to_le16(RX_AIRTIME_CLEAR_EN), + .airtime_clear = true, + }; + int ret; + + ret = mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(RX_AIRTIME_CTRL), &req, + sizeof(req), true); + if (ret) + return ret; + + req.field = cpu_to_le16(RX_AIRTIME_FEATURE_CTRL); + req.sub_field = cpu_to_le16(RX_AIRTIME_CLEAR_EN); + req.airtime_en = true; + + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(RX_AIRTIME_CTRL), &req, + sizeof(req), true); +} + +static int mt7915_red_set_watermark(struct mt7915_dev *dev) +{ +#define RED_GLOBAL_TOKEN_WATERMARK 2 + struct { + __le32 args[3]; + u8 cmd; + u8 version; + u8 __rsv1[4]; + __le16 len; + __le16 high_mark; + __le16 low_mark; + u8 __rsv2[12]; + } __packed req = { + .args[0] = cpu_to_le32(MCU_WA_PARAM_RED_SETTING), + .cmd = RED_GLOBAL_TOKEN_WATERMARK, + .len = cpu_to_le16(sizeof(req) - sizeof(req.args)), + .high_mark = cpu_to_le16(MT7915_HW_TOKEN_SIZE - 256), + .low_mark = cpu_to_le16(MT7915_HW_TOKEN_SIZE - 256 - 1536), + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_WA_PARAM_CMD(SET), &req, + sizeof(req), false); +} + +static int mt7915_mcu_set_red(struct mt7915_dev *dev, bool enabled) +{ +#define RED_DISABLE 0 +#define RED_BY_WA_ENABLE 2 + int ret; + u32 red_type = enabled ? RED_BY_WA_ENABLE : RED_DISABLE; + __le32 req = cpu_to_le32(red_type); + + if (enabled) { + ret = mt7915_red_set_watermark(dev); + if (ret < 0) + return ret; + } + + ret = mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(RED_ENABLE), &req, + sizeof(req), false); + if (ret < 0) + return ret; + + return mt7915_mcu_wa_cmd(dev, MCU_WA_PARAM_CMD(SET), + MCU_WA_PARAM_RED, enabled, 0); +} + +int mt7915_mcu_init_firmware(struct mt7915_dev *dev) +{ + int ret; + + /* force firmware operation mode into normal state, + * which should be set before firmware download stage. + */ + mt76_wr(dev, MT_SWDEF_MODE, MT_SWDEF_NORMAL_MODE); + + ret = mt7915_driver_own(dev, 0); + if (ret) + return ret; + /* set driver own for band1 when two hif exist */ + if (dev->hif2) { + ret = mt7915_driver_own(dev, 1); + if (ret) + return ret; + } + + ret = mt7915_load_firmware(dev); + if (ret) + return ret; + + set_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state); + ret = mt7915_mcu_fw_log_2_host(dev, MCU_FW_LOG_WM, 0); + if (ret) + return ret; + + ret = mt7915_mcu_fw_log_2_host(dev, MCU_FW_LOG_WA, 0); + if (ret) + return ret; + + mt76_connac_mcu_del_wtbl_all(&dev->mt76); + + if ((mtk_wed_device_active(&dev->mt76.mmio.wed) && + is_mt7915(&dev->mt76)) || + !mtk_wed_get_rx_capa(&dev->mt76.mmio.wed)) + mt7915_mcu_wa_cmd(dev, MCU_WA_PARAM_CMD(CAPABILITY), 0, 0, 0); + + ret = mt7915_mcu_set_mwds(dev, 1); + if (ret) + return ret; + + ret = mt7915_mcu_set_muru_ctrl(dev, MURU_SET_PLATFORM_TYPE, + MURU_PLATFORM_TYPE_PERF_LEVEL_2); + if (ret) + return ret; + + ret = mt7915_mcu_init_rx_airtime(dev); + if (ret) + return ret; + + return mt7915_mcu_set_red(dev, mtk_wed_device_active(&dev->mt76.mmio.wed)); +} + +int mt7915_mcu_init(struct mt7915_dev *dev) +{ + static const struct mt76_mcu_ops mt7915_mcu_ops = { + .max_retry = 1, + .headroom = sizeof(struct mt76_connac2_mcu_txd), + .mcu_skb_prepare_msg = mt76_connac2_mcu_fill_message, + .mcu_skb_send_msg = mt7915_mcu_send_message, + .mcu_parse_response = mt7915_mcu_parse_response, + }; + + dev->mt76.mcu_ops = &mt7915_mcu_ops; + + return mt7915_mcu_init_firmware(dev); +} + +void mt7915_mcu_exit(struct mt7915_dev *dev) +{ + mt76_connac_mcu_restart(&dev->mt76); + if (mt7915_firmware_state(dev, false)) { + dev_err(dev->mt76.dev, "Failed to exit mcu\n"); + goto out; + } + + mt76_wr(dev, MT_TOP_LPCR_HOST_BAND(0), MT_TOP_LPCR_HOST_FW_OWN); + if (dev->hif2) + mt76_wr(dev, MT_TOP_LPCR_HOST_BAND(1), + MT_TOP_LPCR_HOST_FW_OWN); +out: + skb_queue_purge(&dev->mt76.mcu.res_q); +} + +static int +mt7915_mcu_set_rx_hdr_trans_blacklist(struct mt7915_dev *dev, int band) +{ + struct { + u8 operation; + u8 count; + u8 _rsv[2]; + u8 index; + u8 enable; + __le16 etype; + } req = { + .operation = 1, + .count = 1, + .enable = 1, + .etype = cpu_to_le16(ETH_P_PAE), + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(RX_HDR_TRANS), + &req, sizeof(req), false); +} + +int mt7915_mcu_set_mac(struct mt7915_dev *dev, int band, + bool enable, bool hdr_trans) +{ + struct { + u8 operation; + u8 enable; + u8 check_bssid; + u8 insert_vlan; + u8 remove_vlan; + u8 tid; + u8 mode; + u8 rsv; + } __packed req_trans = { + .enable = hdr_trans, + }; + struct { + u8 enable; + u8 band; + u8 rsv[2]; + } __packed req_mac = { + .enable = enable, + .band = band, + }; + int ret; + + ret = mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(RX_HDR_TRANS), + &req_trans, sizeof(req_trans), false); + if (ret) + return ret; + + if (hdr_trans) + mt7915_mcu_set_rx_hdr_trans_blacklist(dev, band); + + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(MAC_INIT_CTRL), + &req_mac, sizeof(req_mac), true); +} + +int mt7915_mcu_update_edca(struct mt7915_dev *dev, void *param) +{ + struct mt7915_mcu_tx *req = (struct mt7915_mcu_tx *)param; + u8 num = req->total; + size_t len = sizeof(*req) - + (IEEE80211_NUM_ACS - num) * sizeof(struct edca); + + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(EDCA_UPDATE), req, + len, true); +} + +int mt7915_mcu_set_tx(struct mt7915_dev *dev, struct ieee80211_vif *vif) +{ +#define TX_CMD_MODE 1 + struct mt7915_mcu_tx req = { + .valid = true, + .mode = TX_CMD_MODE, + .total = IEEE80211_NUM_ACS, + }; + struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; + int ac; + + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { + struct ieee80211_tx_queue_params *q = &mvif->queue_params[ac]; + struct edca *e = &req.edca[ac]; + + e->set = WMM_PARAM_SET; + e->queue = ac + mvif->mt76.wmm_idx * MT76_CONNAC_MAX_WMM_SETS; + e->aifs = q->aifs; + e->txop = cpu_to_le16(q->txop); + + if (q->cw_min) + e->cw_min = fls(q->cw_min); + else + e->cw_min = 5; + + if (q->cw_max) + e->cw_max = cpu_to_le16(fls(q->cw_max)); + else + e->cw_max = cpu_to_le16(10); + } + + return mt7915_mcu_update_edca(dev, &req); +} + +int mt7915_mcu_set_fcc5_lpn(struct mt7915_dev *dev, int val) +{ + struct { + __le32 tag; + __le16 min_lpn; + u8 rsv[2]; + } __packed req = { + .tag = cpu_to_le32(0x1), + .min_lpn = cpu_to_le16(val), + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(SET_RDD_TH), &req, + sizeof(req), true); +} + +int mt7915_mcu_set_pulse_th(struct mt7915_dev *dev, + const struct mt7915_dfs_pulse *pulse) +{ + struct { + __le32 tag; + + __le32 max_width; /* us */ + __le32 max_pwr; /* dbm */ + __le32 min_pwr; /* dbm */ + __le32 min_stgr_pri; /* us */ + __le32 max_stgr_pri; /* us */ + __le32 min_cr_pri; /* us */ + __le32 max_cr_pri; /* us */ + } __packed req = { + .tag = cpu_to_le32(0x3), + +#define __req_field(field) .field = cpu_to_le32(pulse->field) + __req_field(max_width), + __req_field(max_pwr), + __req_field(min_pwr), + __req_field(min_stgr_pri), + __req_field(max_stgr_pri), + __req_field(min_cr_pri), + __req_field(max_cr_pri), +#undef __req_field + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(SET_RDD_TH), &req, + sizeof(req), true); +} + +int mt7915_mcu_set_radar_th(struct mt7915_dev *dev, int index, + const struct mt7915_dfs_pattern *pattern) +{ + struct { + __le32 tag; + __le16 radar_type; + + u8 enb; + u8 stgr; + u8 min_crpn; + u8 max_crpn; + u8 min_crpr; + u8 min_pw; + __le32 min_pri; + __le32 max_pri; + u8 max_pw; + u8 min_crbn; + u8 max_crbn; + u8 min_stgpn; + u8 max_stgpn; + u8 min_stgpr; + u8 rsv[2]; + __le32 min_stgpr_diff; + } __packed req = { + .tag = cpu_to_le32(0x2), + .radar_type = cpu_to_le16(index), + +#define __req_field_u8(field) .field = pattern->field +#define __req_field_u32(field) .field = cpu_to_le32(pattern->field) + __req_field_u8(enb), + __req_field_u8(stgr), + __req_field_u8(min_crpn), + __req_field_u8(max_crpn), + __req_field_u8(min_crpr), + __req_field_u8(min_pw), + __req_field_u32(min_pri), + __req_field_u32(max_pri), + __req_field_u8(max_pw), + __req_field_u8(min_crbn), + __req_field_u8(max_crbn), + __req_field_u8(min_stgpn), + __req_field_u8(max_stgpn), + __req_field_u8(min_stgpr), + __req_field_u32(min_stgpr_diff), +#undef __req_field_u8 +#undef __req_field_u32 + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(SET_RDD_TH), &req, + sizeof(req), true); +} + +static int +mt7915_mcu_background_chain_ctrl(struct mt7915_phy *phy, + struct cfg80211_chan_def *chandef, + int cmd) +{ + struct mt7915_dev *dev = phy->dev; + struct mt76_phy *mphy = phy->mt76; + struct ieee80211_channel *chan = mphy->chandef.chan; + int freq = mphy->chandef.center_freq1; + struct mt7915_mcu_background_chain_ctrl req = { + .monitor_scan_type = 2, /* simple rx */ + }; + + if (!chandef && cmd != CH_SWITCH_BACKGROUND_SCAN_STOP) + return -EINVAL; + + if (!cfg80211_chandef_valid(&mphy->chandef)) + return -EINVAL; + + switch (cmd) { + case CH_SWITCH_BACKGROUND_SCAN_START: { + req.chan = chan->hw_value; + req.central_chan = ieee80211_frequency_to_channel(freq); + req.bw = mt76_connac_chan_bw(&mphy->chandef); + req.monitor_chan = chandef->chan->hw_value; + req.monitor_central_chan = + ieee80211_frequency_to_channel(chandef->center_freq1); + req.monitor_bw = mt76_connac_chan_bw(chandef); + req.band_idx = phy->mt76->band_idx; + req.scan_mode = 1; + break; + } + case CH_SWITCH_BACKGROUND_SCAN_RUNNING: + req.monitor_chan = chandef->chan->hw_value; + req.monitor_central_chan = + ieee80211_frequency_to_channel(chandef->center_freq1); + req.band_idx = phy->mt76->band_idx; + req.scan_mode = 2; + break; + case CH_SWITCH_BACKGROUND_SCAN_STOP: + req.chan = chan->hw_value; + req.central_chan = ieee80211_frequency_to_channel(freq); + req.bw = mt76_connac_chan_bw(&mphy->chandef); + req.tx_stream = hweight8(mphy->antenna_mask); + req.rx_stream = mphy->antenna_mask; + break; + default: + return -EINVAL; + } + req.band = chandef ? chandef->chan->band == NL80211_BAND_5GHZ : 1; + + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(OFFCH_SCAN_CTRL), + &req, sizeof(req), false); +} + +int mt7915_mcu_rdd_background_enable(struct mt7915_phy *phy, + struct cfg80211_chan_def *chandef) +{ + struct mt7915_dev *dev = phy->dev; + int err, region, rdd_idx; + + rdd_idx = mt7915_get_rdd_idx(phy, true); + if (rdd_idx < 0) + return -EINVAL; + + if (!chandef) { /* disable offchain */ + err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_STOP, rdd_idx, 0, 0); + if (err) + return err; + + return mt7915_mcu_background_chain_ctrl(phy, NULL, + CH_SWITCH_BACKGROUND_SCAN_STOP); + } + + err = mt7915_mcu_background_chain_ctrl(phy, chandef, + CH_SWITCH_BACKGROUND_SCAN_START); + if (err) + return err; + + switch (dev->mt76.region) { + case NL80211_DFS_ETSI: + region = 0; + break; + case NL80211_DFS_JP: + region = 2; + break; + case NL80211_DFS_FCC: + default: + region = 1; + break; + } + + return mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_START, rdd_idx, 0, region); +} + +int mt7915_mcu_set_chan_info(struct mt7915_phy *phy, int cmd) +{ + static const u8 ch_band[] = { + [NL80211_BAND_2GHZ] = 0, + [NL80211_BAND_5GHZ] = 1, + [NL80211_BAND_6GHZ] = 2, + }; + struct mt7915_dev *dev = phy->dev; + struct cfg80211_chan_def *chandef = &phy->mt76->chandef; + int freq1 = chandef->center_freq1; + u8 band = phy->mt76->band_idx; + struct { + u8 control_ch; + u8 center_ch; + u8 bw; + u8 tx_path_num; + u8 rx_path; /* mask or num */ + u8 switch_reason; + u8 band_idx; + u8 center_ch2; /* for 80+80 only */ + __le16 cac_case; + u8 channel_band; + u8 rsv0; + __le32 outband_freq; + u8 txpower_drop; + u8 ap_bw; + u8 ap_center_ch; + u8 rsv1[57]; + } __packed req = { + .control_ch = chandef->chan->hw_value, + .center_ch = ieee80211_frequency_to_channel(freq1), + .bw = mt76_connac_chan_bw(chandef), + .tx_path_num = hweight16(phy->mt76->chainmask), + .rx_path = mt7915_band_chainmask(phy), + .band_idx = band, + .channel_band = ch_band[chandef->chan->band], + }; + +#ifdef CONFIG_NL80211_TESTMODE + if (phy->mt76->test.tx_antenna_mask && + mt76_testmode_enabled(phy->mt76)) { + req.tx_path_num = fls(phy->mt76->test.tx_antenna_mask); + req.rx_path = phy->mt76->test.tx_antenna_mask; + } +#endif + + if (mt76_connac_spe_idx(phy->mt76->antenna_mask)) + req.tx_path_num = fls(phy->mt76->antenna_mask); + + if (phy->mt76->hw->conf.flags & IEEE80211_CONF_MONITOR) + req.switch_reason = CH_SWITCH_NORMAL; + else if (phy->mt76->offchannel || + phy->mt76->hw->conf.flags & IEEE80211_CONF_IDLE) + req.switch_reason = CH_SWITCH_SCAN_BYPASS_DPD; + else if (!cfg80211_reg_can_beacon(phy->mt76->hw->wiphy, chandef, + NL80211_IFTYPE_AP)) + req.switch_reason = CH_SWITCH_DFS; + else + req.switch_reason = CH_SWITCH_NORMAL; + + if (cmd == MCU_EXT_CMD(CHANNEL_SWITCH)) + req.rx_path = hweight8(req.rx_path); + + if (chandef->width == NL80211_CHAN_WIDTH_80P80) { + int freq2 = chandef->center_freq2; + + req.center_ch2 = ieee80211_frequency_to_channel(freq2); + } + + return mt76_mcu_send_msg(&dev->mt76, cmd, &req, sizeof(req), true); +} + +static int mt7915_mcu_set_eeprom_flash(struct mt7915_dev *dev) +{ +#define MAX_PAGE_IDX_MASK GENMASK(7, 5) +#define PAGE_IDX_MASK GENMASK(4, 2) +#define PER_PAGE_SIZE 0x400 + struct mt7915_mcu_eeprom req = { .buffer_mode = EE_MODE_BUFFER }; + u16 eeprom_size = mt7915_eeprom_size(dev); + u8 total = DIV_ROUND_UP(eeprom_size, PER_PAGE_SIZE); + u8 *eep = (u8 *)dev->mt76.eeprom.data; + int eep_len; + int i; + + for (i = 0; i < total; i++, eep += eep_len) { + struct sk_buff *skb; + int ret; + + if (i == total - 1 && !!(eeprom_size % PER_PAGE_SIZE)) + eep_len = eeprom_size % PER_PAGE_SIZE; + else + eep_len = PER_PAGE_SIZE; + + skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, + sizeof(req) + eep_len); + if (!skb) + return -ENOMEM; + + req.format = FIELD_PREP(MAX_PAGE_IDX_MASK, total - 1) | + FIELD_PREP(PAGE_IDX_MASK, i) | EE_FORMAT_WHOLE; + req.len = cpu_to_le16(eep_len); + + skb_put_data(skb, &req, sizeof(req)); + skb_put_data(skb, eep, eep_len); + + ret = mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_EXT_CMD(EFUSE_BUFFER_MODE), true); + if (ret) + return ret; + } + + return 0; +} + +int mt7915_mcu_set_eeprom(struct mt7915_dev *dev) +{ + struct mt7915_mcu_eeprom req = { + .buffer_mode = EE_MODE_EFUSE, + .format = EE_FORMAT_WHOLE, + }; + + if (dev->flash_mode) + return mt7915_mcu_set_eeprom_flash(dev); + + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(EFUSE_BUFFER_MODE), + &req, sizeof(req), true); +} + +int mt7915_mcu_get_eeprom(struct mt7915_dev *dev, u32 offset, u8 *read_buf) +{ + struct mt7915_mcu_eeprom_info req = { + .addr = cpu_to_le32(round_down(offset, + MT7915_EEPROM_BLOCK_SIZE)), + }; + struct mt7915_mcu_eeprom_info *res; + struct sk_buff *skb; + u8 *buf = read_buf; + int ret; + + ret = mt76_mcu_send_and_get_msg(&dev->mt76, + MCU_EXT_QUERY(EFUSE_ACCESS), + &req, sizeof(req), true, &skb); + if (ret) + return ret; + + res = (struct mt7915_mcu_eeprom_info *)skb->data; + if (!buf) { + u32 addr = le32_to_cpu(res->addr); + + if (addr > dev->mt76.eeprom.size - MT7915_EEPROM_BLOCK_SIZE) { + dev_kfree_skb(skb); + return -EINVAL; + } + buf = dev->mt76.eeprom.data + addr; + } + memcpy(buf, res->data, MT7915_EEPROM_BLOCK_SIZE); + + dev_kfree_skb(skb); + + return 0; +} + +int mt7915_mcu_get_eeprom_free_block(struct mt7915_dev *dev, u8 *block_num) +{ + struct { + u8 _rsv; + u8 version; + u8 die_idx; + u8 _rsv2; + } __packed req = { + .version = 1, + }; + struct sk_buff *skb; + int ret; + + ret = mt76_mcu_send_and_get_msg(&dev->mt76, + MCU_EXT_QUERY(EFUSE_FREE_BLOCK), + &req, sizeof(req), true, &skb); + if (ret) + return ret; + + *block_num = *(u8 *)skb->data; + dev_kfree_skb(skb); + + return 0; +} + +static int mt7915_mcu_set_pre_cal(struct mt7915_dev *dev, u8 idx, + u8 *data, u32 len, int cmd) +{ + struct { + u8 dir; + u8 valid; + __le16 bitmap; + s8 precal; + u8 action; + u8 band; + u8 idx; + u8 rsv[4]; + __le32 len; + } req = {}; + struct sk_buff *skb; + + skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, sizeof(req) + len); + if (!skb) + return -ENOMEM; + + req.idx = idx; + req.len = cpu_to_le32(len); + skb_put_data(skb, &req, sizeof(req)); + skb_put_data(skb, data, len); + + return mt76_mcu_skb_send_msg(&dev->mt76, skb, cmd, false); +} + +int mt7915_mcu_apply_group_cal(struct mt7915_dev *dev) +{ + u8 idx = 0, *cal = dev->cal, *eep = dev->mt76.eeprom.data; + u32 total = mt7915_get_cal_group_size(dev); + u32 offs = is_mt7915(&dev->mt76) ? MT_EE_DO_PRE_CAL : MT_EE_DO_PRE_CAL_V2; + + if (!(eep[offs] & MT_EE_WIFI_CAL_GROUP)) + return 0; + + /* + * Items: Rx DCOC, RSSI DCOC, Tx TSSI DCOC, Tx LPFG + * Tx FDIQ, Tx DCIQ, Rx FDIQ, Rx FIIQ, ADCDCOC + */ + while (total > 0) { + int ret, len; + + len = min_t(u32, total, MT_EE_CAL_UNIT); + + ret = mt7915_mcu_set_pre_cal(dev, idx, cal, len, + MCU_EXT_CMD(GROUP_PRE_CAL_INFO)); + if (ret) + return ret; + + total -= len; + cal += len; + idx++; + } + + return 0; +} + +static int mt7915_find_freq_idx(const u16 *freqs, int n_freqs, u16 cur) +{ + int i; + + for (i = 0; i < n_freqs; i++) + if (cur == freqs[i]) + return i; + + return -1; +} + +static int mt7915_dpd_freq_idx(struct mt7915_dev *dev, u16 freq, u8 bw) +{ + static const u16 freq_list_v1[] = { + 5180, 5200, 5220, 5240, + 5260, 5280, 5300, 5320, + 5500, 5520, 5540, 5560, + 5580, 5600, 5620, 5640, + 5660, 5680, 5700, 5745, + 5765, 5785, 5805, 5825 + }; + static const u16 freq_list_v2[] = { + /* 6G BW20*/ + 5955, 5975, 5995, 6015, + 6035, 6055, 6075, 6095, + 6115, 6135, 6155, 6175, + 6195, 6215, 6235, 6255, + 6275, 6295, 6315, 6335, + 6355, 6375, 6395, 6415, + 6435, 6455, 6475, 6495, + 6515, 6535, 6555, 6575, + 6595, 6615, 6635, 6655, + 6675, 6695, 6715, 6735, + 6755, 6775, 6795, 6815, + 6835, 6855, 6875, 6895, + 6915, 6935, 6955, 6975, + 6995, 7015, 7035, 7055, + 7075, 7095, 7115, + /* 6G BW160 */ + 6025, 6185, 6345, 6505, + 6665, 6825, 6985, + /* 5G BW20 */ + 5180, 5200, 5220, 5240, + 5260, 5280, 5300, 5320, + 5500, 5520, 5540, 5560, + 5580, 5600, 5620, 5640, + 5660, 5680, 5700, 5720, + 5745, 5765, 5785, 5805, + 5825, 5845, 5865, 5885, + /* 5G BW160 */ + 5250, 5570, 5815 + }; + const u16 *freq_list; + int idx, n_freqs; + + if (!is_mt7915(&dev->mt76)) { + freq_list = freq_list_v2; + n_freqs = ARRAY_SIZE(freq_list_v2); + } else { + freq_list = freq_list_v1; + n_freqs = ARRAY_SIZE(freq_list_v1); + } + + if (freq < 4000) { + if (freq < 2432) + return n_freqs; + if (freq < 2457) + return n_freqs + 1; + + return n_freqs + 2; + } + + if (bw == NL80211_CHAN_WIDTH_80P80) + return -1; + + if (bw != NL80211_CHAN_WIDTH_20) { + idx = mt7915_find_freq_idx(freq_list, n_freqs, freq + 10); + if (idx >= 0) + return idx; + + idx = mt7915_find_freq_idx(freq_list, n_freqs, freq - 10); + if (idx >= 0) + return idx; + } + + return mt7915_find_freq_idx(freq_list, n_freqs, freq); +} + +int mt7915_mcu_apply_tx_dpd(struct mt7915_phy *phy) +{ + struct mt7915_dev *dev = phy->dev; + struct cfg80211_chan_def *chandef = &phy->mt76->chandef; + enum nl80211_band band = chandef->chan->band; + u32 offs = is_mt7915(&dev->mt76) ? MT_EE_DO_PRE_CAL : MT_EE_DO_PRE_CAL_V2; + u16 center_freq = chandef->center_freq1; + u8 *cal = dev->cal, *eep = dev->mt76.eeprom.data; + u8 dpd_mask, cal_num = is_mt7915(&dev->mt76) ? 2 : 3; + int idx; + + switch (band) { + case NL80211_BAND_2GHZ: + dpd_mask = MT_EE_WIFI_CAL_DPD_2G; + break; + case NL80211_BAND_5GHZ: + dpd_mask = MT_EE_WIFI_CAL_DPD_5G; + break; + case NL80211_BAND_6GHZ: + dpd_mask = MT_EE_WIFI_CAL_DPD_6G; + break; + default: + dpd_mask = 0; + break; + } + + if (!(eep[offs] & dpd_mask)) + return 0; + + idx = mt7915_dpd_freq_idx(dev, center_freq, chandef->width); + if (idx < 0) + return -EINVAL; + + /* Items: Tx DPD, Tx Flatness */ + idx = idx * cal_num; + cal += mt7915_get_cal_group_size(dev) + (idx * MT_EE_CAL_UNIT); + + while (cal_num--) { + int ret; + + ret = mt7915_mcu_set_pre_cal(dev, idx, cal, MT_EE_CAL_UNIT, + MCU_EXT_CMD(DPD_PRE_CAL_INFO)); + if (ret) + return ret; + + idx++; + cal += MT_EE_CAL_UNIT; + } + + return 0; +} + +int mt7915_mcu_get_chan_mib_info(struct mt7915_phy *phy, bool chan_switch) +{ + struct mt76_channel_state *state = phy->mt76->chan_state; + struct mt76_channel_state *state_ts = &phy->state_ts; + struct mt7915_dev *dev = phy->dev; + struct mt7915_mcu_mib *res, req[5]; + struct sk_buff *skb; + static const u32 *offs; + int i, ret, len, offs_cc; + u64 cc_tx; + + /* strict order */ + if (is_mt7915(&dev->mt76)) { + static const u32 chip_offs[] = { + MIB_NON_WIFI_TIME, + MIB_TX_TIME, + MIB_RX_TIME, + MIB_OBSS_AIRTIME, + MIB_TXOP_INIT_COUNT, + }; + len = ARRAY_SIZE(chip_offs); + offs = chip_offs; + offs_cc = 20; + } else { + static const u32 chip_offs[] = { + MIB_NON_WIFI_TIME_V2, + MIB_TX_TIME_V2, + MIB_RX_TIME_V2, + MIB_OBSS_AIRTIME_V2 + }; + len = ARRAY_SIZE(chip_offs); + offs = chip_offs; + offs_cc = 0; + } + + for (i = 0; i < len; i++) { + req[i].band = cpu_to_le32(phy->mt76->band_idx); + req[i].offs = cpu_to_le32(offs[i]); + } + + ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_EXT_CMD(GET_MIB_INFO), + req, len * sizeof(req[0]), true, &skb); + if (ret) + return ret; + + res = (struct mt7915_mcu_mib *)(skb->data + offs_cc); + +#define __res_u64(s) le64_to_cpu(res[s].data) + /* subtract Tx backoff time from Tx duration for MT7915 */ + if (is_mt7915(&dev->mt76)) { + u64 backoff = (__res_u64(4) & 0xffff) * 79; /* 16us + 9us * 7 */ + cc_tx = __res_u64(1) - backoff; + } else { + cc_tx = __res_u64(1); + } + + if (chan_switch) + goto out; + + state->cc_tx += cc_tx - state_ts->cc_tx; + state->cc_bss_rx += __res_u64(2) - state_ts->cc_bss_rx; + state->cc_rx += __res_u64(2) + __res_u64(3) - state_ts->cc_rx; + state->cc_busy += __res_u64(0) + cc_tx + __res_u64(2) + __res_u64(3) - + state_ts->cc_busy; + +out: + state_ts->cc_tx = cc_tx; + state_ts->cc_bss_rx = __res_u64(2); + state_ts->cc_rx = __res_u64(2) + __res_u64(3); + state_ts->cc_busy = __res_u64(0) + cc_tx + __res_u64(2) + __res_u64(3); +#undef __res_u64 + + dev_kfree_skb(skb); + + return 0; +} + +int mt7915_mcu_get_temperature(struct mt7915_phy *phy) +{ + struct mt7915_dev *dev = phy->dev; + struct { + u8 ctrl_id; + u8 action; + u8 band_idx; + u8 rsv[5]; + } req = { + .ctrl_id = THERMAL_SENSOR_TEMP_QUERY, + .band_idx = phy->mt76->band_idx, + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(THERMAL_CTRL), &req, + sizeof(req), true); +} + +int mt7915_mcu_set_thermal_throttling(struct mt7915_phy *phy, u8 state) +{ + struct mt7915_dev *dev = phy->dev; + struct mt7915_mcu_thermal_ctrl req = { + .band_idx = phy->mt76->band_idx, + .ctrl_id = THERMAL_PROTECT_DUTY_CONFIG, + }; + int level, ret; + + /* set duty cycle and level */ + for (level = 0; level < 4; level++) { + req.duty.duty_level = level; + req.duty.duty_cycle = state; + state /= 2; + + ret = mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(THERMAL_PROT), + &req, sizeof(req), false); + if (ret) + return ret; + } + return 0; +} + +int mt7915_mcu_set_thermal_protect(struct mt7915_phy *phy) +{ + struct mt7915_dev *dev = phy->dev; + struct { + struct mt7915_mcu_thermal_ctrl ctrl; + + __le32 trigger_temp; + __le32 restore_temp; + __le16 sustain_time; + u8 rsv[2]; + } __packed req = { + .ctrl = { + .band_idx = phy->mt76->band_idx, + .type.protect_type = 1, + .type.trigger_type = 1, + }, + }; + int ret; + + req.ctrl.ctrl_id = THERMAL_PROTECT_DISABLE; + ret = mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(THERMAL_PROT), + &req, sizeof(req.ctrl), false); + + if (ret) + return ret; + + /* set high-temperature trigger threshold */ + req.ctrl.ctrl_id = THERMAL_PROTECT_ENABLE; + /* add a safety margin ~10 */ + req.restore_temp = cpu_to_le32(phy->throttle_temp[0] - 10); + req.trigger_temp = cpu_to_le32(phy->throttle_temp[1]); + req.sustain_time = cpu_to_le16(10); + + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(THERMAL_PROT), + &req, sizeof(req), false); +} + +int mt7915_mcu_set_txpower_frame_min(struct mt7915_phy *phy, s8 txpower) +{ + struct mt7915_dev *dev = phy->dev; + struct { + u8 format_id; + u8 rsv; + u8 band_idx; + s8 txpower_min; + } __packed req = { + .format_id = TX_POWER_LIMIT_FRAME_MIN, + .band_idx = phy->mt76->band_idx, + .txpower_min = txpower * 2, /* 0.5db */ + }; + + return mt76_mcu_send_msg(&dev->mt76, + MCU_EXT_CMD(TX_POWER_FEATURE_CTRL), &req, + sizeof(req), true); +} + +int mt7915_mcu_set_txpower_frame(struct mt7915_phy *phy, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, s8 txpower) +{ + struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv; + struct mt7915_dev *dev = phy->dev; + struct mt76_phy *mphy = phy->mt76; + struct { + u8 format_id; + u8 rsv[3]; + u8 band_idx; + s8 txpower_max; + __le16 wcid; + s8 txpower_offs[48]; + } __packed req = { + .format_id = TX_POWER_LIMIT_FRAME, + .band_idx = phy->mt76->band_idx, + .txpower_max = DIV_ROUND_UP(mphy->txpower_cur, 2), + .wcid = cpu_to_le16(msta->wcid.idx), + }; + int ret; + s8 txpower_sku[MT7915_SKU_RATE_NUM]; + + ret = mt7915_mcu_get_txpower_sku(phy, txpower_sku, sizeof(txpower_sku), + TX_POWER_INFO_RATE); + if (ret) + return ret; + + txpower = mt76_get_power_bound(mphy, txpower); + if (txpower > mphy->txpower_cur || txpower < 0) + return -EINVAL; + + if (txpower) { + u32 offs, len, i; + + if (sta->deflink.ht_cap.ht_supported) { + const u8 *sku_len = mt7915_sku_group_len; + + offs = sku_len[SKU_CCK] + sku_len[SKU_OFDM]; + len = sku_len[SKU_HT_BW20] + sku_len[SKU_HT_BW40]; + + if (sta->deflink.vht_cap.vht_supported) { + offs += len; + len = sku_len[SKU_VHT_BW20] * 4; + + if (sta->deflink.he_cap.has_he) { + offs += len + sku_len[SKU_HE_RU26] * 3; + len = sku_len[SKU_HE_RU242] * 4; + } + } + } else { + return -EINVAL; + } + + for (i = 0; i < len; i++, offs++) + req.txpower_offs[i] = + DIV_ROUND_UP(txpower - txpower_sku[offs], 2); + } + + return mt76_mcu_send_msg(&dev->mt76, + MCU_EXT_CMD(TX_POWER_FEATURE_CTRL), &req, + sizeof(req), true); +} + +static void +mt7915_update_txpower(struct mt7915_phy *phy, int tx_power) +{ + struct mt76_phy *mphy = phy->mt76; + struct ieee80211_channel *chan = mphy->main_chandef.chan; + int chain_idx, val, e2p_power_limit = 0; + + if (!chan) { + mphy->txpower_cur = tx_power; + return; + } + + for (chain_idx = 0; chain_idx < hweight16(mphy->chainmask); chain_idx++) { + val = mt7915_eeprom_get_target_power(phy->dev, chan, chain_idx); + val += mt7915_eeprom_get_power_delta(phy->dev, chan->band); + + e2p_power_limit = max_t(int, e2p_power_limit, val); + } + + if (phy->sku_limit_en) + mphy->txpower_cur = min_t(int, e2p_power_limit, tx_power); + else + mphy->txpower_cur = e2p_power_limit; +} + +int mt7915_mcu_set_txpower_sku(struct mt7915_phy *phy) +{ +#define TX_POWER_LIMIT_TABLE_RATE 0 +#define TX_POWER_LIMIT_TABLE_PATH 1 + struct mt7915_dev *dev = phy->dev; + struct mt76_phy *mphy = phy->mt76; + struct ieee80211_hw *hw = mphy->hw; + struct mt7915_sku_val { + u8 format_id; + u8 limit_type; + u8 band_idx; + } __packed hdr = { + .format_id = TX_POWER_LIMIT_TABLE, + .limit_type = TX_POWER_LIMIT_TABLE_RATE, + .band_idx = phy->mt76->band_idx, + }; + int i, ret, tx_power; + const u8 *len = mt7915_sku_group_len; + struct mt76_power_limits la = {}; + struct sk_buff *skb; + + tx_power = mt76_get_power_bound(mphy, hw->conf.power_level); + if (phy->sku_limit_en) { + tx_power = mt76_get_rate_power_limits(mphy, mphy->chandef.chan, + &la, tx_power); + mt7915_update_txpower(phy, tx_power); + } else { + mt7915_update_txpower(phy, tx_power); + return 0; + } + + skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, + sizeof(hdr) + MT7915_SKU_RATE_NUM); + if (!skb) + return -ENOMEM; + + skb_put_data(skb, &hdr, sizeof(hdr)); + skb_put_data(skb, &la.cck, len[SKU_CCK] + len[SKU_OFDM]); + skb_put_data(skb, &la.mcs[0], len[SKU_HT_BW20]); + skb_put_data(skb, &la.mcs[1], len[SKU_HT_BW40]); + + /* vht */ + for (i = 0; i < 4; i++) { + skb_put_data(skb, &la.mcs[i], sizeof(la.mcs[i])); + skb_put_zero(skb, 2); /* padding */ + } + + /* he */ + skb_put_data(skb, &la.ru[0], sizeof(la.ru)); + ret = mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_EXT_CMD(TX_POWER_FEATURE_CTRL), true); + if (ret) + return ret; + + /* only set per-path power table when it's configured */ + if (!phy->sku_path_en) + return 0; + + skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, + sizeof(hdr) + MT7915_SKU_PATH_NUM); + if (!skb) + return -ENOMEM; + + hdr.limit_type = TX_POWER_LIMIT_TABLE_PATH; + skb_put_data(skb, &hdr, sizeof(hdr)); + skb_put_data(skb, &la.path.cck, sizeof(la.path.cck)); + skb_put_data(skb, &la.path.ofdm, sizeof(la.path.ofdm)); + skb_put_data(skb, &la.path.ofdm_bf[1], sizeof(la.path.ofdm_bf) - 1); + + /* HT20 and HT40 */ + skb_put_data(skb, &la.path.ru[3], sizeof(la.path.ru[3])); + skb_put_data(skb, &la.path.ru_bf[3][1], sizeof(la.path.ru_bf[3]) - 1); + skb_put_data(skb, &la.path.ru[4], sizeof(la.path.ru[4])); + skb_put_data(skb, &la.path.ru_bf[4][1], sizeof(la.path.ru_bf[4]) - 1); + + /* start from non-bf and bf fields of + * BW20/RU242, BW40/RU484, BW80/RU996, BW160/RU2x996, + * RU26, RU52, and RU106 + */ + + for (i = 0; i < 8; i++) { + bool bf = i % 2; + u8 idx = (i + 6) / 2; + s8 *buf = bf ? la.path.ru_bf[idx] : la.path.ru[idx]; + /* The non-bf fields of RU26 to RU106 are special cases */ + if (bf) + skb_put_data(skb, buf + 1, 9); + else + skb_put_data(skb, buf, 10); + } + + for (i = 0; i < 6; i++) { + bool bf = i % 2; + u8 idx = i / 2; + s8 *buf = bf ? la.path.ru_bf[idx] : la.path.ru[idx]; + + skb_put_data(skb, buf, 10); + } + + return mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_EXT_CMD(TX_POWER_FEATURE_CTRL), true); +} + +int mt7915_mcu_get_txpower_sku(struct mt7915_phy *phy, s8 *txpower, int len, + u8 category) +{ +#define RATE_POWER_INFO 2 + struct mt7915_dev *dev = phy->dev; + struct { + u8 format_id; + u8 category; + u8 band_idx; + u8 _rsv; + } __packed req = { + .format_id = TX_POWER_LIMIT_INFO, + .category = category, + .band_idx = phy->mt76->band_idx, + }; + struct sk_buff *skb; + int ret, i; + + ret = mt76_mcu_send_and_get_msg(&dev->mt76, + MCU_EXT_CMD(TX_POWER_FEATURE_CTRL), + &req, sizeof(req), true, &skb); + if (ret) + return ret; + + if (category == TX_POWER_INFO_RATE) { + s8 res[MT7915_SKU_RATE_NUM][2]; + + if (skb->len < sizeof(res) + 4) { + dev_kfree_skb(skb); + return -EINVAL; + } + memcpy(res, skb->data + 4, sizeof(res)); + for (i = 0; i < len; i++) + txpower[i] = res[i][req.band_idx]; + } else if (category == TX_POWER_INFO_PATH) { + if (skb->len < len + 4) { + dev_kfree_skb(skb); + return -EINVAL; + } + memcpy(txpower, skb->data + 4, len); + } + + dev_kfree_skb(skb); + + return 0; +} + +int mt7915_mcu_set_test_param(struct mt7915_dev *dev, u8 param, bool test_mode, + u8 en) +{ + struct { + u8 test_mode_en; + u8 param_idx; + u8 _rsv[2]; + + u8 enable; + u8 _rsv2[3]; + + u8 pad[8]; + } __packed req = { + .test_mode_en = test_mode, + .param_idx = param, + .enable = en, + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(ATE_CTRL), &req, + sizeof(req), false); +} + +int mt7915_mcu_set_sku_en(struct mt7915_phy *phy) +{ + struct mt7915_dev *dev = phy->dev; + struct mt7915_sku { + u8 format_id; + u8 sku_enable; + u8 band_idx; + u8 rsv; + } __packed req = { + .band_idx = phy->mt76->band_idx, + }; + int ret; + + req.sku_enable = phy->sku_limit_en; + req.format_id = TX_POWER_LIMIT_ENABLE; + + ret = mt76_mcu_send_msg(&dev->mt76, + MCU_EXT_CMD(TX_POWER_FEATURE_CTRL), &req, + sizeof(req), true); + if (ret) + return ret; + + req.sku_enable = phy->sku_path_en; + req.format_id = TX_POWER_LIMIT_PATH_ENABLE; + + return mt76_mcu_send_msg(&dev->mt76, + MCU_EXT_CMD(TX_POWER_FEATURE_CTRL), &req, + sizeof(req), true); +} + +int mt7915_mcu_set_ser(struct mt7915_dev *dev, u8 action, u8 set, u8 band) +{ + struct { + u8 action; + u8 set; + u8 band; + u8 rsv; + } req = { + .action = action, + .set = set, + .band = band, + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(SET_SER_TRIGGER), + &req, sizeof(req), false); +} + +int mt7915_mcu_set_txbf(struct mt7915_dev *dev, u8 action) +{ + struct { + u8 action; + union { + struct { + u8 snd_mode; + u8 sta_num; + u8 rsv; + u8 wlan_idx[4]; + __le32 snd_period; /* ms */ + } __packed snd; + struct { + bool ebf; + bool ibf; + u8 rsv; + } __packed type; + struct { + u8 bf_num; + u8 bf_bitmap; + u8 bf_sel[8]; + u8 rsv[5]; + } __packed mod; + }; + } __packed req = { + .action = action, + }; + +#define MT_BF_PROCESSING 4 + switch (action) { + case MT_BF_SOUNDING_ON: + req.snd.snd_mode = MT_BF_PROCESSING; + break; + case MT_BF_TYPE_UPDATE: + req.type.ebf = true; + req.type.ibf = dev->ibf; + break; + case MT_BF_MODULE_UPDATE: + req.mod.bf_num = 2; + req.mod.bf_bitmap = GENMASK(1, 0); + break; + default: + return -EINVAL; + } + + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(TXBF_ACTION), &req, + sizeof(req), true); +} + +static int +mt7915_mcu_enable_obss_spr(struct mt7915_phy *phy, u8 action, u8 val) +{ + struct mt7915_dev *dev = phy->dev; + struct mt7915_mcu_sr_ctrl req = { + .action = action, + .argnum = 1, + .band_idx = phy->mt76->band_idx, + .val = cpu_to_le32(val), + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(SET_SPR), &req, + sizeof(req), true); +} + +static int +mt7915_mcu_set_obss_spr_pd(struct mt7915_phy *phy, + struct ieee80211_he_obss_pd *he_obss_pd) +{ + struct mt7915_dev *dev = phy->dev; + struct { + struct mt7915_mcu_sr_ctrl ctrl; + struct { + u8 pd_th_non_srg; + u8 pd_th_srg; + u8 period_offs; + u8 rcpi_src; + __le16 obss_pd_min; + __le16 obss_pd_min_srg; + u8 resp_txpwr_mode; + u8 txpwr_restrict_mode; + u8 txpwr_ref; + u8 rsv[3]; + } __packed param; + } __packed req = { + .ctrl = { + .action = SPR_SET_PARAM, + .argnum = 9, + .band_idx = phy->mt76->band_idx, + }, + }; + int ret; + u8 max_th = 82, non_srg_max_th = 62; + + /* disable firmware dynamical PD asjustment */ + ret = mt7915_mcu_enable_obss_spr(phy, SPR_ENABLE_DPD, false); + if (ret) + return ret; + + if (he_obss_pd->sr_ctrl & + IEEE80211_HE_SPR_NON_SRG_OBSS_PD_SR_DISALLOWED) + req.param.pd_th_non_srg = max_th; + else if (he_obss_pd->sr_ctrl & IEEE80211_HE_SPR_NON_SRG_OFFSET_PRESENT) + req.param.pd_th_non_srg = max_th - he_obss_pd->non_srg_max_offset; + else + req.param.pd_th_non_srg = non_srg_max_th; + + if (he_obss_pd->sr_ctrl & IEEE80211_HE_SPR_SRG_INFORMATION_PRESENT) + req.param.pd_th_srg = max_th - he_obss_pd->max_offset; + + req.param.obss_pd_min = cpu_to_le16(82); + req.param.obss_pd_min_srg = cpu_to_le16(82); + req.param.txpwr_restrict_mode = 2; + req.param.txpwr_ref = 21; + + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(SET_SPR), &req, + sizeof(req), true); +} + +static int +mt7915_mcu_set_obss_spr_siga(struct mt7915_phy *phy, struct ieee80211_vif *vif, + struct ieee80211_he_obss_pd *he_obss_pd) +{ + struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; + struct mt7915_dev *dev = phy->dev; + u8 omac = mvif->mt76.omac_idx; + struct { + struct mt7915_mcu_sr_ctrl ctrl; + struct { + u8 omac; + u8 rsv[3]; + u8 flag[20]; + } __packed siga; + } __packed req = { + .ctrl = { + .action = SPR_SET_SIGA, + .argnum = 1, + .band_idx = phy->mt76->band_idx, + }, + .siga = { + .omac = omac > HW_BSSID_MAX ? omac - 12 : omac, + }, + }; + int ret; + + if (he_obss_pd->sr_ctrl & IEEE80211_HE_SPR_HESIGA_SR_VAL15_ALLOWED) + req.siga.flag[req.siga.omac] = 0xf; + else + return 0; + + /* switch to normal AP mode */ + ret = mt7915_mcu_enable_obss_spr(phy, SPR_ENABLE_MODE, 0); + if (ret) + return ret; + + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(SET_SPR), &req, + sizeof(req), true); +} + +static int +mt7915_mcu_set_obss_spr_bitmap(struct mt7915_phy *phy, + struct ieee80211_he_obss_pd *he_obss_pd) +{ + struct mt7915_dev *dev = phy->dev; + struct { + struct mt7915_mcu_sr_ctrl ctrl; + struct { + __le32 color_l[2]; + __le32 color_h[2]; + __le32 bssid_l[2]; + __le32 bssid_h[2]; + } __packed bitmap; + } __packed req = { + .ctrl = { + .action = SPR_SET_SRG_BITMAP, + .argnum = 4, + .band_idx = phy->mt76->band_idx, + }, + }; + u32 bitmap; + + memcpy(&bitmap, he_obss_pd->bss_color_bitmap, sizeof(bitmap)); + req.bitmap.color_l[req.ctrl.band_idx] = cpu_to_le32(bitmap); + + memcpy(&bitmap, he_obss_pd->bss_color_bitmap + 4, sizeof(bitmap)); + req.bitmap.color_h[req.ctrl.band_idx] = cpu_to_le32(bitmap); + + memcpy(&bitmap, he_obss_pd->partial_bssid_bitmap, sizeof(bitmap)); + req.bitmap.bssid_l[req.ctrl.band_idx] = cpu_to_le32(bitmap); + + memcpy(&bitmap, he_obss_pd->partial_bssid_bitmap + 4, sizeof(bitmap)); + req.bitmap.bssid_h[req.ctrl.band_idx] = cpu_to_le32(bitmap); + + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(SET_SPR), &req, + sizeof(req), true); +} + +int mt7915_mcu_add_obss_spr(struct mt7915_phy *phy, struct ieee80211_vif *vif, + struct ieee80211_he_obss_pd *he_obss_pd) +{ + int ret; + + /* enable firmware scene detection algorithms */ + ret = mt7915_mcu_enable_obss_spr(phy, SPR_ENABLE_SD, sr_scene_detect); + if (ret) + return ret; + + /* firmware dynamically adjusts PD threshold so skip manual control */ + if (sr_scene_detect && !he_obss_pd->enable) + return 0; + + /* enable spatial reuse */ + ret = mt7915_mcu_enable_obss_spr(phy, SPR_ENABLE, he_obss_pd->enable); + if (ret) + return ret; + + if (sr_scene_detect || !he_obss_pd->enable) + return 0; + + ret = mt7915_mcu_enable_obss_spr(phy, SPR_ENABLE_TX, true); + if (ret) + return ret; + + /* set SRG/non-SRG OBSS PD threshold */ + ret = mt7915_mcu_set_obss_spr_pd(phy, he_obss_pd); + if (ret) + return ret; + + /* Set SR prohibit */ + ret = mt7915_mcu_set_obss_spr_siga(phy, vif, he_obss_pd); + if (ret) + return ret; + + /* set SRG BSS color/BSSID bitmap */ + return mt7915_mcu_set_obss_spr_bitmap(phy, he_obss_pd); +} + +int mt7915_mcu_get_rx_rate(struct mt7915_phy *phy, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, struct rate_info *rate) +{ + struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; + struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv; + struct mt7915_dev *dev = phy->dev; + struct mt76_phy *mphy = phy->mt76; + struct { + u8 category; + u8 band; + __le16 wcid; + } __packed req = { + .category = MCU_PHY_STATE_CONTENTION_RX_RATE, + .band = mvif->mt76.band_idx, + .wcid = cpu_to_le16(msta->wcid.idx), + }; + struct ieee80211_supported_band *sband; + struct mt7915_mcu_phy_rx_info *res; + struct sk_buff *skb; + int ret; + bool cck = false; + + ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_EXT_CMD(PHY_STAT_INFO), + &req, sizeof(req), true, &skb); + if (ret) + return ret; + + res = (struct mt7915_mcu_phy_rx_info *)skb->data; + + rate->mcs = res->rate; + rate->nss = res->nsts + 1; + + switch (res->mode) { + case MT_PHY_TYPE_CCK: + cck = true; + fallthrough; + case MT_PHY_TYPE_OFDM: + if (mphy->chandef.chan->band == NL80211_BAND_5GHZ) + sband = &mphy->sband_5g.sband; + else if (mphy->chandef.chan->band == NL80211_BAND_6GHZ) + sband = &mphy->sband_6g.sband; + else + sband = &mphy->sband_2g.sband; + + rate->mcs = mt76_get_rate(&dev->mt76, sband, rate->mcs, cck); + rate->legacy = sband->bitrates[rate->mcs].bitrate; + break; + case MT_PHY_TYPE_HT: + case MT_PHY_TYPE_HT_GF: + if (rate->mcs > 31) { + ret = -EINVAL; + goto out; + } + + rate->flags = RATE_INFO_FLAGS_MCS; + if (res->gi) + rate->flags |= RATE_INFO_FLAGS_SHORT_GI; + break; + case MT_PHY_TYPE_VHT: + if (rate->mcs > 9) { + ret = -EINVAL; + goto out; + } + + rate->flags = RATE_INFO_FLAGS_VHT_MCS; + if (res->gi) + rate->flags |= RATE_INFO_FLAGS_SHORT_GI; + break; + case MT_PHY_TYPE_HE_SU: + case MT_PHY_TYPE_HE_EXT_SU: + case MT_PHY_TYPE_HE_TB: + case MT_PHY_TYPE_HE_MU: + if (res->gi > NL80211_RATE_INFO_HE_GI_3_2 || rate->mcs > 11) { + ret = -EINVAL; + goto out; + } + rate->he_gi = res->gi; + rate->flags = RATE_INFO_FLAGS_HE_MCS; + break; + default: + ret = -EINVAL; + goto out; + } + + switch (res->bw) { + case IEEE80211_STA_RX_BW_160: + rate->bw = RATE_INFO_BW_160; + break; + case IEEE80211_STA_RX_BW_80: + rate->bw = RATE_INFO_BW_80; + break; + case IEEE80211_STA_RX_BW_40: + rate->bw = RATE_INFO_BW_40; + break; + default: + rate->bw = RATE_INFO_BW_20; + break; + } + +out: + dev_kfree_skb(skb); + + return ret; +} + +int mt7915_mcu_set_protection(struct mt7915_phy *phy, struct ieee80211_vif *vif, + u8 ht_mode, bool use_cts_prot) +{ + struct mt7915_dev *dev = phy->dev; + int len = sizeof(struct sta_req_hdr) + sizeof(struct bss_info_prot); + struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; + struct bss_info_prot *prot; + struct sk_buff *skb; + struct tlv *tlv; + enum { + PROT_NONMEMBER = BIT(1), + PROT_20MHZ = BIT(2), + PROT_NONHT_MIXED = BIT(3), + PROT_LEGACY_ERP = BIT(5), + PROT_NONGF_STA = BIT(7), + }; + u32 rts_threshold; + + skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76, + NULL, len); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + tlv = mt76_connac_mcu_add_tlv(skb, BSS_INFO_PROTECT_INFO, + sizeof(*prot)); + prot = (struct bss_info_prot *)tlv; + + switch (ht_mode & IEEE80211_HT_OP_MODE_PROTECTION) { + case IEEE80211_HT_OP_MODE_PROTECTION_NONMEMBER: + prot->prot_mode = cpu_to_le32(PROT_NONMEMBER); + break; + case IEEE80211_HT_OP_MODE_PROTECTION_20MHZ: + prot->prot_mode = cpu_to_le32(PROT_20MHZ); + break; + case IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED: + prot->prot_mode = cpu_to_le32(PROT_NONHT_MIXED); + break; + } + + if (ht_mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT) + prot->prot_mode |= cpu_to_le32(PROT_NONGF_STA); + + if (use_cts_prot) + prot->prot_mode |= cpu_to_le32(PROT_LEGACY_ERP); + + /* reuse current RTS setting */ + rts_threshold = phy->mt76->hw->wiphy->rts_threshold; + if (rts_threshold == (u32)-1) + prot->rts_len_thres = cpu_to_le32(MT7915_RTS_LEN_THRES); + else + prot->rts_len_thres = cpu_to_le32(rts_threshold); + + prot->rts_pkt_thres = 0x2; + + prot->he_rts_thres = cpu_to_le16(vif->bss_conf.frame_time_rts_th); + if (!prot->he_rts_thres) + prot->he_rts_thres = cpu_to_le16(DEFAULT_HE_DURATION_RTS_THRES); + + return mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_EXT_CMD(BSS_INFO_UPDATE), true); +} + +int mt7915_mcu_update_bss_color(struct mt7915_dev *dev, struct ieee80211_vif *vif, + struct cfg80211_he_bss_color *he_bss_color) +{ + int len = sizeof(struct sta_req_hdr) + sizeof(struct bss_info_color); + struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv; + struct bss_info_color *bss_color; + struct sk_buff *skb; + struct tlv *tlv; + + skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76, + NULL, len); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + tlv = mt76_connac_mcu_add_tlv(skb, BSS_INFO_BSS_COLOR, + sizeof(*bss_color)); + bss_color = (struct bss_info_color *)tlv; + bss_color->disable = !he_bss_color->enabled; + bss_color->color = he_bss_color->color; + + return mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_EXT_CMD(BSS_INFO_UPDATE), true); +} + +#define TWT_AGRT_TRIGGER BIT(0) +#define TWT_AGRT_ANNOUNCE BIT(1) +#define TWT_AGRT_PROTECT BIT(2) + +int mt7915_mcu_twt_agrt_update(struct mt7915_dev *dev, + struct mt7915_vif *mvif, + struct mt7915_twt_flow *flow, + int cmd) +{ + struct { + u8 tbl_idx; + u8 cmd; + u8 own_mac_idx; + u8 flowid; /* 0xff for group id */ + __le16 peer_id; /* specify the peer_id (msb=0) + * or group_id (msb=1) + */ + u8 duration; /* 256 us */ + u8 bss_idx; + __le64 start_tsf; + __le16 mantissa; + u8 exponent; + u8 is_ap; + u8 agrt_params; + u8 rsv[23]; + } __packed req = { + .tbl_idx = flow->table_id, + .cmd = cmd, + .own_mac_idx = mvif->mt76.omac_idx, + .flowid = flow->id, + .peer_id = cpu_to_le16(flow->wcid), + .duration = flow->duration, + .bss_idx = mvif->mt76.idx, + .start_tsf = cpu_to_le64(flow->tsf), + .mantissa = flow->mantissa, + .exponent = flow->exp, + .is_ap = true, + }; + + if (flow->protection) + req.agrt_params |= TWT_AGRT_PROTECT; + if (!flow->flowtype) + req.agrt_params |= TWT_AGRT_ANNOUNCE; + if (flow->trigger) + req.agrt_params |= TWT_AGRT_TRIGGER; + + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(TWT_AGRT_UPDATE), + &req, sizeof(req), true); +} + +int mt7915_mcu_wed_wa_tx_stats(struct mt7915_dev *dev, u16 wlan_idx) +{ + struct { + __le32 cmd; + __le32 arg0; + __le32 arg1; + __le16 arg2; + } __packed req = { + .cmd = cpu_to_le32(0x15), + }; + struct mt7915_mcu_wa_tx_stat { + __le16 wcid; + u8 __rsv2[2]; + + /* tx_bytes is deprecated since WA byte counter uses u32, + * which easily leads to overflow. + */ + __le32 tx_bytes; + __le32 tx_packets; + } __packed *res; + struct mt76_wcid *wcid; + struct sk_buff *skb; + int ret, len; + u16 ret_wcid; + + if (is_mt7915(&dev->mt76)) { + req.arg0 = cpu_to_le32(wlan_idx); + len = sizeof(req) - sizeof(req.arg2); + } else { + req.arg0 = cpu_to_le32(1); + req.arg2 = cpu_to_le16(wlan_idx); + len = sizeof(req); + } + + ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_WA_PARAM_CMD(QUERY), + &req, len, true, &skb); + if (ret) + return ret; + + if (!is_mt7915(&dev->mt76)) + skb_pull(skb, 4); + + res = (struct mt7915_mcu_wa_tx_stat *)skb->data; + + ret_wcid = le16_to_cpu(res->wcid); + if (is_mt7915(&dev->mt76)) + ret_wcid &= 0xff; + + if (ret_wcid != wlan_idx) { + ret = -EINVAL; + goto out; + } + + rcu_read_lock(); + + wcid = mt76_wcid_ptr(dev, wlan_idx); + if (wcid) + wcid->stats.tx_packets += le32_to_cpu(res->tx_packets); + else + ret = -EINVAL; + + rcu_read_unlock(); +out: + dev_kfree_skb(skb); + + return ret; +} + +int mt7915_mcu_rf_regval(struct mt7915_dev *dev, u32 regidx, u32 *val, bool set) +{ + struct { + __le32 idx; + __le32 ofs; + __le32 data; + } __packed req = { + .idx = cpu_to_le32(u32_get_bits(regidx, GENMASK(31, 24))), + .ofs = cpu_to_le32(u32_get_bits(regidx, GENMASK(23, 0))), + .data = set ? cpu_to_le32(*val) : 0, + }; + struct sk_buff *skb; + int ret; + + if (set) + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(RF_REG_ACCESS), + &req, sizeof(req), false); + + ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_EXT_QUERY(RF_REG_ACCESS), + &req, sizeof(req), true, &skb); + if (ret) + return ret; + + *val = le32_to_cpu(*(__le32 *)(skb->data + 8)); + dev_kfree_skb(skb); + + return 0; +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h new file mode 100644 index 000000000..76e39e899 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h @@ -0,0 +1,544 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* Copyright (C) 2020 MediaTek Inc. */ + +#ifndef __MT7915_MCU_H +#define __MT7915_MCU_H + +#include "../mt76_connac_mcu.h" + +enum { + MCU_ATE_SET_TRX = 0x1, + MCU_ATE_SET_FREQ_OFFSET = 0xa, + MCU_ATE_SET_SLOT_TIME = 0x13, + MCU_ATE_CLEAN_TXQUEUE = 0x1c, +}; + +struct mt7915_mcu_thermal_ctrl { + u8 ctrl_id; + u8 band_idx; + union { + struct { + u8 protect_type; /* 1: duty admit, 2: radio off */ + u8 trigger_type; /* 0: low, 1: high */ + } __packed type; + struct { + u8 duty_level; /* level 0~3 */ + u8 duty_cycle; + } __packed duty; + }; +} __packed; + +struct mt7915_mcu_thermal_notify { + struct mt76_connac2_mcu_rxd_hdr rxd; + + struct mt7915_mcu_thermal_ctrl ctrl; + __le32 temperature; + u8 rsv[8]; +} __packed; + +struct mt7915_mcu_csa_notify { + struct mt76_connac2_mcu_rxd_hdr rxd; + + u8 omac_idx; + u8 csa_count; + u8 band_idx; + u8 rsv; +} __packed; + +struct mt7915_mcu_bcc_notify { + struct mt76_connac2_mcu_rxd_hdr rxd; + + u8 band_idx; + u8 omac_idx; + u8 cca_count; + u8 rsv; +} __packed; + +struct mt7915_mcu_ps_notify { + struct mt76_connac2_mcu_rxd_hdr rxd; + + u8 wtbl_lower; + u8 ps_bit; + u8 wtbl_higher; + u8 rsv; +} __packed; + +struct mt7915_mcu_rdd_report { + struct mt76_connac2_mcu_rxd_hdr rxd; + + u8 rdd_idx; + u8 long_detected; + u8 constant_prf_detected; + u8 staggered_prf_detected; + u8 radar_type_idx; + u8 periodic_pulse_num; + u8 long_pulse_num; + u8 hw_pulse_num; + + u8 out_lpn; + u8 out_spn; + u8 out_crpn; + u8 out_crpw; + u8 out_crbn; + u8 out_stgpn; + u8 out_stgpw; + + u8 rsv; + + __le32 out_pri_const; + __le32 out_pri_stg[3]; + + struct { + __le32 start; + __le16 pulse_width; + __le16 pulse_power; + u8 mdrdy_flag; + u8 rsv[3]; + } long_pulse[32]; + + struct { + __le32 start; + __le16 pulse_width; + __le16 pulse_power; + u8 mdrdy_flag; + u8 rsv[3]; + } periodic_pulse[32]; + + struct { + __le32 start; + __le16 pulse_width; + __le16 pulse_power; + u8 sc_pass; + u8 sw_reset; + u8 mdrdy_flag; + u8 tx_active; + } hw_pulse[32]; +} __packed; + +struct mt7915_mcu_background_chain_ctrl { + u8 chan; /* primary channel */ + u8 central_chan; /* central channel */ + u8 bw; + u8 tx_stream; + u8 rx_stream; + + u8 monitor_chan; /* monitor channel */ + u8 monitor_central_chan;/* monitor central channel */ + u8 monitor_bw; + u8 monitor_tx_stream; + u8 monitor_rx_stream; + + u8 scan_mode; /* 0: ScanStop + * 1: ScanStart + * 2: ScanRunning + */ + u8 band_idx; /* DBDC */ + u8 monitor_scan_type; + u8 band; /* 0: 2.4GHz, 1: 5GHz */ + u8 rsv[2]; +} __packed; + +struct mt7915_mcu_sr_ctrl { + u8 action; + u8 argnum; + u8 band_idx; + u8 status; + u8 drop_ta_idx; + u8 sta_idx; /* 256 sta */ + u8 rsv[2]; + __le32 val; +} __packed; + +struct mt7915_mcu_eeprom { + u8 buffer_mode; + u8 format; + __le16 len; +} __packed; + +struct mt7915_mcu_eeprom_info { + __le32 addr; + __le32 valid; + u8 data[16]; +} __packed; + +struct mt7915_mcu_phy_rx_info { + u8 category; + u8 rate; + u8 mode; + u8 nsts; + u8 gi; + u8 coding; + u8 stbc; + u8 bw; +}; + +struct mt7915_mcu_mib { + __le32 band; + __le32 offs; + __le64 data; +} __packed; + +enum mt7915_chan_mib_offs { + /* mt7915 */ + MIB_TX_TIME = 81, + MIB_RX_TIME, + MIB_OBSS_AIRTIME = 86, + MIB_NON_WIFI_TIME, + MIB_TXOP_INIT_COUNT, + + /* mt7916 */ + MIB_TX_TIME_V2 = 6, + MIB_RX_TIME_V2 = 8, + MIB_OBSS_AIRTIME_V2 = 490, + MIB_NON_WIFI_TIME_V2 +}; + +struct mt7915_mcu_txpower_sku { + u8 format_id; + u8 limit_type; + u8 band_idx; + s8 txpower_sku[MT7915_SKU_RATE_NUM]; +} __packed; + +struct edca { + u8 queue; + u8 set; + u8 aifs; + u8 cw_min; + __le16 cw_max; + __le16 txop; +}; + +struct mt7915_mcu_tx { + u8 total; + u8 action; + u8 valid; + u8 mode; + + struct edca edca[IEEE80211_NUM_ACS]; +} __packed; + +struct mt7915_mcu_muru_stats { + __le32 event_id; + struct { + __le32 cck_cnt; + __le32 ofdm_cnt; + __le32 htmix_cnt; + __le32 htgf_cnt; + __le32 vht_su_cnt; + __le32 vht_2mu_cnt; + __le32 vht_3mu_cnt; + __le32 vht_4mu_cnt; + __le32 he_su_cnt; + __le32 he_ext_su_cnt; + __le32 he_2ru_cnt; + __le32 he_2mu_cnt; + __le32 he_3ru_cnt; + __le32 he_3mu_cnt; + __le32 he_4ru_cnt; + __le32 he_4mu_cnt; + __le32 he_5to8ru_cnt; + __le32 he_9to16ru_cnt; + __le32 he_gtr16ru_cnt; + } dl; + + struct { + __le32 hetrig_su_cnt; + __le32 hetrig_2ru_cnt; + __le32 hetrig_3ru_cnt; + __le32 hetrig_4ru_cnt; + __le32 hetrig_5to8ru_cnt; + __le32 hetrig_9to16ru_cnt; + __le32 hetrig_gtr16ru_cnt; + __le32 hetrig_2mu_cnt; + __le32 hetrig_3mu_cnt; + __le32 hetrig_4mu_cnt; + } ul; +}; + +#define WMM_AIFS_SET BIT(0) +#define WMM_CW_MIN_SET BIT(1) +#define WMM_CW_MAX_SET BIT(2) +#define WMM_TXOP_SET BIT(3) +#define WMM_PARAM_SET GENMASK(3, 0) + +enum { + MCU_FW_LOG_WM, + MCU_FW_LOG_WA, + MCU_FW_LOG_TO_HOST, +}; + +enum { + MCU_TWT_AGRT_ADD, + MCU_TWT_AGRT_MODIFY, + MCU_TWT_AGRT_DELETE, + MCU_TWT_AGRT_TEARDOWN, + MCU_TWT_AGRT_GET_TSF, +}; + +enum { + MCU_WA_PARAM_CMD_QUERY, + MCU_WA_PARAM_CMD_SET, + MCU_WA_PARAM_CMD_CAPABILITY, + MCU_WA_PARAM_CMD_DEBUG, +}; + +enum { + MCU_WA_PARAM_PDMA_RX = 0x04, + MCU_WA_PARAM_CPU_UTIL = 0x0b, + MCU_WA_PARAM_RED = 0x0e, + MCU_WA_PARAM_RED_SETTING = 0x40, +}; + +enum mcu_mmps_mode { + MCU_MMPS_STATIC, + MCU_MMPS_DYNAMIC, + MCU_MMPS_RSV, + MCU_MMPS_DISABLE, +}; + +struct bss_info_bmc_rate { + __le16 tag; + __le16 len; + __le16 bc_trans; + __le16 mc_trans; + u8 short_preamble; + u8 rsv[7]; +} __packed; + +struct bss_info_ra { + __le16 tag; + __le16 len; + u8 op_mode; + u8 adhoc_en; + u8 short_preamble; + u8 tx_streams; + u8 rx_streams; + u8 algo; + u8 force_sgi; + u8 force_gf; + u8 ht_mode; + u8 has_20_sta; /* Check if any sta support GF. */ + u8 bss_width_trigger_events; + u8 vht_nss_cap; + u8 vht_bw_signal; /* not use */ + u8 vht_force_sgi; /* not use */ + u8 se_off; + u8 antenna_idx; + u8 train_up_rule; + u8 rsv[3]; + __le16 train_up_high_thres; + __le16 train_up_rule_rssi; + __le16 low_traffic_thres; + __le16 max_phyrate; + __le32 phy_cap; + __le32 interval; + __le32 fast_interval; +} __packed; + +struct bss_info_hw_amsdu { + __le16 tag; + __le16 len; + __le32 cmp_bitmap_0; + __le32 cmp_bitmap_1; + __le16 trig_thres; + u8 enable; + u8 rsv; +} __packed; + +struct bss_info_color { + __le16 tag; + __le16 len; + u8 disable; + u8 color; + u8 rsv[2]; +} __packed; + +struct bss_info_he { + __le16 tag; + __le16 len; + u8 he_pe_duration; + u8 vht_op_info_present; + __le16 he_rts_thres; + __le16 max_nss_mcs[CMD_HE_MCS_BW_NUM]; + u8 rsv[6]; +} __packed; + +struct bss_info_bcn { + __le16 tag; + __le16 len; + u8 ver; + u8 enable; + __le16 sub_ntlv; +} __packed __aligned(4); + +struct bss_info_bcn_cntdwn { + __le16 tag; + __le16 len; + u8 cnt; + u8 rsv[3]; +} __packed __aligned(4); + +struct bss_info_bcn_mbss { +#define MAX_BEACON_NUM 32 + __le16 tag; + __le16 len; + __le32 bitmap; + __le16 offset[MAX_BEACON_NUM]; + u8 rsv[8]; +} __packed __aligned(4); + +struct bss_info_bcn_cont { + __le16 tag; + __le16 len; + __le16 tim_ofs; + __le16 csa_ofs; + __le16 bcc_ofs; + __le16 pkt_len; +} __packed __aligned(4); + +struct bss_info_inband_discovery { + __le16 tag; + __le16 len; + u8 tx_type; + u8 tx_mode; + u8 tx_interval; + u8 enable; + __le16 rsv; + __le16 prob_rsp_len; +} __packed __aligned(4); + +struct bss_info_prot { + __le16 tag; + __le16 len; + __le32 prot_type; + __le32 prot_mode; + __le32 rts_len_thres; + __le16 he_rts_thres; + u8 rts_pkt_thres; + u8 rsv[5]; +} __packed; + +enum { + BSS_INFO_BCN_CSA, + BSS_INFO_BCN_BCC, + BSS_INFO_BCN_MBSSID, + BSS_INFO_BCN_CONTENT, + BSS_INFO_BCN_DISCOV, + BSS_INFO_BCN_MAX +}; + +enum { + RATE_PARAM_FIXED = 3, + RATE_PARAM_MMPS_UPDATE = 5, + RATE_PARAM_FIXED_HE_LTF = 7, + RATE_PARAM_FIXED_MCS, + RATE_PARAM_FIXED_GI = 11, + RATE_PARAM_AUTO = 20, + RATE_PARAM_SPE_UPDATE = 22, +}; + +#define RATE_CFG_MCS GENMASK(3, 0) +#define RATE_CFG_NSS GENMASK(7, 4) +#define RATE_CFG_GI GENMASK(11, 8) +#define RATE_CFG_BW GENMASK(15, 12) +#define RATE_CFG_STBC GENMASK(19, 16) +#define RATE_CFG_LDPC GENMASK(23, 20) +#define RATE_CFG_PHY_TYPE GENMASK(27, 24) +#define RATE_CFG_HE_LTF GENMASK(31, 28) + +enum { + TX_POWER_LIMIT_ENABLE, + TX_POWER_LIMIT_PATH_ENABLE = 0x3, + TX_POWER_LIMIT_TABLE = 0x4, + TX_POWER_LIMIT_INFO = 0x7, + TX_POWER_LIMIT_FRAME = 0x11, + TX_POWER_LIMIT_FRAME_MIN = 0x12, +}; + +enum { + TX_POWER_INFO_PATH = 1, + TX_POWER_INFO_RATE, +}; + +enum { + SPR_ENABLE = 0x1, + SPR_ENABLE_SD = 0x3, + SPR_ENABLE_MODE = 0x5, + SPR_ENABLE_DPD = 0x23, + SPR_ENABLE_TX = 0x25, + SPR_SET_SRG_BITMAP = 0x80, + SPR_SET_PARAM = 0xc2, + SPR_SET_SIGA = 0xdc, +}; + +enum { + THERMAL_PROTECT_PARAMETER_CTRL, + THERMAL_PROTECT_BASIC_INFO, + THERMAL_PROTECT_ENABLE, + THERMAL_PROTECT_DISABLE, + THERMAL_PROTECT_DUTY_CONFIG, + THERMAL_PROTECT_MECH_INFO, + THERMAL_PROTECT_DUTY_INFO, + THERMAL_PROTECT_STATE_ACT, +}; + +enum { + MT_BF_SOUNDING_ON = 1, + MT_BF_TYPE_UPDATE = 20, + MT_BF_MODULE_UPDATE = 25 +}; + +enum { + MURU_SET_ARB_OP_MODE = 14, + MURU_SET_PLATFORM_TYPE = 25, +}; + +enum { + MURU_PLATFORM_TYPE_PERF_LEVEL_1 = 1, + MURU_PLATFORM_TYPE_PERF_LEVEL_2, +}; + +/* tx cmd tx statistics */ +enum { + MURU_SET_TXC_TX_STATS_EN = 150, + MURU_GET_TXC_TX_STATS = 151, +}; + +enum { + SER_QUERY, + /* recovery */ + SER_SET_RECOVER_L1, + SER_SET_RECOVER_L2, + SER_SET_RECOVER_L3_RX_ABORT, + SER_SET_RECOVER_L3_TX_ABORT, + SER_SET_RECOVER_L3_TX_DISABLE, + SER_SET_RECOVER_L3_BF, + SER_SET_RECOVER_FULL, + SER_SET_SYSTEM_ASSERT, + /* action */ + SER_ENABLE = 2, + SER_RECOVER +}; + +#define MT7915_MAX_BEACON_SIZE 1308 +#define MT7915_BEACON_UPDATE_SIZE (sizeof(struct sta_req_hdr) + \ + sizeof(struct bss_info_bcn) + \ + sizeof(struct bss_info_bcn_cntdwn) + \ + sizeof(struct bss_info_bcn_mbss) + \ + MT_TXD_SIZE + \ + sizeof(struct bss_info_bcn_cont)) +#define MT7915_MAX_BSS_OFFLOAD_SIZE (MT7915_MAX_BEACON_SIZE + \ + MT7915_BEACON_UPDATE_SIZE) + +#define MT7915_BSS_UPDATE_MAX_SIZE (sizeof(struct sta_req_hdr) + \ + sizeof(struct bss_info_omac) + \ + sizeof(struct bss_info_basic) +\ + sizeof(struct bss_info_rf_ch) +\ + sizeof(struct bss_info_ra) + \ + sizeof(struct bss_info_hw_amsdu) +\ + sizeof(struct bss_info_he) + \ + sizeof(struct bss_info_bmc_rate) +\ + sizeof(struct bss_info_ext_bss)) + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c new file mode 100644 index 000000000..28377c964 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c @@ -0,0 +1,1005 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2020 MediaTek Inc. */ + +#include +#include +#include +#include +#include + +#include "mt7915.h" +#include "mac.h" +#include "mcu.h" +#include "../trace.h" +#include "../dma.h" + +static bool wed_enable; +module_param(wed_enable, bool, 0644); +MODULE_PARM_DESC(wed_enable, "Enable Wireless Ethernet Dispatch support"); + +static const u32 mt7915_reg[] = { + [INT_SOURCE_CSR] = 0xd7010, + [INT_MASK_CSR] = 0xd7014, + [INT1_SOURCE_CSR] = 0xd7088, + [INT1_MASK_CSR] = 0xd708c, + [INT_MCU_CMD_SOURCE] = 0xd51f0, + [INT_MCU_CMD_EVENT] = 0x3108, + [WFDMA0_ADDR] = 0xd4000, + [WFDMA0_PCIE1_ADDR] = 0xd8000, + [WFDMA_EXT_CSR_ADDR] = 0xd7000, + [CBTOP1_PHY_END] = 0x77ffffff, + [INFRA_MCU_ADDR_END] = 0x7c3fffff, + [FW_ASSERT_STAT_ADDR] = 0x219848, + [FW_EXCEPT_TYPE_ADDR] = 0x21987c, + [FW_EXCEPT_COUNT_ADDR] = 0x219848, + [FW_CIRQ_COUNT_ADDR] = 0x216f94, + [FW_CIRQ_IDX_ADDR] = 0x216ef8, + [FW_CIRQ_LISR_ADDR] = 0x2170ac, + [FW_TASK_ID_ADDR] = 0x216f90, + [FW_TASK_IDX_ADDR] = 0x216f9c, + [FW_TASK_QID1_ADDR] = 0x219680, + [FW_TASK_QID2_ADDR] = 0x219760, + [FW_TASK_START_ADDR] = 0x219558, + [FW_TASK_END_ADDR] = 0x219554, + [FW_TASK_SIZE_ADDR] = 0x219560, + [FW_LAST_MSG_ID_ADDR] = 0x216f70, + [FW_EINT_INFO_ADDR] = 0x219818, + [FW_SCHED_INFO_ADDR] = 0x219828, + [SWDEF_BASE_ADDR] = 0x41f200, + [TXQ_WED_RING_BASE] = 0xd7300, + [RXQ_WED_RING_BASE] = 0xd7410, + [RXQ_WED_DATA_RING_BASE] = 0xd4500, +}; + +static const u32 mt7916_reg[] = { + [INT_SOURCE_CSR] = 0xd4200, + [INT_MASK_CSR] = 0xd4204, + [INT1_SOURCE_CSR] = 0xd8200, + [INT1_MASK_CSR] = 0xd8204, + [INT_MCU_CMD_SOURCE] = 0xd41f0, + [INT_MCU_CMD_EVENT] = 0x2108, + [WFDMA0_ADDR] = 0xd4000, + [WFDMA0_PCIE1_ADDR] = 0xd8000, + [WFDMA_EXT_CSR_ADDR] = 0xd7000, + [CBTOP1_PHY_END] = 0x7fffffff, + [INFRA_MCU_ADDR_END] = 0x7c085fff, + [FW_ASSERT_STAT_ADDR] = 0x02204c14, + [FW_EXCEPT_TYPE_ADDR] = 0x022051a4, + [FW_EXCEPT_COUNT_ADDR] = 0x022050bc, + [FW_CIRQ_COUNT_ADDR] = 0x022001ac, + [FW_CIRQ_IDX_ADDR] = 0x02204f84, + [FW_CIRQ_LISR_ADDR] = 0x022050d0, + [FW_TASK_ID_ADDR] = 0x0220406c, + [FW_TASK_IDX_ADDR] = 0x0220500c, + [FW_TASK_QID1_ADDR] = 0x022028c8, + [FW_TASK_QID2_ADDR] = 0x02202a38, + [FW_TASK_START_ADDR] = 0x0220286c, + [FW_TASK_END_ADDR] = 0x02202870, + [FW_TASK_SIZE_ADDR] = 0x02202878, + [FW_LAST_MSG_ID_ADDR] = 0x02204fe8, + [FW_EINT_INFO_ADDR] = 0x0220525c, + [FW_SCHED_INFO_ADDR] = 0x0220516c, + [SWDEF_BASE_ADDR] = 0x411400, + [TXQ_WED_RING_BASE] = 0xd7300, + [RXQ_WED_RING_BASE] = 0xd7410, + [RXQ_WED_DATA_RING_BASE] = 0xd4540, +}; + +static const u32 mt7986_reg[] = { + [INT_SOURCE_CSR] = 0x24200, + [INT_MASK_CSR] = 0x24204, + [INT1_SOURCE_CSR] = 0x28200, + [INT1_MASK_CSR] = 0x28204, + [INT_MCU_CMD_SOURCE] = 0x241f0, + [INT_MCU_CMD_EVENT] = 0x54000108, + [WFDMA0_ADDR] = 0x24000, + [WFDMA0_PCIE1_ADDR] = 0x28000, + [WFDMA_EXT_CSR_ADDR] = 0x27000, + [CBTOP1_PHY_END] = 0x7fffffff, + [INFRA_MCU_ADDR_END] = 0x7c085fff, + [FW_ASSERT_STAT_ADDR] = 0x02204b54, + [FW_EXCEPT_TYPE_ADDR] = 0x022050dc, + [FW_EXCEPT_COUNT_ADDR] = 0x02204ffc, + [FW_CIRQ_COUNT_ADDR] = 0x022001ac, + [FW_CIRQ_IDX_ADDR] = 0x02204ec4, + [FW_CIRQ_LISR_ADDR] = 0x02205010, + [FW_TASK_ID_ADDR] = 0x02204fac, + [FW_TASK_IDX_ADDR] = 0x02204f4c, + [FW_TASK_QID1_ADDR] = 0x02202814, + [FW_TASK_QID2_ADDR] = 0x02202984, + [FW_TASK_START_ADDR] = 0x022027b8, + [FW_TASK_END_ADDR] = 0x022027bc, + [FW_TASK_SIZE_ADDR] = 0x022027c4, + [FW_LAST_MSG_ID_ADDR] = 0x02204f28, + [FW_EINT_INFO_ADDR] = 0x02205194, + [FW_SCHED_INFO_ADDR] = 0x022051a4, + [SWDEF_BASE_ADDR] = 0x411400, + [TXQ_WED_RING_BASE] = 0x24420, + [RXQ_WED_RING_BASE] = 0x24520, + [RXQ_WED_DATA_RING_BASE] = 0x24540, +}; + +static const u32 mt7915_offs[] = { + [TMAC_CDTR] = 0x090, + [TMAC_ODTR] = 0x094, + [TMAC_ATCR] = 0x098, + [TMAC_TRCR0] = 0x09c, + [TMAC_ICR0] = 0x0a4, + [TMAC_ICR1] = 0x0b4, + [TMAC_CTCR0] = 0x0f4, + [TMAC_TFCR0] = 0x1e0, + [MDP_BNRCFR0] = 0x070, + [MDP_BNRCFR1] = 0x074, + [ARB_DRNGR0] = 0x194, + [ARB_SCR] = 0x080, + [RMAC_MIB_AIRTIME14] = 0x3b8, + [AGG_AWSCR0] = 0x05c, + [AGG_PCR0] = 0x06c, + [AGG_ACR0] = 0x084, + [AGG_ACR4] = 0x08c, + [AGG_MRCR] = 0x098, + [AGG_ATCR0] = 0x0ec, + [AGG_ATCR1] = 0x0f0, + [AGG_ATCR3] = 0x0f4, + [LPON_UTTR0] = 0x080, + [LPON_UTTR1] = 0x084, + [LPON_FRCR] = 0x314, + [MIB_SDR3] = 0x014, + [MIB_SDR4] = 0x018, + [MIB_SDR5] = 0x01c, + [MIB_SDR7] = 0x024, + [MIB_SDR8] = 0x028, + [MIB_SDR9] = 0x02c, + [MIB_SDR10] = 0x030, + [MIB_SDR11] = 0x034, + [MIB_SDR12] = 0x038, + [MIB_SDR13] = 0x03c, + [MIB_SDR14] = 0x040, + [MIB_SDR15] = 0x044, + [MIB_SDR16] = 0x048, + [MIB_SDR17] = 0x04c, + [MIB_SDR18] = 0x050, + [MIB_SDR19] = 0x054, + [MIB_SDR20] = 0x058, + [MIB_SDR21] = 0x05c, + [MIB_SDR22] = 0x060, + [MIB_SDR23] = 0x064, + [MIB_SDR24] = 0x068, + [MIB_SDR25] = 0x06c, + [MIB_SDR27] = 0x074, + [MIB_SDR28] = 0x078, + [MIB_SDR29] = 0x07c, + [MIB_SDRVEC] = 0x080, + [MIB_SDR31] = 0x084, + [MIB_SDR32] = 0x088, + [MIB_SDRMUBF] = 0x090, + [MIB_DR8] = 0x0c0, + [MIB_DR9] = 0x0c4, + [MIB_DR11] = 0x0cc, + [MIB_MB_SDR0] = 0x100, + [MIB_MB_SDR1] = 0x104, + [TX_AGG_CNT] = 0x0a8, + [TX_AGG_CNT2] = 0x164, + [MIB_ARNG] = 0x4b8, + [WTBLON_TOP_WDUCR] = 0x0, + [WTBL_UPDATE] = 0x030, + [PLE_FL_Q_EMPTY] = 0x0b0, + [PLE_FL_Q_CTRL] = 0x1b0, + [PLE_AC_QEMPTY] = 0x500, + [PLE_FREEPG_CNT] = 0x100, + [PLE_FREEPG_HEAD_TAIL] = 0x104, + [PLE_PG_HIF_GROUP] = 0x110, + [PLE_HIF_PG_INFO] = 0x114, + [AC_OFFSET] = 0x040, + [ETBF_PAR_RPT0] = 0x068, +}; + +static const u32 mt7916_offs[] = { + [TMAC_CDTR] = 0x0c8, + [TMAC_ODTR] = 0x0cc, + [TMAC_ATCR] = 0x00c, + [TMAC_TRCR0] = 0x010, + [TMAC_ICR0] = 0x014, + [TMAC_ICR1] = 0x018, + [TMAC_CTCR0] = 0x114, + [TMAC_TFCR0] = 0x0e4, + [MDP_BNRCFR0] = 0x090, + [MDP_BNRCFR1] = 0x094, + [ARB_DRNGR0] = 0x1e0, + [ARB_SCR] = 0x000, + [RMAC_MIB_AIRTIME14] = 0x0398, + [AGG_AWSCR0] = 0x030, + [AGG_PCR0] = 0x040, + [AGG_ACR0] = 0x054, + [AGG_ACR4] = 0x05c, + [AGG_MRCR] = 0x068, + [AGG_ATCR0] = 0x1a4, + [AGG_ATCR1] = 0x1a8, + [AGG_ATCR3] = 0x080, + [LPON_UTTR0] = 0x360, + [LPON_UTTR1] = 0x364, + [LPON_FRCR] = 0x37c, + [MIB_SDR3] = 0x698, + [MIB_SDR4] = 0x788, + [MIB_SDR5] = 0x780, + [MIB_SDR7] = 0x5a8, + [MIB_SDR8] = 0x78c, + [MIB_SDR9] = 0x024, + [MIB_SDR10] = 0x76c, + [MIB_SDR11] = 0x790, + [MIB_SDR12] = 0x558, + [MIB_SDR13] = 0x560, + [MIB_SDR14] = 0x564, + [MIB_SDR15] = 0x568, + [MIB_SDR16] = 0x7fc, + [MIB_SDR17] = 0x800, + [MIB_SDR18] = 0x030, + [MIB_SDR19] = 0x5ac, + [MIB_SDR20] = 0x5b0, + [MIB_SDR21] = 0x5b4, + [MIB_SDR22] = 0x770, + [MIB_SDR23] = 0x774, + [MIB_SDR24] = 0x778, + [MIB_SDR25] = 0x77c, + [MIB_SDR27] = 0x080, + [MIB_SDR28] = 0x084, + [MIB_SDR29] = 0x650, + [MIB_SDRVEC] = 0x5a8, + [MIB_SDR31] = 0x55c, + [MIB_SDR32] = 0x7a8, + [MIB_SDRMUBF] = 0x7ac, + [MIB_DR8] = 0x56c, + [MIB_DR9] = 0x570, + [MIB_DR11] = 0x574, + [MIB_MB_SDR0] = 0x688, + [MIB_MB_SDR1] = 0x690, + [TX_AGG_CNT] = 0x7dc, + [TX_AGG_CNT2] = 0x7ec, + [MIB_ARNG] = 0x0b0, + [WTBLON_TOP_WDUCR] = 0x200, + [WTBL_UPDATE] = 0x230, + [PLE_FL_Q_EMPTY] = 0x360, + [PLE_FL_Q_CTRL] = 0x3e0, + [PLE_AC_QEMPTY] = 0x600, + [PLE_FREEPG_CNT] = 0x380, + [PLE_FREEPG_HEAD_TAIL] = 0x384, + [PLE_PG_HIF_GROUP] = 0x00c, + [PLE_HIF_PG_INFO] = 0x388, + [AC_OFFSET] = 0x080, + [ETBF_PAR_RPT0] = 0x100, +}; + +static const struct mt76_connac_reg_map mt7915_reg_map[] = { + { 0x00400000, 0x80000, 0x10000 }, /* WF_MCU_SYSRAM */ + { 0x00410000, 0x90000, 0x10000 }, /* WF_MCU_SYSRAM (configure regs) */ + { 0x40000000, 0x70000, 0x10000 }, /* WF_UMAC_SYSRAM */ + { 0x54000000, 0x02000, 0x01000 }, /* WFDMA PCIE0 MCU DMA0 */ + { 0x55000000, 0x03000, 0x01000 }, /* WFDMA PCIE0 MCU DMA1 */ + { 0x58000000, 0x06000, 0x01000 }, /* WFDMA PCIE1 MCU DMA0 (MEM_DMA) */ + { 0x59000000, 0x07000, 0x01000 }, /* WFDMA PCIE1 MCU DMA1 */ + { 0x7c000000, 0xf0000, 0x10000 }, /* CONN_INFRA */ + { 0x7c020000, 0xd0000, 0x10000 }, /* CONN_INFRA, WFDMA */ + { 0x80020000, 0xb0000, 0x10000 }, /* WF_TOP_MISC_OFF */ + { 0x81020000, 0xc0000, 0x10000 }, /* WF_TOP_MISC_ON */ + { 0x820c0000, 0x08000, 0x04000 }, /* WF_UMAC_TOP (PLE) */ + { 0x820c8000, 0x0c000, 0x02000 }, /* WF_UMAC_TOP (PSE) */ + { 0x820cc000, 0x0e000, 0x02000 }, /* WF_UMAC_TOP (PP) */ + { 0x820ce000, 0x21c00, 0x00200 }, /* WF_LMAC_TOP (WF_SEC) */ + { 0x820cf000, 0x22000, 0x01000 }, /* WF_LMAC_TOP (WF_PF) */ + { 0x820d0000, 0x30000, 0x10000 }, /* WF_LMAC_TOP (WF_WTBLON) */ + { 0x820e0000, 0x20000, 0x00400 }, /* WF_LMAC_TOP BN0 (WF_CFG) */ + { 0x820e1000, 0x20400, 0x00200 }, /* WF_LMAC_TOP BN0 (WF_TRB) */ + { 0x820e2000, 0x20800, 0x00400 }, /* WF_LMAC_TOP BN0 (WF_AGG) */ + { 0x820e3000, 0x20c00, 0x00400 }, /* WF_LMAC_TOP BN0 (WF_ARB) */ + { 0x820e4000, 0x21000, 0x00400 }, /* WF_LMAC_TOP BN0 (WF_TMAC) */ + { 0x820e5000, 0x21400, 0x00800 }, /* WF_LMAC_TOP BN0 (WF_RMAC) */ + { 0x820e7000, 0x21e00, 0x00200 }, /* WF_LMAC_TOP BN0 (WF_DMA) */ + { 0x820e9000, 0x23400, 0x00200 }, /* WF_LMAC_TOP BN0 (WF_WTBLOFF) */ + { 0x820ea000, 0x24000, 0x00200 }, /* WF_LMAC_TOP BN0 (WF_ETBF) */ + { 0x820eb000, 0x24200, 0x00400 }, /* WF_LMAC_TOP BN0 (WF_LPON) */ + { 0x820ec000, 0x24600, 0x00200 }, /* WF_LMAC_TOP BN0 (WF_INT) */ + { 0x820ed000, 0x24800, 0x00800 }, /* WF_LMAC_TOP BN0 (WF_MIB) */ + { 0x820f0000, 0xa0000, 0x00400 }, /* WF_LMAC_TOP BN1 (WF_CFG) */ + { 0x820f1000, 0xa0600, 0x00200 }, /* WF_LMAC_TOP BN1 (WF_TRB) */ + { 0x820f2000, 0xa0800, 0x00400 }, /* WF_LMAC_TOP BN1 (WF_AGG) */ + { 0x820f3000, 0xa0c00, 0x00400 }, /* WF_LMAC_TOP BN1 (WF_ARB) */ + { 0x820f4000, 0xa1000, 0x00400 }, /* WF_LMAC_TOP BN1 (WF_TMAC) */ + { 0x820f5000, 0xa1400, 0x00800 }, /* WF_LMAC_TOP BN1 (WF_RMAC) */ + { 0x820f7000, 0xa1e00, 0x00200 }, /* WF_LMAC_TOP BN1 (WF_DMA) */ + { 0x820f9000, 0xa3400, 0x00200 }, /* WF_LMAC_TOP BN1 (WF_WTBLOFF) */ + { 0x820fa000, 0xa4000, 0x00200 }, /* WF_LMAC_TOP BN1 (WF_ETBF) */ + { 0x820fb000, 0xa4200, 0x00400 }, /* WF_LMAC_TOP BN1 (WF_LPON) */ + { 0x820fc000, 0xa4600, 0x00200 }, /* WF_LMAC_TOP BN1 (WF_INT) */ + { 0x820fd000, 0xa4800, 0x00800 }, /* WF_LMAC_TOP BN1 (WF_MIB) */ + { 0x0, 0x0, 0x0 }, /* imply end of search */ +}; + +static const struct mt76_connac_reg_map mt7916_reg_map[] = { + { 0x54000000, 0x02000, 0x01000 }, /* WFDMA_0 (PCIE0 MCU DMA0) */ + { 0x55000000, 0x03000, 0x01000 }, /* WFDMA_1 (PCIE0 MCU DMA1) */ + { 0x56000000, 0x04000, 0x01000 }, /* WFDMA_2 (Reserved) */ + { 0x57000000, 0x05000, 0x01000 }, /* WFDMA_3 (MCU wrap CR) */ + { 0x58000000, 0x06000, 0x01000 }, /* WFDMA_4 (PCIE1 MCU DMA0) */ + { 0x59000000, 0x07000, 0x01000 }, /* WFDMA_5 (PCIE1 MCU DMA1) */ + { 0x820c0000, 0x08000, 0x04000 }, /* WF_UMAC_TOP (PLE) */ + { 0x820c8000, 0x0c000, 0x02000 }, /* WF_UMAC_TOP (PSE) */ + { 0x820cc000, 0x0e000, 0x02000 }, /* WF_UMAC_TOP (PP) */ + { 0x820e0000, 0x20000, 0x00400 }, /* WF_LMAC_TOP BN0 (WF_CFG) */ + { 0x820e1000, 0x20400, 0x00200 }, /* WF_LMAC_TOP BN0 (WF_TRB) */ + { 0x820e2000, 0x20800, 0x00400 }, /* WF_LMAC_TOP BN0 (WF_AGG) */ + { 0x820e3000, 0x20c00, 0x00400 }, /* WF_LMAC_TOP BN0 (WF_ARB) */ + { 0x820e4000, 0x21000, 0x00400 }, /* WF_LMAC_TOP BN0 (WF_TMAC) */ + { 0x820e5000, 0x21400, 0x00800 }, /* WF_LMAC_TOP BN0 (WF_RMAC) */ + { 0x820ce000, 0x21c00, 0x00200 }, /* WF_LMAC_TOP (WF_SEC) */ + { 0x820e7000, 0x21e00, 0x00200 }, /* WF_LMAC_TOP BN0 (WF_DMA) */ + { 0x820cf000, 0x22000, 0x01000 }, /* WF_LMAC_TOP (WF_PF) */ + { 0x820e9000, 0x23400, 0x00200 }, /* WF_LMAC_TOP BN0 (WF_WTBLOFF) */ + { 0x820ea000, 0x24000, 0x00200 }, /* WF_LMAC_TOP BN0 (WF_ETBF) */ + { 0x820eb000, 0x24200, 0x00400 }, /* WF_LMAC_TOP BN0 (WF_LPON) */ + { 0x820ec000, 0x24600, 0x00200 }, /* WF_LMAC_TOP BN0 (WF_INT) */ + { 0x820ed000, 0x24800, 0x00800 }, /* WF_LMAC_TOP BN0 (WF_MIB) */ + { 0x820ca000, 0x26000, 0x02000 }, /* WF_LMAC_TOP BN0 (WF_MUCOP) */ + { 0x820d0000, 0x30000, 0x10000 }, /* WF_LMAC_TOP (WF_WTBLON) */ + { 0x00400000, 0x80000, 0x10000 }, /* WF_MCU_SYSRAM */ + { 0x00410000, 0x90000, 0x10000 }, /* WF_MCU_SYSRAM (configure cr) */ + { 0x820f0000, 0xa0000, 0x00400 }, /* WF_LMAC_TOP BN1 (WF_CFG) */ + { 0x820f1000, 0xa0600, 0x00200 }, /* WF_LMAC_TOP BN1 (WF_TRB) */ + { 0x820f2000, 0xa0800, 0x00400 }, /* WF_LMAC_TOP BN1 (WF_AGG) */ + { 0x820f3000, 0xa0c00, 0x00400 }, /* WF_LMAC_TOP BN1 (WF_ARB) */ + { 0x820f4000, 0xa1000, 0x00400 }, /* WF_LMAC_TOP BN1 (WF_TMAC) */ + { 0x820f5000, 0xa1400, 0x00800 }, /* WF_LMAC_TOP BN1 (WF_RMAC) */ + { 0x820f7000, 0xa1e00, 0x00200 }, /* WF_LMAC_TOP BN1 (WF_DMA) */ + { 0x820f9000, 0xa3400, 0x00200 }, /* WF_LMAC_TOP BN1 (WF_WTBLOFF) */ + { 0x820fa000, 0xa4000, 0x00200 }, /* WF_LMAC_TOP BN1 (WF_ETBF) */ + { 0x820fb000, 0xa4200, 0x00400 }, /* WF_LMAC_TOP BN1 (WF_LPON) */ + { 0x820fc000, 0xa4600, 0x00200 }, /* WF_LMAC_TOP BN1 (WF_INT) */ + { 0x820fd000, 0xa4800, 0x00800 }, /* WF_LMAC_TOP BN1 (WF_MIB) */ + { 0x820c4000, 0xa8000, 0x01000 }, /* WF_LMAC_TOP (WF_UWTBL ) */ + { 0x820b0000, 0xae000, 0x01000 }, /* [APB2] WFSYS_ON */ + { 0x80020000, 0xb0000, 0x10000 }, /* WF_TOP_MISC_OFF */ + { 0x81020000, 0xc0000, 0x10000 }, /* WF_TOP_MISC_ON */ + { 0x0, 0x0, 0x0 }, /* imply end of search */ +}; + +static const struct mt76_connac_reg_map mt7986_reg_map[] = { + { 0x54000000, 0x402000, 0x01000 }, /* WFDMA_0 (PCIE0 MCU DMA0) */ + { 0x55000000, 0x403000, 0x01000 }, /* WFDMA_1 (PCIE0 MCU DMA1) */ + { 0x56000000, 0x404000, 0x01000 }, /* WFDMA_2 (Reserved) */ + { 0x57000000, 0x405000, 0x01000 }, /* WFDMA_3 (MCU wrap CR) */ + { 0x58000000, 0x406000, 0x01000 }, /* WFDMA_4 (PCIE1 MCU DMA0) */ + { 0x59000000, 0x407000, 0x01000 }, /* WFDMA_5 (PCIE1 MCU DMA1) */ + { 0x820c0000, 0x408000, 0x04000 }, /* WF_UMAC_TOP (PLE) */ + { 0x820c8000, 0x40c000, 0x02000 }, /* WF_UMAC_TOP (PSE) */ + { 0x820cc000, 0x40e000, 0x02000 }, /* WF_UMAC_TOP (PP) */ + { 0x820e0000, 0x420000, 0x00400 }, /* WF_LMAC_TOP BN0 (WF_CFG) */ + { 0x820e1000, 0x420400, 0x00200 }, /* WF_LMAC_TOP BN0 (WF_TRB) */ + { 0x820e2000, 0x420800, 0x00400 }, /* WF_LMAC_TOP BN0 (WF_AGG) */ + { 0x820e3000, 0x420c00, 0x00400 }, /* WF_LMAC_TOP BN0 (WF_ARB) */ + { 0x820e4000, 0x421000, 0x00400 }, /* WF_LMAC_TOP BN0 (WF_TMAC) */ + { 0x820e5000, 0x421400, 0x00800 }, /* WF_LMAC_TOP BN0 (WF_RMAC) */ + { 0x820ce000, 0x421c00, 0x00200 }, /* WF_LMAC_TOP (WF_SEC) */ + { 0x820e7000, 0x421e00, 0x00200 }, /* WF_LMAC_TOP BN0 (WF_DMA) */ + { 0x820cf000, 0x422000, 0x01000 }, /* WF_LMAC_TOP (WF_PF) */ + { 0x820e9000, 0x423400, 0x00200 }, /* WF_LMAC_TOP BN0 (WF_WTBLOFF) */ + { 0x820ea000, 0x424000, 0x00200 }, /* WF_LMAC_TOP BN0 (WF_ETBF) */ + { 0x820eb000, 0x424200, 0x00400 }, /* WF_LMAC_TOP BN0 (WF_LPON) */ + { 0x820ec000, 0x424600, 0x00200 }, /* WF_LMAC_TOP BN0 (WF_INT) */ + { 0x820ed000, 0x424800, 0x00800 }, /* WF_LMAC_TOP BN0 (WF_MIB) */ + { 0x820ca000, 0x426000, 0x02000 }, /* WF_LMAC_TOP BN0 (WF_MUCOP) */ + { 0x820d0000, 0x430000, 0x10000 }, /* WF_LMAC_TOP (WF_WTBLON) */ + { 0x00400000, 0x480000, 0x10000 }, /* WF_MCU_SYSRAM */ + { 0x00410000, 0x490000, 0x10000 }, /* WF_MCU_SYSRAM */ + { 0x820f0000, 0x4a0000, 0x00400 }, /* WF_LMAC_TOP BN1 (WF_CFG) */ + { 0x820f1000, 0x4a0600, 0x00200 }, /* WF_LMAC_TOP BN1 (WF_TRB) */ + { 0x820f2000, 0x4a0800, 0x00400 }, /* WF_LMAC_TOP BN1 (WF_AGG) */ + { 0x820f3000, 0x4a0c00, 0x00400 }, /* WF_LMAC_TOP BN1 (WF_ARB) */ + { 0x820f4000, 0x4a1000, 0x00400 }, /* WF_LMAC_TOP BN1 (WF_TMAC) */ + { 0x820f5000, 0x4a1400, 0x00800 }, /* WF_LMAC_TOP BN1 (WF_RMAC) */ + { 0x820f7000, 0x4a1e00, 0x00200 }, /* WF_LMAC_TOP BN1 (WF_DMA) */ + { 0x820f9000, 0x4a3400, 0x00200 }, /* WF_LMAC_TOP BN1 (WF_WTBLOFF) */ + { 0x820fa000, 0x4a4000, 0x00200 }, /* WF_LMAC_TOP BN1 (WF_ETBF) */ + { 0x820fb000, 0x4a4200, 0x00400 }, /* WF_LMAC_TOP BN1 (WF_LPON) */ + { 0x820fc000, 0x4a4600, 0x00200 }, /* WF_LMAC_TOP BN1 (WF_INT) */ + { 0x820fd000, 0x4a4800, 0x00800 }, /* WF_LMAC_TOP BN1 (WF_MIB) */ + { 0x820c4000, 0x4a8000, 0x01000 }, /* WF_LMAC_TOP (WF_UWTBL ) */ + { 0x820b0000, 0x4ae000, 0x01000 }, /* [APB2] WFSYS_ON */ + { 0x80020000, 0x4b0000, 0x10000 }, /* WF_TOP_MISC_OFF */ + { 0x81020000, 0x4c0000, 0x10000 }, /* WF_TOP_MISC_ON */ + { 0x89000000, 0x4d0000, 0x01000 }, /* WF_MCU_CFG_ON */ + { 0x89010000, 0x4d1000, 0x01000 }, /* WF_MCU_CIRQ */ + { 0x89020000, 0x4d2000, 0x01000 }, /* WF_MCU_GPT */ + { 0x89030000, 0x4d3000, 0x01000 }, /* WF_MCU_WDT */ + { 0x80010000, 0x4d4000, 0x01000 }, /* WF_AXIDMA */ + { 0x0, 0x0, 0x0 }, /* imply end of search */ +}; + +static u32 mt7915_reg_map_l1(struct mt7915_dev *dev, u32 addr) +{ + u32 offset = FIELD_GET(MT_HIF_REMAP_L1_OFFSET, addr); + u32 base = FIELD_GET(MT_HIF_REMAP_L1_BASE, addr); + u32 l1_remap; + + if (is_mt798x(&dev->mt76)) + return MT_CONN_INFRA_OFFSET(addr); + + l1_remap = is_mt7915(&dev->mt76) ? + MT_HIF_REMAP_L1 : MT_HIF_REMAP_L1_MT7916; + + dev->bus_ops->rmw(&dev->mt76, l1_remap, + MT_HIF_REMAP_L1_MASK, + FIELD_PREP(MT_HIF_REMAP_L1_MASK, base)); + /* use read to push write */ + dev->bus_ops->rr(&dev->mt76, l1_remap); + + return MT_HIF_REMAP_BASE_L1 + offset; +} + +static u32 mt7915_reg_map_l2(struct mt7915_dev *dev, u32 addr) +{ + u32 offset, base; + + if (is_mt7915(&dev->mt76)) { + offset = FIELD_GET(MT_HIF_REMAP_L2_OFFSET, addr); + base = FIELD_GET(MT_HIF_REMAP_L2_BASE, addr); + + dev->bus_ops->rmw(&dev->mt76, MT_HIF_REMAP_L2, + MT_HIF_REMAP_L2_MASK, + FIELD_PREP(MT_HIF_REMAP_L2_MASK, base)); + + /* use read to push write */ + dev->bus_ops->rr(&dev->mt76, MT_HIF_REMAP_L2); + } else { + u32 ofs = is_mt798x(&dev->mt76) ? 0x400000 : 0; + + offset = FIELD_GET(MT_HIF_REMAP_L2_OFFSET_MT7916, addr); + base = FIELD_GET(MT_HIF_REMAP_L2_BASE_MT7916, addr); + + dev->bus_ops->rmw(&dev->mt76, MT_HIF_REMAP_L2_MT7916 + ofs, + MT_HIF_REMAP_L2_MASK_MT7916, + FIELD_PREP(MT_HIF_REMAP_L2_MASK_MT7916, base)); + + /* use read to push write */ + dev->bus_ops->rr(&dev->mt76, MT_HIF_REMAP_L2_MT7916 + ofs); + + offset += (MT_HIF_REMAP_BASE_L2_MT7916 + ofs); + } + + return offset; +} + +static u32 __mt7915_reg_addr(struct mt7915_dev *dev, u32 addr) +{ + int i; + + if (addr < 0x100000) + return addr; + + if (!dev->reg.map) { + dev_err(dev->mt76.dev, "err: reg_map is null\n"); + return addr; + } + + for (i = 0; i < dev->reg.map_size; i++) { + u32 ofs; + + if (addr < dev->reg.map[i].phys) + continue; + + ofs = addr - dev->reg.map[i].phys; + if (ofs >= dev->reg.map[i].size) + continue; + + return dev->reg.map[i].maps + ofs; + } + + return 0; +} + +static u32 __mt7915_reg_remap_addr(struct mt7915_dev *dev, u32 addr) +{ + if ((addr >= MT_INFRA_BASE && addr < MT_WFSYS0_PHY_START) || + (addr >= MT_WFSYS0_PHY_START && addr < MT_WFSYS1_PHY_START) || + (addr >= MT_WFSYS1_PHY_START && addr <= MT_WFSYS1_PHY_END)) + return mt7915_reg_map_l1(dev, addr); + + if (dev_is_pci(dev->mt76.dev) && + ((addr >= MT_CBTOP1_PHY_START && addr <= MT_CBTOP1_PHY_END) || + addr >= MT_CBTOP2_PHY_START)) + return mt7915_reg_map_l1(dev, addr); + + /* CONN_INFRA: covert to phyiscal addr and use layer 1 remap */ + if (addr >= MT_INFRA_MCU_START && addr <= MT_INFRA_MCU_END) { + addr = addr - MT_INFRA_MCU_START + MT_INFRA_BASE; + return mt7915_reg_map_l1(dev, addr); + } + + return mt7915_reg_map_l2(dev, addr); +} + +void mt7915_memcpy_fromio(struct mt7915_dev *dev, void *buf, u32 offset, + size_t len) +{ + u32 addr = __mt7915_reg_addr(dev, offset); + + if (addr) { + memcpy_fromio(buf, dev->mt76.mmio.regs + addr, len); + return; + } + + spin_lock_bh(&dev->reg_lock); + memcpy_fromio(buf, dev->mt76.mmio.regs + + __mt7915_reg_remap_addr(dev, offset), len); + spin_unlock_bh(&dev->reg_lock); +} + +static u32 mt7915_rr(struct mt76_dev *mdev, u32 offset) +{ + struct mt7915_dev *dev = container_of(mdev, struct mt7915_dev, mt76); + u32 addr = __mt7915_reg_addr(dev, offset), val; + + if (addr) + return dev->bus_ops->rr(mdev, addr); + + spin_lock_bh(&dev->reg_lock); + val = dev->bus_ops->rr(mdev, __mt7915_reg_remap_addr(dev, offset)); + spin_unlock_bh(&dev->reg_lock); + + return val; +} + +static void mt7915_wr(struct mt76_dev *mdev, u32 offset, u32 val) +{ + struct mt7915_dev *dev = container_of(mdev, struct mt7915_dev, mt76); + u32 addr = __mt7915_reg_addr(dev, offset); + + if (addr) { + dev->bus_ops->wr(mdev, addr, val); + return; + } + + spin_lock_bh(&dev->reg_lock); + dev->bus_ops->wr(mdev, __mt7915_reg_remap_addr(dev, offset), val); + spin_unlock_bh(&dev->reg_lock); +} + +static u32 mt7915_rmw(struct mt76_dev *mdev, u32 offset, u32 mask, u32 val) +{ + struct mt7915_dev *dev = container_of(mdev, struct mt7915_dev, mt76); + u32 addr = __mt7915_reg_addr(dev, offset); + + if (addr) + return dev->bus_ops->rmw(mdev, addr, mask, val); + + spin_lock_bh(&dev->reg_lock); + val = dev->bus_ops->rmw(mdev, __mt7915_reg_remap_addr(dev, offset), mask, val); + spin_unlock_bh(&dev->reg_lock); + + return val; +} + +#ifdef CONFIG_NET_MEDIATEK_SOC_WED +static void mt7915_mmio_wed_update_rx_stats(struct mtk_wed_device *wed, + struct mtk_wed_wo_rx_stats *stats) +{ + int idx = le16_to_cpu(stats->wlan_idx); + struct mt7915_dev *dev; + struct mt76_wcid *wcid; + + dev = container_of(wed, struct mt7915_dev, mt76.mmio.wed); + + rcu_read_lock(); + + wcid = mt76_wcid_ptr(dev, idx); + if (wcid) { + wcid->stats.rx_bytes += le32_to_cpu(stats->rx_byte_cnt); + wcid->stats.rx_packets += le32_to_cpu(stats->rx_pkt_cnt); + wcid->stats.rx_errors += le32_to_cpu(stats->rx_err_cnt); + wcid->stats.rx_drops += le32_to_cpu(stats->rx_drop_cnt); + } + + rcu_read_unlock(); +} + +static int mt7915_mmio_wed_reset(struct mtk_wed_device *wed) +{ + struct mt76_dev *mdev = container_of(wed, struct mt76_dev, mmio.wed); + struct mt7915_dev *dev = container_of(mdev, struct mt7915_dev, mt76); + struct mt76_phy *mphy = &dev->mphy; + int ret; + + ASSERT_RTNL(); + + if (test_and_set_bit(MT76_STATE_WED_RESET, &mphy->state)) + return -EBUSY; + + ret = mt7915_mcu_set_ser(dev, SER_RECOVER, SER_SET_RECOVER_L1, + mphy->band_idx); + if (ret) + goto out; + + rtnl_unlock(); + if (!wait_for_completion_timeout(&mdev->mmio.wed_reset, 20 * HZ)) { + dev_err(mdev->dev, "wed reset timeout\n"); + ret = -ETIMEDOUT; + } + rtnl_lock(); +out: + clear_bit(MT76_STATE_WED_RESET, &mphy->state); + + return ret; +} +#endif + +int mt7915_mmio_wed_init(struct mt7915_dev *dev, void *pdev_ptr, + bool pci, int *irq) +{ +#ifdef CONFIG_NET_MEDIATEK_SOC_WED + struct mtk_wed_device *wed = &dev->mt76.mmio.wed; + int ret; + + if (!wed_enable) + return 0; + + if (pci) { + struct pci_dev *pci_dev = pdev_ptr; + + wed->wlan.pci_dev = pci_dev; + wed->wlan.bus_type = MTK_WED_BUS_PCIE; + wed->wlan.base = devm_ioremap(dev->mt76.dev, + pci_resource_start(pci_dev, 0), + pci_resource_len(pci_dev, 0)); + if (!wed->wlan.base) + return -ENOMEM; + + wed->wlan.phy_base = pci_resource_start(pci_dev, 0); + wed->wlan.wpdma_int = pci_resource_start(pci_dev, 0) + + MT_INT_WED_SOURCE_CSR; + wed->wlan.wpdma_mask = pci_resource_start(pci_dev, 0) + + MT_INT_WED_MASK_CSR; + wed->wlan.wpdma_phys = pci_resource_start(pci_dev, 0) + + MT_WFDMA_EXT_CSR_BASE; + wed->wlan.wpdma_tx = pci_resource_start(pci_dev, 0) + + MT_TXQ_WED_RING_BASE; + wed->wlan.wpdma_txfree = pci_resource_start(pci_dev, 0) + + MT_RXQ_WED_RING_BASE; + wed->wlan.wpdma_rx_glo = pci_resource_start(pci_dev, 0) + + MT_WPDMA_GLO_CFG; + wed->wlan.wpdma_rx[0] = pci_resource_start(pci_dev, 0) + + MT_RXQ_WED_DATA_RING_BASE; + } else { + struct platform_device *plat_dev = pdev_ptr; + struct resource *res; + + res = platform_get_resource(plat_dev, IORESOURCE_MEM, 0); + if (!res) + return 0; + + wed->wlan.platform_dev = plat_dev; + wed->wlan.bus_type = MTK_WED_BUS_AXI; + wed->wlan.base = devm_ioremap(dev->mt76.dev, res->start, + resource_size(res)); + if (!wed->wlan.base) + return -ENOMEM; + + wed->wlan.phy_base = res->start; + wed->wlan.wpdma_int = res->start + MT_INT_SOURCE_CSR; + wed->wlan.wpdma_mask = res->start + MT_INT_MASK_CSR; + wed->wlan.wpdma_tx = res->start + MT_TXQ_WED_RING_BASE; + wed->wlan.wpdma_txfree = res->start + MT_RXQ_WED_RING_BASE; + wed->wlan.wpdma_rx_glo = res->start + MT_WPDMA_GLO_CFG; + wed->wlan.wpdma_rx[0] = res->start + MT_RXQ_WED_DATA_RING_BASE; + } + wed->wlan.nbuf = MT7915_HW_TOKEN_SIZE; + wed->wlan.tx_tbit[0] = is_mt7915(&dev->mt76) ? 4 : 30; + wed->wlan.tx_tbit[1] = is_mt7915(&dev->mt76) ? 5 : 31; + wed->wlan.txfree_tbit = is_mt798x(&dev->mt76) ? 2 : 1; + wed->wlan.token_start = MT7915_TOKEN_SIZE - wed->wlan.nbuf; + wed->wlan.wcid_512 = !is_mt7915(&dev->mt76); + + wed->wlan.rx_nbuf = 65536; + wed->wlan.rx_npkt = MT7915_WED_RX_TOKEN_SIZE; + wed->wlan.rx_size = SKB_WITH_OVERHEAD(MT_RX_BUF_SIZE); + if (is_mt7915(&dev->mt76)) { + wed->wlan.rx_tbit[0] = 16; + wed->wlan.rx_tbit[1] = 17; + } else if (is_mt798x(&dev->mt76)) { + wed->wlan.rx_tbit[0] = 22; + wed->wlan.rx_tbit[1] = 23; + } else { + wed->wlan.rx_tbit[0] = 18; + wed->wlan.rx_tbit[1] = 19; + } + + wed->wlan.init_buf = mt7915_wed_init_buf; + wed->wlan.offload_enable = mt76_wed_offload_enable; + wed->wlan.offload_disable = mt76_wed_offload_disable; + wed->wlan.init_rx_buf = mt76_wed_init_rx_buf; + wed->wlan.release_rx_buf = mt76_wed_release_rx_buf; + wed->wlan.update_wo_rx_stats = mt7915_mmio_wed_update_rx_stats; + wed->wlan.reset = mt7915_mmio_wed_reset; + wed->wlan.reset_complete = mt76_wed_reset_complete; + + dev->mt76.rx_token_size = wed->wlan.rx_npkt; + + if (mtk_wed_device_attach(wed)) + return 0; + + *irq = wed->irq; + dev->mt76.dma_dev = wed->dev; + + ret = dma_set_mask(wed->dev, DMA_BIT_MASK(32)); + if (ret) + return ret; + + return 1; +#else + return 0; +#endif +} + +static int mt7915_mmio_init(struct mt76_dev *mdev, + void __iomem *mem_base, + u32 device_id) +{ + struct mt76_bus_ops *bus_ops; + struct mt7915_dev *dev; + + dev = container_of(mdev, struct mt7915_dev, mt76); + mt76_mmio_init(&dev->mt76, mem_base); + spin_lock_init(&dev->reg_lock); + + switch (device_id) { + case 0x7915: + dev->reg.reg_rev = mt7915_reg; + dev->reg.offs_rev = mt7915_offs; + dev->reg.map = mt7915_reg_map; + dev->reg.map_size = ARRAY_SIZE(mt7915_reg_map); + break; + case 0x7906: + dev->reg.reg_rev = mt7916_reg; + dev->reg.offs_rev = mt7916_offs; + dev->reg.map = mt7916_reg_map; + dev->reg.map_size = ARRAY_SIZE(mt7916_reg_map); + break; + case 0x7981: + case 0x7986: + dev->reg.reg_rev = mt7986_reg; + dev->reg.offs_rev = mt7916_offs; + dev->reg.map = mt7986_reg_map; + dev->reg.map_size = ARRAY_SIZE(mt7986_reg_map); + break; + default: + return -EINVAL; + } + + dev->bus_ops = dev->mt76.bus; + bus_ops = devm_kmemdup(dev->mt76.dev, dev->bus_ops, sizeof(*bus_ops), + GFP_KERNEL); + if (!bus_ops) + return -ENOMEM; + + bus_ops->rr = mt7915_rr; + bus_ops->wr = mt7915_wr; + bus_ops->rmw = mt7915_rmw; + dev->mt76.bus = bus_ops; + + mdev->rev = (device_id << 16) | + (mt76_rr(dev, MT_HW_REV) & 0xff); + dev_dbg(mdev->dev, "ASIC revision: %04x\n", mdev->rev); + + return 0; +} + +void mt7915_dual_hif_set_irq_mask(struct mt7915_dev *dev, + bool write_reg, + u32 clear, u32 set) +{ + struct mt76_dev *mdev = &dev->mt76; + unsigned long flags; + + spin_lock_irqsave(&mdev->mmio.irq_lock, flags); + + mdev->mmio.irqmask &= ~clear; + mdev->mmio.irqmask |= set; + + if (write_reg) { + if (mtk_wed_device_active(&mdev->mmio.wed)) + mtk_wed_device_irq_set_mask(&mdev->mmio.wed, + mdev->mmio.irqmask); + else + mt76_wr(dev, MT_INT_MASK_CSR, mdev->mmio.irqmask); + mt76_wr(dev, MT_INT1_MASK_CSR, mdev->mmio.irqmask); + } + + spin_unlock_irqrestore(&mdev->mmio.irq_lock, flags); +} + +static void mt7915_rx_poll_complete(struct mt76_dev *mdev, + enum mt76_rxq_id q) +{ + struct mt7915_dev *dev = container_of(mdev, struct mt7915_dev, mt76); + + mt7915_irq_enable(dev, MT_INT_RX(q)); +} + +/* TODO: support 2/4/6/8 MSI-X vectors */ +static void mt7915_irq_tasklet(struct tasklet_struct *t) +{ + struct mt7915_dev *dev = from_tasklet(dev, t, mt76.irq_tasklet); + struct mtk_wed_device *wed = &dev->mt76.mmio.wed; + u32 intr, intr1, mask; + + if (mtk_wed_device_active(wed)) { + mtk_wed_device_irq_set_mask(wed, 0); + if (dev->hif2) + mt76_wr(dev, MT_INT1_MASK_CSR, 0); + intr = mtk_wed_device_irq_get(wed, dev->mt76.mmio.irqmask); + } else { + mt76_wr(dev, MT_INT_MASK_CSR, 0); + if (dev->hif2) + mt76_wr(dev, MT_INT1_MASK_CSR, 0); + + intr = mt76_rr(dev, MT_INT_SOURCE_CSR); + intr &= dev->mt76.mmio.irqmask; + mt76_wr(dev, MT_INT_SOURCE_CSR, intr); + } + + if (dev->hif2) { + intr1 = mt76_rr(dev, MT_INT1_SOURCE_CSR); + intr1 &= dev->mt76.mmio.irqmask; + mt76_wr(dev, MT_INT1_SOURCE_CSR, intr1); + + intr |= intr1; + } + + trace_dev_irq(&dev->mt76, intr, dev->mt76.mmio.irqmask); + + mask = intr & MT_INT_RX_DONE_ALL; + if (intr & MT_INT_TX_DONE_MCU) + mask |= MT_INT_TX_DONE_MCU; + + mt7915_irq_disable(dev, mask); + + if (intr & MT_INT_TX_DONE_MCU) + napi_schedule(&dev->mt76.tx_napi); + + if (intr & MT_INT_RX(MT_RXQ_MAIN)) + napi_schedule(&dev->mt76.napi[MT_RXQ_MAIN]); + + if (intr & MT_INT_RX(MT_RXQ_BAND1)) + napi_schedule(&dev->mt76.napi[MT_RXQ_BAND1]); + + if (intr & MT_INT_RX(MT_RXQ_MCU)) + napi_schedule(&dev->mt76.napi[MT_RXQ_MCU]); + + if (intr & MT_INT_RX(MT_RXQ_MCU_WA)) + napi_schedule(&dev->mt76.napi[MT_RXQ_MCU_WA]); + + if (!is_mt7915(&dev->mt76) && + (intr & MT_INT_RX(MT_RXQ_MAIN_WA))) + napi_schedule(&dev->mt76.napi[MT_RXQ_MAIN_WA]); + + if (intr & MT_INT_RX(MT_RXQ_BAND1_WA)) + napi_schedule(&dev->mt76.napi[MT_RXQ_BAND1_WA]); + + if (intr & MT_INT_MCU_CMD) { + u32 val = mt76_rr(dev, MT_MCU_CMD); + + mt76_wr(dev, MT_MCU_CMD, val); + if (val & (MT_MCU_CMD_ERROR_MASK | MT_MCU_CMD_WDT_MASK)) { + dev->recovery.state = val; + mt7915_reset(dev); + } + } +} + +irqreturn_t mt7915_irq_handler(int irq, void *dev_instance) +{ + struct mt7915_dev *dev = dev_instance; + struct mtk_wed_device *wed = &dev->mt76.mmio.wed; + + if (mtk_wed_device_active(wed)) + mtk_wed_device_irq_set_mask(wed, 0); + else + mt76_wr(dev, MT_INT_MASK_CSR, 0); + + if (dev->hif2) + mt76_wr(dev, MT_INT1_MASK_CSR, 0); + + if (!test_bit(MT76_STATE_INITIALIZED, &dev->mphy.state)) + return IRQ_NONE; + + tasklet_schedule(&dev->mt76.irq_tasklet); + + return IRQ_HANDLED; +} + +struct mt7915_dev *mt7915_mmio_probe(struct device *pdev, + void __iomem *mem_base, u32 device_id) +{ + static const struct mt76_driver_ops drv_ops = { + /* txwi_size = txd size + txp size */ + .txwi_size = MT_TXD_SIZE + sizeof(struct mt76_connac_fw_txp), + .drv_flags = MT_DRV_TXWI_NO_FREE | MT_DRV_HW_MGMT_TXQ | + MT_DRV_AMSDU_OFFLOAD | + MT_DRV_HW_PS_BUFFERING, + .survey_flags = SURVEY_INFO_TIME_TX | + SURVEY_INFO_TIME_RX | + SURVEY_INFO_TIME_BSS_RX, + .token_size = MT7915_TOKEN_SIZE, + .tx_prepare_skb = mt7915_tx_prepare_skb, + .tx_complete_skb = mt76_connac_tx_complete_skb, + .rx_skb = mt7915_queue_rx_skb, + .rx_check = mt7915_rx_check, + .rx_poll_complete = mt7915_rx_poll_complete, + .sta_add = mt7915_mac_sta_add, + .sta_event = mt7915_mac_sta_event, + .sta_remove = mt7915_mac_sta_remove, + .update_survey = mt7915_update_channel, + .set_channel = mt7915_set_channel, + }; + struct mt7915_dev *dev; + struct mt76_dev *mdev; + int ret; + + mdev = mt76_alloc_device(pdev, sizeof(*dev), &mt7915_ops, &drv_ops); + if (!mdev) + return ERR_PTR(-ENOMEM); + + dev = container_of(mdev, struct mt7915_dev, mt76); + + ret = mt7915_mmio_init(mdev, mem_base, device_id); + if (ret) + goto error; + + tasklet_setup(&mdev->irq_tasklet, mt7915_irq_tasklet); + + return dev; + +error: + mt76_free_device(&dev->mt76); + + return ERR_PTR(ret); +} + +static int __init mt7915_init(void) +{ + int ret; + + ret = pci_register_driver(&mt7915_hif_driver); + if (ret) + return ret; + + ret = pci_register_driver(&mt7915_pci_driver); + if (ret) + goto error_pci; + + if (IS_ENABLED(CONFIG_MT798X_WMAC)) { + ret = platform_driver_register(&mt798x_wmac_driver); + if (ret) + goto error_wmac; + } + + return 0; + +error_wmac: + pci_unregister_driver(&mt7915_pci_driver); +error_pci: + pci_unregister_driver(&mt7915_hif_driver); + + return ret; +} + +static void __exit mt7915_exit(void) +{ + if (IS_ENABLED(CONFIG_MT798X_WMAC)) + platform_driver_unregister(&mt798x_wmac_driver); + + pci_unregister_driver(&mt7915_pci_driver); + pci_unregister_driver(&mt7915_hif_driver); +} + +module_init(mt7915_init); +module_exit(mt7915_exit); +MODULE_DESCRIPTION("MediaTek MT7915E MMIO helpers"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h new file mode 100644 index 000000000..30fb119ce --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h @@ -0,0 +1,645 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* Copyright (C) 2020 MediaTek Inc. */ + +#ifndef __MT7915_H +#define __MT7915_H + +#include +#include +#include "../mt76_connac.h" +#include "regs.h" + +#define MT7915_MAX_INTERFACES 19 +#define MT7915_WTBL_SIZE 288 +#define MT7916_WTBL_SIZE 544 +#define MT7915_WTBL_RESERVED (mt7915_wtbl_size(dev) - 1) +#define MT7915_WTBL_STA (MT7915_WTBL_RESERVED - \ + MT7915_MAX_INTERFACES) + +#define MT7915_WATCHDOG_TIME (HZ / 10) +#define MT7915_RESET_TIMEOUT (30 * HZ) + +#define MT7915_TX_RING_SIZE 2048 +#define MT7915_TX_MCU_RING_SIZE 256 +#define MT7915_TX_FWDL_RING_SIZE 128 + +#define MT7915_RX_RING_SIZE 1536 +#define MT7915_RX_MCU_RING_SIZE 512 + +#define MT7915_FIRMWARE_WA "mediatek/mt7915_wa.bin" +#define MT7915_FIRMWARE_WM "mediatek/mt7915_wm.bin" +#define MT7915_ROM_PATCH "mediatek/mt7915_rom_patch.bin" + +#define MT7916_FIRMWARE_WA "mediatek/mt7916_wa.bin" +#define MT7916_FIRMWARE_WM "mediatek/mt7916_wm.bin" +#define MT7916_ROM_PATCH "mediatek/mt7916_rom_patch.bin" + +#define MT7981_FIRMWARE_WA "mediatek/mt7981_wa.bin" +#define MT7981_FIRMWARE_WM "mediatek/mt7981_wm.bin" +#define MT7981_ROM_PATCH "mediatek/mt7981_rom_patch.bin" + +#define MT7986_FIRMWARE_WA "mediatek/mt7986_wa.bin" +#define MT7986_FIRMWARE_WM "mediatek/mt7986_wm.bin" +#define MT7986_FIRMWARE_WM_MT7975 "mediatek/mt7986_wm_mt7975.bin" +#define MT7986_ROM_PATCH "mediatek/mt7986_rom_patch.bin" +#define MT7986_ROM_PATCH_MT7975 "mediatek/mt7986_rom_patch_mt7975.bin" + +#define MT7915_EEPROM_DEFAULT "mediatek/mt7915_eeprom.bin" +#define MT7915_EEPROM_DEFAULT_DBDC "mediatek/mt7915_eeprom_dbdc.bin" +#define MT7916_EEPROM_DEFAULT "mediatek/mt7916_eeprom.bin" + +#define MT7981_EEPROM_MT7976_DEFAULT_DBDC "mediatek/mt7981_eeprom_mt7976_dbdc.bin" + +#define MT7986_EEPROM_MT7975_DEFAULT "mediatek/mt7986_eeprom_mt7975.bin" +#define MT7986_EEPROM_MT7975_DUAL_DEFAULT "mediatek/mt7986_eeprom_mt7975_dual.bin" +#define MT7986_EEPROM_MT7976_DEFAULT "mediatek/mt7986_eeprom_mt7976.bin" +#define MT7986_EEPROM_MT7976_DEFAULT_DBDC "mediatek/mt7986_eeprom_mt7976_dbdc.bin" +#define MT7986_EEPROM_MT7976_DUAL_DEFAULT "mediatek/mt7986_eeprom_mt7976_dual.bin" + +#define MT7915_EEPROM_SIZE 3584 +#define MT7916_EEPROM_SIZE 4096 + +#define MT7915_EEPROM_BLOCK_SIZE 16 +#define MT7915_HW_TOKEN_SIZE 4096 +#define MT7915_TOKEN_SIZE 8192 + +#define MT7915_CFEND_RATE_DEFAULT 0x49 /* OFDM 24M */ +#define MT7915_CFEND_RATE_11B 0x03 /* 11B LP, 11M */ + +#define MT7915_THERMAL_THROTTLE_MAX 100 +#define MT7915_CDEV_THROTTLE_MAX 99 + +#define MT7915_SKU_RATE_NUM 161 +#define MT7915_SKU_PATH_NUM 185 + +#define MT7915_MAX_TWT_AGRT 16 +#define MT7915_MAX_STA_TWT_AGRT 8 +#define MT7915_MIN_TWT_DUR 64 +#define MT7915_MAX_QUEUE (MT_RXQ_BAND2 + __MT_MCUQ_MAX + 2) + +#define MT7915_WED_RX_TOKEN_SIZE 12288 + +#define MT7915_CRIT_TEMP_IDX 0 +#define MT7915_MAX_TEMP_IDX 1 +#define MT7915_CRIT_TEMP 110 +#define MT7915_MAX_TEMP 120 + +#define MT7915_RTS_LEN_THRES 0x92b + +struct mt7915_vif; +struct mt7915_sta; +struct mt7915_dfs_pulse; +struct mt7915_dfs_pattern; + +enum mt7915_txq_id { + MT7915_TXQ_FWDL = 16, + MT7915_TXQ_MCU_WM, + MT7915_TXQ_BAND0, + MT7915_TXQ_BAND1, + MT7915_TXQ_MCU_WA, +}; + +enum mt7915_rxq_id { + MT7915_RXQ_BAND0 = 0, + MT7915_RXQ_BAND1, + MT7915_RXQ_MCU_WM = 0, + MT7915_RXQ_MCU_WA, + MT7915_RXQ_MCU_WA_EXT, +}; + +enum mt7916_rxq_id { + MT7916_RXQ_MCU_WM = 0, + MT7916_RXQ_MCU_WA, + MT7916_RXQ_MCU_WA_MAIN, + MT7916_RXQ_MCU_WA_EXT, + MT7916_RXQ_BAND0, + MT7916_RXQ_BAND1, +}; + +struct mt7915_twt_flow { + struct list_head list; + u64 start_tsf; + u64 tsf; + u32 duration; + u16 wcid; + __le16 mantissa; + u8 exp; + u8 table_id; + u8 id; + u8 protection:1; + u8 flowtype:1; + u8 trigger:1; + u8 sched:1; +}; + +DECLARE_EWMA(avg_signal, 10, 8) + +struct mt7915_sta { + struct mt76_wcid wcid; /* must be first */ + + struct mt7915_vif *vif; + + struct list_head rc_list; + u32 airtime_ac[8]; + + int ack_signal; + struct ewma_avg_signal avg_ack_signal; + + unsigned long changed; + unsigned long jiffies; + struct mt76_connac_sta_key_conf bip; + + struct { + u8 flowid_mask; + struct mt7915_twt_flow flow[MT7915_MAX_STA_TWT_AGRT]; + } twt; +}; + +struct mt7915_vif_cap { + bool ht_ldpc:1; + bool vht_ldpc:1; + bool he_ldpc:1; + bool vht_su_ebfer:1; + bool vht_su_ebfee:1; + bool vht_mu_ebfer:1; + bool vht_mu_ebfee:1; + bool he_su_ebfer:1; + bool he_su_ebfee:1; + bool he_mu_ebfer:1; +}; + +struct mt7915_vif { + struct mt76_vif_link mt76; /* must be first */ + + struct mt7915_vif_cap cap; + struct mt7915_sta sta; + struct mt7915_phy *phy; + + struct ieee80211_tx_queue_params queue_params[IEEE80211_NUM_ACS]; + struct cfg80211_bitrate_mask bitrate_mask; +}; + +/* crash-dump */ +struct mt7915_crash_data { + guid_t guid; + struct timespec64 timestamp; + + u8 *memdump_buf; + size_t memdump_buf_len; +}; + +struct mt7915_hif { + struct list_head list; + + struct device *dev; + void __iomem *regs; + int irq; + u32 index; +}; + +struct mt7915_phy { + struct mt76_phy *mt76; + struct mt7915_dev *dev; + + struct ieee80211_sband_iftype_data iftype[NUM_NL80211_BANDS][NUM_NL80211_IFTYPES]; + + struct ieee80211_vif *monitor_vif; + + struct thermal_zone_device *tzone; + struct thermal_cooling_device *cdev; + u8 cdev_state; + u8 throttle_state; + u32 throttle_temp[2]; /* 0: critical high, 1: maximum */ + + u32 rxfilter; + u64 omac_mask; + + u16 noise; + + s16 coverage_class; + u8 slottime; + + u32 trb_ts; + + u32 rx_ampdu_ts; + u32 ampdu_ref; + + struct mt76_mib_stats mib; + struct mt76_channel_state state_ts; + + bool sku_limit_en:1; + bool sku_path_en:1; + +#ifdef CONFIG_NL80211_TESTMODE + struct { + u32 *reg_backup; + + s32 last_freq_offset; + u8 last_rcpi[4]; + s8 last_ib_rssi[4]; + s8 last_wb_rssi[4]; + u8 last_snr; + + u8 spe_idx; + } test; +#endif +}; + +struct mt7915_dev { + union { /* must be first */ + struct mt76_dev mt76; + struct mt76_phy mphy; + }; + + struct mt7915_hif *hif2; + struct mt7915_reg_desc reg; + u8 q_id[MT7915_MAX_QUEUE]; + u32 q_int_mask[MT7915_MAX_QUEUE]; + u32 wfdma_mask; + + const struct mt76_bus_ops *bus_ops; + struct mt7915_phy phy; + + /* monitor rx chain configured channel */ + struct cfg80211_chan_def rdd2_chandef; + struct mt7915_phy *rdd2_phy; + + u16 chainmask; + u16 chainshift; + u32 hif_idx; + + struct work_struct init_work; + struct work_struct rc_work; + struct work_struct dump_work; + struct work_struct reset_work; + wait_queue_head_t reset_wait; + + struct { + u32 state; + u32 wa_reset_count; + u32 wm_reset_count; + bool hw_full_reset:1; + bool hw_init_done:1; + bool restart:1; + } recovery; + + /* protects coredump data */ + struct mutex dump_mutex; +#ifdef CONFIG_DEV_COREDUMP + struct { + struct mt7915_crash_data *crash_data; + } coredump; +#endif + + struct list_head sta_rc_list; + struct list_head twt_list; + spinlock_t reg_lock; + + u32 hw_pattern; + u32 ple1_sts; + + bool dbdc_support; + bool flash_mode; + bool muru_debug; + bool ibf; + + u8 monitor_mask; + + struct dentry *debugfs_dir; + struct rchan *relay_fwlog; + + void *cal; + u32 cur_prek_offset; + u8 dpd_chan_num_2g; + u8 dpd_chan_num_5g; + u8 dpd_chan_num_6g; + + struct { + u8 debug_wm; + u8 debug_wa; + u8 debug_bin; + } fw; + + struct { + u16 table_mask; + u8 n_agrt; + } twt; + + struct reset_control *rstc; + void __iomem *dcm; + void __iomem *sku; +}; + +enum { + WFDMA0 = 0x0, + WFDMA1, + WFDMA_EXT, + __MT_WFDMA_MAX, +}; + +enum rdd_idx { + MT_RDD_IDX_BAND0, /* RDD idx for band idx 0 (single-band) */ + MT_RDD_IDX_BAND1, /* RDD idx for band idx 1 */ + MT_RDD_IDX_BACKGROUND, /* RDD idx for background chain */ +}; + +enum mt7915_rdd_cmd { + RDD_STOP, + RDD_START, + RDD_DET_MODE, + RDD_RADAR_EMULATE, + RDD_START_TXQ = 20, + RDD_SET_WF_ANT = 30, + RDD_CAC_START = 50, + RDD_CAC_END, + RDD_NORMAL_START, + RDD_DISABLE_DFS_CAL, + RDD_PULSE_DBG, + RDD_READ_PULSE, + RDD_RESUME_BF, + RDD_IRQ_OFF, +}; + +static inline int +mt7915_get_rdd_idx(struct mt7915_phy *phy, bool is_background) +{ + if (!phy->mt76->cap.has_5ghz) + return -1; + + if (is_background) + return MT_RDD_IDX_BACKGROUND; + + return phy->mt76->band_idx; +} + +static inline struct mt7915_phy * +mt7915_hw_phy(struct ieee80211_hw *hw) +{ + struct mt76_phy *phy = hw->priv; + + return phy->priv; +} + +static inline struct mt7915_dev * +mt7915_hw_dev(struct ieee80211_hw *hw) +{ + struct mt76_phy *phy = hw->priv; + + return container_of(phy->dev, struct mt7915_dev, mt76); +} + +static inline struct mt7915_phy * +mt7915_ext_phy(struct mt7915_dev *dev) +{ + struct mt76_phy *phy = dev->mt76.phys[MT_BAND1]; + + if (!phy) + return NULL; + + return phy->priv; +} + +/* without dbdc, the chainmask is stored unshifted, even if the phy is + * bound to band 1 + */ +static inline u8 mt7915_band_chainshift(struct mt7915_phy *phy) +{ + struct mt7915_dev *dev = phy->dev; + + if (!dev->dbdc_support) + return 0; + + return phy->mt76->band_idx * dev->chainshift; +} + +static inline u16 mt7915_band_chainmask(struct mt7915_phy *phy) +{ + return phy->mt76->chainmask >> mt7915_band_chainshift(phy); +} + +static inline u32 mt7915_check_adie(struct mt7915_dev *dev, bool sku) +{ + u32 mask = sku ? MT_CONNINFRA_SKU_MASK : MT_ADIE_TYPE_MASK; + if (!is_mt798x(&dev->mt76)) + return 0; + + return mt76_rr(dev, MT_CONNINFRA_SKU_DEC_ADDR) & mask; +} + +extern const struct ieee80211_ops mt7915_ops; +extern const struct mt76_testmode_ops mt7915_testmode_ops; +extern struct pci_driver mt7915_pci_driver; +extern struct pci_driver mt7915_hif_driver; +extern struct platform_driver mt798x_wmac_driver; + +#ifdef CONFIG_MT798X_WMAC +int mt7986_wmac_enable(struct mt7915_dev *dev); +void mt7986_wmac_disable(struct mt7915_dev *dev); +#else +static inline int mt7986_wmac_enable(struct mt7915_dev *dev) +{ + return 0; +} + +static inline void mt7986_wmac_disable(struct mt7915_dev *dev) +{ +} +#endif +struct mt7915_dev *mt7915_mmio_probe(struct device *pdev, + void __iomem *mem_base, u32 device_id); +void mt7915_wfsys_reset(struct mt7915_dev *dev); +irqreturn_t mt7915_irq_handler(int irq, void *dev_instance); +u64 __mt7915_get_tsf(struct ieee80211_hw *hw, struct mt7915_vif *mvif); +u32 mt7915_wed_init_buf(void *ptr, dma_addr_t phys, int token_id); + +int mt7915_register_device(struct mt7915_dev *dev); +void mt7915_unregister_device(struct mt7915_dev *dev); +int mt7915_eeprom_init(struct mt7915_dev *dev); +void mt7915_eeprom_parse_hw_cap(struct mt7915_dev *dev, + struct mt7915_phy *phy); +int mt7915_eeprom_get_target_power(struct mt7915_dev *dev, + struct ieee80211_channel *chan, + u8 chain_idx); +s8 mt7915_eeprom_get_power_delta(struct mt7915_dev *dev, int band); +bool mt7915_eeprom_has_background_radar(struct mt7915_dev *dev); +int mt7915_dma_init(struct mt7915_dev *dev, struct mt7915_phy *phy2); +void mt7915_dma_prefetch(struct mt7915_dev *dev); +void mt7915_dma_cleanup(struct mt7915_dev *dev); +int mt7915_dma_reset(struct mt7915_dev *dev, bool force); +int mt7915_dma_start(struct mt7915_dev *dev, bool reset, bool wed_reset); +int mt7915_txbf_init(struct mt7915_dev *dev); +void mt7915_init_txpower(struct mt7915_phy *phy); +void mt7915_reset(struct mt7915_dev *dev); +int mt7915_run(struct ieee80211_hw *hw); +int mt7915_mcu_init(struct mt7915_dev *dev); +int mt7915_mcu_init_firmware(struct mt7915_dev *dev); +int mt7915_mcu_twt_agrt_update(struct mt7915_dev *dev, + struct mt7915_vif *mvif, + struct mt7915_twt_flow *flow, + int cmd); +int mt7915_mcu_add_dev_info(struct mt7915_phy *phy, + struct ieee80211_vif *vif, bool enable); +int mt7915_mcu_add_bss_info(struct mt7915_phy *phy, + struct ieee80211_vif *vif, int enable); +int mt7915_mcu_add_sta(struct mt7915_dev *dev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, int conn_state, bool newly); +int mt7915_mcu_add_tx_ba(struct mt7915_dev *dev, + struct ieee80211_ampdu_params *params, + bool add); +int mt7915_mcu_add_rx_ba(struct mt7915_dev *dev, + struct ieee80211_ampdu_params *params, + bool add); +int mt7915_mcu_update_bss_color(struct mt7915_dev *dev, struct ieee80211_vif *vif, + struct cfg80211_he_bss_color *he_bss_color); +int mt7915_mcu_add_inband_discov(struct mt7915_dev *dev, struct ieee80211_vif *vif, + u32 changed); +int mt7915_mcu_add_beacon(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + int enable, u32 changed); +int mt7915_mcu_set_protection(struct mt7915_phy *phy, struct ieee80211_vif *vif, + u8 ht_mode, bool use_cts_prot); +int mt7915_mcu_add_obss_spr(struct mt7915_phy *phy, struct ieee80211_vif *vif, + struct ieee80211_he_obss_pd *he_obss_pd); +int mt7915_mcu_add_rate_ctrl(struct mt7915_dev *dev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, bool changed); +int mt7915_mcu_add_smps(struct mt7915_dev *dev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); +int mt7915_set_channel(struct mt76_phy *mphy); +int mt7915_mcu_set_chan_info(struct mt7915_phy *phy, int cmd); +int mt7915_mcu_set_tx(struct mt7915_dev *dev, struct ieee80211_vif *vif); +int mt7915_mcu_update_edca(struct mt7915_dev *dev, void *req); +int mt7915_mcu_set_fixed_rate_ctrl(struct mt7915_dev *dev, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + void *data, u32 field); +int mt7915_mcu_set_eeprom(struct mt7915_dev *dev); +int mt7915_mcu_get_eeprom(struct mt7915_dev *dev, u32 offset, u8 *read_buf); +int mt7915_mcu_get_eeprom_free_block(struct mt7915_dev *dev, u8 *block_num); +int mt7915_mcu_set_mac(struct mt7915_dev *dev, int band, bool enable, + bool hdr_trans); +int mt7915_mcu_set_test_param(struct mt7915_dev *dev, u8 param, bool test_mode, + u8 en); +int mt7915_mcu_set_ser(struct mt7915_dev *dev, u8 action, u8 set, u8 band); +int mt7915_mcu_set_sku_en(struct mt7915_phy *phy); +int mt7915_mcu_set_txpower_sku(struct mt7915_phy *phy); +int mt7915_mcu_get_txpower_sku(struct mt7915_phy *phy, s8 *txpower, int len, + u8 category); +int mt7915_mcu_set_txpower_frame_min(struct mt7915_phy *phy, s8 txpower); +int mt7915_mcu_set_txpower_frame(struct mt7915_phy *phy, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, s8 txpower); +int mt7915_mcu_set_txbf(struct mt7915_dev *dev, u8 action); +int mt7915_mcu_set_fcc5_lpn(struct mt7915_dev *dev, int val); +int mt7915_mcu_set_pulse_th(struct mt7915_dev *dev, + const struct mt7915_dfs_pulse *pulse); +int mt7915_mcu_set_radar_th(struct mt7915_dev *dev, int index, + const struct mt7915_dfs_pattern *pattern); +int mt7915_mcu_set_muru_ctrl(struct mt7915_dev *dev, u32 cmd, u32 val); +int mt7915_mcu_apply_group_cal(struct mt7915_dev *dev); +int mt7915_mcu_apply_tx_dpd(struct mt7915_phy *phy); +int mt7915_mcu_get_chan_mib_info(struct mt7915_phy *phy, bool chan_switch); +int mt7915_mcu_get_temperature(struct mt7915_phy *phy); +int mt7915_mcu_set_thermal_throttling(struct mt7915_phy *phy, u8 state); +int mt7915_mcu_set_thermal_protect(struct mt7915_phy *phy); +int mt7915_mcu_get_rx_rate(struct mt7915_phy *phy, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, struct rate_info *rate); +int mt7915_mcu_rdd_background_enable(struct mt7915_phy *phy, + struct cfg80211_chan_def *chandef); +int mt7915_mcu_wed_wa_tx_stats(struct mt7915_dev *dev, u16 wcid); +int mt7915_mcu_rf_regval(struct mt7915_dev *dev, u32 regidx, u32 *val, bool set); +int mt7915_mcu_wa_cmd(struct mt7915_dev *dev, int cmd, u32 a1, u32 a2, u32 a3); +int mt7915_mcu_fw_log_2_host(struct mt7915_dev *dev, u8 type, u8 ctrl); +int mt7915_mcu_fw_dbg_ctrl(struct mt7915_dev *dev, u32 module, u8 level); +void mt7915_mcu_rx_event(struct mt7915_dev *dev, struct sk_buff *skb); +void mt7915_mcu_exit(struct mt7915_dev *dev); + +static inline u16 mt7915_wtbl_size(struct mt7915_dev *dev) +{ + return is_mt7915(&dev->mt76) ? MT7915_WTBL_SIZE : MT7916_WTBL_SIZE; +} + +static inline u16 mt7915_eeprom_size(struct mt7915_dev *dev) +{ + return is_mt7915(&dev->mt76) ? MT7915_EEPROM_SIZE : MT7916_EEPROM_SIZE; +} + +void mt7915_dual_hif_set_irq_mask(struct mt7915_dev *dev, bool write_reg, + u32 clear, u32 set); + +static inline void mt7915_irq_enable(struct mt7915_dev *dev, u32 mask) +{ + if (dev->hif2) + mt7915_dual_hif_set_irq_mask(dev, false, 0, mask); + else + mt76_set_irq_mask(&dev->mt76, 0, 0, mask); + + tasklet_schedule(&dev->mt76.irq_tasklet); +} + +static inline void mt7915_irq_disable(struct mt7915_dev *dev, u32 mask) +{ + if (dev->hif2) + mt7915_dual_hif_set_irq_mask(dev, true, mask, 0); + else + mt76_set_irq_mask(&dev->mt76, MT_INT_MASK_CSR, mask, 0); +} + +void mt7915_memcpy_fromio(struct mt7915_dev *dev, void *buf, u32 offset, + size_t len); + +void mt7915_mac_init(struct mt7915_dev *dev); +u32 mt7915_mac_wtbl_lmac_addr(struct mt7915_dev *dev, u16 wcid, u8 dw); +bool mt7915_mac_wtbl_update(struct mt7915_dev *dev, int idx, u32 mask); +void mt7915_mac_reset_counters(struct mt7915_phy *phy); +void mt7915_mac_cca_stats_reset(struct mt7915_phy *phy); +void mt7915_mac_enable_nf(struct mt7915_dev *dev, bool ext_phy); +void mt7915_mac_enable_rtscts(struct mt7915_dev *dev, + struct ieee80211_vif *vif, bool enable); +void mt7915_mac_write_txwi(struct mt76_dev *dev, __le32 *txwi, + struct sk_buff *skb, struct mt76_wcid *wcid, int pid, + struct ieee80211_key_conf *key, + enum mt76_txq_id qid, u32 changed); +void mt7915_mac_set_timing(struct mt7915_phy *phy); +int mt7915_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); +int mt7915_mac_sta_event(struct mt76_dev *mdev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, enum mt76_sta_event ev); +void mt7915_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); +void mt7915_mac_work(struct work_struct *work); +void mt7915_mac_reset_work(struct work_struct *work); +void mt7915_mac_dump_work(struct work_struct *work); +void mt7915_mac_sta_rc_work(struct work_struct *work); +void mt7915_mac_update_stats(struct mt7915_phy *phy); +void mt7915_mac_twt_teardown_flow(struct mt7915_dev *dev, + struct mt7915_sta *msta, + u8 flowid); +void mt7915_mac_add_twt_setup(struct ieee80211_hw *hw, + struct ieee80211_sta *sta, + struct ieee80211_twt_setup *twt); +int mt7915_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, + enum mt76_txq_id qid, struct mt76_wcid *wcid, + struct ieee80211_sta *sta, + struct mt76_tx_info *tx_info); +void mt7915_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, + struct sk_buff *skb, u32 *info); +bool mt7915_rx_check(struct mt76_dev *mdev, void *data, int len); +void mt7915_stats_work(struct work_struct *work); +int mt76_dfs_start_rdd(struct mt7915_dev *dev, bool force); +int mt7915_dfs_init_radar_detector(struct mt7915_phy *phy); +void mt7915_set_stream_he_caps(struct mt7915_phy *phy); +void mt7915_set_stream_vht_txbf_caps(struct mt7915_phy *phy); +void mt7915_update_channel(struct mt76_phy *mphy); +int mt7915_mcu_muru_debug_set(struct mt7915_dev *dev, bool enable); +int mt7915_mcu_muru_debug_get(struct mt7915_phy *phy); +int mt7915_mcu_wed_enable_rx_stats(struct mt7915_dev *dev); +int mt7915_init_debugfs(struct mt7915_phy *phy); +void mt7915_debugfs_rx_fw_monitor(struct mt7915_dev *dev, const void *data, int len); +bool mt7915_debugfs_rx_log(struct mt7915_dev *dev, const void *data, int len); +#ifdef CONFIG_MAC80211_DEBUGFS +void mt7915_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, struct dentry *dir); +#endif +int mt7915_mmio_wed_init(struct mt7915_dev *dev, void *pdev_ptr, + bool pci, int *irq); + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/pci.c b/drivers/net/wireless/mediatek/mt76/mt7915/pci.c new file mode 100644 index 000000000..8007e6200 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7915/pci.c @@ -0,0 +1,245 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2020 MediaTek Inc. + * + * Author: Ryder Lee + */ + +#include +#include +#include + +#include "mt7915.h" +#include "mac.h" +#include "../trace.h" + +static LIST_HEAD(hif_list); +static DEFINE_SPINLOCK(hif_lock); +static u32 hif_idx; + +static const struct pci_device_id mt7915_pci_device_table[] = { + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7915) }, + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7906) }, + { }, +}; + +static const struct pci_device_id mt7915_hif_device_table[] = { + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7916) }, + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x790a) }, + { }, +}; + +static struct mt7915_hif *mt7915_pci_get_hif2(u32 idx) +{ + struct mt7915_hif *hif; + u32 val; + + spin_lock_bh(&hif_lock); + + list_for_each_entry(hif, &hif_list, list) { + val = readl(hif->regs + MT_PCIE_RECOG_ID); + val &= MT_PCIE_RECOG_ID_MASK; + if (val != idx) + continue; + + get_device(hif->dev); + hif->index = idx; + goto out; + } + hif = NULL; + +out: + spin_unlock_bh(&hif_lock); + + return hif; +} + +static void mt7915_put_hif2(struct mt7915_hif *hif) +{ + if (!hif) + return; + + put_device(hif->dev); +} + +static struct mt7915_hif *mt7915_pci_init_hif2(struct pci_dev *pdev) +{ + struct pci_dev *tmp_pdev; + + hif_idx++; + + tmp_pdev = pci_get_device(PCI_VENDOR_ID_MEDIATEK, 0x7916, NULL); + if (!tmp_pdev) { + tmp_pdev = pci_get_device(PCI_VENDOR_ID_MEDIATEK, 0x790a, NULL); + if (!tmp_pdev) + return NULL; + } + pci_dev_put(tmp_pdev); + + writel(hif_idx | MT_PCIE_RECOG_ID_SEM, + pcim_iomap_table(pdev)[0] + MT_PCIE_RECOG_ID); + + return mt7915_pci_get_hif2(hif_idx); +} + +static int mt7915_pci_hif2_probe(struct pci_dev *pdev) +{ + struct mt7915_hif *hif; + + hif = devm_kzalloc(&pdev->dev, sizeof(*hif), GFP_KERNEL); + if (!hif) + return -ENOMEM; + + hif->dev = &pdev->dev; + hif->regs = pcim_iomap_table(pdev)[0]; + hif->irq = pdev->irq; + spin_lock_bh(&hif_lock); + list_add(&hif->list, &hif_list); + spin_unlock_bh(&hif_lock); + pci_set_drvdata(pdev, hif); + + return 0; +} + +static int mt7915_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct mt7915_hif *hif2 = NULL; + struct mt7915_dev *dev; + struct mt76_dev *mdev; + int irq; + int ret; + + ret = pcim_enable_device(pdev); + if (ret) + return ret; + + ret = pcim_iomap_regions(pdev, BIT(0), pci_name(pdev)); + if (ret) + return ret; + + pci_set_master(pdev); + + ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) + return ret; + + mt76_pci_disable_aspm(pdev); + + if (id->device == 0x7916 || id->device == 0x790a) + return mt7915_pci_hif2_probe(pdev); + + dev = mt7915_mmio_probe(&pdev->dev, pcim_iomap_table(pdev)[0], + id->device); + if (IS_ERR(dev)) + return PTR_ERR(dev); + + mdev = &dev->mt76; + mt7915_wfsys_reset(dev); + + ret = mt7915_mmio_wed_init(dev, pdev, true, &irq); + if (ret < 0) + goto free_wed_or_irq_vector; + + if (!ret) { + hif2 = mt7915_pci_init_hif2(pdev); + + ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES); + if (ret < 0) { + mt7915_put_hif2(hif2); + goto free_device; + } + + irq = pdev->irq; + } + + ret = devm_request_irq(mdev->dev, irq, mt7915_irq_handler, + IRQF_SHARED, KBUILD_MODNAME, dev); + if (ret) { + mt7915_put_hif2(hif2); + goto free_wed_or_irq_vector; + } + + /* master switch of PCIe tnterrupt enable */ + mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0xff); + + if (hif2) { + dev->hif2 = hif2; + + mt76_wr(dev, MT_INT1_MASK_CSR, 0); + /* master switch of PCIe tnterrupt enable */ + if (is_mt7915(mdev)) + mt76_wr(dev, MT_PCIE1_MAC_INT_ENABLE, 0xff); + else + mt76_wr(dev, MT_PCIE1_MAC_INT_ENABLE_MT7916, 0xff); + + ret = devm_request_irq(mdev->dev, dev->hif2->irq, + mt7915_irq_handler, IRQF_SHARED, + KBUILD_MODNAME "-hif", dev); + if (ret) + goto free_hif2; + } + + ret = mt7915_register_device(dev); + if (ret) + goto free_hif2_irq; + + return 0; + +free_hif2_irq: + if (dev->hif2) + devm_free_irq(mdev->dev, dev->hif2->irq, dev); +free_hif2: + if (dev->hif2) + put_device(dev->hif2->dev); + devm_free_irq(mdev->dev, irq, dev); +free_wed_or_irq_vector: + if (mtk_wed_device_active(&mdev->mmio.wed)) + mtk_wed_device_detach(&mdev->mmio.wed); + else + pci_free_irq_vectors(pdev); +free_device: + mt76_free_device(&dev->mt76); + + return ret; +} + +static void mt7915_hif_remove(struct pci_dev *pdev) +{ + struct mt7915_hif *hif = pci_get_drvdata(pdev); + + list_del(&hif->list); +} + +static void mt7915_pci_remove(struct pci_dev *pdev) +{ + struct mt76_dev *mdev; + struct mt7915_dev *dev; + + mdev = pci_get_drvdata(pdev); + dev = container_of(mdev, struct mt7915_dev, mt76); + mt7915_put_hif2(dev->hif2); + mt7915_unregister_device(dev); +} + +struct pci_driver mt7915_hif_driver = { + .name = KBUILD_MODNAME "_hif", + .id_table = mt7915_hif_device_table, + .probe = mt7915_pci_probe, + .remove = mt7915_hif_remove, +}; + +struct pci_driver mt7915_pci_driver = { + .name = KBUILD_MODNAME, + .id_table = mt7915_pci_device_table, + .probe = mt7915_pci_probe, + .remove = mt7915_pci_remove, +}; + +MODULE_DEVICE_TABLE(pci, mt7915_pci_device_table); +MODULE_DEVICE_TABLE(pci, mt7915_hif_device_table); +MODULE_FIRMWARE(MT7915_FIRMWARE_WA); +MODULE_FIRMWARE(MT7915_FIRMWARE_WM); +MODULE_FIRMWARE(MT7915_ROM_PATCH); +MODULE_FIRMWARE(MT7916_FIRMWARE_WA); +MODULE_FIRMWARE(MT7916_FIRMWARE_WM); +MODULE_FIRMWARE(MT7916_ROM_PATCH); diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/regs.h b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h new file mode 100644 index 000000000..dc7bb9a1f --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7915/regs.h @@ -0,0 +1,1230 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* Copyright (C) 2020 MediaTek Inc. */ + +#ifndef __MT7915_REGS_H +#define __MT7915_REGS_H + +/* used to differentiate between generations */ +struct mt7915_reg_desc { + const u32 *reg_rev; + const u32 *offs_rev; + const struct mt76_connac_reg_map *map; + u32 map_size; +}; + +enum reg_rev { + INT_SOURCE_CSR, + INT_MASK_CSR, + INT1_SOURCE_CSR, + INT1_MASK_CSR, + INT_MCU_CMD_SOURCE, + INT_MCU_CMD_EVENT, + WFDMA0_ADDR, + WFDMA0_PCIE1_ADDR, + WFDMA_EXT_CSR_ADDR, + CBTOP1_PHY_END, + INFRA_MCU_ADDR_END, + FW_ASSERT_STAT_ADDR, + FW_EXCEPT_TYPE_ADDR, + FW_EXCEPT_COUNT_ADDR, + FW_CIRQ_COUNT_ADDR, + FW_CIRQ_IDX_ADDR, + FW_CIRQ_LISR_ADDR, + FW_TASK_ID_ADDR, + FW_TASK_IDX_ADDR, + FW_TASK_QID1_ADDR, + FW_TASK_QID2_ADDR, + FW_TASK_START_ADDR, + FW_TASK_END_ADDR, + FW_TASK_SIZE_ADDR, + FW_LAST_MSG_ID_ADDR, + FW_EINT_INFO_ADDR, + FW_SCHED_INFO_ADDR, + SWDEF_BASE_ADDR, + TXQ_WED_RING_BASE, + RXQ_WED_RING_BASE, + RXQ_WED_DATA_RING_BASE, + __MT_REG_MAX, +}; + +enum offs_rev { + TMAC_CDTR, + TMAC_ODTR, + TMAC_ATCR, + TMAC_TRCR0, + TMAC_ICR0, + TMAC_ICR1, + TMAC_CTCR0, + TMAC_TFCR0, + MDP_BNRCFR0, + MDP_BNRCFR1, + ARB_DRNGR0, + ARB_SCR, + RMAC_MIB_AIRTIME14, + AGG_AWSCR0, + AGG_PCR0, + AGG_ACR0, + AGG_ACR4, + AGG_MRCR, + AGG_ATCR0, + AGG_ATCR1, + AGG_ATCR3, + LPON_UTTR0, + LPON_UTTR1, + LPON_FRCR, + MIB_SDR3, + MIB_SDR4, + MIB_SDR5, + MIB_SDR7, + MIB_SDR8, + MIB_SDR9, + MIB_SDR10, + MIB_SDR11, + MIB_SDR12, + MIB_SDR13, + MIB_SDR14, + MIB_SDR15, + MIB_SDR16, + MIB_SDR17, + MIB_SDR18, + MIB_SDR19, + MIB_SDR20, + MIB_SDR21, + MIB_SDR22, + MIB_SDR23, + MIB_SDR24, + MIB_SDR25, + MIB_SDR27, + MIB_SDR28, + MIB_SDR29, + MIB_SDRVEC, + MIB_SDR31, + MIB_SDR32, + MIB_SDRMUBF, + MIB_DR8, + MIB_DR9, + MIB_DR11, + MIB_MB_SDR0, + MIB_MB_SDR1, + TX_AGG_CNT, + TX_AGG_CNT2, + MIB_ARNG, + WTBLON_TOP_WDUCR, + WTBL_UPDATE, + PLE_FL_Q_EMPTY, + PLE_FL_Q_CTRL, + PLE_AC_QEMPTY, + PLE_FREEPG_CNT, + PLE_FREEPG_HEAD_TAIL, + PLE_PG_HIF_GROUP, + PLE_HIF_PG_INFO, + AC_OFFSET, + ETBF_PAR_RPT0, + __MT_OFFS_MAX, +}; + +#define __REG(id) (dev->reg.reg_rev[(id)]) +#define __OFFS(id) (dev->reg.offs_rev[(id)]) + +/* MCU WFDMA0 */ +#define MT_MCU_WFDMA0_BASE 0x2000 +#define MT_MCU_WFDMA0(ofs) (MT_MCU_WFDMA0_BASE + (ofs)) + +#define MT_MCU_WFDMA0_DUMMY_CR MT_MCU_WFDMA0(0x120) + +/* MCU WFDMA1 */ +#define MT_MCU_WFDMA1_BASE 0x3000 +#define MT_MCU_WFDMA1(ofs) (MT_MCU_WFDMA1_BASE + (ofs)) + +#define MT_MCU_INT_EVENT __REG(INT_MCU_CMD_EVENT) +#define MT_MCU_INT_EVENT_DMA_STOPPED BIT(0) +#define MT_MCU_INT_EVENT_DMA_INIT BIT(1) +#define MT_MCU_INT_EVENT_SER_TRIGGER BIT(2) +#define MT_MCU_INT_EVENT_RESET_DONE BIT(3) + +/* PLE */ +#define MT_PLE_BASE 0x820c0000 +#define MT_PLE(ofs) (MT_PLE_BASE + (ofs)) + +#define MT_PLE_HOST_RPT0 MT_PLE(0x030) +#define MT_PLE_HOST_RPT0_TX_LATENCY BIT(3) + +#define MT_FL_Q_EMPTY MT_PLE(__OFFS(PLE_FL_Q_EMPTY)) +#define MT_FL_Q0_CTRL MT_PLE(__OFFS(PLE_FL_Q_CTRL)) +#define MT_FL_Q2_CTRL MT_PLE(__OFFS(PLE_FL_Q_CTRL) + 0x8) +#define MT_FL_Q3_CTRL MT_PLE(__OFFS(PLE_FL_Q_CTRL) + 0xc) + +#define MT_PLE_FREEPG_CNT MT_PLE(__OFFS(PLE_FREEPG_CNT)) +#define MT_PLE_FREEPG_HEAD_TAIL MT_PLE(__OFFS(PLE_FREEPG_HEAD_TAIL)) +#define MT_PLE_PG_HIF_GROUP MT_PLE(__OFFS(PLE_PG_HIF_GROUP)) +#define MT_PLE_HIF_PG_INFO MT_PLE(__OFFS(PLE_HIF_PG_INFO)) + +#define MT_PLE_AC_QEMPTY(ac, n) MT_PLE(__OFFS(PLE_AC_QEMPTY) + \ + __OFFS(AC_OFFSET) * \ + (ac) + ((n) << 2)) +#define MT_PLE_AMSDU_PACK_MSDU_CNT(n) MT_PLE(0x10e0 + ((n) << 2)) + +#define MT_PSE_BASE 0x820c8000 +#define MT_PSE(ofs) (MT_PSE_BASE + (ofs)) + +/* WF MDP TOP */ +#define MT_MDP_BASE 0x820cd000 +#define MT_MDP(ofs) (MT_MDP_BASE + (ofs)) + +#define MT_MDP_DCR0 MT_MDP(0x000) +#define MT_MDP_DCR0_DAMSDU_EN BIT(15) +#define MT_MDP_DCR0_RX_HDR_TRANS_EN BIT(19) + +#define MT_MDP_DCR1 MT_MDP(0x004) +#define MT_MDP_DCR1_MAX_RX_LEN GENMASK(15, 3) + +#define MT_MDP_DCR2 MT_MDP(0x0e8) +#define MT_MDP_DCR2_RX_TRANS_SHORT BIT(2) + +#define MT_MDP_BNRCFR0(_band) MT_MDP(__OFFS(MDP_BNRCFR0) + \ + ((_band) << 8)) +#define MT_MDP_RCFR0_MCU_RX_MGMT GENMASK(5, 4) +#define MT_MDP_RCFR0_MCU_RX_CTL_NON_BAR GENMASK(7, 6) +#define MT_MDP_RCFR0_MCU_RX_CTL_BAR GENMASK(9, 8) + +#define MT_MDP_BNRCFR1(_band) MT_MDP(__OFFS(MDP_BNRCFR1) + \ + ((_band) << 8)) +#define MT_MDP_RCFR1_MCU_RX_BYPASS GENMASK(23, 22) +#define MT_MDP_RCFR1_RX_DROPPED_UCAST GENMASK(28, 27) +#define MT_MDP_RCFR1_RX_DROPPED_MCAST GENMASK(30, 29) +#define MT_MDP_TO_HIF 0 +#define MT_MDP_TO_WM 1 + +/* TRB: band 0(0x820e1000), band 1(0x820f1000) */ +#define MT_WF_TRB_BASE(_band) ((_band) ? 0x820f1000 : 0x820e1000) +#define MT_WF_TRB(_band, ofs) (MT_WF_TRB_BASE(_band) + (ofs)) + +#define MT_TRB_RXPSR0(_band) MT_WF_TRB(_band, 0x03c) +#define MT_TRB_RXPSR0_RX_WTBL_PTR GENMASK(25, 16) +#define MT_TRB_RXPSR0_RX_RMAC_PTR GENMASK(9, 0) + +/* TMAC: band 0(0x820e4000), band 1(0x820f4000) */ +#define MT_WF_TMAC_BASE(_band) ((_band) ? 0x820f4000 : 0x820e4000) +#define MT_WF_TMAC(_band, ofs) (MT_WF_TMAC_BASE(_band) + (ofs)) + +#define MT_TMAC_TCR0(_band) MT_WF_TMAC(_band, 0) +#define MT_TMAC_TCR0_TX_BLINK GENMASK(7, 6) +#define MT_TMAC_TCR0_TBTT_STOP_CTRL BIT(25) + +#define MT_TMAC_CDTR(_band) MT_WF_TMAC(_band, __OFFS(TMAC_CDTR)) + #define MT_TMAC_ODTR(_band) MT_WF_TMAC(_band, __OFFS(TMAC_ODTR)) +#define MT_TIMEOUT_VAL_PLCP GENMASK(15, 0) +#define MT_TIMEOUT_VAL_CCA GENMASK(31, 16) + +#define MT_TMAC_ATCR(_band) MT_WF_TMAC(_band, __OFFS(TMAC_ATCR)) +#define MT_TMAC_ATCR_TXV_TOUT GENMASK(7, 0) + +#define MT_TMAC_TRCR0(_band) MT_WF_TMAC(_band, __OFFS(TMAC_TRCR0)) +#define MT_TMAC_TRCR0_TR2T_CHK GENMASK(8, 0) +#define MT_TMAC_TRCR0_I2T_CHK GENMASK(24, 16) + +#define MT_TMAC_ICR0(_band) MT_WF_TMAC(_band, __OFFS(TMAC_ICR0)) +#define MT_IFS_EIFS_OFDM GENMASK(8, 0) +#define MT_IFS_RIFS GENMASK(14, 10) +#define MT_IFS_SIFS GENMASK(22, 16) +#define MT_IFS_SLOT GENMASK(30, 24) + +#define MT_TMAC_ICR1(_band) MT_WF_TMAC(_band, __OFFS(TMAC_ICR1)) +#define MT_IFS_EIFS_CCK GENMASK(8, 0) + +#define MT_TMAC_CTCR0(_band) MT_WF_TMAC(_band, __OFFS(TMAC_CTCR0)) +#define MT_TMAC_CTCR0_INS_DDLMT_REFTIME GENMASK(5, 0) +#define MT_TMAC_CTCR0_INS_DDLMT_EN BIT(17) +#define MT_TMAC_CTCR0_INS_DDLMT_VHT_SMPDU_EN BIT(18) + +#define MT_TMAC_TFCR0(_band) MT_WF_TMAC(_band, __OFFS(TMAC_TFCR0)) + +/* WF DMA TOP: band 0(0x820e7000),band 1(0x820f7000) */ +#define MT_WF_DMA_BASE(_band) ((_band) ? 0x820f7000 : 0x820e7000) +#define MT_WF_DMA(_band, ofs) (MT_WF_DMA_BASE(_band) + (ofs)) + +#define MT_DMA_DCR0(_band) MT_WF_DMA(_band, 0x000) +#define MT_DMA_DCR0_MAX_RX_LEN GENMASK(15, 3) +#define MT_DMA_DCR0_RXD_G5_EN BIT(23) + +/* WTBLOFF TOP: band 0(0x820e9000),band 1(0x820f9000) */ +#define MT_WTBLOFF_TOP_BASE(_band) ((_band) ? 0x820f9000 : 0x820e9000) +#define MT_WTBLOFF_TOP(_band, ofs) (MT_WTBLOFF_TOP_BASE(_band) + (ofs)) + +#define MT_WTBLOFF_TOP_RSCR(_band) MT_WTBLOFF_TOP(_band, 0x008) +#define MT_WTBLOFF_TOP_RSCR_RCPI_MODE GENMASK(31, 30) +#define MT_WTBLOFF_TOP_RSCR_RCPI_PARAM GENMASK(25, 24) + +#define MT_WTBLOFF_TOP_ACR(_band) MT_WTBLOFF_TOP(_band, 0x010) +#define MT_WTBLOFF_TOP_ADM_BACKOFFTIME BIT(29) + +/* ETBF: band 0(0x820ea000), band 1(0x820fa000) */ +#define MT_WF_ETBF_BASE(_band) ((_band) ? 0x820fa000 : 0x820ea000) +#define MT_WF_ETBF(_band, ofs) (MT_WF_ETBF_BASE(_band) + (ofs)) + +#define MT_ETBF_TX_NDP_BFRP(_band) MT_WF_ETBF(_band, 0x040) +#define MT_ETBF_TX_FB_CPL GENMASK(31, 16) +#define MT_ETBF_TX_FB_TRI GENMASK(15, 0) + +#define MT_ETBF_PAR_RPT0(_band) MT_WF_ETBF(_band, __OFFS(ETBF_PAR_RPT0)) +#define MT_ETBF_PAR_RPT0_FB_BW GENMASK(7, 6) +#define MT_ETBF_PAR_RPT0_FB_NC GENMASK(5, 3) +#define MT_ETBF_PAR_RPT0_FB_NR GENMASK(2, 0) + +#define MT_ETBF_TX_APP_CNT(_band) MT_WF_ETBF(_band, 0x0f0) +#define MT_ETBF_TX_IBF_CNT GENMASK(31, 16) +#define MT_ETBF_TX_EBF_CNT GENMASK(15, 0) + +#define MT_ETBF_RX_FB_CNT(_band) MT_WF_ETBF(_band, 0x0f8) +#define MT_ETBF_RX_FB_ALL GENMASK(31, 24) +#define MT_ETBF_RX_FB_HE GENMASK(23, 16) +#define MT_ETBF_RX_FB_VHT GENMASK(15, 8) +#define MT_ETBF_RX_FB_HT GENMASK(7, 0) + +/* LPON: band 0(0x820eb000), band 1(0x820fb000) */ +#define MT_WF_LPON_BASE(_band) ((_band) ? 0x820fb000 : 0x820eb000) +#define MT_WF_LPON(_band, ofs) (MT_WF_LPON_BASE(_band) + (ofs)) + +#define MT_LPON_UTTR0(_band) MT_WF_LPON(_band, __OFFS(LPON_UTTR0)) +#define MT_LPON_UTTR1(_band) MT_WF_LPON(_band, __OFFS(LPON_UTTR1)) +#define MT_LPON_FRCR(_band) MT_WF_LPON(_band, __OFFS(LPON_FRCR)) + +#define MT_LPON_TCR(_band, n) MT_WF_LPON(_band, 0x0a8 + \ + (((n) * 4) << 1)) +#define MT_LPON_TCR_MT7916(_band, n) MT_WF_LPON(_band, 0x0a8 + \ + (((n) * 4) << 4)) +#define MT_LPON_TCR_SW_MODE GENMASK(1, 0) +#define MT_LPON_TCR_SW_WRITE BIT(0) +#define MT_LPON_TCR_SW_ADJUST BIT(1) +#define MT_LPON_TCR_SW_READ GENMASK(1, 0) + +/* MIB: band 0(0x820ed000), band 1(0x820fd000) */ +/* These counters are (mostly?) clear-on-read. So, some should not + * be read at all in case firmware is already reading them. These + * are commented with 'DNR' below. The DNR stats will be read by querying + * the firmware API for the appropriate message. For counters the driver + * does read, the driver should accumulate the counters. + */ +#define MT_WF_MIB_BASE(_band) ((_band) ? 0x820fd000 : 0x820ed000) +#define MT_WF_MIB(_band, ofs) (MT_WF_MIB_BASE(_band) + (ofs)) + +#define MT_MIB_SDR0(_band) MT_WF_MIB(_band, 0x010) +#define MT_MIB_SDR0_BERACON_TX_CNT_MASK GENMASK(15, 0) + +#define MT_MIB_SDR3(_band) MT_WF_MIB(_band, __OFFS(MIB_SDR3)) +#define MT_MIB_SDR3_FCS_ERR_MASK GENMASK(15, 0) +#define MT_MIB_SDR3_FCS_ERR_MASK_MT7916 GENMASK(31, 16) + +#define MT_MIB_SDR4(_band) MT_WF_MIB(_band, __OFFS(MIB_SDR4)) +#define MT_MIB_SDR4_RX_FIFO_FULL_MASK GENMASK(15, 0) + +/* rx mpdu counter, full 32 bits */ +#define MT_MIB_SDR5(_band) MT_WF_MIB(_band, __OFFS(MIB_SDR5)) + +#define MT_MIB_SDR6(_band) MT_WF_MIB(_band, 0x020) +#define MT_MIB_SDR6_CHANNEL_IDL_CNT_MASK GENMASK(15, 0) + +#define MT_MIB_SDR7(_band) MT_WF_MIB(_band, __OFFS(MIB_SDR7)) +#define MT_MIB_SDR7_RX_VECTOR_MISMATCH_CNT_MASK GENMASK(15, 0) + +#define MT_MIB_SDR8(_band) MT_WF_MIB(_band, __OFFS(MIB_SDR8)) +#define MT_MIB_SDR8_RX_DELIMITER_FAIL_CNT_MASK GENMASK(15, 0) + +/* aka CCA_NAV_TX_TIME */ +#define MT_MIB_SDR9_DNR(_band) MT_WF_MIB(_band, __OFFS(MIB_SDR9)) +#define MT_MIB_SDR9_CCA_BUSY_TIME_MASK GENMASK(23, 0) + +#define MT_MIB_SDR10(_band) MT_WF_MIB(_band, __OFFS(MIB_SDR10)) +#define MT_MIB_SDR10_MRDY_COUNT_MASK GENMASK(25, 0) +#define MT_MIB_SDR10_MRDY_COUNT_MASK_MT7916 GENMASK(31, 0) + +#define MT_MIB_SDR11(_band) MT_WF_MIB(_band, __OFFS(MIB_SDR11)) +#define MT_MIB_SDR11_RX_LEN_MISMATCH_CNT_MASK GENMASK(15, 0) + +/* tx ampdu cnt, full 32 bits */ +#define MT_MIB_SDR12(_band) MT_WF_MIB(_band, __OFFS(MIB_SDR12)) + +#define MT_MIB_SDR13(_band) MT_WF_MIB(_band, __OFFS(MIB_SDR13)) +#define MT_MIB_SDR13_TX_STOP_Q_EMPTY_CNT_MASK GENMASK(15, 0) + +/* counts all mpdus in ampdu, regardless of success */ +#define MT_MIB_SDR14(_band) MT_WF_MIB(_band, __OFFS(MIB_SDR14)) +#define MT_MIB_SDR14_TX_MPDU_ATTEMPTS_CNT_MASK GENMASK(23, 0) +#define MT_MIB_SDR14_TX_MPDU_ATTEMPTS_CNT_MASK_MT7916 GENMASK(31, 0) + +/* counts all successfully tx'd mpdus in ampdu */ +#define MT_MIB_SDR15(_band) MT_WF_MIB(_band, __OFFS(MIB_SDR15)) +#define MT_MIB_SDR15_TX_MPDU_SUCCESS_CNT_MASK GENMASK(23, 0) +#define MT_MIB_SDR15_TX_MPDU_SUCCESS_CNT_MASK_MT7916 GENMASK(31, 0) + +/* in units of 'us' */ +#define MT_MIB_SDR16(_band) MT_WF_MIB(_band, __OFFS(MIB_SDR16)) +#define MT_MIB_SDR16_PRIMARY_CCA_BUSY_TIME_MASK GENMASK(23, 0) + +#define MT_MIB_SDR17(_band) MT_WF_MIB(_band, __OFFS(MIB_SDR17)) +#define MT_MIB_SDR17_SECONDARY_CCA_BUSY_TIME_MASK GENMASK(23, 0) + +#define MT_MIB_SDR18(_band) MT_WF_MIB(_band, __OFFS(MIB_SDR18)) +#define MT_MIB_SDR18_PRIMARY_ENERGY_DETECT_TIME_MASK GENMASK(23, 0) + +/* units are us */ +#define MT_MIB_SDR19(_band) MT_WF_MIB(_band, __OFFS(MIB_SDR19)) +#define MT_MIB_SDR19_CCK_MDRDY_TIME_MASK GENMASK(23, 0) + +#define MT_MIB_SDR20(_band) MT_WF_MIB(_band, __OFFS(MIB_SDR20)) +#define MT_MIB_SDR20_OFDM_VHT_MDRDY_TIME_MASK GENMASK(23, 0) + +#define MT_MIB_SDR21(_band) MT_WF_MIB(_band, __OFFS(MIB_SDR21)) +#define MT_MIB_SDR21_GREEN_MDRDY_TIME_MASK GENMASK(23, 0) + +/* rx ampdu count, 32-bit */ +#define MT_MIB_SDR22(_band) MT_WF_MIB(_band, __OFFS(MIB_SDR22)) + +/* rx ampdu bytes count, 32-bit */ +#define MT_MIB_SDR23(_band) MT_WF_MIB(_band, __OFFS(MIB_SDR23)) + +/* rx ampdu valid subframe count */ +#define MT_MIB_SDR24(_band) MT_WF_MIB(_band, __OFFS(MIB_SDR24)) +#define MT_MIB_SDR24_RX_AMPDU_SF_CNT_MASK GENMASK(23, 0) +#define MT_MIB_SDR24_RX_AMPDU_SF_CNT_MASK_MT7916 GENMASK(31, 0) + +/* rx ampdu valid subframe bytes count, 32bits */ +#define MT_MIB_SDR25(_band) MT_WF_MIB(_band, __OFFS(MIB_SDR25)) + +/* remaining windows protected stats */ +#define MT_MIB_SDR27(_band) MT_WF_MIB(_band, __OFFS(MIB_SDR27)) +#define MT_MIB_SDR27_TX_RWP_FAIL_CNT_MASK GENMASK(15, 0) + +#define MT_MIB_SDR28(_band) MT_WF_MIB(_band, __OFFS(MIB_SDR28)) +#define MT_MIB_SDR28_TX_RWP_NEED_CNT_MASK GENMASK(15, 0) + +#define MT_MIB_SDR29(_band) MT_WF_MIB(_band, __OFFS(MIB_SDR29)) +#define MT_MIB_SDR29_RX_PFDROP_CNT_MASK GENMASK(7, 0) +#define MT_MIB_SDR29_RX_PFDROP_CNT_MASK_MT7916 GENMASK(15, 0) + +#define MT_MIB_SDRVEC(_band) MT_WF_MIB(_band, __OFFS(MIB_SDRVEC)) +#define MT_MIB_SDR30_RX_VEC_QUEUE_OVERFLOW_DROP_CNT_MASK GENMASK(15, 0) +#define MT_MIB_SDR30_RX_VEC_QUEUE_OVERFLOW_DROP_CNT_MASK_MT7916 GENMASK(31, 16) + +/* rx blockack count, 32 bits */ +#define MT_MIB_SDR31(_band) MT_WF_MIB(_band, __OFFS(MIB_SDR31)) + +#define MT_MIB_SDR32(_band) MT_WF_MIB(_band, __OFFS(MIB_SDR32)) +#define MT_MIB_SDR32_TX_PKT_EBF_CNT GENMASK(15, 0) +#define MT_MIB_SDR32_TX_PKT_IBF_CNT GENMASK(31, 16) + +#define MT_MIB_SDR33(_band) MT_WF_MIB(_band, 0x088) +#define MT_MIB_SDR33_TX_PKT_IBF_CNT GENMASK(15, 0) + +#define MT_MIB_SDRMUBF(_band) MT_WF_MIB(_band, __OFFS(MIB_SDRMUBF)) +#define MT_MIB_MU_BF_TX_CNT GENMASK(15, 0) + +/* 36, 37 both DNR */ + +#define MT_MIB_DR8(_band) MT_WF_MIB(_band, __OFFS(MIB_DR8)) +#define MT_MIB_DR9(_band) MT_WF_MIB(_band, __OFFS(MIB_DR9)) +#define MT_MIB_DR11(_band) MT_WF_MIB(_band, __OFFS(MIB_DR11)) + +#define MT_MIB_MB_SDR0(_band, n) MT_WF_MIB(_band, __OFFS(MIB_MB_SDR0) + (n)) +#define MT_MIB_RTS_RETRIES_COUNT_MASK GENMASK(31, 16) +#define MT_MIB_RTS_COUNT_MASK GENMASK(15, 0) + +#define MT_MIB_MB_SDR1(_band, n) MT_WF_MIB(_band, __OFFS(MIB_MB_SDR1) + (n)) +#define MT_MIB_BA_MISS_COUNT_MASK GENMASK(15, 0) +#define MT_MIB_ACK_FAIL_COUNT_MASK GENMASK(31, 16) + +#define MT_MIB_MB_SDR2(_band, n) MT_WF_MIB(_band, 0x518 + (n)) +#define MT_MIB_MB_BFTF(_band, n) MT_WF_MIB(_band, 0x510 + (n)) + +#define MT_TX_AGG_CNT(_band, n) MT_WF_MIB(_band, __OFFS(TX_AGG_CNT) + \ + ((n) << 2)) +#define MT_TX_AGG_CNT2(_band, n) MT_WF_MIB(_band, __OFFS(TX_AGG_CNT2) + \ + ((n) << 2)) +#define MT_MIB_ARNG(_band, n) MT_WF_MIB(_band, __OFFS(MIB_ARNG) + \ + ((n) << 2)) +#define MT_MIB_ARNCR_RANGE(val, n) (((val) >> ((n) << 3)) & GENMASK(7, 0)) + +#define MT_MIB_BFCR0(_band) MT_WF_MIB(_band, 0x7b0) +#define MT_MIB_BFCR0_RX_FB_HT GENMASK(15, 0) +#define MT_MIB_BFCR0_RX_FB_VHT GENMASK(31, 16) + +#define MT_MIB_BFCR1(_band) MT_WF_MIB(_band, 0x7b4) +#define MT_MIB_BFCR1_RX_FB_HE GENMASK(15, 0) + +#define MT_MIB_BFCR2(_band) MT_WF_MIB(_band, 0x7b8) +#define MT_MIB_BFCR2_BFEE_TX_FB_TRIG GENMASK(15, 0) + +#define MT_MIB_BFCR7(_band) MT_WF_MIB(_band, 0x7cc) +#define MT_MIB_BFCR7_BFEE_TX_FB_CPL GENMASK(15, 0) + +/* WTBLON TOP */ +#define MT_WTBLON_TOP_BASE 0x820d4000 +#define MT_WTBLON_TOP(ofs) (MT_WTBLON_TOP_BASE + (ofs)) +#define MT_WTBLON_TOP_WDUCR MT_WTBLON_TOP(__OFFS(WTBLON_TOP_WDUCR)) +#define MT_WTBLON_TOP_WDUCR_GROUP GENMASK(2, 0) + +#define MT_WTBL_UPDATE MT_WTBLON_TOP(__OFFS(WTBL_UPDATE)) +#define MT_WTBL_UPDATE_WLAN_IDX GENMASK(9, 0) +#define MT_WTBL_UPDATE_ADM_COUNT_CLEAR BIT(12) +#define MT_WTBL_UPDATE_BUSY BIT(31) + +/* WTBL */ +#define MT_WTBL_BASE 0x820d8000 +#define MT_WTBL_LMAC_ID GENMASK(14, 8) +#define MT_WTBL_LMAC_DW GENMASK(7, 2) +#define MT_WTBL_LMAC_OFFS(_id, _dw) (MT_WTBL_BASE | \ + FIELD_PREP(MT_WTBL_LMAC_ID, _id) | \ + FIELD_PREP(MT_WTBL_LMAC_DW, _dw)) + +/* AGG: band 0(0x820e2000), band 1(0x820f2000) */ +#define MT_WF_AGG_BASE(_band) ((_band) ? 0x820f2000 : 0x820e2000) +#define MT_WF_AGG(_band, ofs) (MT_WF_AGG_BASE(_band) + (ofs)) + +#define MT_AGG_AWSCR0(_band, _n) MT_WF_AGG(_band, (__OFFS(AGG_AWSCR0) + \ + (_n) * 4)) +#define MT_AGG_PCR0(_band, _n) MT_WF_AGG(_band, (__OFFS(AGG_PCR0) + \ + (_n) * 4)) +#define MT_AGG_PCR0_MM_PROT BIT(0) +#define MT_AGG_PCR0_GF_PROT BIT(1) +#define MT_AGG_PCR0_BW20_PROT BIT(2) +#define MT_AGG_PCR0_BW40_PROT BIT(4) +#define MT_AGG_PCR0_BW80_PROT BIT(6) +#define MT_AGG_PCR0_ERP_PROT GENMASK(12, 8) +#define MT_AGG_PCR0_VHT_PROT BIT(13) +#define MT_AGG_PCR0_PTA_WIN_DIS BIT(15) + +#define MT_AGG_PCR1_RTS0_NUM_THRES GENMASK(31, 23) +#define MT_AGG_PCR1_RTS0_LEN_THRES GENMASK(19, 0) + +#define MT_AGG_ACR0(_band) MT_WF_AGG(_band, __OFFS(AGG_ACR0)) +#define MT_AGG_ACR_CFEND_RATE GENMASK(13, 0) +#define MT_AGG_ACR_BAR_RATE GENMASK(29, 16) + +#define MT_AGG_ACR4(_band) MT_WF_AGG(_band, __OFFS(AGG_ACR4)) +#define MT_AGG_ACR_PPDU_TXS2H BIT(1) + +#define MT_AGG_MRCR(_band) MT_WF_AGG(_band, __OFFS(AGG_MRCR)) +#define MT_AGG_MRCR_BAR_CNT_LIMIT GENMASK(15, 12) +#define MT_AGG_MRCR_LAST_RTS_CTS_RN BIT(6) +#define MT_AGG_MRCR_RTS_FAIL_LIMIT GENMASK(11, 7) +#define MT_AGG_MRCR_TXCMD_RTS_FAIL_LIMIT GENMASK(28, 24) + +#define MT_AGG_ATCR0(_band) MT_WF_AGG(_band, __OFFS(AGG_ATCR0)) +#define MT_AGG_ATCR_MAC_BFF_TIME_EN BIT(30) + +#define MT_AGG_ATCR1(_band) MT_WF_AGG(_band, __OFFS(AGG_ATCR1)) +#define MT_AGG_ATCR3(_band) MT_WF_AGG(_band, __OFFS(AGG_ATCR3)) + +/* ARB: band 0(0x820e3000), band 1(0x820f3000) */ +#define MT_WF_ARB_BASE(_band) ((_band) ? 0x820f3000 : 0x820e3000) +#define MT_WF_ARB(_band, ofs) (MT_WF_ARB_BASE(_band) + (ofs)) + +#define MT_ARB_SCR(_band) MT_WF_ARB(_band, __OFFS(ARB_SCR)) +#define MT_ARB_SCR_TX_DISABLE BIT(8) +#define MT_ARB_SCR_RX_DISABLE BIT(9) + +#define MT_ARB_DRNGR0(_band, _n) MT_WF_ARB(_band, (__OFFS(ARB_DRNGR0) + \ + (_n) * 4)) + +/* RMAC: band 0(0x820e5000), band 1(0x820f5000) */ +#define MT_WF_RMAC_BASE(_band) ((_band) ? 0x820f5000 : 0x820e5000) +#define MT_WF_RMAC(_band, ofs) (MT_WF_RMAC_BASE(_band) + (ofs)) + +#define MT_WF_RFCR(_band) MT_WF_RMAC(_band, 0x000) +#define MT_WF_RFCR_DROP_STBC_MULTI BIT(0) +#define MT_WF_RFCR_DROP_FCSFAIL BIT(1) +#define MT_WF_RFCR_DROP_VERSION BIT(3) +#define MT_WF_RFCR_DROP_PROBEREQ BIT(4) +#define MT_WF_RFCR_DROP_MCAST BIT(5) +#define MT_WF_RFCR_DROP_BCAST BIT(6) +#define MT_WF_RFCR_DROP_MCAST_FILTERED BIT(7) +#define MT_WF_RFCR_DROP_A3_MAC BIT(8) +#define MT_WF_RFCR_DROP_A3_BSSID BIT(9) +#define MT_WF_RFCR_DROP_A2_BSSID BIT(10) +#define MT_WF_RFCR_DROP_OTHER_BEACON BIT(11) +#define MT_WF_RFCR_DROP_FRAME_REPORT BIT(12) +#define MT_WF_RFCR_DROP_CTL_RSV BIT(13) +#define MT_WF_RFCR_DROP_CTS BIT(14) +#define MT_WF_RFCR_DROP_RTS BIT(15) +#define MT_WF_RFCR_DROP_DUPLICATE BIT(16) +#define MT_WF_RFCR_DROP_OTHER_BSS BIT(17) +#define MT_WF_RFCR_DROP_OTHER_UC BIT(18) +#define MT_WF_RFCR_DROP_OTHER_TIM BIT(19) +#define MT_WF_RFCR_DROP_NDPA BIT(20) +#define MT_WF_RFCR_DROP_UNWANTED_CTL BIT(21) + +#define MT_WF_RFCR1(_band) MT_WF_RMAC(_band, 0x004) +#define MT_WF_RFCR1_DROP_ACK BIT(4) +#define MT_WF_RFCR1_DROP_BF_POLL BIT(5) +#define MT_WF_RFCR1_DROP_BA BIT(6) +#define MT_WF_RFCR1_DROP_CFEND BIT(7) +#define MT_WF_RFCR1_DROP_CFACK BIT(8) + +#define MT_WF_RMAC_RSVD0(_band) MT_WF_RMAC(_band, 0x02e0) +#define MT_WF_RMAC_RSVD0_EIFS_CLR BIT(21) + +#define MT_WF_RMAC_MIB_AIRTIME0(_band) MT_WF_RMAC(_band, 0x0380) +#define MT_WF_RMAC_MIB_RXTIME_CLR BIT(31) +#define MT_WF_RMAC_MIB_OBSS_BACKOFF GENMASK(15, 0) +#define MT_WF_RMAC_MIB_ED_OFFSET GENMASK(20, 16) + +#define MT_WF_RMAC_MIB_AIRTIME1(_band) MT_WF_RMAC(_band, 0x0384) +#define MT_WF_RMAC_MIB_NONQOSD_BACKOFF GENMASK(31, 16) + +#define MT_WF_RMAC_MIB_AIRTIME3(_band) MT_WF_RMAC(_band, 0x038c) +#define MT_WF_RMAC_MIB_QOS01_BACKOFF GENMASK(31, 0) + +#define MT_WF_RMAC_MIB_AIRTIME4(_band) MT_WF_RMAC(_band, 0x0390) +#define MT_WF_RMAC_MIB_QOS23_BACKOFF GENMASK(31, 0) + +/* WFDMA0 */ +#define MT_WFDMA0_BASE __REG(WFDMA0_ADDR) +#define MT_WFDMA0(ofs) (MT_WFDMA0_BASE + (ofs)) + +#define MT_WFDMA0_RST MT_WFDMA0(0x100) +#define MT_WFDMA0_RST_LOGIC_RST BIT(4) +#define MT_WFDMA0_RST_DMASHDL_ALL_RST BIT(5) + +#define MT_WFDMA0_BUSY_ENA MT_WFDMA0(0x13c) +#define MT_WFDMA0_BUSY_ENA_TX_FIFO0 BIT(0) +#define MT_WFDMA0_BUSY_ENA_TX_FIFO1 BIT(1) +#define MT_WFDMA0_BUSY_ENA_RX_FIFO BIT(2) + +#define MT_WFDMA0_MCU_HOST_INT_ENA MT_WFDMA0(0x1f4) + +#define MT_WFDMA0_GLO_CFG MT_WFDMA0(0x208) +#define MT_WFDMA0_GLO_CFG_TX_DMA_EN BIT(0) +#define MT_WFDMA0_GLO_CFG_RX_DMA_EN BIT(2) +#define MT_WFDMA0_GLO_CFG_OMIT_TX_INFO BIT(28) +#define MT_WFDMA0_GLO_CFG_OMIT_RX_INFO BIT(27) +#define MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2 BIT(21) + +#define MT_WFDMA0_RST_DTX_PTR MT_WFDMA0(0x20c) + +#define MT_WFDMA0_EXT0_CFG MT_WFDMA0(0x2b0) +#define MT_WFDMA0_EXT0_RXWB_KEEP BIT(10) + +#define MT_WFDMA0_PRI_DLY_INT_CFG0 MT_WFDMA0(0x2f0) +#define MT_WFDMA0_PRI_DLY_INT_CFG1 MT_WFDMA0(0x2f4) +#define MT_WFDMA0_PRI_DLY_INT_CFG2 MT_WFDMA0(0x2f8) +#define MT_WPDMA_GLO_CFG MT_WFDMA0(0x208) + +/* WFDMA1 */ +#define MT_WFDMA1_BASE 0xd5000 +#define MT_WFDMA1(ofs) (MT_WFDMA1_BASE + (ofs)) + +#define MT_WFDMA1_RST MT_WFDMA1(0x100) +#define MT_WFDMA1_RST_LOGIC_RST BIT(4) +#define MT_WFDMA1_RST_DMASHDL_ALL_RST BIT(5) + +#define MT_WFDMA1_BUSY_ENA MT_WFDMA1(0x13c) +#define MT_WFDMA1_BUSY_ENA_TX_FIFO0 BIT(0) +#define MT_WFDMA1_BUSY_ENA_TX_FIFO1 BIT(1) +#define MT_WFDMA1_BUSY_ENA_RX_FIFO BIT(2) + +#define MT_WFDMA1_GLO_CFG MT_WFDMA1(0x208) +#define MT_WFDMA1_GLO_CFG_TX_DMA_EN BIT(0) +#define MT_WFDMA1_GLO_CFG_RX_DMA_EN BIT(2) +#define MT_WFDMA1_GLO_CFG_OMIT_TX_INFO BIT(28) +#define MT_WFDMA1_GLO_CFG_OMIT_RX_INFO BIT(27) +#define MT_WFDMA1_GLO_CFG_OMIT_RX_INFO_PFET2 BIT(21) + +#define MT_WFDMA1_RST_DTX_PTR MT_WFDMA1(0x20c) +#define MT_WFDMA1_PRI_DLY_INT_CFG0 MT_WFDMA1(0x2f0) + +/* WFDMA CSR */ +#define MT_WFDMA_EXT_CSR_BASE __REG(WFDMA_EXT_CSR_ADDR) +#define MT_WFDMA_EXT_CSR_PHYS_BASE 0x18027000 +#define MT_WFDMA_EXT_CSR(ofs) (MT_WFDMA_EXT_CSR_BASE + (ofs)) +#define MT_WFDMA_EXT_CSR_PHYS(ofs) (MT_WFDMA_EXT_CSR_PHYS_BASE + (ofs)) + +#define MT_WFDMA_HOST_CONFIG MT_WFDMA_EXT_CSR_PHYS(0x30) +#define MT_WFDMA_HOST_CONFIG_PDMA_BAND BIT(0) +#define MT_WFDMA_HOST_CONFIG_WED BIT(1) + +#define MT_WFDMA_WED_RING_CONTROL MT_WFDMA_EXT_CSR_PHYS(0x34) +#define MT_WFDMA_WED_RING_CONTROL_TX0 GENMASK(4, 0) +#define MT_WFDMA_WED_RING_CONTROL_TX1 GENMASK(12, 8) +#define MT_WFDMA_WED_RING_CONTROL_RX1 GENMASK(20, 16) + +#define MT_WFDMA_EXT_CSR_HIF_MISC MT_WFDMA_EXT_CSR_PHYS(0x44) +#define MT_WFDMA_EXT_CSR_HIF_MISC_BUSY BIT(0) + +#define MT_PCIE_RECOG_ID 0xd7090 +#define MT_PCIE_RECOG_ID_MASK GENMASK(30, 0) +#define MT_PCIE_RECOG_ID_SEM BIT(31) + +#define MT_INT_WED_SOURCE_CSR MT_WFDMA_EXT_CSR(0x200) +#define MT_INT_WED_MASK_CSR MT_WFDMA_EXT_CSR(0x204) + +#define MT_WED_TX_RING_BASE MT_WFDMA_EXT_CSR(0x300) +#define MT_WED_RX_RING_BASE MT_WFDMA_EXT_CSR(0x400) + +/* WFDMA0 PCIE1 */ +#define MT_WFDMA0_PCIE1_BASE __REG(WFDMA0_PCIE1_ADDR) +#define MT_WFDMA0_PCIE1(ofs) (MT_WFDMA0_PCIE1_BASE + (ofs)) + +#define MT_WFDMA0_PCIE1_BUSY_ENA MT_WFDMA0_PCIE1(0x13c) +#define MT_WFDMA0_PCIE1_BUSY_ENA_TX_FIFO0 BIT(0) +#define MT_WFDMA0_PCIE1_BUSY_ENA_TX_FIFO1 BIT(1) +#define MT_WFDMA0_PCIE1_BUSY_ENA_RX_FIFO BIT(2) + +/* WFDMA1 PCIE1 */ +#define MT_WFDMA1_PCIE1_BASE 0xd9000 +#define MT_WFDMA1_PCIE1(ofs) (MT_WFDMA1_PCIE1_BASE + (ofs)) + +#define MT_WFDMA1_PCIE1_BUSY_ENA MT_WFDMA1_PCIE1(0x13c) +#define MT_WFDMA1_PCIE1_BUSY_ENA_TX_FIFO0 BIT(0) +#define MT_WFDMA1_PCIE1_BUSY_ENA_TX_FIFO1 BIT(1) +#define MT_WFDMA1_PCIE1_BUSY_ENA_RX_FIFO BIT(2) + +/* WFDMA COMMON */ +#define __RXQ(q) ((q) + __MT_MCUQ_MAX) +#define __TXQ(q) (__RXQ(q) + MT_RXQ_BAND2) + +#define MT_Q_ID(q) (dev->q_id[(q)]) +#define MT_Q_BASE(q) ((dev->wfdma_mask >> (q)) & 0x1 ? \ + MT_WFDMA1_BASE : MT_WFDMA0_BASE) + +#define MT_MCUQ_ID(q) MT_Q_ID(q) +#define MT_TXQ_ID(q) MT_Q_ID(__TXQ(q)) +#define MT_RXQ_ID(q) MT_Q_ID(__RXQ(q)) + +#define MT_MCUQ_RING_BASE(q) (MT_Q_BASE(q) + 0x300) +#define MT_TXQ_RING_BASE(q) (MT_Q_BASE(__TXQ(q)) + 0x300) +#define MT_RXQ_RING_BASE(q) (MT_Q_BASE(__RXQ(q)) + 0x500) + +#define MT_MCUQ_EXT_CTRL(q) (MT_Q_BASE(q) + 0x600 + \ + MT_MCUQ_ID(q)* 0x4) +#define MT_RXQ_BAND1_CTRL(q) (MT_Q_BASE(__RXQ(q)) + 0x680 + \ + MT_RXQ_ID(q)* 0x4) +#define MT_TXQ_EXT_CTRL(q) (MT_Q_BASE(__TXQ(q)) + 0x600 + \ + MT_TXQ_ID(q)* 0x4) + +#define MT_TXQ_WED_RING_BASE __REG(TXQ_WED_RING_BASE) +#define MT_RXQ_WED_RING_BASE __REG(RXQ_WED_RING_BASE) +#define MT_RXQ_WED_DATA_RING_BASE __REG(RXQ_WED_DATA_RING_BASE) + +#define MT_INT_SOURCE_CSR __REG(INT_SOURCE_CSR) +#define MT_INT_MASK_CSR __REG(INT_MASK_CSR) + +#define MT_INT1_SOURCE_CSR __REG(INT1_SOURCE_CSR) +#define MT_INT1_MASK_CSR __REG(INT1_MASK_CSR) + +#define MT_INT_RX_DONE_BAND0 BIT(16) +#define MT_INT_RX_DONE_BAND1 BIT(17) +#define MT_INT_RX_DONE_WM BIT(0) +#define MT_INT_RX_DONE_WA BIT(1) +#define MT_INT_RX_DONE_WA_MAIN BIT(1) +#define MT_INT_RX_DONE_WA_EXT BIT(2) +#define MT_INT_MCU_CMD BIT(29) +#define MT_INT_RX_DONE_BAND0_MT7916 BIT(22) +#define MT_INT_RX_DONE_BAND1_MT7916 BIT(23) +#define MT_INT_RX_DONE_WA_MAIN_MT7916 BIT(2) +#define MT_INT_RX_DONE_WA_EXT_MT7916 BIT(3) + +#define MT_INT_WED_RX_DONE_BAND0_MT7916 BIT(18) +#define MT_INT_WED_RX_DONE_BAND1_MT7916 BIT(19) +#define MT_INT_WED_RX_DONE_WA_MAIN_MT7916 BIT(1) +#define MT_INT_WED_RX_DONE_WA_MT7916 BIT(17) + +#define MT_INT_RX(q) (dev->q_int_mask[__RXQ(q)]) +#define MT_INT_TX_MCU(q) (dev->q_int_mask[(q)]) + +#define MT_INT_RX_DONE_MCU (MT_INT_RX(MT_RXQ_MCU) | \ + MT_INT_RX(MT_RXQ_MCU_WA)) + +#define MT_INT_BAND0_RX_DONE (MT_INT_RX(MT_RXQ_MAIN) | \ + MT_INT_RX(MT_RXQ_MAIN_WA)) + +#define MT_INT_BAND1_RX_DONE (MT_INT_RX(MT_RXQ_BAND1) | \ + MT_INT_RX(MT_RXQ_BAND1_WA) | \ + MT_INT_RX(MT_RXQ_MAIN_WA)) + +#define MT_INT_RX_DONE_ALL (MT_INT_RX_DONE_MCU | \ + MT_INT_BAND0_RX_DONE | \ + MT_INT_BAND1_RX_DONE) + +#define MT_INT_TX_DONE_FWDL BIT(26) +#define MT_INT_TX_DONE_MCU_WM BIT(27) +#define MT_INT_TX_DONE_MCU_WA BIT(15) +#define MT_INT_TX_DONE_BAND0 BIT(30) +#define MT_INT_TX_DONE_BAND1 BIT(31) +#define MT_INT_TX_DONE_MCU_WA_MT7916 BIT(25) +#define MT_INT_WED_TX_DONE_BAND0 BIT(4) +#define MT_INT_WED_TX_DONE_BAND1 BIT(5) + +#define MT_INT_TX_DONE_MCU (MT_INT_TX_MCU(MT_MCUQ_WA) | \ + MT_INT_TX_MCU(MT_MCUQ_WM) | \ + MT_INT_TX_MCU(MT_MCUQ_FWDL)) + +#define MT_MCU_CMD __REG(INT_MCU_CMD_SOURCE) +#define MT_MCU_CMD_STOP_DMA_FW_RELOAD BIT(1) +#define MT_MCU_CMD_STOP_DMA BIT(2) +#define MT_MCU_CMD_RESET_DONE BIT(3) +#define MT_MCU_CMD_RECOVERY_DONE BIT(4) +#define MT_MCU_CMD_NORMAL_STATE BIT(5) +#define MT_MCU_CMD_ERROR_MASK GENMASK(5, 1) + +#define MT_MCU_CMD_WA_WDT BIT(31) +#define MT_MCU_CMD_WM_WDT BIT(30) +#define MT_MCU_CMD_WDT_MASK GENMASK(31, 30) + +/* TOP RGU */ +#define MT_TOP_RGU_BASE 0x18000000 +#define MT_TOP_PWR_CTRL (MT_TOP_RGU_BASE + (0x0)) +#define MT_TOP_PWR_KEY (0x5746 << 16) +#define MT_TOP_PWR_SW_RST BIT(0) +#define MT_TOP_PWR_SW_PWR_ON GENMASK(3, 2) +#define MT_TOP_PWR_HW_CTRL BIT(4) +#define MT_TOP_PWR_PWR_ON BIT(7) + +#define MT_TOP_RGU_SYSRAM_PDN (MT_TOP_RGU_BASE + 0x050) +#define MT_TOP_RGU_SYSRAM_SLP (MT_TOP_RGU_BASE + 0x054) +#define MT_TOP_WFSYS_PWR (MT_TOP_RGU_BASE + 0x010) +#define MT_TOP_PWR_EN_MASK BIT(7) +#define MT_TOP_PWR_ACK_MASK BIT(6) +#define MT_TOP_PWR_KEY_MASK GENMASK(31, 16) + +#define MT7986_TOP_WM_RESET (MT_TOP_RGU_BASE + 0x120) +#define MT7986_TOP_WM_RESET_MASK BIT(0) + +/* l1/l2 remap */ +#define MT_HIF_REMAP_L1 0xf11ac +#define MT_HIF_REMAP_L1_MT7916 0xfe260 +#define MT_HIF_REMAP_L1_MASK GENMASK(15, 0) +#define MT_HIF_REMAP_L1_OFFSET GENMASK(15, 0) +#define MT_HIF_REMAP_L1_BASE GENMASK(31, 16) +#define MT_HIF_REMAP_BASE_L1 0xe0000 + +#define MT_HIF_REMAP_L2 0xf11b0 +#define MT_HIF_REMAP_L2_MASK GENMASK(19, 0) +#define MT_HIF_REMAP_L2_OFFSET GENMASK(11, 0) +#define MT_HIF_REMAP_L2_BASE GENMASK(31, 12) +#define MT_HIF_REMAP_L2_MT7916 0x1b8 +#define MT_HIF_REMAP_L2_MASK_MT7916 GENMASK(31, 16) +#define MT_HIF_REMAP_L2_OFFSET_MT7916 GENMASK(15, 0) +#define MT_HIF_REMAP_L2_BASE_MT7916 GENMASK(31, 16) +#define MT_HIF_REMAP_BASE_L2_MT7916 0x40000 + +#define MT_INFRA_BASE 0x18000000 +#define MT_WFSYS0_PHY_START 0x18400000 +#define MT_WFSYS1_PHY_START 0x18800000 +#define MT_WFSYS1_PHY_END 0x18bfffff +#define MT_CBTOP1_PHY_START 0x70000000 +#define MT_CBTOP1_PHY_END __REG(CBTOP1_PHY_END) +#define MT_CBTOP2_PHY_START 0xf0000000 +#define MT_INFRA_MCU_START 0x7c000000 +#define MT_INFRA_MCU_END __REG(INFRA_MCU_ADDR_END) +#define MT_CONN_INFRA_OFFSET(p) ((p) - MT_INFRA_BASE) + +/* CONN INFRA CFG */ +#define MT_CONN_INFRA_BASE 0x18001000 +#define MT_CONN_INFRA(ofs) (MT_CONN_INFRA_BASE + (ofs)) + +#define MT_CONN_INFRA_EFUSE MT_CONN_INFRA(0x020) + +#define MT_CONN_INFRA_ADIE_RESET MT_CONN_INFRA(0x030) +#define MT_CONN_INFRA_ADIE1_RESET_MASK BIT(0) +#define MT_CONN_INFRA_ADIE2_RESET_MASK BIT(2) + +#define MT_CONN_INFRA_OSC_RC_EN MT_CONN_INFRA(0x380) + +#define MT_CONN_INFRA_OSC_CTRL MT_CONN_INFRA(0x300) +#define MT_CONN_INFRA_OSC_RC_EN_MASK BIT(7) +#define MT_CONN_INFRA_OSC_STB_TIME_MASK GENMASK(23, 0) + +#define MT_CONN_INFRA_HW_CTRL MT_CONN_INFRA(0x200) +#define MT_CONN_INFRA_HW_CTRL_MASK BIT(0) + +#define MT_CONN_INFRA_WF_SLP_PROT MT_CONN_INFRA(0x540) +#define MT_CONN_INFRA_WF_SLP_PROT_MASK BIT(0) + +#define MT_CONN_INFRA_WF_SLP_PROT_RDY MT_CONN_INFRA(0x544) +#define MT_CONN_INFRA_CONN_WF_MASK (BIT(29) | BIT(31)) +#define MT_CONN_INFRA_CONN (BIT(25) | BIT(29) | BIT(31)) + +#define MT_CONN_INFRA_EMI_REQ MT_CONN_INFRA(0x414) +#define MT_CONN_INFRA_EMI_REQ_MASK BIT(0) +#define MT_CONN_INFRA_INFRA_REQ_MASK BIT(5) + +/* AFE */ +#define MT_AFE_CTRL_BASE(_band) (0x18003000 + ((_band) << 19)) +#define MT_AFE_CTRL(_band, ofs) (MT_AFE_CTRL_BASE(_band) + (ofs)) + +#define MT_AFE_DIG_EN_01(_band) MT_AFE_CTRL(_band, 0x00) +#define MT_AFE_DIG_EN_02(_band) MT_AFE_CTRL(_band, 0x04) +#define MT_AFE_DIG_EN_03(_band) MT_AFE_CTRL(_band, 0x08) +#define MT_AFE_DIG_TOP_01(_band) MT_AFE_CTRL(_band, 0x0c) + +#define MT_AFE_PLL_STB_TIME(_band) MT_AFE_CTRL(_band, 0xf4) +#define MT_AFE_PLL_STB_TIME_MASK (GENMASK(30, 16) | GENMASK(14, 0)) +#define MT_AFE_PLL_STB_TIME_VAL (FIELD_PREP(GENMASK(30, 16), 0x4bc) | \ + FIELD_PREP(GENMASK(14, 0), 0x7e4)) +#define MT_AFE_BPLL_CFG_MASK GENMASK(7, 6) +#define MT_AFE_WPLL_CFG_MASK GENMASK(1, 0) +#define MT_AFE_MCU_WPLL_CFG_MASK GENMASK(3, 2) +#define MT_AFE_MCU_BPLL_CFG_MASK GENMASK(17, 16) +#define MT_AFE_PLL_CFG_MASK (MT_AFE_BPLL_CFG_MASK | \ + MT_AFE_WPLL_CFG_MASK | \ + MT_AFE_MCU_WPLL_CFG_MASK | \ + MT_AFE_MCU_BPLL_CFG_MASK) +#define MT_AFE_PLL_CFG_VAL (FIELD_PREP(MT_AFE_BPLL_CFG_MASK, 0x1) | \ + FIELD_PREP(MT_AFE_WPLL_CFG_MASK, 0x2) | \ + FIELD_PREP(MT_AFE_MCU_WPLL_CFG_MASK, 0x1) | \ + FIELD_PREP(MT_AFE_MCU_BPLL_CFG_MASK, 0x2)) + +#define MT_AFE_DIG_TOP_01_MASK GENMASK(18, 15) +#define MT_AFE_DIG_TOP_01_VAL FIELD_PREP(MT_AFE_DIG_TOP_01_MASK, 0x9) + +#define MT_AFE_RG_WBG_EN_RCK_MASK BIT(0) +#define MT_AFE_RG_WBG_EN_BPLL_UP_MASK BIT(21) +#define MT_AFE_RG_WBG_EN_WPLL_UP_MASK BIT(20) +#define MT_AFE_RG_WBG_EN_PLL_UP_MASK (MT_AFE_RG_WBG_EN_BPLL_UP_MASK | \ + MT_AFE_RG_WBG_EN_WPLL_UP_MASK) +#define MT_AFE_RG_WBG_EN_TXCAL_WF4 BIT(29) +#define MT_AFE_RG_WBG_EN_TXCAL_BT BIT(21) +#define MT_AFE_RG_WBG_EN_TXCAL_WF3 BIT(20) +#define MT_AFE_RG_WBG_EN_TXCAL_WF2 BIT(19) +#define MT_AFE_RG_WBG_EN_TXCAL_WF1 BIT(18) +#define MT_AFE_RG_WBG_EN_TXCAL_WF0 BIT(17) + +#define MT_ADIE_SLP_CTRL_BASE(_band) (0x18005000 + ((_band) << 19)) +#define MT_ADIE_SLP_CTRL(_band, ofs) (MT_ADIE_SLP_CTRL_BASE(_band) + (ofs)) + +#define MT_ADIE_SLP_CTRL_CK0(_band) MT_ADIE_SLP_CTRL(_band, 0x120) + +/* ADIE */ +#define MT_ADIE_CHIP_ID 0x02c +#define MT_ADIE_VERSION_MASK GENMASK(15, 0) +#define MT_ADIE_CHIP_ID_MASK GENMASK(31, 16) +#define MT_ADIE_IDX0 GENMASK(15, 0) +#define MT_ADIE_IDX1 GENMASK(31, 16) + +#define MT_ADIE_RG_TOP_THADC_BG 0x034 +#define MT_ADIE_VRPI_SEL_CR_MASK GENMASK(15, 12) +#define MT_ADIE_VRPI_SEL_EFUSE_MASK GENMASK(6, 3) + +#define MT_ADIE_RG_TOP_THADC 0x038 +#define MT_ADIE_PGA_GAIN_MASK GENMASK(25, 23) +#define MT_ADIE_PGA_GAIN_EFUSE_MASK GENMASK(2, 0) +#define MT_ADIE_LDO_CTRL_MASK GENMASK(27, 26) +#define MT_ADIE_LDO_CTRL_EFUSE_MASK GENMASK(6, 5) + +#define MT_AFE_RG_ENCAL_WBTAC_IF_SW 0x070 +#define MT_ADIE_EFUSE_RDATA0 0x130 + +#define MT_ADIE_EFUSE2_CTRL 0x148 +#define MT_ADIE_EFUSE_CTRL_MASK BIT(1) + +#define MT_ADIE_EFUSE_CFG 0x144 +#define MT_ADIE_EFUSE_MODE_MASK GENMASK(7, 6) +#define MT_ADIE_EFUSE_ADDR_MASK GENMASK(25, 16) +#define MT_ADIE_EFUSE_VALID_MASK BIT(29) +#define MT_ADIE_EFUSE_KICK_MASK BIT(30) + +#define MT_ADIE_THADC_ANALOG 0x3a6 + +#define MT_ADIE_THADC_SLOP 0x3a7 +#define MT_ADIE_ANA_EN_MASK BIT(7) + +#define MT_ADIE_7975_XTAL_CAL 0x3a1 +#define MT_ADIE_TRIM_MASK GENMASK(6, 0) +#define MT_ADIE_EFUSE_TRIM_MASK GENMASK(5, 0) +#define MT_ADIE_XO_TRIM_EN_MASK BIT(7) +#define MT_ADIE_XTAL_DECREASE_MASK BIT(6) + +#define MT_ADIE_7975_XO_TRIM2 0x3a2 +#define MT_ADIE_7975_XO_TRIM3 0x3a3 +#define MT_ADIE_7975_XO_TRIM4 0x3a4 +#define MT_ADIE_7975_XTAL_EN 0x3a5 + +#define MT_ADIE_XO_TRIM_FLOW 0x3ac +#define MT_ADIE_XTAL_AXM_80M_OSC 0x390 +#define MT_ADIE_XTAL_AXM_40M_OSC 0x391 +#define MT_ADIE_XTAL_TRIM1_80M_OSC 0x398 +#define MT_ADIE_XTAL_TRIM1_40M_OSC 0x399 +#define MT_ADIE_WRI_CK_SEL 0x4ac +#define MT_ADIE_RG_STRAP_PIN_IN 0x4fc +#define MT_ADIE_XTAL_C1 0x654 +#define MT_ADIE_XTAL_C2 0x658 +#define MT_ADIE_RG_XO_01 0x65c +#define MT_ADIE_RG_XO_03 0x664 + +#define MT_ADIE_CLK_EN 0xa00 + +#define MT_ADIE_7975_XTAL 0xa18 +#define MT_ADIE_7975_XTAL_EN_MASK BIT(29) + +#define MT_ADIE_7975_COCLK 0xa1c +#define MT_ADIE_7975_XO_2 0xa84 +#define MT_ADIE_7975_XO_2_FIX_EN BIT(31) + +#define MT_ADIE_7975_XO_CTRL2 0xa94 +#define MT_ADIE_7975_XO_CTRL2_C1_MASK GENMASK(26, 20) +#define MT_ADIE_7975_XO_CTRL2_C2_MASK GENMASK(18, 12) +#define MT_ADIE_7975_XO_CTRL2_MASK (MT_ADIE_7975_XO_CTRL2_C1_MASK | \ + MT_ADIE_7975_XO_CTRL2_C2_MASK) + +#define MT_ADIE_7975_XO_CTRL6 0xaa4 +#define MT_ADIE_7975_XO_CTRL6_MASK BIT(16) + +/* TOP SPI */ +#define MT_TOP_SPI_ADIE_BASE(_band) (0x18004000 + ((_band) << 19)) +#define MT_TOP_SPI_ADIE(_band, ofs) (MT_TOP_SPI_ADIE_BASE(_band) + (ofs)) + +#define MT_TOP_SPI_BUSY_CR(_band) MT_TOP_SPI_ADIE(_band, 0) +#define MT_TOP_SPI_POLLING_BIT BIT(5) + +#define MT_TOP_SPI_ADDR_CR(_band) MT_TOP_SPI_ADIE(_band, 0x50) +#define MT_TOP_SPI_READ_ADDR_FORMAT (BIT(12) | BIT(13) | BIT(15)) +#define MT_TOP_SPI_WRITE_ADDR_FORMAT (BIT(13) | BIT(15)) + +#define MT_TOP_SPI_WRITE_DATA_CR(_band) MT_TOP_SPI_ADIE(_band, 0x54) +#define MT_TOP_SPI_READ_DATA_CR(_band) MT_TOP_SPI_ADIE(_band, 0x58) + +/* CONN INFRA CKGEN */ +#define MT_INFRA_CKGEN_BASE 0x18009000 +#define MT_INFRA_CKGEN(ofs) (MT_INFRA_CKGEN_BASE + (ofs)) + +#define MT_INFRA_CKGEN_BUS MT_INFRA_CKGEN(0xa00) +#define MT_INFRA_CKGEN_BUS_CLK_SEL_MASK BIT(23) +#define MT_INFRA_CKGEN_BUS_RDY_SEL_MASK BIT(29) + +#define MT_INFRA_CKGEN_BUS_WPLL_DIV_1 MT_INFRA_CKGEN(0x008) +#define MT_INFRA_CKGEN_BUS_WPLL_DIV_2 MT_INFRA_CKGEN(0x00c) + +#define MT_INFRA_CKGEN_RFSPI_WPLL_DIV MT_INFRA_CKGEN(0x040) +#define MT_INFRA_CKGEN_DIV_SEL_MASK GENMASK(7, 2) +#define MT_INFRA_CKGEN_DIV_EN_MASK BIT(0) + +/* CONN INFRA BUS */ +#define MT_INFRA_BUS_BASE 0x1800e000 +#define MT_INFRA_BUS(ofs) (MT_INFRA_BUS_BASE + (ofs)) + +#define MT_INFRA_BUS_OFF_TIMEOUT MT_INFRA_BUS(0x300) +#define MT_INFRA_BUS_TIMEOUT_LIMIT_MASK GENMASK(14, 7) +#define MT_INFRA_BUS_TIMEOUT_EN_MASK GENMASK(3, 0) + +#define MT_INFRA_BUS_ON_TIMEOUT MT_INFRA_BUS(0x31c) +#define MT_INFRA_BUS_EMI_START MT_INFRA_BUS(0x360) +#define MT_INFRA_BUS_EMI_END MT_INFRA_BUS(0x364) + +/* CONN_INFRA_SKU */ +#define MT_CONNINFRA_SKU_DEC_ADDR 0x18050000 +#define MT_CONNINFRA_SKU_MASK GENMASK(15, 0) +#define MT_ADIE_TYPE_MASK BIT(1) + +/* FW MODE SYNC */ +#define MT_FW_ASSERT_STAT __REG(FW_ASSERT_STAT_ADDR) +#define MT_FW_EXCEPT_TYPE __REG(FW_EXCEPT_TYPE_ADDR) +#define MT_FW_EXCEPT_COUNT __REG(FW_EXCEPT_COUNT_ADDR) +#define MT_FW_CIRQ_COUNT __REG(FW_CIRQ_COUNT_ADDR) +#define MT_FW_CIRQ_IDX __REG(FW_CIRQ_IDX_ADDR) +#define MT_FW_CIRQ_LISR __REG(FW_CIRQ_LISR_ADDR) +#define MT_FW_TASK_ID __REG(FW_TASK_ID_ADDR) +#define MT_FW_TASK_IDX __REG(FW_TASK_IDX_ADDR) +#define MT_FW_TASK_QID1 __REG(FW_TASK_QID1_ADDR) +#define MT_FW_TASK_QID2 __REG(FW_TASK_QID2_ADDR) +#define MT_FW_TASK_START __REG(FW_TASK_START_ADDR) +#define MT_FW_TASK_END __REG(FW_TASK_END_ADDR) +#define MT_FW_TASK_SIZE __REG(FW_TASK_SIZE_ADDR) +#define MT_FW_LAST_MSG_ID __REG(FW_LAST_MSG_ID_ADDR) +#define MT_FW_EINT_INFO __REG(FW_EINT_INFO_ADDR) +#define MT_FW_SCHED_INFO __REG(FW_SCHED_INFO_ADDR) + +#define MT_SWDEF_BASE __REG(SWDEF_BASE_ADDR) + +#define MT_SWDEF(ofs) (MT_SWDEF_BASE + (ofs)) +#define MT_SWDEF_MODE MT_SWDEF(0x3c) +#define MT_SWDEF_NORMAL_MODE 0 +#define MT_SWDEF_ICAP_MODE 1 +#define MT_SWDEF_SPECTRUM_MODE 2 + +#define MT_SWDEF_SER_STATS MT_SWDEF(0x040) +#define MT_SWDEF_PLE_STATS MT_SWDEF(0x044) +#define MT_SWDEF_PLE1_STATS MT_SWDEF(0x048) +#define MT_SWDEF_PLE1_MDP_RIOC_HANG_ERR BIT(3) +#define MT_SWDEF_PLE_AMSDU_STATS MT_SWDEF(0x04C) +#define MT_SWDEF_PSE_STATS MT_SWDEF(0x050) +#define MT_SWDEF_PSE1_STATS MT_SWDEF(0x054) +#define MT_SWDEF_LAMC_WISR6_BN0_STATS MT_SWDEF(0x058) +#define MT_SWDEF_LAMC_WISR6_BN1_STATS MT_SWDEF(0x05C) +#define MT_SWDEF_LAMC_WISR7_BN0_STATS MT_SWDEF(0x060) +#define MT_SWDEF_LAMC_WISR7_BN1_STATS MT_SWDEF(0x064) + +#define MT_DIC_CMD_REG_BASE 0x41f000 +#define MT_DIC_CMD_REG(ofs) (MT_DIC_CMD_REG_BASE + (ofs)) +#define MT_DIC_CMD_REG_CMD MT_DIC_CMD_REG(0x10) + +#define MT_CPU_UTIL_BASE 0x41f030 +#define MT_CPU_UTIL(ofs) (MT_CPU_UTIL_BASE + (ofs)) +#define MT_CPU_UTIL_BUSY_PCT MT_CPU_UTIL(0x00) +#define MT_CPU_UTIL_PEAK_BUSY_PCT MT_CPU_UTIL(0x04) +#define MT_CPU_UTIL_IDLE_CNT MT_CPU_UTIL(0x08) +#define MT_CPU_UTIL_PEAK_IDLE_CNT MT_CPU_UTIL(0x0c) +#define MT_CPU_UTIL_CTRL MT_CPU_UTIL(0x1c) + +/* LED */ +#define MT_LED_TOP_BASE 0x18013000 +#define MT_LED_PHYS(_n) (MT_LED_TOP_BASE + (_n)) + +#define MT_LED_CTRL(_n) MT_LED_PHYS(0x00 + ((_n) * 4)) +#define MT_LED_CTRL_KICK BIT(7) +#define MT_LED_CTRL_BAND BIT(4) +#define MT_LED_CTRL_BLINK_MODE BIT(2) +#define MT_LED_CTRL_POLARITY BIT(1) + +#define MT_LED_TX_BLINK(_n) MT_LED_PHYS(0x10 + ((_n) * 4)) +#define MT_LED_TX_BLINK_ON_MASK GENMASK(7, 0) +#define MT_LED_TX_BLINK_OFF_MASK GENMASK(15, 8) + +#define MT_LED_STATUS_0(_n) MT_LED_PHYS(0x20 + ((_n) * 8)) +#define MT_LED_STATUS_1(_n) MT_LED_PHYS(0x24 + ((_n) * 8)) +#define MT_LED_STATUS_OFF GENMASK(31, 24) +#define MT_LED_STATUS_ON GENMASK(23, 16) +#define MT_LED_STATUS_DURATION GENMASK(15, 0) + +#define MT_LED_EN(_n) MT_LED_PHYS(0x40 + ((_n) * 4)) + +#define MT_LED_GPIO_MUX0 0x70005050 /* GPIO 1 and GPIO 2 */ +#define MT_LED_GPIO_MUX1 0x70005054 /* GPIO 14 and 15 */ +#define MT_LED_GPIO_MUX2 0x70005058 /* GPIO 18 */ +#define MT_LED_GPIO_MUX3 0x7000505c /* GPIO 26 */ + +/* MT TOP */ +#define MT_TOP_BASE 0x18060000 +#define MT_TOP(ofs) (MT_TOP_BASE + (ofs)) + +#define MT_TOP_LPCR_HOST_BAND(_band) MT_TOP(0x10 + ((_band) * 0x10)) +#define MT_TOP_LPCR_HOST_FW_OWN BIT(0) +#define MT_TOP_LPCR_HOST_DRV_OWN BIT(1) +#define MT_TOP_LPCR_HOST_FW_OWN_STAT BIT(2) + +#define MT_TOP_LPCR_HOST_BAND_IRQ_STAT(_band) MT_TOP(0x14 + ((_band) * 0x10)) +#define MT_TOP_LPCR_HOST_BAND_STAT BIT(0) + +#define MT_TOP_MISC MT_TOP(0xf0) +#define MT_TOP_MISC_FW_STATE GENMASK(2, 0) + +#define MT_TOP_WFSYS_WAKEUP MT_TOP(0x1a4) +#define MT_TOP_WFSYS_WAKEUP_MASK BIT(0) + +#define MT_TOP_MCU_EMI_BASE MT_TOP(0x1c4) +#define MT_TOP_MCU_EMI_BASE_MASK GENMASK(19, 0) + +#define MT_TOP_WF_AP_PERI_BASE MT_TOP(0x1c8) +#define MT_TOP_WF_AP_PERI_BASE_MASK GENMASK(19, 0) + +#define MT_TOP_EFUSE_BASE MT_TOP(0x1cc) +#define MT_TOP_EFUSE_BASE_MASK GENMASK(19, 0) + +#define MT_TOP_CONN_INFRA_WAKEUP MT_TOP(0x1a0) +#define MT_TOP_CONN_INFRA_WAKEUP_MASK BIT(0) + +#define MT_TOP_WFSYS_RESET_STATUS MT_TOP(0x2cc) +#define MT_TOP_WFSYS_RESET_STATUS_MASK BIT(30) + +/* SEMA */ +#define MT_SEMA_BASE 0x18070000 +#define MT_SEMA(ofs) (MT_SEMA_BASE + (ofs)) + +#define MT_SEMA_RFSPI_STATUS (MT_SEMA(0x2000) + (11 * 4)) +#define MT_SEMA_RFSPI_RELEASE (MT_SEMA(0x2200) + (11 * 4)) +#define MT_SEMA_RFSPI_STATUS_MASK BIT(1) + +/* MCU BUS */ +#define MT_MCU_BUS_BASE 0x18400000 +#define MT_MCU_BUS(ofs) (MT_MCU_BUS_BASE + (ofs)) + +#define MT_MCU_BUS_TIMEOUT MT_MCU_BUS(0xf0440) +#define MT_MCU_BUS_TIMEOUT_SET_MASK GENMASK(7, 0) +#define MT_MCU_BUS_TIMEOUT_CG_EN_MASK BIT(28) +#define MT_MCU_BUS_TIMEOUT_EN_MASK BIT(31) + +#define MT_MCU_BUS_REMAP MT_MCU_BUS(0x120) + +/* TOP CFG */ +#define MT_TOP_CFG_BASE 0x184b0000 +#define MT_TOP_CFG(ofs) (MT_TOP_CFG_BASE + (ofs)) + +#define MT_TOP_CFG_IP_VERSION_ADDR MT_TOP_CFG(0x010) + +/* TOP CFG ON */ +#define MT_TOP_CFG_ON_BASE 0x184c1000 +#define MT_TOP_CFG_ON(ofs) (MT_TOP_CFG_ON_BASE + (ofs)) + +#define MT_TOP_CFG_ON_ROM_IDX MT_TOP_CFG_ON(0x604) + +/* SLP CTRL */ +#define MT_SLP_BASE 0x184c3000 +#define MT_SLP(ofs) (MT_SLP_BASE + (ofs)) + +#define MT_SLP_STATUS MT_SLP(0x00c) +#define MT_SLP_WFDMA2CONN_MASK (BIT(21) | BIT(23)) +#define MT_SLP_CTRL_EN_MASK BIT(0) +#define MT_SLP_CTRL_BSY_MASK BIT(1) + +/* MCU BUS DBG */ +#define MT_MCU_BUS_DBG_BASE 0x18500000 +#define MT_MCU_BUS_DBG(ofs) (MT_MCU_BUS_DBG_BASE + (ofs)) + +#define MT_MCU_BUS_DBG_TIMEOUT MT_MCU_BUS_DBG(0x0) +#define MT_MCU_BUS_DBG_TIMEOUT_SET_MASK GENMASK(31, 16) +#define MT_MCU_BUS_DBG_TIMEOUT_CK_EN_MASK BIT(3) +#define MT_MCU_BUS_DBG_TIMEOUT_EN_MASK BIT(2) + +#define MT_HW_BOUND 0x70010020 +#define MT_HW_REV 0x70010204 +#define MT_WF_SUBSYS_RST 0x70002600 + +/* PCIE MAC */ +#define MT_PCIE_MAC_BASE 0x74030000 +#define MT_PCIE_MAC(ofs) (MT_PCIE_MAC_BASE + (ofs)) +#define MT_PCIE_MAC_INT_ENABLE MT_PCIE_MAC(0x188) + +#define MT_PCIE1_MAC_INT_ENABLE 0x74020188 +#define MT_PCIE1_MAC_INT_ENABLE_MT7916 0x74090188 + +#define MT_WM_MCU_PC 0x7c060204 +#define MT_WA_MCU_PC 0x7c06020c + +/* PP TOP */ +#define MT_WF_PP_TOP_BASE 0x820cc000 +#define MT_WF_PP_TOP(ofs) (MT_WF_PP_TOP_BASE + (ofs)) + +#define MT_WF_PP_TOP_RXQ_WFDMA_CF_5 MT_WF_PP_TOP(0x0e8) +#define MT_WF_PP_TOP_RXQ_QID6_WFDMA_HIF_SEL_MASK BIT(6) + +#define MT_WF_IRPI_BASE 0x83000000 +#define MT_WF_IRPI(ofs) (MT_WF_IRPI_BASE + (ofs)) + +#define MT_WF_IRPI_NSS(phy, nss) MT_WF_IRPI(0x6000 + ((phy) << 20) + ((nss) << 16)) +#define MT_WF_IRPI_NSS_MT7916(phy, nss) MT_WF_IRPI(0x1000 + ((phy) << 20) + ((nss) << 16)) + +/* PHY */ +#define MT_WF_PHY_BASE 0x83080000 +#define MT_WF_PHY(ofs) (MT_WF_PHY_BASE + (ofs)) + +#define MT_WF_PHY_RX_CTRL1(_phy) MT_WF_PHY(0x2004 + ((_phy) << 16)) +#define MT_WF_PHY_RX_CTRL1_MT7916(_phy) MT_WF_PHY(0x2004 + ((_phy) << 20)) +#define MT_WF_PHY_RX_CTRL1_IPI_EN GENMASK(2, 0) +#define MT_WF_PHY_RX_CTRL1_STSCNT_EN GENMASK(11, 9) + +#define MT_WF_PHY_RXTD12(_phy) MT_WF_PHY(0x8230 + ((_phy) << 16)) +#define MT_WF_PHY_RXTD12_MT7916(_phy) MT_WF_PHY(0x8230 + ((_phy) << 20)) +#define MT_WF_PHY_RXTD12_IRPI_SW_CLR_ONLY BIT(18) +#define MT_WF_PHY_RXTD12_IRPI_SW_CLR BIT(29) + +#define MT_WF_PHY_TPC_CTRL_STAT(_phy) MT_WF_PHY(0xe7a0 + ((_phy) << 16)) +#define MT_WF_PHY_TPC_CTRL_STAT_MT7916(_phy) MT_WF_PHY(0xe7a0 + ((_phy) << 20)) +#define MT_WF_PHY_TPC_POWER GENMASK(15, 8) + +#define MT_MCU_WM_CIRQ_BASE 0x89010000 +#define MT_MCU_WM_CIRQ(ofs) (MT_MCU_WM_CIRQ_BASE + (ofs)) +#define MT_MCU_WM_CIRQ_IRQ_MASK_CLR_ADDR MT_MCU_WM_CIRQ(0x80) +#define MT_MCU_WM_CIRQ_IRQ_SOFT_ADDR MT_MCU_WM_CIRQ(0xc0) +#define MT_MCU_WM_CIRQ_EINT_MASK_CLR_ADDR MT_MCU_WM_CIRQ(0x108) +#define MT_MCU_WM_CIRQ_EINT_SOFT_ADDR MT_MCU_WM_CIRQ(0x118) + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/soc.c b/drivers/net/wireless/mediatek/mt76/mt7915/soc.c new file mode 100644 index 000000000..13fba2a06 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7915/soc.c @@ -0,0 +1,1312 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2022 MediaTek Inc. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mt7915.h" + +#define MT7981_CON_INFRA_VERSION 0x02090000 +#define MT7986_CON_INFRA_VERSION 0x02070000 + +/* INFRACFG */ +#define MT_INFRACFG_CONN2AP_SLPPROT 0x0d0 +#define MT_INFRACFG_AP2CONN_SLPPROT 0x0d4 + +#define MT_INFRACFG_RX_EN_MASK BIT(16) +#define MT_INFRACFG_TX_RDY_MASK BIT(4) +#define MT_INFRACFG_TX_EN_MASK BIT(0) + +/* TOP POS */ +#define MT_TOP_POS_FAST_CTRL 0x114 +#define MT_TOP_POS_FAST_EN_MASK BIT(3) + +#define MT_TOP_POS_SKU 0x21c +#define MT_TOP_POS_SKU_MASK GENMASK(31, 28) +#define MT_TOP_POS_SKU_ADIE_DBDC_MASK BIT(2) + +enum { + ADIE_SB, + ADIE_DBDC +}; + +static int +mt76_wmac_spi_read(struct mt7915_dev *dev, u8 adie, u32 addr, u32 *val) +{ + int ret; + u32 cur; + + ret = read_poll_timeout(mt76_rr, cur, !(cur & MT_TOP_SPI_POLLING_BIT), + USEC_PER_MSEC, 50 * USEC_PER_MSEC, false, + dev, MT_TOP_SPI_BUSY_CR(adie)); + if (ret) + return ret; + + mt76_wr(dev, MT_TOP_SPI_ADDR_CR(adie), + MT_TOP_SPI_READ_ADDR_FORMAT | addr); + mt76_wr(dev, MT_TOP_SPI_WRITE_DATA_CR(adie), 0); + + ret = read_poll_timeout(mt76_rr, cur, !(cur & MT_TOP_SPI_POLLING_BIT), + USEC_PER_MSEC, 50 * USEC_PER_MSEC, false, + dev, MT_TOP_SPI_BUSY_CR(adie)); + if (ret) + return ret; + + *val = mt76_rr(dev, MT_TOP_SPI_READ_DATA_CR(adie)); + + return 0; +} + +static int +mt76_wmac_spi_write(struct mt7915_dev *dev, u8 adie, u32 addr, u32 val) +{ + int ret; + u32 cur; + + ret = read_poll_timeout(mt76_rr, cur, !(cur & MT_TOP_SPI_POLLING_BIT), + USEC_PER_MSEC, 50 * USEC_PER_MSEC, false, + dev, MT_TOP_SPI_BUSY_CR(adie)); + if (ret) + return ret; + + mt76_wr(dev, MT_TOP_SPI_ADDR_CR(adie), + MT_TOP_SPI_WRITE_ADDR_FORMAT | addr); + mt76_wr(dev, MT_TOP_SPI_WRITE_DATA_CR(adie), val); + + return read_poll_timeout(mt76_rr, cur, !(cur & MT_TOP_SPI_POLLING_BIT), + USEC_PER_MSEC, 50 * USEC_PER_MSEC, false, + dev, MT_TOP_SPI_BUSY_CR(adie)); +} + +static int +mt76_wmac_spi_rmw(struct mt7915_dev *dev, u8 adie, + u32 addr, u32 mask, u32 val) +{ + u32 cur, ret; + + ret = mt76_wmac_spi_read(dev, adie, addr, &cur); + if (ret) + return ret; + + cur &= ~mask; + cur |= val; + + return mt76_wmac_spi_write(dev, adie, addr, cur); +} + +static int +mt7986_wmac_adie_efuse_read(struct mt7915_dev *dev, u8 adie, + u32 addr, u32 *data) +{ + int ret, temp; + u32 val, mask; + + ret = mt76_wmac_spi_write(dev, adie, MT_ADIE_EFUSE_CFG, + MT_ADIE_EFUSE_CTRL_MASK); + if (ret) + return ret; + + ret = mt76_wmac_spi_rmw(dev, adie, MT_ADIE_EFUSE2_CTRL, BIT(30), 0x0); + if (ret) + return ret; + + mask = (MT_ADIE_EFUSE_MODE_MASK | MT_ADIE_EFUSE_ADDR_MASK | + MT_ADIE_EFUSE_KICK_MASK); + val = FIELD_PREP(MT_ADIE_EFUSE_MODE_MASK, 0) | + FIELD_PREP(MT_ADIE_EFUSE_ADDR_MASK, addr) | + FIELD_PREP(MT_ADIE_EFUSE_KICK_MASK, 1); + ret = mt76_wmac_spi_rmw(dev, adie, MT_ADIE_EFUSE2_CTRL, mask, val); + if (ret) + return ret; + + ret = read_poll_timeout(mt76_wmac_spi_read, temp, + !temp && !FIELD_GET(MT_ADIE_EFUSE_KICK_MASK, val), + USEC_PER_MSEC, 50 * USEC_PER_MSEC, false, + dev, adie, MT_ADIE_EFUSE2_CTRL, &val); + if (ret) + return ret; + + ret = mt76_wmac_spi_read(dev, adie, MT_ADIE_EFUSE2_CTRL, &val); + if (ret) + return ret; + + if (FIELD_GET(MT_ADIE_EFUSE_VALID_MASK, val) == 1) + ret = mt76_wmac_spi_read(dev, adie, MT_ADIE_EFUSE_RDATA0, + data); + + return ret; +} + +static inline void mt76_wmac_spi_lock(struct mt7915_dev *dev) +{ + u32 cur; + + read_poll_timeout(mt76_rr, cur, + FIELD_GET(MT_SEMA_RFSPI_STATUS_MASK, cur), + 1000, 1000 * MSEC_PER_SEC, false, dev, + MT_SEMA_RFSPI_STATUS); +} + +static inline void mt76_wmac_spi_unlock(struct mt7915_dev *dev) +{ + mt76_wr(dev, MT_SEMA_RFSPI_RELEASE, 1); +} + +static u32 mt76_wmac_rmw(void __iomem *base, u32 offset, u32 mask, u32 val) +{ + val |= readl(base + offset) & ~mask; + writel(val, base + offset); + + return val; +} + +static u8 mt798x_wmac_check_adie_type(struct mt7915_dev *dev) +{ + u32 val; + + /* Only DBDC A-die is used with MT7981 */ + if (is_mt7981(&dev->mt76)) + return ADIE_DBDC; + + val = readl(dev->sku + MT_TOP_POS_SKU); + + return FIELD_GET(MT_TOP_POS_SKU_ADIE_DBDC_MASK, val); +} + +static int mt7986_wmac_consys_reset(struct mt7915_dev *dev, bool enable) +{ + if (!enable) + return reset_control_assert(dev->rstc); + + mt76_wmac_rmw(dev->sku, MT_TOP_POS_FAST_CTRL, + MT_TOP_POS_FAST_EN_MASK, + FIELD_PREP(MT_TOP_POS_FAST_EN_MASK, 0x1)); + + return reset_control_deassert(dev->rstc); +} + +static int mt7986_wmac_gpio_setup(struct mt7915_dev *dev) +{ + struct pinctrl_state *state; + struct pinctrl *pinctrl; + int ret; + u8 type; + + type = mt798x_wmac_check_adie_type(dev); + pinctrl = devm_pinctrl_get(dev->mt76.dev); + if (IS_ERR(pinctrl)) + return PTR_ERR(pinctrl); + + switch (type) { + case ADIE_SB: + state = pinctrl_lookup_state(pinctrl, "default"); + if (IS_ERR_OR_NULL(state)) + return -EINVAL; + break; + case ADIE_DBDC: + state = pinctrl_lookup_state(pinctrl, "dbdc"); + if (IS_ERR_OR_NULL(state)) + return -EINVAL; + break; + default: + return -EINVAL; + } + + ret = pinctrl_select_state(pinctrl, state); + if (ret) + return ret; + + usleep_range(500, 1000); + + return 0; +} + +static int mt7986_wmac_consys_lockup(struct mt7915_dev *dev, bool enable) +{ + int ret; + u32 cur; + + mt76_wmac_rmw(dev->dcm, MT_INFRACFG_AP2CONN_SLPPROT, + MT_INFRACFG_RX_EN_MASK, + FIELD_PREP(MT_INFRACFG_RX_EN_MASK, enable)); + ret = read_poll_timeout(readl, cur, !(cur & MT_INFRACFG_RX_EN_MASK), + USEC_PER_MSEC, 50 * USEC_PER_MSEC, false, + dev->dcm + MT_INFRACFG_AP2CONN_SLPPROT); + if (ret) + return ret; + + mt76_wmac_rmw(dev->dcm, MT_INFRACFG_AP2CONN_SLPPROT, + MT_INFRACFG_TX_EN_MASK, + FIELD_PREP(MT_INFRACFG_TX_EN_MASK, enable)); + ret = read_poll_timeout(readl, cur, !(cur & MT_INFRACFG_TX_RDY_MASK), + USEC_PER_MSEC, 50 * USEC_PER_MSEC, false, + dev->dcm + MT_INFRACFG_AP2CONN_SLPPROT); + if (ret) + return ret; + + mt76_wmac_rmw(dev->dcm, MT_INFRACFG_CONN2AP_SLPPROT, + MT_INFRACFG_RX_EN_MASK, + FIELD_PREP(MT_INFRACFG_RX_EN_MASK, enable)); + mt76_wmac_rmw(dev->dcm, MT_INFRACFG_CONN2AP_SLPPROT, + MT_INFRACFG_TX_EN_MASK, + FIELD_PREP(MT_INFRACFG_TX_EN_MASK, enable)); + + return 0; +} + +static int mt798x_wmac_coninfra_check(struct mt7915_dev *dev) +{ + u32 cur; + u32 con_infra_version; + + if (is_mt7981(&dev->mt76)) { + con_infra_version = MT7981_CON_INFRA_VERSION; + } else if (is_mt7986(&dev->mt76)) { + con_infra_version = MT7986_CON_INFRA_VERSION; + } else { + WARN_ON(1); + return -EINVAL; + } + + return read_poll_timeout(mt76_rr, cur, (cur == con_infra_version), + USEC_PER_MSEC, 50 * USEC_PER_MSEC, + false, dev, MT_CONN_INFRA_BASE); +} + +static int mt798x_wmac_coninfra_setup(struct mt7915_dev *dev) +{ + struct device *pdev = dev->mt76.dev; + struct resource res; + u32 val; + int ret; + + ret = of_reserved_mem_region_to_resource(pdev->of_node, 0, &res); + if (ret) + return ret; + + val = (res.start >> 16) & MT_TOP_MCU_EMI_BASE_MASK; + + if (is_mt7986(&dev->mt76)) { + /* Set conninfra subsys PLL check */ + mt76_rmw_field(dev, MT_INFRA_CKGEN_BUS, + MT_INFRA_CKGEN_BUS_RDY_SEL_MASK, 0x1); + mt76_rmw_field(dev, MT_INFRA_CKGEN_BUS, + MT_INFRA_CKGEN_BUS_RDY_SEL_MASK, 0x1); + } + + mt76_rmw_field(dev, MT_TOP_MCU_EMI_BASE, + MT_TOP_MCU_EMI_BASE_MASK, val); + + if (is_mt7981(&dev->mt76)) { + mt76_rmw_field(dev, MT_TOP_WF_AP_PERI_BASE, + MT_TOP_WF_AP_PERI_BASE_MASK, 0x300d0000 >> 16); + + mt76_rmw_field(dev, MT_TOP_EFUSE_BASE, + MT_TOP_EFUSE_BASE_MASK, 0x11f20000 >> 16); + } + + mt76_wr(dev, MT_INFRA_BUS_EMI_START, res.start); + mt76_wr(dev, MT_INFRA_BUS_EMI_END, resource_size(&res)); + + mt76_rr(dev, MT_CONN_INFRA_EFUSE); + + /* Set conninfra sysram */ + mt76_wr(dev, MT_TOP_RGU_SYSRAM_PDN, 0); + mt76_wr(dev, MT_TOP_RGU_SYSRAM_SLP, 1); + + return 0; +} + +static int mt798x_wmac_sku_setup(struct mt7915_dev *dev, u32 *adie_type) +{ + int ret; + u32 adie_main = 0, adie_ext = 0; + + mt76_rmw_field(dev, MT_CONN_INFRA_ADIE_RESET, + MT_CONN_INFRA_ADIE1_RESET_MASK, 0x1); + + if (is_mt7986(&dev->mt76)) { + mt76_rmw_field(dev, MT_CONN_INFRA_ADIE_RESET, + MT_CONN_INFRA_ADIE2_RESET_MASK, 0x1); + } + + mt76_wmac_spi_lock(dev); + + ret = mt76_wmac_spi_read(dev, 0, MT_ADIE_CHIP_ID, &adie_main); + if (ret) + goto out; + + if (is_mt7986(&dev->mt76)) { + ret = mt76_wmac_spi_read(dev, 1, MT_ADIE_CHIP_ID, &adie_ext); + if (ret) + goto out; + } + + *adie_type = FIELD_GET(MT_ADIE_CHIP_ID_MASK, adie_main) | + (MT_ADIE_CHIP_ID_MASK & adie_ext); + +out: + mt76_wmac_spi_unlock(dev); + + return 0; +} + +static inline u16 mt7986_adie_idx(u8 adie, u32 adie_type) +{ + if (adie == 0) + return u32_get_bits(adie_type, MT_ADIE_IDX0); + else + return u32_get_bits(adie_type, MT_ADIE_IDX1); +} + +static inline bool is_7975(struct mt7915_dev *dev, u8 adie, u32 adie_type) +{ + return mt7986_adie_idx(adie, adie_type) == 0x7975; +} + +static inline bool is_7976(struct mt7915_dev *dev, u8 adie, u32 adie_type) +{ + return mt7986_adie_idx(adie, adie_type) == 0x7976; +} + +static int mt7986_wmac_adie_thermal_cal(struct mt7915_dev *dev, u8 adie) +{ + int ret; + u32 data, val; + + ret = mt7986_wmac_adie_efuse_read(dev, adie, MT_ADIE_THADC_ANALOG, + &data); + if (ret || FIELD_GET(MT_ADIE_ANA_EN_MASK, data)) { + val = FIELD_GET(MT_ADIE_VRPI_SEL_EFUSE_MASK, data); + ret = mt76_wmac_spi_rmw(dev, adie, MT_ADIE_RG_TOP_THADC_BG, + MT_ADIE_VRPI_SEL_CR_MASK, + FIELD_PREP(MT_ADIE_VRPI_SEL_CR_MASK, val)); + if (ret) + return ret; + + val = FIELD_GET(MT_ADIE_PGA_GAIN_EFUSE_MASK, data); + ret = mt76_wmac_spi_rmw(dev, adie, MT_ADIE_RG_TOP_THADC, + MT_ADIE_PGA_GAIN_MASK, + FIELD_PREP(MT_ADIE_PGA_GAIN_MASK, val)); + if (ret) + return ret; + } + + ret = mt7986_wmac_adie_efuse_read(dev, adie, MT_ADIE_THADC_SLOP, + &data); + if (ret || FIELD_GET(MT_ADIE_ANA_EN_MASK, data)) { + val = FIELD_GET(MT_ADIE_LDO_CTRL_EFUSE_MASK, data); + + return mt76_wmac_spi_rmw(dev, adie, MT_ADIE_RG_TOP_THADC, + MT_ADIE_LDO_CTRL_MASK, + FIELD_PREP(MT_ADIE_LDO_CTRL_MASK, val)); + } + + return 0; +} + +static int +mt7986_read_efuse_xo_trim_7976(struct mt7915_dev *dev, u8 adie, + bool is_40m, int *result) +{ + int ret; + u32 data, addr; + + addr = is_40m ? MT_ADIE_XTAL_AXM_40M_OSC : MT_ADIE_XTAL_AXM_80M_OSC; + ret = mt7986_wmac_adie_efuse_read(dev, adie, addr, &data); + if (ret) + return ret; + + if (!FIELD_GET(MT_ADIE_XO_TRIM_EN_MASK, data)) { + *result = 64; + } else { + *result = FIELD_GET(MT_ADIE_TRIM_MASK, data); + addr = is_40m ? MT_ADIE_XTAL_TRIM1_40M_OSC : + MT_ADIE_XTAL_TRIM1_80M_OSC; + ret = mt7986_wmac_adie_efuse_read(dev, adie, addr, &data); + if (ret) + return ret; + + if (FIELD_GET(MT_ADIE_XO_TRIM_EN_MASK, data) && + FIELD_GET(MT_ADIE_XTAL_DECREASE_MASK, data)) + *result -= FIELD_GET(MT_ADIE_EFUSE_TRIM_MASK, data); + else if (FIELD_GET(MT_ADIE_XO_TRIM_EN_MASK, data)) + *result += FIELD_GET(MT_ADIE_EFUSE_TRIM_MASK, data); + + *result = max(0, min(127, *result)); + } + + return 0; +} + +static int mt7986_wmac_adie_xtal_trim_7976(struct mt7915_dev *dev, u8 adie) +{ + int ret, trim_80m, trim_40m; + u32 data, val, mode; + + ret = mt7986_wmac_adie_efuse_read(dev, adie, MT_ADIE_XO_TRIM_FLOW, + &data); + if (ret || !FIELD_GET(BIT(1), data)) + return 0; + + ret = mt7986_read_efuse_xo_trim_7976(dev, adie, false, &trim_80m); + if (ret) + return ret; + + ret = mt7986_read_efuse_xo_trim_7976(dev, adie, true, &trim_40m); + if (ret) + return ret; + + ret = mt76_wmac_spi_read(dev, adie, MT_ADIE_RG_STRAP_PIN_IN, &val); + if (ret) + return ret; + + mode = FIELD_PREP(GENMASK(6, 4), val); + if (!mode || mode == 0x2) { + ret = mt76_wmac_spi_rmw(dev, adie, MT_ADIE_XTAL_C1, + GENMASK(31, 24), + FIELD_PREP(GENMASK(31, 24), trim_80m)); + if (ret) + return ret; + + ret = mt76_wmac_spi_rmw(dev, adie, MT_ADIE_XTAL_C2, + GENMASK(31, 24), + FIELD_PREP(GENMASK(31, 24), trim_80m)); + } else if (mode == 0x3 || mode == 0x4 || mode == 0x6) { + ret = mt76_wmac_spi_rmw(dev, adie, MT_ADIE_XTAL_C1, + GENMASK(23, 16), + FIELD_PREP(GENMASK(23, 16), trim_40m)); + if (ret) + return ret; + + ret = mt76_wmac_spi_rmw(dev, adie, MT_ADIE_XTAL_C2, + GENMASK(23, 16), + FIELD_PREP(GENMASK(23, 16), trim_40m)); + } + + return ret; +} + +static int mt798x_wmac_adie_patch_7976(struct mt7915_dev *dev, u8 adie) +{ + u32 id, version, rg_xo_01, rg_xo_03; + int ret; + + ret = mt76_wmac_spi_read(dev, adie, MT_ADIE_CHIP_ID, &id); + if (ret) + return ret; + + version = FIELD_GET(MT_ADIE_VERSION_MASK, id); + + ret = mt76_wmac_spi_write(dev, adie, MT_ADIE_RG_TOP_THADC, 0x4a563b00); + if (ret) + return ret; + + if (version == 0x8a00 || version == 0x8a10 || + version == 0x8b00 || version == 0x8c10) { + rg_xo_01 = 0x1d59080f; + rg_xo_03 = 0x34c00fe0; + } else { + if (is_mt7981(&dev->mt76)) { + rg_xo_01 = 0x1959c80f; + } else if (is_mt7986(&dev->mt76)) { + rg_xo_01 = 0x1959f80f; + } else { + WARN_ON(1); + return -EINVAL; + } + rg_xo_03 = 0x34d00fe0; + } + + ret = mt76_wmac_spi_write(dev, adie, MT_ADIE_RG_XO_01, rg_xo_01); + if (ret) + return ret; + + return mt76_wmac_spi_write(dev, adie, MT_ADIE_RG_XO_03, rg_xo_03); +} + +static int +mt7986_read_efuse_xo_trim_7975(struct mt7915_dev *dev, u8 adie, + u32 addr, u32 *result) +{ + int ret; + u32 data; + + ret = mt7986_wmac_adie_efuse_read(dev, adie, addr, &data); + if (ret) + return ret; + + if ((data & MT_ADIE_XO_TRIM_EN_MASK)) { + if ((data & MT_ADIE_XTAL_DECREASE_MASK)) + *result -= (data & MT_ADIE_EFUSE_TRIM_MASK); + else + *result += (data & MT_ADIE_EFUSE_TRIM_MASK); + + *result = (*result & MT_ADIE_TRIM_MASK); + } + + return 0; +} + +static int mt7986_wmac_adie_xtal_trim_7975(struct mt7915_dev *dev, u8 adie) +{ + int ret; + u32 data, result = 0, value; + + ret = mt7986_wmac_adie_efuse_read(dev, adie, MT_ADIE_7975_XTAL_EN, + &data); + if (ret || !(data & BIT(1))) + return 0; + + ret = mt7986_wmac_adie_efuse_read(dev, adie, MT_ADIE_7975_XTAL_CAL, + &data); + if (ret) + return ret; + + if (data & MT_ADIE_XO_TRIM_EN_MASK) + result = (data & MT_ADIE_TRIM_MASK); + + ret = mt7986_read_efuse_xo_trim_7975(dev, adie, MT_ADIE_7975_XO_TRIM2, + &result); + if (ret) + return ret; + + ret = mt7986_read_efuse_xo_trim_7975(dev, adie, MT_ADIE_7975_XO_TRIM3, + &result); + if (ret) + return ret; + + ret = mt7986_read_efuse_xo_trim_7975(dev, adie, MT_ADIE_7975_XO_TRIM4, + &result); + if (ret) + return ret; + + /* Update trim value to C1 and C2*/ + value = FIELD_GET(MT_ADIE_7975_XO_CTRL2_C1_MASK, result) | + FIELD_GET(MT_ADIE_7975_XO_CTRL2_C2_MASK, result); + ret = mt76_wmac_spi_rmw(dev, adie, MT_ADIE_7975_XO_CTRL2, + MT_ADIE_7975_XO_CTRL2_MASK, value); + if (ret) + return ret; + + ret = mt76_wmac_spi_read(dev, adie, MT_ADIE_7975_XTAL, &value); + if (ret) + return ret; + + if (value & MT_ADIE_7975_XTAL_EN_MASK) { + ret = mt76_wmac_spi_rmw(dev, adie, MT_ADIE_7975_XO_2, + MT_ADIE_7975_XO_2_FIX_EN, 0x0); + if (ret) + return ret; + } + + return mt76_wmac_spi_rmw(dev, adie, MT_ADIE_7975_XO_CTRL6, + MT_ADIE_7975_XO_CTRL6_MASK, 0x1); +} + +static int mt7986_wmac_adie_patch_7975(struct mt7915_dev *dev, u8 adie) +{ + int ret; + + /* disable CAL LDO and fine tune RFDIG LDO */ + ret = mt76_wmac_spi_write(dev, adie, 0x348, 0x00000002); + if (ret) + return ret; + + ret = mt76_wmac_spi_write(dev, adie, 0x378, 0x00000002); + if (ret) + return ret; + + ret = mt76_wmac_spi_write(dev, adie, 0x3a8, 0x00000002); + if (ret) + return ret; + + ret = mt76_wmac_spi_write(dev, adie, 0x3d8, 0x00000002); + if (ret) + return ret; + + /* set CKA driving and filter */ + ret = mt76_wmac_spi_write(dev, adie, 0xa1c, 0x30000aaa); + if (ret) + return ret; + + /* set CKB LDO to 1.4V */ + ret = mt76_wmac_spi_write(dev, adie, 0xa84, 0x8470008a); + if (ret) + return ret; + + /* turn on SX0 LTBUF */ + if (is_mt7981(&dev->mt76)) { + ret = mt76_wmac_spi_write(dev, adie, 0x074, 0x00000007); + } else if (is_mt7986(&dev->mt76)) { + ret = mt76_wmac_spi_write(dev, adie, 0x074, 0x00000002); + } else { + WARN_ON(1); + return -EINVAL; + } + + if (ret) + return ret; + + /* CK_BUF_SW_EN = 1 (all buf in manual mode.) */ + ret = mt76_wmac_spi_write(dev, adie, 0xaa4, 0x01001fc0); + if (ret) + return ret; + + /* BT mode/WF normal mode 00000005 */ + ret = mt76_wmac_spi_write(dev, adie, 0x070, 0x00000005); + if (ret) + return ret; + + /* BG thermal sensor offset update */ + ret = mt76_wmac_spi_write(dev, adie, 0x344, 0x00000088); + if (ret) + return ret; + + ret = mt76_wmac_spi_write(dev, adie, 0x374, 0x00000088); + if (ret) + return ret; + + ret = mt76_wmac_spi_write(dev, adie, 0x3a4, 0x00000088); + if (ret) + return ret; + + ret = mt76_wmac_spi_write(dev, adie, 0x3d4, 0x00000088); + if (ret) + return ret; + + /* set WCON VDD IPTAT to "0000" */ + ret = mt76_wmac_spi_write(dev, adie, 0xa80, 0x44d07000); + if (ret) + return ret; + + /* change back LTBUF SX3 drving to default value */ + ret = mt76_wmac_spi_write(dev, adie, 0xa88, 0x3900aaaa); + if (ret) + return ret; + + /* SM input cap off */ + ret = mt76_wmac_spi_write(dev, adie, 0x2c4, 0x00000000); + if (ret) + return ret; + + /* set CKB driving and filter */ + if (is_mt7986(&dev->mt76)) + return mt76_wmac_spi_write(dev, adie, 0x2c8, 0x00000072); + + return ret; +} + +static int mt7986_wmac_adie_cfg(struct mt7915_dev *dev, u8 adie, u32 adie_type) +{ + int ret; + + mt76_wmac_spi_lock(dev); + ret = mt76_wmac_spi_write(dev, adie, MT_ADIE_CLK_EN, ~0); + if (ret) + goto out; + + if (is_7975(dev, adie, adie_type)) { + ret = mt76_wmac_spi_rmw(dev, adie, MT_ADIE_7975_COCLK, + BIT(1), 0x1); + if (ret) + goto out; + + ret = mt7986_wmac_adie_thermal_cal(dev, adie); + if (ret) + goto out; + + ret = mt7986_wmac_adie_xtal_trim_7975(dev, adie); + if (ret) + goto out; + + ret = mt7986_wmac_adie_patch_7975(dev, adie); + } else if (is_7976(dev, adie, adie_type)) { + if (mt798x_wmac_check_adie_type(dev) == ADIE_DBDC) { + ret = mt76_wmac_spi_write(dev, adie, + MT_ADIE_WRI_CK_SEL, 0x1c); + if (ret) + goto out; + } + + ret = mt7986_wmac_adie_thermal_cal(dev, adie); + if (ret) + goto out; + + ret = mt7986_wmac_adie_xtal_trim_7976(dev, adie); + if (ret) + goto out; + + ret = mt798x_wmac_adie_patch_7976(dev, adie); + } +out: + mt76_wmac_spi_unlock(dev); + + return ret; +} + +static int +mt7986_wmac_afe_cal(struct mt7915_dev *dev, u8 adie, bool dbdc, u32 adie_type) +{ + int ret; + u8 idx; + u32 txcal; + + mt76_wmac_spi_lock(dev); + if (is_7975(dev, adie, adie_type)) + ret = mt76_wmac_spi_write(dev, adie, + MT_AFE_RG_ENCAL_WBTAC_IF_SW, + 0x80000000); + else + ret = mt76_wmac_spi_write(dev, adie, + MT_AFE_RG_ENCAL_WBTAC_IF_SW, + 0x88888005); + if (ret) + goto out; + + idx = dbdc ? ADIE_DBDC : adie; + + mt76_rmw_field(dev, MT_AFE_DIG_EN_01(idx), + MT_AFE_RG_WBG_EN_RCK_MASK, 0x1); + usleep_range(60, 100); + + mt76_rmw(dev, MT_AFE_DIG_EN_01(idx), + MT_AFE_RG_WBG_EN_RCK_MASK, 0x0); + + mt76_rmw_field(dev, MT_AFE_DIG_EN_03(idx), + MT_AFE_RG_WBG_EN_BPLL_UP_MASK, 0x1); + usleep_range(30, 100); + + mt76_rmw_field(dev, MT_AFE_DIG_EN_03(idx), + MT_AFE_RG_WBG_EN_WPLL_UP_MASK, 0x1); + usleep_range(60, 100); + + txcal = (MT_AFE_RG_WBG_EN_TXCAL_BT | + MT_AFE_RG_WBG_EN_TXCAL_WF0 | + MT_AFE_RG_WBG_EN_TXCAL_WF1 | + MT_AFE_RG_WBG_EN_TXCAL_WF2 | + MT_AFE_RG_WBG_EN_TXCAL_WF3); + if (is_mt7981(&dev->mt76)) + txcal |= MT_AFE_RG_WBG_EN_TXCAL_WF4; + + mt76_set(dev, MT_AFE_DIG_EN_01(idx), txcal); + usleep_range(800, 1000); + + mt76_clear(dev, MT_AFE_DIG_EN_01(idx), txcal); + mt76_rmw(dev, MT_AFE_DIG_EN_03(idx), + MT_AFE_RG_WBG_EN_PLL_UP_MASK, 0x0); + + ret = mt76_wmac_spi_write(dev, adie, MT_AFE_RG_ENCAL_WBTAC_IF_SW, + 0x5); + +out: + mt76_wmac_spi_unlock(dev); + + return ret; +} + +static void mt7986_wmac_subsys_pll_initial(struct mt7915_dev *dev, u8 band) +{ + mt76_rmw(dev, MT_AFE_PLL_STB_TIME(band), + MT_AFE_PLL_STB_TIME_MASK, MT_AFE_PLL_STB_TIME_VAL); + + mt76_rmw(dev, MT_AFE_DIG_EN_02(band), + MT_AFE_PLL_CFG_MASK, MT_AFE_PLL_CFG_VAL); + + mt76_rmw(dev, MT_AFE_DIG_TOP_01(band), + MT_AFE_DIG_TOP_01_MASK, MT_AFE_DIG_TOP_01_VAL); +} + +static void mt7986_wmac_subsys_setting(struct mt7915_dev *dev) +{ + /* Subsys pll init */ + mt7986_wmac_subsys_pll_initial(dev, 0); + mt7986_wmac_subsys_pll_initial(dev, 1); + + /* Set legacy OSC control stable time*/ + mt76_rmw(dev, MT_CONN_INFRA_OSC_RC_EN, + MT_CONN_INFRA_OSC_RC_EN_MASK, 0x0); + mt76_rmw(dev, MT_CONN_INFRA_OSC_CTRL, + MT_CONN_INFRA_OSC_STB_TIME_MASK, 0x80706); + + /* prevent subsys from power on/of in a short time interval */ + mt76_rmw(dev, MT_TOP_WFSYS_PWR, + MT_TOP_PWR_ACK_MASK | MT_TOP_PWR_KEY_MASK, + MT_TOP_PWR_KEY); +} + +static int mt7986_wmac_bus_timeout(struct mt7915_dev *dev) +{ + mt76_rmw_field(dev, MT_INFRA_BUS_OFF_TIMEOUT, + MT_INFRA_BUS_TIMEOUT_LIMIT_MASK, 0x2); + + mt76_rmw_field(dev, MT_INFRA_BUS_OFF_TIMEOUT, + MT_INFRA_BUS_TIMEOUT_EN_MASK, 0xf); + + mt76_rmw_field(dev, MT_INFRA_BUS_ON_TIMEOUT, + MT_INFRA_BUS_TIMEOUT_LIMIT_MASK, 0xc); + + mt76_rmw_field(dev, MT_INFRA_BUS_ON_TIMEOUT, + MT_INFRA_BUS_TIMEOUT_EN_MASK, 0xf); + + return mt798x_wmac_coninfra_check(dev); +} + +static void mt7986_wmac_clock_enable(struct mt7915_dev *dev, u32 adie_type) +{ + u32 cur; + + mt76_rmw_field(dev, MT_INFRA_CKGEN_BUS_WPLL_DIV_1, + MT_INFRA_CKGEN_DIV_SEL_MASK, 0x1); + + mt76_rmw_field(dev, MT_INFRA_CKGEN_BUS_WPLL_DIV_2, + MT_INFRA_CKGEN_DIV_SEL_MASK, 0x1); + + mt76_rmw_field(dev, MT_INFRA_CKGEN_BUS_WPLL_DIV_1, + MT_INFRA_CKGEN_DIV_EN_MASK, 0x1); + + mt76_rmw_field(dev, MT_INFRA_CKGEN_BUS_WPLL_DIV_2, + MT_INFRA_CKGEN_DIV_EN_MASK, 0x1); + + mt76_rmw_field(dev, MT_INFRA_CKGEN_RFSPI_WPLL_DIV, + MT_INFRA_CKGEN_DIV_SEL_MASK, 0x8); + + mt76_rmw_field(dev, MT_INFRA_CKGEN_RFSPI_WPLL_DIV, + MT_INFRA_CKGEN_DIV_EN_MASK, 0x1); + + mt76_rmw_field(dev, MT_INFRA_CKGEN_BUS, + MT_INFRA_CKGEN_BUS_CLK_SEL_MASK, 0x0); + + mt76_rmw_field(dev, MT_CONN_INFRA_HW_CTRL, + MT_CONN_INFRA_HW_CTRL_MASK, 0x1); + + mt76_rmw(dev, MT_TOP_CONN_INFRA_WAKEUP, + MT_TOP_CONN_INFRA_WAKEUP_MASK, 0x1); + + usleep_range(900, 1000); + + mt76_wmac_spi_lock(dev); + if (is_7975(dev, 0, adie_type) || is_7976(dev, 0, adie_type)) { + mt76_rmw_field(dev, MT_ADIE_SLP_CTRL_CK0(0), + MT_SLP_CTRL_EN_MASK, 0x1); + + read_poll_timeout(mt76_rr, cur, !(cur & MT_SLP_CTRL_BSY_MASK), + USEC_PER_MSEC, 50 * USEC_PER_MSEC, false, + dev, MT_ADIE_SLP_CTRL_CK0(0)); + } + if (is_7975(dev, 1, adie_type) || is_7976(dev, 1, adie_type)) { + mt76_rmw_field(dev, MT_ADIE_SLP_CTRL_CK0(1), + MT_SLP_CTRL_EN_MASK, 0x1); + + read_poll_timeout(mt76_rr, cur, !(cur & MT_SLP_CTRL_BSY_MASK), + USEC_PER_MSEC, 50 * USEC_PER_MSEC, false, + dev, MT_ADIE_SLP_CTRL_CK0(1)); + } + mt76_wmac_spi_unlock(dev); + + mt76_rmw(dev, MT_TOP_CONN_INFRA_WAKEUP, + MT_TOP_CONN_INFRA_WAKEUP_MASK, 0x0); + usleep_range(900, 1000); +} + +static int mt7986_wmac_top_wfsys_wakeup(struct mt7915_dev *dev, bool enable) +{ + mt76_rmw_field(dev, MT_TOP_WFSYS_WAKEUP, + MT_TOP_WFSYS_WAKEUP_MASK, enable); + + usleep_range(900, 1000); + + if (!enable) + return 0; + + return mt798x_wmac_coninfra_check(dev); +} + +static int mt7986_wmac_wm_enable(struct mt7915_dev *dev, bool enable) +{ + u32 cur; + + if (is_mt7986(&dev->mt76)) + mt76_wr(dev, MT_CONNINFRA_SKU_DEC_ADDR, 0); + + mt76_rmw_field(dev, MT7986_TOP_WM_RESET, + MT7986_TOP_WM_RESET_MASK, enable); + if (!enable) + return 0; + + return read_poll_timeout(mt76_rr, cur, (cur == 0x1d1e), + USEC_PER_MSEC, 5000 * USEC_PER_MSEC, false, + dev, MT_TOP_CFG_ON_ROM_IDX); +} + +static int mt7986_wmac_wfsys_poweron(struct mt7915_dev *dev, bool enable) +{ + u32 mask = MT_TOP_PWR_EN_MASK | MT_TOP_PWR_KEY_MASK; + u32 cur; + + mt76_rmw(dev, MT_TOP_WFSYS_PWR, mask, + MT_TOP_PWR_KEY | FIELD_PREP(MT_TOP_PWR_EN_MASK, enable)); + + return read_poll_timeout(mt76_rr, cur, + (FIELD_GET(MT_TOP_WFSYS_RESET_STATUS_MASK, cur) == enable), + USEC_PER_MSEC, 50 * USEC_PER_MSEC, false, + dev, MT_TOP_WFSYS_RESET_STATUS); +} + +static int mt7986_wmac_wfsys_setting(struct mt7915_dev *dev) +{ + int ret; + u32 cur; + + /* Turn off wfsys2conn bus sleep protect */ + mt76_rmw(dev, MT_CONN_INFRA_WF_SLP_PROT, + MT_CONN_INFRA_WF_SLP_PROT_MASK, 0x0); + + ret = mt7986_wmac_wfsys_poweron(dev, true); + if (ret) + return ret; + + /* Check bus sleep protect */ + + ret = read_poll_timeout(mt76_rr, cur, + !(cur & MT_CONN_INFRA_CONN_WF_MASK), + USEC_PER_MSEC, 50 * USEC_PER_MSEC, false, + dev, MT_CONN_INFRA_WF_SLP_PROT_RDY); + if (ret) + return ret; + + ret = read_poll_timeout(mt76_rr, cur, !(cur & MT_SLP_WFDMA2CONN_MASK), + USEC_PER_MSEC, 50 * USEC_PER_MSEC, false, + dev, MT_SLP_STATUS); + if (ret) + return ret; + + return read_poll_timeout(mt76_rr, cur, (cur == 0x02060000), + USEC_PER_MSEC, 50 * USEC_PER_MSEC, false, + dev, MT_TOP_CFG_IP_VERSION_ADDR); +} + +static void mt7986_wmac_wfsys_set_timeout(struct mt7915_dev *dev) +{ + u32 mask = MT_MCU_BUS_TIMEOUT_SET_MASK | + MT_MCU_BUS_TIMEOUT_CG_EN_MASK | + MT_MCU_BUS_TIMEOUT_EN_MASK; + u32 val = FIELD_PREP(MT_MCU_BUS_TIMEOUT_SET_MASK, 1) | + FIELD_PREP(MT_MCU_BUS_TIMEOUT_CG_EN_MASK, 1) | + FIELD_PREP(MT_MCU_BUS_TIMEOUT_EN_MASK, 1); + + mt76_rmw(dev, MT_MCU_BUS_TIMEOUT, mask, val); + + mt76_wr(dev, MT_MCU_BUS_REMAP, 0x810f0000); + + mask = MT_MCU_BUS_DBG_TIMEOUT_SET_MASK | + MT_MCU_BUS_DBG_TIMEOUT_CK_EN_MASK | + MT_MCU_BUS_DBG_TIMEOUT_EN_MASK; + val = FIELD_PREP(MT_MCU_BUS_DBG_TIMEOUT_SET_MASK, 0x3aa) | + FIELD_PREP(MT_MCU_BUS_DBG_TIMEOUT_CK_EN_MASK, 1) | + FIELD_PREP(MT_MCU_BUS_DBG_TIMEOUT_EN_MASK, 1); + + mt76_rmw(dev, MT_MCU_BUS_DBG_TIMEOUT, mask, val); +} + +static int mt7986_wmac_sku_update(struct mt7915_dev *dev, u32 adie_type) +{ + u32 val; + + if (is_7976(dev, 0, adie_type) && is_7976(dev, 1, adie_type)) + val = 0xf; + else if (is_7975(dev, 0, adie_type) && is_7975(dev, 1, adie_type)) + val = 0xd; + else if (is_7976(dev, 0, adie_type)) + val = 0x7; + else if (is_7975(dev, 1, adie_type)) + val = 0x8; + else if (is_7976(dev, 1, adie_type)) + val = 0xa; + else + return -EINVAL; + + mt76_wmac_rmw(dev->sku, MT_TOP_POS_SKU, MT_TOP_POS_SKU_MASK, + FIELD_PREP(MT_TOP_POS_SKU_MASK, val)); + + mt76_wr(dev, MT_CONNINFRA_SKU_DEC_ADDR, val); + + return 0; +} + +static int +mt7986_wmac_adie_setup(struct mt7915_dev *dev, u8 adie, u32 adie_type) +{ + int ret; + + if (!(is_7975(dev, adie, adie_type) || is_7976(dev, adie, adie_type))) + return 0; + + ret = mt7986_wmac_adie_cfg(dev, adie, adie_type); + if (ret) + return ret; + + ret = mt7986_wmac_afe_cal(dev, adie, false, adie_type); + if (ret) + return ret; + + if (!adie && (mt798x_wmac_check_adie_type(dev) == ADIE_DBDC)) + ret = mt7986_wmac_afe_cal(dev, adie, true, adie_type); + + return ret; +} + +static int mt7986_wmac_subsys_powerup(struct mt7915_dev *dev, u32 adie_type) +{ + int ret; + + mt7986_wmac_subsys_setting(dev); + + ret = mt7986_wmac_bus_timeout(dev); + if (ret) + return ret; + + mt7986_wmac_clock_enable(dev, adie_type); + + return 0; +} + +static int mt7986_wmac_wfsys_powerup(struct mt7915_dev *dev) +{ + int ret; + + ret = mt7986_wmac_wm_enable(dev, false); + if (ret) + return ret; + + ret = mt7986_wmac_wfsys_setting(dev); + if (ret) + return ret; + + mt7986_wmac_wfsys_set_timeout(dev); + + return mt7986_wmac_wm_enable(dev, true); +} + +int mt7986_wmac_enable(struct mt7915_dev *dev) +{ + int ret; + u32 adie_type; + + ret = mt7986_wmac_consys_reset(dev, true); + if (ret) + return ret; + + ret = mt7986_wmac_gpio_setup(dev); + if (ret) + return ret; + + ret = mt7986_wmac_consys_lockup(dev, false); + if (ret) + return ret; + + ret = mt798x_wmac_coninfra_check(dev); + if (ret) + return ret; + + ret = mt798x_wmac_coninfra_setup(dev); + if (ret) + return ret; + + ret = mt798x_wmac_sku_setup(dev, &adie_type); + if (ret) + return ret; + + ret = mt7986_wmac_adie_setup(dev, 0, adie_type); + if (ret) + return ret; + + /* mt7981 doesn't support a second a-die */ + if (is_mt7986(&dev->mt76)) { + ret = mt7986_wmac_adie_setup(dev, 1, adie_type); + if (ret) + return ret; + } + + ret = mt7986_wmac_subsys_powerup(dev, adie_type); + if (ret) + return ret; + + ret = mt7986_wmac_top_wfsys_wakeup(dev, true); + if (ret) + return ret; + + ret = mt7986_wmac_wfsys_powerup(dev); + if (ret) + return ret; + + return mt7986_wmac_sku_update(dev, adie_type); +} + +void mt7986_wmac_disable(struct mt7915_dev *dev) +{ + u32 cur; + + mt7986_wmac_top_wfsys_wakeup(dev, true); + + /* Turn on wfsys2conn bus sleep protect */ + mt76_rmw_field(dev, MT_CONN_INFRA_WF_SLP_PROT, + MT_CONN_INFRA_WF_SLP_PROT_MASK, 0x1); + + /* Check wfsys2conn bus sleep protect */ + read_poll_timeout(mt76_rr, cur, !(cur ^ MT_CONN_INFRA_CONN), + USEC_PER_MSEC, 50 * USEC_PER_MSEC, false, + dev, MT_CONN_INFRA_WF_SLP_PROT_RDY); + + mt7986_wmac_wfsys_poweron(dev, false); + + /* Turn back wpll setting */ + mt76_rmw_field(dev, MT_AFE_DIG_EN_02(0), MT_AFE_MCU_BPLL_CFG_MASK, 0x2); + mt76_rmw_field(dev, MT_AFE_DIG_EN_02(0), MT_AFE_WPLL_CFG_MASK, 0x2); + + /* Reset EMI */ + mt76_rmw_field(dev, MT_CONN_INFRA_EMI_REQ, + MT_CONN_INFRA_EMI_REQ_MASK, 0x1); + mt76_rmw_field(dev, MT_CONN_INFRA_EMI_REQ, + MT_CONN_INFRA_EMI_REQ_MASK, 0x0); + mt76_rmw_field(dev, MT_CONN_INFRA_EMI_REQ, + MT_CONN_INFRA_INFRA_REQ_MASK, 0x1); + mt76_rmw_field(dev, MT_CONN_INFRA_EMI_REQ, + MT_CONN_INFRA_INFRA_REQ_MASK, 0x0); + + mt7986_wmac_top_wfsys_wakeup(dev, false); + mt7986_wmac_consys_lockup(dev, true); + mt7986_wmac_consys_reset(dev, false); +} + +static int mt798x_wmac_init(struct mt7915_dev *dev) +{ + struct device *pdev = dev->mt76.dev; + struct platform_device *pfdev = to_platform_device(pdev); + struct clk *mcu_clk, *ap_conn_clk; + + mcu_clk = devm_clk_get(pdev, "mcu"); + if (IS_ERR(mcu_clk)) + dev_err(pdev, "mcu clock not found\n"); + else if (clk_prepare_enable(mcu_clk)) + dev_err(pdev, "mcu clock configuration failed\n"); + + ap_conn_clk = devm_clk_get(pdev, "ap2conn"); + if (IS_ERR(ap_conn_clk)) + dev_err(pdev, "ap2conn clock not found\n"); + else if (clk_prepare_enable(ap_conn_clk)) + dev_err(pdev, "ap2conn clock configuration failed\n"); + + dev->dcm = devm_platform_ioremap_resource(pfdev, 1); + if (IS_ERR(dev->dcm)) + return PTR_ERR(dev->dcm); + + dev->sku = devm_platform_ioremap_resource(pfdev, 2); + if (IS_ERR(dev->sku)) + return PTR_ERR(dev->sku); + + dev->rstc = devm_reset_control_get(pdev, "consys"); + return PTR_ERR_OR_ZERO(dev->rstc); +} + +static int mt798x_wmac_probe(struct platform_device *pdev) +{ + void __iomem *mem_base; + struct mt7915_dev *dev; + struct mt76_dev *mdev; + int irq, ret; + u32 chip_id; + + chip_id = (uintptr_t)of_device_get_match_data(&pdev->dev); + + mem_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(mem_base)) { + dev_err(&pdev->dev, "Failed to get memory resource\n"); + return PTR_ERR(mem_base); + } + + dev = mt7915_mmio_probe(&pdev->dev, mem_base, chip_id); + if (IS_ERR(dev)) + return PTR_ERR(dev); + + mdev = &dev->mt76; + ret = mt7915_mmio_wed_init(dev, pdev, false, &irq); + if (ret < 0) + goto free_device; + + if (!ret) { + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + ret = irq; + goto free_device; + } + } + + ret = devm_request_irq(mdev->dev, irq, mt7915_irq_handler, + IRQF_SHARED, KBUILD_MODNAME, dev); + if (ret) + goto free_device; + + ret = mt798x_wmac_init(dev); + if (ret) + goto free_irq; + + mt7915_wfsys_reset(dev); + + ret = mt7915_register_device(dev); + if (ret) + goto free_irq; + + return 0; + +free_irq: + devm_free_irq(mdev->dev, irq, dev); +free_device: + if (mtk_wed_device_active(&mdev->mmio.wed)) + mtk_wed_device_detach(&mdev->mmio.wed); + mt76_free_device(mdev); + + return ret; +} + +static void mt798x_wmac_remove(struct platform_device *pdev) +{ + struct mt7915_dev *dev = platform_get_drvdata(pdev); + + mt7915_unregister_device(dev); +} + +static const struct of_device_id mt798x_wmac_of_match[] = { + { .compatible = "mediatek,mt7981-wmac", .data = (u32 *)0x7981 }, + { .compatible = "mediatek,mt7986-wmac", .data = (u32 *)0x7986 }, + {}, +}; + +MODULE_DEVICE_TABLE(of, mt798x_wmac_of_match); + +struct platform_driver mt798x_wmac_driver = { + .driver = { + .name = "mt798x-wmac", + .of_match_table = mt798x_wmac_of_match, + }, + .probe = mt798x_wmac_probe, + .remove = mt798x_wmac_remove, +}; + +MODULE_FIRMWARE(MT7986_FIRMWARE_WA); +MODULE_FIRMWARE(MT7986_FIRMWARE_WM); +MODULE_FIRMWARE(MT7986_FIRMWARE_WM_MT7975); +MODULE_FIRMWARE(MT7986_ROM_PATCH); +MODULE_FIRMWARE(MT7986_ROM_PATCH_MT7975); + +MODULE_FIRMWARE(MT7981_FIRMWARE_WA); +MODULE_FIRMWARE(MT7981_FIRMWARE_WM); +MODULE_FIRMWARE(MT7981_ROM_PATCH); diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c b/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c new file mode 100644 index 000000000..7576973f4 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7915/testmode.c @@ -0,0 +1,787 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2020 MediaTek Inc. */ + +#include "mt7915.h" +#include "mac.h" +#include "mcu.h" +#include "testmode.h" + +enum { + TM_CHANGED_TXPOWER, + TM_CHANGED_FREQ_OFFSET, + + /* must be last */ + NUM_TM_CHANGED +}; + +static const u8 tm_change_map[] = { + [TM_CHANGED_TXPOWER] = MT76_TM_ATTR_TX_POWER, + [TM_CHANGED_FREQ_OFFSET] = MT76_TM_ATTR_FREQ_OFFSET, +}; + +struct reg_band { + u32 band[2]; +}; + +#define REG_BAND(_list, _reg) \ + { _list.band[0] = MT_##_reg(0); \ + _list.band[1] = MT_##_reg(1); } +#define REG_BAND_IDX(_list, _reg, _idx) \ + { _list.band[0] = MT_##_reg(0, _idx); \ + _list.band[1] = MT_##_reg(1, _idx); } + +#define TM_REG_MAX_ID 17 +static struct reg_band reg_backup_list[TM_REG_MAX_ID]; + + +static int +mt7915_tm_set_tx_power(struct mt7915_phy *phy) +{ + struct mt7915_dev *dev = phy->dev; + struct mt76_phy *mphy = phy->mt76; + struct cfg80211_chan_def *chandef = &mphy->chandef; + int freq = chandef->center_freq1; + int ret; + struct { + u8 format_id; + u8 band_idx; + s8 tx_power; + u8 ant_idx; /* Only 0 is valid */ + u8 center_chan; + u8 rsv[3]; + } __packed req = { + .format_id = 0xf, + .band_idx = phy->mt76->band_idx, + .center_chan = ieee80211_frequency_to_channel(freq), + }; + u8 *tx_power = NULL; + + if (phy->mt76->test.state != MT76_TM_STATE_OFF) + tx_power = phy->mt76->test.tx_power; + + /* Tx power of the other antennas are the same as antenna 0 */ + if (tx_power && tx_power[0]) + req.tx_power = tx_power[0]; + + ret = mt76_mcu_send_msg(&dev->mt76, + MCU_EXT_CMD(TX_POWER_FEATURE_CTRL), + &req, sizeof(req), false); + + return ret; +} + +static int +mt7915_tm_set_freq_offset(struct mt7915_phy *phy, bool en, u32 val) +{ + struct mt7915_dev *dev = phy->dev; + struct mt7915_tm_cmd req = { + .testmode_en = en, + .param_idx = MCU_ATE_SET_FREQ_OFFSET, + .param.freq.band = phy->mt76->band_idx, + .param.freq.freq_offset = cpu_to_le32(val), + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(ATE_CTRL), &req, + sizeof(req), false); +} + +static int +mt7915_tm_mode_ctrl(struct mt7915_dev *dev, bool enable) +{ + struct { + u8 format_id; + bool enable; + u8 rsv[2]; + } __packed req = { + .format_id = 0x6, + .enable = enable, + }; + + return mt76_mcu_send_msg(&dev->mt76, + MCU_EXT_CMD(TX_POWER_FEATURE_CTRL), + &req, sizeof(req), false); +} + +static int +mt7915_tm_set_trx(struct mt7915_phy *phy, int type, bool en) +{ + struct mt7915_dev *dev = phy->dev; + struct mt7915_tm_cmd req = { + .testmode_en = 1, + .param_idx = MCU_ATE_SET_TRX, + .param.trx.type = type, + .param.trx.enable = en, + .param.trx.band = phy->mt76->band_idx, + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(ATE_CTRL), &req, + sizeof(req), false); +} + +static int +mt7915_tm_clean_hwq(struct mt7915_phy *phy, u8 wcid) +{ + struct mt7915_dev *dev = phy->dev; + struct mt7915_tm_cmd req = { + .testmode_en = 1, + .param_idx = MCU_ATE_CLEAN_TXQUEUE, + .param.clean.wcid = wcid, + .param.clean.band = phy->mt76->band_idx, + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(ATE_CTRL), &req, + sizeof(req), false); +} + +static int +mt7915_tm_set_slot_time(struct mt7915_phy *phy, u8 slot_time, u8 sifs) +{ + struct mt7915_dev *dev = phy->dev; + struct mt7915_tm_cmd req = { + .testmode_en = !(phy->mt76->test.state == MT76_TM_STATE_OFF), + .param_idx = MCU_ATE_SET_SLOT_TIME, + .param.slot.slot_time = slot_time, + .param.slot.sifs = sifs, + .param.slot.rifs = 2, + .param.slot.eifs = cpu_to_le16(60), + .param.slot.band = phy->mt76->band_idx, + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(ATE_CTRL), &req, + sizeof(req), false); +} + +static int +mt7915_tm_set_tam_arb(struct mt7915_phy *phy, bool enable, bool mu) +{ + struct mt7915_dev *dev = phy->dev; + u32 op_mode; + + if (!enable) + op_mode = TAM_ARB_OP_MODE_NORMAL; + else if (mu) + op_mode = TAM_ARB_OP_MODE_TEST; + else + op_mode = TAM_ARB_OP_MODE_FORCE_SU; + + return mt7915_mcu_set_muru_ctrl(dev, MURU_SET_ARB_OP_MODE, op_mode); +} + +static int +mt7915_tm_set_wmm_qid(struct mt7915_phy *phy, u8 qid, u8 aifs, u8 cw_min, + u16 cw_max, u16 txop) +{ + struct mt7915_vif *mvif = (struct mt7915_vif *)phy->monitor_vif->drv_priv; + struct mt7915_mcu_tx req = { .total = 1 }; + struct edca *e = &req.edca[0]; + + e->queue = qid + mvif->mt76.wmm_idx * MT76_CONNAC_MAX_WMM_SETS; + e->set = WMM_PARAM_SET; + + e->aifs = aifs; + e->cw_min = cw_min; + e->cw_max = cpu_to_le16(cw_max); + e->txop = cpu_to_le16(txop); + + return mt7915_mcu_update_edca(phy->dev, &req); +} + +static int +mt7915_tm_set_ipg_params(struct mt7915_phy *phy, u32 ipg, u8 mode) +{ +#define TM_DEFAULT_SIFS 10 +#define TM_MAX_SIFS 127 +#define TM_MAX_AIFSN 0xf +#define TM_MIN_AIFSN 0x1 +#define BBP_PROC_TIME 1500 + struct mt7915_dev *dev = phy->dev; + u8 sig_ext = (mode == MT76_TM_TX_MODE_CCK) ? 0 : 6; + u8 slot_time = 9, sifs = TM_DEFAULT_SIFS; + u8 aifsn = TM_MIN_AIFSN; + u8 band = phy->mt76->band_idx; + u32 i2t_time, tr2t_time, txv_time; + u16 cw = 0; + + if (ipg < sig_ext + slot_time + sifs) + ipg = 0; + + if (!ipg) + goto done; + + ipg -= sig_ext; + + if (ipg <= (TM_MAX_SIFS + slot_time)) { + sifs = ipg - slot_time; + } else { + u32 val = (ipg + slot_time) / slot_time; + + while (val >>= 1) + cw++; + + if (cw > 16) + cw = 16; + + ipg -= ((1 << cw) - 1) * slot_time; + + aifsn = ipg / slot_time; + if (aifsn > TM_MAX_AIFSN) + aifsn = TM_MAX_AIFSN; + + ipg -= aifsn * slot_time; + + if (ipg > TM_DEFAULT_SIFS) + sifs = min_t(u32, ipg, TM_MAX_SIFS); + } +done: + txv_time = mt76_get_field(dev, MT_TMAC_ATCR(band), + MT_TMAC_ATCR_TXV_TOUT); + txv_time *= 50; /* normal clock time */ + + i2t_time = (slot_time * 1000 - txv_time - BBP_PROC_TIME) / 50; + tr2t_time = (sifs * 1000 - txv_time - BBP_PROC_TIME) / 50; + + mt76_set(dev, MT_TMAC_TRCR0(band), + FIELD_PREP(MT_TMAC_TRCR0_TR2T_CHK, tr2t_time) | + FIELD_PREP(MT_TMAC_TRCR0_I2T_CHK, i2t_time)); + + mt7915_tm_set_slot_time(phy, slot_time, sifs); + + return mt7915_tm_set_wmm_qid(phy, + mt76_connac_lmac_mapping(IEEE80211_AC_BE), + aifsn, cw, cw, 0); +} + +static int +mt7915_tm_set_tx_len(struct mt7915_phy *phy, u32 tx_time) +{ + struct mt76_phy *mphy = phy->mt76; + struct mt76_testmode_data *td = &mphy->test; + struct ieee80211_supported_band *sband; + struct rate_info rate = {}; + u16 flags = 0, tx_len; + u32 bitrate; + int ret; + + if (!tx_time) + return 0; + + rate.mcs = td->tx_rate_idx; + rate.nss = td->tx_rate_nss; + + switch (td->tx_rate_mode) { + case MT76_TM_TX_MODE_CCK: + case MT76_TM_TX_MODE_OFDM: + if (mphy->chandef.chan->band == NL80211_BAND_5GHZ) + sband = &mphy->sband_5g.sband; + else if (mphy->chandef.chan->band == NL80211_BAND_6GHZ) + sband = &mphy->sband_6g.sband; + else + sband = &mphy->sband_2g.sband; + + rate.legacy = sband->bitrates[rate.mcs].bitrate; + break; + case MT76_TM_TX_MODE_HT: + rate.mcs += rate.nss * 8; + flags |= RATE_INFO_FLAGS_MCS; + + if (td->tx_rate_sgi) + flags |= RATE_INFO_FLAGS_SHORT_GI; + break; + case MT76_TM_TX_MODE_VHT: + flags |= RATE_INFO_FLAGS_VHT_MCS; + + if (td->tx_rate_sgi) + flags |= RATE_INFO_FLAGS_SHORT_GI; + break; + case MT76_TM_TX_MODE_HE_SU: + case MT76_TM_TX_MODE_HE_EXT_SU: + case MT76_TM_TX_MODE_HE_TB: + case MT76_TM_TX_MODE_HE_MU: + rate.he_gi = td->tx_rate_sgi; + flags |= RATE_INFO_FLAGS_HE_MCS; + break; + default: + break; + } + rate.flags = flags; + + switch (mphy->chandef.width) { + case NL80211_CHAN_WIDTH_160: + case NL80211_CHAN_WIDTH_80P80: + rate.bw = RATE_INFO_BW_160; + break; + case NL80211_CHAN_WIDTH_80: + rate.bw = RATE_INFO_BW_80; + break; + case NL80211_CHAN_WIDTH_40: + rate.bw = RATE_INFO_BW_40; + break; + default: + rate.bw = RATE_INFO_BW_20; + break; + } + + bitrate = cfg80211_calculate_bitrate(&rate); + tx_len = bitrate * tx_time / 10 / 8; + + ret = mt76_testmode_alloc_skb(phy->mt76, tx_len); + if (ret) + return ret; + + return 0; +} + +static void +mt7915_tm_reg_backup_restore(struct mt7915_phy *phy) +{ + int n_regs = ARRAY_SIZE(reg_backup_list); + struct mt7915_dev *dev = phy->dev; + u32 *b = phy->test.reg_backup; + u8 band = phy->mt76->band_idx; + int i; + + REG_BAND_IDX(reg_backup_list[0], AGG_PCR0, 0); + REG_BAND_IDX(reg_backup_list[1], AGG_PCR0, 1); + REG_BAND_IDX(reg_backup_list[2], AGG_AWSCR0, 0); + REG_BAND_IDX(reg_backup_list[3], AGG_AWSCR0, 1); + REG_BAND_IDX(reg_backup_list[4], AGG_AWSCR0, 2); + REG_BAND_IDX(reg_backup_list[5], AGG_AWSCR0, 3); + REG_BAND(reg_backup_list[6], AGG_MRCR); + REG_BAND(reg_backup_list[7], TMAC_TFCR0); + REG_BAND(reg_backup_list[8], TMAC_TCR0); + REG_BAND(reg_backup_list[9], AGG_ATCR1); + REG_BAND(reg_backup_list[10], AGG_ATCR3); + REG_BAND(reg_backup_list[11], TMAC_TRCR0); + REG_BAND(reg_backup_list[12], TMAC_ICR0); + REG_BAND_IDX(reg_backup_list[13], ARB_DRNGR0, 0); + REG_BAND_IDX(reg_backup_list[14], ARB_DRNGR0, 1); + REG_BAND(reg_backup_list[15], WF_RFCR); + REG_BAND(reg_backup_list[16], WF_RFCR1); + + if (phy->mt76->test.state == MT76_TM_STATE_OFF) { + for (i = 0; i < n_regs; i++) + mt76_wr(dev, reg_backup_list[i].band[band], b[i]); + return; + } + + if (!b) { + b = devm_kzalloc(dev->mt76.dev, 4 * n_regs, GFP_KERNEL); + if (!b) + return; + + phy->test.reg_backup = b; + for (i = 0; i < n_regs; i++) + b[i] = mt76_rr(dev, reg_backup_list[i].band[band]); + } + + mt76_clear(dev, MT_AGG_PCR0(band, 0), MT_AGG_PCR0_MM_PROT | + MT_AGG_PCR0_GF_PROT | MT_AGG_PCR0_ERP_PROT | + MT_AGG_PCR0_VHT_PROT | MT_AGG_PCR0_BW20_PROT | + MT_AGG_PCR0_BW40_PROT | MT_AGG_PCR0_BW80_PROT); + mt76_set(dev, MT_AGG_PCR0(band, 0), MT_AGG_PCR0_PTA_WIN_DIS); + + mt76_wr(dev, MT_AGG_PCR0(band, 1), MT_AGG_PCR1_RTS0_NUM_THRES | + MT_AGG_PCR1_RTS0_LEN_THRES); + + mt76_clear(dev, MT_AGG_MRCR(band), MT_AGG_MRCR_BAR_CNT_LIMIT | + MT_AGG_MRCR_LAST_RTS_CTS_RN | MT_AGG_MRCR_RTS_FAIL_LIMIT | + MT_AGG_MRCR_TXCMD_RTS_FAIL_LIMIT); + + mt76_rmw(dev, MT_AGG_MRCR(band), MT_AGG_MRCR_RTS_FAIL_LIMIT | + MT_AGG_MRCR_TXCMD_RTS_FAIL_LIMIT, + FIELD_PREP(MT_AGG_MRCR_RTS_FAIL_LIMIT, 1) | + FIELD_PREP(MT_AGG_MRCR_TXCMD_RTS_FAIL_LIMIT, 1)); + + mt76_wr(dev, MT_TMAC_TFCR0(band), 0); + mt76_clear(dev, MT_TMAC_TCR0(band), MT_TMAC_TCR0_TBTT_STOP_CTRL); + + /* config rx filter for testmode rx */ + mt76_wr(dev, MT_WF_RFCR(band), 0xcf70a); + mt76_wr(dev, MT_WF_RFCR1(band), 0); +} + +static void +mt7915_tm_init(struct mt7915_phy *phy, bool en) +{ + struct mt7915_dev *dev = phy->dev; + int state; + + if (!test_bit(MT76_STATE_RUNNING, &phy->mt76->state)) + return; + + mt7915_mcu_set_sku_en(phy); + + mt7915_tm_mode_ctrl(dev, en); + mt7915_tm_reg_backup_restore(phy); + mt7915_tm_set_trx(phy, TM_MAC_TXRX, !en); + + mt7915_mcu_add_bss_info(phy, phy->monitor_vif, en); + state = en ? CONN_STATE_PORT_SECURE : CONN_STATE_DISCONNECT; + mt7915_mcu_add_sta(dev, phy->monitor_vif, NULL, state, true); + + if (!en) + mt7915_tm_set_tam_arb(phy, en, 0); +} + +static void +mt7915_tm_update_channel(struct mt7915_phy *phy) +{ + mutex_unlock(&phy->dev->mt76.mutex); + mt76_update_channel(phy->mt76); + mutex_lock(&phy->dev->mt76.mutex); + + mt7915_mcu_set_chan_info(phy, MCU_EXT_CMD(SET_RX_PATH)); +} + +static void +mt7915_tm_set_tx_frames(struct mt7915_phy *phy, bool en) +{ + struct mt76_testmode_data *td = &phy->mt76->test; + struct mt7915_dev *dev = phy->dev; + struct ieee80211_tx_info *info; + u8 duty_cycle = td->tx_duty_cycle; + u32 tx_time = td->tx_time; + u32 ipg = td->tx_ipg; + + mt7915_tm_set_trx(phy, TM_MAC_RX_RXV, false); + mt7915_tm_clean_hwq(phy, dev->mt76.global_wcid.idx); + + if (en) { + mt7915_tm_update_channel(phy); + + if (td->tx_spe_idx) + phy->test.spe_idx = td->tx_spe_idx; + else + phy->test.spe_idx = mt76_connac_spe_idx(td->tx_antenna_mask); + } + + mt7915_tm_set_tam_arb(phy, en, + td->tx_rate_mode == MT76_TM_TX_MODE_HE_MU); + + /* if all three params are set, duty_cycle will be ignored */ + if (duty_cycle && tx_time && !ipg) { + ipg = tx_time * 100 / duty_cycle - tx_time; + } else if (duty_cycle && !tx_time && ipg) { + if (duty_cycle < 100) + tx_time = duty_cycle * ipg / (100 - duty_cycle); + } + + mt7915_tm_set_ipg_params(phy, ipg, td->tx_rate_mode); + mt7915_tm_set_tx_len(phy, tx_time); + + if (ipg) + td->tx_queued_limit = MT76_TM_TIMEOUT * 1000000 / ipg / 2; + + if (!en || !td->tx_skb) + return; + + info = IEEE80211_SKB_CB(td->tx_skb); + info->control.vif = phy->monitor_vif; + + mt7915_tm_set_trx(phy, TM_MAC_TX, en); +} + +static void +mt7915_tm_set_rx_frames(struct mt7915_phy *phy, bool en) +{ + mt7915_tm_set_trx(phy, TM_MAC_RX_RXV, false); + + if (en) { + struct mt7915_dev *dev = phy->dev; + + mt7915_tm_update_channel(phy); + + /* read-clear */ + mt76_rr(dev, MT_MIB_SDR3(phy->mt76->band_idx)); + mt7915_tm_set_trx(phy, TM_MAC_RX_RXV, en); + } +} + +static int +mt7915_tm_rf_switch_mode(struct mt7915_dev *dev, u32 oper) +{ + struct mt7915_tm_rf_test req = { + .op.op_mode = cpu_to_le32(oper), + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(RF_TEST), &req, + sizeof(req), true); +} + +static int +mt7915_tm_set_tx_cont(struct mt7915_phy *phy, bool en) +{ +#define TX_CONT_START 0x05 +#define TX_CONT_STOP 0x06 + struct mt7915_dev *dev = phy->dev; + struct cfg80211_chan_def *chandef = &phy->mt76->chandef; + int freq1 = ieee80211_frequency_to_channel(chandef->center_freq1); + struct mt76_testmode_data *td = &phy->mt76->test; + u32 func_idx = en ? TX_CONT_START : TX_CONT_STOP; + u8 rate_idx = td->tx_rate_idx, mode; + u8 band = phy->mt76->band_idx; + u16 rateval; + struct mt7915_tm_rf_test req = { + .action = 1, + .icap_len = 120, + .op.rf.func_idx = cpu_to_le32(func_idx), + }; + struct tm_tx_cont *tx_cont = &req.op.rf.param.tx_cont; + + tx_cont->control_ch = chandef->chan->hw_value; + tx_cont->center_ch = freq1; + tx_cont->tx_ant = td->tx_antenna_mask; + tx_cont->band = band; + + switch (chandef->width) { + case NL80211_CHAN_WIDTH_40: + tx_cont->bw = CMD_CBW_40MHZ; + break; + case NL80211_CHAN_WIDTH_80: + tx_cont->bw = CMD_CBW_80MHZ; + break; + case NL80211_CHAN_WIDTH_80P80: + tx_cont->bw = CMD_CBW_8080MHZ; + break; + case NL80211_CHAN_WIDTH_160: + tx_cont->bw = CMD_CBW_160MHZ; + break; + case NL80211_CHAN_WIDTH_5: + tx_cont->bw = CMD_CBW_5MHZ; + break; + case NL80211_CHAN_WIDTH_10: + tx_cont->bw = CMD_CBW_10MHZ; + break; + case NL80211_CHAN_WIDTH_20: + tx_cont->bw = CMD_CBW_20MHZ; + break; + case NL80211_CHAN_WIDTH_20_NOHT: + tx_cont->bw = CMD_CBW_20MHZ; + break; + default: + return -EINVAL; + } + + if (!en) { + req.op.rf.param.func_data = cpu_to_le32(band); + goto out; + } + + if (td->tx_rate_mode <= MT76_TM_TX_MODE_OFDM) { + struct ieee80211_supported_band *sband; + u8 idx = rate_idx; + + if (chandef->chan->band == NL80211_BAND_5GHZ) + sband = &phy->mt76->sband_5g.sband; + else if (chandef->chan->band == NL80211_BAND_6GHZ) + sband = &phy->mt76->sband_6g.sband; + else + sband = &phy->mt76->sband_2g.sband; + + if (td->tx_rate_mode == MT76_TM_TX_MODE_OFDM) + idx += 4; + rate_idx = sband->bitrates[idx].hw_value & 0xff; + } + + switch (td->tx_rate_mode) { + case MT76_TM_TX_MODE_CCK: + mode = MT_PHY_TYPE_CCK; + break; + case MT76_TM_TX_MODE_OFDM: + mode = MT_PHY_TYPE_OFDM; + break; + case MT76_TM_TX_MODE_HT: + mode = MT_PHY_TYPE_HT; + break; + case MT76_TM_TX_MODE_VHT: + mode = MT_PHY_TYPE_VHT; + break; + case MT76_TM_TX_MODE_HE_SU: + mode = MT_PHY_TYPE_HE_SU; + break; + case MT76_TM_TX_MODE_HE_EXT_SU: + mode = MT_PHY_TYPE_HE_EXT_SU; + break; + case MT76_TM_TX_MODE_HE_TB: + mode = MT_PHY_TYPE_HE_TB; + break; + case MT76_TM_TX_MODE_HE_MU: + mode = MT_PHY_TYPE_HE_MU; + break; + default: + return -EINVAL; + } + + rateval = mode << 6 | rate_idx; + tx_cont->rateval = cpu_to_le16(rateval); + +out: + if (!en) { + int ret; + + ret = mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(RF_TEST), &req, + sizeof(req), true); + if (ret) + return ret; + + return mt7915_tm_rf_switch_mode(dev, RF_OPER_NORMAL); + } + + mt7915_tm_rf_switch_mode(dev, RF_OPER_RF_TEST); + mt7915_tm_update_channel(phy); + + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(RF_TEST), &req, + sizeof(req), true); +} + +static void +mt7915_tm_update_params(struct mt7915_phy *phy, u32 changed) +{ + struct mt76_testmode_data *td = &phy->mt76->test; + bool en = phy->mt76->test.state != MT76_TM_STATE_OFF; + + if (changed & BIT(TM_CHANGED_FREQ_OFFSET)) + mt7915_tm_set_freq_offset(phy, en, en ? td->freq_offset : 0); + if (changed & BIT(TM_CHANGED_TXPOWER)) + mt7915_tm_set_tx_power(phy); +} + +static int +mt7915_tm_set_state(struct mt76_phy *mphy, enum mt76_testmode_state state) +{ + struct mt76_testmode_data *td = &mphy->test; + struct mt7915_phy *phy = mphy->priv; + enum mt76_testmode_state prev_state = td->state; + + mphy->test.state = state; + + if (prev_state == MT76_TM_STATE_TX_FRAMES || + state == MT76_TM_STATE_TX_FRAMES) + mt7915_tm_set_tx_frames(phy, state == MT76_TM_STATE_TX_FRAMES); + else if (prev_state == MT76_TM_STATE_RX_FRAMES || + state == MT76_TM_STATE_RX_FRAMES) + mt7915_tm_set_rx_frames(phy, state == MT76_TM_STATE_RX_FRAMES); + else if (prev_state == MT76_TM_STATE_TX_CONT || + state == MT76_TM_STATE_TX_CONT) + mt7915_tm_set_tx_cont(phy, state == MT76_TM_STATE_TX_CONT); + else if (prev_state == MT76_TM_STATE_OFF || + state == MT76_TM_STATE_OFF) + mt7915_tm_init(phy, !(state == MT76_TM_STATE_OFF)); + + if ((state == MT76_TM_STATE_IDLE && + prev_state == MT76_TM_STATE_OFF) || + (state == MT76_TM_STATE_OFF && + prev_state == MT76_TM_STATE_IDLE)) { + u32 changed = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(tm_change_map); i++) { + u16 cur = tm_change_map[i]; + + if (td->param_set[cur / 32] & BIT(cur % 32)) + changed |= BIT(i); + } + + mt7915_tm_update_params(phy, changed); + } + + return 0; +} + +static int +mt7915_tm_set_params(struct mt76_phy *mphy, struct nlattr **tb, + enum mt76_testmode_state new_state) +{ + struct mt76_testmode_data *td = &mphy->test; + struct mt7915_phy *phy = mphy->priv; + u32 chainmask = mt7915_band_chainmask(phy), changed = 0; + int i; + + BUILD_BUG_ON(NUM_TM_CHANGED >= 32); + + if (new_state == MT76_TM_STATE_OFF || + td->state == MT76_TM_STATE_OFF) + return 0; + + if (td->tx_antenna_mask > chainmask) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(tm_change_map); i++) { + if (tb[tm_change_map[i]]) + changed |= BIT(i); + } + + mt7915_tm_update_params(phy, changed); + + return 0; +} + +static int +mt7915_tm_dump_stats(struct mt76_phy *mphy, struct sk_buff *msg) +{ + struct mt7915_phy *phy = mphy->priv; + struct mt7915_dev *dev = phy->dev; + enum mt76_rxq_id q; + void *rx, *rssi; + u16 fcs_err; + int i; + u32 cnt; + + rx = nla_nest_start(msg, MT76_TM_STATS_ATTR_LAST_RX); + if (!rx) + return -ENOMEM; + + if (nla_put_s32(msg, MT76_TM_RX_ATTR_FREQ_OFFSET, phy->test.last_freq_offset)) + return -ENOMEM; + + rssi = nla_nest_start(msg, MT76_TM_RX_ATTR_RCPI); + if (!rssi) + return -ENOMEM; + + for (i = 0; i < ARRAY_SIZE(phy->test.last_rcpi); i++) + if (nla_put_u8(msg, i, phy->test.last_rcpi[i])) + return -ENOMEM; + + nla_nest_end(msg, rssi); + + rssi = nla_nest_start(msg, MT76_TM_RX_ATTR_IB_RSSI); + if (!rssi) + return -ENOMEM; + + for (i = 0; i < ARRAY_SIZE(phy->test.last_ib_rssi); i++) + if (nla_put_s8(msg, i, phy->test.last_ib_rssi[i])) + return -ENOMEM; + + nla_nest_end(msg, rssi); + + rssi = nla_nest_start(msg, MT76_TM_RX_ATTR_WB_RSSI); + if (!rssi) + return -ENOMEM; + + for (i = 0; i < ARRAY_SIZE(phy->test.last_wb_rssi); i++) + if (nla_put_s8(msg, i, phy->test.last_wb_rssi[i])) + return -ENOMEM; + + nla_nest_end(msg, rssi); + + if (nla_put_u8(msg, MT76_TM_RX_ATTR_SNR, phy->test.last_snr)) + return -ENOMEM; + + nla_nest_end(msg, rx); + + cnt = mt76_rr(dev, MT_MIB_SDR3(phy->mt76->band_idx)); + fcs_err = is_mt7915(&dev->mt76) ? FIELD_GET(MT_MIB_SDR3_FCS_ERR_MASK, cnt) : + FIELD_GET(MT_MIB_SDR3_FCS_ERR_MASK_MT7916, cnt); + + q = phy->mt76->band_idx ? MT_RXQ_BAND1 : MT_RXQ_MAIN; + mphy->test.rx_stats.packets[q] += fcs_err; + mphy->test.rx_stats.fcs_error[q] += fcs_err; + + return 0; +} + +const struct mt76_testmode_ops mt7915_testmode_ops = { + .set_state = mt7915_tm_set_state, + .set_params = mt7915_tm_set_params, + .dump_stats = mt7915_tm_dump_stats, +}; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/testmode.h b/drivers/net/wireless/mediatek/mt76/mt7915/testmode.h new file mode 100644 index 000000000..bb1bc5687 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7915/testmode.h @@ -0,0 +1,105 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* Copyright (C) 2020 MediaTek Inc. */ + +#ifndef __MT7915_TESTMODE_H +#define __MT7915_TESTMODE_H + +struct mt7915_tm_trx { + u8 type; + u8 enable; + u8 band; + u8 rsv; +}; + +struct mt7915_tm_freq_offset { + u8 band; + __le32 freq_offset; +}; + +struct mt7915_tm_slot_time { + u8 slot_time; + u8 sifs; + u8 rifs; + u8 _rsv; + __le16 eifs; + u8 band; + u8 _rsv1[5]; +}; + +struct mt7915_tm_clean_txq { + bool sta_pause; + u8 wcid; /* 256 sta */ + u8 band; + u8 rsv; +}; + +struct mt7915_tm_cmd { + u8 testmode_en; + u8 param_idx; + u8 _rsv[2]; + union { + __le32 data; + struct mt7915_tm_trx trx; + struct mt7915_tm_freq_offset freq; + struct mt7915_tm_slot_time slot; + struct mt7915_tm_clean_txq clean; + u8 test[72]; + } param; +} __packed; + +enum { + TM_MAC_TX = 1, + TM_MAC_RX, + TM_MAC_TXRX, + TM_MAC_TXRX_RXV, + TM_MAC_RXV, + TM_MAC_RX_RXV, +}; + +struct tm_tx_cont { + u8 control_ch; + u8 center_ch; + u8 bw; + u8 tx_ant; + __le16 rateval; + u8 band; + u8 txfd_mode; +}; + +struct mt7915_tm_rf_test { + u8 action; + u8 icap_len; + u8 _rsv[2]; + union { + __le32 op_mode; + __le32 freq; + + struct { + __le32 func_idx; + union { + __le32 func_data; + __le32 cal_dump; + + struct tm_tx_cont tx_cont; + + u8 _pad[80]; + } param; + } rf; + } op; +} __packed; + +enum { + RF_OPER_NORMAL, + RF_OPER_RF_TEST, + RF_OPER_ICAP, + RF_OPER_ICAP_OVERLAP, + RF_OPER_WIFI_SPECTRUM, +}; + +enum { + TAM_ARB_OP_MODE_NORMAL = 1, + TAM_ARB_OP_MODE_TEST, + TAM_ARB_OP_MODE_FORCE_SU = 5, +}; + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/Kconfig b/drivers/net/wireless/mediatek/mt76/mt7921/Kconfig new file mode 100644 index 000000000..37b5f46e7 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7921/Kconfig @@ -0,0 +1,37 @@ +# SPDX-License-Identifier: BSD-3-Clause-Clear +config MT7921_COMMON + tristate + select MT792x_LIB + select WANT_DEV_COREDUMP + +config MT7921E + tristate "MediaTek MT7921E (PCIe) support" + select MT7921_COMMON + depends on MAC80211 + depends on PCI + help + This adds support for MT7921E 802.11ax 2x2:2SS wireless devices. + + To compile this driver as a module, choose M here. + +config MT7921S + tristate "MediaTek MT7921S (SDIO) support" + select MT76_SDIO + select MT7921_COMMON + depends on MAC80211 + depends on MMC + help + This adds support for MT7921S 802.11ax 2x2:2SS wireless devices. + + To compile this driver as a module, choose M here. + +config MT7921U + tristate "MediaTek MT7921U (USB) support" + select MT792x_USB + select MT7921_COMMON + depends on MAC80211 + depends on USB + help + This adds support for MT7921U 802.11ax 2x2:2SS wireless devices. + + To compile this driver as a module, choose M here. diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/Makefile b/drivers/net/wireless/mediatek/mt76/mt7921/Makefile new file mode 100644 index 000000000..3ef7c9c45 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7921/Makefile @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: BSD-3-Clause-Clear + +obj-$(CONFIG_MT7921_COMMON) += mt7921-common.o +obj-$(CONFIG_MT7921E) += mt7921e.o +obj-$(CONFIG_MT7921S) += mt7921s.o +obj-$(CONFIG_MT7921U) += mt7921u.o + +mt7921-common-y := mac.o mcu.o regd.o main.o init.o debugfs.o +mt7921-common-$(CONFIG_NL80211_TESTMODE) += testmode.o +mt7921e-y := pci.o pci_mac.o pci_mcu.o +mt7921s-y := sdio.o sdio_mac.o sdio_mcu.o +mt7921u-y := usb.o diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7921/debugfs.c new file mode 100644 index 000000000..a5a70d8e8 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7921/debugfs.c @@ -0,0 +1,294 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2020 MediaTek Inc. */ + +#include "mt7921.h" + +static int +mt7921_reg_set(void *data, u64 val) +{ + struct mt792x_dev *dev = data; + + mt792x_mutex_acquire(dev); + mt76_wr(dev, dev->mt76.debugfs_reg, val); + mt792x_mutex_release(dev); + + return 0; +} + +static int +mt7921_reg_get(void *data, u64 *val) +{ + struct mt792x_dev *dev = data; + + mt792x_mutex_acquire(dev); + *val = mt76_rr(dev, dev->mt76.debugfs_reg); + mt792x_mutex_release(dev); + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_regval, mt7921_reg_get, mt7921_reg_set, + "0x%08llx\n"); +static int +mt7921_fw_debug_set(void *data, u64 val) +{ + struct mt792x_dev *dev = data; + + mt792x_mutex_acquire(dev); + + dev->fw_debug = (u8)val; + mt7921_mcu_fw_log_2_host(dev, dev->fw_debug); + + mt792x_mutex_release(dev); + + return 0; +} + +static int +mt7921_fw_debug_get(void *data, u64 *val) +{ + struct mt792x_dev *dev = data; + + *val = dev->fw_debug; + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_fw_debug, mt7921_fw_debug_get, + mt7921_fw_debug_set, "%lld\n"); + +DEFINE_SHOW_ATTRIBUTE(mt792x_tx_stats); + +static void +mt7921_seq_puts_array(struct seq_file *file, const char *str, + s8 *val, int len) +{ + int i; + + seq_printf(file, "%-16s:", str); + for (i = 0; i < len; i++) + if (val[i] == 127) + seq_printf(file, " %6s", "N.A"); + else + seq_printf(file, " %6d", val[i]); + seq_puts(file, "\n"); +} + +#define mt7921_print_txpwr_entry(prefix, rate) \ +({ \ + mt7921_seq_puts_array(s, #prefix " (user)", \ + txpwr.data[TXPWR_USER].rate, \ + ARRAY_SIZE(txpwr.data[TXPWR_USER].rate)); \ + mt7921_seq_puts_array(s, #prefix " (eeprom)", \ + txpwr.data[TXPWR_EEPROM].rate, \ + ARRAY_SIZE(txpwr.data[TXPWR_EEPROM].rate)); \ + mt7921_seq_puts_array(s, #prefix " (tmac)", \ + txpwr.data[TXPWR_MAC].rate, \ + ARRAY_SIZE(txpwr.data[TXPWR_MAC].rate)); \ +}) + +static int +mt7921_txpwr(struct seq_file *s, void *data) +{ + struct mt792x_dev *dev = dev_get_drvdata(s->private); + struct mt7921_txpwr txpwr; + int ret; + + mt792x_mutex_acquire(dev); + ret = mt7921_get_txpwr_info(dev, &txpwr); + mt792x_mutex_release(dev); + + if (ret) + return ret; + + seq_printf(s, "Tx power table (channel %d)\n", txpwr.ch); + seq_printf(s, "%-16s %6s %6s %6s %6s\n", + " ", "1m", "2m", "5m", "11m"); + mt7921_print_txpwr_entry(CCK, cck); + + seq_printf(s, "%-16s %6s %6s %6s %6s %6s %6s %6s %6s\n", + " ", "6m", "9m", "12m", "18m", "24m", "36m", + "48m", "54m"); + mt7921_print_txpwr_entry(OFDM, ofdm); + + seq_printf(s, "%-16s %6s %6s %6s %6s %6s %6s %6s %6s\n", + " ", "mcs0", "mcs1", "mcs2", "mcs3", "mcs4", "mcs5", + "mcs6", "mcs7"); + mt7921_print_txpwr_entry(HT20, ht20); + + seq_printf(s, "%-16s %6s %6s %6s %6s %6s %6s %6s %6s %6s\n", + " ", "mcs0", "mcs1", "mcs2", "mcs3", "mcs4", "mcs5", + "mcs6", "mcs7", "mcs32"); + mt7921_print_txpwr_entry(HT40, ht40); + + seq_printf(s, "%-16s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s\n", + " ", "mcs0", "mcs1", "mcs2", "mcs3", "mcs4", "mcs5", + "mcs6", "mcs7", "mcs8", "mcs9", "mcs10", "mcs11"); + mt7921_print_txpwr_entry(VHT20, vht20); + mt7921_print_txpwr_entry(VHT40, vht40); + mt7921_print_txpwr_entry(VHT80, vht80); + mt7921_print_txpwr_entry(VHT160, vht160); + mt7921_print_txpwr_entry(HE26, he26); + mt7921_print_txpwr_entry(HE52, he52); + mt7921_print_txpwr_entry(HE106, he106); + mt7921_print_txpwr_entry(HE242, he242); + mt7921_print_txpwr_entry(HE484, he484); + mt7921_print_txpwr_entry(HE996, he996); + mt7921_print_txpwr_entry(HE996x2, he996x2); + + return 0; +} + +static int +mt7921_pm_set(void *data, u64 val) +{ + struct mt792x_dev *dev = data; + struct mt76_connac_pm *pm = &dev->pm; + + if (mt76_is_usb(&dev->mt76)) + return -EOPNOTSUPP; + + mutex_lock(&dev->mt76.mutex); + + if (val == pm->enable_user) + goto out; + + if (!pm->enable_user) { + pm->stats.last_wake_event = jiffies; + pm->stats.last_doze_event = jiffies; + } + /* make sure the chip is awake here and ps_work is scheduled + * just at end of the this routine. + */ + pm->enable = false; + mt76_connac_pm_wake(&dev->mphy, pm); + + pm->enable_user = val; + mt7921_set_runtime_pm(dev); + mt76_connac_power_save_sched(&dev->mphy, pm); +out: + mutex_unlock(&dev->mt76.mutex); + + return 0; +} + +static int +mt7921_pm_get(void *data, u64 *val) +{ + struct mt792x_dev *dev = data; + + *val = dev->pm.enable_user; + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_pm, mt7921_pm_get, mt7921_pm_set, "%lld\n"); + +static int +mt7921_deep_sleep_set(void *data, u64 val) +{ + struct mt792x_dev *dev = data; + struct mt76_connac_pm *pm = &dev->pm; + bool monitor = !!(dev->mphy.hw->conf.flags & IEEE80211_CONF_MONITOR); + bool enable = !!val; + + if (mt76_is_usb(&dev->mt76)) + return -EOPNOTSUPP; + + mt792x_mutex_acquire(dev); + if (pm->ds_enable_user == enable) + goto out; + + pm->ds_enable_user = enable; + pm->ds_enable = enable && !monitor; + mt76_connac_mcu_set_deep_sleep(&dev->mt76, pm->ds_enable); +out: + mt792x_mutex_release(dev); + + return 0; +} + +static int +mt7921_deep_sleep_get(void *data, u64 *val) +{ + struct mt792x_dev *dev = data; + + *val = dev->pm.ds_enable_user; + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_ds, mt7921_deep_sleep_get, + mt7921_deep_sleep_set, "%lld\n"); + +DEFINE_DEBUGFS_ATTRIBUTE(fops_pm_idle_timeout, mt792x_pm_idle_timeout_get, + mt792x_pm_idle_timeout_set, "%lld\n"); + +static int mt7921_chip_reset(void *data, u64 val) +{ + struct mt792x_dev *dev = data; + int ret = 0; + + switch (val) { + case 1: + /* Reset wifisys directly. */ + mt792x_reset(&dev->mt76); + break; + default: + /* Collect the core dump before reset wifisys. */ + mt792x_mutex_acquire(dev); + ret = mt76_connac_mcu_chip_config(&dev->mt76); + mt792x_mutex_release(dev); + break; + } + + return ret; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_reset, NULL, mt7921_chip_reset, "%lld\n"); + +static int +mt7921s_sched_quota_read(struct seq_file *s, void *data) +{ + struct mt792x_dev *dev = dev_get_drvdata(s->private); + struct mt76_sdio *sdio = &dev->mt76.sdio; + + seq_printf(s, "pse_data_quota\t%d\n", sdio->sched.pse_data_quota); + seq_printf(s, "ple_data_quota\t%d\n", sdio->sched.ple_data_quota); + seq_printf(s, "pse_mcu_quota\t%d\n", sdio->sched.pse_mcu_quota); + seq_printf(s, "sched_deficit\t%d\n", sdio->sched.deficit); + + return 0; +} + +int mt7921_init_debugfs(struct mt792x_dev *dev) +{ + struct dentry *dir; + + dir = mt76_register_debugfs_fops(&dev->mphy, &fops_regval); + + if (mt76_is_mmio(&dev->mt76)) + debugfs_create_devm_seqfile(dev->mt76.dev, "xmit-queues", + dir, mt792x_queues_read); + else + debugfs_create_devm_seqfile(dev->mt76.dev, "xmit-queues", + dir, mt76_queues_read); + + debugfs_create_devm_seqfile(dev->mt76.dev, "acq", dir, + mt792x_queues_acq); + debugfs_create_devm_seqfile(dev->mt76.dev, "txpower_sku", dir, + mt7921_txpwr); + debugfs_create_file("tx_stats", 0400, dir, dev, &mt792x_tx_stats_fops); + debugfs_create_file("fw_debug", 0600, dir, dev, &fops_fw_debug); + debugfs_create_file("runtime-pm", 0600, dir, dev, &fops_pm); + debugfs_create_file("idle-timeout", 0600, dir, dev, + &fops_pm_idle_timeout); + debugfs_create_file("chip_reset", 0600, dir, dev, &fops_reset); + debugfs_create_devm_seqfile(dev->mt76.dev, "runtime_pm_stats", dir, + mt792x_pm_stats); + debugfs_create_file("deep-sleep", 0600, dir, dev, &fops_ds); + if (mt76_is_sdio(&dev->mt76)) + debugfs_create_devm_seqfile(dev->mt76.dev, "sched-quota", dir, + mt7921s_sched_quota_read); + return 0; +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/init.c b/drivers/net/wireless/mediatek/mt76/mt7921/init.c new file mode 100644 index 000000000..49be2fa4e --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7921/init.c @@ -0,0 +1,256 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2020 MediaTek Inc. */ + +#include +#include +#include +#include +#include +#include "mt7921.h" +#include "../mt76_connac2_mac.h" +#include "mcu.h" +#include "regd.h" + +static ssize_t mt7921_thermal_temp_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + switch (to_sensor_dev_attr(attr)->index) { + case 0: { + struct mt792x_phy *phy = dev_get_drvdata(dev); + struct mt792x_dev *mdev = phy->dev; + int temperature; + + mt792x_mutex_acquire(mdev); + temperature = mt7921_mcu_get_temperature(phy); + mt792x_mutex_release(mdev); + + if (temperature < 0) + return temperature; + /* display in millidegree Celsius */ + return sprintf(buf, "%u\n", temperature * 1000); + } + default: + return -EINVAL; + } +} +static SENSOR_DEVICE_ATTR_RO(temp1_input, mt7921_thermal_temp, 0); + +static struct attribute *mt7921_hwmon_attrs[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + NULL, +}; +ATTRIBUTE_GROUPS(mt7921_hwmon); + +static int mt7921_thermal_init(struct mt792x_phy *phy) +{ + struct wiphy *wiphy = phy->mt76->hw->wiphy; + struct device *hwmon; + const char *name; + + if (!IS_REACHABLE(CONFIG_HWMON)) + return 0; + + name = devm_kasprintf(&wiphy->dev, GFP_KERNEL, "mt7921_%s", + wiphy_name(wiphy)); + if (!name) + return -ENOMEM; + + hwmon = devm_hwmon_device_register_with_groups(&wiphy->dev, name, phy, + mt7921_hwmon_groups); + return PTR_ERR_OR_ZERO(hwmon); +} + +int mt7921_mac_init(struct mt792x_dev *dev) +{ + int i; + + mt76_rmw_field(dev, MT_MDP_DCR1, MT_MDP_DCR1_MAX_RX_LEN, 1536); + /* enable hardware de-agg */ + mt76_set(dev, MT_MDP_DCR0, MT_MDP_DCR0_DAMSDU_EN); + /* enable hardware rx header translation */ + mt76_set(dev, MT_MDP_DCR0, MT_MDP_DCR0_RX_HDR_TRANS_EN); + + for (i = 0; i < MT792x_WTBL_SIZE; i++) + mt7921_mac_wtbl_update(dev, i, + MT_WTBL_UPDATE_ADM_COUNT_CLEAR); + for (i = 0; i < 2; i++) + mt792x_mac_init_band(dev, i); + + return mt76_connac_mcu_set_rts_thresh(&dev->mt76, 0x92b, 0); +} +EXPORT_SYMBOL_GPL(mt7921_mac_init); + +static int __mt7921_init_hardware(struct mt792x_dev *dev) +{ + int ret; + + /* force firmware operation mode into normal state, + * which should be set before firmware download stage. + */ + mt76_wr(dev, MT_SWDEF_MODE, MT_SWDEF_NORMAL_MODE); + ret = mt792x_mcu_init(dev); + if (ret) + goto out; + + ret = mt76_eeprom_override(&dev->mphy); + if (ret) + goto out; + + ret = mt7921_mcu_set_eeprom(dev); + if (ret) + goto out; + + ret = mt7921_mac_init(dev); +out: + return ret; +} + +static int mt7921_init_hardware(struct mt792x_dev *dev) +{ + int ret, i; + + set_bit(MT76_STATE_INITIALIZED, &dev->mphy.state); + + for (i = 0; i < MT792x_MCU_INIT_RETRY_COUNT; i++) { + ret = __mt7921_init_hardware(dev); + if (!ret) + break; + + mt792x_init_reset(dev); + } + + if (i == MT792x_MCU_INIT_RETRY_COUNT) { + dev_err(dev->mt76.dev, "hardware init failed\n"); + return ret; + } + + return 0; +} + +static void mt7921_init_work(struct work_struct *work) +{ + struct mt792x_dev *dev = container_of(work, struct mt792x_dev, + init_work); + int ret; + + ret = mt7921_init_hardware(dev); + if (ret) + return; + + mt76_set_stream_caps(&dev->mphy, true); + mt7921_set_stream_he_caps(&dev->phy); + mt792x_config_mac_addr_list(dev); + + ret = mt76_register_device(&dev->mt76, true, mt76_rates, + ARRAY_SIZE(mt76_rates)); + if (ret) { + dev_err(dev->mt76.dev, "register device failed\n"); + return; + } + + ret = mt7921_init_debugfs(dev); + if (ret) { + dev_err(dev->mt76.dev, "register debugfs failed\n"); + return; + } + + ret = mt7921_thermal_init(&dev->phy); + if (ret) { + dev_err(dev->mt76.dev, "thermal init failed\n"); + return; + } + + /* we support chip reset now */ + dev->hw_init_done = true; + + mt76_connac_mcu_set_deep_sleep(&dev->mt76, dev->pm.ds_enable); +} + +int mt7921_register_device(struct mt792x_dev *dev) +{ + struct ieee80211_hw *hw = mt76_hw(dev); + int ret; + + dev->phy.dev = dev; + dev->phy.mt76 = &dev->mt76.phy; + dev->mt76.phy.priv = &dev->phy; + dev->mt76.tx_worker.fn = mt792x_tx_worker; + + INIT_DELAYED_WORK(&dev->pm.ps_work, mt792x_pm_power_save_work); + INIT_WORK(&dev->pm.wake_work, mt792x_pm_wake_work); + spin_lock_init(&dev->pm.wake.lock); + mutex_init(&dev->pm.mutex); + init_waitqueue_head(&dev->pm.wait); + init_waitqueue_head(&dev->wait); + if (mt76_is_sdio(&dev->mt76)) + init_waitqueue_head(&dev->mt76.sdio.wait); + spin_lock_init(&dev->pm.txq_lock); + INIT_DELAYED_WORK(&dev->mphy.mac_work, mt792x_mac_work); + INIT_DELAYED_WORK(&dev->phy.scan_work, mt7921_scan_work); + INIT_DELAYED_WORK(&dev->coredump.work, mt7921_coredump_work); +#if IS_ENABLED(CONFIG_IPV6) + INIT_WORK(&dev->ipv6_ns_work, mt7921_set_ipv6_ns_work); + skb_queue_head_init(&dev->ipv6_ns_list); +#endif + skb_queue_head_init(&dev->phy.scan_event_list); + skb_queue_head_init(&dev->coredump.msg_list); + + INIT_WORK(&dev->reset_work, mt7921_mac_reset_work); + INIT_WORK(&dev->init_work, mt7921_init_work); + + INIT_WORK(&dev->phy.roc_work, mt7921_roc_work); + timer_setup(&dev->phy.roc_timer, mt792x_roc_timer, 0); + init_waitqueue_head(&dev->phy.roc_wait); + + dev->pm.idle_timeout = MT792x_PM_TIMEOUT; + dev->pm.stats.last_wake_event = jiffies; + dev->pm.stats.last_doze_event = jiffies; + + if (!mt76_is_usb(&dev->mt76) && + !is_mt7902(&dev->mt76)) { + dev->pm.enable_user = true; + dev->pm.enable = true; + dev->pm.ds_enable_user = true; + dev->pm.ds_enable = true; + } + + if (!mt76_is_mmio(&dev->mt76)) + hw->extra_tx_headroom += MT_SDIO_TXD_SIZE + MT_SDIO_HDR_SIZE; + + mt792x_init_acpi_sar(dev); + + ret = mt792x_init_wcid(dev); + if (ret) + return ret; + + ret = mt792x_init_wiphy(hw); + if (ret) + return ret; + + hw->wiphy->reg_notifier = mt7921_regd_notifier; + dev->mphy.sband_2g.sband.ht_cap.cap |= + IEEE80211_HT_CAP_LDPC_CODING | + IEEE80211_HT_CAP_MAX_AMSDU; + dev->mphy.sband_5g.sband.ht_cap.cap |= + IEEE80211_HT_CAP_LDPC_CODING | + IEEE80211_HT_CAP_MAX_AMSDU; + dev->mphy.sband_5g.sband.vht_cap.cap |= + IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 | + IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK | + IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE | + IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE | + (3 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT); + if (is_mt7922(&dev->mt76)) + dev->mphy.sband_5g.sband.vht_cap.cap |= + IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ | + IEEE80211_VHT_CAP_SHORT_GI_160; + + dev->mphy.hw->wiphy->available_antennas_rx = dev->mphy.chainmask; + dev->mphy.hw->wiphy->available_antennas_tx = dev->mphy.chainmask; + + queue_work(system_percpu_wq, &dev->init_work); + + return 0; +} +EXPORT_SYMBOL_GPL(mt7921_register_device); diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c new file mode 100644 index 000000000..e69978184 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c @@ -0,0 +1,881 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2020 MediaTek Inc. */ + +#include +#include +#include +#include "mt7921.h" +#include "../dma.h" +#include "../mt76_connac2_mac.h" +#include "regd.h" +#include "mcu.h" + +#define MT_WTBL_TXRX_CAP_RATE_OFFSET 7 +#define MT_WTBL_TXRX_RATE_G2_HE 24 +#define MT_WTBL_TXRX_RATE_G2 12 + +#define MT_WTBL_AC0_CTT_OFFSET 20 + +bool mt7921_mac_wtbl_update(struct mt792x_dev *dev, int idx, u32 mask) +{ + mt76_rmw(dev, MT_WTBL_UPDATE, MT_WTBL_UPDATE_WLAN_IDX, + FIELD_PREP(MT_WTBL_UPDATE_WLAN_IDX, idx) | mask); + + return mt76_poll(dev, MT_WTBL_UPDATE, MT_WTBL_UPDATE_BUSY, + 0, 5000); +} + +static u32 mt7921_mac_wtbl_lmac_addr(int idx, u8 offset) +{ + return MT_WTBL_LMAC_OFFS(idx, 0) + offset * 4; +} + +static void mt7921_mac_sta_poll(struct mt792x_dev *dev) +{ + static const u8 ac_to_tid[] = { + [IEEE80211_AC_BE] = 0, + [IEEE80211_AC_BK] = 1, + [IEEE80211_AC_VI] = 4, + [IEEE80211_AC_VO] = 6 + }; + struct ieee80211_sta *sta; + struct mt792x_sta *msta; + struct mt792x_link_sta *mlink; + u32 tx_time[IEEE80211_NUM_ACS], rx_time[IEEE80211_NUM_ACS]; + LIST_HEAD(sta_poll_list); + struct rate_info *rate; + s8 rssi[4]; + int i; + + spin_lock_bh(&dev->mt76.sta_poll_lock); + list_splice_init(&dev->mt76.sta_poll_list, &sta_poll_list); + spin_unlock_bh(&dev->mt76.sta_poll_lock); + + while (true) { + bool clear = false; + u32 addr, val; + u16 idx; + u8 bw; + + spin_lock_bh(&dev->mt76.sta_poll_lock); + if (list_empty(&sta_poll_list)) { + spin_unlock_bh(&dev->mt76.sta_poll_lock); + break; + } + mlink = list_first_entry(&sta_poll_list, + struct mt792x_link_sta, + wcid.poll_list); + msta = container_of(mlink, struct mt792x_sta, deflink); + list_del_init(&mlink->wcid.poll_list); + spin_unlock_bh(&dev->mt76.sta_poll_lock); + + idx = mlink->wcid.idx; + addr = mt7921_mac_wtbl_lmac_addr(idx, MT_WTBL_AC0_CTT_OFFSET); + + for (i = 0; i < IEEE80211_NUM_ACS; i++) { + u32 tx_last = mlink->airtime_ac[i]; + u32 rx_last = mlink->airtime_ac[i + 4]; + + mlink->airtime_ac[i] = mt76_rr(dev, addr); + mlink->airtime_ac[i + 4] = mt76_rr(dev, addr + 4); + + tx_time[i] = mlink->airtime_ac[i] - tx_last; + rx_time[i] = mlink->airtime_ac[i + 4] - rx_last; + + if ((tx_last | rx_last) & BIT(30)) + clear = true; + + addr += 8; + } + + if (clear) { + mt7921_mac_wtbl_update(dev, idx, + MT_WTBL_UPDATE_ADM_COUNT_CLEAR); + memset(mlink->airtime_ac, 0, sizeof(mlink->airtime_ac)); + } + + if (!mlink->wcid.sta) + continue; + + sta = container_of((void *)msta, struct ieee80211_sta, + drv_priv); + for (i = 0; i < IEEE80211_NUM_ACS; i++) { + u8 q = mt76_connac_lmac_mapping(i); + u32 tx_cur = tx_time[q]; + u32 rx_cur = rx_time[q]; + u8 tid = ac_to_tid[i]; + + if (!tx_cur && !rx_cur) + continue; + + ieee80211_sta_register_airtime(sta, tid, tx_cur, + rx_cur); + } + + /* We don't support reading GI info from txs packets. + * For accurate tx status reporting and AQL improvement, + * we need to make sure that flags match so polling GI + * from per-sta counters directly. + */ + rate = &mlink->wcid.rate; + addr = mt7921_mac_wtbl_lmac_addr(idx, + MT_WTBL_TXRX_CAP_RATE_OFFSET); + val = mt76_rr(dev, addr); + + switch (rate->bw) { + case RATE_INFO_BW_160: + bw = IEEE80211_STA_RX_BW_160; + break; + case RATE_INFO_BW_80: + bw = IEEE80211_STA_RX_BW_80; + break; + case RATE_INFO_BW_40: + bw = IEEE80211_STA_RX_BW_40; + break; + default: + bw = IEEE80211_STA_RX_BW_20; + break; + } + + if (rate->flags & RATE_INFO_FLAGS_HE_MCS) { + u8 offs = MT_WTBL_TXRX_RATE_G2_HE + 2 * bw; + + rate->he_gi = (val & (0x3 << offs)) >> offs; + } else if (rate->flags & + (RATE_INFO_FLAGS_VHT_MCS | RATE_INFO_FLAGS_MCS)) { + if (val & BIT(MT_WTBL_TXRX_RATE_G2 + bw)) + rate->flags |= RATE_INFO_FLAGS_SHORT_GI; + else + rate->flags &= ~RATE_INFO_FLAGS_SHORT_GI; + } + + /* get signal strength of resp frames (CTS/BA/ACK) */ + addr = mt7921_mac_wtbl_lmac_addr(idx, 30); + val = mt76_rr(dev, addr); + + rssi[0] = to_rssi(GENMASK(7, 0), val); + rssi[1] = to_rssi(GENMASK(15, 8), val); + rssi[2] = to_rssi(GENMASK(23, 16), val); + rssi[3] = to_rssi(GENMASK(31, 24), val); + + mlink->ack_signal = + mt76_rx_signal(msta->vif->phy->mt76->antenna_mask, rssi); + + ewma_avg_signal_add(&mlink->avg_ack_signal, -mlink->ack_signal); + } +} + +static int +mt7921_mac_fill_rx(struct mt792x_dev *dev, struct sk_buff *skb) +{ + u32 csum_mask = MT_RXD0_NORMAL_IP_SUM | MT_RXD0_NORMAL_UDP_TCP_SUM; + struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb; + bool hdr_trans, unicast, insert_ccmp_hdr = false; + u8 chfreq, qos_ctl = 0, remove_pad, amsdu_info; + u16 hdr_gap; + __le32 *rxv = NULL, *rxd = (__le32 *)skb->data; + struct mt76_phy *mphy = &dev->mt76.phy; + struct mt792x_phy *phy = &dev->phy; + struct ieee80211_supported_band *sband; + u32 csum_status = *(u32 *)skb->cb; + u32 rxd0 = le32_to_cpu(rxd[0]); + u32 rxd1 = le32_to_cpu(rxd[1]); + u32 rxd2 = le32_to_cpu(rxd[2]); + u32 rxd3 = le32_to_cpu(rxd[3]); + u32 rxd4 = le32_to_cpu(rxd[4]); + struct mt792x_sta *msta = NULL; + struct mt792x_link_sta *mlink; + u16 seq_ctrl = 0; + __le16 fc = 0; + u8 mode = 0; + int i, idx; + + memset(status, 0, sizeof(*status)); + + if (rxd1 & MT_RXD1_NORMAL_BAND_IDX) + return -EINVAL; + + if (!test_bit(MT76_STATE_RUNNING, &mphy->state)) + return -EINVAL; + + if (rxd2 & MT_RXD2_NORMAL_AMSDU_ERR) + return -EINVAL; + + hdr_trans = rxd2 & MT_RXD2_NORMAL_HDR_TRANS; + if (hdr_trans && (rxd1 & MT_RXD1_NORMAL_CM)) + return -EINVAL; + + /* ICV error or CCMP/BIP/WPI MIC error */ + if (rxd1 & MT_RXD1_NORMAL_ICV_ERR) + status->flag |= RX_FLAG_ONLY_MONITOR; + + chfreq = FIELD_GET(MT_RXD3_NORMAL_CH_FREQ, rxd3); + unicast = FIELD_GET(MT_RXD3_NORMAL_ADDR_TYPE, rxd3) == MT_RXD3_NORMAL_U2M; + idx = FIELD_GET(MT_RXD1_NORMAL_WLAN_IDX, rxd1); + status->wcid = mt792x_rx_get_wcid(dev, idx, unicast); + + if (status->wcid) { + mlink = container_of(status->wcid, struct mt792x_link_sta, wcid); + msta = container_of(mlink, struct mt792x_sta, deflink); + mt76_wcid_add_poll(&dev->mt76, &mlink->wcid); + } + + mt792x_get_status_freq_info(status, chfreq); + + switch (status->band) { + case NL80211_BAND_5GHZ: + sband = &mphy->sband_5g.sband; + break; + case NL80211_BAND_6GHZ: + sband = &mphy->sband_6g.sband; + break; + default: + sband = &mphy->sband_2g.sband; + break; + } + + if (!sband->channels) + return -EINVAL; + + if (mt76_is_mmio(&dev->mt76) && (rxd0 & csum_mask) == csum_mask && + !(csum_status & (BIT(0) | BIT(2) | BIT(3)))) + skb->ip_summed = CHECKSUM_UNNECESSARY; + + if (rxd1 & MT_RXD1_NORMAL_FCS_ERR) + status->flag |= RX_FLAG_FAILED_FCS_CRC; + + if (rxd1 & MT_RXD1_NORMAL_TKIP_MIC_ERR) + status->flag |= RX_FLAG_MMIC_ERROR; + + if (FIELD_GET(MT_RXD1_NORMAL_SEC_MODE, rxd1) != 0 && + !(rxd1 & (MT_RXD1_NORMAL_CLM | MT_RXD1_NORMAL_CM))) { + status->flag |= RX_FLAG_DECRYPTED; + status->flag |= RX_FLAG_IV_STRIPPED; + status->flag |= RX_FLAG_MMIC_STRIPPED | RX_FLAG_MIC_STRIPPED; + } + + remove_pad = FIELD_GET(MT_RXD2_NORMAL_HDR_OFFSET, rxd2); + + if (rxd2 & MT_RXD2_NORMAL_MAX_LEN_ERROR) + return -EINVAL; + + rxd += 6; + if (rxd1 & MT_RXD1_NORMAL_GROUP_4) { + u32 v0 = le32_to_cpu(rxd[0]); + u32 v2 = le32_to_cpu(rxd[2]); + + fc = cpu_to_le16(FIELD_GET(MT_RXD6_FRAME_CONTROL, v0)); + seq_ctrl = FIELD_GET(MT_RXD8_SEQ_CTRL, v2); + qos_ctl = FIELD_GET(MT_RXD8_QOS_CTL, v2); + + rxd += 4; + if ((u8 *)rxd - skb->data >= skb->len) + return -EINVAL; + } + + if (rxd1 & MT_RXD1_NORMAL_GROUP_1) { + u8 *data = (u8 *)rxd; + + if (status->flag & RX_FLAG_DECRYPTED) { + switch (FIELD_GET(MT_RXD1_NORMAL_SEC_MODE, rxd1)) { + case MT_CIPHER_AES_CCMP: + case MT_CIPHER_CCMP_CCX: + case MT_CIPHER_CCMP_256: + insert_ccmp_hdr = + FIELD_GET(MT_RXD2_NORMAL_FRAG, rxd2); + fallthrough; + case MT_CIPHER_TKIP: + case MT_CIPHER_TKIP_NO_MIC: + case MT_CIPHER_GCMP: + case MT_CIPHER_GCMP_256: + status->iv[0] = data[5]; + status->iv[1] = data[4]; + status->iv[2] = data[3]; + status->iv[3] = data[2]; + status->iv[4] = data[1]; + status->iv[5] = data[0]; + break; + default: + break; + } + } + rxd += 4; + if ((u8 *)rxd - skb->data >= skb->len) + return -EINVAL; + } + + if (rxd1 & MT_RXD1_NORMAL_GROUP_2) { + status->timestamp = le32_to_cpu(rxd[0]); + status->flag |= RX_FLAG_MACTIME_START; + + if (!(rxd2 & MT_RXD2_NORMAL_NON_AMPDU)) { + status->flag |= RX_FLAG_AMPDU_DETAILS; + + /* all subframes of an A-MPDU have the same timestamp */ + if (phy->rx_ampdu_ts != status->timestamp) { + if (!++phy->ampdu_ref) + phy->ampdu_ref++; + } + phy->rx_ampdu_ts = status->timestamp; + + status->ampdu_ref = phy->ampdu_ref; + } + + rxd += 2; + if ((u8 *)rxd - skb->data >= skb->len) + return -EINVAL; + } + + /* RXD Group 3 - P-RXV */ + if (rxd1 & MT_RXD1_NORMAL_GROUP_3) { + u32 v0, v1; + int ret; + + rxv = rxd; + rxd += 2; + if ((u8 *)rxd - skb->data >= skb->len) + return -EINVAL; + + v0 = le32_to_cpu(rxv[0]); + v1 = le32_to_cpu(rxv[1]); + + if (v0 & MT_PRXV_HT_AD_CODE) + status->enc_flags |= RX_ENC_FLAG_LDPC; + + ret = mt76_connac2_mac_fill_rx_rate(&dev->mt76, status, sband, + rxv, &mode); + if (ret < 0) + return ret; + + if (rxd1 & MT_RXD1_NORMAL_GROUP_5) { + rxd += 6; + if ((u8 *)rxd - skb->data >= skb->len) + return -EINVAL; + + rxv = rxd; + /* Monitor mode would use RCPI described in GROUP 5 + * instead. + */ + v1 = le32_to_cpu(rxv[0]); + + rxd += 12; + if ((u8 *)rxd - skb->data >= skb->len) + return -EINVAL; + } + + status->chains = mphy->antenna_mask; + status->chain_signal[0] = to_rssi(MT_PRXV_RCPI0, v1); + status->chain_signal[1] = to_rssi(MT_PRXV_RCPI1, v1); + status->chain_signal[2] = to_rssi(MT_PRXV_RCPI2, v1); + status->chain_signal[3] = to_rssi(MT_PRXV_RCPI3, v1); + status->signal = -128; + for (i = 0; i < hweight8(mphy->antenna_mask); i++) { + if (!(status->chains & BIT(i)) || + status->chain_signal[i] >= 0) + continue; + + status->signal = max(status->signal, + status->chain_signal[i]); + } + } + + amsdu_info = FIELD_GET(MT_RXD4_NORMAL_PAYLOAD_FORMAT, rxd4); + status->amsdu = !!amsdu_info; + if (status->amsdu) { + status->first_amsdu = amsdu_info == MT_RXD4_FIRST_AMSDU_FRAME; + status->last_amsdu = amsdu_info == MT_RXD4_LAST_AMSDU_FRAME; + } + + hdr_gap = (u8 *)rxd - skb->data + 2 * remove_pad; + if (hdr_trans && ieee80211_has_morefrags(fc)) { + struct ieee80211_vif *vif; + int err; + + if (!msta || !msta->vif) + return -EINVAL; + + vif = container_of((void *)msta->vif, struct ieee80211_vif, + drv_priv); + err = mt76_connac2_reverse_frag0_hdr_trans(vif, skb, hdr_gap); + if (err) + return err; + + hdr_trans = false; + } else { + skb_pull(skb, hdr_gap); + if (!hdr_trans && status->amsdu) { + memmove(skb->data + 2, skb->data, + ieee80211_get_hdrlen_from_skb(skb)); + skb_pull(skb, 2); + } + } + + if (!hdr_trans) { + struct ieee80211_hdr *hdr; + + if (insert_ccmp_hdr) { + u8 key_id = FIELD_GET(MT_RXD1_NORMAL_KEY_ID, rxd1); + + mt76_insert_ccmp_hdr(skb, key_id); + } + + hdr = mt76_skb_get_hdr(skb); + fc = hdr->frame_control; + if (ieee80211_is_data_qos(fc)) { + seq_ctrl = le16_to_cpu(hdr->seq_ctrl); + qos_ctl = *ieee80211_get_qos_ctl(hdr); + } + } else { + status->flag |= RX_FLAG_8023; + } + + mt792x_mac_assoc_rssi(dev, skb); + + if (rxv && mode >= MT_PHY_TYPE_HE_SU && !(status->flag & RX_FLAG_8023)) + mt76_connac2_mac_decode_he_radiotap(&dev->mt76, skb, rxv, mode); + + if (!status->wcid || !ieee80211_is_data_qos(fc)) + return 0; + + status->aggr = unicast && !ieee80211_is_qos_nullfunc(fc); + status->seqno = IEEE80211_SEQ_TO_SN(seq_ctrl); + status->qos_ctl = qos_ctl; + + return 0; +} + +void mt7921_mac_add_txs(struct mt792x_dev *dev, void *data) +{ + struct mt792x_link_sta *mlink; + struct mt76_wcid *wcid; + __le32 *txs_data = data; + u16 wcidx; + u8 pid; + + if (le32_get_bits(txs_data[0], MT_TXS0_TXS_FORMAT) > 1) + return; + + wcidx = le32_get_bits(txs_data[2], MT_TXS2_WCID); + pid = le32_get_bits(txs_data[3], MT_TXS3_PID); + + if (pid < MT_PACKET_ID_FIRST) + return; + + if (wcidx >= MT792x_WTBL_SIZE) + return; + + rcu_read_lock(); + + wcid = mt76_wcid_ptr(dev, wcidx); + if (!wcid) + goto out; + + mlink = container_of(wcid, struct mt792x_link_sta, wcid); + + mt76_connac2_mac_add_txs_skb(&dev->mt76, wcid, pid, txs_data); + if (!wcid->sta) + goto out; + + mt76_wcid_add_poll(&dev->mt76, &mlink->wcid); + +out: + rcu_read_unlock(); +} + +static void mt7921_mac_tx_free(struct mt792x_dev *dev, void *data, int len) +{ + struct mt76_connac_tx_free *free = data; + __le32 *tx_info = (__le32 *)(data + sizeof(*free)); + struct mt76_dev *mdev = &dev->mt76; + struct mt76_txwi_cache *txwi; + struct ieee80211_sta *sta = NULL; + struct mt76_wcid *wcid = NULL; + struct sk_buff *skb, *tmp; + void *end = data + len; + LIST_HEAD(free_list); + bool wake = false; + u8 i, count; + + /* clean DMA queues and unmap buffers first */ + mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[MT_TXQ_PSD], false); + mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[MT_TXQ_BE], false); + + count = le16_get_bits(free->ctrl, MT_TX_FREE_MSDU_CNT); + if (WARN_ON_ONCE((void *)&tx_info[count] > end)) + return; + + for (i = 0; i < count; i++) { + u32 msdu, info = le32_to_cpu(tx_info[i]); + u8 stat; + + /* 1'b1: new wcid pair. + * 1'b0: msdu_id with the same 'wcid pair' as above. + */ + if (info & MT_TX_FREE_PAIR) { + struct mt792x_link_sta *mlink; + u16 idx; + + count++; + idx = FIELD_GET(MT_TX_FREE_WLAN_ID, info); + wcid = mt76_wcid_ptr(dev, idx); + sta = wcid_to_sta(wcid); + if (!sta) + continue; + + mlink = container_of(wcid, struct mt792x_link_sta, wcid); + mt76_wcid_add_poll(&dev->mt76, &mlink->wcid); + continue; + } + + msdu = FIELD_GET(MT_TX_FREE_MSDU_ID, info); + stat = FIELD_GET(MT_TX_FREE_STATUS, info); + + if (wcid) { + u32 count = FIELD_GET(MT_TX_FREE_COUNT, info); + + wcid->stats.tx_retries += count ? count - 1 : 0; + wcid->stats.tx_failed += !!stat; + } + + txwi = mt76_token_release(mdev, msdu, &wake); + if (!txwi) + continue; + + mt76_connac2_txwi_free(mdev, txwi, sta, &free_list); + } + + if (wake) + mt76_set_tx_blocked(&dev->mt76, false); + + list_for_each_entry_safe(skb, tmp, &free_list, list) { + skb_list_del_init(skb); + napi_consume_skb(skb, 1); + } + + rcu_read_lock(); + mt7921_mac_sta_poll(dev); + rcu_read_unlock(); + + mt76_worker_schedule(&dev->mt76.tx_worker); +} + +bool mt7921_rx_check(struct mt76_dev *mdev, void *data, int len) +{ + struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); + __le32 *rxd = (__le32 *)data; + __le32 *end = (__le32 *)&rxd[len / 4]; + enum rx_pkt_type type; + + type = le32_get_bits(rxd[0], MT_RXD0_PKT_TYPE); + + switch (type) { + case PKT_TYPE_TXRX_NOTIFY: + if (!mt76_is_mmio(mdev)) + return false; + mt7921_mac_tx_free(dev, data, len); + return false; + case PKT_TYPE_TXS: + for (rxd += 2; rxd + 8 <= end; rxd += 8) + mt7921_mac_add_txs(dev, rxd); + return false; + default: + return true; + } +} +EXPORT_SYMBOL_GPL(mt7921_rx_check); + +void mt7921_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, + struct sk_buff *skb, u32 *info) +{ + struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); + __le32 *rxd = (__le32 *)skb->data; + __le32 *end = (__le32 *)&skb->data[skb->len]; + enum rx_pkt_type type; + u16 flag; + + type = le32_get_bits(rxd[0], MT_RXD0_PKT_TYPE); + flag = le32_get_bits(rxd[0], MT_RXD0_PKT_FLAG); + + if (type == PKT_TYPE_RX_EVENT && flag == 0x1) + type = PKT_TYPE_NORMAL_MCU; + + switch (type) { + case PKT_TYPE_TXRX_NOTIFY: + if (!mt76_is_mmio(mdev)) { + napi_consume_skb(skb, 1); + break; + } + mt7921_mac_tx_free(dev, skb->data, skb->len); + napi_consume_skb(skb, 1); + break; + case PKT_TYPE_RX_EVENT: + mt7921_mcu_rx_event(dev, skb); + break; + case PKT_TYPE_TXS: + for (rxd += 2; rxd + 8 <= end; rxd += 8) + mt7921_mac_add_txs(dev, rxd); + dev_kfree_skb(skb); + break; + case PKT_TYPE_NORMAL_MCU: + case PKT_TYPE_NORMAL: + if (!mt7921_mac_fill_rx(dev, skb)) { + mt76_rx(&dev->mt76, q, skb); + return; + } + fallthrough; + default: + dev_kfree_skb(skb); + break; + } +} +EXPORT_SYMBOL_GPL(mt7921_queue_rx_skb); + +static void +mt7921_vif_connect_iter(void *priv, u8 *mac, + struct ieee80211_vif *vif) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + struct mt792x_dev *dev = mvif->phy->dev; + struct ieee80211_hw *hw = mt76_hw(dev); + + if (vif->type == NL80211_IFTYPE_STATION) + ieee80211_disconnect(vif, true); + + mt76_connac_mcu_uni_add_dev(&dev->mphy, &vif->bss_conf, + &mvif->bss_conf.mt76, + &mvif->sta.deflink.wcid, true); + mt7921_mcu_set_tx(dev, vif); + + if (vif->type == NL80211_IFTYPE_AP) { + mt76_connac_mcu_uni_add_bss(dev->phy.mt76, vif, &mvif->sta.deflink.wcid, + true, NULL); + mt7921_mcu_sta_update(dev, NULL, vif, true, + MT76_STA_INFO_STATE_NONE); + mt7921_mcu_uni_add_beacon_offload(dev, hw, vif, true); + } +} + +/* system error recovery */ +void mt7921_mac_reset_work(struct work_struct *work) +{ + struct mt792x_dev *dev = container_of(work, struct mt792x_dev, + reset_work); + struct ieee80211_hw *hw = mt76_hw(dev); + struct mt76_connac_pm *pm = &dev->pm; + int i, ret; + + dev_dbg(dev->mt76.dev, "chip reset\n"); + set_bit(MT76_RESET, &dev->mphy.state); + dev->hw_full_reset = true; + ieee80211_stop_queues(hw); + + cancel_delayed_work_sync(&dev->mphy.mac_work); + cancel_delayed_work_sync(&pm->ps_work); + cancel_work_sync(&pm->wake_work); + + for (i = 0; i < 10; i++) { + if (atomic_read(&dev->mt76.bus_hung)) + return; + + mutex_lock(&dev->mt76.mutex); + ret = mt792x_dev_reset(dev); + mutex_unlock(&dev->mt76.mutex); + + if (!ret) + break; + } + + if ((mt76_is_sdio(&dev->mt76) || mt76_is_usb(&dev->mt76)) && + atomic_read(&dev->mt76.bus_hung)) + return; + + if (i == 10) + dev_err(dev->mt76.dev, "chip reset failed\n"); + + if (test_and_clear_bit(MT76_HW_SCANNING, &dev->mphy.state)) { + struct cfg80211_scan_info info = { + .aborted = true, + }; + + ieee80211_scan_completed(dev->mphy.hw, &info); + } + + dev->hw_full_reset = false; + clear_bit(MT76_RESET, &dev->mphy.state); + pm->suspended = false; + ieee80211_wake_queues(hw); + ieee80211_iterate_active_interfaces(hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7921_vif_connect_iter, NULL); + mt76_connac_power_save_sched(&dev->mt76.phy, pm); + + mt7921_regd_change(&dev->phy, "00"); +} + +void mt7921_coredump_work(struct work_struct *work) +{ + struct mt792x_dev *dev; + char *dump, *data; + + dev = (struct mt792x_dev *)container_of(work, struct mt792x_dev, + coredump.work.work); + + if (time_is_after_jiffies(dev->coredump.last_activity + + 4 * MT76_CONNAC_COREDUMP_TIMEOUT)) { + queue_delayed_work(dev->mt76.wq, &dev->coredump.work, + MT76_CONNAC_COREDUMP_TIMEOUT); + return; + } + + dump = vzalloc(MT76_CONNAC_COREDUMP_SZ); + data = dump; + + while (true) { + struct sk_buff *skb; + + spin_lock_bh(&dev->mt76.lock); + skb = __skb_dequeue(&dev->coredump.msg_list); + spin_unlock_bh(&dev->mt76.lock); + + if (!skb) + break; + + skb_pull(skb, sizeof(struct mt76_connac2_mcu_rxd)); + if (!dump || data + skb->len - dump > MT76_CONNAC_COREDUMP_SZ) { + dev_kfree_skb(skb); + continue; + } + + memcpy(data, skb->data, skb->len); + data += skb->len; + + dev_kfree_skb(skb); + } + + if (dump) + dev_coredumpv(dev->mt76.dev, dump, MT76_CONNAC_COREDUMP_SZ, + GFP_KERNEL); + + mt792x_reset(&dev->mt76); +} + +/* usb_sdio */ +static void +mt7921_usb_sdio_write_txwi(struct mt792x_dev *dev, struct mt76_wcid *wcid, + enum mt76_txq_id qid, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key, int pid, + struct sk_buff *skb) +{ + __le32 *txwi = (__le32 *)(skb->data - MT_SDIO_TXD_SIZE); + + memset(txwi, 0, MT_SDIO_TXD_SIZE); + mt76_connac2_mac_write_txwi(&dev->mt76, txwi, skb, wcid, key, pid, qid, 0); + skb_push(skb, MT_SDIO_TXD_SIZE); +} + +int mt7921_usb_sdio_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, + enum mt76_txq_id qid, struct mt76_wcid *wcid, + struct ieee80211_sta *sta, + struct mt76_tx_info *tx_info) +{ + struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx_info->skb); + struct ieee80211_key_conf *key = info->control.hw_key; + struct sk_buff *skb = tx_info->skb; + int err, pad, pktid, type; + + if (unlikely(tx_info->skb->len <= ETH_HLEN)) + return -EINVAL; + + err = skb_cow_head(skb, MT_SDIO_TXD_SIZE + MT_SDIO_HDR_SIZE); + if (err) + return err; + + if (!wcid) + wcid = &dev->mt76.global_wcid; + + if (sta) { + struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv; + + if (time_after(jiffies, msta->deflink.last_txs + HZ / 4)) { + info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; + msta->deflink.last_txs = jiffies; + } + } + + pktid = mt76_tx_status_skb_add(&dev->mt76, wcid, skb); + mt7921_usb_sdio_write_txwi(dev, wcid, qid, sta, key, pktid, skb); + + type = mt76_is_sdio(mdev) ? MT7921_SDIO_DATA : 0; + mt792x_skb_add_usb_sdio_hdr(dev, skb, type); + pad = round_up(skb->len, 4) - skb->len; + if (mt76_is_usb(mdev)) + pad += 4; + + err = mt76_skb_adjust_pad(skb, pad); + if (err) + /* Release pktid in case of error. */ + idr_remove(&wcid->pktid, pktid); + + return err; +} +EXPORT_SYMBOL_GPL(mt7921_usb_sdio_tx_prepare_skb); + +void mt7921_usb_sdio_tx_complete_skb(struct mt76_dev *mdev, + struct mt76_queue_entry *e) +{ + __le32 *txwi = (__le32 *)(e->skb->data + MT_SDIO_HDR_SIZE); + unsigned int headroom = MT_SDIO_TXD_SIZE + MT_SDIO_HDR_SIZE; + struct ieee80211_sta *sta; + struct mt76_wcid *wcid; + u16 idx; + + idx = le32_get_bits(txwi[1], MT_TXD1_WLAN_IDX); + wcid = __mt76_wcid_ptr(mdev, idx); + sta = wcid_to_sta(wcid); + + if (sta && likely(e->skb->protocol != cpu_to_be16(ETH_P_PAE))) + mt76_connac2_tx_check_aggr(sta, txwi); + + skb_pull(e->skb, headroom); + mt76_tx_complete_skb(mdev, e->wcid, e->skb); +} +EXPORT_SYMBOL_GPL(mt7921_usb_sdio_tx_complete_skb); + +bool mt7921_usb_sdio_tx_status_data(struct mt76_dev *mdev, u8 *update) +{ + struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); + + mt792x_mutex_acquire(dev); + mt7921_mac_sta_poll(dev); + mt792x_mutex_release(dev); + + return false; +} +EXPORT_SYMBOL_GPL(mt7921_usb_sdio_tx_status_data); + +#if IS_ENABLED(CONFIG_IPV6) +void mt7921_set_ipv6_ns_work(struct work_struct *work) +{ + struct mt792x_dev *dev = container_of(work, struct mt792x_dev, + ipv6_ns_work); + struct sk_buff *skb; + int ret = 0; + + do { + skb = skb_dequeue(&dev->ipv6_ns_list); + + if (!skb) + break; + + mt792x_mutex_acquire(dev); + ret = mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_UNI_CMD(OFFLOAD), true); + mt792x_mutex_release(dev); + + } while (!ret); + + if (ret) + skb_queue_purge(&dev->ipv6_ns_list); +} +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/main.c b/drivers/net/wireless/mediatek/mt76/mt7921/main.c new file mode 100644 index 000000000..68a059504 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7921/main.c @@ -0,0 +1,1618 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2020 MediaTek Inc. */ + +#include +#include +#include +#include +#include +#include "mt7921.h" +#include "regd.h" +#include "mcu.h" + +static int +mt7921_init_he_caps(struct mt792x_phy *phy, enum nl80211_band band, + struct ieee80211_sband_iftype_data *data) +{ + int i, idx = 0; + int nss = hweight8(phy->mt76->chainmask); + u16 mcs_map = 0; + + for (i = 0; i < 8; i++) { + if (i < nss) + mcs_map |= (IEEE80211_HE_MCS_SUPPORT_0_11 << (i * 2)); + else + mcs_map |= (IEEE80211_HE_MCS_NOT_SUPPORTED << (i * 2)); + } + + for (i = 0; i < NUM_NL80211_IFTYPES; i++) { + struct ieee80211_sta_he_cap *he_cap = &data[idx].he_cap; + struct ieee80211_he_cap_elem *he_cap_elem = + &he_cap->he_cap_elem; + struct ieee80211_he_mcs_nss_supp *he_mcs = + &he_cap->he_mcs_nss_supp; + + switch (i) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_AP: + break; + default: + continue; + } + + data[idx].types_mask = BIT(i); + he_cap->has_he = true; + + he_cap_elem->mac_cap_info[0] = + IEEE80211_HE_MAC_CAP0_HTC_HE; + he_cap_elem->mac_cap_info[3] = + IEEE80211_HE_MAC_CAP3_OMI_CONTROL | + IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_3; + he_cap_elem->mac_cap_info[4] = + IEEE80211_HE_MAC_CAP4_AMSDU_IN_AMPDU; + + if (band == NL80211_BAND_2GHZ) + he_cap_elem->phy_cap_info[0] = + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G; + else + he_cap_elem->phy_cap_info[0] = + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G; + + he_cap_elem->phy_cap_info[1] = + IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD; + he_cap_elem->phy_cap_info[2] = + IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US | + IEEE80211_HE_PHY_CAP2_STBC_TX_UNDER_80MHZ | + IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ | + IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO | + IEEE80211_HE_PHY_CAP2_UL_MU_PARTIAL_MU_MIMO; + + switch (i) { + case NL80211_IFTYPE_AP: + he_cap_elem->mac_cap_info[2] |= + IEEE80211_HE_MAC_CAP2_BSR; + he_cap_elem->mac_cap_info[4] |= + IEEE80211_HE_MAC_CAP4_BQR; + he_cap_elem->mac_cap_info[5] |= + IEEE80211_HE_MAC_CAP5_OM_CTRL_UL_MU_DATA_DIS_RX; + he_cap_elem->phy_cap_info[3] |= + IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_QPSK | + IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_QPSK; + he_cap_elem->phy_cap_info[6] |= + IEEE80211_HE_PHY_CAP6_PARTIAL_BW_EXT_RANGE | + IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT; + he_cap_elem->phy_cap_info[9] |= + IEEE80211_HE_PHY_CAP9_TX_1024_QAM_LESS_THAN_242_TONE_RU | + IEEE80211_HE_PHY_CAP9_RX_1024_QAM_LESS_THAN_242_TONE_RU; + + if (is_mt7922(phy->mt76->dev)) { + he_cap_elem->phy_cap_info[0] |= + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G; + } + break; + case NL80211_IFTYPE_STATION: + he_cap_elem->mac_cap_info[1] |= + IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US; + + if (band == NL80211_BAND_2GHZ) + he_cap_elem->phy_cap_info[0] |= + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_RU_MAPPING_IN_2G; + else + he_cap_elem->phy_cap_info[0] |= + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_RU_MAPPING_IN_5G; + + he_cap_elem->phy_cap_info[1] |= + IEEE80211_HE_PHY_CAP1_DEVICE_CLASS_A | + IEEE80211_HE_PHY_CAP1_HE_LTF_AND_GI_FOR_HE_PPDUS_0_8US; + he_cap_elem->phy_cap_info[3] |= + IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_QPSK | + IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_QPSK; + he_cap_elem->phy_cap_info[4] |= + IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE | + IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_4; + he_cap_elem->phy_cap_info[5] |= + IEEE80211_HE_PHY_CAP5_NG16_SU_FEEDBACK | + IEEE80211_HE_PHY_CAP5_NG16_MU_FEEDBACK; + he_cap_elem->phy_cap_info[6] |= + IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_42_SU | + IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_75_MU | + IEEE80211_HE_PHY_CAP6_TRIG_CQI_FB | + IEEE80211_HE_PHY_CAP6_PARTIAL_BW_EXT_RANGE | + IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT; + he_cap_elem->phy_cap_info[7] |= + IEEE80211_HE_PHY_CAP7_POWER_BOOST_FACTOR_SUPP | + IEEE80211_HE_PHY_CAP7_HE_SU_MU_PPDU_4XLTF_AND_08_US_GI; + he_cap_elem->phy_cap_info[8] |= + IEEE80211_HE_PHY_CAP8_20MHZ_IN_40MHZ_HE_PPDU_IN_2G | + IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_484; + he_cap_elem->phy_cap_info[9] |= + IEEE80211_HE_PHY_CAP9_LONGER_THAN_16_SIGB_OFDM_SYM | + IEEE80211_HE_PHY_CAP9_NON_TRIGGERED_CQI_FEEDBACK | + IEEE80211_HE_PHY_CAP9_TX_1024_QAM_LESS_THAN_242_TONE_RU | + IEEE80211_HE_PHY_CAP9_RX_1024_QAM_LESS_THAN_242_TONE_RU | + IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_COMP_SIGB | + IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_NON_COMP_SIGB; + + if (is_mt7922(phy->mt76->dev)) { + he_cap_elem->phy_cap_info[0] |= + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G; + he_cap_elem->phy_cap_info[4] |= + IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_4; + he_cap_elem->phy_cap_info[8] |= + IEEE80211_HE_PHY_CAP8_20MHZ_IN_160MHZ_HE_PPDU | + IEEE80211_HE_PHY_CAP8_80MHZ_IN_160MHZ_HE_PPDU; + } + break; + } + + he_mcs->rx_mcs_80 = cpu_to_le16(mcs_map); + he_mcs->tx_mcs_80 = cpu_to_le16(mcs_map); + if (is_mt7922(phy->mt76->dev)) { + he_mcs->rx_mcs_160 = cpu_to_le16(mcs_map); + he_mcs->tx_mcs_160 = cpu_to_le16(mcs_map); + } + + memset(he_cap->ppe_thres, 0, sizeof(he_cap->ppe_thres)); + if (he_cap_elem->phy_cap_info[6] & + IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT) { + mt76_connac_gen_ppe_thresh(he_cap->ppe_thres, nss, band); + } else { + he_cap_elem->phy_cap_info[9] |= + u8_encode_bits(IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_16US, + IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_MASK); + } + + if (band == NL80211_BAND_6GHZ) { + struct ieee80211_supported_band *sband = + &phy->mt76->sband_5g.sband; + struct ieee80211_sta_vht_cap *vht_cap = &sband->vht_cap; + struct ieee80211_sta_ht_cap *ht_cap = &sband->ht_cap; + u32 exp; + u16 cap; + + cap = u16_encode_bits(ht_cap->ampdu_density, + IEEE80211_HE_6GHZ_CAP_MIN_MPDU_START); + exp = u32_get_bits(vht_cap->cap, + IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK); + cap |= u16_encode_bits(exp, + IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP); + exp = u32_get_bits(vht_cap->cap, + IEEE80211_VHT_CAP_MAX_MPDU_MASK); + cap |= u16_encode_bits(exp, + IEEE80211_HE_6GHZ_CAP_MAX_MPDU_LEN); + if (vht_cap->cap & IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN) + cap |= IEEE80211_HE_6GHZ_CAP_TX_ANTPAT_CONS; + if (vht_cap->cap & IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN) + cap |= IEEE80211_HE_6GHZ_CAP_RX_ANTPAT_CONS; + + data[idx].he_6ghz_capa.capa = cpu_to_le16(cap); + } + idx++; + } + + return idx; +} + +void mt7921_set_stream_he_caps(struct mt792x_phy *phy) +{ + struct ieee80211_sband_iftype_data *data; + struct ieee80211_supported_band *band; + int n; + + if (phy->mt76->cap.has_2ghz) { + data = phy->iftype[NL80211_BAND_2GHZ]; + n = mt7921_init_he_caps(phy, NL80211_BAND_2GHZ, data); + + band = &phy->mt76->sband_2g.sband; + _ieee80211_set_sband_iftype_data(band, data, n); + } + + if (phy->mt76->cap.has_5ghz) { + data = phy->iftype[NL80211_BAND_5GHZ]; + n = mt7921_init_he_caps(phy, NL80211_BAND_5GHZ, data); + + band = &phy->mt76->sband_5g.sband; + _ieee80211_set_sband_iftype_data(band, data, n); + + if (phy->mt76->cap.has_6ghz) { + data = phy->iftype[NL80211_BAND_6GHZ]; + n = mt7921_init_he_caps(phy, NL80211_BAND_6GHZ, data); + + band = &phy->mt76->sband_6g.sband; + _ieee80211_set_sband_iftype_data(band, data, n); + } + } +} + +int __mt7921_start(struct mt792x_phy *phy) +{ + struct mt76_phy *mphy = phy->mt76; + int err; + + err = mt76_connac_mcu_set_mac_enable(mphy->dev, 0, true, false); + if (err) + return err; + + err = mt76_connac_mcu_set_channel_domain(mphy); + if (err) + return err; + + err = mt7921_mcu_set_chan_info(phy, MCU_EXT_CMD(SET_RX_PATH)); + if (err) + return err; + + err = mt7921_set_tx_sar_pwr(mphy->hw, NULL); + if (err) + return err; + + mt792x_mac_reset_counters(phy); + set_bit(MT76_STATE_RUNNING, &mphy->state); + + ieee80211_queue_delayed_work(mphy->hw, &mphy->mac_work, + MT792x_WATCHDOG_TIME); + if (mt76_is_mmio(mphy->dev)) { + err = mt7921_mcu_radio_led_ctrl(phy->dev, EXT_CMD_RADIO_LED_CTRL_ENABLE); + if (err) + return err; + + err = mt7921_mcu_radio_led_ctrl(phy->dev, EXT_CMD_RADIO_ON_LED); + if (err) + return err; + } + + if (phy->chip_cap & MT792x_CHIP_CAP_WF_RF_PIN_CTRL_EVT_EN) { + mt7921_mcu_wf_rf_pin_ctrl(phy, WF_RF_PIN_INIT); + wiphy_rfkill_start_polling(mphy->hw->wiphy); + } + + return 0; +} +EXPORT_SYMBOL_GPL(__mt7921_start); + +static int mt7921_start(struct ieee80211_hw *hw) +{ + struct mt792x_phy *phy = mt792x_hw_phy(hw); + int err; + + mt792x_mutex_acquire(phy->dev); + err = __mt7921_start(phy); + mt792x_mutex_release(phy->dev); + + return err; +} + +static void mt7921_stop(struct ieee80211_hw *hw, bool suspend) +{ + struct mt792x_dev *dev = mt792x_hw_dev(hw); + int err = 0; + + if (mt76_is_mmio(&dev->mt76)) { + mt792x_mutex_acquire(dev); + err = mt7921_mcu_radio_led_ctrl(dev, EXT_CMD_RADIO_OFF_LED); + mt792x_mutex_release(dev); + if (err) + return; + } + + mt792x_stop(hw, false); +} + +static int +mt7921_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + struct mt792x_dev *dev = mt792x_hw_dev(hw); + struct mt792x_phy *phy = mt792x_hw_phy(hw); + struct mt76_txq *mtxq; + int idx, ret = 0; + + mt792x_mutex_acquire(dev); + + mvif->bss_conf.mt76.idx = __ffs64(~dev->mt76.vif_mask); + if (mvif->bss_conf.mt76.idx >= MT792x_MAX_INTERFACES) { + ret = -ENOSPC; + goto out; + } + + mvif->bss_conf.mt76.omac_idx = mvif->bss_conf.mt76.idx; + mvif->phy = phy; + mvif->bss_conf.vif = mvif; + mvif->bss_conf.mt76.band_idx = 0; + mvif->bss_conf.mt76.wmm_idx = mvif->bss_conf.mt76.idx % MT76_CONNAC_MAX_WMM_SETS; + + ret = mt76_connac_mcu_uni_add_dev(&dev->mphy, &vif->bss_conf, + &mvif->bss_conf.mt76, + &mvif->sta.deflink.wcid, true); + if (ret) + goto out; + + dev->mt76.vif_mask |= BIT_ULL(mvif->bss_conf.mt76.idx); + phy->omac_mask |= BIT_ULL(mvif->bss_conf.mt76.omac_idx); + + idx = MT792x_WTBL_RESERVED - mvif->bss_conf.mt76.idx; + + INIT_LIST_HEAD(&mvif->sta.deflink.wcid.poll_list); + mvif->sta.deflink.wcid.idx = idx; + mvif->sta.deflink.wcid.tx_info |= MT_WCID_TX_INFO_SET; + mt76_wcid_init(&mvif->sta.deflink.wcid, mvif->bss_conf.mt76.band_idx); + + mt7921_mac_wtbl_update(dev, idx, + MT_WTBL_UPDATE_ADM_COUNT_CLEAR); + + ewma_rssi_init(&mvif->bss_conf.rssi); + + rcu_assign_pointer(dev->mt76.wcid[idx], &mvif->sta.deflink.wcid); + if (vif->txq) { + mtxq = (struct mt76_txq *)vif->txq->drv_priv; + mtxq->wcid = idx; + } + + vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER; + if (phy->chip_cap & MT792x_CHIP_CAP_RSSI_NOTIFY_EVT_EN) + vif->driver_flags |= IEEE80211_VIF_SUPPORTS_CQM_RSSI; + + INIT_WORK(&mvif->csa_work, mt7921_csa_work); + timer_setup(&mvif->csa_timer, mt792x_csa_timer, 0); +out: + mt792x_mutex_release(dev); + + return ret; +} + +static void mt7921_roc_iter(void *priv, u8 *mac, + struct ieee80211_vif *vif) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + struct mt792x_phy *phy = priv; + + mt7921_mcu_abort_roc(phy, mvif, phy->roc_token_id); +} + +void mt7921_roc_abort_sync(struct mt792x_dev *dev) +{ + struct mt792x_phy *phy = &dev->phy; + + if (!test_and_clear_bit(MT76_STATE_ROC, &phy->mt76->state)) + return; + + timer_delete_sync(&phy->roc_timer); + cancel_work(&phy->roc_work); + + ieee80211_iterate_interfaces(mt76_hw(dev), + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7921_roc_iter, (void *)phy); +} +EXPORT_SYMBOL_GPL(mt7921_roc_abort_sync); + +void mt7921_roc_work(struct work_struct *work) +{ + struct mt792x_phy *phy; + + phy = (struct mt792x_phy *)container_of(work, struct mt792x_phy, + roc_work); + + mt792x_mutex_acquire(phy->dev); + if (!test_and_clear_bit(MT76_STATE_ROC, &phy->mt76->state)) { + mt792x_mutex_release(phy->dev); + return; + } + ieee80211_iterate_active_interfaces(phy->mt76->hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7921_roc_iter, phy); + mt792x_mutex_release(phy->dev); + ieee80211_remain_on_channel_expired(phy->mt76->hw); +} + +static int mt7921_abort_roc(struct mt792x_phy *phy, struct mt792x_vif *vif) +{ + int err = 0; + + timer_delete_sync(&phy->roc_timer); + cancel_work_sync(&phy->roc_work); + + mt792x_mutex_acquire(phy->dev); + if (test_and_clear_bit(MT76_STATE_ROC, &phy->mt76->state)) + err = mt7921_mcu_abort_roc(phy, vif, phy->roc_token_id); + mt792x_mutex_release(phy->dev); + + return err; +} + +static int mt7921_set_roc(struct mt792x_phy *phy, + struct mt792x_vif *vif, + struct ieee80211_channel *chan, + int duration, + enum mt7921_roc_req type) +{ + int err; + + if (test_and_set_bit(MT76_STATE_ROC, &phy->mt76->state)) + return -EBUSY; + + phy->roc_grant = false; + + err = mt7921_mcu_set_roc(phy, vif, chan, duration, type, + ++phy->roc_token_id); + if (err < 0) { + clear_bit(MT76_STATE_ROC, &phy->mt76->state); + goto out; + } + + if (!wait_event_timeout(phy->roc_wait, phy->roc_grant, HZ)) { + mt7921_mcu_abort_roc(phy, vif, phy->roc_token_id); + clear_bit(MT76_STATE_ROC, &phy->mt76->state); + err = -ETIMEDOUT; + } + +out: + return err; +} + +static int mt7921_remain_on_channel(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_channel *chan, + int duration, + enum ieee80211_roc_type type) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + struct mt792x_phy *phy = mt792x_hw_phy(hw); + int err; + + mt792x_mutex_acquire(phy->dev); + err = mt7921_set_roc(phy, mvif, chan, duration, MT7921_ROC_REQ_ROC); + mt792x_mutex_release(phy->dev); + + return err; +} + +static int mt7921_cancel_remain_on_channel(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + struct mt792x_phy *phy = mt792x_hw_phy(hw); + + return mt7921_abort_roc(phy, mvif); +} + +int mt7921_set_channel(struct mt76_phy *mphy) +{ + struct mt792x_phy *phy = mphy->priv; + struct mt792x_dev *dev = phy->dev; + int ret; + + mt76_connac_pm_wake(mphy, &dev->pm); + ret = mt7921_mcu_set_chan_info(phy, MCU_EXT_CMD(CHANNEL_SWITCH)); + if (ret) + goto out; + + mt792x_mac_set_timeing(phy); + mt792x_mac_reset_counters(phy); + phy->noise = 0; + +out: + mt76_connac_power_save_sched(mphy, &dev->pm); + + ieee80211_queue_delayed_work(mphy->hw, &mphy->mac_work, + MT792x_WATCHDOG_TIME); + + return ret; +} +EXPORT_SYMBOL_GPL(mt7921_set_channel); + +static int mt7921_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct mt792x_dev *dev = mt792x_hw_dev(hw); + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + struct mt792x_sta *msta = sta ? (struct mt792x_sta *)sta->drv_priv : + &mvif->sta; + struct mt76_wcid *wcid = &msta->deflink.wcid; + u8 *wcid_keyidx = &wcid->hw_key_idx; + int idx = key->keyidx, err = 0; + + /* The hardware does not support per-STA RX GTK, fallback + * to software mode for these. + */ + if ((vif->type == NL80211_IFTYPE_ADHOC || + vif->type == NL80211_IFTYPE_MESH_POINT) && + (key->cipher == WLAN_CIPHER_SUITE_TKIP || + key->cipher == WLAN_CIPHER_SUITE_CCMP) && + !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) + return -EOPNOTSUPP; + + /* fall back to sw encryption for unsupported ciphers */ + switch (key->cipher) { + case WLAN_CIPHER_SUITE_AES_CMAC: + key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIE; + wcid_keyidx = &wcid->hw_key_idx2; + break; + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + if (!mvif->wep_sta) + return -EOPNOTSUPP; + break; + case WLAN_CIPHER_SUITE_TKIP: + case WLAN_CIPHER_SUITE_CCMP: + case WLAN_CIPHER_SUITE_CCMP_256: + case WLAN_CIPHER_SUITE_GCMP: + case WLAN_CIPHER_SUITE_GCMP_256: + case WLAN_CIPHER_SUITE_SMS4: + break; + default: + return -EOPNOTSUPP; + } + + mt792x_mutex_acquire(dev); + + if (cmd == SET_KEY) { + *wcid_keyidx = idx; + } else { + if (idx == *wcid_keyidx) + *wcid_keyidx = -1; + + /* For security issue we don't trigger the key deletion when + * reassociating. But we should trigger the deletion process + * to avoid using incorrect cipher after disconnection, + */ + if (vif->type != NL80211_IFTYPE_STATION || vif->cfg.assoc) + goto out; + } + + mt76_wcid_key_setup(&dev->mt76, wcid, key); + err = mt76_connac_mcu_add_key(&dev->mt76, vif, &msta->deflink.bip, + key, MCU_UNI_CMD(STA_REC_UPDATE), + &msta->deflink.wcid, cmd); + if (err) + goto out; + + if (key->cipher == WLAN_CIPHER_SUITE_WEP104 || + key->cipher == WLAN_CIPHER_SUITE_WEP40) + err = mt76_connac_mcu_add_key(&dev->mt76, vif, + &mvif->wep_sta->deflink.bip, + key, MCU_UNI_CMD(STA_REC_UPDATE), + &mvif->wep_sta->deflink.wcid, cmd); +out: + mt792x_mutex_release(dev); + + return err; +} + +static void +mt7921_pm_interface_iter(void *priv, u8 *mac, struct ieee80211_vif *vif) +{ + struct mt792x_dev *dev = priv; + struct ieee80211_hw *hw = mt76_hw(dev); + bool pm_enable = dev->pm.enable; + int err; + + err = mt7921_mcu_set_beacon_filter(dev, vif, pm_enable); + if (err < 0) + return; + + if (pm_enable) { + vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER; + ieee80211_hw_set(hw, CONNECTION_MONITOR); + } else { + vif->driver_flags &= ~IEEE80211_VIF_BEACON_FILTER; + __clear_bit(IEEE80211_HW_CONNECTION_MONITOR, hw->flags); + } +} + +static void +mt7921_sniffer_interface_iter(void *priv, u8 *mac, struct ieee80211_vif *vif) +{ + struct mt792x_dev *dev = priv; + struct ieee80211_hw *hw = mt76_hw(dev); + struct mt76_connac_pm *pm = &dev->pm; + bool monitor = !!(hw->conf.flags & IEEE80211_CONF_MONITOR); + + mt7921_mcu_set_sniffer(dev, vif, monitor); + pm->enable = pm->enable_user && !monitor; + pm->ds_enable = pm->ds_enable_user && !monitor; + + mt76_connac_mcu_set_deep_sleep(&dev->mt76, pm->ds_enable); + + if (monitor) + mt7921_mcu_set_beacon_filter(dev, vif, false); +} + +void mt7921_set_runtime_pm(struct mt792x_dev *dev) +{ + struct ieee80211_hw *hw = mt76_hw(dev); + struct mt76_connac_pm *pm = &dev->pm; + bool monitor = !!(hw->conf.flags & IEEE80211_CONF_MONITOR); + + pm->enable = pm->enable_user && !monitor; + ieee80211_iterate_active_interfaces(hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7921_pm_interface_iter, dev); + pm->ds_enable = pm->ds_enable_user && !monitor; + mt76_connac_mcu_set_deep_sleep(&dev->mt76, pm->ds_enable); +} + +static int mt7921_config(struct ieee80211_hw *hw, int radio_idx, u32 changed) +{ + struct mt792x_dev *dev = mt792x_hw_dev(hw); + struct mt792x_phy *phy = mt792x_hw_phy(hw); + int ret = 0; + + if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { + ret = mt76_update_channel(phy->mt76); + if (ret) + return ret; + } + + mt792x_mutex_acquire(dev); + + if (changed & IEEE80211_CONF_CHANGE_POWER) { + ret = mt7921_set_tx_sar_pwr(hw, NULL); + if (ret) + goto out; + } + + if (changed & IEEE80211_CONF_CHANGE_MONITOR) { + ieee80211_iterate_active_interfaces(hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7921_sniffer_interface_iter, dev); + } + +out: + mt792x_mutex_release(dev); + + return ret; +} + +static void mt7921_configure_filter(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *total_flags, + u64 multicast) +{ +#define MT7921_FILTER_FCSFAIL BIT(2) +#define MT7921_FILTER_CONTROL BIT(5) +#define MT7921_FILTER_OTHER_BSS BIT(6) +#define MT7921_FILTER_ENABLE BIT(31) + + struct mt792x_dev *dev = mt792x_hw_dev(hw); + u32 flags = MT7921_FILTER_ENABLE; + +#define MT7921_FILTER(_fif, _type) do { \ + if (*total_flags & (_fif)) \ + flags |= MT7921_FILTER_##_type; \ + } while (0) + + MT7921_FILTER(FIF_FCSFAIL, FCSFAIL); + MT7921_FILTER(FIF_CONTROL, CONTROL); + MT7921_FILTER(FIF_OTHER_BSS, OTHER_BSS); + + mt792x_mutex_acquire(dev); + mt7921_mcu_set_rxfilter(dev, flags, 0, 0); + mt792x_mutex_release(dev); + + *total_flags &= (FIF_OTHER_BSS | FIF_FCSFAIL | FIF_CONTROL); +} + +static void mt7921_bss_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, + u64 changed) +{ + struct mt792x_phy *phy = mt792x_hw_phy(hw); + struct mt792x_dev *dev = mt792x_hw_dev(hw); + + mt792x_mutex_acquire(dev); + + if (changed & BSS_CHANGED_ERP_SLOT) { + int slottime = info->use_short_slot ? 9 : 20; + + if (slottime != phy->slottime) { + phy->slottime = slottime; + mt792x_mac_set_timeing(phy); + } + } + + if (changed & (BSS_CHANGED_BEACON | + BSS_CHANGED_BEACON_ENABLED)) + mt7921_mcu_uni_add_beacon_offload(dev, hw, vif, + info->enable_beacon); + + /* ensure that enable txcmd_mode after bss_info */ + if (changed & (BSS_CHANGED_QOS | BSS_CHANGED_BEACON_ENABLED)) + mt7921_mcu_set_tx(dev, vif); + + if (changed & BSS_CHANGED_PS) + mt7921_mcu_uni_bss_ps(dev, vif); + + if (changed & BSS_CHANGED_CQM) + mt7921_mcu_set_rssimonitor(dev, vif); + + if (changed & BSS_CHANGED_ASSOC) { + mt7921_mcu_sta_update(dev, NULL, vif, true, + MT76_STA_INFO_STATE_ASSOC); + mt7921_mcu_set_beacon_filter(dev, vif, vif->cfg.assoc); + } + + if (changed & BSS_CHANGED_ARP_FILTER) { + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + + mt76_connac_mcu_update_arp_filter(&dev->mt76, &mvif->bss_conf.mt76, + info); + } + + mt792x_mutex_release(dev); +} + +static void +mt7921_calc_vif_num(void *priv, u8 *mac, struct ieee80211_vif *vif) +{ + u32 *num = priv; + + if (!priv) + return; + + switch (vif->type) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_P2P_GO: + *num += 1; + break; + default: + break; + } +} + +static void +mt7921_regd_set_6ghz_power_type(struct ieee80211_vif *vif, bool is_add) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + struct mt792x_phy *phy = mvif->phy; + struct mt792x_dev *dev = phy->dev; + u32 valid_vif_num = 0; + + ieee80211_iterate_active_interfaces(mt76_hw(dev), + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7921_calc_vif_num, &valid_vif_num); + + if (valid_vif_num > 1) { + phy->power_type = MT_AP_DEFAULT; + goto out; + } + + if (!is_add) + vif->bss_conf.power_type = IEEE80211_REG_UNSET_AP; + + switch (vif->bss_conf.power_type) { + case IEEE80211_REG_SP_AP: + phy->power_type = MT_AP_SP; + break; + case IEEE80211_REG_VLP_AP: + phy->power_type = MT_AP_VLP; + break; + case IEEE80211_REG_LPI_AP: + phy->power_type = MT_AP_LPI; + break; + case IEEE80211_REG_UNSET_AP: + phy->power_type = MT_AP_UNSET; + break; + default: + phy->power_type = MT_AP_DEFAULT; + break; + } + +out: + if (vif->bss_conf.chanreq.oper.chan->band == NL80211_BAND_6GHZ) + __mt7921_mcu_regd_update(dev, dev->mt76.alpha2, dev->country_ie_env); +} + +int mt7921_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); + struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv; + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + int ret, idx; + + if (sta->aid > MT7921_MAX_AID) + return -ENOENT; + + idx = mt76_wcid_alloc(dev->mt76.wcid_mask, MT792x_WTBL_STA - 1); + if (idx < 0) + return -ENOSPC; + + INIT_LIST_HEAD(&msta->deflink.wcid.poll_list); + msta->vif = mvif; + msta->deflink.wcid.sta = 1; + msta->deflink.wcid.idx = idx; + msta->deflink.wcid.phy_idx = mvif->bss_conf.mt76.band_idx; + msta->deflink.wcid.tx_info |= MT_WCID_TX_INFO_SET; + msta->deflink.last_txs = jiffies; + msta->deflink.sta = msta; + + if (sta->tdls) + set_bit(MT_WCID_FLAG_TDLS_PEER, &msta->deflink.wcid.flags); + + ret = mt76_connac_pm_wake(&dev->mphy, &dev->pm); + if (ret) + return ret; + + if (vif->type == NL80211_IFTYPE_STATION) + mvif->wep_sta = msta; + + mt7921_mac_wtbl_update(dev, idx, + MT_WTBL_UPDATE_ADM_COUNT_CLEAR); + + ret = mt7921_mcu_sta_update(dev, sta, vif, true, + MT76_STA_INFO_STATE_NONE); + if (ret) + return ret; + + mt7921_regd_set_6ghz_power_type(vif, true); + + mt76_connac_power_save_sched(&dev->mphy, &dev->pm); + + return 0; +} +EXPORT_SYMBOL_GPL(mt7921_mac_sta_add); + +int mt7921_mac_sta_event(struct mt76_dev *mdev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, enum mt76_sta_event ev) +{ + struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); + struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv; + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + + if (sta->aid > MT7921_MAX_AID) + return -ENOENT; + + if (ev != MT76_STA_EVENT_ASSOC) + return 0; + + mt792x_mutex_acquire(dev); + + if (vif->type == NL80211_IFTYPE_STATION && !sta->tdls) + mt76_connac_mcu_uni_add_bss(&dev->mphy, vif, &mvif->sta.deflink.wcid, + true, mvif->bss_conf.mt76.ctx); + + ewma_avg_signal_init(&msta->deflink.avg_ack_signal); + + mt7921_mac_wtbl_update(dev, msta->deflink.wcid.idx, + MT_WTBL_UPDATE_ADM_COUNT_CLEAR); + memset(msta->deflink.airtime_ac, 0, sizeof(msta->deflink.airtime_ac)); + + mt7921_mcu_sta_update(dev, sta, vif, true, MT76_STA_INFO_STATE_ASSOC); + + mt792x_mutex_release(dev); + + return 0; +} +EXPORT_SYMBOL_GPL(mt7921_mac_sta_event); + +void mt7921_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); + struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv; + + mt7921_roc_abort_sync(dev); + mt76_connac_free_pending_tx_skbs(&dev->pm, &msta->deflink.wcid); + mt76_connac_pm_wake(&dev->mphy, &dev->pm); + + mt7921_mcu_sta_update(dev, sta, vif, false, MT76_STA_INFO_STATE_NONE); + mt7921_mac_wtbl_update(dev, msta->deflink.wcid.idx, + MT_WTBL_UPDATE_ADM_COUNT_CLEAR); + + if (vif->type == NL80211_IFTYPE_STATION) { + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + + mvif->wep_sta = NULL; + ewma_rssi_init(&mvif->bss_conf.rssi); + if (!sta->tdls) + mt76_connac_mcu_uni_add_bss(&dev->mphy, vif, + &mvif->sta.deflink.wcid, false, + mvif->bss_conf.mt76.ctx); + } + + spin_lock_bh(&dev->mt76.sta_poll_lock); + if (!list_empty(&msta->deflink.wcid.poll_list)) + list_del_init(&msta->deflink.wcid.poll_list); + spin_unlock_bh(&dev->mt76.sta_poll_lock); + + mt7921_regd_set_6ghz_power_type(vif, false); + + mt76_connac_power_save_sched(&dev->mphy, &dev->pm); +} +EXPORT_SYMBOL_GPL(mt7921_mac_sta_remove); + +static int mt7921_set_rts_threshold(struct ieee80211_hw *hw, int radio_idx, + u32 val) +{ + struct mt792x_dev *dev = mt792x_hw_dev(hw); + + mt792x_mutex_acquire(dev); + mt76_connac_mcu_set_rts_thresh(&dev->mt76, val, 0); + mt792x_mutex_release(dev); + + return 0; +} + +static int +mt7921_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_ampdu_params *params) +{ + enum ieee80211_ampdu_mlme_action action = params->action; + struct mt792x_dev *dev = mt792x_hw_dev(hw); + struct ieee80211_sta *sta = params->sta; + struct ieee80211_txq *txq = sta->txq[params->tid]; + struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv; + u16 tid = params->tid; + u16 ssn = params->ssn; + struct mt76_txq *mtxq; + int ret = 0; + + if (!txq) + return -EINVAL; + + mtxq = (struct mt76_txq *)txq->drv_priv; + + mt792x_mutex_acquire(dev); + switch (action) { + case IEEE80211_AMPDU_RX_START: + mt76_rx_aggr_start(&dev->mt76, &msta->deflink.wcid, tid, ssn, + params->buf_size); + mt7921_mcu_uni_rx_ba(dev, params, true); + break; + case IEEE80211_AMPDU_RX_STOP: + mt76_rx_aggr_stop(&dev->mt76, &msta->deflink.wcid, tid); + mt7921_mcu_uni_rx_ba(dev, params, false); + break; + case IEEE80211_AMPDU_TX_OPERATIONAL: + mtxq->aggr = true; + mtxq->send_bar = false; + mt7921_mcu_uni_tx_ba(dev, params, true); + break; + case IEEE80211_AMPDU_TX_STOP_FLUSH: + case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: + mtxq->aggr = false; + clear_bit(tid, &msta->deflink.wcid.ampdu_state); + mt7921_mcu_uni_tx_ba(dev, params, false); + break; + case IEEE80211_AMPDU_TX_START: + set_bit(tid, &msta->deflink.wcid.ampdu_state); + ret = IEEE80211_AMPDU_TX_START_IMMEDIATE; + break; + case IEEE80211_AMPDU_TX_STOP_CONT: + mtxq->aggr = false; + clear_bit(tid, &msta->deflink.wcid.ampdu_state); + mt7921_mcu_uni_tx_ba(dev, params, false); + ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); + break; + } + mt792x_mutex_release(dev); + + return ret; +} + +static int mt7921_sta_state(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + enum ieee80211_sta_state old_state, + enum ieee80211_sta_state new_state) +{ + struct mt792x_dev *dev = mt792x_hw_dev(hw); + + if (dev->pm.ds_enable) { + mt792x_mutex_acquire(dev); + mt76_connac_sta_state_dp(&dev->mt76, old_state, new_state); + mt792x_mutex_release(dev); + } + + return mt76_sta_state(hw, vif, sta, old_state, new_state); +} + +void mt7921_scan_work(struct work_struct *work) +{ + struct mt792x_phy *phy; + + phy = (struct mt792x_phy *)container_of(work, struct mt792x_phy, + scan_work.work); + + while (true) { + struct mt76_connac2_mcu_rxd *rxd; + struct sk_buff *skb; + + spin_lock_bh(&phy->dev->mt76.lock); + skb = __skb_dequeue(&phy->scan_event_list); + spin_unlock_bh(&phy->dev->mt76.lock); + + if (!skb) + break; + + rxd = (struct mt76_connac2_mcu_rxd *)skb->data; + if (rxd->eid == MCU_EVENT_SCHED_SCAN_DONE) { + ieee80211_sched_scan_results(phy->mt76->hw); + } else if (rxd->eid == MCU_EVENT_SCAN_DONE) { + struct mt76_connac_hw_scan_done *event = NULL; + + skb_pull(skb, sizeof(*rxd)); + event = (struct mt76_connac_hw_scan_done *)skb->data; + mt7921_regd_change(phy, event->alpha2); + } + + if (test_and_clear_bit(MT76_HW_SCANNING, + &phy->mt76->state)) { + struct cfg80211_scan_info info = { + .aborted = false, + }; + + ieee80211_scan_completed(phy->mt76->hw, &info); + } + dev_kfree_skb(skb); + } +} + +static int +mt7921_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_scan_request *req) +{ + struct mt792x_dev *dev = mt792x_hw_dev(hw); + struct mt76_phy *mphy = hw->priv; + int err; + + mt792x_mutex_acquire(dev); + err = mt76_connac_mcu_hw_scan(mphy, vif, req); + mt792x_mutex_release(dev); + + return err; +} + +static void +mt7921_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + struct mt792x_dev *dev = mt792x_hw_dev(hw); + struct mt76_phy *mphy = hw->priv; + + mt792x_mutex_acquire(dev); + mt76_connac_mcu_cancel_hw_scan(mphy, vif); + mt792x_mutex_release(dev); +} + +static int +mt7921_start_sched_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct cfg80211_sched_scan_request *req, + struct ieee80211_scan_ies *ies) +{ + struct mt792x_dev *dev = mt792x_hw_dev(hw); + struct mt76_phy *mphy = hw->priv; + int err; + + mt792x_mutex_acquire(dev); + + err = mt76_connac_mcu_sched_scan_req(mphy, vif, req); + if (err < 0) + goto out; + + err = mt76_connac_mcu_sched_scan_enable(mphy, vif, true); +out: + mt792x_mutex_release(dev); + + return err; +} + +static int +mt7921_stop_sched_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + struct mt792x_dev *dev = mt792x_hw_dev(hw); + struct mt76_phy *mphy = hw->priv; + int err; + + mt792x_mutex_acquire(dev); + err = mt76_connac_mcu_sched_scan_enable(mphy, vif, false); + mt792x_mutex_release(dev); + + return err; +} + +static int +mt7921_set_antenna(struct ieee80211_hw *hw, int radio_idx, + u32 tx_ant, u32 rx_ant) +{ + struct mt792x_dev *dev = mt792x_hw_dev(hw); + struct mt792x_phy *phy = mt792x_hw_phy(hw); + int max_nss = hweight8(hw->wiphy->available_antennas_tx); + + if (!tx_ant || tx_ant != rx_ant || ffs(tx_ant) > max_nss) + return -EINVAL; + + if ((BIT(hweight8(tx_ant)) - 1) != tx_ant) + return -EINVAL; + + mt792x_mutex_acquire(dev); + + phy->mt76->antenna_mask = tx_ant; + phy->mt76->chainmask = tx_ant; + + mt76_set_stream_caps(phy->mt76, true); + mt7921_set_stream_he_caps(phy); + + mt792x_mutex_release(dev); + + return 0; +} + +#ifdef CONFIG_PM +static int mt7921_suspend(struct ieee80211_hw *hw, + struct cfg80211_wowlan *wowlan) +{ + struct mt792x_dev *dev = mt792x_hw_dev(hw); + struct mt792x_phy *phy = mt792x_hw_phy(hw); + + cancel_delayed_work_sync(&phy->scan_work); + cancel_delayed_work_sync(&phy->mt76->mac_work); + + cancel_delayed_work_sync(&dev->pm.ps_work); + mt76_connac_free_pending_tx_skbs(&dev->pm, NULL); + + mt792x_mutex_acquire(dev); + + clear_bit(MT76_STATE_RUNNING, &phy->mt76->state); + ieee80211_iterate_active_interfaces(hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7921_mcu_set_suspend_iter, + &dev->mphy); + + mt792x_mutex_release(dev); + + return 0; +} + +static int mt7921_resume(struct ieee80211_hw *hw) +{ + struct mt792x_dev *dev = mt792x_hw_dev(hw); + struct mt792x_phy *phy = mt792x_hw_phy(hw); + + mt792x_mutex_acquire(dev); + + set_bit(MT76_STATE_RUNNING, &phy->mt76->state); + ieee80211_iterate_active_interfaces(hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + mt76_connac_mcu_set_suspend_iter, + &dev->mphy); + + ieee80211_queue_delayed_work(hw, &phy->mt76->mac_work, + MT792x_WATCHDOG_TIME); + + mt792x_mutex_release(dev); + + return 0; +} + +static void mt7921_set_rekey_data(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_gtk_rekey_data *data) +{ + struct mt792x_dev *dev = mt792x_hw_dev(hw); + + mt792x_mutex_acquire(dev); + mt76_connac_mcu_update_gtk_rekey(hw, vif, data); + mt792x_mutex_release(dev); +} +#endif /* CONFIG_PM */ + +static void mt7921_sta_set_decap_offload(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + bool enabled) +{ + struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv; + struct mt792x_dev *dev = mt792x_hw_dev(hw); + + if (!msta->deflink.wcid.sta) + return; + + mt792x_mutex_acquire(dev); + + if (enabled) + set_bit(MT_WCID_FLAG_HDR_TRANS, &msta->deflink.wcid.flags); + else + clear_bit(MT_WCID_FLAG_HDR_TRANS, &msta->deflink.wcid.flags); + + mt76_connac_mcu_sta_update_hdr_trans(&dev->mt76, vif, &msta->deflink.wcid, + MCU_UNI_CMD(STA_REC_UPDATE)); + + mt792x_mutex_release(dev); +} + +#if IS_ENABLED(CONFIG_IPV6) +static void mt7921_ipv6_addr_change(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct inet6_dev *idev) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + struct mt792x_dev *dev = mt792x_hw_dev(hw); + struct inet6_ifaddr *ifa; + struct in6_addr ns_addrs[IEEE80211_BSS_ARP_ADDR_LIST_LEN]; + struct sk_buff *skb; + u8 i, idx = 0; + + struct { + struct { + u8 bss_idx; + u8 pad[3]; + } __packed hdr; + struct mt76_connac_arpns_tlv arpns; + } req_hdr = { + .hdr = { + .bss_idx = mvif->bss_conf.mt76.idx, + }, + .arpns = { + .tag = cpu_to_le16(UNI_OFFLOAD_OFFLOAD_ND), + .mode = 2, /* update */ + .option = 1, /* update only */ + }, + }; + + read_lock_bh(&idev->lock); + list_for_each_entry(ifa, &idev->addr_list, if_list) { + if (ifa->flags & IFA_F_TENTATIVE) + continue; + ns_addrs[idx] = ifa->addr; + if (++idx >= IEEE80211_BSS_ARP_ADDR_LIST_LEN) + break; + } + read_unlock_bh(&idev->lock); + + if (!idx) + return; + + req_hdr.arpns.ips_num = idx; + req_hdr.arpns.len = cpu_to_le16(sizeof(struct mt76_connac_arpns_tlv) + + idx * sizeof(struct in6_addr)); + skb = __mt76_mcu_msg_alloc(&dev->mt76, &req_hdr, + sizeof(req_hdr) + idx * sizeof(struct in6_addr), + sizeof(req_hdr), GFP_ATOMIC); + if (!skb) + return; + + for (i = 0; i < idx; i++) + skb_put_data(skb, &ns_addrs[i].in6_u, sizeof(struct in6_addr)); + + skb_queue_tail(&dev->ipv6_ns_list, skb); + + ieee80211_queue_work(dev->mt76.hw, &dev->ipv6_ns_work); +} +#endif + +int mt7921_set_tx_sar_pwr(struct ieee80211_hw *hw, + const struct cfg80211_sar_specs *sar) +{ + struct mt76_phy *mphy = hw->priv; + + if (sar) { + int err = mt76_init_sar_power(hw, sar); + + if (err) + return err; + } + mt792x_init_acpi_sar_power(mt792x_hw_phy(hw), !sar); + + return mt76_connac_mcu_set_rate_txpower(mphy); +} + +static int mt7921_set_sar_specs(struct ieee80211_hw *hw, + const struct cfg80211_sar_specs *sar) +{ + struct mt792x_dev *dev = mt792x_hw_dev(hw); + int err; + + mt792x_mutex_acquire(dev); + err = mt7921_mcu_set_clc(dev, dev->mt76.alpha2, + dev->country_ie_env); + if (err < 0) + goto out; + + err = mt7921_set_tx_sar_pwr(hw, sar); +out: + mt792x_mutex_release(dev); + + return err; +} + +static void +mt7921_channel_switch_beacon(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_chan_def *chandef) +{ + struct mt792x_dev *dev = mt792x_hw_dev(hw); + + mt792x_mutex_acquire(dev); + mt7921_mcu_uni_add_beacon_offload(dev, hw, vif, true); + mt792x_mutex_release(dev); +} + +static int +mt7921_start_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + struct mt792x_phy *phy = mt792x_hw_phy(hw); + struct mt792x_dev *dev = mt792x_hw_dev(hw); + int err; + + mt792x_mutex_acquire(dev); + + err = mt76_connac_mcu_uni_add_bss(phy->mt76, vif, &mvif->sta.deflink.wcid, + true, mvif->bss_conf.mt76.ctx); + if (err) + goto out; + + err = mt7921_mcu_set_bss_pm(dev, vif, true); + if (err) + goto out; + + err = mt7921_mcu_sta_update(dev, NULL, vif, true, + MT76_STA_INFO_STATE_NONE); +out: + mt792x_mutex_release(dev); + + return err; +} + +static void +mt7921_stop_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + struct mt792x_phy *phy = mt792x_hw_phy(hw); + struct mt792x_dev *dev = mt792x_hw_dev(hw); + int err; + + mt792x_mutex_acquire(dev); + + err = mt7921_mcu_set_bss_pm(dev, vif, false); + if (err) + goto out; + + mt76_connac_mcu_uni_add_bss(phy->mt76, vif, &mvif->sta.deflink.wcid, false, + mvif->bss_conf.mt76.ctx); + +out: + mt792x_mutex_release(dev); +} + +static int +mt7921_add_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *ctx) +{ + struct mt792x_dev *dev = mt792x_hw_dev(hw); + + dev->new_ctx = ctx; + return 0; +} + +static void +mt7921_remove_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *ctx) +{ + struct mt792x_dev *dev = mt792x_hw_dev(hw); + + if (dev->new_ctx == ctx) + dev->new_ctx = NULL; +} + +static void +mt7921_change_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *ctx, + u32 changed) +{ + struct mt792x_chanctx *mctx = (struct mt792x_chanctx *)ctx->drv_priv; + struct mt792x_phy *phy = mt792x_hw_phy(hw); + struct ieee80211_vif *vif; + struct mt792x_vif *mvif; + + if (!mctx->bss_conf) + return; + + mvif = container_of(mctx->bss_conf, struct mt792x_vif, bss_conf); + vif = container_of((void *)mvif, struct ieee80211_vif, drv_priv); + + mt792x_mutex_acquire(phy->dev); + if (vif->type == NL80211_IFTYPE_MONITOR) { + mt7921_mcu_set_sniffer(mvif->phy->dev, vif, true); + mt7921_mcu_config_sniffer(mvif, ctx); + } else { + mt76_connac_mcu_uni_set_chctx(mvif->phy->mt76, &mvif->bss_conf.mt76, ctx); + } + mt792x_mutex_release(phy->dev); +} + +static void mt7921_mgd_prepare_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_prep_tx_info *info) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + struct mt792x_dev *dev = mt792x_hw_dev(hw); + u16 duration = info->duration ? info->duration : + jiffies_to_msecs(HZ); + + mt792x_mutex_acquire(dev); + mt7921_set_roc(mvif->phy, mvif, mvif->bss_conf.mt76.ctx->def.chan, duration, + MT7921_ROC_REQ_JOIN); + mt792x_mutex_release(dev); +} + +static void mt7921_mgd_complete_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_prep_tx_info *info) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + + mt7921_abort_roc(mvif->phy, mvif); +} + +static int mt7921_switch_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif_chanctx_switch *vifs, + int n_vifs, + enum ieee80211_chanctx_switch_mode mode) +{ + return mt792x_assign_vif_chanctx(hw, vifs->vif, vifs->link_conf, + vifs->new_ctx); +} + +void mt7921_csa_work(struct work_struct *work) +{ + struct mt792x_vif *mvif; + struct mt792x_dev *dev; + struct ieee80211_vif *vif; + int ret; + + mvif = (struct mt792x_vif *)container_of(work, struct mt792x_vif, + csa_work); + dev = mvif->phy->dev; + vif = container_of((void *)mvif, struct ieee80211_vif, drv_priv); + + mt792x_mutex_acquire(dev); + ret = mt76_connac_mcu_uni_set_chctx(mvif->phy->mt76, &mvif->bss_conf.mt76, + dev->new_ctx); + mt792x_mutex_release(dev); + + ieee80211_chswitch_done(vif, !ret, 0); +} + +static int mt7921_pre_channel_switch(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_channel_switch *chsw) +{ + if (vif->type != NL80211_IFTYPE_STATION || !vif->cfg.assoc) + return -EOPNOTSUPP; + + if (!cfg80211_chandef_usable(hw->wiphy, &chsw->chandef, + IEEE80211_CHAN_DISABLED)) + return -EOPNOTSUPP; + + return 0; +} + +static void mt7921_channel_switch(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_channel_switch *chsw) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + u16 beacon_interval = vif->bss_conf.beacon_int; + + mvif->csa_timer.expires = TU_TO_EXP_TIME(beacon_interval * chsw->count); + add_timer(&mvif->csa_timer); +} + +static void mt7921_abort_channel_switch(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + + timer_delete_sync(&mvif->csa_timer); + cancel_work_sync(&mvif->csa_work); +} + +static void mt7921_channel_switch_rx_beacon(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_channel_switch *chsw) +{ + struct mt792x_dev *dev = mt792x_hw_dev(hw); + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + u16 beacon_interval = vif->bss_conf.beacon_int; + + if (!dev->new_ctx) + return; + + if (cfg80211_chandef_identical(&chsw->chandef, + &dev->new_ctx->def) && + chsw->count) { + mod_timer(&mvif->csa_timer, + TU_TO_EXP_TIME(beacon_interval * chsw->count)); + } +} + +static void mt7921_rfkill_poll(struct ieee80211_hw *hw) +{ + struct mt792x_phy *phy = mt792x_hw_phy(hw); + int ret = 0; + + mt792x_mutex_acquire(phy->dev); + ret = mt7921_mcu_wf_rf_pin_ctrl(phy, WF_RF_PIN_POLL); + mt792x_mutex_release(phy->dev); + + wiphy_rfkill_set_hw_state(hw->wiphy, ret ? false : true); +} + +const struct ieee80211_ops mt7921_ops = { + .tx = mt792x_tx, + .start = mt7921_start, + .stop = mt7921_stop, + .add_interface = mt7921_add_interface, + .remove_interface = mt792x_remove_interface, + .config = mt7921_config, + .conf_tx = mt792x_conf_tx, + .configure_filter = mt7921_configure_filter, + .bss_info_changed = mt7921_bss_info_changed, + .start_ap = mt7921_start_ap, + .stop_ap = mt7921_stop_ap, + .sta_state = mt7921_sta_state, + .sta_pre_rcu_remove = mt76_sta_pre_rcu_remove, + .set_key = mt7921_set_key, + .sta_set_decap_offload = mt7921_sta_set_decap_offload, +#if IS_ENABLED(CONFIG_IPV6) + .ipv6_addr_change = mt7921_ipv6_addr_change, +#endif /* CONFIG_IPV6 */ + .ampdu_action = mt7921_ampdu_action, + .set_rts_threshold = mt7921_set_rts_threshold, + .wake_tx_queue = mt76_wake_tx_queue, + .release_buffered_frames = mt76_release_buffered_frames, + .channel_switch_beacon = mt7921_channel_switch_beacon, + .get_txpower = mt792x_get_txpower, + .get_stats = mt792x_get_stats, + .get_et_sset_count = mt792x_get_et_sset_count, + .get_et_strings = mt792x_get_et_strings, + .get_et_stats = mt792x_get_et_stats, + .get_tsf = mt792x_get_tsf, + .set_tsf = mt792x_set_tsf, + .get_survey = mt76_get_survey, + .get_antenna = mt76_get_antenna, + .set_antenna = mt7921_set_antenna, + .set_coverage_class = mt792x_set_coverage_class, + .hw_scan = mt7921_hw_scan, + .cancel_hw_scan = mt7921_cancel_hw_scan, + .sta_statistics = mt792x_sta_statistics, + .sched_scan_start = mt7921_start_sched_scan, + .sched_scan_stop = mt7921_stop_sched_scan, + CFG80211_TESTMODE_CMD(mt7921_testmode_cmd) + CFG80211_TESTMODE_DUMP(mt7921_testmode_dump) +#ifdef CONFIG_PM + .suspend = mt7921_suspend, + .resume = mt7921_resume, + .set_wakeup = mt792x_set_wakeup, + .set_rekey_data = mt7921_set_rekey_data, +#endif /* CONFIG_PM */ + .flush = mt792x_flush, + .set_sar_specs = mt7921_set_sar_specs, + .rfkill_poll = mt7921_rfkill_poll, + .remain_on_channel = mt7921_remain_on_channel, + .cancel_remain_on_channel = mt7921_cancel_remain_on_channel, + .add_chanctx = mt7921_add_chanctx, + .remove_chanctx = mt7921_remove_chanctx, + .change_chanctx = mt7921_change_chanctx, + .assign_vif_chanctx = mt792x_assign_vif_chanctx, + .unassign_vif_chanctx = mt792x_unassign_vif_chanctx, + .mgd_prepare_tx = mt7921_mgd_prepare_tx, + .mgd_complete_tx = mt7921_mgd_complete_tx, + .switch_vif_chanctx = mt7921_switch_vif_chanctx, + .pre_channel_switch = mt7921_pre_channel_switch, + .channel_switch = mt7921_channel_switch, + .abort_channel_switch = mt7921_abort_channel_switch, + .channel_switch_rx_beacon = mt7921_channel_switch_rx_beacon, +}; +EXPORT_SYMBOL_GPL(mt7921_ops); + +MODULE_DESCRIPTION("MediaTek MT7921 core driver"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_AUTHOR("Sean Wang "); diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c new file mode 100644 index 000000000..a118a3015 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c @@ -0,0 +1,1518 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2020 MediaTek Inc. */ + +#include +#include +#include "mt7921.h" +#include "regd.h" +#include "mcu.h" +#include "../mt76_connac2_mac.h" +#include "../mt792x_trace.h" + +#define MT_STA_BFER BIT(0) +#define MT_STA_BFEE BIT(1) + +int mt7921_mcu_parse_response(struct mt76_dev *mdev, int cmd, + struct sk_buff *skb, int seq) +{ + int mcu_cmd = FIELD_GET(__MCU_CMD_FIELD_ID, cmd); + struct mt76_connac2_mcu_rxd *rxd; + int ret = 0; + + if (!skb) { + dev_err(mdev->dev, "Message %08x (seq %d) timeout\n", + cmd, seq); + mt792x_reset(mdev); + + return -ETIMEDOUT; + } + + rxd = (struct mt76_connac2_mcu_rxd *)skb->data; + if (seq != rxd->seq) + return -EAGAIN; + + if (cmd == MCU_CMD(PATCH_SEM_CONTROL) || + cmd == MCU_CMD(PATCH_FINISH_REQ)) { + skb_pull(skb, sizeof(*rxd) - 4); + ret = *skb->data; + } else if (cmd == MCU_EXT_CMD(THERMAL_CTRL)) { + skb_pull(skb, sizeof(*rxd) + 4); + ret = le32_to_cpu(*(__le32 *)skb->data); + } else if (cmd == MCU_UNI_CMD(DEV_INFO_UPDATE) || + cmd == MCU_UNI_CMD(BSS_INFO_UPDATE) || + cmd == MCU_UNI_CMD(STA_REC_UPDATE) || + cmd == MCU_UNI_CMD(HIF_CTRL) || + cmd == MCU_UNI_CMD(OFFLOAD) || + cmd == MCU_UNI_CMD(SUSPEND)) { + struct mt76_connac_mcu_uni_event *event; + + skb_pull(skb, sizeof(*rxd)); + event = (struct mt76_connac_mcu_uni_event *)skb->data; + ret = le32_to_cpu(event->status); + /* skip invalid event */ + if (mcu_cmd != event->cid) + ret = -EAGAIN; + } else if (cmd == MCU_CE_QUERY(REG_READ)) { + struct mt76_connac_mcu_reg_event *event; + + skb_pull(skb, sizeof(*rxd)); + event = (struct mt76_connac_mcu_reg_event *)skb->data; + ret = (int)le32_to_cpu(event->val); + } else if (cmd == MCU_EXT_CMD(WF_RF_PIN_CTRL)) { + struct mt7921_wf_rf_pin_ctrl_event *event; + + skb_pull(skb, sizeof(*rxd)); + event = (struct mt7921_wf_rf_pin_ctrl_event *)skb->data; + ret = (int)event->result; + } else { + skb_pull(skb, sizeof(struct mt76_connac2_mcu_rxd)); + } + + return ret; +} +EXPORT_SYMBOL_GPL(mt7921_mcu_parse_response); + +static int mt7921_mcu_read_eeprom(struct mt792x_dev *dev, u32 offset, u8 *val) +{ + struct mt7921_mcu_eeprom_info *res, req = { + .addr = cpu_to_le32(round_down(offset, + MT7921_EEPROM_BLOCK_SIZE)), + }; + struct sk_buff *skb; + int ret; + + ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_EXT_QUERY(EFUSE_ACCESS), + &req, sizeof(req), true, &skb); + if (ret) + return ret; + + res = (struct mt7921_mcu_eeprom_info *)skb->data; + *val = res->data[offset % MT7921_EEPROM_BLOCK_SIZE]; + dev_kfree_skb(skb); + + return 0; +} + +#ifdef CONFIG_PM + +static int +mt7921_mcu_set_ipv6_ns_filter(struct mt76_dev *dev, + struct ieee80211_vif *vif, bool suspend) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + struct { + struct { + u8 bss_idx; + u8 pad[3]; + } __packed hdr; + struct mt76_connac_arpns_tlv arpns; + } req = { + .hdr = { + .bss_idx = mvif->bss_conf.mt76.idx, + }, + .arpns = { + .tag = cpu_to_le16(UNI_OFFLOAD_OFFLOAD_ND), + .len = cpu_to_le16(sizeof(struct mt76_connac_arpns_tlv)), + .mode = suspend, + }, + }; + + return mt76_mcu_send_msg(dev, MCU_UNI_CMD_OFFLOAD, &req, sizeof(req), + true); +} + +void mt7921_mcu_set_suspend_iter(void *priv, u8 *mac, struct ieee80211_vif *vif) +{ + if (IS_ENABLED(CONFIG_IPV6)) { + struct mt76_phy *phy = priv; + + mt7921_mcu_set_ipv6_ns_filter(phy->dev, vif, + !test_bit(MT76_STATE_RUNNING, + &phy->state)); + } + + mt76_connac_mcu_set_suspend_iter(priv, mac, vif); +} + +#endif /* CONFIG_PM */ + +static void +mt7921_mcu_uni_roc_event(struct mt792x_dev *dev, struct sk_buff *skb) +{ + struct mt7921_roc_grant_tlv *grant; + struct mt76_connac2_mcu_rxd *rxd; + int duration; + + rxd = (struct mt76_connac2_mcu_rxd *)skb->data; + grant = (struct mt7921_roc_grant_tlv *)(rxd->tlv + 4); + + /* should never happen */ + WARN_ON_ONCE((le16_to_cpu(grant->tag) != UNI_EVENT_ROC_GRANT)); + + if (grant->reqtype == MT7921_ROC_REQ_ROC) + ieee80211_ready_on_channel(dev->mt76.phy.hw); + + dev->phy.roc_grant = true; + wake_up(&dev->phy.roc_wait); + duration = le32_to_cpu(grant->max_interval); + mod_timer(&dev->phy.roc_timer, + jiffies + msecs_to_jiffies(duration)); +} + +static void +mt7921_mcu_scan_event(struct mt792x_dev *dev, struct sk_buff *skb) +{ + struct mt76_phy *mphy = &dev->mt76.phy; + struct mt792x_phy *phy = mphy->priv; + + spin_lock_bh(&dev->mt76.lock); + __skb_queue_tail(&phy->scan_event_list, skb); + spin_unlock_bh(&dev->mt76.lock); + + ieee80211_queue_delayed_work(mphy->hw, &phy->scan_work, + MT792x_HW_SCAN_TIMEOUT); +} + +static void +mt7921_mcu_connection_loss_iter(void *priv, u8 *mac, + struct ieee80211_vif *vif) +{ + struct mt76_vif_link *mvif = (struct mt76_vif_link *)vif->drv_priv; + struct mt76_connac_beacon_loss_event *event = priv; + + if (mvif->idx != event->bss_idx) + return; + + if (!(vif->driver_flags & IEEE80211_VIF_BEACON_FILTER) || + vif->type != NL80211_IFTYPE_STATION) + return; + + ieee80211_connection_loss(vif); +} + +static void +mt7921_mcu_connection_loss_event(struct mt792x_dev *dev, struct sk_buff *skb) +{ + struct mt76_connac_beacon_loss_event *event; + struct mt76_phy *mphy = &dev->mt76.phy; + + skb_pull(skb, sizeof(struct mt76_connac2_mcu_rxd)); + event = (struct mt76_connac_beacon_loss_event *)skb->data; + + ieee80211_iterate_active_interfaces_atomic(mphy->hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7921_mcu_connection_loss_iter, event); +} + +static void +mt7921_mcu_debug_msg_event(struct mt792x_dev *dev, struct sk_buff *skb) +{ + struct mt7921_debug_msg { + __le16 id; + u8 type; + u8 flag; + __le32 value; + __le16 len; + u8 content[512]; + } __packed * msg; + + skb_pull(skb, sizeof(struct mt76_connac2_mcu_rxd)); + msg = (struct mt7921_debug_msg *)skb->data; + + if (msg->type == 3) { /* fw log */ + u16 len = min_t(u16, le16_to_cpu(msg->len), 512); + int i; + + for (i = 0 ; i < len; i++) { + if (!msg->content[i]) + msg->content[i] = ' '; + } + wiphy_info(mt76_hw(dev)->wiphy, "%.*s", len, msg->content); + } +} + +static void +mt7921_mcu_low_power_event(struct mt792x_dev *dev, struct sk_buff *skb) +{ + struct mt7921_mcu_lp_event { + u8 state; + u8 reserved[3]; + } __packed * event; + + skb_pull(skb, sizeof(struct mt76_connac2_mcu_rxd)); + event = (struct mt7921_mcu_lp_event *)skb->data; + + trace_lp_event(dev, event->state); +} + +static void +mt7921_mcu_tx_done_event(struct mt792x_dev *dev, struct sk_buff *skb) +{ + struct mt7921_mcu_tx_done_event *event; + + skb_pull(skb, sizeof(struct mt76_connac2_mcu_rxd)); + event = (struct mt7921_mcu_tx_done_event *)skb->data; + + mt7921_mac_add_txs(dev, event->txs); +} + +static void +mt7921_mcu_rssi_monitor_iter(void *priv, u8 *mac, + struct ieee80211_vif *vif) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + struct mt76_connac_rssi_notify_event *event = priv; + enum nl80211_cqm_rssi_threshold_event nl_event; + s32 rssi = le32_to_cpu(event->rssi[mvif->bss_conf.mt76.idx]); + + if (!rssi) + return; + + if (!(vif->driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI)) + return; + + if (rssi > vif->bss_conf.cqm_rssi_thold) + nl_event = NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH; + else + nl_event = NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW; + + ieee80211_cqm_rssi_notify(vif, nl_event, rssi, GFP_KERNEL); +} + +static void +mt7921_mcu_rssi_monitor_event(struct mt792x_dev *dev, struct sk_buff *skb) +{ + struct mt76_connac_rssi_notify_event *event; + + skb_pull(skb, sizeof(struct mt76_connac2_mcu_rxd)); + event = (struct mt76_connac_rssi_notify_event *)skb->data; + + ieee80211_iterate_active_interfaces_atomic(mt76_hw(dev), + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7921_mcu_rssi_monitor_iter, event); +} + +static void +mt7921_mcu_rx_unsolicited_event(struct mt792x_dev *dev, struct sk_buff *skb) +{ + struct mt76_connac2_mcu_rxd *rxd; + + rxd = (struct mt76_connac2_mcu_rxd *)skb->data; + switch (rxd->eid) { + case MCU_EVENT_BSS_BEACON_LOSS: + mt7921_mcu_connection_loss_event(dev, skb); + break; + case MCU_EVENT_SCHED_SCAN_DONE: + case MCU_EVENT_SCAN_DONE: + mt7921_mcu_scan_event(dev, skb); + return; + case MCU_EVENT_DBG_MSG: + mt7921_mcu_debug_msg_event(dev, skb); + break; + case MCU_EVENT_COREDUMP: + dev->fw_assert = true; + mt76_connac_mcu_coredump_event(&dev->mt76, skb, + &dev->coredump); + return; + case MCU_EVENT_LP_INFO: + mt7921_mcu_low_power_event(dev, skb); + break; + case MCU_EVENT_TX_DONE: + mt7921_mcu_tx_done_event(dev, skb); + break; + case MCU_EVENT_RSSI_NOTIFY: + mt7921_mcu_rssi_monitor_event(dev, skb); + break; + default: + break; + } + dev_kfree_skb(skb); +} + +static void +mt7921_mcu_uni_rx_unsolicited_event(struct mt792x_dev *dev, + struct sk_buff *skb) +{ + struct mt76_connac2_mcu_rxd *rxd; + + rxd = (struct mt76_connac2_mcu_rxd *)skb->data; + + switch (rxd->eid) { + case MCU_UNI_EVENT_ROC: + mt7921_mcu_uni_roc_event(dev, skb); + break; + default: + break; + } + dev_kfree_skb(skb); +} + +void mt7921_mcu_rx_event(struct mt792x_dev *dev, struct sk_buff *skb) +{ + struct mt76_connac2_mcu_rxd *rxd; + + if (skb_linearize(skb)) { + dev_kfree_skb(skb); + return; + } + + rxd = (struct mt76_connac2_mcu_rxd *)skb->data; + + if (rxd->option & MCU_UNI_CMD_UNSOLICITED_EVENT) { + mt7921_mcu_uni_rx_unsolicited_event(dev, skb); + return; + } + + if (rxd->eid == 0x6) { + mt76_mcu_rx_event(&dev->mt76, skb); + return; + } + + if (rxd->ext_eid == MCU_EXT_EVENT_RATE_REPORT || + rxd->eid == MCU_EVENT_BSS_BEACON_LOSS || + rxd->eid == MCU_EVENT_SCHED_SCAN_DONE || + rxd->eid == MCU_EVENT_RSSI_NOTIFY || + rxd->eid == MCU_EVENT_SCAN_DONE || + rxd->eid == MCU_EVENT_TX_DONE || + rxd->eid == MCU_EVENT_DBG_MSG || + rxd->eid == MCU_EVENT_COREDUMP || + rxd->eid == MCU_EVENT_LP_INFO || + !rxd->seq) + mt7921_mcu_rx_unsolicited_event(dev, skb); + else + mt76_mcu_rx_event(&dev->mt76, skb); +} + +/** starec & wtbl **/ +int mt7921_mcu_uni_tx_ba(struct mt792x_dev *dev, + struct ieee80211_ampdu_params *params, + bool enable) +{ + struct mt792x_sta *msta = (struct mt792x_sta *)params->sta->drv_priv; + + if (enable && !params->amsdu) + msta->deflink.wcid.amsdu = false; + + return mt76_connac_mcu_sta_ba(&dev->mt76, &msta->vif->bss_conf.mt76, params, + MCU_UNI_CMD(STA_REC_UPDATE), + enable, true); +} + +int mt7921_mcu_uni_rx_ba(struct mt792x_dev *dev, + struct ieee80211_ampdu_params *params, + bool enable) +{ + struct mt792x_sta *msta = (struct mt792x_sta *)params->sta->drv_priv; + + return mt76_connac_mcu_sta_ba(&dev->mt76, &msta->vif->bss_conf.mt76, params, + MCU_UNI_CMD(STA_REC_UPDATE), + enable, false); +} + +static int mt7921_load_clc(struct mt792x_dev *dev, const char *fw_name) +{ + const struct mt76_connac2_fw_trailer *hdr; + const struct mt76_connac2_fw_region *region; + const struct mt7921_clc *clc; + struct mt76_dev *mdev = &dev->mt76; + struct mt792x_phy *phy = &dev->phy; + const struct firmware *fw; + size_t clc_len, fw_data_len, len, offset = 0; + int ret, i; + u8 *clc_base = NULL, hw_encap = 0; + + dev->phy.clc_chan_conf = 0xff; + dev->regd_user = false; + if (!mt7921_regd_clc_supported(dev)) + return 0; + + if (mt76_is_mmio(&dev->mt76)) { + ret = mt7921_mcu_read_eeprom(dev, MT_EE_HW_TYPE, &hw_encap); + if (ret) + return ret; + hw_encap = u8_get_bits(hw_encap, MT_EE_HW_TYPE_ENCAP); + } + + ret = request_firmware(&fw, fw_name, mdev->dev); + if (ret) + return ret; + + if (!fw || !fw->data || fw->size < sizeof(*hdr)) { + dev_err(mdev->dev, "Invalid firmware\n"); + ret = -EINVAL; + goto out; + } + + hdr = (const void *)(fw->data + fw->size - sizeof(*hdr)); + if (hdr->n_region > (fw->size - sizeof(*hdr)) / sizeof(*region)) { + dev_err(mdev->dev, "Invalid firmware region table\n"); + ret = -EINVAL; + goto out; + } + fw_data_len = fw->size - sizeof(*hdr) - + hdr->n_region * sizeof(*region); + + for (i = 0; i < hdr->n_region; i++) { + region = (const void *)((const u8 *)hdr - + (hdr->n_region - i) * sizeof(*region)); + len = le32_to_cpu(region->len); + + /* check if we have valid buffer size */ + if (len > fw_data_len - offset) { + dev_err(mdev->dev, "Invalid firmware region\n"); + ret = -EINVAL; + goto out; + } + + if ((region->feature_set & FW_FEATURE_NON_DL) && + region->type == FW_TYPE_CLC) { + clc_base = (u8 *)(fw->data + offset); + break; + } + offset += len; + } + + if (!clc_base) + goto out; + + for (offset = 0; offset < len; offset += clc_len) { + if (len - offset < sizeof(*clc)) { + ret = -EINVAL; + goto out; + } + + clc = (const struct mt7921_clc *)(clc_base + offset); + clc_len = le32_to_cpu(clc->len); + if (clc_len < sizeof(*clc) || clc_len > len - offset || + clc->idx >= ARRAY_SIZE(phy->clc)) { + ret = -EINVAL; + goto out; + } + + /* do not init buf again if chip reset triggered */ + if (phy->clc[clc->idx]) + continue; + + /* header content sanity */ + if (clc->idx == MT7921_CLC_POWER && + u8_get_bits(clc->type, MT_EE_HW_TYPE_ENCAP) != hw_encap) + continue; + + phy->clc[clc->idx] = devm_kmemdup(mdev->dev, clc, + clc_len, + GFP_KERNEL); + + if (!phy->clc[clc->idx]) { + ret = -ENOMEM; + goto out; + } + } + + ret = mt7921_regd_init(phy); +out: + release_firmware(fw); + + return ret; +} + +static void mt7921_mcu_parse_tx_resource(struct mt76_dev *dev, + struct sk_buff *skb) +{ + struct mt76_sdio *sdio = &dev->sdio; + struct mt7921_tx_resource { + __le32 version; + __le32 pse_data_quota; + __le32 pse_mcu_quota; + __le32 ple_data_quota; + __le32 ple_mcu_quota; + __le16 pse_page_size; + __le16 ple_page_size; + u8 pp_padding; + u8 pad[3]; + } __packed * tx_res; + + tx_res = (struct mt7921_tx_resource *)skb->data; + sdio->sched.pse_data_quota = le32_to_cpu(tx_res->pse_data_quota); + sdio->pse_mcu_quota_max = le32_to_cpu(tx_res->pse_mcu_quota); + /* The mcu quota usage of this function itself must be taken into consideration */ + sdio->sched.pse_mcu_quota = + sdio->sched.pse_mcu_quota ? sdio->pse_mcu_quota_max : sdio->pse_mcu_quota_max - 1; + sdio->sched.ple_data_quota = le32_to_cpu(tx_res->ple_data_quota); + sdio->sched.pse_page_size = le16_to_cpu(tx_res->pse_page_size); + sdio->sched.deficit = tx_res->pp_padding; +} + +static void mt7921_mcu_parse_phy_cap(struct mt76_dev *dev, + struct sk_buff *skb) +{ + struct mt7921_phy_cap { + u8 ht; + u8 vht; + u8 _5g; + u8 max_bw; + u8 nss; + u8 dbdc; + u8 tx_ldpc; + u8 rx_ldpc; + u8 tx_stbc; + u8 rx_stbc; + u8 hw_path; + u8 he; + } __packed * cap; + + enum { + WF0_24G, + WF0_5G + }; + + cap = (struct mt7921_phy_cap *)skb->data; + + dev->phy.antenna_mask = BIT(cap->nss) - 1; + dev->phy.chainmask = dev->phy.antenna_mask; + dev->phy.cap.has_2ghz = cap->hw_path & BIT(WF0_24G); + dev->phy.cap.has_5ghz = cap->hw_path & BIT(WF0_5G); +} + +static int mt7921_mcu_get_nic_capability(struct mt792x_phy *mphy) +{ + struct mt76_connac_cap_hdr { + __le16 n_element; + u8 rsv[2]; + } __packed * hdr; + struct sk_buff *skb; + struct mt76_phy *phy = mphy->mt76; + int ret, i; + + ret = mt76_mcu_send_and_get_msg(phy->dev, MCU_CE_CMD(GET_NIC_CAPAB), + NULL, 0, true, &skb); + if (ret) + return ret; + + hdr = (struct mt76_connac_cap_hdr *)skb->data; + if (skb->len < sizeof(*hdr)) { + ret = -EINVAL; + goto out; + } + + skb_pull(skb, sizeof(*hdr)); + + for (i = 0; i < le16_to_cpu(hdr->n_element); i++) { + struct tlv_hdr { + __le32 type; + __le32 len; + } __packed * tlv = (struct tlv_hdr *)skb->data; + int len; + + if (skb->len < sizeof(*tlv)) + break; + + skb_pull(skb, sizeof(*tlv)); + + len = le32_to_cpu(tlv->len); + if (skb->len < len) + break; + + switch (le32_to_cpu(tlv->type)) { + case MT_NIC_CAP_6G: + phy->cap.has_6ghz = skb->data[0]; + break; + case MT_NIC_CAP_MAC_ADDR: + memcpy(phy->macaddr, (void *)skb->data, ETH_ALEN); + break; + case MT_NIC_CAP_PHY: + mt7921_mcu_parse_phy_cap(phy->dev, skb); + break; + case MT_NIC_CAP_TX_RESOURCE: + if (mt76_is_sdio(phy->dev)) + mt7921_mcu_parse_tx_resource(phy->dev, + skb); + break; + case MT_NIC_CAP_CHIP_CAP: + memcpy(&mphy->chip_cap, (void *)skb->data, sizeof(u64)); + break; + default: + break; + } + skb_pull(skb, len); + } +out: + dev_kfree_skb(skb); + + return ret; +} + +int mt7921_mcu_fw_log_2_host(struct mt792x_dev *dev, u8 ctrl) +{ + struct { + u8 ctrl_val; + u8 pad[3]; + } data = { + .ctrl_val = ctrl + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_CE_CMD(FWLOG_2_HOST), + &data, sizeof(data), false); +} + +int mt7921_run_firmware(struct mt792x_dev *dev) +{ + int err; + + err = mt792x_load_firmware(dev); + if (err) + return err; + + err = mt7921_mcu_get_nic_capability(&dev->phy); + if (err) + return err; + + err = mt7921_load_clc(dev, mt792x_ram_name(dev)); + if (err) + return err; + set_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state); + + return mt7921_mcu_fw_log_2_host(dev, 1); +} +EXPORT_SYMBOL_GPL(mt7921_run_firmware); + +int mt7921_mcu_radio_led_ctrl(struct mt792x_dev *dev, u8 value) +{ + struct { + u8 ctrlid; + u8 rsv[3]; + } __packed req = { + .ctrlid = value, + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(ID_RADIO_ON_OFF_CTRL), + &req, sizeof(req), false); +} +EXPORT_SYMBOL_GPL(mt7921_mcu_radio_led_ctrl); + +int mt7921_mcu_set_tx(struct mt792x_dev *dev, struct ieee80211_vif *vif) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + struct edca { + __le16 cw_min; + __le16 cw_max; + __le16 txop; + __le16 aifs; + u8 guardtime; + u8 acm; + } __packed; + struct mt7921_mcu_tx { + struct edca edca[IEEE80211_NUM_ACS]; + u8 bss_idx; + u8 qos; + u8 wmm_idx; + u8 pad; + } __packed req = { + .bss_idx = mvif->bss_conf.mt76.idx, + .qos = vif->bss_conf.qos, + .wmm_idx = mvif->bss_conf.mt76.wmm_idx, + }; + struct mu_edca { + u8 cw_min; + u8 cw_max; + u8 aifsn; + u8 acm; + u8 timer; + u8 padding[3]; + }; + struct mt7921_mcu_mu_tx { + u8 ver; + u8 pad0; + __le16 len; + u8 bss_idx; + u8 qos; + u8 wmm_idx; + u8 pad1; + struct mu_edca edca[IEEE80211_NUM_ACS]; + u8 pad3[32]; + } __packed req_mu = { + .bss_idx = mvif->bss_conf.mt76.idx, + .qos = vif->bss_conf.qos, + .wmm_idx = mvif->bss_conf.mt76.wmm_idx, + }; + static const int to_aci[] = { 1, 0, 2, 3 }; + int ac, ret; + + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { + struct ieee80211_tx_queue_params *q = &mvif->bss_conf.queue_params[ac]; + struct edca *e = &req.edca[to_aci[ac]]; + + e->aifs = cpu_to_le16(q->aifs); + e->txop = cpu_to_le16(q->txop); + + if (q->cw_min) + e->cw_min = cpu_to_le16(q->cw_min); + else + e->cw_min = cpu_to_le16(5); + + if (q->cw_max) + e->cw_max = cpu_to_le16(q->cw_max); + else + e->cw_max = cpu_to_le16(10); + } + + ret = mt76_mcu_send_msg(&dev->mt76, MCU_CE_CMD(SET_EDCA_PARMS), &req, + sizeof(req), false); + if (ret) + return ret; + + if (!vif->bss_conf.he_support) + return 0; + + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { + struct ieee80211_he_mu_edca_param_ac_rec *q; + struct mu_edca *e; + + if (!mvif->bss_conf.queue_params[ac].mu_edca) + break; + + q = &mvif->bss_conf.queue_params[ac].mu_edca_param_rec; + e = &(req_mu.edca[to_aci[ac]]); + + e->cw_min = q->ecw_min_max & 0xf; + e->cw_max = (q->ecw_min_max & 0xf0) >> 4; + e->aifsn = q->aifsn; + e->timer = q->mu_edca_timer; + } + + return mt76_mcu_send_msg(&dev->mt76, MCU_CE_CMD(SET_MU_EDCA_PARMS), + &req_mu, sizeof(req_mu), false); +} + +int mt7921_mcu_set_roc(struct mt792x_phy *phy, struct mt792x_vif *vif, + struct ieee80211_channel *chan, int duration, + enum mt7921_roc_req type, u8 token_id) +{ + int center_ch = ieee80211_frequency_to_channel(chan->center_freq); + struct mt792x_dev *dev = phy->dev; + struct { + struct { + u8 rsv[4]; + } __packed hdr; + struct roc_acquire_tlv { + __le16 tag; + __le16 len; + u8 bss_idx; + u8 tokenid; + u8 control_channel; + u8 sco; + u8 band; + u8 bw; + u8 center_chan; + u8 center_chan2; + u8 bw_from_ap; + u8 center_chan_from_ap; + u8 center_chan2_from_ap; + u8 reqtype; + __le32 maxinterval; + u8 dbdcband; + u8 rsv[3]; + } __packed roc; + } __packed req = { + .roc = { + .tag = cpu_to_le16(UNI_ROC_ACQUIRE), + .len = cpu_to_le16(sizeof(struct roc_acquire_tlv)), + .tokenid = token_id, + .reqtype = type, + .maxinterval = cpu_to_le32(duration), + .bss_idx = vif->bss_conf.mt76.idx, + .control_channel = chan->hw_value, + .bw = CMD_CBW_20MHZ, + .bw_from_ap = CMD_CBW_20MHZ, + .center_chan = center_ch, + .center_chan_from_ap = center_ch, + .dbdcband = 0xff, /* auto */ + }, + }; + + if (chan->hw_value < center_ch) + req.roc.sco = 1; /* SCA */ + else if (chan->hw_value > center_ch) + req.roc.sco = 3; /* SCB */ + + switch (chan->band) { + case NL80211_BAND_6GHZ: + req.roc.band = 3; + break; + case NL80211_BAND_5GHZ: + req.roc.band = 2; + break; + default: + req.roc.band = 1; + break; + } + + return mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(ROC), + &req, sizeof(req), false); +} + +int mt7921_mcu_abort_roc(struct mt792x_phy *phy, struct mt792x_vif *vif, + u8 token_id) +{ + struct mt792x_dev *dev = phy->dev; + struct { + struct { + u8 rsv[4]; + } __packed hdr; + struct roc_abort_tlv { + __le16 tag; + __le16 len; + u8 bss_idx; + u8 tokenid; + u8 dbdcband; + u8 rsv[5]; + } __packed abort; + } __packed req = { + .abort = { + .tag = cpu_to_le16(UNI_ROC_ABORT), + .len = cpu_to_le16(sizeof(struct roc_abort_tlv)), + .tokenid = token_id, + .bss_idx = vif->bss_conf.mt76.idx, + .dbdcband = 0xff, /* auto*/ + }, + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(ROC), + &req, sizeof(req), false); +} + +int mt7921_mcu_set_chan_info(struct mt792x_phy *phy, int cmd) +{ + struct mt792x_dev *dev = phy->dev; + struct cfg80211_chan_def *chandef = &phy->mt76->chandef; + int freq1 = chandef->center_freq1; + struct { + u8 control_ch; + u8 center_ch; + u8 bw; + u8 tx_streams_num; + u8 rx_streams; /* mask or num */ + u8 switch_reason; + u8 band_idx; + u8 center_ch2; /* for 80+80 only */ + __le16 cac_case; + u8 channel_band; + u8 rsv0; + __le32 outband_freq; + u8 txpower_drop; + u8 ap_bw; + u8 ap_center_ch; + u8 rsv1[57]; + } __packed req = { + .control_ch = chandef->chan->hw_value, + .center_ch = ieee80211_frequency_to_channel(freq1), + .bw = mt76_connac_chan_bw(chandef), + .tx_streams_num = hweight8(phy->mt76->antenna_mask), + .rx_streams = phy->mt76->antenna_mask, + .band_idx = phy != &dev->phy, + }; + + if (chandef->chan->band == NL80211_BAND_6GHZ) + req.channel_band = 2; + else + req.channel_band = chandef->chan->band; + + if (cmd == MCU_EXT_CMD(SET_RX_PATH) || + dev->mt76.hw->conf.flags & IEEE80211_CONF_MONITOR) + req.switch_reason = CH_SWITCH_NORMAL; + else if (phy->mt76->offchannel) + req.switch_reason = CH_SWITCH_SCAN_BYPASS_DPD; + else if (!cfg80211_reg_can_beacon(dev->mt76.hw->wiphy, chandef, + NL80211_IFTYPE_AP)) + req.switch_reason = CH_SWITCH_DFS; + else + req.switch_reason = CH_SWITCH_NORMAL; + + if (cmd == MCU_EXT_CMD(CHANNEL_SWITCH)) + req.rx_streams = hweight8(req.rx_streams); + + if (chandef->width == NL80211_CHAN_WIDTH_80P80) { + int freq2 = chandef->center_freq2; + + req.center_ch2 = ieee80211_frequency_to_channel(freq2); + } + + return mt76_mcu_send_msg(&dev->mt76, cmd, &req, sizeof(req), true); +} + +int mt7921_mcu_set_eeprom(struct mt792x_dev *dev) +{ + struct req_hdr { + u8 buffer_mode; + u8 format; + __le16 len; + } __packed req = { + .buffer_mode = EE_MODE_EFUSE, + .format = EE_FORMAT_WHOLE, + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(EFUSE_BUFFER_MODE), + &req, sizeof(req), true); +} +EXPORT_SYMBOL_GPL(mt7921_mcu_set_eeprom); + +int mt7921_mcu_uni_bss_ps(struct mt792x_dev *dev, struct ieee80211_vif *vif) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + struct { + struct { + u8 bss_idx; + u8 pad[3]; + } __packed hdr; + struct ps_tlv { + __le16 tag; + __le16 len; + u8 ps_state; /* 0: device awake + * 1: static power save + * 2: dynamic power saving + * 3: enter TWT power saving + * 4: leave TWT power saving + */ + u8 pad[3]; + } __packed ps; + } __packed ps_req = { + .hdr = { + .bss_idx = mvif->bss_conf.mt76.idx, + }, + .ps = { + .tag = cpu_to_le16(UNI_BSS_INFO_PS), + .len = cpu_to_le16(sizeof(struct ps_tlv)), + .ps_state = vif->cfg.ps ? 2 : 0, + }, + }; + + if (vif->type != NL80211_IFTYPE_STATION) + return -EOPNOTSUPP; + + return mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(BSS_INFO_UPDATE), + &ps_req, sizeof(ps_req), true); +} + +static int +mt7921_mcu_uni_bss_bcnft(struct mt792x_dev *dev, struct ieee80211_vif *vif, + bool enable) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + struct { + struct { + u8 bss_idx; + u8 pad[3]; + } __packed hdr; + struct bcnft_tlv { + __le16 tag; + __le16 len; + __le16 bcn_interval; + u8 dtim_period; + u8 pad; + } __packed bcnft; + } __packed bcnft_req = { + .hdr = { + .bss_idx = mvif->bss_conf.mt76.idx, + }, + .bcnft = { + .tag = cpu_to_le16(UNI_BSS_INFO_BCNFT), + .len = cpu_to_le16(sizeof(struct bcnft_tlv)), + .bcn_interval = cpu_to_le16(vif->bss_conf.beacon_int), + .dtim_period = vif->bss_conf.dtim_period, + }, + }; + + if (vif->type != NL80211_IFTYPE_STATION) + return 0; + + return mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(BSS_INFO_UPDATE), + &bcnft_req, sizeof(bcnft_req), true); +} + +int +mt7921_mcu_set_bss_pm(struct mt792x_dev *dev, struct ieee80211_vif *vif, + bool enable) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + struct { + u8 bss_idx; + u8 dtim_period; + __le16 aid; + __le16 bcn_interval; + __le16 atim_window; + u8 uapsd; + u8 bmc_delivered_ac; + u8 bmc_triggered_ac; + u8 pad; + } req = { + .bss_idx = mvif->bss_conf.mt76.idx, + .aid = cpu_to_le16(vif->cfg.aid), + .dtim_period = vif->bss_conf.dtim_period, + .bcn_interval = cpu_to_le16(vif->bss_conf.beacon_int), + }; + struct { + u8 bss_idx; + u8 pad[3]; + } req_hdr = { + .bss_idx = mvif->bss_conf.mt76.idx, + }; + int err; + + err = mt76_mcu_send_msg(&dev->mt76, MCU_CE_CMD(SET_BSS_ABORT), + &req_hdr, sizeof(req_hdr), false); + if (err < 0 || !enable) + return err; + + return mt76_mcu_send_msg(&dev->mt76, MCU_CE_CMD(SET_BSS_CONNECTED), + &req, sizeof(req), false); +} + +int mt7921_mcu_sta_update(struct mt792x_dev *dev, struct ieee80211_sta *sta, + struct ieee80211_vif *vif, bool enable, + enum mt76_sta_info_state state) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + int rssi = -ewma_rssi_read(&mvif->bss_conf.rssi); + struct mt76_sta_cmd_info info = { + .sta = sta, + .vif = vif, + .enable = enable, + .cmd = MCU_UNI_CMD(STA_REC_UPDATE), + .state = state, + .offload_fw = true, + .rcpi = to_rcpi(rssi), + }; + struct mt792x_sta *msta; + + msta = sta ? (struct mt792x_sta *)sta->drv_priv : NULL; + info.wcid = msta ? &msta->deflink.wcid : &mvif->sta.deflink.wcid; + info.newly = msta ? state != MT76_STA_INFO_STATE_ASSOC : true; + + return mt76_connac_mcu_sta_cmd(&dev->mphy, &info); +} + +int mt7921_mcu_set_beacon_filter(struct mt792x_dev *dev, + struct ieee80211_vif *vif, + bool enable) +{ +#define MT7921_FIF_BIT_CLR BIT(1) +#define MT7921_FIF_BIT_SET BIT(0) + int err; + + if (enable) { + err = mt7921_mcu_uni_bss_bcnft(dev, vif, true); + if (err) + return err; + + err = mt7921_mcu_set_rxfilter(dev, 0, + MT7921_FIF_BIT_SET, + MT_WF_RFCR_DROP_OTHER_BEACON); + if (err) + return err; + + return 0; + } + + err = mt7921_mcu_set_bss_pm(dev, vif, false); + if (err) + return err; + + err = mt7921_mcu_set_rxfilter(dev, 0, + MT7921_FIF_BIT_CLR, + MT_WF_RFCR_DROP_OTHER_BEACON); + if (err) + return err; + + return 0; +} + +int mt7921_get_txpwr_info(struct mt792x_dev *dev, struct mt7921_txpwr *txpwr) +{ + struct mt7921_txpwr_event *event; + struct mt7921_txpwr_req req = { + .dbdc_idx = 0, + }; + struct sk_buff *skb; + int ret; + + ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_CE_CMD(GET_TXPWR), + &req, sizeof(req), true, &skb); + if (ret) + return ret; + + event = (struct mt7921_txpwr_event *)skb->data; + WARN_ON(skb->len != le16_to_cpu(event->len)); + memcpy(txpwr, &event->txpwr, sizeof(event->txpwr)); + + dev_kfree_skb(skb); + + return 0; +} + +int mt7921_mcu_set_sniffer(struct mt792x_dev *dev, struct ieee80211_vif *vif, + bool enable) +{ + struct mt76_vif_link *mvif = (struct mt76_vif_link *)vif->drv_priv; + struct { + struct { + u8 band_idx; + u8 pad[3]; + } __packed hdr; + struct sniffer_enable_tlv { + __le16 tag; + __le16 len; + u8 enable; + u8 pad[3]; + } __packed enable; + } req = { + .hdr = { + .band_idx = mvif->band_idx, + }, + .enable = { + .tag = cpu_to_le16(0), + .len = cpu_to_le16(sizeof(struct sniffer_enable_tlv)), + .enable = enable, + }, + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(SNIFFER), &req, sizeof(req), + true); +} + +int mt7921_mcu_config_sniffer(struct mt792x_vif *vif, + struct ieee80211_chanctx_conf *ctx) +{ + struct cfg80211_chan_def *chandef = &ctx->def; + int freq1 = chandef->center_freq1, freq2 = chandef->center_freq2; + static const u8 ch_band[] = { + [NL80211_BAND_2GHZ] = 1, + [NL80211_BAND_5GHZ] = 2, + [NL80211_BAND_6GHZ] = 3, + }; + static const u8 ch_width[] = { + [NL80211_CHAN_WIDTH_20_NOHT] = 0, + [NL80211_CHAN_WIDTH_20] = 0, + [NL80211_CHAN_WIDTH_40] = 0, + [NL80211_CHAN_WIDTH_80] = 1, + [NL80211_CHAN_WIDTH_160] = 2, + [NL80211_CHAN_WIDTH_80P80] = 3, + [NL80211_CHAN_WIDTH_5] = 4, + [NL80211_CHAN_WIDTH_10] = 5, + [NL80211_CHAN_WIDTH_320] = 6, + }; + struct { + struct { + u8 band_idx; + u8 pad[3]; + } __packed hdr; + struct config_tlv { + __le16 tag; + __le16 len; + u16 aid; + u8 ch_band; + u8 bw; + u8 control_ch; + u8 sco; + u8 center_ch; + u8 center_ch2; + u8 drop_err; + u8 pad[3]; + } __packed tlv; + } __packed req = { + .hdr = { + .band_idx = vif->bss_conf.mt76.band_idx, + }, + .tlv = { + .tag = cpu_to_le16(1), + .len = cpu_to_le16(sizeof(req.tlv)), + .control_ch = chandef->chan->hw_value, + .center_ch = ieee80211_frequency_to_channel(freq1), + .drop_err = 1, + }, + }; + if (chandef->chan->band < ARRAY_SIZE(ch_band)) + req.tlv.ch_band = ch_band[chandef->chan->band]; + if (chandef->width < ARRAY_SIZE(ch_width)) + req.tlv.bw = ch_width[chandef->width]; + + if (freq2) + req.tlv.center_ch2 = ieee80211_frequency_to_channel(freq2); + + if (req.tlv.control_ch < req.tlv.center_ch) + req.tlv.sco = 1; /* SCA */ + else if (req.tlv.control_ch > req.tlv.center_ch) + req.tlv.sco = 3; /* SCB */ + + return mt76_mcu_send_msg(vif->phy->mt76->dev, MCU_UNI_CMD(SNIFFER), + &req, sizeof(req), true); +} + +int +mt7921_mcu_uni_add_beacon_offload(struct mt792x_dev *dev, + struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + bool enable) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + struct mt76_wcid *wcid = &dev->mt76.global_wcid; + struct ieee80211_mutable_offsets offs; + struct { + struct req_hdr { + u8 bss_idx; + u8 pad[3]; + } __packed hdr; + struct bcn_content_tlv { + __le16 tag; + __le16 len; + __le16 tim_ie_pos; + __le16 csa_ie_pos; + __le16 bcc_ie_pos; + /* 0: disable beacon offload + * 1: enable beacon offload + * 2: update probe respond offload + */ + u8 enable; + /* 0: legacy format (TXD + payload) + * 1: only cap field IE + */ + u8 type; + __le16 pkt_len; + u8 pkt[512]; + } __packed beacon_tlv; + } req = { + .hdr = { + .bss_idx = mvif->bss_conf.mt76.idx, + }, + .beacon_tlv = { + .tag = cpu_to_le16(UNI_BSS_INFO_BCN_CONTENT), + .len = cpu_to_le16(sizeof(struct bcn_content_tlv)), + .enable = enable, + }, + }; + struct sk_buff *skb; + + /* support enable/update process only + * disable flow would be handled in bss stop handler automatically + */ + if (!enable) + return -EOPNOTSUPP; + + skb = ieee80211_beacon_get_template(mt76_hw(dev), vif, &offs, 0); + if (!skb) + return -EINVAL; + + if (skb->len > 512 - MT_TXD_SIZE) { + dev_err(dev->mt76.dev, "beacon size limit exceed\n"); + dev_kfree_skb(skb); + return -EINVAL; + } + + mt76_connac2_mac_write_txwi(&dev->mt76, (__le32 *)(req.beacon_tlv.pkt), + skb, wcid, NULL, 0, 0, BSS_CHANGED_BEACON); + memcpy(req.beacon_tlv.pkt + MT_TXD_SIZE, skb->data, skb->len); + req.beacon_tlv.pkt_len = cpu_to_le16(MT_TXD_SIZE + skb->len); + req.beacon_tlv.tim_ie_pos = cpu_to_le16(MT_TXD_SIZE + offs.tim_offset); + + if (offs.cntdwn_counter_offs[0]) { + u16 csa_offs; + + csa_offs = MT_TXD_SIZE + offs.cntdwn_counter_offs[0] - 4; + req.beacon_tlv.csa_ie_pos = cpu_to_le16(csa_offs); + } + dev_kfree_skb(skb); + + return mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(BSS_INFO_UPDATE), + &req, sizeof(req), true); +} + +static +int __mt7921_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2, + enum environment_cap env_cap, + struct mt7921_clc *clc, + u8 idx) +{ +#define CLC_CAP_EVT_EN BIT(0) +#define CLC_CAP_DTS_EN BIT(1) + struct sk_buff *skb, *ret_skb = NULL; + struct { + u8 ver; + u8 pad0; + __le16 len; + u8 idx; + u8 env; + u8 acpi_conf; + u8 cap; + u8 alpha2[2]; + u8 type[2]; + u8 env_6g; + u8 mtcl_conf; + u8 rsvd[62]; + } __packed req = { + .ver = 1, + .idx = idx, + .env = env_cap, + .env_6g = dev->phy.power_type, + .acpi_conf = mt792x_acpi_get_flags(&dev->phy), + .mtcl_conf = mt792x_acpi_get_mtcl_conf(&dev->phy, alpha2), + }; + int ret, valid_cnt = 0; + u32 buf_len = 0; + u8 *pos; + + if (!clc) + return 0; + + if (dev->phy.chip_cap & MT792x_CHIP_CAP_CLC_EVT_EN) + req.cap |= CLC_CAP_EVT_EN; + if (mt76_find_power_limits_node(&dev->mt76)) + req.cap |= CLC_CAP_DTS_EN; + + buf_len = le32_to_cpu(clc->len) - sizeof(*clc); + pos = clc->data; + while (buf_len > 16) { + struct mt7921_clc_rule *rule = (struct mt7921_clc_rule *)pos; + u16 len = le16_to_cpu(rule->len); + u16 offset = len + sizeof(*rule); + + if (buf_len < offset) + break; + + pos += offset; + buf_len -= offset; + if (rule->alpha2[0] != alpha2[0] || + rule->alpha2[1] != alpha2[1]) + continue; + + memcpy(req.alpha2, rule->alpha2, 2); + memcpy(req.type, rule->type, 2); + + req.len = cpu_to_le16(sizeof(req) + len); + skb = __mt76_mcu_msg_alloc(&dev->mt76, &req, + le16_to_cpu(req.len), + sizeof(req), GFP_KERNEL); + if (!skb) + return -ENOMEM; + skb_put_data(skb, rule->data, len); + + ret = mt76_mcu_skb_send_and_get_msg(&dev->mt76, skb, + MCU_CE_CMD(SET_CLC), + !!(req.cap & CLC_CAP_EVT_EN), + &ret_skb); + if (ret < 0) + return ret; + + if (ret_skb) { + struct mt7921_clc_info_tlv *info; + + info = (struct mt7921_clc_info_tlv *)(ret_skb->data + 4); + dev->phy.clc_chan_conf = info->chan_conf; + dev_kfree_skb(ret_skb); + } + + valid_cnt++; + } + + if (!valid_cnt) + return -ENOENT; + + return 0; +} + +int mt7921_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2, + enum environment_cap env_cap) +{ + struct mt792x_phy *phy = (struct mt792x_phy *)&dev->phy; + int i, ret; + + /* submit all clc config */ + for (i = 0; i < ARRAY_SIZE(phy->clc); i++) { + if (i == MT792x_CLC_REGD) + continue; + + ret = __mt7921_mcu_set_clc(dev, alpha2, env_cap, + phy->clc[i], i); + + /* If no country found, set "00" as default */ + if (ret == -ENOENT) + ret = __mt7921_mcu_set_clc(dev, "00", + ENVIRON_INDOOR, + phy->clc[i], i); + if (ret < 0) + return ret; + } + return 0; +} + +int mt7921_mcu_get_temperature(struct mt792x_phy *phy) +{ + struct mt792x_dev *dev = phy->dev; + struct { + u8 ctrl_id; + u8 action; + u8 band_idx; + u8 rsv[5]; + } req = { + .ctrl_id = THERMAL_SENSOR_TEMP_QUERY, + .band_idx = phy->mt76->band_idx, + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(THERMAL_CTRL), &req, + sizeof(req), true); +} + +int mt7921_mcu_wf_rf_pin_ctrl(struct mt792x_phy *phy, u8 action) +{ + struct mt792x_dev *dev = phy->dev; + struct { + u8 action; + u8 value; + } req = { + .action = action, + .value = 0, + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(WF_RF_PIN_CTRL), &req, + sizeof(req), action ? true : false); +} + +int mt7921_mcu_set_rxfilter(struct mt792x_dev *dev, u32 fif, + u8 bit_op, u32 bit_map) +{ + struct { + u8 rsv[4]; + u8 mode; + u8 rsv2[3]; + __le32 fif; + __le32 bit_map; /* bit_* for bitmap update */ + u8 bit_op; + u8 pad[51]; + } __packed data = { + .mode = fif ? 1 : 2, + .fif = cpu_to_le32(fif), + .bit_map = cpu_to_le32(bit_map), + .bit_op = bit_op, + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_CE_CMD(SET_RX_FILTER), + &data, sizeof(data), false); +} + +int mt7921_mcu_set_rssimonitor(struct mt792x_dev *dev, struct ieee80211_vif *vif) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + struct { + u8 enable; + s8 cqm_rssi_high; + s8 cqm_rssi_low; + u8 bss_idx; + u16 duration; + u8 rsv2[2]; + } __packed data = { + .enable = vif->cfg.assoc, + .cqm_rssi_high = vif->bss_conf.cqm_rssi_thold + vif->bss_conf.cqm_rssi_hyst, + .cqm_rssi_low = vif->bss_conf.cqm_rssi_thold - vif->bss_conf.cqm_rssi_hyst, + .bss_idx = mvif->bss_conf.mt76.idx, + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_CE_CMD(RSSI_MONITOR), + &data, sizeof(data), false); +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.h new file mode 100644 index 000000000..de676b83b --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.h @@ -0,0 +1,120 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* Copyright (C) 2020 MediaTek Inc. */ + +#ifndef __MT7921_MCU_H +#define __MT7921_MCU_H + +#include "../mt76_connac_mcu.h" + +struct mt7921_mcu_tx_done_event { + u8 pid; + u8 status; + __le16 seq; + + u8 wlan_idx; + u8 tx_cnt; + __le16 tx_rate; + + u8 flag; + u8 tid; + u8 rsp_rate; + u8 mcs; + + u8 bw; + u8 tx_pwr; + u8 reason; + u8 rsv0[1]; + + __le32 delay; + __le32 timestamp; + __le32 applied_flag; + u8 txs[28]; + + u8 rsv1[32]; +} __packed; + +/* ext event table */ +enum { + MCU_EXT_EVENT_RATE_REPORT = 0x87, +}; + +struct mt7921_mcu_eeprom_info { + __le32 addr; + __le32 valid; + u8 data[MT7921_EEPROM_BLOCK_SIZE]; +} __packed; + +#define MT_RA_RATE_NSS GENMASK(8, 6) +#define MT_RA_RATE_MCS GENMASK(3, 0) +#define MT_RA_RATE_TX_MODE GENMASK(12, 9) +#define MT_RA_RATE_DCM_EN BIT(4) +#define MT_RA_RATE_BW GENMASK(14, 13) + +enum { + MT_EBF = BIT(0), /* explicit beamforming */ + MT_IBF = BIT(1) /* implicit beamforming */ +}; + +struct mt7921_mcu_ant_id_config { + u8 ant_id[4]; +} __packed; + +struct mt7921_txpwr_req { + u8 ver; + u8 action; + __le16 len; + u8 dbdc_idx; + u8 rsv[3]; +} __packed; + +struct mt7921_txpwr_event { + u8 ver; + u8 action; + __le16 len; + struct mt7921_txpwr txpwr; +} __packed; + +struct mt7921_wf_rf_pin_ctrl_event { + u8 result; + u8 value; +} __packed; + +enum { + TM_SWITCH_MODE, + TM_SET_AT_CMD, + TM_QUERY_AT_CMD, +}; + +enum { + MT7921_TM_NORMAL, + MT7921_TM_TESTMODE, + MT7921_TM_ICAP, + MT7921_TM_ICAP_OVERLAP, + MT7921_TM_WIFISPECTRUM, +}; + +struct mt7921_rftest_cmd { + u8 action; + u8 rsv[3]; + __le32 param0; + __le32 param1; +} __packed; + +struct mt7921_rftest_evt { + __le32 param0; + __le32 param1; +} __packed; + +struct mt7921_clc_info_tlv { + __le16 tag; + __le16 len; + + u8 chan_conf; /* BIT(0) : Enable UNII-4 + * BIT(1) : Enable UNII-5 + * BIT(2) : Enable UNII-6 + * BIT(3) : Enable UNII-7 + * BIT(4) : Enable UNII-8 + */ + u8 rsv[63]; +} __packed; +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h b/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h new file mode 100644 index 000000000..fb8654b7c --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h @@ -0,0 +1,353 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* Copyright (C) 2020 MediaTek Inc. */ + +#ifndef __MT7921_H +#define __MT7921_H + +#include "../mt792x.h" +#include "regs.h" + +#define MT7921_MAX_AID 20 + +#define MT7921_TX_RING_SIZE 2048 +#define MT7921_TX_MCU_RING_SIZE 256 +#define MT7921_TX_FWDL_RING_SIZE 128 + +#define MT7921_RX_RING_SIZE 1536 +#define MT7921_RX_MCU_RING_SIZE 8 +#define MT7921_RX_MCU_WA_RING_SIZE 512 + +/* MT7902 Rx Ring0 is for both Rx Event and Tx Done Event */ +#define MT7902_RX_MCU_RING_SIZE 512 + +#define MT7921_EEPROM_SIZE 3584 +#define MT7921_TOKEN_SIZE 8192 + +#define MT7921_EEPROM_BLOCK_SIZE 16 + +#define MT7921_SKU_RATE_NUM 161 +#define MT7921_SKU_MAX_DELTA_IDX MT7921_SKU_RATE_NUM +#define MT7921_SKU_TABLE_SIZE (MT7921_SKU_RATE_NUM + 1) + +#define MCU_UNI_EVENT_ROC 0x27 +#define MCU_UNI_EVENT_CLC 0x80 + +#define EXT_CMD_RADIO_LED_CTRL_ENABLE 0x1 +#define EXT_CMD_RADIO_ON_LED 0x2 +#define EXT_CMD_RADIO_OFF_LED 0x3 + +#define WF_RF_PIN_INIT 0x0 +#define WF_RF_PIN_POLL 0x1 + +enum { + UNI_ROC_ACQUIRE, + UNI_ROC_ABORT, + UNI_ROC_NUM +}; + +enum mt7921_roc_req { + MT7921_ROC_REQ_JOIN, + MT7921_ROC_REQ_ROC, + MT7921_ROC_REQ_NUM +}; + +enum { + UNI_EVENT_ROC_GRANT = 0, + UNI_EVENT_ROC_TAG_NUM +}; + +struct mt7921_realease_info { + __le16 len; + u8 pad_len; + u8 tag; +} __packed; + +struct mt7921_fw_features { + u8 segment; + u8 data; + u8 rsv[14]; +} __packed; + +struct mt7921_roc_grant_tlv { + __le16 tag; + __le16 len; + u8 bss_idx; + u8 tokenid; + u8 status; + u8 primarychannel; + u8 rfsco; + u8 rfband; + u8 channelwidth; + u8 centerfreqseg1; + u8 centerfreqseg2; + u8 reqtype; + u8 dbdcband; + u8 rsv[1]; + __le32 max_interval; +} __packed; + +enum mt7921_sdio_pkt_type { + MT7921_SDIO_TXD, + MT7921_SDIO_DATA, + MT7921_SDIO_CMD, + MT7921_SDIO_FWDL, +}; + +struct mt7921_sdio_intr { + u32 isr; + struct { + u32 wtqcr[16]; + } tx; + struct { + u16 num[2]; + u16 len0[16]; + u16 len1[128]; + } rx; + u32 rec_mb[2]; +} __packed; + +#define to_rssi(field, rxv) ((FIELD_GET(field, rxv) - 220) / 2) +#define to_rcpi(rssi) (2 * (rssi) + 220) + +enum mt7921_txq_id { + MT7921_TXQ_BAND0, + MT7921_TXQ_BAND1, + MT7921_TXQ_FWDL = 16, + MT7921_TXQ_MCU_WM, +}; + +enum mt7921_rxq_id { + MT7921_RXQ_BAND0 = 0, + MT7921_RXQ_BAND1, + MT7921_RXQ_MCU_WM = 0, +}; + +/* MT7902 assigns its MCU-WM TXQ at index 15 */ +enum mt7902_txq_id { + MT7902_TXQ_MCU_WM = 15, +}; + +struct mt7921_dma_layout { + u8 mcu_wm_txq; + u16 mcu_rxdone_ring_size; + bool has_mcu_wa; +}; + +enum { + MT7921_CLC_POWER, + MT7921_CLC_CHAN, + MT7921_CLC_MAX_NUM, +}; + +struct mt7921_clc_rule { + u8 alpha2[2]; + u8 type[2]; + __le16 len; + u8 data[]; +} __packed; + +struct mt7921_clc { + __le32 len; + u8 idx; + u8 ver; + u8 nr_country; + u8 type; + u8 rsv[8]; + u8 data[]; +} __packed; + +enum mt7921_eeprom_field { + MT_EE_CHIP_ID = 0x000, + MT_EE_VERSION = 0x002, + MT_EE_MAC_ADDR = 0x004, + MT_EE_WIFI_CONF = 0x07c, + MT_EE_HW_TYPE = 0x55b, + __MT_EE_MAX = 0x9ff +}; + +#define MT_EE_HW_TYPE_ENCAP BIT(0) + +enum { + TXPWR_USER, + TXPWR_EEPROM, + TXPWR_MAC, + TXPWR_MAX_NUM, +}; + +struct mt7921_txpwr { + u8 ch; + u8 rsv[3]; + struct { + u8 ch; + u8 cck[4]; + u8 ofdm[8]; + u8 ht20[8]; + u8 ht40[9]; + u8 vht20[12]; + u8 vht40[12]; + u8 vht80[12]; + u8 vht160[12]; + u8 he26[12]; + u8 he52[12]; + u8 he106[12]; + u8 he242[12]; + u8 he484[12]; + u8 he996[12]; + u8 he996x2[12]; + } data[TXPWR_MAX_NUM]; +}; + +extern const struct ieee80211_ops mt7921_ops; + +u32 mt7921_reg_map(struct mt792x_dev *dev, u32 addr); + +int __mt7921_start(struct mt792x_phy *phy); +int mt7921_register_device(struct mt792x_dev *dev); +void mt7921_unregister_device(struct mt792x_dev *dev); +int mt7921_run_firmware(struct mt792x_dev *dev); +int mt7921_set_channel(struct mt76_phy *mphy); +int mt7921_mcu_set_bss_pm(struct mt792x_dev *dev, struct ieee80211_vif *vif, + bool enable); +int mt7921_mcu_sta_update(struct mt792x_dev *dev, struct ieee80211_sta *sta, + struct ieee80211_vif *vif, bool enable, + enum mt76_sta_info_state state); +int mt7921_mcu_set_chan_info(struct mt792x_phy *phy, int cmd); +int mt7921_mcu_set_tx(struct mt792x_dev *dev, struct ieee80211_vif *vif); +int mt7921_mcu_set_eeprom(struct mt792x_dev *dev); +int mt7921_mcu_get_rx_rate(struct mt792x_phy *phy, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, struct rate_info *rate); +int mt7921_mcu_fw_log_2_host(struct mt792x_dev *dev, u8 ctrl); +void mt7921_mcu_rx_event(struct mt792x_dev *dev, struct sk_buff *skb); +int mt7921_mcu_set_rxfilter(struct mt792x_dev *dev, u32 fif, + u8 bit_op, u32 bit_map); +int mt7921_mcu_radio_led_ctrl(struct mt792x_dev *dev, u8 value); +int mt7921_mcu_wf_rf_pin_ctrl(struct mt792x_phy *phy, u8 action); + +static inline u32 +mt7921_reg_map_l1(struct mt792x_dev *dev, u32 addr) +{ + u32 offset = FIELD_GET(MT_HIF_REMAP_L1_OFFSET, addr); + u32 base = FIELD_GET(MT_HIF_REMAP_L1_BASE, addr); + + mt76_rmw_field(dev, MT_HIF_REMAP_L1, MT_HIF_REMAP_L1_MASK, base); + /* use read to push write */ + mt76_rr(dev, MT_HIF_REMAP_L1); + + return MT_HIF_REMAP_BASE_L1 + offset; +} + +static inline u32 +mt7921_l1_rr(struct mt792x_dev *dev, u32 addr) +{ + return mt76_rr(dev, mt7921_reg_map_l1(dev, addr)); +} + +static inline void +mt7921_l1_wr(struct mt792x_dev *dev, u32 addr, u32 val) +{ + mt76_wr(dev, mt7921_reg_map_l1(dev, addr), val); +} + +static inline u32 +mt7921_l1_rmw(struct mt792x_dev *dev, u32 addr, u32 mask, u32 val) +{ + val |= mt7921_l1_rr(dev, addr) & ~mask; + mt7921_l1_wr(dev, addr, val); + + return val; +} + +#define mt7921_l1_set(dev, addr, val) mt7921_l1_rmw(dev, addr, 0, val) +#define mt7921_l1_clear(dev, addr, val) mt7921_l1_rmw(dev, addr, val, 0) + +int mt7921_mac_init(struct mt792x_dev *dev); +bool mt7921_mac_wtbl_update(struct mt792x_dev *dev, int idx, u32 mask); +int mt7921_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); +int mt7921_mac_sta_event(struct mt76_dev *mdev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, enum mt76_sta_event ev); +void mt7921_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); +void mt7921_mac_reset_work(struct work_struct *work); +int mt7921e_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, + enum mt76_txq_id qid, struct mt76_wcid *wcid, + struct ieee80211_sta *sta, + struct mt76_tx_info *tx_info); + +bool mt7921_rx_check(struct mt76_dev *mdev, void *data, int len); +void mt7921_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, + struct sk_buff *skb, u32 *info); +void mt7921_stats_work(struct work_struct *work); +void mt7921_set_stream_he_caps(struct mt792x_phy *phy); +int mt7921_init_debugfs(struct mt792x_dev *dev); + +int mt7921_mcu_set_beacon_filter(struct mt792x_dev *dev, + struct ieee80211_vif *vif, + bool enable); +int mt7921_mcu_uni_tx_ba(struct mt792x_dev *dev, + struct ieee80211_ampdu_params *params, + bool enable); +int mt7921_mcu_uni_rx_ba(struct mt792x_dev *dev, + struct ieee80211_ampdu_params *params, + bool enable); +void mt7921_scan_work(struct work_struct *work); +void mt7921_roc_work(struct work_struct *work); +void mt7921_csa_work(struct work_struct *work); +int mt7921_mcu_uni_bss_ps(struct mt792x_dev *dev, struct ieee80211_vif *vif); +void mt7921_coredump_work(struct work_struct *work); +int mt7921_get_txpwr_info(struct mt792x_dev *dev, struct mt7921_txpwr *txpwr); +int mt7921_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + void *data, int len); +int mt7921_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg, + struct netlink_callback *cb, void *data, int len); +int mt7921_mcu_parse_response(struct mt76_dev *mdev, int cmd, + struct sk_buff *skb, int seq); + +int mt7921e_driver_own(struct mt792x_dev *dev); +int mt7921e_mac_reset(struct mt792x_dev *dev); +int mt7921e_mcu_init(struct mt792x_dev *dev); +int mt7921s_wfsys_reset(struct mt792x_dev *dev); +int mt7921s_mac_reset(struct mt792x_dev *dev); +int mt7921s_init_reset(struct mt792x_dev *dev); + +int mt7921s_mcu_init(struct mt792x_dev *dev); +int mt7921s_mcu_drv_pmctrl(struct mt792x_dev *dev); +int mt7921s_mcu_fw_pmctrl(struct mt792x_dev *dev); +void mt7921_mac_add_txs(struct mt792x_dev *dev, void *data); +void mt7921_set_runtime_pm(struct mt792x_dev *dev); +void mt7921_mcu_set_suspend_iter(void *priv, u8 *mac, + struct ieee80211_vif *vif); +void mt7921_set_ipv6_ns_work(struct work_struct *work); + +int mt7921_mcu_set_sniffer(struct mt792x_dev *dev, struct ieee80211_vif *vif, + bool enable); +int mt7921_mcu_config_sniffer(struct mt792x_vif *vif, + struct ieee80211_chanctx_conf *ctx); +int mt7921_mcu_get_temperature(struct mt792x_phy *phy); + +int mt7921_usb_sdio_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, + enum mt76_txq_id qid, struct mt76_wcid *wcid, + struct ieee80211_sta *sta, + struct mt76_tx_info *tx_info); +void mt7921_usb_sdio_tx_complete_skb(struct mt76_dev *mdev, + struct mt76_queue_entry *e); +bool mt7921_usb_sdio_tx_status_data(struct mt76_dev *mdev, u8 *update); + +/* usb */ +int mt7921_mcu_uni_add_beacon_offload(struct mt792x_dev *dev, + struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + bool enable); +int mt7921_set_tx_sar_pwr(struct ieee80211_hw *hw, + const struct cfg80211_sar_specs *sar); + +int mt7921_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2, + enum environment_cap env_cap); +int mt7921_mcu_set_roc(struct mt792x_phy *phy, struct mt792x_vif *vif, + struct ieee80211_channel *chan, int duration, + enum mt7921_roc_req type, u8 token_id); +int mt7921_mcu_abort_roc(struct mt792x_phy *phy, struct mt792x_vif *vif, + u8 token_id); +void mt7921_roc_abort_sync(struct mt792x_dev *dev); +int mt7921_mcu_set_rssimonitor(struct mt792x_dev *dev, struct ieee80211_vif *vif); +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c new file mode 100644 index 000000000..56a914aaa --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c @@ -0,0 +1,743 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2020 MediaTek Inc. + * + */ + +#include +#include +#include +#include + +#include "mt7921.h" +#include "../mt76_connac2_mac.h" +#include "../dma.h" +#include "mcu.h" +#include "regd.h" + +static const struct pci_device_id mt7921_pci_device_table[] = { + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7961), + .driver_data = (kernel_ulong_t)MT7921_FIRMWARE_WM }, + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7922), + .driver_data = (kernel_ulong_t)MT7922_FIRMWARE_WM }, + { PCI_DEVICE(PCI_VENDOR_ID_ITTIM, 0x7922), + .driver_data = (kernel_ulong_t)MT7922_FIRMWARE_WM }, + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x0608), + .driver_data = (kernel_ulong_t)MT7921_FIRMWARE_WM }, + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x0616), + .driver_data = (kernel_ulong_t)MT7922_FIRMWARE_WM }, + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7920), + .driver_data = (kernel_ulong_t)MT7920_FIRMWARE_WM }, + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7902), + .driver_data = (kernel_ulong_t)MT7902_FIRMWARE_WM }, + { }, +}; + +static bool mt7921_disable_aspm; +module_param_named(disable_aspm, mt7921_disable_aspm, bool, 0644); +MODULE_PARM_DESC(disable_aspm, "disable PCI ASPM support"); + +static int mt7921e_init_reset(struct mt792x_dev *dev) +{ + return mt792x_wpdma_reset(dev, true); +} + +static void mt7921e_unregister_device(struct mt792x_dev *dev) +{ + int i; + struct mt76_connac_pm *pm = &dev->pm; + struct ieee80211_hw *hw = mt76_hw(dev); + + if (dev->phy.chip_cap & MT792x_CHIP_CAP_WF_RF_PIN_CTRL_EVT_EN) + wiphy_rfkill_stop_polling(hw->wiphy); + + cancel_work_sync(&dev->init_work); + mt76_unregister_device(&dev->mt76); + mt76_for_each_q_rx(&dev->mt76, i) + napi_disable(&dev->mt76.napi[i]); + cancel_delayed_work_sync(&pm->ps_work); + cancel_work_sync(&pm->wake_work); + cancel_work_sync(&dev->reset_work); + + mt76_connac2_tx_token_put(&dev->mt76); + __mt792x_mcu_drv_pmctrl(dev); + mt792x_dma_cleanup(dev); + mt792x_wfsys_reset(dev); + skb_queue_purge(&dev->mt76.mcu.res_q); + + tasklet_disable(&dev->mt76.irq_tasklet); +} + +static u32 __mt7921_reg_addr(struct mt792x_dev *dev, u32 addr) +{ + static const struct mt76_connac_reg_map fixed_map[] = { + { 0x820d0000, 0x30000, 0x10000 }, /* WF_LMAC_TOP (WF_WTBLON) */ + { 0x820ed000, 0x24800, 0x00800 }, /* WF_LMAC_TOP BN0 (WF_MIB) */ + { 0x820e4000, 0x21000, 0x00400 }, /* WF_LMAC_TOP BN0 (WF_TMAC) */ + { 0x820e7000, 0x21e00, 0x00200 }, /* WF_LMAC_TOP BN0 (WF_DMA) */ + { 0x820eb000, 0x24200, 0x00400 }, /* WF_LMAC_TOP BN0 (WF_LPON) */ + { 0x820e2000, 0x20800, 0x00400 }, /* WF_LMAC_TOP BN0 (WF_AGG) */ + { 0x820e3000, 0x20c00, 0x00400 }, /* WF_LMAC_TOP BN0 (WF_ARB) */ + { 0x820e5000, 0x21400, 0x00800 }, /* WF_LMAC_TOP BN0 (WF_RMAC) */ + { 0x00400000, 0x80000, 0x10000 }, /* WF_MCU_SYSRAM */ + { 0x00410000, 0x90000, 0x10000 }, /* WF_MCU_SYSRAM (configure register) */ + { 0x40000000, 0x70000, 0x10000 }, /* WF_UMAC_SYSRAM */ + { 0x54000000, 0x02000, 0x01000 }, /* WFDMA PCIE0 MCU DMA0 */ + { 0x55000000, 0x03000, 0x01000 }, /* WFDMA PCIE0 MCU DMA1 */ + { 0x58000000, 0x06000, 0x01000 }, /* WFDMA PCIE1 MCU DMA0 (MEM_DMA) */ + { 0x59000000, 0x07000, 0x01000 }, /* WFDMA PCIE1 MCU DMA1 */ + { 0x7c000000, 0xf0000, 0x10000 }, /* CONN_INFRA */ + { 0x7c020000, 0xd0000, 0x10000 }, /* CONN_INFRA, WFDMA */ + { 0x7c060000, 0xe0000, 0x10000 }, /* CONN_INFRA, conn_host_csr_top */ + { 0x80020000, 0xb0000, 0x10000 }, /* WF_TOP_MISC_OFF */ + { 0x81020000, 0xc0000, 0x10000 }, /* WF_TOP_MISC_ON */ + { 0x820c0000, 0x08000, 0x04000 }, /* WF_UMAC_TOP (PLE) */ + { 0x820c8000, 0x0c000, 0x02000 }, /* WF_UMAC_TOP (PSE) */ + { 0x820cc000, 0x0e000, 0x01000 }, /* WF_UMAC_TOP (PP) */ + { 0x820cd000, 0x0f000, 0x01000 }, /* WF_MDP_TOP */ + { 0x74030000, 0x10000, 0x10000 }, /* PCIE_MAC_IREG */ + { 0x820ce000, 0x21c00, 0x00200 }, /* WF_LMAC_TOP (WF_SEC) */ + { 0x820cf000, 0x22000, 0x01000 }, /* WF_LMAC_TOP (WF_PF) */ + { 0x820e0000, 0x20000, 0x00400 }, /* WF_LMAC_TOP BN0 (WF_CFG) */ + { 0x820e1000, 0x20400, 0x00200 }, /* WF_LMAC_TOP BN0 (WF_TRB) */ + { 0x820e9000, 0x23400, 0x00200 }, /* WF_LMAC_TOP BN0 (WF_WTBLOFF) */ + { 0x820ea000, 0x24000, 0x00200 }, /* WF_LMAC_TOP BN0 (WF_ETBF) */ + { 0x820ec000, 0x24600, 0x00200 }, /* WF_LMAC_TOP BN0 (WF_INT) */ + { 0x820f0000, 0xa0000, 0x00400 }, /* WF_LMAC_TOP BN1 (WF_CFG) */ + { 0x820f1000, 0xa0600, 0x00200 }, /* WF_LMAC_TOP BN1 (WF_TRB) */ + { 0x820f2000, 0xa0800, 0x00400 }, /* WF_LMAC_TOP BN1 (WF_AGG) */ + { 0x820f3000, 0xa0c00, 0x00400 }, /* WF_LMAC_TOP BN1 (WF_ARB) */ + { 0x820f4000, 0xa1000, 0x00400 }, /* WF_LMAC_TOP BN1 (WF_TMAC) */ + { 0x820f5000, 0xa1400, 0x00800 }, /* WF_LMAC_TOP BN1 (WF_RMAC) */ + { 0x820f7000, 0xa1e00, 0x00200 }, /* WF_LMAC_TOP BN1 (WF_DMA) */ + { 0x820f9000, 0xa3400, 0x00200 }, /* WF_LMAC_TOP BN1 (WF_WTBLOFF) */ + { 0x820fa000, 0xa4000, 0x00200 }, /* WF_LMAC_TOP BN1 (WF_ETBF) */ + { 0x820fb000, 0xa4200, 0x00400 }, /* WF_LMAC_TOP BN1 (WF_LPON) */ + { 0x820fc000, 0xa4600, 0x00200 }, /* WF_LMAC_TOP BN1 (WF_INT) */ + { 0x820fd000, 0xa4800, 0x00800 }, /* WF_LMAC_TOP BN1 (WF_MIB) */ + }; + int i; + + if (addr < 0x100000) + return addr; + + for (i = 0; i < ARRAY_SIZE(fixed_map); i++) { + u32 ofs; + + if (addr < fixed_map[i].phys) + continue; + + ofs = addr - fixed_map[i].phys; + if (ofs > fixed_map[i].size) + continue; + + return fixed_map[i].maps + ofs; + } + + if ((addr >= 0x18000000 && addr < 0x18c00000) || + (addr >= 0x70000000 && addr < 0x78000000) || + (addr >= 0x7c000000 && addr < 0x7c400000)) + return mt7921_reg_map_l1(dev, addr); + + dev_err(dev->mt76.dev, "Access currently unsupported address %08x\n", + addr); + + return 0; +} + +static u32 mt7921_rr(struct mt76_dev *mdev, u32 offset) +{ + struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); + u32 addr = __mt7921_reg_addr(dev, offset); + + return dev->bus_ops->rr(mdev, addr); +} + +static void mt7921_wr(struct mt76_dev *mdev, u32 offset, u32 val) +{ + struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); + u32 addr = __mt7921_reg_addr(dev, offset); + + dev->bus_ops->wr(mdev, addr, val); +} + +static u32 mt7921_rmw(struct mt76_dev *mdev, u32 offset, u32 mask, u32 val) +{ + struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); + u32 addr = __mt7921_reg_addr(dev, offset); + + return dev->bus_ops->rmw(mdev, addr, mask, val); +} + +static int mt7921_dma_init(struct mt792x_dev *dev) +{ + struct mt7921_dma_layout layout = { + /* General case: MT7921 / MT7922 /MT7920 */ + .mcu_wm_txq = MT7921_TXQ_MCU_WM, + .mcu_rxdone_ring_size = MT7921_RX_MCU_RING_SIZE, + .has_mcu_wa = true, + }; + bool is_mt7902; + int ret; + + is_mt7902 = mt7921_l1_rr(dev, MT_HW_CHIPID) == 0x7902; + + /* + * MT7902 special case: + * - MCU-WM TXQ uses index 15 + * - RX Ring0 is larger and shared for event/TX-done + * - MT7902 does not use the MCU_WA ring + */ + if (is_mt7902) { + layout.mcu_wm_txq = MT7902_TXQ_MCU_WM; + layout.mcu_rxdone_ring_size = MT7902_RX_MCU_RING_SIZE; + layout.has_mcu_wa = false; + } + + mt76_dma_attach(&dev->mt76); + + ret = mt792x_dma_disable(dev, true); + if (ret) + return ret; + + /* init tx queue */ + ret = mt76_connac_init_tx_queues(dev->phy.mt76, MT7921_TXQ_BAND0, + MT7921_TX_RING_SIZE, + MT_TX_RING_BASE, NULL, 0); + if (ret) + return ret; + + mt76_wr(dev, MT_WFDMA0_TX_RING0_EXT_CTRL, 0x4); + + /* command to WM */ + ret = mt76_init_mcu_queue(&dev->mt76, MT_MCUQ_WM, layout.mcu_wm_txq, + MT7921_TX_MCU_RING_SIZE, MT_TX_RING_BASE); + if (ret) + return ret; + + /* firmware download */ + ret = mt76_init_mcu_queue(&dev->mt76, MT_MCUQ_FWDL, MT7921_TXQ_FWDL, + MT7921_TX_FWDL_RING_SIZE, MT_TX_RING_BASE); + if (ret) + return ret; + + /* event from WM before firmware download */ + ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MCU], + MT7921_RXQ_MCU_WM, + layout.mcu_rxdone_ring_size, + MT_RX_BUF_SIZE, MT_RX_EVENT_RING_BASE); + if (ret) + return ret; + + if (layout.has_mcu_wa) { + /* Change mcu queue after firmware download */ + ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MCU_WA], + MT7921_RXQ_MCU_WM, + MT7921_RX_MCU_WA_RING_SIZE, + MT_RX_BUF_SIZE, MT_WFDMA0(0x540)); + if (ret) + return ret; + } + + /* rx data */ + ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MAIN], + MT7921_RXQ_BAND0, MT7921_RX_RING_SIZE, + MT_RX_BUF_SIZE, MT_RX_DATA_RING_BASE); + if (ret) + return ret; + + ret = mt76_init_queues(dev, mt792x_poll_rx); + if (ret < 0) + return ret; + + netif_napi_add_tx(dev->mt76.tx_napi_dev, &dev->mt76.tx_napi, + mt792x_poll_tx); + napi_enable(&dev->mt76.tx_napi); + + return mt792x_dma_enable(dev); +} + +static int mt7921_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + static const struct mt76_driver_ops drv_ops = { + /* txwi_size = txd size + txp size */ + .txwi_size = MT_TXD_SIZE + sizeof(struct mt76_connac_hw_txp), + .drv_flags = MT_DRV_TXWI_NO_FREE | MT_DRV_HW_MGMT_TXQ | + MT_DRV_AMSDU_OFFLOAD, + .survey_flags = SURVEY_INFO_TIME_TX | + SURVEY_INFO_TIME_RX | + SURVEY_INFO_TIME_BSS_RX, + .token_size = MT7921_TOKEN_SIZE, + .tx_prepare_skb = mt7921e_tx_prepare_skb, + .tx_complete_skb = mt76_connac_tx_complete_skb, + .rx_check = mt7921_rx_check, + .rx_skb = mt7921_queue_rx_skb, + .rx_poll_complete = mt792x_rx_poll_complete, + .sta_add = mt7921_mac_sta_add, + .sta_event = mt7921_mac_sta_event, + .sta_remove = mt7921_mac_sta_remove, + .update_survey = mt792x_update_channel, + .set_channel = mt7921_set_channel, + }; + static const struct mt792x_hif_ops mt7921_pcie_ops = { + .init_reset = mt7921e_init_reset, + .reset = mt7921e_mac_reset, + .mcu_init = mt7921e_mcu_init, + .drv_own = mt792xe_mcu_drv_pmctrl, + .fw_own = mt792xe_mcu_fw_pmctrl, + }; + static const struct mt792x_pcie_reg mt7921_pcie_reg = { + .imask = MT_PCIE_MAC_INT_ENABLE, + .pm = MT_PCIE_MAC_PM, + }; + static const struct mt792x_irq_map irq_map = { + .host_irq_enable = MT_WFDMA0_HOST_INT_ENA, + .tx = { + .all_complete_mask = MT_INT_TX_DONE_ALL, + .mcu_complete_mask = MT_INT_TX_DONE_MCU, + }, + .rx = { + .all_complete_mask = MT_INT_RX_DONE_ALL, + .data_complete_mask = MT_INT_RX_DONE_DATA, + .wm_complete_mask = MT_INT_RX_DONE_WM, + .wm2_complete_mask = MT_INT_RX_DONE_WM2, + }, + }; + struct ieee80211_ops *ops; + struct mt76_bus_ops *bus_ops; + struct mt792x_dev *dev; + struct mt76_dev *mdev; + void __iomem *regs; + u16 cmd, chipid; + u8 features; + int ret; + + ret = pcim_enable_device(pdev); + if (ret) + return ret; + + pci_read_config_word(pdev, PCI_COMMAND, &cmd); + if (!(cmd & PCI_COMMAND_MEMORY)) { + cmd |= PCI_COMMAND_MEMORY; + pci_write_config_word(pdev, PCI_COMMAND, cmd); + } + pci_set_master(pdev); + + ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES); + if (ret < 0) + return ret; + + ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) + goto err_free_pci_vec; + + if (mt7921_disable_aspm) + mt76_pci_disable_aspm(pdev); + + ops = mt792x_get_mac80211_ops(&pdev->dev, &mt7921_ops, + (void *)id->driver_data, &features); + if (!ops) { + ret = -ENOMEM; + goto err_free_pci_vec; + } + + mdev = mt76_alloc_device(&pdev->dev, sizeof(*dev), ops, &drv_ops); + if (!mdev) { + ret = -ENOMEM; + goto err_free_pci_vec; + } + + pci_set_drvdata(pdev, mdev); + + dev = container_of(mdev, struct mt792x_dev, mt76); + + regs = pcim_iomap_region(pdev, 0, pci_name(pdev)); + if (IS_ERR(regs)) { + ret = PTR_ERR(regs); + goto err_free_dev; + } + + dev->fw_features = features; + dev->hif_ops = &mt7921_pcie_ops; + dev->pcie_reg = &mt7921_pcie_reg; + dev->irq_map = &irq_map; + mt76_mmio_init(&dev->mt76, regs); + + if (id->device == 0x7902) { + struct mt792x_irq_map *map; + + /* MT7902 needs a mutable copy because wm2_complete_mask differs */ + map = devm_kmemdup(&pdev->dev, &irq_map, + sizeof(irq_map), GFP_KERNEL); + if (!map) { + ret = -ENOMEM; + goto err_free_dev; + } + + map->rx.wm2_complete_mask = 0; + dev->irq_map = map; + } + + tasklet_init(&mdev->irq_tasklet, mt792x_irq_tasklet, (unsigned long)dev); + + dev->phy.dev = dev; + dev->phy.mt76 = &dev->mt76.phy; + dev->mt76.phy.priv = &dev->phy; + dev->bus_ops = dev->mt76.bus; + bus_ops = devm_kmemdup(dev->mt76.dev, dev->bus_ops, sizeof(*bus_ops), + GFP_KERNEL); + if (!bus_ops) { + ret = -ENOMEM; + goto err_free_dev; + } + + bus_ops->rr = mt7921_rr; + bus_ops->wr = mt7921_wr; + bus_ops->rmw = mt7921_rmw; + dev->mt76.bus = bus_ops; + + if (!mt7921_disable_aspm && mt76_pci_aspm_supported(pdev)) + dev->aspm_supported = true; + + ret = mt792xe_mcu_fw_pmctrl(dev); + if (ret) + goto err_free_dev; + + ret = __mt792xe_mcu_drv_pmctrl(dev); + if (ret) + goto err_free_dev; + + chipid = mt7921_l1_rr(dev, MT_HW_CHIPID); + if (chipid == 0x7961 && (mt7921_l1_rr(dev, MT_HW_BOUND) & BIT(7))) + chipid = 0x7920; + mdev->rev = (chipid << 16) | + (mt7921_l1_rr(dev, MT_HW_REV) & 0xff); + dev_info(mdev->dev, "ASIC revision: %04x\n", mdev->rev); + + ret = mt792x_wfsys_reset(dev); + if (ret) + goto err_free_dev; + + mt76_wr(dev, irq_map.host_irq_enable, 0); + + mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0xff); + + ret = devm_request_irq(mdev->dev, pdev->irq, mt792x_irq_handler, + IRQF_SHARED, KBUILD_MODNAME, dev); + if (ret) + goto err_free_dev; + + ret = mt7921_dma_init(dev); + if (ret) + goto err_free_irq; + + ret = mt7921_register_device(dev); + if (ret) + goto err_free_irq; + + if (of_property_read_bool(dev->mt76.dev->of_node, "wakeup-source")) + device_init_wakeup(dev->mt76.dev, true); + + return 0; + +err_free_irq: + devm_free_irq(&pdev->dev, pdev->irq, dev); +err_free_dev: + mt76_free_device(&dev->mt76); +err_free_pci_vec: + pci_free_irq_vectors(pdev); + + return ret; +} + +static void mt7921_pci_remove(struct pci_dev *pdev) +{ + struct mt76_dev *mdev = pci_get_drvdata(pdev); + struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); + + if (of_property_read_bool(dev->mt76.dev->of_node, "wakeup-source")) + device_init_wakeup(dev->mt76.dev, false); + + mt7921e_unregister_device(dev); + set_bit(MT76_REMOVED, &mdev->phy.state); + devm_free_irq(&pdev->dev, pdev->irq, dev); + mt76_free_device(&dev->mt76); + pci_free_irq_vectors(pdev); +} + +static int mt7921_pci_suspend(struct device *device) +{ + struct pci_dev *pdev = to_pci_dev(device); + struct mt76_dev *mdev = pci_get_drvdata(pdev); + struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); + struct mt76_connac_pm *pm = &dev->pm; + int i, err; + + pm->suspended = true; + flush_work(&dev->reset_work); + cancel_delayed_work_sync(&pm->ps_work); + cancel_work_sync(&pm->wake_work); + + mt7921_roc_abort_sync(dev); + + err = mt792x_mcu_drv_pmctrl(dev); + if (err < 0) + goto restore_suspend; + + wait_event_timeout(dev->wait, + !dev->regd_in_progress, 5 * HZ); + + err = mt7921_mcu_radio_led_ctrl(dev, EXT_CMD_RADIO_OFF_LED); + if (err < 0) + goto restore_suspend; + + err = mt76_connac_mcu_set_hif_suspend(mdev, true, true); + if (err) + goto restore_suspend; + + /* always enable deep sleep during suspend to reduce + * power consumption + */ + mt76_connac_mcu_set_deep_sleep(&dev->mt76, true); + + napi_disable(&mdev->tx_napi); + mt76_worker_disable(&mdev->tx_worker); + + mt76_for_each_q_rx(mdev, i) { + napi_disable(&mdev->napi[i]); + } + + /* wait until dma is idle */ + mt76_poll(dev, MT_WFDMA0_GLO_CFG, + MT_WFDMA0_GLO_CFG_TX_DMA_BUSY | + MT_WFDMA0_GLO_CFG_RX_DMA_BUSY, 0, 1000); + + /* put dma disabled */ + mt76_clear(dev, MT_WFDMA0_GLO_CFG, + MT_WFDMA0_GLO_CFG_TX_DMA_EN | MT_WFDMA0_GLO_CFG_RX_DMA_EN); + + /* disable interrupt */ + mt76_wr(dev, dev->irq_map->host_irq_enable, 0); + mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0x0); + synchronize_irq(pdev->irq); + tasklet_kill(&mdev->irq_tasklet); + + err = mt792x_mcu_fw_pmctrl(dev); + if (err) + goto restore_napi; + + return 0; + +restore_napi: + mt76_for_each_q_rx(mdev, i) { + napi_enable(&mdev->napi[i]); + } + napi_enable(&mdev->tx_napi); + + if (!pm->ds_enable) + mt76_connac_mcu_set_deep_sleep(&dev->mt76, false); + + mt76_connac_mcu_set_hif_suspend(mdev, false, true); + +restore_suspend: + pm->suspended = false; + + if (err < 0) + mt792x_reset(&dev->mt76); + + return err; +} + +static int mt7921_pci_resume(struct device *device) +{ + struct pci_dev *pdev = to_pci_dev(device); + struct mt76_dev *mdev = pci_get_drvdata(pdev); + struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); + struct mt76_connac_pm *pm = &dev->pm; + int i, err; + + err = mt792x_mcu_drv_pmctrl(dev); + if (err < 0) + goto failed; + + mt792x_wpdma_reinit_cond(dev); + + /* enable interrupt */ + mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0xff); + mt76_connac_irq_enable(&dev->mt76, + dev->irq_map->tx.all_complete_mask | + dev->irq_map->rx.all_complete_mask | + MT_INT_MCU_CMD); + mt76_set(dev, MT_MCU2HOST_SW_INT_ENA, MT_MCU_CMD_WAKE_RX_PCIE); + + /* put dma enabled */ + mt76_set(dev, MT_WFDMA0_GLO_CFG, + MT_WFDMA0_GLO_CFG_TX_DMA_EN | MT_WFDMA0_GLO_CFG_RX_DMA_EN); + + mt76_worker_enable(&mdev->tx_worker); + + mt76_for_each_q_rx(mdev, i) { + napi_enable(&mdev->napi[i]); + } + napi_enable(&mdev->tx_napi); + + local_bh_disable(); + mt76_for_each_q_rx(mdev, i) { + napi_schedule(&mdev->napi[i]); + } + napi_schedule(&mdev->tx_napi); + local_bh_enable(); + + /* restore previous ds setting */ + if (!pm->ds_enable) + mt76_connac_mcu_set_deep_sleep(&dev->mt76, false); + + err = mt76_connac_mcu_set_hif_suspend(mdev, false, true); + if (err < 0) + goto failed; + + mt7921_mcu_regd_update(dev, mdev->alpha2, dev->country_ie_env); + err = mt7921_mcu_radio_led_ctrl(dev, EXT_CMD_RADIO_ON_LED); +failed: + pm->suspended = false; + + if (err < 0) + mt792x_reset(&dev->mt76); + + return err; +} + +static u32 mt7921_aer_rr(struct mt76_dev *mdev, u32 offset) +{ + return 0; +} + +static void mt7921_aer_wr(struct mt76_dev *mdev, u32 offset, u32 val) +{ + ; +} + +static u32 mt791_aer_rmw(struct mt76_dev *mdev, u32 offset, u32 mask, u32 val) +{ + return 0; +} + +static const struct mt76_bus_ops mt7921_aer_bus_hung_ops = { + .rr = mt7921_aer_rr, + .wr = mt7921_aer_wr, + .rmw = mt791_aer_rmw, + .type = MT76_BUS_MMIO +}; + +static void mt7921_pci_set_aer_bus_hung_ops(struct mt792x_dev *dev) +{ + if (READ_ONCE(dev->mt76.bus) == &mt7921_aer_bus_hung_ops) + return; + + atomic_set(&dev->mt76.bus_hung, true); + WRITE_ONCE(dev->mt76.bus, &mt7921_aer_bus_hung_ops); +} + +static pci_ers_result_t mt7921_error_detected(struct pci_dev *pdev, + pci_channel_state_t state) +{ + struct mt76_dev *mdev = pci_get_drvdata(pdev); + struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); + u32 aer_unc_val = 0, aer_co_val = 0; + + dev_err(mdev->dev, "PCIE error detect state: %d\n", state); + + /* Clear SW IRQ tasklet first */ + tasklet_kill(&mdev->irq_tasklet); + + if (state == pci_channel_io_perm_failure) { + mt7921_pci_set_aer_bus_hung_ops(dev); + return PCI_ERS_RESULT_DISCONNECT; + } + + pci_read_config_dword(pdev, PCIE_AER_UNC_STATUS_OFFSET, &aer_unc_val); + pci_read_config_dword(pdev, PCIE_AER_CO_STATUS_OFFSET, &aer_co_val); + + dev_warn(mdev->dev, "PCIE_AER_UNC_STATUS_OFFSET: 0x%x\n", aer_unc_val); + dev_warn(mdev->dev, "PCIE_AER_CO_STATUS_OFFSET: 0x%x\n", aer_co_val); + + /** + * Due to this error is from link error and this AER is un-correctable, + * so can't covered by device + **/ + if (aer_unc_val != 0) { + mt7921_pci_set_aer_bus_hung_ops(dev); + return PCI_ERS_RESULT_DISCONNECT; + } + + /** + * Try to recover it when state is pci_channel_io_frozen or + * AER is correctable error + **/ + if (state == pci_channel_io_frozen || aer_co_val != 0) { + /* Disable PCIE activity first. */ + pci_disable_device(pdev); + return PCI_ERS_RESULT_NEED_RESET; + } + + return PCI_ERS_RESULT_NONE; +} + +static pci_ers_result_t mt7921_slot_reset(struct pci_dev *pdev) +{ + struct mt76_dev *mdev = pci_get_drvdata(pdev); + int ret = 0; + + ret = pci_enable_device_mem(pdev); + + if (ret) { + dev_err(mdev->dev, "pci_enable_device_mem failed: %d\n", ret); + return PCI_ERS_RESULT_DISCONNECT; + } + + pci_set_master(pdev); + pci_restore_state(pdev); + pci_save_state(pdev); + /* Also try do the vendor reset to let it more clear. */ + mt792x_reset(mdev); + + return PCI_ERS_RESULT_RECOVERED; +} + +static const struct pci_error_handlers mt7921_err_handler = { + .error_detected = mt7921_error_detected, + .slot_reset = mt7921_slot_reset, +}; + +static void mt7921_pci_shutdown(struct pci_dev *pdev) +{ + mt7921_pci_remove(pdev); +} + +static DEFINE_SIMPLE_DEV_PM_OPS(mt7921_pm_ops, mt7921_pci_suspend, mt7921_pci_resume); + +static struct pci_driver mt7921_pci_driver = { + .name = KBUILD_MODNAME, + .id_table = mt7921_pci_device_table, + .probe = mt7921_pci_probe, + .remove = mt7921_pci_remove, + .shutdown = mt7921_pci_shutdown, + .driver.pm = pm_sleep_ptr(&mt7921_pm_ops), + .err_handler = &mt7921_err_handler, +}; + +module_pci_driver(mt7921_pci_driver); + +MODULE_DEVICE_TABLE(pci, mt7921_pci_device_table); +MODULE_FIRMWARE(MT7920_FIRMWARE_WM); +MODULE_FIRMWARE(MT7920_ROM_PATCH); +MODULE_FIRMWARE(MT7921_FIRMWARE_WM); +MODULE_FIRMWARE(MT7921_ROM_PATCH); +MODULE_FIRMWARE(MT7922_FIRMWARE_WM); +MODULE_FIRMWARE(MT7922_ROM_PATCH); +MODULE_FIRMWARE(MT7902_FIRMWARE_WM); +MODULE_FIRMWARE(MT7902_ROM_PATCH); +MODULE_AUTHOR("Sean Wang "); +MODULE_AUTHOR("Lorenzo Bianconi "); +MODULE_DESCRIPTION("MediaTek MT7921E (PCIe) wireless driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/pci_mac.c b/drivers/net/wireless/mediatek/mt76/mt7921/pci_mac.c new file mode 100644 index 000000000..98b3469ac --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7921/pci_mac.c @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2021 MediaTek Inc. */ + +#include "mt7921.h" +#include "../dma.h" +#include "../mt76_connac2_mac.h" + +int mt7921e_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, + enum mt76_txq_id qid, struct mt76_wcid *wcid, + struct ieee80211_sta *sta, + struct mt76_tx_info *tx_info) +{ + struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx_info->skb); + struct ieee80211_key_conf *key = info->control.hw_key; + struct mt76_connac_hw_txp *txp; + struct mt76_txwi_cache *t; + int id, pid; + u8 *txwi = (u8 *)txwi_ptr; + + if (unlikely(tx_info->skb->len <= ETH_HLEN)) + return -EINVAL; + + if (!wcid) + wcid = &dev->mt76.global_wcid; + + t = (struct mt76_txwi_cache *)(txwi + mdev->drv->txwi_size); + t->skb = tx_info->skb; + + id = mt76_token_consume(mdev, &t); + if (id < 0) + return id; + + if (sta) { + struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv; + + if (time_after(jiffies, msta->deflink.last_txs + HZ / 4)) { + info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; + msta->deflink.last_txs = jiffies; + } + } + + pid = mt76_tx_status_skb_add(mdev, wcid, tx_info->skb); + mt76_connac2_mac_write_txwi(mdev, txwi_ptr, tx_info->skb, wcid, key, + pid, qid, 0); + + txp = (struct mt76_connac_hw_txp *)(txwi + MT_TXD_SIZE); + memset(txp, 0, sizeof(struct mt76_connac_hw_txp)); + mt76_connac_write_hw_txp(mdev, tx_info, txp, id); + + tx_info->skb = NULL; + + return 0; +} + +int mt7921e_mac_reset(struct mt792x_dev *dev) +{ + int i, err; + + mt792xe_mcu_drv_pmctrl(dev); + + mt76_connac_free_pending_tx_skbs(&dev->pm, NULL); + + mt76_wr(dev, dev->irq_map->host_irq_enable, 0); + mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0x0); + + set_bit(MT76_MCU_RESET, &dev->mphy.state); + wake_up(&dev->mt76.mcu.wait); + skb_queue_purge(&dev->mt76.mcu.res_q); + + mt76_txq_schedule_all(&dev->mphy); + + mt76_worker_disable(&dev->mt76.tx_worker); + mt76_for_each_q_rx(&dev->mt76, i) { + napi_disable(&dev->mt76.napi[i]); + } + napi_disable(&dev->mt76.tx_napi); + + mt76_connac2_tx_token_put(&dev->mt76); + idr_init(&dev->mt76.token); + + mt792x_wpdma_reset(dev, true); + + mt76_for_each_q_rx(&dev->mt76, i) { + napi_enable(&dev->mt76.napi[i]); + } + + local_bh_disable(); + mt76_for_each_q_rx(&dev->mt76, i) { + napi_schedule(&dev->mt76.napi[i]); + } + local_bh_enable(); + + dev->fw_assert = false; + clear_bit(MT76_MCU_RESET, &dev->mphy.state); + + mt76_wr(dev, dev->irq_map->host_irq_enable, + dev->irq_map->tx.all_complete_mask | + dev->irq_map->rx.all_complete_mask | + MT_INT_MCU_CMD); + mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0xff); + + err = mt7921e_driver_own(dev); + if (err) + goto out; + + err = mt7921_run_firmware(dev); + if (err) + goto out; + + err = mt7921_mcu_set_eeprom(dev); + if (err) + goto out; + + err = mt7921_mac_init(dev); + if (err) + goto out; + + err = __mt7921_start(&dev->phy); +out: + + napi_enable(&dev->mt76.tx_napi); + local_bh_disable(); + napi_schedule(&dev->mt76.tx_napi); + local_bh_enable(); + + mt76_worker_enable(&dev->mt76.tx_worker); + + return err; +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/pci_mcu.c b/drivers/net/wireless/mediatek/mt76/mt7921/pci_mcu.c new file mode 100644 index 000000000..8439c849a --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7921/pci_mcu.c @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2021 MediaTek Inc. */ + +#include "mt7921.h" +#include "mcu.h" + +int mt7921e_driver_own(struct mt792x_dev *dev) +{ + u32 reg = mt7921_reg_map_l1(dev, MT_TOP_LPCR_HOST_BAND0); + + mt76_wr(dev, reg, MT_TOP_LPCR_HOST_DRV_OWN); + if (!mt76_poll_msec(dev, reg, MT_TOP_LPCR_HOST_FW_OWN, + 0, 500)) { + dev_err(dev->mt76.dev, "Timeout for driver own\n"); + return -EIO; + } + + return 0; +} + +static int +mt7921_mcu_send_message(struct mt76_dev *mdev, struct sk_buff *skb, + int cmd, int *seq) +{ + struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); + enum mt76_mcuq_id txq = MT_MCUQ_WM; + int ret; + + ret = mt76_connac2_mcu_fill_message(mdev, skb, cmd, seq); + if (ret) + return ret; + + mdev->mcu.timeout = 3 * HZ; + + if (cmd == MCU_CMD(FW_SCATTER)) + txq = MT_MCUQ_FWDL; + + return mt76_tx_queue_skb_raw(dev, mdev->q_mcu[txq], skb, 0); +} + +int mt7921e_mcu_init(struct mt792x_dev *dev) +{ + static const struct mt76_mcu_ops mt7921_mcu_ops = { + .headroom = sizeof(struct mt76_connac2_mcu_txd), + .mcu_skb_send_msg = mt7921_mcu_send_message, + .mcu_parse_response = mt7921_mcu_parse_response, + }; + int err; + + dev->mt76.mcu_ops = &mt7921_mcu_ops; + + err = mt7921e_driver_own(dev); + if (err) + return err; + + mt76_rmw_field(dev, MT_PCIE_MAC_PM, MT_PCIE_MAC_PM_L0S_DIS, 1); + + err = mt7921_run_firmware(dev); + + mt76_queue_tx_cleanup(dev, dev->mt76.q_mcu[MT_MCUQ_FWDL], false); + + return err; +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/regd.c b/drivers/net/wireless/mediatek/mt76/mt7921/regd.c new file mode 100644 index 000000000..4a8ea4624 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7921/regd.c @@ -0,0 +1,399 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2025 MediaTek Inc. */ + +#include +#include "mt7921.h" +#include "regd.h" +#include "mcu.h" + +static bool mt7921_disable_clc; +module_param_named(disable_clc, mt7921_disable_clc, bool, 0644); +MODULE_PARM_DESC(disable_clc, "disable CLC support"); + +static struct ieee80211_regdomain mt7921_regd_ww = { + .n_reg_rules = 1, + .alpha2 = "00", + .reg_rules = { + /* IEEE 802.11b/g, channels 1..11 */ + REG_RULE(2412 - 10, 2462 + 10, 40, 6, 20, 0), + } +}; + +bool mt7921_regd_clc_supported(struct mt792x_dev *dev) +{ + if (mt7921_disable_clc || + mt76_is_usb(&dev->mt76)) + return false; + + return true; +} + +static void +mt7921_regd_channel_update(struct wiphy *wiphy, struct mt792x_dev *dev) +{ +#define IS_UNII_INVALID(idx, sfreq, efreq) \ + (!(dev->phy.clc_chan_conf & BIT(idx)) && (cfreq) >= (sfreq) && (cfreq) <= (efreq)) + struct ieee80211_supported_band *sband; + struct mt76_dev *mdev = &dev->mt76; + struct device_node *np, *band_np; + struct ieee80211_channel *ch; + int i, cfreq; + + np = mt76_find_power_limits_node(mdev); + + sband = wiphy->bands[NL80211_BAND_5GHZ]; + if (!sband) + return; + + band_np = np ? of_get_child_by_name(np, "txpower-5g") : NULL; + for (i = 0; i < sband->n_channels; i++) { + ch = &sband->channels[i]; + cfreq = ch->center_freq; + + if (np && (!band_np || !mt76_find_channel_node(band_np, ch))) { + ch->flags |= IEEE80211_CHAN_DISABLED; + continue; + } + + /* UNII-4 */ + if (IS_UNII_INVALID(0, 5845, 5925)) + ch->flags |= IEEE80211_CHAN_DISABLED; + } + + sband = wiphy->bands[NL80211_BAND_6GHZ]; + if (!sband) + return; + + band_np = np ? of_get_child_by_name(np, "txpower-6g") : NULL; + for (i = 0; i < sband->n_channels; i++) { + ch = &sband->channels[i]; + cfreq = ch->center_freq; + + if (np && (!band_np || !mt76_find_channel_node(band_np, ch))) { + ch->flags |= IEEE80211_CHAN_DISABLED; + continue; + } + + /* UNII-5/6/7/8 */ + if (IS_UNII_INVALID(1, 5925, 6425) || + IS_UNII_INVALID(2, 6425, 6525) || + IS_UNII_INVALID(3, 6525, 6875) || + IS_UNII_INVALID(4, 6875, 7125)) + ch->flags |= IEEE80211_CHAN_DISABLED; + } +} + +int __mt7921_mcu_regd_update(struct mt792x_dev *dev, u8 *alpha2, + enum environment_cap country_ie_env) +{ + struct mt76_dev *mdev = &dev->mt76; + struct ieee80211_hw *hw = mdev->hw; + struct wiphy *wiphy = hw->wiphy; + int ret; + + lockdep_assert_held(&dev->mt76.mutex); + + if (!dev->regd_change) + return 0; + + ret = mt7921_mcu_set_clc(dev, alpha2, country_ie_env); + if (ret < 0) + return ret; + + mt7921_regd_channel_update(wiphy, dev); + + ret = mt76_connac_mcu_set_channel_domain(hw->priv); + if (ret < 0) + return ret; + + ret = mt7921_set_tx_sar_pwr(hw, NULL); + + return ret; +} + +int mt7921_mcu_regd_update(struct mt792x_dev *dev, u8 *alpha2, + enum environment_cap country_ie_env) +{ + int ret; + + dev->regd_in_progress = true; + + mt792x_mutex_acquire(dev); + ret = __mt7921_mcu_regd_update(dev, alpha2, country_ie_env); + mt792x_mutex_release(dev); + + dev->regd_change = false; + dev->regd_in_progress = false; + wake_up(&dev->wait); + + return ret; +} +EXPORT_SYMBOL_GPL(mt7921_mcu_regd_update); + +void mt7921_regd_notifier(struct wiphy *wiphy, + struct regulatory_request *req) +{ + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct mt792x_dev *dev = mt792x_hw_dev(hw); + struct mt76_connac_pm *pm = &dev->pm; + struct mt76_dev *mdev = &dev->mt76; + + if (req->initiator == NL80211_REGDOM_SET_BY_USER && + !dev->regd_user) + dev->regd_user = true; + + /* do not need to update the same country twice */ + if (!memcmp(req->alpha2, mdev->alpha2, 2) && + dev->country_ie_env == req->country_ie_env) + return; + + memcpy(mdev->alpha2, req->alpha2, 2); + mdev->region = req->dfs_region; + dev->country_ie_env = req->country_ie_env; + + if (req->initiator == NL80211_REGDOM_SET_BY_USER) { + if (dev->mt76.alpha2[0] == '0' && dev->mt76.alpha2[1] == '0') + wiphy->regulatory_flags &= ~REGULATORY_COUNTRY_IE_IGNORE; + else + wiphy->regulatory_flags |= REGULATORY_COUNTRY_IE_IGNORE; + } + + dev->regd_change = true; + + if (pm->suspended) + return; + + if (MT7921_REGD_SUPPORTED(&dev->phy)) { + mt7921_regd_update(&dev->phy, req->alpha2); + + return; + } + + mt7921_mcu_regd_update(dev, req->alpha2, + req->country_ie_env); +} + +static struct sk_buff * +mt7921_regd_query_regdb(struct mt792x_phy *phy, char *alpha2) +{ + struct wiphy *wiphy = phy->mt76->hw->wiphy; + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct mt792x_dev *dev = mt792x_hw_dev(hw); + struct mt7921_clc *clc = phy->clc[MT792x_CLC_REGD]; + struct mt7921_regd_query_req *req; + struct mt7921_regd_cc *regd_cc; + struct sk_buff *ret_skb = NULL; + u8 *pos, *last_pos; + int ret = 0; + + if (!clc) + return NULL; + + pos = clc->data; + last_pos = pos + le32_to_cpu(clc->len) - sizeof(struct mt7921_clc); + while (pos < last_pos) { + u32 req_len = 0; + u32 rules_len = 0; + u32 sign_len = 4; + u32 n_reg_rules; + + if (pos + sizeof(*regd_cc) > last_pos) + break; + + regd_cc = (struct mt7921_regd_cc *)pos; + n_reg_rules = le32_to_cpu(regd_cc->n_reg_rules); + if (n_reg_rules > NL80211_MAX_SUPP_REG_RULES) + break; + + rules_len = sizeof(struct mt7921_regd_rule_header) + + sizeof(struct mt7921_regd_rule) * n_reg_rules; + + if (pos + sizeof(*regd_cc) + rules_len + sign_len > last_pos) + break; + + pos += sizeof(*regd_cc) + rules_len + sign_len; + if (memcmp(regd_cc->alpha2, alpha2, 2)) + continue; + + req_len = sizeof(*req) + rules_len + sign_len; + req = kzalloc(req_len, GFP_KERNEL); + + if (!req) + return NULL; + + req->ver = regd_cc->ver; + req->sign_type = regd_cc->sign_type; + req->size = cpu_to_le32(rules_len + sign_len); + req->n_reg_rules = regd_cc->n_reg_rules; + + memcpy(req->alpha2, regd_cc->alpha2, 2); + memcpy(req->data, regd_cc->data, rules_len + sign_len); + + ret = mt76_mcu_send_and_get_msg(&dev->mt76, + MCU_CE_CMD(SET_REGD_CH), + req, req_len, true, &ret_skb); + + kfree(req); + + return ret < 0 ? NULL : ret_skb; + } + + return NULL; +} + +int mt7921_regd_update(struct mt792x_phy *phy, char *alpha2) +{ + struct wiphy *wiphy = phy->mt76->hw->wiphy; + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct mt792x_dev *dev = mt792x_hw_dev(hw); + struct mt7921_regd_rule *mt7921_rule; + struct mt76_dev *mdev = &dev->mt76; + struct ieee80211_regdomain *regd; + struct ieee80211_reg_rule *rule; + struct mt7921_regd_rule_ev *ev; + int i, num_of_rules = 0; + struct sk_buff *skb; + int ret = 0; + + if (dev->hw_full_reset) + return 0; + + if (!MT7921_REGD_SUPPORTED(phy)) + return -EOPNOTSUPP; + + mt792x_mutex_acquire(dev); + skb = mt7921_regd_query_regdb(phy, alpha2); + mt792x_mutex_release(dev); + + if (!skb) { + ret = -EINVAL; + goto err; + } + + if (skb->len < sizeof(*ev) + 4) { + ret = -EINVAL; + goto err; + } + + ev = (struct mt7921_regd_rule_ev *)(skb->data + 4); + num_of_rules = le32_to_cpu(ev->n_reg_rules); + + if (!num_of_rules || + WARN_ON_ONCE(num_of_rules > NL80211_MAX_SUPP_REG_RULES)) { + ret = -EINVAL; + goto err; + } + + if (skb->len < struct_size(ev, reg_rule, num_of_rules) + 4) { + ret = -EINVAL; + goto err; + } + + regd = kzalloc(struct_size(regd, reg_rules, num_of_rules), GFP_KERNEL); + if (!regd) { + ret = -ENOMEM; + goto err; + } + + for (i = 0; i < num_of_rules; i++) { + mt7921_rule = &ev->reg_rule[i]; + rule = ®d->reg_rules[i]; + + rule->freq_range.start_freq_khz = + MHZ_TO_KHZ(le32_to_cpu(mt7921_rule->start_freq)); + rule->freq_range.end_freq_khz = + MHZ_TO_KHZ(le32_to_cpu(mt7921_rule->end_freq)); + rule->freq_range.max_bandwidth_khz = + MHZ_TO_KHZ(le32_to_cpu(mt7921_rule->max_bw)); + /* not used by fw */ + rule->power_rule.max_antenna_gain = DBI_TO_MBI(6); + rule->power_rule.max_eirp = DBM_TO_MBM(22); + rule->flags = le32_to_cpu(mt7921_rule->flags); + } + + regd->n_reg_rules = num_of_rules; + regd->dfs_region = ev->dfs_region; + + memcpy(regd->alpha2, alpha2, 2); + memcpy(mdev->alpha2, alpha2, 2); + + dev->regd_change = true; + mt7921_mcu_regd_update(dev, alpha2, ENVIRON_ANY); + + ret = regulatory_set_wiphy_regd(wiphy, regd); + + kfree(regd); +err: + dev_kfree_skb(skb); + + if (ret < 0) + return regulatory_set_wiphy_regd(wiphy, &mt7921_regd_ww); + + return ret; +} +EXPORT_SYMBOL_GPL(mt7921_regd_update); + +static bool +mt7921_regd_is_valid_alpha2(const char *alpha2) +{ + if (!alpha2) + return false; + + if (alpha2[0] == '0' && alpha2[1] == '0') + return true; + + if (isalpha(alpha2[0]) && isalpha(alpha2[1])) + return true; + + return false; +} + +int mt7921_regd_change(struct mt792x_phy *phy, char *alpha2) +{ + struct wiphy *wiphy = phy->mt76->hw->wiphy; + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct mt792x_dev *dev = mt792x_hw_dev(hw); + struct mt76_dev *mdev = &dev->mt76; + + if (dev->hw_full_reset) + return 0; + + if (!mt7921_regd_is_valid_alpha2(alpha2) || + !mt7921_regd_clc_supported(dev) || + dev->regd_user) + return -EINVAL; + + if (mdev->alpha2[0] != '0' && mdev->alpha2[1] != '0') + return 0; + + /* do not need to update the same country twice */ + if (!memcmp(alpha2, mdev->alpha2, 2)) + return 0; + + if (MT7921_REGD_SUPPORTED(phy)) + return mt7921_regd_update(phy, alpha2); + else if (phy->chip_cap & MT792x_CHIP_CAP_11D_EN) + return regulatory_hint(wiphy, alpha2); + else + return mt7921_mcu_set_clc(dev, alpha2, ENVIRON_INDOOR); +} + +int mt7921_regd_init(struct mt792x_phy *phy) +{ + struct wiphy *wiphy = phy->mt76->hw->wiphy; + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct mt792x_dev *dev = mt792x_hw_dev(hw); + struct mt76_dev *mdev = &dev->mt76; + + if (MT7921_REGD_SUPPORTED(phy)) { + wiphy->regulatory_flags |= REGULATORY_WIPHY_SELF_MANAGED | + REGULATORY_DISABLE_BEACON_HINTS; + return mt7921_regd_update(phy, "00"); + } else if (phy->chip_cap & MT792x_CHIP_CAP_11D_EN) + wiphy->regulatory_flags |= REGULATORY_COUNTRY_IE_IGNORE | + REGULATORY_DISABLE_BEACON_HINTS; + else + memzero_explicit(&mdev->alpha2, sizeof(mdev->alpha2)); + + return 0; +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/regd.h b/drivers/net/wireless/mediatek/mt76/mt7921/regd.h new file mode 100644 index 000000000..d4223ba8f --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7921/regd.h @@ -0,0 +1,70 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* Copyright (C) 2025 MediaTek Inc. */ + +#ifndef __MT7921_REGD_H +#define __MT7921_REGD_H + +#include "mt7921.h" + +struct mt7921_regd_rule_header { + u8 alpha2[2]; + u8 dfs_region; + u8 rsv[13]; +}; + +struct mt7921_regd_rule { + __le32 start_freq; + __le32 end_freq; + __le32 max_bw; + __le32 eirp; + __le32 flags; + u8 rsv[12]; +}; + +struct mt7921_regd_cc { + u8 alpha2[2]; + u8 ver; + u8 rsv; + __le32 n_reg_rules; + u8 sign_type; + u8 rsv1[7]; + u8 data[]; +}; + +struct mt7921_regd_rule_ev { + __le16 tag; + __le16 len; + __le32 n_reg_rules; + u8 dfs_region; + u8 rsv[15]; + struct mt7921_regd_rule reg_rule[]; +}; + +struct mt7921_regd_query_req { + u8 ver; + u8 sign_type; + u8 rsv1[2]; + __le32 size; + u8 alpha2[2]; + u8 rsv2[2]; + __le32 n_reg_rules; + u8 rsv3[64]; + u8 data[]; +}; + +#define MT7921_REGD_SUPPORTED(phy) \ + (((phy)->chip_cap & MT792x_CHIP_CAP_REGD_EN) && \ + (phy)->clc[MT792x_CLC_REGD]) + +int mt7921_mcu_regd_update(struct mt792x_dev *dev, u8 *alpha2, + enum environment_cap country_ie_env); +int __mt7921_mcu_regd_update(struct mt792x_dev *dev, u8 *alpha2, + enum environment_cap country_ie_env); +void mt7921_regd_notifier(struct wiphy *wiphy, + struct regulatory_request *request); +bool mt7921_regd_clc_supported(struct mt792x_dev *dev); +int mt7921_regd_change(struct mt792x_phy *phy, char *alpha2); +int mt7921_regd_init(struct mt792x_phy *phy); +int mt7921_regd_update(struct mt792x_phy *phy, char *alpha2); + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/regs.h b/drivers/net/wireless/mediatek/mt76/mt7921/regs.h new file mode 100644 index 000000000..75b4e81c7 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7921/regs.h @@ -0,0 +1,86 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* Copyright (C) 2020 MediaTek Inc. */ + +#ifndef __MT7921_REGS_H +#define __MT7921_REGS_H + +#include "../mt792x_regs.h" + +#define MT_MDP_BASE 0x820cd000 +#define MT_MDP(ofs) (MT_MDP_BASE + (ofs)) + +#define MT_MDP_DCR0 MT_MDP(0x000) +#define MT_MDP_DCR0_DAMSDU_EN BIT(15) +#define MT_MDP_DCR0_RX_HDR_TRANS_EN BIT(19) + +#define MT_MDP_DCR1 MT_MDP(0x004) +#define MT_MDP_DCR1_MAX_RX_LEN GENMASK(15, 3) + +#define MT_MDP_BNRCFR0(_band) MT_MDP(0x070 + ((_band) << 8)) +#define MT_MDP_RCFR0_MCU_RX_MGMT GENMASK(5, 4) +#define MT_MDP_RCFR0_MCU_RX_CTL_NON_BAR GENMASK(7, 6) +#define MT_MDP_RCFR0_MCU_RX_CTL_BAR GENMASK(9, 8) + +#define MT_MDP_BNRCFR1(_band) MT_MDP(0x074 + ((_band) << 8)) +#define MT_MDP_RCFR1_MCU_RX_BYPASS GENMASK(23, 22) +#define MT_MDP_RCFR1_RX_DROPPED_UCAST GENMASK(28, 27) +#define MT_MDP_RCFR1_RX_DROPPED_MCAST GENMASK(30, 29) +#define MT_MDP_TO_HIF 0 +#define MT_MDP_TO_WM 1 + +#define MT_WFDMA0_HOST_INT_ENA MT_WFDMA0(0x204) +#define HOST_TX_DONE_INT_ENA8 BIT(12) +#define HOST_TX_DONE_INT_ENA9 BIT(13) +#define HOST_TX_DONE_INT_ENA10 BIT(14) +#define HOST_TX_DONE_INT_ENA11 BIT(15) +#define HOST_TX_DONE_INT_ENA12 BIT(16) +#define HOST_TX_DONE_INT_ENA13 BIT(17) +#define HOST_TX_DONE_INT_ENA14 BIT(18) +#define HOST_RX_DONE_INT_ENA4 BIT(22) +#define HOST_RX_DONE_INT_ENA5 BIT(23) +#define HOST_TX_DONE_INT_ENA16 BIT(26) +#define HOST_TX_DONE_INT_ENA17 BIT(27) + +/* WFDMA interrupt */ +#define MT_INT_RX_DONE_DATA HOST_RX_DONE_INT_ENA2 +#define MT_INT_RX_DONE_WM HOST_RX_DONE_INT_ENA0 +#define MT_INT_RX_DONE_WM2 HOST_RX_DONE_INT_ENA4 +#define MT_INT_RX_DONE_ALL (MT_INT_RX_DONE_DATA | \ + MT_INT_RX_DONE_WM | \ + MT_INT_RX_DONE_WM2) +#define MT_INT_TX_DONE_MCU_WM HOST_TX_DONE_INT_ENA17 +#define MT_INT_TX_DONE_FWDL HOST_TX_DONE_INT_ENA16 +#define MT_INT_TX_DONE_BAND0 HOST_TX_DONE_INT_ENA0 + +#define MT_INT_TX_DONE_MCU (MT_INT_TX_DONE_MCU_WM | \ + MT_INT_TX_DONE_FWDL) +#define MT_INT_TX_DONE_ALL (MT_INT_TX_DONE_MCU_WM | \ + MT_INT_TX_DONE_BAND0 | \ + GENMASK(18, 4)) + +#define MT_RX_DATA_RING_BASE MT_WFDMA0(0x520) + +#define MT_INFRA_CFG_BASE 0xfe000 +#define MT_INFRA(ofs) (MT_INFRA_CFG_BASE + (ofs)) + +#define MT_HIF_REMAP_L1 MT_INFRA(0x24c) +#define MT_HIF_REMAP_L1_MASK GENMASK(15, 0) +#define MT_HIF_REMAP_L1_OFFSET GENMASK(15, 0) +#define MT_HIF_REMAP_L1_BASE GENMASK(31, 16) +#define MT_HIF_REMAP_BASE_L1 0x40000 + +#define MT_WFSYS_SW_RST_B 0x18000140 + +#define MT_WTBLON_TOP_WDUCR MT_WTBLON_TOP(0x200) +#define MT_WTBLON_TOP_WDUCR_GROUP GENMASK(2, 0) + +#define MT_WTBL_UPDATE MT_WTBLON_TOP(0x230) +#define MT_WTBL_UPDATE_WLAN_IDX GENMASK(9, 0) +#define MT_WTBL_UPDATE_ADM_COUNT_CLEAR BIT(12) + +#define MT_PCIE_MAC_BASE 0x10000 +#define MT_PCIE_MAC(ofs) (MT_PCIE_MAC_BASE + (ofs)) +#define MT_PCIE_MAC_INT_ENABLE MT_PCIE_MAC(0x188) +#define MT_PCIE_MAC_PM MT_PCIE_MAC(0x194) + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/sdio.c b/drivers/net/wireless/mediatek/mt76/mt7921/sdio.c new file mode 100644 index 000000000..9150f1857 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7921/sdio.c @@ -0,0 +1,337 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2021 MediaTek Inc. + * + */ + +#include +#include +#include + +#include +#include +#include + +#include "mt7921.h" +#include "../sdio.h" +#include "../mt76_connac2_mac.h" +#include "mcu.h" + +static const struct sdio_device_id mt7921s_table[] = { + { SDIO_DEVICE(SDIO_VENDOR_ID_MEDIATEK, 0x7901), + .driver_data = (kernel_ulong_t)MT7921_FIRMWARE_WM }, + { SDIO_DEVICE(SDIO_VENDOR_ID_MEDIATEK, 0x7902), + .driver_data = (kernel_ulong_t)MT7902_FIRMWARE_WM }, + { } /* Terminating entry */ +}; + +static void mt7921s_txrx_worker(struct mt76_worker *w) +{ + struct mt76_sdio *sdio = container_of(w, struct mt76_sdio, + txrx_worker); + struct mt76_dev *mdev = container_of(sdio, struct mt76_dev, sdio); + struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); + + if (!mt76_connac_pm_ref(&dev->mphy, &dev->pm)) { + queue_work(mdev->wq, &dev->pm.wake_work); + return; + } + + mt76s_txrx_worker(sdio); + mt76_connac_pm_unref(&dev->mphy, &dev->pm); +} + +static void mt7921s_unregister_device(struct mt792x_dev *dev) +{ + struct mt76_connac_pm *pm = &dev->pm; + + cancel_work_sync(&dev->init_work); + mt76_unregister_device(&dev->mt76); + cancel_delayed_work_sync(&pm->ps_work); + cancel_work_sync(&pm->wake_work); + + mt76s_deinit(&dev->mt76); + mt7921s_wfsys_reset(dev); + skb_queue_purge(&dev->mt76.mcu.res_q); + + mt76_free_device(&dev->mt76); +} + +static int mt7921s_parse_intr(struct mt76_dev *dev, struct mt76s_intr *intr) +{ + struct mt76_sdio *sdio = &dev->sdio; + struct mt7921_sdio_intr *irq_data = sdio->intr_data; + int i, err; + + sdio_claim_host(sdio->func); + err = sdio_readsb(sdio->func, irq_data, MCR_WHISR, sizeof(*irq_data)); + sdio_release_host(sdio->func); + + if (err < 0) + return err; + + if (irq_data->rx.num[0] > 16 || + irq_data->rx.num[1] > 128) + return -EINVAL; + + intr->isr = irq_data->isr; + intr->rec_mb = irq_data->rec_mb; + intr->tx.wtqcr = irq_data->tx.wtqcr; + intr->rx.num = irq_data->rx.num; + for (i = 0; i < 2 ; i++) { + if (!i) + intr->rx.len[0] = irq_data->rx.len0; + else + intr->rx.len[1] = irq_data->rx.len1; + } + + return 0; +} + +static int mt7921s_probe(struct sdio_func *func, + const struct sdio_device_id *id) +{ + static const struct mt76_driver_ops drv_ops = { + .txwi_size = MT_SDIO_TXD_SIZE, + .drv_flags = MT_DRV_AMSDU_OFFLOAD, + .survey_flags = SURVEY_INFO_TIME_TX | + SURVEY_INFO_TIME_RX | + SURVEY_INFO_TIME_BSS_RX, + .tx_prepare_skb = mt7921_usb_sdio_tx_prepare_skb, + .tx_complete_skb = mt7921_usb_sdio_tx_complete_skb, + .tx_status_data = mt7921_usb_sdio_tx_status_data, + .rx_skb = mt7921_queue_rx_skb, + .rx_check = mt7921_rx_check, + .sta_add = mt7921_mac_sta_add, + .sta_event = mt7921_mac_sta_event, + .sta_remove = mt7921_mac_sta_remove, + .update_survey = mt792x_update_channel, + .set_channel = mt7921_set_channel, + }; + static const struct mt76_bus_ops mt7921s_ops = { + .rr = mt76s_rr, + .rmw = mt76s_rmw, + .wr = mt76s_wr, + .write_copy = mt76s_write_copy, + .read_copy = mt76s_read_copy, + .wr_rp = mt76s_wr_rp, + .rd_rp = mt76s_rd_rp, + .type = MT76_BUS_SDIO, + }; + static const struct mt792x_hif_ops mt7921_sdio_ops = { + .init_reset = mt7921s_init_reset, + .reset = mt7921s_mac_reset, + .mcu_init = mt7921s_mcu_init, + .drv_own = mt7921s_mcu_drv_pmctrl, + .fw_own = mt7921s_mcu_fw_pmctrl, + }; + struct ieee80211_ops *ops; + struct mt792x_dev *dev; + struct mt76_dev *mdev; + u8 features; + int ret; + + ops = mt792x_get_mac80211_ops(&func->dev, &mt7921_ops, + (void *)id->driver_data, &features); + if (!ops) + return -ENOMEM; + + mdev = mt76_alloc_device(&func->dev, sizeof(*dev), ops, &drv_ops); + if (!mdev) + return -ENOMEM; + + dev = container_of(mdev, struct mt792x_dev, mt76); + dev->fw_features = features; + dev->hif_ops = &mt7921_sdio_ops; + sdio_set_drvdata(func, dev); + + ret = mt76s_init(mdev, func, &mt7921s_ops); + if (ret < 0) + goto error; + + ret = mt76s_hw_init(mdev, func, MT76_CONNAC2_SDIO); + if (ret) + goto error; + + atomic_set(&mdev->bus_hung, false); + + mdev->rev = (mt76_rr(dev, MT_HW_CHIPID) << 16) | + (mt76_rr(dev, MT_HW_REV) & 0xff); + dev_dbg(mdev->dev, "ASIC revision: %04x\n", mdev->rev); + + mdev->sdio.parse_irq = mt7921s_parse_intr; + mdev->sdio.intr_data = devm_kmalloc(mdev->dev, + sizeof(struct mt7921_sdio_intr), + GFP_KERNEL); + if (!mdev->sdio.intr_data) { + ret = -ENOMEM; + goto error; + } + + ret = mt76s_alloc_rx_queue(mdev, MT_RXQ_MAIN); + if (ret) + goto error; + + ret = mt76s_alloc_rx_queue(mdev, MT_RXQ_MCU); + if (ret) + goto error; + + ret = mt76s_alloc_tx(mdev); + if (ret) + goto error; + + ret = mt76_worker_setup(mt76_hw(dev), &mdev->sdio.txrx_worker, + mt7921s_txrx_worker, "sdio-txrx"); + if (ret) + goto error; + + sched_set_fifo_low(mdev->sdio.txrx_worker.task); + + ret = mt7921_register_device(dev); + if (ret) + goto error; + + return 0; + +error: + mt76s_deinit(&dev->mt76); + mt76_free_device(&dev->mt76); + + return ret; +} + +static void mt7921s_remove(struct sdio_func *func) +{ + struct mt792x_dev *dev = sdio_get_drvdata(func); + + mt7921s_unregister_device(dev); +} + +static int mt7921s_suspend(struct device *__dev) +{ + struct sdio_func *func = dev_to_sdio_func(__dev); + struct mt792x_dev *dev = sdio_get_drvdata(func); + struct mt76_connac_pm *pm = &dev->pm; + struct mt76_dev *mdev = &dev->mt76; + int err; + + pm->suspended = true; + set_bit(MT76_STATE_SUSPEND, &mdev->phy.state); + + flush_work(&dev->reset_work); + cancel_delayed_work_sync(&pm->ps_work); + cancel_work_sync(&pm->wake_work); + + mt7921_roc_abort_sync(dev); + + err = mt792x_mcu_drv_pmctrl(dev); + if (err < 0) + goto restore_suspend; + + /* always enable deep sleep during suspend to reduce + * power consumption + */ + mt76_connac_mcu_set_deep_sleep(mdev, true); + + mt76_txq_schedule_all(&dev->mphy); + mt76_worker_disable(&mdev->tx_worker); + mt76_worker_disable(&mdev->sdio.status_worker); + mt76_worker_disable(&mdev->sdio.stat_worker); + clear_bit(MT76_READING_STATS, &dev->mphy.state); + mt76_tx_status_check(mdev, true); + + mt76_worker_schedule(&mdev->sdio.txrx_worker); + wait_event_timeout(dev->mt76.sdio.wait, + mt76s_txqs_empty(&dev->mt76), 5 * HZ); + + /* It is supposed that SDIO bus is idle at the point */ + err = mt76_connac_mcu_set_hif_suspend(mdev, true, true); + if (err) + goto restore_worker; + + mt76_worker_disable(&mdev->sdio.txrx_worker); + mt76_worker_disable(&mdev->sdio.net_worker); + + err = mt792x_mcu_fw_pmctrl(dev); + if (err) + goto restore_txrx_worker; + + sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); + + return 0; + +restore_txrx_worker: + mt76_worker_enable(&mdev->sdio.net_worker); + mt76_worker_enable(&mdev->sdio.txrx_worker); + mt76_connac_mcu_set_hif_suspend(mdev, false, true); + +restore_worker: + mt76_worker_enable(&mdev->tx_worker); + mt76_worker_enable(&mdev->sdio.status_worker); + mt76_worker_enable(&mdev->sdio.stat_worker); + + if (!pm->ds_enable) + mt76_connac_mcu_set_deep_sleep(mdev, false); + +restore_suspend: + clear_bit(MT76_STATE_SUSPEND, &mdev->phy.state); + pm->suspended = false; + + if (err < 0) + mt792x_reset(&dev->mt76); + + return err; +} + +static int mt7921s_resume(struct device *__dev) +{ + struct sdio_func *func = dev_to_sdio_func(__dev); + struct mt792x_dev *dev = sdio_get_drvdata(func); + struct mt76_connac_pm *pm = &dev->pm; + struct mt76_dev *mdev = &dev->mt76; + int err; + + clear_bit(MT76_STATE_SUSPEND, &mdev->phy.state); + + err = mt792x_mcu_drv_pmctrl(dev); + if (err < 0) + goto failed; + + mt76_worker_enable(&mdev->tx_worker); + mt76_worker_enable(&mdev->sdio.txrx_worker); + mt76_worker_enable(&mdev->sdio.status_worker); + mt76_worker_enable(&mdev->sdio.net_worker); + mt76_worker_enable(&mdev->sdio.stat_worker); + + /* restore previous ds setting */ + if (!pm->ds_enable) + mt76_connac_mcu_set_deep_sleep(mdev, false); + + err = mt76_connac_mcu_set_hif_suspend(mdev, false, true); +failed: + pm->suspended = false; + + if (err < 0) + mt792x_reset(&dev->mt76); + + return err; +} + +MODULE_DEVICE_TABLE(sdio, mt7921s_table); +MODULE_FIRMWARE(MT7921_FIRMWARE_WM); +MODULE_FIRMWARE(MT7921_ROM_PATCH); +MODULE_FIRMWARE(MT7902_FIRMWARE_WM); +MODULE_FIRMWARE(MT7902_ROM_PATCH); + +static DEFINE_SIMPLE_DEV_PM_OPS(mt7921s_pm_ops, mt7921s_suspend, mt7921s_resume); + +static struct sdio_driver mt7921s_driver = { + .name = KBUILD_MODNAME, + .probe = mt7921s_probe, + .remove = mt7921s_remove, + .id_table = mt7921s_table, + .drv.pm = pm_sleep_ptr(&mt7921s_pm_ops), +}; +module_sdio_driver(mt7921s_driver); +MODULE_DESCRIPTION("MediaTek MT7921S (SDIO) wireless driver"); +MODULE_AUTHOR("Sean Wang "); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/sdio_mac.c b/drivers/net/wireless/mediatek/mt76/mt7921/sdio_mac.c new file mode 100644 index 000000000..416d49e53 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7921/sdio_mac.c @@ -0,0 +1,199 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2021 MediaTek Inc. */ + +#include +#include +#include "mt7921.h" +#include "../mt76_connac2_mac.h" +#include "../sdio.h" +#include +#include + +static void mt7921s_enable_irq(struct mt76_dev *dev) +{ + struct mt76_sdio *sdio = &dev->sdio; + + sdio_claim_host(sdio->func); + sdio_writel(sdio->func, WHLPCR_INT_EN_SET, MCR_WHLPCR, NULL); + sdio_release_host(sdio->func); +} + +static void mt7921s_disable_irq(struct mt76_dev *dev) +{ + struct mt76_sdio *sdio = &dev->sdio; + + sdio_claim_host(sdio->func); + sdio_writel(sdio->func, WHLPCR_INT_EN_CLR, MCR_WHLPCR, NULL); + sdio_release_host(sdio->func); +} + +static u32 mt7921s_read_whcr(struct mt76_dev *dev) +{ + return sdio_readl(dev->sdio.func, MCR_WHCR, NULL); +} + +int mt7921s_wfsys_reset(struct mt792x_dev *dev) +{ + struct mt76_sdio *sdio = &dev->mt76.sdio; + u32 val, status; + + if (atomic_read(&dev->mt76.bus_hung)) + return 0; + + mt7921s_mcu_drv_pmctrl(dev); + + sdio_claim_host(sdio->func); + + val = sdio_readl(sdio->func, MCR_WHCR, NULL); + val &= ~WF_WHOLE_PATH_RSTB; + sdio_writel(sdio->func, val, MCR_WHCR, NULL); + + msleep(50); + + val = sdio_readl(sdio->func, MCR_WHCR, NULL); + val &= ~WF_SDIO_WF_PATH_RSTB; + sdio_writel(sdio->func, val, MCR_WHCR, NULL); + + usleep_range(1000, 2000); + + val = sdio_readl(sdio->func, MCR_WHCR, NULL); + val |= WF_WHOLE_PATH_RSTB; + sdio_writel(sdio->func, val, MCR_WHCR, NULL); + + readx_poll_timeout(mt7921s_read_whcr, &dev->mt76, status, + status & WF_RST_DONE, 50000, 2000000); + + sdio_release_host(sdio->func); + + clear_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state); + + /* activate mt7921s again */ + mt7921s_mcu_drv_pmctrl(dev); + mt76_clear(dev, MT_CONN_STATUS, MT_WIFI_PATCH_DL_STATE); + mt7921s_mcu_fw_pmctrl(dev); + mt7921s_mcu_drv_pmctrl(dev); + + return 0; +} + +int mt7921s_init_reset(struct mt792x_dev *dev) +{ + set_bit(MT76_MCU_RESET, &dev->mphy.state); + + wake_up(&dev->mt76.mcu.wait); + skb_queue_purge(&dev->mt76.mcu.res_q); + wait_event_timeout(dev->mt76.sdio.wait, + mt76s_txqs_empty(&dev->mt76), 5 * HZ); + mt76_worker_disable(&dev->mt76.sdio.txrx_worker); + + mt7921s_disable_irq(&dev->mt76); + mt7921s_wfsys_reset(dev); + + mt76_worker_enable(&dev->mt76.sdio.txrx_worker); + clear_bit(MT76_MCU_RESET, &dev->mphy.state); + mt7921s_enable_irq(&dev->mt76); + + return 0; +} + +static struct mt76_sdio *msdio; +static void mt7921s_card_reset(struct work_struct *work) +{ + struct mmc_host *sdio_host = msdio->func->card->host; + + sdio_claim_host(msdio->func); + sdio_release_irq(msdio->func); + sdio_release_host(msdio->func); + + mmc_remove_host(sdio_host); + msleep(50); + mmc_add_host(sdio_host); +} + +static DECLARE_WORK(sdio_reset_work, mt7921s_card_reset); +static int mt7921s_check_bus(struct mt76_dev *dev) +{ + struct mt76_sdio *sdio = &dev->sdio; + int err; + + sdio_claim_host(sdio->func); + sdio_readl(dev->sdio.func, MCR_WHCR, &err); + sdio_release_host(sdio->func); + + return err; +} + +static int mt7921s_host_reset(struct mt792x_dev *dev) +{ + struct mt76_dev *mdev = &dev->mt76; + int err = -1; + + if (!atomic_read(&mdev->bus_hung)) + err = mt7921s_check_bus(&dev->mt76); + + if (err) { + atomic_set(&mdev->bus_hung, true); + msdio = &dev->mt76.sdio; + dev_err(mdev->dev, "SDIO bus problem detected(%d), resetting card!!\n", err); + schedule_work(&sdio_reset_work); + return err; + } + + atomic_set(&mdev->bus_hung, false); + + return 0; +} + +int mt7921s_mac_reset(struct mt792x_dev *dev) +{ + int err; + + mt76_connac_free_pending_tx_skbs(&dev->pm, NULL); + + mt7921s_host_reset(dev); + if (atomic_read(&dev->mt76.bus_hung)) + return 0; + + mt76_txq_schedule_all(&dev->mphy); + mt76_worker_disable(&dev->mt76.tx_worker); + set_bit(MT76_MCU_RESET, &dev->mphy.state); + wake_up(&dev->mt76.mcu.wait); + skb_queue_purge(&dev->mt76.mcu.res_q); + wait_event_timeout(dev->mt76.sdio.wait, + mt76s_txqs_empty(&dev->mt76), 5 * HZ); + mt76_worker_disable(&dev->mt76.sdio.txrx_worker); + mt76_worker_disable(&dev->mt76.sdio.status_worker); + mt76_worker_disable(&dev->mt76.sdio.net_worker); + mt76_worker_disable(&dev->mt76.sdio.stat_worker); + + mt7921s_disable_irq(&dev->mt76); + mt7921s_wfsys_reset(dev); + + mt76_worker_enable(&dev->mt76.sdio.txrx_worker); + mt76_worker_enable(&dev->mt76.sdio.status_worker); + mt76_worker_enable(&dev->mt76.sdio.net_worker); + mt76_worker_enable(&dev->mt76.sdio.stat_worker); + + dev->fw_assert = false; + clear_bit(MT76_MCU_RESET, &dev->mphy.state); + mt7921s_enable_irq(&dev->mt76); + + err = mt7921_run_firmware(dev); + if (err) + goto out; + + err = mt7921_mcu_set_eeprom(dev); + if (err) + goto out; + + err = mt7921_mac_init(dev); + if (err) + goto out; + + err = __mt7921_start(&dev->phy); +out: + + mt76_worker_enable(&dev->mt76.tx_worker); + + return err; +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/sdio_mcu.c b/drivers/net/wireless/mediatek/mt76/mt7921/sdio_mcu.c new file mode 100644 index 000000000..14e66f3f5 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7921/sdio_mcu.c @@ -0,0 +1,175 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2021 MediaTek Inc. */ + +#include +#include +#include +#include + +#include "mt7921.h" +#include "../sdio.h" +#include "../mt76_connac2_mac.h" +#include "mcu.h" +#include "regs.h" + +static int +mt7921s_mcu_send_message(struct mt76_dev *mdev, struct sk_buff *skb, + int cmd, int *seq) +{ + struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); + enum mt7921_sdio_pkt_type type = MT7921_SDIO_CMD; + enum mt76_mcuq_id txq = MT_MCUQ_WM; + int ret, pad; + + /* We just return in case firmware assertion to avoid blocking the + * common workqueue to run, for example, the coredump work might be + * blocked by mt792x_mac_work that is excuting register access via sdio + * bus. + */ + if (dev->fw_assert) + return -EBUSY; + + ret = mt76_connac2_mcu_fill_message(mdev, skb, cmd, seq); + if (ret) + return ret; + + mdev->mcu.timeout = 3 * HZ; + + if (cmd == MCU_CMD(FW_SCATTER)) + type = MT7921_SDIO_FWDL; + + mt792x_skb_add_usb_sdio_hdr(dev, skb, type); + pad = round_up(skb->len, 4) - skb->len; + __skb_put_zero(skb, pad); + + ret = mt76_tx_queue_skb_raw(dev, mdev->q_mcu[txq], skb, 0); + if (ret) + return ret; + + mt76_queue_kick(dev, mdev->q_mcu[txq]); + + return ret; +} + +static u32 mt7921s_read_rm3r(struct mt792x_dev *dev) +{ + struct mt76_sdio *sdio = &dev->mt76.sdio; + + return sdio_readl(sdio->func, MCR_D2HRM3R, NULL); +} + +static u32 mt7921s_clear_rm3r_drv_own(struct mt792x_dev *dev) +{ + struct mt76_sdio *sdio = &dev->mt76.sdio; + u32 val; + + val = sdio_readl(sdio->func, MCR_D2HRM3R, NULL); + if (val) + sdio_writel(sdio->func, H2D_SW_INT_CLEAR_MAILBOX_ACK, + MCR_WSICR, NULL); + + return val; +} + +int mt7921s_mcu_init(struct mt792x_dev *dev) +{ + static const struct mt76_mcu_ops mt7921s_mcu_ops = { + .headroom = MT_SDIO_HDR_SIZE + + sizeof(struct mt76_connac2_mcu_txd), + .tailroom = MT_SDIO_TAIL_SIZE, + .mcu_skb_send_msg = mt7921s_mcu_send_message, + .mcu_parse_response = mt7921_mcu_parse_response, + .mcu_rr = mt76_connac_mcu_reg_rr, + .mcu_wr = mt76_connac_mcu_reg_wr, + }; + int ret; + + mt7921s_mcu_drv_pmctrl(dev); + + dev->mt76.mcu_ops = &mt7921s_mcu_ops; + + ret = mt7921_run_firmware(dev); + if (ret) + return ret; + + set_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state); + + return 0; +} + +int mt7921s_mcu_drv_pmctrl(struct mt792x_dev *dev) +{ + struct sdio_func *func = dev->mt76.sdio.func; + struct mt76_phy *mphy = &dev->mt76.phy; + struct mt76_connac_pm *pm = &dev->pm; + u32 status; + int err; + + sdio_claim_host(func); + + sdio_writel(func, WHLPCR_FW_OWN_REQ_CLR, MCR_WHLPCR, NULL); + + err = readx_poll_timeout(mt76s_read_pcr, &dev->mt76, status, + status & WHLPCR_IS_DRIVER_OWN, 2000, 1000000); + + if (!err && test_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state)) + err = readx_poll_timeout(mt7921s_read_rm3r, dev, status, + status & D2HRM3R_IS_DRIVER_OWN, + 2000, 1000000); + + sdio_release_host(func); + + if (err < 0) { + dev_err(dev->mt76.dev, "driver own failed\n"); + return -EIO; + } + + clear_bit(MT76_STATE_PM, &mphy->state); + + pm->stats.last_wake_event = jiffies; + pm->stats.doze_time += pm->stats.last_wake_event - + pm->stats.last_doze_event; + + return 0; +} + +int mt7921s_mcu_fw_pmctrl(struct mt792x_dev *dev) +{ + struct sdio_func *func = dev->mt76.sdio.func; + struct mt76_phy *mphy = &dev->mt76.phy; + struct mt76_connac_pm *pm = &dev->pm; + u32 status; + int err; + + sdio_claim_host(func); + + if (test_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state)) { + err = readx_poll_timeout(mt7921s_clear_rm3r_drv_own, + dev, status, + !(status & D2HRM3R_IS_DRIVER_OWN), + 2000, 1000000); + if (err < 0) { + dev_err(dev->mt76.dev, "mailbox ACK not cleared\n"); + goto out; + } + } + + sdio_writel(func, WHLPCR_FW_OWN_REQ_SET, MCR_WHLPCR, NULL); + + err = readx_poll_timeout(mt76s_read_pcr, &dev->mt76, status, + !(status & WHLPCR_IS_DRIVER_OWN), 2000, 1000000); +out: + sdio_release_host(func); + + if (err < 0) { + dev_err(dev->mt76.dev, "firmware own failed\n"); + clear_bit(MT76_STATE_PM, &mphy->state); + return -EIO; + } + + pm->stats.last_doze_event = jiffies; + pm->stats.awake_time += pm->stats.last_doze_event - + pm->stats.last_wake_event; + + return 0; +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/testmode.c b/drivers/net/wireless/mediatek/mt76/mt7921/testmode.c new file mode 100644 index 000000000..e60ee992e --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7921/testmode.c @@ -0,0 +1,196 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear + +#include "mt7921.h" +#include "mcu.h" + +enum mt7921_testmode_attr { + MT7921_TM_ATTR_UNSPEC, + MT7921_TM_ATTR_SET, + MT7921_TM_ATTR_QUERY, + MT7921_TM_ATTR_RSP, + + /* keep last */ + NUM_MT7921_TM_ATTRS, + MT7921_TM_ATTR_MAX = NUM_MT7921_TM_ATTRS - 1, +}; + +struct mt7921_tm_cmd { + u8 action; + u32 param0; + u32 param1; +}; + +struct mt7921_tm_evt { + u32 param0; + u32 param1; +}; + +static const struct nla_policy mt7921_tm_policy[NUM_MT7921_TM_ATTRS] = { + [MT7921_TM_ATTR_SET] = NLA_POLICY_EXACT_LEN(sizeof(struct mt7921_tm_cmd)), + [MT7921_TM_ATTR_QUERY] = NLA_POLICY_EXACT_LEN(sizeof(struct mt7921_tm_cmd)), +}; + +static int +mt7921_tm_set(struct mt792x_dev *dev, struct mt7921_tm_cmd *req) +{ + struct mt7921_rftest_cmd cmd = { + .action = req->action, + .param0 = cpu_to_le32(req->param0), + .param1 = cpu_to_le32(req->param1), + }; + bool testmode = false, normal = false; + struct mt76_connac_pm *pm = &dev->pm; + struct mt76_phy *phy = &dev->mphy; + int ret = -ENOTCONN; + + mutex_lock(&dev->mt76.mutex); + + if (req->action == TM_SWITCH_MODE) { + if (req->param0 == MT7921_TM_NORMAL) + normal = true; + else + testmode = true; + } + + if (testmode) { + /* Make sure testmode running on full power mode */ + pm->enable = false; + cancel_delayed_work_sync(&pm->ps_work); + cancel_work_sync(&pm->wake_work); + __mt792x_mcu_drv_pmctrl(dev); + + phy->test.state = MT76_TM_STATE_ON; + } + + if (!mt76_testmode_enabled(phy)) + goto out; + + ret = mt76_mcu_send_msg(&dev->mt76, MCU_CE_CMD(TEST_CTRL), &cmd, + sizeof(cmd), false); + if (ret) + goto out; + + if (normal) { + /* Switch back to the normal world */ + phy->test.state = MT76_TM_STATE_OFF; + pm->enable = true; + } +out: + mutex_unlock(&dev->mt76.mutex); + + return ret; +} + +static int +mt7921_tm_query(struct mt792x_dev *dev, struct mt7921_tm_cmd *req, + struct mt7921_tm_evt *evt_resp) +{ + struct mt7921_rftest_cmd cmd = { + .action = req->action, + .param0 = cpu_to_le32(req->param0), + .param1 = cpu_to_le32(req->param1), + }; + struct mt7921_rftest_evt *evt; + struct sk_buff *skb; + int ret; + + ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_CE_CMD(TEST_CTRL), + &cmd, sizeof(cmd), true, &skb); + if (ret) + goto out; + + evt = (struct mt7921_rftest_evt *)skb->data; + evt_resp->param0 = le32_to_cpu(evt->param0); + evt_resp->param1 = le32_to_cpu(evt->param1); +out: + dev_kfree_skb(skb); + + return ret; +} + +int mt7921_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + void *data, int len) +{ + struct nlattr *tb[NUM_MT76_TM_ATTRS]; + struct mt76_phy *mphy = hw->priv; + struct mt792x_phy *phy = mphy->priv; + int err; + + if (!test_bit(MT76_STATE_RUNNING, &mphy->state) || + !(hw->conf.flags & IEEE80211_CONF_MONITOR)) + return -ENOTCONN; + + err = nla_parse_deprecated(tb, MT76_TM_ATTR_MAX, data, len, + mt76_tm_policy, NULL); + if (err) + return err; + + if (tb[MT76_TM_ATTR_DRV_DATA]) { + struct nlattr *drv_tb[NUM_MT7921_TM_ATTRS], *data; + int ret; + + data = tb[MT76_TM_ATTR_DRV_DATA]; + ret = nla_parse_nested_deprecated(drv_tb, + MT7921_TM_ATTR_MAX, + data, mt7921_tm_policy, + NULL); + if (ret) + return ret; + + data = drv_tb[MT7921_TM_ATTR_SET]; + if (data) + return mt7921_tm_set(phy->dev, nla_data(data)); + } + + return -EINVAL; +} + +int mt7921_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg, + struct netlink_callback *cb, void *data, int len) +{ + struct nlattr *tb[NUM_MT76_TM_ATTRS]; + struct mt76_phy *mphy = hw->priv; + struct mt792x_phy *phy = mphy->priv; + int err; + + if (!test_bit(MT76_STATE_RUNNING, &mphy->state) || + !(hw->conf.flags & IEEE80211_CONF_MONITOR) || + !mt76_testmode_enabled(mphy)) + return -ENOTCONN; + + if (cb->args[2]++ > 0) + return -ENOENT; + + err = nla_parse_deprecated(tb, MT76_TM_ATTR_MAX, data, len, + mt76_tm_policy, NULL); + if (err) + return err; + + if (tb[MT76_TM_ATTR_DRV_DATA]) { + struct nlattr *drv_tb[NUM_MT7921_TM_ATTRS], *data; + int ret; + + data = tb[MT76_TM_ATTR_DRV_DATA]; + ret = nla_parse_nested_deprecated(drv_tb, + MT7921_TM_ATTR_MAX, + data, mt7921_tm_policy, + NULL); + if (ret) + return ret; + + data = drv_tb[MT7921_TM_ATTR_QUERY]; + if (data) { + struct mt7921_tm_evt evt_resp; + + err = mt7921_tm_query(phy->dev, nla_data(data), + &evt_resp); + if (err) + return err; + + return nla_put(msg, MT7921_TM_ATTR_RSP, + sizeof(evt_resp), &evt_resp); + } + } + + return -EINVAL; +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/usb.c b/drivers/net/wireless/mediatek/mt76/mt7921/usb.c new file mode 100644 index 000000000..2fdecf239 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7921/usb.c @@ -0,0 +1,355 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2022 MediaTek Inc. + * + * Author: Lorenzo Bianconi + */ + +#include +#include +#include + +#include "mt7921.h" +#include "mcu.h" +#include "../mt76_connac2_mac.h" + +static const struct usb_device_id mt7921u_device_table[] = { + { USB_DEVICE_AND_INTERFACE_INFO(0x0e8d, 0x7902, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)MT7902_FIRMWARE_WM }, + { USB_DEVICE_AND_INTERFACE_INFO(0x0e8d, 0x7961, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)MT7921_FIRMWARE_WM }, + /* Comfast CF-952AX */ + { USB_DEVICE_AND_INTERFACE_INFO(0x3574, 0x6211, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)MT7921_FIRMWARE_WM }, + /* Netgear, Inc. [A8000,AXE3000] */ + { USB_DEVICE_AND_INTERFACE_INFO(0x0846, 0x9060, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)MT7921_FIRMWARE_WM }, + /* Netgear, Inc. A7500 */ + { USB_DEVICE_AND_INTERFACE_INFO(0x0846, 0x9065, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)MT7921_FIRMWARE_WM }, + /* TP-Link TXE50UH */ + { USB_DEVICE_AND_INTERFACE_INFO(0x35bc, 0x0107, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)MT7921_FIRMWARE_WM }, + { }, +}; + +static int +mt7921u_mcu_send_message(struct mt76_dev *mdev, struct sk_buff *skb, + int cmd, int *seq) +{ + struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); + u32 pad, ep; + int ret; + + ret = mt76_connac2_mcu_fill_message(mdev, skb, cmd, seq); + if (ret) + return ret; + + mdev->mcu.timeout = 3 * HZ; + + if (cmd != MCU_CMD(FW_SCATTER)) + ep = MT_EP_OUT_INBAND_CMD; + else + ep = MT_EP_OUT_AC_BE; + + mt792x_skb_add_usb_sdio_hdr(dev, skb, 0); + pad = round_up(skb->len, 4) + 4 - skb->len; + __skb_put_zero(skb, pad); + + ret = mt76u_bulk_msg(&dev->mt76, skb->data, skb->len, NULL, + 1000, ep); + dev_kfree_skb(skb); + + return ret; +} + +static int mt7921u_mcu_init(struct mt792x_dev *dev) +{ + static const struct mt76_mcu_ops mcu_ops = { + .headroom = MT_SDIO_HDR_SIZE + + sizeof(struct mt76_connac2_mcu_txd), + .tailroom = MT_USB_TAIL_SIZE, + .mcu_skb_send_msg = mt7921u_mcu_send_message, + .mcu_parse_response = mt7921_mcu_parse_response, + }; + int ret; + + dev->mt76.mcu_ops = &mcu_ops; + + mt76_set(dev, MT_UDMA_TX_QSEL, MT_FW_DL_EN); + ret = mt7921_run_firmware(dev); + if (ret) + return ret; + + set_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state); + mt76_clear(dev, MT_UDMA_TX_QSEL, MT_FW_DL_EN); + + return 0; +} + +static int mt7921u_mac_reset(struct mt792x_dev *dev) +{ + int err; + + mt792xu_reset_on_bus_error(dev); + if (atomic_read(&dev->mt76.bus_hung)) + return 0; + + mt76_txq_schedule_all(&dev->mphy); + mt76_worker_disable(&dev->mt76.tx_worker); + + set_bit(MT76_RESET, &dev->mphy.state); + set_bit(MT76_MCU_RESET, &dev->mphy.state); + + wake_up(&dev->mt76.mcu.wait); + skb_queue_purge(&dev->mt76.mcu.res_q); + + mt76u_stop_rx(&dev->mt76); + mt76u_stop_tx(&dev->mt76); + + mt792xu_wfsys_reset(dev); + + clear_bit(MT76_MCU_RESET, &dev->mphy.state); + err = mt76u_resume_rx(&dev->mt76); + if (err) + goto out; + + err = mt792xu_mcu_power_on(dev); + if (err) + goto out; + + err = mt792xu_dma_init(dev, false); + if (err) + goto out; + + mt76_wr(dev, MT_SWDEF_MODE, MT_SWDEF_NORMAL_MODE); + mt76_set(dev, MT_UDMA_TX_QSEL, MT_FW_DL_EN); + + err = mt7921_run_firmware(dev); + if (err) + goto out; + + mt76_clear(dev, MT_UDMA_TX_QSEL, MT_FW_DL_EN); + + err = mt7921_mcu_set_eeprom(dev); + if (err) + goto out; + + err = mt7921_mac_init(dev); + if (err) + goto out; + + err = __mt7921_start(&dev->phy); +out: + clear_bit(MT76_RESET, &dev->mphy.state); + + mt76_worker_enable(&dev->mt76.tx_worker); + + return err; +} + +static int mt7921u_probe(struct usb_interface *usb_intf, + const struct usb_device_id *id) +{ + static const struct mt76_driver_ops drv_ops = { + .txwi_size = MT_SDIO_TXD_SIZE, + .drv_flags = MT_DRV_RX_DMA_HDR | MT_DRV_HW_MGMT_TXQ | + MT_DRV_AMSDU_OFFLOAD, + .survey_flags = SURVEY_INFO_TIME_TX | + SURVEY_INFO_TIME_RX | + SURVEY_INFO_TIME_BSS_RX, + .tx_prepare_skb = mt7921_usb_sdio_tx_prepare_skb, + .tx_complete_skb = mt7921_usb_sdio_tx_complete_skb, + .tx_status_data = mt7921_usb_sdio_tx_status_data, + .rx_skb = mt7921_queue_rx_skb, + .rx_check = mt7921_rx_check, + .sta_add = mt7921_mac_sta_add, + .sta_event = mt7921_mac_sta_event, + .sta_remove = mt7921_mac_sta_remove, + .update_survey = mt792x_update_channel, + .set_channel = mt7921_set_channel, + }; + static const struct mt792x_hif_ops hif_ops = { + .mcu_init = mt7921u_mcu_init, + .init_reset = mt792xu_init_reset, + .reset = mt7921u_mac_reset, + }; + static struct mt76_bus_ops bus_ops = { + .rr = mt792xu_rr, + .wr = mt792xu_wr, + .rmw = mt792xu_rmw, + .read_copy = mt76u_read_copy, + .write_copy = mt792xu_copy, + .type = MT76_BUS_USB, + }; + struct usb_device *udev = interface_to_usbdev(usb_intf); + struct ieee80211_ops *ops; + struct ieee80211_hw *hw; + struct mt792x_dev *dev; + struct mt76_dev *mdev; + u8 features; + int ret; + + ops = mt792x_get_mac80211_ops(&usb_intf->dev, &mt7921_ops, + (void *)id->driver_info, &features); + if (!ops) + return -ENOMEM; + + ops->stop = mt792xu_stop; + mdev = mt76_alloc_device(&usb_intf->dev, sizeof(*dev), ops, &drv_ops); + if (!mdev) + return -ENOMEM; + + dev = container_of(mdev, struct mt792x_dev, mt76); + dev->fw_features = features; + dev->hif_ops = &hif_ops; + atomic_set(&dev->mt76.bus_hung, false); + mt792xu_reset_work_init(dev); + + usb_reset_device(udev); + + usb_set_intfdata(usb_intf, dev); + + ret = __mt76u_init(mdev, usb_intf, &bus_ops); + if (ret < 0) + goto error; + + mdev->rev = (mt76_rr(dev, MT_HW_CHIPID) << 16) | + (mt76_rr(dev, MT_HW_REV) & 0xff); + dev_dbg(mdev->dev, "ASIC revision: %04x\n", mdev->rev); + + if (mt76_get_field(dev, MT_CONN_ON_MISC, MT_TOP_MISC2_FW_N9_RDY)) { + ret = mt792xu_wfsys_reset(dev); + if (ret) + goto error; + } + + ret = mt792xu_mcu_power_on(dev); + if (ret) + goto error; + + ret = mt76u_alloc_mcu_queue(&dev->mt76); + if (ret) + goto error; + + ret = mt76u_alloc_queues(&dev->mt76); + if (ret) + goto error; + + ret = mt792xu_dma_init(dev, false); + if (ret) + goto error; + + hw = mt76_hw(dev); + /* check hw sg support in order to enable AMSDU */ + hw->max_tx_fragments = mdev->usb.sg_en ? MT_HW_TXP_MAX_BUF_NUM : 1; + + ret = mt7921_register_device(dev); + if (ret) + goto error; + + return 0; + +error: + mt76u_queues_deinit(&dev->mt76); + mt792xu_reset_work_cleanup(dev); + + usb_set_intfdata(usb_intf, NULL); + + mt76_free_device(&dev->mt76); + + return ret; +} + +#ifdef CONFIG_PM +static int mt7921u_suspend(struct usb_interface *intf, pm_message_t state) +{ + struct mt792x_dev *dev = usb_get_intfdata(intf); + struct mt76_connac_pm *pm = &dev->pm; + int err; + + pm->suspended = true; + flush_work(&dev->reset_work); + + err = mt76_connac_mcu_set_hif_suspend(&dev->mt76, true, true); + if (err) + goto failed; + + mt76u_stop_rx(&dev->mt76); + mt76u_stop_tx(&dev->mt76); + + return 0; + +failed: + pm->suspended = false; + + if (err < 0) + mt792x_reset(&dev->mt76); + + return err; +} + +static int mt7921u_resume(struct usb_interface *intf) +{ + struct mt792x_dev *dev = usb_get_intfdata(intf); + struct mt76_connac_pm *pm = &dev->pm; + bool reinit = true; + int err, i; + + for (i = 0; i < 10; i++) { + u32 val = mt76_rr(dev, MT_WF_SW_DEF_CR_USB_MCU_EVENT); + + if (!(val & MT_WF_SW_SER_TRIGGER_SUSPEND)) { + reinit = false; + break; + } + if (val & MT_WF_SW_SER_DONE_SUSPEND) { + mt76_wr(dev, MT_WF_SW_DEF_CR_USB_MCU_EVENT, 0); + break; + } + + msleep(20); + } + + if (reinit || mt792x_dma_need_reinit(dev)) { + err = mt792xu_dma_init(dev, true); + if (err) + goto failed; + } + + err = mt76u_resume_rx(&dev->mt76); + if (err < 0) + goto failed; + + err = mt76_connac_mcu_set_hif_suspend(&dev->mt76, false, true); +failed: + pm->suspended = false; + + if (err < 0) + mt792x_reset(&dev->mt76); + + return err; +} +#endif /* CONFIG_PM */ + +MODULE_DEVICE_TABLE(usb, mt7921u_device_table); +MODULE_FIRMWARE(MT7921_FIRMWARE_WM); +MODULE_FIRMWARE(MT7921_ROM_PATCH); + +static struct usb_driver mt7921u_driver = { + .name = KBUILD_MODNAME, + .id_table = mt7921u_device_table, + .probe = mt7921u_probe, + .disconnect = mt792xu_disconnect, +#ifdef CONFIG_PM + .suspend = mt7921u_suspend, + .resume = mt7921u_resume, + .reset_resume = mt7921u_resume, +#endif /* CONFIG_PM */ + .soft_unbind = 1, + .disable_hub_initiated_lpm = 1, +}; +module_usb_driver(mt7921u_driver); + +MODULE_DESCRIPTION("MediaTek MT7921U (USB) wireless driver"); +MODULE_AUTHOR("Lorenzo Bianconi "); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/Kconfig b/drivers/net/wireless/mediatek/mt76/mt7925/Kconfig new file mode 100644 index 000000000..f4f7c93c2 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7925/Kconfig @@ -0,0 +1,30 @@ +# SPDX-License-Identifier: BSD-3-Clause-Clear +config MT7925_COMMON + tristate + select MT792x_LIB + select WANT_DEV_COREDUMP + +config MT7925E + tristate "MediaTek MT7925E (PCIe) support" + select MT7925_COMMON + depends on MAC80211 + depends on PCI + help + This adds support for MT7925-based wireless PCIe devices, + which support operation at 6GHz, 5GHz, and 2.4GHz IEEE 802.11be + 2x2:2SS 4096-QAM, 160MHz channels. + + To compile this driver as a module, choose M here. + +config MT7925U + tristate "MediaTek MT7925U (USB) support" + select MT792x_USB + select MT7925_COMMON + depends on MAC80211 + depends on USB + help + This adds support for MT7925-based wireless USB devices, + which support operation at 6GHz, 5GHz, and 2.4GHz IEEE 802.11be + 2x2:2SS 4096-QAM, 160MHz channels. + + To compile this driver as a module, choose M here. diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/Makefile b/drivers/net/wireless/mediatek/mt76/mt7925/Makefile new file mode 100644 index 000000000..f9dcc0bba --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7925/Makefile @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: BSD-3-Clause-Clear + +obj-$(CONFIG_MT7925_COMMON) += mt7925-common.o +obj-$(CONFIG_MT7925E) += mt7925e.o +obj-$(CONFIG_MT7925U) += mt7925u.o + +mt7925-common-y := mac.o mcu.o regd.o main.o init.o debugfs.o nan.o +mt7925-common-$(CONFIG_NL80211_TESTMODE) += testmode.o +mt7925e-y := pci.o pci_mac.o pci_mcu.o +mt7925u-y := usb.o diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c new file mode 100644 index 000000000..d01ff49de --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c @@ -0,0 +1,317 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2023 MediaTek Inc. */ + +#include "mt7925.h" +#include "mcu.h" + +static int +mt7925_reg_set(void *data, u64 val) +{ + struct mt792x_dev *dev = data; + u32 regval = val; + + mt792x_mutex_acquire(dev); + mt7925_mcu_regval(dev, dev->mt76.debugfs_reg, ®val, true); + mt792x_mutex_release(dev); + + return 0; +} + +static int +mt7925_reg_get(void *data, u64 *val) +{ + struct mt792x_dev *dev = data; + u32 regval; + int ret; + + mt792x_mutex_acquire(dev); + ret = mt7925_mcu_regval(dev, dev->mt76.debugfs_reg, ®val, false); + mt792x_mutex_release(dev); + if (!ret) + *val = regval; + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_regval, mt7925_reg_get, mt7925_reg_set, + "0x%08llx\n"); +static int +mt7925_fw_debug_set(void *data, u64 val) +{ + struct mt792x_dev *dev = data; + + mt792x_mutex_acquire(dev); + + dev->fw_debug = (u8)val; + mt7925_mcu_fw_log_2_host(dev, dev->fw_debug); + + mt792x_mutex_release(dev); + + return 0; +} + +static int +mt7925_fw_debug_get(void *data, u64 *val) +{ + struct mt792x_dev *dev = data; + + *val = dev->fw_debug; + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_fw_debug, mt7925_fw_debug_get, + mt7925_fw_debug_set, "%lld\n"); + +DEFINE_SHOW_ATTRIBUTE(mt792x_tx_stats); + +static void +mt7925_seq_puts_array(struct seq_file *file, const char *str, + s8 val[][2], int len, u8 band_idx) +{ + int i; + + seq_printf(file, "%-22s:", str); + for (i = 0; i < len; i++) + if (val[i][band_idx] == 127) + seq_printf(file, " %6s", "N.A"); + else + seq_printf(file, " %6d", val[i][band_idx]); + seq_puts(file, "\n"); +} + +#define mt7925_print_txpwr_entry(prefix, rate, idx) \ +({ \ + mt7925_seq_puts_array(s, #prefix " (tmac)", \ + txpwr->rate, \ + ARRAY_SIZE(txpwr->rate), \ + idx); \ +}) + +static inline void +mt7925_eht_txpwr(struct seq_file *s, struct mt7925_txpwr *txpwr, u8 band_idx) +{ + seq_printf(s, "%-22s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s\n", + " ", "mcs0", "mcs1", "mcs2", "mcs3", "mcs4", "mcs5", + "mcs6", "mcs7", "mcs8", "mcs9", "mcs10", "mcs11", + "mcs12", "mcs13", "mcs14", "mcs15"); + mt7925_print_txpwr_entry(EHT26, eht26, band_idx); + mt7925_print_txpwr_entry(EHT52, eht52, band_idx); + mt7925_print_txpwr_entry(EHT106, eht106, band_idx); + mt7925_print_txpwr_entry(EHT242, eht242, band_idx); + mt7925_print_txpwr_entry(EHT484, eht484, band_idx); + + mt7925_print_txpwr_entry(EHT996, eht996, band_idx); + mt7925_print_txpwr_entry(EHT996x2, eht996x2, band_idx); + mt7925_print_txpwr_entry(EHT996x4, eht996x4, band_idx); + mt7925_print_txpwr_entry(EHT26_52, eht26_52, band_idx); + mt7925_print_txpwr_entry(EHT26_106, eht26_106, band_idx); + mt7925_print_txpwr_entry(EHT484_242, eht484_242, band_idx); + mt7925_print_txpwr_entry(EHT996_484, eht996_484, band_idx); + mt7925_print_txpwr_entry(EHT996_484_242, eht996_484_242, band_idx); + mt7925_print_txpwr_entry(EHT996x2_484, eht996x2_484, band_idx); + mt7925_print_txpwr_entry(EHT996x3, eht996x3, band_idx); + mt7925_print_txpwr_entry(EHT996x3_484, eht996x3_484, band_idx); +} + +static int +mt7925_txpwr(struct seq_file *s, void *data) +{ + struct mt792x_dev *dev = dev_get_drvdata(s->private); + struct mt7925_txpwr *txpwr = NULL; + u8 band_idx = dev->mphy.band_idx; + int ret = 0; + + txpwr = devm_kmalloc(dev->mt76.dev, sizeof(*txpwr), GFP_KERNEL); + + if (!txpwr) + return -ENOMEM; + + mt792x_mutex_acquire(dev); + ret = mt7925_get_txpwr_info(dev, band_idx, txpwr); + mt792x_mutex_release(dev); + + if (ret) + goto out; + + seq_printf(s, "%-22s %6s %6s %6s %6s\n", + " ", "1m", "2m", "5m", "11m"); + mt7925_print_txpwr_entry(CCK, cck, band_idx); + + seq_printf(s, "%-22s %6s %6s %6s %6s %6s %6s %6s %6s\n", + " ", "6m", "9m", "12m", "18m", "24m", "36m", + "48m", "54m"); + mt7925_print_txpwr_entry(OFDM, ofdm, band_idx); + + seq_printf(s, "%-22s %6s %6s %6s %6s %6s %6s %6s %6s\n", + " ", "mcs0", "mcs1", "mcs2", "mcs3", "mcs4", "mcs5", + "mcs6", "mcs7"); + mt7925_print_txpwr_entry(HT20, ht20, band_idx); + + seq_printf(s, "%-22s %6s %6s %6s %6s %6s %6s %6s %6s %6s\n", + " ", "mcs0", "mcs1", "mcs2", "mcs3", "mcs4", "mcs5", + "mcs6", "mcs7", "mcs32"); + mt7925_print_txpwr_entry(HT40, ht40, band_idx); + + seq_printf(s, "%-22s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s %6s\n", + " ", "mcs0", "mcs1", "mcs2", "mcs3", "mcs4", "mcs5", + "mcs6", "mcs7", "mcs8", "mcs9", "mcs10", "mcs11"); + mt7925_print_txpwr_entry(VHT20, vht20, band_idx); + mt7925_print_txpwr_entry(VHT40, vht40, band_idx); + + mt7925_print_txpwr_entry(VHT80, vht80, band_idx); + mt7925_print_txpwr_entry(VHT160, vht160, band_idx); + + mt7925_print_txpwr_entry(HE26, he26, band_idx); + mt7925_print_txpwr_entry(HE52, he52, band_idx); + mt7925_print_txpwr_entry(HE106, he106, band_idx); + mt7925_print_txpwr_entry(HE242, he242, band_idx); + mt7925_print_txpwr_entry(HE484, he484, band_idx); + + mt7925_print_txpwr_entry(HE996, he996, band_idx); + mt7925_print_txpwr_entry(HE996x2, he996x2, band_idx); + + mt7925_eht_txpwr(s, txpwr, band_idx); + +out: + devm_kfree(dev->mt76.dev, txpwr); + return ret; +} + +static int +mt7925_pm_set(void *data, u64 val) +{ + struct mt792x_dev *dev = data; + struct mt76_connac_pm *pm = &dev->pm; + + if (mt76_is_usb(&dev->mt76)) + return -EOPNOTSUPP; + + mutex_lock(&dev->mt76.mutex); + + if (val == pm->enable_user) + goto out; + + if (!pm->enable_user) { + pm->stats.last_wake_event = jiffies; + pm->stats.last_doze_event = jiffies; + } + /* make sure the chip is awake here and ps_work is scheduled + * just at end of the this routine. + */ + pm->enable = false; + mt76_connac_pm_wake(&dev->mphy, pm); + + pm->enable_user = val; + mt7925_set_runtime_pm(dev); + mt76_connac_power_save_sched(&dev->mphy, pm); +out: + mutex_unlock(&dev->mt76.mutex); + + return 0; +} + +static int +mt7925_pm_get(void *data, u64 *val) +{ + struct mt792x_dev *dev = data; + + *val = dev->pm.enable_user; + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_pm, mt7925_pm_get, mt7925_pm_set, "%lld\n"); + +static int +mt7925_deep_sleep_set(void *data, u64 val) +{ + struct mt792x_dev *dev = data; + struct mt76_connac_pm *pm = &dev->pm; + bool monitor = !!(dev->mphy.hw->conf.flags & IEEE80211_CONF_MONITOR); + bool enable = !!val; + + if (mt76_is_usb(&dev->mt76)) + return -EOPNOTSUPP; + + mt792x_mutex_acquire(dev); + if (pm->ds_enable_user == enable) + goto out; + + pm->ds_enable_user = enable; + pm->ds_enable = enable && !monitor; + mt7925_mcu_set_deep_sleep(dev, pm->ds_enable); +out: + mt792x_mutex_release(dev); + + return 0; +} + +static int +mt7925_deep_sleep_get(void *data, u64 *val) +{ + struct mt792x_dev *dev = data; + + *val = dev->pm.ds_enable_user; + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_ds, mt7925_deep_sleep_get, + mt7925_deep_sleep_set, "%lld\n"); + +DEFINE_DEBUGFS_ATTRIBUTE(fops_pm_idle_timeout, mt792x_pm_idle_timeout_get, + mt792x_pm_idle_timeout_set, "%lld\n"); + +static int mt7925_chip_reset(void *data, u64 val) +{ + struct mt792x_dev *dev = data; + int ret = 0; + + switch (val) { + case 1: + /* Reset wifisys directly. */ + mt792x_reset(&dev->mt76); + break; + default: + /* Collect the core dump before reset wifisys. */ + mt792x_mutex_acquire(dev); + ret = mt7925_mcu_chip_config(dev, "assert"); + mt792x_mutex_release(dev); + break; + } + + return ret; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_reset, NULL, mt7925_chip_reset, "%lld\n"); + +int mt7925_init_debugfs(struct mt792x_dev *dev) +{ + struct dentry *dir; + + dir = mt76_register_debugfs_fops(&dev->mphy, &fops_regval); + + if (mt76_is_mmio(&dev->mt76)) + debugfs_create_devm_seqfile(dev->mt76.dev, "xmit-queues", + dir, mt792x_queues_read); + else + debugfs_create_devm_seqfile(dev->mt76.dev, "xmit-queues", + dir, mt76_queues_read); + + debugfs_create_devm_seqfile(dev->mt76.dev, "acq", dir, + mt792x_queues_acq); + debugfs_create_devm_seqfile(dev->mt76.dev, "txpower_sku", dir, + mt7925_txpwr); + debugfs_create_file("tx_stats", 0400, dir, dev, &mt792x_tx_stats_fops); + debugfs_create_file("fw_debug", 0600, dir, dev, &fops_fw_debug); + debugfs_create_file("runtime-pm", 0600, dir, dev, &fops_pm); + debugfs_create_file("idle-timeout", 0600, dir, dev, + &fops_pm_idle_timeout); + debugfs_create_file("chip_reset", 0600, dir, dev, &fops_reset); + debugfs_create_devm_seqfile(dev->mt76.dev, "runtime_pm_stats", dir, + mt792x_pm_stats); + debugfs_create_file("deep-sleep", 0600, dir, dev, &fops_ds); + + return 0; +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/init.c b/drivers/net/wireless/mediatek/mt76/mt7925/init.c new file mode 100644 index 000000000..cd22fcc02 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7925/init.c @@ -0,0 +1,330 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2023 MediaTek Inc. */ + +#include +#include +#include +#include +#include +#include "mt7925.h" +#include "regd.h" +#include "mac.h" +#include "mcu.h" + +static ssize_t mt7925_thermal_temp_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + switch (to_sensor_dev_attr(attr)->index) { + case 0: { + struct mt792x_phy *phy = dev_get_drvdata(dev); + struct mt792x_dev *mdev = phy->dev; + int temperature; + + mt792x_mutex_acquire(mdev); + temperature = mt7925_mcu_get_temperature(phy); + mt792x_mutex_release(mdev); + + if (temperature < 0) + return temperature; + /* display in millidegree Celsius */ + return sprintf(buf, "%u\n", temperature * 1000); + } + default: + return -EINVAL; + } +} +static SENSOR_DEVICE_ATTR_RO(temp1_input, mt7925_thermal_temp, 0); + +static struct attribute *mt7925_hwmon_attrs[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + NULL, +}; +ATTRIBUTE_GROUPS(mt7925_hwmon); + +static int mt7925_thermal_init(struct mt792x_phy *phy) +{ + struct wiphy *wiphy = phy->mt76->hw->wiphy; + struct device *hwmon; + const char *name; + + if (!IS_REACHABLE(CONFIG_HWMON)) + return 0; + + name = devm_kasprintf(&wiphy->dev, GFP_KERNEL, "mt7925_%s", + wiphy_name(wiphy)); + if (!name) + return -ENOMEM; + + hwmon = devm_hwmon_device_register_with_groups(&wiphy->dev, name, phy, + mt7925_hwmon_groups); + return PTR_ERR_OR_ZERO(hwmon); +} + +static void mt7925_mac_init_basic_rates(struct mt792x_dev *dev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(mt76_rates); i++) { + u16 rate = mt76_rates[i].hw_value; + u16 idx = MT792x_BASIC_RATES_TBL + i; + + rate = FIELD_PREP(MT_TX_RATE_MODE, rate >> 8) | + FIELD_PREP(MT_TX_RATE_IDX, rate & GENMASK(7, 0)); + mt7925_mac_set_fixed_rate_table(dev, idx, rate); + } +} + +int mt7925_mac_init(struct mt792x_dev *dev) +{ + int i; + + mt76_rmw_field(dev, MT_MDP_DCR1, MT_MDP_DCR1_MAX_RX_LEN, 1536); + /* enable hardware de-agg */ + mt76_set(dev, MT_MDP_DCR0, MT_MDP_DCR0_DAMSDU_EN); + + for (i = 0; i < MT792x_WTBL_SIZE; i++) + mt7925_mac_wtbl_update(dev, i, + MT_WTBL_UPDATE_ADM_COUNT_CLEAR); + for (i = 0; i < 2; i++) + mt792x_mac_init_band(dev, i); + + mt7925_mac_init_basic_rates(dev); + + memzero_explicit(&dev->mt76.alpha2, sizeof(dev->mt76.alpha2)); + + return 0; +} +EXPORT_SYMBOL_GPL(mt7925_mac_init); + +static int __mt7925_init_hardware(struct mt792x_dev *dev) +{ + int ret; + + ret = mt792x_mcu_init(dev); + if (ret) + goto out; + + ret = mt76_eeprom_override(&dev->mphy); + if (ret) + goto out; + + ret = mt7925_mcu_set_eeprom(dev); + if (ret) + goto out; + + ret = mt7925_mac_init(dev); + if (ret) + goto out; + + if (is_mt7927(&dev->mt76)) { + ret = mt7925_mcu_set_dbdc(&dev->mphy, true); + if (ret) { + dev_warn(dev->mt76.dev, + "MT7927 DBDC enable failed: %d\n", ret); + ret = 0; + } + } + +out: + return ret; +} + +static int mt7925_init_hardware(struct mt792x_dev *dev) +{ + int ret, i; + + set_bit(MT76_STATE_INITIALIZED, &dev->mphy.state); + + for (i = 0; i < MT792x_MCU_INIT_RETRY_COUNT; i++) { + if (atomic_read(&dev->mt76.bus_hung)) { + ret = -EIO; + break; + } + + ret = __mt7925_init_hardware(dev); + if (!ret) + break; + + if (atomic_read(&dev->mt76.bus_hung)) + break; + + mt792x_init_reset(dev); + } + + if (i == MT792x_MCU_INIT_RETRY_COUNT) { + dev_err(dev->mt76.dev, "hardware init failed\n"); + return ret; + } + + return 0; +} + +static int mt7925_init_nan_cap(struct mt76_dev *mdev) +{ + struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); + const struct ieee80211_sta_he_cap *he_cap; + struct ieee80211_supported_band *sband; + struct wiphy *wiphy = mdev->hw->wiphy; + + if (!(dev->fw_features & MT792x_FW_CAP_NAN)) + return 0; + + sband = wiphy->bands[NL80211_BAND_2GHZ]; + if (sband) + wiphy->nan_capa.phy.ht = sband->ht_cap; + + sband = wiphy->bands[NL80211_BAND_5GHZ]; + if (sband) + wiphy->nan_capa.phy.vht = sband->vht_cap; + + sband = wiphy->bands[NL80211_BAND_2GHZ]; + he_cap = sband ? ieee80211_get_he_iftype_cap(sband, NL80211_IFTYPE_NAN) + : NULL; + if (he_cap) + wiphy->nan_capa.phy.he = *he_cap; + + return 0; +} + +static void mt7925_init_work(struct work_struct *work) +{ + struct mt792x_dev *dev = container_of(work, struct mt792x_dev, + init_work); + int ret; + + ret = mt7925_init_hardware(dev); + if (ret) + return; + + mt76_set_stream_caps(&dev->mphy, true); + mt7925_set_stream_he_eht_caps(&dev->phy); + mt792x_config_mac_addr_list(dev); + + ret = mt7925_init_mlo_caps(&dev->phy); + if (ret) { + dev_err(dev->mt76.dev, "MLO init failed\n"); + return; + } + + dev->mt76.init_wiphy = mt7925_init_nan_cap; + + ret = mt76_register_device(&dev->mt76, true, mt76_rates, + ARRAY_SIZE(mt76_rates)); + if (ret) { + dev_err(dev->mt76.dev, "register device failed\n"); + return; + } + + ret = mt7925_init_debugfs(dev); + if (ret) { + dev_err(dev->mt76.dev, "register debugfs failed\n"); + return; + } + + ret = mt7925_thermal_init(&dev->phy); + if (ret) { + dev_err(dev->mt76.dev, "thermal init failed\n"); + return; + } + + ret = mt7925_mcu_set_thermal_protect(dev); + if (ret) { + dev_err(dev->mt76.dev, "thermal protection enable failed\n"); + return; + } + + /* we support chip reset now */ + dev->hw_init_done = true; + + mt7925_mcu_set_deep_sleep(dev, dev->pm.ds_enable); +} + +int mt7925_register_device(struct mt792x_dev *dev) +{ + struct ieee80211_hw *hw = mt76_hw(dev); + int ret; + + dev->phy.dev = dev; + dev->phy.mt76 = &dev->mt76.phy; + dev->mt76.phy.priv = &dev->phy; + dev->mt76.tx_worker.fn = mt792x_tx_worker; + + INIT_DELAYED_WORK(&dev->pm.ps_work, mt792x_pm_power_save_work); + INIT_DELAYED_WORK(&dev->mlo_pm_work, mt7925_mlo_pm_work); + INIT_WORK(&dev->pm.wake_work, mt792x_pm_wake_work); + spin_lock_init(&dev->pm.wake.lock); + mutex_init(&dev->pm.mutex); + init_waitqueue_head(&dev->pm.wait); + init_waitqueue_head(&dev->wait); + spin_lock_init(&dev->pm.txq_lock); + INIT_DELAYED_WORK(&dev->mphy.mac_work, mt792x_mac_work); + INIT_DELAYED_WORK(&dev->phy.scan_work, mt7925_scan_work); + INIT_DELAYED_WORK(&dev->coredump.work, mt7925_coredump_work); +#if IS_ENABLED(CONFIG_IPV6) + INIT_WORK(&dev->ipv6_ns_work, mt7925_set_ipv6_ns_work); + skb_queue_head_init(&dev->ipv6_ns_list); +#endif + skb_queue_head_init(&dev->phy.scan_event_list); + skb_queue_head_init(&dev->coredump.msg_list); + + INIT_WORK(&dev->reset_work, mt7925_mac_reset_work); + INIT_WORK(&dev->init_work, mt7925_init_work); + + INIT_WORK(&dev->phy.roc_work, mt7925_roc_work); + timer_setup(&dev->phy.roc_timer, mt792x_roc_timer, 0); + init_waitqueue_head(&dev->phy.roc_wait); + + dev->pm.idle_timeout = MT792x_PM_TIMEOUT; + dev->pm.stats.last_wake_event = jiffies; + dev->pm.stats.last_doze_event = jiffies; + /* MT7927: runtime PM crashes BT firmware on the shared CONNINFRA domain */ + if (!mt76_is_usb(&dev->mt76) && !is_mt7927(&dev->mt76)) { + dev->pm.enable_user = true; + dev->pm.enable = true; + dev->pm.ds_enable_user = true; + dev->pm.ds_enable = true; + } + + if (!mt76_is_mmio(&dev->mt76)) + hw->extra_tx_headroom += MT_SDIO_TXD_SIZE + MT_SDIO_HDR_SIZE; + + mt792x_init_acpi_sar(dev); + + ret = mt792x_init_wcid(dev); + if (ret) + return ret; + + ret = mt792x_init_wiphy(hw); + if (ret) + return ret; + + hw->wiphy->reg_notifier = mt7925_regd_notifier; + dev->mphy.sband_2g.sband.ht_cap.cap |= + IEEE80211_HT_CAP_LDPC_CODING | + IEEE80211_HT_CAP_MAX_AMSDU; + dev->mphy.sband_2g.sband.ht_cap.ampdu_density = + IEEE80211_HT_MPDU_DENSITY_2; + dev->mphy.sband_5g.sband.ht_cap.cap |= + IEEE80211_HT_CAP_LDPC_CODING | + IEEE80211_HT_CAP_MAX_AMSDU; + dev->mphy.sband_2g.sband.ht_cap.ampdu_density = + IEEE80211_HT_MPDU_DENSITY_1; + dev->mphy.sband_5g.sband.vht_cap.cap |= + IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 | + IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK | + IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE | + IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE | + (3 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT); + dev->mphy.sband_5g.sband.vht_cap.cap |= + IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ | + IEEE80211_VHT_CAP_SHORT_GI_160; + + dev->mphy.hw->wiphy->available_antennas_rx = dev->mphy.chainmask; + dev->mphy.hw->wiphy->available_antennas_tx = dev->mphy.chainmask; + + queue_work(system_percpu_wq, &dev->init_work); + + return 0; +} +EXPORT_SYMBOL_GPL(mt7925_register_device); diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mac.c b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c new file mode 100644 index 000000000..101f571b0 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mac.c @@ -0,0 +1,1737 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2023 MediaTek Inc. */ + +#include +#include +#include +#include "mt7925.h" +#include "../dma.h" +#include "regd.h" +#include "mac.h" +#include "mcu.h" + +bool mt7925_mac_wtbl_update(struct mt792x_dev *dev, int idx, u32 mask) +{ + u32 wtbl_update; + + if (is_mt7928(&dev->mt76)) + wtbl_update = MT7928_WTBL_UPDATE; + else + wtbl_update = MT7925_WTBL_UPDATE; + + mt76_rmw(dev, wtbl_update, MT_WTBL_UPDATE_WLAN_IDX, + FIELD_PREP(MT_WTBL_UPDATE_WLAN_IDX, idx) | mask); + + return mt76_poll(dev, wtbl_update, MT_WTBL_UPDATE_BUSY, + 0, 5000); +} + +static void mt7925_mac_sta_poll(struct mt792x_dev *dev) +{ + static const u8 ac_to_tid[] = { + [IEEE80211_AC_BE] = 0, + [IEEE80211_AC_BK] = 1, + [IEEE80211_AC_VI] = 4, + [IEEE80211_AC_VO] = 6 + }; + struct ieee80211_sta *sta; + struct mt792x_sta *msta; + struct mt792x_link_sta *mlink; + u32 tx_time[IEEE80211_NUM_ACS], rx_time[IEEE80211_NUM_ACS]; + LIST_HEAD(sta_poll_list); + struct rate_info *rate; + s8 rssi[4]; + int i; + + spin_lock_bh(&dev->mt76.sta_poll_lock); + list_splice_init(&dev->mt76.sta_poll_list, &sta_poll_list); + spin_unlock_bh(&dev->mt76.sta_poll_lock); + + while (true) { + bool clear = false; + u32 addr, val; + u16 idx; + u8 bw; + + if (list_empty(&sta_poll_list)) + break; + mlink = list_first_entry(&sta_poll_list, + struct mt792x_link_sta, wcid.poll_list); + msta = mlink->sta; + spin_lock_bh(&dev->mt76.sta_poll_lock); + list_del_init(&mlink->wcid.poll_list); + spin_unlock_bh(&dev->mt76.sta_poll_lock); + + idx = mlink->wcid.idx; + addr = mt7925_mac_wtbl_lmac_addr(dev, idx, MT_WTBL_AC0_CTT_OFFSET); + + for (i = 0; i < IEEE80211_NUM_ACS; i++) { + u32 tx_last = mlink->airtime_ac[i]; + u32 rx_last = mlink->airtime_ac[i + 4]; + + mlink->airtime_ac[i] = mt76_rr(dev, addr); + mlink->airtime_ac[i + 4] = mt76_rr(dev, addr + 4); + + tx_time[i] = mlink->airtime_ac[i] - tx_last; + rx_time[i] = mlink->airtime_ac[i + 4] - rx_last; + + if ((tx_last | rx_last) & BIT(30)) + clear = true; + + addr += 8; + } + + if (clear) { + mt7925_mac_wtbl_update(dev, idx, + MT_WTBL_UPDATE_ADM_COUNT_CLEAR); + memset(mlink->airtime_ac, 0, sizeof(mlink->airtime_ac)); + } + + if (!mlink->wcid.sta) + continue; + + sta = container_of((void *)msta, struct ieee80211_sta, + drv_priv); + for (i = 0; i < IEEE80211_NUM_ACS; i++) { + u8 q = mt76_connac_lmac_mapping(i); + u32 tx_cur = tx_time[q]; + u32 rx_cur = rx_time[q]; + u8 tid = ac_to_tid[i]; + + if (!tx_cur && !rx_cur) + continue; + + ieee80211_sta_register_airtime(sta, tid, tx_cur, + rx_cur); + } + + /* We don't support reading GI info from txs packets. + * For accurate tx status reporting and AQL improvement, + * we need to make sure that flags match so polling GI + * from per-sta counters directly. + */ + rate = &mlink->wcid.rate; + + switch (rate->bw) { + case RATE_INFO_BW_160: + bw = IEEE80211_STA_RX_BW_160; + break; + case RATE_INFO_BW_80: + bw = IEEE80211_STA_RX_BW_80; + break; + case RATE_INFO_BW_40: + bw = IEEE80211_STA_RX_BW_40; + break; + default: + bw = IEEE80211_STA_RX_BW_20; + break; + } + + addr = mt7925_mac_wtbl_lmac_addr(dev, idx, 6); + val = mt76_rr(dev, addr); + if (rate->flags & RATE_INFO_FLAGS_EHT_MCS) { + addr = mt7925_mac_wtbl_lmac_addr(dev, idx, 5); + val = mt76_rr(dev, addr); + rate->eht_gi = FIELD_GET(GENMASK(25, 24), val); + } else if (rate->flags & RATE_INFO_FLAGS_HE_MCS) { + u8 offs = MT_WTBL_TXRX_RATE_G2_HE + 2 * bw; + + rate->he_gi = (val & (0x3 << offs)) >> offs; + } else if (rate->flags & + (RATE_INFO_FLAGS_VHT_MCS | RATE_INFO_FLAGS_MCS)) { + if (val & BIT(MT_WTBL_TXRX_RATE_G2 + bw)) + rate->flags |= RATE_INFO_FLAGS_SHORT_GI; + else + rate->flags &= ~RATE_INFO_FLAGS_SHORT_GI; + } + + /* get signal strength of resp frames (CTS/BA/ACK) */ + addr = mt7925_mac_wtbl_lmac_addr(dev, idx, 34); + val = mt76_rr(dev, addr); + + rssi[0] = to_rssi(GENMASK(7, 0), val); + rssi[1] = to_rssi(GENMASK(15, 8), val); + rssi[2] = to_rssi(GENMASK(23, 16), val); + rssi[3] = to_rssi(GENMASK(31, 24), val); + + mlink->ack_signal = + mt76_rx_signal(msta->vif->phy->mt76->antenna_mask, rssi); + + ewma_avg_signal_add(&mlink->avg_ack_signal, -mlink->ack_signal); + } +} + +void mt7925_mac_set_fixed_rate_table(struct mt792x_dev *dev, + u8 tbl_idx, u16 rate_idx) +{ + u32 ctrl = MT_WTBL_ITCR_WR | MT_WTBL_ITCR_EXEC | tbl_idx; + + if (is_mt7928(&dev->mt76)) { + mt76_wr(dev, MT7928_WTBL_ITDR0, rate_idx); + /* use wtbl spe idx */ + mt76_wr(dev, MT7928_WTBL_ITDR1, MT_WTBL_SPE_IDX_SEL); + mt76_wr(dev, MT7928_WTBL_ITCR, ctrl); + } else { + mt76_wr(dev, MT_WTBL_ITDR0, rate_idx); + /* use wtbl spe idx */ + mt76_wr(dev, MT_WTBL_ITDR1, MT_WTBL_SPE_IDX_SEL); + mt76_wr(dev, MT_WTBL_ITCR, ctrl); + } +} + +/* The HW does not translate the mac header to 802.3 for mesh point */ +static int mt7925_reverse_frag0_hdr_trans(struct sk_buff *skb, u16 hdr_gap) +{ + struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb; + struct ethhdr *eth_hdr = (struct ethhdr *)(skb->data + hdr_gap); + struct mt792x_sta *msta = (struct mt792x_sta *)status->wcid; + __le32 *rxd = (__le32 *)skb->data; + struct ieee80211_sta *sta; + struct ieee80211_vif *vif; + struct ieee80211_hdr hdr; + u16 frame_control; + + if (le32_get_bits(rxd[3], MT_RXD3_NORMAL_ADDR_TYPE) != + MT_RXD3_NORMAL_U2M) + return -EINVAL; + + if (!(le32_to_cpu(rxd[1]) & MT_RXD1_NORMAL_GROUP_4)) + return -EINVAL; + + if (!msta || !msta->vif) + return -EINVAL; + + sta = container_of((void *)msta, struct ieee80211_sta, drv_priv); + vif = container_of((void *)msta->vif, struct ieee80211_vif, drv_priv); + + /* store the info from RXD and ethhdr to avoid being overridden */ + frame_control = le32_get_bits(rxd[8], MT_RXD8_FRAME_CONTROL); + hdr.frame_control = cpu_to_le16(frame_control); + hdr.seq_ctrl = cpu_to_le16(le32_get_bits(rxd[10], MT_RXD10_SEQ_CTRL)); + hdr.duration_id = 0; + + ether_addr_copy(hdr.addr1, vif->addr); + ether_addr_copy(hdr.addr2, sta->addr); + switch (frame_control & (IEEE80211_FCTL_TODS | + IEEE80211_FCTL_FROMDS)) { + case 0: + ether_addr_copy(hdr.addr3, vif->bss_conf.bssid); + break; + case IEEE80211_FCTL_FROMDS: + ether_addr_copy(hdr.addr3, eth_hdr->h_source); + break; + case IEEE80211_FCTL_TODS: + ether_addr_copy(hdr.addr3, eth_hdr->h_dest); + break; + case IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS: + ether_addr_copy(hdr.addr3, eth_hdr->h_dest); + ether_addr_copy(hdr.addr4, eth_hdr->h_source); + break; + default: + break; + } + + skb_pull(skb, hdr_gap + sizeof(struct ethhdr) - 2); + if (eth_hdr->h_proto == cpu_to_be16(ETH_P_AARP) || + eth_hdr->h_proto == cpu_to_be16(ETH_P_IPX)) + ether_addr_copy(skb_push(skb, ETH_ALEN), bridge_tunnel_header); + else if (be16_to_cpu(eth_hdr->h_proto) >= ETH_P_802_3_MIN) + ether_addr_copy(skb_push(skb, ETH_ALEN), rfc1042_header); + else + skb_pull(skb, 2); + + if (ieee80211_has_order(hdr.frame_control)) + memcpy(skb_push(skb, IEEE80211_HT_CTL_LEN), &rxd[11], + IEEE80211_HT_CTL_LEN); + if (ieee80211_is_data_qos(hdr.frame_control)) { + __le16 qos_ctrl; + + qos_ctrl = cpu_to_le16(le32_get_bits(rxd[10], MT_RXD10_QOS_CTL)); + memcpy(skb_push(skb, IEEE80211_QOS_CTL_LEN), &qos_ctrl, + IEEE80211_QOS_CTL_LEN); + } + + if (ieee80211_has_a4(hdr.frame_control)) + memcpy(skb_push(skb, sizeof(hdr)), &hdr, sizeof(hdr)); + else + memcpy(skb_push(skb, sizeof(hdr) - 6), &hdr, sizeof(hdr) - 6); + + return 0; +} + +static int +mt7925_mac_fill_rx_rate(struct mt792x_dev *dev, + struct mt76_rx_status *status, + struct ieee80211_supported_band *sband, + __le32 *rxv, u8 *mode) +{ + u32 v0, v2; + u8 stbc, gi, bw, dcm, nss; + int i, idx; + bool cck = false; + + v0 = le32_to_cpu(rxv[0]); + v2 = le32_to_cpu(rxv[2]); + + idx = FIELD_GET(MT_PRXV_TX_RATE, v0); + i = idx; + nss = FIELD_GET(MT_PRXV_NSTS, v0) + 1; + + stbc = FIELD_GET(MT_PRXV_HT_STBC, v2); + gi = FIELD_GET(MT_PRXV_HT_SHORT_GI, v2); + *mode = FIELD_GET(MT_PRXV_TX_MODE, v2); + dcm = FIELD_GET(MT_PRXV_DCM, v2); + bw = FIELD_GET(MT_PRXV_FRAME_MODE, v2); + + switch (*mode) { + case MT_PHY_TYPE_CCK: + cck = true; + fallthrough; + case MT_PHY_TYPE_OFDM: + i = mt76_get_rate(&dev->mt76, sband, i, cck); + break; + case MT_PHY_TYPE_HT_GF: + case MT_PHY_TYPE_HT: + status->encoding = RX_ENC_HT; + if (gi) + status->enc_flags |= RX_ENC_FLAG_SHORT_GI; + if (i > 31) + return -EINVAL; + break; + case MT_PHY_TYPE_VHT: + status->nss = nss; + status->encoding = RX_ENC_VHT; + if (gi) + status->enc_flags |= RX_ENC_FLAG_SHORT_GI; + if (i > 11) + return -EINVAL; + break; + case MT_PHY_TYPE_HE_MU: + case MT_PHY_TYPE_HE_SU: + case MT_PHY_TYPE_HE_EXT_SU: + case MT_PHY_TYPE_HE_TB: + status->nss = nss; + status->encoding = RX_ENC_HE; + i &= GENMASK(3, 0); + + if (gi <= NL80211_RATE_INFO_HE_GI_3_2) + status->he_gi = gi; + + status->he_dcm = dcm; + break; + case MT_PHY_TYPE_EHT_SU: + case MT_PHY_TYPE_EHT_TRIG: + case MT_PHY_TYPE_EHT_MU: + status->nss = nss; + status->encoding = RX_ENC_EHT; + i &= GENMASK(3, 0); + + if (gi <= NL80211_RATE_INFO_EHT_GI_3_2) + status->eht.gi = gi; + break; + default: + return -EINVAL; + } + status->rate_idx = i; + + switch (bw) { + case IEEE80211_STA_RX_BW_20: + break; + case IEEE80211_STA_RX_BW_40: + if (*mode & MT_PHY_TYPE_HE_EXT_SU && + (idx & MT_PRXV_TX_ER_SU_106T)) { + status->bw = RATE_INFO_BW_HE_RU; + status->he_ru = + NL80211_RATE_INFO_HE_RU_ALLOC_106; + } else { + status->bw = RATE_INFO_BW_40; + } + break; + case IEEE80211_STA_RX_BW_80: + status->bw = RATE_INFO_BW_80; + break; + case IEEE80211_STA_RX_BW_160: + status->bw = RATE_INFO_BW_160; + break; + /* RXV can report 320 in two positions */ + case IEEE80211_STA_RX_BW_320: + case IEEE80211_STA_RX_BW_320 + 1: + status->bw = RATE_INFO_BW_320; + break; + default: + return -EINVAL; + } + + status->enc_flags |= RX_ENC_FLAG_STBC_MASK * stbc; + if (*mode < MT_PHY_TYPE_HE_SU && gi) + status->enc_flags |= RX_ENC_FLAG_SHORT_GI; + + return 0; +} + +static int +mt7925_mac_fill_rx(struct mt792x_dev *dev, struct sk_buff *skb) +{ + u32 csum_mask = MT_RXD3_NORMAL_IP_SUM | MT_RXD3_NORMAL_UDP_TCP_SUM; + struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb; + bool hdr_trans, unicast, insert_ccmp_hdr = false; + u8 chfreq, qos_ctl = 0, remove_pad, amsdu_info; + u16 hdr_gap; + __le32 *rxv = NULL, *rxd = (__le32 *)skb->data; + struct mt76_phy *mphy = &dev->mt76.phy; + struct mt792x_phy *phy = &dev->phy; + struct ieee80211_supported_band *sband; + u32 csum_status = *(u32 *)skb->cb; + u32 rxd1 = le32_to_cpu(rxd[1]); + u32 rxd2 = le32_to_cpu(rxd[2]); + u32 rxd3 = le32_to_cpu(rxd[3]); + u32 rxd4 = le32_to_cpu(rxd[4]); + struct mt792x_link_sta *mlink; + u8 mode = 0; /* , band_idx; */ + u16 seq_ctrl = 0; + __le16 fc = 0; + int idx; + + memset(status, 0, sizeof(*status)); + + if (!test_bit(MT76_STATE_RUNNING, &mphy->state)) + return -EINVAL; + + if (rxd2 & MT_RXD2_NORMAL_AMSDU_ERR) + return -EINVAL; + + hdr_trans = rxd2 & MT_RXD2_NORMAL_HDR_TRANS; + if (hdr_trans && (rxd1 & MT_RXD1_NORMAL_CM)) + return -EINVAL; + + /* ICV error or CCMP/BIP/WPI MIC error */ + if (rxd1 & MT_RXD1_NORMAL_ICV_ERR) + status->flag |= RX_FLAG_ONLY_MONITOR; + + chfreq = FIELD_GET(MT_RXD3_NORMAL_CH_FREQ, rxd3); + unicast = FIELD_GET(MT_RXD3_NORMAL_ADDR_TYPE, rxd3) == MT_RXD3_NORMAL_U2M; + idx = FIELD_GET(MT_RXD1_NORMAL_WLAN_IDX, rxd1); + status->wcid = mt792x_rx_get_wcid(dev, idx, unicast); + + if (status->wcid) { + mlink = container_of(status->wcid, struct mt792x_link_sta, wcid); + mt76_wcid_add_poll(&dev->mt76, &mlink->wcid); + } + + mt792x_get_status_freq_info(status, chfreq); + + switch (status->band) { + case NL80211_BAND_5GHZ: + sband = &mphy->sband_5g.sband; + break; + case NL80211_BAND_6GHZ: + sband = &mphy->sband_6g.sband; + break; + default: + sband = &mphy->sband_2g.sband; + break; + } + + if (!sband->channels) + return -EINVAL; + + if (mt76_is_mmio(&dev->mt76) && (rxd3 & csum_mask) == csum_mask && + !(csum_status & (BIT(0) | BIT(2) | BIT(3)))) + skb->ip_summed = CHECKSUM_UNNECESSARY; + + if (rxd3 & MT_RXD3_NORMAL_FCS_ERR) + status->flag |= RX_FLAG_FAILED_FCS_CRC; + + if (rxd1 & MT_RXD1_NORMAL_TKIP_MIC_ERR) + status->flag |= RX_FLAG_MMIC_ERROR; + + if (FIELD_GET(MT_RXD2_NORMAL_SEC_MODE, rxd2) != 0 && + !(rxd1 & (MT_RXD1_NORMAL_CLM | MT_RXD1_NORMAL_CM))) { + status->flag |= RX_FLAG_DECRYPTED; + status->flag |= RX_FLAG_IV_STRIPPED; + status->flag |= RX_FLAG_MMIC_STRIPPED | RX_FLAG_MIC_STRIPPED; + } + + remove_pad = FIELD_GET(MT_RXD2_NORMAL_HDR_OFFSET, rxd2); + + if (rxd2 & MT_RXD2_NORMAL_MAX_LEN_ERROR) + return -EINVAL; + + rxd += 8; + if (rxd1 & MT_RXD1_NORMAL_GROUP_4) { + u32 v0 = le32_to_cpu(rxd[0]); + u32 v2 = le32_to_cpu(rxd[2]); + + /* TODO: need to map rxd address */ + fc = cpu_to_le16(FIELD_GET(MT_RXD8_FRAME_CONTROL, v0)); + seq_ctrl = FIELD_GET(MT_RXD10_SEQ_CTRL, v2); + qos_ctl = FIELD_GET(MT_RXD10_QOS_CTL, v2); + + rxd += 4; + if ((u8 *)rxd - skb->data >= skb->len) + return -EINVAL; + } + + if (rxd1 & MT_RXD1_NORMAL_GROUP_1) { + u8 *data = (u8 *)rxd; + + if (status->flag & RX_FLAG_DECRYPTED) { + switch (FIELD_GET(MT_RXD2_NORMAL_SEC_MODE, rxd2)) { + case MT_CIPHER_AES_CCMP: + case MT_CIPHER_CCMP_CCX: + case MT_CIPHER_CCMP_256: + insert_ccmp_hdr = + FIELD_GET(MT_RXD2_NORMAL_FRAG, rxd2); + fallthrough; + case MT_CIPHER_TKIP: + case MT_CIPHER_TKIP_NO_MIC: + case MT_CIPHER_GCMP: + case MT_CIPHER_GCMP_256: + status->iv[0] = data[5]; + status->iv[1] = data[4]; + status->iv[2] = data[3]; + status->iv[3] = data[2]; + status->iv[4] = data[1]; + status->iv[5] = data[0]; + break; + default: + break; + } + } + rxd += 4; + if ((u8 *)rxd - skb->data >= skb->len) + return -EINVAL; + } + + if (rxd1 & MT_RXD1_NORMAL_GROUP_2) { + status->timestamp = le32_to_cpu(rxd[0]); + status->flag |= RX_FLAG_MACTIME_START; + + if (!(rxd2 & MT_RXD2_NORMAL_NON_AMPDU)) { + status->flag |= RX_FLAG_AMPDU_DETAILS; + + /* all subframes of an A-MPDU have the same timestamp */ + if (phy->rx_ampdu_ts != status->timestamp) { + if (!++phy->ampdu_ref) + phy->ampdu_ref++; + } + phy->rx_ampdu_ts = status->timestamp; + + status->ampdu_ref = phy->ampdu_ref; + } + + rxd += 4; + if ((u8 *)rxd - skb->data >= skb->len) + return -EINVAL; + } + + /* RXD Group 3 - P-RXV */ + if (rxd1 & MT_RXD1_NORMAL_GROUP_3) { + u32 v3; + int ret; + + rxv = rxd; + rxd += 4; + if ((u8 *)rxd - skb->data >= skb->len) + return -EINVAL; + + v3 = le32_to_cpu(rxv[3]); + + status->chains = mphy->antenna_mask; + status->chain_signal[0] = to_rssi(MT_PRXV_RCPI0, v3); + status->chain_signal[1] = to_rssi(MT_PRXV_RCPI1, v3); + status->chain_signal[2] = to_rssi(MT_PRXV_RCPI2, v3); + status->chain_signal[3] = to_rssi(MT_PRXV_RCPI3, v3); + + /* RXD Group 5 - C-RXV */ + if (rxd1 & MT_RXD1_NORMAL_GROUP_5) { + rxd += 24; + if ((u8 *)rxd - skb->data >= skb->len) + return -EINVAL; + } + + ret = mt7925_mac_fill_rx_rate(dev, status, sband, rxv, &mode); + if (ret < 0) + return ret; + } + + amsdu_info = FIELD_GET(MT_RXD4_NORMAL_PAYLOAD_FORMAT, rxd4); + status->amsdu = !!amsdu_info; + if (status->amsdu) { + status->first_amsdu = amsdu_info == MT_RXD4_FIRST_AMSDU_FRAME; + status->last_amsdu = amsdu_info == MT_RXD4_LAST_AMSDU_FRAME; + } + + hdr_gap = (u8 *)rxd - skb->data + 2 * remove_pad; + if (hdr_trans && ieee80211_has_morefrags(fc)) { + if (mt7925_reverse_frag0_hdr_trans(skb, hdr_gap)) + return -EINVAL; + hdr_trans = false; + } else { + int pad_start = 0; + + skb_pull(skb, hdr_gap); + if (!hdr_trans && status->amsdu) { + pad_start = ieee80211_get_hdrlen_from_skb(skb); + } else if (hdr_trans && (rxd2 & MT_RXD2_NORMAL_HDR_TRANS_ERROR)) { + /* When header translation failure is indicated, + * the hardware will insert an extra 2-byte field + * containing the data length after the protocol + * type field. + */ + pad_start = 12; + if (get_unaligned_be16(skb->data + pad_start) == ETH_P_8021Q) + pad_start += 4; + else + pad_start = 0; + } + + if (pad_start) { + memmove(skb->data + 2, skb->data, pad_start); + skb_pull(skb, 2); + } + } + + if (!hdr_trans) { + struct ieee80211_hdr *hdr; + + if (insert_ccmp_hdr) { + u8 key_id = FIELD_GET(MT_RXD1_NORMAL_KEY_ID, rxd1); + + mt76_insert_ccmp_hdr(skb, key_id); + } + + hdr = mt76_skb_get_hdr(skb); + fc = hdr->frame_control; + if (ieee80211_is_data_qos(fc)) { + seq_ctrl = le16_to_cpu(hdr->seq_ctrl); + qos_ctl = *ieee80211_get_qos_ctl(hdr); + } + skb_set_mac_header(skb, (unsigned char *)hdr - skb->data); + } else { + status->flag |= RX_FLAG_8023; + } + + mt792x_mac_assoc_rssi(dev, skb); + + if (rxv && !(status->flag & RX_FLAG_8023)) { + switch (status->encoding) { + case RX_ENC_EHT: + mt76_connac3_mac_decode_eht_radiotap(skb, rxv, mode); + break; + case RX_ENC_HE: + mt76_connac3_mac_decode_he_radiotap(skb, rxv, mode); + break; + default: + break; + } + } + + if (!status->wcid || !ieee80211_is_data_qos(fc)) + return 0; + + status->aggr = unicast && !ieee80211_is_qos_nullfunc(fc); + status->seqno = IEEE80211_SEQ_TO_SN(seq_ctrl); + status->qos_ctl = qos_ctl; + + return 0; +} + +static void +mt7925_mac_write_txwi_8023(__le32 *txwi, struct sk_buff *skb, + struct mt76_wcid *wcid) +{ + u8 tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK; + u8 fc_type, fc_stype; + u16 ethertype; + bool wmm = false; + u32 val; + + if (wcid->sta) { + struct ieee80211_sta *sta; + + sta = container_of((void *)wcid, struct ieee80211_sta, drv_priv); + wmm = sta->wme; + } + + val = FIELD_PREP(MT_TXD1_HDR_FORMAT, MT_HDR_FORMAT_802_3) | + FIELD_PREP(MT_TXD1_TID, tid); + + ethertype = get_unaligned_be16(&skb->data[12]); + if (ethertype >= ETH_P_802_3_MIN) + val |= MT_TXD1_ETH_802_3; + + txwi[1] |= cpu_to_le32(val); + + fc_type = IEEE80211_FTYPE_DATA >> 2; + fc_stype = wmm ? IEEE80211_STYPE_QOS_DATA >> 4 : 0; + + val = FIELD_PREP(MT_TXD2_FRAME_TYPE, fc_type) | + FIELD_PREP(MT_TXD2_SUB_TYPE, fc_stype); + + txwi[2] |= cpu_to_le32(val); +} + +static void +mt7925_mac_write_txwi_80211(struct mt76_dev *dev, __le32 *txwi, + struct sk_buff *skb, + struct ieee80211_key_conf *key) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + bool multicast = is_multicast_ether_addr(hdr->addr1); + u8 tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK; + __le16 fc = hdr->frame_control; + u8 fc_type, fc_stype; + u32 val; + + if (ieee80211_is_action(fc) && + skb->len >= IEEE80211_MIN_ACTION_SIZE(action_code) && + mgmt->u.action.category == WLAN_CATEGORY_BACK && + mgmt->u.action.action_code == WLAN_ACTION_ADDBA_REQ) + tid = MT_TX_ADDBA; + else if (ieee80211_is_mgmt(hdr->frame_control)) + tid = MT_TX_NORMAL; + + val = FIELD_PREP(MT_TXD1_HDR_FORMAT, MT_HDR_FORMAT_802_11) | + FIELD_PREP(MT_TXD1_HDR_INFO, + ieee80211_get_hdrlen_from_skb(skb) / 2) | + FIELD_PREP(MT_TXD1_TID, tid); + + if (!ieee80211_is_data(fc) || multicast || + info->flags & IEEE80211_TX_CTL_USE_MINRATE) + val |= MT_TXD1_FIXED_RATE; + + if (key && multicast && ieee80211_is_robust_mgmt_frame(skb) && + key->cipher == WLAN_CIPHER_SUITE_AES_CMAC) { + val |= MT_TXD1_BIP; + txwi[3] &= ~cpu_to_le32(MT_TXD3_PROTECT_FRAME); + } + + txwi[1] |= cpu_to_le32(val); + + fc_type = (le16_to_cpu(fc) & IEEE80211_FCTL_FTYPE) >> 2; + fc_stype = (le16_to_cpu(fc) & IEEE80211_FCTL_STYPE) >> 4; + + val = FIELD_PREP(MT_TXD2_FRAME_TYPE, fc_type) | + FIELD_PREP(MT_TXD2_SUB_TYPE, fc_stype); + + txwi[2] |= cpu_to_le32(val); + + if (is_mt7928(dev) && ieee80211_is_mgmt(hdr->frame_control)) + txwi[3] &= ~cpu_to_le32(MT_TXD3_HW_AMSDU); + + txwi[3] |= cpu_to_le32(FIELD_PREP(MT_TXD3_BCM, multicast)); + if (ieee80211_is_beacon(fc)) + txwi[3] |= cpu_to_le32(MT_TXD3_REM_TX_COUNT); + + if (info->flags & IEEE80211_TX_CTL_INJECTED) { + u16 seqno = le16_to_cpu(hdr->seq_ctrl); + + if (ieee80211_is_back_req(hdr->frame_control)) { + struct ieee80211_bar *bar; + + bar = (struct ieee80211_bar *)skb->data; + seqno = le16_to_cpu(bar->start_seq_num); + } + + val = MT_TXD3_SN_VALID | + FIELD_PREP(MT_TXD3_SEQ, IEEE80211_SEQ_TO_SN(seqno)); + txwi[3] |= cpu_to_le32(val); + txwi[3] &= ~cpu_to_le32(MT_TXD3_HW_AMSDU); + } +} + +void +mt7925_mac_write_txwi(struct mt76_dev *dev, __le32 *txwi, + struct sk_buff *skb, struct mt76_wcid *wcid, + struct ieee80211_key_conf *key, int pid, + enum mt76_txq_id qid, u32 changed) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_vif *vif = info->control.vif; + u8 p_fmt, q_idx, omac_idx = 0, wmm_idx = 0, band_idx = 0; + u32 val, sz_txd = mt76_is_mmio(dev) ? MT_TXD_SIZE : MT_SDIO_TXD_SIZE; + bool is_8023 = info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP; + struct mt76_vif_link *mvif; + bool beacon = !!(changed & (BSS_CHANGED_BEACON | + BSS_CHANGED_BEACON_ENABLED)); + bool inband_disc = !!(changed & (BSS_CHANGED_UNSOL_BCAST_PROBE_RESP | + BSS_CHANGED_FILS_DISCOVERY)); + struct mt792x_bss_conf *mconf; + + mconf = vif ? mt792x_vif_to_link((struct mt792x_vif *)vif->drv_priv, + wcid->link_id) : NULL; + mvif = mconf ? (struct mt76_vif_link *)&mconf->mt76 : NULL; + + if (mvif) { + omac_idx = mvif->omac_idx; + wmm_idx = mvif->wmm_idx; + band_idx = mvif->band_idx; + } + + if (inband_disc) { + p_fmt = MT_TX_TYPE_FW; + q_idx = MT_LMAC_ALTX0; + } else if (beacon) { + p_fmt = MT_TX_TYPE_FW; + q_idx = MT_LMAC_BCN0; + } else if (qid >= MT_TXQ_PSD) { + p_fmt = mt76_is_mmio(dev) ? MT_TX_TYPE_CT : MT_TX_TYPE_SF; + q_idx = MT_LMAC_ALTX0; + } else { + p_fmt = mt76_is_mmio(dev) ? MT_TX_TYPE_CT : MT_TX_TYPE_SF; + q_idx = wmm_idx * MT76_CONNAC_MAX_WMM_SETS + + mt76_connac_lmac_mapping(skb_get_queue_mapping(skb)); + + /* counting non-offloading skbs */ + wcid->stats.tx_bytes += skb->len; + wcid->stats.tx_packets++; + } + + val = FIELD_PREP(MT_TXD0_TX_BYTES, skb->len + sz_txd) | + FIELD_PREP(MT_TXD0_PKT_FMT, p_fmt) | + FIELD_PREP(MT_TXD0_Q_IDX, q_idx); + txwi[0] = cpu_to_le32(val); + + val = FIELD_PREP(MT_TXD1_WLAN_IDX, wcid->idx) | + FIELD_PREP(MT_TXD1_OWN_MAC, omac_idx); + + if (band_idx) + val |= FIELD_PREP(MT_TXD1_TGID, band_idx); + + txwi[1] = cpu_to_le32(val); + txwi[2] = 0; + + val = FIELD_PREP(MT_TXD3_REM_TX_COUNT, 15); + + if (key) + val |= MT_TXD3_PROTECT_FRAME; + if (info->flags & IEEE80211_TX_CTL_NO_ACK) + val |= MT_TXD3_NO_ACK; + if (wcid->amsdu) + val |= MT_TXD3_HW_AMSDU; + + txwi[3] = cpu_to_le32(val); + txwi[4] = 0; + + val = FIELD_PREP(MT_TXD5_PID, pid); + if (pid >= MT_PACKET_ID_FIRST) { + val |= MT_TXD5_TX_STATUS_HOST; + txwi[3] |= cpu_to_le32(MT_TXD3_BA_DISABLE); + txwi[3] &= ~cpu_to_le32(MT_TXD3_HW_AMSDU); + } + + txwi[5] = cpu_to_le32(val); + + if (is_mt7928(dev)) + val = MT_TXD6_DAS | FIELD_PREP(MT_TXD6_MSDU_CNT_V2, 1); + else + val = MT_TXD6_DAS | FIELD_PREP(MT_TXD6_MSDU_CNT, 1); + + if (vif && (!ieee80211_vif_is_mld(vif) || + (q_idx >= MT_LMAC_ALTX0 && q_idx <= MT_LMAC_BCN0))) + val |= MT_TXD6_DIS_MAT; + txwi[6] = cpu_to_le32(val); + txwi[7] = 0; + + if (is_8023) + mt7925_mac_write_txwi_8023(txwi, skb, wcid); + else + mt7925_mac_write_txwi_80211(dev, txwi, skb, key); + + if (txwi[1] & cpu_to_le32(MT_TXD1_FIXED_RATE)) { + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + bool mcast = ieee80211_is_data(hdr->frame_control) && + is_multicast_ether_addr(hdr->addr1); + u8 idx = MT792x_BASIC_RATES_TBL; + + if (mvif) { + if (mcast && mvif->mcast_rates_idx) + idx = mvif->mcast_rates_idx; + else if (beacon && mvif->beacon_rates_idx) + idx = mvif->beacon_rates_idx; + else + idx = mvif->basic_rates_idx; + } + + txwi[6] |= cpu_to_le32(FIELD_PREP(MT_TXD6_TX_RATE, idx)); + + if (is_mt7928(dev)) + txwi[6] |= cpu_to_le32(FIELD_PREP(MT_TXD6_BW_V2, 8)); + + txwi[3] |= cpu_to_le32(MT_TXD3_BA_DISABLE); + } +} +EXPORT_SYMBOL_GPL(mt7925_mac_write_txwi); + +static void mt7925_tx_check_aggr(struct ieee80211_sta *sta, struct sk_buff *skb, + struct mt76_wcid *wcid) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_link_sta *link_sta; + struct mt792x_sta *msta; + bool is_8023; + u16 fc, tid; + + if (!sta) + return; + + link_sta = rcu_dereference(sta->link[wcid->link_id]); + if (!link_sta) + return; + + if (!(link_sta->ht_cap.ht_supported || link_sta->he_cap.has_he)) + return; + + tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK; + is_8023 = info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP; + + if (is_8023) { + fc = IEEE80211_FTYPE_DATA | + (sta->wme ? IEEE80211_STYPE_QOS_DATA : + IEEE80211_STYPE_DATA); + } else { + /* No need to get precise TID for Action/Management Frame, + * since it will not meet the following Frame Control + * condition anyway. + */ + + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + + fc = le16_to_cpu(hdr->frame_control) & + (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE); + } + + if (unlikely(fc != (IEEE80211_FTYPE_DATA | IEEE80211_STYPE_QOS_DATA))) + return; + + msta = (struct mt792x_sta *)sta->drv_priv; + + /* Packets belonging to the same TID can be transmitted over multiple + * links. Keep the TX BA session state in the primary link so all links + * share the same AMPDU bookkeeping. + */ + if (!test_and_set_bit(tid, &msta->deflink.wcid.ampdu_state)) { + if (ieee80211_start_tx_ba_session(sta, tid, 0)) + clear_bit(tid, &msta->deflink.wcid.ampdu_state); + + } +} + +static bool +mt7925_mac_add_txs_skb(struct mt792x_dev *dev, struct mt76_wcid *wcid, + int pid, __le32 *txs_data) +{ + struct mt76_sta_stats *stats = &wcid->stats; + struct ieee80211_supported_band *sband; + struct mt76_dev *mdev = &dev->mt76; + struct mt76_phy *mphy; + struct ieee80211_tx_info *info; + struct sk_buff_head list; + struct rate_info rate = {}; + struct sk_buff *skb; + bool cck = false; + u32 txrate, txs, mode, stbc; + + mt76_tx_status_lock(mdev, &list); + skb = mt76_tx_status_skb_get(mdev, wcid, pid, &list); + if (!skb) + goto out_no_skb; + + txs = le32_to_cpu(txs_data[0]); + info = IEEE80211_SKB_CB(skb); + if (!(txs & MT_TXS0_ACK_ERROR_MASK)) + info->flags |= IEEE80211_TX_STAT_ACK; + + info->status.ampdu_len = 1; + info->status.ampdu_ack_len = !!(info->flags & + IEEE80211_TX_STAT_ACK); + + info->status.rates[0].idx = -1; + + txrate = FIELD_GET(MT_TXS0_TX_RATE, txs); + + rate.mcs = FIELD_GET(MT_TX_RATE_IDX, txrate); + rate.nss = FIELD_GET(MT_TX_RATE_NSS, txrate) + 1; + stbc = le32_get_bits(txs_data[3], MT_TXS3_RATE_STBC); + + if (stbc && rate.nss > 1) + rate.nss >>= 1; + + if (rate.nss - 1 < ARRAY_SIZE(stats->tx_nss)) + stats->tx_nss[rate.nss - 1]++; + if (rate.mcs < ARRAY_SIZE(stats->tx_mcs)) + stats->tx_mcs[rate.mcs]++; + + mode = FIELD_GET(MT_TX_RATE_MODE, txrate); + switch (mode) { + case MT_PHY_TYPE_CCK: + cck = true; + fallthrough; + case MT_PHY_TYPE_OFDM: + mphy = mt76_dev_phy(mdev, wcid->phy_idx); + + if (mphy->chandef.chan->band == NL80211_BAND_5GHZ) + sband = &mphy->sband_5g.sband; + else if (mphy->chandef.chan->band == NL80211_BAND_6GHZ) + sband = &mphy->sband_6g.sband; + else + sband = &mphy->sband_2g.sband; + + rate.mcs = mt76_get_rate(mphy->dev, sband, rate.mcs, cck); + rate.legacy = sband->bitrates[rate.mcs].bitrate; + break; + case MT_PHY_TYPE_HT: + case MT_PHY_TYPE_HT_GF: + if (rate.mcs > 31) + goto out; + + rate.flags = RATE_INFO_FLAGS_MCS; + if (wcid->rate.flags & RATE_INFO_FLAGS_SHORT_GI) + rate.flags |= RATE_INFO_FLAGS_SHORT_GI; + break; + case MT_PHY_TYPE_VHT: + if (rate.mcs > 9) + goto out; + + rate.flags = RATE_INFO_FLAGS_VHT_MCS; + break; + case MT_PHY_TYPE_HE_SU: + case MT_PHY_TYPE_HE_EXT_SU: + case MT_PHY_TYPE_HE_TB: + case MT_PHY_TYPE_HE_MU: + if (rate.mcs > 11) + goto out; + + rate.he_gi = wcid->rate.he_gi; + rate.he_dcm = FIELD_GET(MT_TX_RATE_DCM, txrate); + rate.flags = RATE_INFO_FLAGS_HE_MCS; + break; + case MT_PHY_TYPE_EHT_SU: + case MT_PHY_TYPE_EHT_TRIG: + case MT_PHY_TYPE_EHT_MU: + if (rate.mcs > 13) + goto out; + + rate.eht_gi = wcid->rate.eht_gi; + rate.flags = RATE_INFO_FLAGS_EHT_MCS; + break; + default: + goto out; + } + + stats->tx_mode[mode]++; + + switch (FIELD_GET(MT_TXS0_BW, txs)) { + case IEEE80211_STA_RX_BW_320: + rate.bw = RATE_INFO_BW_320; + stats->tx_bw[4]++; + break; + case IEEE80211_STA_RX_BW_160: + rate.bw = RATE_INFO_BW_160; + stats->tx_bw[3]++; + break; + case IEEE80211_STA_RX_BW_80: + rate.bw = RATE_INFO_BW_80; + stats->tx_bw[2]++; + break; + case IEEE80211_STA_RX_BW_40: + rate.bw = RATE_INFO_BW_40; + stats->tx_bw[1]++; + break; + default: + rate.bw = RATE_INFO_BW_20; + stats->tx_bw[0]++; + break; + } + wcid->rate = rate; + +out: + mt76_tx_status_skb_done(mdev, skb, &list); + +out_no_skb: + mt76_tx_status_unlock(mdev, &list); + + return !!skb; +} + +static bool +mt7928_mac_add_txs_skb_msg(struct mt792x_dev *dev, struct mt76_wcid *wcid, + int pid, struct mt7928_uni_txdone_event *pevt) +{ + struct mt76_sta_stats *stats = &wcid->stats; + struct ieee80211_supported_band *sband; + struct mt76_dev *mdev = &dev->mt76; + struct ieee80211_tx_info *info; + struct sk_buff_head list; + u32 txrate, mode, stbc; + struct rate_info rate; + struct mt76_phy *mphy; + struct sk_buff *skb; + bool cck = false; + + mt76_tx_status_lock(mdev, &list); + skb = mt76_tx_status_skb_get(mdev, wcid, pid, &list); + if (!skb) + goto out_no_skb; + + info = IEEE80211_SKB_CB(skb); + if (!pevt->status) + info->flags |= IEEE80211_TX_STAT_ACK; + + info->status.ampdu_len = 1; + info->status.ampdu_ack_len = !!(info->flags & + IEEE80211_TX_STAT_ACK); + + info->status.rates[0].idx = -1; + + txrate = le16_to_cpu(pevt->tx_rate); + + rate.mcs = FIELD_GET(MT_TX_RATE_IDX, txrate); + rate.nss = FIELD_GET(MT_TX_RATE_NSS, txrate) + 1; + stbc = FIELD_GET(MT_TX_RATE_STBC, txrate); + + if (stbc && rate.nss > 1) + rate.nss >>= 1; + + if (rate.nss - 1 < ARRAY_SIZE(stats->tx_nss)) + stats->tx_nss[rate.nss - 1]++; + if (rate.mcs < ARRAY_SIZE(stats->tx_mcs)) + stats->tx_mcs[rate.mcs]++; + + mode = FIELD_GET(MT_TX_RATE_MODE, txrate); + switch (mode) { + case MT_PHY_TYPE_CCK: + cck = true; + fallthrough; + case MT_PHY_TYPE_OFDM: + mphy = mt76_dev_phy(mdev, wcid->phy_idx); + + if (mphy->chandef.chan->band == NL80211_BAND_5GHZ) + sband = &mphy->sband_5g.sband; + else if (mphy->chandef.chan->band == NL80211_BAND_6GHZ) + sband = &mphy->sband_6g.sband; + else + sband = &mphy->sband_2g.sband; + + rate.mcs = mt76_get_rate(mphy->dev, sband, rate.mcs, cck); + rate.legacy = sband->bitrates[rate.mcs].bitrate; + break; + case MT_PHY_TYPE_HT: + case MT_PHY_TYPE_HT_GF: + if (rate.mcs > 31) + goto out; + + rate.flags = RATE_INFO_FLAGS_MCS; + if (wcid->rate.flags & RATE_INFO_FLAGS_SHORT_GI) + rate.flags |= RATE_INFO_FLAGS_SHORT_GI; + break; + case MT_PHY_TYPE_VHT: + if (rate.mcs > 9) + goto out; + + rate.flags = RATE_INFO_FLAGS_VHT_MCS; + break; + case MT_PHY_TYPE_HE_SU: + case MT_PHY_TYPE_HE_EXT_SU: + case MT_PHY_TYPE_HE_TB: + case MT_PHY_TYPE_HE_MU: + if (rate.mcs > 11) + goto out; + + rate.he_gi = wcid->rate.he_gi; + rate.he_dcm = FIELD_GET(MT_TX_RATE_DCM, txrate); + rate.flags = RATE_INFO_FLAGS_HE_MCS; + break; + case MT_PHY_TYPE_EHT_SU: + case MT_PHY_TYPE_EHT_TRIG: + case MT_PHY_TYPE_EHT_MU: + if (rate.mcs > 13) + goto out; + + rate.eht_gi = wcid->rate.eht_gi; + rate.flags = RATE_INFO_FLAGS_EHT_MCS; + break; + default: + goto out; + } + + stats->tx_mode[mode]++; + + switch (pevt->bw) { + case IEEE80211_STA_RX_BW_160: + rate.bw = RATE_INFO_BW_160; + stats->tx_bw[3]++; + break; + case IEEE80211_STA_RX_BW_80: + rate.bw = RATE_INFO_BW_80; + stats->tx_bw[2]++; + break; + case IEEE80211_STA_RX_BW_40: + rate.bw = RATE_INFO_BW_40; + stats->tx_bw[1]++; + break; + default: + rate.bw = RATE_INFO_BW_20; + stats->tx_bw[0]++; + break; + } + wcid->rate = rate; + +out: + mt76_tx_status_skb_done(mdev, skb, &list); + +out_no_skb: + mt76_tx_status_unlock(mdev, &list); + + return !!skb; +} + +void mt7928_mac_add_txs_msg(struct mt792x_dev *dev, + void *evt) +{ + struct mt7928_uni_txdone_event *done_evt; + struct mt792x_link_sta *mlink = NULL; + struct mt76_wcid *wcid; + u16 wcidx; + u8 pid; + + done_evt = (struct mt7928_uni_txdone_event *)evt; + wcidx = done_evt->wcid; + pid = done_evt->pid; + + if (pid < MT_PACKET_ID_FIRST) + return; + + if (wcidx >= MT792x_WTBL_SIZE) + return; + + rcu_read_lock(); + + wcid = mt76_wcid_ptr(dev, wcidx); + if (!wcid) + goto out; + + mlink = container_of(wcid, struct mt792x_link_sta, wcid); + + mt7928_mac_add_txs_skb_msg(dev, wcid, pid, done_evt); + if (!wcid->sta) + goto out; + + mt76_wcid_add_poll(&dev->mt76, &mlink->wcid); + +out: + rcu_read_unlock(); +} + +void mt7925_mac_add_txs(struct mt792x_dev *dev, void *data) +{ + struct mt792x_link_sta *mlink = NULL; + __le32 *txs_data = data; + struct mt76_wcid *wcid; + u8 pid, txs_fm; + u16 wcidx; + + txs_fm = le32_get_bits(txs_data[0], MT_TXS0_TXS_FORMAT); + + if (is_mt7928(&dev->mt76)) { + if (txs_fm != TXS_FM_MPDU && txs_fm != TXS_FM_PPDU) + return; + } else if (txs_fm > 1) { + return; + } + + wcidx = le32_get_bits(txs_data[2], MT_TXS2_WCID); + pid = le32_get_bits(txs_data[3], MT_TXS3_PID); + + if (pid < MT_PACKET_ID_FIRST) + return; + + if (wcidx >= MT792x_WTBL_SIZE) + return; + + rcu_read_lock(); + + wcid = mt76_wcid_ptr(dev, wcidx); + if (!wcid) + goto out; + + mlink = container_of(wcid, struct mt792x_link_sta, wcid); + + mt7925_mac_add_txs_skb(dev, wcid, pid, txs_data); + if (!wcid->sta) + goto out; + + mt76_wcid_add_poll(&dev->mt76, &mlink->wcid); + +out: + rcu_read_unlock(); +} + +void mt7925_txwi_free(struct mt792x_dev *dev, struct mt76_txwi_cache *t, + struct ieee80211_sta *sta, struct mt76_wcid *wcid, + struct list_head *free_list) +{ + struct mt76_dev *mdev = &dev->mt76; + __le32 *txwi; + u16 wcid_idx; + + mt76_connac_txp_skb_unmap(mdev, t); + if (!t->skb) + goto out; + + txwi = (__le32 *)mt76_get_txwi_ptr(mdev, t); + if (sta) { + if (likely(t->skb->protocol != cpu_to_be16(ETH_P_PAE))) + mt7925_tx_check_aggr(sta, t->skb, wcid); + + wcid_idx = wcid->idx; + } else { + wcid_idx = le32_get_bits(txwi[1], MT_TXD1_WLAN_IDX); + } + + __mt76_tx_complete_skb(mdev, wcid_idx, t->skb, free_list); +out: + t->skb = NULL; + mt76_put_txwi(mdev, t); +} +EXPORT_SYMBOL_GPL(mt7925_txwi_free); + +static void +mt7925_mac_tx_free(struct mt792x_dev *dev, void *data, int len) +{ + __le32 *tx_free = (__le32 *)data, *cur_info; + struct mt76_dev *mdev = &dev->mt76; + struct mt76_txwi_cache *txwi; + struct ieee80211_sta *sta = NULL; + struct mt76_wcid *wcid = NULL; + LIST_HEAD(free_list); + struct sk_buff *skb, *tmp; + void *end = data + len; + bool wake = false; + u16 total, count = 0; + + /* clean DMA queues and unmap buffers first */ + mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[MT_TXQ_PSD], false); + mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[MT_TXQ_BE], false); + + if (WARN_ON_ONCE(le32_get_bits(tx_free[1], MT_TXFREE1_VER) < 4)) + return; + + total = le32_get_bits(tx_free[0], MT_TXFREE0_MSDU_CNT); + for (cur_info = &tx_free[2]; count < total; cur_info++) { + u32 msdu, info; + u8 i; + + if (WARN_ON_ONCE((void *)cur_info >= end)) + return; + /* 1'b1: new wcid pair. + * 1'b0: msdu_id with the same 'wcid pair' as above. + */ + info = le32_to_cpu(*cur_info); + if (info & MT_TXFREE_INFO_PAIR) { + struct mt792x_link_sta *mlink; + u16 idx; + + idx = FIELD_GET(MT_TXFREE_INFO_WLAN_ID, info); + wcid = mt76_wcid_ptr(dev, idx); + sta = wcid_to_sta(wcid); + if (!sta) + continue; + + mlink = container_of(wcid, struct mt792x_link_sta, wcid); + mt76_wcid_add_poll(&dev->mt76, &mlink->wcid); + continue; + } + + if (info & MT_TXFREE_INFO_HEADER) { + if (wcid) { + u32 count = FIELD_GET(MT_TXFREE_INFO_COUNT, info); + + wcid->stats.tx_retries += count ? count - 1 : 0; + wcid->stats.tx_failed += + !!FIELD_GET(MT_TXFREE_INFO_STAT, info); + } + continue; + } + + for (i = 0; i < 2; i++) { + msdu = (info >> (15 * i)) & MT_TXFREE_INFO_MSDU_ID; + if (msdu == MT_TXFREE_INFO_MSDU_ID) + continue; + + count++; + txwi = mt76_token_release(mdev, msdu, &wake); + if (!txwi) + continue; + + mt7925_txwi_free(dev, txwi, sta, wcid, &free_list); + } + } + + mt7925_mac_sta_poll(dev); + + if (wake) + mt76_set_tx_blocked(&dev->mt76, false); + + mt76_worker_schedule(&dev->mt76.tx_worker); + + list_for_each_entry_safe(skb, tmp, &free_list, list) { + skb_list_del_init(skb); + napi_consume_skb(skb, 1); + } +} + +bool mt7925_rx_check(struct mt76_dev *mdev, void *data, int len) +{ + struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); + __le32 *rxd = (__le32 *)data; + __le32 *end = (__le32 *)&rxd[len / 4]; + enum rx_pkt_type type; + + type = le32_get_bits(rxd[0], MT_RXD0_PKT_TYPE); + if (type != PKT_TYPE_NORMAL) { + u32 sw_type = le32_get_bits(rxd[0], MT_RXD0_SW_PKT_TYPE_MASK); + + if (unlikely((sw_type & MT_RXD0_SW_PKT_TYPE_MAP) == + MT_RXD0_SW_PKT_TYPE_FRAME)) + return true; + } + + switch (type) { + case PKT_TYPE_TXRX_NOTIFY: + if (!mt76_is_mmio(mdev)) + return false; + mt7925_mac_tx_free(dev, data, len); + return false; + case PKT_TYPE_TXS: + for (rxd += 4; rxd + 12 <= end; rxd += 12) + mt7925_mac_add_txs(dev, rxd); + return false; + default: + return true; + } +} +EXPORT_SYMBOL_GPL(mt7925_rx_check); + +void mt7925_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, + struct sk_buff *skb, u32 *info) +{ + struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); + __le32 *rxd = (__le32 *)skb->data; + __le32 *end = (__le32 *)&skb->data[skb->len]; + enum rx_pkt_type type; + u16 flag; + + type = le32_get_bits(rxd[0], MT_RXD0_PKT_TYPE); + flag = le32_get_bits(rxd[0], MT_RXD0_PKT_FLAG); + if (type != PKT_TYPE_NORMAL) { + u32 sw_type = le32_get_bits(rxd[0], MT_RXD0_SW_PKT_TYPE_MASK); + + if (unlikely((sw_type & MT_RXD0_SW_PKT_TYPE_MAP) == + MT_RXD0_SW_PKT_TYPE_FRAME)) + type = PKT_TYPE_NORMAL; + } + + if (type == PKT_TYPE_RX_EVENT && flag == 0x1) + type = PKT_TYPE_NORMAL_MCU; + + switch (type) { + case PKT_TYPE_TXRX_NOTIFY: + if (!mt76_is_mmio(mdev)) { + napi_consume_skb(skb, 1); + break; + } + mt7925_mac_tx_free(dev, skb->data, skb->len); + napi_consume_skb(skb, 1); + break; + case PKT_TYPE_RX_EVENT: + mt7925_mcu_rx_event(dev, skb); + break; + case PKT_TYPE_TXS: + for (rxd += 2; rxd + 8 <= end; rxd += 8) + mt7925_mac_add_txs(dev, rxd); + dev_kfree_skb(skb); + break; + case PKT_TYPE_NORMAL_MCU: + case PKT_TYPE_NORMAL: + if (!mt7925_mac_fill_rx(dev, skb)) { + mt76_rx(&dev->mt76, q, skb); + return; + } + fallthrough; + default: + dev_kfree_skb(skb); + break; + } +} +EXPORT_SYMBOL_GPL(mt7925_queue_rx_skb); + +static void +mt7925_vif_connect_iter(void *priv, u8 *mac, + struct ieee80211_vif *vif) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + unsigned long valid = ieee80211_vif_is_mld(vif) ? + mvif->valid_links : BIT(0); + struct mt792x_dev *dev = mvif->phy->dev; + struct ieee80211_hw *hw = mt76_hw(dev); + struct ieee80211_bss_conf *bss_conf; + struct mt792x_bss_conf *mconf; + int i; + + if (vif->type == NL80211_IFTYPE_STATION) + ieee80211_disconnect(vif, true); + + for_each_set_bit(i, &valid, IEEE80211_MLD_MAX_NUM_LINKS) { + bss_conf = mt792x_vif_to_bss_conf(vif, i); + if (!bss_conf) + continue; + + mconf = mt792x_vif_to_link(mvif, i); + + mt76_connac_mcu_uni_add_dev(&dev->mphy, bss_conf, &mconf->mt76, + &mvif->sta.deflink.wcid, true); + mt7925_mcu_set_tx(dev, bss_conf); + } + + if (vif->type == NL80211_IFTYPE_AP) { + mt76_connac_mcu_uni_add_bss(dev->phy.mt76, vif, &mvif->sta.deflink.wcid, + true, NULL); + mt7925_mcu_sta_update(dev, NULL, vif, + &mvif->sta.deflink, true, + MT76_STA_INFO_STATE_NONE); + mt7925_mcu_uni_add_beacon_offload(dev, hw, vif, true); + } +} + +/* system error recovery */ +void mt7925_mac_reset_work(struct work_struct *work) +{ + struct mt792x_dev *dev = container_of(work, struct mt792x_dev, + reset_work); + struct ieee80211_hw *hw = mt76_hw(dev); + struct mt76_connac_pm *pm = &dev->pm; + int i, ret; + + if (atomic_read(&dev->mt76.bus_hung)) + return; + + dev_dbg(dev->mt76.dev, "chip reset\n"); + dev->hw_full_reset = true; + ieee80211_stop_queues(hw); + + cancel_delayed_work_sync(&dev->mphy.mac_work); + cancel_delayed_work_sync(&pm->ps_work); + cancel_delayed_work_sync(&dev->mlo_pm_work); + cancel_work_sync(&pm->wake_work); + + for (i = 0; i < 10; i++) { + if (test_bit(MT76_REMOVED, &dev->mphy.state)) + goto out; + + mutex_lock(&dev->mt76.mutex); + ret = mt792x_dev_reset(dev); + mutex_unlock(&dev->mt76.mutex); + + if (!ret) + break; + } + + if (atomic_read(&dev->mt76.bus_hung)) + return; + + if (i == 10) + dev_err(dev->mt76.dev, "chip reset failed\n"); + + if (test_and_clear_bit(MT76_HW_SCANNING, &dev->mphy.state)) { + struct cfg80211_scan_info info = { + .aborted = true, + }; + + ieee80211_scan_completed(dev->mphy.hw, &info); + } + +out: + dev->hw_full_reset = false; + pm->suspended = false; + if (test_bit(MT76_REMOVED, &dev->mphy.state)) + return; + + ieee80211_wake_queues(hw); + ieee80211_iterate_active_interfaces(hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7925_vif_connect_iter, NULL); + mt76_connac_power_save_sched(&dev->mt76.phy, pm); + + mt7925_regd_change(&dev->phy, "00"); +} + +void mt7925_coredump_work(struct work_struct *work) +{ + struct mt792x_dev *dev; + char *dump, *data; + + dev = (struct mt792x_dev *)container_of(work, struct mt792x_dev, + coredump.work.work); + + if (time_is_after_jiffies(dev->coredump.last_activity + + 4 * MT76_CONNAC_COREDUMP_TIMEOUT)) { + queue_delayed_work(dev->mt76.wq, &dev->coredump.work, + MT76_CONNAC_COREDUMP_TIMEOUT); + return; + } + + dump = vzalloc(MT76_CONNAC_COREDUMP_SZ); + data = dump; + + while (true) { + struct sk_buff *skb; + + spin_lock_bh(&dev->mt76.lock); + skb = __skb_dequeue(&dev->coredump.msg_list); + spin_unlock_bh(&dev->mt76.lock); + + if (!skb) + break; + + skb_pull(skb, sizeof(struct mt7925_mcu_rxd) + 8); + if (!dump || data + skb->len - dump > MT76_CONNAC_COREDUMP_SZ) { + dev_kfree_skb(skb); + continue; + } + + memcpy(data, skb->data, skb->len); + data += skb->len; + + dev_kfree_skb(skb); + } + + if (dump) + dev_coredumpv(dev->mt76.dev, dump, MT76_CONNAC_COREDUMP_SZ, + GFP_KERNEL); + + mt792x_reset(&dev->mt76); +} + +/* usb_sdio */ +static void +mt7925_usb_sdio_write_txwi(struct mt792x_dev *dev, struct mt76_wcid *wcid, + enum mt76_txq_id qid, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key, int pid, + struct sk_buff *skb) +{ + __le32 *txwi = (__le32 *)(skb->data - MT_SDIO_TXD_SIZE); + + memset(txwi, 0, MT_SDIO_TXD_SIZE); + mt7925_mac_write_txwi(&dev->mt76, txwi, skb, wcid, key, pid, qid, 0); + skb_push(skb, MT_SDIO_TXD_SIZE); +} + +int mt7925_usb_sdio_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, + enum mt76_txq_id qid, struct mt76_wcid *wcid, + struct ieee80211_sta *sta, + struct mt76_tx_info *tx_info) +{ + struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx_info->skb); + struct ieee80211_key_conf *key = info->control.hw_key; + struct sk_buff *skb = tx_info->skb; + int err, pad, pktid; + + if (unlikely(tx_info->skb->len <= ETH_HLEN)) + return -EINVAL; + + if (!wcid) + wcid = &dev->mt76.global_wcid; + + err = skb_cow_head(skb, MT_SDIO_TXD_SIZE + MT_SDIO_HDR_SIZE); + if (err) + return err; + + if (sta) { + struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv; + + if (time_after(jiffies, msta->deflink.last_txs + HZ / 4)) { + info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; + msta->deflink.last_txs = jiffies; + } + } + + pktid = mt76_tx_status_skb_add(&dev->mt76, wcid, skb); + mt7925_usb_sdio_write_txwi(dev, wcid, qid, sta, key, pktid, skb); + + mt792x_skb_add_usb_sdio_hdr(dev, skb, 0); + pad = round_up(skb->len, 4) - skb->len; + if (mt76_is_usb(mdev)) + pad += 4; + + err = mt76_skb_adjust_pad(skb, pad); + if (err) + /* Release pktid in case of error. */ + idr_remove(&wcid->pktid, pktid); + + return err; +} +EXPORT_SYMBOL_GPL(mt7925_usb_sdio_tx_prepare_skb); + +void mt7925_usb_sdio_tx_complete_skb(struct mt76_dev *mdev, + struct mt76_queue_entry *e) +{ + __le32 *txwi = (__le32 *)(e->skb->data + MT_SDIO_HDR_SIZE); + unsigned int headroom = MT_SDIO_TXD_SIZE + MT_SDIO_HDR_SIZE; + struct ieee80211_sta *sta; + struct mt76_wcid *wcid; + u16 idx; + + idx = le32_get_bits(txwi[1], MT_TXD1_WLAN_IDX); + wcid = __mt76_wcid_ptr(mdev, idx); + sta = wcid_to_sta(wcid); + + if (sta && likely(e->skb->protocol != cpu_to_be16(ETH_P_PAE))) + mt7925_tx_check_aggr(sta, e->skb, wcid); + + skb_pull(e->skb, headroom); + mt76_tx_complete_skb(mdev, e->wcid, e->skb); +} +EXPORT_SYMBOL_GPL(mt7925_usb_sdio_tx_complete_skb); + +bool mt7925_usb_sdio_tx_status_data(struct mt76_dev *mdev, u8 *update) +{ + struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); + + mt792x_mutex_acquire(dev); + mt7925_mac_sta_poll(dev); + mt792x_mutex_release(dev); + + return false; +} +EXPORT_SYMBOL_GPL(mt7925_usb_sdio_tx_status_data); + +#if IS_ENABLED(CONFIG_IPV6) +void mt7925_set_ipv6_ns_work(struct work_struct *work) +{ + struct mt792x_dev *dev = container_of(work, struct mt792x_dev, + ipv6_ns_work); + struct sk_buff *skb; + int ret = 0; + + do { + skb = skb_dequeue(&dev->ipv6_ns_list); + + if (!skb) + break; + + mt792x_mutex_acquire(dev); + ret = mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_UNI_CMD(OFFLOAD), true); + mt792x_mutex_release(dev); + + } while (!ret); + + if (ret) + skb_queue_purge(&dev->ipv6_ns_list); +} +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mac.h b/drivers/net/wireless/mediatek/mt76/mt7925/mac.h new file mode 100644 index 000000000..567d072cb --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mac.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* Copyright (C) 2023 MediaTek Inc. */ + +#ifndef __MT7925_MAC_H +#define __MT7925_MAC_H + +#include "../mt76_connac3_mac.h" + +#define MT_WTBL_TXRX_CAP_RATE_OFFSET 7 +#define MT_WTBL_TXRX_RATE_G2_HE 24 +#define MT_WTBL_TXRX_RATE_G2 12 + +#define MT_WTBL_AC0_CTT_OFFSET 20 + +static inline u32 mt7925_mac_wtbl_lmac_addr(struct mt792x_dev *dev, u16 wcid, u8 dw) +{ + u32 wdu_cr; + + if (is_mt7928(&dev->mt76)) + wdu_cr = MT7928_WTBLON_TOP_WDUCR; + else + wdu_cr = MT7925_WTBLON_TOP_WDUCR; + + mt76_wr(dev, wdu_cr, + FIELD_PREP(MT_WTBLON_TOP_WDUCR_GROUP, (wcid >> 7))); + + return MT_WTBL_LMAC_OFFS(wcid, dw); +} + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c new file mode 100644 index 000000000..84b55f008 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c @@ -0,0 +1,2794 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2023 MediaTek Inc. */ + +#include +#include +#include +#include +#include +#include +#include "mt7925.h" +#include "regd.h" +#include "mcu.h" +#include "mac.h" +#include "nan.h" + +static void +mt7925_init_he_caps(struct mt792x_phy *phy, enum nl80211_band band, + struct ieee80211_sband_iftype_data *data, + enum nl80211_iftype iftype) +{ + struct ieee80211_sta_he_cap *he_cap = &data->he_cap; + struct ieee80211_he_cap_elem *he_cap_elem = &he_cap->he_cap_elem; + struct ieee80211_he_mcs_nss_supp *he_mcs = &he_cap->he_mcs_nss_supp; + int i, nss = hweight8(phy->mt76->antenna_mask); + u16 mcs_map = 0; + + for (i = 0; i < 8; i++) { + if (i < nss) + mcs_map |= (IEEE80211_HE_MCS_SUPPORT_0_11 << (i * 2)); + else + mcs_map |= (IEEE80211_HE_MCS_NOT_SUPPORTED << (i * 2)); + } + + he_cap->has_he = true; + + he_cap_elem->mac_cap_info[0] = IEEE80211_HE_MAC_CAP0_HTC_HE; + he_cap_elem->mac_cap_info[3] = IEEE80211_HE_MAC_CAP3_OMI_CONTROL | + IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_3; + he_cap_elem->mac_cap_info[4] = IEEE80211_HE_MAC_CAP4_AMSDU_IN_AMPDU; + + if (band == NL80211_BAND_2GHZ) + he_cap_elem->phy_cap_info[0] = + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G; + else + he_cap_elem->phy_cap_info[0] = + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G | + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G; + + he_cap_elem->phy_cap_info[1] = + IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD; + he_cap_elem->phy_cap_info[2] = + IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US | + IEEE80211_HE_PHY_CAP2_STBC_TX_UNDER_80MHZ | + IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ | + IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO | + IEEE80211_HE_PHY_CAP2_UL_MU_PARTIAL_MU_MIMO; + + switch (iftype) { + case NL80211_IFTYPE_AP: + he_cap_elem->mac_cap_info[2] |= + IEEE80211_HE_MAC_CAP2_BSR; + he_cap_elem->mac_cap_info[4] |= + IEEE80211_HE_MAC_CAP4_BQR; + he_cap_elem->mac_cap_info[5] |= + IEEE80211_HE_MAC_CAP5_OM_CTRL_UL_MU_DATA_DIS_RX; + he_cap_elem->phy_cap_info[3] |= + IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_QPSK | + IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_QPSK; + he_cap_elem->phy_cap_info[6] |= + IEEE80211_HE_PHY_CAP6_PARTIAL_BW_EXT_RANGE | + IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT; + he_cap_elem->phy_cap_info[9] |= + IEEE80211_HE_PHY_CAP9_TX_1024_QAM_LESS_THAN_242_TONE_RU | + IEEE80211_HE_PHY_CAP9_RX_1024_QAM_LESS_THAN_242_TONE_RU; + break; + case NL80211_IFTYPE_STATION: + he_cap_elem->mac_cap_info[1] |= + IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US; + + if (band == NL80211_BAND_2GHZ) + he_cap_elem->phy_cap_info[0] |= + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_RU_MAPPING_IN_2G; + else + he_cap_elem->phy_cap_info[0] |= + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_RU_MAPPING_IN_5G; + + he_cap_elem->phy_cap_info[1] |= + IEEE80211_HE_PHY_CAP1_DEVICE_CLASS_A | + IEEE80211_HE_PHY_CAP1_HE_LTF_AND_GI_FOR_HE_PPDUS_0_8US; + he_cap_elem->phy_cap_info[3] |= + IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_QPSK | + IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_QPSK; + he_cap_elem->phy_cap_info[4] |= + IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE | + IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_4 | + IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_4; + he_cap_elem->phy_cap_info[5] |= + IEEE80211_HE_PHY_CAP5_NG16_SU_FEEDBACK | + IEEE80211_HE_PHY_CAP5_NG16_MU_FEEDBACK; + he_cap_elem->phy_cap_info[6] |= + IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_42_SU | + IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_75_MU | + IEEE80211_HE_PHY_CAP6_TRIG_CQI_FB | + IEEE80211_HE_PHY_CAP6_PARTIAL_BW_EXT_RANGE | + IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT; + he_cap_elem->phy_cap_info[7] |= + IEEE80211_HE_PHY_CAP7_POWER_BOOST_FACTOR_SUPP | + IEEE80211_HE_PHY_CAP7_HE_SU_MU_PPDU_4XLTF_AND_08_US_GI; + he_cap_elem->phy_cap_info[8] |= + IEEE80211_HE_PHY_CAP8_20MHZ_IN_40MHZ_HE_PPDU_IN_2G | + IEEE80211_HE_PHY_CAP8_20MHZ_IN_160MHZ_HE_PPDU | + IEEE80211_HE_PHY_CAP8_80MHZ_IN_160MHZ_HE_PPDU | + IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_484; + he_cap_elem->phy_cap_info[9] |= + IEEE80211_HE_PHY_CAP9_LONGER_THAN_16_SIGB_OFDM_SYM | + IEEE80211_HE_PHY_CAP9_NON_TRIGGERED_CQI_FEEDBACK | + IEEE80211_HE_PHY_CAP9_TX_1024_QAM_LESS_THAN_242_TONE_RU | + IEEE80211_HE_PHY_CAP9_RX_1024_QAM_LESS_THAN_242_TONE_RU | + IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_COMP_SIGB | + IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_NON_COMP_SIGB; + break; + default: + break; + } + + he_mcs->rx_mcs_80 = cpu_to_le16(mcs_map); + he_mcs->tx_mcs_80 = cpu_to_le16(mcs_map); + he_mcs->rx_mcs_160 = cpu_to_le16(mcs_map); + he_mcs->tx_mcs_160 = cpu_to_le16(mcs_map); + + memset(he_cap->ppe_thres, 0, sizeof(he_cap->ppe_thres)); + + if (he_cap_elem->phy_cap_info[6] & + IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT) { + mt76_connac_gen_ppe_thresh(he_cap->ppe_thres, nss, band); + } else { + he_cap_elem->phy_cap_info[9] |= + u8_encode_bits(IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_16US, + IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_MASK); + } + + if (band == NL80211_BAND_6GHZ) { + struct ieee80211_supported_band *sband = + &phy->mt76->sband_5g.sband; + struct ieee80211_sta_ht_cap *ht_cap = &sband->ht_cap; + + u16 cap = IEEE80211_HE_6GHZ_CAP_TX_ANTPAT_CONS | + IEEE80211_HE_6GHZ_CAP_RX_ANTPAT_CONS; + + cap |= u16_encode_bits(ht_cap->ampdu_density, + IEEE80211_HE_6GHZ_CAP_MIN_MPDU_START) | + u16_encode_bits(IEEE80211_VHT_MAX_AMPDU_1024K, + IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP) | + u16_encode_bits(IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454, + IEEE80211_HE_6GHZ_CAP_MAX_MPDU_LEN); + + data->he_6ghz_capa.capa = cpu_to_le16(cap); + } +} + +static void +mt7925_init_eht_caps(struct mt792x_phy *phy, enum nl80211_band band, + struct ieee80211_sband_iftype_data *data) +{ + struct ieee80211_sta_eht_cap *eht_cap = &data->eht_cap; + struct ieee80211_eht_cap_elem_fixed *eht_cap_elem = &eht_cap->eht_cap_elem; + struct ieee80211_eht_mcs_nss_supp *eht_nss = &eht_cap->eht_mcs_nss_supp; + enum nl80211_chan_width width = phy->mt76->chandef.width; + int nss = hweight8(phy->mt76->antenna_mask); + int sts = hweight16(phy->mt76->chainmask); + u8 val; + + if (!phy->dev->has_eht) + return; + + eht_cap->has_eht = true; + + eht_cap_elem->mac_cap_info[0] = + IEEE80211_EHT_MAC_CAP0_EPCS_PRIO_ACCESS | + IEEE80211_EHT_MAC_CAP0_OM_CONTROL; + + eht_cap_elem->phy_cap_info[0] = + IEEE80211_EHT_PHY_CAP0_NDP_4_EHT_LFT_32_GI | + IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMER | + IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMEE; + + if (band == NL80211_BAND_6GHZ && is_320mhz_supported(&phy->dev->mt76)) + eht_cap_elem->phy_cap_info[0] |= + IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ; + + val = (sts > 3) ? sts - 1 : 3; + + eht_cap_elem->phy_cap_info[0] |= + u8_encode_bits(u8_get_bits(val, BIT(0)), + IEEE80211_EHT_PHY_CAP0_BEAMFORMEE_SS_80MHZ_MASK); + + eht_cap_elem->phy_cap_info[1] = + u8_encode_bits(u8_get_bits(val, GENMASK(2, 1)), + IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_80MHZ_MASK) | + u8_encode_bits(val, + IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_160MHZ_MASK); + + if (band == NL80211_BAND_6GHZ && is_320mhz_supported(&phy->dev->mt76)) + eht_cap_elem->phy_cap_info[1] |= + u8_encode_bits(val, + IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_320MHZ_MASK); + + eht_cap_elem->phy_cap_info[2] = + u8_encode_bits(sts - 1, IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_80MHZ_MASK) | + u8_encode_bits(sts - 1, IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_160MHZ_MASK); + + if (band == NL80211_BAND_6GHZ && is_320mhz_supported(&phy->dev->mt76)) + eht_cap_elem->phy_cap_info[2] |= + u8_encode_bits(sts - 1, + IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_320MHZ_MASK); + + eht_cap_elem->phy_cap_info[3] = + IEEE80211_EHT_PHY_CAP3_NG_16_SU_FEEDBACK | + IEEE80211_EHT_PHY_CAP3_NG_16_MU_FEEDBACK | + IEEE80211_EHT_PHY_CAP3_CODEBOOK_4_2_SU_FDBK | + IEEE80211_EHT_PHY_CAP3_CODEBOOK_7_5_MU_FDBK | + IEEE80211_EHT_PHY_CAP3_TRIG_SU_BF_FDBK | + IEEE80211_EHT_PHY_CAP3_TRIG_MU_BF_PART_BW_FDBK | + IEEE80211_EHT_PHY_CAP3_TRIG_CQI_FDBK; + + eht_cap_elem->phy_cap_info[4] = + u8_encode_bits(min_t(int, sts - 1, 2), + IEEE80211_EHT_PHY_CAP4_MAX_NC_MASK); + + eht_cap_elem->phy_cap_info[5] = + IEEE80211_EHT_PHY_CAP5_NON_TRIG_CQI_FEEDBACK | + u8_encode_bits(IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_16US, + IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_MASK) | + u8_encode_bits(u8_get_bits(0x11, GENMASK(1, 0)), + IEEE80211_EHT_PHY_CAP5_MAX_NUM_SUPP_EHT_LTF_MASK); + + val = width == NL80211_CHAN_WIDTH_320 ? 0xf : + width == NL80211_CHAN_WIDTH_160 ? 0x7 : + width == NL80211_CHAN_WIDTH_80 ? 0x3 : 0x1; + eht_cap_elem->phy_cap_info[6] = + u8_encode_bits(u8_get_bits(0x11, GENMASK(4, 2)), + IEEE80211_EHT_PHY_CAP6_MAX_NUM_SUPP_EHT_LTF_MASK) | + u8_encode_bits(val, IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_MASK); + + eht_cap_elem->phy_cap_info[7] = + IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_80MHZ | + IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_160MHZ | + IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_80MHZ | + IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_160MHZ; + + val = u8_encode_bits(nss, IEEE80211_EHT_MCS_NSS_RX) | + u8_encode_bits(nss, IEEE80211_EHT_MCS_NSS_TX); + + eht_nss->bw._80.rx_tx_mcs9_max_nss = val; + eht_nss->bw._80.rx_tx_mcs11_max_nss = val; + eht_nss->bw._80.rx_tx_mcs13_max_nss = val; + eht_nss->bw._160.rx_tx_mcs9_max_nss = val; + eht_nss->bw._160.rx_tx_mcs11_max_nss = val; + eht_nss->bw._160.rx_tx_mcs13_max_nss = val; + if (band == NL80211_BAND_6GHZ && is_320mhz_supported(&phy->dev->mt76)) { + eht_nss->bw._320.rx_tx_mcs9_max_nss = val; + eht_nss->bw._320.rx_tx_mcs11_max_nss = val; + eht_nss->bw._320.rx_tx_mcs13_max_nss = val; + } +} + +int mt7925_init_mlo_caps(struct mt792x_phy *phy) +{ + struct wiphy *wiphy = phy->mt76->hw->wiphy; + static const u8 ext_capa_sta[] = { + [0] = WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING, + [2] = WLAN_EXT_CAPA3_MULTI_BSSID_SUPPORT, + [7] = WLAN_EXT_CAPA8_OPMODE_NOTIF, + }; + static struct wiphy_iftype_ext_capab ext_capab[] = { + { + .iftype = NL80211_IFTYPE_STATION, + .extended_capabilities = ext_capa_sta, + .extended_capabilities_mask = ext_capa_sta, + .extended_capabilities_len = sizeof(ext_capa_sta), + }, + }; + + if (!(phy->chip_cap & MT792x_CHIP_CAP_MLO_EN)) + return 0; + + ext_capab[0].eml_capabilities = phy->eml_cap; + ext_capab[0].mld_capa_and_ops = + u16_encode_bits(0, IEEE80211_MLD_CAP_OP_MAX_SIMUL_LINKS); + + wiphy->flags |= WIPHY_FLAG_SUPPORTS_MLO; + wiphy->iftype_ext_capab = ext_capab; + wiphy->num_iftype_ext_capab = ARRAY_SIZE(ext_capab); + + return 0; +} + +static void +__mt7925_set_stream_he_eht_caps(struct mt792x_phy *phy, + struct ieee80211_supported_band *sband, + enum nl80211_band band) +{ + struct ieee80211_sband_iftype_data *data = phy->iftype[band]; + int i, n = 0; + + for (i = 0; i < NUM_NL80211_IFTYPES; i++) { + switch (i) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_AP: + break; + default: + continue; + } + + data[n].types_mask = BIT(i); + mt7925_init_he_caps(phy, band, &data[n], i); + mt7925_init_eht_caps(phy, band, &data[n]); + + n++; + } + + _ieee80211_set_sband_iftype_data(sband, data, n); +} + +void mt7925_set_stream_he_eht_caps(struct mt792x_phy *phy) +{ + if (phy->mt76->cap.has_2ghz) + __mt7925_set_stream_he_eht_caps(phy, &phy->mt76->sband_2g.sband, + NL80211_BAND_2GHZ); + + if (phy->mt76->cap.has_5ghz) + __mt7925_set_stream_he_eht_caps(phy, &phy->mt76->sband_5g.sband, + NL80211_BAND_5GHZ); + + if (phy->mt76->cap.has_6ghz) + __mt7925_set_stream_he_eht_caps(phy, &phy->mt76->sband_6g.sband, + NL80211_BAND_6GHZ); +} + +int __mt7925_start(struct mt792x_phy *phy) +{ + struct mt76_phy *mphy = phy->mt76; + int err; + + err = mt7925_mcu_set_channel_domain(mphy); + if (err) + return err; + + err = mt7925_mcu_set_rts_thresh(phy, 0x92b); + if (err) + return err; + + mt792x_mac_reset_counters(phy); + set_bit(MT76_STATE_RUNNING, &mphy->state); + + ieee80211_queue_delayed_work(mphy->hw, &mphy->mac_work, + MT792x_WATCHDOG_TIME); + + if (phy->chip_cap & MT792x_CHIP_CAP_WF_RF_PIN_CTRL_EVT_EN) + wiphy_rfkill_start_polling(mphy->hw->wiphy); + + return 0; +} +EXPORT_SYMBOL_GPL(__mt7925_start); + +static int mt7925_start(struct ieee80211_hw *hw) +{ + struct mt792x_phy *phy = mt792x_hw_phy(hw); + int err; + + mt792x_mutex_acquire(phy->dev); + err = __mt7925_start(phy); + mt792x_mutex_release(phy->dev); + + return err; +} + +static int mt7925_mac_link_bss_add(struct mt792x_dev *dev, + struct ieee80211_bss_conf *link_conf, + struct mt792x_link_sta *mlink) +{ + struct mt792x_bss_conf *mconf = mt792x_link_conf_to_mconf(link_conf); + struct ieee80211_vif *vif = link_conf->vif; + struct mt792x_vif *mvif = mconf->vif; + struct mt76_txq *mtxq; + int idx, ret = 0; + + if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { + mconf->mt76.idx = MT792x_MAX_INTERFACES; + } else { + mconf->mt76.idx = __ffs64(~dev->mt76.vif_mask); + + if (mconf->mt76.idx >= MT792x_MAX_INTERFACES) { + ret = -ENOSPC; + goto out; + } + } + + mconf->mt76.omac_idx = ieee80211_vif_is_mld(vif) ? + 0 : mconf->mt76.idx; + mconf->mt76.band_idx = 0xff; + + if (is_mt7927(&dev->mt76)) { + struct ieee80211_channel *chan; + + if (link_conf->chanreq.oper.chan) + chan = link_conf->chanreq.oper.chan; + else + chan = mvif->phy->mt76->chandef.chan; + + mconf->mt76.band_idx = mt7927_band_idx(chan->band); + } + + mconf->mt76.wmm_idx = ieee80211_vif_is_mld(vif) ? + 0 : mconf->mt76.idx % MT76_CONNAC_MAX_WMM_SETS; + mconf->mt76.link_idx = hweight16(mvif->valid_links); + + if (mvif->phy->mt76->chandef.chan && + mvif->phy->mt76->chandef.chan->band != NL80211_BAND_2GHZ) + mconf->mt76.basic_rates_idx = MT792x_BASIC_RATES_TBL + 4; + else + mconf->mt76.basic_rates_idx = MT792x_BASIC_RATES_TBL; + + dev->mt76.vif_mask |= BIT_ULL(mconf->mt76.idx); + mvif->phy->omac_mask |= BIT_ULL(mconf->mt76.omac_idx); + + idx = MT792x_WTBL_RESERVED - mconf->mt76.idx; + + mlink->wcid.idx = idx; + mlink->wcid.tx_info |= MT_WCID_TX_INFO_SET; + mt76_wcid_init(&mlink->wcid, 0); + + mt7925_mac_wtbl_update(dev, idx, + MT_WTBL_UPDATE_ADM_COUNT_CLEAR); + + ewma_rssi_init(&mconf->rssi); + + rcu_assign_pointer(dev->mt76.wcid[idx], &mlink->wcid); + + ret = mt76_connac_mcu_uni_add_dev(&dev->mphy, link_conf, &mconf->mt76, + &mlink->wcid, true); + if (ret) + goto out; + + if (vif->txq) { + mtxq = (struct mt76_txq *)vif->txq->drv_priv; + mtxq->wcid = idx; + } + +out: + return ret; +} + +static int +mt7925_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + struct mt792x_dev *dev = mt792x_hw_dev(hw); + struct mt792x_phy *phy = mt792x_hw_phy(hw); + int ret = 0; + + mt792x_mutex_acquire(dev); + + mvif->phy = phy; + mvif->bss_conf.vif = mvif; + mvif->sta.vif = mvif; + mvif->deflink_id = IEEE80211_LINK_UNSPECIFIED; + mvif->mlo_pm_state = MT792x_MLO_LINK_DISASSOC; + + ret = mt7925_mac_link_bss_add(dev, &vif->bss_conf, &mvif->sta.deflink); + if (ret < 0) + goto out; + + vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER; + if (phy->chip_cap & MT792x_CHIP_CAP_RSSI_NOTIFY_EVT_EN) + vif->driver_flags |= IEEE80211_VIF_SUPPORTS_CQM_RSSI; + + INIT_WORK(&mvif->csa_work, mt7925_csa_work); + timer_setup(&mvif->csa_timer, mt792x_csa_timer, 0); + + if (vif->type == NL80211_IFTYPE_NAN) + dev->nan_vif = vif; +out: + mt792x_mutex_release(dev); + + return ret; +} + +static void +mt7925_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + struct mt792x_dev *dev = mt792x_hw_dev(hw); + struct mt792x_bss_conf *mconf; + + mt792x_mutex_acquire(dev); + + if (dev->nan_vif == vif) + dev->nan_vif = NULL; + + mconf = mt792x_link_conf_to_mconf(&vif->bss_conf); + mt792x_mac_link_bss_remove(dev, mconf, &mvif->sta.deflink); + + mt792x_mutex_release(dev); +} + +static void mt7925_roc_iter(void *priv, u8 *mac, + struct ieee80211_vif *vif) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + struct mt792x_phy *phy = priv; + + mt7925_mcu_abort_roc(phy, &mvif->bss_conf, phy->roc_token_id); +} + +void mt7925_roc_abort_sync(struct mt792x_dev *dev) +{ + struct mt792x_phy *phy = &dev->phy; + + if (!test_and_clear_bit(MT76_STATE_ROC, &phy->mt76->state)) + return; + + timer_delete_sync(&phy->roc_timer); + + cancel_work(&phy->roc_work); + + ieee80211_iterate_interfaces(mt76_hw(dev), + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7925_roc_iter, (void *)phy); +} +EXPORT_SYMBOL_GPL(mt7925_roc_abort_sync); + +void mt7925_roc_work(struct work_struct *work) +{ + struct mt792x_phy *phy; + + phy = (struct mt792x_phy *)container_of(work, struct mt792x_phy, + roc_work); + + if (!test_and_clear_bit(MT76_STATE_ROC, &phy->mt76->state)) + return; + + mt792x_mutex_acquire(phy->dev); + ieee80211_iterate_active_interfaces(phy->mt76->hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7925_roc_iter, phy); + mt792x_mutex_release(phy->dev); + ieee80211_remain_on_channel_expired(phy->mt76->hw); +} + +static int mt7925_abort_roc(struct mt792x_phy *phy, + struct mt792x_bss_conf *mconf) +{ + int err = 0; + + timer_delete_sync(&phy->roc_timer); + cancel_work_sync(&phy->roc_work); + + mt792x_mutex_acquire(phy->dev); + if (test_and_clear_bit(MT76_STATE_ROC, &phy->mt76->state)) + err = mt7925_mcu_abort_roc(phy, mconf, phy->roc_token_id); + mt792x_mutex_release(phy->dev); + + return err; +} + +static int mt7925_set_roc(struct mt792x_phy *phy, + struct mt792x_bss_conf *mconf, + struct ieee80211_channel *chan, + int duration, + enum mt7925_roc_req type) +{ + int err; + + if (test_and_set_bit(MT76_STATE_ROC, &phy->mt76->state)) + return -EBUSY; + + phy->roc_grant = false; + + err = mt7925_mcu_set_roc(phy, mconf, chan, duration, type, + ++phy->roc_token_id); + if (err < 0) { + clear_bit(MT76_STATE_ROC, &phy->mt76->state); + goto out; + } + + if (!wait_event_timeout(phy->roc_wait, phy->roc_grant, 4 * HZ)) { + mt7925_mcu_abort_roc(phy, mconf, phy->roc_token_id); + clear_bit(MT76_STATE_ROC, &phy->mt76->state); + err = -ETIMEDOUT; + } + +out: + return err; +} + +static int mt7925_set_mlo_roc(struct mt792x_phy *phy, + struct mt792x_bss_conf *mconf, + u16 sel_links) +{ + int err; + + if (WARN_ON_ONCE(test_and_set_bit(MT76_STATE_ROC, &phy->mt76->state))) + return -EBUSY; + + phy->roc_grant = false; + + err = mt7925_mcu_set_mlo_roc(phy, mconf, sel_links, 5, ++phy->roc_token_id); + if (err < 0) { + clear_bit(MT76_STATE_ROC, &phy->mt76->state); + goto out; + } + + if (!wait_event_timeout(phy->roc_wait, phy->roc_grant, 4 * HZ)) { + mt7925_mcu_abort_roc(phy, mconf, phy->roc_token_id); + clear_bit(MT76_STATE_ROC, &phy->mt76->state); + err = -ETIMEDOUT; + } + +out: + return err; +} + +static int mt7925_remain_on_channel(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_channel *chan, + int duration, + enum ieee80211_roc_type type) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + struct mt792x_phy *phy = mt792x_hw_phy(hw); + int err; + + mt792x_mutex_acquire(phy->dev); + err = mt7925_set_roc(phy, &mvif->bss_conf, + chan, duration, MT7925_ROC_REQ_ROC); + mt792x_mutex_release(phy->dev); + + return err; +} + +static int mt7925_cancel_remain_on_channel(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + struct mt792x_phy *phy = mt792x_hw_phy(hw); + + return mt7925_abort_roc(phy, &mvif->bss_conf); +} + +static int mt7925_set_link_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key, int link_id, + struct mt792x_link_sta *mlink) +{ + struct mt792x_dev *dev = mt792x_hw_dev(hw); + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + struct mt792x_sta *msta = sta ? (struct mt792x_sta *)sta->drv_priv : + &mvif->sta; + struct ieee80211_bss_conf *link_conf; + struct ieee80211_link_sta *link_sta; + int idx = key->keyidx, err = 0; + struct mt792x_bss_conf *mconf; + struct mt76_wcid *wcid; + u8 *wcid_keyidx; + + link_conf = mt792x_vif_to_bss_conf(vif, link_id); + link_sta = sta ? mt792x_sta_to_link_sta(vif, sta, link_id) : NULL; + mconf = mt792x_vif_to_link(mvif, link_id); + wcid = &mlink->wcid; + wcid_keyidx = &wcid->hw_key_idx; + + /* fall back to sw encryption for unsupported ciphers */ + switch (key->cipher) { + case WLAN_CIPHER_SUITE_AES_CMAC: + key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIE; + wcid_keyidx = &wcid->hw_key_idx2; + break; + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + if (!mvif->wep_sta) + return -EOPNOTSUPP; + break; + case WLAN_CIPHER_SUITE_TKIP: + case WLAN_CIPHER_SUITE_CCMP: + case WLAN_CIPHER_SUITE_CCMP_256: + case WLAN_CIPHER_SUITE_GCMP: + case WLAN_CIPHER_SUITE_GCMP_256: + case WLAN_CIPHER_SUITE_SMS4: + break; + default: + return -EOPNOTSUPP; + } + + if (cmd == SET_KEY && !mconf->mt76.cipher) { + struct mt792x_phy *phy = mt792x_hw_phy(hw); + + mconf->mt76.cipher = mt7925_mcu_get_cipher(key->cipher); + mt7925_mcu_add_bss_info(phy, mconf->mt76.ctx, link_conf, + link_sta, true); + } + + if (cmd == SET_KEY) + *wcid_keyidx = idx; + else if (idx == *wcid_keyidx) + *wcid_keyidx = -1; + else + goto out; + + mt76_wcid_key_setup(&dev->mt76, wcid, + cmd == SET_KEY ? key : NULL); + + err = mt7925_mcu_add_key(&dev->mt76, vif, &mlink->bip, + key, MCU_UNI_CMD(STA_REC_UPDATE), + &mlink->wcid, cmd, msta); + + if (err) + goto out; + + if (key->cipher == WLAN_CIPHER_SUITE_WEP104 || + key->cipher == WLAN_CIPHER_SUITE_WEP40) + err = mt7925_mcu_add_key(&dev->mt76, vif, &mvif->wep_sta->deflink.bip, + key, MCU_WMWA_UNI_CMD(STA_REC_UPDATE), + &mvif->wep_sta->deflink.wcid, cmd, msta); +out: + return err; +} + +static int mt7925_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct mt792x_dev *dev = mt792x_hw_dev(hw); + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + struct mt792x_sta *msta = sta ? (struct mt792x_sta *)sta->drv_priv : + &mvif->sta; + struct mt792x_link_sta *mlink; + int err; + + /* The hardware does not support per-STA RX GTK, fallback + * to software mode for these. + */ + if ((vif->type == NL80211_IFTYPE_ADHOC || + vif->type == NL80211_IFTYPE_MESH_POINT) && + (key->cipher == WLAN_CIPHER_SUITE_TKIP || + key->cipher == WLAN_CIPHER_SUITE_CCMP) && + !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) + return -EOPNOTSUPP; + + mt792x_mutex_acquire(dev); + + if (ieee80211_vif_is_mld(vif)) { + unsigned int link_id; + unsigned long add; + + add = key->link_id != -1 ? BIT(key->link_id) : msta->valid_links; + + for_each_set_bit(link_id, &add, IEEE80211_MLD_MAX_NUM_LINKS) { + mlink = mt792x_sta_to_link(msta, link_id); + err = mt7925_set_link_key(hw, cmd, vif, sta, key, link_id, + mlink); + if (err < 0) + break; + } + } else { + mlink = mt792x_sta_to_link(msta, vif->bss_conf.link_id); + err = mt7925_set_link_key(hw, cmd, vif, sta, key, + vif->bss_conf.link_id, mlink); + } + + mt792x_mutex_release(dev); + + return err; +} + +static void +mt7925_pm_interface_iter(void *priv, u8 *mac, struct ieee80211_vif *vif) +{ + struct mt792x_dev *dev = priv; + struct ieee80211_hw *hw = mt76_hw(dev); + bool pm_enable = dev->pm.enable; + int err; + + err = mt7925_mcu_set_beacon_filter(dev, vif, pm_enable); + if (err < 0) + return; + + if (pm_enable) { + vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER; + ieee80211_hw_set(hw, CONNECTION_MONITOR); + } else { + vif->driver_flags &= ~IEEE80211_VIF_BEACON_FILTER; + __clear_bit(IEEE80211_HW_CONNECTION_MONITOR, hw->flags); + } +} + +static void +mt7925_sniffer_interface_iter(void *priv, u8 *mac, struct ieee80211_vif *vif) +{ + struct mt792x_dev *dev = priv; + struct ieee80211_hw *hw = mt76_hw(dev); + struct mt76_connac_pm *pm = &dev->pm; + bool monitor = !!(hw->conf.flags & IEEE80211_CONF_MONITOR); + + mt7925_mcu_set_sniffer(dev, vif, monitor); + pm->enable = pm->enable_user && !monitor; + pm->ds_enable = pm->ds_enable_user && !monitor; + + mt7925_mcu_set_deep_sleep(dev, pm->ds_enable); + + if (monitor) + mt7925_mcu_set_beacon_filter(dev, vif, false); +} + +void mt7925_set_runtime_pm(struct mt792x_dev *dev) +{ + struct ieee80211_hw *hw = mt76_hw(dev); + struct mt76_connac_pm *pm = &dev->pm; + bool monitor = !!(hw->conf.flags & IEEE80211_CONF_MONITOR); + + pm->enable = pm->enable_user && !monitor; + ieee80211_iterate_active_interfaces(hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7925_pm_interface_iter, dev); + pm->ds_enable = pm->ds_enable_user && !monitor; + mt7925_mcu_set_deep_sleep(dev, pm->ds_enable); +} + +static int mt7925_config(struct ieee80211_hw *hw, int radio_idx, u32 changed) +{ + struct mt792x_dev *dev = mt792x_hw_dev(hw); + int ret = 0; + + mt792x_mutex_acquire(dev); + + if (changed & IEEE80211_CONF_CHANGE_POWER) { + ret = mt7925_set_tx_sar_pwr(hw, NULL); + if (ret) + goto out; + } + + if (changed & IEEE80211_CONF_CHANGE_MONITOR) { + ieee80211_iterate_active_interfaces(hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7925_sniffer_interface_iter, dev); + } + +out: + mt792x_mutex_release(dev); + + return ret; +} + +static void mt7925_configure_filter(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *total_flags, + u64 multicast) +{ +#define MT7925_FILTER_FCSFAIL BIT(2) +#define MT7925_FILTER_CONTROL BIT(5) +#define MT7925_FILTER_OTHER_BSS BIT(6) +#define MT7925_FILTER_ENABLE BIT(31) + struct mt792x_dev *dev = mt792x_hw_dev(hw); + u32 flags = MT7925_FILTER_ENABLE; + +#define MT7925_FILTER(_fif, _type) do { \ + if (*total_flags & (_fif)) \ + flags |= MT7925_FILTER_##_type; \ + } while (0) + + MT7925_FILTER(FIF_FCSFAIL, FCSFAIL); + MT7925_FILTER(FIF_CONTROL, CONTROL); + MT7925_FILTER(FIF_OTHER_BSS, OTHER_BSS); + + mt792x_mutex_acquire(dev); + mt7925_mcu_set_rxfilter(dev, flags, 0, 0); + mt792x_mutex_release(dev); + + *total_flags &= (FIF_OTHER_BSS | FIF_FCSFAIL | FIF_CONTROL); +} + +static u8 +mt7925_get_rates_table(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + bool beacon, bool mcast) +{ + struct mt76_vif_link *mvif = (struct mt76_vif_link *)vif->drv_priv; + struct mt76_phy *mphy = hw->priv; + u16 rate; + u8 i, idx, ht; + + rate = mt76_connac2_mac_tx_rate_val(mphy, &vif->bss_conf, beacon, mcast); + ht = FIELD_GET(MT_TX_RATE_MODE, rate) > MT_PHY_TYPE_OFDM; + + if (beacon && ht) { + struct mt792x_dev *dev = mt792x_hw_dev(hw); + + /* must odd index */ + idx = MT7925_BEACON_RATES_TBL + 2 * (mvif->idx % 20); + mt7925_mac_set_fixed_rate_table(dev, idx, rate); + return idx; + } + + idx = FIELD_GET(MT_TX_RATE_IDX, rate); + for (i = 0; i < ARRAY_SIZE(mt76_rates); i++) + if ((mt76_rates[i].hw_value & GENMASK(7, 0)) == idx) + return MT792x_BASIC_RATES_TBL + i; + + return mvif->basic_rates_idx; +} + +static int mt7925_mac_link_sta_add(struct mt76_dev *mdev, + struct ieee80211_vif *vif, + struct ieee80211_link_sta *link_sta, + struct mt792x_link_sta *mlink) +{ + struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + struct ieee80211_bss_conf *link_conf; + struct mt792x_bss_conf *mconf; + u8 link_id = link_sta->link_id; + bool wcid_published = false; + struct mt792x_sta *msta; + struct mt76_wcid *wcid; + bool pm_woken = false; + int ret, idx; + + msta = (struct mt792x_sta *)link_sta->sta->drv_priv; + + if (WARN_ON_ONCE(!mlink)) + return -EINVAL; + + idx = mt76_wcid_alloc(dev->mt76.wcid_mask, MT792x_WTBL_STA - 1); + if (idx < 0) + return -ENOSPC; + + mconf = mt792x_vif_to_link(mvif, link_id); + mt76_wcid_init(&mlink->wcid, 0); + mlink->wcid.sta = 1; + mlink->wcid.idx = idx; + mlink->wcid.tx_info |= MT_WCID_TX_INFO_SET; + mlink->last_txs = jiffies; + mlink->wcid.link_id = link_sta->link_id; + mlink->wcid.link_valid = !!link_sta->sta->valid_links; + mlink->sta = msta; + + if (link_sta->sta->tdls) + set_bit(MT_WCID_FLAG_TDLS_PEER, &mlink->wcid.flags); + + wcid = &mlink->wcid; + ewma_signal_init(&wcid->rssi); + rcu_assign_pointer(dev->mt76.wcid[wcid->idx], wcid); + wcid_published = true; + ewma_avg_signal_init(&mlink->avg_ack_signal); + memset(mlink->airtime_ac, 0, + sizeof(msta->deflink.airtime_ac)); + + ret = mt76_connac_pm_wake(&dev->mphy, &dev->pm); + if (ret) + goto out_wcid; + pm_woken = true; + + mt7925_mac_wtbl_update(dev, idx, + MT_WTBL_UPDATE_ADM_COUNT_CLEAR); + + link_conf = mt792x_vif_to_bss_conf(vif, link_id); + + /* should update bss info before STA add */ + if (vif->type == NL80211_IFTYPE_STATION && !link_sta->sta->tdls) { + struct mt792x_link_sta *mlink_bc; + + mlink_bc = mt792x_sta_to_link(&mvif->sta, mconf->link_id); + + if (ieee80211_vif_is_mld(vif)) { + ret = mt7925_mcu_add_bss_info_sta(&dev->phy, mconf->mt76.ctx, + link_conf, link_sta, + mlink_bc->wcid.idx, mlink->wcid.idx, + link_sta != mlink->pri_link); + if (ret) + goto out_pm; + } else { + ret = mt7925_mcu_add_bss_info_sta(&dev->phy, mconf->mt76.ctx, + link_conf, link_sta, + mlink_bc->wcid.idx, mlink->wcid.idx, + false); + if (ret) + goto out_pm; + } + } + + if (ieee80211_vif_is_mld(vif) && + link_sta == mlink->pri_link) { + ret = mt7925_mcu_sta_update(dev, link_sta, vif, + mlink, true, + MT76_STA_INFO_STATE_NONE); + if (ret) + goto out_pm; + } else if (ieee80211_vif_is_mld(vif) && + link_sta != mlink->pri_link) { + struct mt792x_link_sta *pri_mlink; + struct mt76_wcid *pri_wcid; + + /* alternative lookup via def_wcid */ + pri_wcid = mlink->wcid.def_wcid; + + pri_mlink = pri_wcid ? + container_of(pri_wcid, struct mt792x_link_sta, wcid) : + NULL; + + if (WARN_ON_ONCE(!pri_mlink)) { + ret = -EINVAL; + goto out_pm; + } + + ret = mt7925_mcu_sta_update(dev, mlink->pri_link, vif, + pri_mlink, true, + MT76_STA_INFO_STATE_ASSOC); + if (ret) + goto out_pm; + + ret = mt7925_mcu_sta_update(dev, link_sta, vif, + mlink, true, + MT76_STA_INFO_STATE_ASSOC); + if (ret) + goto out_pm; + } else { + ret = mt7925_mcu_sta_update(dev, link_sta, vif, + mlink, true, + MT76_STA_INFO_STATE_NONE); + if (ret) + goto out_pm; + } + + mt76_connac_power_save_sched(&dev->mphy, &dev->pm); + + return 0; + +out_pm: + if (pm_woken) + mt76_connac_power_save_sched(&dev->mphy, &dev->pm); +out_wcid: + if (wcid_published) { + u16 idx = wcid->idx; + + rcu_assign_pointer(dev->mt76.wcid[idx], NULL); + mt76_wcid_cleanup(mdev, wcid); + mt76_wcid_mask_clear(mdev->wcid_mask, wcid->idx); + } + return ret; +} + +/* + * Host-only unwind for sta_add_links() failures. + * + * If add_links fail due to MCU/firmware timeouts; calling the full remove + * path would send more firmware commands and may hang again. So only rollback + * host-published state here (msta->link/valid_links, dev->mt76.wcid[idx]) and + * free mlink objects (RCU-safe). Firmware state is left for reset/recovery. + */ +static void +mt7925_mac_sta_unwind_links_host(struct mt792x_dev *dev, + struct ieee80211_sta *sta, + unsigned long links) +{ + struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv; + unsigned int link_id; + + for_each_set_bit(link_id, &links, IEEE80211_MLD_MAX_NUM_LINKS) { + struct mt792x_link_sta *mlink; + u16 idx; + + mlink = rcu_replace_pointer(msta->link[link_id], NULL, + lockdep_is_held(&dev->mt76.mutex)); + if (!mlink) + continue; + + msta->valid_links &= ~BIT(link_id); + if (msta->deflink_id == link_id) + msta->deflink_id = IEEE80211_LINK_UNSPECIFIED; + + idx = mlink->wcid.idx; + rcu_assign_pointer(dev->mt76.wcid[idx], NULL); + mt76_wcid_cleanup(&dev->mt76, &mlink->wcid); + mt76_wcid_mask_clear(dev->mt76.wcid_mask, idx); + + if (mlink != &msta->deflink) + kfree_rcu(mlink, rcu_head); + } +} + +static int +mt7925_mac_sta_add_links(struct mt792x_dev *dev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, unsigned long new_links) +{ + struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv; + unsigned long added_links = 0; + unsigned int link_id; + int err = 0; + + for_each_set_bit(link_id, &new_links, IEEE80211_MLD_MAX_NUM_LINKS) { + struct ieee80211_link_sta *link_sta; + struct mt792x_link_sta *mlink; + bool is_deflink = false; + + if (msta->deflink_id == IEEE80211_LINK_UNSPECIFIED) { + mlink = &msta->deflink; + is_deflink = true; + } else { + mlink = kzalloc(sizeof(*mlink), GFP_KERNEL); + if (!mlink) { + err = -ENOMEM; + break; + } + } + + mlink->sta = msta; + mlink->pri_link = &sta->deflink; + mlink->wcid.def_wcid = &msta->deflink.wcid; + + link_sta = mt792x_sta_to_link_sta(vif, sta, link_id); + err = mt7925_mac_link_sta_add(&dev->mt76, vif, link_sta, mlink); + if (err) { + if (!is_deflink) + kfree_rcu(mlink, rcu_head); + break; + } + + if (is_deflink) + msta->deflink_id = link_id; + + rcu_assign_pointer(msta->link[link_id], mlink); + msta->valid_links |= BIT(link_id); + + added_links |= BIT(link_id); + } + + if (err && added_links) + mt7925_mac_sta_unwind_links_host(dev, sta, added_links); + + return err; +} + +int mt7925_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv; + int err; + + msta->vif = mvif; + + if (vif->type == NL80211_IFTYPE_STATION) + mvif->wep_sta = msta; + + if (ieee80211_vif_is_mld(vif)) { + msta->deflink_id = IEEE80211_LINK_UNSPECIFIED; + + err = mt7925_mac_sta_add_links(dev, vif, sta, sta->valid_links); + } else { + err = mt7925_mac_link_sta_add(mdev, vif, &sta->deflink, + &msta->deflink); + } + + return err; +} +EXPORT_SYMBOL_GPL(mt7925_mac_sta_add); + +static void +mt7925_mac_set_links(struct mt76_dev *mdev, struct ieee80211_vif *vif) +{ + struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + struct ieee80211_bss_conf *link_conf = + mt792x_vif_to_bss_conf(vif, mvif->deflink_id); + struct cfg80211_chan_def *chandef = &link_conf->chanreq.oper; + enum nl80211_band band = chandef->chan->band, secondary_band; + + u16 sel_links = mt76_select_links(vif, 2); + u8 secondary_link_id = __ffs(~BIT(mvif->deflink_id) & sel_links); + + if (!ieee80211_vif_is_mld(vif) || hweight16(sel_links) < 2) + return; + + link_conf = mt792x_vif_to_bss_conf(vif, secondary_link_id); + secondary_band = link_conf->chanreq.oper.chan->band; + + if (band == NL80211_BAND_2GHZ || + (band == NL80211_BAND_5GHZ && secondary_band == NL80211_BAND_6GHZ)) { + mt7925_abort_roc(mvif->phy, &mvif->bss_conf); + + mt792x_mutex_acquire(dev); + + mt7925_set_mlo_roc(mvif->phy, &mvif->bss_conf, sel_links); + + mt792x_mutex_release(dev); + } + + ieee80211_set_active_links_async(vif, sel_links); +} + +static void mt7925_mac_link_sta_assoc(struct mt76_dev *mdev, + struct ieee80211_vif *vif, + struct ieee80211_link_sta *link_sta) +{ + struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); + struct ieee80211_bss_conf *link_conf; + struct mt792x_link_sta *mlink; + struct mt792x_sta *msta; + + mt792x_mutex_acquire(dev); + + msta = (struct mt792x_sta *)link_sta->sta->drv_priv; + mlink = mt792x_sta_to_link(msta, link_sta->link_id); + + if (ieee80211_vif_is_mld(vif)) { + link_conf = mt792x_vif_to_bss_conf(vif, msta->deflink_id); + } else { + link_conf = mt792x_vif_to_bss_conf(vif, vif->bss_conf.link_id); + } + + if (vif->type == NL80211_IFTYPE_STATION && !link_sta->sta->tdls) { + struct mt792x_bss_conf *mconf; + + mconf = mt792x_link_conf_to_mconf(link_conf); + mt7925_mcu_add_bss_info(&dev->phy, mconf->mt76.ctx, + link_conf, link_sta, true); + } + + ewma_avg_signal_init(&mlink->avg_ack_signal); + + mt7925_mac_wtbl_update(dev, mlink->wcid.idx, + MT_WTBL_UPDATE_ADM_COUNT_CLEAR); + memset(mlink->airtime_ac, 0, sizeof(mlink->airtime_ac)); + + mt7925_mcu_sta_update(dev, link_sta, vif, mlink, true, + MT76_STA_INFO_STATE_ASSOC); + + mt792x_mutex_release(dev); +} + +int mt7925_mac_sta_event(struct mt76_dev *mdev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, enum mt76_sta_event ev) +{ + struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); + struct ieee80211_link_sta *link_sta = &sta->deflink; + + switch (ev) { + case MT76_STA_EVENT_ASSOC: + if (ieee80211_vif_is_mld(vif)) { + struct mt792x_sta *msta = + (struct mt792x_sta *)sta->drv_priv; + + link_sta = mt792x_sta_to_link_sta(vif, sta, + msta->deflink_id); + mt7925_mac_set_links(mdev, vif); + } + + mt7925_mac_link_sta_assoc(mdev, vif, link_sta); + break; + case MT76_STA_EVENT_AUTHORIZE: + if (vif->type == NL80211_IFTYPE_NAN_DATA) { + int ret; + + mt792x_mutex_acquire(dev); + ret = mt792x_nan_map_sta_rec(mdev, vif, sta); + mt792x_mutex_release(dev); + + return ret; + } + break; + default: + break; + } + + return 0; +} +EXPORT_SYMBOL_GPL(mt7925_mac_sta_event); + +static void mt7925_mac_link_sta_remove(struct mt76_dev *mdev, + struct ieee80211_vif *vif, + struct ieee80211_link_sta *link_sta, + struct mt792x_link_sta *mlink) +{ + struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); + struct mt76_wcid *wcid = &mlink->wcid; + struct ieee80211_bss_conf *link_conf; + u8 link_id = link_sta->link_id; + u16 idx = wcid->idx; + + mt7925_roc_abort_sync(dev); + + mt76_connac_free_pending_tx_skbs(&dev->pm, wcid); + mt76_connac_pm_wake(&dev->mphy, &dev->pm); + + mt7925_mcu_sta_update(dev, link_sta, vif, mlink, false, + MT76_STA_INFO_STATE_NONE); + mt7925_mac_wtbl_update(dev, mlink->wcid.idx, + MT_WTBL_UPDATE_ADM_COUNT_CLEAR); + + link_conf = mt792x_vif_to_bss_conf(vif, link_id); + + if (vif->type == NL80211_IFTYPE_STATION && !link_sta->sta->tdls) { + struct mt792x_bss_conf *mconf; + + mconf = mt792x_link_conf_to_mconf(link_conf); + + if (ieee80211_vif_is_mld(vif)) + mt792x_mac_link_bss_remove(dev, mconf, mlink); + else + mt7925_mcu_add_bss_info(&dev->phy, mconf->mt76.ctx, link_conf, + link_sta, false); + } + + spin_lock_bh(&mdev->sta_poll_lock); + if (!list_empty(&mlink->wcid.poll_list)) + list_del_init(&mlink->wcid.poll_list); + spin_unlock_bh(&mdev->sta_poll_lock); + + rcu_assign_pointer(dev->mt76.wcid[idx], NULL); + mt76_wcid_cleanup(mdev, wcid); + mt76_wcid_mask_clear(mdev->wcid_mask, idx); + + mt76_connac_power_save_sched(&dev->mphy, &dev->pm); +} + +static int +mt7925_mac_sta_remove_links(struct mt792x_dev *dev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, unsigned long old_links) +{ + struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv; + struct mt76_dev *mdev = &dev->mt76; + unsigned int link_id; + + /* clean up bss before starec */ + for_each_set_bit(link_id, &old_links, IEEE80211_MLD_MAX_NUM_LINKS) { + struct ieee80211_link_sta *link_sta; + struct ieee80211_bss_conf *link_conf; + struct mt792x_bss_conf *mconf; + struct mt792x_link_sta *mlink; + + if (vif->type == NL80211_IFTYPE_AP) + break; + + if (vif->type == NL80211_IFTYPE_STATION && sta->tdls) + continue; + + link_sta = mt792x_sta_to_link_sta(vif, sta, link_id); + if (!link_sta) + continue; + + mlink = mt792x_sta_to_link(msta, link_id); + if (!mlink) + continue; + + link_conf = mt792x_vif_to_bss_conf(vif, link_id); + if (!link_conf) + continue; + + mconf = mt792x_link_conf_to_mconf(link_conf); + + mt7925_mcu_add_bss_info(&dev->phy, mconf->mt76.ctx, link_conf, + link_sta, false); + } + + for_each_set_bit(link_id, &old_links, IEEE80211_MLD_MAX_NUM_LINKS) { + struct ieee80211_link_sta *link_sta; + struct mt792x_link_sta *mlink; + + link_sta = mt792x_sta_to_link_sta(vif, sta, link_id); + if (!link_sta) + continue; + + mlink = rcu_replace_pointer(msta->link[link_id], NULL, + lockdep_is_held(&mdev->mutex)); + if (!mlink) + continue; + + msta->valid_links &= ~BIT(link_id); + mlink->sta = NULL; + mlink->pri_link = NULL; + + mt7925_mac_link_sta_remove(&dev->mt76, vif, link_sta, mlink); + + if (mlink != &msta->deflink) + kfree_rcu(mlink, rcu_head); + + if (msta->deflink_id == link_id) + msta->deflink_id = IEEE80211_LINK_UNSPECIFIED; + } + + return 0; +} + +void mt7925_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); + struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv; + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + + /* Release NAN peer record before tearing down the STA. */ + if (vif->type == NL80211_IFTYPE_NAN || + vif->type == NL80211_IFTYPE_NAN_DATA) { + int ret = mt792x_nan_set_peer_rec(mdev, sta); + + if (ret) + dev_err(mdev->dev, + "NAN: failed to deactivate peer record: %d\n", + ret); + } + + /* Release NDP context ID for NAN_DATA sta. */ + if (vif->type == NL80211_IFTYPE_NAN_DATA) { + struct ieee80211_sta *nmi_sta; + + rcu_read_lock(); + nmi_sta = rcu_dereference(sta->nmi); + if (nmi_sta) { + struct mt792x_sta *nmi_msta = + (struct mt792x_sta *)nmi_sta->drv_priv; + + if (msta->nan_sched.ndp_ctx_assigned) { + clear_bit(msta->nan_sched.ndp_ctx_id, + &nmi_msta->nan_sched.ndp_ctx_bitmap); + msta->nan_sched.ndp_ctx_assigned = false; + } + } + rcu_read_unlock(); + } + + if (ieee80211_vif_is_mld(vif)) { + mt7925_mac_sta_remove_links(dev, vif, sta, msta->valid_links); + mt7925_mcu_del_dev(mdev, vif); + } else { + mt7925_mac_link_sta_remove(mdev, vif, &sta->deflink, + &msta->deflink); + } + + if (vif->type == NL80211_IFTYPE_STATION) { + mvif->wep_sta = NULL; + ewma_rssi_init(&mvif->bss_conf.rssi); + } + + mvif->mlo_pm_state = MT792x_MLO_LINK_DISASSOC; +} +EXPORT_SYMBOL_GPL(mt7925_mac_sta_remove); + +static int mt7925_set_rts_threshold(struct ieee80211_hw *hw, int radio_idx, + u32 val) +{ + struct mt792x_dev *dev = mt792x_hw_dev(hw); + + mt792x_mutex_acquire(dev); + mt7925_mcu_set_rts_thresh(&dev->phy, val); + mt792x_mutex_release(dev); + + return 0; +} + +static int +mt7925_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_ampdu_params *params) +{ + enum ieee80211_ampdu_mlme_action action = params->action; + struct mt792x_dev *dev = mt792x_hw_dev(hw); + struct ieee80211_sta *sta = params->sta; + struct ieee80211_txq *txq = sta->txq[params->tid]; + struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv; + u16 tid = params->tid; + u16 ssn = params->ssn; + struct mt76_txq *mtxq; + int ret = 0; + + if (!txq) + return -EINVAL; + + mtxq = (struct mt76_txq *)txq->drv_priv; + + mt792x_mutex_acquire(dev); + switch (action) { + case IEEE80211_AMPDU_RX_START: + mt76_rx_aggr_start(&dev->mt76, &msta->deflink.wcid, tid, ssn, + params->buf_size); + mt7925_mcu_uni_rx_ba(dev, params, vif, true); + break; + case IEEE80211_AMPDU_RX_STOP: + mt76_rx_aggr_stop(&dev->mt76, &msta->deflink.wcid, tid); + mt7925_mcu_uni_rx_ba(dev, params, vif, false); + break; + case IEEE80211_AMPDU_TX_OPERATIONAL: + mtxq->aggr = true; + mtxq->send_bar = false; + mt7925_mcu_uni_tx_ba(dev, params, vif, true); + break; + case IEEE80211_AMPDU_TX_STOP_FLUSH: + case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: + mtxq->aggr = false; + clear_bit(tid, &msta->deflink.wcid.ampdu_state); + mt7925_mcu_uni_tx_ba(dev, params, vif, false); + break; + case IEEE80211_AMPDU_TX_START: + set_bit(tid, &msta->deflink.wcid.ampdu_state); + ret = IEEE80211_AMPDU_TX_START_IMMEDIATE; + break; + case IEEE80211_AMPDU_TX_STOP_CONT: + mtxq->aggr = false; + clear_bit(tid, &msta->deflink.wcid.ampdu_state); + mt7925_mcu_uni_tx_ba(dev, params, vif, false); + ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); + break; + } + mt792x_mutex_release(dev); + + return ret; +} + +static void +mt7925_mlo_pm_iter(void *priv, u8 *mac, struct ieee80211_vif *vif) +{ + struct mt792x_dev *dev = priv; + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + unsigned long valid = ieee80211_vif_is_mld(vif) ? + mvif->valid_links : BIT(0); + struct ieee80211_bss_conf *bss_conf; + int i; + + if (mvif->mlo_pm_state != MT792x_MLO_CHANGED_PS) + return; + + mt792x_mutex_acquire(dev); + for_each_set_bit(i, &valid, IEEE80211_MLD_MAX_NUM_LINKS) { + bss_conf = mt792x_vif_to_bss_conf(vif, i); + mt7925_mcu_uni_bss_ps(dev, bss_conf); + } + mt792x_mutex_release(dev); +} + +void mt7925_mlo_pm_work(struct work_struct *work) +{ + struct mt792x_dev *dev = container_of(work, struct mt792x_dev, + mlo_pm_work.work); + struct ieee80211_hw *hw = mt76_hw(dev); + + ieee80211_iterate_active_interfaces(hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7925_mlo_pm_iter, dev); +} + +void mt7925_scan_work(struct work_struct *work) +{ + struct mt792x_phy *phy; + struct mt792x_dev *dev; + struct mt76_connac_pm *pm; + + phy = (struct mt792x_phy *)container_of(work, struct mt792x_phy, + scan_work.work); + + dev = phy->dev; + pm = &dev->pm; + + if (pm->suspended) + return; + + while (true) { + struct sk_buff *skb; + struct tlv *tlv; + u32 tlv_len; + + spin_lock_bh(&phy->dev->mt76.lock); + skb = __skb_dequeue(&phy->scan_event_list); + spin_unlock_bh(&phy->dev->mt76.lock); + + if (!skb) + break; + + skb_pull(skb, sizeof(struct mt7925_mcu_rxd) + 4); + tlv = (struct tlv *)skb->data; + tlv_len = skb->len; + + mt7925_for_each_tlv(tlv, tlv_len) { + struct mt7925_mcu_scan_chinfo_event *evt; + + switch (le16_to_cpu(tlv->tag)) { + case UNI_EVENT_SCAN_DONE_BASIC: + if (test_and_clear_bit(MT76_HW_SCANNING, &phy->mt76->state)) { + struct cfg80211_scan_info info = { + .aborted = false, + }; + ieee80211_scan_completed(phy->mt76->hw, &info); + } + break; + case UNI_EVENT_SCAN_DONE_CHNLINFO: + if (le16_to_cpu(tlv->len) < sizeof(*tlv) + sizeof(*evt)) + break; + + evt = (struct mt7925_mcu_scan_chinfo_event *)tlv->data; + + mt7925_regd_change(phy, evt->alpha2); + + break; + case UNI_EVENT_SCAN_DONE_NLO: + ieee80211_sched_scan_results(phy->mt76->hw); + break; + default: + break; + } + } + + dev_kfree_skb(skb); + } +} + +static int +mt7925_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_scan_request *req) +{ + struct mt792x_dev *dev = mt792x_hw_dev(hw); + struct mt76_phy *mphy = hw->priv; + int err; + + mt792x_mutex_acquire(dev); + err = mt7925_mcu_hw_scan(mphy, vif, req); + mt792x_mutex_release(dev); + + return err; +} + +static void +mt7925_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + struct mt792x_dev *dev = mt792x_hw_dev(hw); + struct mt76_phy *mphy = hw->priv; + + mt792x_mutex_acquire(dev); + mt7925_mcu_cancel_hw_scan(mphy, vif); + mt792x_mutex_release(dev); +} + +static int +mt7925_start_sched_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct cfg80211_sched_scan_request *req, + struct ieee80211_scan_ies *ies) +{ + struct mt792x_dev *dev = mt792x_hw_dev(hw); + struct mt76_phy *mphy = hw->priv; + int err; + + mt792x_mutex_acquire(dev); + + err = mt7925_mcu_sched_scan_req(mphy, vif, req, ies); + if (err < 0) + goto out; + + err = mt7925_mcu_sched_scan_enable(mphy, vif, true); +out: + mt792x_mutex_release(dev); + + return err; +} + +static int +mt7925_stop_sched_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + struct mt792x_dev *dev = mt792x_hw_dev(hw); + struct mt76_phy *mphy = hw->priv; + int err; + + mt792x_mutex_acquire(dev); + err = mt7925_mcu_sched_scan_enable(mphy, vif, false); + mt792x_mutex_release(dev); + + return err; +} + +static int +mt7925_set_antenna(struct ieee80211_hw *hw, int radio_idx, + u32 tx_ant, u32 rx_ant) +{ + struct mt792x_dev *dev = mt792x_hw_dev(hw); + struct mt792x_phy *phy = mt792x_hw_phy(hw); + int max_nss = hweight8(hw->wiphy->available_antennas_tx); + + if (!tx_ant || tx_ant != rx_ant || ffs(tx_ant) > max_nss) + return -EINVAL; + + if ((BIT(hweight8(tx_ant)) - 1) != tx_ant) + tx_ant = BIT(ffs(tx_ant) - 1) - 1; + + mt792x_mutex_acquire(dev); + + phy->mt76->antenna_mask = tx_ant; + phy->mt76->chainmask = tx_ant; + + mt76_set_stream_caps(phy->mt76, true); + mt7925_set_stream_he_eht_caps(phy); + + /* TODO: update bmc_wtbl spe_idx when antenna changes */ + mt792x_mutex_release(dev); + + return 0; +} + +#ifdef CONFIG_PM +static int mt7925_suspend(struct ieee80211_hw *hw, + struct cfg80211_wowlan *wowlan) +{ + struct mt792x_dev *dev = mt792x_hw_dev(hw); + struct mt792x_phy *phy = mt792x_hw_phy(hw); + + cancel_delayed_work_sync(&phy->scan_work); + cancel_delayed_work_sync(&phy->mt76->mac_work); + + cancel_delayed_work_sync(&dev->pm.ps_work); + cancel_delayed_work_sync(&dev->mlo_pm_work); + mt76_connac_free_pending_tx_skbs(&dev->pm, NULL); + + mt792x_mutex_acquire(dev); + + clear_bit(MT76_STATE_RUNNING, &phy->mt76->state); + ieee80211_iterate_active_interfaces(hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7925_mcu_set_suspend_iter, + &dev->mphy); + + mt792x_mutex_release(dev); + + return 0; +} + +static int mt7925_resume(struct ieee80211_hw *hw) +{ + struct mt792x_dev *dev = mt792x_hw_dev(hw); + struct mt792x_phy *phy = mt792x_hw_phy(hw); + + mt792x_mutex_acquire(dev); + + set_bit(MT76_STATE_RUNNING, &phy->mt76->state); + ieee80211_iterate_active_interfaces(hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7925_mcu_set_suspend_iter, + &dev->mphy); + + ieee80211_queue_delayed_work(hw, &phy->mt76->mac_work, + MT792x_WATCHDOG_TIME); + + mt792x_mutex_release(dev); + + return 0; +} + +static void mt7925_set_rekey_data(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_gtk_rekey_data *data) +{ + struct mt792x_dev *dev = mt792x_hw_dev(hw); + + mt792x_mutex_acquire(dev); + mt76_connac_mcu_update_gtk_rekey(hw, vif, data); + mt792x_mutex_release(dev); +} +#endif /* CONFIG_PM */ + +static void mt7925_sta_set_decap_offload(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + bool enabled) +{ + struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv; + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + struct mt792x_dev *dev = mt792x_hw_dev(hw); + unsigned long valid = mvif->valid_links; + u8 i; + + if (!msta->vif) + return; + + mt792x_mutex_acquire(dev); + + valid = ieee80211_vif_is_mld(vif) ? mvif->valid_links : BIT(0); + + for_each_set_bit(i, &valid, IEEE80211_MLD_MAX_NUM_LINKS) { + struct mt792x_bss_conf *mconf; + struct mt792x_link_sta *mlink; + + mconf = mt792x_vif_to_link(mvif, i); + mlink = mt792x_sta_to_link(msta, i); + + if (!mlink) + continue; + + if (enabled) + set_bit(MT_WCID_FLAG_HDR_TRANS, &mlink->wcid.flags); + else + clear_bit(MT_WCID_FLAG_HDR_TRANS, &mlink->wcid.flags); + + if (!mlink->wcid.sta) + continue; + + mt7925_mcu_wtbl_update_hdr_trans(dev, vif, mconf, mlink); + } + + mt792x_mutex_release(dev); +} + +#if IS_ENABLED(CONFIG_IPV6) +static void __mt7925_ipv6_addr_change(struct ieee80211_hw *hw, + struct ieee80211_bss_conf *link_conf, + struct inet6_dev *idev) +{ + struct mt792x_bss_conf *mconf = mt792x_link_conf_to_mconf(link_conf); + struct mt792x_dev *dev = mt792x_hw_dev(hw); + struct inet6_ifaddr *ifa; + struct sk_buff *skb; + u8 idx = 0; + + struct { + struct { + u8 bss_idx; + u8 pad[3]; + } __packed hdr; + struct mt7925_arpns_tlv arpns; + struct in6_addr ns_addrs[IEEE80211_BSS_ARP_ADDR_LIST_LEN]; + } req_hdr = { + .hdr = { + .bss_idx = mconf->mt76.idx, + }, + .arpns = { + .tag = cpu_to_le16(UNI_OFFLOAD_OFFLOAD_ND), + .len = cpu_to_le16(sizeof(req_hdr) - 4), + .enable = true, + }, + }; + + read_lock_bh(&idev->lock); + list_for_each_entry(ifa, &idev->addr_list, if_list) { + if (ifa->flags & IFA_F_TENTATIVE) + continue; + req_hdr.ns_addrs[idx] = ifa->addr; + if (++idx >= IEEE80211_BSS_ARP_ADDR_LIST_LEN) + break; + } + read_unlock_bh(&idev->lock); + + if (!idx) + return; + + req_hdr.arpns.ips_num = idx; + + skb = __mt76_mcu_msg_alloc(&dev->mt76, NULL, sizeof(req_hdr), + 0, GFP_ATOMIC); + if (!skb) + return; + + skb_put_data(skb, &req_hdr, sizeof(req_hdr)); + + skb_queue_tail(&dev->ipv6_ns_list, skb); + + ieee80211_queue_work(dev->mt76.hw, &dev->ipv6_ns_work); +} + +static void mt7925_ipv6_addr_change(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct inet6_dev *idev) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + unsigned long valid = ieee80211_vif_is_mld(vif) ? + mvif->valid_links : BIT(0); + struct ieee80211_bss_conf *bss_conf; + int i; + + for_each_set_bit(i, &valid, IEEE80211_MLD_MAX_NUM_LINKS) { + bss_conf = mt792x_vif_to_bss_conf(vif, i); + __mt7925_ipv6_addr_change(hw, bss_conf, idev); + } +} + +#endif + +int mt7925_set_tx_sar_pwr(struct ieee80211_hw *hw, + const struct cfg80211_sar_specs *sar) +{ + struct mt76_phy *mphy = hw->priv; + + if (sar) { + int err = mt76_init_sar_power(hw, sar); + + if (err) + return err; + } + mt792x_init_acpi_sar_power(mt792x_hw_phy(hw), !sar); + + return mt7925_mcu_set_rate_txpower(mphy); +} + +static int mt7925_set_sar_specs(struct ieee80211_hw *hw, + const struct cfg80211_sar_specs *sar) +{ + struct mt792x_dev *dev = mt792x_hw_dev(hw); + int err; + + mt792x_mutex_acquire(dev); + err = mt7925_mcu_set_clc(dev, dev->mt76.alpha2, + dev->country_ie_env); + if (err < 0) + goto out; + + err = mt7925_set_tx_sar_pwr(hw, sar); + +out: + mt792x_mutex_release(dev); + return err; +} + +static void +mt7925_channel_switch_beacon(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_chan_def *chandef) +{ + struct mt792x_dev *dev = mt792x_hw_dev(hw); + + mt792x_mutex_acquire(dev); + mt7925_mcu_uni_add_beacon_offload(dev, hw, vif, true); + mt792x_mutex_release(dev); +} + +static int +mt7925_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + unsigned int link_id, u16 queue, + const struct ieee80211_tx_queue_params *params) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + struct mt792x_bss_conf *mconf = mt792x_vif_to_link(mvif, link_id); + static const u8 mq_to_aci[] = { + [IEEE80211_AC_VO] = 3, + [IEEE80211_AC_VI] = 2, + [IEEE80211_AC_BE] = 0, + [IEEE80211_AC_BK] = 1, + }; + + /* firmware uses access class index */ + mconf->queue_params[mq_to_aci[queue]] = *params; + + return 0; +} + +static int +mt7925_start_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + struct mt792x_dev *dev = mt792x_hw_dev(hw); + int err; + + mt792x_mutex_acquire(dev); + + err = mt7925_mcu_add_bss_info(&dev->phy, mvif->bss_conf.mt76.ctx, + link_conf, NULL, true); + if (err) + goto out; + + err = mt7925_mcu_set_bss_pm(dev, link_conf, true); + if (err) + goto out; + + err = mt7925_mcu_sta_update(dev, NULL, vif, + &mvif->sta.deflink, true, + MT76_STA_INFO_STATE_NONE); +out: + mt792x_mutex_release(dev); + + return err; +} + +static void +mt7925_stop_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + struct mt792x_dev *dev = mt792x_hw_dev(hw); + int err; + + mt792x_mutex_acquire(dev); + + err = mt7925_mcu_set_bss_pm(dev, link_conf, false); + if (err) + goto out; + + mt7925_mcu_add_bss_info(&dev->phy, mvif->bss_conf.mt76.ctx, link_conf, + NULL, false); + +out: + mt792x_mutex_release(dev); +} + +static int +mt7925_add_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *ctx) +{ + struct mt792x_dev *dev = mt792x_hw_dev(hw); + + dev->new_ctx = ctx; + + return 0; +} + +static void +mt7925_remove_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *ctx) +{ + struct mt792x_dev *dev = mt792x_hw_dev(hw); + + if (dev->new_ctx == ctx) + dev->new_ctx = NULL; + +} + +static void +mt7925_change_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *ctx, + u32 changed) +{ + struct mt792x_chanctx *mctx = (struct mt792x_chanctx *)ctx->drv_priv; + struct mt792x_phy *phy = mt792x_hw_phy(hw); + struct mt792x_bss_conf *mconf; + struct ieee80211_vif *vif; + struct mt792x_vif *mvif; + + if (!mctx->bss_conf) + return; + + mconf = mctx->bss_conf; + mvif = mconf->vif; + vif = container_of((void *)mvif, struct ieee80211_vif, drv_priv); + + mt792x_mutex_acquire(phy->dev); + if (vif->type == NL80211_IFTYPE_MONITOR) { + mt7925_mcu_set_sniffer(mvif->phy->dev, vif, true); + mt7925_mcu_config_sniffer(mvif, ctx); + } else { + if (ieee80211_vif_is_mld(vif)) { + unsigned long valid = mvif->valid_links; + u8 i; + + for_each_set_bit(i, &valid, IEEE80211_MLD_MAX_NUM_LINKS) { + mconf = mt792x_vif_to_link(mvif, i); + if (mconf && mconf->mt76.ctx == ctx) + break; + } + + } else { + mconf = &mvif->bss_conf; + } + + if (mconf) { + struct ieee80211_bss_conf *link_conf; + + link_conf = mt792x_vif_to_bss_conf(vif, mconf->link_id); + mt7925_mcu_set_chctx(mvif->phy->mt76, &mconf->mt76, + link_conf, ctx); + + if (changed & IEEE80211_CHANCTX_CHANGE_PUNCTURING) + mt7925_mcu_set_eht_pp(mvif->phy->mt76, &mconf->mt76, + link_conf, ctx); + } + } + + mt792x_mutex_release(phy->dev); +} + +static void mt7925_mgd_prepare_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_prep_tx_info *info) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + struct mt792x_dev *dev = mt792x_hw_dev(hw); + u16 duration = info->duration ? info->duration : + jiffies_to_msecs(HZ); + + mt792x_mutex_acquire(dev); + mt7925_set_roc(mvif->phy, &mvif->bss_conf, + mvif->bss_conf.mt76.ctx->def.chan, duration, + MT7925_ROC_REQ_JOIN); + mt792x_mutex_release(dev); +} + +static void mt7925_mgd_complete_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_prep_tx_info *info) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + + mt7925_abort_roc(mvif->phy, &mvif->bss_conf); +} + +static void mt7925_vif_cfg_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + u64 changed) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + struct mt792x_dev *dev = mt792x_hw_dev(hw); + unsigned long valid = ieee80211_vif_is_mld(vif) ? + mvif->valid_links : BIT(0); + struct ieee80211_bss_conf *bss_conf; + int i; + + mt792x_mutex_acquire(dev); + + if (changed & BSS_CHANGED_ASSOC) { + mt7925_mcu_sta_update(dev, NULL, vif, + &mvif->sta.deflink, true, + MT76_STA_INFO_STATE_ASSOC); + mt7925_mcu_set_beacon_filter(dev, vif, vif->cfg.assoc); + + if (ieee80211_vif_is_mld(vif)) + mvif->mlo_pm_state = MT792x_MLO_LINK_ASSOC; + } + + if (changed & BSS_CHANGED_ARP_FILTER) { + for_each_set_bit(i, &valid, IEEE80211_MLD_MAX_NUM_LINKS) { + bss_conf = mt792x_vif_to_bss_conf(vif, i); + mt7925_mcu_update_arp_filter(&dev->mt76, bss_conf); + } + } + + if (changed & BSS_CHANGED_PS) { + if (hweight16(mvif->valid_links) < 2) { + /* legacy */ + bss_conf = &vif->bss_conf; + mt7925_mcu_uni_bss_ps(dev, bss_conf); + } else { + if (mvif->mlo_pm_state == MT792x_MLO_LINK_ASSOC) { + mvif->mlo_pm_state = MT792x_MLO_CHANGED_PS_PENDING; + } else if (mvif->mlo_pm_state == MT792x_MLO_CHANGED_PS) { + for_each_set_bit(i, &valid, IEEE80211_MLD_MAX_NUM_LINKS) { + bss_conf = mt792x_vif_to_bss_conf(vif, i); + mt7925_mcu_uni_bss_ps(dev, bss_conf); + } + } + } + } + + mt792x_mutex_release(dev); + + if (vif->type == NL80211_IFTYPE_NAN && + changed & BSS_CHANGED_NAN_LOCAL_SCHED) { + mt7925_nan_local_sched_changed(dev, vif); + } +} + +static void mt7925_link_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, + u64 changed) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + struct mt792x_phy *phy = mt792x_hw_phy(hw); + struct mt792x_dev *dev = mt792x_hw_dev(hw); + struct mt792x_bss_conf *mconf; + + mconf = mt792x_vif_to_link(mvif, info->link_id); + + mt792x_mutex_acquire(dev); + + if (changed & BSS_CHANGED_ERP_SLOT) { + int slottime = info->use_short_slot ? 9 : 20; + + if (slottime != phy->slottime) { + phy->slottime = slottime; + mt7925_mcu_set_timing(phy, info); + } + } + + if (changed & BSS_CHANGED_MCAST_RATE) + mconf->mt76.mcast_rates_idx = + mt7925_get_rates_table(hw, vif, false, true); + + if (changed & BSS_CHANGED_BASIC_RATES) + mconf->mt76.basic_rates_idx = + mt7925_get_rates_table(hw, vif, false, false); + + if (changed & (BSS_CHANGED_BEACON | + BSS_CHANGED_BEACON_ENABLED)) { + mconf->mt76.beacon_rates_idx = + mt7925_get_rates_table(hw, vif, true, false); + + mt7925_mcu_uni_add_beacon_offload(dev, hw, vif, + info->enable_beacon); + } + + /* ensure that enable txcmd_mode after bss_info */ + if (changed & (BSS_CHANGED_QOS | BSS_CHANGED_BEACON_ENABLED)) + mt7925_mcu_set_tx(dev, info); + + if (mvif->mlo_pm_state == MT792x_MLO_CHANGED_PS_PENDING) { + /* Indicate the secondary setup done */ + mt7925_mcu_uni_bss_bcnft(dev, info, true); + + ieee80211_queue_delayed_work(hw, &dev->mlo_pm_work, 5 * HZ); + mvif->mlo_pm_state = MT792x_MLO_CHANGED_PS; + } + + if (changed & BSS_CHANGED_CQM) + mt7925_mcu_set_rssimonitor(dev, vif); + + mt792x_mutex_release(dev); +} + +static int +mt7925_change_vif_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u16 old_links, u16 new_links, + struct ieee80211_bss_conf *old[IEEE80211_MLD_MAX_NUM_LINKS]) +{ + struct mt792x_bss_conf *mconfs[IEEE80211_MLD_MAX_NUM_LINKS] = {}, *mconf; + struct mt792x_link_sta *mlinks[IEEE80211_MLD_MAX_NUM_LINKS] = {}, *mlink; + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + unsigned long add = new_links & ~old_links; + unsigned long rem = old_links & ~new_links; + struct mt792x_dev *dev = mt792x_hw_dev(hw); + struct mt792x_phy *phy = mt792x_hw_phy(hw); + struct ieee80211_bss_conf *link_conf; + unsigned int link_id; + int err; + + if (old_links == new_links) + return 0; + + mt792x_mutex_acquire(dev); + + for_each_set_bit(link_id, &rem, IEEE80211_MLD_MAX_NUM_LINKS) { + mconf = mt792x_vif_to_link(mvif, link_id); + mlink = mt792x_sta_to_link(&mvif->sta, link_id); + + if (!mconf || !mlink) + continue; + + if (mconf != &mvif->bss_conf) { + mt792x_mac_link_bss_remove(dev, mconf, mlink); + devm_kfree(dev->mt76.dev, mconf); + devm_kfree(dev->mt76.dev, mlink); + } + + rcu_assign_pointer(mvif->link_conf[link_id], NULL); + rcu_assign_pointer(mvif->sta.link[link_id], NULL); + } + + for_each_set_bit(link_id, &add, IEEE80211_MLD_MAX_NUM_LINKS) { + if (!old_links) { + mvif->deflink_id = link_id; + mconf = &mvif->bss_conf; + mlink = &mvif->sta.deflink; + } else { + mconf = devm_kzalloc(dev->mt76.dev, sizeof(*mconf), + GFP_KERNEL); + mlink = devm_kzalloc(dev->mt76.dev, sizeof(*mlink), + GFP_KERNEL); + if (!mconf || !mlink) { + mt792x_mutex_release(dev); + return -ENOMEM; + } + } + + mconfs[link_id] = mconf; + mlinks[link_id] = mlink; + mconf->link_id = link_id; + mconf->vif = mvif; + mlink->wcid.link_id = link_id; + mlink->wcid.link_valid = !!vif->valid_links; + mlink->wcid.def_wcid = &mvif->sta.deflink.wcid; + } + + if (hweight16(mvif->valid_links) == 0) + mt792x_mac_link_bss_remove(dev, &mvif->bss_conf, + &mvif->sta.deflink); + + for_each_set_bit(link_id, &add, IEEE80211_MLD_MAX_NUM_LINKS) { + mconf = mconfs[link_id]; + mlink = mlinks[link_id]; + link_conf = mt792x_vif_to_bss_conf(vif, link_id); + + rcu_assign_pointer(mvif->link_conf[link_id], mconf); + rcu_assign_pointer(mvif->sta.link[link_id], mlink); + + err = mt7925_mac_link_bss_add(dev, link_conf, mlink); + if (err < 0) + goto free; + + if (mconf != &mvif->bss_conf) { + err = mt7925_set_mlo_roc(phy, &mvif->bss_conf, + vif->active_links); + if (err < 0) + goto free; + } + } + + mvif->valid_links = new_links; + + mt792x_mutex_release(dev); + + return 0; + +free: + for_each_set_bit(link_id, &add, IEEE80211_MLD_MAX_NUM_LINKS) { + rcu_assign_pointer(mvif->link_conf[link_id], NULL); + rcu_assign_pointer(mvif->sta.link[link_id], NULL); + + if (mconfs[link_id] != &mvif->bss_conf) + devm_kfree(dev->mt76.dev, mconfs[link_id]); + if (mlinks[link_id] != &mvif->sta.deflink) + devm_kfree(dev->mt76.dev, mlinks[link_id]); + } + + mt792x_mutex_release(dev); + + return err; +} + +static int +mt7925_change_sta_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, u16 old_links, u16 new_links) +{ + unsigned long add = new_links & ~old_links; + unsigned long rem = old_links & ~new_links; + struct mt792x_dev *dev = mt792x_hw_dev(hw); + int err = 0; + + if (old_links == new_links) + return 0; + + mt792x_mutex_acquire(dev); + + err = mt7925_mac_sta_remove_links(dev, vif, sta, rem); + if (err < 0) + goto out; + + err = mt7925_mac_sta_add_links(dev, vif, sta, add); + if (err < 0) + goto out; + +out: + mt792x_mutex_release(dev); + + return err; +} + +static int +mt7927_reconfig_band(struct mt792x_dev *dev, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, + struct mt792x_bss_conf *mconf, + u8 band_idx) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + struct mt792x_link_sta *mlink = &mvif->sta.deflink; + int ret; + + ret = mt76_connac_mcu_uni_add_dev(&dev->mphy, link_conf, + &mconf->mt76, &mlink->wcid, + false); + if (ret) + return ret; + + mconf->mt76.band_idx = band_idx; + + return mt76_connac_mcu_uni_add_dev(&dev->mphy, link_conf, + &mconf->mt76, &mlink->wcid, + true); +} + +static int mt7925_assign_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_chanctx_conf *ctx) +{ + struct mt792x_chanctx *mctx = (struct mt792x_chanctx *)ctx->drv_priv; + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + struct mt792x_dev *dev = mt792x_hw_dev(hw); + struct ieee80211_bss_conf *pri_link_conf; + struct mt792x_bss_conf *mconf; + u8 band_idx; + + mutex_lock(&dev->mt76.mutex); + + if (ieee80211_vif_is_mld(vif)) { + mconf = mt792x_vif_to_link(mvif, link_conf->link_id); + pri_link_conf = mt792x_vif_to_bss_conf(vif, mvif->deflink_id); + + if (vif->type == NL80211_IFTYPE_STATION && + mconf == &mvif->bss_conf) + mt7925_mcu_add_bss_info(&dev->phy, NULL, pri_link_conf, + NULL, true); + } else { + mconf = &mvif->bss_conf; + + if (is_mt7927(&dev->mt76)) { + band_idx = mt7927_band_idx(ctx->def.chan->band); + + mt7927_reconfig_band(dev, vif, link_conf, mconf, band_idx); + } + } + + mconf->mt76.ctx = ctx; + mctx->bss_conf = mconf; + mutex_unlock(&dev->mt76.mutex); + + return 0; +} + +static void mt7925_unassign_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_chanctx_conf *ctx) +{ + struct mt792x_chanctx *mctx = (struct mt792x_chanctx *)ctx->drv_priv; + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + struct mt792x_dev *dev = mt792x_hw_dev(hw); + struct mt792x_bss_conf *mconf; + + mutex_lock(&dev->mt76.mutex); + + if (ieee80211_vif_is_mld(vif)) { + mconf = mt792x_vif_to_link(mvif, link_conf->link_id); + + if (vif->type == NL80211_IFTYPE_STATION && + mconf == &mvif->bss_conf) + mt7925_mcu_add_bss_info(&dev->phy, NULL, link_conf, + NULL, false); + } else { + mconf = &mvif->bss_conf; + } + + mctx->bss_conf = NULL; + mconf->mt76.ctx = NULL; + mutex_unlock(&dev->mt76.mutex); + + if (link_conf->csa_active) { + timer_delete_sync(&mvif->csa_timer); + cancel_work_sync(&mvif->csa_work); + } +} + +static void mt7925_rfkill_poll(struct ieee80211_hw *hw) +{ + struct mt792x_phy *phy = mt792x_hw_phy(hw); + int ret; + + mt792x_mutex_acquire(phy->dev); + ret = mt7925_mcu_wf_rf_pin_ctrl(phy); + mt792x_mutex_release(phy->dev); + + wiphy_rfkill_set_hw_state(hw->wiphy, ret == 0); +} + +static int mt7925_switch_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif_chanctx_switch *vifs, + int n_vifs, + enum ieee80211_chanctx_switch_mode mode) +{ + return mt7925_assign_vif_chanctx(hw, vifs->vif, vifs->link_conf, + vifs->new_ctx); +} + +void mt7925_csa_work(struct work_struct *work) +{ + struct mt792x_vif *mvif; + struct mt792x_dev *dev; + struct ieee80211_vif *vif; + struct ieee80211_bss_conf *link_conf; + struct mt792x_bss_conf *mconf; + u8 link_id, roc_rtype; + int ret = 0; + + mvif = (struct mt792x_vif *)container_of(work, struct mt792x_vif, + csa_work); + dev = mvif->phy->dev; + vif = container_of((void *)mvif, struct ieee80211_vif, drv_priv); + + if (ieee80211_vif_is_mld(vif)) + return; + + if (!dev->new_ctx) + return; + + link_id = 0; + mconf = &mvif->bss_conf; + link_conf = &vif->bss_conf; + roc_rtype = MT7925_ROC_REQ_JOIN; + + mt792x_mutex_acquire(dev); + ret = mt7925_set_roc(mvif->phy, mconf, dev->new_ctx->def.chan, + 4000, roc_rtype); + mt792x_mutex_release(dev); + if (!ret) { + mt792x_mutex_acquire(dev); + ret = mt7925_mcu_set_chctx(mvif->phy->mt76, &mconf->mt76, link_conf, + dev->new_ctx); + mt792x_mutex_release(dev); + + mt7925_abort_roc(mvif->phy, mconf); + } + + ieee80211_chswitch_done(vif, !ret, link_id); +} + +static int mt7925_pre_channel_switch(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_channel_switch *chsw) +{ + if (ieee80211_vif_is_mld(vif)) + return -EOPNOTSUPP; + + if (vif->type != NL80211_IFTYPE_STATION || !vif->cfg.assoc) + return -EOPNOTSUPP; + + if (!cfg80211_chandef_usable(hw->wiphy, &chsw->chandef, + IEEE80211_CHAN_DISABLED)) + return -EOPNOTSUPP; + + return 0; +} + +static void mt7925_channel_switch(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_channel_switch *chsw) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + u16 beacon_interval; + + if (ieee80211_vif_is_mld(vif)) + return; + + beacon_interval = vif->bss_conf.beacon_int; + + mvif->csa_timer.expires = TU_TO_EXP_TIME(beacon_interval * chsw->count); + add_timer(&mvif->csa_timer); +} + +static void mt7925_abort_channel_switch(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + + timer_delete_sync(&mvif->csa_timer); + cancel_work_sync(&mvif->csa_work); +} + +static void mt7925_channel_switch_rx_beacon(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_channel_switch *chsw) +{ + struct mt792x_dev *dev = mt792x_hw_dev(hw); + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + u16 beacon_interval; + + if (ieee80211_vif_is_mld(vif)) + return; + + if (!dev->new_ctx) + return; + + beacon_interval = vif->bss_conf.beacon_int; + + if (cfg80211_chandef_identical(&chsw->chandef, + &dev->new_ctx->def) && + chsw->count) { + mod_timer(&mvif->csa_timer, + TU_TO_EXP_TIME(beacon_interval * chsw->count)); + } +} + +static int mt7925_start_nan(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_nan_conf *conf) +{ + struct ieee80211_bss_conf *link_conf = &vif->bss_conf; + struct mt792x_dev *dev = mt792x_hw_dev(hw); + struct ieee80211_channel *chan; + int err = 0; + + mt792x_mutex_acquire(dev); + + chan = conf->band_cfgs[NL80211_BAND_2GHZ].chan; + if (!chan) { + err = -EINVAL; + goto out; + } + + cfg80211_chandef_create(&link_conf->chanreq.oper, chan, + NL80211_CHAN_NO_HT); + + err = mt7925_mcu_add_bss_info(&dev->phy, NULL, link_conf, + NULL, true); + if (err < 0) + goto out; + + dev->nan_vif = vif; + + err = mt7925_nan_set_nmi_addr(dev, vif->addr); + if (err) + goto rollback_bss; + + err = mt7925_nan_enable(vif, dev, conf); + if (err) + goto rollback_bss; + + goto out; + +rollback_bss: + dev->nan_vif = NULL; + mt7925_mcu_add_bss_info(&dev->phy, NULL, link_conf, NULL, false); + +out: + mt792x_mutex_release(dev); + + return err; +} + +static int mt7925_stop_nan(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct ieee80211_bss_conf *link_conf = &vif->bss_conf; + struct mt792x_dev *dev = mt792x_hw_dev(hw); + int err, ret; + + mt792x_mutex_acquire(dev); + + err = mt7925_nan_disable(vif, dev); + + ret = mt7925_mcu_add_bss_info(&dev->phy, NULL, link_conf, + NULL, false); + if (!err) + err = ret; + + if (dev->nan_vif == vif) + dev->nan_vif = NULL; + + mt792x_mutex_release(dev); + + return err; +} + +static int mt7925_nan_change_conf(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_nan_conf *conf, + u32 changes) +{ + struct mt792x_dev *dev = mt792x_hw_dev(hw); + int err = 0; + + mt792x_mutex_acquire(dev); + + err = mt7925_nan_change_configure(vif, dev, conf); + + mt792x_mutex_release(dev); + + return err; +} + +static int mt7925_nan_peer_sched_changed(struct ieee80211_hw *hw, + struct ieee80211_sta *sta) +{ + struct mt792x_dev *dev = mt792x_hw_dev(hw); + int err = 0; + + mt792x_mutex_acquire(dev); + + err = mt792x_nan_set_peer_schedule(dev, sta); + + mt792x_mutex_release(dev); + + return err; +} + +static void mt7925_sta_pre_rcu_remove(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mt76_phy *phy = hw->priv; + struct mt76_dev *dev = phy->dev; + struct mt76_wcid *wcid = (struct mt76_wcid *)sta->drv_priv; + + mutex_lock(&dev->mutex); + spin_lock_bh(&dev->status_lock); + + if (ieee80211_vif_is_mld(vif)) { + struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv; + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + unsigned long valid = mvif->valid_links; + struct mt792x_link_sta *mlink; + unsigned int link_id; + + for_each_set_bit(link_id, &valid, IEEE80211_MLD_MAX_NUM_LINKS) { + mlink = mt792x_sta_to_link(msta, link_id); + if (!mlink || !mlink->wcid.sta) + continue; + if (mlink->wcid.idx < ARRAY_SIZE(dev->wcid)) + rcu_assign_pointer(dev->wcid[mlink->wcid.idx], + NULL); + } + } else { + rcu_assign_pointer(dev->wcid[wcid->idx], NULL); + } + + spin_unlock_bh(&dev->status_lock); + mutex_unlock(&dev->mutex); +} + +static void mt7925_stop(struct ieee80211_hw *hw, bool suspend) +{ + struct mt792x_dev *dev = mt792x_hw_dev(hw); + + cancel_delayed_work_sync(&dev->mlo_pm_work); + + mt792x_stop(hw, suspend); +} + +const struct ieee80211_ops mt7925_ops = { + .tx = mt792x_tx, + .start = mt7925_start, + .stop = mt7925_stop, + .add_interface = mt7925_add_interface, + .remove_interface = mt7925_remove_interface, + .config = mt7925_config, + .conf_tx = mt7925_conf_tx, + .configure_filter = mt7925_configure_filter, + .start_ap = mt7925_start_ap, + .stop_ap = mt7925_stop_ap, + .sta_state = mt76_sta_state, + .sta_pre_rcu_remove = mt7925_sta_pre_rcu_remove, + .set_key = mt7925_set_key, + .sta_set_decap_offload = mt7925_sta_set_decap_offload, +#if IS_ENABLED(CONFIG_IPV6) + .ipv6_addr_change = mt7925_ipv6_addr_change, +#endif /* CONFIG_IPV6 */ + .ampdu_action = mt7925_ampdu_action, + .set_rts_threshold = mt7925_set_rts_threshold, + .wake_tx_queue = mt76_wake_tx_queue, + .release_buffered_frames = mt76_release_buffered_frames, + .channel_switch_beacon = mt7925_channel_switch_beacon, + .get_txpower = mt792x_get_txpower, + .get_stats = mt792x_get_stats, + .get_et_sset_count = mt792x_get_et_sset_count, + .get_et_strings = mt792x_get_et_strings, + .get_et_stats = mt792x_get_et_stats, + .get_tsf = mt792x_get_tsf, + .set_tsf = mt792x_set_tsf, + .get_survey = mt76_get_survey, + .get_antenna = mt76_get_antenna, + .set_antenna = mt7925_set_antenna, + .set_coverage_class = mt792x_set_coverage_class, + .hw_scan = mt7925_hw_scan, + .cancel_hw_scan = mt7925_cancel_hw_scan, + .sta_statistics = mt792x_sta_statistics, + .sched_scan_start = mt7925_start_sched_scan, + .sched_scan_stop = mt7925_stop_sched_scan, + CFG80211_TESTMODE_CMD(mt7925_testmode_cmd) + CFG80211_TESTMODE_DUMP(mt7925_testmode_dump) +#ifdef CONFIG_PM + .suspend = mt7925_suspend, + .resume = mt7925_resume, + .set_wakeup = mt792x_set_wakeup, + .set_rekey_data = mt7925_set_rekey_data, +#endif /* CONFIG_PM */ + .flush = mt792x_flush, + .set_sar_specs = mt7925_set_sar_specs, + .remain_on_channel = mt7925_remain_on_channel, + .cancel_remain_on_channel = mt7925_cancel_remain_on_channel, + .add_chanctx = mt7925_add_chanctx, + .remove_chanctx = mt7925_remove_chanctx, + .change_chanctx = mt7925_change_chanctx, + .assign_vif_chanctx = mt7925_assign_vif_chanctx, + .unassign_vif_chanctx = mt7925_unassign_vif_chanctx, + .mgd_prepare_tx = mt7925_mgd_prepare_tx, + .mgd_complete_tx = mt7925_mgd_complete_tx, + .vif_cfg_changed = mt7925_vif_cfg_changed, + .link_info_changed = mt7925_link_info_changed, + .change_vif_links = mt7925_change_vif_links, + .change_sta_links = mt7925_change_sta_links, + .rfkill_poll = mt7925_rfkill_poll, + + .switch_vif_chanctx = mt7925_switch_vif_chanctx, + .pre_channel_switch = mt7925_pre_channel_switch, + .channel_switch = mt7925_channel_switch, + .abort_channel_switch = mt7925_abort_channel_switch, + .channel_switch_rx_beacon = mt7925_channel_switch_rx_beacon, + .start_nan = mt7925_start_nan, + .stop_nan = mt7925_stop_nan, + .nan_change_conf = mt7925_nan_change_conf, + .nan_peer_sched_changed = mt7925_nan_peer_sched_changed, +}; +EXPORT_SYMBOL_GPL(mt7925_ops); + +MODULE_AUTHOR("Deren Wu "); +MODULE_DESCRIPTION("MediaTek MT7925 core driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c new file mode 100644 index 000000000..fa29c486a --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c @@ -0,0 +1,4174 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2023 MediaTek Inc. */ + +#include +#include +#include "mt7925.h" +#include "regd.h" +#include "mcu.h" +#include "mac.h" +#include "nan.h" + +#define MT_STA_BFER BIT(0) +#define MT_STA_BFEE BIT(1) +#define MT7925_WOW_PATTERN_NEW_FW_DATE "20260414153105" + +static bool mt7925_vif_is_nan(struct ieee80211_vif *vif) +{ + return vif->type == NL80211_IFTYPE_NAN || + vif->type == NL80211_IFTYPE_NAN_DATA; +} + +int mt7925_mcu_parse_response(struct mt76_dev *mdev, int cmd, + struct sk_buff *skb, int seq) +{ + int mcu_cmd = FIELD_GET(__MCU_CMD_FIELD_ID, cmd); + struct mt7925_mcu_rxd *rxd; + int ret = 0; + + if (!skb) { + dev_err(mdev->dev, "Message %08x (seq %d) timeout\n", cmd, seq); + mt792x_reset(mdev); + + return -ETIMEDOUT; + } + + rxd = (struct mt7925_mcu_rxd *)skb->data; + if (seq != rxd->seq) { + if (!is_mt7928(mdev)) + return -EAGAIN; + else if (is_mt7928(mdev) && + cmd != MCU_CMD(CB_PATCH_SEM_CONTROL) && + cmd != MCU_CMD(CB_PATCH_FINISH_REQ)) + return -EAGAIN; + } + + if (cmd == MCU_CMD(PATCH_SEM_CONTROL) || + cmd == MCU_CMD(PATCH_FINISH_REQ)) { + skb_pull(skb, sizeof(*rxd) - 4); + ret = *skb->data; + } else if (is_mt7928(mdev) && + (cmd == MCU_CMD(CB_PATCH_SEM_CONTROL) || + cmd == MCU_CMD(CB_PATCH_FINISH_REQ))) { + skb_pull(skb, sizeof(*rxd) - 4); + ret = *skb->data; + } else if (cmd == MCU_UNI_CMD(DEV_INFO_UPDATE) || + cmd == MCU_UNI_CMD(BSS_INFO_UPDATE) || + cmd == MCU_UNI_CMD(STA_REC_UPDATE) || + cmd == MCU_UNI_CMD(OFFLOAD) || + cmd == MCU_UNI_CMD(SUSPEND) || + cmd == MCU_UNI_CMD(NAN)) { + struct mt7925_mcu_uni_event *event; + + skb_pull(skb, sizeof(*rxd)); + event = (struct mt7925_mcu_uni_event *)skb->data; + ret = le32_to_cpu(event->status); + /* skip invalid event */ + if (mcu_cmd != event->cid) + ret = -EAGAIN; + } else { + skb_pull(skb, sizeof(*rxd)); + } + + return ret; +} +EXPORT_SYMBOL_GPL(mt7925_mcu_parse_response); + +int mt7925_mcu_regval(struct mt792x_dev *dev, u32 regidx, u32 *val, bool set) +{ +#define MT_RF_REG_HDR GENMASK(31, 24) +#define MT_RF_REG_ANT GENMASK(23, 16) +#define RF_REG_PREFIX 0x99 + struct { + u8 __rsv[4]; + union { + struct uni_cmd_access_reg_basic { + __le16 tag; + __le16 len; + __le32 idx; + __le32 data; + } __packed reg; + struct uni_cmd_access_rf_reg_basic { + __le16 tag; + __le16 len; + __le16 ant; + u8 __rsv[2]; + __le32 idx; + __le32 data; + } __packed rf_reg; + }; + } __packed * res, req; + struct sk_buff *skb; + int ret; + + if (u32_get_bits(regidx, MT_RF_REG_HDR) == RF_REG_PREFIX) { + req.rf_reg.tag = cpu_to_le16(UNI_CMD_ACCESS_RF_REG_BASIC); + req.rf_reg.len = cpu_to_le16(sizeof(req.rf_reg)); + req.rf_reg.ant = cpu_to_le16(u32_get_bits(regidx, MT_RF_REG_ANT)); + req.rf_reg.idx = cpu_to_le32(regidx); + req.rf_reg.data = set ? cpu_to_le32(*val) : 0; + } else { + req.reg.tag = cpu_to_le16(UNI_CMD_ACCESS_REG_BASIC); + req.reg.len = cpu_to_le16(sizeof(req.reg)); + req.reg.idx = cpu_to_le32(regidx); + req.reg.data = set ? cpu_to_le32(*val) : 0; + } + + if (set) + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(REG_ACCESS), + &req, sizeof(req), true); + + ret = mt76_mcu_send_and_get_msg(&dev->mt76, + MCU_WM_UNI_CMD_QUERY(REG_ACCESS), + &req, sizeof(req), true, &skb); + if (ret) + return ret; + + res = (void *)skb->data; + if (u32_get_bits(regidx, MT_RF_REG_HDR) == RF_REG_PREFIX) + *val = le32_to_cpu(res->rf_reg.data); + else + *val = le32_to_cpu(res->reg.data); + + dev_kfree_skb(skb); + + return 0; +} +EXPORT_SYMBOL_GPL(mt7925_mcu_regval); + +int mt7925_mcu_update_arp_filter(struct mt76_dev *dev, + struct ieee80211_bss_conf *link_conf) +{ + struct mt792x_bss_conf *mconf = mt792x_link_conf_to_mconf(link_conf); + struct ieee80211_vif *mvif = link_conf->vif; + struct sk_buff *skb; + int i, len = min_t(int, mvif->cfg.arp_addr_cnt, + IEEE80211_BSS_ARP_ADDR_LIST_LEN); + struct { + struct { + u8 bss_idx; + u8 pad[3]; + } __packed hdr; + struct mt7925_arpns_tlv arp; + } req = { + .hdr = { + .bss_idx = mconf->mt76.idx, + }, + .arp = { + .tag = cpu_to_le16(UNI_OFFLOAD_OFFLOAD_ARP), + .len = cpu_to_le16(sizeof(req) - 4 + len * 2 * sizeof(__be32)), + .ips_num = len, + .enable = true, + }, + }; + + skb = mt76_mcu_msg_alloc(dev, NULL, sizeof(req) + len * 2 * sizeof(__be32)); + if (!skb) + return -ENOMEM; + + skb_put_data(skb, &req, sizeof(req)); + for (i = 0; i < len; i++) { + skb_put_data(skb, &mvif->cfg.arp_addr_list[i], sizeof(__be32)); + skb_put_zero(skb, sizeof(__be32)); + } + + return mt76_mcu_skb_send_msg(dev, skb, MCU_UNI_CMD(OFFLOAD), true); +} + +#ifdef CONFIG_PM +static int +mt7925_connac_mcu_set_wow_ctrl(struct mt76_phy *phy, struct ieee80211_vif *vif, + bool suspend, struct cfg80211_wowlan *wowlan) +{ + struct mt76_vif_link *mvif = (struct mt76_vif_link *)vif->drv_priv; + struct ieee80211_scan_ies ies = {}; + struct mt76_dev *dev = phy->dev; + struct { + struct { + u8 bss_idx; + u8 pad[3]; + } __packed hdr; + struct mt76_connac_wow_ctrl_tlv wow_ctrl_tlv; + struct mt76_connac_wow_gpio_param_tlv gpio_tlv; + } req = { + .hdr = { + .bss_idx = mvif->idx, + }, + .wow_ctrl_tlv = { + .tag = cpu_to_le16(UNI_SUSPEND_WOW_CTRL), + .len = cpu_to_le16(sizeof(struct mt76_connac_wow_ctrl_tlv)), + .cmd = suspend ? 1 : 2, + }, + .gpio_tlv = { + .tag = cpu_to_le16(UNI_SUSPEND_WOW_GPIO_PARAM), + .len = cpu_to_le16(sizeof(struct mt76_connac_wow_gpio_param_tlv)), + .gpio_pin = 0xff, /* follow fw about GPIO pin */ + }, + }; + + if (wowlan->magic_pkt) + req.wow_ctrl_tlv.trigger |= UNI_WOW_DETECT_TYPE_MAGIC; + if (wowlan->disconnect) + req.wow_ctrl_tlv.trigger |= (UNI_WOW_DETECT_TYPE_DISCONNECT | + UNI_WOW_DETECT_TYPE_BCN_LOST); + if (wowlan->nd_config) { + mt7925_mcu_sched_scan_req(phy, vif, wowlan->nd_config, &ies); + req.wow_ctrl_tlv.trigger |= UNI_WOW_DETECT_TYPE_SCH_SCAN_HIT; + mt7925_mcu_sched_scan_enable(phy, vif, suspend); + } + if (wowlan->n_patterns) + req.wow_ctrl_tlv.trigger |= UNI_WOW_DETECT_TYPE_BITMAP; + + if (mt76_is_mmio(dev)) + req.wow_ctrl_tlv.wakeup_hif = WOW_PCIE; + else if (mt76_is_usb(dev)) + req.wow_ctrl_tlv.wakeup_hif = WOW_USB; + else if (mt76_is_sdio(dev)) + req.wow_ctrl_tlv.wakeup_hif = WOW_GPIO; + + return mt76_mcu_send_msg(dev, MCU_UNI_CMD(SUSPEND), &req, + sizeof(req), true); +} + +static bool mt7925_mcu_wow_pattern_old_tlv(struct mt76_dev *dev) +{ + const char *fw_version = dev->hw->wiphy->fw_version; + const char *build_date = strrchr(fw_version, '-'); + + if (!is_mt7925(dev)) + return false; + + if (!build_date) + return false; + + build_date++; + + return strncmp(build_date, MT7925_WOW_PATTERN_NEW_FW_DATE, + strlen(MT7925_WOW_PATTERN_NEW_FW_DATE)) < 0; +} + +static int +mt7925_mcu_set_wow_pattern(struct mt76_dev *dev, + struct ieee80211_vif *vif, + u8 index, bool enable, + struct cfg80211_pkt_pattern *pattern) +{ + struct mt76_vif_link *mvif = (struct mt76_vif_link *)vif->drv_priv; + struct mt7925_wow_pattern_tlv *tlv; + struct sk_buff *skb; + int tlv_len; + bool old_tlv; + struct { + u8 bss_idx; + u8 pad[3]; + } __packed hdr = { + .bss_idx = mvif->idx, + }; + + old_tlv = mt7925_mcu_wow_pattern_old_tlv(dev); + tlv_len = old_tlv ? sizeof(struct mt7925_wow_pattern_tlv) : + MT7925_WOW_PATTERN_TLV_V2_SIZE; + + skb = mt76_mcu_msg_alloc(dev, NULL, sizeof(hdr) + tlv_len); + if (!skb) + return -ENOMEM; + + skb_put_data(skb, &hdr, sizeof(hdr)); + tlv = (struct mt7925_wow_pattern_tlv *)skb_put_zero(skb, tlv_len); + tlv->tag = cpu_to_le16(UNI_SUSPEND_WOW_PATTERN); + tlv->len = cpu_to_le16(tlv_len); + tlv->bss_idx = 0xF; + tlv->data_len = pattern->pattern_len; + tlv->enable = enable; + tlv->index = index; + tlv->offset = 0; + + memcpy(tlv->pattern, pattern->pattern, pattern->pattern_len); + memcpy(tlv->mask, pattern->mask, DIV_ROUND_UP(pattern->pattern_len, 8)); + + return mt76_mcu_skb_send_msg(dev, skb, MCU_UNI_CMD(SUSPEND), true); +} + +void mt7925_mcu_set_suspend_iter(void *priv, u8 *mac, + struct ieee80211_vif *vif) +{ + struct mt76_phy *phy = priv; + bool suspend = !test_bit(MT76_STATE_RUNNING, &phy->state); + struct ieee80211_hw *hw = phy->hw; + struct cfg80211_wowlan *wowlan = hw->wiphy->wowlan_config; + int i; + + mt76_connac_mcu_set_gtk_rekey(phy->dev, vif, suspend); + + mt76_connac_mcu_set_suspend_mode(phy->dev, vif, suspend, 1, true); + + for (i = 0; i < wowlan->n_patterns; i++) + mt7925_mcu_set_wow_pattern(phy->dev, vif, i, suspend, + &wowlan->patterns[i]); + mt7925_connac_mcu_set_wow_ctrl(phy, vif, suspend, wowlan); +} + +#endif /* CONFIG_PM */ + +static void +mt7925_mcu_connection_loss_iter(void *priv, u8 *mac, + struct ieee80211_vif *vif) +{ + struct mt76_vif_link *mvif = (struct mt76_vif_link *)vif->drv_priv; + struct mt7925_uni_beacon_loss_event *event = priv; + + if (mvif->idx != event->hdr.bss_idx) + return; + + if (!(vif->driver_flags & IEEE80211_VIF_BEACON_FILTER) || + vif->type != NL80211_IFTYPE_STATION) + return; + + ieee80211_connection_loss(vif); +} + +static void +mt7925_mcu_connection_loss_event(struct mt792x_dev *dev, struct sk_buff *skb) +{ + struct mt7925_uni_beacon_loss_event *event; + struct mt76_phy *mphy = &dev->mt76.phy; + + skb_pull(skb, sizeof(struct mt7925_mcu_rxd)); + event = (struct mt7925_uni_beacon_loss_event *)skb->data; + + ieee80211_iterate_active_interfaces_atomic(mphy->hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7925_mcu_connection_loss_iter, event); +} + +static void +mt7925_mcu_roc_iter(void *priv, u8 *mac, struct ieee80211_vif *vif) +{ + struct mt76_vif_link *mvif = (struct mt76_vif_link *)vif->drv_priv; + struct mt7925_roc_grant_tlv *grant = priv; + + if (ieee80211_vif_is_mld(vif) && vif->type == NL80211_IFTYPE_STATION) + return; + + if (mvif->idx != grant->bss_idx) + return; + + mvif->band_idx = grant->dbdcband; +} + +static void mt7925_mcu_roc_handle_grant(struct mt792x_dev *dev, + struct tlv *tlv) +{ + struct ieee80211_hw *hw = dev->mt76.hw; + struct mt7925_roc_grant_tlv *grant; + int duration; + + grant = (struct mt7925_roc_grant_tlv *)tlv; + + /* should never happen */ + WARN_ON_ONCE((le16_to_cpu(grant->tag) != UNI_EVENT_ROC_GRANT)); + + if (grant->reqtype == MT7925_ROC_REQ_ROC) + ieee80211_ready_on_channel(hw); + else if (grant->reqtype == MT7925_ROC_REQ_JOIN) + ieee80211_iterate_active_interfaces_atomic(hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7925_mcu_roc_iter, grant); + dev->phy.roc_grant = true; + wake_up(&dev->phy.roc_wait); + duration = le32_to_cpu(grant->max_interval); + mod_timer(&dev->phy.roc_timer, + jiffies + msecs_to_jiffies(duration)); +} + +static void +mt7925_mcu_handle_hif_ctrl_basic(struct mt792x_dev *dev, struct tlv *tlv) +{ + struct mt7925_mcu_hif_ctrl_basic_tlv *basic; + + basic = (struct mt7925_mcu_hif_ctrl_basic_tlv *)tlv; + + if (basic->hifsuspend) { + dev->hif_idle = true; + if (!(basic->hif_tx_traffic_status == HIF_TRAFFIC_IDLE && + basic->hif_rx_traffic_status == HIF_TRAFFIC_IDLE)) + dev_info(dev->mt76.dev, "Hif traffic not idle.\n"); + } else { + dev->hif_resumed = true; + } + wake_up(&dev->wait); +} + +static void +mt7925_mcu_uni_hif_ctrl_event(struct mt792x_dev *dev, struct sk_buff *skb) +{ + struct tlv *tlv; + u32 tlv_len; + + skb_pull(skb, sizeof(struct mt7925_mcu_rxd) + 4); + tlv = (struct tlv *)skb->data; + tlv_len = skb->len; + + mt7925_for_each_tlv(tlv, tlv_len) { + switch (le16_to_cpu(tlv->tag)) { + case UNI_EVENT_HIF_CTRL_BASIC: + if (le16_to_cpu(tlv->len) < + sizeof(struct mt7925_mcu_hif_ctrl_basic_tlv)) + break; + + mt7925_mcu_handle_hif_ctrl_basic(dev, tlv); + break; + default: + break; + } + } +} + +static void +mt7925_mcu_uni_roc_event(struct mt792x_dev *dev, struct sk_buff *skb) +{ + struct tlv *tlv; + u32 tlv_len; + + skb_pull(skb, sizeof(struct mt7925_mcu_rxd) + 4); + tlv = (struct tlv *)skb->data; + tlv_len = skb->len; + + mt7925_for_each_tlv(tlv, tlv_len) { + switch (le16_to_cpu(tlv->tag)) { + case UNI_EVENT_ROC_GRANT: + if (le16_to_cpu(tlv->len) < + sizeof(struct mt7925_roc_grant_tlv)) + break; + + mt7925_mcu_roc_handle_grant(dev, tlv); + break; + case UNI_EVENT_ROC_GRANT_SUB_LINK: + break; + } + } +} + +static void +mt7925_mcu_scan_event(struct mt792x_dev *dev, struct sk_buff *skb) +{ + struct mt76_phy *mphy = &dev->mt76.phy; + struct mt792x_phy *phy = mphy->priv; + + spin_lock_bh(&dev->mt76.lock); + __skb_queue_tail(&phy->scan_event_list, skb); + spin_unlock_bh(&dev->mt76.lock); + + ieee80211_queue_delayed_work(mphy->hw, &phy->scan_work, + MT792x_HW_SCAN_TIMEOUT); +} + +static void +mt7925_mcu_tx_done_event(struct mt792x_dev *dev, struct sk_buff *skb) +{ +#define UNI_EVENT_TX_DONE_MSG 0 +#define UNI_EVENT_TX_DONE_RAW 1 + struct mt7925_mcu_txs_event { + u8 ver; + u8 rsv[3]; + u8 data[]; + } __packed * txs; + struct mt7928_uni_txdone_event *evt; + struct tlv *tlv; + u32 tlv_len; + + skb_pull(skb, sizeof(struct mt7925_mcu_rxd) + 4); + tlv = (struct tlv *)skb->data; + tlv_len = skb->len; + + mt7925_for_each_tlv(tlv, tlv_len) { + switch (le16_to_cpu(tlv->tag)) { + case UNI_EVENT_TX_DONE_MSG: + if (!is_mt7928(&dev->mt76)) + break; + + if (le16_to_cpu(tlv->len) < sizeof(*evt)) + break; + + evt = (struct mt7928_uni_txdone_event *)tlv; + if (evt->status) { + dev_info(dev->mt76.dev, + "TxDone: pid=%u status=%#x sn=%#x wcid=%u " + "cnt=%u rate=%#x flag=%#x tid=%u pwr=%u " + "rsp_rate=%#x rate_idx=%u bw=%u flush=%#x " + "delay=%#x ts=%#x flags=%#x\n", + evt->pid, evt->status, + le16_to_cpu(evt->seq), evt->wcid, + evt->tx_count, le16_to_cpu(evt->tx_rate), + evt->flag, evt->tid, evt->tx_pwr, + evt->rsp_rate, evt->rate_tbl_idx, + evt->bw, evt->flush_reason, + le32_to_cpu(evt->tx_delay), + le32_to_cpu(evt->timestamp), + le32_to_cpu(evt->applied_flags)); + } + mt7928_mac_add_txs_msg(dev, evt); + break; + case UNI_EVENT_TX_DONE_RAW: + txs = (struct mt7925_mcu_txs_event *)tlv->data; + mt7925_mac_add_txs(dev, txs->data); + break; + default: + break; + } + } +} + +static void +mt7925_mcu_rssi_monitor_iter(void *priv, u8 *mac, + struct ieee80211_vif *vif) +{ + struct mt7925_uni_rssi_monitor_event *event = priv; + enum nl80211_cqm_rssi_threshold_event nl_event; + s32 rssi = le32_to_cpu(event->rssi); + + if (vif->type != NL80211_IFTYPE_STATION) + return; + + if (!(vif->driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI)) + return; + + if (rssi > vif->bss_conf.cqm_rssi_thold) + nl_event = NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH; + else + nl_event = NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW; + + ieee80211_cqm_rssi_notify(vif, nl_event, rssi, GFP_KERNEL); +} + +static void +mt7925_mcu_rssi_monitor_event(struct mt792x_dev *dev, struct sk_buff *skb) +{ + struct tlv *tlv; + u32 tlv_len; + struct mt7925_uni_rssi_monitor_event *event; + + skb_pull(skb, sizeof(struct mt7925_mcu_rxd) + 4); + tlv = (struct tlv *)skb->data; + tlv_len = skb->len; + + mt7925_for_each_tlv(tlv, tlv_len) { + switch (le16_to_cpu(tlv->tag)) { + case UNI_EVENT_RSSI_MONITOR_INFO: + if (le16_to_cpu(tlv->len) < sizeof(*event)) + break; + + event = (struct mt7925_uni_rssi_monitor_event *)tlv; + ieee80211_iterate_active_interfaces_atomic(dev->mt76.hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7925_mcu_rssi_monitor_iter, + event); + break; + default: + break; + } + } +} + +static void +mt7925_mcu_uni_debug_msg_event(struct mt792x_dev *dev, struct sk_buff *skb) +{ + struct mt7925_uni_debug_msg { + __le16 tag; + __le16 len; + u8 fmt; + u8 rsv[3]; + u8 id; + u8 type:3; + u8 nr_args:5; + union { + struct idxlog { + __le16 rsv; + __le32 ts; + __le32 idx; + u8 data[]; + } __packed idx; + struct txtlog { + u8 len; + u8 rsv; + __le32 ts; + u8 data[]; + } __packed txt; + }; + } __packed * hdr; + + skb_pull(skb, sizeof(struct mt7925_mcu_rxd) + 4); + hdr = (struct mt7925_uni_debug_msg *)skb->data; + + if (hdr->id == 0x28) { + skb_pull(skb, offsetof(struct mt7925_uni_debug_msg, id)); + wiphy_info(mt76_hw(dev)->wiphy, "%.*s", skb->len, skb->data); + return; + } else if (hdr->id != 0xa8) { + return; + } + + if (hdr->type == 0) { /* idx log */ + int i, ret, len = PAGE_SIZE - 1, nr_val; + struct page *page = dev_alloc_pages(get_order(len)); + __le32 *val; + char *buf, *cur; + + if (!page) + return; + + buf = page_address(page); + cur = buf; + + nr_val = (le16_to_cpu(hdr->len) - sizeof(*hdr)) / 4; + val = (__le32 *)hdr->idx.data; + for (i = 0; i < nr_val && len > 0; i++) { + ret = snprintf(cur, len, "0x%x,", le32_to_cpu(val[i])); + if (ret <= 0) + break; + + cur += ret; + len -= ret; + } + if (cur > buf) + wiphy_info(mt76_hw(dev)->wiphy, "idx: 0x%X,%d,%s", + le32_to_cpu(hdr->idx.idx), nr_val, buf); + put_page(page); + } else if (hdr->type == 2) { /* str log */ + wiphy_info(mt76_hw(dev)->wiphy, "%.*s", hdr->txt.len, hdr->txt.data); + } +} + +static void +mt7925_mcu_handle_mbmc_event(struct mt792x_dev *dev, struct sk_buff *skb) +{ + struct mbmc_conf_tlv *tlv; + u32 tlv_len; + + skb_pull(skb, sizeof(struct mt7925_mcu_rxd) + 4); + tlv_len = skb->len; + tlv = (struct mbmc_conf_tlv *)skb->data; + + while (tlv_len >= sizeof(*tlv) && + le16_to_cpu(tlv->len) >= sizeof(*tlv) && + le16_to_cpu(tlv->len) <= tlv_len) { + u16 tag = le16_to_cpu(tlv->tag); + + if (tag == UNI_MBMC_SETTING || tag == UNI_MBMC_NO_RESP_SETTING) { + dev_dbg(dev->mt76.dev, + "MBMC event: tag=%u mbmc_en=%u\n", + tag, tlv->mbmc_en); + break; + } + + tlv_len -= le16_to_cpu(tlv->len); + tlv = (struct mbmc_conf_tlv *) + ((u8 *)tlv + le16_to_cpu(tlv->len)); + } +} + +static void +mt7925_mcu_uni_rx_unsolicited_event(struct mt792x_dev *dev, + struct sk_buff *skb) +{ + struct mt7925_mcu_rxd *rxd; + + rxd = (struct mt7925_mcu_rxd *)skb->data; + + switch (rxd->eid) { + case MCU_UNI_EVENT_HIF_CTRL: + mt7925_mcu_uni_hif_ctrl_event(dev, skb); + break; + case MCU_UNI_EVENT_FW_LOG_2_HOST: + mt7925_mcu_uni_debug_msg_event(dev, skb); + break; + case MCU_UNI_EVENT_ROC: + mt7925_mcu_uni_roc_event(dev, skb); + break; + case MCU_UNI_EVENT_MBMC: + mt7925_mcu_handle_mbmc_event(dev, skb); + break; + case MCU_UNI_EVENT_SCAN_DONE: + mt7925_mcu_scan_event(dev, skb); + return; + case MCU_UNI_EVENT_TX_DONE: + mt7925_mcu_tx_done_event(dev, skb); + break; + case MCU_UNI_EVENT_BSS_BEACON_LOSS: + mt7925_mcu_connection_loss_event(dev, skb); + break; + case MCU_UNI_EVENT_RSSI_MONITOR: + mt7925_mcu_rssi_monitor_event(dev, skb); + break; + case MCU_UNI_EVENT_COREDUMP: + dev->fw_assert = true; + mt76_connac_mcu_coredump_event(&dev->mt76, skb, &dev->coredump); + return; + case MCU_UNI_EVENT_NAN: + mt7925_nan_mcu_event(dev, skb); + break; + default: + break; + } + dev_kfree_skb(skb); +} + +void mt7925_mcu_rx_event(struct mt792x_dev *dev, struct sk_buff *skb) +{ + struct mt7925_mcu_rxd *rxd = (struct mt7925_mcu_rxd *)skb->data; + + if (skb_linearize(skb)) + return; + + if (rxd->option & MCU_UNI_CMD_UNSOLICITED_EVENT) { + mt7925_mcu_uni_rx_unsolicited_event(dev, skb); + return; + } + + mt76_mcu_rx_event(&dev->mt76, skb); +} + +static int +mt7925_mcu_sta_ba(struct mt76_dev *dev, struct mt76_vif_link *mvif, + struct ieee80211_ampdu_params *params, + struct mt76_wcid *wcid, bool enable, bool tx) +{ + struct sta_rec_ba_uni *ba; + struct sk_buff *skb; + struct tlv *tlv; + int len; + + len = sizeof(struct sta_req_hdr) + sizeof(*ba); + skb = __mt76_connac_mcu_alloc_sta_req(dev, mvif, wcid, + len); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_BA, sizeof(*ba)); + + ba = (struct sta_rec_ba_uni *)tlv; + ba->ba_type = tx ? MT_BA_TYPE_ORIGINATOR : MT_BA_TYPE_RECIPIENT; + ba->winsize = cpu_to_le16(params->buf_size); + ba->ssn = cpu_to_le16(params->ssn); + ba->ba_en = enable << params->tid; + ba->amsdu = params->amsdu; + ba->tid = params->tid; + + return mt76_mcu_skb_send_msg(dev, skb, + MCU_UNI_CMD(STA_REC_UPDATE), true); +} + +int mt7925_mcu_uni_tx_ba(struct mt792x_dev *dev, + struct ieee80211_ampdu_params *params, + struct ieee80211_vif *vif, bool enable) +{ + struct ieee80211_sta *sta = params->sta; + struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv; + struct ieee80211_link_sta *link_sta; + unsigned int link_id; + + for_each_sta_active_link(vif, sta, link_sta, link_id) { + struct mt792x_link_sta *mlink; + struct mt792x_bss_conf *mconf; + int ret; + + mlink = mt792x_sta_to_link(msta, link_id); + if (!mlink) + return -EINVAL; + + mconf = mt792x_vif_to_link(msta->vif, link_id); + if (!mconf) + return -EINVAL; + + if (enable && !params->amsdu) + mlink->wcid.amsdu = false; + + ret = mt7925_mcu_sta_ba(&dev->mt76, &mconf->mt76, params, + &mlink->wcid, enable, true); + if (ret) + return ret; + } + + return 0; +} + +int mt7925_mcu_uni_rx_ba(struct mt792x_dev *dev, + struct ieee80211_ampdu_params *params, + struct ieee80211_vif *vif, bool enable) +{ + struct ieee80211_sta *sta = params->sta; + struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv; + struct ieee80211_link_sta *link_sta; + unsigned int link_id; + + for_each_sta_active_link(vif, sta, link_sta, link_id) { + struct mt792x_link_sta *mlink; + struct mt792x_bss_conf *mconf; + int ret; + + mlink = mt792x_sta_to_link(msta, link_id); + if (!mlink) + return -EINVAL; + + mconf = mt792x_vif_to_link(msta->vif, link_id); + if (!mconf) + return -EINVAL; + + ret = mt7925_mcu_sta_ba(&dev->mt76, &mconf->mt76, params, + &mlink->wcid, enable, false); + if (ret) + return ret; + } + + return 0; +} + +static int mt7925_mcu_read_eeprom(struct mt792x_dev *dev, u32 offset, u8 *val) +{ + struct { + u8 rsv[4]; + + __le16 tag; + __le16 len; + + __le32 addr; + __le32 valid; + u8 data[MT7925_EEPROM_BLOCK_SIZE]; + } __packed req = { + .tag = cpu_to_le16(1), + .len = cpu_to_le16(sizeof(req) - 4), + .addr = cpu_to_le32(round_down(offset, + MT7925_EEPROM_BLOCK_SIZE)), + }; + struct evt { + u8 rsv[4]; + + __le16 tag; + __le16 len; + + __le32 ver; + __le32 addr; + __le32 valid; + __le32 size; + __le32 magic_num; + __le32 type; + __le32 rsv1[4]; + u8 data[32]; + } __packed *res; + struct sk_buff *skb; + int ret; + + ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_WM_UNI_CMD_QUERY(EFUSE_CTRL), + &req, sizeof(req), true, &skb); + if (ret) + return ret; + + res = (struct evt *)skb->data; + *val = res->data[offset % MT7925_EEPROM_BLOCK_SIZE]; + + dev_kfree_skb(skb); + + return 0; +} + +static int mt7925_load_clc(struct mt792x_dev *dev, const char *fw_name) +{ + const struct mt76_connac2_fw_trailer *hdr; + const struct mt76_connac2_fw_region *region; + const struct mt7925_clc *clc; + struct mt76_dev *mdev = &dev->mt76; + struct mt792x_phy *phy = &dev->phy; + const struct firmware *fw; + u8 *clc_base = NULL, hw_encap = 0; + int ret, i, len, offset = 0; + + dev->phy.clc_chan_conf = 0xff; + dev->regd_user = false; + if (!mt7925_regd_clc_supported(dev)) + return 0; + + if (mt76_is_mmio(&dev->mt76)) { + ret = mt7925_mcu_read_eeprom(dev, MT_EE_HW_TYPE, &hw_encap); + if (ret) + return ret; + hw_encap = u8_get_bits(hw_encap, MT_EE_HW_TYPE_ENCAP); + } + + ret = request_firmware(&fw, fw_name, mdev->dev); + if (ret) + return ret; + + if (!fw || !fw->data || fw->size < sizeof(*hdr)) { + dev_err(mdev->dev, "Invalid firmware\n"); + ret = -EINVAL; + goto out; + } + + hdr = (const void *)(fw->data + fw->size - sizeof(*hdr)); + for (i = 0; i < hdr->n_region; i++) { + region = (const void *)((const u8 *)hdr - + (hdr->n_region - i) * sizeof(*region)); + len = le32_to_cpu(region->len); + + /* check if we have valid buffer size */ + if (offset + len > fw->size) { + dev_err(mdev->dev, "Invalid firmware region\n"); + ret = -EINVAL; + goto out; + } + + if ((region->feature_set & FW_FEATURE_NON_DL) && + region->type == FW_TYPE_CLC) { + clc_base = (u8 *)(fw->data + offset); + break; + } + offset += len; + } + + if (!clc_base) + goto out; + + for (offset = 0; offset < len; offset += le32_to_cpu(clc->len)) { + clc = (const struct mt7925_clc *)(clc_base + offset); + + if (clc->idx >= ARRAY_SIZE(phy->clc)) + break; + + /* do not init buf again if chip reset triggered */ + if (phy->clc[clc->idx]) + continue; + + /* header content sanity */ + if ((clc->idx == MT792x_CLC_BE_CTRL && + u8_get_bits(clc->t2.type, MT_EE_HW_TYPE_ENCAP) != hw_encap) || + u8_get_bits(clc->t0.type, MT_EE_HW_TYPE_ENCAP) != hw_encap) + continue; + + phy->clc[clc->idx] = devm_kmemdup(mdev->dev, clc, + le32_to_cpu(clc->len), + GFP_KERNEL); + + if (!phy->clc[clc->idx]) { + ret = -ENOMEM; + goto out; + } + } + + ret = mt7925_regd_init(phy); +out: + release_firmware(fw); + + return ret; +} + +int mt7925_mcu_fw_log_2_host(struct mt792x_dev *dev, u8 ctrl) +{ + struct { + u8 _rsv[4]; + + __le16 tag; + __le16 len; + u8 ctrl; + u8 interval; + u8 _rsv2[2]; + } __packed req = { + .tag = cpu_to_le16(UNI_WSYS_CONFIG_FW_LOG_CTRL), + .len = cpu_to_le16(sizeof(req) - 4), + .ctrl = ctrl, + }; + int ret; + + ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_UNI_CMD(WSYS_CONFIG), + &req, sizeof(req), true, NULL); + return ret; +} + +int mt7925_mcu_get_temperature(struct mt792x_phy *phy) +{ + struct { + u8 _rsv[4]; + + __le16 tag; + __le16 len; + u8 _rsv2[4]; + } __packed req = { + .tag = cpu_to_le16(0x0), + .len = cpu_to_le16(sizeof(req) - 4), + }; + struct mt7925_thermal_evt { + u8 rsv[4]; + __le32 temperature; + } __packed * evt; + struct mt792x_dev *dev = phy->dev; + int temperature, ret; + struct sk_buff *skb; + + ret = mt76_mcu_send_and_get_msg(&dev->mt76, + MCU_WM_UNI_CMD_QUERY(THERMAL), + &req, sizeof(req), true, &skb); + if (ret) + return ret; + + skb_pull(skb, 4 + sizeof(struct tlv)); + evt = (struct mt7925_thermal_evt *)skb->data; + + temperature = le32_to_cpu(evt->temperature); + + dev_kfree_skb(skb); + + return temperature; +} + +static void +mt7925_mcu_parse_phy_cap(struct mt792x_dev *dev, char *data) +{ + struct mt76_phy *mphy = &dev->mt76.phy; + struct mt76_dev *mdev = mphy->dev; + struct mt7925_mcu_phy_cap { + u8 ht; + u8 vht; + u8 _5g; + u8 max_bw; + u8 nss; + u8 dbdc; + u8 tx_ldpc; + u8 rx_ldpc; + u8 tx_stbc; + u8 rx_stbc; + u8 hw_path; + u8 he; + u8 eht; + } __packed * cap; + enum { + WF0_24G, + WF0_5G + }; + + cap = (struct mt7925_mcu_phy_cap *)data; + + mdev->phy.antenna_mask = BIT(cap->nss) - 1; + mdev->phy.chainmask = mdev->phy.antenna_mask; + mdev->phy.cap.has_2ghz = cap->hw_path & BIT(WF0_24G); + mdev->phy.cap.has_5ghz = cap->hw_path & BIT(WF0_5G); +} + +static void +mt7925_mcu_parse_eml_cap(struct mt792x_dev *dev, char *data) +{ + struct mt7925_mcu_eml_cap { + u8 rsv[4]; + __le16 eml_cap; + u8 rsv2[6]; + } __packed * cap; + + cap = (struct mt7925_mcu_eml_cap *)data; + + dev->phy.eml_cap = le16_to_cpu(cap->eml_cap); +} + +static int +mt7925_mcu_get_nic_capability(struct mt792x_dev *dev) +{ + struct mt76_phy *mphy = &dev->mt76.phy; + struct { + u8 _rsv[4]; + + __le16 tag; + __le16 len; + } __packed req = { + .tag = cpu_to_le16(UNI_CHIP_CONFIG_NIC_CAPA), + .len = cpu_to_le16(sizeof(req) - 4), + }; + struct mt76_connac_cap_hdr { + __le16 n_element; + u8 rsv[2]; + } __packed * hdr; + struct sk_buff *skb; + int ret, i; + + ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_UNI_CMD(CHIP_CONFIG), + &req, sizeof(req), true, &skb); + if (ret) + return ret; + + hdr = (struct mt76_connac_cap_hdr *)skb->data; + if (skb->len < sizeof(*hdr)) { + ret = -EINVAL; + goto out; + } + + skb_pull(skb, sizeof(*hdr)); + + for (i = 0; i < le16_to_cpu(hdr->n_element); i++) { + struct tlv *tlv = (struct tlv *)skb->data; + int len; + + if (skb->len < sizeof(*tlv)) + break; + + len = le16_to_cpu(tlv->len); + if (skb->len < len) + break; + + switch (le16_to_cpu(tlv->tag)) { + case MT_NIC_CAP_6G: + mphy->cap.has_6ghz = !!tlv->data[0]; + break; + case MT_NIC_CAP_MAC_ADDR: + memcpy(mphy->macaddr, (void *)tlv->data, ETH_ALEN); + break; + case MT_NIC_CAP_PHY: + mt7925_mcu_parse_phy_cap(dev, tlv->data); + break; + case MT_NIC_CAP_CHIP_CAP: + dev->phy.chip_cap = le64_to_cpu(*(__le64 *)tlv->data); + break; + case MT_NIC_CAP_EML_CAP: + mt7925_mcu_parse_eml_cap(dev, tlv->data); + break; + default: + break; + } + skb_pull(skb, len); + } +out: + dev_kfree_skb(skb); + return ret; +} + +int mt7925_mcu_chip_config(struct mt792x_dev *dev, const char *cmd) +{ + u16 len = strlen(cmd) + 1; + struct { + u8 _rsv[4]; + __le16 tag; + __le16 len; + struct mt76_connac_config config; + } __packed req = { + .tag = cpu_to_le16(UNI_CHIP_CONFIG_CHIP_CFG), + .len = cpu_to_le16(sizeof(req) - 4), + .config = { + .resp_type = 0, + .type = 0, + .data_size = cpu_to_le16(len), + }, + }; + + memcpy(req.config.data, cmd, len); + + return mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(CHIP_CONFIG), + &req, sizeof(req), false); +} + +int mt7925_mcu_set_deep_sleep(struct mt792x_dev *dev, bool enable) +{ + char cmd[16]; + + snprintf(cmd, sizeof(cmd), "KeepFullPwr %d", !enable); + + return mt7925_mcu_chip_config(dev, cmd); +} +EXPORT_SYMBOL_GPL(mt7925_mcu_set_deep_sleep); + +int mt7925_mcu_set_thermal_protect(struct mt792x_dev *dev) +{ + char cmd[64]; + int ret = 0; + + snprintf(cmd, sizeof(cmd), "ThermalProtGband %d %d %d %d %d %d %d %d %d %d", + 0, 100, 90, 80, 30, 1, 1, 115, 105, 5); + ret = mt7925_mcu_chip_config(dev, cmd); + + snprintf(cmd, sizeof(cmd), "ThermalProtAband %d %d %d %d %d %d %d %d %d %d", + 1, 100, 90, 80, 30, 1, 1, 115, 105, 5); + ret |= mt7925_mcu_chip_config(dev, cmd); + + return ret; +} +EXPORT_SYMBOL_GPL(mt7925_mcu_set_thermal_protect); + +int mt7925_run_firmware(struct mt792x_dev *dev) +{ + int err; + + err = mt792x_load_firmware(dev); + if (err) + return err; + + err = mt7925_mcu_get_nic_capability(dev); + if (err) + return err; + + err = mt7925_load_clc(dev, mt792x_ram_name(dev)); + if (err) + return err; + set_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state); + + return mt7925_mcu_fw_log_2_host(dev, 1); +} +EXPORT_SYMBOL_GPL(mt7925_run_firmware); + +static void +mt7925_mcu_sta_hdr_trans_tlv(struct sk_buff *skb, + struct ieee80211_vif *vif, + struct mt792x_link_sta *mlink) +{ + struct sta_rec_hdr_trans *hdr_trans; + struct mt76_wcid *wcid; + struct tlv *tlv; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HDR_TRANS, sizeof(*hdr_trans)); + hdr_trans = (struct sta_rec_hdr_trans *)tlv; + hdr_trans->dis_rx_hdr_tran = true; + + if (vif->type == NL80211_IFTYPE_STATION) + hdr_trans->to_ds = true; + else + hdr_trans->from_ds = true; + + if (WARN_ON_ONCE(!mlink)) + return; + + wcid = &mlink->wcid; + + hdr_trans->dis_rx_hdr_tran = !test_bit(MT_WCID_FLAG_HDR_TRANS, &wcid->flags); + if (test_bit(MT_WCID_FLAG_4ADDR, &wcid->flags)) { + hdr_trans->to_ds = true; + hdr_trans->from_ds = true; + } + + if (test_bit(MT_WCID_FLAG_TDLS_PEER, &wcid->flags)) { + hdr_trans->to_ds = false; + hdr_trans->from_ds = false; + } +} + +int mt7925_mcu_wtbl_update_hdr_trans(struct mt792x_dev *dev, + struct ieee80211_vif *vif, + struct mt792x_bss_conf *mconf, + struct mt792x_link_sta *mlink) +{ + struct sk_buff *skb; + + skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mconf->mt76, + &mlink->wcid, + MT7925_STA_UPDATE_MAX_SIZE); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + mt7925_mcu_sta_hdr_trans_tlv(skb, vif, mlink); + return mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true); +} + +int mt7925_mcu_set_tx(struct mt792x_dev *dev, + struct ieee80211_bss_conf *bss_conf) +{ +#define MCU_EDCA_AC_PARAM 0 +#define WMM_AIFS_SET BIT(0) +#define WMM_CW_MIN_SET BIT(1) +#define WMM_CW_MAX_SET BIT(2) +#define WMM_TXOP_SET BIT(3) +#define WMM_PARAM_SET (WMM_AIFS_SET | WMM_CW_MIN_SET | \ + WMM_CW_MAX_SET | WMM_TXOP_SET) + struct mt792x_bss_conf *mconf = mt792x_link_conf_to_mconf(bss_conf); + struct { + u8 bss_idx; + u8 __rsv[3]; + } __packed hdr = { + .bss_idx = mconf->mt76.idx, + }; + struct sk_buff *skb; + int len = sizeof(hdr) + IEEE80211_NUM_ACS * sizeof(struct edca); + int ac; + + skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, len); + if (!skb) + return -ENOMEM; + + skb_put_data(skb, &hdr, sizeof(hdr)); + + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { + struct ieee80211_tx_queue_params *q = &mconf->queue_params[ac]; + struct edca *e; + struct tlv *tlv; + + tlv = mt76_connac_mcu_add_tlv(skb, MCU_EDCA_AC_PARAM, sizeof(*e)); + + e = (struct edca *)tlv; + e->set = WMM_PARAM_SET; + e->queue = ac; + e->aifs = q->aifs; + e->txop = cpu_to_le16(q->txop); + + if (q->cw_min) + e->cw_min = fls(q->cw_min); + else + e->cw_min = 5; + + if (q->cw_max) + e->cw_max = fls(q->cw_max); + else + e->cw_max = 10; + } + + return mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_UNI_CMD(EDCA_UPDATE), true); +} + +static int +mt7925_mcu_sta_key_tlv(struct mt76_wcid *wcid, + struct mt76_connac_sta_key_conf *sta_key_conf, + struct sk_buff *skb, + struct ieee80211_key_conf *key, + enum set_key_cmd cmd, + struct mt792x_sta *msta) +{ + struct mt792x_vif *mvif = msta->vif; + struct mt792x_bss_conf *mconf = mt792x_vif_to_link(mvif, wcid->link_id); + struct sta_rec_sec_uni *sec; + struct ieee80211_sta *sta; + struct ieee80211_vif *vif; + struct tlv *tlv; + + sta = msta == &mvif->sta ? + NULL : + container_of((void *)msta, struct ieee80211_sta, drv_priv); + vif = container_of((void *)mvif, struct ieee80211_vif, drv_priv); + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_KEY_V3, sizeof(*sec)); + sec = (struct sta_rec_sec_uni *)tlv; + sec->bss_idx = mconf->mt76.idx; + sec->is_authenticator = 0; + sec->mgmt_prot = 1; /* only used in MLO mode */ + sec->wlan_idx = (u8)wcid->idx; + + if (sta) { + struct ieee80211_link_sta *link_sta; + + sec->tx_key = 1; + sec->key_type = 1; + link_sta = mt792x_sta_to_link_sta(vif, sta, wcid->link_id); + + if (link_sta) + memcpy(sec->peer_addr, link_sta->addr, ETH_ALEN); + } else { + struct ieee80211_bss_conf *link_conf; + + link_conf = mt792x_vif_to_bss_conf(vif, wcid->link_id); + + if (link_conf) + memcpy(sec->peer_addr, link_conf->bssid, ETH_ALEN); + } + + if (cmd == SET_KEY) { + u8 cipher; + + sec->add = 1; + cipher = mt7925_mcu_get_cipher(key->cipher); + if (cipher == CONNAC3_CIPHER_NONE) + return -EOPNOTSUPP; + + if (cipher == CONNAC3_CIPHER_BIP_CMAC_128) { + sec->cipher_id = CONNAC3_CIPHER_BIP_CMAC_128; + sec->key_id = sta_key_conf->keyidx; + sec->key_len = 32; + memcpy(sec->key, sta_key_conf->key, 16); + memcpy(sec->key + 16, key->key, 16); + } else { + sec->cipher_id = cipher; + sec->key_id = key->keyidx; + sec->key_len = key->keylen; + memcpy(sec->key, key->key, key->keylen); + + if (cipher == CONNAC3_CIPHER_TKIP) { + /* Rx/Tx MIC keys are swapped */ + memcpy(sec->key + 16, key->key + 24, 8); + memcpy(sec->key + 24, key->key + 16, 8); + } + + /* store key_conf for BIP batch update */ + if (cipher == CONNAC3_CIPHER_AES_CCMP) { + memcpy(sta_key_conf->key, key->key, key->keylen); + sta_key_conf->keyidx = key->keyidx; + } + } + } else { + sec->add = 0; + } + + return 0; +} + +int mt7925_mcu_add_key(struct mt76_dev *dev, struct ieee80211_vif *vif, + struct mt76_connac_sta_key_conf *sta_key_conf, + struct ieee80211_key_conf *key, int mcu_cmd, + struct mt76_wcid *wcid, enum set_key_cmd cmd, + struct mt792x_sta *msta) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + struct mt792x_bss_conf *mconf = mt792x_vif_to_link(mvif, wcid->link_id); + struct sk_buff *skb; + int ret; + + skb = __mt76_connac_mcu_alloc_sta_req(dev, &mconf->mt76, wcid, + MT7925_STA_UPDATE_MAX_SIZE); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + ret = mt7925_mcu_sta_key_tlv(wcid, sta_key_conf, skb, key, cmd, msta); + if (ret) { + dev_kfree_skb(skb); + return ret; + } + + return mt76_mcu_skb_send_msg(dev, skb, mcu_cmd, true); +} + +int mt7925_mcu_set_mlo_roc(struct mt792x_phy *phy, struct mt792x_bss_conf *mconf, + u16 sel_links, int duration, u8 token_id) +{ + struct mt792x_vif *mvif = mconf->vif; + struct ieee80211_vif *vif = container_of((void *)mvif, + struct ieee80211_vif, drv_priv); + struct ieee80211_bss_conf *link_conf; + struct ieee80211_channel *chan; + const u8 ch_band[] = { + [NL80211_BAND_2GHZ] = 1, + [NL80211_BAND_5GHZ] = 2, + [NL80211_BAND_6GHZ] = 3, + }; + enum mt7925_roc_req type; + int center_ch, i = 0; + bool is_AG_band = false; + struct { + u8 id; + u8 bss_idx; + u16 tag; + struct mt792x_bss_conf *mconf; + struct ieee80211_channel *chan; + } links[2]; + + struct { + struct { + u8 rsv[4]; + } __packed hdr; + struct roc_acquire_tlv roc[2]; + } __packed req = { + .roc[0].tag = cpu_to_le16(UNI_ROC_NUM), + .roc[0].len = cpu_to_le16(sizeof(struct roc_acquire_tlv)), + .roc[1].tag = cpu_to_le16(UNI_ROC_NUM), + .roc[1].len = cpu_to_le16(sizeof(struct roc_acquire_tlv)) + }; + + struct wiphy *wiphy = phy->mt76->hw->wiphy; + + if (!mconf || hweight16(vif->valid_links) < 2 || + hweight16(sel_links) != 2) + return -EPERM; + + for (i = 0; i < ARRAY_SIZE(links); i++) { + links[i].id = i ? __ffs(~BIT(mconf->link_id) & sel_links) : + mconf->link_id; + link_conf = mt792x_vif_to_bss_conf(vif, links[i].id); + if (WARN_ON_ONCE(!link_conf)) + return -EPERM; + + links[i].chan = link_conf->chanreq.oper.chan; + if (WARN_ON_ONCE(!links[i].chan)) + return -EPERM; + + links[i].mconf = mt792x_vif_to_link(mvif, links[i].id); + links[i].tag = links[i].id == mconf->link_id ? + UNI_ROC_ACQUIRE : UNI_ROC_SUB_LINK; + + is_AG_band |= links[i].chan->band == NL80211_BAND_2GHZ; + } + + if (!(wiphy->iftype_ext_capab[0].mld_capa_and_ops & + IEEE80211_MLD_CAP_OP_MAX_SIMUL_LINKS)) + type = is_AG_band ? MT7925_ROC_REQ_MLSR_AG : + MT7925_ROC_REQ_MLSR_AA; + else + type = MT7925_ROC_REQ_JOIN; + + for (i = 0; i < ARRAY_SIZE(links) && i < hweight16(vif->active_links); i++) { + if (WARN_ON_ONCE(!links[i].mconf || !links[i].chan)) + continue; + + chan = links[i].chan; + center_ch = ieee80211_frequency_to_channel(chan->center_freq); + req.roc[i].len = cpu_to_le16(sizeof(struct roc_acquire_tlv)); + req.roc[i].tag = cpu_to_le16(links[i].tag); + req.roc[i].tokenid = token_id; + req.roc[i].reqtype = type; + req.roc[i].maxinterval = cpu_to_le32(duration); + req.roc[i].bss_idx = links[i].mconf->mt76.idx; + req.roc[i].control_channel = chan->hw_value; + req.roc[i].bw = CMD_CBW_20MHZ; + req.roc[i].bw_from_ap = CMD_CBW_20MHZ; + req.roc[i].center_chan = center_ch; + req.roc[i].center_chan_from_ap = center_ch; + req.roc[i].center_chan2 = 0; + req.roc[i].center_chan2_from_ap = 0; + + /* STR : 0xfe indicates BAND_ALL with enabling DBDC + * EMLSR : 0xff indicates (BAND_AUTO) without DBDC + */ + req.roc[i].dbdcband = type == MT7925_ROC_REQ_JOIN ? 0xfe : 0xff; + + if (chan->hw_value < center_ch) + req.roc[i].sco = 1; /* SCA */ + else if (chan->hw_value > center_ch) + req.roc[i].sco = 3; /* SCB */ + + req.roc[i].band = ch_band[chan->band]; + } + + return mt76_mcu_send_msg(&mvif->phy->dev->mt76, MCU_UNI_CMD(ROC), + &req, sizeof(req), true); +} + +int mt7925_mcu_set_roc(struct mt792x_phy *phy, struct mt792x_bss_conf *mconf, + struct ieee80211_channel *chan, int duration, + enum mt7925_roc_req type, u8 token_id) +{ + int center_ch = ieee80211_frequency_to_channel(chan->center_freq); + struct mt792x_dev *dev = phy->dev; + struct { + struct { + u8 rsv[4]; + } __packed hdr; + struct roc_acquire_tlv roc; + } __packed req = { + .roc = { + .tag = cpu_to_le16(UNI_ROC_ACQUIRE), + .len = cpu_to_le16(sizeof(struct roc_acquire_tlv)), + .tokenid = token_id, + .reqtype = type, + .maxinterval = cpu_to_le32(duration), + .bss_idx = mconf->mt76.idx, + .control_channel = chan->hw_value, + .bw = CMD_CBW_20MHZ, + .bw_from_ap = CMD_CBW_20MHZ, + .center_chan = center_ch, + .center_chan_from_ap = center_ch, + .dbdcband = 0xff, /* auto */ + }, + }; + + if (chan->hw_value < center_ch) + req.roc.sco = 1; /* SCA */ + else if (chan->hw_value > center_ch) + req.roc.sco = 3; /* SCB */ + + switch (chan->band) { + case NL80211_BAND_6GHZ: + req.roc.band = 3; + break; + case NL80211_BAND_5GHZ: + req.roc.band = 2; + break; + default: + req.roc.band = 1; + break; + } + + return mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(ROC), + &req, sizeof(req), true); +} + +int mt7925_mcu_abort_roc(struct mt792x_phy *phy, struct mt792x_bss_conf *mconf, + u8 token_id) +{ + struct mt792x_dev *dev = phy->dev; + struct { + struct { + u8 rsv[4]; + } __packed hdr; + struct roc_abort_tlv { + __le16 tag; + __le16 len; + u8 bss_idx; + u8 tokenid; + u8 dbdcband; + u8 rsv[5]; + } __packed abort; + } __packed req = { + .abort = { + .tag = cpu_to_le16(UNI_ROC_ABORT), + .len = cpu_to_le16(sizeof(struct roc_abort_tlv)), + .tokenid = token_id, + .bss_idx = mconf->mt76.idx, + .dbdcband = 0xff, /* auto*/ + }, + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(ROC), + &req, sizeof(req), true); +} + +int mt7925_mcu_set_eeprom(struct mt792x_dev *dev) +{ + struct { + u8 _rsv[4]; + + __le16 tag; + __le16 len; + u8 buffer_mode; + u8 format; + __le16 buf_len; + } __packed req = { + .tag = cpu_to_le16(UNI_EFUSE_BUFFER_MODE), + .len = cpu_to_le16(sizeof(req) - 4), + .buffer_mode = EE_MODE_EFUSE, + .format = EE_FORMAT_WHOLE, + .buf_len = 0 + }; + + return mt76_mcu_send_and_get_msg(&dev->mt76, MCU_UNI_CMD(EFUSE_CTRL), + &req, sizeof(req), true, NULL); +} +EXPORT_SYMBOL_GPL(mt7925_mcu_set_eeprom); + +int mt7925_mcu_uni_bss_ps(struct mt792x_dev *dev, + struct ieee80211_bss_conf *link_conf) +{ + struct mt792x_bss_conf *mconf = mt792x_link_conf_to_mconf(link_conf); + struct { + struct { + u8 bss_idx; + u8 pad[3]; + } __packed hdr; + struct ps_tlv { + __le16 tag; + __le16 len; + u8 ps_state; /* 0: device awake + * 1: static power save + * 2: dynamic power saving + * 3: enter TWT power saving + * 4: leave TWT power saving + */ + u8 pad[3]; + } __packed ps; + } __packed ps_req = { + .hdr = { + .bss_idx = mconf->mt76.idx, + }, + .ps = { + .tag = cpu_to_le16(UNI_BSS_INFO_PS), + .len = cpu_to_le16(sizeof(struct ps_tlv)), + .ps_state = link_conf->vif->cfg.ps ? 2 : 0, + }, + }; + + if (link_conf->vif->type != NL80211_IFTYPE_STATION) + return -EOPNOTSUPP; + + return mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(BSS_INFO_UPDATE), + &ps_req, sizeof(ps_req), true); +} + +int +mt7925_mcu_uni_bss_bcnft(struct mt792x_dev *dev, + struct ieee80211_bss_conf *link_conf, bool enable) +{ + struct mt792x_bss_conf *mconf = mt792x_link_conf_to_mconf(link_conf); + struct { + struct { + u8 bss_idx; + u8 pad[3]; + } __packed hdr; + struct bcnft_tlv { + __le16 tag; + __le16 len; + __le16 bcn_interval; + u8 dtim_period; + u8 bmc_delivered_ac; + u8 bmc_triggered_ac; + u8 pad[3]; + } __packed bcnft; + } __packed bcnft_req = { + .hdr = { + .bss_idx = mconf->mt76.idx, + }, + .bcnft = { + .tag = cpu_to_le16(UNI_BSS_INFO_BCNFT), + .len = cpu_to_le16(sizeof(struct bcnft_tlv)), + .bcn_interval = cpu_to_le16(link_conf->beacon_int), + .dtim_period = link_conf->dtim_period, + }, + }; + + if (link_conf->vif->type != NL80211_IFTYPE_STATION) + return 0; + + return mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(BSS_INFO_UPDATE), + &bcnft_req, sizeof(bcnft_req), true); +} + +int +mt7925_mcu_set_bss_pm(struct mt792x_dev *dev, + struct ieee80211_bss_conf *link_conf, + bool enable) +{ + struct mt792x_bss_conf *mconf = mt792x_link_conf_to_mconf(link_conf); + struct { + struct { + u8 bss_idx; + u8 pad[3]; + } __packed hdr; + struct bcnft_tlv { + __le16 tag; + __le16 len; + __le16 bcn_interval; + u8 dtim_period; + u8 bmc_delivered_ac; + u8 bmc_triggered_ac; + u8 pad[3]; + } __packed enable; + } req = { + .hdr = { + .bss_idx = mconf->mt76.idx, + }, + .enable = { + .tag = cpu_to_le16(UNI_BSS_INFO_BCNFT), + .len = cpu_to_le16(sizeof(struct bcnft_tlv)), + .dtim_period = link_conf->dtim_period, + .bcn_interval = cpu_to_le16(link_conf->beacon_int), + }, + }; + struct { + struct { + u8 bss_idx; + u8 pad[3]; + } __packed hdr; + struct pm_disable { + __le16 tag; + __le16 len; + } __packed disable; + } req1 = { + .hdr = { + .bss_idx = mconf->mt76.idx, + }, + .disable = { + .tag = cpu_to_le16(UNI_BSS_INFO_PM_DISABLE), + .len = cpu_to_le16(sizeof(struct pm_disable)) + }, + }; + int err; + + err = mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(BSS_INFO_UPDATE), + &req1, sizeof(req1), true); + if (err < 0 || !enable) + return err; + + return mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(BSS_INFO_UPDATE), + &req, sizeof(req), true); +} + +static void +mt7925_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_link_sta *link_sta) +{ + if (!link_sta->he_cap.has_he) + return; + + mt76_connac_mcu_sta_he_tlv_v2(skb, link_sta->sta); +} + +static void +mt7925_mcu_sta_he_6g_tlv(struct sk_buff *skb, + struct ieee80211_link_sta *link_sta) +{ + struct sta_rec_he_6g_capa *he_6g; + struct tlv *tlv; + + if (!link_sta->he_6ghz_capa.capa) + return; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HE_6G, sizeof(*he_6g)); + + he_6g = (struct sta_rec_he_6g_capa *)tlv; + he_6g->capa = link_sta->he_6ghz_capa.capa; +} + +static void +mt7925_mcu_sta_eht_tlv(struct sk_buff *skb, struct ieee80211_link_sta *link_sta) +{ + struct ieee80211_eht_mcs_nss_supp *mcs_map; + struct ieee80211_eht_cap_elem_fixed *elem; + struct sta_rec_eht *eht; + struct tlv *tlv; + + if (!link_sta->eht_cap.has_eht) + return; + + mcs_map = &link_sta->eht_cap.eht_mcs_nss_supp; + elem = &link_sta->eht_cap.eht_cap_elem; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_EHT, sizeof(*eht)); + + eht = (struct sta_rec_eht *)tlv; + eht->tid_bitmap = 0xff; + eht->mac_cap = cpu_to_le16(*(u16 *)elem->mac_cap_info); + eht->phy_cap = cpu_to_le64(*(u64 *)elem->phy_cap_info); + eht->phy_cap_ext = cpu_to_le64(elem->phy_cap_info[8]); + + if (link_sta->bandwidth == IEEE80211_STA_RX_BW_20) + memcpy(eht->mcs_map_bw20, &mcs_map->only_20mhz, sizeof(eht->mcs_map_bw20)); + memcpy(eht->mcs_map_bw80, &mcs_map->bw._80, sizeof(eht->mcs_map_bw80)); + memcpy(eht->mcs_map_bw160, &mcs_map->bw._160, sizeof(eht->mcs_map_bw160)); + memcpy(eht->mcs_map_bw320, &mcs_map->bw._320, sizeof(eht->mcs_map_bw320)); +} + +static void +mt7925_mcu_sta_ht_tlv(struct sk_buff *skb, struct ieee80211_link_sta *link_sta) +{ + struct sta_rec_ht *ht; + struct tlv *tlv; + + if (!link_sta->ht_cap.ht_supported) + return; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HT, sizeof(*ht)); + + ht = (struct sta_rec_ht *)tlv; + ht->ht_cap = cpu_to_le16(link_sta->ht_cap.cap); +} + +static void +mt7925_mcu_sta_vht_tlv(struct sk_buff *skb, struct ieee80211_link_sta *link_sta) +{ + struct sta_rec_vht *vht; + struct tlv *tlv; + + /* For 6G band, this tlv is necessary to let hw work normally */ + if (!link_sta->he_6ghz_capa.capa && !link_sta->vht_cap.vht_supported) + return; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_VHT, sizeof(*vht)); + + vht = (struct sta_rec_vht *)tlv; + vht->vht_cap = cpu_to_le32(link_sta->vht_cap.cap); + vht->vht_rx_mcs_map = link_sta->vht_cap.vht_mcs.rx_mcs_map; + vht->vht_tx_mcs_map = link_sta->vht_cap.vht_mcs.tx_mcs_map; +} + +static void +mt7925_mcu_sta_amsdu_tlv(struct sk_buff *skb, + struct ieee80211_vif *vif, + struct ieee80211_link_sta *link_sta, + struct mt792x_link_sta *mlink) +{ + struct sta_rec_amsdu *amsdu; + struct tlv *tlv; + + if (vif->type != NL80211_IFTYPE_STATION && + vif->type != NL80211_IFTYPE_AP) + return; + + if (!link_sta->agg.max_amsdu_len) + return; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HW_AMSDU, sizeof(*amsdu)); + amsdu = (struct sta_rec_amsdu *)tlv; + amsdu->max_amsdu_num = 8; + amsdu->amsdu_en = true; + + mlink->wcid.amsdu = true; + + switch (link_sta->agg.max_amsdu_len) { + case IEEE80211_MAX_MPDU_LEN_VHT_11454: + amsdu->max_mpdu_size = + IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454; + return; + case IEEE80211_MAX_MPDU_LEN_HT_7935: + case IEEE80211_MAX_MPDU_LEN_VHT_7991: + amsdu->max_mpdu_size = IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991; + return; + default: + amsdu->max_mpdu_size = IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895; + return; + } +} + +static void +mt7925_mcu_sta_phy_tlv(struct sk_buff *skb, + struct ieee80211_vif *vif, + struct ieee80211_link_sta *link_sta) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + struct ieee80211_bss_conf *link_conf; + struct cfg80211_chan_def *chandef; + struct mt792x_bss_conf *mconf; + struct sta_rec_phy *phy; + struct tlv *tlv; + u8 af = 0, mm = 0; + + link_conf = mt792x_vif_to_bss_conf(vif, link_sta->link_id); + mconf = mt792x_vif_to_link(mvif, link_sta->link_id); + chandef = mconf->mt76.ctx ? &mconf->mt76.ctx->def : + &link_conf->chanreq.oper; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_PHY, sizeof(*phy)); + phy = (struct sta_rec_phy *)tlv; + + if (mt7925_vif_is_nan(vif)) { + enum nl80211_band band = chandef->chan ? chandef->chan->band + : NL80211_BAND_2GHZ; + phy->phy_type = PHY_TYPE_BIT_OFDM | PHY_TYPE_BIT_ERP; + phy->phy_type |= mt76_connac_get_phy_mode_v2(mvif->phy->mt76, vif, + band, + link_sta); + } else { + phy->phy_type = mt76_connac_get_phy_mode_v2(mvif->phy->mt76, vif, + chandef->chan->band, + link_sta); + } + + phy->basic_rate = cpu_to_le16((u16)link_conf->basic_rates); + if (link_sta->ht_cap.ht_supported) { + af = link_sta->ht_cap.ampdu_factor; + mm = link_sta->ht_cap.ampdu_density; + } + + if (link_sta->vht_cap.vht_supported) { + u8 vht_af = FIELD_GET(IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK, + link_sta->vht_cap.cap); + + af = max_t(u8, af, vht_af); + } + + if (link_sta->he_6ghz_capa.capa) { + af = le16_get_bits(link_sta->he_6ghz_capa.capa, + IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP); + mm = le16_get_bits(link_sta->he_6ghz_capa.capa, + IEEE80211_HE_6GHZ_CAP_MIN_MPDU_START); + } + + phy->ampdu = FIELD_PREP(IEEE80211_HT_AMPDU_PARM_FACTOR, af) | + FIELD_PREP(IEEE80211_HT_AMPDU_PARM_DENSITY, mm); + phy->max_ampdu_len = af; +} + +static void +mt7925_mcu_sta_state_v2_tlv(struct mt76_phy *mphy, struct sk_buff *skb, + struct ieee80211_link_sta *link_sta, + struct ieee80211_vif *vif, + u8 rcpi, u8 sta_state) +{ + struct sta_rec_state_v2 { + __le16 tag; + __le16 len; + u8 state; + u8 rsv[3]; + __le32 flags; + u8 vht_opmode; + u8 action; + u8 rsv2[2]; + } __packed * state; + struct tlv *tlv; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_STATE, sizeof(*state)); + state = (struct sta_rec_state_v2 *)tlv; + state->state = sta_state; + + if (link_sta->vht_cap.vht_supported) { + state->vht_opmode = link_sta->bandwidth; + state->vht_opmode |= link_sta->rx_nss << + IEEE80211_OPMODE_NOTIF_RX_NSS_SHIFT; + } +} + +static void +mt7925_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, + struct ieee80211_vif *vif, + struct ieee80211_link_sta *link_sta) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + struct ieee80211_bss_conf *link_conf; + struct cfg80211_chan_def *chandef; + struct sta_rec_ra_info *ra_info; + struct mt792x_bss_conf *mconf; + enum nl80211_band band; + struct tlv *tlv; + u16 supp_rates; + + link_conf = mt792x_vif_to_bss_conf(vif, link_sta->link_id); + mconf = mt792x_vif_to_link(mvif, link_sta->link_id); + chandef = mconf->mt76.ctx ? &mconf->mt76.ctx->def : + &link_conf->chanreq.oper; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_RA, sizeof(*ra_info)); + ra_info = (struct sta_rec_ra_info *)tlv; + + if (mt7925_vif_is_nan(vif)) + band = chandef->chan ? chandef->chan->band : NL80211_BAND_2GHZ; + else + band = chandef->chan->band; + + supp_rates = link_sta->supp_rates[band]; + if (band == NL80211_BAND_2GHZ) + supp_rates = FIELD_PREP(RA_LEGACY_OFDM, supp_rates >> 4) | + FIELD_PREP(RA_LEGACY_CCK, supp_rates & 0xf); + else + supp_rates = FIELD_PREP(RA_LEGACY_OFDM, supp_rates); + + ra_info->legacy = cpu_to_le16(supp_rates); + + if (link_sta->ht_cap.ht_supported) + memcpy(ra_info->rx_mcs_bitmask, + link_sta->ht_cap.mcs.rx_mask, + HT_MCS_MASK_NUM); +} + +static void +mt7925_mcu_sta_eht_mld_tlv(struct sk_buff *skb, + struct ieee80211_vif *vif, struct ieee80211_sta *sta) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + struct wiphy *wiphy = mvif->phy->mt76->hw->wiphy; + const struct wiphy_iftype_ext_capab *ext_capa; + struct sta_rec_eht_mld *eht_mld; + struct tlv *tlv; + u16 eml_cap; + + if (!ieee80211_vif_is_mld(vif)) + return; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_EHT_MLD, sizeof(*eht_mld)); + eht_mld = (struct sta_rec_eht_mld *)tlv; + eht_mld->mld_type = 0xff; + + ext_capa = cfg80211_get_iftype_ext_capa(wiphy, + ieee80211_vif_type_p2p(vif)); + if (!ext_capa) + return; + + eml_cap = (vif->cfg.eml_cap & (IEEE80211_EML_CAP_EMLSR_SUPP | + IEEE80211_EML_CAP_TRANSITION_TIMEOUT)) | + (ext_capa->eml_capabilities & (IEEE80211_EML_CAP_EML_PADDING_DELAY | + IEEE80211_EML_CAP_EML_TRANSITION_DELAY)); + + if (eml_cap & IEEE80211_EML_CAP_EMLSR_SUPP) { + eht_mld->eml_cap[0] = u16_get_bits(eml_cap, GENMASK(7, 0)); + eht_mld->eml_cap[1] = u16_get_bits(eml_cap, GENMASK(15, 8)); + } else { + eht_mld->str_cap[0] = BIT(1); + } +} + +static void +mt7925_mcu_sta_mld_tlv(struct sk_buff *skb, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct mt792x_bss_conf *mconf, + struct mt792x_link_sta *mlink) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv; + struct mt792x_dev *dev = mvif->phy->dev; + struct mt792x_bss_conf *mconf_pri; + struct sta_rec_mld *mld; + struct tlv *tlv; + u8 cnt = 0; + + /* Primary link always uses driver's deflink WCID. */ + mconf_pri = (msta->deflink_id != IEEE80211_LINK_UNSPECIFIED) ? + mt792x_vif_to_link(mvif, msta->deflink_id) : NULL; + + /* If caller is operating on deflink, reuse its mconf as primary. */ + if (!mconf_pri && mlink == &msta->deflink) + mconf_pri = mconf; + + if (!mconf_pri) { + dev_warn_ratelimited(dev->mt76.dev, + "mt7925: MLD_TLV_LINK skip (no primary mconf) sta=%pM\n", + sta->addr); + return; + } + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_MLD, sizeof(*mld)); + mld = (struct sta_rec_mld *)tlv; + memcpy(mld->mac_addr, sta->addr, ETH_ALEN); + + mld->primary_id = cpu_to_le16(msta->deflink.wcid.idx); + mld->wlan_id = cpu_to_le16(msta->deflink.wcid.idx); + + /* Always encode primary link first. */ + mld->link[cnt].wlan_id = cpu_to_le16(msta->deflink.wcid.idx); + mld->link[cnt++].bss_idx = mconf_pri->mt76.idx; + + /* Optionally encode the currently-updated secondary link. */ + if (mlink && mlink != &msta->deflink && mconf) { + mld->secondary_id = cpu_to_le16(mlink->wcid.idx); + mld->link[cnt].wlan_id = cpu_to_le16(mlink->wcid.idx); + mld->link[cnt++].bss_idx = mconf->mt76.idx; + } + + mld->link_num = cnt; +} + +static void +mt7925_mcu_sta_remove_tlv(struct sk_buff *skb) +{ + struct sta_rec_remove *rem; + struct tlv *tlv; + + tlv = mt76_connac_mcu_add_tlv(skb, 0x25, sizeof(*rem)); + rem = (struct sta_rec_remove *)tlv; + rem->action = 0; +} + +static int +mt7925_mcu_sta_cmd(struct mt76_phy *phy, + struct mt76_sta_cmd_info *info) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)info->vif->drv_priv; + struct mt76_dev *dev = phy->dev; + struct mt792x_bss_conf *mconf; + struct mt792x_link_sta *mlink; + struct sk_buff *skb; + int conn_state; + + mconf = mt792x_vif_to_link(mvif, info->wcid->link_id); + mlink = container_of(info->wcid, struct mt792x_link_sta, wcid); + + skb = __mt76_connac_mcu_alloc_sta_req(dev, &mconf->mt76, info->wcid, + MT7925_STA_UPDATE_MAX_SIZE); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + conn_state = info->enable ? CONN_STATE_PORT_SECURE : + CONN_STATE_DISCONNECT; + + if (info->enable && info->link_sta) { + mt76_connac_mcu_sta_basic_tlv(dev, skb, info->link_conf, + info->link_sta, + conn_state, info->newly); + mt7925_mcu_sta_phy_tlv(skb, info->vif, info->link_sta); + mt7925_mcu_sta_ht_tlv(skb, info->link_sta); + mt7925_mcu_sta_vht_tlv(skb, info->link_sta); + mt76_connac_mcu_sta_uapsd(skb, info->vif, info->link_sta->sta); + mt7925_mcu_sta_amsdu_tlv(skb, info->vif, info->link_sta, mlink); + mt7925_mcu_sta_he_tlv(skb, info->link_sta); + mt7925_mcu_sta_he_6g_tlv(skb, info->link_sta); + mt7925_mcu_sta_eht_tlv(skb, info->link_sta); + mt7925_mcu_sta_rate_ctrl_tlv(skb, info->vif, + info->link_sta); + mt7925_mcu_sta_state_v2_tlv(phy, skb, info->link_sta, + info->vif, info->rcpi, + info->state); + + if (info->state != MT76_STA_INFO_STATE_NONE) { + mt7925_mcu_sta_mld_tlv(skb, info->vif, + info->link_sta->sta, + mconf, mlink); + + mt7925_mcu_sta_eht_mld_tlv(skb, info->vif, info->link_sta->sta); + } + } + + if (!info->enable) { + mt7925_mcu_sta_remove_tlv(skb); + mt76_connac_mcu_add_tlv(skb, STA_REC_MLD_OFF, + sizeof(struct tlv)); + } else { + if (!info->link_sta) + mlink = &mvif->sta.deflink; + + mt7925_mcu_sta_hdr_trans_tlv(skb, info->vif, mlink); + } + + return mt76_mcu_skb_send_msg(dev, skb, info->cmd, true); +} + +int mt7925_mcu_sta_update(struct mt792x_dev *dev, + struct ieee80211_link_sta *link_sta, + struct ieee80211_vif *vif, + struct mt792x_link_sta *mlink, + bool enable, + enum mt76_sta_info_state state) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + int rssi = -ewma_rssi_read(&mvif->bss_conf.rssi); + struct mt76_sta_cmd_info info = { + .link_sta = link_sta, + .vif = vif, + .link_conf = &vif->bss_conf, + .enable = enable, + .cmd = MCU_UNI_CMD(STA_REC_UPDATE), + .state = state, + .offload_fw = true, + .rcpi = to_rcpi(rssi), + }; + + info.wcid = &mlink->wcid; + info.newly = state != MT76_STA_INFO_STATE_ASSOC; + + return mt7925_mcu_sta_cmd(&dev->mphy, &info); +} + +int mt7925_mcu_set_beacon_filter(struct mt792x_dev *dev, + struct ieee80211_vif *vif, + bool enable) +{ +#define MT7925_FIF_BIT_CLR BIT(1) +#define MT7925_FIF_BIT_SET BIT(0) + int err = 0; + + if (enable) { + err = mt7925_mcu_uni_bss_bcnft(dev, &vif->bss_conf, true); + if (err < 0) + return err; + + return mt7925_mcu_set_rxfilter(dev, 0, + MT7925_FIF_BIT_SET, + MT_WF_RFCR_DROP_OTHER_BEACON); + } + + err = mt7925_mcu_set_bss_pm(dev, &vif->bss_conf, false); + if (err < 0) + return err; + + return mt7925_mcu_set_rxfilter(dev, 0, + MT7925_FIF_BIT_CLR, + MT_WF_RFCR_DROP_OTHER_BEACON); +} + +int mt7925_get_txpwr_info(struct mt792x_dev *dev, u8 band_idx, struct mt7925_txpwr *txpwr) +{ +#define TX_POWER_SHOW_INFO 0x7 +#define TXPOWER_ALL_RATE_POWER_INFO 0x2 + struct mt7925_txpwr_event *event; + struct mt7925_txpwr_req req = { + .tag = cpu_to_le16(TX_POWER_SHOW_INFO), + .len = cpu_to_le16(sizeof(req) - 4), + .catg = TXPOWER_ALL_RATE_POWER_INFO, + .band_idx = band_idx, + }; + struct sk_buff *skb; + int ret; + + ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_UNI_CMD(TXPOWER), + &req, sizeof(req), true, &skb); + if (ret) + return ret; + + event = (struct mt7925_txpwr_event *)skb->data; + memcpy(txpwr, &event->txpwr, sizeof(event->txpwr)); + + dev_kfree_skb(skb); + + return 0; +} + +int mt7925_mcu_set_sniffer(struct mt792x_dev *dev, struct ieee80211_vif *vif, + bool enable) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + struct ieee80211_chanctx_conf *ctx = mvif->bss_conf.mt76.ctx; + struct { + struct { + u8 band_idx; + u8 pad[3]; + } __packed hdr; + struct sniffer_enable_tlv { + __le16 tag; + __le16 len; + u8 enable; + u8 pad[3]; + } __packed enable; + } __packed req = { + .hdr = { + .band_idx = 0, + }, + .enable = { + .tag = cpu_to_le16(UNI_SNIFFER_ENABLE), + .len = cpu_to_le16(sizeof(struct sniffer_enable_tlv)), + .enable = enable, + }, + }; + + if (is_mt7927(&dev->mt76)) { + struct ieee80211_channel *chan; + + chan = ctx ? ctx->def.chan : mvif->phy->mt76->chandef.chan; + + if (chan) + req.hdr.band_idx = mt7927_band_idx(chan->band); + } + + return mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(SNIFFER), &req, sizeof(req), + true); +} + +int mt7925_mcu_config_sniffer(struct mt792x_vif *vif, + struct ieee80211_chanctx_conf *ctx) +{ + struct mt76_phy *mphy = vif->phy->mt76; + struct cfg80211_chan_def *chandef = ctx ? &ctx->def : &mphy->chandef; + int freq1 = chandef->center_freq1, freq2 = chandef->center_freq2; + + static const u8 ch_band[] = { + [NL80211_BAND_2GHZ] = 1, + [NL80211_BAND_5GHZ] = 2, + [NL80211_BAND_6GHZ] = 3, + }; + static const u8 ch_width[] = { + [NL80211_CHAN_WIDTH_20_NOHT] = 0, + [NL80211_CHAN_WIDTH_20] = 0, + [NL80211_CHAN_WIDTH_40] = 0, + [NL80211_CHAN_WIDTH_80] = 1, + [NL80211_CHAN_WIDTH_160] = 2, + [NL80211_CHAN_WIDTH_80P80] = 3, + [NL80211_CHAN_WIDTH_5] = 4, + [NL80211_CHAN_WIDTH_10] = 5, + [NL80211_CHAN_WIDTH_320] = 6, + }; + + struct { + struct { + u8 band_idx; + u8 pad[3]; + } __packed hdr; + struct config_tlv { + __le16 tag; + __le16 len; + u16 aid; + u8 ch_band; + u8 bw; + u8 control_ch; + u8 sco; + u8 center_ch; + u8 center_ch2; + u8 drop_err; + u8 pad[3]; + } __packed tlv; + } __packed req = { + .hdr = { + .band_idx = 0, + }, + .tlv = { + .tag = cpu_to_le16(UNI_SNIFFER_CONFIG), + .len = cpu_to_le16(sizeof(req.tlv)), + .control_ch = chandef->chan->hw_value, + .center_ch = ieee80211_frequency_to_channel(freq1), + .drop_err = 1, + }, + }; + + if (is_mt7927(mphy->dev)) + req.hdr.band_idx = mt7927_band_idx(chandef->chan->band); + + if (chandef->chan->band < ARRAY_SIZE(ch_band)) + req.tlv.ch_band = ch_band[chandef->chan->band]; + if (chandef->width < ARRAY_SIZE(ch_width)) + req.tlv.bw = ch_width[chandef->width]; + + if (freq2) + req.tlv.center_ch2 = ieee80211_frequency_to_channel(freq2); + + if (req.tlv.control_ch < req.tlv.center_ch) + req.tlv.sco = 1; /* SCA */ + else if (req.tlv.control_ch > req.tlv.center_ch) + req.tlv.sco = 3; /* SCB */ + + return mt76_mcu_send_msg(mphy->dev, MCU_UNI_CMD(SNIFFER), + &req, sizeof(req), true); +} + +int +mt7925_mcu_uni_add_beacon_offload(struct mt792x_dev *dev, + struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + bool enable) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + struct ieee80211_mutable_offsets offs; + struct { + struct req_hdr { + u8 bss_idx; + u8 pad[3]; + } __packed hdr; + struct bcn_content_tlv { + __le16 tag; + __le16 len; + __le16 tim_ie_pos; + __le16 csa_ie_pos; + __le16 bcc_ie_pos; + /* 0: disable beacon offload + * 1: enable beacon offload + * 2: update probe respond offload + */ + u8 enable; + /* 0: legacy format (TXD + payload) + * 1: only cap field IE + */ + u8 type; + __le16 pkt_len; + u8 pkt[512]; + } __packed beacon_tlv; + } req = { + .hdr = { + .bss_idx = mvif->bss_conf.mt76.idx, + }, + .beacon_tlv = { + .tag = cpu_to_le16(UNI_BSS_INFO_BCN_CONTENT), + .len = cpu_to_le16(sizeof(struct bcn_content_tlv)), + .enable = enable, + .type = 1, + }, + }; + struct sk_buff *skb; + u8 cap_offs; + + /* support enable/update process only + * disable flow would be handled in bss stop handler automatically + */ + if (!enable) + return -EOPNOTSUPP; + + skb = ieee80211_beacon_get_template(mt76_hw(dev), vif, &offs, 0); + if (!skb) + return -EINVAL; + + cap_offs = offsetof(struct ieee80211_mgmt, u.beacon.capab_info); + if (!skb_pull(skb, cap_offs)) { + dev_err(dev->mt76.dev, "beacon format err\n"); + dev_kfree_skb(skb); + return -EINVAL; + } + + if (skb->len > 512) { + dev_err(dev->mt76.dev, "beacon size limit exceed\n"); + dev_kfree_skb(skb); + return -EINVAL; + } + + memcpy(req.beacon_tlv.pkt, skb->data, skb->len); + req.beacon_tlv.pkt_len = cpu_to_le16(skb->len); + offs.tim_offset -= cap_offs; + req.beacon_tlv.tim_ie_pos = cpu_to_le16(offs.tim_offset); + + if (offs.cntdwn_counter_offs[0]) { + u16 csa_offs; + + csa_offs = offs.cntdwn_counter_offs[0] - cap_offs - 4; + req.beacon_tlv.csa_ie_pos = cpu_to_le16(csa_offs); + } + dev_kfree_skb(skb); + + return mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(BSS_INFO_UPDATE), + &req, sizeof(req), true); +} + +static +void mt7925_mcu_bss_rlm_tlv(struct sk_buff *skb, struct mt76_phy *phy, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_chanctx_conf *ctx) +{ + struct cfg80211_chan_def *chandef = ctx ? &ctx->def : + &link_conf->chanreq.oper; + struct bss_rlm_tlv *req; + enum nl80211_band band; + int freq1, freq2; + struct tlv *tlv; + + if (WARN_ON_ONCE(!chandef || !chandef->chan)) + return; + + freq1 = chandef->center_freq1; + freq2 = chandef->center_freq2; + band = chandef->chan->band; + + tlv = mt76_connac_mcu_add_tlv(skb, UNI_BSS_INFO_RLM, sizeof(*req)); + req = (struct bss_rlm_tlv *)tlv; + req->control_channel = chandef->chan->hw_value; + req->center_chan = ieee80211_frequency_to_channel(freq1); + req->center_chan2 = 0; + req->tx_streams = hweight8(phy->antenna_mask); + req->ht_op_info = 4; /* set HT 40M allowed */ + req->rx_streams = hweight8(phy->antenna_mask); + req->center_chan2 = 0; + req->sco = 0; + req->band = 1; + + switch (band) { + case NL80211_BAND_2GHZ: + req->band = 1; + break; + case NL80211_BAND_5GHZ: + req->band = 2; + break; + case NL80211_BAND_6GHZ: + req->band = 3; + break; + default: + break; + } + + switch (chandef->width) { + case NL80211_CHAN_WIDTH_40: + req->bw = CMD_CBW_40MHZ; + break; + case NL80211_CHAN_WIDTH_80: + req->bw = CMD_CBW_80MHZ; + break; + case NL80211_CHAN_WIDTH_80P80: + req->bw = CMD_CBW_8080MHZ; + req->center_chan2 = ieee80211_frequency_to_channel(freq2); + break; + case NL80211_CHAN_WIDTH_160: + req->bw = CMD_CBW_160MHZ; + break; + case NL80211_CHAN_WIDTH_320: + req->bw = CMD_CBW_320MHZ; + req->center_chan2 = ieee80211_frequency_to_channel(freq2); + break; + case NL80211_CHAN_WIDTH_5: + req->bw = CMD_CBW_5MHZ; + break; + case NL80211_CHAN_WIDTH_10: + req->bw = CMD_CBW_10MHZ; + break; + case NL80211_CHAN_WIDTH_20_NOHT: + case NL80211_CHAN_WIDTH_20: + default: + req->bw = CMD_CBW_20MHZ; + req->ht_op_info = 0; + break; + } + + if (req->control_channel < req->center_chan) + req->sco = 1; /* SCA */ + else if (req->control_channel > req->center_chan) + req->sco = 3; /* SCB */ +} + +static struct sk_buff * +__mt7925_mcu_alloc_bss_req(struct mt76_dev *dev, struct mt76_vif_link *mvif, int len) +{ + struct bss_req_hdr hdr = { + .bss_idx = mvif->idx, + }; + struct sk_buff *skb; + + skb = mt76_mcu_msg_alloc(dev, NULL, len); + if (!skb) + return ERR_PTR(-ENOMEM); + + skb_put_data(skb, &hdr, sizeof(hdr)); + + return skb; +} + +static +void mt7925_mcu_bss_eht_tlv(struct sk_buff *skb, struct mt76_phy *phy, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_chanctx_conf *ctx) +{ + struct cfg80211_chan_def *chandef = ctx ? &ctx->def : + &link_conf->chanreq.oper; + + struct bss_eht_tlv *req; + struct tlv *tlv; + + tlv = mt76_connac_mcu_add_tlv(skb, UNI_BSS_INFO_EHT, sizeof(*req)); + req = (struct bss_eht_tlv *)tlv; + req->is_eth_dscb_present = chandef->punctured ? 1 : 0; + req->eht_dis_sub_chan_bitmap = cpu_to_le16(chandef->punctured); +} + +int mt7925_mcu_set_eht_pp(struct mt76_phy *phy, struct mt76_vif_link *mvif, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_chanctx_conf *ctx) +{ + struct sk_buff *skb; + + skb = __mt7925_mcu_alloc_bss_req(phy->dev, mvif, + MT7925_BSS_UPDATE_MAX_SIZE); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + mt7925_mcu_bss_eht_tlv(skb, phy, link_conf, ctx); + + return mt76_mcu_skb_send_msg(phy->dev, skb, + MCU_UNI_CMD(BSS_INFO_UPDATE), true); +} + +int mt7925_mcu_set_chctx(struct mt76_phy *phy, struct mt76_vif_link *mvif, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_chanctx_conf *ctx) +{ + struct sk_buff *skb; + + skb = __mt7925_mcu_alloc_bss_req(phy->dev, mvif, + MT7925_BSS_UPDATE_MAX_SIZE); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + mt7925_mcu_bss_rlm_tlv(skb, phy, link_conf, ctx); + + return mt76_mcu_skb_send_msg(phy->dev, skb, + MCU_UNI_CMD(BSS_INFO_UPDATE), true); +} + +static u8 +mt7925_get_phy_mode_ext(struct mt76_phy *phy, struct ieee80211_vif *vif, + enum nl80211_band band, + struct ieee80211_link_sta *link_sta) +{ + struct ieee80211_he_6ghz_capa *he_6ghz_capa = NULL; + const struct ieee80211_sta_eht_cap *eht_cap = NULL; + __le16 capa = 0; + u8 mode = 0; + + if (link_sta) { + he_6ghz_capa = &link_sta->he_6ghz_capa; + eht_cap = &link_sta->eht_cap; + } else { + const struct ieee80211_sta_he_cap *he_cap; + struct ieee80211_supported_band *sband; + + sband = phy->hw->wiphy->bands[band]; + + he_cap = (band == NL80211_BAND_6GHZ) ? + ieee80211_get_he_iftype_cap(sband, vif->type) : NULL; + + if (he_cap) { + capa = ieee80211_get_he_6ghz_capa(sband, vif->type); + he_6ghz_capa = (struct ieee80211_he_6ghz_capa *)&capa; + } + + eht_cap = ieee80211_get_eht_iftype_cap(sband, vif->type); + } + + switch (band) { + case NL80211_BAND_2GHZ: + if (eht_cap && eht_cap->has_eht) + mode |= PHY_MODE_BE_24G; + break; + case NL80211_BAND_5GHZ: + if (eht_cap && eht_cap->has_eht) + mode |= PHY_MODE_BE_5G; + break; + case NL80211_BAND_6GHZ: + if (he_6ghz_capa && he_6ghz_capa->capa) + mode |= PHY_MODE_AX_6G; + + if (eht_cap && eht_cap->has_eht) + mode |= PHY_MODE_BE_6G; + break; + default: + break; + } + + return mode; +} + +static void +mt7925_mcu_bss_basic_tlv_nan(struct mt76_phy *phy, + struct ieee80211_vif *vif, + struct ieee80211_link_sta *link_sta, + struct mt76_connac_bss_basic_tlv *basic_req) +{ + u8 mode_2g, mode_5g; + + mode_2g = mt7925_get_phy_mode_ext(phy, vif, NL80211_BAND_2GHZ, + link_sta); + mode_5g = mt7925_get_phy_mode_ext(phy, vif, NL80211_BAND_5GHZ, + link_sta); + basic_req->phymode_ext = mode_2g | mode_5g; + + basic_req->nonht_basic_phy = cpu_to_le16(PHY_TYPE_ERP_INDEX); + + mode_2g = mt76_connac_get_phy_mode(phy, vif, NL80211_BAND_2GHZ, + link_sta); + mode_5g = mt76_connac_get_phy_mode(phy, vif, NL80211_BAND_5GHZ, + link_sta); + basic_req->phymode = (mode_2g | mode_5g) & ~PHY_MODE_B; +} + +static void +mt7925_mcu_bss_basic_tlv(struct sk_buff *skb, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_link_sta *link_sta, + struct ieee80211_chanctx_conf *ctx, + struct mt76_phy *phy, + u16 bmc_tx_wlan_idx, + u16 sta_wlan_idx, + bool enable) +{ + struct ieee80211_vif *vif = link_conf->vif; + struct mt792x_bss_conf *mconf = mt792x_link_conf_to_mconf(link_conf); + struct cfg80211_chan_def *chandef = ctx ? &ctx->def : + &link_conf->chanreq.oper; + enum nl80211_band band = NL80211_BAND_2GHZ; + struct mt76_connac_bss_basic_tlv *basic_req; + struct tlv *tlv; + int conn_type; + u8 idx; + + tlv = mt76_connac_mcu_add_tlv(skb, UNI_BSS_INFO_BASIC, sizeof(*basic_req)); + basic_req = (struct mt76_connac_bss_basic_tlv *)tlv; + + idx = mconf->mt76.omac_idx > EXT_BSSID_START ? HW_BSSID_0 : + mconf->mt76.omac_idx; + basic_req->hw_bss_idx = idx; + + if (mt7925_vif_is_nan(vif)) { + mt7925_mcu_bss_basic_tlv_nan(phy, vif, link_sta, basic_req); + } else { + band = chandef->chan->band; + basic_req->phymode_ext = mt7925_get_phy_mode_ext(phy, vif, band, + link_sta); + + if (band == NL80211_BAND_2GHZ) + basic_req->nonht_basic_phy = + cpu_to_le16(PHY_TYPE_ERP_INDEX); + else + basic_req->nonht_basic_phy = + cpu_to_le16(PHY_TYPE_OFDM_INDEX); + + memcpy(basic_req->bssid, link_conf->bssid, ETH_ALEN); + basic_req->phymode = mt76_connac_get_phy_mode(phy, vif, band, + link_sta); + } + + basic_req->bcn_interval = cpu_to_le16(link_conf->beacon_int); + basic_req->dtim_period = link_conf->dtim_period; + basic_req->bmc_tx_wlan_idx = cpu_to_le16(bmc_tx_wlan_idx); + basic_req->link_idx = mconf->mt76.idx; + basic_req->sta_idx = cpu_to_le16(sta_wlan_idx); + basic_req->omac_idx = mconf->mt76.omac_idx; + basic_req->band_idx = mconf->mt76.band_idx; + basic_req->wmm_idx = mconf->mt76.wmm_idx; + basic_req->conn_state = !enable; + + switch (vif->type) { + case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_AP: + if (vif->p2p) + conn_type = CONNECTION_P2P_GO; + else + conn_type = CONNECTION_INFRA_AP; + basic_req->conn_type = cpu_to_le32(conn_type); + basic_req->active = enable; + break; + case NL80211_IFTYPE_STATION: + if (vif->p2p) + conn_type = CONNECTION_P2P_GC; + else + conn_type = CONNECTION_INFRA_STA; + basic_req->conn_type = cpu_to_le32(conn_type); + basic_req->active = true; + break; + case NL80211_IFTYPE_ADHOC: + basic_req->conn_type = cpu_to_le32(CONNECTION_IBSS_ADHOC); + basic_req->active = true; + break; + case NL80211_IFTYPE_NAN: + case NL80211_IFTYPE_NAN_DATA: + basic_req->conn_type = cpu_to_le32(CONNECTION_NAN); + basic_req->active = enable; + break; + default: + WARN_ON(1); + break; + } +} + +static void +mt7925_mcu_bss_sec_tlv(struct sk_buff *skb, + struct ieee80211_bss_conf *link_conf) +{ + struct mt792x_bss_conf *mconf = mt792x_link_conf_to_mconf(link_conf); + struct mt76_vif_link *mvif = &mconf->mt76; + struct bss_sec_tlv { + __le16 tag; + __le16 len; + u8 mode; + u8 status; + u8 cipher; + u8 __rsv; + } __packed * sec; + struct tlv *tlv; + + tlv = mt76_connac_mcu_add_tlv(skb, UNI_BSS_INFO_SEC, sizeof(*sec)); + sec = (struct bss_sec_tlv *)tlv; + + switch (mvif->cipher) { + case CONNAC3_CIPHER_GCMP_256: + case CONNAC3_CIPHER_GCMP: + sec->mode = MODE_WPA3_SAE; + sec->status = 8; + break; + case CONNAC3_CIPHER_AES_CCMP: + sec->mode = MODE_WPA2_PSK; + sec->status = 6; + break; + case CONNAC3_CIPHER_TKIP: + sec->mode = MODE_WPA2_PSK; + sec->status = 4; + break; + case CONNAC3_CIPHER_WEP104: + case CONNAC3_CIPHER_WEP40: + sec->mode = MODE_SHARED; + sec->status = 0; + break; + default: + sec->mode = MODE_OPEN; + sec->status = 1; + break; + } + + sec->cipher = mvif->cipher; +} + +static void +mt7925_mcu_bss_bmc_tlv(struct sk_buff *skb, struct mt792x_phy *phy, + struct ieee80211_chanctx_conf *ctx, + struct ieee80211_bss_conf *link_conf) +{ + struct ieee80211_vif *vif = link_conf->vif; + struct cfg80211_chan_def *chandef = ctx ? &ctx->def : + &link_conf->chanreq.oper; + struct mt792x_bss_conf *mconf = mt792x_link_conf_to_mconf(link_conf); + enum nl80211_band band = NL80211_BAND_2GHZ; + struct mt76_vif_link *mvif = &mconf->mt76; + struct bss_rate_tlv *bmc; + struct tlv *tlv; + u8 idx = mvif->mcast_rates_idx ? + mvif->mcast_rates_idx : mvif->basic_rates_idx; + + tlv = mt76_connac_mcu_add_tlv(skb, UNI_BSS_INFO_RATE, sizeof(*bmc)); + + bmc = (struct bss_rate_tlv *)tlv; + + if (mt7925_vif_is_nan(vif)) + band = chandef->chan ? chandef->chan->band : NL80211_BAND_2GHZ; + else + band = chandef->chan->band; + + if (band == NL80211_BAND_2GHZ) + bmc->basic_rate = cpu_to_le16(HR_DSSS_ERP_BASIC_RATE); + else + bmc->basic_rate = cpu_to_le16(OFDM_BASIC_RATE); + + bmc->short_preamble = (band == NL80211_BAND_2GHZ); + bmc->bc_fixed_rate = idx; + bmc->mc_fixed_rate = idx; +} + +static void +mt7925_mcu_bss_mld_tlv(struct sk_buff *skb, + struct ieee80211_bss_conf *link_conf) +{ + struct ieee80211_vif *vif = link_conf->vif; + struct mt792x_bss_conf *mconf = mt792x_link_conf_to_mconf(link_conf); + struct mt792x_vif *mvif = (struct mt792x_vif *)link_conf->vif->drv_priv; + struct mt792x_phy *phy = mvif->phy; + struct bss_mld_tlv *mld; + struct tlv *tlv; + bool is_mld; + + is_mld = ieee80211_vif_is_mld(link_conf->vif) || + (hweight16(mvif->valid_links) > 1); + + tlv = mt76_connac_mcu_add_tlv(skb, UNI_BSS_INFO_MLD, sizeof(*mld)); + mld = (struct bss_mld_tlv *)tlv; + + mld->link_id = is_mld ? link_conf->link_id : 0xff; + /* apply the index of the primary link */ + mld->group_mld_id = is_mld ? mvif->bss_conf.mt76.idx : 0xff; + mld->own_mld_id = mconf->mt76.idx + 32; + mld->remap_idx = 0xff; + + if (phy->chip_cap & MT792x_CHIP_CAP_MLO_EML_EN) { + mld->eml_enable = !!(link_conf->vif->cfg.eml_cap & + IEEE80211_EML_CAP_EMLSR_SUPP); + } else { + mld->eml_enable = 0; + } + + memcpy(mld->mac_addr, vif->addr, ETH_ALEN); +} + +static void +mt7925_mcu_bss_qos_tlv(struct sk_buff *skb, struct ieee80211_bss_conf *link_conf) +{ + struct mt76_connac_bss_qos_tlv *qos; + struct tlv *tlv; + + tlv = mt76_connac_mcu_add_tlv(skb, UNI_BSS_INFO_QBSS, sizeof(*qos)); + qos = (struct mt76_connac_bss_qos_tlv *)tlv; + qos->qos = link_conf->qos; +} + +static void +mt7925_mcu_bss_mbssid_tlv(struct sk_buff *skb, struct ieee80211_bss_conf *link_conf, + bool enable) +{ + struct bss_info_uni_mbssid *mbssid; + struct tlv *tlv; + + if (!enable && !link_conf->bssid_indicator) + return; + + tlv = mt76_connac_mcu_add_tlv(skb, UNI_BSS_INFO_11V_MBSSID, + sizeof(*mbssid)); + + mbssid = (struct bss_info_uni_mbssid *)tlv; + mbssid->max_indicator = link_conf->bssid_indicator; + mbssid->mbss_idx = link_conf->bssid_index; + mbssid->tx_bss_omac_idx = 0; +} + +static void +mt7925_mcu_bss_he_tlv(struct sk_buff *skb, struct ieee80211_bss_conf *link_conf, + struct mt792x_phy *phy) +{ +#define DEFAULT_HE_PE_DURATION 4 +#define DEFAULT_HE_DURATION_RTS_THRES 1023 + const struct ieee80211_sta_he_cap *cap; + struct bss_info_uni_he *he; + struct tlv *tlv; + + cap = mt76_connac_get_he_phy_cap(phy->mt76, link_conf->vif); + if (!cap) + return; + + tlv = mt76_connac_mcu_add_tlv(skb, UNI_BSS_INFO_HE_BASIC, sizeof(*he)); + + he = (struct bss_info_uni_he *)tlv; + he->he_pe_duration = link_conf->htc_trig_based_pkt_ext; + if (!he->he_pe_duration) + he->he_pe_duration = DEFAULT_HE_PE_DURATION; + + he->he_rts_thres = cpu_to_le16(link_conf->frame_time_rts_th); + if (!he->he_rts_thres) + he->he_rts_thres = cpu_to_le16(DEFAULT_HE_DURATION_RTS_THRES); + + he->max_nss_mcs[CMD_HE_MCS_BW80] = cap->he_mcs_nss_supp.tx_mcs_80; + he->max_nss_mcs[CMD_HE_MCS_BW160] = cap->he_mcs_nss_supp.tx_mcs_160; + he->max_nss_mcs[CMD_HE_MCS_BW8080] = cap->he_mcs_nss_supp.tx_mcs_80p80; +} + +static void +mt7925_mcu_bss_color_tlv(struct sk_buff *skb, struct ieee80211_bss_conf *link_conf, + bool enable) +{ + struct bss_info_uni_bss_color *color; + struct tlv *tlv; + + tlv = mt76_connac_mcu_add_tlv(skb, UNI_BSS_INFO_BSS_COLOR, sizeof(*color)); + color = (struct bss_info_uni_bss_color *)tlv; + + color->enable = enable ? + link_conf->he_bss_color.enabled : 0; + color->bss_color = enable ? + link_conf->he_bss_color.color : 0; +} + +static void +mt7925_mcu_bss_ifs_tlv(struct sk_buff *skb, + struct ieee80211_bss_conf *link_conf) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)link_conf->vif->drv_priv; + struct mt792x_phy *phy = mvif->phy; + struct bss_ifs_time_tlv *ifs_time; + struct tlv *tlv; + + tlv = mt76_connac_mcu_add_tlv(skb, UNI_BSS_INFO_IFS_TIME, sizeof(*ifs_time)); + ifs_time = (struct bss_ifs_time_tlv *)tlv; + ifs_time->slot_valid = true; + ifs_time->slot_time = cpu_to_le16(phy->slottime); +} + +int mt7925_mcu_set_timing(struct mt792x_phy *phy, + struct ieee80211_bss_conf *link_conf) +{ + struct mt792x_bss_conf *mconf = mt792x_link_conf_to_mconf(link_conf); + struct mt792x_dev *dev = phy->dev; + struct sk_buff *skb; + + skb = __mt7925_mcu_alloc_bss_req(&dev->mt76, &mconf->mt76, + MT7925_BSS_UPDATE_MAX_SIZE); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + mt7925_mcu_bss_ifs_tlv(skb, link_conf); + + return mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_UNI_CMD(BSS_INFO_UPDATE), true); +} + +void mt7925_mcu_del_dev(struct mt76_dev *mdev, + struct ieee80211_vif *vif) +{ + struct mt76_vif_link *mvif = (struct mt76_vif_link *)vif->drv_priv; + struct { + struct { + u8 omac_idx; + u8 band_idx; + __le16 pad; + } __packed hdr; + struct req_tlv { + __le16 tag; + __le16 len; + u8 active; + u8 link_idx; /* hw link idx */ + u8 omac_addr[ETH_ALEN]; + } __packed tlv; + } dev_req = { + .tlv = { + .tag = cpu_to_le16(DEV_INFO_ACTIVE), + .len = cpu_to_le16(sizeof(struct req_tlv)), + .active = true, + }, + }; + struct { + struct { + u8 bss_idx; + u8 pad[3]; + } __packed hdr; + struct mt76_connac_bss_basic_tlv basic; + } basic_req = { + .basic = { + .tag = cpu_to_le16(UNI_BSS_INFO_BASIC), + .len = cpu_to_le16(sizeof(struct mt76_connac_bss_basic_tlv)), + .active = true, + .conn_state = 1, + }, + }; + + dev_req.hdr.omac_idx = mvif->omac_idx; + dev_req.hdr.band_idx = mvif->band_idx; + + basic_req.hdr.bss_idx = mvif->idx; + basic_req.basic.omac_idx = mvif->omac_idx; + basic_req.basic.band_idx = mvif->band_idx; + basic_req.basic.link_idx = mvif->link_idx; + + mt76_mcu_send_msg(mdev, MCU_UNI_CMD(BSS_INFO_UPDATE), + &basic_req, sizeof(basic_req), true); + + /* recovery omac address for the legacy interface */ + memcpy(dev_req.tlv.omac_addr, vif->addr, ETH_ALEN); + mt76_mcu_send_msg(mdev, MCU_UNI_CMD(DEV_INFO_UPDATE), + &dev_req, sizeof(dev_req), true); +} + +int mt7925_mcu_add_bss_info_sta(struct mt792x_phy *phy, + struct ieee80211_chanctx_conf *ctx, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_link_sta *link_sta, + u16 bmc_tx_wlan_idx, + u16 sta_wlan_idx, + int enable) +{ + struct mt792x_bss_conf *mconf = mt792x_link_conf_to_mconf(link_conf); + struct mt792x_dev *dev = phy->dev; + struct sk_buff *skb; + int err; + + skb = __mt7925_mcu_alloc_bss_req(&dev->mt76, &mconf->mt76, + MT7925_BSS_UPDATE_MAX_SIZE); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + /* bss_basic must be first */ + mt7925_mcu_bss_basic_tlv(skb, link_conf, link_sta, ctx, phy->mt76, + bmc_tx_wlan_idx, sta_wlan_idx, enable); + mt7925_mcu_bss_sec_tlv(skb, link_conf); + mt7925_mcu_bss_bmc_tlv(skb, phy, ctx, link_conf); + mt7925_mcu_bss_qos_tlv(skb, link_conf); + mt7925_mcu_bss_mld_tlv(skb, link_conf); + mt7925_mcu_bss_ifs_tlv(skb, link_conf); + + if (link_conf->he_support) { + mt7925_mcu_bss_he_tlv(skb, link_conf, phy); + mt7925_mcu_bss_color_tlv(skb, link_conf, enable); + } + + if (enable) { + mt7925_mcu_bss_rlm_tlv(skb, phy->mt76, link_conf, ctx); + mt7925_mcu_bss_mbssid_tlv(skb, link_conf, enable); + } + + err = mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_UNI_CMD(BSS_INFO_UPDATE), true); + return mt76_connac_mcu_bss_deact_err(&dev->mt76, err, enable); +} + +int mt7925_mcu_add_bss_info(struct mt792x_phy *phy, + struct ieee80211_chanctx_conf *ctx, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_link_sta *link_sta, + int enable) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)link_conf->vif->drv_priv; + struct mt792x_bss_conf *mconf = mt792x_link_conf_to_mconf(link_conf); + struct mt792x_link_sta *mlink_bc; + struct mt792x_link_sta *mlink; + + mlink_bc = mt792x_sta_to_link(&mvif->sta, mconf->link_id); + + if (link_sta) { + struct mt792x_sta *msta = (void *)link_sta->sta->drv_priv; + + mlink = mt792x_sta_to_link(msta, link_sta->link_id); + if (WARN_ON(!mlink)) + return -EINVAL; + } else { + mlink = &mconf->vif->sta.deflink; + } + + return mt7925_mcu_add_bss_info_sta(phy, ctx, link_conf, link_sta, + mlink_bc->wcid.idx, mlink->wcid.idx, enable); +} + +int mt7925_mcu_set_dbdc(struct mt76_phy *phy, bool enable) +{ + struct mt76_dev *mdev = phy->dev; + + struct mbmc_conf_tlv *conf; + struct mbmc_set_req *hdr; + struct sk_buff *skb; + struct tlv *tlv; + int max_len, err; + + max_len = sizeof(*hdr) + sizeof(*conf); + skb = mt76_mcu_msg_alloc(mdev, NULL, max_len); + if (!skb) + return -ENOMEM; + + hdr = (struct mbmc_set_req *)skb_put(skb, sizeof(*hdr)); + + tlv = mt76_connac_mcu_add_tlv(skb, UNI_MBMC_SETTING, sizeof(*conf)); + conf = (struct mbmc_conf_tlv *)tlv; + + conf->mbmc_en = enable; + conf->band = 0; /* unused */ + + err = mt76_mcu_skb_send_msg(mdev, skb, MCU_UNI_CMD(SET_DBDC_PARMS), + true); + + return err; +} +EXPORT_SYMBOL_GPL(mt7925_mcu_set_dbdc); + +static void +mt7925_mcu_build_scan_ie_tlv(struct mt76_dev *mdev, + struct sk_buff *skb, + struct ieee80211_scan_ies *scan_ies) +{ + u32 max_len = sizeof(struct scan_ie_tlv) + MT76_CONNAC_SCAN_IE_LEN; + u32 ies_len, alloc_len; + struct scan_ie_tlv *ie; + enum nl80211_band i; + struct tlv *tlv; + const u8 *ies; + + for (i = 0; i <= NL80211_BAND_6GHZ; i++) { + if (i == NL80211_BAND_60GHZ) + continue; + + ies = scan_ies->ies[i]; + ies_len = scan_ies->len[i]; + + if (!ies || !ies_len) + continue; + + if (is_mt7928(mdev)) + alloc_len = ALIGN(sizeof(*ie) + ies_len, 4); + else + alloc_len = sizeof(*ie) + ies_len; + + if (alloc_len > max_len) + return; + + tlv = mt76_connac_mcu_add_tlv(skb, UNI_SCAN_IE, + alloc_len); + ie = (struct scan_ie_tlv *)tlv; + + memcpy(ie->ies, ies, ies_len); + ie->ies_len = cpu_to_le16(ies_len); + + switch (i) { + case NL80211_BAND_2GHZ: + ie->band = 1; + break; + case NL80211_BAND_6GHZ: + ie->band = 3; + break; + default: + ie->band = 2; + break; + } + + max_len -= alloc_len; + } +} + +int mt7925_mcu_hw_scan(struct mt76_phy *phy, struct ieee80211_vif *vif, + struct ieee80211_scan_request *scan_req) +{ + struct mt76_vif_link *mvif = (struct mt76_vif_link *)vif->drv_priv; + struct cfg80211_scan_request *sreq = &scan_req->req; + int n_ssids = 0, err, i; + struct ieee80211_channel **scan_list = sreq->channels; + struct mt76_dev *mdev = phy->dev; + struct mt76_connac_mcu_scan_channel *chan; + struct sk_buff *skb; + struct scan_hdr_tlv *hdr; + struct scan_req_tlv *req; + struct scan_ssid_tlv *ssid; + struct scan_bssid_tlv *bssid; + struct scan_chan_info_tlv *chan_info; + struct scan_ie_tlv *ie; + struct scan_misc_tlv *misc; + struct tlv *tlv; + int max_len; + + if (test_bit(MT76_HW_SCANNING, &phy->state)) + return -EBUSY; + + max_len = sizeof(*hdr) + sizeof(*req) + sizeof(*ssid) + + sizeof(*bssid) * MT7925_RNR_SCAN_MAX_BSSIDS + + sizeof(*chan_info) + sizeof(*misc) + sizeof(*ie) + + MT76_CONNAC_SCAN_IE_LEN; + + skb = mt76_mcu_msg_alloc(mdev, NULL, max_len); + if (!skb) + return -ENOMEM; + + set_bit(MT76_HW_SCANNING, &phy->state); + mvif->scan_seq_num = (mvif->scan_seq_num + 1) & 0x7f; + + hdr = (struct scan_hdr_tlv *)skb_put(skb, sizeof(*hdr)); + hdr->seq_num = mvif->scan_seq_num | mvif->band_idx << 7; + hdr->bss_idx = mvif->idx; + + tlv = mt76_connac_mcu_add_tlv(skb, UNI_SCAN_REQ, sizeof(*req)); + req = (struct scan_req_tlv *)tlv; + req->scan_type = sreq->n_ssids ? 1 : 0; + req->probe_req_num = sreq->n_ssids ? 2 : 0; + + tlv = mt76_connac_mcu_add_tlv(skb, UNI_SCAN_SSID, sizeof(*ssid)); + ssid = (struct scan_ssid_tlv *)tlv; + for (i = 0; i < sreq->n_ssids; i++) { + if (!sreq->ssids[i].ssid_len) + continue; + if (i >= MT7925_RNR_SCAN_MAX_BSSIDS) + break; + + ssid->ssids[n_ssids].ssid_len = cpu_to_le32(sreq->ssids[i].ssid_len); + memcpy(ssid->ssids[n_ssids].ssid, sreq->ssids[i].ssid, + sreq->ssids[i].ssid_len); + n_ssids++; + } + ssid->ssid_type = n_ssids ? BIT(2) : BIT(0); + ssid->ssids_num = n_ssids; + + if (sreq->n_6ghz_params) { + u8 j; + + mt76_connac_mcu_build_rnr_scan_param(mdev, sreq); + + for (j = 0; j < mdev->rnr.bssid_num; j++) { + if (j >= MT7925_RNR_SCAN_MAX_BSSIDS) + break; + + tlv = mt76_connac_mcu_add_tlv(skb, UNI_SCAN_BSSID, + sizeof(*bssid)); + bssid = (struct scan_bssid_tlv *)tlv; + + ether_addr_copy(bssid->bssid, mdev->rnr.bssid[j]); + bssid->match_ch = mdev->rnr.channel[j]; + bssid->match_ssid_ind = MT7925_RNR_SCAN_MAX_BSSIDS; + bssid->match_short_ssid_ind = MT7925_RNR_SCAN_MAX_BSSIDS; + } + req->scan_func |= SCAN_FUNC_RNR_SCAN; + } else { + tlv = mt76_connac_mcu_add_tlv(skb, UNI_SCAN_BSSID, sizeof(*bssid)); + bssid = (struct scan_bssid_tlv *)tlv; + + ether_addr_copy(bssid->bssid, sreq->bssid); + } + + tlv = mt76_connac_mcu_add_tlv(skb, UNI_SCAN_CHANNEL, sizeof(*chan_info)); + chan_info = (struct scan_chan_info_tlv *)tlv; + chan_info->channels_num = min_t(u8, sreq->n_channels, + ARRAY_SIZE(chan_info->channels)); + for (i = 0; i < chan_info->channels_num; i++) { + chan = &chan_info->channels[i]; + + switch (scan_list[i]->band) { + case NL80211_BAND_2GHZ: + chan->band = 1; + break; + case NL80211_BAND_6GHZ: + chan->band = 3; + break; + default: + chan->band = 2; + break; + } + chan->channel_num = scan_list[i]->hw_value; + } + chan_info->channel_type = sreq->n_channels ? 4 : 0; + + req->scan_func |= SCAN_FUNC_SPLIT_SCAN; + + tlv = mt76_connac_mcu_add_tlv(skb, UNI_SCAN_MISC, sizeof(*misc)); + misc = (struct scan_misc_tlv *)tlv; + if (sreq->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) { + get_random_mask_addr(misc->random_mac, sreq->mac_addr, + sreq->mac_addr_mask); + req->scan_func |= SCAN_FUNC_RANDOM_MAC; + } + + /* Append scan probe IEs as the last tlv */ + mt7925_mcu_build_scan_ie_tlv(mdev, skb, &scan_req->ies); + + err = mt76_mcu_skb_send_msg(mdev, skb, MCU_UNI_CMD(SCAN_REQ), + true); + if (err < 0) + clear_bit(MT76_HW_SCANNING, &phy->state); + + return err; +} +EXPORT_SYMBOL_GPL(mt7925_mcu_hw_scan); + +int mt7925_mcu_sched_scan_req(struct mt76_phy *phy, + struct ieee80211_vif *vif, + struct cfg80211_sched_scan_request *sreq, + struct ieee80211_scan_ies *ies) +{ + struct mt76_vif_link *mvif = (struct mt76_vif_link *)vif->drv_priv; + struct ieee80211_channel **scan_list = sreq->channels; + struct mt76_connac_mcu_scan_channel *chan; + struct mt76_dev *mdev = phy->dev; + struct cfg80211_match_set *cfg_match; + struct cfg80211_ssid *cfg_ssid; + + struct scan_hdr_tlv *hdr; + struct scan_sched_req *req; + struct scan_ssid_tlv *ssid; + struct scan_chan_info_tlv *chan_info; + struct scan_ie_tlv *ie; + struct scan_sched_ssid_match_sets *match; + struct sk_buff *skb; + struct tlv *tlv; + int i, max_len; + + max_len = sizeof(*hdr) + sizeof(*req) + sizeof(*ssid) + + sizeof(*chan_info) + sizeof(*ie) + + sizeof(*match); + + skb = mt76_mcu_msg_alloc(mdev, NULL, max_len); + if (!skb) + return -ENOMEM; + + mvif->scan_seq_num = (mvif->scan_seq_num + 1) & 0x7f; + + hdr = (struct scan_hdr_tlv *)skb_put(skb, sizeof(*hdr)); + hdr->seq_num = mvif->scan_seq_num | mvif->band_idx << 7; + hdr->bss_idx = mvif->idx; + + tlv = mt76_connac_mcu_add_tlv(skb, UNI_SCAN_SCHED_REQ, sizeof(*req)); + req = (struct scan_sched_req *)tlv; + req->version = 1; + + if (sreq->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) + req->scan_func |= SCAN_FUNC_RANDOM_MAC; + + req->intervals_num = sreq->n_scan_plans; + for (i = 0; i < req->intervals_num; i++) + req->intervals[i] = cpu_to_le16(sreq->scan_plans[i].interval); + + tlv = mt76_connac_mcu_add_tlv(skb, UNI_SCAN_SSID, sizeof(*ssid)); + ssid = (struct scan_ssid_tlv *)tlv; + + ssid->ssids_num = sreq->n_ssids; + ssid->ssid_type = BIT(2); + for (i = 0; i < ssid->ssids_num; i++) { + cfg_ssid = &sreq->ssids[i]; + memcpy(ssid->ssids[i].ssid, cfg_ssid->ssid, cfg_ssid->ssid_len); + ssid->ssids[i].ssid_len = cpu_to_le32(cfg_ssid->ssid_len); + } + + tlv = mt76_connac_mcu_add_tlv(skb, UNI_SCAN_SSID_MATCH_SETS, sizeof(*match)); + match = (struct scan_sched_ssid_match_sets *)tlv; + match->match_num = sreq->n_match_sets; + for (i = 0; i < match->match_num; i++) { + cfg_match = &sreq->match_sets[i]; + memcpy(match->match[i].ssid, cfg_match->ssid.ssid, + cfg_match->ssid.ssid_len); + match->match[i].rssi_th = cpu_to_le32(cfg_match->rssi_thold); + match->match[i].ssid_len = cfg_match->ssid.ssid_len; + } + + tlv = mt76_connac_mcu_add_tlv(skb, UNI_SCAN_CHANNEL, sizeof(*chan_info)); + chan_info = (struct scan_chan_info_tlv *)tlv; + chan_info->channels_num = min_t(u8, sreq->n_channels, + ARRAY_SIZE(chan_info->channels)); + for (i = 0; i < chan_info->channels_num; i++) { + chan = &chan_info->channels[i]; + + switch (scan_list[i]->band) { + case NL80211_BAND_2GHZ: + chan->band = 1; + break; + case NL80211_BAND_6GHZ: + chan->band = 3; + break; + default: + chan->band = 2; + break; + } + chan->channel_num = scan_list[i]->hw_value; + } + chan_info->channel_type = sreq->n_channels ? 4 : 0; + + /* Append scan probe IEs as the last tlv */ + mt7925_mcu_build_scan_ie_tlv(mdev, skb, ies); + + return mt76_mcu_skb_send_msg(mdev, skb, MCU_UNI_CMD(SCAN_REQ), + true); +} +EXPORT_SYMBOL_GPL(mt7925_mcu_sched_scan_req); + +int +mt7925_mcu_sched_scan_enable(struct mt76_phy *phy, + struct ieee80211_vif *vif, + bool enable) +{ + struct mt76_dev *mdev = phy->dev; + struct scan_sched_enable *req; + struct scan_hdr_tlv *hdr; + struct sk_buff *skb; + struct tlv *tlv; + int max_len; + + max_len = sizeof(*hdr) + sizeof(*req); + + skb = mt76_mcu_msg_alloc(mdev, NULL, max_len); + if (!skb) + return -ENOMEM; + + hdr = (struct scan_hdr_tlv *)skb_put(skb, sizeof(*hdr)); + hdr->seq_num = 0; + hdr->bss_idx = 0; + + tlv = mt76_connac_mcu_add_tlv(skb, UNI_SCAN_SCHED_ENABLE, sizeof(*req)); + req = (struct scan_sched_enable *)tlv; + req->active = !enable; + + if (enable) + set_bit(MT76_HW_SCHED_SCANNING, &phy->state); + else + clear_bit(MT76_HW_SCHED_SCANNING, &phy->state); + + return mt76_mcu_skb_send_msg(mdev, skb, MCU_UNI_CMD(SCAN_REQ), + true); +} + +int mt7925_mcu_cancel_hw_scan(struct mt76_phy *phy, + struct ieee80211_vif *vif) +{ + struct mt76_vif_link *mvif = (struct mt76_vif_link *)vif->drv_priv; + struct { + struct scan_hdr { + u8 seq_num; + u8 bss_idx; + u8 pad[2]; + } __packed hdr; + struct scan_cancel_tlv { + __le16 tag; + __le16 len; + u8 is_ext_channel; + u8 rsv[3]; + } __packed cancel; + } req = { + .hdr = { + .seq_num = mvif->scan_seq_num, + .bss_idx = mvif->idx, + }, + .cancel = { + .tag = cpu_to_le16(UNI_SCAN_CANCEL), + .len = cpu_to_le16(sizeof(struct scan_cancel_tlv)), + }, + }; + + if (test_and_clear_bit(MT76_HW_SCANNING, &phy->state)) { + struct cfg80211_scan_info info = { + .aborted = true, + }; + + ieee80211_scan_completed(phy->hw, &info); + } + + return mt76_mcu_send_msg(phy->dev, MCU_UNI_CMD(SCAN_REQ), + &req, sizeof(req), true); +} +EXPORT_SYMBOL_GPL(mt7925_mcu_cancel_hw_scan); + +int mt7925_mcu_set_channel_domain(struct mt76_phy *phy) +{ + int len, i, n_max_channels, n_2ch = 0, n_5ch = 0, n_6ch = 0; + struct { + struct { + u8 alpha2[4]; /* regulatory_request.alpha2 */ + u8 bw_2g; /* BW_20_40M 0 + * BW_20M 1 + * BW_20_40_80M 2 + * BW_20_40_80_160M 3 + * BW_20_40_80_8080M 4 + */ + u8 bw_5g; + u8 bw_6g; + u8 pad; + } __packed hdr; + struct n_chan { + __le16 tag; + __le16 len; + u8 n_2ch; + u8 n_5ch; + u8 n_6ch; + u8 pad; + } __packed n_ch; + } req = { + .hdr = { + .bw_2g = 0, + .bw_5g = 3, /* BW_20_40_80_160M */ + .bw_6g = 3, + }, + .n_ch = { + .tag = cpu_to_le16(2), + }, + }; + struct mt76_connac_mcu_chan { + __le16 hw_value; + __le16 pad; + __le32 flags; + } __packed channel; + struct mt76_dev *dev = phy->dev; + struct ieee80211_channel *chan; + struct sk_buff *skb; + + n_max_channels = phy->sband_2g.sband.n_channels + + phy->sband_5g.sband.n_channels + + phy->sband_6g.sband.n_channels; + len = sizeof(req) + n_max_channels * sizeof(channel); + + skb = mt76_mcu_msg_alloc(dev, NULL, len); + if (!skb) + return -ENOMEM; + + skb_reserve(skb, sizeof(req)); + + for (i = 0; i < phy->sband_2g.sband.n_channels; i++) { + chan = &phy->sband_2g.sband.channels[i]; + if (chan->flags & IEEE80211_CHAN_DISABLED) + continue; + + channel.hw_value = cpu_to_le16(chan->hw_value); + channel.flags = cpu_to_le32(chan->flags); + channel.pad = 0; + + skb_put_data(skb, &channel, sizeof(channel)); + n_2ch++; + } + for (i = 0; i < phy->sband_5g.sband.n_channels; i++) { + chan = &phy->sband_5g.sband.channels[i]; + if (chan->flags & IEEE80211_CHAN_DISABLED) + continue; + + channel.hw_value = cpu_to_le16(chan->hw_value); + channel.flags = cpu_to_le32(chan->flags); + channel.pad = 0; + + skb_put_data(skb, &channel, sizeof(channel)); + n_5ch++; + } + for (i = 0; i < phy->sband_6g.sband.n_channels; i++) { + chan = &phy->sband_6g.sband.channels[i]; + if (chan->flags & IEEE80211_CHAN_DISABLED) + continue; + + channel.hw_value = cpu_to_le16(chan->hw_value); + channel.flags = cpu_to_le32(chan->flags); + channel.pad = 0; + + skb_put_data(skb, &channel, sizeof(channel)); + n_6ch++; + } + + BUILD_BUG_ON(sizeof(dev->alpha2) > sizeof(req.hdr.alpha2)); + memcpy(req.hdr.alpha2, dev->alpha2, sizeof(dev->alpha2)); + req.n_ch.n_2ch = n_2ch; + req.n_ch.n_5ch = n_5ch; + req.n_ch.n_6ch = n_6ch; + len = sizeof(struct n_chan) + (n_2ch + n_5ch + n_6ch) * sizeof(channel); + req.n_ch.len = cpu_to_le16(len); + memcpy(__skb_push(skb, sizeof(req)), &req, sizeof(req)); + + return mt76_mcu_skb_send_msg(dev, skb, MCU_UNI_CMD(SET_DOMAIN_INFO), + true); +} +EXPORT_SYMBOL_GPL(mt7925_mcu_set_channel_domain); + +static int +__mt7925_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2, + enum environment_cap env_cap, + struct mt7925_clc *clc, u8 idx) +{ + struct mt7925_clc_segment *seg; + struct sk_buff *skb; + struct { + u8 rsv[4]; + __le16 tag; + __le16 len; + + u8 ver; + u8 pad0; + __le16 size; + u8 idx; + u8 env; + u8 acpi_conf; + u8 pad1; + u8 alpha2[2]; + u8 type[2]; + u8 rsvd[64]; + } __packed req = { + .tag = cpu_to_le16(0x3), + + .idx = idx, + .env = env_cap, + }; + int ret, valid_cnt = 0; + u8 *pos, *last_pos; + + if (!clc) + return 0; + + req.ver = clc->ver; + pos = clc->data + sizeof(*seg) * clc->t0.nr_seg; + last_pos = clc->data + le32_to_cpu(*(__le32 *)(clc->data + 4)); + while (pos < last_pos) { + struct mt7925_clc_rule *rule = (struct mt7925_clc_rule *)pos; + + pos += sizeof(*rule); + if (rule->alpha2[0] != alpha2[0] || + rule->alpha2[1] != alpha2[1]) + continue; + + seg = (struct mt7925_clc_segment *)clc->data + + rule->seg_idx - 1; + + memcpy(req.alpha2, rule->alpha2, 2); + memcpy(req.type, rule->type, 2); + + req.size = cpu_to_le16(seg->len); + req.len = cpu_to_le16(sizeof(req) + seg->len - 4); + dev->phy.clc_chan_conf = clc->ver == 1 ? 0xff : rule->flag; + skb = __mt76_mcu_msg_alloc(&dev->mt76, &req, + le16_to_cpu(req.size) + sizeof(req), + sizeof(req), GFP_KERNEL); + if (!skb) + return -ENOMEM; + skb_put_data(skb, clc->data + seg->offset, seg->len); + + ret = mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_UNI_CMD(SET_POWER_LIMIT), + true); + if (ret < 0) + return ret; + valid_cnt++; + } + + if (!valid_cnt) { + dev->phy.clc_chan_conf = 0xff; + return -ENOENT; + } + + return 0; +} + +int mt7925_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2, + enum environment_cap env_cap) +{ + struct mt792x_phy *phy = (struct mt792x_phy *)&dev->phy; + int i, ret; + + if (!ARRAY_SIZE(phy->clc)) + return -ESRCH; + + /* submit all clc config */ + for (i = 0; i < ARRAY_SIZE(phy->clc); i++) { + if (i == MT792x_CLC_BE_CTRL || + i == MT792x_CLC_REGD) + continue; + + ret = __mt7925_mcu_set_clc(dev, alpha2, env_cap, + phy->clc[i], i); + + /* If no country found, set "00" as default */ + if (ret == -ENOENT) + ret = __mt7925_mcu_set_clc(dev, "00", + ENVIRON_INDOOR, + phy->clc[i], i); + if (ret < 0) + return ret; + } + return 0; +} + +int mt7925_mcu_fill_message(struct mt76_dev *mdev, struct sk_buff *skb, + int cmd, int *wait_seq) +{ + int txd_len, mcu_cmd = FIELD_GET(__MCU_CMD_FIELD_ID, cmd); + struct mt76_connac2_mcu_uni_txd *uni_txd; + struct mt76_connac2_mcu_txd *mcu_txd; + __le32 *txd; + u32 val; + u8 seq; + + /* TODO: make dynamic based on msg type */ + mdev->mcu.timeout = 20 * HZ; + + seq = ++mdev->mcu.msg_seq & 0xf; + if (!seq) + seq = ++mdev->mcu.msg_seq & 0xf; + + if (cmd == MCU_CMD(FW_SCATTER)) + goto exit; + + txd_len = cmd & __MCU_CMD_FIELD_UNI ? sizeof(*uni_txd) : sizeof(*mcu_txd); + txd = (__le32 *)skb_push(skb, txd_len); + + val = FIELD_PREP(MT_TXD0_TX_BYTES, skb->len) | + FIELD_PREP(MT_TXD0_PKT_FMT, MT_TX_TYPE_CMD) | + FIELD_PREP(MT_TXD0_Q_IDX, MT_TX_MCU_PORT_RX_Q0); + txd[0] = cpu_to_le32(val); + + val = FIELD_PREP(MT_TXD1_HDR_FORMAT, MT_HDR_FORMAT_CMD); + txd[1] = cpu_to_le32(val); + + if (cmd & __MCU_CMD_FIELD_UNI) { + uni_txd = (struct mt76_connac2_mcu_uni_txd *)txd; + uni_txd->len = cpu_to_le16(skb->len - sizeof(uni_txd->txd)); + uni_txd->cid = cpu_to_le16(mcu_cmd); + uni_txd->s2d_index = MCU_S2D_H2N; + uni_txd->pkt_type = MCU_PKT_ID; + uni_txd->seq = seq; + + if (cmd & __MCU_CMD_FIELD_QUERY) + uni_txd->option = MCU_CMD_UNI_QUERY_ACK; + else + uni_txd->option = MCU_CMD_UNI_EXT_ACK; + + if (cmd == MCU_UNI_CMD(HIF_CTRL) || + cmd == MCU_UNI_CMD(CHIP_CONFIG)) + uni_txd->option &= ~MCU_CMD_ACK; + + if (mcu_cmd == MCU_UNI_CMD_TESTMODE_CTRL || + mcu_cmd == MCU_UNI_CMD_TESTMODE_RX_STAT) { + if (cmd & __MCU_CMD_FIELD_QUERY) + uni_txd->option = 0x2; + else + uni_txd->option = 0x6; + } + + goto exit; + } + + mcu_txd = (struct mt76_connac2_mcu_txd *)txd; + mcu_txd->len = cpu_to_le16(skb->len - sizeof(mcu_txd->txd)); + mcu_txd->pq_id = cpu_to_le16(MCU_PQ_ID(MT_TX_PORT_IDX_MCU, + MT_TX_MCU_PORT_RX_Q0)); + mcu_txd->pkt_type = MCU_PKT_ID; + mcu_txd->seq = seq; + mcu_txd->cid = mcu_cmd; + mcu_txd->ext_cid = FIELD_GET(__MCU_CMD_FIELD_EXT_ID, cmd); + + if (mcu_txd->ext_cid || (cmd & __MCU_CMD_FIELD_CE)) { + if (cmd & __MCU_CMD_FIELD_QUERY) + mcu_txd->set_query = MCU_Q_QUERY; + else + mcu_txd->set_query = MCU_Q_SET; + mcu_txd->ext_cid_ack = !!mcu_txd->ext_cid; + } else { + mcu_txd->set_query = MCU_Q_NA; + } + + if (cmd & __MCU_CMD_FIELD_WA) + mcu_txd->s2d_index = MCU_S2D_H2C; + else + mcu_txd->s2d_index = MCU_S2D_H2N; + +exit: + if (wait_seq) + *wait_seq = seq; + + return 0; +} +EXPORT_SYMBOL_GPL(mt7925_mcu_fill_message); + +int mt7925_mcu_set_rts_thresh(struct mt792x_phy *phy, u32 val) +{ + struct { + u8 band_idx; + u8 _rsv[3]; + + __le16 tag; + __le16 len; + __le32 len_thresh; + __le32 pkt_thresh; + } __packed req = { + .band_idx = phy->mt76->band_idx, + .tag = cpu_to_le16(UNI_BAND_CONFIG_RTS_THRESHOLD), + .len = cpu_to_le16(sizeof(req) - 4), + .len_thresh = cpu_to_le32(val), + .pkt_thresh = cpu_to_le32(0x2), + }; + + return mt76_mcu_send_msg(&phy->dev->mt76, MCU_UNI_CMD(BAND_CONFIG), + &req, sizeof(req), true); +} + +int mt7925_mcu_set_radio_en(struct mt792x_phy *phy, bool enable) +{ + struct { + u8 band_idx; + u8 _rsv[3]; + + __le16 tag; + __le16 len; + u8 enable; + u8 _rsv2[3]; + } __packed req = { + .band_idx = phy->mt76->band_idx, + .tag = cpu_to_le16(UNI_BAND_CONFIG_RADIO_ENABLE), + .len = cpu_to_le16(sizeof(req) - 4), + .enable = enable, + }; + + return mt76_mcu_send_msg(&phy->dev->mt76, MCU_UNI_CMD(BAND_CONFIG), + &req, sizeof(req), true); +} + +static void +mt7925_mcu_build_sku(struct mt76_dev *dev, s8 *sku, + struct mt76_power_limits *limits, + enum nl80211_band band) +{ + int i, offset = sizeof(limits->cck); + + memset(sku, 127, MT_CONNAC3_SKU_POWER_LIMIT); + + if (band == NL80211_BAND_2GHZ) { + /* cck */ + memcpy(sku, limits->cck, sizeof(limits->cck)); + } + + /* ofdm */ + memcpy(&sku[offset], limits->ofdm, sizeof(limits->ofdm)); + offset += (sizeof(limits->ofdm) * 5); + + /* ht */ + for (i = 0; i < 2; i++) { + memcpy(&sku[offset], limits->mcs[i], 8); + offset += 8; + } + sku[offset++] = limits->mcs[0][0]; + + /* vht */ + for (i = 0; i < ARRAY_SIZE(limits->mcs); i++) { + memcpy(&sku[offset], limits->mcs[i], + ARRAY_SIZE(limits->mcs[i])); + offset += 12; + } + + /* he */ + for (i = 0; i < ARRAY_SIZE(limits->ru); i++) { + memcpy(&sku[offset], limits->ru[i], ARRAY_SIZE(limits->ru[i])); + offset += ARRAY_SIZE(limits->ru[i]); + } + + /* eht */ + for (i = 0; i < ARRAY_SIZE(limits->eht); i++) { + memcpy(&sku[offset], limits->eht[i], ARRAY_SIZE(limits->eht[i])); + offset += ARRAY_SIZE(limits->eht[i]); + } +} + +static int +mt7925_mcu_rate_txpower_band(struct mt76_phy *phy, + enum nl80211_band band) +{ + int tx_power, n_chan, last_ch, err = 0, idx = 0; + int i, sku_len, batch_size, batch_len = 3; + struct mt76_dev *dev = phy->dev; + static const u8 chan_list_2ghz[] = { + 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14 + }; + static const u8 chan_list_5ghz[] = { + 36, 38, 40, 42, 44, 46, 48, + 50, 52, 54, 56, 58, 60, 62, + 64, 100, 102, 104, 106, 108, 110, + 112, 114, 116, 118, 120, 122, 124, + 126, 128, 132, 134, 136, 138, 140, + 142, 144, 149, 151, 153, 155, 157, + 159, 161, 165, 167 + }; + static const u8 chan_list_6ghz[] = { + 1, 3, 5, 7, 9, 11, 13, + 15, 17, 19, 21, 23, 25, 27, + 29, 33, 35, 37, 39, 41, 43, + 45, 47, 49, 51, 53, 55, 57, + 59, 61, 65, 67, 69, 71, 73, + 75, 77, 79, 81, 83, 85, 87, + 89, 91, 93, 97, 99, 101, 103, + 105, 107, 109, 111, 113, 115, 117, + 119, 121, 123, 125, 129, 131, 133, + 135, 137, 139, 141, 143, 145, 147, + 149, 151, 153, 155, 157, 161, 163, + 165, 167, 169, 171, 173, 175, 177, + 179, 181, 183, 185, 187, 189, 193, + 195, 197, 199, 201, 203, 205, 207, + 209, 211, 213, 215, 217, 219, 221, + 225, 227, 229, 233 + }; + struct mt76_power_limits *limits; + struct mt7925_sku_tlv *sku_tlbv; + const u8 *ch_list; + + sku_len = sizeof(*sku_tlbv); + tx_power = 2 * phy->hw->conf.power_level; + if (!tx_power) + tx_power = 127; + + if (band == NL80211_BAND_2GHZ) { + n_chan = ARRAY_SIZE(chan_list_2ghz); + ch_list = chan_list_2ghz; + last_ch = chan_list_2ghz[ARRAY_SIZE(chan_list_2ghz) - 1]; + } else if (band == NL80211_BAND_6GHZ) { + n_chan = ARRAY_SIZE(chan_list_6ghz); + ch_list = chan_list_6ghz; + last_ch = chan_list_6ghz[ARRAY_SIZE(chan_list_6ghz) - 1]; + } else { + n_chan = ARRAY_SIZE(chan_list_5ghz); + ch_list = chan_list_5ghz; + last_ch = chan_list_5ghz[ARRAY_SIZE(chan_list_5ghz) - 1]; + } + batch_size = DIV_ROUND_UP(n_chan, batch_len); + + limits = devm_kmalloc(dev->dev, sizeof(*limits), GFP_KERNEL); + if (!limits) + return -ENOMEM; + + sku_tlbv = devm_kmalloc(dev->dev, sku_len, GFP_KERNEL); + if (!sku_tlbv) { + devm_kfree(dev->dev, limits); + return -ENOMEM; + } + + for (i = 0; i < batch_size; i++) { + struct mt7925_tx_power_limit_tlv *tx_power_tlv; + int j, msg_len, num_ch; + struct sk_buff *skb; + + num_ch = i == batch_size - 1 ? n_chan % batch_len : batch_len; + msg_len = sizeof(*tx_power_tlv) + num_ch * sku_len; + skb = mt76_mcu_msg_alloc(dev, NULL, msg_len); + if (!skb) { + err = -ENOMEM; + goto out; + } + + tx_power_tlv = (struct mt7925_tx_power_limit_tlv *) + skb_put(skb, sizeof(*tx_power_tlv)); + + BUILD_BUG_ON(sizeof(dev->alpha2) > sizeof(tx_power_tlv->alpha2)); + memcpy(tx_power_tlv->alpha2, dev->alpha2, sizeof(dev->alpha2)); + tx_power_tlv->n_chan = num_ch; + tx_power_tlv->tag = cpu_to_le16(0x1); + tx_power_tlv->len = cpu_to_le16(msg_len - 4); + + switch (band) { + case NL80211_BAND_2GHZ: + tx_power_tlv->band = 1; + break; + case NL80211_BAND_6GHZ: + tx_power_tlv->band = 3; + break; + default: + tx_power_tlv->band = 2; + break; + } + + for (j = 0; j < num_ch; j++, idx++) { + struct ieee80211_channel chan = { + .hw_value = ch_list[idx], + .band = band, + }; + s8 reg_power, sar_power; + + reg_power = mt76_connac_get_ch_power(phy, &chan, + tx_power); + sar_power = mt76_get_sar_power(phy, &chan, reg_power); + + mt76_get_rate_power_limits(phy, &chan, limits, + sar_power); + + tx_power_tlv->last_msg = ch_list[idx] == last_ch; + sku_tlbv->channel = ch_list[idx]; + + mt7925_mcu_build_sku(dev, sku_tlbv->pwr_limit, + limits, band); + skb_put_data(skb, sku_tlbv, sku_len); + } + err = mt76_mcu_skb_send_msg(dev, skb, + MCU_UNI_CMD(SET_POWER_LIMIT), + true); + if (err < 0) + goto out; + } + +out: + devm_kfree(dev->dev, sku_tlbv); + devm_kfree(dev->dev, limits); + return err; +} + +int mt7925_mcu_set_rate_txpower(struct mt76_phy *phy) +{ + struct mt76_dev *mdev = phy->dev; + struct mt792x_dev *dev = mt792x_hw_dev(mdev->hw); + int err; + + if (phy->cap.has_2ghz) { + err = mt7925_mcu_rate_txpower_band(phy, + NL80211_BAND_2GHZ); + if (err < 0) + return err; + } + + if (phy->cap.has_5ghz) { + err = mt7925_mcu_rate_txpower_band(phy, + NL80211_BAND_5GHZ); + if (err < 0) + return err; + } + + if (phy->cap.has_6ghz && dev->phy.clc_chan_conf) { + err = mt7925_mcu_rate_txpower_band(phy, + NL80211_BAND_6GHZ); + if (err < 0) + return err; + } + + return 0; +} + +int mt7925_mcu_wf_rf_pin_ctrl(struct mt792x_phy *phy) +{ +#define UNI_CMD_RADIO_STATUS_GET 0 + struct mt792x_dev *dev = phy->dev; + struct sk_buff *skb; + int ret; + struct { + __le16 tag; + __le16 len; + u8 rsv[4]; + } __packed req = { + .tag = UNI_CMD_RADIO_STATUS_GET, + .len = cpu_to_le16(sizeof(req)), + }; + struct mt7925_radio_status_event { + __le16 tag; + __le16 len; + + u8 data; + u8 rsv[3]; + } __packed *status; + + ret = mt76_mcu_send_and_get_msg(&dev->mt76, + MCU_UNI_CMD(RADIO_STATUS), + &req, sizeof(req), true, &skb); + if (ret) + return ret; + + skb_pull(skb, sizeof(struct tlv)); + status = (struct mt7925_radio_status_event *)skb->data; + ret = status->data; + + dev_kfree_skb(skb); + + return ret; +} + +int mt7925_mcu_set_rxfilter(struct mt792x_dev *dev, u32 fif, + u8 bit_op, u32 bit_map) +{ + struct mt792x_phy *phy = &dev->phy; + struct { + u8 band_idx; + u8 rsv1[3]; + + __le16 tag; + __le16 len; + u8 mode; + u8 rsv2[3]; + __le32 fif; + __le32 bit_map; /* bit_* for bitmap update */ + u8 bit_op; + u8 pad[51]; + } __packed req = { + .band_idx = phy->mt76->band_idx, + .tag = cpu_to_le16(UNI_BAND_CONFIG_SET_MAC80211_RX_FILTER), + .len = cpu_to_le16(sizeof(req) - 4), + + .mode = fif ? 0 : 1, + .fif = cpu_to_le32(fif), + .bit_map = cpu_to_le32(bit_map), + .bit_op = bit_op, + }; + + return mt76_mcu_send_msg(&phy->dev->mt76, MCU_UNI_CMD(BAND_CONFIG), + &req, sizeof(req), true); +} + +int mt7925_mcu_set_rssimonitor(struct mt792x_dev *dev, struct ieee80211_vif *vif) +{ + struct mt792x_bss_conf *mconf = mt792x_link_conf_to_mconf(&vif->bss_conf); + struct { + struct { + u8 bss_idx; + u8 pad[3]; + } __packed hdr; + __le16 tag; + __le16 len; + u8 enable; + s8 cqm_rssi_high; + s8 cqm_rssi_low; + u8 rsv; + } req = { + .hdr = { + .bss_idx = mconf->mt76.idx, + }, + .tag = cpu_to_le16(UNI_CMD_RSSI_MONITOR_SET), + .len = cpu_to_le16(sizeof(req) - 4), + .enable = vif->cfg.assoc, + .cqm_rssi_high = (s8)(vif->bss_conf.cqm_rssi_thold + vif->bss_conf.cqm_rssi_hyst), + .cqm_rssi_low = (s8)(vif->bss_conf.cqm_rssi_thold - vif->bss_conf.cqm_rssi_hyst), + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(RSSI_MONITOR), &req, + sizeof(req), false); +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h new file mode 100644 index 000000000..11f9eac13 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h @@ -0,0 +1,765 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* Copyright (C) 2023 MediaTek Inc. */ + +#ifndef __MT7925_MCU_H +#define __MT7925_MCU_H + +#include "../mt76_connac_mcu.h" + +/* ext event table */ +enum { + MCU_EXT_EVENT_RATE_REPORT = 0x87, +}; + +struct mt7925_mcu_eeprom_info { + __le32 addr; + __le32 valid; + u8 data[MT7925_EEPROM_BLOCK_SIZE]; +} __packed; + +#define MT_RA_RATE_NSS GENMASK(8, 6) +#define MT_RA_RATE_MCS GENMASK(3, 0) +#define MT_RA_RATE_TX_MODE GENMASK(12, 9) +#define MT_RA_RATE_DCM_EN BIT(4) +#define MT_RA_RATE_BW GENMASK(14, 13) + +struct mt7925_mcu_rxd { + __le32 rxd[8]; + + __le16 len; + __le16 pkt_type_id; + + u8 eid; + u8 seq; + u8 option; + u8 __rsv; + + u8 ext_eid; + u8 __rsv1[2]; + u8 s2d_index; + + u8 tlv[]; +}; + +struct mt7925_mcu_uni_event { + u8 cid; + u8 pad[3]; + __le32 status; /* 0: success, others: fail */ +} __packed; + +enum { + MT_EBF = BIT(0), /* explicit beamforming */ + MT_IBF = BIT(1) /* implicit beamforming */ +}; + +struct mt7925_mcu_reg_event { + __le32 reg; + __le32 val; +} __packed; + +struct mt7925_mcu_ant_id_config { + u8 ant_id[4]; +} __packed; + +struct mt7925_txpwr_req { + u8 _rsv[4]; + __le16 tag; + __le16 len; + + u8 format_id; + u8 catg; + u8 band_idx; + u8 _rsv1; +} __packed; + +struct mt7925_txpwr_event { + u8 rsv[4]; + __le16 tag; + __le16 len; + + u8 catg; + u8 band_idx; + u8 ch_band; + u8 format; /* 0:Legacy, 1:HE */ + + /* Rate power info */ + struct mt7925_txpwr txpwr; + + s8 pwr_max; + s8 pwr_min; + u8 rsv1; +} __packed; + +enum { + TM_SWITCH_MODE, + TM_SET_AT_CMD, + TM_QUERY_AT_CMD, +}; + +enum { + MT7925_TM_NORMAL, + MT7925_TM_TESTMODE, + MT7925_TM_ICAP, + MT7925_TM_ICAP_OVERLAP, + MT7925_TM_WIFISPECTRUM, +}; + +struct mt7925_rftest_evt { + __le32 param0; + __le32 param1; +} __packed; + +enum { + UNI_CHANNEL_SWITCH, + UNI_CHANNEL_RX_PATH, +}; + +enum { + UNI_CHIP_CONFIG_CHIP_CFG = 0x2, + UNI_CHIP_CONFIG_NIC_CAPA = 0x3, +}; + +enum { + UNI_BAND_CONFIG_RADIO_ENABLE, + UNI_BAND_CONFIG_RTS_THRESHOLD = 0x08, + UNI_BAND_CONFIG_SET_MAC80211_RX_FILTER = 0x0C, +}; + +enum { + UNI_WSYS_CONFIG_FW_LOG_CTRL, + UNI_WSYS_CONFIG_FW_DBG_CTRL, +}; + +enum { + UNI_EFUSE_ACCESS = 1, + UNI_EFUSE_BUFFER_MODE, + UNI_EFUSE_FREE_BLOCK, + UNI_EFUSE_BUFFER_RD, +}; + +enum { + UNI_CMD_ACCESS_REG_BASIC = 0x0, + UNI_CMD_ACCESS_RF_REG_BASIC, +}; + +enum { + UNI_MBMC_SETTING = 0, + UNI_MBMC_NO_RESP_SETTING = 1, +}; + +enum { + UNI_EVENT_SCAN_DONE_BASIC = 0, + UNI_EVENT_SCAN_DONE_CHNLINFO = 2, + UNI_EVENT_SCAN_DONE_NLO = 3, +}; + +enum { + UNI_CMD_RSSI_MONITOR_SET = 0, +}; + +enum { + UNI_EVENT_RSSI_MONITOR_INFO = 0, +}; + +enum connac3_mcu_cipher_type { + CONNAC3_CIPHER_NONE = 0, + CONNAC3_CIPHER_WEP40 = 1, + CONNAC3_CIPHER_TKIP = 2, + CONNAC3_CIPHER_AES_CCMP = 4, + CONNAC3_CIPHER_WEP104 = 5, + CONNAC3_CIPHER_BIP_CMAC_128 = 6, + CONNAC3_CIPHER_WEP128 = 7, + CONNAC3_CIPHER_WAPI = 8, + CONNAC3_CIPHER_CCMP_256 = 10, + CONNAC3_CIPHER_GCMP = 11, + CONNAC3_CIPHER_GCMP_256 = 12, +}; + +enum DMASHDL_GROUP_IDX { + DMASHDL_GROUP_0 = 0, + DMASHDL_GROUP_1, + DMASHDL_GROUP_2, + DMASHDL_GROUP_3, + DMASHDL_GROUP_4, + DMASHDL_GROUP_5, + DMASHDL_GROUP_6, + DMASHDL_GROUP_7, + DMASHDL_GROUP_8, + DMASHDL_GROUP_9, + DMASHDL_GROUP_10, + DMASHDL_GROUP_11, + DMASHDL_GROUP_12, + DMASHDL_GROUP_13, + DMASHDL_GROUP_14, + DMASHDL_GROUP_15, + DMASHDL_GROUP_NUM, + DMASHDL_LITE_GROUP_NUM = 64 +}; + +struct mt7925_mcu_scan_chinfo_event { + u8 nr_chan; + u8 alpha2[3]; +} __packed; + +enum { + UNI_SCAN_REQ = 1, + UNI_SCAN_CANCEL = 2, + UNI_SCAN_SCHED_REQ = 3, + UNI_SCAN_SCHED_ENABLE = 4, + UNI_SCAN_SSID = 10, + UNI_SCAN_BSSID, + UNI_SCAN_CHANNEL, + UNI_SCAN_IE, + UNI_SCAN_MISC, + UNI_SCAN_SSID_MATCH_SETS, +}; + +enum { + UNI_SNIFFER_ENABLE, + UNI_SNIFFER_CONFIG, +}; + +#define MT7925_RNR_SCAN_MAX_BSSIDS 10 +struct scan_hdr_tlv { + /* fixed field */ + u8 seq_num; + u8 bss_idx; + u8 pad[2]; + /* tlv */ + u8 data[]; +} __packed; + +struct scan_req_tlv { + __le16 tag; + __le16 len; + + u8 scan_type; /* 0: PASSIVE SCAN + * 1: ACTIVE SCAN + */ + u8 probe_req_num; /* Number of probe request for each SSID */ + u8 scan_func; /* BIT(0) Enable random MAC scan + * BIT(1) Disable DBDC scan type 1~3. + * BIT(2) Use DBDC scan type 3 (dedicated one RF to scan). + */ + u8 src_mask; + __le16 channel_min_dwell_time; + __le16 channel_dwell_time; /* channel Dwell interval */ + __le16 timeout_value; + __le16 probe_delay_time; + __le32 func_mask_ext; +} __packed; + +struct scan_ssid_tlv { + __le16 tag; + __le16 len; + + u8 ssid_type; /* BIT(0) wildcard SSID + * BIT(1) P2P wildcard SSID + * BIT(2) specified SSID + wildcard SSID + * BIT(2) + ssid_type_ext BIT(0) specified SSID only + */ + u8 ssids_num; + u8 is_short_ssid; + u8 pad; + struct mt76_connac_mcu_scan_ssid ssids[MT7925_RNR_SCAN_MAX_BSSIDS]; +} __packed; + +struct scan_bssid_tlv { + __le16 tag; + __le16 len; + + u8 bssid[ETH_ALEN]; + u8 match_ch; + u8 match_ssid_ind; + u8 rcpi; + u8 match_short_ssid_ind; + u8 pad[2]; +} __packed; + +struct scan_chan_info_tlv { + __le16 tag; + __le16 len; + + u8 channel_type; /* 0: Full channels + * 1: Only 2.4GHz channels + * 2: Only 5GHz channels + * 3: P2P social channel only (channel #1, #6 and #11) + * 4: Specified channels + * Others: Reserved + */ + u8 channels_num; /* valid when channel_type is 4 */ + u8 pad[2]; + struct mt76_connac_mcu_scan_channel channels[64]; +} __packed; + +struct scan_ie_tlv { + __le16 tag; + __le16 len; + + __le16 ies_len; + u8 band; + u8 pad; + u8 ies[]; +}; + +struct scan_misc_tlv { + __le16 tag; + __le16 len; + + u8 random_mac[ETH_ALEN]; + u8 rsv[2]; +}; + +struct scan_sched_req { + __le16 tag; + __le16 len; + + u8 version; + u8 stop_on_match; + u8 intervals_num; + u8 scan_func; + __le16 intervals[MT76_CONNAC_MAX_NUM_SCHED_SCAN_INTERVAL]; +}; + +struct scan_sched_ssid_match_sets { + __le16 tag; + __le16 len; + + u8 match_num; + u8 rsv[3]; + + struct mt76_connac_mcu_scan_match match[MT76_CONNAC_MAX_SCAN_MATCH]; +}; + +struct scan_sched_enable { + __le16 tag; + __le16 len; + + u8 active; + u8 rsv[3]; +}; + +struct mbmc_set_req { + u8 pad[4]; + u8 data[]; +} __packed; + +struct mbmc_conf_tlv { + __le16 tag; + __le16 len; + + u8 mbmc_en; + u8 band; + u8 pad[2]; +} __packed; + +struct edca { + __le16 tag; + __le16 len; + + u8 queue; + u8 set; + u8 cw_min; + u8 cw_max; + __le16 txop; + u8 aifs; + u8 __rsv; +}; + +struct bss_req_hdr { + u8 bss_idx; + u8 __rsv[3]; +} __packed; + +struct bss_rate_tlv { + __le16 tag; + __le16 len; + u8 __rsv1[2]; + __le16 basic_rate; + __le16 bc_trans; + __le16 mc_trans; + u8 short_preamble; + u8 bc_fixed_rate; + u8 mc_fixed_rate; + u8 __rsv2; +} __packed; + +struct bss_mld_tlv { + __le16 tag; + __le16 len; + u8 group_mld_id; + u8 own_mld_id; + u8 mac_addr[ETH_ALEN]; + u8 remap_idx; + u8 link_id; + u8 eml_enable; + u8 max_link_num; + u8 hybrid_mode; + u8 __rsv[3]; +} __packed; + +struct bss_eht_tlv { + __le16 tag; + __le16 len; + u8 is_eht_op_present; + u8 is_eth_dscb_present; + u8 eht_ctrl; + u8 eht_ccfs0; + u8 eht_ccfs1; + u8 pad1; + __le16 eht_dis_sub_chan_bitmap; + u8 pad2[4]; +} __packed; + +struct sta_rec_ba_uni { + __le16 tag; + __le16 len; + u8 tid; + u8 ba_type; + u8 amsdu; + u8 ba_en; + __le16 ssn; + __le16 winsize; + u8 ba_rdd_rro; + u8 __rsv[3]; +} __packed; + +struct sta_rec_eht { + __le16 tag; + __le16 len; + u8 tid_bitmap; + u8 _rsv; + __le16 mac_cap; + __le64 phy_cap; + __le64 phy_cap_ext; + u8 mcs_map_bw20[4]; + u8 mcs_map_bw80[3]; + u8 mcs_map_bw160[3]; + u8 mcs_map_bw320[3]; + u8 _rsv2[3]; +} __packed; + +struct sta_rec_sec_uni { + __le16 tag; + __le16 len; + u8 add; + u8 tx_key; + u8 key_type; + u8 is_authenticator; + u8 peer_addr[6]; + u8 bss_idx; + u8 cipher_id; + u8 key_id; + u8 key_len; + u8 wlan_idx; + u8 mgmt_prot; + u8 key[32]; + u8 key_rsc[16]; +} __packed; + +struct sta_rec_hdr_trans { + __le16 tag; + __le16 len; + u8 from_ds; + u8 to_ds; + u8 dis_rx_hdr_tran; + u8 rsv; +} __packed; + +struct sta_rec_mld { + __le16 tag; + __le16 len; + u8 mac_addr[ETH_ALEN]; + __le16 primary_id; + __le16 secondary_id; + __le16 wlan_id; + u8 link_num; + u8 rsv[3]; + struct { + __le16 wlan_id; + u8 bss_idx; + u8 rsv; + } __packed link[2]; +} __packed; + +struct sta_rec_eht_mld { + __le16 tag; + __le16 len; + u8 nsep; + u8 mld_type; + u8 __rsv1[1]; + u8 str_cap[3]; + u8 eml_cap[3]; + u8 __rsv2[3]; +} __packed; + +struct bss_ifs_time_tlv { + __le16 tag; + __le16 len; + u8 slot_valid; + u8 sifs_valid; + u8 rifs_valid; + u8 eifs_valid; + __le16 slot_time; + __le16 sifs_time; + __le16 rifs_time; + __le16 eifs_time; + u8 eifs_cck_valid; + u8 rsv; + __le16 eifs_cck_time; +} __packed; + +struct bss_rlm_tlv { + __le16 tag; + __le16 len; + u8 control_channel; + u8 center_chan; + u8 center_chan2; + u8 bw; + u8 tx_streams; + u8 rx_streams; + u8 ht_op_info; + u8 sco; + u8 band; + u8 pad[3]; +} __packed; + +#define MT7925_STA_UPDATE_MAX_SIZE (sizeof(struct sta_req_hdr) + \ + sizeof(struct sta_rec_basic) + \ + sizeof(struct sta_rec_bf) + \ + sizeof(struct sta_rec_ht) + \ + sizeof(struct sta_rec_he_v2) + \ + sizeof(struct sta_rec_ba_uni) + \ + sizeof(struct sta_rec_vht) + \ + sizeof(struct sta_rec_uapsd) + \ + sizeof(struct sta_rec_amsdu) + \ + sizeof(struct sta_rec_bfee) + \ + sizeof(struct sta_rec_phy) + \ + sizeof(struct sta_rec_ra) + \ + sizeof(struct sta_rec_sec_uni) + \ + sizeof(struct sta_rec_ra_fixed) + \ + sizeof(struct sta_rec_he_6g_capa) + \ + sizeof(struct sta_rec_eht) + \ + sizeof(struct sta_rec_hdr_trans) + \ + sizeof(struct sta_rec_mld) + \ + sizeof(struct tlv) * 2 + \ + sizeof(struct sta_rec_remove)) + +#define MT7925_BSS_UPDATE_MAX_SIZE (sizeof(struct bss_req_hdr) + \ + sizeof(struct mt76_connac_bss_basic_tlv) + \ + sizeof(struct mt76_connac_bss_qos_tlv) + \ + sizeof(struct bss_rate_tlv) + \ + sizeof(struct bss_mld_tlv) + \ + sizeof(struct bss_info_uni_he) + \ + sizeof(struct bss_info_uni_bss_color) + \ + sizeof(struct bss_ifs_time_tlv) + \ + sizeof(struct bss_rlm_tlv) + \ + sizeof(struct tlv)) + +#define MT_CONNAC3_SKU_POWER_LIMIT 449 +struct mt7925_sku_tlv { + u8 channel; + s8 pwr_limit[MT_CONNAC3_SKU_POWER_LIMIT]; +} __packed; + +struct mt7925_tx_power_limit_tlv { + u8 rsv[4]; + + __le16 tag; + __le16 len; + + /* DW0 - common info*/ + u8 ver; + u8 pad0; + __le16 rsv1; + /* DW1 - cmd hint */ + u8 n_chan; /* # channel */ + u8 band; /* 2.4GHz - 5GHz - 6GHz */ + u8 last_msg; + u8 limit_type; + /* DW3 */ + u8 alpha2[4]; /* regulatory_request.alpha2 */ + u8 pad2[32]; + + u8 data[]; +} __packed; + +struct mt7925_arpns_tlv { + __le16 tag; + __le16 len; + + u8 enable; + u8 ips_num; + u8 rsv[2]; +} __packed; + +struct mt7925_wow_pattern_tlv { + __le16 tag; + __le16 len; + u8 bss_idx; + u8 index; /* pattern index */ + u8 enable; /* 0: disable + * 1: enable + */ + u8 data_len; /* pattern length */ + u8 offset; + u8 mask[MT76_CONNAC_WOW_MASK_MAX_LEN]; + u8 pattern[MT76_CONNAC_WOW_PATTEN_MAX_LEN]; + u8 rsv[4]; +}; + +#define MT7925_WOW_PATTERN_TLV_V2_SIZE \ + (offsetof(struct mt7925_wow_pattern_tlv, rsv) + 3) + +struct roc_acquire_tlv { + __le16 tag; + __le16 len; + u8 bss_idx; + u8 tokenid; + u8 control_channel; + u8 sco; + u8 band; + u8 bw; + u8 center_chan; + u8 center_chan2; + u8 bw_from_ap; + u8 center_chan_from_ap; + u8 center_chan2_from_ap; + u8 reqtype; + __le32 maxinterval; + u8 dbdcband; + u8 rsv[3]; +} __packed; + +enum ENUM_CMD_TEST_CTRL_ACT { + CMD_TEST_CTRL_ACT_SWITCH_MODE = 0, + CMD_TEST_CTRL_ACT_SET_AT = 1, + CMD_TEST_CTRL_ACT_GET_AT = 2, + CMD_TEST_CTRL_ACT_SET_AT_ENG = 3, + CMD_TEST_CTRL_ACT_GET_AT_ENG = 4, + CMD_TEST_CTRL_ACT_NUM +}; + +enum ENUM_CMD_TEST_CTRL_ACT_SWITCH_MODE_OP { + CMD_TEST_CTRL_ACT_SWITCH_MODE_NORMAL = 0, + CMD_TEST_CTRL_ACT_SWITCH_MODE_RF_TEST = 1, + CMD_TEST_CTRL_ACT_SWITCH_MODE_ICAP = 2, + CMD_TEST_CTRL_ACT_SWITCH_MODE_NUM +}; + +union testmode_data { + __le32 op_mode; + __le32 channel_freq; + u8 rf_at_info[84]; +}; + +union testmode_evt { + __le32 op_mode; + __le32 channel_freq; + u8 rf_at_info[1024]; +}; + +struct uni_cmd_testmode_ctrl { + u16 tag; + u16 length; + u8 action; + u8 reserved[3]; + union testmode_data data; +} __packed; + +struct mt7925_rftest_cmd { + u8 padding[4]; + struct uni_cmd_testmode_ctrl ctrl; +} __packed; + +static inline enum connac3_mcu_cipher_type +mt7925_mcu_get_cipher(int cipher) +{ + switch (cipher) { + case WLAN_CIPHER_SUITE_WEP40: + return CONNAC3_CIPHER_WEP40; + case WLAN_CIPHER_SUITE_WEP104: + return CONNAC3_CIPHER_WEP104; + case WLAN_CIPHER_SUITE_TKIP: + return CONNAC3_CIPHER_TKIP; + case WLAN_CIPHER_SUITE_AES_CMAC: + return CONNAC3_CIPHER_BIP_CMAC_128; + case WLAN_CIPHER_SUITE_CCMP: + return CONNAC3_CIPHER_AES_CCMP; + case WLAN_CIPHER_SUITE_CCMP_256: + return CONNAC3_CIPHER_CCMP_256; + case WLAN_CIPHER_SUITE_GCMP: + return CONNAC3_CIPHER_GCMP; + case WLAN_CIPHER_SUITE_GCMP_256: + return CONNAC3_CIPHER_GCMP_256; + case WLAN_CIPHER_SUITE_SMS4: + return CONNAC3_CIPHER_WAPI; + default: + return CONNAC3_CIPHER_NONE; + } +} + +static inline bool +mt7925_mcu_tlv_valid(struct tlv *tlv, u32 rem) +{ + u16 len; + + if (rem < sizeof(*tlv)) + return false; + + len = le16_to_cpu(tlv->len); + + /* a length below the header size would not advance the cursor */ + return len >= sizeof(*tlv) && len <= rem; +} + +#define mt7925_for_each_tlv(tlv, rem) \ + for (; mt7925_mcu_tlv_valid(tlv, rem); \ + (rem) -= le16_to_cpu((tlv)->len), \ + (tlv) = (struct tlv *)((u8 *)(tlv) + le16_to_cpu((tlv)->len))) + +int mt7925_mcu_set_dbdc(struct mt76_phy *phy, bool enable); +int mt7925_mcu_hw_scan(struct mt76_phy *phy, struct ieee80211_vif *vif, + struct ieee80211_scan_request *scan_req); +int mt7925_mcu_cancel_hw_scan(struct mt76_phy *phy, + struct ieee80211_vif *vif); +int mt7925_mcu_sched_scan_req(struct mt76_phy *phy, + struct ieee80211_vif *vif, + struct cfg80211_sched_scan_request *sreq, + struct ieee80211_scan_ies *ies); +int mt7925_mcu_sched_scan_enable(struct mt76_phy *phy, + struct ieee80211_vif *vif, + bool enable); +void mt7925_mcu_del_dev(struct mt76_dev *mdev, + struct ieee80211_vif *vif); +int mt7925_mcu_add_bss_info(struct mt792x_phy *phy, + struct ieee80211_chanctx_conf *ctx, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_link_sta *link_sta, + int enable); +int mt7925_mcu_add_bss_info_sta(struct mt792x_phy *phy, + struct ieee80211_chanctx_conf *ctx, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_link_sta *link_sta, + u16 bmc_tx_wlan_idx, + u16 sta_wlan_idx, + int enable); +int mt7925_mcu_set_timing(struct mt792x_phy *phy, + struct ieee80211_bss_conf *link_conf); +int mt7925_mcu_set_deep_sleep(struct mt792x_dev *dev, bool enable); +int mt7925_mcu_set_thermal_protect(struct mt792x_dev *dev); +int mt7925_mcu_set_channel_domain(struct mt76_phy *phy); +int mt7925_mcu_set_radio_en(struct mt792x_phy *phy, bool enable); +int mt7925_mcu_set_chctx(struct mt76_phy *phy, struct mt76_vif_link *mvif, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_chanctx_conf *ctx); +int mt7925_mcu_set_eht_pp(struct mt76_phy *phy, struct mt76_vif_link *mvif, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_chanctx_conf *ctx); +int mt7925_mcu_set_rate_txpower(struct mt76_phy *phy); +int mt7925_mcu_update_arp_filter(struct mt76_dev *dev, + struct ieee80211_bss_conf *link_conf); +int +mt7925_mcu_uni_bss_bcnft(struct mt792x_dev *dev, + struct ieee80211_bss_conf *link_conf, bool enable); +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h new file mode 100644 index 000000000..321e73234 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h @@ -0,0 +1,417 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* Copyright (C) 2023 MediaTek Inc. */ + +#ifndef __MT7925_H +#define __MT7925_H + +#include "../mt792x.h" +#include "regs.h" + +#define MT7925_BEACON_RATES_TBL 25 + +#define MT7925_TX_RING_SIZE 2048 +#define MT7925_TX_MCU_RING_SIZE 256 +#define MT7925_TX_FWDL_RING_SIZE 128 + +#define MT7925_RX_RING_SIZE 1536 +#define MT7925_RX_MCU_RING_SIZE 512 +#define MT7928_RX_MCU_WA_RING_SIZE 512 + +#define MT7925_EEPROM_SIZE 3584 +#define MT7925_TOKEN_SIZE 8192 + +#define MT7925_EEPROM_BLOCK_SIZE 16 + +#define MT7925_SKU_RATE_NUM 161 +#define MT7925_SKU_MAX_DELTA_IDX MT7925_SKU_RATE_NUM +#define MT7925_SKU_TABLE_SIZE (MT7925_SKU_RATE_NUM + 1) + +#define MCU_UNI_EVENT_ROC 0x27 + +#define HIF_TRAFFIC_IDLE 0x2 + +enum { + UNI_EVENT_HIF_CTRL_BASIC = 0, + UNI_EVENT_HIF_CTRL_TAG_NUM +}; + +struct mt7925_mcu_hif_ctrl_basic_tlv { + __le16 tag; + __le16 len; + u8 cid; + u8 pad[3]; + u32 status; + u8 hif_type; + u8 hif_tx_traffic_status; + u8 hif_rx_traffic_status; + u8 hifsuspend; + u8 rsv[4]; +} __packed; + +enum { + UNI_ROC_ACQUIRE, + UNI_ROC_ABORT, + UNI_ROC_SUB_LINK = 3, + UNI_ROC_NUM +}; + +enum mt7925_roc_req { + MT7925_ROC_REQ_JOIN, + MT7925_ROC_REQ_ROC, + MT7925_ROC_REQ_SUB_LINK, + MT7925_ROC_REQ_MLSR_AG = 10, + MT7925_ROC_REQ_MLSR_AA, + MT7925_ROC_REQ_NUM +}; + +enum { + UNI_EVENT_ROC_GRANT = 0, + UNI_EVENT_ROC_GRANT_SUB_LINK = 4, + UNI_EVENT_ROC_TAG_NUM +}; + +struct mt7925_roc_grant_tlv { + __le16 tag; + __le16 len; + u8 bss_idx; + u8 tokenid; + u8 status; + u8 primarychannel; + u8 rfsco; + u8 rfband; + u8 channelwidth; + u8 centerfreqseg1; + u8 centerfreqseg2; + u8 reqtype; + u8 dbdcband; + u8 rsv[1]; + __le32 max_interval; +} __packed; + +struct mt7925_beacon_loss_tlv { + __le16 tag; + __le16 len; + u8 reason; + u8 nr_btolink; + u8 pad[2]; +} __packed; + +struct mt7925_uni_beacon_loss_event { + struct { + u8 bss_idx; + u8 pad[3]; + } __packed hdr; + struct mt7925_beacon_loss_tlv beacon_loss; +} __packed; + +struct mt7925_uni_rssi_monitor_event { + __le16 tag; + __le16 len; + __le32 rssi; +} __packed; + +#define to_rssi(field, rxv) ((FIELD_GET(field, rxv) - 220) / 2) +#define to_rcpi(rssi) (2 * (rssi) + 220) + +enum mt7925_txq_id { + MT7925_TXQ_BAND0, + MT7925_TXQ_BAND1, + MT7925_TXQ_MCU_WM = 15, + MT7925_TXQ_FWDL, +}; + +enum mt7925_rxq_id { + MT7925_RXQ_BAND0 = 2, + MT7925_RXQ_BAND1, + MT7925_RXQ_MCU_WM = 0, + MT7925_RXQ_MCU_WM2, /* for tx done */ +}; + +enum mt7927_txq_id { + MT7927_TXQ_BAND0 = MT7925_TXQ_BAND0, + MT7927_TXQ_BAND1 = MT7925_TXQ_BAND1, + MT7927_TXQ_MCU_WM = MT7925_TXQ_MCU_WM, + MT7927_TXQ_FWDL = MT7925_TXQ_FWDL, +}; + +enum mt7927_rxq_id { + MT7927_RXQ_BAND0 = 4, + MT7927_RXQ_MCU_WM = 6, + MT7927_RXQ_DATA2 = 7, +}; + +enum mt7928_rxq_id { + MT7928_RXQ_BAND0, + MT7928_RXQ_BAND1 = 2, + MT7928_RXQ_MCU_WM = 3, + MT7928_RXQ_MCU_WM2 = 1, /* for tx done */ +}; + +enum { + MODE_OPEN = 0, + MODE_SHARED = 1, + MODE_WPA = 3, + MODE_WPA_PSK = 4, + MODE_WPA_NONE = 5, + MODE_WPA2 = 6, + MODE_WPA2_PSK = 7, + MODE_WPA3_SAE = 11, +}; + +enum { + MT7925_CLC_POWER, + MT7925_CLC_CHAN, + MT7925_CLC_MAX_NUM, +}; + +struct mt7925_clc_rule_v2 { + u32 flag; + u8 alpha2[2]; + u8 rsv[10]; +} __packed; + +struct mt7925_clc_rule { + u8 alpha2[2]; + u8 type[2]; + u8 seg_idx; + u8 flag; /* UNII4~8 ctrl flag */ + u8 rsv[2]; +} __packed; + +struct mt7925_clc_segment { + u8 idx; + u8 rsv1[3]; + u32 offset; + u32 len; + u8 rsv2[4]; +} __packed; + +struct mt7925_clc_type0 { + u8 nr_country; + u8 type; + u8 nr_seg; + u8 rsv[7]; +} __packed; + +struct mt7925_clc_type2 { + u8 type; + u8 rsv[9]; +} __packed; + +struct mt7925_clc { + __le32 len; + u8 idx; + u8 ver; + union { + struct mt7925_clc_type0 t0; + struct mt7925_clc_type2 t2; + }; + u8 data[]; +} __packed; + +enum mt7925_eeprom_field { + MT_EE_CHIP_ID = 0x000, + MT_EE_VERSION = 0x002, + MT_EE_MAC_ADDR = 0x004, + MT_EE_HW_TYPE = 0xa71, + __MT_EE_MAX = 0x9ff +}; + +#define MT_EE_HW_TYPE_ENCAP GENMASK(1, 0) + +enum { + TXPWR_USER, + TXPWR_EEPROM, + TXPWR_MAC, + TXPWR_MAX_NUM, +}; + +struct mt7925_txpwr { + s8 cck[4][2]; + s8 ofdm[8][2]; + s8 ht20[8][2]; + s8 ht40[9][2]; + s8 vht20[12][2]; + s8 vht40[12][2]; + s8 vht80[12][2]; + s8 vht160[12][2]; + s8 he26[12][2]; + s8 he52[12][2]; + s8 he106[12][2]; + s8 he242[12][2]; + s8 he484[12][2]; + s8 he996[12][2]; + s8 he996x2[12][2]; + s8 eht26[16][2]; + s8 eht52[16][2]; + s8 eht106[16][2]; + s8 eht242[16][2]; + s8 eht484[16][2]; + s8 eht996[16][2]; + s8 eht996x2[16][2]; + s8 eht996x4[16][2]; + s8 eht26_52[16][2]; + s8 eht26_106[16][2]; + s8 eht484_242[16][2]; + s8 eht996_484[16][2]; + s8 eht996_484_242[16][2]; + s8 eht996x2_484[16][2]; + s8 eht996x3[16][2]; + s8 eht996x3_484[16][2]; +}; + +static inline u8 mt7927_band_idx(enum nl80211_band band) +{ + switch (band) { + case NL80211_BAND_2GHZ: + return 0; + case NL80211_BAND_5GHZ: + case NL80211_BAND_6GHZ: + default: + return 1; + } +} + +extern const struct ieee80211_ops mt7925_ops; + +int __mt7925_start(struct mt792x_phy *phy); +int mt7925_register_device(struct mt792x_dev *dev); +void mt7925_unregister_device(struct mt792x_dev *dev); +int mt7925_run_firmware(struct mt792x_dev *dev); +int mt7925_mcu_set_bss_pm(struct mt792x_dev *dev, + struct ieee80211_bss_conf *link_conf, + bool enable); +int mt7925_mcu_sta_update(struct mt792x_dev *dev, + struct ieee80211_link_sta *link_sta, + struct ieee80211_vif *vif, + struct mt792x_link_sta *mlink, + bool enable, + enum mt76_sta_info_state state); +int mt7925_mcu_set_chan_info(struct mt792x_phy *phy, u16 tag); +int mt7925_mcu_set_tx(struct mt792x_dev *dev, struct ieee80211_bss_conf *bss_conf); +int mt7925_mcu_set_eeprom(struct mt792x_dev *dev); +int mt7925_mcu_get_rx_rate(struct mt792x_phy *phy, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, struct rate_info *rate); +int mt7925_mcu_fw_log_2_host(struct mt792x_dev *dev, u8 ctrl); +void mt7925_mcu_rx_event(struct mt792x_dev *dev, struct sk_buff *skb); +int mt7925_mcu_chip_config(struct mt792x_dev *dev, const char *cmd); +int mt7925_mcu_set_rxfilter(struct mt792x_dev *dev, u32 fif, + u8 bit_op, u32 bit_map); + +int mt7925_mac_init(struct mt792x_dev *dev); +int mt7925_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); +bool mt7925_mac_wtbl_update(struct mt792x_dev *dev, int idx, u32 mask); +int mt7925_mac_sta_event(struct mt76_dev *mdev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, enum mt76_sta_event ev); +void mt7925_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); +void mt7925_mac_reset_work(struct work_struct *work); +int mt7925e_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, + enum mt76_txq_id qid, struct mt76_wcid *wcid, + struct ieee80211_sta *sta, + struct mt76_tx_info *tx_info); + +void mt7925_tx_token_put(struct mt792x_dev *dev); +bool mt7925_rx_check(struct mt76_dev *mdev, void *data, int len); +void mt7925_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, + struct sk_buff *skb, u32 *info); +void mt7925_stats_work(struct work_struct *work); +void mt7925_set_stream_he_eht_caps(struct mt792x_phy *phy); +int mt7925_init_mlo_caps(struct mt792x_phy *phy); +int mt7925_init_debugfs(struct mt792x_dev *dev); + +int mt7925_mcu_set_beacon_filter(struct mt792x_dev *dev, + struct ieee80211_vif *vif, + bool enable); +int mt7925_mcu_uni_tx_ba(struct mt792x_dev *dev, + struct ieee80211_ampdu_params *params, + struct ieee80211_vif *vif, bool enable); +int mt7925_mcu_uni_rx_ba(struct mt792x_dev *dev, + struct ieee80211_ampdu_params *params, + struct ieee80211_vif *vif, bool enable); +void mt7925_mlo_pm_work(struct work_struct *work); +void mt7925_scan_work(struct work_struct *work); +void mt7925_roc_work(struct work_struct *work); +void mt7925_csa_work(struct work_struct *work); +int mt7925_mcu_uni_bss_ps(struct mt792x_dev *dev, + struct ieee80211_bss_conf *link_conf); +void mt7925_coredump_work(struct work_struct *work); +int mt7925_get_txpwr_info(struct mt792x_dev *dev, u8 band_idx, + struct mt7925_txpwr *txpwr); +void mt7925_mac_set_fixed_rate_table(struct mt792x_dev *dev, + u8 tbl_idx, u16 rate_idx); +void mt7925_mac_write_txwi(struct mt76_dev *dev, __le32 *txwi, + struct sk_buff *skb, struct mt76_wcid *wcid, + struct ieee80211_key_conf *key, int pid, + enum mt76_txq_id qid, u32 changed); +void mt7925_txwi_free(struct mt792x_dev *dev, struct mt76_txwi_cache *t, + struct ieee80211_sta *sta, struct mt76_wcid *wcid, + struct list_head *free_list); +int mt7925_mcu_parse_response(struct mt76_dev *mdev, int cmd, + struct sk_buff *skb, int seq); + +int mt7925e_mac_reset(struct mt792x_dev *dev); +int mt7925e_mcu_init(struct mt792x_dev *dev); +void mt7925_mac_add_txs(struct mt792x_dev *dev, void *data); +void mt7928_mac_add_txs_msg(struct mt792x_dev *dev, void *evt); +void mt7925_set_runtime_pm(struct mt792x_dev *dev); +void mt7925_mcu_set_suspend_iter(void *priv, u8 *mac, + struct ieee80211_vif *vif); +void mt7925_connac_mcu_set_suspend_iter(void *priv, u8 *mac, + struct ieee80211_vif *vif); +void mt7925_set_ipv6_ns_work(struct work_struct *work); + +int mt7925_mcu_set_sniffer(struct mt792x_dev *dev, struct ieee80211_vif *vif, + bool enable); +int mt7925_mcu_config_sniffer(struct mt792x_vif *vif, + struct ieee80211_chanctx_conf *ctx); +int mt7925_mcu_get_temperature(struct mt792x_phy *phy); + +int mt7925_usb_sdio_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, + enum mt76_txq_id qid, struct mt76_wcid *wcid, + struct ieee80211_sta *sta, + struct mt76_tx_info *tx_info); +void mt7925_usb_sdio_tx_complete_skb(struct mt76_dev *mdev, + struct mt76_queue_entry *e); +bool mt7925_usb_sdio_tx_status_data(struct mt76_dev *mdev, u8 *update); + +int mt7925_mcu_uni_add_beacon_offload(struct mt792x_dev *dev, + struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + bool enable); +int mt7925_set_tx_sar_pwr(struct ieee80211_hw *hw, + const struct cfg80211_sar_specs *sar); + +int mt7925_mcu_regval(struct mt792x_dev *dev, u32 regidx, u32 *val, bool set); +int mt7925_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2, + enum environment_cap env_cap); +int mt7925_mcu_set_mlo_roc(struct mt792x_phy *phy, struct mt792x_bss_conf *mconf, + u16 sel_links, int duration, u8 token_id); +int mt7925_mcu_set_roc(struct mt792x_phy *phy, struct mt792x_bss_conf *mconf, + struct ieee80211_channel *chan, int duration, + enum mt7925_roc_req type, u8 token_id); +int mt7925_mcu_abort_roc(struct mt792x_phy *phy, struct mt792x_bss_conf *mconf, + u8 token_id); +void mt7925_roc_abort_sync(struct mt792x_dev *dev); +int mt7925_mcu_fill_message(struct mt76_dev *mdev, struct sk_buff *skb, + int cmd, int *wait_seq); +int mt7925_mcu_add_key(struct mt76_dev *dev, struct ieee80211_vif *vif, + struct mt76_connac_sta_key_conf *sta_key_conf, + struct ieee80211_key_conf *key, int mcu_cmd, + struct mt76_wcid *wcid, enum set_key_cmd cmd, + struct mt792x_sta *msta); +int mt7925_mcu_set_rts_thresh(struct mt792x_phy *phy, u32 val); +int mt7925_mcu_wtbl_update_hdr_trans(struct mt792x_dev *dev, + struct ieee80211_vif *vif, + struct mt792x_bss_conf *mconf, + struct mt792x_link_sta *mlink); +int mt7925_mcu_wf_rf_pin_ctrl(struct mt792x_phy *phy); + +int mt7925_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + void *data, int len); +int mt7925_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg, + struct netlink_callback *cb, void *data, int len); + +int mt7925_mcu_set_rssimonitor(struct mt792x_dev *dev, struct ieee80211_vif *vif); +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/nan.c b/drivers/net/wireless/mediatek/mt76/mt7925/nan.c new file mode 100644 index 000000000..d260e803d --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7925/nan.c @@ -0,0 +1,1098 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2025-2026 MediaTek Inc. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mt7925.h" +#include "mcu.h" +#include "nan.h" +#include "regd.h" + +static void mt7925_nan_set_5g_channel(struct mt792x_dev *dev, + struct mt7925_nan_enable_req_tlv *req, + struct cfg80211_nan_conf *conf) +{ + struct ieee80211_channel *chan; + u32 ch5g = 0; + + chan = conf->band_cfgs[NL80211_BAND_5GHZ].chan; + + if (!chan) + return; + + if (!mt7925_regd_is_valid_channel(dev, NL80211_BAND_5GHZ, chan)) + return; + + req->config_support_5g = 1; + req->support_5g_val = 1; + req->config_5g_channel = 1; + + if (chan->hw_value == NAN_5G_LOW_DISC_CHANNEL) + ch5g |= BIT(0); + else if (chan->hw_value == NAN_5G_HIGH_DISC_CHANNEL) + ch5g |= BIT(1); + + req->channel_5g_val = cpu_to_le32(ch5g); +} + +static void mt7925_nan_set_2g_support(struct mt7925_nan_enable_req_tlv *req, + struct cfg80211_nan_conf *conf) +{ + if (!conf->band_cfgs[NL80211_BAND_2GHZ].chan) + return; + + req->config_2dot4g_support = 1; + req->support_2dot4g_val = 1; +} + +static void mt7925_nan_set_cluster_id(struct mt7925_nan_enable_req_tlv *req, + const u8 *cluster_id) +{ + if (!cluster_id) + return; + + req->cluster_high = cpu_to_le16(cluster_id[4] | cluster_id[5] << 8); + req->cluster_low = cpu_to_le16((u16)cluster_id[3]); +} + +static void mt7925_nan_set_dw_interval(struct mt7925_nan_enable_req_tlv *req, + struct cfg80211_nan_conf *conf) +{ + if (conf->band_cfgs[NL80211_BAND_2GHZ].awake_dw_interval > 0) { + req->config_dw.config_2dot4g_dw_band = 1; + req->config_dw.dw_2dot4g_interval_val = + cpu_to_le32(conf->band_cfgs[NL80211_BAND_2GHZ].awake_dw_interval); + } + + if (conf->band_cfgs[NL80211_BAND_5GHZ].awake_dw_interval > 0) { + req->config_dw.config_5g_dw_band = 1; + req->config_dw.dw_5g_interval_val = + cpu_to_le32(conf->band_cfgs[NL80211_BAND_5GHZ].awake_dw_interval); + } +} + +static void mt7925_nan_set_disc_beacon(struct mt7925_nan_enable_req_tlv *req, + struct cfg80211_nan_conf *conf) +{ + if (conf->discovery_beacon_interval > 0) { + req->config_2dot4g_beacons = true; + req->beacon_2dot4g_val = conf->discovery_beacon_interval; + } +} + +static void mt7925_nan_set_rssi_thresholds(struct mt7925_nan_enable_req_tlv *req, + struct cfg80211_nan_conf *conf) +{ + if (conf->band_cfgs[NL80211_BAND_2GHZ].chan) { + req->config_2dot4g_rssi_close = 1; + req->rssi_close_2dot4g_val = + abs(conf->band_cfgs[NL80211_BAND_2GHZ].rssi_close); + req->config_2dot4g_rssi_middle = 1; + req->rssi_middle_2dot4g_val = + abs(conf->band_cfgs[NL80211_BAND_2GHZ].rssi_middle); + } + + if (conf->band_cfgs[NL80211_BAND_5GHZ].chan) { + req->config_5g_rssi_close = 1; + req->rssi_close_5g_val = + abs(conf->band_cfgs[NL80211_BAND_5GHZ].rssi_close); + req->config_5g_rssi_middle = 1; + req->rssi_middle_5g_val = + abs(conf->band_cfgs[NL80211_BAND_5GHZ].rssi_middle); + } +} + +static void mt7925_nan_set_scan_params(struct mt7925_nan_enable_req_tlv *req, + struct cfg80211_nan_conf *conf) +{ + req->scan_params_val.scan_period[0] = + cpu_to_le16(conf->scan_period < 255 ? conf->scan_period : 255); + req->scan_params_val.dwell_time[0] = + conf->scan_dwell_time < 255 ? conf->scan_dwell_time : 255; +} + +static u16 +mt7925_nan_avail_attr_ctrl(const struct ieee80211_nan_sched_cfg *sched) +{ + if (sched->avail_blob_len < NAN_AVAIL_ATTR_CTRL_OFFSET + 2) + return 0; + + return sched->avail_blob[NAN_AVAIL_ATTR_CTRL_OFFSET] | + sched->avail_blob[NAN_AVAIL_ATTR_CTRL_OFFSET + 1] << 8; +} + +static void +mt7925_nan_update_conf(struct mt792x_vif *mvif, + const struct cfg80211_nan_conf *conf) +{ + mvif->nan.conf.master_pref = conf->master_pref; + mvif->nan.conf.bands = conf->bands; + mvif->nan.conf.discovery_beacon_interval = + conf->discovery_beacon_interval; + mvif->nan.conf.enable_dw_notification = + conf->enable_dw_notification; + + memcpy(mvif->nan.conf.cluster_id, conf->cluster_id, ETH_ALEN); +} + +int mt7925_nan_set_nmi_addr(struct mt792x_dev *dev, const u8 *addr) +{ + struct mt76_dev *mdev; + struct { + u8 rsv[4]; + struct mt7925_nan_nmi_addr_tlv nmi_addr_tlv; + } nmi_cmd = { + .rsv = { 0 }, + .nmi_addr_tlv = { + .tag = cpu_to_le16(NAN_UNI_CMD_CHANGE_NMI_ADDRESS), + .len = cpu_to_le16(sizeof(struct mt7925_nan_nmi_addr_tlv)), + }, + }; + int ret; + + if (!dev || !addr) + return -EINVAL; + + if (is_zero_ether_addr(addr) || is_multicast_ether_addr(addr)) { + dev_err(dev->mt76.dev, "NAN: invalid NMI address %pM\n", addr); + return -EINVAL; + } + + mdev = &dev->mt76; + memcpy(nmi_cmd.nmi_addr_tlv.nmi_addr, addr, ETH_ALEN); + + ret = mt76_mcu_send_msg(mdev, MCU_UNI_CMD(NAN), &nmi_cmd, + sizeof(nmi_cmd), true); + + return ret; +} + +int mt7925_nan_enable(struct ieee80211_vif *vif, + struct mt792x_dev *dev, + struct cfg80211_nan_conf *conf) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + struct mt76_dev *mdev = &dev->mt76; + struct { + u8 rsv[4]; + struct mt7925_nan_enable_req_tlv nan_req_tlv; + } nan_cmd = { + .rsv = { 0 }, + .nan_req_tlv = { + .tag = cpu_to_le16(NAN_UNI_CMD_ENABLE_REQUEST), + .len = cpu_to_le16(sizeof(struct mt7925_nan_enable_req_tlv)), + .config_random_factor_force = 0, + .random_factor_force_val = 0, + .config_hop_count_force = 0, + .hop_count_force_val = 0, + }, + }; + struct mt7925_nan_enable_req_tlv *p_nan_req_tlv = &nan_cmd.nan_req_tlv; + int ret; + + if (!vif || !dev || !conf) + return -EINVAL; + + p_nan_req_tlv->master_pref = conf->master_pref; + + mt7925_nan_set_2g_support(p_nan_req_tlv, conf); + mt7925_nan_set_5g_channel(dev, p_nan_req_tlv, conf); + mt7925_nan_set_cluster_id(p_nan_req_tlv, conf->cluster_id); + mt7925_nan_set_dw_interval(p_nan_req_tlv, conf); + mt7925_nan_set_disc_beacon(p_nan_req_tlv, conf); + mt7925_nan_set_rssi_thresholds(p_nan_req_tlv, conf); + mt7925_nan_set_scan_params(p_nan_req_tlv, conf); + + mt7925_nan_update_conf(mvif, conf); + + ret = mt76_mcu_send_msg(mdev, MCU_UNI_CMD(NAN), &nan_cmd, sizeof(nan_cmd), true); + + return ret; +} + +int mt7925_nan_disable(struct ieee80211_vif *vif, struct mt792x_dev *dev) +{ + struct mt76_dev *mdev = &dev->mt76; + struct { + u8 rsv[4]; + struct tlv nan_dis_tlv; + } nan_cmd = { + .rsv = { 0 }, + .nan_dis_tlv = { + .tag = cpu_to_le16(NAN_UNI_CMD_DISABLE_REQUEST), + .len = cpu_to_le16(sizeof(struct tlv)), + }, + }; + + if (!dev) + return -EINVAL; + + return mt76_mcu_send_msg(mdev, MCU_UNI_CMD(NAN), &nan_cmd, sizeof(nan_cmd), true); +} + +static int +mt7925_nan_mp_tlv(struct sk_buff *skb, u8 master_pref) +{ + struct mt7925_nan_master_preference_tlv *mp_tlv = NULL; + struct tlv *tlv = NULL; + + if (!skb) + return -EINVAL; + + tlv = mt76_connac_mcu_add_tlv(skb, NAN_UNI_CMD_SET_MASTER_PREFERENCE, + sizeof(struct mt7925_nan_master_preference_tlv)); + if (!tlv) + return -ENOMEM; + + mp_tlv = (struct mt7925_nan_master_preference_tlv *)tlv; + + if (master_pref > NAN_MAX_MASTER_PREFERENCE) + return 0; + + mp_tlv->master_preference = master_pref; + + return 0; +} + +static int +mt7925_nan_dw_tlv(struct sk_buff *skb, struct cfg80211_nan_conf *conf) +{ + struct mt7925_nan_dw_interval_tlv *dw_tlv = NULL; + struct tlv *tlv = NULL; + u16 interval; + + if (!skb || !conf) + return -EINVAL; + + tlv = mt76_connac_mcu_add_tlv(skb, NAN_UNI_CMD_SET_DW_INTERVAL, + sizeof(struct mt7925_nan_dw_interval_tlv)); + + if (!tlv) + return -ENOMEM; + + dw_tlv = (struct mt7925_nan_dw_interval_tlv *)tlv; + + /* Set DW interval for 2.4GHz and 5GHz bands if available */ + if (conf->band_cfgs[NL80211_BAND_2GHZ].awake_dw_interval > 0) { + dw_tlv->dw_interval = conf->band_cfgs[NL80211_BAND_2GHZ].awake_dw_interval; + } else if (conf->band_cfgs[NL80211_BAND_5GHZ].awake_dw_interval > 0) { + dw_tlv->dw_interval = conf->band_cfgs[NL80211_BAND_5GHZ].awake_dw_interval; + } else { + /* Fallback to a default value or log a warning */ + dw_tlv->dw_interval = NAN_DEFAULT_DW_INTERVAL; + } + + /* Validate and set NAN Discovery Beacon Interval */ + interval = conf->discovery_beacon_interval > 0 ? + conf->discovery_beacon_interval : + NAN_DEFAULT_DISC_BCN_INTERVAL; + + dw_tlv->disc_bcn_interval = cpu_to_le16(interval); + + return 0; +} + +static int +mt7925_nan_cluster_id_tlv(struct sk_buff *skb, const u8 *cluster_id) +{ + struct mt7925_nan_cluster_id_tlv *cluster_tlv = NULL; + struct tlv *tlv = NULL; + + if (!skb || !cluster_id) + return -EINVAL; + + tlv = mt76_connac_mcu_add_tlv(skb, NAN_UNI_CMD_SET_CLUSTER_ID, + sizeof(struct mt7925_nan_cluster_id_tlv)); + + if (!tlv) + return -ENOMEM; + + cluster_tlv = (struct mt7925_nan_cluster_id_tlv *)tlv; + + memcpy(cluster_tlv->cluster_id, cluster_id, ETH_ALEN); + + return 0; +} + +static int +mt7925_nan_sync_rssi_tlv(struct sk_buff *skb, struct cfg80211_nan_conf *conf) +{ + struct mt7925_nan_sync_rssi_tlv *rssi_tlv = NULL; + struct tlv *tlv = NULL; + + if (!skb || !conf) + return -EINVAL; + + tlv = mt76_connac_mcu_add_tlv(skb, NAN_UNI_CMD_SET_SYNC_RSSI, + sizeof(struct mt7925_nan_sync_rssi_tlv)); + + if (!tlv) + return -ENOMEM; + + rssi_tlv = (struct mt7925_nan_sync_rssi_tlv *)tlv; + + if (conf->band_cfgs[NL80211_BAND_2GHZ].chan) { + rssi_tlv->rssi_close_2g = + conf->band_cfgs[NL80211_BAND_2GHZ].rssi_close; + rssi_tlv->rssi_middle_2g = + conf->band_cfgs[NL80211_BAND_2GHZ].rssi_middle; + } + + if (conf->band_cfgs[NL80211_BAND_5GHZ].chan) { + rssi_tlv->rssi_close_5g = + conf->band_cfgs[NL80211_BAND_5GHZ].rssi_close; + rssi_tlv->rssi_middle_5g = + conf->band_cfgs[NL80211_BAND_5GHZ].rssi_middle; + } + + return 0; +} + +int mt7925_nan_change_configure(struct ieee80211_vif *vif, + struct mt792x_dev *dev, + struct cfg80211_nan_conf *conf) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + struct mt7925_nan_common_hdr *hdr = NULL; + struct mt76_dev *mdev = &dev->mt76; + struct sk_buff *skb = NULL; + + if (!vif || !dev || !conf) + return -EINVAL; + + skb = mt76_mcu_msg_alloc(mdev, NULL, MT7925_NAN_CONF_MAX_SIZE); + if (!skb) + return -ENOMEM; + + hdr = (struct mt7925_nan_common_hdr *)skb_put(skb, sizeof(*hdr)); + memset(hdr, 0, sizeof(*hdr)); + + if (mt7925_nan_mp_tlv(skb, conf->master_pref) || + mt7925_nan_dw_tlv(skb, conf) || + mt7925_nan_cluster_id_tlv(skb, conf->cluster_id) || + mt7925_nan_sync_rssi_tlv(skb, conf)) { + dev_kfree_skb(skb); + return -ENOMEM; + } + + mt7925_nan_update_conf(mvif, conf); + + return mt76_mcu_skb_send_msg(mdev, skb, + MCU_UNI_CMD(NAN), true); +} + +static void +mt7925_nan_handle_dw_ind(struct mt792x_dev *dev, struct tlv *tlv) +{ + struct ieee80211_channel *chan; + struct nan_rpt_dw_evt *evt; + struct wireless_dev *wdev; + u16 len, channel, dw_num; + struct mt792x_vif *mvif; + enum nl80211_band band; + int freq; + + if (!dev || !tlv) + return; + + len = le16_to_cpu(tlv->len); + if (len < sizeof(*tlv) + sizeof(*evt)) { + dev_warn(dev->mt76.dev, + "nan: short dw event tlv len=%u\n", len); + return; + } + + if (!dev->nan_vif || !ieee80211_vif_nan_started(dev->nan_vif)) + return; + + wdev = ieee80211_vif_to_wdev(dev->nan_vif); + if (!wdev) + return; + + mvif = (struct mt792x_vif *)dev->nan_vif->drv_priv; + if (!mvif->nan.conf.enable_dw_notification) + return; + + evt = (struct nan_rpt_dw_evt *)tlv->data; + channel = le16_to_cpu(evt->channel); + dw_num = le16_to_cpu(evt->dw_num); + + band = channel > 13 ? NL80211_BAND_5GHZ : NL80211_BAND_2GHZ; + freq = ieee80211_channel_to_frequency(channel, band); + chan = ieee80211_get_channel(dev->mt76.hw->wiphy, freq); + if (!chan) { + dev_dbg(dev->mt76.dev, + "nan: no channel for dw end event ch=%u dw=%u\n", + channel, dw_num); + return; + } + + cfg80211_next_nan_dw_notif(wdev, chan, GFP_KERNEL); +} + +static void +mt7925_nan_mcu_handle_de_event(struct mt792x_dev *dev, struct tlv *tlv) +{ + u8 cluster_id[ETH_ALEN] __aligned(2) = {0x50, 0x6f, 0x9a, 0x01, 0x00, 0x00}; + struct mt7925_nan_de_event *de_evt = NULL; + u16 len; + + if (!dev || !tlv) { + if (dev) + dev_warn(dev->mt76.dev, "nan: failed to parse TLV\n"); + return; + } + + len = le16_to_cpu(tlv->len); + if (len < sizeof(*tlv) + sizeof(*de_evt)) { + dev_warn(dev->mt76.dev, + "nan: short de_event tlv len=%u\n", len); + return; + } + + de_evt = (struct mt7925_nan_de_event *)tlv->data; + if (!de_evt) { + dev_warn(dev->mt76.dev, "nan: missing DE event payload\n"); + return; + } + + if (de_evt->event_type == NAN_EVENT_ID_DISC_MAC_ADDR) + return; + + memcpy(cluster_id, de_evt->cluster_id, ETH_ALEN); + + dev_dbg(dev->mt76.dev, "nan: evt=%u cluster=%pM\n", + de_evt->event_type, de_evt->cluster_id); + + if (de_evt->event_type != NAN_EVENT_ID_JOINED_CLUSTER) + return; + + if (!dev->nan_vif || !ieee80211_vif_nan_started(dev->nan_vif)) { + dev_warn(dev->mt76.dev, "nan: joined-cluster event but NAN not started\n"); + return; + } + + dev_dbg(dev->mt76.dev, "nan: anchor_master_rank=%*phN\n", + NAN_ANCHOR_MASTER_RANK_NUM, de_evt->anchor_master_rank); + + dev_dbg(dev->mt76.dev, "nan: own_nmi=%pM master_nmi=%pM\n", + de_evt->own_nmi, de_evt->master_nmi); + + ieee80211_nan_cluster_joined(dev->nan_vif, cluster_id, true, GFP_KERNEL); +} + +void mt7925_nan_mcu_event(struct mt792x_dev *dev, struct sk_buff *skb) +{ + struct tlv *tlv; + u32 tlv_len; + + if (!dev || !skb) + return; + + if (skb->len < sizeof(struct mt7925_mcu_rxd) + 4) + return; + + skb_pull(skb, sizeof(struct mt7925_mcu_rxd) + 4); + tlv = (struct tlv *)skb->data; + tlv_len = skb->len; + + while (tlv_len >= sizeof(*tlv)) { + u16 len = le16_to_cpu(tlv->len); + + if (len < sizeof(*tlv) || len > tlv_len) + break; + + switch (le16_to_cpu(tlv->tag)) { + case NAN_UNI_EVENT_ID_DE_EVENT_IND: + mt7925_nan_mcu_handle_de_event(dev, tlv); + break; + case NAN_UNI_EVENT_REPORT_DW_END: + mt7925_nan_handle_dw_ind(dev, tlv); + break; + default: + break; + } + + tlv_len -= len; + tlv = (struct tlv *)((u8 *)tlv + len); + } +} + +static int mt7925_nan_avail_ctrl_tlv(struct sk_buff *skb, + struct ieee80211_vif *vif) +{ + struct mt7925_nan_avail_ctrl_tlv *avail_ctrl_tlv; + struct ieee80211_nan_sched_cfg *sched; + struct tlv *tlv; + u8 seq_id = 0; + u16 ctrl = 0; + + if (!skb || !vif) + return -EINVAL; + + tlv = mt76_connac_mcu_add_tlv(skb, NAN_UNI_CMD_UPDATE_AVAILABILITY_CTRL, + sizeof(struct mt7925_nan_avail_ctrl_tlv)); + + if (!tlv) + return -ENOMEM; + + sched = &vif->cfg.nan_sched; + + ctrl = mt7925_nan_avail_attr_ctrl(sched); + if (sched->avail_blob_len >= NAN_AVAIL_ATTR_CTRL_OFFSET + 2) + seq_id = sched->avail_blob[NAN_AVAIL_SEQ_ID_OFFSET]; + + avail_ctrl_tlv = (struct mt7925_nan_avail_ctrl_tlv *)tlv; + avail_ctrl_tlv->avail_ctrl = + cpu_to_le16(ctrl & NAN_AVAIL_CTRL_CHECK_FOR_CHANGED); + avail_ctrl_tlv->seq_id = seq_id; + + return 0; +} + +static u32 mt7925_nan_slot_to_bitmap(struct ieee80211_vif *vif, + struct mt7925_nan_ch_timeline *ch_list) +{ + struct ieee80211_nan_channel **slots = vif->cfg.nan_sched.schedule; + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + u32 num_channels = 0; + u32 i, j; + + for (i = 0; i < ARRAY_SIZE(mvif->nan.local_sched); i++) { + struct cfg80211_chan_def *slot_chan = &mvif->nan.local_sched[i]; + struct ieee80211_nan_channel *slot = slots[i]; + bool is_found = false; + + if (slot && !IS_ERR(slot) && slot->chanctx_conf) { + *slot_chan = slot->chanctx_conf->def; + } else { + memset(slot_chan, 0, sizeof(*slot_chan)); + continue; + } + + for (j = 0; j < num_channels; j++) { + u32 raw = le32_to_cpu(ch_list[j].ch_info); + + if (FIELD_GET(NAN_CH_CTRL_PRIMARY_CH, raw) == + slot_chan->chan->hw_value) { + u32 map = le32_to_cpu(ch_list[j].avail_map[0]); + + ch_list[j].avail_map[0] = cpu_to_le32(map | BIT(i)); + le32_add_cpu(&ch_list[j].num, 1); + is_found = true; + break; + } + } + + if (!is_found && num_channels < NAN_TIMELINE_MGMT_CHNL_LIST_NUM) { + ch_list[num_channels].ch_info = + cpu_to_le32(FIELD_PREP(NAN_CH_CTRL_OP_CLASS, + slot->channel_entry[0]) | + FIELD_PREP(NAN_CH_CTRL_PRIMARY_CH, + slot_chan->chan->hw_value)); + ch_list[num_channels].avail_map[0] = cpu_to_le32(BIT(i)); + le32_add_cpu(&ch_list[num_channels].num, 1); + ch_list[num_channels].is_valid++; + num_channels++; + } + } + + return num_channels; +} + +static int mt7925_nan_avail_tlv(struct sk_buff *skb, + struct ieee80211_vif *vif) +{ + struct mt7925_nan_avail_entry_tlv *avail_tlv; + struct ieee80211_nan_sched_cfg *sched; + struct tlv *tlv; + u16 ctrl = 0; + + if (!skb || !vif) + return -EINVAL; + + tlv = mt76_connac_mcu_add_tlv(skb, NAN_UNI_CMD_UPDATE_AVAILABILITY, + sizeof(struct mt7925_nan_avail_entry_tlv)); + + if (!tlv) + return -ENOMEM; + + sched = &vif->cfg.nan_sched; + + ctrl = mt7925_nan_avail_attr_ctrl(sched); + + avail_tlv = (struct mt7925_nan_avail_entry_tlv *)tlv; + avail_tlv->map_id = ctrl & NAN_AVAIL_CTRL_MAPID; + avail_tlv->is_cond_avail = false; + avail_tlv->timeline_idx = 0; + + mt7925_nan_slot_to_bitmap(vif, avail_tlv->ch_list); + + avail_tlv->is_multi_map = false; + + return 0; +} + +void mt7925_nan_local_sched_changed(struct mt792x_dev *dev, + struct ieee80211_vif *vif) +{ + struct mt7925_nan_common_hdr *hdr; + struct mt76_dev *mdev; + bool deferred; + struct sk_buff *skb; + int ret = -ENOMEM; + + if (!dev || !vif) + return; + + mdev = &dev->mt76; + deferred = vif->cfg.nan_sched.deferred; + + mt792x_mutex_acquire(dev); + + skb = mt76_mcu_msg_alloc(mdev, NULL, MT7925_NAN_AVAIL_MAX_SIZE); + if (!skb) + goto out; + + hdr = (struct mt7925_nan_common_hdr *)skb_put(skb, sizeof(*hdr)); + memset(hdr, 0, sizeof(*hdr)); + + if (mt7925_nan_avail_ctrl_tlv(skb, vif) || + mt7925_nan_avail_tlv(skb, vif)) { + dev_kfree_skb(skb); + goto out; + } + + ret = mt76_mcu_skb_send_msg(mdev, skb, + MCU_UNI_CMD(NAN), true); +out: + mt792x_mutex_release(dev); + + if (deferred) { + if (ret) + dev_err(mdev->dev, + "NAN: local schedule update failed: %d\n", + ret); + + ieee80211_nan_sched_update_done(vif); + } +} + +static int mt7925_nan_peer_rec_tlv(struct sk_buff *skb, + struct ieee80211_sta *sta, + struct mt792x_sta *msta, + u8 is_activate) +{ + struct mt7925_nan_sched_manage_peer_rec_tlv *peer_rec_tlv; + struct tlv *tlv; + + if (!skb || !sta || !msta) + return -EINVAL; + + tlv = mt76_connac_mcu_add_tlv(skb, NAN_UNI_CMD_MANAGE_PEER_SCH_RECORD, + sizeof(struct mt7925_nan_sched_manage_peer_rec_tlv)); + + if (!tlv) + return -ENOMEM; + + peer_rec_tlv = (struct mt7925_nan_sched_manage_peer_rec_tlv *)tlv; + peer_rec_tlv->sch_idx = cpu_to_le32(msta->nan_sched.sch_idx); + peer_rec_tlv->is_activate = is_activate; + memcpy(peer_rec_tlv->nmi_addr, sta->addr, ETH_ALEN); + + return 0; +} + +static u8 mt7925_nan_get_supported_bands(struct mt792x_vif *mvif) +{ + struct wiphy *wiphy; + u8 bands = 0; + + if (!mvif || !mvif->phy) + return BIT(NAN_SUPPORTED_BAND_ID_2P4G); + + wiphy = mvif->phy->mt76->hw->wiphy; + if (wiphy->nan_supported_bands & BIT(NL80211_BAND_2GHZ)) + bands |= BIT(NAN_SUPPORTED_BAND_ID_2P4G); + if (wiphy->nan_supported_bands & BIT(NL80211_BAND_5GHZ)) + bands |= BIT(NAN_SUPPORTED_BAND_ID_5G); + + return bands ?: BIT(NAN_SUPPORTED_BAND_ID_2P4G); +} + +static int mt7925_nan_peer_cap_tlv(struct sk_buff *skb, + struct ieee80211_sta *sta, + struct mt792x_sta *msta) +{ + struct mt7925_nan_sched_update_peer_cap_tlv *peer_cap_tlv; + struct ieee80211_nan_peer_sched *sched; + enum nl80211_band band; + struct tlv *tlv; + u16 primary_ch; + u32 i; + + if (!skb || !sta || !msta) + return -EINVAL; + + sched = sta->nan_sched; + if (!sched) + return -EINVAL; + + tlv = mt76_connac_mcu_add_tlv(skb, NAN_UNI_CMD_UPDATE_PEER_CAPABILITY, + sizeof(struct mt7925_nan_sched_update_peer_cap_tlv)); + + if (!tlv) + return -ENOMEM; + + peer_cap_tlv = (struct mt7925_nan_sched_update_peer_cap_tlv *)tlv; + peer_cap_tlv->sch_idx = cpu_to_le32(msta->nan_sched.sch_idx); + peer_cap_tlv->supported_bands = + mt7925_nan_get_supported_bands(msta->vif); + peer_cap_tlv->max_chnl_switch_time = cpu_to_le16(sched->max_chan_switch); + + for (i = 0; i < sched->n_channels; i++) { + if (!sched->channels[i].chanctx_conf) + continue; + + band = sched->channels[i].chanctx_conf->def.chan->band; + primary_ch = + sched->channels[i].chanctx_conf->def.chan->hw_value; + + if (band == NL80211_BAND_2GHZ) + peer_cap_tlv->peer_supported_bands |= + BIT(NAN_SUPPORTED_BN_2G); + else if (primary_ch >= UNII1_LOWER_BOUND && + primary_ch <= UNII1_UPPER_BOUND) + peer_cap_tlv->peer_supported_bands |= + BIT(NAN_SUPPORTED_BN_5G_LOW); + else if (primary_ch >= UNII3_LOWER_BOUND && + primary_ch <= UNII3_UPPER_BOUND) + peer_cap_tlv->peer_supported_bands |= + BIT(NAN_SUPPORTED_BN_5G_HIGH); + } + + return 0; +} + +static void +mt7925_nan_fill_crb_committed(struct mt7925_nan_sched_update_crb_tlv *crb_tlv, + struct ieee80211_vif *vif, + struct ieee80211_nan_peer_sched *sched) +{ + struct ieee80211_nan_sched_cfg *local_sched; + u8 local_map_id; + u32 m, slot; + + if (!vif || !sched) + return; + + local_sched = &vif->cfg.nan_sched; + local_map_id = mt7925_nan_avail_attr_ctrl(local_sched) & + NAN_AVAIL_CTRL_MAPID; + + for (m = 0; m < CFG80211_NAN_MAX_PEER_MAPS && + m < NAN_TIMELINE_MGMT_SIZE; m++) { + struct mt7925_nan_sched_timeline *tl = + &crb_tlv->comm_faw_timeline[m]; + struct ieee80211_nan_peer_map *map = &sched->maps[m]; + u32 avail_map = 0; + + if (map->map_id == CFG80211_NAN_INVALID_MAP_ID) + continue; + + tl->map_id = map->map_id; + tl->local_map_id = local_map_id; + + for (slot = 0; slot < CFG80211_NAN_SCHED_NUM_TIME_SLOTS; + slot++) { + struct ieee80211_nan_channel *local_ch; + struct ieee80211_nan_channel *peer_ch; + + local_ch = local_sched->schedule[slot]; + peer_ch = map->slots[slot]; + + if (!local_ch || !local_ch->chanctx_conf || + !peer_ch || !peer_ch->chanctx_conf) + continue; + + if (local_ch->chanctx_conf != peer_ch->chanctx_conf) + continue; + + avail_map |= BIT(slot); + } + + tl->avail_map[0] = cpu_to_le32(avail_map); + } +} + +static int mt7925_nan_update_crb_tlv(struct sk_buff *skb, + struct ieee80211_sta *sta, + struct mt792x_sta *msta) +{ + struct mt7925_nan_sched_update_crb_tlv *crb_tlv; + struct tlv *tlv; + + if (!skb || !sta || !msta) + return -EINVAL; + + tlv = mt76_connac_mcu_add_tlv(skb, NAN_UNI_CMD_UPDATE_CRB, + sizeof(struct mt7925_nan_sched_update_crb_tlv)); + + if (!tlv) + return -ENOMEM; + + crb_tlv = (struct mt7925_nan_sched_update_crb_tlv *)tlv; + crb_tlv->sch_idx = cpu_to_le32(msta->nan_sched.sch_idx); + crb_tlv->flags = NAN_CRB_USE_DATA_PATH; + crb_tlv->is_use_ranging = false; + crb_tlv->comm_ndc_ctrl.is_valid = false; + + mt7925_nan_fill_crb_committed(crb_tlv, msta->vif->phy->dev->nan_vif, + sta->nan_sched); + + return 0; +} + +int mt792x_nan_set_peer_schedule(struct mt792x_dev *dev, + struct ieee80211_sta *sta) +{ + struct mt7925_nan_common_hdr *hdr; + bool idx_allocated = false; + struct mt792x_sta *msta; + struct mt792x_nan *nan; + struct mt76_dev *mdev; + struct sk_buff *skb; + int ret; + + if (!dev || !sta) + return -EINVAL; + + mdev = &dev->mt76; + + skb = mt76_mcu_msg_alloc(mdev, NULL, MT7925_NAN_PEER_MAX_SIZE); + if (!skb) + return -ENOMEM; + + hdr = (struct mt7925_nan_common_hdr *)skb_put(skb, sizeof(*hdr)); + memset(hdr, 0, sizeof(*hdr)); + + msta = (struct mt792x_sta *)sta->drv_priv; + nan = &msta->vif->nan; + + /* Allocate connection index on first call for this peer */ + if (!msta->nan_sched.idx_assigned) { + int idx = find_first_zero_bit(&nan->conn_bitmap, + NAN_MAX_CONN_CFG); + if (idx >= NAN_MAX_CONN_CFG) { + dev_kfree_skb(skb); + return -ENOSPC; + } + + set_bit(idx, &nan->conn_bitmap); + msta->nan_sched.sch_idx = idx; + msta->nan_sched.idx_assigned = true; + idx_allocated = true; + + if (mt7925_nan_peer_rec_tlv(skb, sta, msta, true) || + mt7925_nan_peer_cap_tlv(skb, sta, msta)) { + ret = -ENOMEM; + goto free_skb; + } + } + + if (mt7925_nan_update_crb_tlv(skb, sta, msta)) { + ret = -ENOMEM; + goto free_skb; + } + + ret = mt76_mcu_skb_send_msg(mdev, skb, MCU_UNI_CMD(NAN), true); + if (ret && idx_allocated) + goto clear_idx; + + return ret; + +free_skb: + dev_kfree_skb(skb); + if (!idx_allocated) + return ret; + +clear_idx: + clear_bit(msta->nan_sched.sch_idx, &nan->conn_bitmap); + msta->nan_sched.idx_assigned = false; + + return ret; +} + +int mt792x_nan_set_peer_rec(struct mt76_dev *mdev, + struct ieee80211_sta *sta) +{ + struct mt7925_nan_common_hdr *hdr; + struct mt792x_sta *msta; + struct mt792x_nan *nan; + struct sk_buff *skb; + int ret; + + if (!mdev || !sta) + return -EINVAL; + + skb = mt76_mcu_msg_alloc(mdev, NULL, + sizeof(struct mt7925_nan_common_hdr) + + sizeof(struct mt7925_nan_sched_manage_peer_rec_tlv)); + if (!skb) + return -ENOMEM; + + hdr = (struct mt7925_nan_common_hdr *)skb_put(skb, sizeof(*hdr)); + memset(hdr, 0, sizeof(*hdr)); + + msta = (struct mt792x_sta *)sta->drv_priv; + nan = &msta->vif->nan; + + if (!msta->nan_sched.idx_assigned) { + dev_kfree_skb(skb); + return 0; + } + + if (mt7925_nan_peer_rec_tlv(skb, sta, msta, false)) { + dev_kfree_skb(skb); + return -ENOMEM; + } + + ret = mt76_mcu_skb_send_msg(mdev, skb, MCU_UNI_CMD(NAN), true); + if (ret) + return ret; + + clear_bit(msta->nan_sched.sch_idx, &nan->conn_bitmap); + msta->nan_sched.idx_assigned = false; + + return 0; +} + +int mt792x_nan_map_sta_rec(struct mt76_dev *mdev, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mt7925_nan_sched_map_sta_rec_tlv *map_tlv; + struct mt7925_nan_common_hdr *hdr; + struct ieee80211_sta *nmi_sta; + struct mt792x_sta *nmi_msta; + struct mt792x_vif *mvif; + struct mt792x_sta *msta; + u8 nmi_addr[ETH_ALEN]; + struct sk_buff *skb; + int ndp_ctx_id = 0; + int ret = -ENOMEM; + struct mt792x_dev *dev; + struct tlv *tlv; + + if (!mdev || !vif || !sta) + return -EINVAL; + + dev = container_of(mdev, struct mt792x_dev, mt76); + msta = (struct mt792x_sta *)sta->drv_priv; + mvif = (struct mt792x_vif *)vif->drv_priv; + + rcu_read_lock(); + nmi_sta = rcu_dereference(sta->nmi); + if (!nmi_sta) { + rcu_read_unlock(); + dev_err(mdev->dev, "NAN: NMI sta not found for NDI sta %pM\n", + sta->addr); + return -EINVAL; + } + + memcpy(nmi_addr, nmi_sta->addr, ETH_ALEN); + nmi_msta = (struct mt792x_sta *)nmi_sta->drv_priv; + + if (!nmi_msta->nan_sched.idx_assigned) { + if (!nmi_sta->nan_sched) { + rcu_read_unlock(); + dev_err(mdev->dev, + "NAN: peer schedule missing for NDI sta %pM\n", + sta->addr); + return -EAGAIN; + } + + rcu_read_unlock(); + ret = mt792x_nan_set_peer_schedule(dev, nmi_sta); + if (ret) + return ret; + + rcu_read_lock(); + nmi_sta = rcu_dereference(sta->nmi); + if (!nmi_sta) { + rcu_read_unlock(); + dev_err(mdev->dev, + "NAN: NMI sta not found for NDI sta %pM\n", + sta->addr); + return -EINVAL; + } + + nmi_msta = (struct mt792x_sta *)nmi_sta->drv_priv; + } + + ndp_ctx_id = find_first_zero_bit(&nmi_msta->nan_sched.ndp_ctx_bitmap, + NAN_MAX_NDP_CXT); + if (ndp_ctx_id >= NAN_MAX_NDP_CXT) { + rcu_read_unlock(); + return -ENOSPC; + } + + set_bit(ndp_ctx_id, &nmi_msta->nan_sched.ndp_ctx_bitmap); + rcu_read_unlock(); + + msta->nan_sched.ndp_ctx_id = ndp_ctx_id; + msta->nan_sched.ndp_ctx_assigned = true; + + skb = mt76_mcu_msg_alloc(mdev, NULL, + sizeof(struct mt7925_nan_common_hdr) + + sizeof(struct mt7925_nan_sched_map_sta_rec_tlv)); + if (!skb) + goto clear_ndp_ctx; + + hdr = (struct mt7925_nan_common_hdr *)skb_put(skb, sizeof(*hdr)); + memset(hdr, 0, sizeof(*hdr)); + + tlv = mt76_connac_mcu_add_tlv(skb, NAN_UNI_CMD_MAP_STA_RECORD, + sizeof(struct mt7925_nan_sched_map_sta_rec_tlv)); + if (!tlv) { + dev_kfree_skb(skb); + ret = -ENOMEM; + goto clear_ndp_ctx; + } + + map_tlv = (struct mt7925_nan_sched_map_sta_rec_tlv *)tlv; + memcpy(map_tlv->nmi_addr, nmi_addr, ETH_ALEN); + map_tlv->sta_rec_idx = msta->deflink.wcid.idx; + map_tlv->ndp_ctx_id = ndp_ctx_id; + map_tlv->role_idx = cpu_to_le32(mvif->bss_conf.mt76.idx); + memcpy(map_tlv->ndi_addr, vif->addr, ETH_ALEN); + + ret = mt76_mcu_skb_send_msg(mdev, skb, + MCU_UNI_CMD(NAN), true); + if (ret) + goto clear_ndp_ctx; + + return 0; + +clear_ndp_ctx: + rcu_read_lock(); + nmi_sta = rcu_dereference(sta->nmi); + if (nmi_sta) { + nmi_msta = (struct mt792x_sta *)nmi_sta->drv_priv; + clear_bit(msta->nan_sched.ndp_ctx_id, + &nmi_msta->nan_sched.ndp_ctx_bitmap); + } + rcu_read_unlock(); + msta->nan_sched.ndp_ctx_assigned = false; + + return ret ?: -ENOMEM; +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/nan.h b/drivers/net/wireless/mediatek/mt76/mt7925/nan.h new file mode 100644 index 000000000..f55730e25 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7925/nan.h @@ -0,0 +1,421 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* Copyright (C) 2025-2026 MediaTek Inc. */ + +#ifndef __MT7925_NAN_H +#define __MT7925_NAN_H + +#include +#include + +#include "../mt76_connac_mcu.h" + +#define NAN_MAX_SOCIAL_CHANNELS 3 +#define NAN_ANCHOR_MASTER_RANK_NUM 8 +#define NAN_5G_LOW_DISC_CHANNEL 44 +#define NAN_5G_HIGH_DISC_CHANNEL 149 +#define NAN_MAX_MASTER_PREFERENCE 255 +#define NAN_DEFAULT_DW_INTERVAL 1 +#define NAN_DEFAULT_DISC_BCN_INTERVAL 100 +#define NAN_TOTAL_DW 16 +#define NAN_SUPPORTED_2G_FAW_CH_NUM 4 +#define NAN_SUPPORTED_5G_FAW_CH_NUM 4 +#define NAN_TIMELINE_MGMT_SIZE 2 +#define NAN_TIMELINE_MGMT_CHNL_LIST_NUM \ + ((NAN_SUPPORTED_2G_FAW_CH_NUM + \ + NAN_SUPPORTED_5G_FAW_CH_NUM) / NAN_TIMELINE_MGMT_SIZE) +#define NAN_NUM_AVAIL_DB 2 +#define NAN_NDC_ATTRIBUTE_ID_LENGTH 6 +#define NAN_MAX_CONN_CFG 8 +#define NAN_MAX_NDP_CXT 4 + +#define MT7925_NAN_CONF_MAX_SIZE \ + (sizeof(struct mt7925_nan_common_hdr) + \ + sizeof(struct mt7925_nan_master_preference_tlv) + \ + sizeof(struct mt7925_nan_dw_interval_tlv) + \ + sizeof(struct mt7925_nan_cluster_id_tlv) + \ + sizeof(struct mt7925_nan_sync_rssi_tlv)) + +#define MT7925_NAN_AVAIL_MAX_SIZE \ + (sizeof(struct mt7925_nan_common_hdr) + \ + sizeof(struct mt7925_nan_avail_ctrl_tlv) + \ + sizeof(struct mt7925_nan_avail_entry_tlv)) + +#define MT7925_NAN_PEER_MAX_SIZE \ + (sizeof(struct mt7925_nan_common_hdr) + \ + sizeof(struct mt7925_nan_sched_manage_peer_rec_tlv) + \ + sizeof(struct mt7925_nan_sched_update_peer_cap_tlv) + \ + sizeof(struct mt7925_nan_sched_update_crb_tlv)) + +/* NAN Availability Attribute */ +#define NAN_AVAIL_ATTR_ID_OFFSET 0 +#define NAN_AVAIL_ATTR_LEN_OFFSET 1 +#define NAN_AVAIL_SEQ_ID_OFFSET 3 +#define NAN_AVAIL_ATTR_CTRL_OFFSET 4 + +/* NAN Availability Attribute - Attribute Control Field */ +#define NAN_AVAIL_CTRL_MAPID GENMASK(3, 0) +#define NAN_AVAIL_CTRL_COMMIT_CHANGED BIT(4) +#define NAN_AVAIL_CTRL_POTN_CHANGED BIT(5) +#define NAN_AVAIL_CTRL_PUBLIC_AVAIL_CHANGED BIT(6) +#define NAN_AVAIL_CTRL_NDC_CHANGED BIT(7) +#define NAN_AVAIL_CTRL_CHECK_FOR_CHANGED GENMASK(7, 4) + +#define UNII1_LOWER_BOUND 36 +#define UNII1_UPPER_BOUND 50 +#define UNII3_LOWER_BOUND 149 +#define UNII3_UPPER_BOUND 165 + +enum nan_uni_cmd_tag { + NAN_UNI_CMD_SET_MASTER_PREFERENCE = 0, + NAN_UNI_CMD_ENABLE_REQUEST = 7, + NAN_UNI_CMD_DISABLE_REQUEST = 8, + NAN_UNI_CMD_UPDATE_AVAILABILITY = 9, + NAN_UNI_CMD_UPDATE_CRB = 10, + NAN_UNI_CMD_MANAGE_PEER_SCH_RECORD = 12, + NAN_UNI_CMD_MAP_STA_RECORD = 13, + NAN_UNI_CMD_UPDATE_AVAILABILITY_CTRL = 20, + NAN_UNI_CMD_UPDATE_PEER_CAPABILITY = 21, + NAN_UNI_CMD_CHANGE_NMI_ADDRESS = 24, + NAN_UNI_CMD_SET_DW_INTERVAL = 26, + NAN_UNI_CMD_SET_SYNC_RSSI = 39, + NAN_UNI_CMD_SET_CLUSTER_ID = 40, + NAN_UNI_CMD_KEY_MANAGEMENT = 53, +}; + +enum nan_uni_event_tag { + NAN_UNI_EVENT_ID_DE_EVENT_IND = 19, + NAN_UNI_EVENT_REPORT_DW_END = 60, +}; + +enum nan_disc_event_type { + NAN_EVENT_ID_DISC_MAC_ADDR = 0, + NAN_EVENT_ID_JOINED_CLUSTER = 2, +}; + +/* NAN 4.0 Table 79. Device Capability attribute format, Supported Bands */ +enum nan_supported_bands { + NAN_SUPPORTED_BAND_ID_2P4G = 2, + NAN_SUPPORTED_BAND_ID_5G = 4, + NAN_PROPRIETARY_BAND_ID_6G = 6, + NAN_SUPPORTED_BAND_ID_6G = 7, +}; + +enum nan_peer_supported_bands { + NAN_SUPPORTED_BN_2G = 0, + NAN_SUPPORTED_BN_5G_LOW, + NAN_SUPPORTED_BN_5G_HIGH, + NAN_SUPPORTED_BN_6G, + NAN_SUPPORTED_BN_NUM +}; + +#define NAN_CH_CTRL_OP_CLASS GENMASK(15, 8) +#define NAN_CH_CTRL_PRIMARY_CH GENMASK(23, 16) + +#define NAN_CRB_USE_DATA_PATH BIT(0) +#define NAN_CRB_AVAIL_6G_FORMAT GENMASK(2, 1) + +struct mt7925_nan_social_ch_scan_params { + u8 dwell_time[NAN_MAX_SOCIAL_CHANNELS]; + __le16 scan_period[NAN_MAX_SOCIAL_CHANNELS]; +} __packed; + +/* Firmware-reported NAN device information */ +struct nan_dev_info_evt { + u8 is_enabled; + u8 my_addr[ETH_ALEN]; + u8 en_fw_election; + __le32 nan_dev_role; + __le32 nan_dev_state; + u8 mst_preference; + u8 random_factor; + u8 cnt_hop; + u8 cluster_id[ETH_ALEN]; + u8 anchor_mst_addr[ETH_ALEN]; + u8 am_preference; + u8 am_random_factor; + u8 parent_mac[ETH_ALEN]; + u8 parent_am_preference; + u8 parent_am_factor; + __le32 ambtt; + __le32 tsf[2]; + u8 pn_igtk[6]; + u8 pn_bigtk[6]; +}; + +/* Firmware NAN discovery window event */ +struct nan_rpt_dw_evt { + struct nan_dev_info_evt device_info; + __le32 expected_tsf_h; + __le32 expected_tsf_l; + __le32 actual_tsf_h; + __le32 actual_tsf_l; + __le16 channel; + __le16 dw_num; +}; + +struct mt7925_nan_conf_dw { + u8 config_2dot4g_dw_band; + __le32 dw_2dot4g_interval_val; + + u8 config_5g_dw_band; + __le32 dw_5g_interval_val; +} __packed; + +struct mt7925_nan_enable_req_tlv { + __le16 tag; + __le16 len; + + u8 master_pref; + __le16 cluster_low; + __le16 cluster_high; + + u8 config_support_5g; + u8 support_5g_val; + + u8 config_sid_beacon; + u8 sid_beacon_val; + + u8 config_2dot4g_rssi_close; + u8 rssi_close_2dot4g_val; + u8 config_2dot4g_rssi_middle; + u8 rssi_middle_2dot4g_val; + + u8 config_2dot4g_rssi_proximity; + u8 rssi_proximity_2dot4g_val; + u8 config_hop_count_limit; + u8 hop_count_limit_val; + + u8 config_2dot4g_support; + u8 support_2dot4g_val; + + u8 config_2dot4g_beacons; + u8 beacon_2dot4g_val; + + u8 config_2dot4g_sdf; + u8 sdf_2dot4g_val; + + u8 config_5g_beacons; + u8 beacon_5g_val; + + u8 config_5g_sdf; + u8 sdf_5g_val; + + u8 config_5g_rssi_close; + u8 rssi_close_5g_val; + + u8 config_5g_rssi_middle; + u8 rssi_middle_5g_val; + + u8 config_5g_rssi_close_proximity; + u8 rssi_close_proximity_5g_val; + + u8 config_rssi_window_size; + u8 rssi_window_size_val; + + u8 config_oui; + __le32 oui_val; + + u8 config_intf_addr; + u8 intf_addr_val[ETH_ALEN]; + + u8 config_cluster_attribute_val; + + u8 config_scan_params; + struct mt7925_nan_social_ch_scan_params scan_params_val; + + u8 config_random_factor_force; + u8 random_factor_force_val; + + u8 config_hop_count_force; + u8 hop_count_force_val; + + u8 config_24g_channel; + __le32 channel_24g_val; + + u8 config_5g_channel; + __le32 channel_5g_val; + + struct mt7925_nan_conf_dw config_dw; + + u8 config_disc_mac_addr_randomization; + __le32 disc_mac_addr_rand_interval_sec; + + u8 discovery_indication_cfg; + + u8 config_subscribe_sid_beacon; + __le32 subscribe_sid_beacon_val; + + u8 enable_log_slot_statistics; +} __packed __aligned(4); + +struct mt7925_nan_common_hdr { + u8 reserved[4]; +}; + +struct mt7925_nan_master_preference_tlv { + __le16 tag; + __le16 len; + u8 master_preference; + u8 reserved[3]; +} __packed __aligned(4); + +struct mt7925_nan_dw_interval_tlv { + __le16 tag; + __le16 len; + u8 dw_interval; + u8 vendor_ioctl; + __le16 disc_bcn_interval; +} __packed __aligned(4); + +struct mt7925_nan_cluster_id_tlv { + __le16 tag; + __le16 len; + u8 cluster_id[ETH_ALEN]; + u8 reserved[2]; +} __packed __aligned(4); + +struct mt7925_nan_sync_rssi_tlv { + __le16 tag; + __le16 len; + s8 rssi_close_2g; + s8 rssi_middle_2g; + s8 rssi_close_5g; + s8 rssi_middle_5g; +} __packed __aligned(4); + +struct mt7925_nan_de_event { + u8 event_type; + u8 cluster_id[ETH_ALEN]; + u8 anchor_master_rank[NAN_ANCHOR_MASTER_RANK_NUM]; + u8 own_nmi[ETH_ALEN]; + u8 master_nmi[ETH_ALEN]; +}; + +struct mt7925_nan_nmi_addr_tlv { + __le16 tag; + __le16 len; + u8 nmi_addr[ETH_ALEN]; +} __packed __aligned(4); + +struct mt7925_nan_avail_ctrl_tlv { + __le16 tag; + __le16 len; + __le16 avail_ctrl; + u8 seq_id; + u8 reserved[1]; +} __packed __aligned(4); + +struct mt7925_nan_ch_timeline { + u8 is_valid; + u8 reserved[3]; + + __le32 ch_info; + + __le32 num; + __le32 avail_map[NAN_TOTAL_DW]; +}; + +struct mt7925_nan_avail_entry_tlv { + __le16 tag; + __le16 len; + u8 map_id; + u8 is_cond_avail; + u8 timeline_idx; + u8 is_multi_map; + + struct mt7925_nan_ch_timeline ch_list[NAN_TIMELINE_MGMT_CHNL_LIST_NUM]; +} __packed __aligned(4); + +struct mt7925_nan_sched_manage_peer_rec_tlv { + __le16 tag; + __le16 len; + __le32 sch_idx; + u8 is_activate; + u8 nmi_addr[ETH_ALEN]; + u8 reserved[1]; +} __packed __aligned(4); + +struct mt7925_nan_sched_update_peer_cap_tlv { + __le16 tag; + __le16 len; + __le32 sch_idx; + u8 supported_bands; + __le16 max_chnl_switch_time; + u8 peer_supported_bands; +} __packed __aligned(4); + +struct mt7925_nan_sched_timeline { + u8 map_id; + u8 local_map_id; + u8 reserved[2]; + union { + __le32 avail_map[NAN_TOTAL_DW]; + u8 avail_block[NAN_TOTAL_DW * 4]; + }; +}; + +struct mt7925_nan_sched_faw_ndc_timeline { + __le32 avail_map[NAN_TOTAL_DW]; +}; + +struct mt7925_nan_sched_ndc_ctrl { + u8 is_valid; + u8 ndc_id[NAN_NDC_ATTRIBUTE_ID_LENGTH]; + u8 ndc_idx; + struct mt7925_nan_sched_timeline timeline[NAN_NUM_AVAIL_DB]; +}; + +struct mt7925_nan_sched_update_crb_tlv { + __le16 tag; + __le16 len; + __le32 sch_idx; + u8 flags; + u8 is_use_ranging; + u8 reserved[2]; + struct mt7925_nan_sched_timeline comm_ranging_timeline[NAN_TIMELINE_MGMT_SIZE]; + struct mt7925_nan_sched_timeline comm_faw_timeline[NAN_TIMELINE_MGMT_SIZE]; + struct mt7925_nan_sched_ndc_ctrl comm_ndc_ctrl; + struct mt7925_nan_sched_faw_ndc_timeline faw_ndc_timeline[NAN_TIMELINE_MGMT_SIZE]; +} __packed __aligned(4); + +struct mt7925_nan_sched_map_sta_rec_tlv { + __le16 tag; + __le16 len; + u8 nmi_addr[ETH_ALEN]; + u8 sta_rec_idx; + u8 ndp_ctx_id; + + __le32 role_idx; + u8 ndi_addr[ETH_ALEN]; + u8 reserved[2]; +} __packed __aligned(4); + +int mt7925_nan_enable(struct ieee80211_vif *vif, + struct mt792x_dev *dev, + struct cfg80211_nan_conf *conf); + +int mt7925_nan_disable(struct ieee80211_vif *vif, + struct mt792x_dev *dev); + +int mt7925_nan_change_configure(struct ieee80211_vif *vif, + struct mt792x_dev *dev, + struct cfg80211_nan_conf *conf); + +void mt7925_nan_mcu_event(struct mt792x_dev *dev, struct sk_buff *skb); + +int mt7925_nan_set_nmi_addr(struct mt792x_dev *dev, const u8 *addr); + +void mt7925_nan_local_sched_changed(struct mt792x_dev *dev, + struct ieee80211_vif *vif); + +int mt792x_nan_set_peer_schedule(struct mt792x_dev *dev, + struct ieee80211_sta *sta); + +int mt792x_nan_set_peer_rec(struct mt76_dev *mdev, + struct ieee80211_sta *sta); + +int mt792x_nan_map_sta_rec(struct mt76_dev *mdev, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta); + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c new file mode 100644 index 000000000..02ef09dd7 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c @@ -0,0 +1,937 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2023 MediaTek Inc. */ + +#include +#include +#include + +#include "mt7925.h" +#include "mac.h" +#include "mcu.h" +#include "regd.h" +#include "../dma.h" + +static const struct pci_device_id mt7925_pci_device_table[] = { + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7925), + .driver_data = (kernel_ulong_t)MT7925_FIRMWARE_WM }, + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x0717), + .driver_data = (kernel_ulong_t)MT7925_FIRMWARE_WM }, + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7927), + .driver_data = (kernel_ulong_t)MT7927_FIRMWARE_WM }, + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x6639), + .driver_data = (kernel_ulong_t)MT7927_FIRMWARE_WM }, + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x0738), + .driver_data = (kernel_ulong_t)MT7927_FIRMWARE_WM }, + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7928), + .driver_data = (kernel_ulong_t)MT7928_FIRMWARE_WM }, + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7935), + .driver_data = (kernel_ulong_t)MT7928_FIRMWARE_WM }, + { }, +}; + +static bool mt7925_disable_aspm; +module_param_named(disable_aspm, mt7925_disable_aspm, bool, 0644); +MODULE_PARM_DESC(disable_aspm, "disable PCI ASPM support"); + +static int mt7925e_init_reset(struct mt792x_dev *dev) +{ + return mt792x_wpdma_reset(dev, true); +} + +static void mt7925e_unregister_device(struct mt792x_dev *dev) +{ + int i; + struct mt76_connac_pm *pm = &dev->pm; + struct ieee80211_hw *hw = mt76_hw(dev); + + if (dev->phy.chip_cap & MT792x_CHIP_CAP_WF_RF_PIN_CTRL_EVT_EN) + wiphy_rfkill_stop_polling(hw->wiphy); + + cancel_work_sync(&dev->reset_work); + cancel_work_sync(&dev->init_work); + mt76_unregister_device(&dev->mt76); + mt76_for_each_q_rx(&dev->mt76, i) + napi_disable(&dev->mt76.napi[i]); + cancel_delayed_work_sync(&pm->ps_work); + cancel_delayed_work_sync(&dev->mlo_pm_work); + cancel_work_sync(&pm->wake_work); + + mt7925_tx_token_put(dev); + __mt792x_mcu_drv_pmctrl(dev); + mt792x_dma_cleanup(dev); + mt792x_wfsys_reset(dev); + skb_queue_purge(&dev->mt76.mcu.res_q); + + tasklet_disable(&dev->mt76.irq_tasklet); +} + +static void mt7925_reg_remap_restore(struct mt792x_dev *dev) +{ + /* remap to ori status */ + if (unlikely(dev->backup_l1)) { + dev->bus_ops->wr(&dev->mt76, MT_HIF_REMAP_L1, dev->backup_l1); + dev->backup_l1 = 0; + } + + if (dev->backup_l2) { + dev->bus_ops->wr(&dev->mt76, MT_HIF_REMAP_L2, dev->backup_l2); + dev->backup_l2 = 0; + } +} + +static u32 mt7925_reg_map_l1(struct mt792x_dev *dev, u32 addr) +{ + u32 offset = FIELD_GET(MT_HIF_REMAP_L1_OFFSET, addr); + u32 base = FIELD_GET(MT_HIF_REMAP_L1_BASE, addr); + + dev->backup_l1 = dev->bus_ops->rr(&dev->mt76, MT_HIF_REMAP_L1); + + dev->bus_ops->rmw(&dev->mt76, MT_HIF_REMAP_L1, + MT_HIF_REMAP_L1_MASK, + FIELD_PREP(MT_HIF_REMAP_L1_MASK, base)); + + /* use read to push write */ + dev->bus_ops->rr(&dev->mt76, MT_HIF_REMAP_L1); + + return MT_HIF_REMAP_BASE_L1 + offset; +} + +static u32 mt7925_reg_map_l2(struct mt792x_dev *dev, u32 addr) +{ + u32 base = FIELD_GET(MT_HIF_REMAP_L1_BASE, MT_HIF_REMAP_BASE_L2); + + dev->backup_l2 = dev->bus_ops->rr(&dev->mt76, MT_HIF_REMAP_L1); + + dev->bus_ops->rmw(&dev->mt76, MT_HIF_REMAP_L1, + MT_HIF_REMAP_L1_MASK, + FIELD_PREP(MT_HIF_REMAP_L1_MASK, base)); + + dev->bus_ops->wr(&dev->mt76, MT_HIF_REMAP_L2, addr); + /* use read to push write */ + dev->bus_ops->rr(&dev->mt76, MT_HIF_REMAP_L1); + + return MT_HIF_REMAP_BASE_L1; +} + +static u32 __mt7925_reg_addr(struct mt792x_dev *dev, u32 addr) +{ + static const struct mt76_connac_reg_map default_fixed_map[] = { + { 0x830c0000, 0x000000, 0x0001000 }, /* WF_MCU_BUS_CR_REMAP */ + { 0x54000000, 0x002000, 0x0001000 }, /* WFDMA PCIE0 MCU DMA0 */ + { 0x55000000, 0x003000, 0x0001000 }, /* WFDMA PCIE0 MCU DMA1 */ + { 0x56000000, 0x004000, 0x0001000 }, /* WFDMA reserved */ + { 0x57000000, 0x005000, 0x0001000 }, /* WFDMA MCU wrap CR */ + { 0x58000000, 0x006000, 0x0001000 }, /* WFDMA PCIE1 MCU DMA0 (MEM_DMA) */ + { 0x59000000, 0x007000, 0x0001000 }, /* WFDMA PCIE1 MCU DMA1 */ + { 0x820c0000, 0x008000, 0x0004000 }, /* WF_UMAC_TOP (PLE) */ + { 0x820c8000, 0x00c000, 0x0002000 }, /* WF_UMAC_TOP (PSE) */ + { 0x820cc000, 0x00e000, 0x0002000 }, /* WF_UMAC_TOP (PP) */ + { 0x74030000, 0x010000, 0x0001000 }, /* PCIe MAC */ + { 0x820e0000, 0x020000, 0x0000400 }, /* WF_LMAC_TOP BN0 (WF_CFG) */ + { 0x820e1000, 0x020400, 0x0000200 }, /* WF_LMAC_TOP BN0 (WF_TRB) */ + { 0x820e2000, 0x020800, 0x0000400 }, /* WF_LMAC_TOP BN0 (WF_AGG) */ + { 0x820e3000, 0x020c00, 0x0000400 }, /* WF_LMAC_TOP BN0 (WF_ARB) */ + { 0x820e4000, 0x021000, 0x0000400 }, /* WF_LMAC_TOP BN0 (WF_TMAC) */ + { 0x820e5000, 0x021400, 0x0000800 }, /* WF_LMAC_TOP BN0 (WF_RMAC) */ + { 0x820ce000, 0x021c00, 0x0000200 }, /* WF_LMAC_TOP (WF_SEC) */ + { 0x820e7000, 0x021e00, 0x0000200 }, /* WF_LMAC_TOP BN0 (WF_DMA) */ + { 0x820cf000, 0x022000, 0x0001000 }, /* WF_LMAC_TOP (WF_PF) */ + { 0x820e9000, 0x023400, 0x0000200 }, /* WF_LMAC_TOP BN0 (WF_WTBLOFF) */ + { 0x820ea000, 0x024000, 0x0000200 }, /* WF_LMAC_TOP BN0 (WF_ETBF) */ + { 0x820eb000, 0x024200, 0x0000400 }, /* WF_LMAC_TOP BN0 (WF_LPON) */ + { 0x820ec000, 0x024600, 0x0000200 }, /* WF_LMAC_TOP BN0 (WF_INT) */ + { 0x820ed000, 0x024800, 0x0000800 }, /* WF_LMAC_TOP BN0 (WF_MIB) */ + { 0x820ca000, 0x026000, 0x0002000 }, /* WF_LMAC_TOP BN0 (WF_MUCOP) */ + { 0x820d0000, 0x030000, 0x0010000 }, /* WF_LMAC_TOP (WF_WTBLON) */ + { 0x40000000, 0x070000, 0x0010000 }, /* WF_UMAC_SYSRAM */ + { 0x00400000, 0x080000, 0x0010000 }, /* WF_MCU_SYSRAM */ + { 0x00410000, 0x090000, 0x0010000 }, /* WF_MCU_SYSRAM (configure register) */ + { 0x820f0000, 0x0a0000, 0x0000400 }, /* WF_LMAC_TOP BN1 (WF_CFG) */ + { 0x820f1000, 0x0a0600, 0x0000200 }, /* WF_LMAC_TOP BN1 (WF_TRB) */ + { 0x820f2000, 0x0a0800, 0x0000400 }, /* WF_LMAC_TOP BN1 (WF_AGG) */ + { 0x820f3000, 0x0a0c00, 0x0000400 }, /* WF_LMAC_TOP BN1 (WF_ARB) */ + { 0x820f4000, 0x0a1000, 0x0000400 }, /* WF_LMAC_TOP BN1 (WF_TMAC) */ + { 0x820f5000, 0x0a1400, 0x0000800 }, /* WF_LMAC_TOP BN1 (WF_RMAC) */ + { 0x820f7000, 0x0a1e00, 0x0000200 }, /* WF_LMAC_TOP BN1 (WF_DMA) */ + { 0x820f9000, 0x0a3400, 0x0000200 }, /* WF_LMAC_TOP BN1 (WF_WTBLOFF) */ + { 0x820fa000, 0x0a4000, 0x0000200 }, /* WF_LMAC_TOP BN1 (WF_ETBF) */ + { 0x820fb000, 0x0a4200, 0x0000400 }, /* WF_LMAC_TOP BN1 (WF_LPON) */ + { 0x820fc000, 0x0a4600, 0x0000200 }, /* WF_LMAC_TOP BN1 (WF_INT) */ + { 0x820fd000, 0x0a4800, 0x0000800 }, /* WF_LMAC_TOP BN1 (WF_MIB) */ + { 0x820c4000, 0x0a8000, 0x0004000 }, /* WF_LMAC_TOP BN1 (WF_MUCOP) */ + { 0x820b0000, 0x0ae000, 0x0001000 }, /* [APB2] WFSYS_ON */ + { 0x80020000, 0x0b0000, 0x0010000 }, /* WF_TOP_MISC_OFF */ + { 0x81020000, 0x0c0000, 0x0010000 }, /* WF_TOP_MISC_ON */ + { 0x7c020000, 0x0d0000, 0x0010000 }, /* CONN_INFRA, wfdma */ + { 0x7c060000, 0x0e0000, 0x0010000 }, /* CONN_INFRA, conn_host_csr_top */ + { 0x7c000000, 0x0f0000, 0x0010000 }, /* CONN_INFRA */ + { 0x70020000, 0x1f0000, 0x0010000 }, /* Reserved for CBTOP, can't switch */ + { 0x7c500000, 0x060000, 0x200000 }, /* remap */ + { 0x0, 0x0, 0x0 } /* End */ + }; + /* The remap table was ordered from highest to lowest frequency + * to improve lookup efficiency. + */ + static const struct mt76_connac_reg_map mt7928_fixed_map[] = { + {0x54000000, 0x002000, 0x01000}, /* WFDMA_0 (PCIE0 MCU DMA0) */ + {0x55000000, 0x003000, 0x01000}, /* WFDMA_1 (PCIE0 MCU DMA1) */ + {0x57000000, 0x005000, 0x01000}, /* WFDMA_3 (MCU wrap CR) */ + {0x58000000, 0x006000, 0x01000}, /* WFDMA_4 (PCIE1 MCU DMA0) */ + {0x59000000, 0x007000, 0x01000}, /* WFDMA_5 (PCIE1 MCU DMA1) */ + {0x56000000, 0x004000, 0x01000}, /* WFDMA_2 (Reserved) */ + {0x820D0000, 0x030000, 0x10000}, /* WF_LMAC_TOP (WF_WTBLON) */ + {0x820C4000, 0x0A8000, 0x04000}, /* WF_LMAC_TOP (WF_UWTBL) */ + {0x820C0000, 0x008000, 0x04000}, /* WF_UMAC_TOP (PLE) */ + {0x820C8000, 0x00C000, 0x02000}, /* WF_UMAC_TOP (PSE) */ + {0x820CC000, 0x00E000, 0x02000}, /* WF_UMAC_TOP (PP) */ + {0x820F0000, 0x0A0000, 0x00400}, /* WF_LMAC_TOP (WF_CFG) */ + {0x820F1000, 0x0A0600, 0x00200}, /* WF_LMAC_TOP (WF_TRB) */ + {0x820F2000, 0x0A0800, 0x00400}, /* WF_LMAC_TOP (WF_AGG) */ + {0x820F3000, 0x0A0C00, 0x00400}, /* WF_LMAC_TOP (WF_ARB) */ + {0x820F4000, 0x0A1000, 0x00400}, /* WF_LMAC_TOP (WF_TMAC) */ + {0x820F5000, 0x0A1400, 0x00800}, /* WF_LMAC_TOP (WF_RMAC) */ + {0x820F7000, 0x0A1E00, 0x00200}, /* WF_LMAC_TOP (WF_DMA) */ + {0x820F9000, 0x0A3400, 0x00200}, /* WF_LMAC_TOP (WF_WTBLOFF) */ + {0x820FA000, 0x0A4000, 0x00200}, /* WF_LMAC_TOP (WF_ETBF) */ + {0x820FB000, 0x0A4200, 0x00400}, /* WF_LMAC_TOP (WF_LPON) */ + {0x820FC000, 0x0A4600, 0x00200}, /* WF_LMAC_TOP (WF_INT) */ + {0x820FD000, 0x0A4800, 0x00800}, /* WF_LMAC_TOP (WF_MIB) */ + {0x820E0000, 0x020000, 0x00400}, /* WF_LMAC_TOP (WF_CFG) */ + {0x820E1000, 0x020400, 0x00200}, /* WF_LMAC_TOP (WF_TRB) */ + {0x820E2000, 0x020800, 0x00400}, /* WF_LMAC_TOP (WF_AGG) */ + {0x820E3000, 0x020C00, 0x00400}, /* WF_LMAC_TOP (WF_ARB) */ + {0x820E4000, 0x021000, 0x00400}, /* WF_LMAC_TOP (WF_TMAC) */ + {0x820E5000, 0x021400, 0x00800}, /* WF_LMAC_TOP (WF_RMAC) */ + {0x820CE000, 0x021C00, 0x00200}, /* WF_LMAC_TOP (WF_SEC) */ + {0x820E7000, 0x021E00, 0x00200}, /* WF_LMAC_TOP (WF_DMA) */ + {0x820CF000, 0x022000, 0x01000}, /* WF_LMAC_TOP (WF_PF) */ + {0x820E9000, 0x023400, 0x00200}, /* WF_LMAC_TOP (WF_WTBLOFF) */ + {0x820EA000, 0x024000, 0x00200}, /* WF_LMAC_TOP (WF_ETBF) */ + {0x820EB000, 0x024200, 0x00400}, /* WF_LMAC_TOP (WF_LPON) */ + {0x820EC000, 0x024600, 0x00200}, /* WF_LMAC_TOP (WF_INT) */ + {0x820ED000, 0x024800, 0x00800}, /* WF_LMAC_TOP (WF_MIB) */ + {0x820CA000, 0x026000, 0x02000}, /* WF_LMAC_TOP (WF_MUCOP) */ + {0x7C500000, 0x060000, 0x200000}, /* remap */ + {0x7C000000, 0x0F0000, 0x10000}, /* CONN_INFRA (off2on) */ + {0x7C060000, 0x0E0000, 0x10000}, /* remap MT_CONN_ON_LPCTL and MT_CONN_ON_MISC */ + {0x20060000, 0x0E0000, 0x10000}, /* CONN_INFRA conn_host_csr_top */ + {0x7C010000, 0x100000, 0x10000}, /* CONN_INFRA (gpio clkgen cfg) */ + {0x7C050000, 0x1A0000, 0x10000}, /* CONN_INFRA SYSRAM */ + {0x7C080000, 0x190000, 0x10000}, /* CONN_INFRA (coex, pta) */ + {0x7C070000, 0x180000, 0x10000}, /* CONN_INFRA Semaphore */ + {0x7C040000, 0x170000, 0x10000}, /* CONN_INFRA (bus, afe) */ + {0x7C026000, 0x0D6000, 0x0019C}, /* remap DMASHL TOP */ + {0x20020000, 0x0D0000, 0x0C000}, /* CONN_INFRA wf_dma_host_side_cr */ + {0x200B0000, 0x050000, 0x10000}, /* CONN_INFRA conn_von_sysram */ + {0x20090000, 0x150000, 0x08000}, /* CONN_INFRA von_connsys_s0-s7 */ + {0x7C098000, 0x158000, 0x08000}, /* CONN_INFRA von_connsys_hclk_s0-s7 */ + {0x20030000, 0x160000, 0x10000}, /* CONN_INFRA CCIF */ + {0x70000000, 0x1E0000, 0x10000}, /* CONN_INFRA CONN2AP */ + {0x830C0000, 0x000000, 0x01000}, /* WF_MCU_BUS_CR_REMAP */ + {0x81020000, 0x0C0000, 0x10000}, /* WF_TOP_MISC_ON */ + {0x80020000, 0x0B0000, 0x10000}, /* WF_TOP_MISC_OFF */ + {0x81040000, 0x120000, 0x01000}, /* WF_MCU_CFG_ON */ + {0x00400000, 0x080000, 0x10000}, /* WF_MCU_SYSRAM */ + {0x00410000, 0x090000, 0x10000}, /* WF_MCU_SYSRAM (Common driver) */ + {0x88000000, 0x140000, 0x10000}, /* WF_MCU_CFG_LS */ + {0x80010000, 0x124000, 0x01000}, /* WF_AXIDMA */ + {0x81050000, 0x121000, 0x01000}, /* WF_MCU_EINT */ + {0x81060000, 0x122000, 0x01000}, /* WF_MCU_GPT */ + {0x81070000, 0x123000, 0x01000}, /* WF_MCU_WDT */ + {0x830A0000, 0x040000, 0x10000}, /* WF_PHY_MAP0 */ + {0x83090000, 0x060000, 0x10000}, /* WF_PHY_MAP2 */ + {0x83000000, 0x110000, 0x10000}, /* WF_PHY_MAP3 */ + {0x83010000, 0x130000, 0x10000}, /* WF_PHY_MAP4 */ + {0x81030000, 0x0AE000, 0x00100}, /* WFSYS_AON */ + {0x81031000, 0x0AE100, 0x00100}, /* WFSYS_AON */ + {0x81032000, 0x0AE200, 0x00100}, /* WFSYS_AON */ + {0x81033000, 0x0AE300, 0x00100}, /* WFSYS_AON */ + {0x81034000, 0x0AE400, 0x00100}, /* WFSYS_AON */ + {0xE0400000, 0x070000, 0x10000}, /* WF_UMCA_SYSRAM */ + {0x70010000, 0x1C0000, 0x10000}, /* CB Infra1 */ + {0x70020000, 0x1F0000, 0x10000}, /* Reserved for CBTOP, can't switch */ + {0x74040000, 0x1D0000, 0x10000}, /* CB PCIe (cbtop remap) */ + {0x18010000, 0x100000, 0x10000}, /* remap MT_HW_EMI_CTRL */ + {0x00000000, 0x000000, 0x00000}, /* END */ + }; + const struct mt76_connac_reg_map *fixed_map; + size_t array_size; + u32 i; + + if (addr < 0x200000) + return addr; + + mt7925_reg_remap_restore(dev); + + if (is_mt7928(&dev->mt76)) { + fixed_map = mt7928_fixed_map; + array_size = ARRAY_SIZE(mt7928_fixed_map); + } else { + fixed_map = default_fixed_map; + array_size = ARRAY_SIZE(default_fixed_map); + } + + for (i = 0; i < array_size; i++) { + u32 ofs; + + if (addr < fixed_map[i].phys) + continue; + + ofs = addr - fixed_map[i].phys; + if (ofs > fixed_map[i].size) + continue; + + return fixed_map[i].maps + ofs; + } + + if ((addr >= 0x18000000 && addr < 0x18c00000) || + (addr >= 0x70000000 && addr < 0x78000000) || + (addr >= 0x7c000000 && addr < 0x7c400000)) + return mt7925_reg_map_l1(dev, addr); + + return mt7925_reg_map_l2(dev, addr); +} + +static u32 mt7925_rr(struct mt76_dev *mdev, u32 offset) +{ + struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); + u32 addr = __mt7925_reg_addr(dev, offset); + + return dev->bus_ops->rr(mdev, addr); +} + +static void mt7925_wr(struct mt76_dev *mdev, u32 offset, u32 val) +{ + struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); + u32 addr = __mt7925_reg_addr(dev, offset); + + dev->bus_ops->wr(mdev, addr, val); +} + +static u32 mt7925_rmw(struct mt76_dev *mdev, u32 offset, u32 mask, u32 val) +{ + struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); + u32 addr = __mt7925_reg_addr(dev, offset); + + return dev->bus_ops->rmw(mdev, addr, mask, val); +} + +static const struct mt792x_dma_layout mt7925_dma_layout = { + .tx_data0 = mt792x_dma_ring(MT7925_TXQ_BAND0, + MT7925_TX_RING_SIZE, + MT_TX_RING_BASE), + .tx_mcu = mt792x_dma_ring(MT7925_TXQ_MCU_WM, + MT7925_TX_MCU_RING_SIZE, + MT_TX_RING_BASE), + .tx_fwdl = mt792x_dma_ring(MT7925_TXQ_FWDL, + MT7925_TX_FWDL_RING_SIZE, + MT_TX_RING_BASE), + .rx_mcu = mt792x_dma_ring(MT7925_RXQ_MCU_WM, + MT7925_RX_MCU_RING_SIZE, + MT_RX_EVENT_RING_BASE), + .rx_data = mt792x_dma_ring(MT7925_RXQ_BAND0, + MT7925_RX_RING_SIZE, + MT_RX_DATA_RING_BASE), +}; + +static const struct mt792x_dma_layout mt7927_dma_layout = { + .tx_data0 = mt792x_dma_ring(MT7927_TXQ_BAND0, + MT7925_TX_RING_SIZE, + MT_TX_RING_BASE), + .tx_mcu = mt792x_dma_ring(MT7927_TXQ_MCU_WM, + MT7925_TX_MCU_RING_SIZE, + MT_TX_RING_BASE), + .tx_fwdl = mt792x_dma_ring(MT7927_TXQ_FWDL, + MT7925_TX_FWDL_RING_SIZE, + MT_TX_RING_BASE), + .rx_mcu = mt792x_dma_ring(MT7927_RXQ_MCU_WM, + MT7925_RX_MCU_RING_SIZE, + MT_RX_EVENT_RING_BASE), + .rx_data = mt792x_dma_ring(MT7927_RXQ_BAND0, + MT7925_RX_RING_SIZE, + MT_RX_DATA_RING_BASE), +}; + +static const struct mt792x_dma_layout mt7928_dma_layout = { + .tx_data0 = mt792x_dma_ring(MT7925_TXQ_BAND0, + MT7925_TX_RING_SIZE, + MT_TX_RING_BASE), + .tx_mcu = mt792x_dma_ring(MT7925_TXQ_MCU_WM, + MT7925_TX_MCU_RING_SIZE, + MT_TX_RING_BASE), + .tx_fwdl = mt792x_dma_ring(MT7925_TXQ_FWDL, + MT7925_TX_FWDL_RING_SIZE, + MT_TX_RING_BASE), + .tx_done = mt792x_dma_ring(MT7928_RXQ_MCU_WM2, + MT7928_RX_MCU_WA_RING_SIZE, + MT_RX_EVENT_RING_BASE), + .rx_mcu = mt792x_dma_ring(MT7928_RXQ_MCU_WM, + MT7925_RX_MCU_RING_SIZE, + MT_RX_EVENT_RING_BASE), + .rx_data = mt792x_dma_ring(MT7928_RXQ_BAND0, + MT7925_RX_RING_SIZE, + MT_RX_DATA_RING_BASE), +}; + +static int mt7927_dma_init(struct mt792x_dev *dev) +{ + int ret; + + ret = mt792x_dma_alloc_queues(dev, &mt7927_dma_layout); + if (ret) + return ret; + + ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MCU_WA], + MT7927_RXQ_DATA2, + MT7925_RX_MCU_RING_SIZE, + MT_RX_BUF_SIZE, + MT_RX_DATA_RING_BASE); + if (ret) + return ret; + + ret = mt76_init_queues(dev, mt792x_poll_rx); + if (ret < 0) + return ret; + + netif_napi_add_tx(dev->mt76.tx_napi_dev, &dev->mt76.tx_napi, + mt792x_poll_tx); + napi_enable(&dev->mt76.tx_napi); + + return mt792x_dma_enable(dev); +} + +static void mt7928_dma_shdl_lite_init(struct mt792x_dev *dev) +{ + u32 addr, idx, grp1_5_quota, grp15_quota; + u32 q2group[8] = { + 0x04000000, /* AC00->G0,..., AC03->G4 */ + 0x04010101, /* AC10->G1,..., AC13->G4 */ + 0x04020202, /* AC20->G2,..., AC23->G4 */ + 0x04030303, /* AC30->G3,..., AC33->G4 */ + 0x00000005, /* ALTX->G5,BMC->G0,BCN->G0 */ + 0x00000005, /* TGID=1 ALTX->G5 */ + 0x00000000, /* NAF/NBCN/FIXFID -> G0 */ + 0x00000005, /* TGID=2 ALTX->G5 */ + }; + + /* RST */ + mt76_wr(dev, MT_DMASHDL_LITE_MAIN_CONTROL, MT_DMASHDL_LITE_MAIN_CONTROL_SW_RST); + /* pse page size 0x10, ple page size 0x7e0 */ + mt76_wr(dev, MT_DMASHDL_LITE_PAGE_SIZE, + FIELD_PREP(MT_DMASHDL_LITE_PSE_PAGE_SIZE_MASK, 0x10) | + FIELD_PREP(MT_DMASHDL_LITE_PLE_PAGE_SIZE_MASK, 0x7e0)); + /* pse max page 8, ple max page 1 */ + mt76_wr(dev, MT_DMASHDL_LITE_PKT_MAX_SIZE, + FIELD_PREP(MT_DMASHDL_LITE_PSE_PKT_MAX_SIZE_MASK, 8) | + FIELD_PREP(MT_DMASHDL_LITE_PLE_PKT_MAX_SIZE_MASK, 1)); + /* SN/UDF check */ + mt76_wr(dev, MT_DMASHDL_LITE_GROUP_SN_CHK0, 0xffffffff); + mt76_wr(dev, MT_DMASHDL_LITE_GROUP_SN_CHK1, 0xffffffff); + mt76_wr(dev, MT_DMASHDL_LITE_GROUP_UDF_CHK0, 0xffffffff); + mt76_wr(dev, MT_DMASHDL_LITE_GROUP_UDF_CHK1, 0xffffffff); + /* q mapping */ + for (addr = MT_DMASHDL_LITE_Q_MAPPING0, idx = 0; + idx < ARRAY_SIZE(q2group); + idx++, addr += 4) + mt76_wr(dev, addr, q2group[idx]); + /* refill, set 0 to enable group 0,1,2,3,4,5 & 15 */ + mt76_wr(dev, MT_DMASHDL_LITE_GROUP_DISABLE0, 0xffff7fc0); + mt76_wr(dev, MT_DMASHDL_LITE_GROUP_DISABLE1, 0xffffffff); + /* max/min quota */ + grp1_5_quota = FIELD_PREP(MT_DMASHDL_LITE_GROUP_MAX_QUOTA_MASK, 0x3f0) | + FIELD_PREP(MT_DMASHDL_LITE_GROUP_MIN_QUOTA_MASK, 0x10); + grp15_quota = FIELD_PREP(MT_DMASHDL_LITE_GROUP_MAX_QUOTA_MASK, 0x30); + + for (addr = MT_DMASHDL_LITE_GROUP0_QUOTA, idx = 0; + idx < DMASHDL_LITE_GROUP_NUM; + idx++, addr += 4) + mt76_wr(dev, addr, (idx <= 5) ? grp1_5_quota : + ((idx == 15) ? grp15_quota : 0)); +} + +static int mt7925_dma_init(struct mt792x_dev *dev) +{ + int ret; + const struct mt792x_dma_layout *layout = + is_mt7928(&dev->mt76) ? &mt7928_dma_layout : &mt7925_dma_layout; + + ret = mt792x_dma_alloc_queues(dev, layout); + if (ret) + return ret; + + ret = mt76_init_queues(dev, mt792x_poll_rx); + if (ret < 0) + return ret; + + if (is_mt7928(&dev->mt76)) + mt7928_dma_shdl_lite_init(dev); + + netif_napi_add_tx(dev->mt76.tx_napi_dev, &dev->mt76.tx_napi, + mt792x_poll_tx); + napi_enable(&dev->mt76.tx_napi); + + return mt792x_dma_enable(dev); +} + +static const struct mt792x_pcie_reg mt7925_pcie_reg = { + .imask = MT7925_PCIE_MAC_INT_ENABLE, + .pm = MT7925_PCIE_MAC_PM, +}; + +static const struct mt792x_pcie_reg mt7928_pcie_reg = { + .imask = MT7928_PCIE_MAC_INT_ENABLE, + .pm = MT7928_PCIE_MAC_PM, +}; + +static const struct mt792x_irq_map mt7925_irq_map = { + .host_irq_enable = MT_WFDMA0_HOST_INT_ENA, + .tx = { + .all_complete_mask = MT_INT_TX_DONE_ALL, + .mcu_complete_mask = MT_INT_TX_DONE_MCU, + }, + .rx = { + .all_complete_mask = MT7925_INT_RX_DONE_ALL, + .data_complete_mask = MT7925_INT_RX_DONE_DATA, + .wm_complete_mask = MT7925_INT_RX_DONE_WM, + .wm2_complete_mask = MT_INT_RX_DONE_WM2, + }, +}; + +static const struct mt792x_irq_map mt7927_irq_map = { + .host_irq_enable = MT_WFDMA0_HOST_INT_ENA, + .tx = { + .all_complete_mask = MT_INT_TX_DONE_ALL, + .mcu_complete_mask = MT_INT_TX_DONE_MCU, + }, + .rx = { + .all_complete_mask = MT7927_INT_RX_DONE_ALL, + .data_complete_mask = MT7927_INT_RX_DONE_DATA, + .wm_complete_mask = MT7927_INT_RX_DONE_WM, + .wm2_complete_mask = MT7927_INT_RX_DONE_WM2, + }, +}; + +static const struct mt792x_irq_map mt7928_irq_map = { + .host_irq_enable = MT_WFDMA0_HOST_INT_ENA, + .tx = { + .all_complete_mask = MT_INT_TX_DONE_ALL, + .mcu_complete_mask = MT_INT_TX_DONE_MCU, + }, + .rx = { + .all_complete_mask = MT7928_INT_RX_DONE_ALL, + .data_complete_mask = MT7928_INT_RX_DONE_DATA, + .wm_complete_mask = MT7928_INT_RX_DONE_WM, + .wm2_complete_mask = MT_INT_RX_DONE_WM2, + }, +}; + +static int mt7925_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + static const struct mt76_driver_ops drv_ops = { + /* txwi_size = txd size + txp size */ + .txwi_size = MT_TXD_SIZE + sizeof(struct mt76_connac_hw_txp), + .drv_flags = MT_DRV_TXWI_NO_FREE | MT_DRV_HW_MGMT_TXQ | + MT_DRV_AMSDU_OFFLOAD, + .survey_flags = SURVEY_INFO_TIME_TX | + SURVEY_INFO_TIME_RX | + SURVEY_INFO_TIME_BSS_RX, + .token_size = MT7925_TOKEN_SIZE, + .tx_prepare_skb = mt7925e_tx_prepare_skb, + .tx_complete_skb = mt76_connac_tx_complete_skb, + .rx_check = mt7925_rx_check, + .rx_skb = mt7925_queue_rx_skb, + .rx_poll_complete = mt792x_rx_poll_complete, + .sta_add = mt7925_mac_sta_add, + .sta_event = mt7925_mac_sta_event, + .sta_remove = mt7925_mac_sta_remove, + .update_survey = mt792x_update_channel, + }; + static const struct mt792x_hif_ops mt7925_pcie_ops = { + .init_reset = mt7925e_init_reset, + .reset = mt7925e_mac_reset, + .mcu_init = mt7925e_mcu_init, + .drv_own = mt792xe_mcu_drv_pmctrl, + .fw_own = mt792xe_mcu_fw_pmctrl, + }; + bool is_mt7927_hw, is_mt7928_hw; + struct mt76_bus_ops *bus_ops; + struct ieee80211_ops *ops; + struct mt792x_dev *dev; + struct mt76_dev *mdev; + u8 features; + int ret; + u16 cmd; + + ret = pcim_enable_device(pdev); + if (ret) + return ret; + + ret = pcim_iomap_regions(pdev, BIT(0), pci_name(pdev)); + if (ret) + return ret; + + pci_read_config_word(pdev, PCI_COMMAND, &cmd); + if (!(cmd & PCI_COMMAND_MEMORY)) { + cmd |= PCI_COMMAND_MEMORY; + pci_write_config_word(pdev, PCI_COMMAND, cmd); + } + pci_set_master(pdev); + + ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES); + if (ret < 0) + return ret; + + is_mt7927_hw = (pdev->device == 0x6639 || pdev->device == 0x7927 || + pdev->device == 0x0738); + is_mt7928_hw = (pdev->device == 0x7928 || pdev->device == 0x7935); + + if (is_mt7928_hw) + ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(34)); + else + ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); + + if (ret) + goto err_free_pci_vec; + + /* MT7927: ASPM L1 causes unreliable WFDMA register access */ + if (mt7925_disable_aspm || is_mt7927_hw) + mt76_pci_disable_aspm(pdev); + + ops = mt792x_get_mac80211_ops(&pdev->dev, &mt7925_ops, + (void *)id->driver_data, &features); + if (!ops) { + ret = -ENOMEM; + goto err_free_pci_vec; + } + + mdev = mt76_alloc_device(&pdev->dev, sizeof(*dev), ops, &drv_ops); + if (!mdev) { + ret = -ENOMEM; + goto err_free_pci_vec; + } + + pci_set_drvdata(pdev, mdev); + + dev = container_of(mdev, struct mt792x_dev, mt76); + dev->fw_features = features; + dev->hif_ops = &mt7925_pcie_ops; + dev->pcie_reg = &mt7925_pcie_reg; + + if (is_mt7928_hw) { + dev->pcie_reg = &mt7928_pcie_reg; + dev->irq_map = &mt7928_irq_map; + mdev->rev = 0x7928 << 16; + } else if (is_mt7927_hw) { + dev->irq_map = &mt7927_irq_map; + } else { + dev->irq_map = &mt7925_irq_map; + } + + mt76_mmio_init(&dev->mt76, pcim_iomap_table(pdev)[0]); + tasklet_init(&mdev->irq_tasklet, mt792x_irq_tasklet, (unsigned long)dev); + + dev->phy.dev = dev; + dev->phy.mt76 = &dev->mt76.phy; + dev->mt76.phy.priv = &dev->phy; + dev->bus_ops = dev->mt76.bus; + bus_ops = devm_kmemdup(dev->mt76.dev, dev->bus_ops, sizeof(*bus_ops), + GFP_KERNEL); + if (!bus_ops) { + ret = -ENOMEM; + goto err_free_dev; + } + + bus_ops->rr = mt7925_rr; + bus_ops->wr = mt7925_wr; + bus_ops->rmw = mt7925_rmw; + dev->mt76.bus = bus_ops; + + if (!mt7925_disable_aspm && mt76_pci_aspm_supported(pdev)) + dev->aspm_supported = true; + + if (is_mt7928(&dev->mt76)) + dev->skip_wpdma_reinit = true; + + ret = __mt792x_mcu_fw_pmctrl(dev); + if (ret) + goto err_free_dev; + + ret = __mt792xe_mcu_drv_pmctrl(dev); + if (ret) + goto err_free_dev; + + mdev->rev = (mt76_rr(dev, MT_HW_CHIPID) << 16) | + (mt76_rr(dev, MT_HW_REV) & 0xff); + + dev_info(mdev->dev, "ASIC revision: %04x\n", mdev->rev); + + if (is_mt7927_hw && mt76_chip(mdev) != 0x7927) { + dev_info(mdev->dev, + "MT7927 raw CHIPID=0x%04x, forcing chip=0x7927\n", + mt76_chip(mdev)); + mdev->rev = (0x7927 << 16) | (mdev->rev & 0xff); + } else if (is_mt7928_hw && mt76_chip(mdev) != 0x7928) { + dev_info(mdev->dev, + "MT7928 raw CHIPID=0x%04x, forcing chip=0x7928\n", + mt76_chip(mdev)); + mdev->rev = (0x7928 << 16) | (mdev->rev & 0xff); + } + + mt76_rmw_field(dev, MT_HW_EMI_CTL, MT_HW_EMI_CTL_SLPPROT_EN, 1); + + ret = mt792x_wfsys_reset(dev); + if (ret) + goto err_free_dev; + + mt76_wr(dev, dev->irq_map->host_irq_enable, 0); + mt76_wr(dev, dev->pcie_reg->imask, 0xff); + + ret = devm_request_irq(mdev->dev, pdev->irq, mt792x_irq_handler, + IRQF_SHARED, KBUILD_MODNAME, dev); + if (ret) + goto err_free_dev; + + if (is_mt7927(&dev->mt76)) + ret = mt7927_dma_init(dev); + else if (is_mt7925(&dev->mt76) || is_mt7928(&dev->mt76)) + ret = mt7925_dma_init(dev); + else + ret = -EINVAL; + + if (ret) + goto err_free_irq; + + ret = mt7925_register_device(dev); + if (ret) + goto err_free_dma; + + return 0; + +err_free_dma: + mt792x_dma_cleanup(dev); +err_free_irq: + devm_free_irq(&pdev->dev, pdev->irq, dev); +err_free_dev: + mt76_free_device(&dev->mt76); +err_free_pci_vec: + pci_free_irq_vectors(pdev); + + return ret; +} + +static void mt7925_pci_remove(struct pci_dev *pdev) +{ + struct mt76_dev *mdev = pci_get_drvdata(pdev); + struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); + + set_bit(MT76_REMOVED, &mdev->phy.state); + mt7925e_unregister_device(dev); + devm_free_irq(&pdev->dev, pdev->irq, dev); + mt76_free_device(&dev->mt76); + pci_free_irq_vectors(pdev); +} + +static int mt7925_pci_suspend(struct device *device) +{ + struct pci_dev *pdev = to_pci_dev(device); + struct mt76_dev *mdev = pci_get_drvdata(pdev); + struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); + struct mt76_connac_pm *pm = &dev->pm; + int i, err, ret; + + pm->suspended = true; + dev->hif_resumed = false; + flush_work(&dev->reset_work); + cancel_delayed_work_sync(&pm->ps_work); + cancel_delayed_work_sync(&dev->mlo_pm_work); + cancel_work_sync(&pm->wake_work); + + mt7925_roc_abort_sync(dev); + + err = mt792x_mcu_drv_pmctrl(dev); + if (err < 0) + goto restore_suspend; + + wait_event_timeout(dev->wait, + !dev->regd_in_progress, 5 * HZ); + + /* always enable deep sleep during suspend to reduce + * power consumption + */ + mt7925_mcu_set_deep_sleep(dev, true); + + mt76_connac_mcu_set_hif_suspend(mdev, true, false); + ret = wait_event_timeout(dev->wait, + dev->hif_idle, 3 * HZ); + if (!ret) { + err = -ETIMEDOUT; + goto restore_suspend; + } + + napi_disable(&mdev->tx_napi); + mt76_worker_disable(&mdev->tx_worker); + + mt76_for_each_q_rx(mdev, i) { + napi_disable(&mdev->napi[i]); + } + + /* wait until dma is idle */ + mt76_poll(dev, MT_WFDMA0_GLO_CFG, + MT_WFDMA0_GLO_CFG_TX_DMA_BUSY | + MT_WFDMA0_GLO_CFG_RX_DMA_BUSY, 0, 1000); + + /* put dma disabled */ + mt76_clear(dev, MT_WFDMA0_GLO_CFG, + MT_WFDMA0_GLO_CFG_TX_DMA_EN | MT_WFDMA0_GLO_CFG_RX_DMA_EN); + + /* disable interrupt */ + mt76_wr(dev, dev->irq_map->host_irq_enable, 0); + mt76_wr(dev, dev->pcie_reg->imask, 0x0); + + synchronize_irq(pdev->irq); + tasklet_kill(&mdev->irq_tasklet); + + err = mt792x_mcu_fw_pmctrl(dev); + if (err) + goto restore_napi; + + return 0; + +restore_napi: + mt76_for_each_q_rx(mdev, i) { + napi_enable(&mdev->napi[i]); + } + napi_enable(&mdev->tx_napi); + + if (!pm->ds_enable) + mt7925_mcu_set_deep_sleep(dev, false); + + mt76_connac_mcu_set_hif_suspend(mdev, false, false); + ret = wait_event_timeout(dev->wait, + dev->hif_resumed, 3 * HZ); + if (!ret) + err = -ETIMEDOUT; +restore_suspend: + pm->suspended = false; + + if (err < 0) + mt792x_reset(&dev->mt76); + + return err; +} + +static int _mt7925_pci_resume(struct device *device, bool restore) +{ + struct pci_dev *pdev = to_pci_dev(device); + struct mt76_dev *mdev = pci_get_drvdata(pdev); + struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); + struct mt76_connac_pm *pm = &dev->pm; + int i, err, ret; + + dev->hif_idle = false; + err = mt792x_mcu_drv_pmctrl(dev); + if (err < 0) + goto failed; + + mt792x_wpdma_reinit_cond(dev); + + /* enable interrupt */ + mt76_wr(dev, dev->pcie_reg->imask, 0xff); + mt76_connac_irq_enable(&dev->mt76, + dev->irq_map->tx.all_complete_mask | + dev->irq_map->rx.all_complete_mask | + MT_INT_MCU_CMD); + mt76_set(dev, MT_MCU2HOST_SW_INT_ENA, MT_MCU_CMD_WAKE_RX_PCIE); + + /* put dma enabled */ + mt76_set(dev, MT_WFDMA0_GLO_CFG, + MT_WFDMA0_GLO_CFG_TX_DMA_EN | MT_WFDMA0_GLO_CFG_RX_DMA_EN); + + mt76_worker_enable(&mdev->tx_worker); + + mt76_for_each_q_rx(mdev, i) { + napi_enable(&mdev->napi[i]); + } + napi_enable(&mdev->tx_napi); + + local_bh_disable(); + mt76_for_each_q_rx(mdev, i) { + napi_schedule(&mdev->napi[i]); + } + napi_schedule(&mdev->tx_napi); + local_bh_enable(); + + if (restore) + goto failed; + + mt76_connac_mcu_set_hif_suspend(mdev, false, false); + ret = wait_event_timeout(dev->wait, + dev->hif_resumed, 3 * HZ); + if (!ret) { + err = -ETIMEDOUT; + goto failed; + } + + /* restore previous ds setting */ + if (!pm->ds_enable) + mt7925_mcu_set_deep_sleep(dev, false); + + mt7925_mcu_regd_update(dev, mdev->alpha2, dev->country_ie_env); +failed: + pm->suspended = false; + + if (err < 0 || restore) + mt792x_reset(&dev->mt76); + + return err; +} + +static void mt7925_pci_shutdown(struct pci_dev *pdev) +{ + mt7925_pci_remove(pdev); +} + +static int mt7925_pci_resume(struct device *device) +{ + return _mt7925_pci_resume(device, false); +} + +static int mt7925_pci_restore(struct device *device) +{ + return _mt7925_pci_resume(device, true); +} + +static const struct dev_pm_ops mt7925_pm_ops = { + .suspend = pm_sleep_ptr(mt7925_pci_suspend), + .resume = pm_sleep_ptr(mt7925_pci_resume), + .freeze = pm_sleep_ptr(mt7925_pci_suspend), + .thaw = pm_sleep_ptr(mt7925_pci_resume), + .poweroff = pm_sleep_ptr(mt7925_pci_suspend), + .restore = pm_sleep_ptr(mt7925_pci_restore), +}; + +static struct pci_driver mt7925_pci_driver = { + .name = KBUILD_MODNAME, + .id_table = mt7925_pci_device_table, + .probe = mt7925_pci_probe, + .remove = mt7925_pci_remove, + .shutdown = mt7925_pci_shutdown, + .driver.pm = pm_sleep_ptr(&mt7925_pm_ops), +}; + +module_pci_driver(mt7925_pci_driver); + +MODULE_DEVICE_TABLE(pci, mt7925_pci_device_table); +MODULE_FIRMWARE(MT7925_FIRMWARE_WM); +MODULE_FIRMWARE(MT7925_ROM_PATCH); +MODULE_FIRMWARE(MT7927_FIRMWARE_WM); +MODULE_FIRMWARE(MT7927_ROM_PATCH); +MODULE_FIRMWARE(MT7928_FIRMWARE_WM); +MODULE_FIRMWARE(MT7928_ROM_PATCH); +MODULE_FIRMWARE(MT7928_CB_ROM_PATCH); +MODULE_FIRMWARE(MT7928_PHY_RAM); +MODULE_AUTHOR("Deren Wu "); +MODULE_AUTHOR("Lorenzo Bianconi "); +MODULE_DESCRIPTION("MediaTek MT7925E (PCIe) wireless driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c new file mode 100644 index 000000000..8477d21ab --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c @@ -0,0 +1,159 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2023 MediaTek Inc. */ + +#include "mt7925.h" +#include "../dma.h" +#include "mcu.h" +#include "mac.h" + +int mt7925e_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, + enum mt76_txq_id qid, struct mt76_wcid *wcid, + struct ieee80211_sta *sta, + struct mt76_tx_info *tx_info) +{ + struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx_info->skb); + struct ieee80211_key_conf *key = info->control.hw_key; + struct mt76_connac_hw_txp *txp; + struct mt76_txwi_cache *t; + int id, pid; + u8 *txwi = (u8 *)txwi_ptr; + + if (unlikely(tx_info->skb->len <= ETH_HLEN)) + return -EINVAL; + + if (!wcid) + wcid = &dev->mt76.global_wcid; + + t = (struct mt76_txwi_cache *)(txwi + mdev->drv->txwi_size); + t->skb = tx_info->skb; + + id = mt76_token_consume(mdev, &t); + if (id < 0) + return id; + + if (sta) { + struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv; + + if (time_after(jiffies, msta->deflink.last_txs + HZ / 4)) { + info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; + msta->deflink.last_txs = jiffies; + } + } + + pid = mt76_tx_status_skb_add(mdev, wcid, tx_info->skb); + mt7925_mac_write_txwi(mdev, txwi_ptr, tx_info->skb, wcid, key, + pid, qid, 0); + + txp = (struct mt76_connac_hw_txp *)(txwi + MT_TXD_SIZE); + memset(txp, 0, sizeof(struct mt76_connac_hw_txp)); + mt76_connac_write_hw_txp(mdev, tx_info, txp, id); + + tx_info->skb = NULL; + + return 0; +} + +void mt7925_tx_token_put(struct mt792x_dev *dev) +{ + struct mt76_txwi_cache *txwi; + int id; + + spin_lock_bh(&dev->mt76.token_lock); + idr_for_each_entry(&dev->mt76.token, txwi, id) { + mt7925_txwi_free(dev, txwi, NULL, NULL, NULL); + dev->mt76.token_count--; + } + spin_unlock_bh(&dev->mt76.token_lock); + idr_destroy(&dev->mt76.token); +} + +int mt7925e_mac_reset(struct mt792x_dev *dev) +{ + const struct mt792x_irq_map *irq_map = dev->irq_map; + int i, err; + + mt792xe_mcu_drv_pmctrl(dev); + + mt76_connac_free_pending_tx_skbs(&dev->pm, NULL); + + mt76_wr(dev, dev->irq_map->host_irq_enable, 0); + mt76_wr(dev, dev->pcie_reg->imask, 0x0); + + set_bit(MT76_RESET, &dev->mphy.state); + set_bit(MT76_MCU_RESET, &dev->mphy.state); + wake_up(&dev->mt76.mcu.wait); + skb_queue_purge(&dev->mt76.mcu.res_q); + + mt76_txq_schedule_all(&dev->mphy); + + mt76_worker_disable(&dev->mt76.tx_worker); + if (irq_map->rx.data_complete_mask) + napi_disable(&dev->mt76.napi[MT_RXQ_MAIN]); + if (irq_map->rx.wm_complete_mask) + napi_disable(&dev->mt76.napi[MT_RXQ_MCU]); + if (irq_map->rx.wm2_complete_mask) + napi_disable(&dev->mt76.napi[MT_RXQ_MCU_WA]); + if (irq_map->tx.all_complete_mask) + napi_disable(&dev->mt76.tx_napi); + + mt7925_tx_token_put(dev); + idr_init(&dev->mt76.token); + + mt792x_wpdma_reset(dev, true); + + mt76_for_each_q_rx(&dev->mt76, i) { + napi_enable(&dev->mt76.napi[i]); + } + napi_enable(&dev->mt76.tx_napi); + + local_bh_disable(); + mt76_for_each_q_rx(&dev->mt76, i) { + napi_schedule(&dev->mt76.napi[i]); + } + napi_schedule(&dev->mt76.tx_napi); + local_bh_enable(); + + dev->fw_assert = false; + clear_bit(MT76_MCU_RESET, &dev->mphy.state); + + mt76_wr(dev, dev->irq_map->host_irq_enable, + dev->irq_map->tx.all_complete_mask | + dev->irq_map->rx.all_complete_mask | + MT_INT_MCU_CMD); + mt76_wr(dev, dev->pcie_reg->imask, 0xff); + + err = mt792xe_mcu_fw_pmctrl(dev); + if (err) + return err; + + err = __mt792xe_mcu_drv_pmctrl(dev); + if (err) + goto out; + + err = mt7925_run_firmware(dev); + if (err) + goto out; + + err = mt7925_mcu_set_eeprom(dev); + if (err) + goto out; + + err = mt7925_mac_init(dev); + if (err) + goto out; + + if (is_mt7927(&dev->mt76)) { + err = mt7925_mcu_set_dbdc(&dev->mphy, true); + if (err) + goto out; + } + + err = __mt7925_start(&dev->phy); +out: + clear_bit(MT76_RESET, &dev->mphy.state); + + mt76_worker_enable(&dev->mt76.tx_worker); + + return err; +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci_mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci_mcu.c new file mode 100644 index 000000000..72707eddc --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci_mcu.c @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2023 MediaTek Inc. */ + +#include "mt7925.h" +#include "mcu.h" + +static int +mt7925_mcu_send_message(struct mt76_dev *mdev, struct sk_buff *skb, + int cmd, int *seq) +{ + struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); + enum mt76_mcuq_id txq = MT_MCUQ_WM; + int ret; + + ret = mt7925_mcu_fill_message(mdev, skb, cmd, seq); + if (ret) + return ret; + + mdev->mcu.timeout = 3 * HZ; + + if (cmd == MCU_CMD(FW_SCATTER)) + txq = MT_MCUQ_FWDL; + + return mt76_tx_queue_skb_raw(dev, mdev->q_mcu[txq], skb, 0); +} + +int mt7925e_mcu_init(struct mt792x_dev *dev) +{ + static const struct mt76_mcu_ops mt7925_mcu_ops = { + .headroom = sizeof(struct mt76_connac2_mcu_txd), + .mcu_skb_send_msg = mt7925_mcu_send_message, + .mcu_parse_response = mt7925_mcu_parse_response, + }; + int err; + + dev->mt76.mcu_ops = &mt7925_mcu_ops; + + err = mt792xe_mcu_fw_pmctrl(dev); + if (err) + return err; + + err = __mt792xe_mcu_drv_pmctrl(dev); + if (err) + return err; + + mt76_rmw_field(dev, dev->pcie_reg->pm, MT_PCIE_MAC_PM_L0S_DIS, 1); + + err = mt7925_run_firmware(dev); + + mt76_queue_tx_cleanup(dev, dev->mt76.q_mcu[MT_MCUQ_FWDL], false); + + return err; +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/regd.c b/drivers/net/wireless/mediatek/mt76/mt7925/regd.c new file mode 100644 index 000000000..f4beb7f52 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7925/regd.c @@ -0,0 +1,480 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2025 MediaTek Inc. */ + +#include "mt7925.h" +#include "regd.h" +#include "mcu.h" + +static bool mt7925_disable_clc; +module_param_named(disable_clc, mt7925_disable_clc, bool, 0644); +MODULE_PARM_DESC(disable_clc, "disable CLC support"); + +static struct ieee80211_regdomain mt7925_regd_ww = { + .n_reg_rules = 1, + .alpha2 = "00", + .reg_rules = { + /* IEEE 802.11b/g, channels 1..11 */ + REG_RULE(2412 - 10, 2462 + 10, 40, 6, 20, 0), + } +}; + +bool mt7925_regd_clc_supported(struct mt792x_dev *dev) +{ + if (mt7925_disable_clc || + mt76_is_usb(&dev->mt76)) + return false; + + return true; +} + +void mt7925_regd_be_ctrl(struct mt792x_dev *dev, u8 *alpha2) +{ + struct mt792x_phy *phy = &dev->phy; + struct mt7925_clc_rule_v2 *rule; + struct mt7925_clc *clc; + bool old = dev->has_eht, new = true; + u32 mtcl_conf = mt792x_acpi_get_mtcl_conf(&dev->phy, alpha2); + u8 *pos; + + if (mtcl_conf != MT792X_ACPI_MTCL_INVALID && + (((mtcl_conf >> 4) & 0x3) == 0)) { + new = false; + goto out; + } + + if (!phy->clc[MT792x_CLC_BE_CTRL]) + goto out; + + clc = (struct mt7925_clc *)phy->clc[MT792x_CLC_BE_CTRL]; + pos = clc->data; + + while (1) { + rule = (struct mt7925_clc_rule_v2 *)pos; + + if (rule->alpha2[0] == alpha2[0] && + rule->alpha2[1] == alpha2[1]) { + new = false; + break; + } + + /* Check the last one */ + if (rule->flag & BIT(0)) + break; + + pos += sizeof(*rule); + } + +out: + if (old == new) + return; + + dev->has_eht = new; + mt7925_set_stream_he_eht_caps(phy); +} + +static void +mt7925_regd_channel_update(struct wiphy *wiphy, struct mt792x_dev *dev) +{ +#define IS_UNII_INVALID(idx, sfreq, efreq, cfreq) \ + (!(dev->phy.clc_chan_conf & BIT(idx)) && (cfreq) >= (sfreq) && (cfreq) <= (efreq)) +#define MT7925_UNII_59G_IS_VALID 0x1 +#define MT7925_UNII_6G_IS_VALID 0x1e + struct ieee80211_supported_band *sband; + struct mt76_dev *mdev = &dev->mt76; + struct ieee80211_channel *ch; + u32 mtcl_conf = mt792x_acpi_get_mtcl_conf(&dev->phy, mdev->alpha2); + int i; + + if (mtcl_conf != MT792X_ACPI_MTCL_INVALID) { + if ((mtcl_conf & 0x3) == 0) + dev->phy.clc_chan_conf &= ~MT7925_UNII_59G_IS_VALID; + if (((mtcl_conf >> 2) & 0x3) == 0) + dev->phy.clc_chan_conf &= ~MT7925_UNII_6G_IS_VALID; + } + + sband = wiphy->bands[NL80211_BAND_2GHZ]; + if (!sband) + return; + + for (i = 0; i < sband->n_channels; i++) { + ch = &sband->channels[i]; + + if (!dev->has_eht) + ch->flags |= IEEE80211_CHAN_NO_EHT; + } + + sband = wiphy->bands[NL80211_BAND_5GHZ]; + if (!sband) + return; + + for (i = 0; i < sband->n_channels; i++) { + ch = &sband->channels[i]; + + /* UNII-4 */ + if (IS_UNII_INVALID(0, 5845, 5925, ch->center_freq)) + ch->flags |= IEEE80211_CHAN_DISABLED; + + if (!dev->has_eht) + ch->flags |= IEEE80211_CHAN_NO_EHT; + } + + sband = wiphy->bands[NL80211_BAND_6GHZ]; + if (!sband) + return; + + for (i = 0; i < sband->n_channels; i++) { + ch = &sband->channels[i]; + + /* UNII-5/6/7/8 */ + if (IS_UNII_INVALID(1, 5925, 6425, ch->center_freq) || + IS_UNII_INVALID(2, 6425, 6525, ch->center_freq) || + IS_UNII_INVALID(3, 6525, 6875, ch->center_freq) || + IS_UNII_INVALID(4, 6875, 7125, ch->center_freq)) + ch->flags |= IEEE80211_CHAN_DISABLED; + + if (!dev->has_eht) + ch->flags |= IEEE80211_CHAN_NO_EHT; + } +} + +static int mt7925_mcu_apply_regd(struct mt792x_dev *dev, u8 *alpha2, + enum environment_cap env) +{ + struct ieee80211_hw *hw = mt76_hw(dev); + struct wiphy *wiphy = hw->wiphy; + int ret; + + ret = mt7925_mcu_set_clc(dev, alpha2, env); + if (ret < 0) + return ret; + + mt7925_regd_be_ctrl(dev, alpha2); + mt7925_regd_channel_update(wiphy, dev); + + ret = mt7925_mcu_set_channel_domain(hw->priv); + if (ret < 0) + return ret; + + return mt7925_set_tx_sar_pwr(hw, NULL); +} + +int mt7925_mcu_regd_update(struct mt792x_dev *dev, u8 *alpha2, + enum environment_cap country_ie_env) +{ + int ret = 0; + + dev->regd_in_progress = true; + + mt792x_mutex_acquire(dev); + if (dev->regd_change) + ret = mt7925_mcu_apply_regd(dev, alpha2, country_ie_env); + mt792x_mutex_release(dev); + dev->regd_change = false; + dev->regd_in_progress = false; + wake_up(&dev->wait); + + return ret; +} +EXPORT_SYMBOL_GPL(mt7925_mcu_regd_update); + +void mt7925_regd_notifier(struct wiphy *wiphy, struct regulatory_request *req) +{ + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct mt792x_dev *dev = mt792x_hw_dev(hw); + struct mt76_connac_pm *pm = &dev->pm; + struct mt76_dev *mdev = &dev->mt76; + + if (req->initiator == NL80211_REGDOM_SET_BY_USER && + !dev->regd_user) + dev->regd_user = true; + + /* allow world regdom at the first boot only */ + if (!memcmp(req->alpha2, "00", 2) && + mdev->alpha2[0] && mdev->alpha2[1]) + return; + + /* do not need to update the same country twice */ + if (!memcmp(req->alpha2, mdev->alpha2, 2) && + dev->country_ie_env == req->country_ie_env) + return; + + memcpy(mdev->alpha2, req->alpha2, 2); + mdev->region = req->dfs_region; + dev->country_ie_env = req->country_ie_env; + + dev->regd_change = true; + + if (pm->suspended) + /* postpone the mcu update to resume */ + return; + + if (MT7925_REGD_SUPPORTED(&dev->phy)) { + mt7925_regd_update(&dev->phy, req->alpha2); + + return; + } + + mt7925_mcu_regd_update(dev, req->alpha2, + req->country_ie_env); + return; +} + +static struct sk_buff * +mt7925_regd_query_regdb(struct mt792x_phy *phy, char *alpha2) +{ + struct wiphy *wiphy = phy->mt76->hw->wiphy; + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct mt792x_dev *dev = mt792x_hw_dev(hw); + struct mt7925_clc *clc = phy->clc[MT792x_CLC_REGD]; + struct mt7925_regd_query_req *req; + struct mt7925_regd_cc *regd_cc; + struct sk_buff *ret_skb = NULL; + u8 *pos, *last_pos; + int ret = 0; + + if (!clc) + return NULL; + + pos = clc->data; + last_pos = pos + le32_to_cpu(clc->len) - sizeof(struct mt7925_clc); + while (pos < last_pos) { + u32 req_len = 0; + u32 rules_len = 0; + u32 sign_len = 4; + u32 n_reg_rules; + + if (pos + sizeof(*regd_cc) > last_pos) + break; + + regd_cc = (struct mt7925_regd_cc *)pos; + n_reg_rules = le32_to_cpu(regd_cc->n_reg_rules); + if (n_reg_rules > NL80211_MAX_SUPP_REG_RULES) + break; + + rules_len = sizeof(struct mt7925_regd_rule_header) + + sizeof(struct mt7925_regd_rule) * n_reg_rules; + + if (pos + sizeof(*regd_cc) + rules_len + sign_len > last_pos) + break; + + pos += sizeof(*regd_cc) + rules_len + sign_len; + if (memcmp(regd_cc->alpha2, alpha2, 2)) + continue; + + req_len = sizeof(*req) + rules_len + sign_len; + req = kzalloc(req_len, GFP_KERNEL); + + if (!req) + return NULL; + + req->tag = cpu_to_le16(0x6); + req->len = cpu_to_le16(req_len - 4); + req->ver = regd_cc->ver; + req->sign_type = regd_cc->sign_type; + req->size = cpu_to_le32(rules_len + sign_len); + req->n_reg_rules = regd_cc->n_reg_rules; + + memcpy(req->alpha2, regd_cc->alpha2, 2); + memcpy(req->data, regd_cc->data, rules_len + sign_len); + + ret = mt76_mcu_send_and_get_msg(&dev->mt76, + MCU_UNI_CMD(SET_POWER_LIMIT), + req, req_len, true, &ret_skb); + kfree(req); + + return ret < 0 ? NULL : ret_skb; + } + + return NULL; +} + +int mt7925_regd_update(struct mt792x_phy *phy, char *alpha2) +{ + struct wiphy *wiphy = phy->mt76->hw->wiphy; + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct mt792x_dev *dev = mt792x_hw_dev(hw); + struct mt7925_regd_rule *mt7925_rule; + struct mt76_dev *mdev = &dev->mt76; + struct ieee80211_regdomain *regd; + struct ieee80211_reg_rule *rule; + struct mt7925_regd_rule_ev *ev; + int i, num_of_rules = 0; + struct sk_buff *skb; + int ret = 0; + + if (dev->hw_full_reset) + return 0; + + if (!MT7925_REGD_SUPPORTED(phy)) + return -EOPNOTSUPP; + + mt792x_mutex_acquire(dev); + skb = mt7925_regd_query_regdb(phy, alpha2); + mt792x_mutex_release(dev); + + if (!skb) { + ret = -EINVAL; + goto err; + } + + if (skb->len < sizeof(*ev) + 4) { + ret = -EINVAL; + goto err; + } + + ev = (struct mt7925_regd_rule_ev *)(skb->data + 4); + num_of_rules = le32_to_cpu(ev->n_reg_rules); + + if (!num_of_rules || + WARN_ON_ONCE(num_of_rules > NL80211_MAX_SUPP_REG_RULES)) { + ret = -EINVAL; + goto err; + } + + if (skb->len < struct_size(ev, reg_rule, num_of_rules) + 4) { + ret = -EINVAL; + goto err; + } + + regd = kzalloc(struct_size(regd, reg_rules, num_of_rules), GFP_KERNEL); + if (!regd) { + ret = -ENOMEM; + goto err; + } + + for (i = 0; i < num_of_rules; i++) { + mt7925_rule = &ev->reg_rule[i]; + rule = ®d->reg_rules[i]; + + rule->freq_range.start_freq_khz = + MHZ_TO_KHZ(le32_to_cpu(mt7925_rule->start_freq)); + rule->freq_range.end_freq_khz = + MHZ_TO_KHZ(le32_to_cpu(mt7925_rule->end_freq)); + rule->freq_range.max_bandwidth_khz = + MHZ_TO_KHZ(le32_to_cpu(mt7925_rule->max_bw)); + /* not used by fw */ + rule->power_rule.max_antenna_gain = DBI_TO_MBI(6); + rule->power_rule.max_eirp = DBM_TO_MBM(22); + rule->flags = le32_to_cpu(mt7925_rule->flags); + } + + regd->n_reg_rules = num_of_rules; + regd->dfs_region = ev->dfs_region; + + memcpy(regd->alpha2, alpha2, 2); + memcpy(mdev->alpha2, alpha2, 2); + + dev->regd_change = true; + mt7925_mcu_regd_update(dev, alpha2, ENVIRON_ANY); + + ret = regulatory_set_wiphy_regd(wiphy, regd); + + kfree(regd); +err: + dev_kfree_skb(skb); + + if (ret < 0) + return regulatory_set_wiphy_regd(wiphy, &mt7925_regd_ww); + + return ret; +} +EXPORT_SYMBOL_GPL(mt7925_regd_update); + +static bool +mt7925_regd_is_valid_alpha2(const char *alpha2) +{ + if (!alpha2) + return false; + + if (alpha2[0] == '0' && alpha2[1] == '0') + return true; + + if (isalpha(alpha2[0]) && isalpha(alpha2[1])) + return true; + + return false; +} + +bool +mt7925_regd_is_valid_channel(struct mt792x_dev *dev, + enum nl80211_band band, + struct ieee80211_channel *chan) +{ + struct ieee80211_hw *hw = mt76_hw(dev); + struct wiphy *wiphy = hw->wiphy; + struct ieee80211_supported_band *sband; + struct ieee80211_channel *ch; + int i; + + if (!chan) + return false; + + sband = wiphy->bands[band]; + if (!sband) + return false; + + for (i = 0; i < sband->n_channels; i++) { + ch = &sband->channels[i]; + + if (ch->hw_value == chan->hw_value && + ((ch->flags & IEEE80211_CHAN_DISABLED) == 0)) + return true; + } + + return false; +} +EXPORT_SYMBOL_GPL(mt7925_regd_is_valid_channel); + +int mt7925_regd_change(struct mt792x_phy *phy, char *alpha2) +{ + struct wiphy *wiphy = phy->mt76->hw->wiphy; + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct mt792x_dev *dev = mt792x_hw_dev(hw); + struct mt76_dev *mdev = &dev->mt76; + + if (dev->hw_full_reset) + return 0; + + if (!mt7925_regd_is_valid_alpha2(alpha2) || + !mt7925_regd_clc_supported(dev) || + dev->regd_user) + return -EINVAL; + + if ((mdev->alpha2[0] && mdev->alpha2[0] != '0') && + (mdev->alpha2[1] && mdev->alpha2[1] != '0')) + return 0; + + /* do not need to update the same country twice */ + if (!memcmp(alpha2, mdev->alpha2, 2)) + return 0; + + if (MT7925_REGD_SUPPORTED(phy)) { + return mt7925_regd_update(phy, alpha2); + } else if (phy->chip_cap & MT792x_CHIP_CAP_11D_EN) { + return regulatory_hint(wiphy, alpha2); + } else { + return mt7925_mcu_set_clc(dev, alpha2, ENVIRON_INDOOR); + } +} +EXPORT_SYMBOL_GPL(mt7925_regd_change); + +int mt7925_regd_init(struct mt792x_phy *phy) +{ + struct wiphy *wiphy = phy->mt76->hw->wiphy; + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct mt792x_dev *dev = mt792x_hw_dev(hw); + struct mt76_dev *mdev = &dev->mt76; + + if (MT7925_REGD_SUPPORTED(phy)) { + wiphy->regulatory_flags |= REGULATORY_WIPHY_SELF_MANAGED | + REGULATORY_DISABLE_BEACON_HINTS; + return mt7925_regd_update(phy, "00"); + } else if (phy->chip_cap & MT792x_CHIP_CAP_11D_EN) { + wiphy->regulatory_flags |= REGULATORY_COUNTRY_IE_IGNORE | + REGULATORY_DISABLE_BEACON_HINTS; + } else { + memzero_explicit(&mdev->alpha2, sizeof(mdev->alpha2)); + } + + return 0; +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/regd.h b/drivers/net/wireless/mediatek/mt76/mt7925/regd.h new file mode 100644 index 000000000..65ef2b140 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7925/regd.h @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* Copyright (C) 2025 MediaTek Inc. */ + +#ifndef __MT7925_REGD_H +#define __MT7925_REGD_H + +#include "mt7925.h" + +struct mt7925_regd_rule_header { + u8 alpha2[2]; + u8 dfs_region; + u8 rsv[13]; +}; + +struct mt7925_regd_rule { + __le32 start_freq; + __le32 end_freq; + __le32 max_bw; + __le32 eirp; + __le32 flags; + u8 rsv[12]; +}; + +struct mt7925_regd_cc { + u8 alpha2[2]; + u8 ver; + u8 rsv; + __le32 n_reg_rules; + u8 sign_type; + u8 rsv1[7]; + u8 data[]; +}; + +struct mt7925_regd_rule_ev { + __le16 tag; + __le16 len; + __le32 n_reg_rules; + u8 dfs_region; + u8 rsv[15]; + struct mt7925_regd_rule reg_rule[]; +}; + +struct mt7925_regd_query_req { + u8 rsv[4]; + __le16 tag; + __le16 len; + u8 ver; + u8 sign_type; + u8 rsv1[2]; + __le32 size; + u8 alpha2[2]; + u8 rsv2[2]; + __le32 n_reg_rules; + u8 rsv3[64]; + u8 data[]; +}; + +#define MT7925_REGD_SUPPORTED(phy) \ + (((phy)->chip_cap & MT792x_CHIP_CAP_REGD_EN) && \ + (phy)->clc[MT792x_CLC_REGD]) + +int mt7925_mcu_regd_update(struct mt792x_dev *dev, u8 *alpha2, + enum environment_cap country_ie_env); + +void mt7925_regd_be_ctrl(struct mt792x_dev *dev, u8 *alpha2); +void mt7925_regd_notifier(struct wiphy *wiphy, struct regulatory_request *req); +bool mt7925_regd_clc_supported(struct mt792x_dev *dev); +int mt7925_regd_update(struct mt792x_phy *phy, char *alpha2); +int mt7925_regd_change(struct mt792x_phy *phy, char *alpha2); +bool mt7925_regd_is_valid_channel(struct mt792x_dev *dev, + enum nl80211_band band, + struct ieee80211_channel *chan); +int mt7925_regd_init(struct mt792x_phy *phy); + +#endif + diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/regs.h b/drivers/net/wireless/mediatek/mt76/mt7925/regs.h new file mode 100644 index 000000000..ab6c33b4a --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7925/regs.h @@ -0,0 +1,139 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* Copyright (C) 2023 MediaTek Inc. */ + +#ifndef __MT7925_REGS_H +#define __MT7925_REGS_H + +#include "../mt792x_regs.h" + +#define MT_MDP_BASE 0x820cc800 +#define MT_MDP(ofs) (MT_MDP_BASE + (ofs)) + +#define MT_MDP_DCR0 MT_MDP(0x000) +#define MT_MDP_DCR0_DAMSDU_EN BIT(15) +#define MT_MDP_DCR0_RX_HDR_TRANS_EN BIT(19) + +#define MT_MDP_DCR1 MT_MDP(0x004) +#define MT_MDP_DCR1_MAX_RX_LEN GENMASK(15, 3) + +#define MT_MDP_BNRCFR0(_band) MT_MDP(0x090 + ((_band) << 8)) +#define MT_MDP_RCFR0_MCU_RX_MGMT GENMASK(5, 4) +#define MT_MDP_RCFR0_MCU_RX_CTL_NON_BAR GENMASK(7, 6) +#define MT_MDP_RCFR0_MCU_RX_CTL_BAR GENMASK(9, 8) + +#define MT_MDP_BNRCFR1(_band) MT_MDP(0x094 + ((_band) << 8)) +#define MT_MDP_RCFR1_MCU_RX_BYPASS GENMASK(23, 22) +#define MT_MDP_RCFR1_RX_DROPPED_UCAST GENMASK(28, 27) +#define MT_MDP_RCFR1_RX_DROPPED_MCAST GENMASK(30, 29) +#define MT_MDP_TO_HIF 0 +#define MT_MDP_TO_WM 1 + +#define MT_WFDMA0_HOST_INT_ENA MT_WFDMA0(0x204) +#define MT_WFDMA0_HOST_INT_DIS MT_WFDMA0(0x22c) +#define HOST_RX_DONE_INT_ENA4 BIT(12) +#define HOST_RX_DONE_INT_ENA5 BIT(13) +#define HOST_RX_DONE_INT_ENA6 BIT(14) +#define HOST_RX_DONE_INT_ENA7 BIT(15) +#define HOST_RX_DONE_INT_ENA8 BIT(16) +#define HOST_RX_DONE_INT_ENA9 BIT(17) +#define HOST_RX_DONE_INT_ENA10 BIT(18) +#define HOST_RX_DONE_INT_ENA11 BIT(19) +#define HOST_TX_DONE_INT_ENA15 BIT(25) +#define HOST_TX_DONE_INT_ENA16 BIT(26) +#define HOST_TX_DONE_INT_ENA17 BIT(27) + +/* WFDMA interrupt */ +#define MT_INT_RX_DONE_WM2 HOST_RX_DONE_INT_ENA1 + +#define MT7925_INT_RX_DONE_DATA HOST_RX_DONE_INT_ENA2 +#define MT7925_INT_RX_DONE_WM HOST_RX_DONE_INT_ENA0 +#define MT7925_INT_RX_DONE_ALL (MT7925_INT_RX_DONE_DATA | \ + MT7925_INT_RX_DONE_WM | \ + MT_INT_RX_DONE_WM2) + +#define MT7927_INT_RX_DONE_DATA MT7927_RX_DONE_INT_ENA4 +#define MT7927_INT_RX_DONE_WM MT7927_RX_DONE_INT_ENA6 +#define MT7927_INT_RX_DONE_WM2 MT7927_RX_DONE_INT_ENA7 +#define MT7927_INT_RX_DONE_ALL (MT7927_INT_RX_DONE_DATA | \ + MT7927_INT_RX_DONE_WM | \ + MT7927_INT_RX_DONE_WM2) + +#define MT7928_INT_RX_DONE_DATA HOST_RX_DONE_INT_ENA0 +#define MT7928_INT_RX_DONE_WM HOST_RX_DONE_INT_ENA3 +#define MT7928_INT_RX_DONE_ALL (MT7928_INT_RX_DONE_DATA | \ + MT7928_INT_RX_DONE_WM | \ + MT_INT_RX_DONE_WM2) + +#define MT_INT_TX_DONE_MCU_WM (HOST_TX_DONE_INT_ENA15 | \ + HOST_TX_DONE_INT_ENA17) + +#define MT_INT_TX_DONE_FWDL HOST_TX_DONE_INT_ENA16 +#define MT_INT_TX_DONE_BAND0 HOST_TX_DONE_INT_ENA0 + +#define MT_INT_TX_DONE_MCU (MT_INT_TX_DONE_MCU_WM | \ + MT_INT_TX_DONE_FWDL) +#define MT_INT_TX_DONE_ALL (MT_INT_TX_DONE_MCU | \ + MT_INT_TX_DONE_BAND0 | \ + GENMASK(18, 4)) + +#define MT_RX_DATA_RING_BASE MT_WFDMA0(0x500) + +#define MT_INFRA_CFG_BASE 0xd1000 +#define MT_INFRA(ofs) (MT_INFRA_CFG_BASE + (ofs)) + +#define MT_HIF_REMAP_L1 0x155024 +#define MT_HIF_REMAP_L1_MASK GENMASK(31, 16) +#define MT_HIF_REMAP_L1_OFFSET GENMASK(15, 0) +#define MT_HIF_REMAP_L1_BASE GENMASK(31, 16) +#define MT_HIF_REMAP_BASE_L1 0x130000 + +#define MT_HIF_REMAP_L2 0x0120 +#define MT_HIF_REMAP_BASE_L2 0x18500000 + +#define MT_WFSYS_SW_RST_B 0x7c000140 + +#define MT7925_WTBLON_TOP_WDUCR MT_WTBLON_TOP(0x370) +#define MT7928_WTBLON_TOP_WDUCR MT_WTBLON_TOP(0x400) +#define MT_WTBLON_TOP_WDUCR_GROUP GENMASK(4, 0) + +#define MT7925_WTBL_UPDATE MT_WTBLON_TOP(0x380) +#define MT7928_WTBL_UPDATE MT_WTBLON_TOP(0x410) +#define MT_WTBL_UPDATE_WLAN_IDX GENMASK(11, 0) +#define MT_WTBL_UPDATE_ADM_COUNT_CLEAR BIT(14) + +#define MT7928_WTBL_ITCR MT_WTBLON_TOP(0x440) +#define MT7928_WTBL_ITDR0 MT_WTBLON_TOP(0x448) +#define MT7928_WTBL_ITDR1 MT_WTBLON_TOP(0x44c) + +#define MT7925_PCIE_MAC_BASE 0x10000 +#define MT7925_PCIE_MAC(ofs) (MT7925_PCIE_MAC_BASE + (ofs)) +#define MT7925_PCIE_MAC_INT_ENABLE MT7925_PCIE_MAC(0x188) +#define MT7925_PCIE_MAC_PM MT7925_PCIE_MAC(0x194) + +#define MT7928_PCIE_MAC_BASE 0x74040000 +#define MT7928_PCIE_MAC(ofs) (MT7928_PCIE_MAC_BASE + (ofs)) +#define MT7928_PCIE_MAC_INT_ENABLE MT7928_PCIE_MAC(0x188) +#define MT7928_PCIE_MAC_PM MT7928_PCIE_MAC(0x194) + +#define MT_DMASHDL_LITE_BASE 0x20026200 +#define MT_DMASHDL_LITE(ofs) (MT_DMASHDL_LITE_BASE + (ofs)) +#define MT_DMASHDL_LITE_MAIN_CONTROL MT_DMASHDL_LITE(0x004) +#define MT_DMASHDL_LITE_MAIN_CONTROL_SW_RST BIT(18) +#define MT_DMASHDL_LITE_PAGE_SIZE MT_DMASHDL_LITE(0x008) +#define MT_DMASHDL_LITE_PLE_PAGE_SIZE_MASK GENMASK(12, 0) +#define MT_DMASHDL_LITE_PSE_PAGE_SIZE_MASK GENMASK(28, 16) +#define MT_DMASHDL_LITE_PKT_MAX_SIZE MT_DMASHDL_LITE(0x00c) +#define MT_DMASHDL_LITE_PLE_PKT_MAX_SIZE_MASK GENMASK(12, 0) +#define MT_DMASHDL_LITE_PSE_PKT_MAX_SIZE_MASK GENMASK(28, 16) +#define MT_DMASHDL_LITE_GROUP_DISABLE0 MT_DMASHDL_LITE(0x010) +#define MT_DMASHDL_LITE_GROUP_DISABLE1 MT_DMASHDL_LITE(0x014) +#define MT_DMASHDL_LITE_GROUP_SN_CHK0 MT_DMASHDL_LITE(0x018) +#define MT_DMASHDL_LITE_GROUP_SN_CHK1 MT_DMASHDL_LITE(0x01c) +#define MT_DMASHDL_LITE_GROUP_UDF_CHK0 MT_DMASHDL_LITE(0x020) +#define MT_DMASHDL_LITE_GROUP_UDF_CHK1 MT_DMASHDL_LITE(0x024) +#define MT_DMASHDL_LITE_Q_MAPPING0 MT_DMASHDL_LITE(0x028) +#define MT_DMASHDL_LITE_GROUP0_QUOTA MT_DMASHDL_LITE(0x100) +#define MT_DMASHDL_LITE_GROUP_MIN_QUOTA_MASK GENMASK(12, 0) +#define MT_DMASHDL_LITE_GROUP_MAX_QUOTA_MASK GENMASK(28, 16) + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/testmode.c b/drivers/net/wireless/mediatek/mt76/mt7925/testmode.c new file mode 100644 index 000000000..22a8f1ddc --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7925/testmode.c @@ -0,0 +1,206 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear + +#include "mt7925.h" +#include "mcu.h" + +#define MT7925_EVT_RSP_LEN 512 + +enum mt7925_testmode_attr { + MT7925_TM_ATTR_UNSPEC, + MT7925_TM_ATTR_SET, + MT7925_TM_ATTR_QUERY, + MT7925_TM_ATTR_RSP, + + /* keep last */ + NUM_MT7925_TM_ATTRS, + MT7925_TM_ATTR_MAX = NUM_MT7925_TM_ATTRS - 1, +}; + +struct mt7925_tm_cmd { + u8 padding[4]; + struct uni_cmd_testmode_ctrl c; +} __packed; + +struct mt7925_tm_evt { + u32 param0; + u32 param1; +} __packed; + +static const struct nla_policy mt7925_tm_policy[NUM_MT7925_TM_ATTRS] = { + [MT7925_TM_ATTR_SET] = NLA_POLICY_EXACT_LEN(sizeof(struct mt7925_tm_cmd)), + [MT7925_TM_ATTR_QUERY] = NLA_POLICY_EXACT_LEN(sizeof(struct mt7925_tm_cmd)), +}; + +static int +mt7925_tm_set(struct mt792x_dev *dev, struct mt7925_tm_cmd *req) +{ + struct mt7925_rftest_cmd cmd; + struct mt7925_rftest_cmd *pcmd = &cmd; + bool testmode = false, normal = false; + struct mt76_connac_pm *pm = &dev->pm; + struct mt76_phy *phy = &dev->mphy; + int ret = -ENOTCONN; + + memset(pcmd, 0, sizeof(*pcmd)); + memcpy(pcmd, req, sizeof(struct mt7925_tm_cmd)); + + mutex_lock(&dev->mt76.mutex); + + if (pcmd->ctrl.action == CMD_TEST_CTRL_ACT_SWITCH_MODE) { + if (pcmd->ctrl.data.op_mode == CMD_TEST_CTRL_ACT_SWITCH_MODE_NORMAL) + normal = true; + else + testmode = true; + } + + if (testmode) { + /* Make sure testmode running on full power mode */ + pm->enable = false; + cancel_delayed_work_sync(&pm->ps_work); + cancel_work_sync(&pm->wake_work); + __mt792x_mcu_drv_pmctrl(dev); + + phy->test.state = MT76_TM_STATE_ON; + } + + if (!mt76_testmode_enabled(phy)) + goto out; + + ret = mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(TESTMODE_CTRL), &cmd, + sizeof(cmd), false); + + if (ret) + goto out; + + if (normal) { + /* Switch back to the normal world */ + phy->test.state = MT76_TM_STATE_OFF; + pm->enable = true; + } +out: + mutex_unlock(&dev->mt76.mutex); + + return ret; +} + +static int +mt7925_tm_query(struct mt792x_dev *dev, struct mt7925_tm_cmd *req, + char *evt_resp) +{ + struct mt7925_rftest_cmd cmd; + char *pcmd = (char *)&cmd; + struct sk_buff *skb = NULL; + int ret = 1; + + memset(pcmd, 0, sizeof(*pcmd)); + memcpy(pcmd + 4, (char *)&req->c, sizeof(struct uni_cmd_testmode_ctrl)); + + if (*((uint16_t *)req->padding) == MCU_UNI_CMD_TESTMODE_CTRL) + ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_UNI_QUERY(TESTMODE_CTRL), + &cmd, sizeof(cmd), true, &skb); + else if (*((uint16_t *)req->padding) == MCU_UNI_CMD_TESTMODE_RX_STAT) + ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_UNI_QUERY(TESTMODE_RX_STAT), + &cmd, sizeof(cmd), true, &skb); + + if (ret) + goto out; + + if (skb->len < MT7925_EVT_RSP_LEN + 8) { + ret = -EINVAL; + goto out; + } + + memcpy((char *)evt_resp, (char *)skb->data + 8, MT7925_EVT_RSP_LEN); + +out: + dev_kfree_skb(skb); + + return ret; +} + +int mt7925_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + void *data, int len) +{ + struct nlattr *tb[NUM_MT76_TM_ATTRS]; + struct mt76_phy *mphy = hw->priv; + struct mt792x_phy *phy = mphy->priv; + int err; + + if (!test_bit(MT76_STATE_RUNNING, &mphy->state) || + !(hw->conf.flags & IEEE80211_CONF_MONITOR)) + return -ENOTCONN; + + err = nla_parse_deprecated(tb, MT76_TM_ATTR_MAX, data, len, + mt76_tm_policy, NULL); + if (err) + return err; + + if (tb[MT76_TM_ATTR_DRV_DATA]) { + struct nlattr *drv_tb[NUM_MT7925_TM_ATTRS], *data; + int ret; + + data = tb[MT76_TM_ATTR_DRV_DATA]; + ret = nla_parse_nested_deprecated(drv_tb, + MT7925_TM_ATTR_MAX, + data, mt7925_tm_policy, + NULL); + if (ret) + return ret; + + data = drv_tb[MT7925_TM_ATTR_SET]; + if (data) + return mt7925_tm_set(phy->dev, nla_data(data)); + } + + return -EINVAL; +} + +int mt7925_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg, + struct netlink_callback *cb, void *data, int len) +{ + struct nlattr *tb[NUM_MT76_TM_ATTRS]; + struct mt76_phy *mphy = hw->priv; + struct mt792x_phy *phy = mphy->priv; + int err; + + if (!test_bit(MT76_STATE_RUNNING, &mphy->state) || + !(hw->conf.flags & IEEE80211_CONF_MONITOR) || + !mt76_testmode_enabled(mphy)) + return -ENOTCONN; + + if (cb->args[2]++ > 0) + return -ENOENT; + + err = nla_parse_deprecated(tb, MT76_TM_ATTR_MAX, data, len, + mt76_tm_policy, NULL); + if (err) + return err; + + if (tb[MT76_TM_ATTR_DRV_DATA]) { + struct nlattr *drv_tb[NUM_MT7925_TM_ATTRS], *data; + int ret; + + data = tb[MT76_TM_ATTR_DRV_DATA]; + ret = nla_parse_nested_deprecated(drv_tb, + MT7925_TM_ATTR_MAX, + data, mt7925_tm_policy, + NULL); + if (ret) + return ret; + + data = drv_tb[MT7925_TM_ATTR_QUERY]; + if (data) { + char evt_resp[MT7925_EVT_RSP_LEN]; + + err = mt7925_tm_query(phy->dev, nla_data(data), + evt_resp); + if (err) + return err; + + return nla_put(msg, MT7925_TM_ATTR_RSP, + sizeof(evt_resp), evt_resp); + } + } + + return -EINVAL; +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/usb.c b/drivers/net/wireless/mediatek/mt76/mt7925/usb.c new file mode 100644 index 000000000..1757023ea --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7925/usb.c @@ -0,0 +1,372 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2023 MediaTek Inc. */ + +#include +#include +#include + +#include "mt7925.h" +#include "mcu.h" +#include "mac.h" + +static const struct usb_device_id mt7925u_device_table[] = { + { USB_DEVICE_AND_INTERFACE_INFO(0x0e8d, 0x6639, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)MT7925_FIRMWARE_WM }, + { USB_DEVICE_AND_INTERFACE_INFO(0x0e8d, 0x7925, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)MT7925_FIRMWARE_WM }, + /* Netgear, Inc. A8500 */ + { USB_DEVICE_AND_INTERFACE_INFO(0x0846, 0x9050, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)MT7925_FIRMWARE_WM }, + /* Netgear, Inc. A9000 */ + { USB_DEVICE_AND_INTERFACE_INFO(0x0846, 0x9072, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)MT7925_FIRMWARE_WM }, + { }, +}; + +static int +mt7925u_mcu_send_message(struct mt76_dev *mdev, struct sk_buff *skb, + int cmd, int *seq) +{ + struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); + u32 pad, ep; + int ret; + + ret = mt7925_mcu_fill_message(mdev, skb, cmd, seq); + if (ret) + return ret; + + mdev->mcu.timeout = 3 * HZ; + + if (cmd != MCU_CMD(FW_SCATTER)) + ep = MT_EP_OUT_INBAND_CMD; + else + ep = MT_EP_OUT_AC_BE; + + mt792x_skb_add_usb_sdio_hdr(dev, skb, 0); + pad = round_up(skb->len, 4) + 4 - skb->len; + __skb_put_zero(skb, pad); + + ret = mt76u_bulk_msg(&dev->mt76, skb->data, skb->len, NULL, + 1000, ep); + dev_kfree_skb(skb); + + return ret; +} + +static int mt7925u_mcu_init(struct mt792x_dev *dev) +{ + static const struct mt76_mcu_ops mcu_ops = { + .headroom = MT_SDIO_HDR_SIZE + + sizeof(struct mt76_connac2_mcu_txd), + .tailroom = MT_USB_TAIL_SIZE, + .mcu_skb_send_msg = mt7925u_mcu_send_message, + .mcu_parse_response = mt7925_mcu_parse_response, + }; + int ret; + + dev->mt76.mcu_ops = &mcu_ops; + + mt76_set(dev, MT_UDMA_TX_QSEL, MT_FW_DL_EN); + ret = mt7925_run_firmware(dev); + if (ret) + return ret; + + set_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state); + mt76_clear(dev, MT_UDMA_TX_QSEL, MT_FW_DL_EN); + + return 0; +} + +static int mt7925u_mac_reset(struct mt792x_dev *dev) +{ + int err; + + if (atomic_read(&dev->mt76.bus_hung)) + return 0; + + mt792xu_reset_on_bus_error(dev); + if (atomic_read(&dev->mt76.bus_hung)) + return 0; + + mt76_txq_schedule_all(&dev->mphy); + mt76_worker_disable(&dev->mt76.tx_worker); + + set_bit(MT76_RESET, &dev->mphy.state); + set_bit(MT76_MCU_RESET, &dev->mphy.state); + + wake_up(&dev->mt76.mcu.wait); + skb_queue_purge(&dev->mt76.mcu.res_q); + + mt76u_stop_rx(&dev->mt76); + mt76u_stop_tx(&dev->mt76); + + mt792xu_wfsys_reset(dev); + + clear_bit(MT76_MCU_RESET, &dev->mphy.state); + err = mt76u_resume_rx(&dev->mt76); + if (err) + goto out; + + err = mt792xu_mcu_power_on(dev); + if (err) + goto out; + + err = mt792xu_dma_init(dev, false); + if (err) + goto out; + + mt76_wr(dev, MT_SWDEF_MODE, MT_SWDEF_NORMAL_MODE); + mt76_set(dev, MT_UDMA_TX_QSEL, MT_FW_DL_EN); + + err = mt7925_run_firmware(dev); + if (err) + goto out; + + mt76_clear(dev, MT_UDMA_TX_QSEL, MT_FW_DL_EN); + + err = mt7925_mcu_set_eeprom(dev); + if (err) + goto out; + + err = mt7925_mac_init(dev); + if (err) + goto out; + + if (is_mt7927(&dev->mt76)) { + err = mt7925_mcu_set_dbdc(&dev->mphy, true); + if (err) + goto out; + } + + err = __mt7925_start(&dev->phy); +out: + clear_bit(MT76_RESET, &dev->mphy.state); + + mt76_worker_enable(&dev->mt76.tx_worker); + + return err; +} + +static int mt7925u_probe(struct usb_interface *usb_intf, + const struct usb_device_id *id) +{ + static const struct mt76_driver_ops drv_ops = { + .txwi_size = MT_SDIO_TXD_SIZE, + .drv_flags = MT_DRV_RX_DMA_HDR | MT_DRV_HW_MGMT_TXQ | + MT_DRV_AMSDU_OFFLOAD, + .survey_flags = SURVEY_INFO_TIME_TX | + SURVEY_INFO_TIME_RX | + SURVEY_INFO_TIME_BSS_RX, + .tx_prepare_skb = mt7925_usb_sdio_tx_prepare_skb, + .tx_complete_skb = mt7925_usb_sdio_tx_complete_skb, + .tx_status_data = mt7925_usb_sdio_tx_status_data, + .rx_skb = mt7925_queue_rx_skb, + .rx_check = mt7925_rx_check, + .sta_add = mt7925_mac_sta_add, + .sta_event = mt7925_mac_sta_event, + .sta_remove = mt7925_mac_sta_remove, + .update_survey = mt792x_update_channel, + }; + static const struct mt792x_hif_ops hif_ops = { + .mcu_init = mt7925u_mcu_init, + .init_reset = mt792xu_init_reset, + .reset = mt7925u_mac_reset, + }; + static struct mt76_bus_ops bus_ops = { + .rr = mt792xu_rr, + .wr = mt792xu_wr, + .rmw = mt792xu_rmw, + .read_copy = mt76u_read_copy, + .write_copy = mt792xu_copy, + .type = MT76_BUS_USB, + }; + struct usb_device *udev = interface_to_usbdev(usb_intf); + struct ieee80211_ops *ops; + struct ieee80211_hw *hw; + struct mt792x_dev *dev; + struct mt76_dev *mdev; + u8 features; + int ret; + + ops = mt792x_get_mac80211_ops(&usb_intf->dev, &mt7925_ops, + (void *)id->driver_info, &features); + if (!ops) + return -ENOMEM; + + ops->stop = mt792xu_stop; + + mdev = mt76_alloc_device(&usb_intf->dev, sizeof(*dev), ops, &drv_ops); + if (!mdev) + return -ENOMEM; + + dev = container_of(mdev, struct mt792x_dev, mt76); + dev->fw_features = features; + dev->hif_ops = &hif_ops; + mt792xu_reset_work_init(dev); + + usb_reset_device(udev); + + usb_set_intfdata(usb_intf, dev); + + ret = __mt76u_init(mdev, usb_intf, &bus_ops); + if (ret < 0) + goto error; + + mdev->rev = (mt76_rr(dev, MT_HW_CHIPID) << 16) | + (mt76_rr(dev, MT_HW_REV) & 0xff); + dev_dbg(mdev->dev, "ASIC revision: %04x\n", mdev->rev); + + if (mt76_chip(mdev) == 0x6639) { + dev_dbg(mdev->dev, + "MT7927 raw CHIPID=0x%04x, forcing chip=0x7927\n", + mt76_chip(mdev)); + mdev->rev = (0x7927 << 16) | (mdev->rev & 0xff); + } + + if (mt76_get_field(dev, MT_CONN_ON_MISC, MT_TOP_MISC2_FW_N9_RDY)) { + ret = mt792xu_wfsys_reset(dev); + if (ret) + goto error; + } + + ret = mt792xu_mcu_power_on(dev); + if (ret) + goto error; + + ret = mt76u_alloc_mcu_queue(&dev->mt76); + if (ret) + goto error; + + ret = mt76u_alloc_queues(&dev->mt76); + if (ret) + goto error; + + ret = mt792xu_dma_init(dev, false); + if (ret) + goto error; + + hw = mt76_hw(dev); + /* check hw sg support in order to enable AMSDU */ + hw->max_tx_fragments = mdev->usb.sg_en ? MT_HW_TXP_MAX_BUF_NUM : 1; + + ret = mt7925_register_device(dev); + if (ret) + goto error; + + return 0; + +error: + mt76u_queues_deinit(&dev->mt76); + mt792xu_reset_work_cleanup(dev); + + usb_set_intfdata(usb_intf, NULL); + + mt76_free_device(&dev->mt76); + + return ret; +} + +#ifdef CONFIG_PM +static int mt7925u_suspend(struct usb_interface *intf, pm_message_t state) +{ + struct mt792x_dev *dev = usb_get_intfdata(intf); + struct mt76_connac_pm *pm = &dev->pm; + int err, ret; + + pm->suspended = true; + dev->hif_resumed = false; + flush_work(&dev->reset_work); + cancel_delayed_work_sync(&dev->mlo_pm_work); + + mt76_connac_mcu_set_hif_suspend(&dev->mt76, true, false); + ret = wait_event_timeout(dev->wait, + dev->hif_idle, 3 * HZ); + if (!ret) { + err = -ETIMEDOUT; + goto failed; + } + + mt76u_stop_rx(&dev->mt76); + mt76u_stop_tx(&dev->mt76); + + return 0; + +failed: + pm->suspended = false; + + if (err < 0) + mt792x_reset(&dev->mt76); + + return err; +} + +static int mt7925u_resume(struct usb_interface *intf) +{ + struct mt792x_dev *dev = usb_get_intfdata(intf); + struct mt76_connac_pm *pm = &dev->pm; + bool reinit = true; + int err, i, ret; + + dev->hif_idle = false; + for (i = 0; i < 10; i++) { + u32 val = mt76_rr(dev, MT_WF_SW_DEF_CR_USB_MCU_EVENT); + + if (!(val & MT_WF_SW_SER_TRIGGER_SUSPEND)) { + reinit = false; + break; + } + if (val & MT_WF_SW_SER_DONE_SUSPEND) { + mt76_wr(dev, MT_WF_SW_DEF_CR_USB_MCU_EVENT, 0); + break; + } + + msleep(20); + } + + if (reinit || mt792x_dma_need_reinit(dev)) { + err = mt792xu_dma_init(dev, true); + if (err) + goto failed; + } + + err = mt76u_resume_rx(&dev->mt76); + if (err < 0) + goto failed; + + mt76_connac_mcu_set_hif_suspend(&dev->mt76, false, false); + ret = wait_event_timeout(dev->wait, + dev->hif_resumed, 3 * HZ); + if (!ret) + err = -ETIMEDOUT; +failed: + pm->suspended = false; + + if (err < 0) + mt792x_reset(&dev->mt76); + + return err; +} +#endif /* CONFIG_PM */ + +MODULE_DEVICE_TABLE(usb, mt7925u_device_table); +MODULE_FIRMWARE(MT7925_FIRMWARE_WM); +MODULE_FIRMWARE(MT7925_ROM_PATCH); + +static struct usb_driver mt7925u_driver = { + .name = KBUILD_MODNAME, + .id_table = mt7925u_device_table, + .probe = mt7925u_probe, + .disconnect = mt792xu_disconnect, +#ifdef CONFIG_PM + .suspend = mt7925u_suspend, + .resume = mt7925u_resume, + .reset_resume = mt7925u_resume, +#endif /* CONFIG_PM */ + .soft_unbind = 1, + .disable_hub_initiated_lpm = 1, +}; +module_usb_driver(mt7925u_driver); + +MODULE_AUTHOR("Lorenzo Bianconi "); +MODULE_DESCRIPTION("MediaTek MT7925U (USB) wireless driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/mediatek/mt76/mt792x.h b/drivers/net/wireless/mediatek/mt76/mt792x.h new file mode 100644 index 000000000..9efc251cb --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt792x.h @@ -0,0 +1,660 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* Copyright (C) 2023 MediaTek Inc. */ + +#ifndef __MT792X_H +#define __MT792X_H + +#include +#include + +#include "mt76_connac_mcu.h" +#include "mt792x_regs.h" +#include "mt792x_acpi_sar.h" + +#define MT792x_PM_TIMEOUT (HZ / 12) +#define MT792x_HW_SCAN_TIMEOUT (HZ / 10) + +#define MT792x_MAX_INTERFACES 4 +#define MT792x_WTBL_SIZE 20 +#define MT792x_WTBL_RESERVED (MT792x_WTBL_SIZE - 1) +#define MT792x_WTBL_STA (MT792x_WTBL_RESERVED - MT792x_MAX_INTERFACES) + +#define MT792x_CFEND_RATE_DEFAULT 0x49 /* OFDM 24M */ +#define MT792x_CFEND_RATE_11B 0x03 /* 11B LP, 11M */ + +#define MT792x_FW_TAG_FEATURE 4 +#define MT792x_FW_CAP_NAN BIT(5) +#define MT792x_FW_CAP_CNM BIT(7) + +#define MT792x_CHIP_CAP_CLC_EVT_EN BIT(0) +#define MT792x_CHIP_CAP_RSSI_NOTIFY_EVT_EN BIT(1) +#define MT792x_CHIP_CAP_WF_RF_PIN_CTRL_EVT_EN BIT(3) +#define MT792x_CHIP_CAP_11D_EN BIT(4) +#define MT792x_CHIP_CAP_REGD_EN BIT(5) +#define MT792x_CHIP_CAP_MLO_EN BIT(8) +#define MT792x_CHIP_CAP_MLO_EML_EN BIT(9) + +/* NOTE: used to map mt76_rates. idx may change if firmware expands table */ +#define MT792x_BASIC_RATES_TBL 14 + +#define MT792x_WATCHDOG_TIME (HZ / 4) + +#define MT792x_DRV_OWN_RETRY_COUNT 10 +#define MT792x_MCU_INIT_RETRY_COUNT 10 +#define MT792x_WFSYS_INIT_RETRY_COUNT 2 + +#define MT7902_FIRMWARE_WM "mediatek/WIFI_RAM_CODE_MT7902_1.bin" +#define MT7920_FIRMWARE_WM "mediatek/WIFI_RAM_CODE_MT7961_1a.bin" +#define MT7921_FIRMWARE_WM "mediatek/WIFI_RAM_CODE_MT7961_1.bin" +#define MT7922_FIRMWARE_WM "mediatek/WIFI_RAM_CODE_MT7922_1.bin" +#define MT7925_FIRMWARE_WM "mediatek/mt7925/WIFI_RAM_CODE_MT7925_1_1.bin" +#define MT7927_FIRMWARE_WM "mediatek/mt7927/WIFI_RAM_CODE_MT6639_2_1.bin" +#define MT7928_FIRMWARE_WM "mediatek/mt7928/WIFI_RAM_CODE_MT7935_1_1.bin" + +#define MT7902_ROM_PATCH "mediatek/WIFI_MT7902_patch_mcu_1_1_hdr.bin" +#define MT7920_ROM_PATCH "mediatek/WIFI_MT7961_patch_mcu_1a_2_hdr.bin" +#define MT7921_ROM_PATCH "mediatek/WIFI_MT7961_patch_mcu_1_2_hdr.bin" +#define MT7922_ROM_PATCH "mediatek/WIFI_MT7922_patch_mcu_1_1_hdr.bin" +#define MT7925_ROM_PATCH "mediatek/mt7925/WIFI_MT7925_PATCH_MCU_1_1_hdr.bin" +#define MT7927_ROM_PATCH "mediatek/mt7927/WIFI_MT6639_PATCH_MCU_2_1_hdr.bin" +#define MT7928_ROM_PATCH "mediatek/mt7928/WIFI_MT7935_PATCH_MCU_1_1_hdr.bin" + +#define MT7928_CB_ROM_PATCH "mediatek/mt7928/CBMCU_CODE_MT7935_1_1.bin" +#define MT7928_PHY_RAM "mediatek/mt7928/WIFI_MT7935_PHY_RAM_CODE_1_1.bin" + +#define MT792x_SDIO_HDR_TX_BYTES GENMASK(15, 0) +#define MT792x_SDIO_HDR_PKT_TYPE GENMASK(17, 16) + +struct mt792x_vif; +struct mt792x_sta; + +struct mt792x_realease_info { + __le16 len; + u8 pad_len; + u8 tag; +} __packed; + +struct mt792x_fw_features { + u8 segment; + u8 data; + u8 rsv[14]; +} __packed; + +enum { + MT792x_CLC_POWER, + MT792x_CLC_POWER_EXT, + MT792x_CLC_BE_CTRL, + MT792x_CLC_REGD, + MT792x_CLC_MAX_NUM, +}; + +enum mt792x_reg_power_type { + MT_AP_UNSET = 0, + MT_AP_DEFAULT, + MT_AP_LPI, + MT_AP_SP, + MT_AP_VLP, +}; + +enum mt792x_mlo_pm_state { + MT792x_MLO_LINK_DISASSOC, + MT792x_MLO_LINK_ASSOC, + MT792x_MLO_CHANGED_PS_PENDING, + MT792x_MLO_CHANGED_PS, +}; + +DECLARE_EWMA(avg_signal, 10, 8) + +struct mt792x_link_sta { + struct mt76_wcid wcid; /* must be first */ + struct rcu_head rcu_head; + + u32 airtime_ac[8]; + + int ack_signal; + struct ewma_avg_signal avg_ack_signal; + + unsigned long last_txs; + + struct mt76_connac_sta_key_conf bip; + + struct mt792x_sta *sta; + + struct ieee80211_link_sta *pri_link; +}; + +struct mt792x_sta_nan_sched { + /* protects NAN peer schedule state */ + u16 committed_dw; + u32 sch_idx; + bool idx_assigned; + unsigned long ndp_ctx_bitmap; + bool ndp_ctx_assigned; + u8 ndp_ctx_id; /* assigned NDP context ID (for NDI sta) */ + struct { + u8 map_id; + struct cfg80211_chan_def chans[CFG80211_NAN_SCHED_NUM_TIME_SLOTS]; + } maps[CFG80211_NAN_MAX_PEER_MAPS]; +}; + +struct mt792x_sta { + struct mt792x_link_sta deflink; /* must be first */ + struct mt792x_link_sta __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS]; + + struct mt792x_vif *vif; + + u16 valid_links; + u8 deflink_id; + + /* NAN peer schedule */ + struct mt792x_sta_nan_sched nan_sched; +}; + +DECLARE_EWMA(rssi, 10, 8); + +struct mt792x_chanctx { + struct mt792x_bss_conf *bss_conf; +}; + +struct mt792x_bss_conf { + struct mt76_vif_link mt76; /* must be first */ + struct mt792x_vif *vif; + struct ewma_rssi rssi; + struct ieee80211_tx_queue_params queue_params[IEEE80211_NUM_ACS]; + unsigned int link_id; +}; + +struct mt792x_nan_conf { + u8 master_pref; + u8 bands; + u8 cluster_id[ETH_ALEN]; + u32 discovery_beacon_interval; + bool enable_dw_notification; +}; + +struct mt792x_nan { + struct mt792x_nan_conf conf; + + /* Scheduler */ + struct cfg80211_chan_def local_sched[CFG80211_NAN_SCHED_NUM_TIME_SLOTS]; + u32 seq_id; + + /* Connection index bitmap, up to NAN_MAX_CONN_CFG peers */ + unsigned long conn_bitmap; +}; + +struct mt792x_vif { + struct mt792x_bss_conf bss_conf; /* must be first */ + struct mt792x_bss_conf __rcu *link_conf[IEEE80211_MLD_MAX_NUM_LINKS]; + + struct mt792x_sta sta; + struct mt792x_sta *wep_sta; + + struct mt792x_phy *phy; + u16 valid_links; + u8 deflink_id; + enum mt792x_mlo_pm_state mlo_pm_state; + + struct work_struct csa_work; + struct timer_list csa_timer; + + struct mt792x_nan nan; +}; + +struct mt792x_phy { + struct mt76_phy *mt76; + struct mt792x_dev *dev; + + struct ieee80211_sband_iftype_data iftype[NUM_NL80211_BANDS][NUM_NL80211_IFTYPES]; + + u64 omac_mask; + + u16 noise; + + s16 coverage_class; + u8 slottime; + + u32 rx_ampdu_ts; + u32 ampdu_ref; + + struct mt76_mib_stats mib; + + u8 sta_work_count; + u8 clc_chan_conf; + enum mt792x_reg_power_type power_type; + + struct sk_buff_head scan_event_list; + struct delayed_work scan_work; +#ifdef CONFIG_ACPI + void *acpisar; +#endif + void *clc[MT792x_CLC_MAX_NUM]; + u64 chip_cap; + u16 eml_cap; + + struct work_struct roc_work; + struct timer_list roc_timer; + wait_queue_head_t roc_wait; + u8 roc_token_id; + bool roc_grant; +}; + +struct mt792x_irq_map { + u32 host_irq_enable; + struct { + u32 all_complete_mask; + u32 mcu_complete_mask; + } tx; + struct { + u32 all_complete_mask; + u32 data_complete_mask; + u32 wm_complete_mask; + u32 wm2_complete_mask; + } rx; +}; + +struct mt792x_dma_ring { + u8 qid; + u16 n_desc; + u32 ring_base; +}; + +struct mt792x_dma_layout { + struct mt792x_dma_ring tx_data0; + struct mt792x_dma_ring tx_mcu; + struct mt792x_dma_ring tx_fwdl; + struct mt792x_dma_ring tx_done; + struct mt792x_dma_ring rx_data; + struct mt792x_dma_ring rx_mcu; +}; + +#define mt792x_dma_ring(_qid, _n_desc, _ring_base) \ + { .qid = (_qid), .n_desc = (_n_desc), .ring_base = (_ring_base) } + +#define mt792x_init_reset(dev) ((dev)->hif_ops->init_reset(dev)) +#define mt792x_dev_reset(dev) ((dev)->hif_ops->reset(dev)) +#define mt792x_mcu_init(dev) ((dev)->hif_ops->mcu_init(dev)) +#define __mt792x_mcu_drv_pmctrl(dev) ((dev)->hif_ops->drv_own(dev)) +#define __mt792x_mcu_fw_pmctrl(dev) ((dev)->hif_ops->fw_own(dev)) + +struct mt792x_hif_ops { + int (*init_reset)(struct mt792x_dev *dev); + int (*reset)(struct mt792x_dev *dev); + int (*mcu_init)(struct mt792x_dev *dev); + int (*drv_own)(struct mt792x_dev *dev); + int (*fw_own)(struct mt792x_dev *dev); +}; + +struct mt792x_pcie_reg { + u32 imask; + u32 pm; +}; + +struct mt792x_dev { + union { /* must be first */ + struct mt76_dev mt76; + struct mt76_phy mphy; + }; + + struct mac_address macaddr_list[8]; + + const struct mt76_bus_ops *bus_ops; + struct mt792x_phy phy; + + struct work_struct reset_work; + bool hw_full_reset:1; + bool hw_init_done:1; + bool fw_assert:1; + bool has_eht:1; + bool regd_user:1; + bool regd_in_progress:1; + bool aspm_supported:1; + bool hif_idle:1; + bool hif_resumed:1; + bool regd_change:1; + bool skip_wpdma_reinit:1; + wait_queue_head_t wait; + + struct work_struct init_work; + struct work_struct usb_reset_work; + atomic_t usb_reset_pending; + + u8 fw_debug; + u8 fw_features; + + struct mt76_connac_pm pm; + struct mt76_connac_coredump coredump; + const struct mt792x_hif_ops *hif_ops; + const struct mt792x_irq_map *irq_map; + const struct mt792x_pcie_reg *pcie_reg; + + struct work_struct ipv6_ns_work; + struct delayed_work mlo_pm_work; + /* IPv6 addresses for WoWLAN */ + struct sk_buff_head ipv6_ns_list; + + enum environment_cap country_ie_env; + u32 backup_l1; + u32 backup_l2; + + struct ieee80211_chanctx_conf *new_ctx; + + struct ieee80211_vif *nan_vif; + const struct ieee80211_iface_combination *iface_combinations; + int n_iface_combinations; +}; + +static inline struct mt792x_bss_conf * +mt792x_vif_to_link(struct mt792x_vif *mvif, u8 link_id) +{ + struct ieee80211_vif *vif; + struct mt792x_bss_conf *bss_conf; + + vif = container_of((void *)mvif, struct ieee80211_vif, drv_priv); + + if (!ieee80211_vif_is_mld(vif) || + link_id >= IEEE80211_LINK_UNSPECIFIED) + return &mvif->bss_conf; + + bss_conf = rcu_dereference_protected(mvif->link_conf[link_id], + lockdep_is_held(&mvif->phy->dev->mt76.mutex)); + + return bss_conf ? bss_conf : &mvif->bss_conf; +} + +static inline struct mt792x_link_sta * +mt792x_sta_to_link(struct mt792x_sta *msta, u8 link_id) +{ + struct ieee80211_vif *vif; + + vif = container_of((void *)msta->vif, struct ieee80211_vif, drv_priv); + + if (!ieee80211_vif_is_mld(vif) || + link_id >= IEEE80211_LINK_UNSPECIFIED) + return &msta->deflink; + + return rcu_dereference_protected(msta->link[link_id], + lockdep_is_held(&msta->vif->phy->dev->mt76.mutex)); +} + +static inline struct mt792x_bss_conf * +mt792x_link_conf_to_mconf(struct ieee80211_bss_conf *link_conf) +{ + struct ieee80211_vif *vif = link_conf->vif; + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + + return mt792x_vif_to_link(mvif, link_conf->link_id); +} + +static inline struct ieee80211_bss_conf * +mt792x_vif_to_bss_conf(struct ieee80211_vif *vif, unsigned int link_id) +{ + if (!ieee80211_vif_is_mld(vif) || + link_id >= IEEE80211_LINK_UNSPECIFIED) + return &vif->bss_conf; + + return link_conf_dereference_protected(vif, link_id); +} + +static inline struct ieee80211_link_sta * +mt792x_sta_to_link_sta(struct ieee80211_vif *vif, struct ieee80211_sta *sta, + unsigned int link_id) +{ + if (!ieee80211_vif_is_mld(vif) || + link_id >= IEEE80211_LINK_UNSPECIFIED) + return &sta->deflink; + + return link_sta_dereference_protected(sta, link_id); +} + +static inline struct mt792x_dev * +mt792x_hw_dev(struct ieee80211_hw *hw) +{ + struct mt76_phy *phy = hw->priv; + + return container_of(phy->dev, struct mt792x_dev, mt76); +} + +static inline struct mt792x_phy * +mt792x_hw_phy(struct ieee80211_hw *hw) +{ + struct mt76_phy *phy = hw->priv; + + return phy->priv; +} + +static inline void +mt792x_get_status_freq_info(struct mt76_rx_status *status, u8 chfreq) +{ + if (chfreq > 180) { + status->band = NL80211_BAND_6GHZ; + chfreq = (chfreq - 181) * 4 + 1; + } else if (chfreq > 14) { + status->band = NL80211_BAND_5GHZ; + } else { + status->band = NL80211_BAND_2GHZ; + } + status->freq = ieee80211_channel_to_frequency(chfreq, status->band); +} + +static inline bool mt792x_dma_need_reinit(struct mt792x_dev *dev) +{ + return !mt76_get_field(dev, MT_WFDMA_DUMMY_CR, MT_WFDMA_NEED_REINIT); +} + +#define mt792x_mutex_acquire(dev) \ + mt76_connac_mutex_acquire(&(dev)->mt76, &(dev)->pm) +#define mt792x_mutex_release(dev) \ + mt76_connac_mutex_release(&(dev)->mt76, &(dev)->pm) + +void mt792x_stop(struct ieee80211_hw *hw, bool suspend); +void mt792x_pm_wake_work(struct work_struct *work); +void mt792x_pm_power_save_work(struct work_struct *work); +void mt792x_reset(struct mt76_dev *mdev); +void mt792x_update_channel(struct mt76_phy *mphy); +void mt792x_mac_reset_counters(struct mt792x_phy *phy); +void mt792x_mac_init_band(struct mt792x_dev *dev, u8 band); +void mt792x_mac_assoc_rssi(struct mt792x_dev *dev, struct sk_buff *skb); +struct mt76_wcid *mt792x_rx_get_wcid(struct mt792x_dev *dev, u16 idx, + bool unicast); +void mt792x_mac_update_mib_stats(struct mt792x_phy *phy); +void mt792x_mac_set_timeing(struct mt792x_phy *phy); +void mt792x_mac_work(struct work_struct *work); +void mt792x_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); +void mt792x_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, + struct sk_buff *skb); +int mt792x_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + unsigned int link_id, u16 queue, + const struct ieee80211_tx_queue_params *params); +int mt792x_get_stats(struct ieee80211_hw *hw, + struct ieee80211_low_level_stats *stats); +u64 mt792x_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif); +void mt792x_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u64 timestamp); +void mt792x_tx_worker(struct mt76_worker *w); +void mt792x_roc_timer(struct timer_list *timer); +void mt792x_csa_timer(struct timer_list *timer); +void mt792x_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 queues, bool drop); +int mt792x_get_txpower(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + unsigned int link_id, int *dbm); +int mt792x_assign_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_chanctx_conf *ctx); +void mt792x_unassign_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_chanctx_conf *ctx); +void mt792x_set_wakeup(struct ieee80211_hw *hw, bool enabled); +void mt792x_get_et_strings(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 sset, u8 *data); +int mt792x_get_et_sset_count(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + int sset); +void mt792x_get_et_stats(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ethtool_stats *stats, u64 *data); +void mt792x_sta_statistics(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct station_info *sinfo); +void mt792x_set_coverage_class(struct ieee80211_hw *hw, int radio_idx, + s16 coverage_class); +void mt792x_dma_cleanup(struct mt792x_dev *dev); +int mt792x_dma_alloc_queues(struct mt792x_dev *dev, + const struct mt792x_dma_layout *layout); +int mt792x_dma_enable(struct mt792x_dev *dev); +int mt792x_wpdma_reset(struct mt792x_dev *dev, bool force); +int mt792x_wpdma_reinit_cond(struct mt792x_dev *dev); +int mt792x_dma_disable(struct mt792x_dev *dev, bool force); +irqreturn_t mt792x_irq_handler(int irq, void *dev_instance); +void mt792x_rx_poll_complete(struct mt76_dev *mdev, enum mt76_rxq_id q); +int mt792x_poll_tx(struct napi_struct *napi, int budget); +int mt792x_poll_rx(struct napi_struct *napi, int budget); +void mt792x_irq_tasklet(unsigned long data); +int mt792x_wfsys_reset(struct mt792x_dev *dev); +int mt792x_tx_stats_show(struct seq_file *file, void *data); +int mt792x_queues_acq(struct seq_file *s, void *data); +int mt792x_queues_read(struct seq_file *s, void *data); +int mt792x_pm_stats(struct seq_file *s, void *data); +int mt792x_pm_idle_timeout_set(void *data, u64 val); +int mt792x_pm_idle_timeout_get(void *data, u64 *val); +int mt792x_init_wiphy(struct ieee80211_hw *hw); +struct ieee80211_ops * +mt792x_get_mac80211_ops(struct device *dev, + const struct ieee80211_ops *mac80211_ops, + void *drv_data, u8 *fw_features); +int mt792x_init_wcid(struct mt792x_dev *dev); +int mt792x_mcu_drv_pmctrl(struct mt792x_dev *dev); +int mt792x_mcu_fw_pmctrl(struct mt792x_dev *dev); +void mt792x_mac_link_bss_remove(struct mt792x_dev *dev, + struct mt792x_bss_conf *mconf, + struct mt792x_link_sta *mlink); +void mt792x_config_mac_addr_list(struct mt792x_dev *dev); + +static inline char *mt792x_ram_name(struct mt792x_dev *dev) +{ + switch (mt76_chip(&dev->mt76)) { + case 0x7902: + return MT7902_FIRMWARE_WM; + case 0x7920: + return MT7920_FIRMWARE_WM; + case 0x7922: + return MT7922_FIRMWARE_WM; + case 0x7925: + return MT7925_FIRMWARE_WM; + case 0x7927: + return MT7927_FIRMWARE_WM; + case 0x7928: + return MT7928_FIRMWARE_WM; + default: + return MT7921_FIRMWARE_WM; + } +} + +static inline char *mt792x_patch_name(struct mt792x_dev *dev) +{ + switch (mt76_chip(&dev->mt76)) { + case 0x7902: + return MT7902_ROM_PATCH; + case 0x7920: + return MT7920_ROM_PATCH; + case 0x7922: + return MT7922_ROM_PATCH; + case 0x7925: + return MT7925_ROM_PATCH; + case 0x7927: + return MT7927_ROM_PATCH; + case 0x7928: + return MT7928_ROM_PATCH; + default: + return MT7921_ROM_PATCH; + } +} + +static inline char *mt792x_cb_patch_name(struct mt792x_dev *dev) +{ + switch (mt76_chip(&dev->mt76)) { + case 0x7928: + return MT7928_CB_ROM_PATCH; + default: + return NULL; + } +} + +static inline char *mt792x_phy_ram_name(struct mt792x_dev *dev) +{ + switch (mt76_chip(&dev->mt76)) { + case 0x7928: + return MT7928_PHY_RAM; + default: + return NULL; + } +} + +int mt792x_load_firmware(struct mt792x_dev *dev); + +/* usb */ +#define MT_USB_TYPE_VENDOR (USB_TYPE_VENDOR | 0x1f) +#define MT_USB_TYPE_UHW_VENDOR (USB_TYPE_VENDOR | 0x1e) +int mt792xu_dma_init(struct mt792x_dev *dev, bool resume); +int mt792xu_mcu_power_on(struct mt792x_dev *dev); +int mt792xu_wfsys_reset(struct mt792x_dev *dev); +int mt792xu_init_reset(struct mt792x_dev *dev); +void mt792xu_reset_work_init(struct mt792x_dev *dev); +void mt792xu_reset_work_cleanup(struct mt792x_dev *dev); +int mt792xu_check_bus(struct mt792x_dev *dev); +int mt792xu_reset_on_bus_error(struct mt792x_dev *dev); +u32 mt792xu_rr(struct mt76_dev *dev, u32 addr); +void mt792xu_wr(struct mt76_dev *dev, u32 addr, u32 val); +u32 mt792xu_rmw(struct mt76_dev *dev, u32 addr, u32 mask, u32 val); +void mt792xu_copy(struct mt76_dev *dev, u32 offset, const void *data, int len); +void mt792xu_disconnect(struct usb_interface *usb_intf); +void mt792xu_stop(struct ieee80211_hw *hw, bool suspend); + +static inline void +mt792x_skb_add_usb_sdio_hdr(struct mt792x_dev *dev, struct sk_buff *skb, + int type) +{ + u32 hdr, len; + + len = mt76_is_usb(&dev->mt76) ? skb->len : skb->len + sizeof(hdr); + hdr = FIELD_PREP(MT792x_SDIO_HDR_TX_BYTES, len) | + FIELD_PREP(MT792x_SDIO_HDR_PKT_TYPE, type); + + put_unaligned_le32(hdr, skb_push(skb, sizeof(hdr))); +} + +int __mt792xe_mcu_drv_pmctrl(struct mt792x_dev *dev); +int mt792xe_mcu_drv_pmctrl(struct mt792x_dev *dev); +int mt792xe_mcu_fw_pmctrl(struct mt792x_dev *dev); + +#ifdef CONFIG_ACPI +int mt792x_init_acpi_sar(struct mt792x_dev *dev); +int mt792x_init_acpi_sar_power(struct mt792x_phy *phy, bool set_default); +u8 mt792x_acpi_get_flags(struct mt792x_phy *phy); +u32 mt792x_acpi_get_mtcl_conf(struct mt792x_phy *phy, char *alpha2); +#else +static inline int mt792x_init_acpi_sar(struct mt792x_dev *dev) +{ + return 0; +} + +static inline int mt792x_init_acpi_sar_power(struct mt792x_phy *phy, + bool set_default) +{ + return 0; +} + +static inline u8 mt792x_acpi_get_flags(struct mt792x_phy *phy) +{ + return 0; +} + +static inline u32 mt792x_acpi_get_mtcl_conf(struct mt792x_phy *phy, char *alpha2) +{ + return MT792X_ACPI_MTCL_INVALID; +} +#endif + +#endif /* __MT7925_H */ diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.c b/drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.c new file mode 100644 index 000000000..946dd7956 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.c @@ -0,0 +1,486 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2023 MediaTek Inc. */ + +#include +#include "mt792x.h" + +static const char * const cc_list_all[] = { + "00", "EU", "AR", "AU", "AZ", "BY", "BO", "BR", + "CA", "CL", "CN", "ID", "JP", "MY", "MX", "ME", + "MA", "NZ", "NG", "PH", "RU", "RS", "SG", "KR", + "TW", "TH", "UA", "GB", "US", "VN", "KH", "PY", +}; + +static const char * const cc_list_eu[] = { + "AD", "AT", "BE", "BG", "CY", "CZ", "HR", "DK", + "EE", "FI", "FR", "DE", "GR", "HU", "IS", "IE", + "IT", "LV", "LI", "LT", "LU", "MC", "MT", "NL", + "NO", "PL", "PT", "RO", "SK", "SI", "ES", "SE", + "CH", +}; + +static const char * const cc_list_be[] = { + "AR", "BR", "BY", "CL", "IQ", "MX", "OM", "RU", + "RW", "VN", "KR", "UA", "", "", "", "", + "EU", "AT", "CN", "CA", "TW", "NZ", "PH", "UK", + "US", +}; + +static int +mt792x_acpi_read(struct mt792x_dev *dev, u8 *method, u8 **tbl, u32 *len) +{ + struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL }; + struct mt76_dev *mdev = &dev->mt76; + union acpi_object *sar_root; + acpi_handle root, handle; + acpi_status status; + u32 i = 0; + int ret; + + root = ACPI_HANDLE(mdev->dev); + if (!root) + return -EOPNOTSUPP; + + status = acpi_get_handle(root, method, &handle); + if (ACPI_FAILURE(status)) + return -EIO; + + status = acpi_evaluate_object(handle, NULL, NULL, &buf); + if (ACPI_FAILURE(status)) + return -EIO; + + sar_root = buf.pointer; + if (sar_root->type != ACPI_TYPE_PACKAGE || + sar_root->package.count < 4 || + sar_root->package.elements[0].type != ACPI_TYPE_INTEGER) { + dev_err(mdev->dev, "sar cnt = %d\n", + sar_root->package.count); + ret = -EINVAL; + goto free; + } + + if (!*tbl) { + *tbl = devm_kzalloc(mdev->dev, sar_root->package.count, + GFP_KERNEL); + if (!*tbl) { + ret = -ENOMEM; + goto free; + } + } + + if (len) + *len = sar_root->package.count; + + for (i = 0; i < sar_root->package.count; i++) { + union acpi_object *sar_unit = &sar_root->package.elements[i]; + + if (sar_unit->type != ACPI_TYPE_INTEGER) + break; + + *(*tbl + i) = (u8)sar_unit->integer.value; + } + + ret = i == sar_root->package.count ? 0 : -EINVAL; +free: + kfree(sar_root); + + return ret; +} + +/* MTCL : Country List Table for 6G band */ +/* MTCL : Country List Table for 6G band and 11BE */ +static int +mt792x_asar_acpi_read_mtcl(struct mt792x_dev *dev, u8 **table, u8 *version) +{ + int len, ret; + + ret = mt792x_acpi_read(dev, MT792x_ACPI_MTCL, table, &len); + if (ret) + return ret; + + if (len == sizeof(struct mt792x_asar_cl)) + *version = ((struct mt792x_asar_cl *)*table)->version; + else if (len == sizeof(struct mt792x_asar_cl_v3)) + *version = ((struct mt792x_asar_cl_v3 *)*table)->version; + else + return -EINVAL; + + return ret; +} + +/* MTDS : Dynamic SAR Power Table */ +static int +mt792x_asar_acpi_read_mtds(struct mt792x_dev *dev, u8 **table, u8 version) +{ + int len, ret, sarlen, prelen, tblcnt; + bool enable; + + ret = mt792x_acpi_read(dev, MT792x_ACPI_MTDS, table, &len); + if (ret) + return ret; + + /* Table content validation */ + switch (version) { + case 1: + enable = ((struct mt792x_asar_dyn *)*table)->enable; + sarlen = sizeof(struct mt792x_asar_dyn_limit); + prelen = sizeof(struct mt792x_asar_dyn); + break; + case 2: + enable = ((struct mt792x_asar_dyn_v2 *)*table)->enable; + sarlen = sizeof(struct mt792x_asar_dyn_limit_v2); + prelen = sizeof(struct mt792x_asar_dyn_v2); + break; + default: + return -EINVAL; + } + + tblcnt = (len - prelen) / sarlen; + if (!enable || + tblcnt > MT792x_ASAR_MAX_DYN || tblcnt < MT792x_ASAR_MIN_DYN) + return -EINVAL; + + return 0; +} + +/* MTGS : Geo SAR Power Table */ +static int +mt792x_asar_acpi_read_mtgs(struct mt792x_dev *dev, u8 **table, u8 version) +{ + int len, ret, sarlen, prelen, tblcnt; + + ret = mt792x_acpi_read(dev, MT792x_ACPI_MTGS, table, &len); + if (ret) + return ret; + + /* Table content validation */ + switch (version) { + case 1: + sarlen = sizeof(struct mt792x_asar_geo_limit); + prelen = sizeof(struct mt792x_asar_geo); + break; + case 2: + sarlen = sizeof(struct mt792x_asar_geo_limit_v2); + prelen = sizeof(struct mt792x_asar_geo_v2); + break; + default: + return -EINVAL; + } + + tblcnt = (len - prelen) / sarlen; + if (tblcnt > MT792x_ASAR_MAX_GEO || tblcnt < MT792x_ASAR_MIN_GEO) + return -EINVAL; + + return 0; +} + +/* MTFG : Flag Table */ +static int +mt792x_asar_acpi_read_mtfg(struct mt792x_dev *dev, u8 **table) +{ + int len, ret; + + ret = mt792x_acpi_read(dev, MT792x_ACPI_MTFG, table, &len); + if (ret) + return ret; + + if (len < MT792x_ASAR_MIN_FG) + return -EINVAL; + + return 0; +} + +int mt792x_init_acpi_sar(struct mt792x_dev *dev) +{ + struct mt792x_acpi_sar *asar; + int ret; + + asar = devm_kzalloc(dev->mt76.dev, sizeof(*asar), GFP_KERNEL); + if (!asar) + return -ENOMEM; + + ret = mt792x_asar_acpi_read_mtcl(dev, (u8 **)&asar->countrylist, &asar->ver); + if (ret) { + devm_kfree(dev->mt76.dev, asar->countrylist); + asar->countrylist = NULL; + } + + ret = mt792x_asar_acpi_read_mtds(dev, (u8 **)&asar->dyn, asar->ver); + if (ret) { + devm_kfree(dev->mt76.dev, asar->dyn); + asar->dyn = NULL; + } + + /* MTGS is optional */ + ret = mt792x_asar_acpi_read_mtgs(dev, (u8 **)&asar->geo, asar->ver); + if (ret) { + devm_kfree(dev->mt76.dev, asar->geo); + asar->geo = NULL; + } + + /* MTFG is optional */ + ret = mt792x_asar_acpi_read_mtfg(dev, (u8 **)&asar->fg); + if (ret) { + devm_kfree(dev->mt76.dev, asar->fg); + asar->fg = NULL; + } + dev->phy.acpisar = asar; + + return 0; +} +EXPORT_SYMBOL_GPL(mt792x_init_acpi_sar); + +static s8 +mt792x_asar_get_geo_pwr(struct mt792x_phy *phy, + enum nl80211_band band, s8 dyn_power) +{ + struct mt792x_acpi_sar *asar = phy->acpisar; + struct mt792x_asar_geo_band *band_pwr; + s8 geo_power; + u8 idx, max; + + if (!asar->geo) + return dyn_power; + + switch (phy->mt76->dev->region) { + case NL80211_DFS_FCC: + idx = 0; + break; + case NL80211_DFS_ETSI: + idx = 1; + break; + default: /* WW */ + idx = 2; + break; + } + + if (asar->ver == 1) { + band_pwr = &asar->geo->tbl[idx].band[0]; + max = ARRAY_SIZE(asar->geo->tbl[idx].band); + } else { + band_pwr = &asar->geo_v2->tbl[idx].band[0]; + max = ARRAY_SIZE(asar->geo_v2->tbl[idx].band); + } + + switch (band) { + case NL80211_BAND_2GHZ: + idx = 0; + break; + case NL80211_BAND_5GHZ: + idx = 1; + break; + case NL80211_BAND_6GHZ: + idx = 2; + break; + default: + return dyn_power; + } + + if (idx >= max) + return dyn_power; + + geo_power = (band_pwr + idx)->pwr; + dyn_power += (band_pwr + idx)->offset; + + return min(geo_power, dyn_power); +} + +static s8 +mt792x_asar_range_pwr(struct mt792x_phy *phy, + const struct cfg80211_sar_freq_ranges *range, + u8 idx) +{ + const struct cfg80211_sar_capa *capa = phy->mt76->hw->wiphy->sar_capa; + struct mt792x_acpi_sar *asar = phy->acpisar; + u8 *limit, band, max; + + if (!capa) + return 127; + + if (asar->ver == 1) { + limit = &asar->dyn->tbl[0].frp[0]; + max = ARRAY_SIZE(asar->dyn->tbl[0].frp); + } else { + limit = &asar->dyn_v2->tbl[0].frp[0]; + max = ARRAY_SIZE(asar->dyn_v2->tbl[0].frp); + } + + if (idx >= max) + return 127; + + if (range->start_freq >= 5945) + band = NL80211_BAND_6GHZ; + else if (range->start_freq >= 5150) + band = NL80211_BAND_5GHZ; + else + band = NL80211_BAND_2GHZ; + + return mt792x_asar_get_geo_pwr(phy, band, limit[idx]); +} + +int mt792x_init_acpi_sar_power(struct mt792x_phy *phy, bool set_default) +{ + const struct cfg80211_sar_capa *capa = phy->mt76->hw->wiphy->sar_capa; + int i; + + if (!phy->acpisar || !((struct mt792x_acpi_sar *)phy->acpisar)->dyn) + return 0; + + /* When ACPI SAR enabled in HW, we should apply rules for .frp + * 1. w/o .sar_specs : set ACPI SAR power as the defatul value + * 2. w/ .sar_specs : set power with min(.sar_specs, ACPI_SAR) + */ + for (i = 0; i < capa->num_freq_ranges; i++) { + struct mt76_freq_range_power *frp = &phy->mt76->frp[i]; + + frp->range = set_default ? &capa->freq_ranges[i] : frp->range; + if (!frp->range) + continue; + + frp->power = min_t(s8, set_default ? 127 : frp->power, + mt792x_asar_range_pwr(phy, frp->range, i)); + } + + return 0; +} +EXPORT_SYMBOL_GPL(mt792x_init_acpi_sar_power); + +u8 mt792x_acpi_get_flags(struct mt792x_phy *phy) +{ + struct mt792x_acpi_sar *acpisar = phy->acpisar; + struct mt792x_asar_fg *fg; + struct { + u8 acpi_idx; + u8 chip_idx; + } map[] = { + { 1, 1 }, + { 4, 2 }, + }; + u8 flags = BIT(0); + int i, j; + + if (!acpisar) + return 0; + + fg = acpisar->fg; + if (!fg) + return flags; + + /* pickup necessary settings per device and + * translate the index of bitmap for chip command. + */ + for (i = 0; i < fg->nr_flag; i++) { + for (j = 0; j < ARRAY_SIZE(map); j++) { + if (fg->flag[i] == map[j].acpi_idx) { + flags |= BIT(map[j].chip_idx); + break; + } + } + } + + return flags; +} +EXPORT_SYMBOL_GPL(mt792x_acpi_get_flags); + +static u32 +mt792x_acpi_get_mtcl_map_v3(int row, int column, struct mt792x_asar_cl_v3 *cl) +{ + u32 config = 0; + u8 mode_be = 0; + + mode_be = (cl->mode_be > 0x02) ? 0 : cl->mode_be; + + if (cl->version > 2 && cl->clbe[row] & BIT(column)) + config |= (mode_be & 0x3) << 4; + + return config; +} + +static u32 +mt792x_acpi_get_mtcl_map(int row, int column, struct mt792x_asar_cl *cl) +{ + u32 config = 0; + u8 mode_6g, mode_5g9; + + mode_6g = (cl->mode_6g > 0x02) ? 0 : cl->mode_6g; + mode_5g9 = (cl->mode_5g9 > 0x01) ? 0 : cl->mode_5g9; + + if ((cl->cl6g[row] & BIT(column)) || cl->mode_6g == 0x02) + config |= (mode_6g & 0x3) << 2; + if (cl->version > 1 && cl->cl5g9[row] & BIT(column)) + config |= (mode_5g9 & 0x3); + + return config; +} + +static u32 +mt792x_acpi_parse_mtcl_tbl_v3(struct mt792x_phy *phy, char *alpha2) +{ + struct mt792x_acpi_sar *sar = phy->acpisar; + struct mt792x_asar_cl_v3 *cl = sar->countrylist_v3; + int col, row, i; + + if (sar->ver != 3) + goto out; + + if (!cl) + return MT792X_ACPI_MTCL_INVALID; + + for (i = 0; i < ARRAY_SIZE(cc_list_be); i++) { + col = 7 - i % 8; + row = i / 8; + if (!memcmp(cc_list_be[i], alpha2, 2)) + return mt792x_acpi_get_mtcl_map_v3(row, col, cl); + } + for (i = 0; i < ARRAY_SIZE(cc_list_eu); i++) { + if (!memcmp(cc_list_eu[i], alpha2, 2)) + return mt792x_acpi_get_mtcl_map_v3(3, 7, cl); + } + +out: + /* Depends on driver */ + return 0x20; +} + +static u32 +mt792x_acpi_parse_mtcl_tbl(struct mt792x_phy *phy, char *alpha2) +{ + struct mt792x_acpi_sar *sar = phy->acpisar; + struct mt792x_asar_cl *cl = sar->countrylist; + int col, row, i; + + if (!cl) + return MT792X_ACPI_MTCL_INVALID; + + for (i = 0; i < ARRAY_SIZE(cc_list_all); i++) { + col = 7 - i % 8; + row = i / 8; + if (!memcmp(cc_list_all[i], alpha2, 2)) + return mt792x_acpi_get_mtcl_map(row, col, cl); + } + + for (i = 0; i < ARRAY_SIZE(cc_list_eu); i++) + if (!memcmp(cc_list_eu[i], alpha2, 2)) + return mt792x_acpi_get_mtcl_map(0, 6, cl); + + return mt792x_acpi_get_mtcl_map(0, 7, cl); +} + +u32 mt792x_acpi_get_mtcl_conf(struct mt792x_phy *phy, char *alpha2) +{ + struct mt792x_acpi_sar *sar = phy->acpisar; + u32 config = 0; + + if (!sar) + return MT792X_ACPI_MTCL_INVALID; + + config = mt792x_acpi_parse_mtcl_tbl_v3(phy, alpha2); + + if (config == MT792X_ACPI_MTCL_INVALID) + return MT792X_ACPI_MTCL_INVALID; + + config |= mt792x_acpi_parse_mtcl_tbl(phy, alpha2); + + return config; +} +EXPORT_SYMBOL_GPL(mt792x_acpi_get_mtcl_conf); diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.h b/drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.h new file mode 100644 index 000000000..474033073 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.h @@ -0,0 +1,123 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* Copyright (C) 2023 MediaTek Inc. */ + +#ifndef __MT7921_ACPI_SAR_H +#define __MT7921_ACPI_SAR_H + +#define MT792x_ASAR_MIN_DYN 1 +#define MT792x_ASAR_MAX_DYN 8 +#define MT792x_ASAR_MIN_GEO 3 +#define MT792x_ASAR_MAX_GEO 8 +#define MT792x_ASAR_MIN_FG 8 + +#define MT792x_ACPI_MTCL "MTCL" +#define MT792x_ACPI_MTDS "MTDS" +#define MT792x_ACPI_MTGS "MTGS" +#define MT792x_ACPI_MTFG "MTFG" + +#define MT792X_ACPI_MTCL_INVALID 0xffffffff + +struct mt792x_asar_dyn_limit { + u8 idx; + u8 frp[5]; +} __packed; + +struct mt792x_asar_dyn { + u8 names[4]; + u8 enable; + u8 nr_tbl; + DECLARE_FLEX_ARRAY(struct mt792x_asar_dyn_limit, tbl); +} __packed; + +struct mt792x_asar_dyn_limit_v2 { + u8 idx; + u8 frp[11]; +} __packed; + +struct mt792x_asar_dyn_v2 { + u8 names[4]; + u8 enable; + u8 rsvd; + u8 nr_tbl; + DECLARE_FLEX_ARRAY(struct mt792x_asar_dyn_limit_v2, tbl); +} __packed; + +struct mt792x_asar_geo_band { + u8 pwr; + u8 offset; +} __packed; + +struct mt792x_asar_geo_limit { + u8 idx; + /* 0:2G, 1:5G */ + struct mt792x_asar_geo_band band[2]; +} __packed; + +struct mt792x_asar_geo { + u8 names[4]; + u8 version; + u8 nr_tbl; + DECLARE_FLEX_ARRAY(struct mt792x_asar_geo_limit, tbl); +} __packed; + +struct mt792x_asar_geo_limit_v2 { + u8 idx; + /* 0:2G, 1:5G, 2:6G */ + struct mt792x_asar_geo_band band[3]; +} __packed; + +struct mt792x_asar_geo_v2 { + u8 names[4]; + u8 version; + u8 rsvd; + u8 nr_tbl; + DECLARE_FLEX_ARRAY(struct mt792x_asar_geo_limit_v2, tbl); +} __packed; + +struct mt792x_asar_cl_v3 { + u8 names[4]; + u8 version; + u8 mode_6g; + u8 cl6g[6]; + u8 mode_5g9; + u8 cl5g9[6]; + u8 mode_be; + u8 clbe[6]; +} __packed; + +struct mt792x_asar_cl { + u8 names[4]; + u8 version; + u8 mode_6g; + u8 cl6g[6]; + u8 mode_5g9; + u8 cl5g9[6]; +} __packed; + +struct mt792x_asar_fg { + u8 names[4]; + u8 version; + u8 rsvd; + u8 nr_flag; + u8 rsvd1; + u8 flag[]; +} __packed; + +struct mt792x_acpi_sar { + u8 ver; + union { + struct mt792x_asar_dyn *dyn; + struct mt792x_asar_dyn_v2 *dyn_v2; + }; + union { + struct mt792x_asar_geo *geo; + struct mt792x_asar_geo_v2 *geo_v2; + }; + union { + struct mt792x_asar_cl *countrylist; + struct mt792x_asar_cl_v3 *countrylist_v3; + }; + struct mt792x_asar_fg *fg; +}; + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_core.c b/drivers/net/wireless/mediatek/mt76/mt792x_core.c new file mode 100644 index 000000000..0ad33f74c --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt792x_core.c @@ -0,0 +1,1172 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2023 MediaTek Inc. */ + +#include +#include +#include + +#include "mt792x.h" +#include "dma.h" + +static const struct ieee80211_iface_limit if_limits[] = { + { + .max = MT792x_MAX_INTERFACES, + .types = BIT(NL80211_IFTYPE_STATION) + }, + { + .max = 1, + .types = BIT(NL80211_IFTYPE_AP) + } +}; + +static const struct ieee80211_iface_combination if_comb[] = { + { + .limits = if_limits, + .n_limits = ARRAY_SIZE(if_limits), + .max_interfaces = MT792x_MAX_INTERFACES, + .num_different_channels = 1, + .beacon_int_infra_match = true, + }, +}; + +static const struct ieee80211_iface_limit if_limits_chanctx_mcc[] = { + { + .max = 2, + .types = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_P2P_CLIENT) + }, + { + .max = 1, + .types = BIT(NL80211_IFTYPE_P2P_GO) + }, + { + .max = 1, + .types = BIT(NL80211_IFTYPE_P2P_DEVICE) + } +}; + +static const struct ieee80211_iface_limit if_limits_chanctx_scc[] = { + { + .max = 2, + .types = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_P2P_CLIENT) + }, + { + .max = 1, + .types = BIT(NL80211_IFTYPE_AP) + }, + { + .max = 1, + .types = BIT(NL80211_IFTYPE_P2P_DEVICE) + } +}; + +static const struct ieee80211_iface_limit if_limits_nan_mcc[] = { + { + .max = 2, + .types = BIT(NL80211_IFTYPE_STATION), + }, + { + .max = 1, + .types = BIT(NL80211_IFTYPE_NAN), + }, + { + .max = 2, + .types = BIT(NL80211_IFTYPE_NAN_DATA), + }, +}; + +static const struct ieee80211_iface_limit if_limits_nan_scc[] = { + { + .max = 2, + .types = BIT(NL80211_IFTYPE_STATION), + }, + { + .max = 1, + .types = BIT(NL80211_IFTYPE_NAN), + }, + { + .max = 2, + .types = BIT(NL80211_IFTYPE_NAN_DATA), + }, + { + .max = 1, + .types = BIT(NL80211_IFTYPE_AP), + }, +}; + +static const struct ieee80211_iface_combination if_comb_chanctx_base[] = { + { + .limits = if_limits_chanctx_mcc, + .n_limits = ARRAY_SIZE(if_limits_chanctx_mcc), + .max_interfaces = 3, + .num_different_channels = 2, + .beacon_int_infra_match = false, + }, + { + .limits = if_limits_chanctx_scc, + .n_limits = ARRAY_SIZE(if_limits_chanctx_scc), + .max_interfaces = 3, + .num_different_channels = 1, + .beacon_int_infra_match = false, + } +}; + +static const struct ieee80211_iface_combination if_comb_chanctx_nan[] = { + { + .limits = if_limits_nan_mcc, + .n_limits = ARRAY_SIZE(if_limits_nan_mcc), + .max_interfaces = MT792x_MAX_INTERFACES, + .num_different_channels = 2, + .beacon_int_infra_match = false, + }, + { + .limits = if_limits_nan_scc, + .n_limits = ARRAY_SIZE(if_limits_nan_scc), + .max_interfaces = MT792x_MAX_INTERFACES, + .num_different_channels = 1, + .beacon_int_infra_match = false, + }, +}; + +static int mt792x_setup_iface_combinations(struct mt792x_dev *dev, + struct wiphy *wiphy) +{ + const bool cnm = !!(dev->fw_features & MT792x_FW_CAP_CNM); + const bool nan = !!(dev->fw_features & MT792x_FW_CAP_NAN); + const int n_base = ARRAY_SIZE(if_comb_chanctx_base); + const int n_nan = ARRAY_SIZE(if_comb_chanctx_nan); + struct ieee80211_iface_combination *comb; + + if (!cnm) { + dev->iface_combinations = if_comb; + dev->n_iface_combinations = ARRAY_SIZE(if_comb); + return 0; + } + + /* CNM enabled, NAN optional */ + if (!nan) { + dev->iface_combinations = if_comb_chanctx_base; + dev->n_iface_combinations = ARRAY_SIZE(if_comb_chanctx_base); + return 0; + } + + /* CNM + NAN: dynamically build base + nan list */ + comb = devm_kcalloc(&wiphy->dev, n_base + n_nan, sizeof(*comb), + GFP_KERNEL); + if (!comb) + return -ENOMEM; + + memcpy(comb, if_comb_chanctx_base, sizeof(if_comb_chanctx_base)); + memcpy(comb + n_base, if_comb_chanctx_nan, sizeof(if_comb_chanctx_nan)); + + dev->iface_combinations = comb; + dev->n_iface_combinations = n_base + n_nan; + + return 0; +} + +void mt792x_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, + struct sk_buff *skb) +{ + struct mt792x_dev *dev = mt792x_hw_dev(hw); + struct mt76_phy *mphy = hw->priv; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_vif *vif = info->control.vif; + struct mt76_wcid *wcid = &dev->mt76.global_wcid; + u8 link_id; + int qid; + + if (control->sta) { + struct mt792x_link_sta *mlink; + struct mt792x_sta *sta; + link_id = u32_get_bits(info->control.flags, + IEEE80211_TX_CTRL_MLO_LINK); + sta = (struct mt792x_sta *)control->sta->drv_priv; + mlink = mt792x_sta_to_link(sta, link_id); + wcid = &mlink->wcid; + } + + if (vif && !control->sta) { + struct mt792x_vif *mvif; + + mvif = (struct mt792x_vif *)vif->drv_priv; + wcid = &mvif->sta.deflink.wcid; + } + + if (vif && control->sta && ieee80211_vif_is_mld(vif) && + !(info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP)) { + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ieee80211_link_sta *link_sta; + struct ieee80211_bss_conf *conf; + + link_id = wcid->link_id; + rcu_read_lock(); + conf = rcu_dereference(vif->link_conf[link_id]); + memcpy(hdr->addr2, conf->addr, ETH_ALEN); + + link_sta = rcu_dereference(control->sta->link[link_id]); + memcpy(hdr->addr1, link_sta->addr, ETH_ALEN); + + if (vif->type == NL80211_IFTYPE_STATION) + memcpy(hdr->addr3, conf->bssid, ETH_ALEN); + rcu_read_unlock(); + } + + if (mt76_connac_pm_ref(mphy, &dev->pm)) { + mt76_tx(mphy, control->sta, wcid, skb); + mt76_connac_pm_unref(mphy, &dev->pm); + return; + } + + qid = skb_get_queue_mapping(skb); + if (qid >= MT_TXQ_PSD) { + qid = IEEE80211_AC_BE; + skb_set_queue_mapping(skb, qid); + } + + mt76_connac_pm_queue_skb(hw, &dev->pm, wcid, skb); +} +EXPORT_SYMBOL_GPL(mt792x_tx); + +void mt792x_stop(struct ieee80211_hw *hw, bool suspend) +{ + struct mt792x_dev *dev = mt792x_hw_dev(hw); + struct mt792x_phy *phy = mt792x_hw_phy(hw); + + cancel_delayed_work_sync(&phy->mt76->mac_work); + + cancel_delayed_work_sync(&dev->pm.ps_work); + cancel_work_sync(&dev->pm.wake_work); + cancel_work_sync(&dev->reset_work); + mt76_connac_free_pending_tx_skbs(&dev->pm, NULL); + + if (is_connac2(&dev->mt76)) { + mt792x_mutex_acquire(dev); + mt76_connac_mcu_set_mac_enable(&dev->mt76, 0, false, false); + mt792x_mutex_release(dev); + } + + clear_bit(MT76_STATE_RUNNING, &phy->mt76->state); +} +EXPORT_SYMBOL_GPL(mt792x_stop); + +void mt792x_mac_link_bss_remove(struct mt792x_dev *dev, + struct mt792x_bss_conf *mconf, + struct mt792x_link_sta *mlink) +{ + struct ieee80211_vif *vif = container_of((void *)mconf->vif, + struct ieee80211_vif, drv_priv); + struct ieee80211_bss_conf *link_conf; + int idx = mlink->wcid.idx; + + link_conf = mt792x_vif_to_bss_conf(vif, mconf->link_id); + + mt76_connac_free_pending_tx_skbs(&dev->pm, &mlink->wcid); + mt76_connac_mcu_uni_add_dev(&dev->mphy, link_conf, &mconf->mt76, + &mlink->wcid, false); + + rcu_assign_pointer(dev->mt76.wcid[idx], NULL); + + dev->mt76.vif_mask &= ~BIT_ULL(mconf->mt76.idx); + mconf->vif->phy->omac_mask &= ~BIT_ULL(mconf->mt76.omac_idx); + + spin_lock_bh(&dev->mt76.sta_poll_lock); + if (!list_empty(&mlink->wcid.poll_list)) + list_del_init(&mlink->wcid.poll_list); + spin_unlock_bh(&dev->mt76.sta_poll_lock); + + mt76_wcid_cleanup(&dev->mt76, &mlink->wcid); +} +EXPORT_SYMBOL_GPL(mt792x_mac_link_bss_remove); + +void mt792x_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + struct mt792x_dev *dev = mt792x_hw_dev(hw); + struct mt792x_bss_conf *mconf; + + mt792x_mutex_acquire(dev); + + mconf = mt792x_link_conf_to_mconf(&vif->bss_conf); + mt792x_mac_link_bss_remove(dev, mconf, &mvif->sta.deflink); + + mt792x_mutex_release(dev); +} +EXPORT_SYMBOL_GPL(mt792x_remove_interface); + +int mt792x_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + unsigned int link_id, u16 queue, + const struct ieee80211_tx_queue_params *params) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + + /* no need to update right away, we'll get BSS_CHANGED_QOS */ + queue = mt76_connac_lmac_mapping(queue); + mvif->bss_conf.queue_params[queue] = *params; + + return 0; +} +EXPORT_SYMBOL_GPL(mt792x_conf_tx); + +int mt792x_get_stats(struct ieee80211_hw *hw, + struct ieee80211_low_level_stats *stats) +{ + struct mt792x_phy *phy = mt792x_hw_phy(hw); + struct mt76_mib_stats *mib = &phy->mib; + + mt792x_mutex_acquire(phy->dev); + + stats->dot11RTSSuccessCount = mib->rts_cnt; + stats->dot11RTSFailureCount = mib->rts_retries_cnt; + stats->dot11FCSErrorCount = mib->fcs_err_cnt; + stats->dot11ACKFailureCount = mib->ack_fail_cnt; + + mt792x_mutex_release(phy->dev); + + return 0; +} +EXPORT_SYMBOL_GPL(mt792x_get_stats); + +u64 mt792x_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + struct mt792x_dev *dev = mt792x_hw_dev(hw); + u8 omac_idx = mvif->bss_conf.mt76.omac_idx; + union { + u64 t64; + u32 t32[2]; + } tsf; + u16 n; + + mt792x_mutex_acquire(dev); + + n = omac_idx > HW_BSSID_MAX ? HW_BSSID_0 : omac_idx; + /* TSF software read */ + mt76_set(dev, MT_LPON_TCR(0, n), MT_LPON_TCR_SW_MODE); + tsf.t32[0] = mt76_rr(dev, MT_LPON_UTTR0(0)); + tsf.t32[1] = mt76_rr(dev, MT_LPON_UTTR1(0)); + + mt792x_mutex_release(dev); + + return tsf.t64; +} +EXPORT_SYMBOL_GPL(mt792x_get_tsf); + +void mt792x_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u64 timestamp) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + struct mt792x_dev *dev = mt792x_hw_dev(hw); + u8 omac_idx = mvif->bss_conf.mt76.omac_idx; + union { + u64 t64; + u32 t32[2]; + } tsf = { .t64 = timestamp, }; + u16 n; + + mt792x_mutex_acquire(dev); + + n = omac_idx > HW_BSSID_MAX ? HW_BSSID_0 : omac_idx; + mt76_wr(dev, MT_LPON_UTTR0(0), tsf.t32[0]); + mt76_wr(dev, MT_LPON_UTTR1(0), tsf.t32[1]); + /* TSF software overwrite */ + mt76_set(dev, MT_LPON_TCR(0, n), MT_LPON_TCR_SW_WRITE); + + mt792x_mutex_release(dev); +} +EXPORT_SYMBOL_GPL(mt792x_set_tsf); + +void mt792x_tx_worker(struct mt76_worker *w) +{ + struct mt792x_dev *dev = container_of(w, struct mt792x_dev, + mt76.tx_worker); + + if (!mt76_connac_pm_ref(&dev->mphy, &dev->pm)) { + queue_work(dev->mt76.wq, &dev->pm.wake_work); + return; + } + + mt76_txq_schedule_all(&dev->mphy); + mt76_connac_pm_unref(&dev->mphy, &dev->pm); +} +EXPORT_SYMBOL_GPL(mt792x_tx_worker); + +void mt792x_roc_timer(struct timer_list *timer) +{ + struct mt792x_phy *phy = timer_container_of(phy, timer, roc_timer); + + ieee80211_queue_work(phy->mt76->hw, &phy->roc_work); +} +EXPORT_SYMBOL_GPL(mt792x_roc_timer); + +void mt792x_csa_timer(struct timer_list *timer) +{ + struct mt792x_vif *mvif = timer_container_of(mvif, timer, csa_timer); + + ieee80211_queue_work(mvif->phy->mt76->hw, &mvif->csa_work); +} +EXPORT_SYMBOL_GPL(mt792x_csa_timer); + +void mt792x_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 queues, bool drop) +{ + struct mt792x_dev *dev = mt792x_hw_dev(hw); + + wait_event_timeout(dev->mt76.tx_wait, + !mt76_has_tx_pending(&dev->mphy), HZ / 2); +} +EXPORT_SYMBOL_GPL(mt792x_flush); + +int mt792x_get_txpower(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + unsigned int link_id, int *dbm) +{ + struct mt76_power_limits limits = {}; + struct ieee80211_bss_conf *link_conf; + struct ieee80211_channel *chan; + struct mt792x_bss_conf *mconf; + struct mt792x_vif *mvif; + struct mt76_phy *phy; + s8 max_power; + + if (!vif) + return mt76_get_txpower(hw, vif, link_id, dbm); + + mvif = (struct mt792x_vif *)vif->drv_priv; + phy = mvif->phy->mt76; + + mt792x_mutex_acquire(mvif->phy->dev); + + link_conf = mt792x_vif_to_bss_conf(vif, link_id); + mconf = link_conf ? mt792x_link_conf_to_mconf(link_conf) : NULL; + if (mconf && mconf->mt76.ctx && mconf->mt76.ctx->def.chan) + chan = mconf->mt76.ctx->def.chan; + else + /* Fall back to the current PHY chandef if the requested link + * does not have a valid channel context. + */ + chan = phy->chandef.chan; + + mt792x_mutex_release(mvif->phy->dev); + + if (!chan) + return -EINVAL; + + max_power = mt76_connac_get_rate_power_limit(phy, chan, &limits); + *dbm = DIV_ROUND_UP(max_power, 2); + + return 0; +} +EXPORT_SYMBOL_GPL(mt792x_get_txpower); + +int mt792x_assign_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_chanctx_conf *ctx) +{ + struct mt792x_chanctx *mctx = (struct mt792x_chanctx *)ctx->drv_priv; + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + struct mt792x_dev *dev = mt792x_hw_dev(hw); + + mutex_lock(&dev->mt76.mutex); + mvif->bss_conf.mt76.ctx = ctx; + mctx->bss_conf = &mvif->bss_conf; + mutex_unlock(&dev->mt76.mutex); + + return 0; +} +EXPORT_SYMBOL_GPL(mt792x_assign_vif_chanctx); + +void mt792x_unassign_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_chanctx_conf *ctx) +{ + struct mt792x_chanctx *mctx = (struct mt792x_chanctx *)ctx->drv_priv; + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + struct mt792x_dev *dev = mt792x_hw_dev(hw); + + mutex_lock(&dev->mt76.mutex); + mctx->bss_conf = NULL; + mvif->bss_conf.mt76.ctx = NULL; + mutex_unlock(&dev->mt76.mutex); + + if (vif->bss_conf.csa_active) { + timer_delete_sync(&mvif->csa_timer); + cancel_work_sync(&mvif->csa_work); + } +} +EXPORT_SYMBOL_GPL(mt792x_unassign_vif_chanctx); + +void mt792x_set_wakeup(struct ieee80211_hw *hw, bool enabled) +{ + struct mt792x_dev *dev = mt792x_hw_dev(hw); + struct mt76_dev *mdev = &dev->mt76; + + device_set_wakeup_enable(mdev->dev, enabled); +} +EXPORT_SYMBOL_GPL(mt792x_set_wakeup); + +static const char mt792x_gstrings_stats[][ETH_GSTRING_LEN] = { + /* tx counters */ + "tx_ampdu_cnt", + "tx_mpdu_attempts", + "tx_mpdu_success", + "tx_pkt_ebf_cnt", + "tx_pkt_ibf_cnt", + "tx_ampdu_len:0-1", + "tx_ampdu_len:2-10", + "tx_ampdu_len:11-19", + "tx_ampdu_len:20-28", + "tx_ampdu_len:29-37", + "tx_ampdu_len:38-46", + "tx_ampdu_len:47-55", + "tx_ampdu_len:56-79", + "tx_ampdu_len:80-103", + "tx_ampdu_len:104-127", + "tx_ampdu_len:128-151", + "tx_ampdu_len:152-175", + "tx_ampdu_len:176-199", + "tx_ampdu_len:200-223", + "tx_ampdu_len:224-247", + "ba_miss_count", + "tx_beamformer_ppdu_iBF", + "tx_beamformer_ppdu_eBF", + "tx_beamformer_rx_feedback_all", + "tx_beamformer_rx_feedback_he", + "tx_beamformer_rx_feedback_vht", + "tx_beamformer_rx_feedback_ht", + "tx_msdu_pack_1", + "tx_msdu_pack_2", + "tx_msdu_pack_3", + "tx_msdu_pack_4", + "tx_msdu_pack_5", + "tx_msdu_pack_6", + "tx_msdu_pack_7", + "tx_msdu_pack_8", + /* rx counters */ + "rx_mpdu_cnt", + "rx_ampdu_cnt", + "rx_ampdu_bytes_cnt", + "rx_ba_cnt", + /* per vif counters */ + "v_tx_mode_cck", + "v_tx_mode_ofdm", + "v_tx_mode_ht", + "v_tx_mode_ht_gf", + "v_tx_mode_vht", + "v_tx_mode_he_su", + "v_tx_mode_he_ext_su", + "v_tx_mode_he_tb", + "v_tx_mode_he_mu", + "v_tx_mode_eht_su", + "v_tx_mode_eht_trig", + "v_tx_mode_eht_mu", + "v_tx_bw_20", + "v_tx_bw_40", + "v_tx_bw_80", + "v_tx_bw_160", + "v_tx_bw_320", + "v_tx_mcs_0", + "v_tx_mcs_1", + "v_tx_mcs_2", + "v_tx_mcs_3", + "v_tx_mcs_4", + "v_tx_mcs_5", + "v_tx_mcs_6", + "v_tx_mcs_7", + "v_tx_mcs_8", + "v_tx_mcs_9", + "v_tx_mcs_10", + "v_tx_mcs_11", + "v_tx_mcs_12", + "v_tx_mcs_13", + "v_tx_nss_1", + "v_tx_nss_2", + "v_tx_nss_3", + "v_tx_nss_4", +}; + +void mt792x_get_et_strings(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 sset, u8 *data) +{ + if (sset != ETH_SS_STATS) + return; + + memcpy(data, mt792x_gstrings_stats, sizeof(mt792x_gstrings_stats)); + + data += sizeof(mt792x_gstrings_stats); + page_pool_ethtool_stats_get_strings(data); +} +EXPORT_SYMBOL_GPL(mt792x_get_et_strings); + +int mt792x_get_et_sset_count(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + int sset) +{ + if (sset != ETH_SS_STATS) + return 0; + + return ARRAY_SIZE(mt792x_gstrings_stats) + + page_pool_ethtool_stats_get_count(); +} +EXPORT_SYMBOL_GPL(mt792x_get_et_sset_count); + +static void +mt792x_ethtool_worker(void *wi_data, struct ieee80211_sta *sta) +{ + struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv; + struct mt76_ethtool_worker_info *wi = wi_data; + + if (msta->vif->bss_conf.mt76.idx != wi->idx) + return; + + mt76_ethtool_worker(wi, &msta->deflink.wcid.stats, true); +} + +void mt792x_get_et_stats(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ethtool_stats *stats, u64 *data) +{ + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + int stats_size = ARRAY_SIZE(mt792x_gstrings_stats); + struct mt792x_phy *phy = mt792x_hw_phy(hw); + struct mt792x_dev *dev = phy->dev; + struct mt76_mib_stats *mib = &phy->mib; + struct mt76_ethtool_worker_info wi = { + .data = data, + .idx = mvif->bss_conf.mt76.idx, + }; + int i, ei = 0; + + mt792x_mutex_acquire(dev); + + mt792x_mac_update_mib_stats(phy); + + data[ei++] = mib->tx_ampdu_cnt; + data[ei++] = mib->tx_mpdu_attempts_cnt; + data[ei++] = mib->tx_mpdu_success_cnt; + data[ei++] = mib->tx_pkt_ebf_cnt; + data[ei++] = mib->tx_pkt_ibf_cnt; + + /* Tx ampdu stat */ + for (i = 0; i < 15; i++) + data[ei++] = phy->mt76->aggr_stats[i]; + + data[ei++] = phy->mib.ba_miss_cnt; + + /* Tx Beamformer monitor */ + data[ei++] = mib->tx_bf_ibf_ppdu_cnt; + data[ei++] = mib->tx_bf_ebf_ppdu_cnt; + + /* Tx Beamformer Rx feedback monitor */ + data[ei++] = mib->tx_bf_rx_fb_all_cnt; + data[ei++] = mib->tx_bf_rx_fb_he_cnt; + data[ei++] = mib->tx_bf_rx_fb_vht_cnt; + data[ei++] = mib->tx_bf_rx_fb_ht_cnt; + + /* Tx amsdu info (pack-count histogram) */ + for (i = 0; i < ARRAY_SIZE(mib->tx_amsdu); i++) + data[ei++] = mib->tx_amsdu[i]; + + /* rx counters */ + data[ei++] = mib->rx_mpdu_cnt; + data[ei++] = mib->rx_ampdu_cnt; + data[ei++] = mib->rx_ampdu_bytes_cnt; + data[ei++] = mib->rx_ba_cnt; + + /* Add values for all stations owned by this vif */ + wi.initial_stat_idx = ei; + ieee80211_iterate_stations_atomic(hw, mt792x_ethtool_worker, &wi); + + mt792x_mutex_release(dev); + + if (!wi.sta_count) + return; + + ei += wi.worker_stat_count; + + mt76_ethtool_page_pool_stats(&dev->mt76, &data[ei], &ei); + stats_size += page_pool_ethtool_stats_get_count(); + + if (ei != stats_size) + dev_err(dev->mt76.dev, "ei: %d SSTATS_LEN: %d", ei, + stats_size); +} +EXPORT_SYMBOL_GPL(mt792x_get_et_stats); + +void mt792x_sta_statistics(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct station_info *sinfo) +{ + struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv; + struct rate_info *txrate = &msta->deflink.wcid.rate; + + if (!txrate->legacy && !txrate->flags) + return; + + if (txrate->legacy) { + sinfo->txrate.legacy = txrate->legacy; + } else { + sinfo->txrate.mcs = txrate->mcs; + sinfo->txrate.nss = txrate->nss; + sinfo->txrate.bw = txrate->bw; + sinfo->txrate.he_gi = txrate->he_gi; + sinfo->txrate.he_dcm = txrate->he_dcm; + sinfo->txrate.he_ru_alloc = txrate->he_ru_alloc; + } + sinfo->tx_failed = msta->deflink.wcid.stats.tx_failed; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_FAILED); + + sinfo->tx_retries = msta->deflink.wcid.stats.tx_retries; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_RETRIES); + + sinfo->txrate.flags = txrate->flags; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE); + + sinfo->ack_signal = (s8)msta->deflink.ack_signal; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_ACK_SIGNAL); + + sinfo->avg_ack_signal = -(s8)ewma_avg_signal_read(&msta->deflink.avg_ack_signal); + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_ACK_SIGNAL_AVG); +} +EXPORT_SYMBOL_GPL(mt792x_sta_statistics); + +void mt792x_set_coverage_class(struct ieee80211_hw *hw, int radio_idx, + s16 coverage_class) +{ + struct mt792x_phy *phy = mt792x_hw_phy(hw); + struct mt792x_dev *dev = phy->dev; + + mt792x_mutex_acquire(dev); + + phy->coverage_class = max_t(s16, coverage_class, 0); + mt792x_mac_set_timeing(phy); + + mt792x_mutex_release(dev); +} +EXPORT_SYMBOL_GPL(mt792x_set_coverage_class); + +int mt792x_init_wiphy(struct ieee80211_hw *hw) +{ + struct mt792x_phy *phy = mt792x_hw_phy(hw); + struct mt792x_dev *dev = phy->dev; + struct wiphy *wiphy = hw->wiphy; + int err; + + hw->queues = 4; + if (dev->has_eht) { + hw->max_rx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF_EHT; + hw->max_tx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF_EHT; + } else { + hw->max_rx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF_HE; + hw->max_tx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF_HE; + } + hw->netdev_features = NETIF_F_RXCSUM; + + hw->radiotap_timestamp.units_pos = + IEEE80211_RADIOTAP_TIMESTAMP_UNIT_US; + + phy->slottime = 9; + + hw->sta_data_size = sizeof(struct mt792x_sta); + hw->vif_data_size = sizeof(struct mt792x_vif); + hw->chanctx_data_size = sizeof(struct mt792x_chanctx); + + if (dev->fw_features & MT792x_FW_CAP_CNM) + wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; + else + wiphy->flags &= ~WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; + + err = mt792x_setup_iface_combinations(dev, wiphy); + if (err) + return err; + + wiphy->iface_combinations = dev->iface_combinations; + wiphy->n_iface_combinations = dev->n_iface_combinations; + wiphy->flags &= ~(WIPHY_FLAG_IBSS_RSN | WIPHY_FLAG_4ADDR_AP | + WIPHY_FLAG_4ADDR_STATION); + wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_P2P_GO) | + BIT(NL80211_IFTYPE_P2P_DEVICE); + + if ((dev->fw_features & MT792x_FW_CAP_CNM) && + (dev->fw_features & MT792x_FW_CAP_NAN)) { + wiphy->interface_modes |= BIT(NL80211_IFTYPE_NAN) | + BIT(NL80211_IFTYPE_NAN_DATA); + wiphy->nan_supported_bands = BIT(NL80211_BAND_2GHZ) | + BIT(NL80211_BAND_5GHZ); + wiphy->nan_capa.flags = WIPHY_NAN_FLAGS_CONFIGURABLE_SYNC | + WIPHY_NAN_FLAGS_USERSPACE_DE; + wiphy->nan_capa.op_mode = NAN_OP_MODE_PHY_MODE_MASK; + wiphy->nan_capa.n_antennas = 0x22; + wiphy->nan_capa.max_channel_switch_time = 12; + wiphy->nan_capa.dev_capabilities = NAN_DEV_CAPA_EXT_KEY_ID_SUPPORTED; + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_SECURE_NAN); + } + + wiphy->max_scan_ie_len = MT76_CONNAC_SCAN_IE_LEN; + wiphy->max_scan_ssids = 4; + wiphy->max_sched_scan_plan_interval = + MT76_CONNAC_MAX_TIME_SCHED_SCAN_INTERVAL; + wiphy->max_sched_scan_ie_len = IEEE80211_MAX_DATA_LEN; + wiphy->max_sched_scan_ssids = MT76_CONNAC_MAX_SCHED_SCAN_SSID; + wiphy->max_match_sets = MT76_CONNAC_MAX_SCAN_MATCH; + wiphy->max_sched_scan_reqs = 1; + wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH | + WIPHY_FLAG_SPLIT_SCAN_6GHZ; + + wiphy->features |= NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR | + NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR; + phy->mt76->no_active_monitor = true; + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_SET_SCAN_DWELL); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_RATE_LEGACY); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_RATE_HT); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_RATE_VHT); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_RATE_HE); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_CAN_REPLACE_PTK0); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_MULTICAST_REGISTRATIONS); + + ieee80211_hw_set(hw, SINGLE_SCAN_ON_ALL_BANDS); + ieee80211_hw_set(hw, HAS_RATE_CONTROL); + ieee80211_hw_set(hw, SUPPORTS_TX_ENCAP_OFFLOAD); + ieee80211_hw_set(hw, SUPPORTS_RX_DECAP_OFFLOAD); + if (is_mt7927(&dev->mt76)) + ieee80211_hw_set(hw, NO_VIRTUAL_MONITOR); + else + ieee80211_hw_set(hw, WANT_MONITOR_VIF); + ieee80211_hw_set(hw, SUPPORTS_PS); + ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS); + ieee80211_hw_set(hw, SUPPORTS_VHT_EXT_NSS_BW); + ieee80211_hw_set(hw, CONNECTION_MONITOR); + ieee80211_hw_set(hw, SUPPORTS_MULTI_BSSID); + ieee80211_hw_set(hw, SUPPORTS_ONLY_HE_MULTI_BSSID); + + ieee80211_hw_set(hw, CHANCTX_STA_CSA); + + + if (dev->pm.enable) + ieee80211_hw_set(hw, CONNECTION_MONITOR); + + hw->max_tx_fragments = 4; + + return 0; +} +EXPORT_SYMBOL_GPL(mt792x_init_wiphy); + +static u8 +mt792x_get_offload_capability(struct device *dev, const char *fw_wm) +{ + const struct mt76_connac2_fw_trailer *hdr; + struct mt792x_realease_info *rel_info; + const struct firmware *fw; + int ret, i, offset = 0; + const u8 *data, *end; + u8 offload_caps = 0; + + ret = request_firmware(&fw, fw_wm, dev); + if (ret) + return ret; + + if (!fw || !fw->data || fw->size < sizeof(*hdr)) { + dev_err(dev, "Invalid firmware\n"); + goto out; + } + + data = fw->data; + hdr = (const void *)(fw->data + fw->size - sizeof(*hdr)); + + for (i = 0; i < hdr->n_region; i++) { + const struct mt76_connac2_fw_region *region; + + region = (const void *)((const u8 *)hdr - + (hdr->n_region - i) * sizeof(*region)); + offset += le32_to_cpu(region->len); + } + + data += offset + 16; + rel_info = (struct mt792x_realease_info *)data; + data += sizeof(*rel_info); + end = data + le16_to_cpu(rel_info->len); + + while (data < end) { + rel_info = (struct mt792x_realease_info *)data; + data += sizeof(*rel_info); + + if (rel_info->tag == MT792x_FW_TAG_FEATURE) { + struct mt792x_fw_features *features; + + features = (struct mt792x_fw_features *)data; + offload_caps = features->data; + break; + } + + data += le16_to_cpu(rel_info->len) + rel_info->pad_len; + } + +out: + release_firmware(fw); + + return offload_caps; +} + +static bool mt792x_needs_cnm_runtime(const void *drv_data) +{ + const char *fw_wm = drv_data; + + return fw_wm && !strcmp(fw_wm, MT7927_FIRMWARE_WM); +} + +struct ieee80211_ops * +mt792x_get_mac80211_ops(struct device *dev, + const struct ieee80211_ops *mac80211_ops, + void *drv_data, u8 *fw_features) +{ + struct ieee80211_ops *ops; + + ops = devm_kmemdup(dev, mac80211_ops, sizeof(struct ieee80211_ops), + GFP_KERNEL); + if (!ops) + return NULL; + + *fw_features = mt792x_get_offload_capability(dev, drv_data); + + if (mt792x_needs_cnm_runtime(drv_data)) + *fw_features |= MT792x_FW_CAP_CNM; + + if (!(*fw_features & MT792x_FW_CAP_CNM)) { + ops->remain_on_channel = NULL; + ops->cancel_remain_on_channel = NULL; + ops->add_chanctx = ieee80211_emulate_add_chanctx; + ops->remove_chanctx = ieee80211_emulate_remove_chanctx; + ops->change_chanctx = ieee80211_emulate_change_chanctx; + ops->switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx; + ops->assign_vif_chanctx = NULL; + ops->unassign_vif_chanctx = NULL; + ops->mgd_prepare_tx = NULL; + ops->mgd_complete_tx = NULL; + } + return ops; +} +EXPORT_SYMBOL_GPL(mt792x_get_mac80211_ops); + +int mt792x_init_wcid(struct mt792x_dev *dev) +{ + int idx; + + /* Beacon and mgmt frames should occupy wcid 0 */ + idx = mt76_wcid_alloc(dev->mt76.wcid_mask, MT792x_WTBL_STA - 1); + if (idx) + return -ENOSPC; + + dev->mt76.global_wcid.idx = idx; + dev->mt76.global_wcid.hw_key_idx = -1; + dev->mt76.global_wcid.tx_info |= MT_WCID_TX_INFO_SET; + rcu_assign_pointer(dev->mt76.wcid[idx], &dev->mt76.global_wcid); + + return 0; +} +EXPORT_SYMBOL_GPL(mt792x_init_wcid); + +int mt792x_mcu_drv_pmctrl(struct mt792x_dev *dev) +{ + struct mt76_phy *mphy = &dev->mt76.phy; + struct mt76_connac_pm *pm = &dev->pm; + int err = 0; + + mutex_lock(&pm->mutex); + + if (!test_bit(MT76_STATE_PM, &mphy->state)) + goto out; + + err = __mt792x_mcu_drv_pmctrl(dev); +out: + mutex_unlock(&pm->mutex); + + if (err) + mt792x_reset(&dev->mt76); + + return err; +} +EXPORT_SYMBOL_GPL(mt792x_mcu_drv_pmctrl); + +int mt792x_mcu_fw_pmctrl(struct mt792x_dev *dev) +{ + struct mt76_phy *mphy = &dev->mt76.phy; + struct mt76_connac_pm *pm = &dev->pm; + int err = 0; + + mutex_lock(&pm->mutex); + + if (mt76_connac_skip_fw_pmctrl(mphy, pm)) + goto out; + + err = __mt792x_mcu_fw_pmctrl(dev); +out: + mutex_unlock(&pm->mutex); + + if (err) + mt792x_reset(&dev->mt76); + + return err; +} +EXPORT_SYMBOL_GPL(mt792x_mcu_fw_pmctrl); + +int __mt792xe_mcu_drv_pmctrl(struct mt792x_dev *dev) +{ + int i, err = 0; + + for (i = 0; i < MT792x_DRV_OWN_RETRY_COUNT; i++) { + mt76_wr(dev, MT_CONN_ON_LPCTL, PCIE_LPCR_HOST_CLR_OWN); + + if (dev->aspm_supported) + usleep_range(2000, 3000); + + if (mt76_poll_msec_tick(dev, MT_CONN_ON_LPCTL, + PCIE_LPCR_HOST_OWN_SYNC, 0, 50, 1)) + break; + } + + if (i == MT792x_DRV_OWN_RETRY_COUNT) { + dev_err(dev->mt76.dev, "driver own failed\n"); + err = -EIO; + } + + return err; +} +EXPORT_SYMBOL_GPL(__mt792xe_mcu_drv_pmctrl); + +int mt792xe_mcu_drv_pmctrl(struct mt792x_dev *dev) +{ + struct mt76_phy *mphy = &dev->mt76.phy; + struct mt76_connac_pm *pm = &dev->pm; + int err; + + err = __mt792xe_mcu_drv_pmctrl(dev); + if (err < 0) + goto out; + + mt792x_wpdma_reinit_cond(dev); + clear_bit(MT76_STATE_PM, &mphy->state); + + pm->stats.last_wake_event = jiffies; + pm->stats.doze_time += pm->stats.last_wake_event - + pm->stats.last_doze_event; +out: + return err; +} +EXPORT_SYMBOL_GPL(mt792xe_mcu_drv_pmctrl); + +int mt792xe_mcu_fw_pmctrl(struct mt792x_dev *dev) +{ + struct mt76_phy *mphy = &dev->mt76.phy; + struct mt76_connac_pm *pm = &dev->pm; + int i; + + for (i = 0; i < MT792x_DRV_OWN_RETRY_COUNT; i++) { + mt76_wr(dev, MT_CONN_ON_LPCTL, PCIE_LPCR_HOST_SET_OWN); + if (mt76_poll_msec_tick(dev, MT_CONN_ON_LPCTL, + PCIE_LPCR_HOST_OWN_SYNC, 4, 50, 1)) + break; + } + + if (i == MT792x_DRV_OWN_RETRY_COUNT) { + dev_err(dev->mt76.dev, "firmware own failed\n"); + clear_bit(MT76_STATE_PM, &mphy->state); + return -EIO; + } + + pm->stats.last_doze_event = jiffies; + pm->stats.awake_time += pm->stats.last_doze_event - + pm->stats.last_wake_event; + + return 0; +} +EXPORT_SYMBOL_GPL(mt792xe_mcu_fw_pmctrl); + +int mt792x_load_firmware(struct mt792x_dev *dev) +{ + int ret; + + mt76_connac_mcu_restart(&dev->mt76); + + if (!mt76_poll_msec(dev, MT_CONN_ON_MISC, MT_TOP_MISC_FW_STATE, + MT_TOP_MISC2_FW_PWR_ON, 1000)) + dev_warn(dev->mt76.dev, + "MCU is not ready for firmware download\n"); + + if (is_mt7928(&dev->mt76)) { + dev_info(dev->mt76.dev, "Loading CB firmware patch: %s\n", + mt792x_cb_patch_name(dev)); + ret = mt76_connac3_load_cb_patch(&dev->mt76, mt792x_cb_patch_name(dev)); + if (ret) + return ret; + } + + dev_info(dev->mt76.dev, "Loading firmware patch: %s\n", mt792x_patch_name(dev)); + ret = mt76_connac2_load_patch(&dev->mt76, mt792x_patch_name(dev)); + if (ret) + return ret; + + if (mt76_is_sdio(&dev->mt76)) { + /* activate again */ + ret = __mt792x_mcu_fw_pmctrl(dev); + if (!ret) + ret = __mt792x_mcu_drv_pmctrl(dev); + } + + if (is_mt7928(&dev->mt76)) { + ret = mt76_connac3_load_phy_ram(&dev->mt76, mt792x_phy_ram_name(dev)); + if (ret) + return ret; + } + + ret = mt76_connac2_load_ram(&dev->mt76, mt792x_ram_name(dev), NULL); + if (ret) + return ret; + + if (!mt76_poll_msec(dev, MT_CONN_ON_MISC, MT_TOP_MISC2_FW_N9_RDY, + MT_TOP_MISC2_FW_N9_RDY, 1500)) { + dev_err(dev->mt76.dev, "Timeout for initializing firmware\n"); + + return -EIO; + } + +#ifdef CONFIG_PM + dev->mt76.hw->wiphy->wowlan = &mt76_connac_wowlan_support; +#endif /* CONFIG_PM */ + + dev_dbg(dev->mt76.dev, "Firmware init done\n"); + + return 0; +} +EXPORT_SYMBOL_GPL(mt792x_load_firmware); + +void mt792x_config_mac_addr_list(struct mt792x_dev *dev) +{ + struct ieee80211_hw *hw = mt76_hw(dev); + struct wiphy *wiphy = hw->wiphy; + int i; + + for (i = 0; i < ARRAY_SIZE(dev->macaddr_list); i++) { + u8 *addr = dev->macaddr_list[i].addr; + + memcpy(addr, dev->mphy.macaddr, ETH_ALEN); + + if (!i) + continue; + + addr[0] |= BIT(1); + addr[0] ^= ((i - 1) << 2); + } + wiphy->addresses = dev->macaddr_list; + wiphy->n_addresses = ARRAY_SIZE(dev->macaddr_list); +} +EXPORT_SYMBOL_GPL(mt792x_config_mac_addr_list); + +MODULE_DESCRIPTION("MediaTek MT792x core driver"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_AUTHOR("Lorenzo Bianconi "); diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_debugfs.c b/drivers/net/wireless/mediatek/mt76/mt792x_debugfs.c new file mode 100644 index 000000000..65c37e0ce --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt792x_debugfs.c @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2023 MediaTek Inc. */ + +#include "mt792x.h" + +static void +mt792x_ampdu_stat_read_phy(struct mt792x_phy *phy, + struct seq_file *file) +{ + struct mt792x_dev *dev = file->private; + int bound[15], range[4], i; + + if (!phy) + return; + + mt792x_mac_update_mib_stats(phy); + + /* Tx ampdu stat */ + for (i = 0; i < ARRAY_SIZE(range); i++) + range[i] = mt76_rr(dev, MT_MIB_ARNG(0, i)); + + for (i = 0; i < ARRAY_SIZE(bound); i++) + bound[i] = MT_MIB_ARNCR_RANGE(range[i / 4], i % 4) + 1; + + seq_puts(file, "\nPhy0\n"); + + seq_printf(file, "Length: %8d | ", bound[0]); + for (i = 0; i < ARRAY_SIZE(bound) - 1; i++) + seq_printf(file, "%3d %3d | ", bound[i] + 1, bound[i + 1]); + + seq_puts(file, "\nCount: "); + for (i = 0; i < ARRAY_SIZE(bound); i++) + seq_printf(file, "%8d | ", phy->mt76->aggr_stats[i]); + seq_puts(file, "\n"); + + seq_printf(file, "BA miss count: %d\n", phy->mib.ba_miss_cnt); +} + +int mt792x_tx_stats_show(struct seq_file *file, void *data) +{ + struct mt792x_dev *dev = file->private; + struct mt792x_phy *phy = &dev->phy; + struct mt76_mib_stats *mib = &phy->mib; + int i; + + mt792x_mutex_acquire(dev); + + mt792x_ampdu_stat_read_phy(phy, file); + + seq_puts(file, "Tx MSDU stat:\n"); + for (i = 0; i < ARRAY_SIZE(mib->tx_amsdu); i++) { + seq_printf(file, "AMSDU pack count of %d MSDU in TXD: %8d ", + i + 1, mib->tx_amsdu[i]); + if (mib->tx_amsdu_cnt) + seq_printf(file, "(%3d%%)\n", + mib->tx_amsdu[i] * 100 / mib->tx_amsdu_cnt); + else + seq_puts(file, "\n"); + } + + mt792x_mutex_release(dev); + + return 0; +} +EXPORT_SYMBOL_GPL(mt792x_tx_stats_show); + +int mt792x_queues_acq(struct seq_file *s, void *data) +{ + struct mt792x_dev *dev = dev_get_drvdata(s->private); + int i; + + mt792x_mutex_acquire(dev); + + for (i = 0; i < 4; i++) { + u32 ctrl, val, qlen = 0; + int j; + + val = mt76_rr(dev, MT_PLE_AC_QEMPTY(i)); + ctrl = BIT(31) | BIT(11) | (i << 24); + + for (j = 0; j < 32; j++) { + if (val & BIT(j)) + continue; + + mt76_wr(dev, MT_PLE_FL_Q0_CTRL, ctrl | j); + qlen += mt76_get_field(dev, MT_PLE_FL_Q3_CTRL, + GENMASK(11, 0)); + } + seq_printf(s, "AC%d: queued=%d\n", i, qlen); + } + + mt792x_mutex_release(dev); + + return 0; +} +EXPORT_SYMBOL_GPL(mt792x_queues_acq); + +int mt792x_queues_read(struct seq_file *s, void *data) +{ + struct mt792x_dev *dev = dev_get_drvdata(s->private); + struct { + struct mt76_queue *q; + char *queue; + } queue_map[] = { + { dev->mphy.q_tx[MT_TXQ_BE], "WFDMA0" }, + { dev->mt76.q_mcu[MT_MCUQ_WM], "MCUWM" }, + { dev->mt76.q_mcu[MT_MCUQ_FWDL], "MCUFWQ" }, + }; + int i; + + for (i = 0; i < ARRAY_SIZE(queue_map); i++) { + struct mt76_queue *q = queue_map[i].q; + + if (!q) + continue; + + seq_printf(s, + "%s: queued=%d head=%d tail=%d\n", + queue_map[i].queue, q->queued, q->head, + q->tail); + } + + return 0; +} +EXPORT_SYMBOL_GPL(mt792x_queues_read); + +int mt792x_pm_stats(struct seq_file *s, void *data) +{ + struct mt792x_dev *dev = dev_get_drvdata(s->private); + struct mt76_connac_pm *pm = &dev->pm; + + unsigned long awake_time = pm->stats.awake_time; + unsigned long doze_time = pm->stats.doze_time; + + if (!test_bit(MT76_STATE_PM, &dev->mphy.state)) + awake_time += jiffies - pm->stats.last_wake_event; + else + doze_time += jiffies - pm->stats.last_doze_event; + + seq_printf(s, "awake time: %14u\ndoze time: %15u\n", + jiffies_to_msecs(awake_time), + jiffies_to_msecs(doze_time)); + + seq_printf(s, "low power wakes: %9d\n", pm->stats.lp_wake); + + return 0; +} +EXPORT_SYMBOL_GPL(mt792x_pm_stats); + +int mt792x_pm_idle_timeout_set(void *data, u64 val) +{ + struct mt792x_dev *dev = data; + + dev->pm.idle_timeout = msecs_to_jiffies(val); + + return 0; +} +EXPORT_SYMBOL_GPL(mt792x_pm_idle_timeout_set); + +int mt792x_pm_idle_timeout_get(void *data, u64 *val) +{ + struct mt792x_dev *dev = data; + + *val = jiffies_to_msecs(dev->pm.idle_timeout); + + return 0; +} +EXPORT_SYMBOL_GPL(mt792x_pm_idle_timeout_get); diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_dma.c b/drivers/net/wireless/mediatek/mt76/mt792x_dma.c new file mode 100644 index 000000000..e74b95fdc --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt792x_dma.c @@ -0,0 +1,626 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2023 MediaTek Inc. */ + +#include +#include + +#include "mt792x.h" +#include "dma.h" +#include "trace.h" + +irqreturn_t mt792x_irq_handler(int irq, void *dev_instance) +{ + struct mt792x_dev *dev = dev_instance; + + if (test_bit(MT76_REMOVED, &dev->mt76.phy.state)) + return IRQ_NONE; + mt76_wr(dev, dev->irq_map->host_irq_enable, 0); + + if (!test_bit(MT76_STATE_INITIALIZED, &dev->mphy.state)) + return IRQ_NONE; + + tasklet_schedule(&dev->mt76.irq_tasklet); + + return IRQ_HANDLED; +} +EXPORT_SYMBOL_GPL(mt792x_irq_handler); + +void mt792x_irq_tasklet(unsigned long data) +{ + struct mt792x_dev *dev = (struct mt792x_dev *)data; + const struct mt792x_irq_map *irq_map = dev->irq_map; + u32 intr, mask = 0; + + mt76_wr(dev, irq_map->host_irq_enable, 0); + + intr = mt76_rr(dev, MT_WFDMA0_HOST_INT_STA); + intr &= dev->mt76.mmio.irqmask; + mt76_wr(dev, MT_WFDMA0_HOST_INT_STA, intr); + + trace_dev_irq(&dev->mt76, intr, dev->mt76.mmio.irqmask); + + mask |= intr & irq_map->rx.all_complete_mask; + if (intr & dev->irq_map->tx.mcu_complete_mask) + mask |= dev->irq_map->tx.mcu_complete_mask; + + if (intr & MT_INT_MCU_CMD) { + u32 intr_sw; + + intr_sw = mt76_rr(dev, MT_MCU_CMD); + /* ack MCU2HOST_SW_INT_STA */ + mt76_wr(dev, MT_MCU_CMD, intr_sw); + if (intr_sw & MT_MCU_CMD_WAKE_RX_PCIE) { + mask |= irq_map->rx.data_complete_mask; + intr |= irq_map->rx.data_complete_mask; + } + } + + mt76_set_irq_mask(&dev->mt76, irq_map->host_irq_enable, mask, 0); + + if (intr & dev->irq_map->tx.all_complete_mask) + napi_schedule(&dev->mt76.tx_napi); + + if (intr & irq_map->rx.wm_complete_mask) + napi_schedule(&dev->mt76.napi[MT_RXQ_MCU]); + + if (intr & irq_map->rx.wm2_complete_mask) + napi_schedule(&dev->mt76.napi[MT_RXQ_MCU_WA]); + + if (intr & irq_map->rx.data_complete_mask) + napi_schedule(&dev->mt76.napi[MT_RXQ_MAIN]); +} +EXPORT_SYMBOL_GPL(mt792x_irq_tasklet); + +void mt792x_rx_poll_complete(struct mt76_dev *mdev, enum mt76_rxq_id q) +{ + struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); + const struct mt792x_irq_map *irq_map = dev->irq_map; + + if (q == MT_RXQ_MAIN) + mt76_connac_irq_enable(mdev, irq_map->rx.data_complete_mask); + else if (q == MT_RXQ_MCU_WA) + mt76_connac_irq_enable(mdev, irq_map->rx.wm2_complete_mask); + else + mt76_connac_irq_enable(mdev, irq_map->rx.wm_complete_mask); +} +EXPORT_SYMBOL_GPL(mt792x_rx_poll_complete); + +int mt792x_dma_alloc_queues(struct mt792x_dev *dev, + const struct mt792x_dma_layout *layout) +{ + int ret; + + mt76_dma_attach(&dev->mt76); + + ret = mt792x_dma_disable(dev, true); + if (ret) + return ret; + + /* init tx queue */ + ret = mt76_connac_init_tx_queues(dev->phy.mt76, layout->tx_data0.qid, + layout->tx_data0.n_desc, + layout->tx_data0.ring_base, + NULL, 0); + if (ret) + return ret; + + mt76_wr(dev, MT_WFDMA0_TX_RING0_EXT_CTRL, 0x4); + + /* command to WM */ + ret = mt76_init_mcu_queue(&dev->mt76, MT_MCUQ_WM, + layout->tx_mcu.qid, + layout->tx_mcu.n_desc, + layout->tx_mcu.ring_base); + if (ret) + return ret; + + /* firmware download */ + ret = mt76_init_mcu_queue(&dev->mt76, MT_MCUQ_FWDL, + layout->tx_fwdl.qid, + layout->tx_fwdl.n_desc, + layout->tx_fwdl.ring_base); + if (ret) + return ret; + + /* tx done */ + if (layout->tx_done.ring_base) { + ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MCU_WA], + layout->tx_done.qid, + layout->tx_done.n_desc, + MT_RX_BUF_SIZE, + layout->tx_done.ring_base); + if (ret) + return ret; + } + + /* rx event */ + ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MCU], + layout->rx_mcu.qid, + layout->rx_mcu.n_desc, + MT_RX_BUF_SIZE, + layout->rx_mcu.ring_base); + if (ret) + return ret; + + /* rx data */ + ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MAIN], + layout->rx_data.qid, + layout->rx_data.n_desc, + MT_RX_BUF_SIZE, + layout->rx_data.ring_base); + return ret; +} +EXPORT_SYMBOL_GPL(mt792x_dma_alloc_queues); + +#define PREFETCH(base, depth) ((base) << 16 | (depth)) + +static void mt7925_dma_prefetch_setup(struct mt792x_dev *dev) +{ + /* rx ring */ + mt76_wr(dev, MT_WFDMA0_RX_RING0_EXT_CTRL, PREFETCH(0x0000, 0x4)); + mt76_wr(dev, MT_WFDMA0_RX_RING1_EXT_CTRL, PREFETCH(0x0040, 0x4)); + mt76_wr(dev, MT_WFDMA0_RX_RING2_EXT_CTRL, PREFETCH(0x0080, 0x4)); + mt76_wr(dev, MT_WFDMA0_RX_RING3_EXT_CTRL, PREFETCH(0x00c0, 0x4)); + + /* tx ring */ + mt76_wr(dev, MT_WFDMA0_TX_RING0_EXT_CTRL, PREFETCH(0x0100, 0x10)); + mt76_wr(dev, MT_WFDMA0_TX_RING1_EXT_CTRL, PREFETCH(0x0200, 0x10)); + mt76_wr(dev, MT_WFDMA0_TX_RING2_EXT_CTRL, PREFETCH(0x0300, 0x10)); + mt76_wr(dev, MT_WFDMA0_TX_RING3_EXT_CTRL, PREFETCH(0x0400, 0x10)); + mt76_wr(dev, MT_WFDMA0_TX_RING15_EXT_CTRL, PREFETCH(0x0500, 0x4)); + mt76_wr(dev, MT_WFDMA0_TX_RING16_EXT_CTRL, PREFETCH(0x0540, 0x4)); +} + +static void mt7925_wfdma_setup(struct mt792x_dev *dev) +{ + mt76_rmw(dev, MT_UWFDMA0_GLO_CFG_EXT1, BIT(28), BIT(28)); + mt76_set(dev, MT_WFDMA0_INT_RX_PRI, 0x0F00); + mt76_set(dev, MT_WFDMA0_INT_TX_PRI, 0x7F00); +} + +static void mt7927_dma_prefetch_setup(struct mt792x_dev *dev) +{ + mt76_wr(dev, MT_WFDMA_PREFETCH_CTRL, + mt76_rr(dev, MT_WFDMA_PREFETCH_CTRL)); + mt76_wr(dev, MT_WFDMA_PREFETCH_CFG0, 0x660077); + mt76_wr(dev, MT_WFDMA_PREFETCH_CFG1, 0x1100); + mt76_wr(dev, MT_WFDMA_PREFETCH_CFG2, 0x30004f); + mt76_wr(dev, MT_WFDMA_PREFETCH_CFG3, 0x542200); + mt76_wr(dev, MT_WFDMA0_RX_RING4_EXT_CTRL, PREFETCH(0x0000, 0x8)); + mt76_wr(dev, MT_WFDMA0_RX_RING6_EXT_CTRL, PREFETCH(0x0080, 0x8)); + mt76_wr(dev, MT_WFDMA0_RX_RING7_EXT_CTRL, PREFETCH(0x0100, 0x4)); + mt76_wr(dev, MT_WFDMA0_TX_RING16_EXT_CTRL, PREFETCH(0x0140, 0x4)); + mt76_wr(dev, MT_WFDMA0_TX_RING15_EXT_CTRL, PREFETCH(0x0180, 0x10)); + mt76_wr(dev, MT_WFDMA0_TX_RING0_EXT_CTRL, PREFETCH(0x0280, 0x4)); +} + +static void mt7927_wfdma_setup(struct mt792x_dev *dev) +{ + mt76_set(dev, MT_WFDMA0_GLO_CFG, + MT_WFDMA0_GLO_CFG_ADDR_EXT_EN | + MT_WFDMA0_GLO_CFG_FW_DWLD_BYPASS_DMASHDL); + mt76_clear(dev, MT_WFDMA0_GLO_CFG, + MT_WFDMA0_GLO_CFG_CSR_LBK_RX_Q_SEL_EN); + mt76_rmw(dev, MT_WFDMA0_GLO_CFG_EXT1, BIT(28), BIT(28)); + mt76_set(dev, MT_WFDMA0_INT_RX_PRI, 0x0F00); + mt76_set(dev, MT_WFDMA0_INT_TX_PRI, 0x7F00); +} + +static void mt7928_dma_prefetch_setup(struct mt792x_dev *dev) +{ + /* rx ring */ + mt76_wr(dev, MT_WFDMA0_RX_RING0_EXT_CTRL, PREFETCH(0x0000, 0x8)); + mt76_wr(dev, MT_WFDMA0_RX_RING1_EXT_CTRL, PREFETCH(0x0080, 0x4)); + mt76_wr(dev, MT_WFDMA0_RX_RING2_EXT_CTRL, PREFETCH(0x00c0, 0x8)); + mt76_wr(dev, MT_WFDMA0_RX_RING3_EXT_CTRL, PREFETCH(0x0140, 0x4)); + mt76_wr(dev, MT_WFDMA0_RX_RING4_EXT_CTRL, PREFETCH(0x0180, 0x4)); + mt76_wr(dev, MT_WFDMA0_RX_RING5_EXT_CTRL, PREFETCH(0x01c0, 0x4)); + mt76_wr(dev, MT_WFDMA0_RX_RING6_EXT_CTRL, PREFETCH(0x0200, 0x4)); + /* tx ring */ + mt76_wr(dev, MT_WFDMA0_TX_RING0_EXT_CTRL, PREFETCH(0x0240, 0x10)); + mt76_wr(dev, MT_WFDMA0_TX_RING1_EXT_CTRL, PREFETCH(0x0340, 0x10)); + mt76_wr(dev, MT_WFDMA0_TX_RING2_EXT_CTRL, PREFETCH(0x0440, 0x10)); + mt76_wr(dev, MT_WFDMA0_TX_RING3_EXT_CTRL, PREFETCH(0x0540, 0x10)); + mt76_wr(dev, MT_WFDMA0_TX_RING4_EXT_CTRL, PREFETCH(0x0640, 0x10)); + mt76_wr(dev, MT_WFDMA0_TX_RING5_EXT_CTRL, PREFETCH(0x0740, 0x10)); + mt76_wr(dev, MT_WFDMA0_TX_RING15_EXT_CTRL, PREFETCH(0x0840, 0x4)); + mt76_wr(dev, MT_WFDMA0_TX_RING16_EXT_CTRL, PREFETCH(0x0880, 0x4)); +} + +static void mt7928_wfdma_setup(struct mt792x_dev *dev) +{ + mt76_wr(dev, MT_WFDMA0_INT_RX_PRI, 0); + mt76_wr(dev, MT_WFDMA0_INT_TX_PRI, 0); +} + +static void mt792x_dma_prefetch(struct mt792x_dev *dev) +{ + if (is_mt7928(&dev->mt76)) { + mt7928_dma_prefetch_setup(dev); + } else if (is_mt7927(&dev->mt76)) { + mt7927_dma_prefetch_setup(dev); + } else if (is_mt7925(&dev->mt76)) { + mt7925_dma_prefetch_setup(dev); + } else if (is_mt7902(&dev->mt76)) { + /* rx ring */ + mt76_wr(dev, MT_WFDMA0_RX_RING0_EXT_CTRL, PREFETCH(0x0000, 0x4)); + mt76_wr(dev, MT_WFDMA0_RX_RING1_EXT_CTRL, PREFETCH(0x0040, 0x4)); + mt76_wr(dev, MT_WFDMA0_RX_RING2_EXT_CTRL, PREFETCH(0x0080, 0x4)); + mt76_wr(dev, MT_WFDMA0_RX_RING3_EXT_CTRL, PREFETCH(0x00c0, 0x4)); + /* tx ring */ + mt76_wr(dev, MT_WFDMA0_TX_RING0_EXT_CTRL, PREFETCH(0x0100, 0x4)); + mt76_wr(dev, MT_WFDMA0_TX_RING1_EXT_CTRL, PREFETCH(0x0140, 0x4)); + mt76_wr(dev, MT_WFDMA0_TX_RING2_EXT_CTRL, PREFETCH(0x0180, 0x4)); + mt76_wr(dev, MT_WFDMA0_TX_RING3_EXT_CTRL, PREFETCH(0x01c0, 0x4)); + mt76_wr(dev, MT_WFDMA0_TX_RING4_EXT_CTRL, PREFETCH(0x0200, 0x4)); + mt76_wr(dev, MT_WFDMA0_TX_RING5_EXT_CTRL, PREFETCH(0x0240, 0x4)); + mt76_wr(dev, MT_WFDMA0_TX_RING6_EXT_CTRL, PREFETCH(0x0280, 0x4)); + mt76_wr(dev, MT_WFDMA0_TX_RING15_EXT_CTRL, PREFETCH(0x02c0, 0x4)); + mt76_wr(dev, MT_WFDMA0_TX_RING16_EXT_CTRL, PREFETCH(0x0300, 0x4)); + } else { + /* rx ring */ + mt76_wr(dev, MT_WFDMA0_RX_RING0_EXT_CTRL, PREFETCH(0x0, 0x4)); + mt76_wr(dev, MT_WFDMA0_RX_RING2_EXT_CTRL, PREFETCH(0x40, 0x4)); + mt76_wr(dev, MT_WFDMA0_RX_RING3_EXT_CTRL, PREFETCH(0x80, 0x4)); + mt76_wr(dev, MT_WFDMA0_RX_RING4_EXT_CTRL, PREFETCH(0xc0, 0x4)); + mt76_wr(dev, MT_WFDMA0_RX_RING5_EXT_CTRL, PREFETCH(0x100, 0x4)); + /* tx ring */ + mt76_wr(dev, MT_WFDMA0_TX_RING0_EXT_CTRL, PREFETCH(0x140, 0x4)); + mt76_wr(dev, MT_WFDMA0_TX_RING1_EXT_CTRL, PREFETCH(0x180, 0x4)); + mt76_wr(dev, MT_WFDMA0_TX_RING2_EXT_CTRL, PREFETCH(0x1c0, 0x4)); + mt76_wr(dev, MT_WFDMA0_TX_RING3_EXT_CTRL, PREFETCH(0x200, 0x4)); + mt76_wr(dev, MT_WFDMA0_TX_RING4_EXT_CTRL, PREFETCH(0x240, 0x4)); + mt76_wr(dev, MT_WFDMA0_TX_RING5_EXT_CTRL, PREFETCH(0x280, 0x4)); + mt76_wr(dev, MT_WFDMA0_TX_RING6_EXT_CTRL, PREFETCH(0x2c0, 0x4)); + mt76_wr(dev, MT_WFDMA0_TX_RING16_EXT_CTRL, PREFETCH(0x340, 0x4)); + mt76_wr(dev, MT_WFDMA0_TX_RING17_EXT_CTRL, PREFETCH(0x380, 0x4)); + } +} + +int mt792x_dma_enable(struct mt792x_dev *dev) +{ + u32 addr; + + /* configure prefetch settings */ + mt792x_dma_prefetch(dev); + + /* reset dma idx */ + mt76_wr(dev, MT_WFDMA0_RST_DTX_PTR, ~0); + if (is_mt7925(&dev->mt76) || is_mt7928(&dev->mt76)) + mt76_wr(dev, MT_WFDMA0_RST_DRX_PTR, ~0); + + /* configure delay interrupt */ + mt76_wr(dev, MT_WFDMA0_PRI_DLY_INT_CFG0, 0); + + if (is_mt7928(&dev->mt76)) { + mt76_wr(dev, MT_WFDMA0_GLO_CFG, + MT_WFDMA0_GLO_CFG_TX_WB_DDONE | + MT_WFDMA0_GLO_CFG_FIFO_LITTLE_ENDIAN | + MT_WFDMA0_GLO_CFG_CLK_GAT_DIS | + MT_WFDMA0_GLO_CFG_OMIT_TX_INFO | + FIELD_PREP(MT_WFDMA0_GLO_CFG_DMA_SIZE, 1) | + MT_WFDMA0_GLO_CFG_FIFO_DIS_CHECK | + MT_WFDMA0_GLO_CFG_RX_WB_DDONE | + MT_WFDMA0_GLO_CFG_CSR_DISP_BASE_PTR_CHAIN_EN | + MT_WFDMA0_GLO_CFG_ADDR_EXT_EN | + MT_WFDMA0_GLO_CFG_CSR_LBK_RX_Q_SEL_EN); + /* set rxq threshold to 2 */ + for (addr = MT_WFDMA0_WPDMA_PAUSE_RXQ_TH10; + addr <= MT_WFDMA0_WPDMA_PAUSE_RXQ_TH76; + addr += 4) { + mt76_wr(dev, addr, + FIELD_PREP(MT_WFDMA0_WPDMA_PAUSE_RXQ_THXX_L_TH_MASK, 2) | + FIELD_PREP(MT_WFDMA0_WPDMA_PAUSE_RXQ_THXX_H_TH_MASK, 2)); + } + mt76_wr(dev, MT_WFDMA0_GLO_CFG_EXT0, + MT_WFDMA0_GLO_CFG_EXT0_CSR_MEM_ARB_LOCK_EN | + MT_WFDMA0_GLO_CFG_EXT0_CSR_TX_DMASHDL_LITE_EN | + MT_WFDMA0_GLO_CFG_EXT0_CSR_RX_WB_KEEP_RSVD | + MT_WFDMA0_GLO_CFG_EXT0_CSR_BID_CHECK_BYPASS_EN | + MT_WFDMA0_GLO_CFG_EXT0_CSR_RX_INFO_WB_EN | + MT_WFDMA0_GLO_CFG_EXT0_CSR_AXI_AWUSER_LOCK_EN | + FIELD_PREP(MT_WFDMA0_GLO_CFG_EXT0_CSR_MAX_PREFETCH_CNT_MASK, 3) | + FIELD_PREP(MT_WFDMA0_GLO_CFG_EXT0_CSR_MEM_BST_SIZE_MASK, 3) | + FIELD_PREP(MT_WFDMA0_GLO_CFG_EXT0_CSR_AXI_AW_OUTSTANDING_NUM_MASK, + 8)); + } else { + mt76_set(dev, MT_WFDMA0_GLO_CFG, + MT_WFDMA0_GLO_CFG_TX_WB_DDONE | + MT_WFDMA0_GLO_CFG_FIFO_LITTLE_ENDIAN | + MT_WFDMA0_GLO_CFG_CLK_GAT_DIS | + MT_WFDMA0_GLO_CFG_OMIT_TX_INFO | + FIELD_PREP(MT_WFDMA0_GLO_CFG_DMA_SIZE, 3) | + MT_WFDMA0_GLO_CFG_FIFO_DIS_CHECK | + MT_WFDMA0_GLO_CFG_RX_WB_DDONE | + MT_WFDMA0_GLO_CFG_CSR_DISP_BASE_PTR_CHAIN_EN | + MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2); + } + + mt76_set(dev, MT_WFDMA0_GLO_CFG, + MT_WFDMA0_GLO_CFG_TX_DMA_EN | MT_WFDMA0_GLO_CFG_RX_DMA_EN); + + if (is_mt7928(&dev->mt76)) + mt7928_wfdma_setup(dev); + else if (is_mt7927(&dev->mt76)) + mt7927_wfdma_setup(dev); + else if (is_mt7925(&dev->mt76)) + mt7925_wfdma_setup(dev); + + mt76_set(dev, MT_WFDMA_DUMMY_CR, MT_WFDMA_NEED_REINIT); + + /* enable interrupts for TX/RX rings */ + mt76_connac_irq_enable(&dev->mt76, + dev->irq_map->tx.all_complete_mask | + dev->irq_map->rx.all_complete_mask | + MT_INT_MCU_CMD); + mt76_set(dev, MT_MCU2HOST_SW_INT_ENA, MT_MCU_CMD_WAKE_RX_PCIE); + + return 0; +} +EXPORT_SYMBOL_GPL(mt792x_dma_enable); + +static int +mt792x_dma_reset(struct mt792x_dev *dev, bool force) +{ + int i, err; + + err = mt792x_dma_disable(dev, force); + if (err) + return err; + + /* reset hw queues */ + for (i = 0; i < __MT_TXQ_MAX; i++) + mt76_queue_reset(dev, dev->mphy.q_tx[i], true); + + for (i = 0; i < __MT_MCUQ_MAX; i++) + mt76_queue_reset(dev, dev->mt76.q_mcu[i], true); + + mt76_for_each_q_rx(&dev->mt76, i) + mt76_queue_reset(dev, &dev->mt76.q_rx[i], true); + + mt76_tx_status_check(&dev->mt76, true); + + return mt792x_dma_enable(dev); +} + +int mt792x_wpdma_reset(struct mt792x_dev *dev, bool force) +{ + int i, err; + + /* clean up hw queues */ + for (i = 0; i < ARRAY_SIZE(dev->mt76.phy.q_tx); i++) + mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[i], true); + + for (i = 0; i < ARRAY_SIZE(dev->mt76.q_mcu); i++) + mt76_queue_tx_cleanup(dev, dev->mt76.q_mcu[i], true); + + mt76_for_each_q_rx(&dev->mt76, i) + mt76_queue_rx_cleanup(dev, &dev->mt76.q_rx[i]); + + if (force) { + err = mt792x_wfsys_reset(dev); + if (err) + return err; + } + err = mt792x_dma_reset(dev, force); + if (err) + return err; + + mt76_for_each_q_rx(&dev->mt76, i) + mt76_queue_rx_reset(dev, i); + + return 0; +} +EXPORT_SYMBOL_GPL(mt792x_wpdma_reset); + +int mt792x_wpdma_reinit_cond(struct mt792x_dev *dev) +{ + struct mt76_connac_pm *pm = &dev->pm; + int err; + + if (dev->skip_wpdma_reinit) + return 0; + + /* check if the wpdma must be reinitialized */ + if (mt792x_dma_need_reinit(dev)) { + /* disable interrutpts */ + mt76_wr(dev, dev->irq_map->host_irq_enable, 0); + if (dev->pcie_reg) + mt76_wr(dev, dev->pcie_reg->imask, 0x0); + + err = mt792x_wpdma_reset(dev, false); + if (err) { + dev_err(dev->mt76.dev, "wpdma reset failed\n"); + return err; + } + + /* enable interrutpts */ + if (dev->pcie_reg) + mt76_wr(dev, dev->pcie_reg->imask, 0xff); + pm->stats.lp_wake++; + } + + return 0; +} +EXPORT_SYMBOL_GPL(mt792x_wpdma_reinit_cond); + +int mt792x_dma_disable(struct mt792x_dev *dev, bool force) +{ + /* disable WFDMA0 */ + mt76_clear(dev, MT_WFDMA0_GLO_CFG, + MT_WFDMA0_GLO_CFG_TX_DMA_EN | MT_WFDMA0_GLO_CFG_RX_DMA_EN | + MT_WFDMA0_GLO_CFG_CSR_DISP_BASE_PTR_CHAIN_EN | + MT_WFDMA0_GLO_CFG_OMIT_TX_INFO | + MT_WFDMA0_GLO_CFG_OMIT_RX_INFO | + MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2); + + if (!mt76_poll_msec_tick(dev, MT_WFDMA0_GLO_CFG, + MT_WFDMA0_GLO_CFG_TX_DMA_BUSY | + MT_WFDMA0_GLO_CFG_RX_DMA_BUSY, 0, 100, 1)) + return -ETIMEDOUT; + + if (is_mt7927(&dev->mt76)) { + mt76_wr(dev, MT_WFDMA0_RST_DTX_PTR, ~0); + mt76_wr(dev, MT_WFDMA0_RST_DRX_PTR, ~0); + } + + /* disable dmashdl */ + mt76_clear(dev, MT_WFDMA0_GLO_CFG_EXT0, + MT_WFDMA0_GLO_CFG_EXT0_CSR_TX_DMASHDL_EN); + mt76_set(dev, MT_DMASHDL_SW_CONTROL, MT_DMASHDL_DMASHDL_BYPASS); + + if (force) { + /* reset */ + mt76_clear(dev, MT_WFDMA0_RST, + MT_WFDMA0_RST_DMASHDL_ALL_RST | + MT_WFDMA0_RST_LOGIC_RST); + + mt76_set(dev, MT_WFDMA0_RST, + MT_WFDMA0_RST_DMASHDL_ALL_RST | + MT_WFDMA0_RST_LOGIC_RST); + } + + return 0; +} +EXPORT_SYMBOL_GPL(mt792x_dma_disable); + +void mt792x_dma_cleanup(struct mt792x_dev *dev) +{ + /* disable */ + mt76_clear(dev, MT_WFDMA0_GLO_CFG, + MT_WFDMA0_GLO_CFG_TX_DMA_EN | + MT_WFDMA0_GLO_CFG_RX_DMA_EN | + MT_WFDMA0_GLO_CFG_CSR_DISP_BASE_PTR_CHAIN_EN | + MT_WFDMA0_GLO_CFG_OMIT_TX_INFO | + MT_WFDMA0_GLO_CFG_OMIT_RX_INFO | + MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2); + + mt76_poll_msec_tick(dev, MT_WFDMA0_GLO_CFG, + MT_WFDMA0_GLO_CFG_TX_DMA_BUSY | + MT_WFDMA0_GLO_CFG_RX_DMA_BUSY, 0, 100, 1); + + /* reset */ + mt76_clear(dev, MT_WFDMA0_RST, + MT_WFDMA0_RST_DMASHDL_ALL_RST | + MT_WFDMA0_RST_LOGIC_RST); + + mt76_set(dev, MT_WFDMA0_RST, + MT_WFDMA0_RST_DMASHDL_ALL_RST | + MT_WFDMA0_RST_LOGIC_RST); + + mt76_dma_cleanup(&dev->mt76); +} +EXPORT_SYMBOL_GPL(mt792x_dma_cleanup); + +int mt792x_poll_tx(struct napi_struct *napi, int budget) +{ + struct mt792x_dev *dev; + + dev = container_of(napi, struct mt792x_dev, mt76.tx_napi); + + if (!mt76_connac_pm_ref(&dev->mphy, &dev->pm)) { + napi_complete(napi); + queue_work(dev->mt76.wq, &dev->pm.wake_work); + return 0; + } + + mt76_connac_tx_cleanup(&dev->mt76); + if (napi_complete(napi)) + mt76_connac_irq_enable(&dev->mt76, + dev->irq_map->tx.all_complete_mask); + mt76_connac_pm_unref(&dev->mphy, &dev->pm); + + return 0; +} +EXPORT_SYMBOL_GPL(mt792x_poll_tx); + +int mt792x_poll_rx(struct napi_struct *napi, int budget) +{ + struct mt792x_dev *dev; + int done; + + dev = mt76_priv(napi->dev); + + if (!mt76_connac_pm_ref(&dev->mphy, &dev->pm)) { + napi_complete(napi); + queue_work(dev->mt76.wq, &dev->pm.wake_work); + return 0; + } + done = mt76_dma_rx_poll(napi, budget); + mt76_connac_pm_unref(&dev->mphy, &dev->pm); + + return done; +} +EXPORT_SYMBOL_GPL(mt792x_poll_rx); + +static void mt7927_sema_status_read(struct mt792x_dev *dev, u32 addr) +{ + u32 remap; + + remap = mt76_rr(dev, MT7927_PCIE2AP_REMAP_WF_0_54); + mt76_wr(dev, MT7927_PCIE2AP_REMAP_WF_0_54, + (remap & ~MT7927_PCIE2AP_REMAP_WF_0_54_MASK) | + MT7927_PCIE2AP_REMAP_WF_0_54_VAL); + usleep_range(10, 20); + + mt76_rr(dev, addr); + + mt76_wr(dev, MT7927_PCIE2AP_REMAP_WF_0_54, remap); + usleep_range(10, 20); +} + +static int mt7927_wfsys_reset(struct mt792x_dev *dev) +{ + struct mt76_dev *mdev = &dev->mt76; + u32 val; + + mt7927_sema_status_read(dev, MT7927_SEMA_OWN_STA); + + mt76_set(dev, MT7927_CBINFRA_RGU_WF_RST, + MT7927_CBINFRA_RGU_WF_RST_WF_SUBSYS); + usleep_range(1000, 2000); + + mt76_clear(dev, MT7927_CBINFRA_RGU_WF_RST, + MT7927_CBINFRA_RGU_WF_RST_WF_SUBSYS); + usleep_range(5000, 10000); + + mt76_wr(dev, MT7927_CBINFRA_MCU_OWN_SET, BIT(0)); + + if (!__mt76_poll_msec(mdev, MT7927_ROMCODE_INDEX, 0xffff, + MT7927_MCU_IDLE_VALUE, 200)) { + val = mt76_rr(dev, MT7927_ROMCODE_INDEX); + dev_err(mdev->dev, + "MT7927 WFSYS reset timeout (ROMCODE_INDEX=0x%04x)\n", + val & 0xffff); + return -ETIMEDOUT; + } + + mt7927_sema_status_read(dev, MT7927_SEMA_OWN_STA_REP); + + return 0; +} + +static int mt792x_wfsys_reset_default(struct mt792x_dev *dev) +{ + u32 addr = is_connac2(&dev->mt76) ? 0x18000140 : 0x7c000140; + + mt76_clear(dev, addr, WFSYS_SW_RST_B); + msleep(50); + mt76_set(dev, addr, WFSYS_SW_RST_B); + + if (!__mt76_poll_msec(&dev->mt76, addr, WFSYS_SW_INIT_DONE, + WFSYS_SW_INIT_DONE, 500)) + return -ETIMEDOUT; + + return 0; +} + +int mt792x_wfsys_reset(struct mt792x_dev *dev) +{ + if (is_mt7927(&dev->mt76)) + return mt7927_wfsys_reset(dev); + + return mt792x_wfsys_reset_default(dev); +} +EXPORT_SYMBOL_GPL(mt792x_wfsys_reset); + diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_mac.c b/drivers/net/wireless/mediatek/mt76/mt792x_mac.c new file mode 100644 index 000000000..888e5a505 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt792x_mac.c @@ -0,0 +1,384 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2023 MediaTek Inc. */ + +#include + +#include "mt792x.h" +#include "mt792x_regs.h" + +void mt792x_mac_work(struct work_struct *work) +{ + struct mt792x_phy *phy; + struct mt76_phy *mphy; + + mphy = (struct mt76_phy *)container_of(work, struct mt76_phy, + mac_work.work); + phy = mphy->priv; + + mt792x_mutex_acquire(phy->dev); + + mt76_update_survey(mphy); + if (++mphy->mac_work_count == 2) { + mphy->mac_work_count = 0; + + mt792x_mac_update_mib_stats(phy); + } + + mt792x_mutex_release(phy->dev); + + mt76_tx_status_check(mphy->dev, false); + ieee80211_queue_delayed_work(phy->mt76->hw, &mphy->mac_work, + MT792x_WATCHDOG_TIME); +} +EXPORT_SYMBOL_GPL(mt792x_mac_work); + +void mt792x_mac_set_timeing(struct mt792x_phy *phy) +{ + s16 coverage_class = phy->coverage_class; + struct mt792x_dev *dev = phy->dev; + u32 val, reg_offset; + u32 cck = FIELD_PREP(MT_TIMEOUT_VAL_PLCP, 231) | + FIELD_PREP(MT_TIMEOUT_VAL_CCA, 48); + u32 ofdm = FIELD_PREP(MT_TIMEOUT_VAL_PLCP, 60) | + FIELD_PREP(MT_TIMEOUT_VAL_CCA, 28); + bool is_2ghz = phy->mt76->chandef.chan->band == NL80211_BAND_2GHZ; + int sifs = is_2ghz ? 10 : 16, offset; + + if (!test_bit(MT76_STATE_RUNNING, &phy->mt76->state)) + return; + + mt76_set(dev, MT_ARB_SCR(0), + MT_ARB_SCR_TX_DISABLE | MT_ARB_SCR_RX_DISABLE); + udelay(1); + + offset = 3 * coverage_class; + reg_offset = FIELD_PREP(MT_TIMEOUT_VAL_PLCP, offset) | + FIELD_PREP(MT_TIMEOUT_VAL_CCA, offset); + + mt76_wr(dev, MT_TMAC_CDTR(0), cck + reg_offset); + mt76_wr(dev, MT_TMAC_ODTR(0), ofdm + reg_offset); + mt76_wr(dev, MT_TMAC_ICR0(0), + FIELD_PREP(MT_IFS_EIFS, 360) | + FIELD_PREP(MT_IFS_RIFS, 2) | + FIELD_PREP(MT_IFS_SIFS, sifs) | + FIELD_PREP(MT_IFS_SLOT, phy->slottime)); + + if (phy->slottime < 20 || !is_2ghz) + val = MT792x_CFEND_RATE_DEFAULT; + else + val = MT792x_CFEND_RATE_11B; + + mt76_rmw_field(dev, MT_AGG_ACR0(0), MT_AGG_ACR_CFEND_RATE, val); + mt76_clear(dev, MT_ARB_SCR(0), + MT_ARB_SCR_TX_DISABLE | MT_ARB_SCR_RX_DISABLE); +} +EXPORT_SYMBOL_GPL(mt792x_mac_set_timeing); + +void mt792x_mac_update_mib_stats(struct mt792x_phy *phy) +{ + struct mt76_mib_stats *mib = &phy->mib; + struct mt792x_dev *dev = phy->dev; + int i, aggr0 = 0, aggr1; + u32 val; + + mib->fcs_err_cnt += mt76_get_field(dev, MT_MIB_SDR3(0), + MT_MIB_SDR3_FCS_ERR_MASK); + mib->ack_fail_cnt += mt76_get_field(dev, MT_MIB_MB_BSDR3(0), + MT_MIB_ACK_FAIL_COUNT_MASK); + mib->ba_miss_cnt += mt76_get_field(dev, MT_MIB_MB_BSDR2(0), + MT_MIB_BA_FAIL_COUNT_MASK); + mib->rts_cnt += mt76_get_field(dev, MT_MIB_MB_BSDR0(0), + MT_MIB_RTS_COUNT_MASK); + mib->rts_retries_cnt += mt76_get_field(dev, MT_MIB_MB_BSDR1(0), + MT_MIB_RTS_FAIL_COUNT_MASK); + + mib->tx_ampdu_cnt += mt76_rr(dev, MT_MIB_SDR12(0)); + mib->tx_mpdu_attempts_cnt += mt76_rr(dev, MT_MIB_SDR14(0)); + mib->tx_mpdu_success_cnt += mt76_rr(dev, MT_MIB_SDR15(0)); + + val = mt76_rr(dev, MT_MIB_SDR32(0)); + mib->tx_pkt_ebf_cnt += FIELD_GET(MT_MIB_SDR9_EBF_CNT_MASK, val); + mib->tx_pkt_ibf_cnt += FIELD_GET(MT_MIB_SDR9_IBF_CNT_MASK, val); + + val = mt76_rr(dev, MT_ETBF_TX_APP_CNT(0)); + mib->tx_bf_ibf_ppdu_cnt += FIELD_GET(MT_ETBF_TX_IBF_CNT, val); + mib->tx_bf_ebf_ppdu_cnt += FIELD_GET(MT_ETBF_TX_EBF_CNT, val); + + val = mt76_rr(dev, MT_ETBF_RX_FB_CNT(0)); + mib->tx_bf_rx_fb_all_cnt += FIELD_GET(MT_ETBF_RX_FB_ALL, val); + mib->tx_bf_rx_fb_he_cnt += FIELD_GET(MT_ETBF_RX_FB_HE, val); + mib->tx_bf_rx_fb_vht_cnt += FIELD_GET(MT_ETBF_RX_FB_VHT, val); + mib->tx_bf_rx_fb_ht_cnt += FIELD_GET(MT_ETBF_RX_FB_HT, val); + + mib->rx_mpdu_cnt += mt76_rr(dev, MT_MIB_SDR5(0)); + mib->rx_ampdu_cnt += mt76_rr(dev, MT_MIB_SDR22(0)); + mib->rx_ampdu_bytes_cnt += mt76_rr(dev, MT_MIB_SDR23(0)); + mib->rx_ba_cnt += mt76_rr(dev, MT_MIB_SDR31(0)); + + for (i = 0; i < ARRAY_SIZE(mib->tx_amsdu); i++) { + val = mt76_rr(dev, MT_PLE_AMSDU_PACK_MSDU_CNT(i)); + mib->tx_amsdu[i] += val; + mib->tx_amsdu_cnt += val; + } + + for (i = 0, aggr1 = aggr0 + 8; i < 4; i++) { + u32 val2; + + val = mt76_rr(dev, MT_TX_AGG_CNT(0, i)); + val2 = mt76_rr(dev, MT_TX_AGG_CNT2(0, i)); + + phy->mt76->aggr_stats[aggr0++] += val & 0xffff; + phy->mt76->aggr_stats[aggr0++] += val >> 16; + phy->mt76->aggr_stats[aggr1++] += val2 & 0xffff; + phy->mt76->aggr_stats[aggr1++] += val2 >> 16; + } +} +EXPORT_SYMBOL_GPL(mt792x_mac_update_mib_stats); + +struct mt76_wcid *mt792x_rx_get_wcid(struct mt792x_dev *dev, u16 idx, + bool unicast) +{ + struct mt792x_link_sta *link; + struct mt792x_sta *sta; + struct mt76_wcid *wcid; + + wcid = mt76_wcid_ptr(dev, idx); + if (unicast || !wcid) + return wcid; + + if (!wcid->sta) + return NULL; + + link = container_of(wcid, struct mt792x_link_sta, wcid); + sta = link->sta; + if (!sta->vif) + return NULL; + + return &sta->vif->sta.deflink.wcid; +} +EXPORT_SYMBOL_GPL(mt792x_rx_get_wcid); + +static void +mt792x_mac_rssi_iter(void *priv, u8 *mac, struct ieee80211_vif *vif) +{ + struct sk_buff *skb = priv; + struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb; + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + struct ieee80211_hdr *hdr = mt76_skb_get_hdr(skb); + + if (status->signal > 0) + return; + + if (!ether_addr_equal(vif->addr, hdr->addr1)) + return; + + ewma_rssi_add(&mvif->bss_conf.rssi, -status->signal); +} + +void mt792x_mac_assoc_rssi(struct mt792x_dev *dev, struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr = mt76_skb_get_hdr(skb); + + if (!ieee80211_is_assoc_resp(hdr->frame_control) && + !ieee80211_is_auth(hdr->frame_control)) + return; + + ieee80211_iterate_active_interfaces_atomic(mt76_hw(dev), + IEEE80211_IFACE_ITER_RESUME_ALL, + mt792x_mac_rssi_iter, skb); +} +EXPORT_SYMBOL_GPL(mt792x_mac_assoc_rssi); + +void mt792x_mac_reset_counters(struct mt792x_phy *phy) +{ + struct mt792x_dev *dev = phy->dev; + int i; + + for (i = 0; i < 4; i++) { + mt76_rr(dev, MT_TX_AGG_CNT(0, i)); + mt76_rr(dev, MT_TX_AGG_CNT2(0, i)); + } + + dev->mt76.phy.survey_time = ktime_get_boottime(); + memset(phy->mt76->aggr_stats, 0, sizeof(phy->mt76->aggr_stats)); + + /* reset airtime counters */ + mt76_rr(dev, MT_MIB_SDR9(0)); + mt76_rr(dev, MT_MIB_SDR36(0)); + mt76_rr(dev, MT_MIB_SDR37(0)); + + mt76_set(dev, MT_WF_RMAC_MIB_TIME0(0), MT_WF_RMAC_MIB_RXTIME_CLR); + mt76_set(dev, MT_WF_RMAC_MIB_AIRTIME0(0), MT_WF_RMAC_MIB_RXTIME_CLR); +} +EXPORT_SYMBOL_GPL(mt792x_mac_reset_counters); + +static u8 +mt792x_phy_get_nf(struct mt792x_phy *phy, int idx) +{ + return 0; +} + +static void +mt792x_phy_update_channel(struct mt76_phy *mphy, int idx) +{ + struct mt792x_dev *dev = container_of(mphy->dev, struct mt792x_dev, mt76); + struct mt792x_phy *phy = mphy->priv; + struct mt76_channel_state *state; + u64 busy_time, tx_time, rx_time, obss_time; + int nf; + + busy_time = mt76_get_field(dev, MT_MIB_SDR9(idx), + MT_MIB_SDR9_BUSY_MASK); + tx_time = mt76_get_field(dev, MT_MIB_SDR36(idx), + MT_MIB_SDR36_TXTIME_MASK); + rx_time = mt76_get_field(dev, MT_MIB_SDR37(idx), + MT_MIB_SDR37_RXTIME_MASK); + obss_time = mt76_get_field(dev, MT_WF_RMAC_MIB_AIRTIME14(idx), + MT_MIB_OBSSTIME_MASK); + + nf = mt792x_phy_get_nf(phy, idx); + if (!phy->noise) + phy->noise = nf << 4; + else if (nf) + phy->noise += nf - (phy->noise >> 4); + + state = mphy->chan_state; + state->cc_busy += busy_time; + state->cc_tx += tx_time; + state->cc_rx += rx_time + obss_time; + state->cc_bss_rx += rx_time; + state->noise = -(phy->noise >> 4); +} + +void mt792x_update_channel(struct mt76_phy *mphy) +{ + struct mt792x_dev *dev = container_of(mphy->dev, struct mt792x_dev, mt76); + + if (mt76_connac_pm_wake(mphy, &dev->pm)) + return; + + mt792x_phy_update_channel(mphy, 0); + /* reset obss airtime */ + mt76_set(dev, MT_WF_RMAC_MIB_TIME0(0), MT_WF_RMAC_MIB_RXTIME_CLR); + mt76_connac_power_save_sched(mphy, &dev->pm); +} +EXPORT_SYMBOL_GPL(mt792x_update_channel); + +void mt792x_reset(struct mt76_dev *mdev) +{ + struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); + struct mt76_connac_pm *pm = &dev->pm; + + if (!dev->hw_init_done) + return; + + if (dev->hw_full_reset) + return; + + if (pm->suspended) + return; + + queue_work(dev->mt76.wq, &dev->reset_work); +} +EXPORT_SYMBOL_GPL(mt792x_reset); + +void mt792x_mac_init_band(struct mt792x_dev *dev, u8 band) +{ + u32 mask, set; + + mt76_rmw_field(dev, MT_TMAC_CTCR0(band), + MT_TMAC_CTCR0_INS_DDLMT_REFTIME, 0x3f); + mt76_set(dev, MT_TMAC_CTCR0(band), + MT_TMAC_CTCR0_INS_DDLMT_VHT_SMPDU_EN | + MT_TMAC_CTCR0_INS_DDLMT_EN); + + mt76_set(dev, MT_WF_RMAC_MIB_TIME0(band), MT_WF_RMAC_MIB_RXTIME_EN); + mt76_set(dev, MT_WF_RMAC_MIB_AIRTIME0(band), MT_WF_RMAC_MIB_RXTIME_EN); + + /* enable MIB tx-rx time reporting */ + mt76_set(dev, MT_MIB_SCR1(band), MT_MIB_TXDUR_EN); + mt76_set(dev, MT_MIB_SCR1(band), MT_MIB_RXDUR_EN); + + mt76_rmw_field(dev, MT_DMA_DCR0(band), MT_DMA_DCR0_MAX_RX_LEN, 1536); + /* disable rx rate report by default due to hw issues */ + mt76_clear(dev, MT_DMA_DCR0(band), MT_DMA_DCR0_RXD_G5_EN); + + /* filter out non-resp frames and get instantaneous signal reporting */ + mask = MT_WTBLOFF_TOP_RSCR_RCPI_MODE | MT_WTBLOFF_TOP_RSCR_RCPI_PARAM; + set = FIELD_PREP(MT_WTBLOFF_TOP_RSCR_RCPI_MODE, 0) | + FIELD_PREP(MT_WTBLOFF_TOP_RSCR_RCPI_PARAM, 0x3); + mt76_rmw(dev, MT_WTBLOFF_TOP_RSCR(band), mask, set); +} +EXPORT_SYMBOL_GPL(mt792x_mac_init_band); + +void mt792x_pm_wake_work(struct work_struct *work) +{ + struct mt792x_dev *dev; + struct mt76_phy *mphy; + + dev = (struct mt792x_dev *)container_of(work, struct mt792x_dev, + pm.wake_work); + mphy = dev->phy.mt76; + + if (!mt792x_mcu_drv_pmctrl(dev)) { + struct mt76_dev *mdev = &dev->mt76; + int i; + + if (mt76_is_sdio(mdev)) { + mt76_connac_pm_dequeue_skbs(mphy, &dev->pm); + mt76_worker_schedule(&mdev->sdio.txrx_worker); + } else { + local_bh_disable(); + mt76_for_each_q_rx(mdev, i) + napi_schedule(&mdev->napi[i]); + local_bh_enable(); + mt76_connac_pm_dequeue_skbs(mphy, &dev->pm); + mt76_connac_tx_cleanup(mdev); + } + if (test_bit(MT76_STATE_RUNNING, &mphy->state)) + ieee80211_queue_delayed_work(mphy->hw, &mphy->mac_work, + MT792x_WATCHDOG_TIME); + } + + ieee80211_wake_queues(mphy->hw); + wake_up(&dev->pm.wait); +} +EXPORT_SYMBOL_GPL(mt792x_pm_wake_work); + +void mt792x_pm_power_save_work(struct work_struct *work) +{ + struct mt792x_dev *dev; + unsigned long delta; + struct mt76_phy *mphy; + + dev = (struct mt792x_dev *)container_of(work, struct mt792x_dev, + pm.ps_work.work); + mphy = dev->phy.mt76; + + delta = dev->pm.idle_timeout; + if (test_bit(MT76_HW_SCANNING, &mphy->state) || + test_bit(MT76_HW_SCHED_SCANNING, &mphy->state) || + dev->fw_assert) + goto out; + + if (mutex_is_locked(&dev->mt76.mutex)) + /* if mt76 mutex is held we should not put the device + * to sleep since we are currently accessing device + * register map. We need to wait for the next power_save + * trigger. + */ + goto out; + + if (time_is_after_jiffies(dev->pm.last_activity + delta)) { + delta = dev->pm.last_activity + delta - jiffies; + goto out; + } + + if (!mt792x_mcu_fw_pmctrl(dev)) { + cancel_delayed_work(&mphy->mac_work); + return; + } +out: + queue_delayed_work(dev->mt76.wq, &dev->pm.ps_work, delta); +} +EXPORT_SYMBOL_GPL(mt792x_pm_power_save_work); diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_regs.h b/drivers/net/wireless/mediatek/mt76/mt792x_regs.h new file mode 100644 index 000000000..3587f61e0 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt792x_regs.h @@ -0,0 +1,529 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* Copyright (C) 2023 MediaTek Inc. */ + +#ifndef __MT792X_REGS_H +#define __MT792X_REGS_H + +/* MCU WFDMA1 */ +#define MT_MCU_WFDMA1_BASE 0x3000 +#define MT_MCU_WFDMA1(ofs) (MT_MCU_WFDMA1_BASE + (ofs)) + +#define MT_MCU_INT_EVENT MT_MCU_WFDMA1(0x108) +#define MT_MCU_INT_EVENT_DMA_STOPPED BIT(0) +#define MT_MCU_INT_EVENT_DMA_INIT BIT(1) +#define MT_MCU_INT_EVENT_SER_TRIGGER BIT(2) +#define MT_MCU_INT_EVENT_RESET_DONE BIT(3) + +#define MT_PLE_BASE 0x820c0000 +#define MT_PLE(ofs) (MT_PLE_BASE + (ofs)) + +#define MT_PLE_FL_Q0_CTRL MT_PLE(0x3e0) +#define MT_PLE_FL_Q1_CTRL MT_PLE(0x3e4) +#define MT_PLE_FL_Q2_CTRL MT_PLE(0x3e8) +#define MT_PLE_FL_Q3_CTRL MT_PLE(0x3ec) + +#define MT_PLE_AC_QEMPTY(_n) MT_PLE(0x500 + 0x40 * (_n)) +#define MT_PLE_AMSDU_PACK_MSDU_CNT(n) MT_PLE(0x10e0 + ((n) << 2)) + +#define MT_PSE_BASE 0x820c8000 + +/* TMAC: band 0(0x21000), band 1(0xa1000) */ +#define MT_WF_TMAC_BASE(_band) ((_band) ? 0x820f4000 : 0x820e4000) +#define MT_WF_TMAC(_band, ofs) (MT_WF_TMAC_BASE(_band) + (ofs)) + +#define MT_TMAC_TCR0(_band) MT_WF_TMAC(_band, 0) +#define MT_TMAC_TCR0_TBTT_STOP_CTRL BIT(25) + +#define MT_TMAC_CDTR(_band) MT_WF_TMAC(_band, 0x090) +#define MT_TMAC_ODTR(_band) MT_WF_TMAC(_band, 0x094) +#define MT_TIMEOUT_VAL_PLCP GENMASK(15, 0) +#define MT_TIMEOUT_VAL_CCA GENMASK(31, 16) + +#define MT_TMAC_ICR0(_band) MT_WF_TMAC(_band, 0x0a4) +#define MT_IFS_EIFS GENMASK(8, 0) +#define MT_IFS_RIFS GENMASK(14, 10) +#define MT_IFS_SIFS GENMASK(22, 16) +#define MT_IFS_SLOT GENMASK(30, 24) + +#define MT_TMAC_CTCR0(_band) MT_WF_TMAC(_band, 0x0f4) +#define MT_TMAC_CTCR0_INS_DDLMT_REFTIME GENMASK(5, 0) +#define MT_TMAC_CTCR0_INS_DDLMT_EN BIT(17) +#define MT_TMAC_CTCR0_INS_DDLMT_VHT_SMPDU_EN BIT(18) + +#define MT_TMAC_TRCR0(_band) MT_WF_TMAC(_band, 0x09c) +#define MT_TMAC_TFCR0(_band) MT_WF_TMAC(_band, 0x1e0) + +#define MT_WF_DMA_BASE(_band) ((_band) ? 0x820f7000 : 0x820e7000) +#define MT_WF_DMA(_band, ofs) (MT_WF_DMA_BASE(_band) + (ofs)) + +#define MT_DMA_DCR0(_band) MT_WF_DMA(_band, 0x000) +#define MT_DMA_DCR0_MAX_RX_LEN GENMASK(15, 3) +#define MT_DMA_DCR0_RXD_G5_EN BIT(23) + +/* WTBLOFF TOP: band 0(0x820e9000),band 1(0x820f9000) */ +#define MT_WTBLOFF_TOP_BASE(_band) ((_band) ? 0x820f9000 : 0x820e9000) +#define MT_WTBLOFF_TOP(_band, ofs) (MT_WTBLOFF_TOP_BASE(_band) + (ofs)) + +#define MT_WTBLOFF_TOP_RSCR(_band) MT_WTBLOFF_TOP(_band, 0x008) +#define MT_WTBLOFF_TOP_RSCR_RCPI_MODE GENMASK(31, 30) +#define MT_WTBLOFF_TOP_RSCR_RCPI_PARAM GENMASK(25, 24) + +/* LPON: band 0(0x24200), band 1(0xa4200) */ +#define MT_WF_LPON_BASE(_band) ((_band) ? 0x820fb000 : 0x820eb000) +#define MT_WF_LPON(_band, ofs) (MT_WF_LPON_BASE(_band) + (ofs)) + +#define MT_LPON_UTTR0(_band) MT_WF_LPON(_band, 0x080) +#define MT_LPON_UTTR1(_band) MT_WF_LPON(_band, 0x084) + +#define MT_LPON_TCR(_band, n) MT_WF_LPON(_band, 0x0a8 + (n) * 4) +#define MT_LPON_TCR_SW_MODE GENMASK(1, 0) +#define MT_LPON_TCR_SW_WRITE BIT(0) + +/* ETBF: band 0(0x24000), band 1(0xa4000) */ +#define MT_WF_ETBF_BASE(_band) ((_band) ? 0x820fa000 : 0x820ea000) +#define MT_WF_ETBF(_band, ofs) (MT_WF_ETBF_BASE(_band) + (ofs)) + +#define MT_ETBF_TX_APP_CNT(_band) MT_WF_ETBF(_band, 0x150) +#define MT_ETBF_TX_IBF_CNT GENMASK(31, 16) +#define MT_ETBF_TX_EBF_CNT GENMASK(15, 0) + +#define MT_ETBF_RX_FB_CNT(_band) MT_WF_ETBF(_band, 0x158) +#define MT_ETBF_RX_FB_ALL GENMASK(31, 24) +#define MT_ETBF_RX_FB_HE GENMASK(23, 16) +#define MT_ETBF_RX_FB_VHT GENMASK(15, 8) +#define MT_ETBF_RX_FB_HT GENMASK(7, 0) + +/* MIB: band 0(0x24800), band 1(0xa4800) */ +#define MT_WF_MIB_BASE(_band) ((_band) ? 0x820fd000 : 0x820ed000) +#define MT_WF_MIB(_band, ofs) (MT_WF_MIB_BASE(_band) + (ofs)) + +#define MT_MIB_SCR1(_band) MT_WF_MIB(_band, 0x004) +#define MT_MIB_TXDUR_EN BIT(8) +#define MT_MIB_RXDUR_EN BIT(9) + +#define MT_MIB_SDR3(_band) MT_WF_MIB(_band, 0x698) +#define MT_MIB_SDR3_FCS_ERR_MASK GENMASK(31, 16) + +#define MT_MIB_SDR5(_band) MT_WF_MIB(_band, 0x780) + +#define MT_MIB_SDR9(_band) MT_WF_MIB(_band, 0x02c) +#define MT_MIB_SDR9_BUSY_MASK GENMASK(23, 0) + +#define MT_MIB_SDR12(_band) MT_WF_MIB(_band, 0x558) +#define MT_MIB_SDR14(_band) MT_WF_MIB(_band, 0x564) +#define MT_MIB_SDR15(_band) MT_WF_MIB(_band, 0x568) + +#define MT_MIB_SDR16(_band) MT_WF_MIB(_band, 0x048) +#define MT_MIB_SDR16_BUSY_MASK GENMASK(23, 0) + +#define MT_MIB_SDR22(_band) MT_WF_MIB(_band, 0x770) +#define MT_MIB_SDR23(_band) MT_WF_MIB(_band, 0x774) +#define MT_MIB_SDR31(_band) MT_WF_MIB(_band, 0x55c) + +#define MT_MIB_SDR32(_band) MT_WF_MIB(_band, 0x7a8) +#define MT_MIB_SDR9_IBF_CNT_MASK GENMASK(31, 16) +#define MT_MIB_SDR9_EBF_CNT_MASK GENMASK(15, 0) + +#define MT_MIB_SDR34(_band) MT_WF_MIB(_band, 0x090) +#define MT_MIB_MU_BF_TX_CNT GENMASK(15, 0) + +#define MT_MIB_SDR36(_band) MT_WF_MIB(_band, 0x054) +#define MT_MIB_SDR36_TXTIME_MASK GENMASK(23, 0) +#define MT_MIB_SDR37(_band) MT_WF_MIB(_band, 0x058) +#define MT_MIB_SDR37_RXTIME_MASK GENMASK(23, 0) + +#define MT_MIB_DR8(_band) MT_WF_MIB(_band, 0x0c0) +#define MT_MIB_DR9(_band) MT_WF_MIB(_band, 0x0c4) +#define MT_MIB_DR11(_band) MT_WF_MIB(_band, 0x0cc) + +#define MT_MIB_MB_SDR0(_band, n) MT_WF_MIB(_band, 0x100 + ((n) << 4)) +#define MT_MIB_RTS_RETRIES_COUNT_MASK GENMASK(31, 16) + +#define MT_MIB_MB_BSDR0(_band) MT_WF_MIB(_band, 0x688) +#define MT_MIB_RTS_COUNT_MASK GENMASK(15, 0) +#define MT_MIB_MB_BSDR1(_band) MT_WF_MIB(_band, 0x690) +#define MT_MIB_RTS_FAIL_COUNT_MASK GENMASK(15, 0) +#define MT_MIB_MB_BSDR2(_band) MT_WF_MIB(_band, 0x518) +#define MT_MIB_BA_FAIL_COUNT_MASK GENMASK(15, 0) +#define MT_MIB_MB_BSDR3(_band) MT_WF_MIB(_band, 0x520) +#define MT_MIB_ACK_FAIL_COUNT_MASK GENMASK(15, 0) + +#define MT_MIB_MB_SDR2(_band, n) MT_WF_MIB(_band, 0x108 + ((n) << 4)) +#define MT_MIB_FRAME_RETRIES_COUNT_MASK GENMASK(15, 0) + +#define MT_TX_AGG_CNT(_band, n) MT_WF_MIB(_band, 0x7dc + ((n) << 2)) +#define MT_TX_AGG_CNT2(_band, n) MT_WF_MIB(_band, 0x7ec + ((n) << 2)) +#define MT_MIB_ARNG(_band, n) MT_WF_MIB(_band, 0x0b0 + ((n) << 2)) +#define MT_MIB_ARNCR_RANGE(val, n) (((val) >> ((n) << 3)) & GENMASK(7, 0)) + +#define MT_WTBLON_TOP_BASE 0x820d4000 +#define MT_WTBLON_TOP(ofs) (MT_WTBLON_TOP_BASE + (ofs)) + +#define MT_WTBL_UPDATE_BUSY BIT(31) + +#define MT_WTBL_ITCR MT_WTBLON_TOP(0x3b0) +#define MT_WTBL_ITCR_WR BIT(16) +#define MT_WTBL_ITCR_EXEC BIT(31) +#define MT_WTBL_ITDR0 MT_WTBLON_TOP(0x3b8) +#define MT_WTBL_ITDR1 MT_WTBLON_TOP(0x3bc) +#define MT_WTBL_SPE_IDX_SEL BIT(6) + +#define MT_WTBL_BASE 0x820d8000 +#define MT_WTBL_LMAC_ID GENMASK(14, 8) +#define MT_WTBL_LMAC_DW GENMASK(7, 2) +#define MT_WTBL_LMAC_OFFS(_id, _dw) (MT_WTBL_BASE | \ + FIELD_PREP(MT_WTBL_LMAC_ID, _id) | \ + FIELD_PREP(MT_WTBL_LMAC_DW, _dw)) + +/* AGG: band 0(0x20800), band 1(0xa0800) */ +#define MT_WF_AGG_BASE(_band) ((_band) ? 0x820f2000 : 0x820e2000) +#define MT_WF_AGG(_band, ofs) (MT_WF_AGG_BASE(_band) + (ofs)) + +#define MT_AGG_AWSCR0(_band, _n) MT_WF_AGG(_band, 0x05c + (_n) * 4) +#define MT_AGG_PCR0(_band, _n) MT_WF_AGG(_band, 0x06c + (_n) * 4) +#define MT_AGG_PCR0_MM_PROT BIT(0) +#define MT_AGG_PCR0_GF_PROT BIT(1) +#define MT_AGG_PCR0_BW20_PROT BIT(2) +#define MT_AGG_PCR0_BW40_PROT BIT(4) +#define MT_AGG_PCR0_BW80_PROT BIT(6) +#define MT_AGG_PCR0_ERP_PROT GENMASK(12, 8) +#define MT_AGG_PCR0_VHT_PROT BIT(13) +#define MT_AGG_PCR0_PTA_WIN_DIS BIT(15) + +#define MT_AGG_PCR1_RTS0_NUM_THRES GENMASK(31, 23) +#define MT_AGG_PCR1_RTS0_LEN_THRES GENMASK(19, 0) + +#define MT_AGG_ACR0(_band) MT_WF_AGG(_band, 0x084) +#define MT_AGG_ACR_CFEND_RATE GENMASK(13, 0) +#define MT_AGG_ACR_BAR_RATE GENMASK(29, 16) + +#define MT_AGG_MRCR(_band) MT_WF_AGG(_band, 0x098) +#define MT_AGG_MRCR_BAR_CNT_LIMIT GENMASK(15, 12) +#define MT_AGG_MRCR_LAST_RTS_CTS_RN BIT(6) +#define MT_AGG_MRCR_RTS_FAIL_LIMIT GENMASK(11, 7) +#define MT_AGG_MRCR_TXCMD_RTS_FAIL_LIMIT GENMASK(28, 24) + +#define MT_AGG_ATCR1(_band) MT_WF_AGG(_band, 0x0f0) +#define MT_AGG_ATCR3(_band) MT_WF_AGG(_band, 0x0f4) + +/* ARB: band 0(0x20c00), band 1(0xa0c00) */ +#define MT_WF_ARB_BASE(_band) ((_band) ? 0x820f3000 : 0x820e3000) +#define MT_WF_ARB(_band, ofs) (MT_WF_ARB_BASE(_band) + (ofs)) + +#define MT_ARB_SCR(_band) MT_WF_ARB(_band, 0x080) +#define MT_ARB_SCR_TX_DISABLE BIT(8) +#define MT_ARB_SCR_RX_DISABLE BIT(9) + +#define MT_ARB_DRNGR0(_band, _n) MT_WF_ARB(_band, 0x194 + (_n) * 4) + +/* RMAC: band 0(0x21400), band 1(0xa1400) */ +#define MT_WF_RMAC_BASE(_band) ((_band) ? 0x820f5000 : 0x820e5000) +#define MT_WF_RMAC(_band, ofs) (MT_WF_RMAC_BASE(_band) + (ofs)) + +#define MT_WF_RFCR(_band) MT_WF_RMAC(_band, 0x000) +#define MT_WF_RFCR_DROP_STBC_MULTI BIT(0) +#define MT_WF_RFCR_DROP_FCSFAIL BIT(1) +#define MT_WF_RFCR_DROP_VERSION BIT(3) +#define MT_WF_RFCR_DROP_PROBEREQ BIT(4) +#define MT_WF_RFCR_DROP_MCAST BIT(5) +#define MT_WF_RFCR_DROP_BCAST BIT(6) +#define MT_WF_RFCR_DROP_MCAST_FILTERED BIT(7) +#define MT_WF_RFCR_DROP_A3_MAC BIT(8) +#define MT_WF_RFCR_DROP_A3_BSSID BIT(9) +#define MT_WF_RFCR_DROP_A2_BSSID BIT(10) +#define MT_WF_RFCR_DROP_OTHER_BEACON BIT(11) +#define MT_WF_RFCR_DROP_FRAME_REPORT BIT(12) +#define MT_WF_RFCR_DROP_CTL_RSV BIT(13) +#define MT_WF_RFCR_DROP_CTS BIT(14) +#define MT_WF_RFCR_DROP_RTS BIT(15) +#define MT_WF_RFCR_DROP_DUPLICATE BIT(16) +#define MT_WF_RFCR_DROP_OTHER_BSS BIT(17) +#define MT_WF_RFCR_DROP_OTHER_UC BIT(18) +#define MT_WF_RFCR_DROP_OTHER_TIM BIT(19) +#define MT_WF_RFCR_DROP_NDPA BIT(20) +#define MT_WF_RFCR_DROP_UNWANTED_CTL BIT(21) + +#define MT_WF_RFCR1(_band) MT_WF_RMAC(_band, 0x004) +#define MT_WF_RFCR1_DROP_ACK BIT(4) +#define MT_WF_RFCR1_DROP_BF_POLL BIT(5) +#define MT_WF_RFCR1_DROP_BA BIT(6) +#define MT_WF_RFCR1_DROP_CFEND BIT(7) +#define MT_WF_RFCR1_DROP_CFACK BIT(8) + +#define MT_WF_RMAC_MIB_TIME0(_band) MT_WF_RMAC(_band, 0x03c4) +#define MT_WF_RMAC_MIB_RXTIME_CLR BIT(31) +#define MT_WF_RMAC_MIB_RXTIME_EN BIT(30) + +#define MT_WF_RMAC_MIB_AIRTIME14(_band) MT_WF_RMAC(_band, 0x03b8) +#define MT_MIB_OBSSTIME_MASK GENMASK(23, 0) +#define MT_WF_RMAC_MIB_AIRTIME0(_band) MT_WF_RMAC(_band, 0x0380) + +/* WFDMA0 */ +#define MT_WFDMA0_BASE 0xd4000 +#define MT_WFDMA0(ofs) (MT_WFDMA0_BASE + (ofs)) + +#define MT_WFDMA0_RST MT_WFDMA0(0x100) +#define MT_WFDMA0_RST_LOGIC_RST BIT(4) +#define MT_WFDMA0_RST_DMASHDL_ALL_RST BIT(5) + +#define MT_WFDMA0_BUSY_ENA MT_WFDMA0(0x13c) +#define MT_WFDMA0_BUSY_ENA_TX_FIFO0 BIT(0) +#define MT_WFDMA0_BUSY_ENA_TX_FIFO1 BIT(1) +#define MT_WFDMA0_BUSY_ENA_RX_FIFO BIT(2) + +#define MT_MCU_CMD MT_WFDMA0(0x1f0) +#define MT_MCU_CMD_WAKE_RX_PCIE BIT(0) +#define MT_MCU_CMD_STOP_DMA_FW_RELOAD BIT(1) +#define MT_MCU_CMD_STOP_DMA BIT(2) +#define MT_MCU_CMD_RESET_DONE BIT(3) +#define MT_MCU_CMD_RECOVERY_DONE BIT(4) +#define MT_MCU_CMD_NORMAL_STATE BIT(5) +#define MT_MCU_CMD_ERROR_MASK GENMASK(5, 1) + +#define MT_MCU2HOST_SW_INT_ENA MT_WFDMA0(0x1f4) + +#define MT_WFDMA0_HOST_INT_STA MT_WFDMA0(0x200) +#define HOST_RX_DONE_INT_STS0 BIT(0) /* Rx mcu */ +#define HOST_RX_DONE_INT_STS2 BIT(2) /* Rx data */ +#define HOST_RX_DONE_INT_STS4 BIT(22) /* Rx mcu after fw downloaded */ +#define HOST_TX_DONE_INT_STS16 BIT(26) +#define HOST_TX_DONE_INT_STS17 BIT(27) /* MCU tx done*/ + +#define MT_WFDMA0_GLO_CFG MT_WFDMA0(0x208) +#define MT_WFDMA0_GLO_CFG_TX_DMA_EN BIT(0) +#define MT_WFDMA0_GLO_CFG_TX_DMA_BUSY BIT(1) +#define MT_WFDMA0_GLO_CFG_RX_DMA_EN BIT(2) +#define MT_WFDMA0_GLO_CFG_RX_DMA_BUSY BIT(3) +#define MT_WFDMA0_GLO_CFG_DMA_SIZE GENMASK(5, 4) +#define MT_WFDMA0_GLO_CFG_TX_WB_DDONE BIT(6) +#define MT_WFDMA0_GLO_CFG_FW_DWLD_BYPASS_DMASHDL BIT(9) +#define MT_WFDMA0_GLO_CFG_FIFO_DIS_CHECK BIT(11) +#define MT_WFDMA0_GLO_CFG_FIFO_LITTLE_ENDIAN BIT(12) +#define MT_WFDMA0_GLO_CFG_RX_WB_DDONE BIT(13) +#define MT_WFDMA0_GLO_CFG_CSR_DISP_BASE_PTR_CHAIN_EN BIT(15) +#define MT_WFDMA0_GLO_CFG_CSR_LBK_RX_Q_SEL_EN BIT(20) +#define MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2 BIT(21) +#define MT_WFDMA0_GLO_CFG_ADDR_EXT_EN BIT(26) +#define MT_WFDMA0_GLO_CFG_OMIT_RX_INFO BIT(27) +#define MT_WFDMA0_GLO_CFG_OMIT_TX_INFO BIT(28) +#define MT_WFDMA0_GLO_CFG_CLK_GAT_DIS BIT(30) + +#define HOST_RX_DONE_INT_ENA0 BIT(0) +#define HOST_RX_DONE_INT_ENA1 BIT(1) +#define HOST_RX_DONE_INT_ENA2 BIT(2) +#define HOST_RX_DONE_INT_ENA3 BIT(3) +#define MT7927_RX_DONE_INT_ENA4 BIT(12) +#define MT7927_RX_DONE_INT_ENA6 BIT(14) +#define MT7927_RX_DONE_INT_ENA7 BIT(15) +#define HOST_TX_DONE_INT_ENA0 BIT(4) +#define HOST_TX_DONE_INT_ENA1 BIT(5) +#define HOST_TX_DONE_INT_ENA2 BIT(6) +#define HOST_TX_DONE_INT_ENA3 BIT(7) +#define HOST_TX_DONE_INT_ENA4 BIT(8) +#define HOST_TX_DONE_INT_ENA5 BIT(9) +#define HOST_TX_DONE_INT_ENA6 BIT(10) +#define HOST_TX_DONE_INT_ENA7 BIT(11) +#define HOST_RX_COHERENT_EN BIT(20) +#define HOST_TX_COHERENT_EN BIT(21) +#define MCU2HOST_SW_INT_ENA BIT(29) +#define HOST_TX_DONE_INT_ENA18 BIT(30) + +#define MT_INT_MCU_CMD MCU2HOST_SW_INT_ENA + +#define MT_WFDMA0_RST_DTX_PTR MT_WFDMA0(0x20c) +#define MT_WFDMA0_WPDMA_PAUSE_RXQ_TH10 MT_WFDMA0(0x260) +#define MT_WFDMA0_WPDMA_PAUSE_RXQ_TH32 MT_WFDMA0(0x264) +#define MT_WFDMA0_WPDMA_PAUSE_RXQ_TH54 MT_WFDMA0(0x268) +#define MT_WFDMA0_WPDMA_PAUSE_RXQ_TH76 MT_WFDMA0(0x26c) +#define MT_WFDMA0_WPDMA_PAUSE_RXQ_THXX_L_TH_MASK GENMASK(11, 0) +#define MT_WFDMA0_WPDMA_PAUSE_RXQ_THXX_H_TH_MASK GENMASK(27, 16) + +#define MT_WFDMA0_RST_DRX_PTR MT_WFDMA0(0x280) +#define MT_WFDMA0_INT_RX_PRI MT_WFDMA0(0x298) +#define MT_WFDMA0_INT_TX_PRI MT_WFDMA0(0x29c) +#define MT_WFDMA0_GLO_CFG_EXT0 MT_WFDMA0(0x2b0) +#define MT_WFDMA0_GLO_CFG_EXT0_CSR_MEM_ARB_LOCK_EN BIT(4) +#define MT_WFDMA0_GLO_CFG_EXT0_CSR_TX_DMASHDL_EN BIT(6) +#define MT_WFDMA0_GLO_CFG_EXT0_CSR_TX_DMASHDL_LITE_EN BIT(7) +#define MT_WFDMA0_GLO_CFG_EXT0_CSR_RX_WB_KEEP_RSVD BIT(10) +#define MT_WFDMA0_GLO_CFG_EXT0_CSR_BID_CHECK_BYPASS_EN BIT(22) +#define MT_WFDMA0_GLO_CFG_EXT0_CSR_RX_INFO_WB_EN BIT(23) +#define MT_WFDMA0_GLO_CFG_EXT0_CSR_AXI_AWUSER_LOCK_EN BIT(29) +#define MT_WFDMA0_GLO_CFG_EXT0_CSR_MAX_PREFETCH_CNT_MASK GENMASK(1, 0) +#define MT_WFDMA0_GLO_CFG_EXT0_CSR_MEM_BST_SIZE_MASK GENMASK(3, 2) +#define MT_WFDMA0_GLO_CFG_EXT0_CSR_AXI_AW_OUTSTANDING_NUM_MASK GENMASK(27, 24) + +#define MT_WFDMA0_GLO_CFG_EXT1 MT_WFDMA0(0x2b4) +#define MT_WFDMA0_PRI_DLY_INT_CFG0 MT_WFDMA0(0x2f0) + +#define MT_WFDMA0_TX_RING0_EXT_CTRL MT_WFDMA0(0x600) +#define MT_WFDMA0_TX_RING1_EXT_CTRL MT_WFDMA0(0x604) +#define MT_WFDMA0_TX_RING2_EXT_CTRL MT_WFDMA0(0x608) +#define MT_WFDMA0_TX_RING3_EXT_CTRL MT_WFDMA0(0x60c) +#define MT_WFDMA0_TX_RING4_EXT_CTRL MT_WFDMA0(0x610) +#define MT_WFDMA0_TX_RING5_EXT_CTRL MT_WFDMA0(0x614) +#define MT_WFDMA0_TX_RING6_EXT_CTRL MT_WFDMA0(0x618) +#define MT_WFDMA0_TX_RING15_EXT_CTRL MT_WFDMA0(0x63c) +#define MT_WFDMA0_TX_RING16_EXT_CTRL MT_WFDMA0(0x640) +#define MT_WFDMA0_TX_RING17_EXT_CTRL MT_WFDMA0(0x644) + +#define MT_WPDMA0_MAX_CNT_MASK GENMASK(7, 0) +#define MT_WPDMA0_BASE_PTR_MASK GENMASK(31, 16) + +#define MT_WFDMA0_RX_RING0_EXT_CTRL MT_WFDMA0(0x680) +#define MT_WFDMA0_RX_RING1_EXT_CTRL MT_WFDMA0(0x684) +#define MT_WFDMA0_RX_RING2_EXT_CTRL MT_WFDMA0(0x688) +#define MT_WFDMA0_RX_RING3_EXT_CTRL MT_WFDMA0(0x68c) +#define MT_WFDMA0_RX_RING4_EXT_CTRL MT_WFDMA0(0x690) +#define MT_WFDMA0_RX_RING5_EXT_CTRL MT_WFDMA0(0x694) +#define MT_WFDMA0_RX_RING6_EXT_CTRL MT_WFDMA0(0x698) +#define MT_WFDMA0_RX_RING7_EXT_CTRL MT_WFDMA0(0x69c) + +#define MT_TX_RING_BASE MT_WFDMA0(0x300) +#define MT_RX_EVENT_RING_BASE MT_WFDMA0(0x500) + +/* WFDMA CSR */ +#define MT_WFDMA_EXT_CSR_BASE 0xd7000 +#define MT_WFDMA_EXT_CSR(ofs) (MT_WFDMA_EXT_CSR_BASE + (ofs)) +#define MT_WFDMA_EXT_CSR_HIF_MISC MT_WFDMA_EXT_CSR(0x44) +#define MT_WFDMA_EXT_CSR_HIF_MISC_BUSY BIT(0) + +#define MT_WFDMA_PREFETCH_CTRL MT_WFDMA_EXT_CSR(0x30) +#define MT_WFDMA_PREFETCH_CFG0 MT_WFDMA_EXT_CSR(0xf0) +#define MT_WFDMA_PREFETCH_CFG1 MT_WFDMA_EXT_CSR(0xf4) +#define MT_WFDMA_PREFETCH_CFG2 MT_WFDMA_EXT_CSR(0xf8) +#define MT_WFDMA_PREFETCH_CFG3 MT_WFDMA_EXT_CSR(0xfc) + +#define MT_SWDEF_BASE 0x41f200 +#define MT_SWDEF(ofs) (MT_SWDEF_BASE + (ofs)) +#define MT_SWDEF_MODE MT_SWDEF(0x3c) +#define MT_SWDEF_NORMAL_MODE 0 +#define MT_SWDEF_ICAP_MODE 1 +#define MT_SWDEF_SPECTRUM_MODE 2 + +#define MT_TOP_BASE 0x18060000 +#define MT_TOP(ofs) (MT_TOP_BASE + (ofs)) + +#define MT_TOP_LPCR_HOST_BAND0 MT_TOP(0x10) +#define MT_TOP_LPCR_HOST_FW_OWN BIT(0) +#define MT_TOP_LPCR_HOST_DRV_OWN BIT(1) + +#define MT_TOP_MISC MT_TOP(0xf0) +#define MT_TOP_MISC_FW_STATE GENMASK(2, 0) + +#define MT_MCU_WPDMA0_BASE 0x54000000 +#define MT_MCU_WPDMA0(ofs) (MT_MCU_WPDMA0_BASE + (ofs)) + +#define MT_WFDMA_DUMMY_CR MT_MCU_WPDMA0(0x120) +#define MT_WFDMA_NEED_REINIT BIT(1) + +#define MT_CBTOP_RGU(ofs) (0x70002000 + (ofs)) +#define MT_CBTOP_RGU_WF_SUBSYS_RST MT_CBTOP_RGU(0x600) +#define MT_CBTOP_RGU_WF_SUBSYS_RST_WF_WHOLE_PATH BIT(0) + +#define MT7925_CBTOP_RGU_WF_SUBSYS_RST 0x70028600 +#define MT7925_WFSYS_INIT_DONE_ADDR 0x184c1604 +#define MT7925_WFSYS_INIT_DONE 0x00001d1e + +#define MT_HW_BOUND 0x70010020 +#define MT_HW_CHIPID 0x70010200 +#define MT_HW_REV 0x70010204 + +#define MT_HW_EMI_CTL 0x18011100 +#define MT_HW_EMI_CTL_SLPPROT_EN BIT(1) + +#define MT_PCIE_MAC_PM_L0S_DIS BIT(8) + +#define MT_DMA_SHDL(ofs) (0x7c026000 + (ofs)) +#define MT_DMASHDL_SW_CONTROL MT_DMA_SHDL(0x004) +#define MT_DMASHDL_DMASHDL_BYPASS BIT(28) +#define MT_DMASHDL_OPTIONAL MT_DMA_SHDL(0x008) +#define MT_DMASHDL_PAGE MT_DMA_SHDL(0x00c) +#define MT_DMASHDL_GROUP_SEQ_ORDER BIT(16) +#define MT_DMASHDL_REFILL MT_DMA_SHDL(0x010) +#define MT_DMASHDL_REFILL_MASK GENMASK(31, 16) +#define MT_DMASHDL_PKT_MAX_SIZE MT_DMA_SHDL(0x01c) +#define MT_DMASHDL_PKT_MAX_SIZE_PLE GENMASK(11, 0) +#define MT_DMASHDL_PKT_MAX_SIZE_PSE GENMASK(27, 16) + +#define MT_DMASHDL_GROUP_QUOTA(_n) MT_DMA_SHDL(0x020 + ((_n) << 2)) +#define MT_DMASHDL_GROUP_QUOTA_MIN GENMASK(11, 0) +#define MT_DMASHDL_GROUP_QUOTA_MAX GENMASK(27, 16) + +#define MT_DMASHDL_Q_MAP(_n) MT_DMA_SHDL(0x060 + ((_n) << 2)) +#define MT_DMASHDL_Q_MAP_MASK GENMASK(3, 0) +#define MT_DMASHDL_Q_MAP_SHIFT(_n) (4 * ((_n) % 8)) + +#define MT_DMASHDL_SCHED_SET(_n) MT_DMA_SHDL(0x070 + ((_n) << 2)) + +#define MT_WFDMA_HOST_CONFIG 0x7c027030 +#define MT_WFDMA_HOST_CONFIG_USB_RXEVT_EP4_EN BIT(6) + +#define MT_UMAC(ofs) (0x74000000 + (ofs)) +#define MT_UDMA_TX_QSEL MT_UMAC(0x008) +#define MT_FW_DL_EN BIT(3) + +#define MT_UDMA_WLCFG_1 MT_UMAC(0x00c) +#define MT_WL_RX_AGG_PKT_LMT GENMASK(7, 0) +#define MT_WL_TX_TMOUT_LMT GENMASK(27, 8) + +#define MT_UDMA_WLCFG_0 MT_UMAC(0x18) +#define MT_WL_RX_AGG_TO GENMASK(7, 0) +#define MT_WL_RX_AGG_LMT GENMASK(15, 8) +#define MT_WL_TX_TMOUT_FUNC_EN BIT(16) +#define MT_WL_TX_DPH_CHK_EN BIT(17) +#define MT_WL_RX_MPSZ_PAD0 BIT(18) +#define MT_WL_RX_FLUSH BIT(19) +#define MT_TICK_1US_EN BIT(20) +#define MT_WL_RX_AGG_EN BIT(21) +#define MT_WL_RX_EN BIT(22) +#define MT_WL_TX_EN BIT(23) +#define MT_WL_RX_BUSY BIT(30) +#define MT_WL_TX_BUSY BIT(31) + +#define MT_UDMA_CONN_INFRA_STATUS MT_UMAC(0xa20) +#define MT_UDMA_CONN_WFSYS_INIT_DONE BIT(22) +#define MT_UDMA_CONN_INFRA_STATUS_SEL MT_UMAC(0xa24) + +#define MT_SSUSB_EPCTL_CSR(ofs) (0x74011800 + (ofs)) +#define MT_SSUSB_EPCTL_CSR_EP_RST_OPT MT_SSUSB_EPCTL_CSR(0x090) + +#define MT_UWFDMA0(ofs) (0x7c024000 + (ofs)) +#define MT_UWFDMA0_GLO_CFG MT_UWFDMA0(0x208) +#define MT_UWFDMA0_GLO_CFG_EXT0 MT_UWFDMA0(0x2b0) +#define MT_UWFDMA0_GLO_CFG_EXT1 MT_UWFDMA0(0x2b4) +#define MT_UWFDMA0_TX_RING_EXT_CTRL(_n) MT_UWFDMA0(0x600 + ((_n) << 2)) + +#define MT_CONN_STATUS 0x7c053c10 +#define MT_WIFI_PATCH_DL_STATE BIT(0) + +#define MT_CONN_ON_LPCTL 0x7c060010 +#define PCIE_LPCR_HOST_SET_OWN BIT(0) +#define PCIE_LPCR_HOST_CLR_OWN BIT(1) +#define PCIE_LPCR_HOST_OWN_SYNC BIT(2) + +#define MT_CONN_ON_MISC 0x7c0600f0 +#define MT_TOP_MISC2_FW_PWR_ON BIT(0) +#define MT_TOP_MISC2_FW_N9_ON BIT(1) +#define MT_TOP_MISC2_FW_N9_RDY GENMASK(1, 0) + +#define MT_WF_SW_DEF_CR(ofs) (0x401a00 + (ofs)) +#define MT_WF_SW_DEF_CR_USB_MCU_EVENT MT_WF_SW_DEF_CR(0x028) +#define MT_WF_SW_SER_TRIGGER_SUSPEND BIT(6) +#define MT_WF_SW_SER_DONE_SUSPEND BIT(7) + +#define WFSYS_SW_RST_B BIT(0) +#define WFSYS_SW_INIT_DONE BIT(4) + +/* CBInfra registers - MT7927 combo chip */ +#define MT7927_CBINFRA_RGU_WF_RST 0x1f8600 +#define MT7927_CBINFRA_RGU_WF_RST_WF_SUBSYS BIT(4) +#define MT7927_CBINFRA_MCU_OWN_SET 0x1f5034 +#define MT7927_ROMCODE_INDEX 0xc1604 +#define MT7927_MCU_IDLE_VALUE 0x1d1e +#define MT7927_PCIE2AP_REMAP_WF_0_54 0x21008 +#define MT7927_PCIE2AP_REMAP_WF_0_54_MASK GENMASK(15, 0) +#define MT7927_PCIE2AP_REMAP_WF_0_54_VAL 0x00001807 +#define MT7927_SEMA_OWN_STA 0x40000 +#define MT7927_SEMA_OWN_STA_REP 0x40400 +#endif /* __MT792X_REGS_H */ diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_trace.c b/drivers/net/wireless/mediatek/mt76/mt792x_trace.c new file mode 100644 index 000000000..ffc77d394 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt792x_trace.c @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (C) 2023 Lorenzo Bianconi + */ + +#include + +#ifndef __CHECKER__ +#define CREATE_TRACE_POINTS +#include "mt792x_trace.h" + +EXPORT_TRACEPOINT_SYMBOL_GPL(lp_event); + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_trace.h b/drivers/net/wireless/mediatek/mt76/mt792x_trace.h new file mode 100644 index 000000000..7b0e3f00b --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt792x_trace.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (C) 2023 Lorenzo Bianconi + */ + +#if !defined(__MT792X_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define __MT792X_TRACE_H + +#include +#include "mt792x.h" + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM mt792x + +#define MAXNAME 32 +#define DEV_ENTRY __array(char, wiphy_name, 32) +#define DEV_ASSIGN strscpy(__entry->wiphy_name, \ + wiphy_name(mt76_hw(dev)->wiphy), MAXNAME) +#define DEV_PR_FMT "%s" +#define DEV_PR_ARG __entry->wiphy_name +#define LP_STATE_PR_ARG __entry->lp_state ? "lp ready" : "lp not ready" + +TRACE_EVENT(lp_event, + TP_PROTO(struct mt792x_dev *dev, u8 lp_state), + + TP_ARGS(dev, lp_state), + + TP_STRUCT__entry( + DEV_ENTRY + __field(u8, lp_state) + ), + + TP_fast_assign( + DEV_ASSIGN; + __entry->lp_state = lp_state; + ), + + TP_printk( + DEV_PR_FMT " %s", + DEV_PR_ARG, LP_STATE_PR_ARG + ) +); + +#endif + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE mt792x_trace + +#include diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_usb.c b/drivers/net/wireless/mediatek/mt76/mt792x_usb.c new file mode 100644 index 000000000..850f6b2cf --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt792x_usb.c @@ -0,0 +1,531 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2023 MediaTek Inc. + * + * Author: Lorenzo Bianconi + */ + +#include +#include +#include + +#include "mt792x.h" +#include "mt76_connac2_mac.h" + +#define MT792X_USB_TX_TIMEOUT_LIMIT 50000 +#define MT792X_USB_UDMA_IDLE_TIMEOUT 1000 + +static int mt792xu_read32(struct mt76_dev *dev, u32 addr, void *buf) +{ + return __mt76u_vendor_request(dev, MT_VEND_READ_EXT, + USB_DIR_IN | MT_USB_TYPE_VENDOR, + (u16)(addr >> 16), (u16)addr, + buf, sizeof(__le32)); +} + +static void mt792xu_reset_work(struct work_struct *work) +{ + struct mt792x_dev *dev = + container_of(work, struct mt792x_dev, usb_reset_work); + struct usb_interface *usb_intf = to_usb_interface(dev->mt76.dev); + + if (usb_intf && usb_get_intfdata(usb_intf) == dev) + usb_queue_reset_device(usb_intf); + + atomic_set(&dev->usb_reset_pending, 0); +} + +static void mt792xu_queue_usb_reset(struct mt792x_dev *dev, int err) +{ + if (!atomic_xchg(&dev->usb_reset_pending, 1)) { + dev_warn(dev->mt76.dev, + "USB transport access failed (%d), queueing device reset\n", + err); + + schedule_work(&dev->usb_reset_work); + } +} + +static u32 mt792xu_bus_hung_rr(struct mt76_dev *mdev, u32 offset) +{ + return 0; +} + +static void mt792xu_bus_hung_wr(struct mt76_dev *mdev, u32 offset, u32 val) +{ +} + +static u32 mt792xu_bus_hung_rmw(struct mt76_dev *mdev, u32 offset, + u32 mask, u32 val) +{ + return 0; +} + +static void mt792xu_bus_hung_write_copy(struct mt76_dev *mdev, u32 offset, + const void *data, int len) +{ +} + +static void mt792xu_bus_hung_read_copy(struct mt76_dev *mdev, u32 offset, + void *data, int len) +{ + memset(data, 0, len); +} + +static const struct mt76_bus_ops mt792xu_bus_hung_ops = { + .rr = mt792xu_bus_hung_rr, + .wr = mt792xu_bus_hung_wr, + .rmw = mt792xu_bus_hung_rmw, + .write_copy = mt792xu_bus_hung_write_copy, + .read_copy = mt792xu_bus_hung_read_copy, + .type = MT76_BUS_USB, +}; + +static void mt792xu_set_bus_hung(struct mt792x_dev *dev) +{ + atomic_set(&dev->mt76.bus_hung, true); + + if (READ_ONCE(dev->mt76.bus) == &mt792xu_bus_hung_ops) + return; + + WRITE_ONCE(dev->mt76.bus, &mt792xu_bus_hung_ops); +} + +static void mt792xu_ctrl_timeout(struct mt76_dev *mdev, int err) +{ + struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); + + mt792xu_set_bus_hung(dev); + mt792xu_queue_usb_reset(dev, err); +} + +void mt792xu_reset_work_init(struct mt792x_dev *dev) +{ + INIT_WORK(&dev->usb_reset_work, mt792xu_reset_work); + atomic_set(&dev->usb_reset_pending, 0); + dev->mt76.usb.ctrl_timeout = mt792xu_ctrl_timeout; +} +EXPORT_SYMBOL_GPL(mt792xu_reset_work_init); + +void mt792xu_reset_work_cleanup(struct mt792x_dev *dev) +{ + cancel_work_sync(&dev->usb_reset_work); + atomic_set(&dev->usb_reset_pending, 0); +} +EXPORT_SYMBOL_GPL(mt792xu_reset_work_cleanup); + +int mt792xu_check_bus(struct mt792x_dev *dev) +{ + int ret; + + mutex_lock(&dev->mt76.usb.usb_ctrl_mtx); + ret = mt792xu_read32(&dev->mt76, MT_HW_CHIPID, dev->mt76.usb.data); + mutex_unlock(&dev->mt76.usb.usb_ctrl_mtx); + + if (ret == sizeof(__le32)) + return 0; + + return ret < 0 ? ret : -EIO; +} +EXPORT_SYMBOL_GPL(mt792xu_check_bus); + +int mt792xu_reset_on_bus_error(struct mt792x_dev *dev) +{ + int err; + + /* Once hung, the no-op bus ops stay installed until the queued USB + * reset re-probes the device. Do not clear bus_hung here, or the caller + * would run a full reset over dropped register I/O and report success. + */ + if (atomic_read(&dev->mt76.bus_hung)) + return -EIO; + + err = mt792xu_check_bus(dev); + if (err) { + mt792xu_set_bus_hung(dev); + mt792xu_queue_usb_reset(dev, err); + + return err; + } + + return 0; +} +EXPORT_SYMBOL_GPL(mt792xu_reset_on_bus_error); + +u32 mt792xu_rr(struct mt76_dev *dev, u32 addr) +{ + u32 ret; + + mutex_lock(&dev->usb.usb_ctrl_mtx); + ret = ___mt76u_rr(dev, MT_VEND_READ_EXT, + USB_DIR_IN | MT_USB_TYPE_VENDOR, addr); + mutex_unlock(&dev->usb.usb_ctrl_mtx); + + return ret; +} +EXPORT_SYMBOL_GPL(mt792xu_rr); + +void mt792xu_wr(struct mt76_dev *dev, u32 addr, u32 val) +{ + mutex_lock(&dev->usb.usb_ctrl_mtx); + ___mt76u_wr(dev, MT_VEND_WRITE_EXT, + USB_DIR_OUT | MT_USB_TYPE_VENDOR, addr, val); + mutex_unlock(&dev->usb.usb_ctrl_mtx); +} +EXPORT_SYMBOL_GPL(mt792xu_wr); + +u32 mt792xu_rmw(struct mt76_dev *dev, u32 addr, u32 mask, u32 val) +{ + mutex_lock(&dev->usb.usb_ctrl_mtx); + val |= ___mt76u_rr(dev, MT_VEND_READ_EXT, + USB_DIR_IN | MT_USB_TYPE_VENDOR, addr) & ~mask; + ___mt76u_wr(dev, MT_VEND_WRITE_EXT, + USB_DIR_OUT | MT_USB_TYPE_VENDOR, addr, val); + mutex_unlock(&dev->usb.usb_ctrl_mtx); + + return val; +} +EXPORT_SYMBOL_GPL(mt792xu_rmw); + +void mt792xu_copy(struct mt76_dev *dev, u32 offset, const void *data, int len) +{ + struct mt76_usb *usb = &dev->usb; + int ret, i = 0, batch_len; + const u8 *val = data; + + len = round_up(len, 4); + + mutex_lock(&usb->usb_ctrl_mtx); + while (i < len) { + batch_len = min_t(int, usb->data_len, len - i); + memcpy(usb->data, val + i, batch_len); + ret = __mt76u_vendor_request(dev, MT_VEND_WRITE_EXT, + USB_DIR_OUT | MT_USB_TYPE_VENDOR, + (offset + i) >> 16, offset + i, + usb->data, batch_len); + if (ret < 0) + break; + + i += batch_len; + } + mutex_unlock(&usb->usb_ctrl_mtx); +} +EXPORT_SYMBOL_GPL(mt792xu_copy); + +int mt792xu_mcu_power_on(struct mt792x_dev *dev) +{ + int ret; + + ret = mt76u_vendor_request(&dev->mt76, MT_VEND_POWER_ON, + USB_DIR_OUT | MT_USB_TYPE_VENDOR, + 0x0, 0x1, NULL, 0); + if (ret) + return ret; + + if (!mt76_poll_msec(dev, MT_CONN_ON_MISC, MT_TOP_MISC2_FW_PWR_ON, + MT_TOP_MISC2_FW_PWR_ON, 500)) { + dev_err(dev->mt76.dev, "Timeout for power on\n"); + ret = -EIO; + } + + return ret; +} +EXPORT_SYMBOL_GPL(mt792xu_mcu_power_on); + +static void mt792xu_cleanup(struct mt792x_dev *dev) +{ + clear_bit(MT76_STATE_INITIALIZED, &dev->mphy.state); + skb_queue_purge(&dev->mt76.mcu.res_q); + mt76u_queues_deinit(&dev->mt76); + mt792xu_wfsys_reset(dev); +} + +static u32 mt792xu_uhw_rr(struct mt76_dev *dev, u32 addr) +{ + u32 ret; + + mutex_lock(&dev->usb.usb_ctrl_mtx); + ret = ___mt76u_rr(dev, MT_VEND_DEV_MODE, + USB_DIR_IN | MT_USB_TYPE_UHW_VENDOR, addr); + mutex_unlock(&dev->usb.usb_ctrl_mtx); + + return ret; +} + +static void mt792xu_uhw_wr(struct mt76_dev *dev, u32 addr, u32 val) +{ + mutex_lock(&dev->usb.usb_ctrl_mtx); + ___mt76u_wr(dev, MT_VEND_WRITE, + USB_DIR_OUT | MT_USB_TYPE_UHW_VENDOR, addr, val); + mutex_unlock(&dev->usb.usb_ctrl_mtx); +} + +static void mt792xu_dma_prefetch(struct mt792x_dev *dev) +{ +#define DMA_PREFETCH_CONF(_idx_, _cnt_, _base_) \ + mt76_rmw(dev, MT_UWFDMA0_TX_RING_EXT_CTRL((_idx_)), \ + MT_WPDMA0_MAX_CNT_MASK | MT_WPDMA0_BASE_PTR_MASK, \ + FIELD_PREP(MT_WPDMA0_MAX_CNT_MASK, (_cnt_)) | \ + FIELD_PREP(MT_WPDMA0_BASE_PTR_MASK, (_base_))) + + DMA_PREFETCH_CONF(0, 4, 0x080); + DMA_PREFETCH_CONF(1, 4, 0x0c0); + DMA_PREFETCH_CONF(2, 4, 0x100); + DMA_PREFETCH_CONF(3, 4, 0x140); + DMA_PREFETCH_CONF(4, 4, 0x180); + DMA_PREFETCH_CONF(16, 4, 0x280); + DMA_PREFETCH_CONF(17, 4, 0x2c0); +} + +static void mt792xu_wfdma_init(struct mt792x_dev *dev) +{ + int i; + + mt792xu_dma_prefetch(dev); + + mt76_clear(dev, MT_UWFDMA0_GLO_CFG, MT_WFDMA0_GLO_CFG_OMIT_RX_INFO); + mt76_set(dev, MT_UWFDMA0_GLO_CFG, + MT_WFDMA0_GLO_CFG_OMIT_TX_INFO | + MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2 | + MT_WFDMA0_GLO_CFG_FW_DWLD_BYPASS_DMASHDL | + MT_WFDMA0_GLO_CFG_TX_DMA_EN | + MT_WFDMA0_GLO_CFG_RX_DMA_EN); + + mt76_rmw(dev, MT_DMASHDL_REFILL, MT_DMASHDL_REFILL_MASK, 0xffe00000); + mt76_clear(dev, MT_DMASHDL_PAGE, MT_DMASHDL_GROUP_SEQ_ORDER); + mt76_rmw(dev, MT_DMASHDL_PKT_MAX_SIZE, + MT_DMASHDL_PKT_MAX_SIZE_PLE | MT_DMASHDL_PKT_MAX_SIZE_PSE, + FIELD_PREP(MT_DMASHDL_PKT_MAX_SIZE_PLE, 1) | + FIELD_PREP(MT_DMASHDL_PKT_MAX_SIZE_PSE, 0)); + for (i = 0; i < 5; i++) + mt76_wr(dev, MT_DMASHDL_GROUP_QUOTA(i), + FIELD_PREP(MT_DMASHDL_GROUP_QUOTA_MIN, 0x3) | + FIELD_PREP(MT_DMASHDL_GROUP_QUOTA_MAX, 0xfff)); + for (i = 5; i < 16; i++) + mt76_wr(dev, MT_DMASHDL_GROUP_QUOTA(i), + FIELD_PREP(MT_DMASHDL_GROUP_QUOTA_MIN, 0x0) | + FIELD_PREP(MT_DMASHDL_GROUP_QUOTA_MAX, 0x0)); + mt76_wr(dev, MT_DMASHDL_Q_MAP(0), 0x32013201); + mt76_wr(dev, MT_DMASHDL_Q_MAP(1), 0x32013201); + mt76_wr(dev, MT_DMASHDL_Q_MAP(2), 0x55555444); + mt76_wr(dev, MT_DMASHDL_Q_MAP(3), 0x55555444); + + mt76_wr(dev, MT_DMASHDL_SCHED_SET(0), 0x76540132); + mt76_wr(dev, MT_DMASHDL_SCHED_SET(1), 0xFEDCBA98); + + mt76_set(dev, MT_WFDMA_DUMMY_CR, MT_WFDMA_NEED_REINIT); +} + +static int mt792xu_dma_rx_evt_ep4(struct mt792x_dev *dev) +{ + if (!mt76_poll(dev, MT_UWFDMA0_GLO_CFG, + MT_WFDMA0_GLO_CFG_RX_DMA_BUSY, 0, 1000)) + return -ETIMEDOUT; + + mt76_clear(dev, MT_UWFDMA0_GLO_CFG, MT_WFDMA0_GLO_CFG_RX_DMA_EN); + mt76_set(dev, MT_WFDMA_HOST_CONFIG, + MT_WFDMA_HOST_CONFIG_USB_RXEVT_EP4_EN); + mt76_set(dev, MT_UWFDMA0_GLO_CFG, MT_WFDMA0_GLO_CFG_RX_DMA_EN); + + return 0; +} + +static void mt792xu_epctl_rst_opt(struct mt792x_dev *dev, bool reset) +{ + u32 val; + + /* usb endpoint reset opt + * bits[4,9]: out blk ep 4-9 + * bits[20,21]: in blk ep 4-5 + * bits[22]: in int ep 6 + */ + val = mt792xu_uhw_rr(&dev->mt76, MT_SSUSB_EPCTL_CSR_EP_RST_OPT); + if (reset) + val |= GENMASK(9, 4) | GENMASK(22, 20); + else + val &= ~(GENMASK(9, 4) | GENMASK(22, 20)); + mt792xu_uhw_wr(&dev->mt76, MT_SSUSB_EPCTL_CSR_EP_RST_OPT, val); +} + +static void mt792xu_wait_udma_idle(struct mt792x_dev *dev) +{ + u32 mask = MT_WL_RX_BUSY | MT_WL_TX_BUSY; + u32 val; + + mt76_set(dev, MT_UDMA_WLCFG_0, MT_WL_RX_FLUSH); + + if (mt76_poll_msec(dev, MT_UDMA_WLCFG_0, mask, 0, + MT792X_USB_UDMA_IDLE_TIMEOUT)) + return; + + val = mt76_rr(dev, MT_UDMA_WLCFG_0); + + dev_warn(dev->mt76.dev, + "UDMA busy before WFSYS reset: WLCFG0=0x%08x\n", val); +} + +struct mt792xu_wfsys_desc { + u32 rst_reg; + u32 done_reg; + u32 done_mask; + u32 done_val; + u32 delay_ms; + bool need_status_sel; +}; + +static const struct mt792xu_wfsys_desc mt7921_wfsys_desc = { + .rst_reg = MT_CBTOP_RGU_WF_SUBSYS_RST, + .done_reg = MT_UDMA_CONN_INFRA_STATUS, + .done_mask = MT_UDMA_CONN_WFSYS_INIT_DONE, + .done_val = MT_UDMA_CONN_WFSYS_INIT_DONE, + .delay_ms = 0, + .need_status_sel = true, +}; + +static const struct mt792xu_wfsys_desc mt7925_wfsys_desc = { + .rst_reg = MT7925_CBTOP_RGU_WF_SUBSYS_RST, + .done_reg = MT7925_WFSYS_INIT_DONE_ADDR, + .done_mask = U32_MAX, + .done_val = MT7925_WFSYS_INIT_DONE, + .delay_ms = 20, + .need_status_sel = false, +}; + +int mt792xu_dma_init(struct mt792x_dev *dev, bool resume) +{ + int err; + + mt792xu_wfdma_init(dev); + + mt76_clear(dev, MT_UDMA_WLCFG_0, MT_WL_RX_FLUSH); + + mt76_set(dev, MT_UDMA_WLCFG_0, + MT_WL_RX_EN | MT_WL_TX_EN | + MT_WL_RX_MPSZ_PAD0 | MT_TICK_1US_EN); + mt76_rmw(dev, MT_UDMA_WLCFG_1, MT_WL_TX_TMOUT_LMT, + FIELD_PREP(MT_WL_TX_TMOUT_LMT, + MT792X_USB_TX_TIMEOUT_LIMIT)); + mt76_set(dev, MT_UDMA_WLCFG_0, MT_WL_TX_TMOUT_FUNC_EN); + mt76_clear(dev, MT_UDMA_WLCFG_0, + MT_WL_RX_AGG_TO | MT_WL_RX_AGG_LMT); + mt76_clear(dev, MT_UDMA_WLCFG_1, MT_WL_RX_AGG_PKT_LMT); + + if (resume) + return 0; + + err = mt792xu_dma_rx_evt_ep4(dev); + if (err) + return err; + + mt792xu_epctl_rst_opt(dev, false); + + return 0; +} +EXPORT_SYMBOL_GPL(mt792xu_dma_init); + +int mt792xu_wfsys_reset(struct mt792x_dev *dev) +{ + const struct mt792xu_wfsys_desc *desc = is_connac3(&dev->mt76) ? + &mt7925_wfsys_desc : + &mt7921_wfsys_desc; + u32 val; + int i; + + if (atomic_read(&dev->mt76.bus_hung)) + return -EIO; + + mt792xu_wait_udma_idle(dev); + mt792xu_epctl_rst_opt(dev, false); + + val = mt792xu_uhw_rr(&dev->mt76, desc->rst_reg); + val |= MT_CBTOP_RGU_WF_SUBSYS_RST_WF_WHOLE_PATH; + mt792xu_uhw_wr(&dev->mt76, desc->rst_reg, val); + + if (desc->delay_ms) + msleep(desc->delay_ms); + else + usleep_range(10, 20); + + val = mt792xu_uhw_rr(&dev->mt76, desc->rst_reg); + val &= ~MT_CBTOP_RGU_WF_SUBSYS_RST_WF_WHOLE_PATH; + mt792xu_uhw_wr(&dev->mt76, desc->rst_reg, val); + + if (desc->need_status_sel) + mt792xu_uhw_wr(&dev->mt76, MT_UDMA_CONN_INFRA_STATUS_SEL, 0); + + for (i = 0; i < MT792x_WFSYS_INIT_RETRY_COUNT; i++) { + val = mt792xu_uhw_rr(&dev->mt76, desc->done_reg); + if ((val & desc->done_mask) == desc->done_val) + break; + + msleep(100); + } + + if (i == MT792x_WFSYS_INIT_RETRY_COUNT) + return -ETIMEDOUT; + + return 0; +} +EXPORT_SYMBOL_GPL(mt792xu_wfsys_reset); + +int mt792xu_init_reset(struct mt792x_dev *dev) +{ + set_bit(MT76_RESET, &dev->mphy.state); + + wake_up(&dev->mt76.mcu.wait); + skb_queue_purge(&dev->mt76.mcu.res_q); + + mt76u_stop_rx(&dev->mt76); + mt76u_stop_tx(&dev->mt76); + + mt792xu_wfsys_reset(dev); + + clear_bit(MT76_RESET, &dev->mphy.state); + + return mt76u_resume_rx(&dev->mt76); +} +EXPORT_SYMBOL_GPL(mt792xu_init_reset); + +void mt792xu_stop(struct ieee80211_hw *hw, bool suspend) +{ + struct mt792x_dev *dev = mt792x_hw_dev(hw); + + mt76u_stop_tx(&dev->mt76); + mt792x_stop(hw, false); +} +EXPORT_SYMBOL_GPL(mt792xu_stop); + +void mt792xu_disconnect(struct usb_interface *usb_intf) +{ + struct mt792x_dev *dev = usb_get_intfdata(usb_intf); + + if (!dev) + return; + + set_bit(MT76_RESET, &dev->mphy.state); + set_bit(MT76_MCU_RESET, &dev->mphy.state); + clear_bit(MT76_STATE_RUNNING, &dev->mphy.state); + wake_up(&dev->mt76.mcu.wait); + skb_queue_purge(&dev->mt76.mcu.res_q); + + cancel_work_sync(&dev->reset_work); + cancel_work_sync(&dev->init_work); + mt76_worker_disable(&dev->mt76.tx_worker); + mt792xu_reset_work_cleanup(dev); + if (!test_bit(MT76_STATE_INITIALIZED, &dev->mphy.state)) { + set_bit(MT76_REMOVED, &dev->mphy.state); + return; + } + + mt76_unregister_device(&dev->mt76); + mt792xu_cleanup(dev); + set_bit(MT76_REMOVED, &dev->mphy.state); + + usb_set_intfdata(usb_intf, NULL); + + mt76_free_device(&dev->mt76); +} +EXPORT_SYMBOL_GPL(mt792xu_disconnect); + +MODULE_DESCRIPTION("MediaTek MT792x USB helpers"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_AUTHOR("Lorenzo Bianconi "); diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/Kconfig b/drivers/net/wireless/mediatek/mt76/mt7996/Kconfig new file mode 100644 index 000000000..5742bce12 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7996/Kconfig @@ -0,0 +1,21 @@ +# SPDX-License-Identifier: BSD-3-Clause-Clear +config MT7996E + tristate "MediaTek MT7996 (PCIe) support" + select MT76_CONNAC_LIB + select WANT_DEV_COREDUMP + select RELAY + depends on MAC80211 + depends on PCI + help + This adds support for MT7996-based PCIe wireless devices, + which support concurrent tri-band operation at 6GHz, 5GHz, + and 2.4GHz IEEE 802.11be 4x4:4SS 4096-QAM, 320MHz channels. + + To compile this driver as a module, choose M here. + +config MT7996_NPU + bool "MT7996 (PCIe) NPU support" + depends on MT7996E + depends on NET_AIROHA_NPU=y || MT76_CORE=NET_AIROHA_NPU + select MT76_NPU + default n diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/Makefile b/drivers/net/wireless/mediatek/mt76/mt7996/Makefile new file mode 100644 index 000000000..69d2d4bb9 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7996/Makefile @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: BSD-3-Clause-Clear + +obj-$(CONFIG_MT7996E) += mt7996e.o + +mt7996e-y := pci.o init.o dma.o eeprom.o main.o mcu.o mac.o \ + debugfs.o mmio.o + +mt7996e-$(CONFIG_MT7996_NPU) += npu.o +mt7996e-$(CONFIG_DEV_COREDUMP) += coredump.o diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/coredump.c b/drivers/net/wireless/mediatek/mt76/mt7996/coredump.c new file mode 100644 index 000000000..5c293ae96 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7996/coredump.c @@ -0,0 +1,268 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2023 MediaTek Inc. */ + +#include +#include +#include +#include +#include "coredump.h" + +static bool coredump_memdump; +module_param(coredump_memdump, bool, 0644); +MODULE_PARM_DESC(coredump_memdump, "Optional ability to dump firmware memory"); + +static const struct mt7996_mem_region mt7996_mem_regions[] = { + { + .start = 0x00800000, + .len = 0x0004ffff, + .name = "ULM0", + }, + { + .start = 0x00900000, + .len = 0x00037fff, + .name = "ULM1", + }, + { + .start = 0x02200000, + .len = 0x0003ffff, + .name = "ULM2", + }, + { + .start = 0x00400000, + .len = 0x00067fff, + .name = "SRAM", + }, + { + .start = 0xe0000000, + .len = 0x0015ffff, + .name = "CRAM0", + }, + { + .start = 0xe0160000, + .len = 0x0011bfff, + .name = "CRAM1", + }, +}; + +const struct mt7996_mem_region* +mt7996_coredump_get_mem_layout(struct mt7996_dev *dev, u32 *num) +{ + switch (mt76_chip(&dev->mt76)) { + case MT7996_DEVICE_ID: + case MT7996_DEVICE_ID_2: + *num = ARRAY_SIZE(mt7996_mem_regions); + return &mt7996_mem_regions[0]; + default: + return NULL; + } +} + +static int mt7996_coredump_get_mem_size(struct mt7996_dev *dev) +{ + const struct mt7996_mem_region *mem_region; + size_t size = 0; + u32 num; + int i; + + mem_region = mt7996_coredump_get_mem_layout(dev, &num); + if (!mem_region) + return 0; + + for (i = 0; i < num; i++) { + size += mem_region->len; + mem_region++; + } + + /* reserve space for the headers */ + size += num * sizeof(struct mt7996_mem_hdr); + /* make sure it is aligned 4 bytes for debug message print out */ + size = ALIGN(size, 4); + + return size; +} + +struct mt7996_crash_data *mt7996_coredump_new(struct mt7996_dev *dev) +{ + struct mt7996_crash_data *crash_data = dev->coredump.crash_data; + + lockdep_assert_held(&dev->dump_mutex); + + if (coredump_memdump && + !mt76_poll_msec(dev, MT_FW_DUMP_STATE, 0x3, 0x2, 500)) + return NULL; + + guid_gen(&crash_data->guid); + ktime_get_real_ts64(&crash_data->timestamp); + + return crash_data; +} + +static void +mt7996_coredump_fw_state(struct mt7996_dev *dev, struct mt7996_coredump *dump, + bool *exception) +{ + u32 count; + + count = mt76_rr(dev, MT_FW_ASSERT_CNT); + + /* normal mode: driver can manually trigger assert for detail info */ + if (!count) + strscpy(dump->fw_state, "normal", sizeof(dump->fw_state)); + else + strscpy(dump->fw_state, "exception", sizeof(dump->fw_state)); + + *exception = !!count; +} + +static void +mt7996_coredump_fw_stack(struct mt7996_dev *dev, struct mt7996_coredump *dump, + bool exception) +{ + u32 oldest, i, idx; + + strscpy(dump->pc_current, "program counter", sizeof(dump->pc_current)); + + /* 0: WM PC log output */ + mt76_wr(dev, MT_CONN_DBG_CTL_OUT_SEL, 0); + /* choose 33th PC log buffer to read current PC index */ + mt76_wr(dev, MT_CONN_DBG_CTL_PC_LOG_SEL, 0x3f); + + /* read current PC */ + dump->pc_stack[0] = mt76_rr(dev, MT_CONN_DBG_CTL_PC_LOG); + + /* stop call stack record */ + if (!exception) { + mt76_clear(dev, MT_MCU_WM_EXCP_PC_CTRL, BIT(0)); + mt76_clear(dev, MT_MCU_WM_EXCP_LR_CTRL, BIT(0)); + } + + oldest = (u32)mt76_get_field(dev, MT_MCU_WM_EXCP_PC_CTRL, + GENMASK(20, 16)) + 2; + for (i = 0; i < 16; i++) { + idx = ((oldest + 2 * i + 1) % 32); + dump->pc_stack[i + 1] = + mt76_rr(dev, MT_MCU_WM_EXCP_PC_LOG + idx * 4); + } + + oldest = (u32)mt76_get_field(dev, MT_MCU_WM_EXCP_LR_CTRL, + GENMASK(20, 16)) + 2; + for (i = 0; i < 16; i++) { + idx = ((oldest + 2 * i + 1) % 32); + dump->lr_stack[i] = + mt76_rr(dev, MT_MCU_WM_EXCP_LR_LOG + idx * 4); + } + + /* start call stack record */ + if (!exception) { + mt76_set(dev, MT_MCU_WM_EXCP_PC_CTRL, BIT(0)); + mt76_set(dev, MT_MCU_WM_EXCP_LR_CTRL, BIT(0)); + } +} + +static struct mt7996_coredump *mt7996_coredump_build(struct mt7996_dev *dev) +{ + struct mt7996_crash_data *crash_data = dev->coredump.crash_data; + struct mt7996_coredump *dump; + struct mt7996_coredump_mem *dump_mem; + size_t len, sofar = 0, hdr_len = sizeof(*dump); + unsigned char *buf; + bool exception; + + len = hdr_len; + + if (coredump_memdump && crash_data->memdump_buf_len) + len += sizeof(*dump_mem) + crash_data->memdump_buf_len; + + sofar += hdr_len; + + /* this is going to get big when we start dumping memory and such, + * so go ahead and use vmalloc. + */ + buf = vzalloc(len); + if (!buf) + return NULL; + + mutex_lock(&dev->dump_mutex); + + dump = (struct mt7996_coredump *)(buf); + dump->len = len; + + /* plain text */ + strscpy(dump->magic, "mt76-crash-dump", sizeof(dump->magic)); + strscpy(dump->kernel, init_utsname()->release, sizeof(dump->kernel)); + strscpy(dump->fw_ver, dev->mt76.hw->wiphy->fw_version, + sizeof(dump->fw_ver)); + + guid_copy(&dump->guid, &crash_data->guid); + dump->tv_sec = crash_data->timestamp.tv_sec; + dump->tv_nsec = crash_data->timestamp.tv_nsec; + dump->device_id = mt76_chip(&dev->mt76); + + mt7996_coredump_fw_state(dev, dump, &exception); + mt7996_coredump_fw_stack(dev, dump, exception); + + /* gather memory content */ + dump_mem = (struct mt7996_coredump_mem *)(buf + sofar); + dump_mem->len = crash_data->memdump_buf_len; + if (coredump_memdump && crash_data->memdump_buf_len) + memcpy(dump_mem->data, crash_data->memdump_buf, + crash_data->memdump_buf_len); + + mutex_unlock(&dev->dump_mutex); + + return dump; +} + +int mt7996_coredump_submit(struct mt7996_dev *dev) +{ + struct mt7996_coredump *dump; + + dump = mt7996_coredump_build(dev); + if (!dump) { + dev_warn(dev->mt76.dev, "no crash dump data found\n"); + return -ENODATA; + } + + dev_coredumpv(dev->mt76.dev, dump, dump->len, GFP_KERNEL); + + return 0; +} + +int mt7996_coredump_register(struct mt7996_dev *dev) +{ + struct mt7996_crash_data *crash_data; + + crash_data = vzalloc(sizeof(*dev->coredump.crash_data)); + if (!crash_data) + return -ENOMEM; + + dev->coredump.crash_data = crash_data; + + if (coredump_memdump) { + crash_data->memdump_buf_len = mt7996_coredump_get_mem_size(dev); + if (!crash_data->memdump_buf_len) + /* no memory content */ + return 0; + + crash_data->memdump_buf = vzalloc(crash_data->memdump_buf_len); + if (!crash_data->memdump_buf) { + vfree(crash_data); + return -ENOMEM; + } + } + + return 0; +} + +void mt7996_coredump_unregister(struct mt7996_dev *dev) +{ + if (dev->coredump.crash_data->memdump_buf) { + vfree(dev->coredump.crash_data->memdump_buf); + dev->coredump.crash_data->memdump_buf = NULL; + dev->coredump.crash_data->memdump_buf_len = 0; + } + + vfree(dev->coredump.crash_data); + dev->coredump.crash_data = NULL; +} + diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/coredump.h b/drivers/net/wireless/mediatek/mt76/mt7996/coredump.h new file mode 100644 index 000000000..baa2f6f50 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7996/coredump.h @@ -0,0 +1,97 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* Copyright (C) 2023 MediaTek Inc. */ + +#ifndef _COREDUMP_H_ +#define _COREDUMP_H_ + +#include "mt7996.h" + +struct mt7996_coredump { + char magic[16]; + + u32 len; + + guid_t guid; + + /* time-of-day stamp */ + u64 tv_sec; + /* time-of-day stamp, nano-seconds */ + u64 tv_nsec; + /* kernel version */ + char kernel[64]; + /* firmware version */ + char fw_ver[ETHTOOL_FWVERS_LEN]; + + u32 device_id; + + /* exception state */ + char fw_state[12]; + + /* program counters */ + char pc_current[16]; + u32 pc_stack[17]; + /* link registers */ + u32 lr_stack[16]; + + /* memory content */ + u8 data[]; +} __packed; + +struct mt7996_coredump_mem { + u32 len; + u8 data[]; +} __packed; + +struct mt7996_mem_hdr { + u32 start; + u32 len; + u8 data[]; +}; + +struct mt7996_mem_region { + u32 start; + size_t len; + + const char *name; +}; + +#ifdef CONFIG_DEV_COREDUMP + +const struct mt7996_mem_region * +mt7996_coredump_get_mem_layout(struct mt7996_dev *dev, u32 *num); +struct mt7996_crash_data *mt7996_coredump_new(struct mt7996_dev *dev); +int mt7996_coredump_submit(struct mt7996_dev *dev); +int mt7996_coredump_register(struct mt7996_dev *dev); +void mt7996_coredump_unregister(struct mt7996_dev *dev); + +#else /* CONFIG_DEV_COREDUMP */ + +static inline const struct mt7996_mem_region * +mt7996_coredump_get_mem_layout(struct mt7996_dev *dev, u32 *num) +{ + return NULL; +} + +static inline int mt7996_coredump_submit(struct mt7996_dev *dev) +{ + return 0; +} + +static inline struct +mt7996_crash_data *mt7996_coredump_new(struct mt7996_dev *dev) +{ + return NULL; +} + +static inline int mt7996_coredump_register(struct mt7996_dev *dev) +{ + return 0; +} + +static inline void mt7996_coredump_unregister(struct mt7996_dev *dev) +{ +} + +#endif /* CONFIG_DEV_COREDUMP */ + +#endif /* _COREDUMP_H_ */ diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c new file mode 100644 index 000000000..b85c3e549 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c @@ -0,0 +1,1078 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (C) 2022 MediaTek Inc. + */ + +#include +#include "mt7996.h" +#include "eeprom.h" +#include "mcu.h" +#include "mac.h" + +#define FW_BIN_LOG_MAGIC 0x44d9c99a + +/** global debugfs **/ + +struct hw_queue_map { + const char *name; + u8 index; + u8 pid; + u8 qid; +}; + +static int +mt7996_implicit_txbf_set(void *data, u64 val) +{ + struct mt7996_dev *dev = data; + + /* The existing connected stations shall reconnect to apply + * new implicit txbf configuration. + */ + dev->ibf = !!val; + + return mt7996_mcu_set_txbf(dev, BF_HW_EN_UPDATE); +} + +static int +mt7996_implicit_txbf_get(void *data, u64 *val) +{ + struct mt7996_dev *dev = data; + + *val = dev->ibf; + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_implicit_txbf, mt7996_implicit_txbf_get, + mt7996_implicit_txbf_set, "%lld\n"); + +/* test knob of system error recovery */ +static ssize_t +mt7996_sys_recovery_set(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct mt7996_dev *dev = file->private_data; + char buf[16], *sep; + int ret = 0; + u16 band, val; + + if (count >= sizeof(buf)) + return -EINVAL; + + if (copy_from_user(buf, user_buf, count)) + return -EFAULT; + + if (count && buf[count - 1] == '\n') + buf[count - 1] = '\0'; + else + buf[count] = '\0'; + + sep = strchr(buf, ','); + if (!sep) + return -EINVAL; + + *sep = 0; + if (kstrtou16(buf, 0, &band) || kstrtou16(sep + 1, 0, &val)) + return -EINVAL; + + switch (val) { + /* + * ,0: grab firmware current SER state. + * ,1: trigger & enable system error L1 recovery. + * ,2: trigger & enable system error L2 recovery. + * ,3: trigger & enable system error L3 rx abort. + * ,4: trigger & enable system error L3 tx abort + * ,5: trigger & enable system error L3 tx disable. + * ,6: trigger & enable system error L3 bf recovery. + * ,7: trigger & enable system error L4 mdp recovery. + * ,8: trigger & enable system error full recovery. + * ,9: trigger firmware crash. + */ + case UNI_CMD_SER_QUERY: + ret = mt7996_mcu_set_ser(dev, UNI_CMD_SER_QUERY, 0, band); + break; + case UNI_CMD_SER_SET_RECOVER_L1: + case UNI_CMD_SER_SET_RECOVER_L2: + case UNI_CMD_SER_SET_RECOVER_L3_RX_ABORT: + case UNI_CMD_SER_SET_RECOVER_L3_TX_ABORT: + case UNI_CMD_SER_SET_RECOVER_L3_TX_DISABLE: + case UNI_CMD_SER_SET_RECOVER_L3_BF: + case UNI_CMD_SER_SET_RECOVER_L4_MDP: + ret = mt7996_mcu_set_ser(dev, UNI_CMD_SER_SET, BIT(val), band); + if (ret) + return ret; + + ret = mt7996_mcu_set_ser(dev, UNI_CMD_SER_TRIGGER, val, band); + break; + + /* enable full chip reset */ + case UNI_CMD_SER_SET_RECOVER_FULL: + mt76_set(dev, MT_WFDMA0_MCU_HOST_INT_ENA, MT_MCU_CMD_WDT_MASK); + dev->recovery.state |= MT_MCU_CMD_WDT_MASK; + mt7996_reset(dev); + break; + + /* WARNING: trigger firmware crash */ + case UNI_CMD_SER_SET_SYSTEM_ASSERT: + ret = mt7996_mcu_trigger_assert(dev); + if (ret) + return ret; + break; + default: + break; + } + + return ret ? ret : count; +} + +static ssize_t +mt7996_sys_recovery_get(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct mt7996_dev *dev = file->private_data; + char *buff; + int desc = 0; + ssize_t ret; + static const size_t bufsz = 1024; + + buff = kmalloc(bufsz, GFP_KERNEL); + if (!buff) + return -ENOMEM; + + /* HELP */ + desc += scnprintf(buff + desc, bufsz - desc, + "Please echo the correct value ...\n"); + desc += scnprintf(buff + desc, bufsz - desc, + ",0: grab firmware transient SER state\n"); + desc += scnprintf(buff + desc, bufsz - desc, + ",1: trigger system error L1 recovery\n"); + desc += scnprintf(buff + desc, bufsz - desc, + ",2: trigger system error L2 recovery\n"); + desc += scnprintf(buff + desc, bufsz - desc, + ",3: trigger system error L3 rx abort\n"); + desc += scnprintf(buff + desc, bufsz - desc, + ",4: trigger system error L3 tx abort\n"); + desc += scnprintf(buff + desc, bufsz - desc, + ",5: trigger system error L3 tx disable\n"); + desc += scnprintf(buff + desc, bufsz - desc, + ",6: trigger system error L3 bf recovery\n"); + desc += scnprintf(buff + desc, bufsz - desc, + ",7: trigger system error L4 mdp recovery\n"); + desc += scnprintf(buff + desc, bufsz - desc, + ",8: trigger system error full recovery\n"); + desc += scnprintf(buff + desc, bufsz - desc, + ",9: trigger firmware crash\n"); + + /* SER statistics */ + desc += scnprintf(buff + desc, bufsz - desc, + "\nlet's dump firmware SER statistics...\n"); + desc += scnprintf(buff + desc, bufsz - desc, + "::E R , SER_STATUS = 0x%08x\n", + mt76_rr(dev, MT_SWDEF_SER_STATS)); + desc += scnprintf(buff + desc, bufsz - desc, + "::E R , SER_PLE_ERR = 0x%08x\n", + mt76_rr(dev, MT_SWDEF_PLE_STATS)); + desc += scnprintf(buff + desc, bufsz - desc, + "::E R , SER_PLE_ERR_1 = 0x%08x\n", + mt76_rr(dev, MT_SWDEF_PLE1_STATS)); + desc += scnprintf(buff + desc, bufsz - desc, + "::E R , SER_PLE_ERR_AMSDU = 0x%08x\n", + mt76_rr(dev, MT_SWDEF_PLE_AMSDU_STATS)); + desc += scnprintf(buff + desc, bufsz - desc, + "::E R , SER_PSE_ERR = 0x%08x\n", + mt76_rr(dev, MT_SWDEF_PSE_STATS)); + desc += scnprintf(buff + desc, bufsz - desc, + "::E R , SER_PSE_ERR_1 = 0x%08x\n", + mt76_rr(dev, MT_SWDEF_PSE1_STATS)); + desc += scnprintf(buff + desc, bufsz - desc, + "::E R , SER_LMAC_WISR6_B0 = 0x%08x\n", + mt76_rr(dev, MT_SWDEF_LAMC_WISR6_BN0_STATS)); + desc += scnprintf(buff + desc, bufsz - desc, + "::E R , SER_LMAC_WISR6_B1 = 0x%08x\n", + mt76_rr(dev, MT_SWDEF_LAMC_WISR6_BN1_STATS)); + desc += scnprintf(buff + desc, bufsz - desc, + "::E R , SER_LMAC_WISR6_B2 = 0x%08x\n", + mt76_rr(dev, MT_SWDEF_LAMC_WISR6_BN2_STATS)); + desc += scnprintf(buff + desc, bufsz - desc, + "::E R , SER_LMAC_WISR7_B0 = 0x%08x\n", + mt76_rr(dev, MT_SWDEF_LAMC_WISR7_BN0_STATS)); + desc += scnprintf(buff + desc, bufsz - desc, + "::E R , SER_LMAC_WISR7_B1 = 0x%08x\n", + mt76_rr(dev, MT_SWDEF_LAMC_WISR7_BN1_STATS)); + desc += scnprintf(buff + desc, bufsz - desc, + "::E R , SER_LMAC_WISR7_B2 = 0x%08x\n", + mt76_rr(dev, MT_SWDEF_LAMC_WISR7_BN2_STATS)); + desc += scnprintf(buff + desc, bufsz - desc, + "\nSYS_RESET_COUNT: WM %d, WA %d\n", + dev->recovery.wm_reset_count, + dev->recovery.wa_reset_count); + + ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc); + kfree(buff); + return ret; +} + +static const struct file_operations mt7996_sys_recovery_ops = { + .write = mt7996_sys_recovery_set, + .read = mt7996_sys_recovery_get, + .open = simple_open, + .llseek = default_llseek, +}; + +static int +mt7996_radar_trigger(void *data, u64 val) +{ +#define RADAR_MAIN_CHAIN 1 +#define RADAR_BACKGROUND 2 + struct mt7996_dev *dev = data; + struct mt7996_phy *phy = mt7996_band_phy(dev, NL80211_BAND_5GHZ); + struct cfg80211_chan_def *chandef; + int rdd_idx, ret; + + if (!phy || !val || val > RADAR_BACKGROUND) + return -EINVAL; + + if (test_bit(MT76_SCANNING, &phy->mt76->state)) + return -EBUSY; + + if (val == RADAR_BACKGROUND) { + if (!dev->rdd2_phy || !cfg80211_chandef_valid(&dev->rdd2_chandef)) { + dev_err(dev->mt76.dev, "Background radar is not enabled\n"); + return -EINVAL; + } + chandef = &dev->rdd2_chandef; + } else { + chandef = &phy->mt76->chandef; + } + + rdd_idx = mt7996_get_rdd_idx(phy, val == RADAR_BACKGROUND); + if (rdd_idx < 0) { + dev_err(dev->mt76.dev, "No RDD found\n"); + return -EINVAL; + } + + ret = cfg80211_chandef_dfs_required(dev->mt76.hw->wiphy, chandef, + NL80211_IFTYPE_AP); + if (ret <= 0) + return ret; + + return mt7996_mcu_rdd_cmd(dev, RDD_RADAR_EMULATE, rdd_idx, 0); +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_radar_trigger, NULL, + mt7996_radar_trigger, "%lld\n"); + +static int +mt7996_rdd_monitor(struct seq_file *s, void *data) +{ + struct mt7996_dev *dev = dev_get_drvdata(s->private); + struct cfg80211_chan_def *chandef = &dev->rdd2_chandef; + const char *bw; + int ret = 0; + + mutex_lock(&dev->mt76.mutex); + + if (!cfg80211_chandef_valid(chandef)) { + ret = -EINVAL; + goto out; + } + + if (!dev->rdd2_phy) { + seq_puts(s, "not running\n"); + goto out; + } + + switch (chandef->width) { + case NL80211_CHAN_WIDTH_40: + bw = "40"; + break; + case NL80211_CHAN_WIDTH_80: + bw = "80"; + break; + case NL80211_CHAN_WIDTH_160: + bw = "160"; + break; + case NL80211_CHAN_WIDTH_80P80: + bw = "80P80"; + break; + default: + bw = "20"; + break; + } + + seq_printf(s, "channel %d (%d MHz) width %s MHz center1: %d MHz\n", + chandef->chan->hw_value, chandef->chan->center_freq, + bw, chandef->center_freq1); +out: + mutex_unlock(&dev->mt76.mutex); + + return ret; +} + +static int +mt7996_fw_debug_wm_set(void *data, u64 val) +{ + struct mt7996_dev *dev = data; + enum { + DEBUG_TXCMD = 62, + DEBUG_CMD_RPT_TX, + DEBUG_CMD_RPT_TRIG, + DEBUG_SPL, + DEBUG_RPT_RX, + DEBUG_RPT_RA = 68, + } debug; + bool tx, rx, en; + int ret; + + dev->fw_debug_wm = val ? MCU_FW_LOG_TO_HOST : 0; + + if (dev->fw_debug_bin) + val = MCU_FW_LOG_RELAY; + else + val = dev->fw_debug_wm; + + tx = dev->fw_debug_wm || (dev->fw_debug_bin & BIT(1)); + rx = dev->fw_debug_wm || (dev->fw_debug_bin & BIT(2)); + en = dev->fw_debug_wm || (dev->fw_debug_bin & BIT(0)); + + ret = mt7996_mcu_fw_log_2_host(dev, MCU_FW_LOG_WM, val); + if (ret) + return ret; + + for (debug = DEBUG_TXCMD; debug <= DEBUG_RPT_RA; debug++) { + if (debug == 67) + continue; + + if (debug == DEBUG_RPT_RX) + val = en && rx; + else + val = en && tx; + + ret = mt7996_mcu_fw_dbg_ctrl(dev, debug, val); + if (ret) + return ret; + } + + return 0; +} + +static int +mt7996_fw_debug_wm_get(void *data, u64 *val) +{ + struct mt7996_dev *dev = data; + + *val = dev->fw_debug_wm; + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_fw_debug_wm, mt7996_fw_debug_wm_get, + mt7996_fw_debug_wm_set, "%lld\n"); + +static int +mt7996_fw_debug_wa_set(void *data, u64 val) +{ + struct mt7996_dev *dev = data; + int ret; + + dev->fw_debug_wa = val ? MCU_FW_LOG_TO_HOST : 0; + + ret = mt7996_mcu_fw_log_2_host(dev, MCU_FW_LOG_WA, dev->fw_debug_wa); + if (ret) + return ret; + + return mt7996_mcu_wa_cmd(dev, MCU_WA_PARAM_CMD(SET), MCU_WA_PARAM_PDMA_RX, + !!dev->fw_debug_wa, 0); +} + +static int +mt7996_fw_debug_wa_get(void *data, u64 *val) +{ + struct mt7996_dev *dev = data; + + *val = dev->fw_debug_wa; + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_fw_debug_wa, mt7996_fw_debug_wa_get, + mt7996_fw_debug_wa_set, "%lld\n"); + +static struct dentry * +create_buf_file_cb(const char *filename, struct dentry *parent, umode_t mode, + struct rchan_buf *buf, int *is_global) +{ + struct dentry *f; + + f = debugfs_create_file("fwlog_data", mode, parent, buf, + &relay_file_operations); + if (IS_ERR(f)) + return NULL; + + *is_global = 1; + + return f; +} + +static int +remove_buf_file_cb(struct dentry *f) +{ + debugfs_remove(f); + + return 0; +} + +static int +mt7996_fw_debug_bin_set(void *data, u64 val) +{ + static struct rchan_callbacks relay_cb = { + .create_buf_file = create_buf_file_cb, + .remove_buf_file = remove_buf_file_cb, + }; + struct mt7996_dev *dev = data; + + if (!dev->relay_fwlog) + dev->relay_fwlog = relay_open("fwlog_data", dev->debugfs_dir, + 1500, 512, &relay_cb, NULL); + if (!dev->relay_fwlog) + return -ENOMEM; + + dev->fw_debug_bin = val; + + relay_reset(dev->relay_fwlog); + + return mt7996_fw_debug_wm_set(dev, dev->fw_debug_wm); +} + +static int +mt7996_fw_debug_bin_get(void *data, u64 *val) +{ + struct mt7996_dev *dev = data; + + *val = dev->fw_debug_bin; + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_fw_debug_bin, mt7996_fw_debug_bin_get, + mt7996_fw_debug_bin_set, "%lld\n"); + +static int +mt7996_fw_util_wa_show(struct seq_file *file, void *data) +{ + struct mt7996_dev *dev = file->private; + + if (dev->fw_debug_wa) + return mt7996_mcu_wa_cmd(dev, MCU_WA_PARAM_CMD(QUERY), + MCU_WA_PARAM_CPU_UTIL, 0, 0); + + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(mt7996_fw_util_wa); + +static void +mt7996_ampdu_stat_read_phy(struct mt7996_phy *phy, struct seq_file *file) +{ + struct mt7996_dev *dev = phy->dev; + int bound[15], range[8], i; + u8 band_idx = phy->mt76->band_idx; + + /* Tx ampdu stat */ + for (i = 0; i < ARRAY_SIZE(range); i++) + range[i] = mt76_rr(dev, MT_MIB_ARNG(band_idx, i)); + + for (i = 0; i < ARRAY_SIZE(bound); i++) + bound[i] = MT_MIB_ARNCR_RANGE(range[i / 2], i % 2) + 1; + + seq_printf(file, "\nPhy %s, Phy band %d\n", + wiphy_name(phy->mt76->hw->wiphy), band_idx); + + seq_printf(file, "Length: %8d | ", bound[0]); + for (i = 0; i < ARRAY_SIZE(bound) - 1; i++) + seq_printf(file, "%3d -%3d | ", + bound[i] + 1, bound[i + 1]); + + seq_puts(file, "\nCount: "); + for (i = 0; i < ARRAY_SIZE(bound); i++) + seq_printf(file, "%8d | ", phy->mt76->aggr_stats[i]); + seq_puts(file, "\n"); + + seq_printf(file, "BA miss count: %d\n", phy->mib.ba_miss_cnt); +} + +static void +mt7996_txbf_stat_read_phy(struct mt7996_phy *phy, struct seq_file *s) +{ + struct mt76_mib_stats *mib = &phy->mib; + static const char * const bw[] = { + "BW20", "BW40", "BW80", "BW160", "BW320" + }; + + /* Tx Beamformer monitor */ + seq_puts(s, "\nTx Beamformer applied PPDU counts: "); + + seq_printf(s, "iBF: %d, eBF: %d\n", + mib->tx_bf_ibf_ppdu_cnt, + mib->tx_bf_ebf_ppdu_cnt); + + /* Tx Beamformer Rx feedback monitor */ + seq_puts(s, "Tx Beamformer Rx feedback statistics: "); + + seq_printf(s, "All: %d, EHT: %d, HE: %d, VHT: %d, HT: %d, ", + mib->tx_bf_rx_fb_all_cnt, + mib->tx_bf_rx_fb_eht_cnt, + mib->tx_bf_rx_fb_he_cnt, + mib->tx_bf_rx_fb_vht_cnt, + mib->tx_bf_rx_fb_ht_cnt); + + seq_printf(s, "%s, NC: %d, NR: %d\n", + bw[mib->tx_bf_rx_fb_bw], + mib->tx_bf_rx_fb_nc_cnt, + mib->tx_bf_rx_fb_nr_cnt); + + /* Tx Beamformee Rx NDPA & Tx feedback report */ + seq_printf(s, "Tx Beamformee successful feedback frames: %d\n", + mib->tx_bf_fb_cpl_cnt); + seq_printf(s, "Tx Beamformee feedback triggered counts: %d\n", + mib->tx_bf_fb_trig_cnt); + + /* Tx SU & MU counters */ + seq_printf(s, "Tx multi-user Beamforming counts: %d\n", + mib->tx_mu_bf_cnt); + seq_printf(s, "Tx multi-user MPDU counts: %d\n", mib->tx_mu_mpdu_cnt); + seq_printf(s, "Tx multi-user successful MPDU counts: %d\n", + mib->tx_mu_acked_mpdu_cnt); + seq_printf(s, "Tx single-user successful MPDU counts: %d\n", + mib->tx_su_acked_mpdu_cnt); + + seq_puts(s, "\n"); +} + +static void +mt7996_tx_stats_show_phy(struct seq_file *file, struct mt7996_phy *phy) +{ + struct mt76_mib_stats *mib = &phy->mib; + u32 attempts, success, per; + int i; + + mt7996_mac_update_stats(phy); + mt7996_ampdu_stat_read_phy(phy, file); + + attempts = mib->tx_mpdu_attempts_cnt; + success = mib->tx_mpdu_success_cnt; + per = attempts ? 100 - success * 100 / attempts : 100; + seq_printf(file, "Tx attempts: %8u (MPDUs)\n", attempts); + seq_printf(file, "Tx success: %8u (MPDUs)\n", success); + seq_printf(file, "Tx PER: %u%%\n", per); + + mt7996_txbf_stat_read_phy(phy, file); + + /* Tx amsdu info */ + seq_puts(file, "Tx MSDU statistics:\n"); + for (i = 0; i < ARRAY_SIZE(mib->tx_amsdu); i++) { + seq_printf(file, "AMSDU pack count of %d MSDU in TXD: %8d ", + i + 1, mib->tx_amsdu[i]); + if (mib->tx_amsdu_cnt) + seq_printf(file, "(%3d%%)\n", + mib->tx_amsdu[i] * 100 / mib->tx_amsdu_cnt); + else + seq_puts(file, "\n"); + } +} + +static int +mt7996_tx_stats_show(struct seq_file *file, void *data) +{ + struct mt7996_dev *dev = file->private; + struct mt7996_phy *phy = &dev->phy; + + mutex_lock(&dev->mt76.mutex); + + mt7996_tx_stats_show_phy(file, phy); + phy = mt7996_phy2(dev); + if (phy) + mt7996_tx_stats_show_phy(file, phy); + phy = mt7996_phy3(dev); + if (phy) + mt7996_tx_stats_show_phy(file, phy); + + mutex_unlock(&dev->mt76.mutex); + + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(mt7996_tx_stats); + +static void +mt7996_hw_queue_read(struct seq_file *s, u32 size, + const struct hw_queue_map *map) +{ + struct mt7996_phy *phy = s->private; + struct mt7996_dev *dev = phy->dev; + u32 i, val; + + val = mt76_rr(dev, MT_FL_Q_EMPTY); + for (i = 0; i < size; i++) { + u32 ctrl, head, tail, queued; + + if (val & BIT(map[i].index)) + continue; + + ctrl = BIT(31) | (map[i].pid << 10) | ((u32)map[i].qid << 24); + mt76_wr(dev, MT_FL_Q0_CTRL, ctrl); + + head = mt76_get_field(dev, MT_FL_Q2_CTRL, + GENMASK(11, 0)); + tail = mt76_get_field(dev, MT_FL_Q2_CTRL, + GENMASK(27, 16)); + queued = mt76_get_field(dev, MT_FL_Q3_CTRL, + GENMASK(11, 0)); + + seq_printf(s, "\t%s: ", map[i].name); + seq_printf(s, "queued:0x%03x head:0x%03x tail:0x%03x\n", + queued, head, tail); + } +} + +static void +mt7996_sta_hw_queue_read(void *data, struct ieee80211_sta *sta) +{ + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct mt7996_vif *mvif = msta->vif; + struct mt7996_phy *phy = mt7996_vif_link_phy(&mvif->deflink); + struct ieee80211_link_sta *link_sta; + struct seq_file *s = data; + struct ieee80211_vif *vif; + struct mt7996_dev *dev; + unsigned int link_id; + + if (!phy) + return; + + vif = container_of((void *)mvif, struct ieee80211_vif, drv_priv); + dev = phy->dev; + + rcu_read_lock(); + + for_each_sta_active_link(vif, sta, link_sta, link_id) { + struct mt7996_sta_link *msta_link; + struct mt76_vif_link *mlink; + u8 ac; + + mlink = rcu_dereference(mvif->mt76.link[link_id]); + if (!mlink) + continue; + + msta_link = mt7996_sta_link(msta, link_id); + if (!msta_link) + continue; + + for (ac = 0; ac < 4; ac++) { + u32 idx = msta_link->wcid.idx >> 5, qlen, ctrl, val; + u8 offs = msta_link->wcid.idx & GENMASK(4, 0); + + ctrl = BIT(31) | BIT(11) | (ac << 24); + val = mt76_rr(dev, MT_PLE_AC_QEMPTY(ac, idx)); + + if (val & BIT(offs)) + continue; + + mt76_wr(dev, + MT_FL_Q0_CTRL, ctrl | msta_link->wcid.idx); + qlen = mt76_get_field(dev, MT_FL_Q3_CTRL, + GENMASK(11, 0)); + seq_printf(s, "\tSTA %pM wcid %d: AC%d%d queued:%d\n", + sta->addr, msta_link->wcid.idx, + mlink->wmm_idx, ac, qlen); + } + } + + rcu_read_unlock(); +} + +static int +mt7996_hw_queues_show(struct seq_file *file, void *data) +{ + struct mt7996_dev *dev = file->private; + struct mt7996_phy *phy = &dev->phy; + static const struct hw_queue_map ple_queue_map[] = { + { "CPU_Q0", 0, 1, MT_CTX0 }, + { "CPU_Q1", 1, 1, MT_CTX0 + 1 }, + { "CPU_Q2", 2, 1, MT_CTX0 + 2 }, + { "CPU_Q3", 3, 1, MT_CTX0 + 3 }, + { "ALTX_Q0", 8, 2, MT_LMAC_ALTX0 }, + { "BMC_Q0", 9, 2, MT_LMAC_BMC0 }, + { "BCN_Q0", 10, 2, MT_LMAC_BCN0 }, + { "PSMP_Q0", 11, 2, MT_LMAC_PSMP0 }, + { "ALTX_Q1", 12, 2, MT_LMAC_ALTX0 + 4 }, + { "BMC_Q1", 13, 2, MT_LMAC_BMC0 + 4 }, + { "BCN_Q1", 14, 2, MT_LMAC_BCN0 + 4 }, + { "PSMP_Q1", 15, 2, MT_LMAC_PSMP0 + 4 }, + }; + static const struct hw_queue_map pse_queue_map[] = { + { "CPU Q0", 0, 1, MT_CTX0 }, + { "CPU Q1", 1, 1, MT_CTX0 + 1 }, + { "CPU Q2", 2, 1, MT_CTX0 + 2 }, + { "CPU Q3", 3, 1, MT_CTX0 + 3 }, + { "HIF_Q0", 8, 0, MT_HIF0 }, + { "HIF_Q1", 9, 0, MT_HIF0 + 1 }, + { "HIF_Q2", 10, 0, MT_HIF0 + 2 }, + { "HIF_Q3", 11, 0, MT_HIF0 + 3 }, + { "HIF_Q4", 12, 0, MT_HIF0 + 4 }, + { "HIF_Q5", 13, 0, MT_HIF0 + 5 }, + { "LMAC_Q", 16, 2, 0 }, + { "MDP_TXQ", 17, 2, 1 }, + { "MDP_RXQ", 18, 2, 2 }, + { "SEC_TXQ", 19, 2, 3 }, + { "SEC_RXQ", 20, 2, 4 }, + }; + u32 val, head, tail; + + /* ple queue */ + val = mt76_rr(dev, MT_PLE_FREEPG_CNT); + head = mt76_get_field(dev, MT_PLE_FREEPG_HEAD_TAIL, GENMASK(11, 0)); + tail = mt76_get_field(dev, MT_PLE_FREEPG_HEAD_TAIL, GENMASK(27, 16)); + seq_puts(file, "PLE page info:\n"); + seq_printf(file, + "\tTotal free page: 0x%08x head: 0x%03x tail: 0x%03x\n", + val, head, tail); + + val = mt76_rr(dev, MT_PLE_PG_HIF_GROUP); + head = mt76_get_field(dev, MT_PLE_HIF_PG_INFO, GENMASK(11, 0)); + tail = mt76_get_field(dev, MT_PLE_HIF_PG_INFO, GENMASK(27, 16)); + seq_printf(file, "\tHIF free page: 0x%03x res: 0x%03x used: 0x%03x\n", + val, head, tail); + + seq_puts(file, "PLE non-empty queue info:\n"); + mt7996_hw_queue_read(file, ARRAY_SIZE(ple_queue_map), + &ple_queue_map[0]); + + /* iterate per-sta ple queue */ + ieee80211_iterate_stations_atomic(phy->mt76->hw, + mt7996_sta_hw_queue_read, file); + phy = mt7996_phy2(dev); + if (phy) + ieee80211_iterate_stations_atomic(phy->mt76->hw, + mt7996_sta_hw_queue_read, file); + phy = mt7996_phy3(dev); + if (phy) + ieee80211_iterate_stations_atomic(phy->mt76->hw, + mt7996_sta_hw_queue_read, file); + + /* pse queue */ + seq_puts(file, "PSE non-empty queue info:\n"); + mt7996_hw_queue_read(file, ARRAY_SIZE(pse_queue_map), + &pse_queue_map[0]); + + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(mt7996_hw_queues); + +static int +mt7996_xmit_queues_show(struct seq_file *file, void *data) +{ + struct mt7996_dev *dev = file->private; + struct mt7996_phy *phy; + struct { + struct mt76_queue *q; + char *queue; + } queue_map[] = { + { dev->mphy.q_tx[MT_TXQ_BE], " MAIN0" }, + { NULL, " MAIN1" }, + { NULL, " MAIN2" }, + { dev->mt76.q_mcu[MT_MCUQ_WM], " MCUWM" }, + { dev->mt76.q_mcu[MT_MCUQ_WA], " MCUWA" }, + { dev->mt76.q_mcu[MT_MCUQ_FWDL], "MCUFWDL" }, + }; + int i; + + phy = mt7996_phy2(dev); + if (phy) + queue_map[1].q = phy->mt76->q_tx[MT_TXQ_BE]; + + phy = mt7996_phy3(dev); + if (phy) + queue_map[2].q = phy->mt76->q_tx[MT_TXQ_BE]; + + seq_puts(file, " queue | hw-queued | head | tail |\n"); + for (i = 0; i < ARRAY_SIZE(queue_map); i++) { + struct mt76_queue *q = queue_map[i].q; + + if (!q) + continue; + + seq_printf(file, " %s | %9d | %9d | %9d |\n", + queue_map[i].queue, q->queued, q->head, + q->tail); + } + + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(mt7996_xmit_queues); + +static int +mt7996_twt_stats(struct seq_file *s, void *data) +{ + struct mt7996_dev *dev = dev_get_drvdata(s->private); + struct mt7996_twt_flow *iter; + + rcu_read_lock(); + + seq_puts(s, " wcid | id | flags | exp | mantissa"); + seq_puts(s, " | duration | tsf |\n"); + list_for_each_entry_rcu(iter, &dev->twt_list, list) + seq_printf(s, + "%9d | %8d | %5c%c%c%c | %8d | %8d | %8d | %14lld |\n", + iter->wcid, iter->id, + iter->sched ? 's' : 'u', + iter->protection ? 'p' : '-', + iter->trigger ? 't' : '-', + iter->flowtype ? '-' : 'a', + iter->exp, iter->mantissa, + iter->duration, iter->tsf); + + rcu_read_unlock(); + + return 0; +} + +/* The index of RF registers use the generic regidx, combined with two parts: + * WF selection [31:24] and offset [23:0]. + */ +static int +mt7996_rf_regval_get(void *data, u64 *val) +{ + struct mt7996_dev *dev = data; + u32 regval; + int ret; + + ret = mt7996_mcu_rf_regval(dev, dev->mt76.debugfs_reg, ®val, false); + if (ret) + return ret; + + *val = regval; + + return 0; +} + +static int +mt7996_rf_regval_set(void *data, u64 val) +{ + struct mt7996_dev *dev = data; + u32 val32 = val; + + return mt7996_mcu_rf_regval(dev, dev->mt76.debugfs_reg, &val32, true); +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_rf_regval, mt7996_rf_regval_get, + mt7996_rf_regval_set, "0x%08llx\n"); + +int mt7996_init_debugfs(struct mt7996_dev *dev) +{ + struct dentry *dir; + + dir = mt76_register_debugfs_fops(&dev->mphy, NULL); + + debugfs_create_file("hw-queues", 0400, dir, dev, + &mt7996_hw_queues_fops); + debugfs_create_file("xmit-queues", 0400, dir, dev, + &mt7996_xmit_queues_fops); + debugfs_create_file("tx_stats", 0400, dir, dev, &mt7996_tx_stats_fops); + debugfs_create_file("sys_recovery", 0600, dir, dev, + &mt7996_sys_recovery_ops); + debugfs_create_file("fw_debug_wm", 0600, dir, dev, &fops_fw_debug_wm); + debugfs_create_file("fw_debug_wa", 0600, dir, dev, &fops_fw_debug_wa); + debugfs_create_file("fw_debug_bin", 0600, dir, dev, &fops_fw_debug_bin); + /* TODO: wm fw cpu utilization */ + debugfs_create_file("fw_util_wa", 0400, dir, dev, + &mt7996_fw_util_wa_fops); + debugfs_create_file("implicit_txbf", 0600, dir, dev, + &fops_implicit_txbf); + debugfs_create_devm_seqfile(dev->mt76.dev, "twt_stats", dir, + mt7996_twt_stats); + debugfs_create_file("rf_regval", 0600, dir, dev, &fops_rf_regval); + + debugfs_create_u32("dfs_hw_pattern", 0400, dir, &dev->hw_pattern); + debugfs_create_file("radar_trigger", 0200, dir, dev, + &fops_radar_trigger); + debugfs_create_devm_seqfile(dev->mt76.dev, "rdd_monitor", dir, + mt7996_rdd_monitor); + + dev->debugfs_dir = dir; + + return 0; +} + +static void +mt7996_debugfs_write_fwlog(struct mt7996_dev *dev, const void *hdr, int hdrlen, + const void *data, int len) +{ + static DEFINE_SPINLOCK(lock); + unsigned long flags; + void *dest; + + spin_lock_irqsave(&lock, flags); + dest = relay_reserve(dev->relay_fwlog, hdrlen + len + 4); + if (dest) { + *(u32 *)dest = hdrlen + len; + dest += 4; + + if (hdrlen) { + memcpy(dest, hdr, hdrlen); + dest += hdrlen; + } + + memcpy(dest, data, len); + relay_flush(dev->relay_fwlog); + } + spin_unlock_irqrestore(&lock, flags); +} + +void mt7996_debugfs_rx_fw_monitor(struct mt7996_dev *dev, const void *data, int len) +{ + struct { + __le32 magic; + u8 version; + u8 _rsv; + __le16 serial_id; + __le32 timestamp; + __le16 msg_type; + __le16 len; + } hdr = { + .version = 0x1, + .magic = cpu_to_le32(FW_BIN_LOG_MAGIC), + .msg_type = cpu_to_le16(PKT_TYPE_RX_FW_MONITOR), + }; + + if (!dev->relay_fwlog) + return; + + hdr.serial_id = cpu_to_le16(dev->fw_debug_seq++); + hdr.timestamp = cpu_to_le32(mt76_rr(dev, MT_LPON_FRCR(0))); + hdr.len = *(__le16 *)data; + mt7996_debugfs_write_fwlog(dev, &hdr, sizeof(hdr), data, len); +} + +bool mt7996_debugfs_rx_log(struct mt7996_dev *dev, const void *data, int len) +{ + if (get_unaligned_le32(data) != FW_BIN_LOG_MAGIC) + return false; + + if (dev->relay_fwlog) + mt7996_debugfs_write_fwlog(dev, NULL, 0, data, len); + + return true; +} + +#ifdef CONFIG_MAC80211_DEBUGFS +/** per-station debugfs **/ + +static int +mt7996_queues_show(struct seq_file *s, void *data) +{ + struct ieee80211_sta *sta = s->private; + + mt7996_sta_hw_queue_read(s, sta); + + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(mt7996_queues); + +void mt7996_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, struct dentry *dir) +{ + debugfs_create_file("hw-queues", 0400, dir, sta, &mt7996_queues_fops); +} + +static ssize_t mt7996_link_sta_fixed_rate_set(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ +#define SHORT_PREAMBLE 0 +#define LONG_PREAMBLE 1 + struct ieee80211_link_sta *link_sta = file->private_data; + struct mt7996_sta *msta = (struct mt7996_sta *)link_sta->sta->drv_priv; + struct mt7996_phy *link_phy = mt7996_vif_link_phy(&msta->vif->deflink); + struct mt7996_sta_link *msta_link; + struct ra_rate phy = {}; + struct mt7996_dev *dev; + char buf[100]; + int ret; + u16 gi, ltf; + + if (!link_phy) + return -EINVAL; + + if (count >= sizeof(buf)) + return -EINVAL; + + if (copy_from_user(buf, user_buf, count)) + return -EFAULT; + + if (count && buf[count - 1] == '\n') + buf[count - 1] = '\0'; + else + buf[count] = '\0'; + + /* mode - cck: 0, ofdm: 1, ht: 2, gf: 3, vht: 4, he_su: 8, he_er: 9 EHT: 15 + * bw - bw20: 0, bw40: 1, bw80: 2, bw160: 3, BW320: 4 + * mcs - cck: 0~4, ofdm: 0~7, ht: 0~32, vht: 0~9, he_su: 0~11, he_er: 0~2, eht: 0~13 + * nss - vht: 1~4, he: 1~4, eht: 1~4, others: ignore + * gi - (ht/vht) lgi: 0, sgi: 1; (he) 0.8us: 0, 1.6us: 1, 3.2us: 2 + * preamble - short: 1, long: 0 + * stbc - off: 0, on: 1 + * ldpc - off: 0, on: 1 + * spe - off: 0, on: 1 + * ltf - 1xltf: 0, 2xltf: 1, 4xltf: 2 + */ + dev = link_phy->dev; + if (sscanf(buf, "%hhu %hhu %hhu %hhu %hu %hhu %hhu %hhu %hhu %hu", + &phy.mode, &phy.bw, &phy.mcs, &phy.nss, &gi, + &phy.preamble, &phy.stbc, &phy.ldpc, &phy.spe, <f) != 10) { + dev_warn(dev->mt76.dev, + "format: Mode BW MCS NSS GI Preamble STBC LDPC SPE ltf\n"); + return -EINVAL; + } + + mutex_lock(&dev->mt76.mutex); + + msta_link = mt7996_sta_link_protected(dev, msta, link_sta->link_id); + if (!msta_link) { + ret = -EINVAL; + goto out; + } + phy.wlan_idx = cpu_to_le16(msta_link->wcid.idx); + phy.gi = cpu_to_le16(gi); + phy.ltf = cpu_to_le16(ltf); + phy.ldpc = phy.ldpc ? 7 : 0; + phy.preamble = phy.preamble ? SHORT_PREAMBLE : LONG_PREAMBLE; + + ret = mt7996_mcu_set_fixed_rate_ctrl(dev, &phy, 0); + if (ret) + goto out; + + ret = count; +out: + mutex_unlock(&dev->mt76.mutex); + return ret; +} + +static const struct file_operations fops_fixed_rate = { + .write = mt7996_link_sta_fixed_rate_set, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +void mt7996_link_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_link_sta *link_sta, + struct dentry *dir) +{ + debugfs_create_file("fixed_rate", 0600, dir, link_sta, &fops_fixed_rate); +} + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/dma.c b/drivers/net/wireless/mediatek/mt76/mt7996/dma.c new file mode 100644 index 000000000..3d9353811 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7996/dma.c @@ -0,0 +1,1057 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (C) 2022 MediaTek Inc. + */ + +#include "mt7996.h" +#include "../dma.h" +#include "mac.h" + +int mt7996_init_tx_queues(struct mt7996_phy *phy, int idx, int n_desc, + int ring_base, struct mtk_wed_device *wed) +{ + struct mt7996_dev *dev = phy->dev; + u32 flags = 0; + + if (mtk_wed_device_active(wed)) { + ring_base += MT_TXQ_ID(0) * MT_RING_SIZE; + idx -= MT_TXQ_ID(0); + + if (wed == &dev->mt76.mmio.wed_hif2) + flags = MT_WED_Q_TX(0); + else + flags = MT_WED_Q_TX(idx); + } + + if (mt76_npu_device_active(&dev->mt76)) + flags = MT_NPU_Q_TX(phy->mt76->band_idx); + + return mt76_connac_init_tx_queues(phy->mt76, idx, n_desc, + ring_base, wed, flags); +} + +static int mt7996_poll_tx(struct napi_struct *napi, int budget) +{ + struct mt7996_dev *dev; + + dev = container_of(napi, struct mt7996_dev, mt76.tx_napi); + + mt76_connac_tx_cleanup(&dev->mt76); + if (napi_complete_done(napi, 0)) + mt7996_irq_enable(dev, MT_INT_TX_DONE_MCU); + + return 0; +} + +static void mt7996_dma_config(struct mt7996_dev *dev) +{ +#define Q_CONFIG(q, wfdma, int, id) do { \ + if (wfdma) \ + dev->q_wfdma_mask |= (1 << (q)); \ + dev->q_int_mask[(q)] = int; \ + dev->q_id[(q)] = id; \ +} while (0) + +#define MCUQ_CONFIG(q, wfdma, int, id) Q_CONFIG(q, (wfdma), (int), (id)) +#define RXQ_CONFIG(q, wfdma, int, id) Q_CONFIG(__RXQ(q), (wfdma), (int), (id)) +#define TXQ_CONFIG(q, wfdma, int, id) Q_CONFIG(__TXQ(q), (wfdma), (int), (id)) + + /* rx queue */ + RXQ_CONFIG(MT_RXQ_MCU, WFDMA0, MT_INT_RX_DONE_WM, MT7996_RXQ_MCU_WM); + /* for mt7990, RX ring 1 is for SDO instead */ + RXQ_CONFIG(MT_RXQ_MCU_WA, WFDMA0, MT_INT_RX_DONE_WA, MT7996_RXQ_MCU_WA); + RXQ_CONFIG(MT_RXQ_MAIN, WFDMA0, MT_INT_RX_DONE_BAND0, MT7996_RXQ_BAND0); + if (mt7996_has_wa(dev)) + RXQ_CONFIG(MT_RXQ_MAIN_WA, WFDMA0, MT_INT_RX_DONE_WA_MAIN, + MT7996_RXQ_MCU_WA_MAIN); + + switch (mt76_chip(&dev->mt76)) { + case MT7992_DEVICE_ID: + RXQ_CONFIG(MT_RXQ_BAND1_WA, WFDMA0, MT_INT_RX_DONE_WA_EXT, MT7996_RXQ_MCU_WA_EXT); + RXQ_CONFIG(MT_RXQ_BAND1, WFDMA0, MT_INT_RX_DONE_BAND1, MT7996_RXQ_BAND1); + break; + case MT7990_DEVICE_ID: + RXQ_CONFIG(MT_RXQ_BAND1, WFDMA0, MT_INT_RX_DONE_BAND1, MT7996_RXQ_BAND1); + RXQ_CONFIG(MT_RXQ_TXFREE_BAND0, WFDMA0, + MT_INT_RX_TXFREE_BAND0_MT7990, MT7990_RXQ_TXFREE0); + if (dev->hif2) + RXQ_CONFIG(MT_RXQ_TXFREE_BAND1, WFDMA0, + MT_INT_RX_TXFREE_BAND1_MT7990, MT7990_RXQ_TXFREE1); + break; + case MT7996_DEVICE_ID: + default: + /* mt7996 band2 */ + RXQ_CONFIG(MT_RXQ_BAND2_WA, WFDMA0, MT_INT_RX_DONE_WA_TRI, MT7996_RXQ_MCU_WA_TRI); + RXQ_CONFIG(MT_RXQ_BAND2, WFDMA0, MT_INT_RX_DONE_BAND2, MT7996_RXQ_BAND2); + break; + } + + if (mt7996_has_hwrro(dev)) { + /* band0 */ + RXQ_CONFIG(MT_RXQ_RRO_BAND0, WFDMA0, MT_INT_RX_DONE_RRO_BAND0, + MT7996_RXQ_RRO_BAND0); + if (dev->mt76.hwrro_mode == MT76_HWRRO_V3) + RXQ_CONFIG(MT_RXQ_MSDU_PAGE_BAND0, WFDMA0, + MT_INT_RX_DONE_MSDU_PG_BAND0, + MT7996_RXQ_MSDU_PG_BAND0); + if (is_mt7996(&dev->mt76)) { + RXQ_CONFIG(MT_RXQ_TXFREE_BAND0, WFDMA0, + MT_INT_RX_TXFREE_MAIN, MT7996_RXQ_TXFREE0); + /* band1 */ + RXQ_CONFIG(MT_RXQ_MSDU_PAGE_BAND1, WFDMA0, + MT_INT_RX_DONE_MSDU_PG_BAND1, + MT7996_RXQ_MSDU_PG_BAND1); + /* band2 */ + RXQ_CONFIG(MT_RXQ_RRO_BAND2, WFDMA0, + MT_INT_RX_DONE_RRO_BAND2, + MT7996_RXQ_RRO_BAND2); + RXQ_CONFIG(MT_RXQ_MSDU_PAGE_BAND2, WFDMA0, + MT_INT_RX_DONE_MSDU_PG_BAND2, + MT7996_RXQ_MSDU_PG_BAND2); + RXQ_CONFIG(MT_RXQ_TXFREE_BAND2, WFDMA0, + MT_INT_RX_TXFREE_TRI, MT7996_RXQ_TXFREE2); + } else { + RXQ_CONFIG(MT_RXQ_RRO_BAND1, WFDMA0, + MT_INT_RX_DONE_RRO_BAND1, + MT7996_RXQ_RRO_BAND1); + } + + if (dev->mt76.hwrro_mode == MT76_HWRRO_V3) + RXQ_CONFIG(MT_RXQ_RRO_IND, WFDMA0, + MT_INT_RX_DONE_RRO_IND, + MT7996_RXQ_RRO_IND); + else + RXQ_CONFIG(MT_RXQ_RRO_RXDMAD_C, WFDMA0, + MT_INT_RX_DONE_RRO_RXDMAD_C, + MT7996_RXQ_RRO_RXDMAD_C); + } + + /* data tx queue */ + if (is_mt7996(&dev->mt76)) { + if (dev->hif2) { + if (mt76_npu_device_active(&dev->mt76)) { + TXQ_CONFIG(0, WFDMA0, MT_INT_TX_DONE_BAND2, + MT7996_TXQ_BAND2); + TXQ_CONFIG(1, WFDMA0, MT_INT_TX_DONE_BAND0, + MT7996_TXQ_BAND0); + TXQ_CONFIG(2, WFDMA0, MT_INT_TX_DONE_BAND1, + MT7996_TXQ_BAND1); + } else { + /* default bn1:ring19 bn2:ring21 */ + TXQ_CONFIG(0, WFDMA0, MT_INT_TX_DONE_BAND0, + MT7996_TXQ_BAND0); + TXQ_CONFIG(1, WFDMA0, MT_INT_TX_DONE_BAND1, + MT7996_TXQ_BAND1); + TXQ_CONFIG(2, WFDMA0, MT_INT_TX_DONE_BAND2, + MT7996_TXQ_BAND2); + } + } else { + /* single pcie bn0/1:ring18 bn2:ring19 */ + TXQ_CONFIG(0, WFDMA0, MT_INT_TX_DONE_BAND0, + MT7996_TXQ_BAND0); + TXQ_CONFIG(2, WFDMA0, MT_INT_TX_DONE_BAND1, + MT7996_TXQ_BAND1); + } + } else { + if (dev->hif2) { + /* bn0:ring18 bn1:ring21 */ + TXQ_CONFIG(0, WFDMA0, MT_INT_TX_DONE_BAND0, + MT7996_TXQ_BAND0); + TXQ_CONFIG(1, WFDMA0, MT_INT_TX_DONE_BAND2, + MT7996_TXQ_BAND2); + } else { + /* single pcie bn0:ring18 bn1:ring19 */ + TXQ_CONFIG(0, WFDMA0, MT_INT_TX_DONE_BAND0, + MT7996_TXQ_BAND0); + TXQ_CONFIG(1, WFDMA0, MT_INT_TX_DONE_BAND1, + MT7996_TXQ_BAND1); + } + } + + /* mcu tx queue */ + MCUQ_CONFIG(MT_MCUQ_FWDL, WFDMA0, MT_INT_TX_DONE_FWDL, MT7996_TXQ_FWDL); + MCUQ_CONFIG(MT_MCUQ_WM, WFDMA0, MT_INT_TX_DONE_MCU_WM, MT7996_TXQ_MCU_WM); + if (mt7996_has_wa(dev)) + MCUQ_CONFIG(MT_MCUQ_WA, WFDMA0, MT_INT_TX_DONE_MCU_WA, + MT7996_TXQ_MCU_WA); +} + +static u32 __mt7996_dma_prefetch_base(u16 *base, u8 depth) +{ + u32 ret = *base << 16 | depth; + + *base = *base + (depth << 4); + + return ret; +} + +static void __mt7996_dma_prefetch(struct mt7996_dev *dev, u32 ofs) +{ + u16 base = 0; + u8 queue, val; + +#define PREFETCH(_depth) (__mt7996_dma_prefetch_base(&base, (_depth))) + /* prefetch SRAM wrapping boundary for tx/rx ring. */ + /* Tx Command Rings */ + val = is_mt7996(&dev->mt76) ? 2 : 4; + mt76_wr(dev, MT_MCUQ_EXT_CTRL(MT_MCUQ_FWDL) + ofs, PREFETCH(val)); + mt76_wr(dev, MT_MCUQ_EXT_CTRL(MT_MCUQ_WM) + ofs, PREFETCH(val)); + if (mt7996_has_wa(dev)) + mt76_wr(dev, MT_MCUQ_EXT_CTRL(MT_MCUQ_WA) + ofs, PREFETCH(val)); + + /* Tx Data Rings */ + mt76_wr(dev, MT_TXQ_EXT_CTRL(0) + ofs, PREFETCH(0x8)); + if (!is_mt7996(&dev->mt76) || dev->hif2) + mt76_wr(dev, MT_TXQ_EXT_CTRL(1) + ofs, PREFETCH(0x8)); + if (is_mt7996(&dev->mt76)) + mt76_wr(dev, MT_TXQ_EXT_CTRL(2) + ofs, PREFETCH(0x8)); + + /* Rx Event Rings */ + mt76_wr(dev, MT_RXQ_EXT_CTRL(MT_RXQ_MCU) + ofs, PREFETCH(val)); + mt76_wr(dev, MT_RXQ_EXT_CTRL(MT_RXQ_MCU_WA) + ofs, PREFETCH(val)); + + /* Rx TxFreeDone From WA Rings */ + if (mt7996_has_wa(dev)) { + mt76_wr(dev, MT_RXQ_EXT_CTRL(MT_RXQ_MAIN_WA) + ofs, PREFETCH(val)); + queue = is_mt7996(&dev->mt76) ? MT_RXQ_BAND2_WA : MT_RXQ_BAND1_WA; + mt76_wr(dev, MT_RXQ_EXT_CTRL(queue) + ofs, PREFETCH(val)); + } + + /* Rx TxFreeDone From MAC Rings */ + val = is_mt7996(&dev->mt76) ? 4 : 8; + if ((is_mt7996(&dev->mt76) && mt7996_has_hwrro(dev)) || + is_mt7990(&dev->mt76)) + mt76_wr(dev, MT_RXQ_EXT_CTRL(MT_RXQ_TXFREE_BAND0) + ofs, PREFETCH(val)); + if (is_mt7990(&dev->mt76) && dev->hif2) + mt76_wr(dev, MT_RXQ_EXT_CTRL(MT_RXQ_TXFREE_BAND1) + ofs, PREFETCH(val)); + else if (is_mt7996(&dev->mt76) && mt7996_has_hwrro(dev)) + mt76_wr(dev, MT_RXQ_EXT_CTRL(MT_RXQ_TXFREE_BAND2) + ofs, PREFETCH(val)); + + /* Rx Data Rings */ + mt76_wr(dev, MT_RXQ_EXT_CTRL(MT_RXQ_MAIN) + ofs, PREFETCH(0x10)); + queue = is_mt7996(&dev->mt76) ? MT_RXQ_BAND2 : MT_RXQ_BAND1; + mt76_wr(dev, MT_RXQ_EXT_CTRL(queue) + ofs, PREFETCH(0x10)); + + /* Rx RRO Rings */ + if (mt7996_has_hwrro(dev)) { + mt76_wr(dev, MT_RXQ_EXT_CTRL(MT_RXQ_RRO_BAND0) + ofs, PREFETCH(0x10)); + queue = is_mt7996(&dev->mt76) ? MT_RXQ_RRO_BAND2 : MT_RXQ_RRO_BAND1; + mt76_wr(dev, MT_RXQ_EXT_CTRL(queue) + ofs, PREFETCH(0x10)); + + mt76_wr(dev, MT_RXQ_EXT_CTRL(MT_RXQ_MSDU_PAGE_BAND0) + ofs, PREFETCH(val)); + if (is_mt7996(&dev->mt76)) { + mt76_wr(dev, MT_RXQ_EXT_CTRL(MT_RXQ_MSDU_PAGE_BAND1) + ofs, + PREFETCH(val)); + mt76_wr(dev, MT_RXQ_EXT_CTRL(MT_RXQ_MSDU_PAGE_BAND2) + ofs, + PREFETCH(val)); + } + } +#undef PREFETCH + + mt76_set(dev, WF_WFDMA0_GLO_CFG_EXT1 + ofs, WF_WFDMA0_GLO_CFG_EXT1_CALC_MODE); +} + +void mt7996_dma_prefetch(struct mt7996_dev *dev) +{ + __mt7996_dma_prefetch(dev, 0); + if (dev->hif2) + __mt7996_dma_prefetch(dev, MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0)); +} + +static void mt7996_dma_disable(struct mt7996_dev *dev, bool reset) +{ + u32 hif1_ofs = 0; + + if (dev->hif2) + hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0); + + if (reset) { + mt76_clear(dev, MT_WFDMA0_RST, + MT_WFDMA0_RST_DMASHDL_ALL_RST | + MT_WFDMA0_RST_LOGIC_RST); + + mt76_set(dev, MT_WFDMA0_RST, + MT_WFDMA0_RST_DMASHDL_ALL_RST | + MT_WFDMA0_RST_LOGIC_RST); + + if (dev->hif2) { + mt76_clear(dev, MT_WFDMA0_RST + hif1_ofs, + MT_WFDMA0_RST_DMASHDL_ALL_RST | + MT_WFDMA0_RST_LOGIC_RST); + + mt76_set(dev, MT_WFDMA0_RST + hif1_ofs, + MT_WFDMA0_RST_DMASHDL_ALL_RST | + MT_WFDMA0_RST_LOGIC_RST); + } + } + + /* disable */ + mt76_clear(dev, MT_WFDMA0_GLO_CFG, + MT_WFDMA0_GLO_CFG_TX_DMA_EN | + MT_WFDMA0_GLO_CFG_RX_DMA_EN | + MT_WFDMA0_GLO_CFG_OMIT_TX_INFO | + MT_WFDMA0_GLO_CFG_OMIT_RX_INFO | + MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2); + + if (dev->hif2) { + mt76_clear(dev, MT_WFDMA0_GLO_CFG + hif1_ofs, + MT_WFDMA0_GLO_CFG_TX_DMA_EN | + MT_WFDMA0_GLO_CFG_RX_DMA_EN | + MT_WFDMA0_GLO_CFG_OMIT_TX_INFO | + MT_WFDMA0_GLO_CFG_OMIT_RX_INFO | + MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2); + } +} + +void mt7996_dma_start(struct mt7996_dev *dev, bool reset, bool wed_reset) +{ + struct mtk_wed_device *wed = &dev->mt76.mmio.wed; + u32 hif1_ofs = 0; + u32 irq_mask; + + if (dev->hif2) + hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0); + + /* enable WFDMA Tx/Rx */ + if (!reset) { + if (mtk_wed_device_active(wed) && mtk_wed_get_rx_capa(wed)) + mt76_set(dev, MT_WFDMA0_GLO_CFG, + MT_WFDMA0_GLO_CFG_TX_DMA_EN | + MT_WFDMA0_GLO_CFG_OMIT_TX_INFO | + MT_WFDMA0_GLO_CFG_EXT_EN); + else + mt76_set(dev, MT_WFDMA0_GLO_CFG, + MT_WFDMA0_GLO_CFG_TX_DMA_EN | + MT_WFDMA0_GLO_CFG_RX_DMA_EN | + MT_WFDMA0_GLO_CFG_OMIT_TX_INFO | + MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2 | + MT_WFDMA0_GLO_CFG_EXT_EN); + + if (dev->hif2) + mt76_set(dev, MT_WFDMA0_GLO_CFG + hif1_ofs, + MT_WFDMA0_GLO_CFG_TX_DMA_EN | + MT_WFDMA0_GLO_CFG_RX_DMA_EN | + MT_WFDMA0_GLO_CFG_OMIT_TX_INFO | + MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2 | + MT_WFDMA0_GLO_CFG_EXT_EN); + } + + /* enable interrupts for TX/RX rings */ + irq_mask = MT_INT_MCU_CMD | MT_INT_RX_DONE_MCU | MT_INT_TX_DONE_MCU; + + if (mt7996_band_valid(dev, MT_BAND0)) + irq_mask |= MT_INT_BAND0_RX_DONE; + + if (mt7996_band_valid(dev, MT_BAND1)) { + irq_mask |= MT_INT_BAND1_RX_DONE; + if (is_mt7992(&dev->mt76) && dev->hif2) + irq_mask |= MT_INT_RX_TXFREE_BAND1_EXT; + } + + if (mt7996_band_valid(dev, MT_BAND2)) + irq_mask |= MT_INT_BAND2_RX_DONE | MT_INT_TX_RX_DONE_EXT; + + if (mtk_wed_device_active(wed) && wed_reset) { + u32 wed_irq_mask = irq_mask; + + wed_irq_mask |= MT_INT_TX_DONE_BAND0 | MT_INT_TX_DONE_BAND1; + mt76_wr(dev, MT_INT_MASK_CSR, wed_irq_mask); + mtk_wed_device_start(wed, wed_irq_mask); + } + + if (!mt7996_has_wa(dev) || mt76_npu_device_active(&dev->mt76)) + irq_mask &= ~(MT_INT_RX(MT_RXQ_MAIN_WA) | + MT_INT_RX(MT_RXQ_BAND1_WA)); + if (is_mt7996(&dev->mt76) && mt76_npu_device_active(&dev->mt76)) + irq_mask &= ~(MT_INT_RX(MT_RXQ_TXFREE_BAND0) | + MT_INT_RX(MT_RXQ_MSDU_PAGE_BAND2)); + irq_mask = reset ? MT_INT_MCU_CMD : irq_mask; + + mt7996_irq_enable(dev, irq_mask); + mt7996_irq_disable(dev, 0); +} + +static void mt7996_dma_enable(struct mt7996_dev *dev, bool reset) +{ + u32 hif1_ofs = 0; + + if (dev->hif2) + hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0); + + /* reset dma idx */ + mt76_wr(dev, MT_WFDMA0_RST_DTX_PTR, ~0); + if (dev->hif2) + mt76_wr(dev, MT_WFDMA0_RST_DTX_PTR + hif1_ofs, ~0); + + /* configure delay interrupt off */ + mt76_wr(dev, MT_WFDMA0_PRI_DLY_INT_CFG0, 0); + mt76_wr(dev, MT_WFDMA0_PRI_DLY_INT_CFG1, 0); + mt76_wr(dev, MT_WFDMA0_PRI_DLY_INT_CFG2, 0); + + if (dev->hif2) { + mt76_wr(dev, MT_WFDMA0_PRI_DLY_INT_CFG0 + hif1_ofs, 0); + mt76_wr(dev, MT_WFDMA0_PRI_DLY_INT_CFG1 + hif1_ofs, 0); + mt76_wr(dev, MT_WFDMA0_PRI_DLY_INT_CFG2 + hif1_ofs, 0); + } + + /* configure perfetch settings */ + mt7996_dma_prefetch(dev); + + /* hif wait WFDMA idle */ + mt76_set(dev, MT_WFDMA0_BUSY_ENA, + MT_WFDMA0_BUSY_ENA_TX_FIFO0 | + MT_WFDMA0_BUSY_ENA_TX_FIFO1 | + MT_WFDMA0_BUSY_ENA_RX_FIFO); + + if (dev->hif2) + mt76_set(dev, MT_WFDMA0_BUSY_ENA + hif1_ofs, + MT_WFDMA0_PCIE1_BUSY_ENA_TX_FIFO0 | + MT_WFDMA0_PCIE1_BUSY_ENA_TX_FIFO1 | + MT_WFDMA0_PCIE1_BUSY_ENA_RX_FIFO); + + mt76_poll(dev, MT_WFDMA_EXT_CSR_HIF_MISC, + MT_WFDMA_EXT_CSR_HIF_MISC_BUSY, 0, 1000); + + /* GLO_CFG_EXT0 */ + mt76_set(dev, WF_WFDMA0_GLO_CFG_EXT0, + WF_WFDMA0_GLO_CFG_EXT0_RX_WB_RXD | + WF_WFDMA0_GLO_CFG_EXT0_WED_MERGE_MODE); + + /* GLO_CFG_EXT1 */ + mt76_set(dev, WF_WFDMA0_GLO_CFG_EXT1, + WF_WFDMA0_GLO_CFG_EXT1_TX_FCTRL_MODE); + + /* WFDMA rx threshold */ + mt76_wr(dev, MT_WFDMA0_PAUSE_RX_Q_45_TH, 0xc000c); + mt76_wr(dev, MT_WFDMA0_PAUSE_RX_Q_67_TH, 0x10008); + mt76_wr(dev, MT_WFDMA0_PAUSE_RX_Q_89_TH, 0x10008); + mt76_wr(dev, MT_WFDMA0_PAUSE_RX_Q_RRO_TH, 0x20); + + if (dev->hif2) { + /* GLO_CFG_EXT0 */ + mt76_set(dev, WF_WFDMA0_GLO_CFG_EXT0 + hif1_ofs, + WF_WFDMA0_GLO_CFG_EXT0_RX_WB_RXD | + WF_WFDMA0_GLO_CFG_EXT0_WED_MERGE_MODE); + + /* GLO_CFG_EXT1 */ + mt76_set(dev, WF_WFDMA0_GLO_CFG_EXT1 + hif1_ofs, + WF_WFDMA0_GLO_CFG_EXT1_TX_FCTRL_MODE); + + mt76_set(dev, MT_WFDMA_HOST_CONFIG, + MT_WFDMA_HOST_CONFIG_PDMA_BAND); + + mt76_clear(dev, MT_WFDMA_HOST_CONFIG, + MT_WFDMA_HOST_CONFIG_BAND0_PCIE1 | + MT_WFDMA_HOST_CONFIG_BAND1_PCIE1 | + MT_WFDMA_HOST_CONFIG_BAND2_PCIE1); + + if (is_mt7996(&dev->mt76)) { + if (mt76_npu_device_active(&dev->mt76)) + mt76_set(dev, MT_WFDMA_HOST_CONFIG, + MT_WFDMA_HOST_CONFIG_BAND0_PCIE1); + else + mt76_set(dev, MT_WFDMA_HOST_CONFIG, + MT_WFDMA_HOST_CONFIG_BAND2_PCIE1); + } else { + mt76_set(dev, MT_WFDMA_HOST_CONFIG, + MT_WFDMA_HOST_CONFIG_BAND1_PCIE1); + } + + /* AXI read outstanding number */ + mt76_rmw(dev, MT_WFDMA_AXI_R2A_CTRL, + MT_WFDMA_AXI_R2A_CTRL_OUTSTAND_MASK, 0x14); + + if (!is_mt7996(&dev->mt76) || + !mt76_npu_device_active(&dev->mt76)) { + if (dev->hif2->speed < PCIE_SPEED_5_0GT || + (dev->hif2->speed == PCIE_SPEED_5_0GT && + dev->hif2->width < PCIE_LNK_X2)) { + mt76_rmw(dev, + WF_WFDMA0_GLO_CFG_EXT0 + hif1_ofs, + WF_WFDMA0_GLO_CFG_EXT0_OUTSTAND_MASK, + FIELD_PREP(WF_WFDMA0_GLO_CFG_EXT0_OUTSTAND_MASK, + 0x1)); + mt76_rmw(dev, MT_WFDMA_AXI_R2A_CTRL2, + MT_WFDMA_AXI_R2A_CTRL2_OUTSTAND_MASK, + FIELD_PREP(MT_WFDMA_AXI_R2A_CTRL2_OUTSTAND_MASK, + 0x1)); + } else if (dev->hif2->speed < PCIE_SPEED_8_0GT || + (dev->hif2->speed == PCIE_SPEED_8_0GT && + dev->hif2->width < PCIE_LNK_X2)) { + mt76_rmw(dev, WF_WFDMA0_GLO_CFG_EXT0 + hif1_ofs, + WF_WFDMA0_GLO_CFG_EXT0_OUTSTAND_MASK, + FIELD_PREP(WF_WFDMA0_GLO_CFG_EXT0_OUTSTAND_MASK, + 0x2)); + mt76_rmw(dev, MT_WFDMA_AXI_R2A_CTRL2, + MT_WFDMA_AXI_R2A_CTRL2_OUTSTAND_MASK, + FIELD_PREP(MT_WFDMA_AXI_R2A_CTRL2_OUTSTAND_MASK, + 0x2)); + } + } + + /* WFDMA rx threshold */ + mt76_wr(dev, MT_WFDMA0_PAUSE_RX_Q_45_TH + hif1_ofs, 0xc000c); + mt76_wr(dev, MT_WFDMA0_PAUSE_RX_Q_67_TH + hif1_ofs, 0x10008); + mt76_wr(dev, MT_WFDMA0_PAUSE_RX_Q_89_TH + hif1_ofs, 0x10008); + mt76_wr(dev, MT_WFDMA0_PAUSE_RX_Q_RRO_TH + hif1_ofs, 0x20); + } + + if (dev->hif2) { + /* fix hardware limitation, pcie1's rx ring3 is not available + * so, redirect pcie0 rx ring3 interrupt to pcie1 + */ + if (mtk_wed_device_active(&dev->mt76.mmio.wed) && + mt7996_has_hwrro(dev)) { + u32 intr = is_mt7996(&dev->mt76) ? + MT_WFDMA0_RX_INT_SEL_RING6 : + MT_WFDMA0_RX_INT_SEL_RING9 | + MT_WFDMA0_RX_INT_SEL_RING5; + + mt76_set(dev, MT_WFDMA0_RX_INT_PCIE_SEL + hif1_ofs, + intr); + } else { + mt76_set(dev, MT_WFDMA0_RX_INT_PCIE_SEL, + MT_WFDMA0_RX_INT_SEL_RING3); + } + } + + mt7996_dma_start(dev, reset, true); +} + +int mt7996_dma_rro_init(struct mt7996_dev *dev) +{ + struct mt76_dev *mdev = &dev->mt76; + u32 size; + int ret; + + if (dev->mt76.hwrro_mode == MT76_HWRRO_V3_1) { + /* rxdmad_c */ + mdev->q_rx[MT_RXQ_RRO_RXDMAD_C].flags = MT_WED_RRO_Q_RXDMAD_C; + if (mtk_wed_device_active(&mdev->mmio.wed)) + mdev->q_rx[MT_RXQ_RRO_RXDMAD_C].wed = &mdev->mmio.wed; + else if (!mt76_npu_device_active(&dev->mt76)) + mdev->q_rx[MT_RXQ_RRO_RXDMAD_C].flags |= MT_QFLAG_EMI_EN; + ret = mt76_queue_alloc(dev, &mdev->q_rx[MT_RXQ_RRO_RXDMAD_C], + MT_RXQ_ID(MT_RXQ_RRO_RXDMAD_C), + MT7996_RX_RING_SIZE, + MT7996_RX_BUF_SIZE, + MT_RXQ_RRO_AP_RING_BASE); + if (ret) + return ret; + + if (!mtk_wed_device_active(&mdev->mmio.wed)) { + /* We need to set cpu idx pointer before resetting the + * EMI queues. + */ + mdev->q_rx[MT_RXQ_RRO_RXDMAD_C].emi_cpu_idx = + &dev->wed_rro.emi_rings_cpu.ptr->ring[0].idx; + mt76_queue_reset(dev, &mdev->q_rx[MT_RXQ_RRO_RXDMAD_C], + true); + } + + return 0; + } + + /* ind cmd */ + mdev->q_rx[MT_RXQ_RRO_IND].flags = MT_WED_RRO_Q_IND; + if (mtk_wed_device_active(&mdev->mmio.wed) && + mtk_wed_get_rx_capa(&mdev->mmio.wed)) + mdev->q_rx[MT_RXQ_RRO_IND].wed = &mdev->mmio.wed; + ret = mt76_queue_alloc(dev, &mdev->q_rx[MT_RXQ_RRO_IND], + MT_RXQ_ID(MT_RXQ_RRO_IND), + MT7996_RX_RING_SIZE, + 0, MT_RXQ_RRO_IND_RING_BASE); + if (ret) + return ret; + + /* rx msdu page queue for band0 */ + mdev->q_rx[MT_RXQ_MSDU_PAGE_BAND0].flags = + MT_WED_RRO_Q_MSDU_PG(0) | MT_QFLAG_WED_RRO_EN; + if (mtk_wed_device_active(&mdev->mmio.wed) && + mtk_wed_get_rx_capa(&mdev->mmio.wed)) + mdev->q_rx[MT_RXQ_MSDU_PAGE_BAND0].wed = &mdev->mmio.wed; + + size = is_mt7996(mdev) && mt76_npu_device_active(mdev) + ? MT7996_NPU_RX_RING_SIZE / 4 : MT7996_RX_RING_SIZE; + ret = mt76_queue_alloc(dev, &mdev->q_rx[MT_RXQ_MSDU_PAGE_BAND0], + MT_RXQ_ID(MT_RXQ_MSDU_PAGE_BAND0), + size, MT7996_RX_MSDU_PAGE_SIZE, + MT_RXQ_RING_BASE(MT_RXQ_MSDU_PAGE_BAND0)); + if (ret) + return ret; + + if (mt7996_band_valid(dev, MT_BAND1) && is_mt7996(&dev->mt76)) { + /* rx msdu page queue for band1 */ + mdev->q_rx[MT_RXQ_MSDU_PAGE_BAND1].flags = + MT_WED_RRO_Q_MSDU_PG(1) | MT_QFLAG_WED_RRO_EN; + if (mtk_wed_device_active(&mdev->mmio.wed) && + mtk_wed_get_rx_capa(&mdev->mmio.wed)) + mdev->q_rx[MT_RXQ_MSDU_PAGE_BAND1].wed = &mdev->mmio.wed; + + size = is_mt7996(mdev) && mt76_npu_device_active(mdev) + ? MT7996_NPU_RX_RING_SIZE / 2 : MT7996_RX_RING_SIZE; + ret = mt76_queue_alloc(dev, &mdev->q_rx[MT_RXQ_MSDU_PAGE_BAND1], + MT_RXQ_ID(MT_RXQ_MSDU_PAGE_BAND1), + size, MT7996_RX_MSDU_PAGE_SIZE, + MT_RXQ_RING_BASE(MT_RXQ_MSDU_PAGE_BAND1)); + if (ret) + return ret; + } + + if (mt7996_band_valid(dev, MT_BAND2)) { + /* rx msdu page queue for band2 */ + mdev->q_rx[MT_RXQ_MSDU_PAGE_BAND2].flags = + MT_WED_RRO_Q_MSDU_PG(2) | MT_QFLAG_WED_RRO_EN; + if (mtk_wed_device_active(&mdev->mmio.wed) && + mtk_wed_get_rx_capa(&mdev->mmio.wed)) + mdev->q_rx[MT_RXQ_MSDU_PAGE_BAND2].wed = &mdev->mmio.wed; + + size = is_mt7996(mdev) && mt76_npu_device_active(mdev) + ? MT7996_NPU_RX_RING_SIZE : MT7996_RX_RING_SIZE; + ret = mt76_queue_alloc(dev, &mdev->q_rx[MT_RXQ_MSDU_PAGE_BAND2], + MT_RXQ_ID(MT_RXQ_MSDU_PAGE_BAND2), + size, MT7996_RX_MSDU_PAGE_SIZE, + MT_RXQ_RING_BASE(MT_RXQ_MSDU_PAGE_BAND2)); + if (ret) + return ret; + } + + return 0; +} + +void mt7996_dma_rro_start(struct mt7996_dev *dev) +{ + u32 irq_mask; + + if (mtk_wed_device_active(&dev->mt76.mmio.wed)) { + irq_mask = dev->mt76.mmio.irqmask | + MT_INT_TX_DONE_BAND2; + + mt76_wr(dev, MT_INT_MASK_CSR, irq_mask); + mtk_wed_device_start_hw_rro(&dev->mt76.mmio.wed, irq_mask, + false); + mt7996_irq_enable(dev, irq_mask); + return; + } + + if (is_mt7996(&dev->mt76)) { + mt76_queue_rx_init(dev, MT_RXQ_MSDU_PAGE_BAND1, + mt76_dma_rx_poll); + mt76_queue_rx_init(dev, MT_RXQ_MSDU_PAGE_BAND2, + mt76_dma_rx_poll); + mt76_queue_rx_init(dev, MT_RXQ_RRO_BAND2, + mt76_dma_rx_poll); + } else { + mt76_queue_rx_init(dev, MT_RXQ_RRO_BAND1, + mt76_dma_rx_poll); + } + + mt76_queue_rx_init(dev, MT_RXQ_RRO_BAND0, mt76_dma_rx_poll); + if (dev->mt76.hwrro_mode == MT76_HWRRO_V3_1) { + mt76_queue_rx_init(dev, MT_RXQ_RRO_RXDMAD_C, + mt76_dma_rx_poll); + } else { + mt76_queue_rx_init(dev, MT_RXQ_RRO_IND, + mt76_dma_rx_poll); + mt76_queue_rx_init(dev, MT_RXQ_MSDU_PAGE_BAND0, + mt76_dma_rx_poll); + } + + if (!mt76_npu_device_active(&dev->mt76)) + mt7996_irq_enable(dev, MT_INT_RRO_RX_DONE); +} + +int mt7996_dma_init(struct mt7996_dev *dev) +{ + struct mtk_wed_device *wed = &dev->mt76.mmio.wed; + struct mtk_wed_device *wed_hif2 = &dev->mt76.mmio.wed_hif2; + u32 rx_base; + u32 hif1_ofs = 0; + int ret; + + mt7996_dma_config(dev); + + mt76_dma_attach(&dev->mt76); + + if (dev->hif2) + hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0); + + mt7996_dma_disable(dev, true); + + /* init tx queue */ + if (is_mt7996(&dev->mt76) && mt76_npu_device_active(&dev->mt76)) + ret = mt7996_init_tx_queues(&dev->phy, MT_TXQ_ID(0), + MT7996_NPU_TX_RING_SIZE, + MT_TXQ_RING_BASE(0) + hif1_ofs, + wed); + else + ret = mt7996_init_tx_queues(&dev->phy, + MT_TXQ_ID(dev->mphy.band_idx), + MT7996_TX_RING_SIZE, + MT_TXQ_RING_BASE(0), wed); + if (ret) + return ret; + + /* command to WM */ + ret = mt76_init_mcu_queue(&dev->mt76, MT_MCUQ_WM, + MT_MCUQ_ID(MT_MCUQ_WM), + MT7996_TX_MCU_RING_SIZE, + MT_MCUQ_RING_BASE(MT_MCUQ_WM)); + if (ret) + return ret; + + /* command to WA */ + if (mt7996_has_wa(dev)) { + ret = mt76_init_mcu_queue(&dev->mt76, MT_MCUQ_WA, + MT_MCUQ_ID(MT_MCUQ_WA), + MT7996_TX_MCU_RING_SIZE, + MT_MCUQ_RING_BASE(MT_MCUQ_WA)); + if (ret) + return ret; + } + + /* firmware download */ + ret = mt76_init_mcu_queue(&dev->mt76, MT_MCUQ_FWDL, + MT_MCUQ_ID(MT_MCUQ_FWDL), + MT7996_TX_FWDL_RING_SIZE, + MT_MCUQ_RING_BASE(MT_MCUQ_FWDL)); + if (ret) + return ret; + + /* event from WM */ + ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MCU], + MT_RXQ_ID(MT_RXQ_MCU), + MT7996_RX_MCU_RING_SIZE, + MT7996_RX_MCU_BUF_SIZE, + MT_RXQ_RING_BASE(MT_RXQ_MCU)); + if (ret) + return ret; + + /* event from WA, or SDO event for mt7990 */ + ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MCU_WA], + MT_RXQ_ID(MT_RXQ_MCU_WA), + MT7996_RX_MCU_RING_SIZE_WA, + MT7996_RX_MCU_BUF_SIZE, + MT_RXQ_RING_BASE(MT_RXQ_MCU_WA)); + if (ret) + return ret; + + /* rx data queue for band0 and mt7996 band1 */ + if (mtk_wed_device_active(wed) && mtk_wed_get_rx_capa(wed)) { + dev->mt76.q_rx[MT_RXQ_MAIN].flags = MT_WED_Q_RX(0); + dev->mt76.q_rx[MT_RXQ_MAIN].wed = wed; + } + + ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MAIN], + MT_RXQ_ID(MT_RXQ_MAIN), + MT7996_RX_RING_SIZE, + MT_RX_BUF_SIZE, + MT_RXQ_RING_BASE(MT_RXQ_MAIN)); + if (ret) + return ret; + + /* tx free notify event from WA for band0 */ + if (mtk_wed_device_active(wed) && + ((is_mt7996(&dev->mt76) && !mt7996_has_hwrro(dev)) || + (is_mt7992(&dev->mt76)))) { + dev->mt76.q_rx[MT_RXQ_MAIN_WA].flags = MT_WED_Q_TXFREE; + dev->mt76.q_rx[MT_RXQ_MAIN_WA].wed = wed; + } else if (is_mt7992(&dev->mt76) && + mt76_npu_device_active(&dev->mt76)) { + dev->mt76.q_rx[MT_RXQ_MAIN_WA].flags = MT_NPU_Q_TXFREE(0); + } + + if (mt7996_has_wa(dev)) { + ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MAIN_WA], + MT_RXQ_ID(MT_RXQ_MAIN_WA), + MT7996_RX_MCU_RING_SIZE, + MT_RX_BUF_SIZE, + MT_RXQ_RING_BASE(MT_RXQ_MAIN_WA)); + if (ret) + return ret; + } else { + if (mtk_wed_device_active(wed)) { + dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0].flags = MT_WED_Q_TXFREE; + dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0].wed = wed; + } + ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0], + MT_RXQ_ID(MT_RXQ_TXFREE_BAND0), + MT7996_RX_MCU_RING_SIZE, + MT7996_RX_BUF_SIZE, + MT_RXQ_RING_BASE(MT_RXQ_TXFREE_BAND0)); + if (ret) + return ret; + } + + if (!mt7996_has_wa(dev) && dev->hif2) { + if (mtk_wed_device_active(wed)) { + dev->mt76.q_rx[MT_RXQ_TXFREE_BAND1].flags = MT_WED_Q_TXFREE; + dev->mt76.q_rx[MT_RXQ_TXFREE_BAND1].wed = wed; + } + ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_TXFREE_BAND1], + MT_RXQ_ID(MT_RXQ_TXFREE_BAND1), + MT7996_RX_MCU_RING_SIZE, + MT7996_RX_BUF_SIZE, + MT_RXQ_RING_BASE(MT_RXQ_TXFREE_BAND1)); + if (ret) + return ret; + } + + if (mt7996_band_valid(dev, MT_BAND2)) { + /* rx data queue for mt7996 band2 */ + rx_base = MT_RXQ_RING_BASE(MT_RXQ_BAND2) + hif1_ofs; + ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_BAND2], + MT_RXQ_ID(MT_RXQ_BAND2), + MT7996_RX_RING_SIZE, + MT_RX_BUF_SIZE, + rx_base); + if (ret) + return ret; + + /* tx free notify event from WA for mt7996 band2 + * use pcie0's rx ring3, but, redirect pcie0 rx ring3 interrupt to pcie1 + */ + if (mtk_wed_device_active(wed_hif2) && !mt7996_has_hwrro(dev)) { + dev->mt76.q_rx[MT_RXQ_BAND2_WA].flags = MT_WED_Q_TXFREE; + dev->mt76.q_rx[MT_RXQ_BAND2_WA].wed = wed_hif2; + } + + ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_BAND2_WA], + MT_RXQ_ID(MT_RXQ_BAND2_WA), + MT7996_RX_MCU_RING_SIZE, + MT_RX_BUF_SIZE, + MT_RXQ_RING_BASE(MT_RXQ_BAND2_WA)); + if (ret) + return ret; + } else if (mt7996_band_valid(dev, MT_BAND1)) { + /* rx data queue for mt7992 band1 */ + rx_base = MT_RXQ_RING_BASE(MT_RXQ_BAND1) + hif1_ofs; + if (mtk_wed_device_active(wed) && mtk_wed_get_rx_capa(wed)) { + dev->mt76.q_rx[MT_RXQ_BAND1].flags = MT_WED_Q_RX(1); + dev->mt76.q_rx[MT_RXQ_BAND1].wed = wed; + } + + ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_BAND1], + MT_RXQ_ID(MT_RXQ_BAND1), + MT7996_RX_RING_SIZE, + MT_RX_BUF_SIZE, + rx_base); + if (ret) + return ret; + + /* tx free notify event from WA for mt7992 band1 */ + if (mt7996_has_wa(dev)) { + rx_base = MT_RXQ_RING_BASE(MT_RXQ_BAND1_WA) + hif1_ofs; + if (mtk_wed_device_active(wed_hif2)) { + dev->mt76.q_rx[MT_RXQ_BAND1_WA].flags = + MT_WED_Q_TXFREE; + dev->mt76.q_rx[MT_RXQ_BAND1_WA].wed = wed_hif2; + } + + ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_BAND1_WA], + MT_RXQ_ID(MT_RXQ_BAND1_WA), + MT7996_RX_MCU_RING_SIZE, + MT_RX_BUF_SIZE, + rx_base); + if (ret) + return ret; + } + } + + if (mt7996_has_hwrro(dev)) { + /* rx rro data queue for band0 */ + dev->mt76.q_rx[MT_RXQ_RRO_BAND0].flags = + MT_WED_RRO_Q_DATA(0) | MT_QFLAG_WED_RRO_EN; + if (mtk_wed_device_active(wed) && mtk_wed_get_rx_capa(wed)) + dev->mt76.q_rx[MT_RXQ_RRO_BAND0].wed = wed; + ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_RRO_BAND0], + MT_RXQ_ID(MT_RXQ_RRO_BAND0), + MT7996_RX_RING_SIZE, + MT7996_RX_BUF_SIZE, + MT_RXQ_RING_BASE(MT_RXQ_RRO_BAND0)); + if (ret) + return ret; + + if (is_mt7992(&dev->mt76)) { + dev->mt76.q_rx[MT_RXQ_RRO_BAND1].flags = + MT_WED_RRO_Q_DATA(1) | MT_QFLAG_WED_RRO_EN; + if (mtk_wed_device_active(wed) && + mtk_wed_get_rx_capa(wed)) + dev->mt76.q_rx[MT_RXQ_RRO_BAND1].wed = wed; + ret = mt76_queue_alloc(dev, + &dev->mt76.q_rx[MT_RXQ_RRO_BAND1], + MT_RXQ_ID(MT_RXQ_RRO_BAND1), + MT7996_RX_RING_SIZE, + MT7996_RX_BUF_SIZE, + MT_RXQ_RING_BASE(MT_RXQ_RRO_BAND1) + hif1_ofs); + if (ret) + return ret; + } else { + if (mtk_wed_device_active(wed)) { + /* tx free notify event from WA for band0 */ + dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0].flags = MT_WED_Q_TXFREE; + dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0].wed = wed; + } else if (mt76_npu_device_active(&dev->mt76)) { + dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0].flags = MT_NPU_Q_TXFREE(0); + } + + ret = mt76_queue_alloc(dev, + &dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0], + MT_RXQ_ID(MT_RXQ_TXFREE_BAND0), + MT7996_RX_MCU_RING_SIZE, + MT7996_RX_BUF_SIZE, + MT_RXQ_RING_BASE(MT_RXQ_TXFREE_BAND0)); + if (ret) + return ret; + } + + if (mt7996_band_valid(dev, MT_BAND2)) { + u32 size; + + /* rx rro data queue for band2 */ + dev->mt76.q_rx[MT_RXQ_RRO_BAND2].flags = + MT_WED_RRO_Q_DATA(1) | MT_QFLAG_WED_RRO_EN; + if (mtk_wed_device_active(wed) && + mtk_wed_get_rx_capa(wed)) + dev->mt76.q_rx[MT_RXQ_RRO_BAND2].wed = wed; + + size = is_mt7996(&dev->mt76) && + mt76_npu_device_active(&dev->mt76) + ? MT7996_NPU_RX_RING_SIZE : MT7996_RX_RING_SIZE; + ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_RRO_BAND2], + MT_RXQ_ID(MT_RXQ_RRO_BAND2), + size, MT7996_RX_BUF_SIZE, + MT_RXQ_RING_BASE(MT_RXQ_RRO_BAND2) + hif1_ofs); + if (ret) + return ret; + + /* tx free notify event from MAC for band2 */ + if (mtk_wed_device_active(wed_hif2)) { + dev->mt76.q_rx[MT_RXQ_TXFREE_BAND2].flags = MT_WED_Q_TXFREE; + dev->mt76.q_rx[MT_RXQ_TXFREE_BAND2].wed = wed_hif2; + } + ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_TXFREE_BAND2], + MT_RXQ_ID(MT_RXQ_TXFREE_BAND2), + MT7996_RX_MCU_RING_SIZE, + MT7996_RX_BUF_SIZE, + MT_RXQ_RING_BASE(MT_RXQ_TXFREE_BAND2) + hif1_ofs); + if (ret) + return ret; + } + } + + ret = mt76_init_queues(dev, mt76_dma_rx_poll); + if (ret < 0) + return ret; + + ret = mt7996_npu_rx_queues_init(dev); + if (ret) + return ret; + + netif_napi_add_tx(dev->mt76.tx_napi_dev, &dev->mt76.tx_napi, + mt7996_poll_tx); + napi_enable(&dev->mt76.tx_napi); + + mt7996_dma_enable(dev, false); + + return 0; +} + +void mt7996_dma_reset(struct mt7996_dev *dev, bool force) +{ + struct mt76_phy *phy2 = dev->mt76.phys[MT_BAND1]; + struct mt76_phy *phy3 = dev->mt76.phys[MT_BAND2]; + u32 hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0); + int i; + + mt76_clear(dev, MT_WFDMA0_GLO_CFG, + MT_WFDMA0_GLO_CFG_TX_DMA_EN | + MT_WFDMA0_GLO_CFG_RX_DMA_EN); + + if (dev->hif2) + mt76_clear(dev, MT_WFDMA0_GLO_CFG + hif1_ofs, + MT_WFDMA0_GLO_CFG_TX_DMA_EN | + MT_WFDMA0_GLO_CFG_RX_DMA_EN); + + usleep_range(1000, 2000); + + for (i = 0; i < __MT_TXQ_MAX; i++) { + mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[i], true); + if (phy2) + mt76_queue_tx_cleanup(dev, phy2->q_tx[i], true); + if (phy3) + mt76_queue_tx_cleanup(dev, phy3->q_tx[i], true); + } + + for (i = 0; i < __MT_MCUQ_MAX; i++) + mt76_queue_tx_cleanup(dev, dev->mt76.q_mcu[i], true); + + mt76_for_each_q_rx(&dev->mt76, i) + mt76_queue_rx_cleanup(dev, &dev->mt76.q_rx[i]); + + mt76_tx_status_check(&dev->mt76, true); + + if (mt7996_has_hwrro(dev) && + !mtk_wed_device_active(&dev->mt76.mmio.wed)) + mt7996_rro_msdu_page_map_free(dev); + + /* reset wfsys */ + if (force) + mt7996_wfsys_reset(dev); + + if (dev->hif2 && mtk_wed_device_active(&dev->mt76.mmio.wed_hif2)) + mtk_wed_device_dma_reset(&dev->mt76.mmio.wed_hif2); + + if (mtk_wed_device_active(&dev->mt76.mmio.wed)) + mtk_wed_device_dma_reset(&dev->mt76.mmio.wed); + + mt76_npu_disable_irqs(&dev->mt76); + mt7996_dma_disable(dev, force); + mt76_wed_dma_reset(&dev->mt76); + + /* reset hw queues */ + for (i = 0; i < __MT_TXQ_MAX; i++) { + mt76_dma_reset_tx_queue(&dev->mt76, dev->mphy.q_tx[i]); + if (phy2) + mt76_dma_reset_tx_queue(&dev->mt76, phy2->q_tx[i]); + if (phy3) + mt76_dma_reset_tx_queue(&dev->mt76, phy3->q_tx[i]); + } + + for (i = 0; i < __MT_MCUQ_MAX; i++) + mt76_queue_reset(dev, dev->mt76.q_mcu[i], true); + + mt76_for_each_q_rx(&dev->mt76, i) { + struct mt76_queue *q = &dev->mt76.q_rx[i]; + + if (mtk_wed_device_active(&dev->mt76.mmio.wed)) { + if (mt76_queue_is_wed_rro(q) || + mt76_queue_is_wed_tx_free(q)) { + if (force && mt76_queue_is_wed_rro_data(q)) + mt76_queue_reset(dev, q, false); + continue; + } + } + mt76_queue_reset(dev, q, true); + } + + mt76_tx_status_check(&dev->mt76, true); + + mt76_for_each_q_rx(&dev->mt76, i) { + if (mtk_wed_device_active(&dev->mt76.mmio.wed) && force && + (mt76_queue_is_wed_rro_ind(&dev->mt76.q_rx[i]) || + mt76_queue_is_wed_rro_msdu_pg(&dev->mt76.q_rx[i]))) + continue; + + mt76_queue_rx_reset(dev, i); + } + + mt7996_dma_enable(dev, !force); +} + +void mt7996_dma_cleanup(struct mt7996_dev *dev) +{ + mt7996_dma_disable(dev, true); + + mt76_dma_cleanup(&dev->mt76); +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c new file mode 100644 index 000000000..ec33521db --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c @@ -0,0 +1,421 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (C) 2022 MediaTek Inc. + */ + +#include +#include "mt7996.h" +#include "eeprom.h" + +static int mt7996_check_eeprom(struct mt7996_dev *dev) +{ + u8 *eeprom = dev->mt76.eeprom.data; + u16 val = get_unaligned_le16(eeprom); + + switch (val) { + case MT7996_DEVICE_ID: + return is_mt7996(&dev->mt76) ? 0 : -EINVAL; + case MT7992_DEVICE_ID: + return is_mt7992(&dev->mt76) ? 0 : -EINVAL; + case MT7990_DEVICE_ID: + return is_mt7990(&dev->mt76) ? 0 : -EINVAL; + default: + return -EINVAL; + } +} + +static char *mt7996_eeprom_name(struct mt7996_dev *dev) +{ + switch (mt76_chip(&dev->mt76)) { + case MT7992_DEVICE_ID: + switch (dev->var.type) { + case MT7992_VAR_TYPE_23: + if (dev->var.fem == MT7996_FEM_INT) + return MT7992_EEPROM_DEFAULT_23_INT; + return MT7992_EEPROM_DEFAULT_23; + case MT7992_VAR_TYPE_24: + return MT7992_EEPROM_DEFAULT_24; + case MT7992_VAR_TYPE_44: + default: + if (dev->var.fem == MT7996_FEM_INT) + return MT7992_EEPROM_DEFAULT_INT; + if (dev->var.fem == MT7996_FEM_MIX) + return MT7992_EEPROM_DEFAULT_MIX; + return MT7992_EEPROM_DEFAULT; + } + case MT7990_DEVICE_ID: + if (dev->var.fem == MT7996_FEM_INT) + return MT7990_EEPROM_DEFAULT_INT; + return MT7990_EEPROM_DEFAULT; + case MT7996_DEVICE_ID: + default: + switch (dev->var.type) { + case MT7996_VAR_TYPE_233: + if (dev->var.fem == MT7996_FEM_INT) + return MT7996_EEPROM_DEFAULT_233_INT; + return MT7996_EEPROM_DEFAULT_233; + case MT7996_VAR_TYPE_444: + default: + if (dev->var.fem == MT7996_FEM_INT) + return MT7996_EEPROM_DEFAULT_INT; + return MT7996_EEPROM_DEFAULT; + } + } +} + +static void +mt7996_eeprom_parse_stream(const u8 *eeprom, u8 band_idx, u8 *path, + u8 *rx_path, u8 *nss) +{ + switch (band_idx) { + case MT_BAND1: + *path = FIELD_GET(MT_EE_WIFI_CONF2_TX_PATH_BAND1, + eeprom[MT_EE_WIFI_CONF + 2]); + *rx_path = FIELD_GET(MT_EE_WIFI_CONF3_RX_PATH_BAND1, + eeprom[MT_EE_WIFI_CONF + 3]); + *nss = FIELD_GET(MT_EE_WIFI_CONF5_STREAM_NUM_BAND1, + eeprom[MT_EE_WIFI_CONF + 5]); + break; + case MT_BAND2: + *path = FIELD_GET(MT_EE_WIFI_CONF2_TX_PATH_BAND2, + eeprom[MT_EE_WIFI_CONF + 2]); + *rx_path = FIELD_GET(MT_EE_WIFI_CONF4_RX_PATH_BAND2, + eeprom[MT_EE_WIFI_CONF + 4]); + *nss = FIELD_GET(MT_EE_WIFI_CONF5_STREAM_NUM_BAND2, + eeprom[MT_EE_WIFI_CONF + 5]); + break; + default: + *path = FIELD_GET(MT_EE_WIFI_CONF1_TX_PATH_BAND0, + eeprom[MT_EE_WIFI_CONF + 1]); + *rx_path = FIELD_GET(MT_EE_WIFI_CONF3_RX_PATH_BAND0, + eeprom[MT_EE_WIFI_CONF + 3]); + *nss = FIELD_GET(MT_EE_WIFI_CONF4_STREAM_NUM_BAND0, + eeprom[MT_EE_WIFI_CONF + 4]); + break; + } +} + +static bool mt7996_eeprom_variant_valid(struct mt7996_dev *dev, const u8 *def) +{ +#define FEM_INT 0 +#define FEM_EXT 3 + u8 *eeprom = dev->mt76.eeprom.data, fem[2]; + int i; + + for (i = 0; i < 2; i++) + fem[i] = u8_get_bits(eeprom[MT_EE_WIFI_CONF + 6 + i], + MT_EE_WIFI_PA_LNA_CONFIG); + + if (dev->var.fem == MT7996_FEM_EXT && + !(fem[0] == FEM_EXT && fem[1] == FEM_EXT)) + return false; + else if (dev->var.fem == MT7996_FEM_INT && + !(fem[0] == FEM_INT && fem[1] == FEM_INT)) + return false; + else if (dev->var.fem == MT7996_FEM_MIX && + !(fem[0] == FEM_INT && fem[1] == FEM_EXT)) + return false; + + for (i = 0; i < __MT_MAX_BAND; i++) { + u8 path, rx_path, nss; + u8 def_path, def_rx_path, def_nss; + + if (!dev->mt76.phys[i]) + continue; + + mt7996_eeprom_parse_stream(eeprom, i, &path, &rx_path, &nss); + mt7996_eeprom_parse_stream(def, i, &def_path, &def_rx_path, + &def_nss); + if (path > def_path || rx_path > def_rx_path || nss > def_nss) + return false; + } + + return true; +} + +static int +mt7996_eeprom_check_or_use_default(struct mt7996_dev *dev, bool use_default) +{ + u8 *eeprom = dev->mt76.eeprom.data; + const struct firmware *fw = NULL; + int ret; + + ret = request_firmware(&fw, mt7996_eeprom_name(dev), dev->mt76.dev); + if (ret) + return ret; + + if (!fw || !fw->data) { + dev_err(dev->mt76.dev, "Invalid default bin\n"); + ret = -EINVAL; + goto out; + } + + if (fw->size < MT7996_EEPROM_SIZE) { + dev_err(dev->mt76.dev, "Invalid default bin size\n"); + ret = -EINVAL; + goto out; + } + + if (!use_default && mt7996_eeprom_variant_valid(dev, fw->data)) + goto out; + + dev_warn(dev->mt76.dev, "eeprom load fail, use default bin\n"); + memcpy(eeprom, fw->data, MT7996_EEPROM_SIZE); + dev->eeprom_mode = EEPROM_MODE_DEFAULT_BIN; + +out: + release_firmware(fw); + + return ret; +} + +static int mt7996_eeprom_load(struct mt7996_dev *dev) +{ + u32 eeprom_blk_size, block_num; + bool use_default = false; + int ret, i; + + ret = mt76_eeprom_init(&dev->mt76, MT7996_EEPROM_SIZE); + if (ret < 0) + return ret; + + if (ret && !mt7996_check_eeprom(dev)) { + dev->eeprom_mode = EEPROM_MODE_FLASH; + goto out; + } + + memset(dev->mt76.eeprom.data, 0, MT7996_EEPROM_SIZE); + if (mt7996_has_ext_eeprom(dev)) { + /* external eeprom mode */ + dev->eeprom_mode = EEPROM_MODE_EXT; + eeprom_blk_size = MT7996_EXT_EEPROM_BLOCK_SIZE; + } else { + u8 free_block_num; + + /* efuse mode */ + dev->eeprom_mode = EEPROM_MODE_EFUSE; + eeprom_blk_size = MT7996_EEPROM_BLOCK_SIZE; + ret = mt7996_mcu_get_efuse_free_block(dev, &free_block_num); + if (ret < 0) + return ret; + + /* efuse info isn't enough */ + if (free_block_num >= 59) { + use_default = true; + goto out; + } + } + + /* check if eeprom data from fw is valid */ + if (mt7996_mcu_get_eeprom(dev, 0, NULL, eeprom_blk_size, + dev->eeprom_mode) || + mt7996_check_eeprom(dev)) { + use_default = true; + goto out; + } + + /* read eeprom data from fw */ + block_num = DIV_ROUND_UP(MT7996_EEPROM_SIZE, eeprom_blk_size); + for (i = 1; i < block_num; i++) { + u32 len = eeprom_blk_size; + + if (i == block_num - 1) + len = MT7996_EEPROM_SIZE % eeprom_blk_size; + ret = mt7996_mcu_get_eeprom(dev, i * eeprom_blk_size, + NULL, len, dev->eeprom_mode); + if (ret && ret != -EINVAL) { + use_default = true; + goto out; + } + } + +out: + return mt7996_eeprom_check_or_use_default(dev, use_default); +} + +static int mt7996_eeprom_parse_efuse_hw_cap(struct mt7996_phy *phy, + u8 *path, u8 *rx_path, u8 *nss) +{ +#define MODE_HE_ONLY BIT(0) +#define WTBL_SIZE_GROUP GENMASK(31, 28) +#define STREAM_CAP(_offs) ((cap & (0x7 << (_offs))) >> (_offs)) + struct mt7996_dev *dev = phy->dev; + u32 cap = 0; + int ret; + + ret = mt7996_mcu_get_chip_config(dev, &cap); + if (ret) + return ret; + + if (cap) { + u8 band_offs = phy->mt76->band_idx * 3; + + dev->has_eht = !(cap & MODE_HE_ONLY); + dev->wtbl_size_group = u32_get_bits(cap, WTBL_SIZE_GROUP); + *nss = min_t(u8, *nss, STREAM_CAP(1 + band_offs)); + *path = min_t(u8, *path, STREAM_CAP(10 + band_offs)); + *rx_path = min_t(u8, *rx_path, STREAM_CAP(19 + band_offs)); + } + + if (dev->wtbl_size_group < 2 || dev->wtbl_size_group > 4) + dev->wtbl_size_group = is_mt7996(&dev->mt76) ? 4 : 2; + + return 0; +} + +static int mt7996_eeprom_parse_band_config(struct mt7996_phy *phy) +{ + u8 *eeprom = phy->dev->mt76.eeprom.data; + u32 val = eeprom[MT_EE_WIFI_CONF]; + int ret = 0; + + switch (phy->mt76->band_idx) { + case MT_BAND1: + val = FIELD_GET(MT_EE_WIFI_CONF1_BAND_SEL, val); + break; + case MT_BAND2: + val = eeprom[MT_EE_WIFI_CONF + 1]; + val = FIELD_GET(MT_EE_WIFI_CONF2_BAND_SEL, val); + break; + default: + val = FIELD_GET(MT_EE_WIFI_CONF0_BAND_SEL, val); + break; + } + + switch (val) { + case MT_EE_BAND_SEL_2GHZ: + phy->mt76->cap.has_2ghz = true; + break; + case MT_EE_BAND_SEL_5GHZ: + phy->mt76->cap.has_5ghz = true; + break; + case MT_EE_BAND_SEL_6GHZ: + phy->mt76->cap.has_6ghz = true; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +int mt7996_eeprom_parse_hw_cap(struct mt7996_dev *dev, struct mt7996_phy *phy) +{ + u8 path, rx_path, nss, band_idx = phy->mt76->band_idx; + u8 *eeprom = dev->mt76.eeprom.data; + struct mt76_phy *mphy = phy->mt76; + int max_path = 5, max_nss = 4; + int ret; + + mt7996_eeprom_parse_stream(eeprom, band_idx, &path, &rx_path, &nss); + ret = mt7996_eeprom_parse_efuse_hw_cap(phy, &path, &rx_path, &nss); + if (ret) + return ret; + + if (!path || path > max_path) + path = max_path; + + if (!nss || nss > max_nss) + nss = max_nss; + + nss = min_t(u8, nss, path); + + if (path != rx_path) + phy->has_aux_rx = true; + + mphy->antenna_mask = BIT(nss) - 1; + phy->orig_antenna_mask = mphy->antenna_mask; + mphy->chainmask = (BIT(path) - 1) << dev->chainshift[band_idx]; + phy->orig_chainmask = mphy->chainmask; + dev->chainmask |= mphy->chainmask; + if (band_idx < MT_BAND2) + dev->chainshift[band_idx + 1] = dev->chainshift[band_idx] + + hweight16(mphy->chainmask); + + return mt7996_eeprom_parse_band_config(phy); +} + +int mt7996_eeprom_init(struct mt7996_dev *dev) +{ + int ret; + + ret = mt7996_eeprom_load(dev); + if (ret < 0) + return ret; + + ret = mt7996_eeprom_parse_hw_cap(dev, &dev->phy); + if (ret < 0) + return ret; + + memcpy(dev->mphy.macaddr, dev->mt76.eeprom.data + MT_EE_MAC_ADDR, ETH_ALEN); + + return mt76_eeprom_override(&dev->mphy); +} + +int mt7996_eeprom_get_target_power(struct mt7996_dev *dev, + struct ieee80211_channel *chan) +{ + u8 *eeprom = dev->mt76.eeprom.data; + int target_power; + + if (chan->band == NL80211_BAND_5GHZ) + target_power = eeprom[MT_EE_TX0_POWER_5G + + mt7996_get_channel_group_5g(chan->hw_value)]; + else if (chan->band == NL80211_BAND_6GHZ) + target_power = eeprom[MT_EE_TX0_POWER_6G + + mt7996_get_channel_group_6g(chan->hw_value)]; + else + target_power = eeprom[MT_EE_TX0_POWER_2G]; + + return target_power; +} + +s8 mt7996_eeprom_get_power_delta(struct mt7996_dev *dev, int band) +{ + u8 *eeprom = dev->mt76.eeprom.data; + u32 val; + s8 delta; + + if (band == NL80211_BAND_5GHZ) + val = eeprom[MT_EE_RATE_DELTA_5G]; + else if (band == NL80211_BAND_6GHZ) + val = eeprom[MT_EE_RATE_DELTA_6G]; + else + val = eeprom[MT_EE_RATE_DELTA_2G]; + + if (!(val & MT_EE_RATE_DELTA_EN)) + return 0; + + delta = FIELD_GET(MT_EE_RATE_DELTA_MASK, val); + + return val & MT_EE_RATE_DELTA_SIGN ? delta : -delta; +} + +bool mt7996_eeprom_has_background_radar(struct mt7996_dev *dev) +{ + switch (mt76_chip(&dev->mt76)) { + case MT7996_DEVICE_ID: + if (dev->var.type == MT7996_VAR_TYPE_233) + return false; + break; + case MT7992_DEVICE_ID: + if (dev->var.type == MT7992_VAR_TYPE_23 || + dev->var.type == MT7992_VAR_TYPE_24) + return false; + break; + case MT7990_DEVICE_ID: { + u8 path, rx_path, nss, *eeprom = dev->mt76.eeprom.data; + + mt7996_eeprom_parse_stream(eeprom, MT_BAND1, &path, &rx_path, &nss); + /* Disable background radar capability in 3T3R */ + if (path == 3 || rx_path == 3) + return false; + break; + } + default: + return false; + } + + return true; +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.h b/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.h new file mode 100644 index 000000000..9e6f0e04c --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.h @@ -0,0 +1,79 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (C) 2022 MediaTek Inc. + */ + +#ifndef __MT7996_EEPROM_H +#define __MT7996_EEPROM_H + +#include "mt7996.h" + +enum mt7996_eeprom_field { + MT_EE_CHIP_ID = 0x000, + MT_EE_VERSION = 0x002, + MT_EE_MAC_ADDR = 0x004, + MT_EE_MAC_ADDR2 = 0x00a, + MT_EE_WIFI_CONF = 0x190, + MT_EE_MAC_ADDR3 = 0x2c0, + MT_EE_RATE_DELTA_2G = 0x1400, + MT_EE_RATE_DELTA_5G = 0x147d, + MT_EE_RATE_DELTA_6G = 0x154a, + MT_EE_TX0_POWER_2G = 0x1300, + MT_EE_TX0_POWER_5G = 0x1301, + MT_EE_TX0_POWER_6G = 0x1310, + + __MT_EE_MAX = 0x1dff, +}; + +#define MT_EE_WIFI_CONF0_TX_PATH GENMASK(2, 0) +#define MT_EE_WIFI_CONF0_BAND_SEL GENMASK(2, 0) +#define MT_EE_WIFI_CONF1_BAND_SEL GENMASK(5, 3) +#define MT_EE_WIFI_CONF2_BAND_SEL GENMASK(2, 0) + +#define MT_EE_WIFI_CONF1_TX_PATH_BAND0 GENMASK(5, 3) +#define MT_EE_WIFI_CONF2_TX_PATH_BAND1 GENMASK(2, 0) +#define MT_EE_WIFI_CONF2_TX_PATH_BAND2 GENMASK(5, 3) +#define MT_EE_WIFI_CONF3_RX_PATH_BAND0 GENMASK(2, 0) +#define MT_EE_WIFI_CONF3_RX_PATH_BAND1 GENMASK(5, 3) +#define MT_EE_WIFI_CONF4_RX_PATH_BAND2 GENMASK(2, 0) +#define MT_EE_WIFI_CONF4_STREAM_NUM_BAND0 GENMASK(5, 3) +#define MT_EE_WIFI_CONF5_STREAM_NUM_BAND1 GENMASK(2, 0) +#define MT_EE_WIFI_CONF5_STREAM_NUM_BAND2 GENMASK(5, 3) + +#define MT_EE_WIFI_PA_LNA_CONFIG GENMASK(1, 0) + +#define MT_EE_RATE_DELTA_MASK GENMASK(5, 0) +#define MT_EE_RATE_DELTA_SIGN BIT(6) +#define MT_EE_RATE_DELTA_EN BIT(7) + +enum mt7996_eeprom_band { + MT_EE_BAND_SEL_DEFAULT, + MT_EE_BAND_SEL_2GHZ, + MT_EE_BAND_SEL_5GHZ, + MT_EE_BAND_SEL_6GHZ, +}; + +static inline int +mt7996_get_channel_group_5g(int channel) +{ + if (channel <= 64) + return 0; + if (channel <= 96) + return 1; + if (channel <= 128) + return 2; + if (channel <= 144) + return 3; + return 4; +} + +static inline int +mt7996_get_channel_group_6g(int channel) +{ + if (channel <= 29) + return 0; + + return DIV_ROUND_UP(channel - 29, 32); +} + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/init.c b/drivers/net/wireless/mediatek/mt76/mt7996/init.c new file mode 100644 index 000000000..fb635a092 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7996/init.c @@ -0,0 +1,1831 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (C) 2022 MediaTek Inc. + */ + +#include +#include +#include +#include +#include +#include "mt7996.h" +#include "mac.h" +#include "mcu.h" +#include "coredump.h" +#include "eeprom.h" + +static const struct ieee80211_iface_limit if_limits_global = { + .max = MT7996_MAX_INTERFACES * MT7996_MAX_RADIOS, + .types = BIT(NL80211_IFTYPE_STATION) + | BIT(NL80211_IFTYPE_ADHOC) + | BIT(NL80211_IFTYPE_AP) +#ifdef CONFIG_MAC80211_MESH + | BIT(NL80211_IFTYPE_MESH_POINT) +#endif +}; + +static const struct ieee80211_iface_combination if_comb_global = { + .limits = &if_limits_global, + .n_limits = 1, + .max_interfaces = MT7996_MAX_INTERFACES * MT7996_MAX_RADIOS, + .num_different_channels = MT7996_MAX_RADIOS, + .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) | + BIT(NL80211_CHAN_WIDTH_20) | + BIT(NL80211_CHAN_WIDTH_40) | + BIT(NL80211_CHAN_WIDTH_80) | + BIT(NL80211_CHAN_WIDTH_160), +}; + +static const struct ieee80211_iface_combination if_comb_global_7992 = { + .limits = &if_limits_global, + .n_limits = 1, + .max_interfaces = 32, + .num_different_channels = MT7996_MAX_RADIOS - 1, + .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) | + BIT(NL80211_CHAN_WIDTH_20) | + BIT(NL80211_CHAN_WIDTH_40) | + BIT(NL80211_CHAN_WIDTH_80) | + BIT(NL80211_CHAN_WIDTH_160), +}; + +static const struct ieee80211_iface_limit if_limits[] = { + { + .max = 16, + .types = BIT(NL80211_IFTYPE_AP) +#ifdef CONFIG_MAC80211_MESH + | BIT(NL80211_IFTYPE_MESH_POINT) +#endif + }, { + .max = MT7996_MAX_INTERFACES, + .types = BIT(NL80211_IFTYPE_STATION) + } +}; + +static const struct ieee80211_iface_combination if_comb = { + .limits = if_limits, + .n_limits = ARRAY_SIZE(if_limits), + .max_interfaces = MT7996_MAX_INTERFACES, + .num_different_channels = 1, + .beacon_int_infra_match = true, + .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) | + BIT(NL80211_CHAN_WIDTH_20) | + BIT(NL80211_CHAN_WIDTH_40) | + BIT(NL80211_CHAN_WIDTH_80) | + BIT(NL80211_CHAN_WIDTH_160), + .beacon_int_min_gcd = 100, +}; + +static const u8 if_types_ext_capa_ap[] = { + [0] = WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING, + [2] = WLAN_EXT_CAPA3_MULTI_BSSID_SUPPORT, + [7] = WLAN_EXT_CAPA8_OPMODE_NOTIF, +}; + +static const struct wiphy_iftype_ext_capab iftypes_ext_capa[] = { + { + .iftype = NL80211_IFTYPE_AP, + .extended_capabilities = if_types_ext_capa_ap, + .extended_capabilities_mask = if_types_ext_capa_ap, + .extended_capabilities_len = sizeof(if_types_ext_capa_ap), + .eml_capabilities = IEEE80211_EML_CAP_EMLSR_SUPP, + .mld_capa_and_ops = + FIELD_PREP_CONST(IEEE80211_MLD_CAP_OP_MAX_SIMUL_LINKS, + MT7996_MAX_RADIOS - 1), + }, { + .iftype = NL80211_IFTYPE_STATION, + .extended_capabilities = if_types_ext_capa_ap, + .extended_capabilities_mask = if_types_ext_capa_ap, + .extended_capabilities_len = sizeof(if_types_ext_capa_ap), + .mld_capa_and_ops = + FIELD_PREP_CONST(IEEE80211_MLD_CAP_OP_FREQ_SEP_TYPE_IND, 1) | + FIELD_PREP_CONST(IEEE80211_MLD_CAP_OP_MAX_SIMUL_LINKS, + MT7996_MAX_RADIOS - 1), + }, +}; + +static ssize_t mt7996_thermal_temp_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct mt7996_phy *phy = dev_get_drvdata(dev); + int i = to_sensor_dev_attr(attr)->index; + int temperature; + + switch (i) { + case 0: + temperature = mt7996_mcu_get_temperature(phy); + if (temperature < 0) + return temperature; + /* display in millidegree celcius */ + return sprintf(buf, "%u\n", temperature * 1000); + case 1: + case 2: + return sprintf(buf, "%u\n", + phy->throttle_temp[i - 1] * 1000); + case 3: + return sprintf(buf, "%hhu\n", phy->throttle_state); + default: + return -EINVAL; + } +} + +static ssize_t mt7996_thermal_temp_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct mt7996_phy *phy = dev_get_drvdata(dev); + int ret, i = to_sensor_dev_attr(attr)->index; + long val; + + ret = kstrtol(buf, 10, &val); + if (ret < 0) + return ret; + + mutex_lock(&phy->dev->mt76.mutex); + val = DIV_ROUND_CLOSEST(clamp_val(val, 40 * 1000, 130 * 1000), 1000); + + /* add a safety margin ~10 */ + if ((i - 1 == MT7996_CRIT_TEMP_IDX && + val > phy->throttle_temp[MT7996_MAX_TEMP_IDX] - 10) || + (i - 1 == MT7996_MAX_TEMP_IDX && + val - 10 < phy->throttle_temp[MT7996_CRIT_TEMP_IDX])) { + dev_err(phy->dev->mt76.dev, + "temp1_max shall be 10 degrees higher than temp1_crit."); + mutex_unlock(&phy->dev->mt76.mutex); + return -EINVAL; + } + + phy->throttle_temp[i - 1] = val; + mutex_unlock(&phy->dev->mt76.mutex); + + ret = mt7996_mcu_set_thermal_protect(phy, true); + if (ret) + return ret; + + return count; +} + +static SENSOR_DEVICE_ATTR_RO(temp1_input, mt7996_thermal_temp, 0); +static SENSOR_DEVICE_ATTR_RW(temp1_crit, mt7996_thermal_temp, 1); +static SENSOR_DEVICE_ATTR_RW(temp1_max, mt7996_thermal_temp, 2); +static SENSOR_DEVICE_ATTR_RO(throttle1, mt7996_thermal_temp, 3); + +static struct attribute *mt7996_hwmon_attrs[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_crit.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_throttle1.dev_attr.attr, + NULL, +}; +ATTRIBUTE_GROUPS(mt7996_hwmon); + +static int +mt7996_thermal_get_max_throttle_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + *state = MT7996_CDEV_THROTTLE_MAX; + + return 0; +} + +static int +mt7996_thermal_get_cur_throttle_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + struct mt7996_phy *phy = cdev->devdata; + + *state = phy->cdev_state; + + return 0; +} + +static int +mt7996_thermal_set_cur_throttle_state(struct thermal_cooling_device *cdev, + unsigned long state) +{ + struct mt7996_phy *phy = cdev->devdata; + u8 throttling = MT7996_THERMAL_THROTTLE_MAX - state; + int ret; + + if (state > MT7996_CDEV_THROTTLE_MAX) { + dev_err(phy->dev->mt76.dev, + "please specify a valid throttling state\n"); + return -EINVAL; + } + + if (state == phy->cdev_state) + return 0; + + /* cooling_device convention: 0 = no cooling, more = more cooling + * mcu convention: 1 = max cooling, more = less cooling + */ + ret = mt7996_mcu_set_thermal_throttling(phy, throttling); + if (ret) + return ret; + + phy->cdev_state = state; + + return 0; +} + +static const struct thermal_cooling_device_ops mt7996_thermal_ops = { + .get_max_state = mt7996_thermal_get_max_throttle_state, + .get_cur_state = mt7996_thermal_get_cur_throttle_state, + .set_cur_state = mt7996_thermal_set_cur_throttle_state, +}; + +static void mt7996_unregister_thermal(struct mt7996_phy *phy) +{ + struct wiphy *wiphy = phy->mt76->hw->wiphy; + char name[sizeof("cooling_deviceXXX")]; + + if (!phy->cdev) + return; + + snprintf(name, sizeof(name), "cooling_device%d", phy->mt76->band_idx); + sysfs_remove_link(&wiphy->dev.kobj, name); + thermal_cooling_device_unregister(phy->cdev); +} + +static int mt7996_thermal_init(struct mt7996_phy *phy) +{ + struct wiphy *wiphy = phy->mt76->hw->wiphy; + char cname[sizeof("cooling_deviceXXX")]; + struct thermal_cooling_device *cdev; + struct device *hwmon; + const char *name; + + name = devm_kasprintf(&wiphy->dev, GFP_KERNEL, "mt7996_%s.%d", + wiphy_name(wiphy), phy->mt76->band_idx); + if (!name) + return -ENOMEM; + + snprintf(cname, sizeof(cname), "cooling_device%d", phy->mt76->band_idx); + + cdev = thermal_cooling_device_register(name, phy, &mt7996_thermal_ops); + if (!IS_ERR(cdev)) { + if (sysfs_create_link(&wiphy->dev.kobj, &cdev->device.kobj, + cname) < 0) + thermal_cooling_device_unregister(cdev); + else + phy->cdev = cdev; + } + + /* initialize critical/maximum high temperature */ + phy->throttle_temp[MT7996_CRIT_TEMP_IDX] = MT7996_CRIT_TEMP; + phy->throttle_temp[MT7996_MAX_TEMP_IDX] = MT7996_MAX_TEMP; + + if (!IS_REACHABLE(CONFIG_HWMON)) + return 0; + + hwmon = devm_hwmon_device_register_with_groups(&wiphy->dev, name, phy, + mt7996_hwmon_groups); + + if (IS_ERR(hwmon)) + return PTR_ERR(hwmon); + + return 0; +} + +static void mt7996_led_set_config(struct led_classdev *led_cdev, + u8 delay_on, u8 delay_off) +{ + struct mt7996_dev *dev; + struct mt76_phy *mphy; + u32 val; + + mphy = container_of(led_cdev, struct mt76_phy, leds.cdev); + dev = container_of(mphy->dev, struct mt7996_dev, mt76); + + /* select TX blink mode, 2: only data frames */ + mt76_rmw_field(dev, MT_TMAC_TCR0(mphy->band_idx), MT_TMAC_TCR0_TX_BLINK, 2); + + /* enable LED */ + mt76_wr(dev, MT_LED_EN(mphy->band_idx), 1); + + /* set LED Tx blink on/off time */ + val = FIELD_PREP(MT_LED_TX_BLINK_ON_MASK, delay_on) | + FIELD_PREP(MT_LED_TX_BLINK_OFF_MASK, delay_off); + mt76_wr(dev, MT_LED_TX_BLINK(mphy->band_idx), val); + + /* turn LED off */ + if (delay_off == 0xff && delay_on == 0x0) { + val = MT_LED_CTRL_POLARITY | MT_LED_CTRL_KICK; + } else { + /* control LED */ + val = MT_LED_CTRL_BLINK_MODE | MT_LED_CTRL_KICK; + if (mphy->band_idx == MT_BAND1) + val |= MT_LED_CTRL_BLINK_BAND_SEL; + } + + if (mphy->leds.al) + val |= MT_LED_CTRL_POLARITY; + + mt76_wr(dev, MT_LED_CTRL(mphy->band_idx), val); + mt76_clear(dev, MT_LED_CTRL(mphy->band_idx), MT_LED_CTRL_KICK); +} + +static int mt7996_led_set_blink(struct led_classdev *led_cdev, + unsigned long *delay_on, + unsigned long *delay_off) +{ + u16 delta_on = 0, delta_off = 0; + +#define HW_TICK 10 +#define TO_HW_TICK(_t) (((_t) > HW_TICK) ? ((_t) / HW_TICK) : HW_TICK) + + if (*delay_on) + delta_on = TO_HW_TICK(*delay_on); + if (*delay_off) + delta_off = TO_HW_TICK(*delay_off); + + mt7996_led_set_config(led_cdev, delta_on, delta_off); + + return 0; +} + +static void mt7996_led_set_brightness(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + if (!brightness) + mt7996_led_set_config(led_cdev, 0, 0xff); + else + mt7996_led_set_config(led_cdev, 0xff, 0); +} + +static void __mt7996_init_txpower(struct mt7996_phy *phy, + struct ieee80211_supported_band *sband) +{ + struct mt7996_dev *dev = phy->dev; + int i, n_chains = hweight16(phy->mt76->chainmask); + int path_delta = mt76_tx_power_path_delta(n_chains); + int pwr_delta = mt7996_eeprom_get_power_delta(dev, sband->band); + struct mt76_power_limits limits; + + for (i = 0; i < sband->n_channels; i++) { + struct ieee80211_channel *chan = &sband->channels[i]; + int target_power = mt7996_eeprom_get_target_power(dev, chan); + + target_power += pwr_delta; + target_power = mt76_get_rate_power_limits(phy->mt76, chan, + &limits, + target_power); + target_power += path_delta; + target_power = DIV_ROUND_UP(target_power, 2); + chan->max_power = min_t(int, chan->max_reg_power, + target_power); + chan->orig_mpwr = target_power; + } +} + +void mt7996_init_txpower(struct mt7996_phy *phy) +{ + if (!phy) + return; + + if (phy->mt76->cap.has_2ghz) + __mt7996_init_txpower(phy, &phy->mt76->sband_2g.sband); + if (phy->mt76->cap.has_5ghz) + __mt7996_init_txpower(phy, &phy->mt76->sband_5g.sband); + if (phy->mt76->cap.has_6ghz) + __mt7996_init_txpower(phy, &phy->mt76->sband_6g.sband); +} + +static void +mt7996_regd_notifier(struct wiphy *wiphy, + struct regulatory_request *request) +{ + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mt7996_phy *phy; + + memcpy(dev->mt76.alpha2, request->alpha2, sizeof(dev->mt76.alpha2)); + dev->mt76.region = request->dfs_region; + + mt7996_for_each_phy(dev, phy) { + if (dev->mt76.region == NL80211_DFS_UNSET) + mt7996_mcu_rdd_background_enable(phy, NULL); + + mt7996_init_txpower(phy); + phy->mt76->dfs_state = MT_DFS_STATE_UNKNOWN; + mt7996_dfs_init_radar_detector(phy); + } +} + +static void +mt7996_init_wiphy_band(struct ieee80211_hw *hw, struct mt7996_phy *phy) +{ + struct mt7996_dev *dev = phy->dev; + struct wiphy *wiphy = hw->wiphy; + int n_radios = hw->wiphy->n_radio; + struct wiphy_radio_freq_range *freq = &dev->radio_freqs[n_radios]; + struct wiphy_radio *radio = &dev->radios[n_radios]; + + phy->slottime = 9; + phy->beacon_rate = -1; + phy->rxfilter = MT_WF_RFCR_DROP_OTHER_UC; + + if (phy->mt76->cap.has_2ghz) { + phy->mt76->sband_2g.sband.ht_cap.cap |= + IEEE80211_HT_CAP_LDPC_CODING | + IEEE80211_HT_CAP_MAX_AMSDU; + phy->mt76->sband_2g.sband.ht_cap.ampdu_density = + IEEE80211_HT_MPDU_DENSITY_2; + freq->start_freq = 2400000; + freq->end_freq = 2500000; + } else if (phy->mt76->cap.has_5ghz) { + phy->mt76->sband_5g.sband.ht_cap.cap |= + IEEE80211_HT_CAP_LDPC_CODING | + IEEE80211_HT_CAP_MAX_AMSDU; + + phy->mt76->sband_5g.sband.vht_cap.cap |= + IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 | + IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK | + IEEE80211_VHT_CAP_SHORT_GI_160 | + IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; + phy->mt76->sband_5g.sband.ht_cap.ampdu_density = + IEEE80211_HT_MPDU_DENSITY_1; + + ieee80211_hw_set(hw, SUPPORTS_VHT_EXT_NSS_BW); + freq->start_freq = 5000000; + freq->end_freq = 5900000; + } else if (phy->mt76->cap.has_6ghz) { + freq->start_freq = 5900000; + freq->end_freq = 7200000; + } else { + return; + } + + dev->radio_phy[n_radios] = phy; + radio->freq_range = freq; + radio->n_freq_range = 1; + radio->iface_combinations = &if_comb; + radio->n_iface_combinations = 1; + memcpy(dev->radio_addrs[n_radios].addr, phy->mt76->macaddr, ETH_ALEN); + hw->wiphy->n_addresses++; + hw->wiphy->n_radio++; + + wiphy->available_antennas_rx |= phy->mt76->chainmask; + wiphy->available_antennas_tx |= phy->mt76->chainmask; + + mt76_set_stream_caps(phy->mt76, true); + mt7996_set_stream_vht_txbf_caps(phy); + mt7996_set_stream_he_eht_caps(phy); + mt7996_init_txpower(phy); +} + +static void +mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed) +{ + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mt76_dev *mdev = &dev->mt76; + struct wiphy *wiphy = hw->wiphy; + u16 max_subframes = dev->has_eht ? IEEE80211_MAX_AMPDU_BUF_EHT : + IEEE80211_MAX_AMPDU_BUF_HE; + + hw->queues = 4; + hw->max_rx_aggregation_subframes = max_subframes; + hw->max_tx_aggregation_subframes = max_subframes; + if (is_mt7990(mdev) && dev->has_eht) + hw->max_tx_aggregation_subframes = 512; + + hw->netdev_features = NETIF_F_RXCSUM; + if (mtk_wed_device_active(wed) || mt76_npu_device_active(mdev)) + hw->netdev_features |= NETIF_F_HW_TC; + + hw->radiotap_timestamp.units_pos = + IEEE80211_RADIOTAP_TIMESTAMP_UNIT_US; + + hw->sta_data_size = sizeof(struct mt7996_sta); + hw->vif_data_size = sizeof(struct mt7996_vif); + hw->chanctx_data_size = sizeof(struct mt76_chanctx); + + wiphy->iface_combinations = is_mt7996(&dev->mt76) ? &if_comb_global : + &if_comb_global_7992; + wiphy->n_iface_combinations = 1; + + wiphy->radio = dev->radios; + wiphy->addresses = dev->radio_addrs; + + wiphy->reg_notifier = mt7996_regd_notifier; + wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH | + WIPHY_FLAG_SUPPORTS_MLO; + wiphy->mbssid_max_interfaces = 16; + wiphy->iftype_ext_capab = iftypes_ext_capa; + wiphy->num_iftype_ext_capab = ARRAY_SIZE(iftypes_ext_capa); + + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BSS_COLOR); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_VHT_IBSS); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_RATE_LEGACY); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_RATE_HT); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_RATE_VHT); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_RATE_HE); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_UNSOL_BCAST_PROBE_RESP); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_FILS_DISCOVERY); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_CAN_REPLACE_PTK0); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_SET_SCAN_DWELL); + + if (mt7996_eeprom_has_background_radar(dev) && + (!mdev->dev->of_node || + !of_property_read_bool(mdev->dev->of_node, + "mediatek,disable-radar-background"))) + wiphy_ext_feature_set(wiphy, + NL80211_EXT_FEATURE_RADAR_BACKGROUND); + + ieee80211_hw_set(hw, HAS_RATE_CONTROL); + ieee80211_hw_set(hw, SUPPORTS_TX_ENCAP_OFFLOAD); + ieee80211_hw_set(hw, SUPPORTS_RX_DECAP_OFFLOAD); + ieee80211_hw_set(hw, NO_VIRTUAL_MONITOR); + ieee80211_hw_set(hw, SUPPORTS_MULTI_BSSID); + ieee80211_hw_set(hw, CHANCTX_STA_CSA); + + hw->max_tx_fragments = 4; + wiphy->txq_memory_limit = 32 << 20; /* 32 MiB */ + + /* init led callbacks */ + if (IS_ENABLED(CONFIG_MT76_LEDS)) { + dev->mphy.leds.cdev.brightness_set = mt7996_led_set_brightness; + dev->mphy.leds.cdev.blink_set = mt7996_led_set_blink; + } + + wiphy->max_scan_ssids = 4; + wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN; + + mt7996_init_wiphy_band(hw, &dev->phy); +} + +static void +mt7996_mac_init_band(struct mt7996_dev *dev, u8 band) +{ + u32 mask, set; + + if (!mt7996_band_valid(dev, band)) + return; + + /* clear estimated value of EIFS for Rx duration & OBSS time */ + mt76_wr(dev, MT_WF_RMAC_RSVD0(band), MT_WF_RMAC_RSVD0_EIFS_CLR); + + /* clear backoff time for Rx duration */ + mt76_clear(dev, MT_WF_RMAC_MIB_AIRTIME1(band), + MT_WF_RMAC_MIB_NONQOSD_BACKOFF); + mt76_clear(dev, MT_WF_RMAC_MIB_AIRTIME3(band), + MT_WF_RMAC_MIB_QOS01_BACKOFF); + mt76_clear(dev, MT_WF_RMAC_MIB_AIRTIME4(band), + MT_WF_RMAC_MIB_QOS23_BACKOFF); + + /* clear backoff time for Tx duration */ + mt76_clear(dev, MT_WTBLOFF_ACR(band), + MT_WTBLOFF_ADM_BACKOFFTIME); + + /* clear backoff time and set software compensation for OBSS time */ + mask = MT_WF_RMAC_MIB_OBSS_BACKOFF | MT_WF_RMAC_MIB_ED_OFFSET; + set = FIELD_PREP(MT_WF_RMAC_MIB_OBSS_BACKOFF, 0) | + FIELD_PREP(MT_WF_RMAC_MIB_ED_OFFSET, 4); + mt76_rmw(dev, MT_WF_RMAC_MIB_AIRTIME0(band), mask, set); + + /* filter out non-resp frames and get instanstaeous signal reporting */ + mask = MT_WTBLOFF_RSCR_RCPI_MODE | MT_WTBLOFF_RSCR_RCPI_PARAM; + set = FIELD_PREP(MT_WTBLOFF_RSCR_RCPI_MODE, 0) | + FIELD_PREP(MT_WTBLOFF_RSCR_RCPI_PARAM, 0x3); + mt76_rmw(dev, MT_WTBLOFF_RSCR(band), mask, set); + + /* MT_TXD5_TX_STATUS_HOST (MPDU format) has higher priority than + * MT_AGG_ACR_PPDU_TXS2H (PPDU format) even though ACR bit is set. + */ + mt76_set(dev, MT_AGG_ACR4(band), MT_AGG_ACR_PPDU_TXS2H); + + if (!is_mt7996(&dev->mt76)) + mt7996_mcu_set_bssid_mapping_addr(&dev->mt76, band); +} + +static void mt7996_mac_init_basic_rates(struct mt7996_dev *dev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(mt76_rates); i++) { + u16 rate = mt76_rates[i].hw_value; + /* odd index for driver, even index for firmware */ + u16 idx = MT7996_BASIC_RATES_TBL + 2 * i; + + rate = FIELD_PREP(MT_TX_RATE_MODE, rate >> 8) | + FIELD_PREP(MT_TX_RATE_IDX, rate & GENMASK(7, 0)); + mt7996_mcu_set_fixed_rate_table(&dev->phy, idx, rate, false); + } +} + +void mt7996_mac_init(struct mt7996_dev *dev) +{ +#define HIF_TXD_V2_1 0x21 + int i, rx_path_type; + + mt76_clear(dev, MT_MDP_DCR2, MT_MDP_DCR2_RX_TRANS_SHORT); + + for (i = 0; i < mt7996_wtbl_size(dev); i++) + mt7996_mac_wtbl_update(dev, i, + MT_WTBL_UPDATE_ADM_COUNT_CLEAR); + + if (IS_ENABLED(CONFIG_MT76_LEDS)) { + i = dev->mphy.leds.pin ? MT_LED_GPIO_MUX3 : MT_LED_GPIO_MUX2; + mt76_rmw_field(dev, i, MT_LED_GPIO_SEL_MASK, 4); + } + + /* rro module init */ + if (dev->hif2) { + if (mt76_npu_device_active(&dev->mt76)) + rx_path_type = is_mt7996(&dev->mt76) ? 6 : 8; + else + rx_path_type = is_mt7996(&dev->mt76) ? 2 : 7; + mt7996_mcu_set_rro(dev, UNI_RRO_SET_PLATFORM_TYPE, + rx_path_type); + } else { + mt7996_mcu_set_rro(dev, UNI_RRO_SET_PLATFORM_TYPE, 0); + } + + if (mt7996_has_hwrro(dev)) { + u16 timeout; + + timeout = mt76_rr(dev, MT_HW_REV) == MT_HW_REV1 ? 512 : 128; + mt7996_mcu_set_rro(dev, UNI_RRO_SET_FLUSH_TIMEOUT, timeout); + mt7996_mcu_set_rro(dev, UNI_RRO_SET_BYPASS_MODE, + is_mt7996(&dev->mt76) ? 1 : 2); + mt7996_mcu_set_rro(dev, UNI_RRO_SET_TXFREE_PATH, + !is_mt7996(&dev->mt76)); + } else { + mt7996_mcu_set_rro(dev, UNI_RRO_SET_BYPASS_MODE, 3); + mt7996_mcu_set_rro(dev, UNI_RRO_SET_TXFREE_PATH, 1); + } + + mt7996_mcu_wa_cmd(dev, MCU_WA_PARAM_CMD(SET), + MCU_WA_PARAM_HW_PATH_HIF_VER, + HIF_TXD_V2_1, 0); + + for (i = MT_BAND0; i <= MT_BAND2; i++) + mt7996_mac_init_band(dev, i); + + mt7996_mac_init_basic_rates(dev); +} + +int mt7996_txbf_init(struct mt7996_dev *dev) +{ + int ret; + + if (mt7996_band_valid(dev, MT_BAND1) || + mt7996_band_valid(dev, MT_BAND2)) { + ret = mt7996_mcu_set_txbf(dev, BF_MOD_EN_CTRL); + if (ret) + return ret; + } + + /* trigger sounding packets */ + ret = mt7996_mcu_set_txbf(dev, BF_SOUNDING_ON); + if (ret) + return ret; + + /* enable eBF */ + return mt7996_mcu_set_txbf(dev, BF_HW_EN_UPDATE); +} + +static int mt7996_register_phy(struct mt7996_dev *dev, enum mt76_band_id band) +{ + struct mt7996_phy *phy; + struct mt76_phy *mphy; + u32 mac_ofs, hif1_ofs = 0; + int ret; + struct mtk_wed_device *wed = &dev->mt76.mmio.wed; + + if (!mt7996_band_valid(dev, band)) + return 0; + + if (dev->hif2 && + ((is_mt7992(&dev->mt76) && band == MT_BAND1) || + (is_mt7996(&dev->mt76) && band == MT_BAND2 && + !mt76_npu_device_active(&dev->mt76)))) { + hif1_ofs = MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0); + wed = &dev->mt76.mmio.wed_hif2; + } + + mphy = mt76_alloc_radio_phy(&dev->mt76, sizeof(*phy), band); + if (!mphy) + return -ENOMEM; + + phy = mphy->priv; + phy->dev = dev; + phy->mt76 = mphy; + mphy->dev->phys[band] = mphy; + + INIT_DELAYED_WORK(&mphy->mac_work, mt7996_mac_work); + + ret = mt7996_eeprom_parse_hw_cap(dev, phy); + if (ret) + goto error; + + mac_ofs = band == MT_BAND2 ? MT_EE_MAC_ADDR3 : MT_EE_MAC_ADDR2; + memcpy(mphy->macaddr, dev->mt76.eeprom.data + mac_ofs, ETH_ALEN); + /* Make the extra PHY MAC address local without overlapping with + * the usual MAC address allocation scheme on multiple virtual interfaces + */ + if (!is_valid_ether_addr(mphy->macaddr)) { + memcpy(mphy->macaddr, dev->mt76.eeprom.data + MT_EE_MAC_ADDR, + ETH_ALEN); + mphy->macaddr[0] |= 2; + mphy->macaddr[0] ^= BIT(7); + if (band == MT_BAND2) + mphy->macaddr[0] ^= BIT(6); + } + ret = mt76_eeprom_override(mphy); + if (ret) + goto error; + + /* init wiphy according to mphy and phy */ + mt7996_init_wiphy_band(mphy->hw, phy); + + if (is_mt7996(&dev->mt76) && + ((band == MT_BAND1 && !dev->hif2) || + (band == MT_BAND2 && mt76_npu_device_active(&dev->mt76)))) { + int i; + + for (i = 0; i <= MT_TXQ_PSD; i++) + mphy->q_tx[i] = dev->mt76.phys[band - 1]->q_tx[0]; + } else { + int size = is_mt7996(&dev->mt76) && + mt76_npu_device_active(&dev->mt76) + ? MT7996_NPU_TX_RING_SIZE / 2 : MT7996_TX_RING_SIZE; + + ret = mt7996_init_tx_queues(mphy->priv, MT_TXQ_ID(band), size, + MT_TXQ_RING_BASE(band) + hif1_ofs, + wed); + if (ret) + goto error; + } + + ret = mt76_register_phy(mphy, true, mt76_rates, + ARRAY_SIZE(mt76_rates)); + if (ret) + goto error; + + if (wed == &dev->mt76.mmio.wed_hif2 && mtk_wed_device_active(wed)) { + mt76_wr(dev, MT_INT_PCIE1_MASK_CSR, MT_INT_TX_RX_DONE_EXT); + mtk_wed_device_start(&dev->mt76.mmio.wed_hif2, + MT_INT_TX_RX_DONE_EXT); + } + + return 0; + +error: + mphy->dev->phys[band] = NULL; + return ret; +} + +static void +mt7996_unregister_phy(struct mt7996_phy *phy) +{ + if (phy) + mt7996_unregister_thermal(phy); +} + +static void mt7996_init_work(struct work_struct *work) +{ + struct mt7996_dev *dev = container_of(work, struct mt7996_dev, + init_work); + + mt7996_mcu_set_eeprom(dev); + mt7996_mac_init(dev); + mt7996_txbf_init(dev); + + if (!is_mt7990(&dev->mt76)) + mt7996_mcu_set_dup_wtbl(dev); +} + +void mt7996_wfsys_reset(struct mt7996_dev *dev) +{ + if (!is_mt7990(&dev->mt76)) { + mt76_set(dev, MT_WF_SUBSYS_RST, 0x1); + msleep(20); + + mt76_clear(dev, MT_WF_SUBSYS_RST, 0x1); + msleep(20); + + return; + } + + if (!dev->recovery.hw_full_reset) + return; + + mt76_set(dev, MT_WF_SUBSYS_RST, + MT_WF_SUBSYS_RST_WHOLE_PATH_RST_REVERT | + MT_WF_SUBSYS_RST_BYPASS_WFDMA_SLP_PROT | + MT_WF_SUBSYS_RST_BYPASS_WFDMA2_SLP_PROT); + mt76_rmw(dev, MT_WF_SUBSYS_RST, + MT_WF_SUBSYS_RST_WHOLE_PATH_RST_REVERT_CYCLE, + u32_encode_bits(0x20, MT_WF_SUBSYS_RST_WHOLE_PATH_RST_REVERT_CYCLE)); + mt76_clear(dev, MT_WF_L05_RST, MT_WF_L05_RST_WF_RST_MASK); + mt76_set(dev, MT_WF_SUBSYS_RST, MT_WF_SUBSYS_RST_WHOLE_PATH_RST); + msleep(20); + + if (mt76_poll(dev, MT_WF_L05_RST, MT_WF_L05_RST_WF_RST_MASK, 0x1a, 1000)) + return; + + dev_err(dev->mt76.dev, "wfsys reset fail\n"); +} + +static void mt7996_rro_hw_init_v3(struct mt7996_dev *dev) +{ + struct mtk_wed_device *wed = &dev->mt76.mmio.wed; + u32 session_id; + + if (dev->mt76.hwrro_mode == MT76_HWRRO_V3_1) + return; + +#ifdef CONFIG_NET_MEDIATEK_SOC_WED + if (mtk_wed_device_active(wed) && mtk_wed_get_rx_capa(wed)) { + wed->wlan.ind_cmd.win_size = + ffs(MT7996_RRO_WINDOW_MAX_LEN) - 6; + if (is_mt7996(&dev->mt76)) + wed->wlan.ind_cmd.particular_sid = + MT7996_RRO_MAX_SESSION; + else + wed->wlan.ind_cmd.particular_sid = 1; + wed->wlan.ind_cmd.particular_se_phys = + dev->wed_rro.session.phy_addr; + wed->wlan.ind_cmd.se_group_nums = MT7996_RRO_ADDR_ELEM_LEN; + wed->wlan.ind_cmd.ack_sn_addr = MT_RRO_ACK_SN_CTRL; + } +#endif /* CONFIG_NET_MEDIATEK_SOC_WED */ + + if (mtk_wed_device_active(wed) && mtk_wed_get_rx_capa(wed)) { + mt76_wr(dev, MT_RRO_IND_CMD_SIGNATURE_BASE0, 0x15010e00); + mt76_set(dev, MT_RRO_IND_CMD_SIGNATURE_BASE1, + MT_RRO_IND_CMD_SIGNATURE_BASE1_EN); + } else { + mt76_wr(dev, MT_RRO_IND_CMD_SIGNATURE_BASE0, 0); + mt76_wr(dev, MT_RRO_IND_CMD_SIGNATURE_BASE1, 0); + } + + /* particular session configure */ + /* use max session idx + 1 as particular session id */ + mt76_wr(dev, MT_RRO_PARTICULAR_CFG0, dev->wed_rro.session.phy_addr); + + session_id = is_mt7996(&dev->mt76) ? MT7996_RRO_MAX_SESSION : 1; + mt76_wr(dev, MT_RRO_PARTICULAR_CFG1, + MT_RRO_PARTICULAR_CONFG_EN | + FIELD_PREP(MT_RRO_PARTICULAR_SID, session_id)); +} + +void mt7996_rro_hw_init(struct mt7996_dev *dev) +{ + u32 reg = MT_RRO_ADDR_ELEM_SEG_ADDR0; + int i; + + if (!mt7996_has_hwrro(dev)) + return; + + INIT_LIST_HEAD(&dev->wed_rro.page_cache); + for (i = 0; i < ARRAY_SIZE(dev->wed_rro.page_map); i++) + INIT_LIST_HEAD(&dev->wed_rro.page_map[i]); + + if (!is_mt7996(&dev->mt76)) { + reg = MT_RRO_MSDU_PG_SEG_ADDR0; + + if (dev->mt76.hwrro_mode == MT76_HWRRO_V3_1) { + mt76_clear(dev, MT_RRO_3_0_EMU_CONF, + MT_RRO_3_0_EMU_CONF_EN_MASK); + mt76_set(dev, MT_RRO_3_1_GLOBAL_CONFIG, + MT_RRO_3_1_GLOBAL_CONFIG_RXDMAD_SEL); + if (!mtk_wed_device_active(&dev->mt76.mmio.wed) && + !mt76_npu_device_active(&dev->mt76)) { + mt76_set(dev, MT_RRO_3_1_GLOBAL_CONFIG, + MT_RRO_3_1_GLOBAL_CONFIG_RX_DIDX_WR_EN | + MT_RRO_3_1_GLOBAL_CONFIG_RX_CIDX_RD_EN); + mt76_wr(dev, MT_RRO_RX_RING_AP_CIDX_ADDR, + dev->wed_rro.emi_rings_cpu.phy_addr >> 4); + mt76_wr(dev, MT_RRO_RX_RING_AP_DIDX_ADDR, + dev->wed_rro.emi_rings_dma.phy_addr >> 4); + } + } else { + /* set emul 3.0 function */ + mt76_set(dev, MT_RRO_3_0_EMU_CONF, MT_RRO_3_0_EMU_CONF_EN_MASK); + + mt76_wr(dev, MT_RRO_ADDR_ARRAY_BASE0, + dev->wed_rro.addr_elem[0].phy_addr); + } + + mt76_set(dev, MT_RRO_3_1_GLOBAL_CONFIG, + MT_RRO_3_1_GLOBAL_CONFIG_INTERLEAVE_EN); + + /* setup Msdu page address */ + for (i = 0; i < ARRAY_SIZE(dev->wed_rro.msdu_pg); i++) { + mt76_wr(dev, reg, + dev->wed_rro.msdu_pg[i].phy_addr >> 4); + reg += 4; + } + } else { + /* TODO: remove line after WM has set */ + mt76_clear(dev, WF_RRO_AXI_MST_CFG, + WF_RRO_AXI_MST_CFG_DIDX_OK); + + /* setup BA bitmap cache address */ + mt76_wr(dev, MT_RRO_BA_BITMAP_BASE0, + dev->wed_rro.ba_bitmap[0].phy_addr); + mt76_wr(dev, MT_RRO_BA_BITMAP_BASE1, 0); + mt76_wr(dev, MT_RRO_BA_BITMAP_BASE_EXT0, + dev->wed_rro.ba_bitmap[1].phy_addr); + mt76_wr(dev, MT_RRO_BA_BITMAP_BASE_EXT1, 0); + + /* Setup Address element address */ + for (i = 0; i < ARRAY_SIZE(dev->wed_rro.addr_elem); i++) { + mt76_wr(dev, reg, + dev->wed_rro.addr_elem[i].phy_addr >> 4); + reg += 4; + } + + /* Setup Address element address - separate address segment + * mode. + */ + mt76_wr(dev, MT_RRO_ADDR_ARRAY_BASE1, + MT_RRO_ADDR_ARRAY_ELEM_ADDR_SEG_MODE); + } + + mt7996_rro_hw_init_v3(dev); + + /* interrupt enable */ + mt76_wr(dev, MT_RRO_HOST_INT_ENA, + MT_RRO_HOST_INT_ENA_HOST_RRO_DONE_ENA); +} + +static int mt7996_wed_rro_init(struct mt7996_dev *dev) +{ + u32 val = FIELD_PREP(WED_RRO_ADDR_SIGNATURE_MASK, 0xff); + struct mt7996_wed_rro_addr *addr; + void *ptr; + int i; + + if (!mt7996_has_hwrro(dev)) + return 0; + + if (dev->mt76.hwrro_mode == MT76_HWRRO_V3) { + for (i = 0; i < ARRAY_SIZE(dev->wed_rro.ba_bitmap); i++) { + ptr = dmam_alloc_coherent(dev->mt76.dma_dev, + MT7996_RRO_BA_BITMAP_CR_SIZE, + &dev->wed_rro.ba_bitmap[i].phy_addr, + GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + dev->wed_rro.ba_bitmap[i].ptr = ptr; + } + } + + for (i = 0; i < ARRAY_SIZE(dev->wed_rro.addr_elem); i++) { + int j; + + ptr = dmam_alloc_coherent(dev->mt76.dma_dev, + MT7996_RRO_WINDOW_MAX_SIZE * sizeof(*addr), + &dev->wed_rro.addr_elem[i].phy_addr, + GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + dev->wed_rro.addr_elem[i].ptr = ptr; + memset(dev->wed_rro.addr_elem[i].ptr, 0, + MT7996_RRO_WINDOW_MAX_SIZE * sizeof(*addr)); + + addr = dev->wed_rro.addr_elem[i].ptr; + for (j = 0; j < MT7996_RRO_WINDOW_MAX_SIZE; j++) { + addr->data = cpu_to_le32(val); + addr++; + } + + if (is_mt7996(&dev->mt76) && + mt76_npu_device_active(&dev->mt76)) + mt76_npu_send_txrx_addr(&dev->mt76, 0, i, + dev->wed_rro.addr_elem[i].phy_addr, + 0, 0); + +#ifdef CONFIG_NET_MEDIATEK_SOC_WED + if (mtk_wed_device_active(&dev->mt76.mmio.wed) && + mtk_wed_get_rx_capa(&dev->mt76.mmio.wed)) { + struct mtk_wed_device *wed = &dev->mt76.mmio.wed; + + wed->wlan.ind_cmd.addr_elem_phys[i] = + dev->wed_rro.addr_elem[i].phy_addr; + } +#endif /* CONFIG_NET_MEDIATEK_SOC_WED */ + } + + for (i = 0; i < ARRAY_SIZE(dev->wed_rro.msdu_pg); i++) { + ptr = dmam_alloc_coherent(dev->mt76.dma_dev, + MT7996_RRO_MSDU_PG_SIZE_PER_CR, + &dev->wed_rro.msdu_pg[i].phy_addr, + GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + dev->wed_rro.msdu_pg[i].ptr = ptr; + + memset(dev->wed_rro.msdu_pg[i].ptr, 0, + MT7996_RRO_MSDU_PG_SIZE_PER_CR); + } + + if (!mtk_wed_device_active(&dev->mt76.mmio.wed) && + dev->mt76.hwrro_mode == MT76_HWRRO_V3_1) { + ptr = dmam_alloc_coherent(dev->mt76.dma_dev, + sizeof(*dev->wed_rro.emi_rings_cpu.ptr), + &dev->wed_rro.emi_rings_cpu.phy_addr, + GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + dev->wed_rro.emi_rings_cpu.ptr = ptr; + + ptr = dmam_alloc_coherent(dev->mt76.dma_dev, + sizeof(*dev->wed_rro.emi_rings_dma.ptr), + &dev->wed_rro.emi_rings_dma.phy_addr, + GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + dev->wed_rro.emi_rings_dma.ptr = ptr; + } + + ptr = dmam_alloc_coherent(dev->mt76.dma_dev, + MT7996_RRO_WINDOW_MAX_LEN * sizeof(*addr), + &dev->wed_rro.session.phy_addr, + GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + dev->wed_rro.session.ptr = ptr; + addr = dev->wed_rro.session.ptr; + for (i = 0; i < MT7996_RRO_WINDOW_MAX_LEN; i++) { + addr->data = cpu_to_le32(val); + addr++; + } + + if (is_mt7996(&dev->mt76) && mt76_npu_device_active(&dev->mt76)) + mt76_npu_send_txrx_addr(&dev->mt76, 1, 0, + dev->wed_rro.session.phy_addr, 0, 0); + + mt7996_rro_hw_init(dev); + + return mt7996_dma_rro_init(dev); +} + +static void mt7996_wed_rro_free(struct mt7996_dev *dev) +{ + int i; + + if (!mt7996_has_hwrro(dev)) + return; + + for (i = 0; i < ARRAY_SIZE(dev->wed_rro.ba_bitmap); i++) { + if (!dev->wed_rro.ba_bitmap[i].ptr) + continue; + + dmam_free_coherent(dev->mt76.dma_dev, + MT7996_RRO_BA_BITMAP_CR_SIZE, + dev->wed_rro.ba_bitmap[i].ptr, + dev->wed_rro.ba_bitmap[i].phy_addr); + } + + for (i = 0; i < ARRAY_SIZE(dev->wed_rro.addr_elem); i++) { + if (!dev->wed_rro.addr_elem[i].ptr) + continue; + + dmam_free_coherent(dev->mt76.dma_dev, + MT7996_RRO_WINDOW_MAX_SIZE * + sizeof(struct mt7996_wed_rro_addr), + dev->wed_rro.addr_elem[i].ptr, + dev->wed_rro.addr_elem[i].phy_addr); + } + + for (i = 0; i < ARRAY_SIZE(dev->wed_rro.msdu_pg); i++) { + if (!dev->wed_rro.msdu_pg[i].ptr) + continue; + + dmam_free_coherent(dev->mt76.dma_dev, + MT7996_RRO_MSDU_PG_SIZE_PER_CR, + dev->wed_rro.msdu_pg[i].ptr, + dev->wed_rro.msdu_pg[i].phy_addr); + } + + if (dev->wed_rro.emi_rings_cpu.ptr) + dmam_free_coherent(dev->mt76.dma_dev, + sizeof(*dev->wed_rro.emi_rings_cpu.ptr), + dev->wed_rro.emi_rings_cpu.ptr, + dev->wed_rro.emi_rings_cpu.phy_addr); + + if (dev->wed_rro.emi_rings_dma.ptr) + dmam_free_coherent(dev->mt76.dma_dev, + sizeof(*dev->wed_rro.emi_rings_dma.ptr), + dev->wed_rro.emi_rings_dma.ptr, + dev->wed_rro.emi_rings_dma.phy_addr); + + if (!dev->wed_rro.session.ptr) + return; + + dmam_free_coherent(dev->mt76.dma_dev, + MT7996_RRO_WINDOW_MAX_LEN * + sizeof(struct mt7996_wed_rro_addr), + dev->wed_rro.session.ptr, + dev->wed_rro.session.phy_addr); +} + +static void mt7996_wed_rro_work(struct work_struct *work) +{ + u32 val = FIELD_PREP(WED_RRO_ADDR_SIGNATURE_MASK, 0xff); + struct mt7996_dev *dev; + LIST_HEAD(list); + + dev = (struct mt7996_dev *)container_of(work, struct mt7996_dev, + wed_rro.work); + + spin_lock_bh(&dev->wed_rro.lock); + list_splice_init(&dev->wed_rro.poll_list, &list); + spin_unlock_bh(&dev->wed_rro.lock); + + while (!list_empty(&list)) { + struct mt7996_wed_rro_session_id *e; + int i; + + e = list_first_entry(&list, struct mt7996_wed_rro_session_id, + list); + list_del_init(&e->list); + + if (mt76_npu_device_active(&dev->mt76)) { + if (is_mt7996(&dev->mt76)) + mt76_npu_send_txrx_addr(&dev->mt76, 3, e->id, + 0, 0, 0); + goto reset_session; + } + + for (i = 0; i < MT7996_RRO_WINDOW_MAX_LEN; i++) { + void *ptr = dev->wed_rro.session.ptr; + struct mt7996_wed_rro_addr *elem; + u32 idx, elem_id = i; + + if (e->id == MT7996_RRO_MAX_SESSION) + goto reset; + + idx = e->id / MT7996_RRO_BA_BITMAP_SESSION_SIZE; + if (idx >= ARRAY_SIZE(dev->wed_rro.addr_elem)) + goto out; + + ptr = dev->wed_rro.addr_elem[idx].ptr; + elem_id += + (e->id % MT7996_RRO_BA_BITMAP_SESSION_SIZE) * + MT7996_RRO_WINDOW_MAX_LEN; +reset: + elem = ptr + elem_id * sizeof(*elem); + elem->data |= cpu_to_le32(val); + } +reset_session: + mt7996_mcu_wed_rro_reset_sessions(dev, e->id); +out: + kfree(e); + } +} + +static int mt7996_variant_type_init(struct mt7996_dev *dev) +{ + u32 val = mt76_rr(dev, MT_PAD_GPIO); + u8 var_type; + + switch (mt76_chip(&dev->mt76)) { + case MT7996_DEVICE_ID: + if (val & MT_PAD_GPIO_2ADIE_TBTC) + var_type = MT7996_VAR_TYPE_233; + else + var_type = MT7996_VAR_TYPE_444; + break; + case MT7992_DEVICE_ID: + if (val & MT_PAD_GPIO_ADIE_SINGLE) + var_type = MT7992_VAR_TYPE_23; + else if (u32_get_bits(val, MT_PAD_GPIO_ADIE_COMB_7992)) + var_type = MT7992_VAR_TYPE_44; + else + var_type = MT7992_VAR_TYPE_24; + break; + case MT7990_DEVICE_ID: + var_type = MT7990_VAR_TYPE_23; + break; + default: + return -EINVAL; + } + + dev->var.type = var_type; + return 0; +} + +static int mt7996_variant_fem_init(struct mt7996_dev *dev) +{ +#define MT7976C_EFUSE_OFFSET 0x470 + u8 buf[MT7996_EEPROM_BLOCK_SIZE], idx, adie_idx, adie_comb; + u32 regval, val = mt76_rr(dev, MT_PAD_GPIO); + u16 adie_id, adie_ver; + bool is_7976c; + int ret; + + if (is_mt7992(&dev->mt76)) { + adie_idx = (val & MT_PAD_GPIO_ADIE_SINGLE) ? 0 : 1; + adie_comb = u32_get_bits(val, MT_PAD_GPIO_ADIE_COMB_7992); + } else { + adie_idx = 0; + adie_comb = u32_get_bits(val, MT_PAD_GPIO_ADIE_COMB); + } + + ret = mt7996_mcu_rf_regval(dev, MT_ADIE_CHIP_ID(adie_idx), ®val, false); + if (ret) + return ret; + + ret = mt7996_mcu_get_eeprom(dev, MT7976C_EFUSE_OFFSET, buf, sizeof(buf), + EEPROM_MODE_EFUSE); + if (ret && ret != -EINVAL) + return ret; + + adie_ver = u32_get_bits(regval, MT_ADIE_VERSION_MASK); + idx = MT7976C_EFUSE_OFFSET % MT7996_EEPROM_BLOCK_SIZE; + is_7976c = adie_ver == 0x8a10 || adie_ver == 0x8b00 || + adie_ver == 0x8c10 || buf[idx] == 0xc; + + adie_id = u32_get_bits(regval, MT_ADIE_CHIP_ID_MASK); + if (adie_id == 0x7975 || adie_id == 0x7979 || + (adie_id == 0x7976 && is_7976c)) + dev->var.fem = MT7996_FEM_INT; + else if (adie_id == 0x7977 && adie_comb == 1) + dev->var.fem = MT7996_FEM_MIX; + else + dev->var.fem = MT7996_FEM_EXT; + + return 0; +} + +static int mt7996_init_hardware(struct mt7996_dev *dev) +{ + int ret, idx; + + mt76_wr(dev, MT_INT_SOURCE_CSR, ~0); + if (is_mt7992(&dev->mt76)) { + mt76_rmw(dev, MT_AFE_CTL_BAND_PLL_03(MT_BAND0), MT_AFE_CTL_BAND_PLL_03_MSB_EN, 0); + mt76_rmw(dev, MT_AFE_CTL_BAND_PLL_03(MT_BAND1), MT_AFE_CTL_BAND_PLL_03_MSB_EN, 0); + } + + INIT_WORK(&dev->init_work, mt7996_init_work); + INIT_WORK(&dev->wed_rro.work, mt7996_wed_rro_work); + INIT_LIST_HEAD(&dev->wed_rro.poll_list); + spin_lock_init(&dev->wed_rro.lock); + + ret = mt7996_variant_type_init(dev); + if (ret) + return ret; + + ret = mt7996_dma_init(dev); + if (ret) + return ret; + + set_bit(MT76_STATE_INITIALIZED, &dev->mphy.state); + + ret = mt7996_mcu_init(dev); + if (ret) + return ret; + + ret = mt7996_wed_rro_init(dev); + if (ret) + return ret; + + ret = mt7996_variant_fem_init(dev); + if (ret) + return ret; + + ret = mt7996_eeprom_init(dev); + if (ret < 0) + return ret; + + /* Beacon and mgmt frames should occupy wcid 0 */ + idx = mt76_wcid_alloc(dev->mt76.wcid_mask, MT7996_WTBL_STA); + if (idx) + return -ENOSPC; + + dev->mt76.global_wcid.idx = idx; + dev->mt76.global_wcid.hw_key_idx = -1; + dev->mt76.global_wcid.tx_info |= MT_WCID_TX_INFO_SET; + rcu_assign_pointer(dev->mt76.wcid[idx], &dev->mt76.global_wcid); + + return 0; +} + +void mt7996_set_stream_vht_txbf_caps(struct mt7996_phy *phy) +{ + int sts; + u32 *cap; + + if (!phy->mt76->cap.has_5ghz) + return; + + sts = hweight16(phy->mt76->chainmask); + cap = &phy->mt76->sband_5g.sband.vht_cap.cap; + + *cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE | + IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE; + + if (is_mt7992(phy->mt76->dev)) + *cap |= FIELD_PREP(IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK, 4); + else + *cap |= FIELD_PREP(IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK, 3); + + *cap &= ~(IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK | + IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE | + IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE); + + if (sts < 2) + return; + + *cap |= IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE | + IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE | + FIELD_PREP(IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK, sts - 1); +} + +static void +mt7996_set_stream_he_txbf_caps(struct mt7996_phy *phy, + struct ieee80211_sta_he_cap *he_cap, int vif, + enum nl80211_band band) +{ + struct ieee80211_he_cap_elem *elem = &he_cap->he_cap_elem; + int sts = hweight16(phy->mt76->chainmask); + bool non_2g = band != NL80211_BAND_2GHZ; + u8 c; + +#ifdef CONFIG_MAC80211_MESH + if (vif == NL80211_IFTYPE_MESH_POINT) + return; +#endif + + elem->phy_cap_info[3] &= ~IEEE80211_HE_PHY_CAP3_SU_BEAMFORMER; + elem->phy_cap_info[4] &= ~IEEE80211_HE_PHY_CAP4_MU_BEAMFORMER; + + c = IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_MASK | + IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_MASK; + elem->phy_cap_info[5] &= ~c; + + c = IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMING_FB | + IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMING_PARTIAL_BW_FB; + elem->phy_cap_info[6] &= ~c; + + elem->phy_cap_info[7] &= ~IEEE80211_HE_PHY_CAP7_MAX_NC_MASK; + + c = IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US | + IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO; + elem->phy_cap_info[2] |= c; + + c = IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE; + + if (is_mt7992(phy->mt76->dev)) + c |= IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_5 | + (IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_5 * non_2g); + else + c |= IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_4 | + (IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_4 * non_2g); + + elem->phy_cap_info[4] |= c; + + /* do not support NG16 due to spec D4.0 changes subcarrier idx */ + c = IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_42_SU | + IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_75_MU; + + if (vif == NL80211_IFTYPE_STATION) + c |= IEEE80211_HE_PHY_CAP6_PARTIAL_BANDWIDTH_DL_MUMIMO; + + elem->phy_cap_info[6] |= c; + + if (sts < 2) + return; + + /* the maximum cap is 4 x 3, (Nr, Nc) = (3, 2) */ + elem->phy_cap_info[7] |= min_t(int, sts - 1, 2) << 3; + + if (!(vif == NL80211_IFTYPE_AP || vif == NL80211_IFTYPE_STATION)) + return; + + elem->phy_cap_info[3] |= IEEE80211_HE_PHY_CAP3_SU_BEAMFORMER; + + c = FIELD_PREP(IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_MASK, + sts - 1) | + (FIELD_PREP(IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_MASK, + sts - 1) * non_2g); + + elem->phy_cap_info[5] |= c; + + if (vif != NL80211_IFTYPE_AP) + return; + + elem->phy_cap_info[4] |= IEEE80211_HE_PHY_CAP4_MU_BEAMFORMER; + + c = IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMING_FB | + IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMING_PARTIAL_BW_FB; + elem->phy_cap_info[6] |= c; + + c = 0; + if (non_2g) + c |= IEEE80211_HE_PHY_CAP7_STBC_TX_ABOVE_80MHZ | + IEEE80211_HE_PHY_CAP7_STBC_RX_ABOVE_80MHZ; + elem->phy_cap_info[7] |= c; +} + +static void +mt7996_init_he_caps(struct mt7996_phy *phy, enum nl80211_band band, + struct ieee80211_sband_iftype_data *data, + enum nl80211_iftype iftype) +{ + struct ieee80211_sta_he_cap *he_cap = &data->he_cap; + struct ieee80211_he_cap_elem *he_cap_elem = &he_cap->he_cap_elem; + struct ieee80211_he_mcs_nss_supp *he_mcs = &he_cap->he_mcs_nss_supp; + int i, nss = hweight8(phy->mt76->antenna_mask); + u16 mcs_map = 0; + + for (i = 0; i < 8; i++) { + if (i < nss) + mcs_map |= (IEEE80211_HE_MCS_SUPPORT_0_11 << (i * 2)); + else + mcs_map |= (IEEE80211_HE_MCS_NOT_SUPPORTED << (i * 2)); + } + + he_cap->has_he = true; + + he_cap_elem->mac_cap_info[0] = IEEE80211_HE_MAC_CAP0_HTC_HE; + he_cap_elem->mac_cap_info[3] = IEEE80211_HE_MAC_CAP3_OMI_CONTROL | + IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_3; + he_cap_elem->mac_cap_info[4] = IEEE80211_HE_MAC_CAP4_AMSDU_IN_AMPDU; + + if (band == NL80211_BAND_2GHZ) + he_cap_elem->phy_cap_info[0] = + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G; + else + he_cap_elem->phy_cap_info[0] = + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G | + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G; + + he_cap_elem->phy_cap_info[1] = IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD; + he_cap_elem->phy_cap_info[2] = IEEE80211_HE_PHY_CAP2_STBC_TX_UNDER_80MHZ | + IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ; + + he_cap_elem->phy_cap_info[7] = + IEEE80211_HE_PHY_CAP7_HE_SU_MU_PPDU_4XLTF_AND_08_US_GI; + + switch (iftype) { + case NL80211_IFTYPE_AP: + he_cap_elem->mac_cap_info[0] |= IEEE80211_HE_MAC_CAP0_TWT_RES; + he_cap_elem->mac_cap_info[2] |= IEEE80211_HE_MAC_CAP2_BSR; + he_cap_elem->mac_cap_info[4] |= IEEE80211_HE_MAC_CAP4_BQR; + he_cap_elem->mac_cap_info[5] |= + IEEE80211_HE_MAC_CAP5_OM_CTRL_UL_MU_DATA_DIS_RX; + he_cap_elem->phy_cap_info[3] |= + IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_QPSK | + IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_QPSK; + he_cap_elem->phy_cap_info[6] |= + IEEE80211_HE_PHY_CAP6_PARTIAL_BW_EXT_RANGE | + IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT; + he_cap_elem->phy_cap_info[9] |= + IEEE80211_HE_PHY_CAP9_TX_1024_QAM_LESS_THAN_242_TONE_RU | + IEEE80211_HE_PHY_CAP9_RX_1024_QAM_LESS_THAN_242_TONE_RU; + break; + case NL80211_IFTYPE_STATION: + he_cap_elem->mac_cap_info[1] |= + IEEE80211_HE_MAC_CAP1_TF_MAC_PAD_DUR_16US; + + if (band == NL80211_BAND_2GHZ) + he_cap_elem->phy_cap_info[0] |= + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_RU_MAPPING_IN_2G; + else + he_cap_elem->phy_cap_info[0] |= + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_RU_MAPPING_IN_5G; + + he_cap_elem->phy_cap_info[1] |= + IEEE80211_HE_PHY_CAP1_DEVICE_CLASS_A | + IEEE80211_HE_PHY_CAP1_HE_LTF_AND_GI_FOR_HE_PPDUS_0_8US; + he_cap_elem->phy_cap_info[3] |= + IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_QPSK | + IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_QPSK; + he_cap_elem->phy_cap_info[6] |= + IEEE80211_HE_PHY_CAP6_TRIG_CQI_FB | + IEEE80211_HE_PHY_CAP6_PARTIAL_BW_EXT_RANGE | + IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT; + he_cap_elem->phy_cap_info[7] |= + IEEE80211_HE_PHY_CAP7_POWER_BOOST_FACTOR_SUPP; + he_cap_elem->phy_cap_info[8] |= + IEEE80211_HE_PHY_CAP8_20MHZ_IN_40MHZ_HE_PPDU_IN_2G | + IEEE80211_HE_PHY_CAP8_20MHZ_IN_160MHZ_HE_PPDU | + IEEE80211_HE_PHY_CAP8_80MHZ_IN_160MHZ_HE_PPDU | + IEEE80211_HE_PHY_CAP8_DCM_MAX_RU_484; + he_cap_elem->phy_cap_info[9] |= + IEEE80211_HE_PHY_CAP9_LONGER_THAN_16_SIGB_OFDM_SYM | + IEEE80211_HE_PHY_CAP9_NON_TRIGGERED_CQI_FEEDBACK | + IEEE80211_HE_PHY_CAP9_TX_1024_QAM_LESS_THAN_242_TONE_RU | + IEEE80211_HE_PHY_CAP9_RX_1024_QAM_LESS_THAN_242_TONE_RU | + IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_COMP_SIGB | + IEEE80211_HE_PHY_CAP9_RX_FULL_BW_SU_USING_MU_WITH_NON_COMP_SIGB; + break; + default: + break; + } + + he_mcs->rx_mcs_80 = cpu_to_le16(mcs_map); + he_mcs->tx_mcs_80 = cpu_to_le16(mcs_map); + he_mcs->rx_mcs_160 = cpu_to_le16(mcs_map); + he_mcs->tx_mcs_160 = cpu_to_le16(mcs_map); + + mt7996_set_stream_he_txbf_caps(phy, he_cap, iftype, band); + + memset(he_cap->ppe_thres, 0, sizeof(he_cap->ppe_thres)); + if (he_cap_elem->phy_cap_info[6] & + IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT) { + mt76_connac_gen_ppe_thresh(he_cap->ppe_thres, nss, band); + } else { + he_cap_elem->phy_cap_info[9] |= + u8_encode_bits(IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_16US, + IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_MASK); + } + + if (band == NL80211_BAND_6GHZ) { + u16 cap = IEEE80211_HE_6GHZ_CAP_TX_ANTPAT_CONS | + IEEE80211_HE_6GHZ_CAP_RX_ANTPAT_CONS; + + cap |= u16_encode_bits(IEEE80211_HT_MPDU_DENSITY_0_5, + IEEE80211_HE_6GHZ_CAP_MIN_MPDU_START) | + u16_encode_bits(IEEE80211_VHT_MAX_AMPDU_1024K, + IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP) | + u16_encode_bits(IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454, + IEEE80211_HE_6GHZ_CAP_MAX_MPDU_LEN); + + data->he_6ghz_capa.capa = cpu_to_le16(cap); + } +} + +static void +mt7996_init_eht_caps(struct mt7996_phy *phy, enum nl80211_band band, + struct ieee80211_sband_iftype_data *data, + enum nl80211_iftype iftype) +{ + struct ieee80211_sta_eht_cap *eht_cap = &data->eht_cap; + struct ieee80211_eht_cap_elem_fixed *eht_cap_elem = &eht_cap->eht_cap_elem; + struct ieee80211_eht_mcs_nss_supp *eht_nss = &eht_cap->eht_mcs_nss_supp; + int nss = hweight8(phy->mt76->antenna_mask); + int sts = hweight16(phy->mt76->chainmask); + u8 val; + + if (!phy->dev->has_eht) + return; + + eht_cap->has_eht = true; + + eht_cap_elem->mac_cap_info[0] = + IEEE80211_EHT_MAC_CAP0_OM_CONTROL | + u8_encode_bits(IEEE80211_EHT_MAC_CAP0_MAX_MPDU_LEN_11454, + IEEE80211_EHT_MAC_CAP0_MAX_MPDU_LEN_MASK); + + eht_cap_elem->mac_cap_info[1] |= + IEEE80211_EHT_MAC_CAP1_MAX_AMPDU_LEN_MASK; + + eht_cap_elem->phy_cap_info[0] = + IEEE80211_EHT_PHY_CAP0_NDP_4_EHT_LFT_32_GI | + IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMER | + IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMEE; + + /* Set the maximum capability regardless of the antenna configuration. */ + val = is_mt7992(phy->mt76->dev) ? 4 : 3; + eht_cap_elem->phy_cap_info[0] |= + u8_encode_bits(u8_get_bits(val, BIT(0)), + IEEE80211_EHT_PHY_CAP0_BEAMFORMEE_SS_80MHZ_MASK); + + eht_cap_elem->phy_cap_info[1] = + u8_encode_bits(u8_get_bits(val, GENMASK(2, 1)), + IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_80MHZ_MASK); + + eht_cap_elem->phy_cap_info[2] = + u8_encode_bits(sts - 1, IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_80MHZ_MASK); + + if (band != NL80211_BAND_2GHZ) { + eht_cap_elem->phy_cap_info[1] |= + u8_encode_bits(val, + IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_160MHZ_MASK); + + eht_cap_elem->phy_cap_info[2] |= + u8_encode_bits(sts - 1, + IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_160MHZ_MASK); + } + + if (band == NL80211_BAND_6GHZ) { + eht_cap_elem->phy_cap_info[0] |= + IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ; + + eht_cap_elem->phy_cap_info[1] |= + u8_encode_bits(val, + IEEE80211_EHT_PHY_CAP1_BEAMFORMEE_SS_320MHZ_MASK); + + eht_cap_elem->phy_cap_info[2] |= + u8_encode_bits(sts - 1, + IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_320MHZ_MASK); + } + + eht_cap_elem->phy_cap_info[3] = + IEEE80211_EHT_PHY_CAP3_NG_16_SU_FEEDBACK | + IEEE80211_EHT_PHY_CAP3_NG_16_MU_FEEDBACK | + IEEE80211_EHT_PHY_CAP3_CODEBOOK_4_2_SU_FDBK | + IEEE80211_EHT_PHY_CAP3_CODEBOOK_7_5_MU_FDBK; + + eht_cap_elem->phy_cap_info[4] = + IEEE80211_EHT_PHY_CAP4_EHT_MU_PPDU_4_EHT_LTF_08_GI | + u8_encode_bits(min_t(int, sts - 1, 2), + IEEE80211_EHT_PHY_CAP4_MAX_NC_MASK); + + eht_cap_elem->phy_cap_info[5] = + u8_encode_bits(IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_16US, + IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_MASK) | + u8_encode_bits(u8_get_bits(1, GENMASK(1, 0)), + IEEE80211_EHT_PHY_CAP5_MAX_NUM_SUPP_EHT_LTF_MASK); + + eht_cap_elem->phy_cap_info[6] = IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_MASK; + if (band != NL80211_BAND_6GHZ) { + eht_cap_elem->phy_cap_info[6] &= + ~IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_320MHZ; + + if (band != NL80211_BAND_5GHZ) + eht_cap_elem->phy_cap_info[6] &= + ~(IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_160MHZ | + IEEE80211_EHT_PHY_CAP6_MCS15_SUPP_80MHZ); + } + + val = u8_encode_bits(nss, IEEE80211_EHT_MCS_NSS_RX) | + u8_encode_bits(nss, IEEE80211_EHT_MCS_NSS_TX); +#define SET_EHT_MAX_NSS(_bw, _val) do { \ + eht_nss->bw._##_bw.rx_tx_mcs9_max_nss = _val; \ + eht_nss->bw._##_bw.rx_tx_mcs11_max_nss = _val; \ + eht_nss->bw._##_bw.rx_tx_mcs13_max_nss = _val; \ + } while (0) + + SET_EHT_MAX_NSS(80, val); + SET_EHT_MAX_NSS(160, val); + if (band == NL80211_BAND_6GHZ) + SET_EHT_MAX_NSS(320, val); +#undef SET_EHT_MAX_NSS + + if (iftype != NL80211_IFTYPE_AP) + return; + + eht_cap_elem->phy_cap_info[3] |= + IEEE80211_EHT_PHY_CAP3_TRIG_SU_BF_FDBK | + IEEE80211_EHT_PHY_CAP3_TRIG_MU_BF_PART_BW_FDBK; + + eht_cap_elem->phy_cap_info[7] = + IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_80MHZ | + IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_80MHZ; + + if (band == NL80211_BAND_2GHZ) + return; + + eht_cap_elem->phy_cap_info[7] |= + IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_160MHZ | + IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_160MHZ; + + if (band != NL80211_BAND_6GHZ) + return; + + eht_cap_elem->phy_cap_info[7] |= + IEEE80211_EHT_PHY_CAP7_NON_OFDMA_UL_MU_MIMO_320MHZ | + IEEE80211_EHT_PHY_CAP7_MU_BEAMFORMER_320MHZ; +} + +static void +__mt7996_set_stream_he_eht_caps(struct mt7996_phy *phy, + struct ieee80211_supported_band *sband, + enum nl80211_band band) +{ + struct ieee80211_sband_iftype_data *data = phy->iftype[band]; + int i, n = 0; + + for (i = 0; i < NUM_NL80211_IFTYPES; i++) { + switch (i) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_AP: +#ifdef CONFIG_MAC80211_MESH + case NL80211_IFTYPE_MESH_POINT: +#endif + break; + default: + continue; + } + + data[n].types_mask = BIT(i); + mt7996_init_he_caps(phy, band, &data[n], i); + mt7996_init_eht_caps(phy, band, &data[n], i); + + n++; + } + + _ieee80211_set_sband_iftype_data(sband, data, n); +} + +void mt7996_set_stream_he_eht_caps(struct mt7996_phy *phy) +{ + if (phy->mt76->cap.has_2ghz) + __mt7996_set_stream_he_eht_caps(phy, &phy->mt76->sband_2g.sband, + NL80211_BAND_2GHZ); + + if (phy->mt76->cap.has_5ghz) + __mt7996_set_stream_he_eht_caps(phy, &phy->mt76->sband_5g.sband, + NL80211_BAND_5GHZ); + + if (phy->mt76->cap.has_6ghz) + __mt7996_set_stream_he_eht_caps(phy, &phy->mt76->sband_6g.sband, + NL80211_BAND_6GHZ); +} + +int mt7996_register_device(struct mt7996_dev *dev) +{ + struct ieee80211_hw *hw = mt76_hw(dev); + struct mt7996_phy *phy; + int ret; + + dev->phy.dev = dev; + dev->phy.mt76 = &dev->mt76.phy; + dev->mt76.phy.priv = &dev->phy; + INIT_WORK(&dev->rc_work, mt7996_mac_sta_rc_work); + INIT_DELAYED_WORK(&dev->mphy.mac_work, mt7996_mac_work); + INIT_LIST_HEAD(&dev->sta_rc_list); + INIT_LIST_HEAD(&dev->twt_list); + + init_waitqueue_head(&dev->reset_wait); + INIT_WORK(&dev->reset_work, mt7996_mac_reset_work); + INIT_WORK(&dev->dump_work, mt7996_mac_dump_work); + mutex_init(&dev->dump_mutex); + + ret = mt7996_init_hardware(dev); + if (ret) + return ret; + + mt7996_init_wiphy(hw, &dev->mt76.mmio.wed); + + ret = mt7996_register_phy(dev, MT_BAND1); + if (ret) + return ret; + + ret = mt7996_register_phy(dev, MT_BAND2); + if (ret) + return ret; + + ret = mt7996_npu_hw_init(dev); + if (ret) + return ret; + + mt7996_dma_rro_start(dev); + + ret = mt76_register_device(&dev->mt76, true, mt76_rates, + ARRAY_SIZE(mt76_rates)); + if (ret) + return ret; + + mt7996_for_each_phy(dev, phy) + mt7996_thermal_init(phy); + + ieee80211_queue_work(mt76_hw(dev), &dev->init_work); + + dev->recovery.hw_init_done = true; + + ret = mt7996_init_debugfs(dev); + if (ret) + goto error; + + ret = mt7996_coredump_register(dev); + if (ret) + goto error; + + return 0; + +error: + cancel_work_sync(&dev->init_work); + + return ret; +} + +void mt7996_unregister_device(struct mt7996_dev *dev) +{ + cancel_work_sync(&dev->dump_work); + cancel_work_sync(&dev->wed_rro.work); + cancel_work_sync(&dev->reset_work); + cancel_work_sync(&dev->rc_work); + mt7996_unregister_phy(mt7996_phy3(dev)); + mt7996_unregister_phy(mt7996_phy2(dev)); + mt7996_unregister_thermal(&dev->phy); + mt7996_coredump_unregister(dev); + mt76_unregister_device(&dev->mt76); + mt7996_wed_rro_free(dev); + mt7996_mcu_exit(dev); + mt7996_tx_token_put(dev); + mt7996_dma_cleanup(dev); + if (mt7996_has_hwrro(dev) && + !mtk_wed_device_active(&dev->mt76.mmio.wed)) + mt7996_rro_msdu_page_map_free(dev); + tasklet_disable(&dev->mt76.irq_tasklet); + + mt76_free_device(&dev->mt76); +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c new file mode 100644 index 000000000..1cba381a7 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c @@ -0,0 +1,3328 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (C) 2022 MediaTek Inc. + */ + +#include +#include +#include "coredump.h" +#include "mt7996.h" +#include "../dma.h" +#include "mac.h" +#include "mcu.h" + +#define to_rssi(field, rcpi) ((FIELD_GET(field, rcpi) - 220) / 2) + +static struct mt76_wcid *mt7996_rx_get_wcid(struct mt7996_dev *dev, + u16 idx, u8 band_idx) +{ + struct mt7996_sta_link *msta_link; + struct mt7996_sta *msta; + struct mt7996_vif *mvif; + struct mt76_wcid *wcid; + int i; + + wcid = mt76_wcid_ptr(dev, idx); + if (!wcid || !wcid->sta) + return NULL; + + if (!mt7996_band_valid(dev, band_idx)) + return NULL; + + if (wcid->phy_idx == band_idx) + return wcid; + + msta_link = container_of(wcid, struct mt7996_sta_link, wcid); + msta = msta_link->sta; + if (!msta || !msta->vif) + return NULL; + + mvif = msta->vif; + for (i = 0; i < ARRAY_SIZE(mvif->mt76.link); i++) { + struct mt76_vif_link *mlink; + + mlink = rcu_dereference(mvif->mt76.link[i]); + if (!mlink) + continue; + + if (mlink->band_idx != band_idx) + continue; + + msta_link = mt7996_sta_link(msta, i); + break; + } + + return &msta_link->wcid; +} + +bool mt7996_mac_wtbl_update(struct mt7996_dev *dev, int idx, u32 mask) +{ + mt76_rmw(dev, MT_WTBL_UPDATE, MT_WTBL_UPDATE_WLAN_IDX, + FIELD_PREP(MT_WTBL_UPDATE_WLAN_IDX, idx) | mask); + + return mt76_poll(dev, MT_WTBL_UPDATE, MT_WTBL_UPDATE_BUSY, + 0, 5000); +} + +u32 mt7996_mac_wtbl_lmac_addr(struct mt7996_dev *dev, u16 wcid, u8 dw) +{ + mt76_wr(dev, MT_WTBLON_TOP_WDUCR, + FIELD_PREP(MT_WTBLON_TOP_WDUCR_GROUP, (wcid >> 7))); + + return MT_WTBL_LMAC_OFFS(wcid, dw); +} + +static void mt7996_mac_sta_poll(struct mt7996_dev *dev) +{ + static const u8 ac_to_tid[] = { + [IEEE80211_AC_BE] = 0, + [IEEE80211_AC_BK] = 1, + [IEEE80211_AC_VI] = 4, + [IEEE80211_AC_VO] = 6 + }; + struct mt7996_sta_link *msta_link; + struct mt76_vif_link *mlink; + struct ieee80211_sta *sta; + struct mt7996_sta *msta; + u32 tx_time[IEEE80211_NUM_ACS], rx_time[IEEE80211_NUM_ACS]; + LIST_HEAD(sta_poll_list); + struct mt76_wcid *wcid; + int i; + + spin_lock_bh(&dev->mt76.sta_poll_lock); + list_splice_init(&dev->mt76.sta_poll_list, &sta_poll_list); + spin_unlock_bh(&dev->mt76.sta_poll_lock); + + rcu_read_lock(); + + while (true) { + bool clear = false; + u32 addr, val; + u16 idx; + s8 rssi[4]; + + spin_lock_bh(&dev->mt76.sta_poll_lock); + if (list_empty(&sta_poll_list)) { + spin_unlock_bh(&dev->mt76.sta_poll_lock); + break; + } + msta_link = list_first_entry(&sta_poll_list, + struct mt7996_sta_link, + wcid.poll_list); + msta = msta_link->sta; + wcid = &msta_link->wcid; + list_del_init(&wcid->poll_list); + spin_unlock_bh(&dev->mt76.sta_poll_lock); + + idx = wcid->idx; + + /* refresh peer's airtime reporting */ + addr = mt7996_mac_wtbl_lmac_addr(dev, idx, 20); + + for (i = 0; i < IEEE80211_NUM_ACS; i++) { + u32 tx_last = msta_link->airtime_ac[i]; + u32 rx_last = msta_link->airtime_ac[i + 4]; + + msta_link->airtime_ac[i] = mt76_rr(dev, addr); + msta_link->airtime_ac[i + 4] = mt76_rr(dev, addr + 4); + + tx_time[i] = msta_link->airtime_ac[i] - tx_last; + rx_time[i] = msta_link->airtime_ac[i + 4] - rx_last; + + if ((tx_last | rx_last) & BIT(30)) + clear = true; + + addr += 8; + } + + if (clear) { + mt7996_mac_wtbl_update(dev, idx, + MT_WTBL_UPDATE_ADM_COUNT_CLEAR); + memset(msta_link->airtime_ac, 0, + sizeof(msta_link->airtime_ac)); + } + + if (!wcid->sta) + continue; + + sta = container_of((void *)msta, struct ieee80211_sta, + drv_priv); + for (i = 0; i < IEEE80211_NUM_ACS; i++) { + u8 q = mt76_connac_lmac_mapping(i); + u32 tx_cur = tx_time[q]; + u32 rx_cur = rx_time[q]; + u8 tid = ac_to_tid[i]; + + if (!tx_cur && !rx_cur) + continue; + + ieee80211_sta_register_airtime(sta, tid, tx_cur, rx_cur); + } + + /* get signal strength of resp frames (CTS/BA/ACK) */ + addr = mt7996_mac_wtbl_lmac_addr(dev, idx, 34); + val = mt76_rr(dev, addr); + + rssi[0] = to_rssi(GENMASK(7, 0), val); + rssi[1] = to_rssi(GENMASK(15, 8), val); + rssi[2] = to_rssi(GENMASK(23, 16), val); + rssi[3] = to_rssi(GENMASK(31, 24), val); + + mlink = rcu_dereference(msta->vif->mt76.link[wcid->link_id]); + if (mlink) { + struct mt76_phy *mphy = mt76_vif_link_phy(mlink); + + if (mphy) + msta_link->ack_signal = + mt76_rx_signal(mphy->antenna_mask, + rssi); + } + + ewma_avg_signal_add(&msta_link->avg_ack_signal, + -msta_link->ack_signal); + } + + rcu_read_unlock(); +} + +/* The HW does not translate the mac header to 802.3 for mesh point */ +static int mt7996_reverse_frag0_hdr_trans(struct sk_buff *skb, u16 hdr_gap) +{ + struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb; + struct ethhdr *eth_hdr = (struct ethhdr *)(skb->data + hdr_gap); + struct mt7996_sta_link *msta_link = (void *)status->wcid; + struct mt7996_sta *msta = msta_link->sta; + struct ieee80211_bss_conf *link_conf; + __le32 *rxd = (__le32 *)skb->data; + struct ieee80211_sta *sta; + struct ieee80211_vif *vif; + struct ieee80211_hdr hdr; + u16 frame_control; + + if (le32_get_bits(rxd[3], MT_RXD3_NORMAL_ADDR_TYPE) != + MT_RXD3_NORMAL_U2M) + return -EINVAL; + + if (!(le32_to_cpu(rxd[1]) & MT_RXD1_NORMAL_GROUP_4)) + return -EINVAL; + + if (!msta || !msta->vif) + return -EINVAL; + + sta = wcid_to_sta(status->wcid); + vif = container_of((void *)msta->vif, struct ieee80211_vif, drv_priv); + link_conf = rcu_dereference(vif->link_conf[msta_link->wcid.link_id]); + if (!link_conf) + return -EINVAL; + + /* store the info from RXD and ethhdr to avoid being overridden */ + frame_control = le32_get_bits(rxd[8], MT_RXD8_FRAME_CONTROL); + hdr.frame_control = cpu_to_le16(frame_control); + hdr.seq_ctrl = cpu_to_le16(le32_get_bits(rxd[10], MT_RXD10_SEQ_CTRL)); + hdr.duration_id = 0; + + ether_addr_copy(hdr.addr1, vif->addr); + ether_addr_copy(hdr.addr2, sta->addr); + switch (frame_control & (IEEE80211_FCTL_TODS | + IEEE80211_FCTL_FROMDS)) { + case 0: + ether_addr_copy(hdr.addr3, link_conf->bssid); + break; + case IEEE80211_FCTL_FROMDS: + ether_addr_copy(hdr.addr3, eth_hdr->h_source); + break; + case IEEE80211_FCTL_TODS: + ether_addr_copy(hdr.addr3, eth_hdr->h_dest); + break; + case IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS: + ether_addr_copy(hdr.addr3, eth_hdr->h_dest); + ether_addr_copy(hdr.addr4, eth_hdr->h_source); + break; + default: + return -EINVAL; + } + + skb_pull(skb, hdr_gap + sizeof(struct ethhdr) - 2); + if (eth_hdr->h_proto == cpu_to_be16(ETH_P_AARP) || + eth_hdr->h_proto == cpu_to_be16(ETH_P_IPX)) + ether_addr_copy(skb_push(skb, ETH_ALEN), bridge_tunnel_header); + else if (be16_to_cpu(eth_hdr->h_proto) >= ETH_P_802_3_MIN) + ether_addr_copy(skb_push(skb, ETH_ALEN), rfc1042_header); + else + skb_pull(skb, 2); + + if (ieee80211_has_order(hdr.frame_control)) + memcpy(skb_push(skb, IEEE80211_HT_CTL_LEN), &rxd[11], + IEEE80211_HT_CTL_LEN); + if (ieee80211_is_data_qos(hdr.frame_control)) { + __le16 qos_ctrl; + + qos_ctrl = cpu_to_le16(le32_get_bits(rxd[10], MT_RXD10_QOS_CTL)); + memcpy(skb_push(skb, IEEE80211_QOS_CTL_LEN), &qos_ctrl, + IEEE80211_QOS_CTL_LEN); + } + + if (ieee80211_has_a4(hdr.frame_control)) + memcpy(skb_push(skb, sizeof(hdr)), &hdr, sizeof(hdr)); + else + memcpy(skb_push(skb, sizeof(hdr) - 6), &hdr, sizeof(hdr) - 6); + + return 0; +} + +static int +mt7996_mac_fill_rx_rate(struct mt7996_dev *dev, + struct mt76_rx_status *status, + struct ieee80211_supported_band *sband, + __le32 *rxv, u8 *mode) +{ + u32 v0, v2; + u8 stbc, gi, bw, dcm, nss; + int i, idx; + bool cck = false; + + v0 = le32_to_cpu(rxv[0]); + v2 = le32_to_cpu(rxv[2]); + + idx = FIELD_GET(MT_PRXV_TX_RATE, v0); + i = idx; + nss = FIELD_GET(MT_PRXV_NSTS, v0) + 1; + + stbc = FIELD_GET(MT_PRXV_HT_STBC, v2); + gi = FIELD_GET(MT_PRXV_HT_SHORT_GI, v2); + *mode = FIELD_GET(MT_PRXV_TX_MODE, v2); + dcm = FIELD_GET(MT_PRXV_DCM, v2); + bw = FIELD_GET(MT_PRXV_FRAME_MODE, v2); + + /* the hardware reports NSTS; report the data NSS for STBC frames */ + if (stbc && nss > 1) + nss >>= 1; + + switch (*mode) { + case MT_PHY_TYPE_CCK: + cck = true; + fallthrough; + case MT_PHY_TYPE_OFDM: + i = mt76_get_rate(&dev->mt76, sband, i, cck); + break; + case MT_PHY_TYPE_HT_GF: + case MT_PHY_TYPE_HT: + status->encoding = RX_ENC_HT; + if (gi) + status->enc_flags |= RX_ENC_FLAG_SHORT_GI; + if (i > 31) + return -EINVAL; + break; + case MT_PHY_TYPE_VHT: + status->nss = nss; + status->encoding = RX_ENC_VHT; + if (gi) + status->enc_flags |= RX_ENC_FLAG_SHORT_GI; + if (i > 11) + return -EINVAL; + break; + case MT_PHY_TYPE_HE_MU: + case MT_PHY_TYPE_HE_SU: + case MT_PHY_TYPE_HE_EXT_SU: + case MT_PHY_TYPE_HE_TB: + status->nss = nss; + status->encoding = RX_ENC_HE; + i &= GENMASK(3, 0); + + if (gi <= NL80211_RATE_INFO_HE_GI_3_2) + status->he_gi = gi; + + status->he_dcm = dcm; + break; + case MT_PHY_TYPE_EHT_SU: + case MT_PHY_TYPE_EHT_TRIG: + case MT_PHY_TYPE_EHT_MU: + status->nss = nss; + status->encoding = RX_ENC_EHT; + i &= GENMASK(3, 0); + + if (gi <= NL80211_RATE_INFO_EHT_GI_3_2) + status->eht.gi = gi; + break; + default: + return -EINVAL; + } + status->rate_idx = i; + + switch (bw) { + case IEEE80211_STA_RX_BW_20: + break; + case IEEE80211_STA_RX_BW_40: + if (*mode == MT_PHY_TYPE_HE_EXT_SU && + (idx & MT_PRXV_TX_ER_SU_106T)) { + status->bw = RATE_INFO_BW_HE_RU; + status->he_ru = + NL80211_RATE_INFO_HE_RU_ALLOC_106; + } else { + status->bw = RATE_INFO_BW_40; + } + break; + case IEEE80211_STA_RX_BW_80: + status->bw = RATE_INFO_BW_80; + break; + case IEEE80211_STA_RX_BW_160: + status->bw = RATE_INFO_BW_160; + break; + /* rxv reports bw 320-1 and 320-2 separately */ + case IEEE80211_STA_RX_BW_320: + case IEEE80211_STA_RX_BW_320 + 1: + status->bw = RATE_INFO_BW_320; + break; + default: + return -EINVAL; + } + + status->enc_flags |= RX_ENC_FLAG_STBC_MASK * stbc; + if (*mode < MT_PHY_TYPE_HE_SU && gi) + status->enc_flags |= RX_ENC_FLAG_SHORT_GI; + + return 0; +} + +static void +mt7996_wed_check_ppe(struct mt7996_dev *dev, struct mt76_queue *q, + struct mt7996_sta *msta, struct sk_buff *skb, + u32 info) +{ + struct ieee80211_vif *vif; + struct wireless_dev *wdev; + + if (!msta || !msta->vif) + return; + + if (!mt76_queue_is_wed_rx(q)) + return; + + if (!(info & MT_DMA_INFO_PPE_VLD)) + return; + + vif = container_of((void *)msta->vif, struct ieee80211_vif, + drv_priv); + wdev = ieee80211_vif_to_wdev(vif); + skb->dev = wdev->netdev; + + mtk_wed_device_ppe_check(&dev->mt76.mmio.wed, skb, + FIELD_GET(MT_DMA_PPE_CPU_REASON, info), + FIELD_GET(MT_DMA_PPE_ENTRY, info)); +} + +static int +mt7996_mac_fill_rx(struct mt7996_dev *dev, enum mt76_rxq_id q, + struct sk_buff *skb, u32 *info) +{ + struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb; + struct mt76_phy *mphy = &dev->mt76.phy; + struct mt7996_phy *phy = &dev->phy; + struct ieee80211_supported_band *sband; + __le32 *rxd = (__le32 *)skb->data; + __le32 *rxv = NULL; + u32 rxd0 = le32_to_cpu(rxd[0]); + u32 rxd1 = le32_to_cpu(rxd[1]); + u32 rxd2 = le32_to_cpu(rxd[2]); + u32 rxd3 = le32_to_cpu(rxd[3]); + u32 rxd4 = le32_to_cpu(rxd[4]); + u32 csum_mask = MT_RXD3_NORMAL_IP_SUM | MT_RXD3_NORMAL_UDP_TCP_SUM; + u32 csum_status = *(u32 *)skb->cb; + u32 mesh_mask = MT_RXD0_MESH | MT_RXD0_MHCP; + bool is_mesh = (rxd0 & mesh_mask) == mesh_mask; + bool unicast, insert_ccmp_hdr = false; + u8 remove_pad, amsdu_info, band_idx; + u8 mode = 0, qos_ctl = 0; + bool hdr_trans; + u16 hdr_gap; + u16 seq_ctrl = 0; + __le16 fc = 0; + int idx; + u8 hw_aggr = false; + struct mt7996_sta *msta = NULL; + + hw_aggr = status->aggr; + memset(status, 0, sizeof(*status)); + + band_idx = FIELD_GET(MT_RXD1_NORMAL_BAND_IDX, rxd1); + if (!mt7996_band_valid(dev, band_idx)) + return -EINVAL; + + mphy = dev->mt76.phys[band_idx]; + if (!mphy) + return -EINVAL; + + phy = mphy->priv; + status->phy_idx = mphy->band_idx; + + if (!test_bit(MT76_STATE_RUNNING, &mphy->state)) + return -EINVAL; + + if (rxd2 & MT_RXD2_NORMAL_AMSDU_ERR) + return -EINVAL; + + hdr_trans = rxd2 & MT_RXD2_NORMAL_HDR_TRANS; + if (hdr_trans && (rxd1 & MT_RXD1_NORMAL_CM)) + return -EINVAL; + + /* ICV error or CCMP/BIP/WPI MIC error */ + if (rxd1 & MT_RXD1_NORMAL_ICV_ERR) + status->flag |= RX_FLAG_ONLY_MONITOR; + + unicast = FIELD_GET(MT_RXD3_NORMAL_ADDR_TYPE, rxd3) == MT_RXD3_NORMAL_U2M; + idx = FIELD_GET(MT_RXD1_NORMAL_WLAN_IDX, rxd1); + status->wcid = mt7996_rx_get_wcid(dev, idx, band_idx); + + if (status->wcid) { + struct mt7996_sta_link *msta_link; + + msta_link = container_of(status->wcid, struct mt7996_sta_link, + wcid); + msta = msta_link->sta; + mt76_wcid_add_poll(&dev->mt76, &msta_link->wcid); + } + + status->freq = mphy->chandef.chan->center_freq; + status->band = mphy->chandef.chan->band; + if (status->band == NL80211_BAND_5GHZ) + sband = &mphy->sband_5g.sband; + else if (status->band == NL80211_BAND_6GHZ) + sband = &mphy->sband_6g.sband; + else + sband = &mphy->sband_2g.sband; + + if (!sband->channels) + return -EINVAL; + + if ((rxd3 & csum_mask) == csum_mask && + !(csum_status & (BIT(0) | BIT(2) | BIT(3)))) + skb->ip_summed = CHECKSUM_UNNECESSARY; + + if (rxd3 & MT_RXD3_NORMAL_FCS_ERR) + status->flag |= RX_FLAG_FAILED_FCS_CRC; + + if (rxd1 & MT_RXD1_NORMAL_TKIP_MIC_ERR) + status->flag |= RX_FLAG_MMIC_ERROR; + + if (FIELD_GET(MT_RXD2_NORMAL_SEC_MODE, rxd2) != 0 && + !(rxd1 & (MT_RXD1_NORMAL_CLM | MT_RXD1_NORMAL_CM))) { + status->flag |= RX_FLAG_DECRYPTED; + status->flag |= RX_FLAG_IV_STRIPPED; + status->flag |= RX_FLAG_MMIC_STRIPPED | RX_FLAG_MIC_STRIPPED; + } + + remove_pad = FIELD_GET(MT_RXD2_NORMAL_HDR_OFFSET, rxd2); + + if (rxd2 & MT_RXD2_NORMAL_MAX_LEN_ERROR) + return -EINVAL; + + rxd += 8; + if (rxd1 & MT_RXD1_NORMAL_GROUP_4) { + u32 v0 = le32_to_cpu(rxd[0]); + u32 v2 = le32_to_cpu(rxd[2]); + + fc = cpu_to_le16(FIELD_GET(MT_RXD8_FRAME_CONTROL, v0)); + qos_ctl = FIELD_GET(MT_RXD10_QOS_CTL, v2); + seq_ctrl = FIELD_GET(MT_RXD10_SEQ_CTRL, v2); + + rxd += 4; + if ((u8 *)rxd - skb->data >= skb->len) + return -EINVAL; + } + + if (rxd1 & MT_RXD1_NORMAL_GROUP_1) { + u8 *data = (u8 *)rxd; + + if (status->flag & RX_FLAG_DECRYPTED) { + switch (FIELD_GET(MT_RXD2_NORMAL_SEC_MODE, rxd2)) { + case MT_CIPHER_AES_CCMP: + case MT_CIPHER_CCMP_CCX: + case MT_CIPHER_CCMP_256: + insert_ccmp_hdr = + FIELD_GET(MT_RXD2_NORMAL_FRAG, rxd2); + fallthrough; + case MT_CIPHER_TKIP: + case MT_CIPHER_TKIP_NO_MIC: + case MT_CIPHER_GCMP: + case MT_CIPHER_GCMP_256: + status->iv[0] = data[5]; + status->iv[1] = data[4]; + status->iv[2] = data[3]; + status->iv[3] = data[2]; + status->iv[4] = data[1]; + status->iv[5] = data[0]; + break; + default: + break; + } + } + rxd += 4; + if ((u8 *)rxd - skb->data >= skb->len) + return -EINVAL; + } + + if (rxd1 & MT_RXD1_NORMAL_GROUP_2) { + status->timestamp = le32_to_cpu(rxd[0]); + status->flag |= RX_FLAG_MACTIME_START; + + if (!(rxd2 & MT_RXD2_NORMAL_NON_AMPDU)) { + status->flag |= RX_FLAG_AMPDU_DETAILS; + + /* all subframes of an A-MPDU have the same timestamp */ + if (phy->rx_ampdu_ts != status->timestamp) { + if (!++phy->ampdu_ref) + phy->ampdu_ref++; + } + phy->rx_ampdu_ts = status->timestamp; + + status->ampdu_ref = phy->ampdu_ref; + } + + rxd += 4; + if ((u8 *)rxd - skb->data >= skb->len) + return -EINVAL; + } + + /* RXD Group 3 - P-RXV */ + if (rxd1 & MT_RXD1_NORMAL_GROUP_3) { + u32 v3; + int ret; + + rxv = rxd; + rxd += 4; + if ((u8 *)rxd - skb->data >= skb->len) + return -EINVAL; + + v3 = le32_to_cpu(rxv[3]); + + status->chains = mphy->antenna_mask; + status->chain_signal[0] = to_rssi(MT_PRXV_RCPI0, v3); + status->chain_signal[1] = to_rssi(MT_PRXV_RCPI1, v3); + status->chain_signal[2] = to_rssi(MT_PRXV_RCPI2, v3); + status->chain_signal[3] = to_rssi(MT_PRXV_RCPI3, v3); + + /* RXD Group 5 - C-RXV */ + if (rxd1 & MT_RXD1_NORMAL_GROUP_5) { + rxd += 24; + if ((u8 *)rxd - skb->data >= skb->len) + return -EINVAL; + } + + ret = mt7996_mac_fill_rx_rate(dev, status, sband, rxv, &mode); + if (ret < 0) + return ret; + } + + amsdu_info = FIELD_GET(MT_RXD4_NORMAL_PAYLOAD_FORMAT, rxd4); + status->amsdu = !!amsdu_info; + if (status->amsdu) { + status->first_amsdu = amsdu_info == MT_RXD4_FIRST_AMSDU_FRAME; + status->last_amsdu = amsdu_info == MT_RXD4_LAST_AMSDU_FRAME; + } + + /* IEEE 802.11 fragmentation can only be applied to unicast frames. + * Hence, drop fragments with multicast/broadcast RA. + * This check fixes vulnerabilities, like CVE-2020-26145. + */ + if ((ieee80211_has_morefrags(fc) || seq_ctrl & IEEE80211_SCTL_FRAG) && + FIELD_GET(MT_RXD3_NORMAL_ADDR_TYPE, rxd3) != MT_RXD3_NORMAL_U2M) + return -EINVAL; + + hdr_gap = (u8 *)rxd - skb->data + 2 * remove_pad; + if (hdr_trans && ieee80211_has_morefrags(fc)) { + if (mt7996_reverse_frag0_hdr_trans(skb, hdr_gap)) + return -EINVAL; + hdr_trans = false; + } else { + int pad_start = 0; + + skb_pull(skb, hdr_gap); + if (!hdr_trans && status->amsdu && !(ieee80211_has_a4(fc) && is_mesh)) { + pad_start = ieee80211_get_hdrlen_from_skb(skb); + } else if (hdr_trans && (rxd2 & MT_RXD2_NORMAL_HDR_TRANS_ERROR)) { + /* When header translation failure is indicated, + * the hardware will insert an extra 2-byte field + * containing the data length after the protocol + * type field. This happens either when the LLC-SNAP + * pattern did not match, or if a VLAN header was + * detected. + */ + pad_start = 12; + if (get_unaligned_be16(skb->data + pad_start) == ETH_P_8021Q) + pad_start += 4; + else + pad_start = 0; + } + + if (pad_start) { + memmove(skb->data + 2, skb->data, pad_start); + skb_pull(skb, 2); + } + } + + if (!hdr_trans) { + struct ieee80211_hdr *hdr; + + if (insert_ccmp_hdr) { + u8 key_id = FIELD_GET(MT_RXD1_NORMAL_KEY_ID, rxd1); + + mt76_insert_ccmp_hdr(skb, key_id); + } + + hdr = mt76_skb_get_hdr(skb); + fc = hdr->frame_control; + if (ieee80211_is_beacon(fc)) + mt76_rx_beacon(mphy, skb); + if (ieee80211_is_data_qos(fc)) { + u8 *qos = ieee80211_get_qos_ctl(hdr); + + seq_ctrl = le16_to_cpu(hdr->seq_ctrl); + qos_ctl = *qos; + + /* Mesh DA/SA/Length will be stripped after hardware + * de-amsdu, so here needs to clear amsdu present bit + * to mark it as a normal mesh frame. + */ + if (ieee80211_has_a4(fc) && is_mesh && status->amsdu) + *qos &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT; + } + skb_set_mac_header(skb, (unsigned char *)hdr - skb->data); + } else { + status->flag |= RX_FLAG_8023; + mt7996_wed_check_ppe(dev, &dev->mt76.q_rx[q], msta, skb, + *info); + mt76_npu_check_ppe(&dev->mt76, skb, *info); + } + + if (rxv && !(status->flag & RX_FLAG_8023)) { + switch (status->encoding) { + case RX_ENC_EHT: + mt76_connac3_mac_decode_eht_radiotap(skb, rxv, mode); + break; + case RX_ENC_HE: + mt76_connac3_mac_decode_he_radiotap(skb, rxv, mode); + break; + default: + break; + } + } + + if (!status->wcid || !ieee80211_is_data_qos(fc) || hw_aggr) + return 0; + + status->aggr = unicast && + !ieee80211_is_qos_nullfunc(fc); + status->qos_ctl = qos_ctl; + status->seqno = IEEE80211_SEQ_TO_SN(seq_ctrl); + + return 0; +} + +static void +mt7996_mac_write_txwi_8023(struct mt7996_dev *dev, __le32 *txwi, + struct sk_buff *skb, struct mt76_wcid *wcid) +{ + u8 tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK; + u8 fc_type, fc_stype; + u16 ethertype; + bool wmm = false; + u32 val; + + if (wcid->sta) { + struct ieee80211_sta *sta = wcid_to_sta(wcid); + + wmm = sta->wme; + } + + val = FIELD_PREP(MT_TXD1_HDR_FORMAT, MT_HDR_FORMAT_802_3) | + FIELD_PREP(MT_TXD1_TID, tid); + + ethertype = get_unaligned_be16(&skb->data[12]); + if (ethertype >= ETH_P_802_3_MIN) + val |= MT_TXD1_ETH_802_3; + + txwi[1] |= cpu_to_le32(val); + + fc_type = IEEE80211_FTYPE_DATA >> 2; + fc_stype = wmm ? IEEE80211_STYPE_QOS_DATA >> 4 : 0; + + val = FIELD_PREP(MT_TXD2_FRAME_TYPE, fc_type) | + FIELD_PREP(MT_TXD2_SUB_TYPE, fc_stype); + + txwi[2] |= cpu_to_le32(val); + + if (wcid->amsdu) + txwi[3] |= cpu_to_le32(MT_TXD3_HW_AMSDU); +} + +static void +mt7996_mac_write_txwi_80211(struct mt7996_dev *dev, __le32 *txwi, + struct sk_buff *skb, + struct ieee80211_key_conf *key, + struct mt76_wcid *wcid) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + bool multicast = is_multicast_ether_addr(hdr->addr1); + u8 tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK; + __le16 fc = hdr->frame_control, sc = hdr->seq_ctrl; + struct ieee80211_vif *vif = info->control.vif; + u16 seqno = le16_to_cpu(sc); + bool hw_bigtk = false; + u8 fc_type, fc_stype; + u32 val; + + if (ieee80211_is_action(fc) && + skb->len >= IEEE80211_MIN_ACTION_SIZE(action_code) && + mgmt->u.action.category == WLAN_CATEGORY_BACK && + mgmt->u.action.action_code == WLAN_ACTION_ADDBA_REQ) { + if (is_mt7990(&dev->mt76)) + txwi[6] |= cpu_to_le32(FIELD_PREP(MT_TXD6_TID_ADDBA, tid)); + else + txwi[7] |= cpu_to_le32(MT_TXD7_MAC_TXD); + + tid = MT_TX_ADDBA; + } else if (ieee80211_is_mgmt(hdr->frame_control)) { + tid = MT_TX_NORMAL; + } + + val = FIELD_PREP(MT_TXD1_HDR_FORMAT, MT_HDR_FORMAT_802_11) | + FIELD_PREP(MT_TXD1_HDR_INFO, + ieee80211_get_hdrlen_from_skb(skb) / 2) | + FIELD_PREP(MT_TXD1_TID, tid); + + if (!ieee80211_is_data(fc) || multicast || + info->flags & IEEE80211_TX_CTL_USE_MINRATE) + val |= MT_TXD1_FIXED_RATE; + + if (is_mt7990(&dev->mt76) && ieee80211_is_beacon(fc) && + (wcid->hw_key_idx2 == 6 || wcid->hw_key_idx2 == 7)) + hw_bigtk = true; + + if ((key && multicast && ieee80211_is_robust_mgmt_frame(skb)) || hw_bigtk) { + val |= MT_TXD1_BIP; + txwi[3] &= ~cpu_to_le32(MT_TXD3_PROTECT_FRAME); + } + + txwi[1] |= cpu_to_le32(val); + + fc_type = (le16_to_cpu(fc) & IEEE80211_FCTL_FTYPE) >> 2; + fc_stype = (le16_to_cpu(fc) & IEEE80211_FCTL_STYPE) >> 4; + + val = FIELD_PREP(MT_TXD2_FRAME_TYPE, fc_type) | + FIELD_PREP(MT_TXD2_SUB_TYPE, fc_stype); + + if (ieee80211_has_morefrags(fc) && ieee80211_is_first_frag(sc)) + val |= FIELD_PREP(MT_TXD2_FRAG, MT_TX_FRAG_FIRST); + else if (ieee80211_has_morefrags(fc) && !ieee80211_is_first_frag(sc)) + val |= FIELD_PREP(MT_TXD2_FRAG, MT_TX_FRAG_MID); + else if (!ieee80211_has_morefrags(fc) && !ieee80211_is_first_frag(sc)) + val |= FIELD_PREP(MT_TXD2_FRAG, MT_TX_FRAG_LAST); + else + val |= FIELD_PREP(MT_TXD2_FRAG, MT_TX_FRAG_NONE); + + txwi[2] |= cpu_to_le32(val); + + txwi[3] |= cpu_to_le32(FIELD_PREP(MT_TXD3_BCM, multicast)); + if (ieee80211_is_beacon(fc)) { + txwi[3] &= ~cpu_to_le32(MT_TXD3_SW_POWER_MGMT); + txwi[3] |= cpu_to_le32(MT_TXD3_REM_TX_COUNT); + } + + if (multicast && vif && ieee80211_vif_is_mld(vif)) { + val = MT_TXD3_SN_VALID | + FIELD_PREP(MT_TXD3_SEQ, IEEE80211_SEQ_TO_SN(seqno)); + txwi[3] |= cpu_to_le32(val); + } + + if (info->flags & IEEE80211_TX_CTL_INJECTED) { + if (ieee80211_is_back_req(hdr->frame_control)) { + struct ieee80211_bar *bar; + + bar = (struct ieee80211_bar *)skb->data; + seqno = le16_to_cpu(bar->start_seq_num); + } + + val = MT_TXD3_SN_VALID | + FIELD_PREP(MT_TXD3_SEQ, IEEE80211_SEQ_TO_SN(seqno)); + txwi[3] |= cpu_to_le32(val); + txwi[3] &= ~cpu_to_le32(MT_TXD3_HW_AMSDU); + } + + if (vif && ieee80211_vif_is_mld(vif) && + (multicast || unlikely(skb->protocol == cpu_to_be16(ETH_P_PAE)))) + txwi[5] |= cpu_to_le32(MT_TXD5_FL); + + if (ieee80211_is_nullfunc(fc) && ieee80211_has_a4(fc) && + vif && ieee80211_vif_is_mld(vif)) { + txwi[5] |= cpu_to_le32(MT_TXD5_FL); + txwi[6] |= cpu_to_le32(MT_TXD6_DIS_MAT); + } + + if (!wcid->sta && ieee80211_is_mgmt(fc)) + txwi[6] |= cpu_to_le32(MT_TXD6_DIS_MAT); +} + +/* The WLAN_IDX in the TXD and TXP must belong to the primary or secondary + * link of an MLD station; any other link id can make the firmware spin when + * that link is in powersave. Completion events carry the same index, so the + * wcid used for status tracking and accounting must match it + */ +struct mt76_wcid *mt7996_get_tx_wcid(struct mt76_wcid *wcid) +{ + struct mt7996_sta_link *msta_link; + struct mt7996_sta *msta; + + if (!wcid->sta) + return wcid; + + msta_link = container_of(wcid, struct mt7996_sta_link, wcid); + msta = msta_link->sta; + + if (!msta || wcid->link_id == msta->seclink_id || + wcid->link_id == msta->deflink_id) + return wcid; + + msta_link = mt7996_sta_link(msta, msta->deflink_id); + if (msta_link) + return &msta_link->wcid; + + return wcid; +} + +void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi, + struct sk_buff *skb, struct mt76_wcid *wcid, + struct ieee80211_key_conf *key, int pid, + enum mt76_txq_id qid, u32 changed, + unsigned int link_id) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_vif *vif = info->control.vif; + u8 band_idx = (info->hw_queue & MT_TX_HW_QUEUE_PHY) >> 2; + u8 p_fmt, q_idx, omac_idx = 0, wmm_idx = 0; + bool is_8023 = info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP; + struct mt76_vif_link *mlink = NULL; + struct mt7996_vif *mvif; + u16 tx_count = 15; + u32 val; + bool inband_disc = !!(changed & (BSS_CHANGED_UNSOL_BCAST_PROBE_RESP | + BSS_CHANGED_FILS_DISCOVERY)); + bool beacon = !!(changed & (BSS_CHANGED_BEACON | + BSS_CHANGED_BEACON_ENABLED)) && (!inband_disc); + + mvif = vif ? (struct mt7996_vif *)vif->drv_priv : NULL; + if (mvif) { + if (wcid->offchannel) + mlink = rcu_dereference(mvif->mt76.offchannel_link); + if (!mlink && link_id != IEEE80211_LINK_UNSPECIFIED) + mlink = rcu_dereference(mvif->mt76.link[link_id]); + } + + if (mlink) { + omac_idx = mlink->omac_idx; + wmm_idx = mlink->wmm_idx; + band_idx = mlink->band_idx; + } + + if (inband_disc) { + p_fmt = MT_TX_TYPE_FW; + q_idx = MT_LMAC_ALTX0; + } else if (beacon) { + p_fmt = MT_TX_TYPE_FW; + q_idx = MT_LMAC_BCN0; + } else if (qid >= MT_TXQ_PSD) { + p_fmt = MT_TX_TYPE_CT; + q_idx = MT_LMAC_ALTX0; + } else { + p_fmt = MT_TX_TYPE_CT; + q_idx = wmm_idx * MT7996_MAX_WMM_SETS + + mt76_connac_lmac_mapping(skb_get_queue_mapping(skb)); + } + + val = FIELD_PREP(MT_TXD0_TX_BYTES, skb->len + MT_TXD_SIZE) | + FIELD_PREP(MT_TXD0_PKT_FMT, p_fmt) | + FIELD_PREP(MT_TXD0_Q_IDX, q_idx); + txwi[0] = cpu_to_le32(val); + + val = FIELD_PREP(MT_TXD1_WLAN_IDX, wcid->idx) | + FIELD_PREP(MT_TXD1_OWN_MAC, omac_idx); + + if (band_idx) + val |= FIELD_PREP(MT_TXD1_TGID, band_idx); + + txwi[1] = cpu_to_le32(val); + txwi[2] = 0; + + val = MT_TXD3_SW_POWER_MGMT | + FIELD_PREP(MT_TXD3_REM_TX_COUNT, tx_count); + if (key) + val |= MT_TXD3_PROTECT_FRAME; + if (info->flags & IEEE80211_TX_CTL_NO_ACK) + val |= MT_TXD3_NO_ACK; + + txwi[3] = cpu_to_le32(val); + txwi[4] = 0; + + val = FIELD_PREP(MT_TXD5_PID, pid); + if (pid >= MT_PACKET_ID_FIRST) + val |= MT_TXD5_TX_STATUS_HOST; + txwi[5] = cpu_to_le32(val); + + val = MT_TXD6_DAS | MT_TXD6_VTA; + if ((q_idx >= MT_LMAC_ALTX0 && q_idx <= MT_LMAC_BCN0) || + skb->protocol == cpu_to_be16(ETH_P_PAE)) + val |= MT_TXD6_DIS_MAT; + + if (is_mt7996(&dev->mt76)) + val |= FIELD_PREP(MT_TXD6_MSDU_CNT, 1); + else if (is_8023 || !ieee80211_is_mgmt(hdr->frame_control)) + val |= FIELD_PREP(MT_TXD6_MSDU_CNT_V2, 1); + + txwi[6] = cpu_to_le32(val); + txwi[7] = 0; + + if (is_8023) + mt7996_mac_write_txwi_8023(dev, txwi, skb, wcid); + else + mt7996_mac_write_txwi_80211(dev, txwi, skb, key, wcid); + + if (txwi[1] & cpu_to_le32(MT_TXD1_FIXED_RATE)) { + bool mcast = ieee80211_is_data(hdr->frame_control) && + is_multicast_ether_addr(hdr->addr1); + u8 idx = MT7996_BASIC_RATES_TBL; + + if (mlink) { + if (mcast && mlink->mcast_rates_idx) + idx = mlink->mcast_rates_idx; + else if (beacon && mlink->beacon_rates_idx) + idx = mlink->beacon_rates_idx; + else + idx = mlink->basic_rates_idx; + } + + val = FIELD_PREP(MT_TXD6_TX_RATE, idx) | MT_TXD6_FIXED_BW; + if (mcast) + val |= MT_TXD6_DIS_MAT; + txwi[6] |= cpu_to_le32(val); + txwi[3] |= cpu_to_le32(MT_TXD3_BA_DISABLE); + } +} + +static bool +mt7996_tx_use_mgmt(struct mt7996_dev *dev, struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + + if (ieee80211_is_mgmt(hdr->frame_control)) + return true; + + /* for SDO to bypass specific data frame */ + if (!mt7996_has_wa(dev)) { + if (unlikely(skb->protocol == cpu_to_be16(ETH_P_PAE))) + return true; + + if (ieee80211_has_a4(hdr->frame_control) && + !ieee80211_is_data_present(hdr->frame_control)) + return true; + } + + return false; +} + +int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, + enum mt76_txq_id qid, struct mt76_wcid *wcid, + struct ieee80211_sta *sta, + struct mt76_tx_info *tx_info) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx_info->skb->data; + struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76); + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx_info->skb); + struct ieee80211_key_conf *key = info->control.hw_key; + struct ieee80211_vif *vif = info->control.vif; + struct mt7996_vif *mvif = vif ? (struct mt7996_vif *)vif->drv_priv : NULL; + struct mt7996_sta *msta = sta ? (struct mt7996_sta *)sta->drv_priv : NULL; + struct mt76_vif_link *mlink = NULL; + struct mt76_txwi_cache *t; + int id, i, pid, nbuf = tx_info->nbuf - 1; + bool is_8023 = info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP; + __le32 *ptr = (__le32 *)txwi_ptr; + u8 *txwi = (u8 *)txwi_ptr; + u8 link_id; + + if (unlikely(tx_info->skb->len <= ETH_HLEN)) + return -EINVAL; + + if (!wcid) + wcid = &dev->mt76.global_wcid; + + if ((is_8023 || ieee80211_is_data_qos(hdr->frame_control)) && sta->mlo && + likely(tx_info->skb->protocol != cpu_to_be16(ETH_P_PAE))) { + u8 tid = tx_info->skb->priority & IEEE80211_QOS_CTL_TID_MASK; + + link_id = (tid % 2) ? msta->seclink_id : msta->deflink_id; + } else { + link_id = u32_get_bits(info->control.flags, + IEEE80211_TX_CTRL_MLO_LINK); + } + + /* non-MLD frames are LINK_UNSPECIFIED; use the wcid's own link */ + if (link_id == IEEE80211_LINK_UNSPECIFIED && + wcid != &dev->mt76.global_wcid) + link_id = wcid->link_id; + + if (link_id != wcid->link_id && link_id != IEEE80211_LINK_UNSPECIFIED) { + if (msta) { + struct mt7996_sta_link *msta_link = + mt7996_sta_link(msta, link_id); + + if (msta_link) + wcid = &msta_link->wcid; + } else if (mvif) { + mlink = rcu_dereference(mvif->mt76.link[link_id]); + if (mlink && mlink->wcid) + wcid = mlink->wcid; + } + } + + t = (struct mt76_txwi_cache *)(txwi + mdev->drv->txwi_size); + t->skb = tx_info->skb; + + id = mt76_token_consume(mdev, &t); + if (id < 0) + return id; + + /* Since the rules of HW MLD address translation are not fully + * compatible with 802.11 EAPOL frame, we do the translation by + * software + */ + if (tx_info->skb->protocol == cpu_to_be16(ETH_P_PAE) && sta->mlo) { + struct ieee80211_hdr *hdr = (void *)tx_info->skb->data; + struct ieee80211_bss_conf *link_conf; + struct ieee80211_link_sta *link_sta; + + link_conf = rcu_dereference(vif->link_conf[wcid->link_id]); + if (!link_conf) + goto error_release_token; + + link_sta = rcu_dereference(sta->link[wcid->link_id]); + if (!link_sta) + goto error_release_token; + + dma_sync_single_for_cpu(mdev->dma_dev, tx_info->buf[1].addr, + tx_info->buf[1].len, DMA_TO_DEVICE); + + memcpy(hdr->addr1, link_sta->addr, ETH_ALEN); + memcpy(hdr->addr2, link_conf->addr, ETH_ALEN); + if (ieee80211_has_a4(hdr->frame_control)) { + memcpy(hdr->addr3, sta->addr, ETH_ALEN); + memcpy(hdr->addr4, vif->addr, ETH_ALEN); + } else if (ieee80211_has_tods(hdr->frame_control)) { + memcpy(hdr->addr3, sta->addr, ETH_ALEN); + } else if (ieee80211_has_fromds(hdr->frame_control)) { + memcpy(hdr->addr3, vif->addr, ETH_ALEN); + } + + dma_sync_single_for_device(mdev->dma_dev, tx_info->buf[1].addr, + tx_info->buf[1].len, DMA_TO_DEVICE); + } + + wcid = mt7996_get_tx_wcid(wcid); + + pid = mt76_tx_status_skb_add(mdev, wcid, tx_info->skb); + memset(txwi_ptr, 0, MT_TXD_SIZE); + /* Transmit non qos data by 802.11 header and need to fill txd by host*/ + if (!is_8023 || pid >= MT_PACKET_ID_FIRST) + mt7996_mac_write_txwi(dev, txwi_ptr, tx_info->skb, wcid, key, + pid, qid, 0, link_id); + + /* MT7996 and MT7992 require driver to provide the MAC TXP for AddBA + * req + */ + if (le32_to_cpu(ptr[7]) & MT_TXD7_MAC_TXD) { + u32 val, mac_txp_size = sizeof(struct mt76_connac_hw_txp); + + ptr = (__le32 *)(txwi + MT_TXD_SIZE); + memset((void *)ptr, 0, mac_txp_size); + + val = FIELD_PREP(MT_TXP0_TOKEN_ID0, id) | + MT_TXP0_TOKEN_ID0_VALID_MASK; + ptr[0] = cpu_to_le32(val); + + val = FIELD_PREP(MT_TXP1_TID_ADDBA, + tx_info->skb->priority & + IEEE80211_QOS_CTL_TID_MASK); + ptr[1] = cpu_to_le32(val); + ptr[2] = cpu_to_le32(tx_info->buf[1].addr & 0xFFFFFFFF); + + val = FIELD_PREP(MT_TXP_BUF_LEN, tx_info->buf[1].len) | + MT_TXP3_ML0_MASK; +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + val |= FIELD_PREP(MT_TXP3_DMA_ADDR_H, + tx_info->buf[1].addr >> 32); +#endif + ptr[3] = cpu_to_le32(val); + + tx_info->buf[0].len = MT_TXD_SIZE + mac_txp_size; + } else { + struct mt76_connac_txp_common *txp; + + txp = (struct mt76_connac_txp_common *)(txwi + MT_TXD_SIZE); + for (i = 0; i < nbuf; i++) { + u16 len; + + len = FIELD_PREP(MT_TXP_BUF_LEN, tx_info->buf[i + 1].len); +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + len |= FIELD_PREP(MT_TXP_DMA_ADDR_H, + tx_info->buf[i + 1].addr >> 32); +#endif + + txp->fw.buf[i] = cpu_to_le32(tx_info->buf[i + 1].addr); + txp->fw.len[i] = cpu_to_le16(len); + } + txp->fw.nbuf = nbuf; + + txp->fw.flags = cpu_to_le16(MT_CT_INFO_FROM_HOST); + + if (!is_8023 || pid >= MT_PACKET_ID_FIRST) + txp->fw.flags |= cpu_to_le16(MT_CT_INFO_APPLY_TXD); + + if (!key) + txp->fw.flags |= cpu_to_le16(MT_CT_INFO_NONE_CIPHER_FRAME); + + if (!is_8023 && mt7996_tx_use_mgmt(dev, tx_info->skb)) + txp->fw.flags |= cpu_to_le16(MT_CT_INFO_MGMT_FRAME); + + if (mvif) { + if (wcid->offchannel) + mlink = rcu_dereference(mvif->mt76.offchannel_link); + if (!mlink) + mlink = rcu_dereference(mvif->mt76.link[wcid->link_id]); + + txp->fw.bss_idx = mlink ? mlink->idx : mvif->deflink.mt76.idx; + } + + txp->fw.token = cpu_to_le16(id); + txp->fw.rept_wds_wcid = cpu_to_le16(sta ? wcid->idx : 0xfff); + } + + tx_info->skb = NULL; + + /* pass partial skb header to fw */ + tx_info->buf[1].len = MT_CT_PARSE_LEN; + tx_info->buf[1].skip_unmap = true; + tx_info->nbuf = MT_CT_DMA_BUF_NUM; + + return 0; + +error_release_token: + mt76_token_release(mdev, id, NULL); + return -EINVAL; +} + +u32 mt7996_wed_init_buf(void *ptr, dma_addr_t phys, int token_id) +{ + struct mt76_connac_fw_txp *txp = ptr + MT_TXD_SIZE; + __le32 *txwi = ptr; + u32 val; + + memset(ptr, 0, MT_TXD_SIZE + sizeof(*txp)); + + val = FIELD_PREP(MT_TXD0_TX_BYTES, MT_TXD_SIZE) | + FIELD_PREP(MT_TXD0_PKT_FMT, MT_TX_TYPE_CT); + txwi[0] = cpu_to_le32(val); + + val = BIT(31) | + FIELD_PREP(MT_TXD1_HDR_FORMAT, MT_HDR_FORMAT_802_3); + txwi[1] = cpu_to_le32(val); + + txp->token = cpu_to_le16(token_id); + txp->nbuf = 1; + txp->buf[0] = cpu_to_le32(phys + MT_TXD_SIZE + sizeof(*txp)); + + return MT_TXD_SIZE + sizeof(*txp); +} + +static void +mt7996_tx_check_aggr(struct ieee80211_link_sta *link_sta, + struct mt76_wcid *wcid, struct sk_buff *skb) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + bool is_8023 = info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP; + u16 fc, tid; + + if (!(link_sta->ht_cap.ht_supported || link_sta->he_cap.has_he)) + return; + + tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK; + + if (is_8023) { + fc = IEEE80211_FTYPE_DATA | + (link_sta->sta->wme ? IEEE80211_STYPE_QOS_DATA + : IEEE80211_STYPE_DATA); + } else { + /* No need to get precise TID for Action/Management Frame, + * since it will not meet the following Frame Control + * condition anyway. + */ + + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + + fc = le16_to_cpu(hdr->frame_control) & + (IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE); + } + + if (unlikely(fc != (IEEE80211_FTYPE_DATA | IEEE80211_STYPE_QOS_DATA))) + return; + + if (!test_and_set_bit(tid, &wcid->ampdu_state) && + ieee80211_start_tx_ba_session(link_sta->sta, tid, 0)) + clear_bit(tid, &wcid->ampdu_state); +} + +static void +mt7996_txp_skb_unmap(struct mt76_dev *mdev, struct mt76_txwi_cache *t) +{ + u8 *txwi_ptr = mt76_get_txwi_ptr(mdev, t); + __le32 *txwi = (__le32 *)txwi_ptr; + __le32 *txp; + dma_addr_t addr; + u32 val; + + if (!(le32_to_cpu(txwi[7]) & MT_TXD7_MAC_TXD)) { + mt76_connac_txp_skb_unmap(mdev, t); + return; + } + + txp = (__le32 *)(txwi_ptr + MT_TXD_SIZE); + val = le32_to_cpu(txp[3]); + addr = le32_to_cpu(txp[2]); +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + addr |= (dma_addr_t)FIELD_GET(MT_TXP3_DMA_ADDR_H, val) << 32; +#endif + dma_unmap_single(mdev->dma_dev, addr, FIELD_GET(MT_TXP_BUF_LEN, val), + DMA_TO_DEVICE); +} + +static void +mt7996_txwi_free(struct mt7996_dev *dev, struct mt76_txwi_cache *t, + struct ieee80211_link_sta *link_sta, + struct mt76_wcid *wcid, struct list_head *free_list) +{ + struct mt76_dev *mdev = &dev->mt76; + __le32 *txwi; + u16 wcid_idx; + + mt7996_txp_skb_unmap(mdev, t); + if (!t->skb) + goto out; + + txwi = (__le32 *)mt76_get_txwi_ptr(mdev, t); + if (link_sta) { + wcid_idx = wcid->idx; + if (likely(t->skb->protocol != cpu_to_be16(ETH_P_PAE))) { + struct mt7996_sta *msta; + + /* AMPDU state is stored in the primary link */ + msta = (void *)link_sta->sta->drv_priv; + mt7996_tx_check_aggr(link_sta, &msta->deflink.wcid, + t->skb); + } + } else { + wcid_idx = le32_get_bits(txwi[9], MT_TXD9_WLAN_IDX); + } + + __mt76_tx_complete_skb(mdev, wcid_idx, t->skb, free_list); + +out: + t->skb = NULL; + mt76_put_txwi(mdev, t); +} + +static void +mt7996_mac_tx_free(struct mt7996_dev *dev, void *data, int len) +{ + __le32 *tx_free = (__le32 *)data, *cur_info; + struct mt76_dev *mdev = &dev->mt76; + struct mt76_phy *phy2 = mdev->phys[MT_BAND1]; + struct mt76_phy *phy3 = mdev->phys[MT_BAND2]; + struct ieee80211_link_sta *link_sta = NULL; + struct mt76_txwi_cache *txwi; + struct mt76_wcid *wcid = NULL; + LIST_HEAD(free_list); + struct sk_buff *skb, *tmp; + void *end = data + len; + bool wake = false; + u16 total, count = 0; + u8 ver; + + /* clean DMA queues and unmap buffers first */ + mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[MT_TXQ_PSD], false); + mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[MT_TXQ_BE], false); + if (phy2) { + mt76_queue_tx_cleanup(dev, phy2->q_tx[MT_TXQ_PSD], false); + mt76_queue_tx_cleanup(dev, phy2->q_tx[MT_TXQ_BE], false); + } + if (phy3) { + mt76_queue_tx_cleanup(dev, phy3->q_tx[MT_TXQ_PSD], false); + mt76_queue_tx_cleanup(dev, phy3->q_tx[MT_TXQ_BE], false); + } + + ver = le32_get_bits(tx_free[1], MT_TXFREE1_VER); + if (WARN_ON_ONCE(ver < 5)) + return; + + total = le32_get_bits(tx_free[0], MT_TXFREE0_MSDU_CNT); + for (cur_info = &tx_free[2]; count < total; cur_info++) { + u32 msdu, info; + u8 i; + + if (WARN_ON_ONCE((void *)cur_info >= end)) + return; + /* 1'b1: new wcid pair. + * 1'b0: msdu_id with the same 'wcid pair' as above. + */ + info = le32_to_cpu(*cur_info); + if (info & MT_TXFREE_INFO_PAIR) { + struct ieee80211_sta *sta; + unsigned long valid_links; + struct mt7996_sta *msta; + unsigned int id; + u16 idx; + + idx = FIELD_GET(MT_TXFREE_INFO_WLAN_ID, info); + wcid = mt76_wcid_ptr(dev, idx); + sta = wcid_to_sta(wcid); + if (!sta) { + link_sta = NULL; + goto next; + } + + link_sta = rcu_dereference(sta->link[wcid->link_id]); + if (!link_sta) + goto next; + + msta = (struct mt7996_sta *)sta->drv_priv; + valid_links = sta->valid_links ?: BIT(0); + + /* For MLD STA, add all link's wcid to sta_poll_list */ + for_each_set_bit(id, &valid_links, + IEEE80211_MLD_MAX_NUM_LINKS) { + struct mt7996_sta_link *msta_link; + + msta_link = mt7996_sta_link(msta, id); + if (!msta_link) + continue; + + mt76_wcid_add_poll(&dev->mt76, + &msta_link->wcid); + } +next: + /* ver 7 has a new DW with pair = 1, skip it */ + if (ver == 7 && ((void *)(cur_info + 1) < end) && + (le32_to_cpu(*(cur_info + 1)) & MT_TXFREE_INFO_PAIR)) + cur_info++; + continue; + } else if (info & MT_TXFREE_INFO_HEADER) { + u32 tx_retries = 0, tx_failed = 0, count; + + if (!wcid) + continue; + + count = FIELD_GET(MT_TXFREE_INFO_COUNT, info); + tx_retries = count ? count - 1 : 0; + tx_failed = tx_retries + + !!FIELD_GET(MT_TXFREE_INFO_STAT, info); + + wcid->stats.tx_retries += tx_retries; + wcid->stats.tx_failed += tx_failed; + continue; + } + + for (i = 0; i < 2; i++) { + msdu = (info >> (15 * i)) & MT_TXFREE_INFO_MSDU_ID; + if (msdu == MT_TXFREE_INFO_MSDU_ID) + continue; + + count++; + txwi = mt76_token_release(mdev, msdu, &wake); + if (!txwi) + continue; + + mt7996_txwi_free(dev, txwi, link_sta, wcid, + &free_list); + } + } + + mt7996_mac_sta_poll(dev); + + if (wake) + mt76_set_tx_blocked(&dev->mt76, false); + + mt76_worker_schedule(&dev->mt76.tx_worker); + + list_for_each_entry_safe(skb, tmp, &free_list, list) { + skb_list_del_init(skb); + napi_consume_skb(skb, 1); + } +} + +static bool +mt7996_mac_add_txs_skb(struct mt7996_dev *dev, struct mt76_wcid *wcid, + int pid, __le32 *txs_data) +{ + struct mt76_sta_stats *stats = &wcid->stats; + struct ieee80211_supported_band *sband; + struct mt76_dev *mdev = &dev->mt76; + struct mt76_phy *mphy; + struct ieee80211_tx_info *info; + struct sk_buff_head list; + struct rate_info rate = {}; + struct sk_buff *skb = NULL; + bool cck = false; + u32 txrate, txs, mode, stbc; + + txs = le32_to_cpu(txs_data[0]); + + mt76_tx_status_lock(mdev, &list); + + /* only report MPDU TXS */ + if (le32_get_bits(txs_data[0], MT_TXS0_TXS_FORMAT) == 0) { + skb = mt76_tx_status_skb_get(mdev, wcid, pid, &list); + if (skb) { + info = IEEE80211_SKB_CB(skb); + if (!(txs & MT_TXS0_ACK_ERROR_MASK)) + info->flags |= IEEE80211_TX_STAT_ACK; + + info->status.ampdu_len = 1; + info->status.ampdu_ack_len = + !!(info->flags & IEEE80211_TX_STAT_ACK); + + info->status.rates[0].idx = -1; + } + } + + if (mtk_wed_device_active(&dev->mt76.mmio.wed) && wcid->sta) { + struct ieee80211_sta *sta; + u8 tid; + + sta = wcid_to_sta(wcid); + tid = FIELD_GET(MT_TXS0_TID, txs); + ieee80211_refresh_tx_agg_session_timer(sta, tid); + } + + txrate = FIELD_GET(MT_TXS0_TX_RATE, txs); + + rate.mcs = FIELD_GET(MT_TX_RATE_IDX, txrate); + rate.nss = FIELD_GET(MT_TX_RATE_NSS, txrate) + 1; + stbc = le32_get_bits(txs_data[3], MT_TXS3_RATE_STBC); + + if (stbc && rate.nss > 1) + rate.nss >>= 1; + + if (rate.nss - 1 < ARRAY_SIZE(stats->tx_nss)) + stats->tx_nss[rate.nss - 1]++; + if (rate.mcs < ARRAY_SIZE(stats->tx_mcs)) + stats->tx_mcs[rate.mcs]++; + + mode = FIELD_GET(MT_TX_RATE_MODE, txrate); + switch (mode) { + case MT_PHY_TYPE_CCK: + cck = true; + fallthrough; + case MT_PHY_TYPE_OFDM: + mphy = mt76_dev_phy(mdev, wcid->phy_idx); + + if (mphy->chandef.chan->band == NL80211_BAND_5GHZ) + sband = &mphy->sband_5g.sband; + else if (mphy->chandef.chan->band == NL80211_BAND_6GHZ) + sband = &mphy->sband_6g.sband; + else + sband = &mphy->sband_2g.sband; + + rate.mcs = mt76_get_rate(mphy->dev, sband, rate.mcs, cck); + rate.legacy = sband->bitrates[rate.mcs].bitrate; + break; + case MT_PHY_TYPE_HT: + case MT_PHY_TYPE_HT_GF: + if (rate.mcs > 31) + goto out; + + rate.flags = RATE_INFO_FLAGS_MCS; + if (wcid->rate.flags & RATE_INFO_FLAGS_SHORT_GI) + rate.flags |= RATE_INFO_FLAGS_SHORT_GI; + break; + case MT_PHY_TYPE_VHT: + if (rate.mcs > 9) + goto out; + + rate.flags = RATE_INFO_FLAGS_VHT_MCS; + if (wcid->rate.flags & RATE_INFO_FLAGS_SHORT_GI) + rate.flags |= RATE_INFO_FLAGS_SHORT_GI; + break; + case MT_PHY_TYPE_HE_SU: + case MT_PHY_TYPE_HE_EXT_SU: + case MT_PHY_TYPE_HE_TB: + case MT_PHY_TYPE_HE_MU: + if (rate.mcs > 11) + goto out; + + rate.he_gi = wcid->rate.he_gi; + rate.he_dcm = FIELD_GET(MT_TX_RATE_DCM, txrate); + rate.flags = RATE_INFO_FLAGS_HE_MCS; + break; + case MT_PHY_TYPE_EHT_SU: + case MT_PHY_TYPE_EHT_TRIG: + case MT_PHY_TYPE_EHT_MU: + if (rate.mcs > 13) + goto out; + + rate.eht_gi = wcid->rate.eht_gi; + rate.flags = RATE_INFO_FLAGS_EHT_MCS; + break; + default: + goto out; + } + + stats->tx_mode[mode]++; + + switch (FIELD_GET(MT_TXS0_BW, txs)) { + case IEEE80211_STA_RX_BW_320: + rate.bw = RATE_INFO_BW_320; + stats->tx_bw[4]++; + break; + case IEEE80211_STA_RX_BW_160: + rate.bw = RATE_INFO_BW_160; + stats->tx_bw[3]++; + break; + case IEEE80211_STA_RX_BW_80: + rate.bw = RATE_INFO_BW_80; + stats->tx_bw[2]++; + break; + case IEEE80211_STA_RX_BW_40: + rate.bw = RATE_INFO_BW_40; + stats->tx_bw[1]++; + break; + default: + rate.bw = RATE_INFO_BW_20; + stats->tx_bw[0]++; + break; + } + wcid->rate = rate; + +out: + if (skb) + mt76_tx_status_skb_done(mdev, skb, &list); + mt76_tx_status_unlock(mdev, &list); + + return !!skb; +} + +static void mt7996_mac_add_txs(struct mt7996_dev *dev, void *data) +{ + struct mt7996_sta_link *msta_link; + struct mt76_wcid *wcid; + __le32 *txs_data = data; + u16 wcidx; + u8 pid; + + wcidx = le32_get_bits(txs_data[2], MT_TXS2_WCID); + pid = le32_get_bits(txs_data[3], MT_TXS3_PID); + + if (pid < MT_PACKET_ID_NO_SKB) + return; + + rcu_read_lock(); + + wcid = mt76_wcid_ptr(dev, wcidx); + if (!wcid) + goto out; + + mt7996_mac_add_txs_skb(dev, wcid, pid, txs_data); + + if (!wcid->sta) + goto out; + + msta_link = container_of(wcid, struct mt7996_sta_link, wcid); + mt76_wcid_add_poll(&dev->mt76, &msta_link->wcid); + +out: + rcu_read_unlock(); +} + +bool mt7996_rx_check(struct mt76_dev *mdev, void *data, int len) +{ + struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76); + __le32 *rxd = (__le32 *)data; + __le32 *end = (__le32 *)&rxd[len / 4]; + enum rx_pkt_type type; + + type = le32_get_bits(rxd[0], MT_RXD0_PKT_TYPE); + if (type != PKT_TYPE_NORMAL) { + u32 sw_type = le32_get_bits(rxd[0], MT_RXD0_SW_PKT_TYPE_MASK); + + if (unlikely((sw_type & MT_RXD0_SW_PKT_TYPE_MAP) == + MT_RXD0_SW_PKT_TYPE_FRAME)) + return true; + } + + switch (type) { + case PKT_TYPE_TXRX_NOTIFY: + mt7996_mac_tx_free(dev, data, len); + return false; + case PKT_TYPE_TXS: + for (rxd += MT_TXS_HDR_SIZE; rxd + MT_TXS_SIZE <= end; rxd += MT_TXS_SIZE) + mt7996_mac_add_txs(dev, rxd); + return false; + case PKT_TYPE_RX_FW_MONITOR: + mt7996_debugfs_rx_fw_monitor(dev, data, len); + return false; + default: + return true; + } +} + +void mt7996_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, + struct sk_buff *skb, u32 *info) +{ + struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76); + __le32 *rxd = (__le32 *)skb->data; + __le32 *end = (__le32 *)&skb->data[skb->len]; + enum rx_pkt_type type; + + type = le32_get_bits(rxd[0], MT_RXD0_PKT_TYPE); + if (type != PKT_TYPE_NORMAL) { + u32 sw_type = le32_get_bits(rxd[0], MT_RXD0_SW_PKT_TYPE_MASK); + + if (unlikely((sw_type & MT_RXD0_SW_PKT_TYPE_MAP) == + MT_RXD0_SW_PKT_TYPE_FRAME)) + type = PKT_TYPE_NORMAL; + } + + switch (type) { + case PKT_TYPE_TXRX_NOTIFY: + if (mtk_wed_device_active(&dev->mt76.mmio.wed_hif2) && + q == MT_RXQ_TXFREE_BAND2) { + dev_kfree_skb(skb); + break; + } + + mt7996_mac_tx_free(dev, skb->data, skb->len); + napi_consume_skb(skb, 1); + break; + case PKT_TYPE_RX_EVENT: + mt7996_mcu_rx_event(dev, skb); + break; + case PKT_TYPE_TXS: + for (rxd += MT_TXS_HDR_SIZE; rxd + MT_TXS_SIZE <= end; rxd += MT_TXS_SIZE) + mt7996_mac_add_txs(dev, rxd); + dev_kfree_skb(skb); + break; + case PKT_TYPE_RX_FW_MONITOR: + mt7996_debugfs_rx_fw_monitor(dev, skb->data, skb->len); + dev_kfree_skb(skb); + break; + case PKT_TYPE_NORMAL: + if (!mt7996_mac_fill_rx(dev, q, skb, info)) { + mt76_rx(&dev->mt76, q, skb); + return; + } + fallthrough; + default: + dev_kfree_skb(skb); + break; + } +} + +static struct mt7996_msdu_page * +mt7996_msdu_page_get_from_cache(struct mt7996_dev *dev) +{ + struct mt7996_msdu_page *p = NULL; + + spin_lock(&dev->wed_rro.lock); + + if (!list_empty(&dev->wed_rro.page_cache)) { + p = list_first_entry(&dev->wed_rro.page_cache, + struct mt7996_msdu_page, list); + list_del(&p->list); + } + + spin_unlock(&dev->wed_rro.lock); + + return p; +} + +static struct mt7996_msdu_page *mt7996_msdu_page_get(struct mt7996_dev *dev) +{ + struct mt7996_msdu_page *p; + + p = mt7996_msdu_page_get_from_cache(dev); + if (!p) { + p = kzalloc(L1_CACHE_ALIGN(sizeof(*p)), GFP_ATOMIC); + if (p) + INIT_LIST_HEAD(&p->list); + } + + return p; +} + +static void mt7996_msdu_page_put_to_cache(struct mt7996_dev *dev, + struct mt7996_msdu_page *p) +{ + if (p->buf) { + mt76_put_page_pool_buf(p->buf, false); + p->buf = NULL; + } + + spin_lock(&dev->wed_rro.lock); + list_add(&p->list, &dev->wed_rro.page_cache); + spin_unlock(&dev->wed_rro.lock); +} + +static void mt7996_msdu_page_free_cache(struct mt7996_dev *dev) +{ + while (true) { + struct mt7996_msdu_page *p; + + p = mt7996_msdu_page_get_from_cache(dev); + if (!p) + break; + + if (p->buf) + mt76_put_page_pool_buf(p->buf, false); + + kfree(p); + } +} + +static u32 mt7996_msdu_page_hash_from_addr(dma_addr_t dma_addr) +{ + u32 val = 0; + int i = 0; + + while (dma_addr) { + val += (u32)((dma_addr & 0xff) + i) % MT7996_RRO_MSDU_PG_HASH_SIZE; + dma_addr >>= 8; + i += 13; + } + + return val % MT7996_RRO_MSDU_PG_HASH_SIZE; +} + +static struct mt7996_msdu_page * +mt7996_rro_msdu_page_get(struct mt7996_dev *dev, dma_addr_t dma_addr) +{ + u32 hash = mt7996_msdu_page_hash_from_addr(dma_addr); + struct mt7996_msdu_page *p, *tmp, *addr = NULL; + + spin_lock(&dev->wed_rro.lock); + + list_for_each_entry_safe(p, tmp, &dev->wed_rro.page_map[hash], + list) { + if (p->dma_addr == dma_addr) { + list_del(&p->list); + addr = p; + break; + } + } + + spin_unlock(&dev->wed_rro.lock); + + return addr; +} + +static void mt7996_rx_token_put(struct mt7996_dev *dev) +{ + int i; + + for (i = 0; i < dev->mt76.rx_token_size; i++) { + struct mt76_txwi_cache *t; + + t = mt76_rx_token_release(&dev->mt76, i); + if (!t || !t->ptr) + continue; + + mt76_put_page_pool_buf(t->ptr, false); + t->dma_addr = 0; + t->ptr = NULL; + + mt76_put_rxwi(&dev->mt76, t); + } +} + +void mt7996_rro_msdu_page_map_free(struct mt7996_dev *dev) +{ + struct mt7996_msdu_page *p, *tmp; + int i; + + local_bh_disable(); + + for (i = 0; i < ARRAY_SIZE(dev->wed_rro.page_map); i++) { + list_for_each_entry_safe(p, tmp, &dev->wed_rro.page_map[i], + list) { + list_del_init(&p->list); + if (p->buf) + mt76_put_page_pool_buf(p->buf, false); + kfree(p); + } + } + mt7996_msdu_page_free_cache(dev); + + local_bh_enable(); + + mt7996_rx_token_put(dev); +} + +int mt7996_rro_msdu_page_add(struct mt76_dev *mdev, struct mt76_queue *q, + dma_addr_t dma_addr, void *data) +{ + struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76); + struct mt7996_msdu_page_info *pinfo = data; + struct mt7996_msdu_page *p; + u32 hash; + + pinfo->data |= cpu_to_le32(FIELD_PREP(MSDU_PAGE_INFO_OWNER_MASK, 1)); + p = mt7996_msdu_page_get(dev); + if (!p) + return -ENOMEM; + + p->buf = data; + p->dma_addr = dma_addr; + p->q = q; + + hash = mt7996_msdu_page_hash_from_addr(dma_addr); + + spin_lock(&dev->wed_rro.lock); + list_add_tail(&p->list, &dev->wed_rro.page_map[hash]); + spin_unlock(&dev->wed_rro.lock); + + return 0; +} + +static struct mt7996_wed_rro_addr * +mt7996_rro_addr_elem_get(struct mt7996_dev *dev, u16 session_id, u16 seq_num) +{ + u32 idx = 0; + void *addr; + + if (session_id == MT7996_RRO_MAX_SESSION) { + addr = dev->wed_rro.session.ptr; + } else { + idx = session_id / MT7996_RRO_BA_BITMAP_SESSION_SIZE; + addr = dev->wed_rro.addr_elem[idx].ptr; + + idx = session_id % MT7996_RRO_BA_BITMAP_SESSION_SIZE; + idx = idx * MT7996_RRO_WINDOW_MAX_LEN; + } + idx += seq_num % MT7996_RRO_WINDOW_MAX_LEN; + + return addr + idx * sizeof(struct mt7996_wed_rro_addr); +} + +#define MT996_RRO_SN_MASK GENMASK(11, 0) + +void mt7996_rro_rx_process(struct mt76_dev *mdev, void *data) +{ + struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76); + struct mt76_wed_rro_ind *cmd = (struct mt76_wed_rro_ind *)data; + u32 cmd_data0 = le32_to_cpu(cmd->data0); + u32 cmd_data1 = le32_to_cpu(cmd->data1); + u8 ind_reason = FIELD_GET(RRO_IND_DATA0_IND_REASON_MASK, cmd_data0); + u16 start_seq = FIELD_GET(RRO_IND_DATA0_START_SEQ_MASK, cmd_data0); + u16 seq_id = FIELD_GET(RRO_IND_DATA0_SEQ_ID_MASK, cmd_data0); + u16 ind_count = FIELD_GET(RRO_IND_DATA1_IND_COUNT_MASK, cmd_data1); + struct mt7996_msdu_page_info *pinfo = NULL; + struct mt7996_msdu_page *p = NULL; + int i, seq_num = 0; + + for (i = 0; i < ind_count; i++) { + struct mt7996_wed_rro_addr *e; + struct mt76_rx_status *status; + struct mt7996_rro_hif *rxd; + int j, len, qid, data_len; + struct mt76_txwi_cache *t; + dma_addr_t dma_addr = 0; + u16 rx_token_id, count; + struct mt76_queue *q; + struct sk_buff *skb; + u32 info = 0, data; + u8 signature; + void *buf; + bool ls; + + seq_num = FIELD_GET(MT996_RRO_SN_MASK, start_seq + i); + e = mt7996_rro_addr_elem_get(dev, seq_id, seq_num); + data = le32_to_cpu(e->data); + signature = FIELD_GET(WED_RRO_ADDR_SIGNATURE_MASK, data); + if (signature != (seq_num / MT7996_RRO_WINDOW_MAX_LEN)) { + u32 val = FIELD_PREP(WED_RRO_ADDR_SIGNATURE_MASK, + 0xff); + + e->data |= cpu_to_le32(val); + goto update_ack_seq_num; + } + +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + dma_addr = FIELD_GET(WED_RRO_ADDR_HEAD_HIGH_MASK, data); + dma_addr <<= 32; +#endif + dma_addr |= le32_to_cpu(e->head_low); + + count = FIELD_GET(WED_RRO_ADDR_COUNT_MASK, data); + for (j = 0; j < count; j++) { + if (!p) { + p = mt7996_rro_msdu_page_get(dev, dma_addr); + if (!p) + continue; + + dma_sync_single_for_cpu(mdev->dma_dev, p->dma_addr, + SKB_WITH_OVERHEAD(p->q->buf_size), + page_pool_get_dma_dir(p->q->page_pool)); + pinfo = (struct mt7996_msdu_page_info *)p->buf; + } + + rxd = &pinfo->rxd[j % MT7996_MAX_HIF_RXD_IN_PG]; + len = FIELD_GET(RRO_HIF_DATA1_SDL_MASK, + le32_to_cpu(rxd->data1)); + + rx_token_id = FIELD_GET(RRO_HIF_DATA4_RX_TOKEN_ID_MASK, + le32_to_cpu(rxd->data4)); + t = mt76_rx_token_release(mdev, rx_token_id); + if (!t) + goto next_page; + + qid = t->qid; + buf = t->ptr; + q = &mdev->q_rx[qid]; + dma_sync_single_for_cpu(mdev->dma_dev, t->dma_addr, + SKB_WITH_OVERHEAD(q->buf_size), + page_pool_get_dma_dir(q->page_pool)); + + t->dma_addr = 0; + t->ptr = NULL; + mt76_put_rxwi(mdev, t); + if (!buf) + goto next_page; + + if (q->rx_head) + data_len = q->buf_size; + else + data_len = SKB_WITH_OVERHEAD(q->buf_size); + + if (data_len < len + q->buf_offset) { + dev_kfree_skb(q->rx_head); + mt76_put_page_pool_buf(buf, false); + q->rx_head = NULL; + goto next_page; + } + + ls = FIELD_GET(RRO_HIF_DATA1_LS_MASK, + le32_to_cpu(rxd->data1)); + if (q->rx_head) { + /* TODO: Take into account non-linear skb. */ + mt76_put_page_pool_buf(buf, false); + if (ls) { + dev_kfree_skb(q->rx_head); + q->rx_head = NULL; + } + goto next_page; + } + + if (ls && !mt7996_rx_check(mdev, buf, len)) + goto next_page; + + skb = build_skb(buf, q->buf_size); + if (!skb) + goto next_page; + + skb_reserve(skb, q->buf_offset); + skb_mark_for_recycle(skb); + __skb_put(skb, len); + + if (ind_reason == 1 || ind_reason == 2) { + dev_kfree_skb(skb); + goto next_page; + } + + if (!ls) { + q->rx_head = skb; + goto next_page; + } + + status = (struct mt76_rx_status *)skb->cb; + if (seq_id != MT7996_RRO_MAX_SESSION) + status->aggr = true; + + mt7996_queue_rx_skb(mdev, qid, skb, &info); +next_page: + if ((j + 1) % MT7996_MAX_HIF_RXD_IN_PG == 0) { +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + dma_addr = + FIELD_GET(MSDU_PAGE_INFO_PG_HIGH_MASK, + le32_to_cpu(pinfo->data)); + dma_addr <<= 32; + dma_addr |= le32_to_cpu(pinfo->pg_low); +#else + dma_addr = le32_to_cpu(pinfo->pg_low); +#endif + mt7996_msdu_page_put_to_cache(dev, p); + p = NULL; + } + } + +update_ack_seq_num: + if ((i + 1) % 4 == 0) + mt76_wr(dev, MT_RRO_ACK_SN_CTRL, + FIELD_PREP(MT_RRO_ACK_SN_CTRL_SESSION_MASK, + seq_id) | + FIELD_PREP(MT_RRO_ACK_SN_CTRL_SN_MASK, + seq_num)); + if (p) { + mt7996_msdu_page_put_to_cache(dev, p); + p = NULL; + } + } + + /* Update ack_seq_num for remaining addr_elem */ + if (i % 4) + mt76_wr(dev, MT_RRO_ACK_SN_CTRL, + FIELD_PREP(MT_RRO_ACK_SN_CTRL_SESSION_MASK, seq_id) | + FIELD_PREP(MT_RRO_ACK_SN_CTRL_SN_MASK, seq_num)); +} + +void mt7996_mac_cca_stats_reset(struct mt7996_phy *phy) +{ + struct mt7996_dev *dev = phy->dev; + u32 reg = MT_WF_PHYRX_BAND_RX_CTRL1(phy->mt76->band_idx); + + mt76_clear(dev, reg, MT_WF_PHYRX_BAND_RX_CTRL1_STSCNT_EN); + mt76_set(dev, reg, BIT(11) | BIT(9)); +} + +void mt7996_mac_reset_counters(struct mt7996_phy *phy) +{ + struct mt7996_dev *dev = phy->dev; + u8 band_idx = phy->mt76->band_idx; + int i; + + for (i = 0; i < 16; i++) + mt76_rr(dev, MT_TX_AGG_CNT(band_idx, i)); + + phy->mt76->survey_time = ktime_get_boottime(); + + memset(phy->mt76->aggr_stats, 0, sizeof(phy->mt76->aggr_stats)); + + /* reset airtime counters */ + mt76_set(dev, MT_WF_RMAC_MIB_AIRTIME0(band_idx), + MT_WF_RMAC_MIB_RXTIME_CLR); + + mt7996_mcu_get_chan_mib_info(phy, true); +} + +void mt7996_mac_set_coverage_class(struct mt7996_phy *phy) +{ + s16 coverage_class = phy->coverage_class; + struct mt7996_dev *dev = phy->dev; + u32 reg_offset; + u32 cck = FIELD_PREP(MT_TIMEOUT_VAL_PLCP, 231) | + FIELD_PREP(MT_TIMEOUT_VAL_CCA, 48); + u32 ofdm = FIELD_PREP(MT_TIMEOUT_VAL_PLCP, 60) | + FIELD_PREP(MT_TIMEOUT_VAL_CCA, 28); + u8 band_idx = phy->mt76->band_idx; + int offset; + + if (!test_bit(MT76_STATE_RUNNING, &phy->mt76->state)) + return; + + offset = 3 * coverage_class; + reg_offset = FIELD_PREP(MT_TIMEOUT_VAL_PLCP, offset) | + FIELD_PREP(MT_TIMEOUT_VAL_CCA, offset); + + mt76_wr(dev, MT_TMAC_CDTR(band_idx), cck + reg_offset); + mt76_wr(dev, MT_TMAC_ODTR(band_idx), ofdm + reg_offset); +} + +void mt7996_mac_enable_nf(struct mt7996_dev *dev, u8 band) +{ + mt76_set(dev, MT_WF_PHYRX_CSD_BAND_RXTD12(band), + MT_WF_PHYRX_CSD_BAND_RXTD12_IRPI_SW_CLR_ONLY | + MT_WF_PHYRX_CSD_BAND_RXTD12_IRPI_SW_CLR); + + mt76_set(dev, MT_WF_PHYRX_BAND_RX_CTRL1(band), + FIELD_PREP(MT_WF_PHYRX_BAND_RX_CTRL1_IPI_EN, 0x5)); +} + +static u8 +mt7996_phy_get_nf(struct mt7996_phy *phy, u8 band_idx) +{ + static const u8 nf_power[] = { 92, 89, 86, 83, 80, 75, 70, 65, 60, 55, 52 }; + struct mt7996_dev *dev = phy->dev; + u32 val, sum = 0, n = 0; + int ant, i; + + for (ant = 0; ant < hweight8(phy->mt76->antenna_mask); ant++) { + u32 reg = MT_WF_PHYRX_CSD_IRPI(band_idx, ant); + + for (i = 0; i < ARRAY_SIZE(nf_power); i++, reg += 4) { + val = mt76_rr(dev, reg); + sum += val * nf_power[i]; + n += val; + } + } + + return n ? sum / n : 0; +} + +void mt7996_update_channel(struct mt76_phy *mphy) +{ + struct mt7996_phy *phy = mphy->priv; + struct mt76_channel_state *state = mphy->chan_state; + int nf; + + mt7996_mcu_get_chan_mib_info(phy, false); + + nf = mt7996_phy_get_nf(phy, mphy->band_idx); + if (!phy->noise) + phy->noise = nf << 4; + else if (nf) + phy->noise += nf - (phy->noise >> 4); + + state->noise = -(phy->noise >> 4); +} + +static bool +mt7996_wait_reset_state(struct mt7996_dev *dev, u32 state) +{ + bool ret; + + ret = wait_event_timeout(dev->reset_wait, + (READ_ONCE(dev->recovery.state) & state), + MT7996_RESET_TIMEOUT); + + WARN(!ret, "Timeout waiting for MCU reset state %x\n", state); + return ret; +} + +static void +mt7996_update_vif_beacon(void *priv, u8 *mac, struct ieee80211_vif *vif) +{ + struct ieee80211_bss_conf *link_conf; + struct mt7996_phy *phy = priv; + struct mt7996_dev *dev = phy->dev; + unsigned int link_id; + + + switch (vif->type) { + case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_AP: + break; + default: + return; + } + + for_each_vif_active_link(vif, link_conf, link_id) { + struct mt7996_vif_link *link; + struct mt7996_phy *link_phy; + + link = mt7996_vif_link(dev, vif, link_id); + if (!link) + continue; + + link_phy = mt7996_vif_link_phy(link); + if (link_phy != phy) + continue; + + mt7996_mcu_add_beacon(dev->mt76.hw, vif, link_conf, + link_conf->enable_beacon); + } +} + +void mt7996_mac_update_beacons(struct mt7996_phy *phy) +{ + ieee80211_iterate_active_interfaces(phy->mt76->hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7996_update_vif_beacon, phy); +} + +static void +mt7996_update_beacons(struct mt7996_dev *dev) +{ + struct mt76_phy *phy2, *phy3; + + mt7996_mac_update_beacons(&dev->phy); + + phy2 = dev->mt76.phys[MT_BAND1]; + if (phy2) + mt7996_mac_update_beacons(phy2->priv); + + phy3 = dev->mt76.phys[MT_BAND2]; + if (phy3) + mt7996_mac_update_beacons(phy3->priv); +} + +void mt7996_tx_token_put(struct mt7996_dev *dev) +{ + struct mt76_txwi_cache *txwi; + int id; + + spin_lock_bh(&dev->mt76.token_lock); + idr_for_each_entry(&dev->mt76.token, txwi, id) { + mt7996_txwi_free(dev, txwi, NULL, NULL, NULL); + dev->mt76.token_count--; + } + spin_unlock_bh(&dev->mt76.token_lock); + idr_destroy(&dev->mt76.token); + + for (id = 0; id < __MT_MAX_BAND; id++) { + struct mt76_phy *phy = dev->mt76.phys[id]; + if (phy) + atomic_set(&phy->mgmt_tx_pending, 0); + } +} + +static int +mt7996_mac_restart(struct mt7996_dev *dev) +{ + struct mt76_dev *mdev = &dev->mt76; + struct mt7996_phy *phy; + int i, ret; + + if (dev->hif2) { + mt76_wr(dev, MT_INT1_MASK_CSR, 0x0); + mt76_wr(dev, MT_INT1_SOURCE_CSR, ~0); + } + + if (dev_is_pci(mdev->dev)) { + mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0x0); + if (dev->hif2) + mt76_wr(dev, MT_PCIE1_MAC_INT_ENABLE, 0x0); + } + + set_bit(MT76_MCU_RESET, &dev->mphy.state); + mt7996_for_each_phy(dev, phy) + set_bit(MT76_RESET, &phy->mt76->state); + wake_up(&dev->mt76.mcu.wait); + + /* lock/unlock all queues to ensure that no tx is pending */ + mt7996_for_each_phy(dev, phy) + mt76_txq_schedule_all(phy->mt76); + + /* disable all tx/rx napi */ + mt76_worker_disable(&dev->mt76.tx_worker); + mt76_for_each_q_rx(mdev, i) { + if (mtk_wed_device_active(&dev->mt76.mmio.wed) && + mt76_queue_is_wed_rro(&mdev->q_rx[i])) + continue; + + if (mdev->q_rx[i].ndesc) + napi_disable(&dev->mt76.napi[i]); + } + napi_disable(&dev->mt76.tx_napi); + + /* token reinit */ + mt7996_tx_token_put(dev); + idr_init(&dev->mt76.token); + + mt7996_dma_reset(dev, true); + + mt76_for_each_q_rx(mdev, i) { + if (mtk_wed_device_active(&dev->mt76.mmio.wed) && + mt76_queue_is_wed_rro(&mdev->q_rx[i])) + continue; + + if (mdev->q_rx[i].ndesc) { + napi_enable(&dev->mt76.napi[i]); + local_bh_disable(); + napi_schedule(&dev->mt76.napi[i]); + local_bh_enable(); + } + } + clear_bit(MT76_MCU_RESET, &dev->mphy.state); + clear_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state); + + mt76_wr(dev, MT_INT_MASK_CSR, dev->mt76.mmio.irqmask); + mt76_wr(dev, MT_INT_SOURCE_CSR, ~0); + if (dev->hif2) { + mt76_wr(dev, MT_INT1_MASK_CSR, dev->mt76.mmio.irqmask); + mt76_wr(dev, MT_INT1_SOURCE_CSR, ~0); + } + if (dev_is_pci(mdev->dev)) { + mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0xff); + if (dev->hif2) + mt76_wr(dev, MT_PCIE1_MAC_INT_ENABLE, 0xff); + } + + /* load firmware */ + ret = mt7996_mcu_init_firmware(dev); + if (ret) + goto out; + + if (mtk_wed_device_active(&dev->mt76.mmio.wed) && + mt7996_has_hwrro(dev)) { + u32 wed_irq_mask = dev->mt76.mmio.irqmask | + MT_INT_TX_DONE_BAND2; + + mt7996_rro_hw_init(dev); + mt76_for_each_q_rx(&dev->mt76, i) { + if (mt76_queue_is_wed_rro_ind(&dev->mt76.q_rx[i]) || + mt76_queue_is_wed_rro_msdu_pg(&dev->mt76.q_rx[i])) + mt76_queue_rx_reset(dev, i); + } + + mt76_wr(dev, MT_INT_MASK_CSR, wed_irq_mask); + mtk_wed_device_start_hw_rro(&dev->mt76.mmio.wed, wed_irq_mask, + false); + mt7996_irq_enable(dev, wed_irq_mask); + mt7996_irq_disable(dev, 0); + } + + if (mtk_wed_device_active(&dev->mt76.mmio.wed_hif2)) { + mt76_wr(dev, MT_INT_PCIE1_MASK_CSR, + MT_INT_TX_RX_DONE_EXT); + mtk_wed_device_start(&dev->mt76.mmio.wed_hif2, + MT_INT_TX_RX_DONE_EXT); + } + + /* set the necessary init items */ + ret = mt7996_mcu_set_eeprom(dev); + if (ret) + goto out; + + mt7996_mac_init(dev); + mt7996_for_each_phy(dev, phy) + mt7996_init_txpower(phy); + ret = mt7996_txbf_init(dev); + if (ret) + goto out; + + mt7996_for_each_phy(dev, phy) { + if (!test_bit(MT76_STATE_RUNNING, &phy->mt76->state)) + continue; + + ret = mt7996_run(phy); + if (ret) + goto out; + } + +out: + /* reset done */ + mt7996_for_each_phy(dev, phy) + clear_bit(MT76_RESET, &phy->mt76->state); + + napi_enable(&dev->mt76.tx_napi); + local_bh_disable(); + napi_schedule(&dev->mt76.tx_napi); + local_bh_enable(); + + mt76_worker_enable(&dev->mt76.tx_worker); + return ret; +} + +static void +mt7996_mac_reset_sta_iter(void *data, struct ieee80211_sta *sta) +{ + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct mt7996_dev *dev = data; + int i; + + for (i = 0; i < ARRAY_SIZE(msta->link); i++) + mt7996_mac_sta_remove_link(dev, sta, i, true); +} + +static void +mt7996_mac_reset_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif) +{ + struct mt76_vif_link *mlink = (struct mt76_vif_link *)vif->drv_priv; + struct mt76_vif_data *mvif = mlink->mvif; + struct mt7996_dev *dev = data; + int i; + + rcu_read_lock(); + for (i = 0; i < ARRAY_SIZE(mvif->link); i++) { + + mlink = mt76_dereference(mvif->link[i], &dev->mt76); + if (!mlink || mlink == (struct mt76_vif_link *)vif->drv_priv) + continue; + + rcu_assign_pointer(mvif->link[i], NULL); + kfree_rcu(mlink, rcu_head); + } + mvif->valid_links = 0; + rcu_read_unlock(); +} + +static void +mt7996_mac_full_reset(struct mt7996_dev *dev) +{ + struct ieee80211_hw *hw = mt76_hw(dev); + struct mt7996_phy *phy; + LIST_HEAD(list); + int i; + + dev->recovery.hw_full_reset = true; + + set_bit(MT76_MCU_RESET, &dev->mphy.state); + wake_up(&dev->mt76.mcu.wait); + ieee80211_stop_queues(hw); + + cancel_work_sync(&dev->wed_rro.work); + mt7996_for_each_phy(dev, phy) + cancel_delayed_work_sync(&phy->mt76->mac_work); + + mt76_abort_scan(&dev->mt76); + + mutex_lock(&dev->mt76.mutex); + for (i = 0; i < 10; i++) { + if (!mt7996_mac_restart(dev)) + break; + } + + if (i == 10) + dev_err(dev->mt76.dev, "chip full reset failed\n"); + + mt7996_for_each_phy(dev, phy) + phy->omac_mask = 0; + dev->mld_idx_mask = 0; + dev->mld_remap_idx_mask = 0; + + ieee80211_iterate_stations_atomic(hw, mt7996_mac_reset_sta_iter, dev); + mt76_reset_device(&dev->mt76); + ieee80211_iterate_active_interfaces_atomic(hw, + IEEE80211_IFACE_SKIP_SDATA_NOT_IN_DRIVER, + mt7996_mac_reset_vif_iter, dev); + + INIT_LIST_HEAD(&dev->sta_rc_list); + INIT_LIST_HEAD(&dev->twt_list); + + spin_lock_bh(&dev->wed_rro.lock); + list_splice_init(&dev->wed_rro.poll_list, &list); + spin_unlock_bh(&dev->wed_rro.lock); + + while (!list_empty(&list)) { + struct mt7996_wed_rro_session_id *e; + + e = list_first_entry(&list, struct mt7996_wed_rro_session_id, + list); + list_del_init(&e->list); + kfree(e); + } + + i = mt76_wcid_alloc(dev->mt76.wcid_mask, MT7996_WTBL_STA); + dev->mt76.global_wcid.idx = i; + dev->recovery.hw_full_reset = false; + + mutex_unlock(&dev->mt76.mutex); + + ieee80211_wake_queues(mt76_hw(dev)); + + ieee80211_restart_hw(mt76_hw(dev)); +} + +void mt7996_mac_reset_work(struct work_struct *work) +{ + struct ieee80211_hw *hw; + struct mt7996_dev *dev; + struct mt7996_phy *phy; + int i; + + dev = container_of(work, struct mt7996_dev, reset_work); + hw = mt76_hw(dev); + + /* chip full reset */ + if (dev->recovery.restart) { + /* disable WA/WM WDT */ + mt76_clear(dev, MT_WFDMA0_MCU_HOST_INT_ENA, + MT_MCU_CMD_WDT_MASK); + + if (READ_ONCE(dev->recovery.state) & MT_MCU_CMD_WA_WDT) + dev->recovery.wa_reset_count++; + else + dev->recovery.wm_reset_count++; + + mt7996_mac_full_reset(dev); + + /* enable mcu irq */ + mt7996_irq_enable(dev, MT_INT_MCU_CMD); + mt7996_irq_disable(dev, 0); + + /* enable WA/WM WDT */ + mt76_set(dev, MT_WFDMA0_MCU_HOST_INT_ENA, MT_MCU_CMD_WDT_MASK); + + dev->recovery.state = MT_MCU_CMD_NORMAL_STATE; + dev->recovery.restart = false; + return; + } + + if (!(READ_ONCE(dev->recovery.state) & MT_MCU_CMD_STOP_DMA)) + return; + + dev_info(dev->mt76.dev,"\n%s L1 SER recovery start.", + wiphy_name(hw->wiphy)); + + if (mtk_wed_device_active(&dev->mt76.mmio.wed_hif2)) + mtk_wed_device_stop(&dev->mt76.mmio.wed_hif2); + + if (mtk_wed_device_active(&dev->mt76.mmio.wed)) + mtk_wed_device_stop(&dev->mt76.mmio.wed); + + mt7996_npu_hw_stop(dev); + ieee80211_stop_queues(mt76_hw(dev)); + + set_bit(MT76_RESET, &dev->mphy.state); + set_bit(MT76_MCU_RESET, &dev->mphy.state); + wake_up(&dev->mt76.mcu.wait); + mt76_abort_scan(&dev->mt76); + + cancel_work_sync(&dev->wed_rro.work); + mt7996_for_each_phy(dev, phy) { + mt76_abort_roc(phy->mt76); + set_bit(MT76_RESET, &phy->mt76->state); + cancel_delayed_work_sync(&phy->mt76->mac_work); + } + + mutex_lock(&dev->mt76.mutex); + + mt76_worker_disable(&dev->mt76.tx_worker); + mt76_for_each_q_rx(&dev->mt76, i) { + if (mtk_wed_device_active(&dev->mt76.mmio.wed) && + mt76_queue_is_wed_rro(&dev->mt76.q_rx[i])) + continue; + + if (mt76_npu_device_active(&dev->mt76) && + mt76_queue_is_wed_rro(&dev->mt76.q_rx[i])) + continue; + + if (mt76_queue_is_npu_txfree(&dev->mt76.q_rx[i])) + continue; + + napi_disable(&dev->mt76.napi[i]); + } + napi_disable(&dev->mt76.tx_napi); + + mt76_wr(dev, MT_MCU_INT_EVENT, MT_MCU_INT_EVENT_DMA_STOPPED); + + if (mt7996_wait_reset_state(dev, MT_MCU_CMD_RESET_DONE)) { + mt7996_dma_reset(dev, false); + + mt7996_tx_token_put(dev); + idr_init(&dev->mt76.token); + + mt76_wr(dev, MT_MCU_INT_EVENT, MT_MCU_INT_EVENT_DMA_INIT); + mt7996_wait_reset_state(dev, MT_MCU_CMD_RECOVERY_DONE); + } + + mt76_wr(dev, MT_MCU_INT_EVENT, MT_MCU_INT_EVENT_RESET_DONE); + mt7996_wait_reset_state(dev, MT_MCU_CMD_NORMAL_STATE); + + /* enable DMA Rx/Tx and interrupt */ + mt7996_dma_start(dev, false, false); + + if (!is_mt7996(&dev->mt76) && dev->mt76.hwrro_mode == MT76_HWRRO_V3) + mt76_set(dev, MT_RRO_3_0_EMU_CONF, MT_RRO_3_0_EMU_CONF_EN_MASK); + + if (mtk_wed_device_active(&dev->mt76.mmio.wed)) { + u32 wed_irq_mask = MT_INT_TX_DONE_BAND2 | + dev->mt76.mmio.irqmask; + + mt76_wr(dev, MT_INT_MASK_CSR, wed_irq_mask); + mtk_wed_device_start_hw_rro(&dev->mt76.mmio.wed, wed_irq_mask, + true); + mt7996_irq_enable(dev, wed_irq_mask); + mt7996_irq_disable(dev, 0); + } + + if (mtk_wed_device_active(&dev->mt76.mmio.wed_hif2)) { + mt76_wr(dev, MT_INT_PCIE1_MASK_CSR, MT_INT_TX_RX_DONE_EXT); + mtk_wed_device_start(&dev->mt76.mmio.wed_hif2, + MT_INT_TX_RX_DONE_EXT); + } + + __mt7996_npu_hw_init(dev); + + clear_bit(MT76_MCU_RESET, &dev->mphy.state); + mt7996_for_each_phy(dev, phy) + clear_bit(MT76_RESET, &phy->mt76->state); + + mt76_for_each_q_rx(&dev->mt76, i) { + if (mtk_wed_device_active(&dev->mt76.mmio.wed) && + mt76_queue_is_wed_rro(&dev->mt76.q_rx[i])) + continue; + + if (mt76_npu_device_active(&dev->mt76) && + mt76_queue_is_wed_rro(&dev->mt76.q_rx[i])) + continue; + + if (mt76_queue_is_npu_txfree(&dev->mt76.q_rx[i])) + continue; + + napi_enable(&dev->mt76.napi[i]); + local_bh_disable(); + napi_schedule(&dev->mt76.napi[i]); + local_bh_enable(); + } + + tasklet_schedule(&dev->mt76.irq_tasklet); + + mt76_worker_enable(&dev->mt76.tx_worker); + + napi_enable(&dev->mt76.tx_napi); + local_bh_disable(); + napi_schedule(&dev->mt76.tx_napi); + local_bh_enable(); + + ieee80211_wake_queues(hw); + mt7996_update_beacons(dev); + + mutex_unlock(&dev->mt76.mutex); + + mt7996_for_each_phy(dev, phy) + ieee80211_queue_delayed_work(hw, &phy->mt76->mac_work, + MT7996_WATCHDOG_TIME); + dev_info(dev->mt76.dev,"\n%s L1 SER recovery completed.", + wiphy_name(dev->mt76.hw->wiphy)); +} + +/* firmware coredump */ +void mt7996_mac_dump_work(struct work_struct *work) +{ + const struct mt7996_mem_region *mem_region; + struct mt7996_crash_data *crash_data; + struct mt7996_dev *dev; + struct mt7996_mem_hdr *hdr; + size_t buf_len; + int i; + u32 num; + u8 *buf; + + dev = container_of(work, struct mt7996_dev, dump_work); + + mutex_lock(&dev->dump_mutex); + + crash_data = mt7996_coredump_new(dev); + if (!crash_data) { + mutex_unlock(&dev->dump_mutex); + goto skip_coredump; + } + + mem_region = mt7996_coredump_get_mem_layout(dev, &num); + if (!mem_region || !crash_data->memdump_buf_len) { + mutex_unlock(&dev->dump_mutex); + goto skip_memdump; + } + + buf = crash_data->memdump_buf; + buf_len = crash_data->memdump_buf_len; + + /* dumping memory content... */ + memset(buf, 0, buf_len); + for (i = 0; i < num; i++) { + if (mem_region->len > buf_len) { + dev_warn(dev->mt76.dev, "%s len %zu is too large\n", + mem_region->name, mem_region->len); + break; + } + + /* reserve space for the header */ + hdr = (void *)buf; + buf += sizeof(*hdr); + buf_len -= sizeof(*hdr); + + mt7996_memcpy_fromio(dev, buf, mem_region->start, + mem_region->len); + + hdr->start = mem_region->start; + hdr->len = mem_region->len; + + if (!mem_region->len) + /* note: the header remains, just with zero length */ + break; + + buf += mem_region->len; + buf_len -= mem_region->len; + + mem_region++; + } + + mutex_unlock(&dev->dump_mutex); + +skip_memdump: + mt7996_coredump_submit(dev); +skip_coredump: + queue_work(dev->mt76.wq, &dev->reset_work); +} + +void mt7996_reset(struct mt7996_dev *dev) +{ + if (!dev->recovery.hw_init_done) + return; + + if (dev->recovery.hw_full_reset) + return; + + /* wm/wa exception: do full recovery */ + if (READ_ONCE(dev->recovery.state) & MT_MCU_CMD_WDT_MASK) { + dev->recovery.restart = true; + dev_info(dev->mt76.dev, + "%s indicated firmware crash, attempting recovery\n", + wiphy_name(dev->mt76.hw->wiphy)); + + mt7996_irq_disable(dev, MT_INT_MCU_CMD); + queue_work(dev->mt76.wq, &dev->dump_work); + return; + } + + if (READ_ONCE(dev->recovery.state) & MT_MCU_CMD_STOP_DMA) { + set_bit(MT76_MCU_RESET, &dev->mphy.state); + wake_up(&dev->mt76.mcu.wait); + } + + queue_work(dev->mt76.wq, &dev->reset_work); + wake_up(&dev->reset_wait); +} + +void mt7996_mac_update_stats(struct mt7996_phy *phy) +{ + struct mt76_mib_stats *mib = &phy->mib; + struct mt7996_dev *dev = phy->dev; + u8 band_idx = phy->mt76->band_idx; + u32 cnt; + int i; + + cnt = mt76_rr(dev, MT_MIB_RSCR1(band_idx)); + mib->fcs_err_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_RSCR33(band_idx)); + mib->rx_fifo_full_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_RSCR31(band_idx)); + mib->rx_mpdu_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_SDR6(band_idx)); + mib->channel_idle_cnt += FIELD_GET(MT_MIB_SDR6_CHANNEL_IDL_CNT_MASK, cnt); + + cnt = mt76_rr(dev, MT_MIB_RVSR0(band_idx)); + mib->rx_vector_mismatch_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_RSCR35(band_idx)); + mib->rx_delimiter_fail_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_RSCR36(band_idx)); + mib->rx_len_mismatch_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_TSCR0(band_idx)); + mib->tx_ampdu_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_TSCR2(band_idx)); + mib->tx_stop_q_empty_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_TSCR3(band_idx)); + mib->tx_mpdu_attempts_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_TSCR4(band_idx)); + mib->tx_mpdu_success_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_RSCR27(band_idx)); + mib->rx_ampdu_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_RSCR28(band_idx)); + mib->rx_ampdu_bytes_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_RSCR29(band_idx)); + mib->rx_ampdu_valid_subframe_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_RSCR30(band_idx)); + mib->rx_ampdu_valid_subframe_bytes_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_SDR27(band_idx)); + mib->tx_rwp_fail_cnt += FIELD_GET(MT_MIB_SDR27_TX_RWP_FAIL_CNT, cnt); + + cnt = mt76_rr(dev, MT_MIB_SDR28(band_idx)); + mib->tx_rwp_need_cnt += FIELD_GET(MT_MIB_SDR28_TX_RWP_NEED_CNT, cnt); + + cnt = mt76_rr(dev, MT_UMIB_RPDCR(band_idx)); + mib->rx_pfdrop_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_RVSR1(band_idx)); + mib->rx_vec_queue_overflow_drop_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_TSCR1(band_idx)); + mib->rx_ba_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_BSCR0(band_idx)); + mib->tx_bf_ebf_ppdu_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_BSCR1(band_idx)); + mib->tx_bf_ibf_ppdu_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_BSCR2(band_idx)); + mib->tx_mu_bf_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_TSCR5(band_idx)); + mib->tx_mu_mpdu_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_TSCR6(band_idx)); + mib->tx_mu_acked_mpdu_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_TSCR7(band_idx)); + mib->tx_su_acked_mpdu_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_BSCR3(band_idx)); + mib->tx_bf_rx_fb_ht_cnt += cnt; + mib->tx_bf_rx_fb_all_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_BSCR4(band_idx)); + mib->tx_bf_rx_fb_vht_cnt += cnt; + mib->tx_bf_rx_fb_all_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_BSCR5(band_idx)); + mib->tx_bf_rx_fb_he_cnt += cnt; + mib->tx_bf_rx_fb_all_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_BSCR6(band_idx)); + mib->tx_bf_rx_fb_eht_cnt += cnt; + mib->tx_bf_rx_fb_all_cnt += cnt; + + cnt = mt76_rr(dev, MT_ETBF_RX_FB_CONT(band_idx)); + mib->tx_bf_rx_fb_bw = FIELD_GET(MT_ETBF_RX_FB_BW, cnt); + mib->tx_bf_rx_fb_nc_cnt += FIELD_GET(MT_ETBF_RX_FB_NC, cnt); + mib->tx_bf_rx_fb_nr_cnt += FIELD_GET(MT_ETBF_RX_FB_NR, cnt); + + cnt = mt76_rr(dev, MT_MIB_BSCR7(band_idx)); + mib->tx_bf_fb_trig_cnt += cnt; + + cnt = mt76_rr(dev, MT_MIB_BSCR17(band_idx)); + mib->tx_bf_fb_cpl_cnt += cnt; + + for (i = 0; i < ARRAY_SIZE(mib->tx_amsdu); i++) { + cnt = mt76_rr(dev, MT_PLE_AMSDU_PACK_MSDU_CNT(i)); + mib->tx_amsdu[i] += cnt; + mib->tx_amsdu_cnt += cnt; + } + + /* rts count */ + cnt = mt76_rr(dev, MT_MIB_BTSCR5(band_idx)); + mib->rts_cnt += cnt; + + /* rts retry count */ + cnt = mt76_rr(dev, MT_MIB_BTSCR6(band_idx)); + mib->rts_retries_cnt += cnt; + + /* ba miss count */ + cnt = mt76_rr(dev, MT_MIB_BTSCR0(band_idx)); + mib->ba_miss_cnt += cnt; + + /* ack fail count */ + cnt = mt76_rr(dev, MT_MIB_BFTFCR(band_idx)); + mib->ack_fail_cnt += cnt; + + for (i = 0; i < 16; i++) { + cnt = mt76_rr(dev, MT_TX_AGG_CNT(band_idx, i)); + phy->mt76->aggr_stats[i] += cnt; + } +} + +void mt7996_mac_sta_rc_work(struct work_struct *work) +{ + struct mt7996_dev *dev = container_of(work, struct mt7996_dev, rc_work); + struct mt7996_sta_link *msta_link; + struct ieee80211_vif *vif; + struct mt7996_vif *mvif; + LIST_HEAD(list); + u32 changed; + + mutex_lock(&dev->mt76.mutex); + + spin_lock_bh(&dev->mt76.sta_poll_lock); + list_splice_init(&dev->sta_rc_list, &list); + + while (!list_empty(&list)) { + msta_link = list_first_entry(&list, struct mt7996_sta_link, + rc_list); + list_del_init(&msta_link->rc_list); + + changed = msta_link->changed; + msta_link->changed = 0; + mvif = msta_link->sta->vif; + vif = container_of((void *)mvif, struct ieee80211_vif, + drv_priv); + + spin_unlock_bh(&dev->mt76.sta_poll_lock); + + if (changed & (IEEE80211_RC_SUPP_RATES_CHANGED | + IEEE80211_RC_NSS_CHANGED | + IEEE80211_RC_BW_CHANGED)) + mt7996_mcu_add_rate_ctrl(dev, msta_link->sta, vif, + msta_link->wcid.link_id, + true); + + if (changed & IEEE80211_RC_SMPS_CHANGED) + mt7996_mcu_set_fixed_field(dev, msta_link->sta, NULL, + msta_link->wcid.link_id, + RATE_PARAM_MMPS_UPDATE); + + spin_lock_bh(&dev->mt76.sta_poll_lock); + } + + spin_unlock_bh(&dev->mt76.sta_poll_lock); + + mutex_unlock(&dev->mt76.mutex); +} + +void mt7996_mac_work(struct work_struct *work) +{ + struct mt7996_phy *phy; + struct mt76_phy *mphy; + + mphy = (struct mt76_phy *)container_of(work, struct mt76_phy, + mac_work.work); + phy = mphy->priv; + + mutex_lock(&mphy->dev->mutex); + + mt76_update_survey(mphy); + if (++mphy->mac_work_count == 5) { + mphy->mac_work_count = 0; + + mt7996_mac_update_stats(phy); + + mt7996_mcu_get_all_sta_info(phy, UNI_ALL_STA_TXRX_RATE); + if (mtk_wed_device_active(&phy->dev->mt76.mmio.wed)) { + mt7996_mcu_get_all_sta_info(phy, UNI_ALL_STA_TXRX_ADM_STAT); + mt7996_mcu_get_all_sta_info(phy, UNI_ALL_STA_TXRX_MSDU_COUNT); + } + } + + mutex_unlock(&mphy->dev->mutex); + + mt76_beacon_mon_check(mphy); + mt76_tx_status_check(mphy->dev, false); + + ieee80211_queue_delayed_work(mphy->hw, &mphy->mac_work, + MT7996_WATCHDOG_TIME); +} + +static void mt7996_dfs_stop_radar_detector(struct mt7996_phy *phy) +{ + struct mt7996_dev *dev = phy->dev; + int rdd_idx = mt7996_get_rdd_idx(phy, false); + + if (rdd_idx < 0) + return; + + mt7996_mcu_rdd_cmd(dev, RDD_STOP, rdd_idx, 0); +} + +static int mt7996_dfs_start_rdd(struct mt7996_dev *dev, int rdd_idx) +{ + int region; + + switch (dev->mt76.region) { + case NL80211_DFS_ETSI: + region = 0; + break; + case NL80211_DFS_JP: + region = 2; + break; + case NL80211_DFS_FCC: + default: + region = 1; + break; + } + + return mt7996_mcu_rdd_cmd(dev, RDD_START, rdd_idx, region); +} + +static int mt7996_dfs_start_radar_detector(struct mt7996_phy *phy) +{ + struct mt7996_dev *dev = phy->dev; + int err, rdd_idx; + + rdd_idx = mt7996_get_rdd_idx(phy, false); + if (rdd_idx < 0) + return -EINVAL; + + /* start CAC */ + err = mt7996_mcu_rdd_cmd(dev, RDD_CAC_START, rdd_idx, 0); + if (err < 0) + return err; + + err = mt7996_dfs_start_rdd(dev, rdd_idx); + + return err; +} + +int mt7996_dfs_init_radar_detector(struct mt7996_phy *phy) +{ + struct mt7996_dev *dev = phy->dev; + enum mt76_dfs_state dfs_state, prev_state; + int err, rdd_idx = mt7996_get_rdd_idx(phy, false); + + prev_state = phy->mt76->dfs_state; + dfs_state = mt76_phy_dfs_state(phy->mt76); + + if (prev_state == dfs_state || rdd_idx < 0) + return 0; + + if (prev_state == MT_DFS_STATE_UNKNOWN) + mt7996_dfs_stop_radar_detector(phy); + + if (dfs_state == MT_DFS_STATE_DISABLED) + goto stop; + + if (prev_state <= MT_DFS_STATE_DISABLED) { + err = mt7996_dfs_start_radar_detector(phy); + if (err < 0) + return err; + + phy->mt76->dfs_state = MT_DFS_STATE_CAC; + } + + if (dfs_state == MT_DFS_STATE_CAC) + return 0; + + err = mt7996_mcu_rdd_cmd(dev, RDD_CAC_END, rdd_idx, 0); + if (err < 0) { + phy->mt76->dfs_state = MT_DFS_STATE_UNKNOWN; + return err; + } + + phy->mt76->dfs_state = MT_DFS_STATE_ACTIVE; + return 0; + +stop: + err = mt7996_mcu_rdd_cmd(dev, RDD_NORMAL_START, rdd_idx, 0); + if (err < 0) + return err; + + mt7996_dfs_stop_radar_detector(phy); + phy->mt76->dfs_state = MT_DFS_STATE_DISABLED; + + return 0; +} + +static int +mt7996_mac_twt_duration_align(int duration) +{ + return duration << 8; +} + +static u64 +mt7996_mac_twt_sched_list_add(struct mt7996_dev *dev, + struct mt7996_twt_flow *flow) +{ + struct mt7996_twt_flow *iter, *iter_next; + u32 duration = flow->duration << 8; + u64 start_tsf; + + iter = list_first_entry_or_null(&dev->twt_list, + struct mt7996_twt_flow, list); + if (!iter || !iter->sched || iter->start_tsf > duration) { + /* add flow as first entry in the list */ + list_add(&flow->list, &dev->twt_list); + return 0; + } + + list_for_each_entry_safe(iter, iter_next, &dev->twt_list, list) { + start_tsf = iter->start_tsf + + mt7996_mac_twt_duration_align(iter->duration); + if (list_is_last(&iter->list, &dev->twt_list)) + break; + + if (!iter_next->sched || + iter_next->start_tsf > start_tsf + duration) { + list_add(&flow->list, &iter->list); + goto out; + } + } + + /* add flow as last entry in the list */ + list_add_tail(&flow->list, &dev->twt_list); +out: + return start_tsf; +} + +static int mt7996_mac_check_twt_req(struct ieee80211_twt_setup *twt) +{ + struct ieee80211_twt_params *twt_agrt; + u64 interval, duration; + u16 mantissa; + u8 exp; + + /* only individual agreement supported */ + if (twt->control & IEEE80211_TWT_CONTROL_NEG_TYPE_BROADCAST) + return -EOPNOTSUPP; + + /* only 256us unit supported */ + if (twt->control & IEEE80211_TWT_CONTROL_WAKE_DUR_UNIT) + return -EOPNOTSUPP; + + twt_agrt = (struct ieee80211_twt_params *)twt->params; + + /* explicit agreement not supported */ + if (!(twt_agrt->req_type & cpu_to_le16(IEEE80211_TWT_REQTYPE_IMPLICIT))) + return -EOPNOTSUPP; + + exp = FIELD_GET(IEEE80211_TWT_REQTYPE_WAKE_INT_EXP, + le16_to_cpu(twt_agrt->req_type)); + mantissa = le16_to_cpu(twt_agrt->mantissa); + duration = twt_agrt->min_twt_dur << 8; + + interval = (u64)mantissa << exp; + if (interval < duration) + return -EOPNOTSUPP; + + return 0; +} + +static bool +mt7996_mac_twt_param_equal(struct mt7996_sta_link *msta_link, + struct ieee80211_twt_params *twt_agrt) +{ + u16 type = le16_to_cpu(twt_agrt->req_type); + u8 exp; + int i; + + exp = FIELD_GET(IEEE80211_TWT_REQTYPE_WAKE_INT_EXP, type); + for (i = 0; i < MT7996_MAX_STA_TWT_AGRT; i++) { + struct mt7996_twt_flow *f; + + if (!(msta_link->twt.flowid_mask & BIT(i))) + continue; + + f = &msta_link->twt.flow[i]; + if (f->duration == twt_agrt->min_twt_dur && + f->mantissa == twt_agrt->mantissa && + f->exp == exp && + f->protection == !!(type & IEEE80211_TWT_REQTYPE_PROTECTION) && + f->flowtype == !!(type & IEEE80211_TWT_REQTYPE_FLOWTYPE) && + f->trigger == !!(type & IEEE80211_TWT_REQTYPE_TRIGGER)) + return true; + } + + return false; +} + +void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw, + struct ieee80211_sta *sta, + struct ieee80211_twt_setup *twt) +{ + enum ieee80211_twt_setup_cmd setup_cmd = TWT_SETUP_CMD_REJECT; + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct ieee80211_twt_params *twt_agrt = (void *)twt->params; + struct mt7996_sta_link *msta_link = &msta->deflink; + u16 req_type = le16_to_cpu(twt_agrt->req_type); + enum ieee80211_twt_setup_cmd sta_setup_cmd; + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mt7996_twt_flow *flow; + u8 flowid, table_id, exp; + + /* the firmware does not support iTWT agreements with MLD peers, and + * driver TWT state is only tracked on the default link + */ + if (sta->mlo) + goto out; + + if (mt7996_mac_check_twt_req(twt)) + goto out; + + mutex_lock(&dev->mt76.mutex); + + if (dev->twt.n_agrt == MT7996_MAX_TWT_AGRT) + goto unlock; + + if (hweight8(msta_link->twt.flowid_mask) == + ARRAY_SIZE(msta_link->twt.flow)) + goto unlock; + + if (twt_agrt->min_twt_dur < MT7996_MIN_TWT_DUR) { + setup_cmd = TWT_SETUP_CMD_DICTATE; + twt_agrt->min_twt_dur = MT7996_MIN_TWT_DUR; + goto unlock; + } + + if (mt7996_mac_twt_param_equal(msta_link, twt_agrt)) + goto unlock; + + flowid = ffs(~msta_link->twt.flowid_mask) - 1; + twt_agrt->req_type &= ~cpu_to_le16(IEEE80211_TWT_REQTYPE_FLOWID); + twt_agrt->req_type |= le16_encode_bits(flowid, + IEEE80211_TWT_REQTYPE_FLOWID); + + table_id = ffs(~dev->twt.table_mask) - 1; + exp = FIELD_GET(IEEE80211_TWT_REQTYPE_WAKE_INT_EXP, req_type); + sta_setup_cmd = FIELD_GET(IEEE80211_TWT_REQTYPE_SETUP_CMD, req_type); + + flow = &msta_link->twt.flow[flowid]; + memset(flow, 0, sizeof(*flow)); + INIT_LIST_HEAD(&flow->list); + flow->wcid = msta_link->wcid.idx; + flow->table_id = table_id; + flow->id = flowid; + flow->duration = twt_agrt->min_twt_dur; + flow->mantissa = twt_agrt->mantissa; + flow->exp = exp; + flow->protection = !!(req_type & IEEE80211_TWT_REQTYPE_PROTECTION); + flow->flowtype = !!(req_type & IEEE80211_TWT_REQTYPE_FLOWTYPE); + flow->trigger = !!(req_type & IEEE80211_TWT_REQTYPE_TRIGGER); + + if (sta_setup_cmd == TWT_SETUP_CMD_REQUEST || + sta_setup_cmd == TWT_SETUP_CMD_SUGGEST) { + u64 interval = (u64)le16_to_cpu(twt_agrt->mantissa) << exp; + u64 flow_tsf, curr_tsf; + u32 rem; + + flow->sched = true; + flow->start_tsf = mt7996_mac_twt_sched_list_add(dev, flow); + curr_tsf = __mt7996_get_tsf(hw, &msta->vif->deflink); + div_u64_rem(curr_tsf - flow->start_tsf, interval, &rem); + flow_tsf = curr_tsf + interval - rem; + twt_agrt->twt = cpu_to_le64(flow_tsf); + } else { + list_add_tail(&flow->list, &dev->twt_list); + } + flow->tsf = le64_to_cpu(twt_agrt->twt); + + if (mt7996_mcu_twt_agrt_update(dev, &msta->vif->deflink, flow, + MCU_TWT_AGRT_ADD)) + goto unlock; + + setup_cmd = TWT_SETUP_CMD_ACCEPT; + dev->twt.table_mask |= BIT(table_id); + msta_link->twt.flowid_mask |= BIT(flowid); + dev->twt.n_agrt++; + +unlock: + mutex_unlock(&dev->mt76.mutex); +out: + twt_agrt->req_type &= ~cpu_to_le16(IEEE80211_TWT_REQTYPE_SETUP_CMD); + twt_agrt->req_type |= + le16_encode_bits(setup_cmd, IEEE80211_TWT_REQTYPE_SETUP_CMD); + twt->control = twt->control & IEEE80211_TWT_CONTROL_RX_DISABLED; +} + +void mt7996_mac_twt_teardown_flow(struct mt7996_dev *dev, + struct mt7996_vif_link *link, + struct mt7996_sta_link *msta_link, + u8 flowid) +{ + struct mt7996_twt_flow *flow; + + lockdep_assert_held(&dev->mt76.mutex); + + if (flowid >= ARRAY_SIZE(msta_link->twt.flow)) + return; + + if (!(msta_link->twt.flowid_mask & BIT(flowid))) + return; + + flow = &msta_link->twt.flow[flowid]; + if (mt7996_mcu_twt_agrt_update(dev, link, flow, MCU_TWT_AGRT_DELETE)) + return; + + list_del_init(&flow->list); + msta_link->twt.flowid_mask &= ~BIT(flowid); + dev->twt.table_mask &= ~BIT(flow->table_id); + dev->twt.n_agrt--; +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.h b/drivers/net/wireless/mediatek/mt76/mt7996/mac.h new file mode 100644 index 000000000..70ee30f32 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (C) 2022 MediaTek Inc. + */ + +#ifndef __MT7996_MAC_H +#define __MT7996_MAC_H + +#include "../mt76_connac3_mac.h" + +struct mt7996_dfs_pulse { + u32 max_width; /* us */ + int max_pwr; /* dbm */ + int min_pwr; /* dbm */ + u32 min_stgr_pri; /* us */ + u32 max_stgr_pri; /* us */ + u32 min_cr_pri; /* us */ + u32 max_cr_pri; /* us */ +}; + +struct mt7996_dfs_pattern { + u8 enb; + u8 stgr; + u8 min_crpn; + u8 max_crpn; + u8 min_crpr; + u8 min_pw; + u32 min_pri; + u32 max_pri; + u8 max_pw; + u8 min_crbn; + u8 max_crbn; + u8 min_stgpn; + u8 max_stgpn; + u8 min_stgpr; + u8 rsv[2]; + u32 min_stgpr_diff; +} __packed; + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c new file mode 100644 index 000000000..e218856b0 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -0,0 +1,2607 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (C) 2022 MediaTek Inc. + */ + +#include "mt7996.h" +#include "mcu.h" +#include "mac.h" + +int mt7996_run(struct mt7996_phy *phy) +{ + struct mt7996_dev *dev = phy->dev; + int ret; + + mt7996_mac_enable_nf(dev, phy->mt76->band_idx); + + ret = mt7996_mcu_set_rts_thresh(phy, 0x92b); + if (ret) + return ret; + + ret = mt7996_mcu_set_radio_en(phy, true); + if (ret) + return ret; + + ret = mt7996_mcu_set_chan_info(phy, UNI_CHANNEL_RX_PATH); + if (ret) + return ret; + + ret = mt7996_mcu_set_thermal_throttling(phy, MT7996_THERMAL_THROTTLE_MAX); + if (ret) + return ret; + + ret = mt7996_mcu_set_thermal_protect(phy, true); + if (ret) + return ret; + + set_bit(MT76_STATE_RUNNING, &phy->mt76->state); + + ieee80211_queue_delayed_work(dev->mphy.hw, &phy->mt76->mac_work, + MT7996_WATCHDOG_TIME); + + if (!phy->counter_reset) { + mt7996_mac_reset_counters(phy); + phy->counter_reset = true; + } + + return 0; +} + +static int mt7996_start(struct ieee80211_hw *hw) +{ + struct mt7996_dev *dev = mt7996_hw_dev(hw); + int ret; + + flush_work(&dev->init_work); + + mutex_lock(&dev->mt76.mutex); + ret = mt7996_mcu_set_hdr_trans(dev, true); + if (!ret && !is_mt7996(&dev->mt76)) { + u8 queue = mt76_connac_lmac_mapping(IEEE80211_AC_VI); + + ret = mt7996_mcu_cp_support(dev, queue); + } + mutex_unlock(&dev->mt76.mutex); + + return ret; +} + +static void mt7996_stop_phy(struct mt7996_phy *phy) +{ + struct mt7996_dev *dev; + + if (!phy || !test_bit(MT76_STATE_RUNNING, &phy->mt76->state)) + return; + + dev = phy->dev; + + cancel_delayed_work_sync(&phy->mt76->mac_work); + + mutex_lock(&dev->mt76.mutex); + + mt7996_mcu_rdd_resume_tx(phy); + mt7996_mcu_set_radio_en(phy, false); + + clear_bit(MT76_STATE_RUNNING, &phy->mt76->state); + + mutex_unlock(&dev->mt76.mutex); +} + +static void mt7996_stop(struct ieee80211_hw *hw, bool suspend) +{ +} + +static inline int get_free_idx(u64 mask, u8 start, u8 end) +{ + if (~mask & GENMASK_ULL(end, start)) + return __ffs64(~mask & GENMASK_ULL(end, start)) + 1; + return 0; +} + +static int get_omac_idx(enum nl80211_iftype type, u64 mask) +{ + int i; + + switch (type) { + case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_STATION: + /* prefer hw bssid slot 1-3 */ + i = get_free_idx(mask, HW_BSSID_1, HW_BSSID_3); + if (i) + return i - 1; + + if (type != NL80211_IFTYPE_STATION) + break; + + i = get_free_idx(mask, EXT_BSSID_1, EXT_BSSID_MAX); + if (i) + return i - 1; + + if (~mask & BIT(HW_BSSID_0)) + return HW_BSSID_0; + + break; + case NL80211_IFTYPE_MONITOR: + case NL80211_IFTYPE_AP: + /* ap uses hw bssid 0 and ext bssid */ + if (~mask & BIT(HW_BSSID_0)) + return HW_BSSID_0; + + i = get_free_idx(mask, EXT_BSSID_1, EXT_BSSID_MAX); + if (i) + return i - 1; + + break; + default: + WARN_ON(1); + break; + } + + return -1; +} + +static int get_own_mld_idx(u64 mask, bool group_mld) +{ + u8 start = group_mld ? 0 : 16; + u8 end = group_mld ? 15 : 63; + int idx; + + idx = get_free_idx(mask, start, end); + if (idx) + return idx - 1; + + /* If the 16-63 range is not available, perform another lookup in the + * range 0-15 + */ + if (!group_mld) { + idx = get_free_idx(mask, 0, 15); + if (idx) + return idx - 1; + } + + return -EINVAL; +} + +static void +mt7996_init_bitrate_mask(struct ieee80211_vif *vif, struct mt7996_vif_link *mlink) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(mlink->bitrate_mask.control); i++) { + mlink->bitrate_mask.control[i].gi = NL80211_TXRATE_DEFAULT_GI; + mlink->bitrate_mask.control[i].he_gi = 0xff; + mlink->bitrate_mask.control[i].he_ltf = 0xff; + mlink->bitrate_mask.control[i].legacy = GENMASK(31, 0); + memset(mlink->bitrate_mask.control[i].ht_mcs, 0xff, + sizeof(mlink->bitrate_mask.control[i].ht_mcs)); + memset(mlink->bitrate_mask.control[i].vht_mcs, 0xff, + sizeof(mlink->bitrate_mask.control[i].vht_mcs)); + memset(mlink->bitrate_mask.control[i].he_mcs, 0xff, + sizeof(mlink->bitrate_mask.control[i].he_mcs)); + } +} + +static int +mt7996_set_hw_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + unsigned int link_id, struct ieee80211_key_conf *key) +{ + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct ieee80211_bss_conf *link_conf; + struct mt7996_sta_link *msta_link; + struct mt7996_vif_link *link; + int idx = key->keyidx; + u8 *wcid_keyidx; + bool is_bigtk; + int err; + + link = mt7996_vif_link(dev, vif, link_id); + if (!link) + return 0; + + if (!mt7996_vif_link_phy(link)) + return 0; + + if (sta) { + struct mt7996_sta *msta; + + msta = (struct mt7996_sta *)sta->drv_priv; + msta_link = mt7996_sta_link_protected(dev, msta, link_id); + if (!msta_link) + return 0; + + if (!msta_link->wcid.sta) + return -EOPNOTSUPP; + } else { + msta_link = &link->msta_link; + } + wcid_keyidx = &msta_link->wcid.hw_key_idx; + + is_bigtk = key->keyidx == 6 || key->keyidx == 7; + switch (key->cipher) { + case WLAN_CIPHER_SUITE_AES_CMAC: + case WLAN_CIPHER_SUITE_BIP_CMAC_256: + case WLAN_CIPHER_SUITE_BIP_GMAC_128: + case WLAN_CIPHER_SUITE_BIP_GMAC_256: + if (is_bigtk) { + wcid_keyidx = &msta_link->wcid.hw_key_idx2; + key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIE; + } + break; + default: + break; + } + + link_conf = link_conf_dereference_protected(vif, link_id); + if (!link_conf) + link_conf = &vif->bss_conf; + + if (cmd == SET_KEY && !sta && !link->mt76.cipher) { + struct mt7996_phy *phy = mt7996_vif_link_phy(link); + + link->mt76.cipher = + mt76_connac_mcu_get_cipher(key->cipher); + if (phy) + mt7996_mcu_add_bss_info(phy, vif, link_conf, + &link->mt76, msta_link, true); + } + + if (cmd == SET_KEY) + *wcid_keyidx = idx; + else if (idx == *wcid_keyidx) + *wcid_keyidx = -1; + + /* only do remove key for BIGTK */ + if (cmd != SET_KEY && !is_bigtk) + return 0; + + mt76_wcid_key_setup(&dev->mt76, &msta_link->wcid, key); + + err = mt7996_mcu_add_key(&dev->mt76, link, key, + MCU_WMWA_UNI_CMD(STA_REC_UPDATE), + &msta_link->wcid, cmd); + + /* remove and add beacon in order to enable beacon protection */ + if (cmd == SET_KEY && is_bigtk && link_conf->enable_beacon) { + mt7996_mcu_add_beacon(hw, vif, link_conf, false); + mt7996_mcu_add_beacon(hw, vif, link_conf, true); + } + + return err; +} + +struct mt7996_key_iter_data { + enum set_key_cmd cmd; + unsigned int link_id; +}; + +static void +mt7996_key_iter(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, struct ieee80211_key_conf *key, + void *data) +{ + struct mt7996_key_iter_data *it = data; + + if (sta) + return; + + WARN_ON(mt7996_set_hw_key(hw, it->cmd, vif, NULL, it->link_id, key)); +} + +int mt7996_vif_link_add(struct mt76_phy *mphy, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, + struct mt76_vif_link *mlink) +{ + struct mt7996_vif_link *link = container_of(mlink, struct mt7996_vif_link, mt76); + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct mt7996_sta_link *msta_link = &link->msta_link; + struct mt7996_phy *phy = mphy->priv; + struct mt7996_dev *dev = phy->dev; + u8 band_idx = phy->mt76->band_idx; + struct mt7996_key_iter_data it = { + .cmd = SET_KEY, + .link_id = link_conf->link_id, + }; + int mld_idx, idx, ret; + + if ((mvif->mt76.valid_links & BIT(link_conf->link_id)) && + !mlink->offchannel) { + if (vif->type == NL80211_IFTYPE_AP) + return mt7996_mcu_mld_link_oper(dev, link_conf, link, + true); + return 0; + } + + mlink->idx = __ffs64(~dev->mt76.vif_mask); + if (mlink->idx >= mt7996_max_interface_num(dev)) + return -ENOSPC; + + idx = get_omac_idx(vif->type, phy->omac_mask); + if (idx < 0) + return -ENOSPC; + + mld_idx = get_own_mld_idx(dev->mld_idx_mask, false); + if (mld_idx < 0) + return -ENOSPC; + + link->mld_idx = mld_idx; + mlink->omac_idx = idx; + mlink->band_idx = band_idx; + mlink->wmm_idx = vif->type == NL80211_IFTYPE_AP ? 0 : 3; + mlink->wcid = &msta_link->wcid; + mlink->wcid->offchannel = mlink->offchannel; + + ret = mt7996_mcu_add_dev_info(phy, vif, link_conf, mlink, true); + if (ret) + return ret; + + dev->mt76.vif_mask |= BIT_ULL(mlink->idx); + dev->mld_idx_mask |= BIT_ULL(link->mld_idx); + phy->omac_mask |= BIT_ULL(mlink->omac_idx); + + idx = MT7996_WTBL_RESERVED - mlink->idx; + + INIT_LIST_HEAD(&msta_link->rc_list); + msta_link->wcid.idx = idx; + msta_link->wcid.link_id = link_conf->link_id; + msta_link->wcid.link_valid = ieee80211_vif_is_mld(vif); + msta_link->wcid.tx_info |= MT_WCID_TX_INFO_SET; + mt76_wcid_init(&msta_link->wcid, band_idx); + + mt7996_mac_wtbl_update(dev, idx, + MT_WTBL_UPDATE_ADM_COUNT_CLEAR); + + if (vif->type != NL80211_IFTYPE_AP && + (!mlink->omac_idx || mlink->omac_idx > 3)) + vif->offload_flags = 0; + + if (phy->mt76->chandef.chan->band != NL80211_BAND_2GHZ) + mlink->basic_rates_idx = MT7996_BASIC_RATES_TBL + 4; + else + mlink->basic_rates_idx = MT7996_BASIC_RATES_TBL; + + mt7996_init_bitrate_mask(vif, link); + + mt7996_mcu_add_bss_info(phy, vif, link_conf, mlink, msta_link, true); + /* defer the first STA_REC of BMC entry to BSS_CHANGED_BSSID for STA + * interface, since firmware only records BSSID when the entry is new + */ + if (vif->type != NL80211_IFTYPE_STATION) + mt7996_mcu_add_sta(dev, link_conf, NULL, link, NULL, + CONN_STATE_PORT_SECURE, true); + rcu_assign_pointer(dev->mt76.wcid[idx], &msta_link->wcid); + + if (!mlink->wcid->offchannel) + ieee80211_iter_keys(mphy->hw, vif, mt7996_key_iter, &it); + + if (!mlink->wcid->offchannel) { + if (vif->txq && + mvif->mt76.deflink_id == IEEE80211_LINK_UNSPECIFIED) { + struct mt76_txq *mtxq; + + mtxq = (struct mt76_txq *)vif->txq->drv_priv; + mvif->mt76.deflink_id = link_conf->link_id; + mtxq->wcid = idx; + } + mvif->mt76.valid_links |= BIT(link_conf->link_id); + } + + if (vif->type == NL80211_IFTYPE_STATION) { + vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER; + + if (vif->cfg.assoc && link_conf->beacon_int) { + mlink->beacon_mon_interval = + msecs_to_jiffies(ieee80211_tu_to_usec( + link_conf->beacon_int) / 1000); + WRITE_ONCE(mlink->beacon_mon_last, jiffies); + } + } + + return 0; +} + +static void mt7996_vif_link_destroy(struct mt7996_phy *phy, + struct mt7996_vif_link *link, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf) +{ + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct mt7996_sta_link *msta_link = &link->msta_link; + unsigned int link_id = msta_link->wcid.link_id; + struct mt76_vif_link *mlink = &link->mt76; + struct mt7996_key_iter_data it = { + .cmd = SET_KEY, + .link_id = link_id, + }; + struct mt7996_dev *dev = phy->dev; + int idx = msta_link->wcid.idx; + + if (!link_conf) + link_conf = &vif->bss_conf; + + if (!mlink->wcid->offchannel) + ieee80211_iter_keys(phy->mt76->hw, vif, mt7996_key_iter, &it); + + mt7996_mcu_add_sta(dev, link_conf, NULL, link, NULL, + CONN_STATE_DISCONNECT, false); + mt7996_mcu_add_bss_info(phy, vif, link_conf, mlink, msta_link, false); + mt7996_mcu_add_dev_info(phy, vif, link_conf, mlink, false); + + rcu_assign_pointer(dev->mt76.wcid[idx], NULL); + + dev->mt76.vif_mask &= ~BIT_ULL(mlink->idx); + dev->mld_idx_mask &= ~BIT_ULL(link->mld_idx); + phy->omac_mask &= ~BIT_ULL(mlink->omac_idx); + if (!mlink->wcid->offchannel) + mvif->mt76.valid_links &= ~BIT(link_id); + + spin_lock_bh(&dev->mt76.sta_poll_lock); + if (!list_empty(&msta_link->wcid.poll_list)) + list_del_init(&msta_link->wcid.poll_list); + spin_unlock_bh(&dev->mt76.sta_poll_lock); + + mt76_wcid_cleanup(&dev->mt76, &msta_link->wcid); + + if (mlink != (struct mt76_vif_link *)vif->drv_priv && + !mlink->wcid->offchannel) { + rcu_assign_pointer(mlink->mvif->link[link_id], NULL); + kfree_rcu(mlink, rcu_head); + } +} + +void mt7996_vif_link_remove(struct mt76_phy *mphy, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, + struct mt76_vif_link *mlink) +{ + struct mt7996_vif_link *link = container_of(mlink, struct mt7996_vif_link, mt76); + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct mt7996_sta_link *msta_link = &link->msta_link; + unsigned int link_id = msta_link->wcid.link_id; + struct mt7996_phy *phy = mphy->priv; + + /* Hw requires to destroy active links tearing down the interface, so + * postpone it removing the interface. + */ + if (mlink->wcid->offchannel) { + mt7996_vif_link_destroy(phy, link, vif, link_conf); + } else { + if (vif->type == NL80211_IFTYPE_AP) { + mt7996_mcu_mld_reconf_stop_link(phy->dev, vif, + BIT(link_id)); + mt7996_mcu_mld_link_oper(phy->dev, link_conf, link, + false); + } + + if (vif->txq && mvif->mt76.deflink_id == link_id) { + struct ieee80211_bss_conf *iter; + struct mt76_txq *mtxq; + + mvif->mt76.deflink_id = IEEE80211_LINK_UNSPECIFIED; + mtxq = (struct mt76_txq *)vif->txq->drv_priv; + /* Primary link will be removed, look for a new one */ + for_each_vif_active_link(vif, iter, link_id) { + if (link_id == msta_link->wcid.link_id) + continue; + + link = mt7996_vif_link(phy->dev, vif, link_id); + if (!link) + continue; + + mtxq->wcid = link->msta_link.wcid.idx; + mvif->mt76.deflink_id = link_id; + break; + } + } + } +} + +static void mt7996_phy_set_rxfilter(struct mt7996_phy *phy) +{ + struct mt7996_dev *dev = phy->dev; + u32 ctl_flags = MT_WF_RFCR1_DROP_ACK | + MT_WF_RFCR1_DROP_BF_POLL | + MT_WF_RFCR1_DROP_BA | + MT_WF_RFCR1_DROP_CFEND | + MT_WF_RFCR1_DROP_CFACK; + u32 filter = phy->rxfilter; + + if (filter & MT_WF_RFCR_DROP_OTHER_UC) { + filter |= MT_WF_RFCR_DROP_CTS | + MT_WF_RFCR_DROP_RTS | + MT_WF_RFCR_DROP_CTL_RSV | + MT_WF_RFCR_DROP_FCSFAIL; + } + + mt76_wr(dev, MT_WF_RFCR(phy->mt76->band_idx), filter); + if (filter & MT_WF_RFCR_DROP_CTL_RSV) + mt76_set(dev, MT_WF_RFCR1(phy->mt76->band_idx), ctl_flags); + else + mt76_clear(dev, MT_WF_RFCR1(phy->mt76->band_idx), ctl_flags); +} + +static void mt7996_set_monitor(struct mt7996_phy *phy, bool enabled) +{ + struct mt7996_dev *dev; + + if (!phy) + return; + + dev = phy->dev; + + if (enabled == !(phy->rxfilter & MT_WF_RFCR_DROP_OTHER_UC)) + return; + + if (!enabled) + phy->rxfilter |= MT_WF_RFCR_DROP_OTHER_UC; + else + phy->rxfilter &= ~MT_WF_RFCR_DROP_OTHER_UC; + + mt76_rmw_field(dev, MT_DMA_DCR0(phy->mt76->band_idx), + MT_DMA_DCR0_RXD_G5_EN, enabled); + mt76_rmw_field(dev, MT_MDP_DCR0, + MT_MDP_DCR0_RX_HDR_TRANS_EN, !enabled); + mt7996_phy_set_rxfilter(phy); + mt7996_mcu_set_sniffer_mode(phy, enabled); +} + +static int mt7996_add_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct wireless_dev *wdev = ieee80211_vif_to_wdev(vif); + struct mt7996_dev *dev = mt7996_hw_dev(hw); + int i, err = 0; + + mutex_lock(&dev->mt76.mutex); + + for (i = 0; i < MT7996_MAX_RADIOS; i++) { + struct mt7996_phy *phy = dev->radio_phy[i]; + + if (!phy || !(wdev->radio_mask & BIT(i)) || + test_bit(MT76_STATE_RUNNING, &phy->mt76->state)) + continue; + + err = mt7996_run(phy); + if (err) + goto out; + + if (vif->type == NL80211_IFTYPE_MONITOR) + mt7996_set_monitor(phy, true); + } + + mt76_vif_init(vif, &mvif->mt76); + + vif->offload_flags |= IEEE80211_OFFLOAD_ENCAP_4ADDR; + mvif->mt76.deflink_id = IEEE80211_LINK_UNSPECIFIED; + +out: + mutex_unlock(&dev->mt76.mutex); + + return err; +} + +struct mt7996_radio_data { + u32 active_mask; + u32 monitor_mask; +}; + +static void mt7996_remove_iter(void *data, u8 *mac, struct ieee80211_vif *vif) +{ + struct wireless_dev *wdev = ieee80211_vif_to_wdev(vif); + struct mt7996_radio_data *rdata = data; + + rdata->active_mask |= wdev->radio_mask; + if (vif->type == NL80211_IFTYPE_MONITOR) + rdata->monitor_mask |= wdev->radio_mask; +} + +static void mt7996_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + unsigned long rem_links = mvif->mt76.valid_links; + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mt7996_radio_data rdata = {}; + unsigned int link_id; + int i; + + mutex_lock(&dev->mt76.mutex); + + /* Remove all active links */ + for_each_set_bit(link_id, &rem_links, IEEE80211_MLD_MAX_NUM_LINKS) { + struct mt7996_vif_link *link; + struct mt7996_phy *phy; + + link = mt7996_vif_link(dev, vif, link_id); + if (!link) + continue; + + phy = __mt7996_phy(dev, link->msta_link.wcid.phy_idx); + if (!phy) + continue; + + mt7996_vif_link_destroy(phy, link, vif, NULL); + } + + mutex_unlock(&dev->mt76.mutex); + + ieee80211_iterate_active_interfaces_mtx(hw, 0, mt7996_remove_iter, + &rdata); + mt76_vif_cleanup(&dev->mt76, vif); + + for (i = 0; i < MT7996_MAX_RADIOS; i++) { + struct mt7996_phy *phy = dev->radio_phy[i]; + + if (!phy) + continue; + if (!(rdata.monitor_mask & BIT(i))) + mt7996_set_monitor(phy, false); + if (!(rdata.active_mask & BIT(i))) + mt7996_stop_phy(phy); + } +} + +int mt7996_set_channel(struct mt76_phy *mphy) +{ + struct mt7996_phy *phy = mphy->priv; + int ret; + + if (mphy->offchannel) + mt7996_mac_update_beacons(phy); + + ret = mt7996_mcu_set_chan_info(phy, UNI_CHANNEL_SWITCH); + if (ret) + goto out; + + ret = mt7996_mcu_set_chan_info(phy, UNI_CHANNEL_RX_PATH); + if (ret) + goto out; + + ret = mt7996_mcu_set_txpower_sku(phy); + if (ret) + goto out; + + ret = mt7996_dfs_init_radar_detector(phy); + mt7996_mac_cca_stats_reset(phy); + + mt7996_mac_reset_counters(phy); + phy->noise = 0; + if (!mphy->offchannel) + mt7996_mac_update_beacons(phy); + +out: + ieee80211_queue_delayed_work(mphy->hw, &mphy->mac_work, + MT7996_WATCHDOG_TIME); + + return ret; +} + +static int mt7996_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct mt7996_dev *dev = mt7996_hw_dev(hw); + unsigned int link_id; + unsigned long links; + int err = 0; + + /* The hardware does not support per-STA RX GTK, fallback + * to software mode for these. + */ + if ((vif->type == NL80211_IFTYPE_ADHOC || + vif->type == NL80211_IFTYPE_MESH_POINT) && + (key->cipher == WLAN_CIPHER_SUITE_TKIP || + key->cipher == WLAN_CIPHER_SUITE_CCMP) && + !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) + return -EOPNOTSUPP; + + /* fall back to sw encryption for unsupported ciphers */ + switch (key->cipher) { + case WLAN_CIPHER_SUITE_TKIP: + case WLAN_CIPHER_SUITE_CCMP: + case WLAN_CIPHER_SUITE_CCMP_256: + case WLAN_CIPHER_SUITE_GCMP: + case WLAN_CIPHER_SUITE_GCMP_256: + case WLAN_CIPHER_SUITE_SMS4: + break; + case WLAN_CIPHER_SUITE_AES_CMAC: + case WLAN_CIPHER_SUITE_BIP_CMAC_256: + case WLAN_CIPHER_SUITE_BIP_GMAC_128: + case WLAN_CIPHER_SUITE_BIP_GMAC_256: + if (key->keyidx == 6 || key->keyidx == 7) + break; + fallthrough; + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + default: + return -EOPNOTSUPP; + } + + mutex_lock(&dev->mt76.mutex); + + if (key->link_id >= 0) + links = BIT(key->link_id); + else if (sta && sta->valid_links) + links = sta->valid_links; + else if (vif->valid_links) + links = vif->valid_links; + else + links = BIT(0); + + for_each_set_bit(link_id, &links, IEEE80211_MLD_MAX_NUM_LINKS) { + err = mt7996_set_hw_key(hw, cmd, vif, sta, link_id, key); + if (err) + break; + } + mutex_unlock(&dev->mt76.mutex); + + return err; +} + +static int mt7996_config(struct ieee80211_hw *hw, int radio_idx, u32 changed) +{ + return 0; +} + +static int +mt7996_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + unsigned int link_id, u16 queue, + const struct ieee80211_tx_queue_params *params) +{ + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct mt7996_vif_link_info *link_info = &mvif->link_info[link_id]; + static const u8 mq_to_aci[] = { + [IEEE80211_AC_VO] = 3, + [IEEE80211_AC_VI] = 2, + [IEEE80211_AC_BE] = 0, + [IEEE80211_AC_BK] = 1, + }; + + /* firmware uses access class index */ + link_info->queue_params[mq_to_aci[queue]] = *params; + /* no need to update right away, we'll get BSS_CHANGED_QOS */ + + return 0; +} + +static void mt7996_configure_filter(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *total_flags, + u64 multicast) +{ + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mt7996_phy *phy; + u32 filter_mask = 0, filter_set = 0; + u32 flags = 0; + +#define MT76_FILTER(_flag, _hw) do { \ + flags |= *total_flags & FIF_##_flag; \ + filter_mask |= (_hw); \ + filter_set |= !(flags & FIF_##_flag) * (_hw); \ + } while (0) + + mutex_lock(&dev->mt76.mutex); + + MT76_FILTER(OTHER_BSS, MT_WF_RFCR_DROP_OTHER_TIM | + MT_WF_RFCR_DROP_A3_MAC | + MT_WF_RFCR_DROP_A3_BSSID); + + MT76_FILTER(FCSFAIL, MT_WF_RFCR_DROP_FCSFAIL); + + MT76_FILTER(CONTROL, MT_WF_RFCR_DROP_CTS | + MT_WF_RFCR_DROP_RTS | + MT_WF_RFCR_DROP_CTL_RSV); + + *total_flags = flags; + + mt7996_for_each_phy(dev, phy) { + phy->rxfilter &= ~(MT_WF_RFCR_DROP_OTHER_BSS | + MT_WF_RFCR_DROP_OTHER_BEACON | + MT_WF_RFCR_DROP_FRAME_REPORT | + MT_WF_RFCR_DROP_PROBEREQ | + MT_WF_RFCR_DROP_MCAST_FILTERED | + MT_WF_RFCR_DROP_MCAST | + MT_WF_RFCR_DROP_BCAST | + MT_WF_RFCR_DROP_DUPLICATE | + MT_WF_RFCR_DROP_A2_BSSID | + MT_WF_RFCR_DROP_UNWANTED_CTL | + MT_WF_RFCR_DROP_STBC_MULTI | + filter_mask); + phy->rxfilter |= filter_set; + mt7996_phy_set_rxfilter(phy); + } + + mutex_unlock(&dev->mt76.mutex); +} + +static int +mt7996_get_txpower(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + unsigned int link_id, int *dbm) +{ + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct mt7996_phy *phy = mt7996_vif_link_phy(&mvif->deflink); + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct wireless_dev *wdev; + int n_chains, delta, i; + + if (!phy) { + wdev = ieee80211_vif_to_wdev(vif); + for (i = 0; i < hw->wiphy->n_radio; i++) + if (wdev->radio_mask & BIT(i)) + phy = dev->radio_phy[i]; + + if (!phy) + return -EINVAL; + } + + n_chains = hweight16(phy->mt76->chainmask); + delta = mt76_tx_power_path_delta(n_chains); + *dbm = DIV_ROUND_UP(phy->mt76->txpower_cur + delta, 2); + + return 0; +} + +static u8 +mt7996_get_rates_table(struct mt7996_phy *phy, struct ieee80211_bss_conf *conf, + bool beacon, bool mcast) +{ + struct mt7996_dev *dev = phy->dev; + struct mt76_vif_link *mvif = mt76_vif_conf_link(&dev->mt76, conf->vif, conf); + u16 rate; + u8 i, idx; + + rate = mt76_connac2_mac_tx_rate_val(phy->mt76, conf, beacon, mcast); + + if (beacon) { + /* odd index for driver, even index for firmware */ + idx = MT7996_BEACON_RATES_TBL + 2 * phy->mt76->band_idx; + if (phy->beacon_rate != rate) + mt7996_mcu_set_fixed_rate_table(phy, idx, rate, beacon); + + return idx; + } + + idx = FIELD_GET(MT_TX_RATE_IDX, rate); + for (i = 0; i < ARRAY_SIZE(mt76_rates); i++) + if ((mt76_rates[i].hw_value & GENMASK(7, 0)) == idx) + return MT7996_BASIC_RATES_TBL + 2 * i; + + return mvif->basic_rates_idx; +} + +static void +mt7996_update_mu_group(struct ieee80211_hw *hw, struct mt7996_vif_link *link, + struct ieee80211_bss_conf *info) +{ + struct mt7996_dev *dev = mt7996_hw_dev(hw); + u8 band = link->mt76.band_idx; + u32 *mu; + + mu = (u32 *)info->mu_group.membership; + mt76_wr(dev, MT_WF_PHYRX_BAND_GID_TAB_VLD0(band), mu[0]); + mt76_wr(dev, MT_WF_PHYRX_BAND_GID_TAB_VLD1(band), mu[1]); + + mu = (u32 *)info->mu_group.position; + mt76_wr(dev, MT_WF_PHYRX_BAND_GID_TAB_POS0(band), mu[0]); + mt76_wr(dev, MT_WF_PHYRX_BAND_GID_TAB_POS1(band), mu[1]); + mt76_wr(dev, MT_WF_PHYRX_BAND_GID_TAB_POS2(band), mu[2]); + mt76_wr(dev, MT_WF_PHYRX_BAND_GID_TAB_POS3(band), mu[3]); +} + +static void +mt7996_vif_cfg_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u64 changed) +{ + struct mt7996_dev *dev = mt7996_hw_dev(hw); + + mutex_lock(&dev->mt76.mutex); + + if ((changed & BSS_CHANGED_ASSOC) && vif->cfg.assoc) { + struct ieee80211_bss_conf *link_conf; + unsigned long link_id; + + for_each_vif_active_link(vif, link_conf, link_id) { + struct mt7996_vif_link *link; + struct mt7996_phy *phy; + + link = mt7996_vif_link(dev, vif, link_id); + if (!link) + continue; + + if (vif->type == NL80211_IFTYPE_STATION) { + link->mt76.beacon_mon_interval = + msecs_to_jiffies(ieee80211_tu_to_usec( + link_conf->beacon_int) / 1000); + WRITE_ONCE(link->mt76.beacon_mon_last, jiffies); + } + + phy = mt7996_vif_link_phy(link); + if (!phy) + continue; + + mt7996_mcu_add_bss_info(phy, vif, link_conf, + &link->mt76, &link->msta_link, + true); + mt7996_mcu_add_sta(dev, link_conf, NULL, link, NULL, + CONN_STATE_PORT_SECURE, + !!(changed & BSS_CHANGED_BSSID)); + } + } + + if ((changed & BSS_CHANGED_ASSOC) && !vif->cfg.assoc && + vif->type == NL80211_IFTYPE_STATION) { + struct ieee80211_bss_conf *link_conf; + unsigned long link_id; + + for_each_vif_active_link(vif, link_conf, link_id) { + struct mt7996_vif_link *link; + + link = mt7996_vif_link(dev, vif, link_id); + if (link) + link->mt76.beacon_mon_interval = 0; + } + } + + mutex_unlock(&dev->mt76.mutex); +} + +static void +mt7996_link_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, u64 changed) +{ + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mt7996_vif_link *link; + struct mt7996_phy *phy; + struct mt76_phy *mphy; + + mutex_lock(&dev->mt76.mutex); + + link = mt7996_vif_conf_link(dev, vif, info); + if (!link) + goto out; + + mphy = mt76_vif_link_phy(&link->mt76); + if (!mphy) + goto out; + + phy = mphy->priv; + + /* station mode uses BSSID to map the wlan entry to a peer, + * and then peer references bss_info_rfch to set bandwidth cap. + */ + if ((changed & BSS_CHANGED_BSSID && !is_zero_ether_addr(info->bssid)) || + (changed & BSS_CHANGED_BEACON_ENABLED && info->enable_beacon)) { + mt7996_mcu_add_bss_info(phy, vif, info, &link->mt76, + &link->msta_link, true); + mt7996_mcu_add_sta(dev, info, NULL, link, NULL, + CONN_STATE_PORT_SECURE, + !!(changed & BSS_CHANGED_BSSID)); + } + + if (changed & BSS_CHANGED_HT || changed & BSS_CHANGED_ERP_CTS_PROT) + mt7996_mcu_set_protection(phy, link, info->ht_operation_mode, + info->use_cts_prot); + + if (changed & BSS_CHANGED_ERP_SLOT) { + int slottime = info->use_short_slot ? 9 : 20; + + if (slottime != phy->slottime) { + phy->slottime = slottime; + mt7996_mcu_set_timing(phy, vif, info); + } + } + + if (changed & BSS_CHANGED_MCAST_RATE) + link->mt76.mcast_rates_idx = + mt7996_get_rates_table(phy, info, false, true); + + if (changed & BSS_CHANGED_BASIC_RATES) + link->mt76.basic_rates_idx = + mt7996_get_rates_table(phy, info, false, false); + + /* ensure that enable txcmd_mode after bss_info */ + if (changed & (BSS_CHANGED_QOS | BSS_CHANGED_BEACON_ENABLED)) + mt7996_mcu_set_tx(dev, vif, info); + + if (changed & BSS_CHANGED_HE_OBSS_PD) + mt7996_mcu_add_obss_spr(phy, link, &info->he_obss_pd); + + if (changed & BSS_CHANGED_HE_BSS_COLOR) { + if ((vif->type == NL80211_IFTYPE_AP && + link->mt76.omac_idx <= HW_BSSID_MAX) || + vif->type == NL80211_IFTYPE_STATION) + mt7996_mcu_update_bss_color(dev, &link->mt76, + &info->he_bss_color); + } + + if (changed & (BSS_CHANGED_BEACON | + BSS_CHANGED_BEACON_ENABLED)) { + link->mt76.beacon_rates_idx = + mt7996_get_rates_table(phy, info, true, false); + + mt7996_mcu_add_beacon(hw, vif, info, info->enable_beacon); + } + + if (changed & (BSS_CHANGED_UNSOL_BCAST_PROBE_RESP | + BSS_CHANGED_FILS_DISCOVERY)) + mt7996_mcu_beacon_inband_discov(dev, info, link, changed); + + if (changed & BSS_CHANGED_MU_GROUPS) + mt7996_update_mu_group(hw, link, info); + + if (changed & BSS_CHANGED_TXPOWER && + info->txpower != phy->txpower) { + phy->txpower = info->txpower; + mt7996_mcu_set_txpower_sku(phy); + } + +out: + mutex_unlock(&dev->mt76.mutex); +} + +static void +mt7996_channel_switch_beacon(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_chan_def *chandef) +{ + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mt7996_phy *phy = mt7996_band_phy(dev, chandef->chan->band); + struct ieee80211_bss_conf *link_conf; + unsigned int link_id; + + mutex_lock(&dev->mt76.mutex); + + for_each_vif_active_link(vif, link_conf, link_id) { + struct mt7996_vif_link *link; + struct mt7996_phy *link_phy; + + link = mt7996_vif_link(dev, vif, link_id); + if (!link) + continue; + + link_phy = mt7996_vif_link_phy(link); + if (link_phy != phy) + continue; + + /* Reset beacon when channel switch triggered during CAC to let + * FW correctly perform CSA countdown + */ + if (!cfg80211_reg_can_beacon(hw->wiphy, &phy->mt76->chandef, + vif->type)) + mt7996_mcu_add_beacon(hw, vif, link_conf, false); + + mt7996_mcu_add_beacon(hw, vif, link_conf, true); + break; + } + + mutex_unlock(&dev->mt76.mutex); +} + +static int +mt7996_post_channel_switch(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf) +{ + struct cfg80211_chan_def *chandef = &link_conf->chanreq.oper; + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mt7996_phy *phy = mt7996_band_phy(dev, chandef->chan->band); + struct mt7996_vif_link *link; + int ret = -EINVAL; + + mutex_lock(&dev->mt76.mutex); + + link = mt7996_vif_conf_link(dev, vif, link_conf); + if (!link) + goto out; + + ret = mt7996_mcu_update_bss_rfch(phy, link); + if (ret) + goto out; + + ieee80211_iterate_stations_mtx(hw, mt7996_mcu_update_sta_rec_bw, link); + + ret = mt7996_mcu_rdd_resume_tx(phy); + +out: + mutex_unlock(&dev->mt76.mutex); + + return ret; +} + +static void +mt7996_sta_init_txq_wcid(struct ieee80211_sta *sta, int idx) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(sta->txq); i++) { + struct mt76_txq *mtxq; + + if (!sta->txq[i]) + continue; + + mtxq = (struct mt76_txq *)sta->txq[i]->drv_priv; + mtxq->wcid = idx; + } +} + +static int +mt7996_mac_sta_init_link(struct mt7996_dev *dev, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_link_sta *link_sta, + struct mt7996_vif_link *link, unsigned int link_id) +{ + struct ieee80211_sta *sta = link_sta->sta; + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct mt7996_phy *phy = mt7996_vif_link_phy(link); + struct mt7996_sta_link *msta_link; + int idx; + + if (!phy) + return -EINVAL; + + idx = mt76_wcid_alloc(dev->mt76.wcid_mask, MT7996_WTBL_STA); + if (idx < 0) + return -ENOSPC; + + if (msta->deflink_id == IEEE80211_LINK_UNSPECIFIED) { + msta_link = &msta->deflink; + msta->deflink_id = link_id; + msta->seclink_id = msta->deflink_id; + mt7996_sta_init_txq_wcid(sta, idx); + } else { + msta_link = kzalloc_obj(*msta_link); + if (!msta_link) + return -ENOMEM; + + if (msta->seclink_id == msta->deflink_id && + (sta->valid_links & ~BIT(msta->deflink_id))) + msta->seclink_id = __ffs(sta->valid_links & + ~BIT(msta->deflink_id)); + } + + INIT_LIST_HEAD(&msta_link->rc_list); + INIT_LIST_HEAD(&msta_link->wcid.poll_list); + msta_link->sta = msta; + msta_link->wcid.sta = 1; + msta_link->wcid.idx = idx; + msta_link->wcid.link_id = link_id; + msta_link->wcid.link_valid = !!sta->valid_links; + msta_link->wcid.def_wcid = &msta->deflink.wcid; + + if (link_sta->sta->tdls) + set_bit(MT_WCID_FLAG_TDLS_PEER, &msta_link->wcid.flags); + + ewma_avg_signal_init(&msta_link->avg_ack_signal); + ewma_signal_init(&msta_link->wcid.rssi); + + rcu_assign_pointer(msta->link[link_id], msta_link); + + mt7996_mac_wtbl_update(dev, idx, MT_WTBL_UPDATE_ADM_COUNT_CLEAR); + mt7996_mcu_add_sta(dev, link_conf, link_sta, link, msta_link, + CONN_STATE_DISCONNECT, true); + + rcu_assign_pointer(dev->mt76.wcid[idx], &msta_link->wcid); + mt76_wcid_init(&msta_link->wcid, phy->mt76->band_idx); + + return 0; +} + +void mt7996_mac_sta_remove_link(struct mt7996_dev *dev, + struct ieee80211_sta *sta, + unsigned int link_id, bool flush) +{ + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct mt7996_sta_link *msta_link; + + msta_link = mt76_dereference(msta->link[link_id], &dev->mt76); + if (!msta_link) + return; + + spin_lock_bh(&dev->mt76.sta_poll_lock); + if (!list_empty(&msta_link->wcid.poll_list)) + list_del_init(&msta_link->wcid.poll_list); + if (!list_empty(&msta_link->rc_list)) + list_del_init(&msta_link->rc_list); + spin_unlock_bh(&dev->mt76.sta_poll_lock); + + mt76_wcid_cleanup(&dev->mt76, &msta_link->wcid); + + if (msta_link->wcid.link_valid) { + mt7996_mac_wtbl_update(dev, msta_link->wcid.idx, + MT_WTBL_UPDATE_ADM_COUNT_CLEAR); + + if (msta->deflink_id == link_id) { + msta->deflink_id = IEEE80211_LINK_UNSPECIFIED; + if (msta->seclink_id == link_id) { + /* no secondary link available */ + msta->seclink_id = msta->deflink_id; + } else { + struct mt7996_sta_link *msta_seclink; + + /* switch to the secondary link */ + msta_seclink = mt76_dereference( + msta->link[msta->seclink_id], + &dev->mt76); + if (msta_seclink) { + msta->deflink_id = msta->seclink_id; + mt7996_sta_init_txq_wcid(sta, + msta_seclink->wcid.idx); + } + } + } else if (msta->seclink_id == link_id) { + msta->seclink_id = msta->deflink_id; + } + msta_link->wcid.link_valid = false; + } + + if (flush) { + struct mt7996_phy *phy = + __mt7996_phy(dev, msta_link->wcid.phy_idx); + + if (phy) + phy->mt76->num_sta--; + + rcu_assign_pointer(msta->link[link_id], NULL); + rcu_assign_pointer(dev->mt76.wcid[msta_link->wcid.idx], NULL); + mt76_wcid_mask_clear(dev->mt76.wcid_mask, msta_link->wcid.idx); + if (msta_link != &msta->deflink) + kfree_rcu(msta_link, rcu_head); + } +} + +static void +mt7996_mac_sta_remove_links(struct mt7996_dev *dev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, unsigned long links, + bool flush) +{ + unsigned int link_id; + + for_each_set_bit(link_id, &links, IEEE80211_MLD_MAX_NUM_LINKS) + mt7996_mac_sta_remove_link(dev, sta, link_id, flush); +} + +static int +mt7996_mac_sta_add_links(struct mt7996_dev *dev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, unsigned long new_links) +{ + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + unsigned int link_id; + int err = 0; + + for_each_set_bit(link_id, &new_links, IEEE80211_MLD_MAX_NUM_LINKS) { + struct ieee80211_bss_conf *link_conf; + struct ieee80211_link_sta *link_sta; + struct mt7996_sta_link *msta_link; + struct mt7996_vif_link *link; + struct mt76_phy *mphy; + + msta_link = mt76_dereference(msta->link[link_id], &dev->mt76); + if (msta_link) { + msta_link->wcid.link_valid = true; + continue; + } + + link_conf = link_conf_dereference_protected(vif, link_id); + if (!link_conf) { + err = -EINVAL; + goto error_unlink; + } + + link = mt7996_vif_link(dev, vif, link_id); + if (!link) { + err = -EINVAL; + goto error_unlink; + } + + link_sta = link_sta_dereference_protected(sta, link_id); + if (!link_sta) { + err = -EINVAL; + goto error_unlink; + } + + mphy = mt76_vif_link_phy(&link->mt76); + if (!mphy) { + err = -EINVAL; + goto error_unlink; + } + + err = mt7996_mac_sta_init_link(dev, link_conf, link_sta, link, + link_id); + if (err) + goto error_unlink; + + mphy->num_sta++; + } + + return 0; + +error_unlink: + mt7996_mac_sta_remove_links(dev, vif, sta, new_links, true); + + return err; +} + +static int +mt7996_mac_sta_change_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, u16 old_links, + u16 new_links) +{ + struct mt7996_dev *dev = mt7996_hw_dev(hw); + unsigned long add = new_links & ~old_links; + unsigned long rem = old_links & ~new_links; + int ret; + + mutex_lock(&dev->mt76.mutex); + + mt7996_mac_sta_remove_links(dev, vif, sta, rem, false); + ret = mt7996_mac_sta_add_links(dev, vif, sta, add); + + mutex_unlock(&dev->mt76.mutex); + + return ret; +} + +static int +mt7996_mac_sta_add(struct mt7996_dev *dev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + unsigned long links = sta->valid_links ? sta->valid_links : BIT(0); + int err; + + mutex_lock(&dev->mt76.mutex); + + msta->deflink_id = IEEE80211_LINK_UNSPECIFIED; + msta->seclink_id = IEEE80211_LINK_UNSPECIFIED; + msta->vif = mvif; + err = mt7996_mac_sta_add_links(dev, vif, sta, links); + + mutex_unlock(&dev->mt76.mutex); + + return err; +} + +static int +mt7996_mac_sta_event(struct mt7996_dev *dev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, enum mt76_sta_event ev) +{ + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + unsigned long links = sta->valid_links; + struct ieee80211_link_sta *link_sta; + unsigned int link_id; + int err = 0; + + mutex_lock(&dev->mt76.mutex); + + for_each_sta_active_link(vif, sta, link_sta, link_id) { + struct ieee80211_bss_conf *link_conf; + struct mt7996_sta_link *msta_link; + struct mt7996_vif_link *link; + int i; + + link_conf = link_conf_dereference_protected(vif, link_id); + if (!link_conf) + continue; + + link = mt7996_vif_link(dev, vif, link_id); + if (!link) + continue; + + msta_link = mt7996_sta_link_protected(dev, msta, link_id); + if (!msta_link) + continue; + + switch (ev) { + case MT76_STA_EVENT_ASSOC: + err = mt7996_mcu_add_sta(dev, link_conf, link_sta, + link, msta_link, + CONN_STATE_CONNECT, true); + if (err) + goto unlock; + + err = mt7996_mcu_add_rate_ctrl(dev, msta_link->sta, vif, + link_id, false); + if (err) + goto unlock; + + msta_link->wcid.tx_info |= MT_WCID_TX_INFO_SET; + break; + case MT76_STA_EVENT_AUTHORIZE: + err = mt7996_mcu_add_sta(dev, link_conf, link_sta, + link, msta_link, + CONN_STATE_PORT_SECURE, false); + if (err) + goto unlock; + break; + case MT76_STA_EVENT_DISASSOC: + for (i = 0; i < ARRAY_SIZE(msta_link->twt.flow); i++) + mt7996_mac_twt_teardown_flow(dev, link, + msta_link, i); + + if (!sta->mlo) + mt7996_mcu_add_sta(dev, link_conf, link_sta, + link, msta_link, + CONN_STATE_DISCONNECT, false); + else if (sta->mlo && links == BIT(link_id)) /* last link */ + mt7996_mcu_teardown_mld_sta(dev, link, + msta_link); + msta_link->wcid.sta_disabled = 1; + msta_link->wcid.sta = 0; + links = links & ~BIT(link_id); + break; + } + } +unlock: + mutex_unlock(&dev->mt76.mutex); + + return err; +} + +static void +mt7996_mac_sta_remove(struct mt7996_dev *dev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + int i; + + mutex_lock(&dev->mt76.mutex); + for (i = 0; i < ARRAY_SIZE(msta->link); i++) + mt7996_mac_sta_remove_link(dev, sta, i, true); + mutex_unlock(&dev->mt76.mutex); +} + +static void +mt7996_set_active_links(struct ieee80211_vif *vif) +{ + u16 active_links; + + if (vif->type != NL80211_IFTYPE_STATION) + return; + + if (!ieee80211_vif_is_mld(vif)) + return; + + active_links = mt76_select_links(vif, MT7996_MAX_RADIOS); + if (hweight16(active_links) < 2) + return; + + ieee80211_set_active_links_async(vif, active_links); +} + +static int +mt7996_sta_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, enum ieee80211_sta_state old_state, + enum ieee80211_sta_state new_state) +{ + struct mt7996_dev *dev = mt7996_hw_dev(hw); + enum mt76_sta_event ev; + + if (old_state == IEEE80211_STA_NOTEXIST && + new_state == IEEE80211_STA_NONE) + return mt7996_mac_sta_add(dev, vif, sta); + + if (old_state == IEEE80211_STA_NONE && + new_state == IEEE80211_STA_NOTEXIST) + mt7996_mac_sta_remove(dev, vif, sta); + + if (old_state == IEEE80211_STA_AUTH && + new_state == IEEE80211_STA_ASSOC) { + mt7996_set_active_links(vif); + ev = MT76_STA_EVENT_ASSOC; + } else if (old_state == IEEE80211_STA_ASSOC && + new_state == IEEE80211_STA_AUTHORIZED) { + ev = MT76_STA_EVENT_AUTHORIZE; + } else if (old_state == IEEE80211_STA_ASSOC && + new_state == IEEE80211_STA_AUTH) { + ev = MT76_STA_EVENT_DISASSOC; + } else { + return 0; + } + + return mt7996_mac_sta_event(dev, vif, sta, ev); +} + +static void mt7996_tx(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, + struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct ieee80211_sta *sta = control->sta; + struct mt7996_sta *msta = sta ? (void *)sta->drv_priv : NULL; + struct mt76_phy *mphy = hw->priv; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_vif *vif = info->control.vif; + struct mt7996_vif *mvif = vif ? (void *)vif->drv_priv : NULL; + struct mt76_wcid *wcid = &dev->mt76.global_wcid; + u8 deflink_id = IEEE80211_LINK_UNSPECIFIED; + u8 link_id = u32_get_bits(info->control.flags, + IEEE80211_TX_CTRL_MLO_LINK); + + rcu_read_lock(); + + if (msta) + deflink_id = msta->deflink_id; + else if (mvif) + deflink_id = mvif->mt76.deflink_id; + + /* the primary link is unset until the first link has been added */ + if (deflink_id >= IEEE80211_MLD_MAX_NUM_LINKS) + deflink_id = 0; + + /* Use primary link_id if the value from mac80211 is set to + * IEEE80211_LINK_UNSPECIFIED. + */ + if (link_id == IEEE80211_LINK_UNSPECIFIED) + link_id = deflink_id; + + if (vif && ieee80211_vif_is_mld(vif)) { + struct ieee80211_bss_conf *link_conf; + + if (msta) { + struct ieee80211_link_sta *link_sta; + + link_sta = rcu_dereference(sta->link[link_id]); + if (!link_sta) + link_sta = rcu_dereference(sta->link[deflink_id]); + + if (link_sta) { + memcpy(hdr->addr1, link_sta->addr, ETH_ALEN); + if (ether_addr_equal(sta->addr, hdr->addr3)) + memcpy(hdr->addr3, link_sta->addr, ETH_ALEN); + } + } + + link_conf = rcu_dereference(vif->link_conf[link_id]); + if (link_conf) { + memcpy(hdr->addr2, link_conf->addr, ETH_ALEN); + if (ether_addr_equal(vif->addr, hdr->addr3)) + memcpy(hdr->addr3, link_conf->addr, ETH_ALEN); + } + } + + if (mvif) { + struct mt76_vif_link *mlink; + + mlink = rcu_dereference(mvif->mt76.link[link_id]); + if (mlink && mlink->wcid) + wcid = mlink->wcid; + + if (mvif->mt76.roc_phy && + (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN)) { + mphy = mvif->mt76.roc_phy; + if (mphy->roc_link) + wcid = mphy->roc_link->wcid; + } else if (mlink) { + mphy = mt76_vif_link_phy(mlink); + } + } + + if (!mphy) { + ieee80211_free_txskb(hw, skb); + goto unlock; + } + + if (msta) { + struct mt7996_sta_link *msta_link; + + msta_link = mt7996_sta_link(msta, link_id); + if (msta_link) + wcid = &msta_link->wcid; + } + mt76_tx(mphy, control->sta, mt7996_get_tx_wcid(wcid), skb); +unlock: + rcu_read_unlock(); +} + +static int mt7996_set_rts_threshold(struct ieee80211_hw *hw, int radio_idx, + u32 val) +{ + struct mt7996_dev *dev = mt7996_hw_dev(hw); + int i, ret = 0; + + mutex_lock(&dev->mt76.mutex); + + for (i = 0; i < hw->wiphy->n_radio; i++) { + struct mt7996_phy *phy = dev->radio_phy[i]; + + ret = mt7996_mcu_set_rts_thresh(phy, val); + if (ret) + break; + } + + mutex_unlock(&dev->mt76.mutex); + + return ret; +} + +static int +mt7996_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_ampdu_params *params) +{ + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct ieee80211_sta *sta = params->sta; + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct ieee80211_txq *txq = sta->txq[params->tid]; + u16 tid = params->tid; + u16 ssn = params->ssn; + struct mt76_txq *mtxq; + int ret = 0; + + if (!txq) + return -EINVAL; + + mtxq = (struct mt76_txq *)txq->drv_priv; + + mutex_lock(&dev->mt76.mutex); + + switch (params->action) { + case IEEE80211_AMPDU_RX_START: + /* Since packets belonging to the same TID can be split over + * multiple links, store the AMPDU state for reordering in the + * primary link + */ + mt76_rx_aggr_start(&dev->mt76, &msta->deflink.wcid, tid, + ssn, params->buf_size); + ret = mt7996_mcu_add_rx_ba(dev, params, vif, true); + break; + case IEEE80211_AMPDU_RX_STOP: + mt76_rx_aggr_stop(&dev->mt76, &msta->deflink.wcid, tid); + ret = mt7996_mcu_add_rx_ba(dev, params, vif, false); + break; + case IEEE80211_AMPDU_TX_OPERATIONAL: + mtxq->aggr = true; + mtxq->send_bar = false; + ret = mt7996_mcu_add_tx_ba(dev, params, vif, true); + break; + case IEEE80211_AMPDU_TX_STOP_FLUSH: + case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: + mtxq->aggr = false; + clear_bit(tid, &msta->deflink.wcid.ampdu_state); + ret = mt7996_mcu_add_tx_ba(dev, params, vif, false); + break; + case IEEE80211_AMPDU_TX_START: + set_bit(tid, &msta->deflink.wcid.ampdu_state); + ret = IEEE80211_AMPDU_TX_START_IMMEDIATE; + break; + case IEEE80211_AMPDU_TX_STOP_CONT: + mtxq->aggr = false; + clear_bit(tid, &msta->deflink.wcid.ampdu_state); + ret = mt7996_mcu_add_tx_ba(dev, params, vif, false); + ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); + break; + } + + mutex_unlock(&dev->mt76.mutex); + + return ret; +} + +static int +mt7996_get_stats(struct ieee80211_hw *hw, + struct ieee80211_low_level_stats *stats) +{ + struct mt7996_dev *dev = mt7996_hw_dev(hw); + int i; + + mutex_lock(&dev->mt76.mutex); + + memset(stats, 0, sizeof(*stats)); + for (i = 0; i < hw->wiphy->n_radio; i++) { + struct mt7996_phy *phy = dev->radio_phy[i]; + struct mt76_mib_stats *mib = &phy->mib; + + stats->dot11RTSSuccessCount += mib->rts_cnt; + stats->dot11RTSFailureCount += mib->rts_retries_cnt; + stats->dot11FCSErrorCount += mib->fcs_err_cnt; + stats->dot11ACKFailureCount += mib->ack_fail_cnt; + } + + mutex_unlock(&dev->mt76.mutex); + + return 0; +} + +u64 __mt7996_get_tsf(struct ieee80211_hw *hw, struct mt7996_vif_link *link) +{ + struct mt7996_phy *phy = mt7996_vif_link_phy(link); + struct mt7996_dev *dev = mt7996_hw_dev(hw); + union { + u64 t64; + u32 t32[2]; + } tsf; + u16 n; + + if (!phy) + return 0; + + lockdep_assert_held(&dev->mt76.mutex); + + n = link->mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0 + : link->mt76.omac_idx; + /* TSF software read */ + mt76_rmw(dev, MT_LPON_TCR(phy->mt76->band_idx, n), MT_LPON_TCR_SW_MODE, + MT_LPON_TCR_SW_READ); + tsf.t32[0] = mt76_rr(dev, MT_LPON_UTTR0(phy->mt76->band_idx)); + tsf.t32[1] = mt76_rr(dev, MT_LPON_UTTR1(phy->mt76->band_idx)); + + return tsf.t64; +} + +static u64 +mt7996_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct mt7996_dev *dev = mt7996_hw_dev(hw); + u64 ret; + + mutex_lock(&dev->mt76.mutex); + ret = __mt7996_get_tsf(hw, &mvif->deflink); + mutex_unlock(&dev->mt76.mutex); + + return ret; +} + +static void +mt7996_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u64 timestamp) +{ + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mt7996_vif_link *link; + struct mt7996_phy *phy; + union { + u64 t64; + u32 t32[2]; + } tsf = { .t64 = timestamp, }; + u16 n; + + mutex_lock(&dev->mt76.mutex); + + link = mt7996_vif_link(dev, vif, mvif->mt76.deflink_id); + if (!link) + goto unlock; + + n = link->mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0 + : link->mt76.omac_idx; + phy = mt7996_vif_link_phy(link); + if (!phy) + goto unlock; + + mt76_wr(dev, MT_LPON_UTTR0(phy->mt76->band_idx), tsf.t32[0]); + mt76_wr(dev, MT_LPON_UTTR1(phy->mt76->band_idx), tsf.t32[1]); + /* TSF software overwrite */ + mt76_rmw(dev, MT_LPON_TCR(phy->mt76->band_idx, n), MT_LPON_TCR_SW_MODE, + MT_LPON_TCR_SW_WRITE); + +unlock: + mutex_unlock(&dev->mt76.mutex); +} + +static void +mt7996_offset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + s64 timestamp) +{ + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mt7996_vif_link *link; + struct mt7996_phy *phy; + union { + u64 t64; + u32 t32[2]; + } tsf = { .t64 = timestamp, }; + u16 n; + + mutex_lock(&dev->mt76.mutex); + + link = mt7996_vif_link(dev, vif, mvif->mt76.deflink_id); + if (!link) + goto unlock; + + phy = mt7996_vif_link_phy(link); + if (!phy) + goto unlock; + + n = link->mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0 + : link->mt76.omac_idx; + mt76_wr(dev, MT_LPON_UTTR0(phy->mt76->band_idx), tsf.t32[0]); + mt76_wr(dev, MT_LPON_UTTR1(phy->mt76->band_idx), tsf.t32[1]); + /* TSF software adjust*/ + mt76_rmw(dev, MT_LPON_TCR(phy->mt76->band_idx, n), MT_LPON_TCR_SW_MODE, + MT_LPON_TCR_SW_ADJUST); + +unlock: + mutex_unlock(&dev->mt76.mutex); +} + +static void +mt7996_set_coverage_class(struct ieee80211_hw *hw, int radio_idx, + s16 coverage_class) +{ + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mt7996_phy *phy; + + mutex_lock(&dev->mt76.mutex); + mt7996_for_each_phy(dev, phy) { + phy->coverage_class = max_t(s16, coverage_class, 0); + mt7996_mac_set_coverage_class(phy); + } + mutex_unlock(&dev->mt76.mutex); +} + +static int +mt7996_set_antenna(struct ieee80211_hw *hw, int radio_idx, + u32 tx_ant, u32 rx_ant) +{ + struct mt7996_dev *dev = mt7996_hw_dev(hw); + int i; + + if (tx_ant != rx_ant) + return -EINVAL; + + for (i = 0; i < hw->wiphy->n_radio; i++) { + struct mt7996_phy *phy = dev->radio_phy[i]; + + if (!(tx_ant & phy->orig_chainmask)) + return -EINVAL; + } + + mutex_lock(&dev->mt76.mutex); + + for (i = 0; i < hw->wiphy->n_radio; i++) { + struct mt7996_phy *phy = dev->radio_phy[i]; + u8 band_idx = phy->mt76->band_idx; + u8 shift = dev->chainshift[band_idx]; + + phy->mt76->chainmask = tx_ant & phy->orig_chainmask; + phy->mt76->antenna_mask = (phy->mt76->chainmask >> shift) & + phy->orig_antenna_mask; + + mt76_set_stream_caps(phy->mt76, true); + mt7996_set_stream_vht_txbf_caps(phy); + mt7996_set_stream_he_eht_caps(phy); + mt7996_mcu_set_txpower_sku(phy); + } + + mutex_unlock(&dev->mt76.mutex); + + return 0; +} + +static void mt7996_sta_statistics(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct station_info *sinfo) +{ + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct mt7996_sta_link *msta_link = &msta->deflink; + struct rate_info *txrate = &msta_link->wcid.rate; + + if (txrate->legacy || txrate->flags) { + if (txrate->legacy) { + sinfo->txrate.legacy = txrate->legacy; + } else { + sinfo->txrate.mcs = txrate->mcs; + sinfo->txrate.nss = txrate->nss; + sinfo->txrate.bw = txrate->bw; + sinfo->txrate.he_gi = txrate->he_gi; + sinfo->txrate.he_dcm = txrate->he_dcm; + sinfo->txrate.he_ru_alloc = txrate->he_ru_alloc; + sinfo->txrate.eht_gi = txrate->eht_gi; + } + sinfo->txrate.flags = txrate->flags; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE); + } + + sinfo->tx_failed = msta_link->wcid.stats.tx_failed; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_FAILED); + + sinfo->tx_retries = msta_link->wcid.stats.tx_retries; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_RETRIES); + + sinfo->ack_signal = (s8)msta_link->ack_signal; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_ACK_SIGNAL); + + sinfo->avg_ack_signal = + -(s8)ewma_avg_signal_read(&msta_link->avg_ack_signal); + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_ACK_SIGNAL_AVG); + + if (mtk_wed_device_active(&dev->mt76.mmio.wed)) { + sinfo->tx_bytes = msta_link->wcid.stats.tx_bytes; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BYTES64); + + sinfo->rx_bytes = msta_link->wcid.stats.rx_bytes; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BYTES64); + + sinfo->tx_packets = msta_link->wcid.stats.tx_packets; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_PACKETS); + + sinfo->rx_packets = msta_link->wcid.stats.rx_packets; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_PACKETS); + } +} + +static void mt7996_link_rate_ctrl_update(void *data, + struct mt7996_sta_link *msta_link) +{ + struct mt7996_sta *msta = msta_link->sta; + struct mt7996_phy *phy = mt7996_vif_link_phy(&msta->vif->deflink); + struct mt7996_dev *dev; + u32 *changed = data; + + if (!phy) + return; + + dev = phy->dev; + spin_lock_bh(&dev->mt76.sta_poll_lock); + + msta_link->changed |= *changed; + if (list_empty(&msta_link->rc_list)) + list_add_tail(&msta_link->rc_list, &dev->sta_rc_list); + + spin_unlock_bh(&dev->mt76.sta_poll_lock); +} + +static void mt7996_link_sta_rc_update(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_link_sta *link_sta, + u32 changed) +{ + struct ieee80211_sta *sta = link_sta->sta; + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct mt7996_sta_link *msta_link; + + rcu_read_lock(); + + msta_link = mt7996_sta_link(msta, link_sta->link_id); + if (msta_link) { + struct mt7996_dev *dev = mt7996_hw_dev(hw); + + mt7996_link_rate_ctrl_update(&changed, msta_link); + ieee80211_queue_work(hw, &dev->rc_work); + } + + rcu_read_unlock(); +} + +static void mt7996_sta_rate_ctrl_update(void *data, struct ieee80211_sta *sta) +{ + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct mt7996_sta_link *msta_link; + struct mt7996_vif *mvif = data; + u32 changed = IEEE80211_RC_SUPP_RATES_CHANGED; + + if (msta->vif != mvif) + return; + + msta_link = mt7996_sta_link(msta, msta->deflink_id); + if (msta_link) + mt7996_link_rate_ctrl_update(&changed, msta_link); +} + +static int +mt7996_set_bitrate_mask(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + const struct cfg80211_bitrate_mask *mask) +{ + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + + mvif->deflink.bitrate_mask = *mask; + + /* if multiple rates across different preambles are given we can + * reconfigure this info with all peers using sta_rec command with + * the below exception cases. + * - single rate : if a rate is passed along with different preambles, + * we select the highest one as fixed rate. i.e VHT MCS for VHT peers. + * - multiple rates: if it's not in range format i.e 0-{7,8,9} for VHT + * then multiple MCS setting (MCS 4,5,6) is not supported. + */ + ieee80211_iterate_stations_atomic(hw, mt7996_sta_rate_ctrl_update, + mvif); + ieee80211_queue_work(hw, &dev->rc_work); + + return 0; +} + +static void mt7996_sta_set_4addr(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + bool enabled) +{ + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct ieee80211_link_sta *link_sta; + unsigned int link_id; + + mutex_lock(&dev->mt76.mutex); + + for_each_sta_active_link(vif, sta, link_sta, link_id) { + struct mt7996_sta_link *msta_link; + struct mt7996_vif_link *link; + + link = mt7996_vif_link(dev, vif, link_id); + if (!link) + continue; + + msta_link = mt7996_sta_link_protected(dev, msta, link_id); + if (!msta_link) + continue; + + if (enabled) + set_bit(MT_WCID_FLAG_4ADDR, &msta_link->wcid.flags); + else + clear_bit(MT_WCID_FLAG_4ADDR, &msta_link->wcid.flags); + + if (!msta_link->wcid.sta) + continue; + + mt7996_mcu_wtbl_update_hdr_trans(dev, vif, link, msta_link); + + /* HW cannot derive the BSSID from 4-address non-AMSDU frames, + * so PS exit is missed on links other than the setup link. + * Let the firmware wake those links instead. + */ + if (enabled && msta->deflink_id != link_id && + is_mt7996(&dev->mt76)) + mt7996_mcu_ps_leave(dev, link, msta_link); + } + + mutex_unlock(&dev->mt76.mutex); +} + +static void mt7996_sta_set_decap_offload(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + bool enabled) +{ + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct ieee80211_link_sta *link_sta; + unsigned int link_id; + + mutex_lock(&dev->mt76.mutex); + + for_each_sta_active_link(vif, sta, link_sta, link_id) { + struct mt7996_sta_link *msta_link; + struct mt7996_vif_link *link; + + link = mt7996_vif_link(dev, vif, link_id); + if (!link) + continue; + + msta_link = mt7996_sta_link_protected(dev, msta, link_id); + if (!msta_link) + continue; + + if (enabled) + set_bit(MT_WCID_FLAG_HDR_TRANS, + &msta_link->wcid.flags); + else + clear_bit(MT_WCID_FLAG_HDR_TRANS, + &msta_link->wcid.flags); + + if (!msta_link->wcid.sta) + continue; + + mt7996_mcu_wtbl_update_hdr_trans(dev, vif, link, msta_link); + } + + mutex_unlock(&dev->mt76.mutex); +} + +static const char mt7996_gstrings_stats[][ETH_GSTRING_LEN] = { + "tx_ampdu_cnt", + "tx_stop_q_empty_cnt", + "tx_mpdu_attempts", + "tx_mpdu_success", + "tx_rwp_fail_cnt", + "tx_rwp_need_cnt", + "tx_pkt_ebf_cnt", + "tx_pkt_ibf_cnt", + "tx_ampdu_len:0-1", + "tx_ampdu_len:2-10", + "tx_ampdu_len:11-19", + "tx_ampdu_len:20-28", + "tx_ampdu_len:29-37", + "tx_ampdu_len:38-46", + "tx_ampdu_len:47-55", + "tx_ampdu_len:56-79", + "tx_ampdu_len:80-103", + "tx_ampdu_len:104-127", + "tx_ampdu_len:128-151", + "tx_ampdu_len:152-175", + "tx_ampdu_len:176-199", + "tx_ampdu_len:200-223", + "tx_ampdu_len:224-247", + "ba_miss_count", + "tx_beamformer_ppdu_iBF", + "tx_beamformer_ppdu_eBF", + "tx_beamformer_rx_feedback_all", + "tx_beamformer_rx_feedback_he", + "tx_beamformer_rx_feedback_vht", + "tx_beamformer_rx_feedback_ht", + "tx_beamformer_rx_feedback_bw", /* zero based idx: 20, 40, 80, 160 */ + "tx_beamformer_rx_feedback_nc", + "tx_beamformer_rx_feedback_nr", + "tx_beamformee_ok_feedback_pkts", + "tx_beamformee_feedback_trig", + "tx_mu_beamforming", + "tx_mu_mpdu", + "tx_mu_successful_mpdu", + "tx_su_successful_mpdu", + "tx_msdu_pack_1", + "tx_msdu_pack_2", + "tx_msdu_pack_3", + "tx_msdu_pack_4", + "tx_msdu_pack_5", + "tx_msdu_pack_6", + "tx_msdu_pack_7", + "tx_msdu_pack_8", + + /* rx counters */ + "rx_fifo_full_cnt", + "rx_mpdu_cnt", + "channel_idle_cnt", + "rx_vector_mismatch_cnt", + "rx_delimiter_fail_cnt", + "rx_len_mismatch_cnt", + "rx_ampdu_cnt", + "rx_ampdu_bytes_cnt", + "rx_ampdu_valid_subframe_cnt", + "rx_ampdu_valid_subframe_b_cnt", + "rx_pfdrop_cnt", + "rx_vec_queue_overflow_drop_cnt", + "rx_ba_cnt", + + /* per vif counters */ + "v_tx_mode_cck", + "v_tx_mode_ofdm", + "v_tx_mode_ht", + "v_tx_mode_ht_gf", + "v_tx_mode_vht", + "v_tx_mode_he_su", + "v_tx_mode_he_ext_su", + "v_tx_mode_he_tb", + "v_tx_mode_he_mu", + "v_tx_mode_eht_su", + "v_tx_mode_eht_trig", + "v_tx_mode_eht_mu", + "v_tx_bw_20", + "v_tx_bw_40", + "v_tx_bw_80", + "v_tx_bw_160", + "v_tx_bw_320", + "v_tx_mcs_0", + "v_tx_mcs_1", + "v_tx_mcs_2", + "v_tx_mcs_3", + "v_tx_mcs_4", + "v_tx_mcs_5", + "v_tx_mcs_6", + "v_tx_mcs_7", + "v_tx_mcs_8", + "v_tx_mcs_9", + "v_tx_mcs_10", + "v_tx_mcs_11", + "v_tx_mcs_12", + "v_tx_mcs_13", + "v_tx_nss_1", + "v_tx_nss_2", + "v_tx_nss_3", + "v_tx_nss_4", +}; + +#define MT7996_SSTATS_LEN ARRAY_SIZE(mt7996_gstrings_stats) + +/* Ethtool related API */ +static +void mt7996_get_et_strings(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + u32 sset, u8 *data) +{ + if (sset == ETH_SS_STATS) + memcpy(data, mt7996_gstrings_stats, + sizeof(mt7996_gstrings_stats)); +} + +static +int mt7996_get_et_sset_count(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, int sset) +{ + if (sset == ETH_SS_STATS) + return MT7996_SSTATS_LEN; + + return 0; +} + +static void mt7996_ethtool_worker(void *wi_data, struct ieee80211_sta *sta) +{ + struct mt76_ethtool_worker_info *wi = wi_data; + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct mt7996_sta_link *msta_link = &msta->deflink; + + if (msta->vif->deflink.mt76.idx != wi->idx) + return; + + mt76_ethtool_worker(wi, &msta_link->wcid.stats, true); +} + +static +void mt7996_get_et_stats(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ethtool_stats *stats, u64 *data) +{ + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct mt7996_phy *phy = mt7996_vif_link_phy(&mvif->deflink); + struct mt76_mib_stats *mib = &phy->mib; + struct mt76_ethtool_worker_info wi = { + .data = data, + .idx = mvif->deflink.mt76.idx, + }; + /* See mt7996_ampdu_stat_read_phy, etc */ + int i, ei = 0; + + if (!phy) + return; + + mutex_lock(&dev->mt76.mutex); + + mt7996_mac_update_stats(phy); + + data[ei++] = mib->tx_ampdu_cnt; + data[ei++] = mib->tx_stop_q_empty_cnt; + data[ei++] = mib->tx_mpdu_attempts_cnt; + data[ei++] = mib->tx_mpdu_success_cnt; + data[ei++] = mib->tx_rwp_fail_cnt; + data[ei++] = mib->tx_rwp_need_cnt; + data[ei++] = mib->tx_bf_ebf_ppdu_cnt; + data[ei++] = mib->tx_bf_ibf_ppdu_cnt; + + /* Tx ampdu stat */ + for (i = 0; i < 15 /*ARRAY_SIZE(bound)*/; i++) + data[ei++] = phy->mt76->aggr_stats[i]; + data[ei++] = phy->mib.ba_miss_cnt; + + /* Tx Beamformer monitor */ + data[ei++] = mib->tx_bf_ibf_ppdu_cnt; + data[ei++] = mib->tx_bf_ebf_ppdu_cnt; + + /* Tx Beamformer Rx feedback monitor */ + data[ei++] = mib->tx_bf_rx_fb_all_cnt; + data[ei++] = mib->tx_bf_rx_fb_he_cnt; + data[ei++] = mib->tx_bf_rx_fb_vht_cnt; + data[ei++] = mib->tx_bf_rx_fb_ht_cnt; + + data[ei++] = mib->tx_bf_rx_fb_bw; + data[ei++] = mib->tx_bf_rx_fb_nc_cnt; + data[ei++] = mib->tx_bf_rx_fb_nr_cnt; + + /* Tx Beamformee Rx NDPA & Tx feedback report */ + data[ei++] = mib->tx_bf_fb_cpl_cnt; + data[ei++] = mib->tx_bf_fb_trig_cnt; + + /* Tx SU & MU counters */ + data[ei++] = mib->tx_mu_bf_cnt; + data[ei++] = mib->tx_mu_mpdu_cnt; + data[ei++] = mib->tx_mu_acked_mpdu_cnt; + data[ei++] = mib->tx_su_acked_mpdu_cnt; + + /* Tx amsdu info (pack-count histogram) */ + for (i = 0; i < ARRAY_SIZE(mib->tx_amsdu); i++) + data[ei++] = mib->tx_amsdu[i]; + + /* rx counters */ + data[ei++] = mib->rx_fifo_full_cnt; + data[ei++] = mib->rx_mpdu_cnt; + data[ei++] = mib->channel_idle_cnt; + data[ei++] = mib->rx_vector_mismatch_cnt; + data[ei++] = mib->rx_delimiter_fail_cnt; + data[ei++] = mib->rx_len_mismatch_cnt; + data[ei++] = mib->rx_ampdu_cnt; + data[ei++] = mib->rx_ampdu_bytes_cnt; + data[ei++] = mib->rx_ampdu_valid_subframe_cnt; + data[ei++] = mib->rx_ampdu_valid_subframe_bytes_cnt; + data[ei++] = mib->rx_pfdrop_cnt; + data[ei++] = mib->rx_vec_queue_overflow_drop_cnt; + data[ei++] = mib->rx_ba_cnt; + + /* Add values for all stations owned by this vif */ + wi.initial_stat_idx = ei; + ieee80211_iterate_stations_atomic(hw, mt7996_ethtool_worker, &wi); + + mutex_unlock(&dev->mt76.mutex); + + if (wi.sta_count == 0) + return; + + ei += wi.worker_stat_count; + if (ei != MT7996_SSTATS_LEN) + dev_err(dev->mt76.dev, "ei: %d MT7996_SSTATS_LEN: %d", + ei, (int)MT7996_SSTATS_LEN); +} + +static void +mt7996_twt_teardown_request(struct ieee80211_hw *hw, + struct ieee80211_sta *sta, + u8 flowid) +{ + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct mt7996_sta_link *msta_link = &msta->deflink; + struct mt7996_vif_link *link = &msta->vif->deflink; + struct mt7996_dev *dev = mt7996_hw_dev(hw); + + mutex_lock(&dev->mt76.mutex); + mt7996_mac_twt_teardown_flow(dev, link, msta_link, flowid); + mutex_unlock(&dev->mt76.mutex); +} + +static int +mt7996_set_radar_background(struct ieee80211_hw *hw, + struct cfg80211_chan_def *chandef) +{ + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mt7996_phy *phy; + int ret = -EINVAL; + bool running; + + if (chandef) + phy = mt7996_band_phy(dev, chandef->chan->band); + else + phy = dev->rdd2_phy; + if (!phy) + return -EINVAL; + + mutex_lock(&dev->mt76.mutex); + + if (dev->mt76.region == NL80211_DFS_UNSET) + goto out; + + if (dev->rdd2_phy && dev->rdd2_phy != phy) { + /* rdd2 is already locked */ + ret = -EBUSY; + goto out; + } + + /* rdd2 already configured on a radar channel */ + running = dev->rdd2_phy && + cfg80211_chandef_valid(&dev->rdd2_chandef) && + !!(dev->rdd2_chandef.chan->flags & IEEE80211_CHAN_RADAR); + + if (!chandef || running || + !(chandef->chan->flags & IEEE80211_CHAN_RADAR)) { + ret = mt7996_mcu_rdd_background_enable(phy, NULL); + if (ret) + goto out; + + if (!running) + goto update_phy; + } + + ret = mt7996_mcu_rdd_background_enable(phy, chandef); + if (ret) + goto out; + +update_phy: + dev->rdd2_phy = chandef ? phy : NULL; + if (chandef) + dev->rdd2_chandef = *chandef; +out: + mutex_unlock(&dev->mt76.mutex); + + return ret; +} + +static int +mt7996_net_fill_forward_path(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct net_device_path_ctx *ctx, + struct net_device_path *path) +{ + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mtk_wed_device *wed = &dev->mt76.mmio.wed; + struct mt7996_sta_link *msta_link; + struct mt7996_vif_link *link; + + link = mt7996_vif_link(dev, vif, msta->deflink_id); + if (!link) + return -EIO; + + msta_link = mt7996_sta_link(msta, msta->deflink_id); + if (!msta_link) + return -EIO; + + if (!msta_link->wcid.sta || msta_link->wcid.idx > MT7996_WTBL_STA) + return -EIO; + + if (dev->hif2 && + ((is_mt7996(&dev->mt76) && msta_link->wcid.phy_idx == MT_BAND2) || + (is_mt7992(&dev->mt76) && msta_link->wcid.phy_idx == MT_BAND1))) + wed = &dev->mt76.mmio.wed_hif2; + + if (!mtk_wed_device_active(wed) && + !mt76_npu_device_active(&dev->mt76)) + return -ENODEV; + + path->type = DEV_PATH_MTK_WDMA; + path->dev = ctx->dev; +#ifdef CONFIG_NET_MEDIATEK_SOC_WED + if (mtk_wed_device_active(wed)) + path->mtk_wdma.wdma_idx = wed->wdma_idx; + else +#endif + if (is_mt7996(&dev->mt76) && mt76_npu_device_active(&dev->mt76) && + msta_link->wcid.phy_idx == MT_BAND2) + path->mtk_wdma.wdma_idx = 1; + else + path->mtk_wdma.wdma_idx = link->mt76.band_idx; + path->mtk_wdma.bss = link->mt76.idx; + path->mtk_wdma.queue = 0; + path->mtk_wdma.wcid = msta_link->wcid.idx; + + if (ieee80211_hw_check(hw, SUPPORTS_AMSDU_IN_AMPDU) && + mtk_wed_is_amsdu_supported(wed)) + path->mtk_wdma.amsdu = msta_link->wcid.amsdu; + else + path->mtk_wdma.amsdu = 0; + ctx->dev = NULL; + + return 0; +} + +#if defined(CONFIG_NET_MEDIATEK_SOC_WED) || defined(CONFIG_MT7996_NPU) +static int +mt7996_net_setup_tc(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct net_device *netdev, enum tc_setup_type type, + void *type_data) +{ +#ifdef CONFIG_NET_MEDIATEK_SOC_WED + struct mt7996_dev *dev = mt7996_hw_dev(hw); + + if (mtk_wed_device_active(&dev->mt76.mmio.wed)) + return mt76_wed_net_setup_tc(hw, vif, netdev, type, + type_data); +#endif + return mt76_npu_net_setup_tc(hw, vif, netdev, type, type_data); +} +#endif + +static int +mt7996_change_vif_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u16 old_links, u16 new_links, + struct ieee80211_bss_conf *old[IEEE80211_MLD_MAX_NUM_LINKS]) +{ + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + int ret = 0; + + mutex_lock(&dev->mt76.mutex); + + if (!old_links) { + int idx; + + idx = get_own_mld_idx(dev->mld_idx_mask, true); + if (idx < 0) { + ret = -ENOSPC; + goto out; + } + mvif->mld_group_idx = idx; + dev->mld_idx_mask |= BIT_ULL(mvif->mld_group_idx); + + idx = get_free_idx(dev->mld_remap_idx_mask, 0, 15) - 1; + if (idx < 0) { + dev->mld_idx_mask &= ~BIT_ULL(mvif->mld_group_idx); + ret = -ENOSPC; + goto out; + } + mvif->mld_remap_idx = idx; + dev->mld_remap_idx_mask |= BIT_ULL(mvif->mld_remap_idx); + } + + if (new_links) + goto out; + + dev->mld_idx_mask &= ~BIT_ULL(mvif->mld_group_idx); + dev->mld_remap_idx_mask &= ~BIT_ULL(mvif->mld_remap_idx); + +out: + mutex_unlock(&dev->mt76.mutex); + + return ret; +} + +static void +mt7996_reconfig_complete(struct ieee80211_hw *hw, + enum ieee80211_reconfig_type reconfig_type) +{ + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mt7996_phy *phy; + + ieee80211_wake_queues(hw); + mt7996_for_each_phy(dev, phy) + ieee80211_queue_delayed_work(hw, &phy->mt76->mac_work, + MT7996_WATCHDOG_TIME); +} + +static int +mt7996_set_eml_op_mode(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_eml_params *eml_params) +{ + struct mt7996_dev *dev = mt7996_hw_dev(hw); + int ret; + + mutex_lock(&dev->mt76.mutex); + ret = mt7996_mcu_set_emlsr_mode(dev, vif, sta, eml_params); + mutex_unlock(&dev->mt76.mutex); + + return ret; +} + +const struct ieee80211_ops mt7996_ops = { + .add_chanctx = mt76_add_chanctx, + .remove_chanctx = mt76_remove_chanctx, + .change_chanctx = mt76_change_chanctx, + .assign_vif_chanctx = mt76_assign_vif_chanctx, + .unassign_vif_chanctx = mt76_unassign_vif_chanctx, + .switch_vif_chanctx = mt76_switch_vif_chanctx, + .tx = mt7996_tx, + .start = mt7996_start, + .stop = mt7996_stop, + .add_interface = mt7996_add_interface, + .remove_interface = mt7996_remove_interface, + .config = mt7996_config, + .conf_tx = mt7996_conf_tx, + .configure_filter = mt7996_configure_filter, + .vif_cfg_changed = mt7996_vif_cfg_changed, + .link_info_changed = mt7996_link_info_changed, + .sta_state = mt7996_sta_state, + .sta_pre_rcu_remove = mt76_sta_pre_rcu_remove, + .link_sta_rc_update = mt7996_link_sta_rc_update, + .set_key = mt7996_set_key, + .ampdu_action = mt7996_ampdu_action, + .set_rts_threshold = mt7996_set_rts_threshold, + .wake_tx_queue = mt76_wake_tx_queue, + .hw_scan = mt76_hw_scan, + .cancel_hw_scan = mt76_cancel_hw_scan, + .remain_on_channel = mt76_remain_on_channel, + .cancel_remain_on_channel = mt76_cancel_remain_on_channel, + .release_buffered_frames = mt76_release_buffered_frames, + .get_txpower = mt7996_get_txpower, + .channel_switch_beacon = mt7996_channel_switch_beacon, + .post_channel_switch = mt7996_post_channel_switch, + .get_stats = mt7996_get_stats, + .get_et_sset_count = mt7996_get_et_sset_count, + .get_et_stats = mt7996_get_et_stats, + .get_et_strings = mt7996_get_et_strings, + .get_tsf = mt7996_get_tsf, + .set_tsf = mt7996_set_tsf, + .offset_tsf = mt7996_offset_tsf, + .get_survey = mt76_get_survey, + .get_antenna = mt76_get_antenna, + .set_antenna = mt7996_set_antenna, + .set_bitrate_mask = mt7996_set_bitrate_mask, + .set_coverage_class = mt7996_set_coverage_class, + .sta_statistics = mt7996_sta_statistics, + .sta_set_4addr = mt7996_sta_set_4addr, + .sta_set_decap_offload = mt7996_sta_set_decap_offload, + .add_twt_setup = mt7996_mac_add_twt_setup, + .twt_teardown_request = mt7996_twt_teardown_request, +#ifdef CONFIG_MAC80211_DEBUGFS + .sta_add_debugfs = mt7996_sta_add_debugfs, + .link_sta_add_debugfs = mt7996_link_sta_add_debugfs, +#endif + .set_radar_background = mt7996_set_radar_background, + .net_fill_forward_path = mt7996_net_fill_forward_path, +#if defined(CONFIG_NET_MEDIATEK_SOC_WED) || defined(CONFIG_MT7996_NPU) + .net_setup_tc = mt7996_net_setup_tc, +#endif + .change_vif_links = mt7996_change_vif_links, + .change_sta_links = mt7996_mac_sta_change_links, + .reconfig_complete = mt7996_reconfig_complete, + .set_eml_op_mode = mt7996_set_eml_op_mode, +}; diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c new file mode 100644 index 000000000..cd7587ce5 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c @@ -0,0 +1,5560 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (C) 2022 MediaTek Inc. + */ + +#include +#include +#include "mt7996.h" +#include "mcu.h" +#include "mac.h" +#include "eeprom.h" + +#define fw_name(_dev, name, ...) ({ \ + char *_fw; \ + switch (mt76_chip(&(_dev)->mt76)) { \ + case MT7992_DEVICE_ID: \ + switch ((_dev)->var.type) { \ + case MT7992_VAR_TYPE_23: \ + _fw = MT7992_##name##_23; \ + break; \ + case MT7992_VAR_TYPE_24: \ + _fw = MT7992_##name##_24; \ + break; \ + default: \ + _fw = MT7992_##name; \ + } \ + break; \ + case MT7990_DEVICE_ID: \ + _fw = MT7990_##name; \ + break; \ + case MT7996_DEVICE_ID: \ + default: \ + switch ((_dev)->var.type) { \ + case MT7996_VAR_TYPE_233: \ + _fw = MT7996_##name##_233; \ + break; \ + default: \ + _fw = MT7996_##name; \ + } \ + break; \ + } \ + _fw; \ +}) + +struct mt7996_patch_hdr { + char build_date[16]; + char platform[4]; + __be32 hw_sw_ver; + __be32 patch_ver; + __be16 checksum; + u16 reserved; + struct { + __be32 patch_ver; + __be32 subsys; + __be32 feature; + __be32 n_region; + __be32 crc; + u32 reserved[11]; + } desc; +} __packed; + +struct mt7996_patch_sec { + __be32 type; + __be32 offs; + __be32 size; + union { + __be32 spec[13]; + struct { + __be32 addr; + __be32 len; + __be32 sec_key_idx; + __be32 align_len; + u32 reserved[9]; + } info; + }; +} __packed; + +struct mt7996_fw_trailer { + u8 chip_id; + u8 eco_code; + u8 n_region; + u8 format_ver; + u8 format_flag; + u8 reserved[2]; + char fw_ver[10]; + char build_date[15]; + u32 crc; +} __packed; + +struct mt7996_fw_region { + __le32 decomp_crc; + __le32 decomp_len; + __le32 decomp_blk_sz; + u8 reserved[4]; + __le32 addr; + __le32 len; + u8 feature_set; + u8 reserved1[15]; +} __packed; + +#define MCU_PATCH_ADDRESS 0x200000 + +#define HE_PHY(p, c) u8_get_bits(c, IEEE80211_HE_PHY_##p) +#define HE_MAC(m, c) u8_get_bits(c, IEEE80211_HE_MAC_##m) +#define EHT_PHY(p, c) u8_get_bits(c, IEEE80211_EHT_PHY_##p) + +static bool sr_scene_detect = true; +module_param(sr_scene_detect, bool, 0644); +MODULE_PARM_DESC(sr_scene_detect, "Enable firmware scene detection algorithm"); + +static u8 +mt7996_mcu_get_sta_nss(u16 mcs_map) +{ + u8 nss; + + for (nss = 8; nss > 0; nss--) { + u8 nss_mcs = (mcs_map >> (2 * (nss - 1))) & 3; + + if (nss_mcs != IEEE80211_VHT_MCS_NOT_SUPPORTED) + break; + } + + return nss - 1; +} + +static void +mt7996_mcu_set_sta_he_mcs(struct ieee80211_link_sta *link_sta, + struct mt7996_vif_link *link, + __le16 *he_mcs, u16 mcs_map) +{ + struct mt76_phy *mphy = mt76_vif_link_phy(&link->mt76); + int nss, max_nss = link_sta->rx_nss > 3 ? 4 : link_sta->rx_nss; + enum nl80211_band band; + const u16 *mask; + + if (!mphy) + return; + + band = mphy->chandef.chan->band; + mask = link->bitrate_mask.control[band].he_mcs; + + for (nss = 0; nss < max_nss; nss++) { + int mcs; + + switch ((mcs_map >> (2 * nss)) & 0x3) { + case IEEE80211_HE_MCS_SUPPORT_0_11: + mcs = GENMASK(11, 0); + break; + case IEEE80211_HE_MCS_SUPPORT_0_9: + mcs = GENMASK(9, 0); + break; + case IEEE80211_HE_MCS_SUPPORT_0_7: + mcs = GENMASK(7, 0); + break; + default: + mcs = 0; + } + + mcs = mcs ? fls(mcs & mask[nss]) - 1 : -1; + + switch (mcs) { + case 0 ... 7: + mcs = IEEE80211_HE_MCS_SUPPORT_0_7; + break; + case 8 ... 9: + mcs = IEEE80211_HE_MCS_SUPPORT_0_9; + break; + case 10 ... 11: + mcs = IEEE80211_HE_MCS_SUPPORT_0_11; + break; + default: + mcs = IEEE80211_HE_MCS_NOT_SUPPORTED; + break; + } + mcs_map &= ~(0x3 << (nss * 2)); + mcs_map |= mcs << (nss * 2); + } + + *he_mcs = cpu_to_le16(mcs_map); +} + +static void +mt7996_mcu_set_sta_vht_mcs(struct ieee80211_link_sta *link_sta, + __le16 *vht_mcs, const u16 *mask) +{ + u16 mcs, mcs_map = le16_to_cpu(link_sta->vht_cap.vht_mcs.rx_mcs_map); + int nss, max_nss = link_sta->rx_nss > 3 ? 4 : link_sta->rx_nss; + + for (nss = 0; nss < max_nss; nss++, mcs_map >>= 2) { + switch (mcs_map & 0x3) { + case IEEE80211_VHT_MCS_SUPPORT_0_9: + mcs = GENMASK(9, 0); + break; + case IEEE80211_VHT_MCS_SUPPORT_0_8: + mcs = GENMASK(8, 0); + break; + case IEEE80211_VHT_MCS_SUPPORT_0_7: + mcs = GENMASK(7, 0); + break; + default: + mcs = 0; + } + + vht_mcs[nss] = cpu_to_le16(mcs & mask[nss]); + } +} + +static void +mt7996_mcu_set_sta_ht_mcs(struct ieee80211_link_sta *link_sta, + u8 *ht_mcs, const u8 *mask) +{ + int nss, max_nss = link_sta->rx_nss > 3 ? 4 : link_sta->rx_nss; + + for (nss = 0; nss < max_nss; nss++) + ht_mcs[nss] = link_sta->ht_cap.mcs.rx_mask[nss] & mask[nss]; +} + +static int +mt7996_mcu_parse_response(struct mt76_dev *mdev, int cmd, + struct sk_buff *skb, int seq) +{ + struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76); + struct mt7996_mcu_rxd *rxd; + struct mt7996_mcu_uni_event *event; + int mcu_cmd = FIELD_GET(__MCU_CMD_FIELD_ID, cmd); + int ret = 0; + + if (!skb) { + dev_err(mdev->dev, "Message %08x (seq %d) timeout\n", + cmd, seq); + + if (!test_and_set_bit(MT76_MCU_RESET, &dev->mphy.state)) { + dev->recovery.restart = true; + wake_up(&dev->mt76.mcu.wait); + queue_work(dev->mt76.wq, &dev->reset_work); + wake_up(&dev->reset_wait); + } + + return -ETIMEDOUT; + } + + rxd = (struct mt7996_mcu_rxd *)skb->data; + if (seq != rxd->seq) + return -EAGAIN; + + if (cmd == MCU_CMD(PATCH_SEM_CONTROL)) { + skb_pull(skb, sizeof(*rxd) - 4); + ret = *skb->data; + } else if ((rxd->option & MCU_UNI_CMD_EVENT) && + rxd->eid == MCU_UNI_EVENT_RESULT) { + skb_pull(skb, sizeof(*rxd)); + event = (struct mt7996_mcu_uni_event *)skb->data; + ret = le32_to_cpu(event->status); + /* skip invalid event */ + if (mcu_cmd != le16_to_cpu(event->cid)) + ret = -EAGAIN; + } else { + skb_pull(skb, sizeof(struct mt7996_mcu_rxd)); + } + + return ret; +} + +static void +mt7996_mcu_set_timeout(struct mt76_dev *mdev, int cmd) +{ + mdev->mcu.timeout = 5 * HZ; + + if (!(cmd & __MCU_CMD_FIELD_UNI)) + return; + + switch (FIELD_GET(__MCU_CMD_FIELD_ID, cmd)) { + case MCU_UNI_CMD_THERMAL: + case MCU_UNI_CMD_TWT: + case MCU_UNI_CMD_GET_MIB_INFO: + case MCU_UNI_CMD_STA_REC_UPDATE: + case MCU_UNI_CMD_BSS_INFO_UPDATE: + mdev->mcu.timeout = 2 * HZ; + return; + case MCU_UNI_CMD_EFUSE_CTRL: + case MCU_UNI_CMD_EXT_EEPROM_CTRL: + mdev->mcu.timeout = 30 * HZ; + return; + default: + break; + } +} + +static int +mt7996_mcu_send_message(struct mt76_dev *mdev, struct sk_buff *skb, + int cmd, int *wait_seq) +{ + struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76); + int txd_len, mcu_cmd = FIELD_GET(__MCU_CMD_FIELD_ID, cmd); + struct mt76_connac2_mcu_uni_txd *uni_txd; + struct mt76_connac2_mcu_txd *mcu_txd; + enum mt76_mcuq_id qid; + __le32 *txd; + u32 val; + u8 seq; + + mt7996_mcu_set_timeout(mdev, cmd); + + seq = ++dev->mt76.mcu.msg_seq & 0xf; + if (!seq) + seq = ++dev->mt76.mcu.msg_seq & 0xf; + + if (cmd == MCU_CMD(FW_SCATTER)) { + qid = MT_MCUQ_FWDL; + goto exit; + } + + txd_len = cmd & __MCU_CMD_FIELD_UNI ? sizeof(*uni_txd) : sizeof(*mcu_txd); + txd = (__le32 *)skb_push(skb, txd_len); + if (test_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state) && mt7996_has_wa(dev)) + qid = MT_MCUQ_WA; + else + qid = MT_MCUQ_WM; + + val = FIELD_PREP(MT_TXD0_TX_BYTES, skb->len) | + FIELD_PREP(MT_TXD0_PKT_FMT, MT_TX_TYPE_CMD) | + FIELD_PREP(MT_TXD0_Q_IDX, MT_TX_MCU_PORT_RX_Q0); + txd[0] = cpu_to_le32(val); + + val = FIELD_PREP(MT_TXD1_HDR_FORMAT, MT_HDR_FORMAT_CMD); + txd[1] = cpu_to_le32(val); + + if (cmd & __MCU_CMD_FIELD_UNI) { + uni_txd = (struct mt76_connac2_mcu_uni_txd *)txd; + uni_txd->len = cpu_to_le16(skb->len - sizeof(uni_txd->txd)); + uni_txd->cid = cpu_to_le16(mcu_cmd); + uni_txd->s2d_index = MCU_S2D_H2CN; + uni_txd->pkt_type = MCU_PKT_ID; + uni_txd->seq = seq; + + uni_txd->option = MCU_CMD_UNI; + if (!(cmd & __MCU_CMD_FIELD_QUERY)) + uni_txd->option |= MCU_CMD_SET; + + if (wait_seq) + uni_txd->option |= MCU_CMD_ACK; + + if ((cmd & __MCU_CMD_FIELD_WA) && (cmd & __MCU_CMD_FIELD_WM)) + uni_txd->s2d_index = MCU_S2D_H2CN; + else if (cmd & __MCU_CMD_FIELD_WA) + uni_txd->s2d_index = MCU_S2D_H2C; + else if (cmd & __MCU_CMD_FIELD_WM) + uni_txd->s2d_index = MCU_S2D_H2N; + + goto exit; + } + + mcu_txd = (struct mt76_connac2_mcu_txd *)txd; + mcu_txd->len = cpu_to_le16(skb->len - sizeof(mcu_txd->txd)); + mcu_txd->pq_id = cpu_to_le16(MCU_PQ_ID(MT_TX_PORT_IDX_MCU, + MT_TX_MCU_PORT_RX_Q0)); + mcu_txd->pkt_type = MCU_PKT_ID; + mcu_txd->seq = seq; + + mcu_txd->cid = FIELD_GET(__MCU_CMD_FIELD_ID, cmd); + mcu_txd->set_query = MCU_Q_NA; + mcu_txd->ext_cid = FIELD_GET(__MCU_CMD_FIELD_EXT_ID, cmd); + if (mcu_txd->ext_cid) { + mcu_txd->ext_cid_ack = 1; + + if (cmd & __MCU_CMD_FIELD_QUERY) + mcu_txd->set_query = MCU_Q_QUERY; + else + mcu_txd->set_query = MCU_Q_SET; + } + + if (cmd & __MCU_CMD_FIELD_WA) + mcu_txd->s2d_index = MCU_S2D_H2C; + else + mcu_txd->s2d_index = MCU_S2D_H2N; + +exit: + if (wait_seq) + *wait_seq = seq; + + return mt76_tx_queue_skb_raw(dev, mdev->q_mcu[qid], skb, 0); +} + +int mt7996_mcu_wa_cmd(struct mt7996_dev *dev, int cmd, u32 a1, u32 a2, u32 a3) +{ + struct { + u8 _rsv[4]; + + __le16 tag; + __le16 len; + __le32 args[3]; + } __packed req = { + .args = { + cpu_to_le32(a1), + cpu_to_le32(a2), + cpu_to_le32(a3), + }, + }; + + if (mt7996_has_wa(dev)) + return mt76_mcu_send_msg(&dev->mt76, cmd, &req.args, + sizeof(req.args), false); + + req.tag = cpu_to_le16(cmd == MCU_WA_PARAM_CMD(QUERY) ? UNI_CMD_SDO_QUERY : + UNI_CMD_SDO_SET); + req.len = cpu_to_le16(sizeof(req) - 4); + + return mt76_mcu_send_msg(&dev->mt76, MCU_WA_UNI_CMD(SDO), &req, + sizeof(req), false); +} + +struct mt7996_mcu_countdown_data { + struct mt76_phy *mphy; + u8 omac_idx; +}; + +static void +mt7996_mcu_csa_finish(void *priv, u8 *mac, struct ieee80211_vif *vif) +{ + struct mt7996_mcu_countdown_data *cdata = (void *)priv; + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct ieee80211_bss_conf *link_conf = NULL; + unsigned long valid_links = vif->valid_links ?: BIT(0); + unsigned int link_id; + + if (vif->type == NL80211_IFTYPE_STATION) + return; + + for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) { + struct mt76_vif_link *mlink = + rcu_dereference(mvif->mt76.link[link_id]); + + if (mlink && mlink->band_idx == cdata->mphy->band_idx && + mlink->omac_idx == cdata->omac_idx) { + link_conf = rcu_dereference(vif->link_conf[link_id]); + break; + } + } + + if (!link_conf || !link_conf->csa_active) + return; + + ieee80211_csa_finish(vif, link_conf->link_id); +} + +static void +mt7996_mcu_cca_finish(void *priv, u8 *mac, struct ieee80211_vif *vif) +{ + struct mt7996_mcu_countdown_data *cdata = (void *)priv; + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct ieee80211_bss_conf *link_conf = NULL; + unsigned long valid_links = vif->valid_links ?: BIT(0); + unsigned int link_id; + + if (vif->type == NL80211_IFTYPE_STATION) + return; + + for_each_set_bit(link_id, &valid_links, IEEE80211_MLD_MAX_NUM_LINKS) { + struct mt76_vif_link *mlink = + rcu_dereference(mvif->mt76.link[link_id]); + + if (mlink && mlink->band_idx == cdata->mphy->band_idx && + mlink->omac_idx == cdata->omac_idx) { + link_conf = rcu_dereference(vif->link_conf[link_id]); + break; + } + } + + if (!link_conf || !link_conf->color_change_active) + return; + + ieee80211_color_change_finish(vif, link_conf->link_id); +} + +static void +mt7996_mcu_ie_countdown(struct mt7996_dev *dev, struct sk_buff *skb) +{ +#define UNI_EVENT_IE_COUNTDOWN_CSA 0 +#define UNI_EVENT_IE_COUNTDOWN_BCC 1 + struct header { + u8 band; + u8 rsv[3]; + }; + struct mt7996_mcu_rxd *rxd = (struct mt7996_mcu_rxd *)skb->data; + const char *data = (char *)&rxd[1], *tail; + struct header *hdr = (struct header *)data; + struct tlv *tlv = (struct tlv *)(data + 4); + struct mt7996_mcu_countdown_notify *event; + struct mt7996_mcu_countdown_data cdata; + + if (hdr->band >= ARRAY_SIZE(dev->mt76.phys)) + return; + + cdata.mphy = dev->mt76.phys[hdr->band]; + if (!cdata.mphy) + return; + + tail = skb->data + skb->len; + data += sizeof(*hdr); + while (data + sizeof(*tlv) < tail && le16_to_cpu(tlv->len)) { + event = (struct mt7996_mcu_countdown_notify *)tlv->data; + + cdata.omac_idx = event->omac_idx; + + switch (le16_to_cpu(tlv->tag)) { + case UNI_EVENT_IE_COUNTDOWN_CSA: + ieee80211_iterate_active_interfaces_atomic(mt76_hw(dev), + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7996_mcu_csa_finish, &cdata); + break; + case UNI_EVENT_IE_COUNTDOWN_BCC: + ieee80211_iterate_active_interfaces_atomic(mt76_hw(dev), + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7996_mcu_cca_finish, &cdata); + break; + default: + break; + } + + data += le16_to_cpu(tlv->len); + tlv = (struct tlv *)data; + } +} + +static void +mt7996_mcu_rx_radar_detected(struct mt7996_dev *dev, struct sk_buff *skb) +{ + struct mt76_phy *mphy = &dev->mt76.phy; + struct mt7996_mcu_rdd_report *r; + + r = (struct mt7996_mcu_rdd_report *)skb->data; + + switch (r->rdd_idx) { + case MT_RDD_IDX_BAND2: + mphy = dev->mt76.phys[MT_BAND2]; + break; + case MT_RDD_IDX_BAND1: + mphy = dev->mt76.phys[MT_BAND1]; + break; + case MT_RDD_IDX_BACKGROUND: + if (!dev->rdd2_phy) + goto err; + mphy = dev->rdd2_phy->mt76; + break; + default: + goto err; + } + + if (!mphy) + goto err; + + if (r->rdd_idx == MT_RDD_IDX_BACKGROUND) { + cfg80211_background_radar_event(mphy->hw->wiphy, + &dev->rdd2_chandef, + GFP_ATOMIC); + } else { + struct mt7996_phy *phy = mphy->priv; + + phy->rdd_tx_paused = true; + ieee80211_radar_detected(mphy->hw, NULL); + } + dev->hw_pattern++; + + return; + +err: + dev_err(dev->mt76.dev, "Invalid RDD idx %d\n", r->rdd_idx); +} + +static void +mt7996_mcu_rx_log_message(struct mt7996_dev *dev, struct sk_buff *skb) +{ +#define UNI_EVENT_FW_LOG_FORMAT 0 + struct mt7996_mcu_rxd *rxd = (struct mt7996_mcu_rxd *)skb->data; + const char *data = (char *)&rxd[1] + 4, *type; + struct tlv *tlv = (struct tlv *)data; + int len; + + if (!(rxd->option & MCU_UNI_CMD_EVENT)) { + len = skb->len - sizeof(*rxd); + data = (char *)&rxd[1]; + goto out; + } + + if (le16_to_cpu(tlv->tag) != UNI_EVENT_FW_LOG_FORMAT) + return; + + data += sizeof(*tlv) + 4; + len = le16_to_cpu(tlv->len) - sizeof(*tlv) - 4; + +out: + switch (rxd->s2d_index) { + case 0: + if (mt7996_debugfs_rx_log(dev, data, len)) + return; + + type = "WM"; + break; + case 2: + type = "WA"; + break; + default: + type = "unknown"; + break; + } + + wiphy_info(mt76_hw(dev)->wiphy, "%s: %.*s", type, len, data); +} + +static int +mt7996_mcu_update_tx_gi(struct rate_info *rate, struct all_sta_trx_rate *mcu_rate) +{ + switch (mcu_rate->tx_mode) { + case MT_PHY_TYPE_CCK: + case MT_PHY_TYPE_OFDM: + break; + case MT_PHY_TYPE_HT: + case MT_PHY_TYPE_HT_GF: + case MT_PHY_TYPE_VHT: + if (mcu_rate->tx_gi) + rate->flags |= RATE_INFO_FLAGS_SHORT_GI; + else + rate->flags &= ~RATE_INFO_FLAGS_SHORT_GI; + break; + case MT_PHY_TYPE_HE_SU: + case MT_PHY_TYPE_HE_EXT_SU: + case MT_PHY_TYPE_HE_TB: + case MT_PHY_TYPE_HE_MU: + if (mcu_rate->tx_gi > NL80211_RATE_INFO_HE_GI_3_2) + return -EINVAL; + rate->he_gi = mcu_rate->tx_gi; + break; + case MT_PHY_TYPE_EHT_SU: + case MT_PHY_TYPE_EHT_TRIG: + case MT_PHY_TYPE_EHT_MU: + if (mcu_rate->tx_gi > NL80211_RATE_INFO_EHT_GI_3_2) + return -EINVAL; + rate->eht_gi = mcu_rate->tx_gi; + break; + default: + return -EINVAL; + } + + return 0; +} + +static void +mt7996_mcu_rx_all_sta_info_event(struct mt7996_dev *dev, struct sk_buff *skb) +{ + struct mt7996_mcu_all_sta_info_event *res; + u16 i; + + skb_pull(skb, sizeof(struct mt7996_mcu_rxd)); + + res = (struct mt7996_mcu_all_sta_info_event *)skb->data; + + for (i = 0; i < le16_to_cpu(res->sta_num); i++) { + u8 ac; + u16 wlan_idx; + struct mt76_wcid *wcid; + + switch (le16_to_cpu(res->tag)) { + case UNI_ALL_STA_TXRX_RATE: + wlan_idx = le16_to_cpu(res->rate[i].wlan_idx); + wcid = mt76_wcid_ptr(dev, wlan_idx); + + if (!wcid) + break; + + if (mt7996_mcu_update_tx_gi(&wcid->rate, &res->rate[i])) + dev_err(dev->mt76.dev, "Failed to update TX GI\n"); + break; + case UNI_ALL_STA_TXRX_ADM_STAT: + wlan_idx = le16_to_cpu(res->adm_stat[i].wlan_idx); + wcid = mt76_wcid_ptr(dev, wlan_idx); + + if (!wcid) + break; + + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { + wcid->stats.tx_bytes += + le32_to_cpu(res->adm_stat[i].tx_bytes[ac]); + wcid->stats.rx_bytes += + le32_to_cpu(res->adm_stat[i].rx_bytes[ac]); + } + break; + case UNI_ALL_STA_TXRX_MSDU_COUNT: + wlan_idx = le16_to_cpu(res->msdu_cnt[i].wlan_idx); + wcid = mt76_wcid_ptr(dev, wlan_idx); + + if (!wcid) + break; + + wcid->stats.tx_packets += + le32_to_cpu(res->msdu_cnt[i].tx_msdu_cnt); + wcid->stats.rx_packets += + le32_to_cpu(res->msdu_cnt[i].rx_msdu_cnt); + break; + default: + break; + } + } +} + +static void +mt7996_mcu_rx_thermal_notify(struct mt7996_dev *dev, struct sk_buff *skb) +{ +#define THERMAL_NOTIFY_TAG 0x4 +#define THERMAL_NOTIFY 0x2 + struct mt76_phy *mphy = &dev->mt76.phy; + struct mt7996_mcu_thermal_notify *n; + struct mt7996_phy *phy; + + n = (struct mt7996_mcu_thermal_notify *)skb->data; + + if (le16_to_cpu(n->tag) != THERMAL_NOTIFY_TAG) + return; + + if (n->event_id != THERMAL_NOTIFY) + return; + + if (n->band_idx > MT_BAND2) + return; + + mphy = dev->mt76.phys[n->band_idx]; + if (!mphy) + return; + + phy = (struct mt7996_phy *)mphy->priv; + phy->throttle_state = n->duty_percent; +} + +static void +mt7996_mcu_rx_ext_event(struct mt7996_dev *dev, struct sk_buff *skb) +{ + struct mt7996_mcu_rxd *rxd = (struct mt7996_mcu_rxd *)skb->data; + + switch (rxd->ext_eid) { + case MCU_EXT_EVENT_FW_LOG_2_HOST: + mt7996_mcu_rx_log_message(dev, skb); + break; + default: + break; + } +} + +static void +mt7996_mcu_rx_unsolicited_event(struct mt7996_dev *dev, struct sk_buff *skb) +{ + struct mt7996_mcu_rxd *rxd = (struct mt7996_mcu_rxd *)skb->data; + + switch (rxd->eid) { + case MCU_EVENT_EXT: + mt7996_mcu_rx_ext_event(dev, skb); + break; + case MCU_UNI_EVENT_THERMAL: + mt7996_mcu_rx_thermal_notify(dev, skb); + break; + default: + break; + } + dev_kfree_skb(skb); +} + +static void +mt7996_mcu_wed_rro_event(struct mt7996_dev *dev, struct sk_buff *skb) +{ + struct mt7996_mcu_wed_rro_event *event = (void *)skb->data; + + if (!mt7996_has_hwrro(dev)) + return; + + skb_pull(skb, sizeof(struct mt7996_mcu_rxd) + 4); + + switch (le16_to_cpu(event->tag)) { + case UNI_WED_RRO_BA_SESSION_STATUS: { + struct mt7996_mcu_wed_rro_ba_event *e; + + while (skb->len >= sizeof(*e)) { + struct mt76_rx_tid *tid; + struct mt76_wcid *wcid; + u16 idx; + + e = (void *)skb->data; + idx = le16_to_cpu(e->wlan_id); + wcid = mt76_wcid_ptr(dev, idx); + if (!wcid || !wcid->sta) + break; + + if (e->tid >= ARRAY_SIZE(wcid->aggr)) + break; + + tid = rcu_dereference(wcid->aggr[e->tid]); + if (!tid) + break; + + tid->id = le16_to_cpu(e->id); + skb_pull(skb, sizeof(*e)); + } + break; + } + case UNI_WED_RRO_BA_SESSION_DELETE: { + struct mt7996_mcu_wed_rro_ba_delete_event *e; + + while (skb->len >= sizeof(*e)) { + struct mt7996_wed_rro_session_id *session; + + e = (void *)skb->data; + session = kzalloc_obj(*session, GFP_ATOMIC); + if (!session) + break; + + session->id = le16_to_cpu(e->session_id); + + spin_lock_bh(&dev->wed_rro.lock); + list_add_tail(&session->list, &dev->wed_rro.poll_list); + spin_unlock_bh(&dev->wed_rro.lock); + + ieee80211_queue_work(mt76_hw(dev), &dev->wed_rro.work); + skb_pull(skb, sizeof(*e)); + } + break; + } + default: + break; + } +} + +static void +mt7996_mcu_ps_transition(struct mt7996_dev *dev, u16 wcid_idx, bool ps) +{ + struct mt76_wcid *wcid; + + wcid = mt76_wcid_ptr(dev, wcid_idx); + if (!wcid || !wcid_to_sta(wcid)) + return; + + mt76_sta_ps_transition(&dev->mt76, wcid, ps); +} + +static void +mt7996_mcu_rx_ps_sync(struct mt7996_dev *dev, struct sk_buff *skb) +{ + struct mt7996_mcu_ps_sync_event *event = (void *)skb->data; + struct tlv *tlv; + int len; + + if (skb->len < sizeof(*event)) + return; + + skb_pull(skb, sizeof(*event)); + + len = skb->len; + while (len >= sizeof(*tlv)) { + u16 tag, tag_len; + + tlv = (struct tlv *)skb->data; + tag = le16_to_cpu(tlv->tag); + tag_len = le16_to_cpu(tlv->len); + /* a tag_len below the header size would not advance the buffer */ + if (tag_len < sizeof(*tlv) || tag_len > len) + break; + + switch (tag) { + case UNI_PS_CLIENT_INFO: { + struct mt7996_mcu_ps_client_info *info = (void *)tlv; + + if (tag_len < sizeof(*info)) + break; + + mt7996_mcu_ps_transition(dev, + le16_to_cpu(info->wlan_idx), + info->ps_bit); + break; + } + case UNI_PS_MULTI_CLIENT_INFO: { + struct mt7996_mcu_ps_multi_client_info *info = (void *)tlv; + u16 cnt; + int i; + + if (tag_len < sizeof(*info)) + break; + + cnt = min_t(u16, le16_to_cpu(info->sta_cnt), + (tag_len - sizeof(*info)) / sizeof(info->sta_ps_info[0])); + for (i = 0; i < cnt; i++) { + u16 entry = le16_to_cpu(info->sta_ps_info[i]); + + mt7996_mcu_ps_transition(dev, + FIELD_GET(MT7996_PS_MULTI_WCID, entry), + !!(entry & MT7996_PS_MULTI_PS_BIT)); + } + break; + } + case UNI_PS_MULTI_CLIENT_INFO_BITMAP: { + u8 *bitmap = tlv->data; + int bitmap_len = tag_len - sizeof(*tlv); + int i; + + for (i = 0; i < bitmap_len * 8; i++) + mt7996_mcu_ps_transition(dev, i, + !!(bitmap[i / 8] & BIT(i % 8))); + break; + } + } + + skb_pull(skb, tag_len); + len -= tag_len; + } +} + +static void +mt7996_mcu_uni_rx_unsolicited_event(struct mt7996_dev *dev, struct sk_buff *skb) +{ + struct mt7996_mcu_rxd *rxd = (struct mt7996_mcu_rxd *)skb->data; + + switch (rxd->eid) { + case MCU_UNI_EVENT_FW_LOG_2_HOST: + mt7996_mcu_rx_log_message(dev, skb); + break; + case MCU_UNI_EVENT_IE_COUNTDOWN: + mt7996_mcu_ie_countdown(dev, skb); + break; + case MCU_UNI_EVENT_RDD_REPORT: + mt7996_mcu_rx_radar_detected(dev, skb); + break; + case MCU_UNI_EVENT_ALL_STA_INFO: + mt7996_mcu_rx_all_sta_info_event(dev, skb); + break; + case MCU_UNI_EVENT_WED_RRO: + mt7996_mcu_wed_rro_event(dev, skb); + break; + case MCU_UNI_EVENT_PS_SYNC: + mt7996_mcu_rx_ps_sync(dev, skb); + break; + default: + break; + } + dev_kfree_skb(skb); +} + +void mt7996_mcu_rx_event(struct mt7996_dev *dev, struct sk_buff *skb) +{ + struct mt7996_mcu_rxd *rxd = (struct mt7996_mcu_rxd *)skb->data; + + if (rxd->option & MCU_UNI_CMD_UNSOLICITED_EVENT) { + mt7996_mcu_uni_rx_unsolicited_event(dev, skb); + return; + } + + /* WA still uses legacy event*/ + if (rxd->ext_eid == MCU_EXT_EVENT_FW_LOG_2_HOST || + !rxd->seq) + mt7996_mcu_rx_unsolicited_event(dev, skb); + else + mt76_mcu_rx_event(&dev->mt76, skb); +} + +static struct tlv * +mt7996_mcu_add_uni_tlv(struct sk_buff *skb, u16 tag, u16 len) +{ + struct tlv *ptlv = skb_put_zero(skb, len); + + ptlv->tag = cpu_to_le16(tag); + ptlv->len = cpu_to_le16(len); + + return ptlv; +} + +static void +mt7996_mcu_bss_rfch_tlv(struct sk_buff *skb, struct mt7996_phy *phy) +{ + static const u8 rlm_ch_band[] = { + [NL80211_BAND_2GHZ] = 1, + [NL80211_BAND_5GHZ] = 2, + [NL80211_BAND_6GHZ] = 3, + }; + struct cfg80211_chan_def *chandef = &phy->mt76->chandef; + struct bss_rlm_tlv *ch; + struct tlv *tlv; + int freq1 = chandef->center_freq1; + + tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_RLM, sizeof(*ch)); + + ch = (struct bss_rlm_tlv *)tlv; + ch->control_channel = chandef->chan->hw_value; + ch->center_chan = ieee80211_frequency_to_channel(freq1); + ch->bw = mt76_connac_chan_bw(chandef); + ch->tx_streams = hweight8(phy->mt76->antenna_mask); + ch->rx_streams = hweight8(phy->mt76->antenna_mask); + ch->band = rlm_ch_band[chandef->chan->band]; + + if (chandef->width == NL80211_CHAN_WIDTH_80P80) { + int freq2 = chandef->center_freq2; + + ch->center_chan2 = ieee80211_frequency_to_channel(freq2); + } +} + +static void +mt7996_mcu_bss_ra_tlv(struct sk_buff *skb, struct mt7996_phy *phy) +{ + struct bss_ra_tlv *ra; + struct tlv *tlv; + + tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_RA, sizeof(*ra)); + + ra = (struct bss_ra_tlv *)tlv; + ra->short_preamble = true; +} + +static void +mt7996_mcu_bss_he_tlv(struct sk_buff *skb, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, + struct mt7996_phy *phy) +{ +#define DEFAULT_HE_PE_DURATION 4 +#define DEFAULT_HE_DURATION_RTS_THRES 1023 + const struct ieee80211_sta_he_cap *cap; + struct bss_info_uni_he *he; + struct tlv *tlv; + + cap = mt76_connac_get_he_phy_cap(phy->mt76, vif); + if (!cap) + return; + + tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_HE_BASIC, sizeof(*he)); + + he = (struct bss_info_uni_he *)tlv; + he->he_pe_duration = link_conf->htc_trig_based_pkt_ext; + if (!he->he_pe_duration) + he->he_pe_duration = DEFAULT_HE_PE_DURATION; + + he->he_rts_thres = cpu_to_le16(link_conf->frame_time_rts_th); + if (!he->he_rts_thres) + he->he_rts_thres = cpu_to_le16(DEFAULT_HE_DURATION_RTS_THRES); + + he->max_nss_mcs[CMD_HE_MCS_BW80] = cap->he_mcs_nss_supp.tx_mcs_80; + he->max_nss_mcs[CMD_HE_MCS_BW160] = cap->he_mcs_nss_supp.tx_mcs_160; + he->max_nss_mcs[CMD_HE_MCS_BW8080] = cap->he_mcs_nss_supp.tx_mcs_80p80; +} + +static void +mt7996_mcu_bss_mbssid_tlv(struct sk_buff *skb, struct ieee80211_bss_conf *link_conf, + bool enable) +{ + struct bss_info_uni_mbssid *mbssid; + struct tlv *tlv; + + if (!link_conf->bssid_indicator && enable) + return; + + tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_11V_MBSSID, sizeof(*mbssid)); + + mbssid = (struct bss_info_uni_mbssid *)tlv; + + if (enable) { + mbssid->max_indicator = link_conf->bssid_indicator; + mbssid->mbss_idx = link_conf->bssid_index; + mbssid->tx_bss_omac_idx = 0; + } +} + +static void +mt7996_mcu_bss_bmc_tlv(struct sk_buff *skb, struct mt76_vif_link *mlink, + struct mt7996_phy *phy) +{ + struct bss_rate_tlv *bmc; + struct cfg80211_chan_def *chandef = &phy->mt76->chandef; + enum nl80211_band band = chandef->chan->band; + struct tlv *tlv; + u8 idx = mlink->mcast_rates_idx ? + mlink->mcast_rates_idx : mlink->basic_rates_idx; + + tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_RATE, sizeof(*bmc)); + + bmc = (struct bss_rate_tlv *)tlv; + + bmc->short_preamble = (band == NL80211_BAND_2GHZ); + bmc->bc_fixed_rate = idx; + bmc->mc_fixed_rate = idx; +} + +static void +mt7996_mcu_bss_txcmd_tlv(struct sk_buff *skb, bool en) +{ + struct bss_txcmd_tlv *txcmd; + struct tlv *tlv; + + tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_TXCMD, sizeof(*txcmd)); + + txcmd = (struct bss_txcmd_tlv *)tlv; + txcmd->txcmd_mode = en; +} + +static void +mt7996_mcu_bss_mld_tlv(struct sk_buff *skb, + struct ieee80211_bss_conf *link_conf, + struct mt7996_vif_link *link) +{ + struct ieee80211_vif *vif = link_conf->vif; + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct bss_mld_tlv *mld; + struct tlv *tlv; + + tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_MLD, sizeof(*mld)); + mld = (struct bss_mld_tlv *)tlv; + mld->own_mld_id = link->mld_idx; + mld->link_id = link_conf->link_id; + + if (ieee80211_vif_is_mld(vif)) { + mld->group_mld_id = mvif->mld_group_idx; + mld->remap_idx = mvif->mld_remap_idx; + memcpy(mld->mac_addr, vif->addr, ETH_ALEN); + } else { + mld->group_mld_id = 0xff; + mld->remap_idx = 0xff; + } +} + +static void +mt7996_mcu_bss_sec_tlv(struct sk_buff *skb, struct mt76_vif_link *mlink) +{ + struct bss_sec_tlv *sec; + struct tlv *tlv; + + tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_SEC, sizeof(*sec)); + + sec = (struct bss_sec_tlv *)tlv; + sec->cipher = mlink->cipher; +} + +static void +mt7996_mcu_bss_ifs_timing_tlv(struct sk_buff *skb, struct mt7996_phy *phy) +{ + struct bss_ifs_time_tlv *ifs_time; + struct tlv *tlv; + bool is_2ghz = phy->mt76->chandef.chan->band == NL80211_BAND_2GHZ; + + tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_IFS_TIME, sizeof(*ifs_time)); + + ifs_time = (struct bss_ifs_time_tlv *)tlv; + ifs_time->slot_valid = true; + ifs_time->sifs_valid = true; + ifs_time->rifs_valid = true; + ifs_time->eifs_valid = true; + + ifs_time->slot_time = cpu_to_le16(phy->slottime); + ifs_time->sifs_time = cpu_to_le16(10); + ifs_time->rifs_time = cpu_to_le16(2); + ifs_time->eifs_time = cpu_to_le16(is_2ghz ? 78 : 84); + + if (is_2ghz) { + ifs_time->eifs_cck_valid = true; + ifs_time->eifs_cck_time = cpu_to_le16(314); + } +} + +static int +mt7996_mcu_bss_basic_tlv(struct sk_buff *skb, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, + struct mt76_vif_link *mvif, + struct mt76_phy *phy, u16 wlan_idx, + bool enable) +{ + struct cfg80211_chan_def *chandef = &phy->chandef; + struct mt76_connac_bss_basic_tlv *bss; + u32 type = CONNECTION_INFRA_AP; + u16 sta_wlan_idx = wlan_idx; + struct tlv *tlv; + int idx; + + switch (vif->type) { + case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_MONITOR: + break; + case NL80211_IFTYPE_STATION: + if (enable) { + struct ieee80211_sta *sta; + + rcu_read_lock(); + sta = ieee80211_find_sta(vif, link_conf->bssid); + if (sta) { + struct mt7996_sta *msta = (void *)sta->drv_priv; + struct mt7996_sta_link *msta_link; + int link_id = link_conf->link_id; + + msta_link = mt7996_sta_link(msta, link_id); + if (msta_link) + sta_wlan_idx = msta_link->wcid.idx; + } + rcu_read_unlock(); + } + type = CONNECTION_INFRA_STA; + break; + case NL80211_IFTYPE_ADHOC: + type = CONNECTION_IBSS_ADHOC; + break; + default: + WARN_ON(1); + break; + } + + tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_BASIC, sizeof(*bss)); + + bss = (struct mt76_connac_bss_basic_tlv *)tlv; + bss->bmc_tx_wlan_idx = cpu_to_le16(wlan_idx); + bss->sta_idx = cpu_to_le16(sta_wlan_idx); + bss->conn_type = cpu_to_le32(type); + bss->omac_idx = mvif->omac_idx; + bss->band_idx = mvif->band_idx; + bss->wmm_idx = mvif->wmm_idx; + bss->conn_state = !enable; + bss->active = enable; + + idx = mvif->omac_idx > EXT_BSSID_START ? HW_BSSID_0 : mvif->omac_idx; + bss->hw_bss_idx = idx; + + if (vif->type == NL80211_IFTYPE_MONITOR) { + memcpy(bss->bssid, phy->macaddr, ETH_ALEN); + return 0; + } + + memcpy(bss->bssid, link_conf->bssid, ETH_ALEN); + bss->bcn_interval = cpu_to_le16(link_conf->beacon_int); + bss->dtim_period = link_conf->dtim_period; + bss->phymode = mt76_connac_get_phy_mode(phy, vif, + chandef->chan->band, NULL); + bss->phymode_ext = mt76_connac_get_phy_mode_ext(phy, link_conf, + chandef->chan->band); + + return 0; +} + +static struct sk_buff * +__mt7996_mcu_alloc_bss_req(struct mt76_dev *dev, struct mt76_vif_link *mvif, int len) +{ + struct bss_req_hdr hdr = { + .bss_idx = mvif->idx, + }; + struct sk_buff *skb; + + skb = mt76_mcu_msg_alloc(dev, NULL, len); + if (!skb) + return ERR_PTR(-ENOMEM); + + skb_put_data(skb, &hdr, sizeof(hdr)); + + return skb; +} + +int mt7996_mcu_add_bss_info(struct mt7996_phy *phy, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, + struct mt76_vif_link *mlink, + struct mt7996_sta_link *msta_link, int enable) +{ + struct mt7996_dev *dev = phy->dev; + struct sk_buff *skb; + + skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, mlink, + MT7996_BSS_UPDATE_MAX_SIZE); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + /* bss_basic must be first */ + mt7996_mcu_bss_basic_tlv(skb, vif, link_conf, mlink, phy->mt76, + msta_link->wcid.idx, enable); + mt7996_mcu_bss_sec_tlv(skb, mlink); + + if (vif->type == NL80211_IFTYPE_MONITOR) + goto out; + + if (enable) { + struct mt7996_vif_link *link; + + mt7996_mcu_bss_rfch_tlv(skb, phy); + mt7996_mcu_bss_bmc_tlv(skb, mlink, phy); + mt7996_mcu_bss_ra_tlv(skb, phy); + mt7996_mcu_bss_txcmd_tlv(skb, true); + mt7996_mcu_bss_ifs_timing_tlv(skb, phy); + + if (vif->bss_conf.he_support) + mt7996_mcu_bss_he_tlv(skb, vif, link_conf, phy); + + /* this tag is necessary no matter if the vif is MLD */ + link = container_of(mlink, struct mt7996_vif_link, mt76); + mt7996_mcu_bss_mld_tlv(skb, link_conf, link); + } + + mt7996_mcu_bss_mbssid_tlv(skb, link_conf, enable); + +out: + return mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_WMWA_UNI_CMD(BSS_INFO_UPDATE), true); +} + +int mt7996_mcu_update_bss_rfch(struct mt7996_phy *phy, struct mt7996_vif_link *link) +{ + struct mt7996_dev *dev = phy->dev; + struct sk_buff *skb; + + skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &link->mt76, + MT7996_BSS_UPDATE_MAX_SIZE); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + mt7996_mcu_bss_rfch_tlv(skb, phy); + + return mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_WMWA_UNI_CMD(BSS_INFO_UPDATE), true); +} + +int mt7996_mcu_set_protection(struct mt7996_phy *phy, struct mt7996_vif_link *link, + u8 ht_mode, bool use_cts_prot) +{ + struct mt7996_dev *dev = phy->dev; + struct bss_prot_tlv *prot; + struct sk_buff *skb; + struct tlv *tlv; + enum { + PROT_NONMEMBER = BIT(1), + PROT_20MHZ = BIT(2), + PROT_NONHT_MIXED = BIT(3), + PROT_LEGACY_ERP = BIT(5), + PROT_NONGF_STA = BIT(7), + }; + + /* The current firmware causes TX failure. Need further investigation */ + if (is_mt7996(&dev->mt76)) + return 0; + + skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &link->mt76, + MT7996_BSS_UPDATE_MAX_SIZE); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_PROTECT_INFO, + sizeof(*prot)); + prot = (struct bss_prot_tlv *)tlv; + + switch (ht_mode & IEEE80211_HT_OP_MODE_PROTECTION) { + case IEEE80211_HT_OP_MODE_PROTECTION_NONMEMBER: + prot->prot_mode = cpu_to_le32(PROT_NONMEMBER); + break; + case IEEE80211_HT_OP_MODE_PROTECTION_20MHZ: + prot->prot_mode = cpu_to_le32(PROT_20MHZ); + break; + case IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED: + prot->prot_mode = cpu_to_le32(PROT_NONHT_MIXED); + break; + } + + if (ht_mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT) + prot->prot_mode |= cpu_to_le32(PROT_NONGF_STA); + + if (use_cts_prot) + prot->prot_mode |= cpu_to_le32(PROT_LEGACY_ERP); + + return mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_WM_UNI_CMD(BSS_INFO_UPDATE), true); +} + +int mt7996_mcu_set_emlsr_mode(struct mt7996_dev *dev, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_eml_params *eml_params) +{ + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct mt7996_sta_link *msta_link; + struct sta_rec_eml_op *eml_op; + struct mt7996_vif_link *link; + struct sk_buff *skb; + struct tlv *tlv; + + msta_link = mt76_dereference(msta->link[eml_params->link_id], + &dev->mt76); + if (!msta_link) + return -EINVAL; + + link = mt7996_vif_link(dev, vif, eml_params->link_id); + if (!link) + return -EINVAL; + + skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &link->mt76, + &msta_link->wcid, + MT7996_STA_UPDATE_MAX_SIZE); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_EML_OP, sizeof(*eml_op)); + eml_op = (struct sta_rec_eml_op *)tlv; + eml_op->link_bitmap = 0; + + if (eml_params->control & IEEE80211_EML_CTRL_EMLSR_MODE) { + unsigned long link_bitmap = eml_params->link_bitmap; + unsigned int link_id; + + for_each_set_bit(link_id, &link_bitmap, + IEEE80211_MLD_MAX_NUM_LINKS) { + struct mt76_phy *mphy; + + link = mt7996_vif_link(dev, vif, link_id); + if (!link) + continue; + + mphy = mt76_vif_link_phy(&link->mt76); + if (!mphy) + continue; + + eml_op->link_bitmap |= BIT(mphy->band_idx); + } + } + + return mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true); +} + +int mt7996_mcu_set_timing(struct mt7996_phy *phy, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf) +{ + struct mt7996_dev *dev = phy->dev; + struct mt76_vif_link *mlink = mt76_vif_conf_link(&dev->mt76, vif, link_conf); + struct sk_buff *skb; + + skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, mlink, + MT7996_BSS_UPDATE_MAX_SIZE); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + mt7996_mcu_bss_ifs_timing_tlv(skb, phy); + + return mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_WMWA_UNI_CMD(BSS_INFO_UPDATE), true); +} + +static int +mt7996_mcu_sta_ba(struct mt7996_dev *dev, struct mt76_vif_link *mvif, + struct ieee80211_ampdu_params *params, + struct mt76_wcid *wcid, bool enable, bool tx) +{ + struct sta_rec_ba_uni *ba; + struct sk_buff *skb; + struct tlv *tlv; + + skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, mvif, wcid, + MT7996_STA_UPDATE_MAX_SIZE); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_BA, sizeof(*ba)); + + ba = (struct sta_rec_ba_uni *)tlv; + ba->ba_type = tx ? MT_BA_TYPE_ORIGINATOR : MT_BA_TYPE_RECIPIENT; + ba->winsize = cpu_to_le16(params->buf_size); + ba->ssn = cpu_to_le16(params->ssn); + ba->ba_en = enable << params->tid; + ba->amsdu = params->amsdu; + ba->tid = params->tid; + ba->ba_rdd_rro = !tx && enable && mt7996_has_hwrro(dev); + + return mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true); +} + +/** starec & wtbl **/ +int mt7996_mcu_add_tx_ba(struct mt7996_dev *dev, + struct ieee80211_ampdu_params *params, + struct ieee80211_vif *vif, bool enable) +{ + struct ieee80211_sta *sta = params->sta; + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct ieee80211_link_sta *link_sta; + unsigned int link_id; + int ret = 0; + + for_each_sta_active_link(vif, sta, link_sta, link_id) { + struct mt7996_sta_link *msta_link; + struct mt7996_vif_link *link; + + msta_link = mt7996_sta_link_protected(dev, msta, link_id); + if (!msta_link) + continue; + + link = mt7996_vif_link(dev, vif, link_id); + if (!link) + continue; + + if (enable && !params->amsdu) + msta_link->wcid.amsdu = false; + + ret = mt7996_mcu_sta_ba(dev, &link->mt76, params, + &msta_link->wcid, enable, true); + if (ret) + break; + } + + return ret; +} + +int mt7996_mcu_add_rx_ba(struct mt7996_dev *dev, + struct ieee80211_ampdu_params *params, + struct ieee80211_vif *vif, bool enable) +{ + struct ieee80211_sta *sta = params->sta; + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct ieee80211_link_sta *link_sta; + unsigned int link_id; + int ret = 0; + + for_each_sta_active_link(vif, sta, link_sta, link_id) { + struct mt7996_sta_link *msta_link; + struct mt7996_vif_link *link; + + msta_link = mt7996_sta_link_protected(dev, msta, link_id); + if (!msta_link) + continue; + + link = mt7996_vif_link(dev, vif, link_id); + if (!link) + continue; + + ret = mt7996_mcu_sta_ba(dev, &link->mt76, params, + &msta_link->wcid, enable, false); + if (ret) + break; + } + + return ret; +} + +static void +mt7996_mcu_sta_he_tlv(struct sk_buff *skb, + struct ieee80211_link_sta *link_sta, + struct mt7996_vif_link *link) +{ + struct ieee80211_he_cap_elem *elem = &link_sta->he_cap.he_cap_elem; + struct ieee80211_he_mcs_nss_supp mcs_map; + struct sta_rec_he_v2 *he; + struct tlv *tlv; + int i = 0; + + if (!link_sta->he_cap.has_he) + return; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HE_V2, sizeof(*he)); + + he = (struct sta_rec_he_v2 *)tlv; + for (i = 0; i < 11; i++) { + if (i < 6) + he->he_mac_cap[i] = elem->mac_cap_info[i]; + he->he_phy_cap[i] = elem->phy_cap_info[i]; + } + + mcs_map = link_sta->he_cap.he_mcs_nss_supp; + switch (link_sta->bandwidth) { + case IEEE80211_STA_RX_BW_160: + if (elem->phy_cap_info[0] & + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G) + mt7996_mcu_set_sta_he_mcs(link_sta, link, + &he->max_nss_mcs[CMD_HE_MCS_BW8080], + le16_to_cpu(mcs_map.rx_mcs_80p80)); + + mt7996_mcu_set_sta_he_mcs(link_sta, link, + &he->max_nss_mcs[CMD_HE_MCS_BW160], + le16_to_cpu(mcs_map.rx_mcs_160)); + fallthrough; + default: + mt7996_mcu_set_sta_he_mcs(link_sta, link, + &he->max_nss_mcs[CMD_HE_MCS_BW80], + le16_to_cpu(mcs_map.rx_mcs_80)); + break; + } + + he->pkt_ext = 2; +} + +static void +mt7996_mcu_sta_he_6g_tlv(struct sk_buff *skb, + struct ieee80211_link_sta *link_sta) +{ + struct sta_rec_he_6g_capa *he_6g; + struct tlv *tlv; + + if (!link_sta->he_6ghz_capa.capa) + return; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HE_6G, sizeof(*he_6g)); + + he_6g = (struct sta_rec_he_6g_capa *)tlv; + he_6g->capa = link_sta->he_6ghz_capa.capa; +} + +static void +mt7996_mcu_sta_eht_tlv(struct sk_buff *skb, + struct ieee80211_link_sta *link_sta) +{ + struct mt7996_sta *msta = (struct mt7996_sta *)link_sta->sta->drv_priv; + struct ieee80211_vif *vif = container_of((void *)msta->vif, + struct ieee80211_vif, drv_priv); + struct ieee80211_eht_mcs_nss_supp *mcs_map; + struct ieee80211_eht_cap_elem_fixed *elem; + struct sta_rec_eht *eht; + struct tlv *tlv; + + if (!link_sta->eht_cap.has_eht) + return; + + mcs_map = &link_sta->eht_cap.eht_mcs_nss_supp; + elem = &link_sta->eht_cap.eht_cap_elem; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_EHT, sizeof(*eht)); + + eht = (struct sta_rec_eht *)tlv; + eht->tid_bitmap = 0xff; + eht->mac_cap = cpu_to_le16(*(u16 *)elem->mac_cap_info); + eht->phy_cap = cpu_to_le64(*(u64 *)elem->phy_cap_info); + eht->phy_cap_ext = cpu_to_le64(elem->phy_cap_info[8]); + + if (vif->type != NL80211_IFTYPE_STATION && + (link_sta->he_cap.he_cap_elem.phy_cap_info[0] & + (IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G | + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G | + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G | + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G)) == 0) { + memcpy(eht->mcs_map_bw20, &mcs_map->only_20mhz, + sizeof(eht->mcs_map_bw20)); + return; + } + + memcpy(eht->mcs_map_bw80, &mcs_map->bw._80, sizeof(eht->mcs_map_bw80)); + memcpy(eht->mcs_map_bw160, &mcs_map->bw._160, sizeof(eht->mcs_map_bw160)); + memcpy(eht->mcs_map_bw320, &mcs_map->bw._320, sizeof(eht->mcs_map_bw320)); +} + +static void +mt7996_mcu_sta_ht_tlv(struct sk_buff *skb, struct ieee80211_link_sta *link_sta) +{ + struct sta_rec_ht_uni *ht; + struct tlv *tlv; + + if (!link_sta->ht_cap.ht_supported) + return; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HT, sizeof(*ht)); + + ht = (struct sta_rec_ht_uni *)tlv; + ht->ht_cap = cpu_to_le16(link_sta->ht_cap.cap); + ht->ampdu_param = u8_encode_bits(link_sta->ht_cap.ampdu_factor, + IEEE80211_HT_AMPDU_PARM_FACTOR) | + u8_encode_bits(link_sta->ht_cap.ampdu_density, + IEEE80211_HT_AMPDU_PARM_DENSITY); +} + +static void +mt7996_mcu_sta_vht_tlv(struct sk_buff *skb, struct ieee80211_link_sta *link_sta) +{ + struct sta_rec_vht *vht; + struct tlv *tlv; + + /* For 6G band, this tlv is necessary to let hw work normally */ + if (!link_sta->he_6ghz_capa.capa && !link_sta->vht_cap.vht_supported) + return; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_VHT, sizeof(*vht)); + + vht = (struct sta_rec_vht *)tlv; + vht->vht_cap = cpu_to_le32(link_sta->vht_cap.cap); + vht->vht_rx_mcs_map = link_sta->vht_cap.vht_mcs.rx_mcs_map; + vht->vht_tx_mcs_map = link_sta->vht_cap.vht_mcs.tx_mcs_map; +} + +static void +mt7996_mcu_sta_amsdu_tlv(struct mt7996_dev *dev, struct sk_buff *skb, + struct ieee80211_vif *vif, + struct ieee80211_link_sta *link_sta, + struct mt7996_sta_link *msta_link) +{ + struct sta_rec_amsdu *amsdu; + struct tlv *tlv; + + if (vif->type != NL80211_IFTYPE_STATION && + vif->type != NL80211_IFTYPE_MESH_POINT && + vif->type != NL80211_IFTYPE_AP) + return; + + if (!link_sta->agg.max_amsdu_len) + return; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HW_AMSDU, sizeof(*amsdu)); + amsdu = (struct sta_rec_amsdu *)tlv; + amsdu->max_amsdu_num = 8; + amsdu->amsdu_en = true; + msta_link->wcid.amsdu = true; + + switch (link_sta->agg.max_amsdu_len) { + case IEEE80211_MAX_MPDU_LEN_VHT_11454: + amsdu->max_mpdu_size = + IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454; + return; + case IEEE80211_MAX_MPDU_LEN_HT_7935: + case IEEE80211_MAX_MPDU_LEN_VHT_7991: + amsdu->max_mpdu_size = IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991; + return; + default: + amsdu->max_mpdu_size = IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895; + return; + } +} + +static void +mt7996_mcu_sta_muru_tlv(struct mt7996_dev *dev, struct sk_buff *skb, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_link_sta *link_sta) +{ + struct ieee80211_he_cap_elem *elem = &link_sta->he_cap.he_cap_elem; + struct sta_rec_muru *muru; + struct tlv *tlv; + + if (link_conf->vif->type != NL80211_IFTYPE_STATION && + link_conf->vif->type != NL80211_IFTYPE_AP) + return; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_MURU, sizeof(*muru)); + + muru = (struct sta_rec_muru *)tlv; + muru->cfg.mimo_dl_en = link_conf->eht_mu_beamformer || + link_conf->he_mu_beamformer || + link_conf->vht_mu_beamformer || + link_conf->vht_mu_beamformee; + muru->cfg.ofdma_dl_en = true; + + if (link_sta->vht_cap.vht_supported) + muru->mimo_dl.vht_mu_bfee = + !!(link_sta->vht_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE); + + if (!link_sta->he_cap.has_he) + return; + + muru->mimo_dl.partial_bw_dl_mimo = + HE_PHY(CAP6_PARTIAL_BANDWIDTH_DL_MUMIMO, elem->phy_cap_info[6]); + + muru->mimo_ul.full_ul_mimo = + HE_PHY(CAP2_UL_MU_FULL_MU_MIMO, elem->phy_cap_info[2]); + muru->mimo_ul.partial_ul_mimo = + HE_PHY(CAP2_UL_MU_PARTIAL_MU_MIMO, elem->phy_cap_info[2]); + + muru->ofdma_dl.punc_pream_rx = + HE_PHY(CAP1_PREAMBLE_PUNC_RX_MASK, elem->phy_cap_info[1]); + muru->ofdma_dl.he_20m_in_40m_2g = + HE_PHY(CAP8_20MHZ_IN_40MHZ_HE_PPDU_IN_2G, elem->phy_cap_info[8]); + muru->ofdma_dl.he_20m_in_160m = + HE_PHY(CAP8_20MHZ_IN_160MHZ_HE_PPDU, elem->phy_cap_info[8]); + muru->ofdma_dl.he_80m_in_160m = + HE_PHY(CAP8_80MHZ_IN_160MHZ_HE_PPDU, elem->phy_cap_info[8]); + + muru->ofdma_ul.t_frame_dur = + HE_MAC(CAP1_TF_MAC_PAD_DUR_MASK, elem->mac_cap_info[1]); + muru->ofdma_ul.mu_cascading = + HE_MAC(CAP2_MU_CASCADING, elem->mac_cap_info[2]); + muru->ofdma_ul.uo_ra = + HE_MAC(CAP3_OFDMA_RA, elem->mac_cap_info[3]); + muru->ofdma_ul.rx_ctrl_frame_to_mbss = + HE_MAC(CAP3_RX_CTRL_FRAME_TO_MULTIBSS, elem->mac_cap_info[3]); +} + +static inline bool +mt7996_is_ebf_supported(struct mt7996_phy *phy, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_link_sta *link_sta, bool bfee) +{ + int sts = hweight16(phy->mt76->chainmask); + + if (link_conf->vif->type != NL80211_IFTYPE_STATION && + link_conf->vif->type != NL80211_IFTYPE_AP) + return false; + + if (!bfee && sts < 2) + return false; + + if (link_sta->eht_cap.has_eht) { + struct ieee80211_sta_eht_cap *pc = &link_sta->eht_cap; + struct ieee80211_eht_cap_elem_fixed *pe = &pc->eht_cap_elem; + + if (bfee) + return link_conf->eht_su_beamformee && + EHT_PHY(CAP0_SU_BEAMFORMER, pe->phy_cap_info[0]); + else + return link_conf->eht_su_beamformer && + EHT_PHY(CAP0_SU_BEAMFORMEE, pe->phy_cap_info[0]); + } + + if (link_sta->he_cap.has_he) { + struct ieee80211_he_cap_elem *pe = &link_sta->he_cap.he_cap_elem; + + if (bfee) + return link_conf->he_su_beamformee && + HE_PHY(CAP3_SU_BEAMFORMER, pe->phy_cap_info[3]); + else + return link_conf->he_su_beamformer && + HE_PHY(CAP4_SU_BEAMFORMEE, pe->phy_cap_info[4]); + } + + if (link_sta->vht_cap.vht_supported) { + u32 cap = link_sta->vht_cap.cap; + + if (bfee) + return link_conf->vht_su_beamformee && + (cap & IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE); + else + return link_conf->vht_su_beamformer && + (cap & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE); + } + + return false; +} + +static void +mt7996_mcu_sta_sounding_rate(struct sta_rec_bf *bf, struct mt7996_phy *phy) +{ + bf->sounding_phy = MT_PHY_TYPE_OFDM; + bf->ndp_rate = 0; /* mcs0 */ + if (is_mt7996(phy->mt76->dev)) + bf->ndpa_rate = MT7996_CFEND_RATE_DEFAULT; /* ofdm 24m */ + else + bf->ndpa_rate = MT7992_CFEND_RATE_DEFAULT; /* ofdm 6m */ + + bf->rept_poll_rate = MT7996_CFEND_RATE_DEFAULT; /* ofdm 24m */ +} + +static void +mt7996_mcu_sta_bfer_ht(struct ieee80211_link_sta *link_sta, + struct mt7996_phy *phy, struct sta_rec_bf *bf, + bool explicit) +{ + struct ieee80211_mcs_info *mcs = &link_sta->ht_cap.mcs; + u8 n = 0; + + bf->tx_mode = MT_PHY_TYPE_HT; + + if ((mcs->tx_params & IEEE80211_HT_MCS_TX_RX_DIFF) && + (mcs->tx_params & IEEE80211_HT_MCS_TX_DEFINED)) + n = FIELD_GET(IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK, + mcs->tx_params); + else if (mcs->rx_mask[3]) + n = 3; + else if (mcs->rx_mask[2]) + n = 2; + else if (mcs->rx_mask[1]) + n = 1; + + bf->nrow = hweight8(phy->mt76->antenna_mask) - 1; + bf->ncol = min_t(u8, bf->nrow, n); + bf->ibf_ncol = explicit ? min_t(u8, MT7996_IBF_MAX_NC, bf->ncol) : + min_t(u8, MT7996_IBF_MAX_NC, n); +} + +static void +mt7996_mcu_sta_bfer_vht(struct ieee80211_link_sta *link_sta, + struct mt7996_phy *phy, struct sta_rec_bf *bf, + bool explicit) +{ + struct ieee80211_sta_vht_cap *pc = &link_sta->vht_cap; + struct ieee80211_sta_vht_cap *vc = &phy->mt76->sband_5g.sband.vht_cap; + u16 mcs_map = le16_to_cpu(pc->vht_mcs.rx_mcs_map); + u8 nss_mcs = mt7996_mcu_get_sta_nss(mcs_map); + u8 tx_ant = hweight8(phy->mt76->antenna_mask) - 1; + + bf->tx_mode = MT_PHY_TYPE_VHT; + + if (explicit) { + u8 sts, snd_dim; + + mt7996_mcu_sta_sounding_rate(bf, phy); + + sts = FIELD_GET(IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK, + pc->cap); + snd_dim = FIELD_GET(IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK, + vc->cap); + bf->nrow = min_t(u8, min_t(u8, snd_dim, sts), tx_ant); + bf->ncol = min_t(u8, nss_mcs, bf->nrow); + bf->ibf_ncol = min_t(u8, MT7996_IBF_MAX_NC, bf->ncol); + + if (link_sta->bandwidth == IEEE80211_STA_RX_BW_160) + bf->nrow = 1; + } else { + bf->nrow = tx_ant; + bf->ncol = min_t(u8, nss_mcs, bf->nrow); + bf->ibf_ncol = min_t(u8, MT7996_IBF_MAX_NC, nss_mcs); + + if (link_sta->bandwidth == IEEE80211_STA_RX_BW_160) + bf->ibf_nrow = 1; + } +} + +static void +mt7996_mcu_sta_bfer_he(struct ieee80211_link_sta *link_sta, + struct ieee80211_vif *vif, struct mt7996_phy *phy, + struct sta_rec_bf *bf, bool explicit) +{ + struct ieee80211_sta_he_cap *pc = &link_sta->he_cap; + struct ieee80211_he_cap_elem *pe = &pc->he_cap_elem; + u16 mcs_map = le16_to_cpu(pc->he_mcs_nss_supp.rx_mcs_80); + u8 nss_mcs = mt7996_mcu_get_sta_nss(mcs_map); + const struct ieee80211_he_cap_elem *ve; + const struct ieee80211_sta_he_cap *vc; + u8 snd_dim, sts; + + vc = mt76_connac_get_he_phy_cap(phy->mt76, vif); + if (!vc) + return; + + bf->tx_mode = MT_PHY_TYPE_HE_SU; + ve = &vc->he_cap_elem; + + mt7996_mcu_sta_sounding_rate(bf, phy); + + bf->trigger_su = HE_PHY(CAP6_TRIG_SU_BEAMFORMING_FB, + pe->phy_cap_info[6]); + bf->trigger_mu = HE_PHY(CAP6_TRIG_MU_BEAMFORMING_PARTIAL_BW_FB, + pe->phy_cap_info[6]); + snd_dim = HE_PHY(CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_MASK, + ve->phy_cap_info[5]); + sts = HE_PHY(CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_MASK, + pe->phy_cap_info[4]); + bf->nrow = min_t(u8, snd_dim, sts); + bf->ncol = min_t(u8, nss_mcs, bf->nrow); + bf->ibf_ncol = explicit ? min_t(u8, MT7996_IBF_MAX_NC, bf->ncol) : + min_t(u8, MT7996_IBF_MAX_NC, nss_mcs); + + if (link_sta->bandwidth != IEEE80211_STA_RX_BW_160) + return; + + /* go over for 160MHz and 80p80 */ + if (pe->phy_cap_info[0] & + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G) { + mcs_map = le16_to_cpu(pc->he_mcs_nss_supp.rx_mcs_160); + nss_mcs = mt7996_mcu_get_sta_nss(mcs_map); + + bf->ncol_gt_bw80 = nss_mcs; + } + + if (pe->phy_cap_info[0] & + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G) { + mcs_map = le16_to_cpu(pc->he_mcs_nss_supp.rx_mcs_80p80); + nss_mcs = mt7996_mcu_get_sta_nss(mcs_map); + + if (bf->ncol_gt_bw80) + bf->ncol_gt_bw80 = min_t(u8, bf->ncol_gt_bw80, nss_mcs); + else + bf->ncol_gt_bw80 = nss_mcs; + } + + snd_dim = HE_PHY(CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_MASK, + ve->phy_cap_info[5]); + sts = HE_PHY(CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_MASK, + pe->phy_cap_info[4]); + + bf->nrow_gt_bw80 = min_t(int, snd_dim, sts); +} + +static void +mt7996_mcu_sta_bfer_eht(struct ieee80211_link_sta *link_sta, + struct ieee80211_vif *vif, struct mt7996_phy *phy, + struct sta_rec_bf *bf, bool explicit) +{ + struct ieee80211_sta_eht_cap *pc = &link_sta->eht_cap; + struct ieee80211_eht_cap_elem_fixed *pe = &pc->eht_cap_elem; + struct ieee80211_eht_mcs_nss_supp *eht_nss = &pc->eht_mcs_nss_supp; + u8 nss_mcs = u8_get_bits(eht_nss->bw._80.rx_tx_mcs9_max_nss, + IEEE80211_EHT_MCS_NSS_RX) - 1; + const struct ieee80211_eht_cap_elem_fixed *ve; + const struct ieee80211_sta_eht_cap *vc; + u8 snd_dim, sts; + + vc = mt76_connac_get_eht_phy_cap(phy->mt76, vif); + if (!vc) + return; + + bf->tx_mode = MT_PHY_TYPE_EHT_MU; + ve = &vc->eht_cap_elem; + + mt7996_mcu_sta_sounding_rate(bf, phy); + + bf->trigger_su = EHT_PHY(CAP3_TRIG_SU_BF_FDBK, pe->phy_cap_info[3]); + bf->trigger_mu = EHT_PHY(CAP3_TRIG_MU_BF_PART_BW_FDBK, pe->phy_cap_info[3]); + snd_dim = EHT_PHY(CAP2_SOUNDING_DIM_80MHZ_MASK, ve->phy_cap_info[2]); + sts = EHT_PHY(CAP0_BEAMFORMEE_SS_80MHZ_MASK, pe->phy_cap_info[0]) + + (EHT_PHY(CAP1_BEAMFORMEE_SS_80MHZ_MASK, pe->phy_cap_info[1]) << 1); + bf->nrow = min_t(u8, snd_dim, sts); + bf->ncol = min_t(u8, nss_mcs, bf->nrow); + bf->ibf_ncol = explicit ? min_t(u8, MT7996_IBF_MAX_NC, bf->ncol) : + min_t(u8, MT7996_IBF_MAX_NC, nss_mcs); + + if (link_sta->bandwidth < IEEE80211_STA_RX_BW_160) + return; + + switch (link_sta->bandwidth) { + case IEEE80211_STA_RX_BW_160: + snd_dim = EHT_PHY(CAP2_SOUNDING_DIM_160MHZ_MASK, ve->phy_cap_info[2]); + sts = EHT_PHY(CAP1_BEAMFORMEE_SS_160MHZ_MASK, pe->phy_cap_info[1]); + nss_mcs = u8_get_bits(eht_nss->bw._160.rx_tx_mcs9_max_nss, + IEEE80211_EHT_MCS_NSS_RX) - 1; + + bf->nrow_gt_bw80 = min_t(u8, snd_dim, sts); + bf->ncol_gt_bw80 = nss_mcs; + break; + case IEEE80211_STA_RX_BW_320: + snd_dim = EHT_PHY(CAP2_SOUNDING_DIM_320MHZ_MASK, ve->phy_cap_info[2]) + + (EHT_PHY(CAP3_SOUNDING_DIM_320MHZ_MASK, + ve->phy_cap_info[3]) << 1); + sts = EHT_PHY(CAP1_BEAMFORMEE_SS_320MHZ_MASK, pe->phy_cap_info[1]); + nss_mcs = u8_get_bits(eht_nss->bw._320.rx_tx_mcs9_max_nss, + IEEE80211_EHT_MCS_NSS_RX) - 1; + + bf->nrow_gt_bw80 = min_t(u8, snd_dim, sts) << 4; + bf->ncol_gt_bw80 = nss_mcs << 4; + break; + default: + break; + } +} + +static void +mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_link_sta *link_sta, + struct mt7996_vif_link *link) +{ +#define EBF_MODE BIT(0) +#define IBF_MODE BIT(1) +#define BF_MAT_ORDER 4 + struct mt7996_phy *phy = mt7996_vif_link_phy(link); + struct ieee80211_vif *vif = link_conf->vif; + struct sta_rec_bf *bf; + struct tlv *tlv; + static const u8 matrix[BF_MAT_ORDER][BF_MAT_ORDER] = { + {0, 0, 0, 0}, + {1, 1, 0, 0}, /* 2x1, 2x2, 2x3, 2x4 */ + {2, 4, 4, 0}, /* 3x1, 3x2, 3x3, 3x4 */ + {3, 5, 6, 0} /* 4x1, 4x2, 4x3, 4x4 */ + }; + int tx_ant; + bool ebf; + + if (!phy) + return; + + if (!(link_sta->ht_cap.ht_supported || link_sta->he_cap.has_he)) + return; + + ebf = mt7996_is_ebf_supported(phy, link_conf, link_sta, false); + if (!ebf && !dev->ibf) + return; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_BF, sizeof(*bf)); + bf = (struct sta_rec_bf *)tlv; + + /* he/eht: eBF only, except mt7992 that has 5T on 5GHz also supports iBF + * vht: support eBF and iBF + * ht: iBF only, since mac80211 lacks of eBF support + */ + if (link_sta->eht_cap.has_eht) + mt7996_mcu_sta_bfer_eht(link_sta, vif, phy, bf, ebf); + else if (link_sta->he_cap.has_he) + mt7996_mcu_sta_bfer_he(link_sta, vif, phy, bf, ebf); + else if (link_sta->vht_cap.vht_supported) + mt7996_mcu_sta_bfer_vht(link_sta, phy, bf, ebf); + else if (link_sta->ht_cap.ht_supported) + mt7996_mcu_sta_bfer_ht(link_sta, phy, bf, ebf); + else + return; + + bf->bf_cap = ebf ? EBF_MODE : (dev->ibf ? IBF_MODE : 0); + tx_ant = hweight16(phy->mt76->chainmask) - 1; + if (is_mt7992(&dev->mt76) && tx_ant == 4) + bf->bf_cap |= IBF_MODE; + + bf->bw = link_sta->bandwidth; + bf->ibf_dbw = link_sta->bandwidth; + bf->ibf_nrow = tx_ant; + + if (link_sta->eht_cap.has_eht || link_sta->he_cap.has_he) + bf->ibf_timeout = is_mt7992(&dev->mt76) ? MT7992_IBF_TIMEOUT : + MT7996_IBF_TIMEOUT; + else if (!ebf && link_sta->bandwidth <= IEEE80211_STA_RX_BW_40 && !bf->ncol) + bf->ibf_timeout = MT7996_IBF_TIMEOUT_LEGACY; + else + bf->ibf_timeout = MT7996_IBF_TIMEOUT; + + if (bf->ncol < BF_MAT_ORDER) { + if (ebf) + bf->mem_20m = tx_ant < BF_MAT_ORDER ? + matrix[tx_ant][bf->ncol] : 0; + else + bf->mem_20m = bf->nrow < BF_MAT_ORDER ? + matrix[bf->nrow][bf->ncol] : 0; + } +} + +static void +mt7996_mcu_sta_bfee_tlv(struct mt7996_dev *dev, struct sk_buff *skb, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_link_sta *link_sta, + struct mt7996_vif_link *link) +{ + struct mt7996_phy *phy = mt7996_vif_link_phy(link); + struct sta_rec_bfee *bfee; + struct tlv *tlv; + u8 nrow = 0; + int tx_ant; + + if (!phy) + return; + + if (!(link_sta->vht_cap.vht_supported || link_sta->he_cap.has_he)) + return; + + if (!mt7996_is_ebf_supported(phy, link_conf, link_sta, true)) + return; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_BFEE, sizeof(*bfee)); + bfee = (struct sta_rec_bfee *)tlv; + + if (link_sta->he_cap.has_he) { + struct ieee80211_he_cap_elem *pe = &link_sta->he_cap.he_cap_elem; + + nrow = HE_PHY(CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_MASK, + pe->phy_cap_info[5]); + } else if (link_sta->vht_cap.vht_supported) { + struct ieee80211_sta_vht_cap *pc = &link_sta->vht_cap; + + nrow = FIELD_GET(IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK, + pc->cap); + } + + /* reply with identity matrix to avoid 2x2 BF negative gain */ + tx_ant = hweight8(phy->mt76->antenna_mask) - 1; + bfee->fb_identity_matrix = (nrow == 1 && tx_ant == 2); +} + +static void +mt7996_mcu_sta_tx_proc_tlv(struct sk_buff *skb) +{ + struct sta_rec_tx_proc *tx_proc; + struct tlv *tlv; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_TX_PROC, sizeof(*tx_proc)); + + tx_proc = (struct sta_rec_tx_proc *)tlv; + tx_proc->flag = cpu_to_le32(0); +} + +static void +mt7996_mcu_sta_hdrt_tlv(struct mt7996_dev *dev, struct sk_buff *skb) +{ + struct sta_rec_hdrt *hdrt; + struct tlv *tlv; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HDRT, sizeof(*hdrt)); + + hdrt = (struct sta_rec_hdrt *)tlv; + hdrt->hdrt_mode = 1; +} + +static void +mt7996_mcu_sta_hdr_trans_tlv(struct mt7996_dev *dev, struct sk_buff *skb, + struct ieee80211_vif *vif, struct mt76_wcid *wcid) +{ + struct sta_rec_hdr_trans *hdr_trans; + struct tlv *tlv; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HDR_TRANS, sizeof(*hdr_trans)); + hdr_trans = (struct sta_rec_hdr_trans *)tlv; + hdr_trans->dis_rx_hdr_tran = true; + + if (vif->type == NL80211_IFTYPE_STATION) + hdr_trans->to_ds = true; + else + hdr_trans->from_ds = true; + + if (!wcid) + return; + + hdr_trans->dis_rx_hdr_tran = !test_bit(MT_WCID_FLAG_HDR_TRANS, &wcid->flags); + if (test_bit(MT_WCID_FLAG_4ADDR, &wcid->flags)) { + hdr_trans->to_ds = true; + hdr_trans->from_ds = true; + } + + if (vif->type == NL80211_IFTYPE_MESH_POINT) { + hdr_trans->to_ds = true; + hdr_trans->from_ds = true; + hdr_trans->mesh = true; + } + + if (test_bit(MT_WCID_FLAG_TDLS_PEER, &wcid->flags)) { + hdr_trans->to_ds = false; + hdr_trans->from_ds = false; + } +} + +static enum mcu_mmps_mode +mt7996_mcu_get_mmps_mode(enum ieee80211_smps_mode smps) +{ + switch (smps) { + case IEEE80211_SMPS_OFF: + return MCU_MMPS_DISABLE; + case IEEE80211_SMPS_STATIC: + return MCU_MMPS_STATIC; + case IEEE80211_SMPS_DYNAMIC: + return MCU_MMPS_DYNAMIC; + default: + return MCU_MMPS_DISABLE; + } +} + +int mt7996_mcu_set_fixed_rate_ctrl(struct mt7996_dev *dev, + void *data, u16 version) +{ + struct uni_header hdr = {}; + struct ra_fixed_rate *req; + struct sk_buff *skb; + struct tlv *tlv; + int len; + + len = sizeof(hdr) + sizeof(*req); + + skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, len); + if (!skb) + return -ENOMEM; + + skb_put_data(skb, &hdr, sizeof(hdr)); + + tlv = mt7996_mcu_add_uni_tlv(skb, UNI_RA_FIXED_RATE, sizeof(*req)); + req = (struct ra_fixed_rate *)tlv; + req->version = cpu_to_le16(version); + memcpy(&req->rate, data, sizeof(req->rate)); + + return mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_WM_UNI_CMD(RA), true); +} + +int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev, struct mt7996_sta *msta, + void *data, u8 link_id, u32 field) +{ + struct mt7996_vif *mvif = msta->vif; + struct mt7996_sta_link *msta_link; + struct sta_rec_ra_fixed_uni *ra; + struct sta_phy_uni *phy = data; + struct mt76_vif_link *mlink; + struct sk_buff *skb; + int err = -ENODEV; + struct tlv *tlv; + + rcu_read_lock(); + + mlink = rcu_dereference(mvif->mt76.link[link_id]); + if (!mlink) + goto error_unlock; + + msta_link = mt7996_sta_link(msta, link_id); + if (!msta_link) + goto error_unlock; + + skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, mlink, + &msta_link->wcid, + MT7996_STA_UPDATE_MAX_SIZE); + if (IS_ERR(skb)) { + err = PTR_ERR(skb); + goto error_unlock; + } + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_RA_UPDATE, sizeof(*ra)); + ra = (struct sta_rec_ra_fixed_uni *)tlv; + + switch (field) { + case RATE_PARAM_AUTO: + break; + case RATE_PARAM_FIXED: + case RATE_PARAM_FIXED_MCS: + case RATE_PARAM_FIXED_GI: + case RATE_PARAM_FIXED_HE_LTF: + if (phy) + ra->phy = *phy; + break; + case RATE_PARAM_MMPS_UPDATE: { + struct ieee80211_sta *sta = wcid_to_sta(&msta_link->wcid); + struct ieee80211_link_sta *link_sta; + + link_sta = rcu_dereference(sta->link[link_id]); + if (!link_sta) { + dev_kfree_skb(skb); + goto error_unlock; + } + + ra->mmps_mode = mt7996_mcu_get_mmps_mode(link_sta->smps_mode); + break; + } + default: + break; + } + ra->field = cpu_to_le32(field); + + rcu_read_unlock(); + + return mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true); +error_unlock: + rcu_read_unlock(); + + return err; +} + +static int +mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev, struct mt7996_sta *msta, + struct ieee80211_vif *vif, u8 link_id) +{ + struct ieee80211_link_sta *link_sta; + struct cfg80211_bitrate_mask mask; + struct mt7996_sta_link *msta_link; + struct mt7996_vif_link *link; + struct sta_phy_uni phy = {}; + struct ieee80211_sta *sta; + int ret, nrates = 0, idx; + enum nl80211_band band; + struct mt76_phy *mphy; + bool has_he; + +#define __sta_phy_bitrate_mask_check(_mcs, _gi, _ht, _he) \ + do { \ + u8 i, gi = mask.control[band]._gi; \ + gi = (_he) ? gi : gi == NL80211_TXRATE_FORCE_SGI; \ + phy.sgi = gi; \ + phy.he_ltf = mask.control[band].he_ltf; \ + for (i = 0; i < ARRAY_SIZE(mask.control[band]._mcs); i++) { \ + if (!mask.control[band]._mcs[i]) \ + continue; \ + nrates += hweight16(mask.control[band]._mcs[i]); \ + phy.mcs = ffs(mask.control[band]._mcs[i]) - 1; \ + if (_ht) \ + phy.mcs += 8 * i; \ + } \ + } while (0) + + rcu_read_lock(); + + link = mt7996_vif_link(dev, vif, link_id); + if (!link) + goto error_unlock; + + msta_link = mt7996_sta_link(msta, link_id); + if (!msta_link) + goto error_unlock; + + sta = wcid_to_sta(&msta_link->wcid); + link_sta = rcu_dereference(sta->link[link_id]); + if (!link_sta) + goto error_unlock; + + mphy = mt76_vif_link_phy(&link->mt76); + if (!mphy) + goto error_unlock; + + band = mphy->chandef.chan->band; + has_he = link_sta->he_cap.has_he; + mask = link->bitrate_mask; + idx = msta_link->wcid.idx; + + if (has_he) { + __sta_phy_bitrate_mask_check(he_mcs, he_gi, 0, 1); + } else if (link_sta->vht_cap.vht_supported) { + __sta_phy_bitrate_mask_check(vht_mcs, gi, 0, 0); + } else if (link_sta->ht_cap.ht_supported) { + __sta_phy_bitrate_mask_check(ht_mcs, gi, 1, 0); + } else { + nrates = hweight32(mask.control[band].legacy); + phy.mcs = ffs(mask.control[band].legacy) - 1; + } + + rcu_read_unlock(); + +#undef __sta_phy_bitrate_mask_check + + /* fall back to auto rate control */ + if (mask.control[band].gi == NL80211_TXRATE_DEFAULT_GI && + mask.control[band].he_gi == GENMASK(7, 0) && + mask.control[band].he_ltf == GENMASK(7, 0) && + nrates != 1) + return 0; + + /* fixed single rate */ + if (nrates == 1) { + ret = mt7996_mcu_set_fixed_field(dev, msta, &phy, link_id, + RATE_PARAM_FIXED_MCS); + if (ret) + return ret; + } + + /* fixed GI */ + if (mask.control[band].gi != NL80211_TXRATE_DEFAULT_GI || + mask.control[band].he_gi != GENMASK(7, 0)) { + u32 addr; + + /* firmware updates only TXCMD but doesn't take WTBL into + * account, so driver should update here to reflect the + * actual txrate hardware sends out. + */ + addr = mt7996_mac_wtbl_lmac_addr(dev, idx, 7); + if (has_he) + mt76_rmw_field(dev, addr, GENMASK(31, 24), phy.sgi); + else + mt76_rmw_field(dev, addr, GENMASK(15, 12), phy.sgi); + + ret = mt7996_mcu_set_fixed_field(dev, msta, &phy, link_id, + RATE_PARAM_FIXED_GI); + if (ret) + return ret; + } + + /* fixed HE_LTF */ + if (mask.control[band].he_ltf != GENMASK(7, 0)) { + ret = mt7996_mcu_set_fixed_field(dev, msta, &phy, link_id, + RATE_PARAM_FIXED_HE_LTF); + if (ret) + return ret; + } + + return 0; + +error_unlock: + rcu_read_unlock(); + + return -ENODEV; +} + +static void +mt7996_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7996_dev *dev, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_link_sta *link_sta, + struct mt7996_vif_link *link) +{ +#define INIT_RCPI 180 + struct mt76_phy *mphy = mt76_vif_link_phy(&link->mt76); + struct cfg80211_bitrate_mask *mask = &link->bitrate_mask; + u32 cap = link_sta->sta->wme ? STA_CAP_WMM : 0; + struct cfg80211_chan_def *chandef; + struct sta_rec_ra_uni *ra; + enum nl80211_band band; + struct tlv *tlv; + u32 supp_rate; + + if (!mphy) + return; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_RA, sizeof(*ra)); + ra = (struct sta_rec_ra_uni *)tlv; + + chandef = &mphy->chandef; + band = chandef->chan->band; + supp_rate = link_sta->supp_rates[band]; + + ra->valid = true; + ra->auto_rate = true; + ra->phy_mode = mt76_connac_get_phy_mode(mphy, vif, band, link_sta); + ra->channel = chandef->chan->hw_value; + ra->bw = (link_sta->bandwidth == IEEE80211_STA_RX_BW_320) ? + CMD_CBW_320MHZ : link_sta->bandwidth; + ra->phy.bw = ra->bw; + ra->mmps_mode = mt7996_mcu_get_mmps_mode(link_sta->smps_mode); + + if (supp_rate) { + supp_rate &= mask->control[band].legacy; + ra->rate_len = hweight32(supp_rate); + + if (band == NL80211_BAND_2GHZ) { + ra->supp_mode = MODE_CCK; + ra->supp_cck_rate = supp_rate & GENMASK(3, 0); + + if (ra->rate_len > 4) { + ra->supp_mode |= MODE_OFDM; + ra->supp_ofdm_rate = supp_rate >> 4; + } + } else { + ra->supp_mode = MODE_OFDM; + ra->supp_ofdm_rate = supp_rate; + } + } + + if (link_sta->ht_cap.ht_supported) { + ra->supp_mode |= MODE_HT; + ra->af = link_sta->ht_cap.ampdu_factor; + ra->ht_gf = !!(link_sta->ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD); + + cap |= STA_CAP_HT; + if (link_sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20) + cap |= STA_CAP_SGI_20; + if (link_sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40) + cap |= STA_CAP_SGI_40; + if (link_sta->ht_cap.cap & IEEE80211_HT_CAP_TX_STBC) + cap |= STA_CAP_TX_STBC; + if (link_sta->ht_cap.cap & IEEE80211_HT_CAP_RX_STBC) + cap |= STA_CAP_RX_STBC; + if (link_conf->ht_ldpc && + (link_sta->ht_cap.cap & IEEE80211_HT_CAP_LDPC_CODING)) + cap |= STA_CAP_LDPC; + + mt7996_mcu_set_sta_ht_mcs(link_sta, ra->ht_mcs, + mask->control[band].ht_mcs); + ra->supp_ht_mcs = *(__le32 *)ra->ht_mcs; + } + + if (link_sta->vht_cap.vht_supported) { + u8 af; + + ra->supp_mode |= MODE_VHT; + af = FIELD_GET(IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK, + link_sta->vht_cap.cap); + ra->af = max_t(u8, ra->af, af); + + cap |= STA_CAP_VHT; + if (link_sta->vht_cap.cap & IEEE80211_VHT_CAP_SHORT_GI_80) + cap |= STA_CAP_VHT_SGI_80; + if (link_sta->vht_cap.cap & IEEE80211_VHT_CAP_SHORT_GI_160) + cap |= STA_CAP_VHT_SGI_160; + if (link_sta->vht_cap.cap & IEEE80211_VHT_CAP_TXSTBC) + cap |= STA_CAP_VHT_TX_STBC; + if (link_sta->vht_cap.cap & IEEE80211_VHT_CAP_RXSTBC_MASK) + cap |= STA_CAP_VHT_RX_STBC; + if ((vif->type != NL80211_IFTYPE_AP || link_conf->vht_ldpc) && + (link_sta->vht_cap.cap & IEEE80211_VHT_CAP_RXLDPC)) + cap |= STA_CAP_VHT_LDPC; + + mt7996_mcu_set_sta_vht_mcs(link_sta, ra->supp_vht_mcs, + mask->control[band].vht_mcs); + } + + if (link_sta->he_cap.has_he) { + ra->supp_mode |= MODE_HE; + cap |= STA_CAP_HE; + + if (link_sta->he_6ghz_capa.capa) + ra->af = le16_get_bits(link_sta->he_6ghz_capa.capa, + IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP); + } + ra->sta_cap = cpu_to_le32(cap); + + memset(ra->rx_rcpi, INIT_RCPI, sizeof(ra->rx_rcpi)); +} + +int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev, struct mt7996_sta *msta, + struct ieee80211_vif *vif, u8 link_id, + bool changed) +{ + struct ieee80211_bss_conf *link_conf; + struct ieee80211_link_sta *link_sta; + struct mt7996_sta_link *msta_link; + struct mt7996_vif_link *link; + struct ieee80211_sta *sta; + struct sk_buff *skb; + int ret = -ENODEV; + + rcu_read_lock(); + + link = mt7996_vif_link(dev, vif, link_id); + if (!link) + goto error_unlock; + + msta_link = mt7996_sta_link(msta, link_id); + if (!msta_link) + goto error_unlock; + + sta = wcid_to_sta(&msta_link->wcid); + link_sta = rcu_dereference(sta->link[link_id]); + if (!link_sta) + goto error_unlock; + + link_conf = rcu_dereference(vif->link_conf[link_id]); + if (!link_conf) + goto error_unlock; + + skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &link->mt76, + &msta_link->wcid, + MT7996_STA_UPDATE_MAX_SIZE); + if (IS_ERR(skb)) { + ret = PTR_ERR(skb); + goto error_unlock; + } + + /* firmware rc algorithm refers to sta_rec_he for HE control. + * once dev->rc_work changes the settings driver should also + * update sta_rec_he here. + */ + if (changed) + mt7996_mcu_sta_he_tlv(skb, link_sta, link); + + /* sta_rec_ra accommodates BW, NSS and only MCS range format + * i.e 0-{7,8,9} for VHT. + */ + mt7996_mcu_sta_rate_ctrl_tlv(skb, dev, vif, link_conf, link_sta, link); + + rcu_read_unlock(); + + ret = mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true); + if (ret) + return ret; + + return mt7996_mcu_add_rate_ctrl_fixed(dev, msta, vif, link_id); + +error_unlock: + rcu_read_unlock(); + + return ret; +} + +static int +mt7996_mcu_add_group(struct mt7996_dev *dev, struct mt7996_vif_link *link, + struct mt76_wcid *wcid) +{ +#define MT_STA_BSS_GROUP 1 + struct { + u8 __rsv1[4]; + + __le16 tag; + __le16 len; + __le16 wlan_idx; + u8 __rsv2[2]; + __le32 action; + __le32 val; + u8 __rsv3[8]; + } __packed req = { + .tag = cpu_to_le16(UNI_VOW_DRR_CTRL), + .len = cpu_to_le16(sizeof(req) - 4), + .action = cpu_to_le32(MT_STA_BSS_GROUP), + .val = cpu_to_le32(link->mt76.idx % 16), + .wlan_idx = cpu_to_le16(wcid->idx), + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(VOW), &req, + sizeof(req), true); +} + +int mt7996_mcu_mld_reconf_stop_link(struct mt7996_dev *dev, + struct ieee80211_vif *vif, + u16 removed_links) +{ + unsigned long rem_links = removed_links; + struct mld_reconf_stop_link *sl; + struct mld_req_hdr hdr = {}; + unsigned int link_id; + struct sk_buff *skb; + struct tlv *tlv; + + skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, sizeof(hdr) + sizeof(*sl)); + if (!skb) + return -ENOMEM; + + memcpy(hdr.mld_addr, vif->addr, ETH_ALEN); + skb_put_data(skb, &hdr, sizeof(hdr)); + + tlv = mt7996_mcu_add_uni_tlv(skb, UNI_CMD_MLD_RECONF_STOP_LINK, + sizeof(*sl)); + sl = (struct mld_reconf_stop_link *)tlv; + sl->link_bitmap = cpu_to_le16(removed_links); + + for_each_set_bit(link_id, &rem_links, IEEE80211_MLD_MAX_NUM_LINKS) { + struct mt7996_vif_link *link; + + link = mt7996_vif_link(dev, vif, link_id); + if (!link) + continue; + + sl->bss_idx[link_id] = link->mt76.idx; + } + + return mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_WM_UNI_CMD(MLD), + true); +} + +int mt7996_mcu_mld_link_oper(struct mt7996_dev *dev, + struct ieee80211_bss_conf *link_conf, + struct mt7996_vif_link *link, bool add) +{ + struct ieee80211_vif *vif = link_conf->vif; + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct bss_mld_link_op_tlv *mld_op; + struct sk_buff *skb; + struct tlv *tlv; + + skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &link->mt76, + MT7996_BSS_UPDATE_MAX_SIZE); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + tlv = mt7996_mcu_add_uni_tlv(skb, UNI_BSS_INFO_MLD_LINK_OP, + sizeof(*mld_op)); + mld_op = (struct bss_mld_link_op_tlv *)tlv; + mld_op->link_operation = add; + mld_op->own_mld_id = link->mld_idx; + mld_op->link_id = link_conf->link_id; + mld_op->group_mld_id = add ? mvif->mld_group_idx : 0xff; + mld_op->remap_idx = add ? mvif->mld_remap_idx : 0xff; + memcpy(mld_op->mac_addr, vif->addr, ETH_ALEN); + + return mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_WMWA_UNI_CMD(BSS_INFO_UPDATE), true); +} + +static void +mt7996_mcu_sta_mld_setup_tlv(struct mt7996_dev *dev, struct sk_buff *skb, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + unsigned int nlinks = hweight16(sta->valid_links); + struct mld_setup_link *mld_setup_link; + struct ieee80211_link_sta *link_sta; + struct sta_rec_mld_setup *mld_setup; + struct mt7996_sta_link *msta_link; + unsigned int link_id; + struct tlv *tlv; + + msta_link = mt7996_sta_link_protected(dev, msta, msta->deflink_id); + if (!msta_link) + return; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_MLD, + sizeof(struct sta_rec_mld_setup) + + sizeof(struct mld_setup_link) * nlinks); + + mld_setup = (struct sta_rec_mld_setup *)tlv; + memcpy(mld_setup->mld_addr, sta->addr, ETH_ALEN); + mld_setup->setup_wcid = cpu_to_le16(msta_link->wcid.idx); + mld_setup->primary_id = cpu_to_le16(msta_link->wcid.idx); + + if (nlinks > 1) { + msta_link = mt7996_sta_link_protected(dev, msta, + msta->seclink_id); + if (!msta_link) + return; + } + mld_setup->seconed_id = cpu_to_le16(msta_link->wcid.idx); + mld_setup->link_num = nlinks; + + mld_setup_link = (struct mld_setup_link *)mld_setup->link_info; + for_each_sta_active_link(vif, sta, link_sta, link_id) { + struct mt7996_vif_link *link; + + msta_link = mt7996_sta_link_protected(dev, msta, link_id); + if (!msta_link) + continue; + + link = mt7996_vif_link(dev, vif, link_id); + if (!link) + continue; + + mld_setup_link->wcid = cpu_to_le16(msta_link->wcid.idx); + mld_setup_link->bss_idx = link->mt76.idx; + mld_setup_link++; + } +} + +static void +mt7996_mcu_sta_eht_mld_tlv(struct mt7996_dev *dev, struct sk_buff *skb, + struct ieee80211_sta *sta) +{ + struct sta_rec_eht_mld *eht_mld; + struct tlv *tlv; + int i; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_EHT_MLD, sizeof(*eht_mld)); + eht_mld = (struct sta_rec_eht_mld *)tlv; + + for (i = 0; i < ARRAY_SIZE(eht_mld->str_cap); i++) + eht_mld->str_cap[i] = 0x7; +} + +int mt7996_mcu_add_sta(struct mt7996_dev *dev, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_link_sta *link_sta, + struct mt7996_vif_link *link, + struct mt7996_sta_link *msta_link, + int conn_state, bool newly) +{ + struct mt76_wcid *wcid = msta_link ? &msta_link->wcid : link->mt76.wcid; + struct ieee80211_sta *sta = link_sta ? link_sta->sta : NULL; + struct sk_buff *skb; + int ret; + + skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &link->mt76, wcid, + MT7996_STA_UPDATE_MAX_SIZE); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + /* starec basic */ + mt76_connac_mcu_sta_basic_tlv(&dev->mt76, skb, link_conf, link_sta, + conn_state, newly); + + if (conn_state == CONN_STATE_DISCONNECT) + goto out; + + /* starec hdr trans */ + mt7996_mcu_sta_hdr_trans_tlv(dev, skb, link_conf->vif, wcid); + /* starec tx proc */ + mt7996_mcu_sta_tx_proc_tlv(skb); + + /* tag order is in accordance with firmware dependency. */ + if (link_sta) { + /* starec hdrt mode */ + mt7996_mcu_sta_hdrt_tlv(dev, skb); + if (conn_state == CONN_STATE_CONNECT) { + /* starec bfer */ + mt7996_mcu_sta_bfer_tlv(dev, skb, link_conf, link_sta, + link); + /* starec bfee */ + mt7996_mcu_sta_bfee_tlv(dev, skb, link_conf, link_sta, + link); + } + /* starec ht */ + mt7996_mcu_sta_ht_tlv(skb, link_sta); + /* starec vht */ + mt7996_mcu_sta_vht_tlv(skb, link_sta); + /* starec uapsd */ + mt76_connac_mcu_sta_uapsd(skb, link_conf->vif, sta); + /* starec amsdu */ + mt7996_mcu_sta_amsdu_tlv(dev, skb, link_conf->vif, link_sta, + msta_link); + /* starec he */ + mt7996_mcu_sta_he_tlv(skb, link_sta, link); + /* starec he 6g*/ + mt7996_mcu_sta_he_6g_tlv(skb, link_sta); + /* starec eht */ + mt7996_mcu_sta_eht_tlv(skb, link_sta); + /* starec muru */ + mt7996_mcu_sta_muru_tlv(dev, skb, link_conf, link_sta); + + if (sta->mlo) { + mt7996_mcu_sta_mld_setup_tlv(dev, skb, link_conf->vif, + sta); + mt7996_mcu_sta_eht_mld_tlv(dev, skb, sta); + } + } + + ret = mt7996_mcu_add_group(dev, link, wcid); + if (ret) { + dev_kfree_skb(skb); + return ret; + } +out: + return mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true); +} + +int mt7996_mcu_teardown_mld_sta(struct mt7996_dev *dev, + struct mt7996_vif_link *link, + struct mt7996_sta_link *msta_link) +{ + struct sk_buff *skb; + + skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &link->mt76, + &msta_link->wcid, + MT7996_STA_UPDATE_MAX_SIZE); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + mt76_connac_mcu_add_tlv(skb, STA_REC_MLD_OFF, sizeof(struct tlv)); + + return mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true); +} + +void mt7996_mcu_update_sta_rec_bw(void *data, struct ieee80211_sta *sta) +{ + struct mt7996_vif_link *link = (struct mt7996_vif_link *)data; + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct mt7996_phy *phy = mt7996_vif_link_phy(link); + struct mt7996_sta_link *msta_link; + struct mt7996_dev *dev; + struct ieee80211_bss_conf *link_conf; + struct ieee80211_link_sta *link_sta; + struct ieee80211_vif *vif; + struct sk_buff *skb; + int link_id; + + if (!phy) + return; + + if (link->mt76.mvif != &msta->vif->mt76) + return; + + dev = phy->dev; + link_id = link->msta_link.wcid.link_id; + link_sta = link_sta_dereference_protected(sta, link_id); + if (!link_sta) + return; + + msta_link = mt7996_sta_link_protected(dev, msta, link_id); + if (!msta_link) + return; + + vif = container_of((void *)msta->vif, struct ieee80211_vif, drv_priv); + link_conf = link_conf_dereference_protected(vif, link_id); + if (!link_conf) + return; + + skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &link->mt76, + &msta_link->wcid, + MT7996_STA_UPDATE_MAX_SIZE); + if (IS_ERR(skb)) + return; + + mt7996_mcu_sta_bfer_tlv(dev, skb, link_conf, link_sta, link); + mt7996_mcu_sta_rate_ctrl_tlv(skb, dev, vif, link_conf, link_sta, link); + + mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true); +} + +static int +mt7996_mcu_sta_key_tlv(struct mt76_dev *dev, struct mt76_wcid *wcid, + struct sk_buff *skb, + struct ieee80211_key_conf *key, + enum set_key_cmd cmd) +{ + struct sta_rec_sec_uni *sec; + struct sec_key_uni *sec_key; + struct tlv *tlv; + u8 cipher; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_KEY_V2, sizeof(*sec)); + sec = (struct sta_rec_sec_uni *)tlv; + /* due to connac3 FW design, we only do remove key for BIGTK; even for + * removal, the field should be filled with SET_KEY + */ + sec->add = SET_KEY; + sec->n_cipher = 1; + sec_key = &sec->key[0]; + sec_key->wlan_idx = cpu_to_le16(wcid->idx); + sec_key->key_id = key->keyidx; + + if (cmd != SET_KEY) + return 0; + + cipher = mt76_connac_mcu_get_cipher(key->cipher); + if (cipher == MCU_CIPHER_NONE) + return -EOPNOTSUPP; + + sec_key->mgmt_prot = 0; + sec_key->cipher_id = cipher; + sec_key->cipher_len = sizeof(*sec_key); + sec_key->key_len = key->keylen; + sec_key->need_resp = 0; + memcpy(sec_key->key, key->key, key->keylen); + + if (cipher == MCU_CIPHER_TKIP) { + /* Rx/Tx MIC keys are swapped */ + memcpy(sec_key->key + 16, key->key + 24, 8); + memcpy(sec_key->key + 24, key->key + 16, 8); + return 0; + } + + if (sec_key->key_id != 6 && sec_key->key_id != 7) + return 0; + + switch (key->cipher) { + case WLAN_CIPHER_SUITE_AES_CMAC: + sec_key->cipher_id = MCU_CIPHER_BCN_PROT_CMAC_128; + break; + case WLAN_CIPHER_SUITE_BIP_GMAC_128: + sec_key->cipher_id = MCU_CIPHER_BCN_PROT_GMAC_128; + break; + case WLAN_CIPHER_SUITE_BIP_GMAC_256: + sec_key->cipher_id = MCU_CIPHER_BCN_PROT_GMAC_256; + break; + case WLAN_CIPHER_SUITE_BIP_CMAC_256: + if (!is_mt7990(dev)) + return -EOPNOTSUPP; + sec_key->cipher_id = MCU_CIPHER_BCN_PROT_CMAC_256; + break; + default: + return -EOPNOTSUPP; + } + + sec_key->bcn_mode = is_mt7990(dev) ? BP_HW_MODE : BP_SW_MODE; + + return 0; +} + +int mt7996_mcu_add_key(struct mt76_dev *dev, struct mt7996_vif_link *link, + struct ieee80211_key_conf *key, int mcu_cmd, + struct mt76_wcid *wcid, enum set_key_cmd cmd) +{ + struct sk_buff *skb; + int ret; + + skb = __mt76_connac_mcu_alloc_sta_req(dev, (struct mt76_vif_link *)link, + wcid, MT7996_STA_UPDATE_MAX_SIZE); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + ret = mt7996_mcu_sta_key_tlv(dev, wcid, skb, key, cmd); + if (ret) { + dev_kfree_skb(skb); + return ret; + } + + return mt76_mcu_skb_send_msg(dev, skb, mcu_cmd, true); +} + +int mt7996_mcu_add_dev_info(struct mt7996_phy *phy, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, + struct mt76_vif_link *mlink, bool enable) +{ + struct mt7996_dev *dev = phy->dev; + struct { + struct req_hdr { + u8 omac_idx; + u8 band_idx; + u8 __rsv[2]; + } __packed hdr; + struct req_tlv { + __le16 tag; + __le16 len; + u8 active; + u8 __rsv; + u8 omac_addr[ETH_ALEN]; + } __packed tlv; + } data = { + .hdr = { + .omac_idx = mlink->omac_idx, + .band_idx = mlink->band_idx, + }, + .tlv = { + .tag = cpu_to_le16(DEV_INFO_ACTIVE), + .len = cpu_to_le16(sizeof(struct req_tlv)), + .active = enable, + }, + }; + + memcpy(data.tlv.omac_addr, link_conf->addr, ETH_ALEN); + return mt76_mcu_send_msg(&dev->mt76, MCU_WMWA_UNI_CMD(DEV_INFO_UPDATE), + &data, sizeof(data), true); +} + +static void +mt7996_mcu_beacon_cntdwn(struct sk_buff *rskb, struct sk_buff *skb, + struct ieee80211_mutable_offsets *offs, + bool csa) +{ + struct bss_bcn_cntdwn_tlv *info; + struct tlv *tlv; + u16 tag; + + if (!offs->cntdwn_counter_offs[0]) + return; + + tag = csa ? UNI_BSS_INFO_BCN_CSA : UNI_BSS_INFO_BCN_BCC; + + tlv = mt7996_mcu_add_uni_tlv(rskb, tag, sizeof(*info)); + + info = (struct bss_bcn_cntdwn_tlv *)tlv; + info->cnt = skb->data[offs->cntdwn_counter_offs[0]]; + + /* abort the CCA countdown when starting CSA countdown */ + if (csa) { + struct bss_bcn_cntdwn_tlv *cca_info; + + tlv = mt7996_mcu_add_uni_tlv(rskb, UNI_BSS_INFO_BCN_BCC, + sizeof(*cca_info)); + cca_info = (struct bss_bcn_cntdwn_tlv *)tlv; + cca_info->cca.abort = true; + } +} + +static void +mt7996_mcu_beacon_mbss(struct sk_buff *rskb, struct sk_buff *skb, + struct bss_bcn_content_tlv *bcn, + struct ieee80211_mutable_offsets *offs) +{ + struct bss_bcn_mbss_tlv *mbss; + const struct element *elem; + struct tlv *tlv; + + tlv = mt7996_mcu_add_uni_tlv(rskb, UNI_BSS_INFO_BCN_MBSSID, sizeof(*mbss)); + + mbss = (struct bss_bcn_mbss_tlv *)tlv; + mbss->offset[0] = cpu_to_le16(offs->tim_offset); + mbss->bitmap = cpu_to_le32(1); + + for_each_element_id(elem, WLAN_EID_MULTIPLE_BSSID, + &skb->data[offs->mbssid_off], + skb->len - offs->mbssid_off) { + const struct element *sub_elem; + + if (elem->datalen < 2) + continue; + + for_each_element(sub_elem, elem->data + 1, elem->datalen - 1) { + const struct ieee80211_bssid_index *idx; + const u8 *idx_ie; + + /* not a valid BSS profile */ + if (sub_elem->id || sub_elem->datalen < 4) + continue; + + /* Find WLAN_EID_MULTI_BSSID_IDX + * in the merged nontransmitted profile + */ + idx_ie = cfg80211_find_ie(WLAN_EID_MULTI_BSSID_IDX, + sub_elem->data, sub_elem->datalen); + if (!idx_ie || idx_ie[1] < sizeof(*idx)) + continue; + + idx = (void *)(idx_ie + 2); + if (!idx->bssid_index || idx->bssid_index > 31) + continue; + + mbss->offset[idx->bssid_index] = cpu_to_le16(idx_ie - + skb->data); + mbss->bitmap |= cpu_to_le32(BIT(idx->bssid_index)); + } + } +} + +static void +mt7996_mcu_beacon_cont(struct mt7996_dev *dev, + struct ieee80211_bss_conf *link_conf, + struct mt7996_vif_link *link, + struct sk_buff *rskb, struct sk_buff *skb, + struct bss_bcn_content_tlv *bcn, + struct ieee80211_mutable_offsets *offs) +{ + u8 *buf, keyidx = link->msta_link.wcid.hw_key_idx2; + struct mt76_wcid *wcid; + + if (is_mt7990(&dev->mt76) && (keyidx == 6 || keyidx == 7)) + wcid = &link->msta_link.wcid; + else + wcid = &dev->mt76.global_wcid; + + bcn->pkt_len = cpu_to_le16(MT_TXD_SIZE + skb->len); + bcn->tim_ie_pos = cpu_to_le16(offs->tim_offset); + + if (offs->cntdwn_counter_offs[0]) { + u16 offset = offs->cntdwn_counter_offs[0]; + + if (link_conf->csa_active) + bcn->csa_ie_pos = cpu_to_le16(offset - 4); + if (link_conf->color_change_active) + bcn->bcc_ie_pos = cpu_to_le16(offset - 3); + } + + buf = (u8 *)bcn + sizeof(*bcn); + mt7996_mac_write_txwi(dev, (__le32 *)buf, skb, wcid, NULL, 0, 0, + BSS_CHANGED_BEACON, link_conf->link_id); + + memcpy(buf + MT_TXD_SIZE, skb->data, skb->len); +} + +int mt7996_mcu_add_beacon(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, bool enabled) +{ + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct mt7996_vif_link *link = mt7996_vif_conf_link(dev, vif, link_conf); + struct mt76_phy *mphy = link ? mt76_vif_link_phy(&link->mt76) : NULL; + struct mt76_vif_link *mlink = link ? &link->mt76 : NULL; + struct ieee80211_mutable_offsets offs; + struct ieee80211_tx_info *info; + struct sk_buff *skb, *rskb; + struct tlv *tlv; + struct bss_bcn_content_tlv *bcn; + int len, extra_len = 0; + + if (link_conf->nontransmitted) + return 0; + + if (!mlink) + return -EINVAL; + + if (mphy && mphy->offchannel) + enabled = false; + + rskb = __mt7996_mcu_alloc_bss_req(&dev->mt76, mlink, + MT7996_MAX_BSS_OFFLOAD_SIZE); + if (IS_ERR(rskb)) + return PTR_ERR(rskb); + + skb = ieee80211_beacon_get_template(hw, vif, &offs, link_conf->link_id); + if (enabled && !skb) { + dev_kfree_skb(rskb); + return -EINVAL; + } + + if (skb) { + if (skb->len > MT7996_MAX_BEACON_SIZE) { + dev_err(dev->mt76.dev, "Bcn size limit exceed\n"); + dev_kfree_skb(rskb); + dev_kfree_skb(skb); + return -EINVAL; + } + + extra_len = skb->len; + } + + len = ALIGN(sizeof(*bcn) + MT_TXD_SIZE + extra_len, 4); + tlv = mt7996_mcu_add_uni_tlv(rskb, UNI_BSS_INFO_BCN_CONTENT, len); + bcn = (struct bss_bcn_content_tlv *)tlv; + bcn->enable = enabled; + if (!bcn->enable) + goto out; + + info = IEEE80211_SKB_CB(skb); + info->hw_queue |= FIELD_PREP(MT_TX_HW_QUEUE_PHY, mlink->band_idx); + + mt7996_mcu_beacon_cont(dev, link_conf, link, rskb, skb, bcn, &offs); + if (link_conf->bssid_indicator) + mt7996_mcu_beacon_mbss(rskb, skb, bcn, &offs); + mt7996_mcu_beacon_cntdwn(rskb, skb, &offs, link_conf->csa_active); +out: + dev_kfree_skb(skb); + return mt76_mcu_skb_send_msg(&dev->mt76, rskb, + MCU_WMWA_UNI_CMD(BSS_INFO_UPDATE), true); +} + +int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev, + struct ieee80211_bss_conf *link_conf, + struct mt7996_vif_link *link, u32 changed) +{ +#define OFFLOAD_TX_MODE_SU BIT(0) +#define OFFLOAD_TX_MODE_MU BIT(1) + struct mt76_phy *mphy = mt76_vif_link_phy(&link->mt76); + struct ieee80211_vif *vif = link_conf->vif; + struct ieee80211_hw *hw = mt76_hw(dev); + struct mt76_wcid *wcid = &dev->mt76.global_wcid; + struct bss_inband_discovery_tlv *discov; + struct ieee80211_tx_info *info; + struct sk_buff *rskb, *skb = NULL; + struct cfg80211_chan_def *chandef; + enum nl80211_band band; + struct tlv *tlv; + u8 *buf, interval; + int len; + + if (!mphy) + return -EINVAL; + + chandef = &mphy->chandef; + band = chandef->chan->band; + + if (link_conf->nontransmitted) + return 0; + + rskb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &link->mt76, + MT7996_MAX_BSS_OFFLOAD_SIZE); + if (IS_ERR(rskb)) + return PTR_ERR(rskb); + + if (changed & BSS_CHANGED_FILS_DISCOVERY && + link_conf->fils_discovery.max_interval) { + interval = link_conf->fils_discovery.max_interval; + skb = ieee80211_get_fils_discovery_tmpl(hw, vif, + link_conf->link_id); + } else if (changed & BSS_CHANGED_UNSOL_BCAST_PROBE_RESP && + link_conf->unsol_bcast_probe_resp_interval) { + interval = link_conf->unsol_bcast_probe_resp_interval; + skb = ieee80211_get_unsol_bcast_probe_resp_tmpl(hw, vif, + link_conf->link_id); + } + + if (!skb) { + dev_kfree_skb(rskb); + return -EINVAL; + } + + if (skb->len > MT7996_MAX_BEACON_SIZE) { + dev_err(dev->mt76.dev, "inband discovery size limit exceed\n"); + dev_kfree_skb(rskb); + dev_kfree_skb(skb); + return -EINVAL; + } + + info = IEEE80211_SKB_CB(skb); + info->control.vif = vif; + info->band = band; + info->hw_queue |= FIELD_PREP(MT_TX_HW_QUEUE_PHY, mphy->band_idx); + + len = ALIGN(sizeof(*discov) + MT_TXD_SIZE + skb->len, 4); + tlv = mt7996_mcu_add_uni_tlv(rskb, UNI_BSS_INFO_OFFLOAD, len); + + discov = (struct bss_inband_discovery_tlv *)tlv; + discov->tx_mode = OFFLOAD_TX_MODE_SU; + /* 0: UNSOL PROBE RESP, 1: FILS DISCOV */ + discov->tx_type = !!(changed & BSS_CHANGED_FILS_DISCOVERY); + discov->tx_interval = interval; + discov->prob_rsp_len = cpu_to_le16(MT_TXD_SIZE + skb->len); + discov->enable = true; + discov->wcid = cpu_to_le16(MT7996_WTBL_RESERVED); + + buf = (u8 *)tlv + sizeof(*discov); + + mt7996_mac_write_txwi(dev, (__le32 *)buf, skb, wcid, NULL, 0, 0, + changed, link_conf->link_id); + + memcpy(buf + MT_TXD_SIZE, skb->data, skb->len); + + dev_kfree_skb(skb); + + return mt76_mcu_skb_send_msg(&dev->mt76, rskb, + MCU_WMWA_UNI_CMD(BSS_INFO_UPDATE), true); +} + +static int mt7996_driver_own(struct mt7996_dev *dev, u8 band) +{ + mt76_wr(dev, MT_TOP_LPCR_HOST_BAND(band), MT_TOP_LPCR_HOST_DRV_OWN); + if (!mt76_poll_msec(dev, MT_TOP_LPCR_HOST_BAND(band), + MT_TOP_LPCR_HOST_FW_OWN_STAT, 0, 500)) { + dev_err(dev->mt76.dev, "Timeout for driver own\n"); + return -EIO; + } + + /* clear irq when the driver own success */ + mt76_wr(dev, MT_TOP_LPCR_HOST_BAND_IRQ_STAT(band), + MT_TOP_LPCR_HOST_BAND_STAT); + + return 0; +} + +static u32 mt7996_patch_sec_mode(u32 key_info) +{ + u32 sec = u32_get_bits(key_info, MT7996_PATCH_SEC), key = 0; + + if (key_info == GENMASK(31, 0) || sec == MT7996_SEC_MODE_PLAIN) + return 0; + + if (sec == MT7996_SEC_MODE_AES) + key = u32_get_bits(key_info, MT7996_PATCH_AES_KEY); + else + key = u32_get_bits(key_info, MT7996_PATCH_SCRAMBLE_KEY); + + return MT7996_SEC_ENCRYPT | MT7996_SEC_IV | + u32_encode_bits(key, MT7996_SEC_KEY_IDX); +} + +static int mt7996_load_patch(struct mt7996_dev *dev) +{ + const struct mt7996_patch_hdr *hdr; + const struct firmware *fw = NULL; + int i, ret, sem; + + sem = mt76_connac_mcu_patch_sem_ctrl(&dev->mt76, 1); + switch (sem) { + case PATCH_IS_DL: + return 0; + case PATCH_NOT_DL_SEM_SUCCESS: + break; + default: + dev_err(dev->mt76.dev, "Failed to get patch semaphore\n"); + return -EAGAIN; + } + + ret = request_firmware(&fw, fw_name(dev, ROM_PATCH), dev->mt76.dev); + if (ret) + goto out; + + if (!fw || !fw->data || fw->size < sizeof(*hdr)) { + dev_err(dev->mt76.dev, "Invalid firmware\n"); + ret = -EINVAL; + goto out; + } + + hdr = (const struct mt7996_patch_hdr *)(fw->data); + + dev_info(dev->mt76.dev, "HW/SW Version: 0x%x, Build Time: %.16s\n", + be32_to_cpu(hdr->hw_sw_ver), hdr->build_date); + + for (i = 0; i < be32_to_cpu(hdr->desc.n_region); i++) { + struct mt7996_patch_sec *sec; + const u8 *dl; + u32 len, addr, sec_key_idx, mode = DL_MODE_NEED_RSP; + + sec = (struct mt7996_patch_sec *)(fw->data + sizeof(*hdr) + + i * sizeof(*sec)); + if ((be32_to_cpu(sec->type) & PATCH_SEC_TYPE_MASK) != + PATCH_SEC_TYPE_INFO) { + ret = -EINVAL; + goto out; + } + + addr = be32_to_cpu(sec->info.addr); + len = be32_to_cpu(sec->info.len); + sec_key_idx = be32_to_cpu(sec->info.sec_key_idx); + dl = fw->data + be32_to_cpu(sec->offs); + + mode |= mt7996_patch_sec_mode(sec_key_idx); + + ret = mt76_connac_mcu_init_download(&dev->mt76, addr, len, + mode); + if (ret) { + dev_err(dev->mt76.dev, "Download request failed\n"); + goto out; + } + + ret = __mt76_mcu_send_firmware(&dev->mt76, MCU_CMD(FW_SCATTER), + dl, len, 4096); + if (ret) { + dev_err(dev->mt76.dev, "Failed to send patch\n"); + goto out; + } + } + + ret = mt76_connac_mcu_start_patch(&dev->mt76); + if (ret) + dev_err(dev->mt76.dev, "Failed to start patch\n"); + +out: + sem = mt76_connac_mcu_patch_sem_ctrl(&dev->mt76, 0); + switch (sem) { + case PATCH_REL_SEM_SUCCESS: + break; + default: + ret = -EAGAIN; + dev_err(dev->mt76.dev, "Failed to release patch semaphore\n"); + break; + } + release_firmware(fw); + + return ret; +} + +static int +mt7996_mcu_send_ram_firmware(struct mt7996_dev *dev, + const struct mt7996_fw_trailer *hdr, + const u8 *data, enum mt7996_ram_type type) +{ + int i, offset = 0; + u32 override = 0, option = 0; + + for (i = 0; i < hdr->n_region; i++) { + const struct mt7996_fw_region *region; + int err; + u32 len, addr, mode; + + region = (const struct mt7996_fw_region *)((const u8 *)hdr - + (hdr->n_region - i) * sizeof(*region)); + /* DSP and WA use same mode */ + mode = mt76_connac_mcu_gen_dl_mode(&dev->mt76, + region->feature_set, + type != MT7996_RAM_TYPE_WM); + len = le32_to_cpu(region->len); + addr = le32_to_cpu(region->addr); + + if (region->feature_set & FW_FEATURE_OVERRIDE_ADDR) + override = addr; + + err = mt76_connac_mcu_init_download(&dev->mt76, addr, len, + mode); + if (err) { + dev_err(dev->mt76.dev, "Download request failed\n"); + return err; + } + + err = __mt76_mcu_send_firmware(&dev->mt76, MCU_CMD(FW_SCATTER), + data + offset, len, 4096); + if (err) { + dev_err(dev->mt76.dev, "Failed to send firmware.\n"); + return err; + } + + offset += len; + } + + if (override) + option |= FW_START_OVERRIDE; + + if (type == MT7996_RAM_TYPE_WA) + option |= FW_START_WORKING_PDA_CR4; + else if (type == MT7996_RAM_TYPE_DSP) + option |= FW_START_WORKING_PDA_DSP; + + return mt76_connac_mcu_start_firmware(&dev->mt76, override, option); +} + +static int __mt7996_load_ram(struct mt7996_dev *dev, const char *fw_type, + const char *fw_file, enum mt7996_ram_type ram_type) +{ + const struct mt7996_fw_trailer *hdr; + const struct firmware *fw; + int ret; + + ret = request_firmware(&fw, fw_file, dev->mt76.dev); + if (ret) + return ret; + + if (!fw || !fw->data || fw->size < sizeof(*hdr)) { + dev_err(dev->mt76.dev, "Invalid firmware\n"); + ret = -EINVAL; + goto out; + } + + hdr = (const void *)(fw->data + fw->size - sizeof(*hdr)); + dev_info(dev->mt76.dev, "%s Firmware Version: %.10s, Build Time: %.15s\n", + fw_type, hdr->fw_ver, hdr->build_date); + + ret = mt7996_mcu_send_ram_firmware(dev, hdr, fw->data, ram_type); + if (ret) { + dev_err(dev->mt76.dev, "Failed to start %s firmware\n", fw_type); + goto out; + } + + snprintf(dev->mt76.hw->wiphy->fw_version, + sizeof(dev->mt76.hw->wiphy->fw_version), + "%.10s-%.15s", hdr->fw_ver, hdr->build_date); + +out: + release_firmware(fw); + + return ret; +} + +static int mt7996_load_ram(struct mt7996_dev *dev) +{ + int ret; + + ret = __mt7996_load_ram(dev, "WM", fw_name(dev, FIRMWARE_WM), + MT7996_RAM_TYPE_WM); + if (ret) + return ret; + + if (!mt7996_has_wa(dev)) + return 0; + + ret = __mt7996_load_ram(dev, "DSP", fw_name(dev, FIRMWARE_DSP), + MT7996_RAM_TYPE_DSP); + if (ret) + return ret; + + return __mt7996_load_ram(dev, "WA", fw_name(dev, FIRMWARE_WA), + MT7996_RAM_TYPE_WA); +} + +static int +mt7996_firmware_state(struct mt7996_dev *dev, u8 fw_state) +{ + u32 state = FIELD_PREP(MT_TOP_MISC_FW_STATE, fw_state); + + if (!mt76_poll_msec(dev, MT_TOP_MISC, MT_TOP_MISC_FW_STATE, + state, 1000)) { + dev_err(dev->mt76.dev, "Timeout for initializing firmware\n"); + return -EIO; + } + return 0; +} + +static int +mt7996_mcu_restart(struct mt76_dev *dev) +{ + struct { + u8 __rsv1[4]; + + __le16 tag; + __le16 len; + u8 power_mode; + u8 __rsv2[3]; + } __packed req = { + .tag = cpu_to_le16(UNI_POWER_OFF), + .len = cpu_to_le16(sizeof(req) - 4), + .power_mode = 1, + }; + + return mt76_mcu_send_msg(dev, MCU_WM_UNI_CMD(POWER_CTRL), &req, + sizeof(req), false); +} + +static int mt7996_load_firmware(struct mt7996_dev *dev) +{ + u8 fw_state; + int ret; + + /* make sure fw is download state */ + if (mt7996_firmware_state(dev, FW_STATE_FW_DOWNLOAD)) { + /* restart firmware once */ + mt7996_mcu_restart(&dev->mt76); + ret = mt7996_firmware_state(dev, FW_STATE_FW_DOWNLOAD); + if (ret) { + dev_err(dev->mt76.dev, + "Firmware is not ready for download\n"); + return ret; + } + } + + ret = mt7996_load_patch(dev); + if (ret) + return ret; + + ret = mt7996_load_ram(dev); + if (ret) + return ret; + + fw_state = mt7996_has_wa(dev) ? FW_STATE_RDY : FW_STATE_NORMAL_TRX; + ret = mt7996_firmware_state(dev, fw_state); + if (ret) + return ret; + + mt76_queue_tx_cleanup(dev, dev->mt76.q_mcu[MT_MCUQ_FWDL], false); + + dev_dbg(dev->mt76.dev, "Firmware init done\n"); + + return 0; +} + +int mt7996_mcu_fw_log_2_host(struct mt7996_dev *dev, u8 type, u8 ctrl) +{ + struct { + u8 _rsv[4]; + + __le16 tag; + __le16 len; + u8 ctrl; + u8 interval; + u8 _rsv2[2]; + } __packed data = { + .tag = cpu_to_le16(UNI_WSYS_CONFIG_FW_LOG_CTRL), + .len = cpu_to_le16(sizeof(data) - 4), + .ctrl = ctrl, + }; + + if (type == MCU_FW_LOG_WA) + return mt76_mcu_send_msg(&dev->mt76, MCU_WA_UNI_CMD(WSYS_CONFIG), + &data, sizeof(data), true); + + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(WSYS_CONFIG), &data, + sizeof(data), true); +} + +int mt7996_mcu_fw_dbg_ctrl(struct mt7996_dev *dev, u32 module, u8 level) +{ + struct { + u8 _rsv[4]; + + __le16 tag; + __le16 len; + __le32 module_idx; + u8 level; + u8 _rsv2[3]; + } data = { + .tag = cpu_to_le16(UNI_WSYS_CONFIG_FW_DBG_CTRL), + .len = cpu_to_le16(sizeof(data) - 4), + .module_idx = cpu_to_le32(module), + .level = level, + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(WSYS_CONFIG), &data, + sizeof(data), false); +} + +static int mt7996_mcu_set_mwds(struct mt7996_dev *dev, bool enabled) +{ + struct { + u8 enable; + u8 _rsv[3]; + } __packed req = { + .enable = enabled + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_WA_EXT_CMD(MWDS_SUPPORT), &req, + sizeof(req), false); +} + +static void mt7996_add_rx_airtime_tlv(struct sk_buff *skb, u8 band_idx) +{ + struct vow_rx_airtime *req; + struct tlv *tlv; + + tlv = mt7996_mcu_add_uni_tlv(skb, UNI_VOW_RX_AT_AIRTIME_CLR_EN, sizeof(*req)); + req = (struct vow_rx_airtime *)tlv; + req->enable = true; + req->band = band_idx; + + tlv = mt7996_mcu_add_uni_tlv(skb, UNI_VOW_RX_AT_AIRTIME_EN, sizeof(*req)); + req = (struct vow_rx_airtime *)tlv; + req->enable = true; + req->band = band_idx; +} + +static int +mt7996_mcu_init_rx_airtime(struct mt7996_dev *dev) +{ + struct uni_header hdr = {}; + struct sk_buff *skb; + int len, num, i; + + num = 2 + 2 * (mt7996_band_valid(dev, MT_BAND1) + + mt7996_band_valid(dev, MT_BAND2)); + len = sizeof(hdr) + num * sizeof(struct vow_rx_airtime); + skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, len); + if (!skb) + return -ENOMEM; + + skb_put_data(skb, &hdr, sizeof(hdr)); + + for (i = 0; i < __MT_MAX_BAND; i++) { + if (mt7996_band_valid(dev, i)) + mt7996_add_rx_airtime_tlv(skb, i); + } + + return mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_WM_UNI_CMD(VOW), true); +} + +int mt7996_mcu_init_firmware(struct mt7996_dev *dev) +{ + int ret; + + /* force firmware operation mode into normal state, + * which should be set before firmware download stage. + */ + mt76_wr(dev, MT_SWDEF_MODE, MT_SWDEF_NORMAL_MODE); + + ret = mt7996_driver_own(dev, 0); + if (ret) + return ret; + /* set driver own for band1 when two hif exist */ + if (dev->hif2) { + ret = mt7996_driver_own(dev, 1); + if (ret) + return ret; + } + + ret = mt7996_load_firmware(dev); + if (ret) + return ret; + + set_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state); + ret = mt7996_mcu_fw_log_2_host(dev, MCU_FW_LOG_WM, 0); + if (ret) + return ret; + + if (mt7996_has_wa(dev)) { + ret = mt7996_mcu_fw_log_2_host(dev, MCU_FW_LOG_WA, 0); + if (ret) + return ret; + + ret = mt7996_mcu_set_mwds(dev, 1); + if (ret) + return ret; + } + + ret = mt7996_mcu_init_rx_airtime(dev); + if (ret) + return ret; + + return mt7996_mcu_wa_cmd(dev, MCU_WA_PARAM_CMD(SET), + MCU_WA_PARAM_RED, 0, 0); +} + +int mt7996_mcu_init(struct mt7996_dev *dev) +{ + static const struct mt76_mcu_ops mt7996_mcu_ops = { + .headroom = sizeof(struct mt76_connac2_mcu_txd), /* reuse */ + .mcu_skb_send_msg = mt7996_mcu_send_message, + .mcu_parse_response = mt7996_mcu_parse_response, + }; + + dev->mt76.mcu_ops = &mt7996_mcu_ops; + + return mt7996_mcu_init_firmware(dev); +} + +void mt7996_mcu_exit(struct mt7996_dev *dev) +{ + mt7996_mcu_restart(&dev->mt76); + if (mt7996_firmware_state(dev, FW_STATE_FW_DOWNLOAD)) { + dev_err(dev->mt76.dev, "Failed to exit mcu\n"); + goto out; + } + + mt76_wr(dev, MT_TOP_LPCR_HOST_BAND(0), MT_TOP_LPCR_HOST_FW_OWN); + if (dev->hif2) + mt76_wr(dev, MT_TOP_LPCR_HOST_BAND(1), + MT_TOP_LPCR_HOST_FW_OWN); +out: + skb_queue_purge(&dev->mt76.mcu.res_q); +} + +int mt7996_mcu_set_hdr_trans(struct mt7996_dev *dev, bool hdr_trans) +{ + struct { + u8 __rsv[4]; + } __packed hdr = {}; + struct hdr_trans_blacklist *req_blacklist; + struct hdr_trans_en *req_en; + struct sk_buff *skb; + struct tlv *tlv; + int len = MT7996_HDR_TRANS_MAX_SIZE + sizeof(hdr); + + skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, len); + if (!skb) + return -ENOMEM; + + skb_put_data(skb, &hdr, sizeof(hdr)); + + tlv = mt7996_mcu_add_uni_tlv(skb, UNI_HDR_TRANS_EN, sizeof(*req_en)); + req_en = (struct hdr_trans_en *)tlv; + req_en->enable = hdr_trans; + + tlv = mt7996_mcu_add_uni_tlv(skb, UNI_HDR_TRANS_VLAN, + sizeof(struct hdr_trans_vlan)); + + if (hdr_trans) { + tlv = mt7996_mcu_add_uni_tlv(skb, UNI_HDR_TRANS_BLACKLIST, + sizeof(*req_blacklist)); + req_blacklist = (struct hdr_trans_blacklist *)tlv; + req_blacklist->enable = 1; + req_blacklist->type = cpu_to_le16(ETH_P_PAE); + } + + return mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_WM_UNI_CMD(RX_HDR_TRANS), true); +} + +int mt7996_mcu_set_tx(struct mt7996_dev *dev, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf) +{ +#define MCU_EDCA_AC_PARAM 0 +#define WMM_AIFS_SET BIT(0) +#define WMM_CW_MIN_SET BIT(1) +#define WMM_CW_MAX_SET BIT(2) +#define WMM_TXOP_SET BIT(3) +#define WMM_PARAM_SET (WMM_AIFS_SET | WMM_CW_MIN_SET | \ + WMM_CW_MAX_SET | WMM_TXOP_SET) + struct mt7996_vif_link *link = mt7996_vif_conf_link(dev, vif, link_conf); + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + unsigned int link_id = link_conf->link_id; + struct mt7996_vif_link_info *link_info = &mvif->link_info[link_id]; + struct { + u8 bss_idx; + u8 __rsv[3]; + } __packed hdr = { + .bss_idx = link->mt76.idx, + }; + struct sk_buff *skb; + int len = sizeof(hdr) + IEEE80211_NUM_ACS * sizeof(struct edca); + int ac; + + skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, len); + if (!skb) + return -ENOMEM; + + skb_put_data(skb, &hdr, sizeof(hdr)); + + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { + struct ieee80211_tx_queue_params *q = &link_info->queue_params[ac]; + struct edca *e; + struct tlv *tlv; + + tlv = mt7996_mcu_add_uni_tlv(skb, MCU_EDCA_AC_PARAM, sizeof(*e)); + + e = (struct edca *)tlv; + e->set = WMM_PARAM_SET; + e->queue = ac; + e->aifs = q->aifs; + e->txop = cpu_to_le16(q->txop); + + if (q->cw_min) + e->cw_min = fls(q->cw_min); + else + e->cw_min = 5; + + if (q->cw_max) + e->cw_max = fls(q->cw_max); + else + e->cw_max = 10; + } + + return mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_WM_UNI_CMD(EDCA_UPDATE), true); +} + +int mt7996_mcu_set_fcc5_lpn(struct mt7996_dev *dev, int val) +{ + struct { + u8 _rsv[4]; + + __le16 tag; + __le16 len; + + __le32 ctrl; + __le16 min_lpn; + u8 rsv[2]; + } __packed req = { + .tag = cpu_to_le16(UNI_RDD_CTRL_SET_TH), + .len = cpu_to_le16(sizeof(req) - 4), + + .ctrl = cpu_to_le32(0x1), + .min_lpn = cpu_to_le16(val), + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(RDD_CTRL), + &req, sizeof(req), true); +} + +int mt7996_mcu_set_pulse_th(struct mt7996_dev *dev, + const struct mt7996_dfs_pulse *pulse) +{ + struct { + u8 _rsv[4]; + + __le16 tag; + __le16 len; + + __le32 ctrl; + + __le32 max_width; /* us */ + __le32 max_pwr; /* dbm */ + __le32 min_pwr; /* dbm */ + __le32 min_stgr_pri; /* us */ + __le32 max_stgr_pri; /* us */ + __le32 min_cr_pri; /* us */ + __le32 max_cr_pri; /* us */ + } __packed req = { + .tag = cpu_to_le16(UNI_RDD_CTRL_SET_TH), + .len = cpu_to_le16(sizeof(req) - 4), + + .ctrl = cpu_to_le32(0x3), + +#define __req_field(field) .field = cpu_to_le32(pulse->field) + __req_field(max_width), + __req_field(max_pwr), + __req_field(min_pwr), + __req_field(min_stgr_pri), + __req_field(max_stgr_pri), + __req_field(min_cr_pri), + __req_field(max_cr_pri), +#undef __req_field + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(RDD_CTRL), + &req, sizeof(req), true); +} + +int mt7996_mcu_set_radar_th(struct mt7996_dev *dev, int index, + const struct mt7996_dfs_pattern *pattern) +{ + struct { + u8 _rsv[4]; + + __le16 tag; + __le16 len; + + __le32 ctrl; + __le16 radar_type; + + u8 enb; + u8 stgr; + u8 min_crpn; + u8 max_crpn; + u8 min_crpr; + u8 min_pw; + __le32 min_pri; + __le32 max_pri; + u8 max_pw; + u8 min_crbn; + u8 max_crbn; + u8 min_stgpn; + u8 max_stgpn; + u8 min_stgpr; + u8 rsv[2]; + __le32 min_stgpr_diff; + } __packed req = { + .tag = cpu_to_le16(UNI_RDD_CTRL_SET_TH), + .len = cpu_to_le16(sizeof(req) - 4), + + .ctrl = cpu_to_le32(0x2), + .radar_type = cpu_to_le16(index), + +#define __req_field_u8(field) .field = pattern->field +#define __req_field_u32(field) .field = cpu_to_le32(pattern->field) + __req_field_u8(enb), + __req_field_u8(stgr), + __req_field_u8(min_crpn), + __req_field_u8(max_crpn), + __req_field_u8(min_crpr), + __req_field_u8(min_pw), + __req_field_u32(min_pri), + __req_field_u32(max_pri), + __req_field_u8(max_pw), + __req_field_u8(min_crbn), + __req_field_u8(max_crbn), + __req_field_u8(min_stgpn), + __req_field_u8(max_stgpn), + __req_field_u8(min_stgpr), + __req_field_u32(min_stgpr_diff), +#undef __req_field_u8 +#undef __req_field_u32 + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(RDD_CTRL), + &req, sizeof(req), true); +} + +static int +mt7996_mcu_background_chain_ctrl(struct mt7996_phy *phy, + struct cfg80211_chan_def *chandef, + int cmd) +{ + struct mt7996_dev *dev = phy->dev; + struct mt76_phy *mphy = phy->mt76; + struct ieee80211_channel *chan = mphy->chandef.chan; + int freq = mphy->chandef.center_freq1; + struct mt7996_mcu_background_chain_ctrl req = { + .tag = cpu_to_le16(0), + .len = cpu_to_le16(sizeof(req) - 4), + .monitor_scan_type = 2, /* simple rx */ + }; + + if (!chandef && cmd != CH_SWITCH_BACKGROUND_SCAN_STOP) + return -EINVAL; + + if (!cfg80211_chandef_valid(&mphy->chandef)) + return -EINVAL; + + switch (cmd) { + case CH_SWITCH_BACKGROUND_SCAN_START: { + req.chan = chan->hw_value; + req.central_chan = ieee80211_frequency_to_channel(freq); + req.bw = mt76_connac_chan_bw(&mphy->chandef); + req.monitor_chan = chandef->chan->hw_value; + req.monitor_central_chan = + ieee80211_frequency_to_channel(chandef->center_freq1); + req.monitor_bw = mt76_connac_chan_bw(chandef); + req.band_idx = phy->mt76->band_idx; + req.scan_mode = 1; + break; + } + case CH_SWITCH_BACKGROUND_SCAN_RUNNING: + req.monitor_chan = chandef->chan->hw_value; + req.monitor_central_chan = + ieee80211_frequency_to_channel(chandef->center_freq1); + req.band_idx = phy->mt76->band_idx; + req.scan_mode = 2; + break; + case CH_SWITCH_BACKGROUND_SCAN_STOP: + req.chan = chan->hw_value; + req.central_chan = ieee80211_frequency_to_channel(freq); + req.bw = mt76_connac_chan_bw(&mphy->chandef); + req.tx_stream = hweight8(mphy->antenna_mask); + req.rx_stream = mphy->antenna_mask; + break; + default: + return -EINVAL; + } + req.band = chandef ? chandef->chan->band == NL80211_BAND_5GHZ : 1; + + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(OFFCH_SCAN_CTRL), + &req, sizeof(req), false); +} + +int mt7996_mcu_rdd_background_enable(struct mt7996_phy *phy, + struct cfg80211_chan_def *chandef) +{ + struct mt7996_dev *dev = phy->dev; + int err, region, rdd_idx = mt7996_get_rdd_idx(phy, true); + + if (rdd_idx < 0) + return -EINVAL; + + if (!chandef) { /* disable offchain */ + err = mt7996_mcu_rdd_cmd(dev, RDD_STOP, rdd_idx, 0); + if (err) + return err; + + return mt7996_mcu_background_chain_ctrl(phy, NULL, + CH_SWITCH_BACKGROUND_SCAN_STOP); + } + + err = mt7996_mcu_background_chain_ctrl(phy, chandef, + CH_SWITCH_BACKGROUND_SCAN_START); + if (err) + return err; + + switch (dev->mt76.region) { + case NL80211_DFS_ETSI: + region = 0; + break; + case NL80211_DFS_JP: + region = 2; + break; + case NL80211_DFS_FCC: + default: + region = 1; + break; + } + + return mt7996_mcu_rdd_cmd(dev, RDD_START, rdd_idx, region); +} + +int mt7996_mcu_set_chan_info(struct mt7996_phy *phy, u16 tag) +{ + static const u8 ch_band[] = { + [NL80211_BAND_2GHZ] = 0, + [NL80211_BAND_5GHZ] = 1, + [NL80211_BAND_6GHZ] = 2, + }; + struct mt7996_dev *dev = phy->dev; + struct cfg80211_chan_def *chandef = &phy->mt76->chandef; + int freq1 = chandef->center_freq1; + u8 band_idx = phy->mt76->band_idx; + struct { + /* fixed field */ + u8 __rsv[4]; + + __le16 tag; + __le16 len; + u8 control_ch; + u8 center_ch; + u8 bw; + u8 tx_path_num; + u8 rx_path; /* mask or num */ + u8 switch_reason; + u8 band_idx; + u8 center_ch2; /* for 80+80 only */ + __le16 cac_case; + u8 channel_band; + u8 rsv0; + __le32 outband_freq; + u8 txpower_drop; + u8 ap_bw; + u8 ap_center_ch; + u8 rsv1[53]; + } __packed req = { + .tag = cpu_to_le16(tag), + .len = cpu_to_le16(sizeof(req) - 4), + .control_ch = chandef->chan->hw_value, + .center_ch = ieee80211_frequency_to_channel(freq1), + .bw = mt76_connac_chan_bw(chandef), + .tx_path_num = hweight16(phy->mt76->chainmask), + .rx_path = mt7996_rx_chainmask(phy) >> dev->chainshift[band_idx], + .band_idx = band_idx, + .channel_band = ch_band[chandef->chan->band], + }; + + if (phy->mt76->hw->conf.flags & IEEE80211_CONF_MONITOR) + req.switch_reason = CH_SWITCH_NORMAL; + else if (phy->mt76->offchannel || !phy->mt76->chanctx) + req.switch_reason = CH_SWITCH_SCAN_BYPASS_DPD; + else if (!cfg80211_reg_can_beacon(phy->mt76->hw->wiphy, chandef, + NL80211_IFTYPE_AP)) + req.switch_reason = CH_SWITCH_DFS; + else + req.switch_reason = CH_SWITCH_NORMAL; + + if (tag == UNI_CHANNEL_SWITCH) + req.rx_path = hweight8(req.rx_path); + + if (chandef->width == NL80211_CHAN_WIDTH_80P80) { + int freq2 = chandef->center_freq2; + + req.center_ch2 = ieee80211_frequency_to_channel(freq2); + } + + return mt76_mcu_send_msg(&dev->mt76, MCU_WMWA_UNI_CMD(CHANNEL_SWITCH), + &req, sizeof(req), true); +} + +static int +mt7996_mcu_get_cal_free_data(struct mt7996_dev *dev) +{ +#define MT_EE_7977BN_OFFSET (0x1200 - 0x500) + struct cal_free_data { + u16 adie_offs; + u16 eep_offs; + }; + static const struct cal_free_data cal_7975[] = { + { 0x5cd, 0x451 }, { 0x5cf, 0x453 }, { 0x5d1, 0x455 }, + { 0x5d3, 0x457 }, { 0x6c0, 0x44c }, { 0x6c1, 0x44d }, + { 0x6c2, 0x44e }, { 0x6c3, 0x44f }, { 0x7a1, 0xba1 }, + { 0x7a6, 0xba6 }, { 0x7a8, 0xba8 }, { 0x7aa, 0xbaa }, + }; + static const struct cal_free_data cal_7976[] = { + { 0x4c, 0x44c }, { 0x4d, 0x44d }, { 0x4e, 0x44e }, + { 0x4f, 0x44f }, { 0x50, 0x450 }, { 0x51, 0x451 }, + { 0x53, 0x453 }, { 0x55, 0x455 }, { 0x57, 0x457 }, + { 0x59, 0x459 }, { 0x70, 0x470 }, { 0x71, 0x471 }, + { 0x790, 0xb90 }, { 0x791, 0xb91 }, { 0x794, 0xb94 }, + { 0x795, 0xb95 }, { 0x7a6, 0xba6 }, { 0x7a8, 0xba8 }, + { 0x7aa, 0xbaa }, + }; + static const struct cal_free_data cal_7977[] = { + { 0x4c, 0x124c }, { 0x4d, 0x124d }, { 0x4e, 0x124e }, + { 0x4f, 0x124f }, { 0x50, 0x1250 }, { 0x51, 0x1251 }, + { 0x53, 0x1253 }, { 0x55, 0x1255 }, { 0x57, 0x1257 }, + { 0x59, 0x1259 }, { 0x69, 0x1269 }, { 0x6a, 0x126a }, + { 0x7a, 0x127a }, { 0x7b, 0x127b }, { 0x7c, 0x127c }, + { 0x7d, 0x127d }, { 0x7e, 0x127e }, + }; + static const struct cal_free_data cal_7978[] = { + { 0x91, 0xb91 }, { 0x95, 0xb95 }, { 0x100, 0x480 }, + { 0x102, 0x482 }, { 0x104, 0x484 }, { 0x106, 0x486 }, + { 0x107, 0x487 }, { 0x108, 0x488 }, { 0x109, 0x489 }, + { 0x10a, 0x48a }, { 0x10b, 0x48b }, { 0x10c, 0x48c }, + { 0x10e, 0x48e }, { 0x110, 0x490 }, + }; + static const struct cal_free_data cal_7979[] = { + { 0x4c, 0x124c }, { 0x4d, 0x124d }, { 0x4e, 0x124e }, + { 0x4f, 0x124f }, { 0x50, 0x1250 }, { 0x51, 0x1251 }, + { 0x53, 0x1253 }, { 0x55, 0x1255 }, { 0x57, 0x1257 }, + { 0x59, 0x1259 }, { 0x69, 0x1269 }, { 0x6a, 0x126a }, + { 0x7a, 0x127a }, { 0x7b, 0x127b }, { 0x7c, 0x127c }, + { 0x7e, 0x127e }, { 0x80, 0x1280 }, + }; + const struct cal_free_data *cal_arr[__MT_MAX_BAND]; + u16 cal_arr_len[__MT_MAX_BAND] = {}; + u8 *eeprom = (u8 *)dev->mt76.eeprom.data; + int band, i, ret; + +#define CAL_ARR(_band, _adie) do { \ + cal_arr[_band] = cal_##_adie; \ + cal_arr_len[_band] = ARRAY_SIZE(cal_##_adie); \ + } while (0) + + switch (mt76_chip(&dev->mt76)) { + case MT7996_DEVICE_ID: + /* adie 0 */ + if (dev->var.fem == MT7996_FEM_INT && + dev->var.type != MT7996_VAR_TYPE_233) + CAL_ARR(0, 7975); + else + CAL_ARR(0, 7976); + + /* adie 1 */ + if (dev->var.type == MT7996_VAR_TYPE_444) + CAL_ARR(1, 7977); + + /* adie 2 */ + CAL_ARR(2, 7977); + break; + case MT7992_DEVICE_ID: + /* adie 0 */ + if (dev->var.type == MT7992_VAR_TYPE_44 && + dev->var.fem != MT7996_FEM_EXT) + CAL_ARR(0, 7975); + else if (dev->var.type == MT7992_VAR_TYPE_24) + CAL_ARR(0, 7978); + else + CAL_ARR(0, 7976); + + /* adie 1 */ + if (dev->var.type == MT7992_VAR_TYPE_44 && + dev->var.fem != MT7996_FEM_INT) + CAL_ARR(1, 7977); + else if (dev->var.type != MT7992_VAR_TYPE_23) + CAL_ARR(1, 7979); + break; + case MT7990_DEVICE_ID: + /* adie 0 */ + CAL_ARR(0, 7976); + break; + default: + return -EINVAL; + } + + for (band = 0; band < __MT_MAX_BAND; band++) { + u8 buf[MT7996_EEPROM_BLOCK_SIZE]; + const struct cal_free_data *cal; + u16 prev_block_idx = -1; + u16 adie_base; + + if (!cal_arr_len[band]) + continue; + + if (band == MT_BAND0) + adie_base = MT7996_EFUSE_BASE_OFFS_ADIE0; + else if (band == MT_BAND1 && is_mt7992(&dev->mt76)) + adie_base = MT7992_EFUSE_BASE_OFFS_ADIE1; + else if (band == MT_BAND1) + adie_base = MT7996_EFUSE_BASE_OFFS_ADIE1; + else + adie_base = MT7996_EFUSE_BASE_OFFS_ADIE2; + + cal = cal_arr[band]; + for (i = 0; i < cal_arr_len[band]; i++) { + u16 adie_offset = cal[i].adie_offs + adie_base; + u16 eep_offset = cal[i].eep_offs; + u16 block_idx = adie_offset / MT7996_EEPROM_BLOCK_SIZE; + u16 offset = adie_offset % MT7996_EEPROM_BLOCK_SIZE; + + if (is_mt7996(&dev->mt76) && band == MT_BAND1 && + dev->var.type == MT7996_VAR_TYPE_444) + eep_offset -= MT_EE_7977BN_OFFSET; + + if (prev_block_idx != block_idx) { + memset(buf, 0, sizeof(buf)); + ret = mt7996_mcu_get_eeprom(dev, adie_offset, buf, + MT7996_EEPROM_BLOCK_SIZE, + EEPROM_MODE_EFUSE); + if (ret) { + if (ret != -EINVAL) + return ret; + prev_block_idx = -1; + continue; + } + } + eeprom[eep_offset] = buf[offset]; + prev_block_idx = block_idx; + } + } + + return 0; +} + +int mt7996_mcu_set_eeprom(struct mt7996_dev *dev) +{ +#define MAX_PAGE_IDX_MASK GENMASK(7, 5) +#define PAGE_IDX_MASK GENMASK(4, 2) +#define PER_PAGE_SIZE 0x400 + struct mt7996_mcu_eeprom_update req = { + .tag = cpu_to_le16(UNI_EFUSE_BUFFER_MODE), + .buffer_mode = EE_MODE_BUFFER + }; + u16 eeprom_size = MT7996_EEPROM_SIZE; + u8 total = DIV_ROUND_UP(eeprom_size, PER_PAGE_SIZE); + u8 *eep = (u8 *)dev->mt76.eeprom.data; + int ret, eep_len, i; + + ret = mt7996_mcu_get_cal_free_data(dev); + if (ret) + return ret; + + for (i = 0; i < total; i++, eep += eep_len) { + struct sk_buff *skb; + int msg_len; + + if (i == total - 1 && !!(eeprom_size % PER_PAGE_SIZE)) + eep_len = eeprom_size % PER_PAGE_SIZE; + else + eep_len = PER_PAGE_SIZE; + + msg_len = sizeof(req) + eep_len; + skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, msg_len); + if (!skb) + return -ENOMEM; + + req.len = cpu_to_le16(msg_len - 4); + req.format = FIELD_PREP(MAX_PAGE_IDX_MASK, total - 1) | + FIELD_PREP(PAGE_IDX_MASK, i) | EE_FORMAT_WHOLE; + req.buf_len = cpu_to_le16(eep_len); + + skb_put_data(skb, &req, sizeof(req)); + skb_put_data(skb, eep, eep_len); + + ret = mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_WM_UNI_CMD(EFUSE_CTRL), true); + if (ret) + return ret; + } + + return 0; +} + +int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset, u8 *buf, u32 buf_len, + enum mt7996_eeprom_mode mode) +{ + struct mt7996_mcu_eeprom_access req = { + .info.len = cpu_to_le16(sizeof(req) - 4), + }; + struct mt7996_mcu_eeprom_access_event *event; + struct sk_buff *skb; + int ret, cmd; + u32 addr; + + switch (mode) { + case EEPROM_MODE_EFUSE: + addr = round_down(offset, MT7996_EEPROM_BLOCK_SIZE); + cmd = MCU_WM_UNI_CMD_QUERY(EFUSE_CTRL); + req.info.tag = cpu_to_le16(UNI_EFUSE_ACCESS); + break; + case EEPROM_MODE_EXT: + addr = round_down(offset, MT7996_EXT_EEPROM_BLOCK_SIZE); + cmd = MCU_WM_UNI_CMD_QUERY(EXT_EEPROM_CTRL); + req.info.tag = cpu_to_le16(UNI_EXT_EEPROM_ACCESS); + req.eeprom.ext_eeprom.data_len = cpu_to_le32(buf_len); + break; + default: + return -EINVAL; + } + + req.info.addr = cpu_to_le32(addr); + ret = mt76_mcu_send_and_get_msg(&dev->mt76, cmd, &req, sizeof(req), + true, &skb); + if (ret) + return ret; + + event = (struct mt7996_mcu_eeprom_access_event *)skb->data; + if (event->valid) { + u32 ret_len = le32_to_cpu(event->eeprom.ext_eeprom.data_len); + u32 block = mode == EEPROM_MODE_EXT ? MT7996_EXT_EEPROM_BLOCK_SIZE : + MT7996_EEPROM_BLOCK_SIZE; + + addr = le32_to_cpu(event->addr); + + if (!buf) { + if (addr > dev->mt76.eeprom.size - block) { + dev_kfree_skb(skb); + return -EINVAL; + } + buf = (u8 *)dev->mt76.eeprom.data + addr; + } + + switch (mode) { + case EEPROM_MODE_EFUSE: + if (!buf_len || buf_len > MT7996_EEPROM_BLOCK_SIZE) + buf_len = MT7996_EEPROM_BLOCK_SIZE; + + memcpy(buf, event->eeprom.efuse, buf_len); + break; + case EEPROM_MODE_EXT: + if (!buf_len || buf_len > MT7996_EXT_EEPROM_BLOCK_SIZE) + buf_len = MT7996_EXT_EEPROM_BLOCK_SIZE; + + memcpy(buf, event->eeprom.ext_eeprom.data, + ret_len < buf_len ? ret_len : buf_len); + break; + default: + ret = -EINVAL; + break; + } + } else { + ret = -EINVAL; + } + + dev_kfree_skb(skb); + + return ret; +} + +int mt7996_mcu_get_efuse_free_block(struct mt7996_dev *dev, u8 *block_num) +{ + struct { + u8 _rsv[4]; + + __le16 tag; + __le16 len; + u8 num; + u8 version; + u8 die_idx; + u8 _rsv2; + } __packed req = { + .tag = cpu_to_le16(UNI_EFUSE_FREE_BLOCK), + .len = cpu_to_le16(sizeof(req) - 4), + .version = 2, + }; + struct sk_buff *skb; + int ret; + + ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_WM_UNI_CMD_QUERY(EFUSE_CTRL), &req, + sizeof(req), true, &skb); + if (ret) + return ret; + + *block_num = *(u8 *)(skb->data + 8); + dev_kfree_skb(skb); + + return 0; +} + +int mt7996_mcu_get_chip_config(struct mt7996_dev *dev, u32 *cap) +{ +#define UNI_EVENT_CHIP_CONFIG_EFUSE_VERSION 0x21 + struct { + u8 _rsv[4]; + + __le16 tag; + __le16 len; + } __packed req = { + .tag = cpu_to_le16(UNI_CHIP_CONFIG_NIC_CAPA), + .len = cpu_to_le16(sizeof(req) - 4), + }; + struct sk_buff *skb; + u8 *buf; + int ret; + + ret = mt76_mcu_send_and_get_msg(&dev->mt76, + MCU_WM_UNI_CMD_QUERY(CHIP_CONFIG), &req, + sizeof(req), true, &skb); + if (ret) + return ret; + + /* fixed field */ + if (skb->len < 4) { + dev_kfree_skb(skb); + return -EINVAL; + } + skb_pull(skb, 4); + + buf = skb->data; + while (buf - skb->data + sizeof(struct tlv) <= skb->len) { + struct tlv *tlv = (struct tlv *)buf; + u16 tlv_len = le16_to_cpu(tlv->len); + + if (tlv_len < sizeof(*tlv) || + tlv_len > skb->len - (buf - skb->data)) + break; + + switch (le16_to_cpu(tlv->tag)) { + case UNI_EVENT_CHIP_CONFIG_EFUSE_VERSION: + if (tlv_len >= sizeof(*tlv) + sizeof(__le32)) + *cap = le32_to_cpu(*(__le32 *)(buf + sizeof(*tlv))); + break; + default: + break; + } + + buf += tlv_len; + } + + dev_kfree_skb(skb); + + return 0; +} + +int mt7996_mcu_get_chan_mib_info(struct mt7996_phy *phy, bool chan_switch) +{ + enum { + IDX_TX_TIME, + IDX_RX_TIME, + IDX_OBSS_AIRTIME, + IDX_NON_WIFI_TIME, + IDX_NUM + }; + struct { + struct { + u8 band; + u8 __rsv[3]; + } hdr; + struct { + __le16 tag; + __le16 len; + __le32 offs; + } data[IDX_NUM]; + } __packed req = { + .hdr.band = phy->mt76->band_idx, + }; + static const u32 offs[] = { + [IDX_TX_TIME] = UNI_MIB_TX_TIME, + [IDX_RX_TIME] = UNI_MIB_RX_TIME, + [IDX_OBSS_AIRTIME] = UNI_MIB_OBSS_AIRTIME, + [IDX_NON_WIFI_TIME] = UNI_MIB_NON_WIFI_TIME, + }; + struct mt76_channel_state *state = phy->mt76->chan_state; + struct mt76_channel_state *state_ts = &phy->state_ts; + struct mt7996_dev *dev = phy->dev; + struct mt7996_mcu_mib *res; + struct sk_buff *skb; + int i, ret; + + for (i = 0; i < IDX_NUM; i++) { + req.data[i].tag = cpu_to_le16(UNI_CMD_MIB_DATA); + req.data[i].len = cpu_to_le16(sizeof(req.data[i])); + req.data[i].offs = cpu_to_le32(offs[i]); + } + + ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_WM_UNI_CMD_QUERY(GET_MIB_INFO), + &req, sizeof(req), true, &skb); + if (ret) + return ret; + + skb_pull(skb, sizeof(req.hdr)); + + res = (struct mt7996_mcu_mib *)(skb->data); + + if (chan_switch) + goto out; + +#define __res_u64(s) le64_to_cpu(res[s].data) + state->cc_tx += __res_u64(IDX_TX_TIME) - state_ts->cc_tx; + state->cc_bss_rx += __res_u64(IDX_RX_TIME) - state_ts->cc_bss_rx; + state->cc_rx += __res_u64(IDX_RX_TIME) + + __res_u64(IDX_OBSS_AIRTIME) - + state_ts->cc_rx; + state->cc_busy += __res_u64(IDX_TX_TIME) + + __res_u64(IDX_RX_TIME) + + __res_u64(IDX_OBSS_AIRTIME) + + __res_u64(IDX_NON_WIFI_TIME) - + state_ts->cc_busy; +out: + state_ts->cc_tx = __res_u64(IDX_TX_TIME); + state_ts->cc_bss_rx = __res_u64(IDX_RX_TIME); + state_ts->cc_rx = __res_u64(IDX_RX_TIME) + __res_u64(IDX_OBSS_AIRTIME); + state_ts->cc_busy = __res_u64(IDX_TX_TIME) + + __res_u64(IDX_RX_TIME) + + __res_u64(IDX_OBSS_AIRTIME) + + __res_u64(IDX_NON_WIFI_TIME); +#undef __res_u64 + + dev_kfree_skb(skb); + + return 0; +} + +int mt7996_mcu_get_temperature(struct mt7996_phy *phy) +{ +#define TEMPERATURE_QUERY 0 +#define GET_TEMPERATURE 0 + struct { + u8 _rsv[4]; + + __le16 tag; + __le16 len; + + u8 rsv1; + u8 action; + u8 band_idx; + u8 rsv2; + } req = { + .tag = cpu_to_le16(TEMPERATURE_QUERY), + .len = cpu_to_le16(sizeof(req) - 4), + .action = GET_TEMPERATURE, + .band_idx = phy->mt76->band_idx, + }; + struct mt7996_mcu_thermal { + u8 _rsv[4]; + + __le16 tag; + __le16 len; + + __le32 rsv; + __le32 temperature; + } __packed * res; + struct sk_buff *skb; + int ret; + u32 temp; + + ret = mt76_mcu_send_and_get_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(THERMAL), + &req, sizeof(req), true, &skb); + if (ret) + return ret; + + res = (void *)skb->data; + temp = le32_to_cpu(res->temperature); + dev_kfree_skb(skb); + + return temp; +} + +int mt7996_mcu_set_thermal_throttling(struct mt7996_phy *phy, u8 state) +{ + struct { + u8 _rsv[4]; + + __le16 tag; + __le16 len; + + struct mt7996_mcu_thermal_ctrl ctrl; + } __packed req = { + .tag = cpu_to_le16(UNI_CMD_THERMAL_PROTECT_DUTY_CONFIG), + .len = cpu_to_le16(sizeof(req) - 4), + .ctrl = { + .band_idx = phy->mt76->band_idx, + }, + }; + int level, ret; + + /* set duty cycle and level */ + for (level = 0; level < 4; level++) { + req.ctrl.duty.duty_level = level; + req.ctrl.duty.duty_cycle = state; + state /= 2; + + ret = mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(THERMAL), + &req, sizeof(req), false); + if (ret) + return ret; + } + + return 0; +} + +int mt7996_mcu_set_thermal_protect(struct mt7996_phy *phy, bool enable) +{ +#define SUSTAIN_PERIOD 10 + struct { + u8 _rsv[4]; + + __le16 tag; + __le16 len; + + struct mt7996_mcu_thermal_ctrl ctrl; + struct mt7996_mcu_thermal_enable enable; + } __packed req = { + .len = cpu_to_le16(sizeof(req) - 4 - sizeof(req.enable)), + .ctrl = { + .band_idx = phy->mt76->band_idx, + .type.protect_type = 1, + .type.trigger_type = 1, + }, + }; + int ret; + + req.tag = cpu_to_le16(UNI_CMD_THERMAL_PROTECT_DISABLE); + + ret = mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(THERMAL), + &req, sizeof(req) - sizeof(req.enable), false); + if (ret || !enable) + return ret; + + /* set high-temperature trigger threshold */ + req.tag = cpu_to_le16(UNI_CMD_THERMAL_PROTECT_ENABLE); + req.enable.restore_temp = cpu_to_le32(phy->throttle_temp[0]); + req.enable.trigger_temp = cpu_to_le32(phy->throttle_temp[1]); + req.enable.sustain_time = cpu_to_le16(SUSTAIN_PERIOD); + + req.len = cpu_to_le16(sizeof(req) - 4); + + return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(THERMAL), + &req, sizeof(req), false); +} + +int mt7996_mcu_set_ser(struct mt7996_dev *dev, u8 action, u8 val, u8 band) +{ + struct { + u8 rsv[4]; + + __le16 tag; + __le16 len; + + union { + struct { + __le32 mask; + } __packed set; + + struct { + u8 method; + u8 band; + u8 rsv2[2]; + } __packed trigger; + }; + } __packed req = { + .tag = cpu_to_le16(action), + .len = cpu_to_le16(sizeof(req) - 4), + }; + + switch (action) { + case UNI_CMD_SER_SET: + req.set.mask = cpu_to_le32(val); + break; + case UNI_CMD_SER_TRIGGER: + req.trigger.method = val; + req.trigger.band = band; + break; + default: + return -EINVAL; + } + + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(SER), + &req, sizeof(req), false); +} + +int mt7996_mcu_set_txbf(struct mt7996_dev *dev, u8 action) +{ +#define MT7996_BF_MAX_SIZE sizeof(union bf_tag_tlv) +#define BF_PROCESSING 4 + struct uni_header hdr; + struct sk_buff *skb; + struct tlv *tlv; + int len = sizeof(hdr) + MT7996_BF_MAX_SIZE; + + memset(&hdr, 0, sizeof(hdr)); + + skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, len); + if (!skb) + return -ENOMEM; + + skb_put_data(skb, &hdr, sizeof(hdr)); + + switch (action) { + case BF_SOUNDING_ON: { + struct bf_sounding_on *req_snd_on; + + tlv = mt7996_mcu_add_uni_tlv(skb, action, sizeof(*req_snd_on)); + req_snd_on = (struct bf_sounding_on *)tlv; + req_snd_on->snd_mode = BF_PROCESSING; + break; + } + case BF_HW_EN_UPDATE: { + struct bf_hw_en_status_update *req_hw_en; + + tlv = mt7996_mcu_add_uni_tlv(skb, action, sizeof(*req_hw_en)); + req_hw_en = (struct bf_hw_en_status_update *)tlv; + req_hw_en->ebf = true; + req_hw_en->ibf = dev->ibf; + break; + } + case BF_MOD_EN_CTRL: { + struct bf_mod_en_ctrl *req_mod_en; + + tlv = mt7996_mcu_add_uni_tlv(skb, action, sizeof(*req_mod_en)); + req_mod_en = (struct bf_mod_en_ctrl *)tlv; + req_mod_en->bf_num = mt7996_band_valid(dev, MT_BAND2) ? 3 : 2; + req_mod_en->bf_bitmap = mt7996_band_valid(dev, MT_BAND2) ? + GENMASK(2, 0) : GENMASK(1, 0); + break; + } + default: + return -EINVAL; + } + + return mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_WM_UNI_CMD(BF), true); +} + +static int +mt7996_mcu_enable_obss_spr(struct mt7996_phy *phy, u16 action, u8 val) +{ + struct mt7996_dev *dev = phy->dev; + struct { + u8 band_idx; + u8 __rsv[3]; + + __le16 tag; + __le16 len; + + __le32 val; + } __packed req = { + .band_idx = phy->mt76->band_idx, + .tag = cpu_to_le16(action), + .len = cpu_to_le16(sizeof(req) - 4), + .val = cpu_to_le32(val), + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(SR), + &req, sizeof(req), true); +} + +static int +mt7996_mcu_set_obss_spr_pd(struct mt7996_phy *phy, + struct ieee80211_he_obss_pd *he_obss_pd) +{ + struct mt7996_dev *dev = phy->dev; + u8 max_th = 82, non_srg_max_th = 62; + struct { + u8 band_idx; + u8 __rsv[3]; + + __le16 tag; + __le16 len; + + u8 pd_th_non_srg; + u8 pd_th_srg; + u8 period_offs; + u8 rcpi_src; + __le16 obss_pd_min; + __le16 obss_pd_min_srg; + u8 resp_txpwr_mode; + u8 txpwr_restrict_mode; + u8 txpwr_ref; + u8 __rsv2[3]; + } __packed req = { + .band_idx = phy->mt76->band_idx, + .tag = cpu_to_le16(UNI_CMD_SR_SET_PARAM), + .len = cpu_to_le16(sizeof(req) - 4), + .obss_pd_min = cpu_to_le16(max_th), + .obss_pd_min_srg = cpu_to_le16(max_th), + .txpwr_restrict_mode = 2, + .txpwr_ref = 21 + }; + int ret; + + /* disable firmware dynamical PD asjustment */ + ret = mt7996_mcu_enable_obss_spr(phy, UNI_CMD_SR_ENABLE_DPD, false); + if (ret) + return ret; + + if (he_obss_pd->sr_ctrl & + IEEE80211_HE_SPR_NON_SRG_OBSS_PD_SR_DISALLOWED) + req.pd_th_non_srg = max_th; + else if (he_obss_pd->sr_ctrl & IEEE80211_HE_SPR_NON_SRG_OFFSET_PRESENT) + req.pd_th_non_srg = max_th - he_obss_pd->non_srg_max_offset; + else + req.pd_th_non_srg = non_srg_max_th; + + if (he_obss_pd->sr_ctrl & IEEE80211_HE_SPR_SRG_INFORMATION_PRESENT) + req.pd_th_srg = max_th - he_obss_pd->max_offset; + + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(SR), + &req, sizeof(req), true); +} + +static int +mt7996_mcu_set_obss_spr_siga(struct mt7996_phy *phy, + struct mt7996_vif_link *link, + struct ieee80211_he_obss_pd *he_obss_pd) +{ + struct mt7996_dev *dev = phy->dev; + u8 omac = link->mt76.omac_idx; + struct { + u8 band_idx; + u8 __rsv[3]; + + __le16 tag; + __le16 len; + + u8 omac; + u8 __rsv2[3]; + u8 flag[20]; + } __packed req = { + .band_idx = phy->mt76->band_idx, + .tag = cpu_to_le16(UNI_CMD_SR_SET_SIGA), + .len = cpu_to_le16(sizeof(req) - 4), + .omac = omac > HW_BSSID_MAX ? omac - 12 : omac, + }; + int ret; + + if (he_obss_pd->sr_ctrl & IEEE80211_HE_SPR_HESIGA_SR_VAL15_ALLOWED) + req.flag[req.omac] = 0xf; + else + return 0; + + /* switch to normal AP mode */ + ret = mt7996_mcu_enable_obss_spr(phy, UNI_CMD_SR_ENABLE_MODE, 0); + if (ret) + return ret; + + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(SR), + &req, sizeof(req), true); +} + +static int +mt7996_mcu_set_obss_spr_bitmap(struct mt7996_phy *phy, + struct ieee80211_he_obss_pd *he_obss_pd) +{ + struct mt7996_dev *dev = phy->dev; + struct { + u8 band_idx; + u8 __rsv[3]; + + __le16 tag; + __le16 len; + + __le32 color_l[2]; + __le32 color_h[2]; + __le32 bssid_l[2]; + __le32 bssid_h[2]; + } __packed req = { + .band_idx = phy->mt76->band_idx, + .tag = cpu_to_le16(UNI_CMD_SR_SET_SRG_BITMAP), + .len = cpu_to_le16(sizeof(req) - 4), + }; + u32 bitmap; + + memcpy(&bitmap, he_obss_pd->bss_color_bitmap, sizeof(bitmap)); + req.color_l[req.band_idx] = cpu_to_le32(bitmap); + + memcpy(&bitmap, he_obss_pd->bss_color_bitmap + 4, sizeof(bitmap)); + req.color_h[req.band_idx] = cpu_to_le32(bitmap); + + memcpy(&bitmap, he_obss_pd->partial_bssid_bitmap, sizeof(bitmap)); + req.bssid_l[req.band_idx] = cpu_to_le32(bitmap); + + memcpy(&bitmap, he_obss_pd->partial_bssid_bitmap + 4, sizeof(bitmap)); + req.bssid_h[req.band_idx] = cpu_to_le32(bitmap); + + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(SR), &req, + sizeof(req), true); +} + +int mt7996_mcu_add_obss_spr(struct mt7996_phy *phy, + struct mt7996_vif_link *link, + struct ieee80211_he_obss_pd *he_obss_pd) +{ + int ret; + + /* enable firmware scene detection algorithms */ + ret = mt7996_mcu_enable_obss_spr(phy, UNI_CMD_SR_ENABLE_SD, + sr_scene_detect); + if (ret) + return ret; + + /* firmware dynamically adjusts PD threshold so skip manual control */ + if (sr_scene_detect && !he_obss_pd->enable) + return 0; + + /* enable spatial reuse */ + ret = mt7996_mcu_enable_obss_spr(phy, UNI_CMD_SR_ENABLE, + he_obss_pd->enable); + if (ret) + return ret; + + if (sr_scene_detect || !he_obss_pd->enable) + return 0; + + ret = mt7996_mcu_enable_obss_spr(phy, UNI_CMD_SR_ENABLE_TX, true); + if (ret) + return ret; + + /* set SRG/non-SRG OBSS PD threshold */ + ret = mt7996_mcu_set_obss_spr_pd(phy, he_obss_pd); + if (ret) + return ret; + + /* Set SR prohibit */ + ret = mt7996_mcu_set_obss_spr_siga(phy, link, he_obss_pd); + if (ret) + return ret; + + /* set SRG BSS color/BSSID bitmap */ + return mt7996_mcu_set_obss_spr_bitmap(phy, he_obss_pd); +} + +int mt7996_mcu_update_bss_color(struct mt7996_dev *dev, + struct mt76_vif_link *mlink, + struct cfg80211_he_bss_color *he_bss_color) +{ + int len = sizeof(struct bss_req_hdr) + sizeof(struct bss_color_tlv); + struct bss_color_tlv *bss_color; + struct sk_buff *skb; + struct tlv *tlv; + + skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, mlink, len); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + tlv = mt76_connac_mcu_add_tlv(skb, UNI_BSS_INFO_BSS_COLOR, + sizeof(*bss_color)); + bss_color = (struct bss_color_tlv *)tlv; + bss_color->enable = he_bss_color->enabled; + bss_color->color = he_bss_color->color; + + return mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_WMWA_UNI_CMD(BSS_INFO_UPDATE), true); +} + +#define TWT_AGRT_TRIGGER BIT(0) +#define TWT_AGRT_ANNOUNCE BIT(1) +#define TWT_AGRT_PROTECT BIT(2) + +int mt7996_mcu_twt_agrt_update(struct mt7996_dev *dev, + struct mt7996_vif_link *link, + struct mt7996_twt_flow *flow, + int cmd) +{ + struct { + /* fixed field */ + u8 bss; + u8 _rsv[3]; + + __le16 tag; + __le16 len; + u8 tbl_idx; + u8 cmd; + u8 own_mac_idx; + u8 flowid; /* 0xff for group id */ + __le16 peer_id; /* specify the peer_id (msb=0) + * or group_id (msb=1) + */ + u8 duration; /* 256 us */ + u8 bss_idx; + __le64 start_tsf; + __le16 mantissa; + u8 exponent; + u8 is_ap; + u8 agrt_params; + u8 __rsv2[23]; + } __packed req = { + .tag = cpu_to_le16(UNI_CMD_TWT_ARGT_UPDATE), + .len = cpu_to_le16(sizeof(req) - 4), + .tbl_idx = flow->table_id, + .cmd = cmd, + .own_mac_idx = link->mt76.omac_idx, + .flowid = flow->id, + .peer_id = cpu_to_le16(flow->wcid), + .duration = flow->duration, + .bss = link->mt76.idx, + .bss_idx = link->mt76.idx, + .start_tsf = cpu_to_le64(flow->tsf), + .mantissa = flow->mantissa, + .exponent = flow->exp, + .is_ap = true, + }; + + if (flow->protection) + req.agrt_params |= TWT_AGRT_PROTECT; + if (!flow->flowtype) + req.agrt_params |= TWT_AGRT_ANNOUNCE; + if (flow->trigger) + req.agrt_params |= TWT_AGRT_TRIGGER; + + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(TWT), + &req, sizeof(req), true); +} + +int mt7996_mcu_set_bssid_mapping_addr(struct mt76_dev *dev, u8 band_idx) +{ + enum { + BSSID_MAPPING_ADDR1, + BSSID_MAPPING_ADDR2, + BSSID_MAPPING_ADDR3, + }; + struct { + u8 band_idx; + u8 _rsv1[3]; + + __le16 tag; + __le16 len; + u8 addr; + u8 _rsv2[3]; + } __packed req = { + .band_idx = band_idx, + .tag = cpu_to_le16(UNI_BAND_CONFIG_BSSID_MAPPING_ADDR), + .len = cpu_to_le16(sizeof(req) - 4), + .addr = BSSID_MAPPING_ADDR1, + }; + + return mt76_mcu_send_msg(dev, MCU_WM_UNI_CMD(BAND_CONFIG), + &req, sizeof(req), true); +} + +int mt7996_mcu_set_rts_thresh(struct mt7996_phy *phy, u32 val) +{ + struct { + u8 band_idx; + u8 _rsv[3]; + + __le16 tag; + __le16 len; + __le32 len_thresh; + __le32 pkt_thresh; + } __packed req = { + .band_idx = phy->mt76->band_idx, + .tag = cpu_to_le16(UNI_BAND_CONFIG_RTS_THRESHOLD), + .len = cpu_to_le16(sizeof(req) - 4), + .len_thresh = cpu_to_le32(val), + .pkt_thresh = cpu_to_le32(0x2), + }; + + return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(BAND_CONFIG), + &req, sizeof(req), true); +} + +int mt7996_mcu_set_radio_en(struct mt7996_phy *phy, bool enable) +{ + struct { + u8 band_idx; + u8 _rsv[3]; + + __le16 tag; + __le16 len; + u8 enable; + u8 _rsv2[3]; + } __packed req = { + .band_idx = phy->mt76->band_idx, + .tag = cpu_to_le16(UNI_BAND_CONFIG_RADIO_ENABLE), + .len = cpu_to_le16(sizeof(req) - 4), + .enable = enable, + }; + + return mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(BAND_CONFIG), + &req, sizeof(req), true); +} + +int mt7996_mcu_rdd_resume_tx(struct mt7996_phy *phy) +{ + struct { + u8 band_idx; + u8 _rsv[3]; + + __le16 tag; + __le16 len; + u8 mac_enable; + u8 _rsv2[3]; + } __packed req = { + .band_idx = phy->mt76->band_idx, + .tag = cpu_to_le16(UNI_BAND_CONFIG_MAC_ENABLE_CTRL), + .len = cpu_to_le16(sizeof(req) - 4), + .mac_enable = 2, + }; + int ret; + + if (!phy->rdd_tx_paused) + return 0; + + ret = mt76_mcu_send_msg(&phy->dev->mt76, MCU_WM_UNI_CMD(BAND_CONFIG), + &req, sizeof(req), true); + if (!ret) + phy->rdd_tx_paused = false; + + return ret; +} + +int mt7996_mcu_rdd_cmd(struct mt7996_dev *dev, int cmd, u8 rdd_idx, u8 val) +{ + struct { + u8 _rsv[4]; + + __le16 tag; + __le16 len; + + u8 ctrl; + u8 rdd_idx; + u8 rdd_rx_sel; + u8 val; + u8 rsv[4]; + } __packed req = { + .tag = cpu_to_le16(UNI_RDD_CTRL_PARM), + .len = cpu_to_le16(sizeof(req) - 4), + .ctrl = cmd, + .rdd_idx = rdd_idx, + .val = val, + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(RDD_CTRL), + &req, sizeof(req), true); +} + +int mt7996_mcu_wtbl_update_hdr_trans(struct mt7996_dev *dev, + struct ieee80211_vif *vif, + struct mt7996_vif_link *link, + struct mt7996_sta_link *msta_link) +{ + struct sk_buff *skb; + + skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &link->mt76, + &msta_link->wcid, + MT7996_STA_UPDATE_MAX_SIZE); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + /* starec hdr trans */ + mt7996_mcu_sta_hdr_trans_tlv(dev, skb, vif, &msta_link->wcid); + return mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true); +} + +int mt7996_mcu_ps_leave(struct mt7996_dev *dev, struct mt7996_vif_link *link, + struct mt7996_sta_link *msta_link) +{ + struct sk_buff *skb; + + skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &link->mt76, + &msta_link->wcid, + MT7996_STA_UPDATE_MAX_SIZE); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + mt76_connac_mcu_add_tlv(skb, STA_REC_PS_LEAVE, + sizeof(struct sta_rec_ps_leave)); + + return mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true); +} + +int mt7996_mcu_set_fixed_rate_table(struct mt7996_phy *phy, u8 table_idx, + u16 rate_idx, bool beacon) +{ +#define UNI_FIXED_RATE_TABLE_SET 0 +#define SPE_IXD_SELECT_TXD 0 +#define SPE_IXD_SELECT_BMC_WTBL 1 + struct mt7996_dev *dev = phy->dev; + struct fixed_rate_table_ctrl req = { + .tag = cpu_to_le16(UNI_FIXED_RATE_TABLE_SET), + .len = cpu_to_le16(sizeof(req) - 4), + .table_idx = table_idx, + .rate_idx = cpu_to_le16(rate_idx), + .gi = 1, + .he_ltf = 1, + }; + u8 band_idx = phy->mt76->band_idx; + + if (beacon) { + req.spe_idx_sel = SPE_IXD_SELECT_TXD; + req.spe_idx = 24 + band_idx; + phy->beacon_rate = rate_idx; + } else { + req.spe_idx_sel = SPE_IXD_SELECT_BMC_WTBL; + } + + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(FIXED_RATE_TABLE), + &req, sizeof(req), false); +} + +int mt7996_mcu_rf_regval(struct mt7996_dev *dev, u32 regidx, u32 *val, bool set) +{ + struct { + u8 __rsv1[4]; + + __le16 tag; + __le16 len; + __le16 idx; + u8 __rsv2[2]; + __le32 ofs; + __le32 data; + } __packed *res, req = { + .tag = cpu_to_le16(UNI_CMD_ACCESS_RF_REG_BASIC), + .len = cpu_to_le16(sizeof(req) - 4), + + .idx = cpu_to_le16(u32_get_bits(regidx, GENMASK(31, 24))), + .ofs = cpu_to_le32(u32_get_bits(regidx, GENMASK(23, 0))), + .data = set ? cpu_to_le32(*val) : 0, + }; + struct sk_buff *skb; + int ret; + + if (set) + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(REG_ACCESS), + &req, sizeof(req), true); + + ret = mt76_mcu_send_and_get_msg(&dev->mt76, + MCU_WM_UNI_CMD_QUERY(REG_ACCESS), + &req, sizeof(req), true, &skb); + if (ret) + return ret; + + res = (void *)skb->data; + *val = le32_to_cpu(res->data); + dev_kfree_skb(skb); + + return 0; +} + +int mt7996_mcu_trigger_assert(struct mt7996_dev *dev) +{ + struct { + __le16 tag; + __le16 len; + u8 enable; + u8 rsv[3]; + } __packed req = { + .len = cpu_to_le16(sizeof(req) - 4), + .enable = true, + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(ASSERT_DUMP), + &req, sizeof(req), false); +} + +int mt7996_mcu_set_rro(struct mt7996_dev *dev, u16 tag, u16 val) +{ + struct { + u8 __rsv1[4]; + __le16 tag; + __le16 len; + union { + struct { + u8 type; + u8 __rsv2[3]; + } __packed platform_type; + struct { + u8 type; + u8 dest; + u8 __rsv2[2]; + } __packed bypass_mode; + struct { + u8 path; + u8 __rsv2[3]; + } __packed txfree_path; + struct { + __le16 flush_one; + __le16 flush_all; + u8 __rsv2[4]; + } __packed timeout; + }; + } __packed req = { + .tag = cpu_to_le16(tag), + .len = cpu_to_le16(sizeof(req) - 4), + }; + + switch (tag) { + case UNI_RRO_SET_PLATFORM_TYPE: + req.platform_type.type = val; + break; + case UNI_RRO_SET_BYPASS_MODE: + req.bypass_mode.type = val; + break; + case UNI_RRO_SET_TXFREE_PATH: + req.txfree_path.path = val; + break; + case UNI_RRO_SET_FLUSH_TIMEOUT: + req.timeout.flush_one = cpu_to_le16(val); + req.timeout.flush_all = cpu_to_le16(2 * val); + break; + default: + return -EINVAL; + } + + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(RRO), &req, + sizeof(req), true); +} + +int mt7996_mcu_get_all_sta_info(struct mt7996_phy *phy, u16 tag) +{ + struct mt7996_dev *dev = phy->dev; + struct { + u8 _rsv[4]; + + __le16 tag; + __le16 len; + } __packed req = { + .tag = cpu_to_le16(tag), + .len = cpu_to_le16(sizeof(req) - 4), + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(ALL_STA_INFO), + &req, sizeof(req), false); +} + +int mt7996_mcu_wed_rro_reset_sessions(struct mt7996_dev *dev, u16 id) +{ + struct { + u8 __rsv[4]; + + __le16 tag; + __le16 len; + __le16 session_id; + u8 pad[4]; + } __packed req = { + .tag = cpu_to_le16(UNI_RRO_DEL_BA_SESSION), + .len = cpu_to_le16(sizeof(req) - 4), + .session_id = cpu_to_le16(id), + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(RRO), &req, + sizeof(req), true); +} + +int mt7996_mcu_set_sniffer_mode(struct mt7996_phy *phy, bool enabled) +{ + struct mt7996_dev *dev = phy->dev; + struct { + u8 band_idx; + u8 _rsv[3]; + __le16 tag; + __le16 len; + u8 enable; + u8 _pad[3]; + } __packed req = { + .band_idx = phy->mt76->band_idx, + .tag = 0, + .len = cpu_to_le16(sizeof(req) - 4), + .enable = enabled, + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(SNIFFER), &req, + sizeof(req), true); +} + +int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy) +{ +#define TX_POWER_LIMIT_TABLE_RATE 0 + struct mt7996_dev *dev = phy->dev; + struct mt76_phy *mphy = phy->mt76; + struct tx_power_limit_table_ctrl { + u8 __rsv1[4]; + + __le16 tag; + __le16 len; + u8 power_ctrl_id; + u8 power_limit_type; + u8 band_idx; + } __packed req = { + .tag = cpu_to_le16(UNI_TXPOWER_POWER_LIMIT_TABLE_CTRL), + .len = cpu_to_le16(sizeof(req) + MT7996_SKU_PATH_NUM - 4), + .power_ctrl_id = UNI_TXPOWER_POWER_LIMIT_TABLE_CTRL, + .power_limit_type = TX_POWER_LIMIT_TABLE_RATE, + .band_idx = phy->mt76->band_idx, + }; + struct mt76_power_limits la = {}; + struct sk_buff *skb; + int i, tx_power; + + tx_power = mt76_get_power_bound(mphy, phy->txpower); + tx_power = mt76_get_rate_power_limits(mphy, mphy->chandef.chan, + &la, tx_power); + mphy->txpower_cur = tx_power; + + skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, + sizeof(req) + MT7996_SKU_PATH_NUM); + if (!skb) + return -ENOMEM; + + skb_put_data(skb, &req, sizeof(req)); + /* cck and ofdm */ + skb_put_data(skb, &la.cck, sizeof(la.cck)); + skb_put_data(skb, &la.ofdm, sizeof(la.ofdm)); + /* ht20 */ + skb_put_data(skb, &la.mcs[0], 8); + /* ht40 */ + skb_put_data(skb, &la.mcs[1], 9); + + /* vht */ + for (i = 0; i < 4; i++) { + skb_put_data(skb, &la.mcs[i], sizeof(la.mcs[i])); + skb_put_zero(skb, 2); /* padding */ + } + + /* he */ + skb_put_data(skb, &la.ru[0], sizeof(la.ru)); + /* eht */ + skb_put_data(skb, &la.eht[0], sizeof(la.eht)); + + /* padding */ + skb_put_zero(skb, MT7996_SKU_PATH_NUM - MT7996_SKU_RATE_NUM); + + return mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_WM_UNI_CMD(TXPOWER), true); +} + +int mt7996_mcu_cp_support(struct mt7996_dev *dev, u8 mode) +{ + __le32 cp_mode; + + if (mode < mt76_connac_lmac_mapping(IEEE80211_AC_BE) || + mode > mt76_connac_lmac_mapping(IEEE80211_AC_VO)) + return -EINVAL; + + if (!mt7996_has_wa(dev)) { + struct { + u8 _rsv[4]; + + __le16 tag; + __le16 len; + u8 cp_mode; + u8 rsv[3]; + } __packed req = { + .tag = cpu_to_le16(UNI_CMD_SDO_CP_MODE), + .len = cpu_to_le16(sizeof(req) - 4), + .cp_mode = mode, + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_WA_UNI_CMD(SDO), + &req, sizeof(req), false); + } + + cp_mode = cpu_to_le32(mode); + + return mt76_mcu_send_msg(&dev->mt76, MCU_WA_EXT_CMD(CP_SUPPORT), + &cp_mode, sizeof(cp_mode), true); +} + +int mt7996_mcu_set_dup_wtbl(struct mt7996_dev *dev) +{ +#define DUP_WTBL_NUM 80 + struct { + u8 _rsv[4]; + + __le16 tag; + __le16 len; + __le16 base; + __le16 num; + u8 _rsv2[4]; + } __packed req = { + .tag = cpu_to_le16(UNI_CHIP_CONFIG_DUP_WTBL), + .len = cpu_to_le16(sizeof(req) - 4), + .base = cpu_to_le16(MT7996_WTBL_STA - DUP_WTBL_NUM + 1), + .num = cpu_to_le16(DUP_WTBL_NUM), + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(CHIP_CONFIG), &req, + sizeof(req), true); +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h new file mode 100644 index 000000000..74b70fb6d --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h @@ -0,0 +1,1108 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (C) 2022 MediaTek Inc. + */ + +#ifndef __MT7996_MCU_H +#define __MT7996_MCU_H + +#include "../mt76_connac_mcu.h" + +struct mt7996_mcu_rxd { + __le32 rxd[8]; + + __le16 len; + __le16 pkt_type_id; + + u8 eid; + u8 seq; + u8 option; + u8 __rsv; + + u8 ext_eid; + u8 __rsv1[2]; + u8 s2d_index; +}; + +struct mt7996_mcu_uni_event { + __le16 cid; + u8 __rsv[2]; + __le32 status; /* 0: success, others: fail */ +} __packed; + +struct mt7996_mcu_thermal_ctrl { + u8 ctrl_id; + u8 band_idx; + union { + struct { + u8 protect_type; /* 1: duty admit, 2: radio off */ + u8 trigger_type; /* 0: low, 1: high */ + } __packed type; + struct { + u8 duty_level; /* level 0~3 */ + u8 duty_cycle; + } __packed duty; + }; +} __packed; + +struct mt7996_mcu_thermal_enable { + __le32 trigger_temp; + __le32 restore_temp; + __le16 sustain_time; + u8 rsv[2]; +} __packed; + +struct mt7996_mcu_countdown_notify { + u8 omac_idx; + u8 count; + u8 csa_failure_reason; /* 0: success, 1: beacon disabled */ + u8 rsv; +} __packed; + +struct mt7996_mcu_rdd_report { + struct mt7996_mcu_rxd rxd; + + u8 __rsv1[4]; + + __le16 tag; + __le16 len; + + u8 rdd_idx; + u8 long_detected; + u8 constant_prf_detected; + u8 staggered_prf_detected; + u8 radar_type_idx; + u8 periodic_pulse_num; + u8 long_pulse_num; + u8 hw_pulse_num; + + u8 out_lpn; + u8 out_spn; + u8 out_crpn; + u8 out_crpw; + u8 out_crbn; + u8 out_stgpn; + u8 out_stgpw; + + u8 __rsv2; + + __le32 out_pri_const; + __le32 out_pri_stg[3]; + __le32 out_pri_stg_dmin; + + struct { + __le32 start; + __le16 pulse_width; + __le16 pulse_power; + u8 mdrdy_flag; + u8 rsv[3]; + } long_pulse[32]; + + struct { + __le32 start; + __le16 pulse_width; + __le16 pulse_power; + u8 mdrdy_flag; + u8 rsv[3]; + } periodic_pulse[32]; + + struct { + __le32 start; + __le16 pulse_width; + __le16 pulse_power; + u8 sc_pass; + u8 sw_reset; + u8 mdrdy_flag; + u8 tx_active; + } hw_pulse[32]; +} __packed; + +struct mt7996_mcu_background_chain_ctrl { + u8 _rsv[4]; + + __le16 tag; + __le16 len; + + u8 chan; /* primary channel */ + u8 central_chan; /* central channel */ + u8 bw; + u8 tx_stream; + u8 rx_stream; + + u8 monitor_chan; /* monitor channel */ + u8 monitor_central_chan;/* monitor central channel */ + u8 monitor_bw; + u8 monitor_tx_stream; + u8 monitor_rx_stream; + + u8 scan_mode; /* 0: ScanStop + * 1: ScanStart + * 2: ScanRunning + */ + u8 band_idx; /* DBDC */ + u8 monitor_scan_type; + u8 band; /* 0: 2.4GHz, 1: 5GHz */ + u8 rsv[2]; +} __packed; + +struct mt7996_mcu_eeprom_update { + u8 _rsv[4]; + + __le16 tag; + __le16 len; + u8 buffer_mode; + u8 format; + __le16 buf_len; +} __packed; + +union eeprom_data { + struct { + __le32 data_len; + DECLARE_FLEX_ARRAY(u8, data); + } ext_eeprom; + DECLARE_FLEX_ARRAY(u8, efuse); +} __packed; + +struct mt7996_mcu_eeprom_info { + u8 _rsv[4]; + + __le16 tag; + __le16 len; + __le32 addr; + __le32 valid; +} __packed; + +struct mt7996_mcu_eeprom_access { + struct mt7996_mcu_eeprom_info info; + union eeprom_data eeprom; +} __packed; + +struct mt7996_mcu_eeprom_access_event { + u8 _rsv[4]; + + __le16 tag; + __le16 len; + __le32 version; + __le32 addr; + __le32 valid; + __le32 size; + __le32 magic_no; + __le32 type; + __le32 rsv[4]; + union eeprom_data eeprom; +} __packed; + +struct mt7996_mcu_phy_rx_info { + u8 category; + u8 rate; + u8 mode; + u8 nsts; + u8 gi; + u8 coding; + u8 stbc; + u8 bw; +}; + +struct mt7996_mcu_mib { + __le16 tag; + __le16 len; + __le32 offs; + __le64 data; +} __packed; + +struct all_sta_trx_rate { + __le16 wlan_idx; + u8 __rsv1[2]; + u8 tx_mode; + u8 flags; + u8 tx_stbc; + u8 tx_gi; + u8 tx_bw; + u8 tx_ldpc; + u8 tx_mcs; + u8 tx_nss; + u8 rx_rate; + u8 rx_mode; + u8 rx_nsts; + u8 rx_gi; + u8 rx_coding; + u8 rx_stbc; + u8 rx_bw; + u8 __rsv2; +} __packed; + +struct mt7996_mcu_all_sta_info_event { + u8 rsv[4]; + __le16 tag; + __le16 len; + u8 more; + u8 rsv2; + __le16 sta_num; + u8 rsv3[4]; + + union { + DECLARE_FLEX_ARRAY(struct all_sta_trx_rate, rate); + DECLARE_FLEX_ARRAY(struct { + __le16 wlan_idx; + u8 rsv[2]; + __le32 tx_bytes[IEEE80211_NUM_ACS]; + __le32 rx_bytes[IEEE80211_NUM_ACS]; + } __packed, adm_stat); + + DECLARE_FLEX_ARRAY(struct { + __le16 wlan_idx; + u8 rsv[2]; + __le32 tx_msdu_cnt; + __le32 rx_msdu_cnt; + } __packed, msdu_cnt); + } __packed; +} __packed; + +struct mt7996_mcu_wed_rro_event { + struct mt7996_mcu_rxd rxd; + + u8 __rsv1[4]; + + __le16 tag; + __le16 len; +} __packed; + +struct mt7996_mcu_wed_rro_ba_event { + __le16 tag; + __le16 len; + + __le16 wlan_id; + u8 tid; + u8 __rsv1; + __le32 status; + __le16 id; + u8 __rsv2[2]; +} __packed; + +struct mt7996_mcu_wed_rro_ba_delete_event { + __le16 tag; + __le16 len; + + __le16 session_id; + u8 __rsv2[2]; +} __packed; + +enum { + UNI_WED_RRO_BA_SESSION_STATUS, + UNI_WED_RRO_BA_SESSION_TBL, + UNI_WED_RRO_BA_SESSION_DELETE, +}; + +struct mt7996_mcu_ps_sync_event { + struct mt7996_mcu_rxd rxd; + + u8 bss_idx; + u8 __rsv[3]; +} __packed; + +struct mt7996_mcu_ps_client_info { + __le16 tag; + __le16 len; + u8 ps_bit; + u8 __rsv; + __le16 wlan_idx; + u8 buffer_size; + u8 __rsv2[3]; +} __packed; + +struct mt7996_mcu_ps_multi_client_info { + __le16 tag; + __le16 len; + __le16 sta_cnt; + __le16 sta_ps_info[]; +} __packed; + +#define MT7996_PS_MULTI_WCID GENMASK(10, 0) +#define MT7996_PS_MULTI_PS_BIT BIT(15) + +enum { + UNI_PS_CLIENT_INFO = 0, + UNI_PS_MULTI_CLIENT_INFO = 1, + UNI_PS_MULTI_CLIENT_INFO_BITMAP = 2, +}; + +struct mt7996_mcu_thermal_notify { + struct mt7996_mcu_rxd rxd; + + u8 __rsv1[4]; + + __le16 tag; + __le16 len; + + u8 event_id; + u8 band_idx; + u8 level_idx; + u8 duty_percent; + __le32 restore_temp; + u8 __rsv2[4]; +} __packed; + +enum mt7996_chan_mib_offs { + UNI_MIB_OBSS_AIRTIME = 26, + UNI_MIB_NON_WIFI_TIME = 27, + UNI_MIB_TX_TIME = 28, + UNI_MIB_RX_TIME = 29 +}; + +struct edca { + __le16 tag; + __le16 len; + + u8 queue; + u8 set; + u8 cw_min; + u8 cw_max; + __le16 txop; + u8 aifs; + u8 __rsv; +}; + +#define MCU_PQ_ID(p, q) (((p) << 15) | ((q) << 10)) +#define MCU_PKT_ID 0xa0 + +enum { + MCU_FW_LOG_WM, + MCU_FW_LOG_WA, + MCU_FW_LOG_TO_HOST, + MCU_FW_LOG_RELAY = 16 +}; + +enum { + MCU_TWT_AGRT_ADD, + MCU_TWT_AGRT_MODIFY, + MCU_TWT_AGRT_DELETE, + MCU_TWT_AGRT_TEARDOWN, + MCU_TWT_AGRT_GET_TSF, +}; + +enum { + MCU_WA_PARAM_CMD_QUERY, + MCU_WA_PARAM_CMD_SET, + MCU_WA_PARAM_CMD_CAPABILITY, + MCU_WA_PARAM_CMD_DEBUG, +}; + +enum { + MCU_WA_PARAM_PDMA_RX = 0x04, + MCU_WA_PARAM_CPU_UTIL = 0x0b, + MCU_WA_PARAM_RED = 0x0e, + MCU_WA_PARAM_HW_PATH_HIF_VER = 0x2f, +}; + +enum mcu_mmps_mode { + MCU_MMPS_STATIC, + MCU_MMPS_DYNAMIC, + MCU_MMPS_RSV, + MCU_MMPS_DISABLE, +}; + +struct bss_rate_tlv { + __le16 tag; + __le16 len; + u8 __rsv1[4]; + __le16 bc_trans; + __le16 mc_trans; + u8 short_preamble; + u8 bc_fixed_rate; + u8 mc_fixed_rate; + u8 __rsv2[9]; +} __packed; + +enum { + BP_DISABLE, + BP_SW_MODE, + BP_HW_MODE, +}; + +struct bss_ra_tlv { + __le16 tag; + __le16 len; + u8 short_preamble; + u8 force_sgi; + u8 force_gf; + u8 ht_mode; + u8 se_off; + u8 antenna_idx; + __le16 max_phyrate; + u8 force_tx_streams; + u8 __rsv[3]; +} __packed; + +struct bss_rlm_tlv { + __le16 tag; + __le16 len; + u8 control_channel; + u8 center_chan; + u8 center_chan2; + u8 bw; + u8 tx_streams; + u8 rx_streams; + u8 ht_op_info; + u8 sco; + u8 band; + u8 __rsv[3]; +} __packed; + +struct bss_color_tlv { + __le16 tag; + __le16 len; + u8 enable; + u8 color; + u8 rsv[2]; +} __packed; + +struct bss_inband_discovery_tlv { + __le16 tag; + __le16 len; + u8 tx_type; + u8 tx_mode; + u8 tx_interval; + u8 enable; + __le16 wcid; + __le16 prob_rsp_len; +} __packed; + +struct bss_bcn_content_tlv { + __le16 tag; + __le16 len; + __le16 tim_ie_pos; + __le16 csa_ie_pos; + __le16 bcc_ie_pos; + u8 enable; + u8 type; + __le16 pkt_len; +} __packed; + +struct bss_bcn_cntdwn_tlv { + __le16 tag; + __le16 len; + u8 cnt; + union { + struct { + bool static_pp; + bool abort; + } csa; + struct { + bool abort; + } cca; + }; + u8 rsv; +} __packed; + +struct bss_bcn_mbss_tlv { + __le16 tag; + __le16 len; + __le32 bitmap; +#define MAX_BEACON_NUM 32 + __le16 offset[MAX_BEACON_NUM]; +} __packed __aligned(4); + +struct bss_txcmd_tlv { + __le16 tag; + __le16 len; + u8 txcmd_mode; + u8 __rsv[3]; +} __packed; + +struct bss_sec_tlv { + __le16 tag; + __le16 len; + u8 __rsv1[2]; + u8 cipher; + u8 __rsv2[1]; +} __packed; + +struct bss_ifs_time_tlv { + __le16 tag; + __le16 len; + u8 slot_valid; + u8 sifs_valid; + u8 rifs_valid; + u8 eifs_valid; + __le16 slot_time; + __le16 sifs_time; + __le16 rifs_time; + __le16 eifs_time; + u8 eifs_cck_valid; + u8 rsv; + __le16 eifs_cck_time; +} __packed; + +struct bss_power_save { + __le16 tag; + __le16 len; + u8 profile; + u8 _rsv[3]; +} __packed; + +struct bss_mld_tlv { + __le16 tag; + __le16 len; + u8 group_mld_id; + u8 own_mld_id; + u8 mac_addr[ETH_ALEN]; + u8 remap_idx; + u8 link_id; + u8 __rsv[2]; +} __packed; + +struct bss_prot_tlv { + __le16 tag; + __le16 len; + __le32 prot_mode; +} __packed; + +struct bss_mld_link_op_tlv { + __le16 tag; + __le16 len; + u8 group_mld_id; + u8 own_mld_id; + u8 mac_addr[ETH_ALEN]; + u8 remap_idx; + u8 link_operation; + u8 link_id; + u8 rsv[2]; +} __packed; + +struct sta_rec_ht_uni { + __le16 tag; + __le16 len; + __le16 ht_cap; + __le16 ht_cap_ext; + u8 ampdu_param; + u8 _rsv[3]; +} __packed; + +struct sta_rec_ba_uni { + __le16 tag; + __le16 len; + u8 tid; + u8 ba_type; + u8 amsdu; + u8 ba_en; + __le16 ssn; + __le16 winsize; + u8 ba_rdd_rro; + u8 __rsv[3]; +} __packed; + +struct sta_rec_eht { + __le16 tag; + __le16 len; + u8 tid_bitmap; + u8 _rsv; + __le16 mac_cap; + __le64 phy_cap; + __le64 phy_cap_ext; + u8 mcs_map_bw20[4]; + u8 mcs_map_bw80[3]; + u8 mcs_map_bw160[3]; + u8 mcs_map_bw320[3]; + u8 _rsv2[3]; +} __packed; + +struct sec_key_uni { + __le16 wlan_idx; + u8 mgmt_prot; + u8 cipher_id; + u8 cipher_len; + u8 key_id; + u8 key_len; + u8 need_resp; + u8 key[32]; + u8 pn[6]; + u8 bcn_mode; + u8 _rsv; +} __packed; + +struct sta_rec_sec_uni { + __le16 tag; + __le16 len; + u8 add; + u8 n_cipher; + u8 rsv[2]; + + struct sec_key_uni key[2]; +} __packed; + +struct sta_phy_uni { + u8 type; + u8 flag; + u8 stbc; + u8 sgi; + u8 bw; + u8 ldpc; + u8 mcs; + u8 nss; + u8 he_ltf; + u8 rsv[3]; +}; + +struct sta_rec_ra_uni { + __le16 tag; + __le16 len; + + u8 valid; + u8 auto_rate; + u8 phy_mode; + u8 channel; + u8 bw; + u8 disable_cck; + u8 ht_mcs32; + u8 ht_gf; + u8 ht_mcs[4]; + u8 mmps_mode; + u8 gband_256; + u8 af; + u8 auth_wapi_mode; + u8 rate_len; + + u8 supp_mode; + u8 supp_cck_rate; + u8 supp_ofdm_rate; + __le32 supp_ht_mcs; + __le16 supp_vht_mcs[4]; + + u8 op_mode; + u8 op_vht_chan_width; + u8 op_vht_rx_nss; + u8 op_vht_rx_nss_type; + + __le32 sta_cap; + + struct sta_phy_uni phy; + u8 rx_rcpi[4]; +} __packed; + +struct sta_rec_ra_fixed_uni { + __le16 tag; + __le16 len; + + __le32 field; + u8 op_mode; + u8 op_vht_chan_width; + u8 op_vht_rx_nss; + u8 op_vht_rx_nss_type; + + struct sta_phy_uni phy; + + u8 spe_idx; + u8 short_preamble; + u8 is_5g; + u8 mmps_mode; +} __packed; + +struct sta_rec_hdrt { + __le16 tag; + __le16 len; + u8 hdrt_mode; + u8 rsv[3]; +} __packed; + +struct sta_rec_hdr_trans { + __le16 tag; + __le16 len; + u8 from_ds; + u8 to_ds; + u8 dis_rx_hdr_tran; + u8 mesh; +} __packed; + +struct sta_rec_ps_leave { + __le16 tag; + __le16 len; + u8 __rsv[4]; +} __packed; + +struct sta_rec_mld_setup { + __le16 tag; + __le16 len; + u8 mld_addr[ETH_ALEN]; + __le16 primary_id; + __le16 seconed_id; + __le16 setup_wcid; + u8 link_num; + u8 info; + u8 __rsv[2]; + u8 link_info[]; +} __packed; + +struct sta_rec_eht_mld { + __le16 tag; + __le16 len; + u8 nsep; + u8 __rsv1[2]; + u8 str_cap[__MT_MAX_BAND]; + __le16 eml_cap; + u8 __rsv2[4]; +} __packed; + +struct mld_setup_link { + __le16 wcid; + u8 bss_idx; + u8 __rsv; +} __packed; + +struct mld_req_hdr { + u8 ver; + u8 mld_addr[ETH_ALEN]; + u8 mld_idx; + u8 flag; + u8 rsv[3]; + u8 buf[]; +} __packed; + +struct mld_reconf_stop_link { + __le16 tag; + __le16 len; + __le16 link_bitmap; + u8 rsv[2]; + u8 bss_idx[16]; +} __packed; + +enum { + UNI_CMD_MLD_RECONF_AP_REM_TIMER = 0x03, + UNI_CMD_MLD_RECONF_STOP_LINK = 0x04, +}; + +struct hdr_trans_en { + __le16 tag; + __le16 len; + u8 enable; + u8 check_bssid; + u8 mode; + u8 __rsv; +} __packed; + +struct hdr_trans_vlan { + __le16 tag; + __le16 len; + u8 insert_vlan; + u8 remove_vlan; + u8 tid; + u8 __rsv; +} __packed; + +struct hdr_trans_blacklist { + __le16 tag; + __le16 len; + u8 idx; + u8 enable; + __le16 type; +} __packed; + +struct uni_header { + u8 __rsv[4]; +} __packed; + +struct vow_rx_airtime { + __le16 tag; + __le16 len; + + u8 enable; + u8 band; + u8 __rsv[2]; +} __packed; + +struct bf_sounding_on { + __le16 tag; + __le16 len; + + u8 snd_mode; + u8 sta_num; + u8 __rsv[2]; + __le16 wlan_id[4]; + __le32 snd_period; +} __packed; + +struct bf_hw_en_status_update { + __le16 tag; + __le16 len; + + bool ebf; + bool ibf; + u8 __rsv[2]; +} __packed; + +struct bf_mod_en_ctrl { + __le16 tag; + __le16 len; + + u8 bf_num; + u8 bf_bitmap; + u8 bf_sel[8]; + u8 __rsv[2]; +} __packed; + +union bf_tag_tlv { + struct bf_sounding_on bf_snd; + struct bf_hw_en_status_update bf_hw_en; + struct bf_mod_en_ctrl bf_mod_en; +}; + +struct ra_rate { + __le16 wlan_idx; + u8 mode; + u8 stbc; + __le16 gi; + u8 bw; + u8 ldpc; + u8 mcs; + u8 nss; + __le16 ltf; + u8 spe; + u8 preamble; + u8 __rsv[2]; +} __packed; + +struct ra_fixed_rate { + __le16 tag; + __le16 len; + + __le16 version; + struct ra_rate rate; +} __packed; + +enum { + UNI_RA_FIXED_RATE = 0xf, +}; + +#define MT7996_HDR_TRANS_MAX_SIZE (sizeof(struct hdr_trans_en) + \ + sizeof(struct hdr_trans_vlan) + \ + sizeof(struct hdr_trans_blacklist)) + +enum { + UNI_HDR_TRANS_EN, + UNI_HDR_TRANS_VLAN, + UNI_HDR_TRANS_BLACKLIST, +}; + +enum { + RATE_PARAM_FIXED = 3, + RATE_PARAM_MMPS_UPDATE = 5, + RATE_PARAM_FIXED_HE_LTF = 7, + RATE_PARAM_FIXED_MCS, + RATE_PARAM_FIXED_GI = 11, + RATE_PARAM_AUTO = 20, +}; + +enum { + BF_SOUNDING_ON = 1, + BF_HW_EN_UPDATE = 17, + BF_MOD_EN_CTRL = 20, +}; + +enum { + CMD_BAND_NONE, + CMD_BAND_24G, + CMD_BAND_5G, + CMD_BAND_6G, +}; + +struct bss_req_hdr { + u8 bss_idx; + u8 __rsv[3]; +} __packed; + +enum { + UNI_CHANNEL_SWITCH, + UNI_CHANNEL_RX_PATH, +}; + +enum { + UNI_CHIP_CONFIG_NIC_CAPA = 3, + UNI_CHIP_CONFIG_DUP_WTBL = 4, +}; + +#define MT7996_BSS_UPDATE_MAX_SIZE (sizeof(struct bss_req_hdr) + \ + sizeof(struct mt76_connac_bss_basic_tlv) + \ + sizeof(struct bss_rlm_tlv) + \ + sizeof(struct bss_ra_tlv) + \ + sizeof(struct bss_info_uni_he) + \ + sizeof(struct bss_rate_tlv) + \ + sizeof(struct bss_txcmd_tlv) + \ + sizeof(struct bss_power_save) + \ + sizeof(struct bss_sec_tlv) + \ + sizeof(struct bss_ifs_time_tlv) + \ + sizeof(struct bss_mld_tlv)) + +#define MT7996_STA_UPDATE_MAX_SIZE (sizeof(struct sta_req_hdr) + \ + sizeof(struct sta_rec_basic) + \ + sizeof(struct sta_rec_bf) + \ + sizeof(struct sta_rec_ht_uni) + \ + sizeof(struct sta_rec_he_v2) + \ + sizeof(struct sta_rec_ba_uni) + \ + sizeof(struct sta_rec_vht) + \ + sizeof(struct sta_rec_uapsd) + \ + sizeof(struct sta_rec_amsdu) + \ + sizeof(struct sta_rec_bfee) + \ + sizeof(struct sta_rec_ra_uni) + \ + sizeof(struct sta_rec_sec) + \ + sizeof(struct sta_rec_ra_fixed_uni) + \ + sizeof(struct sta_rec_he_6g_capa) + \ + sizeof(struct sta_rec_eht) + \ + sizeof(struct sta_rec_hdrt) + \ + sizeof(struct sta_rec_hdr_trans) + \ + sizeof(struct sta_rec_mld_setup) + \ + sizeof(struct mld_setup_link) * 3 + \ + sizeof(struct sta_rec_eht_mld) + \ + sizeof(struct tlv)) + +#define MT7996_BEACON_UPDATE_SIZE (sizeof(struct bss_req_hdr) + \ + sizeof(struct bss_bcn_content_tlv) + \ + 4 + MT_TXD_SIZE + \ + sizeof(struct bss_bcn_cntdwn_tlv) * 2 + \ + sizeof(struct bss_bcn_mbss_tlv)) +#define MT7996_MAX_BSS_OFFLOAD_SIZE 2048 +#define MT7996_MAX_BEACON_SIZE (MT7996_MAX_BSS_OFFLOAD_SIZE - \ + MT7996_BEACON_UPDATE_SIZE) + +enum { + UNI_BAND_CONFIG_RADIO_ENABLE, + UNI_BAND_CONFIG_RTS_THRESHOLD = 0x08, + UNI_BAND_CONFIG_MAC_ENABLE_CTRL = 0x0c, + UNI_BAND_CONFIG_BSSID_MAPPING_ADDR = 0x12, +}; + +enum { + UNI_WSYS_CONFIG_FW_LOG_CTRL, + UNI_WSYS_CONFIG_FW_DBG_CTRL, +}; + +enum { + UNI_RDD_CTRL_PARM, + UNI_RDD_CTRL_SET_TH = 0x3, +}; + +enum { + UNI_EFUSE_ACCESS = 1, + UNI_EFUSE_BUFFER_MODE, + UNI_EFUSE_FREE_BLOCK, + UNI_EFUSE_BUFFER_RD, +}; + +enum { + UNI_EXT_EEPROM_ACCESS = 1, +}; + +enum { + UNI_VOW_DRR_CTRL, + UNI_VOW_RX_AT_AIRTIME_EN = 0x0b, + UNI_VOW_RX_AT_AIRTIME_CLR_EN = 0x0e, +}; + +enum { + UNI_CMD_MIB_DATA, +}; + +enum { + UNI_POWER_OFF, +}; + +enum { + UNI_CMD_TWT_ARGT_UPDATE = 0x0, + UNI_CMD_TWT_MGMT_OFFLOAD, +}; + +enum { + UNI_RRO_DEL_ENTRY = 0x1, + UNI_RRO_SET_PLATFORM_TYPE, + UNI_RRO_GET_BA_SESSION_TABLE, + UNI_RRO_SET_BYPASS_MODE, + UNI_RRO_SET_TXFREE_PATH, + UNI_RRO_DEL_BA_SESSION, + UNI_RRO_SET_FLUSH_TIMEOUT +}; + +enum{ + UNI_CMD_SR_ENABLE = 0x1, + UNI_CMD_SR_ENABLE_SD, + UNI_CMD_SR_ENABLE_MODE, + UNI_CMD_SR_ENABLE_DPD = 0x12, + UNI_CMD_SR_ENABLE_TX, + UNI_CMD_SR_SET_SRG_BITMAP = 0x80, + UNI_CMD_SR_SET_PARAM = 0xc1, + UNI_CMD_SR_SET_SIGA = 0xd0, +}; + +enum { + UNI_CMD_THERMAL_PROTECT_ENABLE = 0x6, + UNI_CMD_THERMAL_PROTECT_DISABLE, + UNI_CMD_THERMAL_PROTECT_DUTY_CONFIG, +}; + +enum { + UNI_TXPOWER_POWER_LIMIT_TABLE_CTRL = 4, +}; + +enum { + UNI_CMD_ACCESS_REG_BASIC = 0x0, + UNI_CMD_ACCESS_RF_REG_BASIC, +}; + +enum { + UNI_CMD_SER_QUERY, + /* recovery */ + UNI_CMD_SER_SET_RECOVER_L1, + UNI_CMD_SER_SET_RECOVER_L2, + UNI_CMD_SER_SET_RECOVER_L3_RX_ABORT, + UNI_CMD_SER_SET_RECOVER_L3_TX_ABORT, + UNI_CMD_SER_SET_RECOVER_L3_TX_DISABLE, + UNI_CMD_SER_SET_RECOVER_L3_BF, + UNI_CMD_SER_SET_RECOVER_L4_MDP, + UNI_CMD_SER_SET_RECOVER_FROM_ETH, + UNI_CMD_SER_SET_RECOVER_FULL = 8, + UNI_CMD_SER_SET_SYSTEM_ASSERT, + /* action */ + UNI_CMD_SER_ENABLE = 1, + UNI_CMD_SER_SET, + UNI_CMD_SER_TRIGGER +}; + +enum { + UNI_CMD_SDO_SET = 1, + UNI_CMD_SDO_QUERY, + UNI_CMD_SDO_CP_MODE = 6, +}; + +enum { + MT7996_SEC_MODE_PLAIN, + MT7996_SEC_MODE_AES, + MT7996_SEC_MODE_SCRAMBLE, + MT7996_SEC_MODE_MAX, +}; + +#define MT7996_PATCH_SEC GENMASK(31, 24) +#define MT7996_PATCH_SCRAMBLE_KEY GENMASK(15, 8) +#define MT7996_PATCH_AES_KEY GENMASK(7, 0) + +#define MT7996_SEC_ENCRYPT BIT(0) +#define MT7996_SEC_KEY_IDX GENMASK(2, 1) +#define MT7996_SEC_IV BIT(3) + +struct fixed_rate_table_ctrl { + u8 _rsv[4]; + + __le16 tag; + __le16 len; + + u8 table_idx; + u8 antenna_idx; + __le16 rate_idx; + u8 spe_idx_sel; + u8 spe_idx; + u8 gi; + u8 he_ltf; + bool ldpc; + bool txbf; + bool dynamic_bw; + + u8 _rsv2; +} __packed; + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c new file mode 100644 index 000000000..fbfbb96a7 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c @@ -0,0 +1,909 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (C) 2022 MediaTek Inc. + */ + +#include +#include +#include +#include + +#include "mt7996.h" +#include "mac.h" +#include "mcu.h" +#include "../trace.h" +#include "../dma.h" + +static bool wed_enable; +module_param(wed_enable, bool, 0644); + +#define INVALID_REG_ADDR 0xffffffff + +static const struct __base mt7996_reg_base[] = { + [WF_AGG_BASE] = { { 0x820e2000, 0x820f2000, 0x830e2000 } }, + [WF_ARB_BASE] = { { 0x820e3000, 0x820f3000, 0x830e3000 } }, + [WF_TMAC_BASE] = { { 0x820e4000, 0x820f4000, 0x830e4000 } }, + [WF_RMAC_BASE] = { { 0x820e5000, 0x820f5000, 0x830e5000 } }, + [WF_DMA_BASE] = { { 0x820e7000, 0x820f7000, 0x830e7000 } }, + [WF_WTBLOFF_BASE] = { { 0x820e9000, 0x820f9000, 0x830e9000 } }, + [WF_ETBF_BASE] = { { 0x820ea000, 0x820fa000, 0x830ea000 } }, + [WF_LPON_BASE] = { { 0x820eb000, 0x820fb000, 0x830eb000 } }, + [WF_MIB_BASE] = { { 0x820ed000, 0x820fd000, 0x830ed000 } }, + [WF_RATE_BASE] = { { 0x820ee000, 0x820fe000, 0x830ee000 } }, +}; + +static const u32 mt7996_offs[] = { + [MIB_RVSR0] = 0x720, + [MIB_RVSR1] = 0x724, + [MIB_BTSCR5] = 0x788, + [MIB_BTSCR6] = 0x798, + [MIB_RSCR1] = 0x7ac, + [MIB_RSCR27] = 0x954, + [MIB_RSCR28] = 0x958, + [MIB_RSCR29] = 0x95c, + [MIB_RSCR30] = 0x960, + [MIB_RSCR31] = 0x964, + [MIB_RSCR33] = 0x96c, + [MIB_RSCR35] = 0x974, + [MIB_RSCR36] = 0x978, + [MIB_BSCR0] = 0x9cc, + [MIB_BSCR1] = 0x9d0, + [MIB_BSCR2] = 0x9d4, + [MIB_BSCR3] = 0x9d8, + [MIB_BSCR4] = 0x9dc, + [MIB_BSCR5] = 0x9e0, + [MIB_BSCR6] = 0x9e4, + [MIB_BSCR7] = 0x9e8, + [MIB_BSCR17] = 0xa10, + [MIB_TRDR1] = 0xa28, + [MIB_TSCR0] = 0x6b0, + [MIB_TSCR1] = 0x6b4, + [MIB_TSCR2] = 0x6b8, + [MIB_TSCR3] = 0x6bc, + [MIB_TSCR4] = 0x6c0, + [MIB_TSCR5] = 0x6c4, + [MIB_TSCR6] = 0x6c8, + [MIB_TSCR7] = 0x6d0, + [HIF_REMAP_L1] = 0x24, + [HIF_REMAP_BASE_L1] = 0x130000, + [HIF_REMAP_L2] = 0x1b4, + [HIF_REMAP_BASE_L2] = 0x1000, + [CBTOP1_PHY_END] = 0x77ffffff, + [INFRA_MCU_END] = 0x7c3fffff, + [WTBLON_WDUCR] = 0x370, + [WTBL_UPDATE] = 0x380, + [WTBL_ITCR] = 0x3b0, + [WTBL_ITCR0] = 0x3b8, + [WTBL_ITCR1] = 0x3bc, +}; + +static const u32 mt7992_offs[] = { + [MIB_RVSR0] = 0x760, + [MIB_RVSR1] = 0x764, + [MIB_BTSCR5] = 0x7c8, + [MIB_BTSCR6] = 0x7d8, + [MIB_RSCR1] = 0x7f0, + [MIB_RSCR27] = 0x998, + [MIB_RSCR28] = 0x99c, + [MIB_RSCR29] = 0x9a0, + [MIB_RSCR30] = 0x9a4, + [MIB_RSCR31] = 0x9a8, + [MIB_RSCR33] = 0x9b0, + [MIB_RSCR35] = 0x9b8, + [MIB_RSCR36] = 0x9bc, + [MIB_BSCR0] = 0xac8, + [MIB_BSCR1] = 0xacc, + [MIB_BSCR2] = 0xad0, + [MIB_BSCR3] = 0xad4, + [MIB_BSCR4] = 0xad8, + [MIB_BSCR5] = 0xadc, + [MIB_BSCR6] = 0xae0, + [MIB_BSCR7] = 0xae4, + [MIB_BSCR17] = 0xb0c, + [MIB_TRDR1] = 0xb24, + [MIB_TSCR0] = 0x6b0, + [MIB_TSCR1] = 0x6b4, + [MIB_TSCR2] = 0x6b8, + [MIB_TSCR3] = 0x6bc, + [MIB_TSCR4] = 0x6c0, + [MIB_TSCR5] = 0x6c4, + [MIB_TSCR6] = 0x6c8, + [MIB_TSCR7] = 0x6d0, + [HIF_REMAP_L1] = 0x8, + [HIF_REMAP_BASE_L1] = 0x40000, + [HIF_REMAP_L2] = 0x1b4, + [HIF_REMAP_BASE_L2] = 0x1000, + [CBTOP1_PHY_END] = 0x77ffffff, + [INFRA_MCU_END] = 0x7c3fffff, + [WTBLON_WDUCR] = 0x370, + [WTBL_UPDATE] = 0x380, + [WTBL_ITCR] = 0x3b0, + [WTBL_ITCR0] = 0x3b8, + [WTBL_ITCR1] = 0x3bc, +}; + +static const u32 mt7990_offs[] = { + [MIB_RVSR0] = 0x800, + [MIB_RVSR1] = 0x804, + [MIB_BTSCR5] = 0x868, + [MIB_BTSCR6] = 0x878, + [MIB_RSCR1] = 0x890, + [MIB_RSCR27] = 0xa38, + [MIB_RSCR28] = 0xa3c, + [MIB_RSCR29] = 0xa40, + [MIB_RSCR30] = 0xa44, + [MIB_RSCR31] = 0xa48, + [MIB_RSCR33] = 0xa50, + [MIB_RSCR35] = 0xa58, + [MIB_RSCR36] = 0xa5c, + [MIB_BSCR0] = 0xbb8, + [MIB_BSCR1] = 0xbbc, + [MIB_BSCR2] = 0xbc0, + [MIB_BSCR3] = 0xbc4, + [MIB_BSCR4] = 0xbc8, + [MIB_BSCR5] = 0xbcc, + [MIB_BSCR6] = 0xbd0, + [MIB_BSCR7] = 0xbd4, + [MIB_BSCR17] = 0xbfc, + [MIB_TRDR1] = 0xc14, + [MIB_TSCR0] = 0x750, + [MIB_TSCR1] = 0x754, + [MIB_TSCR2] = 0x758, + [MIB_TSCR3] = 0x75c, + [MIB_TSCR4] = 0x760, + [MIB_TSCR5] = 0x764, + [MIB_TSCR6] = 0x768, + [MIB_TSCR7] = 0x770, + [HIF_REMAP_L1] = 0x8, + [HIF_REMAP_BASE_L1] = 0x40000, + [HIF_REMAP_L2] = 0x1b8, + [HIF_REMAP_BASE_L2] = 0x110000, + [CBTOP1_PHY_END] = 0x7fffffff, + [INFRA_MCU_END] = 0x7cffffff, + [WTBLON_WDUCR] = 0x400, + [WTBL_UPDATE] = 0x410, + [WTBL_ITCR] = 0x440, + [WTBL_ITCR0] = 0x448, + [WTBL_ITCR1] = 0x44c, +}; + +static const struct __map mt7996_reg_map[] = { + { 0x54000000, 0x02000, 0x1000 }, /* WFDMA_0 (PCIE0 MCU DMA0) */ + { 0x55000000, 0x03000, 0x1000 }, /* WFDMA_1 (PCIE0 MCU DMA1) */ + { 0x56000000, 0x04000, 0x1000 }, /* WFDMA reserved */ + { 0x57000000, 0x05000, 0x1000 }, /* WFDMA MCU wrap CR */ + { 0x58000000, 0x06000, 0x1000 }, /* WFDMA PCIE1 MCU DMA0 (MEM_DMA) */ + { 0x59000000, 0x07000, 0x1000 }, /* WFDMA PCIE1 MCU DMA1 */ + { 0x820c0000, 0x08000, 0x4000 }, /* WF_UMAC_TOP (PLE) */ + { 0x820c8000, 0x0c000, 0x2000 }, /* WF_UMAC_TOP (PSE) */ + { 0x820cc000, 0x0e000, 0x1000 }, /* WF_UMAC_TOP (PP) */ + { 0x74030000, 0x10000, 0x1000 }, /* PCIe MAC */ + { 0x820e0000, 0x20000, 0x0400 }, /* WF_LMAC_TOP BN0 (WF_CFG) */ + { 0x820e1000, 0x20400, 0x0200 }, /* WF_LMAC_TOP BN0 (WF_TRB) */ + { 0x820e2000, 0x20800, 0x0400 }, /* WF_LMAC_TOP BN0 (WF_AGG) */ + { 0x820e3000, 0x20c00, 0x0400 }, /* WF_LMAC_TOP BN0 (WF_ARB) */ + { 0x820e4000, 0x21000, 0x0400 }, /* WF_LMAC_TOP BN0 (WF_TMAC) */ + { 0x820e5000, 0x21400, 0x0800 }, /* WF_LMAC_TOP BN0 (WF_RMAC) */ + { 0x820ce000, 0x21c00, 0x0200 }, /* WF_LMAC_TOP (WF_SEC) */ + { 0x820e7000, 0x21e00, 0x0200 }, /* WF_LMAC_TOP BN0 (WF_DMA) */ + { 0x820cf000, 0x22000, 0x1000 }, /* WF_LMAC_TOP (WF_PF) */ + { 0x820e9000, 0x23400, 0x0200 }, /* WF_LMAC_TOP BN0 (WF_WTBLOFF) */ + { 0x820ea000, 0x24000, 0x0200 }, /* WF_LMAC_TOP BN0 (WF_ETBF) */ + { 0x820eb000, 0x24200, 0x0400 }, /* WF_LMAC_TOP BN0 (WF_LPON) */ + { 0x820ec000, 0x24600, 0x0200 }, /* WF_LMAC_TOP BN0 (WF_INT) */ + { 0x820ed000, 0x24800, 0x0800 }, /* WF_LMAC_TOP BN0 (WF_MIB) */ + { 0x820ca000, 0x26000, 0x2000 }, /* WF_LMAC_TOP BN0 (WF_MUCOP) */ + { 0x820d0000, 0x30000, 0x10000 }, /* WF_LMAC_TOP (WF_WTBLON) */ + { 0x40000000, 0x70000, 0x10000 }, /* WF_UMAC_SYSRAM */ + { 0x00400000, 0x80000, 0x10000 }, /* WF_MCU_SYSRAM */ + { 0x00410000, 0x90000, 0x10000 }, /* WF_MCU_SYSRAM (configure register) */ + { 0x820f0000, 0xa0000, 0x0400 }, /* WF_LMAC_TOP BN1 (WF_CFG) */ + { 0x820f1000, 0xa0600, 0x0200 }, /* WF_LMAC_TOP BN1 (WF_TRB) */ + { 0x820f2000, 0xa0800, 0x0400 }, /* WF_LMAC_TOP BN1 (WF_AGG) */ + { 0x820f3000, 0xa0c00, 0x0400 }, /* WF_LMAC_TOP BN1 (WF_ARB) */ + { 0x820f4000, 0xa1000, 0x0400 }, /* WF_LMAC_TOP BN1 (WF_TMAC) */ + { 0x820f5000, 0xa1400, 0x0800 }, /* WF_LMAC_TOP BN1 (WF_RMAC) */ + { 0x820f7000, 0xa1e00, 0x0200 }, /* WF_LMAC_TOP BN1 (WF_DMA) */ + { 0x820f9000, 0xa3400, 0x0200 }, /* WF_LMAC_TOP BN1 (WF_WTBLOFF) */ + { 0x820fa000, 0xa4000, 0x0200 }, /* WF_LMAC_TOP BN1 (WF_ETBF) */ + { 0x820fb000, 0xa4200, 0x0400 }, /* WF_LMAC_TOP BN1 (WF_LPON) */ + { 0x820fc000, 0xa4600, 0x0200 }, /* WF_LMAC_TOP BN1 (WF_INT) */ + { 0x820fd000, 0xa4800, 0x0800 }, /* WF_LMAC_TOP BN1 (WF_MIB) */ + { 0x820cc000, 0xa5000, 0x2000 }, /* WF_LMAC_TOP BN1 (WF_MUCOP) */ + { 0x820c4000, 0xa8000, 0x4000 }, /* WF_LMAC_TOP BN1 (WF_MUCOP) */ + { 0x820b0000, 0xae000, 0x1000 }, /* [APB2] WFSYS_ON */ + { 0x80020000, 0xb0000, 0x10000 }, /* WF_TOP_MISC_OFF */ + { 0x81020000, 0xc0000, 0x10000 }, /* WF_TOP_MISC_ON */ + { 0x7c020000, 0xd0000, 0x10000 }, /* CONN_INFRA, wfdma */ + { 0x7c060000, 0xe0000, 0x10000 }, /* CONN_INFRA, conn_host_csr_top */ + { 0x7c000000, 0xf0000, 0x10000 }, /* CONN_INFRA */ + { 0x0, 0x0, 0x0 }, /* imply end of search */ +}; + +static const struct __map mt7990_reg_map[] = { + {0x54000000, 0x02000, 0x1000}, /* WFDMA_0 (PCIE0 MCU DMA0) */ + {0x55000000, 0x03000, 0x1000}, /* WFDMA_1 (PCIE0 MCU DMA1) */ + {0x56000000, 0x04000, 0x1000}, /* WFDMA_2 (Reserved) */ + {0x57000000, 0x05000, 0x1000}, /* WFDMA_3 (MCU wrap CR) */ + {0x58000000, 0x06000, 0x1000}, /* WFDMA_4 (PCIE1 MCU DMA0 (MEM_DMA)) */ + {0x59000000, 0x07000, 0x1000}, /* WFDMA_5 (PCIE1 MCU DMA1) */ + {0x820c0000, 0x08000, 0x4000}, /* WF_UMAC_TOP (PLE) */ + {0x820c8000, 0x0c000, 0x2000}, /* WF_UMAC_TOP (PSE) */ + {0x820cc000, 0x0e000, 0x2000}, /* WF_UMAC_TOP (PP) */ + {0x820e0000, 0x20000, 0x0400}, /* WF_LMAC_TOP BN0 (WF_CFG) */ + {0x820e1000, 0x20400, 0x0200}, /* WF_LMAC_TOP BN0 (WF_TRB) */ + {0x820e2000, 0x20800, 0x0400}, /* WF_LMAC_TOP BN0 (WF_AGG) */ + {0x820e3000, 0x20c00, 0x0400}, /* WF_LMAC_TOP BN0 (WF_ARB) */ + {0x820e4000, 0x21000, 0x0400}, /* WF_LMAC_TOP BN0 (WF_TMAC) */ + {0x820e5000, 0x21400, 0x0800}, /* WF_LMAC_TOP BN0 (WF_RMAC) */ + {0x820ce000, 0x21c00, 0x0200}, /* WF_LMAC_TOP (WF_SEC) */ + {0x820e7000, 0x21e00, 0x0200}, /* WF_LMAC_TOP BN0 (WF_DMA) */ + {0x820cf000, 0x22000, 0x1000}, /* WF_LMAC_TOP (WF_PF) */ + {0x820e9000, 0x23400, 0x0200}, /* WF_LMAC_TOP BN0 (WF_WTBLOFF) */ + {0x820ea000, 0x24000, 0x0200}, /* WF_LMAC_TOP BN0 (WF_ETBF) */ + {0x820eb000, 0x24200, 0x0400}, /* WF_LMAC_TOP BN0 (WF_LPON) */ + {0x820ec000, 0x24600, 0x0200}, /* WF_LMAC_TOP BN0 (WF_INT) */ + {0x820ed000, 0x24800, 0x0800}, /* WF_LMAC_TOP BN0 (WF_MIB) */ + {0x820ca000, 0x26000, 0x2000}, /* WF_LMAC_TOP BN0 (WF_MUCOP) */ + {0x820d0000, 0x30000, 0x10000}, /* WF_LMAC_TOP (WF_WTBLON) */ + {0x00400000, 0x80000, 0x10000}, /* WF_MCU_SYSRAM */ + {0x820f0000, 0xa0000, 0x0400}, /* WF_LMAC_TOP BN1 (WF_CFG) */ + {0x820f1000, 0xa0600, 0x0200}, /* WF_LMAC_TOP BN1 (WF_TRB) */ + {0x820f2000, 0xa0800, 0x0400}, /* WF_LMAC_TOP BN1 (WF_AGG) */ + {0x820f3000, 0xa0c00, 0x0400}, /* WF_LMAC_TOP BN1 (WF_ARB) */ + {0x820f4000, 0xa1000, 0x0400}, /* WF_LMAC_TOP BN1 (WF_TMAC) */ + {0x820f5000, 0xa1400, 0x0800}, /* WF_LMAC_TOP BN1 (WF_RMAC) */ + {0x820f7000, 0xa1e00, 0x0200}, /* WF_LMAC_TOP BN1 (WF_DMA) */ + {0x820f9000, 0xa3400, 0x0200}, /* WF_LMAC_TOP BN1 (WF_WTBLOFF) */ + {0x820fa000, 0xa4000, 0x0200}, /* WF_LMAC_TOP BN1 (WF_ETBF) */ + {0x820fb000, 0xa4200, 0x0400}, /* WF_LMAC_TOP BN1 (WF_LPON) */ + {0x820fc000, 0xa4600, 0x0200}, /* WF_LMAC_TOP BN1 (WF_INT) */ + {0x820fd000, 0xa4800, 0x0800}, /* WF_LMAC_TOP BN1 (WF_MIB) */ + {0x820cc000, 0xa5000, 0x2000}, /* WF_LMAC_TOP BN1 (WF_MUCOP) */ + {0x820c4000, 0xa8000, 0x4000}, /* WF_LMAC_TOP (WF_UWTBL) */ + {0x81030000, 0xae000, 0x100}, /* WFSYS_AON part 1 */ + {0x81031000, 0xae100, 0x100}, /* WFSYS_AON part 2 */ + {0x81032000, 0xae200, 0x100}, /* WFSYS_AON part 3 */ + {0x81033000, 0xae300, 0x100}, /* WFSYS_AON part 4 */ + {0x81034000, 0xae400, 0x100}, /* WFSYS_AON part 5 */ + {0x80020000, 0xb0000, 0x10000}, /* WF_TOP_MISC_OFF */ + {0x81020000, 0xc0000, 0x10000}, /* WF_TOP_MISC_ON */ + {0x81040000, 0x120000, 0x1000}, /* WF_MCU_CFG_ON */ + {0x81050000, 0x121000, 0x1000}, /* WF_MCU_EINT */ + {0x81060000, 0x122000, 0x1000}, /* WF_MCU_GPT */ + {0x81070000, 0x123000, 0x1000}, /* WF_MCU_WDT */ + {0x80010000, 0x124000, 0x1000}, /* WF_AXIDMA */ + {0x7c020000, 0xd0000, 0x10000}, /* CONN_INFRA, wfdma for from CODA flow use */ + {0x7c060000, 0xe0000, 0x10000}, /* CONN_INFRA, conn_host_csr_top for from CODA flow use */ + {0x20020000, 0xd0000, 0x10000}, /* CONN_INFRA, wfdma */ + {0x20060000, 0xe0000, 0x10000}, /* CONN_INFRA, conn_host_csr_top */ + {0x7c000000, 0xf0000, 0x10000}, /* CONN_INFRA */ + {0x70020000, 0x1f0000, 0x9000}, /* PCIE remapping (AP2CONN) */ + {0x0, 0x0, 0x0}, /* imply end of search */ +}; + +static u32 mt7996_reg_map_l1(struct mt7996_dev *dev, u32 addr) +{ + u32 offset = FIELD_GET(MT_HIF_REMAP_L1_OFFSET, addr); + u32 base = FIELD_GET(MT_HIF_REMAP_L1_BASE, addr); + u32 l1_mask, val; + + if (is_mt7996(&dev->mt76)) { + l1_mask = MT_HIF_REMAP_L1_MASK_7996; + val = FIELD_PREP(MT_HIF_REMAP_L1_MASK_7996, base); + } else { + l1_mask = MT_HIF_REMAP_L1_MASK; + val = FIELD_PREP(MT_HIF_REMAP_L1_MASK, base); + } + + dev->bus_ops->rmw(&dev->mt76, MT_HIF_REMAP_L1, l1_mask, val); + /* use read to push write */ + dev->bus_ops->rr(&dev->mt76, MT_HIF_REMAP_L1); + + return MT_HIF_REMAP_BASE_L1 + offset; +} + +static u32 mt7996_reg_map_l2(struct mt7996_dev *dev, u32 addr) +{ + u32 offset, base, l2_mask, val; + + if (is_mt7990(&dev->mt76)) { + offset = FIELD_GET(MT_HIF_REMAP_L2_OFFSET_7990, addr); + base = FIELD_GET(MT_HIF_REMAP_L2_BASE_7990, addr); + l2_mask = MT_HIF_REMAP_L2_MASK_7990; + val = FIELD_PREP(MT_HIF_REMAP_L2_MASK_7990, base); + } else { + offset = FIELD_GET(MT_HIF_REMAP_L2_OFFSET, addr); + base = FIELD_GET(MT_HIF_REMAP_L2_BASE, addr); + l2_mask = MT_HIF_REMAP_L2_MASK; + val = FIELD_PREP(MT_HIF_REMAP_L2_MASK, base); + } + + dev->bus_ops->rmw(&dev->mt76, MT_HIF_REMAP_L2, l2_mask, val); + /* use read to push write */ + dev->bus_ops->rr(&dev->mt76, MT_HIF_REMAP_L2); + + return MT_HIF_REMAP_BASE_L2 + offset; +} + +static u32 mt7996_reg_map_cbtop(struct mt7996_dev *dev, u32 addr) +{ + u32 offset = FIELD_GET(MT_HIF_REMAP_CBTOP_OFFSET, addr); + u32 base = FIELD_GET(MT_HIF_REMAP_CBTOP_BASE, addr); + + dev->bus_ops->rmw(&dev->mt76, MT_HIF_REMAP_CBTOP, + MT_HIF_REMAP_CBTOP_MASK, + FIELD_PREP(MT_HIF_REMAP_CBTOP_MASK, base)); + /* use read to push write */ + dev->bus_ops->rr(&dev->mt76, MT_HIF_REMAP_CBTOP); + + return MT_HIF_REMAP_BASE_CBTOP + offset; +} + +static u32 __mt7996_reg_addr(struct mt7996_dev *dev, u32 addr) +{ + int i; + + if (addr < 0x100000) + return addr; + + for (i = 0; i < dev->reg.map_size; i++) { + u32 ofs; + + if (addr < dev->reg.map[i].phys) + continue; + + ofs = addr - dev->reg.map[i].phys; + if (ofs >= dev->reg.map[i].size) + continue; + + return dev->reg.map[i].mapped + ofs; + } + + return INVALID_REG_ADDR; +} + +static u32 __mt7996_reg_remap_addr(struct mt7996_dev *dev, u32 addr) +{ + if ((addr >= MT_INFRA_BASE && addr < MT_WFSYS0_PHY_START) || + (addr >= MT_WFSYS0_PHY_START && addr < MT_WFSYS1_PHY_START) || + (addr >= MT_WFSYS1_PHY_START && addr <= MT_WFSYS1_PHY_END)) + return mt7996_reg_map_l1(dev, addr); + + /* CONN_INFRA: covert to phyiscal addr and use layer 1 remap */ + if (addr >= MT_INFRA_MCU_START && addr <= MT_INFRA_MCU_END) { + addr = addr - MT_INFRA_MCU_START + MT_INFRA_BASE; + return mt7996_reg_map_l1(dev, addr); + } + + if (dev_is_pci(dev->mt76.dev) && + ((addr >= MT_CBTOP1_PHY_START && addr <= MT_CBTOP1_PHY_END) || + addr >= MT_CBTOP2_PHY_START)) { + if (is_mt7990(&dev->mt76)) + return mt7996_reg_map_cbtop(dev, addr); + return mt7996_reg_map_l1(dev, addr); + } + + return mt7996_reg_map_l2(dev, addr); +} + +void mt7996_memcpy_fromio(struct mt7996_dev *dev, void *buf, u32 offset, + size_t len) +{ + u32 addr = __mt7996_reg_addr(dev, offset); + + if (addr != INVALID_REG_ADDR) { + memcpy_fromio(buf, dev->mt76.mmio.regs + addr, len); + return; + } + + spin_lock_bh(&dev->reg_lock); + memcpy_fromio(buf, dev->mt76.mmio.regs + + __mt7996_reg_remap_addr(dev, offset), len); + spin_unlock_bh(&dev->reg_lock); +} + +static u32 mt7996_rr(struct mt76_dev *mdev, u32 offset) +{ + struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76); + u32 addr = __mt7996_reg_addr(dev, offset), val; + + if (addr != INVALID_REG_ADDR) + return dev->bus_ops->rr(mdev, addr); + + spin_lock_bh(&dev->reg_lock); + val = dev->bus_ops->rr(mdev, __mt7996_reg_remap_addr(dev, offset)); + spin_unlock_bh(&dev->reg_lock); + + return val; +} + +static void mt7996_wr(struct mt76_dev *mdev, u32 offset, u32 val) +{ + struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76); + u32 addr = __mt7996_reg_addr(dev, offset); + + if (addr != INVALID_REG_ADDR) { + dev->bus_ops->wr(mdev, addr, val); + return; + } + + spin_lock_bh(&dev->reg_lock); + dev->bus_ops->wr(mdev, __mt7996_reg_remap_addr(dev, offset), val); + spin_unlock_bh(&dev->reg_lock); +} + +static u32 mt7996_rmw(struct mt76_dev *mdev, u32 offset, u32 mask, u32 val) +{ + struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76); + u32 addr = __mt7996_reg_addr(dev, offset); + + if (addr != INVALID_REG_ADDR) + return dev->bus_ops->rmw(mdev, addr, mask, val); + + spin_lock_bh(&dev->reg_lock); + val = dev->bus_ops->rmw(mdev, __mt7996_reg_remap_addr(dev, offset), mask, val); + spin_unlock_bh(&dev->reg_lock); + + return val; +} + +#ifdef CONFIG_NET_MEDIATEK_SOC_WED +static int mt7996_mmio_wed_reset(struct mtk_wed_device *wed) +{ + struct mt76_dev *mdev = container_of(wed, struct mt76_dev, mmio.wed); + struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76); + struct mt76_phy *mphy = &dev->mphy; + int ret; + + ASSERT_RTNL(); + + if (test_and_set_bit(MT76_STATE_WED_RESET, &mphy->state)) + return -EBUSY; + + ret = mt7996_mcu_set_ser(dev, UNI_CMD_SER_TRIGGER, UNI_CMD_SER_SET_RECOVER_FROM_ETH, + mphy->band_idx); + if (ret) + goto out; + + rtnl_unlock(); + if (!wait_for_completion_timeout(&mdev->mmio.wed_reset, 20 * HZ)) { + dev_err(mdev->dev, "wed reset timeout\n"); + ret = -ETIMEDOUT; + } + rtnl_lock(); +out: + clear_bit(MT76_STATE_WED_RESET, &mphy->state); + + return ret; +} +#endif + +int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr, + bool hif2, int *irq) +{ +#ifdef CONFIG_NET_MEDIATEK_SOC_WED + struct mtk_wed_device *wed = &dev->mt76.mmio.wed; + struct pci_dev *pci_dev = pdev_ptr; + u32 hif1_ofs; + + if (!wed_enable) + return 0; + + if (hif2 && !mtk_wed_device_active(&dev->mt76.mmio.wed)) + return 0; + + hif1_ofs = dev->hif2 ? MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0) : 0; + + if (hif2) + wed = &dev->mt76.mmio.wed_hif2; + + wed->wlan.pci_dev = pci_dev; + wed->wlan.bus_type = MTK_WED_BUS_PCIE; + + wed->wlan.base = devm_ioremap(dev->mt76.dev, + pci_resource_start(pci_dev, 0), + pci_resource_len(pci_dev, 0)); + if (!wed->wlan.base) + return -ENOMEM; + + wed->wlan.phy_base = pci_resource_start(pci_dev, 0); + + if (hif2) { + wed->wlan.wpdma_int = wed->wlan.phy_base + + MT_INT_PCIE1_SOURCE_CSR_EXT; + wed->wlan.wpdma_mask = wed->wlan.phy_base + + MT_INT_PCIE1_MASK_CSR; + wed->wlan.wpdma_tx = wed->wlan.phy_base + hif1_ofs + + MT_TXQ_RING_BASE(0) + + MT7996_TXQ_BAND2 * MT_RING_SIZE; + if (is_mt7996(&dev->mt76)) { + wed->wlan.txfree_tbit = ffs(MT_INT_RX_TXFREE_EXT) - 1; + wed->wlan.wpdma_txfree = wed->wlan.phy_base + hif1_ofs + + MT_RXQ_RING_BASE(0) + + MT7996_RXQ_TXFREE2 * MT_RING_SIZE; + } else { + wed->wlan.txfree_tbit = ffs(MT_INT_RX_TXFREE_BAND1_EXT) - 1; + wed->wlan.wpdma_txfree = wed->wlan.phy_base + hif1_ofs + + MT_RXQ_RING_BASE(0) + + MT7996_RXQ_MCU_WA_EXT * MT_RING_SIZE; + } + + wed->wlan.wpdma_rx_glo = wed->wlan.phy_base + hif1_ofs + MT_WFDMA0_GLO_CFG; + wed->wlan.wpdma_rx[0] = wed->wlan.phy_base + hif1_ofs + + MT_RXQ_RING_BASE(MT7996_RXQ_BAND2) + + MT7996_RXQ_BAND2 * MT_RING_SIZE; + + wed->wlan.id = MT7996_DEVICE_ID_2; + wed->wlan.tx_tbit[0] = ffs(MT_INT_TX_DONE_BAND2) - 1; + } else { + wed->wlan.hw_rro = true; + wed->wlan.wpdma_int = wed->wlan.phy_base + MT_INT_SOURCE_CSR; + wed->wlan.wpdma_mask = wed->wlan.phy_base + MT_INT_MASK_CSR; + wed->wlan.wpdma_tx = wed->wlan.phy_base + MT_TXQ_RING_BASE(0) + + MT7996_TXQ_BAND0 * MT_RING_SIZE; + + wed->wlan.wpdma_rx_glo = wed->wlan.phy_base + MT_WFDMA0_GLO_CFG; + + wed->wlan.wpdma_rx[0] = wed->wlan.phy_base + + MT_RXQ_RING_BASE(MT7996_RXQ_BAND0) + + MT7996_RXQ_BAND0 * MT_RING_SIZE; + + wed->wlan.wpdma_rx_rro[0] = wed->wlan.phy_base + + MT_RXQ_RING_BASE(MT7996_RXQ_RRO_BAND0) + + MT7996_RXQ_RRO_BAND0 * MT_RING_SIZE; + if (is_mt7996(&dev->mt76)) { + wed->wlan.wpdma_rx_rro[1] = wed->wlan.phy_base + hif1_ofs + + MT_RXQ_RING_BASE(MT7996_RXQ_RRO_BAND2) + + MT7996_RXQ_RRO_BAND2 * MT_RING_SIZE; + } else { + wed->wlan.wpdma_rx_rro[1] = wed->wlan.phy_base + hif1_ofs + + MT_RXQ_RING_BASE(MT7996_RXQ_RRO_BAND1) + + MT7996_RXQ_RRO_BAND1 * MT_RING_SIZE; + wed->wlan.wpdma_rx[1] = wed->wlan.phy_base + hif1_ofs + + MT_RXQ_RING_BASE(MT7996_RXQ_BAND1) + + MT7996_RXQ_BAND1 * MT_RING_SIZE; + } + + wed->wlan.wpdma_rx_pg = wed->wlan.phy_base + + MT_RXQ_RING_BASE(MT7996_RXQ_MSDU_PG_BAND0) + + MT7996_RXQ_MSDU_PG_BAND0 * MT_RING_SIZE; + + wed->wlan.rx_nbuf = 65536; + wed->wlan.rx_npkt = dev->hif2 ? 32768 : 24576; + wed->wlan.rx_size = SKB_WITH_OVERHEAD(MT_RX_BUF_SIZE); + + wed->wlan.rx_tbit[0] = ffs(MT_INT_RX_DONE_BAND0) - 1; + wed->wlan.rro_rx_tbit[0] = ffs(MT_INT_RX_DONE_RRO_BAND0) - 1; + if (is_mt7996(&dev->mt76)) { + wed->wlan.rx_tbit[1] = ffs(MT_INT_RX_DONE_BAND2) - 1; + wed->wlan.rro_rx_tbit[1] = ffs(MT_INT_RX_DONE_RRO_BAND2) - 1; + } else { + wed->wlan.rx_tbit[1] = ffs(MT_INT_RX_DONE_BAND1) - 1; + wed->wlan.rro_rx_tbit[1] = ffs(MT_INT_RX_DONE_RRO_BAND1) - 1; + } + + wed->wlan.rx_pg_tbit[0] = ffs(MT_INT_RX_DONE_MSDU_PG_BAND0) - 1; + wed->wlan.rx_pg_tbit[1] = ffs(MT_INT_RX_DONE_MSDU_PG_BAND1) - 1; + wed->wlan.rx_pg_tbit[2] = ffs(MT_INT_RX_DONE_MSDU_PG_BAND2) - 1; + + wed->wlan.tx_tbit[0] = ffs(MT_INT_TX_DONE_BAND0) - 1; + wed->wlan.tx_tbit[1] = ffs(MT_INT_TX_DONE_BAND1) - 1; + if (is_mt7996(&dev->mt76)) { + wed->wlan.wpdma_txfree = wed->wlan.phy_base + + MT_RXQ_RING_BASE(0) + + MT7996_RXQ_TXFREE0 * MT_RING_SIZE; + wed->wlan.txfree_tbit = ffs(MT_INT_RX_TXFREE_MAIN) - 1; + } else { + wed->wlan.txfree_tbit = ffs(MT_INT_RX_DONE_WA_MAIN) - 1; + wed->wlan.wpdma_txfree = wed->wlan.phy_base + MT_RXQ_RING_BASE(0) + + MT7996_RXQ_MCU_WA_MAIN * MT_RING_SIZE; + } + + if (dev->hif2 && is_mt7992(&dev->mt76)) + wed->wlan.id = 0x7992; + } + + wed->wlan.nbuf = MT7996_HW_TOKEN_SIZE; + wed->wlan.token_start = MT7996_TOKEN_SIZE - wed->wlan.nbuf; + wed->wlan.hif2 = hif2; + + wed->wlan.amsdu_max_subframes = 8; + wed->wlan.amsdu_max_len = 1536; + + wed->wlan.init_buf = mt7996_wed_init_buf; + wed->wlan.init_rx_buf = mt76_wed_init_rx_buf; + wed->wlan.release_rx_buf = mt76_wed_release_rx_buf; + wed->wlan.offload_enable = mt76_wed_offload_enable; + wed->wlan.offload_disable = mt76_wed_offload_disable; + if (!hif2) { + wed->wlan.reset = mt7996_mmio_wed_reset; + wed->wlan.reset_complete = mt76_wed_reset_complete; + } + + if (mtk_wed_device_attach(wed)) + return 0; + + if (!hif2) { + dev->mt76.hwrro_mode = is_mt7996(&dev->mt76) ? MT76_HWRRO_V3 + : MT76_HWRRO_V3_1; + dev->mt76.rx_token_size = MT7996_TOKEN_SIZE + + wed->wlan.rx_npkt; + } + + *irq = wed->irq; + dev->mt76.dma_dev = wed->dev; + + return 1; +#else + return 0; +#endif +} + +static int mt7996_mmio_init(struct mt76_dev *mdev, + void __iomem *mem_base, + u32 device_id) +{ + struct mt76_bus_ops *bus_ops; + struct mt7996_dev *dev; + + dev = container_of(mdev, struct mt7996_dev, mt76); + mt76_mmio_init(&dev->mt76, mem_base); + spin_lock_init(&dev->reg_lock); + + switch (device_id) { + case MT7996_DEVICE_ID: + dev->reg.base = mt7996_reg_base; + dev->reg.offs_rev = mt7996_offs; + dev->reg.map = mt7996_reg_map; + dev->reg.map_size = ARRAY_SIZE(mt7996_reg_map); + break; + case MT7992_DEVICE_ID: + dev->reg.base = mt7996_reg_base; + dev->reg.offs_rev = mt7992_offs; + dev->reg.map = mt7996_reg_map; + dev->reg.map_size = ARRAY_SIZE(mt7996_reg_map); + break; + case MT7990_DEVICE_ID: + dev->reg.base = mt7996_reg_base; + dev->reg.offs_rev = mt7990_offs; + dev->reg.map = mt7990_reg_map; + dev->reg.map_size = ARRAY_SIZE(mt7990_reg_map); + break; + default: + return -EINVAL; + } + + dev->bus_ops = dev->mt76.bus; + bus_ops = devm_kmemdup(dev->mt76.dev, dev->bus_ops, sizeof(*bus_ops), + GFP_KERNEL); + if (!bus_ops) + return -ENOMEM; + + bus_ops->rr = mt7996_rr; + bus_ops->wr = mt7996_wr; + bus_ops->rmw = mt7996_rmw; + dev->mt76.bus = bus_ops; + + mdev->rev = (device_id << 16) | (mt76_rr(dev, MT_HW_REV) & 0xff); + + dev_dbg(mdev->dev, "ASIC revision: %04x\n", mdev->rev); + + return 0; +} + +void mt7996_dual_hif_set_irq_mask(struct mt7996_dev *dev, bool write_reg, + u32 clear, u32 set) +{ + struct mt76_dev *mdev = &dev->mt76; + unsigned long flags; + + spin_lock_irqsave(&mdev->mmio.irq_lock, flags); + + mdev->mmio.irqmask &= ~clear; + mdev->mmio.irqmask |= set; + + if (write_reg) { + if (mtk_wed_device_active(&mdev->mmio.wed)) { + mtk_wed_device_irq_set_mask(&mdev->mmio.wed, + mdev->mmio.irqmask); + if (mtk_wed_device_active(&mdev->mmio.wed_hif2)) { + mtk_wed_device_irq_set_mask(&mdev->mmio.wed_hif2, + mdev->mmio.irqmask); + } + } else { + mt76_wr(dev, MT_INT_MASK_CSR, mdev->mmio.irqmask); + mt76_wr(dev, MT_INT1_MASK_CSR, mdev->mmio.irqmask); + } + } + + spin_unlock_irqrestore(&mdev->mmio.irq_lock, flags); +} + +static void mt7996_rx_poll_complete(struct mt76_dev *mdev, + enum mt76_rxq_id q) +{ + if (q == MT_RXQ_NPU0 || q == MT_RXQ_NPU1) { + struct airoha_npu *npu; + + npu = rcu_dereference(mdev->mmio.npu); + if (npu) + airoha_npu_wlan_enable_irq(npu, q - MT_RXQ_NPU0); + } else { + struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, + mt76); + + mt7996_irq_enable(dev, MT_INT_RX(q)); + } +} + +/* TODO: support 2/4/6/8 MSI-X vectors */ +static void mt7996_irq_tasklet(struct tasklet_struct *t) +{ + struct mt7996_dev *dev = from_tasklet(dev, t, mt76.irq_tasklet); + struct mtk_wed_device *wed = &dev->mt76.mmio.wed; + struct mtk_wed_device *wed_hif2 = &dev->mt76.mmio.wed_hif2; + u32 i, intr, mask, intr1 = 0; + + if (dev->hif2 && mtk_wed_device_active(wed_hif2)) { + mtk_wed_device_irq_set_mask(wed_hif2, 0); + intr1 = mtk_wed_device_irq_get(wed_hif2, + dev->mt76.mmio.irqmask); + if (intr1 & MT_INT_RX_TXFREE_EXT) + napi_schedule(&dev->mt76.napi[MT_RXQ_TXFREE_BAND2]); + + if (intr1 & MT_INT_RX_DONE_BAND2_EXT) + napi_schedule(&dev->mt76.napi[MT_RXQ_BAND2]); + + if (intr1 & MT_INT_RX_TXFREE_BAND1_EXT) + napi_schedule(&dev->mt76.napi[MT_RXQ_BAND1_WA]); + } + + if (mtk_wed_device_active(wed)) { + mtk_wed_device_irq_set_mask(wed, 0); + intr = mtk_wed_device_irq_get(wed, dev->mt76.mmio.irqmask); + intr |= (intr1 & ~MT_INT_TX_RX_DONE_EXT); + } else { + mt76_wr(dev, MT_INT_MASK_CSR, 0); + if (dev->hif2) + mt76_wr(dev, MT_INT1_MASK_CSR, 0); + + intr = mt76_rr(dev, MT_INT_SOURCE_CSR); + intr &= dev->mt76.mmio.irqmask; + mt76_wr(dev, MT_INT_SOURCE_CSR, intr); + if (dev->hif2) { + intr1 = mt76_rr(dev, MT_INT1_SOURCE_CSR); + intr1 &= dev->mt76.mmio.irqmask; + mt76_wr(dev, MT_INT1_SOURCE_CSR, intr1); + intr |= intr1; + } + } + + trace_dev_irq(&dev->mt76, intr, dev->mt76.mmio.irqmask); + + mask = intr & MT_INT_RX_DONE_ALL; + if (intr & MT_INT_TX_DONE_MCU) + mask |= MT_INT_TX_DONE_MCU; + mt7996_irq_disable(dev, mask); + + if (intr & MT_INT_TX_DONE_MCU) + napi_schedule(&dev->mt76.tx_napi); + + for (i = 0; i < __MT_RXQ_MAX; i++) { + if ((intr & MT_INT_RX(i))) + napi_schedule(&dev->mt76.napi[i]); + } + + if (intr & MT_INT_MCU_CMD) { + u32 val = mt76_rr(dev, MT_MCU_CMD); + + mt76_wr(dev, MT_MCU_CMD, val); + if (val & (MT_MCU_CMD_ERROR_MASK | MT_MCU_CMD_WDT_MASK)) { + dev->recovery.state = val; + mt7996_reset(dev); + } + } +} + +irqreturn_t mt7996_irq_handler(int irq, void *dev_instance) +{ + struct mt7996_dev *dev = dev_instance; + + if (mtk_wed_device_active(&dev->mt76.mmio.wed)) + mtk_wed_device_irq_set_mask(&dev->mt76.mmio.wed, 0); + else + mt76_wr(dev, MT_INT_MASK_CSR, 0); + + if (dev->hif2) { + if (mtk_wed_device_active(&dev->mt76.mmio.wed_hif2)) + mtk_wed_device_irq_set_mask(&dev->mt76.mmio.wed_hif2, 0); + else + mt76_wr(dev, MT_INT1_MASK_CSR, 0); + } + + if (!test_bit(MT76_STATE_INITIALIZED, &dev->mphy.state)) + return IRQ_NONE; + + tasklet_schedule(&dev->mt76.irq_tasklet); + + return IRQ_HANDLED; +} + +struct mt7996_dev *mt7996_mmio_probe(struct device *pdev, + void __iomem *mem_base, u32 device_id) +{ + static const struct mt76_driver_ops drv_ops = { + /* txwi_size = txd size + txp size */ + .txwi_size = MT_TXD_SIZE + sizeof(struct mt76_connac_fw_txp), + .link_data_size = sizeof(struct mt7996_vif_link), + .drv_flags = MT_DRV_TXWI_NO_FREE | + MT_DRV_AMSDU_OFFLOAD | + MT_DRV_HW_MGMT_TXQ | + MT_DRV_HW_PS_BUFFERING, + .survey_flags = SURVEY_INFO_TIME_TX | + SURVEY_INFO_TIME_RX | + SURVEY_INFO_TIME_BSS_RX, + .token_size = MT7996_TOKEN_SIZE, + .tx_prepare_skb = mt7996_tx_prepare_skb, + .tx_complete_skb = mt76_connac_tx_complete_skb, + .rx_skb = mt7996_queue_rx_skb, + .rx_check = mt7996_rx_check, + .rx_poll_complete = mt7996_rx_poll_complete, + .rx_rro_ind_process = mt7996_rro_rx_process, + .rx_rro_add_msdu_page = mt7996_rro_msdu_page_add, + .update_survey = mt7996_update_channel, + .set_channel = mt7996_set_channel, + .vif_link_add = mt7996_vif_link_add, + .vif_link_remove = mt7996_vif_link_remove, + }; + struct mt7996_dev *dev; + struct mt76_dev *mdev; + int ret; + + mdev = mt76_alloc_device(pdev, sizeof(*dev), &mt7996_ops, &drv_ops); + if (!mdev) + return ERR_PTR(-ENOMEM); + + dev = container_of(mdev, struct mt7996_dev, mt76); + + ret = mt7996_mmio_init(mdev, mem_base, device_id); + if (ret) + goto error; + + tasklet_setup(&mdev->irq_tasklet, mt7996_irq_tasklet); + + mt76_wr(dev, MT_INT_MASK_CSR, 0); + + return dev; + +error: + mt76_free_device(&dev->mt76); + + return ERR_PTR(ret); +} + +static int __init mt7996_init(void) +{ + int ret; + + ret = pci_register_driver(&mt7996_hif_driver); + if (ret) + return ret; + + ret = pci_register_driver(&mt7996_pci_driver); + if (ret) + pci_unregister_driver(&mt7996_hif_driver); + + return ret; +} + +static void __exit mt7996_exit(void) +{ + pci_unregister_driver(&mt7996_pci_driver); + pci_unregister_driver(&mt7996_hif_driver); +} + +module_init(mt7996_init); +module_exit(mt7996_exit); +MODULE_DESCRIPTION("MediaTek MT7996 MMIO helpers"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h new file mode 100644 index 000000000..2a0cdb56f --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h @@ -0,0 +1,979 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (C) 2022 MediaTek Inc. + */ + +#ifndef __MT7996_H +#define __MT7996_H + +#include +#include +#include "../mt76_connac.h" +#include "regs.h" + +#define MT7996_MAX_RADIOS 3 +#define MT7996_MAX_INTERFACES 19 /* per-band */ +#define MT7996_MAX_WMM_SETS 4 +#define MT7996_WTBL_BMC_SIZE (is_mt7996(&dev->mt76) ? 64 : 32) +#define MT7996_WTBL_RESERVED (mt7996_wtbl_size(dev) - 1) +#define MT7996_WTBL_STA (MT7996_WTBL_RESERVED - \ + mt7996_max_interface_num(dev)) + +#define MT7996_WATCHDOG_TIME (HZ / 10) +#define MT7996_RESET_TIMEOUT (30 * HZ) + +#define MT7996_TX_RING_SIZE 2048 +#define MT7996_TX_MCU_RING_SIZE 256 +#define MT7996_TX_FWDL_RING_SIZE 128 + +#define MT7996_RX_RING_SIZE 1536 +#define MT7996_RX_MCU_RING_SIZE 512 +#define MT7996_RX_MCU_RING_SIZE_WA 1024 +#define MT7996_NPU_TX_RING_SIZE 1024 +#define MT7996_NPU_RX_RING_SIZE 1024 +#define MT7996_NPU_TXD_SIZE 3 + +/* scatter-gather of mcu event is not supported in connac3 */ +#define MT7996_RX_MCU_BUF_SIZE (2048 + \ + SKB_DATA_ALIGN(sizeof(struct skb_shared_info))) + +#define MT7996_DEVICE_ID 0x7990 +#define MT7996_DEVICE_ID_2 0x7991 +#define MT7992_DEVICE_ID 0x7992 +#define MT7992_DEVICE_ID_2 0x799a +#define MT7990_DEVICE_ID 0x7993 +#define MT7990_DEVICE_ID_2 0x799b + +#define MT7996_FIRMWARE_WA "mediatek/mt7996/mt7996_wa.bin" +#define MT7996_FIRMWARE_WM "mediatek/mt7996/mt7996_wm.bin" +#define MT7996_FIRMWARE_DSP "mediatek/mt7996/mt7996_dsp.bin" +#define MT7996_ROM_PATCH "mediatek/mt7996/mt7996_rom_patch.bin" + +#define MT7996_FIRMWARE_WA_233 "mediatek/mt7996/mt7996_wa_233.bin" +#define MT7996_FIRMWARE_WM_233 "mediatek/mt7996/mt7996_wm_233.bin" +#define MT7996_FIRMWARE_DSP_233 MT7996_FIRMWARE_DSP +#define MT7996_ROM_PATCH_233 "mediatek/mt7996/mt7996_rom_patch_233.bin" + +#define MT7992_FIRMWARE_WA "mediatek/mt7996/mt7992_wa.bin" +#define MT7992_FIRMWARE_WM "mediatek/mt7996/mt7992_wm.bin" +#define MT7992_FIRMWARE_DSP "mediatek/mt7996/mt7992_dsp.bin" +#define MT7992_ROM_PATCH "mediatek/mt7996/mt7992_rom_patch.bin" + +#define MT7992_FIRMWARE_WA_23 "mediatek/mt7996/mt7992_wa_23.bin" +#define MT7992_FIRMWARE_WM_23 "mediatek/mt7996/mt7992_wm_23.bin" +#define MT7992_FIRMWARE_DSP_23 "mediatek/mt7996/mt7992_dsp_23.bin" +#define MT7992_ROM_PATCH_23 "mediatek/mt7996/mt7992_rom_patch_23.bin" + +#define MT7992_FIRMWARE_WA_24 "mediatek/mt7996/mt7992_wa_24.bin" +#define MT7992_FIRMWARE_WM_24 "mediatek/mt7996/mt7992_wm_24.bin" +#define MT7992_FIRMWARE_DSP_24 "mediatek/mt7996/mt7992_dsp_24.bin" +#define MT7992_ROM_PATCH_24 "mediatek/mt7996/mt7992_rom_patch_24.bin" + +#define MT7990_FIRMWARE_WA "" +#define MT7990_FIRMWARE_WM "mediatek/mt7996/mt7990_wm.bin" +#define MT7990_FIRMWARE_DSP "" +#define MT7990_ROM_PATCH "mediatek/mt7996/mt7990_rom_patch.bin" + +#define MT7996_EEPROM_DEFAULT "mediatek/mt7996/mt7996_eeprom.bin" +#define MT7996_EEPROM_DEFAULT_INT "mediatek/mt7996/mt7996_eeprom_2i5i6i.bin" +#define MT7996_EEPROM_DEFAULT_233 "mediatek/mt7996/mt7996_eeprom_233.bin" +#define MT7996_EEPROM_DEFAULT_233_INT "mediatek/mt7996/mt7996_eeprom_233_2i5i6i.bin" + +#define MT7992_EEPROM_DEFAULT "mediatek/mt7996/mt7992_eeprom.bin" +#define MT7992_EEPROM_DEFAULT_INT "mediatek/mt7996/mt7992_eeprom_2i5i.bin" +#define MT7992_EEPROM_DEFAULT_MIX "mediatek/mt7996/mt7992_eeprom_2i5e.bin" +#define MT7992_EEPROM_DEFAULT_23 "mediatek/mt7996/mt7992_eeprom_23.bin" +#define MT7992_EEPROM_DEFAULT_23_INT "mediatek/mt7996/mt7992_eeprom_23_2i5i.bin" +#define MT7992_EEPROM_DEFAULT_24 "mediatek/mt7996/mt7992_eeprom_24_2i5i.bin" + +#define MT7990_EEPROM_DEFAULT "mediatek/mt7996/mt7990_eeprom.bin" +#define MT7990_EEPROM_DEFAULT_INT "mediatek/mt7996/mt7990_eeprom_2i5i.bin" + +#define MT7996_EEPROM_SIZE 7680 +#define MT7996_EEPROM_BLOCK_SIZE 16 +#define MT7996_EXT_EEPROM_BLOCK_SIZE 1024 +#define MT7996_TOKEN_SIZE 16384 +#define MT7996_HW_TOKEN_SIZE 8192 + +#define MT7996_CFEND_RATE_DEFAULT 0x49 /* OFDM 24M */ +#define MT7996_CFEND_RATE_11B 0x03 /* 11B LP, 11M */ +#define MT7996_IBF_MAX_NC 2 +#define MT7996_IBF_TIMEOUT 0x18 +#define MT7996_IBF_TIMEOUT_LEGACY 0x48 + +#define MT7992_CFEND_RATE_DEFAULT 0x4b /* OFDM 6M */ +#define MT7992_IBF_TIMEOUT 0xff + +#define MT7996_SKU_RATE_NUM 417 +#define MT7996_SKU_PATH_NUM 494 + +#define MT7996_MAX_TWT_AGRT 16 +#define MT7996_MAX_STA_TWT_AGRT 8 +#define MT7996_MIN_TWT_DUR 64 +#define MT7996_MAX_QUEUE (__MT_RXQ_MAX + __MT_MCUQ_MAX + 3) + +/* NOTE: used to map mt76_rates. idx may change if firmware expands table */ +#define MT7996_BASIC_RATES_TBL 31 +#define MT7996_BEACON_RATES_TBL 25 + +#define MT7996_THERMAL_THROTTLE_MAX 100 +#define MT7996_CDEV_THROTTLE_MAX 99 +#define MT7996_CRIT_TEMP_IDX 0 +#define MT7996_MAX_TEMP_IDX 1 +#define MT7996_CRIT_TEMP 110 +#define MT7996_MAX_TEMP 120 + +#define MT7996_MAX_HIF_RXD_IN_PG 5 +#define MT7996_RRO_MSDU_PG_HASH_SIZE 127 +#define MT7996_RRO_MAX_SESSION 1024 +#define MT7996_RRO_WINDOW_MAX_LEN 1024 +#define MT7996_RRO_ADDR_ELEM_LEN 128 +#define MT7996_RRO_BA_BITMAP_LEN 2 +#define MT7996_RRO_BA_BITMAP_CR_SIZE ((MT7996_RRO_MAX_SESSION * 128) / \ + MT7996_RRO_BA_BITMAP_LEN) +#define MT7996_RRO_BA_BITMAP_SESSION_SIZE (MT7996_RRO_MAX_SESSION / \ + MT7996_RRO_ADDR_ELEM_LEN) +#define MT7996_RRO_WINDOW_MAX_SIZE (MT7996_RRO_WINDOW_MAX_LEN * \ + MT7996_RRO_BA_BITMAP_SESSION_SIZE) + +#define MT7996_RX_BUF_SIZE (1800 + \ + SKB_DATA_ALIGN(sizeof(struct skb_shared_info))) +#define MT7996_RX_MSDU_PAGE_SIZE (128 + \ + SKB_DATA_ALIGN(sizeof(struct skb_shared_info))) + +/* RRO 3.1 */ +#define MT7996_RRO_MSDU_PG_CR_CNT 8 +#define MT7996_RRO_MSDU_PG_SIZE_PER_CR 0x10000 + +struct mt7996_vif; +struct mt7996_sta; +struct mt7996_dfs_pulse; +struct mt7996_dfs_pattern; + +enum mt7996_ram_type { + MT7996_RAM_TYPE_WM, + MT7996_RAM_TYPE_WA, + MT7996_RAM_TYPE_DSP, +}; + +enum mt7996_var_type { + MT7996_VAR_TYPE_444, + MT7996_VAR_TYPE_233, +}; + +enum mt7992_var_type { + MT7992_VAR_TYPE_44, + MT7992_VAR_TYPE_23, + MT7992_VAR_TYPE_24, +}; + +enum mt7990_var_type { + MT7990_VAR_TYPE_23, +}; + +enum mt7996_fem_type { + MT7996_FEM_EXT, + MT7996_FEM_INT, + MT7996_FEM_MIX, +}; + +enum mt7996_eeprom_mode { + EEPROM_MODE_DEFAULT_BIN, + EEPROM_MODE_EFUSE, + EEPROM_MODE_FLASH, + EEPROM_MODE_EXT, +}; + +#define MT7996_EFUSE_BASE_OFFS_ADIE0 0x400 +#define MT7996_EFUSE_BASE_OFFS_ADIE1 0x1e00 +#define MT7996_EFUSE_BASE_OFFS_ADIE2 0x1200 +#define MT7992_EFUSE_BASE_OFFS_ADIE1 0x1200 + +enum mt7996_txq_id { + MT7996_TXQ_FWDL = 16, + MT7996_TXQ_MCU_WM, + MT7996_TXQ_BAND0, + MT7996_TXQ_BAND1, + MT7996_TXQ_MCU_WA, + MT7996_TXQ_BAND2, +}; + +enum mt7996_rxq_id { + MT7996_RXQ_MCU_WM = 0, + MT7996_RXQ_MCU_WA, + MT7996_RXQ_MCU_WA_MAIN = 2, + MT7996_RXQ_MCU_WA_EXT = 3, /* for mt7992 */ + MT7996_RXQ_MCU_WA_TRI = 3, + MT7996_RXQ_BAND0 = 4, + MT7996_RXQ_BAND1 = 5, /* for mt7992 */ + MT7996_RXQ_BAND2 = 5, + MT7996_RXQ_RRO_BAND0 = 8, + MT7996_RXQ_RRO_BAND1 = 9, + MT7996_RXQ_RRO_BAND2 = 6, + MT7996_RXQ_MSDU_PG_BAND0 = 10, + MT7996_RXQ_MSDU_PG_BAND1 = 11, + MT7996_RXQ_MSDU_PG_BAND2 = 12, + MT7996_RXQ_TXFREE0 = 9, + MT7996_RXQ_TXFREE1 = 9, + MT7996_RXQ_TXFREE2 = 7, + MT7996_RXQ_RRO_IND = 0, + MT7996_RXQ_RRO_RXDMAD_C = 0, + MT7990_RXQ_TXFREE0 = 6, + MT7990_RXQ_TXFREE1 = 7, +}; + +struct mt7996_twt_flow { + struct list_head list; + u64 start_tsf; + u64 tsf; + u32 duration; + u16 wcid; + __le16 mantissa; + u8 exp; + u8 table_id; + u8 id; + u8 protection:1; + u8 flowtype:1; + u8 trigger:1; + u8 sched:1; +}; + +DECLARE_EWMA(avg_signal, 10, 8) + +struct mt7996_sta_link { + struct mt76_wcid wcid; /* must be first */ + + struct mt7996_sta *sta; + + struct list_head rc_list; + u32 airtime_ac[8]; + + int ack_signal; + struct ewma_avg_signal avg_ack_signal; + + unsigned long changed; + + struct mt76_connac_sta_key_conf bip; + + struct { + u8 flowid_mask; + struct mt7996_twt_flow flow[MT7996_MAX_STA_TWT_AGRT]; + } twt; + + struct rcu_head rcu_head; +}; + +struct mt7996_sta { + struct mt7996_sta_link deflink; /* must be first */ + struct mt7996_sta_link __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS]; + u8 deflink_id; + u8 seclink_id; + + struct mt7996_vif *vif; +}; + +struct mt7996_vif_link { + struct mt76_vif_link mt76; /* must be first */ + + struct mt7996_sta_link msta_link; + struct cfg80211_bitrate_mask bitrate_mask; + + u8 mld_idx; +}; + +struct mt7996_vif_link_info { + struct ieee80211_tx_queue_params queue_params[IEEE80211_NUM_ACS]; +}; + +struct mt7996_vif { + struct mt7996_vif_link deflink; /* must be first */ + struct mt76_vif_data mt76; + + struct mt7996_vif_link_info link_info[IEEE80211_MLD_MAX_NUM_LINKS]; + + u8 mld_group_idx; + u8 mld_remap_idx; +}; + +/* crash-dump */ +struct mt7996_crash_data { + guid_t guid; + struct timespec64 timestamp; + + u8 *memdump_buf; + size_t memdump_buf_len; +}; + +struct mt7996_hif { + struct list_head list; + + struct device *dev; + void __iomem *regs; + int irq; + + enum pci_bus_speed speed; + enum pcie_link_width width; +}; + +#define WED_RRO_ADDR_SIGNATURE_MASK GENMASK(31, 24) +#define WED_RRO_ADDR_COUNT_MASK GENMASK(14, 4) +#define WED_RRO_ADDR_HEAD_HIGH_MASK GENMASK(3, 0) +struct mt7996_wed_rro_addr { + __le32 head_low; + __le32 data; +}; + +struct mt7996_wed_rro_session_id { + struct list_head list; + u16 id; +}; + +struct mt7996_msdu_page { + struct list_head list; + + struct mt76_queue *q; + dma_addr_t dma_addr; + void *buf; +}; + +/* data1 */ +#define RRO_HIF_DATA1_LS_MASK BIT(30) +#define RRO_HIF_DATA1_SDL_MASK GENMASK(29, 16) +/* data4 */ +#define RRO_HIF_DATA4_RX_TOKEN_ID_MASK GENMASK(15, 0) +struct mt7996_rro_hif { + __le32 data0; + __le32 data1; + __le32 data2; + __le32 data3; + __le32 data4; + __le32 data5; +}; + +#define MSDU_PAGE_INFO_OWNER_MASK BIT(31) +#define MSDU_PAGE_INFO_PG_HIGH_MASK GENMASK(3, 0) +struct mt7996_msdu_page_info { + struct mt7996_rro_hif rxd[MT7996_MAX_HIF_RXD_IN_PG]; + __le32 pg_low; + __le32 data; +}; + +#define MT7996_MAX_RRO_RRS_RING 4 +struct mt7996_rro_queue_regs_emi { + struct { + __le16 idx; + __le16 rsv; + } ring[MT7996_MAX_RRO_RRS_RING]; +}; + +struct mt7996_phy { + struct mt76_phy *mt76; + struct mt7996_dev *dev; + + struct ieee80211_sband_iftype_data iftype[NUM_NL80211_BANDS][NUM_NL80211_IFTYPES]; + + struct thermal_cooling_device *cdev; + u8 cdev_state; + u8 throttle_state; + u32 throttle_temp[2]; /* 0: critical high, 1: maximum */ + + u32 rxfilter; + u64 omac_mask; + + u16 noise; + + s16 coverage_class; + u8 slottime; + + u16 beacon_rate; + + u32 rx_ampdu_ts; + u32 ampdu_ref; + int txpower; + + struct mt76_mib_stats mib; + struct mt76_channel_state state_ts; + + u16 orig_chainmask; + u16 orig_antenna_mask; + + bool has_aux_rx; + bool counter_reset; + bool rdd_tx_paused; +}; + +struct mt7996_dev { + union { /* must be first */ + struct mt76_dev mt76; + struct mt76_phy mphy; + }; + + struct mt7996_phy *radio_phy[MT7996_MAX_RADIOS]; + struct wiphy_radio radios[MT7996_MAX_RADIOS]; + struct wiphy_radio_freq_range radio_freqs[MT7996_MAX_RADIOS]; + struct mac_address radio_addrs[MT7996_MAX_RADIOS]; + + struct mt7996_hif *hif2; + struct mt7996_reg_desc reg; + u8 q_id[MT7996_MAX_QUEUE]; + u32 q_int_mask[MT7996_MAX_QUEUE]; + u32 q_wfdma_mask; + + u64 mld_idx_mask; + u64 mld_remap_idx_mask; + + const struct mt76_bus_ops *bus_ops; + struct mt7996_phy phy; + + /* monitor rx chain configured channel */ + struct cfg80211_chan_def rdd2_chandef; + struct mt7996_phy *rdd2_phy; + + u16 chainmask; + u8 chainshift[__MT_MAX_BAND]; + u32 hif_idx; + + struct work_struct init_work; + struct work_struct rc_work; + struct work_struct dump_work; + struct work_struct reset_work; + wait_queue_head_t reset_wait; + struct { + u32 state; + u32 wa_reset_count; + u32 wm_reset_count; + bool hw_full_reset:1; + bool hw_init_done:1; + bool restart:1; + } recovery; + + /* protects coredump data */ + struct mutex dump_mutex; +#ifdef CONFIG_DEV_COREDUMP + struct { + struct mt7996_crash_data *crash_data; + } coredump; +#endif + + struct list_head sta_rc_list; + struct list_head twt_list; + + u32 hw_pattern; + + u8 eeprom_mode; + bool has_eht:1; + + struct { + struct { + void *ptr; + dma_addr_t phy_addr; + } ba_bitmap[MT7996_RRO_BA_BITMAP_LEN]; + struct { + void *ptr; + dma_addr_t phy_addr; + } addr_elem[MT7996_RRO_ADDR_ELEM_LEN]; + struct { + void *ptr; + dma_addr_t phy_addr; + } session; + struct { + void *ptr; + dma_addr_t phy_addr; + } msdu_pg[MT7996_RRO_MSDU_PG_CR_CNT]; + struct { + struct mt7996_rro_queue_regs_emi *ptr; + dma_addr_t phy_addr; + } emi_rings_cpu; + struct { + struct mt7996_rro_queue_regs_emi *ptr; + dma_addr_t phy_addr; + } emi_rings_dma; + + struct work_struct work; + struct list_head poll_list; + spinlock_t lock; + + struct list_head page_cache; + struct list_head page_map[MT7996_RRO_MSDU_PG_HASH_SIZE]; + } wed_rro; + + dma_addr_t npu_txd_addr[2 * MT7996_NPU_TXD_SIZE]; + + bool ibf; + u8 fw_debug_wm; + u8 fw_debug_wa; + u8 fw_debug_bin; + u16 fw_debug_seq; + + struct dentry *debugfs_dir; + struct rchan *relay_fwlog; + + struct { + u16 table_mask; + u8 n_agrt; + } twt; + + spinlock_t reg_lock; + + u8 wtbl_size_group; + struct { + u8 type:4; + u8 fem:4; + } var; +}; + +enum { + WFDMA0 = 0x0, + WFDMA1, + WFDMA_EXT, + __MT_WFDMA_MAX, +}; + +enum rdd_idx { + MT_RDD_IDX_BAND2, /* RDD idx for band idx 2 */ + MT_RDD_IDX_BAND1, /* RDD idx for band idx 1 */ + MT_RDD_IDX_BACKGROUND, /* RDD idx for background chain */ +}; + +enum mt7996_rdd_cmd { + RDD_STOP, + RDD_START, + RDD_DET_MODE, + RDD_RADAR_EMULATE, + RDD_START_TXQ = 20, + RDD_CAC_START = 50, + RDD_CAC_END, + RDD_NORMAL_START, + RDD_DISABLE_DFS_CAL, + RDD_PULSE_DBG, + RDD_READ_PULSE, + RDD_RESUME_BF, + RDD_IRQ_OFF, +}; + +static inline int +mt7996_get_rdd_idx(struct mt7996_phy *phy, bool is_background) +{ + if (!phy->mt76->cap.has_5ghz) + return -1; + + if (is_background) + return MT_RDD_IDX_BACKGROUND; + + if (phy->mt76->band_idx == MT_BAND2) + return MT_RDD_IDX_BAND2; + + return MT_RDD_IDX_BAND1; +} + +static inline struct mt7996_dev * +mt7996_hw_dev(struct ieee80211_hw *hw) +{ + struct mt76_phy *phy = hw->priv; + + return container_of(phy->dev, struct mt7996_dev, mt76); +} + +static inline struct mt7996_phy * +__mt7996_phy(struct mt7996_dev *dev, enum mt76_band_id band) +{ + struct mt76_phy *phy = dev->mt76.phys[band]; + + if (!phy) + return NULL; + + return phy->priv; +} + +static inline struct mt7996_phy * +mt7996_phy2(struct mt7996_dev *dev) +{ + return __mt7996_phy(dev, MT_BAND1); +} + +static inline struct mt7996_phy * +mt7996_phy3(struct mt7996_dev *dev) +{ + return __mt7996_phy(dev, MT_BAND2); +} + +static inline bool +mt7996_band_valid(struct mt7996_dev *dev, u8 band) +{ + if (!is_mt7996(&dev->mt76)) + return band <= MT_BAND1; + + return band <= MT_BAND2; +} + +static inline struct mt7996_phy * +mt7996_band_phy(struct mt7996_dev *dev, enum nl80211_band band) +{ + struct mt76_phy *mphy; + + mphy = dev->mt76.band_phys[band]; + if (!mphy) + return NULL; + + return mphy->priv; +} + +static inline struct mt7996_vif_link * +mt7996_vif_link(struct mt7996_dev *dev, struct ieee80211_vif *vif, int link_id) +{ + return (struct mt7996_vif_link *)mt76_vif_link(&dev->mt76, vif, link_id); +} + +static inline struct mt7996_phy * +mt7996_vif_link_phy(struct mt7996_vif_link *link) +{ + struct mt76_phy *mphy = mt76_vif_link_phy(&link->mt76); + + if (!mphy) + return NULL; + + return mphy->priv; +} + +static inline struct mt7996_vif_link * +mt7996_vif_conf_link(struct mt7996_dev *dev, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf) +{ + return (struct mt7996_vif_link *)mt76_vif_conf_link(&dev->mt76, vif, + link_conf); +} + +static inline struct mt7996_sta_link * +mt7996_sta_link(struct mt7996_sta *msta, u8 link_id) +{ + if (link_id >= IEEE80211_MLD_MAX_NUM_LINKS) + return NULL; + + return rcu_dereference(msta->link[link_id]); +} + +static inline struct mt7996_sta_link * +mt7996_sta_link_protected(struct mt7996_dev *dev, struct mt7996_sta *msta, + u8 link_id) +{ + if (link_id >= IEEE80211_MLD_MAX_NUM_LINKS) + return NULL; + + return mt76_dereference(msta->link[link_id], &dev->mt76); +} + +#define mt7996_for_each_phy(dev, phy) \ + for (int __i = 0; __i < ARRAY_SIZE((dev)->radio_phy); __i++) \ + if (((phy) = (dev)->radio_phy[__i]) != NULL) + +extern const struct ieee80211_ops mt7996_ops; +extern struct pci_driver mt7996_pci_driver; +extern struct pci_driver mt7996_hif_driver; + +struct mt7996_dev *mt7996_mmio_probe(struct device *pdev, + void __iomem *mem_base, u32 device_id); +void mt7996_rro_hw_init(struct mt7996_dev *dev); +void mt7996_wfsys_reset(struct mt7996_dev *dev); +irqreturn_t mt7996_irq_handler(int irq, void *dev_instance); +u64 __mt7996_get_tsf(struct ieee80211_hw *hw, struct mt7996_vif_link *link); +int mt7996_register_device(struct mt7996_dev *dev); +void mt7996_unregister_device(struct mt7996_dev *dev); +int mt7996_vif_link_add(struct mt76_phy *mphy, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, + struct mt76_vif_link *mlink); +void mt7996_vif_link_remove(struct mt76_phy *mphy, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, + struct mt76_vif_link *mlink); +int mt7996_eeprom_init(struct mt7996_dev *dev); +int mt7996_eeprom_parse_hw_cap(struct mt7996_dev *dev, struct mt7996_phy *phy); +int mt7996_eeprom_get_target_power(struct mt7996_dev *dev, + struct ieee80211_channel *chan); +s8 mt7996_eeprom_get_power_delta(struct mt7996_dev *dev, int band); +bool mt7996_eeprom_has_background_radar(struct mt7996_dev *dev); +int mt7996_dma_init(struct mt7996_dev *dev); +void mt7996_dma_reset(struct mt7996_dev *dev, bool force); +void mt7996_dma_prefetch(struct mt7996_dev *dev); +void mt7996_dma_cleanup(struct mt7996_dev *dev); +void mt7996_dma_start(struct mt7996_dev *dev, bool reset, bool wed_reset); +int mt7996_init_tx_queues(struct mt7996_phy *phy, int idx, + int n_desc, int ring_base, struct mtk_wed_device *wed); +void mt7996_init_txpower(struct mt7996_phy *phy); +int mt7996_txbf_init(struct mt7996_dev *dev); +void mt7996_reset(struct mt7996_dev *dev); +int mt7996_run(struct mt7996_phy *phy); +int mt7996_mcu_init(struct mt7996_dev *dev); +int mt7996_mcu_init_firmware(struct mt7996_dev *dev); +int mt7996_mcu_twt_agrt_update(struct mt7996_dev *dev, + struct mt7996_vif_link *link, + struct mt7996_twt_flow *flow, + int cmd); +int mt7996_mcu_add_dev_info(struct mt7996_phy *phy, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, + struct mt76_vif_link *mlink, bool enable); +int mt7996_mcu_add_bss_info(struct mt7996_phy *phy, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, + struct mt76_vif_link *mlink, + struct mt7996_sta_link *msta_link, int enable); +int mt7996_mcu_update_bss_rfch(struct mt7996_phy *phy, + struct mt7996_vif_link *link); +int mt7996_mcu_add_sta(struct mt7996_dev *dev, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_link_sta *link_sta, + struct mt7996_vif_link *link, + struct mt7996_sta_link *msta_link, + int conn_state, bool newly); +int mt7996_mcu_teardown_mld_sta(struct mt7996_dev *dev, + struct mt7996_vif_link *link, + struct mt7996_sta_link *msta_link); +void mt7996_mcu_update_sta_rec_bw(void *data, struct ieee80211_sta *sta); +int mt7996_mcu_add_tx_ba(struct mt7996_dev *dev, + struct ieee80211_ampdu_params *params, + struct ieee80211_vif *vif, bool enable); +int mt7996_mcu_add_rx_ba(struct mt7996_dev *dev, + struct ieee80211_ampdu_params *params, + struct ieee80211_vif *vif, bool enable); +int mt7996_mcu_update_bss_color(struct mt7996_dev *dev, + struct mt76_vif_link *mlink, + struct cfg80211_he_bss_color *he_bss_color); +int mt7996_mcu_add_beacon(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, bool enabled); +int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev, + struct ieee80211_bss_conf *link_conf, + struct mt7996_vif_link *link, u32 changed); +int mt7996_mcu_add_obss_spr(struct mt7996_phy *phy, + struct mt7996_vif_link *link, + struct ieee80211_he_obss_pd *he_obss_pd); +int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev, struct mt7996_sta *msta, + struct ieee80211_vif *vif, u8 link_id, + bool changed); +int mt7996_set_channel(struct mt76_phy *mphy); +int mt7996_mcu_set_chan_info(struct mt7996_phy *phy, u16 tag); +int mt7996_mcu_set_tx(struct mt7996_dev *dev, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf); +int mt7996_mcu_set_fixed_rate_ctrl(struct mt7996_dev *dev, + void *data, u16 version); +int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev, struct mt7996_sta *msta, + void *data, u8 link_id, u32 field); +int mt7996_mcu_set_eeprom(struct mt7996_dev *dev); +int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset, u8 *buf, u32 buf_len, + enum mt7996_eeprom_mode mode); +int mt7996_mcu_get_efuse_free_block(struct mt7996_dev *dev, u8 *block_num); +int mt7996_mcu_get_chip_config(struct mt7996_dev *dev, u32 *cap); +int mt7996_mcu_set_ser(struct mt7996_dev *dev, u8 action, u8 set, u8 band); +int mt7996_mcu_set_txbf(struct mt7996_dev *dev, u8 action); +int mt7996_mcu_set_fcc5_lpn(struct mt7996_dev *dev, int val); +int mt7996_mcu_set_pulse_th(struct mt7996_dev *dev, + const struct mt7996_dfs_pulse *pulse); +int mt7996_mcu_set_radar_th(struct mt7996_dev *dev, int index, + const struct mt7996_dfs_pattern *pattern); +int mt7996_mcu_set_radio_en(struct mt7996_phy *phy, bool enable); +int mt7996_mcu_set_rts_thresh(struct mt7996_phy *phy, u32 val); +int mt7996_mcu_set_protection(struct mt7996_phy *phy, struct mt7996_vif_link *link, + u8 ht_mode, bool use_cts_prot); +int mt7996_mcu_set_timing(struct mt7996_phy *phy, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf); +int mt7996_mcu_set_bssid_mapping_addr(struct mt76_dev *dev, u8 band_idx); +int mt7996_mcu_get_chan_mib_info(struct mt7996_phy *phy, bool chan_switch); +int mt7996_mcu_get_temperature(struct mt7996_phy *phy); +int mt7996_mcu_set_thermal_throttling(struct mt7996_phy *phy, u8 state); +int mt7996_mcu_set_thermal_protect(struct mt7996_phy *phy, bool enable); +int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy); +int mt7996_mcu_rdd_resume_tx(struct mt7996_phy *phy); +int mt7996_mcu_rdd_cmd(struct mt7996_dev *dev, int cmd, u8 rdd_idx, u8 val); +int mt7996_mcu_rdd_background_enable(struct mt7996_phy *phy, + struct cfg80211_chan_def *chandef); +int mt7996_mcu_set_fixed_rate_table(struct mt7996_phy *phy, u8 table_idx, + u16 rate_idx, bool beacon); +int mt7996_mcu_rf_regval(struct mt7996_dev *dev, u32 regidx, u32 *val, bool set); +int mt7996_mcu_set_hdr_trans(struct mt7996_dev *dev, bool hdr_trans); +int mt7996_mcu_set_rro(struct mt7996_dev *dev, u16 tag, u16 val); +int mt7996_mcu_wa_cmd(struct mt7996_dev *dev, int cmd, u32 a1, u32 a2, u32 a3); +int mt7996_mcu_fw_log_2_host(struct mt7996_dev *dev, u8 type, u8 ctrl); +int mt7996_mcu_fw_dbg_ctrl(struct mt7996_dev *dev, u32 module, u8 level); +int mt7996_mcu_trigger_assert(struct mt7996_dev *dev); +void mt7996_mcu_rx_event(struct mt7996_dev *dev, struct sk_buff *skb); +void mt7996_mcu_exit(struct mt7996_dev *dev); +int mt7996_mcu_get_all_sta_info(struct mt7996_phy *phy, u16 tag); +int mt7996_mcu_wed_rro_reset_sessions(struct mt7996_dev *dev, u16 id); +int mt7996_mcu_set_sniffer_mode(struct mt7996_phy *phy, bool enabled); +int mt7996_mcu_set_dup_wtbl(struct mt7996_dev *dev); +int mt7996_mcu_mld_reconf_stop_link(struct mt7996_dev *dev, + struct ieee80211_vif *vif, + u16 removed_links); +int mt7996_mcu_mld_link_oper(struct mt7996_dev *dev, + struct ieee80211_bss_conf *link_conf, + struct mt7996_vif_link *link, bool add); + +static inline bool mt7996_has_hwrro(struct mt7996_dev *dev) +{ + return dev->mt76.hwrro_mode != MT76_HWRRO_OFF; +} + +static inline u8 mt7996_max_interface_num(struct mt7996_dev *dev) +{ + return min(MT7996_MAX_INTERFACES * (1 + mt7996_band_valid(dev, MT_BAND1) + + mt7996_band_valid(dev, MT_BAND2)), + MT7996_WTBL_BMC_SIZE); +} + +static inline u16 mt7996_wtbl_size(struct mt7996_dev *dev) +{ + return (dev->wtbl_size_group << 8) + MT7996_WTBL_BMC_SIZE; +} + +void mt7996_dual_hif_set_irq_mask(struct mt7996_dev *dev, bool write_reg, + u32 clear, u32 set); + +static inline void mt7996_irq_enable(struct mt7996_dev *dev, u32 mask) +{ + if (dev->hif2) + mt7996_dual_hif_set_irq_mask(dev, false, 0, mask); + else + mt76_set_irq_mask(&dev->mt76, 0, 0, mask); + + tasklet_schedule(&dev->mt76.irq_tasklet); +} + +static inline void mt7996_irq_disable(struct mt7996_dev *dev, u32 mask) +{ + if (dev->hif2) + mt7996_dual_hif_set_irq_mask(dev, true, mask, 0); + else + mt76_set_irq_mask(&dev->mt76, MT_INT_MASK_CSR, mask, 0); +} + +void mt7996_memcpy_fromio(struct mt7996_dev *dev, void *buf, u32 offset, + size_t len); + +static inline u16 mt7996_rx_chainmask(struct mt7996_phy *phy) +{ + int max_nss = hweight16(phy->orig_antenna_mask); + int cur_nss = hweight8(phy->mt76->antenna_mask); + u16 tx_chainmask = phy->mt76->chainmask; + + if (cur_nss != max_nss) + return tx_chainmask; + + return tx_chainmask | (BIT(fls(tx_chainmask)) * phy->has_aux_rx); +} + +static inline bool mt7996_has_wa(struct mt7996_dev *dev) +{ + return !is_mt7990(&dev->mt76); +} + +static inline bool mt7996_has_ext_eeprom(struct mt7996_dev *dev) +{ + return !is_mt7996(&dev->mt76); +} + +void mt7996_mac_init(struct mt7996_dev *dev); +u32 mt7996_mac_wtbl_lmac_addr(struct mt7996_dev *dev, u16 wcid, u8 dw); +bool mt7996_mac_wtbl_update(struct mt7996_dev *dev, int idx, u32 mask); +void mt7996_mac_reset_counters(struct mt7996_phy *phy); +void mt7996_mac_cca_stats_reset(struct mt7996_phy *phy); +void mt7996_mac_enable_nf(struct mt7996_dev *dev, u8 band); +struct mt76_wcid *mt7996_get_tx_wcid(struct mt76_wcid *wcid); +void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi, + struct sk_buff *skb, struct mt76_wcid *wcid, + struct ieee80211_key_conf *key, int pid, + enum mt76_txq_id qid, u32 changed, + unsigned int link_id); +void mt7996_mac_update_beacons(struct mt7996_phy *phy); +void mt7996_mac_set_coverage_class(struct mt7996_phy *phy); +void mt7996_mac_work(struct work_struct *work); +void mt7996_mac_reset_work(struct work_struct *work); +void mt7996_mac_dump_work(struct work_struct *work); +void mt7996_mac_sta_rc_work(struct work_struct *work); +void mt7996_mac_update_stats(struct mt7996_phy *phy); +void mt7996_mac_twt_teardown_flow(struct mt7996_dev *dev, + struct mt7996_vif_link *link, + struct mt7996_sta_link *msta_link, + u8 flowid); +void mt7996_mac_sta_remove_link(struct mt7996_dev *dev, + struct ieee80211_sta *sta, + unsigned int link_id, bool flush); +void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw, + struct ieee80211_sta *sta, + struct ieee80211_twt_setup *twt); +int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, + enum mt76_txq_id qid, struct mt76_wcid *wcid, + struct ieee80211_sta *sta, + struct mt76_tx_info *tx_info); +void mt7996_tx_token_put(struct mt7996_dev *dev); +void mt7996_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, + struct sk_buff *skb, u32 *info); +void mt7996_rro_msdu_page_map_free(struct mt7996_dev *dev); +int mt7996_rro_msdu_page_add(struct mt76_dev *mdev, struct mt76_queue *q, + dma_addr_t dma_addr, void *data); +void mt7996_rro_rx_process(struct mt76_dev *mdev, void *data); +bool mt7996_rx_check(struct mt76_dev *mdev, void *data, int len); +void mt7996_stats_work(struct work_struct *work); +int mt76_dfs_start_rdd(struct mt7996_dev *dev, bool force); +int mt7996_dfs_init_radar_detector(struct mt7996_phy *phy); +void mt7996_set_stream_he_eht_caps(struct mt7996_phy *phy); +void mt7996_set_stream_vht_txbf_caps(struct mt7996_phy *phy); +void mt7996_update_channel(struct mt76_phy *mphy); +int mt7996_init_debugfs(struct mt7996_dev *dev); +void mt7996_debugfs_rx_fw_monitor(struct mt7996_dev *dev, const void *data, int len); +bool mt7996_debugfs_rx_log(struct mt7996_dev *dev, const void *data, int len); +int mt7996_mcu_add_key(struct mt76_dev *dev, struct mt7996_vif_link *link, + struct ieee80211_key_conf *key, int mcu_cmd, + struct mt76_wcid *wcid, enum set_key_cmd cmd); +int mt7996_mcu_bcn_prot_enable(struct mt7996_dev *dev, + struct mt7996_vif_link *link, + struct mt7996_sta_link *msta_link, + struct ieee80211_key_conf *key); +int mt7996_mcu_wtbl_update_hdr_trans(struct mt7996_dev *dev, + struct ieee80211_vif *vif, + struct mt7996_vif_link *link, + struct mt7996_sta_link *msta_link); +int mt7996_mcu_ps_leave(struct mt7996_dev *dev, struct mt7996_vif_link *link, + struct mt7996_sta_link *msta_link); +int mt7996_mcu_cp_support(struct mt7996_dev *dev, u8 mode); +int mt7996_mcu_set_emlsr_mode(struct mt7996_dev *dev, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_eml_params *eml_params); +#ifdef CONFIG_MAC80211_DEBUGFS +void mt7996_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, struct dentry *dir); +void mt7996_link_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_link_sta *link_sta, + struct dentry *dir); +#endif +int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr, + bool hif2, int *irq); +u32 mt7996_wed_init_buf(void *ptr, dma_addr_t phys, int token_id); + +int mt7996_dma_rro_init(struct mt7996_dev *dev); +void mt7996_dma_rro_start(struct mt7996_dev *dev); + +#ifdef CONFIG_MT7996_NPU +int __mt7996_npu_hw_init(struct mt7996_dev *dev); +int mt7996_npu_hw_init(struct mt7996_dev *dev); +int mt7996_npu_hw_stop(struct mt7996_dev *dev); +int mt7996_npu_rx_queues_init(struct mt7996_dev *dev); +#else +static inline int __mt7996_npu_hw_init(struct mt7996_dev *dev) +{ + return 0; +} + +static inline int mt7996_npu_hw_init(struct mt7996_dev *dev) +{ + return 0; +} + +static inline int mt7996_npu_hw_stop(struct mt7996_dev *dev) +{ + return 0; +} + +static inline int mt7996_npu_rx_queues_init(struct mt7996_dev *dev) +{ + return 0; +} +#endif /* CONFIG_MT7996_NPU */ + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/npu.c b/drivers/net/wireless/mediatek/mt76/mt7996/npu.c new file mode 100644 index 000000000..b8006b872 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7996/npu.c @@ -0,0 +1,631 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2025 AIROHA Inc + * Author: Lorenzo Bianconi + */ +#include +#include + +#include "mt7996.h" + +static int mt7992_npu_txrx_offload_init(struct mt7996_dev *dev, + struct airoha_npu *npu) +{ + u32 hif1_ofs = dev->hif2 ? MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0) : 0; + phys_addr_t phy_addr = dev->mt76.mmio.phy_addr; + u32 dma_addr; + int i, err; + + for (i = MT_BAND0; i < MT_BAND2; i++) { + err = mt76_npu_send_msg(npu, i, WLAN_FUNC_SET_WAIT_DESC, + MT7996_RX_RING_SIZE, GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, + "failed setting NPU wlan rx desc size\n"); + return err; + } + + dma_addr = phy_addr; + if (i) + dma_addr += MT_TXQ_RING_BASE(0) + 0x150 + hif1_ofs; + else + dma_addr += MT_TXQ_RING_BASE(0) + 0x120; + + err = mt76_npu_send_msg(npu, i, + WLAN_FUNC_SET_WAIT_TX_RING_PCIE_ADDR, + dma_addr, GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, + "failed setting NPU wlan tx desc addr\n"); + return err; + } + } + + err = mt76_npu_send_msg(npu, 9, WLAN_FUNC_SET_WAIT_PCIE_ADDR, + phy_addr + MT_RXQ_RRO_AP_RING_BASE, + GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, + "failed setting NPU wlan rxdmad_c addr\n"); + return err; + } + + err = mt76_npu_send_msg(npu, 9, WLAN_FUNC_SET_WAIT_DESC, + MT7996_RX_RING_SIZE, GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, + "failed setting NPU wlan rxdmad_c desc size\n"); + return err; + } + + err = mt76_npu_send_msg(npu, 2, WLAN_FUNC_SET_WAIT_TX_RING_PCIE_ADDR, + phy_addr + MT_RRO_ACK_SN_CTRL, GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, + "failed setting NPU wlan tx desc addr\n"); + return err; + } + + return 0; +} + +static int mt7996_npu_txrx_offload_init(struct mt7996_dev *dev, + struct airoha_npu *npu) +{ + u32 hif1_ofs = dev->hif2 ? MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0) : 0; + phys_addr_t phy_addr = dev->mt76.mmio.phy_addr; + u32 dma_addr; + int err; + + /* npu rx rro ring0 */ + err = mt76_npu_send_msg(npu, 0, WLAN_FUNC_SET_WAIT_DESC, + MT7996_RX_RING_SIZE, GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, + "failed setting NPU wlan rx desc size\n"); + return err; + } + + /* npu rx rro ring1 */ + err = mt76_npu_send_msg(npu, 2, WLAN_FUNC_SET_WAIT_DESC, + MT7996_NPU_RX_RING_SIZE, GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, + "failed setting NPU wlan rx desc size\n"); + return err; + } + + /* msdu pg 2GHz */ + dma_addr = phy_addr + MT_RXQ_RING_BASE(MT_RXQ_MSDU_PAGE_BAND0) + 0xa0; + err = mt76_npu_send_msg(npu, 5, WLAN_FUNC_SET_WAIT_PCIE_ADDR, + dma_addr, GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, + "failed setting NPU wlan PCIe desc addr\n"); + return err; + } + + err = mt76_npu_send_msg(npu, 5, WLAN_FUNC_SET_WAIT_DESC, + MT7996_NPU_RX_RING_SIZE / 4, GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, + "failed setting NPU wlan rx desc size\n"); + return err; + } + + /* msdu pg 5GHz */ + dma_addr = phy_addr + MT_RXQ_RING_BASE(MT_RXQ_MSDU_PAGE_BAND1) + 0xb0; + err = mt76_npu_send_msg(npu, 6, WLAN_FUNC_SET_WAIT_PCIE_ADDR, + dma_addr, GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, + "failed setting NPU wlan PCIe desc addr\n"); + return err; + } + + err = mt76_npu_send_msg(npu, 6, WLAN_FUNC_SET_WAIT_DESC, + MT7996_NPU_RX_RING_SIZE / 2, GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, + "failed setting NPU wlan rx desc size\n"); + return err; + } + + /* msdu pg 6GHz */ + dma_addr = phy_addr + MT_RXQ_RING_BASE(MT_RXQ_MSDU_PAGE_BAND2) + 0xc0; + err = mt76_npu_send_msg(npu, 7, WLAN_FUNC_SET_WAIT_PCIE_ADDR, + dma_addr, GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, + "failed setting NPU wlan PCIe desc addr\n"); + return err; + } + + err = mt76_npu_send_msg(npu, 7, WLAN_FUNC_SET_WAIT_DESC, + MT7996_NPU_RX_RING_SIZE, GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, + "failed setting NPU wlan rx desc size\n"); + return err; + } + + /* ind cmd ring */ + err = mt76_npu_send_msg(npu, 8, WLAN_FUNC_SET_WAIT_PCIE_ADDR, + phy_addr + MT_RXQ_RRO_IND_RING_BASE, + GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, + "failed setting NPU wlan PCIe desc addr\n"); + return err; + } + + err = mt76_npu_send_msg(npu, 8, WLAN_FUNC_SET_WAIT_DESC, + MT7996_RX_RING_SIZE, GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, + "failed setting NPU wlan rx desc size\n"); + return err; + } + + err = mt76_npu_send_msg(npu, 3, WLAN_FUNC_SET_WAIT_TX_RING_PCIE_ADDR, + phy_addr + MT_RRO_ACK_SN_CTRL, GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, + "failed setting NPU wlan tx desc addr\n"); + return err; + } + + /* npu tx */ + dma_addr = phy_addr + MT_TXQ_RING_BASE(1) + 0x120; + err = mt76_npu_send_msg(npu, 0, WLAN_FUNC_SET_WAIT_TX_RING_PCIE_ADDR, + dma_addr, GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, + "failed setting NPU wlan tx desc addr\n"); + return err; + } + + dma_addr = phy_addr + MT_TXQ_RING_BASE(0) + 0x150 + hif1_ofs; + err = mt76_npu_send_msg(npu, 2, WLAN_FUNC_SET_WAIT_TX_RING_PCIE_ADDR, + dma_addr, GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, + "failed setting NPU wlan tx desc addr\n"); + return err; + } + + return 0; +} + +static int mt7996_npu_offload_init(struct mt7996_dev *dev, + struct airoha_npu *npu) +{ + u32 val; + int err; + + err = mt76_npu_get_msg(npu, 0, WLAN_FUNC_GET_WAIT_NPU_VERSION, + &val, GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, "failed getting NPU fw version\n"); + return err; + } + + dev_info(dev->mt76.dev, "NPU version: %0d.%d\n", + (val >> 16) & 0xffff, val & 0xffff); + + err = mt76_npu_send_msg(npu, 0, WLAN_FUNC_SET_WAIT_PCIE_PORT_TYPE, + dev->mt76.mmio.npu_type, GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, + "failed setting NPU wlan PCIe port type\n"); + return err; + } + + if (is_mt7996(&dev->mt76)) + err = mt7996_npu_txrx_offload_init(dev, npu); + else + err = mt7992_npu_txrx_offload_init(dev, npu); + + if (err) + return err; + + err = mt76_npu_send_msg(npu, 0, WLAN_FUNC_SET_WAIT_TOKEN_ID_SIZE, + MT7996_HW_TOKEN_SIZE, GFP_KERNEL); + if (err) + return err; + + dev->mt76.token_start = MT7996_HW_TOKEN_SIZE; + + return 0; +} + +static int mt7992_npu_rxd_init(struct mt7996_dev *dev, struct airoha_npu *npu) +{ + u32 val; + int err; + + err = mt76_npu_get_msg(npu, 0, WLAN_FUNC_GET_WAIT_RXDESC_BASE, + &val, GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, + "failed retrieving NPU wlan rx ring0 addr\n"); + return err; + } + writel(val, &dev->mt76.q_rx[MT_RXQ_RRO_BAND0].regs->desc_base); + + err = mt76_npu_get_msg(npu, 1, WLAN_FUNC_GET_WAIT_RXDESC_BASE, + &val, GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, + "failed retrieving NPU wlan rx ring1 addr\n"); + return err; + } + writel(val, &dev->mt76.q_rx[MT_RXQ_RRO_BAND1].regs->desc_base); + + err = mt76_npu_get_msg(npu, 9, WLAN_FUNC_GET_WAIT_RXDESC_BASE, + &val, GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, + "failed retrieving NPU wlan rxdmad_c ring addr\n"); + return err; + } + writel(val, &dev->mt76.q_rx[MT_RXQ_RRO_RXDMAD_C].regs->desc_base); + + return 0; +} + +static int mt7996_npu_rxd_init(struct mt7996_dev *dev, struct airoha_npu *npu) +{ + u32 val; + int err; + + err = mt76_npu_get_msg(npu, 0, WLAN_FUNC_GET_WAIT_RXDESC_BASE, + &val, GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, + "failed retriving NPU wlan rx ring0 addr\n"); + return err; + } + writel(val, &dev->mt76.q_rx[MT_RXQ_RRO_BAND0].regs->desc_base); + + err = mt76_npu_get_msg(npu, 2, WLAN_FUNC_GET_WAIT_RXDESC_BASE, + &val, GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, + "failed retriving NPU wlan rx ring2 addr\n"); + return err; + } + writel(val, &dev->mt76.q_rx[MT_RXQ_RRO_BAND2].regs->desc_base); + + /* msdu pg ring */ + err = mt76_npu_get_msg(npu, 10, WLAN_FUNC_GET_WAIT_RXDESC_BASE, + &val, GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, + "failed retriving NPU wlan msdu pg ring addr\n"); + return err; + } + writel(val, &dev->mt76.q_rx[MT_RXQ_MSDU_PAGE_BAND0].regs->desc_base); + + err = mt76_npu_get_msg(npu, 11, WLAN_FUNC_GET_WAIT_RXDESC_BASE, + &val, GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, + "failed retriving NPU wlan msdu pg ring addr\n"); + return err; + } + writel(val, &dev->mt76.q_rx[MT_RXQ_MSDU_PAGE_BAND1].regs->desc_base); + + err = mt76_npu_get_msg(npu, 12, WLAN_FUNC_GET_WAIT_RXDESC_BASE, + &val, GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, + "failed retriving NPU wlan msdu pg ring addr\n"); + return err; + } + writel(val, &dev->mt76.q_rx[MT_RXQ_MSDU_PAGE_BAND2].regs->desc_base); + + /* ind_cmd ring */ + err = mt76_npu_get_msg(npu, 8, WLAN_FUNC_GET_WAIT_RXDESC_BASE, + &val, GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, + "failed retriving NPU wlan ind_cmd ring addr\n"); + return err; + } + writel(val, &dev->mt76.q_rx[MT_RXQ_RRO_IND].regs->desc_base); + + return 0; +} + +static int mt7996_npu_txd_init(struct mt7996_dev *dev, struct airoha_npu *npu) +{ + const enum mt76_band_id band_list[] = { + MT_BAND0, + is_mt7996(&dev->mt76) ? MT_BAND2 : MT_BAND1, + }; + int i, index = 0; + + BUILD_BUG_ON(ARRAY_SIZE(band_list) * 3 != + ARRAY_SIZE(dev->npu_txd_addr)); + + for (i = 0; i < ARRAY_SIZE(band_list); i++) { + int err, band = band_list[i], phy_id; + u32 val; + + err = mt76_npu_get_msg(npu, band + 5, + WLAN_FUNC_GET_WAIT_RXDESC_BASE, + &val, GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, + "failed retrieving NPU wlan tx ring addr\n"); + return err; + } + + phy_id = is_mt7996(&dev->mt76) ? band == MT_BAND0 ? 1 : 0 + : band; + writel(val, &dev->mt76.phys[phy_id]->q_tx[0]->regs->desc_base); + + err = mt76_npu_send_msg(npu, band, + WLAN_FUNC_SET_WAIT_TX_BUF_SPACE_HW_BASE, + dev->npu_txd_addr[index++], GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, + "failed setting NPU wlan queue buf addr\n"); + return err; + } + + err = mt76_npu_send_msg(npu, band + 5, + WLAN_FUNC_SET_WAIT_TX_BUF_SPACE_HW_BASE, + dev->npu_txd_addr[index++], + GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, + "failed setting NPU wlan tx buf addr\n"); + return err; + } + + err = mt76_npu_send_msg(npu, band + 10, + WLAN_FUNC_SET_WAIT_TX_BUF_SPACE_HW_BASE, + dev->npu_txd_addr[index++], + GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, + "failed setting NPU wlan tx buf base\n"); + return err; + } + } + + return 0; +} + +static int mt7996_npu_rx_event_init(struct mt7996_dev *dev, + struct airoha_npu *npu) +{ + int qid = is_mt7996(&dev->mt76) ? MT_RXQ_TXFREE_BAND0 : MT_RXQ_MAIN_WA; + phys_addr_t phy_addr = dev->mt76.mmio.phy_addr; + struct mt76_queue *q = &dev->mt76.q_rx[qid]; + int err; + + err = mt76_npu_send_msg(npu, 0, + WLAN_FUNC_SET_WAIT_RX_RING_FOR_TXDONE_HW_BASE, + q->desc_dma, GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, + "failed setting NPU wlan tx-done ring\n"); + return err; + } + + err = mt76_npu_send_msg(npu, 10, WLAN_FUNC_SET_WAIT_DESC, + MT7996_RX_MCU_RING_SIZE, GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, + "failed setting NPU wlan descriptors\n"); + return err; + } + + phy_addr += MT_RXQ_RING_BASE(qid); + phy_addr += is_mt7996(&dev->mt76) ? 0x90 : 0x20; + err = mt76_npu_send_msg(npu, 10, WLAN_FUNC_SET_WAIT_PCIE_ADDR, + phy_addr, GFP_KERNEL); + if (err) + dev_warn(dev->mt76.dev, + "failed setting NPU wlan rx pcie address\n"); + return err; +} + +static int mt7996_npu_set_pcie_addr(struct mt7996_dev *dev, + struct airoha_npu *npu) +{ + u32 hif1_ofs = dev->hif2 ? MT_WFDMA0_PCIE1(0) - MT_WFDMA0(0) : 0; + dma_addr_t dma_addr = dev->mt76.mmio.phy_addr; + int err; + + dma_addr += MT_RXQ_RING_BASE(MT_RXQ_RRO_BAND0) + 0x80; + err = mt76_npu_send_msg(npu, 0, WLAN_FUNC_SET_WAIT_PCIE_ADDR, + dma_addr, GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, + "failed setting NPU wlan PCIe desc addr\n"); + return err; + } + + dma_addr = dev->mt76.mmio.phy_addr + hif1_ofs; + if (is_mt7996(&dev->mt76)) { + dma_addr += MT_RXQ_RING_BASE(MT_RXQ_RRO_BAND2) + 0x60; + err = mt76_npu_send_msg(npu, 2, WLAN_FUNC_SET_WAIT_PCIE_ADDR, + dma_addr, GFP_KERNEL); + } else { + dma_addr += MT_RXQ_RING_BASE(MT_RXQ_RRO_BAND1) + 0x90; + err = mt76_npu_send_msg(npu, 1, WLAN_FUNC_SET_WAIT_PCIE_ADDR, + dma_addr, GFP_KERNEL); + } + + if (err) + dev_warn(dev->mt76.dev, + "failed setting NPU wlan PCIe desc addr\n"); + + return err; +} + +static int mt7996_npu_tx_done_init(struct mt7996_dev *dev, + struct airoha_npu *npu) +{ + int err; + + /* rro ring cpu idx */ + err = mt76_npu_send_msg(npu, 15, WLAN_FUNC_SET_WAIT_PCIE_ADDR, + 0, GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, + "failed setting NPU wlan PCIe desc addr\n"); + return err; + } + + err = mt76_npu_send_msg(npu, 2, WLAN_FUNC_SET_WAIT_INODE_TXRX_REG_ADDR, + 0, GFP_KERNEL); + if (err) { + dev_warn(dev->mt76.dev, "failed setting NPU wlan txrx addr2\n"); + return err; + } + + err = mt76_npu_send_msg(npu, 7, WLAN_FUNC_SET_WAIT_INODE_TXRX_REG_ADDR, + 0, GFP_KERNEL); + if (err) + dev_warn(dev->mt76.dev, "failed setting NPU wlan txrx addr7\n"); + + return err; +} + +int mt7996_npu_rx_queues_init(struct mt7996_dev *dev) +{ + int err; + + if (!mt76_npu_device_active(&dev->mt76)) + return 0; + + err = mt76_npu_rx_queue_init(&dev->mt76, + &dev->mt76.q_rx[MT_RXQ_NPU0]); + if (err) + return err; + + return mt76_npu_rx_queue_init(&dev->mt76, + &dev->mt76.q_rx[MT_RXQ_NPU1]); +} + +int __mt7996_npu_hw_init(struct mt7996_dev *dev) +{ + struct airoha_npu *npu; + int i, err; + + npu = rcu_dereference_protected(dev->mt76.mmio.npu, &dev->mt76.mutex); + if (!npu) + return 0; + + err = mt7996_npu_offload_init(dev, npu); + if (err) + return err; + + if (is_mt7996(&dev->mt76)) + err = mt7996_npu_rxd_init(dev, npu); + else + err = mt7992_npu_rxd_init(dev, npu); + + if (err) + return err; + + err = mt7996_npu_txd_init(dev, npu); + if (err) + return err; + + err = mt7996_npu_rx_event_init(dev, npu); + if (err) + return err; + + err = mt7996_npu_set_pcie_addr(dev, npu); + if (err) + return err; + + err = mt7996_npu_tx_done_init(dev, npu); + if (err) + return err; + + for (i = MT_RXQ_NPU0; i <= MT_RXQ_NPU1; i++) + airoha_npu_wlan_enable_irq(npu, i - MT_RXQ_NPU0); + + return 0; +} + +int mt7996_npu_hw_init(struct mt7996_dev *dev) +{ + int i, err; + + BUILD_BUG_ON(ARRAY_SIZE(dev->npu_txd_addr) % 3); + + for (i = 0; i < ARRAY_SIZE(dev->npu_txd_addr); i += 3) { + int band = i && is_mt7996(&dev->mt76) ? MT_BAND2 : MT_BAND0; + u32 size = is_mt7996(&dev->mt76) ? band == MT_BAND2 + ? MT7996_NPU_TX_RING_SIZE + : MT7996_NPU_RX_RING_SIZE / 2 + : MT7996_TX_RING_SIZE; + + if (!dmam_alloc_coherent(dev->mt76.dma_dev, 256 * size, + &dev->npu_txd_addr[i], GFP_KERNEL)) + return -ENOMEM; + + if (!dmam_alloc_coherent(dev->mt76.dma_dev, 256 * size, + &dev->npu_txd_addr[i + 1], + GFP_KERNEL)) + return -ENOMEM; + + if (!dmam_alloc_coherent(dev->mt76.dma_dev, 256 * 1024, + &dev->npu_txd_addr[i + 2], + GFP_KERNEL)) + return -ENOMEM; + } + + mutex_lock(&dev->mt76.mutex); + err = __mt7996_npu_hw_init(dev); + mutex_unlock(&dev->mt76.mutex); + + return err; +} + +int mt7996_npu_hw_stop(struct mt7996_dev *dev) +{ + struct airoha_npu *npu; + int i, err = 0; + u32 info; + + mutex_lock(&dev->mt76.mutex); + + npu = rcu_dereference_protected(dev->mt76.mmio.npu, &dev->mt76.mutex); + if (!npu) + goto unlock; + + err = mt76_npu_send_msg(npu, 4, WLAN_FUNC_SET_WAIT_INODE_TXRX_REG_ADDR, + 0, GFP_KERNEL); + if (err) + goto unlock; + + for (i = 0; i < 10; i++) { + err = mt76_npu_get_msg(npu, 3, WLAN_FUNC_GET_WAIT_NPU_INFO, + &info, GFP_KERNEL); + if (!err && !info) + break; + + err = -ETIMEDOUT; + usleep_range(10000, 15000); + } + + if (!err) + err = mt76_npu_send_msg(npu, 6, + WLAN_FUNC_SET_WAIT_INODE_TXRX_REG_ADDR, + 0, GFP_KERNEL); + else + dev_err(dev->mt76.dev, "npu stop failed\n"); +unlock: + mutex_unlock(&dev->mt76.mutex); + + return err; +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/pci.c b/drivers/net/wireless/mediatek/mt76/mt7996/pci.c new file mode 100644 index 000000000..b7d9193e0 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7996/pci.c @@ -0,0 +1,269 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (C) 2022 MediaTek Inc. + */ + +#include +#include +#include + +#include "mt7996.h" +#include "mac.h" +#include "../trace.h" + +static LIST_HEAD(hif_list); +static DEFINE_SPINLOCK(hif_lock); +static u32 hif_idx; + +static const struct pci_device_id mt7996_pci_device_table[] = { + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, MT7996_DEVICE_ID) }, + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, MT7992_DEVICE_ID) }, + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, MT7990_DEVICE_ID) }, + { }, +}; + +static const struct pci_device_id mt7996_hif_device_table[] = { + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, MT7996_DEVICE_ID_2) }, + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, MT7992_DEVICE_ID_2) }, + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, MT7990_DEVICE_ID_2) }, + { }, +}; + +static struct mt7996_hif *mt7996_pci_get_hif2(u32 idx) +{ + struct mt7996_hif *hif; + u32 val; + + spin_lock_bh(&hif_lock); + + list_for_each_entry(hif, &hif_list, list) { + val = readl(hif->regs + MT_PCIE_RECOG_ID); + val &= MT_PCIE_RECOG_ID_MASK; + if (val != idx) + continue; + + get_device(hif->dev); + goto out; + } + hif = NULL; + +out: + spin_unlock_bh(&hif_lock); + + return hif; +} + +static void mt7996_put_hif2(struct mt7996_hif *hif) +{ + if (!hif) + return; + + put_device(hif->dev); +} + +static struct mt7996_hif *mt7996_pci_init_hif2(struct pci_dev *pdev) +{ + hif_idx++; + + if (!pci_get_device(PCI_VENDOR_ID_MEDIATEK, MT7996_DEVICE_ID_2, NULL) && + !pci_get_device(PCI_VENDOR_ID_MEDIATEK, MT7992_DEVICE_ID_2, NULL) && + !pci_get_device(PCI_VENDOR_ID_MEDIATEK, MT7990_DEVICE_ID_2, NULL)) + return NULL; + + writel(hif_idx | MT_PCIE_RECOG_ID_SEM, + pcim_iomap_table(pdev)[0] + MT_PCIE_RECOG_ID); + + return mt7996_pci_get_hif2(hif_idx); +} + +static int mt7996_pci_hif2_probe(struct pci_dev *pdev) +{ + struct mt7996_hif *hif; + + hif = devm_kzalloc(&pdev->dev, sizeof(*hif), GFP_KERNEL); + if (!hif) + return -ENOMEM; + + hif->dev = &pdev->dev; + hif->regs = pcim_iomap_table(pdev)[0]; + hif->irq = pdev->irq; + pcie_bandwidth_available(pdev, NULL, &hif->speed, &hif->width); + spin_lock_bh(&hif_lock); + list_add(&hif->list, &hif_list); + spin_unlock_bh(&hif_lock); + pci_set_drvdata(pdev, hif); + + return 0; +} + +static int mt7996_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct pci_dev *hif2_dev; + struct mt7996_hif *hif2; + struct mt7996_dev *dev; + int irq, hif2_irq, ret; + struct mt76_dev *mdev; + + ret = pcim_enable_device(pdev); + if (ret) + return ret; + + ret = pcim_iomap_regions(pdev, BIT(0), pci_name(pdev)); + if (ret) + return ret; + + pci_set_master(pdev); + + ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(36)); + if (ret) + return ret; + + ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) + return ret; + + mt76_pci_disable_aspm(pdev); + + if (id->device == MT7996_DEVICE_ID_2 || + id->device == MT7992_DEVICE_ID_2 || + id->device == MT7990_DEVICE_ID_2) + return mt7996_pci_hif2_probe(pdev); + + dev = mt7996_mmio_probe(&pdev->dev, pcim_iomap_table(pdev)[0], + id->device); + if (IS_ERR(dev)) + return PTR_ERR(dev); + + mdev = &dev->mt76; + mt7996_wfsys_reset(dev); + hif2 = mt7996_pci_init_hif2(pdev); + dev->hif2 = hif2; + + mt76_npu_init(mdev, pci_resource_start(pdev, 0), + pci_domain_nr(pdev->bus) ? 3 : 2); + + ret = mt7996_mmio_wed_init(dev, pdev, false, &irq); + if (ret < 0) + goto free_wed_or_irq_vector; + + if (!ret) { + ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES); + if (ret < 0) + goto free_device; + + irq = pdev->irq; + } + + ret = devm_request_irq(mdev->dev, irq, mt7996_irq_handler, + IRQF_SHARED, KBUILD_MODNAME, dev); + if (ret) + goto free_wed_or_irq_vector; + + mt76_wr(dev, MT_INT_MASK_CSR, 0); + /* master switch of PCIe interrupt enable */ + mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0xff); + + if (hif2) { + hif2_dev = container_of(hif2->dev, struct pci_dev, dev); + + ret = mt7996_mmio_wed_init(dev, hif2_dev, true, &hif2_irq); + if (ret < 0) + goto free_hif2_wed_irq_vector; + + if (!ret) { + ret = pci_alloc_irq_vectors(hif2_dev, 1, 1, + PCI_IRQ_ALL_TYPES); + if (ret < 0) + goto free_hif2; + + dev->hif2->irq = hif2_dev->irq; + hif2_irq = dev->hif2->irq; + } + + ret = devm_request_irq(mdev->dev, hif2_irq, mt7996_irq_handler, + IRQF_SHARED, KBUILD_MODNAME "-hif", + dev); + if (ret) + goto free_hif2_wed_irq_vector; + + mt76_wr(dev, MT_INT1_MASK_CSR, 0); + /* master switch of PCIe tnterrupt enable */ + mt76_wr(dev, MT_PCIE1_MAC_INT_ENABLE, 0xff); + } + + ret = mt7996_register_device(dev); + if (ret) + goto free_hif2_irq; + + return 0; + +free_hif2_irq: + if (dev->hif2) + devm_free_irq(mdev->dev, hif2_irq, dev); +free_hif2_wed_irq_vector: + if (dev->hif2) { + if (mtk_wed_device_active(&dev->mt76.mmio.wed_hif2)) + mtk_wed_device_detach(&dev->mt76.mmio.wed_hif2); + else + pci_free_irq_vectors(hif2_dev); + } +free_hif2: + if (dev->hif2) + put_device(dev->hif2->dev); + devm_free_irq(mdev->dev, irq, dev); +free_wed_or_irq_vector: + if (mtk_wed_device_active(&dev->mt76.mmio.wed)) + mtk_wed_device_detach(&dev->mt76.mmio.wed); + else + pci_free_irq_vectors(pdev); +free_device: + mt76_free_device(&dev->mt76); + + return ret; +} + +static void mt7996_hif_remove(struct pci_dev *pdev) +{ + struct mt7996_hif *hif = pci_get_drvdata(pdev); + + list_del(&hif->list); +} + +static void mt7996_pci_remove(struct pci_dev *pdev) +{ + struct mt76_dev *mdev; + struct mt7996_dev *dev; + + mdev = pci_get_drvdata(pdev); + dev = container_of(mdev, struct mt7996_dev, mt76); + mt7996_put_hif2(dev->hif2); + mt7996_unregister_device(dev); +} + +struct pci_driver mt7996_hif_driver = { + .name = KBUILD_MODNAME "_hif", + .id_table = mt7996_hif_device_table, + .probe = mt7996_pci_probe, + .remove = mt7996_hif_remove, +}; + +struct pci_driver mt7996_pci_driver = { + .name = KBUILD_MODNAME, + .id_table = mt7996_pci_device_table, + .probe = mt7996_pci_probe, + .remove = mt7996_pci_remove, +}; + +MODULE_DEVICE_TABLE(pci, mt7996_pci_device_table); +MODULE_DEVICE_TABLE(pci, mt7996_hif_device_table); +MODULE_FIRMWARE(MT7996_FIRMWARE_WA); +MODULE_FIRMWARE(MT7996_FIRMWARE_WM); +MODULE_FIRMWARE(MT7996_FIRMWARE_DSP); +MODULE_FIRMWARE(MT7996_ROM_PATCH); +MODULE_FIRMWARE(MT7992_FIRMWARE_WA); +MODULE_FIRMWARE(MT7992_FIRMWARE_WM); +MODULE_FIRMWARE(MT7992_FIRMWARE_DSP); +MODULE_FIRMWARE(MT7992_ROM_PATCH); +MODULE_FIRMWARE(MT7990_FIRMWARE_WM); +MODULE_FIRMWARE(MT7990_ROM_PATCH); diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/regs.h b/drivers/net/wireless/mediatek/mt76/mt7996/regs.h new file mode 100644 index 000000000..8ff78cf6e --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7996/regs.h @@ -0,0 +1,810 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (C) 2022 MediaTek Inc. + */ + +#ifndef __MT7996_REGS_H +#define __MT7996_REGS_H + +struct __map { + u32 phys; + u32 mapped; + u32 size; +}; + +struct __base { + u32 band_base[__MT_MAX_BAND]; +}; + +/* used to differentiate between generations */ +struct mt7996_reg_desc { + const struct __base *base; + const u32 *offs_rev; + const struct __map *map; + u32 map_size; +}; + +enum base_rev { + WF_AGG_BASE, + WF_ARB_BASE, + WF_TMAC_BASE, + WF_RMAC_BASE, + WF_DMA_BASE, + WF_WTBLOFF_BASE, + WF_ETBF_BASE, + WF_LPON_BASE, + WF_MIB_BASE, + WF_RATE_BASE, + __MT_REG_BASE_MAX, +}; + +#define __BASE(_id, _band) (dev->reg.base[(_id)].band_base[(_band)]) + +enum offs_rev { + MIB_RVSR0, + MIB_RVSR1, + MIB_BTSCR5, + MIB_BTSCR6, + MIB_RSCR1, + MIB_RSCR27, + MIB_RSCR28, + MIB_RSCR29, + MIB_RSCR30, + MIB_RSCR31, + MIB_RSCR33, + MIB_RSCR35, + MIB_RSCR36, + MIB_BSCR0, + MIB_BSCR1, + MIB_BSCR2, + MIB_BSCR3, + MIB_BSCR4, + MIB_BSCR5, + MIB_BSCR6, + MIB_BSCR7, + MIB_BSCR17, + MIB_TRDR1, + MIB_TSCR0, + MIB_TSCR1, + MIB_TSCR2, + MIB_TSCR3, + MIB_TSCR4, + MIB_TSCR5, + MIB_TSCR6, + MIB_TSCR7, + HIF_REMAP_L1, + HIF_REMAP_BASE_L1, + HIF_REMAP_L2, + HIF_REMAP_BASE_L2, + CBTOP1_PHY_END, + INFRA_MCU_END, + WTBLON_WDUCR, + WTBL_UPDATE, + WTBL_ITCR, + WTBL_ITCR0, + WTBL_ITCR1, + __MT_OFFS_MAX, +}; + +#define __OFFS(id) (dev->reg.offs_rev[(id)]) + +/* RRO TOP */ +#define MT_RRO_TOP_BASE 0xA000 +#define MT_RRO_TOP(ofs) (MT_RRO_TOP_BASE + (ofs)) + +#define MT_RRO_BA_BITMAP_BASE0 MT_RRO_TOP(0x8) +#define MT_RRO_BA_BITMAP_BASE1 MT_RRO_TOP(0xC) +#define WF_RRO_AXI_MST_CFG MT_RRO_TOP(0xB8) +#define WF_RRO_AXI_MST_CFG_DIDX_OK BIT(12) + +#define MT_RRO_ADDR_ARRAY_BASE0 MT_RRO_TOP(0x30) +#define MT_RRO_ADDR_ARRAY_BASE1 MT_RRO_TOP(0x34) +#define MT_RRO_ADDR_ARRAY_ELEM_ADDR_SEG_MODE BIT(31) + +#define MT_RRO_IND_CMD_SIGNATURE_BASE0 MT_RRO_TOP(0x38) +#define MT_RRO_IND_CMD_SIGNATURE_BASE1 MT_RRO_TOP(0x3C) +#define MT_RRO_IND_CMD_0_CTRL0 MT_RRO_TOP(0x40) +#define MT_RRO_IND_CMD_SIGNATURE_BASE1_EN BIT(31) + +#define MT_RRO_PARTICULAR_CFG0 MT_RRO_TOP(0x5C) +#define MT_RRO_PARTICULAR_CFG1 MT_RRO_TOP(0x60) +#define MT_RRO_PARTICULAR_CONFG_EN BIT(31) +#define MT_RRO_PARTICULAR_SID GENMASK(30, 16) + +#define MT_RRO_BA_BITMAP_BASE_EXT0 MT_RRO_TOP(0x70) +#define MT_RRO_BA_BITMAP_BASE_EXT1 MT_RRO_TOP(0x74) +#define MT_RRO_HOST_INT_ENA MT_RRO_TOP(0x204) +#define MT_RRO_HOST_INT_ENA_HOST_RRO_DONE_ENA BIT(0) + +#define MT_RRO_ADDR_ELEM_SEG_ADDR0 MT_RRO_TOP(0x400) + +#define MT_RRO_3_0_EMU_CONF MT_RRO_TOP(0x600) +#define MT_RRO_3_0_EMU_CONF_EN_MASK BIT(11) + +#define MT_RRO_3_1_GLOBAL_CONFIG MT_RRO_TOP(0x604) +#define MT_RRO_3_1_GLOBAL_CONFIG_RXDMAD_SEL BIT(6) +#define MT_RRO_3_1_GLOBAL_CONFIG_RX_CIDX_RD_EN BIT(3) +#define MT_RRO_3_1_GLOBAL_CONFIG_RX_DIDX_WR_EN BIT(2) +#define MT_RRO_3_1_GLOBAL_CONFIG_INTERLEAVE_EN BIT(0) + +#define MT_RRO_MSDU_PG_SEG_ADDR0 MT_RRO_TOP(0x620) +#define MT_RRO_RX_RING_AP_CIDX_ADDR MT_RRO_TOP(0x6f0) +#define MT_RRO_RX_RING_AP_DIDX_ADDR MT_RRO_TOP(0x6f4) + +#define MT_RRO_ACK_SN_CTRL MT_RRO_TOP(0x50) +#define MT_RRO_ACK_SN_CTRL_SN_MASK GENMASK(27, 16) +#define MT_RRO_ACK_SN_CTRL_SESSION_MASK GENMASK(11, 0) + +#define MT_RRO_DBG_RD_CTRL MT_RRO_TOP(0xe0) +#define MT_RRO_DBG_RD_ADDR GENMASK(15, 0) +#define MT_RRO_DBG_RD_EXEC BIT(31) + +#define MT_RRO_DBG_RDAT_DW(_n) MT_RRO_TOP(0xf0 + (_n) * 0x4) + +#define MT_MCU_INT_EVENT 0x2108 +#define MT_MCU_INT_EVENT_DMA_STOPPED BIT(0) +#define MT_MCU_INT_EVENT_DMA_INIT BIT(1) +#define MT_MCU_INT_EVENT_RESET_DONE BIT(3) + +/* PLE */ +#define MT_PLE_BASE 0x820c0000 +#define MT_PLE(ofs) (MT_PLE_BASE + (ofs)) + +#define MT_FL_Q_EMPTY MT_PLE(0x360) +#define MT_FL_Q0_CTRL MT_PLE(0x3e0) +#define MT_FL_Q2_CTRL MT_PLE(0x3e8) +#define MT_FL_Q3_CTRL MT_PLE(0x3ec) + +#define MT_PLE_FREEPG_CNT MT_PLE(0x380) +#define MT_PLE_FREEPG_HEAD_TAIL MT_PLE(0x384) +#define MT_PLE_PG_HIF_GROUP MT_PLE(0x00c) +#define MT_PLE_HIF_PG_INFO MT_PLE(0x388) + +#define MT_PLE_AC_QEMPTY(ac, n) MT_PLE(0x600 + 0x80 * (ac) + ((n) << 2)) +#define MT_PLE_AMSDU_PACK_MSDU_CNT(n) MT_PLE(0x10e0 + ((n) << 2)) + +/* WF MDP TOP */ +#define MT_MDP_BASE 0x820cc000 +#define MT_MDP(ofs) (MT_MDP_BASE + (ofs)) + +#define MT_MDP_DCR0 MT_MDP(0x800) +#define MT_MDP_DCR0_RX_HDR_TRANS_EN BIT(19) + +#define MT_MDP_DCR2 MT_MDP(0x8e8) +#define MT_MDP_DCR2_RX_TRANS_SHORT BIT(2) + +/* TMAC: band 0(0x820e4000), band 1(0x820f4000), band 2(0x830e4000) */ +#define MT_WF_TMAC_BASE(_band) __BASE(WF_TMAC_BASE, (_band)) +#define MT_WF_TMAC(_band, ofs) (MT_WF_TMAC_BASE(_band) + (ofs)) + +#define MT_TMAC_TCR0(_band) MT_WF_TMAC(_band, 0) +#define MT_TMAC_TCR0_TX_BLINK GENMASK(7, 6) + +#define MT_TMAC_CDTR(_band) MT_WF_TMAC(_band, 0x0c8) +#define MT_TMAC_ODTR(_band) MT_WF_TMAC(_band, 0x0cc) +#define MT_TIMEOUT_VAL_PLCP GENMASK(15, 0) +#define MT_TIMEOUT_VAL_CCA GENMASK(31, 16) + +#define MT_TMAC_ICR0(_band) MT_WF_TMAC(_band, 0x014) +#define MT_IFS_EIFS_OFDM GENMASK(8, 0) +#define MT_IFS_RIFS GENMASK(14, 10) +#define MT_IFS_SIFS GENMASK(22, 16) +#define MT_IFS_SLOT GENMASK(30, 24) + +#define MT_TMAC_ICR1(_band) MT_WF_TMAC(_band, 0x018) +#define MT_IFS_EIFS_CCK GENMASK(8, 0) + +/* WF DMA TOP: band 0(0x820e7000), band 1(0x820f7000), band 2(0x830e7000) */ +#define MT_WF_DMA_BASE(_band) __BASE(WF_DMA_BASE, (_band)) +#define MT_WF_DMA(_band, ofs) (MT_WF_DMA_BASE(_band) + (ofs)) + +#define MT_DMA_DCR0(_band) MT_WF_DMA(_band, 0x000) +#define MT_DMA_DCR0_RXD_G5_EN BIT(23) + +#define MT_DMA_TCRF1(_band) MT_WF_DMA(_band, 0x054) +#define MT_DMA_TCRF1_QIDX GENMASK(15, 13) + +/* WTBLOFF TOP: band 0(0x820e9000), band 1(0x820f9000), band 2(0x830e9000) */ +#define MT_WTBLOFF_BASE(_band) __BASE(WF_WTBLOFF_BASE, (_band)) +#define MT_WTBLOFF(_band, ofs) (MT_WTBLOFF_BASE(_band) + (ofs)) + +#define MT_WTBLOFF_RSCR(_band) MT_WTBLOFF(_band, 0x008) +#define MT_WTBLOFF_RSCR_RCPI_MODE GENMASK(31, 30) +#define MT_WTBLOFF_RSCR_RCPI_PARAM GENMASK(25, 24) + +#define MT_WTBLOFF_ACR(_band) MT_WTBLOFF(_band, 0x010) +#define MT_WTBLOFF_ADM_BACKOFFTIME BIT(29) + +/* ETBF: band 0(0x820ea000), band 1(0x820fa000), band 2(0x830ea000) */ +#define MT_WF_ETBF_BASE(_band) __BASE(WF_ETBF_BASE, (_band)) +#define MT_WF_ETBF(_band, ofs) (MT_WF_ETBF_BASE(_band) + (ofs)) + +#define MT_ETBF_RX_FB_CONT(_band) MT_WF_ETBF(_band, 0x100) +#define MT_ETBF_RX_FB_BW GENMASK(10, 8) +#define MT_ETBF_RX_FB_NC GENMASK(7, 4) +#define MT_ETBF_RX_FB_NR GENMASK(3, 0) + +/* LPON: band 0(0x820eb000), band 1(0x820fb000), band 2(0x830eb000) */ +#define MT_WF_LPON_BASE(_band) __BASE(WF_LPON_BASE, (_band)) +#define MT_WF_LPON(_band, ofs) (MT_WF_LPON_BASE(_band) + (ofs)) + +#define MT_LPON_UTTR0(_band) MT_WF_LPON(_band, 0x360) +#define MT_LPON_UTTR1(_band) MT_WF_LPON(_band, 0x364) +#define MT_LPON_FRCR(_band) MT_WF_LPON(_band, 0x37c) + +#define MT_LPON_TCR(_band, n) MT_WF_LPON(_band, 0x0a8 + (((n) * 4) << 4)) +#define MT_LPON_TCR_SW_MODE GENMASK(1, 0) +#define MT_LPON_TCR_SW_WRITE BIT(0) +#define MT_LPON_TCR_SW_ADJUST BIT(1) +#define MT_LPON_TCR_SW_READ GENMASK(1, 0) + +/* MIB: band 0(0x820ed000), band 1(0x820fd000), band 2(0x830ed000)*/ +/* These counters are (mostly?) clear-on-read. So, some should not + * be read at all in case firmware is already reading them. These + * are commented with 'DNR' below. The DNR stats will be read by querying + * the firmware API for the appropriate message. For counters the driver + * does read, the driver should accumulate the counters. + */ +#define MT_WF_MIB_BASE(_band) __BASE(WF_MIB_BASE, (_band)) +#define MT_WF_MIB(_band, ofs) (MT_WF_MIB_BASE(_band) + (ofs)) + +#define MT_MIB_BSCR0(_band) MT_WF_MIB(_band, __OFFS(MIB_BSCR0)) +#define MT_MIB_BSCR1(_band) MT_WF_MIB(_band, __OFFS(MIB_BSCR1)) +#define MT_MIB_BSCR2(_band) MT_WF_MIB(_band, __OFFS(MIB_BSCR2)) +#define MT_MIB_BSCR3(_band) MT_WF_MIB(_band, __OFFS(MIB_BSCR3)) +#define MT_MIB_BSCR4(_band) MT_WF_MIB(_band, __OFFS(MIB_BSCR4)) +#define MT_MIB_BSCR5(_band) MT_WF_MIB(_band, __OFFS(MIB_BSCR5)) +#define MT_MIB_BSCR6(_band) MT_WF_MIB(_band, __OFFS(MIB_BSCR6)) +#define MT_MIB_BSCR7(_band) MT_WF_MIB(_band, __OFFS(MIB_BSCR7)) +#define MT_MIB_BSCR17(_band) MT_WF_MIB(_band, __OFFS(MIB_BSCR17)) + +#define MT_MIB_TSCR5(_band) MT_WF_MIB(_band, __OFFS(MIB_TSCR5)) +#define MT_MIB_TSCR6(_band) MT_WF_MIB(_band, __OFFS(MIB_TSCR6)) +#define MT_MIB_TSCR7(_band) MT_WF_MIB(_band, __OFFS(MIB_TSCR7)) + +#define MT_MIB_RSCR1(_band) MT_WF_MIB(_band, __OFFS(MIB_RSCR1)) +/* rx mpdu counter, full 32 bits */ +#define MT_MIB_RSCR31(_band) MT_WF_MIB(_band, __OFFS(MIB_RSCR31)) +#define MT_MIB_RSCR33(_band) MT_WF_MIB(_band, __OFFS(MIB_RSCR33)) + +#define MT_MIB_SDR6(_band) MT_WF_MIB(_band, 0x020) +#define MT_MIB_SDR6_CHANNEL_IDL_CNT_MASK GENMASK(15, 0) + +#define MT_MIB_RVSR0(_band) MT_WF_MIB(_band, __OFFS(MIB_RVSR0)) + +#define MT_MIB_RSCR35(_band) MT_WF_MIB(_band, __OFFS(MIB_RSCR35)) +#define MT_MIB_RSCR36(_band) MT_WF_MIB(_band, __OFFS(MIB_RSCR36)) + +/* tx ampdu cnt, full 32 bits */ +#define MT_MIB_TSCR0(_band) MT_WF_MIB(_band, __OFFS(MIB_TSCR0)) +#define MT_MIB_TSCR2(_band) MT_WF_MIB(_band, __OFFS(MIB_TSCR2)) + +/* counts all mpdus in ampdu, regardless of success */ +#define MT_MIB_TSCR3(_band) MT_WF_MIB(_band, __OFFS(MIB_TSCR3)) + +/* counts all successfully tx'd mpdus in ampdu */ +#define MT_MIB_TSCR4(_band) MT_WF_MIB(_band, __OFFS(MIB_TSCR4)) + +/* rx ampdu count, 32-bit */ +#define MT_MIB_RSCR27(_band) MT_WF_MIB(_band, __OFFS(MIB_RSCR27)) + +/* rx ampdu bytes count, 32-bit */ +#define MT_MIB_RSCR28(_band) MT_WF_MIB(_band, __OFFS(MIB_RSCR28)) + +/* rx ampdu valid subframe count */ +#define MT_MIB_RSCR29(_band) MT_WF_MIB(_band, __OFFS(MIB_RSCR29)) + +/* rx ampdu valid subframe bytes count, 32bits */ +#define MT_MIB_RSCR30(_band) MT_WF_MIB(_band, __OFFS(MIB_RSCR30)) + +/* remaining windows protected stats */ +#define MT_MIB_SDR27(_band) MT_WF_MIB(_band, 0x080) +#define MT_MIB_SDR27_TX_RWP_FAIL_CNT GENMASK(15, 0) + +#define MT_MIB_SDR28(_band) MT_WF_MIB(_band, 0x084) +#define MT_MIB_SDR28_TX_RWP_NEED_CNT GENMASK(15, 0) + +#define MT_MIB_RVSR1(_band) MT_WF_MIB(_band, __OFFS(MIB_RVSR1)) + +/* rx blockack count, 32 bits */ +#define MT_MIB_TSCR1(_band) MT_WF_MIB(_band, __OFFS(MIB_TSCR1)) + +#define MT_MIB_BTSCR0(_band) MT_WF_MIB(_band, 0x5e0) +#define MT_MIB_BTSCR5(_band) MT_WF_MIB(_band, __OFFS(MIB_BTSCR5)) +#define MT_MIB_BTSCR6(_band) MT_WF_MIB(_band, __OFFS(MIB_BTSCR6)) + +#define MT_MIB_BFTFCR(_band) MT_WF_MIB(_band, 0x5d0) + +#define MT_TX_AGG_CNT(_band, n) MT_WF_MIB(_band, __OFFS(MIB_TRDR1) + ((n) << 2)) +#define MT_MIB_ARNG(_band, n) MT_WF_MIB(_band, 0x0b0 + ((n) << 2)) +#define MT_MIB_ARNCR_RANGE(val, n) (((val) >> ((n) << 4)) & GENMASK(9, 0)) + +/* UMIB */ +#define MT_WF_UMIB_BASE 0x820cd000 +#define MT_WF_UMIB(ofs) (MT_WF_UMIB_BASE + (ofs)) + +#define MT_UMIB_RPDCR(_band) (MT_WF_UMIB(0x594) + (_band) * 0x164) + +/* WTBLON TOP */ +#define MT_WTBLON_TOP_BASE 0x820d4000 +#define MT_WTBLON_TOP(ofs) (MT_WTBLON_TOP_BASE + (ofs)) +#define MT_WTBLON_TOP_WDUCR MT_WTBLON_TOP(__OFFS(WTBLON_WDUCR)) +#define MT_WTBLON_TOP_WDUCR_GROUP GENMASK(4, 0) + +#define MT_WTBL_UPDATE MT_WTBLON_TOP(__OFFS(WTBL_UPDATE)) +#define MT_WTBL_UPDATE_WLAN_IDX GENMASK(11, 0) +#define MT_WTBL_UPDATE_ADM_COUNT_CLEAR BIT(14) +#define MT_WTBL_UPDATE_BUSY BIT(31) + +#define MT_WTBL_ITCR MT_WTBLON_TOP(__OFFS(WTBL_ITCR)) +#define MT_WTBL_ITCR_WR BIT(16) +#define MT_WTBL_ITCR_EXEC BIT(31) +#define MT_WTBL_ITDR0 MT_WTBLON_TOP(__OFFS(WTBL_ITCR0)) +#define MT_WTBL_ITDR1 MT_WTBLON_TOP(__OFFS(WTBL_ITCR1)) +#define MT_WTBL_SPE_IDX_SEL BIT(6) + +/* WTBL */ +#define MT_WTBL_BASE 0x820d8000 +#define MT_WTBL_LMAC_ID GENMASK(14, 8) +#define MT_WTBL_LMAC_DW GENMASK(7, 2) +#define MT_WTBL_LMAC_OFFS(_id, _dw) (MT_WTBL_BASE | \ + FIELD_PREP(MT_WTBL_LMAC_ID, _id) | \ + FIELD_PREP(MT_WTBL_LMAC_DW, _dw)) + +/* AGG: band 0(0x820e2000), band 1(0x820f2000), band 2(0x830e2000) */ +#define MT_WF_AGG_BASE(_band) __BASE(WF_AGG_BASE, (_band)) +#define MT_WF_AGG(_band, ofs) (MT_WF_AGG_BASE(_band) + (ofs)) + +#define MT_AGG_ACR4(_band) MT_WF_AGG(_band, 0x3c) +#define MT_AGG_ACR_PPDU_TXS2H BIT(1) + +/* ARB: band 0(0x820e3000), band 1(0x820f3000), band 2(0x830e3000) */ +#define MT_WF_ARB_BASE(_band) __BASE(WF_ARB_BASE, (_band)) +#define MT_WF_ARB(_band, ofs) (MT_WF_ARB_BASE(_band) + (ofs)) + +#define MT_ARB_SCR(_band) MT_WF_ARB(_band, 0x000) +#define MT_ARB_SCR_TX_DISABLE BIT(8) +#define MT_ARB_SCR_RX_DISABLE BIT(9) + +/* RMAC: band 0(0x820e5000), band 1(0x820f5000), band 2(0x830e5000), */ +#define MT_WF_RMAC_BASE(_band) __BASE(WF_RMAC_BASE, (_band)) +#define MT_WF_RMAC(_band, ofs) (MT_WF_RMAC_BASE(_band) + (ofs)) + +#define MT_WF_RFCR(_band) MT_WF_RMAC(_band, 0x000) +#define MT_WF_RFCR_DROP_STBC_MULTI BIT(0) +#define MT_WF_RFCR_DROP_FCSFAIL BIT(1) +#define MT_WF_RFCR_DROP_PROBEREQ BIT(4) +#define MT_WF_RFCR_DROP_MCAST BIT(5) +#define MT_WF_RFCR_DROP_BCAST BIT(6) +#define MT_WF_RFCR_DROP_MCAST_FILTERED BIT(7) +#define MT_WF_RFCR_DROP_A3_MAC BIT(8) +#define MT_WF_RFCR_DROP_A3_BSSID BIT(9) +#define MT_WF_RFCR_DROP_A2_BSSID BIT(10) +#define MT_WF_RFCR_DROP_OTHER_BEACON BIT(11) +#define MT_WF_RFCR_DROP_FRAME_REPORT BIT(12) +#define MT_WF_RFCR_DROP_CTL_RSV BIT(13) +#define MT_WF_RFCR_DROP_CTS BIT(14) +#define MT_WF_RFCR_DROP_RTS BIT(15) +#define MT_WF_RFCR_DROP_DUPLICATE BIT(16) +#define MT_WF_RFCR_DROP_OTHER_BSS BIT(17) +#define MT_WF_RFCR_DROP_OTHER_UC BIT(18) +#define MT_WF_RFCR_DROP_OTHER_TIM BIT(19) +#define MT_WF_RFCR_DROP_NDPA BIT(20) +#define MT_WF_RFCR_DROP_UNWANTED_CTL BIT(21) + +#define MT_WF_RFCR1(_band) MT_WF_RMAC(_band, 0x004) +#define MT_WF_RFCR1_DROP_ACK BIT(4) +#define MT_WF_RFCR1_DROP_BF_POLL BIT(5) +#define MT_WF_RFCR1_DROP_BA BIT(6) +#define MT_WF_RFCR1_DROP_CFEND BIT(7) +#define MT_WF_RFCR1_DROP_CFACK BIT(8) + +#define MT_WF_RMAC_MIB_AIRTIME0(_band) MT_WF_RMAC(_band, 0x0380) +#define MT_WF_RMAC_MIB_RXTIME_CLR BIT(31) +#define MT_WF_RMAC_MIB_ED_OFFSET GENMASK(20, 16) +#define MT_WF_RMAC_MIB_OBSS_BACKOFF GENMASK(15, 0) + +#define MT_WF_RMAC_MIB_AIRTIME1(_band) MT_WF_RMAC(_band, 0x0384) +#define MT_WF_RMAC_MIB_NONQOSD_BACKOFF GENMASK(31, 16) + +#define MT_WF_RMAC_MIB_AIRTIME3(_band) MT_WF_RMAC(_band, 0x038c) +#define MT_WF_RMAC_MIB_QOS01_BACKOFF GENMASK(31, 0) + +#define MT_WF_RMAC_MIB_AIRTIME4(_band) MT_WF_RMAC(_band, 0x0390) +#define MT_WF_RMAC_MIB_QOS23_BACKOFF GENMASK(31, 0) + +#define MT_WF_RMAC_RSVD0(_band) MT_WF_RMAC(_band, 0x03e0) +#define MT_WF_RMAC_RSVD0_EIFS_CLR BIT(21) + +/* RATE: band 0(0x820ee000), band 1(0x820fe000), band 2(0x830ee000) */ +#define MT_WF_RATE_BASE(_band) __BASE(WF_RATE_BASE, (_band)) +#define MT_WF_RATE(_band, ofs) (MT_WF_RATE_BASE(_band) + (ofs)) + +#define MT_RATE_HRCR0(_band) MT_WF_RATE(_band, 0x050) +#define MT_RATE_HRCR0_CFEND_RATE GENMASK(14, 0) + +/* WFDMA0 */ +#define MT_WFDMA0_BASE 0xd4000 +#define MT_WFDMA0(ofs) (MT_WFDMA0_BASE + (ofs)) + +#define MT_WFDMA0_RST MT_WFDMA0(0x100) +#define MT_WFDMA0_RST_LOGIC_RST BIT(4) +#define MT_WFDMA0_RST_DMASHDL_ALL_RST BIT(5) + +#define MT_WFDMA0_BUSY_ENA MT_WFDMA0(0x13c) +#define MT_WFDMA0_BUSY_ENA_TX_FIFO0 BIT(0) +#define MT_WFDMA0_BUSY_ENA_TX_FIFO1 BIT(1) +#define MT_WFDMA0_BUSY_ENA_RX_FIFO BIT(2) + +#define MT_WFDMA0_RX_INT_PCIE_SEL MT_WFDMA0(0x154) +#define MT_WFDMA0_RX_INT_SEL_RING3 BIT(3) +#define MT_WFDMA0_RX_INT_SEL_RING5 BIT(5) +#define MT_WFDMA0_RX_INT_SEL_RING6 BIT(6) +#define MT_WFDMA0_RX_INT_SEL_RING9 BIT(9) + +#define MT_WFDMA0_MCU_HOST_INT_ENA MT_WFDMA0(0x1f4) + +#define MT_WFDMA0_GLO_CFG MT_WFDMA0(0x208) +#define MT_WFDMA0_GLO_CFG_TX_DMA_EN BIT(0) +#define MT_WFDMA0_GLO_CFG_RX_DMA_EN BIT(2) +#define MT_WFDMA0_GLO_CFG_OMIT_RX_INFO_PFET2 BIT(21) +#define MT_WFDMA0_GLO_CFG_EXT_EN BIT(26) +#define MT_WFDMA0_GLO_CFG_OMIT_RX_INFO BIT(27) +#define MT_WFDMA0_GLO_CFG_OMIT_TX_INFO BIT(28) + +#define MT_WFDMA0_PAUSE_RX_Q_45_TH MT_WFDMA0(0x268) +#define MT_WFDMA0_PAUSE_RX_Q_67_TH MT_WFDMA0(0x26c) +#define MT_WFDMA0_PAUSE_RX_Q_89_TH MT_WFDMA0(0x270) +#define MT_WFDMA0_PAUSE_RX_Q_RRO_TH MT_WFDMA0(0x27c) + +#define WF_WFDMA0_GLO_CFG_EXT0 MT_WFDMA0(0x2b0) +#define WF_WFDMA0_GLO_CFG_EXT0_OUTSTAND_MASK GENMASK(27, 24) +#define WF_WFDMA0_GLO_CFG_EXT0_RX_WB_RXD BIT(18) +#define WF_WFDMA0_GLO_CFG_EXT0_WED_MERGE_MODE BIT(14) + +#define WF_WFDMA0_GLO_CFG_EXT1 MT_WFDMA0(0x2b4) +#define WF_WFDMA0_GLO_CFG_EXT1_CALC_MODE BIT(31) +#define WF_WFDMA0_GLO_CFG_EXT1_TX_FCTRL_MODE BIT(28) + +#define MT_WFDMA0_RST_DTX_PTR MT_WFDMA0(0x20c) +#define MT_WFDMA0_PRI_DLY_INT_CFG0 MT_WFDMA0(0x2f0) +#define MT_WFDMA0_PRI_DLY_INT_CFG1 MT_WFDMA0(0x2f4) +#define MT_WFDMA0_PRI_DLY_INT_CFG2 MT_WFDMA0(0x2f8) + +/* WFDMA1 */ +#define MT_WFDMA1_BASE 0xd5000 + +/* WFDMA CSR */ +#define MT_WFDMA_EXT_CSR_BASE 0xd7000 +#define MT_WFDMA_EXT_CSR(ofs) (MT_WFDMA_EXT_CSR_BASE + (ofs)) + +#define MT_WFDMA_HOST_CONFIG MT_WFDMA_EXT_CSR(0x30) +#define MT_WFDMA_HOST_CONFIG_PDMA_BAND BIT(0) +#define MT_WFDMA_HOST_CONFIG_BAND0_PCIE1 BIT(20) +#define MT_WFDMA_HOST_CONFIG_BAND1_PCIE1 BIT(21) +#define MT_WFDMA_HOST_CONFIG_BAND2_PCIE1 BIT(22) + +#define MT_WFDMA_EXT_CSR_HIF_MISC MT_WFDMA_EXT_CSR(0x44) +#define MT_WFDMA_EXT_CSR_HIF_MISC_BUSY BIT(0) + +#define MT_WFDMA_AXI_R2A_CTRL MT_WFDMA_EXT_CSR(0x500) +#define MT_WFDMA_AXI_R2A_CTRL_OUTSTAND_MASK GENMASK(4, 0) + +#define MT_WFDMA_AXI_R2A_CTRL2 MT_WFDMA_EXT_CSR(0x508) +#define MT_WFDMA_AXI_R2A_CTRL2_OUTSTAND_MASK GENMASK(31, 28) + +#define MT_PCIE_RECOG_ID 0xd7090 +#define MT_PCIE_RECOG_ID_MASK GENMASK(30, 0) +#define MT_PCIE_RECOG_ID_SEM BIT(31) + +/* WFDMA0 PCIE1 */ +#define MT_WFDMA0_PCIE1_BASE 0xd8000 +#define MT_WFDMA0_PCIE1(ofs) (MT_WFDMA0_PCIE1_BASE + (ofs)) + +#define MT_INT_PCIE1_SOURCE_CSR_EXT MT_WFDMA0_PCIE1(0x118) +#define MT_INT_PCIE1_MASK_CSR MT_WFDMA0_PCIE1(0x11c) + +#define MT_WFDMA0_PCIE1_BUSY_ENA MT_WFDMA0_PCIE1(0x13c) +#define MT_WFDMA0_PCIE1_BUSY_ENA_TX_FIFO0 BIT(0) +#define MT_WFDMA0_PCIE1_BUSY_ENA_TX_FIFO1 BIT(1) +#define MT_WFDMA0_PCIE1_BUSY_ENA_RX_FIFO BIT(2) + +/* WFDMA COMMON */ +#define __RXQ(q) ((q) + __MT_MCUQ_MAX) +#define __TXQ(q) (__RXQ(q) + __MT_RXQ_MAX) + +#define MT_Q_ID(q) (dev->q_id[(q)]) +#define MT_Q_BASE(q) ((dev->q_wfdma_mask >> (q)) & 0x1 ? \ + MT_WFDMA1_BASE : MT_WFDMA0_BASE) + +#define MT_MCUQ_ID(q) MT_Q_ID(q) +#define MT_TXQ_ID(q) MT_Q_ID(__TXQ(q)) +#define MT_RXQ_ID(q) MT_Q_ID(__RXQ(q)) + +#define MT_MCUQ_RING_BASE(q) (MT_Q_BASE(q) + 0x300) +#define MT_TXQ_RING_BASE(q) (MT_Q_BASE(__TXQ(q)) + 0x300) +#define MT_RXQ_RING_BASE(q) (MT_Q_BASE(__RXQ(q)) + 0x500) +#define MT_RXQ_RRO_IND_RING_BASE MT_RRO_TOP(0x40) + +#define MT_RXQ_RRO_AP_RING_BASE MT_RRO_TOP(0x650) + +#define MT_MCUQ_EXT_CTRL(q) (MT_Q_BASE(q) + 0x600 + \ + MT_MCUQ_ID(q) * 0x4) +#define MT_RXQ_EXT_CTRL(q) (MT_Q_BASE(__RXQ(q)) + 0x680 + \ + MT_RXQ_ID(q) * 0x4) +#define MT_TXQ_EXT_CTRL(q) (MT_Q_BASE(__TXQ(q)) + 0x600 + \ + MT_TXQ_ID(q) * 0x4) + +#define MT_INT_SOURCE_CSR MT_WFDMA0(0x200) +#define MT_INT_MASK_CSR MT_WFDMA0(0x204) + +#define MT_INT1_SOURCE_CSR MT_WFDMA0_PCIE1(0x200) +#define MT_INT1_MASK_CSR MT_WFDMA0_PCIE1(0x204) + +#define MT_INT_RX_DONE_BAND0 BIT(12) +#define MT_INT_RX_DONE_BAND1 BIT(13) /* for mt7992 */ +#define MT_INT_RX_DONE_BAND2 BIT(13) +#define MT_INT_RX_DONE_WM BIT(0) +#define MT_INT_RX_DONE_WA BIT(1) +#define MT_INT_RX_DONE_WA_MAIN BIT(2) +#define MT_INT_RX_DONE_WA_EXT BIT(3) /* for mt7992 */ +#define MT_INT_RX_DONE_WA_TRI BIT(3) +#define MT_INT_RX_TXFREE_MAIN BIT(17) +#define MT_INT_RX_TXFREE_BAND1 BIT(15) +#define MT_INT_RX_TXFREE_TRI BIT(15) +#define MT_INT_RX_TXFREE_BAND1_EXT BIT(19) /* for mt7992 two PCIE*/ +#define MT_INT_RX_TXFREE_BAND0_MT7990 BIT(14) +#define MT_INT_RX_TXFREE_BAND1_MT7990 BIT(15) +#define MT_INT_RX_DONE_BAND2_EXT BIT(23) +#define MT_INT_RX_TXFREE_EXT BIT(26) +#define MT_INT_MCU_CMD BIT(29) + +#define MT_INT_RX_DONE_RRO_BAND0 BIT(16) +#define MT_INT_RX_DONE_RRO_BAND1 BIT(17) +#define MT_INT_RX_DONE_RRO_BAND2 BIT(14) +#define MT_INT_RX_DONE_RRO_IND BIT(11) +#define MT_INT_RX_DONE_RRO_RXDMAD_C BIT(11) +#define MT_INT_RX_DONE_MSDU_PG_BAND0 BIT(18) +#define MT_INT_RX_DONE_MSDU_PG_BAND1 BIT(19) +#define MT_INT_RX_DONE_MSDU_PG_BAND2 BIT(23) + +#define MT_INT_RX(q) (dev->q_int_mask[__RXQ(q)]) +#define MT_INT_TX_MCU(q) (dev->q_int_mask[(q)]) + +#define MT_INT_RX_DONE_MCU (MT_INT_RX(MT_RXQ_MCU) | \ + MT_INT_RX(MT_RXQ_MCU_WA)) + +#define MT_INT_BAND0_RX_DONE (MT_INT_RX(MT_RXQ_MAIN) | \ + MT_INT_RX(MT_RXQ_MAIN_WA) | \ + MT_INT_RX(MT_RXQ_TXFREE_BAND0)) + +#define MT_INT_BAND1_RX_DONE (MT_INT_RX(MT_RXQ_BAND1) | \ + MT_INT_RX(MT_RXQ_BAND1_WA) | \ + MT_INT_RX(MT_RXQ_MAIN_WA) | \ + MT_INT_RX(MT_RXQ_TXFREE_BAND0)) + +#define MT_INT_BAND2_RX_DONE (MT_INT_RX(MT_RXQ_BAND2) | \ + MT_INT_RX(MT_RXQ_BAND2_WA) | \ + MT_INT_RX(MT_RXQ_MAIN_WA) | \ + MT_INT_RX(MT_RXQ_TXFREE_BAND0)) + +#define MT_INT_RRO_RX_DONE (MT_INT_RX(MT_RXQ_RRO_BAND0) | \ + MT_INT_RX(MT_RXQ_RRO_BAND1) | \ + MT_INT_RX(MT_RXQ_RRO_BAND2) | \ + MT_INT_RX(MT_RXQ_RRO_IND) | \ + MT_INT_RX(MT_RXQ_RRO_RXDMAD_C) | \ + MT_INT_RX(MT_RXQ_MSDU_PAGE_BAND0) | \ + MT_INT_RX(MT_RXQ_MSDU_PAGE_BAND1) | \ + MT_INT_RX(MT_RXQ_MSDU_PAGE_BAND2)) + +#define MT_INT_RX_DONE_ALL (MT_INT_RX_DONE_MCU | \ + MT_INT_BAND0_RX_DONE | \ + MT_INT_BAND1_RX_DONE | \ + MT_INT_BAND2_RX_DONE | \ + MT_INT_RRO_RX_DONE) + +#define MT_INT_TX_DONE_FWDL BIT(26) +#define MT_INT_TX_DONE_MCU_WM BIT(27) +#define MT_INT_TX_DONE_MCU_WA BIT(22) +#define MT_INT_TX_DONE_BAND0 BIT(30) +#define MT_INT_TX_DONE_BAND1 BIT(31) +#define MT_INT_TX_DONE_BAND2 BIT(15) + +#define MT_INT_TX_RX_DONE_EXT (MT_INT_TX_DONE_BAND2 | \ + MT_INT_RX_DONE_BAND2_EXT | \ + MT_INT_RX_TXFREE_EXT) + +#define MT_INT_TX_DONE_MCU (MT_INT_TX_MCU(MT_MCUQ_WA) | \ + MT_INT_TX_MCU(MT_MCUQ_WM) | \ + MT_INT_TX_MCU(MT_MCUQ_FWDL)) + +#define MT_MCU_CMD MT_WFDMA0(0x1f0) +#define MT_MCU_CMD_STOP_DMA BIT(2) +#define MT_MCU_CMD_RESET_DONE BIT(3) +#define MT_MCU_CMD_RECOVERY_DONE BIT(4) +#define MT_MCU_CMD_NORMAL_STATE BIT(5) +#define MT_MCU_CMD_ERROR_MASK GENMASK(5, 1) + +#define MT_MCU_CMD_WA_WDT BIT(31) +#define MT_MCU_CMD_WM_WDT BIT(30) +#define MT_MCU_CMD_WDT_MASK GENMASK(31, 30) + +/* l1/l2 remap */ +#define CONN_BUS_CR_VON_BASE 0x155000 +#define MT_HIF_REMAP_L1 (CONN_BUS_CR_VON_BASE + __OFFS(HIF_REMAP_L1)) +#define MT_HIF_REMAP_L1_MASK_7996 GENMASK(31, 16) +#define MT_HIF_REMAP_L1_MASK GENMASK(15, 0) +#define MT_HIF_REMAP_L1_OFFSET GENMASK(15, 0) +#define MT_HIF_REMAP_L1_BASE GENMASK(31, 16) +#define MT_HIF_REMAP_BASE_L1 __OFFS(HIF_REMAP_BASE_L1) + +#define MT_HIF_REMAP_L2 __OFFS(HIF_REMAP_L2) +#define MT_HIF_REMAP_L2_MASK GENMASK(19, 0) +#define MT_HIF_REMAP_L2_OFFSET GENMASK(11, 0) +#define MT_HIF_REMAP_L2_BASE GENMASK(31, 12) +#define MT_HIF_REMAP_L2_MASK_7990 GENMASK(15, 0) +#define MT_HIF_REMAP_L2_OFFSET_7990 GENMASK(15, 0) +#define MT_HIF_REMAP_L2_BASE_7990 GENMASK(31, 16) +#define MT_HIF_REMAP_BASE_L2 __OFFS(HIF_REMAP_BASE_L2) + +/* for mt7990 only */ +#define MT_HIF_REMAP_CBTOP 0x1f6554 +#define MT_HIF_REMAP_CBTOP_MASK GENMASK(15, 0) +#define MT_HIF_REMAP_CBTOP_OFFSET GENMASK(15, 0) +#define MT_HIF_REMAP_CBTOP_BASE GENMASK(31, 16) +#define MT_HIF_REMAP_BASE_CBTOP 0x1c0000 + +#define MT_INFRA_BASE 0x18000000 +#define MT_WFSYS0_PHY_START 0x18400000 +#define MT_WFSYS1_PHY_START 0x18800000 +#define MT_WFSYS1_PHY_END 0x18bfffff +#define MT_CBTOP1_PHY_START 0x70000000 +#define MT_CBTOP1_PHY_END __OFFS(CBTOP1_PHY_END) +#define MT_CBTOP2_PHY_START 0xf0000000 +#define MT_INFRA_MCU_START 0x7c000000 +#define MT_INFRA_MCU_END __OFFS(INFRA_MCU_END) + +/* FW MODE SYNC */ +#define MT_FW_ASSERT_CNT 0x02208274 +#define MT_FW_DUMP_STATE 0x02209e90 + +#define MT_SWDEF_BASE 0x00401400 + +#define MT_SWDEF(ofs) (MT_SWDEF_BASE + (ofs)) +#define MT_SWDEF_MODE MT_SWDEF(0x3c) +#define MT_SWDEF_NORMAL_MODE 0 + +#define MT_SWDEF_SER_STATS MT_SWDEF(0x040) +#define MT_SWDEF_PLE_STATS MT_SWDEF(0x044) +#define MT_SWDEF_PLE1_STATS MT_SWDEF(0x048) +#define MT_SWDEF_PLE_AMSDU_STATS MT_SWDEF(0x04c) +#define MT_SWDEF_PSE_STATS MT_SWDEF(0x050) +#define MT_SWDEF_PSE1_STATS MT_SWDEF(0x054) +#define MT_SWDEF_LAMC_WISR6_BN0_STATS MT_SWDEF(0x058) +#define MT_SWDEF_LAMC_WISR6_BN1_STATS MT_SWDEF(0x05c) +#define MT_SWDEF_LAMC_WISR6_BN2_STATS MT_SWDEF(0x060) +#define MT_SWDEF_LAMC_WISR7_BN0_STATS MT_SWDEF(0x064) +#define MT_SWDEF_LAMC_WISR7_BN1_STATS MT_SWDEF(0x068) +#define MT_SWDEF_LAMC_WISR7_BN2_STATS MT_SWDEF(0x06c) + +/* LED */ +#define MT_LED_TOP_BASE 0x18013000 +#define MT_LED_PHYS(_n) (MT_LED_TOP_BASE + (_n)) + +#define MT_LED_CTRL(_n) MT_LED_PHYS(0x00 + ((_n) * 4)) +#define MT_LED_CTRL_KICK BIT(7) +#define MT_LED_CTRL_BLINK_BAND_SEL BIT(4) +#define MT_LED_CTRL_BLINK_MODE BIT(2) +#define MT_LED_CTRL_POLARITY BIT(1) + +#define MT_LED_TX_BLINK(_n) MT_LED_PHYS(0x10 + ((_n) * 4)) +#define MT_LED_TX_BLINK_ON_MASK GENMASK(7, 0) +#define MT_LED_TX_BLINK_OFF_MASK GENMASK(15, 8) + +#define MT_LED_EN(_n) MT_LED_PHYS(0x40 + ((_n) * 4)) + +/* CONN DBG */ +#define MT_CONN_DBG_CTL_BASE 0x18023000 +#define MT_CONN_DBG_CTL(ofs) (MT_CONN_DBG_CTL_BASE + (ofs)) +#define MT_CONN_DBG_CTL_OUT_SEL MT_CONN_DBG_CTL(0x604) +#define MT_CONN_DBG_CTL_PC_LOG_SEL MT_CONN_DBG_CTL(0x60c) +#define MT_CONN_DBG_CTL_PC_LOG MT_CONN_DBG_CTL(0x610) + +#define MT_LED_GPIO_MUX2 0x70005058 /* GPIO 18 */ +#define MT_LED_GPIO_MUX3 0x7000505C /* GPIO 26 */ +#define MT_LED_GPIO_SEL_MASK GENMASK(11, 8) + +/* MT TOP */ +#define MT_TOP_BASE 0xe0000 +#define MT_TOP(ofs) (MT_TOP_BASE + (ofs)) + +#define MT_TOP_LPCR_HOST_BAND(_band) MT_TOP(0x10 + ((_band) * 0x10)) +#define MT_TOP_LPCR_HOST_FW_OWN BIT(0) +#define MT_TOP_LPCR_HOST_DRV_OWN BIT(1) +#define MT_TOP_LPCR_HOST_FW_OWN_STAT BIT(2) + +#define MT_TOP_LPCR_HOST_BAND_IRQ_STAT(_band) MT_TOP(0x14 + ((_band) * 0x10)) +#define MT_TOP_LPCR_HOST_BAND_STAT BIT(0) + +#define MT_TOP_MISC MT_TOP(0xf0) +#define MT_TOP_MISC_FW_STATE GENMASK(2, 0) + +/* ADIE */ +#define MT_ADIE_CHIP_ID(_idx) (0x0f00002c + ((_idx) << 28)) +#define MT_ADIE_VERSION_MASK GENMASK(15, 0) +#define MT_ADIE_CHIP_ID_MASK GENMASK(31, 16) + +#define MT_PAD_GPIO 0x700056f0 +#define MT_PAD_GPIO_ADIE_COMB GENMASK(16, 15) +#define MT_PAD_GPIO_2ADIE_TBTC BIT(19) +/* for mt7992 */ +#define MT_PAD_GPIO_ADIE_COMB_7992 GENMASK(17, 16) +#define MT_PAD_GPIO_ADIE_SINGLE BIT(15) + +#define MT_HW_REV 0x70010204 +#define MT_HW_REV1 0x8a00 + +#define MT_WF_L05_RST 0x70028550 +#define MT_WF_L05_RST_WF_RST_MASK GENMASK(4, 0) + +#define MT_WF_SUBSYS_RST 0x70028600 +#define MT_WF_SUBSYS_RST_WHOLE_PATH_RST BIT(0) +#define MT_WF_SUBSYS_RST_WHOLE_PATH_RST_REVERT BIT(5) +#define MT_WF_SUBSYS_RST_BYPASS_WFDMA_SLP_PROT BIT(6) +#define MT_WF_SUBSYS_RST_BYPASS_WFDMA2_SLP_PROT BIT(16) +#define MT_WF_SUBSYS_RST_WHOLE_PATH_RST_REVERT_CYCLE GENMASK(15, 8) + +/* PCIE MAC */ +#define MT_PCIE_MAC_BASE 0x74030000 +#define MT_PCIE_MAC(ofs) (MT_PCIE_MAC_BASE + (ofs)) +#define MT_PCIE_MAC_INT_ENABLE MT_PCIE_MAC(0x188) + +#define MT_PCIE1_MAC_BASE 0x74090000 +#define MT_PCIE1_MAC(ofs) (MT_PCIE1_MAC_BASE + (ofs)) + +#define MT_PCIE1_MAC_INT_ENABLE MT_PCIE1_MAC(0x188) + +/* PHYRX CSD */ +#define MT_WF_PHYRX_CSD_BASE 0x83000000 +#define MT_WF_PHYRX_CSD(_band, _wf, ofs) (MT_WF_PHYRX_CSD_BASE + \ + ((_band) << 20) + \ + ((_wf) << 16) + (ofs)) +#define MT_WF_PHYRX_CSD_IRPI(_band, _wf) MT_WF_PHYRX_CSD(_band, _wf, 0x1000) + +/* PHYRX CTRL */ +#define MT_WF_PHYRX_BAND_BASE 0x83080000 +#define MT_WF_PHYRX_BAND(_band, ofs) (MT_WF_PHYRX_BAND_BASE + \ + ((_band) << 20) + (ofs)) + +#define MT_WF_PHYRX_BAND_GID_TAB_VLD0(_band) MT_WF_PHYRX_BAND(_band, 0x1054) +#define MT_WF_PHYRX_BAND_GID_TAB_VLD1(_band) MT_WF_PHYRX_BAND(_band, 0x1058) +#define MT_WF_PHYRX_BAND_GID_TAB_POS0(_band) MT_WF_PHYRX_BAND(_band, 0x105c) +#define MT_WF_PHYRX_BAND_GID_TAB_POS1(_band) MT_WF_PHYRX_BAND(_band, 0x1060) +#define MT_WF_PHYRX_BAND_GID_TAB_POS2(_band) MT_WF_PHYRX_BAND(_band, 0x1064) +#define MT_WF_PHYRX_BAND_GID_TAB_POS3(_band) MT_WF_PHYRX_BAND(_band, 0x1068) + +#define MT_WF_PHYRX_BAND_RX_CTRL1(_band) MT_WF_PHYRX_BAND(_band, 0x2004) +#define MT_WF_PHYRX_BAND_RX_CTRL1_IPI_EN GENMASK(2, 0) +#define MT_WF_PHYRX_BAND_RX_CTRL1_STSCNT_EN GENMASK(11, 9) + +/* PHYRX CSD BAND */ +#define MT_WF_PHYRX_CSD_BAND_RXTD12(_band) MT_WF_PHYRX_BAND(_band, 0x8230) +#define MT_WF_PHYRX_CSD_BAND_RXTD12_IRPI_SW_CLR_ONLY BIT(18) +#define MT_WF_PHYRX_CSD_BAND_RXTD12_IRPI_SW_CLR BIT(29) + +/* CONN MCU EXCP CON */ +#define MT_MCU_WM_EXCP_BASE 0x89050000 +#define MT_MCU_WM_EXCP(ofs) (MT_MCU_WM_EXCP_BASE + (ofs)) +#define MT_MCU_WM_EXCP_PC_CTRL MT_MCU_WM_EXCP(0x100) +#define MT_MCU_WM_EXCP_PC_LOG MT_MCU_WM_EXCP(0x104) +#define MT_MCU_WM_EXCP_LR_CTRL MT_MCU_WM_EXCP(0x200) +#define MT_MCU_WM_EXCP_LR_LOG MT_MCU_WM_EXCP(0x204) + +/* CONN AFE CTL CON */ +#define MT_AFE_CTL_BASE 0x18043000 +#define MT_AFE_CTL_BAND(_band, ofs) (MT_AFE_CTL_BASE + \ + ((_band) * 0x1000) + (ofs)) +#define MT_AFE_CTL_BAND_PLL_03(_band) MT_AFE_CTL_BAND(_band, 0x2c) +#define MT_AFE_CTL_BAND_PLL_03_MSB_EN BIT(1) + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/npu.c b/drivers/net/wireless/mediatek/mt76/npu.c new file mode 100644 index 000000000..c4c7c0af6 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/npu.c @@ -0,0 +1,528 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2025 AIROHA Inc + * Author: Lorenzo Bianconi + */ +#include +#include +#include + +#include "mt76.h" +#include "dma.h" +#include "mt76_connac.h" + +#define MT76_NPU_RX_BUF_SIZE (1800 + \ + SKB_DATA_ALIGN(sizeof(struct skb_shared_info))) + +int mt76_npu_fill_rx_queue(struct mt76_dev *dev, struct mt76_queue *q) +{ + int nframes = 0; + + while (q->queued < q->ndesc - 1) { + struct airoha_npu_rx_dma_desc *desc = (void *)q->desc; + struct mt76_queue_entry *e = &q->entry[q->head]; + struct page *page; + int offset; + + e->buf = mt76_get_page_pool_buf(q, &offset, q->buf_size); + if (!e->buf) + break; + + e->dma_len[0] = SKB_WITH_OVERHEAD(q->buf_size); + page = virt_to_head_page(e->buf); + e->dma_addr[0] = page_pool_get_dma_addr(page) + offset; + + memset(&desc[q->head], 0, sizeof(*desc)); + desc[q->head].addr = e->dma_addr[0]; + + q->head = (q->head + 1) % q->ndesc; + q->queued++; + nframes++; + } + + return nframes; +} + +void mt76_npu_queue_cleanup(struct mt76_dev *dev, struct mt76_queue *q) +{ + spin_lock_bh(&q->lock); + while (q->queued > 0) { + struct mt76_queue_entry *e = &q->entry[q->tail]; + + dma_sync_single_for_cpu(dev->dma_dev, e->dma_addr[0], + e->dma_len[0], + page_pool_get_dma_dir(q->page_pool)); + mt76_put_page_pool_buf(e->buf, false); + q->tail = (q->tail + 1) % q->ndesc; + q->queued--; + } + spin_unlock_bh(&q->lock); +} + +static struct sk_buff *mt76_npu_dequeue(struct mt76_dev *dev, + struct mt76_queue *q, + u32 *info) +{ + struct airoha_npu_rx_dma_desc *desc = (void *)q->desc; + int i, nframes, index = q->tail; + struct sk_buff *skb = NULL; + + nframes = FIELD_GET(NPU_RX_DMA_PKT_COUNT_MASK, desc[index].info); + nframes = max_t(int, nframes, 1); + + for (i = 0; i < nframes; i++) { + struct mt76_queue_entry *e = &q->entry[index]; + int len = FIELD_GET(NPU_RX_DMA_DESC_CUR_LEN_MASK, + desc[index].ctrl); + + if (!FIELD_GET(NPU_RX_DMA_DESC_DONE_MASK, desc[index].ctrl)) { + dev_kfree_skb(skb); + return NULL; + } + + dma_sync_single_for_cpu(dev->dma_dev, e->dma_addr[0], + e->dma_len[0], + page_pool_get_dma_dir(q->page_pool)); + + if (!skb) { + skb = napi_build_skb(e->buf, q->buf_size); + if (!skb) + return NULL; + + __skb_put(skb, len); + skb_reset_mac_header(skb); + skb_mark_for_recycle(skb); + } else { + struct skb_shared_info *shinfo = skb_shinfo(skb); + struct page *page = virt_to_head_page(e->buf); + int nr_frags = shinfo->nr_frags; + + if (nr_frags < ARRAY_SIZE(shinfo->frags)) + skb_add_rx_frag(skb, nr_frags, page, + e->buf - page_address(page), + len, q->buf_size); + } + + *info = desc[index].info; + index = (index + 1) % q->ndesc; + } + q->tail = index; + q->queued -= i; + Q_WRITE(q, dma_idx, q->tail); + + return skb; +} + +void mt76_npu_check_ppe(struct mt76_dev *dev, struct sk_buff *skb, + u32 info) +{ + struct airoha_ppe_dev *ppe_dev; + u16 reason, hash; + + if (!mt76_npu_device_active(dev)) + return; + + rcu_read_lock(); + + ppe_dev = rcu_dereference(dev->mmio.ppe_dev); + if (!ppe_dev) + goto out; + + hash = FIELD_GET(NPU_RX_DMA_FOE_ID_MASK, info); + skb_set_hash(skb, hash, PKT_HASH_TYPE_L4); + + reason = FIELD_GET(NPU_RX_DMA_CRSN_MASK, info); + if (reason == PPE_CPU_REASON_HIT_UNBIND_RATE_REACHED) { + skb_set_mac_header(skb, 0); + airoha_ppe_dev_check_skb(ppe_dev, skb, hash, true); + } +out: + rcu_read_unlock(); +} +EXPORT_SYMBOL_GPL(mt76_npu_check_ppe); + +static int mt76_npu_rx_poll(struct napi_struct *napi, int budget) +{ + struct mt76_dev *dev = mt76_priv(napi->dev); + enum mt76_rxq_id qid = napi - dev->napi; + struct airoha_npu *npu; + int done = 0; + + rcu_read_lock(); + + npu = rcu_dereference(dev->mmio.npu); + if (!npu) + goto out; + + while (done < budget) { + struct sk_buff *skb; + u32 info = 0; + + skb = mt76_npu_dequeue(dev, &dev->q_rx[qid], &info); + if (!skb) + break; + + dev->drv->rx_skb(dev, qid, skb, &info); + mt76_rx_poll_complete(dev, qid, napi); + done++; + } + + mt76_npu_fill_rx_queue(dev, &dev->q_rx[qid]); +out: + if (done < budget && napi_complete(napi)) + dev->drv->rx_poll_complete(dev, qid); + + rcu_read_unlock(); + + return done; +} + +static irqreturn_t mt76_npu_irq_handler(int irq, void *q_instance) +{ + struct mt76_queue *q = q_instance; + struct mt76_dev *dev = q->dev; + int qid = q - &dev->q_rx[0]; + int index = qid - MT_RXQ_NPU0; + struct airoha_npu *npu; + u32 status; + + rcu_read_lock(); + + npu = rcu_dereference(dev->mmio.npu); + if (!npu) + goto out; + + status = airoha_npu_wlan_get_irq_status(npu, index); + airoha_npu_wlan_set_irq_status(npu, status); + + airoha_npu_wlan_disable_irq(npu, index); + napi_schedule(&dev->napi[qid]); +out: + rcu_read_unlock(); + + return IRQ_HANDLED; +} + +int mt76_npu_dma_add_buf(struct mt76_phy *phy, struct mt76_queue *q, + struct sk_buff *skb, struct mt76_queue_buf *buf, + void *txwi_ptr) +{ + u16 txwi_len = min_t(u16, phy->dev->drv->txwi_size, NPU_TXWI_LEN); + struct airoha_npu_tx_dma_desc *desc = (void *)q->desc; + int ret; + + /* TODO: Take into account unlinear skbs */ + memcpy(desc[q->head].txwi, txwi_ptr, txwi_len); + desc[q->head].addr = buf->addr; + desc[q->head].ctrl = FIELD_PREP(NPU_TX_DMA_DESC_VEND_LEN_MASK, txwi_len) | + FIELD_PREP(NPU_TX_DMA_DESC_LEN_MASK, skb->len) | + NPU_TX_DMA_DESC_DONE_MASK; + + ret = q->head; + q->entry[q->head].skip_buf0 = true; + q->entry[q->head].skip_buf1 = true; + q->entry[q->head].txwi = NULL; + q->entry[q->head].skb = NULL; + q->entry[q->head].wcid = 0xffff; + + q->head = (q->head + 1) % q->ndesc; + q->queued++; + + return ret; +} + +void mt76_npu_txdesc_cleanup(struct mt76_queue *q, int index) +{ + struct airoha_npu_tx_dma_desc *desc = (void *)q->desc; + + if (!mt76_queue_is_npu_tx(q)) + return; + + desc[index].ctrl &= ~NPU_TX_DMA_DESC_DONE_MASK; +} + +void mt76_npu_queue_setup(struct mt76_dev *dev, struct mt76_queue *q) +{ + int qid = FIELD_GET(MT_QFLAG_WED_RING, q->flags); + bool xmit = mt76_queue_is_npu_tx(q); + struct airoha_npu *npu; + + if (!mt76_queue_is_npu(q)) + return; + + npu = rcu_dereference_protected(dev->mmio.npu, &dev->mutex); + if (npu) + q->wed_regs = airoha_npu_wlan_get_queue_addr(npu, qid, xmit); +} + +int mt76_npu_rx_queue_init(struct mt76_dev *dev, struct mt76_queue *q) +{ + int err, irq, qid = q - &dev->q_rx[0]; + int size, index = qid - MT_RXQ_NPU0; + struct airoha_npu *npu; + const char *name; + + mutex_lock(&dev->mutex); + + npu = rcu_dereference_protected(dev->mmio.npu, &dev->mutex); + irq = npu && index < ARRAY_SIZE(npu->irqs) ? npu->irqs[index] + : -EINVAL; + if (irq < 0) { + err = irq; + goto out; + } + + q->flags = MT_NPU_Q_RX(index); + size = qid == MT_RXQ_NPU1 ? NPU_RX1_DESC_NUM : NPU_RX0_DESC_NUM; + err = dev->queue_ops->alloc(dev, q, 0, size, + MT76_NPU_RX_BUF_SIZE, 0); + if (err) + goto out; + + name = devm_kasprintf(dev->dev, GFP_KERNEL, "mt76-npu.%d", index); + if (!name) { + err = -ENOMEM; + goto out; + } + + err = devm_request_irq(dev->dev, irq, mt76_npu_irq_handler, + IRQF_SHARED, name, q); + if (err) + goto out; + + netif_napi_add(dev->napi_dev, &dev->napi[qid], mt76_npu_rx_poll); + mt76_npu_fill_rx_queue(dev, q); + napi_enable(&dev->napi[qid]); +out: + mutex_unlock(&dev->mutex); + + return err; +} +EXPORT_SYMBOL_GPL(mt76_npu_rx_queue_init); + +static int mt76_npu_setup_tc_block_cb(enum tc_setup_type type, + void *type_data, void *cb_priv) +{ + struct mt76_phy *phy = cb_priv; + struct mt76_dev *dev = phy->dev; + struct airoha_ppe_dev *ppe_dev; + int err = -EOPNOTSUPP; + + if (type != TC_SETUP_CLSFLOWER) + return -EOPNOTSUPP; + + mutex_lock(&dev->mutex); + + ppe_dev = rcu_dereference_protected(dev->mmio.ppe_dev, &dev->mutex); + if (ppe_dev) + err = airoha_ppe_dev_setup_tc_block_cb(ppe_dev, type_data); + + mutex_unlock(&dev->mutex); + + return err; +} + +static int mt76_npu_setup_tc_block(struct mt76_phy *phy, + struct net_device *dev, + struct flow_block_offload *f) +{ + flow_setup_cb_t *cb = mt76_npu_setup_tc_block_cb; + static LIST_HEAD(block_cb_list); + struct flow_block_cb *block_cb; + + if (f->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS) + return -EOPNOTSUPP; + + if (!tc_can_offload(dev)) + return -EOPNOTSUPP; + + f->driver_block_list = &block_cb_list; + switch (f->command) { + case FLOW_BLOCK_BIND: + block_cb = flow_block_cb_lookup(f->block, cb, dev); + if (block_cb) { + flow_block_cb_incref(block_cb); + return 0; + } + + block_cb = flow_block_cb_alloc(cb, dev, phy, NULL); + if (IS_ERR(block_cb)) + return PTR_ERR(block_cb); + + flow_block_cb_incref(block_cb); + flow_block_cb_add(block_cb, f); + list_add_tail(&block_cb->driver_list, &block_cb_list); + return 0; + case FLOW_BLOCK_UNBIND: + block_cb = flow_block_cb_lookup(f->block, cb, dev); + if (!block_cb) + return -ENOENT; + + if (!flow_block_cb_decref(block_cb)) { + flow_block_cb_remove(block_cb, f); + list_del(&block_cb->driver_list); + } + return 0; + default: + return -EOPNOTSUPP; + } +} + +int mt76_npu_net_setup_tc(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct net_device *dev, enum tc_setup_type type, + void *type_data) +{ + struct mt76_phy *phy = hw->priv; + + if (!tc_can_offload(dev)) + return -EOPNOTSUPP; + + if (!mt76_npu_device_active(phy->dev)) + return -EOPNOTSUPP; + + switch (type) { + case TC_SETUP_BLOCK: + case TC_SETUP_FT: + return mt76_npu_setup_tc_block(phy, dev, type_data); + default: + return -EOPNOTSUPP; + } +} +EXPORT_SYMBOL_GPL(mt76_npu_net_setup_tc); + +int mt76_npu_send_txrx_addr(struct mt76_dev *dev, int ifindex, + u32 direction, u32 i_count_addr, + u32 o_status_addr, u32 o_count_addr) +{ + struct { + __le32 dir; + __le32 in_count_addr; + __le32 out_status_addr; + __le32 out_count_addr; + } info = { + .dir = cpu_to_le32(direction), + .in_count_addr = cpu_to_le32(i_count_addr), + .out_status_addr = cpu_to_le32(o_status_addr), + .out_count_addr = cpu_to_le32(o_count_addr), + }; + struct airoha_npu *npu; + int err = -ENODEV; + + rcu_read_lock(); + npu = rcu_dereference(dev->mmio.npu); + if (npu) + err = airoha_npu_wlan_send_msg(npu, ifindex, + WLAN_FUNC_SET_WAIT_INODE_TXRX_REG_ADDR, + &info, sizeof(info), GFP_ATOMIC); + rcu_read_unlock(); + + return err; +} +EXPORT_SYMBOL_GPL(mt76_npu_send_txrx_addr); + +void mt76_npu_disable_irqs(struct mt76_dev *dev) +{ + struct airoha_npu *npu; + int i; + + rcu_read_lock(); + + npu = rcu_dereference(dev->mmio.npu); + if (!npu) + goto unlock; + + for (i = MT_RXQ_NPU0; i <= MT_RXQ_NPU1; i++) { + int qid = i - MT_RXQ_NPU0; + u32 status; + + status = airoha_npu_wlan_get_irq_status(npu, qid); + airoha_npu_wlan_set_irq_status(npu, status); + airoha_npu_wlan_disable_irq(npu, qid); + } +unlock: + rcu_read_unlock(); +} +EXPORT_SYMBOL_GPL(mt76_npu_disable_irqs); + +int mt76_npu_init(struct mt76_dev *dev, phys_addr_t phy_addr, int type) +{ + struct airoha_ppe_dev *ppe_dev; + struct airoha_npu *npu; + int err = 0; + + mutex_lock(&dev->mutex); + + npu = airoha_npu_get(dev->dev); + if (IS_ERR(npu)) { + request_module("airoha-npu"); + npu = airoha_npu_get(dev->dev); + } + + if (IS_ERR(npu)) { + err = PTR_ERR(npu); + goto error_unlock; + } + + ppe_dev = airoha_ppe_get_dev(dev->dev); + if (IS_ERR(ppe_dev)) { + request_module("airoha-eth"); + ppe_dev = airoha_ppe_get_dev(dev->dev); + } + + if (IS_ERR(ppe_dev)) { + err = PTR_ERR(ppe_dev); + goto error_npu_put; + } + + err = airoha_npu_wlan_init_reserved_memory(npu); + if (err) + goto error_ppe_put; + + dev->dma_dev = npu->dev; + dev->mmio.phy_addr = phy_addr; + dev->mmio.npu_type = type; + /* NPU offloading requires HW-RRO for RX packet reordering. */ + dev->hwrro_mode = is_mt7996(dev) ? MT76_HWRRO_V3 : MT76_HWRRO_V3_1; + dev->rx_token_size = 32768; + + rcu_assign_pointer(dev->mmio.npu, npu); + rcu_assign_pointer(dev->mmio.ppe_dev, ppe_dev); + synchronize_rcu(); + + mutex_unlock(&dev->mutex); + + return 0; + +error_ppe_put: + airoha_ppe_put_dev(ppe_dev); +error_npu_put: + airoha_npu_put(npu); +error_unlock: + mutex_unlock(&dev->mutex); + + return err; +} +EXPORT_SYMBOL_GPL(mt76_npu_init); + +void mt76_npu_deinit(struct mt76_dev *dev) +{ + struct airoha_ppe_dev *ppe_dev; + struct airoha_npu *npu; + + mutex_lock(&dev->mutex); + + npu = rcu_replace_pointer(dev->mmio.npu, NULL, + lockdep_is_held(&dev->mutex)); + if (npu) + airoha_npu_put(npu); + + ppe_dev = rcu_replace_pointer(dev->mmio.ppe_dev, NULL, + lockdep_is_held(&dev->mutex)); + if (ppe_dev) + airoha_ppe_put_dev(ppe_dev); + + mutex_unlock(&dev->mutex); + + mt76_npu_queue_cleanup(dev, &dev->q_rx[MT_RXQ_NPU0]); + mt76_npu_queue_cleanup(dev, &dev->q_rx[MT_RXQ_NPU1]); +} diff --git a/drivers/net/wireless/mediatek/mt76/pci.c b/drivers/net/wireless/mediatek/mt76/pci.c new file mode 100644 index 000000000..11fe62aa8 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/pci.c @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (C) 2019 Lorenzo Bianconi + */ + +#include "mt76.h" +#include + +void mt76_pci_disable_aspm(struct pci_dev *pdev) +{ + struct pci_dev *parent = pdev->bus->self; + u16 aspm_conf, parent_aspm_conf = 0; + + pcie_capability_read_word(pdev, PCI_EXP_LNKCTL, &aspm_conf); + aspm_conf &= PCI_EXP_LNKCTL_ASPMC; + if (parent) { + pcie_capability_read_word(parent, PCI_EXP_LNKCTL, + &parent_aspm_conf); + parent_aspm_conf &= PCI_EXP_LNKCTL_ASPMC; + } + + if (!aspm_conf && (!parent || !parent_aspm_conf)) { + /* aspm already disabled */ + return; + } + + dev_info(&pdev->dev, "disabling ASPM %s %s\n", + (aspm_conf & PCI_EXP_LNKCTL_ASPM_L0S) ? "L0s" : "", + (aspm_conf & PCI_EXP_LNKCTL_ASPM_L1) ? "L1" : ""); + + if (IS_ENABLED(CONFIG_PCIEASPM)) { + int err; + int state = 0; + + if (aspm_conf & PCI_EXP_LNKCTL_ASPM_L0S) + state |= PCIE_LINK_STATE_L0S; + if (aspm_conf & PCI_EXP_LNKCTL_ASPM_L1) + state |= PCIE_LINK_STATE_L1; + + err = pci_disable_link_state(pdev, state); + if (!err) + return; + } + + /* both device and parent should have the same ASPM setting. + * disable ASPM in downstream component first and then upstream. + */ + pcie_capability_clear_word(pdev, PCI_EXP_LNKCTL, aspm_conf); + if (parent) + pcie_capability_clear_word(parent, PCI_EXP_LNKCTL, + aspm_conf); +} +EXPORT_SYMBOL_GPL(mt76_pci_disable_aspm); + +bool mt76_pci_aspm_supported(struct pci_dev *pdev) +{ + struct pci_dev *parent = pdev->bus->self; + u16 aspm_conf, parent_aspm_conf = 0; + bool result = true; + + pcie_capability_read_word(pdev, PCI_EXP_LNKCTL, &aspm_conf); + aspm_conf &= PCI_EXP_LNKCTL_ASPMC; + if (parent) { + pcie_capability_read_word(parent, PCI_EXP_LNKCTL, + &parent_aspm_conf); + parent_aspm_conf &= PCI_EXP_LNKCTL_ASPMC; + } + + if (!aspm_conf && (!parent || !parent_aspm_conf)) { + /* aspm already disabled */ + result = false; + } + + return result; +} +EXPORT_SYMBOL_GPL(mt76_pci_aspm_supported); diff --git a/drivers/net/wireless/mediatek/mt76/scan.c b/drivers/net/wireless/mediatek/mt76/scan.c new file mode 100644 index 000000000..3594b5996 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/scan.c @@ -0,0 +1,244 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (C) 2024 Felix Fietkau + */ +#include "mt76.h" + +static void mt76_scan_complete(struct mt76_dev *dev, bool abort) +{ + struct mt76_phy *phy = dev->scan.phy; + struct cfg80211_scan_info info = { + .aborted = abort, + }; + + lockdep_assert_held(&dev->mutex); + + if (!phy) + return; + + clear_bit(MT76_SCANNING, &phy->state); + + /* Re-program the operating channel even when the scan never left it: + * any channel set during the scan ran with MT76_SCANNING held, which + * left DFS radar detection disabled + */ + if (phy->main_chandef.chan && + !test_bit(MT76_MCU_RESET, &dev->phy.state)) { + bool offchannel = phy->offchannel; + + __mt76_set_channel(phy, &phy->main_chandef, false); + if (offchannel) + mt76_offchannel_notify(phy, false); + } + mt76_put_vif_phy_link(phy, dev->scan.vif, dev->scan.mlink); + memset(&dev->scan, 0, sizeof(dev->scan)); + if (!test_bit(MT76_MCU_RESET, &dev->phy.state)) + ieee80211_scan_completed(phy->hw, &info); +} + +void mt76_abort_scan(struct mt76_dev *dev) +{ + spin_lock_bh(&dev->scan_lock); + dev->scan.beacon_wait = false; + spin_unlock_bh(&dev->scan_lock); + + cancel_delayed_work_sync(&dev->scan_work); + + mutex_lock(&dev->mutex); + mt76_scan_complete(dev, true); + mutex_unlock(&dev->mutex); +} +EXPORT_SYMBOL_GPL(mt76_abort_scan); + +static void +mt76_scan_send_probe(struct mt76_dev *dev, struct cfg80211_ssid *ssid) +{ + struct cfg80211_scan_request *req = dev->scan.req; + struct ieee80211_vif *vif = dev->scan.vif; + struct mt76_vif_link *mvif = dev->scan.mlink; + enum nl80211_band band = dev->scan.chan->band; + struct mt76_phy *phy = dev->scan.phy; + struct ieee80211_tx_info *info; + struct sk_buff *skb; + u8 link_id; + + skb = ieee80211_probereq_get(phy->hw, vif->addr, ssid->ssid, + ssid->ssid_len, req->ie_len); + if (!skb) + return; + + if (is_unicast_ether_addr(req->bssid)) { + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + + ether_addr_copy(hdr->addr1, req->bssid); + ether_addr_copy(hdr->addr3, req->bssid); + } + + if (req->ie_len) + skb_put_data(skb, req->ie, req->ie_len); + + skb->priority = 7; + skb_set_queue_mapping(skb, IEEE80211_AC_VO); + + rcu_read_lock(); + + if (!ieee80211_tx_prepare_skb(phy->hw, vif, skb, band, NULL)) + goto out; + + info = IEEE80211_SKB_CB(skb); + if (req->no_cck) + info->flags |= IEEE80211_TX_CTL_NO_CCK_RATE; + info->control.flags |= IEEE80211_TX_CTRL_DONT_USE_RATE_MASK; + + link_id = mvif->wcid ? mvif->wcid->link_id : IEEE80211_LINK_UNSPECIFIED; + info->control.flags &= ~IEEE80211_TX_CTRL_MLO_LINK; + info->control.flags |= u32_encode_bits(link_id, IEEE80211_TX_CTRL_MLO_LINK); + + mt76_tx(phy, NULL, mvif->wcid, skb); + +out: + rcu_read_unlock(); +} + +void mt76_scan_rx_beacon(struct mt76_dev *dev, struct ieee80211_channel *chan) +{ + struct mt76_phy *phy; + + spin_lock(&dev->scan_lock); + + if (!dev->scan.beacon_wait || dev->scan.beacon_received || + dev->scan.chan != chan) + goto out; + + phy = dev->scan.phy; + if (!phy) + goto out; + + dev->scan.beacon_received = true; + ieee80211_queue_delayed_work(phy->hw, &dev->scan_work, 0); + +out: + spin_unlock(&dev->scan_lock); +} + +void mt76_scan_work(struct work_struct *work) +{ + struct mt76_dev *dev = container_of(work, struct mt76_dev, + scan_work.work); + struct cfg80211_scan_request *req = dev->scan.req; + struct cfg80211_chan_def chandef = {}; + struct mt76_phy *phy = dev->scan.phy; + int duration = HZ / 9; /* ~110 ms */ + bool beacon_rx, offchannel = true; + int i; + + if (!phy || !req) + return; + + spin_lock_bh(&dev->scan_lock); + beacon_rx = dev->scan.beacon_wait && dev->scan.beacon_received; + dev->scan.beacon_wait = false; + spin_unlock_bh(&dev->scan_lock); + + if (beacon_rx) + goto probe; + + if (dev->scan.chan_idx >= req->n_channels) { + mutex_lock(&dev->mutex); + mt76_scan_complete(dev, false); + mutex_unlock(&dev->mutex); + return; + } + + if (dev->scan.chan && phy->num_sta && phy->offchannel) { + dev->scan.chan = NULL; + mt76_set_channel(phy, &phy->main_chandef, false); + mt76_offchannel_notify(phy, false); + goto out; + } + + dev->scan.chan = req->channels[dev->scan.chan_idx++]; + offchannel = mt76_offchannel_chandef(phy, dev->scan.chan, &chandef); + + if (offchannel) + mt76_offchannel_notify(phy, true); + mt76_set_channel(phy, &chandef, offchannel); + + if (!req->n_ssids) + goto out; + + if (chandef.chan->flags & (IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_RADAR)) { + spin_lock_bh(&dev->scan_lock); + dev->scan.beacon_received = false; + dev->scan.beacon_wait = true; + spin_unlock_bh(&dev->scan_lock); + goto out; + } + +probe: + if (phy->offchannel) + duration = HZ / 16; /* ~60 ms */ + local_bh_disable(); + for (i = 0; i < req->n_ssids; i++) + mt76_scan_send_probe(dev, &req->ssids[i]); + local_bh_enable(); + +out: + if (dev->scan.chan && phy->offchannel) + duration = max_t(int, duration, + msecs_to_jiffies(req->duration + + (req->duration >> 5))); + + ieee80211_queue_delayed_work(dev->phy.hw, &dev->scan_work, duration); +} + +int mt76_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_scan_request *req) +{ + struct mt76_phy *phy = hw->priv; + struct mt76_dev *dev = phy->dev; + struct mt76_vif_link *mlink; + int ret = 0; + + if (hw->wiphy->n_radio > 1) { + phy = dev->band_phys[req->req.channels[0]->band]; + if (!phy) + return -EINVAL; + } + + mutex_lock(&dev->mutex); + + if (dev->scan.req || phy->roc_vif || + test_bit(MT76_MCU_RESET, &dev->phy.state)) { + ret = -EBUSY; + goto out; + } + + mlink = mt76_get_vif_phy_link(phy, vif); + if (IS_ERR(mlink)) { + ret = PTR_ERR(mlink); + goto out; + } + + memset(&dev->scan, 0, sizeof(dev->scan)); + dev->scan.req = &req->req; + dev->scan.vif = vif; + dev->scan.phy = phy; + dev->scan.mlink = mlink; + set_bit(MT76_SCANNING, &phy->state); + ieee80211_queue_delayed_work(dev->phy.hw, &dev->scan_work, 0); + +out: + mutex_unlock(&dev->mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(mt76_hw_scan); + +void mt76_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + struct mt76_phy *phy = hw->priv; + + mt76_abort_scan(phy->dev); +} +EXPORT_SYMBOL_GPL(mt76_cancel_hw_scan); diff --git a/drivers/net/wireless/mediatek/mt76/sdio.c b/drivers/net/wireless/mediatek/mt76/sdio.c new file mode 100644 index 000000000..ba5f123f7 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/sdio.c @@ -0,0 +1,690 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2020 MediaTek Inc. + * + * This file is written based on mt76/usb.c. + * + * Author: Felix Fietkau + * Lorenzo Bianconi + * Sean Wang + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mt76.h" +#include "sdio.h" + +static u32 mt76s_read_whisr(struct mt76_dev *dev) +{ + return sdio_readl(dev->sdio.func, MCR_WHISR, NULL); +} + +u32 mt76s_read_pcr(struct mt76_dev *dev) +{ + struct mt76_sdio *sdio = &dev->sdio; + + return sdio_readl(sdio->func, MCR_WHLPCR, NULL); +} +EXPORT_SYMBOL_GPL(mt76s_read_pcr); + +static u32 mt76s_read_mailbox(struct mt76_dev *dev, u32 offset) +{ + struct sdio_func *func = dev->sdio.func; + u32 val = ~0, status; + int err; + + sdio_claim_host(func); + + sdio_writel(func, offset, MCR_H2DSM0R, &err); + if (err < 0) { + dev_err(dev->dev, "failed setting address [err=%d]\n", err); + goto out; + } + + sdio_writel(func, H2D_SW_INT_READ, MCR_WSICR, &err); + if (err < 0) { + dev_err(dev->dev, "failed setting read mode [err=%d]\n", err); + goto out; + } + + err = readx_poll_timeout(mt76s_read_whisr, dev, status, + status & H2D_SW_INT_READ, 0, 1000000); + if (err < 0) { + dev_err(dev->dev, "query whisr timeout\n"); + goto out; + } + + sdio_writel(func, H2D_SW_INT_READ, MCR_WHISR, &err); + if (err < 0) { + dev_err(dev->dev, "failed setting read mode [err=%d]\n", err); + goto out; + } + + val = sdio_readl(func, MCR_H2DSM0R, &err); + if (err < 0) { + dev_err(dev->dev, "failed reading h2dsm0r [err=%d]\n", err); + goto out; + } + + if (val != offset) { + dev_err(dev->dev, "register mismatch\n"); + val = ~0; + goto out; + } + + val = sdio_readl(func, MCR_D2HRM1R, &err); + if (err < 0) + dev_err(dev->dev, "failed reading d2hrm1r [err=%d]\n", err); + +out: + sdio_release_host(func); + + return val; +} + +static void mt76s_write_mailbox(struct mt76_dev *dev, u32 offset, u32 val) +{ + struct sdio_func *func = dev->sdio.func; + u32 status; + int err; + + sdio_claim_host(func); + + sdio_writel(func, offset, MCR_H2DSM0R, &err); + if (err < 0) { + dev_err(dev->dev, "failed setting address [err=%d]\n", err); + goto out; + } + + sdio_writel(func, val, MCR_H2DSM1R, &err); + if (err < 0) { + dev_err(dev->dev, + "failed setting write value [err=%d]\n", err); + goto out; + } + + sdio_writel(func, H2D_SW_INT_WRITE, MCR_WSICR, &err); + if (err < 0) { + dev_err(dev->dev, "failed setting write mode [err=%d]\n", err); + goto out; + } + + err = readx_poll_timeout(mt76s_read_whisr, dev, status, + status & H2D_SW_INT_WRITE, 0, 1000000); + if (err < 0) { + dev_err(dev->dev, "query whisr timeout\n"); + goto out; + } + + sdio_writel(func, H2D_SW_INT_WRITE, MCR_WHISR, &err); + if (err < 0) { + dev_err(dev->dev, "failed setting write mode [err=%d]\n", err); + goto out; + } + + val = sdio_readl(func, MCR_H2DSM0R, &err); + if (err < 0) { + dev_err(dev->dev, "failed reading h2dsm0r [err=%d]\n", err); + goto out; + } + + if (val != offset) + dev_err(dev->dev, "register mismatch\n"); + +out: + sdio_release_host(func); +} + +u32 mt76s_rr(struct mt76_dev *dev, u32 offset) +{ + if (test_bit(MT76_STATE_MCU_RUNNING, &dev->phy.state)) + return dev->mcu_ops->mcu_rr(dev, offset); + else + return mt76s_read_mailbox(dev, offset); +} +EXPORT_SYMBOL_GPL(mt76s_rr); + +void mt76s_wr(struct mt76_dev *dev, u32 offset, u32 val) +{ + if (test_bit(MT76_STATE_MCU_RUNNING, &dev->phy.state)) + dev->mcu_ops->mcu_wr(dev, offset, val); + else + mt76s_write_mailbox(dev, offset, val); +} +EXPORT_SYMBOL_GPL(mt76s_wr); + +u32 mt76s_rmw(struct mt76_dev *dev, u32 offset, u32 mask, u32 val) +{ + val |= mt76s_rr(dev, offset) & ~mask; + mt76s_wr(dev, offset, val); + + return val; +} +EXPORT_SYMBOL_GPL(mt76s_rmw); + +void mt76s_write_copy(struct mt76_dev *dev, u32 offset, + const void *data, int len) +{ + const u32 *val = data; + int i; + + for (i = 0; i < len / sizeof(u32); i++) { + mt76s_wr(dev, offset, val[i]); + offset += sizeof(u32); + } +} +EXPORT_SYMBOL_GPL(mt76s_write_copy); + +void mt76s_read_copy(struct mt76_dev *dev, u32 offset, + void *data, int len) +{ + u32 *val = data; + int i; + + for (i = 0; i < len / sizeof(u32); i++) { + val[i] = mt76s_rr(dev, offset); + offset += sizeof(u32); + } +} +EXPORT_SYMBOL_GPL(mt76s_read_copy); + +int mt76s_wr_rp(struct mt76_dev *dev, u32 base, + const struct mt76_reg_pair *data, + int len) +{ + int i; + + for (i = 0; i < len; i++) { + mt76s_wr(dev, data->reg, data->value); + data++; + } + + return 0; +} +EXPORT_SYMBOL_GPL(mt76s_wr_rp); + +int mt76s_rd_rp(struct mt76_dev *dev, u32 base, + struct mt76_reg_pair *data, int len) +{ + int i; + + for (i = 0; i < len; i++) { + data->value = mt76s_rr(dev, data->reg); + data++; + } + + return 0; +} +EXPORT_SYMBOL_GPL(mt76s_rd_rp); + +int mt76s_hw_init(struct mt76_dev *dev, struct sdio_func *func, int hw_ver) +{ + u32 status, ctrl; + int ret; + + dev->sdio.hw_ver = hw_ver; + + sdio_claim_host(func); + + ret = sdio_enable_func(func); + if (ret < 0) + goto release; + + /* Get ownership from the device */ + sdio_writel(func, WHLPCR_INT_EN_CLR | WHLPCR_FW_OWN_REQ_CLR, + MCR_WHLPCR, &ret); + if (ret < 0) + goto disable_func; + + ret = readx_poll_timeout(mt76s_read_pcr, dev, status, + status & WHLPCR_IS_DRIVER_OWN, 2000, 1000000); + if (ret < 0) { + dev_err(dev->dev, "Cannot get ownership from device"); + goto disable_func; + } + + ret = sdio_set_block_size(func, 512); + if (ret < 0) + goto disable_func; + + /* Enable interrupt */ + sdio_writel(func, WHLPCR_INT_EN_SET, MCR_WHLPCR, &ret); + if (ret < 0) + goto disable_func; + + ctrl = WHIER_RX0_DONE_INT_EN | WHIER_TX_DONE_INT_EN; + if (hw_ver == MT76_CONNAC2_SDIO) + ctrl |= WHIER_RX1_DONE_INT_EN; + sdio_writel(func, ctrl, MCR_WHIER, &ret); + if (ret < 0) + goto disable_func; + + switch (hw_ver) { + case MT76_CONNAC_SDIO: + /* set WHISR as read clear and Rx aggregation number as 16 */ + ctrl = FIELD_PREP(MAX_HIF_RX_LEN_NUM, 16); + break; + default: + ctrl = sdio_readl(func, MCR_WHCR, &ret); + if (ret < 0) + goto disable_func; + ctrl &= ~MAX_HIF_RX_LEN_NUM_CONNAC2; + ctrl &= ~W_INT_CLR_CTRL; /* read clear */ + ctrl |= FIELD_PREP(MAX_HIF_RX_LEN_NUM_CONNAC2, 0); + break; + } + + sdio_writel(func, ctrl, MCR_WHCR, &ret); + if (ret < 0) + goto disable_func; + + ret = sdio_claim_irq(func, mt76s_sdio_irq); + if (ret < 0) + goto disable_func; + + sdio_release_host(func); + + return 0; + +disable_func: + sdio_disable_func(func); +release: + sdio_release_host(func); + + return ret; +} +EXPORT_SYMBOL_GPL(mt76s_hw_init); + +int mt76s_alloc_rx_queue(struct mt76_dev *dev, enum mt76_rxq_id qid) +{ + struct mt76_queue *q = &dev->q_rx[qid]; + + spin_lock_init(&q->lock); + q->entry = devm_kcalloc(dev->dev, + MT76S_NUM_RX_ENTRIES, sizeof(*q->entry), + GFP_KERNEL); + if (!q->entry) + return -ENOMEM; + + q->ndesc = MT76S_NUM_RX_ENTRIES; + q->head = q->tail = 0; + q->queued = 0; + + return 0; +} +EXPORT_SYMBOL_GPL(mt76s_alloc_rx_queue); + +static struct mt76_queue *mt76s_alloc_tx_queue(struct mt76_dev *dev) +{ + struct mt76_queue *q; + + q = devm_kzalloc(dev->dev, sizeof(*q), GFP_KERNEL); + if (!q) + return ERR_PTR(-ENOMEM); + + spin_lock_init(&q->lock); + q->entry = devm_kcalloc(dev->dev, + MT76S_NUM_TX_ENTRIES, sizeof(*q->entry), + GFP_KERNEL); + if (!q->entry) + return ERR_PTR(-ENOMEM); + + q->ndesc = MT76S_NUM_TX_ENTRIES; + + return q; +} + +int mt76s_alloc_tx(struct mt76_dev *dev) +{ + struct mt76_queue *q; + int i; + + for (i = 0; i <= MT_TXQ_PSD; i++) { + q = mt76s_alloc_tx_queue(dev); + if (IS_ERR(q)) + return PTR_ERR(q); + + dev->phy.q_tx[i] = q; + } + + q = mt76s_alloc_tx_queue(dev); + if (IS_ERR(q)) + return PTR_ERR(q); + + dev->q_mcu[MT_MCUQ_WM] = q; + + return 0; +} +EXPORT_SYMBOL_GPL(mt76s_alloc_tx); + +static struct mt76_queue_entry * +mt76s_get_next_rx_entry(struct mt76_queue *q) +{ + struct mt76_queue_entry *e = NULL; + + spin_lock_bh(&q->lock); + if (q->queued > 0) { + e = &q->entry[q->tail]; + q->tail = (q->tail + 1) % q->ndesc; + q->queued--; + } + spin_unlock_bh(&q->lock); + + return e; +} + +static int +mt76s_process_rx_queue(struct mt76_dev *dev, struct mt76_queue *q) +{ + int qid = q - &dev->q_rx[MT_RXQ_MAIN]; + int nframes = 0; + + while (true) { + struct mt76_queue_entry *e; + + if (!test_bit(MT76_STATE_INITIALIZED, &dev->phy.state)) + break; + + e = mt76s_get_next_rx_entry(q); + if (!e || !e->skb) + break; + + dev->drv->rx_skb(dev, MT_RXQ_MAIN, e->skb, NULL); + e->skb = NULL; + nframes++; + } + if (qid == MT_RXQ_MAIN) + mt76_rx_poll_complete(dev, MT_RXQ_MAIN, NULL); + + return nframes; +} + +static void mt76s_net_worker(struct mt76_worker *w) +{ + struct mt76_sdio *sdio = container_of(w, struct mt76_sdio, + net_worker); + struct mt76_dev *dev = container_of(sdio, struct mt76_dev, sdio); + int i, nframes; + + do { + nframes = 0; + + local_bh_disable(); + rcu_read_lock(); + + mt76_for_each_q_rx(dev, i) + nframes += mt76s_process_rx_queue(dev, &dev->q_rx[i]); + + rcu_read_unlock(); + local_bh_enable(); + } while (nframes > 0); +} + +static int mt76s_process_tx_queue(struct mt76_dev *dev, struct mt76_queue *q) +{ + struct mt76_queue_entry entry; + int nframes = 0; + bool mcu; + + if (!q) + return 0; + + mcu = q == dev->q_mcu[MT_MCUQ_WM]; + while (q->queued > 0) { + if (!q->entry[q->tail].done) + break; + + entry = q->entry[q->tail]; + q->entry[q->tail].done = false; + + if (mcu) { + dev_kfree_skb(entry.skb); + entry.skb = NULL; + } + + mt76_queue_tx_complete(dev, q, &entry); + nframes++; + } + + if (!q->queued) + wake_up(&dev->tx_wait); + + return nframes; +} + +static void mt76s_status_worker(struct mt76_worker *w) +{ + struct mt76_sdio *sdio = container_of(w, struct mt76_sdio, + status_worker); + struct mt76_dev *dev = container_of(sdio, struct mt76_dev, sdio); + bool resched = false; + int i, nframes; + + do { + int ndata_frames = 0; + + nframes = mt76s_process_tx_queue(dev, dev->q_mcu[MT_MCUQ_WM]); + + for (i = 0; i <= MT_TXQ_PSD; i++) + ndata_frames += mt76s_process_tx_queue(dev, + dev->phy.q_tx[i]); + nframes += ndata_frames; + if (ndata_frames > 0) + resched = true; + + if (dev->drv->tx_status_data && ndata_frames > 0 && + !test_and_set_bit(MT76_READING_STATS, &dev->phy.state) && + !test_bit(MT76_STATE_SUSPEND, &dev->phy.state)) + mt76_worker_schedule(&sdio->stat_worker); + } while (nframes > 0); + + if (resched) + mt76_worker_schedule(&dev->tx_worker); +} + +static void mt76s_tx_status_data(struct mt76_worker *worker) +{ + struct mt76_sdio *sdio; + struct mt76_dev *dev; + u8 update = 1; + u16 count = 0; + + sdio = container_of(worker, struct mt76_sdio, stat_worker); + dev = container_of(sdio, struct mt76_dev, sdio); + + while (true) { + if (test_bit(MT76_RESET, &dev->phy.state) || + test_bit(MT76_REMOVED, &dev->phy.state)) + break; + + if (!dev->drv->tx_status_data(dev, &update)) + break; + count++; + } + + if (count && test_bit(MT76_STATE_RUNNING, &dev->phy.state)) + mt76_worker_schedule(&sdio->status_worker); + else + clear_bit(MT76_READING_STATS, &dev->phy.state); +} + +static int +mt76s_tx_queue_skb(struct mt76_phy *phy, struct mt76_queue *q, + enum mt76_txq_id qid, struct sk_buff *skb, + struct mt76_wcid *wcid, struct ieee80211_sta *sta) +{ + struct ieee80211_tx_status status = { + .sta = sta, + }; + + struct mt76_tx_info tx_info = { + .skb = skb, + }; + struct mt76_dev *dev = phy->dev; + int err, len = skb->len; + u16 idx = q->head; + + if (q->queued == q->ndesc) + return -ENOSPC; + + skb->prev = skb->next = NULL; + err = dev->drv->tx_prepare_skb(dev, NULL, qid, wcid, sta, &tx_info); + if (err < 0) { + status.skb = tx_info.skb; + spin_lock_bh(&dev->rx_lock); + ieee80211_tx_status_ext(dev->hw, &status); + spin_unlock_bh(&dev->rx_lock); + return err; + } + + q->entry[q->head].skb = tx_info.skb; + q->entry[q->head].buf_sz = len; + q->entry[q->head].wcid = 0xffff; + + smp_wmb(); + + q->head = (q->head + 1) % q->ndesc; + q->queued++; + + return idx; +} + +static int +mt76s_tx_queue_skb_raw(struct mt76_dev *dev, struct mt76_queue *q, + struct sk_buff *skb, u32 tx_info) +{ + int ret, len = skb->len, pad; + + pad = round_up(skb->len, 4) - skb->len; + ret = mt76_skb_adjust_pad(skb, pad); + if (ret) + goto error; + + spin_lock_bh(&q->lock); + + if (q->queued == q->ndesc) { + ret = -ENOSPC; + spin_unlock_bh(&q->lock); + goto error; + } + + q->entry[q->head].buf_sz = len; + q->entry[q->head].skb = skb; + + /* ensure the entry fully updated before bus access */ + smp_wmb(); + + q->head = (q->head + 1) % q->ndesc; + q->queued++; + + spin_unlock_bh(&q->lock); + + return 0; + +error: + dev_kfree_skb(skb); + + return ret; +} + +static void mt76s_tx_kick(struct mt76_dev *dev, struct mt76_queue *q) +{ + struct mt76_sdio *sdio = &dev->sdio; + + mt76_worker_schedule(&sdio->txrx_worker); +} + +static const struct mt76_queue_ops sdio_queue_ops = { + .tx_queue_skb = mt76s_tx_queue_skb, + .kick = mt76s_tx_kick, + .tx_queue_skb_raw = mt76s_tx_queue_skb_raw, +}; + +void mt76s_deinit(struct mt76_dev *dev) +{ + struct mt76_sdio *sdio = &dev->sdio; + int i; + + mt76_worker_teardown(&sdio->txrx_worker); + mt76_worker_teardown(&sdio->status_worker); + mt76_worker_teardown(&sdio->net_worker); + mt76_worker_teardown(&sdio->stat_worker); + + clear_bit(MT76_READING_STATS, &dev->phy.state); + + mt76_tx_status_check(dev, true); + + sdio_claim_host(sdio->func); + sdio_release_irq(sdio->func); + sdio_release_host(sdio->func); + + mt76_for_each_q_rx(dev, i) { + struct mt76_queue *q = &dev->q_rx[i]; + int j; + + for (j = 0; j < q->ndesc; j++) { + struct mt76_queue_entry *e = &q->entry[j]; + + if (!e->skb) + continue; + + dev_kfree_skb(e->skb); + e->skb = NULL; + } + } +} +EXPORT_SYMBOL_GPL(mt76s_deinit); + +int mt76s_init(struct mt76_dev *dev, struct sdio_func *func, + const struct mt76_bus_ops *bus_ops) +{ + struct mt76_sdio *sdio = &dev->sdio; + u32 host_max_cap; + int err; + + err = mt76_worker_setup(dev->hw, &sdio->status_worker, + mt76s_status_worker, "sdio-status"); + if (err) + return err; + + err = mt76_worker_setup(dev->hw, &sdio->net_worker, mt76s_net_worker, + "sdio-net"); + if (err) + return err; + + err = mt76_worker_setup(dev->hw, &sdio->stat_worker, mt76s_tx_status_data, + "sdio-sta"); + if (err) + return err; + + sched_set_fifo_low(sdio->status_worker.task); + sched_set_fifo_low(sdio->net_worker.task); + sched_set_fifo_low(sdio->stat_worker.task); + + dev->queue_ops = &sdio_queue_ops; + dev->bus = bus_ops; + dev->sdio.func = func; + + host_max_cap = min_t(u32, func->card->host->max_req_size, + func->cur_blksize * + func->card->host->max_blk_count); + dev->sdio.xmit_buf_sz = min_t(u32, host_max_cap, MT76S_XMIT_BUF_SZ); + dev->sdio.xmit_buf = devm_kmalloc(dev->dev, dev->sdio.xmit_buf_sz, + GFP_KERNEL); + if (!dev->sdio.xmit_buf) + err = -ENOMEM; + + return err; +} +EXPORT_SYMBOL_GPL(mt76s_init); + +MODULE_AUTHOR("Sean Wang "); +MODULE_AUTHOR("Lorenzo Bianconi "); +MODULE_DESCRIPTION("MediaTek MT76x SDIO helpers"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/mediatek/mt76/sdio.h b/drivers/net/wireless/mediatek/mt76/sdio.h new file mode 100644 index 000000000..41b89f3de --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/sdio.h @@ -0,0 +1,140 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* Copyright (C) 2020 MediaTek Inc. + * + * Author: Sean Wang + */ + +#ifndef __MT76S_H +#define __MT76S_H + +#define MT_PSE_PAGE_SZ 128 + +#define MCR_WCIR 0x0000 +#define MCR_WHLPCR 0x0004 +#define WHLPCR_FW_OWN_REQ_CLR BIT(9) +#define WHLPCR_FW_OWN_REQ_SET BIT(8) +#define WHLPCR_IS_DRIVER_OWN BIT(8) +#define WHLPCR_INT_EN_CLR BIT(1) +#define WHLPCR_INT_EN_SET BIT(0) + +#define MCR_WSDIOCSR 0x0008 +#define MCR_WHCR 0x000C +#define W_INT_CLR_CTRL BIT(1) +#define RECV_MAILBOX_RD_CLR_EN BIT(2) +#define WF_SYS_RSTB BIT(4) /* supported in CONNAC2 */ +#define WF_WHOLE_PATH_RSTB BIT(5) /* supported in CONNAC2 */ +#define WF_SDIO_WF_PATH_RSTB BIT(6) /* supported in CONNAC2 */ +#define MAX_HIF_RX_LEN_NUM GENMASK(13, 8) +#define MAX_HIF_RX_LEN_NUM_CONNAC2 GENMASK(14, 8) /* supported in CONNAC2 */ +#define WF_RST_DONE BIT(15) /* supported in CONNAC2 */ +#define RX_ENHANCE_MODE BIT(16) + +#define MCR_WHISR 0x0010 +#define MCR_WHIER 0x0014 +#define WHIER_D2H_SW_INT GENMASK(31, 8) +#define WHIER_FW_OWN_BACK_INT_EN BIT(7) +#define WHIER_ABNORMAL_INT_EN BIT(6) +#define WHIER_WDT_INT_EN BIT(5) /* supported in CONNAC2 */ +#define WHIER_RX1_DONE_INT_EN BIT(2) +#define WHIER_RX0_DONE_INT_EN BIT(1) +#define WHIER_TX_DONE_INT_EN BIT(0) +#define WHIER_DEFAULT (WHIER_RX0_DONE_INT_EN | \ + WHIER_RX1_DONE_INT_EN | \ + WHIER_TX_DONE_INT_EN | \ + WHIER_ABNORMAL_INT_EN | \ + WHIER_D2H_SW_INT) + +#define MCR_WASR 0x0020 +#define MCR_WSICR 0x0024 +#define MCR_WTSR0 0x0028 +#define TQ0_CNT GENMASK(7, 0) +#define TQ1_CNT GENMASK(15, 8) +#define TQ2_CNT GENMASK(23, 16) +#define TQ3_CNT GENMASK(31, 24) + +#define MCR_WTSR1 0x002c +#define TQ4_CNT GENMASK(7, 0) +#define TQ5_CNT GENMASK(15, 8) +#define TQ6_CNT GENMASK(23, 16) +#define TQ7_CNT GENMASK(31, 24) + +#define MCR_WTDR1 0x0034 +#define MCR_WRDR0 0x0050 +#define MCR_WRDR1 0x0054 +#define MCR_WRDR(p) (0x0050 + 4 * (p)) +#define MCR_H2DSM0R 0x0070 +#define H2D_SW_INT_READ BIT(16) +#define H2D_SW_INT_WRITE BIT(17) +#define H2D_SW_INT_CLEAR_MAILBOX_ACK BIT(22) + +#define MCR_H2DSM1R 0x0074 +#define MCR_D2HRM0R 0x0078 +#define MCR_D2HRM1R 0x007c +#define MCR_D2HRM2R 0x0080 +#define MCR_WRPLR 0x0090 +#define RX0_PACKET_LENGTH GENMASK(15, 0) +#define RX1_PACKET_LENGTH GENMASK(31, 16) + +#define MCR_WTMDR 0x00b0 +#define MCR_WTMCR 0x00b4 +#define MCR_WTMDPCR0 0x00b8 +#define MCR_WTMDPCR1 0x00bc +#define MCR_WPLRCR 0x00d4 +#define MCR_WSR 0x00D8 +#define MCR_CLKIOCR 0x0100 +#define MCR_CMDIOCR 0x0104 +#define MCR_DAT0IOCR 0x0108 +#define MCR_DAT1IOCR 0x010C +#define MCR_DAT2IOCR 0x0110 +#define MCR_DAT3IOCR 0x0114 +#define MCR_CLKDLYCR 0x0118 +#define MCR_CMDDLYCR 0x011C +#define MCR_ODATDLYCR 0x0120 +#define MCR_IDATDLYCR1 0x0124 +#define MCR_IDATDLYCR2 0x0128 +#define MCR_ILCHCR 0x012C +#define MCR_WTQCR0 0x0130 +#define MCR_WTQCR1 0x0134 +#define MCR_WTQCR2 0x0138 +#define MCR_WTQCR3 0x013C +#define MCR_WTQCR4 0x0140 +#define MCR_WTQCR5 0x0144 +#define MCR_WTQCR6 0x0148 +#define MCR_WTQCR7 0x014C +#define MCR_WTQCR(x) (0x130 + 4 * (x)) +#define TXQ_CNT_L GENMASK(15, 0) +#define TXQ_CNT_H GENMASK(31, 16) + +#define MCR_SWPCDBGR 0x0154 + +#define MCR_H2DSM2R 0x0160 /* supported in CONNAC2 */ +#define MCR_H2DSM3R 0x0164 /* supported in CONNAC2 */ +#define MCR_D2HRM3R 0x0174 /* supported in CONNAC2 */ +#define D2HRM3R_IS_DRIVER_OWN BIT(0) +#define MCR_WTQCR8 0x0190 /* supported in CONNAC2 */ +#define MCR_WTQCR9 0x0194 /* supported in CONNAC2 */ +#define MCR_WTQCR10 0x0198 /* supported in CONNAC2 */ +#define MCR_WTQCR11 0x019C /* supported in CONNAC2 */ +#define MCR_WTQCR12 0x01A0 /* supported in CONNAC2 */ +#define MCR_WTQCR13 0x01A4 /* supported in CONNAC2 */ +#define MCR_WTQCR14 0x01A8 /* supported in CONNAC2 */ +#define MCR_WTQCR15 0x01AC /* supported in CONNAC2 */ + +enum mt76_connac_sdio_ver { + MT76_CONNAC_SDIO, + MT76_CONNAC2_SDIO, +}; + +struct mt76s_intr { + u32 isr; + u32 *rec_mb; + struct { + u32 *wtqcr; + } tx; + struct { + u16 *len[2]; + u16 *num; + } rx; +}; + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/sdio_txrx.c b/drivers/net/wireless/mediatek/mt76/sdio_txrx.c new file mode 100644 index 000000000..3f314e8e1 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/sdio_txrx.c @@ -0,0 +1,390 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2020 MediaTek Inc. + * + * Author: Felix Fietkau + * Lorenzo Bianconi + * Sean Wang + */ + +#include +#include +#include + +#include +#include +#include + +#include "trace.h" +#include "sdio.h" +#include "mt76.h" + +static int mt76s_refill_sched_quota(struct mt76_dev *dev, u32 *data) +{ + u32 ple_ac_data_quota[] = { + FIELD_GET(TXQ_CNT_L, data[4]), /* VO */ + FIELD_GET(TXQ_CNT_H, data[3]), /* VI */ + FIELD_GET(TXQ_CNT_L, data[3]), /* BE */ + FIELD_GET(TXQ_CNT_H, data[2]), /* BK */ + }; + u32 pse_ac_data_quota[] = { + FIELD_GET(TXQ_CNT_H, data[1]), /* VO */ + FIELD_GET(TXQ_CNT_L, data[1]), /* VI */ + FIELD_GET(TXQ_CNT_H, data[0]), /* BE */ + FIELD_GET(TXQ_CNT_L, data[0]), /* BK */ + }; + u32 pse_mcu_quota = FIELD_GET(TXQ_CNT_L, data[2]); + u32 pse_data_quota = 0, ple_data_quota = 0; + struct mt76_sdio *sdio = &dev->sdio; + int i; + + for (i = 0; i < ARRAY_SIZE(pse_ac_data_quota); i++) { + pse_data_quota += pse_ac_data_quota[i]; + ple_data_quota += ple_ac_data_quota[i]; + } + + if (!pse_data_quota && !ple_data_quota && !pse_mcu_quota) + return 0; + + sdio->sched.pse_mcu_quota += pse_mcu_quota; + if (sdio->pse_mcu_quota_max && + sdio->sched.pse_mcu_quota > sdio->pse_mcu_quota_max) { + sdio->sched.pse_mcu_quota = sdio->pse_mcu_quota_max; + } + sdio->sched.pse_data_quota += pse_data_quota; + sdio->sched.ple_data_quota += ple_data_quota; + + return pse_data_quota + ple_data_quota + pse_mcu_quota; +} + +static struct sk_buff * +mt76s_build_rx_skb(void *data, int data_len, int buf_len) +{ + int len = min_t(int, data_len, MT_SKB_HEAD_LEN); + struct sk_buff *skb; + + skb = alloc_skb(len, GFP_KERNEL); + if (!skb) + return NULL; + + skb_put_data(skb, data, len); + if (data_len > len) { + struct page *page; + + data += len; + page = virt_to_head_page(data); + skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, + page, data - page_address(page), + data_len - len, buf_len); + get_page(page); + } + + return skb; +} + +static int +mt76s_rx_run_queue(struct mt76_dev *dev, enum mt76_rxq_id qid, + struct mt76s_intr *intr) +{ + struct mt76_queue *q = &dev->q_rx[qid]; + struct mt76_sdio *sdio = &dev->sdio; + int len = 0, err, i; + struct page *page; + u8 *buf, *end; + + for (i = 0; i < intr->rx.num[qid]; i++) + len += round_up(intr->rx.len[qid][i] + 4, 4); + + if (!len) + return 0; + + if (len > sdio->func->cur_blksize) + len = roundup(len, sdio->func->cur_blksize); + + page = __dev_alloc_pages(GFP_KERNEL, get_order(len)); + if (!page) + return -ENOMEM; + + buf = page_address(page); + + sdio_claim_host(sdio->func); + err = sdio_readsb(sdio->func, buf, MCR_WRDR(qid), len); + sdio_release_host(sdio->func); + + if (err < 0) { + dev_err(dev->dev, "sdio read data failed:%d\n", err); + atomic_set(&dev->bus_hung, true); + put_page(page); + return err; + } + + end = buf + len; + i = 0; + + while (i < intr->rx.num[qid] && buf < end) { + int index = (q->head + i) % q->ndesc; + struct mt76_queue_entry *e = &q->entry[index]; + __le32 *rxd = (__le32 *)buf; + + /* parse rxd to get the actual packet length */ + len = le32_get_bits(rxd[0], GENMASK(15, 0)); + + /* Optimized path for TXS */ + if (!dev->drv->rx_check || dev->drv->rx_check(dev, buf, len)) { + e->skb = mt76s_build_rx_skb(buf, len, + round_up(len + 4, 4)); + if (!e->skb) + break; + + if (q->queued + i + 1 == q->ndesc) + break; + i++; + } + buf += round_up(len + 4, 4); + } + put_page(page); + + spin_lock_bh(&q->lock); + q->head = (q->head + i) % q->ndesc; + q->queued += i; + spin_unlock_bh(&q->lock); + + return i; +} + +static int mt76s_rx_handler(struct mt76_dev *dev) +{ + struct mt76_sdio *sdio = &dev->sdio; + struct mt76s_intr intr; + int nframes = 0, ret; + + ret = sdio->parse_irq(dev, &intr); + if (ret) + return ret; + + trace_dev_irq(dev, intr.isr, 0); + + if (intr.isr & WHIER_RX0_DONE_INT_EN) { + ret = mt76s_rx_run_queue(dev, 0, &intr); + if (ret > 0) { + mt76_worker_schedule(&sdio->net_worker); + nframes += ret; + } + } + + if (intr.isr & WHIER_RX1_DONE_INT_EN) { + ret = mt76s_rx_run_queue(dev, 1, &intr); + if (ret > 0) { + mt76_worker_schedule(&sdio->net_worker); + nframes += ret; + } + } + + nframes += !!mt76s_refill_sched_quota(dev, intr.tx.wtqcr); + + return nframes; +} + +static int +mt76s_tx_pick_quota(struct mt76_sdio *sdio, bool mcu, int buf_sz, + int *pse_size, int *ple_size) +{ + int pse_sz; + + pse_sz = DIV_ROUND_UP(buf_sz + sdio->sched.deficit, + sdio->sched.pse_page_size); + + if (mcu && sdio->hw_ver == MT76_CONNAC2_SDIO) + pse_sz = 1; + + if (mcu) { + if (sdio->sched.pse_mcu_quota < *pse_size + pse_sz) + return -EBUSY; + } else { + if (sdio->sched.pse_data_quota < *pse_size + pse_sz || + sdio->sched.ple_data_quota < *ple_size + 1) + return -EBUSY; + + *ple_size = *ple_size + 1; + } + *pse_size = *pse_size + pse_sz; + + return 0; +} + +static void +mt76s_tx_update_quota(struct mt76_sdio *sdio, bool mcu, int pse_size, + int ple_size) +{ + if (mcu) { + sdio->sched.pse_mcu_quota -= pse_size; + } else { + sdio->sched.pse_data_quota -= pse_size; + sdio->sched.ple_data_quota -= ple_size; + } +} + +static int __mt76s_xmit_queue(struct mt76_dev *dev, u8 *data, int len) +{ + struct mt76_sdio *sdio = &dev->sdio; + int err; + + if (len > sdio->func->cur_blksize) + len = roundup(len, sdio->func->cur_blksize); + + sdio_claim_host(sdio->func); + err = sdio_writesb(sdio->func, MCR_WTDR1, data, len); + sdio_release_host(sdio->func); + + if (err) { + dev_err(dev->dev, "sdio write failed: %d\n", err); + atomic_set(&dev->bus_hung, true); + } + return err; +} + +static int mt76s_tx_run_queue(struct mt76_dev *dev, struct mt76_queue *q) +{ + int err, nframes = 0, len = 0, pse_sz = 0, ple_sz = 0; + bool mcu = q == dev->q_mcu[MT_MCUQ_WM]; + struct mt76_sdio *sdio = &dev->sdio; + u8 pad; + + while (q->first != q->head) { + struct mt76_queue_entry *e = &q->entry[q->first]; + struct sk_buff *iter; + + smp_rmb(); + + if (test_bit(MT76_MCU_RESET, &dev->phy.state)) + goto next; + + if (!test_bit(MT76_STATE_MCU_RUNNING, &dev->phy.state)) { + __skb_put_zero(e->skb, 4); + err = __skb_grow(e->skb, roundup(e->skb->len, + sdio->func->cur_blksize)); + if (err) + return err; + err = __mt76s_xmit_queue(dev, e->skb->data, + e->skb->len); + if (err) + return err; + + goto next; + } + + pad = roundup(e->skb->len, 4) - e->skb->len; + if (len + e->skb->len + pad + 4 > dev->sdio.xmit_buf_sz) + break; + + if (mt76s_tx_pick_quota(sdio, mcu, e->buf_sz, &pse_sz, + &ple_sz)) + break; + + memcpy(sdio->xmit_buf + len, e->skb->data, skb_headlen(e->skb)); + len += skb_headlen(e->skb); + nframes++; + + skb_walk_frags(e->skb, iter) { + memcpy(sdio->xmit_buf + len, iter->data, iter->len); + len += iter->len; + nframes++; + } + + if (unlikely(pad)) { + memset(sdio->xmit_buf + len, 0, pad); + len += pad; + } +next: + q->first = (q->first + 1) % q->ndesc; + e->done = true; + } + + if (nframes) { + memset(sdio->xmit_buf + len, 0, 4); + err = __mt76s_xmit_queue(dev, sdio->xmit_buf, len + 4); + if (err) + return err; + } + mt76s_tx_update_quota(sdio, mcu, pse_sz, ple_sz); + + mt76_worker_schedule(&sdio->status_worker); + + return nframes; +} + +void mt76s_txrx_worker(struct mt76_sdio *sdio) +{ + struct mt76_dev *dev = container_of(sdio, struct mt76_dev, sdio); + int i, nframes, ret; + + /* disable interrupt */ + sdio_claim_host(sdio->func); + sdio_writel(sdio->func, WHLPCR_INT_EN_CLR, MCR_WHLPCR, NULL); + sdio_release_host(sdio->func); + + do { + nframes = 0; + + /* tx */ + for (i = 0; i <= MT_TXQ_PSD; i++) { + ret = mt76s_tx_run_queue(dev, dev->phy.q_tx[i]); + if (ret > 0) + nframes += ret; + } + ret = mt76s_tx_run_queue(dev, dev->q_mcu[MT_MCUQ_WM]); + if (ret > 0) + nframes += ret; + + /* rx */ + ret = mt76s_rx_handler(dev); + if (ret > 0) + nframes += ret; + + if (test_bit(MT76_MCU_RESET, &dev->phy.state) || + test_bit(MT76_STATE_SUSPEND, &dev->phy.state)) { + if (!mt76s_txqs_empty(dev)) + continue; + else + wake_up(&sdio->wait); + } + } while (nframes > 0); + + /* enable interrupt */ + sdio_claim_host(sdio->func); + sdio_writel(sdio->func, WHLPCR_INT_EN_SET, MCR_WHLPCR, NULL); + sdio_release_host(sdio->func); +} +EXPORT_SYMBOL_GPL(mt76s_txrx_worker); + +void mt76s_sdio_irq(struct sdio_func *func) +{ + struct mt76_dev *dev = sdio_get_drvdata(func); + struct mt76_sdio *sdio = &dev->sdio; + + if (!test_bit(MT76_STATE_INITIALIZED, &dev->phy.state) || + test_bit(MT76_MCU_RESET, &dev->phy.state)) + return; + + sdio_writel(sdio->func, WHLPCR_INT_EN_CLR, MCR_WHLPCR, NULL); + mt76_worker_schedule(&sdio->txrx_worker); +} +EXPORT_SYMBOL_GPL(mt76s_sdio_irq); + +bool mt76s_txqs_empty(struct mt76_dev *dev) +{ + struct mt76_queue *q; + int i; + + for (i = 0; i <= MT_TXQ_PSD + 1; i++) { + if (i <= MT_TXQ_PSD) + q = dev->phy.q_tx[i]; + else + q = dev->q_mcu[MT_MCUQ_WM]; + + if (q->first != q->head) + return false; + } + + return true; +} +EXPORT_SYMBOL_GPL(mt76s_txqs_empty); diff --git a/drivers/net/wireless/mediatek/mt76/testmode.c b/drivers/net/wireless/mediatek/mt76/testmode.c new file mode 100644 index 000000000..6ee160bda --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/testmode.c @@ -0,0 +1,676 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (C) 2020 Felix Fietkau */ + +#include +#include "mt76.h" + +const struct nla_policy mt76_tm_policy[NUM_MT76_TM_ATTRS] = { + [MT76_TM_ATTR_RESET] = { .type = NLA_FLAG }, + [MT76_TM_ATTR_STATE] = { .type = NLA_U8 }, + [MT76_TM_ATTR_TX_COUNT] = { .type = NLA_U32 }, + [MT76_TM_ATTR_TX_LENGTH] = { .type = NLA_U32 }, + [MT76_TM_ATTR_TX_RATE_MODE] = { .type = NLA_U8 }, + [MT76_TM_ATTR_TX_RATE_NSS] = { .type = NLA_U8 }, + [MT76_TM_ATTR_TX_RATE_IDX] = { .type = NLA_U8 }, + [MT76_TM_ATTR_TX_RATE_SGI] = { .type = NLA_U8 }, + [MT76_TM_ATTR_TX_RATE_LDPC] = { .type = NLA_U8 }, + [MT76_TM_ATTR_TX_RATE_STBC] = { .type = NLA_U8 }, + [MT76_TM_ATTR_TX_LTF] = { .type = NLA_U8 }, + [MT76_TM_ATTR_TX_ANTENNA] = { .type = NLA_U8 }, + [MT76_TM_ATTR_TX_SPE_IDX] = { .type = NLA_U8 }, + [MT76_TM_ATTR_TX_POWER_CONTROL] = { .type = NLA_U8 }, + [MT76_TM_ATTR_TX_POWER] = { .type = NLA_NESTED }, + [MT76_TM_ATTR_TX_DUTY_CYCLE] = { .type = NLA_U8 }, + [MT76_TM_ATTR_TX_IPG] = { .type = NLA_U32 }, + [MT76_TM_ATTR_TX_TIME] = { .type = NLA_U32 }, + [MT76_TM_ATTR_FREQ_OFFSET] = { .type = NLA_U32 }, + [MT76_TM_ATTR_DRV_DATA] = { .type = NLA_NESTED }, +}; +EXPORT_SYMBOL_GPL(mt76_tm_policy); + +void mt76_testmode_tx_pending(struct mt76_phy *phy) +{ + struct mt76_testmode_data *td = &phy->test; + struct mt76_dev *dev = phy->dev; + struct mt76_wcid *wcid = &dev->global_wcid; + struct sk_buff *skb = td->tx_skb; + struct mt76_queue *q; + u16 tx_queued_limit; + int qid; + + if (!skb || !td->tx_pending) + return; + + qid = skb_get_queue_mapping(skb); + q = phy->q_tx[qid]; + + tx_queued_limit = td->tx_queued_limit ? td->tx_queued_limit : 1000; + + spin_lock_bh(&q->lock); + + while (td->tx_pending > 0 && + td->tx_queued - td->tx_done < tx_queued_limit && + q->queued < q->ndesc / 2) { + int ret; + + ret = dev->queue_ops->tx_queue_skb(phy, q, qid, skb_get(skb), + wcid, NULL); + if (ret < 0) + break; + + td->tx_pending--; + td->tx_queued++; + } + + dev->queue_ops->kick(dev, q); + + spin_unlock_bh(&q->lock); +} + +static u32 +mt76_testmode_max_mpdu_len(struct mt76_phy *phy, u8 tx_rate_mode) +{ + switch (tx_rate_mode) { + case MT76_TM_TX_MODE_HT: + return IEEE80211_MAX_MPDU_LEN_HT_7935; + case MT76_TM_TX_MODE_VHT: + case MT76_TM_TX_MODE_HE_SU: + case MT76_TM_TX_MODE_HE_EXT_SU: + case MT76_TM_TX_MODE_HE_TB: + case MT76_TM_TX_MODE_HE_MU: + if (phy->sband_5g.sband.vht_cap.cap & + IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991) + return IEEE80211_MAX_MPDU_LEN_VHT_7991; + return IEEE80211_MAX_MPDU_LEN_VHT_11454; + case MT76_TM_TX_MODE_CCK: + case MT76_TM_TX_MODE_OFDM: + default: + return IEEE80211_MAX_FRAME_LEN; + } +} + +static void +mt76_testmode_free_skb(struct mt76_phy *phy) +{ + struct mt76_testmode_data *td = &phy->test; + + dev_kfree_skb(td->tx_skb); + td->tx_skb = NULL; +} + +int mt76_testmode_alloc_skb(struct mt76_phy *phy, u32 len) +{ +#define MT_TXP_MAX_LEN 4095 + u16 fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA | + IEEE80211_FCTL_FROMDS; + struct mt76_testmode_data *td = &phy->test; + struct sk_buff **frag_tail, *head; + struct ieee80211_tx_info *info; + struct ieee80211_hdr *hdr; + u32 max_len, head_len; + int nfrags, i; + + max_len = mt76_testmode_max_mpdu_len(phy, td->tx_rate_mode); + if (len > max_len) + len = max_len; + else if (len < sizeof(struct ieee80211_hdr)) + len = sizeof(struct ieee80211_hdr); + + nfrags = len / MT_TXP_MAX_LEN; + head_len = nfrags ? MT_TXP_MAX_LEN : len; + + if (len > IEEE80211_MAX_FRAME_LEN) + fc |= IEEE80211_STYPE_QOS_DATA; + + head = alloc_skb(head_len, GFP_KERNEL); + if (!head) + return -ENOMEM; + + hdr = __skb_put_zero(head, sizeof(*hdr)); + hdr->frame_control = cpu_to_le16(fc); + memcpy(hdr->addr1, td->addr[0], ETH_ALEN); + memcpy(hdr->addr2, td->addr[1], ETH_ALEN); + memcpy(hdr->addr3, td->addr[2], ETH_ALEN); + skb_set_queue_mapping(head, IEEE80211_AC_BE); + get_random_bytes(__skb_put(head, head_len - sizeof(*hdr)), + head_len - sizeof(*hdr)); + + info = IEEE80211_SKB_CB(head); + info->flags = IEEE80211_TX_CTL_INJECTED | + IEEE80211_TX_CTL_NO_ACK | + IEEE80211_TX_CTL_NO_PS_BUFFER; + + info->hw_queue |= FIELD_PREP(MT_TX_HW_QUEUE_PHY, phy->band_idx); + frag_tail = &skb_shinfo(head)->frag_list; + + for (i = 0; i < nfrags; i++) { + struct sk_buff *frag; + u16 frag_len; + + if (i == nfrags - 1) + frag_len = len % MT_TXP_MAX_LEN; + else + frag_len = MT_TXP_MAX_LEN; + + frag = alloc_skb(frag_len, GFP_KERNEL); + if (!frag) { + mt76_testmode_free_skb(phy); + dev_kfree_skb(head); + return -ENOMEM; + } + + get_random_bytes(__skb_put(frag, frag_len), frag_len); + head->len += frag->len; + head->data_len += frag->len; + + *frag_tail = frag; + frag_tail = &(*frag_tail)->next; + } + + mt76_testmode_free_skb(phy); + td->tx_skb = head; + + return 0; +} +EXPORT_SYMBOL(mt76_testmode_alloc_skb); + +static int +mt76_testmode_tx_init(struct mt76_phy *phy) +{ + struct mt76_testmode_data *td = &phy->test; + struct ieee80211_tx_info *info; + struct ieee80211_tx_rate *rate; + u8 max_nss = hweight8(phy->antenna_mask); + int ret; + + ret = mt76_testmode_alloc_skb(phy, td->tx_mpdu_len); + if (ret) + return ret; + + if (td->tx_rate_mode > MT76_TM_TX_MODE_VHT) + goto out; + + if (td->tx_antenna_mask) + max_nss = min_t(u8, max_nss, hweight8(td->tx_antenna_mask)); + + info = IEEE80211_SKB_CB(td->tx_skb); + rate = &info->control.rates[0]; + rate->count = 1; + rate->idx = td->tx_rate_idx; + + switch (td->tx_rate_mode) { + case MT76_TM_TX_MODE_CCK: + if (phy->chandef.chan->band != NL80211_BAND_2GHZ) + return -EINVAL; + + if (rate->idx > 4) + return -EINVAL; + break; + case MT76_TM_TX_MODE_OFDM: + if (phy->chandef.chan->band != NL80211_BAND_2GHZ) + break; + + if (rate->idx > 8) + return -EINVAL; + + rate->idx += 4; + break; + case MT76_TM_TX_MODE_HT: + if (rate->idx > 8 * max_nss && + !(rate->idx == 32 && + phy->chandef.width >= NL80211_CHAN_WIDTH_40)) + return -EINVAL; + + rate->flags |= IEEE80211_TX_RC_MCS; + break; + case MT76_TM_TX_MODE_VHT: + if (rate->idx > 9) + return -EINVAL; + + if (td->tx_rate_nss > max_nss) + return -EINVAL; + + ieee80211_rate_set_vht(rate, td->tx_rate_idx, td->tx_rate_nss); + rate->flags |= IEEE80211_TX_RC_VHT_MCS; + break; + default: + break; + } + + if (td->tx_rate_sgi) + rate->flags |= IEEE80211_TX_RC_SHORT_GI; + + if (td->tx_rate_ldpc) + info->flags |= IEEE80211_TX_CTL_LDPC; + + if (td->tx_rate_stbc) + info->flags |= IEEE80211_TX_CTL_STBC; + + if (td->tx_rate_mode >= MT76_TM_TX_MODE_HT) { + switch (phy->chandef.width) { + case NL80211_CHAN_WIDTH_40: + rate->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; + break; + case NL80211_CHAN_WIDTH_80: + rate->flags |= IEEE80211_TX_RC_80_MHZ_WIDTH; + break; + case NL80211_CHAN_WIDTH_80P80: + case NL80211_CHAN_WIDTH_160: + rate->flags |= IEEE80211_TX_RC_160_MHZ_WIDTH; + break; + default: + break; + } + } +out: + return 0; +} + +static void +mt76_testmode_tx_start(struct mt76_phy *phy) +{ + struct mt76_testmode_data *td = &phy->test; + struct mt76_dev *dev = phy->dev; + + td->tx_queued = 0; + td->tx_done = 0; + td->tx_pending = td->tx_count; + mt76_worker_schedule(&dev->tx_worker); +} + +static void +mt76_testmode_tx_stop(struct mt76_phy *phy) +{ + struct mt76_testmode_data *td = &phy->test; + struct mt76_dev *dev = phy->dev; + + mt76_worker_disable(&dev->tx_worker); + + td->tx_pending = 0; + + mt76_worker_enable(&dev->tx_worker); + + wait_event_timeout(dev->tx_wait, td->tx_done == td->tx_queued, + MT76_TM_TIMEOUT * HZ); + + mt76_testmode_free_skb(phy); +} + +static inline void +mt76_testmode_param_set(struct mt76_testmode_data *td, u16 idx) +{ + td->param_set[idx / 32] |= BIT(idx % 32); +} + +static inline bool +mt76_testmode_param_present(struct mt76_testmode_data *td, u16 idx) +{ + return td->param_set[idx / 32] & BIT(idx % 32); +} + +static void +mt76_testmode_init_defaults(struct mt76_phy *phy) +{ + struct mt76_testmode_data *td = &phy->test; + + if (td->tx_mpdu_len > 0) + return; + + td->tx_mpdu_len = 1024; + td->tx_count = 1; + td->tx_rate_mode = MT76_TM_TX_MODE_OFDM; + td->tx_rate_nss = 1; + + memcpy(td->addr[0], phy->macaddr, ETH_ALEN); + memcpy(td->addr[1], phy->macaddr, ETH_ALEN); + memcpy(td->addr[2], phy->macaddr, ETH_ALEN); +} + +static int +__mt76_testmode_set_state(struct mt76_phy *phy, enum mt76_testmode_state state) +{ + enum mt76_testmode_state prev_state = phy->test.state; + struct mt76_dev *dev = phy->dev; + int err; + + if (prev_state == MT76_TM_STATE_TX_FRAMES) + mt76_testmode_tx_stop(phy); + + if (state == MT76_TM_STATE_TX_FRAMES) { + err = mt76_testmode_tx_init(phy); + if (err) + return err; + } + + err = dev->test_ops->set_state(phy, state); + if (err) { + if (state == MT76_TM_STATE_TX_FRAMES) + mt76_testmode_tx_stop(phy); + + return err; + } + + if (state == MT76_TM_STATE_TX_FRAMES) + mt76_testmode_tx_start(phy); + else if (state == MT76_TM_STATE_RX_FRAMES) { + memset(&phy->test.rx_stats, 0, sizeof(phy->test.rx_stats)); + } + + phy->test.state = state; + + return 0; +} + +int mt76_testmode_set_state(struct mt76_phy *phy, enum mt76_testmode_state state) +{ + struct mt76_testmode_data *td = &phy->test; + struct ieee80211_hw *hw = phy->hw; + + if (state == td->state && state == MT76_TM_STATE_OFF) + return 0; + + if (state > MT76_TM_STATE_OFF && + (!test_bit(MT76_STATE_RUNNING, &phy->state) || + !(hw->conf.flags & IEEE80211_CONF_MONITOR))) + return -ENOTCONN; + + if (state != MT76_TM_STATE_IDLE && + td->state != MT76_TM_STATE_IDLE) { + int ret; + + ret = __mt76_testmode_set_state(phy, MT76_TM_STATE_IDLE); + if (ret) + return ret; + } + + return __mt76_testmode_set_state(phy, state); + +} +EXPORT_SYMBOL(mt76_testmode_set_state); + +static int +mt76_tm_get_u8(struct nlattr *attr, u8 *dest, u8 min, u8 max) +{ + u8 val; + + if (!attr) + return 0; + + val = nla_get_u8(attr); + if (val < min || val > max) + return -EINVAL; + + *dest = val; + return 0; +} + +int mt76_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + void *data, int len) +{ + struct mt76_phy *phy = hw->priv; + struct mt76_dev *dev = phy->dev; + struct mt76_testmode_data *td = &phy->test; + struct nlattr *tb[NUM_MT76_TM_ATTRS]; + u32 state; + int err; + int i; + + if (!dev->test_ops) + return -EOPNOTSUPP; + + err = nla_parse_deprecated(tb, MT76_TM_ATTR_MAX, data, len, + mt76_tm_policy, NULL); + if (err) + return err; + + err = -EINVAL; + + mutex_lock(&dev->mutex); + + if (tb[MT76_TM_ATTR_RESET]) { + mt76_testmode_set_state(phy, MT76_TM_STATE_OFF); + memset(td, 0, sizeof(*td)); + } + + mt76_testmode_init_defaults(phy); + + if (tb[MT76_TM_ATTR_TX_COUNT]) + td->tx_count = nla_get_u32(tb[MT76_TM_ATTR_TX_COUNT]); + + if (tb[MT76_TM_ATTR_TX_RATE_IDX]) + td->tx_rate_idx = nla_get_u8(tb[MT76_TM_ATTR_TX_RATE_IDX]); + + if (mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_RATE_MODE], &td->tx_rate_mode, + 0, MT76_TM_TX_MODE_MAX) || + mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_RATE_NSS], &td->tx_rate_nss, + 1, hweight8(phy->antenna_mask)) || + mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_RATE_SGI], &td->tx_rate_sgi, 0, 2) || + mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_RATE_LDPC], &td->tx_rate_ldpc, 0, 1) || + mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_RATE_STBC], &td->tx_rate_stbc, 0, 1) || + mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_LTF], &td->tx_ltf, 0, 2) || + mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_ANTENNA], + &td->tx_antenna_mask, 0, 0xff) || + mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_SPE_IDX], &td->tx_spe_idx, 0, 27) || + mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_DUTY_CYCLE], + &td->tx_duty_cycle, 0, 99) || + mt76_tm_get_u8(tb[MT76_TM_ATTR_TX_POWER_CONTROL], + &td->tx_power_control, 0, 1)) + goto out; + + if (tb[MT76_TM_ATTR_TX_LENGTH]) { + u32 val = nla_get_u32(tb[MT76_TM_ATTR_TX_LENGTH]); + + if (val > mt76_testmode_max_mpdu_len(phy, td->tx_rate_mode) || + val < sizeof(struct ieee80211_hdr)) + goto out; + + td->tx_mpdu_len = val; + } + + if (tb[MT76_TM_ATTR_TX_IPG]) + td->tx_ipg = nla_get_u32(tb[MT76_TM_ATTR_TX_IPG]); + + if (tb[MT76_TM_ATTR_TX_TIME]) + td->tx_time = nla_get_u32(tb[MT76_TM_ATTR_TX_TIME]); + + if (tb[MT76_TM_ATTR_FREQ_OFFSET]) + td->freq_offset = nla_get_u32(tb[MT76_TM_ATTR_FREQ_OFFSET]); + + if (tb[MT76_TM_ATTR_STATE]) { + state = nla_get_u32(tb[MT76_TM_ATTR_STATE]); + if (state > MT76_TM_STATE_MAX) + goto out; + } else { + state = td->state; + } + + if (tb[MT76_TM_ATTR_TX_POWER]) { + struct nlattr *cur; + int idx = 0; + int rem; + + nla_for_each_nested(cur, tb[MT76_TM_ATTR_TX_POWER], rem) { + if (nla_len(cur) != 1 || + idx >= ARRAY_SIZE(td->tx_power)) + goto out; + + td->tx_power[idx++] = nla_get_u8(cur); + } + } + + if (tb[MT76_TM_ATTR_MAC_ADDRS]) { + struct nlattr *cur; + int idx = 0; + int rem; + + nla_for_each_nested(cur, tb[MT76_TM_ATTR_MAC_ADDRS], rem) { + if (nla_len(cur) != ETH_ALEN || idx >= 3) + goto out; + + memcpy(td->addr[idx], nla_data(cur), ETH_ALEN); + idx++; + } + } + + if (dev->test_ops->set_params) { + err = dev->test_ops->set_params(phy, tb, state); + if (err) + goto out; + } + + for (i = MT76_TM_ATTR_STATE; i < ARRAY_SIZE(tb); i++) + if (tb[i]) + mt76_testmode_param_set(td, i); + + err = 0; + if (tb[MT76_TM_ATTR_STATE]) + err = mt76_testmode_set_state(phy, state); + +out: + mutex_unlock(&dev->mutex); + + return err; +} +EXPORT_SYMBOL(mt76_testmode_cmd); + +static int +mt76_testmode_dump_stats(struct mt76_phy *phy, struct sk_buff *msg) +{ + struct mt76_testmode_data *td = &phy->test; + struct mt76_dev *dev = phy->dev; + u64 rx_packets = 0; + u64 rx_fcs_error = 0; + int i; + + if (dev->test_ops->dump_stats) { + int ret; + + ret = dev->test_ops->dump_stats(phy, msg); + if (ret) + return ret; + } + + for (i = 0; i < ARRAY_SIZE(td->rx_stats.packets); i++) { + rx_packets += td->rx_stats.packets[i]; + rx_fcs_error += td->rx_stats.fcs_error[i]; + } + + if (nla_put_u32(msg, MT76_TM_STATS_ATTR_TX_PENDING, td->tx_pending) || + nla_put_u32(msg, MT76_TM_STATS_ATTR_TX_QUEUED, td->tx_queued) || + nla_put_u32(msg, MT76_TM_STATS_ATTR_TX_DONE, td->tx_done) || + nla_put_u64_64bit(msg, MT76_TM_STATS_ATTR_RX_PACKETS, rx_packets, + MT76_TM_STATS_ATTR_PAD) || + nla_put_u64_64bit(msg, MT76_TM_STATS_ATTR_RX_FCS_ERROR, rx_fcs_error, + MT76_TM_STATS_ATTR_PAD)) + return -EMSGSIZE; + + return 0; +} + +int mt76_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg, + struct netlink_callback *cb, void *data, int len) +{ + struct mt76_phy *phy = hw->priv; + struct mt76_dev *dev = phy->dev; + struct mt76_testmode_data *td = &phy->test; + struct nlattr *tb[NUM_MT76_TM_ATTRS] = {}; + int err = 0; + void *a; + int i; + + if (!dev->test_ops) + return -EOPNOTSUPP; + + if (cb->args[2]++ > 0) + return -ENOENT; + + if (data) { + err = nla_parse_deprecated(tb, MT76_TM_ATTR_MAX, data, len, + mt76_tm_policy, NULL); + if (err) + return err; + } + + mutex_lock(&dev->mutex); + + if (tb[MT76_TM_ATTR_STATS]) { + err = -EINVAL; + + a = nla_nest_start(msg, MT76_TM_ATTR_STATS); + if (a) { + err = mt76_testmode_dump_stats(phy, msg); + nla_nest_end(msg, a); + } + + goto out; + } + + mt76_testmode_init_defaults(phy); + + err = -EMSGSIZE; + if (nla_put_u32(msg, MT76_TM_ATTR_STATE, td->state)) + goto out; + + if (dev->test_mtd.name && + (nla_put_string(msg, MT76_TM_ATTR_MTD_PART, dev->test_mtd.name) || + nla_put_u32(msg, MT76_TM_ATTR_MTD_OFFSET, dev->test_mtd.offset))) + goto out; + + if (nla_put_u32(msg, MT76_TM_ATTR_TX_COUNT, td->tx_count) || + nla_put_u32(msg, MT76_TM_ATTR_TX_LENGTH, td->tx_mpdu_len) || + nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_MODE, td->tx_rate_mode) || + nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_NSS, td->tx_rate_nss) || + nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_IDX, td->tx_rate_idx) || + nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_SGI, td->tx_rate_sgi) || + nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_LDPC, td->tx_rate_ldpc) || + nla_put_u8(msg, MT76_TM_ATTR_TX_RATE_STBC, td->tx_rate_stbc) || + (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_LTF) && + nla_put_u8(msg, MT76_TM_ATTR_TX_LTF, td->tx_ltf)) || + (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_ANTENNA) && + nla_put_u8(msg, MT76_TM_ATTR_TX_ANTENNA, td->tx_antenna_mask)) || + (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_SPE_IDX) && + nla_put_u8(msg, MT76_TM_ATTR_TX_SPE_IDX, td->tx_spe_idx)) || + (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_DUTY_CYCLE) && + nla_put_u8(msg, MT76_TM_ATTR_TX_DUTY_CYCLE, td->tx_duty_cycle)) || + (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_IPG) && + nla_put_u32(msg, MT76_TM_ATTR_TX_IPG, td->tx_ipg)) || + (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_TIME) && + nla_put_u32(msg, MT76_TM_ATTR_TX_TIME, td->tx_time)) || + (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_POWER_CONTROL) && + nla_put_u8(msg, MT76_TM_ATTR_TX_POWER_CONTROL, td->tx_power_control)) || + (mt76_testmode_param_present(td, MT76_TM_ATTR_FREQ_OFFSET) && + nla_put_u8(msg, MT76_TM_ATTR_FREQ_OFFSET, td->freq_offset))) + goto out; + + if (mt76_testmode_param_present(td, MT76_TM_ATTR_TX_POWER)) { + a = nla_nest_start(msg, MT76_TM_ATTR_TX_POWER); + if (!a) + goto out; + + for (i = 0; i < ARRAY_SIZE(td->tx_power); i++) + if (nla_put_u8(msg, i, td->tx_power[i])) + goto out; + + nla_nest_end(msg, a); + } + + if (mt76_testmode_param_present(td, MT76_TM_ATTR_MAC_ADDRS)) { + a = nla_nest_start(msg, MT76_TM_ATTR_MAC_ADDRS); + if (!a) + goto out; + + for (i = 0; i < 3; i++) + if (nla_put(msg, i, ETH_ALEN, td->addr[i])) + goto out; + + nla_nest_end(msg, a); + } + + err = 0; + +out: + mutex_unlock(&dev->mutex); + + return err; +} +EXPORT_SYMBOL(mt76_testmode_dump); diff --git a/drivers/net/wireless/mediatek/mt76/testmode.h b/drivers/net/wireless/mediatek/mt76/testmode.h new file mode 100644 index 000000000..bed1ba40b --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/testmode.h @@ -0,0 +1,200 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (C) 2020 Felix Fietkau + */ +#ifndef __MT76_TESTMODE_H +#define __MT76_TESTMODE_H + +#define MT76_TM_TIMEOUT 10 + +#include + +/** + * enum mt76_testmode_attr - testmode attributes inside NL80211_ATTR_TESTDATA + * + * @MT76_TM_ATTR_UNSPEC: (invalid attribute) + * + * @MT76_TM_ATTR_RESET: reset parameters to default (flag) + * @MT76_TM_ATTR_STATE: test state (u32), see &enum mt76_testmode_state + * + * @MT76_TM_ATTR_MTD_PART: mtd partition used for eeprom data (string) + * @MT76_TM_ATTR_MTD_OFFSET: offset of eeprom data within the partition (u32) + * + * @MT76_TM_ATTR_TX_COUNT: configured number of frames to send when setting + * state to MT76_TM_STATE_TX_FRAMES (u32) + * @MT76_TM_ATTR_TX_PENDING: pending frames during MT76_TM_STATE_TX_FRAMES (u32) + * @MT76_TM_ATTR_TX_LENGTH: packet tx mpdu length (u32) + * @MT76_TM_ATTR_TX_RATE_MODE: packet tx mode (u8, see &enum mt76_testmode_tx_mode) + * @MT76_TM_ATTR_TX_RATE_NSS: packet tx number of spatial streams (u8) + * @MT76_TM_ATTR_TX_RATE_IDX: packet tx rate/MCS index (u8) + * @MT76_TM_ATTR_TX_RATE_SGI: packet tx use short guard interval (u8) + * @MT76_TM_ATTR_TX_RATE_LDPC: packet tx enable LDPC (u8) + * @MT76_TM_ATTR_TX_RATE_STBC: packet tx enable STBC (u8) + * @MT76_TM_ATTR_TX_LTF: packet tx LTF, set 0 to 2 for 1x, 2x, and 4x LTF (u8) + * + * @MT76_TM_ATTR_TX_ANTENNA: tx antenna mask (u8) + * @MT76_TM_ATTR_TX_POWER_CONTROL: enable tx power control (u8) + * @MT76_TM_ATTR_TX_POWER: per-antenna tx power array (nested, u8 attrs) + * + * @MT76_TM_ATTR_FREQ_OFFSET: RF frequency offset (u32) + * + * @MT76_TM_ATTR_STATS: statistics (nested, see &enum mt76_testmode_stats_attr) + * + * @MT76_TM_ATTR_TX_SPE_IDX: tx spatial extension index (u8) + * + * @MT76_TM_ATTR_TX_DUTY_CYCLE: packet tx duty cycle (u8) + * @MT76_TM_ATTR_TX_IPG: tx inter-packet gap, in unit of us (u32) + * @MT76_TM_ATTR_TX_TIME: packet transmission time, in unit of us (u32) + * + * @MT76_TM_ATTR_DRV_DATA: driver specific netlink attrs (nested) + * + * @MT76_TM_ATTR_MAC_ADDRS: array of nested MAC addresses (nested) + */ +enum mt76_testmode_attr { + MT76_TM_ATTR_UNSPEC, + + MT76_TM_ATTR_RESET, + MT76_TM_ATTR_STATE, + + MT76_TM_ATTR_MTD_PART, + MT76_TM_ATTR_MTD_OFFSET, + + MT76_TM_ATTR_TX_COUNT, + MT76_TM_ATTR_TX_LENGTH, + MT76_TM_ATTR_TX_RATE_MODE, + MT76_TM_ATTR_TX_RATE_NSS, + MT76_TM_ATTR_TX_RATE_IDX, + MT76_TM_ATTR_TX_RATE_SGI, + MT76_TM_ATTR_TX_RATE_LDPC, + MT76_TM_ATTR_TX_RATE_STBC, + MT76_TM_ATTR_TX_LTF, + + MT76_TM_ATTR_TX_ANTENNA, + MT76_TM_ATTR_TX_POWER_CONTROL, + MT76_TM_ATTR_TX_POWER, + + MT76_TM_ATTR_FREQ_OFFSET, + + MT76_TM_ATTR_STATS, + + MT76_TM_ATTR_TX_SPE_IDX, + + MT76_TM_ATTR_TX_DUTY_CYCLE, + MT76_TM_ATTR_TX_IPG, + MT76_TM_ATTR_TX_TIME, + + MT76_TM_ATTR_DRV_DATA, + + MT76_TM_ATTR_MAC_ADDRS, + + /* keep last */ + NUM_MT76_TM_ATTRS, + MT76_TM_ATTR_MAX = NUM_MT76_TM_ATTRS - 1, +}; + +/** + * enum mt76_testmode_state - statistics attributes + * + * @MT76_TM_STATS_ATTR_TX_PENDING: pending tx frames (u32) + * @MT76_TM_STATS_ATTR_TX_QUEUED: queued tx frames (u32) + * @MT76_TM_STATS_ATTR_TX_QUEUED: completed tx frames (u32) + * + * @MT76_TM_STATS_ATTR_RX_PACKETS: number of rx packets (u64) + * @MT76_TM_STATS_ATTR_RX_FCS_ERROR: number of rx packets with FCS error (u64) + * @MT76_TM_STATS_ATTR_LAST_RX: information about the last received packet + * see &enum mt76_testmode_rx_attr + */ +enum mt76_testmode_stats_attr { + MT76_TM_STATS_ATTR_UNSPEC, + MT76_TM_STATS_ATTR_PAD, + + MT76_TM_STATS_ATTR_TX_PENDING, + MT76_TM_STATS_ATTR_TX_QUEUED, + MT76_TM_STATS_ATTR_TX_DONE, + + MT76_TM_STATS_ATTR_RX_PACKETS, + MT76_TM_STATS_ATTR_RX_FCS_ERROR, + MT76_TM_STATS_ATTR_LAST_RX, + + /* keep last */ + NUM_MT76_TM_STATS_ATTRS, + MT76_TM_STATS_ATTR_MAX = NUM_MT76_TM_STATS_ATTRS - 1, +}; + + +/** + * enum mt76_testmode_rx_attr - packet rx information + * + * @MT76_TM_RX_ATTR_FREQ_OFFSET: frequency offset (s32) + * @MT76_TM_RX_ATTR_RCPI: received channel power indicator (array, u8) + * @MT76_TM_RX_ATTR_IB_RSSI: internal inband RSSI (array, s8) + * @MT76_TM_RX_ATTR_WB_RSSI: internal wideband RSSI (array, s8) + * @MT76_TM_RX_ATTR_SNR: signal-to-noise ratio (u8) + */ +enum mt76_testmode_rx_attr { + MT76_TM_RX_ATTR_UNSPEC, + + MT76_TM_RX_ATTR_FREQ_OFFSET, + MT76_TM_RX_ATTR_RCPI, + MT76_TM_RX_ATTR_IB_RSSI, + MT76_TM_RX_ATTR_WB_RSSI, + MT76_TM_RX_ATTR_SNR, + + /* keep last */ + NUM_MT76_TM_RX_ATTRS, + MT76_TM_RX_ATTR_MAX = NUM_MT76_TM_RX_ATTRS - 1, +}; + +/** + * enum mt76_testmode_state - phy test state + * + * @MT76_TM_STATE_OFF: test mode disabled (normal operation) + * @MT76_TM_STATE_IDLE: test mode enabled, but idle + * @MT76_TM_STATE_TX_FRAMES: send a fixed number of test frames + * @MT76_TM_STATE_RX_FRAMES: receive packets and keep statistics + * @MT76_TM_STATE_TX_CONT: waveform tx without time gap + * @MT76_TM_STATE_ON: test mode enabled used in offload firmware + */ +enum mt76_testmode_state { + MT76_TM_STATE_OFF, + MT76_TM_STATE_IDLE, + MT76_TM_STATE_TX_FRAMES, + MT76_TM_STATE_RX_FRAMES, + MT76_TM_STATE_TX_CONT, + MT76_TM_STATE_ON, + + /* keep last */ + NUM_MT76_TM_STATES, + MT76_TM_STATE_MAX = NUM_MT76_TM_STATES - 1, +}; + +/** + * enum mt76_testmode_tx_mode - packet tx phy mode + * + * @MT76_TM_TX_MODE_CCK: legacy CCK mode + * @MT76_TM_TX_MODE_OFDM: legacy OFDM mode + * @MT76_TM_TX_MODE_HT: 802.11n MCS + * @MT76_TM_TX_MODE_VHT: 802.11ac MCS + * @MT76_TM_TX_MODE_HE_SU: 802.11ax single-user MIMO + * @MT76_TM_TX_MODE_HE_EXT_SU: 802.11ax extended-range SU + * @MT76_TM_TX_MODE_HE_TB: 802.11ax trigger-based + * @MT76_TM_TX_MODE_HE_MU: 802.11ax multi-user MIMO + */ +enum mt76_testmode_tx_mode { + MT76_TM_TX_MODE_CCK, + MT76_TM_TX_MODE_OFDM, + MT76_TM_TX_MODE_HT, + MT76_TM_TX_MODE_VHT, + MT76_TM_TX_MODE_HE_SU, + MT76_TM_TX_MODE_HE_EXT_SU, + MT76_TM_TX_MODE_HE_TB, + MT76_TM_TX_MODE_HE_MU, + + /* keep last */ + NUM_MT76_TM_TX_MODES, + MT76_TM_TX_MODE_MAX = NUM_MT76_TM_TX_MODES - 1, +}; + +extern const struct nla_policy mt76_tm_policy[NUM_MT76_TM_ATTRS]; + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/trace.c b/drivers/net/wireless/mediatek/mt76/trace.c new file mode 100644 index 000000000..f17cc0101 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/trace.c @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (C) 2016 Felix Fietkau + */ + +#include + +#ifndef __CHECKER__ +#define CREATE_TRACE_POINTS +#include "trace.h" + +EXPORT_TRACEPOINT_SYMBOL_GPL(mac_txdone); +EXPORT_TRACEPOINT_SYMBOL_GPL(dev_irq); + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/trace.h b/drivers/net/wireless/mediatek/mt76/trace.h new file mode 100644 index 000000000..794b957ac --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/trace.h @@ -0,0 +1,111 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (C) 2016 Felix Fietkau + */ + +#if !defined(__MT76_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define __MT76_TRACE_H + +#include +#include "mt76.h" + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM mt76 + +#define MAXNAME 32 +#define DEV_ENTRY __array(char, wiphy_name, 32) +#define DEVICE_ASSIGN strscpy(__entry->wiphy_name, \ + wiphy_name(dev->hw->wiphy), MAXNAME) +#define DEV_PR_FMT "%s" +#define DEV_PR_ARG __entry->wiphy_name + +#define REG_ENTRY __field(u32, reg) __field(u32, val) +#define REG_ASSIGN __entry->reg = reg; __entry->val = val +#define REG_PR_FMT " %04x=%08x" +#define REG_PR_ARG __entry->reg, __entry->val + +#define TXID_ENTRY __field(u8, wcid) __field(u8, pktid) +#define TXID_ASSIGN __entry->wcid = wcid; __entry->pktid = pktid +#define TXID_PR_FMT " [%d:%d]" +#define TXID_PR_ARG __entry->wcid, __entry->pktid + +DECLARE_EVENT_CLASS(dev_reg_evt, + TP_PROTO(struct mt76_dev *dev, u32 reg, u32 val), + TP_ARGS(dev, reg, val), + TP_STRUCT__entry( + DEV_ENTRY + REG_ENTRY + ), + TP_fast_assign( + DEVICE_ASSIGN; + REG_ASSIGN; + ), + TP_printk( + DEV_PR_FMT REG_PR_FMT, + DEV_PR_ARG, REG_PR_ARG + ) +); + +DEFINE_EVENT(dev_reg_evt, reg_rr, + TP_PROTO(struct mt76_dev *dev, u32 reg, u32 val), + TP_ARGS(dev, reg, val) +); + +DEFINE_EVENT(dev_reg_evt, reg_wr, + TP_PROTO(struct mt76_dev *dev, u32 reg, u32 val), + TP_ARGS(dev, reg, val) +); + +TRACE_EVENT(dev_irq, + TP_PROTO(struct mt76_dev *dev, u32 val, u32 mask), + + TP_ARGS(dev, val, mask), + + TP_STRUCT__entry( + DEV_ENTRY + __field(u32, val) + __field(u32, mask) + ), + + TP_fast_assign( + DEVICE_ASSIGN; + __entry->val = val; + __entry->mask = mask; + ), + + TP_printk( + DEV_PR_FMT " %08x & %08x", + DEV_PR_ARG, __entry->val, __entry->mask + ) +); + +DECLARE_EVENT_CLASS(dev_txid_evt, + TP_PROTO(struct mt76_dev *dev, u8 wcid, u8 pktid), + TP_ARGS(dev, wcid, pktid), + TP_STRUCT__entry( + DEV_ENTRY + TXID_ENTRY + ), + TP_fast_assign( + DEVICE_ASSIGN; + TXID_ASSIGN; + ), + TP_printk( + DEV_PR_FMT TXID_PR_FMT, + DEV_PR_ARG, TXID_PR_ARG + ) +); + +DEFINE_EVENT(dev_txid_evt, mac_txdone, + TP_PROTO(struct mt76_dev *dev, u8 wcid, u8 pktid), + TP_ARGS(dev, wcid, pktid) +); + +#endif + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE trace + +#include diff --git a/drivers/net/wireless/mediatek/mt76/tx.c b/drivers/net/wireless/mediatek/mt76/tx.c new file mode 100644 index 000000000..3707ee19e --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/tx.c @@ -0,0 +1,1027 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (C) 2016 Felix Fietkau + */ + +#include "mt76.h" + +static int +mt76_txq_get_qid(struct ieee80211_txq *txq) +{ + if (!txq->sta) + return MT_TXQ_BE; + + return txq->ac; +} + +void +mt76_tx_check_agg_ssn(struct ieee80211_sta *sta, struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ieee80211_txq *txq; + struct mt76_txq *mtxq; + u8 tid; + + if (!sta || !ieee80211_is_data_qos(hdr->frame_control) || + !ieee80211_is_data_present(hdr->frame_control)) + return; + + tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK; + txq = sta->txq[tid]; + mtxq = (struct mt76_txq *)txq->drv_priv; + if (!mtxq->aggr) + return; + + mtxq->agg_ssn = le16_to_cpu(hdr->seq_ctrl) + 0x10; +} +EXPORT_SYMBOL_GPL(mt76_tx_check_agg_ssn); + +void +mt76_tx_status_lock(struct mt76_dev *dev, struct sk_buff_head *list) + __acquires(&dev->status_lock) +{ + __skb_queue_head_init(list); + spin_lock_bh(&dev->status_lock); +} +EXPORT_SYMBOL_GPL(mt76_tx_status_lock); + +void +mt76_tx_status_unlock(struct mt76_dev *dev, struct sk_buff_head *list) + __releases(&dev->status_lock) +{ + struct ieee80211_hw *hw; + struct sk_buff *skb; + + spin_unlock_bh(&dev->status_lock); + + rcu_read_lock(); + while ((skb = __skb_dequeue(list)) != NULL) { + struct ieee80211_tx_status status = { + .skb = skb, + .info = IEEE80211_SKB_CB(skb), + }; + struct ieee80211_rate_status rs = {}; + struct mt76_tx_cb *cb = mt76_tx_skb_cb(skb); + struct mt76_wcid *wcid; + + wcid = __mt76_wcid_ptr(dev, cb->wcid); + if (wcid) { + status.sta = wcid_to_sta(wcid); + if (status.sta && (wcid->rate.flags || wcid->rate.legacy)) { + rs.rate_idx = wcid->rate; + status.rates = &rs; + status.n_rates = 1; + } else { + status.n_rates = 0; + } + } + + hw = mt76_tx_status_get_hw(dev, skb); + spin_lock_bh(&dev->rx_lock); + ieee80211_tx_status_ext(hw, &status); + spin_unlock_bh(&dev->rx_lock); + } + rcu_read_unlock(); +} +EXPORT_SYMBOL_GPL(mt76_tx_status_unlock); + +static void +__mt76_tx_status_skb_done(struct mt76_dev *dev, struct sk_buff *skb, u8 flags, + struct sk_buff_head *list) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct mt76_tx_cb *cb = mt76_tx_skb_cb(skb); + u8 done = MT_TX_CB_DMA_DONE | MT_TX_CB_TXS_DONE; + + flags |= cb->flags; + cb->flags = flags; + + if ((flags & done) != done) + return; + + /* Tx status can be unreliable. if it fails, mark the frame as ACKed */ + if (flags & MT_TX_CB_TXS_FAILED && + (dev->drv->drv_flags & MT_DRV_IGNORE_TXS_FAILED)) { + info->status.rates[0].count = 0; + info->status.rates[0].idx = -1; + info->flags |= IEEE80211_TX_STAT_ACK; + } + + __skb_queue_tail(list, skb); +} + +void +mt76_tx_status_skb_done(struct mt76_dev *dev, struct sk_buff *skb, + struct sk_buff_head *list) +{ + __mt76_tx_status_skb_done(dev, skb, MT_TX_CB_TXS_DONE, list); +} +EXPORT_SYMBOL_GPL(mt76_tx_status_skb_done); + +int +mt76_tx_status_skb_add(struct mt76_dev *dev, struct mt76_wcid *wcid, + struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct mt76_tx_cb *cb = mt76_tx_skb_cb(skb); + int pid; + + memset(cb, 0, sizeof(*cb)); + + if (!wcid || !rcu_access_pointer(dev->wcid[wcid->idx])) + return MT_PACKET_ID_NO_ACK; + + if (info->flags & IEEE80211_TX_CTL_NO_ACK) + return MT_PACKET_ID_NO_ACK; + + if (!(info->flags & (IEEE80211_TX_CTL_REQ_TX_STATUS | + IEEE80211_TX_CTL_RATE_CTRL_PROBE))) { + if (mtk_wed_device_active(&dev->mmio.wed) && + ((info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP) || + ieee80211_is_data(hdr->frame_control))) + return MT_PACKET_ID_WED; + + return MT_PACKET_ID_NO_SKB; + } + + spin_lock_bh(&dev->status_lock); + + pid = idr_alloc(&wcid->pktid, skb, MT_PACKET_ID_FIRST, + MT_PACKET_ID_MASK, GFP_ATOMIC); + if (pid < 0) { + pid = MT_PACKET_ID_NO_SKB; + goto out; + } + + cb->wcid = wcid->idx; + cb->pktid = pid; + + if (list_empty(&wcid->list)) + list_add_tail(&wcid->list, &dev->wcid_list); + +out: + spin_unlock_bh(&dev->status_lock); + + return pid; +} +EXPORT_SYMBOL_GPL(mt76_tx_status_skb_add); + +struct sk_buff * +mt76_tx_status_skb_get(struct mt76_dev *dev, struct mt76_wcid *wcid, int pktid, + struct sk_buff_head *list) +{ + struct sk_buff *skb; + int id; + + lockdep_assert_held(&dev->status_lock); + + skb = idr_remove(&wcid->pktid, pktid); + if (skb) + goto out; + + /* look for stale entries in the wcid idr queue */ + idr_for_each_entry(&wcid->pktid, skb, id) { + struct mt76_tx_cb *cb = mt76_tx_skb_cb(skb); + + if (pktid >= 0) { + if (!(cb->flags & MT_TX_CB_DMA_DONE)) + continue; + + if (time_is_after_jiffies(cb->jiffies + + MT_TX_STATUS_SKB_TIMEOUT)) + continue; + } + + /* It has been too long since DMA_DONE, time out this packet + * and stop waiting for TXS callback. + */ + idr_remove(&wcid->pktid, cb->pktid); + __mt76_tx_status_skb_done(dev, skb, MT_TX_CB_TXS_FAILED | + MT_TX_CB_TXS_DONE, list); + } + +out: + if (idr_is_empty(&wcid->pktid)) + list_del_init(&wcid->list); + + return skb; +} +EXPORT_SYMBOL_GPL(mt76_tx_status_skb_get); + +void +mt76_tx_status_check(struct mt76_dev *dev, bool flush) +{ + struct mt76_wcid *wcid, *tmp; + struct sk_buff_head list; + + mt76_tx_status_lock(dev, &list); + list_for_each_entry_safe(wcid, tmp, &dev->wcid_list, list) + mt76_tx_status_skb_get(dev, wcid, flush ? -1 : 0, &list); + mt76_tx_status_unlock(dev, &list); +} +EXPORT_SYMBOL_GPL(mt76_tx_status_check); + +static void +mt76_tx_check_non_aql(struct mt76_dev *dev, struct mt76_wcid *wcid, + struct sk_buff *skb) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_sta *sta; + int pending; + int i; + + if (!wcid || info->tx_time_est) + return; + + pending = atomic_dec_return(&wcid->non_aql_packets); + if (pending < 0) + atomic_cmpxchg(&wcid->non_aql_packets, pending, 0); + + sta = wcid_to_sta(wcid); + if (!sta || pending != MT_MAX_NON_AQL_PKT - 1) + return; + + for (i = 0; i < ARRAY_SIZE(sta->txq); i++) { + if (!sta->txq[i]) + continue; + + ieee80211_schedule_txq(dev->hw, sta->txq[i]); + } +} + +void __mt76_tx_complete_skb(struct mt76_dev *dev, u16 wcid_idx, struct sk_buff *skb, + struct list_head *free_list) +{ + struct mt76_tx_cb *cb = mt76_tx_skb_cb(skb); + struct ieee80211_tx_status status = { + .skb = skb, + .free_list = free_list, + }; + struct mt76_wcid *wcid = NULL; + struct ieee80211_hw *hw; + struct sk_buff_head list; + + rcu_read_lock(); + + wcid = __mt76_wcid_ptr(dev, wcid_idx); + mt76_tx_check_non_aql(dev, wcid, skb); + + if (wcid && (dev->drv->drv_flags & MT_DRV_HW_PS_BUFFERING) && + test_bit(MT_WCID_FLAG_PS, &wcid->flags)) { + struct ieee80211_sta *sta = wcid_to_sta(wcid); + + if (sta) { + struct ieee80211_hw *hw = mt76_phy_hw(dev, wcid->phy_idx); + int i; + + for (i = 0; i < ARRAY_SIZE(sta->txq); i++) + if (sta->txq[i]) + ieee80211_schedule_txq(hw, sta->txq[i]); + mt76_worker_schedule(&dev->tx_worker); + } + } + +#ifdef CONFIG_NL80211_TESTMODE + if (mt76_is_testmode_skb(dev, skb, &hw)) { + struct mt76_phy *phy = hw->priv; + + if (skb == phy->test.tx_skb) + phy->test.tx_done++; + if (phy->test.tx_queued == phy->test.tx_done) + wake_up(&dev->tx_wait); + + dev_kfree_skb_any(skb); + goto out; + } +#endif + + if (cb->pktid < MT_PACKET_ID_FIRST) { + struct ieee80211_rate_status rs = {}; + + hw = mt76_tx_status_get_hw(dev, skb); + status.sta = wcid_to_sta(wcid); + if (status.sta && (wcid->rate.flags || wcid->rate.legacy)) { + rs.rate_idx = wcid->rate; + status.rates = &rs; + status.n_rates = 1; + } + spin_lock_bh(&dev->rx_lock); + ieee80211_tx_status_ext(hw, &status); + spin_unlock_bh(&dev->rx_lock); + goto out; + } + + mt76_tx_status_lock(dev, &list); + cb->jiffies = jiffies; + __mt76_tx_status_skb_done(dev, skb, MT_TX_CB_DMA_DONE, &list); + mt76_tx_status_unlock(dev, &list); + +out: + rcu_read_unlock(); +} +EXPORT_SYMBOL_GPL(__mt76_tx_complete_skb); + +static int +__mt76_tx_queue_skb(struct mt76_phy *phy, int qid, struct sk_buff *skb, + struct mt76_wcid *wcid, struct ieee80211_sta *sta, + bool *stop) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct mt76_queue *q = phy->q_tx[qid]; + struct mt76_dev *dev = phy->dev; + bool non_aql; + int pending; + int idx; + + non_aql = !info->tx_time_est; + idx = dev->queue_ops->tx_queue_skb(phy, q, qid, skb, wcid, sta); + if (idx < 0 || !sta) + return idx; + + q->entry[idx].wcid = wcid->idx; + + if (!non_aql) + return idx; + + pending = atomic_inc_return(&wcid->non_aql_packets); + if (stop && pending >= MT_MAX_NON_AQL_PKT) + *stop = true; + + return idx; +} + +void +mt76_tx(struct mt76_phy *phy, struct ieee80211_sta *sta, + struct mt76_wcid *wcid, struct sk_buff *skb) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr = (void *)skb->data; + struct sk_buff_head *head; + + if (mt76_testmode_enabled(phy)) { + ieee80211_free_txskb(phy->hw, skb); + return; + } + + if (WARN_ON(skb_get_queue_mapping(skb) >= MT_TXQ_PSD)) + skb_set_queue_mapping(skb, MT_TXQ_BE); + + if (wcid && !(wcid->tx_info & MT_WCID_TX_INFO_SET)) + ieee80211_get_tx_rates(info->control.vif, sta, skb, + info->control.rates, 1); + + info->hw_queue |= FIELD_PREP(MT_TX_HW_QUEUE_PHY, phy->band_idx); + + if ((info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) || + ((info->control.flags & IEEE80211_TX_CTRL_DONT_USE_RATE_MASK) && + ieee80211_is_probe_req(hdr->frame_control))) + head = &wcid->tx_offchannel; + else + head = &wcid->tx_pending; + + spin_lock_bh(&head->lock); + __skb_queue_tail(head, skb); + spin_unlock_bh(&head->lock); + + spin_lock_bh(&phy->tx_lock); + if (list_empty(&wcid->tx_list)) + list_add_tail(&wcid->tx_list, &phy->tx_list); + spin_unlock_bh(&phy->tx_lock); + + mt76_worker_schedule(&phy->dev->tx_worker); +} +EXPORT_SYMBOL_GPL(mt76_tx); + +static struct sk_buff * +mt76_txq_dequeue(struct mt76_phy *phy, struct mt76_txq *mtxq) +{ + struct ieee80211_txq *txq = mtxq_to_txq(mtxq); + struct ieee80211_tx_info *info; + struct sk_buff *skb; + + skb = ieee80211_tx_dequeue(phy->hw, txq); + if (!skb) + return NULL; + + info = IEEE80211_SKB_CB(skb); + info->hw_queue |= FIELD_PREP(MT_TX_HW_QUEUE_PHY, phy->band_idx); + + return skb; +} + +static void +mt76_queue_ps_skb(struct mt76_phy *phy, struct ieee80211_sta *sta, + struct sk_buff *skb, bool last, + enum ieee80211_frame_release_type reason) +{ + struct mt76_wcid *wcid = (struct mt76_wcid *)sta->drv_priv; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + + info->control.flags |= IEEE80211_TX_CTRL_PS_RESPONSE; + if (last) { + info->flags |= IEEE80211_TX_STATUS_EOSP | + IEEE80211_TX_CTL_REQ_TX_STATUS; + + if (reason == IEEE80211_FRAME_RELEASE_UAPSD && + ieee80211_is_data_qos(hdr->frame_control)) + *ieee80211_get_qos_ctl(hdr) |= IEEE80211_QOS_CTL_EOSP; + } + + mt76_skb_set_moredata(skb, !last); + __mt76_tx_queue_skb(phy, MT_TXQ_PSD, skb, wcid, sta, NULL); +} + +void +mt76_release_buffered_frames(struct ieee80211_hw *hw, struct ieee80211_sta *sta, + u16 tids, int nframes, + enum ieee80211_frame_release_type reason, + bool more_data) +{ + struct mt76_phy *phy = hw->priv; + struct mt76_dev *dev = phy->dev; + struct sk_buff *last_skb = NULL; + struct mt76_queue *hwq = phy->q_tx[MT_TXQ_PSD]; + int i; + + spin_lock_bh(&hwq->lock); + for (i = 0; tids && nframes; i++, tids >>= 1) { + struct ieee80211_txq *txq = sta->txq[i]; + struct mt76_txq *mtxq = (struct mt76_txq *)txq->drv_priv; + struct sk_buff *skb; + + if (!(tids & 1)) + continue; + + do { + skb = mt76_txq_dequeue(phy, mtxq); + if (!skb) + break; + + nframes--; + if (last_skb) + mt76_queue_ps_skb(phy, sta, last_skb, false, + reason); + + last_skb = skb; + } while (nframes); + } + + if (last_skb) { + mt76_queue_ps_skb(phy, sta, last_skb, true, reason); + dev->queue_ops->kick(dev, hwq); + } else { + ieee80211_sta_eosp(sta); + } + + spin_unlock_bh(&hwq->lock); +} +EXPORT_SYMBOL_GPL(mt76_release_buffered_frames); + +static bool +mt76_txq_stopped(struct mt76_queue *q) +{ + return q->stopped || q->blocked || + q->queued + MT_TXQ_FREE_THR >= q->ndesc; +} + +static int +mt76_txq_send_burst(struct mt76_phy *phy, struct mt76_queue *q, + struct mt76_txq *mtxq, struct mt76_wcid *wcid) +{ + struct mt76_dev *dev = phy->dev; + struct ieee80211_txq *txq = mtxq_to_txq(mtxq); + enum mt76_txq_id qid = mt76_txq_get_qid(txq); + struct ieee80211_tx_info *info; + struct sk_buff *skb; + int n_frames = 1; + bool stop = false; + int idx; + + if (test_bit(MT_WCID_FLAG_PS, &wcid->flags)) { + if (!(dev->drv->drv_flags & MT_DRV_HW_PS_BUFFERING)) + return 0; + if (ieee80211_txq_aql_pending(phy->hw, txq)) + return 0; + } + + if (atomic_read(&wcid->non_aql_packets) >= MT_MAX_NON_AQL_PKT) + return 0; + + skb = mt76_txq_dequeue(phy, mtxq); + if (!skb) + return 0; + + info = IEEE80211_SKB_CB(skb); + if (!(wcid->tx_info & MT_WCID_TX_INFO_SET)) + ieee80211_get_tx_rates(txq->vif, txq->sta, skb, + info->control.rates, 1); + + spin_lock(&q->lock); + idx = __mt76_tx_queue_skb(phy, qid, skb, wcid, txq->sta, &stop); + spin_unlock(&q->lock); + if (idx < 0) + return idx; + + if (test_bit(MT_WCID_FLAG_PS, &wcid->flags)) + goto out; + + do { + if (test_bit(MT76_RESET, &phy->state) || phy->offchannel) + break; + + if (stop || mt76_txq_stopped(q)) + break; + + skb = mt76_txq_dequeue(phy, mtxq); + if (!skb) + break; + + info = IEEE80211_SKB_CB(skb); + if (!(wcid->tx_info & MT_WCID_TX_INFO_SET)) + ieee80211_get_tx_rates(txq->vif, txq->sta, skb, + info->control.rates, 1); + + spin_lock(&q->lock); + idx = __mt76_tx_queue_skb(phy, qid, skb, wcid, txq->sta, &stop); + spin_unlock(&q->lock); + if (idx < 0) + break; + + n_frames++; + } while (1); + +out: + spin_lock(&q->lock); + dev->queue_ops->kick(dev, q); + spin_unlock(&q->lock); + + return n_frames; +} + +static int +mt76_txq_schedule_list(struct mt76_phy *phy, enum mt76_txq_id qid) +{ + struct mt76_dev *dev = phy->dev; + struct ieee80211_txq *txq; + struct mt76_txq *mtxq; + struct mt76_wcid *wcid; + struct mt76_queue *q; + int ret = 0; + + while (1) { + int n_frames = 0; + + txq = ieee80211_next_txq(phy->hw, qid); + if (!txq) + break; + + mtxq = (struct mt76_txq *)txq->drv_priv; + wcid = __mt76_wcid_ptr(dev, mtxq->wcid); + if (!wcid) + continue; + + phy = mt76_dev_phy(dev, wcid->phy_idx); + if (test_bit(MT76_RESET, &phy->state) || phy->offchannel) + continue; + + q = phy->q_tx[qid]; + + if (test_bit(MT_WCID_FLAG_PS, &wcid->flags)) { + if (!(dev->drv->drv_flags & MT_DRV_HW_PS_BUFFERING)) + continue; + + if (!mt76_txq_stopped(q)) + n_frames = mt76_txq_send_burst(phy, q, mtxq, wcid); + + ieee80211_return_txq(phy->hw, txq, false); + + if (unlikely(n_frames < 0)) + return n_frames; + ret += n_frames; + continue; + } + + if (atomic_read(&wcid->non_aql_packets) >= MT_MAX_NON_AQL_PKT) + continue; + if (dev->queue_ops->tx_cleanup && + q->queued + 2 * MT_TXQ_FREE_THR >= q->ndesc) { + dev->queue_ops->tx_cleanup(dev, q, false); + } + + if (mtxq->send_bar && mtxq->aggr) { + struct ieee80211_txq *txq = mtxq_to_txq(mtxq); + struct ieee80211_sta *sta = txq->sta; + struct ieee80211_vif *vif = txq->vif; + u16 agg_ssn = mtxq->agg_ssn; + u8 tid = txq->tid; + + mtxq->send_bar = false; + ieee80211_send_bar(vif, sta->addr, tid, agg_ssn); + } + + if (!mt76_txq_stopped(q)) + n_frames = mt76_txq_send_burst(phy, q, mtxq, wcid); + + ieee80211_return_txq(phy->hw, txq, false); + + if (unlikely(n_frames < 0)) + return n_frames; + + ret += n_frames; + } + + return ret; +} + +void mt76_txq_schedule(struct mt76_phy *phy, enum mt76_txq_id qid) +{ + int len; + + if (qid >= 4) + return; + + local_bh_disable(); + rcu_read_lock(); + + do { + ieee80211_txq_schedule_start(phy->hw, qid); + len = mt76_txq_schedule_list(phy, qid); + ieee80211_txq_schedule_end(phy->hw, qid); + } while (len > 0); + + rcu_read_unlock(); + local_bh_enable(); +} +EXPORT_SYMBOL_GPL(mt76_txq_schedule); + +static int +mt76_txq_schedule_pending_wcid(struct mt76_phy *phy, struct mt76_wcid *wcid, + struct sk_buff_head *head) +{ + struct mt76_dev *dev = phy->dev; + struct ieee80211_sta *sta; + struct mt76_queue *q; + struct sk_buff *skb; + int ret = 0; + + spin_lock(&head->lock); + while ((skb = skb_peek(head)) != NULL) { + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + int qid = skb_get_queue_mapping(skb); + + if ((dev->drv->drv_flags & MT_DRV_HW_MGMT_TXQ) && + !(info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP) && + !ieee80211_is_data_present(hdr->frame_control) && + (!ieee80211_is_bufferable_mmpdu(skb) || + ieee80211_is_deauth(hdr->frame_control) || + ieee80211_is_disassoc(hdr->frame_control) || + head == &wcid->tx_offchannel)) + qid = MT_TXQ_PSD; + + q = phy->q_tx[qid]; + if (mt76_txq_stopped(q) || test_bit(MT76_RESET, &phy->state)) { + ret = -1; + break; + } + + __skb_unlink(skb, head); + spin_unlock(&head->lock); + + sta = wcid_to_sta(wcid); + spin_lock(&q->lock); + __mt76_tx_queue_skb(phy, qid, skb, wcid, sta, NULL); + dev->queue_ops->kick(dev, q); + spin_unlock(&q->lock); + + spin_lock(&head->lock); + } + spin_unlock(&head->lock); + + return ret; +} + +void mt76_txq_schedule_pending(struct mt76_phy *phy) +{ + LIST_HEAD(tx_list); + int ret = 0; + + if (list_empty(&phy->tx_list)) + return; + + local_bh_disable(); + rcu_read_lock(); + + spin_lock(&phy->tx_lock); + list_splice_init(&phy->tx_list, &tx_list); + while (!list_empty(&tx_list)) { + struct mt76_wcid *wcid; + + wcid = list_first_entry(&tx_list, struct mt76_wcid, tx_list); + list_del_init(&wcid->tx_list); + + spin_unlock(&phy->tx_lock); + if (ret >= 0) + ret = mt76_txq_schedule_pending_wcid(phy, wcid, &wcid->tx_offchannel); + if (ret >= 0 && !phy->offchannel) + ret = mt76_txq_schedule_pending_wcid(phy, wcid, &wcid->tx_pending); + spin_lock(&phy->tx_lock); + + if ((!skb_queue_empty(&wcid->tx_pending) || + !skb_queue_empty(&wcid->tx_offchannel)) && + list_empty(&wcid->tx_list)) + list_add_tail(&wcid->tx_list, &phy->tx_list); + } + spin_unlock(&phy->tx_lock); + + rcu_read_unlock(); + local_bh_enable(); +} + +void mt76_txq_schedule_all(struct mt76_phy *phy) +{ + struct mt76_phy *main_phy = &phy->dev->phy; + int i; + + mt76_txq_schedule_pending(phy); + + if (phy != main_phy && phy->hw == main_phy->hw) + return; + + for (i = 0; i <= MT_TXQ_BK; i++) + mt76_txq_schedule(phy, i); +} +EXPORT_SYMBOL_GPL(mt76_txq_schedule_all); + +void mt76_tx_worker_run(struct mt76_dev *dev) +{ + struct mt76_phy *phy; + int i; + + mt76_txq_schedule_all(&dev->phy); + for (i = 0; i < ARRAY_SIZE(dev->phys); i++) { + phy = dev->phys[i]; + if (!phy) + continue; + + mt76_txq_schedule_all(phy); + } + +#ifdef CONFIG_NL80211_TESTMODE + for (i = 0; i < ARRAY_SIZE(dev->phys); i++) { + phy = dev->phys[i]; + if (!phy || !phy->test.tx_pending) + continue; + + mt76_testmode_tx_pending(phy); + } +#endif +} +EXPORT_SYMBOL_GPL(mt76_tx_worker_run); + +void mt76_tx_worker(struct mt76_worker *w) +{ + struct mt76_dev *dev = container_of(w, struct mt76_dev, tx_worker); + + mt76_tx_worker_run(dev); +} + +void mt76_stop_tx_queues(struct mt76_phy *phy, struct ieee80211_sta *sta, + bool send_bar) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(sta->txq); i++) { + struct ieee80211_txq *txq = sta->txq[i]; + struct mt76_queue *hwq; + struct mt76_txq *mtxq; + + if (!txq) + continue; + + hwq = phy->q_tx[mt76_txq_get_qid(txq)]; + mtxq = (struct mt76_txq *)txq->drv_priv; + + spin_lock_bh(&hwq->lock); + mtxq->send_bar = mtxq->aggr && send_bar; + spin_unlock_bh(&hwq->lock); + } +} +EXPORT_SYMBOL_GPL(mt76_stop_tx_queues); + +void mt76_sta_ps_transition(struct mt76_dev *dev, struct mt76_wcid *wcid, + bool ps) +{ + struct ieee80211_sta *sta; + struct ieee80211_hw *hw; + int i; + + if (ps) { + set_bit(MT_WCID_FLAG_PS, &wcid->flags); + mt76_worker_schedule(&dev->tx_worker); + return; + } + + clear_bit(MT_WCID_FLAG_PS, &wcid->flags); + + sta = wcid_to_sta(wcid); + if (!sta) + return; + + hw = mt76_phy_hw(dev, wcid->phy_idx); + for (i = 0; i < ARRAY_SIZE(sta->txq); i++) + if (sta->txq[i]) + ieee80211_schedule_txq(hw, sta->txq[i]); + mt76_worker_schedule(&dev->tx_worker); +} +EXPORT_SYMBOL_GPL(mt76_sta_ps_transition); + +void mt76_wake_tx_queue(struct ieee80211_hw *hw, struct ieee80211_txq *txq) +{ + struct mt76_phy *phy = hw->priv; + struct mt76_dev *dev = phy->dev; + + mt76_worker_schedule(&dev->tx_worker); +} +EXPORT_SYMBOL_GPL(mt76_wake_tx_queue); + +u8 mt76_ac_to_hwq(u8 ac) +{ + static const u8 wmm_queue_map[] = { + [IEEE80211_AC_BE] = 0, + [IEEE80211_AC_BK] = 1, + [IEEE80211_AC_VI] = 2, + [IEEE80211_AC_VO] = 3, + }; + + if (WARN_ON(ac >= IEEE80211_NUM_ACS)) + return 0; + + return wmm_queue_map[ac]; +} +EXPORT_SYMBOL_GPL(mt76_ac_to_hwq); + +int mt76_skb_adjust_pad(struct sk_buff *skb, int pad) +{ + struct sk_buff *iter, *last = skb; + + /* First packet of a A-MSDU burst keeps track of the whole burst + * length, need to update length of it and the last packet. + */ + skb_walk_frags(skb, iter) { + last = iter; + if (!iter->next) { + skb->data_len += pad; + skb->len += pad; + break; + } + } + + if (skb_pad(last, pad)) + return -ENOMEM; + + __skb_put(last, pad); + + return 0; +} +EXPORT_SYMBOL_GPL(mt76_skb_adjust_pad); + +void mt76_queue_tx_complete(struct mt76_dev *dev, struct mt76_queue *q, + struct mt76_queue_entry *e) +{ + if (e->skb) + dev->drv->tx_complete_skb(dev, e); + + spin_lock_bh(&q->lock); + q->tail = (q->tail + 1) % q->ndesc; + q->queued--; + spin_unlock_bh(&q->lock); +} +EXPORT_SYMBOL_GPL(mt76_queue_tx_complete); + +void __mt76_set_tx_blocked(struct mt76_dev *dev, bool blocked) +{ + struct mt76_phy *phy = &dev->phy; + struct mt76_queue *q = phy->q_tx[0]; + + if (blocked == q->blocked) + return; + + q->blocked = blocked; + + phy = dev->phys[MT_BAND1]; + if (phy) { + q = phy->q_tx[0]; + q->blocked = blocked; + } + phy = dev->phys[MT_BAND2]; + if (phy) { + q = phy->q_tx[0]; + q->blocked = blocked; + } + + if (!blocked) + mt76_worker_schedule(&dev->tx_worker); +} +EXPORT_SYMBOL_GPL(__mt76_set_tx_blocked); + +int mt76_token_consume(struct mt76_dev *dev, struct mt76_txwi_cache **ptxwi) +{ + int token; + + spin_lock_bh(&dev->token_lock); + + token = idr_alloc(&dev->token, *ptxwi, dev->token_start, + dev->token_start + dev->token_size, + GFP_ATOMIC); + if (token >= dev->token_start) { + dev->token_count++; + + if ((*ptxwi)->qid == MT_TXQ_PSD) { + struct mt76_phy *mphy = mt76_dev_phy(dev, (*ptxwi)->phy_idx); + atomic_inc(&mphy->mgmt_tx_pending); + } + } + +#ifdef CONFIG_NET_MEDIATEK_SOC_WED + if (mtk_wed_device_active(&dev->mmio.wed) && + token >= dev->mmio.wed.wlan.token_start) + dev->wed_token_count++; +#endif + + if (dev->token_count >= dev->token_size - MT76_TOKEN_FREE_THR) + __mt76_set_tx_blocked(dev, true); + + spin_unlock_bh(&dev->token_lock); + + return token; +} +EXPORT_SYMBOL_GPL(mt76_token_consume); + +int mt76_rx_token_consume(struct mt76_dev *dev, void *ptr, + struct mt76_txwi_cache *t, dma_addr_t phys) +{ + int token; + + spin_lock_bh(&dev->rx_token_lock); + token = idr_alloc(&dev->rx_token, t, 0, dev->rx_token_size, + GFP_ATOMIC); + if (token >= 0) { + t->ptr = ptr; + t->dma_addr = phys; + } + spin_unlock_bh(&dev->rx_token_lock); + + return token; +} +EXPORT_SYMBOL_GPL(mt76_rx_token_consume); + +struct mt76_txwi_cache * +mt76_token_release(struct mt76_dev *dev, int token, bool *wake) +{ + struct mt76_txwi_cache *txwi; + + spin_lock_bh(&dev->token_lock); + + txwi = idr_remove(&dev->token, token); + if (txwi) { + dev->token_count--; + + if (txwi->qid == MT_TXQ_PSD) { + struct mt76_phy *mphy = mt76_dev_phy(dev, txwi->phy_idx); + if (atomic_dec_and_test(&mphy->mgmt_tx_pending)) + wake_up(&dev->tx_wait); + } + +#ifdef CONFIG_NET_MEDIATEK_SOC_WED + if (mtk_wed_device_active(&dev->mmio.wed) && + token >= dev->mmio.wed.wlan.token_start && + --dev->wed_token_count == 0) + wake_up(&dev->tx_wait); +#endif + } + + if (wake && dev->token_count < dev->token_size - MT76_TOKEN_FREE_THR && + dev->phy.q_tx[0]->blocked) + *wake = true; + + spin_unlock_bh(&dev->token_lock); + + return txwi; +} +EXPORT_SYMBOL_GPL(mt76_token_release); + +struct mt76_txwi_cache * +mt76_rx_token_release(struct mt76_dev *dev, int token) +{ + struct mt76_txwi_cache *t; + + spin_lock_bh(&dev->rx_token_lock); + t = idr_remove(&dev->rx_token, token); + spin_unlock_bh(&dev->rx_token_lock); + + return t; +} +EXPORT_SYMBOL_GPL(mt76_rx_token_release); diff --git a/drivers/net/wireless/mediatek/mt76/usb.c b/drivers/net/wireless/mediatek/mt76/usb.c new file mode 100644 index 000000000..a9af3aa6b --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/usb.c @@ -0,0 +1,1202 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (C) 2018 Lorenzo Bianconi + */ + +#include +#include "mt76.h" +#include "usb_trace.h" +#include "dma.h" + +#define MT_VEND_REQ_MAX_RETRY 10 +#define MT_VEND_REQ_TOUT_MS 300 + +static bool disable_usb_sg; +module_param_named(disable_usb_sg, disable_usb_sg, bool, 0644); +MODULE_PARM_DESC(disable_usb_sg, "Disable usb scatter-gather support"); + +int __mt76u_vendor_request(struct mt76_dev *dev, u8 req, u8 req_type, + u16 val, u16 offset, void *buf, size_t len) +{ + struct usb_interface *uintf = to_usb_interface(dev->dev); + struct usb_device *udev = interface_to_usbdev(uintf); + unsigned int pipe; + int i, ret; + + lockdep_assert_held(&dev->usb.usb_ctrl_mtx); + + pipe = (req_type & USB_DIR_IN) ? usb_rcvctrlpipe(udev, 0) + : usb_sndctrlpipe(udev, 0); + for (i = 0; i < MT_VEND_REQ_MAX_RETRY; i++) { + if (test_bit(MT76_REMOVED, &dev->phy.state)) + return -EIO; + if (dev->usb.ctrl_timeout && atomic_read(&dev->bus_hung)) + return -EIO; + + ret = usb_control_msg(udev, pipe, req, req_type, val, + offset, buf, len, MT_VEND_REQ_TOUT_MS); + if (ret == -ENODEV || ret == -EPROTO) + set_bit(MT76_REMOVED, &dev->phy.state); + if (ret >= 0 || ret == -ENODEV || ret == -EPROTO) + return ret; + usleep_range(5000, 10000); + } + + dev_err(dev->dev, "vendor request req:%02x off:%04x failed:%d\n", + req, offset, ret); + + if (dev->usb.ctrl_timeout) { + atomic_set(&dev->bus_hung, true); + dev_err(dev->dev, "vendor request req:%02x off:%04x timed out, marking bus hung\n", + req, offset); + dev->usb.ctrl_timeout(dev, ret); + return ret; + } + + return ret; +} +EXPORT_SYMBOL_GPL(__mt76u_vendor_request); + +int mt76u_vendor_request(struct mt76_dev *dev, u8 req, + u8 req_type, u16 val, u16 offset, + void *buf, size_t len) +{ + int ret; + + mutex_lock(&dev->usb.usb_ctrl_mtx); + ret = __mt76u_vendor_request(dev, req, req_type, + val, offset, buf, len); + trace_usb_reg_wr(dev, offset, val); + mutex_unlock(&dev->usb.usb_ctrl_mtx); + + return ret; +} +EXPORT_SYMBOL_GPL(mt76u_vendor_request); + +u32 ___mt76u_rr(struct mt76_dev *dev, u8 req, u8 req_type, u32 addr) +{ + struct mt76_usb *usb = &dev->usb; + u32 data = ~0; + int ret; + + ret = __mt76u_vendor_request(dev, req, req_type, addr >> 16, + addr, usb->data, sizeof(__le32)); + if (ret == sizeof(__le32)) + data = get_unaligned_le32(usb->data); + trace_usb_reg_rr(dev, addr, data); + + return data; +} +EXPORT_SYMBOL_GPL(___mt76u_rr); + +static u32 __mt76u_rr(struct mt76_dev *dev, u32 addr) +{ + u8 req; + + switch (addr & MT_VEND_TYPE_MASK) { + case MT_VEND_TYPE_EEPROM: + req = MT_VEND_READ_EEPROM; + break; + case MT_VEND_TYPE_CFG: + req = MT_VEND_READ_CFG; + break; + default: + req = MT_VEND_MULTI_READ; + break; + } + + return ___mt76u_rr(dev, req, USB_DIR_IN | USB_TYPE_VENDOR, + addr & ~MT_VEND_TYPE_MASK); +} + +static u32 mt76u_rr(struct mt76_dev *dev, u32 addr) +{ + u32 ret; + + mutex_lock(&dev->usb.usb_ctrl_mtx); + ret = __mt76u_rr(dev, addr); + mutex_unlock(&dev->usb.usb_ctrl_mtx); + + return ret; +} + +void ___mt76u_wr(struct mt76_dev *dev, u8 req, u8 req_type, + u32 addr, u32 val) +{ + struct mt76_usb *usb = &dev->usb; + + put_unaligned_le32(val, usb->data); + __mt76u_vendor_request(dev, req, req_type, addr >> 16, + addr, usb->data, sizeof(__le32)); + trace_usb_reg_wr(dev, addr, val); +} +EXPORT_SYMBOL_GPL(___mt76u_wr); + +static void __mt76u_wr(struct mt76_dev *dev, u32 addr, u32 val) +{ + u8 req; + + switch (addr & MT_VEND_TYPE_MASK) { + case MT_VEND_TYPE_CFG: + req = MT_VEND_WRITE_CFG; + break; + default: + req = MT_VEND_MULTI_WRITE; + break; + } + ___mt76u_wr(dev, req, USB_DIR_OUT | USB_TYPE_VENDOR, + addr & ~MT_VEND_TYPE_MASK, val); +} + +static void mt76u_wr(struct mt76_dev *dev, u32 addr, u32 val) +{ + mutex_lock(&dev->usb.usb_ctrl_mtx); + __mt76u_wr(dev, addr, val); + mutex_unlock(&dev->usb.usb_ctrl_mtx); +} + +static u32 mt76u_rmw(struct mt76_dev *dev, u32 addr, + u32 mask, u32 val) +{ + mutex_lock(&dev->usb.usb_ctrl_mtx); + val |= __mt76u_rr(dev, addr) & ~mask; + __mt76u_wr(dev, addr, val); + mutex_unlock(&dev->usb.usb_ctrl_mtx); + + return val; +} + +static void mt76u_copy(struct mt76_dev *dev, u32 offset, + const void *data, int len) +{ + struct mt76_usb *usb = &dev->usb; + const u8 *val = data; + int ret; + int current_batch_size; + int i = 0; + + /* Assure that always a multiple of 4 bytes are copied, + * otherwise beacons can be corrupted. + * See: "mt76: round up length on mt76_wr_copy" + * Commit 850e8f6fbd5d0003b0 + */ + len = round_up(len, 4); + + mutex_lock(&usb->usb_ctrl_mtx); + while (i < len) { + current_batch_size = min_t(int, usb->data_len, len - i); + memcpy(usb->data, val + i, current_batch_size); + ret = __mt76u_vendor_request(dev, MT_VEND_MULTI_WRITE, + USB_DIR_OUT | USB_TYPE_VENDOR, + 0, offset + i, usb->data, + current_batch_size); + if (ret < 0) + break; + + i += current_batch_size; + } + mutex_unlock(&usb->usb_ctrl_mtx); +} + +void mt76u_read_copy(struct mt76_dev *dev, u32 offset, + void *data, int len) +{ + struct mt76_usb *usb = &dev->usb; + int i = 0, batch_len, ret; + u8 *val = data; + + len = round_up(len, 4); + mutex_lock(&usb->usb_ctrl_mtx); + while (i < len) { + batch_len = min_t(int, usb->data_len, len - i); + ret = __mt76u_vendor_request(dev, MT_VEND_READ_EXT, + USB_DIR_IN | USB_TYPE_VENDOR, + (offset + i) >> 16, offset + i, + usb->data, batch_len); + if (ret < 0) + break; + + memcpy(val + i, usb->data, batch_len); + i += batch_len; + } + mutex_unlock(&usb->usb_ctrl_mtx); +} +EXPORT_SYMBOL_GPL(mt76u_read_copy); + +void mt76u_single_wr(struct mt76_dev *dev, const u8 req, + const u16 offset, const u32 val) +{ + mutex_lock(&dev->usb.usb_ctrl_mtx); + __mt76u_vendor_request(dev, req, + USB_DIR_OUT | USB_TYPE_VENDOR, + val & 0xffff, offset, NULL, 0); + __mt76u_vendor_request(dev, req, + USB_DIR_OUT | USB_TYPE_VENDOR, + val >> 16, offset + 2, NULL, 0); + mutex_unlock(&dev->usb.usb_ctrl_mtx); +} +EXPORT_SYMBOL_GPL(mt76u_single_wr); + +static int +mt76u_req_wr_rp(struct mt76_dev *dev, u32 base, + const struct mt76_reg_pair *data, int len) +{ + struct mt76_usb *usb = &dev->usb; + + mutex_lock(&usb->usb_ctrl_mtx); + while (len > 0) { + __mt76u_wr(dev, base + data->reg, data->value); + len--; + data++; + } + mutex_unlock(&usb->usb_ctrl_mtx); + + return 0; +} + +static int +mt76u_wr_rp(struct mt76_dev *dev, u32 base, + const struct mt76_reg_pair *data, int n) +{ + if (test_bit(MT76_STATE_MCU_RUNNING, &dev->phy.state)) + return dev->mcu_ops->mcu_wr_rp(dev, base, data, n); + else + return mt76u_req_wr_rp(dev, base, data, n); +} + +static int +mt76u_req_rd_rp(struct mt76_dev *dev, u32 base, struct mt76_reg_pair *data, + int len) +{ + struct mt76_usb *usb = &dev->usb; + + mutex_lock(&usb->usb_ctrl_mtx); + while (len > 0) { + data->value = __mt76u_rr(dev, base + data->reg); + len--; + data++; + } + mutex_unlock(&usb->usb_ctrl_mtx); + + return 0; +} + +static int +mt76u_rd_rp(struct mt76_dev *dev, u32 base, + struct mt76_reg_pair *data, int n) +{ + if (test_bit(MT76_STATE_MCU_RUNNING, &dev->phy.state)) + return dev->mcu_ops->mcu_rd_rp(dev, base, data, n); + else + return mt76u_req_rd_rp(dev, base, data, n); +} + +static bool mt76u_check_sg(struct mt76_dev *dev) +{ + struct usb_interface *uintf = to_usb_interface(dev->dev); + struct usb_device *udev = interface_to_usbdev(uintf); + + return (!disable_usb_sg && udev->bus->sg_tablesize > 0 && + udev->bus->no_sg_constraint); +} + +static int +mt76u_set_endpoints(struct usb_interface *intf, + struct mt76_usb *usb) +{ + struct usb_host_interface *intf_desc = intf->cur_altsetting; + struct usb_endpoint_descriptor *ep_desc; + int i, in_ep = 0, out_ep = 0; + + for (i = 0; i < intf_desc->desc.bNumEndpoints; i++) { + ep_desc = &intf_desc->endpoint[i].desc; + + if (usb_endpoint_is_bulk_in(ep_desc) && + in_ep < __MT_EP_IN_MAX) { + usb->in_ep[in_ep] = usb_endpoint_num(ep_desc); + in_ep++; + } else if (usb_endpoint_is_bulk_out(ep_desc) && + out_ep < __MT_EP_OUT_MAX) { + usb->out_ep[out_ep] = usb_endpoint_num(ep_desc); + out_ep++; + } + } + + if (in_ep != __MT_EP_IN_MAX || out_ep != __MT_EP_OUT_MAX) + return -EINVAL; + return 0; +} + +static int +mt76u_fill_rx_sg(struct mt76_dev *dev, struct mt76_queue *q, struct urb *urb, + int nsgs) +{ + int i; + + for (i = 0; i < nsgs; i++) { + void *data; + int offset; + + data = mt76_get_page_pool_buf(q, &offset, q->buf_size); + if (!data) + break; + + sg_set_page(&urb->sg[i], virt_to_head_page(data), q->buf_size, + offset); + } + + if (i < nsgs) { + int j; + + for (j = nsgs; j < urb->num_sgs; j++) + mt76_put_page_pool_buf(sg_virt(&urb->sg[j]), false); + urb->num_sgs = i; + } + + urb->num_sgs = max_t(int, i, urb->num_sgs); + urb->transfer_buffer_length = urb->num_sgs * q->buf_size; + sg_init_marker(urb->sg, urb->num_sgs); + + return i ? : -ENOMEM; +} + +static int +mt76u_refill_rx(struct mt76_dev *dev, struct mt76_queue *q, + struct urb *urb, int nsgs) +{ + enum mt76_rxq_id qid = q - &dev->q_rx[MT_RXQ_MAIN]; + int offset; + + if (qid == MT_RXQ_MAIN && dev->usb.sg_en) + return mt76u_fill_rx_sg(dev, q, urb, nsgs); + + urb->transfer_buffer_length = q->buf_size; + urb->transfer_buffer = mt76_get_page_pool_buf(q, &offset, q->buf_size); + + return urb->transfer_buffer ? 0 : -ENOMEM; +} + +static int +mt76u_urb_alloc(struct mt76_dev *dev, struct mt76_queue_entry *e, + int sg_max_size) +{ + unsigned int size = sizeof(struct urb); + + if (dev->usb.sg_en) + size += sg_max_size * sizeof(struct scatterlist); + + e->urb = kzalloc(size, GFP_KERNEL); + if (!e->urb) + return -ENOMEM; + + usb_init_urb(e->urb); + + if (dev->usb.sg_en && sg_max_size > 0) + e->urb->sg = (struct scatterlist *)(e->urb + 1); + + return 0; +} + +static int +mt76u_rx_urb_alloc(struct mt76_dev *dev, struct mt76_queue *q, + struct mt76_queue_entry *e) +{ + enum mt76_rxq_id qid = q - &dev->q_rx[MT_RXQ_MAIN]; + int err, sg_size; + + sg_size = qid == MT_RXQ_MAIN ? MT_RX_SG_MAX_SIZE : 0; + err = mt76u_urb_alloc(dev, e, sg_size); + if (err) + return err; + + return mt76u_refill_rx(dev, q, e->urb, sg_size); +} + +static void mt76u_urb_free(struct urb *urb) +{ + int i; + + for (i = 0; i < urb->num_sgs; i++) + mt76_put_page_pool_buf(sg_virt(&urb->sg[i]), false); + + if (urb->transfer_buffer) + mt76_put_page_pool_buf(urb->transfer_buffer, false); + + usb_free_urb(urb); +} + +static void +mt76u_fill_bulk_urb(struct mt76_dev *dev, int dir, int index, + struct urb *urb, usb_complete_t complete_fn, + void *context) +{ + struct usb_interface *uintf = to_usb_interface(dev->dev); + struct usb_device *udev = interface_to_usbdev(uintf); + unsigned int pipe; + + if (dir == USB_DIR_IN) + pipe = usb_rcvbulkpipe(udev, dev->usb.in_ep[index]); + else + pipe = usb_sndbulkpipe(udev, dev->usb.out_ep[index]); + + urb->dev = udev; + urb->pipe = pipe; + urb->complete = complete_fn; + urb->context = context; +} + +static struct urb * +mt76u_get_next_rx_entry(struct mt76_queue *q) +{ + struct urb *urb = NULL; + unsigned long flags; + + spin_lock_irqsave(&q->lock, flags); + if (q->queued > 0) { + urb = q->entry[q->tail].urb; + q->tail = (q->tail + 1) % q->ndesc; + q->queued--; + } + spin_unlock_irqrestore(&q->lock, flags); + + return urb; +} + +static int +mt76u_get_rx_entry_len(struct mt76_dev *dev, u8 *data, + u32 data_len) +{ + u16 dma_len, min_len; + + dma_len = get_unaligned_le16(data); + if (dev->drv->drv_flags & MT_DRV_RX_DMA_HDR) + return dma_len; + + min_len = MT_DMA_HDR_LEN + MT_RX_RXWI_LEN + MT_FCE_INFO_LEN; + if (data_len < min_len || !dma_len || + dma_len + MT_DMA_HDR_LEN > data_len || + (dma_len & 0x3)) + return -EINVAL; + return dma_len; +} + +static struct sk_buff * +mt76u_build_rx_skb(struct mt76_dev *dev, void *data, + int len, int buf_size) +{ + int head_room, drv_flags = dev->drv->drv_flags; + struct sk_buff *skb; + + head_room = drv_flags & MT_DRV_RX_DMA_HDR ? 0 : MT_DMA_HDR_LEN; + if (SKB_WITH_OVERHEAD(buf_size) < head_room + len) { + struct page *page; + + /* slow path, not enough space for data and + * skb_shared_info + */ + skb = alloc_skb(MT_SKB_HEAD_LEN, GFP_ATOMIC); + if (!skb) + return NULL; + + skb_put_data(skb, data + head_room, MT_SKB_HEAD_LEN); + data += head_room + MT_SKB_HEAD_LEN; + page = virt_to_head_page(data); + skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, + page, data - page_address(page), + len - MT_SKB_HEAD_LEN, buf_size); + + return skb; + } + + /* fast path */ + skb = build_skb(data, buf_size); + if (!skb) + return NULL; + + skb_reserve(skb, head_room); + __skb_put(skb, len); + + return skb; +} + +static int +mt76u_process_rx_entry(struct mt76_dev *dev, struct urb *urb, + int buf_size) +{ + u8 *data = urb->num_sgs ? sg_virt(&urb->sg[0]) : urb->transfer_buffer; + int data_len = urb->num_sgs ? urb->sg[0].length : urb->actual_length; + int len, nsgs = 1, head_room, drv_flags = dev->drv->drv_flags; + struct sk_buff *skb; + + if (!test_bit(MT76_STATE_INITIALIZED, &dev->phy.state)) + return 0; + + len = mt76u_get_rx_entry_len(dev, data, urb->actual_length); + if (len < 0) + return 0; + + head_room = drv_flags & MT_DRV_RX_DMA_HDR ? 0 : MT_DMA_HDR_LEN; + data_len = min_t(int, len, data_len - head_room); + + if (len == data_len && + dev->drv->rx_check && !dev->drv->rx_check(dev, data, data_len)) + return 0; + + skb = mt76u_build_rx_skb(dev, data, data_len, buf_size); + if (!skb) + return 0; + + len -= data_len; + while (len > 0 && nsgs < urb->num_sgs) { + data_len = min_t(int, len, urb->sg[nsgs].length); + skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, + sg_page(&urb->sg[nsgs]), + urb->sg[nsgs].offset, data_len, + buf_size); + len -= data_len; + nsgs++; + } + + skb_mark_for_recycle(skb); + dev->drv->rx_skb(dev, MT_RXQ_MAIN, skb, NULL); + + return nsgs; +} + +static void mt76u_complete_rx(struct urb *urb) +{ + struct mt76_dev *dev = dev_get_drvdata(&urb->dev->dev); + struct mt76_queue *q = urb->context; + unsigned long flags; + + trace_rx_urb(dev, urb); + + switch (urb->status) { + case -ECONNRESET: + case -ESHUTDOWN: + case -ENOENT: + case -EPROTO: + return; + default: + dev_err_ratelimited(dev->dev, "rx urb failed: %d\n", + urb->status); + fallthrough; + case 0: + break; + } + + spin_lock_irqsave(&q->lock, flags); + if (WARN_ONCE(q->entry[q->head].urb != urb, "rx urb mismatch")) + goto out; + + q->head = (q->head + 1) % q->ndesc; + q->queued++; + + if (q == &dev->q_rx[MT_RXQ_MAIN]) + napi_schedule(&dev->napi[MT_RXQ_MAIN]); + else + mt76_worker_schedule(&dev->usb.rx_worker); +out: + spin_unlock_irqrestore(&q->lock, flags); +} + +static int +mt76u_submit_rx_buf(struct mt76_dev *dev, enum mt76_rxq_id qid, + struct urb *urb) +{ + int ep = qid == MT_RXQ_MAIN ? MT_EP_IN_PKT_RX : MT_EP_IN_CMD_RESP; + + mt76u_fill_bulk_urb(dev, USB_DIR_IN, ep, urb, + mt76u_complete_rx, &dev->q_rx[qid]); + trace_submit_urb(dev, urb); + + return usb_submit_urb(urb, GFP_ATOMIC); +} + +static void +mt76u_process_rx_queue(struct mt76_dev *dev, struct mt76_queue *q) +{ + int qid = q - &dev->q_rx[MT_RXQ_MAIN]; + struct urb *urb; + int err, count; + + while (true) { + urb = mt76u_get_next_rx_entry(q); + if (!urb) + break; + + count = mt76u_process_rx_entry(dev, urb, q->buf_size); + if (count > 0) { + err = mt76u_refill_rx(dev, q, urb, count); + if (err < 0) + break; + } + mt76u_submit_rx_buf(dev, qid, urb); + } +} + +/* Threaded NAPI poll for the MAIN RX queue: drain URBs, build skbs, resubmit, + * then deliver through napi_gro_receive() and let napi_complete() flush GRO. + */ +static int mt76u_napi_poll(struct napi_struct *napi, int budget) +{ + struct mt76_dev *dev = mt76_priv(napi->dev); + + rcu_read_lock(); + mt76u_process_rx_queue(dev, &dev->q_rx[MT_RXQ_MAIN]); + mt76_rx_poll_complete(dev, MT_RXQ_MAIN, napi); + rcu_read_unlock(); + + napi_complete(napi); + + return 0; +} + +static void mt76u_rx_worker(struct mt76_worker *w) +{ + struct mt76_usb *usb = container_of(w, struct mt76_usb, rx_worker); + struct mt76_dev *dev = container_of(usb, struct mt76_dev, usb); + int i; + + rcu_read_lock(); + mt76_for_each_q_rx(dev, i) { + /* MT_RXQ_MAIN is serviced by the threaded NAPI poll */ + if (i == MT_RXQ_MAIN) + continue; + + mt76u_process_rx_queue(dev, &dev->q_rx[i]); + } + rcu_read_unlock(); +} + +static int +mt76u_submit_rx_buffers(struct mt76_dev *dev, enum mt76_rxq_id qid) +{ + struct mt76_queue *q = &dev->q_rx[qid]; + unsigned long flags; + int i, err = 0; + + spin_lock_irqsave(&q->lock, flags); + for (i = 0; i < q->ndesc; i++) { + err = mt76u_submit_rx_buf(dev, qid, q->entry[i].urb); + if (err < 0) + break; + } + q->head = q->tail = 0; + q->queued = 0; + spin_unlock_irqrestore(&q->lock, flags); + + return err; +} + +static int +mt76u_alloc_rx_queue(struct mt76_dev *dev, enum mt76_rxq_id qid) +{ + struct mt76_queue *q = &dev->q_rx[qid]; + int i, err; + + err = mt76_create_page_pool(dev, q); + if (err) + return err; + + spin_lock_init(&q->lock); + q->entry = devm_kcalloc(dev->dev, + MT_NUM_RX_ENTRIES, sizeof(*q->entry), + GFP_KERNEL); + if (!q->entry) + return -ENOMEM; + + q->ndesc = MT_NUM_RX_ENTRIES; + q->buf_size = PAGE_SIZE; + + for (i = 0; i < q->ndesc; i++) { + err = mt76u_rx_urb_alloc(dev, q, &q->entry[i]); + if (err < 0) + return err; + } + + return mt76u_submit_rx_buffers(dev, qid); +} + +int mt76u_alloc_mcu_queue(struct mt76_dev *dev) +{ + return mt76u_alloc_rx_queue(dev, MT_RXQ_MCU); +} +EXPORT_SYMBOL_GPL(mt76u_alloc_mcu_queue); + +static void +mt76u_free_rx_queue(struct mt76_dev *dev, struct mt76_queue *q) +{ + int i; + + for (i = 0; i < q->ndesc; i++) { + if (!q->entry[i].urb) + continue; + + mt76u_urb_free(q->entry[i].urb); + q->entry[i].urb = NULL; + } + page_pool_destroy(q->page_pool); + q->page_pool = NULL; +} + +static void mt76u_free_rx(struct mt76_dev *dev) +{ + int i; + + mt76_worker_teardown(&dev->usb.rx_worker); + + mt76_for_each_q_rx(dev, i) + mt76u_free_rx_queue(dev, &dev->q_rx[i]); +} + +void mt76u_stop_rx(struct mt76_dev *dev) +{ + int i; + + mt76_worker_disable(&dev->usb.rx_worker); + + mt76_for_each_q_rx(dev, i) { + struct mt76_queue *q = &dev->q_rx[i]; + int j; + + for (j = 0; j < q->ndesc; j++) + usb_poison_urb(q->entry[j].urb); + } + + /* The MAIN queue napi stays enabled for the device lifetime. The URBs + * are now poisoned, so mt76u_complete_rx() can no longer reschedule it; + * just drain any in-flight poll before the caller frees or resets. + */ + if (dev->napi_dev) + napi_synchronize(&dev->napi[MT_RXQ_MAIN]); +} +EXPORT_SYMBOL_GPL(mt76u_stop_rx); + +int mt76u_resume_rx(struct mt76_dev *dev) +{ + int i; + + mt76_for_each_q_rx(dev, i) { + struct mt76_queue *q = &dev->q_rx[i]; + int err, j; + + for (j = 0; j < q->ndesc; j++) + usb_unpoison_urb(q->entry[j].urb); + + err = mt76u_submit_rx_buffers(dev, i); + if (err < 0) + return err; + } + + mt76_worker_enable(&dev->usb.rx_worker); + + return 0; +} +EXPORT_SYMBOL_GPL(mt76u_resume_rx); + +static void mt76u_status_worker(struct mt76_worker *w) +{ + struct mt76_usb *usb = container_of(w, struct mt76_usb, status_worker); + struct mt76_dev *dev = container_of(usb, struct mt76_dev, usb); + struct mt76_queue_entry entry; + struct mt76_queue *q; + int i; + + if (!test_bit(MT76_STATE_RUNNING, &dev->phy.state)) + return; + + for (i = 0; i <= MT_TXQ_PSD; i++) { + q = dev->phy.q_tx[i]; + if (!q) + continue; + + while (q->queued > 0) { + if (!q->entry[q->tail].done) + break; + + entry = q->entry[q->tail]; + q->entry[q->tail].done = false; + + mt76_queue_tx_complete(dev, q, &entry); + } + + if (!q->queued) + wake_up(&dev->tx_wait); + + mt76_worker_schedule(&dev->tx_worker); + } + + if (dev->drv->tx_status_data && + !test_and_set_bit(MT76_READING_STATS, &dev->phy.state)) + queue_work(dev->wq, &dev->usb.stat_work); +} + +static void mt76u_tx_status_data(struct work_struct *work) +{ + struct mt76_usb *usb; + struct mt76_dev *dev; + u8 update = 1; + u16 count = 0; + + usb = container_of(work, struct mt76_usb, stat_work); + dev = container_of(usb, struct mt76_dev, usb); + + while (true) { + if (test_bit(MT76_REMOVED, &dev->phy.state)) + break; + + if (!dev->drv->tx_status_data(dev, &update)) + break; + count++; + } + + if (count && test_bit(MT76_STATE_RUNNING, &dev->phy.state)) + queue_work(dev->wq, &usb->stat_work); + else + clear_bit(MT76_READING_STATS, &dev->phy.state); +} + +static void mt76u_complete_tx(struct urb *urb) +{ + struct mt76_dev *dev = dev_get_drvdata(&urb->dev->dev); + struct mt76_queue_entry *e = urb->context; + + if (mt76u_urb_error(urb)) + dev_err(dev->dev, "tx urb failed: %d\n", urb->status); + e->done = true; + + mt76_worker_schedule(&dev->usb.status_worker); +} + +static int +mt76u_tx_setup_buffers(struct mt76_dev *dev, struct sk_buff *skb, + struct urb *urb) +{ + urb->transfer_buffer_length = skb->len; + + if (!dev->usb.sg_en) { + urb->transfer_buffer = skb->data; + return 0; + } + + sg_init_table(urb->sg, MT_TX_SG_MAX_SIZE); + urb->num_sgs = skb_to_sgvec(skb, urb->sg, 0, skb->len); + if (!urb->num_sgs) + return -ENOMEM; + + return urb->num_sgs; +} + +static int +mt76u_tx_queue_skb(struct mt76_phy *phy, struct mt76_queue *q, + enum mt76_txq_id qid, struct sk_buff *skb, + struct mt76_wcid *wcid, struct ieee80211_sta *sta) +{ + struct mt76_tx_info tx_info = { + .skb = skb, + }; + struct mt76_dev *dev = phy->dev; + u16 idx = q->head; + int err; + + if (q->queued == q->ndesc) + return -ENOSPC; + + skb->prev = skb->next = NULL; + err = dev->drv->tx_prepare_skb(dev, NULL, qid, wcid, sta, &tx_info); + if (err < 0) + return err; + + err = mt76u_tx_setup_buffers(dev, tx_info.skb, q->entry[idx].urb); + if (err < 0) + return err; + + mt76u_fill_bulk_urb(dev, USB_DIR_OUT, q->ep, q->entry[idx].urb, + mt76u_complete_tx, &q->entry[idx]); + + q->head = (q->head + 1) % q->ndesc; + q->entry[idx].skb = tx_info.skb; + q->entry[idx].wcid = 0xffff; + q->queued++; + + return idx; +} + +static void mt76u_tx_kick(struct mt76_dev *dev, struct mt76_queue *q) +{ + struct urb *urb; + int err; + + while (q->first != q->head) { + urb = q->entry[q->first].urb; + + trace_submit_urb(dev, urb); + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err < 0) { + if (err == -ENODEV) + set_bit(MT76_REMOVED, &dev->phy.state); + else + dev_err(dev->dev, "tx urb submit failed:%d\n", + err); + break; + } + q->first = (q->first + 1) % q->ndesc; + } +} + +static void +mt76u_ac_to_hwq(struct mt76_dev *dev, struct mt76_queue *q, u8 qid) +{ + u8 ac = qid < IEEE80211_NUM_ACS ? qid : IEEE80211_AC_BE; + + switch (mt76_chip(dev)) { + case 0x7663: { + static const u8 lmac_queue_map[] = { + /* ac to lmac mapping */ + [IEEE80211_AC_BK] = 0, + [IEEE80211_AC_BE] = 1, + [IEEE80211_AC_VI] = 2, + [IEEE80211_AC_VO] = 4, + }; + + q->hw_idx = lmac_queue_map[ac]; + q->ep = q->hw_idx + 1; + break; + } + case 0x6639: + case 0x7902: + case 0x7961: + case 0x7925: + q->hw_idx = mt76_ac_to_hwq(ac); + q->ep = qid == MT_TXQ_PSD ? MT_EP_OUT_HCCA : q->hw_idx + 1; + break; + default: + q->hw_idx = mt76_ac_to_hwq(ac); + q->ep = q->hw_idx + 1; + break; + } +} + +static int mt76u_alloc_tx(struct mt76_dev *dev) +{ + int i; + + for (i = 0; i <= MT_TXQ_PSD; i++) { + struct mt76_queue *q; + int j, err; + + q = devm_kzalloc(dev->dev, sizeof(*q), GFP_KERNEL); + if (!q) + return -ENOMEM; + + spin_lock_init(&q->lock); + mt76u_ac_to_hwq(dev, q, i); + dev->phy.q_tx[i] = q; + + q->entry = devm_kcalloc(dev->dev, + MT_NUM_TX_ENTRIES, sizeof(*q->entry), + GFP_KERNEL); + if (!q->entry) + return -ENOMEM; + + q->ndesc = MT_NUM_TX_ENTRIES; + for (j = 0; j < q->ndesc; j++) { + err = mt76u_urb_alloc(dev, &q->entry[j], + MT_TX_SG_MAX_SIZE); + if (err < 0) + return err; + } + } + return 0; +} + +static void mt76u_free_tx(struct mt76_dev *dev) +{ + int i; + + mt76_worker_teardown(&dev->usb.status_worker); + + for (i = 0; i <= MT_TXQ_PSD; i++) { + struct mt76_queue *q; + int j; + + q = dev->phy.q_tx[i]; + if (!q) + continue; + + for (j = 0; j < q->ndesc; j++) { + usb_free_urb(q->entry[j].urb); + q->entry[j].urb = NULL; + } + } +} + +void mt76u_stop_tx(struct mt76_dev *dev) +{ + int ret; + + mt76_worker_disable(&dev->usb.status_worker); + + ret = wait_event_timeout(dev->tx_wait, !mt76_has_tx_pending(&dev->phy), + HZ / 5); + if (!ret) { + struct mt76_queue_entry entry; + struct mt76_queue *q; + int i, j; + + dev_err(dev->dev, "timed out waiting for pending tx\n"); + + for (i = 0; i <= MT_TXQ_PSD; i++) { + q = dev->phy.q_tx[i]; + if (!q) + continue; + + for (j = 0; j < q->ndesc; j++) + usb_kill_urb(q->entry[j].urb); + } + + mt76_worker_disable(&dev->tx_worker); + + /* On device removal we maight queue skb's, but mt76u_tx_kick() + * will fail to submit urb, cleanup those skb's manually. + */ + for (i = 0; i <= MT_TXQ_PSD; i++) { + q = dev->phy.q_tx[i]; + if (!q) + continue; + + while (q->queued > 0) { + entry = q->entry[q->tail]; + q->entry[q->tail].done = false; + mt76_queue_tx_complete(dev, q, &entry); + } + } + + mt76_worker_enable(&dev->tx_worker); + } + + cancel_work_sync(&dev->usb.stat_work); + clear_bit(MT76_READING_STATS, &dev->phy.state); + + mt76_worker_enable(&dev->usb.status_worker); + + mt76_tx_status_check(dev, true); +} +EXPORT_SYMBOL_GPL(mt76u_stop_tx); + +void mt76u_queues_deinit(struct mt76_dev *dev) +{ + mt76u_stop_rx(dev); + mt76u_stop_tx(dev); + + if (dev->napi_dev) { + napi_disable(&dev->napi[MT_RXQ_MAIN]); + netif_napi_del(&dev->napi[MT_RXQ_MAIN]); + free_netdev(dev->napi_dev); + dev->napi_dev = NULL; + } + + mt76u_free_rx(dev); + mt76u_free_tx(dev); +} +EXPORT_SYMBOL_GPL(mt76u_queues_deinit); + +int mt76u_alloc_queues(struct mt76_dev *dev) +{ + int err; + + err = mt76u_alloc_rx_queue(dev, MT_RXQ_MAIN); + if (err < 0) + return err; + + return mt76u_alloc_tx(dev); +} +EXPORT_SYMBOL_GPL(mt76u_alloc_queues); + +static const struct mt76_queue_ops usb_queue_ops = { + .tx_queue_skb = mt76u_tx_queue_skb, + .kick = mt76u_tx_kick, +}; + +int __mt76u_init(struct mt76_dev *dev, struct usb_interface *intf, + struct mt76_bus_ops *ops) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct mt76_usb *usb = &dev->usb; + struct mt76_dev **priv; + int err; + + INIT_WORK(&usb->stat_work, mt76u_tx_status_data); + + usb->data_len = usb_maxpacket(udev, usb_sndctrlpipe(udev, 0)); + if (usb->data_len < 32) + usb->data_len = 32; + + usb->data = devm_kmalloc(dev->dev, usb->data_len, GFP_KERNEL); + if (!usb->data) + return -ENOMEM; + + mutex_init(&usb->usb_ctrl_mtx); + dev->bus = ops; + dev->queue_ops = &usb_queue_ops; + + dev_set_drvdata(&udev->dev, dev); + + usb->sg_en = mt76u_check_sg(dev); + + err = mt76u_set_endpoints(intf, usb); + if (err < 0) + return err; + + err = mt76_worker_setup(dev->hw, &usb->rx_worker, mt76u_rx_worker, + "usb-rx"); + if (err) + return err; + + err = mt76_worker_setup(dev->hw, &usb->status_worker, + mt76u_status_worker, "usb-status"); + if (err) + return err; + + sched_set_fifo_low(usb->rx_worker.task); + sched_set_fifo_low(usb->status_worker.task); + + /* threaded NAPI on a dummy netdev (reusing mt76_dev's napi_dev/napi[]) + * services the MAIN RX queue and gives the RX path GRO + */ + dev->napi_dev = alloc_netdev_dummy(sizeof(struct mt76_dev *)); + if (!dev->napi_dev) + return -ENOMEM; + + priv = netdev_priv(dev->napi_dev); + *priv = dev; + strscpy(dev->napi_dev->name, "mt76u-rx", sizeof(dev->napi_dev->name)); + dev->napi_dev->threaded = 1; + netif_napi_add(dev->napi_dev, &dev->napi[MT_RXQ_MAIN], mt76u_napi_poll); + napi_enable(&dev->napi[MT_RXQ_MAIN]); + + return 0; +} +EXPORT_SYMBOL_GPL(__mt76u_init); + +int mt76u_init(struct mt76_dev *dev, struct usb_interface *intf) +{ + static struct mt76_bus_ops bus_ops = { + .rr = mt76u_rr, + .wr = mt76u_wr, + .rmw = mt76u_rmw, + .read_copy = mt76u_read_copy, + .write_copy = mt76u_copy, + .wr_rp = mt76u_wr_rp, + .rd_rp = mt76u_rd_rp, + .type = MT76_BUS_USB, + }; + + return __mt76u_init(dev, intf, &bus_ops); +} +EXPORT_SYMBOL_GPL(mt76u_init); + +MODULE_AUTHOR("Lorenzo Bianconi "); +MODULE_DESCRIPTION("MediaTek MT76x USB helpers"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/mediatek/mt76/usb_trace.c b/drivers/net/wireless/mediatek/mt76/usb_trace.c new file mode 100644 index 000000000..a04585b4b --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/usb_trace.c @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (C) 2018 Lorenzo Bianconi + */ + +#include + +#ifndef __CHECKER__ +#define CREATE_TRACE_POINTS +#include "usb_trace.h" + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/usb_trace.h b/drivers/net/wireless/mediatek/mt76/usb_trace.h new file mode 100644 index 000000000..93bb69c65 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/usb_trace.h @@ -0,0 +1,86 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (C) 2018 Lorenzo Bianconi + */ + +#if !defined(__MT76_USB_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define __MT76_USB_TRACE_H + +#include +#include "mt76.h" + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM mt76_usb + +#define MAXNAME 32 +#define DEV_ENTRY __array(char, wiphy_name, 32) +#define DEV_ASSIGN strscpy(__entry->wiphy_name, \ + wiphy_name(dev->hw->wiphy), MAXNAME) +#define DEV_PR_FMT "%s " +#define DEV_PR_ARG __entry->wiphy_name + +#define REG_ENTRY __field(u32, reg) __field(u32, val) +#define REG_ASSIGN __entry->reg = reg; __entry->val = val +#define REG_PR_FMT "reg:0x%04x=0x%08x" +#define REG_PR_ARG __entry->reg, __entry->val + +DECLARE_EVENT_CLASS(dev_reg_evt, + TP_PROTO(struct mt76_dev *dev, u32 reg, u32 val), + TP_ARGS(dev, reg, val), + TP_STRUCT__entry( + DEV_ENTRY + REG_ENTRY + ), + TP_fast_assign( + DEV_ASSIGN; + REG_ASSIGN; + ), + TP_printk( + DEV_PR_FMT REG_PR_FMT, + DEV_PR_ARG, REG_PR_ARG + ) +); + +DEFINE_EVENT(dev_reg_evt, usb_reg_rr, + TP_PROTO(struct mt76_dev *dev, u32 reg, u32 val), + TP_ARGS(dev, reg, val) +); + +DEFINE_EVENT(dev_reg_evt, usb_reg_wr, + TP_PROTO(struct mt76_dev *dev, u32 reg, u32 val), + TP_ARGS(dev, reg, val) +); + +DECLARE_EVENT_CLASS(urb_transfer, + TP_PROTO(struct mt76_dev *dev, struct urb *u), + TP_ARGS(dev, u), + TP_STRUCT__entry( + DEV_ENTRY __field(unsigned int, pipe) __field(u32, len) + ), + TP_fast_assign( + DEV_ASSIGN; + __entry->pipe = u->pipe; + __entry->len = u->transfer_buffer_length; + ), + TP_printk(DEV_PR_FMT "p:%08x len:%u", + DEV_PR_ARG, __entry->pipe, __entry->len) +); + +DEFINE_EVENT(urb_transfer, submit_urb, + TP_PROTO(struct mt76_dev *dev, struct urb *u), + TP_ARGS(dev, u) +); + +DEFINE_EVENT(urb_transfer, rx_urb, + TP_PROTO(struct mt76_dev *dev, struct urb *u), + TP_ARGS(dev, u) +); + +#endif + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE usb_trace + +#include diff --git a/drivers/net/wireless/mediatek/mt76/util.c b/drivers/net/wireless/mediatek/mt76/util.c new file mode 100644 index 000000000..83d3dc42e --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/util.c @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (C) 2016 Felix Fietkau + */ + +#include +#include "mt76.h" + +bool __mt76_poll(struct mt76_dev *dev, u32 offset, u32 mask, u32 val, + int timeout) +{ + u32 cur; + + timeout /= 10; + do { + cur = __mt76_rr(dev, offset) & mask; + if (cur == val) + return true; + + udelay(10); + } while (timeout-- > 0); + + return false; +} +EXPORT_SYMBOL_GPL(__mt76_poll); + +bool ____mt76_poll_msec(struct mt76_dev *dev, u32 offset, u32 mask, u32 val, + int timeout, int tick) +{ + u32 cur; + + timeout /= tick; + do { + cur = __mt76_rr(dev, offset) & mask; + if (cur == val) + return true; + + usleep_range(1000 * tick, 2000 * tick); + } while (timeout-- > 0); + + return false; +} +EXPORT_SYMBOL_GPL(____mt76_poll_msec); + +int mt76_wcid_alloc(u32 *mask, int size) +{ + int i, idx = 0, cur; + + for (i = 0; i < DIV_ROUND_UP(size, 32); i++) { + idx = ffs(~mask[i]); + if (!idx) + continue; + + idx--; + cur = i * 32 + idx; + if (cur >= size) + break; + + mask[i] |= BIT(idx); + return cur; + } + + return -1; +} +EXPORT_SYMBOL_GPL(mt76_wcid_alloc); + +int mt76_get_min_avg_rssi(struct mt76_dev *dev, u8 phy_idx) +{ + struct mt76_wcid *wcid; + int i, j, min_rssi = 0; + s8 cur_rssi; + + local_bh_disable(); + rcu_read_lock(); + + for (i = 0; i < ARRAY_SIZE(dev->wcid_mask); i++) { + u32 mask = dev->wcid_mask[i]; + + if (!mask) + continue; + + for (j = i * 32; mask; j++, mask >>= 1) { + if (!(mask & 1)) + continue; + + wcid = __mt76_wcid_ptr(dev, j); + if (!wcid || wcid->phy_idx != phy_idx) + continue; + + spin_lock(&dev->rx_lock); + if (wcid->inactive_count++ < 5) + cur_rssi = -ewma_signal_read(&wcid->rssi); + else + cur_rssi = 0; + spin_unlock(&dev->rx_lock); + + if (cur_rssi < min_rssi) + min_rssi = cur_rssi; + } + } + + rcu_read_unlock(); + local_bh_enable(); + + return min_rssi; +} +EXPORT_SYMBOL_GPL(mt76_get_min_avg_rssi); + +int __mt76_worker_fn(void *ptr) +{ + struct mt76_worker *w = ptr; + + while (!kthread_should_stop()) { + set_current_state(TASK_INTERRUPTIBLE); + + if (kthread_should_park()) { + kthread_parkme(); + continue; + } + + if (!test_and_clear_bit(MT76_WORKER_SCHEDULED, &w->state)) { + schedule(); + continue; + } + + set_bit(MT76_WORKER_RUNNING, &w->state); + set_current_state(TASK_RUNNING); + w->fn(w); + cond_resched(); + clear_bit(MT76_WORKER_RUNNING, &w->state); + } + + return 0; +} +EXPORT_SYMBOL_GPL(__mt76_worker_fn); + +MODULE_DESCRIPTION("MediaTek MT76x helpers"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/mediatek/mt76/util.h b/drivers/net/wireless/mediatek/mt76/util.h new file mode 100644 index 000000000..617966e8d --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/util.h @@ -0,0 +1,115 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (C) 2016 Felix Fietkau + */ + +#ifndef __MT76_UTIL_H +#define __MT76_UTIL_H + +#include +#include +#include +#include + +struct mt76_worker +{ + struct task_struct *task; + void (*fn)(struct mt76_worker *); + unsigned long state; +}; + +enum { + MT76_WORKER_SCHEDULED, + MT76_WORKER_RUNNING, +}; + +#define MT76_INCR(_var, _size) \ + (_var = (((_var) + 1) % (_size))) + +int mt76_wcid_alloc(u32 *mask, int size); + +static inline void +mt76_wcid_mask_set(u32 *mask, int idx) +{ + mask[idx / 32] |= BIT(idx % 32); +} + +static inline void +mt76_wcid_mask_clear(u32 *mask, int idx) +{ + mask[idx / 32] &= ~BIT(idx % 32); +} + +static inline void +mt76_skb_set_moredata(struct sk_buff *skb, bool enable) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + + if (enable) + hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA); + else + hdr->frame_control &= ~cpu_to_le16(IEEE80211_FCTL_MOREDATA); +} + +int __mt76_worker_fn(void *ptr); + +static inline int +mt76_worker_setup(struct ieee80211_hw *hw, struct mt76_worker *w, + void (*fn)(struct mt76_worker *), + const char *name) +{ + const char *dev_name = wiphy_name(hw->wiphy); + int ret; + + if (fn) + w->fn = fn; + w->task = kthread_run(__mt76_worker_fn, w, + "mt76-%s %s", name, dev_name); + + if (IS_ERR(w->task)) { + ret = PTR_ERR(w->task); + w->task = NULL; + return ret; + } + + return 0; +} + +static inline void mt76_worker_schedule(struct mt76_worker *w) +{ + if (!w->task) + return; + + if (!test_and_set_bit(MT76_WORKER_SCHEDULED, &w->state) && + !test_bit(MT76_WORKER_RUNNING, &w->state)) + wake_up_process(w->task); +} + +static inline void mt76_worker_disable(struct mt76_worker *w) +{ + if (!w->task) + return; + + kthread_park(w->task); + WRITE_ONCE(w->state, 0); +} + +static inline void mt76_worker_enable(struct mt76_worker *w) +{ + if (!w->task) + return; + + kthread_unpark(w->task); + mt76_worker_schedule(w); +} + +static inline void mt76_worker_teardown(struct mt76_worker *w) +{ + if (!w->task) + return; + + kthread_stop(w->task); + w->task = NULL; +} + +#endif diff --git a/drivers/net/wireless/mediatek/mt76/wed.c b/drivers/net/wireless/mediatek/mt76/wed.c new file mode 100644 index 000000000..f210a0c57 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/wed.c @@ -0,0 +1,214 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (C) 2023 Lorenzo Bianconi + */ + +#include "mt76.h" +#include "dma.h" + +void mt76_wed_release_rx_buf(struct mtk_wed_device *wed) +{ + struct mt76_dev *dev = mt76_wed_to_dev(wed); + int i; + + for (i = 0; i < dev->rx_token_size; i++) { + struct mt76_txwi_cache *t; + + t = mt76_rx_token_release(dev, i); + if (!t || !t->ptr) + continue; + + mt76_put_page_pool_buf(t->ptr, false); + t->ptr = NULL; + + mt76_put_rxwi(dev, t); + } + + mt76_free_pending_rxwi(dev); +} +EXPORT_SYMBOL_GPL(mt76_wed_release_rx_buf); + +#ifdef CONFIG_NET_MEDIATEK_SOC_WED +u32 mt76_wed_init_rx_buf(struct mtk_wed_device *wed, int size) +{ + struct mtk_wed_bm_desc *desc = wed->rx_buf_ring.desc; + struct mt76_dev *dev = mt76_wed_to_dev(wed); + struct mt76_txwi_cache *t = NULL; + struct mt76_queue *q; + int i; + + if (wed->version == 2 && dev->phy.band_idx) + q = &dev->q_rx[MT_RXQ_BAND1]; + else + q = &dev->q_rx[MT_RXQ_MAIN]; + + for (i = 0; i < size; i++) { + dma_addr_t addr; + u32 offset; + int token; + void *buf; + + t = mt76_get_rxwi(dev); + if (!t) + goto unmap; + + buf = mt76_get_page_pool_buf(q, &offset, q->buf_size); + if (!buf) + goto unmap; + + addr = page_pool_get_dma_addr(virt_to_head_page(buf)) + offset; + desc->buf0 = cpu_to_le32(addr); + token = mt76_rx_token_consume(dev, buf, t, addr); + if (token < 0) { + mt76_put_page_pool_buf(buf, false); + goto unmap; + } + + token = FIELD_PREP(MT_DMA_CTL_TOKEN, token); +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + token |= FIELD_PREP(MT_DMA_CTL_SDP0_H, addr >> 32); +#endif + desc->token |= cpu_to_le32(token); + desc++; + } + + return 0; + +unmap: + if (t) + mt76_put_rxwi(dev, t); + mt76_wed_release_rx_buf(wed); + + return -ENOMEM; +} +EXPORT_SYMBOL_GPL(mt76_wed_init_rx_buf); + +int mt76_wed_offload_enable(struct mtk_wed_device *wed) +{ + struct mt76_dev *dev = mt76_wed_to_dev(wed); + + spin_lock_bh(&dev->token_lock); + dev->token_size = wed->wlan.token_start; + spin_unlock_bh(&dev->token_lock); + + return !wait_event_timeout(dev->tx_wait, !dev->wed_token_count, HZ); +} +EXPORT_SYMBOL_GPL(mt76_wed_offload_enable); + +int mt76_wed_dma_setup(struct mt76_dev *dev, struct mt76_queue *q, bool reset) +{ + int ret = 0, type, ring; + u16 flags; + + if (!q || !q->ndesc) + return -EINVAL; + + flags = q->flags; + if (!q->wed || !mtk_wed_device_active(q->wed)) + q->flags &= ~MT_QFLAG_WED; + + if (!(q->flags & MT_QFLAG_WED)) + return 0; + + type = FIELD_GET(MT_QFLAG_WED_TYPE, q->flags); + ring = FIELD_GET(MT_QFLAG_WED_RING, q->flags); + + switch (type) { + case MT76_WED_Q_TX: + ret = mtk_wed_device_tx_ring_setup(q->wed, ring, q->regs, + reset); + if (!ret) + q->wed_regs = q->wed->tx_ring[ring].reg_base; + break; + case MT76_WED_Q_TXFREE: + /* WED txfree queue needs ring to be initialized before setup */ + q->flags = 0; + mt76_dma_queue_reset(dev, q, true); + mt76_dma_rx_fill(dev, q, false); + + ret = mtk_wed_device_txfree_ring_setup(q->wed, q->regs); + if (!ret) + q->wed_regs = q->wed->txfree_ring.reg_base; + break; + case MT76_WED_Q_RX: + ret = mtk_wed_device_rx_ring_setup(q->wed, ring, q->regs, + reset); + if (!ret) + q->wed_regs = q->wed->rx_ring[ring].reg_base; + break; + case MT76_WED_RRO_Q_DATA: + q->flags &= ~MT_QFLAG_WED; + mt76_dma_queue_reset(dev, q, false); + mtk_wed_device_rro_rx_ring_setup(q->wed, ring, q->regs); + q->head = q->ndesc - 1; + q->queued = q->head; + break; + case MT76_WED_RRO_Q_MSDU_PG: + q->flags &= ~MT_QFLAG_WED; + mt76_dma_queue_reset(dev, q, false); + mtk_wed_device_msdu_pg_rx_ring_setup(q->wed, ring, q->regs); + q->head = q->ndesc - 1; + q->queued = q->head; + break; + case MT76_WED_RRO_Q_IND: + q->flags &= ~MT_QFLAG_WED; + mt76_dma_queue_reset(dev, q, true); + mt76_dma_rx_fill(dev, q, false); + mtk_wed_device_ind_rx_ring_setup(q->wed, q->regs); + break; + default: + ret = -EINVAL; + break; + } + q->flags = flags; + + return ret; +} +EXPORT_SYMBOL_GPL(mt76_wed_dma_setup); +#endif /*CONFIG_NET_MEDIATEK_SOC_WED */ + +void mt76_wed_offload_disable(struct mtk_wed_device *wed) +{ + struct mt76_dev *dev = mt76_wed_to_dev(wed); + + spin_lock_bh(&dev->token_lock); + dev->token_size = dev->drv->token_size; + spin_unlock_bh(&dev->token_lock); +} +EXPORT_SYMBOL_GPL(mt76_wed_offload_disable); + +void mt76_wed_reset_complete(struct mtk_wed_device *wed) +{ + struct mt76_dev *dev = mt76_wed_to_dev(wed); + + complete(&dev->mmio.wed_reset_complete); +} +EXPORT_SYMBOL_GPL(mt76_wed_reset_complete); + +int mt76_wed_net_setup_tc(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct net_device *netdev, enum tc_setup_type type, + void *type_data) +{ + struct mt76_phy *phy = hw->priv; + struct mtk_wed_device *wed = &phy->dev->mmio.wed; + + if (!mtk_wed_device_active(wed)) + return -EOPNOTSUPP; + + return mtk_wed_device_setup_tc(wed, netdev, type, type_data); +} +EXPORT_SYMBOL_GPL(mt76_wed_net_setup_tc); + +void mt76_wed_dma_reset(struct mt76_dev *dev) +{ + struct mt76_mmio *mmio = &dev->mmio; + + if (!test_bit(MT76_STATE_WED_RESET, &dev->phy.state)) + return; + + complete(&mmio->wed_reset); + + if (!wait_for_completion_timeout(&mmio->wed_reset_complete, 3 * HZ)) + dev_err(dev->dev, "wed reset complete timeout\n"); +} +EXPORT_SYMBOL_GPL(mt76_wed_dma_reset); diff --git a/drivers/net/wireless/mediatek/mt7601u/Kconfig b/drivers/net/wireless/mediatek/mt7601u/Kconfig new file mode 100644 index 000000000..4880fc053 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt7601u/Kconfig @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0-only +config MT7601U + tristate "MediaTek MT7601U (USB) support" + depends on MAC80211 + depends on USB + help + This adds support for MT7601U-based USB wireless dongles. diff --git a/drivers/net/wireless/mediatek/mt7601u/Makefile b/drivers/net/wireless/mediatek/mt7601u/Makefile new file mode 100644 index 000000000..30f2391c7 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt7601u/Makefile @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_MT7601U) += mt7601u.o + +mt7601u-objs = \ + usb.o init.o main.o mcu.o trace.o dma.o core.o eeprom.o phy.o \ + mac.o util.o debugfs.o tx.o + +CFLAGS_trace.o := -I$(src) diff --git a/drivers/net/wireless/mediatek/mt7601u/core.c b/drivers/net/wireless/mediatek/mt7601u/core.c new file mode 100644 index 000000000..907443b36 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt7601u/core.c @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2014 Felix Fietkau + * Copyright (C) 2015 Jakub Kicinski + */ + +#include "mt7601u.h" + +int mt7601u_wait_asic_ready(struct mt7601u_dev *dev) +{ + int i = 100; + u32 val; + + do { + if (test_bit(MT7601U_STATE_REMOVED, &dev->state)) + return -EIO; + + val = mt7601u_rr(dev, MT_MAC_CSR0); + if (val && ~val) + return 0; + + udelay(10); + } while (i--); + + return -EIO; +} + +bool mt76_poll(struct mt7601u_dev *dev, u32 offset, u32 mask, u32 val, + int timeout) +{ + u32 cur; + + timeout /= 10; + do { + if (test_bit(MT7601U_STATE_REMOVED, &dev->state)) + return false; + + cur = mt7601u_rr(dev, offset) & mask; + if (cur == val) + return true; + + udelay(10); + } while (timeout-- > 0); + + dev_err(dev->dev, "Error: Time out with reg %08x\n", offset); + + return false; +} + +bool mt76_poll_msec(struct mt7601u_dev *dev, u32 offset, u32 mask, u32 val, + int timeout) +{ + u32 cur; + + timeout /= 10; + do { + if (test_bit(MT7601U_STATE_REMOVED, &dev->state)) + return false; + + cur = mt7601u_rr(dev, offset) & mask; + if (cur == val) + return true; + + msleep(10); + } while (timeout-- > 0); + + dev_err(dev->dev, "Error: Time out with reg %08x\n", offset); + + return false; +} diff --git a/drivers/net/wireless/mediatek/mt7601u/debugfs.c b/drivers/net/wireless/mediatek/mt7601u/debugfs.c new file mode 100644 index 000000000..dbddf2569 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt7601u/debugfs.c @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2014 Felix Fietkau + * Copyright (C) 2015 Jakub Kicinski + */ + +#include + +#include "mt7601u.h" +#include "eeprom.h" + +static int +mt76_reg_set(void *data, u64 val) +{ + struct mt7601u_dev *dev = data; + + mt76_wr(dev, dev->debugfs_reg, val); + return 0; +} + +static int +mt76_reg_get(void *data, u64 *val) +{ + struct mt7601u_dev *dev = data; + + *val = mt76_rr(dev, dev->debugfs_reg); + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(fops_regval, mt76_reg_get, mt76_reg_set, "0x%08llx\n"); + +static int +mt7601u_ampdu_stat_show(struct seq_file *file, void *data) +{ + struct mt7601u_dev *dev = file->private; + int i, j; + +#define stat_printf(grp, off, name) \ + seq_printf(file, #name ":\t%llu\n", dev->stats.grp[off]) + + stat_printf(rx_stat, 0, rx_crc_err); + stat_printf(rx_stat, 1, rx_phy_err); + stat_printf(rx_stat, 2, rx_false_cca); + stat_printf(rx_stat, 3, rx_plcp_err); + stat_printf(rx_stat, 4, rx_fifo_overflow); + stat_printf(rx_stat, 5, rx_duplicate); + + stat_printf(tx_stat, 0, tx_fail_cnt); + stat_printf(tx_stat, 1, tx_bcn_cnt); + stat_printf(tx_stat, 2, tx_success); + stat_printf(tx_stat, 3, tx_retransmit); + stat_printf(tx_stat, 4, tx_zero_len); + stat_printf(tx_stat, 5, tx_underflow); + + stat_printf(aggr_stat, 0, non_aggr_tx); + stat_printf(aggr_stat, 1, aggr_tx); + + stat_printf(zero_len_del, 0, tx_zero_len_del); + stat_printf(zero_len_del, 1, rx_zero_len_del); +#undef stat_printf + + seq_puts(file, "Aggregations stats:\n"); + for (i = 0; i < 4; i++) { + for (j = 0; j < 8; j++) + seq_printf(file, "%08llx ", + dev->stats.aggr_n[i * 8 + j]); + seq_putc(file, '\n'); + } + + seq_printf(file, "recent average AMPDU len: %d\n", + atomic_read(&dev->avg_ampdu_len)); + + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(mt7601u_ampdu_stat); + +static int +mt7601u_eeprom_param_show(struct seq_file *file, void *data) +{ + struct mt7601u_dev *dev = file->private; + struct mt7601u_rate_power *rp = &dev->ee->power_rate_table; + struct tssi_data *td = &dev->ee->tssi_data; + int i; + + seq_printf(file, "RF freq offset: %hhx\n", dev->ee->rf_freq_off); + seq_printf(file, "RSSI offset: %hhx %hhx\n", + dev->ee->rssi_offset[0], dev->ee->rssi_offset[1]); + seq_printf(file, "Reference temp: %hhx\n", dev->ee->ref_temp); + seq_printf(file, "LNA gain: %hhx\n", dev->ee->lna_gain); + seq_printf(file, "Reg channels: %hhu-%d\n", dev->ee->reg.start, + dev->ee->reg.start + dev->ee->reg.num - 1); + + seq_puts(file, "Per rate power:\n"); + for (i = 0; i < 2; i++) + seq_printf(file, "\t raw:%02hhx bw20:%02hhx bw40:%02hhx\n", + rp->cck[i].raw, rp->cck[i].bw20, rp->cck[i].bw40); + for (i = 0; i < 4; i++) + seq_printf(file, "\t raw:%02hhx bw20:%02hhx bw40:%02hhx\n", + rp->ofdm[i].raw, rp->ofdm[i].bw20, rp->ofdm[i].bw40); + for (i = 0; i < 4; i++) + seq_printf(file, "\t raw:%02hhx bw20:%02hhx bw40:%02hhx\n", + rp->ht[i].raw, rp->ht[i].bw20, rp->ht[i].bw40); + + seq_puts(file, "Per channel power:\n"); + for (i = 0; i < 7; i++) + seq_printf(file, "\t tx_power ch%u:%02hhx ch%u:%02hhx\n", + i * 2 + 1, dev->ee->chan_pwr[i * 2], + i * 2 + 2, dev->ee->chan_pwr[i * 2 + 1]); + + if (!dev->ee->tssi_enabled) + return 0; + + seq_puts(file, "TSSI:\n"); + seq_printf(file, "\t slope:%02hhx\n", td->slope); + seq_printf(file, "\t offset=%02hhx %02hhx %02hhx\n", + td->offset[0], td->offset[1], td->offset[2]); + seq_printf(file, "\t delta_off:%08x\n", td->tx0_delta_offset); + + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(mt7601u_eeprom_param); + +void mt7601u_init_debugfs(struct mt7601u_dev *dev) +{ + struct dentry *dir; + + dir = debugfs_create_dir("mt7601u", dev->hw->wiphy->debugfsdir); + + debugfs_create_u8("temperature", 0400, dir, &dev->raw_temp); + debugfs_create_u32("temp_mode", 0400, dir, &dev->temp_mode); + + debugfs_create_u32("regidx", 0600, dir, &dev->debugfs_reg); + debugfs_create_file("regval", 0600, dir, dev, &fops_regval); + debugfs_create_file("ampdu_stat", 0400, dir, dev, &mt7601u_ampdu_stat_fops); + debugfs_create_file("eeprom_param", 0400, dir, dev, &mt7601u_eeprom_param_fops); +} diff --git a/drivers/net/wireless/mediatek/mt7601u/dma.c b/drivers/net/wireless/mediatek/mt7601u/dma.c new file mode 100644 index 000000000..773a1cc2f --- /dev/null +++ b/drivers/net/wireless/mediatek/mt7601u/dma.c @@ -0,0 +1,551 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2015 Jakub Kicinski + */ + +#include "mt7601u.h" +#include "dma.h" +#include "usb.h" +#include "trace.h" + +static int mt7601u_submit_rx_buf(struct mt7601u_dev *dev, + struct mt7601u_dma_buf_rx *e, gfp_t gfp); + +static unsigned int ieee80211_get_hdrlen_from_buf(const u8 *data, unsigned len) +{ + const struct ieee80211_hdr *hdr = (const struct ieee80211_hdr *)data; + unsigned int hdrlen; + + if (unlikely(len < 10)) + return 0; + hdrlen = ieee80211_hdrlen(hdr->frame_control); + if (unlikely(hdrlen > len)) + return 0; + return hdrlen; +} + +static struct sk_buff * +mt7601u_rx_skb_from_seg(struct mt7601u_dev *dev, struct mt7601u_rxwi *rxwi, + void *data, u32 seg_len, u32 truesize, struct page *p) +{ + struct sk_buff *skb; + u32 true_len, hdr_len = 0, copy, frag; + + skb = alloc_skb(p ? 128 : seg_len, GFP_ATOMIC); + if (!skb) + return NULL; + + true_len = mt76_mac_process_rx(dev, skb, data, rxwi); + if (!true_len || true_len > seg_len) + goto bad_frame; + + hdr_len = ieee80211_get_hdrlen_from_buf(data, true_len); + if (!hdr_len) + goto bad_frame; + + if (rxwi->rxinfo & cpu_to_le32(MT_RXINFO_L2PAD)) { + skb_put_data(skb, data, hdr_len); + + data += hdr_len + 2; + true_len -= hdr_len; + hdr_len = 0; + } + + /* If not doing paged RX allocated skb will always have enough space */ + copy = (true_len <= skb_tailroom(skb)) ? true_len : hdr_len + 8; + frag = true_len - copy; + + skb_put_data(skb, data, copy); + data += copy; + + if (frag) { + skb_add_rx_frag(skb, 0, p, data - page_address(p), + frag, truesize); + get_page(p); + } + + return skb; + +bad_frame: + dev_err_ratelimited(dev->dev, "Error: incorrect frame len:%u hdr:%u\n", + true_len, hdr_len); + dev_kfree_skb(skb); + return NULL; +} + +static void mt7601u_rx_process_seg(struct mt7601u_dev *dev, u8 *data, + u32 seg_len, struct page *p, + struct list_head *list) +{ + struct sk_buff *skb; + struct mt7601u_rxwi *rxwi; + u32 fce_info, truesize = seg_len; + + /* DMA_INFO field at the beginning of the segment contains only some of + * the information, we need to read the FCE descriptor from the end. + */ + fce_info = get_unaligned_le32(data + seg_len - MT_FCE_INFO_LEN); + seg_len -= MT_FCE_INFO_LEN; + + data += MT_DMA_HDR_LEN; + seg_len -= MT_DMA_HDR_LEN; + + rxwi = (struct mt7601u_rxwi *) data; + data += sizeof(struct mt7601u_rxwi); + seg_len -= sizeof(struct mt7601u_rxwi); + + if (unlikely(rxwi->zero[0] || rxwi->zero[1] || rxwi->zero[2])) + dev_err_once(dev->dev, "Error: RXWI zero fields are set\n"); + if (unlikely(FIELD_GET(MT_RXD_INFO_TYPE, fce_info))) + dev_err_once(dev->dev, "Error: RX path seen a non-pkt urb\n"); + + trace_mt_rx(dev, rxwi, fce_info); + + skb = mt7601u_rx_skb_from_seg(dev, rxwi, data, seg_len, truesize, p); + if (!skb) + return; + + local_bh_disable(); + rcu_read_lock(); + + ieee80211_rx_list(dev->hw, NULL, skb, list); + + rcu_read_unlock(); + local_bh_enable(); +} + +static u16 mt7601u_rx_next_seg_len(u8 *data, u32 data_len) +{ + u32 min_seg_len = MT_DMA_HDR_LEN + MT_RX_INFO_LEN + + sizeof(struct mt7601u_rxwi) + MT_FCE_INFO_LEN; + u16 dma_len = get_unaligned_le16(data); + + if (data_len < min_seg_len || + WARN_ON_ONCE(!dma_len) || + WARN_ON_ONCE(dma_len + MT_DMA_HDRS > data_len) || + WARN_ON_ONCE(dma_len & 0x3) || + WARN_ON_ONCE(dma_len < min_seg_len)) + return 0; + + return MT_DMA_HDRS + dma_len; +} + +static void +mt7601u_rx_process_entry(struct mt7601u_dev *dev, struct mt7601u_dma_buf_rx *e) +{ + u32 seg_len, data_len = e->urb->actual_length; + u8 *data = page_address(e->p); + struct page *new_p = NULL; + LIST_HEAD(list); + int cnt = 0; + + if (!test_bit(MT7601U_STATE_INITIALIZED, &dev->state)) + return; + + /* Copy if there is very little data in the buffer. */ + if (data_len > 512) + new_p = dev_alloc_pages(MT_RX_ORDER); + + while ((seg_len = mt7601u_rx_next_seg_len(data, data_len))) { + mt7601u_rx_process_seg(dev, data, seg_len, + new_p ? e->p : NULL, &list); + + data_len -= seg_len; + data += seg_len; + cnt++; + } + + if (cnt > 1) + trace_mt_rx_dma_aggr(dev, cnt, !!new_p); + + netif_receive_skb_list(&list); + + if (new_p) { + /* we have one extra ref from the allocator */ + put_page(e->p); + e->p = new_p; + } +} + +static struct mt7601u_dma_buf_rx * +mt7601u_rx_get_pending_entry(struct mt7601u_dev *dev) +{ + struct mt7601u_rx_queue *q = &dev->rx_q; + struct mt7601u_dma_buf_rx *buf = NULL; + unsigned long flags; + + spin_lock_irqsave(&dev->rx_lock, flags); + + if (!q->pending) + goto out; + + buf = &q->e[q->start]; + q->pending--; + q->start = (q->start + 1) % q->entries; +out: + spin_unlock_irqrestore(&dev->rx_lock, flags); + + return buf; +} + +static void mt7601u_complete_rx(struct urb *urb) +{ + struct mt7601u_dev *dev = urb->context; + struct mt7601u_rx_queue *q = &dev->rx_q; + unsigned long flags; + + /* do no schedule rx tasklet if urb has been unlinked + * or the device has been removed + */ + switch (urb->status) { + case -ECONNRESET: + case -ESHUTDOWN: + case -ENOENT: + case -EPROTO: + return; + default: + dev_err_ratelimited(dev->dev, "rx urb failed: %d\n", + urb->status); + fallthrough; + case 0: + break; + } + + spin_lock_irqsave(&dev->rx_lock, flags); + if (WARN_ONCE(q->e[q->end].urb != urb, "RX urb mismatch")) + goto out; + + q->end = (q->end + 1) % q->entries; + q->pending++; + tasklet_schedule(&dev->rx_tasklet); +out: + spin_unlock_irqrestore(&dev->rx_lock, flags); +} + +static void mt7601u_rx_tasklet(struct tasklet_struct *t) +{ + struct mt7601u_dev *dev = from_tasklet(dev, t, rx_tasklet); + struct mt7601u_dma_buf_rx *e; + + while ((e = mt7601u_rx_get_pending_entry(dev))) { + if (e->urb->status) + continue; + + mt7601u_rx_process_entry(dev, e); + mt7601u_submit_rx_buf(dev, e, GFP_ATOMIC); + } +} + +static void mt7601u_complete_tx(struct urb *urb) +{ + struct mt7601u_tx_queue *q = urb->context; + struct mt7601u_dev *dev = q->dev; + struct sk_buff *skb; + unsigned long flags; + + switch (urb->status) { + case -ECONNRESET: + case -ESHUTDOWN: + case -ENOENT: + case -EPROTO: + return; + default: + dev_err_ratelimited(dev->dev, "tx urb failed: %d\n", + urb->status); + fallthrough; + case 0: + break; + } + + spin_lock_irqsave(&dev->tx_lock, flags); + if (WARN_ONCE(q->e[q->start].urb != urb, "TX urb mismatch")) + goto out; + + skb = q->e[q->start].skb; + q->e[q->start].skb = NULL; + trace_mt_tx_dma_done(dev, skb); + + __skb_queue_tail(&dev->tx_skb_done, skb); + tasklet_schedule(&dev->tx_tasklet); + + if (q->used == q->entries - q->entries / 8) + ieee80211_wake_queue(dev->hw, skb_get_queue_mapping(skb)); + + q->start = (q->start + 1) % q->entries; + q->used--; +out: + spin_unlock_irqrestore(&dev->tx_lock, flags); +} + +static void mt7601u_tx_tasklet(struct tasklet_struct *t) +{ + struct mt7601u_dev *dev = from_tasklet(dev, t, tx_tasklet); + struct sk_buff_head skbs; + unsigned long flags; + + __skb_queue_head_init(&skbs); + + spin_lock_irqsave(&dev->tx_lock, flags); + + set_bit(MT7601U_STATE_MORE_STATS, &dev->state); + if (!test_and_set_bit(MT7601U_STATE_READING_STATS, &dev->state)) + queue_delayed_work(dev->stat_wq, &dev->stat_work, + msecs_to_jiffies(10)); + + skb_queue_splice_init(&dev->tx_skb_done, &skbs); + + spin_unlock_irqrestore(&dev->tx_lock, flags); + + while (!skb_queue_empty(&skbs)) { + struct sk_buff *skb = __skb_dequeue(&skbs); + + mt7601u_tx_status(dev, skb); + } +} + +static int mt7601u_dma_submit_tx(struct mt7601u_dev *dev, + struct sk_buff *skb, u8 ep) +{ + struct usb_device *usb_dev = mt7601u_to_usb_dev(dev); + unsigned snd_pipe = usb_sndbulkpipe(usb_dev, dev->out_eps[ep]); + struct mt7601u_dma_buf_tx *e; + struct mt7601u_tx_queue *q = &dev->tx_q[ep]; + unsigned long flags; + int ret; + + spin_lock_irqsave(&dev->tx_lock, flags); + + if (WARN_ON(q->entries <= q->used)) { + ret = -ENOSPC; + goto out; + } + + e = &q->e[q->end]; + usb_fill_bulk_urb(e->urb, usb_dev, snd_pipe, skb->data, skb->len, + mt7601u_complete_tx, q); + ret = usb_submit_urb(e->urb, GFP_ATOMIC); + if (ret) { + /* Special-handle ENODEV from TX urb submission because it will + * often be the first ENODEV we see after device is removed. + */ + if (ret == -ENODEV) + set_bit(MT7601U_STATE_REMOVED, &dev->state); + else + dev_err(dev->dev, "Error: TX urb submit failed:%d\n", + ret); + goto out; + } + + q->end = (q->end + 1) % q->entries; + q->used++; + e->skb = skb; + + if (q->used >= q->entries) + ieee80211_stop_queue(dev->hw, skb_get_queue_mapping(skb)); +out: + spin_unlock_irqrestore(&dev->tx_lock, flags); + + return ret; +} + +/* Map hardware Q to USB endpoint number */ +static u8 q2ep(u8 qid) +{ + /* TODO: take management packets to queue 5 */ + return qid + 1; +} + +/* Map USB endpoint number to Q id in the DMA engine */ +static enum mt76_qsel ep2dmaq(u8 ep) +{ + if (ep == 5) + return MT_QSEL_MGMT; + return MT_QSEL_EDCA; +} + +int mt7601u_dma_enqueue_tx(struct mt7601u_dev *dev, struct sk_buff *skb, + struct mt76_wcid *wcid, int hw_q) +{ + u8 ep = q2ep(hw_q); + u32 dma_flags; + int ret; + + dma_flags = MT_TXD_PKT_INFO_80211; + if (wcid->hw_key_idx == 0xff) + dma_flags |= MT_TXD_PKT_INFO_WIV; + + ret = mt7601u_dma_skb_wrap_pkt(skb, ep2dmaq(ep), dma_flags); + if (ret) + return ret; + + ret = mt7601u_dma_submit_tx(dev, skb, ep); + if (ret) { + ieee80211_free_txskb(dev->hw, skb); + return ret; + } + + return 0; +} + +static void mt7601u_kill_rx(struct mt7601u_dev *dev) +{ + int i; + + for (i = 0; i < dev->rx_q.entries; i++) + usb_poison_urb(dev->rx_q.e[i].urb); +} + +static int mt7601u_submit_rx_buf(struct mt7601u_dev *dev, + struct mt7601u_dma_buf_rx *e, gfp_t gfp) +{ + struct usb_device *usb_dev = mt7601u_to_usb_dev(dev); + u8 *buf = page_address(e->p); + unsigned pipe; + int ret; + + pipe = usb_rcvbulkpipe(usb_dev, dev->in_eps[MT_EP_IN_PKT_RX]); + + usb_fill_bulk_urb(e->urb, usb_dev, pipe, buf, MT_RX_URB_SIZE, + mt7601u_complete_rx, dev); + + trace_mt_submit_urb(dev, e->urb); + ret = usb_submit_urb(e->urb, gfp); + if (ret) + dev_err(dev->dev, "Error: submit RX URB failed:%d\n", ret); + + return ret; +} + +static int mt7601u_submit_rx(struct mt7601u_dev *dev) +{ + int i, ret; + + for (i = 0; i < dev->rx_q.entries; i++) { + ret = mt7601u_submit_rx_buf(dev, &dev->rx_q.e[i], GFP_KERNEL); + if (ret) + return ret; + } + + return 0; +} + +static void mt7601u_free_rx(struct mt7601u_dev *dev) +{ + int i; + + for (i = 0; i < dev->rx_q.entries; i++) { + __free_pages(dev->rx_q.e[i].p, MT_RX_ORDER); + usb_free_urb(dev->rx_q.e[i].urb); + } +} + +static int mt7601u_alloc_rx(struct mt7601u_dev *dev) +{ + int i; + + memset(&dev->rx_q, 0, sizeof(dev->rx_q)); + dev->rx_q.dev = dev; + dev->rx_q.entries = N_RX_ENTRIES; + + for (i = 0; i < N_RX_ENTRIES; i++) { + dev->rx_q.e[i].urb = usb_alloc_urb(0, GFP_KERNEL); + dev->rx_q.e[i].p = dev_alloc_pages(MT_RX_ORDER); + + if (!dev->rx_q.e[i].urb || !dev->rx_q.e[i].p) + return -ENOMEM; + } + + return 0; +} + +static void mt7601u_free_tx_queue(struct mt7601u_tx_queue *q) +{ + int i; + + for (i = 0; i < q->entries; i++) { + usb_poison_urb(q->e[i].urb); + if (q->e[i].skb) + mt7601u_tx_status(q->dev, q->e[i].skb); + usb_free_urb(q->e[i].urb); + } +} + +static void mt7601u_free_tx(struct mt7601u_dev *dev) +{ + int i; + + if (!dev->tx_q) + return; + + for (i = 0; i < __MT_EP_OUT_MAX; i++) + mt7601u_free_tx_queue(&dev->tx_q[i]); +} + +static int mt7601u_alloc_tx_queue(struct mt7601u_dev *dev, + struct mt7601u_tx_queue *q) +{ + int i; + + q->dev = dev; + q->entries = N_TX_ENTRIES; + + for (i = 0; i < N_TX_ENTRIES; i++) { + q->e[i].urb = usb_alloc_urb(0, GFP_KERNEL); + if (!q->e[i].urb) + return -ENOMEM; + } + + return 0; +} + +static int mt7601u_alloc_tx(struct mt7601u_dev *dev) +{ + int i; + + dev->tx_q = devm_kcalloc(dev->dev, __MT_EP_OUT_MAX, + sizeof(*dev->tx_q), GFP_KERNEL); + if (!dev->tx_q) + return -ENOMEM; + + for (i = 0; i < __MT_EP_OUT_MAX; i++) + if (mt7601u_alloc_tx_queue(dev, &dev->tx_q[i])) + return -ENOMEM; + + return 0; +} + +int mt7601u_dma_init(struct mt7601u_dev *dev) +{ + int ret; + + tasklet_setup(&dev->tx_tasklet, mt7601u_tx_tasklet); + tasklet_setup(&dev->rx_tasklet, mt7601u_rx_tasklet); + + ret = mt7601u_alloc_tx(dev); + if (ret) + goto err; + ret = mt7601u_alloc_rx(dev); + if (ret) + goto err; + + ret = mt7601u_submit_rx(dev); + if (ret) + goto err; + + return 0; +err: + mt7601u_dma_cleanup(dev); + return ret; +} + +void mt7601u_dma_cleanup(struct mt7601u_dev *dev) +{ + mt7601u_kill_rx(dev); + + tasklet_kill(&dev->rx_tasklet); + + mt7601u_free_rx(dev); + mt7601u_free_tx(dev); + + tasklet_kill(&dev->tx_tasklet); +} diff --git a/drivers/net/wireless/mediatek/mt7601u/dma.h b/drivers/net/wireless/mediatek/mt7601u/dma.h new file mode 100644 index 000000000..cda9c2675 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt7601u/dma.h @@ -0,0 +1,117 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2014 Felix Fietkau + * Copyright (C) 2015 Jakub Kicinski + */ + +#ifndef __MT7601U_DMA_H +#define __MT7601U_DMA_H + +#include +#include + +#define MT_DMA_HDR_LEN 4 +#define MT_RX_INFO_LEN 4 +#define MT_FCE_INFO_LEN 4 +#define MT_DMA_HDRS (MT_DMA_HDR_LEN + MT_RX_INFO_LEN) + +/* Common Tx DMA descriptor fields */ +#define MT_TXD_INFO_LEN GENMASK(15, 0) +#define MT_TXD_INFO_D_PORT GENMASK(29, 27) +#define MT_TXD_INFO_TYPE GENMASK(31, 30) + +enum mt76_msg_port { + WLAN_PORT, + CPU_RX_PORT, + CPU_TX_PORT, + HOST_PORT, + VIRTUAL_CPU_RX_PORT, + VIRTUAL_CPU_TX_PORT, + DISCARD, +}; + +enum mt76_info_type { + DMA_PACKET, + DMA_COMMAND, +}; + +/* Tx DMA packet specific flags */ +#define MT_TXD_PKT_INFO_NEXT_VLD BIT(16) +#define MT_TXD_PKT_INFO_TX_BURST BIT(17) +#define MT_TXD_PKT_INFO_80211 BIT(19) +#define MT_TXD_PKT_INFO_TSO BIT(20) +#define MT_TXD_PKT_INFO_CSO BIT(21) +#define MT_TXD_PKT_INFO_WIV BIT(24) +#define MT_TXD_PKT_INFO_QSEL GENMASK(26, 25) + +enum mt76_qsel { + MT_QSEL_MGMT, + MT_QSEL_HCCA, + MT_QSEL_EDCA, + MT_QSEL_EDCA_2, +}; + +/* Tx DMA MCU command specific flags */ +#define MT_TXD_CMD_INFO_SEQ GENMASK(19, 16) +#define MT_TXD_CMD_INFO_TYPE GENMASK(26, 20) + +static inline int mt7601u_dma_skb_wrap(struct sk_buff *skb, + enum mt76_msg_port d_port, + enum mt76_info_type type, u32 flags) +{ + u32 info; + + /* Buffer layout: + * | 4B | xfer len | pad | 4B | + * | TXINFO | pkt/cmd | zero pad to 4B | zero | + * + * length field of TXINFO should be set to 'xfer len'. + */ + + info = flags | + FIELD_PREP(MT_TXD_INFO_LEN, round_up(skb->len, 4)) | + FIELD_PREP(MT_TXD_INFO_D_PORT, d_port) | + FIELD_PREP(MT_TXD_INFO_TYPE, type); + + put_unaligned_le32(info, skb_push(skb, sizeof(info))); + return skb_put_padto(skb, round_up(skb->len, 4) + 4); +} + +static inline int +mt7601u_dma_skb_wrap_pkt(struct sk_buff *skb, enum mt76_qsel qsel, u32 flags) +{ + flags |= FIELD_PREP(MT_TXD_PKT_INFO_QSEL, qsel); + return mt7601u_dma_skb_wrap(skb, WLAN_PORT, DMA_PACKET, flags); +} + +/* Common Rx DMA descriptor fields */ +#define MT_RXD_INFO_LEN GENMASK(13, 0) +#define MT_RXD_INFO_PCIE_INTR BIT(24) +#define MT_RXD_INFO_QSEL GENMASK(26, 25) +#define MT_RXD_INFO_PORT GENMASK(29, 27) +#define MT_RXD_INFO_TYPE GENMASK(31, 30) + +/* Rx DMA packet specific flags */ +#define MT_RXD_PKT_INFO_UDP_ERR BIT(16) +#define MT_RXD_PKT_INFO_TCP_ERR BIT(17) +#define MT_RXD_PKT_INFO_IP_ERR BIT(18) +#define MT_RXD_PKT_INFO_PKT_80211 BIT(19) +#define MT_RXD_PKT_INFO_L3L4_DONE BIT(20) +#define MT_RXD_PKT_INFO_MAC_LEN GENMASK(23, 21) + +/* Rx DMA MCU command specific flags */ +#define MT_RXD_CMD_INFO_SELF_GEN BIT(15) +#define MT_RXD_CMD_INFO_CMD_SEQ GENMASK(19, 16) +#define MT_RXD_CMD_INFO_EVT_TYPE GENMASK(23, 20) + +enum mt76_evt_type { + CMD_DONE, + CMD_ERROR, + CMD_RETRY, + EVENT_PWR_RSP, + EVENT_WOW_RSP, + EVENT_CARRIER_DETECT_RSP, + EVENT_DFS_DETECT_RSP, +}; + +#endif diff --git a/drivers/net/wireless/mediatek/mt7601u/eeprom.c b/drivers/net/wireless/mediatek/mt7601u/eeprom.c new file mode 100644 index 000000000..d4d31a546 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt7601u/eeprom.c @@ -0,0 +1,390 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2014 Felix Fietkau + * Copyright (C) 2015 Jakub Kicinski + */ + +#include +#include +#include +#include +#include +#include "mt7601u.h" +#include "eeprom.h" +#include "mac.h" + +static bool +field_valid(u8 val) +{ + return val != 0xff; +} + +static s8 +field_validate(u8 val) +{ + if (!field_valid(val)) + return 0; + + return val; +} + +static int +mt7601u_efuse_read(struct mt7601u_dev *dev, u16 addr, u8 *data, + enum mt7601u_eeprom_access_modes mode) +{ + u32 val; + int i; + + val = mt76_rr(dev, MT_EFUSE_CTRL); + val &= ~(MT_EFUSE_CTRL_AIN | + MT_EFUSE_CTRL_MODE); + val |= FIELD_PREP(MT_EFUSE_CTRL_AIN, addr & ~0xf) | + FIELD_PREP(MT_EFUSE_CTRL_MODE, mode) | + MT_EFUSE_CTRL_KICK; + mt76_wr(dev, MT_EFUSE_CTRL, val); + + if (!mt76_poll(dev, MT_EFUSE_CTRL, MT_EFUSE_CTRL_KICK, 0, 1000)) + return -ETIMEDOUT; + + val = mt76_rr(dev, MT_EFUSE_CTRL); + if ((val & MT_EFUSE_CTRL_AOUT) == MT_EFUSE_CTRL_AOUT) { + /* Parts of eeprom not in the usage map (0x80-0xc0,0xf0) + * will not return valid data but it's ok. + */ + memset(data, 0xff, 16); + return 0; + } + + for (i = 0; i < 4; i++) { + val = mt76_rr(dev, MT_EFUSE_DATA(i)); + put_unaligned_le32(val, data + 4 * i); + } + + return 0; +} + +static int +mt7601u_efuse_physical_size_check(struct mt7601u_dev *dev) +{ + const int map_reads = DIV_ROUND_UP(MT_EFUSE_USAGE_MAP_SIZE, 16); + u8 data[round_up(MT_EFUSE_USAGE_MAP_SIZE, 16)]; + int ret, i; + u32 start = 0, end = 0, cnt_free; + + for (i = 0; i < map_reads; i++) { + ret = mt7601u_efuse_read(dev, MT_EE_USAGE_MAP_START + i * 16, + data + i * 16, MT_EE_PHYSICAL_READ); + if (ret) + return ret; + } + + for (i = 0; i < MT_EFUSE_USAGE_MAP_SIZE; i++) + if (!data[i]) { + if (!start) + start = MT_EE_USAGE_MAP_START + i; + end = MT_EE_USAGE_MAP_START + i; + } + cnt_free = end - start + 1; + + if (MT_EFUSE_USAGE_MAP_SIZE - cnt_free < 5) { + dev_err(dev->dev, "Error: your device needs default EEPROM file and this driver doesn't support it!\n"); + return -EINVAL; + } + + return 0; +} + +static bool +mt7601u_has_tssi(struct mt7601u_dev *dev, u8 *eeprom) +{ + u16 nic_conf1 = get_unaligned_le16(eeprom + MT_EE_NIC_CONF_1); + + return (u16)~nic_conf1 && (nic_conf1 & MT_EE_NIC_CONF_1_TX_ALC_EN); +} + +static void +mt7601u_set_chip_cap(struct mt7601u_dev *dev, u8 *eeprom) +{ + u16 nic_conf0 = get_unaligned_le16(eeprom + MT_EE_NIC_CONF_0); + u16 nic_conf1 = get_unaligned_le16(eeprom + MT_EE_NIC_CONF_1); + + if (!field_valid(nic_conf1 & 0xff)) + nic_conf1 &= 0xff00; + + dev->ee->tssi_enabled = mt7601u_has_tssi(dev, eeprom) && + !(nic_conf1 & MT_EE_NIC_CONF_1_TEMP_TX_ALC); + + if (nic_conf1 & MT_EE_NIC_CONF_1_HW_RF_CTRL) + dev_err(dev->dev, + "Error: this driver does not support HW RF ctrl\n"); + + if (!field_valid(nic_conf0 >> 8)) + return; + + if (FIELD_GET(MT_EE_NIC_CONF_0_RX_PATH, nic_conf0) > 1 || + FIELD_GET(MT_EE_NIC_CONF_0_TX_PATH, nic_conf0) > 1) + dev_err(dev->dev, + "Error: device has more than 1 RX/TX stream!\n"); +} + +static void mt7601u_set_channel_target_power(struct mt7601u_dev *dev, + u8 *eeprom, u8 max_pwr) +{ + u8 trgt_pwr = eeprom[MT_EE_TX_TSSI_TARGET_POWER]; + + if (trgt_pwr > max_pwr || !trgt_pwr) { + dev_warn(dev->dev, "Error: EEPROM trgt power invalid %hhx!\n", + trgt_pwr); + trgt_pwr = 0x20; + } + + memset(dev->ee->chan_pwr, trgt_pwr, sizeof(dev->ee->chan_pwr)); +} + +static void +mt7601u_set_channel_power(struct mt7601u_dev *dev, u8 *eeprom) +{ + u32 i, val; + u8 max_pwr; + + val = mt7601u_rr(dev, MT_TX_ALC_CFG_0); + max_pwr = FIELD_GET(MT_TX_ALC_CFG_0_LIMIT_0, val); + + if (mt7601u_has_tssi(dev, eeprom)) { + mt7601u_set_channel_target_power(dev, eeprom, max_pwr); + return; + } + + for (i = 0; i < 14; i++) { + s8 power = field_validate(eeprom[MT_EE_TX_POWER_OFFSET + i]); + + if (power > max_pwr || power < 0) + power = MT7601U_DEFAULT_TX_POWER; + + dev->ee->chan_pwr[i] = power; + } +} + +static void +mt7601u_set_country_reg(struct mt7601u_dev *dev, u8 *eeprom) +{ + /* Note: - region 31 is not valid for mt7601u (see rtmp_init.c) + * - comments in rtmp_def.h are incorrect (see rt_channel.c) + */ + static const struct reg_channel_bounds chan_bounds[] = { + /* EEPROM country regions 0 - 7 */ + { 1, 11 }, { 1, 13 }, { 10, 2 }, { 10, 4 }, + { 14, 1 }, { 1, 14 }, { 3, 7 }, { 5, 9 }, + /* EEPROM country regions 32 - 33 */ + { 1, 11 }, { 1, 14 } + }; + u8 val = eeprom[MT_EE_COUNTRY_REGION]; + int idx = -1; + + if (val < 8) + idx = val; + if (val > 31 && val < 33) + idx = val - 32 + 8; + + if (idx != -1) + dev_info(dev->dev, + "EEPROM country region %02x (channels %d-%d)\n", + val, chan_bounds[idx].start, + chan_bounds[idx].start + chan_bounds[idx].num - 1); + else + idx = 5; /* channels 1 - 14 */ + + dev->ee->reg = chan_bounds[idx]; + + /* TODO: country region 33 is special - phy should be set to B-mode + * before entering channel 14 (see sta/connect.c) + */ +} + +static void +mt7601u_set_rf_freq_off(struct mt7601u_dev *dev, u8 *eeprom) +{ + u8 comp; + + dev->ee->rf_freq_off = field_validate(eeprom[MT_EE_FREQ_OFFSET]); + comp = field_validate(eeprom[MT_EE_FREQ_OFFSET_COMPENSATION]); + + if (comp & BIT(7)) + dev->ee->rf_freq_off -= comp & 0x7f; + else + dev->ee->rf_freq_off += comp; +} + +static void +mt7601u_set_rssi_offset(struct mt7601u_dev *dev, u8 *eeprom) +{ + int i; + s8 *rssi_offset = dev->ee->rssi_offset; + + for (i = 0; i < 2; i++) { + rssi_offset[i] = eeprom[MT_EE_RSSI_OFFSET + i]; + + if (rssi_offset[i] < -10 || rssi_offset[i] > 10) { + dev_warn(dev->dev, + "Warning: EEPROM RSSI is invalid %02hhx\n", + rssi_offset[i]); + rssi_offset[i] = 0; + } + } +} + +static void +mt7601u_extra_power_over_mac(struct mt7601u_dev *dev) +{ + u32 val; + + val = ((mt7601u_rr(dev, MT_TX_PWR_CFG_1) & 0x0000ff00) >> 8); + val |= ((mt7601u_rr(dev, MT_TX_PWR_CFG_2) & 0x0000ff00) << 8); + mt7601u_wr(dev, MT_TX_PWR_CFG_7, val); + + val = ((mt7601u_rr(dev, MT_TX_PWR_CFG_4) & 0x0000ff00) >> 8); + mt7601u_wr(dev, MT_TX_PWR_CFG_9, val); +} + +static void +mt7601u_set_power_rate(struct power_per_rate *rate, s8 delta, u8 value) +{ + /* Invalid? Note: vendor driver does not handle this */ + if (value == 0xff) + return; + + rate->raw = s6_validate(value); + rate->bw20 = s6_to_int(value); + /* Note: vendor driver does cap the value to s6 right away */ + rate->bw40 = rate->bw20 + delta; +} + +static void +mt7601u_save_power_rate(struct mt7601u_dev *dev, s8 delta, u32 val, int i) +{ + struct mt7601u_rate_power *t = &dev->ee->power_rate_table; + + switch (i) { + case 0: + mt7601u_set_power_rate(&t->cck[0], delta, (val >> 0) & 0xff); + mt7601u_set_power_rate(&t->cck[1], delta, (val >> 8) & 0xff); + /* Save cck bw20 for fixups of channel 14 */ + dev->ee->real_cck_bw20[0] = t->cck[0].bw20; + dev->ee->real_cck_bw20[1] = t->cck[1].bw20; + + mt7601u_set_power_rate(&t->ofdm[0], delta, (val >> 16) & 0xff); + mt7601u_set_power_rate(&t->ofdm[1], delta, (val >> 24) & 0xff); + break; + case 1: + mt7601u_set_power_rate(&t->ofdm[2], delta, (val >> 0) & 0xff); + mt7601u_set_power_rate(&t->ofdm[3], delta, (val >> 8) & 0xff); + mt7601u_set_power_rate(&t->ht[0], delta, (val >> 16) & 0xff); + mt7601u_set_power_rate(&t->ht[1], delta, (val >> 24) & 0xff); + break; + case 2: + mt7601u_set_power_rate(&t->ht[2], delta, (val >> 0) & 0xff); + mt7601u_set_power_rate(&t->ht[3], delta, (val >> 8) & 0xff); + break; + } +} + +static s8 +get_delta(u8 val) +{ + s8 ret; + + if (!field_valid(val) || !(val & BIT(7))) + return 0; + + ret = val & 0x1f; + if (ret > 8) + ret = 8; + if (val & BIT(6)) + ret = -ret; + + return ret; +} + +static void +mt7601u_config_tx_power_per_rate(struct mt7601u_dev *dev, u8 *eeprom) +{ + u32 val; + s8 bw40_delta; + int i; + + bw40_delta = get_delta(eeprom[MT_EE_TX_POWER_DELTA_BW40]); + + for (i = 0; i < 5; i++) { + val = get_unaligned_le32(eeprom + MT_EE_TX_POWER_BYRATE(i)); + + mt7601u_save_power_rate(dev, bw40_delta, val, i); + + if (~val) + mt7601u_wr(dev, MT_TX_PWR_CFG_0 + i * 4, val); + } + + mt7601u_extra_power_over_mac(dev); +} + +static void +mt7601u_init_tssi_params(struct mt7601u_dev *dev, u8 *eeprom) +{ + struct tssi_data *d = &dev->ee->tssi_data; + + if (!dev->ee->tssi_enabled) + return; + + d->slope = eeprom[MT_EE_TX_TSSI_SLOPE]; + d->tx0_delta_offset = eeprom[MT_EE_TX_TSSI_OFFSET] * 1024; + d->offset[0] = eeprom[MT_EE_TX_TSSI_OFFSET_GROUP]; + d->offset[1] = eeprom[MT_EE_TX_TSSI_OFFSET_GROUP + 1]; + d->offset[2] = eeprom[MT_EE_TX_TSSI_OFFSET_GROUP + 2]; +} + +int +mt7601u_eeprom_init(struct mt7601u_dev *dev) +{ + u8 *eeprom; + int i, ret; + + ret = mt7601u_efuse_physical_size_check(dev); + if (ret) + return ret; + + dev->ee = devm_kzalloc(dev->dev, sizeof(*dev->ee), GFP_KERNEL); + if (!dev->ee) + return -ENOMEM; + + eeprom = kmalloc(MT7601U_EEPROM_SIZE, GFP_KERNEL); + if (!eeprom) + return -ENOMEM; + + for (i = 0; i + 16 <= MT7601U_EEPROM_SIZE; i += 16) { + ret = mt7601u_efuse_read(dev, i, eeprom + i, MT_EE_READ); + if (ret) + goto out; + } + + if (eeprom[MT_EE_VERSION_EE] > MT7601U_EE_MAX_VER) + dev_warn(dev->dev, + "Warning: unsupported EEPROM version %02hhx\n", + eeprom[MT_EE_VERSION_EE]); + dev_info(dev->dev, "EEPROM ver:%02hhx fae:%02hhx\n", + eeprom[MT_EE_VERSION_EE], eeprom[MT_EE_VERSION_FAE]); + + mt7601u_set_macaddr(dev, eeprom + MT_EE_MAC_ADDR); + mt7601u_set_chip_cap(dev, eeprom); + mt7601u_set_channel_power(dev, eeprom); + mt7601u_set_country_reg(dev, eeprom); + mt7601u_set_rf_freq_off(dev, eeprom); + mt7601u_set_rssi_offset(dev, eeprom); + dev->ee->ref_temp = eeprom[MT_EE_REF_TEMP]; + dev->ee->lna_gain = eeprom[MT_EE_LNA_GAIN]; + + mt7601u_config_tx_power_per_rate(dev, eeprom); + + mt7601u_init_tssi_params(dev, eeprom); +out: + kfree(eeprom); + return ret; +} diff --git a/drivers/net/wireless/mediatek/mt7601u/eeprom.h b/drivers/net/wireless/mediatek/mt7601u/eeprom.h new file mode 100644 index 000000000..4d3fd49f2 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt7601u/eeprom.h @@ -0,0 +1,143 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2014 Felix Fietkau + * Copyright (C) 2015 Jakub Kicinski + */ + +#ifndef __MT7601U_EEPROM_H +#define __MT7601U_EEPROM_H + +struct mt7601u_dev; + +#define MT7601U_EE_MAX_VER 0x0d +#define MT7601U_EEPROM_SIZE 256 + +#define MT7601U_DEFAULT_TX_POWER 6 + +enum mt76_eeprom_field { + MT_EE_CHIP_ID = 0x00, + MT_EE_VERSION_FAE = 0x02, + MT_EE_VERSION_EE = 0x03, + MT_EE_MAC_ADDR = 0x04, + MT_EE_NIC_CONF_0 = 0x34, + MT_EE_NIC_CONF_1 = 0x36, + MT_EE_COUNTRY_REGION = 0x39, + MT_EE_FREQ_OFFSET = 0x3a, + MT_EE_NIC_CONF_2 = 0x42, + + MT_EE_LNA_GAIN = 0x44, + MT_EE_RSSI_OFFSET = 0x46, + + MT_EE_TX_POWER_DELTA_BW40 = 0x50, + MT_EE_TX_POWER_OFFSET = 0x52, + + MT_EE_TX_TSSI_SLOPE = 0x6e, + MT_EE_TX_TSSI_OFFSET_GROUP = 0x6f, + MT_EE_TX_TSSI_OFFSET = 0x76, + + MT_EE_TX_TSSI_TARGET_POWER = 0xd0, + MT_EE_REF_TEMP = 0xd1, + MT_EE_FREQ_OFFSET_COMPENSATION = 0xdb, + MT_EE_TX_POWER_BYRATE_BASE = 0xde, + + MT_EE_USAGE_MAP_START = 0x1e0, + MT_EE_USAGE_MAP_END = 0x1fc, +}; + +#define MT_EE_NIC_CONF_0_RX_PATH GENMASK(3, 0) +#define MT_EE_NIC_CONF_0_TX_PATH GENMASK(7, 4) +#define MT_EE_NIC_CONF_0_BOARD_TYPE GENMASK(13, 12) + +#define MT_EE_NIC_CONF_1_HW_RF_CTRL BIT(0) +#define MT_EE_NIC_CONF_1_TEMP_TX_ALC BIT(1) +#define MT_EE_NIC_CONF_1_LNA_EXT_2G BIT(2) +#define MT_EE_NIC_CONF_1_LNA_EXT_5G BIT(3) +#define MT_EE_NIC_CONF_1_TX_ALC_EN BIT(13) + +#define MT_EE_NIC_CONF_2_RX_STREAM GENMASK(3, 0) +#define MT_EE_NIC_CONF_2_TX_STREAM GENMASK(7, 4) +#define MT_EE_NIC_CONF_2_HW_ANTDIV BIT(8) +#define MT_EE_NIC_CONF_2_XTAL_OPTION GENMASK(10, 9) +#define MT_EE_NIC_CONF_2_TEMP_DISABLE BIT(11) +#define MT_EE_NIC_CONF_2_COEX_METHOD GENMASK(15, 13) + +#define MT_EE_TX_POWER_BYRATE(i) (MT_EE_TX_POWER_BYRATE_BASE + \ + (i) * 4) + +#define MT_EFUSE_USAGE_MAP_SIZE (MT_EE_USAGE_MAP_END - \ + MT_EE_USAGE_MAP_START + 1) + +enum mt7601u_eeprom_access_modes { + MT_EE_READ = 0, + MT_EE_PHYSICAL_READ = 1, +}; + +struct power_per_rate { + u8 raw; /* validated s6 value */ + s8 bw20; /* sign-extended int */ + s8 bw40; /* sign-extended int */ +}; + +/* Power per rate - one value per two rates */ +struct mt7601u_rate_power { + struct power_per_rate cck[2]; + struct power_per_rate ofdm[4]; + struct power_per_rate ht[4]; +}; + +struct reg_channel_bounds { + u8 start; + u8 num; +}; + +struct mt7601u_eeprom_params { + bool tssi_enabled; + u8 rf_freq_off; + s8 rssi_offset[2]; + s8 ref_temp; + s8 lna_gain; + + u8 chan_pwr[14]; + struct mt7601u_rate_power power_rate_table; + s8 real_cck_bw20[2]; + + /* TSSI stuff - only with internal TX ALC */ + struct tssi_data { + int tx0_delta_offset; + u8 slope; + u8 offset[3]; + } tssi_data; + + struct reg_channel_bounds reg; +}; + +int mt7601u_eeprom_init(struct mt7601u_dev *dev); + +static inline u32 s6_validate(u32 reg) +{ + WARN_ON(reg & ~GENMASK(5, 0)); + return reg & GENMASK(5, 0); +} + +static inline int s6_to_int(u32 reg) +{ + int s6; + + s6 = s6_validate(reg); + if (s6 & BIT(5)) + s6 -= BIT(6); + + return s6; +} + +static inline u32 int_to_s6(int val) +{ + if (val < -0x20) + return 0x20; + if (val > 0x1f) + return 0x1f; + + return val & 0x3f; +} + +#endif diff --git a/drivers/net/wireless/mediatek/mt7601u/init.c b/drivers/net/wireless/mediatek/mt7601u/init.c new file mode 100644 index 000000000..5d9e952b2 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt7601u/init.c @@ -0,0 +1,631 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * (c) Copyright 2002-2010, Ralink Technology, Inc. + * Copyright (C) 2014 Felix Fietkau + * Copyright (C) 2015 Jakub Kicinski + */ + +#include "mt7601u.h" +#include "eeprom.h" +#include "trace.h" +#include "mcu.h" + +#include "initvals.h" + +static void +mt7601u_set_wlan_state(struct mt7601u_dev *dev, u32 val, bool enable) +{ + int i; + + /* Note: we don't turn off WLAN_CLK because that makes the device + * not respond properly on the probe path. + * In case anyone (PSM?) wants to use this function we can + * bring the clock stuff back and fixup the probe path. + */ + + if (enable) + val |= (MT_WLAN_FUN_CTRL_WLAN_EN | + MT_WLAN_FUN_CTRL_WLAN_CLK_EN); + else + val &= ~(MT_WLAN_FUN_CTRL_WLAN_EN); + + mt7601u_wr(dev, MT_WLAN_FUN_CTRL, val); + udelay(20); + + if (enable) { + set_bit(MT7601U_STATE_WLAN_RUNNING, &dev->state); + } else { + clear_bit(MT7601U_STATE_WLAN_RUNNING, &dev->state); + return; + } + + for (i = 200; i; i--) { + val = mt7601u_rr(dev, MT_CMB_CTRL); + + if (val & MT_CMB_CTRL_XTAL_RDY && val & MT_CMB_CTRL_PLL_LD) + break; + + udelay(20); + } + + /* Note: vendor driver tries to disable/enable wlan here and retry + * but the code which does it is so buggy it must have never + * triggered, so don't bother. + */ + if (!i) + dev_err(dev->dev, "Error: PLL and XTAL check failed!\n"); +} + +static void mt7601u_chip_onoff(struct mt7601u_dev *dev, bool enable, bool reset) +{ + u32 val; + + mutex_lock(&dev->hw_atomic_mutex); + + val = mt7601u_rr(dev, MT_WLAN_FUN_CTRL); + + if (reset) { + val |= MT_WLAN_FUN_CTRL_GPIO_OUT_EN; + val &= ~MT_WLAN_FUN_CTRL_FRC_WL_ANT_SEL; + + if (val & MT_WLAN_FUN_CTRL_WLAN_EN) { + val |= (MT_WLAN_FUN_CTRL_WLAN_RESET | + MT_WLAN_FUN_CTRL_WLAN_RESET_RF); + mt7601u_wr(dev, MT_WLAN_FUN_CTRL, val); + udelay(20); + + val &= ~(MT_WLAN_FUN_CTRL_WLAN_RESET | + MT_WLAN_FUN_CTRL_WLAN_RESET_RF); + } + } + + mt7601u_wr(dev, MT_WLAN_FUN_CTRL, val); + udelay(20); + + mt7601u_set_wlan_state(dev, val, enable); + + mutex_unlock(&dev->hw_atomic_mutex); +} + +static void mt7601u_reset_csr_bbp(struct mt7601u_dev *dev) +{ + mt7601u_wr(dev, MT_MAC_SYS_CTRL, (MT_MAC_SYS_CTRL_RESET_CSR | + MT_MAC_SYS_CTRL_RESET_BBP)); + mt7601u_wr(dev, MT_USB_DMA_CFG, 0); + msleep(1); + mt7601u_wr(dev, MT_MAC_SYS_CTRL, 0); +} + +static void mt7601u_init_usb_dma(struct mt7601u_dev *dev) +{ + u32 val; + + val = FIELD_PREP(MT_USB_DMA_CFG_RX_BULK_AGG_TOUT, MT_USB_AGGR_TIMEOUT) | + FIELD_PREP(MT_USB_DMA_CFG_RX_BULK_AGG_LMT, + MT_USB_AGGR_SIZE_LIMIT) | + MT_USB_DMA_CFG_RX_BULK_EN | + MT_USB_DMA_CFG_TX_BULK_EN; + if (dev->in_max_packet == 512) + val |= MT_USB_DMA_CFG_RX_BULK_AGG_EN; + mt7601u_wr(dev, MT_USB_DMA_CFG, val); + + val |= MT_USB_DMA_CFG_UDMA_RX_WL_DROP; + mt7601u_wr(dev, MT_USB_DMA_CFG, val); + val &= ~MT_USB_DMA_CFG_UDMA_RX_WL_DROP; + mt7601u_wr(dev, MT_USB_DMA_CFG, val); +} + +static int mt7601u_init_bbp(struct mt7601u_dev *dev) +{ + int ret; + + ret = mt7601u_wait_bbp_ready(dev); + if (ret) + return ret; + + ret = mt7601u_write_reg_pairs(dev, MT_MCU_MEMMAP_BBP, bbp_common_vals, + ARRAY_SIZE(bbp_common_vals)); + if (ret) + return ret; + + return mt7601u_write_reg_pairs(dev, MT_MCU_MEMMAP_BBP, bbp_chip_vals, + ARRAY_SIZE(bbp_chip_vals)); +} + +static void +mt76_init_beacon_offsets(struct mt7601u_dev *dev) +{ + u16 base = MT_BEACON_BASE; + u32 regs[4] = {}; + int i; + + for (i = 0; i < 16; i++) { + u16 addr = dev->beacon_offsets[i]; + + regs[i / 4] |= ((addr - base) / 64) << (8 * (i % 4)); + } + + for (i = 0; i < 4; i++) + mt7601u_wr(dev, MT_BCN_OFFSET(i), regs[i]); +} + +static int mt7601u_write_mac_initvals(struct mt7601u_dev *dev) +{ + int ret; + + ret = mt7601u_write_reg_pairs(dev, MT_MCU_MEMMAP_WLAN, mac_common_vals, + ARRAY_SIZE(mac_common_vals)); + if (ret) + return ret; + ret = mt7601u_write_reg_pairs(dev, MT_MCU_MEMMAP_WLAN, + mac_chip_vals, ARRAY_SIZE(mac_chip_vals)); + if (ret) + return ret; + + mt76_init_beacon_offsets(dev); + + mt7601u_wr(dev, MT_AUX_CLK_CFG, 0); + + return 0; +} + +static int mt7601u_init_wcid_mem(struct mt7601u_dev *dev) +{ + u32 *vals; + int i, ret; + + vals = kmalloc(sizeof(*vals) * N_WCIDS * 2, GFP_KERNEL); + if (!vals) + return -ENOMEM; + + for (i = 0; i < N_WCIDS; i++) { + vals[i * 2] = 0xffffffff; + vals[i * 2 + 1] = 0x00ffffff; + } + + ret = mt7601u_burst_write_regs(dev, MT_WCID_ADDR_BASE, + vals, N_WCIDS * 2); + kfree(vals); + + return ret; +} + +static int mt7601u_init_key_mem(struct mt7601u_dev *dev) +{ + u32 vals[4] = {}; + + return mt7601u_burst_write_regs(dev, MT_SKEY_MODE_BASE_0, + vals, ARRAY_SIZE(vals)); +} + +static int mt7601u_init_wcid_attr_mem(struct mt7601u_dev *dev) +{ + u32 *vals; + int i, ret; + + vals = kmalloc(sizeof(*vals) * N_WCIDS * 2, GFP_KERNEL); + if (!vals) + return -ENOMEM; + + for (i = 0; i < N_WCIDS * 2; i++) + vals[i] = 1; + + ret = mt7601u_burst_write_regs(dev, MT_WCID_ATTR_BASE, + vals, N_WCIDS * 2); + kfree(vals); + + return ret; +} + +static void mt7601u_reset_counters(struct mt7601u_dev *dev) +{ + mt7601u_rr(dev, MT_RX_STA_CNT0); + mt7601u_rr(dev, MT_RX_STA_CNT1); + mt7601u_rr(dev, MT_RX_STA_CNT2); + mt7601u_rr(dev, MT_TX_STA_CNT0); + mt7601u_rr(dev, MT_TX_STA_CNT1); + mt7601u_rr(dev, MT_TX_STA_CNT2); +} + +int mt7601u_mac_start(struct mt7601u_dev *dev) +{ + mt7601u_wr(dev, MT_MAC_SYS_CTRL, MT_MAC_SYS_CTRL_ENABLE_TX); + + if (!mt76_poll(dev, MT_WPDMA_GLO_CFG, MT_WPDMA_GLO_CFG_TX_DMA_BUSY | + MT_WPDMA_GLO_CFG_RX_DMA_BUSY, 0, 200000)) + return -ETIMEDOUT; + + dev->rxfilter = MT_RX_FILTR_CFG_CRC_ERR | + MT_RX_FILTR_CFG_PHY_ERR | MT_RX_FILTR_CFG_PROMISC | + MT_RX_FILTR_CFG_VER_ERR | MT_RX_FILTR_CFG_DUP | + MT_RX_FILTR_CFG_CFACK | MT_RX_FILTR_CFG_CFEND | + MT_RX_FILTR_CFG_ACK | MT_RX_FILTR_CFG_CTS | + MT_RX_FILTR_CFG_RTS | MT_RX_FILTR_CFG_PSPOLL | + MT_RX_FILTR_CFG_BA | MT_RX_FILTR_CFG_CTRL_RSV; + mt7601u_wr(dev, MT_RX_FILTR_CFG, dev->rxfilter); + + mt7601u_wr(dev, MT_MAC_SYS_CTRL, + MT_MAC_SYS_CTRL_ENABLE_TX | MT_MAC_SYS_CTRL_ENABLE_RX); + + if (!mt76_poll(dev, MT_WPDMA_GLO_CFG, MT_WPDMA_GLO_CFG_TX_DMA_BUSY | + MT_WPDMA_GLO_CFG_RX_DMA_BUSY, 0, 50)) + return -ETIMEDOUT; + + return 0; +} + +static void mt7601u_mac_stop_hw(struct mt7601u_dev *dev) +{ + int i, ok; + + if (test_bit(MT7601U_STATE_REMOVED, &dev->state)) + return; + + mt76_clear(dev, MT_BEACON_TIME_CFG, MT_BEACON_TIME_CFG_TIMER_EN | + MT_BEACON_TIME_CFG_SYNC_MODE | MT_BEACON_TIME_CFG_TBTT_EN | + MT_BEACON_TIME_CFG_BEACON_TX); + + if (!mt76_poll(dev, MT_USB_DMA_CFG, MT_USB_DMA_CFG_TX_BUSY, 0, 1000)) + dev_warn(dev->dev, "Warning: TX DMA did not stop!\n"); + + /* Page count on TxQ */ + i = 200; + while (i-- && ((mt76_rr(dev, 0x0438) & 0xffffffff) || + (mt76_rr(dev, 0x0a30) & 0x000000ff) || + (mt76_rr(dev, 0x0a34) & 0x00ff00ff))) + msleep(10); + + if (!mt76_poll(dev, MT_MAC_STATUS, MT_MAC_STATUS_TX, 0, 1000)) + dev_warn(dev->dev, "Warning: MAC TX did not stop!\n"); + + mt76_clear(dev, MT_MAC_SYS_CTRL, MT_MAC_SYS_CTRL_ENABLE_RX | + MT_MAC_SYS_CTRL_ENABLE_TX); + + /* Page count on RxQ */ + ok = 0; + i = 200; + while (i--) { + if (!(mt76_rr(dev, MT_RXQ_STA) & 0x00ff0000) && + !mt76_rr(dev, 0x0a30) && + !mt76_rr(dev, 0x0a34)) { + if (ok++ > 5) + break; + continue; + } + msleep(1); + } + + if (!mt76_poll(dev, MT_MAC_STATUS, MT_MAC_STATUS_RX, 0, 1000)) + dev_warn(dev->dev, "Warning: MAC RX did not stop!\n"); + + if (!mt76_poll(dev, MT_USB_DMA_CFG, MT_USB_DMA_CFG_RX_BUSY, 0, 1000)) + dev_warn(dev->dev, "Warning: RX DMA did not stop!\n"); +} + +void mt7601u_mac_stop(struct mt7601u_dev *dev) +{ + mt7601u_mac_stop_hw(dev); + flush_delayed_work(&dev->stat_work); + cancel_delayed_work_sync(&dev->stat_work); +} + +static void mt7601u_stop_hardware(struct mt7601u_dev *dev) +{ + mt7601u_chip_onoff(dev, false, false); +} + +int mt7601u_init_hardware(struct mt7601u_dev *dev) +{ + static const u16 beacon_offsets[16] = { + /* 512 byte per beacon */ + 0xc000, 0xc200, 0xc400, 0xc600, + 0xc800, 0xca00, 0xcc00, 0xce00, + 0xd000, 0xd200, 0xd400, 0xd600, + 0xd800, 0xda00, 0xdc00, 0xde00 + }; + int ret; + + dev->beacon_offsets = beacon_offsets; + + mt7601u_chip_onoff(dev, true, false); + + ret = mt7601u_wait_asic_ready(dev); + if (ret) + goto err; + ret = mt7601u_mcu_init(dev); + if (ret) + goto err; + + if (!mt76_poll_msec(dev, MT_WPDMA_GLO_CFG, + MT_WPDMA_GLO_CFG_TX_DMA_BUSY | + MT_WPDMA_GLO_CFG_RX_DMA_BUSY, 0, 100)) { + ret = -EIO; + goto err; + } + + /* Wait for ASIC ready after FW load. */ + ret = mt7601u_wait_asic_ready(dev); + if (ret) + goto err; + + mt7601u_reset_csr_bbp(dev); + mt7601u_init_usb_dma(dev); + + ret = mt7601u_mcu_cmd_init(dev); + if (ret) + goto err; + ret = mt7601u_dma_init(dev); + if (ret) + goto err_mcu; + ret = mt7601u_write_mac_initvals(dev); + if (ret) + goto err_rx; + + if (!mt76_poll_msec(dev, MT_MAC_STATUS, + MT_MAC_STATUS_TX | MT_MAC_STATUS_RX, 0, 100)) { + ret = -EIO; + goto err_rx; + } + + ret = mt7601u_init_bbp(dev); + if (ret) + goto err_rx; + ret = mt7601u_init_wcid_mem(dev); + if (ret) + goto err_rx; + ret = mt7601u_init_key_mem(dev); + if (ret) + goto err_rx; + ret = mt7601u_init_wcid_attr_mem(dev); + if (ret) + goto err_rx; + + mt76_clear(dev, MT_BEACON_TIME_CFG, (MT_BEACON_TIME_CFG_TIMER_EN | + MT_BEACON_TIME_CFG_SYNC_MODE | + MT_BEACON_TIME_CFG_TBTT_EN | + MT_BEACON_TIME_CFG_BEACON_TX)); + + mt7601u_reset_counters(dev); + + mt7601u_rmw(dev, MT_US_CYC_CFG, MT_US_CYC_CNT, 0x1e); + + mt7601u_wr(dev, MT_TXOP_CTRL_CFG, + FIELD_PREP(MT_TXOP_TRUN_EN, 0x3f) | + FIELD_PREP(MT_TXOP_EXT_CCA_DLY, 0x58)); + + ret = mt7601u_eeprom_init(dev); + if (ret) + goto err_rx; + + ret = mt7601u_phy_init(dev); + if (ret) + goto err_rx; + + mt7601u_set_rx_path(dev, 0); + mt7601u_set_tx_dac(dev, 0); + + mt7601u_mac_set_ctrlch(dev, false); + mt7601u_bbp_set_ctrlch(dev, false); + mt7601u_bbp_set_bw(dev, MT_BW_20); + + return 0; + +err_rx: + mt7601u_dma_cleanup(dev); +err_mcu: + mt7601u_mcu_cmd_deinit(dev); +err: + mt7601u_chip_onoff(dev, false, false); + return ret; +} + +void mt7601u_cleanup(struct mt7601u_dev *dev) +{ + if (!test_and_clear_bit(MT7601U_STATE_INITIALIZED, &dev->state)) + return; + + mt7601u_stop_hardware(dev); + mt7601u_dma_cleanup(dev); + mt7601u_mcu_cmd_deinit(dev); +} + +struct mt7601u_dev *mt7601u_alloc_device(struct device *pdev) +{ + struct ieee80211_hw *hw; + struct mt7601u_dev *dev; + + hw = ieee80211_alloc_hw(sizeof(*dev), &mt7601u_ops); + if (!hw) + return NULL; + + dev = hw->priv; + dev->dev = pdev; + dev->hw = hw; + mutex_init(&dev->vendor_req_mutex); + mutex_init(&dev->reg_atomic_mutex); + mutex_init(&dev->hw_atomic_mutex); + mutex_init(&dev->mutex); + spin_lock_init(&dev->tx_lock); + spin_lock_init(&dev->rx_lock); + spin_lock_init(&dev->lock); + spin_lock_init(&dev->mac_lock); + spin_lock_init(&dev->con_mon_lock); + atomic_set(&dev->avg_ampdu_len, 1); + skb_queue_head_init(&dev->tx_skb_done); + + dev->stat_wq = alloc_workqueue("mt7601u", WQ_UNBOUND, 0); + if (!dev->stat_wq) { + ieee80211_free_hw(hw); + return NULL; + } + + return dev; +} + +#define CHAN2G(_idx, _freq) { \ + .band = NL80211_BAND_2GHZ, \ + .center_freq = (_freq), \ + .hw_value = (_idx), \ + .max_power = 30, \ +} + +static const struct ieee80211_channel mt76_channels_2ghz[] = { + CHAN2G(1, 2412), + CHAN2G(2, 2417), + CHAN2G(3, 2422), + CHAN2G(4, 2427), + CHAN2G(5, 2432), + CHAN2G(6, 2437), + CHAN2G(7, 2442), + CHAN2G(8, 2447), + CHAN2G(9, 2452), + CHAN2G(10, 2457), + CHAN2G(11, 2462), + CHAN2G(12, 2467), + CHAN2G(13, 2472), + CHAN2G(14, 2484), +}; + +#define CCK_RATE(_idx, _rate) { \ + .bitrate = _rate, \ + .flags = IEEE80211_RATE_SHORT_PREAMBLE, \ + .hw_value = (MT_PHY_TYPE_CCK << 8) | _idx, \ + .hw_value_short = (MT_PHY_TYPE_CCK << 8) | (8 + _idx), \ +} + +#define OFDM_RATE(_idx, _rate) { \ + .bitrate = _rate, \ + .hw_value = (MT_PHY_TYPE_OFDM << 8) | _idx, \ + .hw_value_short = (MT_PHY_TYPE_OFDM << 8) | _idx, \ +} + +static struct ieee80211_rate mt76_rates[] = { + CCK_RATE(0, 10), + CCK_RATE(1, 20), + CCK_RATE(2, 55), + CCK_RATE(3, 110), + OFDM_RATE(0, 60), + OFDM_RATE(1, 90), + OFDM_RATE(2, 120), + OFDM_RATE(3, 180), + OFDM_RATE(4, 240), + OFDM_RATE(5, 360), + OFDM_RATE(6, 480), + OFDM_RATE(7, 540), +}; + +static int +mt76_init_sband(struct mt7601u_dev *dev, struct ieee80211_supported_band *sband, + const struct ieee80211_channel *chan, int n_chan, + struct ieee80211_rate *rates, int n_rates) +{ + struct ieee80211_sta_ht_cap *ht_cap; + void *chanlist; + int size; + + size = n_chan * sizeof(*chan); + chanlist = devm_kmemdup(dev->dev, chan, size, GFP_KERNEL); + if (!chanlist) + return -ENOMEM; + + sband->channels = chanlist; + sband->n_channels = n_chan; + sband->bitrates = rates; + sband->n_bitrates = n_rates; + + ht_cap = &sband->ht_cap; + ht_cap->ht_supported = true; + ht_cap->cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 | + IEEE80211_HT_CAP_GRN_FLD | + IEEE80211_HT_CAP_SGI_20 | + IEEE80211_HT_CAP_SGI_40 | + (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT); + + ht_cap->mcs.rx_mask[0] = 0xff; + ht_cap->mcs.rx_mask[4] = 0x1; + ht_cap->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; + ht_cap->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; + ht_cap->ampdu_density = IEEE80211_HT_MPDU_DENSITY_2; + + dev->chandef.chan = &sband->channels[0]; + + return 0; +} + +static int +mt76_init_sband_2g(struct mt7601u_dev *dev) +{ + dev->sband_2g = devm_kzalloc(dev->dev, sizeof(*dev->sband_2g), + GFP_KERNEL); + if (!dev->sband_2g) + return -ENOMEM; + + dev->hw->wiphy->bands[NL80211_BAND_2GHZ] = dev->sband_2g; + + WARN_ON(dev->ee->reg.start - 1 + dev->ee->reg.num > + ARRAY_SIZE(mt76_channels_2ghz)); + + return mt76_init_sband(dev, dev->sband_2g, + &mt76_channels_2ghz[dev->ee->reg.start - 1], + dev->ee->reg.num, + mt76_rates, ARRAY_SIZE(mt76_rates)); +} + +int mt7601u_register_device(struct mt7601u_dev *dev) +{ + struct ieee80211_hw *hw = dev->hw; + struct wiphy *wiphy = hw->wiphy; + int ret; + + /* Reserve WCID 0 for mcast - thanks to this APs WCID will go to + * entry no. 1 like it does in the vendor driver. + */ + dev->wcid_mask[0] |= 1; + + /* init fake wcid for monitor interfaces */ + dev->mon_wcid = devm_kmalloc(dev->dev, sizeof(*dev->mon_wcid), + GFP_KERNEL); + if (!dev->mon_wcid) + return -ENOMEM; + dev->mon_wcid->idx = 0xff; + dev->mon_wcid->hw_key_idx = -1; + + SET_IEEE80211_DEV(hw, dev->dev); + + hw->queues = 4; + ieee80211_hw_set(hw, SIGNAL_DBM); + ieee80211_hw_set(hw, PS_NULLFUNC_STACK); + ieee80211_hw_set(hw, SUPPORTS_HT_CCK_RATES); + ieee80211_hw_set(hw, AMPDU_AGGREGATION); + ieee80211_hw_set(hw, SUPPORTS_RC_TABLE); + ieee80211_hw_set(hw, MFP_CAPABLE); + hw->max_rates = 1; + hw->max_report_rates = 7; + hw->max_rate_tries = 1; + + hw->sta_data_size = sizeof(struct mt76_sta); + hw->vif_data_size = sizeof(struct mt76_vif); + + SET_IEEE80211_PERM_ADDR(hw, dev->macaddr); + + wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR; + wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); + wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS; + + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); + + ret = mt76_init_sband_2g(dev); + if (ret) + return ret; + + INIT_DELAYED_WORK(&dev->mac_work, mt7601u_mac_work); + INIT_DELAYED_WORK(&dev->stat_work, mt7601u_tx_stat); + + ret = ieee80211_register_hw(hw); + if (ret) + return ret; + + mt7601u_init_debugfs(dev); + + return 0; +} diff --git a/drivers/net/wireless/mediatek/mt7601u/initvals.h b/drivers/net/wireless/mediatek/mt7601u/initvals.h new file mode 100644 index 000000000..59acbf570 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt7601u/initvals.h @@ -0,0 +1,157 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * (c) Copyright 2002-2010, Ralink Technology, Inc. + * Copyright (C) 2015 Jakub Kicinski + */ + +#ifndef __MT7601U_INITVALS_H +#define __MT7601U_INITVALS_H + +static const struct mt76_reg_pair bbp_common_vals[] = { + { 65, 0x2c }, + { 66, 0x38 }, + { 68, 0x0b }, + { 69, 0x12 }, + { 70, 0x0a }, + { 73, 0x10 }, + { 81, 0x37 }, + { 82, 0x62 }, + { 83, 0x6a }, + { 84, 0x99 }, + { 86, 0x00 }, + { 91, 0x04 }, + { 92, 0x00 }, + { 103, 0x00 }, + { 105, 0x05 }, + { 106, 0x35 }, +}; + +static const struct mt76_reg_pair bbp_chip_vals[] = { + { 1, 0x04 }, { 4, 0x40 }, { 20, 0x06 }, { 31, 0x08 }, + /* CCK Tx Control */ + { 178, 0xff }, + /* AGC/Sync controls */ + { 66, 0x14 }, { 68, 0x8b }, { 69, 0x12 }, { 70, 0x09 }, + { 73, 0x11 }, { 75, 0x60 }, { 76, 0x44 }, { 84, 0x9a }, + { 86, 0x38 }, { 91, 0x07 }, { 92, 0x02 }, + /* Rx Path Controls */ + { 99, 0x50 }, { 101, 0x00 }, { 103, 0xc0 }, { 104, 0x92 }, + { 105, 0x3c }, { 106, 0x03 }, { 128, 0x12 }, + /* Change RXWI content: Gain Report */ + { 142, 0x04 }, { 143, 0x37 }, + /* Change RXWI content: Antenna Report */ + { 142, 0x03 }, { 143, 0x99 }, + /* Calibration Index Register */ + /* CCK Receiver Control */ + { 160, 0xeb }, { 161, 0xc4 }, { 162, 0x77 }, { 163, 0xf9 }, + { 164, 0x88 }, { 165, 0x80 }, { 166, 0xff }, { 167, 0xe4 }, + /* Added AGC controls - these AGC/GLRT registers are accessed + * through R195 and R196. + */ + { 195, 0x00 }, { 196, 0x00 }, + { 195, 0x01 }, { 196, 0x04 }, + { 195, 0x02 }, { 196, 0x20 }, + { 195, 0x03 }, { 196, 0x0a }, + { 195, 0x06 }, { 196, 0x16 }, + { 195, 0x07 }, { 196, 0x05 }, + { 195, 0x08 }, { 196, 0x37 }, + { 195, 0x0a }, { 196, 0x15 }, + { 195, 0x0b }, { 196, 0x17 }, + { 195, 0x0c }, { 196, 0x06 }, + { 195, 0x0d }, { 196, 0x09 }, + { 195, 0x0e }, { 196, 0x05 }, + { 195, 0x0f }, { 196, 0x09 }, + { 195, 0x10 }, { 196, 0x20 }, + { 195, 0x20 }, { 196, 0x17 }, + { 195, 0x21 }, { 196, 0x06 }, + { 195, 0x22 }, { 196, 0x09 }, + { 195, 0x23 }, { 196, 0x17 }, + { 195, 0x24 }, { 196, 0x06 }, + { 195, 0x25 }, { 196, 0x09 }, + { 195, 0x26 }, { 196, 0x17 }, + { 195, 0x27 }, { 196, 0x06 }, + { 195, 0x28 }, { 196, 0x09 }, + { 195, 0x29 }, { 196, 0x05 }, + { 195, 0x2a }, { 196, 0x09 }, + { 195, 0x80 }, { 196, 0x8b }, + { 195, 0x81 }, { 196, 0x12 }, + { 195, 0x82 }, { 196, 0x09 }, + { 195, 0x83 }, { 196, 0x17 }, + { 195, 0x84 }, { 196, 0x11 }, + { 195, 0x85 }, { 196, 0x00 }, + { 195, 0x86 }, { 196, 0x00 }, + { 195, 0x87 }, { 196, 0x18 }, + { 195, 0x88 }, { 196, 0x60 }, + { 195, 0x89 }, { 196, 0x44 }, + { 195, 0x8a }, { 196, 0x8b }, + { 195, 0x8b }, { 196, 0x8b }, + { 195, 0x8c }, { 196, 0x8b }, + { 195, 0x8d }, { 196, 0x8b }, + { 195, 0x8e }, { 196, 0x09 }, + { 195, 0x8f }, { 196, 0x09 }, + { 195, 0x90 }, { 196, 0x09 }, + { 195, 0x91 }, { 196, 0x09 }, + { 195, 0x92 }, { 196, 0x11 }, + { 195, 0x93 }, { 196, 0x11 }, + { 195, 0x94 }, { 196, 0x11 }, + { 195, 0x95 }, { 196, 0x11 }, + /* PPAD */ + { 47, 0x80 }, { 60, 0x80 }, { 150, 0xd2 }, { 151, 0x32 }, + { 152, 0x23 }, { 153, 0x41 }, { 154, 0x00 }, { 155, 0x4f }, + { 253, 0x7e }, { 195, 0x30 }, { 196, 0x32 }, { 195, 0x31 }, + { 196, 0x23 }, { 195, 0x32 }, { 196, 0x45 }, { 195, 0x35 }, + { 196, 0x4a }, { 195, 0x36 }, { 196, 0x5a }, { 195, 0x37 }, + { 196, 0x5a }, +}; + +static const struct mt76_reg_pair mac_common_vals[] = { + { MT_LEGACY_BASIC_RATE, 0x0000013f }, + { MT_HT_BASIC_RATE, 0x00008003 }, + { MT_MAC_SYS_CTRL, 0x00000000 }, + { MT_RX_FILTR_CFG, 0x00017f97 }, + { MT_BKOFF_SLOT_CFG, 0x00000209 }, + { MT_TX_SW_CFG0, 0x00000000 }, + { MT_TX_SW_CFG1, 0x00080606 }, + { MT_TX_LINK_CFG, 0x00001020 }, + { MT_TX_TIMEOUT_CFG, 0x000a2090 }, + { MT_MAX_LEN_CFG, 0x00003fff }, + { MT_PBF_TX_MAX_PCNT, 0x1fbf1f1f }, + { MT_PBF_RX_MAX_PCNT, 0x0000009f }, + { MT_TX_RETRY_CFG, 0x47d01f0f }, + { MT_AUTO_RSP_CFG, 0x00000013 }, + { MT_CCK_PROT_CFG, 0x05740003 }, + { MT_OFDM_PROT_CFG, 0x05740003 }, + { MT_MM40_PROT_CFG, 0x03f44084 }, + { MT_GF20_PROT_CFG, 0x01744004 }, + { MT_GF40_PROT_CFG, 0x03f44084 }, + { MT_MM20_PROT_CFG, 0x01744004 }, + { MT_TXOP_CTRL_CFG, 0x0000583f }, + { MT_TX_RTS_CFG, 0x01092b20 }, + { MT_EXP_ACK_TIME, 0x002400ca }, + { MT_TXOP_HLDR_ET, 0x00000002 }, + { MT_XIFS_TIME_CFG, 0x33a41010 }, + { MT_PWR_PIN_CFG, 0x00000000 }, + { MT_PN_PAD_MODE, 0x00000001 }, +}; + +static const struct mt76_reg_pair mac_chip_vals[] = { + { MT_TSO_CTRL, 0x00006050 }, + { MT_BCN_OFFSET(0), 0x18100800 }, + { MT_BCN_OFFSET(1), 0x38302820 }, + { MT_PBF_SYS_CTRL, 0x00080c00 }, + { MT_PBF_CFG, 0x7f723c1f }, + { MT_FCE_PSE_CTRL, 0x00000001 }, + { MT_PAUSE_ENABLE_CONTROL1, 0x00000000 }, + { MT_TX0_RF_GAIN_CORR, 0x003b0005 }, + { MT_TX0_RF_GAIN_ATTEN, 0x00006900 }, + { MT_TX0_BB_GAIN_ATTEN, 0x00000400 }, + { MT_TX_ALC_VGA3, 0x00060006 }, + { MT_TX_SW_CFG0, 0x00000402 }, + { MT_TX_SW_CFG1, 0x00000000 }, + { MT_TX_SW_CFG2, 0x00000000 }, + { MT_HEADER_TRANS_CTRL_REG, 0x00000000 }, + { MT_FCE_CSO, 0x0000030f }, + { MT_FCE_PARAMETERS, 0x00256f0f }, +}; + +#endif diff --git a/drivers/net/wireless/mediatek/mt7601u/initvals_phy.h b/drivers/net/wireless/mediatek/mt7601u/initvals_phy.h new file mode 100644 index 000000000..55cfedb24 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt7601u/initvals_phy.h @@ -0,0 +1,283 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * (c) Copyright 2002-2010, Ralink Technology, Inc. + * Copyright (C) 2015 Jakub Kicinski + */ + +#ifndef __MT7601U_PHY_INITVALS_H +#define __MT7601U_PHY_INITVALS_H + +#define RF_REG_PAIR(bank, reg, value) \ + { MT_MCU_MEMMAP_RF | (bank) << 16 | (reg), value } + +static const struct mt76_reg_pair rf_central[] = { + /* Bank 0 - for central blocks: BG, PLL, XTAL, LO, ADC/DAC */ + RF_REG_PAIR(0, 0, 0x02), + RF_REG_PAIR(0, 1, 0x01), + RF_REG_PAIR(0, 2, 0x11), + RF_REG_PAIR(0, 3, 0xff), + RF_REG_PAIR(0, 4, 0x0a), + RF_REG_PAIR(0, 5, 0x20), + RF_REG_PAIR(0, 6, 0x00), + /* B/G */ + RF_REG_PAIR(0, 7, 0x00), + RF_REG_PAIR(0, 8, 0x00), + RF_REG_PAIR(0, 9, 0x00), + RF_REG_PAIR(0, 10, 0x00), + RF_REG_PAIR(0, 11, 0x21), + /* XO */ + RF_REG_PAIR(0, 13, 0x00), /* 40mhz xtal */ + /* RF_REG_PAIR(0, 13, 0x13), */ /* 20mhz xtal */ + RF_REG_PAIR(0, 14, 0x7c), + RF_REG_PAIR(0, 15, 0x22), + RF_REG_PAIR(0, 16, 0x80), + /* PLL */ + RF_REG_PAIR(0, 17, 0x99), + RF_REG_PAIR(0, 18, 0x99), + RF_REG_PAIR(0, 19, 0x09), + RF_REG_PAIR(0, 20, 0x50), + RF_REG_PAIR(0, 21, 0xb0), + RF_REG_PAIR(0, 22, 0x00), + RF_REG_PAIR(0, 23, 0xc5), + RF_REG_PAIR(0, 24, 0xfc), + RF_REG_PAIR(0, 25, 0x40), + RF_REG_PAIR(0, 26, 0x4d), + RF_REG_PAIR(0, 27, 0x02), + RF_REG_PAIR(0, 28, 0x72), + RF_REG_PAIR(0, 29, 0x01), + RF_REG_PAIR(0, 30, 0x00), + RF_REG_PAIR(0, 31, 0x00), + /* test ports */ + RF_REG_PAIR(0, 32, 0x00), + RF_REG_PAIR(0, 33, 0x00), + RF_REG_PAIR(0, 34, 0x23), + RF_REG_PAIR(0, 35, 0x01), /* change setting to reduce spurs */ + RF_REG_PAIR(0, 36, 0x00), + RF_REG_PAIR(0, 37, 0x00), + /* ADC/DAC */ + RF_REG_PAIR(0, 38, 0x00), + RF_REG_PAIR(0, 39, 0x20), + RF_REG_PAIR(0, 40, 0x00), + RF_REG_PAIR(0, 41, 0xd0), + RF_REG_PAIR(0, 42, 0x1b), + RF_REG_PAIR(0, 43, 0x02), + RF_REG_PAIR(0, 44, 0x00), +}; + +static const struct mt76_reg_pair rf_channel[] = { + RF_REG_PAIR(4, 0, 0x01), + RF_REG_PAIR(4, 1, 0x00), + RF_REG_PAIR(4, 2, 0x00), + RF_REG_PAIR(4, 3, 0x00), + /* LDO */ + RF_REG_PAIR(4, 4, 0x00), + RF_REG_PAIR(4, 5, 0x08), + RF_REG_PAIR(4, 6, 0x00), + /* RX */ + RF_REG_PAIR(4, 7, 0x5b), + RF_REG_PAIR(4, 8, 0x52), + RF_REG_PAIR(4, 9, 0xb6), + RF_REG_PAIR(4, 10, 0x57), + RF_REG_PAIR(4, 11, 0x33), + RF_REG_PAIR(4, 12, 0x22), + RF_REG_PAIR(4, 13, 0x3d), + RF_REG_PAIR(4, 14, 0x3e), + RF_REG_PAIR(4, 15, 0x13), + RF_REG_PAIR(4, 16, 0x22), + RF_REG_PAIR(4, 17, 0x23), + RF_REG_PAIR(4, 18, 0x02), + RF_REG_PAIR(4, 19, 0xa4), + RF_REG_PAIR(4, 20, 0x01), + RF_REG_PAIR(4, 21, 0x12), + RF_REG_PAIR(4, 22, 0x80), + RF_REG_PAIR(4, 23, 0xb3), + RF_REG_PAIR(4, 24, 0x00), /* reserved */ + RF_REG_PAIR(4, 25, 0x00), /* reserved */ + RF_REG_PAIR(4, 26, 0x00), /* reserved */ + RF_REG_PAIR(4, 27, 0x00), /* reserved */ + /* LOGEN */ + RF_REG_PAIR(4, 28, 0x18), + RF_REG_PAIR(4, 29, 0xee), + RF_REG_PAIR(4, 30, 0x6b), + RF_REG_PAIR(4, 31, 0x31), + RF_REG_PAIR(4, 32, 0x5d), + RF_REG_PAIR(4, 33, 0x00), /* reserved */ + /* TX */ + RF_REG_PAIR(4, 34, 0x96), + RF_REG_PAIR(4, 35, 0x55), + RF_REG_PAIR(4, 36, 0x08), + RF_REG_PAIR(4, 37, 0xbb), + RF_REG_PAIR(4, 38, 0xb3), + RF_REG_PAIR(4, 39, 0xb3), + RF_REG_PAIR(4, 40, 0x03), + RF_REG_PAIR(4, 41, 0x00), /* reserved */ + RF_REG_PAIR(4, 42, 0x00), /* reserved */ + RF_REG_PAIR(4, 43, 0xc5), + RF_REG_PAIR(4, 44, 0xc5), + RF_REG_PAIR(4, 45, 0xc5), + RF_REG_PAIR(4, 46, 0x07), + RF_REG_PAIR(4, 47, 0xa8), + RF_REG_PAIR(4, 48, 0xef), + RF_REG_PAIR(4, 49, 0x1a), + /* PA */ + RF_REG_PAIR(4, 54, 0x07), + RF_REG_PAIR(4, 55, 0xa7), + RF_REG_PAIR(4, 56, 0xcc), + RF_REG_PAIR(4, 57, 0x14), + RF_REG_PAIR(4, 58, 0x07), + RF_REG_PAIR(4, 59, 0xa8), + RF_REG_PAIR(4, 60, 0xd7), + RF_REG_PAIR(4, 61, 0x10), + RF_REG_PAIR(4, 62, 0x1c), + RF_REG_PAIR(4, 63, 0x00), /* reserved */ +}; + +static const struct mt76_reg_pair rf_vga[] = { + RF_REG_PAIR(5, 0, 0x47), + RF_REG_PAIR(5, 1, 0x00), + RF_REG_PAIR(5, 2, 0x00), + RF_REG_PAIR(5, 3, 0x08), + RF_REG_PAIR(5, 4, 0x04), + RF_REG_PAIR(5, 5, 0x20), + RF_REG_PAIR(5, 6, 0x3a), + RF_REG_PAIR(5, 7, 0x3a), + RF_REG_PAIR(5, 8, 0x00), + RF_REG_PAIR(5, 9, 0x00), + RF_REG_PAIR(5, 10, 0x10), + RF_REG_PAIR(5, 11, 0x10), + RF_REG_PAIR(5, 12, 0x10), + RF_REG_PAIR(5, 13, 0x10), + RF_REG_PAIR(5, 14, 0x10), + RF_REG_PAIR(5, 15, 0x20), + RF_REG_PAIR(5, 16, 0x22), + RF_REG_PAIR(5, 17, 0x7c), + RF_REG_PAIR(5, 18, 0x00), + RF_REG_PAIR(5, 19, 0x00), + RF_REG_PAIR(5, 20, 0x00), + RF_REG_PAIR(5, 21, 0xf1), + RF_REG_PAIR(5, 22, 0x11), + RF_REG_PAIR(5, 23, 0x02), + RF_REG_PAIR(5, 24, 0x41), + RF_REG_PAIR(5, 25, 0x20), + RF_REG_PAIR(5, 26, 0x00), + RF_REG_PAIR(5, 27, 0xd7), + RF_REG_PAIR(5, 28, 0xa2), + RF_REG_PAIR(5, 29, 0x20), + RF_REG_PAIR(5, 30, 0x49), + RF_REG_PAIR(5, 31, 0x20), + RF_REG_PAIR(5, 32, 0x04), + RF_REG_PAIR(5, 33, 0xf1), + RF_REG_PAIR(5, 34, 0xa1), + RF_REG_PAIR(5, 35, 0x01), + RF_REG_PAIR(5, 41, 0x00), + RF_REG_PAIR(5, 42, 0x00), + RF_REG_PAIR(5, 43, 0x00), + RF_REG_PAIR(5, 44, 0x00), + RF_REG_PAIR(5, 45, 0x00), + RF_REG_PAIR(5, 46, 0x00), + RF_REG_PAIR(5, 47, 0x00), + RF_REG_PAIR(5, 48, 0x00), + RF_REG_PAIR(5, 49, 0x00), + RF_REG_PAIR(5, 50, 0x00), + RF_REG_PAIR(5, 51, 0x00), + RF_REG_PAIR(5, 52, 0x00), + RF_REG_PAIR(5, 53, 0x00), + RF_REG_PAIR(5, 54, 0x00), + RF_REG_PAIR(5, 55, 0x00), + RF_REG_PAIR(5, 56, 0x00), + RF_REG_PAIR(5, 57, 0x00), + RF_REG_PAIR(5, 58, 0x31), + RF_REG_PAIR(5, 59, 0x31), + RF_REG_PAIR(5, 60, 0x0a), + RF_REG_PAIR(5, 61, 0x02), + RF_REG_PAIR(5, 62, 0x00), + RF_REG_PAIR(5, 63, 0x00), +}; + +/* TODO: BBP178 is set to 0xff for "CCK CH14 OBW" which overrides the settings + * from channel switching. Seems stupid at best. + */ +static const struct mt76_reg_pair bbp_high_temp[] = { + { 75, 0x60 }, + { 92, 0x02 }, + { 178, 0xff }, /* For CCK CH14 OBW */ + { 195, 0x88 }, { 196, 0x60 }, +}, bbp_high_temp_bw20[] = { + { 69, 0x12 }, + { 91, 0x07 }, + { 195, 0x23 }, { 196, 0x17 }, + { 195, 0x24 }, { 196, 0x06 }, + { 195, 0x81 }, { 196, 0x12 }, + { 195, 0x83 }, { 196, 0x17 }, +}, bbp_high_temp_bw40[] = { + { 69, 0x15 }, + { 91, 0x04 }, + { 195, 0x23 }, { 196, 0x12 }, + { 195, 0x24 }, { 196, 0x08 }, + { 195, 0x81 }, { 196, 0x15 }, + { 195, 0x83 }, { 196, 0x16 }, +}, bbp_low_temp[] = { + { 178, 0xff }, /* For CCK CH14 OBW */ +}, bbp_low_temp_bw20[] = { + { 69, 0x12 }, + { 75, 0x5e }, + { 91, 0x07 }, + { 92, 0x02 }, + { 195, 0x23 }, { 196, 0x17 }, + { 195, 0x24 }, { 196, 0x06 }, + { 195, 0x81 }, { 196, 0x12 }, + { 195, 0x83 }, { 196, 0x17 }, + { 195, 0x88 }, { 196, 0x5e }, +}, bbp_low_temp_bw40[] = { + { 69, 0x15 }, + { 75, 0x5c }, + { 91, 0x04 }, + { 92, 0x03 }, + { 195, 0x23 }, { 196, 0x10 }, + { 195, 0x24 }, { 196, 0x08 }, + { 195, 0x81 }, { 196, 0x15 }, + { 195, 0x83 }, { 196, 0x16 }, + { 195, 0x88 }, { 196, 0x5b }, +}, bbp_normal_temp[] = { + { 75, 0x60 }, + { 92, 0x02 }, + { 178, 0xff }, /* For CCK CH14 OBW */ + { 195, 0x88 }, { 196, 0x60 }, +}, bbp_normal_temp_bw20[] = { + { 69, 0x12 }, + { 91, 0x07 }, + { 195, 0x23 }, { 196, 0x17 }, + { 195, 0x24 }, { 196, 0x06 }, + { 195, 0x81 }, { 196, 0x12 }, + { 195, 0x83 }, { 196, 0x17 }, +}, bbp_normal_temp_bw40[] = { + { 69, 0x15 }, + { 91, 0x04 }, + { 195, 0x23 }, { 196, 0x12 }, + { 195, 0x24 }, { 196, 0x08 }, + { 195, 0x81 }, { 196, 0x15 }, + { 195, 0x83 }, { 196, 0x16 }, +}; + +#define BBP_TABLE(arr) { arr, ARRAY_SIZE(arr), } + +static const struct reg_table { + const struct mt76_reg_pair *regs; + size_t n; +} bbp_mode_table[3][3] = { + { + BBP_TABLE(bbp_normal_temp_bw20), + BBP_TABLE(bbp_normal_temp_bw40), + BBP_TABLE(bbp_normal_temp), + }, { + BBP_TABLE(bbp_high_temp_bw20), + BBP_TABLE(bbp_high_temp_bw40), + BBP_TABLE(bbp_high_temp), + }, { + BBP_TABLE(bbp_low_temp_bw20), + BBP_TABLE(bbp_low_temp_bw40), + BBP_TABLE(bbp_low_temp), + } +}; + +#endif diff --git a/drivers/net/wireless/mediatek/mt7601u/mac.c b/drivers/net/wireless/mediatek/mt7601u/mac.c new file mode 100644 index 000000000..ca9cf628e --- /dev/null +++ b/drivers/net/wireless/mediatek/mt7601u/mac.c @@ -0,0 +1,593 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2014 Felix Fietkau + * Copyright (C) 2015 Jakub Kicinski + */ + +#include "mt7601u.h" +#include "trace.h" +#include + +void mt7601u_set_macaddr(struct mt7601u_dev *dev, const u8 *addr) +{ + ether_addr_copy(dev->macaddr, addr); + + 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); + } + + mt76_wr(dev, MT_MAC_ADDR_DW0, get_unaligned_le32(dev->macaddr)); + mt76_wr(dev, MT_MAC_ADDR_DW1, get_unaligned_le16(dev->macaddr + 4) | + FIELD_PREP(MT_MAC_ADDR_DW1_U2ME_MASK, 0xff)); +} + +static void +mt76_mac_process_tx_rate(struct ieee80211_tx_rate *txrate, u16 rate) +{ + u8 idx = FIELD_GET(MT_TXWI_RATE_MCS, rate); + + txrate->idx = 0; + txrate->flags = 0; + txrate->count = 1; + + switch (FIELD_GET(MT_TXWI_RATE_PHY_MODE, rate)) { + case MT_PHY_TYPE_OFDM: + txrate->idx = idx + 4; + return; + case MT_PHY_TYPE_CCK: + if (idx >= 8) + idx -= 8; + + txrate->idx = idx; + return; + case MT_PHY_TYPE_HT_GF: + txrate->flags |= IEEE80211_TX_RC_GREEN_FIELD; + fallthrough; + case MT_PHY_TYPE_HT: + txrate->flags |= IEEE80211_TX_RC_MCS; + txrate->idx = idx; + break; + default: + WARN_ON(1); + return; + } + + if (FIELD_GET(MT_TXWI_RATE_BW, rate) == MT_PHY_BW_40) + txrate->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; + + if (rate & MT_TXWI_RATE_SGI) + txrate->flags |= IEEE80211_TX_RC_SHORT_GI; +} + +static void +mt76_mac_fill_tx_status(struct mt7601u_dev *dev, struct ieee80211_tx_info *info, + struct mt76_tx_status *st) +{ + struct ieee80211_tx_rate *rate = info->status.rates; + int cur_idx, last_rate; + int i; + + last_rate = min_t(int, st->retry, IEEE80211_TX_MAX_RATES - 1); + mt76_mac_process_tx_rate(&rate[last_rate], st->rate); + if (last_rate < IEEE80211_TX_MAX_RATES - 1) + rate[last_rate + 1].idx = -1; + + cur_idx = rate[last_rate].idx + st->retry; + for (i = 0; i <= last_rate; i++) { + rate[i].flags = rate[last_rate].flags; + rate[i].idx = max_t(int, 0, cur_idx - i); + rate[i].count = 1; + } + + if (last_rate > 0) + rate[last_rate - 1].count = st->retry + 1 - last_rate; + + info->status.ampdu_len = 1; + info->status.ampdu_ack_len = st->success; + + if (st->is_probe) + info->flags |= IEEE80211_TX_CTL_RATE_CTRL_PROBE; + + if (st->aggr) + info->flags |= IEEE80211_TX_CTL_AMPDU | + IEEE80211_TX_STAT_AMPDU; + + if (!st->ack_req) + info->flags |= IEEE80211_TX_CTL_NO_ACK; + else if (st->success) + info->flags |= IEEE80211_TX_STAT_ACK; +} + +u16 mt76_mac_tx_rate_val(struct mt7601u_dev *dev, + const struct ieee80211_tx_rate *rate, u8 *nss_val) +{ + u16 rateval; + u8 phy, rate_idx; + u8 nss = 1; + u8 bw = 0; + + if (rate->flags & IEEE80211_TX_RC_MCS) { + rate_idx = rate->idx; + nss = 1 + (rate->idx >> 3); + phy = MT_PHY_TYPE_HT; + if (rate->flags & IEEE80211_TX_RC_GREEN_FIELD) + phy = MT_PHY_TYPE_HT_GF; + if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) + bw = 1; + } else { + const struct ieee80211_rate *r; + int band = dev->chandef.chan->band; + u16 val; + + r = &dev->hw->wiphy->bands[band]->bitrates[rate->idx]; + if (rate->flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) + val = r->hw_value_short; + else + val = r->hw_value; + + phy = val >> 8; + rate_idx = val & 0xff; + bw = 0; + } + + rateval = FIELD_PREP(MT_RXWI_RATE_MCS, rate_idx); + rateval |= FIELD_PREP(MT_RXWI_RATE_PHY, phy); + rateval |= FIELD_PREP(MT_RXWI_RATE_BW, bw); + if (rate->flags & IEEE80211_TX_RC_SHORT_GI) + rateval |= MT_RXWI_RATE_SGI; + + *nss_val = nss; + return rateval; +} + +void mt76_mac_wcid_set_rate(struct mt7601u_dev *dev, struct mt76_wcid *wcid, + const struct ieee80211_tx_rate *rate) +{ + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + wcid->tx_rate = mt76_mac_tx_rate_val(dev, rate, &wcid->tx_rate_nss); + wcid->tx_rate_set = true; + spin_unlock_irqrestore(&dev->lock, flags); +} + +struct mt76_tx_status mt7601u_mac_fetch_tx_status(struct mt7601u_dev *dev) +{ + struct mt76_tx_status stat = {}; + u32 val; + + val = mt7601u_rr(dev, MT_TX_STAT_FIFO); + stat.valid = !!(val & MT_TX_STAT_FIFO_VALID); + stat.success = !!(val & MT_TX_STAT_FIFO_SUCCESS); + stat.aggr = !!(val & MT_TX_STAT_FIFO_AGGR); + stat.ack_req = !!(val & MT_TX_STAT_FIFO_ACKREQ); + stat.pktid = FIELD_GET(MT_TX_STAT_FIFO_PID_TYPE, val); + stat.wcid = FIELD_GET(MT_TX_STAT_FIFO_WCID, val); + stat.rate = FIELD_GET(MT_TX_STAT_FIFO_RATE, val); + + return stat; +} + +void mt76_send_tx_status(struct mt7601u_dev *dev, struct mt76_tx_status *stat) +{ + struct ieee80211_tx_info info = {}; + struct ieee80211_sta *sta = NULL; + struct mt76_wcid *wcid = NULL; + void *msta; + + rcu_read_lock(); + if (stat->wcid < ARRAY_SIZE(dev->wcid)) + wcid = rcu_dereference(dev->wcid[stat->wcid]); + + if (wcid) { + msta = container_of(wcid, struct mt76_sta, wcid); + sta = container_of(msta, struct ieee80211_sta, + drv_priv); + } + + mt76_mac_fill_tx_status(dev, &info, stat); + + spin_lock_bh(&dev->mac_lock); + ieee80211_tx_status_noskb(dev->hw, sta, &info); + spin_unlock_bh(&dev->mac_lock); + + rcu_read_unlock(); +} + +void mt7601u_mac_set_protection(struct mt7601u_dev *dev, bool legacy_prot, + int ht_mode) +{ + int mode = ht_mode & IEEE80211_HT_OP_MODE_PROTECTION; + bool non_gf = !!(ht_mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT); + u32 prot[6]; + bool ht_rts[4] = {}; + int i; + + prot[0] = MT_PROT_NAV_SHORT | + MT_PROT_TXOP_ALLOW_ALL | + MT_PROT_RTS_THR_EN; + prot[1] = prot[0]; + if (legacy_prot) + prot[1] |= MT_PROT_CTRL_CTS2SELF; + + prot[2] = prot[4] = MT_PROT_NAV_SHORT | MT_PROT_TXOP_ALLOW_BW20; + prot[3] = prot[5] = MT_PROT_NAV_SHORT | MT_PROT_TXOP_ALLOW_ALL; + + if (legacy_prot) { + prot[2] |= MT_PROT_RATE_CCK_11; + prot[3] |= MT_PROT_RATE_CCK_11; + prot[4] |= MT_PROT_RATE_CCK_11; + prot[5] |= MT_PROT_RATE_CCK_11; + } else { + prot[2] |= MT_PROT_RATE_OFDM_24; + prot[3] |= MT_PROT_RATE_DUP_OFDM_24; + prot[4] |= MT_PROT_RATE_OFDM_24; + prot[5] |= MT_PROT_RATE_DUP_OFDM_24; + } + + switch (mode) { + case IEEE80211_HT_OP_MODE_PROTECTION_NONE: + break; + + case IEEE80211_HT_OP_MODE_PROTECTION_NONMEMBER: + ht_rts[0] = ht_rts[1] = ht_rts[2] = ht_rts[3] = true; + break; + + case IEEE80211_HT_OP_MODE_PROTECTION_20MHZ: + ht_rts[1] = ht_rts[3] = true; + break; + + case IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED: + ht_rts[0] = ht_rts[1] = ht_rts[2] = ht_rts[3] = true; + break; + } + + if (non_gf) + ht_rts[2] = ht_rts[3] = true; + + for (i = 0; i < 4; i++) + if (ht_rts[i]) + prot[i + 2] |= MT_PROT_CTRL_RTS_CTS; + + for (i = 0; i < 6; i++) + mt7601u_wr(dev, MT_CCK_PROT_CFG + i * 4, prot[i]); +} + +void mt7601u_mac_set_short_preamble(struct mt7601u_dev *dev, bool short_preamb) +{ + if (short_preamb) + mt76_set(dev, MT_AUTO_RSP_CFG, MT_AUTO_RSP_PREAMB_SHORT); + else + mt76_clear(dev, MT_AUTO_RSP_CFG, MT_AUTO_RSP_PREAMB_SHORT); +} + +void mt7601u_mac_config_tsf(struct mt7601u_dev *dev, bool enable, int interval) +{ + u32 val = mt7601u_rr(dev, MT_BEACON_TIME_CFG); + + val &= ~(MT_BEACON_TIME_CFG_TIMER_EN | + MT_BEACON_TIME_CFG_SYNC_MODE | + MT_BEACON_TIME_CFG_TBTT_EN); + + if (!enable) { + mt7601u_wr(dev, MT_BEACON_TIME_CFG, val); + return; + } + + val &= ~MT_BEACON_TIME_CFG_INTVAL; + val |= FIELD_PREP(MT_BEACON_TIME_CFG_INTVAL, interval << 4) | + MT_BEACON_TIME_CFG_TIMER_EN | + MT_BEACON_TIME_CFG_SYNC_MODE | + MT_BEACON_TIME_CFG_TBTT_EN; +} + +static void mt7601u_check_mac_err(struct mt7601u_dev *dev) +{ + u32 val = mt7601u_rr(dev, 0x10f4); + + if (!(val & BIT(29)) || !(val & (BIT(7) | BIT(5)))) + return; + + dev_err(dev->dev, "Error: MAC specific condition occurred\n"); + + mt76_set(dev, MT_MAC_SYS_CTRL, MT_MAC_SYS_CTRL_RESET_CSR); + udelay(10); + mt76_clear(dev, MT_MAC_SYS_CTRL, MT_MAC_SYS_CTRL_RESET_CSR); +} + +void mt7601u_mac_work(struct work_struct *work) +{ + struct mt7601u_dev *dev = container_of(work, struct mt7601u_dev, + mac_work.work); + struct { + u32 addr_base; + u32 span; + u64 *stat_base; + } spans[] = { + { MT_RX_STA_CNT0, 3, dev->stats.rx_stat }, + { MT_TX_STA_CNT0, 3, dev->stats.tx_stat }, + { MT_TX_AGG_STAT, 1, dev->stats.aggr_stat }, + { MT_MPDU_DENSITY_CNT, 1, dev->stats.zero_len_del }, + { MT_TX_AGG_CNT_BASE0, 8, &dev->stats.aggr_n[0] }, + { MT_TX_AGG_CNT_BASE1, 8, &dev->stats.aggr_n[16] }, + }; + u32 sum, n; + int i, j, k; + + /* Note: using MCU_RANDOM_READ is actually slower then reading all the + * registers by hand. MCU takes ca. 20ms to complete read of 24 + * registers while reading them one by one will takes roughly + * 24*200us =~ 5ms. + */ + + k = 0; + n = 0; + sum = 0; + for (i = 0; i < ARRAY_SIZE(spans); i++) + for (j = 0; j < spans[i].span; j++) { + u32 val = mt7601u_rr(dev, spans[i].addr_base + j * 4); + + spans[i].stat_base[j * 2] += val & 0xffff; + spans[i].stat_base[j * 2 + 1] += val >> 16; + + /* Calculate average AMPDU length */ + if (spans[i].addr_base != MT_TX_AGG_CNT_BASE0 && + spans[i].addr_base != MT_TX_AGG_CNT_BASE1) + continue; + + n += (val >> 16) + (val & 0xffff); + sum += (val & 0xffff) * (1 + k * 2) + + (val >> 16) * (2 + k * 2); + k++; + } + + atomic_set(&dev->avg_ampdu_len, n ? DIV_ROUND_CLOSEST(sum, n) : 1); + + mt7601u_check_mac_err(dev); + + ieee80211_queue_delayed_work(dev->hw, &dev->mac_work, 10 * HZ); +} + +void +mt7601u_mac_wcid_setup(struct mt7601u_dev *dev, u8 idx, u8 vif_idx, u8 *mac) +{ + u8 zmac[ETH_ALEN] = {}; + u32 attr; + + attr = FIELD_PREP(MT_WCID_ATTR_BSS_IDX, vif_idx & 7) | + FIELD_PREP(MT_WCID_ATTR_BSS_IDX_EXT, !!(vif_idx & 8)); + + mt76_wr(dev, MT_WCID_ATTR(idx), attr); + + if (mac) + memcpy(zmac, mac, sizeof(zmac)); + + mt7601u_addr_wr(dev, MT_WCID_ADDR(idx), zmac); +} + +void mt7601u_mac_set_ampdu_factor(struct mt7601u_dev *dev) +{ + struct ieee80211_sta *sta; + struct mt76_wcid *wcid; + void *msta; + u8 min_factor = 3; + int i; + + rcu_read_lock(); + for (i = 0; i < ARRAY_SIZE(dev->wcid); i++) { + wcid = rcu_dereference(dev->wcid[i]); + if (!wcid) + continue; + + msta = container_of(wcid, struct mt76_sta, wcid); + sta = container_of(msta, struct ieee80211_sta, drv_priv); + + min_factor = min(min_factor, sta->deflink.ht_cap.ampdu_factor); + } + rcu_read_unlock(); + + mt7601u_wr(dev, MT_MAX_LEN_CFG, 0xa0fff | + FIELD_PREP(MT_MAX_LEN_CFG_AMPDU, min_factor)); +} + +static void +mt76_mac_process_rate(struct ieee80211_rx_status *status, u16 rate) +{ + u8 idx = FIELD_GET(MT_RXWI_RATE_MCS, rate); + + switch (FIELD_GET(MT_RXWI_RATE_PHY, rate)) { + case MT_PHY_TYPE_OFDM: + if (WARN_ON(idx >= 8)) + idx = 0; + idx += 4; + + status->rate_idx = idx; + return; + case MT_PHY_TYPE_CCK: + if (idx >= 8) { + idx -= 8; + status->enc_flags |= RX_ENC_FLAG_SHORTPRE; + } + + if (WARN_ON(idx >= 4)) + idx = 0; + + status->rate_idx = idx; + return; + case MT_PHY_TYPE_HT_GF: + status->enc_flags |= RX_ENC_FLAG_HT_GF; + fallthrough; + case MT_PHY_TYPE_HT: + status->encoding = RX_ENC_HT; + status->rate_idx = idx; + break; + default: + WARN_ON(1); + return; + } + + if (rate & MT_RXWI_RATE_SGI) + status->enc_flags |= RX_ENC_FLAG_SHORT_GI; + + if (rate & MT_RXWI_RATE_STBC) + status->enc_flags |= 1 << RX_ENC_FLAG_STBC_SHIFT; + + if (rate & MT_RXWI_RATE_BW) + status->bw = RATE_INFO_BW_40; +} + +static void +mt7601u_rx_monitor_beacon(struct mt7601u_dev *dev, struct mt7601u_rxwi *rxwi, + u16 rate, int rssi) +{ + dev->bcn_freq_off = rxwi->freq_off; + dev->bcn_phy_mode = FIELD_GET(MT_RXWI_RATE_PHY, rate); + ewma_rssi_add(&dev->avg_rssi, -rssi); +} + +static int +mt7601u_rx_is_our_beacon(struct mt7601u_dev *dev, u8 *data) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)data; + + return ieee80211_is_beacon(hdr->frame_control) && + ether_addr_equal(hdr->addr2, dev->ap_bssid); +} + +u32 mt76_mac_process_rx(struct mt7601u_dev *dev, struct sk_buff *skb, + u8 *data, void *rxi) +{ + struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); + struct mt7601u_rxwi *rxwi = rxi; + u32 len, ctl = le32_to_cpu(rxwi->ctl); + u16 rate = le16_to_cpu(rxwi->rate); + int rssi; + + len = FIELD_GET(MT_RXWI_CTL_MPDU_LEN, ctl); + if (len < 10) + return 0; + + if (rxwi->rxinfo & cpu_to_le32(MT_RXINFO_DECRYPT)) { + status->flag |= RX_FLAG_DECRYPTED; + status->flag |= RX_FLAG_MMIC_STRIPPED; + status->flag |= RX_FLAG_MIC_STRIPPED; + status->flag |= RX_FLAG_ICV_STRIPPED; + status->flag |= RX_FLAG_IV_STRIPPED; + } + /* let mac80211 take care of PN validation since apparently + * the hardware does not support it + */ + if (rxwi->rxinfo & cpu_to_le32(MT_RXINFO_PN_LEN)) + status->flag &= ~RX_FLAG_IV_STRIPPED; + + status->chains = BIT(0); + rssi = mt7601u_phy_get_rssi(dev, rxwi, rate); + status->chain_signal[0] = status->signal = rssi; + status->freq = dev->chandef.chan->center_freq; + status->band = dev->chandef.chan->band; + + mt76_mac_process_rate(status, rate); + + spin_lock_bh(&dev->con_mon_lock); + if (mt7601u_rx_is_our_beacon(dev, data)) + mt7601u_rx_monitor_beacon(dev, rxwi, rate, rssi); + else if (rxwi->rxinfo & cpu_to_le32(MT_RXINFO_U2M)) + ewma_rssi_add(&dev->avg_rssi, -rssi); + spin_unlock_bh(&dev->con_mon_lock); + + return len; +} + +static enum mt76_cipher_type +mt76_mac_get_key_info(struct ieee80211_key_conf *key, u8 *key_data) +{ + memset(key_data, 0, 32); + if (!key) + return MT_CIPHER_NONE; + + if (key->keylen > 32) + return MT_CIPHER_NONE; + + memcpy(key_data, key->key, key->keylen); + + switch (key->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + return MT_CIPHER_WEP40; + case WLAN_CIPHER_SUITE_WEP104: + return MT_CIPHER_WEP104; + case WLAN_CIPHER_SUITE_TKIP: + return MT_CIPHER_TKIP; + case WLAN_CIPHER_SUITE_CCMP: + return MT_CIPHER_AES_CCMP; + default: + return MT_CIPHER_NONE; + } +} + +int mt76_mac_wcid_set_key(struct mt7601u_dev *dev, u8 idx, + struct ieee80211_key_conf *key) +{ + enum mt76_cipher_type cipher; + u8 key_data[32]; + u8 iv_data[8]; + u32 val; + + cipher = mt76_mac_get_key_info(key, key_data); + if (cipher == MT_CIPHER_NONE && key) + return -EINVAL; + + trace_set_key(dev, idx); + + mt7601u_wr_copy(dev, MT_WCID_KEY(idx), key_data, sizeof(key_data)); + + memset(iv_data, 0, sizeof(iv_data)); + if (key) { + iv_data[3] = key->keyidx << 6; + if (cipher >= MT_CIPHER_TKIP) { + /* Note: start with 1 to comply with spec, + * (see comment on common/cmm_wpa.c:4291). + */ + iv_data[0] |= 1; + iv_data[3] |= 0x20; + } + } + mt7601u_wr_copy(dev, MT_WCID_IV(idx), iv_data, sizeof(iv_data)); + + val = mt7601u_rr(dev, MT_WCID_ATTR(idx)); + val &= ~MT_WCID_ATTR_PKEY_MODE & ~MT_WCID_ATTR_PKEY_MODE_EXT; + val |= FIELD_PREP(MT_WCID_ATTR_PKEY_MODE, cipher & 7) | + FIELD_PREP(MT_WCID_ATTR_PKEY_MODE_EXT, cipher >> 3); + val &= ~MT_WCID_ATTR_PAIRWISE; + val |= MT_WCID_ATTR_PAIRWISE * + !!(key && key->flags & IEEE80211_KEY_FLAG_PAIRWISE); + mt7601u_wr(dev, MT_WCID_ATTR(idx), val); + + return 0; +} + +int mt76_mac_shared_key_setup(struct mt7601u_dev *dev, u8 vif_idx, u8 key_idx, + struct ieee80211_key_conf *key) +{ + enum mt76_cipher_type cipher; + u8 key_data[32]; + u32 val; + + cipher = mt76_mac_get_key_info(key, key_data); + if (cipher == MT_CIPHER_NONE && key) + return -EINVAL; + + trace_set_shared_key(dev, vif_idx, key_idx); + + mt7601u_wr_copy(dev, MT_SKEY(vif_idx, key_idx), + key_data, sizeof(key_data)); + + val = mt76_rr(dev, MT_SKEY_MODE(vif_idx)); + val &= ~(MT_SKEY_MODE_MASK << MT_SKEY_MODE_SHIFT(vif_idx, key_idx)); + val |= cipher << MT_SKEY_MODE_SHIFT(vif_idx, key_idx); + mt76_wr(dev, MT_SKEY_MODE(vif_idx), val); + + return 0; +} diff --git a/drivers/net/wireless/mediatek/mt7601u/mac.h b/drivers/net/wireless/mediatek/mt7601u/mac.h new file mode 100644 index 000000000..54bd4fac5 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt7601u/mac.h @@ -0,0 +1,171 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2014 Felix Fietkau + * Copyright (C) 2015 Jakub Kicinski + */ + +#ifndef __MT76_MAC_H +#define __MT76_MAC_H + +struct mt76_tx_status { + u8 valid:1; + u8 success:1; + u8 aggr:1; + u8 ack_req:1; + u8 is_probe:1; + u8 wcid; + u8 pktid; + u8 retry; + u16 rate; +} __packed __aligned(2); + +/* Note: values in original "RSSI" and "SNR" fields are not actually what they + * are called for MT7601U, names used by this driver are educated guesses + * (see vendor mac/ral_omac.c). + */ +struct mt7601u_rxwi { + __le32 rxinfo; + + __le32 ctl; + + __le16 frag_sn; + __le16 rate; + + u8 unknown; + u8 zero[3]; + + u8 snr; + u8 ant; + u8 gain; + u8 freq_off; + + __le32 resv2; + __le32 expert_ant; +} __packed __aligned(4); + +#define MT_RXINFO_BA BIT(0) +#define MT_RXINFO_DATA BIT(1) +#define MT_RXINFO_NULL BIT(2) +#define MT_RXINFO_FRAG BIT(3) +#define MT_RXINFO_U2M BIT(4) +#define MT_RXINFO_MULTICAST BIT(5) +#define MT_RXINFO_BROADCAST BIT(6) +#define MT_RXINFO_MYBSS BIT(7) +#define MT_RXINFO_CRCERR BIT(8) +#define MT_RXINFO_ICVERR BIT(9) +#define MT_RXINFO_MICERR BIT(10) +#define MT_RXINFO_AMSDU BIT(11) +#define MT_RXINFO_HTC BIT(12) +#define MT_RXINFO_RSSI BIT(13) +#define MT_RXINFO_L2PAD BIT(14) +#define MT_RXINFO_AMPDU BIT(15) +#define MT_RXINFO_DECRYPT BIT(16) +#define MT_RXINFO_BSSIDX3 BIT(17) +#define MT_RXINFO_WAPI_KEY BIT(18) +#define MT_RXINFO_PN_LEN GENMASK(21, 19) +#define MT_RXINFO_SW_PKT_80211 BIT(22) +#define MT_RXINFO_TCP_SUM_BYPASS BIT(28) +#define MT_RXINFO_IP_SUM_BYPASS BIT(29) +#define MT_RXINFO_TCP_SUM_ERR BIT(30) +#define MT_RXINFO_IP_SUM_ERR BIT(31) + +#define MT_RXWI_CTL_WCID GENMASK(7, 0) +#define MT_RXWI_CTL_KEY_IDX GENMASK(9, 8) +#define MT_RXWI_CTL_BSS_IDX GENMASK(12, 10) +#define MT_RXWI_CTL_UDF GENMASK(15, 13) +#define MT_RXWI_CTL_MPDU_LEN GENMASK(27, 16) +#define MT_RXWI_CTL_TID GENMASK(31, 28) + +#define MT_RXWI_FRAG GENMASK(3, 0) +#define MT_RXWI_SN GENMASK(15, 4) + +#define MT_RXWI_RATE_MCS GENMASK(6, 0) +#define MT_RXWI_RATE_BW BIT(7) +#define MT_RXWI_RATE_SGI BIT(8) +#define MT_RXWI_RATE_STBC GENMASK(10, 9) +#define MT_RXWI_RATE_ETXBF BIT(11) +#define MT_RXWI_RATE_SND BIT(12) +#define MT_RXWI_RATE_ITXBF BIT(13) +#define MT_RXWI_RATE_PHY GENMASK(15, 14) + +#define MT_RXWI_GAIN_RSSI_VAL GENMASK(5, 0) +#define MT_RXWI_GAIN_RSSI_LNA_ID GENMASK(7, 6) +#define MT_RXWI_ANT_AUX_LNA BIT(7) + +#define MT_RXWI_EANT_ENC_ANT_ID GENMASK(7, 0) + +enum mt76_phy_type { + MT_PHY_TYPE_CCK, + MT_PHY_TYPE_OFDM, + MT_PHY_TYPE_HT, + MT_PHY_TYPE_HT_GF, +}; + +enum mt76_phy_bandwidth { + MT_PHY_BW_20, + MT_PHY_BW_40, +}; + +struct mt76_txwi { + __le16 flags; + __le16 rate_ctl; + + u8 ack_ctl; + u8 wcid; + __le16 len_ctl; + + __le32 iv; + + __le32 eiv; + + u8 aid; + u8 txstream; + __le16 ctl; +} __packed __aligned(4); + +#define MT_TXWI_FLAGS_FRAG BIT(0) +#define MT_TXWI_FLAGS_MMPS BIT(1) +#define MT_TXWI_FLAGS_CFACK BIT(2) +#define MT_TXWI_FLAGS_TS BIT(3) +#define MT_TXWI_FLAGS_AMPDU BIT(4) +#define MT_TXWI_FLAGS_MPDU_DENSITY GENMASK(7, 5) +#define MT_TXWI_FLAGS_TXOP GENMASK(9, 8) +#define MT_TXWI_FLAGS_CWMIN GENMASK(12, 10) +#define MT_TXWI_FLAGS_NO_RATE_FALLBACK BIT(13) +#define MT_TXWI_FLAGS_TX_RPT BIT(14) +#define MT_TXWI_FLAGS_TX_RATE_LUT BIT(15) + +#define MT_TXWI_RATE_MCS GENMASK(6, 0) +#define MT_TXWI_RATE_BW BIT(7) +#define MT_TXWI_RATE_SGI BIT(8) +#define MT_TXWI_RATE_STBC GENMASK(10, 9) +#define MT_TXWI_RATE_PHY_MODE GENMASK(15, 14) + +#define MT_TXWI_ACK_CTL_REQ BIT(0) +#define MT_TXWI_ACK_CTL_NSEQ BIT(1) +#define MT_TXWI_ACK_CTL_BA_WINDOW GENMASK(7, 2) + +#define MT_TXWI_LEN_BYTE_CNT GENMASK(11, 0) +#define MT_TXWI_LEN_PKTID GENMASK(15, 12) + +#define MT_TXWI_CTL_TX_POWER_ADJ GENMASK(3, 0) +#define MT_TXWI_CTL_CHAN_CHECK_PKT BIT(4) +#define MT_TXWI_CTL_PIFS_REV BIT(6) + +u32 mt76_mac_process_rx(struct mt7601u_dev *dev, struct sk_buff *skb, + u8 *data, void *rxi); +int mt76_mac_wcid_set_key(struct mt7601u_dev *dev, u8 idx, + struct ieee80211_key_conf *key); +void mt76_mac_wcid_set_rate(struct mt7601u_dev *dev, struct mt76_wcid *wcid, + const struct ieee80211_tx_rate *rate); + +int mt76_mac_shared_key_setup(struct mt7601u_dev *dev, u8 vif_idx, u8 key_idx, + struct ieee80211_key_conf *key); +u16 mt76_mac_tx_rate_val(struct mt7601u_dev *dev, + const struct ieee80211_tx_rate *rate, u8 *nss_val); +struct mt76_tx_status +mt7601u_mac_fetch_tx_status(struct mt7601u_dev *dev); +void mt76_send_tx_status(struct mt7601u_dev *dev, struct mt76_tx_status *stat); +void mt7601u_set_macaddr(struct mt7601u_dev *dev, const u8 *addr); + +#endif diff --git a/drivers/net/wireless/mediatek/mt7601u/main.c b/drivers/net/wireless/mediatek/mt7601u/main.c new file mode 100644 index 000000000..05ba43e19 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt7601u/main.c @@ -0,0 +1,432 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2014 Felix Fietkau + * Copyright (C) 2015 Jakub Kicinski + */ + +#include "mt7601u.h" +#include "mac.h" +#include + +static int mt7601u_start(struct ieee80211_hw *hw) +{ + struct mt7601u_dev *dev = hw->priv; + int ret; + + mutex_lock(&dev->mutex); + + ret = mt7601u_mac_start(dev); + if (ret) + goto out; + + ieee80211_queue_delayed_work(dev->hw, &dev->mac_work, + MT_CALIBRATE_INTERVAL); + ieee80211_queue_delayed_work(dev->hw, &dev->cal_work, + MT_CALIBRATE_INTERVAL); +out: + mutex_unlock(&dev->mutex); + return ret; +} + +static void mt7601u_stop(struct ieee80211_hw *hw, bool suspend) +{ + struct mt7601u_dev *dev = hw->priv; + + mutex_lock(&dev->mutex); + + cancel_delayed_work_sync(&dev->cal_work); + cancel_delayed_work_sync(&dev->mac_work); + mt7601u_mac_stop(dev); + + mutex_unlock(&dev->mutex); +} + +static int mt7601u_add_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct mt7601u_dev *dev = hw->priv; + struct mt76_vif *mvif = (struct mt76_vif *) vif->drv_priv; + unsigned int idx = 0; + unsigned int wcid = GROUP_WCID(idx); + + /* Note: for AP do the AP-STA things mt76 does: + * - beacon offsets + * - do mac address tricks + * - shift vif idx + */ + mvif->idx = idx; + + if (!ether_addr_equal(dev->macaddr, vif->addr)) + mt7601u_set_macaddr(dev, vif->addr); + + if (dev->wcid_mask[wcid / BITS_PER_LONG] & BIT(wcid % BITS_PER_LONG)) + return -ENOSPC; + dev->wcid_mask[wcid / BITS_PER_LONG] |= BIT(wcid % BITS_PER_LONG); + mvif->group_wcid.idx = wcid; + mvif->group_wcid.hw_key_idx = -1; + + return 0; +} + +static void mt7601u_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct mt7601u_dev *dev = hw->priv; + struct mt76_vif *mvif = (struct mt76_vif *) vif->drv_priv; + unsigned int wcid = mvif->group_wcid.idx; + + dev->wcid_mask[wcid / BITS_PER_LONG] &= ~BIT(wcid % BITS_PER_LONG); +} + +static int mt7601u_config(struct ieee80211_hw *hw, int radio_idx, u32 changed) +{ + struct mt7601u_dev *dev = hw->priv; + int ret = 0; + + mutex_lock(&dev->mutex); + + if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { + ieee80211_stop_queues(hw); + ret = mt7601u_phy_set_channel(dev, &hw->conf.chandef); + ieee80211_wake_queues(hw); + } + + mutex_unlock(&dev->mutex); + + return ret; +} + +static void +mt76_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, + unsigned int *total_flags, u64 multicast) +{ + struct mt7601u_dev *dev = hw->priv; + u32 flags = 0; + +#define MT76_FILTER(_flag, _hw) do { \ + flags |= *total_flags & FIF_##_flag; \ + dev->rxfilter &= ~(_hw); \ + dev->rxfilter |= !(flags & FIF_##_flag) * (_hw); \ + } while (0) + + mutex_lock(&dev->mutex); + + dev->rxfilter &= ~MT_RX_FILTR_CFG_OTHER_BSS; + + MT76_FILTER(OTHER_BSS, MT_RX_FILTR_CFG_PROMISC); + MT76_FILTER(FCSFAIL, MT_RX_FILTR_CFG_CRC_ERR); + MT76_FILTER(PLCPFAIL, MT_RX_FILTR_CFG_PHY_ERR); + MT76_FILTER(CONTROL, MT_RX_FILTR_CFG_ACK | + MT_RX_FILTR_CFG_CTS | + MT_RX_FILTR_CFG_CFEND | + MT_RX_FILTR_CFG_CFACK | + MT_RX_FILTR_CFG_BA | + MT_RX_FILTR_CFG_CTRL_RSV); + MT76_FILTER(PSPOLL, MT_RX_FILTR_CFG_PSPOLL); + + *total_flags = flags; + mt76_wr(dev, MT_RX_FILTR_CFG, dev->rxfilter); + + mutex_unlock(&dev->mutex); +} + +static void +mt7601u_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, u64 changed) +{ + struct mt7601u_dev *dev = hw->priv; + + mutex_lock(&dev->mutex); + + if (changed & BSS_CHANGED_ASSOC) + mt7601u_phy_con_cal_onoff(dev, info); + + if (changed & BSS_CHANGED_BSSID) { + mt7601u_addr_wr(dev, MT_MAC_BSSID_DW0, info->bssid); + + /* Note: this is a hack because beacon_int is not changed + * on leave nor is any more appropriate event generated. + * rt2x00 doesn't seem to be bothered though. + */ + if (is_zero_ether_addr(info->bssid)) + mt7601u_mac_config_tsf(dev, false, 0); + } + + if (changed & BSS_CHANGED_BASIC_RATES) { + mt7601u_wr(dev, MT_LEGACY_BASIC_RATE, info->basic_rates); + mt7601u_wr(dev, MT_HT_FBK_CFG0, 0x65432100); + mt7601u_wr(dev, MT_HT_FBK_CFG1, 0xedcba980); + mt7601u_wr(dev, MT_LG_FBK_CFG0, 0xedcba988); + mt7601u_wr(dev, MT_LG_FBK_CFG1, 0x00002100); + } + + if (changed & BSS_CHANGED_BEACON_INT) + mt7601u_mac_config_tsf(dev, true, info->beacon_int); + + if (changed & BSS_CHANGED_HT || changed & BSS_CHANGED_ERP_CTS_PROT) + mt7601u_mac_set_protection(dev, info->use_cts_prot, + info->ht_operation_mode); + + if (changed & BSS_CHANGED_ERP_PREAMBLE) + mt7601u_mac_set_short_preamble(dev, info->use_short_preamble); + + if (changed & BSS_CHANGED_ERP_SLOT) { + int slottime = info->use_short_slot ? 9 : 20; + + mt76_rmw_field(dev, MT_BKOFF_SLOT_CFG, + MT_BKOFF_SLOT_CFG_SLOTTIME, slottime); + } + + if (changed & BSS_CHANGED_ASSOC) + mt7601u_phy_recalibrate_after_assoc(dev); + + mutex_unlock(&dev->mutex); +} + +static int +mt76_wcid_alloc(struct mt7601u_dev *dev) +{ + int i, idx = 0; + + for (i = 0; i < ARRAY_SIZE(dev->wcid_mask); i++) { + idx = ffs(~dev->wcid_mask[i]); + if (!idx) + continue; + + idx--; + dev->wcid_mask[i] |= BIT(idx); + break; + } + + idx = i * BITS_PER_LONG + idx; + if (idx > 119) + return -1; + + return idx; +} + +static int +mt7601u_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mt7601u_dev *dev = hw->priv; + struct mt76_sta *msta = (struct mt76_sta *) sta->drv_priv; + struct mt76_vif *mvif = (struct mt76_vif *) vif->drv_priv; + int ret = 0; + int idx = 0; + + mutex_lock(&dev->mutex); + + idx = mt76_wcid_alloc(dev); + if (idx < 0) { + ret = -ENOSPC; + goto out; + } + + msta->wcid.idx = idx; + msta->wcid.hw_key_idx = -1; + mt7601u_mac_wcid_setup(dev, idx, mvif->idx, sta->addr); + mt76_clear(dev, MT_WCID_DROP(idx), MT_WCID_DROP_MASK(idx)); + rcu_assign_pointer(dev->wcid[idx], &msta->wcid); + mt7601u_mac_set_ampdu_factor(dev); + +out: + mutex_unlock(&dev->mutex); + + return ret; +} + +static int +mt7601u_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mt7601u_dev *dev = hw->priv; + struct mt76_sta *msta = (struct mt76_sta *) sta->drv_priv; + int idx = msta->wcid.idx; + + mutex_lock(&dev->mutex); + rcu_assign_pointer(dev->wcid[idx], NULL); + mt76_set(dev, MT_WCID_DROP(idx), MT_WCID_DROP_MASK(idx)); + dev->wcid_mask[idx / BITS_PER_LONG] &= ~BIT(idx % BITS_PER_LONG); + mt7601u_mac_wcid_setup(dev, idx, 0, NULL); + mt7601u_mac_set_ampdu_factor(dev); + mutex_unlock(&dev->mutex); + + return 0; +} + +static void +mt7601u_sta_notify(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + enum sta_notify_cmd cmd, struct ieee80211_sta *sta) +{ +} + +static void +mt7601u_sw_scan(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + const u8 *mac_addr) +{ + struct mt7601u_dev *dev = hw->priv; + + mt7601u_agc_save(dev); + set_bit(MT7601U_STATE_SCANNING, &dev->state); +} + +static void +mt7601u_sw_scan_complete(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct mt7601u_dev *dev = hw->priv; + + mt7601u_agc_restore(dev); + clear_bit(MT7601U_STATE_SCANNING, &dev->state); + + ieee80211_queue_delayed_work(dev->hw, &dev->cal_work, + MT_CALIBRATE_INTERVAL); + if (dev->freq_cal.enabled) + ieee80211_queue_delayed_work(dev->hw, &dev->freq_cal.work, + MT_FREQ_CAL_INIT_DELAY); +} + +static int +mt7601u_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct mt7601u_dev *dev = hw->priv; + struct mt76_vif *mvif = (struct mt76_vif *) vif->drv_priv; + struct mt76_sta *msta = sta ? (struct mt76_sta *) sta->drv_priv : NULL; + struct mt76_wcid *wcid = msta ? &msta->wcid : &mvif->group_wcid; + int idx = key->keyidx; + int ret; + + /* fall back to sw encryption for unsupported ciphers */ + switch (key->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + case WLAN_CIPHER_SUITE_TKIP: + case WLAN_CIPHER_SUITE_CCMP: + break; + default: + return -EOPNOTSUPP; + } + + if (cmd == SET_KEY) { + key->hw_key_idx = wcid->idx; + wcid->hw_key_idx = idx; + } else { + if (idx == wcid->hw_key_idx) + wcid->hw_key_idx = -1; + + key = NULL; + } + + if (!msta) { + if (key || wcid->hw_key_idx == idx) { + ret = mt76_mac_wcid_set_key(dev, wcid->idx, key); + if (ret) + return ret; + } + + return mt76_mac_shared_key_setup(dev, mvif->idx, idx, key); + } + + return mt76_mac_wcid_set_key(dev, msta->wcid.idx, key); +} + +static int mt7601u_set_rts_threshold(struct ieee80211_hw *hw, int radio_idx, + u32 value) +{ + struct mt7601u_dev *dev = hw->priv; + + mt76_rmw_field(dev, MT_TX_RTS_CFG, MT_TX_RTS_CFG_THRESH, value); + + return 0; +} + +static int +mt76_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_ampdu_params *params) +{ + struct mt7601u_dev *dev = hw->priv; + struct ieee80211_sta *sta = params->sta; + enum ieee80211_ampdu_mlme_action action = params->action; + u16 tid = params->tid; + u16 ssn = params->ssn; + struct mt76_sta *msta = (struct mt76_sta *) sta->drv_priv; + + WARN_ON(msta->wcid.idx > GROUP_WCID(0)); + + switch (action) { + case IEEE80211_AMPDU_RX_START: + mt76_set(dev, MT_WCID_ADDR(msta->wcid.idx) + 4, BIT(16 + tid)); + break; + case IEEE80211_AMPDU_RX_STOP: + mt76_clear(dev, MT_WCID_ADDR(msta->wcid.idx) + 4, + BIT(16 + tid)); + break; + case IEEE80211_AMPDU_TX_OPERATIONAL: + ieee80211_send_bar(vif, sta->addr, tid, msta->agg_ssn[tid]); + break; + case IEEE80211_AMPDU_TX_STOP_FLUSH: + case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: + break; + case IEEE80211_AMPDU_TX_START: + msta->agg_ssn[tid] = ssn << 4; + return IEEE80211_AMPDU_TX_START_IMMEDIATE; + case IEEE80211_AMPDU_TX_STOP_CONT: + ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); + break; + } + + return 0; +} + +static void +mt76_sta_rate_tbl_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mt7601u_dev *dev = hw->priv; + struct mt76_sta *msta = (struct mt76_sta *) sta->drv_priv; + struct ieee80211_sta_rates *rates; + struct ieee80211_tx_rate rate = {}; + + rcu_read_lock(); + rates = rcu_dereference(sta->rates); + + if (!rates) + goto out; + + rate.idx = rates->rate[0].idx; + rate.flags = rates->rate[0].flags; + mt76_mac_wcid_set_rate(dev, &msta->wcid, &rate); + +out: + rcu_read_unlock(); +} + +const struct ieee80211_ops mt7601u_ops = { + .add_chanctx = ieee80211_emulate_add_chanctx, + .remove_chanctx = ieee80211_emulate_remove_chanctx, + .change_chanctx = ieee80211_emulate_change_chanctx, + .switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx, + .tx = mt7601u_tx, + .wake_tx_queue = ieee80211_handle_wake_tx_queue, + .start = mt7601u_start, + .stop = mt7601u_stop, + .add_interface = mt7601u_add_interface, + .remove_interface = mt7601u_remove_interface, + .config = mt7601u_config, + .configure_filter = mt76_configure_filter, + .bss_info_changed = mt7601u_bss_info_changed, + .sta_add = mt7601u_sta_add, + .sta_remove = mt7601u_sta_remove, + .sta_notify = mt7601u_sta_notify, + .set_key = mt7601u_set_key, + .conf_tx = mt7601u_conf_tx, + .sw_scan_start = mt7601u_sw_scan, + .sw_scan_complete = mt7601u_sw_scan_complete, + .ampdu_action = mt76_ampdu_action, + .sta_rate_tbl_update = mt76_sta_rate_tbl_update, + .set_rts_threshold = mt7601u_set_rts_threshold, +}; diff --git a/drivers/net/wireless/mediatek/mt7601u/mcu.c b/drivers/net/wireless/mediatek/mt7601u/mcu.c new file mode 100644 index 000000000..bad6ca821 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt7601u/mcu.c @@ -0,0 +1,548 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * (c) Copyright 2002-2010, Ralink Technology, Inc. + * Copyright (C) 2014 Felix Fietkau + * Copyright (C) 2015 Jakub Kicinski + */ + +#include +#include +#include +#include +#include + +#include "mt7601u.h" +#include "dma.h" +#include "mcu.h" +#include "usb.h" +#include "trace.h" + +#define MCU_FW_URB_MAX_PAYLOAD 0x3800 +#define MCU_FW_URB_SIZE (MCU_FW_URB_MAX_PAYLOAD + 12) +#define MCU_RESP_URB_SIZE 1024 + +static inline int firmware_running(struct mt7601u_dev *dev) +{ + return mt7601u_rr(dev, MT_MCU_COM_REG0) == 1; +} + +static inline void skb_put_le32(struct sk_buff *skb, u32 val) +{ + put_unaligned_le32(val, skb_put(skb, 4)); +} + +static inline void mt7601u_dma_skb_wrap_cmd(struct sk_buff *skb, + u8 seq, enum mcu_cmd cmd) +{ + WARN_ON(mt7601u_dma_skb_wrap(skb, CPU_TX_PORT, DMA_COMMAND, + FIELD_PREP(MT_TXD_CMD_INFO_SEQ, seq) | + FIELD_PREP(MT_TXD_CMD_INFO_TYPE, cmd))); +} + +static inline void trace_mt_mcu_msg_send_cs(struct mt7601u_dev *dev, + struct sk_buff *skb, bool need_resp) +{ + u32 i, csum = 0; + + for (i = 0; i < skb->len / 4; i++) + csum ^= get_unaligned_le32(skb->data + i * 4); + + trace_mt_mcu_msg_send(dev, skb, csum, need_resp); +} + +static struct sk_buff *mt7601u_mcu_msg_alloc(const void *data, int len) +{ + struct sk_buff *skb; + + WARN_ON(len % 4); /* if length is not divisible by 4 we need to pad */ + + skb = alloc_skb(len + MT_DMA_HDR_LEN + 4, GFP_KERNEL); + if (skb) { + skb_reserve(skb, MT_DMA_HDR_LEN); + skb_put_data(skb, data, len); + } + + return skb; +} + +static int mt7601u_mcu_wait_resp(struct mt7601u_dev *dev, u8 seq) +{ + struct urb *urb = dev->mcu.resp.urb; + u32 rxfce; + int urb_status, ret, i = 5; + + while (i--) { + if (!wait_for_completion_timeout(&dev->mcu.resp_cmpl, + msecs_to_jiffies(300))) { + dev_warn(dev->dev, "Warning: %s retrying\n", __func__); + continue; + } + + /* Make copies of important data before reusing the urb */ + rxfce = get_unaligned_le32(dev->mcu.resp.buf); + urb_status = urb->status * mt7601u_urb_has_error(urb); + + ret = mt7601u_usb_submit_buf(dev, USB_DIR_IN, MT_EP_IN_CMD_RESP, + &dev->mcu.resp, GFP_KERNEL, + mt7601u_complete_urb, + &dev->mcu.resp_cmpl); + if (ret) + return ret; + + if (urb_status) + dev_err(dev->dev, "Error: MCU resp urb failed:%d\n", + urb_status); + + if (FIELD_GET(MT_RXD_CMD_INFO_CMD_SEQ, rxfce) == seq && + FIELD_GET(MT_RXD_CMD_INFO_EVT_TYPE, rxfce) == CMD_DONE) + return 0; + + dev_err(dev->dev, "Error: MCU resp evt:%lx seq:%hhx-%lx!\n", + FIELD_GET(MT_RXD_CMD_INFO_EVT_TYPE, rxfce), + seq, FIELD_GET(MT_RXD_CMD_INFO_CMD_SEQ, rxfce)); + } + + dev_err(dev->dev, "Error: %s timed out\n", __func__); + return -ETIMEDOUT; +} + +static int +mt7601u_mcu_msg_send(struct mt7601u_dev *dev, struct sk_buff *skb, + enum mcu_cmd cmd, bool wait_resp) +{ + struct usb_device *usb_dev = mt7601u_to_usb_dev(dev); + unsigned cmd_pipe = usb_sndbulkpipe(usb_dev, + dev->out_eps[MT_EP_OUT_INBAND_CMD]); + int sent, ret; + u8 seq = 0; + + if (test_bit(MT7601U_STATE_REMOVED, &dev->state)) { + consume_skb(skb); + return 0; + } + + mutex_lock(&dev->mcu.mutex); + + if (wait_resp) + while (!seq) + seq = ++dev->mcu.msg_seq & 0xf; + + mt7601u_dma_skb_wrap_cmd(skb, seq, cmd); + + if (dev->mcu.resp_cmpl.done) + dev_err(dev->dev, "Error: MCU response pre-completed!\n"); + + trace_mt_mcu_msg_send_cs(dev, skb, wait_resp); + trace_mt_submit_urb_sync(dev, cmd_pipe, skb->len); + ret = usb_bulk_msg(usb_dev, cmd_pipe, skb->data, skb->len, &sent, 500); + if (ret) { + dev_err(dev->dev, "Error: send MCU cmd failed:%d\n", ret); + goto out; + } + if (sent != skb->len) + dev_err(dev->dev, "Error: %s sent != skb->len\n", __func__); + + if (wait_resp) + ret = mt7601u_mcu_wait_resp(dev, seq); +out: + mutex_unlock(&dev->mcu.mutex); + + consume_skb(skb); + + return ret; +} + +static int mt7601u_mcu_function_select(struct mt7601u_dev *dev, + enum mcu_function func, u32 val) +{ + struct sk_buff *skb; + struct { + __le32 id; + __le32 value; + } __packed __aligned(4) msg = { + .id = cpu_to_le32(func), + .value = cpu_to_le32(val), + }; + + skb = mt7601u_mcu_msg_alloc(&msg, sizeof(msg)); + if (!skb) + return -ENOMEM; + return mt7601u_mcu_msg_send(dev, skb, CMD_FUN_SET_OP, func == 5); +} + +int mt7601u_mcu_tssi_read_kick(struct mt7601u_dev *dev, int use_hvga) +{ + int ret; + + if (!test_bit(MT7601U_STATE_MCU_RUNNING, &dev->state)) + return 0; + + ret = mt7601u_mcu_function_select(dev, ATOMIC_TSSI_SETTING, + use_hvga); + if (ret) { + dev_warn(dev->dev, "Warning: MCU TSSI read kick failed\n"); + return ret; + } + + dev->tssi_read_trig = true; + + return 0; +} + +int +mt7601u_mcu_calibrate(struct mt7601u_dev *dev, enum mcu_calibrate cal, u32 val) +{ + struct sk_buff *skb; + struct { + __le32 id; + __le32 value; + } __packed __aligned(4) msg = { + .id = cpu_to_le32(cal), + .value = cpu_to_le32(val), + }; + + skb = mt7601u_mcu_msg_alloc(&msg, sizeof(msg)); + if (!skb) + return -ENOMEM; + return mt7601u_mcu_msg_send(dev, skb, CMD_CALIBRATION_OP, true); +} + +int mt7601u_write_reg_pairs(struct mt7601u_dev *dev, u32 base, + const struct mt76_reg_pair *data, int n) +{ + const int max_vals_per_cmd = INBAND_PACKET_MAX_LEN / 8; + struct sk_buff *skb; + int cnt, i, ret; + + if (!n) + return 0; + + cnt = min(max_vals_per_cmd, n); + + skb = alloc_skb(cnt * 8 + MT_DMA_HDR_LEN + 4, GFP_KERNEL); + if (!skb) + return -ENOMEM; + skb_reserve(skb, MT_DMA_HDR_LEN); + + for (i = 0; i < cnt; i++) { + skb_put_le32(skb, base + data[i].reg); + skb_put_le32(skb, data[i].value); + } + + ret = mt7601u_mcu_msg_send(dev, skb, CMD_RANDOM_WRITE, cnt == n); + if (ret) + return ret; + + return mt7601u_write_reg_pairs(dev, base, data + cnt, n - cnt); +} + +int mt7601u_burst_write_regs(struct mt7601u_dev *dev, u32 offset, + const u32 *data, int n) +{ + const int max_regs_per_cmd = INBAND_PACKET_MAX_LEN / 4 - 1; + struct sk_buff *skb; + int cnt, i, ret; + + if (!n) + return 0; + + cnt = min(max_regs_per_cmd, n); + + skb = alloc_skb(cnt * 4 + MT_DMA_HDR_LEN + 4, GFP_KERNEL); + if (!skb) + return -ENOMEM; + skb_reserve(skb, MT_DMA_HDR_LEN); + + skb_put_le32(skb, MT_MCU_MEMMAP_WLAN + offset); + for (i = 0; i < cnt; i++) + skb_put_le32(skb, data[i]); + + ret = mt7601u_mcu_msg_send(dev, skb, CMD_BURST_WRITE, cnt == n); + if (ret) + return ret; + + return mt7601u_burst_write_regs(dev, offset + cnt * 4, + data + cnt, n - cnt); +} + +struct mt76_fw_header { + __le32 ilm_len; + __le32 dlm_len; + __le16 build_ver; + __le16 fw_ver; + u8 pad[4]; + char build_time[16]; +}; + +struct mt76_fw { + struct mt76_fw_header hdr; + u8 ivb[MT_MCU_IVB_SIZE]; + u8 ilm[]; +}; + +static int __mt7601u_dma_fw(struct mt7601u_dev *dev, + const struct mt7601u_dma_buf *dma_buf, + const void *data, u32 len, u32 dst_addr) +{ + DECLARE_COMPLETION_ONSTACK(cmpl); + struct mt7601u_dma_buf buf = *dma_buf; /* we need to fake length */ + __le32 reg; + u32 val; + int ret; + + reg = cpu_to_le32(FIELD_PREP(MT_TXD_INFO_TYPE, DMA_PACKET) | + FIELD_PREP(MT_TXD_INFO_D_PORT, CPU_TX_PORT) | + FIELD_PREP(MT_TXD_INFO_LEN, len)); + memcpy(buf.buf, ®, sizeof(reg)); + memcpy(buf.buf + sizeof(reg), data, len); + memset(buf.buf + sizeof(reg) + len, 0, 8); + + ret = mt7601u_vendor_single_wr(dev, MT_VEND_WRITE_FCE, + MT_FCE_DMA_ADDR, dst_addr); + if (ret) + return ret; + len = roundup(len, 4); + ret = mt7601u_vendor_single_wr(dev, MT_VEND_WRITE_FCE, + MT_FCE_DMA_LEN, len << 16); + if (ret) + return ret; + + buf.len = MT_DMA_HDR_LEN + len + 4; + ret = mt7601u_usb_submit_buf(dev, USB_DIR_OUT, MT_EP_OUT_INBAND_CMD, + &buf, GFP_KERNEL, + mt7601u_complete_urb, &cmpl); + if (ret) + return ret; + + if (!wait_for_completion_timeout(&cmpl, msecs_to_jiffies(1000))) { + dev_err(dev->dev, "Error: firmware upload timed out\n"); + usb_kill_urb(buf.urb); + return -ETIMEDOUT; + } + if (mt7601u_urb_has_error(buf.urb)) { + dev_err(dev->dev, "Error: firmware upload urb failed:%d\n", + buf.urb->status); + return buf.urb->status; + } + + val = mt7601u_rr(dev, MT_TX_CPU_FROM_FCE_CPU_DESC_IDX); + val++; + mt7601u_wr(dev, MT_TX_CPU_FROM_FCE_CPU_DESC_IDX, val); + + return 0; +} + +static int +mt7601u_dma_fw(struct mt7601u_dev *dev, struct mt7601u_dma_buf *dma_buf, + const void *data, int len, u32 dst_addr) +{ + int n, ret; + + if (len == 0) + return 0; + + n = min(MCU_FW_URB_MAX_PAYLOAD, len); + ret = __mt7601u_dma_fw(dev, dma_buf, data, n, dst_addr); + if (ret) + return ret; + + if (!mt76_poll_msec(dev, MT_MCU_COM_REG1, BIT(31), BIT(31), 500)) + return -ETIMEDOUT; + + return mt7601u_dma_fw(dev, dma_buf, data + n, len - n, dst_addr + n); +} + +static int +mt7601u_upload_firmware(struct mt7601u_dev *dev, const struct mt76_fw *fw) +{ + struct mt7601u_dma_buf dma_buf; + void *ivb; + u32 ilm_len, dlm_len; + int i, ret; + + ivb = kmemdup(fw->ivb, sizeof(fw->ivb), GFP_KERNEL); + if (!ivb) + return -ENOMEM; + if (mt7601u_usb_alloc_buf(dev, MCU_FW_URB_SIZE, &dma_buf)) { + ret = -ENOMEM; + goto error; + } + + ilm_len = le32_to_cpu(fw->hdr.ilm_len) - sizeof(fw->ivb); + dev_dbg(dev->dev, "loading FW - ILM %u + IVB %zu\n", + ilm_len, sizeof(fw->ivb)); + ret = mt7601u_dma_fw(dev, &dma_buf, fw->ilm, ilm_len, sizeof(fw->ivb)); + if (ret) + goto error; + + dlm_len = le32_to_cpu(fw->hdr.dlm_len); + dev_dbg(dev->dev, "loading FW - DLM %u\n", dlm_len); + ret = mt7601u_dma_fw(dev, &dma_buf, fw->ilm + ilm_len, + dlm_len, MT_MCU_DLM_OFFSET); + if (ret) + goto error; + + ret = mt7601u_vendor_request(dev, MT_VEND_DEV_MODE, USB_DIR_OUT, + 0x12, 0, ivb, sizeof(fw->ivb)); + if (ret < 0) + goto error; + ret = 0; + + for (i = 100; i && !firmware_running(dev); i--) + msleep(10); + if (!i) { + ret = -ETIMEDOUT; + goto error; + } + + dev_dbg(dev->dev, "Firmware running!\n"); +error: + kfree(ivb); + mt7601u_usb_free_buf(dev, &dma_buf); + + return ret; +} + +static const char * const mt7601u_fw_paths[] = { + "mediatek/" MT7601U_FIRMWARE, + MT7601U_FIRMWARE, +}; + +static int mt7601u_load_firmware(struct mt7601u_dev *dev) +{ + const struct firmware *fw; + const struct mt76_fw_header *hdr; + int len, ret; + u32 val; + int i; + + mt7601u_wr(dev, MT_USB_DMA_CFG, (MT_USB_DMA_CFG_RX_BULK_EN | + MT_USB_DMA_CFG_TX_BULK_EN)); + + if (firmware_running(dev)) + return firmware_request_cache(dev->dev, MT7601U_FIRMWARE); + + /* Try loading firmware from multiple locations */ + fw = NULL; + for (i = 0; i < MT7601U_FIRMWARE_PATHS; i++) { + ret = request_firmware(&fw, mt7601u_fw_paths[i], dev->dev); + if (ret == 0) + break; + } + + if (ret) + return ret; + + if (!fw || !fw->data || fw->size < sizeof(*hdr)) + goto err_inv_fw; + + hdr = (const struct mt76_fw_header *) fw->data; + + if (le32_to_cpu(hdr->ilm_len) <= MT_MCU_IVB_SIZE) + goto err_inv_fw; + + len = sizeof(*hdr); + len += le32_to_cpu(hdr->ilm_len); + len += le32_to_cpu(hdr->dlm_len); + + if (fw->size != len) + goto err_inv_fw; + + val = le16_to_cpu(hdr->fw_ver); + dev_info(dev->dev, + "Firmware Version: %d.%d.%02d Build: %x Build time: %.16s\n", + (val >> 12) & 0xf, (val >> 8) & 0xf, val & 0xf, + le16_to_cpu(hdr->build_ver), hdr->build_time); + + len = le32_to_cpu(hdr->ilm_len); + + mt7601u_wr(dev, 0x94c, 0); + mt7601u_wr(dev, MT_FCE_PSE_CTRL, 0); + + mt7601u_vendor_reset(dev); + msleep(5); + + mt7601u_wr(dev, 0xa44, 0); + mt7601u_wr(dev, 0x230, 0x84210); + mt7601u_wr(dev, 0x400, 0x80c00); + mt7601u_wr(dev, 0x800, 1); + + mt7601u_rmw(dev, MT_PBF_CFG, 0, (MT_PBF_CFG_TX0Q_EN | + MT_PBF_CFG_TX1Q_EN | + MT_PBF_CFG_TX2Q_EN | + MT_PBF_CFG_TX3Q_EN)); + + mt7601u_wr(dev, MT_FCE_PSE_CTRL, 1); + + mt7601u_wr(dev, MT_USB_DMA_CFG, (MT_USB_DMA_CFG_RX_BULK_EN | + MT_USB_DMA_CFG_TX_BULK_EN)); + val = mt76_set(dev, MT_USB_DMA_CFG, MT_USB_DMA_CFG_TX_CLR); + val &= ~MT_USB_DMA_CFG_TX_CLR; + mt7601u_wr(dev, MT_USB_DMA_CFG, val); + + /* FCE tx_fs_base_ptr */ + mt7601u_wr(dev, MT_TX_CPU_FROM_FCE_BASE_PTR, 0x400230); + /* FCE tx_fs_max_cnt */ + mt7601u_wr(dev, MT_TX_CPU_FROM_FCE_MAX_COUNT, 1); + /* FCE pdma enable */ + mt7601u_wr(dev, MT_FCE_PDMA_GLOBAL_CONF, 0x44); + /* FCE skip_fs_en */ + mt7601u_wr(dev, MT_FCE_SKIP_FS, 3); + + ret = mt7601u_upload_firmware(dev, (const struct mt76_fw *)fw->data); + + release_firmware(fw); + + return ret; + +err_inv_fw: + dev_err(dev->dev, "Invalid firmware image\n"); + release_firmware(fw); + return -ENOENT; +} + +int mt7601u_mcu_init(struct mt7601u_dev *dev) +{ + int ret; + + mutex_init(&dev->mcu.mutex); + + ret = mt7601u_load_firmware(dev); + if (ret) + return ret; + + set_bit(MT7601U_STATE_MCU_RUNNING, &dev->state); + + return 0; +} + +int mt7601u_mcu_cmd_init(struct mt7601u_dev *dev) +{ + int ret; + + ret = mt7601u_mcu_function_select(dev, Q_SELECT, 1); + if (ret) + return ret; + + init_completion(&dev->mcu.resp_cmpl); + if (mt7601u_usb_alloc_buf(dev, MCU_RESP_URB_SIZE, &dev->mcu.resp)) { + mt7601u_usb_free_buf(dev, &dev->mcu.resp); + return -ENOMEM; + } + + ret = mt7601u_usb_submit_buf(dev, USB_DIR_IN, MT_EP_IN_CMD_RESP, + &dev->mcu.resp, GFP_KERNEL, + mt7601u_complete_urb, &dev->mcu.resp_cmpl); + if (ret) { + mt7601u_usb_free_buf(dev, &dev->mcu.resp); + return ret; + } + + return 0; +} + +void mt7601u_mcu_cmd_deinit(struct mt7601u_dev *dev) +{ + usb_kill_urb(dev->mcu.resp.urb); + mt7601u_usb_free_buf(dev, &dev->mcu.resp); +} diff --git a/drivers/net/wireless/mediatek/mt7601u/mcu.h b/drivers/net/wireless/mediatek/mt7601u/mcu.h new file mode 100644 index 000000000..56cdd31aa --- /dev/null +++ b/drivers/net/wireless/mediatek/mt7601u/mcu.h @@ -0,0 +1,86 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2014 Felix Fietkau + * Copyright (C) 2015 Jakub Kicinski + */ + +#ifndef __MT7601U_MCU_H +#define __MT7601U_MCU_H + +struct mt7601u_dev; + +/* Register definitions */ +#define MT_MCU_RESET_CTL 0x070C +#define MT_MCU_INT_LEVEL 0x0718 +#define MT_MCU_COM_REG0 0x0730 +#define MT_MCU_COM_REG1 0x0734 +#define MT_MCU_COM_REG2 0x0738 +#define MT_MCU_COM_REG3 0x073C + +#define MT_MCU_IVB_SIZE 0x40 +#define MT_MCU_DLM_OFFSET 0x80000 + +#define MT_MCU_MEMMAP_WLAN 0x00410000 +#define MT_MCU_MEMMAP_BBP 0x40000000 +#define MT_MCU_MEMMAP_RF 0x80000000 + +#define INBAND_PACKET_MAX_LEN 192 + +enum mcu_cmd { + CMD_FUN_SET_OP = 1, + CMD_LOAD_CR = 2, + CMD_INIT_GAIN_OP = 3, + CMD_DYNC_VGA_OP = 6, + CMD_TDLS_CH_SW = 7, + CMD_BURST_WRITE = 8, + CMD_READ_MODIFY_WRITE = 9, + CMD_RANDOM_READ = 10, + CMD_BURST_READ = 11, + CMD_RANDOM_WRITE = 12, + CMD_LED_MODE_OP = 16, + CMD_POWER_SAVING_OP = 20, + CMD_WOW_CONFIG = 21, + CMD_WOW_QUERY = 22, + CMD_WOW_FEATURE = 24, + CMD_CARRIER_DETECT_OP = 28, + CMD_RADOR_DETECT_OP = 29, + CMD_SWITCH_CHANNEL_OP = 30, + CMD_CALIBRATION_OP = 31, + CMD_BEACON_OP = 32, + CMD_ANTENNA_OP = 33, +}; + +enum mcu_function { + Q_SELECT = 1, + ATOMIC_TSSI_SETTING = 5, +}; + +enum mcu_power_mode { + RADIO_OFF = 0x30, + RADIO_ON = 0x31, + RADIO_OFF_AUTO_WAKEUP = 0x32, + RADIO_OFF_ADVANCE = 0x33, + RADIO_ON_ADVANCE = 0x34, +}; + +enum mcu_calibrate { + MCU_CAL_R = 1, + MCU_CAL_DCOC, + MCU_CAL_LC, + MCU_CAL_LOFT, + MCU_CAL_TXIQ, + MCU_CAL_BW, + MCU_CAL_DPD, + MCU_CAL_RXIQ, + MCU_CAL_TXDCOC, +}; + +int mt7601u_mcu_init(struct mt7601u_dev *dev); +int mt7601u_mcu_cmd_init(struct mt7601u_dev *dev); +void mt7601u_mcu_cmd_deinit(struct mt7601u_dev *dev); + +int +mt7601u_mcu_calibrate(struct mt7601u_dev *dev, enum mcu_calibrate cal, u32 val); +int mt7601u_mcu_tssi_read_kick(struct mt7601u_dev *dev, int use_hvga); + +#endif diff --git a/drivers/net/wireless/mediatek/mt7601u/mt7601u.h b/drivers/net/wireless/mediatek/mt7601u/mt7601u.h new file mode 100644 index 000000000..118d43707 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt7601u/mt7601u.h @@ -0,0 +1,393 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2014 Felix Fietkau + * Copyright (C) 2015 Jakub Kicinski + */ + +#ifndef MT7601U_H +#define MT7601U_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "regs.h" + +#define MT_CALIBRATE_INTERVAL (4 * HZ) + +#define MT_FREQ_CAL_INIT_DELAY (30 * HZ) +#define MT_FREQ_CAL_CHECK_INTERVAL (10 * HZ) +#define MT_FREQ_CAL_ADJ_INTERVAL (HZ / 2) + +#define MT_BBP_REG_VERSION 0x00 + +#define MT_USB_AGGR_SIZE_LIMIT 28 /* * 1024B */ +#define MT_USB_AGGR_TIMEOUT 0x80 /* * 33ns */ +#define MT_RX_ORDER 3 +#define MT_RX_URB_SIZE (PAGE_SIZE << MT_RX_ORDER) + +struct mt7601u_dma_buf { + struct urb *urb; + void *buf; + dma_addr_t dma; + size_t len; +}; + +struct mt7601u_mcu { + struct mutex mutex; + + u8 msg_seq; + + struct mt7601u_dma_buf resp; + struct completion resp_cmpl; +}; + +struct mt7601u_freq_cal { + struct delayed_work work; + u8 freq; + bool enabled; + bool adjusting; +}; + +struct mac_stats { + u64 rx_stat[6]; + u64 tx_stat[6]; + u64 aggr_stat[2]; + u64 aggr_n[32]; + u64 zero_len_del[2]; +}; + +#define N_RX_ENTRIES 16 +struct mt7601u_rx_queue { + struct mt7601u_dev *dev; + + struct mt7601u_dma_buf_rx { + struct urb *urb; + struct page *p; + } e[N_RX_ENTRIES]; + + unsigned int start; + unsigned int end; + unsigned int entries; + unsigned int pending; +}; + +#define N_TX_ENTRIES 64 + +struct mt7601u_tx_queue { + struct mt7601u_dev *dev; + + struct mt7601u_dma_buf_tx { + struct urb *urb; + struct sk_buff *skb; + } e[N_TX_ENTRIES]; + + unsigned int start; + unsigned int end; + unsigned int entries; + unsigned int used; + unsigned int fifo_seq; +}; + +/* WCID allocation: + * 0: mcast wcid + * 1: bssid wcid + * 1...: STAs + * ...7e: group wcids + * 7f: reserved + */ +#define N_WCIDS 128 +#define GROUP_WCID(idx) (N_WCIDS - 2 - idx) + +struct mt7601u_eeprom_params; + +#define MT_EE_TEMPERATURE_SLOPE 39 +#define MT_FREQ_OFFSET_INVALID -128 + +enum mt_temp_mode { + MT_TEMP_MODE_NORMAL, + MT_TEMP_MODE_HIGH, + MT_TEMP_MODE_LOW, +}; + +enum mt_bw { + MT_BW_20, + MT_BW_40, +}; + +enum { + MT7601U_STATE_INITIALIZED, + MT7601U_STATE_REMOVED, + MT7601U_STATE_WLAN_RUNNING, + MT7601U_STATE_MCU_RUNNING, + MT7601U_STATE_SCANNING, + MT7601U_STATE_READING_STATS, + MT7601U_STATE_MORE_STATS, +}; + +DECLARE_EWMA(rssi, 10, 4); + +/** + * struct mt7601u_dev - adapter structure + * @lock: protects @wcid->tx_rate. + * @mac_lock: locks out mac80211's tx status and rx paths. + * @tx_lock: protects @tx_q and changes of MT7601U_STATE_*_STATS + * flags in @state. + * @rx_lock: protects @rx_q. + * @con_mon_lock: protects @ap_bssid, @bcn_*, @avg_rssi. + * @mutex: ensures exclusive access from mac80211 callbacks. + * @vendor_req_mutex: protects @vend_buf, ensures atomicity of read/write + * accesses + * @reg_atomic_mutex: ensures atomicity of indirect register accesses + * (accesses to RF and BBP). + * @hw_atomic_mutex: ensures exclusive access to HW during critical + * operations (power management, channel switch). + */ +struct mt7601u_dev { + struct ieee80211_hw *hw; + struct device *dev; + + unsigned long state; + + struct mutex mutex; + + unsigned long wcid_mask[N_WCIDS / BITS_PER_LONG]; + + struct cfg80211_chan_def chandef; + struct ieee80211_supported_band *sband_2g; + + struct mt7601u_mcu mcu; + + struct delayed_work cal_work; + struct delayed_work mac_work; + + struct workqueue_struct *stat_wq; + struct delayed_work stat_work; + + struct mt76_wcid *mon_wcid; + struct mt76_wcid __rcu *wcid[N_WCIDS]; + + spinlock_t lock; + spinlock_t mac_lock; + + const u16 *beacon_offsets; + + u8 macaddr[ETH_ALEN]; + struct mt7601u_eeprom_params *ee; + + struct mutex vendor_req_mutex; + void *vend_buf; + + struct mutex reg_atomic_mutex; + struct mutex hw_atomic_mutex; + + u32 rxfilter; + u32 debugfs_reg; + + u8 out_eps[8]; + u8 in_eps[8]; + u16 out_max_packet; + u16 in_max_packet; + + /* TX */ + spinlock_t tx_lock; + struct tasklet_struct tx_tasklet; + struct mt7601u_tx_queue *tx_q; + struct sk_buff_head tx_skb_done; + + atomic_t avg_ampdu_len; + + /* RX */ + spinlock_t rx_lock; + struct tasklet_struct rx_tasklet; + struct mt7601u_rx_queue rx_q; + + /* Connection monitoring things */ + spinlock_t con_mon_lock; + u8 ap_bssid[ETH_ALEN]; + + s8 bcn_freq_off; + u8 bcn_phy_mode; + + struct ewma_rssi avg_rssi; + + u8 agc_save; + + struct mt7601u_freq_cal freq_cal; + + bool tssi_read_trig; + + s8 tssi_init; + s8 tssi_init_hvga; + s16 tssi_init_hvga_offset_db; + + int prev_pwr_diff; + + enum mt_temp_mode temp_mode; + int curr_temp; + int dpd_temp; + s8 raw_temp; + bool pll_lock_protect; + + u8 bw; + bool chan_ext_below; + + /* PA mode */ + u32 rf_pa_mode[2]; + + struct mac_stats stats; +}; + +struct mt7601u_tssi_params { + char tssi0; + int trgt_power; +}; + +struct mt76_wcid { + u8 idx; + u8 hw_key_idx; + + u16 tx_rate; + bool tx_rate_set; + u8 tx_rate_nss; +}; + +struct mt76_vif { + u8 idx; + + struct mt76_wcid group_wcid; +}; + +struct mt76_sta { + struct mt76_wcid wcid; + u16 agg_ssn[IEEE80211_NUM_TIDS]; +}; + +struct mt76_reg_pair { + u32 reg; + u32 value; +}; + +struct mt7601u_rxwi; + +extern const struct ieee80211_ops mt7601u_ops; + +void mt7601u_init_debugfs(struct mt7601u_dev *dev); + +u32 mt7601u_rr(struct mt7601u_dev *dev, u32 offset); +void mt7601u_wr(struct mt7601u_dev *dev, u32 offset, u32 val); +u32 mt7601u_rmw(struct mt7601u_dev *dev, u32 offset, u32 mask, u32 val); +u32 mt7601u_rmc(struct mt7601u_dev *dev, u32 offset, u32 mask, u32 val); +void mt7601u_wr_copy(struct mt7601u_dev *dev, u32 offset, + const void *data, int len); + +int mt7601u_wait_asic_ready(struct mt7601u_dev *dev); +bool mt76_poll(struct mt7601u_dev *dev, u32 offset, u32 mask, u32 val, + int timeout); +bool mt76_poll_msec(struct mt7601u_dev *dev, u32 offset, u32 mask, u32 val, + int timeout); + +/* Compatibility with mt76 */ +#define mt76_rmw_field(_dev, _reg, _field, _val) \ + mt76_rmw(_dev, _reg, _field, FIELD_PREP(_field, _val)) + +static inline u32 mt76_rr(struct mt7601u_dev *dev, u32 offset) +{ + return mt7601u_rr(dev, offset); +} + +static inline void mt76_wr(struct mt7601u_dev *dev, u32 offset, u32 val) +{ + return mt7601u_wr(dev, offset, val); +} + +static inline u32 +mt76_rmw(struct mt7601u_dev *dev, u32 offset, u32 mask, u32 val) +{ + return mt7601u_rmw(dev, offset, mask, val); +} + +static inline u32 mt76_set(struct mt7601u_dev *dev, u32 offset, u32 val) +{ + return mt76_rmw(dev, offset, 0, val); +} + +static inline u32 mt76_clear(struct mt7601u_dev *dev, u32 offset, u32 val) +{ + return mt76_rmw(dev, offset, val, 0); +} + +int mt7601u_write_reg_pairs(struct mt7601u_dev *dev, u32 base, + const struct mt76_reg_pair *data, int len); +int mt7601u_burst_write_regs(struct mt7601u_dev *dev, u32 offset, + const u32 *data, int n); +void mt7601u_addr_wr(struct mt7601u_dev *dev, const u32 offset, const u8 *addr); + +/* Init */ +struct mt7601u_dev *mt7601u_alloc_device(struct device *dev); +int mt7601u_init_hardware(struct mt7601u_dev *dev); +int mt7601u_register_device(struct mt7601u_dev *dev); +void mt7601u_cleanup(struct mt7601u_dev *dev); + +int mt7601u_mac_start(struct mt7601u_dev *dev); +void mt7601u_mac_stop(struct mt7601u_dev *dev); + +/* PHY */ +int mt7601u_phy_init(struct mt7601u_dev *dev); +int mt7601u_wait_bbp_ready(struct mt7601u_dev *dev); +void mt7601u_set_rx_path(struct mt7601u_dev *dev, u8 path); +void mt7601u_set_tx_dac(struct mt7601u_dev *dev, u8 path); +int mt7601u_bbp_set_bw(struct mt7601u_dev *dev, int bw); +void mt7601u_agc_save(struct mt7601u_dev *dev); +void mt7601u_agc_restore(struct mt7601u_dev *dev); +int mt7601u_phy_set_channel(struct mt7601u_dev *dev, + struct cfg80211_chan_def *chandef); +void mt7601u_phy_recalibrate_after_assoc(struct mt7601u_dev *dev); +int mt7601u_phy_get_rssi(struct mt7601u_dev *dev, + struct mt7601u_rxwi *rxwi, u16 rate); +void mt7601u_phy_con_cal_onoff(struct mt7601u_dev *dev, + struct ieee80211_bss_conf *info); + +/* MAC */ +void mt7601u_mac_work(struct work_struct *work); +void mt7601u_mac_set_protection(struct mt7601u_dev *dev, bool legacy_prot, + int ht_mode); +void mt7601u_mac_set_short_preamble(struct mt7601u_dev *dev, bool short_preamb); +void mt7601u_mac_config_tsf(struct mt7601u_dev *dev, bool enable, int interval); +void +mt7601u_mac_wcid_setup(struct mt7601u_dev *dev, u8 idx, u8 vif_idx, u8 *mac); +void mt7601u_mac_set_ampdu_factor(struct mt7601u_dev *dev); + +/* TX */ +void mt7601u_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, + struct sk_buff *skb); +int mt7601u_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + unsigned int link_id, u16 queue, + const struct ieee80211_tx_queue_params *params); +void mt7601u_tx_status(struct mt7601u_dev *dev, struct sk_buff *skb); +void mt7601u_tx_stat(struct work_struct *work); + +/* util */ +void mt76_remove_hdr_pad(struct sk_buff *skb); +int mt76_insert_hdr_pad(struct sk_buff *skb); + +u32 mt7601u_bbp_set_ctrlch(struct mt7601u_dev *dev, bool below); + +static inline u32 mt7601u_mac_set_ctrlch(struct mt7601u_dev *dev, bool below) +{ + return mt7601u_rmc(dev, MT_TX_BAND_CFG, 1, below); +} + +int mt7601u_dma_init(struct mt7601u_dev *dev); +void mt7601u_dma_cleanup(struct mt7601u_dev *dev); + +int mt7601u_dma_enqueue_tx(struct mt7601u_dev *dev, struct sk_buff *skb, + struct mt76_wcid *wcid, int hw_q); + +#endif diff --git a/drivers/net/wireless/mediatek/mt7601u/phy.c b/drivers/net/wireless/mediatek/mt7601u/phy.c new file mode 100644 index 000000000..d4cd2215a --- /dev/null +++ b/drivers/net/wireless/mediatek/mt7601u/phy.c @@ -0,0 +1,1258 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * (c) Copyright 2002-2010, Ralink Technology, Inc. + * Copyright (C) 2014 Felix Fietkau + * Copyright (C) 2015 Jakub Kicinski + */ + +#include "mt7601u.h" +#include "mcu.h" +#include "eeprom.h" +#include "trace.h" +#include "initvals_phy.h" + +#include + +static void mt7601u_agc_reset(struct mt7601u_dev *dev); + +static int +mt7601u_rf_wr(struct mt7601u_dev *dev, u8 bank, u8 offset, u8 value) +{ + int ret = 0; + + if (WARN_ON(!test_bit(MT7601U_STATE_WLAN_RUNNING, &dev->state)) || + WARN_ON(offset > 63)) + return -EINVAL; + if (test_bit(MT7601U_STATE_REMOVED, &dev->state)) + return 0; + + mutex_lock(&dev->reg_atomic_mutex); + + if (!mt76_poll(dev, MT_RF_CSR_CFG, MT_RF_CSR_CFG_KICK, 0, 100)) { + ret = -ETIMEDOUT; + goto out; + } + + mt7601u_wr(dev, MT_RF_CSR_CFG, + FIELD_PREP(MT_RF_CSR_CFG_DATA, value) | + FIELD_PREP(MT_RF_CSR_CFG_REG_BANK, bank) | + FIELD_PREP(MT_RF_CSR_CFG_REG_ID, offset) | + MT_RF_CSR_CFG_WR | + MT_RF_CSR_CFG_KICK); + trace_rf_write(dev, bank, offset, value); +out: + mutex_unlock(&dev->reg_atomic_mutex); + + if (ret < 0) + dev_err(dev->dev, "Error: RF write %02hhx:%02hhx failed:%d!!\n", + bank, offset, ret); + + return ret; +} + +static int +mt7601u_rf_rr(struct mt7601u_dev *dev, u8 bank, u8 offset) +{ + int ret = -ETIMEDOUT; + u32 val; + + if (WARN_ON(!test_bit(MT7601U_STATE_WLAN_RUNNING, &dev->state)) || + WARN_ON(offset > 63)) + return -EINVAL; + if (test_bit(MT7601U_STATE_REMOVED, &dev->state)) + return 0xff; + + mutex_lock(&dev->reg_atomic_mutex); + + if (!mt76_poll(dev, MT_RF_CSR_CFG, MT_RF_CSR_CFG_KICK, 0, 100)) + goto out; + + mt7601u_wr(dev, MT_RF_CSR_CFG, + FIELD_PREP(MT_RF_CSR_CFG_REG_BANK, bank) | + FIELD_PREP(MT_RF_CSR_CFG_REG_ID, offset) | + MT_RF_CSR_CFG_KICK); + + if (!mt76_poll(dev, MT_RF_CSR_CFG, MT_RF_CSR_CFG_KICK, 0, 100)) + goto out; + + val = mt7601u_rr(dev, MT_RF_CSR_CFG); + if (FIELD_GET(MT_RF_CSR_CFG_REG_ID, val) == offset && + FIELD_GET(MT_RF_CSR_CFG_REG_BANK, val) == bank) { + ret = FIELD_GET(MT_RF_CSR_CFG_DATA, val); + trace_rf_read(dev, bank, offset, ret); + } +out: + mutex_unlock(&dev->reg_atomic_mutex); + + if (ret < 0) + dev_err(dev->dev, "Error: RF read %02hhx:%02hhx failed:%d!!\n", + bank, offset, ret); + + return ret; +} + +static int +mt7601u_rf_rmw(struct mt7601u_dev *dev, u8 bank, u8 offset, u8 mask, u8 val) +{ + int ret; + + ret = mt7601u_rf_rr(dev, bank, offset); + if (ret < 0) + return ret; + val |= ret & ~mask; + ret = mt7601u_rf_wr(dev, bank, offset, val); + if (ret) + return ret; + + return val; +} + +static int +mt7601u_rf_set(struct mt7601u_dev *dev, u8 bank, u8 offset, u8 val) +{ + return mt7601u_rf_rmw(dev, bank, offset, 0, val); +} + +static int +mt7601u_rf_clear(struct mt7601u_dev *dev, u8 bank, u8 offset, u8 mask) +{ + return mt7601u_rf_rmw(dev, bank, offset, mask, 0); +} + +static void mt7601u_bbp_wr(struct mt7601u_dev *dev, u8 offset, u8 val) +{ + if (WARN_ON(!test_bit(MT7601U_STATE_WLAN_RUNNING, &dev->state)) || + test_bit(MT7601U_STATE_REMOVED, &dev->state)) + return; + + mutex_lock(&dev->reg_atomic_mutex); + + if (!mt76_poll(dev, MT_BBP_CSR_CFG, MT_BBP_CSR_CFG_BUSY, 0, 1000)) { + dev_err(dev->dev, "Error: BBP write %02hhx failed!!\n", offset); + goto out; + } + + mt7601u_wr(dev, MT_BBP_CSR_CFG, + FIELD_PREP(MT_BBP_CSR_CFG_VAL, val) | + FIELD_PREP(MT_BBP_CSR_CFG_REG_NUM, offset) | + MT_BBP_CSR_CFG_RW_MODE | MT_BBP_CSR_CFG_BUSY); + trace_bbp_write(dev, offset, val); +out: + mutex_unlock(&dev->reg_atomic_mutex); +} + +static int mt7601u_bbp_rr(struct mt7601u_dev *dev, u8 offset) +{ + u32 val; + int ret = -ETIMEDOUT; + + if (WARN_ON(!test_bit(MT7601U_STATE_WLAN_RUNNING, &dev->state))) + return -EINVAL; + if (test_bit(MT7601U_STATE_REMOVED, &dev->state)) + return 0xff; + + mutex_lock(&dev->reg_atomic_mutex); + + if (!mt76_poll(dev, MT_BBP_CSR_CFG, MT_BBP_CSR_CFG_BUSY, 0, 1000)) + goto out; + + mt7601u_wr(dev, MT_BBP_CSR_CFG, + FIELD_PREP(MT_BBP_CSR_CFG_REG_NUM, offset) | + MT_BBP_CSR_CFG_RW_MODE | MT_BBP_CSR_CFG_BUSY | + MT_BBP_CSR_CFG_READ); + + if (!mt76_poll(dev, MT_BBP_CSR_CFG, MT_BBP_CSR_CFG_BUSY, 0, 1000)) + goto out; + + val = mt7601u_rr(dev, MT_BBP_CSR_CFG); + if (FIELD_GET(MT_BBP_CSR_CFG_REG_NUM, val) == offset) { + ret = FIELD_GET(MT_BBP_CSR_CFG_VAL, val); + trace_bbp_read(dev, offset, ret); + } +out: + mutex_unlock(&dev->reg_atomic_mutex); + + if (ret < 0) + dev_err(dev->dev, "Error: BBP read %02hhx failed:%d!!\n", + offset, ret); + + return ret; +} + +static int mt7601u_bbp_rmw(struct mt7601u_dev *dev, u8 offset, u8 mask, u8 val) +{ + int ret; + + ret = mt7601u_bbp_rr(dev, offset); + if (ret < 0) + return ret; + val |= ret & ~mask; + mt7601u_bbp_wr(dev, offset, val); + + return val; +} + +static u8 mt7601u_bbp_rmc(struct mt7601u_dev *dev, u8 offset, u8 mask, u8 val) +{ + int ret; + + ret = mt7601u_bbp_rr(dev, offset); + if (ret < 0) + return ret; + val |= ret & ~mask; + if (ret != val) + mt7601u_bbp_wr(dev, offset, val); + + return val; +} + +int mt7601u_wait_bbp_ready(struct mt7601u_dev *dev) +{ + int i = 20; + u8 val; + + do { + val = mt7601u_bbp_rr(dev, MT_BBP_REG_VERSION); + if (val && val != 0xff) + break; + } while (--i); + + if (!i) { + dev_err(dev->dev, "Error: BBP is not ready\n"); + return -EIO; + } + + return 0; +} + +u32 mt7601u_bbp_set_ctrlch(struct mt7601u_dev *dev, bool below) +{ + return mt7601u_bbp_rmc(dev, 3, 0x20, below ? 0x20 : 0); +} + +int mt7601u_phy_get_rssi(struct mt7601u_dev *dev, + struct mt7601u_rxwi *rxwi, u16 rate) +{ + static const s8 lna[2][2][3] = { + /* main LNA */ { + /* bw20 */ { -2, 15, 33 }, + /* bw40 */ { 0, 16, 34 } + }, + /* aux LNA */ { + /* bw20 */ { -2, 15, 33 }, + /* bw40 */ { -2, 16, 34 } + } + }; + int bw = FIELD_GET(MT_RXWI_RATE_BW, rate); + int aux_lna = FIELD_GET(MT_RXWI_ANT_AUX_LNA, rxwi->ant); + int lna_id = FIELD_GET(MT_RXWI_GAIN_RSSI_LNA_ID, rxwi->gain); + int val; + + if (lna_id) /* LNA id can be 0, 2, 3. */ + lna_id--; + + val = 8; + val -= lna[aux_lna][bw][lna_id]; + val -= FIELD_GET(MT_RXWI_GAIN_RSSI_VAL, rxwi->gain); + val -= dev->ee->lna_gain; + val -= dev->ee->rssi_offset[0]; + + return val; +} + +static void mt7601u_vco_cal(struct mt7601u_dev *dev) +{ + mt7601u_rf_wr(dev, 0, 4, 0x0a); + mt7601u_rf_wr(dev, 0, 5, 0x20); + mt7601u_rf_set(dev, 0, 4, BIT(7)); + msleep(2); +} + +static int mt7601u_set_bw_filter(struct mt7601u_dev *dev, bool cal) +{ + u32 filter = 0; + int ret; + + if (!cal) + filter |= 0x10000; + if (dev->bw != MT_BW_20) + filter |= 0x00100; + + /* TX */ + ret = mt7601u_mcu_calibrate(dev, MCU_CAL_BW, filter | 1); + if (ret) + return ret; + /* RX */ + return mt7601u_mcu_calibrate(dev, MCU_CAL_BW, filter); +} + +static int mt7601u_load_bbp_temp_table_bw(struct mt7601u_dev *dev) +{ + const struct reg_table *t; + + if (WARN_ON(dev->temp_mode > MT_TEMP_MODE_LOW)) + return -EINVAL; + + t = &bbp_mode_table[dev->temp_mode][dev->bw]; + + return mt7601u_write_reg_pairs(dev, MT_MCU_MEMMAP_BBP, t->regs, t->n); +} + +static int mt7601u_bbp_temp(struct mt7601u_dev *dev, int mode, const char *name) +{ + const struct reg_table *t; + int ret; + + if (dev->temp_mode == mode) + return 0; + + dev->temp_mode = mode; + trace_temp_mode(dev, mode); + + t = bbp_mode_table[dev->temp_mode]; + ret = mt7601u_write_reg_pairs(dev, MT_MCU_MEMMAP_BBP, + t[2].regs, t[2].n); + if (ret) + return ret; + + return mt7601u_write_reg_pairs(dev, MT_MCU_MEMMAP_BBP, + t[dev->bw].regs, t[dev->bw].n); +} + +static void mt7601u_apply_ch14_fixup(struct mt7601u_dev *dev, int hw_chan) +{ + struct mt7601u_rate_power *t = &dev->ee->power_rate_table; + + if (hw_chan != 14 || dev->bw != MT_BW_20) { + mt7601u_bbp_rmw(dev, 4, 0x20, 0); + mt7601u_bbp_wr(dev, 178, 0xff); + + t->cck[0].bw20 = dev->ee->real_cck_bw20[0]; + t->cck[1].bw20 = dev->ee->real_cck_bw20[1]; + } else { /* Apply CH14 OBW fixup */ + mt7601u_bbp_wr(dev, 4, 0x60); + mt7601u_bbp_wr(dev, 178, 0); + + /* Note: vendor code is buggy here for negative values */ + t->cck[0].bw20 = dev->ee->real_cck_bw20[0] - 2; + t->cck[1].bw20 = dev->ee->real_cck_bw20[1] - 2; + } +} + +static int __mt7601u_phy_set_channel(struct mt7601u_dev *dev, + struct cfg80211_chan_def *chandef) +{ +#define FREQ_PLAN_REGS 4 + static const u8 freq_plan[14][FREQ_PLAN_REGS] = { + { 0x99, 0x99, 0x09, 0x50 }, + { 0x46, 0x44, 0x0a, 0x50 }, + { 0xec, 0xee, 0x0a, 0x50 }, + { 0x99, 0x99, 0x0b, 0x50 }, + { 0x46, 0x44, 0x08, 0x51 }, + { 0xec, 0xee, 0x08, 0x51 }, + { 0x99, 0x99, 0x09, 0x51 }, + { 0x46, 0x44, 0x0a, 0x51 }, + { 0xec, 0xee, 0x0a, 0x51 }, + { 0x99, 0x99, 0x0b, 0x51 }, + { 0x46, 0x44, 0x08, 0x52 }, + { 0xec, 0xee, 0x08, 0x52 }, + { 0x99, 0x99, 0x09, 0x52 }, + { 0x33, 0x33, 0x0b, 0x52 }, + }; + struct mt76_reg_pair channel_freq_plan[FREQ_PLAN_REGS] = { + { 17, 0 }, { 18, 0 }, { 19, 0 }, { 20, 0 }, + }; + struct mt76_reg_pair bbp_settings[3] = { + { 62, 0x37 - dev->ee->lna_gain }, + { 63, 0x37 - dev->ee->lna_gain }, + { 64, 0x37 - dev->ee->lna_gain }, + }; + + struct ieee80211_channel *chan = chandef->chan; + enum nl80211_channel_type chan_type = + cfg80211_get_chandef_type(chandef); + struct mt7601u_rate_power *t = &dev->ee->power_rate_table; + int chan_idx; + bool chan_ext_below; + u8 bw; + int i, ret; + + bw = MT_BW_20; + chan_ext_below = (chan_type == NL80211_CHAN_HT40MINUS); + chan_idx = chan->hw_value - 1; + + if (chandef->width == NL80211_CHAN_WIDTH_40) { + bw = MT_BW_40; + + if (chan_idx > 1 && chan_type == NL80211_CHAN_HT40MINUS) + chan_idx -= 2; + else if (chan_idx < 12 && chan_type == NL80211_CHAN_HT40PLUS) + chan_idx += 2; + else + dev_err(dev->dev, "Error: invalid 40MHz channel!!\n"); + } + + if (bw != dev->bw || chan_ext_below != dev->chan_ext_below) { + dev_dbg(dev->dev, "Info: switching HT mode bw:%d below:%d\n", + bw, chan_ext_below); + + mt7601u_bbp_set_bw(dev, bw); + + mt7601u_bbp_set_ctrlch(dev, chan_ext_below); + mt7601u_mac_set_ctrlch(dev, chan_ext_below); + dev->chan_ext_below = chan_ext_below; + } + + for (i = 0; i < FREQ_PLAN_REGS; i++) + channel_freq_plan[i].value = freq_plan[chan_idx][i]; + + ret = mt7601u_write_reg_pairs(dev, MT_MCU_MEMMAP_RF, + channel_freq_plan, FREQ_PLAN_REGS); + if (ret) + return ret; + + mt7601u_rmw(dev, MT_TX_ALC_CFG_0, 0x3f3f, + dev->ee->chan_pwr[chan_idx] & 0x3f); + + ret = mt7601u_write_reg_pairs(dev, MT_MCU_MEMMAP_BBP, + bbp_settings, ARRAY_SIZE(bbp_settings)); + if (ret) + return ret; + + mt7601u_vco_cal(dev); + mt7601u_bbp_set_bw(dev, bw); + ret = mt7601u_set_bw_filter(dev, false); + if (ret) + return ret; + + mt7601u_apply_ch14_fixup(dev, chan->hw_value); + mt7601u_wr(dev, MT_TX_PWR_CFG_0, int_to_s6(t->ofdm[1].bw20) << 24 | + int_to_s6(t->ofdm[0].bw20) << 16 | + int_to_s6(t->cck[1].bw20) << 8 | + int_to_s6(t->cck[0].bw20)); + + if (test_bit(MT7601U_STATE_SCANNING, &dev->state)) + mt7601u_agc_reset(dev); + + dev->chandef = *chandef; + + return 0; +} + +int mt7601u_phy_set_channel(struct mt7601u_dev *dev, + struct cfg80211_chan_def *chandef) +{ + int ret; + + cancel_delayed_work_sync(&dev->cal_work); + cancel_delayed_work_sync(&dev->freq_cal.work); + + mutex_lock(&dev->hw_atomic_mutex); + ret = __mt7601u_phy_set_channel(dev, chandef); + mutex_unlock(&dev->hw_atomic_mutex); + if (ret) + return ret; + + if (test_bit(MT7601U_STATE_SCANNING, &dev->state)) + return 0; + + ieee80211_queue_delayed_work(dev->hw, &dev->cal_work, + MT_CALIBRATE_INTERVAL); + if (dev->freq_cal.enabled) + ieee80211_queue_delayed_work(dev->hw, &dev->freq_cal.work, + MT_FREQ_CAL_INIT_DELAY); + return 0; +} + +#define BBP_R47_FLAG GENMASK(2, 0) +#define BBP_R47_F_TSSI 0 +#define BBP_R47_F_PKT_T 1 +#define BBP_R47_F_TX_RATE 2 +#define BBP_R47_F_TEMP 4 +/** + * mt7601u_bbp_r47_get - read value through BBP R47/R49 pair + * @dev: pointer to adapter structure + * @reg: value of BBP R47 before the operation + * @flag: one of the BBP_R47_F_* flags + * + * Convenience helper for reading values through BBP R47/R49 pair. + * Takes old value of BBP R47 as @reg, because callers usually have it + * cached already. + * + * Return: value of BBP R49. + */ +static u8 mt7601u_bbp_r47_get(struct mt7601u_dev *dev, u8 reg, u8 flag) +{ + flag |= reg & ~BBP_R47_FLAG; + mt7601u_bbp_wr(dev, 47, flag); + usleep_range(500, 700); + return mt7601u_bbp_rr(dev, 49); +} + +static s8 mt7601u_read_bootup_temp(struct mt7601u_dev *dev) +{ + u8 bbp_val, temp; + u32 rf_bp, rf_set; + int i; + + rf_set = mt7601u_rr(dev, MT_RF_SETTING_0); + rf_bp = mt7601u_rr(dev, MT_RF_BYPASS_0); + + mt7601u_wr(dev, MT_RF_BYPASS_0, 0); + mt7601u_wr(dev, MT_RF_SETTING_0, 0x00000010); + mt7601u_wr(dev, MT_RF_BYPASS_0, 0x00000010); + + bbp_val = mt7601u_bbp_rmw(dev, 47, 0, 0x10); + + mt7601u_bbp_wr(dev, 22, 0x40); + + for (i = 100; i && (bbp_val & 0x10); i--) + bbp_val = mt7601u_bbp_rr(dev, 47); + + temp = mt7601u_bbp_r47_get(dev, bbp_val, BBP_R47_F_TEMP); + + mt7601u_bbp_wr(dev, 22, 0); + + bbp_val = mt7601u_bbp_rr(dev, 21); + bbp_val |= 0x02; + mt7601u_bbp_wr(dev, 21, bbp_val); + bbp_val &= ~0x02; + mt7601u_bbp_wr(dev, 21, bbp_val); + + mt7601u_wr(dev, MT_RF_BYPASS_0, 0); + mt7601u_wr(dev, MT_RF_SETTING_0, rf_set); + mt7601u_wr(dev, MT_RF_BYPASS_0, rf_bp); + + trace_read_temp(dev, temp); + return temp; +} + +static s8 mt7601u_read_temp(struct mt7601u_dev *dev) +{ + int i; + u8 val; + s8 temp; + + val = mt7601u_bbp_rmw(dev, 47, 0x7f, 0x10); + + /* Note: this rarely succeeds, temp can change even if it fails. */ + for (i = 100; i && (val & 0x10); i--) + val = mt7601u_bbp_rr(dev, 47); + + temp = mt7601u_bbp_r47_get(dev, val, BBP_R47_F_TEMP); + + trace_read_temp(dev, temp); + return temp; +} + +static void mt7601u_rxdc_cal(struct mt7601u_dev *dev) +{ + static const struct mt76_reg_pair intro[] = { + { 158, 0x8d }, { 159, 0xfc }, + { 158, 0x8c }, { 159, 0x4c }, + }, outro[] = { + { 158, 0x8d }, { 159, 0xe0 }, + }; + u32 mac_ctrl; + int i, ret; + + mac_ctrl = mt7601u_rr(dev, MT_MAC_SYS_CTRL); + mt7601u_wr(dev, MT_MAC_SYS_CTRL, MT_MAC_SYS_CTRL_ENABLE_RX); + + ret = mt7601u_write_reg_pairs(dev, MT_MCU_MEMMAP_BBP, + intro, ARRAY_SIZE(intro)); + if (ret) + dev_err(dev->dev, "%s intro failed:%d\n", __func__, ret); + + for (i = 20; i; i--) { + usleep_range(300, 500); + + mt7601u_bbp_wr(dev, 158, 0x8c); + if (mt7601u_bbp_rr(dev, 159) == 0x0c) + break; + } + if (!i) + dev_err(dev->dev, "%s timed out\n", __func__); + + mt7601u_wr(dev, MT_MAC_SYS_CTRL, 0); + + ret = mt7601u_write_reg_pairs(dev, MT_MCU_MEMMAP_BBP, + outro, ARRAY_SIZE(outro)); + if (ret) + dev_err(dev->dev, "%s outro failed:%d\n", __func__, ret); + + mt7601u_wr(dev, MT_MAC_SYS_CTRL, mac_ctrl); +} + +void mt7601u_phy_recalibrate_after_assoc(struct mt7601u_dev *dev) +{ + if (test_bit(MT7601U_STATE_REMOVED, &dev->state)) + return; + + mt7601u_mcu_calibrate(dev, MCU_CAL_DPD, dev->curr_temp); + + mt7601u_rxdc_cal(dev); +} + +/* Note: function copied from vendor driver */ +static s16 lin2dBd(u16 linear) +{ + short exp = 0; + unsigned int mantisa; + int app, dBd; + + if (WARN_ON(!linear)) + return -10000; + + mantisa = linear; + + exp = fls(mantisa) - 16; + if (exp > 0) + mantisa >>= exp; + else + mantisa <<= abs(exp); + + if (mantisa <= 0xb800) + app = (mantisa + (mantisa >> 3) + (mantisa >> 4) - 0x9600); + else + app = (mantisa - (mantisa >> 3) - (mantisa >> 6) - 0x5a00); + if (app < 0) + app = 0; + + dBd = ((15 + exp) << 15) + app; + dBd = (dBd << 2) + (dBd << 1) + (dBd >> 6) + (dBd >> 7); + dBd = (dBd >> 10); + + return dBd; +} + +static void +mt7601u_set_initial_tssi(struct mt7601u_dev *dev, s16 tssi_db, s16 tssi_hvga_db) +{ + struct tssi_data *d = &dev->ee->tssi_data; + int init_offset; + + init_offset = -((tssi_db * d->slope + d->offset[1]) / 4096) + 10; + + mt76_rmw(dev, MT_TX_ALC_CFG_1, MT_TX_ALC_CFG_1_TEMP_COMP, + int_to_s6(init_offset) & MT_TX_ALC_CFG_1_TEMP_COMP); +} + +static void mt7601u_tssi_dc_gain_cal(struct mt7601u_dev *dev) +{ + u8 rf_vga, rf_mixer, bbp_r47; + int i, j; + s8 res[4]; + s16 tssi_init_db, tssi_init_hvga_db; + + mt7601u_wr(dev, MT_RF_SETTING_0, 0x00000030); + mt7601u_wr(dev, MT_RF_BYPASS_0, 0x000c0030); + mt7601u_wr(dev, MT_MAC_SYS_CTRL, 0); + + mt7601u_bbp_wr(dev, 58, 0); + mt7601u_bbp_wr(dev, 241, 0x2); + mt7601u_bbp_wr(dev, 23, 0x8); + bbp_r47 = mt7601u_bbp_rr(dev, 47); + + /* Set VGA gain */ + rf_vga = mt7601u_rf_rr(dev, 5, 3); + mt7601u_rf_wr(dev, 5, 3, 8); + + /* Mixer disable */ + rf_mixer = mt7601u_rf_rr(dev, 4, 39); + mt7601u_rf_wr(dev, 4, 39, 0); + + for (i = 0; i < 4; i++) { + mt7601u_rf_wr(dev, 4, 39, (i & 1) ? rf_mixer : 0); + + mt7601u_bbp_wr(dev, 23, (i < 2) ? 0x08 : 0x02); + mt7601u_rf_wr(dev, 5, 3, (i < 2) ? 0x08 : 0x11); + + /* BBP TSSI initial and soft reset */ + mt7601u_bbp_wr(dev, 22, 0); + mt7601u_bbp_wr(dev, 244, 0); + + mt7601u_bbp_wr(dev, 21, 1); + udelay(1); + mt7601u_bbp_wr(dev, 21, 0); + + /* TSSI measurement */ + mt7601u_bbp_wr(dev, 47, 0x50); + mt7601u_bbp_wr(dev, (i & 1) ? 244 : 22, (i & 1) ? 0x31 : 0x40); + + for (j = 20; j; j--) + if (!(mt7601u_bbp_rr(dev, 47) & 0x10)) + break; + if (!j) + dev_err(dev->dev, "%s timed out\n", __func__); + + /* TSSI read */ + mt7601u_bbp_wr(dev, 47, 0x40); + res[i] = mt7601u_bbp_rr(dev, 49); + } + + tssi_init_db = lin2dBd((short)res[1] - res[0]); + tssi_init_hvga_db = lin2dBd(((short)res[3] - res[2]) * 4); + dev->tssi_init = res[0]; + dev->tssi_init_hvga = res[2]; + dev->tssi_init_hvga_offset_db = tssi_init_hvga_db - tssi_init_db; + + dev_dbg(dev->dev, + "TSSI_init:%hhx db:%hx hvga:%hhx hvga_db:%hx off_db:%hx\n", + dev->tssi_init, tssi_init_db, dev->tssi_init_hvga, + tssi_init_hvga_db, dev->tssi_init_hvga_offset_db); + + mt7601u_bbp_wr(dev, 22, 0); + mt7601u_bbp_wr(dev, 244, 0); + + mt7601u_bbp_wr(dev, 21, 1); + udelay(1); + mt7601u_bbp_wr(dev, 21, 0); + + mt7601u_wr(dev, MT_RF_BYPASS_0, 0); + mt7601u_wr(dev, MT_RF_SETTING_0, 0); + + mt7601u_rf_wr(dev, 5, 3, rf_vga); + mt7601u_rf_wr(dev, 4, 39, rf_mixer); + mt7601u_bbp_wr(dev, 47, bbp_r47); + + mt7601u_set_initial_tssi(dev, tssi_init_db, tssi_init_hvga_db); +} + +static int mt7601u_temp_comp(struct mt7601u_dev *dev, bool on) +{ + int ret, temp, hi_temp = 400, lo_temp = -200; + + temp = (dev->raw_temp - dev->ee->ref_temp) * MT_EE_TEMPERATURE_SLOPE; + dev->curr_temp = temp; + + /* DPD Calibration */ + if (temp - dev->dpd_temp > 450 || temp - dev->dpd_temp < -450) { + dev->dpd_temp = temp; + + ret = mt7601u_mcu_calibrate(dev, MCU_CAL_DPD, dev->dpd_temp); + if (ret) + return ret; + + mt7601u_vco_cal(dev); + + dev_dbg(dev->dev, "Recalibrate DPD\n"); + } + + /* PLL Lock Protect */ + if (temp < -50 && !dev->pll_lock_protect) { /* < 20C */ + dev->pll_lock_protect = true; + + mt7601u_rf_wr(dev, 4, 4, 6); + mt7601u_rf_clear(dev, 4, 10, 0x30); + + dev_dbg(dev->dev, "PLL lock protect on - too cold\n"); + } else if (temp > 50 && dev->pll_lock_protect) { /* > 30C */ + dev->pll_lock_protect = false; + + mt7601u_rf_wr(dev, 4, 4, 0); + mt7601u_rf_rmw(dev, 4, 10, 0x30, 0x10); + + dev_dbg(dev->dev, "PLL lock protect off\n"); + } + + if (on) { + hi_temp -= 50; + lo_temp -= 50; + } + + /* BBP CR for H, L, N temperature */ + if (temp > hi_temp) + return mt7601u_bbp_temp(dev, MT_TEMP_MODE_HIGH, "high"); + else if (temp > lo_temp) + return mt7601u_bbp_temp(dev, MT_TEMP_MODE_NORMAL, "normal"); + else + return mt7601u_bbp_temp(dev, MT_TEMP_MODE_LOW, "low"); +} + +/* Note: this is used only with TSSI, we can just use trgt_pwr from eeprom. */ +static int mt7601u_current_tx_power(struct mt7601u_dev *dev) +{ + return dev->ee->chan_pwr[dev->chandef.chan->hw_value - 1]; +} + +static bool mt7601u_use_hvga(struct mt7601u_dev *dev) +{ + return !(mt7601u_current_tx_power(dev) > 20); +} + +static s16 +mt7601u_phy_rf_pa_mode_val(struct mt7601u_dev *dev, int phy_mode, int tx_rate) +{ + static const s16 decode_tb[] = { 0, 8847, -5734, -5734 }; + u32 reg; + + switch (phy_mode) { + case MT_PHY_TYPE_OFDM: + tx_rate += 4; + fallthrough; + case MT_PHY_TYPE_CCK: + reg = dev->rf_pa_mode[0]; + break; + default: + reg = dev->rf_pa_mode[1]; + break; + } + + return decode_tb[(reg >> (tx_rate * 2)) & 0x3]; +} + +static struct mt7601u_tssi_params +mt7601u_tssi_params_get(struct mt7601u_dev *dev) +{ + static const u8 ofdm_pkt2rate[8] = { 6, 4, 2, 0, 7, 5, 3, 1 }; + static const int static_power[4] = { 0, -49152, -98304, 49152 }; + struct mt7601u_tssi_params p; + u8 bbp_r47, pkt_type, tx_rate; + struct power_per_rate *rate_table; + + bbp_r47 = mt7601u_bbp_rr(dev, 47); + + p.tssi0 = mt7601u_bbp_r47_get(dev, bbp_r47, BBP_R47_F_TSSI); + dev->raw_temp = mt7601u_bbp_r47_get(dev, bbp_r47, BBP_R47_F_TEMP); + pkt_type = mt7601u_bbp_r47_get(dev, bbp_r47, BBP_R47_F_PKT_T); + + p.trgt_power = mt7601u_current_tx_power(dev); + + switch (pkt_type & 0x03) { + case MT_PHY_TYPE_CCK: + tx_rate = (pkt_type >> 4) & 0x03; + rate_table = dev->ee->power_rate_table.cck; + break; + + case MT_PHY_TYPE_OFDM: + tx_rate = ofdm_pkt2rate[(pkt_type >> 4) & 0x07]; + rate_table = dev->ee->power_rate_table.ofdm; + break; + + default: + tx_rate = mt7601u_bbp_r47_get(dev, bbp_r47, BBP_R47_F_TX_RATE); + tx_rate &= 0x7f; + rate_table = dev->ee->power_rate_table.ht; + break; + } + + if (dev->bw == MT_BW_20) + p.trgt_power += rate_table[tx_rate / 2].bw20; + else + p.trgt_power += rate_table[tx_rate / 2].bw40; + + p.trgt_power <<= 12; + + dev_dbg(dev->dev, "tx_rate:%02hhx pwr:%08x\n", tx_rate, p.trgt_power); + + p.trgt_power += mt7601u_phy_rf_pa_mode_val(dev, pkt_type & 0x03, + tx_rate); + + /* Channel 14, cck, bw20 */ + if ((pkt_type & 0x03) == MT_PHY_TYPE_CCK) { + if (mt7601u_bbp_rr(dev, 4) & 0x20) + p.trgt_power += mt7601u_bbp_rr(dev, 178) ? 18022 : 9830; + else + p.trgt_power += mt7601u_bbp_rr(dev, 178) ? 819 : 24576; + } + + p.trgt_power += static_power[mt7601u_bbp_rr(dev, 1) & 0x03]; + + p.trgt_power += dev->ee->tssi_data.tx0_delta_offset; + + dev_dbg(dev->dev, + "tssi:%02hhx t_power:%08x temp:%02hhx pkt_type:%02hhx\n", + p.tssi0, p.trgt_power, dev->raw_temp, pkt_type); + + return p; +} + +static bool mt7601u_tssi_read_ready(struct mt7601u_dev *dev) +{ + return !(mt7601u_bbp_rr(dev, 47) & 0x10); +} + +static int mt7601u_tssi_cal(struct mt7601u_dev *dev) +{ + struct mt7601u_tssi_params params; + int curr_pwr, diff_pwr; + char tssi_offset; + s8 tssi_init; + s16 tssi_m_dc, tssi_db; + bool hvga; + u32 val; + + if (!dev->ee->tssi_enabled) + return 0; + + hvga = mt7601u_use_hvga(dev); + if (!dev->tssi_read_trig) + return mt7601u_mcu_tssi_read_kick(dev, hvga); + + if (!mt7601u_tssi_read_ready(dev)) + return 0; + + params = mt7601u_tssi_params_get(dev); + + tssi_init = (hvga ? dev->tssi_init_hvga : dev->tssi_init); + tssi_m_dc = params.tssi0 - tssi_init; + tssi_db = lin2dBd(tssi_m_dc); + dev_dbg(dev->dev, "tssi dc:%04hx db:%04hx hvga:%d\n", + tssi_m_dc, tssi_db, hvga); + + if (dev->chandef.chan->hw_value < 5) + tssi_offset = dev->ee->tssi_data.offset[0]; + else if (dev->chandef.chan->hw_value < 9) + tssi_offset = dev->ee->tssi_data.offset[1]; + else + tssi_offset = dev->ee->tssi_data.offset[2]; + + if (hvga) + tssi_db -= dev->tssi_init_hvga_offset_db; + + curr_pwr = tssi_db * dev->ee->tssi_data.slope + (tssi_offset << 9); + diff_pwr = params.trgt_power - curr_pwr; + dev_dbg(dev->dev, "Power curr:%08x diff:%08x\n", curr_pwr, diff_pwr); + + if (params.tssi0 > 126 && diff_pwr > 0) { + dev_err(dev->dev, "Error: TSSI upper saturation\n"); + diff_pwr = 0; + } + if (params.tssi0 - tssi_init < 1 && diff_pwr < 0) { + dev_err(dev->dev, "Error: TSSI lower saturation\n"); + diff_pwr = 0; + } + + if ((dev->prev_pwr_diff ^ diff_pwr) < 0 && abs(diff_pwr) < 4096 && + (abs(diff_pwr) > abs(dev->prev_pwr_diff) || + (diff_pwr > 0 && diff_pwr == -dev->prev_pwr_diff))) + diff_pwr = 0; + else + dev->prev_pwr_diff = diff_pwr; + + diff_pwr += (diff_pwr > 0) ? 2048 : -2048; + diff_pwr /= 4096; + + dev_dbg(dev->dev, "final diff: %08x\n", diff_pwr); + + val = mt7601u_rr(dev, MT_TX_ALC_CFG_1); + curr_pwr = s6_to_int(FIELD_GET(MT_TX_ALC_CFG_1_TEMP_COMP, val)); + diff_pwr += curr_pwr; + val = (val & ~MT_TX_ALC_CFG_1_TEMP_COMP) | int_to_s6(diff_pwr); + mt7601u_wr(dev, MT_TX_ALC_CFG_1, val); + + return mt7601u_mcu_tssi_read_kick(dev, hvga); +} + +static u8 mt7601u_agc_default(struct mt7601u_dev *dev) +{ + return (dev->ee->lna_gain - 8) * 2 + 0x34; +} + +static void mt7601u_agc_reset(struct mt7601u_dev *dev) +{ + u8 agc = mt7601u_agc_default(dev); + + mt7601u_bbp_wr(dev, 66, agc); +} + +void mt7601u_agc_save(struct mt7601u_dev *dev) +{ + dev->agc_save = mt7601u_bbp_rr(dev, 66); +} + +void mt7601u_agc_restore(struct mt7601u_dev *dev) +{ + mt7601u_bbp_wr(dev, 66, dev->agc_save); +} + +static void mt7601u_agc_tune(struct mt7601u_dev *dev) +{ + u8 val = mt7601u_agc_default(dev); + long avg_rssi; + + if (test_bit(MT7601U_STATE_SCANNING, &dev->state)) + return; + + /* Note: only in STA mode and not dozing; perhaps do this only if + * there is enough rssi updates since last run? + * Rssi updates are only on beacons and U2M so should work... + */ + spin_lock_bh(&dev->con_mon_lock); + avg_rssi = ewma_rssi_read(&dev->avg_rssi); + spin_unlock_bh(&dev->con_mon_lock); + if (avg_rssi == 0) + return; + + avg_rssi = -avg_rssi; + if (avg_rssi <= -70) + val -= 0x20; + else if (avg_rssi <= -60) + val -= 0x10; + + if (val != mt7601u_bbp_rr(dev, 66)) + mt7601u_bbp_wr(dev, 66, val); + + /* TODO: also if lost a lot of beacons try resetting + * (see RTMPSetAGCInitValue() call in mlme.c). + */ +} + +static void mt7601u_phy_calibrate(struct work_struct *work) +{ + struct mt7601u_dev *dev = container_of(work, struct mt7601u_dev, + cal_work.work); + + mt7601u_agc_tune(dev); + mt7601u_tssi_cal(dev); + /* If TSSI calibration was run it already updated temperature. */ + if (!dev->ee->tssi_enabled) + dev->raw_temp = mt7601u_read_temp(dev); + mt7601u_temp_comp(dev, true); /* TODO: find right value for @on */ + + ieee80211_queue_delayed_work(dev->hw, &dev->cal_work, + MT_CALIBRATE_INTERVAL); +} + +static unsigned long +__mt7601u_phy_freq_cal(struct mt7601u_dev *dev, s8 last_offset, u8 phy_mode) +{ + u8 activate_threshold, deactivate_threshold; + + trace_freq_cal_offset(dev, phy_mode, last_offset); + + /* No beacons received - reschedule soon */ + if (last_offset == MT_FREQ_OFFSET_INVALID) + return MT_FREQ_CAL_ADJ_INTERVAL; + + switch (phy_mode) { + case MT_PHY_TYPE_CCK: + activate_threshold = 19; + deactivate_threshold = 5; + break; + case MT_PHY_TYPE_OFDM: + activate_threshold = 102; + deactivate_threshold = 32; + break; + case MT_PHY_TYPE_HT: + case MT_PHY_TYPE_HT_GF: + activate_threshold = 82; + deactivate_threshold = 20; + break; + default: + WARN_ON(1); + return MT_FREQ_CAL_CHECK_INTERVAL; + } + + if (abs(last_offset) >= activate_threshold) + dev->freq_cal.adjusting = true; + else if (abs(last_offset) <= deactivate_threshold) + dev->freq_cal.adjusting = false; + + if (!dev->freq_cal.adjusting) + return MT_FREQ_CAL_CHECK_INTERVAL; + + if (last_offset > deactivate_threshold) { + if (dev->freq_cal.freq > 0) + dev->freq_cal.freq--; + else + dev->freq_cal.adjusting = false; + } else if (last_offset < -deactivate_threshold) { + if (dev->freq_cal.freq < 0xbf) + dev->freq_cal.freq++; + else + dev->freq_cal.adjusting = false; + } + + trace_freq_cal_adjust(dev, dev->freq_cal.freq); + mt7601u_rf_wr(dev, 0, 12, dev->freq_cal.freq); + mt7601u_vco_cal(dev); + + return dev->freq_cal.adjusting ? MT_FREQ_CAL_ADJ_INTERVAL : + MT_FREQ_CAL_CHECK_INTERVAL; +} + +static void mt7601u_phy_freq_cal(struct work_struct *work) +{ + struct mt7601u_dev *dev = container_of(work, struct mt7601u_dev, + freq_cal.work.work); + s8 last_offset; + u8 phy_mode; + unsigned long delay; + + spin_lock_bh(&dev->con_mon_lock); + last_offset = dev->bcn_freq_off; + phy_mode = dev->bcn_phy_mode; + spin_unlock_bh(&dev->con_mon_lock); + + delay = __mt7601u_phy_freq_cal(dev, last_offset, phy_mode); + ieee80211_queue_delayed_work(dev->hw, &dev->freq_cal.work, delay); + + spin_lock_bh(&dev->con_mon_lock); + dev->bcn_freq_off = MT_FREQ_OFFSET_INVALID; + spin_unlock_bh(&dev->con_mon_lock); +} + +void mt7601u_phy_con_cal_onoff(struct mt7601u_dev *dev, + struct ieee80211_bss_conf *info) +{ + struct ieee80211_vif *vif = container_of(info, struct ieee80211_vif, + bss_conf); + + if (!vif->cfg.assoc) + cancel_delayed_work_sync(&dev->freq_cal.work); + + /* Start/stop collecting beacon data */ + spin_lock_bh(&dev->con_mon_lock); + ether_addr_copy(dev->ap_bssid, info->bssid); + ewma_rssi_init(&dev->avg_rssi); + dev->bcn_freq_off = MT_FREQ_OFFSET_INVALID; + spin_unlock_bh(&dev->con_mon_lock); + + dev->freq_cal.freq = dev->ee->rf_freq_off; + dev->freq_cal.enabled = vif->cfg.assoc; + dev->freq_cal.adjusting = false; + + if (vif->cfg.assoc) + ieee80211_queue_delayed_work(dev->hw, &dev->freq_cal.work, + MT_FREQ_CAL_INIT_DELAY); +} + +static int mt7601u_init_cal(struct mt7601u_dev *dev) +{ + u32 mac_ctrl; + int ret; + + dev->raw_temp = mt7601u_read_bootup_temp(dev); + dev->curr_temp = (dev->raw_temp - dev->ee->ref_temp) * + MT_EE_TEMPERATURE_SLOPE; + dev->dpd_temp = dev->curr_temp; + + mac_ctrl = mt7601u_rr(dev, MT_MAC_SYS_CTRL); + + ret = mt7601u_mcu_calibrate(dev, MCU_CAL_R, 0); + if (ret) + return ret; + + ret = mt7601u_rf_rr(dev, 0, 4); + if (ret < 0) + return ret; + ret |= 0x80; + ret = mt7601u_rf_wr(dev, 0, 4, ret); + if (ret) + return ret; + msleep(2); + + ret = mt7601u_mcu_calibrate(dev, MCU_CAL_TXDCOC, 0); + if (ret) + return ret; + + mt7601u_rxdc_cal(dev); + + ret = mt7601u_set_bw_filter(dev, true); + if (ret) + return ret; + ret = mt7601u_mcu_calibrate(dev, MCU_CAL_LOFT, 0); + if (ret) + return ret; + ret = mt7601u_mcu_calibrate(dev, MCU_CAL_TXIQ, 0); + if (ret) + return ret; + ret = mt7601u_mcu_calibrate(dev, MCU_CAL_RXIQ, 0); + if (ret) + return ret; + ret = mt7601u_mcu_calibrate(dev, MCU_CAL_DPD, dev->dpd_temp); + if (ret) + return ret; + + mt7601u_rxdc_cal(dev); + + mt7601u_tssi_dc_gain_cal(dev); + + mt7601u_wr(dev, MT_MAC_SYS_CTRL, mac_ctrl); + + mt7601u_temp_comp(dev, true); + + return 0; +} + +int mt7601u_bbp_set_bw(struct mt7601u_dev *dev, int bw) +{ + u32 val, old; + + if (bw == dev->bw) { + /* Vendor driver does the rmc even when no change is needed. */ + mt7601u_bbp_rmc(dev, 4, 0x18, bw == MT_BW_20 ? 0 : 0x10); + + return 0; + } + dev->bw = bw; + + /* Stop MAC for the time of bw change */ + old = mt7601u_rr(dev, MT_MAC_SYS_CTRL); + val = old & ~(MT_MAC_SYS_CTRL_ENABLE_TX | MT_MAC_SYS_CTRL_ENABLE_RX); + mt7601u_wr(dev, MT_MAC_SYS_CTRL, val); + mt76_poll(dev, MT_MAC_STATUS, MT_MAC_STATUS_TX | MT_MAC_STATUS_RX, + 0, 500000); + + mt7601u_bbp_rmc(dev, 4, 0x18, bw == MT_BW_20 ? 0 : 0x10); + + mt7601u_wr(dev, MT_MAC_SYS_CTRL, old); + + return mt7601u_load_bbp_temp_table_bw(dev); +} + +/** + * mt7601u_set_rx_path - set rx path in BBP + * @dev: pointer to adapter structure + * @path: rx path to set values are 0-based + */ +void mt7601u_set_rx_path(struct mt7601u_dev *dev, u8 path) +{ + mt7601u_bbp_rmw(dev, 3, 0x18, path << 3); +} + +/** + * mt7601u_set_tx_dac - set which tx DAC to use + * @dev: pointer to adapter structure + * @dac: DAC index, values are 0-based + */ +void mt7601u_set_tx_dac(struct mt7601u_dev *dev, u8 dac) +{ + mt7601u_bbp_rmc(dev, 1, 0x18, dac << 3); +} + +int mt7601u_phy_init(struct mt7601u_dev *dev) +{ + int ret; + + dev->rf_pa_mode[0] = mt7601u_rr(dev, MT_RF_PA_MODE_CFG0); + dev->rf_pa_mode[1] = mt7601u_rr(dev, MT_RF_PA_MODE_CFG1); + + ret = mt7601u_rf_wr(dev, 0, 12, dev->ee->rf_freq_off); + if (ret) + return ret; + ret = mt7601u_write_reg_pairs(dev, 0, rf_central, + ARRAY_SIZE(rf_central)); + if (ret) + return ret; + ret = mt7601u_write_reg_pairs(dev, 0, rf_channel, + ARRAY_SIZE(rf_channel)); + if (ret) + return ret; + ret = mt7601u_write_reg_pairs(dev, 0, rf_vga, ARRAY_SIZE(rf_vga)); + if (ret) + return ret; + + ret = mt7601u_init_cal(dev); + if (ret) + return ret; + + dev->prev_pwr_diff = 100; + + INIT_DELAYED_WORK(&dev->cal_work, mt7601u_phy_calibrate); + INIT_DELAYED_WORK(&dev->freq_cal.work, mt7601u_phy_freq_cal); + + return 0; +} diff --git a/drivers/net/wireless/mediatek/mt7601u/regs.h b/drivers/net/wireless/mediatek/mt7601u/regs.h new file mode 100644 index 000000000..e75522788 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt7601u/regs.h @@ -0,0 +1,627 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2014 Felix Fietkau + * Copyright (C) 2015 Jakub Kicinski + */ + +#ifndef __MT76_REGS_H +#define __MT76_REGS_H + +#include + +#define MT_ASIC_VERSION 0x0000 + +#define MT76XX_REV_E3 0x22 +#define MT76XX_REV_E4 0x33 + +#define MT_CMB_CTRL 0x0020 +#define MT_CMB_CTRL_XTAL_RDY BIT(22) +#define MT_CMB_CTRL_PLL_LD BIT(23) + +#define MT_EFUSE_CTRL 0x0024 +#define MT_EFUSE_CTRL_AOUT GENMASK(5, 0) +#define MT_EFUSE_CTRL_MODE GENMASK(7, 6) +#define MT_EFUSE_CTRL_LDO_OFF_TIME GENMASK(13, 8) +#define MT_EFUSE_CTRL_LDO_ON_TIME GENMASK(15, 14) +#define MT_EFUSE_CTRL_AIN GENMASK(25, 16) +#define MT_EFUSE_CTRL_KICK BIT(30) +#define MT_EFUSE_CTRL_SEL BIT(31) + +#define MT_EFUSE_DATA_BASE 0x0028 +#define MT_EFUSE_DATA(_n) (MT_EFUSE_DATA_BASE + ((_n) << 2)) + +#define MT_COEXCFG0 0x0040 +#define MT_COEXCFG0_COEX_EN BIT(0) + +#define MT_WLAN_FUN_CTRL 0x0080 +#define MT_WLAN_FUN_CTRL_WLAN_EN BIT(0) +#define MT_WLAN_FUN_CTRL_WLAN_CLK_EN BIT(1) +#define MT_WLAN_FUN_CTRL_WLAN_RESET_RF BIT(2) + +#define MT_WLAN_FUN_CTRL_WLAN_RESET BIT(3) /* MT76x0 */ +#define MT_WLAN_FUN_CTRL_CSR_F20M_CKEN BIT(3) /* MT76x2 */ + +#define MT_WLAN_FUN_CTRL_PCIE_CLK_REQ BIT(4) +#define MT_WLAN_FUN_CTRL_FRC_WL_ANT_SEL BIT(5) +#define MT_WLAN_FUN_CTRL_INV_ANT_SEL BIT(6) +#define MT_WLAN_FUN_CTRL_WAKE_HOST BIT(7) + +#define MT_WLAN_FUN_CTRL_THERM_RST BIT(8) /* MT76x2 */ +#define MT_WLAN_FUN_CTRL_THERM_CKEN BIT(9) /* MT76x2 */ + +#define MT_WLAN_FUN_CTRL_GPIO_IN GENMASK(15, 8) /* MT76x0 */ +#define MT_WLAN_FUN_CTRL_GPIO_OUT GENMASK(23, 16) /* MT76x0 */ +#define MT_WLAN_FUN_CTRL_GPIO_OUT_EN GENMASK(31, 24) /* MT76x0 */ + +#define MT_XO_CTRL0 0x0100 +#define MT_XO_CTRL1 0x0104 +#define MT_XO_CTRL2 0x0108 +#define MT_XO_CTRL3 0x010c +#define MT_XO_CTRL4 0x0110 + +#define MT_XO_CTRL5 0x0114 +#define MT_XO_CTRL5_C2_VAL GENMASK(14, 8) + +#define MT_XO_CTRL6 0x0118 +#define MT_XO_CTRL6_C2_CTRL GENMASK(14, 8) + +#define MT_XO_CTRL7 0x011c + +#define MT_WLAN_MTC_CTRL 0x10148 +#define MT_WLAN_MTC_CTRL_MTCMOS_PWR_UP BIT(0) +#define MT_WLAN_MTC_CTRL_PWR_ACK BIT(12) +#define MT_WLAN_MTC_CTRL_PWR_ACK_S BIT(13) +#define MT_WLAN_MTC_CTRL_BBP_MEM_PD GENMASK(19, 16) +#define MT_WLAN_MTC_CTRL_PBF_MEM_PD BIT(20) +#define MT_WLAN_MTC_CTRL_FCE_MEM_PD BIT(21) +#define MT_WLAN_MTC_CTRL_TSO_MEM_PD BIT(22) +#define MT_WLAN_MTC_CTRL_BBP_MEM_RB BIT(24) +#define MT_WLAN_MTC_CTRL_PBF_MEM_RB BIT(25) +#define MT_WLAN_MTC_CTRL_FCE_MEM_RB BIT(26) +#define MT_WLAN_MTC_CTRL_TSO_MEM_RB BIT(27) +#define MT_WLAN_MTC_CTRL_STATE_UP BIT(28) + +#define MT_INT_SOURCE_CSR 0x0200 +#define MT_INT_MASK_CSR 0x0204 + +#define MT_INT_RX_DONE(_n) BIT(_n) +#define MT_INT_RX_DONE_ALL GENMASK(1, 0) +#define MT_INT_TX_DONE_ALL GENMASK(13, 4) +#define MT_INT_TX_DONE(_n) BIT(_n + 4) +#define MT_INT_RX_COHERENT BIT(16) +#define MT_INT_TX_COHERENT BIT(17) +#define MT_INT_ANY_COHERENT BIT(18) +#define MT_INT_MCU_CMD BIT(19) +#define MT_INT_TBTT BIT(20) +#define MT_INT_PRE_TBTT BIT(21) +#define MT_INT_TX_STAT BIT(22) +#define MT_INT_AUTO_WAKEUP BIT(23) +#define MT_INT_GPTIMER BIT(24) +#define MT_INT_RXDELAYINT BIT(26) +#define MT_INT_TXDELAYINT BIT(27) + +#define MT_WPDMA_GLO_CFG 0x0208 +#define MT_WPDMA_GLO_CFG_TX_DMA_EN BIT(0) +#define MT_WPDMA_GLO_CFG_TX_DMA_BUSY BIT(1) +#define MT_WPDMA_GLO_CFG_RX_DMA_EN BIT(2) +#define MT_WPDMA_GLO_CFG_RX_DMA_BUSY BIT(3) +#define MT_WPDMA_GLO_CFG_DMA_BURST_SIZE GENMASK(5, 4) +#define MT_WPDMA_GLO_CFG_TX_WRITEBACK_DONE BIT(6) +#define MT_WPDMA_GLO_CFG_BIG_ENDIAN BIT(7) +#define MT_WPDMA_GLO_CFG_HDR_SEG_LEN GENMASK(15, 8) +#define MT_WPDMA_GLO_CFG_CLK_GATE_DIS BIT(30) +#define MT_WPDMA_GLO_CFG_RX_2B_OFFSET BIT(31) + +#define MT_WPDMA_RST_IDX 0x020c + +#define MT_WPDMA_DELAY_INT_CFG 0x0210 + +#define MT_WMM_AIFSN 0x0214 +#define MT_WMM_AIFSN_MASK GENMASK(3, 0) +#define MT_WMM_AIFSN_SHIFT(_n) ((_n) * 4) + +#define MT_WMM_CWMIN 0x0218 +#define MT_WMM_CWMIN_MASK GENMASK(3, 0) +#define MT_WMM_CWMIN_SHIFT(_n) ((_n) * 4) + +#define MT_WMM_CWMAX 0x021c +#define MT_WMM_CWMAX_MASK GENMASK(3, 0) +#define MT_WMM_CWMAX_SHIFT(_n) ((_n) * 4) + +#define MT_WMM_TXOP_BASE 0x0220 +#define MT_WMM_TXOP(_n) (MT_WMM_TXOP_BASE + (((_n) / 2) << 2)) +#define MT_WMM_TXOP_SHIFT(_n) ((_n & 1) * 16) +#define MT_WMM_TXOP_MASK GENMASK(15, 0) + +#define MT_FCE_DMA_ADDR 0x0230 +#define MT_FCE_DMA_LEN 0x0234 + +#define MT_USB_DMA_CFG 0x238 +#define MT_USB_DMA_CFG_RX_BULK_AGG_TOUT GENMASK(7, 0) +#define MT_USB_DMA_CFG_RX_BULK_AGG_LMT GENMASK(15, 8) +#define MT_USB_DMA_CFG_PHY_CLR BIT(16) +#define MT_USB_DMA_CFG_TX_CLR BIT(19) +#define MT_USB_DMA_CFG_TXOP_HALT BIT(20) +#define MT_USB_DMA_CFG_RX_BULK_AGG_EN BIT(21) +#define MT_USB_DMA_CFG_RX_BULK_EN BIT(22) +#define MT_USB_DMA_CFG_TX_BULK_EN BIT(23) +#define MT_USB_DMA_CFG_UDMA_RX_WL_DROP BIT(25) +#define MT_USB_DMA_CFG_EP_OUT_VALID GENMASK(29, 27) +#define MT_USB_DMA_CFG_RX_BUSY BIT(30) +#define MT_USB_DMA_CFG_TX_BUSY BIT(31) + +#define MT_TSO_CTRL 0x0250 +#define MT_HEADER_TRANS_CTRL_REG 0x0260 + +#define MT_US_CYC_CFG 0x02a4 +#define MT_US_CYC_CNT GENMASK(7, 0) + +#define MT_TX_RING_BASE 0x0300 +#define MT_RX_RING_BASE 0x03c0 +#define MT_RING_SIZE 0x10 + +#define MT_TX_HW_QUEUE_MCU 8 +#define MT_TX_HW_QUEUE_MGMT 9 + +#define MT_PBF_SYS_CTRL 0x0400 +#define MT_PBF_SYS_CTRL_MCU_RESET BIT(0) +#define MT_PBF_SYS_CTRL_DMA_RESET BIT(1) +#define MT_PBF_SYS_CTRL_MAC_RESET BIT(2) +#define MT_PBF_SYS_CTRL_PBF_RESET BIT(3) +#define MT_PBF_SYS_CTRL_ASY_RESET BIT(4) + +#define MT_PBF_CFG 0x0404 +#define MT_PBF_CFG_TX0Q_EN BIT(0) +#define MT_PBF_CFG_TX1Q_EN BIT(1) +#define MT_PBF_CFG_TX2Q_EN BIT(2) +#define MT_PBF_CFG_TX3Q_EN BIT(3) +#define MT_PBF_CFG_RX0Q_EN BIT(4) +#define MT_PBF_CFG_RX_DROP_EN BIT(8) + +#define MT_PBF_TX_MAX_PCNT 0x0408 +#define MT_PBF_RX_MAX_PCNT 0x040c + +#define MT_BCN_OFFSET_BASE 0x041c +#define MT_BCN_OFFSET(_n) (MT_BCN_OFFSET_BASE + ((_n) << 2)) + +#define MT_RXQ_STA 0x0430 +#define MT_TXQ_STA 0x0434 + +#define MT_RF_CSR_CFG 0x0500 +#define MT_RF_CSR_CFG_DATA GENMASK(7, 0) +#define MT_RF_CSR_CFG_REG_ID GENMASK(13, 8) +#define MT_RF_CSR_CFG_REG_BANK GENMASK(17, 14) +#define MT_RF_CSR_CFG_WR BIT(30) +#define MT_RF_CSR_CFG_KICK BIT(31) + +#define MT_RF_BYPASS_0 0x0504 +#define MT_RF_BYPASS_1 0x0508 +#define MT_RF_SETTING_0 0x050c + +#define MT_RF_DATA_WRITE 0x0524 + +#define MT_RF_CTRL 0x0528 +#define MT_RF_CTRL_ADDR GENMASK(11, 0) +#define MT_RF_CTRL_WRITE BIT(12) +#define MT_RF_CTRL_BUSY BIT(13) +#define MT_RF_CTRL_IDX BIT(16) + +#define MT_RF_DATA_READ 0x052c + +#define MT_FCE_PSE_CTRL 0x0800 +#define MT_FCE_PARAMETERS 0x0804 +#define MT_FCE_CSO 0x0808 + +#define MT_FCE_L2_STUFF 0x080c +#define MT_FCE_L2_STUFF_HT_L2_EN BIT(0) +#define MT_FCE_L2_STUFF_QOS_L2_EN BIT(1) +#define MT_FCE_L2_STUFF_RX_STUFF_EN BIT(2) +#define MT_FCE_L2_STUFF_TX_STUFF_EN BIT(3) +#define MT_FCE_L2_STUFF_WR_MPDU_LEN_EN BIT(4) +#define MT_FCE_L2_STUFF_MVINV_BSWAP BIT(5) +#define MT_FCE_L2_STUFF_TS_CMD_QSEL_EN GENMASK(15, 8) +#define MT_FCE_L2_STUFF_TS_LEN_EN GENMASK(23, 16) +#define MT_FCE_L2_STUFF_OTHER_PORT GENMASK(25, 24) + +#define MT_FCE_WLAN_FLOW_CONTROL1 0x0824 + +#define MT_TX_CPU_FROM_FCE_BASE_PTR 0x09a0 +#define MT_TX_CPU_FROM_FCE_MAX_COUNT 0x09a4 +#define MT_TX_CPU_FROM_FCE_CPU_DESC_IDX 0x09a8 + +#define MT_FCE_PDMA_GLOBAL_CONF 0x09c4 + +#define MT_PAUSE_ENABLE_CONTROL1 0x0a38 + +#define MT_FCE_SKIP_FS 0x0a6c + +#define MT_MAC_CSR0 0x1000 + +#define MT_MAC_SYS_CTRL 0x1004 +#define MT_MAC_SYS_CTRL_RESET_CSR BIT(0) +#define MT_MAC_SYS_CTRL_RESET_BBP BIT(1) +#define MT_MAC_SYS_CTRL_ENABLE_TX BIT(2) +#define MT_MAC_SYS_CTRL_ENABLE_RX BIT(3) + +#define MT_MAC_ADDR_DW0 0x1008 +#define MT_MAC_ADDR_DW1 0x100c +#define MT_MAC_ADDR_DW1_U2ME_MASK GENMASK(23, 16) + +#define MT_MAC_BSSID_DW0 0x1010 +#define MT_MAC_BSSID_DW1 0x1014 +#define MT_MAC_BSSID_DW1_ADDR GENMASK(15, 0) +#define MT_MAC_BSSID_DW1_MBSS_MODE GENMASK(17, 16) +#define MT_MAC_BSSID_DW1_MBEACON_N GENMASK(20, 18) +#define MT_MAC_BSSID_DW1_MBSS_LOCAL_BIT BIT(21) +#define MT_MAC_BSSID_DW1_MBSS_MODE_B2 BIT(22) +#define MT_MAC_BSSID_DW1_MBEACON_N_B3 BIT(23) +#define MT_MAC_BSSID_DW1_MBSS_IDX_BYTE GENMASK(26, 24) + +#define MT_MAX_LEN_CFG 0x1018 +#define MT_MAX_LEN_CFG_AMPDU GENMASK(13, 12) + +#define MT_BBP_CSR_CFG 0x101c +#define MT_BBP_CSR_CFG_VAL GENMASK(7, 0) +#define MT_BBP_CSR_CFG_REG_NUM GENMASK(15, 8) +#define MT_BBP_CSR_CFG_READ BIT(16) +#define MT_BBP_CSR_CFG_BUSY BIT(17) +#define MT_BBP_CSR_CFG_PAR_DUR BIT(18) +#define MT_BBP_CSR_CFG_RW_MODE BIT(19) + +#define MT_AMPDU_MAX_LEN_20M1S 0x1030 +#define MT_AMPDU_MAX_LEN_20M2S 0x1034 +#define MT_AMPDU_MAX_LEN_40M1S 0x1038 +#define MT_AMPDU_MAX_LEN_40M2S 0x103c +#define MT_AMPDU_MAX_LEN 0x1040 + +#define MT_WCID_DROP_BASE 0x106c +#define MT_WCID_DROP(_n) (MT_WCID_DROP_BASE + ((_n) >> 5) * 4) +#define MT_WCID_DROP_MASK(_n) BIT((_n) % 32) + +#define MT_BCN_BYPASS_MASK 0x108c + +#define MT_MAC_APC_BSSID_BASE 0x1090 +#define MT_MAC_APC_BSSID_L(_n) (MT_MAC_APC_BSSID_BASE + ((_n) * 8)) +#define MT_MAC_APC_BSSID_H(_n) (MT_MAC_APC_BSSID_BASE + ((_n) * 8 + 4)) +#define MT_MAC_APC_BSSID_H_ADDR GENMASK(15, 0) +#define MT_MAC_APC_BSSID0_H_EN BIT(16) + +#define MT_XIFS_TIME_CFG 0x1100 +#define MT_XIFS_TIME_CFG_CCK_SIFS GENMASK(7, 0) +#define MT_XIFS_TIME_CFG_OFDM_SIFS GENMASK(15, 8) +#define MT_XIFS_TIME_CFG_OFDM_XIFS GENMASK(19, 16) +#define MT_XIFS_TIME_CFG_EIFS GENMASK(28, 20) +#define MT_XIFS_TIME_CFG_BB_RXEND_EN BIT(29) + +#define MT_BKOFF_SLOT_CFG 0x1104 +#define MT_BKOFF_SLOT_CFG_SLOTTIME GENMASK(7, 0) +#define MT_BKOFF_SLOT_CFG_CC_DELAY GENMASK(11, 8) + +#define MT_BEACON_TIME_CFG 0x1114 +#define MT_BEACON_TIME_CFG_INTVAL GENMASK(15, 0) +#define MT_BEACON_TIME_CFG_TIMER_EN BIT(16) +#define MT_BEACON_TIME_CFG_SYNC_MODE GENMASK(18, 17) +#define MT_BEACON_TIME_CFG_TBTT_EN BIT(19) +#define MT_BEACON_TIME_CFG_BEACON_TX BIT(20) +#define MT_BEACON_TIME_CFG_TSF_COMP GENMASK(31, 24) + +#define MT_TBTT_SYNC_CFG 0x1118 +#define MT_TBTT_TIMER_CFG 0x1124 + +#define MT_INT_TIMER_CFG 0x1128 +#define MT_INT_TIMER_CFG_PRE_TBTT GENMASK(15, 0) +#define MT_INT_TIMER_CFG_GP_TIMER GENMASK(31, 16) + +#define MT_INT_TIMER_EN 0x112c +#define MT_INT_TIMER_EN_PRE_TBTT_EN BIT(0) +#define MT_INT_TIMER_EN_GP_TIMER_EN BIT(1) + +#define MT_MAC_STATUS 0x1200 +#define MT_MAC_STATUS_TX BIT(0) +#define MT_MAC_STATUS_RX BIT(1) + +#define MT_PWR_PIN_CFG 0x1204 +#define MT_AUX_CLK_CFG 0x120c + +#define MT_BB_PA_MODE_CFG0 0x1214 +#define MT_BB_PA_MODE_CFG1 0x1218 +#define MT_RF_PA_MODE_CFG0 0x121c +#define MT_RF_PA_MODE_CFG1 0x1220 + +#define MT_RF_PA_MODE_ADJ0 0x1228 +#define MT_RF_PA_MODE_ADJ1 0x122c + +#define MT_DACCLK_EN_DLY_CFG 0x1264 + +#define MT_EDCA_CFG_BASE 0x1300 +#define MT_EDCA_CFG_AC(_n) (MT_EDCA_CFG_BASE + ((_n) << 2)) +#define MT_EDCA_CFG_TXOP GENMASK(7, 0) +#define MT_EDCA_CFG_AIFSN GENMASK(11, 8) +#define MT_EDCA_CFG_CWMIN GENMASK(15, 12) +#define MT_EDCA_CFG_CWMAX GENMASK(19, 16) + +#define MT_TX_PWR_CFG_0 0x1314 +#define MT_TX_PWR_CFG_1 0x1318 +#define MT_TX_PWR_CFG_2 0x131c +#define MT_TX_PWR_CFG_3 0x1320 +#define MT_TX_PWR_CFG_4 0x1324 + +#define MT_TX_BAND_CFG 0x132c +#define MT_TX_BAND_CFG_UPPER_40M BIT(0) +#define MT_TX_BAND_CFG_5G BIT(1) +#define MT_TX_BAND_CFG_2G BIT(2) + +#define MT_HT_FBK_TO_LEGACY 0x1384 +#define MT_TX_MPDU_ADJ_INT 0x1388 + +#define MT_TX_PWR_CFG_7 0x13d4 +#define MT_TX_PWR_CFG_8 0x13d8 +#define MT_TX_PWR_CFG_9 0x13dc + +#define MT_TX_SW_CFG0 0x1330 +#define MT_TX_SW_CFG1 0x1334 +#define MT_TX_SW_CFG2 0x1338 + +#define MT_TXOP_CTRL_CFG 0x1340 +#define MT_TXOP_TRUN_EN GENMASK(5, 0) +#define MT_TXOP_EXT_CCA_DLY GENMASK(15, 8) +#define MT_TXOP_CTRL + +#define MT_TX_RTS_CFG 0x1344 +#define MT_TX_RTS_CFG_RETRY_LIMIT GENMASK(7, 0) +#define MT_TX_RTS_CFG_THRESH GENMASK(23, 8) +#define MT_TX_RTS_FALLBACK BIT(24) + +#define MT_TX_TIMEOUT_CFG 0x1348 +#define MT_TX_RETRY_CFG 0x134c +#define MT_TX_LINK_CFG 0x1350 +#define MT_HT_FBK_CFG0 0x1354 +#define MT_HT_FBK_CFG1 0x1358 +#define MT_LG_FBK_CFG0 0x135c +#define MT_LG_FBK_CFG1 0x1360 + +#define MT_CCK_PROT_CFG 0x1364 +#define MT_OFDM_PROT_CFG 0x1368 +#define MT_MM20_PROT_CFG 0x136c +#define MT_MM40_PROT_CFG 0x1370 +#define MT_GF20_PROT_CFG 0x1374 +#define MT_GF40_PROT_CFG 0x1378 + +#define MT_PROT_RATE GENMASK(15, 0) +#define MT_PROT_CTRL_RTS_CTS BIT(16) +#define MT_PROT_CTRL_CTS2SELF BIT(17) +#define MT_PROT_NAV_SHORT BIT(18) +#define MT_PROT_NAV_LONG BIT(19) +#define MT_PROT_TXOP_ALLOW_CCK BIT(20) +#define MT_PROT_TXOP_ALLOW_OFDM BIT(21) +#define MT_PROT_TXOP_ALLOW_MM20 BIT(22) +#define MT_PROT_TXOP_ALLOW_MM40 BIT(23) +#define MT_PROT_TXOP_ALLOW_GF20 BIT(24) +#define MT_PROT_TXOP_ALLOW_GF40 BIT(25) +#define MT_PROT_RTS_THR_EN BIT(26) +#define MT_PROT_RATE_CCK_11 0x0003 +#define MT_PROT_RATE_OFDM_6 0x4000 +#define MT_PROT_RATE_OFDM_24 0x4004 +#define MT_PROT_RATE_DUP_OFDM_24 0x4084 +#define MT_PROT_TXOP_ALLOW_ALL GENMASK(25, 20) +#define MT_PROT_TXOP_ALLOW_BW20 (MT_PROT_TXOP_ALLOW_ALL & \ + ~MT_PROT_TXOP_ALLOW_MM40 & \ + ~MT_PROT_TXOP_ALLOW_GF40) + +#define MT_EXP_ACK_TIME 0x1380 + +#define MT_TX_PWR_CFG_0_EXT 0x1390 +#define MT_TX_PWR_CFG_1_EXT 0x1394 + +#define MT_TX_FBK_LIMIT 0x1398 +#define MT_TX_FBK_LIMIT_MPDU_FBK GENMASK(7, 0) +#define MT_TX_FBK_LIMIT_AMPDU_FBK GENMASK(15, 8) +#define MT_TX_FBK_LIMIT_MPDU_UP_CLEAR BIT(16) +#define MT_TX_FBK_LIMIT_AMPDU_UP_CLEAR BIT(17) +#define MT_TX_FBK_LIMIT_RATE_LUT BIT(18) + +#define MT_TX0_RF_GAIN_CORR 0x13a0 +#define MT_TX1_RF_GAIN_CORR 0x13a4 +#define MT_TX0_RF_GAIN_ATTEN 0x13a8 + +#define MT_TX_ALC_CFG_0 0x13b0 +#define MT_TX_ALC_CFG_0_CH_INIT_0 GENMASK(5, 0) +#define MT_TX_ALC_CFG_0_CH_INIT_1 GENMASK(13, 8) +#define MT_TX_ALC_CFG_0_LIMIT_0 GENMASK(21, 16) +#define MT_TX_ALC_CFG_0_LIMIT_1 GENMASK(29, 24) + +#define MT_TX_ALC_CFG_1 0x13b4 +#define MT_TX_ALC_CFG_1_TEMP_COMP GENMASK(5, 0) + +#define MT_TX_ALC_CFG_2 0x13a8 +#define MT_TX_ALC_CFG_2_TEMP_COMP GENMASK(5, 0) + +#define MT_TX0_BB_GAIN_ATTEN 0x13c0 + +#define MT_TX_ALC_VGA3 0x13c8 + +#define MT_TX_PROT_CFG6 0x13e0 +#define MT_TX_PROT_CFG7 0x13e4 +#define MT_TX_PROT_CFG8 0x13e8 + +#define MT_PIFS_TX_CFG 0x13ec + +#define MT_RX_FILTR_CFG 0x1400 + +#define MT_RX_FILTR_CFG_CRC_ERR BIT(0) +#define MT_RX_FILTR_CFG_PHY_ERR BIT(1) +#define MT_RX_FILTR_CFG_PROMISC BIT(2) +#define MT_RX_FILTR_CFG_OTHER_BSS BIT(3) +#define MT_RX_FILTR_CFG_VER_ERR BIT(4) +#define MT_RX_FILTR_CFG_MCAST BIT(5) +#define MT_RX_FILTR_CFG_BCAST BIT(6) +#define MT_RX_FILTR_CFG_DUP BIT(7) +#define MT_RX_FILTR_CFG_CFACK BIT(8) +#define MT_RX_FILTR_CFG_CFEND BIT(9) +#define MT_RX_FILTR_CFG_ACK BIT(10) +#define MT_RX_FILTR_CFG_CTS BIT(11) +#define MT_RX_FILTR_CFG_RTS BIT(12) +#define MT_RX_FILTR_CFG_PSPOLL BIT(13) +#define MT_RX_FILTR_CFG_BA BIT(14) +#define MT_RX_FILTR_CFG_BAR BIT(15) +#define MT_RX_FILTR_CFG_CTRL_RSV BIT(16) + +#define MT_AUTO_RSP_CFG 0x1404 + +#define MT_AUTO_RSP_PREAMB_SHORT BIT(4) + +#define MT_LEGACY_BASIC_RATE 0x1408 +#define MT_HT_BASIC_RATE 0x140c + +#define MT_RX_PARSER_CFG 0x1418 +#define MT_RX_PARSER_RX_SET_NAV_ALL BIT(0) + +#define MT_EXT_CCA_CFG 0x141c +#define MT_EXT_CCA_CFG_CCA0 GENMASK(1, 0) +#define MT_EXT_CCA_CFG_CCA1 GENMASK(3, 2) +#define MT_EXT_CCA_CFG_CCA2 GENMASK(5, 4) +#define MT_EXT_CCA_CFG_CCA3 GENMASK(7, 6) +#define MT_EXT_CCA_CFG_CCA_MASK GENMASK(11, 8) +#define MT_EXT_CCA_CFG_ED_CCA_MASK GENMASK(15, 12) + +#define MT_TX_SW_CFG3 0x1478 + +#define MT_PN_PAD_MODE 0x150c + +#define MT_TXOP_HLDR_ET 0x1608 + +#define MT_PROT_AUTO_TX_CFG 0x1648 + +#define MT_RX_STA_CNT0 0x1700 +#define MT_RX_STA_CNT1 0x1704 +#define MT_RX_STA_CNT2 0x1708 +#define MT_TX_STA_CNT0 0x170c +#define MT_TX_STA_CNT1 0x1710 +#define MT_TX_STA_CNT2 0x1714 + +/* Vendor driver defines content of the second word of STAT_FIFO as follows: + * MT_TX_STAT_FIFO_RATE GENMASK(26, 16) + * MT_TX_STAT_FIFO_ETXBF BIT(27) + * MT_TX_STAT_FIFO_SND BIT(28) + * MT_TX_STAT_FIFO_ITXBF BIT(29) + * However, tests show that b16-31 have the same layout as TXWI rate_ctl + * with rate set to rate at which frame was acked. + */ +#define MT_TX_STAT_FIFO 0x1718 +#define MT_TX_STAT_FIFO_VALID BIT(0) +#define MT_TX_STAT_FIFO_PID_TYPE GENMASK(4, 1) +#define MT_TX_STAT_FIFO_SUCCESS BIT(5) +#define MT_TX_STAT_FIFO_AGGR BIT(6) +#define MT_TX_STAT_FIFO_ACKREQ BIT(7) +#define MT_TX_STAT_FIFO_WCID GENMASK(15, 8) +#define MT_TX_STAT_FIFO_RATE GENMASK(31, 16) + +#define MT_TX_AGG_STAT 0x171c + +#define MT_TX_AGG_CNT_BASE0 0x1720 + +#define MT_MPDU_DENSITY_CNT 0x1740 + +#define MT_TX_AGG_CNT_BASE1 0x174c + +#define MT_TX_AGG_CNT(_id) ((_id) < 8 ? \ + MT_TX_AGG_CNT_BASE0 + ((_id) << 2) : \ + MT_TX_AGG_CNT_BASE1 + ((_id - 8) << 2)) + +#define MT_TX_STAT_FIFO_EXT 0x1798 +#define MT_TX_STAT_FIFO_EXT_RETRY GENMASK(7, 0) + +#define MT_BBP_CORE_BASE 0x2000 +#define MT_BBP_IBI_BASE 0x2100 +#define MT_BBP_AGC_BASE 0x2300 +#define MT_BBP_TXC_BASE 0x2400 +#define MT_BBP_RXC_BASE 0x2500 +#define MT_BBP_TXO_BASE 0x2600 +#define MT_BBP_TXBE_BASE 0x2700 +#define MT_BBP_RXFE_BASE 0x2800 +#define MT_BBP_RXO_BASE 0x2900 +#define MT_BBP_DFS_BASE 0x2a00 +#define MT_BBP_TR_BASE 0x2b00 +#define MT_BBP_CAL_BASE 0x2c00 +#define MT_BBP_DSC_BASE 0x2e00 +#define MT_BBP_PFMU_BASE 0x2f00 + +#define MT_BBP(_type, _n) (MT_BBP_##_type##_BASE + ((_n) << 2)) + +#define MT_BBP_CORE_R1_BW GENMASK(4, 3) + +#define MT_BBP_AGC_R0_CTRL_CHAN GENMASK(9, 8) +#define MT_BBP_AGC_R0_BW GENMASK(14, 12) + +/* AGC, R4/R5 */ +#define MT_BBP_AGC_LNA_GAIN GENMASK(21, 16) + +/* AGC, R8/R9 */ +#define MT_BBP_AGC_GAIN GENMASK(14, 8) + +#define MT_BBP_AGC20_RSSI0 GENMASK(7, 0) +#define MT_BBP_AGC20_RSSI1 GENMASK(15, 8) + +#define MT_BBP_TXBE_R0_CTRL_CHAN GENMASK(1, 0) + +#define MT_WCID_ADDR_BASE 0x1800 +#define MT_WCID_ADDR(_n) (MT_WCID_ADDR_BASE + (_n) * 8) + +#define MT_SRAM_BASE 0x4000 + +#define MT_WCID_KEY_BASE 0x8000 +#define MT_WCID_KEY(_n) (MT_WCID_KEY_BASE + (_n) * 32) + +#define MT_WCID_IV_BASE 0xa000 +#define MT_WCID_IV(_n) (MT_WCID_IV_BASE + (_n) * 8) + +#define MT_WCID_ATTR_BASE 0xa800 +#define MT_WCID_ATTR(_n) (MT_WCID_ATTR_BASE + (_n) * 4) + +#define MT_WCID_ATTR_PAIRWISE BIT(0) +#define MT_WCID_ATTR_PKEY_MODE GENMASK(3, 1) +#define MT_WCID_ATTR_BSS_IDX GENMASK(6, 4) +#define MT_WCID_ATTR_RXWI_UDF GENMASK(9, 7) +#define MT_WCID_ATTR_PKEY_MODE_EXT BIT(10) +#define MT_WCID_ATTR_BSS_IDX_EXT BIT(11) +#define MT_WCID_ATTR_WAPI_MCBC BIT(15) +#define MT_WCID_ATTR_WAPI_KEYID GENMASK(31, 24) + +#define MT_SKEY_BASE_0 0xac00 +#define MT_SKEY_BASE_1 0xb400 +#define MT_SKEY_0(_bss, _idx) \ + (MT_SKEY_BASE_0 + (4 * (_bss) + _idx) * 32) +#define MT_SKEY_1(_bss, _idx) \ + (MT_SKEY_BASE_1 + (4 * ((_bss) & 7) + _idx) * 32) +#define MT_SKEY(_bss, _idx) \ + ((_bss & 8) ? MT_SKEY_1(_bss, _idx) : MT_SKEY_0(_bss, _idx)) + +#define MT_SKEY_MODE_BASE_0 0xb000 +#define MT_SKEY_MODE_BASE_1 0xb3f0 +#define MT_SKEY_MODE_0(_bss) \ + (MT_SKEY_MODE_BASE_0 + ((_bss / 2) << 2)) +#define MT_SKEY_MODE_1(_bss) \ + (MT_SKEY_MODE_BASE_1 + ((((_bss) & 7) / 2) << 2)) +#define MT_SKEY_MODE(_bss) \ + ((_bss & 8) ? MT_SKEY_MODE_1(_bss) : MT_SKEY_MODE_0(_bss)) +#define MT_SKEY_MODE_MASK GENMASK(3, 0) +#define MT_SKEY_MODE_SHIFT(_bss, _idx) (4 * ((_idx) + 4 * (_bss & 1))) + +#define MT_BEACON_BASE 0xc000 + +#define MT_TEMP_SENSOR 0x1d000 +#define MT_TEMP_SENSOR_VAL GENMASK(6, 0) + +enum mt76_cipher_type { + MT_CIPHER_NONE, + MT_CIPHER_WEP40, + MT_CIPHER_WEP104, + MT_CIPHER_TKIP, + MT_CIPHER_AES_CCMP, + MT_CIPHER_CKIP40, + MT_CIPHER_CKIP104, + MT_CIPHER_CKIP128, + MT_CIPHER_WAPI, +}; + +#endif diff --git a/drivers/net/wireless/mediatek/mt7601u/trace.c b/drivers/net/wireless/mediatek/mt7601u/trace.c new file mode 100644 index 000000000..42df68a54 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt7601u/trace.c @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2014 Felix Fietkau + * Copyright (C) 2015 Jakub Kicinski + */ + +#include + +#ifndef __CHECKER__ +#define CREATE_TRACE_POINTS +#include "trace.h" + +#endif diff --git a/drivers/net/wireless/mediatek/mt7601u/trace.h b/drivers/net/wireless/mediatek/mt7601u/trace.h new file mode 100644 index 000000000..07696b672 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt7601u/trace.h @@ -0,0 +1,392 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2014 Felix Fietkau + * Copyright (C) 2015 Jakub Kicinski + */ + +#if !defined(__MT7601U_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define __MT7601U_TRACE_H + +#include +#include "mt7601u.h" +#include "mac.h" + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM mt7601u + +#define MAXNAME 32 +#define DEV_ENTRY __array(char, wiphy_name, 32) +#define DEV_ASSIGN strscpy(__entry->wiphy_name, \ + wiphy_name(dev->hw->wiphy), MAXNAME) +#define DEV_PR_FMT "%s " +#define DEV_PR_ARG __entry->wiphy_name + +#define REG_ENTRY __field(u32, reg) __field(u32, val) +#define REG_ASSIGN __entry->reg = reg; __entry->val = val +#define REG_PR_FMT "%04x=%08x" +#define REG_PR_ARG __entry->reg, __entry->val + +DECLARE_EVENT_CLASS(dev_reg_evtu, + TP_PROTO(struct mt7601u_dev *dev, u32 reg, u32 val), + TP_ARGS(dev, reg, val), + TP_STRUCT__entry( + DEV_ENTRY + REG_ENTRY + ), + TP_fast_assign( + DEV_ASSIGN; + REG_ASSIGN; + ), + TP_printk( + DEV_PR_FMT REG_PR_FMT, + DEV_PR_ARG, REG_PR_ARG + ) +); + +DEFINE_EVENT(dev_reg_evtu, reg_read, + TP_PROTO(struct mt7601u_dev *dev, u32 reg, u32 val), + TP_ARGS(dev, reg, val) +); + +DEFINE_EVENT(dev_reg_evtu, reg_write, + TP_PROTO(struct mt7601u_dev *dev, u32 reg, u32 val), + TP_ARGS(dev, reg, val) +); + +TRACE_EVENT(mt_submit_urb, + TP_PROTO(struct mt7601u_dev *dev, struct urb *u), + TP_ARGS(dev, u), + TP_STRUCT__entry( + DEV_ENTRY __field(unsigned, pipe) __field(u32, len) + ), + TP_fast_assign( + DEV_ASSIGN; + __entry->pipe = u->pipe; + __entry->len = u->transfer_buffer_length; + ), + TP_printk(DEV_PR_FMT "p:%08x len:%u", + DEV_PR_ARG, __entry->pipe, __entry->len) +); + +#define trace_mt_submit_urb_sync(__dev, __pipe, __len) ({ \ + struct urb u; \ + u.pipe = __pipe; \ + u.transfer_buffer_length = __len; \ + trace_mt_submit_urb(__dev, &u); \ +}) + +TRACE_EVENT(mt_mcu_msg_send, + TP_PROTO(struct mt7601u_dev *dev, + struct sk_buff *skb, u32 csum, bool resp), + TP_ARGS(dev, skb, csum, resp), + TP_STRUCT__entry( + DEV_ENTRY + __field(u32, info) + __field(u32, csum) + __field(bool, resp) + ), + TP_fast_assign( + DEV_ASSIGN; + __entry->info = *(u32 *)skb->data; + __entry->csum = csum; + __entry->resp = resp; + ), + TP_printk(DEV_PR_FMT "i:%08x c:%08x r:%d", + DEV_PR_ARG, __entry->info, __entry->csum, __entry->resp) +); + +TRACE_EVENT(mt_vend_req, + TP_PROTO(struct mt7601u_dev *dev, unsigned pipe, u8 req, u8 req_type, + u16 val, u16 offset, void *buf, size_t buflen, int ret), + TP_ARGS(dev, pipe, req, req_type, val, offset, buf, buflen, ret), + TP_STRUCT__entry( + DEV_ENTRY + __field(unsigned, pipe) __field(u8, req) __field(u8, req_type) + __field(u16, val) __field(u16, offset) __field(void*, buf) + __field(int, buflen) __field(int, ret) + ), + TP_fast_assign( + DEV_ASSIGN; + __entry->pipe = pipe; + __entry->req = req; + __entry->req_type = req_type; + __entry->val = val; + __entry->offset = offset; + __entry->buf = buf; + __entry->buflen = buflen; + __entry->ret = ret; + ), + TP_printk(DEV_PR_FMT + "%d p:%08x req:%02hhx %02hhx val:%04hx %04hx buf:%d %d", + DEV_PR_ARG, __entry->ret, __entry->pipe, __entry->req, + __entry->req_type, __entry->val, __entry->offset, + !!__entry->buf, __entry->buflen) +); + +TRACE_EVENT(ee_read, + TP_PROTO(struct mt7601u_dev *dev, int offset, u16 val), + TP_ARGS(dev, offset, val), + TP_STRUCT__entry( + DEV_ENTRY + __field(int, o) __field(u16, v) + ), + TP_fast_assign( + DEV_ASSIGN; + __entry->o = offset; + __entry->v = val; + ), + TP_printk(DEV_PR_FMT "%04x=%04x", DEV_PR_ARG, __entry->o, __entry->v) +); + +DECLARE_EVENT_CLASS(dev_rf_reg_evt, + TP_PROTO(struct mt7601u_dev *dev, u8 bank, u8 reg, u8 val), + TP_ARGS(dev, bank, reg, val), + TP_STRUCT__entry( + DEV_ENTRY + __field(u8, bank) + __field(u8, reg) + __field(u8, val) + ), + TP_fast_assign( + DEV_ASSIGN; + REG_ASSIGN; + __entry->bank = bank; + ), + TP_printk( + DEV_PR_FMT "%02hhx:%02hhx=%02hhx", + DEV_PR_ARG, __entry->bank, __entry->reg, __entry->val + ) +); + +DEFINE_EVENT(dev_rf_reg_evt, rf_read, + TP_PROTO(struct mt7601u_dev *dev, u8 bank, u8 reg, u8 val), + TP_ARGS(dev, bank, reg, val) +); + +DEFINE_EVENT(dev_rf_reg_evt, rf_write, + TP_PROTO(struct mt7601u_dev *dev, u8 bank, u8 reg, u8 val), + TP_ARGS(dev, bank, reg, val) +); + +DECLARE_EVENT_CLASS(dev_bbp_reg_evt, + TP_PROTO(struct mt7601u_dev *dev, u8 reg, u8 val), + TP_ARGS(dev, reg, val), + TP_STRUCT__entry( + DEV_ENTRY + __field(u8, reg) + __field(u8, val) + ), + TP_fast_assign( + DEV_ASSIGN; + REG_ASSIGN; + ), + TP_printk( + DEV_PR_FMT "%02hhx=%02hhx", + DEV_PR_ARG, __entry->reg, __entry->val + ) +); + +DEFINE_EVENT(dev_bbp_reg_evt, bbp_read, + TP_PROTO(struct mt7601u_dev *dev, u8 reg, u8 val), + TP_ARGS(dev, reg, val) +); + +DEFINE_EVENT(dev_bbp_reg_evt, bbp_write, + TP_PROTO(struct mt7601u_dev *dev, u8 reg, u8 val), + TP_ARGS(dev, reg, val) +); + +DECLARE_EVENT_CLASS(dev_simple_evt, + TP_PROTO(struct mt7601u_dev *dev, u8 val), + TP_ARGS(dev, val), + TP_STRUCT__entry( + DEV_ENTRY + __field(u8, val) + ), + TP_fast_assign( + DEV_ASSIGN; + __entry->val = val; + ), + TP_printk( + DEV_PR_FMT "%02hhx", DEV_PR_ARG, __entry->val + ) +); + +DEFINE_EVENT(dev_simple_evt, temp_mode, + TP_PROTO(struct mt7601u_dev *dev, u8 val), + TP_ARGS(dev, val) +); + +DEFINE_EVENT(dev_simple_evt, read_temp, + TP_PROTO(struct mt7601u_dev *dev, u8 val), + TP_ARGS(dev, val) +); + +DEFINE_EVENT(dev_simple_evt, freq_cal_adjust, + TP_PROTO(struct mt7601u_dev *dev, u8 val), + TP_ARGS(dev, val) +); + +TRACE_EVENT(freq_cal_offset, + TP_PROTO(struct mt7601u_dev *dev, u8 phy_mode, s8 freq_off), + TP_ARGS(dev, phy_mode, freq_off), + TP_STRUCT__entry( + DEV_ENTRY + __field(u8, phy_mode) + __field(s8, freq_off) + ), + TP_fast_assign( + DEV_ASSIGN; + __entry->phy_mode = phy_mode; + __entry->freq_off = freq_off; + ), + TP_printk(DEV_PR_FMT "phy:%02hhx off:%02hhx", + DEV_PR_ARG, __entry->phy_mode, __entry->freq_off) +); + +TRACE_EVENT(mt_rx, + TP_PROTO(struct mt7601u_dev *dev, struct mt7601u_rxwi *rxwi, u32 f), + TP_ARGS(dev, rxwi, f), + TP_STRUCT__entry( + DEV_ENTRY + __field_struct(struct mt7601u_rxwi, rxwi) + __field(u32, fce_info) + ), + TP_fast_assign( + DEV_ASSIGN; + __entry->rxwi = *rxwi; + __entry->fce_info = f; + ), + TP_printk(DEV_PR_FMT "rxi:%08x ctl:%08x frag_sn:%04hx rate:%04hx " + "uknw:%02hhx z:%02hhx%02hhx%02hhx snr:%02hhx " + "ant:%02hhx gain:%02hhx freq_o:%02hhx " + "r:%08x ea:%08x fce:%08x", DEV_PR_ARG, + le32_to_cpu(__entry->rxwi.rxinfo), + le32_to_cpu(__entry->rxwi.ctl), + le16_to_cpu(__entry->rxwi.frag_sn), + le16_to_cpu(__entry->rxwi.rate), + __entry->rxwi.unknown, + __entry->rxwi.zero[0], __entry->rxwi.zero[1], + __entry->rxwi.zero[2], + __entry->rxwi.snr, __entry->rxwi.ant, + __entry->rxwi.gain, __entry->rxwi.freq_off, + __entry->rxwi.resv2, __entry->rxwi.expert_ant, + __entry->fce_info) +); + +TRACE_EVENT(mt_tx, + TP_PROTO(struct mt7601u_dev *dev, struct sk_buff *skb, + struct mt76_sta *sta, struct mt76_txwi *h), + TP_ARGS(dev, skb, sta, h), + TP_STRUCT__entry( + DEV_ENTRY + __field_struct(struct mt76_txwi, h) + __field(struct sk_buff *, skb) + __field(struct mt76_sta *, sta) + ), + TP_fast_assign( + DEV_ASSIGN; + __entry->h = *h; + __entry->skb = skb; + __entry->sta = sta; + ), + TP_printk(DEV_PR_FMT "skb:%p sta:%p flg:%04hx rate_ctl:%04hx " + "ack:%02hhx wcid:%02hhx len_ctl:%05hx", DEV_PR_ARG, + __entry->skb, __entry->sta, + le16_to_cpu(__entry->h.flags), + le16_to_cpu(__entry->h.rate_ctl), + __entry->h.ack_ctl, __entry->h.wcid, + le16_to_cpu(__entry->h.len_ctl)) +); + +TRACE_EVENT(mt_tx_dma_done, + TP_PROTO(struct mt7601u_dev *dev, struct sk_buff *skb), + TP_ARGS(dev, skb), + TP_STRUCT__entry( + DEV_ENTRY + __field(struct sk_buff *, skb) + ), + TP_fast_assign( + DEV_ASSIGN; + __entry->skb = skb; + ), + TP_printk(DEV_PR_FMT "%p", DEV_PR_ARG, __entry->skb) +); + +TRACE_EVENT(mt_tx_status_cleaned, + TP_PROTO(struct mt7601u_dev *dev, int cleaned), + TP_ARGS(dev, cleaned), + TP_STRUCT__entry( + DEV_ENTRY + __field(int, cleaned) + ), + TP_fast_assign( + DEV_ASSIGN; + __entry->cleaned = cleaned; + ), + TP_printk(DEV_PR_FMT "%d", DEV_PR_ARG, __entry->cleaned) +); + +TRACE_EVENT(mt_tx_status, + TP_PROTO(struct mt7601u_dev *dev, u32 stat1, u32 stat2), + TP_ARGS(dev, stat1, stat2), + TP_STRUCT__entry( + DEV_ENTRY + __field(u32, stat1) __field(u32, stat2) + ), + TP_fast_assign( + DEV_ASSIGN; + __entry->stat1 = stat1; + __entry->stat2 = stat2; + ), + TP_printk(DEV_PR_FMT "%08x %08x", + DEV_PR_ARG, __entry->stat1, __entry->stat2) +); + +TRACE_EVENT(mt_rx_dma_aggr, + TP_PROTO(struct mt7601u_dev *dev, int cnt, bool paged), + TP_ARGS(dev, cnt, paged), + TP_STRUCT__entry( + DEV_ENTRY + __field(u8, cnt) + __field(bool, paged) + ), + TP_fast_assign( + DEV_ASSIGN; + __entry->cnt = cnt; + __entry->paged = paged; + ), + TP_printk(DEV_PR_FMT "cnt:%d paged:%d", + DEV_PR_ARG, __entry->cnt, __entry->paged) +); + +DEFINE_EVENT(dev_simple_evt, set_key, + TP_PROTO(struct mt7601u_dev *dev, u8 val), + TP_ARGS(dev, val) +); + +TRACE_EVENT(set_shared_key, + TP_PROTO(struct mt7601u_dev *dev, u8 vid, u8 key), + TP_ARGS(dev, vid, key), + TP_STRUCT__entry( + DEV_ENTRY + __field(u8, vid) + __field(u8, key) + ), + TP_fast_assign( + DEV_ASSIGN; + __entry->vid = vid; + __entry->key = key; + ), + TP_printk(DEV_PR_FMT "phy:%02hhx off:%02hhx", + DEV_PR_ARG, __entry->vid, __entry->key) +); + +#endif + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE trace + +#include diff --git a/drivers/net/wireless/mediatek/mt7601u/tx.c b/drivers/net/wireless/mediatek/mt7601u/tx.c new file mode 100644 index 000000000..5aeeac0dd --- /dev/null +++ b/drivers/net/wireless/mediatek/mt7601u/tx.c @@ -0,0 +1,316 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2014 Felix Fietkau + * Copyright (C) 2015 Jakub Kicinski + */ + +#include "mt7601u.h" +#include "trace.h" + +enum mt76_txq_id { + MT_TXQ_VO = IEEE80211_AC_VO, + MT_TXQ_VI = IEEE80211_AC_VI, + MT_TXQ_BE = IEEE80211_AC_BE, + MT_TXQ_BK = IEEE80211_AC_BK, + MT_TXQ_PSD, + MT_TXQ_MCU, + __MT_TXQ_MAX +}; + +/* Hardware uses mirrored order of queues with Q0 having the highest priority */ +static u8 q2hwq(u8 q) +{ + return q ^ 0x3; +} + +/* Take mac80211 Q id from the skb and translate it to hardware Q id */ +static u8 skb2q(struct sk_buff *skb) +{ + int qid = skb_get_queue_mapping(skb); + + if (WARN_ON(qid >= MT_TXQ_PSD)) { + qid = MT_TXQ_BE; + skb_set_queue_mapping(skb, qid); + } + + return q2hwq(qid); +} + +/* Note: TX retry reporting is a bit broken. + * Retries are reported only once per AMPDU and often come a frame early + * i.e. they are reported in the last status preceding the AMPDU. Apart + * from the fact that it's hard to know the length of the AMPDU (which is + * required to know to how many consecutive frames retries should be + * applied), if status comes early on full FIFO it gets lost and retries + * of the whole AMPDU become invisible. + * As a work-around encode the desired rate in PKT_ID of TX descriptor + * and based on that guess the retries (every rate is tried once). + * Only downside here is that for MCS0 we have to rely solely on + * transmission failures as no retries can ever be reported. + * Not having to read EXT_FIFO has a nice effect of doubling the number + * of reports which can be fetched. + * Also the vendor driver never uses the EXT_FIFO register so it may be + * undertested. + */ +static u8 mt7601u_tx_pktid_enc(struct mt7601u_dev *dev, u8 rate, bool is_probe) +{ + u8 encoded = (rate + 1) + is_probe * 8; + + /* Because PKT_ID 0 disables status reporting only 15 values are + * available but 16 are needed (8 MCS * 2 for encoding is_probe) + * - we need to cram together two rates. MCS0 and MCS7 with is_probe + * share PKT_ID 9. + */ + if (is_probe && rate == 7) + return encoded - 7; + + return encoded; +} + +static void +mt7601u_tx_pktid_dec(struct mt7601u_dev *dev, struct mt76_tx_status *stat) +{ + u8 req_rate = stat->pktid; + u8 eff_rate = stat->rate & 0x7; + + req_rate -= 1; + + if (req_rate > 7) { + stat->is_probe = true; + req_rate -= 8; + + /* Decide between MCS0 and MCS7 which share pktid 9 */ + if (!req_rate && eff_rate) + req_rate = 7; + } + + stat->retry = req_rate - eff_rate; +} + +static void mt7601u_tx_skb_remove_dma_overhead(struct sk_buff *skb, + struct ieee80211_tx_info *info) +{ + int pkt_len = (unsigned long)info->status.status_driver_data[0]; + + skb_pull(skb, sizeof(struct mt76_txwi) + 4); + if (ieee80211_get_hdrlen_from_skb(skb) % 4) + mt76_remove_hdr_pad(skb); + + skb_trim(skb, pkt_len); +} + +void mt7601u_tx_status(struct mt7601u_dev *dev, struct sk_buff *skb) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + + mt7601u_tx_skb_remove_dma_overhead(skb, info); + + ieee80211_tx_info_clear_status(info); + info->status.rates[0].idx = -1; + info->flags |= IEEE80211_TX_STAT_ACK; + + spin_lock_bh(&dev->mac_lock); + ieee80211_tx_status_skb(dev->hw, skb); + spin_unlock_bh(&dev->mac_lock); +} + +static int mt7601u_skb_rooms(struct mt7601u_dev *dev, struct sk_buff *skb) +{ + int hdr_len = ieee80211_get_hdrlen_from_skb(skb); + u32 need_head; + + need_head = sizeof(struct mt76_txwi) + 4; + if (hdr_len % 4) + need_head += 2; + + return skb_cow(skb, need_head); +} + +static struct mt76_txwi * +mt7601u_push_txwi(struct mt7601u_dev *dev, struct sk_buff *skb, + struct ieee80211_sta *sta, struct mt76_wcid *wcid, + int pkt_len) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_tx_rate *rate = &info->control.rates[0]; + struct mt76_txwi *txwi; + unsigned long flags; + bool is_probe; + u32 pkt_id; + u16 rate_ctl; + u8 nss; + + txwi = skb_push(skb, sizeof(struct mt76_txwi)); + memset(txwi, 0, sizeof(*txwi)); + + if (!wcid->tx_rate_set) + ieee80211_get_tx_rates(info->control.vif, sta, skb, + info->control.rates, 1); + + spin_lock_irqsave(&dev->lock, flags); + if (rate->idx < 0 || !rate->count) + rate_ctl = wcid->tx_rate; + else + rate_ctl = mt76_mac_tx_rate_val(dev, rate, &nss); + spin_unlock_irqrestore(&dev->lock, flags); + txwi->rate_ctl = cpu_to_le16(rate_ctl); + + if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) + txwi->ack_ctl |= MT_TXWI_ACK_CTL_REQ; + if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) + txwi->ack_ctl |= MT_TXWI_ACK_CTL_NSEQ; + + if ((info->flags & IEEE80211_TX_CTL_AMPDU) && sta) { + u8 ba_size = IEEE80211_MIN_AMPDU_BUF; + + ba_size <<= sta->deflink.ht_cap.ampdu_factor; + ba_size = min_t(int, 63, ba_size); + if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) + ba_size = 0; + txwi->ack_ctl |= FIELD_PREP(MT_TXWI_ACK_CTL_BA_WINDOW, ba_size); + + txwi->flags = + cpu_to_le16(MT_TXWI_FLAGS_AMPDU | + FIELD_PREP(MT_TXWI_FLAGS_MPDU_DENSITY, + sta->deflink.ht_cap.ampdu_density)); + if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) + txwi->flags = 0; + } + + txwi->wcid = wcid->idx; + + is_probe = !!(info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE); + pkt_id = mt7601u_tx_pktid_enc(dev, rate_ctl & 0x7, is_probe); + pkt_len |= FIELD_PREP(MT_TXWI_LEN_PKTID, pkt_id); + txwi->len_ctl = cpu_to_le16(pkt_len); + + return txwi; +} + +void mt7601u_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, + struct sk_buff *skb) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct mt7601u_dev *dev = hw->priv; + struct ieee80211_vif *vif = info->control.vif; + struct ieee80211_sta *sta = control->sta; + struct mt76_sta *msta = NULL; + struct mt76_wcid *wcid = dev->mon_wcid; + struct mt76_txwi *txwi; + int pkt_len = skb->len; + int hw_q = skb2q(skb); + + BUILD_BUG_ON(ARRAY_SIZE(info->status.status_driver_data) < 1); + info->status.status_driver_data[0] = (void *)(unsigned long)pkt_len; + + if (mt7601u_skb_rooms(dev, skb) || mt76_insert_hdr_pad(skb)) { + ieee80211_free_txskb(dev->hw, skb); + return; + } + + if (sta) { + msta = (struct mt76_sta *) sta->drv_priv; + wcid = &msta->wcid; + } else if (vif) { + struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv; + + wcid = &mvif->group_wcid; + } + + txwi = mt7601u_push_txwi(dev, skb, sta, wcid, pkt_len); + + if (mt7601u_dma_enqueue_tx(dev, skb, wcid, hw_q)) + return; + + trace_mt_tx(dev, skb, msta, txwi); +} + +void mt7601u_tx_stat(struct work_struct *work) +{ + struct mt7601u_dev *dev = container_of(work, struct mt7601u_dev, + stat_work.work); + struct mt76_tx_status stat; + unsigned long flags; + int cleaned = 0; + + while (!test_bit(MT7601U_STATE_REMOVED, &dev->state)) { + stat = mt7601u_mac_fetch_tx_status(dev); + if (!stat.valid) + break; + + mt7601u_tx_pktid_dec(dev, &stat); + mt76_send_tx_status(dev, &stat); + + cleaned++; + } + trace_mt_tx_status_cleaned(dev, cleaned); + + spin_lock_irqsave(&dev->tx_lock, flags); + if (cleaned) + queue_delayed_work(dev->stat_wq, &dev->stat_work, + msecs_to_jiffies(10)); + else if (test_and_clear_bit(MT7601U_STATE_MORE_STATS, &dev->state)) + queue_delayed_work(dev->stat_wq, &dev->stat_work, + msecs_to_jiffies(20)); + else + clear_bit(MT7601U_STATE_READING_STATS, &dev->state); + spin_unlock_irqrestore(&dev->tx_lock, flags); +} + +int mt7601u_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + unsigned int link_id, u16 queue, + const struct ieee80211_tx_queue_params *params) +{ + struct mt7601u_dev *dev = hw->priv; + u8 cw_min = 5, cw_max = 10, hw_q = q2hwq(queue); + u32 val; + + /* TODO: should we do funny things with the parameters? + * See what mt7601u_set_default_edca() used to do in init.c. + */ + + if (params->cw_min) + cw_min = fls(params->cw_min); + if (params->cw_max) + cw_max = fls(params->cw_max); + + WARN_ON(params->txop > 0xff); + WARN_ON(params->aifs > 0xf); + WARN_ON(cw_min > 0xf); + WARN_ON(cw_max > 0xf); + + val = FIELD_PREP(MT_EDCA_CFG_AIFSN, params->aifs) | + FIELD_PREP(MT_EDCA_CFG_CWMIN, cw_min) | + FIELD_PREP(MT_EDCA_CFG_CWMAX, cw_max); + /* TODO: based on user-controlled EnableTxBurst var vendor drv sets + * a really long txop on AC0 (see connect.c:2009) but only on + * connect? When not connected should be 0. + */ + if (!hw_q) + val |= 0x60; + else + val |= FIELD_PREP(MT_EDCA_CFG_TXOP, params->txop); + mt76_wr(dev, MT_EDCA_CFG_AC(hw_q), val); + + val = mt76_rr(dev, MT_WMM_TXOP(hw_q)); + val &= ~(MT_WMM_TXOP_MASK << MT_WMM_TXOP_SHIFT(hw_q)); + val |= params->txop << MT_WMM_TXOP_SHIFT(hw_q); + mt76_wr(dev, MT_WMM_TXOP(hw_q), val); + + val = mt76_rr(dev, MT_WMM_AIFSN); + val &= ~(MT_WMM_AIFSN_MASK << MT_WMM_AIFSN_SHIFT(hw_q)); + val |= params->aifs << MT_WMM_AIFSN_SHIFT(hw_q); + mt76_wr(dev, MT_WMM_AIFSN, val); + + val = mt76_rr(dev, MT_WMM_CWMIN); + val &= ~(MT_WMM_CWMIN_MASK << MT_WMM_CWMIN_SHIFT(hw_q)); + val |= cw_min << MT_WMM_CWMIN_SHIFT(hw_q); + mt76_wr(dev, MT_WMM_CWMIN, val); + + val = mt76_rr(dev, MT_WMM_CWMAX); + val &= ~(MT_WMM_CWMAX_MASK << MT_WMM_CWMAX_SHIFT(hw_q)); + val |= cw_max << MT_WMM_CWMAX_SHIFT(hw_q); + mt76_wr(dev, MT_WMM_CWMAX, val); + + return 0; +} diff --git a/drivers/net/wireless/mediatek/mt7601u/usb.c b/drivers/net/wireless/mediatek/mt7601u/usb.c new file mode 100644 index 000000000..9306870cb --- /dev/null +++ b/drivers/net/wireless/mediatek/mt7601u/usb.c @@ -0,0 +1,379 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2015 Jakub Kicinski + */ + +#include +#include +#include + +#include "mt7601u.h" +#include "usb.h" +#include "trace.h" + +static const struct usb_device_id mt7601u_device_table[] = { + { USB_DEVICE(0x0b05, 0x17d3) }, + { USB_DEVICE(0x0e8d, 0x760a) }, + { USB_DEVICE(0x0e8d, 0x760b) }, + { USB_DEVICE(0x13d3, 0x3431) }, + { USB_DEVICE(0x13d3, 0x3434) }, + { USB_DEVICE(0x148f, 0x7601) }, + { USB_DEVICE(0x148f, 0x760a) }, + { USB_DEVICE(0x148f, 0x760b) }, + { USB_DEVICE(0x148f, 0x760c) }, + { USB_DEVICE(0x148f, 0x760d) }, + { USB_DEVICE(0x2001, 0x3d04) }, + { USB_DEVICE(0x2717, 0x4106) }, + { USB_DEVICE(0x2955, 0x0001) }, + { USB_DEVICE(0x2955, 0x1001) }, + { USB_DEVICE(0x2955, 0x1003) }, + { USB_DEVICE(0x2a5f, 0x1000) }, + { USB_DEVICE(0x7392, 0x7710) }, + { 0, } +}; + +bool mt7601u_usb_alloc_buf(struct mt7601u_dev *dev, size_t len, + struct mt7601u_dma_buf *buf) +{ + struct usb_device *usb_dev = mt7601u_to_usb_dev(dev); + + buf->len = len; + buf->urb = usb_alloc_urb(0, GFP_KERNEL); + buf->buf = usb_alloc_coherent(usb_dev, buf->len, GFP_KERNEL, &buf->dma); + + return !buf->urb || !buf->buf; +} + +void mt7601u_usb_free_buf(struct mt7601u_dev *dev, struct mt7601u_dma_buf *buf) +{ + struct usb_device *usb_dev = mt7601u_to_usb_dev(dev); + + usb_free_coherent(usb_dev, buf->len, buf->buf, buf->dma); + usb_free_urb(buf->urb); +} + +int mt7601u_usb_submit_buf(struct mt7601u_dev *dev, int dir, int ep_idx, + struct mt7601u_dma_buf *buf, gfp_t gfp, + usb_complete_t complete_fn, void *context) +{ + struct usb_device *usb_dev = mt7601u_to_usb_dev(dev); + unsigned pipe; + int ret; + + if (dir == USB_DIR_IN) + pipe = usb_rcvbulkpipe(usb_dev, dev->in_eps[ep_idx]); + else + pipe = usb_sndbulkpipe(usb_dev, dev->out_eps[ep_idx]); + + usb_fill_bulk_urb(buf->urb, usb_dev, pipe, buf->buf, buf->len, + complete_fn, context); + buf->urb->transfer_dma = buf->dma; + buf->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + trace_mt_submit_urb(dev, buf->urb); + ret = usb_submit_urb(buf->urb, gfp); + if (ret) + dev_err(dev->dev, "Error: submit URB dir:%d ep:%d failed:%d\n", + dir, ep_idx, ret); + return ret; +} + +void mt7601u_complete_urb(struct urb *urb) +{ + struct completion *cmpl = urb->context; + + complete(cmpl); +} + +int mt7601u_vendor_request(struct mt7601u_dev *dev, const u8 req, + const u8 direction, const u16 val, const u16 offset, + void *buf, const size_t buflen) +{ + int i, ret; + struct usb_device *usb_dev = mt7601u_to_usb_dev(dev); + const u8 req_type = direction | USB_TYPE_VENDOR | USB_RECIP_DEVICE; + const unsigned int pipe = (direction == USB_DIR_IN) ? + usb_rcvctrlpipe(usb_dev, 0) : usb_sndctrlpipe(usb_dev, 0); + + for (i = 0; i < MT_VEND_REQ_MAX_RETRY; i++) { + ret = usb_control_msg(usb_dev, pipe, req, req_type, + val, offset, buf, buflen, + MT_VEND_REQ_TOUT_MS); + trace_mt_vend_req(dev, pipe, req, req_type, val, offset, + buf, buflen, ret); + + if (ret == -ENODEV) + set_bit(MT7601U_STATE_REMOVED, &dev->state); + if (ret >= 0 || ret == -ENODEV) + return ret; + + msleep(5); + } + + dev_err(dev->dev, "Vendor request req:%02x off:%04x failed:%d\n", + req, offset, ret); + + return ret; +} + +void mt7601u_vendor_reset(struct mt7601u_dev *dev) +{ + mt7601u_vendor_request(dev, MT_VEND_DEV_MODE, USB_DIR_OUT, + MT_VEND_DEV_MODE_RESET, 0, NULL, 0); +} + +/* should be called with vendor_req_mutex held */ +static u32 __mt7601u_rr(struct mt7601u_dev *dev, u32 offset) +{ + int ret; + u32 val = ~0; + + WARN_ONCE(offset > USHRT_MAX, "read high off:%08x", offset); + + ret = mt7601u_vendor_request(dev, MT_VEND_MULTI_READ, USB_DIR_IN, + 0, offset, dev->vend_buf, MT_VEND_BUF); + if (ret == MT_VEND_BUF) + val = get_unaligned_le32(dev->vend_buf); + else if (ret > 0) + dev_err(dev->dev, "Error: wrong size read:%d off:%08x\n", + ret, offset); + + trace_reg_read(dev, offset, val); + return val; +} + +u32 mt7601u_rr(struct mt7601u_dev *dev, u32 offset) +{ + u32 ret; + + mutex_lock(&dev->vendor_req_mutex); + ret = __mt7601u_rr(dev, offset); + mutex_unlock(&dev->vendor_req_mutex); + + return ret; +} + +/* should be called with vendor_req_mutex held */ +static int __mt7601u_vendor_single_wr(struct mt7601u_dev *dev, const u8 req, + const u16 offset, const u32 val) +{ + int ret = mt7601u_vendor_request(dev, req, USB_DIR_OUT, + val & 0xffff, offset, NULL, 0); + if (!ret) + ret = mt7601u_vendor_request(dev, req, USB_DIR_OUT, + val >> 16, offset + 2, NULL, 0); + trace_reg_write(dev, offset, val); + return ret; +} + +int mt7601u_vendor_single_wr(struct mt7601u_dev *dev, const u8 req, + const u16 offset, const u32 val) +{ + int ret; + + mutex_lock(&dev->vendor_req_mutex); + ret = __mt7601u_vendor_single_wr(dev, req, offset, val); + mutex_unlock(&dev->vendor_req_mutex); + + return ret; +} + +void mt7601u_wr(struct mt7601u_dev *dev, u32 offset, u32 val) +{ + WARN_ONCE(offset > USHRT_MAX, "write high off:%08x", offset); + + mt7601u_vendor_single_wr(dev, MT_VEND_WRITE, offset, val); +} + +u32 mt7601u_rmw(struct mt7601u_dev *dev, u32 offset, u32 mask, u32 val) +{ + mutex_lock(&dev->vendor_req_mutex); + val |= __mt7601u_rr(dev, offset) & ~mask; + __mt7601u_vendor_single_wr(dev, MT_VEND_WRITE, offset, val); + mutex_unlock(&dev->vendor_req_mutex); + + return val; +} + +u32 mt7601u_rmc(struct mt7601u_dev *dev, u32 offset, u32 mask, u32 val) +{ + u32 reg; + + mutex_lock(&dev->vendor_req_mutex); + reg = __mt7601u_rr(dev, offset); + val |= reg & ~mask; + if (reg != val) + __mt7601u_vendor_single_wr(dev, MT_VEND_WRITE, + offset, val); + mutex_unlock(&dev->vendor_req_mutex); + + return val; +} + +void mt7601u_wr_copy(struct mt7601u_dev *dev, u32 offset, + const void *data, int len) +{ + WARN_ONCE(offset & 3, "unaligned write copy off:%08x", offset); + WARN_ONCE(len & 3, "short write copy off:%08x", offset); + + mt7601u_burst_write_regs(dev, offset, data, len / 4); +} + +void mt7601u_addr_wr(struct mt7601u_dev *dev, const u32 offset, const u8 *addr) +{ + mt7601u_wr(dev, offset, get_unaligned_le32(addr)); + mt7601u_wr(dev, offset + 4, addr[4] | addr[5] << 8); +} + +static int mt7601u_assign_pipes(struct usb_interface *usb_intf, + struct mt7601u_dev *dev) +{ + struct usb_endpoint_descriptor *ep_desc; + struct usb_host_interface *intf_desc = usb_intf->cur_altsetting; + unsigned i, ep_i = 0, ep_o = 0; + + BUILD_BUG_ON(sizeof(dev->in_eps) < __MT_EP_IN_MAX); + BUILD_BUG_ON(sizeof(dev->out_eps) < __MT_EP_OUT_MAX); + + for (i = 0; i < intf_desc->desc.bNumEndpoints; i++) { + ep_desc = &intf_desc->endpoint[i].desc; + + if (usb_endpoint_is_bulk_in(ep_desc) && + ep_i++ < __MT_EP_IN_MAX) { + dev->in_eps[ep_i - 1] = usb_endpoint_num(ep_desc); + dev->in_max_packet = usb_endpoint_maxp(ep_desc); + /* Note: this is ignored by usb sub-system but vendor + * code does it. We can drop this at some point. + */ + dev->in_eps[ep_i - 1] |= USB_DIR_IN; + } else if (usb_endpoint_is_bulk_out(ep_desc) && + ep_o++ < __MT_EP_OUT_MAX) { + dev->out_eps[ep_o - 1] = usb_endpoint_num(ep_desc); + dev->out_max_packet = usb_endpoint_maxp(ep_desc); + } + } + + if (ep_i != __MT_EP_IN_MAX || ep_o != __MT_EP_OUT_MAX) { + dev_err(dev->dev, "Error: wrong pipe number in:%d out:%d\n", + ep_i, ep_o); + return -EINVAL; + } + + return 0; +} + +static int mt7601u_probe(struct usb_interface *usb_intf, + const struct usb_device_id *id) +{ + struct usb_device *usb_dev = interface_to_usbdev(usb_intf); + struct mt7601u_dev *dev; + u32 asic_rev, mac_rev; + int ret; + + dev = mt7601u_alloc_device(&usb_intf->dev); + if (!dev) + return -ENOMEM; + + usb_reset_device(usb_dev); + + usb_set_intfdata(usb_intf, dev); + + dev->vend_buf = devm_kmalloc(dev->dev, MT_VEND_BUF, GFP_KERNEL); + if (!dev->vend_buf) { + ret = -ENOMEM; + goto err; + } + + ret = mt7601u_assign_pipes(usb_intf, dev); + if (ret) + goto err; + ret = mt7601u_wait_asic_ready(dev); + if (ret) + goto err; + + asic_rev = mt7601u_rr(dev, MT_ASIC_VERSION); + mac_rev = mt7601u_rr(dev, MT_MAC_CSR0); + dev_info(dev->dev, "ASIC revision: %08x MAC revision: %08x\n", + asic_rev, mac_rev); + if ((asic_rev >> 16) != 0x7601) { + ret = -ENODEV; + goto err; + } + + /* Note: vendor driver skips this check for MT7601U */ + if (!(mt7601u_rr(dev, MT_EFUSE_CTRL) & MT_EFUSE_CTRL_SEL)) + dev_warn(dev->dev, "Warning: eFUSE not present\n"); + + ret = mt7601u_init_hardware(dev); + if (ret) + goto err; + ret = mt7601u_register_device(dev); + if (ret) + goto err_hw; + + set_bit(MT7601U_STATE_INITIALIZED, &dev->state); + + return 0; +err_hw: + mt7601u_cleanup(dev); +err: + usb_set_intfdata(usb_intf, NULL); + + destroy_workqueue(dev->stat_wq); + ieee80211_free_hw(dev->hw); + return ret; +} + +static void mt7601u_disconnect(struct usb_interface *usb_intf) +{ + struct mt7601u_dev *dev = usb_get_intfdata(usb_intf); + + ieee80211_unregister_hw(dev->hw); + mt7601u_cleanup(dev); + + usb_set_intfdata(usb_intf, NULL); + + destroy_workqueue(dev->stat_wq); + ieee80211_free_hw(dev->hw); +} + +static int mt7601u_suspend(struct usb_interface *usb_intf, pm_message_t state) +{ + struct mt7601u_dev *dev = usb_get_intfdata(usb_intf); + + mt7601u_cleanup(dev); + + return 0; +} + +static int mt7601u_resume(struct usb_interface *usb_intf) +{ + struct mt7601u_dev *dev = usb_get_intfdata(usb_intf); + int ret; + + ret = mt7601u_init_hardware(dev); + if (ret) + return ret; + + set_bit(MT7601U_STATE_INITIALIZED, &dev->state); + + return 0; +} + +MODULE_DEVICE_TABLE(usb, mt7601u_device_table); +MODULE_FIRMWARE(MT7601U_FIRMWARE); +MODULE_DESCRIPTION("MediaTek MT7601U USB Wireless LAN driver"); +MODULE_LICENSE("GPL"); + +static struct usb_driver mt7601u_driver = { + .name = KBUILD_MODNAME, + .id_table = mt7601u_device_table, + .probe = mt7601u_probe, + .disconnect = mt7601u_disconnect, + .suspend = mt7601u_suspend, + .resume = mt7601u_resume, + .reset_resume = mt7601u_resume, + .soft_unbind = 1, + .disable_hub_initiated_lpm = 1, +}; +module_usb_driver(mt7601u_driver); diff --git a/drivers/net/wireless/mediatek/mt7601u/usb.h b/drivers/net/wireless/mediatek/mt7601u/usb.h new file mode 100644 index 000000000..723025f84 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt7601u/usb.h @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2015 Jakub Kicinski + */ + +#ifndef __MT7601U_USB_H +#define __MT7601U_USB_H + +#include "mt7601u.h" + +#define MT7601U_FIRMWARE "mt7601u.bin" +#define MT7601U_FIRMWARE_PATHS ARRAY_SIZE(mt7601u_fw_paths) + +#define MT_VEND_REQ_MAX_RETRY 10 +#define MT_VEND_REQ_TOUT_MS 300 + +#define MT_VEND_DEV_MODE_RESET 1 + +#define MT_VEND_BUF sizeof(__le32) + +enum mt_vendor_req { + MT_VEND_DEV_MODE = 1, + MT_VEND_WRITE = 2, + MT_VEND_MULTI_READ = 7, + MT_VEND_WRITE_FCE = 0x42, +}; + +enum mt_usb_ep_in { + MT_EP_IN_PKT_RX, + MT_EP_IN_CMD_RESP, + __MT_EP_IN_MAX, +}; + +enum mt_usb_ep_out { + MT_EP_OUT_INBAND_CMD, + MT_EP_OUT_AC_BK, + MT_EP_OUT_AC_BE, + MT_EP_OUT_AC_VI, + MT_EP_OUT_AC_VO, + MT_EP_OUT_HCCA, + __MT_EP_OUT_MAX, +}; + +static inline struct usb_device *mt7601u_to_usb_dev(struct mt7601u_dev *mt7601u) +{ + return interface_to_usbdev(to_usb_interface(mt7601u->dev)); +} + +static inline bool mt7601u_urb_has_error(struct urb *urb) +{ + return urb->status && + urb->status != -ENOENT && + urb->status != -ECONNRESET && + urb->status != -ESHUTDOWN; +} + +bool mt7601u_usb_alloc_buf(struct mt7601u_dev *dev, size_t len, + struct mt7601u_dma_buf *buf); +void mt7601u_usb_free_buf(struct mt7601u_dev *dev, struct mt7601u_dma_buf *buf); +int mt7601u_usb_submit_buf(struct mt7601u_dev *dev, int dir, int ep_idx, + struct mt7601u_dma_buf *buf, gfp_t gfp, + usb_complete_t complete_fn, void *context); +void mt7601u_complete_urb(struct urb *urb); + +int mt7601u_vendor_request(struct mt7601u_dev *dev, const u8 req, + const u8 direction, const u16 val, const u16 offset, + void *buf, const size_t buflen); +void mt7601u_vendor_reset(struct mt7601u_dev *dev); +int mt7601u_vendor_single_wr(struct mt7601u_dev *dev, const u8 req, + const u16 offset, const u32 val); + +#endif diff --git a/drivers/net/wireless/mediatek/mt7601u/util.c b/drivers/net/wireless/mediatek/mt7601u/util.c new file mode 100644 index 000000000..050c2dd9d --- /dev/null +++ b/drivers/net/wireless/mediatek/mt7601u/util.c @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2014 Felix Fietkau + */ + +#include "mt7601u.h" + +void mt76_remove_hdr_pad(struct sk_buff *skb) +{ + int len = ieee80211_get_hdrlen_from_skb(skb); + + memmove(skb->data + 2, skb->data, len); + skb_pull(skb, 2); +} + +int mt76_insert_hdr_pad(struct sk_buff *skb) +{ + int len = ieee80211_get_hdrlen_from_skb(skb); + int ret; + + if (len % 4 == 0) + return 0; + + ret = skb_cow(skb, 2); + if (ret) + return ret; + + skb_push(skb, 2); + memmove(skb->data, skb->data + 2, len); + + skb->data[len] = 0; + skb->data[len + 1] = 0; + return 0; +} -- cgit v1.3.1